当前位置: 首页> 健康> 知识 > 树莓派3B点灯(4)-- 自写驱动(低级版)(TODO)

树莓派3B点灯(4)-- 自写驱动(低级版)(TODO)

时间:2025/7/18 6:51:31来源:https://blog.csdn.net/fanged/article/details/142112864 浏览次数:0次

(TODO)

在树莓派 3B 上,直接通过操作寄存器控制 GPIO26 的 LED,而不使用 `gpio` 子系统和 `pinctrl` 子系统,可以通过直接访问 **Broadcom BCM2837 SoC** 的 GPIO 寄存器来实现。我们将通过内核模块,手动访问 GPIO 寄存器,并控制 GPIO26 的输出来点亮 LED。

### 1. 了解树莓派 GPIO 寄存器

树莓派的 GPIO 控制器有以下重要的寄存器:
- **GPIO Function Select Registers (`GPFSELx`)**:用于配置引脚的功能(输入、输出或其他外围功能)。
- **GPIO Pin Output Set Registers (`GPSETx`)**:用于设置 GPIO 引脚的输出(置位)。
- **GPIO Pin Output Clear Registers (`GPCLRx`)**:用于清除 GPIO 引脚的输出(复位)。

#### 树莓派 3B 的 GPIO26 对应的寄存器:
- **GPIO26** 对应于:
  - **GPFSEL2**:GPIO26 的功能选择寄存器(用于设置 GPIO26 为输出模式)。
  - **GPSET0**:用于将 GPIO26 置为高电平(点亮 LED)。
  - **GPCLR0**:用于将 GPIO26 置为低电平(关闭 LED)。

#### GPIO 基地址:
在树莓派 3B(BCM2837)上,**外设寄存器**的基地址为 `0x3F000000`。GPIO 寄存器位于基地址的偏移 `0x200000` 处,因此 GPIO 基地址为:
```
GPIO_BASE = 0x3F200000
```

### 2. 编写内核模块:直接操作寄存器控制 LED

下面的代码将通过直接访问 GPIO 寄存器控制 GPIO26 上的 LED。我们将手动设置 GPIO26 为输出模式,并控制它的电平。

#### `my_gpio_led.c`

```c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/io.h>   // 用于内存映射IO
#include <linux/delay.h> // 延迟函数

#define BCM2837_PERI_BASE        0x3F000000  // 树莓派 3B 外设基地址
#define GPIO_BASE                (BCM2837_PERI_BASE + 0x200000)  // GPIO 基地址

#define GPFSEL2                  (gpio_base + 0x08)  // GPFSEL2 控制 GPIO 20-29
#define GPSET0                   (gpio_base + 0x1C)  // GPSET0 控制 GPIO 0-31 的置位
#define GPCLR0                   (gpio_base + 0x28)  // GPCLR0 控制 GPIO 0-31 的清除

#define GPIO_PIN                 26  // 使用 GPIO 26 控制 LED

static void __iomem *gpio_base;  // GPIO寄存器的映射基地址

// 初始化 GPIO26 为输出模式
static void gpio_init(void) {
    unsigned int reg_val;

    // 设置 GPIO26 为输出模式
    reg_val = ioread32(GPFSEL2);  // 读取 GPFSEL2 的值
    reg_val &= ~(7 << 18);        // 清除 GPIO26 的 FSEL 位 (三位控制每个 GPIO)
    reg_val |= (1 << 18);         // 设置 GPIO26 为输出 (001 = 输出)
    iowrite32(reg_val, GPFSEL2);  // 写回 GPFSEL2
}

// 点亮 LED (设置 GPIO26 输出高电平)
static void led_on(void) {
    iowrite32((1 << GPIO_PIN), GPSET0);  // 设置 GPIO26
}

// 关闭 LED (设置 GPIO26 输出低电平)
static void led_off(void) {
    iowrite32((1 << GPIO_PIN), GPCLR0);  // 清除 GPIO26
}

// 模块加载函数
static int __init my_led_init(void) {
    pr_info("LED GPIO Module loaded\n");

    // 映射 GPIO 基地址
    gpio_base = ioremap(GPIO_BASE, 0x100);  // 映射寄存器地址
    if (!gpio_base) {
        pr_err("Failed to map GPIO base address\n");
        return -EBUSY;
    }

    // 初始化 GPIO26 为输出模式
    gpio_init();

    // 点亮 LED
    led_on();
    pr_info("LED on (GPIO26)\n");

    return 0;
}

// 模块卸载函数
static void __exit my_led_exit(void) {
    // 关闭 LED
    led_off();
    pr_info("LED off (GPIO26)\n");

    // 解除 GPIO 基地址映射
    if (gpio_base)
        iounmap(gpio_base);

    pr_info("LED GPIO Module unloaded\n");
}

module_init(my_led_init);
module_exit(my_led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Direct GPIO control for LED on Raspberry Pi 3B");
```

### 3. 编写 `Makefile`

编写一个 `Makefile` 文件用于编译内核模块:

```makefile
obj-m += my_gpio_led.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
```

### 4. 编译和加载内核模块

1. **编译模块**:
   在 `my_gpio_led.c` 文件和 `Makefile` 所在目录下,执行以下命令进行编译:

   ```bash
   make
   ```

2. **加载模块**:
   使用 `insmod` 将编译好的内核模块加载到内核中:

   ```bash
   sudo insmod my_gpio_led.ko
   ```

3. **检查内核日志**:
   使用 `dmesg` 查看模块加载时的内核日志:

   ```bash
   dmesg | tail
   ```

   日志应显示 "LED on (GPIO26)",表明 GPIO26 上的 LED 已经点亮。

4. **卸载模块**:
   当你想关闭 LED 并卸载模块时,使用以下命令:

   ```bash
   sudo rmmod my_gpio_led
   ```

   再次使用 `dmesg` 查看日志,应该显示 "LED off (GPIO26)",表示 LED 已关闭。

### 5. 解释代码逻辑

- **`ioremap` 和 `iounmap`**:这些函数用于将物理地址映射到虚拟地址空间,允许内核模块访问设备的寄存器。我们通过 `ioremap` 将 GPIO 基地址映射到内存。
- **寄存器操作**:
  - `GPFSEL2`:设置 GPIO26 为输出模式。
  - `GPSET0`:将 GPIO26 设置为高电平,点亮 LED。
  - `GPCLR0`:将 GPIO26 设置为低电平,关闭 LED。
- **点亮和关闭 LED**:通过 `led_on()` 和 `led_off()` 函数直接操作 GPIO26 的寄存器。

### 6. 总结

在这个例子中,我们跳过了 Linux 的 `gpio` 和 `pinctrl` 子系统,直接操作树莓派 3B 的 GPIO 寄存器,通过 `ioremap` 将寄存器映射到内存,并直接控制 GPIO26 的电平来点亮和关闭 LED。

这种方法提供了更低级的硬件访问,但需要更多的寄存器级别的控制。如果您需要对 GPIO 进行更复杂的操作,通常建议使用 `gpio` 子系统和 `pinctrl` 子系统。

关键字:树莓派3B点灯(4)-- 自写驱动(低级版)(TODO)

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: