DS18B20 驱动编写

嵌入式开发平台:mini2440

DS18B20 所用GPIO:S3C2410_GPF(3)

一、DS18B20 时序分析

DS18B20的一线工作协议流程是:初始化→ROM操作指令→存储器操作指令→数据传输,其工作时序包括:初始化时序、写时序、读时序。

1、初始化时序

主机首先发出一个480-960微秒的低电平脉冲,然后释放总线变为高电平,并在随后的480微秒时间内对总线进行检测,如果有低电平出现说明总线上有器件已做出应答,若无低电平出现一直都是高电平说明总线上无器件应答。

   作为从器件的DS18B20在一上电后就一直在检测总线上是否有480-960微秒的低电平出现,如果有,在总线转为高电平后等待15-60微秒后将总线电平拉低60-240微秒做出响应存在脉冲,告诉主机本器件已做好准备,若没有检测到就一直在检测等待。

[cpp] view
plain
 copy

  1. static int ds18b20_init(void)
  2. {
  3. int retval = 0;
  4. s3c2410_gpio_cfgpin(DQ, CFG_OUT);
  5. s3c2410_gpio_pullup(DQ, 0);
  6. s3c2410_gpio_setpin(DQ, 1);
  7. udelay(2);
  8. s3c2410_gpio_setpin(DQ, 0); // 拉低ds18b20总线,复位ds18b20
  9. udelay(500);                // 保持复位电平500us
  10. s3c2410_gpio_setpin(DQ, 1); // 释放ds18b20总线
  11. udelay(60);
  12. // 若复位成功,ds18b20发出存在脉冲(低电平,持续60~240us)
  13. s3c2410_gpio_cfgpin(DQ, CFG_IN);
  14. retval = s3c2410_gpio_getpin(DQ);
  15. udelay(500);
  16. s3c2410_gpio_cfgpin(DQ, CFG_OUT);
  17. s3c2410_gpio_pullup(DQ, 0);
  18. s3c2410_gpio_setpin(DQ, 1); // 释放总线
  19. return retval;
  20. }

2、写时序

写周期最少为60微秒,最长不超过120微秒,写周期一开始作为主机先把总线拉低1微秒表示写周期开始,随后若主机想写0,则继续拉低电平最少60微秒直至写周期结束,然后释放总线为高电平;若主机想写1,在一开始拉低总线电平1微秒后就释放总线为高电平,一直到写周期结束。

而作为从机的DS18B20则在检测到总线被拉低后等待15微秒然后从15μs到45μs开始对总线采样,在采样期内总线为高电平则为1,若采样期内总线为低电平则为0。

[cpp] view
plain
 copy

  1. static void write_byte(unsigned char data)
  2. {
  3. int i = 0;
  4. s3c2410_gpio_cfgpin(DQ, CFG_OUT);
  5. s3c2410_gpio_pullup(DQ, 1);
  6. for (i = 0; i < 8; i++)
  7. {
  8. // 总线从高拉至低电平时,就产生写时隙
  9. s3c2410_gpio_setpin(DQ, 1);
  10. udelay(2);
  11. s3c2410_gpio_setpin(DQ, 0);
  12. s3c2410_gpio_setpin(DQ, data & 0x01);
  13. udelay(60);
  14. data >>= 1;
  15. }
  16. s3c2410_gpio_setpin(DQ, 1); // 重新释放ds18b20总线
  17. }

3、读时序

对于读数据操作时序也分为读0时序和读1时序两个过程,读时序是从主机把单总线拉低之后,在1微秒之后就得释放单总线为高电平,以让DS18B20把数据传输到单总线上。DS18B20在检测到总线被拉低1微秒后,便开始送出数据,若是要送出0就把总线拉为低电平直到读周期结束;若要送出1则释放总线为高电平。

