PIC32 UART Console

I hate debugging code without a UART console. Don’t get me wrong, using the ICD3 is extremely easy, and allows you to quickly debug things that would take a while to debug through the UART console (like structures, large arrays) and allows for setting breakpoints which come in extremely useful to find out why certain events happened or the program took certain paths, but in developing programs from the ground up, especially with peripherals that you don’t have libraries for, a UART console is often much easier.

Basically, the UART console allows you to control the flow of your program, dispay diagnostic messages, etc. A sample of my most recent UART console output is below:

UART_Console_01

A sample of my most recent UART console, debugging the ENC28J60 SPI - Ethernet Controller.

Implementation is simple. I enable the UART interrupt, and just pass each character to the Console_HandleChar function. The Console_HandleChar just adds the character to a buffer (array) and parses the character received. It handles backspaces correctly (not arrow keys or the delete, home, or end keys, though) and checks for the enter key.

On enter, the buffer is bytewise copied into another buffer (to avoid overwriting / stomping) and a flag (console_entry_flag) is set. The main loop checks this flag, and if it is set, parses the command. Most of my debug consoles use a two character comand (0-255) specified as 00-FF in hex. Anyway, here is the code:

// console.h
#ifndef H_CONSOLE
#define H_CONSOLE

#include <p32xxxx.h>
#include "globals.h"
#include "static.h"
#include "controlboard.h"

#define CONSOLE_BUFFER_WIDTH 32 // Because of iteration variables, this must be < 256

void Console_Init(void);

void Console_HandleChar(unsigned char);

unsigned char Console_getchar(void);

unsigned char Console_getline(void);

#endif
// console.c

#include "console.h"

unsigned char console_buffer[CONSOLE_BUFFER_WIDTH];
unsigned char console_buffer_index;

unsigned char console_entry[CONSOLE_BUFFER_WIDTH];
unsigned char console_entry_index;
unsigned char console_entry_flag;

void Console_Init(void)
{
	unsigned char i;

	for(i=0; i<CONSOLE_BUFFER_WIDTH; i++)
	{
		console_buffer[i] = 0x00;
	}

	console_buffer_index = 0x00;

	return;
}

void Console_HandleChar(unsigned char byte)
{
	unsigned char i;

	console_buffer[console_buffer_index++] = byte;

	switch(byte)
	{
		case 0x7F:									//Escape
		case 0x08:
			console_buffer_index -= 2;				//Rem Escape and Last Char
			_mon_putc(byte);
			_mon_putc(' ');
			_mon_putc(byte);
			break;
		case 0x0d:
			printf("\r\n");
			for(i=0; i<console_buffer_index; i++)
			{
				console_entry[i] = console_buffer[i];
			}
			console_entry_index = console_buffer_index;
			console_entry_flag = 1;

			// Reset Buffer Index
			console_buffer_index = 0;
			break;
		default:
			_mon_putc(byte);
			break;
	}

	if(console_buffer_index >= CONSOLE_BUFFER_WIDTH)
	{
		printf("\r\n");
		console_buffer_index = 0;
	}

	return;
}

The main function is simple and I’ll only show parts that I think are relevant.

Interrupt Handler (PIC32):

void __ISR(_UART_1_VECTOR, ipl4) U1_ISR(void)
{
	if(mU1RXGetIntFlag())
	{
		// Clear the RX interrupt Flag
		mU1RXClearIntFlag();

		// Handle Character
		Console_HandleChar(ReadUART1());
	}

	if ( mU1TXGetIntFlag() )
	{
		mU1TXClearIntFlag();
	}
}

Main Loop:

while(1)
{
	if(console_entry_flag)
	{
		if(console_entry_index < 2)
		{
			printf("1x: ENC Communication                \r\n");
			printf("  10: Read Control Register (10xx)   \r\n");
			printf("  11: Read Buffer Memory             \r\n");
			printf("  12: Write Control Memory (12xxyy)  \r\n");
			printf("  13: Write Buffer Memory (13xx)     \r\n");
			printf("  14: Bit Field Set (14xxyy)         \r\n");
			printf("  15: Bit Field Clear (15xxyy)       \r\n");
			printf("  16: Issue Soft Reset               \r\n");
			printf("  17: Set ENC Register Bank          \r\n");
			printf("  18: Dump ETH Registers in Bank     \r\n");
			printf("  19: Dump Entire ETH Controller     \r\n");
			console_entry_flag = 0;
			continue;
		}
		cmd = form8(ctob(console_entry[0]), ctob(console_entry[1]));
		len = console_entry_index - 1;

		switch(cmd)
		{
			case 0x10:
				if(len < 4){ printf("Check Syntax.\r\n"); break; }
				address = form8(ctob(console_entry[2]), ctob(console_entry[3]));
				data = ENC_ReadControlRegister(address);
				printf("ENC[0x%02x] => 0x%02x\r\n", address, data);
				break;
			case 0x11:
				data = ENC_ReadBufferMemory();
				printf("ENC[BM] = 0x%02x\r\n", data);
				break;
			case 0x12:
				if(len < 6){ printf("Check Syntax.\r\n"); break; }
				address = form8(ctob(console_entry[2]), ctob(console_entry[3]));
				data = form8(ctob(console_entry[4]), ctob(console_entry[5]));
				ENC_WriteControlRegister(address, data);
				printf("ENC[0x%02x] <= 0x%02x\r\n", address, data);
				break;
			case 0x13:
				if(len < 4){ printf("Check Syntax.\r\n"); break; }
				data = form8(ctob(console_entry[2]), ctob(console_entry[3]));
				ENC_WriteBufferMemory(data);
				printf("ENC[BM] => 0x%02x\r\n", data);
				break;
				break;
			case 0x14:
				if(len < 6){ printf("Check Syntax.\r\n"); break; }
				address = form8(ctob(console_entry[2]), ctob(console_entry[3]));
				data = form8(ctob(console_entry[4]), ctob(console_entry[5]));
				ENC_BFS(address, data);
				printf("ENC[0x%02x] <| 0x%02x\r\n", address, data);
				break;
			case 0x15:
				if(len < 6){ printf("Check Syntax.\r\n"); break; }
				address = form8(ctob(console_entry[2]), ctob(console_entry[3]));
				data = form8(ctob(console_entry[4]), ctob(console_entry[5]));
				ENC_BFC(address, data);
				printf("ENC[0x%02x] <: 0x%02x\r\n", address, data);
				break;
			case 0x16:
				ENC_SoftReset();
				printf("Soft Reset Issued to ENC\r\n");
				break;
			case 0x17:
				if(len < 4){ printf("Check Syntax.\r\n"); break; }
				data = form8(ctob(console_entry[2]), ctob(console_entry[3]));
				ENC_SetBank(data);
				printf("ENC Bank Set (%1d)\r\n", data);
				break;
			case 0x18:
				for(i=0; i<0x20; i++)
				{
					data = ENC_ReadControlRegister(i);
					printf("ENC[0x%02x] = 0x%02x", i, data);
					if(i % 2 == 1){ printf("\r\n"); }
					else{ printf("    "); }
				}
				break;
			case 0x19:
				for(j=0; j<4; j++)
				{
					ENC_SetBank(j);
					printf("ENC BANK %1d\r\n", j);
					for(i=0; i<0x20; i++)
					{
						data = ENC_ReadControlRegister(i);
						printf("[0x%02x] = 0x%02x", i, data);
						if(i % 4 == 3){ printf("\r\n"); }
						else{ printf("    "); }
					}
					printf("\r\n");
				}
				break;
			default:
				printf("Command not found. (%02x)\r\n", cmd);
				break;
		}
		console_entry_flag = 0;
	}
}

Anyway, this should give you a good start to creating your own debugging UART console.

But wait! I just assumed you knew what to do with a UART console. Even if your microcontroller had a fully functional UART console, how do you access it. There are many ways, but I’ll touch on the few I use:

  1. Use a UART – USB Bridge. FTDI sells a great IC (somewhat pricey, though, so see #2 for a better solution) that you just slap on the board, connect to the D+ and D- lines of a USB connector (or cut up a USB cable and use that) and connect TX and RX to the microcontroller (remember to switch them, so that the micro’s RX is connected to the FT chip’s TX, etc. — I’ve screwed that up before) — and voila. The FT232RL (my favorite) comes in a small SOIC package, has outputs for LEDs to show transmit/ receive status as well as programmable GPIOs and an internal 3.3V regulator you can run some small draw devices off of. Highly recommeneded, but again, the parts are around $5/ ea. Open up minicom (linux), HypterTerminal or Putty (Windows), connect to the right port, set the baud and you’re good to go.
  2. Use a UART – USB Bridge breakout board. SparkFun sells these. They are just the chip I described in #1 on a breakout board. Solder a 4 pin header on the back of the connector and you have 3.3V, ground, RX and TX. Connect these with clip leads or stick this pin header into a 4-pin socket you’ve added on your dev board. Done and done.
  3. Use the PICKit 2‘s UART Interface — not as full featured as using a real UART interface, but useful if you don’t have access to the SparkFun breakout board or a serial cable to hack up (4). For more info on the PICKit 2′s UART interface, see here and here.
  4. Hack up a serial cable — google the pin out (like here) — connect the RX and TX to the right pins on a buffer IC, connect the buffer IC to the micro and connect this to your computer’s serial port (if you have one).
Posted Saturday, March 7th, 2009 under firmware, tips and tricks.

5 comments

  1. Tom Mignone says:

    Would it be possible to get a copy of the console.h file to go with the above code?

    Thanks

    Tom M.

  2. Tom, check your email.

    Regards, Jim

  3. Great,

    Not having a Console output seems to be the only difference between the 32 I/O and the older 16 I/O expension boards. But without the Console it is difficult to port PIC16/24 to a PIC32.

    I bought 2 sets of Pic32 starter kits + 2×32 I/O board ( with the 120 pins Hirose connector) + the littele boards with the MRF24J40. But after bugging Microchip they admitted that this combination is not supported (yet).

    Is there somebody that knows whether or not this been done?

    By the way: I bought a 20 euro dongle with a PIC16 + MRF24J40 @ ISASensing in Spain/Portugal. They have also a PIC24 + MRF24J40 module and that module and dongle worked without a hitch. But if I can get the PIC32 to run with the P2P or MiWi stacks than I move (back) to my original target processor the PIC32.

    Very good to share this knowledge to that others don’t have to re-invent the wheels.

    André

  4. Hope you could still read this.
    this is a wonderful tip especially if you are coding from scratch.
    Like what I am doing right now. I am having a hard time debugging the code because I don’t have an output console.
    Just a question, is it possible if I dont have USART bridge? I am using PIC32 usb starter kit.

  5. Hi Joseph,

    It is possible with the PIC32 but to be honest with you I’m not sure how. One of the demo programs prints debug output over USB.

Leave a Reply