驱动开发相关文章:
驱动开发(六):应用层通过文件系统与内核层交互
目录
功能概述
模拟饮水机按键控制开始停止功能,并根据时间控制出水量 (红灯表示出水状态,绿灯表示停止状态)。应用层程序模拟刷卡或者扫码计费,如应用层(向驱动)传递数据 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;
}