Arduino: просмотр размера кода и ассемблерного листинга программы
При разработке программ для плат Arduino иногда может возникнуть необходимость оценки, возможно ли выполнить оптимизацию кода, и определения места, где эта оптимизация необходима. Для этого необходимо получить ассемблерный листинг программы, а также определить, какой размер flash-памяти занимает какая-либо часть вашего кода.
Для этих целей воспользуемся двумя инструментами из набора AVR toolchain: avr-nm
и avr-objdump
. Оба этих инструмента поставляются вместе с Arduino IDE.
Поиск .elf файла
Итак, предположим, что вы написали скетч для Arduino и хотите оптимизировать его размер.
После компиляции скетча, первое, что необходимо сделать, это найти скомпилированный бинарный файл. Самый простой способ сделать это – включить показ подробного вывода процесса компиляции и посмотреть путь к .elf файлу в конце лога.
В зависимости от версии Arduino IDE файл может называться либо Blink.ino.elf, либо Blink.cpp.elf, либо Blink.elf.
Просмотр размера кода с помощью avr-nm
При оптимизации размера программы важно определить место, где эта оптимизация необходима. Используя avr-nm
, вы сможете определить размер каждой функции.
Откройте командную строку и введите:
set PATH=%PATH%;C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\
avr-nm --size-sort -Crtd Blink.ino.elf
После чего в консоль будет выведен размер всех функций и данных:
00000148 T __vector_16
00000118 T init
00000114 T pinMode
00000108 T digitalWrite
00000082 t turnOffPWM
00000080 T delay
00000070 T micros
00000040 T loop
00000030 T main
00000020 T digital_pin_to_timer_PGM
00000020 T digital_pin_to_port_PGM
00000020 T digital_pin_to_bit_mask_PGM
00000016 T __do_clear_bss
00000010 T port_to_output_PGM
00000010 T port_to_mode_PGM
00000008 T setup
00000004 B timer0_overflow_count
00000004 B timer0_millis
00000002 W yield
00000002 W initVariant
00000002 t __empty
Первый столбец – это размер объекта, на который ссылается символ. Второй столбец – тип, где 't' (в верхнем или нижнем регистре) означает «текст» (код или данные PROGMEM
), 'd' означает «инициализированные данные» (сохраненные во flash-памяти и скопированные в RAM при запуске), а 'b' означает «неинициализированные данные» (также известные, как «BSS», не занимают flash-память, только RAM). Регистр буквы показывает, является ли символ локальным (нижний регистр) или глобальным (верхний регистр). И последний столбец – имя символа. В данном примере вы можете увидеть, что самой большой функцией здесь является __vector_16
(обработчик прерывания по переполнению таймера 0), она занимает 148 байт во flash-памяти.
В этом примере мы передали avr-nm следующие опции:
--size-sort
, очевидно, сортирует символы по размеру. Она также показывает в первом столбце размер символа, вместо его адреса;-C
означает декодирование символов C++. Например, вместо отображения “_ZN14HardwareSerial5beginEm
”, она покажет “HardwareSerial::begin(unsigned long)
”;-r
означает «обратный порядок сортировки». Она выводит самые большие функции вверху;-td
означает отображение размеров в десятичном формате, вместо шестнадцатеричного.
Просмотр ассемблерного листинга программы с помощью avr-objdump
Когда avr-nm
становится недостаточно, у вас не остается выбора, кроме как использовать самое мощное оружие: дизассемблер. С помощью avr-objdump
вы можете извлечь ассемблерный код из .elf файла.
Наберите в командной строке:
set PATH=%PATH%;C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\
avr-objdump -S "путь_к_elf_файлу" > "путь_к_новому_файлу_листинга"
Например:
avr-objdump -S Blink.ino.elf > list.txt
Опция -S
означает вывод исходного кода вместе с дизассемблированным.
В результате получили следующее:
Blink.ino.elf: file format elf32-avr
Disassembly of section .text:
00000000 <__vectors>:
0: 0c 94 5c 00 jmp 0xb8 ; 0xb8 <__ctors_end>
4: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
8: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
c: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
10: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
14: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
18: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
1c: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
20: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
24: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
28: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
2c: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
30: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
34: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
38: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
3c: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
40: 0c 94 88 00 jmp 0x110 ; 0x110 <__vector_16>
44: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
48: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
4c: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
50: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
54: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
58: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
5c: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
60: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
64: 0c 94 6e 00 jmp 0xdc ; 0xdc <__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_output_PGM>:
a4: 00 00 00 00 25 00 28 00 2b 00 ....%.(.+.
000000ae <port_to_mode_PGM>:
ae: 00 00 00 00 24 00 27 00 2a 00 ....$.'.*.
000000b8 <__ctors_end>:
b8: 11 24 eor r1, r1
ba: 1f be out 0x3f, r1 ; 63
bc: cf ef ldi r28, 0xFF ; 255
be: d8 e0 ldi r29, 0x08 ; 8
c0: de bf out 0x3e, r29 ; 62
c2: cd bf out 0x3d, r28 ; 61
000000c4 <__do_clear_bss>:
c4: 21 e0 ldi r18, 0x01 ; 1
c6: a0 e0 ldi r26, 0x00 ; 0
c8: b1 e0 ldi r27, 0x01 ; 1
ca: 01 c0 rjmp .+2 ; 0xce <.do_clear_bss_start>
000000cc <.do_clear_bss_loop>:
cc: 1d 92 st X+, r1
000000ce <.do_clear_bss_start>:
ce: a9 30 cpi r26, 0x09 ; 9
d0: b2 07 cpc r27, r18
d2: e1 f7 brne .-8 ; 0xcc <.do_clear_bss_loop>
d4: 0e 94 f1 01 call 0x3e2 ; 0x3e2 <main>
d8: 0c 94 01 02 jmp 0x402 ; 0x402 <_exit>
000000dc <__bad_interrupt>:
dc: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>
000000e0 <setup>:
// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin 13 as an output.
pinMode(13, OUTPUT);
e0: 61 e0 ldi r22, 0x01 ; 1
e2: 8d e0 ldi r24, 0x0D ; 13
e4: 0c 94 81 01 jmp 0x302 ; 0x302 <pinMode>
000000e8 <loop>:
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(13, HIGH); // turn the LED on (HIGH is the voltage level)
e8: 61 e0 ldi r22, 0x01 ; 1
ea: 8d e0 ldi r24, 0x0D ; 13
ec: 0e 94 ba 01 call 0x374 ; 0x374 <digitalWrite>
delay(1000); // wait for a second
f0: 68 ee ldi r22, 0xE8 ; 232
f2: 73 e0 ldi r23, 0x03 ; 3
f4: 80 e0 ldi r24, 0x00 ; 0
f6: 90 e0 ldi r25, 0x00 ; 0
f8: 0e 94 f5 00 call 0x1ea ; 0x1ea <delay>
digitalWrite(13, LOW); // turn the LED off by making the voltage LOW
fc: 60 e0 ldi r22, 0x00 ; 0
fe: 8d e0 ldi r24, 0x0D ; 13
100: 0e 94 ba 01 call 0x374 ; 0x374 <digitalWrite>
delay(1000); // wait for a second
104: 68 ee ldi r22, 0xE8 ; 232
106: 73 e0 ldi r23, 0x03 ; 3
108: 80 e0 ldi r24, 0x00 ; 0
10a: 90 e0 ldi r25, 0x00 ; 0
10c: 0c 94 f5 00 jmp 0x1ea ; 0x1ea <delay>
00000110 <__vector_16>:
#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
110: 1f 92 push r1
112: 0f 92 push r0
114: 0f b6 in r0, 0x3f ; 63
116: 0f 92 push r0
118: 11 24 eor r1, r1
11a: 2f 93 push r18
11c: 3f 93 push r19
11e: 8f 93 push r24
120: 9f 93 push r25
122: af 93 push r26
124: bf 93 push r27
// copy these to local variables so they can be stored in registers
// (volatile variables must be read from memory on every access)
unsigned long m = timer0_millis;
126: 80 91 01 01 lds r24, 0x0101
12a: 90 91 02 01 lds r25, 0x0102
12e: a0 91 03 01 lds r26, 0x0103
132: b0 91 04 01 lds r27, 0x0104
unsigned char f = timer0_fract;
136: 30 91 00 01 lds r19, 0x0100
m += MILLIS_INC;
f += FRACT_INC;
13a: 23 e0 ldi r18, 0x03 ; 3
13c: 23 0f add r18, r19
if (f >= FRACT_MAX) {
13e: 2d 37 cpi r18, 0x7D ; 125
140: 20 f4 brcc .+8 ; 0x14a <__vector_16+0x3a>
// copy these to local variables so they can be stored in registers
// (volatile variables must be read from memory on every access)
unsigned long m = timer0_millis;
unsigned char f = timer0_fract;
m += MILLIS_INC;
142: 01 96 adiw r24, 0x01 ; 1
144: a1 1d adc r26, r1
146: b1 1d adc r27, r1
148: 05 c0 rjmp .+10 ; 0x154 <__vector_16+0x44>
f += FRACT_INC;
if (f >= FRACT_MAX) {
f -= FRACT_MAX;
14a: 26 e8 ldi r18, 0x86 ; 134
14c: 23 0f add r18, r19
m += 1;
14e: 02 96 adiw r24, 0x02 ; 2
150: a1 1d adc r26, r1
152: b1 1d adc r27, r1
}
timer0_fract = f;
154: 20 93 00 01 sts 0x0100, r18
timer0_millis = m;
158: 80 93 01 01 sts 0x0101, r24
15c: 90 93 02 01 sts 0x0102, r25
160: a0 93 03 01 sts 0x0103, r26
164: b0 93 04 01 sts 0x0104, r27
timer0_overflow_count++;
168: 80 91 05 01 lds r24, 0x0105
16c: 90 91 06 01 lds r25, 0x0106
170: a0 91 07 01 lds r26, 0x0107
174: b0 91 08 01 lds r27, 0x0108
178: 01 96 adiw r24, 0x01 ; 1
17a: a1 1d adc r26, r1
17c: b1 1d adc r27, r1
17e: 80 93 05 01 sts 0x0105, r24
182: 90 93 06 01 sts 0x0106, r25
186: a0 93 07 01 sts 0x0107, r26
18a: b0 93 08 01 sts 0x0108, r27
}
18e: bf 91 pop r27
190: af 91 pop r26
192: 9f 91 pop r25
194: 8f 91 pop r24
196: 3f 91 pop r19
198: 2f 91 pop r18
19a: 0f 90 pop r0
19c: 0f be out 0x3f, r0 ; 63
19e: 0f 90 pop r0
1a0: 1f 90 pop r1
1a2: 18 95 reti
000001a4 <micros>:
return m;
}
unsigned long micros() {
unsigned long m;
uint8_t oldSREG = SREG, t;
1a4: 3f b7 in r19, 0x3f ; 63
cli();
1a6: f8 94 cli
m = timer0_overflow_count;
1a8: 80 91 05 01 lds r24, 0x0105
1ac: 90 91 06 01 lds r25, 0x0106
1b0: a0 91 07 01 lds r26, 0x0107
1b4: b0 91 08 01 lds r27, 0x0108
#if defined(TCNT0)
t = TCNT0;
1b8: 26 b5 in r18, 0x26 ; 38
#else
#error TIMER 0 not defined
#endif
#ifdef TIFR0
if ((TIFR0 & _BV(TOV0)) && (t < 255))
1ba: a8 9b sbis 0x15, 0 ; 21
1bc: 05 c0 rjmp .+10 ; 0x1c8 <micros+0x24>
1be: 2f 3f cpi r18, 0xFF ; 255
1c0: 19 f0 breq .+6 ; 0x1c8 <micros+0x24>
m++;
1c2: 01 96 adiw r24, 0x01 ; 1
1c4: a1 1d adc r26, r1
1c6: b1 1d adc r27, r1
#else
if ((TIFR & _BV(TOV0)) && (t < 255))
m++;
#endif
SREG = oldSREG;
1c8: 3f bf out 0x3f, r19 ; 63
return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
1ca: 66 27 eor r22, r22
1cc: 78 2f mov r23, r24
1ce: 89 2f mov r24, r25
1d0: 9a 2f mov r25, r26
1d2: 62 0f add r22, r18
1d4: 71 1d adc r23, r1
1d6: 81 1d adc r24, r1
1d8: 91 1d adc r25, r1
1da: 42 e0 ldi r20, 0x02 ; 2
1dc: 66 0f add r22, r22
1de: 77 1f adc r23, r23
1e0: 88 1f adc r24, r24
1e2: 99 1f adc r25, r25
1e4: 4a 95 dec r20
1e6: d1 f7 brne .-12 ; 0x1dc <micros+0x38>
}
1e8: 08 95 ret
000001ea <delay>:
void delay(unsigned long ms)
{
1ea: cf 92 push r12
1ec: df 92 push r13
1ee: ef 92 push r14
1f0: ff 92 push r15
1f2: cf 93 push r28
1f4: df 93 push r29
1f6: 6b 01 movw r12, r22
1f8: 7c 01 movw r14, r24
uint16_t start = (uint16_t)micros();
1fa: 0e 94 d2 00 call 0x1a4 ; 0x1a4 <micros>
1fe: eb 01 movw r28, r22
while (ms > 0) {
200: c1 14 cp r12, r1
202: d1 04 cpc r13, r1
204: e1 04 cpc r14, r1
206: f1 04 cpc r15, r1
208: 89 f0 breq .+34 ; 0x22c <delay+0x42>
yield();
20a: 0e 94 00 02 call 0x400 ; 0x400 <yield>
if (((uint16_t)micros() - start) >= 1000) {
20e: 0e 94 d2 00 call 0x1a4 ; 0x1a4 <micros>
212: 6c 1b sub r22, r28
214: 7d 0b sbc r23, r29
216: 68 3e cpi r22, 0xE8 ; 232
218: 73 40 sbci r23, 0x03 ; 3
21a: 90 f3 brcs .-28 ; 0x200 <delay+0x16>
ms--;
21c: 81 e0 ldi r24, 0x01 ; 1
21e: c8 1a sub r12, r24
220: d1 08 sbc r13, r1
222: e1 08 sbc r14, r1
224: f1 08 sbc r15, r1
start += 1000;
226: c8 51 subi r28, 0x18 ; 24
228: dc 4f sbci r29, 0xFC ; 252
22a: ea cf rjmp .-44 ; 0x200 <delay+0x16>
}
}
}
22c: df 91 pop r29
22e: cf 91 pop r28
230: ff 90 pop r15
232: ef 90 pop r14
234: df 90 pop r13
236: cf 90 pop r12
238: 08 95 ret
0000023a <init>:
void init()
{
// this needs to be called before setup() or some functions won't
// work there
sei();
23a: 78 94 sei
// on the ATmega168, timer 0 is also used for fast hardware pwm
// (using phase-correct PWM would mean that timer 0 overflowed half as often
// resulting in different millis() behavior on the ATmega8 and ATmega168)
#if defined(TCCR0A) && defined(WGM01)
sbi(TCCR0A, WGM01);
23c: 84 b5 in r24, 0x24 ; 36
23e: 82 60 ori r24, 0x02 ; 2
240: 84 bd out 0x24, r24 ; 36
sbi(TCCR0A, WGM00);
242: 84 b5 in r24, 0x24 ; 36
244: 81 60 ori r24, 0x01 ; 1
246: 84 bd out 0x24, r24 ; 36
// this combination is for the standard atmega8
sbi(TCCR0, CS01);
sbi(TCCR0, CS00);
#elif defined(TCCR0B) && defined(CS01) && defined(CS00)
// this combination is for the standard 168/328/1280/2560
sbi(TCCR0B, CS01);
248: 85 b5 in r24, 0x25 ; 37
24a: 82 60 ori r24, 0x02 ; 2
24c: 85 bd out 0x25, r24 ; 37
sbi(TCCR0B, CS00);
24e: 85 b5 in r24, 0x25 ; 37
250: 81 60 ori r24, 0x01 ; 1
252: 85 bd out 0x25, r24 ; 37
// enable timer 0 overflow interrupt
#if defined(TIMSK) && defined(TOIE0)
sbi(TIMSK, TOIE0);
#elif defined(TIMSK0) && defined(TOIE0)
sbi(TIMSK0, TOIE0);
254: ee e6 ldi r30, 0x6E ; 110
256: f0 e0 ldi r31, 0x00 ; 0
258: 80 81 ld r24, Z
25a: 81 60 ori r24, 0x01 ; 1
25c: 80 83 st Z, r24
// this is better for motors as it ensures an even waveform
// note, however, that fast pwm mode can achieve a frequency of up
// 8 MHz (with a 16 MHz clock) at 50% duty cycle
#if defined(TCCR1B) && defined(CS11) && defined(CS10)
TCCR1B = 0;
25e: e1 e8 ldi r30, 0x81 ; 129
260: f0 e0 ldi r31, 0x00 ; 0
262: 10 82 st Z, r1
// set timer 1 prescale factor to 64
sbi(TCCR1B, CS11);
264: 80 81 ld r24, Z
266: 82 60 ori r24, 0x02 ; 2
268: 80 83 st Z, r24
#if F_CPU >= 8000000L
sbi(TCCR1B, CS10);
26a: 80 81 ld r24, Z
26c: 81 60 ori r24, 0x01 ; 1
26e: 80 83 st Z, r24
sbi(TCCR1, CS10);
#endif
#endif
// put timer 1 in 8-bit phase correct pwm mode
#if defined(TCCR1A) && defined(WGM10)
sbi(TCCR1A, WGM10);
270: e0 e8 ldi r30, 0x80 ; 128
272: f0 e0 ldi r31, 0x00 ; 0
274: 80 81 ld r24, Z
276: 81 60 ori r24, 0x01 ; 1
278: 80 83 st Z, r24
// set timer 2 prescale factor to 64
#if defined(TCCR2) && defined(CS22)
sbi(TCCR2, CS22);
#elif defined(TCCR2B) && defined(CS22)
sbi(TCCR2B, CS22);
27a: e1 eb ldi r30, 0xB1 ; 177
27c: f0 e0 ldi r31, 0x00 ; 0
27e: 80 81 ld r24, Z
280: 84 60 ori r24, 0x04 ; 4
282: 80 83 st Z, r24
// configure timer 2 for phase correct pwm (8-bit)
#if defined(TCCR2) && defined(WGM20)
sbi(TCCR2, WGM20);
#elif defined(TCCR2A) && defined(WGM20)
sbi(TCCR2A, WGM20);
284: e0 eb ldi r30, 0xB0 ; 176
286: f0 e0 ldi r31, 0x00 ; 0
288: 80 81 ld r24, Z
28a: 81 60 ori r24, 0x01 ; 1
28c: 80 83 st Z, r24
#endif
#if defined(ADCSRA)
// set a2d prescaler so we are inside the desired 50-200 KHz range.
#if F_CPU >= 16000000 // 16 MHz / 128 = 125 KHz
sbi(ADCSRA, ADPS2);
28e: ea e7 ldi r30, 0x7A ; 122
290: f0 e0 ldi r31, 0x00 ; 0
292: 80 81 ld r24, Z
294: 84 60 ori r24, 0x04 ; 4
296: 80 83 st Z, r24
sbi(ADCSRA, ADPS1);
298: 80 81 ld r24, Z
29a: 82 60 ori r24, 0x02 ; 2
29c: 80 83 st Z, r24
sbi(ADCSRA, ADPS0);
29e: 80 81 ld r24, Z
2a0: 81 60 ori r24, 0x01 ; 1
2a2: 80 83 st Z, r24
cbi(ADCSRA, ADPS2);
cbi(ADCSRA, ADPS1);
sbi(ADCSRA, ADPS0);
#endif
// enable a2d conversions
sbi(ADCSRA, ADEN);
2a4: 80 81 ld r24, Z
2a6: 80 68 ori r24, 0x80 ; 128
2a8: 80 83 st Z, r24
// here so they can be used as normal digital i/o; they will be
// reconnected in Serial.begin()
#if defined(UCSRB)
UCSRB = 0;
#elif defined(UCSR0B)
UCSR0B = 0;
2aa: 10 92 c1 00 sts 0x00C1, r1
2ae: 08 95 ret
000002b0 <turnOffPWM>:
//
//static inline void turnOffPWM(uint8_t timer) __attribute__ ((always_inline));
//static inline void turnOffPWM(uint8_t timer)
static void turnOffPWM(uint8_t timer)
{
switch (timer)
2b0: 83 30 cpi r24, 0x03 ; 3
2b2: 81 f0 breq .+32 ; 0x2d4 <turnOffPWM+0x24>
2b4: 28 f4 brcc .+10 ; 0x2c0 <turnOffPWM+0x10>
2b6: 81 30 cpi r24, 0x01 ; 1
2b8: 99 f0 breq .+38 ; 0x2e0 <turnOffPWM+0x30>
2ba: 82 30 cpi r24, 0x02 ; 2
2bc: a1 f0 breq .+40 ; 0x2e6 <turnOffPWM+0x36>
2be: 08 95 ret
2c0: 87 30 cpi r24, 0x07 ; 7
2c2: a9 f0 breq .+42 ; 0x2ee <turnOffPWM+0x3e>
2c4: 88 30 cpi r24, 0x08 ; 8
2c6: b9 f0 breq .+46 ; 0x2f6 <turnOffPWM+0x46>
2c8: 84 30 cpi r24, 0x04 ; 4
2ca: d1 f4 brne .+52 ; 0x300 <turnOffPWM+0x50>
{
#if defined(TCCR1A) && defined(COM1A1)
case TIMER1A: cbi(TCCR1A, COM1A1); break;
#endif
#if defined(TCCR1A) && defined(COM1B1)
case TIMER1B: cbi(TCCR1A, COM1B1); break;
2cc: 80 91 80 00 lds r24, 0x0080
2d0: 8f 7d andi r24, 0xDF ; 223
2d2: 03 c0 rjmp .+6 ; 0x2da <turnOffPWM+0x2a>
static void turnOffPWM(uint8_t timer)
{
switch (timer)
{
#if defined(TCCR1A) && defined(COM1A1)
case TIMER1A: cbi(TCCR1A, COM1A1); break;
2d4: 80 91 80 00 lds r24, 0x0080
2d8: 8f 77 andi r24, 0x7F ; 127
#endif
#if defined(TCCR1A) && defined(COM1B1)
case TIMER1B: cbi(TCCR1A, COM1B1); break;
2da: 80 93 80 00 sts 0x0080, r24
2de: 08 95 ret
#if defined(TCCR2) && defined(COM21)
case TIMER2: cbi(TCCR2, COM21); break;
#endif
#if defined(TCCR0A) && defined(COM0A1)
case TIMER0A: cbi(TCCR0A, COM0A1); break;
2e0: 84 b5 in r24, 0x24 ; 36
2e2: 8f 77 andi r24, 0x7F ; 127
2e4: 02 c0 rjmp .+4 ; 0x2ea <turnOffPWM+0x3a>
#endif
#if defined(TCCR0A) && defined(COM0B1)
case TIMER0B: cbi(TCCR0A, COM0B1); break;
2e6: 84 b5 in r24, 0x24 ; 36
2e8: 8f 7d andi r24, 0xDF ; 223
2ea: 84 bd out 0x24, r24 ; 36
2ec: 08 95 ret
#endif
#if defined(TCCR2A) && defined(COM2A1)
case TIMER2A: cbi(TCCR2A, COM2A1); break;
2ee: 80 91 b0 00 lds r24, 0x00B0
2f2: 8f 77 andi r24, 0x7F ; 127
2f4: 03 c0 rjmp .+6 ; 0x2fc <turnOffPWM+0x4c>
#endif
#if defined(TCCR2A) && defined(COM2B1)
case TIMER2B: cbi(TCCR2A, COM2B1); break;
2f6: 80 91 b0 00 lds r24, 0x00B0
2fa: 8f 7d andi r24, 0xDF ; 223
2fc: 80 93 b0 00 sts 0x00B0, r24
300: 08 95 ret
00000302 <pinMode>:
#define ARDUINO_MAIN
#include "wiring_private.h"
#include "pins_arduino.h"
void pinMode(uint8_t pin, uint8_t mode)
{
302: cf 93 push r28
304: df 93 push r29
uint8_t bit = digitalPinToBitMask(pin);
306: 90 e0 ldi r25, 0x00 ; 0
308: fc 01 movw r30, r24
30a: e4 58 subi r30, 0x84 ; 132
30c: ff 4f sbci r31, 0xFF ; 255
30e: 24 91 lpm r18, Z
uint8_t port = digitalPinToPort(pin);
310: fc 01 movw r30, r24
312: e0 57 subi r30, 0x70 ; 112
314: ff 4f sbci r31, 0xFF ; 255
316: 84 91 lpm r24, Z
volatile uint8_t *reg, *out;
if (port == NOT_A_PIN) return;
318: 88 23 and r24, r24
31a: 49 f1 breq .+82 ; 0x36e <pinMode+0x6c>
// JWS: can I let the optimizer do this?
reg = portModeRegister(port);
31c: 90 e0 ldi r25, 0x00 ; 0
31e: 88 0f add r24, r24
320: 99 1f adc r25, r25
322: fc 01 movw r30, r24
324: e2 55 subi r30, 0x52 ; 82
326: ff 4f sbci r31, 0xFF ; 255
328: a5 91 lpm r26, Z+
32a: b4 91 lpm r27, Z
out = portOutputRegister(port);
32c: 8c 55 subi r24, 0x5C ; 92
32e: 9f 4f sbci r25, 0xFF ; 255
330: fc 01 movw r30, r24
332: c5 91 lpm r28, Z+
334: d4 91 lpm r29, Z
if (mode == INPUT) {
uint8_t oldSREG = SREG;
336: 9f b7 in r25, 0x3f ; 63
// JWS: can I let the optimizer do this?
reg = portModeRegister(port);
out = portOutputRegister(port);
if (mode == INPUT) {
338: 61 11 cpse r22, r1
33a: 08 c0 rjmp .+16 ; 0x34c <pinMode+0x4a>
uint8_t oldSREG = SREG;
cli();
33c: f8 94 cli
*reg &= ~bit;
33e: 8c 91 ld r24, X
340: 20 95 com r18
342: 82 23 and r24, r18
344: 8c 93 st X, r24
*out &= ~bit;
346: 88 81 ld r24, Y
348: 82 23 and r24, r18
34a: 0a c0 rjmp .+20 ; 0x360 <pinMode+0x5e>
SREG = oldSREG;
} else if (mode == INPUT_PULLUP) {
34c: 62 30 cpi r22, 0x02 ; 2
34e: 51 f4 brne .+20 ; 0x364 <pinMode+0x62>
uint8_t oldSREG = SREG;
cli();
350: f8 94 cli
*reg &= ~bit;
352: 8c 91 ld r24, X
354: 32 2f mov r19, r18
356: 30 95 com r19
358: 83 23 and r24, r19
35a: 8c 93 st X, r24
*out |= bit;
35c: 88 81 ld r24, Y
35e: 82 2b or r24, r18
360: 88 83 st Y, r24
362: 04 c0 rjmp .+8 ; 0x36c <pinMode+0x6a>
SREG = oldSREG;
} else {
uint8_t oldSREG = SREG;
cli();
364: f8 94 cli
*reg |= bit;
366: 8c 91 ld r24, X
368: 82 2b or r24, r18
36a: 8c 93 st X, r24
SREG = oldSREG;
36c: 9f bf out 0x3f, r25 ; 63
}
}
36e: df 91 pop r29
370: cf 91 pop r28
372: 08 95 ret
00000374 <digitalWrite>:
#endif
}
}
void digitalWrite(uint8_t pin, uint8_t val)
{
374: 0f 93 push r16
376: 1f 93 push r17
378: cf 93 push r28
37a: df 93 push r29
37c: 1f 92 push r1
37e: cd b7 in r28, 0x3d ; 61
380: de b7 in r29, 0x3e ; 62
uint8_t timer = digitalPinToTimer(pin);
382: 28 2f mov r18, r24
384: 30 e0 ldi r19, 0x00 ; 0
386: f9 01 movw r30, r18
388: e8 59 subi r30, 0x98 ; 152
38a: ff 4f sbci r31, 0xFF ; 255
38c: 84 91 lpm r24, Z
uint8_t bit = digitalPinToBitMask(pin);
38e: f9 01 movw r30, r18
390: e4 58 subi r30, 0x84 ; 132
392: ff 4f sbci r31, 0xFF ; 255
394: 14 91 lpm r17, Z
uint8_t port = digitalPinToPort(pin);
396: f9 01 movw r30, r18
398: e0 57 subi r30, 0x70 ; 112
39a: ff 4f sbci r31, 0xFF ; 255
39c: 04 91 lpm r16, Z
volatile uint8_t *out;
if (port == NOT_A_PIN) return;
39e: 00 23 and r16, r16
3a0: c9 f0 breq .+50 ; 0x3d4 <digitalWrite+0x60>
// If the pin that support PWM output, we need to turn it off
// before doing a digital write.
if (timer != NOT_ON_TIMER) turnOffPWM(timer);
3a2: 88 23 and r24, r24
3a4: 21 f0 breq .+8 ; 0x3ae <digitalWrite+0x3a>
3a6: 69 83 std Y+1, r22 ; 0x01
3a8: 0e 94 58 01 call 0x2b0 ; 0x2b0 <turnOffPWM>
3ac: 69 81 ldd r22, Y+1 ; 0x01
out = portOutputRegister(port);
3ae: e0 2f mov r30, r16
3b0: f0 e0 ldi r31, 0x00 ; 0
3b2: ee 0f add r30, r30
3b4: ff 1f adc r31, r31
3b6: ec 55 subi r30, 0x5C ; 92
3b8: ff 4f sbci r31, 0xFF ; 255
3ba: a5 91 lpm r26, Z+
3bc: b4 91 lpm r27, Z
uint8_t oldSREG = SREG;
3be: 9f b7 in r25, 0x3f ; 63
cli();
3c0: f8 94 cli
if (val == LOW) {
*out &= ~bit;
3c2: 8c 91 ld r24, X
out = portOutputRegister(port);
uint8_t oldSREG = SREG;
cli();
if (val == LOW) {
3c4: 61 11 cpse r22, r1
3c6: 03 c0 rjmp .+6 ; 0x3ce <digitalWrite+0x5a>
*out &= ~bit;
3c8: 10 95 com r17
3ca: 81 23 and r24, r17
3cc: 01 c0 rjmp .+2 ; 0x3d0 <digitalWrite+0x5c>
} else {
*out |= bit;
3ce: 81 2b or r24, r17
3d0: 8c 93 st X, r24
}
SREG = oldSREG;
3d2: 9f bf out 0x3f, r25 ; 63
}
3d4: 0f 90 pop r0
3d6: df 91 pop r29
3d8: cf 91 pop r28
3da: 1f 91 pop r17
3dc: 0f 91 pop r16
3de: 08 95 ret
000003e0 <initVariant>:
int atexit(void (* /*func*/ )()) { return 0; }
// Weak empty variant initialization function.
// May be redefined by variant files.
void initVariant() __attribute__((weak));
void initVariant() { }
3e0: 08 95 ret
000003e2 <main>:
void setupUSB() __attribute__((weak));
void setupUSB() { }
int main(void)
{
init();
3e2: 0e 94 1d 01 call 0x23a ; 0x23a <init>
initVariant();
3e6: 0e 94 f0 01 call 0x3e0 ; 0x3e0 <initVariant>
#if defined(USBCON)
USBDevice.attach();
#endif
setup();
3ea: 0e 94 70 00 call 0xe0 ; 0xe0 <setup>
for (;;) {
loop();
if (serialEventRun) serialEventRun();
3ee: c0 e0 ldi r28, 0x00 ; 0
3f0: d0 e0 ldi r29, 0x00 ; 0
#endif
setup();
for (;;) {
loop();
3f2: 0e 94 74 00 call 0xe8 ; 0xe8 <loop>
if (serialEventRun) serialEventRun();
3f6: 20 97 sbiw r28, 0x00 ; 0
3f8: e1 f3 breq .-8 ; 0x3f2 <main+0x10>
3fa: 0e 94 00 00 call 0 ; 0x0 <__vectors>
3fe: f9 cf rjmp .-14 ; 0x3f2 <main+0x10>
00000400 <yield>:
* libraries or sketches that supports cooperative threads.
*
* Its defined as a weak symbol and it can be redefined to implement a
* real cooperative scheduler.
*/
static void __empty() {
400: 08 95 ret
00000402 <_exit>:
402: f8 94 cli
00000404 <__stop_program>:
404: ff cf rjmp .-2 ; 0x404 <__stop_program>
Вот и всё! Надеюсь, статья оказалась полезной. Оставляйте комментарии!