lonelynw 发表于 2009-3-27 14:31:51

帮忙调试一下 M128外挂16MHz晶振的DS18B20程序……郁闷两个晚上没调出

//软件平台:AVR Studio 4
//硬件:Atmega128 +DS18B20   +LCD1602
//系统时钟:外部16MHz晶体振荡器
//使用资源:
//               PA--------LCD1602    //DB0-DB7
//               PG0-------RS
//               PG1-------RW
//               PG2-------EN

//               PF0---------DS18B20

//程序
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
//#include <util/delay.h>
#include "1602.h"

/*------宏定义------*/
#define uchar        unsigned char
#define uint        unsigned int
#define BIT(x)        (1<<(x))
#define NOP()        asm("nop")
#define WDR()         asm("wdr")

#define dat      1               //PF0


//Atmega128   PF0(PIN61)(ADC0)   
#define    CLR_18B20    PORTF&=~BIT(dat)    //数据线强制拉低
#define    SET_18B20    PORTF|=BIT(dat)   //数据线强制拉高
#define    OUT_18B20    DDRF|=BIT(dat)      //主机控制,信号线为输出"1"
#define    IN_18B20   DDRF&=~BIT(dat)   //释放总线,信号线为输入"0"
#define    STU_18B20    PINF&BIT(dat)       //查询总线状态


uchar temp_num[]={"Temperature:"};
//uchar temp_data={0x00,0x00};            //读出温度暂放
float temperature;       //显示温度值


//定时器T0初始化
//T0 的64分频最大定时周期1024us   单步间隔4us
void timer0_init(void)
{
        TCCR0= 0x00;//停止定时器
//        TCNT0= 0x00;//初始值
//        OCR0   = 0xFF;//匹配值
//        TIMSK |= 0x00;//中断允许
//        TCCR0= 0x03;//启动定时器
}


//端口初始化
void port_init(void)
{
        DDRA=0xff;
        PORTA=0x00;
        DDRG=0xff;
        PORTG=0xff;
        DDRF=0xff;
        PORTF=0x00;
}

void init_devices(void)
{
        cli(); //禁止所有中断
        MCUCR= 0x00;   //MCU控制寄存器
        MCUCSR = 0x80;//禁止JTAG//MCU控制和状态寄存器
//        GICR   = 0x00;
        port_init();
        timer0_init();
//        sei();//开全局中断
}


//使用定时器精确定时
void delay_time(uchar time)
{
   TCNT0= time;//初始值
       TCCR0= 0x03;//启动定时器
       while((TIFR&(1<<TOV0))==0);//查询标志位
//   while ((TIFR&0x01));
   TIFR|=(1<<TOV0);   //软件清零标志位
//   TIFR |= 0x01;
   TCCR0= 0x00;//停止定时器
}


//Initialization the DS18B20
void init_18B20(void)
{
      OUT_18B20;       //设置为输出
          SET_18B20;       //上拉电阻使能
          CLR_18B20;       //强制拉低,并等待480~960us;

          delay_time(0x6A);

          SET_18B20;       //释放总线
          IN_18B20;      //等待18B20响应

          delay_time(0xF1);//等待15-60us,60us内18B20会将总线拉低

          while(~STU_18B20);      //总线被18B20拉低表示存在

          OUT_18B20;
          SET_18B20;
}


//Write one byte to DS18B20
void write_byte_18B20(uchar val)
{
       uchar j;
           uchar y;
           for(j=0;j<8;j++)
           {
                y=val>>j;      //先写低位
                        y&=0x01;       //查询要写入的位是“0”或者“1”
                        if(y)          //如果要写入的是“1”
                  {
                        NOP();
                                  NOP();
                                  NOP();
                                  SET_18B20;                //写入“1”                  
                  }
                  elseCLR_18B20;                //强制拉低,产生写间隙,写“0”时保持即可
             //输出一位之后,维持时间大于60us,小于120us
                       delay_time(0xF1);
                       SET_18B20;                        //DATA=1,上拉
                       delay_time(0xFD);         
          }
                SET_18B20;   //8位写完成后置位总线
}



