目录
1.串口模式配置
c
/**
* @brief 配置 DEBUG 串口模式
* @param 无
* @retval 无
*/
void DEBUG_USART_MODEConfig(void)
{
USART_InitTypeDef usart_InitStruct = { 0 };
/* 开启 DEBUG 相关的GPIO外设/端口时钟 */
DEBUG_APBXCLKCMD(DEBUG_USARTX_CLK_PORT, ENABLE); //开启 端口时钟
/* 配置串口的工作参数 */
//波特率配置:115200
usart_InitStruct.USART_BaudRate = DEBUG_BAUDRATE;
//硬件流控开关配置:无
usart_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
//配置工作模式:发送模式
usart_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
//校验:无校验位
usart_InitStruct.USART_Parity = USART_Parity_No;
//停止位:1个停止位
usart_InitStruct.USART_StopBits = USART_StopBits_1;
//数据帧长度:8位
usart_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init(DEBUG_USARTX, &usart_InitStruct);
}
需要注意的地方是,在配置模式的时候可以写成这样
c
usart_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
这里将发送模式和接收模式可以或运算起来,并不冲突的,这是因为

一个是0x00004,另外一个是0x00008
而这个4是0100,而这个8是1000,或运算就是1100,所以并不会相互冲突影响的。
2.串口IO配置
2.1 串口头文件
c
#ifndef __BSP_DEBUG_H
#define __BSP_DEBUG_H
#include "stm32f10x.h"
#define DEBUG_USART_NUM 1
#if(DEBUG_USART_NUM == 1)
#define DEBUG_TX_GPIO_PORT GPIOA /* GPIO 端口 */
#define DEBUG_TX_GPIO_CLK_PORT RCC_APB2Periph_GPIOA /* GPIO 端口时钟 */
#define DEBUG_TX_GPIO_PIN GPIO_Pin_9 /* 对应PIN脚 */
#define DEBUG_RX_GPIO_PORT GPIOA /* GPIO 端口 */
#define DEBUG_RX_GPIO_CLK_PORT RCC_APB2Periph_GPIOA /* GPIO 端口时钟 */
#define DEBUG_RX_GPIO_PIN GPIO_Pin_10 /*对应PIN脚 */
#define DEBUG_USARTX USART1 /* 串口号 */
#define DEBUG_USARTX_CLK_PORT RCC_APB2Periph_USART1 /* 串口外设时钟位 */
#define DEBUG_APBXCLKCMD RCC_APB2PeriphClockCmd /* 串口外设时钟 */
#define DEBUG_BAUDRATE 115200 /* 波特率 */
#elif(DEBUG_USART_NUM == 2)
#define DEBUG_TX_GPIO_PORT GPIOA /* GPIO 端口 */
#define DEBUG_TX_GPIO_CLK_PORT RCC_APB2Periph_GPIOA /* GPIO 端口时钟 */
#define DEBUG_TX_GPIO_PIN GPIO_Pin_2 /* 对应PIN脚 */
#define DEBUG_RX_GPIO_PORT GPIOA /* GPIO 端口 */
#define DEBUG_RX_GPIO_CLK_PORT RCC_APB2Periph_GPIOA /* GPIO 端口时钟 */
#define DEBUG_RX_GPIO_PIN GPIO_Pin_3 /* 对应PIN脚 */
#define DEBUG_USARTX USART2 /* 串口号 */
#define DEBUG_USARTX_CLK_PORT RCC_APB1Periph_USART2 /* 串口外设时钟位 */
#define DEBUG_APBXCLKCMD RCC_APB1PeriphClockCmd /* 串口外设时钟 */
#define DEBUG_BAUDRATE 115200 /* 波特率 */
#elif(DEBUG_USART_NUM == 3)
#define DEBUG_TX_GPIO_PORT GPIOB /* GPIO 端口 */
#define DEBUG_TX_GPIO_CLK_PORT RCC_APB2Periph_GPIOB /* GPIO 端口时钟 */
#define DEBUG_TX_GPIO_PIN GPIO_Pin_10 /* 对应PIN脚 */
#define DEBUG_RX_GPIO_PORT GPIOB /* GPIO 端口 */
#define DEBUG_RX_GPIO_CLK_PORT RCC_APB2Periph_GPIOB /* GPIO 端口时钟 */
#define DEBUG_RX_GPIO_PIN GPIO_Pin_11 /* 对应PIN脚 */
#define DEBUG_USARTX USART3 /* 串口号 */
#define DEBUG_USARTX_CLK_PORT RCC_APB1Periph_USART3 /* 串口外设时钟位 */
#define DEBUG_APBXCLKCMD RCC_APB1PeriphClockCmd /* 串口外设时钟 */
#define DEBUG_BAUDRATE 115200 /* 波特率 */
#endif
void DEBUG_USART_PinConfig(void);
void DEBUG_USART_MODEConfig(void);
void DEBUG_USART_Init(void);
#endif /*__BSP_DEBUG_H*/
| 引脚 | APB2总线 USART1 | APB1总线 USART2 | APB1总线 USART3 | APB1总线 UART4 | APB1总线 UART5 |
|---|---|---|---|---|---|
| TX | PA9 | PA2 | PB10 | PC10 | PC12 |
| RX | PA10 | PA3 | PB11 | PC11 | PD2 |
| SCLK | PA8 | PA4 | PB12 | ||
| nCTS | PA11 | PA0 | PB13 | ||
| nRTS | PA12 | PA1 | PB14 |
可以看到串口1的发送引脚在PA9,接收是PA10,串口2的发送引脚在PA2,接收是PA3,串口3的发送引脚是在PB10,接收引脚在PB11
2.2 串口io发送/接收配置
c
/**
* @brief 初始化控制 DEBUG 串口 的IO
* @param 无
* @retval 无
*/
void DEBUG_USART_PinConfig(void)
{
GPIO_InitTypeDef GPIO_InitStruct = { 0 };
#if 1
/* 开启 DEBUG 相关的GPIO外设/端口时钟 */
RCC_APB2PeriphClockCmd(DEBUG_TX_GPIO_CLK_PORT, ENABLE); //开启GPIOA 端口时钟
// 配置引脚:选择GPIOA的Pin9
GPIO_InitStruct.GPIO_Pin = DEBUG_TX_GPIO_PIN;
// 配置输出速度:50MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
// 配置工作模式:推挽复用
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(DEBUG_TX_GPIO_PORT, &GPIO_InitStruct);
#endif
#if 1
/* 开启 DEBUG 相关的GPIO外设/端口时钟 */
RCC_APB2PeriphClockCmd(DEBUG_RX_GPIO_CLK_PORT, ENABLE); //开启GPIOA 端口时钟
// 配置引脚:选择GPIOA的Pin10
GPIO_InitStruct.GPIO_Pin = DEBUG_RX_GPIO_PIN;
// 配置输出速度:50MHz
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
// 配置工作模式:上拉输入/浮空输入
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(DEBUG_RX_GPIO_PORT, &GPIO_InitStruct);
#endif
}

