從會寫代碼,到想要寫好代碼,這個程序是很難受的,
由于做的嵌入式軟體,主要是MCU方面,都是要跟硬體底層打交道的軟體設計,接手的別人的軟體給人影響最深刻的就是典型的面向程序式編程,高層模塊大量依賴低層模塊,特別是高層模塊依賴底層硬體,還有各種跨層呼叫等,
缺點: 修改底層模塊,將影響高層模塊,在實際應用中,底層模塊又是經常要被修改的,
怎么解決?依賴反轉,低層模塊依賴高層,低層根據高層模塊來實作,
怎么實作依賴反轉?面向物件編程中有一個很重要的概念 —— 面向抽象介面編程,在C++中使用虛函式實作多型、抽象介面,C語言沒有虛函式,對于OOPC來說只能使用函式指標來實作多型、抽象介面,
了解很多理論后嘗試了在STM32上使用,
對GPIO進行抽象
資料結構:
struct gpio {
void (*gpio_init)(unsigned char port, unsigned short pin, unsigned char mode);
void (*set_gpio)(unsigned char port, unsigned short pin, unsigned char level));
void (*set_gpio_port)(unsigned char port, unsigned int portval);
unsigned char (*get_gpio)(unsigned char port, unsigned short pin);
unsigned int (*get_gpio_port)(unsigned char port);
};
MCU的GPIO雖然有很多模式,實際上只有一種,感覺有點類似設計模式中的單例模式
實作抽象介面:
#include "hal_gpio.h"
#include "stm32f10x.h"
#define STM32_PIN(X) (2 << (X-1))
#define ARR(A) (sizeof(A)/sizeof(A[0]))
static GPIO_TypeDef* stm32_port[] = {GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG};
static void hal_gpio_init(unsigned char port, unsigned short pin, unsigned char mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = STM32_PIN(pin); //LED0-->PB.5 埠配置
GPIO_InitStructure.GPIO_Mode = mode; //推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度為50MHz
GPIO_Init(stm32_port[port], &GPIO_InitStructure); //根據設定引數初始化GPIOB.5
}
static void hal_set_gpio(unsigned char port, unsigned short pin, unsigned char level)
{
if (level == SET)
{
GPIO_SetBits(stm32_port[port], (uint16_t)STM32_PIN(pin));
}
else
{
GPIO_ResetBits(stm32_port[port], (uint16_t)STM32_PIN(pin));
}
}
static void hal_set_gpio_port(unsigned char port, unsigned int portval)
{
GPIO_Write(stm32_port[port], (uint16_t )portval);
}
static unsigned char hal_get_gpio(unsigned char port, unsigned short pin)
{
return (unsigned char)GPIO_ReadInputDataBit(stm32_port[port], STM32_PIN(pin));
}
static unsigned int hal_get_gpio_port(unsigned char port)
{
return (unsigned int)GPIO_ReadOutputData(stm32_port[port]);
}
struct gpio gpio;
void init_gpio()
{
gpio.get_gpio = hal_get_gpio;
gpio.get_gpio_port = hal_get_gpio_port;
gpio.set_gpio = hal_set_gpio;
gpio.set_gpio_port = hal_set_gpio_port;
gpio.gpio_init = hal_gpio_init;
}
實作了STM32的GPIO硬體抽象介面,之后的GPIO操作使用gpio物件進行操作,這樣做之后,上層使用到GPIO功能,當要換其他MCU,只需要實作這些抽象介面即可,上一層的程式不要修改,
知道抽象介面這玩意后考慮使用它來給軟體分層,
關于嵌入式軟體分層設計的文章:
https://www.cnblogs.com/kmust/p/9250263.html
實作了GPIO硬體適配,假設現在有一個LED模塊,一個開發板總是要點燈的嘛,
抽象led:
led無非就是點亮、熄滅,
struct led{
void (*led_on)();
void (*led_off)();
};
void led_init(struct led *led, void (*led_on)(), void (*led_off)())
{
led->led_on = led_on;
led->led_off = led_off;
}
void set_led_on(struct led *led)
{
if (led->led_on != NULL)
{
led->led_on();
}
}
void set_led_off(struct led *led)
{
if (led->led_off != NULL)
{
led->led_off();
}
}
led的功能模塊:
struct led led1;
struct led led2;
void led1_on()
{
gpio.set_gpio(GPIO_E, 5, 0);
}
void led1_off()
{
gpio.set_gpio(GPIO_E, 5, 1);
}
void led2_on()
{
gpio.set_gpio(GPIO_B, 5, 0);
}
void led2_off()
{
gpio.set_gpio(GPIO_B, 5, 1);
}
led_init(&led1, led1_on, led1_off);
led_init(&led2, led2_on, led1_off);
更高一層的業務邏輯層可能呼叫led模塊去實作閃爍等功能,假如現在由于硬體變更,某個led控制引腳變更,用了抽象介面后業務邏輯層關于led閃爍的代碼不需要修改,只需要修改led功能模塊給這個led換一個引腳即可,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/35849.html
標籤:其他
