xiaoluo2009 发表于 2009-11-21 09:43:23

终于调通DS18B20,ATmega8+DS18B20,希望对像我这样的新手有点用。

今天早上终于把DS18B20给调试出来了,程序比较简单,因为我还是新手,刚学AVR不久。希望这个程序对像我这样的新手有点帮助。
我用的是ATmega8,时钟是1M的。内部时钟振荡。
MCU:ATmega8
CLK:1M

PC1:RS
PC2:R/W
PC3:E
PD0--PD7:液晶1602数据双向口
PB0;ds18b20数据I/O口

#include<iom8v.h>
#include<macros.h>

#define uchar unsigned char
#define uint unsigned int

#define rs_h PORTC |= BIT(1)
#define rs_l PORTC &= ~BIT(1)
#define rw_h PORTC |= BIT(2)
#define rw_l PORTC &= ~BIT(2)
#define e_hPORTC |= BIT(3)
#define e_lPORTC &= ~BIT(3)

#define DQ_H PORTB |= BIT(0)
#define DQ_L PORTB &= ~BIT(0)

uchar temp,temp_dec,nub;
//温度的整数,温度的小数变量
uchar lcd={"0123456789."};
uchar b_d={0};
/****************************/
void delay_ms(uint x)
{
    uchar i;
        while(x--)
        {
          for(i=0;i<100;i++);
        }
}

/**************************/
void delay_us(uint x)
{
    while(x--);
}

/***************************/
void port_init(void)
{
    DDRC |= BIT(1)|BIT(2)|BIT(3);
        PORTC &= ~(BIT(1)|BIT(2)|BIT(3));
        DDRD=0XFF;
        PORTD=0X00;
       
        DDRB |= BIT(0);
        PORTB |= BIT(0);
       
        DDRB |= BIT(1);
        PORTB |= BIT(1);   
}

/********18b20*****************/
void reset(void)//复位函数
{
    DQ_H;             //数据线高
        DQ_L;             //把数据线拉低
        delay_us(80);   //延时480us
        DQ_H;             //释放总线。       
        DDRB &= ~BIT(0);//改变PB0口的方向
        delay_us(10);   //延时60us
        while( PINB&BIT(0) );        //复位成功
        delay_us(40);       //延时240us
        //PORTB &= ~BIT(1); //检测一下是不是在死循环里了.
        DDRB |= BIT(0);
        DQ_H;            //再次释放总线
}

/***************************/
void write_byte(uchar x)
{
    uchar i;
        for(i=0;i<8;i++)
        {          
                DQ_L;   //拉低数据线,空闲时是为高电平
                delay_us(1); //15us
                if( ( x&0x01 )==0x01 ) DQ_H;
                else DQ_L;
                delay_us(5); //45us
                DQ_H;//释放总线,以便于下次进for语句时,DQ_H;是为高电平.
                x>>=1;
        }       
}

/********************************/
uchar read_byte(void)   //ds18b20读一字节
{
   uchar i,z;
   z=0;
   for(i=0;i<8;i++)
   {
       z>>=1;                
           DQ_L;         //拉低数据线,空闲时是为高电平
           DQ_H;         //释放总线
           delay_us(1);    //延时15us
           DDRB &= ~BIT(0);
           if( ( PINB&BIT(0) )==0X01 ) z|=0x80;
           else z&=0x7f;
           delay_us(10);    //延时60us
           DDRB |= BIT(0);
           DQ_H;            //再次释放总线
   }   
   return(z);   
}

/*******************************/
void get_time(void)
{
    uchar temp_l,temp_h,i;
               
       
        reset();         //复位.
        write_byte(0xcc);//跳过ROM.
        write_byte(0x44);//温度转换.
        delay_ms(2000);    //延时2秒.
        reset();         //复位.
        write_byte(0xcc);//跳过ROM.
        write_byte(0xbe);//读暂存存储器.
        temp_l=read_byte();    //温度存储器中的温度低8位.
        temp_h=read_byte();    //温度存储器中的高8位.
        reset();             //再来个复位,终止读暂存存储器.
                       
        i=temp_l;
        i &= 0x0f;      //温度的低四位.
        temp_dec=i/16;      //温度的小数.
        temp_dec=temp_dec*1000; //放大1000倍,用于显示小数点后的小数.
       
        temp_l>>=4;//把小数点移掉,得到温度整数的个位,既低四位。            
        temp_h<<=4;   //把前面4个符号位移掉,只保留一位符号位和温度的高三位。
        temp = temp_l|temp_h;        //温度的整数.
       
}

