tingpan

生命不息,折腾不止!
  首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

单总线通讯协议

Posted on 2015-11-25 22:49  tingpan  阅读(5807)  评论(0编辑  收藏  举报

用软件实现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");//协议