AVR的DS18B20 温度采集示例程序(原发于2007-11-20)

来源:互联网 发布:seo博客资源 编辑:程序博客网 时间:2024/04/29 20:25
DS18B20 是 Dallas 的数字集成化温度采集芯片。其实早就想用用了。一直放在那里。最近朋友说调不出来,正好就看了一下,花了一天时间,看了数据手册,参考了一下别人的程序,写了一个测试程序。和大家一起分享。共同进步。
我用的是Mega128 , 接是的7.3728M的晶振,用了mega128的串口0。用PB2口与18B20连接.
程序在AVRStudio4.12sp4 + WinAVR20060421, Winxp环境下编译通过。

如果想要移到其他的AVR芯片上的话很简单,如下:
    1.修改串口初始化的代码。因为各个AVR芯片寄存器然存在差别
    2.修改时钟,因为要用到延时程序_delay_us(). 关于_delay_us()如何用,上篇文章我已说了。
    3.修改你的单片机的型号。
    4.修改你与18b20的 DQ 线的接口的引脚定义。
    5.打开代码优化。
    6.用avr-gcc重新编译整个工程,下载到目标板,运行。就可以了。
建义使用前看看DS18B20的英文手册,说得很清楚。中文的都是对DS1820来说明的。有些区别。
如果你想要了解 CRC 如何校验的,请参考 Maxim 的 AN27 应用手册(中文和英文都有)。呵呵。就说这么多了吧。下面是程序。

/*********************************************/
//           DS18B20 温度采集示例程序
//           2007.11.20
//           semilog
/*********************************************/
#include <avr/io.h>         //引脚定义 
#include <avr/interrupt.h>  //中断服务宏
#include <util/delay.h>     //延时的宏
#include <stdint.h>         //数据类型宏
#include <avr/eeprom.h>     //EEPROM存储器操作的宏
#include <stdlib.h>
#include <avr/pgmspace.h>
//××××××××××××引脚宏定义×××××××××××××
#define setbit(var, bit) ((var) |= (1 << (bit))) //把某一位置1
#define clrbit(var, bit) ((var) &=~(1 << (bit))) //把某一位清0
//18B20定义
#define SET_DQ (PORTB) |= (1 << (PB2))      // 18b20 高电平
#define CLR_DQ (PORTB) &=~(1 << (PB2))      // 18b20 低电平
#define DQ_IN  (PINB)&(1<<(PB2))            // 18b20信号输入
#define SET_OUT (DDRB)|= (1<<(PB2))         //PB2定义成输出
#define SET_IN  (DDRB)&=~(1<<(PB2))         //PB2定义成输入

/***********************变量定义***************************/
uint8_t u_flag=0;

prog_uint8_t CRC_Code[256]= {
    0,  94, 188,  226,  97,  63,  221,  131,  194,  156,  126,  32,  163,  253,  31,  65,
    157,  195,  33,  127,  252,  162,  64,  30,  95,  1,  227,  189,  62,  96,  130,  220,
    35,  125,  159,  193,  66,  28,  254,  160,  225,  191,  93,  3,  128,  222,  60,  98,
    190,  224,  2,  92,  223,  129,  99,  61,  124,  34,  192,  158,  29,  67,  161,  255,
    70,  24,  250,  164,  39,  121,  155,  197,  132,  218,  56,  102,  229,  187,  89,  7,
    219,  133, 103,  57,  186,  228,  6,  88,  25,  71,  165,  251,  120,  38,  196,  154,
    101,  59, 217,  135,  4,  90,  184,  230,  167,  249,  27,  69,  198,  152,  122,  36,
    248,  166, 68,  26,  153,  199,  37,  123,  58,  100,  134,  216,  91,  5,  231,  185,
    140,  210, 48,  110,  237,  179,  81,  15,  78,  16,  242,  172,  47,  113,  147,  205,
    17,  79,  173,  243,  112,  46,  204,  146,  211,  141,  111,  49,  178,  236,  14,  80,
    175,  241, 19,  77,  206,  144,  114,  44,  109,  51,  209,  143,  12,  82,  176,  238,
    50,  108,  142,  208,  83,  13,  239,  177,  240,  174,  76,  18,  145,  207,  45,  115,
    202,  148, 118,  40,  171,  245,  23,  73,  8,  86,  180,  234,  105,  55,  213, 139,
    87,  9,  235,  181,  54,  104,  138,  212,  149,  203,  41,  119,  244,  170,  72,  22,
    233,  183,  85,  11,  136,  214,  52,  106,  43,  117,  151,  201,  74,  20,  246,  168,
    116,  42,  200,  150,  21,  75,  169,  247,  182,  232,  10,  84,  215,  137,  107,  53
};    //定义256位CRC 校验码在 code 程序区
//××××××××××××函数申明×××××××××××××
void USART_init(void);       //串口初始化
void INT_Init(void);         //中断初级化

void Send_uint8_t(uint8_t dat); //发送一个字节
void Send_str(uint8_t *str);    //发送字符串

uint8_t Init_18b20(void);           //18b20初始化
uint8_t read_18b20(void);        //读18b20温度
void write_18b20(uint8_t dat);   //写18b20温度

/****************主程序***********************/
int main(void)
{
    uint32_t temp;
    uint8_t i,temp_l,temp_h,a,b,c,d,temp_flag;
    uint8_t rom64[8],scr_pad[9];
    uint8_t crc_temp;
    USART_init();
    INT_Init();
    crc_temp=0;
    temp_flag=0;

    Init_18b20();          //初始化,然后读出温度值
    write_18b20(0xCC);     //发送ROM命令,不进行匹配
    write_18b20(0x4E);     //发送功能命令,写配置寄存器
    write_18b20(0x02);     // TH
    write_18b20(0x80);     // TL
    write_18b20(0x7F);     // Config  :0x1F:9位, 0x3F:10位 ,0x5F:11位 ,0x7F:12位
    // 所需时间: 93.75ms   187.75ms    375ms      750ms
    Init_18b20();          //初始化,然后读出温度值
    write_18b20(0x33);     //发送ROM命令,读64-bit ROM 值
    for(i=0; i<8; i++)
    {
        rom64[i]=read_18b20();
    }
    /*
       for(i=0;i<8;i++)   //查看ROM值, DS18b20的型号代码:28H
       {
             Send_uint8_t(rom64[i]);
       }
    */
    crc_temp = pgm_read_byte_near(&CRC_Code[rom64[0]]);   //算第一个CRC码
    for(i=1; i<7; i++)         /*后面的CRC码:I = (当前CRC) EXOR (输入字)*/
    {
        crc_temp =  pgm_read_byte_near(&CRC_Code[rom64[i]^crc_temp]);
    }
    if(crc_temp == rom64[7])
        Send_str("ROM CRC较验成功");
    else
        Send_str("ROM CRC较验失败");
    Send_uint8_t(0x0D);
    Send_uint8_t(0x0A);
    while(1)
    {
        nit_18b20();          //初始化18b20
        write_18b20(0xCC);     //发送ROM命令,不进行匹配
        write_18b20(0x44);     //启动18B20进行转换
        for(i=0; i<50; i++)    //延时等转换完成
        {
            _delay_ms(20);
        }
        Init_18b20();          //初始化,然后读出温度值
        write_18b20(0xCC);     //发送ROM命令,不进行匹配
        write_18b20(0xBE);     //发送功能命令,读出暂存寄存器中内容
        for(i=0; i<9; i++)
        {
            scr_pad[i]=read_18b20();   //读出9位scratchpad 的值
        }

//----------下面校验读出的值是否正确---------------
        crc_temp = pgm_read_byte_near(&CRC_Code[scr_pad[0]]);   //选算第一个CRC码
        for(i=1; i<8; i++)
        {
            crc_temp =  pgm_read_byte_near(&CRC_Code[scr_pad[i]^crc_temp]);
        }

        if( crc_temp==scr_pad[8] ) //如果结果正确则显示
        {
            if(scr_pad[1]&0x08)
            {
                scr_pad[1]=~scr_pad[1];
                scr_pad[0]=~scr_pad[0];

                SREG=SREG&(~(1<<SREG_C));   //清零 C 进位
                scr_pad[0]++;
                if(SREG&(1<<SREG_C))        //如果有进位,则高位加1
                    scr_pad[1]++;
            }
            temp = scr_pad[1];
            temp = (temp << 8) + scr_pad[0];
            temp = (temp*625)/1000;   //如果直接乘0.625的话,因为用到浮点数,
            //代码会曾加很多
            a = temp/1000 +0x30;
            b = (temp%1000)/100 +0x30;
            c = (temp%100)/10 +0x30;
            d = temp%10 + 0x30;
            Send_str("当前温度:");
            if(temp_flag)   //发送温度符号
                Send_uint8_t('-');
            else
                Send_uint8_t(' ');
            if(a>0x30)
                Send_uint8_t(a);
            if((a!=0x30)|(b!=0x30))
                Send_uint8_t(b);
            Send_uint8_t(c);
            Send_uint8_t('.');
            Send_uint8_t(d);
        }
        else                    //结果不正确则提示错误
        {
            Send_str("发生错误,读出的结果不正确");
        }
        Send_uint8_t(0x0D);
        Send_uint8_t(0x0A);
    }
    return(0);
}

