基于野火霸天虎STM32F407ZGT6开发板
- 一、USART功能框图
- 二、编程要求
- 三、使用STM32CubeMX生成初始化函数体
- 四、使用Keil5编写实现函数
- 五、实验现象
- 六、改进用中断方式收发数据
- 七、改进用DMA方式收发数据
一、USART功能框图

- USART简介
USART数据寄存器(USART_DR)由两个寄存器组成,一个只用于发送数据的可写寄存器TDR,一个只用于接受数据的可读寄存器RDR。当进行发送操作时,写入USART_DR的数据会自动存储到TDR中;当进行读取操作时,USART_DR 会自动获取RDR中的数据。
- 几个发送数据比较重要的标志位
名称 |
描述 |
TE |
发送使能 |
TXE |
发送寄存器为空,发送单字节的时候使用 |
TC |
发送完成,发送多个字节数据的时候使用 |
TXIE |
发送完成中断使能 |
名称 |
描述 |
RE |
接收使能 |
RXNE |
读数据寄存器非空 |
RXNEIE |
发送完成中断使能 |
二、编程要求
- 将串口指向USART1,重定义printf函数;
- 串口配置设置波特率为115200 Bits/s。传输数据长度为8 Bit。奇偶检验无,停止位1;
- 数据发送到串口助手,传输间隔为一秒;
三、使用STM32CubeMX生成初始化函数体
- 打开软件

- 选择芯片

- 配置Debug方式

- 配置RCC的高速时钟HSE为陶瓷晶振

- 配置如图所示时钟树

- 打开串口(USART1)

- 配置参数

- 生成代码

四、使用Keil5编写实现函数
- 系统时钟初始化函数(由STM32CubeMX生成)
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
}
- USART1初始化函数(由STM32CubeMX生成)
#include "usart.h"
UART_HandleTypeDef huart1;
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();
}
}
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(uartHandle->Instance==USART1)
{
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = TXD1_Pin|RXD1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
}
void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{
if(uartHandle->Instance==USART1)
{
__HAL_RCC_USART1_CLK_DISABLE();
HAL_GPIO_DeInit(GPIOA, TXD1_Pin|RXD1_Pin);
}
}
- 重定向串口(添加一个int fputc(int ch, FILE *f)函数)
记得添加标准输入输出头文件 #include < stdio.h >,否则会报错 identifier “FILE” is undefined
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
return (ch);
}
while (1)
{
printf("Ich studiere Deutsch...\r\n");
HAL_Delay(1000);
}
五、实验现象

上面用到的轮询的模式。CPU不断查询IO设备,如设备有请求则加以处理。例如CPU不断查询串口是否传输完成,如传输超过则返回超时错误。轮询方式会占用CPU处理时间,效率较低。
六、改进用中断方式收发数据
- 双击.ioc文件,打开工程,修改配置参数


- 生成代码

- 在主文件中定义接收中断缓存区
uint8_t aRxBuffer[20];
extern uint8_t aRxBuffer[20];
HAL_UART_Receive_IT(&huart1,aRxBuffer,10);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
UNUSED(huart);
HAL_UART_Transmit(&huart1,aRxBuffer,10,1000);
}
- 在中断函数中添加中断接受使能函数,重新使能接受中断
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart1);
HAL_UART_Receive_IT(&huart1,aRxBuffer,10);
}
- 实验现象

说明:由 HAL_UART_Receive_IT(&huart1,aRxBuffer,10) 可以知道,当接收10个数据后,触发接收中断,当接收的数据不足十个时,下次接收的数据自动补足上次空缺的数据,满十个触发中断,调用接收完成回调函数,现象如下(发送两次数据才触发中断),第二次接收自动补足第一次数据,满十个再发送。

七、改进用DMA方式收发数据
- 双击.ioc文件,打开工程,修改配置参数

- 配置DMA2,模式为单次传输,数据宽度为字节,外设地址不递增,存储器地址递增

- 生成代码

- 在主文件中定义接收中断缓存区
uint8_t aRxBuffer[20];
extern uint8_t aRxBuffer[20];
HAL_UART_Receive_DMA(&huart1,aRxBuffer,10);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
UNUSED(huart);
HAL_UART_Transmit_DMA(&huart1,aRxBuffer,10);
}
- 在中断函数中添加中断接受使能函数,重新使能接受中断
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart1);
HAL_UART_Receive_DMA(&huart1,aRxBuffer,10);
}
- 实验现象

说明:由 HAL_UART_Receive_DMA(&huart1,aRxBuffer,10) 可以知道,当接收10个数据后,触发接收中断,当接收的数据不足十个时,下次接收的数据自动补足上次空缺的数据,满十个触发中断,调用接收完成回调函数,现象如下(发送两次数据才触发中断),第二次接收自动补足第一次数据,满十个再发送。

蓝色为发送,红色为接收。
–END–