硬件平台:AM335x
编写驱动分下面几步:
a -- 查看原理图、数据手册,了解设备的操作方法;
b -- 在内核中找到相近的驱动程序,以它为模板进行开发,有时候需要从零开始;
c -- 实现驱动程序的初始化:比如向内核注册这个驱动程序,这样应用程序传入文件名,内核才能找到相应的驱动程序;
d -- 设计所要实现的操作,比如 open、close、read、write 等函数;
e -- 实现中断服务(中断不是每个设备驱动所必须的);
f -- 编译该驱动程序到内核中,或者用 insmod 命令加载;
g-- 测试驱动程序;
我们以点灯驱动程序作为示例:
第一步,当然是查看原理图,查看手册,找到相应寄存器;
通信指示灯为例,当GPIO3_20输出低电平时,COM灯亮。
因此,我们驱动要做的任务是:
- a. 将gpio3_20设置为输出模式;
配置GPIO_OE[32:0](0x481A_E134)的位[20]都等于0(输出模式)
- b. 设置gpio3_20的状态寄存器,控制引脚输出高低电平;
配置GPIO_DATEOUT[32:0](0x481A_E13C)的位[20]来控制亮灭;
查看am335x白皮书:AM335x Technical Reference Manual 手册 <2 Memory Map> 章节 [ARM Cortex-A8 Memory Map] 表,gpio3 物理基地址为 0x481A_E000:
点击GPIO3(上图红框),查看寄存器列表:
首先将对应引脚设置为输出引脚,看输出寄存器的描述:
所以需要将将GPIO_OE寄存器的第20位写0;(输出模式)
接下来对引脚状态的设定:
拉高:GPIO3_DATAOUT |= (1<<20)
拉低:GPIO3_DATAOUT &= ~(1<<20)
这里要注意:arm体系架构是io内存,必须要映射 ioremap( ); 其作用是物理内存向虚拟内存的映射。
第二步,写代码
在字符设备模型上添加我们的具体硬件操作,关于字符设备模型的编写请移步:Linux 字符设备驱动模型。
- 添加全局变量:
volatile unsigned long *GPIO3_OE=NULL;
volatile unsigned long *GPIO3_DATAOUT =NULL;
- 入口函数中使用ioremap()映射虚拟地址:
GPIO3_OE = ioremap(0x481AE134, sizeof(GPIO3_OE)); //ioremap:物理地址映射,返回虚拟地址
//GPIO3_DATAOUT = (volatile unsigned long *)ioremap(0x481AE13C, GPIO3_OE);
GPIO3_DATAOUT = GPIO3_OE+2;
- 出口函数中注销虚拟地址:
iounmap(GPIO3_OE); //注销虚拟地址
- open函数中添加配置GPIO3_OE:
*GPIO3_OE &= ~(1<<20);
- write函数中添加拷贝应用层数据,然后来控制GPIO3_DATAOUT:
/*copy_to_user():将数据上给用户*/
copy_from_user(&val,buf,count); //从用户(应用层)拷贝数据
if(val==1) //点灯(低电平亮)
{
*GPIO3_DATAOUT &= ~(1<<20)
}
else //灭灯
{
*GPIO3_DATAOUT |= (1<<20)
}
接下来编写测试程序:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
/*
* ledtest on
* ledtest off
*/
int main(int argc,char **argv) //argc:参数个数,argv数组
{
int fd1, fd2;
int val=1;
fd1 = open("/dev/led",O_RDWR); //打开/dev/xxx设备节点
if(fd1<0) //无法打开,返回-1
printf("can't open%d!\n", fd1);
if(argc!=2)
{
printf("Usage:\n");
printf("%s <on|off>",argv[0]);
return 0;
}
if(strcmp(argv[1],"on")==0) //开灯
{
printf("led on...\n");
val=1;
}
else //关灯
{
printf("led off...\n");
val=0;
}
write(fd1, &val, 4);
return 0;
}
网友评论