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.

Suraj143

Active Member
Hi I need to light up three LEDs with three brightness levels.

I have written a code with software loops.

Problem is I can see a small flickering in all three LEDs.

Can anybody help me to improve my looping system to make it smoother?

(Note: I’ve used 255 steps.

Ex: If I need to dim half then LED must turn on 128 times & the other 128 time must off the LED)

I need to do this with software loops not with PWM modules.

I use 16F628A with 4Mhz


Code:
Start		clrf		OFFtime		;OFFtime = space time bits
		movlw		.200		;more bright
		movwf		S_CH1
		movwf		CH1
		movlw		.50		;less bright
		movwf		S_CH2
		movwf		CH2
		movlw		.10		;very dim
		movwf		S_CH3
		movwf		CH3

;********************************************************************************
;Do channel one
;********************************************************************************

L4_Do_CH1	btfsc		OFFtime,1	;is it mark or space?
		goto		L4_CH1_OFF	;it is space
L4_CH1_ON	bsf		PORTB,0		;it is mark then turn on RB0
		decfsz		CH1,F
		goto		L4_Do_CH2
		bsf		OFFtime,1
		movf		S_CH1,W		;get the bakup brightness value
		movwf		CH1
			
L4_CH1_OFF	bcf		PORTB,0		;turn off RB0
		incfsz		CH1,F
		goto		L4_Do_CH2
		bcf		OFFtime,1	;turn off off time
		movf		S_CH1,W		;load backup value for next turn
		movwf		CH1

;********************************************************************************
;Do channel two
;********************************************************************************
		
L4_Do_CH2	btfsc		OFFtime,2	;is it mark or space?
		goto		L4_CH2_OFF	;it is space
L4_CH2_ON	bsf		PORTB,1		;it is mark then turn on RB1
		decfsz		CH2,F
		goto		L4_Do_CH4
		bsf		OFFtime,2
		movf		S_CH2,W		;get the bakup brightness value
		movwf		CH2
			
L4_CH2_OFF	bcf		PORTB,1		;turn off RB1
		incfsz		CH2,F
		goto		L4_Do_CH3
		bcf		OFFtime,2	;turn off off time
		movf		S_CH2,W		;load backup value for next turn
		movwf		CH2	
		
;********************************************************************************
;Do channel three
;********************************************************************************
		
L4_Do_CH3	btfsc		OFFtime,3	;is it mark or space?
		goto		L4_CH3_OFF	;it is space
L4_CH3_ON	bsf		PORTB,2		;it is mark then turn on RB2
		decfsz		CH3,F
		goto		L4_Do_CH1
		bsf		OFFtime,3
		movf		S_CH3,W		;get the bakup brightness value
		movwf		CH3
			
L4_CH3_OFF	bcf		PORTB,2		;turn off RB2
		incfsz		CH3,F
		goto		L4_Do_CH1
		bcf		OFFtime,3	;turn off off time
		movf		S_CH3,W		;load backup value for next turn
		movwf		CH3						

		goto		L4_Do_CH1
 
I would rewrite the code to double buffer the output. ie: Set your three bits in GPR memory with BSFs and BCFs and then copy that byte to the port once per main loop. Use one of the timers to ensure a steady PWM rate.
 
Last edited:
Hi futz that is the full code I have.Didn't you see the 3 LEDs lights up with different brightness levels?
 
I would rewrite the code to double buffer the output. ie: Set your three bits in GPR memory with BSFs and BCFs and then copy that byte to the port once per main loop. Use one of the timers to ensure a steady PWM rate.

Hi kchriste thanks for the advice.
You mean the bits in "OFFtime" register?

Can you please show some small example.
 
Hi futz that is the full code I have.Didn't you see the 3 LEDs lights up with different brightness levels?
Definitely not! :D Parts are missing. I have a "goto L4_Do_CH4", and there is no L4_Do_CH4. There's no org and no
Code:
	include "P16F628A.inc"
	__config _INTOSC_OSC_NOCLKOUT & _WDT_OFF & _LVP_OFF & _MCLRE_ON

	cblock	0x20
	OFFtime,S_CH1,CH1,S_CH2,CH2,S_CH3,CH3
	endc
