Part7:第一个LED驱动程序

本文详细介绍了一个简单的LED驱动程序的开发过程,包括驱动程序的设计原理、代码实现、编译测试及硬件操作步骤。从软件架构与硬件操作两方面,阐述了如何在Linux环境下编写并测试LED驱动程序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 实现原理

重点:驱动程序 = 软件架构 + 硬件操作
实现原理看两张图即可
1.1 用户程序访问驱动程序的流程
用户程序访问驱动程序流程图
2.驱动程序的架构
驱动程序的软件架构

2 代码实现
2.1 搭建框架

led驱动程序: led_drv.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>

//LED驱动程序的open函数
static int led_open(struct inode *inode, struct file *file)
{
    printk("first_drv_open"); //注意不能用printf,而是用内核提供的printk
    return 0;
}
// LED驱动程序的write函数
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    printk("first_drv_write");
    return 0;
}
// 驱动程序通过下面的文件操作结构体告诉内核对应的操作
// 即用户程序的open、write对应 led_open、lep_write
// 注意:file_operations结构体放在led_open led_write函数下面,不然编译时出现未定义这两个函数
static struct file_operations led_fops = {
    .owner  =   THIS_MODULE,     
    .open   =   led_open,     
    .write = led_write,    
};

// LED驱动程序的入口函数
static int led_init(void)
{
    //注册字符设备,即把first_drv_fops插入到内核维护的字符设备数组
    //而在这个数组里,主设备号111即为数组索引  
    register_chrdev(111, "led_drv", &led_fops);
    return 0;
}
// LED驱动的卸载函数
void led_exit(void)
{
    //在字符设备数组中卸载主设备号为111的first_drv_fops
    unregister_chrdev(111, "led_drv");
}
// 让内核维护的一个结构体里的函数指针指向驱动程序的入口函数
module_init(led_init)
// 让内核维护的一个结构体里的函数指针指向驱动程序的卸载函数
module_exit(led_exit)

// 模块注册为GPL,避免内核不能识别LED驱动程序
MODULE_LICENSE("GPL");

Makefile 文件

KERN_DIR = /work/system/linux-2.6.22.6
all:
	make -C $(KERN_DIR) M=`pwd` modules
clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
obj-m += led_drv.o
注意:必须先编译linux-2.6.22.6内核,可参考https://blog.youkuaiyun.com/qq_42800075/article/details/105470114
此外,pwd不是单引号,而是 `pwd` ,否则会报错,如下所示

注意pwd
测试函数:led_drv_test.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char * * argv)
{
    int fd, val = 1;
    fd = open("/dev/led_drv", O_RDWR);
    if(fd < 0)
        printf("can't open /dev/led_drv");
    
    write(fd, &val, 4)
    return 0;
}
2.2 硬件操作
先看下面对软件框架的编译、测试
3 编译、测试与完善
3.1 编译

当执行make时,运行结果如下
编译LED驱动程序插入LED驱动模块

3.2 测试与完善

测试结果
LED驱动程序测试结果图
为啥呢?
因为还没创建/dev/led_drv设备节点,看下图
创建/dev/led_drv设备节点
提问1:有没发现,手动指定一个主设备号不仅可能跟现有设备号冲突,且特别麻烦,有没自动指定主设备号?
完善1:看下图
完善1
注意,上图两个函数拼写有误,是register_chrdev unregister_chrdev 是dev 而不是 drv

提问2:每次手动新建设备节点,太麻烦了。有没有自动创建设备节点?
完善2:看下图
完善2
接下来,让我们填充“硬件操作”,实现三个LED灯的点亮和熄灭控制
硬件操作三步走

  1. 定义配置寄存器、数据寄存器指针,并进行地址映射,如下图
    定义寄存器指针并地址映射2.编写驱动程序led_open、led_write
    编写led_open led_write3. 编写main函数,测试
    led_drv_ts的main函数编写
    测试图,没问题,on时三个LED都亮了,off时都熄灭
    led驱动测试
4 总结
驱动程序 = 软件架构 + 硬件操作
软件架构如前面原理讲解的第二张图
硬件操作 = 1.看原理图,确定引脚   + 2.看手册,确定操作流程  + 3 编程
(驱动程序的硬件操作与单片机的硬件操作主要区别:驱动程序操作虚拟地址,需进行地址映射,而单片机直接操作物理地址)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值