色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

如何在嵌入式中使用設計模式的思想?

STM32嵌入式開發 ? 來源:CSDN ? 2023-08-09 16:15 ? 次閱讀

嵌入式

嵌入式的標簽多為:低配,偏硬件,底層,資源緊張,代碼多以C語言匯編為主,代碼應用邏輯簡單。但隨著AIOT時代的到來,局面組件改變。芯片的性能資源逐漸提升,業務邏輯也逐漸變得復雜,相對于代碼的效率而言,代碼的復用可移植性要求越來越高,以獲得更短的項目周期 和更高的可維護性。下面是AIOT時代嵌入式設備的常見的軟件框架。72092d50-368b-11ee-9e74-dac502259ad0.png

設計模式

設計模式的標簽:高級語言 ,高端,架構等。在AIOT時代,設計模式與嵌入式能擦出怎樣的火花?設計模式可描述為:對于某類相似的問題,經過前人的不斷嘗試,總結出了處理此類問題的公認的有效解決辦法。嵌入式主要以C語言開發,且面向過程,而設計模式常見于高級語言(面向對象),目前市面上描述設計模式的書籍多數使用JAVA 語言,C語言能實現設計模式嗎?設計模式與語言無關,它是解決問題的方法,JAVA可以實現,C語言同樣可以實現。同樣的,JAVA程序員會遇到需要用模式來處理的問題,C程序員也可能遇見,因此設計模式是很有必要學習的。模式陷阱:設計模式是針對具體的某些類問題的有效解決辦法,不是所有的問題都能匹配到對應的設計模式。因此,不能一味的追求設計模式,有時候簡單直接的處理反而更有效。有的問題沒有合適的模式,可以盡量滿足一些設計原則,如開閉原則(對擴展開放,對修改關閉)
觀察者模式
情景在對象之間定義一個一對多的依賴,當一個對象狀態改變的時候,所有依賴的對象都會自動收到通知。7218cf44-368b-11ee-9e74-dac502259ad0.png實現

主題對象提供統一的注冊接口,以及注冊函數 。由觀察者本身實例化observer_intf 接口,然后使用注冊函數,添加到對應的主題列表中,主題狀態發生改變,依次通知列表中的所有對象。


	
 struct observer_ops
 {
     void*(handle)(uint8_t evt);  
 };
 struct observer_intf
 {
     struct observer_intf* next;
     const char* name;
     void* condition;
     const struct observer_ops *ops;
 }
intobserver_register(structtopical*top,structobserver_intf*observer);

	

當主題狀態發生改變,將通知到所有觀察者,觀察者本身也可以設置條件,是否選擇接收通知。

 struct observer_intf observer_list;
     
 void XXXX_topical_evt(uint8_t evt)
{
      struct observer_intf* cur_observer = observer_list.next;
      uint8_t* condition = NULL;
      while(cur_observer != NULL)
      {
          condition = (uint8_t*)cur_observer->condition;
          if(NULL == condition || (condition && *condition))
          {
              if(cur_observer->ops->handle){
                  cur_observer->ops->handle(evt);
              }       
          }
          cur_observer = cur_observer->next;
      }
 }

	實例:嵌入式裸機低功耗框架
  • 設備功耗分布
723e857c-368b-11ee-9e74-dac502259ad0.png其中線路損耗,電源電路等軟件無法控制,故不討論。板載外設,如傳感器可能通過某條命令配置進入低功耗模式,又或者硬件上支持控制外設電源來控制功耗。片內外設,及芯片內部的外設,通過卸載相關驅動,關閉時鐘配置工作模式來控制功耗。
  • 設備喚醒方式當系統某個定時事件到來時,系統被主動喚醒處理事件系統處于睡眠,被外部事件喚醒,如串口接收到一包數據,傳感器檢測到變化,通過引腳通知芯片
    • 被動喚醒
    • 主動喚醒
  • 系統允許睡眠的條件
    • 外設無正在收發的數據
    • 緩存無需要處理的數據
    • 應用層狀態處于空閑(無需要處理的事件)
  • 基于觀察者模式的PM框架實現

PM組件提供的接口:

struct pm
{
    struct pm* next;
    const char* name;
   void(*init)(void);
    void(*deinit(void);
    void* condition;
};
static struct pm pm_list;
static uint8_t pm_num;
static uint8_t pm_status;
         
int pm_register(const struct pm* pm , const char* name)
{
     struct pm* cur_pm =  &pm_list;
     while(cur_pm->next)
     {
         cur_pm = cur_pm->next;
     }
     cur_pm->next = pm;
     pm->next = NULL;
     pm->name = name;
     pm_num++;
}
 
void pm_loop(void)
{
    uint32_t pm_condition = 0;
    struct pm* cur_pm =  pm_list.next;
    static uint8_t cnt;
    
    /*check all condition*/
    while(cur_pm)
    {
        if(cur_pm->condition){
         pm_condition |=  *((uint32_t*)(cur_pm->condition));
        }
        cur_pm = cur_pm->next;
    }
    if(pm_condition == 0)
    {
      cnt++;
        if(cnt>=5)
        {
            pm_status = READY_SLEEP;
        }
    }
    else
    {
        cnt = 0;
    }
    if( pm_status == READY_SLEEP)
    {
         cur_pm =  pm_list.next;
         while(cur_pm)
         {
             if(cur_pm->deinit){
                cur_pm->deinit();
             }
             cur_pm = cur_pm->next;
         }
        pm_status = SLEEP;
        ENTER_SLEEP_MODE();
    }   
    /*sleep--->wakeup*/
    if(pm_status == SLEEP)
    {
         pm_status = NORMAL;
         cur_pm =  pm_list.next;
         while(cur_pm)
         {
             if(cur_pm->init){
                cur_pm->init();
             }
             cur_pm = cur_pm->next;
         }
    }
}

	

外設使用PM接口:


	
struct uart_dev
{
 ...
 struct pm pm;
    uint32_t pm_condition; 
};
struct uart_dev uart1;
 
void hal_uart1_init(void);
void hal_uart1_deinit(void);
    
void uart_init(void)
{
    uart1.pm.init =  hal_uart1_init;
    uart1.pm.deinit =  hal_uart1_deinit;
    uart1.pm.condition = &uart1.pm_condition;
    pm_register(&uart1.pm , "uart1");
}
/*接下來串口驅動檢查緩存 , 發送 , 接收是否空閑或者忙碌 , 給uart1.pm_condition賦值*/



結論
  • PM 電源管理可以單獨形成模塊,當功耗外設增加時,只需實現接口,注冊即可
  • 通過定義段導出操作,可以更加簡化應用層或外設的注冊邏輯
  • 方便調試,可以很方便打印出系統當前為滿足睡眠條件的模塊
  • 通過條件字段劃分,應該可以實現系統部分睡眠
職責鏈模式
情景在現實生活中,一個事件(任務)需要經過多個對象處理是很常見的場景。如報銷流程,公司員工報銷, 首先員工整理報銷單,核對報銷金額,有誤則繼續核對整理,直到無誤,將報銷單遞交到財務,財務部門進行核對,核對無誤后,判斷金額數量,若小于一定金額,則財務部門可直接審批,若金額超過范圍,則報銷單流傳到總經理,得到批準后,整個任務才算結束。類似的情景還有很多,如配置一個WIFI模塊,通過AT指令,要想模塊正確連入WIFI , 需要按一定的順序依次發送配置指令 , 如設置設置模式 ,設置 需要連接的WIFI名,密碼,每發送一條配置指令,模塊都將返回配置結果,而發送者需要判斷結果的正確性,再選擇是否發送下一條指令或者進行重傳。總結起來是,一系列任務需要嚴格按照時間線依次處理的順序邏輯,如下圖所示:

7244a04c-368b-11ee-9e74-dac502259ad0.png

在存在系統的情況下,此類邏輯可以很容易的用阻塞延時來實現,實現如下:

void process_task(void)
{
    task1_process();
    msleep(1000);
    
    task2_process();
    mq_recv(&param , 1000);
    
    task3_process();
    while(mq_recv(¶m , 1000) != OK)
    {
        if(retry)
        {
             task3_process();
             --try;
        }
    }
}

	

在裸機的情況下,為了保證系統的實時性,無法使用阻塞延時,一般使用定時事件配合狀態機來實現:

void process_task(void)
{
     switch(task_state)
     {
         case task1:
             task1_process();
             set_timeout(1000);break;
         case task2:
             task1_process();
             set_timeout(1000);break;
         case task3:
             task1_process();
             set_timeout(1000)break;
         default:break;
     }
}
/*定時器超時回調*/
void timeout_cb(void)
{
    if(task_state == task1)
    {
        task_state = task2;
        process_task();
    }
    else  //task2 and task3
    {
        if(retry)
        {
            retry--;
             process_task();
        }
    }
}
/*任務的應答回調*/
void task_ans_cb(void* param)
{
    if(task==task2)
    {
        task_state = task3;
        process_task();
    }
}

	和系統實現相比,裸機的實現更加復雜,為了避免阻塞,只能通過狀態和定時器來實現順序延時的邏輯,可以看到,實現過程相當分散,對于單個任務的處理分散到了3個函數中處理,這樣導致的后果是:修改,移植的不便。而實際的應用中,類似的邏輯相當多,如果按照上面的方法去實現,將會導致應用程序的強耦合實現可以發現,上面的情景有以下特點:
  • 任務按順序執行,只有當前任務執行完了(有結論,成功或者失敗)才允許執行下一個任務
  • 前一個任務的執行結果會影響到下一個任務的執行情況
  • 任務有一些特性,如超時時間,延時時間,重試次數
通過以上信息,我們可以抽象出這樣一個模型:任務作為節點, 每一個任務節點有其屬性:如超時,延時,重試,參數,處理方法,執行結果。當需要按照順序執行一系列任務時,依次將任務節點串成一條鏈,啟動鏈運行,則從任務鏈的第一個節點開始運行,運行的結果可以是 OK , BUSY ,ERROR 。若是OK, 表示節點已處理,從任務鏈中刪除,ERROR 表示運行出錯,任務鏈將停止運行,進行錯誤回調,可以有用戶決定是否繼續運行下去。BUSY表示任務鏈處于等待應答,或者等待延時的情況。當整條任務鏈上的節點都執行完,進行成功回調。

725f54b4-368b-11ee-9e74-dac502259ad0.png

node數據結構定義:

/*shadow node api type for req_chain src*/
typedef struct shadow_resp_chain_node
{
 uint16_t timeout;
 uint16_t duration;
 uint8_t init_retry;
 uint8_t param_type;
 uint16_t retry;
 /*used in mpool*/
   struct shadow_resp_chain_node* mp_prev;
 struct shadow_resp_chain_node* mp_next;
 
    /*used resp_chain*/
 struct shadow_resp_chain_node* next;
 
 node_resp_handle_fp handle;
 void* param;
}shadow_resp_chain_node_t;
node內存池:使用內存池的必要性:實際情況下,同一時間,責任鏈的條數,以及單條鏈的節點數比較有限,但種類是相當多的。比如一個支持AT指令的模塊,可能支持幾十條AT指令,但執行一個配置操作,可能就只會使用3-5條指令,若全部靜態定義節點,將會消耗大量內存資源。因此動態分配是必要的。72821ab2-368b-11ee-9e74-dac502259ad0.png初始化node內存池,內存池內所有節點都將添加到free_list。當申請節點時,會取出第一個空閑節點,加入到used_list , 并且接入到責任鏈。當責任鏈某一個節點執行完,將會被自動回收(從責任鏈中刪除,并從used_list中刪除,然后添加到free_list)

職責鏈數據結構定義:

typedef struct resp_chain
{
   bool enable;               //enble == true 責任鏈啟動 
 bool  is_ans;              //收到應答,與void* param 共同組成應答信號
 
 uint8_t state;            
 const char* name;
 void* param;
 TimerEvent_t timer;
 bool timer_is_running;
 shadow_resp_chain_node_t node;        //節點鏈
 void(*resp_done)(void* result);       //執行結果回調
}resp_chain_t;

	

職責鏈初始化:

void resp_chain_init(resp_chain_t* chain ,  const char* name , 
                                            void(*callback)(void* result))                   
{
   RESP_ASSERT(chain);
 /*only init one time*/
 resp_chain_mpool_init();
 
   chain->enable = false;
 chain->is_ans = false;
 chain->resp_done = callback;
 chain->name = name;
 
 chain->state = RESP_STATUS_IDLE;
 chain->node.next = NULL;
 chain->param = NULL;
 
 TimerInit(&chain->timer,NULL);
}

	

職責鏈添加節點:

int resp_chain_node_add(resp_chain_t* chain , 
                        node_resp_handle_fp handle , void* param)
{
   RESP_ASSERT(chain);
 BoardDisableIrq();  
 shadow_resp_chain_node_t* node = chain_node_malloc();
 if(node == NULL)
 {
    BoardEnableIrq();
    RESP_LOG("node malloc error ,no free node");
  return -2;
 }
 /*初始化節點,并加入責任鏈*/
 shadow_resp_chain_node_t* l = &chain->node;
 while(l->next != NULL)
 {
  l = l->next;
 }
 l->next = node;
 node->next = NULL; 
 node->handle = handle;
 node->param = param;
 node->timeout = RESP_CHIAN_NODE_DEFAULT_TIMEOUT;
 node->duration = RESP_CHIAN_NODE_DEFAULT_DURATION;
 node->init_retry = RESP_CHIAN_NODE_DEFAULT_RETRY;
 node->retry = (node->init_retry == 0)? 0 :(node->init_retry-1);
 BoardEnableIrq();
 return 0;
}

	

職責鏈的啟動:

void resp_chain_start(resp_chain_t* chain)
{
   RESP_ASSERT(chain);
 chain->enable = true;
}

	

職責鏈的應答:

void resp_chain_set_ans(resp_chain_t* chain , void* param)
{
 RESP_ASSERT(chain);
   if(chain->enable)
 {
  chain->is_ans = true;
  if(param != NULL)
     chain->param = param;
  else
  {
   chain->param = "NO PARAM";
  }
 }
}

	

職責鏈的運行:

int resp_chain_run(resp_chain_t* chain)
{ 
 RESP_ASSERT(chain);
 if(chain->enable)
 {
    shadow_resp_chain_node_t* cur_node = chain->node.next;
    /*maybe ans occur in handle,so cannot change state direct when ans comming*/
    if(chain->is_ans)
  {
   chain->is_ans = false;
   chain->state = RESP_STATUS_ANS;
  }  
   
  switch(chain->state)
  {
   case RESP_STATUS_IDLE:
   {
    if(cur_node)
    {
       uint16_t retry = cur_node->init_retry;
     if(cur_node->handle)
     {
        cur_node->param_type = RESP_PARAM_INPUT;
      chain->state = cur_node->handle((resp_chain_node_t*)cur_node                                                               ,cur_node->param);
     }
     else
     {
         RESP_LOG("node handle is null ,goto next node");
      chain->state = RESP_STATUS_OK;
     }
     if(retry != cur_node->init_retry)
     {
      cur_node->retry = cur_node->init_retry>0?(cur_node-                                                      >init_retry-1):0;                       
     }
    }
    else
    {
       if(chain->resp_done)
     {
      chain->resp_done((void*)RESP_RESULT_OK);
     }
     chain->enable = 0;
     chain->state = RESP_STATUS_IDLE;
     TimerStop(&chain->timer);
     chain->timer_is_running  = false;
    }
    break;
   }
   case RESP_STATUS_DELAY:
   {
    if(chain->timer_is_running == false)
    {
       chain->timer_is_running  = true;
     TimerSetValueStart(&chain->timer , cur_node->duration);
    }
    if(TimerGetFlag(&chain->timer) == true)
    {
     chain->state = RESP_STATUS_OK;
     chain->timer_is_running  = false;
    }
     break; 
   }
   case RESP_STATUS_BUSY:
   {
      /*waiting for ans or timeout*/
      if(chain->timer_is_running == false)
    {
       chain->timer_is_running  = true;
     TimerSetValueStart(&chain->timer , cur_node->timeout);
    }
    if(TimerGetFlag(&chain->timer) == true)
    {
     chain->state = RESP_STATUS_TIMEOUT;
     chain->timer_is_running  = false;
    }
    break;
      }
   case RESP_STATUS_ANS:
     {
      /*already got the ans,put the param back to the request handle*/
      TimerStop(&chain->timer);
    chain->timer_is_running  = false;
    if(cur_node->handle)
    {
     cur_node->param_type = RESP_PARAM_ANS;
     chain->state = cur_node->handle((resp_chain_node_t*)cur_node ,                                                                 chain->param);
    }
    else
    {
     RESP_LOG("node handle is null ,goto next node");
     chain->state = RESP_STATUS_OK;
    }
    break;
   }
   case RESP_STATUS_TIMEOUT:
   {
    if(cur_node->retry)
    {
     cur_node->retry--; 
     /*retry to request until cnt is 0*/
     chain->state = RESP_STATUS_IDLE;
    }
    else
    {
     chain->state = RESP_STATUS_ERROR;
    }
    break;
   }
   case RESP_STATUS_ERROR:
   {
      if(chain->resp_done)
    {
       chain->resp_done((void*)RESP_RESULT_ERROR);
    }
    chain->enable = 0;
    chain->state = RESP_STATUS_IDLE;
    TimerStop(&chain->timer);
    chain->timer_is_running  = false;
    cur_node->retry = cur_node->init_retry>0?(cur_node->init_retry-1):0;
    chain_node_free_all(chain);
    break;
   }
   case RESP_STATUS_OK:
   {
      /*get the next node*/
      cur_node->retry = cur_node->init_retry>0?(cur_node->init_retry-1):0;
    chain_node_free(cur_node);
    chain->node.next = chain->node.next->next;
    chain->state = RESP_STATUS_IDLE;
    break;
   }
      default:
     break;
  }
 }
 return chain->enable;
}

	

測試用例:

