海思Hi3559AV100平台韦根接收程序

        项目中需要基于海思Hi3559AV100开发韦根功能。韦根分为韦根输出和韦根输入,这篇文章讲韦根输入程序,下一篇文章讲韦根输出程序。

        首先,必须要感谢https://blog.csdn.net/qq_37803273/article/details/84993571  《RK3288平台韦根接收》的作者的无私分享,文章中提供了源码(以下简称例程),很详细,我的代码也是以这篇文章中的代码为基础进行修正改进而得到的。

        其次,必须说明,例程中还是有不少坑,在按照例程编写、修改和调试韦根输入代码的过程中踩了很多坑,最终才成为现在的程序。

        最后,整个工程源码以附件形式放到网上,初始设定为0积分加载。

 韦根输入代码如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/jiffies.h>
#include <linux/semaphore.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/completion.h>
#include <linux/cdev.h>
//#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>
#include <linux/slab.h>


#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; i<wiegand_in_devp->count; 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

发布了34 篇原创文章 · 获赞 12 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/phmatthaus/article/details/104616298