//Read one byte from DS18B20
uchar read_byte_18B20(void)
{
      uchar x;
          uchar retval=0;
          for(x=0;x<8;x++)
          {
                retval>>=1;//先读取低位字节,右移保存
            OUT_18B20;   //主机控制总线
                        CLR_18B20;   //强制拉低
                        //之后在到读取出数据为止,15us内必须读出数据
                        SET_18B20;   //释放总线,生成读间隙
                        IN_18B20;    //总线设置为输入
                        if(STU_18B20)        retval|=0x80;    //读出为“1”信号
            delay_time(0xF1);
          }
//          SET_18B20;      //读完8位后置位总线
          return(retval); //返回读到的值
}




//Read temperature from DS18B20
void read_temp_18B20(void)
{          
      uint k=0;
          uchar temp_L=0;       //温度低八位
      uchar temp_H=0;       //温度高八位       
          init_18B20();
          write_byte_18B20(0xcc);      //跳过ROM
          write_byte_18B20(0x44);      //启动温度转换
      
          for(;k<750;k++) {delay_time(0x00);}   //??????此处延时是否需要?
            //数据手册提示说要750ms左右,蒙了……温度转换效率那么低还要来干球!!
       
          init_18B20();                  //重新复位
          write_byte_18B20(0xcc);                //跳过ROM
          write_byte_18B20(0xbe);                //发读数据指令

          temp_L=read_byte_18B20();      //温度数据低8位
          temp_H=read_byte_18B20();      //温度数据高8位

          if(temp_H<0xf8)   temperature=((temp_H*256)+temp_L)*0.0625;   //大于0的情况       
          else
          {
              temperature = (((~temp_H)*256)+(~temp_L)+1)*0.0625;    //小于0的情况
                  temperature = -temperature;
           }
}


void display_temp(void)
{
        uint temp_B,temp_S,temp_G,temp_F1;

        if(temperature>=0)
        {
              temp_B= ((int)temperature)/100;
              temp_S= ((int)temperature)%100/10;
              temp_G= ((int)temperature)%100%10;

                  temp_F1 = ((int)(temperature*100))%100/10;
//                  temp_F2 = ((int)(temperature*100))%10;

                  WriteNum(2,1,temp_B+0x30);
                  WriteNum(2,2,temp_S+0x30);
                  WriteNum(2,3,temp_G+0x30);

                  WriteChar(2,4,1,".");      //标志行

                  WriteNum(2,5,temp_F1+0x30);
//              WriteNum(2,6,temp_F2+0x30);
        }
        else
        {
              temp_B= ((int)(-temperature))/100;
              temp_S= ((int)(-temperature))%100/10;
              temp_G= ((int)(-temperature))%100%10;

                  temp_F1 = ((int)((-temperature)*100))%100/10;
//                  temp_F2 = ((int)((-temperature)*100))%10;


              WriteChar(2,1,1,"-");      //标志行

                  WriteNum(2,2,temp_B+0x30);
                  WriteNum(2,3,temp_S+0x30);
                  WriteNum(2,4,temp_G+0x30);

                  WriteChar(2,5,1,".");      //标志行

                  WriteNum(2,6,temp_F1+0x30);
//              WriteNum(2,7,temp_F2+0x30);
        }

}


int main(void)
{   
        init_devices();
        LcdInit();         //初始化
        WriteChar(1,0,12,temp_num);      //标志行

//        init_18B20();

        while(1)
        {
               read_temp_18B20();
               display_temp();
//               for(z=0;z<18;z++)   {s_ms(60000);}
        }

}





/*-----------------------------------------------------------------------------------------
//包含LCD1602的头文件如下
-----------------------------------------------------------------------------------------*/
#define uchar unsigned char
#define uint unsigned int

#define RS 0
#define RW 1
#define EN 2

void s_ms(uint ms)
{
    uint i=0;
        for(i=0;i<1000;i++){for(;ms>1;ms--);}
}       


