STM32————UART串口通信

(stm32之HAL库)UART工作在DMA模式要打开串口中断吗?_hal uart dma-CSDN博客

10 通用同步异步收发器(USART) - 知乎

串口简介

串口是串行接口的简称 常用串口:RS232,RS485,USB(Universal Serial Bus)通用串行接口,TTL串口 TTLTransistor-Transistor Logic的简写,是一种电平逻辑,晶体管-晶体管逻辑

USART简介

USART,即通用同步/异步收发传输器(Universal Asynchronous Receiver/Transmitter),是单片机上的一个外设

UART遵循异步串行通信协议的,USRT遵循同步串行通信协议 异步不需要统一时钟信号,同步需要统一时钟信号

一般使用最多的是UART,我们此次学习也是针对UART

UART通信协议

  1. 起始位:发出1位低电平信号,表示开始传输字符
  2. 数据位:真正发送的数据,一般为8位(1个字节),常采用ASCII编码,从最低位开始发送
  3. 校验位:用于检验接收到的数据是否正确,分为奇校验和偶校验
  4. 停止位:一组数据的结束传输的标志。可以是1位、1.5位、2位的高电平
  5. 空闲位:空闲时数据线为高电平状态,代表无数据传输
  6. 波特率:衡量传输速率的指标。UART通信中波特率等于比特率

UART发送/接收机制

UART发送端口,首先是CPU将数据放入数据发送寄存器,然后发送移位寄存器会将数据从数据发送寄存器 取出进行移位并通过UART_TX发送出去

UART接收端口,首先是UART_RX接收串行数据至接收移位寄存器,接收移位寄存器将串行数据移位变换为并行数据给数据接收寄存器,再通过APB总线给STM32内部

UART模式

轮询模式

CPU不断查询发送数据寄存器或者接受数据寄存器导致程序的阻塞

发送

UART底层有发送移位寄存器和发送数据寄存器,发送数据时,需要CPU将发送数据寄存器的数据移动至发送移位寄存器,然后UART按照指定的波特率发送数据,CPU则不断查询数据发送寄存器是否空,如果空则需要移动数据进来

接收

UART底层有接收移位寄存器和接收数据寄存器,接收数据时,UART按照指定的波特率接收数据至接收移位寄存器,然后数据会存储在接收数据寄存器,CPU则不断查询数据接收寄存器是否有数据,如果有数据,CPU会把寄存器的数据移动至我们用来接收数据的变量的内存里

HAL_UART_Receive执行完成,我们就可以知道数据接收完成

中断模式

使用中断模式时,需要先配置UART的NVIC中断设置

底层中断实现数据每传送一字节,召回CPU继续运输数据,不需要我们考虑,HAL库底层已经帮我们实现 上层中断实现数据接收完成后的数据处理,通过中断回调函数实现,需要我们自己设计

发送

CPU将数据送入 发送数据寄存器,然后就去执行其他代码,当发送移位寄存器的数据发送后,发送数据寄存器空,会触发发送数据寄存器空中断,CPU再次将数据送入 发送数据寄存器,然后又去执行其他代码,所有数据发送完成后会触发 传送完成中断,调用HAL_UART_TxCpltCallback中断回调函数

接收

CPU在处理其他代码,接收移位寄存器将一帧数据移动至 接受数据寄存器后,会触发 接收数据寄存器非空中断,CPU会回来将 接收数据寄存器 的数据移动至我们用来接收数据的变量的内存里,然后又去执行其他代码,所有数据完成后会调用_HAL_UART_RxCpltCallback_中断回调函数处理接收的数据

而执行HAL_UART_Receive_IT后,我们不能直接对数据继续处理,数据还没接收完成,因为CPU不会进入阻塞态,会去执行其他代码,需要通过HAL_UART_RxCpltCallback回调函数处理接收的数据

DMA模式

DMA,全称为Direct Memory Access,直接内存访问,本质是将传输数据从一个内存空间搬运至另一个内存空间,可以用来提供外设和内存,内存和外设之间的高速数据传输

DMA模式中也会有中断参与,在Cube配置中需要开启中断

发送

在普通的轮询USART中,CPU一直在等待外设发送数据,外设每发送一帧数据,CPU就从内存中移动一帧数据到外设的寄存器

在中断的USART中,外设每从寄存器中发送一帧数据,就会触发一次发送数据寄存器空中断,使CPU回来将数据从内存搬运至外设的寄存器中

没有DMA的话,CPU会作为数据发送的中转站,而使用DMA的话,整个数据传输过程不需要CPU的参与,由DMA代理执行,DMA负责将内存数据搬运至外设的传输寄存器

接收

