STM32F1与STM32CubeIDE快速入门-USART/UART串口通信

USART/UART串口通信

1、USART介绍

通用同步异步收发器(Universal Synchronous Asynchronous Receiver and Transmitter)是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换。

USART提供了一种灵活的方式,可以与需要行业标准 NRZ 异步串行数据格式的外部设备进行全双工数据交换。 USART使用小数波特率发生器提供非常广泛的波特率范围。

支持同步单向通信和半双工单线通信。 它还支持 LIN(本地互连网络)、智能卡协议和 IrDA(红外数据协会)SIR ENDEC 规范以及调制解调器操作 (CTS/RTS)。 它允许多处理器通信。

在STM32中,还可以通过将DMA用于多缓冲区配置,从而实现高速数据通信。
在这里插入图片描述

STM32F103VET6微控制器有三个 USART 和两个 UART,其中 USART1 和时钟来源于 APB2 总线时钟,其最大频率为 72MHz,其他四个的时钟来源于 APB1 总线时钟,其最大频率为 36MHz。UART 只是异步传输功能,所以没有 SCLK、nCTS 和 nRTS 功能引脚。USART的框图如下:

在这里插入图片描述

STM32F103VE中UART的引脚如下:

在这里插入图片描述

中断控制寄存器如下:

在这里插入图片描述

2、HAL的USART/UART使用步骤介绍

1)USART使用步骤

  • 第一步:声明一个 USART_HandleTypeDef 句柄结构(例如 USART_HandleTypeDef husart)
  • 第二步:通过实现 HAL_USART_MspInit() API 来初始化 USART 底层资源:
    • a)使能 USARTx 接口时钟
    • b)USART 引脚配置:
      • 启用 USART GPIO 的时钟
      • 将 USART 引脚配置为复用功能上拉
    • c)如果需要使用中断处理,则需要配置NVIC,通过函数HAL_USART_Transmit_IT和HAL_USART_TransmitReceive_IT实现
      • 配置 USARTx 中断优先级
      • 启用 NVIC USART IRQ 句柄
    • d)如果需要使用DMA,则通过HAL_USART_Transmit_DMA、HAL_USART_Receive_DMA() 和 HAL_USART_TransmitReceive_DMA()实现:
      • 为 Tx/Rx 通道声明一个 DMA 句柄结构
      • 使能 DMAx 接口时钟
      • 使用所需的 Tx/Rx 参数配置声明的 DMA 句柄结构
      • 配置 DMA Tx/Rx 通道
      • 将初始化的 DMA 句柄关联到 USART DMA Tx/Rx 句柄
      • 为 DMA Tx/Rx 通道上的传输完成中断配置优先级并启用 NVIC
      • 配置 USARTx 中断优先级并启用 NVIC USART IRQ 句柄(用于 DMA 非循环模式下的最后一个字节发送完成检测)
  • 第三步:在 USART_HandleTypeDef实例结构中对波特率、字长、停止位、奇偶校验、硬件流控制和模式(接收器/发送器)进行设置
  • 第四步:通过调用 HAL_USART_Init() 函数初始化 USART 寄存器,
    • 这些 API 还通过调用自定义的 HAL_USART_MspInit(&husart) API 来配置低级硬件 GPIO、时钟、CORTEX 等)

UART与USART的使用步骤类似,在这里就不展开介绍了,请参考HAL参考手册。

3、USART配置

第一步:创建工程

在这里插入图片描述

第二步:选择芯片

在这里插入图片描述

第三步:配置系统时钟

在这里插入图片描述

在这里插入图片描述

第四步:配置USART

1)配置USART模式

在这里插入图片描述

2)配置USART引脚

在这里插入图片描述

3)配置USART中断

在这里插入图片描述

保存配置并生成代码。

4、USART代码生成与功能实现

配置工程生成的关键代码如下:

1)USART引脚初始化

main.c文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
/\*\*
\* @brief GPIO Initialization Function
\* @param None
\* @retval None
\*/
static void MX\_GPIO\_Init(void)
{

/\* GPIO Ports Clock Enable \*/
\_\_HAL\_RCC\_GPIOA\_CLK\_ENABLE();

}

stm32f10x_hal_msp.c文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/\*\*
\* @brief UART MSP Initialization
\* This function configures the hardware resources used in this example
\* @param huart: UART handle pointer
\* @retval None
\*/
void HAL\_UART\_MspInit(UART_HandleTypeDef\* huart)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(huart->Instance==USART1)
{
/\* USER CODE BEGIN USART1\_MspInit 0 \*/

/\* USER CODE END USART1\_MspInit 0 \*/
/\* Peripheral clock enable \*/
\_\_HAL\_RCC\_USART1\_CLK\_ENABLE();

\_\_HAL\_RCC\_GPIOA\_CLK\_ENABLE();
/\*\*USART1 GPIO Configuration
PA9 ------> USART1\_TX
PA10 ------> USART1\_RX
\*/
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL\_GPIO\_Init(GPIOA, &GPIO_InitStruct);

GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL\_GPIO\_Init(GPIOA, &GPIO_InitStruct);

/\* USER CODE BEGIN USART1\_MspInit 1 \*/

/\* USER CODE END USART1\_MspInit 1 \*/
}

}

