ホーム作品紹介問題のある作品マイコン・メモご意見管理人紹介
プログラム作成例へ
H8/3694Fの使い方へ
RX621/RX62Nの使い方へ
RX220の使い方へ
マイコン・Cプログラミング メモ
■ H8用Cコンパイラで引数の渡し方(H8/300Hの場合)
関数の引数はfunc(char a, char b, short c, long d)のように、小さいビット幅から並べて作成した方が良い。
→ H8/300Hは1つの汎用レジスタを8bit×2,16bit×2,32bit×1のように分割可能で、コンパイラはレジスタ分割を利用して引数を左から8bit→16bit→32bitの順にレジスタに割り振り、有効利用しようとするため。
(参考:レジスタで渡し切れない引数はスタック渡しとなる)


■ 同じ処理を繰り返すループ制御命令
必ず1回は実行されるなら、for,while より do~while が良い。
→ 一般にループ制御命令は、ループ内処理の下(後ろの部分)にループ条件を判定するコードが生成される。したがって、ループ内処理を実行するかどうかわからない for や while は、まずループ条件判定を行うためにループ内処理の下に分岐する。
一方、do~while は最低1回は処理を実行することがわかっているのでループ内処理の下に分岐する必要がない、したがって分岐に必要なコードも時間も節約できる。
(備考:コンパイラの違い、またはコンパイラの最適化機能により上記と異なる場合もある)


■ TRUEと比較しない
変数xxxが TRUE だったとき分岐したい場合
if(xxx == TRUE)  … ダメ
if(xxx != FALSE)  … OK
→ TRUEは「0以外の値」なので比較ができない

■ 構造体を引数とする関数に定数で渡す
下記のように const 宣言した定数の構造体を定義して渡す。
struct exST{~メンバー~}; … 構造体の宣言
void func(struct exST argument); … 構造体を引数とする関数のプロトタイプ
void main(){
  const struct exST ex={定数メンバー}; … 定数の構造体を定義
  func( ex ); … 関数呼び出し
}
(参考:一般に構造体はサイズが大きい場合が多いので、コードサイズが大きくなったり実行速度が落ちたりするため、構造体自体を引数に渡すことは少なく、構造体へのアドレスを渡すことが多い。)

■ アドレスを関数として呼ぶ
((int (*)(short))adrs)(arg); … 戻り値の型int,引数の型short,アドレスadrs,引数arg
→ 例
int answer; … 戻り値を入れる変数
answer = ((int(*)(int)) 0x0123456 )(789); … 0x0123456を関数のアドレスとし、引数789を渡して実行
(注:これは特殊な用途)


■ ポインタ変数 p の宣言
int *p; … int型を指すポインタ
int *p[10]; … int型を指す要素10個のポインタ配列
int (*p)[10]; … 要素10個のint型配列へのポインタ
int **p; … int型ポインタ配列を指すポインタ

■ ポインタ変数での const の使い方
const char* p1; … p1が示すところは変更できない。p1=string;などはできるが、*p1='A';などはエラー。
char* const p2; … p2は変更できない。*p2='B';などはできるが、p2=string;などはエラー。
const char* const p3; … p3が示すところもp3自体も変更できない。*p3='C';や p3=string;などはエラー。

■ 関数へのポインタ変数
int (*func)(short, ...); … 戻り値の型int,変数名func,引数の型short, ...
→ 例
int Calculation(int argument); … 実行する関数のプロトタイプ
int (*func)(int, int); … 関数へのポインタ変数
void main(void)
{
    int answer; … 戻り値を入れる変数
    func = Calculation; … 関数のアドレスをポインタ変数に入れる
    answer = func(123, 456); … 関数の実行
    ~
}


■ 関数へのポインタの配列定義(テーブルジャンプを実行)
void (*const FuncTbl[n])( int )={関数名, ...}; … 戻り値の型void,関数配列名FuncTbl,配列要素数n,引数の型int
→ 例
static int (*const FuncTbl[ 5 ])( int )={
    func_1, … 1番目の関数のアドレス
    func_2, … 2番目の関数のアドレス
     ~
    func_5 … 5番目の関数のアドレス
};
void main()
{
    int result;
    result = FuncTbl[ x ](123); … x番目の関数を実行(x=0~4)
    ~
}
(備考1:関数配列内の関数func_1~func_5は、戻り値と引数がそれぞれ同じ型であること、各関数のプロトタイプ宣言が必要。)
(備考2:const 宣言があるため、この関数配列はROM中に作られる。)


