一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

原來搞單片機(jī)也可以面向?qū)ο?/span>

 新用戶0118F7lQ 2021-10-28

摘要:在看別人單片機(jī)程序時(shí),你也許是奔潰的,因?yàn)槿肿兞繚M天飛,不知道哪個(gè)在哪用了,哪個(gè)表示什么,而且編寫極其不規(guī)范。自己寫單片機(jī)程序時(shí),也許你也是奔潰的??偢杏X重新開啟一個(gè)項(xiàng)目,之前的寫過相似的代碼也無法使用,得重新敲,代碼重用度不高,編程效率低下,代碼無法積累。而且感覺寫這個(gè)代碼沒有思想,沒有靈魂,沒有框架,只是一個(gè)一個(gè)功能代碼的堆砌,很空泛。

那么這個(gè)時(shí)候,你也許應(yīng)該在單片機(jī)中引入面向?qū)ο蟮乃枷肓?,使代碼更規(guī)范。

一、單片機(jī)程序框架

1、輪流執(zhí)行

int main (void)
{
 while(1)
 {
  sing();
  dance();
  play();
 }
}

函數(shù)sing執(zhí)行的時(shí)間比較長的話,函數(shù)dance就不能很快的被執(zhí)行。任何一個(gè)函數(shù)死掉的話就會(huì)影響整個(gè)系統(tǒng)。

2、前后臺(tái)

在使用 51、AVR、STM32 單片機(jī)裸機(jī)的時(shí)候一般都是在main函數(shù)里面用while(1)做一個(gè)大循環(huán)來完成所有的處理,即應(yīng)用程序是一個(gè)無限的循環(huán),循環(huán)中調(diào)用相應(yīng)的函數(shù)完成所需的處理。有時(shí)候我們也需要中斷中完成一些處理。相對(duì)于多任務(wù)系統(tǒng)而言,這個(gè)就是單任務(wù)系統(tǒng),也稱作前后臺(tái)系統(tǒng),中斷服務(wù)函數(shù)作為前臺(tái)程序,大循環(huán)while(1)作為后臺(tái)程序。

圖片

對(duì)應(yīng)的編程代碼大概是這樣的:

void EXTI_IRQHandler()
{
    flag = 1;
}
int main (void)
{
    while(1)
    {
        if (flag = 1)
        {
            do_something();
            flag = 0;
        }
    }
}

有什么問題?

前后臺(tái)系統(tǒng)的實(shí)時(shí)性差,前后臺(tái)系統(tǒng)各個(gè)任務(wù)(應(yīng)用程序)都是排隊(duì)等著輪流執(zhí)行,不管你這個(gè)程序現(xiàn)在有多緊急,沒輪到你就只能等著!相當(dāng)于所有任務(wù)(應(yīng)用程序)的優(yōu)先級(jí)都是一樣的。但是前后臺(tái)系統(tǒng)簡單啊,資源消耗也少?。≡谏晕⒋笠稽c(diǎn)的嵌入式應(yīng)用中前后臺(tái)系統(tǒng)就明顯力不從心了。

3、多任務(wù)

void first_task()
{
    while (1)
    {
        if(has_data())
            put_data();
    }
}
void second_task()
{
    while (1)
    {
        if(get_data())
            do_something();
    }
}

int main(void)
{
    create_task(first_task);
    create_task(second_task);
    start_scheduler();
}

多任務(wù)系統(tǒng)會(huì)把一個(gè)大問題“分而治之”,把大任務(wù)劃分成很多個(gè)小問題,逐步的把小任務(wù)解決掉,大任務(wù)也就隨之解決了,這些任務(wù)是并發(fā)處理的。注意,并不是說同一時(shí)刻一起執(zhí)行很多個(gè)任務(wù),而是由于每個(gè)任務(wù)執(zhí)行的時(shí)間很短,導(dǎo)致看起來像是同一時(shí)刻執(zhí)行了很多個(gè)任務(wù)一樣。

二、執(zhí)行的程序怎么寫?

以按鍵為例,點(diǎn)亮一個(gè)小燈!

圖片

1.常規(guī)寫法

int mian(void)
{
    while (1)
    {
        if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == GPIO_PIN_SET)
        {
            printf('按鍵按下\r\n');
        }
    }
}

2.面向?qū)ο蟮膶懛?/span>

首先我們把每一個(gè)按鍵都看成一個(gè)對(duì)象,既然是對(duì)象就肯定有屬性和行為,比如我們定義一個(gè)學(xué)生,那么這個(gè)學(xué)生有什么屬性呢?

