Home Gumstix PWM

Gumstix PWM

Controlling PWM outputs from an OMAP3 based Linux systems.

 

Overview

There are 12 general purpose timers available on the OMAP3, four of them can be used to generate PWM outputs.

GPT8 - GPT11 are the PWM capable timers.

They are exposed on the Overo expansion card 40 pin headers as

  • Pin 27 - GPIO146_PWM11
  • Pin 28 - GPIO145_PWM10
  • Pin 29 - GPIO147_PWM8
  • Pin 30 - GPIO144_PWM9

 

PWM Kernel Module

There is a PWM kernel module available on Github here - omap3-pwm.

The module provides a character device interface that allows standard file operations to control all four of the PWM timers.

root@overo# insmod pwm.ko timers=10
root@overo# cat /dev/pwm10
0
root@overo# echo 80 > /dev/pwm10
root@overo# cat /dev/pwm10
80

And you could measure Pin28 to check the value. Send a zero for the duty cycle or unload the module to turn off the PWM signal.

The driver also supports a 'servo' mode of operation with a 50 Hz signal and a duty cycle range of 1-2 ms.

See the project README for details on building and using.

 

Command Line - /dev/mem

NOTE: The devmem2 procedure won't work with kernel 2.6.39 or greater. The source clocks for PWM are now disabled by default and have to be explicitly enabled before the timer registers can be accessed. The devmem2 steps below do not do this. Refer to the omap3-pwm driver and look for pwm_enable_clock() in pwm.c to see how to enable the clocks in a driver.

 

Any userspace program that can access /dev/mem can operate a PWM timer. Since devmem2 is part of all the standard Gumstix images, it's a convenient program to use.

Here's how to start PWM10 from the command line with a frequency of 1kHz and a duty cycle of 50%.

root@overo# devmem2 0x48086024 w 0
root@overo# devmem2 0x48002176 h 0x0002
root@overo# devmem2 0x4808602C w 0xFFFFFFE0
root@overo# devmem2 0x48086038 w 0xFFFFFFEF
root@overo# devmem2 0x48086028 w 0xFFFFFFE0
root@overo# devmem2 0x48086024 w 0x01843

The signal can then be measured on Pin28 on the expansion board header using Pin15 as ground.

 

 

Breakdown

Here is what those devmem2 calls above did.

 

1. root@overo# devmem2 0x48086024 w 0

Disables the timer completely before any configuration changes.

 

2. root@overo# devmem2 0x48002176 h 0x0002

Change the PAD multiplexing to allow PWM10 off this pin instead of its current configuration as GPIO_145. (Note: You can change the mux configuration for any pad/pin on a running system using this same procedure.)

 

3. root@overo# devmem2 0x4808602C w 0xFFFFFFE0

Set the frequency of the PWM signal to 1024 Hz.

 

4. root@overo# devmem2 0x48086038 w 0xFFFFFFEF

This sets the duty cycle of the PWM signal to 50%, ON time one half of cycle time.

 

5. root@overo# devmem2 0x48086028 w 0xFFFFFFE0

This primes the timer counter for the first cycle. Primed with the same value as the frequency.

 

6. root@overo# devmem2 0x48086024 w 0x01843

This starts the timer in PWM mode.

 

Further Breakdown

1. root@overo# devmem2 0x48086024 w 0

I'll skip step 1 above as it will be obvious later.

You'll need the OMAP3 Technical Reference Manual (TRM) to follow the rest of this.

 

2. root@overo# devmem2 0x48002176 h 0x0002

The OMAP3 allows multiple functional uses for many of the pads coming out of the chip. Gumstix has brought some of the pads out as pins on the expansion header. The functionality of a pad is controlled by its Pad Configuration Register. Each register controls two pads. Each pad can have up to 7 different modes, but most have only a few. The address 0x48002176 comes from the TRM Table 7.5 (page 838 if you are looking at Revision D). The register names in the table refer to the Mode 0 functionality of the pad controlled by the lower 16 bits of the register. So for instance,

