## Relatively simple DFT example for dsPIC

I’ve been trying to create a simple DFT example for the dsPIC. I found it more difficult than I expected to come up with something which is both useful and readable. I’m intentionally not doing either of the following:

1. Using the special DSP hardware features of the dsPIC.
2. Using an FFT algorithm.

Obviously, this could run faster if I did either or both of the above items. However, I’m trying to create an example that’s as understandable as possible, rather than one which is as fast as possible.

This example records a 64-sample frame of data from AN0 (analog input channel 0, pin 2) and then calculates the DFT and power spectrum, up to and including the Nyquist frequency. (Since the input data is real, the frequency spectrum values above Nyquist are redundant.) The program then searches through the power spectrum to identify the frequency bin of maximum power. Bin 0 (DC) is exluded, since the input signal will presumably have a large DC component.

//
// dsPIC DFT Example
// Written by Ted Burke
// Last updated 10-12-2013
//

#include <xc.h>
#include <stdio.h>
#include <libpic30.h>

// Configuration settings
_FOSC(CSW_FSCM_OFF & FRC_PLL16); // Fosc=16x7.5MHz, Fcy=30MHz
_FWDT(WDT_OFF);                  // Watchdog timer off
_FBORPOR(MCLR_DIS);              // Disable reset pin

// Function prototypes

// Window length, N (greater than 4 and a power of 2)
#define N 64

// Twiddle factors (64th roots of unity)
const float W[] = {
1.00000, 0.99518, 0.98079, 0.95694, 0.92388, 0.88192, 0.83147, 0.77301,
0.70711, 0.63439, 0.55557, 0.47139, 0.38268, 0.29028, 0.19509, 0.09801,
-0.00000,-0.09802,-0.19509,-0.29029,-0.38269,-0.47140,-0.55557,-0.63440,
-0.70711,-0.77301,-0.83147,-0.88192,-0.92388,-0.95694,-0.98079,-0.99519,
-1.00000,-0.99518,-0.98078,-0.95694,-0.92388,-0.88192,-0.83146,-0.77300,
-0.70710,-0.63439,-0.55556,-0.47139,-0.38267,-0.29027,-0.19508,-0.09801,
0.00001, 0.09803, 0.19510, 0.29030, 0.38269, 0.47141, 0.55558, 0.63440,
0.70712, 0.77302, 0.83148, 0.88193, 0.92388, 0.95694, 0.98079, 0.99519
};

int main()
{

// Setup UART
U1BRG = 48;            // 38400 baud @ 30 MIPS
U1MODEbits.UARTEN = 1; // Enable UART

// time and frequency domain data arrays
int n, k;                     // time and frequency domain indices
float x[N];                   // discrete-time signal, x
float Xre[N/2+1], Xim[N/2+1]; // DFT of x (real and imaginary parts)
float P[N/2+1];               // power spectrum of x
int a, b;                     // twiddle indices
int to_sin = 3*N/4; // index offset for sin
int k_max;                    // index of max frequency bin
float X_max;                  // power at max frequency bin

while(1)
{
// Record N samples @ 10kHz
for (n=0 ; n<N ; ++n)
{
__delay32(3000); // 100us delay
}

// Calculate DFT and power spectrum up to Nyquist frequency
for (k=0 ; k<=N/2 ; ++k)
{
Xre[k] = 0; Xim[k] = 0;
a = 0; b = to_sin;
for (n=0 ; n<N ; ++n)
{
Xre[k] += x[n] * W[a%N];
Xim[k] -= x[n] * W[b%N];
a += k; b += k;
}
P[k] = Xre[k]*Xre[k] + Xim[k]*Xim[k];
}

// Find and print max amplitude frequency bin
X_max = 0;
for (k=1 ; k<=N/2 ; ++k) if (P[k] > X_max)
{
X_max = P[k];
k_max = k;
}
printf("Max freq bin: %d\n", k_max);
}

return 0;
}

