Monthly Archives: February 2016

Debugging Micro-controller Programs over USART

This 13th article in the series of “Do It Yourself: Electronics”, demonstrates debugging of micro-controller programs over the USART.

<< Previous Article

With the working of the basic serial functionality, one can directly use that for debugging by logging on serial console. However, Pugs wanted to make it more sophisticated. So, he turned the earlier serial.c into a library file by including serial.h in it, and removing main() from it, with the corresponding function prototypes added into the new file serial.h. With that the serial.h looks as follows:

#ifndef SERIAL_H

#define SERIAL_H

#include <avr/io.h>

void usart_init(unsigned long baud);
void usart_shut(void);
void usart_byte_tx(uint8_t data);
int usart_byte_available(void);
uint8_t usart_byte_rx(void);
void usart_tx(char *str);
void usart_rx(char *str, int max_len);

#endif

Then, the debugging wrapper functions were implemented in debug.c with the following prototypes in debug.h:

#ifndef DEBUG_H

#define DEBUG_H

#include <avr/io.h>

void debug_init(void);
void debug_shut(void);
void print_nl(void);
void print_str(char *str);
void print_str_nl(char *str);
void print_num(uint32_t n);
void print_hex(uint32_t n);
char scan_char(void);
void scan_line(char *line, int max_len);

#endif

The complete implementation of debug.c goes here:

#include "serial.h"
#include "debug.h"

void debug_init(void)
{
	usart_init(9600);
}
void debug_shut(void)
{
	usart_shut();
}

void print_nl(void)
{
	usart_tx("\r\n");
}
void print_str(char *str)
{
	usart_tx(str);
}
void print_str_nl(char *str)
{
	usart_tx(str);
	usart_tx("\r\n");
}
void print_num(uint32_t n)
{
	int i;
	char num[11];

	for (i = 9; i >= 0; i--)
	{
		num[i] = '0' + (n % 10);
		n /= 10;
	}
	num[10] = 0;
	usart_tx(num);
}
void print_hex(uint32_t n)
{
	int i;
	char c;

	usart_byte_tx('0');
	usart_byte_tx('x');
	for (i = 7; i >= 0; i--)
	{
		c = ((n >> (i << 2)) & 0xF);
		c += ((c <= 9) ? '0' : (-10 + 'A'));
		usart_byte_tx(c);
	}
}
char scan_char(void)
{
	return usart_byte_rx();
}
void scan_line(char *line, int max_len)
{
	usart_rx(line, max_len);
}

And to test the complete debug library, Pugs wrote the following debug_test.c:

#include <avr/io.h>

#include "debug.h"

int main(void)
{
	char str[32];
	uint32_t value = (1L << 26);

	debug_init();

	scan_char(); // Waiting for a character, typically an 
	print_str_nl("Welcome to SysPlay's serial debugger");
	print_str("(1L << 26) in decimal: ");
	print_num(value);
	print_nl();
	print_str("(1L << 26) in hexadecimal: ");
	print_hex(value);
	print_nl();
	while (1)
	{
		print_str("SysPlay> ");
		scan_line(str, 32);
		print_str("You gave: ");
		print_str(str);
		print_nl();
	}

	debug_shut();

	return 0;
}

Then, Pugs compiled all the programs together as follows:

$ avr-gcc -mmcu=atmega16 -DF_CPU=1000000 -Os debug_test.c serial.c debug.c -o
debug_test.elf
$ avr-objcopy -O ihex debug_test.elf debug_test.hex

And finally, downloaded the debug_test.hex into the ATmega16 with J1 shorted (same as in the previous articles), using the following command:

$ avrdude -c ponyser -P /dev/ttyUSB0 -p m16 -U flash:w:debug_test.hex:i

Pugs then connected the USB to TTL converter to his laptop on the USB side, and the RX, TX, GND pins of the USB to TTL converter to the TXD, RXD, GND pins of the micro-controller. And started the serial application minicom as root on his laptop as follows:

# minicom -D /dev/ttyUSB1 -b 9600 -o

Note that Pugs’ TTL to USB connector shows up as /dev/ttyUSB1 on his laptop. In case, yours is different, use that instead. And then, as soon as Pugs removed the jumper J1, he got the value printed in decimal and then in hexadecimal followed by the “SysPlay> ” prompt for taking string inputs and displaying them back. To see what he was typing, Pugs typed Ctrl-A E on minicom. And, to quit from minicom, Pugs typed Ctrl-A Q.

Next Article >>

   Send article as PDF