主机在一开始拉低总线1微秒后释放总线,然后在包括前面的拉低总线电平1微秒在内的15微秒时间内完成对总线进行采样检测,采样期内总线为低电平则确认为0,采样期内总线为高电平则确认为1,完成一个读时序过程,至少需要60μs才能完成。

[cpp] view
plain
 copy

  1. static unsigned char read_byte(void)
  2. {
  3. int i;
  4. unsigned char data = 0;
  5. // 总线从高拉至低,只需维持低电平17ts,再把总线拉高,就产生读时隙
  6. s3c2410_gpio_cfgpin(DQ, CFG_OUT);
  7. s3c2410_gpio_pullup(DQ, 0);
  8. for (i = 0; i < 8; i++)
  9. {
  10. s3c2410_gpio_setpin(DQ, 1);
  11. udelay(2);
  12. s3c2410_gpio_setpin(DQ, 0);
  13. udelay(2);
  14. s3c2410_gpio_setpin(DQ, 1);
  15. udelay(8);
  16. data >>= 1;
  17. s3c2410_gpio_cfgpin(DQ, CFG_IN);
  18. if (s3c2410_gpio_getpin(DQ))
  19. data |= 0x80;
  20. udelay(50);
  21. }
  22. s3c2410_gpio_cfgpin(DQ, CFG_OUT);
  23. s3c2410_gpio_pullup(DQ, 0);
  24. s3c2410_gpio_setpin(DQ, 1); // 释放ds18b20总线
  25. return data;
  26. }

二、操作方法

DS18B20单线通信功能是分时完成的,有严格的时序概念,如果出现序列混乱,1-WIRE器件将不影响主机,因此读写时序很重要。系统对DS18B20的各种操作必须按协议进行,根据DS18B20的协议规定,微控制器控制DS18B20完成温度的转换必须经过以下4个步骤:

1)每次读写前对DS18B20进行复位初始化。复位要求主CPU将数据线下拉500μs,然后释放,DS18B20收到信号后等待16μs-60μs左右,然后发出60μs-240μs的存在低脉冲,主CPU收到此信号后表示复位成功。

2)发送一条ROM指令

3)发送存储器指令

1、让DS18B20进行一次温度转换的具体操作如下:

a -- 主机先做个复位操作;

b -- 主机再写跳过ROM的操作(CCH)命令;

c -- 然后主机接着写转换温度的操作指令,后面释放总线至少1秒,让DS18B20完成转换操作。需要注意的是每个命令字节在写的时候都是低字节先写,例如CCH的二进制为11001100,在写到总线上时要从低位开始写,写的顺序是“0、0、1、1、0、0、1、1”,整个操作的总线状态如图所。

2、读取RAM的温度数据,同样,这个操作也要按照三个步骤:

a -- 主机发出复位操作并接受DS18B20的应答(存在)脉冲;

b -- 主机发出跳过对ROM操作的命令(CCH);

c -- 主机发出读取RAM的命令(BEH),随后主机依次读取DS18B20发出的从第0-第8,共九个字节的数据。如果只想读取温度数据,那在读完第0和第1个数据后就不再理会后面DS18B20发出的数据即可,同样读取数据也是低位在前,整个操作的总线状态如图所示。

三、具体驱动编写

1、ds18b20_drv.c

[cpp] view
plain
 copy

  1. #include <linux/init.h>
  2. #include <linux/module.h>
  3. #include <linux/delay.h>
  4. #include <linux/kernel.h>
  5. #include <linux/moduleparam.h>
  6. #include <linux/init.h>
  7. #include <linux/types.h>
  8. #include <linux/fs.h>
  9. #include <mach/regs-gpio.h>
  10. #include <mach/hardware.h>
  11. #include <linux/cdev.h>
  12. #include <asm/uaccess.h>
  13. #include <linux/errno.h>
  14. #include <linux/gpio.h>
  15. #include <linux/device.h>
  16. /* 相关引脚定义,方便以后移植 */
  17. #define DQ         S3C2410_GPF(3)
  18. #define CFG_IN     S3C2410_GPIO_INPUT
  19. #define CFG_OUT    S3C2410_GPIO_OUTPUT
  20. // ds18b20主次设备号(动态分配)
  21. static int ds18b20_major = 0;
  22. static int ds18b20_minor = 0;
  23. static int ds18b20_nr_devs = 1;
  24. // 定义设备类型
  25. static struct ds18b20_device
  26. {
  27. struct cdev cdev;
  28. };
  29. struct ds18b20_device *ds18b20_devp;    /*设备结构体指针 */
  30. static struct class *ds18b20_class;
  31. static struct class_device *ds18b20_class_dev;
  32. /* 函数声明 */
  33. static int ds18b20_open(struct inode *inode, struct file *filp);
  34. static int ds18b20_init(void);
  35. static void write_byte(unsigned char data);
  36. static unsigned char read_byte(void);
  37. static ssize_t ds18b20_read(struct file *filp, char __user * buf, size_t count, loff_t * f_pos);
  38. void ds18b20_setup_cdev(struct ds18b20_device *dev, int index);
  39. static int ds18b20_open(struct inode *inode, struct file *filp)
  40. {
  41. int flag = 0;
  42. flag = ds18b20_init();
  43. if (flag & 0x01)
  44. {
  45. printk(KERN_WARNING "open ds18b20 failed\n");
  46. return -1;
  47. }
  48. printk(KERN_NOTICE "open ds18b20 successful\n");
  49. return 0;
  50. }
  51. static int ds18b20_init(void)
  52. {
  53. int retval = 0;
  54. s3c2410_gpio_cfgpin(DQ, CFG_OUT);
  55. s3c2410_gpio_pullup(DQ, 0);
  56. s3c2410_gpio_setpin(DQ, 1);
  57. udelay(2);
  58. s3c2410_gpio_setpin(DQ, 0); // 拉低ds18b20总线,复位ds18b20
  59. udelay(500);                // 保持复位电平500us
  60. s3c2410_gpio_setpin(DQ, 1); // 释放ds18b20总线
  61. udelay(60);
  62. // 若复位成功,ds18b20发出存在脉冲(低电平,持续60~240us)
  63. s3c2410_gpio_cfgpin(DQ, CFG_IN);
  64. retval = s3c2410_gpio_getpin(DQ);
  65. udelay(500);
  66. s3c2410_gpio_cfgpin(DQ, CFG_OUT);
  67. s3c2410_gpio_pullup(DQ, 0);
  68. s3c2410_gpio_setpin(DQ, 1); // 释放总线
  69. return retval;
  70. }
  71. static void write_byte(unsigned char data)
  72. {
  73. int i = 0;
  74. s3c2410_gpio_cfgpin(DQ, CFG_OUT);
  75. s3c2410_gpio_pullup(DQ, 1);
  76. for (i = 0; i < 8; i++)
  77. {
  78. // 总线从高拉至低电平时,就产生写时隙
  79. s3c2410_gpio_setpin(DQ, 1);
  80. udelay(2);
  81. s3c2410_gpio_setpin(DQ, 0);
  82. s3c2410_gpio_setpin(DQ, data & 0x01);
  83. udelay(60);
  84. data >>= 1;
  85. }
  86. s3c2410_gpio_setpin(DQ, 1); // 重新释放ds18b20总线
  87. }
  88. static unsigned char read_byte(void)
  89. {
  90. int i;
  91. unsigned char data = 0;
  92. for (i = 0; i < 8; i++)
  93. {
  94. // 总线从高拉至低,只需维持低电平17ts,再把总线拉高,就产生读时隙
  95. s3c2410_gpio_cfgpin(DQ, CFG_OUT);
  96. s3c2410_gpio_pullup(DQ, 0);
  97. s3c2410_gpio_setpin(DQ, 1);
  98. udelay(2);
  99. s3c2410_gpio_setpin(DQ, 0);
  100. udelay(2);
  101. s3c2410_gpio_setpin(DQ, 1);
  102. udelay(8);
  103. data >>= 1;
  104. s3c2410_gpio_cfgpin(DQ, CFG_IN);
  105. if (s3c2410_gpio_getpin(DQ))
  106. data |= 0x80;
  107. udelay(50);
  108. }
  109. s3c2410_gpio_cfgpin(DQ, CFG_OUT);
  110. s3c2410_gpio_pullup(DQ, 0);
  111. s3c2410_gpio_setpin(DQ, 1); // 释放ds18b20总线
  112. return data;
  113. }
  114. static ssize_t ds18b20_read(struct file *filp, char __user * buf, size_t count, loff_t * f_pos)
  115. {
  116. int flag;
  117. unsigned long err;
  118. unsigned char result[2] = { 0x00, 0x00 };
  119. //struct ds18b20_device *dev = filp->private_data;
  120. flag = ds18b20_init();
  121. if (flag & 0x01)
  122. {
  123. printk(KERN_WARNING "ds18b20 init failed\n");
  124. return -1;
  125. }
  126. write_byte(0xcc);
  127. write_byte(0x44);
  128. flag = ds18b20_init();
  129. if (flag & 0x01)
  130. return -1;
  131. write_byte(0xcc);
  132. write_byte(0xbe);
  133. result[0] = read_byte();    // 温度低八位
  134. result[1] = read_byte();    // 温度高八位
  135. err = copy_to_user(buf, &result, sizeof(result));
  136. return err ? -EFAULT : min(sizeof(result), count);
  137. }
  138. static struct file_operations ds18b20_dev_fops = {
  139. .owner = THIS_MODULE,
  140. .open = ds18b20_open,
  141. .read = ds18b20_read,
  142. };
  143. void ds18b20_setup_cdev(struct ds18b20_device *dev, int index)
  144. {
  145. int err, devno = MKDEV(ds18b20_major, ds18b20_minor + index);
  146. cdev_init(&dev->cdev, &ds18b20_dev_fops);
  147. dev->cdev.owner = THIS_MODULE;
  148. err = cdev_add(&(dev->cdev), devno, 1);
  149. if (err)
  150. {
  151. printk(KERN_NOTICE "ERROR %d add ds18b20\n", err);
  152. }
  153. }
  154. static int __init ds18b20_dev_init(void)
  155. {
  156. int result;
  157. dev_t dev = 0;
  158. dev = MKDEV(ds18b20_major, ds18b20_minor);
  159. if (ds18b20_major)
  160. {
  161. result = register_chrdev_region(dev, ds18b20_nr_devs, "ds18b20");
  162. }
  163. else
  164. {
  165. result = alloc_chrdev_region(&dev, ds18b20_minor, ds18b20_nr_devs, "ds18b20");
  166. ds18b20_major = MAJOR(dev);
  167. }
  168. if (result < 0)
  169. {
  170. printk(KERN_WARNING "ds18b20: failed to get major\n");
  171. return result;
  172. }
  173. /* 为新设备分配内存和初始化 */
  174. ds18b20_devp = kmalloc(sizeof(struct ds18b20_device), GFP_KERNEL);
  175. if (!ds18b20_devp)
  176. {                           /*申请失败 */
  177. result = -ENOMEM;
  178. goto fail_malloc;
  179. }
  180. memset(ds18b20_devp, 0, sizeof(struct ds18b20_device));
  181. ds18b20_setup_cdev(ds18b20_devp, 0);
  182. /* 自动创建设备节点 */
  183. ds18b20_class = class_create(THIS_MODULE, "ds18b20_sys_class");
  184. if (IS_ERR(ds18b20_class))
  185. return PTR_ERR(ds18b20_class);
  186. ds18b20_class_dev =
  187. device_create(ds18b20_class, NULL, MKDEV(ds18b20_major, 0), NULL, "ds18b20");
  188. if (unlikely(IS_ERR(ds18b20_class_dev)))
  189. return PTR_ERR(ds18b20_class_dev);
  190. return 0;
  191. fail_malloc:
  192. unregister_chrdev_region(dev, 1);
  193. return result;
  194. }
  195. static void __exit ds18b20_dev_exit(void)
  196. {
  197. cdev_del(&ds18b20_devp->cdev);  /*注销cdev */
  198. kfree(ds18b20_devp);        /*释放设备结构体内存 */
  199. unregister_chrdev_region(MKDEV(ds18b20_major, 0), ds18b20_nr_devs); /*释放设备号 */
  200. device_unregister(ds18b20_class_dev);
  201. class_destroy(ds18b20_class);
  202. }
  203. module_init(ds18b20_dev_init);
  204. module_exit(ds18b20_dev_exit);
  205. MODULE_LICENSE("Dual BSD/GPL");

