stm32F103C8T6标准库串口发送之发送字节2

目录

1.屏蔽870警告

--diag_suppress=870 直白讲解(Keil5 专用)

1、870 警告是什么?

#870-D:invalid multibyte character sequence(无效多字节字符)

出现原因:代码里写了中文注释、中文字符串(UTF-8 汉字),Keil 编译器不认中文编码,编译就疯狂弹窗警告 870。

只是警告(Warning),不是报错,不影响程序编译、下载运行。

2、加--diag_suppress=870的作用

放到工程 Options→C/C++→Misc Controls 框里:

全局屏蔽整个工程所有 870 号警告,编译不再弹出中文相关的冗余警告。

不加这个的坏处:

代码里只要有中文注释,编译框疯狂刷屏一堆#870-D警告,一堆无用信息,容易把真正有用的报错、重要警告淹没,不好排查 BUG。

3、两种屏蔽写法区别

全局(你截图这种):--diag_suppress=870

工程所有文件全部屏蔽 870 警告,一劳永逸,整项目生效。

单文件屏蔽:代码开头写 #pragma diag_suppress 870

只对当前.c 文件屏蔽 870,别的文件照常提示警告。

4、一句话总结

加它 = 关掉「中文注释 / 中文字符串」带来的烦人 870 警告,编译窗口变干净,方便找真正错误。

补充:不想屏蔽的替代方案

把文件编码改成 GB2312,Keil 就能正常识别中文,自然不会报 870 警告,但中文跨设备打开容易乱码,所以大部分工程师直接加这条屏蔽指令。

2. 串口调试工具设置

勾选HEX,日志模式,清除接收区,打开串口。

3.STM32串口对应引脚

SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式。

USART引脚在STM32F103ZET6芯片具体分布见表STM32F103ZET6芯片的

USART引脚。

引脚 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

STM32F103ZET6系统控制器有三个USART和两个UART,其中USART1和时钟来源于APB2总线时钟,其最大频率为72MHz,其他四个的时钟来源于APB1总线时钟,其最大频率为36MHz,UART只是异步传输功能,所以没有SCLK、nCTS和nRTS功能引脚。

4.debug代码理解

4.1 debug_gpio配置

