Solution:
Turns out it did not have anything to do with the USART. The problem was in the Makefile. I reused a Makefile I have been using for atmega328p and atmega2560 for a long time. But this Makefile does not include the .rodata section when converting the elf to hex file. I guess this section is never used for the "older" atmega parts. After including the .rodata section in the hex file everything worked as expected for the atmega4809.
Makefile part before:
$(OUT).hex: $(OUT).elf
rm -f $(OUT).hex $(OUT).eep.hex
$(OBJCOPY) -j .text -j .data -O ihex $(OUT).elf $(OUT).hex
$(SIZE) $(OUT).hex
Makefile part after:
$(OUT).hex: $(OUT).elf
rm -f $(OUT).hex $(OUT).eep.hex
$(OBJCOPY) -j .text -j .data -j .rodata -O ihex $(OUT).elf $(OUT).hex
$(SIZE) $(OUT).hex
Original question:
The following code results in:
#include <avr/io.h>
#define BAUDRATE 115200
#define BAUD_REG (F_CPU * 64UL) / (16UL * BAUDRATE)
void usart_init(USART_t * usart) {
// Sets up usart
usart->BAUD = BAUD_REG;
usart->CTRLB = (1 << USART_TXEN_bp) | (1 << USART_RXEN_bp);
}
void usart_write(USART_t * usart, char c) {
// Wait TX ready
while (!(usart->STATUS & USART_DREIF_bm)) {;;}
// Send character
usart->TXDATAL = c;
}
int main(void)
{
// Setup 16 MHz clock internal clock
CCP = 0xD8;
CLKCTRL_MCLKCTRLB = 0;
// Setup uart1
// Set alternate pin mux for uart1 to PC4 and PC5
PORTMUX.USARTROUTEA |= PORTMUX_USART1_0_bm;
// Set TX pin output
VPORTC.DIR |= PIN4_bm;
// Setup USART1
usart_init(&USART1);
// String to send
char st[] = "Hello world!";
// Pointer to iterate of characters
char * st_ptr = st;
// Send char -> works fine
usart_write(&USART1, 'a');
// Send string -> sends garbage
while (*st_ptr) {
usart_write(&USART1, *st_ptr++);
}
// Send another char -> works fine
usart_write(&USART1, 'b');
while (1) {;;}
}
I would expect "aHello world!b".
Writing single characters always works fine. When writing strings, the first character is fine the remaining characters result in 0xFF.
I've tried different baudrates (all same result).
Different compilers (The microchip toolchain, manually build gcc).
Adding a delay after sending each character. (same result).
What am I missing here?
Update: I changed to sending code to:
// Send char -> works fine
usart_write(&USART1, 'a');
// String to send
const char* st = "Hello world!";
uint8_t i;
for (i=0; i<8; i++) {
char s = st[i];
usart_write(&USART1, s);
}
// Send another char -> works fine
usart_write(&USART1, 'b');
No difference unfortunately: a��������b
Update 2, progress:
// Send char -> works fine
usart_write(&USART1, 'a');
// String to send
const char* st = "Hello world!";
usart_write(&USART1, st[0]);
usart_write(&USART1, st[1]);
usart_write(&USART1, st[2]);
usart_write(&USART1, st[3]);
usart_write(&USART1, st[4]);
// Send another char -> works fine
usart_write(&USART1, 'b');
Results in: aHellob
update 3:
// Send char -> works fine
usart_write(&USART1, 'a');
// String to send
const char* st = "UUUUUUUU";
uint8_t i;
for (i=0; i<8; i++) {
char s = st[i];
usart_write(&USART1, s);
}
usart_write(&USART1, st[0]);
usart_write(&USART1, st[1]);
usart_write(&USART1, st[2]);
usart_write(&USART1, st[3]);
usart_write(&USART1, st[4]);
// Send another char -> works fine
usart_write(&USART1, 'b');
No luck: a��������UUUUUb
Update 4 (delay added):
// Send char -> works fine
usart_write(&USART1, 'a');
// String to send
const char* st = "Hello world!";
uint8_t i;
for (i=0; i<8; i++) {
char s = st[i];
usart_write(&USART1, s);
_delay_ms(10);
}
usart_write(&USART1, st[0]);
usart_write(&USART1, st[1]);
usart_write(&USART1, st[2]);
usart_write(&USART1, st[3]);
usart_write(&USART1, st[4]);
// Send another char -> works fine
usart_write(&USART1, 'b');
No luck: a��������Hellob
update 5: Found: hello world example Where the following code is used:
void USART1_sendChar(char c)
{
while (!(USART1.STATUS & USART_DREIF_bm))
{
;
}
USART1.TXDATAL = c;
}
void USART1_sendString(char *str)
{
for(size_t i = 0; i < strlen(str); i++)
{
USART1_sendChar(str[i]);
}
}
Seems very similar
Update 6: Changed clock to 20MHz internal oscillator Disabled compiler optimizations, makefile snip:
# parent directory of the toolchain, device pack, and pyupdi directories
AVR_BASE_DIR := /home/paul/workspace/atmega4809
#AVR_TOOLCHAIN_DIR := $(AVR_BASE_DIR)/avr8-gnu-toolchain-$(HOST_OS)_$(HOST_ARCH)
AVR_TOOLCHAIN_DIR := /usr
AVR_DFP_DIR := $(lastword $(sort $(wildcard $(AVR_BASE_DIR)/Atmel.ATmega_DFP*)))
#CFLAGS_COMMON = -O2 -B $(AVR_DFP_DIR)/gcc/dev/$(DEVICE) -I $(AVR_DFP_DIR)/include -Wall -DF_CPU=$(F_CPU) -mmcu=$(DEVICE) -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -fdata-sections -ffunction-sections --param=min-pagesize=0 -mrelax -MMD -MP -Wa,-adhlns=$(OUT).lst
CFLAGS_COMMON = -O0 -B $(AVR_DFP_DIR)/gcc/dev/$(DEVICE) -I $(AVR_DFP_DIR)/include -Wall -DF_CPU=$(F_CPU) -mmcu=$(DEVICE) --param=min-pagesize=0 -mrelax -MMD -MP -Wa,-adhlns=$(OUT).lst
// Send char -> works fine
usart_write(&USART1, 'a');
// String to send
const char* st = "Hello world!";
uint8_t i;
for (i=0; i<8; i++) {
char s = st[i];
usart_write(&USART1, s);
}
usart_write(&USART1, st[0]);
usart_write(&USART1, st[1]);
usart_write(&USART1, st[2]);
usart_write(&USART1, st[3]);
usart_write(&USART1, st[4]);
// Send another char -> works fine
usart_write(&USART1, 'b');
Now every char from string "st" fails : a�������������b
Turning compiler optimizations back on:
CFLAGS_COMMON = -O2 -B $(AVR_DFP_DIR)/gcc/dev/$(DEVICE) -I $(AVR_DFP_DIR)/include -Wall -DF_CPU=$(F_CPU) -mmcu=$(DEVICE) -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -fdata-sections -ffunction-sections --param=min-pagesize=0 -mrelax -MMD -MP -Wa,-adhlns=$(OUT).lst
Some chars are working again:
a��������Hellob
Update 7:
const char st[] = "Hello world!";
results in: a��������He���b
const char * st = "Hello world!";
results in: a��������Hellob
update 8: Switched to usart2: Same result
Update 9: Its the initialization of the array:
// String to send
char st[8];
st[0] = 'H';
st[1] = 'e';
st[2] = 'l';
st[3] = 'l';
st[4] = 'o';
st[5] = ' ';
st[6] = 'w';
st[7] = 'o';
// Send char -> works fine
usart_write(&USART2, 'a');
uint8_t i;
for (i=0; i<8; i++) {
char s = st[i];
usart_write(&USART2, s);
}
usart_write(&USART2, st[0]);
usart_write(&USART2, st[1]);
usart_write(&USART2, st[2]);
usart_write(&USART2, st[3]);
usart_write(&USART2, st[4]);
// Send another char -> works fine
usart_write(&USART2, 'b');
Result: aHello woHellob