在数据手册可以看到,串口的发送 IO 口模式配置的是推挽复用输出模式,串口的接收 IO 口模式配置的是浮空输入模式 / 上拉输入模式
3.数据接收与发送


发送数据过程:数据寄存器 DR → 移位寄存器 → TX 引脚 (PA9)
接收数据过程:RX引脚 → 接收移位寄存器 → 接收数据寄存器DR → CPU读取
当接收移位寄存器采集完一整个字节的串行bit,将数据转移到接收数据寄存器DR之后,硬件自动将RXNE标志置1。
接收数据部分:
RXNE=1含义:接收数据寄存器DR里面已经存放了新收到的有效数据,也就是说移位寄存器全部转移到数据寄存器(DR)中,等待CPU读取。
发送数据部分:
TXE = 1 理解:
当数据寄存器 DR里面的数据,全部转移到发送移位寄存器之后,硬件自动把 TXE 置 1。
TC = 1理解:
TC=1:移位寄存器里面所有 bit,全部通过 TXD 引脚发送出去,一整个字节发送全部完成。
当CPU读取DR寄存器拿到数据之后,硬件自动将RXNE标志清零,准备接收下一个字节。
这个自动清除0的理解:

当调用这个函数的时候,通过return DR(数据寄存器)的值,就会触发自动清除RXNE的标志位
代码部分
c
/* 数据回显 */
if (USART_GetFlagStatus(DEBUG_USARTX, USART_FLAG_RXNE) == SET)
{
uint8_t data_temp = USART_ReceiveData(DEBUG_USARTX); //读取数据寄存器,读取后对应的寄存器会被复位
USART_SenByte(DEBUG_USARTX, data_temp);
USART_ClearFlag(DEBUG_USARTX, USART_FLAG_RXNE); //清除接收标志位
}
当上位机发送数据,RXD引脚接收数据,接收到的数据到移位寄存器,然后移位寄存器转移到数据寄存器,通过if判断RXNE是否置1,如果置1,就可以直接在DR(数据寄存器)读取了,而这个if用来检测移位寄存器是否全部转移到数据寄存器中了,如果转移成功标志这RXNE置1,最后可以直接读取数据寄存器的值。
读取数据调用函数
c
uint8_t data_temp = USART_ReceiveData(DEBUG_USARTX);
这个函数是直接return DR的值
相当于当调用这个读取DR寄存器值的函数,就自动将RXNE标志位清0
如果不放心也可以手动清0,调用该函数
c
USART_ClearFlag(DEBUG_USARTX, USART_FLAG_RXNE); //清除接收标志位
当数据成功读取到临时变量data_temp中,此时可以调用自己编写的串口发送数据函数,直接发送。