找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索

误人子弟的程序分析

查看数: 8523 | 评论数: 29 | 收藏 2
关灯 | 提示:支持键盘翻页<-左 右->
    组图打开中,请稍候......
发布时间: 2014-10-6 22:36

正文摘要:

本帖最后由 codenew 于 2014-10-6 22:42 编辑        下面程序是我从别的地方搞来的,最后发现很多错误。为了避免误人子弟,特地作了一下分析,开头部分for分析是我写的,程序中红色的部分我 ...

回复

ID:675145 发表于 2020-4-1 10:44
神仙打架
ID:450849 发表于 2020-4-1 08:29
阻塞式延时都是垃圾,,哈哈
ID:149451 发表于 2017-5-10 09:52
钦佩做事认真的人,因为曾有伟人说过,“世界上怕就怕认真二字”,科学来不得半点马虎。
ID:196669 发表于 2017-5-5 10:54
完整的有吗?
ID:141210 发表于 2016-10-3 15:54
看不懂呀,有完整程序啊


ID:134697 发表于 2016-7-23 12:13
楼主人好,技术不错。
ID:101595 发表于 2016-1-10 21:04
18b20有时候跟硬件关系很大,不同硬件程序跑出来不一样,还有中断也会影响到它的测量
ID:99672 发表于 2015-12-28 10:09
有没有完整的对的程序u
ID:92810 发表于 2015-10-18 14:20
好的,谢谢分享了。。。
ID:65751 发表于 2014-10-9 09:50
本帖最后由 codenew 于 2014-10-9 09:58 编辑

//下面的time在源程序中都是全局变量!
/*****************************************************
函数功能:将DS18B20传感器初始化,读取应答信号
出口参数:flag
***************************************************/
bit Init_DS18B20(void)
{
       bitflag;         //储存DS18B20是否存在的标志,flag=0,表示存在;flag=1,表示不存在
       DQ= 1;           //先将数据线拉高
       for(time=0;time<2;time++);//略微延时约6微秒。实际是延时10us,局部变量时为8us,不是注释所说的6us。
       DQ= 0;           //再将数据线从高拉低,要求保持480~960us
       for(time=0;time<200;time++);//略微延时约600微秒,以向DS18B20发出一持续480~960us的低电平复位脉冲。实际是延时802us,time为局部变量时才为602us,即约600us。这点作者明显误撞了,幸好802us还在范围内。
       DQ= 1;           //释放数据线(将数据线拉高)
       for(time=0;time<10;time++);//延时约30us(释放总线后需等待15~60us让DS18B20输出存在脉冲)。实际是延时42us,time为局部变量时才为 32us,即约32us。明显作者又错了,把time当作局部变量了,幸好 42us还在范围内。实际最好延时60us,确保能读到存在脉冲。
       flag=DQ;          //让单片机检测是否输出了存在脉冲(DQ=0表示存在)      
       for(time=0;time<200;time++);  //延时足够长时间,等待存在脉冲输出。延时802us。
       return(flag);    //返回检测成功标志
}
/*****************************************************
函数功能:从DS18B20读取一个字节数据
出口参数:dat
***************************************************/
unsigned char ReadOneChar(void)
{
       unsignedchar i=0;  
       unsignedchar dat;            //储存读出的一个字节数据
       for(i=0;i<8;i++)
       {
              DQ=1;                    //先将数据线拉高
              _nop_();               //等待一个机器周期   
              DQ= 0;        //单片机从DS18B20读书据时,将数据线从高拉低即启动读时序
              dat>>=1;
              _nop_();        //等待一个机器周期                 
              DQ= 1;        //将数据线"人为"拉高,为单片机检测DS18B20的输出电平作准备
              for(time=0;time<2;time++);//延时约6us,使主机在15us内采样。实际是延时10us,局部变量时为8us,都不是注释的6us。
              if(DQ==1)
              dat|=0x80;                //如果读到的数据是1,则将1存入dat
              else
              dat|=0x00;/如果读到的数据是0,则将0存入dat,将单片机检测到的电平信号DQ、r
              for(time=0;time<8;time++);//延时3us,两个读时序之间必须有大于1us的恢复期。实际是延时34us,与3us差很多。
           }                        
              return(dat);    //返回读出的十进制数据
}
/*****************************************************
函数功能:向DS18B20写入一个字节数据
入口参数:dat
***************************************************/  
WriteOneChar(unsigned char dat)
{
       unsignedchar i=0;
       for(i=0; i<8; i++)
       {
              DQ=1;         // 先将数据线拉高
              _nop_();     //等待一个机器周期        
              DQ=0;          //将数据线从高拉低时即启动写时序      
              DQ=dat&0x01;   //利用与运算取出要写的某位二进制数据,并将其送到数据线上等待DS18B20采样   
              for(time=0;time<10;time++);//延时约30us,DS18B20在拉低后的约15~60us期间从数据线上采样。实际是延时42us,局部变量时才为32us。
              DQ=1;          //释放数据线            
              for(time=0;time<1;time++);//延时3us,两个写时序间至少需要1us的恢复期 。实际是延时6us,局部变量时才为5us。
              dat>>=1;       //将dat中的各二进制位数据右移1位
       }
       for(time=0;time<4;time++);//稍作延时,给硬件一点反应时间。延时18us。   
}
/*****************************************************
函数功能:做好读温度的准备
***************************************************/
void ReadyReadTemp(void)
{
       Init_DS18B20();             //将DS18B20初始化。既然Init_DS18B20();有返回值flag,不判断其值是否成功就写命令了,又笔误。
       WriteOneChar(0xCC);         // 跳过读序号列号的操作
       WriteOneChar(0x44);         // 启动温度转换        
       for(time=0;time<100;time++);//温度转换需要一点时间。延时402us。
       Init_DS18B20();     //将DS18B20初始化
       WriteOneChar(0xCC);//跳过读序号列号的操作
       WriteOneChar(0xBE);//读取温度寄存器,前两个分别是温度的低位和高位   
}
/*****************************************************
函数功能:延时若干毫秒
入口参数:n
***************************************************/
voiddelaynms(unsigned char n)
{
  unsigned char i;
       for(i=0;i<n;i++)
          delay1ms();
}
    // 照理说void delaynms(unsigned char n),参数n的取值范围是0~255,因是是无符号数。但在主函数中却调用delaynms(1000),明显错误,超出取值范围。/*****************************************************