2)USART配置初始化

main.c文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/\*\*
\* @brief USART1 Initialization Function
\* @param None
\* @retval None
\*/
static void MX\_USART1\_UART\_Init(void)
{

/\* USER CODE BEGIN USART1\_Init 0 \*/

/\* USER CODE END USART1\_Init 0 \*/

/\* USER CODE BEGIN USART1\_Init 1 \*/

/\* USER CODE END USART1\_Init 1 \*/
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();
}
/\* USER CODE BEGIN USART1\_Init 2 \*/

/\* USER CODE END USART1\_Init 2 \*/


3)USART中断初始化及中断处理

main.c文件中:

1
2
3
4
5
6
7
8
9
10
11
/\*\*
\* @brief NVIC Configuration.
\* @retval None
\*/
static void MX\_NVIC\_Init(void)
{
/\* USART1\_IRQn interrupt configuration \*/
HAL\_NVIC\_SetPriority(USART1_IRQn, 0, 0);
HAL\_NVIC\_EnableIRQ(USART1_IRQn);
}

stm32f10x_it.c文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/\*\*
\* @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 \*/
}

函数HAL_UART_IRQHandler处理USARTx的中断请求。HAL_UART_IRQHandler完成后,有调用了如下函数:

  • HAL_UART_TxHalfCpltCallback:半双工发送完成回调
  • HAL_UART_TxCpltCallback:全双工发送完成回调
  • HAL_UART_RxHalfCpltCallback:半双工接收完成回调
  • HAL_UART_RxCpltCallback:全双工接收完成回调
  • HAL_UART_ErrorCallback:错误回调
  • HAL_UART_TxRxCpltCallback:接收发送完成回调

注意:这些函数运行于非阻塞模式。

在本次实例中,通过HAL_UART_RxCpltCallback函数实现一个串口回显功能。在stm32f10x_it.c文件中添加代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
/\* USER CODE BEGIN 1 \*/
void HAL\_UART\_RxCpltCallback(UART_HandleTypeDef \*huart) {
if (huart->Instance == USART1) {
// 发送数据,实现Echo
HAL\_UART\_Transmit(&huart1, (uint8_t\*) RX_Buffer, 1, 0xffff);
// 重新启动接收中断
HAL\_UART\_Receive\_IT(&huart1, (uint8_t\*) RX_Buffer, 1);
}

}
/\* USER CODE END 1 \*/

4)重定向

有时,为了方便调用stdio.h标准C库中的printf函数,就需要进行输出重定向。由于STM32Cube IDE使用的编译器是arm-noneabi-gcc,所以需要重写int _write(int fd, char *ptr, int len)函数。在main.c文件中添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/\* USER CODE BEGIN 4 \*/
// 重定向
/\*
\* To implement the STDIO functions you need to create
\* the \_read and \_write functions and hook them to the
\* USART you are using. This example also has a buffered
\* read function for basic line editing.
\*/
int \_write(int fd, char \*ptr, int len);

/\*
\* Called by libc stdio fwrite functions
\*/
int \_write(int fd, char \*ptr, int len) {
int i = 0;

/\*
\* write "len" of char from "ptr" to file id "fd"
\* Return number of char written.
\*
\* Only work for STDOUT, STDIN, and STDERR
\*/
if (fd > 2) {
return -1;
}

while (\*ptr && (i < len)) {
HAL\_UART\_Transmit(&huart1, (uint8_t\*) ptr, 1, 100);
if (\*ptr == '\n') {
HAL\_UART\_Transmit(&huart1, (uint8_t\*) __r_chr, 1, 100);
}
// 重启接收中断
HAL\_UART\_Receive\_IT(&huart1, (uint8_t\*) RX_Buffer, 1);
i++;
ptr++;
}

return i;
}
/\* USER CODE END 4 \*/

5)调用实现

main.c文件中,程序调用实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/\*\*
\* @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();

/\* Initialize interrupts \*/
MX\_NVIC\_Init();
/\* USER CODE BEGIN 2 \*/
// 开启接收中断
HAL\_UART\_Receive\_IT(&huart1, (uint8_t \*)RX_Buffer, 1);
/\* USER CODE END 2 \*/

/\* Infinite loop \*/
/\* USER CODE BEGIN WHILE \*/
printf("STM32F103VET6 UART Demo\n");
while (1) {
/\* USER CODE END WHILE \*/
HAL\_Delay(1);
/\* USER CODE BEGIN 3 \*/
}
/\* USER CODE END 3 \*/
}

5、运行结果

在这里插入图片描述

文章来源: https://iotsmart.blog.csdn.net/article/details/123263120