Follow along with the video below to see how to install our site as a web app on your home screen.
Note: This feature may not be available in some browsers.
Hi,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?
Please disregard my code examples. They are not for 256 steps.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.
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
}
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.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.
#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;
}
;
; 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
Why did you get stucked? 40 µs x 256 is 0.01024 second, which is 97.65625, as you understood.@ 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.
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 ;
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.