通用外设驱动模型(四步法)
举例:GPIO配置步骤
1、使能时钟
__HAL_RCC_GPIOx_CLK_ENABLE()
2、设置工作模式
HAL_GPIO_Init()
3、设置输出状态(可选)
HAL_GPIO_WritePin()
HAL_GPIO_TogglePin()
4、读取输入状态(可选)
HAL_GPIO_ReadPin()
模块化编程
led.h
#ifndef _LED_H
#define _LED_H
#include "./SYSTEM/sys/sys.h"/******************************************************************************************/
/* 引脚 定义 */#define LED0_GPIO_PORT GPIOB
#define LED0_GPIO_PIN GPIO_PIN_5
#define LED0_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PB口时钟使能 */#define LED1_GPIO_PORT GPIOE
#define LED1_GPIO_PIN GPIO_PIN_5
#define LED1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口时钟使能 *//******************************************************************************************/
/* LED端口定义 */
#define LED0(x) do{ x ? \HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_SET) : \HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_RESET); \}while(0) /* LED0翻转 */#define LED1(x) do{ x ? \HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_SET) : \HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_RESET); \}while(0) /* LED1翻转 *//* LED取反定义 */
#define LED0_TOGGLE() do{ HAL_GPIO_TogglePin(LED0_GPIO_PORT, LED0_GPIO_PIN); }while(0) /* 翻转LED0 */
#define LED1_TOGGLE() do{ HAL_GPIO_TogglePin(LED1_GPIO_PORT, LED1_GPIO_PIN); }while(0) /* 翻转LED1 *//******************************************************************************************/
/* 外部接口函数*/
void led_init(void); /* 初始化 */#endif
led.c
#include "./BSP/LED/led.h"/*** @brief 初始化LED相关IO口, 并使能时钟* @param 无* @retval 无*/
void led_init(void)
{GPIO_InitTypeDef gpio_init_struct;LED0_GPIO_CLK_ENABLE(); /* LED0时钟使能 */LED1_GPIO_CLK_ENABLE(); /* LED1时钟使能 */gpio_init_struct.Pin = LED0_GPIO_PIN; /* LED0引脚 */gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */HAL_GPIO_Init(LED0_GPIO_PORT, &gpio_init_struct); /* 初始化LED0引脚 */gpio_init_struct.Pin = LED1_GPIO_PIN; /* LED1引脚 */HAL_GPIO_Init(LED1_GPIO_PORT, &gpio_init_struct); /* 初始化LED1引脚 */LED0(1); /* 关闭 LED0 */LED1(1); /* 关闭 LED1 */
}
mian.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/delay/delay.h"
#include "./SYSTEM/usart/usart.h"
#include "./BSP/LED/led.h"int main(void)
{HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟,72M */delay_init(72); /* 初始化延时函数 */led_init(); /* 初始化LED */while(1){LED0(0); /* LED0 亮 */LED1(1); /* LED1 灭 */delay_ms(500);LED0(1); /* LED0 灭 */LED1(0); /* LED1 亮 */delay_ms(500);}
}
按键控制灯的亮灭
按键
key.h
#ifndef __KEY_H
#define __KEY_H#include "./SYSTEM/sys/sys.h"/******************************************************************************************/
/* 引脚 定义 */#define KEY0_GPIO_PORT GPIOE
#define KEY0_GPIO_PIN GPIO_PIN_4
#define KEY0_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口时钟使能 */#define KEY1_GPIO_PORT GPIOE
#define KEY1_GPIO_PIN GPIO_PIN_3
#define KEY1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口时钟使能 */#define KEY2_GPIO_PORT GPIOE
#define KEY2_GPIO_PIN GPIO_PIN_2
#define KEY2_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0) /* PE口时钟使能 */#define WKUP_GPIO_PORT GPIOA
#define WKUP_GPIO_PIN GPIO_PIN_0
#define WKUP_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 *//******************************************************************************************/#define KEY0 HAL_GPIO_ReadPin(KEY0_GPIO_PORT, KEY0_GPIO_PIN) /* 读取KEY0引脚 */
#define KEY1 HAL_GPIO_ReadPin(KEY1_GPIO_PORT, KEY1_GPIO_PIN) /* 读取KEY1引脚 */
#define KEY2 HAL_GPIO_ReadPin(KEY2_GPIO_PORT, KEY2_GPIO_PIN) /* 读取KEY2引脚 */
#define WK_UP HAL_GPIO_ReadPin(WKUP_GPIO_PORT, WKUP_GPIO_PIN) /* 读取WKUP引脚 */#define KEY0_PRES 1 /* KEY0按下 */
#define KEY1_PRES 2 /* KEY1按下 */
#define KEY2_PRES 3 /* KEY2按下 */
#define WKUP_PRES 4 /* KEY_UP按下(即WK_UP) */void key_init(void); /* 按键初始化函数 */
uint8_t key_scan(uint8_t mode); /* 按键扫描函数 */#endif
key.c
#include "./BSP/KEY/key.h"
#include "./SYSTEM/delay/delay.h"/*** @brief 按键初始化函数* @param 无* @retval 无*/
void key_init(void)
{GPIO_InitTypeDef gpio_init_struct;KEY0_GPIO_CLK_ENABLE(); /* KEY0时钟使能 */KEY1_GPIO_CLK_ENABLE(); /* KEY1时钟使能 */KEY2_GPIO_CLK_ENABLE(); /* KEY2时钟使能 */WKUP_GPIO_CLK_ENABLE(); /* WKUP时钟使能 */gpio_init_struct.Pin = KEY0_GPIO_PIN; /* KEY0引脚 */gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */HAL_GPIO_Init(KEY0_GPIO_PORT, &gpio_init_struct); /* KEY0引脚模式设置,上拉输入 */gpio_init_struct.Pin = KEY1_GPIO_PIN; /* KEY1引脚 */gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */HAL_GPIO_Init(KEY1_GPIO_PORT, &gpio_init_struct); /* KEY1引脚模式设置,上拉输入 */gpio_init_struct.Pin = KEY2_GPIO_PIN; /* KEY2引脚 */gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */HAL_GPIO_Init(KEY2_GPIO_PORT, &gpio_init_struct); /* KEY2引脚模式设置,上拉输入 */gpio_init_struct.Pin = WKUP_GPIO_PIN; /* WKUP引脚 */gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */gpio_init_struct.Pull = GPIO_PULLDOWN; /* 下拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */HAL_GPIO_Init(WKUP_GPIO_PORT, &gpio_init_struct); /* WKUP引脚模式设置,下拉输入 */}/*** @brief 按键扫描函数* @note 该函数有响应优先级(同时按下多个按键): WK_UP > KEY2 > KEY1 > KEY0!!* @param mode:0 / 1, 具体含义如下:* @arg 0, 不支持连续按(当按键按下不放时, 只有第一次调用会返回键值,* 必须松开以后, 再次按下才会返回其他键值)* @arg 1, 支持连续按(当按键按下不放时, 每次调用该函数都会返回键值)* @retval 键值, 定义如下:* KEY0_PRES, 1, KEY0按下* KEY1_PRES, 2, KEY1按下* KEY2_PRES, 3, KEY2按下* WKUP_PRES, 4, WKUP按下*/
uint8_t key_scan(uint8_t mode)
{static uint8_t key_up = 1; /* 按键按松开标志 */uint8_t keyval = 0;if (mode) key_up = 1; /* 支持连按 */if (key_up && (KEY0 == 0 || KEY1 == 0 || KEY2 == 0 || WK_UP == 1)) /* 按键松开标志为1, 且有任意一个按键按下了 */{delay_ms(10); /* 去抖动 */key_up = 0;if (KEY0 == 0) keyval = KEY0_PRES;if (KEY1 == 0) keyval = KEY1_PRES;if (KEY2 == 0) keyval = KEY2_PRES;if (WK_UP == 1) keyval = WKUP_PRES;}else if (KEY0 == 1 && KEY1 == 1 && KEY2 == 1 && WK_UP == 0) /* 没有任何按键按下, 标记按键松开 */{key_up = 1;}return keyval; /* 返回键值 */
}
蜂鸣器
beep.h
#ifndef __BEEP_H
#define __BEEP_H#include "./SYSTEM/sys/sys.h"/******************************************************************************************/
/* 引脚 定义 */#define BEEP_GPIO_PORT GPIOB
#define BEEP_GPIO_PIN GPIO_PIN_8
#define BEEP_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PB口时钟使能 *//******************************************************************************************//* 蜂鸣器控制 */
#define BEEP(x) do{ x ? \HAL_GPIO_WritePin(BEEP_GPIO_PORT, BEEP_GPIO_PIN, GPIO_PIN_SET) : \HAL_GPIO_WritePin(BEEP_GPIO_PORT, BEEP_GPIO_PIN, GPIO_PIN_RESET); \}while(0)/* BEEP状态翻转 */
#define BEEP_TOGGLE() do{ HAL_GPIO_TogglePin(BEEP_GPIO_PORT, BEEP_GPIO_PIN); }while(0) /* BEEP = !BEEP */void beep_init(void); /* 初始化蜂鸣器 */#endif
beep.c
#include "./BSP/BEEP/beep.h"/*** @brief 初始化BEEP相关IO口, 并使能时钟* @param 无* @retval 无*/
void beep_init(void)
{GPIO_InitTypeDef gpio_init_struct;BEEP_GPIO_CLK_ENABLE(); /* BEEP时钟使能 */gpio_init_struct.Pin = BEEP_GPIO_PIN; /* 蜂鸣器引脚 */gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */HAL_GPIO_Init(BEEP_GPIO_PORT, &gpio_init_struct); /* 初始化蜂鸣器引脚 */BEEP(0); /* 关闭蜂鸣器 */
}
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/BEEP/beep.h"
#include "./BSP/KEY/key.h"int main(void)
{uint8_t key;HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72); /* 延时初始化 */led_init(); /* 初始化LED */key_init(); /* 初始化按键 */beep_init(); /* 初始化蜂鸣器 */LED0(0); /* 先点亮LED0 */while(1){key = key_scan(0); /* 得到键值 */if (key){switch (key){case WKUP_PRES: /* 控制蜂鸣器 */BEEP_TOGGLE(); /* BEEP状态取反 */break;case KEY2_PRES: /* 控制LED0(RED)翻转 */LED0_TOGGLE(); /* LED0状态取反 */break;case KEY1_PRES: /* 控制LED1(GREEN)翻转 */LED1_TOGGLE(); /* LED1状态取反 */break;case KEY0_PRES: /* 同时控制LED0, LED1翻转 */LED0_TOGGLE(); /* LED0状态取反 */LED1_TOGGLE(); /* LED1状态取反 */break;}}else{delay_ms(10);}}
}