驱动开发(六):应用层通过文件系统与内核层交互
目录
功能概述
模拟饮水机按键控制开始停止功能,并根据时间控制出水量 (红灯表示出水状态,绿灯表示停止状态)。应用层程序模拟刷卡或者扫码计费,如应用层(向驱动)传递数据 3 那么就表示付费3元。
详情
1、检测终端输入数据 (有包头包尾的数据处理功能)
输入格式 :包头 id 金额 包尾
eg: (0x55 0x01 0x02 0xff)
包头:0x55
i d:0x01
金额2元:0x02
包尾:0xff
2、本地保存用户数据(保存id和消费金额)。
3、驱动层上电红灯闪烁,当检测到应用层安装以后绿灯常亮。
4、用户刷卡或扫码前(接收到应用层数据前)按键无效,提示“请先刷卡”
5、接收到金额数据后,判断按键(开始、停止)
6、开始灌水亮红灯,售水结束亮绿灯并且蜂鸣器响一段时间表示水停。
7、无操作检测,按下停止后10s内没有其他操作则售水结束,退还剩余金额。
驱动代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <asm/io.h>
#include <linux/uaccess.h>
irqreturn_t handler(int irqno, void *dev);
void timer_function(unsigned long data);
void timer_function2(unsigned long data);
void timer_function3(unsigned long data);
void timer_function4(unsigned long data);
void timer_function5(unsigned long data);
#define modname "lianxi" //驱动文件名字
unsigned int major=0; //主设备号
#define gpiono(m, n) m * 32 + n
#define gpio_b8 (gpiono(1, 8))
#define gpio_b16 (gpiono(1, 16))
int gpiono[] = {gpio_b8, gpio_b16};
char *name[] = {"gpio_it_8", "gpio_it_16"};
#define red 0xc001a000
#define green 0xc001e000
#define blue 0xc001b000
unsigned int *red_base = null;
unsigned int *green_base = null;
unsigned int *blue_base = null;
#define beep 0xc001c000
unsigned int *beep_base = null;
struct timer_list mytimer; //按键
struct timer_list mytimer2; //扣费
struct timer_list mytimer3; //闪灯
struct timer_list mytimer4; //蜂鸣器
struct timer_list mytimer5; //无操作检测
//******************************读写操作函数**************
int copy_size = 0;
char kbuf[128] = {0};
ssize_t mycdv_read(struct file *file, char __user *ubuf, size_t len, loff_t *loff)
{
if (len > sizeof(kbuf)) //len是用户需要的,有可能大于内核传的
{
len = sizeof(kbuf); //大于的时候,有多少就给它多少
}
copy_size = copy_to_user(ubuf, kbuf, len);
if (copy_size)
{
printk(kern_err "copy_to_user error\n");
return copy_size;
}
return 0;
}
ssize_t mycdv_write(struct file *file, const char __user *ubuf, size_t len, loff_t *lo)
{
if (len > sizeof(kbuf))
{
len = sizeof(kbuf);
}
copy_size = copy_from_user(kbuf, ubuf, len);
if (copy_size)
{
printk(kern_err "copy_from_user error\n");
return copy_size;
}
if( kbuf[0] != 0x55 || kbuf[3] != 0xff) //判断数据格式是否正确
{
printk(kern_err "数据格式错误,请重新输入\n");
return 0;
}
kbuf[4]=0xff;
printk(kern_err "等待灌装......\n");
return 0;
}
int mycdv_open(struct inode *inode, struct file *file)
{
printk(kern_err "mycdv open ok\n");
del_timer(&mytimer3); //***********停止闪烁
*(red_base) &= ~(1 << 28); //**关红灯
*(green_base) |= 1 << 13; //****开绿灯
return 0;
}
int mycdv_release(struct inode *inode, struct file *file)
{
printk(kern_err "mycdv release ok\n");
*(green_base) &=~(1 << 13); //****************关绿灯
mytimer3.expires = jiffies + 10; //****************再闪红灯
mytimer3.function = timer_function3;
mytimer3.data = 0;
init_timer(&mytimer3);
add_timer(&mytimer3);
return 0;
}
const struct file_operations fops=
{
.read = mycdv_read,
.write = mycdv_write,
.open = mycdv_open,
.release = mycdv_release,
};
struct class *cls;
struct device *dev;
int ret;
int i;
//入口 申请资源
static int __init hello_init(void)
{
//*********************** 创建字符设备*************
major = register_chrdev(major, modname, &fops); //注册字符设备
if(major<0)
{
printk(kern_err "register chrdev error\n");
return major;
}
cls = class_create(this_module, modname); //创建一个class类型的对象,向用户空间提交目录信息(内核目录的创建)
if(is_err(cls))
{
return ptr_err(cls);
}
dev = device_create(cls, null,mkdev(major,0), null,modname ); //向用户空间提交文件信息
if(is_err(dev ))
{
return ptr_err(dev);
}
//****************************初始化定时器***************
mytimer.expires = jiffies + 10;
mytimer.function = timer_function;
mytimer.data = 0;
init_timer(&mytimer); //内核帮你填充你未填充的对象
add_timer(&mytimer); //开启一次定时器
mytimer3.expires = jiffies + 10;
mytimer3.function = timer_function3;
mytimer3.data = 0;
init_timer(&mytimer3); //内核帮你填充你未填充的对象
add_timer(&mytimer3); //开启一次定时器
mytimer4.expires = jiffies + 10;
mytimer4.function = timer_function4;
mytimer4.data = 0;
init_timer(&mytimer4);
add_timer(&mytimer4);
//***************************开启按键中断************
for (i = 0; i < sizeof(gpiono) / sizeof(int); i++)
{
ret = request_irq(gpio_to_irq(gpiono[i]), handler, irqf_trigger_falling, name[i], null);
if (ret != 0)
{
printk(kern_err "%s request irq err\n", name[i]);
return ret;
}
}
//***************************初始化led、蜂鸣器******
red_base = ioremap(red, 40); //关闭红灯
if (null == red_base)
{
printk("red ioremap err\n");
}
*(red_base) &= ~(1 << 28);
*(red_base + 1) |= 1 << 28;
*(red_base + 9) &= ~(3 << 24);
blue_base = ioremap(blue,40); //关闭蓝灯
if (null == blue_base)
{
printk("blue ioremap err\n");
}
*(blue_base) &= ~(1<<12);
*(blue_base+1) |= 1<<12;
*(blue_base+8) |= 2<<24;
green_base = ioremap(green, 40); //关绿灯
if (null == green_base)
{
printk("green ioremap err\n");
}
*(green_base) &=~(1 << 13);
*(green_base + 1) |= 1 << 13;
*(green_base + 8) &= ~(3 << 26);
beep_base = ioremap(beep,40); //关蜂鸣器
if (null == beep_base)
{
printk("beep ioremap err\n");
}
*(beep_base) &= ~ (1<<14) ;
*(beep_base+1) |= 1<<14;
*(beep_base+8) |= 1<<28;
return 0;
}
//出口 释放资源
static void __exit hello_exit(void)
{
//********************还原led、蜂鸣器************
*(red_base) |= (1 << 28);
*(green_base) |= 1<<13;
*(blue_base) |=(1<<12);
*(beep_base) &= ~ (1<<14) ;
iounmap(red_base);
iounmap(green_base);
iounmap(blue_base);
iounmap(beep_base);
//*********************关闭按键、定时器中断***********
for (i = 0; i < sizeof(gpiono) / sizeof(int); i++)
{
free_irq(gpio_to_irq(gpiono[i]), null);
}
del_timer(&mytimer);
del_timer(&mytimer2);
del_timer(&mytimer3);
del_timer(&mytimer4);
del_timer(&mytimer5);
//*******************************注销字符设备*****************
device_destroy(cls, mkdev(major, 0));
class_destroy(cls);
unregister_chrdev(major, modname);
}
module_init(hello_init); //入口
module_exit(hello_exit); //出口
module_license("gpl"); //许可证
//*****************按键中断处理函数***********
irqreturn_t handler(int irqno, void *dev)
{
mod_timer(&mytimer,jiffies + 30);
return irq_handled;
}
//*****************定时器中断处理函数********
void timer_function(unsigned long data) //***********检测按键
{
int b8 = gpio_get_value(gpio_b8);
int b16 = gpio_get_value(gpio_b16);
if (b8 == 0) //****************暂停按键按下
{
if(kbuf[4]!=0xff)
{
printk(kern_err "请先刷卡缴费\n");
return;
}
printk(kern_err "暂停\n");
*(green_base) |=1 << 13; //****************开绿灯
*(red_base) &= ~(1 << 28);//********关红灯
mytimer5.expires = jiffies + 10000; //****开启无操作检测10s
mytimer5.function = timer_function5;
mytimer5.data = 0;
init_timer(&mytimer5);
add_timer(&mytimer5);
del_timer(&mytimer2);
}
if (b16 == 0) //*****************开始按键按下
{
if(kbuf[4]!=0xff)
{
printk(kern_err "请先刷卡缴费\n");
return;
}
printk(kern_err "开始\n");
del_timer(&mytimer5); //**********关闭无操作检测
*(green_base) &=~(1 << 13); //****************关绿灯
*(red_base) |= 1 << 28; //********开红灯
printk(kern_err "当前余额:%d\n",kbuf[2]);
mytimer2.expires = jiffies + 1000;
mytimer2.function = timer_function2;
mytimer2.data = 0;
init_timer(&mytimer2); //内核帮你填充你未填充的对象
add_timer(&mytimer2); //开启一次定时器
}
}
void timer_function2(unsigned long data) //***********按秒扣费
{
if(kbuf[2]==0)
{
del_timer(&mytimer2);
printk(kern_err "灌水结束\n");
*(green_base) |=1 << 13; //****************开绿灯
*(red_base) &= ~(1 << 28); //********关红灯
kbuf[4]=0x00; //**********结束标志
*(beep_base) |= 1<<14 ;//******开蜂鸣器
mod_timer(&mytimer4,jiffies + 1000);//*****1s后关闭
return;
}
kbuf[2]=kbuf[2]-1;
printk(kern_err "当前余额:%d\n",kbuf[2]);
mod_timer(&mytimer2,jiffies + 1000);
}
int led_flag=0;
void timer_function3(unsigned long data) //***********闪红灯 0.3s
{
led_flag++;
if(led_flag==2)
led_flag=0;
if(led_flag==0)
*(red_base) &= ~(1 << 28);
if(led_flag==1)
*(red_base) |= 1 << 28;
mod_timer(&mytimer3,jiffies + 300);
}
void timer_function4(unsigned long data) //***********关蜂鸣器
{
*(beep_base) &= ~ (1<<14) ;
}
void timer_function5(unsigned long data) //***********长时间无操作
{
printk(kern_err "售水结束,余额:%d\n",kbuf[2]);
kbuf[4]=0x00;
}
应用层代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
char buf[128]={0};
char history[128]={0};
int main(int argc, char const *argv[])
{
int fd;
int fd_history;
fd = open("/dev/lianxi",o_rdwr); //打开驱动文件
if(fd<0)
{
perror("open err");
return -1;
}
fd_history = open("history.txt",o_wronly | o_append | o_creat ,0777 ); //打开历史记录文件
if(fd_history<0)
{
perror("open err");
return -1;
}
int old,new;
while (1)
{
printf("请输入id和金额 eg:(0x55 0x01 0x02 0xff) 表示包头 0x55 id 0x01 金额 2元0x02 包尾 0xff)\n");
scanf("%x %x %x %x",&buf[0],&buf[1],&buf[2],&buf[3]);
old=buf[2];
write(fd,buf,sizeof(buf)); //向驱动写入数据
memset(buf,0,sizeof(buf));
do{
read(fd,buf,sizeof(buf)); //等待售水结束
}while (buf[4]!=0x00);
new=buf[2];
sprintf(history,"id:0x%02hx consumption: %d\n",buf[1],old-new); //拼接历史记消息,写入history.txt
write(fd_history, history, strlen(history));
}
close(fd_history);
close(fd);
return 0;
}
发表评论