Register Name Physical Address Mode 0 Mode 1 Mode 2 Mode 4
CONTROL_PADCONF_UART2_CTS[15:0] 0x48002174 uart2_cts mcbsp3_dx gpt9_pwm_evt gpio_144
CONTROL_PADCONF_UART2_CTS[31:16] 0x48002174 uart2_rts mcbsp3_dr gpt10_pwm_evt gpio_145
CONTROL_PADCONF_UART2_TX[15:0] 0x48002178 uart2_tx mcbsp3_clkx gpt11_pwm_evt gpio_146
CONTROL_PADCONF_UART2_TX[31:16] 0x48002178 uart2_rx mcbsp3_fsx gpt8_pwm_evt gpio_147

 

The PWM timers are a Mode 2 configuration. The reason for 0x48002176 is because gpt10_pwm_evt is the upper 16 bits of physical address 0x48002174 or 0x48002174 + 2. The PADCONF registers are 32 bits, but they can be accessed at 16 bit offsets.

0x0002 is the value being written to the register. The fields of the PADCONF register are defined in Section 7.6.3 of the TRM, Table 7-86. The value 0x0002 sets the muxmode to 2 and all other fields to zero or disabled.

 

3. root@overo# devmem2 0x4808602C w 0xFFFFFFE0

Chapter 16 is all about timers. Section 16.2.4.6 talks about Pulse-Width Modulation. Section 16.3 has the register offsets and descriptions. Each timer has 19 registers that can be configured.

To access a timer register, you first find the base address for the timer and then add the offset for the particular register you are interested in. The base addresses for each timer can be found in Table 16-12. Here are the PWM capable timer base addresses. Each timer has 4K bytes of memory assigned for registers.

Timer Base Address
GPTIMER8 0x4903 E000
GPTIMER9 0x4904 0000
GPTIMER10 0x4808 6000
GPTIMER11 0x4808 8000

 

Only 4 of the registers need modification to control PWM functionality, here are the names and offsets.

Register Offset Description
TCLR 0x024 Controls optional features specific to the timer functionality
TCRR 0x028 Holds the value of the internal counter
TLDR 0x02C Holds the timer load values
TMAR 0x038 Holds the value to be compared with the counter value

 

So the address 0x4808602C refers to the TLDR register for Timer 10.

 

The TLDR register controls the frequency of the PWM signal (Section 16.2.4.6). The formula for the frequency calculation is

FREQUENCY = GPTi_FCLK / ((0xFFFF FFFF - TLDR) + 1)

where GPTi_FCLK defaults to

Timer FCLK Notes
GPTIMER8 13 MHz
GPTIMER9 13 MHz
GPTIMER10 32 kHz Can be changed to 13 MHz
GPTIMER11 32 kHz Can be changed to 13 MHz

For this PWM10 example, a TLDR value of 0xFFFF FFE0 results in a frequency of

FREQUENCY = 32 kHz / ((0xFFFF FFFF - 0xFFFF FFFE0) + 1)
FREQUENCY = 32 kHz / (0x1F + 1)
FREQUENCY = 32 kHz / 0x20
FREQUENCY = 1 kHz

The TLDR value also determines the number of settings or adjustment granularity you have for duty-cycle adjustments. You can calculate it this way

NUM_SETTINGS = 0xFFFF FFFE - TLDR

In this example there will be

NUM_SETTINGS = 0xFFFF FFFE - 0xFFFF FFE0 = 0x1E = 30

 

4. root@overo# devmem2 0x48086038 w 0xFFFFFFEF

Address 0x48086038 is for the TMAR register for Timer 10.

 

The TMAR register controls the duty-cycle of the PWM signal. It's the on-time for the PWM pulse.

 

TMAR values are in the range (TLDR + 1) to 0xFFFF FFFE or as calculated above there are NUM_SETTINGS possible values for TMAR.

