驱动文件编写(基于框架)
基本框架
#include <linux/fs.h> //file_operations声明
#include <linux/module.h> //module_init module_exit声明
#include <linux/init.h> //__init __exit 宏定义声明
#include <linux/device.h> //class devise声明
#include <linux/uaccess.h> //copy_from_user 的头文件
#include <linux/types.h> //设备号 dev_t 类型声明
#include <asm/io.h> //ioremap iounmap的头文件
static struct class *relay_class;
static struct device *relay_class_dev;
static dev_t devno; //设备号
static int major =235; //主设备号
static int minor =0; //次设备号
static char *module_name="relay"; //模块名
static int relay_open(struct inode *inode,struct file *file)
{
printk("relay_open\n"); //内核的打印函数和printf类似
return 0;
}
static ssize_t relay_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
copy_from_user(&usercmd,buf,count);
return 0;
}
static struct file_operations relay_fops = {
.owner = THIS_MODULE,
.open = relay_open,
.write = relay_write,
};
int __init relay_drv_init(void)
{
int ret;
devno = MKDEV(major,minor); //创建设备号
ret = register_chrdev(major, module_name,&relay_fops); //注册驱动 告诉内核,把这个驱动加入到内核驱动的链表中
relay_class=class_create(THIS_MODULE,"myfirstdemo");
relay_class_dev =device_create(relay_class,NULL,devno,NULL,module_name); //创建设备文件
return 0;
}
void __exit relay_drv_exit(void)
{
device_destroy(relay_class,devno);
class_destroy(relay_class);
unregister_chrdev(major, module_name); //卸载驱动
}
module_init(relay_drv_init); //入口
module_exit(relay_drv_exit);
MODULE_LICENSE("GPL v2");
核心部分(自定义指令格式)
核心思想:继电器有4路,开关各一条指令,全开全关各一条指令,总共10条指令,根据不同指令实现不同操作,推荐使用switich case来选择,指令格式推荐:char型,ascll码形式或者16进制形式,int型10进制或者16进制。
将switch case写入以下函数。这里采用int型16进制(只用到2位16进制数),赋予一些简单的可以理解的含义(也可以直接粗暴的选0-9),第一位表示继电器通道,0表示所有通道,1表示通道1,以此类推,第二位表示高地电平,1表示高电平,0表示低电平。只是人为赋予的含义!!!!
执行内容:根据选择的树莓派IO口,以及人为规定的指令,来让对应寄存器的对应位 置1(SET寄存器和CLR寄存器)
例如:树莓派IO口选择的是5 6 13 19,指令0x01
case 0x01:
*GPSET0 = 0x1<<5;
*GPSET0 = 0x1<<6;
*GPSET0 = 0x1<<13;
*GPSET0 = 0x1<<19;
static ssize_t relay_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
int usercmd = 0x01;
copy_from_user(&usercmd,buf,count);
switch(usercmd){
case 0x01:
break;
case 0x11:
break;
case 0x21:
break;
case 0x31:
break;
case 0x41:
break;
case 0x00:
case 0x10:
break;
case 0x20:
break;
case 0x30:
break;
case 0x40:
break;
}
return 0;
}
驱动加载省略1万字
驱动测试
编写测试程序
注意:内核switch case使用的是16进制选择,测试程序输入也要16进制输入(scanf 格式控制符%x)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int cmd = 0x01;
int size = 0;
int fd = open("/dev/relay",O_RDWR);
if(fd == -1){
perror("open");
exit(-1);
}
while(1){
printf("input cmd : ");
scanf("%x",&cmd);
printf("your cmd : %x\n",cmd);
switch(cmd){
case 0x00:
case 0x10:
case 0x20:
case 0x30:
case 0x40:
case 0x01:
case 0x11:
case 0x21:
case 0x31:
case 0x41:
write(fd,&cmd,4);
break;
default:
printf("cmd not exist\n");
break;
}
}
return 0;
}