linux驱动之一、LED驱动(驱动代码小结附:github代码链接)


前言:最近在学习韦东山老师的arm驱动部分教学,本文记录一下自己对最基本的驱动框架结构的理解,以及最基本的led驱动实现的方法。本博客以米尔科技的rico board开发板为例,完成rico board的led驱动代码的实现过程。

备注:完整代码直接跳转到《led驱动代码(完整代码,亲测有效)》章节或跳转至《github工程链接》章节,中间各个小结的代码为实现过程(不完整),无法正确编译。


一、相关知识点(涉及接口、结构体、调用关系等)

一个软件系统可以分为以下四层:应用程序、库、内核、驱动,借用韦老师一副图,如下图:
即:应用层开发的功能,各个接口会通过库、通过内核调用到底层驱动程序的对应接口,从而执行对应的功能。
在这里插入图片描述

1.1 裸机开发步骤与驱动开发过程对比

1.1.1 裸机开发步骤

裸机开发时,我们需要按照如下过程进行开发。

步骤:
1、看电路图,查清楚led在哪个管脚,对应哪个GPIO口
2、看芯片手册,查看需要配置的相关寄存器地址——(主要查看需要配置哪些寄存器可以将GPIO配置成输出模式,地址可以在map地址映射章节找到)
3、看芯片手册,查清楚怎么样能将GPIO配置成输出高电平或低电平。
4、编译代码,烧写到板卡中,重新上电

1.1.2 Linux系统下LED驱动开发步骤

备注:裸机开发与带操作系统的驱动开发对寄存器的操作不同,裸机开发直接操作寄存器地址即可,而带linux操作系统时,必须要进行地址映射(有相关接口可以直接使用),映射完成后,再进行操作即可。

步骤:
1、撰写基本字符驱动框架代码(字符驱动代码框架实现步骤详见第2节:基本驱动框架实现流程
2、看电路图,查清楚led在哪个管脚,对应哪个GPIO口
3、看芯片手册,查看需要配置的相关寄存器地址——(主要查看需要配置哪些寄存器可以将GPIO配置成输出模式,地址可以在map地址映射章节找到)
4、在驱动入口函数与出口函数中完成寄存器地址映射与取消映射
5、将led操作功能代码填充到驱动框架中

1.2 预备知识:写驱动时涉及的接口、结构体、宏等

name 含义
struct file_operations 结构体:该结构体中告诉每一个接口成员指向某个函数指针,指向的函数指针则为要实现的功能
struct class
module_init(lxxx_init); 注册驱动入口接口
module_exit(xxx_exit); 注册驱动出口接口
MODULE_LICENSE(“Dual BSD/GPL”); 声明
xxx_init() 驱动入口函数
xxx_exit() 驱动出口函数
register_chrdev() 注册模块,告诉内核让内核知道有这个模块,并且返回值为自动分配的主设备号
unregister_chrdev() 卸载驱动程序,告诉内核
class_create() 创建一个类
device_create(major, …); 创建一个设备,使用该接口创建设备以后,在板卡中insmod xxx之后,会自动生成/dev/设备,并且会将major主设备号分配给该设备
device_destroy() 删除设备,删除设备号
class_destroy 删除类
ioremap(); 映射寄存器地址
iounmap(); 删除映射关系

二、基本驱动框架

本小结,作为预备知识,先对字符驱动基本框架做一个简要描述,具体从两个方面进行描述,分别是:

  • 2.1 基本驱动框架实现流程(思路)
  • 2.2 基本驱动框架结构

2.1 基本驱动框架实现流程(思路)

撰写驱动框架时,按照以下驱动思路来写即可,后续代码实现,即可对比参照此流程来分析代码。

步骤如下:
1、实现结构体:【xxx_fops】。该结构体内部制定了相关的功能代码接口,从而应用能够知道哪个对应功能在哪个接口中实现。
2、实现步骤1中的相关函数(读写函数)框架,此时不需要实现函数具体细节,函数内部为空即可。
3、驱动入口函数:【xxx_init()】

xxx_init() {
    (a)在函数前面定义全局变量【int major】,下一步用该变量接收系统自动分配的主设备号。
    (b)驱动入口函数中实现注册函数,返回值为系统自动分配的主设备号:major = register_chrdev(0,设备名称,结构体);
}

4、驱动入口修饰函数:【modules_init();】
5、驱动出口函数:【xxx_exit();】

xxx_exit() {
    (a)驱动出口函数中实现卸载函数:【unregister_chrdev(主设备号,设备名称)】
}

6、驱动出口修饰函数:【modules_exit();】
7、给sysfs文件系统提供更多驱动信息,这样上层应用因为【udev机制】便可自动创建设备节点,跟底层驱动代码中自动生成的主设备号保持一致,从而关联起来。(备注:udev机制个mdev机制基本可以认为是同一个东西,mdev是udev的简化版本)

(a)定义结构体class:【static struct class *xxx_class;】
(b)驱动入口函数xxx_init()中增加两个接口:【创建类class_create()】和【创建设备device_create()】
(c)驱动出口函数xxx_exit()中增加两个接口:【删除设备device_destroy()】和【删除类class_destroy()】
(d)注意:步骤b和步骤c中两个接口顺序是相反的!!

8、添加Licence:【MODULE_LICENSE(“Dual BSD/GPL”);】。

2.2 基本驱动框架结构

/* 头文件 */
#include xxxxxxx

/* 功能接口1 */
x1()
{
    ...
}

/* 功能接口2
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值