// This function reads a single sample from the specified
// microcontroller is running at 30 MIPS.
// The dsPIC30F4011 has a 10-bit ADC, so the value
// returned is between 0 and 1023 inclusive.
{
ADCHS = channel;          // Select the requested channel
ADCON1bits.SAMP = 1;      // Start sampling
__delay32(30);            // 1us delay @ 30 MIPS
ADCON1bits.SAMP = 0;      // Start Converting
}


I saved the program above to a file called “main.c” and compiled it using Microchip’s XC16 C compiler. This is my build script:

xc16-gcc main.c -mcpu=30F4011 -Wl,--script=p30F4011.gld
if errorlevel 0 xc16-bin2hex a.out


By the way, I generated the values for the global array of twiddle factors in the above example using the following short C program (which runs on the PC rather than the dsPIC):

//
// twiddle.c - Print array of twiddle factors
// Written by Ted Burke
// Last updated 10-12-2013
//
// To compile:
//    gcc twiddle.c -o twiddle.exe
//
// To run:
//    twiddle.exe
//

#include <stdio.h>
#include <math.h>

#define N 64

int main()
{
int n;

for (n=0 ; n<N ; ++n)
{
printf("%8.5lf", cos(n*6.2832/N));
if (n<N-1) printf(",");
if ((n+1)%8==0) printf("\n");
}
return 0;
}


## Simple DFT in C

I’ve been spending some time recently thinking about the Discrete Fourier Transform (DFT), as well as the Fast Fourier Transform (FFT) which provides a computationally efficient means of calculating the DFT of a signal.

The DFT is defined as follows. Given a discrete-time sequence x[n] where n = 0,1,2,….,N-1

$X[k] = \sum\limits_{n=0}^{N-1} x[n] e^{-\frac{jkn2\pi}{N}}$

De Moivre’s Theorem tells us that

$e^{j\theta} = cos(\theta) + jsin(\theta)$

Therefore, we can rewrite the DFT summation as separate expressions for the real and imaginary part of X[k] as follows (assuming x[n] is real).

$X[k] = X_{re}[k] + jX_{im}[k]$

where

$X_{re}[k] = \sum\limits_{n=0}^{N-1} x[n].cos(\frac{jkn2\pi}{N})$

and

$X_{im}[k] = -\sum\limits_{n=0}^{N-1} x[n].sin(\frac{jkn2\pi}{N})$

As part of my thought process, I’ve been sketching out different implementations of the DFT and FFT in C and MATLAB/Octave. This one struck me as particularly nice because it’s a very plain and simple DFT by brute force.

//
// dft.c - Simple brute force DFT
// Written by Ted Burke
// Last updated 7-12-2013
//
// To compile:
//    gcc dft.c -o dft.exe
//
// To run:
//    dft.exe
//

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define N 16
#define PI2 6.2832

