AVRのコンパイラ(AVR GCC)で悩んでいる内容、こんなのです。
(半角スペースだと圧縮されてしまうので全角にしています)
割り込みルーチン内で処理されるワードデータを読み書き
するために作った関数です。
<1>は比較のための関数でタイマーを直接読んでます。
<2>は書き込み処理、
<3>が問題の読み出しルーチンです。
割り込みをいったん禁止してからワードデータを読み出して
います。
AVRマイコンのOPコードは16ビットですが、メモリの読み書きを
処理できるのは8ビット単位ですんでしかたありません。
= = = = = = = = = = = = = =
/***** <1>タイマー1(word)データ読み出し *****/
uint16_t read_timer1(void)
{
uint8_t sreg;
uint16_t d;
sreg = SREG;
cli(); // 割り込み禁止
d = TCNT1; // タイマー読み出し
SREG = sreg; // ◆割り込み状態復帰
return d;
}
/***** <2>wordデータ書き込み *****/
void write_word(uint16_t *p, uint16_t d)
{
uint8_t sreg;
sreg = SREG;
cli();
*p = d; // wordで書き込み
SREG = sreg; // ◆
}
/***** <3>wordデータ読み出し *****/
uint16_t read_word(uint16_t *p)
{
uint8_t sreg;
uint16_t d;
sreg = SREG;
cli();
d = *p; // ★1
SREG = sreg; // ★2
return d;
}
= = = = = = = = = = = = = =
これをコンパイルして出てきたコードがこれ↓
= = = = = = = = = = = = = =
<1>
0000005a <read_timer1>:
5a: 8f b7 in r24, 0x3f ; 63
5c: f8 94 cli
5e: 2c b5 in r18, 0x2c ; 44
60: 3d b5 in r19, 0x2d ; 45
62: 8f bf out 0x3f, r24 ; 63 ◆
64: c9 01 movw r24, r18
66: 08 95 ret
<2>
00000068 <write_word>:
68: fc 01 movw r30, r24
6a: 8f b7 in r24, 0x3f ; 63
6c: f8 94 cli
6e: 71 83 std Z+1, r23 ; 0x01
70: 60 83 st Z, r22
72: 8f bf out 0x3f, r24 ; 63 ◆
74: 08 95 ret
<3>
00000076 <read_word>:
76: fc 01 movw r30, r24
78: 8f b7 in r24, 0x3f ; 63
7a: f8 94 cli
7c: 8f bf out 0x3f, r24 ; 63 ★2
7e: 80 81 ld r24, Z ★1
80: 91 81 ldd r25, Z+1 ; 0x01
82: 08 95 ret
= = = = = = = = = = = = = =
★1と★2の順番が問題。
リターンししなにメモリを読もうとして禁止した割り込みが先に再開され
てしまっています。
これではあきません。
特定アドレスのデータを直接示すと大丈夫なんですが、ポインタにすると
このようにアウト。
書き込みではうまく行くのに…
そして、コンパイラオプションを「最適化無し」にすると、こんなコードが
でてきて★1、★2の順になりうまく行くようになります。
けど、こんなコードでマイコンを動かしたくないですよ。
= = = = = = = = = = = = = =
★最適化コンパイルオプション「-O0」で
(最適化なし)
000000d8 <read_word>:
d8: df 93 push r29
da: cf 93 push r28
dc: 00 d0 rcall .+0 ; 0xde <read_word+0x6>
de: 00 d0 rcall .+0 ; 0xe0 <read_word+0x8>
e0: 0f 92 push r0
e2: cd b7 in r28, 0x3d ; 61
e4: de b7 in r29, 0x3e ; 62
e6: 9d 83 std Y+5, r25 ; 0x05
e8: 8c 83 std Y+4, r24 ; 0x04
ea: ef e5 ldi r30, 0x5F ; 95
ec: f0 e0 ldi r31, 0x00 ; 0
ee: 80 81 ld r24, Z
f0: 8b 83 std Y+3, r24 ; 0x03
f2: f8 94 cli
f4: ec 81 ldd r30, Y+4 ; 0x04
f6: fd 81 ldd r31, Y+5 ; 0x05
f8: 80 81 ld r24, Z ★1
fa: 91 81 ldd r25, Z+1 ; 0x01
fc: 9a 83 std Y+2, r25 ; 0x02
fe: 89 83 std Y+1, r24 ; 0x01
100: ef e5 ldi r30, 0x5F ; 95
102: f0 e0 ldi r31, 0x00 ; 0
104: 8b 81 ldd r24, Y+3 ; 0x03
106: 80 83 st Z, r24 ★2
108: 89 81 ldd r24, Y+1 ; 0x01
10a: 9a 81 ldd r25, Y+2 ; 0x02
10c: 0f 90 pop r0
10e: 0f 90 pop r0
110: 0f 90 pop r0
112: 0f 90 pop r0
114: 0f 90 pop r0
116: cf 91 pop r28
118: df 91 pop r29
11a: 08 95 ret
= = = = = = = = = = = = = =
その後、あれこれしていましたら、あっさり解決しました。
関数の引数をこんな具合に変えたらOK。
「uint16_t read_word(volatile uint16_t *p)」
魔法の言葉「volatile」。 出てきたコードがこれ↓
= = = = = = = = = = = = = =
00000076 <read_word>:
76: fc 01 movw r30, r24
78: 8f b7 in r24, 0x3f ; 63
7a: f8 94 cli
7c: 20 81 ld r18, Z ★1
7e: 31 81 ldd r19, Z+1 ; 0x01
80: 8f bf out 0x3f, r24 ; 63 ★2
82: c9 01 movw r24, r18
84: 08 95 ret
= = = = = = = = = = = = = =
★1と★2の順番がおもわくどうりになりました。
|
もともと容量の小さなマイコンですんで、複雑なことをさせる
必要がなかったということもあります。
先日のArduinoでコンパイラを初体験。
その流れでAVR studioでのコンパイルとなったのです。
コンパイラがはき出すコード、どんなチップでもクリチカルな所は
機械語での動きを確かめておかなくてはなりません。
賢いコードが出てきて感心することもあれば「やめてくれ〜」って
なこともあります。