如何在FreeBSD14.2树莓派上用C直接访问BCM2835寄存器控制GPIO
树莓派FreeBSD下直接访问BCM2835 GPIO寄存器的问题解决
问题描述
在树莓派3/4的FreeBSD14.2系统上,尝试通过直接访问BCM2835寄存器控制GPIO引脚,代码基于Mike McCauley的C库。该代码在树莓派OS(Debian Bookworm)上运行正常,但在FreeBSD下可正常编译、运行,却无法实际控制GPIO引脚。
简化测试代码如下:
/* Program to test direct access to bcm2835 registers to blink a LED Build as follows: gcc -Wall -o gpio gpio.c Run as root */ #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> #include <stdint.h> #include <time.h> // 补充FreeBSD需要的sleep头文件 // GPIO pin to which a LED is connected: #define GPIO_PIN (23) // 根据树莓派型号选择外设基地址:3用0x3F000000,4用0xFE000000 #define PERIPH_BASE 0x3F000000 int main() { int mem_fd; uint32_t *periph_base; uint32_t *periph_gpio; uint32_t *gpio_function_register; uint32_t gpio_function_register_pin_mask; uint32_t function_register_value; volatile uint32_t *gpio_set_lower_register; volatile uint32_t *gpio_clear_lower_register; uint32_t gpio_pin_mask; mem_fd = open("/dev/mem", O_RDWR|O_SYNC); if (mem_fd < 0) return 1; periph_base = (uint32_t *)mmap( NULL, // 自动分配地址 0x01000000, // 映射长度 PROT_READ|PROT_WRITE|PROT_NOCACHE, // 增加PROT_NOCACHE确保MMIO非缓存访问 MAP_SHARED, // 共享映射 mem_fd, // 映射的文件 PERIPH_BASE // 外设基地址偏移 ); close(mem_fd); if (periph_base == (void *)-1) return 2; /********* 计算所需寄存器地址 *********/ // GPIO寄存器相对于外设基地址的偏移是0x200000,除以4是因为uint32_t指针步长为4字节 periph_gpio = periph_base + 0x200000/4; // 引脚功能选择寄存器地址 gpio_function_register = periph_gpio + GPIO_PIN/10; // 功能选择位掩码 gpio_function_register_pin_mask = (7 << ((GPIO_PIN%10)*3)); // GPIO置位/清零寄存器地址 gpio_set_lower_register = periph_gpio + 7; gpio_clear_lower_register = periph_gpio + 10; gpio_pin_mask = (uint32_t)1 << GPIO_PIN; /********* 设置GPIO为输出模式 *********/ function_register_value = *gpio_function_register; function_register_value &= ~gpio_function_register_pin_mask; function_register_value |= (((uint32_t)1) << ((GPIO_PIN%10)*3)); *gpio_function_register = function_register_value; /********* 循环闪烁LED *********/ for (;;) { *gpio_set_lower_register = gpio_pin_mask; sleep(1); *gpio_clear_lower_register = gpio_pin_mask; sleep(1); } return 0; }
解决方案
1. 启用非缓存内存映射
BCM2835的GPIO寄存器属于内存映射IO(MMIO),必须绕过CPU缓存直接访问硬件。FreeBSD默认不会为/dev/mem映射启用非缓存,因此需要在mmap的保护标志中添加PROT_NOCACHE,确保写操作直接同步到硬件而非停留在缓存中。
2. 禁用内核GPIO驱动
FreeBSD默认加载了bcmgpio内核模块,该模块会接管GPIO引脚的控制权,导致直接寄存器访问失效。执行以下命令卸载模块:
kldunload bcmgpio
若需永久禁用,可在/boot/loader.conf中添加:
bcmgpio_load="NO"
3. 确认外设基地址
- 树莓派3的外设基地址为
0x3F000000 - 树莓派4的外设基地址为
0xFE000000
确保代码中PERIPH_BASE宏与你的树莓派型号匹配。
4. 补充必要头文件
FreeBSD中sleep函数需要显式包含<time.h>头文件,否则可能出现隐式声明导致的运行时异常。
5. 以Root权限运行
直接访问/dev/mem需要root权限,运行程序时使用:
sudo ./gpio
内容的提问来源于stack exchange,提问作者Vimsent
相关产品推荐
相关产品推荐