int main()
{
// time and frequency domain data arrays
int n, k;             // indices for time and frequency domains
float x[N];           // discrete-time signal, x
float Xre[N], Xim[N]; // DFT of x (real and imaginary parts)
float P[N];           // power spectrum of x

// Generate random discrete-time signal x in range (-1,+1)
srand(time(0));
for (n=0 ; n<N ; ++n) x[n] = ((2.0 * rand()) / RAND_MAX) - 1.0;

// Calculate DFT of x using brute force
for (k=0 ; k<N ; ++k)
{
// Real part of X[k]
Xre[k] = 0;
for (n=0 ; n<N ; ++n) Xre[k] += x[n] * cos(n * k * PI2 / N);

// Imaginary part of X[k]
Xim[k] = 0;
for (n=0 ; n<N ; ++n) Xim[k] -= x[n] * sin(n * k * PI2 / N);

// Power at kth frequency bin
P[k] = Xre[k]*Xre[k] + Xim[k]*Xim[k];
}

// Output results to MATLAB / Octave M-file for plotting
FILE *f = fopen("dftplots.m", "w");
fprintf(f, "n = [0:%d];\n", N-1);
fprintf(f, "x = [ ");
for (n=0 ; n<N ; ++n) fprintf(f, "%f ", x[n]);
fprintf(f, "];\n");
fprintf(f, "Xre = [ ");
for (k=0 ; k<N ; ++k) fprintf(f, "%f ", Xre[k]);
fprintf(f, "];\n");
fprintf(f, "Xim = [ ");
for (k=0 ; k<N ; ++k) fprintf(f, "%f ", Xim[k]);
fprintf(f, "];\n");
fprintf(f, "P = [ ");
for (k=0 ; k<N ; ++k) fprintf(f, "%f ", P[k]);
fprintf(f, "];\n");
fprintf(f, "subplot(3,1,1)\nplot(n,x)\n");
fprintf(f, "xlim([0 %d])\n", N-1);
fprintf(f, "subplot(3,1,2)\nplot(n,Xre,n,Xim)\n");
fprintf(f, "xlim([0 %d])\n", N-1);
fprintf(f, "subplot(3,1,3)\nstem(n,P)\n");
fprintf(f, "xlim([0 %d])\n", N-1);
fclose(f);

// exit normally
return 0;
}


To compile and run the above code, do the following:

When you run the executable file dft.exe, it writes an M-file called dftplots.m which can then be run in MATLAB or Octave to produce plots of the results. This is the M-file produced by dft.exe:

n = [0:15];
x = [ -0.763848 0.445723 0.350261 0.036225 -0.217444 0.121921 -0.604968 0.623280 -0.966613 0.806513 -0.387738 0.412580 0.580554 0.705191 0.440352 -0.077792 ];
Xre = [ 1.504196 0.561935 -1.724586 -1.655775 -1.165293 -0.460708 -2.462571 2.365616 -4.643086 2.365602 -2.462494 -0.460714 -1.165152 -1.655658 -1.724676 0.561582 ];
Xim = [ 0.000000 1.771657 -0.359353 -1.262270 -1.085051 -0.100950 -0.105040 -0.259128 0.000203 0.258721 0.105206 0.100861 1.085066 1.262431 0.359592 -1.771775 ];
P = [ 2.262606 3.454538 3.103332 4.334917 2.535245 0.222443 6.075290 5.663288 21.558247 5.663008 6.074944 0.222430 2.534947 4.334937 3.103816 3.454561 ];
subplot(3,1,1)
plot(n,x)
xlim([0 15])
subplot(3,1,2)
plot(n,Xre,n,Xim)
xlim([0 15])
subplot(3,1,3)
stem(n,P)
xlim([0 15])


I ran dftplots.m in Octave, as shown below:

It produced the following graphs:

The top plot is the original random discrete-time sequence x[n]. The second plot shows the real and imaginary parts of X[k]. Note the symmetry of X[k], which is just as we expect for real x[n]. The final plot is P[k], the power spectrum of x[n]. Basically, each value of P[k] is the square of the magnitude of the corresponding complex value X[k]. Again the symmetry is just as we expect for real x[n].

As a follow up to the above example, I modified the program a little to perform the DFT somewhat more efficiently and to transform a longer (64-sample) frame of data. I also modified the random time domain signal generation process a bit to add a dominant frequency component (at k=5.7, i.e. between bins 5 and 6).

This is the modified C code:

//
// dft2.c - Basic, but slightly more efficient DFT
// Written by Ted Burke
// Last updated 10-12-2013
//
// To compile:
//    gcc dft2.c -o dft2.exe
//
// To run:
//    dft2.exe
//

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

// N is assumed to be greater than 4 and a power of 2
#define N 64
#define PI2 6.2832

