linux,驱动开发,通过 mem 设备控制 GPIO

在Linux系统中,通过/dev/mem设备直接操作物理内存控制GPIO是一种底层方法,通常用于用户空间程序直接访问硬件寄存器。以下是分步指南和注意事项:


1. 准备工作

1.1 硬件文档
  • 查阅SoC手册(如BCM2835/BCM2837用于树莓派),确定目标GPIO的:
    • 物理地址基址(如GPIO0基址:0x3F200000
    • 寄存器偏移量(如功能选择寄存器、输出设置寄存器等)
1.2 权限配置
  • 运行程序需root权限,或修改/dev/mem权限:
    sudo chmod 666 /dev/mem

2. 用户空间操作步骤

2.1 打开/dev/mem

#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>


int fd = open("/dev/mem", O_RDWR | O_SYNC);
if (fd < 0) {
    perror("Failed to open /dev/mem");
    return -1;
}
2.2 映射物理地址到用户空间

#define GPIO_BASE 0x3F200000 // 示例:BCM2835 GPIO基址
#define PAGE_SIZE 4096
#define BLOCK_SIZE (4 * PAGE_SIZE)


volatile unsigned int *gpio = mmap(
    NULL, // 虚拟地址由内核分配
    BLOCK_SIZE, // 映射内存块大小
    PROT_READ | PROT_WRITE, // 读写权限
    MAP_SHARED, // 共享映射
    fd, // 文件描述符
    GPIO_BASE // 物理地址基址
);


if (gpio == MAP_FAILED) {
    perror("mmap failed");
    close(fd);
    return -1;
}
2.3 操作GPIO寄存器

假设控制GPIO17(物理引脚11):

  1. 设置GPIO为输出模式

    #define GPFSEL1 (*(gpio + 1)) // 功能选择寄存器1(偏移0x04)
    GPFSEL1 &= ~(0b111 << (3 * 3)); // 清除GPIO17的配置位(3组,每组3位)
    GPFSEL1 |= (0b001 << (3 * 3)); // 设置为输出模式(0b001)
  2. 设置输出电平

    #define GPSET0 (*(gpio + 7)) // 输出设置寄存器(偏移0x1C)
    #define GPCLR0 (*(gpio + 10)) // 输出清除寄存器(偏移0x28)
    
    
    GPSET0 = (1 << 17); // 设置GPIO17为高电平
    // GPCLR0 = (1 << 17); // 设置GPIO17为低电平
2.4 清理资源

munmap((void *)gpio, BLOCK_SIZE);
close(fd);

3. 完整示例代码

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

#define GPIO_BASE 0x3F200000
#define PAGE_SIZE 4096
#define BLOCK_SIZE (4 * PAGE_SIZE)

int main() {
    int fd = open("/dev/mem", O_RDWR | O_SYNC);
    if (fd < 0) {
        perror("open /dev/mem");
        return 1;
    }

    volatile unsigned int *gpio = mmap(
        NULL, BLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO_BASE
    );
    if (gpio == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return 1;
    }

    // 设置GPIO17为输出
    #define GPFSEL1 (*(gpio + 1))
    GPFSEL1 &= ~(0b111 << 15);  // 清除位15-17(GPIO17配置)
    GPFSEL1 |=  (0b001 << 15);   // 设置为输出模式

    // 输出高电平
    #define GPSET0 (*(gpio + 7))
    GPSET0 = (1 << 17);

    sleep(1);

    // 输出低电平
    #define GPCLR0 (*(gpio + 10))
    GPCLR0 = (1 << 17);

    munmap((void *)gpio, BLOCK_SIZE);
    close(fd);
    return 0;
}

4. 注意事项

  1. 平台依赖性:不同SoC的GPIO寄存器布局不同,需查阅具体手册。
  2. 权限风险:直接操作硬件可能导致系统崩溃,建议:
    • 使用sudo运行程序。
    • 避免在关键系统上运行未经验证的代码。
  3. 内核驱动推荐:对于内核模块开发,优先使用Linux GPIO子系统:

    #include <linux/gpio.h>
    gpio_request(gpio_num, "my_gpio");
    gpio_direction_output(gpio_num, 1);
  4. 替代方案:使用用户空间库(如wiringPilibgpiod)简化操作。

5. 扩展建议

  • 调试工具:使用raspi-gpio(树莓派)或devmem2查看/修改寄存器。
  • 内核模块:若需在内核中操作,使用ioremap()writel()等函数。

通过上述方法,您可以直接通过/dev/mem控制GPIO,但需谨慎处理硬件细节和权限问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值