  • 定義并初始化責任鏈
void chain_test_init(void)
{
    resp_chain_init(&test_req_chain , "test request" , test_req_callback);
}

	
  • 定義運行函數,在主循環中調用
void chain_test_run(void)
{
    resp_chain_run(&test_req_chain);
}

	
  • 測試節點添加并啟動觸發函數
void chain_test_tigger(void)
{
    resp_chain_node_add(&test_req_chain ,  node1_req ,NULL);
    resp_chain_node_add(&test_req_chain ,  node2_req,NULL);
    resp_chain_node_add(&test_req_chain ,  node3_req,NULL);
    resp_chain_start(&test_req_chain);
}

	
  • 分別實現節點請求函數
 /*延時1s 后執行下一個節點*/
 int node1_req(resp_chain_node_t* cfg, void* param)
{
     cfg->duration = 1000;
     RESP_LOG("node1 send direct request: delay :%d ms" , cfg->duration);
     return RESP_STATUS_DELAY;
 }
 /*超時時間1S , 重傳次數5次*/ 
 int node2_req(resp_chain_node_t* cfg , void* param)
{
     static uint8_t cnt;
     if(param == NULL)
     {
         cfg->init_retry = 5;
         cfg->timeout  = 1000;
         RESP_LOG("node2 send request max retry:%d , waiting for ans...");
         return RESP_STATUS_BUSY;
     }
     RESP_LOG("node2 get ans: %d",(int)param);
     return RESP_STATUS_OK;
 }
 /*非異步請求*/  
 int node3_req(resp_chain_node_t* cfg , void* param)
{
     RESP_LOG("node4 send direct request");
     return RESP_STATUS_OK;
 }
 