在普通的轮询USART中,CPU一直在询问外设是否接收完数据,外设接收一帧数据,该位数据由CPU从外设的数据接收寄存器运进内存,CPU再次询问外设是否接收完数据,再运,直至整个数据接收完成

而在中断USART中,外设每接收一帧数据,触发一次数据接收寄存器非空中断,CPU过来将数据从寄存器搬运至内存,所有数据接收完成后,会调用接收完成中断回调函数(HAL_UART_RxCpltCallback

没有DMA的话,CPU会作为数据转运的中转站,而使用DMA的话,整个数据转运过程不需要CPU的参与,由DMA代理执行

Cube配置解析

基础参数配置

  1. Baud Rate:波特率
  2. Word Length:包含校验位在内的数据包长度
  3. Parity:奇偶校验位
  4. Stop Bits:停止位位数
  5. Data Direction:发送和

NVIC配置

如果使用中断模式,需要在NVIC settings开启USARTi global interrupt

DMA配置

  1. Add:选择添加USARTi_RXDMA或者USARTi_TXDMA
  2. Delete:删除对应的DMA
  3. Mode:选择normal模式或者circular模式
  4. Data Byte:数据传输的位数
  5. Increment Address:开启地址自增,外设只有一个寄存器,地址固定,不需要开启地址自增,而内存中我们存数据的变量是数组,接受一字节数据后需要往后移,需要开启地址自增
  6. Priority:优先级设置
  7. Direction:方向,固定死的

GPIO配置

一般用来查看引脚的GPIO配置,不需要更改(有特殊需求时,可以按需修改) 在GPIO Settings处可以查看TXRXGPIO引脚配置,此处可以看到RX为输入模式,无上拉无下拉

HAL库UART常用函数

轮询模式

1
2
HAL_UART_Receive(&huart1, rxBuffer, 16 , HAL_MAX_DELAY);//接收
HAL_UART_Transmit(&huart1 , txBuffer , sizeof(txBuffer) , 100 );//发送

中断模式

只能接收一次,接收完成后需要再次开启

1
2
3
4
5
6
7
HAL_UART_Receive_IT(&huart1, rxBuffer, 16 );//接收
HAL_UART_Transmit_IT(&huart1 , txBuffer , sizeof(txBuffer) );//发送

//回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {

}

DMA模式

(stm32之HAL库)UART工作在DMA模式要打开串口中断吗?_hal uart dma-CSDN博客

1
2
3
4
5
6
7
HAL_UART_Receive_DMA(&huart1, rxBuffer, 16 );//接收
HAL_UART_Transmit_DMA(&huart1 , txBuffer , sizeof(txBuffer) );//发送

//回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {

}

HAL库中使用的HAL_UART_Transmit_DMAHAL_UART_Receive_DMA等都有中断的参与,需要在Cube中开启中断

Demo

接收定长数据Nomad_violet,同时在主函数中翻转小灯

轮询实现数据收发

Cube配置

模式选择为异步

波特率选择115200 Bit/s,其余不动即可

PA8配置为GPIO_Output

GPIO配置保持默认即可

代码实现
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

#include "string.h"

#define rxBufferLen 16
#define txBufferLen 25
uint8_t rxBuffer[rxBufferLen] ;
uint8_t txBuffer[txBufferLen] = "to feel,to experience ";// 去感受,去经历「感其生,历其境」

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  while (1)
  {
    HAL_UART_Receive(&huart1, rxBuffer, 12 , HAL_MAX_DELAY);//只能接收定长数据
		if( !strcmp( (char *)rxBuffer , "Nomad_violet" ) ) {//Nomad_violet 12字节
			HAL_UART_Transmit(&huart1 , txBuffer , sizeof(txBuffer) , 100 );
		}
	HAL_GPIO_TogglePin(GPIOA , GPIO_PIN_8 );
  }
}

中断实现数据收发

Cube配置

配置同上,只需额外开启中断即可

代码实现
 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

#include "string.h"

#define rxBufferLen 12
#define txBufferLen 25
uint8_t rxBuffer[rxBufferLen] ;
uint8_t txBuffer[txBufferLen] = "to feel,to experience ";// 去感受,去经历「感其生,历其境」

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
	if(huart == &huart1) {
		if( !strcmp( (char *)rxBuffer , "Nomad_violet" ) ) {
			HAL_UART_Transmit(&huart1 , txBuffer , sizeof(txBuffer) , 100 );
		}
		HAL_UART_Receive_IT(&huart1, rxBuffer, rxBufferLen );
	}
}


int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
	HAL_UART_Receive_IT(&huart1, rxBuffer, rxBufferLen );
  while (1)
  {
		HAL_GPIO_TogglePin(GPIOA , GPIO_PIN_8 );
	    HAL_Delay(500); 
  }
}
experience
使用 Hugo 构建
主题 StackJimmy 设计