FL2440—基于platform 模型的DS18B20驱动实例
来源:互联网 发布:mac usb 以太网转接器 编辑:程序博客网 时间:2024/05/21 18:32
基于platform 模型的DS18B20驱动实例
通过前面的学习了解到DS18B20是基于单总线协议靠一个单线端口与CPU通信实现数据传输,关于单总线设备之前接触的比较少,这次通过学习编写DS18B20的驱动,总算对这种通信协议有了多一点的了解。
单总线通信协议时序理解
初始化序列——复位和存在脉冲
如图为DS18B20的单总线通信协议的初始化序列时序,DS18B20的所有通信都由由复位脉冲组成的初始化序列开始。该初始化序列由主机发出,后跟由DS18B20发出的存在脉冲(presence pulse)。当发出应答复位脉冲的存在脉冲后,DS18B20通知主机它在总线上并且准备好操作了。
在初始化步骤中,总线上的主机通过拉低单总线至少480μs来产生复位脉冲。然后总线主机释放总线并进入接收模式。 当总线释放后,5kΩ的上拉电阻把单总线上的电平拉回高电平。当DS18B20检测到上升沿后等待15到60us,然后以拉低总线60-240us的方式发出存在脉冲。
读写时序
写时序
由以上的时序图可知,总线主机使用写“1”时间隙向DS18B20写入逻辑1,使用写“0”时间隙向DS18B20写入逻辑0.所有的写时隙必须有最少60us的持续时间,相邻两个写时隙必须要有最少1us的恢复时间。两种写时隙都通过主机拉低总线产生。为产生写1时隙,在拉低总线后主机必须在15μs内释放总线。在总线被释放后,由于5kΩ上拉电阻的作用,总线恢复为高电平。为产生写0时隙,在拉低总线后主机必须继续拉低总线以满足时隙持续时间的要求(至少60μs)。 在主机产生写时序后,DS18B20会在其后的15到60us的一个时间窗口内采样单总线。在采样的时间窗口内,如果总线为高电平,主机会向DS18B20写入1;如果总线为低电平,主机会向DS18B20写入0。
读时序
DS18B20只有在主机发出读暂存器命令 [BEh]或温度转换命令T [44h]后,主机才会产生读时隙以便DS18B20提供所需数据。在主机产生读时序后,DS18B20开始发送0或1到总线上。DS18B20让总线保持高电平的方式发送1,以拉低总线的方式表示发送0.当发送0的时候,DS18B20在读时序的末期将会释放总线,总线将会被上拉电阻拉回高电平(也是总线空闲的状态)。DS18B20输出的数据在下降沿(下降沿产生读时隙)产生后15us后有效。因此,主机释放总线和采样总线等动作要在15μs内完成。
DS18B20温度值获取原理
DS18B20的测温原理如图2所示,图中低温度系数晶振的振荡频率受温度的影响很小[1],用于产生固定频率的脉冲信号送给减法计数器1,高温度系数晶振随温度变化其震荡频率明显改变,所产生的信号作为减法计数器2的脉冲输入,图中还隐含着计数门,当计数门打开时,DS18B20就对低温度系数振荡器产生的时钟脉冲后进行计数,进而完成温度测量。计数门的开启时间由高温度系数振荡器来决定,每次测量前,首先将-55 ℃所对应的基数分别置入减法计数器1和温度寄存器中,减法计数器1和温度寄存器被预置在-55 ℃所对应的一个基数值。减法计数器1对低温度系数晶振产生的脉冲信号进行减法计数,当减法计数器1的预置值减到0时温度寄存器的值将加1,减法计数器1的预置将重新被装入,减法计数器1重新开始对低温度系数晶振产生的脉冲信号进行计数,如此循环直到减法计数器2计数到0时,停止温度寄存器值的累加,此时温度寄存器中的数值即为所测温度。
驱动程序&&测试程序
下面是自己参考ds18b20的datasheet以及前辈们的博客编写的驱动和测试程序:
- 驱动程序:s3c_plat_ds18b20.c
/********************************************************************************* * Copyright: (C) 2017 Li Wanneng<liwjng@gmail.com> * All rights reserved. * * Filename: s3c_ds18b20.c * Description: This file is driver for s3c_ds18b20 * * Version: 1.0.0(04/24/2017) * Author: Li Wanneng <liwjng@gmail.com> * ChangeLog: 1, Release initial version on "04/24/2017 07:34:17 PM" * ********************************************************************************/#include<linux/module.h>#include<linux/kernel.h>#include<linux/fs.h>#include<linux/init.h>#include<linux/delay.h>#include<linux/gpio.h>#include<linux/types.h>#include<asm/irq.h>#include<mach/regs-gpio.h>#include<mach/hardware.h>#include<linux/device.h>#include<linux/kdev_t.h>#include<linux/cdev.h>#include<linux/errno.h>#include<asm/uaccess.h>#include<linux/platform_device.h>#define DRV_AUTHOR "Li Wanneng<liwjng@gmail.com>"#define DRV_DESC "S3C_DS18B20 driver"#define DISABLE 0/* Driver version */#define DRV_MAJOR_VER 1#define DRV_MINOR_VER 0#define DRV_REVER_VER 0/* Set major static */#define DEV_NAME "s3c_ds18b20"/* dynamic major by default */#ifndef DEV_MAJOR#define DEV_MAJOR 0#endif#define DQ S3C2410_GPG(0) #define INPUT S3C2410_GPIO_INPUT #define OUTPUT S3C2410_GPIO_OUTPUT static int debug = DISABLE;static int dev_major = DEV_MAJOR;static int dev_minor = 0;struct ds18b20_device{ struct s3c_ds18b20_platform_data *data; struct class *dev_class; struct cdev cdev;} ds18b20_device;static struct s3c_ds18b20_platform_data{ unsigned int gpio; unsigned int is_open_drain:1;};static struct s3c_ds18b20_platform_data s3c_ds18b20_data = { .gpio = S3C2410_GPG(0), .is_open_drain = 0,};/* Reset W1-ds18b20 and read presence pluse */static unsigned int w1_ds18b20_reset(void){ int err; s3c2410_gpio_cfgpin(DQ, OUTPUT); s3c2410_gpio_pullup(DQ, 0); s3c2410_gpio_setpin(DQ, 1); udelay(10); s3c2410_gpio_setpin(DQ, 0); udelay(600); s3c2410_gpio_setpin(DQ, 1); udelay(60); s3c2410_gpio_cfgpin(DQ, INPUT); udelay(400); err = s3c2410_gpio_getpin(DQ); return err;}/* w1 write bit */static unsigned int w1_ds18b20_write(unsigned char data){ unsigned int i; s3c2410_gpio_cfgpin(DQ, OUTPUT); for (i = 0; i < 8; i++) { s3c2410_gpio_setpin(DQ, 0); udelay(5); if (data & 0x01) { s3c2410_gpio_setpin(DQ, 1); udelay(60); } else udelay(60); data >>= 1; s3c2410_gpio_setpin(DQ, 1); udelay(1); } return 2;}/* w1 read byte */static unsigned int w1_ds18b20_read(void){ unsigned int i; unsigned char data = 0x00; for (i = 0; i < 8; i++) { s3c2410_gpio_cfgpin(DQ, OUTPUT); s3c2410_gpio_setpin(DQ, 1); udelay(1); s3c2410_gpio_setpin(DQ, 0); udelay(2); s3c2410_gpio_setpin(DQ, 1); s3c2410_gpio_cfgpin(DQ, INPUT); data >>= 1; if (0 != s3c2410_gpio_getpin(DQ)) data |= 0x80; udelay(60); } return data;}static ssize_t ds18b20_read(struct file *filp, char __user * buf, size_t count, loff_t * f_pos){ unsigned char Data[2] = { 0x00, 0x00 }; unsigned long err; /* Enable temperature conversion*/ if ((w1_ds18b20_reset()) <= 0) { printk(KERN_ERR "ds18b20 reset fail!\n"); return -1; } w1_ds18b20_write(0xcc);/* Skip ROM command. */ w1_ds18b20_write(0x44);/* Initiates temperature conversion*/ /* Read temperature value.*/ if ((w1_ds18b20_reset()) <= 0) { printk(KERN_ERR "ds18b20 reset fail!\n"); return -1; } w1_ds18b20_write(0xcc);/* Skop ROM commond. */ w1_ds18b20_write(0xbe);/* Read Scratchpad. */ Data[0] = w1_ds18b20_read();/* Read low byte */ Data[1] = w1_ds18b20_read();/* Read high byte */ w1_ds18b20_reset(); err = copy_to_user(buf, Data, sizeof(Data)); return err ? -EFAULT : count;}static int ds18b20_release(struct inode *inode, struct file *file){ return 0;}static int ds18b20_open(struct inode *inode, struct file *file){ int flag = 0; struct ds18b20_device *pdev; struct s3c_ds18b20_platform_data *pdata; /* debug */ printk(KERN_INFO"ds18b20_open.\n"); pdev = container_of(inode->i_cdev,struct ds18b20_device, cdev); pdata = pdev->data; file->private_data = pdata; flag = w1_ds18b20_reset(); if (flag) { printk(KERN_INFO "open ds18b20 successful!\n"); } else printk("open ds18b20 failed!\n"); return 0;}static struct platform_device s3c_ds18b20_device = { .name = "s3c_ds18b20", .id = -1, .dev = { .platform_data = &s3c_ds18b20_data, },};static struct file_operations ds18b20_fops = { .owner = THIS_MODULE, .open = ds18b20_open, .read = ds18b20_read, .release = ds18b20_release,};static int s3c_ds18b20_probe(struct platform_device *dev){ struct s3c_ds18b20_platform_data *pdata = dev->dev.platform_data; int result = 0; dev_t devno; /* Alloc the device for driver */ if (0 != dev_major) { devno = MKDEV(dev_major, dev_minor); result = register_chrdev_region(devno, 1, DEV_NAME); } else { result = alloc_chrdev_region(&devno, dev_minor, 1, DEV_NAME); dev_major = MAJOR(devno); } /* debug */ printk(KERN_INFO "major is %d.\n",dev_major); /* Alloc for device major failure */ if (result < 0) { printk("%s driver can't get major %d\n", DEV_NAME, dev_major); return result; } /* Initialize ds18b20 structure and register cdev */ memset(&ds18b20_device, 0, sizeof(ds18b20_device)); ds18b20_device.data = dev->dev.platform_data; cdev_init(&(ds18b20_device.cdev), &ds18b20_fops); ds18b20_device.cdev.owner = THIS_MODULE; result = cdev_add(&(ds18b20_device.cdev), devno, 1); /* debug */ printk(KERN_INFO "result is %d.\n",result); if (result) { printk(KERN_NOTICE "error %d add %s device", result, DEV_NAME); goto ERROR; } ds18b20_device.dev_class = class_create(THIS_MODULE, DEV_NAME); if (IS_ERR(ds18b20_device.dev_class)) { printk("%s driver create class failture\n", DEV_NAME); result = -ENOMEM; goto ERROR; } device_create(ds18b20_device.dev_class, NULL, devno, NULL, DEV_NAME); printk("S3C %s driver version %d.%d.%d initiliazed.\n", DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER, DRV_REVER_VER); return 0; ERROR: printk("S3C %s driver version %d.%d.%d install failure.\n", DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER, DRV_REVER_VER); cdev_del(&(ds18b20_device.cdev)); unregister_chrdev_region(devno, 1); return result;}static int s3c_ds18b20_remove(struct platform_device *dev){ dev_t devno = MKDEV(dev_major, dev_minor); cdev_del(&(ds18b20_device.cdev)); device_destroy(ds18b20_device.dev_class, devno); class_destroy(ds18b20_device.dev_class); unregister_chrdev_region(devno, 1); printk("%s driver removed\n", DEV_NAME); return 0;}static struct platform_driver s3c_ds18b20_driver = { .probe = s3c_ds18b20_probe, .remove = s3c_ds18b20_remove, .driver = { .name = "s3c_ds18b20", .owner = THIS_MODULE, },};static int __init s3c_ds18b20_init(void){ int ret = 0; ret = platform_device_register(&s3c_ds18b20_device); if (ret) { printk(KERN_ERR "%s:%d: Can't register platform device %d\n", __FUNCTION__, __LINE__, ret); goto fail_reg_plat_dev; } ret = platform_driver_register(&s3c_ds18b20_driver); if (ret) { printk(KERN_ERR "%s:%d: Can't register platform driver %d\n", __FUNCTION__, __LINE__, ret); goto fail_reg_plat_drv; } return 0; fail_reg_plat_dev: return ret; fail_reg_plat_drv: platform_driver_unregister(&s3c_ds18b20_driver); return ret;}static void s3c_ds18b20_exit(void){ platform_driver_unregister(&s3c_ds18b20_driver); platform_device_unregister(&s3c_ds18b20_device);}module_init(s3c_ds18b20_init);module_exit(s3c_ds18b20_exit);module_param(debug, int, S_IRUGO);module_param(dev_major, int, S_IRUGO);module_param(dev_minor, int, S_IRUGO);MODULE_AUTHOR(DRV_AUTHOR);MODULE_DESCRIPTION(DRV_DESC);MODULE_LICENSE("GPL");MODULE_ALIAS("platform:S3C_DS18B20");
- Makefile
[lwn@localhost ds18b20]$ vim Makefile LINUX_SRC?=~/myfl2440/kernel/linux-lwn-3.0.1CROSS_COMPILE=/opt/dl/buildroot-2012.08/ARM920t/usr/bin/arm-linux-obj-m := s3c_plat_ds18b20.omodules: @make -C $(LINUX_SRC) M=`pwd` modules @make cleanclean: rm -f *.ko.* *.o *mod.c *.order *.symvers
- 测试程序:test_ds18b20.c
/********************************************************************************* * Copyright: (C) 2017 Li Wanneng<liwjng@gmail.com> * All rights reserved. * * Filename: test_ds18b20.c * Description: This file used for test ds18b20 driver * * Version: 1.0.0(04/26/2017) * Author: Li Wanneng <liwjng@gmail.com> * ChangeLog: 1, Release initial version on "04/26/2017 03:19:52 PM" * ********************************************************************************/#include <stdio.h>#include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> /******************************************************************************** * Description: * Input Args: * Output Args: * Return Value: ********************************************************************************/int main(int argc, char **argv){ int fd; int data = 0; unsigned char result[2]; float temperature = 0; fd = open("/dev/s3c_ds18b20", O_RDWR | O_NONBLOCK); if (fd < 0) { printf("user:open s3c_ds18b20 failed.\n"); printf("user_Message : %s\n", strerror(errno)); return -1; } while (1) { int ret = 0; usleep(100); ret = read(fd, result, sizeof(result)); if (ret != 2) { printf("read wrong\n"); exit(0); } data = (int)result[1]; data <<= 8; data = data | result[0]; temperature = data * 0.0625; printf("Temperature = %.2f\n", temperature); fflush(stdout); sleep(1); } close(fd); return 0;} /* ----- End of main() ----- */
编译&&测试
完成上述的驱动和测试程序的编写,对于驱动程序基于上述makefile使用make命令编译生成ko文件,对于测试程序使用gcc交叉编译生成可执行二进制文件test,然后使用tftp将其下载到开发板测试。
- 编译生成驱动文件和测试文件
- 开发板测试温度
遇到的问题及解决:
1.Q:在测试驱动程序的时候,一直打不开设备节点,读取文件失败。提示错误信息:No such device or address。
A:通过调试发现,在ds18b20_open(struct inode *inode, struct file *file)函数中没有对参数file赋值。于是赶紧添加了下面三行代码,重新编译之后驱动程序可以运行了。
pdev = container_of(inode->i_cdev,struct ds18b20_device, cdev);pdata = pdev->data;file->private_data = pdata;
2.Q:执行测试程序的时候出现以下错误信息:
WARNING: at drivers/gpio/gpiolib.c:101 gpio_ensure_requested+0x54/0xd4()autorequest GPIO-192Modules linked in: s3c_plat_ds18b20[<c002d508>] (unwind_backtrace+0x0/0xf0) from [<c003a9ac>] (warn_slowpath_common+0x48/0x60)[<c003a9ac>] (warn_slowpath_common+0x48/0x60) from [<c003aa58>] (warn_slowpath_fmt+0x30/0x40)[<c003aa58>] (warn_slowpath_fmt+0x30/0x40) from [<c01fad00>] (gpio_ensure_requested+0x54/0xd4)
A:经查阅资料以及警告信息(autorequest GPIO-192)得知是因为gpio冲突,联想到前两天添加了内核自带的ds18b20驱动,可能是两个驱动都同时使用了同一个GPIO管脚,于是我在make manuconfig中取消内核对于ds18b20驱动的选项,重新编译内核。最终问题得到了解决。
参考文章:
http://www.cnblogs.com/wangyuezhuiyi/archive/2012/10/12/2721839.html
http://blog.csdn.net/u010944778/article/details/48058433
在此感谢两位网友的文章给予的帮助和启发
- FL2440—基于platform 模型的DS18B20驱动实例
- FL2440—基于platform 模型的DS18B20驱动实例
- 基于S3C2440的嵌入式Linux驱动——DS18B20温度传感器(添加使用platform总线机制)
- 基于S3C2440的嵌入式Linux驱动——DS18B20温度传感器(添加使用platform总线机制)
- 基于S3C2440的嵌入式Linux驱动——DS18B20温度传感器(添加使用platform总线机制)
- fl2440——DS18B20 驱动编写
- FL2440添加DS18B20驱动
- FL2440添加DS18B20驱动
- 基于platform驱动模型的LED驱动
- 基于platform机制的驱动模型
- 基于platform机制的驱动模型
- 基于platform机制的驱动模型
- 基于platform机制的驱动模型
- 基于platform机制的驱动模型
- 基于Tiny6410的ds18b20驱动
- 基于Tiny6410的ds18b20驱动
- 基于STM32的DS18B20驱动
- fl2440——Platform-LED实例
- 软件测试从零开始之五:探索性测试技术(上)
- C/C++ 修改控制台字体大小。。
- maven工程编译优化
- 嵌入式系统
- Swift 开发:UINavigationController和UITabBarController用法案例
- FL2440—基于platform 模型的DS18B20驱动实例
- 京东右侧栏
- 深度学习框架caffe 各层参数解析
- MIME详解
- 把有依赖包的ec项目导入as中遇见的问题一
- 分析Android项目中各个gradle文件,简单Groovy语法、实例
- tcp在TIME_WAIT状态下等待2MSL
- Volley的使用
- 响应轮播图