<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          很強(qiáng)大!低耦合高內(nèi)聚的MCU實(shí)用軟件框架

          共 10782字,需瀏覽 22分鐘

           ·

          2021-07-10 16:25

          大家好,我是曉宇,不知道大家有沒(méi)有聽(tīng)過(guò)軟件設(shè)計(jì)中的低耦合,高內(nèi)聚的兩個(gè)原則。 

          具體是什么意思呢? 

          在一個(gè)項(xiàng)目中:每個(gè)模塊之間相聯(lián)系越緊密,則耦合性越高;這樣你改動(dòng)其中一個(gè)模塊,其他模塊也需要一起改動(dòng),換言之:牽一發(fā)而動(dòng)全身

          一個(gè)模塊內(nèi)部各個(gè)元素之間的聯(lián)系的緊密程度,如果各個(gè)元素(語(yǔ)句、程序段)之間的聯(lián)系程度越高,則內(nèi)聚性越高,也就是高內(nèi)聚

          如果一個(gè)程序的邏輯處理部分,分散到好幾個(gè)文件中,那么每次改動(dòng),就會(huì)改動(dòng)好幾個(gè)文件,這就是高耦合。

          高耦合,低內(nèi)聚


          低耦合,高內(nèi)聚           

          現(xiàn)在的軟件結(jié)構(gòu)設(shè)計(jì),都會(huì)要求“低耦合,高內(nèi)聚”,來(lái)保證軟件的高質(zhì)量,提高軟件的可維護(hù)性。

          下面是一個(gè)低耦合高內(nèi)聚的一種無(wú)OS的MCU實(shí)用軟件框架。

          包括任務(wù)輪詢管理,命令管理器、低功耗管理、環(huán)形緩沖區(qū)等實(shí)用模塊。系統(tǒng)中廣泛利用自定義段技術(shù)減少各個(gè)模塊間的耦合關(guān)系,大大提供程序的可維護(hù)性。

          主要功能

          • 支持模塊自動(dòng)化管理,并提供不同優(yōu)先等級(jí)初始化聲明接口。
          • 支持任務(wù)輪詢管理,通過(guò)簡(jiǎn)單的宏聲明即可實(shí)現(xiàn),不需要復(fù)雜的聲明調(diào)用。
          • 支持低功耗管理,休眠與喚醒通知。
          • 支持命令行解析,命令注冊(cè)與執(zhí)行。
          • blink設(shè)備支持,統(tǒng)一管理LED、震動(dòng)馬達(dá)、蜂鳴器

          使用說(shuō)明

          完整的代碼可以參考工程文件,系統(tǒng)開(kāi)發(fā)平臺(tái)如下:
          MCU:STM32F401RET6
          IDE:IAR 7.4或者Keil MDK 4.72A

          任務(wù)初始化及任務(wù)輪詢管理

          使用此模塊前需要系統(tǒng)提供滴答定時(shí)器,用于驅(qū)動(dòng)任務(wù)輪詢作業(yè)。(參考platform.c)

          //定時(shí)器中斷(提供系統(tǒng)滴答)
          void SysTick_Handler(void)
          {
              systick_increase(SYS_TICK_INTERVAL); //增加系統(tǒng)節(jié)拍
          }

          注冊(cè)初始化入口及任務(wù)(參考自key_task.c)

          static void key_init(void)
          {
              /*do something*/
          }

          static void key_scan(void)
          {
              /*do something*/
          }

          module_init("key", key_init);              //注冊(cè)按鍵初始化接口
          driver_register("key", key_scan, 20);      //注冊(cè)按鍵任務(wù)(20ms輪詢1次)

          命令管理器(cli)

          適用于在線調(diào)試、參數(shù)配置等(參考使用cli_task.c),用戶可以通過(guò)串口輸出命令行控制設(shè)備行為、查詢?cè)O(shè)備狀態(tài)等功能。

          命令格式

          cli支持的命令行格式如下:

          <cmd name> < param1> < param2> < paramn> < \r\n > <cmd name> ,< param1>, < param2>, < paramn>, < \r\n >

          每行命令包含一個(gè)命令名稱+命令參數(shù)(可選),命令名稱及參數(shù)可以通過(guò)空格或者','進(jìn)行分隔。

          系統(tǒng)默認(rèn)命令

          cli系統(tǒng)自帶了2條默認(rèn)命令,分別是"?"與"help"命令,輸入他們可以列出當(dāng)前系統(tǒng)包含的命令列表,如下所示:

          ?         - alias for 'help'
          help      - list all command.
          pm        - Low power control command
          reset     - reset system
          sysinfo   - show system infomation.

          適配命令管理器

          完整的例子可以參考cli_task.c

          static cli_obj_t cli;                               /*命令管理器對(duì)象 */

          /* 
           * @brief       命令行任務(wù)初始化
           * @return      none
           */
           
          static void cli_task_init(void)
          {
              cli_port_t p = {tty.write, tty.read};           /*讀寫接口 */

              cli_init(&cli, &p);                             /*初始化命令行對(duì)象 */

              cli_enable(&cli);

              cli_exec_cmd(&cli,"sysinfo");                   /*顯示系統(tǒng)信息*/
          }

          /* 
           * @brief       命令行任務(wù)處理
           * @return      none
           */
           
          static void cli_task_process(void)
          {
              cli_process(&cli);
          }

          module_init("cli", cli_task_init);                  
          task_register("cli", cli_task_process, 10);          /*注冊(cè)命令行任務(wù)*/

          命令注冊(cè)

          以復(fù)位命令為例(參考cmd_devinfo.c):

          #include "cli.h"
          //...
          /* 
           * @brief       復(fù)位命令
           */
           
          int do_cmd_reset(struct cli_obj *o, int argc, char *argv[])
          {
              NVIC_SystemReset();
              return 0;
          }cmd_register("reset",do_cmd_reset, "reset system");

          低功耗管理器(pm)

          控制間歇運(yùn)行,降低系統(tǒng)功耗。其基本的工作原理是通過(guò)輪詢系統(tǒng)中各個(gè)模塊是否可以允許系統(tǒng)進(jìn)入低功耗。實(shí)際上這是一種判決機(jī)制,所有模塊都具有有票否決權(quán),即只要有一個(gè)模塊不允許休眠,那么系統(tǒng)就不會(huì)進(jìn)入休眠狀態(tài)。pm模塊在休眠前會(huì)統(tǒng)計(jì)出各個(gè)模塊會(huì)返回最小允許休眠時(shí)長(zhǎng),并以最小休眠時(shí)長(zhǎng)為單位進(jìn)行休眠。

          如何適配

          使用前需要通過(guò)pm_init進(jìn)行初始化適配,并提供當(dāng)前系統(tǒng)允許的最大休眠時(shí)間,進(jìn)入休眠的函數(shù)接口,基本的接口定義如下:

          /*低功耗適配器 ---------------------------------------------------------*/
          typedef struct {
              /**
               * @brief    系統(tǒng)最大休眠時(shí)長(zhǎng)(ms)
               */
            
              unsigned int max_sleep_time;
              /**
               * @brief     進(jìn)入休眠狀態(tài)
               * @param[in] time - 期待休眠時(shí)長(zhǎng)(ms)
               * @retval    實(shí)際休眠時(shí)長(zhǎng)
               * @note      休眠之后需要考慮兩件事情,1個(gè)是需要定時(shí)起來(lái)給喂看門狗,否則會(huì)在休眠
               *            期間發(fā)送重啟.另外一件事情是需要補(bǔ)償休眠時(shí)間給系統(tǒng)滴答時(shí)鐘,否則會(huì)
               *            造成時(shí)間不準(zhǔn)。
               */
               
              unsigned int (*goto_sleep)(unsigned int time);
          }pm_adapter_t;
          void pm_init(const pm_adapter_t *adt);

          void pm_enable(void);

          void pm_disable(void);

          void pm_process(void);

          完成的使用例子可以參考platform-lowpower.c,默認(rèn)情況下是禁用低功耗功能的,讀者可以去除工程中原來(lái)不帶低功耗版本的platform.c,并加入platform-lowpower.c文件進(jìn)行編譯即可使用。

          注冊(cè)低功耗設(shè)備

          以按鍵掃描為例,正常情況下,如果按鍵沒(méi)有按下,那么系統(tǒng)休眠可以進(jìn)入休眠狀態(tài),對(duì)按鍵功能是沒(méi)有影響的。如果按鍵按下時(shí),那么系統(tǒng)需要定時(shí)喚醒并輪詢按鍵任務(wù)。所以在一個(gè)低功耗系統(tǒng)下,為了不影響按鍵實(shí)時(shí)性需要處理好兩個(gè)事情:

          1. 系統(tǒng)休眠狀態(tài)下,如果有按鍵按下,那系統(tǒng)系統(tǒng)應(yīng)立即喚醒,以便處理接下來(lái)的掃描工作。
          2. 如果按鍵按下時(shí),系統(tǒng)可以進(jìn)入休眠,但需要定時(shí)喚醒起來(lái)輪詢按鍵任務(wù)。對(duì)于第一種情況,將按鍵配置為邊沿中斷喚醒即可,以STM32F4為例(參考key_task.c),它支持外部中斷喚醒功能。
          /* 
           * @brief       按鍵 io初始化
           *              PC0 -> key;
           * @return      none
           */
           
          static void key_io_init(void)
          {
              /* Enable GPIOA clock */
              RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

              gpio_conf(GPIOC, GPIO_Mode_IN, GPIO_PuPd_UP, GPIO_Pin_0);

              //低功耗模式下,為了能夠檢測(cè)到按鍵,配置為中斷喚醒
              RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
              SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource0);
              exti_conf(EXTI_Line0, EXTI_Trigger_Falling, ENABLE);
              nvic_conf(EXTI0_IRQn, 0x0F0x0F);

              key_create(&key, readkey, key_event);            /*創(chuàng)建按鍵*/
          }

          對(duì)于第二種情況,可以通過(guò)pm_dev_register來(lái)處理,當(dāng)系統(tǒng)請(qǐng)求休眠時(shí),如果此時(shí)按鍵按下,則返回下次喚醒時(shí)間即可,如下面的例子所示。

          //參考key_task.c
          #include "pm.h"                                     
          /*
           * @brief    休眠通知
           */

          static unsigned int  key_sleep_notify(void)
          {
              return key_busy(&key) || readkey() ? 20 : 0;    /* 非空閑時(shí)20ms要喚醒1次*/
          } pm_dev_register("key"NULL, key_sleep_notify, NULL);

          blink模塊

          具有閃爍特性(led, motor, buzzer)的設(shè)備(led, motor, buzzer)管理 使用步驟:

          • 需要系統(tǒng)提供滴答時(shí)鐘,blick.c中是通過(guò)get_tick()接口獲取,依賴module模塊
          • 需要在任務(wù)中定時(shí)進(jìn)行輪詢

          或者通過(guò)"module"模塊的任務(wù)注冊(cè)來(lái)實(shí)現(xiàn)

          task_register("blink", blink_dev_process, 50);  //50ms輪詢1次

          LED驅(qū)動(dòng)

          blink_dev_t led;                             //定義led設(shè)備

          /*
           *@brief     紅色LED控制(GPIOA.8)
           *@param[in] on - 亮滅控制
           */

          static void led_ctrl(int on)
          {
              if (on)
                  GPIOA->ODR |= (1 << 8);
              else 
                  GPIOA->ODR &= ~(1 << 8);
          }

          /*
           *@brief     led初始化程序
           */

          void led_init(void)
          {
              led_io_init(void);                  //led io初始化
              blink_dev_create(&led, led_ctrl);   //創(chuàng)建led設(shè)備

              blink_dev_ctrl(&led, 501000);   //快閃(50ms亮, 100ms滅)
          }

          按鍵管理模塊

          類似blink模塊,使用之前有兩個(gè)注意事項(xiàng):

          • 需要系統(tǒng)提供滴答時(shí)鐘,key.c中是通過(guò)get_tick()接口獲取,依賴module模塊

          • 需要在任務(wù)中定時(shí)進(jìn)行輪詢

          key_t key;                             //定義按鍵管理器

          /*
           *@brief     按鍵事件
           *@param[in] type     - 按鍵類型(KEY_PRESS, KEY_LONG_DOWN, KEY_LONG_UP)  
           *@param[in] duration - 長(zhǎng)按持續(xù)時(shí)間
           */

          void key_event(int type, unsigned int duration)
          {
           if (type == KEY_PRESS) {                //短按

           } else if (type == KEY_LONG_DOWN) {     //長(zhǎng)按

           }


          //讀取鍵值(假設(shè)按鍵輸出口為STM32 MCU PA8)
          int read_key(void)
          {
           return GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8) == Bit_RESET;
          }

          /*
           *@brief     按鍵初始化
           */

          void key_init(void)
          {
              //打開(kāi)GPIO 時(shí)鐘
              RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
           //配置成輸入模式
              gpio_conf(GPIOA, GPIO_Mode_IN, GPIO_PuPd_NOPULL, GPIO_Pin_8); 
              //創(chuàng)建1個(gè)按鍵
              key_create(&key, read_key, key_event);  
          }

          開(kāi)源地址:https://gitee.com/moluo-tech/CodeBrick

          來(lái)源:小麥大叔

                                         


          瀏覽 34
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  啪啪啪啪啪AV | 国产精品久久久久久久免费 | 欧美日韩亚洲视频 | 国产成人豆花在线影视 | 国产色无码网站www色视频 |