找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 88342|回复: 498
打印 上一主题 下一主题
收起左侧

请工程化,定制化你的单片机代码

    [复制链接]
回帖奖励 40 黑币 回复本帖可获得 2 黑币奖励! 每人限 1 次
跳转到指定楼层
楼主
  • - 前言 -
    • 时至今日,依旧看到很多小伙伴们放着单片机里的定时器不用,动辄delay1s(); delay500ms();虽然简单粗暴,但是其实是很不妥当的。
    • 还有很多需要按键的程序,动不动就“while(k1==0);”的等按键松开,这样的代码只是“为了满足某个功能“而设计的代码,而非”为了保证产品的质量“而设计的代码。
    • 我们应该时刻考虑“如果这傻逼用户不按我的标准操作来搞我的产品,我该怎么写程序?”,而不是”只要客户乖乖听我的,这样做是没问题的,就行了;出了问题都是他乱搞,跟我有个毛关系”,后面的思维是典型的只顾自己爽的思维,我们应当想方设法的提高产品的可靠度。
    • OK,扯远了,现在只是想提出一些设计的思路。
  • part1 . 系统的时基 & 依托于时基去设计函数
- 系统的时钟基线 -
  • 关键词,是“时钟基线”。
  • 我们需要设置一个定时器,这个定时器会告诉单片机:“好的,现在已经过了1ms了(或者100ms?这都没关系)”单片机一拍脑袋:“啊,已经到了1ms了么,那我就把某一件事情做一下吧”。
  • 大致像这样:
  • 这个程序的意思就是,让单片机每隔1ms就do_sth()一次(定时器的初始化函数省略了没画);
  • 这样写还可以让中断函数非常的简洁,几乎一进去就出来,基本不会影响到被中断打断的函数的工作;
  • 以传递关键参数的形式,大循环里只要查一查这个flag1ms值,知道什么时间要做什么事情就对了。
- 按键函数举例 -
  • 上图中这个do_sth()可以是任意函数,以按键函数keyPress()为例,可以这样写:
  1. void keyPress(){
  2.     static unsigned int key_press_time = 0;  // ……请记得标为静态变量
  3.     if(K1==0){
  4.         if(++key_press_time <=0 ) --key_press_time;//计量按键时间,并避免数据溢出
  5.         if(key_press_time==3000){
  6.             //在此写下按键长按3s时要做的事情;
  7.         }
  8.     }else{
  9.         if(20<=key_press_time && key_press_time < 3000){
  10.             //大于20ms小于3s,视为短按,在此写下写短按的处理代码
  11.         }
  12.         key_press_time=0;
  13.     }
  14. }
复制代码

  • 这里有很多可以研究的地方:
    • ①“if(++key_press_time <=0 ) --key_press_time;”这一句,看起来用“++key_press_time”就能搞定,但是,谁也不能保证某些用户真的不会按按键超过65秒的啊;万一他真的按了65576ms;单片机还就真的以为用户“短按”了一次呢(65576-65536=40ms,属于短按范畴),这下那个短按程序段也会被执行一次;现在这样写,哪怕你按100年也没关系了,反正我的单片机就每隔1ms进来看一次,K1这个按钮你想按多久就按多久,掉在我的范围内我就处理你,超出我的范围我就无视你。
    • ②“if(key_press_time==3000){ ; }”这里的3000只是随便设置的的一个时间(3s),如果你需要做按键长短按功能,这里就是你的长按程序所放的位置;你也可以不用3000;用2000,50000都没事,别超过65534就行(提问:为什么这里又是65534呢?);
    • ③“if(20<=key_press_time && key_press_time < 3000)”这里,前面的>=20是个消抖的设计,客户再强也是人类,再强的人类也不可能1秒按一个按键超过20次;也就是不可能短于50ms的时间;这里用了20ms相当于兼容 男上加男的快男 也来按按键了。后面<3000是不能和长按的时间冲突,因为3s我们已经人为的设置成长按时间节点了。
  • 好,现在知道相关的程序该怎么写了,我们回到刚刚讨论的时钟基线处。

