Can I control the brightness of 2 or more LED's with PWM?

#1
I'm not an electronics person but have some programming knowledge, very new to picaxe. I'm trying to control as many LEDs as I can on a 14M2 to make the LEDs glow brighter and dimmer independent. Currently I have a program which is an endless loop which uses several variables monitor the brightness and also variables to indicate if the LED is getting brighter or dimmer. IF statements check the limits and change brightness/dimness direction. Then a series of PWM commands to control the LEDs level of brightness. The problem I have is that on each pass of the PWM commands, the LEDs flash which prevents the smooth brightness change that I'm looking. Is there a way to prevent this flashing, perhaps with some kind of concurrent subroutines? I'm not sure if I should be using PWMDUTY, PWMOUT or whatever. Also, I've played unsuccessful with the simulator to display the values of the variables as the program executes.

One of the versions of the code I've been tinkering with is listed below, thanks in advance.

Code:
w1=50
w2=100
w4=150
w6=200

w7=1
w8=1
w10=1
w12=1

PWMOUT B.2,w1,1
PWMOUT B.4,w2,1
PWMOUT C.0,w4,1
PWMOUT C.2,w6,1

main:

IF w7=1 THEN
    INC w1
ELSE
    DEC w1
ENDIF

IF w8=1 THEN
    INC w2
ELSE
    DEC w2
ENDIF

IF w10=1 THEN
    INC w4
ELSE
    DEC w4
ENDIF

IF w12=1 THEN
    INC w6
ELSE
    DEC w6
ENDIF

IF w1 >= 255 THEN
    w7 = 0
ELSEIF w1 <= 15 THEN
    w7 = 1
ENDIF

IF w2 >= 255 THEN
    w8 = 0
ELSEIF w2 <= 15 THEN
    w8 = 1
ENDIF

IF w4 >= 255 THEN
    w10 = 0
ELSEIF w4 <= 15 THEN
    w10 = 1
ENDIF


IF w6 >= 255 THEN
    w12 = 0
ELSEIF w6 <= 15 THEN
    w12 = 1
ENDIF

PWMDUTY B.2,w1
PWMDUTY B.4,w2
PWMDUTY C.0,w4
PWMDUTY C.2,w6

GOTO MAIN
 

hippy

Technical Support
Staff member
#2
The main problem I would guess is there is no PAUSE command before your GOTO MAIN at the end. This means the program runs incredibly fast, possibly too fast for the PWMOUT hardware to keep up with, so you are seeing odd LCD flickering.

You are also setting your PWMOUT frequencies differently with your w1, w2, w4 and w6 variables. It would normally be better to use a single frequency for all PWMOUT, and a value of 63 is optimal for using a duty with a maximum of 255.
 
#4
Yes, Hippy, that has fixed my program, well as far as the program is now doing what it is programmed to do! My understanding how PWM works with LED's is still preventing me getting the results that I'm really looking for. I'm enjoying tinkering but what I'm trying to get is the LED's to go from full brightness to almost off and returning to full brightness again over say a 10-15 second period, all out of phase with each other. Any further suggestions would be greatly appreciated.
 

hippy

Technical Support
Staff member
#5
The simplest solution would be to start with one LED, ramp that up and down, then modify that to handle more than one LED.

For example, choose a LED level which can range from 0 to 1023, set your desired MIN_LEVEL and MAX_LEVEL, and the time you want to fade over, 15 seconds = 15000 milliseconds -
Code:
#Picaxe 14M2

Symbol MIN_LEVEL   = 15
Symbol MAX_LEVEL   = 1023
Symbol TIME_MS     = 15000

Symbol MIN_MAX_GAP = MAX_LEVEL - MIN_LEVEL
Symbol STEP_PERIOD = TIME_MS / MIN_MAX_GAP

PwmOut C.0, 255, 0
Do
  For w0 = MIN_LEVEL To MAX_LEVEL Step 1
    PwmDuty C.0, w0
    Pause STEP_PERIOD
  Next
  For w0 = MAX_LEVEL To MIN_LEVEL Step -1
    PwmDuty C.0, w0
    Pause STEP_PERIOD
  Next
