if(INTCON.TMR0IF ) // TMR0 Interrupt? 64Mhz/4/8/256 = 7812hz ~ uS
{
//=== 8x8 matrix Bicolor ===== Payload ISR 46% at 16 level PWM ==
LEDRED=1;
if(slice < (PWMdepth-1) ) slice++;
else
{
slice=0;
if(Irow<7) Irow++; else Irow=0; // 8 rows
}
Ival<<=Irow; // translate row in rowvalue
for(Ia=7 ; Ia!=255; Ia--) // Send Green 8 bits to 74HC595's
{
if(slice>=Dgrn[Irow][Ia]) SER88=1; // PIXEL OFF
else SER88=0; // PIXEL ON
CLK88=1; // Send current bit to 74HC595
asm nop
CLK88=0;
}
for(Ia=7 ; Ia!=255; Ia--) // Send Red 8 bits to 74HC595's
{
if(slice>=Dred[Irow][Ia]) SER88=1; // PIXEL OFF
else SER88=0; // PIXEL ON
CLK88=1; // Send current bit to 74HC595
asm nop
CLK88=0;
}
PORTC = ~Ival; // Change to next row (row on ='0')
STC88 = 1; STC88 = 0; // Latch data in registers to output
Ival=0x01; // Initialize for next turn
LEDRED=0;
INTCON.TMR0IF = 0; // clear T0 Int flag
}
Make the periodic update more efficient so you can do more of them for your given framerate.How can i improve my code to get more pwm depth?
if(INTCON.TMR0IF ) // TMR0 Interrupt? 64Mhz/4/8/256 = 7812hz ~ uS
{
//=== 8x8 matrix Bicolor ===== Payload ISR 46% at 16 level PWM ==
LEDRED = 1;
if(++slice == PWMDepth)
{
slice = 0;
Irow = (Irow + 1) & 7; // 8 rows
}
Ival <<= 1;
if(Ival == 0)
Ival = 1; // translate row in rowvalue
for(Ia=7 ; Ia!=255; Ia--) // Send Green 8 bits to 74HC595's
{
if(slice>=Dgrn[Irow][Ia]) SER88=1; // PIXEL OFF
else SER88=0; // PIXEL ON
CLK88=1; // Send current bit to 74HC595
asm nop
CLK88=0;
}
for(Ia=7 ; Ia!=255; Ia--) // Send Red 8 bits to 74HC595's
{
if(slice>=Dred[Irow][Ia]) SER88=1; // PIXEL OFF
else SER88=0; // PIXEL ON
CLK88=1; // Send current bit to 74HC595
asm nop
CLK88=0;
}
PORTC = ~Ival; // Change to next row (row on ='0')
STC88 = 1;
STC88 = 0; // Latch data in registers to output
LEDRED=0;
INTCON.TMR0IF = 0; // clear T0 Int flag
}
;
; K8LH "toggle array" 8-bit 8-chan soft PWM method (pseudo C code)
;
;
; unsigned char Led[] = { 0,14,37,7,50,7,42,0 }; // 0..255 range
;
; unsigned char Interval = 0; // 0..255
; unsigned char Toggle[256]; // 256 element "toggle" array
;
; void isr_hi ()
; { if (PIR1bits.CCP1IF == 1) // if CCP1 "special event trigger"
; { PIR1bits.CCP1IF = 0; // clear CCP1 interrupt flag
; LATB ^= Toggle[Interval]; // update PWM outputs
; Toggle[Interval] = 0; // clear element for next period
;
; if(++Interval == 0) // if end of period, refresh array
; { Toggle[Led[0]] |= 1; // insert b0 output toggle bit
; Toggle[Led[1]] |= 2; // insert b1 output toggle bit
; Toggle[Led[2]] |= 4; // insert b2 output toggle bit
; Toggle[Led[3]] |= 8; // insert b3 output toggle bit
; Toggle[Led[4]] |= 16; // insert b4 output toggle bit
; Toggle[Led[5]] |= 32; // insert b5 output toggle bit
; Toggle[Led[6]] |= 64; // insert b6 output toggle bit
; Toggle[Led[7]] |= 128; // insert b7 output toggle bit
; Toggle[0] ^= ~LATB; // initialize 1st toggle element
; Toggle[255] = 0; // duty cycle 255 = digital on
; }
; }
; }
;
; 78.125 Hz, 256 steps, 50.0 usec interrupts
; 97.656 Hz, 256 steps, 40.0 usec interrupts
; 130.208 Hz, 256 steps, 30.0 usec interrupts
; 195.312 Hz, 256 steps, 20.0 usec interrupts
; 390.625 Hz, 256 steps, 10.0 usec interrupts
; 781.250 Hz, 256 steps, 5.0 usec interrupts
;
; 10 cycles for 255 interrupts, 30 cycles every 256th interrupt
;
org 0x0008
v_inthi
bcf PIR1,TMR2IF ; clear timer 2 interrupt flag |
movf INDF0,W ;
xorwf LATB,F ; LATB ^= Toggle[Interval]
clrf INDF0 ; Toggle[Interval] = 0
incfsz FSR0L,F ; all 256 steps? yes, skip, else
retfie FAST ; exit
; lfsr 0,Toggle ; fsr0 = &Toggle[0]
movf Led+0,W ; insert b0 output toggle bit
bsf PLUSW0,0 ; Toggle[Led[0]] |= 0b00000001
movf Led+1,W ; insert b1 output toggle bit
bsf PLUSW0,1 ; Toggle[Led[1]] |= 0b00000010
movf Led+2,W ; insert b2 output toggle bit
bsf PLUSW0,2 ; Toggle[Led[2]] |= 0b00000100
movf Led+3,W ; insert b3 output toggle bit
bsf PLUSW0,3 ; Toggle[Led[3]] |= 0b00001000
movf Led+4,W ; insert b4 output toggle bit
bsf PLUSW0,4 ; Toggle[Led[4]] |= 0b00010000
movf Led+5,W ; insert b5 output toggle bit
bsf PLUSW0,5 ; Toggle[Led[5]] |= 0b00100000
movf Led+6,W ; insert b6 output toggle bit
bsf PLUSW0,6 ; Toggle[Led[6]] |= 0b01000000
movf Led+7,W ; insert b7 output toggle bit
bsf PLUSW0,7 ; Toggle[Led[7]] |= 0b10000000
comf LATB,W ;
xorwf Toggle,F ; Toggle[0] ^= ~LATB
clrf Toggle+255 ; duty cycle 255 = digital on
retfie FAST ;
... The BAM would be the best (& simplest) bet, just note that the smallest
bit time cannot be smaller than the fastest speed of the ISR. therefore it might be advantageous to precalculate the values so that the ISR is simple.
;bi88pwm.c,162 :: if(INTCON.TMR0IF ) // TMR0 Interrupt? 64Mhz/4/8/256 = 7812hz ~ uS
BTFSS INTCON+0, 2
GOTO L_interrupt36
;bi88pwm.c,165 :: LEDRED=1;
BSF PORTA+0, 2
;bi88pwm.c,166 :: if(slice < (PWMdepth-1) ) slice++;
MOVLW 15
SUBWF _slice+0, 0
BTFSC STATUS+0, 0
GOTO L_interrupt37
INCF _slice+0, 1
GOTO L_interrupt38
L_interrupt37:
;bi88pwm.c,169 :: slice=0;
CLRF _slice+0
;bi88pwm.c,170 :: if(Irow<7) Irow++; else Irow=0; // 8 rows
MOVLW 7
SUBWF _Irow+0, 0
BTFSC STATUS+0, 0
GOTO L_interrupt39
INCF _Irow+0, 1
GOTO L_interrupt40
L_interrupt39:
CLRF _Irow+0
L_interrupt40:
;bi88pwm.c,171 :: }
L_interrupt38:
;bi88pwm.c,172 :: Ival<<=Irow; // translate row in rowvalue
MOVF _Irow+0, 0
MOVWF R0
MOVF R0, 0
L__interrupt60:
BZ L__interrupt61
RLCF _Ival+0, 1
BCF _Ival+0, 0
ADDLW 255
GOTO L__interrupt60
L__interrupt61:
;bi88pwm.c,174 :: for(Ia=7 ; Ia!=255; Ia--) // Send Green 8 bits to 74HC595's
MOVLW 7
MOVWF _Ia+0
L_interrupt41:
MOVF _Ia+0, 0
XORLW 255
BTFSC STATUS+0, 2
GOTO L_interrupt42
;bi88pwm.c,176 :: if(slice>=Dgrn[Irow][Ia]) SER88=1; // PIXEL OFF
MOVLW 3
MOVWF R2
MOVF _Irow+0, 0
MOVWF R0
MOVLW 0
MOVWF R1
MOVF R2, 0
L__interrupt62:
BZ L__interrupt63
RLCF R0, 1
BCF R0, 0
RLCF R1, 1
ADDLW 255
GOTO L__interrupt62
L__interrupt63:
MOVLW _Dgrn+0
ADDWF R0, 1
MOVLW hi_addr(_Dgrn+0)
ADDWFC R1, 1
MOVF _Ia+0, 0
ADDWF R0, 0
MOVWF FSR2L
MOVLW 0
ADDWFC R1, 0
MOVWF FSR2H
MOVF POSTINC2+0, 0
SUBWF _slice+0, 0
BTFSS STATUS+0, 0
GOTO L_interrupt44
BSF PORTB+0, 4
GOTO L_interrupt45
L_interrupt44:
;bi88pwm.c,177 :: else SER88=0; // PIXEL ON
BCF PORTB+0, 4
L_interrupt45:
;bi88pwm.c,178 :: CLK88=1; // Send current bit to 74HC595
BSF PORTB+0, 5
;bi88pwm.c,180 :: CLK88=0;
BCF PORTB+0, 5
;bi88pwm.c,174 :: for(Ia=7 ; Ia!=255; Ia--) // Send Green 8 bits to 74HC595's
DECF _Ia+0, 1
;bi88pwm.c,181 :: }
GOTO L_interrupt41
L_interrupt42:
;bi88pwm.c,182 :: for(Ia=7 ; Ia!=255; Ia--) // Send Red 8 bits to 74HC595's
MOVLW 7
MOVWF _Ia+0
L_interrupt46:
MOVF _Ia+0, 0
XORLW 255
BTFSC STATUS+0, 2
GOTO L_interrupt47
;bi88pwm.c,184 :: if(slice>=Dred[Irow][Ia]) SER88=1; // PIXEL OFF
MOVLW 3
MOVWF R2
MOVF _Irow+0, 0
MOVWF R0
MOVLW 0
MOVWF R1
MOVF R2, 0
L__interrupt64:
BZ L__interrupt65
RLCF R0, 1
BCF R0, 0
RLCF R1, 1
ADDLW 255
GOTO L__interrupt64
L__interrupt65:
MOVLW _Dred+0
ADDWF R0, 1
MOVLW hi_addr(_Dred+0)
ADDWFC R1, 1
MOVF _Ia+0, 0
ADDWF R0, 0
MOVWF FSR2L
MOVLW 0
ADDWFC R1, 0
MOVWF FSR2H
MOVF POSTINC2+0, 0
SUBWF _slice+0, 0
BTFSS STATUS+0, 0
GOTO L_interrupt49
BSF PORTB+0, 4
GOTO L_interrupt50
L_interrupt49:
;bi88pwm.c,185 :: else SER88=0; // PIXEL ON
BCF PORTB+0, 4
L_interrupt50:
;bi88pwm.c,186 :: CLK88=1; // Send current bit to 74HC595
BSF PORTB+0, 5
;bi88pwm.c,188 :: CLK88=0;
BCF PORTB+0, 5
;bi88pwm.c,182 :: for(Ia=7 ; Ia!=255; Ia--) // Send Red 8 bits to 74HC595's
DECF _Ia+0, 1
;bi88pwm.c,189 :: }
GOTO L_interrupt46
L_interrupt47:
;bi88pwm.c,190 :: PORTC = ~Ival; // Change to next row (row on ='0')
COMF _Ival+0, 0
MOVWF PORTC+0
;bi88pwm.c,191 :: STC88 = 1; STC88 = 0; // Latch data in registers to output
BSF PORTB+0, 6
BCF PORTB+0, 6
;bi88pwm.c,192 :: Ival=0x01; // Initialize for next turn
MOVLW 1
MOVWF _Ival+0
;bi88pwm.c,193 :: LEDRED=0;
BCF PORTA+0, 2
;bi88pwm.c,194 :: INTCON.TMR0IF = 0; // clear T0 Int flag
BCF INTCON+0, 2
;bi88pwm.c,195 :: }
L_interrupt36:
;bi88pwm.c,196 :: }
L__interrupt59:
RETFIE 1
; end of _interrupt
I would daisy-chain the 574's which would require 9 pins. You could probably use an 18-pin PIC if you wanted.@mike your suggestion of 74HC754's would mean I need a pic with lots more pins
as the data needs to be sent parallel to the 754's.
Well, it's a binary weighted interval so it's just a matter of shifting the interval value 1 bit to the left each interval. You could probably do it something like this using the CCP module "special event" interrupts;If i understand correctly the BAM method requires only 8 Intcycles per frame per led but happens at different intervals, so it seems hard to code.
void interrupt() //
{ pir1.CCP1IF = 0; // clear "special event" interrupt flag
ccpr1 = interval; // new "compare" interrupt value
if(interval.14) // if last (2^7) interval
interval = 128; // reset to 1T (2^0) interval (128 cycles)
else //
interval <<= 1; //
}
No, you don't use 256 interrupts (yuch!).Or does one still use 256 interrupts and only use 8 of them to update the led data?
That particular visual artifact / anomaly is not an issue on multiplexed arrays because there's plenty of "off" time between each LED refresh interval.I read somewhere that basic BAM has some blinking problems when fading in or out.
I've added the assembler file generated by the C compiler for my interrupt.
Could I optimize this by using inline assembler?
[FONT=monospace]
if(Irow<7) Irow++; else Irow=0; // 8 rows
[/FONT]
Irow++; // bump "Irow"
Irow &= 0b11110111; // 0..7 inclusive (pseudo %7)
That compiler output listing suggests there's a lot of opportunities for improving performance.
Using the hw SPI make the send run in parallel (i.e. in the background) so you can continue to do useful stuff with the CPU.@doug even if i used hw spi i would still need to create the 2 bytes of data to send to the 595's
realtime in my isr by checking/comparing the 2dim arrays with my slice value?
Wont the speed increase be minimal? Or am i overseeing something?
void interrupt isr()
{
// setup the BAM-specific interrupt spacing
interval >>= 1;
if(interval == 0)
{
interval |= 128;
row = (row + 1) & 0xF7; // 0..7
rowMask <<= 1;
if(rowMask == 0)
rowMask++;
}
TMRxINTERVAL = interval; // TMRx has an appropriate prescaler
// send the first 8 bits
ROWSEL_PINS = 0;
CS_PIN = 1; // whatever the latch of the 595 is to shift the data
TXREG = redPrecalc;
char *ptr = &red[row];
if(*ptr++ & interval)
redPrecalc |= 1;
if(*ptr++ & interval)
redPrecalc |= 2;
if(*ptr++ & interval)
redPrecalc |= 4;
if(*ptr++ & interval)
redPrecalc |= 8;
if(*ptr++ & interval)
redPrecalc |= 16;
if(*ptr++ & interval)
redPrecalc |= 32;
if(*ptr++ & interval)
redPrecalc |= 64;
if(*ptr++ & interval)
redPrecalc |= 128;
// possibly wait for tx to finish here if the precalc above took < 32 cycles
TXREG = grnPrecalc;
ptr = &grn[row];
if(*ptr++ & interval)
grnPrecalc |= 1;
if(*ptr++ & interval)
grnPrecalc |= 2;
if(*ptr++ & interval)
grnPrecalc |= 4;
if(*ptr++ & interval)
grnPrecalc |= 8;
if(*ptr++ & interval)
grnPrecalc |= 16;
if(*ptr++ & interval)
grnPrecalc |= 32;
if(*ptr++ & interval)
grnPrecalc |= 64;
if(*ptr++ & interval)
grnPrecalc |= 128;
// possibly wait for tx to finish here if the above precalc took < 32 cycles
CS_PIN = 1; // latch the data or something
ROWSEL_PINS = rowMask; // enable the row
}
I imagine that is due to updating the intensity values part way through a cycle. If you wait until the end of a cycle before updating, I wouldn't think there'd be any flicker.I read somewhere that basic BAM has some blinking problems when fading in or out.
I imagine that is due to updating the intensity values part way through a cycle. If you wait until the end of a cycle before updating, I wouldn't think there'd be any flicker.I read somewhere that basic BAM has some blinking problems when fading in or out.
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?