《匠人手记》推荐网上购书渠道:
EDN网(ednchina)购书入口   >>>
互动出版网(china-pub)购书入口   >>>
当当网(dangdang)购书入口   >>>
淘宝网(taobao)购书入口   >>>
更多购书渠道……   >>> 

设为首页加入收藏联系匠人管理入口21IC首页21IC博客21IC社区侃单片机回复的贴参与的贴

天气预报
百宝日历

百宝专栏

  • 首页 相册 标签
  • 电脑应用(65)
  • 供需信息(22)
  • 写书近况(82)
  • 匠人文集(115)
  • 硬件技术(171)
  • 匠人公告(86)
  • 与非门专栏(545)
  • 匠人笔记(115)
  • 团队撰写(96)
  • 汽车电子(52)
  • 编程技巧(465)
  • 程序宝典(476)
  • 网络酷文(472)
  • 开发工具(19)
  • 资料宝藏(274)
  • 项目管理(11)
  • 藏经宝阁(42)
  • 趣味设计(5)
  • 社区热贴(2)
  • 比尔盖茨熊专栏(0) 
  • 百宝信息

    载入中...

    百宝流量

    (2006-07-01开始)



    匠人手记

    51单片机 Keil C 延时程序的简单研究
    程序匠人 发表于 2006-5-8 12:30:00  阅读全文 | 回复(1) | 引用通告 | 编辑

    51单片机 Keil C 延时程序的简单研究

    作者:InfiniteSpace Studio/isjfk 
     
     
     
        应用单片机的时候,经常会遇到需要短时间延时的情况。需要的延时时间很短,一般都是几十到几百微妙(us)。有时候还需要很高的精度,比如用单片机驱动DS18B20的时候,误差容许的范围在十几us以内,不然很容易出错。这种情况下,用计时器往往有点小题大做。而在极端的情况下,计时器甚至已经全部派上了别的用途。这时就需要我们另想别的办法了。
        以前用汇编语言写单片机程序的时候,这个问题还是相对容易解决的。比如用的是12MHz晶振的51,打算延时20us,只要用下面的代码,就可以满足一般的需要:
            mov     r0, #09h
    loop:   djnz    r0, loop
    51单片机的指令周期是晶振频率的1/12,也就是1us一个周期。mov r0, #09h需要2个极其周期,djnz也需要2个极其周期。那么存在r0里的数就是(20-2)/2。用这种方法,可以非常方便的实现256us以下时间的延时。如果需要更长时间,可以使用两层嵌套。而且精度可以达到2us,一般来说,这已经足够了。
        现在,应用更广泛的毫无疑问是Keil的C编译器。相对汇编来说,C固然有很多优点,比如程序易维护,便于理解,适合大的项目。但缺点(我觉得这是C的唯一一个缺点了)就是实时性没有保证,无法预测代码执行的指令周期。因而在实时性要求高的场合,还需要汇编和C的联合应用。但是是不是这样一个延时程序,也需要用汇编来实现呢?为了找到这个答案,我做了一个实验。
        用C语言实现延时程序,首先想到的就是C常用的循环语句。下面这段代码是我经常在网上看到的:
    void delay2(unsigned char i)
    {
        for(; i != 0; i--);
    }
    到底这段代码能达到多高的精度呢?为了直接衡量这段代码的效果,我把 Keil C 根据这段代码产生的汇编代码找了出来:
                 ; FUNCTION _delay2 (BEGIN)
                                               ; SOURCE LINE # 18
    ;---- Variable ’i’ assigned to Register ’R7’ ----
                                               ; SOURCE LINE # 19
                                               ; SOURCE LINE # 20
    0000         ?C0007:
    0000 EF                MOV     A,R7
    0001 6003              JZ      ?C0010
    0003 1F                DEC     R7
    0004 80FA              SJMP    ?C0007
                                               ; SOURCE LINE # 21
    0006         ?C0010:
    0006 22                RET     
                 ; FUNCTION _delay2 (END)
    真是不看不知道~~~一看才知道这个延时程序是多么的不准点~~~光看主要的那四条语句,就需要6个机器周期。也就是说,它的精度顶多也就是6us而已,这还没算上一条 lcall 和一条 ret。如果我们把调用函数时赋的i值根延时长度列一个表的话,就是:
    i    delay time/us
    0    6
    1    12
    2    18
    ...
    因为函数的调用需要2个时钟周期的lcall,所以delay time比从函数代码的执行时间多2。顺便提一下,有的朋友写的是这样的代码:
    void delay2(unsigned char i)
    {
        unsigned char a;
        for(a = i; a != 0; a--);
    }
    可能有人认为这会生成更长的汇编代码来,但是事实证明:
                 ; FUNCTION _delay2 (BEGIN)
                                               ; SOURCE LINE # 18
    ;---- Variable ’i’ assigned to Register ’R7’ ----
                                               ; SOURCE LINE # 19
                                               ; SOURCE LINE # 21
    ;---- Variable ’a’ assigned to Register ’R7’ ----
    0000         ?C0007:
    0000 EF                MOV     A,R7
    0001 6003              JZ      ?C0010
    0003 1F                DEC     R7
    0004 80FA              SJMP    ?C0007
                                               ; SOURCE LINE # 22
    0006         ?C0010:
    0006 22                RET     
                 ; FUNCTION _delay2 (END)
    其生成的代码是一样的。不过这的确不是什么好的习惯。因为这里实在没有必要再引入多余的变量。我们继续讨论正题。有的朋友为了得当更长的延时,甚至用了这样的代码:
    void delay2(unsigned long i)
    {
        for(; i != 0; i--);
    }
    这段代码产生的汇编代码是什么样子的?其实不用想也知道它是如何恐怖的$#^%&%$......让我们看一看:
                 ; FUNCTION _delay2 (BEGIN)
                                               ; SOURCE LINE # 18
    0000 8F00        R     MOV     i+03H,R7
    0002 8E00        R     MOV     i+02H,R6
    0004 8D00        R     MOV     i+01H,R5
    0006 8C00        R     MOV     i,R4
                                               ; SOURCE LINE # 19
                                               ; SOURCE LINE # 20
    0008         ?C0007:
    0008 E4                CLR     A
    0009 FF                MOV     R7,A
    000A FE                MOV     R6,A
    000B FD                MOV     R5,A
    000C FC                MOV     R4,A
    000D AB00        R     MOV     R3,i+03H
    000F AA00        R     MOV     R2,i+02H
    0011 A900        R     MOV     R1,i+01H
    0013 A800        R     MOV     R0,i
    0015 C3                CLR     C
    0016 120000      E     LCALL   ?C?ULCMP
    0019 601A              JZ      ?C0010
    001B E500        R     MOV     A,i+03H
    001D 24FF              ADD     A,#0FFH
    001F F500        R     MOV     i+03H,A
    0021 E500        R     MOV     A,i+02H
    0023 34FF              ADDC    A,#0FFH
    0025 F500        R     MOV     i+02H,A
    0027 E500        R     MOV     A,i+01H
    0029 34FF              ADDC    A,#0FFH
    002B F500        R     MOV     i+01H,A
    002D E500        R     MOV     A,i
    002F 34FF              ADDC    A,#0FFH
    0031 F500        R     MOV     i,A
    0033 80D3              SJMP    ?C0007
                                               ; SOURCE LINE # 21
    0035         ?C0010:
    0035 22                RET     
                 ; FUNCTION _delay2 (END)
    呵呵,这倒是的确可以延迟很长时间~~~但是毫无精度可言了。
        那么,用C到底能不能实现精确的延时呢?我把代码稍微改了一下:
    void delay1(unsigned char i)
    {
        while(i--);
    }
    因为根据经验,越简洁的C代码往往也能得出越简洁的机器代码。那这样结果如何呢?把它生成的汇编代码拿出来看一看就知道了。满怀希望的我按下了“Build target”键,结果打击是巨大的:
                 ; FUNCTION _delay1 (BEGIN)
                                               ; SOURCE LINE # 13
    ;---- Variable ’i’ assigned to Register ’R7’ ----
                                               ; SOURCE LINE # 14
    0000         ?C0004:
                                               ; SOURCE LINE # 15
    0000 AE07              MOV     R6,AR7
    0002 1F                DEC     R7
    0003 EE                MOV     A,R6
    0004 70FA              JNZ     ?C0004
                                               ; SOURCE LINE # 16
    0006         ?C0006:
    0006 22                RET     
                 ; FUNCTION _delay1 (END)
    虽说生成的代码跟用for语句是不大一样,不过我可以毫无疑问的说,这两种方法的效率是一样的。似乎到此为止了,因为我实在想不出来源程序还有什么简化的余地。看来我就要得出来这个结论了:“如果需要us级的延时精度,需要时用汇编语言。”但是真的是这样吗?我还是不甘心。因为我不相信大名鼎鼎的 Keil C 编译器居然连 djnz 都不会用???因为实际上程序体里只需要一句 loop: djnz r7, loop。近乎绝望之际(往往人在这种情况下确可以爆发出来,哦呵呵呵~~~),我随手改了一下:
    void delay1(unsigned char i)
    {
        while(--i);
    }
    心不在焉的编译,看源码:
                 ; FUNCTION _delay1 (BEGIN)
                                               ; SOURCE LINE # 13
    ;---- Variable ’i’ assigned to Register ’R7’ ----
                                               ; SOURCE LINE # 14
    0000         ?C0004:
                                               ; SOURCE LINE # 15
    0000 DFFE              DJNZ    R7,?C0004
                                               ; SOURCE LINE # 16
    0002         ?C0006:
    0002 22                RET     
                 ; FUNCTION _delay1 (END)
    天~~~奇迹出现了......我想这个程序应该已经可以满足一般情况下的需要了。如果列个表格的话:
    i    delay time/us
    1    5
    2    7
    3    9
    ...
    计算延时时间时,已经算上了调用函数的lcall语句所花的2个时钟周期的时间。
        终于,结果已经明了了。只要合理的运用,C还是可以达到意想不到的效果。很多朋友抱怨C效率比汇编差了很多,其实如果对Keil C的编译原理有一个较深入的理解,是可以通过恰当的语法运用,让生成的C代码达到最优化。即使这看起来不大可能,但还是有一些简单的原则可循的:1.尽量使用unsigned型的数据结构。2.尽量使用char型,实在不够用再用int,然后才是long。3.如果有可能,不要用浮点型。4.使用简洁的代码,因为按照经验,简洁的C代码往往可以生成简洁的目标代码(虽说不是在所有的情况下都成立)。5...想不起来了,哦呵呵呵 
     
     

    看《匠人手记》,与匠人同行!北航出版,正在热卖!

    回复:51单片机 Keil C 延时程序的简单
    yy(游客)发表评论于2006-6-16 15:55:00  个人主页 | 引用 | 返回 | 删除 | 回复

    yy(游客)

    C:0x000C    02000F   LJMP     main(C:000F)
         4: void main()
         5: {
         6:  
         7:  unsigned char i;
         8:  for(i=10;i>0;--i);
    C:0x000F    7F0A     MOV      R7,#0x0A
    C:0x0011    DFFE     DJNZ     R7,C:0011
         9:  }

    我用你的方法得不到DJNZ,但是我所用的方法却可以,请看一下,仅用了一条指令

     

    看《匠人手记》,与匠人同行!北航出版,正在热卖!

    回复:51单片机 Keil C 延时程序的简单
    阿朗(游客)发表评论于2006-5-25 16:05:00  个人主页 | 引用 | 返回 | 删除 | 回复

    阿朗(游客)8错8错,刚好要用到这个部分,,,,,++i,还真没想到,,,thanks!!

    看《匠人手记》,与匠人同行!北航出版,正在热卖!

    回复:51单片机 Keil C 延时程序的简单
    lag3631(游客)发表评论于2006-5-12 23:09:00  个人主页 | 引用 | 返回 | 删除 | 回复

    lag3631(游客)

    好文章,一改我对汇编的不满

    学习!

    看《匠人手记》,与匠人同行!北航出版,正在热卖!

    发表评论:
    载入中...

    芯片专题

    器件专题

    软件专题

    硬件专题

    综合专题

    项目专题

    原创专题

    器件检测
    LCD LED
    按键 触摸键
    E2PROM
    电池 电机
    电阻 电容 电感

    指令系统
    软件算法
    编程规范
    滤波算法
    串行通讯

    PCB设计
    I2C PWM
    红外遥控
    充电技术
    中断 ADC 

    匠人手记
    匠人夜话
    网络心路
    一周热点串烧
    从零开始玩PIC
    DIY旋转时钟

    广告5号位 [投放]


    学习板、开发板、编程器、下载器、仿真器(查看详情……)

    站内搜索


    站外搜索


    百度  google
    mp3  歌词 
    图片  FLASH 
    知道  文档
    新闻  词典 
    地图  mp3 
    软件  天网 
    雅虎  爱问 
    搜狗  讯雷 
    网讯  华军 
    天空 

    21IC器件搜索
    百宝箱分站
  • 《匠人的百宝箱》21IC站
  • 《匠人的百宝箱》21IC笔记团队
  • 《匠人的百宝箱》MCUBLOG站
  • 《匠人的百宝箱》MCUBLOG笔记团队
  • 《匠人的百宝箱》EDN站
  • 《匠人手记》EDN书友会
  • 《匠人的百宝箱》与非网站
  • 《匠人的百宝箱》新浪站
  • 《匠人的百宝箱》百度站
  • 《匠人的百宝箱》网易126站
  • 《匠人的百宝箱》网易163站
  • 《匠人的百宝箱》互动出版网站
  • 广告4号位 [投放]

     
     
     

    新鲜货色

    匠人手记

    近期动态

    载入中...

      《匠人手记》购书全攻略 
     书友近况:淘书手记答疑与讨论:什么是散转程序 
     《匠人手记》新书艳照
     EDN《匠人手记》签名售书优惠活动开始报名啦!
     欢迎加入《匠人手记》EDN书友会
     欢迎加入《匠人手记》书友会Q群
     《匠人手记》终稿目录
     《匠人手记》封面,请大家先睹为快
     上周六收到了北航寄来的《匠人手记》清样,让大家先睹为快

    匠人原创

    粉丝评论

    往日酷贴

    载入中...

    载入中...



     网络酷文:博客,改变的不仅仅是图书 
     网络酷文:C语言宏定义技巧C语言 条件编译详解

      21IC上海2008-04聚会报名进行中。。。 
     两分钟让你明白什么是ERP![转]
      神奇的Duff's Device 算法
      实用一线通讯电路及软件设计方法
      程序员的“七年之痒”
      史上最短但最精彩的武侠小说
      网络无厘头文学《缺钙水浒》(爆笑)

     你的博客还能持续多久(转贴)
     电动车无刷电机控制器软件设计要点(作者:谢渊斌)

    大千八卦

    友情连接

    新浪新闻:
    新浪财经:
    AK58新闻:
    新浪股票:
    新浪股票:
    证券之星:

     [更多酷站连接]

     

     

    [欢迎交换连接]

    [百宝箱之与非门分舵]

    [电脑圈圈的家当]

    [IC921的博客]

    [柔月阁]

    [八楼的呼吸]

    [hotpower 的水潭]

    [xwj的文君阁]

    [所长的BLOG]

    [阿摆手记]

    [电子伙伴]

    [unaided的笔记]

    [小飞的笔记]

    [单片机开发联盟]

    [网址之家]

    [好东西网址大全]

    [美萍中文精选]

    [数字电视之家]

    [SMARTCODE电子书斋]

    [软件开发之窗]

    [Armoric]

    [我爱研发网]

    [infernal的笔记]

    [雄鹰的空中加油站]

    [SunK]

    [逍遥电子]

    [ningpanda的博客]

    [C-Design]

    [一网见天下]

    [海边淘沙]

    [嵌入式365]

    [水牛的仓库]

    [股剩是怎样炼成的]

    [PIC论坛]

    [ICC AVR开发网]

    [中国高校自动化网]

     

     

     

    MCU博客-中国电子工程师博客网 

    大学生电子网 

     

     

     

     

     

    !!! 《匠人的百宝箱》 !!!