PWM Jellyfish

nbw

Senior Member
This is a diagram I created of how the 28X2 PWM / HPWM single mode configuration is -

Code:
.--------.
| PWM #1 |--------------------------------> B.0
`--------'
.--------.
| PWM #2 |--------------------------------> B.5
`--------'
.--------.
| PWM #3 |--------------------------------> C.1
`--------'
.--------.
| PWM #4 |---.----------------------o
`--------'   |   .---.               \o---> C.2
             |   |   |---> HPWMA ---o
             |___|   |---> HPWMB ---------> B.2
                 |   |---> HPWMC ---------> B.1
                 |   |---> HPWMD ---------> B.4
                 `---'
HPWM is not a PWM generator in its own right, but a router for PWM#4. PWMA-PWMD can be individually enabled, allowing up to four things to be separately controlled, but what is output on PWMA-PWMD comes from the same PWM#4 source.

It is like a car with a single engine and a clever selector which can drive just the front wheels, back wheels, both front and back together, or none. Each set of wheels is separately controlled, front and back can be driven or not, but both sets of wheels cannot be driven at the same time at different speeds.

Also, when HPWM mode is not used, C.2 is driven by PWM#4. Once HPWM is enabled, PWM#4 (PWMOUT C.2) is no longer available because PWM#4 is allocated to HPWM use.

Hence the description that not all of the PWMOUT channels are available when HPWM is used; N x PWMOUT, or N-1 x PWMOUT + HPWM.
Thought I'd drop in as I'm making a copper jellyfish with four banks of LEDs that need PWMing... and all I have in my toolbox is a 20X2, a 20M2, and some 08M2s. THe four banks operate LEDs on the tentacles, and I want them to pulse at different rates, rather than all fading up and down together.

Sounds like my cheeky get-out-of-jail with the four x HPWM on the 20x2 isn't going to work... a bit wasteful, but I guess I could use one 08m2 per bank??
 

hippy

Technical Support
Staff member
There's always multi-channel bit-banged PWM. It's quite amazing what can be achieved wth a faster PICAXE and a 20X2 fits that bill. You might be impressed by the following driving four fading output LED's and it could probably be optimised to be even better -
Code:
#Picaxe 20X2
#No_Data

Symbol LED1 = pinC.0 : Symbol DIR1 = dirC.0
Symbol LED2 = pinC.1 : Symbol DIR2 = dirC.1
Symbol LED3 = pinC.2 : Symbol DIR3 = dirC.2
Symbol LED4 = pinC.3 : Symbol DIR4 = dirC.3

Symbol ramp     = w0 ; b1:b0
Symbol ramp.lsb = b0
Symbol ramp.msb = b1

Symbol level1   = b2 : Symbol direction1 = b3
Symbol level2   = b4 : Symbol direction2 = b5
Symbol level3   = b6 : Symbol direction3 = b7
Symbol level4   = b8 : Symbol direction4 = b9

#Macro Fade( level, direction, inc )
  IF direction = 0 Then
    level = level + inc Max 255
    If level = 255 Then
      direction = 1
    End If
  Else
    level = level Min inc - inc
    If level = 0 Then
      direction = 0
    End If
  End If
#EndMacro

PowerOnReset:
  SetFreq M64
  DIR1 = 1
  DIR2 = 1
  DIR3 = 1
  DIR4 = 1

MainLoop:
  Do
    Fade( level1, direction1, 3 )
    Fade( level2, direction2, 5 )
    Fade( level3, direction3, 7 )
    Fade( level4, direction4, 9 )
    Gosub GeneratePwm
  Loop

GeneratePwm:
  ramp = 0
  Do
    LED1 = ramp - level1 & $8000 Max 1
    LED2 = ramp - level2 & $8000 Max 1
    LED3 = ramp - level3 & $8000 Max 1
    LED4 = ramp - level4 & $8000 Max 1
    ramp = ramp + 8
  Loop While ramp.msb = 0
  Return
 

hippy

Technical Support
Staff member
If you can live with each LED only going to 25% duty cycle, which may be acceptable with super-bright LED's and perception of brightness being non-linear, the following code gives very impressive flicker-free fading. This has been modified to cater for much slower fades with a few other optimisations -
Code:
#Picaxe 20X2
#No_Data

Symbol LED1   = C.0
Symbol LED2   = C.1
Symbol LED3   = C.2
Symbol LED4   = C.3

Symbol bits   = w0
Symbol level1 = w1 : Symbol level1.msb = b3 ; b3:b2
Symbol level2 = w2 : Symbol level2.msb = b5 ; b5:b4
Symbol level3 = w3 : Symbol level3.msb = b7 ; b7:b6
Symbol level4 = w4 : Symbol level4.msb = b9 ; b9:b8
Symbol paused = w5

#Macro Fade( level, direction, inc )
  IF direction = 0 Then
    level = $FFFF - inc Max level + inc
    direction = level ^ $FFFF Max 1 ^ 1
  Else
    level = level Min inc - inc
    direction = level Max 1
  End If
  Gosub GeneratePwm
#EndMacro

PowerOnReset:
  SetFreq M64

MainLoop:
  Do
    Fade( level1, bit1, 100 )
    Fade( level2, bit2, 300 )
    Fade( level3, bit3, 500 )
    Fade( level4, bit4, 700 )
  Loop

GeneratePwm:
  PulsOut LED1, level1.msb
  PulsOut LED2, level2.msb
  PulsOut LED3, level3.msb
  PulsOut LED4, level4.msb
  paused = 1020 - level1.msb - level2.msb - level3.msb - level4.msb
  PauseUs paused
  Return
That uses the PULSOUT trick for generating rock-steady servo pulse frames but, with four LED's ,does mean each LED can only ever be on for a maximum of a quarter of the time, equivalent to a maximum of 25% duty.

But it does run from 0% to 25% extremely smoothly, with 0.1% per brightness step resolution.
 

AllyCat

Senior Member
Hi,

Also, I believe there's often a misunderstanding that (unlike for HSERIN and HSEROUT, etc.) the "H" in "HPWM" doesn't stand for "Hardware" but "H-bridge", in other words the four outputs have the same frequency and duty cycle (but different polarities). However, the "ordinary" PWMOUT/DUTY command also uses the on-chip "Hardware", with potentially (up to 4) "independent" channels. You need to read the exact details of the PWMOUT command for the specific PICaxe chip, to determine what interactions may occur (or are permissible) between the four "channels" (PWM pins) and also other facilities such as the SERVO command.

Cheers, Alan.
 

nbw

Senior Member
Hi folks, been away for a couple of days.

I'll describe the project in the very next post after this.

I thought I'd share the result of my PWM queries that the forum helped with last year - an ornamental goldfish about 40cm long made out of copper, brass, an old windscreen wiper motor that died, acupuncture needles, injectors from a dead gas bbq, and a dove bodywash bottle.

The picaxe bit is simple: it reads a thermistor every minute (winks the eye when it reads - turns it white) then translates that into room temp, and displays the room temp as a different coloured eye. If it's purple-ish, the room is a bit cold, about 15. Normal light blue is about 18 deg C. Green is about 20. Should it ever reach 28 - unlikely here! - it'll be red.

There's a 1W RGB LED in the eye, and the 14m2 with its 3 PWM channels happily does all the work for me.3.png1.png2.png
 
Last edited:

nbw

Senior Member
So... the jellyfish.
Made out of a recycled hot water cylinder head, some clotheslines, 16 dead spark plugs. And a laptop power supply, and lots of LEDs. (I might even add an EL wire if I can figure out to safely and easily vary its brightness digitally)

On the top of the jellyfish will be 2 banks of 8, and 16 1W and 2W LEDs, warm white. They have a G4 base and at full 12V are pretty bright. I run them at about 7V so they're barely on, to about 9.5V brighter but still gentle. Sort of like you might see underwater. I've attached a pic of one, when running dim you can see the individual LEDs which is quite neat.

These 2 banks are simply driven by 10.5V, a BD169 transistor, and a 5K pot. Maximum current is 0.05A x 16 = 0.8A, well within the transistors' capability. The user adjusts their brightness manually.

The tentacles (approx 20) will be 1.2m strings of those little warm white LEDs you can buy and operate with 2 CR2032 coin cells. I've made 4 op-amp triangle wave generators, all at different RC combos, so they're all pleasantly out of sync.

Then to the PICAXE.

Under the jellyfish will be a bank of 12 of the 2W LEDs. These I'd like to PWM individually, with different patterns. Each bank will have a switch (off, on 1, on 2). On1 is a gentle pwm up and down. On2 will be a combination fade / mini-flash pattern.

shopping.jpg

I went through my stock of PICAXEs again - no 14m2 which did a good job of the RGB in Cupris auratus (the goldfish). I have lots of 08M with one pwm each. 4 x 08m2. 4 x 20x2.

@hippy: that might be worth a go when I get to coding stage. Possibly if I up the headroom (instead of 10.5V for the LED transistors, give them the full 12V), then 25% might be ok. At 12V these LEDs run around 0.16A. Maximum I need them is 0.05A.

Another thought: I found a MAX519 dual DAC in my box of stuff. Never used one - but it's I2C which might need a bit of figuring out - but could work. Those dual channels, plus the one on just about any PICAXE, could be good. Was going to attach the datasheet here for anyone interested, but the file is too large - it's a whopping 1.2Mb. Here's the link instead


THough I like Hippy's inventive solution.
 
Last edited:

nbw

Senior Member
Hi,

Also, I believe there's often a misunderstanding that (unlike for HSERIN and HSEROUT, etc.) the "H" in "HPWM" doesn't stand for "Hardware" but "H-bridge", in other words the four outputs have the same frequency and duty cycle (but different polarities). However, the "ordinary" PWMOUT/DUTY command also uses the on-chip "Hardware", with potentially (up to 4) "independent" channels. You need to read the exact details of the PWMOUT command for the specific PICaxe chip, to determine what interactions may occur (or are permissible) between the four "channels" (PWM pins) and also other facilities such as the SERVO command.

Cheers, Alan.
I have to be honest, the whole hpwm thing confuses me. Hippy's explanation helped a bit, the main thing for me was - they're not 4 fully spearate channels, but 4 linked ones with a special use.....
 

premelec

Senior Member
Some years ago I used some 8 channel TI serial DACs bit banged with 08M2 to set CC drivers for LEDs- worked quite well as they are latched.
 

AllyCat

Senior Member
Hi,
.., the whole hpwm thing confuses me. - they're not 4 fully spearate channels, but 4 linked ones ....
True, but you don't need separate PWM frequencies just to dim some LEDs. But the point about the H-bridge (HPWM) is that the four outputs also have the same duty cycle. Or to be precise, two LED / outputs will increase from Minimum to Maximum whilst the other two decrease correspondingly from Maximum to Minimum, when you increase the duty cycle parameter.

However, with most PICaxes (except the 08s) you should be able to use four independent PWMOUT commands (one for each port.pin), since you would normally set the same frequency for the four (LED) channels anyway (or even accept 50 Hz if servos are also in use).

Cheers, Alan.
 

lbenson

Senior Member
Beautiful work, nbw.

One nice thing about hippy's pulsout code--the only thing about it that is specific to the X2 is the M64--change that to M32 and it works on a 14M2 as well:
Code:
' 14fade4leds
#Picaxe 14M2
#No_Data

Symbol LED1   = B.2
Symbol LED2   = B.3
Symbol LED3   = B.4
Symbol LED4   = B.5

Symbol bits   = w0
Symbol level1 = w1 : Symbol level1.msb = b3 ; b3:b2
Symbol level2 = w2 : Symbol level2.msb = b5 ; b5:b4
Symbol level3 = w3 : Symbol level3.msb = b7 ; b7:b6
Symbol level4 = w4 : Symbol level4.msb = b9 ; b9:b8
Symbol paused = w5

#Macro Fade( level, direction, inc )
  IF direction = 0 Then
    level = $FFFF - inc Max level + inc
    direction = level ^ $FFFF Max 1 ^ 1
  Else
    level = level Min inc - inc
    direction = level Max 1
  End If
  Gosub GeneratePwm
#EndMacro

PowerOnReset:
  SetFreq M32

MainLoop:
  Do
    Fade( level1, bit1, 100 )
    Fade( level2, bit2, 300 )
    Fade( level3, bit3, 500 )
    Fade( level4, bit4, 700 )
  Loop

GeneratePwm:
  PulsOut LED1, level1.msb
  PulsOut LED2, level2.msb
  PulsOut LED3, level3.msb
  PulsOut LED4, level4.msb
  paused = 1020 - level1.msb - level2.msb - level3.msb - level4.msb
  PauseUs paused
  Return
 

nbw

Senior Member
Hi,

True, but you don't need separate PWM frequencies just to dim some LEDs. But the point about the H-bridge (HPWM) is that the four outputs also have the same duty cycle. Or to be precise, two LED / outputs will increase from Minimum to Maximum whilst the other two decrease correspondingly from Maximum to Minimum, when you increase the duty cycle parameter.

However, with most PICaxes (except the 08s) you should be able to use four independent PWMOUT commands (one for each port.pin), since you would normally set the same frequency for the four (LED) channels anyway (or even accept 50 Hz if servos are also in use).

Cheers, Alan.
Oh, something's just resonated.
Is it something like:

I could have A, B, C, D hpwm all running at say 5 kHz, but not different ones.
Thinking about the stage in the PWM cycle they'd be at - would 2 need to be the same, and 2 also the same but inverse? I envisaged the whole lot out of sync as it were, so one on 14% brightness, one at 34%, maybe 75% on the way down, etc...

So I agree - the frequency bit (all being the same) is fine for LEDs. It's whether I am locked into the 2+2 arrangement.
 

nbw

Senior Member
Beautiful work, nbw.

One nice thing about hippy's pulsout code--the only thing about it that is specific to the X2 is the M64--change that to M32 and it works on a 14M2 as well:
Code:
' 14fade4leds
#Picaxe 14M2
#No_Data

Symbol LED1   = B.2
Symbol LED2   = B.3
Symbol LED3   = B.4
Symbol LED4   = B.5

Symbol bits   = w0
Symbol level1 = w1 : Symbol level1.msb = b3 ; b3:b2
Symbol level2 = w2 : Symbol level2.msb = b5 ; b5:b4
Symbol level3 = w3 : Symbol level3.msb = b7 ; b7:b6
Symbol level4 = w4 : Symbol level4.msb = b9 ; b9:b8
Symbol paused = w5

#Macro Fade( level, direction, inc )
  IF direction = 0 Then
    level = $FFFF - inc Max level + inc
    direction = level ^ $FFFF Max 1 ^ 1
  Else
    level = level Min inc - inc
    direction = level Max 1
  End If
  Gosub GeneratePwm
#EndMacro

PowerOnReset:
  SetFreq M32

MainLoop:
  Do
    Fade( level1, bit1, 100 )
    Fade( level2, bit2, 300 )
    Fade( level3, bit3, 500 )
    Fade( level4, bit4, 700 )
  Loop

GeneratePwm:
  PulsOut LED1, level1.msb
  PulsOut LED2, level2.msb
  PulsOut LED3, level3.msb
  PulsOut LED4, level4.msb
  paused = 1020 - level1.msb - level2.msb - level3.msb - level4.msb
  PauseUs paused
  Return
thanks! I love working light / electronics into my art, even though my children can't understand why we need more fish style lights in the house!

I also have ideas for a starfish, seahorse, and a 2 metre long oarfish. Lots of eye rolls from the kids.
 

nbw

Senior Member
If you can live with each LED only going to 25% duty cycle, which may be acceptable with super-bright LED's and perception of brightness being non-linear, the following code gives very impressive flicker-free fading. This has been modified to cater for much slower fades with a few other optimisations -
Code:
#Picaxe 20X2
#No_Data

Symbol LED1   = C.0
Symbol LED2   = C.1
Symbol LED3   = C.2
Symbol LED4   = C.3

Symbol bits   = w0
Symbol level1 = w1 : Symbol level1.msb = b3 ; b3:b2
Symbol level2 = w2 : Symbol level2.msb = b5 ; b5:b4
Symbol level3 = w3 : Symbol level3.msb = b7 ; b7:b6
Symbol level4 = w4 : Symbol level4.msb = b9 ; b9:b8
Symbol paused = w5

#Macro Fade( level, direction, inc )
  IF direction = 0 Then
    level = $FFFF - inc Max level + inc
    direction = level ^ $FFFF Max 1 ^ 1
  Else
    level = level Min inc - inc
    direction = level Max 1
  End If
  Gosub GeneratePwm
#EndMacro

PowerOnReset:
  SetFreq M64

MainLoop:
  Do
    Fade( level1, bit1, 100 )
    Fade( level2, bit2, 300 )
    Fade( level3, bit3, 500 )
    Fade( level4, bit4, 700 )
  Loop

GeneratePwm:
  PulsOut LED1, level1.msb
  PulsOut LED2, level2.msb
  PulsOut LED3, level3.msb
  PulsOut LED4, level4.msb
  paused = 1020 - level1.msb - level2.msb - level3.msb - level4.msb
  PauseUs paused
  Return
That uses the PULSOUT trick for generating rock-steady servo pulse frames but, with four LED's ,does mean each LED can only ever be on for a maximum of a quarter of the time, equivalent to a maximum of 25% duty.

But it does run from 0% to 25% extremely smoothly, with 0.1% per brightness step resolution.
Once I've got over a hump at work, (next 2 weeks) I'll try this out with a transistor, 11V, a 20X2 and one of the LEDs. 25% might actually be enough in this case!
 

hippy

Technical Support
Staff member
I could have A, B, C, D hpwm all running at say 5 kHz, but not different ones ... I envisaged the whole lot out of sync as it were, so one on 14% brightness, one at 34%, maybe 75% on the way down, etc...
For HPWM, all frequencies will be the same, all duties will be the same, only polarities can be different.

It is best thought of as a single PWM generator with four synchronised outputs.
 

AllyCat

Senior Member
Hi,
...... It's whether I am locked into the 2+2 arrangement.
With HPWM yes you are. HOWEVER, PWMOUT uses the same (PIC) "background" hardware, but without the "restriction" (implicit in driving four transistors as an H bridge) of the duty period (and frequency) being the same for each channel (pin).

Don't confuse PWMOUT with the legacy "PWM" command which does indeed use "software" (the PICaxe operating system) and blocks other tasks whilst the PWM output is active. Also, you can largely ignore the PWMDUTY command, which uses a lot more PICaxe program memory space (and time) and is needed only where "glitches" in the PWM output may cause problems.

Cheers, Alan.
 

nbw

Senior Member
If you can live with each LED only going to 25% duty cycle, which may be acceptable with super-bright LED's and perception of brightness being non-linear, the following code gives very impressive flicker-free fading. This has been modified to cater for much slower fades with a few other optimisations -
Code:
#Picaxe 20X2
#No_Data

Symbol LED1   = C.0
Symbol LED2   = C.1
Symbol LED3   = C.2
Symbol LED4   = C.3

Symbol bits   = w0
Symbol level1 = w1 : Symbol level1.msb = b3 ; b3:b2
Symbol level2 = w2 : Symbol level2.msb = b5 ; b5:b4
Symbol level3 = w3 : Symbol level3.msb = b7 ; b7:b6
Symbol level4 = w4 : Symbol level4.msb = b9 ; b9:b8
Symbol paused = w5

#Macro Fade( level, direction, inc )
  IF direction = 0 Then
    level = $FFFF - inc Max level + inc
    direction = level ^ $FFFF Max 1 ^ 1
  Else
    level = level Min inc - inc
    direction = level Max 1
  End If
  Gosub GeneratePwm
#EndMacro

PowerOnReset:
  SetFreq M64

MainLoop:
  Do
    Fade( level1, bit1, 100 )
    Fade( level2, bit2, 300 )
    Fade( level3, bit3, 500 )
    Fade( level4, bit4, 700 )
  Loop

GeneratePwm:
  PulsOut LED1, level1.msb
  PulsOut LED2, level2.msb
  PulsOut LED3, level3.msb
  PulsOut LED4, level4.msb
  paused = 1020 - level1.msb - level2.msb - level3.msb - level4.msb
  PauseUs paused
  Return
That uses the PULSOUT trick for generating rock-steady servo pulse frames but, with four LED's ,does mean each LED can only ever be on for a maximum of a quarter of the time, equivalent to a maximum of 25% duty.

But it does run from 0% to 25% extremely smoothly, with 0.1% per brightness step resolution.
Hi Hippy, I'm going to give this a go.
Best I get the beginner questions out of the way....

I can see in your code where the LEDs are assigned ports which is obvious. The 25% maximum duty is fine.

How do I specify how long the fade for each LED should be? If I have offset fade times, they'll appear out of sync and random, provided I don't go for common multipliers.

E.g.
LED1 - fade on and fade right out cycle = 9.5s
LED2 - 6.7s
LED3 - 7.4s
LED4 - 8.2s
(these are just random timings, but you get the idea. The fade-ins and fade-outs will overlap).

I'll have each port connected to a 330 Ohm resistor, to the base of a BD165 transistor (capable of handling 1.5A, I'm only sending it about 0.2A), to drive the LEDs. I initially thought 4 banks of 3 LEDs would be nice and random. I could go for 3 banks of 4 LEDs and 33% duty, but I like the 4 banks idea a little better to be honest.
 

nbw

Senior Member
Hi,

With HPWM yes you are. HOWEVER, PWMOUT uses the same (PIC) "background" hardware, but without the "restriction" (implicit in driving four transistors as an H bridge) of the duty period (and frequency) being the same for each channel (pin).

Don't confuse PWMOUT with the legacy "PWM" command which does indeed use "software" (the PICaxe operating system) and blocks other tasks whilst the PWM output is active. Also, you can largely ignore the PWMDUTY command, which uses a lot more PICaxe program memory space (and time) and is needed only where "glitches" in the PWM output may cause problems.

Cheers, Alan.
Hi there Alan
I've only ever used the PWMOUT command. It's served me well (up to this point, at least!)
 

hippy

Technical Support
Staff member
How do I specify how long the fade for each LED should be?
The fade time is the third number in the Fade() macro invocations, for example the 100 in my example here -

Fade( level1, bit1, 100 )

Actualy that's an increment rather than a time.

We have 16-bit levels so we increment from off (0) to full ($FFFF) and we add that increment every 40.8 ms

The number of increments will be Tms / 40.8ms, and the amount of increment per step will be $FFFF / number of increments. So I make that -

increment = $FFFF / number of increments

increment = $FFFF / ( Tms / 40.8ms )

increment = $FFFF * 40.8 / Tms

increment = 2673828 / Tms

For 9.5s : 2673828 / 9500 = 281
For 6.7s : 2673828 / 6700 = 399
For 7.4s : 2673828 / 7400 = 361
For 8.2s : 2673828 / 8200 = 326

so -
Code:
Fade( level1, bit1, 281 )
Fade( level2, bit2, 399 )
Fade( level3, bit3, 361 )
Fade( level4, bit4, 326 )
Worth a try anyway.

Added : This would be even better ...
Code:
Symbol TIME1 = 9500
Symbol TIME2 = 6700
Symbol TIME3 = 7400
Symbol TIME4 = 8200

Symbol K     = 2673828 ; $FFFF * 40.8

Symbol INC1  = K / TIME1
Symbol INC2  = K / TIME2
Symbol INC3  = K / TIME3
Symbol INC4  = K / TIME4

Fade( level1, bit1, INC1 )
Fade( level2, bit2, INC2 )
Fade( level3, bit3, INC3 )
Fade( level4, bit4, INC4 )
 
Last edited:

nbw

Senior Member
For HPWM, all frequencies will be the same, all duties will be the same, only polarities can be different.

It is best thought of as a single PWM generator with four synchronised outputs.
The fade time is the third number in the Fade() macro invocations, for example the 100 in my example here -

Fade( level1, bit1, 100 )

Actualy that's an increment rather than a time.

We have 16-bit levels so we increment from off (0) to full ($FFFF) and we add that increment every 40.8 ms

The number of increments will be Tms / 40.8ms, and the amount of increment per step will be $FFFF / number of increments. So I make that -

increment = $FFFF / number of increments

increment = $FFFF / ( Tms / 40.8ms )

increment = $FFFF * 40.8 / Tms

increment = 2673828 / Tms

For 9.5s : 2673828 / 9500 = 281
For 6.7s : 2673828 / 6700 = 399
For 7.4s : 2673828 / 7400 = 361
For 8.2s : 2673828 / 8200 = 326

so -
Code:
Fade( level1, bit1, 281 )
Fade( level2, bit2, 399 )
Fade( level3, bit3, 361 )
Fade( level4, bit4, 326 )
Worth a try anyway.

Added : This would be even better ...
Code:
Symbol TIME1 = 9500
Symbol TIME2 = 6700
Symbol TIME3 = 7400
Symbol TIME4 = 8200

Symbol K     = 2673828 ; $FFFF * 40.8

Symbol INC1  = K / TIME1
Symbol INC2  = K / TIME2
Symbol INC3  = K / TIME3
Symbol INC4  = K / TIME4

Fade( level1, bit1, INC1 )
Fade( level2, bit2, INC2 )
Fade( level3, bit3, INC3 )
Fade( level4, bit4, INC4 )
Hi Hippy, I gave this a go tonight - it works very well. In fact, I'll probably have to dial down the 11.5V for the LEDs down to approx 9.5V as even at 25%, they're cranking on very bright.
The fading works great. 300 as a fade number is a bit high but smooth, about 130-150 is good but i find it seems to go 'clunk-clunk-clunk' in brightness steps, quite noticable. But, with four banks of three LEDs, the little 'steps' might look quite neat

I'm thinking a pause would be good, along the lines of:
start > slow fade on over 4 secs > immediately start fading down to off over 4 secs > pause 3 seconds > start again

I'm guessing a "Wait 2" could be added somewhere in your macro section, but not sure where. I'll add the other 3 LEDs for testing tomorrow night.

another useful variation would be :
start > medium fade on over 2.5 secs > immediately start fading down to off over 4 secs > pause 3 seconds > start again

thanks
Barney
 

nbw

Senior Member
I wonder as an afterthought:
I have a 390 ohm resistor driving the base of the transistor, maybe I could raise this to lessen the turn-on for it, to reduce brightness.
Also, could put a very small capacitor across the LED terminals to try to smooth out the bumpiness a little.
 

nbw

Senior Member
Ah! The 12V LEDs I'm using can be fed with DC or AC.... I wonder if the internal rectifying circuitry is doing the step-step-step.
When I add a good old fashioned normal LED (orange, 2V forward drop) - it's smooth.
 

hippy

Technical Support
Staff member
I'm guessing a "Wait 2" could be added somewhere in your macro section, but not sure where.
Unfortunately that won't work. The 'GeneratePwm' routine has to be called regularly or the LED's won't light and you will have long pauses of all LED's off.

It should however be possible to add an additional state which allows a pause period within the 'Fade macro'. I look at doing that.

i find it seems to go 'clunk-clunk-clunk' in brightness steps, quite noticable.
When I add a good old fashioned normal LED (orange, 2V forward drop) - it's smooth
It could be that it's the brightness of the used LED's which is showing the issue, stretching the dim to bright range which means every 256th step is more significant. When the increment is over 256 there will be multiple step increments.

You may be able to improve that by doubling up the "Gosub GeneratePwm" in the Fade macro and then halving the increment values in the Fade invocations, or halving the K value if using SYMBOL definitions to set timing.

The 'GeneratePwm' routine uses 8-bit PWM but it might be possible to extend that to more bits. Untested -
Code:
Symbol pulse1   = w11 ; b23:b22
Symbol pulse2   = w12 ; b25:b24
Symbol pulse3   = w13 ; b27:b26
Symbol pulse4   = w14 ; b29:b28

Symbol PWM_BITS = 9
Symbol PWM_DIV  = 128 ; 1 << ( 16 - PWM_BITS )
Symbol PWM_MPY  = 4   ; 1 << ( ( 1 << ( PWM_BITS - 8 ) ) - 1 )

; Symbol PWM_BITS = 10
; Symbol PWM_DIV  = 64 ; 1 << ( 16 - PWM_BITS )
; Symbol PWM_MPY  = 8  ; 1 << ( ( 1 << ( PWM_BITS - 8 ) ) - 1 )

GeneratePwm:
  pulse1 = level1 / PWM_DIV : PulsOut LED1, pulse1
  pulse2 = level2 / PWM_DIV : PulsOut LED2, pulse2
  pulse3 = level3 / PWM_DIV : PulsOut LED3, pulse3
  pulse4 = level4 / PWM_DIV : PulsOut LED4, pulse4
  paused = PWM_MPY * 1020 - pulse1 - pulse2 - pulse3 - pulse4
  PauseUs paused
  Return
 

hippy

Technical Support
Staff member
I'm not at all convinced the maths matches the theory, and because of rounding errors you will need to tweak the TIMEx and DWELLx settings but this appears to work on a 20X2 with normal LED's -
Code:
#Picaxe 14M2
#No_Data

; Note : Do not set DWELLx to zero !

Symbol TIME1     = 9500 : Symbol DWELL1 = 1000
Symbol TIME2     = 6700 : Symbol DWELL2 = 1000
Symbol TIME3     = 7400 : Symbol DWELL3 = 1000
Symbol TIME4     = 8200 : Symbol DWELL4 = 1000

; Symbol PWM_BIT   = 8
; Symbol PWM_DIV   = 256
; Symbol PWM_MAX   = 255
; Symbol PWM_ADD   = PWM_MAX * 4
; Symbol T_LOOP_MS = PWM_ADD / 100

; Symbol PWM_BIT   = 9
; Symbol PWM_DIV   = 128
; Symbol PWM_MAX   = 511
; Symbol PWM_ADD   = PWM_MAX * 4
; Symbol T_LOOP_MS = PWM_ADD / 200

Symbol PWM_BIT   = 10
Symbol PWM_DIV   = 64
Symbol PWM_MAX   = 1023
Symbol PWM_ADD   = PWM_MAX * 4
Symbol T_LOOP_MS = PWM_ADD / 400

Symbol K         = 65535 * T_LOOP_MS

Symbol TLOOP1    = TIME1 / T_LOOP_MS
Symbol TLOOP2    = TIME2 / T_LOOP_MS
Symbol TLOOP3    = TIME3 / T_LOOP_MS
Symbol TLOOP4    = TIME4 / T_LOOP_MS

Symbol TINC1     = $FFFF / TLOOP1
Symbol TINC2     = $FFFF / TLOOP2
Symbol TINC3     = $FFFF / TLOOP3
Symbol TINC4     = $FFFF / TLOOP4

Symbol DLOOP1    = DWELL1 / T_LOOP_MS
Symbol DLOOP2    = DWELL2 / T_LOOP_MS
Symbol DLOOP3    = DWELL3 / T_LOOP_MS
Symbol DLOOP4    = DWELL4 / T_LOOP_MS

Symbol DINC1     = $FFFF / DLOOP1
Symbol DINC2     = $FFFF / DLOOP2
Symbol DINC3     = $FFFF / DLOOP3
Symbol DINC4     = $FFFF / DLOOP4

Symbol LED1      = B.2
Symbol LED2      = B.3
Symbol LED3      = B.4
Symbol LED4      = B.5

Symbol bits      = w0
Symbol paused    = w1

Symbol level1    = w2
Symbol level2    = w3
Symbol level3    = w4
Symbol level4    = w5

Symbol paused1   = w6
Symbol paused2   = w7
Symbol paused3   = w8
Symbol paused4   = w9

Symbol pulse1    = w10
Symbol pulse2    = w11
Symbol pulse3    = w12
Symbol pulse4    = w13

#Macro Fade( level, direction, fadeInc, pausedVar, pausedBit, pauseInc )
  If pausedBit = 0 Then
    ; Paused
    pausedVar = pausedVar Min pauseInc - pauseInc
    If pausedVar = 0 Then
      pausedVar = $FFFF
      pausedBit = 1
    End If
  Else If direction = 0 Then
    ; Fading up
    level = $FFFF - fadeInc Max level + fadeInc
    direction = level ^ $FFFF Max 1 ^ 1
  Else
    ; Fading down
    level = level Min fadeInc - fadeInc
    direction = level Max 1
    pausedBit = direction
  End If
#EndMacro

PowerOnReset:
  SetFreq M32

MainLoop:
  Do
    Fade( level1, bit1, TINC1, paused1, bit11, DINC1 )
    Fade( level2, bit2, TINC2, paused2, bit12, DINC2 )
    Fade( level3, bit3, TINC3, paused3, bit13, DINC3 )
    Fade( level4, bit4, TINC4, paused4, bit14, DINC4 )
    Gosub GeneratePwm
  Loop

GeneratePwm:
  pulse1 = level1 / PWM_DIV : PulsOut LED1, pulse1
  pulse2 = level2 / PWM_DIV : PulsOut LED2, pulse2
  pulse3 = level3 / PWM_DIV : PulsOut LED3, pulse3
  pulse4 = level4 / PWM_DIV : PulsOut LED4, pulse4
  paused = PWM_ADD - pulse1 - pulse2 - pulse3 - pulse4
  PauseUs paused
  Return
 

nbw

Senior Member
I'm not at all convinced the maths matches the theory, and because of rounding errors you will need to tweak the TIMEx and DWELLx settings but this appears to work on a 20X2 with normal LED's -
Code:
#Picaxe 14M2
#No_Data

; Note : Do not set DWELLx to zero !

Symbol TIME1     = 9500 : Symbol DWELL1 = 1000
Symbol TIME2     = 6700 : Symbol DWELL2 = 1000
Symbol TIME3     = 7400 : Symbol DWELL3 = 1000
Symbol TIME4     = 8200 : Symbol DWELL4 = 1000

; Symbol PWM_BIT   = 8
; Symbol PWM_DIV   = 256
; Symbol PWM_MAX   = 255
; Symbol PWM_ADD   = PWM_MAX * 4
; Symbol T_LOOP_MS = PWM_ADD / 100

; Symbol PWM_BIT   = 9
; Symbol PWM_DIV   = 128
; Symbol PWM_MAX   = 511
; Symbol PWM_ADD   = PWM_MAX * 4
; Symbol T_LOOP_MS = PWM_ADD / 200

Symbol PWM_BIT   = 10
Symbol PWM_DIV   = 64
Symbol PWM_MAX   = 1023
Symbol PWM_ADD   = PWM_MAX * 4
Symbol T_LOOP_MS = PWM_ADD / 400

Symbol K         = 65535 * T_LOOP_MS

Symbol TLOOP1    = TIME1 / T_LOOP_MS
Symbol TLOOP2    = TIME2 / T_LOOP_MS
Symbol TLOOP3    = TIME3 / T_LOOP_MS
Symbol TLOOP4    = TIME4 / T_LOOP_MS

Symbol TINC1     = $FFFF / TLOOP1
Symbol TINC2     = $FFFF / TLOOP2
Symbol TINC3     = $FFFF / TLOOP3
Symbol TINC4     = $FFFF / TLOOP4

Symbol DLOOP1    = DWELL1 / T_LOOP_MS
Symbol DLOOP2    = DWELL2 / T_LOOP_MS
Symbol DLOOP3    = DWELL3 / T_LOOP_MS
Symbol DLOOP4    = DWELL4 / T_LOOP_MS

Symbol DINC1     = $FFFF / DLOOP1
Symbol DINC2     = $FFFF / DLOOP2
Symbol DINC3     = $FFFF / DLOOP3
Symbol DINC4     = $FFFF / DLOOP4

Symbol LED1      = B.2
Symbol LED2      = B.3
Symbol LED3      = B.4
Symbol LED4      = B.5

Symbol bits      = w0
Symbol paused    = w1

Symbol level1    = w2
Symbol level2    = w3
Symbol level3    = w4
Symbol level4    = w5

Symbol paused1   = w6
Symbol paused2   = w7
Symbol paused3   = w8
Symbol paused4   = w9

Symbol pulse1    = w10
Symbol pulse2    = w11
Symbol pulse3    = w12
Symbol pulse4    = w13

#Macro Fade( level, direction, fadeInc, pausedVar, pausedBit, pauseInc )
  If pausedBit = 0 Then
    ; Paused
    pausedVar = pausedVar Min pauseInc - pauseInc
    If pausedVar = 0 Then
      pausedVar = $FFFF
      pausedBit = 1
    End If
  Else If direction = 0 Then
    ; Fading up
    level = $FFFF - fadeInc Max level + fadeInc
    direction = level ^ $FFFF Max 1 ^ 1
  Else
    ; Fading down
    level = level Min fadeInc - fadeInc
    direction = level Max 1
    pausedBit = direction
  End If
#EndMacro

PowerOnReset:
  SetFreq M32

MainLoop:
  Do
    Fade( level1, bit1, TINC1, paused1, bit11, DINC1 )
    Fade( level2, bit2, TINC2, paused2, bit12, DINC2 )
    Fade( level3, bit3, TINC3, paused3, bit13, DINC3 )
    Fade( level4, bit4, TINC4, paused4, bit14, DINC4 )
    Gosub GeneratePwm
  Loop

GeneratePwm:
  pulse1 = level1 / PWM_DIV : PulsOut LED1, pulse1
  pulse2 = level2 / PWM_DIV : PulsOut LED2, pulse2
  pulse3 = level3 / PWM_DIV : PulsOut LED3, pulse3
  pulse4 = level4 / PWM_DIV : PulsOut LED4, pulse4
  paused = PWM_ADD - pulse1 - pulse2 - pulse3 - pulse4
  PauseUs paused
  Return
I tried this one tonight - it didn't do anything. It uploaded to the 20x2 ok, after I over-rode the 14m2 at the start.

Good news though - I connected 3 transistor-LED combinations today, upped the base resistor value from 390 ohms to 1k8 and it softens the full-on "glaring bright" period at the end quite well. When I actually build the circuit the source voltage will be 11 - 11.5V, so I'll adjust the resistor value down a little, accordingly.

Values of about 120 - 180 for your "fade" lines work quite well.
 

nbw

Senior Member
Unfortunately that won't work. The 'GeneratePwm' routine has to be called regularly or the LED's won't light and you will have long pauses of all LED's off.

It should however be possible to add an additional state which allows a pause period within the 'Fade macro'. I look at doing that.



It could be that it's the brightness of the used LED's which is showing the issue, stretching the dim to bright range which means every 256th step is more significant. When the increment is over 256 there will be multiple step increments.

You may be able to improve that by doubling up the "Gosub GeneratePwm" in the Fade macro and then halving the increment values in the Fade invocations, or halving the K value if using SYMBOL definitions to set timing.

The 'GeneratePwm' routine uses 8-bit PWM but it might be possible to extend that to more bits. Untested -
Code:
Symbol pulse1   = w11 ; b23:b22
Symbol pulse2   = w12 ; b25:b24
Symbol pulse3   = w13 ; b27:b26
Symbol pulse4   = w14 ; b29:b28

Symbol PWM_BITS = 9
Symbol PWM_DIV  = 128 ; 1 << ( 16 - PWM_BITS )
Symbol PWM_MPY  = 4   ; 1 << ( ( 1 << ( PWM_BITS - 8 ) ) - 1 )

; Symbol PWM_BITS = 10
; Symbol PWM_DIV  = 64 ; 1 << ( 16 - PWM_BITS )
; Symbol PWM_MPY  = 8  ; 1 << ( ( 1 << ( PWM_BITS - 8 ) ) - 1 )

GeneratePwm:
  pulse1 = level1 / PWM_DIV : PulsOut LED1, pulse1
  pulse2 = level2 / PWM_DIV : PulsOut LED2, pulse2
  pulse3 = level3 / PWM_DIV : PulsOut LED3, pulse3
  pulse4 = level4 / PWM_DIV : PulsOut LED4, pulse4
  paused = PWM_MPY * 1020 - pulse1 - pulse2 - pulse3 - pulse4
  PauseUs paused
  Return
I'll try this too, but just to check so I don't blow it up:

1. I add the Symbol statements at the top of the code,
2. Replace the current GeneratePWM subroutine with this one?

--> thanks for your help to date by the way; much appreciated
 

hippy

Technical Support
Staff member
I tried this one tonight - it didn't do anything. It uploaded to the 20x2 ok, after I over-rode the 14m2 at the start.
Works for me when I do the same. Are the LED1-LED4 pins correct for your hardware ?
 

nbw

Senior Member
Before I say I forgot to change them over, I'll say I'll change them over tonight and re-try.
 

nbw

Senior Member
OK, gave it a go. They all work. Amazing what assigning the right outputs does.

I have 3 boarded up and going at present:

Symbol TIME1 = 2200 : Symbol DWELL1 = 1900 ' pin 4
Symbol TIME2 = 3000 : Symbol DWELL2 = 1500 ' pin 3
Symbol TIME3 = 1900 : Symbol DWELL3 = 200 ' this one not used
Symbol TIME4 = 1600 : Symbol DWELL4 = 1200 ' pin 1

They snap on a bit sharply once they've been off, I think that might be partially caused by the LEDs with the AC/DC configuration in them. I have an 8V setting on my portable power supply - the LEDs are too dim with that, but it does show that reduced voltage seems to make the fade-on a bit gentler, and less top-end full-on at the top of the fade (compared to 12V). Plus, I think instead of 330 / 390 R at the transistor bases, about 2K7 means a smoother run-in too.
The TIME2 one seems a bit slow
The TIME4 one is a bit fast, so 2000 TIME is about the minimum
The TIME1 one is about right ... so I'll try 2000, 2400, 2800 with slightly different DWELL times (these are in ms from what I can read)

I'll keep experimenting, thanks! :)
 

nbw

Senior Member
OK, gave it a go. They all work. Amazing what assigning the right outputs does.

I have 3 boarded up and going at present:

Symbol TIME1 = 2200 : Symbol DWELL1 = 1900 ' pin 4
Symbol TIME2 = 3000 : Symbol DWELL2 = 1500 ' pin 3
Symbol TIME3 = 1900 : Symbol DWELL3 = 200 ' this one not used
Symbol TIME4 = 1600 : Symbol DWELL4 = 1200 ' pin 1

They snap on a bit sharply once they've been off, I think that might be partially caused by the LEDs with the AC/DC configuration in them. I have an 8V setting on my portable power supply - the LEDs are too dim with that, but it does show that reduced voltage seems to make the fade-on a bit gentler, and less top-end full-on at the top of the fade (compared to 12V). Plus, I think instead of 330 / 390 R at the transistor bases, about 2K7 means a smoother run-in too.
The TIME2 one seems a bit slow
The TIME4 one is a bit fast, so 2000 TIME is about the minimum
The TIME1 one is about right ... so I'll try 2000, 2400, 2800 with slightly different DWELL times (these are in ms from what I can read)

I'll keep experimenting, thanks! :)
If there is some way of slowing down say the first 20, and last 20% of the fade-up, and fade-down to say twice the time it takes now, it would be ideal. I've added 6K8 resistors to the transistor-bases and that has helped.

I wondered if with the tight pulsout routines having other things going in the code would hamper it. But, I'm not using this picaxe in this project for anything else, so it's just out of curiosity.
 

techElder

Well-known member
I haven't read absolutely all the posts here, but I don't remember anything about you standardizing the LEDs or comparing each color's brightness to the others. Generally speaking, different colors will require different driving force to obtain a perceived brightness. Driving with PWM is driving all colors the same way, so you should observe differences in brightness and turn-on with different colors involved.

Now you throw in "AC/DC" LEDs (whatever that is internally, because a diode is a diode), and this might be a significant factor.
 

hippy

Technical Support
Staff member
They snap on a bit sharply once they've been off, I think that might be partially caused by the LEDs with the AC/DC configuration in them.
I did notice they appear to snap off to nothing when dimming normal LED+R but not sure if that is just perceived or real. There's nothing which would do that in the code which I can see.

Putting SERTXD's in the GeneratePwm routine may cause some flicker and slow-down of fading but should show what's going on, whether there are any odd jumps or not.

The TIME2 one seems a bit slow
The TIME4 one is a bit fast,
As noted I'm not at all convinced by the maths for setting the timing. I'm not sure if I've made some mistake in my theory or implementation. You can forgo the 'trying to calculate TINCx and DINCx from easy to use numbers' and calculate their values by hand and drop those in.

The xINCx values are what, when added every loop, will increment a 16-bit value from zero to $FFFF in the time desired.

If there is some way of slowing down say the first 20, and last 20% of the fade-up, and fade-down to say twice the time it takes now, it would be ideal.
The best way to do that would be a lookup table which converted what the 0-100% level is to a PWM value. That's what's usually done in stage lighting dimmers to ensure that the eye perceives a linear brightness change. That's usually an "S-curve" compensation which I believe does slow down the first 30% or so and speed up the last.

I wondered if with the tight pulsout routines having other things going in the code would hamper it.
If the things executed between each GeneratePwm call take too long it will lead to flicker and strobing because it's basically relying upon persistence of vision.
 

techElder

Well-known member
I did notice they appear to snap off to nothing when dimming
What you are seeing is the non-linearity of the LEDs. You would have to create a negative bias at the cathodes to create a linear adjustment down to zero percent control.
 

hippy

Technical Support
Staff member
I did notice they appear to snap off to nothing when dimming
What you are seeing is the non-linearity of the LEDs.
It could be that but I had not noticed it so much with earlier code. There is a definite perceived jump from 'extremely dim' to totally off. That may be a result of having added the dwell, with it now remaining off for a period of time. It could be that going from 'extremely dim' to off and rising immediately was masking the perception of that.

The other oddity which can sometimes be observed is that a very dim LED will appear to flicker or strobe far more than when brighter. I assume that the shorter on period creates a worse persistence of vision effect, allows the eye and brain to determine it is not always on.
 

nbw

Senior Member
I haven't read absolutely all the posts here, but I don't remember anything about you standardizing the LEDs or comparing each color's brightness to the others. Generally speaking, different colors will require different driving force to obtain a perceived brightness. Driving with PWM is driving all colors the same way, so you should observe differences in brightness and turn-on with different colors involved.

Now you throw in "AC/DC" LEDs (whatever that is internally, because a diode is a diode), and this might be a significant factor.
Might be some crossed wires (no pun) there. The fish I made with RGB LED had different colours. THe jellyfish ones (this project are all the same colours, and same types - I'm just aiming for out-of-sync fading patterns). THe LEDs I have can be powered with either AC or DC - they have inbuilt rectifiers, should you choose to use AC. I use DC, but the rectifying diodes could be having something on an effect.
What you are seeing is the non-linearity of the LEDs. You would have to create a negative bias at the cathodes to create a linear adjustment down to zero percent control.
Agree. I think I can live it, there'll be four banks of these things fading in and out so the eye won't be drawn to any particular one. Dropping the voltage for the LEDs a little (certainly down from 12V), and high-ish base resistors at their transistors will certainly help.
 

nbw

Senior Member
I'm just curious. Do you have a spec sheet on this "AC/DC" LED? :D
No sorry. There's a pic of the LED way up in this thread. I believe it's multiple strings of small white LEDs chained together to operate under 12VDC. I can see in the clear encapsulate there's some capacitors and what looks like a tiny rectifier, which would cater for people using AC and smooth out what the LEDs get, I'm guessing. If you put 12V DC in, that works just as well :)
 

hippy

Technical Support
Staff member
There's a pic of the LED way up in this thread
Seems to be a G4 12V AC LED.

eclectic beat me to it

I am guessing there's a rectifier and the capacitor is to keep the 12V across the LED sustained when it zero-crosses.

Apart from being G4 styled, giving a reasonable expectation that it is, the capacitor could be used as an impedance, acting as a resistance for mains voltage.
 

nbw

Senior Member
Seems to be a G4 12V AC LED.

eclectic beat me to it

I am guessing there's a rectifier and the capacitor is to keep the 12V across the LED sustained when it zero-crosses.

Apart from being G4 styled, giving a reasonable expectation that it is, the capacitor could be used as an impedance, acting as a resistance for mains voltage.
Yes, these are similar to the G4 ones I'm using - actually sold as AC or DC, and they can be dimmed quite well with a variation in DC voltage. Down at about 8V you can see the individual LED dies glowing dimly which is quite nice, 12V is full on too bright. About 10.5V maximum is quite bright. WIth hippy's pulsout code I estimate the brightness maps to levels of what I'd see at about 6V - 9.5V - so right off, to low-medium brightness which is quite good.
 
Top