Since the serial communication with the terminal window
is a very useful troubleshooting tool,
it is important to develop code for serial communication with the processor.
The figure shows a simplified block diagram of the processor ATmega USART.
The actual circuit is still more complex and is described in detail in the processor data sheet.
We limit ourselves here to initiate and use the USART for easy communication with the Arduino terminal window.
The USART in the Block diagram has three control registers UCSR0A, UCSR0B, UCSR0C, where you configure the settings that control the unit. Register bits has their own names, and we will need to use the bits with these names RXEN0 TXEN0 (i UCSRB), RXC0 (i UCSRA), UDRE (i UCSRA), UCSZ01 UCSZ00 (i UCSRC).
The setting of the data transfer rate, Baude rate, is done with the 16-bit register UBRR.
A common transfer rate is 9600 bit/sec.
The data sheet specifies a formula for figuring out what number to place in the UBRR register
(UBRR consists of two 8 bit registers, but the register pair can be directly accessed as a 16 bit number with the C language).
With bits RXEN0 and TXEN0 you turn on the receiver section and the transmitter section, and the bit pair UCSZ01 UCSZ00 sets the mode - in our case, asynchronous 8 bits.
void init_uart( void ) { UCSR0B |= (1 << ( RXEN0 )) | (1 << ( TXEN0 )); // Transmitter on, Reciever on UCSR0C |= (1 << ( UCSZ01 )) | (1 << ( UCSZ00 )); // Mode: 8 bit asynk UBRR0 = 103; // Baud prescaler 103 -> 9600 Baud }
One retrieves received characters in UDR (Receiver) register. This is "double buffered" so up to two characters can be read. Bit RXC0 (in UCSR0A) is '1' when at least one character is available. As the getchar_uart() below is written, it will be blocking - as long as no character arrives we'll wait here.
unsigned char getchar_uart( void ) { unsigned char ch; // wait until a byte is ready to read while( ( UCSR0A & (1 << ( RXC0 )) ) == 0 ){} // grab the byte from the serial port ch = UDR0; return ch; }
One writes the characters for transmission to UDR (Transmitter) register. The transmitter unit is ready for this when bit UDRE0 (in UCSR0A) is '1'.
void putchar_uart( unsigned char ch ) { // wait until the port is ready to be written to while( ( UCSR0A & ( (1 << ( UDRE0 )) ) == 0 ){} // write the byte to the serial port UDR0 = ch; }
One can try that getchar_uart() and putchar_uart() works by running an echo-program. The writing in the terminal window should then echoed back from Arduino card!
The ATmega processor has a Harvard architecture. RAM memory consists of a small number of General Purpose Register (GPU), and program memory consists of a separate larger flash memory. The programming language C is actually designed for computers with von Newman architecture characterized by that all memory is shared.
unsigned char buffer[20]; const unsigned char hello[] = "hello world";
The first declaration means that the buffer is an array with 20 GPU registers.
The registers can be indexed (eg, the fifth element buffer [4]) or accessed with pointers
(eg, the fifth element * (buffer + 4)).
The second statement is a string constant.
It is a common need to store such various predetermined (= constant) messages to be printed
out at different times in the program.
String constants are initially stored in the program memory,
but to be able to be indexed and accessed by pointers, they will be copied over to
GPU registers at the start of the program.
The problem with this is that the GPU registers often are so few that they are probably needed better for other things!
To avoid wasting RAM one needs to be able to store constants directly in the program memory,
and to reach them there without first copied to RAM.
Atmel has solved this problem by expanding the AVR compiler with a number of macros and functions for this.
They are found in the include file pgmspace.h.
#include <avr/pgmspace.h> const unsigned char FlashString[] PROGMEM = "This is a string held completely in flash memory.";
If you want to print a string constant, without having it first copied to RAM, you have to use macros for special "pointers" to the program memory. The string_out_uart_P() take such a "pointer" to the program memory as argument. The function name is ending with _P to indicate this. Many of C's string functions for the AVR is available in two variants, one with normal pointers to RAM and one for the "pointer" to the program memory (_P).
string_out_uart_P( FlashString ); void string_out_uart_P( const unsigned char * string ) // use for strings in program memory { while ( pgm_read_byte( string ) != 0x00 ) putchar_uart( pgm_read_byte( string ++) ); }
string_out_uart("Hello World\r\n"); // Warning! this string will be copied to RAM registers at program start void string_out_uart( unsigned char * string ) // use for strings in RAM { while (* string != '\0') putchar_uart(*string ++); }
The program Hello World shows how you can use different storage method and various printing functions for strings in RAM and in program memory.
This sample program uses the "Windows style" for Enter, both return and newline characters.
William Sandqvist william@kth.se