// Twiddle factors (64th roots of unity)
const float W[] = {
1.00000, 0.99518, 0.98079, 0.95694, 0.92388, 0.88192, 0.83147, 0.77301,
0.70711, 0.63439, 0.55557, 0.47139, 0.38268, 0.29028, 0.19509, 0.09801,
-0.00000,-0.09802,-0.19509,-0.29029,-0.38269,-0.47140,-0.55557,-0.63440,
-0.70711,-0.77301,-0.83147,-0.88192,-0.92388,-0.95694,-0.98079,-0.99519,
-1.00000,-0.99518,-0.98078,-0.95694,-0.92388,-0.88192,-0.83146,-0.77300,
-0.70710,-0.63439,-0.55556,-0.47139,-0.38267,-0.29027,-0.19508,-0.09801,
0.00001, 0.09803, 0.19510, 0.29030, 0.38269, 0.47141, 0.55558, 0.63440,
0.70712, 0.77302, 0.83148, 0.88193, 0.92388, 0.95694, 0.98079, 0.99519
};

int main()
{
// time and frequency domain data arrays
int n, k;                     // time and frequency domain indices
float x[N];                   // discrete-time signal, x
float Xre[N/2+1], Xim[N/2+1]; // DFT of x (real and imaginary parts)
float P[N/2+1];               // power spectrum of x

// Generate random discrete-time signal x in range (-1,+1)
srand(time(0));
for (n=0 ; n<N ; ++n) x[n] = ((2.0 * rand()) / RAND_MAX) - 1.0 + sin(PI2 * n * 5.7 / N);

// Calculate DFT and power spectrum up to Nyquist frequency
int to_sin = 3*N/4; // index offset for sin
int a, b;
for (k=0 ; k<=N/2 ; ++k)
{
Xre[k] = 0; Xim[k] = 0;
a = 0; b = to_sin;
for (n=0 ; n<N ; ++n)
{
Xre[k] += x[n] * W[a%N];
Xim[k] -= x[n] * W[b%N];
a += k; b += k;
}
P[k] = Xre[k]*Xre[k] + Xim[k]*Xim[k];
}

// Output results to MATLAB / Octave M-file for plotting
FILE *f = fopen("dftplots.m", "w");
fprintf(f, "n = [0:%d];\n", N-1);
fprintf(f, "x = [ ");
for (n=0 ; n<N ; ++n) fprintf(f, "%f ", x[n]);
fprintf(f, "];\n");
fprintf(f, "Xre = [ ");
for (k=0 ; k<=N/2 ; ++k) fprintf(f, "%f ", Xre[k]);
fprintf(f, "];\n");
fprintf(f, "Xim = [ ");
for (k=0 ; k<=N/2 ; ++k) fprintf(f, "%f ", Xim[k]);
fprintf(f, "];\n");
fprintf(f, "P = [ ");
for (k=0 ; k<=N/2 ; ++k) fprintf(f, "%f ", P[k]);
fprintf(f, "];\n");
fprintf(f, "subplot(3,1,1)\nplot(n,x)\n");
fprintf(f, "xlim([0 %d])\n", N-1);
fprintf(f, "subplot(3,1,2)\nplot([0:%d],Xre,[0:%d],Xim)\n", N/2, N/2);
fprintf(f, "xlim([0 %d])\n", N/2);
fprintf(f, "subplot(3,1,3)\nstem([0:%d],P)\n", N/2);
fprintf(f, "xlim([0 %d])\n", N/2);
fclose(f);

// exit normally
return 0;
}


When I ran the resulting M-file in Octave, the following graphs were produced:

The dominant frequency component introduced into the random signal results in the single prominent peak in the above graph.

By the way, I generated the values for the global array of twiddle factors in the above example using the following short C program (which runs on the PC rather than the dsPIC):

//
// twiddle.c - Print array of twiddle factors
// Written by Ted Burke
// Last updated 10-12-2013
//
// To compile:
//    gcc twiddle.c -o twiddle.exe
//
// To run:
//    twiddle.exe
//

#include <stdio.h>
#include <math.h>

#define N 64

