digitalWrite(pin, state) の引数はほとんど固定で使うことが多い (と思いますがどうでしょうか? http://lowreal.net/2015/12/14/1 こういうケースでは常に固定ですね) が、digitalWrite は実装上、実行時にいちいちテーブルを使って、ピン番号からどのポートのどのビットかをひいている。たいへん無駄である。

そして、digitalWrite() は1度の1つのビットしか設定できないという制約がある。これは大変こまる場合がある (同時に複数のビットを設定して出力を変化させようとしても、タイムラグができてしまう)

ということで、これらを解決できないかと思った結果 C++ のテンプレートメタプログラミングに手を出すことになった

前提

Arduino が使っている 8bit AVR では PORTB, PORTC, PORTD などと最大8bitのレジスタがあり、これら単位では同時に操作が可能になっている。

Arduino ではこれらのポート単位を隠蔽し、ピン番号という一連の番号によって区別するように抽象化されている。具体的には 0〜7 が PD, 8〜14 が PB というような感じ (standard なArduino の場合)

仕様

  • digitalWrite() とインターフェイス互換であること (静的であること以外)
  • 複数ビットを同時に操作できること
  • 指定したピン番号同士のポートが同一でない場合エラーとすること
// オリジナル。0ピンをLOWにする
digitalWrite(0, LOW);

// 作ったもの。0ピンをLOW, 1ピンをHIGH... とする (同時に)
WRITE_MULTI(
  0, LOW,	
  1, HIGH,
  2, LOW,	
  3, HIGH
);

コード

Gist

//#!ano build -f="-std=c++11 -Os" && /Applications/Arduino.app/Contents/Java/hardware/tools/avr/bin/avr-objdump -d .build_ano/uno/firmware.elf ;:
#include <Arduino.h>
// ARDUINO_MAIN で ifdef されているので外からみえない
constexpr uint8_t PB = 2;
constexpr uint8_t PC = 3;
constexpr uint8_t PD = 4;
// template version of Arduino builtin functions
constexpr uint16_t port_to_mode[] = {
NOT_A_PORT,
NOT_A_PORT,
(uint16_t) &DDRB,
(uint16_t) &DDRC,
(uint16_t) &DDRD,
};
constexpr uint16_t port_to_output[] = {
NOT_A_PORT,
NOT_A_PORT,
(uint16_t) &PORTB,
(uint16_t) &PORTC,
(uint16_t) &PORTD,
};
constexpr uint16_t port_to_input[] = {
NOT_A_PORT,
NOT_A_PORT,
(uint16_t) &PINB,
(uint16_t) &PINC,
(uint16_t) &PIND,
};
constexpr uint8_t digital_pin_to_port[] = {
PD, /* 0 */
PD,
PD,
PD,
PD,
PD,
PD,
PD,
PB, /* 8 */
PB,
PB,
PB,
PB,
PB,
PC, /* 14 */
PC,
PC,
PC,
PC,
PC,
};
constexpr uint8_t digital_pin_to_bit_mask[] = {
_BV(0), /* 0, port D */
_BV(1),
_BV(2),
_BV(3),
_BV(4),
_BV(5),
_BV(6),
_BV(7),
_BV(0), /* 8, port B */
_BV(1),
_BV(2),
_BV(3),
_BV(4),
_BV(5),
_BV(0), /* 14, port C */
_BV(1),
_BV(2),
_BV(3),
_BV(4),
_BV(5),
};
template<uint8_t port>
constexpr volatile uint8_t* portModeRegisterX() {
static_assert(port < sizeof(port_to_mode), "invalid port number");
static_assert(port_to_mode[port] != NOT_A_PORT, "invalid port number");
return (volatile uint8_t*)port_to_mode[port];
}
template<uint8_t port>
constexpr volatile uint8_t* portOutputRegisterX() {
static_assert(port < sizeof(port_to_output), "invalid port number");
static_assert(port_to_output[port] != NOT_A_PORT, "invalid port number");
return (volatile uint8_t*)port_to_output[port];
}
template<uint8_t port>
constexpr volatile uint8_t* portInputRegisterX() {
static_assert(port < sizeof(port_to_input), "invalid port number");
static_assert(port_to_input[port] != NOT_A_PORT, "invalid port number");
return (volatile uint8_t*)port_to_input[port];
}
template <uint16_t pin>
constexpr uint8_t digitalPinToBitMaskX() {
static_assert(pin < sizeof(digital_pin_to_bit_mask), "invalid pin number");
return digital_pin_to_bit_mask[pin];
};
template <uint16_t pin>
constexpr uint8_t digitalPinToPortX() {
static_assert(pin < sizeof(digital_pin_to_port), "invalid pin number");
return digital_pin_to_port[pin];
};
// multiple bit set function with cheking port consistency
template<uint8_t port, uint8_t mask, uint8_t vals>
constexpr void digitalWriteMulti_() {
volatile uint8_t *out = portOutputRegisterX<port>();
*out = (*out & ~mask) | vals;
}
template<uint8_t port, uint8_t mask, uint8_t vals, uint16_t pin, uint8_t val, uint16_t... Rest>
constexpr void digitalWriteMulti_() {
static_assert(digitalPinToPortX<pin>() == port, "all port must be same");
constexpr uint8_t bit = digitalPinToBitMaskX<pin>();
digitalWriteMulti_<
port,
mask | bit,
vals | (val ? bit : 0),
Rest...
>();
}
template<uint16_t pin, uint8_t val, uint16_t... Rest>
constexpr void digitalWriteMulti() {
constexpr uint8_t port = digitalPinToPortX<pin>();
return digitalWriteMulti_<
port,
0,
0,
pin,
val,
Rest...
>();
}
// hide template magic from surface
#define WRITE_MULTI(...) digitalWriteMulti<__VA_ARGS__>()
void setup() {
PORTD = (PORTD & 0b11110000) | (0b1010);
asm volatile ("nop"); // marker
// same as above
WRITE_MULTI(
0, LOW,
1, HIGH,
2, LOW,
3, HIGH
);
asm volatile ("nop");
// another port
WRITE_MULTI(
8, LOW,
9, HIGH,
10, LOW,
11, HIGH
);
asm volatile ("nop");
/** following is invalid
* static_assert(digitalPinToPortX<pin>() == port, "all port must be same");
* pin=0 -> PORTB, pin=8 -> PORTC error
digitalWriteMulti<
0, LOW,
1, HIGH,
2, LOW,
8, HIGH
>();
*/
asm volatile ("nop");
/** builtin dynamic method
* this is very slow
*/
digitalWrite(0, LOW);
}
void loop() {
}
.build_ano/uno/firmware.elf: file format elf32-avr
Disassembly of section .text:
00000000 <__vectors>:
0: 0c 94 61 00 jmp 0xc2 ; 0xc2 <__ctors_end>
4: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
8: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
c: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
10: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
14: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
18: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
1c: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
20: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
24: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
28: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
2c: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
30: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
34: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
38: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
3c: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
40: 0c 94 71 01 jmp 0x2e2 ; 0x2e2 <__vector_16>
44: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
48: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
4c: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
50: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
54: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
58: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
5c: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
60: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
64: 0c 94 73 00 jmp 0xe6 ; 0xe6 <__bad_interrupt>
00000068 <__trampolines_end>:
68: 00 00 nop
6a: 00 08 sbc r0, r0
6c: 00 02 muls r16, r16
6e: 01 00 .word 0x0001 ; ????
70: 00 03 mulsu r16, r16
72: 04 07 cpc r16, r20
...
0000007c <digital_pin_to_bit_mask_PGM>:
7c: 01 02 04 08 10 20 40 80 01 02 04 08 10 20 01 02 ..... @...... ..
8c: 04 08 10 20 ...
00000090 <digital_pin_to_port_PGM>:
90: 04 04 04 04 04 04 04 04 02 02 02 02 02 02 03 03 ................
a0: 03 03 03 03 ....
000000a4 <port_to_input_PGM>:
a4: 00 00 00 00 23 00 26 00 29 00 ....#.&.).
000000ae <port_to_output_PGM>:
ae: 00 00 00 00 25 00 28 00 2b 00 ....%.(.+.
000000b8 <port_to_mode_PGM>:
b8: 00 00 00 00 24 00 27 00 2a 00 ....$.'.*.
000000c2 <__ctors_end>:
c2: 11 24 eor r1, r1
c4: 1f be out 0x3f, r1 ; 63
c6: cf ef ldi r28, 0xFF ; 255
c8: d8 e0 ldi r29, 0x08 ; 8
ca: de bf out 0x3e, r29 ; 62
cc: cd bf out 0x3d, r28 ; 61
000000ce <__do_clear_bss>:
ce: 21 e0 ldi r18, 0x01 ; 1
d0: a0 e0 ldi r26, 0x00 ; 0
d2: b1 e0 ldi r27, 0x01 ; 1
d4: 01 c0 rjmp .+2 ; 0xd8 <.do_clear_bss_start>
000000d6 <.do_clear_bss_loop>:
d6: 1d 92 st X+, r1
000000d8 <.do_clear_bss_start>:
d8: a9 30 cpi r26, 0x09 ; 9
da: b2 07 cpc r27, r18
dc: e1 f7 brne .-8 ; 0xd6 <.do_clear_bss_loop>
de: 0e 94 b8 02 call 0x570 ; 0x570 <main>
e2: 0c 94 cc 02 jmp 0x598 ; 0x598 <_exit>
000000e6 <__bad_interrupt>:
e6: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>
000000ea <setup>:
ea: 8b b1 in r24, 0x0b ; 11
ec: 80 7f andi r24, 0xF0 ; 240
ee: 8a 60 ori r24, 0x0A ; 10
f0: 8b b9 out 0x0b, r24 ; 11
f2: 00 00 nop
f4: 8b b1 in r24, 0x0b ; 11
f6: 80 7f andi r24, 0xF0 ; 240
f8: 8a 60 ori r24, 0x0A ; 10
fa: 8b b9 out 0x0b, r24 ; 11
fc: 00 00 nop
fe: 85 b1 in r24, 0x05 ; 5
100: 80 7f andi r24, 0xF0 ; 240
102: 8a 60 ori r24, 0x0A ; 10
104: 85 b9 out 0x05, r24 ; 5
106: 00 00 nop
108: 00 00 nop
10a: 60 e0 ldi r22, 0x00 ; 0
10c: 80 e0 ldi r24, 0x00 ; 0
10e: 0c 94 c8 00 jmp 0x190 ; 0x190 <digitalWrite>
00000112 <loop>:
112: 08 95 ret
00000114 <pinMode>:
114: cf 93 push r28
116: df 93 push r29
118: 90 e0 ldi r25, 0x00 ; 0
11a: fc 01 movw r30, r24
11c: e4 58 subi r30, 0x84 ; 132
11e: ff 4f sbci r31, 0xFF ; 255
120: 24 91 lpm r18, Z
122: fc 01 movw r30, r24
124: e0 57 subi r30, 0x70 ; 112
126: ff 4f sbci r31, 0xFF ; 255
128: 84 91 lpm r24, Z
12a: 88 23 and r24, r24
12c: b9 f0 breq .+46 ; 0x15c <pinMode+0x48>
12e: 90 e0 ldi r25, 0x00 ; 0
130: 88 0f add r24, r24
132: 99 1f adc r25, r25
134: fc 01 movw r30, r24
136: e8 54 subi r30, 0x48 ; 72
138: ff 4f sbci r31, 0xFF ; 255
13a: a5 91 lpm r26, Z+
13c: b4 91 lpm r27, Z
13e: 82 55 subi r24, 0x52 ; 82
140: 9f 4f sbci r25, 0xFF ; 255
142: fc 01 movw r30, r24
144: c5 91 lpm r28, Z+
146: d4 91 lpm r29, Z
148: 9f b7 in r25, 0x3f ; 63
14a: 66 23 and r22, r22
14c: 51 f0 breq .+20 ; 0x162 <pinMode+0x4e>
14e: 62 30 cpi r22, 0x02 ; 2
150: a1 f0 breq .+40 ; 0x17a <pinMode+0x66>
152: f8 94 cli
154: 8c 91 ld r24, X
156: 82 2b or r24, r18
158: 8c 93 st X, r24
15a: 9f bf out 0x3f, r25 ; 63
15c: df 91 pop r29
15e: cf 91 pop r28
160: 08 95 ret
162: f8 94 cli
164: 8c 91 ld r24, X
166: 20 95 com r18
168: 82 23 and r24, r18
16a: 8c 93 st X, r24
16c: 88 81 ld r24, Y
16e: 82 23 and r24, r18
170: 88 83 st Y, r24
172: 9f bf out 0x3f, r25 ; 63
174: df 91 pop r29
176: cf 91 pop r28
178: 08 95 ret
17a: f8 94 cli
17c: 8c 91 ld r24, X
17e: 32 2f mov r19, r18
180: 30 95 com r19
182: 83 23 and r24, r19
184: 8c 93 st X, r24
186: 88 81 ld r24, Y
188: 82 2b or r24, r18
18a: 88 83 st Y, r24
18c: 9f bf out 0x3f, r25 ; 63
18e: e6 cf rjmp .-52 ; 0x15c <pinMode+0x48>
00000190 <digitalWrite>:
190: 90 e0 ldi r25, 0x00 ; 0
192: fc 01 movw r30, r24
194: e8 59 subi r30, 0x98 ; 152
196: ff 4f sbci r31, 0xFF ; 255
198: 24 91 lpm r18, Z
19a: fc 01 movw r30, r24
19c: e4 58 subi r30, 0x84 ; 132
19e: ff 4f sbci r31, 0xFF ; 255
1a0: 34 91 lpm r19, Z
1a2: fc 01 movw r30, r24
1a4: e0 57 subi r30, 0x70 ; 112
1a6: ff 4f sbci r31, 0xFF ; 255
1a8: 84 91 lpm r24, Z
1aa: 88 23 and r24, r24
1ac: 99 f0 breq .+38 ; 0x1d4 <digitalWrite+0x44>
1ae: 21 11 cpse r18, r1
1b0: 16 c0 rjmp .+44 ; 0x1de <digitalWrite+0x4e>
1b2: e8 2f mov r30, r24
1b4: f0 e0 ldi r31, 0x00 ; 0
1b6: ee 0f add r30, r30
1b8: ff 1f adc r31, r31
1ba: e2 55 subi r30, 0x52 ; 82
1bc: ff 4f sbci r31, 0xFF ; 255
1be: a5 91 lpm r26, Z+
1c0: b4 91 lpm r27, Z
1c2: 8f b7 in r24, 0x3f ; 63
1c4: f8 94 cli
1c6: 9c 91 ld r25, X
1c8: 61 11 cpse r22, r1
1ca: 05 c0 rjmp .+10 ; 0x1d6 <digitalWrite+0x46>
1cc: 30 95 com r19
1ce: 93 23 and r25, r19
1d0: 9c 93 st X, r25
1d2: 8f bf out 0x3f, r24 ; 63
1d4: 08 95 ret
1d6: 93 2b or r25, r19
1d8: 9c 93 st X, r25
1da: 8f bf out 0x3f, r24 ; 63
1dc: fb cf rjmp .-10 ; 0x1d4 <digitalWrite+0x44>
1de: 23 30 cpi r18, 0x03 ; 3
1e0: 29 f1 breq .+74 ; 0x22c <digitalWrite+0x9c>
1e2: 60 f0 brcs .+24 ; 0x1fc <digitalWrite+0x6c>
1e4: 27 30 cpi r18, 0x07 ; 7
1e6: e1 f0 breq .+56 ; 0x220 <digitalWrite+0x90>
1e8: 28 30 cpi r18, 0x08 ; 8
1ea: a1 f0 breq .+40 ; 0x214 <digitalWrite+0x84>
1ec: 24 30 cpi r18, 0x04 ; 4
1ee: 09 f7 brne .-62 ; 0x1b2 <digitalWrite+0x22>
1f0: 90 91 80 00 lds r25, 0x0080
1f4: 9f 7d andi r25, 0xDF ; 223
1f6: 90 93 80 00 sts 0x0080, r25
1fa: db cf rjmp .-74 ; 0x1b2 <digitalWrite+0x22>
1fc: 21 30 cpi r18, 0x01 ; 1
1fe: 31 f0 breq .+12 ; 0x20c <digitalWrite+0x7c>
200: 22 30 cpi r18, 0x02 ; 2
202: b9 f6 brne .-82 ; 0x1b2 <digitalWrite+0x22>
204: 94 b5 in r25, 0x24 ; 36
206: 9f 7d andi r25, 0xDF ; 223
208: 94 bd out 0x24, r25 ; 36
20a: d3 cf rjmp .-90 ; 0x1b2 <digitalWrite+0x22>
20c: 94 b5 in r25, 0x24 ; 36
20e: 9f 77 andi r25, 0x7F ; 127
210: 94 bd out 0x24, r25 ; 36
212: cf cf rjmp .-98 ; 0x1b2 <digitalWrite+0x22>
214: 90 91 b0 00 lds r25, 0x00B0
218: 9f 7d andi r25, 0xDF ; 223
21a: 90 93 b0 00 sts 0x00B0, r25
21e: c9 cf rjmp .-110 ; 0x1b2 <digitalWrite+0x22>
220: 90 91 b0 00 lds r25, 0x00B0
224: 9f 77 andi r25, 0x7F ; 127
226: 90 93 b0 00 sts 0x00B0, r25
22a: c3 cf rjmp .-122 ; 0x1b2 <digitalWrite+0x22>
22c: 90 91 80 00 lds r25, 0x0080
230: 9f 77 andi r25, 0x7F ; 127
232: 90 93 80 00 sts 0x0080, r25
236: bd cf rjmp .-134 ; 0x1b2 <digitalWrite+0x22>
00000238 <digitalRead>:
238: 90 e0 ldi r25, 0x00 ; 0
23a: fc 01 movw r30, r24
23c: e8 59 subi r30, 0x98 ; 152
23e: ff 4f sbci r31, 0xFF ; 255
240: 24 91 lpm r18, Z
242: fc 01 movw r30, r24
244: e4 58 subi r30, 0x84 ; 132
246: ff 4f sbci r31, 0xFF ; 255
248: 34 91 lpm r19, Z
24a: fc 01 movw r30, r24
24c: e0 57 subi r30, 0x70 ; 112
24e: ff 4f sbci r31, 0xFF ; 255
250: 84 91 lpm r24, Z
252: 88 23 and r24, r24
254: 91 f0 breq .+36 ; 0x27a <digitalRead+0x42>
256: 21 11 cpse r18, r1
258: 13 c0 rjmp .+38 ; 0x280 <digitalRead+0x48>
25a: e8 2f mov r30, r24
25c: f0 e0 ldi r31, 0x00 ; 0
25e: ee 0f add r30, r30
260: ff 1f adc r31, r31
262: ec 55 subi r30, 0x5C ; 92
264: ff 4f sbci r31, 0xFF ; 255
266: a5 91 lpm r26, Z+
268: b4 91 lpm r27, Z
26a: 2c 91 ld r18, X
26c: 23 23 and r18, r19
26e: 81 e0 ldi r24, 0x01 ; 1
270: 90 e0 ldi r25, 0x00 ; 0
272: 09 f0 breq .+2 ; 0x276 <digitalRead+0x3e>
274: 08 95 ret
276: 80 e0 ldi r24, 0x00 ; 0
278: 08 95 ret
27a: 80 e0 ldi r24, 0x00 ; 0
27c: 90 e0 ldi r25, 0x00 ; 0
27e: 08 95 ret
280: 23 30 cpi r18, 0x03 ; 3
282: 29 f1 breq .+74 ; 0x2ce <digitalRead+0x96>
284: 60 f0 brcs .+24 ; 0x29e <digitalRead+0x66>
286: 27 30 cpi r18, 0x07 ; 7
288: e1 f0 breq .+56 ; 0x2c2 <digitalRead+0x8a>
28a: 28 30 cpi r18, 0x08 ; 8
28c: a1 f0 breq .+40 ; 0x2b6 <digitalRead+0x7e>
28e: 24 30 cpi r18, 0x04 ; 4
290: 21 f7 brne .-56 ; 0x25a <digitalRead+0x22>
292: 90 91 80 00 lds r25, 0x0080
296: 9f 7d andi r25, 0xDF ; 223
298: 90 93 80 00 sts 0x0080, r25
29c: de cf rjmp .-68 ; 0x25a <digitalRead+0x22>
29e: 21 30 cpi r18, 0x01 ; 1
2a0: 31 f0 breq .+12 ; 0x2ae <digitalRead+0x76>
2a2: 22 30 cpi r18, 0x02 ; 2
2a4: d1 f6 brne .-76 ; 0x25a <digitalRead+0x22>
2a6: 94 b5 in r25, 0x24 ; 36
2a8: 9f 7d andi r25, 0xDF ; 223
2aa: 94 bd out 0x24, r25 ; 36
2ac: d6 cf rjmp .-84 ; 0x25a <digitalRead+0x22>
2ae: 94 b5 in r25, 0x24 ; 36
2b0: 9f 77 andi r25, 0x7F ; 127
2b2: 94 bd out 0x24, r25 ; 36
2b4: d2 cf rjmp .-92 ; 0x25a <digitalRead+0x22>
2b6: 90 91 b0 00 lds r25, 0x00B0
2ba: 9f 7d andi r25, 0xDF ; 223
2bc: 90 93 b0 00 sts 0x00B0, r25
2c0: cc cf rjmp .-104 ; 0x25a <digitalRead+0x22>
2c2: 90 91 b0 00 lds r25, 0x00B0
2c6: 9f 77 andi r25, 0x7F ; 127
2c8: 90 93 b0 00 sts 0x00B0, r25
2cc: c6 cf rjmp .-116 ; 0x25a <digitalRead+0x22>
2ce: 90 91 80 00 lds r25, 0x0080
2d2: 9f 77 andi r25, 0x7F ; 127
2d4: 90 93 80 00 sts 0x0080, r25
2d8: c0 cf rjmp .-128 ; 0x25a <digitalRead+0x22>
000002da <atexit>:
2da: 80 e0 ldi r24, 0x00 ; 0
2dc: 90 e0 ldi r25, 0x00 ; 0
2de: 08 95 ret
000002e0 <initVariant>:
2e0: 08 95 ret
000002e2 <__vector_16>:
2e2: 1f 92 push r1
2e4: 0f 92 push r0
2e6: 0f b6 in r0, 0x3f ; 63
2e8: 0f 92 push r0
2ea: 11 24 eor r1, r1
2ec: 2f 93 push r18
2ee: 3f 93 push r19
2f0: 8f 93 push r24
2f2: 9f 93 push r25
2f4: af 93 push r26
2f6: bf 93 push r27
2f8: 80 91 00 01 lds r24, 0x0100
2fc: 90 91 01 01 lds r25, 0x0101
300: a0 91 02 01 lds r26, 0x0102
304: b0 91 03 01 lds r27, 0x0103
308: 30 91 08 01 lds r19, 0x0108
30c: 23 e0 ldi r18, 0x03 ; 3
30e: 23 0f add r18, r19
310: 2d 37 cpi r18, 0x7D ; 125
312: 68 f1 brcs .+90 ; 0x36e <__vector_16+0x8c>
314: 26 e8 ldi r18, 0x86 ; 134
316: 23 0f add r18, r19
318: 02 96 adiw r24, 0x02 ; 2
31a: a1 1d adc r26, r1
31c: b1 1d adc r27, r1
31e: 20 93 08 01 sts 0x0108, r18
322: 80 93 00 01 sts 0x0100, r24
326: 90 93 01 01 sts 0x0101, r25
32a: a0 93 02 01 sts 0x0102, r26
32e: b0 93 03 01 sts 0x0103, r27
332: 80 91 04 01 lds r24, 0x0104
336: 90 91 05 01 lds r25, 0x0105
33a: a0 91 06 01 lds r26, 0x0106
33e: b0 91 07 01 lds r27, 0x0107
342: 01 96 adiw r24, 0x01 ; 1
344: a1 1d adc r26, r1
346: b1 1d adc r27, r1
348: 80 93 04 01 sts 0x0104, r24
34c: 90 93 05 01 sts 0x0105, r25
350: a0 93 06 01 sts 0x0106, r26
354: b0 93 07 01 sts 0x0107, r27
358: bf 91 pop r27
35a: af 91 pop r26
35c: 9f 91 pop r25
35e: 8f 91 pop r24
360: 3f 91 pop r19
362: 2f 91 pop r18
364: 0f 90 pop r0
366: 0f be out 0x3f, r0 ; 63
368: 0f 90 pop r0
36a: 1f 90 pop r1
36c: 18 95 reti
36e: 01 96 adiw r24, 0x01 ; 1
370: a1 1d adc r26, r1
372: b1 1d adc r27, r1
374: d4 cf rjmp .-88 ; 0x31e <__vector_16+0x3c>
00000376 <millis>:
376: 2f b7 in r18, 0x3f ; 63
378: f8 94 cli
37a: 60 91 00 01 lds r22, 0x0100
37e: 70 91 01 01 lds r23, 0x0101
382: 80 91 02 01 lds r24, 0x0102
386: 90 91 03 01 lds r25, 0x0103
38a: 2f bf out 0x3f, r18 ; 63
38c: 08 95 ret
0000038e <micros>:
38e: 3f b7 in r19, 0x3f ; 63
390: f8 94 cli
392: 80 91 04 01 lds r24, 0x0104
396: 90 91 05 01 lds r25, 0x0105
39a: a0 91 06 01 lds r26, 0x0106
39e: b0 91 07 01 lds r27, 0x0107
3a2: 26 b5 in r18, 0x26 ; 38
3a4: a8 9b sbis 0x15, 0 ; 21
3a6: 05 c0 rjmp .+10 ; 0x3b2 <micros+0x24>
3a8: 2f 3f cpi r18, 0xFF ; 255
3aa: 19 f0 breq .+6 ; 0x3b2 <micros+0x24>
3ac: 01 96 adiw r24, 0x01 ; 1
3ae: a1 1d adc r26, r1
3b0: b1 1d adc r27, r1
3b2: 3f bf out 0x3f, r19 ; 63
3b4: 66 27 eor r22, r22
3b6: 78 2f mov r23, r24
3b8: 89 2f mov r24, r25
3ba: 9a 2f mov r25, r26
3bc: 62 0f add r22, r18
3be: 71 1d adc r23, r1
3c0: 81 1d adc r24, r1
3c2: 91 1d adc r25, r1
3c4: 66 0f add r22, r22
3c6: 77 1f adc r23, r23
3c8: 88 1f adc r24, r24
3ca: 99 1f adc r25, r25
3cc: 66 0f add r22, r22
3ce: 77 1f adc r23, r23
3d0: 88 1f adc r24, r24
3d2: 99 1f adc r25, r25
3d4: 08 95 ret
000003d6 <delay>:
3d6: cf 92 push r12
3d8: df 92 push r13
3da: ef 92 push r14
3dc: ff 92 push r15
3de: 0f 93 push r16
3e0: 1f 93 push r17
3e2: cf 93 push r28
3e4: df 93 push r29
3e6: 00 d0 rcall .+0 ; 0x3e8 <delay+0x12>
3e8: 00 d0 rcall .+0 ; 0x3ea <delay+0x14>
3ea: cd b7 in r28, 0x3d ; 61
3ec: de b7 in r29, 0x3e ; 62
3ee: 3f b7 in r19, 0x3f ; 63
3f0: f8 94 cli
3f2: c0 90 04 01 lds r12, 0x0104
3f6: d0 90 05 01 lds r13, 0x0105
3fa: e0 90 06 01 lds r14, 0x0106
3fe: f0 90 07 01 lds r15, 0x0107
402: 26 b5 in r18, 0x26 ; 38
404: a8 9b sbis 0x15, 0 ; 21
406: 07 c0 rjmp .+14 ; 0x416 <delay+0x40>
408: 2f 3f cpi r18, 0xFF ; 255
40a: 29 f0 breq .+10 ; 0x416 <delay+0x40>
40c: 4f ef ldi r20, 0xFF ; 255
40e: c4 1a sub r12, r20
410: d4 0a sbc r13, r20
412: e4 0a sbc r14, r20
414: f4 0a sbc r15, r20
416: 3f bf out 0x3f, r19 ; 63
418: fe 2c mov r15, r14
41a: ed 2c mov r14, r13
41c: dc 2c mov r13, r12
41e: cc 24 eor r12, r12
420: c2 0e add r12, r18
422: d1 1c adc r13, r1
424: e1 1c adc r14, r1
426: f1 1c adc r15, r1
428: cc 0c add r12, r12
42a: dd 1c adc r13, r13
42c: ee 1c adc r14, r14
42e: ff 1c adc r15, r15
430: cc 0c add r12, r12
432: dd 1c adc r13, r13
434: ee 1c adc r14, r14
436: ff 1c adc r15, r15
438: 86 01 movw r16, r12
43a: 61 15 cp r22, r1
43c: 71 05 cpc r23, r1
43e: 81 05 cpc r24, r1
440: 91 05 cpc r25, r1
442: 09 f4 brne .+2 ; 0x446 <delay+0x70>
444: 41 c0 rjmp .+130 ; 0x4c8 <delay+0xf2>
446: 69 83 std Y+1, r22 ; 0x01
448: 7a 83 std Y+2, r23 ; 0x02
44a: 8b 83 std Y+3, r24 ; 0x03
44c: 9c 83 std Y+4, r25 ; 0x04
44e: 0e 94 b7 02 call 0x56e ; 0x56e <yield>
452: 3f b7 in r19, 0x3f ; 63
454: f8 94 cli
456: c0 90 04 01 lds r12, 0x0104
45a: d0 90 05 01 lds r13, 0x0105
45e: e0 90 06 01 lds r14, 0x0106
462: f0 90 07 01 lds r15, 0x0107
466: 26 b5 in r18, 0x26 ; 38
468: 69 81 ldd r22, Y+1 ; 0x01
46a: 7a 81 ldd r23, Y+2 ; 0x02
46c: 8b 81 ldd r24, Y+3 ; 0x03
46e: 9c 81 ldd r25, Y+4 ; 0x04
470: a8 9b sbis 0x15, 0 ; 21
472: 07 c0 rjmp .+14 ; 0x482 <delay+0xac>
474: 2f 3f cpi r18, 0xFF ; 255
476: 29 f0 breq .+10 ; 0x482 <delay+0xac>
478: ef ef ldi r30, 0xFF ; 255
47a: ce 1a sub r12, r30
47c: de 0a sbc r13, r30
47e: ee 0a sbc r14, r30
480: fe 0a sbc r15, r30
482: 3f bf out 0x3f, r19 ; 63
484: fe 2c mov r15, r14
486: ed 2c mov r14, r13
488: dc 2c mov r13, r12
48a: cc 24 eor r12, r12
48c: c2 0e add r12, r18
48e: d1 1c adc r13, r1
490: e1 1c adc r14, r1
492: f1 1c adc r15, r1
494: cc 0c add r12, r12
496: dd 1c adc r13, r13
498: ee 1c adc r14, r14
49a: ff 1c adc r15, r15
49c: cc 0c add r12, r12
49e: dd 1c adc r13, r13
4a0: ee 1c adc r14, r14
4a2: ff 1c adc r15, r15
4a4: 96 01 movw r18, r12
4a6: 20 1b sub r18, r16
4a8: 31 0b sbc r19, r17
4aa: 28 3e cpi r18, 0xE8 ; 232
4ac: 33 40 sbci r19, 0x03 ; 3
4ae: 28 f2 brcs .-118 ; 0x43a <delay+0x64>
4b0: 61 50 subi r22, 0x01 ; 1
4b2: 71 09 sbc r23, r1
4b4: 81 09 sbc r24, r1
4b6: 91 09 sbc r25, r1
4b8: 08 51 subi r16, 0x18 ; 24
4ba: 1c 4f sbci r17, 0xFC ; 252
4bc: 61 15 cp r22, r1
4be: 71 05 cpc r23, r1
4c0: 81 05 cpc r24, r1
4c2: 91 05 cpc r25, r1
4c4: 09 f0 breq .+2 ; 0x4c8 <delay+0xf2>
4c6: bf cf rjmp .-130 ; 0x446 <delay+0x70>
4c8: 0f 90 pop r0
4ca: 0f 90 pop r0
4cc: 0f 90 pop r0
4ce: 0f 90 pop r0
4d0: df 91 pop r29
4d2: cf 91 pop r28
4d4: 1f 91 pop r17
4d6: 0f 91 pop r16
4d8: ff 90 pop r15
4da: ef 90 pop r14
4dc: df 90 pop r13
4de: cf 90 pop r12
4e0: 08 95 ret
000004e2 <delayMicroseconds>:
4e2: 82 30 cpi r24, 0x02 ; 2
4e4: 91 05 cpc r25, r1
4e6: 38 f0 brcs .+14 ; 0x4f6 <delayMicroseconds+0x14>
4e8: 88 0f add r24, r24
4ea: 99 1f adc r25, r25
4ec: 88 0f add r24, r24
4ee: 99 1f adc r25, r25
4f0: 05 97 sbiw r24, 0x05 ; 5
4f2: 01 97 sbiw r24, 0x01 ; 1
4f4: f1 f7 brne .-4 ; 0x4f2 <delayMicroseconds+0x10>
4f6: 08 95 ret
000004f8 <init>:
4f8: 78 94 sei
4fa: 84 b5 in r24, 0x24 ; 36
4fc: 82 60 ori r24, 0x02 ; 2
4fe: 84 bd out 0x24, r24 ; 36
500: 84 b5 in r24, 0x24 ; 36
502: 81 60 ori r24, 0x01 ; 1
504: 84 bd out 0x24, r24 ; 36
506: 85 b5 in r24, 0x25 ; 37
508: 82 60 ori r24, 0x02 ; 2
50a: 85 bd out 0x25, r24 ; 37
50c: 85 b5 in r24, 0x25 ; 37
50e: 81 60 ori r24, 0x01 ; 1
510: 85 bd out 0x25, r24 ; 37
512: ee e6 ldi r30, 0x6E ; 110
514: f0 e0 ldi r31, 0x00 ; 0
516: 80 81 ld r24, Z
518: 81 60 ori r24, 0x01 ; 1
51a: 80 83 st Z, r24
51c: e1 e8 ldi r30, 0x81 ; 129
51e: f0 e0 ldi r31, 0x00 ; 0
520: 10 82 st Z, r1
522: 80 81 ld r24, Z
524: 82 60 ori r24, 0x02 ; 2
526: 80 83 st Z, r24
528: 80 81 ld r24, Z
52a: 81 60 ori r24, 0x01 ; 1
52c: 80 83 st Z, r24
52e: e0 e8 ldi r30, 0x80 ; 128
530: f0 e0 ldi r31, 0x00 ; 0
532: 80 81 ld r24, Z
534: 81 60 ori r24, 0x01 ; 1
536: 80 83 st Z, r24
538: e1 eb ldi r30, 0xB1 ; 177
53a: f0 e0 ldi r31, 0x00 ; 0
53c: 80 81 ld r24, Z
53e: 84 60 ori r24, 0x04 ; 4
540: 80 83 st Z, r24
542: e0 eb ldi r30, 0xB0 ; 176
544: f0 e0 ldi r31, 0x00 ; 0
546: 80 81 ld r24, Z
548: 81 60 ori r24, 0x01 ; 1
54a: 80 83 st Z, r24
54c: ea e7 ldi r30, 0x7A ; 122
54e: f0 e0 ldi r31, 0x00 ; 0
550: 80 81 ld r24, Z
552: 84 60 ori r24, 0x04 ; 4
554: 80 83 st Z, r24
556: 80 81 ld r24, Z
558: 82 60 ori r24, 0x02 ; 2
55a: 80 83 st Z, r24
55c: 80 81 ld r24, Z
55e: 81 60 ori r24, 0x01 ; 1
560: 80 83 st Z, r24
562: 80 81 ld r24, Z
564: 80 68 ori r24, 0x80 ; 128
566: 80 83 st Z, r24
568: 10 92 c1 00 sts 0x00C1, r1
56c: 08 95 ret
0000056e <yield>:
56e: 08 95 ret
00000570 <main>:
570: 0e 94 7c 02 call 0x4f8 ; 0x4f8 <init>
574: 0e 94 70 01 call 0x2e0 ; 0x2e0 <initVariant>
578: 0e 94 75 00 call 0xea ; 0xea <setup>
57c: 80 e0 ldi r24, 0x00 ; 0
57e: 90 e0 ldi r25, 0x00 ; 0
580: 89 2b or r24, r25
582: 29 f0 breq .+10 ; 0x58e <main+0x1e>
584: 0e 94 89 00 call 0x112 ; 0x112 <loop>
588: 0e 94 00 00 call 0 ; 0x0 <__vectors>
58c: fb cf rjmp .-10 ; 0x584 <main+0x14>
58e: 0e 94 89 00 call 0x112 ; 0x112 <loop>
592: 0e 94 89 00 call 0x112 ; 0x112 <loop>
596: fb cf rjmp .-10 ; 0x58e <main+0x1e>
00000598 <_exit>:
598: f8 94 cli
0000059a <__stop_program>:
59a: ff cf rjmp .-2 ; 0x59a <__stop_program>
view raw obj-dump hosted with ❤ by GitHub

解説

C++11

コンパイラは Arduino.app に入っている avr-gcc を使っているが -std=c++11 を指定して C++11 の機能を有効にしている (constexpr 関数) なので素の Arduino.app からだとコンパイルできない。

Arduino ビルトイン関数の再実装

Arduino 側で portOutputRegister digitalPinToBitMask digitalPinToPort といった関数が定義されており、これらは PROGMEM からテーブルをひいてくる実装になっているので、まずこれをコンパイル時にできるように constexpr 関数で再実装した。

C++14 ならもっと簡単に書けるはずなんだけど、C++11 相当だと関数本体に return 以外書けないのでキモい実装になっている。

digitalWriteMulti

ここがキモで、まず digitalWriteMulti でうけて、digitalWriteMulti_ で再帰的に処理している。digitalWriteMulti_ の最初の3引数は、持ってまわっている状態で、残りは元の digitalWriteMulti に渡された引数。

C++ のテンプレートメタプログラミングではこのようにパターンマッチでしか可変長引数を扱えないみたい。だけど思ったより綺麗に実装できた気がする。

WRITE_MULTI() マクロ

これはプリプロセッサマクロで単に置換している。テンプレート引数として渡すのが気持ち悪かったので(慣れかもしれない)マクロでラップしている。

その他

C++ はほとんど書いたことがないので、きっともっといい方法がありそうだと思う。特にテンプレートメタプログラミングで可変長引数を処理するとき、状態を全てテンプレート引数の頭にフラットに渡しているのがダサい。うまくコンパイル時だけ使えるコンテナみたいなのを持ちまわしていければよかったが、おもいつかなかった。

pinMode にも応用できると思うが pinMode は PORTx もいじるのでとりあえず省略した。

大きなメリット

同じようなことはテンプレートを使わない場合(素のCとか)だとまずできない。

特に「指定しなかった部分のビットはかえない」を実現しようとすると、どうしてもビットマスクと実際に設定するビット値の2つを扱わないといけないので、かなりコードが煩雑になる。テンプレート化することで表面上の可読性がかなり上がる (テンプレート部分はアレだけど)

そしてコンパイル時にチェックができる点はおおきい。マイコンはどうしてもフィードバックがすくないので、実行時エラーというのは実際気付くのが難しい。

速度的にも当然メリットがある。これの場合はコンパイルしたあとは素で書く場合と完全に同じバイナリになる。

000000ea <setup>:
; PORTD = (PORTD & 0b11110000) | (0b1010);
  ea:	8b b1       	in	r24, 0x0b	; 11
  ec:	80 7f       	andi	r24, 0xF0	; 240
  ee:	8a 60       	ori	r24, 0x0A	; 10
  f0:	8b b9       	out	0x0b, r24	; 11
; asm volatile ("nop");	// marker
  f2:	00 00       	nop
; WRITE_MULTI(...)
  f4:	8b b1       	in	r24, 0x0b	; 11
  f6:	80 7f       	andi	r24, 0xF0	; 240
  f8:	8a 60       	ori	r24, 0x0A	; 10
  fa:	8b b9       	out	0x0b, r24	; 11

素の digitalWrite の場合

; digitalWrite(0, LOW);
 10a:	60 e0       	ldi	r22, 0x00	; 0
 10c:	80 e0       	ldi	r24, 0x00	; 0
 10e:	0c 94 c8 00 	jmp	0x190	; 0x190 <digitalWrite>

で関数コールして

00000190 <digitalWrite>:
 190:	90 e0       	ldi	r25, 0x00	; 0
 192:	fc 01       	movw	r30, r24
 194:	e8 59       	subi	r30, 0x98	; 152
 196:	ff 4f       	sbci	r31, 0xFF	; 255
 198:	24 91       	lpm	r18, Z
 19a:	fc 01       	movw	r30, r24
 19c:	e4 58       	subi	r30, 0x84	; 132
 19e:	ff 4f       	sbci	r31, 0xFF	; 255
 1a0:	34 91       	lpm	r19, Z
 1a2:	fc 01       	movw	r30, r24
 1a4:	e0 57       	subi	r30, 0x70	; 112
 1a6:	ff 4f       	sbci	r31, 0xFF	; 255
 1a8:	84 91       	lpm	r24, Z
 1aa:	88 23       	and	r24, r24
 1ac:	99 f0       	breq	.+38     	; 0x1d4 <digitalWrite+0x44>
 1ae:	21 11       	cpse	r18, r1
 1b0:	16 c0       	rjmp	.+44     	; 0x1de <digitalWrite+0x4e>
 1b2:	e8 2f       	mov	r30, r24
 1b4:	f0 e0       	ldi	r31, 0x00	; 0
 1b6:	ee 0f       	add	r30, r30
 1b8:	ff 1f       	adc	r31, r31
 1ba:	e2 55       	subi	r30, 0x52	; 82
 1bc:	ff 4f       	sbci	r31, 0xFF	; 255
 1be:	a5 91       	lpm	r26, Z+
 1c0:	b4 91       	lpm	r27, Z
 1c2:	8f b7       	in	r24, 0x3f	; 63
 1c4:	f8 94       	cli
 1c6:	9c 91       	ld	r25, X
 1c8:	61 11       	cpse	r22, r1
 1ca:	05 c0       	rjmp	.+10     	; 0x1d6 <digitalWrite+0x46>
 1cc:	30 95       	com	r19
 1ce:	93 23       	and	r25, r19
 1d0:	9c 93       	st	X, r25
 1d2:	8f bf       	out	0x3f, r24	; 63
 1d4:	08 95       	ret
 1d6:	93 2b       	or	r25, r19
 1d8:	9c 93       	st	X, r25
 1da:	8f bf       	out	0x3f, r24	; 63
 1dc:	fb cf       	rjmp	.-10     	; 0x1d4 <digitalWrite+0x44>
 1de:	23 30       	cpi	r18, 0x03	; 3
 1e0:	29 f1       	breq	.+74     	; 0x22c <digitalWrite+0x9c>
 1e2:	60 f0       	brcs	.+24     	; 0x1fc <digitalWrite+0x6c>
 1e4:	27 30       	cpi	r18, 0x07	; 7
 1e6:	e1 f0       	breq	.+56     	; 0x220 <digitalWrite+0x90>
 1e8:	28 30       	cpi	r18, 0x08	; 8
 1ea:	a1 f0       	breq	.+40     	; 0x214 <digitalWrite+0x84>
 1ec:	24 30       	cpi	r18, 0x04	; 4
 1ee:	09 f7       	brne	.-62     	; 0x1b2 <digitalWrite+0x22>
 1f0:	90 91 80 00 	lds	r25, 0x0080
 1f4:	9f 7d       	andi	r25, 0xDF	; 223
 1f6:	90 93 80 00 	sts	0x0080, r25
 1fa:	db cf       	rjmp	.-74     	; 0x1b2 <digitalWrite+0x22>
 1fc:	21 30       	cpi	r18, 0x01	; 1
 1fe:	31 f0       	breq	.+12     	; 0x20c <digitalWrite+0x7c>
 200:	22 30       	cpi	r18, 0x02	; 2
 202:	b9 f6       	brne	.-82     	; 0x1b2 <digitalWrite+0x22>
 204:	94 b5       	in	r25, 0x24	; 36
 206:	9f 7d       	andi	r25, 0xDF	; 223
 208:	94 bd       	out	0x24, r25	; 36
 20a:	d3 cf       	rjmp	.-90     	; 0x1b2 <digitalWrite+0x22>
 20c:	94 b5       	in	r25, 0x24	; 36
 20e:	9f 77       	andi	r25, 0x7F	; 127
 210:	94 bd       	out	0x24, r25	; 36
 212:	cf cf       	rjmp	.-98     	; 0x1b2 <digitalWrite+0x22>
 214:	90 91 b0 00 	lds	r25, 0x00B0
 218:	9f 7d       	andi	r25, 0xDF	; 223
 21a:	90 93 b0 00 	sts	0x00B0, r25
 21e:	c9 cf       	rjmp	.-110    	; 0x1b2 <digitalWrite+0x22>
 220:	90 91 b0 00 	lds	r25, 0x00B0
 224:	9f 77       	andi	r25, 0x7F	; 127
 226:	90 93 b0 00 	sts	0x00B0, r25
 22a:	c3 cf       	rjmp	.-122    	; 0x1b2 <digitalWrite+0x22>
 22c:	90 91 80 00 	lds	r25, 0x0080
 230:	9f 77       	andi	r25, 0x7F	; 127
 232:	90 93 80 00 	sts	0x0080, r25
 236:	bd cf       	rjmp	.-134    	; 0x1b2 <digitalWrite+0x22>

となっていて、1bit 書くだけでかなり大層なことをしていることがわかる。

  1. トップ
  2. tech
  3. Arduino の digitalWrite をコンパイルタイムに解決する