3 LED PWM Outputs

Status
Not open for further replies.
Hi Mike can you tell me in the beginning what values to move to Led0,Led1 & Led2?
Can I use any values ex:50,128,200 etc...

Every time you checking the bit 2 of "duty" register I don't know why.

What Timer frequency do I need to use? Can I use 40uS interrupt?
 
Hi,
If the interrupt rate is 25 kHz with 256 steps, the PWM frequency is 97.65625 Hz, while the timer interrupt frequency is still 25 kHz.
I think this frequency is suitable as human won't notice the flicker at 50 Hz and above.
 
Please disregard my code examples. They are not for 256 steps.

I think Philba should provide an example. His description sounds like one of Gramo's examples where he decrements a master duty cycle counter and all three LED duty cycle variables.
 
Please disregard my code examples. They are not for 256 steps.

Thanks Mike.

@ Bananasiong

The interrupt rate is 25 kHz with 256 steps, the PWM frequency is 97.65625 Hz

That I understood.

The problem is the code goes to the ISR routine on every 40uS (25 KHz).

So How can I generate 256 steps.Then it must go 256 times to the ISR.

That is 40uS X 256

This is the place I got stucked.
 
Ok, I'll do a pseudoC example

Code:
uchar r,g,b,T;
uchar rVal, gVal, bVal;
init() {
    r = rVal;
    g = gVal;
    b = bVal;
    set up timer so PWMTICK gives you the right interval
    timer = PWMTICK;
    T = MAXPWMCOUNT;
    LED_R = LED_B = LED_G = 1;
    turn on interrupts (timer and general)
}

InterruptServiceRoutine(){
    timer = PWMTICK; 
    if(--r == 0) LED_R = 0;     
    if(--g == 0) LED_G = 0;
    if(--b == 0) LED_B = 0;
    if(--T == 0) {
        // reset PWM period and rgb counts
        T = MAXPWMCOUNT;
        r = rVal;
        g = gVal;
        b = bVal;
        LED_R = LED_G = LED_B = 1
    }
    clear timer int flag
}

main() {

    init PIC HW
    set rVal, gVal, bVal

    init()

    while(1);  // or what ever you want here like change rVal, bVal, gVal
}
For 255 levels, I'd set MAXPWMCOUNT to 255. Also be careful that your ISR takes less time than the timer interrupt period. Definition of the I/O pins for the LEDs, PWMTICK, timer initialization left as an exercise for the reader. Generalizing to greater than 8 bit values is up to you.

I probably missed something but that's the basic idea. edit: bug when led value is 0.
 
Last edited:
Hey Suraj. Just for fun I was tinkering with this thing and came up with this in C. It's very simple. Uses this algorithm:
Since 4MHz is too slow to do that without flickering, I preload TMR0 to 0xd0 every time it interrupts, to make it interrupt often enough to prevent flicker.

The ISR (void interrupt(void) ) does all the work. The FOR loops inside the WHILE loop just change the PWM duty cycles so it does something vaguely interesting instead of just sitting there. Here's the source and hex in a zip file: View attachment suraj_pwm.zip

Here's the source:
Code:
#include <system.h>
#pragma CLOCK_FREQ 4000000
#pragma DATA _CONFIG, _INTOSC_OSC_NOCLKOUT & _WDT_OFF & _LVP_OFF & _MCLRE_ON

unsigned char led1,led2,led3,pwm,out;

void main(void)
{
	unsigned char x,y,z;
	trisb=0;
	portb=0;
	led1=led2=led3=pwm=0;
	option_reg=0b10001000;
	intcon=0b10100000;
	while(1){
		y=255;
		for(x=0;x<255;x++){
			led1=x;
			led2=y--;
			led3=x;
			delay_ms(3);
		}
		y=0;
		for(x=255;x>0;x--){
			led1=x;
			led2=y++;
			led3=x;
			delay_ms(3);
		}
	}
}	

void interrupt(void)
{
	tmr0=0xd0;
	intcon.T0IF=0;
	pwm=pwm+1;
	if(pwm==0)
		out=0b00000111;
	if(pwm==led1)
		out.0=0;
	if(pwm==led2)
		out.1=0;
	if(pwm==led3)
		out.2=0;
	portb=out;
}
 
Last edited:
Philba,

That code is very much like Gramo's old BASIC RGB example. Why decrement four counters and waste precious cycles resetting r, g, and b at the end-of-period? You're also not properley handling duty cycle values of 0.

Here's my revised example for 256 steps (40 usec interrupts) with just a single operation at end-of-period.

Mike

