5171|13

75

帖子

0

TA的资源

一粒金砂(初级)

楼主
 

(急!!!)ds18b20时序问题 [复制链接]

帮我调下ARM+DS18b20时序,我调了半天调不出来。晶振11.0592。控制器LPC2131,串口和中断不用看了。谢谢!
程序:
#include "..\\basescr\\config.h"
#include "stdio.h"

#define DS1820_DQ 1<<7 //DQ脚接P0.2
#define        UART_BPS        115200                                // 串口通讯波特率
int8 time0_flag=0;
//延时函数
void delay(uint8 i)
{
int j,k;
for(j=i;j>0;j--)
  for(k=500;k>0;k--);
}
void DelayNl (uint32 dly)
{

        uint32 i;
       
        for ( ; dly>0; dly--)
                for (i=0; i<5000; i++);
}
/*
*********************************************************************************************************
** 函数名称 :UART0_Init()
** 函数功能 :串口初始化,设置为8位数据位,1位停止位,无奇偶校验,波特率115200。
** 入口参数 :无
** 出口参数 :无
*********************************************************************************************************
*/
void UART0_Init (void)
{
        uint16 Fdiv;
       
        U0LCR = 0x83;                                                // DLAB=1,允许设置波特率
        Fdiv  = (Fpclk / 16) / UART_BPS;        // 设置波特率
        U0DLM = Fdiv / 256;
        U0DLL = Fdiv % 256;
        U0LCR = 0x03;
}
/*
*******************************************************************************************************
** 函数名称 :UART0_SendByte()
** 函数功能 :向串口发送字节数据,并等待数据发送完毕。
** 入口参数 :data                要发送的数据
** 出口参数 :无
*******************************************************************************************************
*/
void UART0_SendByte (uint8 data)
{
        U0THR = data;        
        while ((U0LSR & 0x40) == 0);        // 等待数据发送完毕
}

/*中断服务程序*/
void __irq IRQ_Timer0 (void)
{
        time0_flag=1;
        T0IR = 0x01;                                /* 清除中断标志                                                                        */
        VICVectAddr = 0x00;                        /* 通知VIC中断处理结束                                                        */
}
/*定时0.5秒钟*/
void Timer0_Init(void)
{
    IRQEnable();                                        /* IRQ中断使能                                                                */        
        /* 定时器0初始化 */
        T0TC   = 0;                        /* 定时器设置为0                                                                                */
        T0PR   = 0;                        /* 时钟不分频                                                                                        */
        T0MCR  = 0x03;                /* 设置T0MR0匹配后复位T0TC,并产生中断标志                                */
        T0MR0  = Fpclk;            /*0.5秒钟定时                                                                                        */
        T0TCR  = 0x01;                /*启动定时器                                                                                         */
       
        /* 设置定时器0中断IRQ */
        VICIntSelect = 0x00;                                /* 所有中断通道设置为IRQ中断*/                       
        VICVectCntl0 = 0x20 | 0x04;                        /* 设置定时器0中断通道分配最高优先级*/
        VICVectAddr0 = (uint32)IRQ_Timer0;        /* 设置中断服务程序地址                                         */
        VICIntEnable = 1 << 0x04;                        /* 使能定时器0中断                                                */       
}

