C言語開発で,IOレジスタ直叩きの際,書き込み完了待機をするには

こんにちは

初めてこのフォーラムを利用させていただきます.

 RXマイコン(RX621)とマトリクス接続されたスイッチ(秋月)を接続し,キー入力を取得しようとしているのですが,
おそらくIOレジスタの読み込みが早すぎて出力の変更が反映される前にポート読みされていないようなのです.
ハードウェアマニュアルP131にはIOレジスタ書き込み時の注意事項があるので,これが原因ではないかと思っているのですが,対処方法がわかっていません.

>>引用
このような場合には、I/O レジスタの書き込みを行った後、以下の手順で書き込みの完了を待ってから、後
続の命令を実行するようにしてください。
(a) I/Oレジスタの書き込み
(b) 書き込んだI/Oレジスタの値を汎用レジスタに読み出し
(c) 読み出し値を使って演算を実行
(d) 後続の命令を実行

 

秋月のキーマトリクスキット
http://akizukidenshi.com/catalog/g/gK-12229/
を使用しています.

PORTDの4~6bit目を出力モードにし,マトリクスのXYZへ入力,
PORTDの0~3bit目を入力モードにし,マトリクスのABCDへ接続

 

ソースのコメントアウトされたdelay_ms(1)を入れると正しく動作します.
最初,レジスタを読めばいいのかと思い,以下のようにwhileをdelay_msの代わりに挿入してみましたがこの方法ではうまくいきませんでした.

while(PORTD.DR.BYTE != 0xb0);


以下ソース

※KEY_XDなどはdefineで整数を割り振っています


int8_t GetMatrixInput(){
    int8_t ret = 0;
 
    uint8_t a;
    PORTD.DR.BYTE = 0xb0;   //1011
    //delay_ms(1);
 
    a = PORTD.PORT.BYTE & 0x0f;
    if(!(a & 1)){
        ret = KEY_XD;
    }else if(!(a & 2)){
        ret = KEY_XC;
    }else if(!(a & 4)){
        ret = KEY_XB;
    }else if(!(a & 8)){
        ret = KEY_XA;
    }
    if(ret)
    return ret;


    PORTD.DR.BYTE = 0xd0;   //1101
    //delay_ms(1);
 
    a = PORTD.PORT.BYTE & 0x0f;
    if(!(a & 1)){
        ret = KEY_YD;
    }else if(!(a & 2)){
        ret = KEY_YC;
    }else if(!(a & 4)){
        ret = KEY_YB;
    }else if(!(a & 8)){
        ret = KEY_YA;
    }
    if(ret)
    return ret;


    PORTD.DR.BYTE = 0xe0;   //1110 0000
    //delay_ms(1);
 
    a = PORTD.PORT.BYTE & 0x0f;
    //return a;
    if(!(a&1)){
        ret= KEY_ZD;
    }else if(!(a&2)){
        ret= KEY_ZC;
    }else if(!(a&4) ){
        ret= KEY_ZB;
    }else if(!(a&8)){
        ret = KEY_ZA;
    }

    return ret;
}
Parents Reply
  • お返事ありがとうございます
    恥ずかしながらその発想はありませんでした

    今回のソースではGetMatrixInputの中で3回列を変えながらスキャンしているのでそのままでは適用できませんが,解決策として大変面白いと思いました
Children
  • チョコです。
    キースキャンについては、Renesasの「FAQ(よくあるお問い合わせ) インフォメーション」というPDFファイルの293ページから解説があるので、参考にしてみてください。
    8bitの78K0のアセンブラでのプログラムですが、説明の部分だけでも役に立つと思います。
    3wayrenesasさんがコメントした方法は、296ページの「【ちょっと注意 2】 」にタイミングチャート付きで説明されています。

    このPDFは、RenesasのTopページで"FAQ"で検索し、「キーワード検索」のページのドキュメントには6件該当でそのうちの4件だけが表示されるので、「ドキュメントをもっと見る」をクリックすると見れるようになります。
    ちょっとサイズが大きいですが、一度ダウンロードされることをお勧めします。
  • こんなPDFがあったんですね
    ノウハウの塊で大変参考になります


    実験結果のご報告ですが,試しに
    PORTBとPORTCを直結して,次のようなソースを試してみました.


    PORTB.DDR.BYTE = 0xff;
    PORTC.DDR.BYTE = 0x00;
    PORTC.ICR.BYTE = 0xff;
    delay_ms(10);

    PORTB.DR.BYTE = 0x55;
    uint8_t data = PORTC.PORT.BYTE;
    xprintf("data(1)=%X\n", data);
    delay_ms(1);
    data = PORTC.PORT.BYTE;
    xprintf("data(2)=%X\n", data);
    while(1);

    実行結果は,
    data(1)=55
    data(2)=55

    となり,ポートの書き込み完了を待機することをせずとも問題がないということがわかりました.
    まだオシロでのチェックはできていませんが,今回の件はマトリクスまでの容量チャージが追い付いていないことが原因と結論付けられそうです.
    となると教えていただいた手段含め,何らかの方法で容量チャージ分の時間を待たねばならないようです.

    Arduinoを使っていたころはそもそもdigitalWrite等が激遅だったので気にする必要がなかったのですね.大変勉強になりました.