//××××××××××××MCU初始化×××××××××××××
void USART_init(void)  //初始化串口
{
    UBRR0H=0;
    UBRR0L=47;                                        //设置波特率为9600,(使用外部7.3728M晶振)
    UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);                 //8位数据,1位停止位,无校验
    UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);       //接收使能,发送使能, 接收中断使能
}

void INT_Init(void)  //中断初级化
{
    EICRA = (1<<ISC01)|(1<<ISC11);     //INT0 INT1 的中断触发方式为下降沿触发
    EIMSK = (1<<INT1)|(1<<INT0);       //开中断INT0 INT1
    sei();                             //全局中断使能
}

/****************** DS18b20操作 **************************/
uint8_t Init_18b20(void)            //18b20初始化
{
    uint8_t flg,i;
    SET_OUT;
    CLR_DQ;
    for(i=0; i<10; i++)       //拉低 500us
    {
        _delay_us(50);
    }
    SET_IN;            //输入 释放
    _delay_us(80);
    if(DQ_IN)          //等待回复
        flg = 0;
    else
        flg = 1;        //0表示初始化成功
    for(i=0; i<4; i++) //回复的低电平在60到240US
    {
        _delay_us(60);
    }
    SET_OUT;
    SET_DQ;            //回到初始DQ=1;
    return flg;
}

//-------------- 写18b20温度 --------------------
void write_18b20(uint8_t dat)   //写18b20温度
{
    uint8_t i;
    SET_OUT;            //控制脚定义成输出
    for(i=0; i<8; i++)
    {
        CLR_DQ;
        _delay_us(5);
        if(dat&1)        //写数据,从低位开始
        {
            SET_DQ;       // bit=1
        }
        else
        {
            CLR_DQ;       // bit=0
        }
        _delay_us(60);   // wait for 60us ,total > 60us
        SET_DQ;          //再次拉高
        delay_us(10);    //写两个位之间的间隔
        dat>>=1;
    }
    SET_DQ;
}
//--------------- 读18b20温度 -------------------
uint8_t read_18b20(void)   //读18b20温度
{
    uint8_t i, dat=0;        // dat用于存储读到的数据
    SET_OUT;               //第一次拉低,设置引脚为输出
    for(i=0; i<8; i++)
    {
        dat>>=1;
        CLR_DQ;
        _delay_us(5);    //拉低5微秒
        SET_IN;          //定义成输入,读入状态
        _delay_us(5);    //等18b20响应
        if(DQ_IN)        //读状态,放入dat中
        {
            dat |=0x80;    //放到最高位,左移
        }
        _delay_us(80);   //wait for 60~120us
        SET_DQ;
        SET_OUT;         //再次定义成输出
    }
    return dat;
}

/*******************串口操作***********************/
void Send_uint8_t(uint8_t dat)   //发送一个字节
{
    while(!(UCSR0A&(1<<UDRE0)));
    UDR0= dat;
}
void Send_str(uint8_t * str)   //发送字符串
{
    while(*str!=0)
    {
        Send_uint8_t(*str);
        str++;
    }
}

//××××××××××××中断服务程序×××××××××××××
ISR(USART0_RX_vect)        // USRAT0 接收中断服务子程序
{
    uint8_t dat;
    dat = UDR0;
    if(dat=='A') u_flag=0;
    if(dat=='B') u_flag=1;
    if(dat=='C') u_flag=2;
}
//程序完