 void ans_callback(void* param)
{
     resp_chain_set_ans(&test_req_chain , param);
 }

	結論
  • 實現了裸機處理 順序延時任務
  • 較大程度的簡化了應用程的實現,用戶只需要實現響應的處理函數 , 調用接口添加,即可按時間要求執行
  • 參數為空,表明為請求 ,否則為應答。(在某些場合,請求可能也帶參數,如接下來所說的LAP協議,此時需要通過判斷參數的類型)
設計模式參考Head First 設計模式(中文版)人人都懂設計模式:從生活中領悟設計模式:Python實現設計模式之禪設計模式的C語言應用.https://bbs.huaweicloud.com/blogs/113179適配器模式


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 芯片
    +關注

    關注

    456

    文章

    51140

    瀏覽量

    426150
  • 嵌入式
    +關注

    關注

    5090

    文章

    19173

    瀏覽量

    306844
  • C語言
    +關注

    關注

    180

    文章

    7614

    瀏覽量

    137409

原文標題:如何在嵌入式中使用設計模式的思想?

文章出處:【微信號:c-stm32,微信公眾號:STM32嵌入式開發】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    深入了解嵌入式編程

    能從PC機器編程去看嵌入式問題,那是第一步;學會用嵌入式編程思想,那是第二步;用PC的思想嵌入式思想
    的頭像 發表于 10-18 09:56 ?3691次閱讀

    何在嵌入式系統設計中使用UML技術

    嵌入式系統設計是一個軟、硬件結合的協同設計(Hardware/Software Co-design),需要不同技術背景的人共同開發。 本文將重點討論如何在嵌入式系統設計中使用 UML
    發表于 04-14 08:02 ?3923次閱讀
    如<b class='flag-5'>何在</b><b class='flag-5'>嵌入式</b>系統設計<b class='flag-5'>中使</b>用UML技術

    怎么利用分層思想進行嵌入式項目設計

    利用分層思想進行嵌入式項目設計,你試過嗎?
    發表于 05-22 10:16

    嵌入式開發必須掌握的設計思想

    嵌入式開發重要的2個設計思想
    發表于 03-09 07:02

    何在嵌入式系統設計中使用UML技術?

    統一建模語言UML及其特點UML在車載GPS終端設計中的應用如何在嵌入式系統設計中使用UML技術
    發表于 04-23 06:26

