Skip to content

Instantly share code, notes, and snippets.

@rutles
Last active December 10, 2019 09:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rutles/6648946 to your computer and use it in GitHub Desktop.
Save rutles/6648946 to your computer and use it in GitHub Desktop.
Raspberry Pi BCM2835 peripheral registers handling example
/*
file name: register.c
description: Raspberry Pi BCM2835 peripheral registers handling example
compile: cc register.c -o register
execute: sudo ./register
Tetsuya Suzuki, 2013
*/
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdint.h>
// offset address
#define OFF_PRT 0x20200000 // port registers
#define OFF_PWM 0x2020C000 // PWM registers
#define OFF_CLK 0x20101000 // clock registers
#define OFF_PAD 0x20100000 // pads registers
// Create new mapping of registers
// offset: offset address of registers
// return: pointer to the mapped area, or MAP_FAILED on error
uint32_t *regs_map(off_t offset){
int fd;
void *map;
fd = open("/dev/mem", O_RDWR | O_SYNC);
if(fd < 0)
return MAP_FAILED;
map = mmap(NULL,
getpagesize(),
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
offset);
close(fd);
return map;
}
// read selected function
// *regs: GPIO mapping, port: GPIO number
// return: function value, ex.0 input, 1 output
int fnc_rd(uint32_t *regs, int port){
return (regs[port / 10] >> (port % 10) * 3) & 7;
}
// function select
// *regs: GPIO mapping, port: GPIO number, val: function value
void fnc_wr(uint32_t *regs, int port, int val){
regs[port / 10] &= ~(7 << ((port % 10) * 3));
regs[port / 10] |= (val << ((port % 10) * 3));
}
// read port level
// *regs: GPIO mapping, port: GPIO number
// return: 1 or 0
int prt_rd(uint32_t *regs, int port){
return (regs[13] >> port) & 1;
}
// write port level
// *regs: GPIO mapping, port: GPIO number, val: 1 or 0
void prt_wr(uint32_t *regs, int port, int val){
if(val)
regs[7] = (1 << port);
else
regs[10] = (1 << port);
}
// set pull up/down to a input port
// *regs: GPIO mapping, port: GPIO number, val: 0 none, 1 pull down, 2 pull up
// return: success flag
int prt_pud(uint32_t *regs, int port, int val){
if(fnc_rd(regs, port)){
printf("GPIO%d FSEL: %d\n", port, fnc_rd(regs, port));
fprintf(stderr, "pull up/down set valid only input port.\n");
return -1;
}
regs[37] = val;
usleep(1);
regs[38] = (1 << port);
usleep(1);
regs[37] = 0;
regs[38] = 0;
return 0;
}
// set Edge detect enable
// *regs: GPIO mapping, port: GPIO number, val: 0 none, 1 rising, 2 falling, 3 both
void prt_edg(uint32_t *regs, int port, int val){
if(val & 1)//rising
regs[19] |= (1 << port);
else
regs[19] &= ~(1 << port);
if(val & 2)//falling
regs[22] |= (1 << port);
else
regs[22] &= ~(1 << port);
}
// get Edge detect status
// *regs: GPIO mapping, port: GPIO number
// return: 1 detect, 0 not detect
int prt_event(uint32_t *regs, int port){
int ret;
ret = regs[16] &= ~(1 << port);//read detect
if(ret)
regs[16] |= (1 << port);//clear detect
return ret;
}
// change drive strength of all GPIO pins
// val: 0 - 7 -> 2mA - 16mA
void prt_drv(int val){
uint32_t state;
uint32_t *regs;
regs = regs_map(OFF_PAD);
state = (regs[11] & ~7) | val;
regs[11] = 0x5A000000 | state;
munmap((void *)regs, getpagesize());
}
// CLK turn on/off
// *regs: PWM mapping, val: 0 off, 1 on
void clk_sw(uint32_t *regs, int val){
if(val){
regs[28] = 0x5A000000 | 0x11;// enable clock
} else {
regs[28] = 0x5A000000 | 0x01;// disable clock
while(regs[28] & 0x80); // loop while busy
}
}
// set CLK frequency
// *regs: CLK mapping, val: frequency
void clk_frq(uint32_t *regs, uint32_t val){
uint32_t save;
int di, dr, df;
di = 19200000 / val;
dr = 19200000 % val;
df = (int)((double)dr * 4096.0 / 19200000.0);
save = regs[28];//backup on/off
clk_sw(regs, 0);//temporary off
regs[29] = 0x5A000000 | (di << 12) | df;
regs[28] = save;//restore on/off
}
// set GPIO4 as free running clock out
// return: pointer to the mapped area, or MAP_FAILED on error
uint32_t *fnc_clk(){
uint32_t *regs;
regs = regs_map(OFF_PRT);
if(regs == MAP_FAILED)
return MAP_FAILED;
fnc_wr(regs, 4, 4);//set GPIO4 for CLK
munmap((void *)regs, getpagesize());
regs = regs_map(OFF_CLK);
if(regs == MAP_FAILED)
return MAP_FAILED;
return regs;
}
// set PWM space
// *regs: PWM mapping, val: space
void pwm_spc(uint32_t *regs, uint32_t val){
regs[4] = val;
usleep(10);
}
// set PWM mark
// *regs: PWM mapping, value: mark
void pwm_mrk(uint32_t *regs, uint32_t value){
regs[5] = value;
}
// PWM turn on/off
// *regs: PWM mapping, val: 0 off, 1 on
void pwm_sw(uint32_t *regs, uint32_t val){
if(val)
regs[0] |= 1;
else
regs[0] &= ~1;
}
// set PWM clock devide
// *regs: PWM mapping, val: divide
// NOTE: Frequency = 19200kHz / space / divide
void pwm_clk(uint32_t *regs, uint32_t val){
uint32_t save;
uint32_t *regs_clk;
save = regs[0];
regs[0] = 0;
regs_clk = regs_map(OFF_CLK);
regs_clk[40] = 0x5A000000 | 1;
usleep(100);
while(regs_clk[40] & 0x80)
usleep(1);
regs_clk[41] = 0x5A000000 | (val << 12);
regs_clk[40] = 0x5A000000 | 0x11;
munmap((void *)regs_clk, getpagesize());
regs[0] = save;
}
// set GPIO18 as PWM M/S mode
// return: pointer to the mapped area, or MAP_FAILED on error
uint32_t *fnc_pwm(){
uint32_t *regs;
regs = regs_map(OFF_PRT);
if(regs == MAP_FAILED)
return MAP_FAILED;
fnc_wr(regs, 18, 2);//set GPIO18 for PWM
munmap((void *)regs, getpagesize());
regs = regs_map(OFF_PWM);
if(regs == MAP_FAILED)
return MAP_FAILED;
regs[0] |= 0x80;// PWM/MS mode
return regs;
}
// CLK example
// Piezo sounder shall be connected to GPIO4
// Beep on/off repeat 10 interval 0.5sec
int main(){
int i;
int ret;
uint32_t *regs;
regs = fnc_clk();
if(regs == MAP_FAILED){
fprintf(stderr, "fail to get registers map. %s\n", strerror(errno));
return -1;
}
clk_frq(regs, 4000);// 4kHz
for(i = 0; i < 10; i++){
clk_sw(regs, 1);// beep
usleep(500000);
clk_sw(regs, 0);// stop
usleep(500000);
}
ret = munmap((void *)regs, getpagesize());
if(ret)
fprintf(stderr, "fail to unmap. %s\n", strerror(errno));
return ret;
}
// PWM example
// LED with resistor shall be connected to GPIO18
// harf bright on/off repeat 10 interval 0.5sec
int ex2_main(){
int i;
int ret;
uint32_t *regs;
regs = fnc_pwm();
if(regs == MAP_FAILED){
fprintf(stderr, "fail to get registers map. %s\n", strerror(errno));
return -1;
}
pwm_spc(regs, 64);// space 64 clock
pwm_mrk(regs, 32);// mark 32 clock (duty 50%)
pwm_clk(regs, 8);// divide 8 (37.5 kHz)
for(i = 0; i < 10; i++){
pwm_sw(regs, 1);
usleep(500000);
pwm_sw(regs, 0);
usleep(500000);
}
ret = munmap((void *)regs, getpagesize());
if(ret)
fprintf(stderr, "fail to unmap. %s\n", strerror(errno));
return ret;
}
// Port pull up/down example
// pull up and read, then pull down and read
int ex3_main(){
int ret;
uint32_t *regs;
regs = regs_map(OFF_PRT);
if(regs == MAP_FAILED){
fprintf(stderr, "fail to get registers map. %s\n", strerror(errno));
return -1;
}
fnc_wr(regs, 25, 0); //set GPIO25 as input
prt_pud(regs, 25, 2);//pull up GPIO25
ret = prt_rd(regs, 25); // read GPIO25;
printf("GPIO%d value:%d\n", 25, ret);
prt_pud(regs, 25, 1);//pull down GPIO25
ret = prt_rd(regs, 25); // read GPIO25;
printf("GPIO%d value:%d\n", 25, ret);
prt_pud(regs, 25, 0);//clear pull up/down GPIO25
ret = munmap((void *)regs, getpagesize());
if(ret){
fprintf(stderr, "fail to unmap. %s\n", strerror(errno));
return -1;
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment