手把手教你学BSP(8.2)--BSP 的编写驱动程序:实现对硬件设备的访问接口

目录

介绍 BSP 的编写驱动程序:实现对硬件设备的访问接口

1. 准备工作

1.1 获取 Linux 内核源码

1.2 安装开发工具

2. 创建字符设备驱动

2.1 定义设备号

2.1.1 创建驱动程序文件

2.2 编译内核模块

2.3 加载和卸载内核模块

3. 测试驱动程序

3.1 创建设备节点

3.2 测试读写操作

4. 总结


介绍 BSP 的编写驱动程序:实现对硬件设备的访问接口

在嵌入式系统开发中,编写驱动程序是连接硬件和操作系统的桥梁,负责管理和控制硬件设备。本文将详细介绍如何编写一个简单的字符设备驱动程序,实现对硬件设备的访问接口。我们将以一个 GPIO 控制器为例进行说明。

1. 准备工作

1.1 获取 Linux 内核源码

从 Linux 内核官方网站或 GitHub 仓库获取最新的内核源码:

 

sh

深色版本

git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
1.2 安装开发工具

确保安装了必要的开发工具和库。例如,在 Ubuntu 上可以使用以下命令安装:

 

sh

深色版本

sudo apt-get update
sudo apt-get install build-essential kernel-package libncurses5-dev libssl-dev

2. 创建字符设备驱动

2.1 定义设备号

在内核模块中注册字符设备,分配设备号和设备类。

2.1.1 创建驱动程序文件

创建一个名为 gpio_driver.c 的文件,用于编写驱动程序代码:

 

c

深色版本

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>

#define DEVICE_NAME "gpio_driver"
#define CLASS_NAME "gpio_class"

static dev_t dev_num;
static struct cdev gpio_cdev;
static struct class *gpio_class;
static struct device *gpio_device;

static int gpio_major = 0;
static int gpio_minor = 0;

static int gpio_open(struct inode *inode, struct file *file) {
    printk(KERN_INFO "gpio_driver: device opened\n");
    return 0;
}

static int gpio_release(struct inode *inode, struct file *file) {
    printk(KERN_INFO "gpio_driver: device closed\n");
    return 0;
}

static ssize_t gpio_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {
    char gpio_value;
    gpio_value = gpio_get_value(12); // 假设 GPIO 12 是我们要读取的引脚
    copy_to_user(buf, &gpio_value, 1);
    printk(KERN_INFO "gpio_driver: read value %d from GPIO 12\n", gpio_value);
    return 1;
}

static ssize_t gpio_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) {
    char gpio_value;
    copy_from_user(&gpio_value, buf, 1);
    gpio_set_value(12, gpio_value); // 假设 GPIO 12 是我们要控制的引脚
    printk(KERN_INFO "gpio_driver: wrote value %d to GPIO 12\n", gpio_value);
    return 1;
}

static const struct file_operations gpio_fops = {
    .owner = THIS_MODULE,
    .open = gpio_open,
    .release = gpio_release,
    .read = gpio_read,
    .write = gpio_write,
};

static int __init gpio_driver_init(void) {
    int ret;

    // 请求 GPIO 引脚
    ret = gpio_request(12, "gpio_12");
    if (ret < 0) {
        printk(KERN_ALERT "Failed to request GPIO 12\n");
        return ret;
    }

    // 设置 GPIO 方向
    gpio_direction_input(12); // 假设初始方向为输入

    // 动态分配设备号
    ret = alloc_chrdev_region(&dev_num, gpio_minor, 1, DEVICE_NAME);
    if (ret < 0) {
        gpio_free(12);
        printk(KERN_ALERT "Failed to register device\n");
        return ret;
    }

    // 初始化字符设备
    cdev_init(&gpio_cdev, &gpio_fops);
    gpio_cdev.owner = THIS_MODULE;
    ret = cdev_add(&gpio_cdev, dev_num, 1);
    if (ret < 0) {
        unregister_chrdev_region(dev_num, 1);
        gpio_free(12);
        printk(KERN_ALERT "Failed to add character device\n");
        return ret;
    }

    // 创建设备类
    gpio_class = class_create(THIS_MODULE, CLASS_NAME);
    if (IS_ERR(gpio_class)) {
        cdev_del(&gpio_cdev);
        unregister_chrdev_region(dev_num, 1);
        gpio_free(12);
        printk(KERN_ALERT "Failed to create device class\n");
        return PTR_ERR(gpio_class);
    }

    // 创建设备文件
    gpio_device = device_create(gpio_class, NULL, dev_num, NULL, DEVICE_NAME);
    if (IS_ERR(gpio_device)) {
        class_destroy(gpio_class);
        cdev_del(&gpio_cdev);
        unregister_chrdev_region(dev_num, 1);
        gpio_free(12);
        printk(KERN_ALERT "Failed to create device file\n");
        return PTR_ERR(gpio_device);
    }

    printk(KERN_INFO "gpio_driver: device registered successfully\n");
    return 0;
}

static void __exit gpio_driver_exit(void) {
    device_destroy(gpio_class, dev_num);
    class_destroy(gpio_class);
    cdev_del(&gpio_cdev);
    unregister_chrdev_region(dev_num, 1);
    gpio_free(12);
    printk(KERN_INFO "gpio_driver: device unregistered\n");
}

module_init(gpio_driver_init);
module_exit(gpio_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple GPIO driver");
2.2 编译内核模块

创建一个 Makefile 文件,用于编译内核模块:

 

makefile

深色版本

obj-m += gpio_driver.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

编译内核模块:

 

sh

深色版本

make
2.3 加载和卸载内核模块

加载内核模块:

 

sh

深色版本

sudo insmod gpio_driver.ko

卸载内核模块:

 

sh

深色版本

sudo rmmod gpio_driver

3. 测试驱动程序

3.1 创建设备节点

如果设备节点不存在,可以手动创建:

 

sh

深色版本

sudo mknod /dev/gpio_driver c $(cat /proc/devices | grep gpio_driver | awk '{print $1}') 0
3.2 测试读写操作

使用 catecho 命令测试设备节点的读写操作:

 

sh

深色版本

echo 1 > /dev/gpio_driver
cat /dev/gpio_driver

4. 总结

通过上述步骤,可以系统地编写一个简单的字符设备驱动程序,实现对硬件设备的访问接口。字符设备驱动模型适用于多种硬件设备,能够提供灵活的设备管理和控制功能。希望这些详细的解释和示例能够帮助你更好地理解和实施驱动程序的开发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值