本博客中示例代碼下載路徑: https://github.com/maziot-stm32/A1/releases/tag/v0.6
驅(qū)動(dòng)可配置化開發(fā)
前面已經(jīng)提到了代碼要分層, 便于理解和閱讀代碼. 這里針對其中的驅(qū)動(dòng)層引入一個(gè)新的概念, 驅(qū)動(dòng)可配置化開發(fā).
何為可配置化開發(fā), 舉個(gè)最簡單的例子:
原來我的 STM32F103RC 小板子(簡稱A1設(shè)備)上 PA8 和 PD2 接了一個(gè) LED 燈, 現(xiàn)在我重新畫了一個(gè)板子(簡稱C3設(shè)備), 用的依然是 F103RC 主控, 但是 LED 燈的接在 PC13 引腳上. 現(xiàn)在我希望 C3 可以直接復(fù)用 A1 的驅(qū)動(dòng), 驅(qū)動(dòng)代碼中改動(dòng)量控制在僅僅只需要修改硬件上外掛的 LED 燈個(gè)數(shù), 有效電平, 連接的 GPIO 引腳等與硬件電路強(qiáng)相關(guān)的信息, 其余與硬件不相關(guān)的代碼不需要做任何修改.
簡單總結(jié)下:
- 可配置化開發(fā)目的是為了提高代碼的復(fù)用性
- 可配置化開發(fā)將與硬件設(shè)計(jì)強(qiáng)相關(guān)的參數(shù)剝離出來, 針對不同的設(shè)備做不同的配置
LED 驅(qū)動(dòng)可配置化抽象
基于上面的概念, 想一下, LED 燈與硬件強(qiáng)相關(guān)的配置有哪些?
目前我想到的有這些:
- 硬件連接: 接在STM32的哪個(gè)GPIO口上, 包括PORT和PIN.
- 有效電平: 高電平點(diǎn)亮還是低電平點(diǎn)亮.
- 默認(rèn)狀態(tài): 上電后默認(rèn)是點(diǎn)亮還是熄滅.
用結(jié)構(gòu)體描述如下:
typedef struct _MAZDRV_LED_CTRL_
{
MAZDRV_LED_GPIO_PORT port; // 連接LED的GPIO PORT
MAZDRV_LED_GPIO_PIN pin; // 連接LED的GPIO PIN
MAZDRV_LED_GPIO_LEVEL light; // 高電平還是低電平點(diǎn)亮
MAZDRV_LED_STATUS status; // 默認(rèn)狀態(tài)是點(diǎn)亮還是熄滅
} MAZDRV_LED_CTRL;
每一個(gè)成員分別定義對應(yīng)的枚舉類型, 用于規(guī)范配置項(xiàng), 枚舉定義如下:
typedef enum _MAZDRV_LED_GPIO_PORT_
{
MAZDRV_LED_GPIO_PORTA = GPIOA_BASE,
MAZDRV_LED_GPIO_PORTB = GPIOB_BASE,
MAZDRV_LED_GPIO_PORTC = GPIOC_BASE,
MAZDRV_LED_GPIO_PORTD = GPIOD_BASE,
MAZDRV_LED_GPIO_PORTE = GPIOE_BASE,
MAZDRV_LED_GPIO_PORTF = GPIOF_BASE,
MAZDRV_LED_GPIO_PORTG = GPIOG_BASE,
} MAZDRV_LED_GPIO_PORT;
typedef enum _MAZDRV_LED_GPIO_PIN_
{
MAZDRV_LED_GPIO_PIN0 = GPIO_PIN_0,
MAZDRV_LED_GPIO_PIN1 = GPIO_PIN_1,
MAZDRV_LED_GPIO_PIN2 = GPIO_PIN_2,
MAZDRV_LED_GPIO_PIN3 = GPIO_PIN_3,
MAZDRV_LED_GPIO_PIN4 = GPIO_PIN_4,
MAZDRV_LED_GPIO_PIN5 = GPIO_PIN_5,
MAZDRV_LED_GPIO_PIN6 = GPIO_PIN_6,
MAZDRV_LED_GPIO_PIN7 = GPIO_PIN_7,
MAZDRV_LED_GPIO_PIN8 = GPIO_PIN_8,
MAZDRV_LED_GPIO_PIN9 = GPIO_PIN_9,
MAZDRV_LED_GPIO_PIN10 = GPIO_PIN_10,
MAZDRV_LED_GPIO_PIN11 = GPIO_PIN_11,
MAZDRV_LED_GPIO_PIN12 = GPIO_PIN_12,
MAZDRV_LED_GPIO_PIN13 = GPIO_PIN_13,
MAZDRV_LED_GPIO_PIN14 = GPIO_PIN_14,
MAZDRV_LED_GPIO_PIN15 = GPIO_PIN_15,
MAZDRV_LED_GPIO_PIN_ALL = GPIO_PIN_All,
} MAZDRV_LED_GPIO_PIN;
typedef enum _MAZDRV_LED_GPIO_LEVEL_
{
MAZDRV_LED_GPIO_LEVEL_HIGH = GPIO_PIN_SET,
MAZDRV_LED_GPIO_LEVEL_LOW = GPIO_PIN_RESET,
} MAZDRV_LED_GPIO_LEVEL;
typedef enum _MAZDRV_LED_STATUS_
{
MAZDRV_LED_STATUS_ON = 0,
MAZDRV_LED_STATUS_OFF,
MAZDRV_LED_STATUS_TOGGLE,
} MAZDRV_LED_STATUS;
基于上述剝離出來的參數(shù), 硬件上不同的 LED 燈就可以用 MAZDRV_LED_CTRL 對象來描述了. 示例代碼如下:
typedef enum _MAZDRV_LED_
{
MAZDRV_LED0 = 0,
MAZDRV_LED1,
MAZDRV_LED_MAX,
} MAZDRV_LED;
static MAZDRV_LED_CTRL g_mazdrv_led_ctrl[MAZDRV_LED_MAX] =
{
[MAZDRV_LED0] =
{
.port = MAZDRV_LED_GPIO_PORTA,
.pin = MAZDRV_LED_GPIO_PIN8,
.light = MAZDRV_LED_GPIO_LEVEL_LOW,
.status = MAZDRV_LED_STATUS_OFF,
},
[MAZDRV_LED1] =
{
.port = MAZDRV_LED_GPIO_PORTD,
.pin = MAZDRV_LED_GPIO_PIN2,
.light = MAZDRV_LED_GPIO_LEVEL_LOW,
.status = MAZDRV_LED_STATUS_OFF,
},
};
LED 可配置化驅(qū)動(dòng)程序
上面通過 MAZDRV_LED_CTRL 對象將硬件上 LED 的信息描述出來了. 現(xiàn)在我們的驅(qū)動(dòng)就必須以這些硬件信息為輸入, 支持各種各樣的配置情況.
LED 初始化:
int MAZ_Drv_led_init(void)
{
MAZDRV_LED led;
MAZDRV_LED_CTRL *ctrl = NULL;
GPIO_InitTypeDef GPIO_InitStruct = { 0 };
ctrl = g_mazdrv_led_ctrl;
for (led = MAZDRV_LED0; led < MAZDRV_LED_MAX; led++)
{
/* GPIO Ports Clock Enable */
MAZ_Drv_led_gpio_prot_clk_enable(led);
/* Configure GPIO pin */
GPIO_InitStruct.Pin = ctrl[led].pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Pull = (ctrl[led].light == MAZDRV_LED_GPIO_LEVEL_HIGH) ? GPIO_PULLDOWN : GPIO_PULLUP;
HAL_GPIO_Init((GPIO_TypeDef*) ctrl[led].port, &GPIO_InitStruct);
/* Set default status */
MAZ_Drv_led_set_status(led, ctrl[led].status);
}
return MAZRET_NOERR;
}
int MAZ_Drv_led_gpio_prot_clk_enable(MAZDRV_LED led)
{
MAZDRV_LED_GPIO_PORT port;
MAZDRV_LED_CTRL *ctrl = NULL;
if (led < MAZDRV_LED0 || led >= MAZDRV_LED_MAX)
{
return MAZRET_EINVAL;
}
ctrl = g_mazdrv_led_ctrl;
port = ctrl[led].port;
/* GPIO Ports Clock Enable */
switch (port)
{
case MAZDRV_LED_GPIO_PORTA:
__HAL_RCC_GPIOA_CLK_ENABLE();
break;
case MAZDRV_LED_GPIO_PORTB:
__HAL_RCC_GPIOB_CLK_ENABLE();
break;
case MAZDRV_LED_GPIO_PORTC:
__HAL_RCC_GPIOC_CLK_ENABLE();
break;
case MAZDRV_LED_GPIO_PORTD:
__HAL_RCC_GPIOD_CLK_ENABLE();
break;
case MAZDRV_LED_GPIO_PORTE:
__HAL_RCC_GPIOE_CLK_ENABLE();
break;
case MAZDRV_LED_GPIO_PORTF:
__HAL_RCC_GPIOF_CLK_ENABLE();
break;
case MAZDRV_LED_GPIO_PORTG:
__HAL_RCC_GPIOG_CLK_ENABLE();
break;
}
return MAZRET_NOERR;
}
LED 設(shè)置狀態(tài):
int MAZ_Drv_led_set_status(MAZDRV_LED led, MAZDRV_LED_STATUS status)
{
MAZDRV_LED_GPIO_LEVEL level;
MAZDRV_LED_CTRL *ctrl = NULL;
if (led < MAZDRV_LED0 || led >= MAZDRV_LED_MAX)
{
return MAZRET_EINVAL;
}
if (status < MAZDRV_LED_STATUS_ON || status > MAZDRV_LED_STATUS_TOGGLE)
{
return MAZRET_EINVAL;
}
ctrl = g_mazdrv_led_ctrl;
switch (status)
{
case MAZDRV_LED_STATUS_ON:
level = ctrl[led].light;
HAL_GPIO_WritePin((GPIO_TypeDef*) ctrl[led].port, ctrl[led].pin, level);
break;
case MAZDRV_LED_STATUS_OFF:
if (MAZDRV_LED_GPIO_LEVEL_HIGH == ctrl[led].light)
level = MAZDRV_LED_GPIO_LEVEL_LOW;
else if (MAZDRV_LED_GPIO_LEVEL_LOW == ctrl[led].light)
level = MAZDRV_LED_GPIO_LEVEL_HIGH;
HAL_GPIO_WritePin((GPIO_TypeDef*) ctrl[led].port, ctrl[led].pin, level);
break;
case MAZDRV_LED_STATUS_TOGGLE:
HAL_GPIO_TogglePin((GPIO_TypeDef*) ctrl[led].port, ctrl[led].pin);
break;
}
return MAZRET_NOERR;
}
不難發(fā)現(xiàn), 驅(qū)動(dòng)函數(shù)中所有與硬件相關(guān)的部分, 全部由 g_mazdrv_led_ctrl 變量提供. 因此移植代碼時(shí), 僅需要修改 g_mazdrv_led_ctrl 變量即可實(shí)現(xiàn)快速移植.
代碼倉庫
倉庫路徑: https://github.com/maziot-stm32/A1
驅(qū)動(dòng)文件: https://github.com/maziot-stm32/A1/tree/master/MAZ_Drivers/MAZ_Drv_led