【OK210试用体验】功能篇(4)Linux字符驱动之DS18B20温度传感器驱动 - 在线问答 - 电子技术论坛 - 最好最受欢迎电子论坛!

【OK210试用体验】功能篇(4)Linux字符驱动之DS18B20温度传感器驱动

gjianw217 ( 楼主 )2015-8-15 00:40:43只看该作者倒序浏览
本帖最后由 gjianw217 于 2015-8-22 02:25 编辑

前面进行了OK210试用体验的入门篇介绍,算是初步入门,分别包含:
【OK210试用体验】入门篇(1):开箱验板
【OK210试用体验】入门篇(2):板载资源
【OK210试用体验】入门篇(3):开发环境(软件安装,开发环境,烧写系统
【OK210试用体验】入门篇(4):编程入门(NFS登录,驱动入门)
【OK210试用体验】功能篇(1):Linux字符驱动之Led
【OK210试用体验】功能(2):Linux字符驱动之Key按键
【OK210试用体验】功能篇(3):Linux Input子系统之Key按键
今天是功能篇的第四篇: Linux字符驱动之DS18B20,本节主要分3部分:硬件分析,软件基础,驱动编程。
一、硬件分析
【OK210试用体验】的第二篇:板载资源 中,简单分析了DS18B20传感器的 功能和作用。其实对DS18B20的操作,包含两部分,一是对字符设备驱动的深入理解,二是对DS18B20传感器时序的掌握。前面3篇功能体验,分别对GPIO的输出(LED)和输入(Key)进行了驱动的编写,而这篇将同时涉及GPIO的输入和输出。
首先从OK210的底板原理图中可知, OK210开发板上的DS18B20连接通过一个上拉电阻连接到了核心板的XM0ADDR3引脚上,如下图所示:

XM0ADDR3引脚由S5PV210用户手册,可知,该引脚位于MP0_4[3]引脚上,默认连接到了EBI接口上,如下图所示:

所以,我们要对DS18B20进行操作,就是通过对MP0_4[3]引脚的设置进行实现

二、软件基础
该部分首先总结一下,字符设备驱动的注册过程,然后简单介绍一下DS18B20驱动编写过程中,注意的事项:
1 字符设备的注册与注销
以下的步骤,一般在驱动初始化函数中和驱动退出函数中实现。
第一步:注册设备号 信息#tail -f /var/log/message
注册函数:
register_chrdev_region() 查看#lsmod
alloc_chrdev_region() 查看#cat /proc/devices
register_chrdev()
注销函数:
unregist_chrdev_region()
unregister_chrdev()
第二步:初始化cdev 并添加到系统
初始化cdev
静态初始化cdev_init()
动态初始化cdev_alloc()
添加到系统函数
cdev_add()
从系统删除函数
cdev_del()
第三步:创建设备节点
创建类
class_create() 将放于/sysfs 查看#ls /sys/class
删除类
class_destroy()

创建节点
device_create() class_device_create() 将存放于/dev
查看#ls /dev
删除节点
device_destroy() class_device_destroy()

2 DS18B20注意事项

DS18B20 是由DALLAS 半导体公司推出的一种的一线总线接口的温度传感器。与传统的热敏电阻等测温元件相比,它是一种新型的体积小、适用电压宽、与微处理器接口简单的数字化温度传感器。一线总线结构具有简洁且经济的特点,可使用户轻松地组建传感器网络,从而为测量系统的构建引入全新概念,测量温度范围为-55~+125 ℃,精度为±0 5 ℃。现场温度直接以一线总线的数字方式传输,大大提高了系统的抗干扰性。它能直接读出被测温度,并且可根据实际要求通过简单的编程实现9~l2 位的数字值读数方式。它工作在3—5 5 V 的电压范围,采用多种封装形式,从而使系统设计灵活、方便,设定分辨率及用户设定的报警温度存储在EEPROM 中,掉电后依然保存。其内部结构如下图所示:
ROM 中的64 位序列号是出厂前被光记好的,它可以看作是该DS18B20 的地址序列码,每DS18B20 64 位序列号均不相同。64 ROM 的排列是:前8 位是产品家族码,接着48 位是DS18B20 的序列号,最后8 位是前面56 位的循环冗余校验码(CRC=X8+X5+X4+1) ROM 作用是使每一个DS18B20 都各不相同,这样就可实现一根总线上挂接多个。
所有的单总线器件要求采用严格的信号时序,以保证数据的完整性。DS18B20 共有6 种信号类型:复位脉冲、应答脉冲、写0 、写1 、读0 和读1 。所有这些信号,除了应答脉冲以外,都由主机发出同步信号。并且发送所有的命令和数据都是字节的低位在前。
1)复位脉冲和应答脉冲
单总线上的所有通信都是以初始化序列开始。主机输出低电平,保持低电平时间至少480 us ,,以产生复位脉冲。接着主机释放总线,4.7K 的上拉电阻将单总线拉高,延时15 60 us ,并进入接收模式(Rx) 。接着DS18B20 拉低总线60~240us ,以产生低电平应答脉冲,
若为低电平,再延时480 us