//查忙
void busy(void)
{
    uchar temp;
        s_ms(500);
        PORTG&=~(1<<RS);    //RS=0
        s_ms(500);
        PORTG|=(1<<RW);   //RW=1
        s_ms(500);
        while(temp)
        {
                PORTG|=(1<<EN); //EN=1
                s_ms(500);
                DDRA=0x00;      //A口变输入
                PORTA=0xff;   //上拉使能
                s_ms(500);
                temp = PINA&0x80;    //读取A口
                s_ms(500);      
                DDRA=0xff;      
                PORTA=0xff;      //A口变输出
                s_ms(500);
                PORTG&=~(1<<EN);   //EN=0
                s_ms(500);
        }
}

//写指令
void writecom(uchar        com)
{
        busy();
        s_ms(500);
        PORTG&=~(1<<RS);   //RS=0
        s_ms(500);
        PORTG&=~(1<<RW);   //RW=0
        s_ms(500);
        PORTG|=(1<<EN);    //EN=1
        s_ms(500);
        PORTA = com;       //输出指令
        s_ms(500);
        PORTG&=~(1<<EN);   //EN=0
        s_ms(500);
}

//1602初始化
void        LcdInit(void)
{
        writecom(0x38);
        s_ms(1000);
        writecom(0x01);
        s_ms(10000);
        s_ms(1000);
        s_ms(1000);
        s_ms(1000);
        s_ms(1000);
        s_ms(1000);
        s_ms(1000);
        writecom(0x02);
        s_ms(1000);
        writecom(0x06);
        s_ms(1000);
        writecom(0x0c);
        s_ms(1000);
        writecom(0x38);       
        s_ms(1000);
}       

//写数据
void        writedata(uchar data)
{
        busy();
        s_ms(500);
        PORTG|=(1<<RS);   //RS=1
        s_ms(500);
        PORTG&=~(1<<RW);   //RW=0
        s_ms(500);
        PORTG|=(1<<EN);    //EN=1
        s_ms(500);
        PORTA = data;      //输出数据
        s_ms(500);
        PORTG&=~(1<<EN);   //EN=0
        s_ms(500);
}


//读数据
uchar        readdata(void)
{
        uchar temp;
        busy();
        s_ms(500);
        PORTG|=(1<<RS);//RS=1
        s_ms(500);
        PORTG|=(1<<RW);//RW=1
        s_ms(500);
        PORTG|=(1<<EN);//EN=1
        s_ms(500);
        DDRA=0x00;       //A端口变输入
        s_ms(500);
        temp = PINA;   //读A端口
        s_ms(500);
        DDRA=0xff;       //A端口变输出
        s_ms(500);
        PORTG&=~(1<<EN); //EN=0
        s_ms(500);
        return temp;       
}

//=================================================
// 描述: 写LCD内部CGRAM函数
// 入口: ‘num’要写的数据个数
//      ‘pbuffer’要写的数据的首地址
// 出口: 无
//================================================
void        WriteCGRAM(uint        num, const uint        *pBuffer)
{
        uint        i,t;
        writecom(0x40);
        PORTG|=(1<<RS);
        PORTG&=~(1<<RW);
        for(i=num;i!=0;i--)
        {
                t = *pBuffer;
                PORTG|=(1<<EN);
                PORTA = t;
                PORTG&=~(1<<EN);                               
                pBuffer++;
        }
       
}

