日本a√视频在线,久久青青亚洲国产,亚洲一区欧美二区,免费g片在线观看网站

        <style id="k3y6c"><u id="k3y6c"></u></style>
        <s id="k3y6c"></s>
        <mark id="k3y6c"></mark>
          
          

          <mark id="k3y6c"></mark>

          "); //-->

          博客專欄

          EEPW首頁 > 博客 > 簡單實(shí)用IO輸入輸出框架

          簡單實(shí)用IO輸入輸出框架

          發(fā)布人:魚鷹談單片機(jī) 時(shí)間:2021-05-12 來源:工程師 發(fā)布文章

          在一個(gè)嵌入式系統(tǒng)中,可能存在許多輸入或輸出的IO口,輸入有霍爾傳感器、紅外對(duì)管等,輸出有LED、電源控制開關(guān)等。

          如果說硬件可以一次成型,那么隨便一份代碼都可以完成IO的配置工作,但研發(fā)階段的產(chǎn)品,硬件各種修改是難免的,每一次 IO 的修改,對(duì)于底層開發(fā)人員來說,可能都是一次挑戰(zhàn)。

          因?yàn)橐坏┯心骋粋€(gè) IO 配置錯(cuò)誤,或者原來的配置沒有修改正確(比如一個(gè) IO 在原來的硬件適配中是輸入,之后的硬件需要修改成輸出),那么你很難查出來這是什么問題,因?yàn)檫@個(gè)時(shí)候不僅硬件修改了,軟件也修改了,你需要先定位到底是軟件問題還是硬件問題,所以一個(gè)好用的 IO 的配置框架就顯得很有必要了。

          有道友會(huì)說,不如使用 CubeMx 軟件進(jìn)行開發(fā)吧。


          1、這個(gè)軟件適用于 ST 單片機(jī),以前還能用,現(xiàn)在,除非你家里有礦,不然誰用的起STM32?基本上都國產(chǎn)化了(雖然有些單片機(jī)號(hào)稱兼容,但到底還是有些差異的)。

          2、公司原本的代碼就是使用標(biāo)準(zhǔn)庫,只是因?yàn)镮O 的變化,你就需要把整個(gè)庫換掉嗎?時(shí)間上允許嗎?你確定修改后不會(huì)出現(xiàn)大問題?

          3、國產(chǎn)化的芯片可沒有所謂的標(biāo)準(zhǔn)庫和HAL庫供你選擇,每一家都有各自的庫,如果你的產(chǎn)品臨時(shí)換方案怎么辦?

          4、HAL 效率問題。

          今天魚鷹介紹一個(gè)簡單實(shí)用的框架,可用于快速增加或修改IO配置,甚至修改底層庫。

          假設(shè)有3個(gè) LED 作為輸出、3 個(gè)霍爾傳感器作為輸入:

          輸入配置代碼:

          #define GPIOx_Def           GPIO_TypeDef*
          #define GPIOMode_Def        GPIOMode_TypeDef
          typedef struct
          {
              GPIOx_Def       gpio; 
              uint16_t        msk;
              GPIOMode_Def    pull_up_down;     
          } bsp_input_pin_def; 
          #define  _GPIO_PIN_INPUT(id, pull, gpiox, pinx)   [id].gpio = (GPIOx_Def)gpiox, [id].msk = (1 << pinx), [id].pull_up_down = (GPIOMode_Def)pull
          #define  GPIO_PIN_INPUT(id, pull, gpiox, pinx)    _GPIO_PIN_INPUT(id, pull, gpiox, pinx)
          #define bsp_pin_get_port(gpiox)             ((uint16_t)((GPIO_TypeDef *)gpiox)->IDR)
          #define bsp_pin_get_value(variable,id)      do{ bsp_pin_get_port(bsp_input_pin[id].gpio) & bsp_input_pin[id].msk ? variable |= (1 << id) : 0;} while(0)
          #define BSP_GPIO_PUPD_NONE                                          GPIO_Mode_IN_FLOATING
          #define BSP_GPIO_PUPD_PULLUP                                        GPIO_Mode_IPU
          #define BSP_GPIO_PUPD_PULLDOWN                                      GPIO_Mode_IPD
          typedef enum
          {
              PIN_INPUT_HALL_0 = 0,  // 輸入 IO 定義
              PIN_INPUT_HALL_1,   
              PIN_INPUT_HALL_2,                    
              PIN_INPUT_MAX
          }bsp_pin_input_id_def;
          static const bsp_input_pin_def  bsp_input_pin [PIN_INPUT_MAX] = 
          {
              GPIO_PIN_INPUT(PIN_INPUT_HALL_0,          BSP_GPIO_PUPD_NONE, GPIOA, 0),
              GPIO_PIN_INPUT(PIN_INPUT_HALL_1,          BSP_GPIO_PUPD_NONE, GPIOB, 8),    
              GPIO_PIN_INPUT(PIN_INPUT_HALL_2,          BSP_GPIO_PUPD_NONE, GPIOE, 9),   
          };
          // 單個(gè) IO 初始化函數(shù)  
          void bsp_pin_init_input(GPIOx_Def gpiox, uint32_t msk, GPIOMode_TypeDef pull_up_down)
          {
              uint32_t temp;
              assert_param((msk & 0xffff0000) == 0 && gpiox != 0);
              temp = ((uint32_t) gpiox - (uint32_t) GPIOA) / ( (uint32_t) GPIOB - (uint32_t) GPIOA);
              /* enable the led clock */
              RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA << temp, ENABLE);
              GPIO_InitTypeDef GPIO_InitStruct;
              GPIO_InitStruct.GPIO_Mode  = (GPIOMode_Def)pull_up_down;
              GPIO_InitStruct.GPIO_Pin   = msk;
              GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
              GPIO_Init((GPIO_TypeDef*)gpiox, &GPIO_InitStruct);
          }
          // 所有 IO 初始化
          void gpio_input_init()
          {    
              bsp_input_pin_def  *info;
              info = (bsp_input_pin_def *)&bsp_input_pin;
              for(int i = 0; i < sizeof(bsp_input_pin)/sizeof(bsp_input_pin[0]); i++)
              {
                  bsp_pin_init_input(info->gpio, info->msk, info->pull_up_down);
                  info++;
              }   
          }
          // 最多支持 32 個(gè) IO 輸入
          uint32_t bsp_input_all(void)
          {
              uint32_t temp = 0;
              bsp_pin_get_value(temp, PIN_INPUT_HALL_0);
              bsp_pin_get_value(temp, PIN_INPUT_HALL_1);
              bsp_pin_get_value(temp, PIN_INPUT_HALL_2);
              return temp;
          }
          // 讀取單個(gè) IO 狀態(tài)
          uint32_t bsp_input_level(bsp_pin_input_id_def id)
          {
              return (bsp_pin_get_port(bsp_input_pin[id].gpio) & bsp_input_pin[id].msk) ? 1 : 0;
          }
          typedef enum
          {
              HW_HAL_LEVEL_ACTIVE = 0, // 可直接修改為 0 或 1,另一個(gè)枚舉值自動(dòng)修改為相反值
              HW_HAL_LEVEL_NO_ACTIVE = !HW_HAL_LEVEL_ACTIVE,
          }hw_input_hal_status_def;
          typedef struct  
          {
              hw_input_hal_status_def hal_level0; 
              uint8_t                 hal_level1;
              uint8_t                 hal_level2;
          }bsp_input_status_def;
          bsp_input_status_def bsp_input_status;
          int main(void)
          {  
              USRAT_Init(9600);//必須,進(jìn)入調(diào)試模式后點(diǎn)擊全速運(yùn)行
              gpio_input_init();
              while(1)
              {
                  uint32_t temp = bsp_input_all();
                  bsp_input_status.hal_level0 = (hw_input_hal_status_def)((temp >> PIN_INPUT_HALL_0) & 1);
                  bsp_input_status.hal_level1 = ((temp >> PIN_INPUT_HALL_1) & 1);
                  bsp_input_status.hal_level2 = ((temp >> PIN_INPUT_HALL_2) & 1);
              }                      
          }
          調(diào)試的時(shí)候,我們可以很方便的查看每個(gè) IO 的狀態(tài)是怎樣的,而不用管 0 或 1 到底代表什么意思:
          圖片
          輸出配置代碼:
          #define GPIOx_Def           GPIO_TypeDef*
          #define GPIOMode_Def        GPIOMode_TypeDef
          typedef struct
          {
              GPIOx_Def  gpio; 
              uint32_t   msk; 
              uint32_t   init_value; 
          } bsp_output_pin_def; 
          #define  _GPIO_PIN_OUT(id, gpiox, pinx, init)                        [id].gpio = gpiox, [id].msk = (1 << pinx), [id].init_value = init
          #define  GPIO_PIN_OUT(id, gpiox, pinx, init)                         _GPIO_PIN_OUT(id, gpiox, pinx, init)
          #define _bsp_pin_output_set(gpiox, pin)                              (gpiox)->BSRR = pin
          #define bsp_pin_output_set(gpiox, pin)                               _bsp_pin_output_set(gpiox, pin)
          #define _bsp_pin_output_clr(gpiox, pin)                              (gpiox)->BRR = pin
          #define bsp_pin_output_clr(gpiox, pin)                               _bsp_pin_output_clr(gpiox, pin)
          typedef enum
          {
              PIN_OUTPUT_LED_G,
              PIN_OUTPUT_LED_R,  
              PIN_OUTPUT_LED_B,
              PIN_OUTPUT_MAX
          }bsp_pin_output_id_def;
          static const bsp_output_pin_def  bsp_output_pin [PIN_OUTPUT_MAX] = 
          {
              GPIO_PIN_OUT(PIN_OUTPUT_LED_G,          GPIOA,  0, 0),
              GPIO_PIN_OUT(PIN_OUTPUT_LED_R,          GPIOF, 15, 0),
              GPIO_PIN_OUT(PIN_OUTPUT_LED_B,          GPIOD, 10, 0),
          };
          void bsp_pin_init_output(GPIOx_Def gpiox, uint32_t msk, uint32_t init)
          {
              uint32_t temp;
              assert_param((msk & 0xffff0000) == 0 && gpiox != 0);
              temp = ((uint32_t) gpiox - (uint32_t) GPIOA) / ( (uint32_t) GPIOB - (uint32_t) GPIOA);
              /* enable the led clock */
              RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA << temp, ENABLE);
              GPIO_InitTypeDef GPIO_InitStruct;
              GPIO_InitStruct.GPIO_Mode  = (GPIOMode_Def)GPIO_Mode_Out_PP;
              GPIO_InitStruct.GPIO_Pin   = msk;
              GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
              GPIO_Init((GPIO_TypeDef*)gpiox, &GPIO_InitStruct);
              if(init == 0)
              {
                  bsp_pin_output_clr(gpiox, msk);
              }
              else
              {
                  bsp_pin_output_set(gpiox, msk);
              }
          }
          void bsp_output_init()
          {
              bsp_output_pin_def  *info;
              info = (bsp_output_pin_def *)&bsp_output_pin;
              for(int i = 0; i < sizeof(bsp_output_pin)/sizeof(bsp_output_pin[0]); i++)
              {
                  bsp_pin_init_output(info->gpio, info->msk, info->init_value);
                  info++;
              }
          }
          void bsp_output(bsp_pin_output_id_def id, uint32_t value)
          {
              assert_param(id < PIN_OUTPUT_MAX);
              if(value == 0)
              {
                  bsp_pin_output_clr(bsp_output_pin[id].gpio, bsp_output_pin[id].msk);
              }
              else
              {
                  bsp_pin_output_set(bsp_output_pin[id].gpio, bsp_output_pin[id].msk);
              }
          }
          int main(void)
          {  
              USRAT_Init(9600);//必須,進(jìn)入調(diào)試模式后點(diǎn)擊全速運(yùn)行
              bsp_output_init();
              while(1)
              {
                  bsp_output(PIN_OUTPUT_LED_G, 1);
                  bsp_output(PIN_OUTPUT_LED_B, 0);
                  bsp_output(PIN_OUTPUT_LED_R, 1);
              }                      
          }

          這個(gè)框架有啥好處呢?

          1、自動(dòng)完成 GPIO 的時(shí)鐘初始化工作,也就是說你只需要修改引腳即可,不必關(guān)心時(shí)鐘配置,但對(duì)于特殊引腳(比如PB3),還是得另外配置才行。

          2、應(yīng)用和底層具體 IO 分離,這樣一旦修改了 IO,應(yīng)用代碼不需要進(jìn)行任何修改。

          3、增加或刪減 IO 變得很簡單,增加 IO時(shí),首先加入對(duì)應(yīng)枚舉,然后就可以添加對(duì)應(yīng)的 IO 了。刪除 IO時(shí),只要屏蔽對(duì)應(yīng)枚舉值和引腳即可。

          4、參數(shù)檢查功能, IO 刪除時(shí),因?yàn)槠帘瘟藢?duì)應(yīng)的枚舉,所以編譯時(shí)可以幫你發(fā)現(xiàn)問題,而增加 IO 時(shí),它可以幫你在運(yùn)行時(shí)檢查該 IO是否進(jìn)行配置了,可以防止因?yàn)槭д`導(dǎo)致的問題。

          1.jpg

          5、更改庫時(shí)可以很方便,只需要修改對(duì)應(yīng)的宏即可,目前可以順利在 GD32 和 STM32 庫進(jìn)行快速更換。

          6、對(duì)于輸入 IO 而言,可以方便的修改有效和無效狀態(tài),防止硬件修改有效電平。對(duì)于輸出 IO 而言,可以設(shè)定初始 IO 電平狀態(tài)。

          7、代碼簡單高效,盡可能的復(fù)用代碼,增加一個(gè) IO 只需要很少的空間。

          8、缺點(diǎn)就是,只對(duì)同種配置的 IO 可以這樣用。


          好好看看,或許能學(xué)到不少技巧哦。


          *博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。



          關(guān)鍵詞: ST

          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