Loop
That will prove you can get what you want to achieve though it might not be appropriate for multiple LED's out of phase, or fading at different rates.

If you have an idea of how they will be out of phase, locked out of phase by a fixed amount, somewhat random or otherwise, that will help in suggesting how best to approach that.

Corrected: PwmDuty C.0, w1 to PwmDuty C.0, w0. See Posts #7 and #8.
 
Last edited:
#6
Thanks again for this excellent assistance again, most appreciated. Let me explain the situation I'm trying to resolve and I hope you find this of some interest. In Hastings. New Zealand I have a friend who works for the council and he organises a Chinese lantern festival each year; usually in April lasting for 3 or 4 nights to coincide with similar events in China. A couple of years ago the city Hastings is twined with in China gifted the city a set of 8 large lotus lilies. These are about 5ft diameter with 12 petals each, which sit on pedestals to make them appear to float on the water of the shallow pond where they are situated. The lilies were also supplied with a series of eco bulbs running of a 240v mains supply; which when the health and safety inspector saw this with their proximity to the water immediately condemned them. Hence our need to look for an alternative solution. This image shows the lilies with the 240v supply before the inspector arrived! https://www.hastingsdc.govt.nz/assets/Image-Gallery/Lighting-of-Osmanthus-Large.jpg (sorry, couldn't get this to link).

I'm now using a number of white 10mm LED's to illuminate these and they work remarkably well. Although in reality I only need to light up the last third of the petal tip and this is the part mostly seen by people viewing them. The petals are made from some type of coloured material stretched on a wire frame. If just using a static light source then the lilies look quite boring, especially with the dimmer light source provided by the LEDs. The idea I had to dim the LEDs automatically has some appeal and we certainly don't want to flash them as the idea is to create a tranquil feel for the place. The dimming effect to give some kind of animation I guess.

So, using the four outputs on the 14M2 for PWM, I hope to illuminate 3 petals from each output (remember 12 petals in total) by having the LEDs in parallel. I'm powering the 14M2 with 3 AA batteries which can easily be changed each evening and they last long enough for what we need as I've a few 330 ohm resistors to help too. Ideally, it would be very nice to have all the 4 sets of LEDs to operate independently of each other. However, if it is more practical to have a few subroutines to just animate one set of petals at a time then so be it. These could work at random or in sequence.

Thanks very much again for your assistance. One thing I realise is that I could also upgrade the program each year as my understanding of picaxe improves and certainly each lily could run a slightly different program to gave more randomness to the effect.
 

Bill.b

Senior Member
#7
Hippy

For w0 = MIN_LEVEL To MAX_LEVEL Step 1
PwmDuty C.0, w1
Pause STEP_PERIOD
Next


should this be
For w0 = MIN_LEVEL To MAX_LEVEL Step 1
PwmDuty C.0, w0
Pause STEP_PERIOD
Next

Bill
 

hippy

Technical Support
Staff member
#9
The dimming effect to give some kind of animation I guess.
Sounds like a great idea and I guess a gentle fading, either out of phase or more random with each other, should give a nice, subtle, changing effect.

It is possible to fade four LED's up and down with independent timing, and code which does that can be quite easily locked in phase simply be setting a different starting value but having the same timing. So having independent timing is probably the best place to start as it allows both.

The code below should do that though untested. It looks a little complex at first glance but go straight to the end of the program and it's quite simple. It really just increments the duty level each time round the loop.

But it does use some trickery in that to allow slow fades which means the calculation of the increment isn't as straightforward as '1 means 1'. In fact an increment value of 1 is actually an increment of the level by 1/32.

Duty is normally a 0-1023 value which is 10-bit. By multiplying duty by $20 (32) we can make it a fixed point decimal value, 0.n to 1023.n -
Code:
.---------------------------------.
| - - - - - - 9 8 7 6 5 4 3 2 1 0 |
`---------------------------------'
.---------------------------------.
| - 9 8 7 6 5 4 3 2 1 0 n n n n n |
`---------------------------------'
We can turn that into an actual 0-1023 duty to output simply by dividing by $20.