/*******************************/
void b_d_data(void)   //数据处理
{
    b_d=temp/100;   //温度的百位.
        b_d=temp%100/10;//温度的十位.
        b_d=temp%100%10;//温度的个位.
       
        b_d=temp_dec/1000;//小数的第一位
        b_d=temp_dec%1000/100;//小数的第二位
        b_d=temp_dec%1000%100/10;//小数的第三位
        b_d=temp_dec%1000%100%10;//小数的第四位
       
       
        //b_d=temp_dec%10;//温度的一位小数.
}



/********1602液晶***************/
void busy(void)   //1602判断忙函数
{
    DDRD=0X00;
        rs_l;
        rw_h;
        e_h;
        while( ( PIND&0x80 )==0x80 );       
        e_l;
        DDRD=0xff;
}
/******************************/
void write_com(uchar com)//1602液晶写命令
{
    busy();
    PORTD=0x00;
    rs_l;
        rw_l;
        PORTD=com;
        e_h;
        e_l;
        PORTD=0x00;   
}

/*****************************/
void write_data(uchar data)//1602写数据
{
    busy();
    PORTD=0x00;
    rs_h;
        rw_l;
        e_h;
        PORTD=data;       
        e_l;
        PORTD=0x00;
}

/*****************************/
void init_1602(void)
{
    write_com(0x38);    //1602显示模式设置
        write_com(0x01);    //清显示
        delay_ms(2);
        write_com(0x0c);    //开显示,不显示光标,光标不闪烁。
        write_com(0x06);    //当写一字符时,地址加一,而且光标加一.
}

void disp_lcd(void)//显示刷新
{
    write_com(0x80+0x04);//显示地址
        write_data(lcd]);
        write_data(lcd]);
        write_data(lcd]);
        write_data(lcd);
        write_data(lcd]);
        write_data(lcd]);
        write_data(lcd]);
        write_data(lcd]);
}

/**************************/
void main(void)
{
    port_init();       
        init_1602();               
        while(1)
        {
          get_time();
          b_d_data();               
                disp_lcd();
        }
}

kangkang 发表于 2009-11-22 12:40:25

恭喜啊

taio 发表于 2009-11-22 15:51:26

MARK

xiaoluo2009 发表于 2009-11-22 21:57:07

呵呵,自己也庆祝一下

jingxinchong 发表于 2009-11-22 22:39:34

我也在调18b20的,可是用的是mega16,使用内部1M时钟,直接改了数据端口,可是显示不了。请问楼主,16的1M和8的1M时钟是不是一样的?
如下修改后的,请楼主给看看,问什么不能显示?谢谢
/*MCU:ATmega16
CLK:1M

PC4:RS
PC5:R/W
PC6:E
PA0--PA7:液晶1602数据双向口
PB7;ds18b20数据I/O口
*/
#include<iom16v.h> ////////改了头文件
#include<macros.h>

#define uchar unsigned char
#define uint unsigned int

#define rs_h PORTC |= BIT(4) ////////改了端口
#define rs_l PORTC &= ~BIT(4) ////////改了端口
#define rw_h PORTC |= BIT(5) ////////改了端口
#define rw_l PORTC &= ~BIT(5) ////////改了端口
#define e_hPORTC |= BIT(6) ////////改了端口
#define e_lPORTC &= ~BIT(6) ////////改了端口

#define DQ_H PORTB |= BIT(7) ////////改了端口
#define DQ_L PORTB &= ~BIT(7) ////////改了端口

uchar temp,temp_dec,nub;
//温度的整数,温度的小数变量
uchar lcd={"0123456789."};
uchar b_d={0};
/****************************/
void delay_ms(uint x)
{
    uchar i;
while(x--)
{
    for(i=0;i<100;i++);
}
}

/**************************/
void delay_us(uint x)
{
    while(x--);
}

/***************************/
void port_init(void)
{
    DDRC |= BIT(4)|BIT(5)|BIT(6); ////////改了端口
PORTC &= ~(BIT(4)|BIT(5)|BIT(6)); ////////改了端口
DDRA=0XFF; ////////改了端口
PORTA=0X00; ////////改了端口

DDRB |= BIT(7); ////////改了端口
PORTB |= BIT(7); ////////改了端口

DDRB |= BIT(1);
PORTB |= BIT(1);   
}