//初始化函数
void Init_DS18B20(void)
{
        uint8 x;
        IO0DIR=DS1820_DQ;  //做数据数出口       
    IO0SET = DS1820_DQ;    //DQ复位
    delay(1);  //稍做延时
    IO0CLR = DS1820_DQ;    //将DQ拉低
    delay(80); //精确延时 大于 480us
    IO0SET = DS1820_DQ;    //拉高总线
        delay(6);   
        IO0DIR=IO0DIR & ~(DS1820_DQ);        //做数据数入口
        delay(6);
        if(IO0PIN & DS1820_DQ==0x0080)      //稍做延时后 如果x=0则初始化成功 x=1则初始化失败
           x=1;
        else
           x=0;
    delay(6);
}
//读一个字节
uint8  ReadOneChar(void)  
{
        uint8 i=0;
        uint8 dat = 0;
        for (i=8;i>0;i--)
         {       
                IO0DIR=DS1820_DQ;  //做数据数出口
                IO0CLR = DS1820_DQ; // 给脉冲信号
                  dat>>=1;
                  IO0SET = DS1820_DQ; // 给脉冲信号                 
                IO0DIR=IO0DIR & ~(DS1820_DQ);        //做数据数入口
                delay(1);
                  if((IO0SET & DS1820_DQ)==0x80)
                  {
                         dat|=0x80;
                }
                  delay(4);
        }
        return(dat);
}
//写一个字节
void WriteOneChar(uint8 dat)  
{
         uint8 i=0;
         uint8 temp;
         IO0DIR = DS1820_DQ;  //DQ为输出口
           for (i=8; i>0; i--)
         {
                  IO0CLR = DS1820_DQ;
                  temp=dat&0x01;
                if(temp==1) //按位写入
                  IO0SET=DS1820_DQ;
                else if(temp==0)
                  IO0CLR=DS1820_DQ;
                  delay(5);
                  IO0SET = DS1820_DQ;
                  dat>>=1;
         }
}
//读取温度值
float ReadTemperature()
{
        uint8 a=0;
        uint8 b=0;
        uint16 t=0;
        float tt=0;
        Init_DS18B20();    //调用初始化函数
        WriteOneChar(0xCC); // 跳过读序号列号的操作
        WriteOneChar(0x44); // 启动温度转换
        Init_DS18B20();    //调用初始化函数
        WriteOneChar(0xCC); //跳过读序号列号的操作
        WriteOneChar(0xBE); //读取温度寄存器等(共可读9个寄存器) 前两个就是温度
        a=ReadOneChar(); //读取温度的低8位
        b=ReadOneChar(); //读取温度的高8位
        t=b;
        t<<=8;
        t=t|a; //得到温度值
        tt=t*0.0625;
        return(tt);
}