int main()
{
int n;

for (n=0 ; n<N ; ++n)
{
printf("%8.5lf", cos(n*6.2832/N));
if (n<N-1) printf(",");
if ((n+1)%8==0) printf("\n");
}
return 0;
}


## Simple ARM example for LPC1114

My colleagues Frank Duignan and Richard Hayes have been experimenting with an ARM Cortex M0 microcontroller in a dual inline package (DIP) which can be plugged straight into a breadboard. The chip they’re using is LPC1114FN28/102, which is made by NXP and is available for less than €2. Frank spent a bit of time working out how to program the chip using a low-cost USB-to-serial converter, so the whole thing provides a micrcontroller programming setup that’s incredibly inexpensive, but quite powerful!

Following Frank’s instructions, I’ve just got my first simple flashing LED example working on the ARM LPC1114. Here’s a couple of snaps of my breadboard circuit:

The following code (filename “main.c”) is really just a stripped back version of a more extensive example Frank sent me, so I must emphasise that all credit here goes to him.

//
// main.c - Blinking LED example for ARM LPC1114
// Written by Ted Burke (based on code by Frank Duignan)
// Last updated 29-11-2013
//

#include "lpc111x.h"

int main()
{
// Turn on clock for GPIO, IOCON and ADC
SYSAHBCLKCTRL |= BIT6 + BIT13 + BIT16;
GPIO0DIR = BIT8; // Make PIO0_8 an output
GPIO0DATA = 0;   // Turn off PIO0 outputs

int n;
while(1)
{
GPIO0DATA = BIT8; // Turn on PIO0_8
n=1000000; while(--n);
GPIO0DATA = 0;    // Turn on PIO0_8
n=1000000; while(--n);
}
}


I’m using the following build script (filename “build.bat”), also courtesy of Frank Duignan. Note the full path included in the linker command on line 3. If your installation directory is different from mine, you will need to modify line 3 to match your own installation folder. Note that the default installation folder may be different on other versions of Windows (note that my installation folder is under “C:\Program Files (x86)\”).

arm-none-eabi-gcc -mcpu=cortex-m0 -mthumb -g -c init.c -o init.o
arm-none-eabi-gcc -mcpu=cortex-m0 -mthumb -g -c main.c -o main.o
arm-none-eabi-ld init.o main.o -L "C:\Program Files (x86)\GNU Tools ARM Embedded\4.7 2013q3\lib\gcc\arm-none-eabi\4.7.4\armv6-m" -lgcc -T linker_script.ld --cref -Map main.map -nostartfiles -o main.elf
objcopy -O ihex main.elf main.hex



I built my program using the gcc compiler from the GNU Tools for ARM Embedded Processors. This was the version I installed (4.7 2013q3):

During the installation, the following dialog box appears. I suggest ticking the check box to “Add path to environment variable”.

I used Flash Magic to transfer the compiled program (main.hex) to the microcontroller:

To compile the example code above, three more files are required: a header file “lpc111x.h”, a linker script “linker_script.ld”, and a C file called “init.c” which contains initialisation code for the ARM. All three files can be downloaded (along with lots of other useful info) from Frank’s web page. Alternatively, a quick way to get all three is to download this zip file (another of Frank’s ARM examples).

So, before building the program, my project folder contained the following five files:

Then I built the program by running the batch file build.bat.

After building, the project folder contained the following files:

Finally, I transferred the compiled program (“main.hex”) to the LPC1114 using FlashMagic.

## Videos from Robotics 3.1

I teach a Robotics module in the final year of our DT009 Electrical Engineering programme here in DIT Kevin St. The philosophy of the module is to give the students enough bits and pieces to get stuck into designing and building some small-scale robotic systems that they dream up themselves. It’s a very enjoyable module to teach (and hopefully to participate in!) because it spans a really useful range of problem solving technology and theory (sensors, actuators, programming, interfacing, kinematics, etc) and it involves many hours of enjoyable robot hacking in the lab.