Code:
;
;  uchar duty = 0;              // duty cycle counter, 0..255
;  uchar Led0,Led1,Led2 = 0;    // R/G/B Led duty cycle, 0..255
;  uchar shadow = 0;            // gpio shadow register
;
;  void interrupt()             // 40 usec timer 2 interrupts
;  { pir1.TMR2IF = 0;           // clear timer 2 interrupt flag
;    if(Led0 = duty)            //
;      shadow.0 = 0;            // turn off Led0 bit in shadow
;    if(Led1 = duty)            //
;      shadow.1 = 0;            // turn off Led1 bit in shadow
;    if(Led2 = duty)            //
;      shadow.2 = 0;            // turn off Led2 bit in shadow
;    gpio = shadow;             // update gpio from shadow
;    duty++;                    // bump duty cycle counter
;    if(duty == 0)              // if end of period
;      shadow = 0b00000111;     // reset shadow
;  }
;
        movf    duty,W          ; 0..255
        xorwf   Led0,W          ; same as Led0 duty cycle?
        skpnz                   ; no, skip, else
        bcf     shadow,0        ; turn off Led0 bit in shadow
        movf    duty,W          ; 0..255
        xorwf   Led1,W          ; same as Led1 duty cycle?
        skpnz                   ; no, skip, else
        bcf     shadow,1        ; turn off Led1 bit in shadow
        movf    duty,W          ; 0..255
        xorwf   Led2,W          ; same as Led2 duty cycle?
        skpnz                   ; no, skip, else
        bcf     shadow,2        ; turn off Led2 bit in shadow
        movf    shadow,W        ;
        movwf   PORTB           ; update port from shadow
        movlw   b'00000111'     ; 
        incf    duty,F          ; end of period?
        skpnz                   ; no, branch, else
        movwf   shadow          ; reset shadow
isr.exit
 
Last edited:
Why did you get stucked? 40 µs x 256 is 0.01024 second, which is 97.65625, as you understood.
 
Excellent thanks for the new codes.Now I got an idea & going to write them with ASM never used C before.
 
Hi Mike your new ASM code is very familiar to my knowledge.Thanks for that.

I just calculate some technical informations from your code.

Can you tell me whether they are correct?

Timer frequency = 25Khz = 40uS
PWM period = 0.01024 S = 97.65625 Hz
Duty cycle range for LED variables = 0-255
 
Yes, that's correct. Code is isochronous (takes exactly the same amount of time to execute each interrupt cycle) and uses 18 cycles. You'll also need to add cycles for ISR "context save" and "context restore".

If you're using a 4 MHz clock (Tcy = 1 usec) you may end up using about 75% of your total processing time in the ISR.

Mike

BTW, same code on 18F' is incredibly simple (19 cycles for the whole ISR);

Code:
ISR_Hi
        bcf     PIR1,TMR2IF     ; clear timer 2 interrupt flag
        movf    duty,W          ; duty cycle counter, 0..255
        cpfsgt  Led0            ; 
        bcf     Shadow,0        ; 
        cpfsgt  Led1            ; 
        bcf     Shadow,1        ; 
        cpfsgt  Led2            ;
        bcf     Shadow,2        ; 
        movff   Shadow,LATB     ;
        movlw   b'00000111'     ;
        infsnz  duty,F          ;
        movwf   Shadow          ;
        retfie  FAST            ;
 
Last edited:
Philba,

That code is very much like Gramo's old BASIC RGB example. Why decrement four counters and waste precious cycles resetting r, g, and b at the end-of-period? You're also not properley handling duty cycle values of 0.

As I noted in my edit, it didn't handle 0. 4 extra decrements out of 1024 is hardly wasteful. I'm surprised you didn't complain about the continued decrements of the counters for the LEDs that were turned off. Like I said earlier, a better approach is to run the event chain that i described. That's more complex and probably not a good idea for neophyte programmers. Then there is 1 decrement per timer tick.
 
Hi I applied Mike’s code & it really worked well. So I’m really happy.

I research on TIMER frequency Vs PWM frequency & got best three values.

Here are they.

So always I must write the ISR without overlapping this timer frequency.
 

Attachments

  • PWM F.PNG
    5 KB · Views: 136
I like to dim 6 LEDs inside ISR.If there is no space then I have to alter the timer frequency according the graph.

Because i need to dim 6 LEDs inside ISR & need to do some other work in the main routine.

I think I can do that.

If I load d'128' for LED variables then it will dim halfly.

That means (128/256) X 100 = 50%

So this is how you tell duty cycle.Tell me am I right?
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…