單總線通訊協議


用軟件實現1-Wire®通信

摘要:在沒有專用總線主機(如DS2480B、DS2482)的情況下,微處理器可以輕松地產生1-Wire時序信號。本應用筆記給出了一個采用‘C’語言編寫、支持標准速率的1-Wire主機通信基本子程序實例。1-Wire總線的四個基本操作是:復位、寫“1”、寫“0”和讀數據位。字節操作可以通過反復調用位操作實現,本文提供了通過各種傳輸線與1-Wire器件進行可靠通信的時間參數。

引言

在沒有專用總線主機的情況下,微處理器可以輕松地產生1-Wire時序信號。本應用筆記給出了一個采用C語言編寫、支持標准速率的1-Wire主機通信基本子程序實例。此外,本文也討論了高速通信模式。要使該實例中的代碼正常運行,系統必須滿足以下幾點要求:

  1. 微處理器的通信端口必須是雙向的,其輸出為漏極開路,且線上具有弱上拉。這也是所有1-Wire總線的基本要求。關於簡單的1-Wire主機微處理器電路實例,請參見應用筆記4206:"為嵌入式應用選擇合適的1-Wire主機"中的1類部分。
  2. 微處理器必須能產生標准速度1-Wire通信所需的精確1µs延時和高速通信所需要的0.25µs延時。
  3. 通信過程不能被中斷。

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
Release bus, delay B

Write 0 bit

send a '0' bit to the 1-Wire slaves (Write 0 time slot)

Drive bus low, delay C
Release bus, delay D

Read bit

Read a bit from the 1-Wire slaves (Read time slot)

Drive bus low, delay A
Release bus, delay E
Sample bus to read bit from slave
Delay F

Reset

Reset the 1-Wire bus slave devices and ready them for a command

Delay G
Drive bus low, delay H
Release bus, delay I
Sample bus, 0 = device(s) present, 1 = no device present
Delay J

clip_image001
1. 1-Wire時序圖
2. 1-Wire主機時序

image

計算這些值的工作表可供下載。

示例代碼:

/*
 * 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");//協議


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM