【STM32】HAL库中的实现(二):串口(USART)/看门狗(IWDG/WWDG)/定时器(TIM)
承接上文:【STM32】HAL库中的实现(一)GPIO/SysTick/EXTI
这些模块是实际开发中用到最多的功能,这个系列的文章是帮助你熟悉其 HAL 封装及底层机制。
HAL库中的实现 串口(USART)
配置USART:
配置好后,直接生成工程代码:
stm32f1xx_it.c
新增:
/* USER CODE BEGIN 1 */void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{if(GPIO_Pin == PA0_Key_Pin){if( HAL_GPIO_ReadPin( PA0_Key_GPIO_Port, PA0_Key_Pin) == GPIO_PIN_RESET){HAL_GPIO_TogglePin( LED_G_GPIO_Port, LED_G_Pin );}}
}void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{U1RxLen = Size;HAL_UARTEx_ReceiveToIdle_IT( &huart1 , U1RxData, U1RxDataSize);U1RxFlag = 1;
}/* USER CODE END 1 */
usart.c
中配置变量:
/* USER CODE BEGIN 0 */uint8_t U1RxData[U1RxDataSize] = {0};
uint16_t U1RxLen = 0;
uint8_t U1RxBuf[1] = {0};bool U1RxFlag = 0;
/* USER CODE END 0 */
/* USER CODE BEGIN 1 *///标准库下重定向c库函数printf到串口,并且关闭半主机模式
/*注意此代码我是封装在USART.c模块中*/
#if 1
#pragma import(__use_no_semihosting)
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x) //这里有的版本没有void会导致错误
{ x = x;
}
//标准库需要的支持函数
struct __FILE
{ int handle;
};
FILE __stdout;//重定向fputc函数
int fputc(int ch, FILE *f)
{ while((USART1->SR&0X40)==0){} //循环发送,直到发送完毕//操作寄存器方式 USART1->DR = (uint8_t) ch; return ch;
}
#endif/* USER CODE END 1 */
usart.h
/* USER CODE BEGIN Private defines */#define U1RxDataSize 128extern uint8_t U1RxData[U1RxDataSize] ;
extern uint16_t U1RxLen ;
extern uint8_t U1RxBuf[1] ;extern bool U1RxFlag;/* USER CODE END Private defines */
main.c
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */HAL_UARTEx_ReceiveToIdle_IT( &huart1 , U1RxData, U1RxDataSize);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */HAL_UART_Transmit( &huart1, U1SendData, sizeof(U1SendData), 0xff ); //发送数据while (1){if(U1RxFlag == 1){printf(" U1RxLen = %d \r\n",U1RxLen);HAL_UART_Transmit( &huart1, U1RxData, U1RxLen, 0xff );if(strcmp((char*)U1RxData,"绿灯亮") == 0){HAL_GPIO_WritePin(GPIOC, LED_G_Pin, GPIO_PIN_RESET);}if(strcmp((char*)U1RxData,"绿灯灭") == 0){HAL_GPIO_WritePin(GPIOC, LED_G_Pin, GPIO_PIN_SET);}memset(U1RxData,0,U1RxDataSize);U1RxFlag = 0;}/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
USART(串口)HAL实现思路
✅ HAL 常用函数:
HAL_UART_Transmit(&huart1, data, len, timeout);
HAL_UART_Receive_IT(&huart1, buffer, len);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
HAL 内部机制
- 初始化:
MX_USART1_UART_Init()
设置波特率、数据位、停止位、校验位等。
- 发送:
HAL_UART_Transmit()
↓
轮询状态寄存器 TXE(发送缓冲区空)和 TC(发送完成)
- 接收(中断):
HAL_UART_Receive_IT()
↓
开启 RXNE 中断:使能 USART_CR1_RXNEIE
- 中断处理:
void USART1_IRQHandler(void)
{HAL_UART_IRQHandler(&huart1);
}
- 回调函数:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{// 接收到数据后自动调用
}
HAL库中的实现 看门狗(IWDG/WWDG)
配置看门狗的预分频值:
配置好后,直接生成工程代码:
stm32f1xx_it.c
新增:
/*** @brief This function handles EXTI line0 interrupt.*/
void EXTI0_IRQHandler(void)
{/* USER CODE BEGIN EXTI0_IRQn 0 *//* USER CODE END EXTI0_IRQn 0 */HAL_GPIO_EXTI_IRQHandler(PA0_Key_Pin);/* USER CODE BEGIN EXTI0_IRQn 1 *//* USER CODE END EXTI0_IRQn 1 */
}/*** @brief This function handles USART1 global interrupt.*/
void USART1_IRQHandler(void)
{/* USER CODE BEGIN USART1_IRQn 0 *//* USER CODE END USART1_IRQn 0 */HAL_UART_IRQHandler(&huart1);/* USER CODE BEGIN USART1_IRQn 1 *//* USER CODE END USART1_IRQn 1 */
}/* USER CODE BEGIN 1 */void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
// static uint32_t Count = 0; //看门狗计数器if(GPIO_Pin == PA0_Key_Pin){if( HAL_GPIO_ReadPin(PA0_Key_GPIO_Port, PA0_Key_Pin) == GPIO_PIN_RESET){HAL_GPIO_TogglePin(LED_R_GPIO_Port, LED_R_Pin); //绿灯的状态翻转
// HAL_IWDG_Refresh(&hiwdg); //进行喂狗
// printf("Iwdg Count = %d \r\n", Count++); //打印喂狗次数}}
}void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{U1RxLen = Size;HAL_UARTEx_ReceiveToIdle_IT(&huart1, U1RxData, U1RxDataSize); //启动串口空闲中断的接收U1RxFlag = 1;
}/* USER CODE END 1 */
main.c
中新增:
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "iwdg.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "string.h"/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
uint8_t U1SendData[] = {"hello world!"};
/* USER CODE END PTD *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();MX_IWDG_Init();/* USER CODE BEGIN 2 */HAL_UARTEx_ReceiveToIdle_IT(&huart1, U1RxData, U1RxDataSize); //使能空闲中断/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */HAL_UART_Transmit(&huart1, U1SendData, sizeof(U1SendData), 0xff); //启动串口空闲中断的发送while (1){HAL_IWDG_Refresh(&hiwdg); //进行喂狗if(U1RxFlag == 1) //代表串口已经接收到数据{printf("U1RxLen = %d \r\n", U1RxLen);HAL_UART_Transmit(&huart1, U1RxData, U1RxLen, 0xff); //启动串口空闲中断的发送if(strcmp((char*)U1RxData,"LED_ON") == 0){HAL_GPIO_WritePin(GPIOC, LED_G_Pin, GPIO_PIN_RESET);}if(strcmp((char*)U1RxData,"LED_OFF") == 0){HAL_GPIO_WritePin(GPIOC, LED_G_Pin, GPIO_PIN_SET);}memset(U1RxData,0,U1RxDataSize);U1RxFlag = 0;}/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
HAL库中的看门狗(IWDG / WWDG)
STM32 提供两种看门狗:
IWDG | 独立看门狗 | 独立于主时钟,掉电仍工作,安全性高 |
---|---|---|
WWDG | 窗口看门狗 | 有窗口限制,适合检测任务卡死 |
- 独立看门狗(IWDG)
HAL_IWDG_Start(&hiwdg); // 启动看门狗
HAL_IWDG_Refresh(&hiwdg); // 喂狗
🔍 实现原理
启动后不可关闭
使用内部 40kHz LSI 时钟
计数器从 Reload 值递减至 0 后复位
HAL库代码部分:
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_64;
hiwdg.Init.Reload = 1000;
HAL_IWDG_Init(&hiwdg);
HAL_IWDG_Start(&hiwdg);// 主循环中周期性喂狗
HAL_IWDG_Refresh(&hiwdg);
- 窗口看门狗(WWDG)
HAL_WWDG_Start(&hwwdg);
HAL_WWDG_Refresh(&hwwdg); // 必须在窗口内喂狗
WWDG 具有一个“允许喂狗”的时间窗口,早或晚都将导致复位。
适合检测“程序卡死”或“提前喂狗”的情况。
HAL库中的实现 定时器(TIM)
配置好后,直接生成工程代码:
stm32f1xx_it.c
新增:
/*******************************************************/
/*** @brief This function handles EXTI line0 interrupt.*/
void EXTI0_IRQHandler(void)
{/* USER CODE BEGIN EXTI0_IRQn 0 *//* USER CODE END EXTI0_IRQn 0 */HAL_GPIO_EXTI_IRQHandler(PA0_Key_Pin);/* USER CODE BEGIN EXTI0_IRQn 1 *//* USER CODE END EXTI0_IRQn 1 */
}/*** @brief This function handles TIM3 global interrupt.*/
void TIM3_IRQHandler(void)
{/* USER CODE BEGIN TIM3_IRQn 0 */static uint32_t Count = 0; //直接获取寄存器标志位的方式来产生中断if( __HAL_TIM_GET_FLAG( &htim3, TIM_FLAG_UPDATE) == SET ){__HAL_TIM_CLEAR_FLAG( &htim3, TIM_FLAG_UPDATE); //清除中断源HAL_GPIO_TogglePin(LED_G_GPIO_Port, LED_G_Pin); //绿灯的状态翻转HAL_IWDG_Refresh(&hiwdg); //进行喂狗printf("Iwdg Count = %d \r\n", Count++);}/* USER CODE END TIM3_IRQn 0 */
// HAL_TIM_IRQHandler(&htim3);/* USER CODE BEGIN TIM3_IRQn 1 *//* USER CODE END TIM3_IRQn 1 */
}/*** @brief This function handles USART1 global interrupt.*/
void USART1_IRQHandler(void)
{/* USER CODE BEGIN USART1_IRQn 0 *//* USER CODE END USART1_IRQn 0 */HAL_UART_IRQHandler(&huart1);/* USER CODE BEGIN USART1_IRQn 1 *//* USER CODE END USART1_IRQn 1 */
}/* USER CODE BEGIN 1 */void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{if(GPIO_Pin == PA0_Key_Pin){if( HAL_GPIO_ReadPin(PA0_Key_GPIO_Port, PA0_Key_Pin) == GPIO_PIN_RESET){HAL_GPIO_TogglePin(LED_R_GPIO_Port, LED_R_Pin); //红灯的状态翻转
// HAL_IWDG_Refresh(&hiwdg); //进行喂狗
// printf("Iwdg Count = %d \r\n", Count++);}}
}void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{U1RxLen = Size;HAL_UARTEx_ReceiveToIdle_IT(&huart1, U1RxData, U1RxDataSize); //启动串口空闲中断的接收U1RxFlag = 1;
}
main.c
新增:
/*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();MX_IWDG_Init();MX_TIM3_Init();/* USER CODE BEGIN 2 */HAL_UARTEx_ReceiveToIdle_IT(&huart1, U1RxData, U1RxDataSize); //使能空闲中断//定时器初始化完毕之后,找到对应定时器的启动函数HAL_TIM_Base_Start_IT(&htim3);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */HAL_UART_Transmit(&huart1, U1SendData, sizeof(U1SendData), 0xff); //启动串口空闲中断的发送while (1){if(U1RxFlag == 1) //代表串口已经接收到数据{printf("U1RxLen = %d \r\n", U1RxLen);HAL_UART_Transmit(&huart1, U1RxData, U1RxLen, 0xff); //启动串口空闲中断的发送if(strcmp((char*)U1RxData,"LED_ON") == 0){HAL_GPIO_WritePin(GPIOC, LED_G_Pin, GPIO_PIN_RESET);}if(strcmp((char*)U1RxData,"LED_OFF") == 0){HAL_GPIO_WritePin(GPIOC, LED_G_Pin, GPIO_PIN_SET);}memset(U1RxData,0,U1RxDataSize);U1RxFlag = 0;}/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
HAL库中的定时器(TIM)
HAL 常用函数:
HAL_TIM_Base_Start(&htim3); // 启动定时器(无中断)
HAL_TIM_Base_Start_IT(&htim3); // 启动定时器(带中断)
HAL_TIM_PeriodElapsedCallback(); // 中断回调
HAL 实现关键流程
- 初始化函数:
HAL_TIM_Base_Init(&htim3);
内部设置:预分频器 PSC自动重装载值 ARR时钟分频 ClockDivision
- 启动函数:
HAL_TIM_Base_Start_IT()
↓
__HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE); // 开启更新中断
__HAL_TIM_ENABLE(htim); // 使能定时器
- 中断处理:
void TIM3_IRQHandler(void)
{HAL_TIM_IRQHandler(&htim3);
}
- 用户回调:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if (htim->Instance == TIM3){// 1秒触发一次}
}
综上。深入 STM32 HAL 库的核心模块 串口(USART)/看门狗(IWDG/WWDG)/定时器(TIM),大大简化了 STM32 的开发流程,但也增加了代码体积和抽象层级。文章帮助你理解 HAL 的实现原理,有利于更加高效地调试、优化和移植项目。(我提供的代码不要完整照抄,仅仅提供HAL库实现的思路流程,希望对你产生帮助。)
以上,欢迎有从事同行业的电子信息工程、互联网通信、嵌入式开发的朋友共同探讨与提问,我可以提供实战演示或模板库。希望内容能够对你产生帮助!