基于WinAVR的DS18B20源程序

基于WinAVR的DS18B20源程序 Recommend to my friend via E-mail(推荐给朋友)
作者: Administrator   
2007-11-14

来自21icbbs中的AVR论坛

http://www.21icbbs.com/club/bbs/bbsView.asp?boardid=9

/*
********************************************************
*   文件:   ds18b20.c
*   功能:   AVR微控制器 VS 温度芯片DS18B20(TO-92)
*   工具:   WinAVR20040404(AVR-GCC)
*   作者:   孤欲化境(qjy_dali)
*   E-mail: qjy_dali@sohu.com
*   日期:   6/02/2004
*   版本:   1.41
*   声明:   你可随意地拷贝,复制或修改这个程序,但请你注明
*          你的修改。本作者不对这个程序的后果负责,无论是明
*          确的,还是隐含的(^_^)。自由软件不是万能的,但它
*          的确是令人振奋的。支持一下GCC!!!
********************************************************
*/

#ifndef  _DS18B20_C_
#define  _DS18B20_C_


/*      我的一线温度芯片DS18B20被连接到AVR微控制器ATmega8
*  -16PI(PDIP28)的PD3(INT1)引脚,但是我只是用了PD3功能而
*  没有使用中断INT1功能。DS18B20(TO-92)的连接如下:
*  +-------------+-------------------------------------+
*  | Pin-1(GND)  | GND(ground)                         |
*  +-------------+-------------------------------------|
*  | Pin-2(DQ)   | 通过240欧姆的电阻连接到ATmega8的    |
*  |             | PD3引脚,同时用一个3K的电阻上拉到   |
*  |             | VCC(5V)。                           |
*  +-------------+-------------------------------------+
*  | Pin-3(VD)   | GND(ground)                         |
*  +-------------+-------------------------------------+
*      说明:很显然,我采用的是"总线窃电"模式,这是DS-
*  18B20数据手册认可的工作模式之一。串联240欧姆电阻的用意
*  是为了防止有缺陷的用户程序损坏DS18B20的可能性。例如,
*  如果用户没有正确地用OC(集电极开路)或OD(漏极开路)结构去
*  驱动DS18B20,而是错误地选择了推挽式结构,则DS18B20会立
*  即或在"被虐待"一段时间后"死翘翘"(^_^)。当然,240欧姆的
*  取值未必是最恰当的,设计者可自己去优化。这需要设计者仔细
*  阅读数据手册。
*
*      此外,我采用了DS18B20的默认精度(12位),并未修改,我
*  也没有使用它的其它功能。简而言之,这只是一个简单的读取温度
*  的实例。用户可以自己去改进,自己去研究数据手册(我并未完全
*  看,只看了想看的一点点 ^_^ )。但是我可以肯定一点,这个程
*  序我是实践过的!并且成功地读取了温度值!
*
*      请注意我是如何驱动DS18B20的:我是用AVR的方向寄存器
*  而不是输出端口寄存器! 同时预先在输出端口寄存器中写入0。
*  这实际上相当于一个三态门:只不过输入被接地,使它成了一
*  个OC门,使能端成了这个OC门的实际输入!
*
*      1-wire总线的电气特性与I2C总线相似,具有线与功能,所
*  以,总线上的任一设备都可在合适的时间强行拉低总线,但是总线
*  要呈现高电平,则必须是每一个设备都释放了总线。就像我下面的
*  宏DQ_TO_1(),它只是释放了总线,但不是说总线一定被强行驱动
*  至高电平,总线的高电平是由上拉电阻实现的。
*/
#define  DQ_18B20       (1<<3)                // PD3
#define  DQ_TO_0()      (DDRD |=  DQ_18B20)   // PD3='0'
#define  DQ_TO_1()      (DDRD &= ~DQ_18B20)   // PD3='float'
#define  DQ_status()    (PIND & DQ_18B20)     // read PD3 pin

/*     请认真检查你的AVR微控制器的时钟频率! 特别注意:频率
* 定义的单位是MHz! 并且请使用浮点数! 假如你的晶振是12MHz,
* 你应该写成12.0000或12.0之类。
*     我的实验电路的晶振是:11.0592MHz
*/
#ifndef  CPU_CRYSTAL
#define  CPU_CRYSTAL    (11.0592)
#endif

/*     请包含WinAVR系统提供的延时头文件"delay.h",其中给出
*  两个延时模块,我用16位的那个(16-bit count, 4 cycles/l-
*  oop.),细节请看这个头文件。
*/
#define  wait_us(us)/
    _delay_loop_2((INT16U)((us)*CPU_CRYSTAL/4))

/*---------------- 函数原型声明 ------------------*/
// 1个初始化模块
void   ds18b20_config(void);       // 配置端口

// 3个基本模块
BOOL   ds18b20_reset(void);        // 复位DS18B20
void   ds18b20_write(INT8U dat);   // 写字节到DS18B20
INT8U  ds18b20_read(void);         // 读字节从DS18B20

// 2个应用模块
void   convert_T(void);            // 启动温度转换
INT16U read_T(void);               // 读取转换值


/*-------------------------------------------------------
*  配置(使能)AVR与DS18B20的接口
*/
void ds18b20_config(void)
{
    DDRD  &= ~DQ_18B20;   // 输入模式(上电时为高电平)
    PORTD &= ~DQ_18B20;   // 输出锁存器写0,以后不再更改
}

/*-------------------------------------------------------
*    复位1-wire总线,并探测是否有温度芯片DS18B20(TO-92
*  封装)挂在总线上,有返回SUCC,没有返回FAIL
*/
BOOL ds18b20_reset(void)
{
    BOOL bus_flag;

    DQ_TO_0();      // 设置1-wire总线为低电平(占领总线)...

    /* 现在延迟480us~960us, 与硬件密切相关,但应尽可能选小值(480us),
       把抖动留给系统(比如在延迟期间发生中断导致延迟变长)。
     */
    wait_us(490);   // 490us

    cli();          // 下面这段时间要求比较严格,为保险起见,关中断
    DQ_TO_1();      // 设置1-wire总线为高电平(释放总线)
    
    /* 这个浮点数是由编译器计算好的,而不是由你的MCU在运行时临时计算的,
       所以不会占用用户MCU的时间,不必担心(看看前面的宏你就可以确定了)
     */
    wait_us(67.5);  // 最佳时间: 60us+7.5us!(忙延时,只是一种策略)
    
    // 探测总线上是否有器件    
    if(DQ_status()) bus_flag=FAIL;   // 复位单总线但没有发现有器件在线
    else bus_flag=SUCC;              // 复位单总线并发现有器件在线
    
    sei();          // 退出临界代码区(开中断)

    /* 保证Master释放总线的时间(不是说总线处于高电平的时间)不小于
       480us即可,这一时间从读总线状态之前就开始了,所以这里把这个
       时间计算在内。在Master释放总线的前半段,也是被动器件声明它
       们在线之时。
     */
    wait_us(490-67.5);   // 490-67.5us

    return(bus_flag);
}

/*--------------------------------------------------------
*  写命令或数据到温度芯片DS18B20(发送一个字节)
*/
void ds18b20_write(INT8U dat)
{
    INT8U count;

    // 每个字节共8位,一次发一位
    for(count=0; count<8; count++) {
        cli();                   // 保证绝对不会发生中断!
        DQ_TO_0();               // 设置1-wire总线为低电平
        wait_us(2);              // about 2us
        
        if(dat&0x01) DQ_TO_1();  // 并串转换,先低位后高位
        else DQ_TO_0();
        dat >>= 1;               // 下一位做好准备
        
        // 60us~120us(实际不能到120us, 因为其它语句也用时间了!)
        wait_us(62);             // 62us
        
        DQ_TO_1();
        sei();                   // 恢复系统中断
        wait_us(2);              // 2us
    }
}

/*---------------------------------------------------------
*  从温度芯片DS18B20读配置或数据(接收一个字节)
*/
INT8U ds18b20_read(void)
{
    INT8U count,dat;

    dat = 0x00;       // 数据接收准备
    
    // 每个字节共8位,一次收一位
    for(count=0; count<8; count++) {
        cli();        // 保证绝对不会发生中断!
        
        // 从总线拉低到读总线状态,不能大于15us!
        DQ_TO_0();    // 设置1-wire总线为低电平(拉低总线以同步)
        wait_us(2);   // 2us
        DQ_TO_1();    // 设置1-wire总线为高电平(释放总线)
        wait_us(4);   // 4us        
        dat >>= 1;
        if(DQ_status()) dat|=0x80;   // 读取总线电平,先收低位再收高位
        
        sei();        // 恢复系统中断
        wait_us(62);  // 必须大于60us
    }
    return(dat);
}

/*-------------------------------------------------------
*     我的电路中只有一个器件DS18B20,所以不需要多个器件的ID
*  识别,跳过之后,启动温度转换,但在启动后,用户应等待几百个
*  毫秒,才能读到这次的转换值,这是DS18B20的数据手册规定的。
*  因为温度转换是需要时间的嘛!(^_^)
*/
void convert_T(void)
{
    if(ds18b20_reset()==SUCC) {  // 如果复位成功
        ds18b20_write(0xcc);     // 跳过多器件识别
        ds18b20_write(0x44);     // 启动温度转换
    }
}

/*-------------------------------------------------------
*  读取转换后的温度值
*  我假定DS18B20一定是正确的,所以没有返回有关状态。当你故意
*  把DS18B20从电路中拔下而能让程序告诉你出错时,你可以自己修
*  改这段代码!
*/
INT16U read_T(void)
{
    INT16U value=0;
    
    if(ds18b20_reset()==SUCC) {  // 如果复位成功
        ds18b20_write(0xcc);     // 跳过多器件识别
        ds18b20_write(0xbe);     // 读暂存器
        value  = (INT16U)ds18b20_read();       // 低字节
        value += (INT16U)(ds18b20_read())<<8;  // 高字节
    }
    return(value);
}


#endif
/* 文件ds18b20.c结束 */

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值