Qball's Weblog

PWM on Udoo using the i.MX6

Tags Grind My Gears  DIY 

I have been very bad, not writing blog post. I guess that will be habit I am never going to break.

This blog post is a quick write down what I did to get pwm output working on the Udoo.

Out of the box

Out of the box PWM the only usable PWM is the one on the Atmel, but I did not want to use that, it feels wrong (also it is arduino based, so getting a decent PWM requires hacking, etc.). The Freescale has it, so why not use that See pinout. So looking around on the install I did see a nice /sys/class/pwm/ directory with the 4 pwm generators, however this was completely non working for two reasons:

  1. The pinmux is wrong
  2. The entries do not seem to setup the right registers.

So looking around, I found this post basically the only one I could find that talked about setting up PWM on the Udoo. But it gave a nice starting point. Lets first try to compile that imx-utils repository. This failed, it is setup for cross-compilation, and tries to link against framebuffer device. ( I only want the devregs tool.) A quick hack to the make file fixed this, the whole makefile now looks like:

    all: devregs

    devregs: devregs.cpp

The second problem when trying to get the tool to work is the processor detection, under ArchLinux (running 3.17 kernel) the right Revision is not exposed. A quick hack of the code to always load the imx6 version made things work. I might post this version later, mail me if interested.

So lets start setting up the PWM, first thing we need to do is setup the right pinmux. BEWARE THAT THIS PIN SHOULD NOT BE AN OUTPUT ON THE ATMEL, IF YOU DO THIS WILL DESTROY THE BOARD

   devregs IOMUXC_SW_MUX_CTL_PAD_SD4_DAT2 2

This sets GPIO42 to PWM4 output. Check the pinout. Then I followed the steps from the forum post, setting up the PWM:

devregs PWM4_PWMCR 0x0003fff0   # clocksource = 32kHz, div by 4096 = 8 Hz
devregs PWM4_PWMPR 6            # set period (6 + 2 = 8 cycles)
devregs PWM4_PWMSAR 1           # set duty cycle to 1/8
devregs PWM4_PWMCR 0x0003fff1   # enable PWM output

While I got some output from the PWM, it looked like utter sh??. It has a lot of high frequency components, and the output looks fully random. What could this be? (Sorry I forgot to take a photo of the output). Looking at the freescale manual IMX6DQRM.pdf it mentions several settings related to power? management: STOPEN stop mode enabled, DOZEN doze mode, WAITEN wait mode. By default if we hit any of these modes the PWM is stopped. I am not sure what Linux is doing, but lets be on the safe side and disable these.

devregs PWM4_PWMCR 0x0383fff0 

And this instantly cleared up the PWM signal:

Plot

It is not perfect, there is still a lot of high frequency scruff on the signal, but it is clean enough. Now lets see if we can get a bit higher frequency going. Looking again at the datasheet, it looks like we can switch to highfreq clock,

devregs PWM4_PWMCR 0x0382fff0 

And indeed the frequency goes up, now lets play with the prescaler a bit:

devregs PWM4_PWMCR 0x03824000 

This gave me a nice stable 64kHz clock. (half from what I would expect based on the documentation, but that is an investigation for another time).

So to tie things together (I also inversed the PWM because my LED driver also inverts):

## Set pinmux
devregs IOMUXC_SW_MUX_CTL_PAD_SD4_DAT2 2 
## Set high freq clock, inverse polairty, disable power  down modes etc.
devregs PWM4_PWMCR         0x03864000
## Set period to 255
devregs PWM4_PWMPR         0xfe
## Lights off
devregs PWM4_PWMSAR        0x00 
## Enable clock
devregs PWM4_PWMCR         0x03864001

Now if I want to change the Light strength I call:

devregs PWM4_PWMSAR $HEX_LIGHT

Changing the duty cycle.

There is one small thing we still need to tweak, the PWM has a 4 element queue before the ‘SAR’ (duty cycle) register, we need to make sure this fifo is not full before we insert an element. We can do this by checking the Status Register: (PWM4_PWMSR4). The lower 3 bits indicate the remaining space. Luckely, I never manage to hit this, so will fix this at a later stage when making a dedicated PWM application instead of using devregs. Maybe even a kernel driver?

This now gives me a very nice controllable PWM. I need to play a bit with improving ‘fading’ effects. Currently it takes 9 seconds to go from low to high.

Some scripts:

#!/usr/bin/env bash

declare -i LIGHT=$@

if [ -z "$LIGHT" ]
then
        LIGHT=0
fi

if [ $LIGHT -gt 255 ]
then
        LIGHT=255
fi 


HEX_LIGHT=$(printf "%X" $LIGHT)

devregs PWM4_PWMSAR $HEX_LIGHT
#!/usr/bin/env bash

STRENGTH=$(devregs PWM4_PWMSAR | awk -F= '{print strtonum($2)}')
echo $STRENGTH

and fading:

#!/usr/bin/env bash

declare -i LIGHT=$@

if [ -z "$LIGHT" ]
then
        LIGHT=0
fi

if [ $LIGHT -gt 255 ]
then
        LIGHT=255
fi 

START=$(get_light.sh)

echo Going from $START to $LIGHT
if [ $START -gt $LIGHT ]
then

for a in `seq $START -1 $LIGHT`
do
        HEX_LIGHT=$(printf "%X" $a)
        devregs PWM4_PWMSAR $HEX_LIGHT > /dev/null
done

else

for a in `seq $START 1 $LIGHT`
do
        HEX_LIGHT=$(printf "%X" $a)
        devregs PWM4_PWMSAR $HEX_LIGHT > /dev/null

done

fi
comments powered by Disqus