目录
通用同步异步收发器(USART)
理论部分
USART概览
STM32和PC通信模型
STM32和PC通过RS-232标准通信
RS-232标准介绍
RS-232协议电平标准对比
RS-232标准的物理接口规定及接口标号
RS-232标准下接口标号的作用
RS-232标准数据传输协议层
协议概念
数据校验
外设框图
实验部分
CubeMX配置过程
CubeMX生成串口如何使用
手动编写(BSP板级支持包)
通用同步异步收发器(USART)
相关内容参考:
CubeMX生成串口如何使用:cubemx快速使用串口_cubemx 串口_弦续北上i的博客-CSDN博客
串口重映射:STM32——USART1重映射_usart重映射_Jack_Cheng_2021的博客-CSDN博客
理论部分
USART概览
- 通用异步收发传输器,英文全称Universal Asynchronous Receiver/Transmitter,简称UART。
- STM32上的USART外设可以实现同步传输功能,所以外设名为USART,比UART多了一个S,即synchronous(同步)。
- UART器件主要用来产生相关接口的协议信号,如RS232\RS485等串行接口标准规范和总线标准规范,要使用传输数据这些接口,就要按照接口规定的协议信号发送数据。所以UART器件广泛应用于串口通信中,扮演者传输器的角色。
STM32和PC通信模型
STM32和PC通过RS-232标准通信
RS-232标准介绍
- RS-232是美国电子工业联盟(EIA)制定的串行数据通信的接口标准,原始编号全称是EIA-RS-232(简称232,RS232)。它被广泛用于计算机串行接口外设连接。
- RS-232 标准规定了连接电缆和机械、电气特性、信号功能及发送过程。
RS-232协议电平标准对比
一般开发板上使用的电平标准与通讯使用的电平标准不同,如TTL 标准及 RS-232 标准。
因为控制器一般使用 TTL 电平标准,所以常常会使用 MA3232 芯片对 TTL 及 RS-232电平的信号进行互相转换。
RS-232标准的物理接口规定及接口标号
现在一般不使用RS232串口而使用USB转串口
RS-232标准下接口标号的作用
一般只使用RXD、TXD和GND即可完成串口通信。
RS-232标准数据传输协议层
串口通讯的数据包由发送设备通过自身的 TXD 接口传输到接收设备的 RXD 接口。在串口通讯的协议层中,规定了数据包的内容,它由起始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据,其组成见图。
协议概念
- 波特率
本章中主要讲解的是串口异步通讯,异步通讯中由于没有时钟信号(如前面讲解的 DB9接口中是没有时钟信号的),所以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码, 图中用虚线分开的每一格就是代表一个码元。常见的波特率为4800、 9600、 115200 等。
- 通讯的起始和停止信号
串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一个逻辑 0 的数据位表示,而数据包的停止信号可由 0.5、 1、 1.5 或 2 个逻辑 1 的数据位表示,只要双方约定一致即可。
- 有效数据
在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定为 5、 6、 7 或 8 位长。
- 数据校验
在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验(odd)、偶校验(even)、 0 校验(space)、 1 校验(mark)以及无校验(noparity)。
数据校验
外设框图
F1\F4\F7-USART外设
其中TX和RX使用GPIO引脚作为输入输出,涉及到GPIO引脚的复用功能
H7-USART外设
实验部分
CubeMX配置过程
在CDE数据手册(STM32产品手册)中,可以查找所有GPIO引脚的功能
以配置串口1为例
(1)选择异步传输模式Asynchronous
(2)配置串口的工作模式参数
(3)对于USART1,可以有多组引脚选择(参照GPIO功能表),默认为PA9和PA10,如果选择PB6,会自动变为PB6和PB7。注意:在实际使用中,每个串口只能使用一组GPIO
(4)勾选NVIC中断,关于DMA下一讲介绍,这里暂时不用
(5)工程代码:
如图所示:
HAL_UART_Init调用了HAL_UART_MspInit函数
补充:
以HAL_UART_MspInit为例,HAL_UART_MspInit为弱定义函数,可以用户自己实现功能
补充:
MSP是指和MCU相关的初始化,举个例子就可以理解:
比如串口的初始化
1、我们要初始化和MCU无关的东西:例如串口协议,其中包括波特率,奇偶校验,停止位等等,这些设置和使用什么样的MCU没有任何关系,可以使用F1的MCU,也可以是F2...F4,甚至是PC上的串口。所以就把串口抽像成为一个“串口”。
2、有了抽像的串口,这个“串口”就要在MCU上进行承载,用STM32F4进行承载,PA9做为发送,PA10做为接收.MSP就是要初始化PA9,PA10。配置这两个引解
所以HAL驱动方式的初始化流程就是:HAL_USART_Init() ------>HAL_USART_MSP_Init() 先初始化协议,再初始化MCU的引角。在STM32的HAL驱动中MSP_Init()是做为回调,包含在PPP_Init()中的。
CubeMX生成串口如何使用
以USART1回显为例
(1)添加使能串口中断
(2)由于我们用CubeMX生成的串口引脚是PB6、PB7,而实际使用的是PA9和PA10,所以将重映射部分注释掉,否则会影响串口使用
(3)添加自定义发送字符串函数和重定义C库函数
(4)在终端函数中的回调函数后加上自定义功能
(5)在main函数中打印一句话
(6)上位机测试
以上是CubeMX串口生成已经使用过程。
手动编写(BSP板级支持包)
步骤如下:
一、 定义一个UART_HandleTypeDef结构体句柄
二、 通过HAL_UART_MspInit函数来实现串口外设的底层初始化:
要做什么功能:
1、 使能UART外设时钟
2、 配置UART使用的引脚模式
3、 如果要用中断,就配置中断
4、 如果要用DMA,就配置DMA
三、 通过前面定义的结构体,来配置串口的波特率、数据字长、停止位、奇偶校验位、硬件流控制。
四、 通过调用HAL_UART_Init函数,来将串口配置为异步模式。
同样以USART1回显为例
头文件
#ifndef __DEBUG_USART_H
#define __DEBUG_USART_H
#include "stm32f1xx.h"
#include <stdio.h>
//串口波特率
#define DEBUG_USART_BAUDRATE 115200
//引脚定义
/*******************************************************/
#define DEBUG_USART USART1
#define DEBUG_USART_CLK_ENABLE() __HAL_RCC_USART1_CLK_ENABLE();
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define DEBUG_USART_RX_PIN GPIO_PIN_10
#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define DEBUG_USART_TX_PIN GPIO_PIN_9
#define DEBUG_USART_IRQHandler USART1_IRQHandler
#define DEBUG_USART_IRQ USART1_IRQn
/************************************************************/
void Usart_SendString(uint8_t *str);
void DEBUG_USART_Config(void);
int fputc(int ch, FILE *f);
int fgetc(FILE *f);
extern UART_HandleTypeDef UartHandle;
#endif /* __USART1_H */
源文件
#include "./usart/bsp_debug_usart.h"
UART_HandleTypeDef UartHandle;
//extern uint8_t ucTemp;
/**
* @brief DEBUG_USART GPIO 配置,工作模式配置。115200 8-N-1
* @param 无
* @retval 无
*/
void DEBUG_USART_Config(void)
{
UartHandle.Instance = DEBUG_USART;
UartHandle.Init.BaudRate = DEBUG_USART_BAUDRATE;
UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
UartHandle.Init.StopBits = UART_STOPBITS_1;
UartHandle.Init.Parity = UART_PARITY_NONE;
UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
UartHandle.Init.Mode = UART_MODE_TX_RX;
HAL_UART_Init(&UartHandle);
/*使能串口接收断 */
__HAL_UART_ENABLE_IT(&UartHandle,UART_IT_RXNE);
}
/**
* @brief UART MSP 初始化
* @param huart: UART handle
* @retval 无
*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
GPIO_InitTypeDef GPIO_InitStruct;
DEBUG_USART_CLK_ENABLE();
DEBUG_USART_RX_GPIO_CLK_ENABLE();
DEBUG_USART_TX_GPIO_CLK_ENABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
/* 配置Tx引脚为复用功能 */
GPIO_InitStruct.Pin = DEBUG_USART_TX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStruct);
/* 配置Rx引脚为复用功能 */
GPIO_InitStruct.Pin = DEBUG_USART_RX_PIN;
GPIO_InitStruct.Mode=GPIO_MODE_AF_INPUT; //模式要设置为复用输入模式!
HAL_GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStruct);
HAL_NVIC_SetPriority(DEBUG_USART_IRQ ,0,1); //抢占优先级0,子优先级1
HAL_NVIC_EnableIRQ(DEBUG_USART_IRQ ); //使能USART1中断通道
}
/***************** 发送字符串 **********************/
void Usart_SendString(uint8_t *str)
{
unsigned int k=0;
do
{
HAL_UART_Transmit(&UartHandle,(uint8_t *)(str + k) ,1,1000);
k++;
} while(*(str + k)!='\0');
}
//重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到串口DEBUG_USART */
HAL_UART_Transmit(&UartHandle, (uint8_t *)&ch, 1, 1000);
return (ch);
}
//重定向c库函数scanf到串口DEBUG_USART,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
int ch;
HAL_UART_Receive(&UartHandle, (uint8_t *)&ch, 1, 1000);
return (ch);
}
/*********************************************END OF FILE**********************/
中断:
void DEBUG_USART_IRQHandler(void)
{
uint8_t ch=0;
if(__HAL_UART_GET_FLAG( &UartHandle, UART_FLAG_RXNE ) != RESET)
{
ch=( uint16_t)READ_REG(UartHandle.Instance->DR);
WRITE_REG(UartHandle.Instance->DR,ch);
}
}