//=================================================
//描述:写菜单函数,本程序使用的LCD规格为 16 * 2
//入口:菜单数组首地址
//出口:无
//=================================================
void        WriteMenu(const uchar *pBuffer)
{
        uchar        i,t;
        writecom(0x80);   //数据地址
       
        PORTG|=(1<<RS);
        PORTG&=~(1<<RW);
        s_ms(50);
        for(i=0;i<16;i++)
        {
                t = *pBuffer;
                PORTA = t;
                PORTG|=(1<<EN);
                s_ms(50);
                PORTG&=~(1<<EN);                               
                pBuffer++;
        }
        writecom(0xC0);

        PORTG|=(1<<RS);
        PORTG&=~(1<<RW);
        s_ms(50);       
        for(i=0;i<16;i++)
        {
                t = *pBuffer;
                PORTA = t;
                PORTG|=(1<<EN);
                s_ms(50);
                PORTG&=~(1<<EN);                               
                pBuffer++;
        }
}
//====================================================
// 描述:在任意位置写数字函数
// 入口:’row‘表示要写数字所在的行地址,只能为1或2
//       ’col‘表示要写数字所在的列地址,只能为0--15
//               ‘num’表示要写的数字,只能为0--9
// 出口:无
//===================================================
void WriteNum(uchar row,uchar col,uchar num)
{
        if (row == 1)        row = 0x80 + col;
        else        row = 0xC0 + col;
        writecom(row);

        PORTG|=(1<<RS);
        s_ms(500);
        PORTG&=~(1<<RW);
        s_ms(500);
        PORTA = num;
        s_ms(500);
        PORTG|=(1<<EN);
        s_ms(500);
        PORTG&=~(1<<EN);       
        s_ms(500);                       
}
//================================================================
// 描述:在任意位置写任意多个字符
// 入口:’row‘要写的字符所在的行,只能为1或2;
//       ‘col’要写的字符所在的列,只能为0---15
//       ‘num’要写字符的个数
//       ‘pbuffer’要写字符的首地址
//==================================================================
void        WriteChar(uchar row,uchar col,uint num,uchar *pBuffer)
{
        uchar i,t;
        if (row == 1)        row = 0x80 + col;
        else        row = 0xC0 + col;
        writecom(row);


        PORTG|=(1<<RS);
        s_ms(500);
        PORTG&=~(1<<RW);
        s_ms(500);
        for(i=num;i!=0;i--)
        {
                t = *pBuffer;
                s_ms(500);
                PORTA = t;
                s_ms(500);
                PORTG|=(1<<EN);
                s_ms(500);
                PORTG&=~(1<<EN);               
                s_ms(500);               
                pBuffer++;
        }
       
}



问题点:
1:定时器用做延时时如何初始化以及如何查询标志位?-----T0为例
   个人想法是初始化其实可以忽略,只要程序一开始关闭定时器即可,这样在以后需要延时的地方再启动它,不过是否需要设置匹配值OCR0,我还是搞不大明白;查询定时器是否溢出的标示位TOV0是否如我编写的那样查询?总感觉有点悬乎……不知道查询方式对没
2:温度转换子程序中发转换命令后是否需要等待那么长时间?太悬了……我看到很多个版本,有的说是必须要,有的说不要也照样跑得过去
3:直接如程序里的方式来判断温度正/负是否妥当?
4:目前读出一个数值:015.9-------------不知是哪里的数值,数值保持不变



也许用内部晶振的话估计能找到好多参考,而且是可以直接移植的参考……M8低速模式下跟C51单片机我都调出过N次的DS18B20
这里我只是提出一个高速晶振时对于精确延时的寻求解决办法,这里用到定时器做延时有点大材小用了!
望高手能指点一二或者有兴趣的同僚多多交流

lonelynw 发表于 2009-3-27 16:04:07

没有大虾路过吗?……伤哀

eaglelpx 发表于 2009-3-27 16:17:11

宁万大哥!不用这么拼命吧

lonelynw 发表于 2009-3-27 17:02:30

……严重的对楼上无语,我的帖都被你翻到~~~~~~~

fsclub 发表于 2009-3-27 17:12:03

几十微秒别用定时器了.没必要.中断一下,或者查询一下,时间差不多.

楼主,没谁有兴趣看代码.
不过我给你个我试过了,也是本站某兄弟写的,绝对可用,不过是8M的,需要把延时程序改下.改一下端口定义.
里面什么定时器什么的你都不用管了.


http://www.ouravr.com/bbs/bbs_content.jsp?bbs_sn=657257&bbs_page_no=1&search_mode=1&search_text=18B20&bbs_id=1000

