#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdbool.h>

#define DQ_DDR_IN    DDRA &= ~(1<<PA0)  // 设置为输入
#define DQ_DDR_OUT   DDRA |= (1<<PA0)   // 设置为输出
#define DQ_SET    PORTA |= (1<<PA0)  // 18b20 高电平
#define DQ_CLR    PORTA &= ~(1<<PA0) // 18b20 低电平
#define DQ_IN      PINA & (1<<PA0)    // 18b20信号输入


/**
   DS18B20初始化
 */
unsigned char ds18b20_init(void)
{
    unsigned char is_reset_ok;

    DQ_DDR_OUT; // PA2设置为输出口(相当于拉低数据线上的电平)

    // 复位脉冲
    DQ_SET;
    DQ_CLR; // 输出低电平脉冲
    _delay_us(500); //延时:480us~960us
    DQ_SET;

    // 应答总共480us,包括等待15~60us,应答60~240us,上拉。
    _delay_us(100); // 延时大于60US,

    // 检查复位是否成功
    DQ_DDR_IN; //输入 释放数据线(相当于拉高数据线上的电平)
    //while(DQ_R); //可以用两个while()死循环来判断复位是否成功,当数据线被拉低,说明
    //while(!(DQ_R)); //18b20开始复位应答,当数据线变高,说明应答完毕

    if(DQ_IN) //判断DS18B20是否拉低数据线
    {
        is_reset_ok = 0; // 数据线是高?复位失败
    }
    else
    {
        is_reset_ok = 1; // 数据线是低?复位成功
    }

    _delay_us(500); //有复位应答信号后,应当再延时一段时间,以等待应答完毕

    return is_reset_ok; //返回复位标志
}

/**
    向18b20写入一个字节
 */
void ds18b20_write_byte(unsigned char dat)
{

    unsigned char i;

    for(i = 0; i < 8; i++) //写8次,一次写1位,先写低字节
    {
        DQ_DDR_OUT; //拉低数据线2us,开始写数据
        DQ_CLR;
        _delay_us(10);

        if(dat & 0x01) //写数据
        {
            DQ_SET;
        }
        _delay_us(100); //延时大于60us
        DQ_SET;
        dat >>= 1;
    }
}



/**
    从DS18B20读取一个字节数据
 */
unsigned char ds18b20_read_byte(void)
{
    unsigned char i;
    unsigned char dat = 0; // dat用于存储读到的数据,先清零

    for(i = 0; i < 8; i++) //共读8位数据,构成一个字节
    {
        dat = dat >> 1; //数据右移,读顺序:先低后高

        DQ_DDR_OUT; //定义为输出(拉低数据线)
        DQ_CLR;
        _delay_us(10); //拉低2微秒
        DQ_SET;
        DQ_DDR_IN; //定义成输入,读入数据(同时也相当于拉高数据线)

        if(DQ_IN) //读数据,
        {
            dat |= 0x80; //如果是高,置1,右移数据
        }
        _delay_us(50); //延时大于60us
    }

    return dat; //返回读到的1字节数据
}


/**
    从DS18B20读取温度信息到十进制字符数组
 */
void ds18b20_get_temperature(unsigned char temp[10])
{
    unsigned char temp_h, temp_l; //温度高位,低位,复位成功标志
    unsigned int temp_i; // 中间变量

    temp_h = 0;
    temp_l = 0;
    temp_i = 0;

    cli(); //关中断

    ds18b20_init(); // 初始化ds18b20
    ds18b20_write_byte(0xcc); // 跳过序列号
    ds18b20_write_byte(0x44); // 发送温度转换命令
    _delay_ms(10); // 延时,等转换完成

    ds18b20_init(); // 初始化ds18b20
    ds18b20_write_byte(0xcc); // 跳过序列号
    ds18b20_write_byte(0xbe); // 发送读取暂存器指令
    temp_l = ds18b20_read_byte(); //获得温度的低位
    temp_h = ds18b20_read_byte(); //获得温度的高位
    if(temp_h & 0x08) //判断温度的正负
    {
        //负温度。取反加1
        temp_h = ~temp_h;
        temp_l = ~temp_l;
        //清零进位位标志
        SREG |= ~(1 << 0);
        //温度低字节加1
        temp_l++;
        if(SREG & (1 << 0)) //有进位吗?
        {
            temp_h++; //有进位,则温度高字节加1
        }

        // 负温度
        temp[0] = '-';
    }
    else
    {
        // 正温度
        temp[0] = '+';
    }


    temp_i = ((temp_h << 4) & 0x70) | (temp_l >> 4); //获得温度的整数位
    temp[1] = temp_i / 1000 + 0x30; //千位
    temp[2] = temp_i % 1000 / 100 + 0x30; //百位
    temp[3] = temp_i % 100 / 10 + 0x30; //十位
    temp[4] = temp_i % 10 + 0x30; //个位

    temp[5] = '.'; //小数点

    temp_i = temp_l & 0x0f; //取出温度的小数位
    temp_i = (temp_i * 625); //小数位乘以0.625得出温度的小数位值,在此扩大1000倍,得出温度的4位小数位,显示的时候加小数点
    temp[6] = temp_i / 1000 + 0x30; //千位
    temp[7] = temp_i % 1000 / 100 + 0x30; //百位
    temp[8] = temp_i % 100 / 10 + 0x30; //十位
    temp[9] = temp_i % 10 + 0x30; //个位

    sei(); //开中断
}

[ 编辑 | 历史 ]
最近由“jilili”在“2013-12-10 11:30:30”修改