c 复制代码
void DEBUG_USART_PinConfig(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = { 0 };
    
    /* 开启 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);
    
}

4.1.1 TX引脚GPIO模式配置理解

对串口发送的GPIO引脚模式配置:推挽复用输出

推挽复用输出模式(GPIO)通俗理解

一、一句话说清它是干嘛的

推挽复用输出模式 = 让 GPIO 引脚,交给片上外设(比如串口、SPI、I2C、定时器)来控制,而不是普通的高低电平输出。

它的核心是 "复用":这个引脚不再受普通 GPIO 控制,而是被指定给某个外设专用,用来输出外设信号(比如串口的 TX 发送脚、定时器的 PWM 输出脚)。

二、什么时候设置这个模式?

当你需要用引脚的 外设功能(复用功能)时,就必须设置成推挽复用输出模式。

最常见的场景:

串口发送(TX 引脚)

比如你之前用的 USART1 的 PA9(TX)、USART2 的 PA2(TX),这些引脚要发送串口数据,就必须配置成推挽复用输出模式,让串口外设来控制引脚输出高低电平时序。

定时器 PWM 输出

比如定时器的通道引脚,要输出 PWM 波控制电机、LED 呼吸灯,必须配置成推挽复用输出模式,让定时器外设来控制引脚电平。

SPI、I2S、CAN 等通信的输出引脚

比如 SPI 的 SCK、MOSI 引脚,需要由 SPI 外设控制输出时钟和数据,就用这个模式。

三、和普通推挽输出的区别(关键)

模式 控制权 用途
普通推挽输出(GPIO_Mode_Out_PP) CPU 直接控制 输出普通高低电平,比如点亮 LED、控制继电器
推挽复用输出(GPIO_Mode_AF_PP) 片上外设控制 输出外设信号,比如串口 TX、PWM、SPI 时钟等

四、举个你熟悉的例子(串口 TX 引脚配置)

以你之前用的 USART1 的 PA9(TX)为例,配置步骤就是:

打开 GPIOA 和 USART1 的时钟

把 PA9 设置为 推挽复用输出模式(GPIO_Mode_AF_PP)

配置 PA9 的复用功能为 USART1_TX

初始化 USART1

此时,PA9 引脚就不再受 GPIO 控制,而是由 USART1 外设来控制,自动输出串口数据的高低电平时序,你只需要往 USART_DR 写数据,引脚就会自动把数据发出去。

五、补充:为什么是 "推挽"?

推挽模式的优势是:

高低电平驱动能力强,能直接驱动负载(或外设)

输出电平稳定,不会被外部电路拉偏

非常适合串口、PWM 这种需要稳定输出信号的场景。

六、什么时候不要用它?

当你只是想用引脚做普通高低电平控制(比如按键输入、LED 点亮)时,不要用这个模式,直接用普通推挽输出或输入模式即可。

4.1.2 RX引脚GPIO模式配置理解

先给结论:串口 RX 必须是输入模式;选浮空或上拉,都是为了让 "空闲时电平稳定、不乱跳、不误触发起始位"。

一、RX 为什么必须是 "输入模式"

TX 是发送:STM32 主动往外发数据 → 配置成推挽复用输出。

RX 是接收:STM32 被动听别人发来的数据 → 只能配置成输入模式(浮空 / 上拉 / 下拉),不能是输出。

二、为什么常用 "浮空输入" 或 "上拉输入"

串口是异步通信:

空闲状态:RX 应该是高电平(逻辑 1)。

起始位:一个下降沿(1→0),表示一帧开始。

1)浮空输入(GPIO_Mode_IN_FLOATING)

内部不上拉、不下拉,电平完全由外部电路决定。

优点:外部发送端(TX)是推挽输出时,驱动能力强、电平干净,浮空最标准。

风险:如果外部断开 / 悬空,引脚像 "天线",噪声会让电平乱跳,误判起始位。

典型:两个 STM32 互发(TX→RX 直连),常用浮空。

2)上拉输入(GPIO_Mode_IPU)

内部有约 40kΩ 上拉电阻到 VDD;空闲 / 悬空时自动为高电平。

优点:不怕悬空、抗干扰强,不会乱跳,保证空闲是高,不容易误触发起始位。

小缺点:外部拉低时要多克服一个上拉电流,但串口电平足够,不影响。

典型:布线长、噪声大、外部可能断开,优先上拉。

3)为什么不推荐 "下拉输入"

下拉空闲是低电平,而串口空闲必须是高 → 一直误判 "在发起始位",完全不能用。

三、浮空 vs 上拉

模式 空闲电平 抗干扰 适用场景
浮空输入(IN_FLOATING) 由外部决定(理想高) 较弱 短距离、直连、外部TX是推挽输出
上拉输入(IPU) 固定高电平 长距离、噪声大、外部可能悬空/断开

四、一句话总结

RX 配置为浮空或上拉输入,核心是保证串口空闲时 RX 稳定在高电平,防止噪声误触发起始位,确保接收可靠;下拉输入因空闲为低,不符合串口协议,不能用。

4.2 波特率配置

c 复制代码
/**
  * @brief  配置 DEBUG 串口模式
  * @param  无
  * @retval 无
  */
  
void DEBUG_USART_MODEConfig(void)
{
    USART_InitTypeDef usart_InitStruct = { 0 };
    
    /* 开启 DEBUG 相关的GPIO外设/端口时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //开启GPIOA 端口时钟
    
    /* 配置串口的工作参数 */
    
    //波特率配置
    usart_InitStruct.USART_BaudRate            = ;
    //硬件流控开关配置
    usart_InitStruct.USART_HardwareFlowControl = ;
    //配置工作模式:
    usart_InitStruct.USART_Mode                = ;
    //校验
    usart_InitStruct.USART_Parity              = ;
    //停止位
    usart_InitStruct.USART_StopBits            = ;
    //数据帧长度
    usart_InitStruct.USART_WordLength          = ;
    
    USART_Init(USART1, &usart_InitStruct);
    
}
c 复制代码
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //开启GPIOA 端口时钟

因为我设置PA9是TX在总线APB2并且在串口1USART1上,所以需要开启串口1时钟总线

串口结构体成员定义:

c 复制代码
typedef struct
{
  uint32_t USART_BaudRate;            /*!< This member configures the USART communication baud rate.
                                           The baud rate is computed using the following formula:
                                            - IntegerDivider = ((PCLKx) / (16 * (USART_InitStruct->USART_BaudRate)))
                                            - FractionalDivider = ((IntegerDivider - ((u32) IntegerDivider)) * 16) + 0.5 */

  uint16_t USART_WordLength;          /*!< Specifies the number of data bits transmitted or received in a frame.
                                           This parameter can be a value of @ref USART_Word_Length */

  uint16_t USART_StopBits;            /*!< Specifies the number of stop bits transmitted.
                                           This parameter can be a value of @ref USART_Stop_Bits */

  uint16_t USART_Parity;              /*!< Specifies the parity mode.
                                           This parameter can be a value of @ref USART_Parity
                                           @note When parity is enabled, the computed parity is inserted
                                                 at the MSB position of the transmitted data (9th bit when
                                                 the word length is set to 9 data bits; 8th bit when the
                                                 word length is set to 8 data bits). */
 
  uint16_t USART_Mode;                /*!< Specifies wether the Receive or Transmit mode is enabled or disabled.
                                           This parameter can be a value of @ref USART_Mode */

  uint16_t USART_HardwareFlowControl; /*!< Specifies wether the hardware flow control mode is enabled
                                           or disabled.
                                           This parameter can be a value of @ref USART_Hardware_Flow_Control */
} USART_InitTypeDef;

成员1 uint32_t USART_BaudRate 对波特率设置,可以看到上位机上面的波特率是115200,所以这里设置115200

成员2 uint16_t USART_WordLength 数据帧长度设置

在注释里面标记这句

@ref USART_Word_Length,告诉我们在 USART_Word_Length去找

这里定义的有两个数据长度,8和9,这里设置8

成员6 uint16_t USART_HardwareFlowControl 流控设置

在注释里面标记这句

@ref USART_Hardware_Flow_Control 告诉我们在USART_Hardware_Flow_Control 去找

这里设置USART_HardwareFlowControl_None,无流控

成员5 uint16_t USART_Mode 模式设置

在注释里面标记这句

@ref USART_Mode 告诉我们在 USART_Mode 去找

这里设置 USART_Mode_Tx 发送模式

成员4 uint16_t USART_Parity 校验设置

在注释里面标记这句

@ref USART_Parity 告诉我们在USART_Parity 去找

这里设置USART_Parity_No 无校验

成员3 uint16_t USART_StopBits 停止位设置

在注释里面标记这句

@ref USART_Stop_Bits 告诉我们在USART_Stop_Bits 去找

这里设置USART_StopBits_1 1个停止位

5.发送数据代码理解

c 复制代码
/**
  * @brief  发送一字节函数
  * @param  pUSARTx:USARTx(X=1,2,3)/UARTx(x=4,5)
  * @param  ch:要发送的数据
  * @note   
  * @retval 无
  */
  
void USART_SenByte(USART_TypeDef* pUSARTx, uint8_t ch)
{
    /* 等待发送完成 */
    while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
    
    /*发送一字节数据到pUSARTx*/
    USART_SendData(pUSARTx, ch);

		/* 等待发送数据寄存器为空 */
    while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

/**
  * @brief  发送8位的数组函数
  * @param  pUSARTx:USARTx(X=1,2,3)/UARTx(x=4,5)
  * @param  arrary:要发送的数组
  * @param  num: 数组大小
  * @note   
  * @retval 无
  */
  
void USART_SenArray(USART_TypeDef* pUSARTx, uint8_t* array, uint32_t num)
{
    /* 等待发送完成 */
    while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
    
    for(uint32_t i = 0; i < num; i++)
    {
        /*发送一字节数据到pUSARTx*/
        USART_SendData(pUSARTx, array[i]);
        
        /* 等待发送数据寄存器为空 */
        while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
    }  
} 

USART_GetFlagStatus 函数大白话详解

这个函数是 STM32 标准库串口的 "状态标志读取工具",作用是帮你查询串口的各种状态(比如发完没、收到没、有没有错误)。

一、一句话搞懂它

USART_GetFlagStatus(USARTx, USART_FLAG_xxx)

= 查询串口 USARTx 的某个状态标志位是否为 1(SET)

返回值是:SET(标志位为 1)或 RESET(标志位为 0)。

如果返回值是 RESET 则发送数据未完成

如果返回值是 SET 则发送数据完成

二、函数参数拆解

c 复制代码
FlagStatus USART_GetFlagStatus(
    USART_TypeDef* USARTx,    // 参数1:指定要查哪个串口(USART1/2/3等)
    uint16_t USART_FLAG       // 参数2:指定要查哪个标志位
);

参数 1:USARTx

就是你之前问的串口指针,用来指定你要操作的串口外设,比如:

USART1:查询串口 1 的状态

USART2:查询串口 2 的状态

参数 2:USART_FLAG

标志位 含义(大白话) 常用场景
USART_FLAG_TXE 发送数据寄存器空 检查"数据能不能写入发送寄存器"
USART_FLAG_TC 一帧数据发送完成 检查"数据是不是真的发完了"
USART_FLAG_RXNE 接收数据寄存器非空 检查"有没有收到数据"
USART_FLAG_IDLE 总线空闲 检查"一整包数据是不是收完了"
USART_FLAG_ORE 接收溢出错误 检查"有没有数据没读被覆盖"

三、最常用的 3 个例子(直接复制就能用)

  1. 等待发送寄存器空(写数据前)
c 复制代码
// 等TXE=1,确保上一个数据已经进移位寄存器了
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART1->DR = 'A'; // 现在可以安全写数据了
  1. 等待一帧数据完全发送完成
c 复制代码
USART1->DR = 'A';
// 等TC=1,确保数据已经全部发出去了
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
  1. 检查有没有收到数据
c 复制代码
if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
{
    uint8_t data = USART1->DR; // 读走收到的数据
}

四、关键细节(你一定要记)

USART_FLAG_TC 必须手动清零

它不会自动清零,发完数据后需要手动调用:

c 复制代码
USART_ClearFlag(USART1, USART_FLAG_TC);

不然下次查询永远是 SET。

USART_FLAG_IDLE 清零方式特殊

必须先读 SR 再读 DR,才能清除标志位,函数也无法直接清除它。

和中断的区别

USART_GetFlagStatus:查询标志位(轮询方式),不触发中断

对应的中断(如TXEIE):标志位为 1 时自动触发中断,适合高实时性场景

相关推荐
fffzd1 小时前
STM32:时钟树与时钟源
单片机·嵌入式硬件·嵌入式软件·时钟树·时钟源
嵌入式小站1 小时前
STM32 零基础可移植教程 22:SPI 入门,先读一个外部 Flash
stm32·单片机·嵌入式硬件
崇山峻岭之间1 小时前
单片机USB 鼠标键盘实验
单片机·嵌入式硬件·计算机外设
大卡片1 小时前
单片机第二次答辩
单片机·嵌入式硬件
广州灵眸科技有限公司11 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) 开发(编译)方式说明
linux·服务器·单片机·嵌入式硬件·电脑
IT_阿水11 小时前
STM32 HAL库输入捕获配置
stm32·单片机·嵌入式硬件
nuoxin11411 小时前
WILX1200HC-5TG144I替代 LCMXO2-1200HC-5TG144I(富利威)
人工智能·嵌入式硬件·fpga开发·电脑·硬件工程·dsp开发
zlinear数据采集卡12 小时前
555触摸延时开关深度解析:从电路原理到智能楼道灯应用
单片机·嵌入式硬件
国科安芯15 小时前
国科安芯推出商业航天级抗辐照全双工 RS485/422 收发器 ASC491S2Y
网络·分布式·单片机·架构·安全性测试