Elastic Sheep

Because elasticdog was already taken

Elastic Sheep header image 2

Testing the serial interface and the GPS

November 3rd, 2009 · 4 Comments · Project Wiggum

In this post I will connect my EM-406A GPS module to the prototype and write some code to test the sending and reception of serial data.

Connecting the GPS

I already wrote about the connection of the EM-406A GPS module to an ATMEGA168 and Teensy AT90USB162 in this post and this post.

I will use the same level adaptation circuit to shift the 2.8V of the GPS TX to the 5V level required by an ATMEGA168 input.

Connection of level shifter to ATMEGA168 serial input

EM406-A GPS connection on level shifter to ATMEGA168 serial input

The TX pin of the ATMEGA168 is connected to the PC through a USB-to-serial adapter. The next photo shows an overview of the current state of the prototype.

Wiggum prototype circuit overview

Test 1: sending bytes

First I configure the serial interface at 4800 bauds. I enable the receiver and the transceiver and I set the frame format to 8 bits data and 1 bit stop:

  #define BAUD_RATE 4800
  #define BAUD_RATE_UBRR (F_CPU/16/BAUD_RATE - 1)
 
  /* ... */
 
  // Setup the UART baud rate
  UBRR0H = (BAUD_RATE_UBRR >> 8);
  UBRR0L = BAUD_RATE_UBRR;
 
  // Enable the UART receiver and transceiver
  UCSR0B = _BV(RXEN0) | _BV(TXEN0);
  // Set the frame format : 8 bits data, 1 bit stop
  UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);

The UBBR0 register value is computed from the baud rate and the clock frequency.

To transmit a byte to the UART, you have to wait for the transmit buffer being empty then you can write the byte to transmit. This example comes straight from the section 18 of the ATMEGA168 datasheet.

  while (!(UCSR0A & (1 << UDRE0)));
  UDR0 = byte;

Then I write a mainloop that loops a byte variable from ‘A’ to ‘Z’ and write it to the UART:

  for(;;)
  {  
    byte++;
    if (byte == 'Z')
      byte = 'A';
 
    while (!(UCSR0A & (1 << UDRE0)));
    UDR0 = byte;
 
    delay_ms(200);
  }

You can see the result in the following video:

Test 2: sending bytes with printf

Now that we are able to send a byte to the UART, we can implement a printf.

The avr-libc library provides various printf implementation. If you don’t need to format float numbers, you can save memory by using only the standard implementation. An explanation of the different available implementation can be found here

In the Makefile I use, you can select you preferred flavor of printf with the PRINTF_LIB variable.

# If this is left blank, then it will use the Standard printf version.
PRINTF_LIB = 
#PRINTF_LIB = $(PRINTF_LIB_MIN)
#PRINTF_LIB = $(PRINTF_LIB_FLOAT)

To use the printf function you need to define the stdout implementation:

void uart_write(uint8_t byte)
{
  while (!(UCSR0A & (1 << UDRE0)));
  UDR0 = byte;
}
 
int uart_stream_write(char c, FILE *stream)
{
  if (c == '\n')
    uart_stream_write('\r', stream);
 
  uart_write(c);
  return 0;
}
 
static FILE uart_stdout = FDEV_SETUP_STREAM(uart_stream_write, NULL, _FDEV_SETUP_WRITE);

The uart_write function synchronously writes one byte on the UART. The uart_stream_write is a stream write handler for FDEV_SETUP_STREAM. I have added the automatic insertion of the carriage return character to be compatible with telnet but still use only the line feed character internally as in the Unix world.

In my init code I associate stdout with the uart_stdout implementation:

  // Setup stdout
  stdout = &uart_stdout;

The printf function uses stdout to print characters that will be redirected to the UART.

My test mainloop prints numbered lines every 500ms:

  /* Main loop */
  for(;;)
  {  
    counter++;
    if (counter == 100)
      counter = 0;
 
    printf("Line %02i\n", counter);
    delay_ms(500);
  }

Here is a video of the result:

Test 3: receiving bytes from the GPS

To read bytes from the UART, you wait for the UART to have received a character then you can read the received character in UDR0:

uint8_t uart_read(void)
{
  while (!(UCSR0A & (1 << RXC0)));
  return UDR0;
}

To use the standard C function getc and gets, we need an implementation of stdin. This implementation will be similar to the implementation of stdout:

int uart_stream_read(FILE *stream)
{
  return uart_read();
}
 
static FILE uart_stdin = FDEV_SETUP_STREAM(NULL, uart_stream_read, _FDEV_SETUP_READ);

And in my init code:

  stdin = &uart_stdin;

My test main loop will now simply read a character from the UART and then print it back:

  /* Main loop */
  for(;;)
  {  
    uint8_t byte = getc(stdin);
    printf("%c", byte);
  }

The data source will be the GPS that once powered output NMEA sentences at 4800 bauds. The following video shows the result of the test:

We first observe the boot data from the GPS prefixed with $PSRFTXT: firmware version, number of channels, baud rate… Then the GPS starts to output the default NMEA sentences ($GPGGA, $GPGSA, $GPRMC). As I am using the GPS indoor, it can not have any position fix on satellites so the received data are not very interesting.

Some basic data in a $GPRMC sentence:

$GPRMC,194148.772,V,,,,,,,131009,,*26

The first value is the time, 19h41. Then the V character means the GPS don’t have a position fix. When locking on satellites, the V becomes a A. At last we have the current date, October 13th 2009.

You can find information about the NMEA sentences supported by the EM406-A GPS in its user manual.

Coming next

I have connected and checked all the peripherals I need for my project thus defining the connections and the pin configurations. From that I will capture a schematic and draw the layout of a prototype PCB.

Full source code

I have gathered all the tests in one directory. You have to choose the test you would like to build in the Makefile:

# Target file name (without extension).
TARGET = test_serial_1
#TARGET = test_serial_2
#TARGET = test_serial_3

test_serial.zip

Tags: ···

4 Comments so far ↓

  • Olivier

    marrant :)

  • ril3y

    Hey man.. Good stuff.

    Quick question:
    Then I write a mainloop that loops a byte variable from ‘A’ to ‘Y’ and write it to the UART:

    Is A-Y a NEMA protocol thing? Meaning why were those values looped through..

    Ril3y

  • The Chief Sheep

    Hi ril3y,

    In fact I mean from the ‘A’ to the ‘Z’ character ;) Post fixed.
    It has nothing to do with the NMEA protocol apart than the NMEA protocol uses a subset of the ASCII character set. The goal just was to repeatedly send human readable ASCII data to check their reception on the PC.

  • ril3y

    Thanks for the clear up. Can you toss me an email? I didnt see yours here. Wanted to ask you a few questions.

    riley (nospace) porter [at] gmail dot com

Leave a Comment