Difference between revisions of "BeagleBoardPWM"
(Added links to pages covering some other PWM techniques) |
(Added discussion of using /dev/mem and mmap() to access timer registers) |
||
Line 1: | Line 1: | ||
− | There are three pins capable of [http://en.wikipedia.org/wiki/Pulse-width_modulation PWM (pulse-width modulation)] exposed on the C3/C4 BeagleBoard expansion header. PWM is useful for control of a number of devices, from LEDs (which can be faded smoothly with PWM) to DC motors. For robotics, this means that three hobby servos can easily be controlled by the Beagle given nothing more than a simple [[BeagleBoard_Hardware_Interfacing#Level_Shifting | level-shifting]] circuit. | + | There are three pins capable of [http://en.wikipedia.org/wiki/Pulse-width_modulation PWM (pulse-width modulation)] exposed on the C3/C4 BeagleBoard expansion header. PWM is useful for control of a number of devices, from LEDs (which can be faded smoothly with PWM) to [[ECE597 Project Sumo Robot | DC motors]]. For robotics, this means that three hobby servos can easily be controlled by the Beagle given nothing more than a simple [[BeagleBoard_Hardware_Interfacing#Level_Shifting | level-shifting]] circuit, with no CPU usage to speak of. |
Alternative approaches are possible. [http://github.com/tallakt/servodrive servodrive] is a kernel module that emits servo control PWM using straight GPIO (this page also claims that straight 1.8 V from the Beagle is sufficient to control servos). [http://thoughtshubham.blogspot.com/2010/04/pwm-generation-in-beagleboard.html This page] shows how to use threading and GPIO to accomplish PWM in userspace. The rest of this page focuses on use of the OMAP's hardware PWM capabilities. | Alternative approaches are possible. [http://github.com/tallakt/servodrive servodrive] is a kernel module that emits servo control PWM using straight GPIO (this page also claims that straight 1.8 V from the Beagle is sufficient to control servos). [http://thoughtshubham.blogspot.com/2010/04/pwm-generation-in-beagleboard.html This page] shows how to use threading and GPIO to accomplish PWM in userspace. The rest of this page focuses on use of the OMAP's hardware PWM capabilities. | ||
Line 24: | Line 24: | ||
{|border=1 | {|border=1 | ||
− | |+ BeagleBoard C4 GP Timer Base Addresses | + | |+ BeagleBoard C3/C4 GP Timer Base Addresses |
|- | |- | ||
| '''Timer''' || '''Base address''' || '''Expansion header pin''' | | '''Timer''' || '''Base address''' || '''Expansion header pin''' | ||
Line 38: | Line 38: | ||
{|border=1 | {|border=1 | ||
− | |+ BeagleBoard C4 GP Timer Registers | + | |+ BeagleBoard C3/C4 GP Timer Registers |
|- | |- | ||
| '''Name''' || '''TRM section''' || '''Offset''' | | '''Name''' || '''TRM section''' || '''Offset''' | ||
Line 58: | Line 58: | ||
<!--The relevant bits of the control register TCLR are as follows:--> | <!--The relevant bits of the control register TCLR are as follows:--> | ||
− | <!--TODO: show bits of TCLR, | + | <!--TODO: show bits of TCLR --> |
+ | |||
+ | == Interacting with Timer Registers in Linux == | ||
+ | |||
+ | The ideal way to interact with these registers would be through a Linux kernel module, which could provide a clean and safe interface to manipulate them. Unfortunately, such a thing does not yet exist. However, Linux does provide a way to get at these registers from userspace: <code>/dev/mem</code>. This file contains a live view of the contents of physical memory --- meaning that reading and writing to the physical address of a timer register as an offset in <code>/dev/mem</code> reflects the actual thing. | ||
+ | |||
+ | There is one complication, though, in that reads and writes to the OMAP registers cannot be done with byte-oriented I/O (such as the <code>write()</code> system call); however, this can be worked around by using the <code>mmap()</code> syscall. This means that a pointer to a register can be cast to <code>volatile uint32_t*</code> and function correctly. | ||
== OMAP3530 PWM library == | == OMAP3530 PWM library == |
Revision as of 23:15, 25 July 2010
There are three pins capable of PWM (pulse-width modulation) exposed on the C3/C4 BeagleBoard expansion header. PWM is useful for control of a number of devices, from LEDs (which can be faded smoothly with PWM) to DC motors. For robotics, this means that three hobby servos can easily be controlled by the Beagle given nothing more than a simple level-shifting circuit, with no CPU usage to speak of.
Alternative approaches are possible. servodrive is a kernel module that emits servo control PWM using straight GPIO (this page also claims that straight 1.8 V from the Beagle is sufficient to control servos). This page shows how to use threading and GPIO to accomplish PWM in userspace. The rest of this page focuses on use of the OMAP's hardware PWM capabilities.
Contents
OMAP Mux Configuration
Because the PWM pins are not set as such by default, the OMAP's mux must be configured to expose them before they can be used. See BeagleBoardPinMux for more details on this procedure. The short version is to add the following lines to the definition of board_mux[]
in arch/arm/mach-omap2/board-omap3beagle.c (this has been tested with the 2.6.33 OMAP branch of the kernel).
OMAP3_MUX(DSS_DATA15, OMAP_MUX_MODE2|OMAP_PIN_OUTPUT), /* GPT9_PWMEVT, ball AB26, ex pin 4 */ OMAP3_MUX(UART2_TX, OMAP_MUX_MODE2|OMAP_PIN_OUTPUT), /* GPT11_PWMEVT, ball AA25, ex pin 6 */ OMAP3_MUX(UART2_RTS, OMAP_MUX_MODE2|OMAP_PIN_OUTPUT), /* GTP10_PWMEVT, ball AB25, ex pin 10 */
Obviously these lines should precede the line terminating the array.
Activating PWM via Timer Registers
PWM output on the BeagleBoard is done via the OMAP processor's general-purpose timer mechanism, described in the OMAP35x TRM in section 16.2.4 (page 2546). To briefly summarize this (and simplify significantly), the general-purpose timer is a continuously-incrementing counter that can be configured to toggle the PWM output high when a certain value is reached, and low when it overflows. By adjusting the first number the duty cycle can be set. Setting the value the counter starts at can be used to set the frequency of the PWM.
Each GP timer has a 4K block for memory-mapped registers (see TRM Table 16-12). The start addresses of these blocks for the timers on the BeagleBoard are listed below.
Timer | Base address | Expansion header pin |
GPTIMER9 | 0x4904 0000 | 4 |
GPTIMER10 | 0x4808 6000 | 6 |
GPTIMER11 | 0x4808 8000 | 10 |
These are the registers relevant to our purpose:
Name | TRM section | Offset | Description |
TCLR | 16.3.2.6 (p. 2568) | 0x024 | Control register. |
TCRR | 16.3.2.7 (p. 2570) | 0x028 | The counter. Increments with the clock when the timer is running. |
TLDR | 16.3.2.8 (p. 2571) | 0x02c | Timer load register. Holds the value assumed by TCRR when it overflows. |
TMAR | 16.3.2.11 (p. 2575) | 0x038 | Value to be compared with the counter. |
Interacting with Timer Registers in Linux
The ideal way to interact with these registers would be through a Linux kernel module, which could provide a clean and safe interface to manipulate them. Unfortunately, such a thing does not yet exist. However, Linux does provide a way to get at these registers from userspace: /dev/mem
. This file contains a live view of the contents of physical memory --- meaning that reading and writing to the physical address of a timer register as an offset in /dev/mem
reflects the actual thing.
There is one complication, though, in that reads and writes to the OMAP registers cannot be done with byte-oriented I/O (such as the write()
system call); however, this can be worked around by using the mmap()
syscall. This means that a pointer to a register can be cast to volatile uint32_t*
and function correctly.
OMAP3530 PWM library
There is a small library available to simplify manipulating the timer registers via /dev/mem
. It is made available under the LGPL 2.1.
Download: omap3530-pwm-1.0.tar.gz
Example usage
{{#source: c|
- include <glib.h>
- include <errno.h>
- include "omap3530-pwm.h"
int main(int argc, char **argv) {
int mem_fd = pwm_open_devmem(); if (mem_fd == -1) { g_error("Unable to open /dev/mem, are you root?: %s", g_strerror(errno)); } // Set instances 10 and 11 to use the 13 Mhz clock pwm_config_clock(mem_fd, TRUE, TRUE); guint8 *gpt10 = pwm_mmap_instance(mem_fd, 10); // Get the resolution for 20 kHz PWM guint32 resolution = pwm_calc_resolution(20000, PWM_FREQUENCY_13MHZ); // Set to half duty cycle pwm_config_timer(gpt10, resolution, 0.5); pwm_munmap_instance(gpt10); pwm_close_devmem(mem_fd);
} }}
Links and References
- RE: PWM Driver for Overo?, Scott Ellis
man 2 mmap
man 2 open
- OMAP 3530 Technical Reference Manaul
- BeagleBoard System Reference Manaul
- Presentation on I2C, PWM and Hardware interfacing with the BeagleBoard
- Project: Sumo Robot -- uses PWM to control drive motors via L298 motor drivers.