bg4uvr 发表于 2009-3-27 19:57:20

我搞的时候情况和楼主相反,是高速时钟下没问题,低速时钟下才碰到困难。

不过某人逛这里的时候,看到个帖子,一下子就解决了。

帖上我当时的BLOG,希望对楼主有用。

http://hi.baidu.com/bg4uvr/blog/item/263145335bdbb045ad4b5f8f.html

sunliezhi 发表于 2009-3-28 09:57:11

//Write one byte to DS18B20
void write_byte_18B20(uchar val)
{
       uchar j;
         uchar y;
         for(j=0;j<8;j++)
         {
                  y=val>>j;      //先写低位
                  y&=0x01;       //查询要写入的位是“0”或者“1”
                  if(y)          //如果要写入的是“1”
                  {
                        NOP();
                        NOP();
                        NOP();
                        SET_18B20;                //写入“1”                  
                  }
                  elseCLR_18B20;                //强制拉低,产生写间隙,写“0”时保持即可
             //输出一位之后,维持时间大于60us,小于120us
                         delay_time(0xF1);
                         SET_18B20;                        //DATA=1,上拉
                         delay_time(0xFD);         
            }
                SET_18B20;   //8位写完成后置位总线
}

写“1”时也要拉低总线20us左右以通知ds18b20,否则它不知道的。

lonelynw 发表于 2009-3-28 10:35:00

楼上诸位大虾,感激不尽、感激不尽……
四楼的大哥提醒得对,确实很多人不喜欢看别人的代码,包括我自己,也不喜欢看别人的代码,很头大啊!!!
可没办法三……问题就出在延时代码上,只好附上来了
我原先写的程序没用定时器的,可这18B20比较挑,用内嵌的循环写成的延时又不好把握时长,所以……

wooyer 发表于 2009-6-4 16:53:05

请教一下:怎样把主机接收的DS18b20数据(二进制补码)转换为10进制数呢?
楼主用了这段代码:
if(temp_H<0xf8)   
temperature=((temp_H*256)+temp_L)*0.0625;   //大于0的情况         
else
{
   temperature = (((~temp_H)*256)+(~temp_L)+1)*0.0625;    //小于0的情况
   temperature = -temperature;
}

lisun0410 发表于 2013-2-1 00:55:05

lonelynw 发表于 2009-3-28 10:35 static/image/common/back.gif
楼上诸位大虾,感激不尽、感激不尽……
四楼的大哥提醒得对,确实很多人不喜欢看别人的代码,包括我自己, ...

想问问您这个高速晶振下的18B20问题解决了没... 我最近也碰到这个情况 想向您求助一下

lisun0410 发表于 2013-2-1 00:56:20

lonelynw 发表于 2009-3-28 10:35 static/image/common/back.gif
楼上诸位大虾,感激不尽、感激不尽……
四楼的大哥提醒得对,确实很多人不喜欢看别人的代码,包括我自己, ...

感觉您测出的温度不是15.9而是 I5.9是英文字母 "i" 的大写

清雨影 发表于 2013-2-1 21:07:15

我只学过C51……瞎说说啊……
应该是时钟问题,在对DS18B20进行读写的子程序里面用到的delay一般就是几个至十几个NOP,16MHz相当于51单片机一百多MHz,我在C51下晶振从12Mhz(12T单倍速)换成了20Mhz丫都罩不住……别说您这么快了
贴上一小段C51的代码:
/*

        DQ为输入数据的接口,接ds18b20的中间
        调用方法:
        uint ds18b20_get_temp();
        得到的温度如果最高位=1则温度为负温度,否则正温度

*/



#include<reg52.h>
#include"ds18b20_driver.h"
#include"delay_api.h"

#define jump_ROM   0xCC
#define start      0x44
#define read_EEROM 0xBE



sbit DQ=1^7;
uchar TMPL,TMPH;