函数功能:延时1ms
(3j+2)*i=(3×33+2)×10=1010(微秒),可以认为是1毫秒
***************************************************/
void delay1ms()
{
   unsigned char i,j;        
         for(i=0;i<4;i++)
          for(j=0;j<33;j++);               
}
//实际延时422us,代码优化为0时才接近1ms。


ID:67070 发表于 2014-10-8 10:21
没看懂的说
ID:19715 发表于 2014-10-8 00:36
本帖最后由 明白 于 2014-10-8 00:42 编辑

你篡改了别人原c语言程序的本意,
到此为止,你也应该知道源程序哪里被你改了吧
ID:19715 发表于 2014-10-8 00:30
全部的机器码

全部代码.jpg (88.27 KB, 下载次数: 189)

全部代码.jpg
ID:19715 发表于 2014-10-8 00:06
本帖最后由 明白 于 2014-10-8 00:37 编辑

delay1ms()延时函数:
void delay1ms()
{
   unsigned char i,j;        
         for(i=0;i<4;i++)
          for(j=0;j<33;j++);               
}

834-412=422
看看delay1ms(),跟我上面精度计算结果也是422,一模一样。

函数调用结束.jpg (18.06 KB, 下载次数: 196)

函数调用结束.jpg

函数调用开始.jpg (26.17 KB, 下载次数: 167)

函数调用开始.jpg

函数调用延时机器码.jpg (32.77 KB, 下载次数: 183)

函数调用延时机器码.jpg
ID:19715 发表于 2014-10-7 23:16
本帖最后由 明白 于 2014-10-8 00:25 编辑

for(time=0;time<2;time++);的话:
399-391=8个机器周期

参数2结束.png (3.08 KB, 下载次数: 206)

参数2结束.png

参数2开始.png (3.43 KB, 下载次数: 204)

参数2开始.png

原地延时机器码.jpg (16.78 KB, 下载次数: 168)

原地延时机器码.jpg
ID:19715 发表于 2014-10-7 22:19
本帖最后由 明白 于 2014-10-7 23:59 编辑

刚才我也仿真一下:

for(time=0;time<1;time++);的话:
396-391=5个机器周期

参数1结束.png (3.38 KB, 下载次数: 183)

参数1结束.png

参数1开始.png (4.2 KB, 下载次数: 216)

参数1开始.png
ID:44262 发表于 2014-10-7 22:15
我靠 太牛了 ,楼主的这个帖子对我的帮助非常大 ,佩服得五体投地 阿
ID:19715 发表于 2014-10-7 16:12
   for(time=0;time<1;time++);  //15us
   for(time=0;time<2;time++);  //23us
   for(time=0;time<3;time++);  //31us
   for(time=0;time<4;time++);  //39us
   for(time=0;time<5;time++);  //47us
   for(time=0;time<6;time++);  //55us
   for(time=0;time<7;time++);  //63us
   for(time=0;time<8;time++);  //71us
   for(time=0;time<9;time++);  //79us
   for(time=0;time<10;time++); //87us
   for(time=0;time<20;time++); //167us
   for(time=0;time<60ime++);  //487us
   for(time=0;time<70ime++);  //567us
   for(time=0;time<80ime++);  //647us
   for(time=0;time<100;time++);  //807us
   for(time=0;time<200;time++); //1607us
这些原地延时是没有可变性的(当然是排除中断的骚扰),
编译出来的机器码是有固定模式的:
START:        CLR   A       //1T
                   MOV   R7,A  //1T
LOOP:         INC   R7      //1T
                   CJNE  R7,#02H,LOOP     //2T
所以延时时间:2T+time*(1T+2T)=2T+time*3T=(2+time*3)T
如果一个机器周期为:1us
  an=am+8*(n-m)这跟:
    =2+3*time差别大不大?
   for(time=0;time<1;time++);  //15us-------5us
   for(time=0;time<2;time++);  //23us-------8us
   for(time=0;time<3;time++);  //31us-------11us
   for(time=0;time<4;time++);  //39us-------14us
   for(time=0;time<5;time++);  //47us-------17us
   for(time=0;time<6;time++);  //55us-------20us
   for(time=0;time<7;time++);  //63us-------23us
   for(time=0;time<8;time++);  //71us-------26us
   for(time=0;time<9;time++);  //79us-------29us
   for(time=0;time<10;time++); //87us-------32us
   for(time=0;time<20;time++); //167us-------62us
   for(time=0;time<60ime++);  //487us-------182us
   for(time=0;time<70ime++);  //567us-------212us
   for(time=0;time<80ime++);  //647us-------242us
   for(time=0;time<100;time++);  //807us-------302us
   for(time=0;time<200;time++); //1607us-------602us
ID:19715 发表于 2014-10-7 15:48
你延时这样计算就有错误
看看 delay1ms()
编译出来的内码,精算一下:
delay1ms:   CLR   A                  //1T
                   MOV   R7,A            //1T
D1:             CLR   A                   //1T
                   MOV   R6,A             //1T
D2:              INC   R6                 //1T
                   CJNE  R6,#21H,D2 //2T
                   INC   R7                  //1T
                   CJNE  R7,#04H,D1 //2T
                   RET                        //2T
2T + [( 2T + 33 * 3T) + 1T + 2T ] * 4 + 2T
= 2T + [ 101T + 1T + 2T ] * 4 + 2T
= 2T + [ 104 T] * 4 + 2T
= 2T + [ 104 T] * 4 + 2T
= 420T
再加上调用delay1ms()时间,需要的LCALL两个机器周期,就是:
422us
ID:19715 发表于 2014-10-7 15:40
codenew 发表于 2014-10-7 11:13
这要动脑再想想哪程序错了,为什么还能正常用,的确运气很好。例如源程序的另一个延时程序又是错的,其注释 ...

如果晶振是12Mhz。那么
void delay1ms()
{
   unsigned char i,j;        
         for(i=0;i<4;i++)
          for(j=0;j<33;j++);               
}
这个就不能认为是1ms,相差不只一倍
ID:19715 发表于 2014-10-7 15:37
codenew 发表于 2014-10-7 10:54
楼上,我敢保证我的想法是对的。因为把源程序原封不动写入芯片,测温是正常显示的,晶振是12Mhz。当然引脚 ...

DS18B20的读写操作需要一段时间,是不能短于时顺规定的时间,如果延时长了,只是浪费时间而已。
可以长,但是不能短
ID:65751 发表于 2014-10-7 11:13
这要动脑再想想哪程序错了,为什么还能正常用,的确运气很好。例如源程序的另一个延时程序又是错的,其注释思路肯定不对,但其实际效果还是接近1ms的:
/*****************************************************
函数功能:延时1ms
(3j+2)*i=(3×33+2)×10=1010(微秒),可以认为是1毫秒
***************************************************/
void delay1ms()
{
   unsigned char i,j;        
         for(i=0;i<4;i++)
          for(j=0;j<33;j++);               
}
ID:65751 发表于 2014-10-7 10:54
楼上,我敢保证我的想法是对的。因为把源程序原封不动写入芯片,测温是正常显示的,晶振是12Mhz。当然引脚也是对应的。
ID:19715 发表于 2014-10-7 08:47
程序的注释,不是完全对。
程序通过返复调整得到正确代码,这是每一个程序员编程过程,
在这过程中不可能总是修改注释,
这样就导致注释的不同步。
延时函数一般情况下是线性。
原程序的运行机器周期可能不是1us

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表