レジスタの特定ビットのみ設定する | てすとぺーじ v0.0.8

めんどくさいAVRのレジスタアクセス(ビット演算)を解決します。

AVRのレジスタ操作はめんどくさい

AVRでのレジスタ操作はとてもめんどくさいです。
ある特定のビットだけ操作したい場合などは特にめんどくさいです。
以下のようなマクロを組み合わせて、がんばって設定することになると思います。

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
    // ====== PORTD.0をセット
    PORTD    |= _BV(0);
    // ====== PORTD.1をクリア
    PORTD    &= ~_BV(1);
    // ====== PORTD.1, 2をクリア
    PORTD    &= ~(_BV(1) | _BV(2) );
    // ====== PIND.1がセットされているか判断
    if( bit_is_set( PIND, 1 )){ nop(); }
    // ====== PIND.2がセットされるまでビジーループ
    loop_until_bit_is_set( PIND, 2 );
このコードは CodePublisher で生成しています

これは直感的にわかりずらく、なんとか解決したいです。
しかも潜在的にリードモディファイライト処理になる場合が多く、
微妙なタイミングで発生する、発見しづらい不具合に繋がりやすいです。

 

自分でI/Oアドレスをマッピングしてしまおう

掲題のとおり自分でマッピングしてしまうことで全て解決します。
ここではマッピングの仕方について記述します。以下を参考にして下さい。

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
// =============================================
//    ビットレジスタ用共用体
// =============================================
typedef union{
    // ▼====== 全ビットアクセス用メンバ
    unsigned char    all    : 8;
    // ▼====== 各ビットアクセス用フラグ
    struct{
       unsigned char    b0 : 1;
       unsigned char    b1 : 1;
       unsigned char    b2 : 1;
       unsigned char    b3 : 1;
       unsigned char    b4 : 1;
       unsigned char    b5 : 1;
       unsigned char    b6 : 1;
       unsigned char    b7 : 1;
    }bit;
}SFR8;
// =============================================
//    ポートDレジスタのアドレスマッピング
// =============================================
// ====== ポートD入力レジスタ
#define R_PIND    (*(volatile SFR8 *)0x29u)    // ← 0x29がPINDのI/Oアドレス
// ====== ポートD方向レジスタ
#define R_DDRD    (*(volatile SFR8 *)0x2Au)    // ← 0x2AがDDRDのI/Oアドレス
// ====== ポートD出力レジスタ
#define R_PORTD    (*(volatile SFR8 *)0x2Bu)    // ← 0x2BがPORTDのI/Oアドレス
このコードは CodePublisher で生成しています

 

マッピングしたレジスタのアクセス方法

こうやってアクセスできます。ちょー便利。

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
int     main( void )
{
    // ====== PORTDを全て出力に設定
    R_DDRD.all        = 0xFFu;
    // ====== PORTD.1だけ入力に設定
    R_DDRD.bit.b1    = 1u;
    // ====== PORTD全てLow出力(.1は入力のため関係ない)
    R_PORTD.all        = 0x00u;
    // ====== PORTD.1の入力チェック
    if( R_PIND.bit.b1 ){
        DEBUG_MSG("PORTD.1 is Hi-Level!!");
    }
}
このコードは CodePublisher で生成しています

 

複数ビットのアクセス

ビットフィールドでマッピングしているので、以下のように複数ビットのアクセスも簡単です。

1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
// =============================================
//    クロック分周レジスタのマッピング
// =============================================
#define R_CLKPR        (*(volatile CS_CLKPR *)0x61u)
typedef union{
    // ▼====== 全ビットアクセス用メンバ
    unsigned char    all;
    // ▼====== ビット単位アクセス用
    SFR8    byte;
    // ▼====== データ単位アクセス
    struct{
        // ====== クロック分周設定ビット
        unsigned char    B_CLKPCE : 4;
        // ====== RESERVE
        unsigned char    reserve  : 3;
        // ====== 分周変更許可ビット
        unsigned char    B_CLKPS  : 1;
    }bit;
}CS_CKLPR;
// =============================================
//    クロック分周レジスタの設定
// =============================================
int    main( void )
{
    // ====== 分周変更許可
    R_CLKPR.bit.B_CLKPS   = 1u;
    // ====== 分周変更
    R_CLKPR.bit.B_CLKPCE  = 0u;    // 1^0分周(分周なし)
    R_CLKPR.bit.B_CLKPCE  = 1u;    // 2^1分周(2分周)
    R_CLKPR.bit.B_CLKPCE  = 2u;    // 2^2分周(4分周)
    ..
    R_CLKPR.bit.B_CLKPCE  = 10u;   // 2^10分周(1024分周)
    // ====== ビット単位でアクセスもできる
    R_CKLPR.byte.bit.b7   = 1;
}
このコードは CodePublisher で生成しています

コメント

  • コメントはまだありません。

コメントを投稿する

コメントを投稿する場合は以下に注意してください。

・記事に関係する内容であること
・特別な理由がない限り「全体公開」であること

これさえ守っていただければどんな内容でも投稿可能です。
皆様の質問やコメントは他の方の問題解決に繋がりますのでご協力ください。

コメント投稿フォーム
名前
メール
URL
コメント
閲覧制限

コメントを投稿するには、以下の問題に答えてください。
問題:「このサイト名をカタカナ六文字にして入力してください」