肯定有姓名、年齡、身高、體重對(duì)吧,這些是一些基本的屬性,我們可以用一些單獨(dú)的變量來定義它,比如:

typedef struct
{

 uint8_t  *name; //姓名(變量)
 uint8_t  age;   //年齡(變量)
  uint8_t  height;//身高(變量)
  uint8_t  weight;//體重(變量)
student_t;

但是一個(gè)學(xué)生還有很多行為對(duì)吧,它會(huì)唱歌、跳舞、打籃球、也會(huì)關(guān)注果果小師弟的公眾號(hào)對(duì)吧,于是我們就可以這樣定義:

typedef struct
{

 uint8_t  *name;  //姓名(變量)
 uint8_t  age;    //年齡(變量)
  uint8_t  height; //身高(變量)
  uint8_t  weight; //體重(變量)
 void (*Sing_song)(void); //會(huì)唱歌(函數(shù)指針)
 void (*Dance_latin)(void); //會(huì)跳舞(函數(shù)指針)
  void (*Wechat_zhiguoxin)(void); //會(huì)關(guān)注果果的公眾號(hào)(函數(shù)指針)
student_t;

好了,這里我們提到了函數(shù)指針,所以就來說一說函數(shù)指針。

函數(shù)指針,顧名思義它就是一個(gè)指針,只不過它是一個(gè)函數(shù)指針,所以指向的是一個(gè)函數(shù)。類比一般的變量指針,指針變量,實(shí)質(zhì)上是一個(gè)變量,只不過這個(gè)變量存放的是一個(gè)地址,在32位單片機(jī)中,任何類型的指針變量都存放的是一個(gè)大小為4字節(jié)的地址。

重要的話說三遍!牢記在心?。。槭惨涀『瘮?shù)指針,因?yàn)樵趩纹瑱C(jī)面向?qū)ο缶幊讨?,結(jié)構(gòu)體的成員不是變量就是函數(shù)指針這兩種類型。變量就不用說了,函數(shù)指針理解就好。

其實(shí)函數(shù)指針可以類比一般的變量,看下面:

int   a; < = > void Sing_song(void);
int * p; < = > void (*zhiguoxin)(void);
p=&a;   < = > zhiguoxin = &Sing_song;
  1. 左邊走義變量a,右邊定義函數(shù)Sing_song;
  2. 左邊定義int指針,右邊定義函數(shù)指針;
  3. 左邊賦值指針,右邊賦值函數(shù)指針;

那么函數(shù)指針怎么用呢?我們還是以單片機(jī)為例,把按鍵類比為一個(gè)對(duì)象,這個(gè)按鍵有按鍵標(biāo)志位,有長按或者短按,按鍵還有行為:按鍵初始化、按鍵循環(huán)檢測(cè)等。

所以我們創(chuàng)建下面這樣一個(gè)結(jié)構(gòu)體,當(dāng)然這個(gè)結(jié)構(gòu)體不一定僅僅有這些變量和函數(shù),這完全取決于你自己的定義,你想怎么定義就怎么定義,你甚至可以定義按鍵的顏色都。

typedef struct
{

 uint8_t  KEY_Flag;  //標(biāo)志位(變量)
 uint8_t  Click;//按下(變量)
 void (*KEY_Init)(void); //按鍵初始化(函數(shù)指針)
 void (*KEY_Detect)(void); //按鍵檢測(cè)(函數(shù)指針)
} KEY_t;

現(xiàn)在已經(jīng)定義了KEY_t這種類型的結(jié)構(gòu)體,處理器還沒有分配給這個(gè)結(jié)構(gòu)體內(nèi)存,因?yàn)槲覀冎皇锹暶鬟@樣一個(gè)類型,而類型是不占用內(nèi)存的,只有我們定義對(duì)應(yīng)的結(jié)構(gòu)體類型的變量時(shí)才會(huì)在占用內(nèi)存空間。

那么怎么定義一個(gè)結(jié)構(gòu)體類型的變量呢?

KEY_t   KEY1;

然后就要初始化結(jié)構(gòu)體的成員變量了。

KEY_t  KEY1 = {0,0,KEY_init,KEY_detect};  

這里要注意了現(xiàn)在結(jié)構(gòu)體有四個(gè)成員,前兩個(gè)普通的變量,我們初始化為0,還有兩個(gè)函數(shù)指針,我們是不是要把我們想寫得函數(shù)的函數(shù)名字放在這里啊。

