Skip to content

Instantly share code, notes, and snippets.

@masquaremo
Created January 30, 2013 12:20
Show Gist options
  • Save masquaremo/4672925 to your computer and use it in GitHub Desktop.
Save masquaremo/4672925 to your computer and use it in GitHub Desktop.
Raspberry PiのPWM制御サンプル
//
// BCM2835 pwm lib
//
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <assert.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define BCM2708_PERI_BASE 0x20000000
#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO controller */
#define PWM_BASE (BCM2708_PERI_BASE + 0x20C000) /* PWM controller */
#define CLOCK_BASE (BCM2708_PERI_BASE + 0x101000)
// PWM controller offset
#define PWM_CONTROL 0
#define PWM_STATUS 1
#define PWM0_RANGE 4
#define PWM0_DATA 5
#define PWM1_RANGE 8
#define PWM1_DATA 9
// GPIO clock reg offset
#define PWMCLK_CNTL 40
#define PWMCLK_DIV 41
// BCM Magic
#define BCM_PASSWORD 0x5A000000
#define PWM1_MS_MODE 0x8000 // Run in MS mode
#define PWM1_USEFIFO 0x2000 // Data from FIFO
#define PWM1_REVPOLAR 0x1000 // Reverse polarity
#define PWM1_OFFSTATE 0x0800 // Ouput Off state
#define PWM1_REPEATFF 0x0400 // Repeat last value if FIFO empty
#define PWM1_SERIAL 0x0200 // Run in serial mode
#define PWM1_ENABLE 0x0100 // Channel Enable
#define PWM0_MS_MODE 0x0080 // Run in MS mode
#define PWM0_USEFIFO 0x0020 // Data from FIFO
#define PWM0_REVPOLAR 0x0010 // Reverse polarity
#define PWM0_OFFSTATE 0x0008 // Ouput Off state
#define PWM0_REPEATFF 0x0004 // Repeat last value if FIFO empty
#define PWM0_SERIAL 0x0002 // Run in serial mode
#define PWM0_ENABLE 0x0001 // Channel Enable
#define PAGE_SIZE (4*1024)
#define BLOCK_SIZE (4*1024)
// GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y)
#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
#define OUT_GPIO(g) *(gpio+((g)/10)) |= (1<<(((g)%10)*3))
#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
#define GPIO_SET *(gpio+7) // sets bits which are 1 ignores bits which are 0
#define GPIO_CLR *(gpio+10) // clears bits which are 1 ignores bits which are 0
#define GPIO_GET *(gpio+13) // read bits
// I/O access
volatile unsigned *gpio;
volatile unsigned *pwm;
volatile unsigned *clk;
static int mem_fd = 0;
//
// Set up a memory regions to access GPIO
//
volatile unsigned * io_mapping(int base_addr)
{
char *gpio_mem, *gpio_map;
/* open /dev/mem */
if (!mem_fd) {
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
printf("can't open /dev/mem \n");
exit (-1);
}
}
/* mmap GPIO */
// Allocate MAP block
if ((gpio_mem = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) {
printf("allocation error \n");
exit (-1);
}
// Make sure pointer is on 4K boundary
if ((unsigned long)gpio_mem % PAGE_SIZE)
gpio_mem += PAGE_SIZE - ((unsigned long)gpio_mem % PAGE_SIZE);
// Now map it
gpio_map = (char *)mmap(
(caddr_t)gpio_mem,
BLOCK_SIZE,
PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_FIXED,
mem_fd,
base_addr
);
if ((long)gpio_map < 0) {
printf("mmap error %d\n", (int)gpio_map);
exit (-1);
}
// Always use volatile pointer!
return (volatile unsigned *)gpio_map;
}
//
void hw_initialize()
{
gpio = io_mapping(GPIO_BASE);
pwm = io_mapping(PWM_BASE);
clk = io_mapping(CLOCK_BASE);
// GPIO18をPWMに
SET_GPIO_ALT(18, 5);
*(pwm + PWM_CONTROL) = 0; // Stop PWM
*(clk + PWMCLK_CNTL) = BCM_PASSWORD | 0x01; // Stop PWM Clock
usleep(110000); // See comments in pwmSetClockWPi
while ((*(clk + PWMCLK_CNTL) & 0x80) != 0) // Wait for clock to be !BUSY
usleep(1000) ;
*(clk + PWMCLK_DIV) = BCM_PASSWORD | (32 << 12); // set pwm div to 32 (19.2/32 = 600KHz)
*(clk + PWMCLK_CNTL) = BCM_PASSWORD | 0x11; // enable clk
usleep(110000);
// Default range register of 1024
*(pwm + PWM0_RANGE) = 1024;
usleep(10000);
*(pwm + PWM1_RANGE) = 1024;
usleep(10000);
*(pwm + PWM0_DATA) = 0;
usleep(10000);
*(pwm + PWM1_DATA) = 0;
usleep(10000);
// Enable PWMs in balanced mode (default)
*(pwm + PWM_CONTROL) = PWM0_ENABLE | PWM1_ENABLE;
usleep(100000);
}
// Range設定
void set_range(int range)
{
*(pwm + PWM0_RANGE) = range;
usleep(1000);
}
// Duty設定
void set_duty(int duty)
{
*(pwm + PWM0_DATA) = duty;
usleep(1000);
}
int main(void)
{
int i;
hw_initialize();
set_range(1000);
for(i = 0; i <= 1000; i += 100) {
usleep(1000000);
set_duty(i);
printf("d=%d\n", i);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment