STM32串口通信

一、串口通信的原理与应用

1. 通信的方式

处理器与外部设备之间或者处理器与处理器之间通信的方式分两种:串行通信和并行通信。

串行通信
传输原理:数据按位依次顺序传输(每一位占据固定的时间长度 MSB or LSB)
优点:占用引脚资源少、成本低、传输距离远
缺点:速度慢
应用场合:常用于工控、测量设备,以及部分通信设备 如 USB、COM 口

并行通信
传输原理:数据按位同时传输(按照字节或者字节的倍数去传输)
优点:速度快
缺点:占用引脚多,成本高、传输距离近、抗干扰能力弱(串扰)
应用场合:一般适用数据量大、传输距离较近的场合 如计算机总线


2. 串行通信分类

如果按照数据的传送方向,可以分为3 种

单 工 :数据只能从一方传向另一方,而不能往反方向传输。这是根据应用的特点,对通信双方的软、硬件进行了简化。单向传输设备,如并行接口打印机就是通过单工的方式进行数据传输的。
半双工 :允许数据往两方传向传输,但只能交替进行,而不能同时进行的传输方式。常见应用如对讲机。
**全双工 :****数据支持同时在两个方向上传输,就相当于两个单工通信的结合 如移动电话

如果按照数据的同步方式,可以分为2 种

同步通信:指的是带时钟同步信号,如IIC 接口和SPI 接口都属于同步通信,同步通信要求通信双方的时钟必须一致(发送数据的同时进行接收数据),同步通信是一种连续串行传输数据的通信方式,一次只传输一帧数据,相对于异步通信而言传输速率更高,但是缺点是通信双方必须使用同一个时钟。

异步通信:指的是不带时钟同步信号,如UART 接口就属于异步通信,要求通信双方必须具有接收器和发送器,但是接收器和发送器可以使用各自的时钟,异步通信是以字符为单位去传输,由于异步通信工作是非同步的,所以接收器必须时刻做好接收数据的准备,所以每个
字符都会使用起始位和停止位代表字符的开始和结束,相比于同步通信而言设备成本低,但是缺点是信道利用率低。

注意:使用异步通信,通信双方要约定好的字符格式和通信速率,否则会导致数据丢失等

image-20251121150931766

3.串行接口意义

串行通信就是把数据按位进行顺序传输,在计算机传输的过程中是使用并行通信的,就涉及到串转并和并转串。可以利用软件实现,也可以用硬件实现。如果使用软件实现,会增大CPU 的负担,通常使用硬件(串口)实现。

串口是计算机进行串行通信的物理接口,线路简单,串口是按位进行数据的收发,可以用一根线(TXD)发送数据的同时用另一根线接收(RXD)。

常用的串口接口有两种:9 针串口(DB-9) 25 针串口(DB-25) 每种都分为公头和母头

一般DB9 串口有9 个引脚,但是一般作为串口通信时只需要用到三个引脚,分别是RXD、TXD、GND,其他的线一般用于握手。
单片机如果打算和计算机进行串口通信,则需要注意串口的电平协议,单片机采用的是TTL电平,而计算机采用的RS232 电平,所以单片机需要和计算机通信,则需要增加如MAX232电平转换芯片进行电气转换。

TTL 电平协议 :采用正逻辑电平 +5V 等价于逻辑1 0V 等价于逻辑0
RS232 电平协议 :采用负逻辑电平 -15V~-3V 等价于逻辑1 +3V~+15V 等价于逻辑0

为使通信顺利进行,由通信双方就如何交换信息所建立进来的一套规定,称为通信控制规程,网络术语中称为协议。另外,为统一接口和连接,规程中还有关于信号线功能、电气特性、机械特性等明确的规定,称之为接口标准。如RS-232、RS-485 接口标准等。

RS-232 是一个已制定很久的标准(RS 表示推荐标准,232 表示标识符),它描述了计算机及相关设备间较低速率的串行数据通信的物理接口及协议。它是由一个工业贸易组电子工业联合会(EIA)定义的,最初是为电传打印机设备而制定。RS-232 是计算机用来与modem 及其它串行设备交谈或交换数据的接口。

RS-232 接口速率不会很高,线缆最长为15 米。RS-232 接口通常被用于将电脑信号输入控制,当通信距离较近时,可不需要Modem,通信双方可以直接连接,这种情况下,只需使用少数几根信号线,即TXD、RXD、GND。

RS-232 采用负逻辑规定逻辑电平,-3V ~ -15V 为逻辑“1”电平,+3V ~ +15V 为逻辑“0”电平。这种信号电平与通常并行接口中使用的TTL 电平不同,会常用一些电平转换芯片如MAX232。

现在个人计算机提供的串行端口终端的传输速度一般都可以达到115200bps 甚至更高,标准串口能够提供的传输速度主要有以下波特率:1200bps、2400bps、4800bps、9600bps、19200bsp、38400bps、57600bps、115200bps 等,在仪器仪表或工业控制场合,9600bps 是最常见的传输速度,在传输距离较近时,使用最高传输速度也是可以的。传输距离和传输速度的关系成反比,适当地降低传输速度,可以延长RS-232 的传输距离,提高通讯的稳定性。

4.硬件接线说明

但是对于目前来说,很多设备并不会集成DB-9 串口,而是直接采用4 针的串口(TXD、RXD、GND、VCC),然后通过CH340 芯片进行转换,就可以直接通过USB 口转串口的方式达到和计算机通信的目的。

如果采用有线方式与PC 机进行通信,则需要用到CH340 转换芯片,数据会通过USART1 串口传输。如果采用无线方式与手机进行通信,则不需要用到CH340 转换芯片,但是需要通过蓝牙模块进行数据的转发,蓝牙模块如果接在USART1 串口的接口,则数据也是通过USART1 进行传输。

注意:如果打算实现有线通信和无线通信的结合,则需要把无线通信模块链接在其他的串口上即可,想要和计算机通信,只能使用USART1 串口,因为引脚是固定的。

5.串口通信参数

串口通信属于全双工异步通信,所以通信双方必须具有发送端(TXD)与接收端(RXD),由于异步通信不需要时钟来进行数据同步,但是通信双方必须提前约定好字符格式与通信速率。

字符格式

一般在进行串口通信的时候,需要通信双方在协议层规定好传输的数据包(字符帧)的格式,字符帧由起始位、数据位、校验位、停止位组成。这样通信双方就可以利用起始位和停止位实现同步。 关于字符格式的相关参数的说明可以参考STM32 中文参考手册第26.3.1 章节。

image-20251121151529214

image-20251121151548100

通信速率

在串口通信中,如果设置好了通信的字符格式,还需要通信双方约定好通信速率,也就是单位时间内传输的有效二进制数的个数,所以也被称为波特率(bps baud pre second)。一般串口通信常用的波特率为9600bps、38400bps、57600bps、115200bps,当然也有其他的选择,波特率有对应的计算公式,可以参考STM32 中文参考手册的第26.3.4 章节。

image-20251121151648004

举个例子:大多数使用串口通信的传感器的字符格式都是8bit 数据位、1bit 停止位、无奇偶校验位,当然还必须有1bit 起始位,所以一帧数据是10bit,如果通信速率设置为9600bps,则单位时间内能传输的数据帧为9600/10 = 960 帧。这是使用频率最高的一种通信速率!

6.USART 的使用流程

USART 指的是通用同步异步收发器,是STM32 中的串行通信设备,STM32F407ZET6 一共提供了6 个串行接口供用户使用,其中4 个为USART,2 个为UART。UART 指的是通用异步收发器,其实就是在USART 的基础上裁掉了同步通信的功能,只保留了异步通信。
平时一般都是使用串口的异步通信功能,区分同步还是异步其实很简单,就是看是否在通信是对外提供时钟输出。

image-20251121152012792

可以看到STM32 中的USART 采用工业标准中常用的NRZ 编码进行通信,NRZ 码指的是不归零码(Non-Return-Zero),指的是每次传输1bit 数据后电平不归零,其实还有一种RZ 码,指的是归零码(Return-Zero),指的是每次传输1bit 数据后需要电平归零。

设计一个程序,实现可以向计算机的串口调试助手发送一个字符串,要求把程序封装为一个接口,该接口专门用于发送字符串。

image-20251121152156845

如果打算串口发送字符串,由于字符串属于字符序列,所以按照顺序发送字符,由于串口外设的发送数据寄存器是8bit 的,所以每次只能发送1 个字符,并且只有等上一次字符发送完成,才可以发送下一个字符。可以通过检测串口外设的发送数据寄存器是否为空,来判断待发送的字节是否发送完成,所以需要检测状态标志。

程序设计

/**
 ******************************************************************************
 * @file    usart.c 
 * @author  qrshxc@163.com
 * @version V1.0
 * @date    2025.7.27
 * @brief   利用USART1和计算机通信(PA9和PA10引脚)
 ******************************************************************************
 **/

#include "stm32f4xx.h"

/**
 * @name      delay_us
 * @brief     微秒级延时函数
 * @param     nus: 延时的微秒数
 * @return    无
 * @version   1.0
 * @note      使用SysTick定时器实现精确微秒延时
 */
void delay_us(u32 nus)
{
	SysTick->CTRL = 0;  		  // 关闭系统嘀嗒定时器
	SysTick->LOAD = nus * 21 -1;  // 计数次数,递减计数,0结束
	SysTick->VAL = 0;   		  // 清除当前数值寄存器
	SysTick->CTRL = 1;   		  // 打开系统嘀嗒定时器,使用参考时钟
	while ((SysTick->CTRL & 0x00010000)==0); // 等待计时完成
	SysTick->CTRL = 0;   		  // 关闭系统嘀嗒定时器
}

/**
 * @name      delay_ms
 * @brief     毫秒级延时函数
 * @param     nus: 延时的毫秒数
 * @return    无
 * @version   1.0
 * @note      使用SysTick定时器实现精确毫秒延时
 */
void delay_ms(u32 nus)
{
	SysTick->CTRL = 0;    // 关闭系统嘀嗒定时器
	SysTick->LOAD = nus * 21 * 1000 -1; // 计数次数,递减计数,0结束
	SysTick->VAL = 0;     // 清除当前数值寄存器
	SysTick->CTRL = 1;    // 打开系统嘀嗒定时器,使用参考时钟
	while ((SysTick->CTRL & 0x00010000)==0); // 等待计时完成
	SysTick->CTRL = 0;    // 关闭系统嘀嗒定时器
}

/**
 * @name      USART1_IRQHandler
 * @brief     USART1中断服务函数
 * @param     无
 * @return    无
 * @version   1.0
 * @note      处理USART1接收中断,实现数据回显功能
 */
void USART1_IRQHandler(void)
{
	uint8_t data;
	/* 检查USART1接收中断标志 */
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		/* 接收数据,一次一个字节 */
		data = USART_ReceiveData(USART1);
		
		/* 发送数据(回显功能) */
		USART_SendData(USART1, data);
	}
}

/**
 * @name      USART1_Config
 * @brief     配置USART1串口
 * @param     baud: 波特率
 * @return    无
 * @version   1.0
 * @note      配置USART1为异步模式,8位数据位,1位停止位,无校验
 */
void USART1_Config(uint32_t baud)
{
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;

	/* 打开GPIOA时钟 */	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

	/* 打开USART1时钟 */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	
	
	/* 设置PA9和PA10的复用功能为USART1 */
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
	
	/* 配置GPIO的复用功能+初始化 */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        // 复用功能模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;  // 高速模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      // 推挽输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        // 上拉模式
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_9 | GPIO_Pin_10; // PA9(TX), PA10(RX)
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	/* 配置USART1参数+初始化 */
	USART_InitStructure.USART_BaudRate = baud;          // 波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 8位数据位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;     // 1位停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;        // 无校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无硬件流控
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 收发模式
	USART_Init(USART1, &USART_InitStructure);
	
	/* 配置NVIC中断控制器 */
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;   // USART1中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  // 子优先级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;     // 使能中断通道
	NVIC_Init(&NVIC_InitStructure);
	
	/* 使能USART1接收中断 */
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

	/* 打开USART1 */
	USART_Cmd(USART1, ENABLE);
}

/**
 * @name      USART_SendString
 * @brief     发送字符串
 * @param     str: 要发送的字符串
 * @return    无
 * @version   1.0
 * @note      循环发送字符串中的每个字符,等待发送完成
 */
void USART_SendString(const char* str)
{
	while(*str)
	{
		USART_SendData(USART1, *str++);  // 发送字符并指针后移
		while(!USART_GetFlagStatus(USART1, USART_FLAG_TXE)); // 等待发送完成
	}
}

/**
 * @name      main
 * @brief     主函数
 * @param     无
 * @return    int: 程序执行状态
 * @version   1.0
 * @note      USART1通信测试程序,每5秒发送"hello world!"
 */
int main()
{
	/* USART1初始化,波特率9600 */
	USART1_Config(9600);
	
	/* 主循环 */
	while(1)
	{
		/* 发送测试字符串 */
		USART_SendString("hello world!\n");
		
		/* 延时5秒 */
		delay_ms(5000);
	}
}
posted @ 2025-11-22 23:51  九思0404  阅读(9)  评论(0)    收藏  举报