To increment the duty output by 1, we actually increment the level value by $20. If we incremented by $10 that would be an effective 0.5 increment, $08 is 0.25 etc.

This way we can increment a level by a fractional amount every loop.

And a note for those wondering why we multiplied by $20 when we could have multiplied by $40, moved the msb of the duty into the msb of the word; that makes it more difficult to handle increment overflows as 1023.0+1.0, or 1023.5+0.5, will cause an overflow wraparound with PICAXE maths. Keeping the msb clear allows overflows to be more easily handled, allows us to check if a value is greater or equal to 1024.0

Code:
#Picaxe 14M2
#No_Data

; .--------------------------------------------.
; | Adjust below to set fading characteristics |
; `--------------------------------------------'

Symbol LED1_MIN  = 23
Symbol LED2_MIN  = 23
Symbol LED3_MIN  = 23
Symbol LED4_MIN  = 23

Symbol LED1_MAX  = 1023
Symbol LED2_MAX  = 1023
Symbol LED3_MAX  = 1023
Symbol LED4_MAX  = 1023

Symbol LED1_MS   = 10000
Symbol LED2_MS   = 20000 
Symbol LED3_MS   = 30000
Symbol LED4_MS   = 40000

; .--------------------------------------------.
; | Adjust below to match the hardware used    |
; `--------------------------------------------'

Symbol LED1      = B.2  ; LED1 output pin
Symbol LED2      = B.4  ; LED2 output pin
Symbol LED3      = C.0  ; LED3 output pin
Symbol LED4      = C.2  ; LED4 output pin

; .--------------------------------------------.
; | No need to alter anything below this point |
; `--------------------------------------------'

; Set the basic loop timing (ms)

Symbol LOOP_MS   = 100

; Calculate the increments for each loop

Symbol LED1_GAP  = LED1_MAX - LED1_MIN
Symbol LED2_GAP  = LED2_MAX - LED2_MIN
Symbol LED3_GAP  = LED3_MAX - LED3_MIN
Symbol LED4_GAP  = LED4_MAX - LED4_MIN

Symbol LED1_LOOP = LED1_MS / LOOP_MS
Symbol LED2_LOOP = LED2_MS / LOOP_MS
Symbol LED3_LOOP = LED3_MS / LOOP_MS
Symbol LED4_LOOP = LED4_MS / LOOP_MS

Symbol LED1_GAPX = LED1_GAP * $20
Symbol LED2_GAPX = LED2_GAP * $20
Symbol LED3_GAPX = LED3_GAP * $20
Symbol LED4_GAPX = LED4_GAP * $20

Symbol LED1_INC  = LED1_GAPX / LED1_LOOP
Symbol LED2_INC  = LED2_GAPX / LED2_LOOP
Symbol LED3_INC  = LED3_GAPX / LED3_LOOP
Symbol LED4_INC  = LED4_GAPX / LED4_LOOP

; Show LED details for integrity checking

#Macro ShowLed( ledX, X, ledX_min, ledX_max, ledX_inc, ledX_ms, ledX_loop )
  SerTxd( "LED", #X, " on pin " )
  w5 = ledX / 8
  w6 = ledX & 7
  LookUp w5, ("BCDA"),w5
  SerTxd( w5, ".", #w6, CR, LF )
  SerTxd( "     from ", #ledX_min, " to ",   #ledX_max, CR, LF )
  SerTxd( "     over ", #ledX_ms, " ms, ", #ledX_loop, " loops at ", #LOOP_MS, " ms per loop", CR, LF )
  w5 = ledX_inc / $20
  SerTxd( "     inc  ", #w5, "." )
  w5 = 0
  b1 = ledX_inc & $1F
  If bit8  = 1 Then : w5 = w5 +  312 : End If
  If bit9  = 1 Then : w5 = w5 +  625 : End If
  If bit10 = 1 Then : w5 = w5 + 1250 : End If
  If bit11 = 1 Then : w5 = w5 + 2500 : End If
  If bit12 = 1 Then : w5 = w5 + 5000 : End If
  w6 = bit8 * 5
  Select Case w5
    Case <   10 : SerTxd( "000" )
    Case <  100 : SerTxd( "00"  )
    Case < 1000 : SerTxd( "0"   )
  End Select
  SerTxd( #w5, #w6, " per loop (", #ledX_inc, ")", CR, LF )
#EndMacro

; Initialise a LED

#Macro InitLed( ledX, wX, bitX, ledX_min )
  wX   = ledX_min * $20      ; Initial value
  bitX = 0                   ; Fading up
  PwmOut ledX, 255, ledX_min ; Set LED
#EndMacro

; Update a LED fade

#Macro UpdateLed( ledX, wX, bitX, ledX_min, ledX_max, ledX_inc )
  If bitX = 0 Then
    ; Fading up
    wX = wX + ledX_inc
    w5 = ledX_max * $20
    If wX >= w5 Then
      wX   = w5 ; Limit to maximum
      bitX = 1  ; Fade down next
    End If
  Else
    ; Fading down
    wX = wX Min ledX_inc - ledX_inc
    w5 = ledX_min * $20
    If wX <= w5 Then
      wX   = w5 ; Limit to minimum
      bitX = 0  ; Fade up next
    End If
  End If
#EndMacro

; Output a LED level

#Macro OutputLed( ledX, wX )
  w5 = wX / $20
  PwmDuty ledX, w5 
#EndMacro

; .--------------------------------------------.
; | This is the main body of the program       |
; `--------------------------------------------'