unsigned char reset_ds18b20(void)
{
        uchar deceive_ready;
        DQ = 0;
        macdelay(29);
        DQ = 1;
        macdelay(3);
        deceive_ready = DQ;
        macdelay(25);
        return(deceive_ready);
}
uchar ds18b20_read_bit(void)
{
        uchar i;
        DQ = 0;
        DQ = 1;
        for(i=0; i<3; i++);
        return(DQ);
}

void ds18b20_write_bit(uchar bitval)
{
DQ=0;if(bitval==1)
DQ=1;
macdelay(5);
DQ=1;
}

uchar ds18b20_read_byte(void)
{
        uchar i,m,receive_data;
        m = 1;
        receive_data = 0;
        for(i=0; i<8; i++)
        {
                if(ds18b20_read_bit())
                {
                        receive_data = receive_data + (m << i);
                }
                macdelay(6);
        }
        return(receive_data);
}

void ds18b20_write_byte(uchar val)
{
        uchar i,temp;
        for(i=0; i<8; i++)
        {
                temp = val >> i;
                temp = temp & 0x01;
                ds18b20_write_bit(temp);
                macdelay(5);
        }
}
uint ds18b20_get_temp(uchar cf)
{
        uint temp;
        uchar tflag;
        reset_ds18b20();
        ds18b20_write_byte(jump_ROM);
        ds18b20_write_byte(start);
        reset_ds18b20();
        ds18b20_write_byte(jump_ROM);
        ds18b20_write_byte(read_EEROM);
        TMPL = ds18b20_read_byte();
        TMPH = ds18b20_read_byte();
        temp=TMPH;
        temp<<=8;
        temp=temp|TMPL;
        if(temp<0x0fff){
                tflag=0;
        }else{
                tflag=1;
                temp=~temp+1;
        }
       
        if(cf){
                temp=temp*0.1125+32+tflag*0x1000;
        }else{
                temp=temp*0.0625+tflag*0x1000;
        }
        return temp;
}
/*温度模块结束*/
里面使用的macdelay如下:
void macdelay(uint i){
        uint j=0;
        for(j=0;j<i;j++){
        }
}
在20Mhz下,将macdelay的延时扩充为本来的两倍就正常了
所以不妨试试。

nichengyu1989 发表于 2013-2-2 00:03:29

以后也要用到

424x3 发表于 2013-2-2 00:21:06

弄时序方面的示波器不离手    在程序里面丢脉冲到其他引脚   用同步来看时序走到了哪一步    那一步不通   就抓住了再分析

xqn2012 发表于 2013-2-3 11:19:21

lisun0410 发表于 2013-2-1 00:55 static/image/common/back.gif
想问问您这个高速晶振下的18B20问题解决了没... 我最近也碰到这个情况 想向您求助一下 ...

应该是延时大多过长了

lisun0410 发表于 2013-2-3 15:38:11

xqn2012 发表于 2013-2-3 11:19 static/image/common/back.gif
应该是延时大多过长了

额 晶振从8M变成16M 延时需要适当增加 我先翻倍 然后逐渐减少 现在已经调通了

woiled 发表于 2013-2-4 08:05:05

mark以备查,18b20的时序很麻烦

xqn2012 发表于 2013-2-4 09:34:39

lisun0410 发表于 2013-2-3 15:38 static/image/common/back.gif
额 晶振从8M变成16M 延时需要适当增加 我先翻倍 然后逐渐减少 现在已经调通了 ...

其实我是想说,延时比较麻烦,如果是avr studio话,还要优化。就是慢慢调的

lisun0410 发表于 2013-2-4 09:45:39

xqn2012 发表于 2013-2-4 09:34 static/image/common/back.gif
其实我是想说,延时比较麻烦,如果是avr studio话,还要优化。就是慢慢调的 ...

额 好吧 我用的CVAVR

chump 发表于 2013-3-17 15:13:43

我也调了两天的ATMEGA12816MHZ 下的DS18B20。。 好累啊,不知道哪里错了。求楼主指点哪里要注意的。
页: [1]
查看完整版本: 帮忙调试一下 M128外挂16MHz晶振的DS18B20程序……郁闷两个晚上没调出