一、
BCM2835驱动库介绍
BCM2835是一个树莓派开源的驱动库,它提供了树莓派板上J8排针上引出的GPIO的操作接口,包括IIC、SPI、PWM等驱动程序样例。以下是在使用的过程中对该库的源码分析总结。首先先来介绍一下该库的C文件组成。该库的C文件主要分布在src和example这两个文件夹。Src文件夹里面有bcm2835.c、bcm2835.h和test.c,整个库的主要代码实现都在bcm2835.c里面。Example文件夹里面放置着几个实例。
下面就先从bcm2835.c开始介绍。Bcm2835驱动源码地址:http://www.airspayce.com/mikem/bcm2835/
二、
源码分析
1、
int bcm2835_init(void)该函数主要完成了BCM2835硬件寄存器物理地址到系统虚拟地址的映射。我们来看看代码实现。
if ((fp = fopen(BMC2835_RPI2_DT_FILENAME , "rb")))
{
unsigned char buf[4];
fseek(fp, BMC2835_RPI2_DT_PERI_BASE_ADDRESS_OFFSET, SEEK_SET);
if (fread(buf, 1, sizeof(buf), fp) == sizeof(buf))
bcm2835_peripherals_base = (uint32_t *)(buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0);
fseek(fp, BMC2835_RPI2_DT_PERI_SIZE_OFFSET, SEEK_SET);
if (fread(buf, 1, sizeof(buf), fp) == sizeof(buf))
bcm2835_peripherals_size = (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0);
fclose(fp);
}
可以看到,首先在既定的文件中读到bcm2835外围设备的物理基址和整个外围设备地址存储范围的大小。
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;
}
/* Base of the peripherals block is mapped to VM */
bcm2835_peripherals = mapmem("gpio", bcm2835_peripherals_size, memfd, (uint32_t)bcm2835_peripherals_base);
if (bcm2835_peripherals == MAP_FAILED) goto exit;
/* Now compute the base addresses of various peripherals,
// which are at fixed offsets within the mapped peripherals block
// Caution: bcm2835_peripherals is uint32_t*, so divide offsets by 4
*/
bcm2835_gpio = bcm2835_peripherals + BCM2835_GPIO_BASE/4;
bcm2835_pwm = bcm2835_peripherals + BCM2835_GPIO_PWM/4;
bcm2835_clk = bcm2835_peripherals + BCM2835_CLOCK_BASE/4;
bcm2835_pads = bcm2835_peripherals + BCM2835_GPIO_PADS/4;
bcm2835_spi0 = bcm2835_peripherals + BCM2835_SPI0_BASE/4;
bcm2835_bsc0 = bcm2835_peripherals + BCM2835_BSC0_BASE/4; /* I2C */
bcm2835_bsc1 = bcm2835_peripherals + BCM2835_BSC1_BASE/4; /* I2C */
bcm2835_st = bcm2835_peripherals + BCM2835_ST_BASE/4;
然后通过mmap系统调用函数将外围设备基址映射到系统的虚拟地址上的到系统虚拟的设备基址
bcm2835_peripherals。由于各外围设备的偏移量相对于基址来说是固定的,因此我们可以得到各个外围设备的虚拟地址。如上面bcm2835_gpio、bcm2835_pwm等就是设备的虚拟地址。对该地址的操作等同于对相对应的硬件物理地址所指的寄存器的操作。上面所列出来的只是一部分的外设,同样的,只要我们通过bcm2835的规格书知道某个外设寄存器相对于外设基址的偏移量我们就能得到它的虚拟地址。
2、小提示:
※MAP_FAILED是系统的一个宏,其值为(void*)(-1)也即是0xFFFFFFFF(32位系统时)。
※BCM2835_GPIO_BASE/4为什么偏移量要除于4?因为bcm2835_peripherals是一个uint32_t的指针变量,其他同。
※为什么从文件中读到的外设物理基址不是0x20000000?因为只有bcm2835(树莓派1)的外设基址是0x20000000开始的,而树莓派2(bcm2836)、树莓派3(bcm2837)的外设基址不是这个。你想知道是多少,你打出来看一下就知道了。各个模块的相对偏移量应该是不变的。不过应该记得这个区别。
三、参考链接
Bcm2835驱动源码地址:http://www.airspayce.com/mikem/bcm2835/
BCM2835规格书:https://www.raspberrypi.org/documentation/hardware/raspberrypi/README.md