Calculate an appropriate TMAR for a particular duty-cycle as follows

TMAR = TLDR + (NUM_SETTINGS * DUTY_CYCLE_PERCENTAGE)

In this example to get a 50% duty-cycle the calculation is

TMAR = 0xFFFF FFE0 + (30 * (50 / 100))
TMAR = 0xFFFF FFE0 + 15
TMAR = 0xFFFF FFEF

To get a 10% duty-cycle it would be

TMAR = 0xFFFF FFE0 + (30 * (10 / 100))
TMAR = 0xFFFF FFE0 + 3
TMAR = 0xFFFF FFE3

You do need to keep TMAR in the range TLDR + 1 to 0xFFFF FFFE.
TODO: Check if TMAR = TLDR, i.e. 0% duty-cycle is OK?
This is not real important since turning off the PWM output accomplishes the same thing.

5. root@overo# devmem2 0x48086028 w 0xFFFFFFE0

Address 0x48086028 is the TCRR register for Timer 10. This is the timer counter register which counts up and gets reset to the TLDR value during PWM operation whenever 0xFFFF FFFF or overflow is reached.

The TMAR register is compared to the TCRR to know when to trigger a change in the output. We prime it here by giving it an initial value of TLDR but any value from TLDR to 0xFFFF FFFE would work.

Bad explanation, I'll get back to this...

6. root@overo# devmem2 0x48086024 w 0x01843

Address 0x48086028 is the TCLR register for Timer 10. Unlike the other PWM timer registers which took values interpreted as numbers, the TLDR register is interpreted as a series of bit fields. From table 16-21 of the TRM:

 

TCLR Register Description

Reserved 31-15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
GPO_CFG CAPT_MODE PT TRG TCM SCPWM CE PRE PTV AR ST

 

 

Bits Field Name Description
14 GPO_CFG PWM output/event detection input pin direction control:
0x0 Configure the pin as output (needed for PWM mode)
0x1 Configure the pin as input (needed for capture mode)
13 CAPT_MODE Capture mode select bit (first/second)
0x0 Capture the first enabled capture event in TCAR1
0x1 Capture the second enabled capture event in TCAR2
12 PT Pulse or toggle select bit
0x0 Pulse modulation
0x1 Toggle modulation
11:10 TRG Trigger output mode
0x0 No trigger
0x1 Overflow trigger
0x2 Overflow and match trigger
0x3 Reserved
9:8 TCM Transition capture mode
0x0 No capture
0x1 Capture on rising edges
0x2 Capture on falling edges
0x3 Capture on both edges
7 SCPWM PWM output pin default when stopped
0x0 PWM output: 0
0x1 PWM output: 1
6 CE Compare enable
0x0 Compare disabled
0x1 Compare enabled
5 PRE Prescaler enable
0x0 Prescaler disabled
0x1 Prescaler enabled
4:2 PTV Trigger output mode
0x0 Timer counter is prescaled with the value 2^(PTV+1)
1 AR Autoreload mode
0x0 One-shot mode overflow
0x1 Autoreload mode overflow
0 ST Start/stop timer control
0x0 Stop the timer
0x1 Start the timer

 

So the value 0x1843 written to the TCLR register translates to

GPO_CFG 0x0 Configure pin for output
CAPT_MODE 0x0 Capture mode disabled
PT 0x1 Toggle modulation mode
TRG 0x2 Trigger on both TLDR overflow and TMAR match
TCM 0x0 Capture disabled
SCPWM 0x0 Pin value is low when TRG=0x0 or when stopped
CE 0x0 Compare disabled
PRE 0x0 Prescaler disabled
PTV 0x0 PTV value, disabled because PRE=0x0
AR 0x1 Autoreload mode on overflow (continuous output vs one-shot)
ST 0x1 Start timer

 

To be continued...

Additional References

Curtis Olson has an Overo PWM article Gumstix Overo, RC Servos and PWM Signal Generation