Hi Darrel,
May I pass along my late congrats' on some brilliant work and share a couple ideas?
Instead of using six instructions (6 cycles) per LED during each update interval you might consider updating one port at a time using an exclusive-or instruction (2 cycles) and data from an eight byte "toggle" array that's refreshed during that long 64T end-of-period interval. This reduces overhead from 288 cycles for 48 LEDs to just 12 cycles during each update interval. Using "toggle" data and an exclusive-or instruction lets us update MIBAM output bits without changing non-MIBAM bits on each port.
Since the three '1T' intervals seem to be a major bottleneck we might consider combining the center |2T|1T|1T|1T|2T| intervals into a single '7T' interval and use small in-line isochronous delays to effect precise timing between updates. This would practically eliminate "overhead" effects on the center three '1T' intervals.
Here's a pseudo C code driver example (sorry, I don't have PBP). I'm sure you realize that the "switch" function should be replaced with assembly language to reduce overhead and eliminate jitter and that the LEDs and "data benders" should probably be setup as macros as you did in your driver.
An assembly language version I wrote supports 48 LEDs at decent refresh rates at almost any clock (4-MHz, 177 Hz, 1T = 22-usecs).
Food for thought. Kind regards, Mike, K8LH
Code:
unsigned char interval = 0; // ISR state machine var'
unsigned char red = 44;
unsigned char grn = 55;
unsigned char blu = 66;
volatile unsigned short ccpr1 @ 0xFBE;
/* *
* original intervals -> |64|32|16|8|4|2|1|1|1|2|4|8|16|32|64| *
* modified intervals -> |64|32|16|8|4|----7----|4|8|16|32|64| *
* */
void interrupt() // CCP1 "special event" interrupts
{ unsigned char Adat[8]; // porta "toggle" array
unsigned char Bdat[8]; // portb "toggle" array
pir1.CCP1IF = 0; // clear CCP1 interrupt flag
switch(interval++) //
{ case 0: // 64T
porta ^= Adat[7]; //
portb ^= Bdat[7]; //
ccpr1 = (64*tStep); // ccpr1 = half 2^7 (64T)
break; //
case 1: // 32T
porta ^= Adat[6]; //
portb ^= Bdat[6]; //
ccpr1 >>= 1; // ccpr1 = half 2^6 (32T)
break; //
case 2: // 16T
porta ^= Adat[5]; //
portb ^= Bdat[5]; //
ccpr1 >>= 1; // ccpr1 = half 2^5 (16T)
break; //
case 3: // 8T
porta ^= Adat[4]; //
portb ^= Bdat[4]; //
ccpr1 >>= 1; // ccpr1 = half 2^4 (8T)
break; //
case 4: // 4T
porta ^= Adat[3]; //
portb ^= Bdat[3]; //
ccpr1 >>= 1; // ccpr1 = half 2^3 (4T)
break; //
case 5: // 7T (2T+1T+1T+1T+2T)
porta ^= Adat[2]; //
portb ^= Bdat[2]; //
ccpr1 = (7*tStep); // ccpr1 = 7*tStep (7T)
delayCy(2*tStep-8); // 2T minus 8 cycles
porta ^= Adat[1]; //
portb ^= Bdat[1]; //
delayCy(1*tStep-4); // 1T minus 4 cycles
porta ^= Adat[0]; //
portb ^= Bdat[0]; //
delayCy(1*tStep-4); // 1T minus 4 cycles
porta ^= Adat[0]; //
portb ^= Bdat[0]; //
delayCy(1*tStep-4); // 1T minus 4 cycles
porta ^= Adat[1]; //
portb ^= Bdat[1]; //
break; //
case 6: // 4T
porta ^= Adat[2]; //
portb ^= Bdat[2]; //
ccpr1 = (4*tStep); // ccpr1 = half 2^3 (4T)
break; //
case 7: // 8T
porta ^= Adat[3]; //
portb ^= Bdat[3]; //
ccpr1 <<= 1; // ccpr1 = half 2^4 (8T)
break; //
case 8: // 16T
porta ^= Adat[4]; //
portb ^= Bdat[4]; //
ccpr1 <<= 1; // ccpr1 = half 2^5 (16T)
break; //
case 9: // 32T
porta ^= Adat[5]; //
portb ^= Bdat[5]; //
ccpr1 <<= 1; // ccpr1 = half 2^6 (32T)
break; //
case 10: // 64T (end-of-period)
porta ^= Adat[6]; //
portb ^= Bdat[6]; //
ccpr1 <<= 1; // ccpr1 = half 2^7 (64T)
interval = 0; // prep for new period
Adat[0] = 0; Adat[1] = 0; // clear Adat[] array
Adat[2] = 0; Adat[3] = 0; //
Adat[4] = 0; Adat[5] = 0; //
Adat[6] = 0; Adat[7] = 0; //
Bdat[0] = 0; Bdat[1] = 0; // clear Bdat[] array
Bdat[2] = 0; Bdat[3] = 0; //
Bdat[4] = 0; Bdat[5] = 0; //
Bdat[6] = 0; Bdat[7] = 0; //
/* *
* use one "data bender" for each LED (16 cycles each) *
* */
if(red.0) Bdat[0] |= 1; // 2^0 data RB0 pin
if(red.1) Bdat[1] |= 1; // 2^1 data RB0 pin
if(red.2) Bdat[2] |= 1; // 2^2 data RB0 pin
if(red.3) Bdat[3] |= 1; // 2^3 data RB0 pin
if(red.4) Bdat[4] |= 1; // 2^4 data RB0 pin
if(red.5) Bdat[5] |= 1; // 2^5 data RB0 pin
if(red.6) Bdat[6] |= 1; // 2^6 data RB0 pin
if(red.7) Bdat[7] |= 1; // 2^7 data RB0 pin
if(grn.0) Bdat[0] |= 2; // 2^0 data RB1 pin
if(grn.1) Bdat[1] |= 2; // 2^1 data RB1 pin
if(grn.2) Bdat[2] |= 2; // 2^2 data RB1 pin
if(grn.3) Bdat[3] |= 2; // 2^3 data RB1 pin
if(grn.4) Bdat[4] |= 2; // 2^4 data RB1 pin
if(grn.5) Bdat[5] |= 2; // 2^5 data RB1 pin
if(grn.6) Bdat[6] |= 2; // 2^6 data RB1 pin
if(grn.7) Bdat[7] |= 2; // 2^7 data RB1 pin
if(blu.0) Bdat[0] |= 4; // 2^0 data RB2 pin
if(blu.1) Bdat[1] |= 4; // 2^1 data RB2 pin
if(blu.2) Bdat[2] |= 4; // 2^2 data RB2 pin
if(blu.3) Bdat[3] |= 4; // 2^3 data RB2 pin
if(blu.4) Bdat[4] |= 4; // 2^4 data RB2 pin
if(blu.5) Bdat[5] |= 4; // 2^5 data RB2 pin
if(blu.6) Bdat[6] |= 4; // 2^6 data RB2 pin
if(blu.7) Bdat[7] |= 4; // 2^7 data RB2 pin
/* *
* convert Bdat[] and Adat[] array "output" data to "toggle" data *
* *
* red 0x2C 00101100 (on RB0) *
* grn 0x37 00110111 (on RB1) *
* blu 0x42 01000010 (on RB2) *
* *
* Bdat 'output data' Bdat 'toggle data' Bdat 'toggle data' *
* [0] 0x02 00000010 [0] 0x04 00000100 [0] 0x04 00000100 *
* [1] 0x06 00000110 [1] 0x05 00000101 [1] 0x05 00000101 *
* [2] 0x03 00000011 [2] 0x02 00000010 [2] 0x02 00000010 *
* [3] 0x01 00000001 [3] 0x03 00000011 [3] 0x03 00000011 *
* [4] 0x02 00000010 [4] 0x01 00000001 [4] 0x01 00000001 *
* [5] 0x03 00000011 [5] 0x07 00000111 [5] 0x07 00000111 *
* [6] 0x04 00000100 [6] 0x04 00000100 [6] 0x04 00000100 *
* [7] 0x00 00000000 [7] 0x00 00000000 [7] 0x01 00000001 *
* portb 0x00 00000000 portb 0x01 00000001 *
* */
asm {
movf _porta,W // get current PORTA bits
andlw 0 // keep only MIBAM output bits
xorwf _Adat+7,F // create 2^7 toggle bits
xorwf _Adat+7,W // W simulates cumulative PORTA
xorwf _Adat+6,F // create 2^6 toggle bits
xorwf _Adat+6,W //
xorwf _Adat+5,F // create 2^5 toggle bits
xorwf _Adat+5,W //
xorwf _Adat+4,F // create 2^4 toggle bits
xorwf _Adat+4,W //
xorwf _Adat+3,F // create 2^3 toggle bits
xorwf _Adat+3,W //
xorwf _Adat+2,F // create 2^2 toggle bits
xorwf _Adat+2,W //
xorwf _Adat+1,F // create 2^1 toggle bits
xorwf _Adat+1,W //
xorwf _Adat+0,F // create 2^0 toggle bits
movf _portb,W // get current PORTB bits
andlw 7 // keep only MIBAM output bits
xorwf _Bdat+7,F // create 2^7 toggle bits
xorwf _Bdat+7,W // W simulates cumulative PORTB
xorwf _Bdat+6,F // create 2^6 toggle bits
xorwf _Bdat+6,W //
xorwf _Bdat+5,F // create 2^5 toggle bits
xorwf _Bdat+5,W //
xorwf _Bdat+4,F // create 2^4 toggle bits
xorwf _Bdat+4,W //
xorwf _Bdat+3,F // create 2^3 toggle bits
xorwf _Bdat+3,W //
xorwf _Bdat+2,F // create 2^2 toggle bits
xorwf _Bdat+2,W //
xorwf _Bdat+1,F // create 2^1 toggle bits
xorwf _Bdat+1,W //
xorwf _Bdat+0,F // create 2^0 toggle bits
}
break; //
}
}
Bookmarks