組み込み開発をやっていると、ICの応答や安定待ちで数usのウェイトを入れたいことが良くある。
厳密な時間を管理したい訳ではなく、「ざっくりとこの程度の時間を待ちたい」という処理なのでタイマを使うのも微妙。そんな時は、ソースコード中にインラインアセンブラで記述してやる。
プログラミングマニュアルにMCUの命令セットが大抵記載されているので、そこから"何もしない"命令を探してインラインアセンブラで以下のように記述する。
// STM32F4の場合
// 1クロックだけ待つ
asm("NOP");
100MHzのクロックであれば、asm("NOP");で10nsのウェイトになる。以下はサンプルコード。これをforやwhileで回したりすると繰り返しの分だけ余計に処理がかかるため、思った通りの待ち時間にならないことに注意。
//これはダメな例
void delay_us(uint32_t i){
//NOP100回@100MHz = 1us
i=i*100;
while(i>0){
asm("NOP");
i--;
}
}
繰り返し処理や減算処理以外にも、関数の呼び出し処理があるため厳密には合わない。asm("NOP");を100回まとめて書いたものでストップウォッチで実測してみると、delay_us(10000000);で10.8秒くらい。NOPを90回に減らして調整。
本当はwhile文、iの減算処理にかかる処理数をアセンブラレベルで追いかければ正確にでるが、ざっくりとしたウェイトなのでこれで十分かな、と。(もちろん、割込みが入ればウェイトの時間はのびる…)
//妥協できる例
void delay_us(uint32_t i){
while(i>0){
//NOP100回@100MHz = 1us
//実際はwhile文、iの減算の処理がある。実測でNOP90回が1usに相当する。
asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP");
asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP");
asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP");
asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP");
asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP");
asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP");
asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP");
asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP");
asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP");
asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP");
i--;
}
}