spacer LEDactus - Generation 0

The initial programming of LEDactus will focus on creating a simple but attractive display. This will allow us to focus on getting the basic charlieplexing routine working. Afterwards we can begin adding more features.


Code

#define slowdownDelay 400


void display21(unsigned long pattern){
   unsigned short trisB_pat;
   unsigned short portB_pat;
   unsigned short i,j;


   for (i=0; i<3; i++) {
      portB_pat = pattern & 0b1111111;
      pattern >>= 7;
      for (j=0; j<=i; j++){
         portB_pat = ((portB_pat & 0b10000000) > 0) | (portB_pat << 1);
      }

      trisB_pat = ~(portB_pat | (1 << i)); TRISB = 0b11111111;
      PORTB = portB_pat;
      TRISB = trisB_pat;
   }
}

void main(){
   unsigned long  pattern = 0b100100100100100100100;
   unsigned int   i; 
   OSCCON = 0x70; 
   INTCON = 0x00;
   ADCON1 = 0b01111111; 
   
   do {
      for (i = slowdownDelay; i; i--) {
         display21(pattern);
      }

      pattern = ((pattern & 0b100000000000000000000) > 0) | (pattern << 1);
   } while(1);
}
//@DRI
    

Let's dissect the code and see what's happening. We'll start with main() which is the entry point.

  OSCCON = 0x70;
     INTCON = 0x00;
     ADCON1 = 0b01111111; 

These three lines configure the PIC18F1320. OSCCON is a register that controls the PIC's main oscillator. The value 0x70 sets flags that tell the PIC to run at the full 8MHz of the internal clock. INTCON is the interrupt control register. The value 0x00 turns off all interrupts. Finally, ADCON1 is the analog/digital control register. On power up, the PIC18F1320 defaults to having it's analog-to-digital convertors turned on. 0b0111111 turns these pins back to digital I/O.

   do {
      for (i = slowdownDelay; i; i--) {
         display21(pattern);
      }
      //roll the displayed pattern 1 bit to the left
      pattern = ((pattern & 0b100000000000000000000) > 0) | (pattern << 1);
   } while(1);

The rest of the main routine defines an initial pattern, sets up an infinite loop where the pattern is displayed and then rolled to the left. Because the routine runs very quickly, we display each iteration of the pattern 400 times before rolling it left. The line that calculates the next pattern rolls only the lower 21 bits of 'pattern'.

void display21(unsigned long pattern){
   unsigned short trisB_pat;
   unsigned short portB_pat;
   unsigned short i,j;


   for (i=0; i<3; i++) {
      //isolate the lower seven bits
      portB_pat = pattern & 0b1111111;
      pattern >>= 7;
      //roll the PORTB pattern based on the bank select line
      for (j=0; j<=i; j++){
         portB_pat = ((portB_pat & 0b10000000) > 0) | (portB_pat << 1);
      }
      //generate the TRISB pattern using a bitwise NOT
      trisB_pat = ~(portB_pat | (1 << i)); 
      //turn off all LEDs before switching banks
      TRISB = 0b11111111;
      PORTB = portB_pat;
      TRISB = trisB_pat;
   }
}

Finally we arrive at the meat of the routine. display21( ) displays the lower 21 bits of 'pattern' on our charlieplexed display. Let's determine the port settings required to display our starting pattern of 0b100100100100100100100.

As mentioned, the LEDactus display uses three banks of seven LEDs. First we will break the pattern apart into three seven bit segments. Bank one will display the lower seven bits 0100100, bank two will display the middle seven bits 0010010 and bank three will display the upper seven bits 1001001.

To enable a bank we program the bank select I/O line as an output and set its value to low. This means that the corresponding bit in the PORTB register is 0 (0 volts) and in the TRISB register is 0 (output). You can see this in the diagram below where we are setting up the low bits for display.

For LEDs that are on, the corresponding I/O line PORTB bits should be 1 (5 volts) and the TRISB bits should be 0 (output). For LEDs that are off, the value written to PORTB is irrelevant but I chose 0 since this is part of the pattern we're displaying. The TRISB bits should be set to 1 (input).

You'll note that, except for the bank select line, the TRISB pattern is a bitwise NOT of the PORTB pattern and this is how I derive it in the code.

The remaining banks are programmed the same way except that the PORTB and TRISB register patterns are rolled to the left in order to select the appropriate bank select bit.

Charlieplexing and the Register Values

Next Section: LEDactus - PWM Intensity Control