Programming a Knight Rider LED light scanner

In this tutorial we extend the blinking LEDs to create a Knight Rider light scanner. Several concepts of the programming language C are applied in this tutorial. The project also provides the opportunity to review the concepts of the previous tutorial.



Requirements

  1. One Dwengo board
  2. One Dwengo programmer
  3. Enclosed USB cable

A Knight Rider

We start the program by adding the required C18 libraries:

  1. #include <p18f4550.h>
  2. #include <delays.h>

In the second step we configure the fuses correctly by using the following pragmas:

  1. // Fuses configuration
  2. #pragma config PLLDIV = 5 // Divide by 5 (20 MHz oscillator input)
  3. #pragma config FOSC = HSPLL_HS // HS oscillator, PLL enabled, HS used by USB
  4. #pragma config IESO = OFF // Oscillator Switchover mode disabled
  5. #pragma config PWRT = OFF // PWRT disabled
  6. #pragma config BOR = OFF // Brown-out Reset enabled in hardware only (SBOREN is disabled)
  7. #pragma config WDT = OFF // HW Disabled - SW Controlled
  8. #pragma config WDTPS = 32768 // 1:32768
  9. #pragma config MCLRE = ON // MCLR pin enabled; RE3 input pin disabled
  10. #pragma config LVP = OFF // Disable low-voltage programming
  11. #pragma config CCP2MX = ON // Config3h
  12. #pragma config PBADEN = OFF // PORB digital IO on powerup

In the text about commonly used configuration bits for the Dwengo board many of the underlying meanings of these fuses are explained.

In the third step we introduce several macros the make the code more readable. It is recommended to pick up this technique to keep your code orderly, especially when working with larger projects.

  1. #define TRUE 1
  2. #define LEDS PORTD

In the schematics of the Dwengo board you can find
that the LEDs of the Dwengo board are connected to the RD pins. These pins are controlled by the PORTD register. For this reason we introduced the LEDS macro. The compiler will replace all occurrences of LEDS in the code with PORTD. The same happens for the macro TRUE.

Now we can write the main program:

  1. void main(void) {
  2. unsigned char i;
  3. TRISD = 0x00; // Configure PORTD-pins as outputs
  4. LEDS = 0b00000001;
  5. while (TRUE) {
  6. for (i=0; i<7; i++) {
  7. LEDS <<= 1; // Rotate to the right
  8. Delay10KTCYx(48);
  9. }
  10. for (i=0; i<7; i++) {
  11. LEDS >>= 1; // Rotate to the left
  12. Delay10KTCYx(48);
  13. }
  14. }
  15. }

The main loop of the program is immediately recognisable. In this loop we first declare the variable i which is used as a loop counter. All data types are defined as unsigned char. This is the smallest variable type in our processor. It can store all numbers between 0 and 255. In this example i only counts from 0 up to 6, so this is more than enough. For more information we refer to our general description of data types and numbers in C.

Next we configure all RD pins as digital outputs by setting TRISD to 0 (this is equal to 0x00 in hexadecimal notation or 0b00000000 in binary notation). The binary notation is convenient when you want to configure one pin as a digital input. For example, in order to configure pin RD2 as an input, the TRISD register should be initialized to 0b00000100. Note that the pin RD0 corresponds to the rightmost bit (least significant) and that RD7 corresponds with the leftmost bit (most significant) in binary notation. 0b00000100 corresponds to the decimal value 4 or 0x04 hexadecimal, but 0b00000100 reads more easily when the value of each bit is to be interpreted independently.

Next we initialise the LEDS variable (remember that this is defined as PORTD, to which the LEDs are connected) to 0b00000001. This will set pin RD0 to high. Now pin RD0 is high, the LED0 on the Dwengo board will light up.

Afterwards the while(TRUE) construct creates an infinite loop. The code in this loop will be repeated over and over again until the power of the board is turned of.

The infinite loop contains two for loops. The code in each of these loops is executed 7 times — from i=0 up to i=6 — before we jump to the next loop. In the first loop the contents of LEDS is shifted to the left by one bit by using the binary shift operator: <<. In the free spot that was created on the right of the bit array, a 0 is inserted. In mathematics this operation corresponds to multiplying by 2. Next the Delay10KTCYx(48) function call causes the program to wait for 480,000 instruction cycles (TCYx). From the blinking LEDs tutorial we already know that one TCYx corresponds with 83.333 ns. So 480,000 TCYx corresponds with 40 ms. This means that every 40 ms another LED will light up.

In the second loop we do the opposite from the first loop: every iteration we shift the contents of LEDS by 1 bit to the right. This corresponds with a division by 2. On the board this translates to the LEDs lighting up from right to left (from LED7 to LED0).

Because the two for loops are repeated one after another, it seems that the LEDs are moving from one way to the other: just like a real Knight Rider as shown in the movie.

Source codeSource code files are only available to Dwengo customers. If you have bought with us before, please log in to download source files.