; Initialise the four LED levels and outputs

Initialise:
  InitLed( LED1, w1, bit1, LED1_MIN )
  InitLed( LED2, w2, bit2, LED2_MIN )
  InitLed( LED3, w3, bit3, LED3_MIN )
  InitLed( LED4, w4, bit4, LED4_MIN )

; Integrity checking for when testing

IntegrityChecking:
  #IfDef SIMULATING
    ShowLed( LED1, 1, LED1_MIN, LED1_MAX, LED1_INC, LED1_MS, LED1_LOOP )
    ShowLed( LED2, 2, LED2_MIN, LED2_MAX, LED2_INC, LED2_MS, LED2_LOOP )
    ShowLed( LED3, 3, LED3_MIN, LED3_MAX, LED3_INC, LED3_MS, LED3_LOOP )
    ShowLed( LED4, 4, LED4_MIN, LED4_MAX, LED4_INC, LED4_MS, LED4_LOOP )
  #EndIf

; Main fade up and fade down loop

MainLoop:
  Do
    ; Fade the four LEDs
    UpdateLed( LED1, w1, bit1, LED1_MIN, LED1_MAX, LED1_INC )
    UpdateLed( LED2, w2, bit2, LED2_MIN, LED2_MAX, LED2_INC )
    UpdateLed( LED3, w3, bit3, LED3_MIN, LED3_MAX, LED3_INC )
    UpdateLed( LED4, w4, bit4, LED4_MIN, LED4_MAX, LED4_INC )
    ; Output the four LED levels
    OutputLed( LED1, w1 )
    OutputLed( LED2, w2 )
    OutputLed( LED3, w3 )
    OutputLed( LED4, w4 )
    ; Fix the loop timing
    Pause LOOP_MS
  Loop
 
#10
Thanks yet again. I've been tied up with other matters for the last couple of evenings. Now I can't wait for it to go dark and I can see the effect of this latest idea.
 
#11
Ahhhh I can't see a rhyme or reason for this error!! And it's almost dark here too :)

#Macro ShowLed( ledX, X, ledX_min, ledX_max, ledX_inc, ledX_ms, ledX_loop )
^
Syntax error on line 64 at/before position 1

Error: syntax error
 
#12
I can't get the pre-processor to remain checked (compiler-options) as suggested elsewhere. I don't think I'm in blocky mode, says Logicator for PICAXE 6.1.0.0

I'm not sure how to start PE6 in a different more or which mode to use the pre=processor,
 

hippy

Technical Support
Staff member
#14
I can't get the pre-processor to remain checked (compiler-options) as suggested elsewhere. I don't think I'm in blocky mode, says Logicator for PICAXE 6.1.0.0
You seem to have figured it out; but that was the issue; when PE6 is started in Blockly mode, and perhaps Logicator mode, it ignores the enabling of the pre-processor, resets it to being off.
 
