功能实现:
- 上电后,周期发送hello world 给串口调试助手
- 通过串口调试助手发送字符串"1" 开启LED灯,发送字符串"0" 关闭LED灯
准备工作:
- 1、STM32开发板(我的是STM32F103VET6)
- 2、STM32CubeMx软件、 IDE: Keil软件
- 3、按键管脚 : PA0 PC13
- 4、LED管脚 : PB0 PB1 PB5
新建工程
1.1 New Project
- 1 搜索芯片型号
- 2选择芯片
- 3创建工程
1.2设置时钟源RCC
- 系统时钟源选择外部晶振时钟源
1.3 设置GPIO
-
设置PB0和PB1为输出模式
串口设置
使能串口中断
设置时钟
项目文件设置
- 1 设置项目名称
- 2 选择所用IDE
生成代码
- 为每个功能生成独立的.c和.h文件
- 生成代码
代码部分
HAL库UART函数库介绍
UART结构体定义
UART_HandleTypeDef huart1;
UART的名称定义,这个结构体中存放了UART所有用到的配置。
在usart.c文件中定义了结构体的实例huart1,同时对结构体初始化
UART_HandleTypeDef huart1;
/* USART1 init function */
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
与串口相关的常用的函数,找到stm32f1xx_hal_uart.h文件
- HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
- HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
- void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
- void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
串口发送/接收数据,一般是发送数据采用阻塞式方式,也就是直接发送,二接收数据采用非阻塞式,就是中断方式
下面简单介绍每个函数
串口发送函数:
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
功能:串口发送指定长度的数据。如果超时没发送完成,则不再发送,返回超时标志(HAL_TIMEOUT)。
参数:
- UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
- *pData 需要发送的数据
- Size 发送的字节数
- Timeout 最大发送时间,单位ms,发送数据超过该时间退出发送
举例:
uint8_t send_data[] = "hello world\r\n";
HAL_UART_Transmit(&huart1, send_data , sizeof(send_data), 1000);
中断接收函数:
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
功能:串口中断接收,以中断方式接收指定长度数据。
大致过程是,设置数据存放位置,接收数据长度,然后使能串口接收中断。接收到数据时,会触发串口中断。
再然后,串口中断函数处理,直到接收到指定长度数据,而后关闭中断,进入中断接收回调函数,不再触发接收中断。(只触发一次中断)
参数:
- UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
- *pData 接收到的数据存放地址
- Size 接收的字节数
举例:
uint8_t receive_data;
HAL_UART_Receive_IT(&huart1, &receive_data, 1);
注意:此函数需在main函数的初始化开启中断接收,如果一直接收数据,必须在回调函数中再调用一次
串口中断处理函数
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
功能:如果串口发生中断,则直接进入此函数,具体的接收数据的处理,一般不写在此函数中,此函数可以写一些调试的代码,判断串口是否进入此中断
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 */
}
串口接收中断回调函数:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
功能:HAL库的中断进行完之后,并不会直接退出,而是会进入中断回调函数中,用户可以在此函数中处理相关的数据,
此函数为虚函数,需要用户重写该函数,一般在main.c文件中重写该函数。
举例:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1){
if(receive_data=='1'){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, 0);
}
else if(receive_data=='0'){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, 1);
}
else{
//HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
}
}
HAL_UART_Receive_IT(&huart1, &receive_data, 1);
}
main.c函数具体代码
/* USER CODE BEGIN 0 */
uint8_t send_data[] = "hello world\r\n";
uint8_t receive_data;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1){
if(receive_data=='1'){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, 0);
}
else if(receive_data=='0'){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, 1);
}
else{
//HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
}
}
HAL_UART_Receive_IT(&huart1, &receive_data, 1);
}
/* USER CODE END 0 */
/**
* @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();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1, &receive_data, 1); //在这里面一定不要忘记开启串口中断
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
HAL_UART_Transmit(&huart1, send_data , sizeof(send_data), 1000);
HAL_Delay(1000);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
总结
串口操作的具体流程:
- 初始化串口
- 如果是发送操作直接在无限循环中调用串口发送函数
- 如果是接收数据操作,则现在main函数的初始化中调用串口接收中断函数。
- 进入中断后,接收完数据 进入中断回调函数
- 重写中断回调函数,并且在此函数中处理接收的数据
- 处理完数据,一定要在回调函数中再调用一次HAL_UART_Receive_IT函数,使得程序可以重新触发接收中断