2)写时序
写时序包括写0 时序和写1 时序。所有写时序至少需要60us ,且在2 次独立的写时序之间至少需要1us 的恢复时间,两种写时序均起始于主机拉低总线。写1 时序:主机输出低电平,延时2us ,然后释放总线,延时60us 。写0 时序:主机输出低电平,延时60us ,然后释放总线,延时2us


3)读时序
单总线器件仅在主机发出读时序时,才向主机传输数据,所以,在主机发出读数据命令后,必须马上产生读时序,以便从机能够传输数据。所有读时序至少需要60us ,且在2 时序之间至少需要1us 的恢复时间。每个读时序都由主机发起,至少拉低总线1us 。主机在读时序期间必须释放总线,并且在时序起始后的15us 之内采样总线状态。典型的读时序过程为:主机输出低电平延时2us ,然后主机转入输入模式延时12us ,然后读取单总线当前的电平,然后延时50us
三、驱动编程
如上所述,驱动的编写,主要包括两部分,一是根据DS18B20时序,编写读写函数,二是根据字符设备,编写设备驱动程序。具体的代码见下,最后附上效果图:

1 驱动代码
  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6. #include
  7. #include
  8. #include
  9. #include

  10. #define DEVICE_NAME "DS18B20"

  11. static struct cdev cdev;
  12. struct class *tem_class;
  13. static dev_t devno;
  14. static int major = 100;

  15. unsigned int gpio=S5PV210_MP04(3);

  16. void tem_reset(void)
  17. {
  18. s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(1));
  19. gpio_set_value(gpio, 1);
  20. //gpio_direction_output(gpio,1);
  21. udelay(100);
  22. gpio_set_value(gpio, 0);
  23. udelay(600);
  24. gpio_set_value(gpio, 1);
  25. udelay(100);
  26. s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0));
  27. // gpio_direction_input(gpio);
  28. }

  29. void tem_wbyte(unsigned char data)
  30. {
  31. int i;

  32. s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(1));
  33. for (i = 0; i < 8; ++i)
  34. {
  35. gpio_set_value(gpio, 0);
  36. udelay(1);

  37. if (data & 0x01)
  38. {
  39. gpio_set_value(gpio, 1);
  40. }
  41. udelay(60);
  42. gpio_set_value(gpio, 1);
  43. udelay(15);
  44. data >>= 1;
  45. }
  46. gpio_set_value(gpio, 1);
  47. }

  48. unsigned char tem_rbyte(void)
  49. {
  50. int i;
  51. unsigned char ret = 0;

  52. for (i = 0; i < 8; ++i)
  53. {
  54. s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(1));
  55. gpio_set_value(gpio, 0);
  56. // gpio_direction_output(gpio,0);
  57. udelay(1);
  58. gpio_set_value(gpio, 1);

  59. s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0));
  60. // gpio_direction_input(gpio);
  61. ret >>= 1;
  62. if (gpio_get_value(gpio))
  63. {
  64. ret |= 0x80;
  65. }
  66. udelay(60);
  67. }
  68. s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(1));


  69. return ret;
  70. }

  71. static ssize_t tem_read(struct file *filp, char *buf, size_t len, loff_t *offset)
  72. {
  73. unsigned char low, high;

  74. tem_reset();
  75. udelay(420);
  76. tem_wbyte(0xcc);
  77. tem_wbyte(0x44);

  78. mdelay(750);
  79. tem_reset();
  80. udelay(400);
  81. tem_wbyte(0xcc);
  82. tem_wbyte(0xbe);

  83. low = tem_rbyte();
  84. high = tem_rbyte();

  85. *buf = low / 16 + high * 16;

  86. *(buf + 1) = (low & 0x0f) * 10 / 16 + (high & 0x0f) * 100 / 16 % 10;
  87. return 0;
  88. }

  89. static struct file_operations tem_fops =
  90. {
  91. .owner = THIS_MODULE,
  92. .read = tem_read,
  93. };

  94. static int __init tem_init(void)
  95. {
  96. int result;
  97. devno = MKDEV(major, 0);

  98. result = register_chrdev_region(devno, 1, DEVICE_NAME);
  99. if (result)
  100. {
  101. printk("register failedn");
  102. return result;
  103. }
  104. cdev_init(&cdev, &tem_fops);
  105. cdev.owner = THIS_MODULE;
  106. cdev.ops = &tem_fops;
  107. result = cdev_add(&cdev, devno, 1);
  108. if (result)
  109. {
  110. printk("cdev add failedn");
  111. goto fail1;
  112. }

  113. tem_class = class_create(THIS_MODULE, "tmp_class");
  114. if (IS_ERR(tem_class))
  115. {
  116. printk("class create failedn");
  117. goto fail2;
  118. }

  119. device_create(tem_class, NULL, devno, DEVICE_NAME, DEVICE_NAME);
  120. return 0;
  121. fail2:
  122. cdev_del(&cdev);
  123. fail1:
  124. unregister_chrdev_region(devno, 1);
  125. return result;
  126. }

  127. static void __exit tem_exit(void)
  128. {
  129. device_destroy(tem_class, devno);
  130. class_destroy(tem_class);
  131. cdev_del(&cdev);
  132. unregister_chrdev_region(devno, 1);
  133. }

  134. module_init(tem_init);
  135. module_exit(tem_exit);

  136. MODULE_LICENSE("GPL");
  137. MODULE_AUTHOR("gjianw217@163.com");
  138. MODULE_DESCRIPTION("DS18B20 driver");