■ 文字列へのポインタ配列(文字列一覧)の定義
const char* const stringTbl[m]={文字列, ...}; … 文字列への要素数mのポインタ配列,ポインタ配列名stringTbl
→ 例
static const char*
const stYear[12]={ … 要素数12のポインタ配列を定義
    "January",
    "February",
    "March",
    "April",
    "May",
    "June",
    "July",
    "August",
    "September",
    "October",
    "November",
    "December",
};
(参考1:const 宣言(赤字)がないと stYear は変数とみなされてRAM中に作られ、リセット直後にROM中に作られた初期値(=各文字列のアドレス)がコピーされる。それは効率が悪いため、stYear を書き変える必要がなければ const 宣言としROM中に作ることを勧める。)
※各文字列の文字数が全て同じであれば、ポインタ配列より文字列配列の使用を勧める(下記)。
→ 例
static const char arWeek[7][4]={ … 文字列は3文字だが終端を示す \0 が入るので[4]とする
    "SUN",
    "MON",
    "TUE",
    "WED",
    "THU",
    "FRI",
    "SAT",
};


■変数のチェックでループから抜けないことがある
static volatile int flag = 0;
/* ある割り込みでフラグを立てる関数 */
void int_xxx(void)
{
    flag = 1;
    ~
}
/* フラグが立つのを待って、ある処理をする関数 */
void func_yyy(void)
{
    /* フラグを待つ */
    while( flag == 0 ){  //※ ループから抜けないことがある!
         ~
    }
    flag = 0;
    ~ある処理~
}
→ volatile(赤字)がないと、割り込み処理関数 int_xxx( )でflagを '1' にしても、※部分のフラグ待ちループから抜けられない場合がある。
それは、コンパイラの最適化機能によって、※部分の待ちループの中でflagをメモリから再読み込みしないようになっていることが原因。
volatile(赤字)があると、flagに対する最適化を停止するので、常に読み込みが行われ '1' がセットされたことに気づくことができる。
(参考:フラグだけでなく、ソフト的なタイマをメモリ中に作って、割り込みでカウントする場合などでやりがちである。)


■記述漏れでもエラーにならないこともある
{
  ~
  /* 〇〇初期化 */
  aaa =
  bbb =
  /* △△関数呼び出し */
  func();
}
→ 上記のように変数代入式の記述漏れがあったとしても、関数func()が値を返すものであれば、下記と同じになりエラーとならない。
  aaa = bbb = func();


■AVRマイコンでは、定数データの取り扱いに注意(GCC使用の場合)
(1)constを指定してもROM中ではなくRAM中に配置される。そのため大きな定数では容量オーバーになる。
(2)RAMとROMはメモリ空間(マップ)が異なるため、ROM中にしたい場合は専用修飾子で定義し、専用の命令で読み出す。
(3)前記理由により、RAM中データまたはROM中データに対して、共通に使える関数を作る時は注意。
→例えば、
    const char array_xxx[10]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
としたとき、配列array_xxxはRAM中に作られ、リセット直後にROM中にある定数{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}がコピーされる※1
RAM中とはいえ、配列array_xxxはconst(=定数指定)なので、書き変えようとするとコンパイルでエラーとなるので使用上問題はない(誤動作等があれば書き変えられることはあり得る)。
通常、マイコンのRAM容量はROM容量より少ないので、ROM中に作るつもりで大量の定数を定義すると、RAM不足でエラーとなることがある。
ROM中に作りたい場合は次のように書く(配列以外でも同様)。
    const char array_xxx[10]
PROGMEM={1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    または
    const
PROGMEM char array_xxx[10]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
ただし、データの読み込みは、次のように書けない。
    ByteValue = array_xxx[n];    
← ダメ
専用関数(マクロ)を使って次のように書く。
    ByteValue = pgm_read_byte( &array_xxx[n] );
    16ビットデータの場合は
    WordValue = pgm_read_word( &array_yyy[n] );
ROM中のデータにアクセスするための PROGMEM や pgm_read_byte() などを使うには、あらかじめヘッダファイル
<avr/pgmspace.h>をインクルードしておく必要がある。
※1.この動作は、リンクされるライブラリが行うらしく、Cソースプログラムが作られるわけではない。ただし、生成されるリストファイル "XXXXX.lss" ではアセンブラソースとして見ることができる。

注意:ここに記載された内容(写真,図,表などを含む)の全てまたは一部を、無断で使用しないでください.
Attention: Please do not use all or part of contents (photograph,figure,list,etc) listed here without permission.