This is a surprisingly common question. When using WinAVR, how do you get printf() working on an Atmel Xmega processor? Hopefully this post will help explain how.
First of all, this documentation page has some good information regarding the different flavours of WinAVR printf:
http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html
Note the existence of the printf_P() function, which makes it easy to work with strings kept in program space (flash). This can be super useful on an AVR, which has very little SRAM but loads of flash. The regular printf() function will put your text string in SRAM, which is not always ideal.
The big question after reading that documentation page is: how do I initialise the UART, and how do I implement the putchar function, on an Xmega? Because the example code on that webpage is geared towards the non-xmega parts.
Here’s some Xmega example code. Feel free to use it however you see fit.
#include
#include
static int uart_putchar(char c, FILE *stream);
static void uart_init (void);
static FILE mystdout = FDEV_SETUP_STREAM (uart_putchar, NULL, _FDEV_SETUP_WRITE);
int main (void)
{
uart_init();
stdout = &mystdout;
while (1)
printf("Hello, world!\n");
}
static int uart_putchar (char c, FILE *stream)
{
if (c == '\n')
uart_putchar('\r', stream);
// Wait for the transmit buffer to be empty
while ( !( USARTC0.STATUS & USART_DREIF_bm) );
// Put our character into the transmit buffer
USARTC0.DATA = c;
return 0;
}
// Init USART. Transmit only (we're not receiving anything)
// We use USARTC0, transmit pin on PC3.
// Want 9600 baud. Have a 2 MHz clock. BSCALE = 0
// BSEL = ( 2000000 / (2^0 * 16*9600)) -1 = 12
// Fbaud = 2000000 / (2^0 * 16 * (12+1)) = 9615 bits/sec
static void uart_init (void)
{
// Set the TxD pin high - set PORTC DIR register bit 3 to 1
PORTC.OUTSET = PIN3_bm;
// Set the TxD pin as an output - set PORTC OUT register bit 3 to 1
PORTC.DIRSET = PIN3_bm;
// Set baud rate & frame format
USARTC0.BAUDCTRLB = 0; // BSCALE = 0 as well
USARTC0.BAUDCTRLA = 12;
// Set mode of operation
USARTC0.CTRLA = 0; // no interrupts please
USARTC0.CTRLC = 0x03; // async, no parity, 8 bit data, 1 stop bit
// Enable transmitter only
USARTC0.CTRLB = USART_TXEN_bm;
}
This code shows the things you need to do, in its most basic form. It only demonstrates transmitting out the UART; there’s no receive here (we are talking about printf, after all). This code:
- sets up the stdout FILE (mystdout in this example)
- initialises the UART. In this example USARTC0 is used, but you can of course use any USART block.
- implements the putchar function
- does a printf(“hello world”) forever in main().
Note the initialisation sequence. With the Xmega it’s important to manually set the UART transmit pin, ie the corresponding port pin, as an output, and set it high, before configuring the UART. This is specified in the Xmega documentation, and is different to most other AVR parts which don’t have this requirement.
Note also that the UART is set to 9600 baud, no parity, 8 data bits, 1 stop bit. I have this code running on an ATXmega128 board (an Atmel Xplain board).
This should be enough to get things started, and you can build on it from here.
Thank you very much. I am working on getting scanf working too. Once I am done I will update you
Thanks for the tip.
hi
if you change FCPU you must change value USARTC0.BAUDCTRLA
BSEL = ( 32000000/ (2^0 * 16*9600)) -1 = 207
I’ve no idea – I suggest you post your question over at avrfreaks.
Hi, can you please show us how to send data from hyperterminal to xplain xmega ? I’am tryign for several days now, and i cant get it to work 🙁 Some help would be apprecieated 🙂
ups second dosen´t works
Hallo Frank,
thanks for the easy code.
I tried to run this on 32 MHz
If I use :
USARTC0.BAUDCTRLB = 0; // BSCALE = 0 as well
USARTC0.BAUDCTRLA = 207;
It works.
If I try
USARTC0.BAUDCTRLB = 4; // BSCALE = 0 as well
USARTC0.BAUDCTRLA = 12;
(calculatet by a Baudrate Genrator from chip45)
Sorry I´m just beginner in C 😉
regards
Jörg