目录
[1. 通信的方式](#1. 通信的方式)
[2. 串行通信的数据的传送方向](#2. 串行通信的数据的传送方向)
[3. 串行通信按照数据的通信方式](#3. 串行通信按照数据的通信方式)
[4. 串行接口意义](#4. 串行接口意义)
[5. 硬件接线说明](#5. 硬件接线说明)
[7. USART的使用流程](#7. USART的使用流程)
[8. USART中断请求](#8. USART中断请求)
[案例1 :检测RXNE中断事件标志](#案例1 :检测RXNE中断事件标志)
[案例2 :检测状态标志](#案例2 :检测状态标志)
1. 通信的方式
" 处理器与外部设备之间 " 或者 " 处理器与处理器之间 " 通信的方式 分两种:串行通信和并行通信。
(1)串行通信
|----------|---------------------------------------------------|
| 串行通信 | 传输原理:数据按位依次顺序传输 (每一位占据固定的时间长度 MSB or LSB) |
| 串行通信 | 优点:占用引脚资源少、成本低、传输距离远 |
| 串行通信 | 缺点:速度慢 |
| 串行通信 | 应用场合:常用于工控、测量设备,以及部分通信设备 如 USB、COM口 |

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

2. 串行通信的数据的传送方向
单工 :数据只能从一方传向另一方,而不能往反方向传输。这是根据应用的特点,对通信双方的软、硬件进行了简化。单向传输设备,如并行接口打印机就是通过单工的方式进行数据传输的。
例如:打印机

半双工:允许数据往两方传向传输,但只能交替进行,而不能同时进行的传输方式。
例如:对讲机。

全双工 :数据支持同时在两个方向上传输,就相当于两个单工通信的结合。
例如:移动电话。

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

++++异步通信++++ ++++:++++ ++++不带时钟同步信号++++ ,如++++UART++++接口就属于异步通信。
UART(Universal Asynchronous Receiver/Transmitter)
- 异步通信要求++++通信双方必须具有接收器和发送器++++ ,++++但是接收器和发送器可以使用各自的时钟++++。
- 异步通信是以字符为单位** 去**传输 。
- 由于异步通信工作是非同步的,所以接收器必须时刻做好接收数据的准备 ,所以每个字符都会使用 起始位 和 停止位 代表字符的开始和结束,相比于同步通信而言设备成本低,但是缺点是信道利用率低。
注意:使用异步通信, 通信双方要约定好的字符格式和通信速率 ,否则会导致数据丢失等问题出现。
4. 串行接口意义
串行通信 :把数据按位进行顺序传输 (一个字节以比特的形式发出 去),++在计算机传输的过程中是使用并行通信的 ,单片机用的是串行通信++ ,所以++++涉及到串转并和并转串++++ 。可以利用软件实现,也可以用硬件实现。如果使用软件(程序/设计算法的方式)实现,会增大CPU的负担(本来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
5. 硬件接线说明
对于目前,很多设备++++直接采用4针的串口(TXD、RXD、GND、VCC),然后通过CH340芯片进行转换++++,就可以直接通过USB口转串口的方式达到和计算机通信的目的。
串口的输出输入Tx,Rx
USB的输出输入D+,D-

- 采用有线方式与PC机进行通信 ,则需要用到CH340转换芯片,数据会通过USART1串口传输。
- 采用无线方式与手机进行通信 ,则不需要用到CH340转换芯片,但是需要通过蓝牙模块进行数据的转发,蓝牙模块如果接在USART1串口的接口,则数据也是通过USART1进行传输。
注意:如果打算实现有线通信和无线通信的结合,则需要把无线通信模块链接在其他的串口上即可, ++++想要和计算机通信,只能使用USART1串口,因为引脚是固定的。++++
单片机传输用的串行通信,计算机传输用的并行通信,==> 涉及串行、并行中间的转换
串口的存在,是为了增加硬件,降低CPU的负担。
CH340是实现USB转串口的转接芯片。
6.串口通信参数
++++串口通信属于全双工异步通信++++ ,所以++通信双方必须具有发送端(TXD)与接收端(RXD)++。
++++由于异步通信不需要时钟来进行数据同步,但是++++ ++++通信双方必须提前约定好字符格式++++ ++++与通信速率。++++
(1)字符格式
在进行串口通信的时候,需要通信双方在协议层规定好传输的数据包(字符帧)的格式 ,字符帧由起始位、数据位、校验位、停止位组成。这样通信双方就可以利用起始位和停止位实现同步。 关于字符格式的相关参数的说明可以参考STM32中文参考手册第26.3.1章节。


(2)通信速率
在串口通信中,如果设置好了通信的字符格式,还需要通信双方约定好通信速率,也就是单位时间内传输的有效二进制数的个数,所以也被称为波特率(bps baud pre second)。
一般串口通信常用的波特率为9600bps 、38400bps、57600bps、115200bps,当然也有其他的选择,波特率有对应的计算公式,可以参考STM32中文参考手册的第26.3.4章节。
举个例子 :大多数使用串口通信的传感器的字符格式都是8bit数据位、1bit停止位、无奇偶校验位,当然还必须有1bit起始位,所以++++一帧数据是10bit,如果通信速率设置为9600bps,则单位时间内能传输的数据帧为9600/10 = 960帧++++。这是使用频率最高的一种通信速率!!!!
7. USART的使用流程
USART指的是++++通用同步异步收发器++++ ,是STM32中的串行通信设备,STM32F407ZET6一共提供了6个串行接口供用户使用,其中4个为USART,2个为UART。++++UART指的是通用异步收发器,其实就是在USART的基础上裁掉了同步通信的功能,只保留了异步通信。++++
平时大家一般都是使用串口的异步通信功能,区分同步还是异步其实很简单,就是看是否在通信是对外提供时钟输出

8. USART中断请求

如果打算串口发送字符串,由于字符串属于字符序列,所以按照顺序发送字符,由于++串口外设的发送数据寄存器是8bit的,每次只能发送1个字符++ ,并且只有等上一次字符发送完成,才可以发送下一个字符。
可以通过检测 "串口外设的发送数据寄存器" 是否为空,来判断待发送的字节是否发送完成 ,所以需要检测状态标志。
案例1 :检测RXNE中断事件标志
cpp
void USART1_IRQHandler(void)
{
/* USART in Receiver mode */ //收到数据的时候发生触发
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)//串口的RX检测到数据
{
uint8_t data = 0;
/* Receive Transaction data */
data = USART_ReceiveData(USART1);//单片机接收数据
//由于当前单片机没有屏幕显示接收到的数据,只能把接收到的数据返回给计算机
USART_SendData(USART1, data);
}
}
USART_SendData() 函数本身是"非阻塞"的。 它只负责将数据写入寄存器,然后立即返回,并不会等待数据真正从硬件上发送完毕。
++++如果在发送完一个字节后,没有确认上一个字节是否发送完成就立即尝试发送下一个字节,可能会导致数据覆盖或丢失。++++
Q :如何确保数据可靠发送?
**调用 USART_SendData() 后,必须配合使用状态标志检查函数 USART_GetFlagStatus() 来确保数据真正发送完毕。**通常有两种等待方式:
- 等待发送数据寄存器空(TXE, Transmit Data Register Empty)标志: 表示TDR中的数据已经转移到移位寄存器,可以写入下一个数据。这是最常用的方式。
- 等待发送完成(TC, Transmission Complete)标志: 表示不仅TDR空了,整个数据帧(包括停止位)都已经从移位寄存器发送出去了。在关闭串口或进入低功耗模式前,等待此标志尤为重要。
案例2 :检测状态标志
cpp
void SendComputerString(char *string)
{
while(*string != '\0')
{
USART_SendData(USART1, *string++ );
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET); //检测"发送数据寄存器数据位为空"
}
}
USART_SendData() : 是单片机/MCU主动给**发数据。 单片机-->**
触发中断 : 触发"计算机上在上位机向MCU发送数据"的中断。 计算机-->单片机
9.程序设计
需求:实现可以向计算机的串口调试助手发送一个字符串,要求把程序封装为一个接口,该接口专门用于发送字符串。
cpp
/**
******************************************************************************
* @file GPIO/GPIO_IOToggle/main.c
* @author
* @version
* @date 03-August-2026
* @brief 利用USART1和计算机通信
USART1_TX -- PA9
USART1_RX -- PA10
******************************************************************************
**/
#include "stm32f4xx.h" //必须要包含的头文件
#include <string.h>
void delay_us(uint32_t nus);
void delay_ms(uint32_t nms);
void delay_s(uint32_t ns);
void USART1_Config(unsigned int baud)
{
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
//Enable peripheral clock using the following functions
/* Enable USART clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//According to the USART mode, enable the GPIO clocks
/* Enable GPIO clock */
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOA , ENABLE);
//Peripheral's alternate function:
/* Connect USART pins to AF7 */
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
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;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* Program the Baud Rate, Word Length , Stop Bit, Parity, Hardware
flow control and Mode(Receiver/Transmitter) using the USART_Init()
function. */
USART_InitStructure.USART_BaudRate = baud; //波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //数据位
USART_InitStructure.USART_StopBits = USART_StopBits_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);
//Enable the NVIC and the corresponding interrupt
/* NVIC configuration */
/* Configure the Priority Group to 2 bits */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* Enable the USARTx Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//接收缓冲区为空的时候使用中断
// Enable the USART using the USART_Cmd() function.
USART_Cmd(USART1, ENABLE);
}
//向计算机的串口调试助手发送一个字符串,要求把程序封装为一个接口,该接口专门用于发送字符串。
void SendComputerString(char *string) //参数里传入一个字符串
{
while(*string != '\0')
{
USART_SendData(USART1, *string++ );
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET);
}
}
int main()
{
USART1_Config(9600);
while(1)
{
SendComputerString("12345\r\n");
//USART_SendData(USART1, 'f');//单片机给计算机发,不需要中断
delay_ms(500);
}
}
void USART1_IRQHandler(void)
{
/* USART in Receiver mode */ //收到数据的时候发生触发
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)//串口的RX检测到数据
{
uint8_t data = 0;
/* Receive Transaction data */
data = USART_ReceiveData(USART1);//单片机接收数据
//由于当前单片机没有屏幕显示接收到的数据,只能把接收到的数据返回给计算机
USART_SendData(USART1, data);
}
}
void delay_us(uint32_t nus)
{
SysTick->CTRL = 0; // 关闭systick的控制状态寄存器――该寄存器的bit0置0
SysTick->LOAD = ( 21 * nus - 1); // systick的重载寄存器――计数时长 = 计数周期 * 计数次数
SysTick->VAL = 0; // 清空systick的当前数值寄存器
SysTick->CTRL = 1; // 使能systick的控制状态寄存器――该寄存器的bit0置1
while ((SysTick->CTRL & 0x00010000)==0);// 递减计数到了则控制状态寄存器的bit16为1,计数次数没到则bit16为0.
SysTick->CTRL = 0; //关闭systick的控制状态寄存器
}
void delay_ms(uint32_t nms)
{
SysTick->CTRL = 0; // 关闭systick的控制状态寄存器――该寄存器的bit0置0
SysTick->LOAD = ( 21*1000*nms - 1); // systick的重载寄存器――计数时长 = 计数周期 * 计数次数
SysTick->VAL = 0; // 清空systick的当前数值寄存器
SysTick->CTRL = 1; // 使能systick的控制状态寄存器――该寄存器的bit0置1
while ((SysTick->CTRL & 0x00010000)==0);// 递减计数到了则控制状态寄存器的bit16为1,计数次数没到则bit16为0.
SysTick->CTRL = 0; //关闭systick的控制状态寄存器
}
void delay_s(uint32_t ns)
{
while(ns--)
{
delay_ms(500);
delay_ms(500);
}
}
同步通信:发送的时候会接收。
Q:如何区分是同步通信or异步通信
A:看算法需要脉冲信号/时钟。
如果需要脉冲信号/时钟,就是同步;
如果不需要脉冲信号/时钟,就是异步。