//主函数
int main()
{
  float temp=0;
  char   str[30];
  int i,n;
  /*管脚设置*/
  PINSEL0 = 0x00000005;                                // 设置I/O连接到UART0
  PINSEL1= 0x00000000;
  IO0DIR = DS1820_DQ;  //初始化为数出口
  Timer0_Init();
  UART0_Init();
  DelayNl(10);
       

  while(1)
  {
    if(time0_flag==1)
    {
           time0_flag=0;       
             temp=ReadTemperature();//读取温度值
           n=sprintf(str, "Now Temp is:%.3f ℃\n", temp);
       i=0;
              while(i            {
              temp=str;                        
              UART0_SendByte(temp);
              i++;
           }
        }
       
  }
//  return 1;
}

最新回复

我也遇到类似的问题了。我的是rabbit2000,时序怎么都不对,有高手以前玩过rabbit2000上用18B20吗。  详情 回复 发表于 2009-7-16 22:39
点赞 关注

回复
举报

71

帖子

0

TA的资源

一粒金砂(初级)

沙发
 
DS18B20用过,不过是在51下用汇编写的操作函数,用C很难满足其时序要求。不过那可能是因为51单片机的时钟周期和指令周期的问题。

这个。。。太多了,看起来头晕。你自己的努力情况怎么样呢?示波器抓波形看的时候,全错吗?发一次,能得到18B20的返回波形吗?
 
 

回复

81

帖子

0

TA的资源

一粒金砂(初级)

板凳
 
你的delay 有没有用定时器测过延时时间? 以前我用C给51写的程序也没问题的
 
 
 

回复

78

帖子

0

TA的资源

一粒金砂(初级)

4
 
1-wire,单总线对时序的要求很高,高电平持续时间、低电平持续时间、延时时间不能简单估计,必须很精确。所以最好先用示波器看一下每段波形是否完整,是否符合要求。
 
 
 

回复

95

帖子

0

TA的资源

一粒金砂(初级)

5
 
原来用51做过,现在转ARM就调不出来了,中间串口和定时中断部分不用看的。
怎么用定时器来测延时时间啊?
 
 
 

回复

68

帖子

0

TA的资源

一粒金砂(初级)

6
 
原来用51做过,现在转ARM就调不出来了,中间串口和定时中断部分不用看的。
怎么用定时器来测延时时间啊?
 
 
 

回复

77

帖子

0

TA的资源

一粒金砂(初级)

7
 
现在才反应过来,你是不是直接将51下的delay给移植过来了?同样是11.0529MHz的晶振,对于ARM和51,这个delay产生的真正延时也是相差极大的。你要么就看编译之后的delay对应的汇编指令,要么就用示波器确认delay实际的延时,要么就改用定时器进行真正的延时,总之,这个delay的内在本质,你需要重新认识。这个也是你这次碰到问题的关键。
 
 
 

回复

77

帖子

0

TA的资源

一粒金砂(初级)

8
 
干脆直接嵌入汇编语句来延时可不可以啊?
 
 
 

回复

75

帖子

0

TA的资源

一粒金砂(初级)

9
 
找到一篇延时文章,帮忙看下讲的准不准确。
Keil C51程序设计中几种精确延时方法
来源:嵌入式技术网 作者:河南师范大学 段向东 毋茂盛  时间:2007-12-19 发布人:林逸

  摘要 实际的单片机应用系统开发过程中,由于程序功能的需要,经常编写各种延时程序,延时时间从数微秒到数秒不等,对于许多C51开发者特别是初学者编制非常精确的延时程序有一定难度。本文从实际应用出发,讨论几种实用的编制精确延时程序和计算程序执行时间的方法,并给出各种方法使用的详细步骤,以便读者能够很好地掌握理解。

引言

  单片机因具有体积小、功能强、成本低以及便于实现分布式控制而有非常广泛的应用领域[1]。单片机开发者在编制各种应用程序时经常会遇到实现精确延时的问题,比如按键去抖、数据传输等操作都要在程序中插入一段或几段延时,时间从几十微秒到几秒。有时还要求有很高的精度,如使用单总线芯片DS18B20时,允许误差范围在十几微秒以内[2],否则,芯片无法工作。用51汇编语言写程序时,这种问题很容易得到解决,而目前开发嵌入式系统软件的主流工具为C语言,用C51写延时程序时需要一些技巧[3]。因此,在多年单片机开发经验的基础上,介绍几种实用的编制精确延时程序和计算程序执行时间的方法。

  实现延时通常有两种方法:一种是硬件延时,要用到定时器/计数器,这种方法可以提高CPU的工作效率,也能做到精确延时;另一种是软件延时,这种方法主要采用循环体进行。

1 使用定时器/计数器实现精确延时

  单片机系统一般常选用11.059 2 MHz、12 MHz或6 MHz晶振。第一种更容易产生各种标准的波特率,后两种的一个机器周期分别为1 μs和2 μs,便于精确延时。本程序中假设使用频率为12 MHz的晶振。最长的延时时间可达216=65 536 μs。若定时器工作在方式2,则可实现极短时间的精确延时;如使用其他定时方式,则要考虑重装定时初值的时间(重装定时器初值占用2个机器周期)。

  在实际应用中,定时常采用中断方式,如进行适当的循环可实现几秒甚至更长时间的延时。使用定时器/计数器延时从程序的执行效率和稳定性两方面考虑都是最佳的方案。但应该注意,C51编写的中断服务程序编译后会自动加上PUSH ACC、PUSH PSW、POP PSW和POP ACC语句,执行时占用了4个机器周期;如程序中还有计数值加1语句,则又会占用1个机器周期。这些语句所消耗的时间在计算定时初值时要考虑进去,从初值中减去以达到最小误差的目的。

2 软件延时与时间计算

  在很多情况下,定时器/计数器经常被用作其他用途,这时候就只能用软件方法延时。下面介绍几种软件延时的方法。

2.1 短暂延时

  可以在C文件中通过使用带_NOP_( )语句的函数实现,定义一系列不同的延时函数,如Delay10us( )、Delay25us( )、Delay40us( )等存放在一个自定义的C文件中,需要时在主程序中直接调用。如延时10 μs的延时函数可编写如下:

                    

  Delay10us( )函数中共用了6个_NOP_( )语句,每个语句执行时间为1 μs。主函数调用Delay10us( )时,先执行一个LCALL指令(2 μs),然后执行6个_NOP_( )语句(6 μs),最后执行了一个RET指令(2 μs),所以执行上述函数时共需要10 μs。  可以把这一函数当作基本延时函数,在其他函数中调用,即嵌套调用\[4\],以实现较长时间的延时;但需要注意,如在Delay40us( )中直接调用4次Delay10us( )函数,得到的延时时间将是42 μs,而不是40 μs。这是因为执行Delay40us( )时,先执行了一次LCALL指令(2 μs),然后开始执行第一个Delay10us( ),执行完最后一个Delay10us( )时,直接返回到主程序。依此类推,如果是两层嵌套调用,如在Delay80us( )中两次调用Delay40us( ),则也要先执行一次LCALL指令(2 μs),然后执行两次Delay40us( )函数(84 μs),所以,实际延时时间为86 μs。简言之,只有最内层的函数执行RET指令。该指令直接返回到上级函数或主函数。如在Delay80μs( )中直接调用8次Delay10us( ),此时的延时时间为82 μs。通过修改基本延时函数和适当的组合调用,上述方法可以实现不同时间的延时。

2.2 在C51中嵌套汇编程序段实现延时

  在C51中通过预处理指令#pragma asm和#pragma endasm可以嵌套汇编语言语句。用户编写的汇编语言紧跟在#pragma asm之后,在#pragma endasm之前结束。




  如:#pragma asm
    …
    汇编语言程序段
    …
    #pragma endasm

  延时函数可设置入口参数,可将参数定义为unsigned char、int或long型。根据参数与返回值的传递规则,这时参数和函数返回值位于R7、R7R6、R7R6R5中。在应用时应注意以下几点:

  ◆ #pragma asm、#pragma endasm不允许嵌套使用;
  ◆ 在程序的开头应加上预处理指令#pragma asm,在该指令之前只能有注释或其他预处理指令;
  ◆ 当使用asm语句时,编译系统并不输出目标模块,而只输出汇编源文件;
  ◆ asm只能用小写字母,如果把asm写成大写,编译系统就把它作为普通变量;
  ◆ #pragma asm、#pragma endasm和 asm只能在函数内使用。

  将汇编语言与C51结合起来,充分发挥各自的优势,无疑是单片机开发人员的最佳选择。

2.3 使用示波器确定延时时间

  熟悉硬件的开发人员,也可以利用示波器来测定延时程序执行时间。方法如下:编写一个实现延时的函数,在该函数的开始置某个I/O口线如P1.0为高电平,在函数的最后清P1.0为低电平。在主程序中循环调用该延时函数,通过示波器测量P1.0引脚上的高电平时间即可确定延时函数的执行时间。方法如下:

               


  把P1.0接入示波器,运行上面的程序,可以看到P1.0输出的波形为周期是3 ms的方波。其中,高电平为2 ms,低电平为1 ms,即for循环结构“for(j=0;j<124;j++) {;}”的执行时间为1 ms。通过改变循环次数,可得到不同时间的延时。当然,也可以不用for循环而用别的语句实现延时。这里讨论的只是确定延时的方法。

2.4 使用反汇编工具计算延时时间

  对于不熟悉示波器的开发人员可用Keil C51中的反汇编工具计算延时时间,在反汇编窗口中可用源程序和汇编程序的混合代码或汇编代码显示目标应用程序。为了说明这种方法,还使用“for (i=0;i                   


  可以看出,0x000F~0x0017一共8条语句,分析语句可以发现并不是每条语句都执行DlyT次。核心循环只有0x0011~0x0017共6条语句,总共8个机器周期,第1次循环先执行“CLR A”和“MOV R6,A”两条语句,需要2个机器周期,每循环1次需要8个机器周期,但最后1次循环需要5个机器周期。DlyT次核心循环语句消耗(2+DlyT×8+5)个机器周期,当系统采用12 MHz时,精度为7 μs。

  当采用while (DlyT--)循环体时,DlyT的值存放在R7中。相对应的汇编代码如下:
                  


  循环语句执行的时间为(DlyT+1)×5个机器周期,即这种循环结构的延时精度为5 μs。

  通过实验发现,如将while (DlyT--)改为while (--DlyT),经过反汇编后得到如下代码:

  C:0x0014DFFE DJNZR7,C:0014//2T

  可以看出,这时代码只有1句,共占用2个机器周期,精度达到2 μs,循环体耗时DlyT×2个机器周期;但这时应该注意,DlyT初始值不能为0。

  这3种循环结构的延时与循环次数的关系如表1所列。

  表1 循环次数与延时时间关系单位:μs
           



  注意:计算时间时还应加上函数调用和函数返回各2个机器周期时间。

2.5 使用性能分析器计算延时时间

  很多C程序员可能对汇编语言不太熟悉,特别是每个指令执行的时间是很难记忆的,因此,再给出一种使用Keil C51的性能分析器计算延时时间的方法。这里还以前面介绍的for (i=0;i<124;i++)结构为例。使用这种方法时,必须先设置系统所用的晶振频率,选择Options for target中的target选项,在Xtal(MHz)中填入所用晶振的频率。将程序编译后,分别在_point = 1和T_point = 0处设置两个运行断点。选择start/stop debug session按钮进入程序调试窗口,分别打开Performance Analyzer window和Disassembly window。运行程序前,要首先将程序复位,计时器清零;然后按F5键运行程序,从程序效率评估窗口的下部分可以看到程序到了第一个断点,也就是所要算的程序段的开始处,用了389 μs;再按F5键,程序到了第2个断点处也就是所要算的程序段的结束处,此时时间为1 386 μs。最后用结束处的时间减去开始处时间,就得到循环程序段所占用的时间为997 μs。

  当然也可以不用打开Performance Analyzer window,这时观察左边工具栏秒(SEC)项。全速运行时,时间不变,只有当程序运行到断点处,才显示运行所用的时间。

3 总结

  本文介绍了多种实现并计算延时程序执行时间的方法。使用定时器进行延时是最佳的选择,可以提高MCU工作效率,在无法使用定时器而又需要实现比较精确的延时时,后面介绍的几种方法可以实现不等时间的延时: 使用自定义头文件的优点是,可实现任意时间长短的延时,并减少主程序的代码长度,便于对程序的阅读理解和维护。编写延时程序是一项很麻烦的任务,可能需要多次修改才能满足要求。掌握延时程序的编写,能够使程序准确得以执行,这对项目开发有着重要的意义。本文所讨论的几种方法,都是来源于实际项目的开发经验,有着很好的实用性和适应性。




 
 
 

回复

64

帖子

0

TA的资源

一粒金砂(初级)

10
 
再多问一个问题,在KEIL C51中执行一条汇编语句和在DOS下执行一条汇编语句的时间是不是一样的?谢谢!
 
 
 

回复

64

帖子

0

TA的资源

一粒金砂(初级)

11
 
肯定不一样,不同的平台的汇编指令都不一样,即使执行相同的功能,消耗的时钟周期也不同。
既然你已经牵涉到这个层面了,那就赶紧去看看51单片机的汇编知识。在工作中学的东西,印象会特别深的。
 
 
 

回复

77

帖子

0

TA的资源

一粒金砂(初级)

12
 
谢谢各位!先结贴,有问题再请教。
 
 
 

回复

71

帖子

0

TA的资源

一粒金砂(初级)

13
 
看来51延时我还没有搞明白,又学了点
 
 
 

回复

71

帖子

0

TA的资源

一粒金砂(初级)

14
 
我也遇到类似的问题了。我的是rabbit2000,时序怎么都不对,有高手以前玩过rabbit2000上用18B20吗。
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

开源项目 更多>>
    随便看看
    查找数据手册?

    EEWorld Datasheet 技术支持

    相关文章 更多>>
    关闭
    站长推荐上一条 1/8 下一条

     
    EEWorld订阅号

     
    EEWorld服务号

     
    汽车开发圈

    About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

    站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

    北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

    电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
    快速回复 返回顶部 返回列表