short URL https://goo.gl/xVY56d
problem & observation https://xiaolaba.wordpress.com/2015/05/27/arduino-c-code-and-problem-for-loop-do-while-loop/
further to test, C code
結論是, ATMEGA2560 (或所有 AVR 8 bit MCU CORE),
1) 沒有 8 bit 常數加法指令 ADD, 只有16 bit 的 adwi, 所以這GCC編譯器加法執行用減法指令 subi, 例如 +1 = -(-1) = -(0xff)
2) 只有用 brne 指令, 所以 “小於或等於" 或 “不大於" 並沒有使用對應指令, <= 無法執行, 除非比較 n+1, 因此 <=0xfe, 編碼試驗 <= (0xfe+1), 實際與 0xff 比較, 無法用 8 bit counter 配合 for-loop 達成 0 至 255 的計數 (256), 需要改用16 bit counter 或棄用 for-loop, 改用 do-while-loop, 並且 i++ 與 ++i 並沒有分別, 實際編碼都是先加值, 再比較. 所以用 asm 編寫 8 bit for-loop, 與C編譯器生成的編碼嚴重分歧. 也解釋了C程序跑飛的實際原因. 到底有甚麼法方法呢 ? 還是 GCC 配 AVR8 就只能這樣 ?
/* test routine to understand what is the for-loop and code generation 2015-05-28 xiaolaba AVR8 arduino 0022. GCC version ? */ void setup() { // initialize the digital pin as an output. // Pin 13 has an LED connected on most Arduino boards: //pinMode(13, OUTPUT); } void loop() { /* //C code, works fine byte i =0; do { i++; __asm ("nop"); __asm ("pop r21"); } while (i != 10) ; //C code, works fine i =0; do { i++; __asm ("nop"); __asm ("pop r23"); } while (i != 0) ; __asm ("nop"); i =0; do { i++; __asm ("nop"); __asm ("pop r23"); } while (i != 0); //C code, works fine for (int i = 0; i < 512; i += 2) { // OK __asm ("nop"); __asm ("pop r24"); } */ //C code, not working but why !!! for (byte i = 0; i < 0xff; i++) { __asm ("nop"); __asm ("pop r25"); } for (byte i = 0; i <= 0xfe; ++i) { __asm ("nop"); __asm ("pop r26"); } for (byte i = 0; i <= 0x10; ++i) { __asm ("nop"); __asm ("pop r27"); } for (byte i = 0; i != 0xff ; ++i) { __asm ("nop"); __asm ("pop r28"); } }
this ASM code, generated for such loop test 0000012e : 12e: 80 e0 ldi r24, 0x00 ; 0 130: 00 00 nop 132: 9f 91 pop r25 134: 8f 5f subi r24, 0xFF ; 255 136: 8f 3f cpi r24, 0xFF ; 255 138: d9 f7 brne .-10 ; 0x130 13a: 80 e0 ldi r24, 0x00 ; 0 13c: 00 00 nop 13e: af 91 pop r26 140: 8f 5f subi r24, 0xFF ; 255 142: 8f 3f cpi r24, 0xFF ; 255 144: d9 f7 brne .-10 ; 0x13c 146: 80 e0 ldi r24, 0x00 ; 0 148: 00 00 nop 14a: bf 91 pop r27 14c: 8f 5f subi r24, 0xFF ; 255 14e: 81 31 cpi r24, 0x11 ; 17 150: d9 f7 brne .-10 ; 0x148 152: 80 e0 ldi r24, 0x00 ; 0 154: 00 00 nop 156: cf 91 pop r28 158: 8f 5f subi r24, 0xFF ; 255 15a: 8f 3f cpi r24, 0xFF ; 255 15c: d9 f7 brne .-10 ; 0x154 15e: 08 95 ret
rem This is asm.bat, uses avr-objdump to obtian assembler listing rem change buildxxxxxxxxxxxxxx.tmp, every time arduino IDE has a different path rem change xxxx.xxx.xxxx.elf, every project has own project name set build=build7733546659278691547.tmp set elf_file=loop_test.cpp.elf set input=C:\DOCUME~1\user\LOCALS~1\Temp\%build%\%elf_file% set output=%elf_file%.txt avr-objdump -d %input% > %output%
.
.
2020-JAN-24, update.
補記, 解決方案
明確定義, 計數器定義為 short, 只有 8 bit, 對比以上案例的 byte 不同.
short i; for (i = 0; i <=0xff; i++) {}
生成的 ASM, 確定可以計數 0 – 0xff, 共256次
short i; for (i = 0; i <=0xff; i++) { 154: 2f 5f subi r18, 0xFF ; 255 156: 3f 4f sbci r19, 0xFF ; 255 158: 81 e0 ldi r24, 0x01 ; 1 15a: 20 30 cpi r18, 0x00 ; 0 15c: 38 07 cpc r19, r24 15e: 11 f6 brne .-124 ; 0xe4
完整源碼,
https://github.com/xiaolaba/ATmega328_usart_print0xff
/* * UART, dump 0x00 to 0xff to terminal * hardware, Arduino Nano, Atmega168p/328p, 16MHz XTAL * compiler, Arduino IDE 1.8.9 or, avr-gcc * xiaolaba * 2020-JAN-24 */ #include avr/io.h #include util/twi.h #define F_CPU 16000000L #define SERIAL_BAUD 115200L #define UBBRn_V ((F_CPU / 16 + SERIAL_BAUD / 2) / SERIAL_BAUD - 1) static void usart_putstr(char *s); static void usart_putchar(char data); static void usart_printhex(uint8_t v); static void serial_init(void); static char HEX_TABLE[]="0123456789ABCDEF"; int main(void) { serial_init(); return 0; } /** Initializes the serial port and prints "banner. */ // serial port 115200 N81 static void serial_init(void) { UBRR0H = UBBRn_V >> 8; UBRR0L = UBBRn_V; UCSR0B = _BV(TXEN0); usart_putstr("xiaolaba\n"); usart_putstr("Arduino Nano, ATmega168, 115200, N81, serial print 0x00 - 0xff\n"); short i; for (i = 0; i > 4; usart_putchar(HEX_TABLE[vn]); vn = v & 0x0F; usart_putchar(HEX_TABLE[vn]); } // ******************************************************************************** // usart Related // ******************************************************************************** static void usart_putchar(char data) { // Wait for empty transmit buffer while ( !(UCSR0A & (_BV(UDRE0))) ); // Start transmission UDR0 = data; } static void usart_putstr(char *s) { // loop through entire string while (*s) { usart_putchar(*s); s++; } }
a.bat, 不開 Arduino IDE, 直接用 avr-gcc 編譯也可以
@echo off ::set mcu=mega324p set mcu=mega168p set main=usart_print0xff.ino set ac=C:\WinAVR-20100110 path %ac%\bin;%ac%\utils\bin;%path% avr-gcc.exe -dumpversion avr-gcc.exe -xc -Os -mmcu=at%mcu% -Wall -g -o %main%.out *.ino ::avr-gcc.exe -O2 -Wl,-Map,%1.map -o %1.out %1.c %2 %3 -mmcu=at%mcu% cmd /c avr-objdump.exe -h -S %main%.out >%main%.lst cmd /c avr-objcopy.exe -O ihex %main%.out %main%.hex avr-size.exe %main%.out del %main%.out ::goto end ::pboot.exe -c1 -b19200 -p%main%.hex ::l.exe -b9600 :end
結果的畫面如下