Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

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 all now I’m ready to dim a single LED via timer interrupt. I’m gathering some basic information. So please be with me to understand this.

I can generate an interrupt on every 40uS.That is 25 KHz rate. Is this called the PWM period? Or is this called the Timer frequency?

Then what is PWM frequency?
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.
 
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?
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:
Sceadwian said:
Create three variables (or use three registers) and set them to the duty cycle you want.

Also create and clear a master PWM variable.
Set the timer up to simply free run and enable the timer overflow interrupt. If your timer is running at clock speed you'll get an interrupt routine every 256 clock cycles on an 8bit timer with no prescaler.
When the ISR triggers compare the master PWM, if it's zero turn on all of your PWM I/O lines and immediatly return from the ISR after increasing the master PWM variable 1. If it's not 0 compare it with each in turn of the LED registers. If it's equal, turn that particular I/O line off and go to the next LED compare line, the last line should increase the master PWM variable..
That's about it. You just have to make sure that worst case scenario your ISR routine executes in less than 256 cycles.
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:
@ 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.
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
    PWM F.PNG
    5 KB · Views: 146
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.

Latest threads

New Articles From Microcontroller Tips

Back
Top