用軟件實現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");//協議