/********18b20*****************/
void reset(void)//复位函数
{
    DQ_H;             //数据线高
DQ_L;             //把数据线拉低
delay_us(80);   //延时480us
DQ_H;             //释放总线。
DDRB &= ~BIT(7);//改变PB0口的方向 ////////改了端口
delay_us(10);   //延时60us
while( PINB&BIT(7) ); //复位成功         ////////改了端口
delay_us(40);       //延时240us
//PORTB &= ~BIT(1); //检测一下是不是在死循环里了. ////////改了端口
DDRB |= BIT(7);                     ////////改了端口
DQ_H;            //再次释放总线
}

/***************************/
void write_byte(uchar x)
{
    uchar i;
for(i=0;i<8;i++)
{      
DQ_L;   //拉低数据线,空闲时是为高电平
delay_us(1); //15us
if( ( x&0x01 )==0x01 ) DQ_H;
else DQ_L;
delay_us(5); //45us
DQ_H;//释放总线,以便于下次进for语句时,DQ_H;是为高电平.
x>>=1;   
}
}

/********************************/
uchar read_byte(void)   //ds18b20读一字节
{
   uchar i,z;
   z=0;
   for(i=0;i<8;i++)
   {
       z>>=1;         
   DQ_L;         //拉低数据线,空闲时是为高电平
   DQ_H;         //释放总线
   delay_us(1);    //延时15us
   DDRB &= ~BIT(7);               ////////改了端口
   if( ( PINB&BIT(7) )==0X80 ) z|=0x80; ////////改了端口和括号中0x01为0x80
   else z&=0x7f;
   delay_us(10);    //延时60us
   DDRB |= BIT(7);             ////////改了端口
   DQ_H;            //再次释放总线
   }   
   return(z);   
}

/*******************************/
void get_time(void)
{
    uchar temp_l,temp_h,i;


reset();         //复位.
write_byte(0xcc);//跳过ROM.
write_byte(0x44);//温度转换.
delay_ms(2000);    //延时2秒.
reset();         //复位.
write_byte(0xcc);//跳过ROM.
write_byte(0xbe);//读暂存存储器.
temp_l=read_byte();    //温度存储器中的温度低8位.
temp_h=read_byte();    //温度存储器中的高8位.
reset();             //再来个复位,终止读暂存存储器.

i=temp_l;
i &= 0x0f;      //温度的低四位.
temp_dec=i/16;      //温度的小数.
temp_dec=temp_dec*1000; //放大1000倍,用于显示小数点后的小数.

temp_l>>=4;//把小数点移掉,得到温度整数的个位,既低四位。            
temp_h<<=4;   //把前面4个符号位移掉,只保留一位符号位和温度的高三位。
temp = temp_l|temp_h; //温度的整数.

}

/*******************************/
void b_d_data(void)   //数据处理
{
    b_d=temp/100;   //温度的百位.
b_d=temp%100/10;//温度的十位.
b_d=temp%100%10;//温度的个位.

b_d=temp_dec/1000;//小数的第一位
b_d=temp_dec%1000/100;//小数的第二位
b_d=temp_dec%1000%100/10;//小数的第三位
b_d=temp_dec%1000%100%10;//小数的第四位


//b_d=temp_dec%10;//温度的一位小数.
}



/********1602液晶***************/
void busy(void)   //1602判断忙函数
{
    DDRA=0X00; ////////改了端口
rs_l;
rw_h;
e_h;
while( ( PINA&0x80 )==0x80 ); ////////改了端口
e_l;
DDRA=0xff; ////////改了端口
}
/******************************/
void write_com(uchar com)//1602液晶写命令
{
    busy();
    PORTA=0x00; ////////改了端口
    rs_l;
rw_l;
PORTA=com; ////////改了端口
e_h;
e_l;
PORTA=0x00;   ////////改了端口
}

/*****************************/
void write_data(uchar data)//1602写数据
{
    busy();
    PORTA=0x00; ////////改了端口
    rs_h;
rw_l;
e_h;
PORTA=data; ////////改了端口
e_l;
PORTA=0x00; ////////改了端口
}

/*****************************/
void init_1602(void)
{
    write_com(0x38);    //1602显示模式设置
write_com(0x01);    //清显示
delay_ms(2);
write_com(0x0c);    //开显示,不显示光标,光标不闪烁。
write_com(0x06);    //当写一字符时,地址加一,而且光标加一.
}

void disp_lcd(void)//显示刷新
{
    write_com(0x80+0x04);//显示地址
write_data(lcd]);
write_data(lcd]);
write_data(lcd]);
write_data(lcd);
write_data(lcd]);
write_data(lcd]);
write_data(lcd]);
write_data(lcd]);
}

/**************************/
void main(void)
{
    port_init();
init_1602();
while(1)
{
    get_time();
    b_d_data();
disp_lcd();
}
}

xiaoluo2009 发表于 2009-11-22 22:46:02

硬件有都是好的吗?你的mega16复位脚是PC6吗?

xiaoluo2009 发表于 2009-11-22 22:51:32

回:6楼。
你的液晶你测试过能正常显示吗?
液晶还有个对比度的问题,如果对比度太高会看不见显示的,我刚开始玩液晶也是的,调试了好多次,最终还是出现在液晶那对比度上。你先好好查查你的硬件。

jingxinchong 发表于 2009-11-23 10:28:11

To:楼主
   不好意思。昨天没及时回复。
   今天早晨检查了一下硬件,发现液晶没有问题,能正常显示。并且PC6也不是复位引脚。而是默认的JTAG使能的原因导致液晶不能显示。之后把JTAG使能去掉后,能显示255.000.但是不能显示温度变化值。温度传感器绝对正常。不知道什么原因?

jingxinchong 发表于 2009-11-23 19:25:32

to:xiaoluo

今天我又实验,把PB7换成了PD7作为18b20的数据端口,还是显示255.000.数值不会变化,为什么?请帮助分析

jingxinchong 发表于 2009-11-24 11:52:14

终于调通了,^_^ 谢谢xiaoluo
主要是因为程序中的18b20的读写延时有点问题,经过修改,终于调通了。同时做四位小数运算时,最好用乘法运算。除法有点问题。
/*MCU:ATmega16
CLK:1M

PC4:RS
PC5:R/W
PC6:E
PA0--PA7:液晶1602数据双向口
PB7;ds18b20数据I/O口
*/
#include<iom16v.h>
#include<macros.h>

#define uchar unsigned char
#define uint unsigned int

#define rs_h PORTC |= BIT(4)
#define rs_l PORTC &= ~BIT(4)
#define rw_h PORTC |= BIT(5)
#define rw_l PORTC &= ~BIT(5)
#define e_hPORTC |= BIT(6)
#define e_lPORTC &= ~BIT(6)

#define DQ_H PORTB |= BIT(7)
#define DQ_L PORTB &= ~BIT(7)

uchar temp;
float temp_dec;//做小数运算一定要用浮点型
uint temp_dec_F; //小数扩大10000后数值超过255,至少用int
//温度的整数,温度的小数变量
uchar lcd={"0123456789."};
uchar b_d={0};
/****************************/
void delay_ms(uint x)
{
    uchar i;
while(x--)
{
    for(i=0;i<100;i++);
}
}

/**************************/
void delay_us(uint x)
{
    while(x--);
}

/***************************/
void port_init(void)
{
    DDRC |= BIT(4)|BIT(5)|BIT(6);
PORTC &= ~(BIT(4)|BIT(5)|BIT(6));
DDRA=0XFF;
PORTA=0X00;

DDRB |= BIT(7);
PORTB |= BIT(7);

//DDRB |= BIT(1);
//PORTB |= BIT(1);   
}

/********18b20*****************/
void reset(void)//复位函数
{ //DDRB |= BIT(7);   //有没有此句都行,port_init()已经初始化,否则此句必须有。
    DQ_H;             //数据线高
DQ_L;             //把数据线拉低
delay_us(80);   //延时480us
DQ_H;             //释放总线。
DDRB &= ~BIT(7);//改变PB0口的方向
delay_us(10);   //延时60us
while( PINB&BIT(7) ); //复位成功
delay_us(40);       //延时240us ,没有此延时,下面延时必须有。
//PORTB &= ~BIT(1); //检测一下是不是在死循环里了.
DDRB |= BIT(7);
DQ_H;            //再次释放总线
//delay_us(40);   //此句与上面延时二者必有其一,或都有。
}

/***************************/
void write_byte(uchar x)
{
    uchar i,j;
for(i=0;i<8;i++)
{      
DQ_L;   //拉低数据线,空闲时是为高电平
for(j=0;j<1;j++);//延时9us,没有此句也行
//delay_us(1); //15us不行, 此处延时必须小于15us
if( ( x&0x01 )==0x01 )
DQ_H;
else
DQ_L;
delay_us(5); //45us
DQ_H;//释放总线,以便于下次进for语句时,DQ_H;是为高电平.
x>>=1;   
}
DQ_H;   //没有此句也行,因为每次循环末尾都有
}