2、app-ds18b20.c

[cpp] view
plain
 copy

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <linux/ioctl.h>
  5. // 函数声明
  6. void ds18b20_delay(int i);
  7. int main()
  8. {
  9. int fd, i;
  10. unsigned char result[2];    // 从ds18b20读出的结果,result[0]存放低八位
  11. unsigned char integer_value = 0;
  12. float decimal_value = 0;    // 温度数值,decimal_value为小数部分的值
  13. float temperature = 0;
  14. fd = open("/dev/ds18b20", 0);
  15. if (fd < 0)
  16. {
  17. perror("open device failed\n");
  18. exit(1);
  19. }
  20. while (1)
  21. {
  22. i++;
  23. read(fd, &result, sizeof(result));
  24. integer_value = ((result[0] & 0xf0) >> 4) | ((result[1] & 0x07) << 4);
  25. // 精确到0.25度
  26. decimal_value = 0.5 * ((result[0] & 0x0f) >> 3) + 0.25 * ((result[0] & 0x07) >> 2);
  27. temperature = (float)integer_value + decimal_value;
  28. printf("Current Temperature:%6.2f\n", temperature);
  29. ds18b20_delay(500);
  30. }
  31. }
  32. void ds18b20_delay(int i)
  33. {
  34. int j, k;
  35. for (j = 0; j < i; j++)
  36. for (k = 0; k < 50000; k++) ;
  37. }

测试结果:

[cpp] view
plain
 copy

  1. [[email protected] home]#
  2. [[email protected] home]#./app-ds18b20
  3. open ds18b20 successful
  4. Current Temperature: 23.50
  5. Current Temperature: 23.50
  6. Current Temperature: 23.25
  7. Current Temperature: 23.50
  8. Current Temperature: 23.50
  9. Current Temperature: 23.50
  10. ^C
  11. [[email protected] home]#
时间: 2024-08-28 00:15:01

DS18B20 驱动编写的相关文章

Tiny4412之串口(Uart)驱动编写

一:tiny4412串口驱动编写 1.串口通信简介 串口通信指串口按位(bit)发送和接收字节,串口通信的概念非常简单,串口按位(bit)发送和接收字节.尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线 接收数据.它很简单并且能够实现远距离通信.比如IEEE488定义并行通行状态时,规定设备线总长不得超过20米,并且任意两个设备间的长度不得超过2 米:而对于串口而言,长度可达1200米. 串口通信所采用的通信协议为RS-232,RS-232通信方式允许简单连

[arm驱动]input system 子系统的驱动编写

更多可参考 Linux输入子系统分析 input 子系统架构总结 1.定义一个static struct input_dev结构体 static struct input_dev *mybutton_dev; 2.初始化时分配input_dev结构体 mybutton_dev = input_allocate_device();//分配 input_dev /*能产生的事件类型 1. #define EV_SYN 0x00 /*表示设备支持所有的事件*/ 2. #define EV_KEY 0x

SPI驱动编写要点

