[PIC]HI-TECH Cで一定時間スリープさせる

PICでは、HI-TECH Cコンパイラをを使用した、C言語による開発を行う事ができます。
C言語で開発を行う場合、プログラム中で一定時間処理をスリープさせるにはどうしたら良いでしょうか?


HI-TECH Cの環境では、主に以下の4つの手法があります。

1. 空ループを回す
2. _delay()マクロを使用する
3. __delay_ms()マクロを使用する
4. __delay_us()マクロを使用する



上記のうち2,3,4のマクロはHI-TECH Cがあらかじめ用意してくれているものです。
__delay_ms()と__delay_us()は先頭のアンダーバーが2つですが、_delay()は1つで有ることに注意してください。また、これらのマクロはhtc.hで定義されているため、ファイルの先頭に「#include <htc.h>」の記述が必要です。


それでは、各パターンの具体的な方法について確認していきます。

1. 空ループを回す


空ループを回すパターンは、MPASMのアセンブラでコードを書いた経験がある人にとってはわかりやすい方法です。ループ内の処理は、NOP()関数を呼んでおけばアセンブラのNOP命令が1つ実行できます。

#include <htc.h>
...
 
int loop = 100;
while( loop-- > 0 ) {
    NOP();
}



上記のコードをコンパイルすると、このようなアセンブリになりました。

12:                 int loop = 100;
   3C9    3064     MOVLW 0x64
   3CA    1283     BCF 0x3, 0x5
   3CB    008F     MOVWF 0xf
   3CC    3000     MOVLW 0
   3CD    0090     MOVWF 0x10
13:                 while( loop-- > 0 ) {
   3CE    2BD1     GOTO 0x3d1
   3D1    30FF     MOVLW 0xff
   3D2    1283     BCF 0x3, 0x5
   3D3    078F     ADDWF 0xf, F
   3D4    1803     BTFSC 0x3, 0
   3D5    0A90     INCF 0x10, F
   3D6    30FF     MOVLW 0xff
   3D7    0790     ADDWF 0x10, F
   3D8    1F90     BTFSS 0x10, 0x7
   3D9    2BDB     GOTO 0x3db
   3DA    2BDC     GOTO 0x3dc
   3DB    2BCF     GOTO 0x3cf
14:                     NOP();
   3CF    0000     NOP
   3D0    2BD1     GOTO 0x3d1
15:                 }



空ループで対応する場合は、生成されるコードとクロック周波数から待たせたい時間を逆算する必要があるので、待機時間が決まっている場合は面倒になります。

2. _delay()マクロを使用する


_delay()マクロを使用する場合も、基本的には空ループと同様なのですが、引数を指定できる点が違います。例えば以下のコードで100命令分の待ちを作ることが出来るので、空ループの時に考慮が必要だったループ判定はNOPで待機時間の計算についての面倒さから開放されます。

void main( void )
{
    _delay(100);
}





_delay()関数は、大きすぎる値を指定すると以下のエラーが発生する場合があるので注意が必要です。
例えば、下記のコードをコンパイルすると…

void main( void )
{
    _delay(50462463);  // ok
    _delay(50462464);   // ng
}



下側の_delay()命令でエラーになります。

Error   [1274] test\main.c; 11. delay exceeds maximum limit of 50462464 cycles


エラーメッセージにも表示されていますが、引数に50462464以上の値(16進数だと0x301FF00)を指定するとコンパイルが出来ません。

マイコンでこれほどの時間をスリープさせる事はあまり無いかもしれませんが、もし多くの時間をスリープさせたい場合は以下のようにループにすれば対処できます。

    // これはNG
    _delay(50462464);
 
    // これはOK
    int loop = 0;
    for ( loop = 0; loop < 1000; loop++ ) {
        _delay(50000);
    }




また、_delay()マクロに指定する値には定数での指定が必要です。
変数を使用するとエラーになります。

int waitTime = 100;
_delay(waitTime);



上記のコードをコンパイルした時に出力されるエラーメッセージです。

Error   [1387] test\main.c; 13. inline delay argument must be constantError   
         [712] test\main.c; 13. can't generate code for this expression



定数なら大丈夫なので、以下のようにdefineにしておけばエラーになりません。

#define WAIT_TIME 100
_delay(WAIT_TIME);




3. __delay_ms()マクロを使用する


_delay()命令をさらに便利にしたものが__delay_ms()です。
これは時間指定(ミリ秒)でスリープさせる事が可能です。

__delay_ms(1000); // 1秒待機



時間指定で待たせる場合、PICは時間をクロックから算出します。
PICの動作は、範囲内であれば任意のクロックが入力可能なので、プログラムでクロック周波数を定義する必要があり、具体的には_XTAL_FREQをdefineします。値はhzで指定するので、例えば8Mhzの場合、以下の定義となります。

#define _XTAL_FREQ 8000000



もし、この定義を書き忘れると以下のエラーが出力されます。

Error   [192] test\main.c; 22.32 undefined identifier "_XTAL_FREQ"




また、_delay_ms()も内部的に_delay()をコールしているため、同様の制限を受けます。
手元の環境だと、_delay_ms()に対して10000(10秒)を与えた時はOKでしたが、100秒だとエラーになりました。__delay_ms()の場合も、多くの時間をスリープさせたい場合は以下のようにループにすれば対処できます。

// これはNG
__delay_ms(100000); 
 
// これはOK
int loop = 0;
for ( loop = 0; loop < 10; loop++ ) {
    __delay_ms(10000); 
}



4. __delay_us()マクロを使用する

__delay_us()は__delay_ms()と基本的には同じです。
異なるのは、待ち時間の単位がマイクロ秒である点だけで、制限も同様です。




今回は、PICのプログラムをC言語で開発する際に、一定時間スリープさせる方法を紹介しました。
空ループによる手法もありますが、待ち時間が決まっている場合は、提供されている__delay_ms()や__delay_us()マクロを使用することで、簡単に処理を記述する事が出来ます。

関連記事

コメントを残す

メールアドレスが公開されることはありません。