Last active
December 10, 2019 09:00
-
-
Save rutles/6648946 to your computer and use it in GitHub Desktop.
Raspberry Pi BCM2835 peripheral registers handling example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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