    何在嵌入式系統設計中使用UML技術?

    何在嵌入式系統設計中使用UML技術?怎樣去設計嵌入式系統?
    發表于 04-26 07:14

    嵌入式系統硬件抽象層的設計思想簡析

    嵌入式系統硬件抽象層(HAL & BSP)的設計思想1 前言1.1 層次化思想1.2 模塊化思想1.3 對象化思想2 板級支持包(BSP)3
    發表于 02-11 07:49

    何在嵌入式設備上運行高性能Java

    何在嵌入式設備上運行高性能Java
    發表于 03-28 09:43 ?16次下載

    嵌入式DHCP Server如何在vxWorks中

    嵌入式DHCP Server如何在vxWorks中的軟件實現
    發表于 03-29 12:27 ?23次下載

    基于AVR單片機的嵌入式“瘦服務器”系統設計思想

    基于AVR單片機的嵌入式“瘦服務器”系統設計思想 根據國內嵌入式設備的研究形勢和產業發展規模,提出了基于AVR單片機(ATmega103)的嵌入式 “瘦
    發表于 05-04 22:10 ?931次閱讀
    基于AVR單片機的<b class='flag-5'>嵌入式</b>“瘦服務器”系統設計<b class='flag-5'>思想</b>

    嵌入式系統中使用FPGA時的常見問題及對策

    電子發燒友網核心提示 :在嵌入式系統中使用FPGA時會經常出現以下常見問題,如在嵌入式設計中,怎樣使用FPGA、在嵌入式設計中,怎樣采用FPGA進行設計來降低風險等。今天電子發燒友
    發表于 10-17 13:38 ?1056次閱讀

    何在嵌入式Linux中使用GPIO

    了解如何在嵌入式Linux中使用GPIO,特別強調Zynq-7000系列。 我們介紹了基本的用戶和內核空間GPIO使用情況,以及GPIO,GPIO密鑰和GPIO LED上的bit-banged I / O.
    的頭像 發表于 11-26 07:02 ?4108次閱讀

    嵌入式系統系列叢書-時間觸發嵌入式系統設計模式

    嵌入式系統系列叢書-時間觸發嵌入式系統設計模式
    發表于 12-13 11:30 ?0次下載

    嵌入式中使用設計模式思想

    嵌入式的標簽多為:低配,偏硬件,底層,資源緊張,代碼多以C語言,匯編為主,代碼應用邏輯簡單。
    的頭像 發表于 05-05 14:35 ?600次閱讀
    在<b class='flag-5'>嵌入式</b><b class='flag-5'>中使</b>用設計<b class='flag-5'>模式</b>的<b class='flag-5'>思想</b>

    嵌入式思想與PC思想結合

    能從PC機器編程去看嵌入式問題,那是第一步。
    的頭像 發表于 05-10 10:42 ?631次閱讀
    <b class='flag-5'>嵌入式</b><b class='flag-5'>思想</b>與PC<b class='flag-5'>思想</b>結合
    主站蜘蛛池模板: G0GO人体大尺香蕉| 久久亚洲网站| 牲高潮99爽久久久久777| 岛国在线无码免费观| 午夜国产精品视频在线| 精品AV国产一区二区三区| 中文字幕偷乱免费视频在线| 男人都懂www深夜免费网站| 成人无码精品一区二区在线观看| 香蕉尹人综合精品| 久久国产综合精品欧美| xxx性欧美在线观看| 亚洲成人一区二区| 免费在线伦理片| 国产乱码一区二区三区| 中文字幕无码乱人伦蜜桃| 日本理论片和搜子同居的日子2| 国产内射AV徐夜夜| 中文字幕无码亚洲字幕成A人蜜桃| 秋霞影院福利电影| 簧片在线观看| 超碰在线vip| 伊人精品影院一本到综合| 日本无码毛片一区二区手机看 | 亚洲绝美精品一区二区| 蜜桃成熟时2在线| 国产精品99久久久久久AV色戒| 在线观看国产亚洲| 叔叔 电影完整版免费观看韩国| 久久中文字幕人妻AV熟女| 国产国产成年在线视频区| 99C视频色欲在线| 亚洲精品伊人久久久久| 日本午夜精品理论片A级APP发布| 久久99国产精品无码AV| 东北老妇人70OLDMAN| 在线观看永久免费网址| 无码射肉在线播放视频| 欧美成 人 网 站 免费| 久久re6热在线视频| 国产成人片视频一区二区青青|