I put all that in and it assembles now, but does nothing.
 
Hi Futz replace "goto L4_Do_CH4" with "goto L4_Do_CH3"

Other parts you have done :)
Code:
include "P16F628A.inc"
	__config _INTOSC_OSC_NOCLKOUT & _WDT_OFF & _LVP_OFF & _MCLRE_OFF

	cblock	0x20
	OFFtime,S_CH1,CH1,S_CH2,CH2,S_CH3,CH3
	endc

But I turned off MCLR
 
Oh crap! All the TRIS setup is missing too. I'll put that in and it might work...

EDIT: Ah! There they are. Bright, not so bright and even less bright. :p
 
Last edited:
Basically, you need to set your PWM frequency higher. I agree, use a timer. I'm not sure you need to double buffer.

Sort your LED on times from shortest to longest - call them A, B and C. Call your PWM period T. Make sure that T >= max (A, B, C). I would set things up so that T << 10 mS (100 Hz pwm frequency). I would simply think in terms of timer ticks.

Turn on all the LEDs, then start the timer with A, when it interrupts turn off the LED associated with A, program timer with B-A. When the timer interrupts, turn off LED B. Then program timer with C-B. When it interrupts turn off LED C and program timer with T-C. When that interrupts, start over again.

You will need to handle the cases where any combo of A, B, C and or T are equal.

If interrupts scare you, simply sit in a loop waiting for the timer to run out.

You should be able to handle T in sub mS range.

By the way, you should start out with just one LED to get the basic idea working. The add in the second. The third should be snap.
 
I don't know futz I just set INTOSC_OSC & run the program.Never heard that 48kHz thing.
Do I need to set any clock settings?
 
I don't know futz I just set INTOSC_OSC & run the program.Never heard that 48kHz thing.
Do I need to set any clock settings?
If you didn't do it on purpose then that's not it. Upload the entire source and I'll try it and see if mine flickers. We may be working on different versions since I typed in parts myself.
 
Last edited:
Basically, you need to set your PWM frequency higher. I agree, use a timer. I'm not sure you need to double buffer.

Sort your LED on times from shortest to longest - call them A, B and C. Call your PWM period T. Make sure that T >= max (A, B, C). I would set things up so that T << 10 mS (100 Hz pwm frequency). I would simply think in terms of timer ticks.

Turn on all the LEDs, then start the timer with A, when it interrupts turn off the LED associated with A, program timer with B-A. When the timer interrupts, turn off LED B. Then program timer with C-B. When it interrupts turn off LED C and program timer with T-C. When that interrupts, start over again.

You will need to handle the cases where any combo of A, B, C and or T are equal.

If interrupts scare you, simply sit in a loop waiting for the timer to run out.

You should be able to handle T in sub mS range.

By the way, you should start out with just one LED to get the basic idea working. The add in the second. The third should be snap.

Hi philba thanks for the explanation.I have never used timers for dimming LEDs yet now I need to learn a bit.I'll go through your settings.

I'm going to use TMR0.
 
Hi philba I really like to do this for a single LED using TMR0 to dim the LED 50% brightness.

The thing is I'm confused with the PWM period for single LED & the values that need to load for the TMR0 & PS.
 
Last edited:
Philba's idea is a good start but you don't need to sort the pwm values and you could probably use a fixed interrupt rate for the four duty cycles.

You need three PWM variables, one for each LED, which will contain values of 0 to 3 which represent duty cycles of 0% (off), 33%, 66%, and 100%.

Now you need a four byte "toggle" array for your ISR, one byte for each interval (0, 33, 66, and 100%). Place a single "toggle" bit for each LED in the array at the correct duty cycle interval by using the LED pwm value (0..3) as the array index.

The ISR will toggle each LED off at the correct interval as it encounters its toggle bit in the array.

Mike

Code:
unsigned char index = 0;        // index, 0..3
unsigned char Led0, Led1, Led2; // duty cycle, 0..3
unsigned char toggle[] = { 0,   // interval 0 (0%)
                           0,   // interval 1 (33%)
                           0,   // interval 2 (66%)
                           0 }; // interval 3 (100%)

