Real Time Embedded Code On Small Microcontrollers (Part 1)

Introduction

This is the first of two articles. This one describes a very basic way of getting a small embedded project going. It is suitable for very simple projects. In the second article, I show a better way.

a PIC prototype board

Embedded hardware and software gets very messy if you don't have a plan

I like small microcontrollers. I like them because they are cheap, small, solutions that can give great flexibility to a design. I happen to have used Microchip's PIC series for many years, but I have also been involved with 8051 based designs, and would quite happily work with Atmel, ARM, Hitachi or whatever.

Although some of these chips now have generous amounts of memory and 32 bit architectures, there is still a place for tiny micros with only a few hundred bytes of RAM. One of the challenges then is how to write code that is well structured and flexible, and gives known real time performance.

In this article, I actually describe how not to do that - this is more about the simplest way to get up and running with some debug output, using the same connector for both in-circuit programming and serial debug. The code in this article is about as basic as it can be. In the next, I show a better way to do things. But first you need working hardware, and this gets you there.

My plan of attack for firing up a new embedded project usually goes like this:

  1. Build some hardware, with at least one pin of the chip used for a "blink" LED, and another available for serial output.
  2. Get the LED flashing in a simple while loop. This is just to know that the controller is running.
  3. Get serial output working to a PC terminal program.
  4. Write driver functions for the IO devices.
  5. Code the application in some kind of while loop.

The last step obviously depends on what exactly the target board will be used for, which requires careful thought. What follows though is a basic framework which can be reused for many applications.

Debug output

wiring of the debug switcher

Fig 1 : Wiring diagram of the debug switcher

"The most effective debugging tool is still careful thought, coupled with judiciously placed print statements." (Brian Kernhighan)

Getting that blinking LED should not take long (unless I am on new and unfamiliar hardware, but let's not go there). For debug output, I used to use the device's on-board UART, but this is inconvenient, if that pin is needed for something else.

I now prefer either a bit-bashed or interrupt driven code that can be assigned to any pin. On PIC's, the In-Circuit Programming header uses up two IO pins (as well as /MCLR and the power rails). There are quite a lot of hardware restrictions on what you can connect to these, so it is useful to also use them for debugging. As there are TWO pins (luxury!), one can be used to enable debug output, avoiding the need for "#ifdef DEBUG ..." all over your code.

If we are going to use the debug port for programming and serial, we better make it easy. We definitely don't want to be unplugging and replugging every time we add a print statement and reload. I use a little hand-wired adaptor which connects between the ICP connector, the programmer, and the PC serial port. Everything stays connected, and a DPCO switch selects between PROG and DEBUG modes. Fig 1 shows the wiring and Fig 2 is a photo.

I use a PICKIT3 for device programming. It places 4k7 pull-down resistors on the PGD and PGC pins, so I use these to inhibit DEBUG output in my code. When the switch on the adapter is switched to DEBUG, the PGC pin is pulled up to +3V3 with a resistor (hidden in a piece of sleeving in the photo).

PIC debug switcher

Fig 2: The debug switcher on my (slightly burnt) workbench.

As well as the PICKIT3, I am using an RS232 to USB adaptor from Parallax, and a 6 pin programming cable from Tag Connect. (The Tag Connect thing is real nice as it means there is no need for any ICP connector on the PCB.) Because the Tag-Connect is terminated in an RJ12 connector, the wiring looks a little messy, but it works fine. Maybe one day I'll make up a little circuit board. For now, this is good enough to get the job done.

UART code

Now that we have hardware, time for some code. This example is for a PIC 18F1xK22. On this device, PGD is on pin 19, which is also RA0, and PGC is pin 18, RA1. So we need to do several things:

  1. initialise RA0 as an output, and RA1 as an input.
  2. write a macro that tests whether RA1 is high
  3. write a routine to send a single character on RA1

Once that is done, we will be able to print characters to a console. We also really need a decent C printf, which will print strings, formatted numbers, newlines, and so on. I use this very neat little "xprintf" library by a guy called Chan. (He also has some other nice code suitable for small embedded platforms, such as a tiny library for reading and writing a FAT file system.) xprintf is super small, and gives you all you need and no more. To use it, we just have to provide one function, to send a single character : put_char()

main.c

#include "BBUart.h"
#include  "xprintf.h" // Chan's code
// enable or disable terminal reporting in real time
#define D if(PORTAbits.RA1==1)
               .
               .
               .
 void sys_init(void){ 
   // (other init code)
    TRISAbits.TRISA0 = 0; // debug TX output
    TRISAbits.TRISA1 = 1; // switch input
    init_bbuart();
}

//in main code, we can now do:

void main(void){
   D xputs("some debug stuff for my project\r\n");
   D xprintf("variable x = %h\r\n", x);

   while(1){
      // do useful stuff
   }
}

bbuart.h

#ifndef BBUART_H
#define BBUART_H
#include <xc.h> // microchip pin definitions
void  initBBUart(void);
void  put_char(unsigned char b);
#endif /* BBUART_H */

bbuart.c

#include "BBUart.h"
// 300 baud 8n1 with 500kHz clock for PIC18F1xK22, 
// adjust to  your tastes/needs
#define WAIT_BAUD int uc; for  (uc = 0; uc <  33; uc++);
#define BBUART_MARK LATAbits.LA0 = 0;
#define BBUART_SPACE  LATAbits.LA0 = 1;

void init_bbuart(void){
   BBUART_MARK;
}

void put_char(unsigned char b){
// this manually sends a serial byte out  any PIC pin.
   unsigned char i;
   i=8;                           // 8 data bits to send
   BBUART_SPACE;                  // make start bit
   WAIT_BAUD;
   while(i)                       // send 8 serial bits, LSB first
     {
       if(b & 0x01 ){
         BBUART_MARK;
       } else {
         BBUART_SPACE;
       }
      b = (b >> 1);               // rotate right to get next bit
      i--;
      WAIT_BAUD;
     }
   BBUART_MARK;                   // make stop bit
   WAIT_BAUD;
   WAIT_BAUD;
}

And there it is. It is not pretty, and the UART code simply blocks every time it sends a character - but it is light and for simple apps it gets you going. My code set the baud rate at 300 baud, which makes you feel like you are watching a teleprinter in an old movie. You will probably have to tune the loop, based on your setup. The easiest way to do this is do put something like:

while(1){
  put_char(0x55);
}

in your main loop, and tweak the magic numbers until your terminal program fills up with the character 'U'. A scope is also helpful in setting the baud rate.

NB : One thing to note is that when you remove the programming cable, the device will probably start sending out debug code. To stop this, a high value pulldown, say 100k, should be fitted from the PGC line to 0V.

As I've said above, this approach is only really OK for the most simple projects. In the next article I show a better approach.