One of the things I like most about this module is that every year people come up with brilliant ideas of things to build. Everyone on the module (including me) documents their work on WordPress blogs, which means that a lot of the useful stuff learned during the design and implementation process is available immediately for others to learn from. Each year, it takes a little while to build up momentum but after a few weeks, little gems start appearing on the students’ blogs. I’m just beginning to see some interesting stuff appearing on this year’s blogs, so I thought I’d share a few videos that caught my eye over the last few days. All three of these use a \$2 stepper motor driven by a dsPIC30F4011 microcontroller, via a SN754410NE driver chip. These three components are included in the kit I give out at the start of the module.

This video by Simon Kirwan is of his Countdown Clock, complete with the music from the iconic TV show. You can read more about this on Simon’s blog.

This video by Jonathan Baldwin is of a mock-up of a light-tracking solar cell. There’s no actual PV cell (yet?) but it really does track the light. An array of four light-dependent resistors is used to work out the direction of brightest light and then a stepper motor is used to face the dummy solar panel in that direction. You can read more about how it works on Jonathan’s blog.

Finally, this stone cold classic from Jason Franey almost eclipses the original Kylie video. Ok, maybe not quite.

You can read more on Jason’s blog.

Edit: Two more great videos hot off the presses! Both are servo-based systems.

The first is an object tracker by Aron Horan. A SHARP infrared rangefinder is attached to a low-cost servo and it scans back and forth looking for an object. You can read more on Aron’s blog.

The second is a servo pointer controlled by an ultrasonic rangefinder. This one was created by Andrea McConnon – you can read about it in detail on her blog.

## Basic LCD display example for the PIC18F4620

I previously posted an LCD display example program for the dsPIC30F4011 microcontroller. This is the same program adapted for the PIC18F4620 microcontroller. I’ll have to update this later with a circuit diagram, but here’s a quick photo of the program running on a breadboard circuit. Hopefully you can work out which pins are which from the code and/or photo, as well as by referring to the more complete documentation for the dsPIC30F4011 example.

//
// lcd.c - LCD display examplefor dsPIC18F4620
// Written by Ted Burke - Last updated 8-10-2013
//

#include <xc.h>

// Select clock oscillator (default frequency Fosc=1MHz -> Tcy = 4us).
// Disable reset pin, watchdog timer, low voltage programming and
// brown-out reset.
#pragma config OSC=INTIO67,MCLRE=OFF,WDT=OFF,LVP=OFF,BOREN=OFF

// Select which pins the program will use for the LCD screen
// control signals, RS, RW and E.
// NB I had to change these from the names used in my previous
// dsPIC30F4011 example to avoid a clash with the equivalent
// definitions in the XC8 compiler's peripheral library.
#define PIN_RS LATDbits.LATD7
#define PIN_RW LATDbits.LATD6
#define PIN_E LATDbits.LATD5

// Select a pin to use for the flashing LED
#define PIN_LED LATDbits.LATD4

// Function prototypes for transmitting to LCD
void delay_ms(unsigned int n);
void send_nibble(unsigned char nibble);
void send_command_byte(unsigned char byte);
void send_data_byte(unsigned char byte);