void isr_pwm()
{                               // 2 msec interrupts (125 Hz refresh rate)
  pir1.TMR2IF = 0;              // clear timer 2 interrupt flag
  portb ^= toggle[index];       // toggle LED outputs
  toggle[index] = 0;            // clear array for next cycle
  index++;                      // bump index
  if(index = 4)                 // if end of period
  {
    index = 0;                  // reset index, refresh toggle array
    toggle[Led0] |= 1;          // insert Led0 toggle bit (RB0)
    toggle[Led1] |= 2;          // insert Led1 toggle bit (RB1)
    toggle[Led2] |= 4;          // insert Led2 toggle bit (RB2)
    toggle[0] = ~toggle[0];     // prep interval 0
    toggle[0] ^= portb;         //
  }
}
Code:
;
;  save context before
;
        bcf     PIR1,TMR2IF     ; clear timer 2 interrupt flag
        movf    index,W         ; toggle array index, 0..3
        addlw   toggle          ; add to toggle array address
        movwf   FSR             ; FSR = &toggle[0..3]
        movf    INDF,W          ; toggle bits for this interval
        xorwf   PORTB,F         ; toggle LED outputs
        clrf    INDF            ; clear element for next cycle
        incf    index,F         ; bump index
        btfss   index,2         ; end of period (index == 4)?
        goto    isr.exit        ; no, branch, else
        clrf    index           ; reset index, refresh toggle array
        movf    Led0,W          ; duty cycle, 0..3
        addlw   toggle          ; add array address
        movwf   FSR             ; 
        bsf     INDF,0          ; insert Led0/RB0 toggle bit
        movf    Led1,W          ; duty cycle, 0..3
        addlw   toggle          ; add array address
        movwf   FSR             ; 
        bsf     INDF,1          ; insert Led1/RB1 toggle bit
        movf    Led2,W          ; duty cycle, 0..3
        addlw   toggle          ; add array address
        movwf   FSR             ; 
        bsf     INDF,2          ; insert Led2/RB2 toggle bit
        comf    toggle,W        ; prep interval 0 (0%)
        xorwf   portb,W         ;
        movwf   toggle          ;
isr.exit                        ; restore context
 
Actually, that method in the previous post is much more complex than what you need. Just rename the 'index' variable to 'duty' and compare that to each Ledx variable in the interrupt routine. Use duty cycle values of 0..3 for your three Ledx variables.

Mike

Code:
;
        movf    duty,W          ; 0..3
        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..3
        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..3
        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           ;
        incf    duty,F          ; bump duty cycle counter
        btfss   duty,2          ; duty == 4?  yes, skip, else
        goto    isr.exit        ; exit
        clrf    duty            ; reset duty cycle counter
        movlw   b'00000111'     ;
        movwf   shadow          ; reset shadow
isr.exit
 
I think he wanted 256 levels.

Yes, he doesn't need to sort the led times - I gave that as a way to make it easier to understand. The simplest way to do this is have 4 counts one for each LED count plus the PWM period count. At each timer tick decrement each count. When a count gets to 0, turn off the LED. When the PWM period count gets to zero, reset the counts, turn on the LEDs and restart the process. That way he can generalize it to any PWM period - i.e. to an arbitrary number of intensity levels.

The technique I gave is a standard multi event delay line that is used in operating systems. In the generalized case, each timer event is a node in a linked list. It's easy to insert a new timer event and allows effectively unlimited events. Definitely overkill but the technique is a good one to know.
 
Here's the OP's first sentence;
Hi I need to light up three LEDs with three brightness levels.
Later he says he's using 256 steps as an example... I thought he meant just three distinct brightness levels per LED... Oops!!!
 
Last edited:
Hi Mike, K8LH thanks for your excellent codings.The first code you post was really tough to understand.So I gave that.After the next code you post it looks good & I'll try to do that.

Thanks for your support.

@ philba

Thanks for your explanation that I can understand so i'm going to write my own code using that method.
 
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?
 
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top