/********************************/
uchar read_byte(void)   //ds18b20读一字节
{
   uchar i,j,z;
   z=0;
   for(i=0;i<8;i++)
   {
       z>>=1;         
   DQ_L;         //拉低数据线,空闲时是为高电平
   DQ_H;         //释放总线
   for(j=0;j<1;j++);//延时9us,没有此句也行
   //delay_us(1);    //延时15us 不行, 此处延时必须小于15us
   DDRB &= ~BIT(7);
   if( ( PINB&BIT(7) )==0X80 )
   z|=0x80;
   else
   z&=0x7f;
   delay_us(10);    //延时60us
   DDRB |= BIT(7);
   DQ_H;            //再次释放总线
   }   
   return(z);   
}

/*******************************/
void get_time(void)
{
    uchar temp_l,temp_h,i;

reset();         //复位.
write_byte(0xcc);//跳过ROM.
write_byte(0x44);//温度转换.
delay_ms(1000);    //延时2秒.
reset();         //复位.
write_byte(0xcc);//跳过ROM.
write_byte(0xbe);//读暂存存储器.
temp_l=read_byte();    //温度存储器中的温度低8位.
temp_h=read_byte();    //温度存储器中的高8位.
reset();             //再来个复位,终止读暂存存储器.

i=temp_l;
i &= 0x0f;      //温度的低四位.
temp_dec=i*0.0625;      //温度的小数.此处用乘法,如果用除16,则会显示乱码
temp_dec_F=temp_dec*10000; //放大10000倍,当除以1000时才能得到第一位小数。

temp_l>>=4;//把小数点移掉,得到温度整数的个位,既低四位。            
temp_h<<=4;   //把前面4个符号位移掉,只保留一位符号位和温度的高三位。
temp = temp_l|temp_h; //温度的整数.
}

/*******************************/
void b_d_data(void)   //数据处理
{
    b_d=temp/100;   //温度的百位.
b_d=temp%100/10;//温度的十位.
b_d=temp%100%10;//温度的个位.

b_d=temp_dec_F/1000;//小数的第一位
b_d=temp_dec_F%1000/100;//小数的第二位
b_d=temp_dec_F%1000%100/10;//小数的第三位
b_d=temp_dec_F%1000%100%10;//小数的第四位
}

/********1602液晶***************/
void busy(void)   //1602判断忙函数
{
    DDRA=0X00;
rs_l;
rw_h;
e_h;
while( ( PINA&0x80 )==0x80 );
e_l;
DDRA=0xff;
}
/******************************/
void write_com(uchar com)//1602液晶写命令
{
    busy();
    PORTA=0x00;
    rs_l;
rw_l;
PORTA=com;
e_h;
e_l;
PORTA=0x00;   
}

/*****************************/
void write_data(uchar data)//1602写数据
{
    busy();
    PORTA=0x00;
    rs_h;
rw_l;
e_h;
PORTA=data;
e_l;
PORTA=0x00;
}

/*****************************/
void init_1602(void)
{
    write_com(0x38);    //1602显示模式设置
write_com(0x01);    //清显示
delay_ms(2);
write_com(0x0c);    //开显示,不显示光标,光标不闪烁。
write_com(0x06);    //当写一字符时,地址加一,而且光标加一.
}

void disp_lcd(void)//显示刷新
{
    write_com(0x80+0x04);//显示地址
write_data(lcd]);
write_data(lcd]);
write_data(lcd]);
write_data(lcd);
write_data(lcd]);
write_data(lcd]);
write_data(lcd]);
write_data(lcd]);
}

/**************************/
void main(void)
{
    port_init();
init_1602();
while(1)
{
    get_time();
    b_d_data();
disp_lcd();
}
}

xiaoluo2009 发表于 2009-11-24 13:28:00

我用的除法好像就没有乱码,我写的是放大10倍,只显示一位小数。

meisen999 发表于 2010-3-5 00:12:56

厉害

luojiyin 发表于 2010-3-6 21:32:41

mark

xunke 发表于 2010-3-7 16:18:15

请教!
请问你的测量结果准吗?
我的结果比实际高出约2度,不知为何?

s1031129012 发表于 2010-3-19 11:02:43

楼主,你的程序里没有处理温度零下的问题,如果温度是零下的话,你的液晶屏估计会显示实际温度的补码.....

ele2011 发表于 2011-12-31 15:25:20

mark

jz701209李 发表于 2013-4-10 20:14:42

学习一下。。。。
页: [1]
查看完整版本: 终于调通DS18B20,ATmega8+DS18B20,希望对像我这样的新手有点用。