字符设备驱动之点亮LED

时间:2021-02-01 14:41:58

这次弄一个复杂一点的驱动测试,暂且认为是LED驱动吧!应用程序借用驱动程序控制4个LED灯亮灭。

对于驱动程序的编写,首先是构建框架,打通LED与应用程序之间的关系。本文在《字符设备驱动之"Hello, World!"》的基础上捣鼓,框架已经有了。然后便是完善对硬件的操作。

具体下文细细道来。

此LED驱动程序首先能够实现的功能是:调用应用程序app_test1,能够实现LED灯亮灭控制。譬如在终端输入“app_test1 on”,灯亮;输入“app_test1 off”,灯灭。

对于完善硬件的操作,首先阅读原理图和S3C2440 datasheet,然后编写代码。

通过原理图,可以知道开发板四个控制LEDIO口,如下:

字符设备驱动之点亮LED       字符设备驱动之点亮LED

然后阅读S3C2440 datasheet,相关GPIO控制寄存器信息如下:

字符设备驱动之点亮LED

更多信息参考datasheet,接下来就是编写代码了。

1.首先添加两个指向GPBCON、GPBDAT控制寄存器的全局变量,如下:

字符设备驱动之点亮LED

显然需要将控制寄存器的地址映射到这两个变量上。

2. 在入口函数(test1_init)加上两行代码,如下:

字符设备驱动之点亮LED

内核在初始化阶段完成了物理地址到虚拟地址的映射工作,但我们只能知道的GPBCON的物理地址,所以需要实现物理地址到虚拟地址的映射,ioremap就是专门干这事儿的函数,它的具体解析此处不详细阐述。

ps:gpbcon和gpbdat都是定义为long类型的指针变量,即它们都是指向32bit数据的地址指针,“gpbdat = gpbcon + 1;”中的1的单位是long数据类型的长度(4字节),而不是字节。

3.入口完成映射,同样,需要在出口解除映射,在出口函数(test1_exit)加上一行代码,如下:

字符设备驱动之点亮LED

对IO口写操作有两个步骤要做:一是设置IO口状态,二是赋值。前者操作GPBCON寄存器,后者操作GPBDAT寄存器。前者在驱动的open函数里实现,后者在驱动的write函数里实现。

4.在open函数(test1_open)添加两行代码如下:

字符设备驱动之点亮LED

ps:这两行操作GPIO的代码比较常见,相较于直接赋值,这样做的好处是不影响其他不相干的IO状态,本文需要操作的IO口是GPB5、GPB6、GPB7、GPB8,首先把它们的相关位(每个IO口对应2bit)给清零,然后对相关位赋值01,使其处于输出状态。

5.在write函数(test1_write)添加代码如下:

字符设备驱动之点亮LED

对于test1_write函数,应用程序在执行系统调用write的时候调用它,系统调用write有传递参数,buf就是传递的一个参数,关于其他参数的意义,此处略过。值得注意的是,获取系统调用write的参数不知直接用“val = buf;”来实现,因为它们不是同一个函数,中间必有蹊跷,值得深挖。

ps:对于GPBCON和GPBDAT的赋值,当然有其他的安排方式,譬如都在write函数里实现,但似乎按本文安排更显逻辑性。

6.测试程序

字符设备驱动之点亮LED

ps:对于这个应用程序,当执行语句是“./app_test1”时会进入“if(argc != 2)...”语句,因为此时参数只有一个app_test1也算一个参数!