项目中需要基于海思Hi3559AV100开发韦根功能。韦根分为韦根输出和韦根输入,这篇文章讲韦根输入程序,下一篇文章讲韦根输出程序。
首先,必须要感谢https://blog.csdn.net/qq_37803273/article/details/84993571 《RK3288平台韦根接收》的作者的无私分享,文章中提供了源码(以下简称例程),很详细,我的代码也是以这篇文章中的代码为基础进行修正改进而得到的。
其次,必须说明,例程中还是有不少坑,在按照例程编写、修改和调试韦根输入代码的过程中踩了很多坑,最终才成为现在的程序。
最后,整个工程源码以附件形式放到网上,初始设定为0积分加载。
韦根输入代码如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#include
#include
#include
#define WIEGAND_MAJOR 250
#define TIMER_DELAY HZ/8
#define DEVICE_NAME "wiegand_in"
#define GPIO12_4 (12 * 8 + 4)
#define GPIO12_2 (12 * 8 + 2)
#define WG_DATA0 GPIO12_4
#define WG_DATA1 GPIO12_2
static int wiegand_dev_major = WIEGAND_MAJOR;
static bool dev_open_flag = false;
static bool overflow_flag = false;
struct wiegand_dev
{
char wiegand[34]; //wiegand-34
unsigned int data; //actual data
unsigned int count; //global counter
struct cdev cdev;
struct semaphore sem;
struct completion receive_completion;
struct timer_list wiegand_timer;
struct work_struct pen_event_work;
struct workqueue_struct *ts_workqueue;
int gpio_d0;
int gpio_d1;
int d0_irq;
int d1_irq;
};
struct wiegand_dev *wiegand_in_devp = NULL;
//使能中断
static void enable_irqs(void)
{
enable_irq(wiegand_in_devp->d0_irq);
enable_irq(wiegand_in_devp->d1_irq);
}
//禁止中断
static void disable_irqs(void)
{
disable_irq(wiegand_in_devp->d0_irq);
disable_irq(wiegand_in_devp->d1_irq);
}
static char convert_data(void)
{
int i;
int even = 0;
int odd = 0;
//偶校验
for(i=1; i<=16; i++)
{
if(wiegand_in_devp->wiegand[i] == 1)
even++;
}
//偶校验不通过的情况
//偶校验:当实际数据中1的个数为偶数的时候,这个校验位就是0,否则这个校验位就是1
if(even%2 != wiegand_in_devp->wiegand[0])
{
wiegand_in_devp->count = 0;
goto error;
}
//奇校验
for(i=17; i<=32; i++)
{
if(wiegand_in_devp->wiegand[i] == 1)
odd++;
}
//奇校验不通过的情况
//奇校验:当实际数据中1的个数为偶数的时候,这个校验位就是1,否则这个校验位就是0
if(odd%2 == wiegand_in_devp->wiegand[33])
{
wiegand_in_devp->count = 0;
goto error;
}
//奇偶校验通过,将韦根转换为实际数据
wiegand_in_devp->data = 0;
for(i=1; i<=32; i++)
{
wiegand_in_devp->data <<= 1;
wiegand_in_devp->data |= wiegand_in_devp->wiegand[i];
}
wiegand_in_devp->count = 0;
printk("data is: %#x\n", wiegand_in_devp->data);
return 0;
error:
printk("parity efficacy error\n");
return -1;
}
static void wiegand_do_timer(unsigned long arg)
{
int i = 0;
//虽然超时时间已到达,但还要等待维根数据传送完毕才进行转换
//wait_for_completion(&(wiegand_in_devp->receive_completion));
printk("%d\n", wiegand_in_devp->count);
for(i=0; icount; i++)
printk("%d ", wiegand_in_devp->wiegand[i]);
printk("\n");
//如果不是数据位不是34(由于中断中有限制,实际上不会超过,只能小于34),则返回全F代表出错
if(wiegand_in_devp->count != 34)
{
wiegand_in_devp->count = 0;
wiegand_in_devp->data = 0xFFFFFFFF; //全F代表出错
up(&wiegand_in_devp->sem);
overflow_flag = false;
return;
}
//关闭外部中断,防止wiegand_data在转换期间发生变化
disable_irqs();
//如果转换错误,则返回全F代表出错
if(convert_data() != 0)
{
wiegand_in_devp->data = 0xFFFFFFFF; //全F代表出错
}
overflow_flag = false;
up(&wiegand_in_devp->sem);
//恢复中断
enable_irqs();
}
static irqreturn_t wiegand_irq0(int irq, void *dev_id)
{
disable_irq_nosync(wiegand_in_devp->d0_irq);
udelay(5);
if(gpio_get_value(wiegand_in_devp->gpio_d0) == 0) //必须有这句话!因为有很多噪声会导致误进入中断,但实际上并没有数据
{
if(overflow_flag == false)
{
wiegand_in_devp->wiegand[wiegand_in_devp->count++] = 0;
//printk("0\n"); //调试的时候可以加上这句话,最终要封掉,中断中不要加入打印,否则影响接收数据
if(wiegand_in_devp->count == 1)
{
//init_completion(&wiegand_in_devp->receive_completion);
//接收到第1位数据的时候就启动定时器,相当于设置一个超时时间
wiegand_in_devp->wiegand_timer.expires = jiffies + TIMER_DELAY;
add_timer(&wiegand_in_devp->wiegand_timer); //添加(注册)定时器
}
else if(wiegand_in_devp->count > 34)
{
//complete(&(wiegand_in_devp->receive_completion));
overflow_flag = true;
}
}
}
//else
//{
//printk(KERN_ERR"wigand in d0 err\n");
//}
enable_irq(wiegand_in_devp->d0_irq);
return IRQ_HANDLED;
}
static irqreturn_t wiegand_irq1(int irq, void *dev_id)
{
disable_irq_nosync(wiegand_in_devp->d1_irq);
udelay(5);
if(gpio_get_value(wiegand_in_devp->gpio_d1) == 0) //必须有这句话!因为有很多噪声会导致误进入中断,但实际上并没有数据
{
if(overflow_flag == false)
{
wiegand_in_devp->wiegand[wiegand_in_devp->count++] = 1;
//printk("1\n"); //调试的时候可以加上这句话,最终要封掉,中断中不要加入打印,否则影响接收数据
if(wiegand_in_devp->count == 1)
{
//init_completion(&wiegand_in_devp->receive_completion);
//接收到第1位数据的时候就启动定时器,相当于设置一个超时时间
wiegand_in_devp->wiegand_timer.expires = jiffies + TIMER_DELAY;
add_timer(&wiegand_in_devp->wiegand_timer); //添加(注册)定时器
}
else if(wiegand_in_devp->count > 34)
{
//complete(&(wiegand_in_devp->receive_completion));
overflow_flag = true;
}
}
}
//else
//{
//printk(KERN_ERR"wigand in d1 err\n");
//}
enable_irq(wiegand_in_devp->d1_irq);
return IRQ_HANDLED;
}
//注册中断
static int request_irqs(void)
{
int ret;
if(!wiegand_in_devp)
return -1;
//映射操作的GPIO对应的中断号
wiegand_in_devp->d0_irq = gpio_to_irq(wiegand_in_devp->gpio_d0);
wiegand_in_devp->d1_irq = gpio_to_irq(wiegand_in_devp->gpio_d1);
//注册GPIO12_4(WG_DATA0)外部中断
ret = request_irq(wiegand_in_devp->d0_irq, wiegand_irq0, IRQF_SHARED | IRQF_TRIGGER_FALLING, "wiegand_data0", wiegand_in_devp);
if(ret)
{
printk(KERN_ERR"request wiegand_in_devp->d0_irq:%d, ret:%d failed\n", wiegand_in_devp->d0_irq, ret);
return -1;
}
//注册GPIO12_2(WG_DATA1)外部中断
ret = request_irq(wiegand_in_devp->d1_irq, wiegand_irq1, IRQF_SHARED | IRQF_TRIGGER_FALLING, "wiegand_data1", wiegand_in_devp);
if(ret)
{
printk(KERN_ERR"request wiegand_in_devp->d1_irq:%d, ret:%d failed\n", wiegand_in_devp->d1_irq, ret);
return -1;
}
printk(KERN_INFO"request irqs success\n");
return 0;
}
//释放注册的中断
static void free_irqs(void)
{
free_irq(wiegand_in_devp->d0_irq, wiegand_in_devp);
free_irq(wiegand_in_devp->d1_irq, wiegand_in_devp);
}
//设置相应GPIO引脚为输入
void gpio_set_input(void)
{
gpio_direction_input(wiegand_in_devp->gpio_d0); //设置GPIO12_4(WG_DATA0)为输入
gpio_direction_input(wiegand_in_devp->gpio_d1); //设置GPIO12_2(WG_DATA1)为输入
}
void gpio_init(void)
{
//注册要操作的GPIO编号
gpio_request(wiegand_in_devp->gpio_d0, "wiegand_d0");
gpio_request(wiegand_in_devp->gpio_d1, "wiegand_d1");
gpio_set_input();
}
//释放注册的GPIO编号
void gpio_deinit(void)
{
gpio_free(wiegand_in_devp->gpio_d0);
gpio_free(wiegand_in_devp->gpio_d1);
}
static int wiegand_in_open(struct inode *inode, struct file *filp)
{
if(dev_open_flag)
{
printk(KERN_ERR"wiegand in device has beed opened\n");
return -EBUSY;
}
dev_open_flag = true;
overflow_flag = false;
setup_timer(&wiegand_in_devp->wiegand_timer, wiegand_do_timer, 0);
memset(wiegand_in_devp->wiegand, 0x00, sizeof(wiegand_in_devp->wiegand));
wiegand_in_devp->count = 0;
//注册中断
request_irqs();
return 0;
}
static int wiegand_in_close(struct inode *inode, struct file *filp)
{
//释放注册的中断
disable_irqs();
free_irqs();
dev_open_flag = false;
overflow_flag = false;
del_timer_sync(&wiegand_in_devp->wiegand_timer);
return 0;
}
static ssize_t wiegand_in_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
if(down_interruptible(&wiegand_in_devp->sem))
{
printk(KERN_ERR"down wiegand_in_devp->sem error\n");
return -1;
}
printk(KERN_INFO"data:%#x\n", wiegand_in_devp->data);
return copy_to_user(buf, &wiegand_in_devp->data, sizeof(wiegand_in_devp->data));
}
static ssize_t wiegand_in_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
return 0;
}
static const struct file_operations wiegand_in_fops =
{
.owner = THIS_MODULE,
.read = wiegand_in_read,
.write = wiegand_in_write,
.open = wiegand_in_open,
.release = wiegand_in_close,
};
//设备驱动模块加载函数
static int __init wiegand_in_init(void)
{
int ret = 0;
dev_t devno = MKDEV(WIEGAND_MAJOR, 0);
printk(KERN_INFO"wiegand_in_init\n");
if(wiegand_dev_major) //申请设备号
ret = register_chrdev_region(devno, 1, DEVICE_NAME);
else //动态申请设备号
{
ret = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
wiegand_dev_major = MAJOR(devno);
}
if(ret < 0)
{
printk(KERN_ERR"register_chrdev_region or alloc_chrdev_region error\n");
return ret;
}
wiegand_in_devp = kzalloc(sizeof(struct wiegand_dev), GFP_KERNEL);
if(!wiegand_in_devp)
{
printk(KERN_ERR"kzalloc error\n");
unregister_chrdev_region(devno, 1);
ret = -ENOMEM;
return ret;
}
//初始化wiegand_in_devp
wiegand_in_devp->count = 0;
cdev_init(&(wiegand_in_devp->cdev), &wiegand_in_fops);
wiegand_in_devp->cdev.owner = THIS_MODULE;
ret = cdev_add(&wiegand_in_devp->cdev, devno, 1);
if(ret)
{
printk(KERN_ERR"cdev_add error\n");
kfree(wiegand_in_devp);
wiegand_in_devp = NULL;
unregister_chrdev_region(devno, 1);
return ret;
}
//init_completion(&(wiegand_in_devp->receive_completion)); //初始化completion
sema_init(&wiegand_in_devp->sem, 0); //初始化信号量,将信号量计数器值设置为0
wiegand_in_devp->gpio_d0 = WG_DATA0;
wiegand_in_devp->gpio_d1 = WG_DATA1;
//注册及初始化GPIO
gpio_init();
//setup_timer(&wiegand_in_devp->wiegand_timer, wiegand_do_timer, 0);
return 0;
}
//设备驱动模块卸载函数
static void __exit wiegand_in_exit(void)
{
printk(KERN_INFO"wiegand_in_exit\n");
cdev_del(&wiegand_in_devp->cdev); //注销cdev
unregister_chrdev_region(MKDEV(wiegand_dev_major, 0), 1); //释放设备号
//del_timer_sync(&wiegand_in_devp->wiegand_timer); //(同步)删除定时器
//释放注册的GPIO编号
gpio_deinit();
kfree(wiegand_in_devp); //释放设备结构体内存
wiegand_in_devp = NULL;
}
module_init(wiegand_in_init);
module_exit(wiegand_in_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("[email protected]");
需要特别注意的地方(坑点):
按照以上程序编译出模块,并且在系统上插入运行后,韦根转换器发送韦根数据时,发现有的时候收到的数据是34位,这是正确的,而有的时候只收到了33或32或31个数据乃至更少。这一问题查了2天,反复分析代码,反复调试,最后探明是海思MPP的各个模块产生的影响。具体原因是MPP代码中会有中断而且中断占据的时间还比较长,由于韦根接收是通过GPIO外部中断实现的,因此会受到干扰,本来该进的中断因为被其他中断阻碍干扰,进而丢中断。最为诡异的是,一旦进行了MPP相关的初始化函数,即使再反初始化,问题依然存在,除非根本就不运行初始化函数。这一点后来者必须注意,目前也没有找到很好的兼容方法。
项目资源下载地址:
https://download.csdn.net/download/phmatthaus/12206112