プログラムのバグは、直ってしまえばそれでハッピーだが、バグによる現象を解析しておくと、次回に似たような現象に出会ったときにその原因の特定に役立つというメリットがある。余裕のあるときには、きちんと検証しておくとスキルアップにつながるはず。
ということで、連休で、しかも台風直撃でどこにも出かけられないので、解析してみよう。
//****************************************
// TMR1×4(262ms毎)処理
// LED表示切替(262ms毎) 4秒サイクル
// MPPTターゲット電圧再設定(2分毎:262ms×16×28)
// 正常状態:●●●●○○○○・・・・(後半はモードを表す)
// 異常状態:●○●○●○●○・・・・(後半はモードを表す)
// 特殊状態:●●○○●●○○・・・・(後半はモードを表す)
void disp_and_mppt_reset( void ){
static unsigned char led_count; // LED表示切替カウンタ
WPUA3 = (led_led>>led_count)&&(0b01); // led_count番目のビットが1:点灯、0:消灯
led_count++; // カウントインクリメント
if(!(led_count-LED_DISP_CYCLE)) { // 約4sサイクル
led_count = 0; // カウンタクリア
}
}
上記で、led_ledという変数には、例えば「0101010101010101」という2進数が設定されているとする。それぞれのビットが「1」のところでLEDが点灯、「0」のところでLEDが消灯するように動作させたい。
led_countは、この関数が呼ばれる毎に+1ずつされる。これは、led_ledのビットを選択する働きをもつ。
led_count の値は、led_led のビット位置を以下のように指し示す。
led_count 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
led_led 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
例えば、led_count=4の場合は、led_ledの右端から5番目のビットを指し示す。
これが、「0」か「1」かを判断する。
正しい文 (led_led>>led_count)&(0b01) の動作は、以下のようになる。
led_led を led_count分右にシフトする。シフトとは、1ビットずつ右にずらして、左からは0で埋めることである。led_count=3だと、以下のようになる
0101010101010101 右シフト3ビット 00000101010101010 赤文字部分が左から埋められた
これを1とビット演算「&」(アンド)する。同じ位置のビットがともに「1」の場合は「1」、どちらかが「0」の場合は「0」となる。例の場合は、以下のようになり、結果は「1」となる。
led_led 0000101010101010
0b01 0000000000000001
片方が「0」なので「&」の結果は「0」となる。
こうして、led_ledのビットを一つずつ右にずらしながら1、0を確認し、一番左まで来たら、次は一番右に戻り、繰返す。
さて、これが、「&&」の場合はどういう動作になるか。
「&&」は、論理和といって、二つの変数(ビットではなく、全体)がともに「0」以外のときに「1」となり、その他は「0」となる。上記の場合は、以下のようになる。
led_led 0000101010101010 0でない
0b01 0000000000000001 0でない
よって「&&」の結果は「1」となる。
「&」と「&&」では、異なった結果となる。
今回のおかしな現象としては、本来はチカチカと点滅して欲しいのに、ずっと点灯しっぱなしというもの。上記の結果を考えれば、ほとんどの場合で、「1」となり、点灯する事になるという現象が理解できる。
でも、たまに、消灯する場合もあった。
led_led の値は、以下のようにいくつかのパタンがあった。
STOP 0000001100001111 // 入力電圧不足休止中(duty=0)
STANDBY 0101010100001111 // 電力供給可能(負荷がない状態)(duty=0)
MPPT 0001000100001111 // PV最大電力点追従モード
RECOVERY_WAIT 0000111100001111 // PWM停止後に出力電圧低下待ち
CALIBRATION 0011001100110011 // キャリブレーション
ERR_VIN_OVER 0001001101010101 // 入力電圧上限オーバー(緊急停止)
ERR_VCTL_OVER 0010001101010101 // 制御電圧上限オーバー(緊急停止)
ERR_VOUT_DANGER 0100001101010101 // 出力電圧上限オーバー(緊急停止)
ERR_INVALID_INT 0101010101010101 // 割込み異常
この中で、例えば以下のように、左側に”0″が複数連続している場合、10ビットシフトした以降は「0」となるため、「&&」の結果「0」となる。
0000001100001111
10ビット分シフトするまでは点灯、11~15ビット分は消灯という動作となる。
なるほど。まさに、現象に現れたとおりとなる。当り前なんだけど。
以上のように今回のバグにより現れた現象が、このミスによるものであることが特定できた。