题外话:面对成功和失败,一个人有没有“冠军之心”,直接影响他的表现. 几周前剖析了Linux SPI 驱动框架,算是明白个所以然,对于这么一个庞大的框架,并不是每一行代码都要自己去敲,因为前人已经把这个框架搭建好了,作为驱动开发者的我们只需要搞清楚哪一部分是需要自己修改或重新编写就OK了. 结合Linux内核面向对象的设计思想,SPI总的设计思路大概是这样的: 第①处:内核中抽象了SPI控制器,让spi_master成为他的象征,他的实例化对象就是与硬生生的SPI控制器对应的,在Linux内核中

linux最简单的驱动编写及测试流程

本文采用nfs挂载网络文件系统的方式,手动创建设备节点, 动态加载驱动模块,对理解驱动编写流程有很大好处! 一.初级驱动执行流程: 1.写好Makefile文件,里面包含将应用程序编译成app文件,将驱动程序编译成mydrv.ko文件的脚本 2.在ubuntu里执行sudo make 3.确保mydrv.ko和app被拷贝到nfs挂载的根文件系统的/modules目录 4.在模块中创建设备节点(在串口软件显示的开发板文件系统里执行) mknod /dev/dcx-drv c 250 0 4.1.

AM335x(TQ335x)学习笔记——触摸屏驱动编写

前面几篇文章已经通过配置DTS的方式完成了多个驱动的移植,接下来我们解决TQ335x的触摸驱动问题.由于种种原因,TQ335x的触摸屏驱动是以模块方式提供的,且Linux官方内核中也没有带该触摸屏的驱动源码,单纯的配置DTS是无法完成TQ335x的触摸驱动移植工作的,因此,本文参考内核中原有的pixcir_i2c_ts驱动编写TQ335x的触摸屏(TN92)驱动. 在之前移植TQ210时,我已经编写过TQ210的触摸屏驱动,我的TQ335x还是使用的TQ210的屏,因此,难度不是很大.这里需要说

Linux I2C驱动编写要点

继续上一篇博文没讲完的内容“针对 RepStart 型i2c设备的驱动模型”,其中涉及的内容有:i2c_client 的注册.i2c_driver 的注册.驱动程序的编写. 一.i2c 设备的注册分析:在新版本内核的i2c驱动模型中,支持多种方式来注册 i2c 设备,在Documentation/i2c/instantiating-devices文件中有讲到,在内核中对应的抽象数据结构就是 struct i2c_client. (1)Declare the I2C devices by bus

Nandflash底层驱动编写与验证

平台:MINI2440 在目录drivers\mtd\nand\下创建空的C文件mynand.c mynand.c: #include <linux/module.h> #include <linux/types.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/ioport.h> #includ

第三十三天:Tiny4412驱动开发之LED驱动和按键驱动编写

从今天开始进入驱动开发的课程的学习,共完成四件事情.一:u-boot的简单移植,二:uboot中编写helloword程序 三:开发板中led灯的驱动编写,包括led点亮,闪烁,跑马,流水.四:开发板中按键的驱动编写,按下按键后在屏幕中显示字符. 一:u-boot的简单移植 1.进入开发板提供的源码文件包,解压uboot源码包. cd /home/bunfly/source_code/ tar xf uboot_tiny4412-20130729.tgz 2.进入uboot文件夹,更改uboot

v4l2驱动编写篇【转】

转自:http://blog.csdn.net/michaelcao1980/article/details/53008418 大部分所需的信息都在这里.作为一个驱动作者,当挖掘头文件的时候,你可能也得看看include/media/v4l2-dev.h,它定义了许多你将来要打交道的结构体. 一个视频驱动很可能要有处理PCI总线,或USB总线的部分.这里我们不会花什么时间还接触这些东西.通常会有一个内部一I2C接口,我们在这一系列的后续文章中会接触到它.然后还有一个V4L2的子系统接口.这个子系