驱动,是指驱动计算机里软件的程序。驱动程序全称设备驱动程序,是添加到操作系统中的特殊程序,其中包含有关硬件设备的信息。驱动程序是操作系统与硬件连接的桥梁。
设备驱动最通俗的解释就是“驱使硬件设备行动”。驱动与底层硬件直接打交道,按照硬件设备的具体工作方式,读写设备的寄存器,完成设备的轮询、中断处理、DMA通信,进行物理内存向虚拟内存的映射等,最终让通信设备能收发数据,让显示设备能显示文字和画面,让存储设备能记录文件和数据。
驱动程序是应用层和硬件层的连接桥梁,应用层只管完成应用逻辑开发和界面设计,驱动层则处理硬件配置,实现应用层相关接口函数。
杂项设备:字符设备类的一种,是除了上述输入设备、帧缓冲设备、RTC设备后的其它设备,例LED设备,由于不好归结于上述分类,则可将LED设备按杂项设备类进行注册。杂项设备主设备号为10。
在生成的设备节点中,主设备号用来区分设备类。如字符设备中杂项设备主设备号为10,帧缓冲设备主设备号为29;次设备号用来区分这个类中的具体硬件。
开发平台:Ubuntu18.04
编译器:arm-linux-gcc
硬件平台:tiny4412基于Cortex-A9 4核1.5GHZ
开发板内核:Linux3.5
#include
#include
/*驱动初始化*/
static int __init wbyq_hello_module_init(void)
{
printk("驱动入口,驱动注册成功\n");
return 0;
}
/*驱动释放*/
static void __exit wbyq_hello_module_cleanup(void)
{
printk("驱动出口,驱动注销成功\n");
}
module_init(wbyq_hello_module_init);//驱动入口函数
module_exit(wbyq_hello_module_cleanup);//驱动出口函数
MODULE_LICENSE("GPL");//驱动注册协议
MODULE_AUTHOR("it_ashui");
MODULE_DESCRIPTION("Exynos4 hello Driver");
KER_ADD=/home/wbyq/src_pack/linux-3.5 #linux3.5内核的Makefile位置
all:
make -C $(KER_ADD) M=`pwd` modules #通过调用linux3.5内核的Makefile文件进行编译
cp ./*.ko /home/wbyq/src_pack/rootfs/code #将驱动文件拷贝到code目录下
make -C $(KER_ADD) M=`pwd` modules clean #清空文件
obj-m +=hello_drv.o #添加依赖文件
[root@wbyq code]# insmod hello_drv.ko #安装驱动
[ 4684.795000] 驱动入口,驱动注册成功
[root@wbyq code]# modinfo hello_drv.ko #查看驱动详细信息
modinfo: can't open '/lib/modules/3.5.0-FriendlyARM/modules.dep': No such file or directory
[root@wbyq code]# mkdir /lib/modules/3.5.0-FriendlyARM/ -p
[root@wbyq code]# touch /lib/modules/3.5.0-FriendlyARM/modules.dep
[root@wbyq code]# lsmod #查看动态安装的驱动
hello_drv 612 0 - Live 0xbf000000 (O)
[root@wbyq code]# rmmod hello_drv.ko #注销驱动
[ 5610.635000] 驱动出口,驱动注销成功
蜂鸣接口:BP1 – GPD0_0 高电平驱动。
GPD0_CON:0x1140 0000+0x00A0 --配置寄存器
GPD0_DAT:0x1140 0000+0x00A4 --数据寄存器
1.调用驱动注册和注销函数,在驱动入口函数中实现BEEP硬件接口配置。在驱动出口函数中完成硬件资源释放。
2.通调用杂项设备驱动框架完成杂项设备注册,注册成功后在/dev生成beep的设备节点。完成应用层相关接口函数编写。
#include
#include
#include
#include
#include
/*驱动初始化*/
unsigned int *GPDO_CON;
unsigned int *GPDO_DAT;
int beep_open(struct inode *inode, struct file *file)/*对应应用层open函数*/
{
printk("open函数调用成功\n");
return 0;
}
int beep_release(struct inode *inode, struct file *file) /*对应应用层close*/
{
printk("releasse函数调用成功\n");
*GPDO_DAT&=~(1<<0);
return 0;
}
ssize_t beep_read(struct file *file, char __user * data, size_t size, loff_t *offset)/*对应应用层read*/
{
int *p=(int *)data;
*p=123;
printk("read函数调用成功\n");
return 4;
}
ssize_t beep_write(struct file *file, const char __user *data, size_t size, loff_t *offset)/*对应应用层write*/
{
char buff[20];
memcpy(buff,data,size);
buff[size]='\0';
printk("buff=%s\n",buff);
if(strcmp(buff,"beep_on")==0)//开蜂鸣器
{
*GPDO_DAT|=1<<0;
}
else if(strcmp(buff,"beep_off")==0)//关蜂鸣器
{
*GPDO_DAT&=~(1<<0);
}
return size;//返回写入成功的字节数
}
/*文件操作集合结构体*/
static struct file_operations beep_fops=
{
.owner = THIS_MODULE,/*当前模块*/
.open = beep_open,
.release = beep_release,
.read =beep_read,
.write =beep_write
};
/*杂项设备结构体*/
static struct miscdevice tiny4412_beep = {
.minor = MISC_DYNAMIC_MINOR,//次设备号,255表示由系统自动分配
.name = "beep", /*在/dev生成的设备节点名字*/
.fops = &beep_fops,
};
static int __init wbyq_beep_init(void)
{
printk("驱动入口,驱动注册成功\n");
/*蜂鸣器配置*/
GPDO_CON=ioremap(0x114000A0, 4);//将GPDO_CON物理地址转换虚拟地址
GPDO_DAT=ioremap(0x114000A4, 4);//将GPDO_DAT物理地址转换虚拟地址
/*蜂鸣器*/
*GPDO_CON&=0xfffffff0;//清除当前GPD0_0的配置
*GPDO_CON|=0x00000001;//设置为输出模式
/*注册杂项设备:在/dev下生成设备节点,实现应用层接口函数*/
misc_register(&tiny4412_beep);
return 0;
}
/*驱动释放*/
static void __exit wbyq_beep_cleanup(void)
{
printk("驱动出口,驱动注销成功\n");
/*注销杂项设备*/
misc_deregister(&tiny4412_beep);
iounmap(GPDO_CON);
iounmap(GPDO_DAT);
}
module_init(wbyq_beep_init);//驱动入口函数
module_exit(wbyq_beep_cleanup);//驱动出口函数
MODULE_LICENSE("GPL");//驱动注册协议
MODULE_AUTHOR("it_ashui");
MODULE_DESCRIPTION("Exynos4 beep Driver");
杂项设备主设备为10,次设备可填值为0~254;当次设备号填255时表示有系统分配。
Linux下一切皆文件,操作设备就和操作文件一样。只需要open打开设备、读写设备、操作完成关闭设备即可。
#include
#include
#include
#include
#include
#include
int main()
{
/*1.打开设备*/
int fd=open("/dev/beep",2);
if(fd<0)
{
printf("/dev/beep 设备打开失败\n");
return 0;
}
int data=0;
int size;
/*读写文件*/
size=read(fd,&data,4);
printf("size=%d,data=%d\n",size,data);
while(1)
{
write(fd,"beep_on",strlen("beep_on"));//开蜂鸣器
sleep(1);
write(fd,"beep_off",strlen("beep_off"));//关蜂鸣器
sleep(1);
}
close(fd);//关闭文件
}
在Linux内核中,设备节点的访问是通过主设备+次设备号的组合来完成的,占32位,主设备号是20 ~ 31位。次设备号是0 ~ 19位。