#15
Yes, I've figured it all out hippy. Thanks very much for your help over the last week or so. Unfortunately it looks like I'll only have time to get one of these lotus flowers up and running now but next year we'll have all eight working and I'm sure they will look very nice. I've also learnt an awful lot in the last two weeks and this is all thanks to people like yourself selflessly contributing to this forum. I can't express my gratitude enough.
 
#16
Just to revisit this is I may. I've noticed that if I just use something like High B.2 then the LED is a certain brightness. However using PwmOut and PWDuty etc, whatever parameters I use for these functions I can never get more then about half the brightness for the LED that I get with just using High B.2. Is there anyway I can use the functions to get at least close to the 'maximum' brightness for the LED?
 

hippy

Technical Support
Staff member
#17
However using PwmOut and PWDuty etc, whatever parameters I use for these functions I can never get more then about half the brightness for the LED that I get with just using High B.2
Perhaps post the code you have which may reveal why you are not getting full brightness.

It should be possible to have (almost) full brightness when the duty is four times the period value, full brightness is when duty is four times the period plus three. If using a variable duty value then it should be possible to get full brightness.

For example, with a period of 63, full brightness will be when duty is (63*4)+3 = 255.
 
Last edited:
#18
Thanks, that explains things somewhat. I'll play around with this ratio in mind. I'm using the code you gave me a few posts up from this but comparing the brightness of a LED on an output using a 'high' command only.
 

hippy

Technical Support
Staff member
#19
If using the code in Post #9, you should be able to test full brightness by inserting the following before the "MainLoop:' label -
Code:
InitLed( LED1, w1, bit1, LED1_MAX )
InitLed( LED2, w2, bit2, LED2_MAX )
InitLed( LED3, w3, bit3, LED3_MAX )
InitLed( LED4, w4, bit4, LED4_MAX )
Do : Loop
It might however be better to try a simple direct hardware test program first, to check that works -
Code:
PwmOut B.2, 255, 1023
PwmOut B.4, 255, 1023
PwmOut C.0, 255, 1023
PwmOut C.2, 255, 1023
Do : Loop
If the above is not giving full brightness that would need to be investigated.

If it does but the first modification doesn't I would guess there's some bug in the code which needs fixing.

If both work, give full brightness, then the issue would presumably be in the fading code.

By a process of elimination, determining what does work, it should be possible to figure out what isn't working as desired.
 
#20
Regarding the test, I regret that with the equipment I've used, I can not make PwmOut produce a LED brightness as bright as one powered directly. I've tried several combinations of LED's but all being identical pairs. I've switched the LED's round etc etc. I've no way of checking the actual brightness levels but I could perhaps use an LDR and just give a kind of comparison. Overall though, I'd say the maximum 'brightness' a PwmOut command can produce is somewhere around 50% that of a directly powered LED.
 

hippy

Technical Support
Staff member
#21
It sounds like the LED's may be trying to draw too much current from the output pins, are causing its voltage to collapse, which then means brightness is reduced.

You will probably need to take the PWMOUT outputs to a FET or transistor which switches the LED power.

But I am confused as to why they seem to be brighter with a HIGH command than with a 100% PWMOUT. That doesn't really make much sense.

You could try this test, which will switch the LED on C.0 from a HIGH to a 100% PWMOUT every second -
Code:
Do
  PwmOut C,0, 255, 1023
  Pause  1000
  PwmOut C.0, OFF
  High   C.0
  Pause  1000
Loop
 
#22
Sorry, maybe you misunderstood me in post #20 as I perhaps didn't explain well enough. When I meant 'directly', I mean that I connected the LED straight to the +ve and -ve sides of the 4.5v supply I'm using. This gives a much greater brightness than using an output from the chip. I did try your last test and as expected there seems no difference in brightness at all, switching from PwmOut and High.
 

hippy

Technical Support
Staff member
#23
Thanks for clarifying.

If HIGH and PWMOUT at 100% give the same brightness, but direct across the power rails gives a much greater brightness, that does suggest trying to draw too much current through the PICAXE I/O pins.
 
Top