复制代码
2 测试程序
  1. #include "stdio.h"
  2. #include "sys/types.h"
  3. #include "sys/ioctl.h"
  4. #include "stdlib.h"
  5. #include "termios.h"
  6. #include "sys/stat.h"
  7. #include "fcntl.h"
  8. #include "sys/time.h"

  9. main()
  10. {
  11. int fd;
  12. unsigned char buf[2]={0};
  13. float result;

  14. if ((fd=open("/dev/DS18B20",O_RDWR | O_NDELAY | O_NOCTTY)) < 0)
  15. {
  16. printf("Open Device DS18B20 failed.rn");
  17. exit(1);
  18. }
  19. else
  20. {
  21. printf("Open Device DS18B20 successed.rn");
  22. while(1)
  23. {
  24. read(fd, buf, sizeof(buf));
  25. printf("%d.%d Crn", buf[0], buf[1]);
  26. sleep(1);
  27. }
  28. close(fd);
  29. }
  30. }
复制代码
3 Makefile
  1. #DS18B20 Makefile
  2. ARCH=arm
  3. CROSS_COMPILE=/home/ok210/arm-2009q3/bin/arm-none-linux-gnueabi-
  4. APP_COMPILE=/home/ok210/arm-2009q3/bin/arm-none-linux-gnueabi-
  5. #obj-m := app-drv.o
  6. obj-m := ds18b20-drv.o
  7. #KDIR := /path/to/kernel/linux/
  8. KDIR := /home/ok210/android-kernel-samsung-dev/
  9. PWD := $(shell pwd)
  10. default:
  11. make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
  12. app:ds18b20-app.c
  13. $(APP_COMPILE)gcc -o app ds18b20-app.c
  14. clean:
  15. $(MAKE) -C $(KDIR) M=$(PWD) clea
复制代码
欢迎大家关注本人的微信公众号【口袋物联】,微信号为koudaiwulian

1个回复

小虎嵌入式 发表于 2015-10-16 10:00:57
写的很好,支持下哈!
您需要登录后才可以回帖 登录| 注册

本版积分规则


关闭

站长推荐上一条/6下一条

小黑屋|手机版|Archiver|德赢Vwin官网(湘ICP备2023018690号)

GMT+8, 2024-8-29 00:50, Processed in 0.606937 second(s), Total 54, Slave 40 queries .

Powered by德赢Vwin官网 网

© 2015bbs.elecfans.com

微信扫描
快速回复 返回顶部 返回列表