CCS Cコンパイラ独自の機能
リロケータブル・オブジェクト / マルチ・コンパイル・ユニット(IDEのみ*)
バージョン4以前ではコンパイルのステップとリンクのステップが結合されていましたので他のオブジェクトと共にリンクする リロケータブル・オブジェクトの編集能力を持っていませんでした。 コードを幾つかのユニットに分割することでプロジェクトのメンテナンス性を良くする目的のみでなく最後のビルド以後に変更を加えた ユニットだけをコンパイルすれだけで、コンパイル速度を速めることができます。リロケータブル・オブジェクトを作成して、そしてそれらを一緒にリンクすることが可能ないくつかの方法が追加されました。: :
- PCW(PCD IDE, PCW, PCWH, PCWHD等のウィンドウズ版)ではCCS IDEにおいて
プロジェクトに複数のユニットを追加するファイル・ナビゲータを使用することが出来ます。
"Build"を押すと、全てのユニットをコンパイルしまとめてリンクします。 - #import() と#export()プリプロセッサ・コマンドはソースコード・レベルにおいてユニットを生成し、ユニットをリンクします。
/*
次のプリプロセッサ・コマンドによってコンパイラはこのコードをリロケータブル・オブジェクト・ファイル(.o)にコンパイルします。
RELOCATABLE[リロケータブル]はCCSのリロケータブル・オブジェクト・ファイルを生成します、そして、#EXPORTが使われるときはデフォルト・フォーマットとなります。
FILEが出力ファイル名の指定を選択できます。
ONLYオプションはコンパイラに対して、シンボル"GetString"(関数又は変数)のみをインポート/リンクしたモジュールで使用可能(可視)にします。
もう1つのオプションEXCEPTは逆の機能で −リストされたシンボル以外のシンボルをインポート/リンクしたモジュールで使用可能(可視)にします。
*/
#export(RELOCATABLE, FILE=getstring.o, ONLY=GetString)/*
次のプリプロセッサ・コマンドはコンパイラがこのユニットの中に他のオブジェクトをリンクします。(例では unit.o をリンク)
FILE, ONLYとEXCEPTオプションが同様に用意されており、詳細は上記例に従います。
COFFはRELOCATABLEオプションの逆で、コンパイラはMPASM, C18及びC30 で生成された.o をリンク/インポートします。 COFFは#importでのみ使用が可能で、#exportで使用する事は出来ません。
*/
#import(RELOCATABLE, FILE=unit.o) - コマンドライン・オプションは複数のユニット(ワンステップでのコンパイル/リンクの代わりに)を作成して、最終的に1つのHEXファイルすべてのユニットをリンクします。
次の例は2つの .C ファイルを別々の .Oファイルにコンパイルし、そして1つの .HEXファイルにリンクしています:
C:\project\ccsc +FH +EXPORT main.c
C:\project\ccsc +FH +EXPORT uart.c
C:\project\ccsc +FH LINK="main.hex=main.o,uart.o" - MPLAB® IDE内部で、もし、プロジェクト・マネージャー中に1つ以上の .Cファイルを追加すると、全ての.Cファイルが別々にリロケータブル・オブジェクトとしてコンパイルされ、最終ステップで全てのリロケータブル・オブジェクトが1つのHEXにリンクされます。
* コマンド-ライン・コンパイラのユーザーはリンクのみ可能で、リローケタブル・オブジェクトは生成出来ません。
リローケタブル・オブジェクト/リンカー機能はCCS CウィンドウズIDEでのみ使用出来ます。
可変長定数文字列
定数文字列の非効率な例の検証:
{
"HELLO",
"WORLD",
"EXTRALONGERWORD"
};
上記の例では"EXTRALONGERWORD"の長さから各文字列の最大長を15キャラクタにしなければなりませんでした。 しかし、"HELLO" と "WORLD"は6キャラクタ(nullターミネーションに1キャラクタが必要)ですので、各々9バイトが無駄でした。
この問題を解決するためにCCSは可変長定数文字列に対する方法を追加しました:
{
"HELLO",
"WORLD",
"EXTRALONGERWORD"
};
ノート: 定数データ・テーブルの索引に特別な機能を追加することで解決しました。尚、可変長定数文字列のポインタの作成は出来ません。
定数データへのポインタのよりフレキシブルな操作
定数データへのポインターが追加されました:簡単な例は文字列定数のアドレスを持った定数へポインターを割り当てます:
*/
const char version[] = "PRODUCT ID V1.01";
const char *ptr;
ptr = &version[0];
さらに複雑な例は定数文字列に配列のポインターを作成します:
*/
const char *strings[] =
{
"HELLO",
"WORLD",
"CONST",
"STRINGS"
};
/*
上記の定数文字列はポインター配列を用いて以下のようにアクセスされます。
*/
const char *ptr;
while (i = 0; i < (sizeof(strings) / sizeof(const char *)); i++)
{
ptr = strings[i];
printf("%s", ptr);
}
他の追加はさらに定数文字列を、文字のポインターを探している関数に渡す事ができる機能です:
下記の設定により、パラメータを関数へ渡す時、 コンパイラがRAMに定数文字列にコピーすることを可能にします:
*/
#device PASS_STRINGS=IN_RAM
/*
この新機能を使用する例::
*/
if (stricmp(buffer,"ATDT\r")==0)
{
//ここに何らかのコードを記載
}
ノート: CCSのconst(定数)修飾子は常にデータがプログラム・メモリーに置かれることを意味し、 そのデータは `読取専用`です。これはconst(定数)修飾子が単純に `読取専用`としているANSI定義には従っていません。
#USE I2C()
パワフル I2Cライブラリーがコンパイラに含まれています。 まず、ユーザーはI2Cポートに異なったストリーム識別子を与えることが出来ます。 異なった2Cチャンネル・ストリーム拡張子を与えることで、 コードでどのポートが使用されているかを簡単に識別することが出来ます。
次は, i2c_start()関数がI2Cスタート、又は、I2C リスタート信号を送ります。i2c_start() のための新しいリスタート・パラメータはスタートの代わりにリスタートを強制するために2つを セットすることが出来ます。値1はノーマル・スタートです。もし、リスタートが指定されなかったり、又は、0の場合は、 リスタートは最後のi2c_start()と no i2c_stop()でのみリスタートされます。
これは2つのI2Cポートを コンフィギャします。それぞれ異なったストリーム名を持ちます。
*/
#use i2c(sda=PIN_C4, scl=PIN_C3, stream=I2C_HW)
#use i2c(sda=PIN_B1, scl=PIN_B2, stream=I2C_SW)
/*
次の関数はI2C_HWストリームを使って、Microchip 24LC16のI2C EEPROMからデータ・バイトを読みます。
*/
int Read2416(int address)
{
i2c_start(I2C_HW, 1); //実行を開始
i2c_write(I2C_HW, 0xA0);
i2c_write(I2C_HW, address);
i2c_start(I2C_HW, 2); //リスタートを実行
i2c_write(I2C_HW, 0xA1);
data=i2c_read(IC2_HW, 0);
i2c_stop(I2C_HW);
return(data);
}
外部メモリを持ったチップに対する複数hexファイルの作成
#exportはHEXファイルをどのようにエクスポート及びインポートするかを指定するオプションを持っています。 これらのコマンドはブートローダの作成や外部にプログラムメモリを持つことができるデバイス(CPU又は、EMCUモード)を使用する時に有用です:
コンパイラは2つのHEXファイルを生成します。 ODDオプションはプログラムメモリの奇数アドレスに書き込むもの、EVENオプションは偶数アドレスに書き込むものとなります。 これらは18Fファミリーの外部メモリ・インターフェースの16ビット・バイトの書き込みモードを使用する場合に使用します。
*/
#export(HEX, file=odd.hex, ODD)
#export(HEX, file=even.hex, EVEN)
オフセットはコンパイラがこのプロジェクトに対してHEXファイルを生成する時、全てのアドレスは0x800バイトだけオフセットします。
*/
#export(HEX, file=application.hex, offset=0x800)
上記のサンプルは最終のHEXファイルが0x800にオフセットされたアドレス持っているようなアプリケーションです。もし、ローダープログラム(loader.hex)をアプリケーションにインポートしたい場合、以下のようにrangeオプションを使用します:
*/
#import(HEX, file=loader.hex, range=0:0x7FF)
#USE SPI()
CCSの最もパワフルなライブラリーの幾つかはRS-232とI2C ライブラリーを持っています。 これらは汎用I/Oピンの使用を1度セットしするだけで、ハードウエア・ペリフェラルのみを使用しなくても、 マルチでRS-232と I2C ポート使用できるフレキシビリティーを提供します。 SPIライブラリーは次の機能を含んでいます。:如何なる汎用I/Oピンの使用, クロック・コンフィギュレーション, 色々なデータビットの数, ストリーム, クロック・レート、等々!
#use SPIはSPIポートを設定。これはシンプルな設定です:
*/
#use SPI(
DO = PIN_B0,
DI = PIN_B1,
CLK = PIN_B2,
baud = 100000,
BITS = 8,
LSB_FIRST,
SAMPLE_RISE,
stream = SPI_PORT0
)
/*
新しいSPIストリームを使って外部EEPROMのデータのバイトを9356まで読みます。
*/
void Read9356(long address, int data)
{
output_high(EEPROM_9356_SELECT);
SPI_XFER(SPI_PORT0, 0x18);
SPI_XFER(SPI_PORT0, address);
data=SPI_XFER(SPI_PORT0, 0);
output_low(EEPROM_9356_SELECT);
}
#USE RS232()
パワフルなRS-232ライブラリーは下記のオプションを含んでいます:
- #use rs232()に2つの新しいパラメータが追加:UART1とUART2。 これらのどちらかを選びますと指定されたハードウエア MSSP送信とPIC® MCUの受信ピンがCCS RS232ライブラリーの送信と受信ピンを自動的にセットします。
- タイムアウト・パラメータを追加しますと、getc()によりミリ秒の単位で指定数でタイムアウトを得られます。
- クロック・パラメータを追加しますと、#use delayで指定されたクロックを使う代わりにシステムのクロック速度を指定することが出来ます。 クロック・パラメータが指定されない時は#use delayで指定されたクロック速度を使用します。
- ストップ・ビットの数を定義することが出来ます。
- 同期マスター又は、同期スレーブでRS-232を使用することが出来ます。
- ボーレート・オプションでカンマ、ピリオッドと次のプリフィックスをサポート: K, KHZ, M, MHZ。例えば、これらが有効です。: BAUD=9.6k, BAUD=115,200, BAUD=115.2K, 等々
自動 #fusesコンフィギュレーション
ユーザーのコードに基づいて自動的にコンフィギュレーション・ビット(#fuses)のいくつかを設定します:
- デフォルトではNOLVPヒューズがセットされます。(低電圧プログラミングをオフにします。)
- デフォルトではPUTヒューズがセットされます。(パワーアップ・タイマーがオン)
- もし、コードに"restart_wdt()"が無い場合はNOWDTヒューズがセットされます。もし、コードに"restart_wdt()"が有る場合は、 WDTヒューズにセットされます。
- オシレータ・コンフィギュレーション・ビットは#use delay()に基づいて自動的にセットされます。(次のセクションを参照して下さい。)
- もし、PCW IDEでデバッガをイネーブルにしていますとDEBUGヒューズがセットされます。 .
ベーシックな#fusesで多くのプログラムが自動的にセットされますので、#fusesディレクティブは必要有りません。
この機能はCCS3バックワード・コンパティビリティを使うことでディスエーブルにすることが出来ます。(前のセクションを参照して下さい。)
Addressmod機能は任意の種類のメモリ・デバイスにユーザー定義アドレスのスペースを作ります。
IEEE組み込みC規格(ISO/IEC TR 18037)の一部では、addressmodはどの様な種類のメモリ・デバイスにも変数を 作成するためにカスタムの領域を作成することが出来ます。新しい修飾子は、構造体、共用体、配列及びポインタを含む いかなるデータ・タイプでも使用することが出来ます。 addressmodを使って外部メモリに変数を作成する次のサンプルを参照して下さい:
addressmodの構文は:
addressmod (identifier,read,write,start,end)
identifier - 新しいカスタム修飾子(identifier)名
read/write - 外部メモリへアクセスするためのread/write関数
start/end - この修飾子(identifier)がアクセス出来るアドレスの範囲
*/
addressmod(extram, readextram, writeextram, 0, 0xFFFF)
/*
大きな配列(アレイ)を作成します。このアレイの実際の内容は外部メモリに保存されます。
*/
extram largeBuffer[2000]
/*
外部メモリにポインタを作成します。ポインタ自体はPICメモリ上に置かれます。
*/
extram char *extramPtr;
/*
いくつかの使用例
*/
//直接アクセス
largeBuffer[0] = 5;
//ポインタ割り当て、アドレス取得
extramPtr = &largeBuffer[0];
//間接的に外部メモリにアクセス
*extramPtr = 5;
デフォルト・パラメータ
C++で取り入れられたデフォルトのパラメータ設定も追加されます。デフォルト・パラメータは関数で指定することが出来ます。 もしパラメータを関数にパスされない場合は初期値が使用されます。
{
/*
この関数はRS232上で n ミリ秒間文字を待ちます。もし、文字が受信されればポインターCに保存され、TRUEを返します。 タイムアウトが発生した場合はFALSEを返します。
*/
}
//文字を取得、タイムアウトまで100ms間待ちます。
mygetc(&c);
//文字を取得、タイムアウトまで200ms間待ちます。
mygetc(&c, 200);
パラメータの可変数
コンパイラーでパラメータの可変数を持った関数を使用することが出来ます。 これはprintfとfprintfライブラリーを書いている時良く見つけることが出来ます。
stdarg.h はマクロを保持し、パラメータの可変数のためにva_listデータの型を必要とします。
*/
#include <stdarg.h>
/*
パラメータの可変数を持っ関数は2つのものを必要とします。最初は関数の最後のパラメータでなければならないエリプシス[省略符号](...)。 この省略符号は可変引数リストを表します。2番目は省略符号(...)の前に1つ以上の変数を必要とします。 一般的に、この変数は、いくつの変数を省略符号に拡張してあるかを決める方法として使用されます。
これは全ての変数の合計を計算し返す関数です:
*/
int Sum(int count, ...)
{
//引数リストへのポインタ
va_list al;
int x, sum=0;
//引数リストを開始
//省略符号の前にある最初の変数をカウント
va_start(al, count);
while(count--) {
//リストから整数値を取得
x = var_arg(al, int);
sum += x;
}
//リストの使用を停止
va_end(al);
return(sum);
}
/*
この関数を使ったいくつかのサンプル例:
*/
x=Sum(5, 10, 20, 30, 40, 50);
y=Sum(3, a, b, c);
多重関数定義[関数オーバーロード]
C++から関数多重定義と呼ばれる機能を取り入れることができます。 関数多重定義はパラメータの数とタイプの違いのみで同じ名前でいくつかの関数を持つことが出来ます。
関数多重定義のサンプル: 2つの関数が同じ名前を持っています。しかし、パラメータのタイプは違います。 コンパイラはどちらのデータ・タイプがパラメータとして渡されるべきかを決定し、適切な関数を呼びます。
*/
void FindSquareRoot(long *n)
{
/*
この関数はlong int型の変数(ポインタから)の平方根を見つけてポインタに結果を保存します。
*/
}
void FindSquareRoot(float *n)
{
/*
この関数は浮動変数(ポインタから)の平方根を見つけて ポインタに結果を保存します。
*/
}
/*
FindSquareRootが呼ばれます。もし、変数がlong型の場合は、最初に示した FindSquareRoot()の例を呼びます。 もし、変数がfloat型の場合は、次のFindSquareRoot()の例を呼びます。
*/
FindSquareRoot(&variable);
ビット・アレイ
ビット・アレイ(又は、ブーリアン[booleans])を作成することが出来ます。ビットのアレイ、又は、ビットにポインタは作成出来ません。
これはビット・アレイを作成し、そして、それらの値を初期化します。
*/
int1 flags[]={TRUE, TRUE, TRUE, FALSE, FALSE, FALSE};
/*
幾つかの使用法:
*/
bool = flags[1];
if ( flags[2] ) { /* ここに何らかのコードを記載 */ }
flags[i++] = FALSE;
固定小数点
コンパイラのパワフルな機能では新しいデータ・タイプ、固定少数点を使って10進数を表わしますことが出来ます。 固定少数点は10進数を表現できますが速度は整数です。これはfloatを使った場合に驚異的なスピードアップとなります。 これは新しい修飾子: _fixed(x)で可能です。xはデータ・タイプが保持することができる小数点の後の桁数です。
0.00 から 655.35の範囲で16ビット変数を作成
*/
int16 _fixed(2) dollars;
/*
dollarsに1.23を割り当て。内部で123はint16整数にセーブされます。
*/
dollars=1.23;
/*
dollarsに3.00を加えます。内部で300はint16整数に加えられます。
*/
dollars += 3;
/*
printfは4.23を表示
*/
printf("%w", dollars);
CCSの旧バージョンとの互換性
CCSは旧バージョンで書かれたコードを必要なバージョンでコンパイルする場合に、コンパイル手法を変更することで問題を
最小限にする留める方法を提供しております。現在は下記の互換性が提供されています。
CCS V2.xxx, CCS V3.xxx, CCS V4.xxxとANSIです。
ノート:これはコンパイルに影響するだけであって旧バージョンで利用出来ていたどのようなドライバー、ライブラリー及び
インクルード・ファイルにも変更を加えません。
- #device CCS2
- ADCのデフォルト・サイズはデバイスの分解能でセット(#device ADC=10、ADC=12等)
- boolean = int8 は: boolean = (int8 != 0) としてコンパイルされます。
- オーバーロード関数が必要な場合、オーバーロード・ディレクティブが必要です。
- ポインター・サイズは最初のバンクのアクセスでのみセットされます。(PCM *=8,PCB *=5)
- var16 = NegConst8はコンパイルで:var16 = NegConst8 & 0xFF(シグナル拡張なし)
- WDT/NOWDT/NOLVPヒューズは自動的にセットされません。
- rom修飾子は _romをコールします。
- boolean = int8 は: boolean = (int8 != 0) としてコンパイルされます。
- #device CCS3
- ADCデフォルトは8ビット(#device ADC=8)
- boolean = int8 は: boolean = (int8 & 1) としてコンパイルされます。
- オーバーロード関数が必要な場合、オーバーロード・ディレクティブが必要です。
- ポインター・サイズは最初のバンクのアクセスでのみセットされます。(PCM *=8,PCB *=5)
- var16 = NegConst8はコンパイルで:var16 = NegConst8 & 0xFF(シグナル拡張なし)
- rom修飾子は _romをコールします。
- boolean = int8 は: boolean = (int8 & 1) としてコンパイルされます。
- #device CCS4
- ADCデフォルトは8ビット(#device ADC=8)
- boolean = int8 は: boolean = (int8 & 1) としてコンパイルされます。
- オーバーロード・ディレクティブ無しで関数をオーバーロード出来ます。
- もし、デバイスがRAMの1バンク以上持たない場合は、デフォルトのポインターサイズは16 (#device *=16がデフォルト)
- var16 = NegConst8は最適なシグナル拡張を実行
- 自動#fusesコンフィギュレーション(次のセクションを参照して下さい。)
- boolean = int8 は: boolean = (int8 & 1) としてコンパイルされます。
- #device ANSI
- CCSのconst(定数)修飾子は常にデータがプログラム・メモリーに置かれることを意味し、そのデータは `読取専用`です。
これはconst(定数)修飾子が単純に `読み取り専用`としているANSI定義には従っていません。
- CCS4と同じ。しかし、もし、ANSI標準と違ったものが見つかりますとANSIに変更されます。
- データはデフォルトでSIGNED, 他のモードは全てデフォルトはUNSIGNED
- 定数(const)修飾子は読取専用RAMです。プログラム・メモリーに 置かれません。(プログラム・メモリに置くにはrom修飾子を使用)
- コンパイルはデフォルトでケース・センシティブ
- 定数文字列は関数にパスすることが出来ます。(#device PASS_STRINGS_IN_RAM)
- データはデフォルトでSIGNED, 他のモードは全てデフォルトはUNSIGNED
- ADCのデフォルト・サイズはデバイスの分解能でセット(#device ADC=10、ADC=12等)