用软件实现1-Wire®通信 |
摘要:在没有专用总线主机(如DS2480B、DS2482)的情况下,微处理器可以轻松地产生1-Wire时序信号。本应用笔记给出了一个采用‘C’语言编写、支持标准速率的1-Wire主机通信基本子程序实例。1-Wire总线的四个基本操作是:复位、写“1”、写“0”和读数据位。字节操作可以通过反复调用位操作实现,本文提供了通过各种传输线与1-Wire器件进行可靠通信的时间参数。
引言
在没有专用总线主机的情况下,微处理器可以轻松地产生1-Wire时序信号。本应用笔记给出了一个采用C语言编写、支持标准速率的1-Wire主机通信基本子程序实例。此外,本文也讨论了高速通信模式。要使该实例中的代码正常运行,系统必须满足以下几点要求:
- 微处理器的通信端口必须是双向的,其输出为漏极开路,且线上具有弱上拉。这也是所有1-Wire总线的基本要求。关于简单的1-Wire主机微处理器电路实例,请参见应用笔记4206:"为嵌入式应用选择合适的1-Wire主机"中的1类部分。
- 微处理器必须能产生标准速度1-Wire通信所需的精确1µs延时和高速通信所需要的0.25µs延时。
- 通信过程不能被中断。
1-Wire总线有四种基本操作:复位、写1位、写0位和读位操作。在数据资料中,将完成一位传输的时间称为一个时隙。于是字节传输可以通过多次调用位操作来实现,下面的表1是各个操作的简要说明以及实现这些操作所必须的步骤列表。图1为其时序波形图。表2给出了通常线路条件下1-Wire主机与1-Wire器件通信的推荐时间。如果与1-Wire主机相连的器件比较特殊或者线路条件比较特殊,则可以采用最值。请参考可下载的工作表中的系统和器件参数,确定最小值和最大值。
表1. 1-Wire操作
Operation |
Description |
Implementation |
Write 1 bit |
Send a '1' bit to the 1-Wire slaves (Write 1 time slot) |
Drive bus low, delay A |
Write 0 bit |
send a '0' bit to the 1-Wire slaves (Write 0 time slot) |
Drive bus low, delay C |
Read bit |
Read a bit from the 1-Wire slaves (Read time slot) |
Drive bus low, delay A |
Reset |
Reset the 1-Wire bus slave devices and ready them for a command |
Delay G |
计算这些值的工作表可供下载。
示例代码:
/* * Copyright (c) 2015 tingpan * Copyright 2012-2015 Senscom.cn * tingpan <smbx-ztbz@cnblog.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #include <linux/kernel.h> #include <linux/init.h> #include <linux/miscdevice.h> //混杂设备 #include <linux/fs.h> #include <linux/module.h> #include <linux/delay.h> //mdelay #include <linux/device.h> #include <linux/gpio.h> #include <linux/spi/spi.h> #include <linux/spi/spi_gpio.h> #include <linux/kfifo.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/types.h> //u8 #include <linux/ioctl.h> #define SENSORID_DEBUG 1 #if (SENSORID_DEBUG == 1) #define PRK(...) printk(__VA_ARGS__) #else #define PRK(...) #endif #define DRV_NAME "sensorid" #define DRV_DESC "1 wire sensor id" #define DRV_VERSION "0.1.0" #define SENSORID_NODE_NAME DRV_NAME #define sensorid_pin 13 u16 A,B,C,D,E,F,G,H,I,J; u8 rom[8]; static void set_speed(int standard) { if (standard) { // Adjust tick values depending on speed // Standard Speed A = 6 ; B = 64 ; C = 60 ; D = 10 ; E = 9 ; F = 55 ; G = 0; H = 480 ; I = 70 ; J = 410 ; } else { // Overdrive Speed A = 2 ; B = 8 ; C = 8 ; D = 3 ; E = 1 ; F = 7 ; G = 3 ; H = 70 ; I = 9 ; J = 40 ; } } static int init_ds2431(void) //初始化DS2431 { int presece; udelay(G); gpio_direction_output(sensorid_pin, 0); //将DQ信号线拉低 udelay(H); //保持DQ低电平480us gpio_set_value(sensorid_pin,1); //将DQ信号拉高、释放总线 udelay(I); //保持DQ高电平70us gpio_direction_input(sensorid_pin); presece = gpio_get_value(sensorid_pin); udelay(J); return presece; //为1表示该总线上没有设备,为0表示有设备 } static int read_bit(void) //从单总线上读取一个数据位 { int result; gpio_direction_output(sensorid_pin,0);//启动读时序 udelay(A); gpio_set_value(sensorid_pin,1);//释放总线,等待从机返回数据位 udelay(E); gpio_direction_input(sensorid_pin); result = gpio_get_value(sensorid_pin); udelay(F); gpio_direction_output(sensorid_pin,1);//返回总线状态 return result; } static void write_bit(u8 bit) //向单总线设备写入一个数据位 { if (bit){ //写 "1" gpio_direction_output(sensorid_pin,0);//启动写时序 udelay(A); gpio_set_value(sensorid_pin,1); udelay(B); } else { //写 "0" gpio_direction_output(sensorid_pin,0);//启动写时序 udelay(C); gpio_set_value(sensorid_pin,1); udelay(D); } } static void write_byte(u8 data)//向单总线写一个字节 { u8 i; for (i = 0; i < 8; i++) { //先低位,后高位 write_bit(data & 0x01); data >>= 1; } } static int read_byte(void) { u8 i,value=0; for (i = 0; i < 8; i++) { //先读低位,再读高位 if(read_bit()) value = value | (0x01 << i); //如果当前读取的数据位为1,将返回字节对应的位置1 } return value; //返回读取的数据 } static u8 crccheck(u8* p,u8 len) //CRC校验,返回CRC检验值 { u8 bit0,cbit,i,j,byte,temp; temp = 0; for (j = 0; j < len; j++) { byte = p[j]; for(i = 0; i < 8; i++) { cbit = temp & 0x01; bit0 = byte & 0x01; temp = temp >> 1; if ((cbit^bit0)) temp ^= 0x8c; byte >>= 1; } } return temp; } static int read_2431_serial(void) //读 2431序列号 { int i; u8 crc; set_speed(1);// set the speed to 'standard' if (init_ds2431()) {// Reset the 1-Wire bus PRK(KERN_INFO "no device on the bus\n"); return -1; // Return if no devices found } write_byte(0x33); // Send Read ROM command to select single device // read the page data udelay(60); for (i = 0; i < 8; i++) rom[i] = read_byte(); crc = crccheck(rom, 8); PRK(KERN_INFO "crc value is %x\n",crc); if (!crc) { //crc校验为0,则读取正确 return 0; } else { return -1; } return 0; } static ssize_t op_sensorid_read(struct file *file, char __user * dat, size_t len, loff_t *loff) { int err = 0; err = !access_ok(VERIFY_WRITE, (void __user *)dat, _IOC_SIZE(len)); if (err) { PRK(KERN_INFO " access not allowed!\n"); return -EFAULT; } err = read_2431_serial(); if (err) { PRK("[%d]read_2431_serial failed.\n", __LINE__); return -1; } __copy_to_user(dat, &rom, sizeof(rom)); return 0; } static ssize_t op_sensorid_write(struct file *file, char __user * dat, size_t len, loff_t *loff) { } static int op_sensorid_open(struct inode *inode, struct file *file) { int err; err = gpio_request(sensorid_pin, "sensorid_pin");//管脚申请 if (err) { PRK("[%d]gpio_request sensorid_pin failed.\n", __LINE__); return -1; } gpio_direction_output(sensorid_pin, 1);//该管脚设为输出,且输出为高电平 PRK(KERN_INFO " op_sensorid_open\n"); return 0; } static int op_sensorid_release(struct inode *inode, struct file *file) { gpio_free(sensorid_pin);//释放IO口 PRK(KERN_INFO " op_sensorid_release\n"); return 0; } static const struct file_operations sensorid_fops = { .owner = THIS_MODULE, .open = op_sensorid_open, .read = op_sensorid_read, .write = op_sensorid_write, .release = op_sensorid_release, }; static struct miscdevice sensorid_miscdev = { //次设备号,驱动注册时,如果次号指定MISC_DYNAMIC_MINOR,则进行动态分配。 .minor = MISC_DYNAMIC_MINOR, .name = SENSORID_NODE_NAME,//设备名称,将在/dev文件夹下显示 .fops = &sensorid_fops, }; static int __init sensorid_init(void)//放后面,因为 misc_register 要包含前面的内容 { int ret; ret = misc_register(&sensorid_miscdev); if (ret) { printk("misc_unregister error\n"); return ret; } printk(KERN_INFO DRV_NAME " ver " DRV_VERSION" init\n"); return 0; } module_init(sensorid_init); static void __exit sensorid_exit(void) { int ret; ret = misc_deregister(&sensorid_miscdev);//注销 if (ret) { printk("misc_register error\n"); return ; } printk(KERN_INFO DRV_NAME " ver " DRV_VERSION" exit\n"); } module_exit(sensorid_exit); MODULE_DESCRIPTION(DRV_DESC);//描述 MODULE_VERSION(DRV_VERSION);//版本 MODULE_AUTHOR("tingpan <smbx-ztbz@cnblogs.com>");//作者 MODULE_LICENSE("GPL v2");//协议