int main()
{
TRISD = 0b00000000; // Set RD0-7 as digital outputs

// Let's just write to the LCD and never read!
// We'll wait 2ms after every command since we can't
// check the busy flag.
PIN_RW = 0;
PIN_RS = 0;
PIN_E = 1;

// Initialisation
delay_ms(16); // must be more than 15ms
send_nibble(0b0011);
delay_ms(5); // must be more than 4.1ms
send_nibble(0b0011);
delay_ms(1); // must be more than 100us
send_nibble(0b0011);
delay_ms(5); // must be more than 4.1ms
send_nibble(0b0010); // select 4-bit mode

// Display settings
send_command_byte(0b00101000); // N=0 : 2 lines (half lines!), F=0 : 5x7 font
send_command_byte(0b00001000); // Display: display off, cursor off, blink off
send_command_byte(0b00000001); // Clear display
send_command_byte(0b00000110); // Set entry mode: ID=1, S=0
send_command_byte(0b00001111); // Display: display on, cursor on, blink on

// Define two 8 character strings
const char line1[] = "  Ted's ";
const char line2[] = "PIC18F  ";

// Write the two strings to lines 1 and 2
int n;
send_command_byte(0x02); // Go to start of line 1
for (n=0 ; n<8 ; ++n) send_data_byte(line1[n]);
send_command_byte(0xC0); // Go to start of line 2
for (n=0 ; n<8 ; ++n) send_data_byte(line2[n]);

// Now just blink LED indefinitely
while(1)
{
PIN_LED = 1;
delay_ms(500);
PIN_LED = 0;
delay_ms(500);
}
}

// Delay by specified number of milliseconds
void delay_ms(unsigned int n)
{
// At Fosc=1Mhz, Tcy is 4us. That's the time
// taken to perform one machine code instruction.
// Therefore a delay of 250 x Tcy = 1ms.
while(n--) _delay(250);
}

void send_nibble(unsigned char nibble)
{
// Set RD0-3 without affecting RD4-7
LATD = (LATD & 0xF0) + nibble;
delay_ms(1);
// Note: data is latched on falling edge of pin E
PIN_E = 0;
delay_ms(1);
PIN_E = 1;
delay_ms(2); // Enough time even for slowest command
}

// Send a command byte (i.e. with pin RS low)
void send_command_byte(unsigned char byte)
{
PIN_RS = 0;
send_nibble(byte >> 4);
send_nibble(byte & 0xF);
}

// Send a data byte (i.e. with pin RS high)
void send_data_byte(unsigned char byte)
{
PIN_RS = 1;
send_nibble(byte >> 4);
send_nibble(byte & 0xF);
}


I compiled the program using Microchip’s XC8 compiler. This was the command I used:

xc8 --chip=18F4620 lcd.c


I then used the PICkit 2 application to transfer the compiled hex file onto the microcontroller.

Posted in Uncategorized | 11 Comments

David Dorran, one of my colleagues in the School of Electrical and Electronic Engineering here in the Dublin Institute of Technology, has created a wonderful bootloader for the PIC18F4620 microcontroller:

A bootloader is basically a very small program which resides in the flash memory of a microcontroller alongside the main program which the developer has written to control the chip’s behaviour during normal operation. What normally happens is that the bootloader runs first whenever the chip is powered up. It then does one of two things – either

1. The main program is simply executed as normal, or
2. The bootloader begins downloading a new version of the main program from an external source (probably a PC) and overwriting the previous version in flash memory.

Different bootloaders use different criteria for choosing whether to enter normal operation or update the microcontroller’s software, but it’s usually something relatively simple like the state (high or low) of a specific pin at the moment the chip is powered up (which is exactly what Dave’s bootloader does).

What’s great about bootloaders is that they allow you to update the software on a microcontroller using a very simple and cheap hardware device such as a USB-to-serial converter, rather than using a dedicated programming device (such as the PICkit 2 for the PIC) which may be considerably more expensive.

In our school, we use a lot of PIC microcontrollers and they’re really great to work with. However, one downside of using them in teaching is that the USB programming interface is relatively expensive – around €30 for the PICkit 2. Dave’s bootloader will allow us (and others) to program each chip once with a PICkit 2 to put the bootloader on it, and then give it to a student with a €5 USB-to-serial converter, which they can use to program it from then on.

As well as the actual bootloader (that’s the tiny code that actually runs on the microcontroller each time it is powered up) Dave has provided a very clear circuit diagram and a build script for use with the XC8 compiler when you’re compiling your own code for the PIC.

I think this is really exciting news for teachers or anyone else using PIC microcontrollers on a tight budget – Well done Dave! Here’s that link again: