【STM32】HAL库中的实现(二):串口(USART)/看门狗(IWDG/WWDG)/定时器(TIM)

承接上文:【STM32】HAL库中的实现(一)GPIO/SysTick/EXTI

这些模块是实际开发中用到最多的功能,这个系列的文章是帮助你熟悉其 HAL 封装及底层机制。

HAL库中的实现 串口(USART)

配置USART:

配置好后,直接生成工程代码:
stm32f1xx_it.c新增:

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中配置变量:

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 */
c 复制代码
/* 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

c 复制代码
/* USER CODE BEGIN Private defines */

#define U1RxDataSize 128


extern uint8_t 	 U1RxData[U1RxDataSize] ;
extern uint16_t  U1RxLen ;
extern uint8_t   U1RxBuf[1] ;

extern bool U1RxFlag;

/* USER CODE END Private defines */

main.c

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 常用函数:

c 复制代码
HAL_UART_Transmit(&huart1, data, len, timeout);
HAL_UART_Receive_IT(&huart1, buffer, len);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
HAL 内部机制
  1. 初始化:

MX_USART1_UART_Init() 设置波特率、数据位、停止位、校验位等。

  1. 发送:
c 复制代码
HAL_UART_Transmit()
↓
轮询状态寄存器 TXE(发送缓冲区空)和 TC(发送完成)
  1. 接收(中断):
c 复制代码
HAL_UART_Receive_IT()
↓
开启 RXNE 中断:使能 USART_CR1_RXNEIE
  1. 中断处理:
c 复制代码
void USART1_IRQHandler(void)
{
    HAL_UART_IRQHandler(&huart1);
}
  1. 回调函数:
c 复制代码
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    // 接收到数据后自动调用
}

HAL库中的实现 看门狗(IWDG/WWDG)

配置看门狗的预分频值:

配置好后,直接生成工程代码:
stm32f1xx_it.c新增:

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中新增:

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 窗口看门狗 有窗口限制,适合检测任务卡死
  1. 独立看门狗(IWDG)
c 复制代码
HAL_IWDG_Start(&hiwdg);             // 启动看门狗
HAL_IWDG_Refresh(&hiwdg);           // 喂狗
复制代码
🔍 实现原理
启动后不可关闭
使用内部 40kHz LSI 时钟
计数器从 Reload 值递减至 0 后复位

HAL库代码部分:

c 复制代码
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);
  1. 窗口看门狗(WWDG)
c 复制代码
HAL_WWDG_Start(&hwwdg);
HAL_WWDG_Refresh(&hwwdg);      // 必须在窗口内喂狗

WWDG 具有一个"允许喂狗"的时间窗口,早或晚都将导致复位。
适合检测"程序卡死"或"提前喂狗"的情况。


HAL库中的实现 定时器(TIM)

配置好后,直接生成工程代码:
stm32f1xx_it.c新增:

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新增:

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 常用函数:

c 复制代码
HAL_TIM_Base_Start(&htim3);           // 启动定时器(无中断)
HAL_TIM_Base_Start_IT(&htim3);        // 启动定时器(带中断)
HAL_TIM_PeriodElapsedCallback();      // 中断回调
HAL 实现关键流程
  1. 初始化函数:
c 复制代码
HAL_TIM_Base_Init(&htim3);
复制代码
内部设置:
	预分频器 PSC
	自动重装载值 ARR
	时钟分频 ClockDivision
  1. 启动函数:
c 复制代码
HAL_TIM_Base_Start_IT()
↓
__HAL_TIM_ENABLE_IT(htim, TIM_IT_UPDATE);    // 开启更新中断
__HAL_TIM_ENABLE(htim);                      // 使能定时器
  1. 中断处理:
c 复制代码
void TIM3_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&htim3);
}
  1. 用户回调:
c 复制代码
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM3)
    {
        // 1秒触发一次
    }
}

综上。深入 STM32 HAL 库的核心模块 串口(USART)/看门狗(IWDG/WWDG)/定时器(TIM),大大简化了 STM32 的开发流程,但也增加了代码体积和抽象层级。文章帮助你理解 HAL 的实现原理,有利于更加高效地调试、优化和移植项目。(我提供的代码不要完整照抄,仅仅提供HAL库实现的思路流程,希望对你产生帮助。)

以上,欢迎有从事同行业的电子信息工程、互联网通信、嵌入式开发的朋友共同探讨与提问,我可以提供实战演示或模板库。希望内容能够对你产生帮助!

相关推荐
QQ52862112415 分钟前
STM32F2/F4系列单片机解密和芯片应用介绍
stm32·单片机·嵌入式硬件·程序·pcb抄板
2006yu2 小时前
从零开始学习单片机17
单片机·嵌入式硬件·学习
智能物联实验室2 小时前
如何解决网关断网后时间不再统计的问题?无RTC子设备如何打通主网关的时间同步功能?
嵌入式硬件·网关·实时音视频·智能硬件
kaikaile19954 小时前
GY-BMP280压强传感器完整工程stm32控制
stm32·单片机·嵌入式硬件
智能物联实验室5 小时前
屏随人动+视觉魔方+多样主题+智能留言,涂鸦Wukong AI 2.0助力打造爆款带屏云台相机
图像处理·人工智能·嵌入式硬件·数码相机·智能硬件
GodKK老神灭6 小时前
STM32 定时器(互补输出+刹车)
stm32·单片机·嵌入式硬件
lingzhilab9 小时前
零知开源——基于STM32F407VET6实现ULN2003AN驱动28BYJ-48步进电机控制系统
stm32·单片机·嵌入式硬件
JasmineX-110 小时前
直流电机驱动与TB6612
c语言·stm32·单片机·嵌入式硬件
hahaha601620 小时前
模拟电路中什么时候适合使用电流传递信号,什么时候合适使用电压传递信号
stm32·单片机·嵌入式硬件
小小少年1231 天前
基于蓝牙的stm32智能火灾烟雾报警系统设计
stm32·单片机·嵌入式硬件