Arduino, why C code and problem, for-loop


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

結果的畫面如下
usart_print0xff_result

發表留言

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料