12 H5上实现dht12单总线工作方式的驱动
来源:互联网 发布:linux显示文件夹命令 编辑:程序博客网 时间:2024/05/29 06:45
具体的硬件工作原理可参考:http://blog.csdn.net/jklinux/article/details/73460008
dht12发出的二进制数据0的周期(从下降沿开始)大约78微秒, 数据1的信号周期大约120微秒.
通过捕捉数据脚的下降沿中断的间隔时间来计算接收到的数据是二进制0和1. 但全志方案里的gpio控制器默认是使用32Khz的工作时钟信号,频率过低会丢失中断信号,需要把gpio控制器的时钟信号切换到24Mhz.
PA组IO口的时钟配置寄存器:
另在现用的内核里,当一个IO口作中断使用后,就不可以再作输出使用。所以在发出开始信号到dht12模块前,不可以先请求使用中断,直到开始信号完成后才可以请求中断。数据接收完成后再释放中断.
驱动里加入struct timer_list定时器,用于防止数据传输过程中,发生中断次数不准确而避免用户进程死堵塞的问题.
dht12在设备树里的描述:
mydht12 { compatible = "mydht12"; data-gpios = <&pio 0 12 GPIO_ACTIVE_HIGH>; };
驱动代码:
/* mydrv.c */#include <linux/init.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/property.h>#include <linux/gpio/consumer.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/ktime.h>#include <asm/io.h>#include <linux/gpio.h>#include <linux/timer.h>#include <linux/cdev.h>#include <linux/fs.h>#include <linux/mutex.h>#define MYMA 1314#define COUNT 1typedef struct { struct gpio_desc *data_io; int n; //记录当前接收的数据是第几位 int times[41]; s64 prev_time; unsigned char data[5]; //存放接收到的温湿度数据 bool sum_checked; //记录数据是否有效,检得校验和 dev_t devid; struct cdev cdev; struct class *cls; struct mutex mutex; struct mutex mutex_read; //确保只有一个进程来调用read函数 struct timer_list mytimer;}mypdata;extern irqreturn_t irq_func(int irqno, void *arg);ssize_t myread(struct file *fl, char *__user buf, size_t len, loff_t *off){ struct inode *ind = fl->f_path.dentry->d_inode; mypdata *pdata = container_of(ind->i_cdev, mypdata, cdev); int ret; mutex_lock(&pdata->mutex_read); //发出开始信号 pdata->n = 0; gpiod_direction_output(pdata->data_io, 1); msleep(100); gpiod_set_value(pdata->data_io, 0); msleep(30); gpiod_set_value(pdata->data_io, 1); udelay(30); gpiod_direction_input(pdata->data_io); ret = request_any_context_irq(gpiod_to_irq(pdata->data_io), irq_func, IRQF_TRIGGER_FALLING, "mydht12", pdata); if (ret < 0) return -ENODATA; ret = mutex_lock_interruptible(&pdata->mutex); if (ret < 0) goto out; if (pdata->sum_checked) { sprintf(buf, "%02d.%02d %02d.%02d\n", pdata->data[0], pdata->data[1], pdata->data[2], pdata->data[3]); ret = strlen(buf); } else ret = -ENODATA;out: free_irq(gpiod_to_irq(pdata->data_io), pdata); mutex_unlock(&pdata->mutex_read); return ret;}struct file_operations fops = { .owner = THIS_MODULE, .read = myread,};void timer_func(unsigned long data){ mypdata *pdata = (mypdata *)data; int i, j, sum = 0; for (i = 0; i < 5; i++) { pdata->data[i] = 0; for (j = 0; j < 8; j++) { if (pdata->times[i*8+j+1] > 100) pdata->data[i] |= 1 << (7-j); //数据从高位开始 } if (i < 4) sum += pdata->data[i]; } pdata->sum_checked = (sum == pdata->data[4]);// if (pdata->sum_checked)// printk("in kernel : %d.%d, %d.%d\n", pdata->data[0], pdata->data[1], pdata->data[2], pdata->data[3]); mutex_unlock(&pdata->mutex);}irqreturn_t irq_func(int irqno, void *arg){ mypdata *pdata = (mypdata *)arg; s64 now = ktime_to_us(ktime_get()); if (0 == pdata->n) pdata->prev_time = now; else if (pdata->n < ARRAY_SIZE(pdata->times)) { pdata->times[pdata->n] = now - pdata->prev_time; pdata->prev_time = now; } pdata->n++; mod_timer(&pdata->mytimer, jiffies+HZ*10/1000); //10ms return IRQ_HANDLED;}int myprobe(struct platform_device *pdev){ struct gpio_desc *gpiod = devm_gpiod_get(&pdev->dev, "data", GPIOD_OUT_HIGH); mypdata *pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); int ret = -ENODEV; static int mi = 0; if (IS_ERR(gpiod)) { printk("gpio get failed\n"); goto err0; } pdata->data_io = gpiod; init_timer(&pdata->mytimer); pdata->mytimer.function = timer_func; pdata->mytimer.data = (unsigned long)pdata; /////////// cdev /////////// pdata->devid = MKDEV(MYMA,mi); ret = register_chrdev_region(pdata->devid, COUNT, pdev->name); if (ret < 0) goto err1; cdev_init(&pdata->cdev, &fops); pdata->cdev.owner = THIS_MODULE; ret = cdev_add(&pdata->cdev, pdata->devid, COUNT); if (ret < 0) goto err2; pdata->cls = class_create(THIS_MODULE, pdev->name); device_create(pdata->cls, NULL, pdata->devid, NULL, pdev->name);/////////////////// mutex_init(&pdata->mutex); mutex_lock(&pdata->mutex); mutex_init(&pdata->mutex_read); platform_set_drvdata(pdev, pdata); printk("probe done ...\n");//////////////////////////////////// return 0;err2: unregister_chrdev_region(pdata->devid, COUNT);err1: devm_kfree(&pdev->dev, pdata); devm_gpiod_put(&pdev->dev, gpiod);err0: return ret;}int myremove(struct platform_device *pdev){ mypdata *pdata = platform_get_drvdata(pdev); unregister_chrdev_region(pdata->devid, COUNT); cdev_del(&pdata->cdev); device_destroy(pdata->cls, pdata->devid); class_destroy(pdata->cls); del_timer(&pdata->mytimer); devm_gpiod_put(&pdev->dev, pdata->data_io); devm_kfree(&pdev->dev, pdata); return 0;}struct of_device_id ids[] = { {.compatible = "mydht12"}, {},};struct platform_driver mydrv = { .probe = myprobe, .remove = myremove, .driver = { .owner = THIS_MODULE, .name = "mydrv" , .of_match_table = ids, },};#define BASE (0x01C20800+0x0218)static u8 *vaddr;static int __init test_init(void){ //把PA组所属的gpio控制器的工作时钟改为24Mhz vaddr = ioremap(BASE, SZ_4K); iowrite32(ioread32(vaddr)|1, vaddr); return platform_driver_register(&mydrv);}static void __exit test_exit(void){ iounmap(vaddr); platform_driver_unregister(&mydrv);}module_init(test_init);module_exit(test_exit);MODULE_LICENSE("GPL");
阅读全文
0 0
- 12 H5上实现dht12单总线工作方式的驱动
- 34 dht12单总线方式的Linux驱动及解决H3丢失中断的问题
- 46 dht11/dht12 linux platform驱动实现
- 11 H5上实现超声波测距模块的设备驱动
- 14 在H5上实现的矩阵键盘驱动
- SPI总线的特点、工作方式介绍
- SPI总线的工作方式和特点
- 27 在H5上实现spi-tft屏的简单驱动
- SEP4020 spi 总线驱动的实现
- SPI总线的特点、工作方式及常见错误解答
- SPI总线的特点、工作方式及常见错误解答
- DHT12温湿度传感器STM32驱动IIC
- 虚拟总线上注册驱动
- SDIO驱动(5)sdio总线上的probe
- PCI总线协议的FPGA实现及驱动设计
- 用虚拟总线来实现的MINI2440 LED驱动
- 单总线和多总线的区别
- 单总线和多总线的区别
- 图像视频处理的作者主页(更新中)
- PHP面向对象设计模式
- 操作系统 带有缓冲机制的生产者消费者问题
- win10与centos 7双系统安装,win10引导消失,解决方案
- IntelliJ IDEA+Maven运行apache-storm的LocalCluster例子
- 12 H5上实现dht12单总线工作方式的驱动
- 学生管理系统前奏1
- 任务的创建、挂起和恢复
- 从零开始动手写个公司内部用的iOS App
- Mongoose初体验
- spring Boot 设置tomcat端口号
- 友盟推送集成小米华为通道
- 这篇文章主要介绍了Python os模块介绍,需要的朋友可以参考下
- 基于Spring boot,使用idea方便地切换启动环境