-  多个时钟基线的方案  -
  • 刚刚的例子中我们只展示了1ms的时基,对于单片机来说,1ms已经很慢了,但对于人和某些设备来说来说还是快的不行,比如一些本就需要10ms间隔的通信设置,需要隔100ms才能刷一次的显示,需要50ms才能重新采的某些传感器等等,这些东西放进1ms的do_sth()里面肯定是不行的(其实也行,但不方便管理来着)——怎么办呢?
  • ——好办,放进10ms 或50ms 或100ms 的do_sth() 里就OK了
  • ——但是我们只有1个定时器啊,怎么弄几个时基呢?
  • ——1个定时器就够了,其他的时基都基于那个1ms的时基来就好。我们已经有了flag1ms,当然可以弄几个flag10ms乃至flag1000ms,只要你需要,就能设置。
  • 如下图,我们通过参数控制参数的形式,将flag1ms扩展为多个时基,使得程序渐渐的趋于模块化,精准化
  • 这里有个要注意的地方,就是函数从宏观上来看,确实是“每隔1ms就do_sth();每隔10ms就do_sth1();每隔100ms就do_sth2()"。
  • 但是,从微观上看,单片机是没法在同一时刻做2件事情的!所以,每到10ms的时候,单片机会”先把1ms的事情做完再做10ms的事“;每到100ms的时候,单片机会”先把1ms的事情做完再做10ms的事,再做100ms的事“。
  • 现在,我们的系统已经趋于成型了,不过要注意一点,这些做事情的子函数里万万不得有delay函数了(放几个nop倒是无伤大雅),好不容易划分好了时钟基线,你在1ms的时基里塞上几个delay2ms是要闹怎样。
  • 还有一个问题,那就是子函数不要拖泥带水,写得越简洁越好,赶紧把事情做完,把控制权还给单片机,由它来决定接下来要做什么。

-  函数应该放在哪个时基里?  -
  • 这也是关键的地方,这要求我们要对自己的程序要有清楚的把握。以及一定的产品思维。
  • 总的原则还是没变:所有的函数都要写得简洁干净,不要有任何模块的delay()加起来超过200us!
  • 一般来说,按键按一次不会超过50ms,放到flag10ms或者flag20ms的时间段里是没什么关系的(注意,这时候函数里的3000表示的就不是3s了,而是30s或者60s!这是遵循乘法原则的!);
  • LCD显示之类的,控制某个LED之类的,100ms一次就行了,人眼视觉暂留之类的看不出问题的。
  • 一般:检测,通信这类的子程序都能放到flag10ms或者flag20ms里。输出,显示这类的放flag100ms就OK了。
  • flag1ms里面应该放什么呢?我的意见是尽量什么也别放,空着都OK,因为其实没什么东西需要刷新得这么快的。
  • 如果有特别需要关照的部分,比如说步进电机的驱动啥的,请放到另一个定时器中断里(单片机有俩定时器的,不用白不用),按你需要的来设置。
  • 定时器的中断触发时间建议不要低于0.5ms,不然进中断就太频繁了,谁也不会希望自己正看着小电影的时候,爸妈过来拍你的房门吧?
  • —— 2019年6月10日更新 ——
  • Part2.将代码模块化,降低耦合度
  • - 降低代码间的耦合度 -
    • 通俗点说,耦合度就是表示两个东西“你中有我,我中有你”的程度,代码间的耦合度越高,你修改起来就越费劲,有时候看到代码黏糊糊的像一大坨蜜糖,难免会让人不知从何处下口。
    • 还是用个例子来说明一下吧——《电风扇系统设计1》
      • 假设我们要写个电风扇的控制程序 —— 按键方面:风扇有开关键,强风弱风切换,摆头控制3个按键(注意,这里的按键是那种大开大合的开关按键,不是那种轻触按键); 显示方面:每个按键旁边各有1个小led灯,共3个灯; 输出方面:有个控制风扇摆头的负载,有个控制风扇转不转的负载,有个控制风扇转得快或者慢的负载。
      • 或许有人三下两下就画了个这样的流程图(一般风扇没开的时候是不能摆头的,这里就先不管了):
      • 然后就随手写出了这样的代码:
      • 老实说,这没啥问题,因为我们的题目要求是那么的简单,简单的要求那就简单的实现吧。虽然这里的按键、显示、负载的程序代码像蜜糖一样黏糊糊的,但是代码也才十几行,谁会在意呢?也大致画一下如上代码的结构吧:
      • 但是,别忘了2点!①是客户的需求随时都会改动;是这么简单的项目还用不到你做。所以,我们稍微,加点难度上来看看?
    • 《电风扇系统设计2》
      • 在《风扇1》的基础上,①风扇没开时不能摆头;②风扇没开时,为了与没电作区分,要让3个LED灯同时以1s为周期闪烁;③开风扇后若是5小时内按键都没有变化过,则自动关掉风扇(避免久开,当然此后LED也要闪烁),若想重开风扇必须把“风扇开关按键”先关掉再打开才行。
      • 说句实话,先前的那份代码也到此为止了,如果有人还想在原来的代码上改出满足现在的需求的代码,我只能祝他好运。虽然也不是不能改,但是若是想用你那delay500ms来满足我的闪烁要求;或者硬是嵌入一些绕来绕去的东西在里面,那肯定没有从头再来要省事。
      • 那么——现在该怎么做呢?
        • 首先,把粘连的模块分开,按键归按键,显示归显示,输出归输出。就像这样子:
          • 这样的话,大家各做各的事情,互不干扰,需要共享的信息则通过关键参数来传递。这里我们设置了3个全局变量:key_on_flag, key_strong_flag, key_sheak_flag; 在keyPress()函数里可以修改这3个变量的值,然后在display()和output()函数里查询这3个变量的值来控制显示和输出。这时候的代码结构就是这种样子的了——各个模块分开,用关键参数传递信息

          • 所谓的“模块化,降低耦合度”大致就是这种感觉,现在你需要在哪里改动就单独改动哪个部分的,不至于瞻前顾后、束手束脚了。
        • 还没完呢,第二步,把我们的“时基”弄进来(不然上一堂课就白讲了),如图所示:
          • 如此一来,关于那些指定时间的、指定频率的要求,我们也能轻松的应对了。比如这时候的display()函数,可以这样写(这个其实还有缺陷,不过先凑合一下)——


      • 相信通过这样一个例子,大家应该能看到模块化对你的软件系统有多大的改善了。模块间的耦合度降低之后,自有一种九阳神功中的“他强由他强,清风拂山岗。它弱由他弱,明月照大江。”的感觉。你那边爱怎么变就怎么变,我这边有必要就改动,没必要就不改动,将彼此间的影响减到最低。
      • 时间有限,模块化的解说先到这里,后续有时间会将状态机等知识尽可能的讲解给大家。同时还得把这个需求变更后的《电风扇系统设计2》的软件给写完。
      • ——2019年6月11日 ,今天没空更贴,暂时先优化一下排版——
      • ——2019年6月14日,修复了教程中的keyPress()和display()函数中的两个静态变量忘了加static关键字的bug——
      • ——2019年6月16日,更新,状态机上篇 ——
      • part3.使用状态机,帮助你管理系统状态(上篇)
      • (本章较为复杂,需要读者有较好的数电和C语言功底,方能融汇贯通)
      • - 状态机入门(有基础的可以跳过本小节) -
        • 直白的说,状态机就是若干个“当前状态 + 触发条件 = 新状态( + 附加动作)”的公式。
        • 状态机可以画成图的形式(《状态迁移图》),也可以做成表格的形式(《状态分析表》),如果大家还有数字电子技术的功底的话,应该对下图还不至于太陌生。
          • ↑此为某状态迁移图举例(与下表同义)
          • ↑此为某状态分析表举例(与上图同义)
        • 如果上述图表使用公式来表达的话那就是4个公式(取决于图的箭头数,或者表的跳转数),用公式的话,公式的数量不但多,看起来也很麻烦,所以我们一般用《图》或者《表》来描述你的状态机。
        • 注意,一般的状态机是包含“动作”的,这里为了教学方便,略过了“动作”(后面会加回来的)。
        • 状态机的使用有助于我们更直接,更便捷的管理我们的系统工作。尤其是在系统比较复杂的时候。
      • - 用程序来表现状态机 -
        • 状态用枚举量为佳,因为我们应当保证系统的状态处于可控范围内,枚举量是我们工程师自定义的一个量,可以使系统处处受我们控制。
        • 触发条件用bit变量即可,触发了就是1,没触发就是0;当然char之类也可以,但没必要。
          • 有基础的同学可以使用"位结构体"来优化这些触发条件的内存,这里不提,请自行百度。
        • ok,那现在可以先定义我们的状态机变量了(以上述图表为例)。
            1. typedef enum{
            2.     STATE1,
            3.     STATE2,
            4.     STATE3
            5. }ENUM_STATE;     //定义ENUM_STATE枚举类型,表状态
            6. ENUM_STATE system_state = STATE1;    //定义上述枚举类型的枚举变量system_state, 初始化为STATE1
            7. bit test_flag_a,  test_flag_b, test_flag_c;   //定义3个触发条件的bit变量。
            复制代码

        • 那么,状态机的程序要怎么写呢?其实我们观察《状态分析表》的时候,有人会喜欢根据当前状态,分析触发条件,来决定下一刻的状态;有人会喜欢从触发条件开始,看看现在的状态是否受这种触发条件影响,而进入新的状态
        • ——好吧,有两种观察方法,就有2种写法,读者可以自由选择自己喜欢的写法。
          • 写法1(根据当前状态,看触发条件是否有效):
            1. void systemStateCtrl(){
            2.     switch(system_state){
            3.         case STATE1:
            4.             if(test_flag_b) system_state = STATE2;
            5.         break;
            6.         case STATE2:
            7.             if(test_flag_a) system_state = STATE1;
            8.             else if(test_flag_c) system_state = STATE3;//条件a比条件c优先
            9.         break;
            10.         case STATE3:
            11.             if(test_flag_a) system_state = STATE1;
            12.         break;
            13.         default:
            14.         break;
            15.     }
            16. }
            复制代码


          • 写法2(根据触发条件,看当前状态是否需要改变):
            1. void systemStateCtrl(){
            2.     if(test_flag_a){
            3.         if(system_state==STATE2 || system_state==STATE3)
            4.             system_state = STATE1;
            5.     }
            6.     else if(test_flag_b){
            7.         if(system_state==STATE1)
            8.             system_state = STATE2;
            9.     }
            10.     else if(test_flag_c){
            11.         if(system_state==STATE2)
            12.             system_state = STATE3;
            13.     }
            14.     else{
            15.         ;
            16.     }
            17. }
            复制代码

        • 这2种写法各有特点,并没有优劣之分,各位可以自取所需。非要比较的话,我个人认为:第一种写法"好读一些",第二种写法"好写一些"。
      • - 根据状态的值,控制系统的工作流 -
        • “系统的工作”很好理解,就是系统现在在干什么的意思,那么“系统的工作流”是什么意思?
        • ——系统的工作流,表示系统在某段时间内的工作流程。
        • ——为什么要普及“工作流”这个东西呢?
        • ——因为,很多情况下,某个状态的工作不是写死的,而是可变的。比如:冰箱在制冷时,制冷器并不是一直开着的,而是一段时间开,一段时间关;洗衣机在洗衣服时,滚筒不是一直开着的,而是先等注水完成之后,正着转一段时间,然后反着转一段时间,然后又正着转……就是有一系列的工作步骤,这些工作的步骤其实就是我们所说的工作流。
        • 如果你非要把洗衣机在洗衣服时,滚筒正着转和反着转分成2种新的状态的话……一路走好。
        • 总之,为了给状态下的动作有一定的预留空间(因为天知道,需求会不会发生变化),我们需要给每个状态都做一套关于此状态下的动作的设计
        • 这个程序写起来也很简单,嵌入位置上,直接塞进状态机的屁股后面就行。如下:
          1. /* 状态机程序 */
          2. void systemStateCtrl(){
          3.     //你的状态机程序
          4.     systemStateWork();//把状态工作程序放这里
          5. }
          6. /* 状态工作程序 */
          7. void systemStateWork(){ //设计你各个状态下的工作
          8.     switch(system_state){
          9.         case STATE1:
          10.             do_sth1();break;
          11.         case STATE2:
          12.             do_sth2();break;
          13.         case STATE3:
          14.             do_sth3();break;
          15.         default:
          16.             break;
          17.     }
          18. }
          复制代码

      • - 例程:《电风扇系统设计2》的状态机初版 -
        • 状态机真的是一个很庞大的知识点啊,好不容易把理论说完了,接下来诸位看看我的实例吧。
        • 这个系统设计的需求我就不再重复了,各位往回看一看就能找到,关键在于,需求③我们还未处理【③开风扇后若是5小时内按键都没有变化过,则自动关掉风扇(避免久开,当然此后LED也要闪烁),若想重开风扇必须把“风扇开关按键”先关掉再打开才行。】。
        • 那么开始吧,我们在一开始,会将系统的状态分成“风扇开”和“风扇关”两种,直接由风扇的开机键控制切换。但是,多了新的需求之后,开机键就不好使了——因为有种“风扇关”的状态,这时候的开机键也是按下的!经过一番思索,为了和真正的“风扇关”作区别,我们可以再创造一种新的状态——“停机”!也就是开机太久了,需要停机休息。停机时候的摇头键,强弱风键都无效,只有开机键松开,才能让你退出“停机”,进入"关机"。
        • 根据我们的分析,可以画出系统的状态迁移图:
        • 同样的,状态分析表。
        • 相关程序如下
        • typedef enum{
              STATE_OFF,
              STATE_ON,
              STATE_STOP
          }ENUM_STATE;     //定义ENUM_STATE枚举类型
          ENUM_STATE system_state = STATE_OFF; //定义枚举变量system_state, 初始化为STATE_OFF
          bit key_on_flag, key_off_flag, work_too_long_flag; //定义3个触发条件的bit变量(其实用2个就行)
          void systemStateCtrl(){
              if(key_on_flag){
                  if(system_state==STATE_ON || system_state==STATE_STOP)
                      system_state = STATE_OFF;
              }
              else if(key_off_flag){
                  if(system_state==STATE_OFF)
                      system_state = STATE_ON;
              }
              else if(work_too_long_flag){
                  if(system_state==STATE_ON)
                      system_state = STATE_STOP;
              }
              else{
                  ;
              }
              systemStateWork();//把状态工作程序放这里
          }
          void systemStateWork(){ //设计你各个状态下的工作
              switch( system_state ){
                  case STATE_OFF:
                      do_sth1();    //关机时的工作
                  break;
                  case STATE_ON:
                      do_sth2();    //开机时的工作
                  break;
                  case STATE_STOP:
                      do_sth3();    //超时停机时的工作
                  break;
                  default:
                  break;
              }
          }


          • 现在,我们从《电风扇系统设计2》的需求出发,解析系统的状态;再作图表(其实图和表做一个就行,这里为了解读需要,都做了出来),将状态机的大体结构都描述出来;这时候写各个状态下的工作程序,就是水到渠成的事情了。

      • - 状态机小结 -
        • 状态机确实是个比较长的教程,所以我将之分成了2部分来解说,第一部分只强调“状态+条件=新状态”这一部分,目的是让大家先对状态机有个初步的认知;第二部分再来描述“状态+条件=新状态+动作”的完整的状态机。
        • 眼尖的贴友应该也发现了,这里为了解说方便,还没有将“强弱风”,“是否摆头”等动作做进状态机里。不过他们其实只是一些“动作”而已,在开始分析系统的状态时,我们要分清楚系统的“主要动作”和“次要动作”,对于电风扇而言,开和关是主要的动作,摆头和强弱风只是一些次要的附加动作罢了。
        • 关于如何划分系统的主要状态?
        • ——这也是一个值得拿来长篇大论的话题,但我这里不想深入探讨,也无力深入探讨。因为“将指定物品分类”,或者“请给苹果,梨,水果,蔬菜,白菜,豆腐,白板笔这几个词进行排序”这类问题本就有无数解法,是很看重解答者的主观意识的。
        • 熟练掌握状态机的用法,可以使你能够更准确的把握系统的状态
        • 本章未完,此外,由于我的个人原因,状态机的下篇只能保证在今年7月15日前更新,还请各位谅解。届时会将本教程划上一个完美的句号,同时各种源码等也会打包上传。

评分

参与人数 28黑币 +416 收起 理由
hqh8001 + 10 很给力!
130boby + 5 绝世好帖!
gqzjl + 15 绝世好帖!
stone2018 + 5
ko44 + 11 很给力!
wjl520oo + 12 很给力!
ag26259 + 6 绝世好帖!
leooo + 30 绝世好帖!
13487086265 + 12 绝世好帖!
hxszrb + 10 很给力!
大胆1 + 10 很给力!
digger + 15 绝世好帖!
菜鸟yc + 5 赞一个!
cheng95 + 30
wfqxgw + 5
1255230 + 5 共享资料的黑币奖励!
fl1983 + 30 很给力!
莫烨 + 10 赞一个!
梁廷明 + 4 淡定
5083 + 5 赞一个!
wps10025 + 9 好贴子
copower + 30 赞一个!
杨雪飞 + 8 收藏,细细理解消化
164652999 + 30 好文章
dww465757120 + 30 很给力!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏359 分享淘帖 顶105 踩1
回复

使用道具 举报

来自 2#
ID:476527 发表于 2019-6-6 16:41 | 只看该作者
如果有需要,我可以把相关的主程序和架构打包上传……不过说实话这些代码随便敲敲就有了,需要锻炼的是一种“精准控制”软件的逻辑的思维。
跑快了的东西我们把它放慢些,跑慢了的东西给它放快些。
对于一个系统来说,“平衡”是很重要的事情。

最后再说一句,while(1)大循环里面不要放“delay();”或者“while(按键被按下);”了……
回复

使用道具 举报

来自 3#
ID:476527 发表于 2019-7-7 19:26 | 只看该作者
很抱歉的告诉大家,之前的想法还是太乐观,现在每天忙成狗,完全不确定啥时候能抽时间写完,7月15日要是没有更新的话还请大家谅解。

不过我是不会就这么停笔的,只是不知道啥时候有时间写而已。作为打工仔,真的身不由己。
为了不辜负大家的期待,下次更新时我会把文档做成pdf,附带源码,流程图,表格一并上传。

其实还有很多可以写的地方,但是现在这篇帖子就写这3点——时基,模块化,状态机——已经够大家消化一阵子了。而且,如果帖子的篇幅太长了,其实不利于阅读的。
以后如果还有别的分享,我会另开帖子的,多蹭点管理员的黑币,哈哈。
回复

使用道具 举报

来自 4#
ID:476527 发表于 2019-8-2 20:36 | 只看该作者
ccdmcu 发表于 2019-8-2 16:22
按键的松手检测呢?怎么用定时器写?

我不是给了个keyPress()程序么,你再好好看看。
简单来说,有键按下时,时间就会++;
无键按下时,时间就会清零。
但在清零之前会先检查,时间是否大于0,时间值大于0(或者消抖值)说明曾经有键按下过,并且刚刚松开。这时候处理“这次按键”不就好了。
回复

使用道具 举报

来自 5#
ID:385637 发表于 2019-8-10 10:38 | 只看该作者
楼主这文章比那些所谓的教科书好多了,那些教科书按键消抖动不动就是delay(20ms),真是误人子弟,真正出来做到大型项目才知道 DELAY是多么不实际。希望楼主继续更新,最好转成PDF,多点例程,尤其是一些按键的,短按,长按,双击组合键例程。现在绝大部份写按键程序都喜欢delay消抖,while松手检测。郭天详的书也是这么教的,一点不实用
回复

使用道具 举报

来自 6#
ID:476527 发表于 2019-8-12 11:46 | 只看该作者

确实不会“小于”0,但“等于”0是有可能的喔,所以这样写是没问题的。
至于为什么我写了“<=",而不是”==“呢?
理由是 —— 容错机制:
万一手残了,在定义key_press_time的时候忘记加了unsigned,我们的按键程序照样可以正常工作(只是计数最大值只有原来的一半了)
回复

使用道具 举报

来自 7#
ID:476527 发表于 2019-8-14 14:12 | 只看该作者
本帖最后由 Similarv 于 2019-8-15 10:40 编辑
IdeaMing 发表于 2019-8-14 10:27
我想问,怎么把DS18B20的加入到你这种机制里?一次通讯时间都要好几毫秒

这个好说,把基础时基变成10ms(或者20ms),也就是定时器中断10ms(或者20ms)产生一次。
然后就是……
  1. unsigned char flag10ms=0,flag100ms=0;
  2. void t0Init()
  3. { /*略*/}
  4. void t0Xint() Interrupt n
  5. {
  6.     flag10ms = 1;//10ms产生一次中断
  7. }
复制代码



回复

使用道具 举报

来自 8#
ID:476527 发表于 2019-8-19 01:00 来自手机 | 只看该作者
IdeaMing 发表于 2019-8-16 16:25
如果同时存在数码管的刷新呢,比如一秒读一次18B20,读的时候数码管的刷新就被卡了一下

我刚刚特意去查了一下,这玩意仅仅是个温度传感器,而且还特么是数字的,读一次温度居然至少要5Ms?!(还不确定是否会更久)
这样,如果你有示波器的话,请你做这么一个实验,把单片机某个端口设为输出,这个端口平时是低电平,然后用18b20读取温度之前拉高它,读取结束的时候又拉低它,然后用示波器好好的看看这个端口被拉高的时间,你就知道它每次采温需要多长时间了。再结合其他子程序的工作时长,设计你的时基。
其实,这种数字型温度传感器在我们实际做项目的人的眼里,只有华而不实四个字可以评价。如果我们的项目里需要测温,我们一般会采用内部自带adc模块的单片机,测温只要一个热敏电阻就可以搞定。整个测温的时间只需大约10us。不但更便宜,精度更好,效率还是你那个测温模块的500倍!具体方案这里就不赘述了。
回复

使用道具 举报

来自 9#
ID:476527 发表于 2020-1-13 16:52 | 只看该作者
本帖最后由 Similarv 于 2020-1-13 18:07 编辑
13303022280 发表于 2020-1-7 14:29
如果我只需要执行一次按键之后的程序,为了避免按键长按导致的重复运行,就用一个变量,在每次进入中断检 ...

你的做法当然也没有问题,不过我是这么做的:
按键就只是做按键检测,并且设置相应的“按键已触发”标志(bit变量);至于按键的功能则是在别处比如状态机中去执行。
例子:当按键按下之后开/关灯

key.c 文件:
bit key_flag_light;  //当按键触发时为1,否则为0, 此变量是给状态机使用的
static bit key_press_light; //当按键按下时为1,未按下为0,此变量仅在key.c内使用。
函数1-keyScan()  -- 功能:检测按键是否按下,若按键按下则将key_press_light置一,否则清零。
函数2-keyDeal()   -- 功能:使用某静态变量统计key_press_light的时间;当key_press_light从1变为0的时候,检查静态变量的值是否大于消抖值,若大于消抖时间则将key_flag_light设置为1;

state.c文件
函数1-StateMachine() -- 功能:当key_flag_light==1时,取反led_flag_out的值,随后将key_flag_light设置为0.

led.c文件
bit led_flag_out; //当该值==1,开灯;当该值==0;关灯
函数1-ledOutput() -- 功能:根据led_flag_out的取值开灯或者关灯

main.c文件
在10ms或者20ms的时基中依次调用
keyScan();
keyDeal();
StateMachine();
在100ms的时基中调用
ledOutput();

/******/
稍微修改一下,刚刚没有认真审题,你需要做长按的判定,我这个架构是基于短按的。
修改的地方位于keyDeal()部分,
短按 —— 是等待按键松开时裁决静态变量的计时值。
长按 —— 则是直接在静态变量等于‘长按时间点’的时刻将key_flag_light置一即可。key_flag_light==1的时候,状态机自然会处理长按事件,此后keyDeal()里的静态变量会在按键继续按着的时候继续加,但其值已经‘大于’‘长按时间点’了,所以key_flag_light不会被重复置一,当按键释放之后,此静态变量清零,一切重新开始。


回复

使用道具 举报

来自 10#
ID:476527 发表于 2020-1-13 17:46 | 只看该作者
hubj627 发表于 2020-1-13 09:20
这种针对代码量小的项目实时性没有问题。但代码量大的整个程序架构就会显得臃肿,也需针对同一个事情,在主 ...

进阶教程尚待坛友们自己修行,鄙人不才,只能带路到这了
回复

使用道具 举报

11#
ID:521890 发表于 2019-6-6 20:53 | 只看该作者
很好的文章,请继续。感谢!
回复

使用道具 举报

12#
ID:464755 发表于 2019-6-7 00:36 | 只看该作者
我对delay1s(); delay500ms()这样的延时函数就很不爽,当然程序流程很简单的,用用也没什么,如果是一个还算比较长的程序,使用delay1s(); delay500ms()这种就是在人为迟滞片子的运行,响应不过来又要骂,完全不会看自己做的程序写的是什么狗 p。你可以发现,凡是这样搞的,里面的变量,定义乱七八糟,想怎么写就怎么写。只要编译的过,不求后面优化。
以前的专业程序员,可是对单片机了如指掌,每一点一滴的性能,一个字节都很珍惜,写的程序兼职就是艺术品。
之前我们这也有个做机构设计的,画的加工件,完全不考虑加工难度,想怎么画就怎么画,机构极度粗 暴yu 蠢,我看不下去了,问他还就一句话,加工不管我的事,一个月不到就让他滚 蛋了。
回复

使用道具 举报

13#
ID:435708 发表于 2019-6-7 00:42 | 只看该作者
真的是好文章,对51定时器的理解更深了,原来程序还能这么写
回复

使用道具 举报

14#
ID:476527 发表于 2019-6-10 10:04 | 只看该作者
nsush 发表于 2019-6-7 00:36
我对delay1s(); delay500ms()这样的延时函数就很不爽,当然程序流程很简单的,用用也没什么,如果是一个还 ...

哈哈哈,做技术的可不能只管自己那小小的一亩三分地啊,不去学习和进步终究要被淘汰的。
还有些人在步入一个新领域的时候总是抱着玩一下的心态,勉强能用就行,却不想把事情做到更好,其实也是自己限制住了自己的成长。
回复

使用道具 举报

15#
ID:476527 发表于 2019-6-10 17:25 | 只看该作者
@admin 这应该是个比较长的教程,预计下下次更新才能写完,届时再发些源码和参考例程
回复

使用道具 举报

16#
ID:58925 发表于 2019-6-10 21:08 | 只看该作者
不错的文章,希望有后续
回复

使用道具 举报

17#
ID:557774 发表于 2019-6-11 10:13 来自手机 | 只看该作者
很好,学习了,现在实践一下
回复

使用道具 举报

18#
ID:342911 发表于 2019-6-12 11:56 来自手机 | 只看该作者
学习了  很受用  会慢慢去尝试应用  期待后续
回复

使用道具 举报

19#
ID:56665 发表于 2019-6-13 09:04 | 只看该作者
谢谢分享,多发一些类似文章。
回复

使用道具 举报

20#
ID:557550 发表于 2019-6-13 10:53 | 只看该作者
他们写程序粗的不行,可能自己觉的学个框架就算是学到了整个语言的精华,殊不知差得远呢。
回复

使用道具 举报

21#
ID:25481 发表于 2019-6-13 11:30 | 只看该作者
支持持续更新,很好的东西。
回复

使用道具 举报

22#
ID:104825 发表于 2019-6-13 12:24 来自手机 | 只看该作者
好文章,好理论,!通熟易懂!
回复

使用道具 举报

23#
ID:235691 发表于 2019-6-13 13:46 | 只看该作者
期待楼主的新文章
回复

使用道具 举报

24#
ID:382454 发表于 2019-6-13 21:19 来自手机 | 只看该作者
专家讲解,学习了。
回复

使用道具 举报

25#
ID:557774 发表于 2019-6-14 02:25 来自手机 | 只看该作者
感谢楼主,学习到了
回复

使用道具 举报

26#
ID:562287 发表于 2019-6-14 06:48 来自手机 | 只看该作者
这个必须加精啊,新手不知道有没有收藏功能
回复

使用道具 举报

27#
ID:143924 发表于 2019-6-14 08:38 | 只看该作者
很不错的文章,启迪匪浅。
回复

使用道具 举报

28#
ID:168971 发表于 2019-6-14 09:24 来自手机 | 只看该作者
好文章!
回复

使用道具 举报

29#
ID:516571 发表于 2019-6-14 10:18 | 只看该作者
很不错的文章,请继续
回复

使用道具 举报

30#
ID:111376 发表于 2019-6-14 11:31 | 只看该作者
感谢楼主的文章!
回复

使用道具 举报

31#
ID:562693 发表于 2019-6-14 17:13 来自手机 | 只看该作者
怎么关注楼主啊?感觉写的很有意思
回复

使用道具 举报

32#
ID:461428 发表于 2019-6-16 11:24 来自手机 | 只看该作者
新手完全不晓得你说的啥玩意
回复

使用道具 举报

33#
ID:476527 发表于 2019-6-16 12:05 | 只看该作者
kissme 发表于 2019-6-16 11:24
新手完全不晓得你说的啥玩意

那就请新手(自称)们加油,这帖子本来就是进阶教程,希望他们先将基础打牢
回复

使用道具 举报

34#
ID:564821 发表于 2019-6-17 18:05 | 只看该作者
感谢楼主,学到了很多
回复

使用道具 举报

35#
ID:512205 发表于 2019-6-17 22:30 | 只看该作者
很不错的文章
回复

使用道具 举报

36#
ID:517235 发表于 2019-6-18 09:05 | 只看该作者
很好的编程思路,条理清晰,把复杂的事情简单化。
回复

使用道具 举报

37#
ID:509639 发表于 2019-6-18 12:55 | 只看该作者
好贴!!!
回复

使用道具 举报

38#
ID:559308 发表于 2019-6-18 16:16 | 只看该作者
送人玫瑰,手有余香
回复

使用道具 举报

39#
ID:307505 发表于 2019-6-19 15:30 | 只看该作者
很好的技术贴
回复

使用道具 举报

40#
ID:282431 发表于 2019-6-20 08:39 | 只看该作者
很好的文章,请继续。感谢!
回复

使用道具 举报

41#
ID:467698 发表于 2019-6-20 10:48 | 只看该作者
楼主经验丰富,编程思想总结的很好,文章写得也很棒,希望可以继续分享,感谢!
回复

使用道具 举报

42#
ID:499184 发表于 2019-6-20 12:01 | 只看该作者
去除按键的抖动 不用延时函数的话,怎么处理
回复

使用道具 举报

43#
ID:476527 发表于 2019-6-20 13:30 | 只看该作者
tesrsm 发表于 2019-6-20 12:01
去除按键的抖动 不用延时函数的话,怎么处理

用静态变量去统计按键的时间,随后对此时间进行裁决。
延时只会让你的程序卡在那里一段时间,动弹不得。
回复

使用道具 举报

44#
ID:243491 发表于 2019-6-20 13:46 | 只看该作者
好文章,不知楼主能否弄成pdf格式上传呢,想保存起来
回复

使用道具 举报

45#
ID:476527 发表于 2019-6-20 14:29 | 只看该作者
lianzai 发表于 2019-6-20 13:46
好文章,不知楼主能否弄成pdf格式上传呢,想保存起来

等我抽空把状态机的下半部分写完先再说吧
回复

使用道具 举报

46#
ID:140275 发表于 2019-6-20 18:27 | 只看该作者
这样就是操作系统的雏形
回复

使用道具 举报

47#
ID:306475 发表于 2019-6-21 15:13 | 只看该作者
高手,膜拜了。
回复

使用道具 举报

48#
ID:476274 发表于 2019-6-24 11:34 | 只看该作者
不错学习了
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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