那么聰明的你肯定知道還要定義KEY_init();KEY_detect();這兩個(gè)函數(shù)。這兩個(gè)函數(shù)可以這樣寫。

static void KEY_init()
{
  GPIO_InitTypeDef GPIO_InitStruct;
  GPIO_InitStruct.Pin = GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
static void KEY_detect() 
{
 uint8_t i = 0
 if(KEY1.KEY_Flag == 1)
 {
  HAL_Delay(100);
  if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_3) == GPIO_PIN_SET)
  {
    printf('按鍵按下\r\n');
  }
  KEY1.KEY_Flag = 0;
 }
}

好了具體函數(shù)中的代碼我就不需要解釋了。這樣一個(gè)按鍵的對(duì)象我們就定義好了,這個(gè)按鍵我們賦予了'他'生命,有屬性(變量)有行為(函數(shù))。

這樣我們?cè)谥骱瘮?shù)就可以這樣的調(diào)用,來實(shí)現(xiàn)相應(yīng)的功能了。按鍵使用了中斷,這里并沒有講解。

void main(void)
{
  KEY1.KEY_Init();//初始化按鍵
  while(1
   {  
  KEY1.KEY_Detect();//按鍵檢測(cè)
 }
}

如果理解了這些,那么面向?qū)ο蟮木枘慊疽呀?jīng)掌握了,接下來就是不斷地去練習(xí)和實(shí)踐了。

三、為什么要面向?qū)ο螅?/span>

我們知道,現(xiàn)有的編程范式主要是:面向過程編程、面向?qū)ο缶幊?、函?shù)式編程。

對(duì)于流程清晰的簡單程序,一般只有一條流程主線,很容易被劃分成順序執(zhí)行的幾個(gè)步驟,面向?qū)ο缶幊毯兔嫦蜻^程編程沒有太大差別,并且面向過程編程常常比面向?qū)ο缶幊谈又庇^高效。

但當(dāng)我們面對(duì)一個(gè)大型的復(fù)雜程序,由于其錯(cuò)綜復(fù)雜的流程和交互關(guān)系,很難將其簡單地拆分成一條主線串成的簡單步驟,而通常表現(xiàn)為一個(gè)網(wǎng)狀關(guān)系結(jié)構(gòu)。這個(gè)時(shí)候,面向過程編程的這種流程化和線性化的思維方式就會(huì)顯得比較吃力,而面向?qū)ο缶幊痰膬?yōu)勢(shì)就比較明顯了。

面向?qū)ο缶幊田L(fēng)格的代碼更容易復(fù)用、擴(kuò)展和維護(hù)、更高級(jí)、更人性化、更適合大規(guī)模復(fù)雜程序的開發(fā)。在Linux中就是用的面向?qū)ο缶幊?,里面有很多的結(jié)構(gòu)體、指針、鏈表等等。如果還沒有接觸到面向?qū)ο缶幊讨荒苷f明你做的東西還不夠復(fù)雜。

在單片機(jī)舉一個(gè)例子,一塊開發(fā)板可能會(huì)適配不同的屏幕:

圖片
一塊板子,三個(gè)屏幕

那么每一塊板子肯定有不同的代碼適配,在程序中我們可以讀出屏幕的ID,然后通過if判斷來執(zhí)行不同的指令,就行這樣。

圖片
果果小師弟

如果使用面向?qū)ο缶幊?,那么就可以這樣寫代碼。

typedef struct lcd{
 uint8_t type;
 void (*LCD_Init)(void)
}lcd_t, *plcd_t;

int Read_id()
{
 /* 0: LCDA
  * 1: LCDB
  */

 return 0
}

int Get_Lcd_Type(void)
{
 return Read_id();
}

void LCDA_Init(void)//屏幕A初始化
{
    LCD_WR_REG(0xCF);  
    LCD_WR_DATA(0x00); 
    LCD_WR_DATA(0xC1); 
    LCD_WR_DATA(0X30); 
}

void LCDB_Init(void)//屏幕B初始化
{
    LCD_WR_REG(0X11);
    delay_ms(20);
    LCD_WR_REG(0XD0);
    LCD_WR_DATA(0X07); 
}

lcd_t openedv_com_lcds[] = {
 {0, LCDA_Init},
 {1, LCDB_Init},
};

plcd_t get_lcd(void)//獲取到屏幕類型
{
 int type = Get_Lcd_Type();
 return &openedv_com_lcds[type];
}

int main(void )
{
 plcd_t lcd; 
 lcd = get_lcd();//獲取到屏幕類型
 lcd-> LCD_Init();//初始化對(duì)應(yīng)屏幕
 while (1)
  {} 
}

這里只是偽代碼處理辦法,原理就和上面所講的一樣,在結(jié)構(gòu)體中使用變量和函數(shù)。

到這里你應(yīng)該掌握了面向?qū)ο蟮脝纹瑱C(jī)編程方法,一起來試驗(yàn)幾個(gè)例子:

LED燈

typedef struct
{
 
 void (*LED_ON)(uint8_t LED_Num);     //打開
 void (*LED_OFF)(uint8_t LED_Num);    //關(guān)閉
 void (*LED_Flip)(uint8_t LED_Num);   //翻轉(zhuǎn)
} LED_t;

按鍵KEY

typedef struct
{

 uint8_t  KEY_Flag;        //標(biāo)志位(變量)
 uint8_t  Click;           //按下(變量)
 void (*KEY_Init)(void);   //按鍵初始化(函數(shù)指針)
 void (*KEY_Detect)(void); //按鍵檢測(cè)(函數(shù)指針)
} KEY_t;

蜂鳴器BEEP

typedef struct
{

 uint8_t Status;      //狀態(tài)
 void (*ON)(void);     //打開
 void (*OFF)(void);    //關(guān)閉
} BEEP_t;

串口UART

typedef struct
{

 USART_TypeDef *uart;/* STM32內(nèi)部串口設(shè)備指針 */
 uint8_t *pTxBuf;   /* 發(fā)送緩沖區(qū) */
 uint8_t *pRxBuf;   /* 接收緩沖區(qū) */
 
 uint16_t usTxBufSize;  /* 發(fā)送緩沖區(qū)大小 */
 uint16_t usRxBufSize;  /* 接收緩沖區(qū)大小 */
 
  uint16_t usTxWrite; /* 發(fā)送緩沖區(qū)寫指針 */
  uint16_t usTxRead;  /* 發(fā)送緩沖區(qū)讀指針 */
  uint16_t usTxCount; /* 等待發(fā)送的數(shù)據(jù)個(gè)數(shù) */

  uint16_t usRxWrite; /* 接收緩沖區(qū)寫指針 */
  uint16_t usRxRead;  /* 接收緩沖區(qū)讀指針 */
  uint16_t usRxCount; /* 還未讀取的新數(shù)據(jù)個(gè)數(shù) */
  
  void (*RS485_Set_SendMode)(void);  //RS-485接口設(shè)置為發(fā)送模式
  void (*RS485_Set_RecMode)(void);   //RS-485接口設(shè)置為接收模式
}UART_T;

圖片
面向?qū)ο蟮膯纹瑱C(jī)編程

圖片





    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    亚洲超碰成人天堂涩涩| 91后入中出内射在线| 日本婷婷色大香蕉视频在线观看| 99精品国产一区二区青青 | 国产一级内片内射免费看| 国产亚洲精品香蕉视频播放| 一本色道久久综合狠狠躁| 五月婷婷欧美中文字幕| 精品香蕉国产一区二区三区| 中文字幕一区二区三区中文| 欧美日韩国产的另类视频| 精品人妻一区二区三区免费看| 日本在线视频播放91| 久热青青草视频在线观看| 亚洲高清中文字幕一区二区三区| 国产午夜精品在线免费看| 日本 一区二区 在线| 一区二区三区日韩在线| 欧美一本在线免费观看| 国产精品免费无遮挡不卡视频| 久久精品国产亚洲av久按摩| 久久黄片免费播放大全| 日本不卡一本二本三区| 日本一本不卡免费视频| 日本丰满大奶熟女一区二区| 亚洲一区二区三区三州| 又色又爽又黄的三级视频| 国产福利在线播放麻豆| 国产不卡的视频在线观看| 人妻乱近亲奸中文字幕| 免费精品国产日韩热久久| 日韩色婷婷综合在线观看| 亚洲夫妻性生活免费视频| 精品熟女少妇av免费久久野外| 日本欧美一区二区三区高清| 精品人妻久久一品二品三品| 午夜国产成人福利视频| 国产亚洲精品俞拍视频福利区| 尤物久久91欧美人禽亚洲| 夫妻性生活一级黄色录像| 久热青青草视频在线观看|