问题背景
我们在做嵌入式开发时常需要从应用程序中读取某个GPIO端口,这些端口往往连接了其他的板子,或外设。
那么解决问题的办法有两种。
写驱动
- 编写一个linux驱动
- 加载到内核中
- 应用程序通过读取驱动生成的虚拟文件来读写GPIO
这种做法实在有些麻烦。
第二种方法 mmap
了解到这种方法的起因是 在阅读相关树莓派的资料时,发现人家居然可以用python访问gpio口!!
看了它的C实现版本。弄明白了!
#include <bcm2835.h>
#include <stdio.h>
int main(int argc, char **argv)
{
if (geteuid() == 0)
{
if (!bcm2835_init())
return 1;
if (!bcm2835_close())
return 1;
}
else
{
fprintf(stderr, "****You need to be root to properly run this test program\n");
}
return 0;
}
下载了这个类库的源码发现init中写到
if ((memfd = open("/dev/mem", O_RDWR | O_SYNC) ) < 0)
{
fprintf(stderr, "bcm2835_init: Unable to open /dev/mem: %s\n",
strerror(errno)) ;
goto exit;
}
....
bcm2835_gpio = (volatile uint32_t *)mapmem("gpio", BCM2835_BLOCK_SIZE, memfd, BCM2835_GPIO_BASE);
而mapmem中写到:
static void *mapmem(const char *msg, size_t size, int fd, off_t off)
{
void *map = mmap(NULL, size, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, off);
if (MAP_FAILED == map)
fprintf(stderr, "bcm2835_init: %s mmap failed: %s\n", msg, strerror(errno));
return map;
}
一切就明白了。
1. 打开/dev/mem 这个特殊的设备
2. mmap映射。
3. 访问映射后的内存即可
那么GPIO对应的内存地址是多少呢? 这个要看文档了。对于树莓派来说:
#define BCM2835_PERI_BASE 0x20000000
/// Base Physical Address of the System Timer registers
#define BCM2835_ST_BASE (BCM2835_PERI_BASE + 0x3000)
/// Base Physical Address of the Pads registers
#define BCM2835_GPIO_PADS (BCM2835_PERI_BASE + 0x100000)
/// Base Physical Address of the Clock/timer registers
#define BCM2835_CLOCK_BASE (BCM2835_PERI_BASE + 0x101000)
/// Base Physical Address of the GPIO registers
#define BCM2835_GPIO_BASE (BCM2835_PERI_BASE + 0x200000)
/// Base Physical Address of the SPI0 registers
#define BCM2835_SPI0_BASE (BCM2835_PERI_BASE + 0x204000)
/// Base Physical Address of the BSC0 registers
#define BCM2835_BSC0_BASE (BCM2835_PERI_BASE + 0x205000)
/// Base Physical Address of the PWM registers
#define BCM2835_GPIO_PWM (BCM2835_PERI_BASE + 0x20C000)
/// Base Physical Address of the BSC1 registers
#define BCM2835_BSC1_BASE (BCM2835_PERI_BASE + 0x804000)