volatileはお節介焼きのコンパイラにぶぶ漬けをお出しする修飾子です。*1
たとえば、以下のような処理をしたいとします
- 書き込み先:0x12345678 (外部インタフェイスへのアドレス)
- 書き込むもの:(順番に)0x31,0x32,0x33 //ASCIIの'1','2','3'
この内容ならポインタ使えばいーじゃん楽勝じゃーんと
unsigned char* UART0_THR = (unsigned char*)0x12345678 ; *UART0_THR = 0x31 ; // '1' *UART0_THR = 0x32 ; // '2' *UART0_THR = 0x33 ; // '3'
などとやると、UART0_THRには'3'しかいかない「こともあります」。もしくは順番が入れ替わったり。
……すべてコンパイラのお節介が原因です。
例えば、起動コマンド等々でこの順番に値を入れないけない場合……知らなければ原因不明で詰みますね。
そんなあなたに、volatile。
講義ではまず見かけない、習ったとしても音速で忘れ去れれる子ですが……組み込み野郎にとっては背中を任せられる戦友です。
無難な記述法
毎回、間接参照演算子をつかって代入する方法です。
可読性が犠牲にならず、かつ「お節介」を阻止できます。
//最適化阻止のため、volatile化。 //(多少面倒でも、識別しやすい名前を付けること。) volatile unsigned char* UART0_THR = (volatile unsigned char*)0x12345678 ; //volatileなしだと、一番下の行しか実行されない。最終的に3が入ると認識されるから。 *UART0_THR = 0x31 ; // '1' *UART0_THR = 0x32 ; // '2' *UART0_THR = 0x33 ; // '3'
お上品でない方法その1 入れ子ポインタ
以下は、可読性の問題から「お上品でない」と言われるコードです。
次の例は、入れ子ポインタです。パッと見、わけがわかりません。
一回しか使わない、あるいはC言語コンテストでアスキーアートする必要性に駆られた場合はアリかもしれません。
*((volatile unsigned char*)0x12345678) = 0x31 ;
お上品でない方法その2 マクロ
次はマクロです。volatileの量が多い場合には、使うと幸せになれます。
//これはただのマクロ #define UART0_THR (*((volatile unsigned char*)0x12345678)) //関数マクロ。邪道なので、可能な限り使わない。 #define UART0_THR(a) (*((volatile unsigned char*)0x12345678)) = a UART0_THR = 0x31 ; a = UART0_THR ; //MISRA的には正しくないが、動作完了待ちの場合は以下を入れるのも手。 //というのも、書き込みが完了していなければ(volatileにより)以下の命令は実行されないから。 UART0_THR; //meaning less
と、こんな具合に素敵なシンボルなのです。最適化してほしくない箇所には是非つかってください。
ただ無闇やたらにvolatile化するのも考え物です。
コンパイラは頭がいい人が設計したものなので、基本的に信じて最適化はまかせましょう。
適材適所で使い分けてください。
*1:実際のぶぶ漬け〜は都市伝説の類らしいですが