Controlling the output power of a pin (dimming external LED)

The official pyboard running MicroPython.
This is the reference design and main target board for MicroPython.
You can buy one at the store.
Target audience: Users with a pyboard.
drexxler
Posts: 7
Joined: Tue Aug 12, 2014 8:38 pm

Controlling the output power of a pin (dimming external LED)

Post by drexxler » Tue Aug 12, 2014 8:42 pm

So I'm new to microcontroller development as a whole, but after seeing this nice introductory board on kickstarter I decided to give it a try since I love python so much.

I'm trying to create a LED light show display using this board. I've gotten my hands on a few nice RGB LED lights, and I was able to write a little program to create a ribbon out of the 6 possible colors you can make with RGB. However, it's not quite good enough for me. I'm trying to create smooth blending colors via dimming each color channel. So far, all I could do is turn each color on/off, is there a way to control the output voltage for each pin (I assume that's the easiest way to dim an LED light) or will I have to use something extra to dim the LEDs?

Thanks for the help!

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Controlling the output power of a pin (dimming external

Post by dhylands » Tue Aug 12, 2014 8:59 pm

The normal way that control brightness of an LED is to use PWM.

PWM isn't properly supported on the pyboard, but this thread has a way to do it:
http://forum.micropython.org/viewtopic. ... t=210#p967
Also see here: http://forum.micropython.org/viewtopic. ... t=PWM#p982

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Controlling the output power of a pin (dimming external

Post by dhylands » Tue Aug 12, 2014 10:29 pm

And I thought I should mention, that the STM32F405 datasheet says that the max current for a single pin is 25 mA.

So if you're going to be driving more than a single LED, you should really consider using a transistor or a MOSFET to switch the LEDs on and off.

drexxler
Posts: 7
Joined: Tue Aug 12, 2014 8:38 pm

Re: Controlling the output power of a pin (dimming external

Post by drexxler » Tue Aug 12, 2014 11:03 pm

Thanks a lot!

So I added the pwm module to my controller, and the contents are (I'm assuming this is the latest version)

Code: Select all

import pyb, stm

#as pwm is configured 0-3.3v is 0-10000
# used to truncate to 0-255;
# change if you need finer resolution
RESOLUTION = 255

#for each pin name
# list of possible pwm available alt fn's
# and corresponding timer/channel
__PIN_TIMERS  = { \
  'A0':   { 0b0010: ( 5,1) }, \
  'A1':   { 0b0001: ( 2,2), 0b0010: ( 5,2) }, \
  'A2':   { 0b0001: ( 2,3), 0b0010: ( 5,3), 0b0011: ( 9,1) }, \
  'A3':   { 0b0001: ( 2,4), 0b0010: ( 5,4), 0b0011: ( 9,2) }, \
  'A6':   { 0b0010: ( 3,1), 0b1001: (13,1) }, \
  'A7':   { 0b0010: ( 3,2), 0b1001: (14,1) }, \
  'A8':   { 0b0001: ( 1,1) }, \
  'A9':   { 0b0001: ( 1,2) }, \
  'A10':  { 0b0001: ( 1,3) }, \
  'A11':  { 0b0001: ( 1,4) }, \
  'B0':   { 0b0010: ( 3,3) }, \
  'B1':   { 0b0010: ( 3,4) }, \
  'B3':   { 0b0001: ( 2,2) }, \
  'B4':   { 0b0010: ( 3,1) }, \
  'B5':   { 0b0010: ( 3,2) }, \
  'B6':   { 0b0010: ( 4,1) }, \
  'B7':   { 0b0010: ( 4,2) }, \
  'B8':   { 0b0010: ( 4,3), 0b0011: (10,1) }, \
  'B9':   { 0b0010: ( 4,4), 0b0011: (11,1) }, \
  'B10':  { 0b0001: ( 2,3) }, \
  'B11':  { 0b0001: ( 2,4) }, \
  'B14':  { 0b1001: (12,1) }, \
  'B15':  { 0b1001: (12,2) }, \
  'C6':   { 0b0010: ( 3,1), 0b0011: ( 8,1) }, \
  'C7':   { 0b0010: ( 3,2), 0b0011: ( 8,2) }, \
  'C8':   { 0b0010: ( 3,3), 0b0011: ( 8,3) }, \
  'C9':   { 0b0010: ( 3,4), 0b0011: ( 8,4) }, \
  'D12':  { 0b0010: ( 4,1) }, \
  'D13':  { 0b0010: ( 4,2) }, \
  'D14':  { 0b0010: ( 4,3) }, \
  'D15':  { 0b0010: ( 4,4) }, \
  'E5':   { 0b0011: ( 9,1) }, \
  'E6':   { 0b0011: ( 9,2) }, \
  'E9':   { 0b0001: ( 1,1) }, \
  'E11':  { 0b0001: ( 1,2) }, \
  'E13':  { 0b0001: ( 1,3) }, \
  'E14':  { 0b0001: ( 1,4) }, \
  'F6':   { 0b0011: (10,1) }, \
  'F7':   { 0b0011: (11,1) }, \
  'F8':   { 0b1001: (13,1) }, \
  'F9':   { 0b1001: (14,1) }, \
  'H9':   { 0b1001: (12,2) }, \
  'H10':  { 0b0010: ( 5,1) }, \
  'H11':  { 0b0010: ( 5,2) }, \
  'H12':  { 0b0010: ( 5,3) }, \
  'H6':   { 0b1001: (12,1) }, \
  'I0':   { 0b0010: ( 5,4) }, \
  'I2':   { 0b0011: ( 8,4) } \
  }

class PWM:
  """dirty Pulse Width Modulation class"""

  def __init__(self, pin, afmode=None):
    if not pin.name() in __PIN_TIMERS:
      raise ValueError('Pin does not support pwm')
    if afmode == None:
      #select the first timer in the dictionary
      pwm_afmode = list(__PIN_TIMERS[pin.name()].keys())[0]
    else:
      pwm_afmode = afmode
    if not pwm_afmode in __PIN_TIMERS[pin.name()]:
      raise ValueError('Pin does not support afmode {0}'.format(bin(afmode)))
    self.pin      = pin
    self.gpio     = eval('stm.GPIO{0}'.format(pin.name()[0]))
    pwm_pin       = int(self.pin.name()[1:])
    pwm_timer_no  = __PIN_TIMERS[self.pin.name()][pwm_afmode][0]
    pwm_timer_ch  = __PIN_TIMERS[self.pin.name()][pwm_afmode][1]

    #setup gpio
    #set alternate fn mode, before setting pin to alternate function
    if pwm_pin < 7:
      stm.mem32[self.gpio + stm.GPIO_AFR0]   |= pwm_afmode << (pwm_pin * 4)
    elif pwm_pin == 7:
      #avoid potential overflow on 32nd bit being set
      stm.mem8[self.gpio + stm.GPIO_AFR0 + 3]   |= pwm_afmode << 4
    elif pwm_pin < 15:
      stm.mem32[self.gpio + stm.GPIO_AFR1]   |= pwm_afmode << ((pwm_pin - 8) * 4)
    elif pwm_pin == 15:
      #avoid potential overflow on 32nd bit being set
      stm.mem8[self.gpio + stm.GPIO_AFR1 + 3]   |= pwm_afmode << 4

    if pwm_pin < 15:
      stm.mem32[self.gpio + stm.GPIO_MODER]       |= 0b10 << (pwm_pin * 2)
      stm.mem32[self.gpio + stm.GPIO_OSPEEDR]     |= 0b10 << (pwm_pin * 2)
    elif pwm_pin == 15:
      stm.mem8[self.gpio + stm.GPIO_MODER + 3]    |= 0b10 << 6
      stm.mem8[self.gpio + stm.GPIO_OSPEEDR + 3]  |= 0b10 << 6

    #setup timer
    #TODO: work out how to init timer directly, so can use reserved ones TIM3
    pyb.Timer(pwm_timer_no).init(freq=5)    #use pyb module to kickstart the timer
    pwm_timer                               =  eval('stm.TIM{0}'.format(pwm_timer_no))
    self.__pwm_duty                         =  pwm_timer + eval('stm.TIM_CCR{0}'.format(pwm_timer_ch))
    stm.mem32[self.__pwm_duty]              =  0       #make sure duty is 0
    stm.mem32[pwm_timer + stm.TIM_ARR]      =  9999    #replicate damiens's prescalar, period
    stm.mem32[pwm_timer + stm.TIM_PSC]      =  83      #gives 10,000 steps between 0 and 3.3v
    stm.mem32[pwm_timer + stm.TIM_DIER]     |= 0b1
    stm.mem32[pwm_timer + stm.TIM_SR]       |= 0b11110 #sets all channel interupt flags
    if pwm_timer_ch <= 2:
      stm.mem32[pwm_timer + stm.TIM_CCMR1]  |= 0b01101000 << ((pwm_timer_ch - 1) * 8)
    else:
      stm.mem32[pwm_timer + stm.TIM_CCMR2]  |= 0b01101000 << ((pwm_timer_ch - 3) * 8)
    stm.mem32[pwm_timer + stm.TIM_CCER]     |= 0b1 << ((pwm_timer_ch -1) * 4)
    stm.mem32[pwm_timer + stm.TIM_DMAR]     |= 0b1

  def duty(self, value=None):
    if value == None:
      return int( stm.mem32[self.__pwm_duty] / 10000 * RESOLUTION )
    else:
      if value > RESOLUTION: value = RESOLUTION
      if value < 0:   value = 0
      stm.mem32[self.__pwm_duty] = int(value / RESOLUTION * 10000)

I then moved my LED lights to be X1, X2, and X3 [RGB] (as apparently not all pins work with PWM). However, when I open up REPL and perform

Code: Select all

b = pwm.PWM(pyb.Pin.board.X3)
b.duty(255)
nothing happens. No matter what I set the value to, I can't get any response. I get the same result no matter what color I test. Any ideas what's going on?

drexxler
Posts: 7
Joined: Tue Aug 12, 2014 8:38 pm

Re: Controlling the output power of a pin (dimming external

Post by drexxler » Wed Aug 13, 2014 1:31 am

Fixed it!

Seemed like I needed to still put the pyb.Pin.OUT_PP as the second argument to PWM. Awesome!

drexxler
Posts: 7
Joined: Tue Aug 12, 2014 8:38 pm

Re: Controlling the output power of a pin (dimming external

Post by drexxler » Wed Aug 13, 2014 2:06 am

Maybe I haven't fixed it. I'm curious now if this is a problem due to using the OUT_PP function, or if it's a problem with the LED/PWM setup.

My problem is, when a color channel is set at anything less than 255, it's not a solid light, it actually has a flicker to it. When it's sitting still, it's unnoticeable; but if you start waving the LED around, you can see that the flicker gets faster and faster as the intensity increases until it hits 255, at which point it's a solid beam. Normally this wouldn't bother me, but the point of my application is to be able to generate geometric patterns using the lights. It just doesn't look right when you adjust custom colors like [255, 100, 150], simply because the green and blue channels are flickering while the red sits at a solid beam. It looks really bad as a ribbon.

Any idea what could be causing this?

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Controlling the output power of a pin (dimming external

Post by dhylands » Wed Aug 13, 2014 2:44 am

Try changing freq=5 to a higher number (perhaps 100)

drexxler
Posts: 7
Joined: Tue Aug 12, 2014 8:38 pm

Re: Controlling the output power of a pin (dimming external

Post by drexxler » Wed Aug 13, 2014 3:12 am

I assume you mean the line

Code: Select all

pyb.Timer(pwm_timer_no).init(freq=5)
in the pwm module?

At 100, it doesn't work, the highest value that appears to work is 80, anything above and the LED doesn't turn on. I also don't notice any changes once I up the frequency. The lights are still flickering at the same rate (as far as I can tell).

drexxler
Posts: 7
Joined: Tue Aug 12, 2014 8:38 pm

Re: Controlling the output power of a pin (dimming external

Post by drexxler » Wed Aug 13, 2014 3:19 am

Correct me if I'm wrong (as I know next to nothing about hardware programming, LED programming, and PWM) but PWM stands for Pulse Width Modulation, and so I'm assuming it's dimming the LED by sending gradually slower and slower pulses of electricity into the pin. If this is the case, wouldn't that be the cause of the flickering effect I'm noticing while waving the LED around? I could be completely wrong, it's just me speculating and trying to problem solve.

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Controlling the output power of a pin (dimming external

Post by dhylands » Wed Aug 13, 2014 3:45 am

There are 2 things that go together, frequency and duty cycle.

If you send a 50% duty cycle at 1000 cycles per second or 10 cycles per second you'll get about 50% brightness.

As you get slower you can see the frequency visibly.

Post Reply