【STM32-学习笔记-7-】USART串口通信

news2025/1/15 2:45:06

文章目录

  • USART串口通信
    • Ⅰ、硬件电路
    • Ⅱ、常见的电平标准
    • Ⅲ、串口参数及时序
    • Ⅳ、STM32的USART简介
      • 数据帧
      • 起始位侦测
      • 数据采样
      • 波特率发生器
    • Ⅴ、USART函数介绍
    • Ⅵ、USART_InitTypeDef结构体参数
      • 1、USART_BaudRate
      • 2、USART_WordLength
      • 3、USART_StopBits
      • 4、USART_Parity
      • 5、USART_Mode
      • 6、USART_HardwareFlowControl
    • Ⅶ、串口发送数据
      • 可变参数函数,模拟实现printf
    • Ⅷ、串口接收数据(包含发送)
    • Ⅸ、USART收发数据包
      • 1、收发HEX数据包
      • 2、收发文本数据包

USART串口通信

USARTUniversal Synchronous/Asynchronous Receiver/Transmitter,通用同步/异步收发器)是一种用于串行通信的硬件模块,它支持同步(需要SCL时钟线)和异步两种通信模式

Ⅰ、硬件电路

image-20241228154308723

  • TX 为发送端, RX 为接收端
  • 若是设备之间的电平标准不一致时,则需要加上电平转换芯片

Ⅱ、常见的电平标准

  1. TTL电平
    • 供电范围在0~5V
    • 输出:
      • 高电平1大于2.7V
      • 低电平0小于0.5V
    • 输入:
      • 高电平1大于2.0V
      • 低电平0小于0.8V
    • TTL电平输入脚悬空时内部认为是高电平,且TTL电平输出不能驱动CMOS电平输入
  2. CMOS电平
    • 供电范围在3~15V
    • 输出:
      • 高电平1大于4.6V
      • 低电平0小于0.05V
    • 输入:
      • 高电平1大于3.5V
      • 低电平0小于1.5V
  3. LVTTL电平
    • 是TTL的一种低功耗变种,供电电压通常小于等于3.3V
    • 输出:
      • 高电平1大于2.4V
      • 低电平0小于0.4V
    • 输入:
      • 高电平1大于2.0V
      • 低电平0小于0.8V
  4. RS232电平
    • 输出:
      • -5~-15V 输出1
      • +5~+15V 输出0
    • 输入:
      • -3~-15V 输入1
      • +3~+15V 输入0
  5. LVDS电平
    • 低电压差分信号,驱动器由驱动差分线对的电流源组成,电流通常为3.5mA
    • 接收器具有很高的输入阻抗,输入端允许信号上携带的直流偏置电平范围为0.227~2.173V
  6. RS485电平
    • 采用差分传输方式,输出A、B之间的电压差:高电平+2~+6V低电平-2~-6V
  • STM32使用的是TTL电平

Ⅲ、串口参数及时序

  1. 波特率(Baud Rate)
    • 波特率是串口通信中每秒传输的符号数,通常以bps(位/秒)为单位
    • 波特率必须在通信双方之间匹配,否则会导致数据传输错误
  2. 数据位(Data Bits)
    • 数据位是指每个字符中用于传输实际数据的位数
    • 常见的数据位设置有8位
  3. 停止位(Stop Bits)
    • 停止位是在每个字符传输结束后,用于标识字符结束的位数
    • 可以是1位、1.5位或2位停止位
  4. 奇偶校验(Parity)
    • 奇偶校验是一种错误检测机制,通过在数据位后面添加一个校验位来实现
    • 可以设置为无校验(None)、奇校验(Odd)、偶校验(Even)或标记/空格校验(Mark/Space)
  • 波特率:串口通信的速率

  • 起始位:标志一个数据帧的开始,固定为低电平

  • 数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行

  • 校验位:用于数据验证,根据数据位计算得来

  • 停止位:用于数据帧间隔,固定为高电平

  • 一个数据帧10位
    • 起始位:1bit
    • 数据位:8bit(1byte)
    • 停止位:1bit

image-20241228160015933

  • 一个数据帧11位
    • 起始位:1bit
    • 数据位:8bit
    • 校验位:1bit
    • 停止位:1bit

image-20241228160032152

  • 实测串口时序

image-20241228160155805

Ⅳ、STM32的USART简介

  • USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里

  • 自带波特率发生器,最高达4.5Mbits/s(可据此配置通讯的波特率

  • 可配置数据位长度(无校验位8bit,有校验位9bit)、停止位长度(0.5/1/1.5/2 确定了帧的间隔

  • 可选校验位(无校验/奇校验/偶校验)

  • 支持同步模式(有时钟CLK输出)、硬件流控制(可控的发送和接收数据)、DMA(串口支持DMA转运数据)、智能卡、IrDA、LIN

  • STM32F103C8T6 USART资源: USART1(挂载在上APB2总线上)、 USART(APB1)、 USART3(APB1

image-20241229145512719

  • 流控:
    • nCTS: 清除发送,若是高电平,在当前数据传输结束时阻断下一次的数据发送(判断对方是否准备好接收数据
    • nRTS: 发送请求,若是低电平,表明USART准备好接收数据(告诉对方自己是否准备好接收数据

image-20241229152925780

  • TXERXNE 是判断发送状态和接收状态的重要标志位

image-20241229165609863

  • TDRRDR都是通过DR寄存器来实现其功能

数据帧

image-20241229170041255

image-20241229172307272

起始位侦测

image-20241229172941339

数据采样

image-20241229173342805

  • 进行三次数据采样,增加容错
  • 采样时钟是波特率的16倍

波特率发生器

  • 发送器和接收器的波特率由波特率寄存器BRR里的DIV确定

  • 计算公式:

    • 波特率 = f P C L K 2 / 1 16 ∗ D I V 波特率 = \frac{f_{PCLK2/1} }{16 * DIV} 波特率=16DIVfPCLK2/1

    • 解释:PCLK1或PLCK2的时钟频率除以16倍的DIV

image-20241229174442754

  • DIV分为整数部分(12bit)和小数部分(4bit),整数部分高位补0,小数部分低位补0

Ⅴ、USART函数介绍

// 重置指定的USART为默认值
void USART_DeInit(USART_TypeDef* USARTx);

// 初始化指定的USART,根据初始化结构体配置参数
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
// 初始化USART初始化结构体的默认值
void USART_StructInit(USART_InitTypeDef* USART_InitStruct);
// 初始化指定的USART时钟,根据时钟初始化结构体配置参数
void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef* USART_ClockInitStruct);
// 初始化USART时钟初始化结构体的默认值
void USART_ClockStructInit(USART_ClockInitTypeDef* USART_ClockInitStruct);

// 开启或关闭指定的USART
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);

// 开启或关闭USART的中断
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);

// 开启或关闭USART的DMA请求
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState);

// 设置USART地址
void USART_SetAddress(USART_TypeDef* USARTx, uint8_t USART_Address);

// 配置USART唤醒模式
void USART_WakeUpConfig(USART_TypeDef* USARTx, uint16_t USART_WakeUp);

// 开启或关闭USART接收器唤醒功能
void USART_ReceiverWakeUpCmd(USART_TypeDef* USARTx, FunctionalState NewState);

// 配置USART LIN断裂检测长度
void USART_LINBreakDetectLengthConfig(USART_TypeDef* USARTx, uint16_t USART_LINBreakDetectLength);

// 开启或关闭USART LIN模式
void USART_LINCmd(USART_TypeDef* USARTx, FunctionalState NewState);

// 通过USART发送数据
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
// 通过USART接收数据
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);

// 发送USART断点信号
void USART_SendBreak(USART_TypeDef* USARTx);

// 设置USART保护时间
void USART_SetGuardTime(USART_TypeDef* USARTx, uint8_t USART_GuardTime);

// 设置USART预分频器
void USART_SetPrescaler(USART_TypeDef* USARTx, uint8_t USART_Prescaler);

// 开启或关闭USART智能卡模式
void USART_SmartCardCmd(USART_TypeDef* USARTx, FunctionalState NewState);
// 开启或关闭USART智能卡NACK应答
void USART_SmartCardNACKCmd(USART_TypeDef* USARTx, FunctionalState NewState);
// 开启或关闭USART半双工模式
void USART_HalfDuplexCmd(USART_TypeDef* USARTx, FunctionalState NewState);

// 开启或关闭USART 8位过采样模式
void USART_OverSampling8Cmd(USART_TypeDef* USARTx, FunctionalState NewState);

// 开启或关闭USART单线方法
void USART_OneBitMethodCmd(USART_TypeDef* USARTx, FunctionalState NewState);

// 配置USART IrDA模式
void USART_IrDAConfig(USART_TypeDef* USARTx, uint16_t USART_IrDAMode);

// 开启或关闭USART IrDA模式
void USART_IrDACmd(USART_TypeDef* USARTx, FunctionalState NewState);

// 获取USART标志状态
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
// 清除USART标志
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);

// 获取USART中断状态
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
// 清除USART中断待处理位
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

Ⅵ、USART_InitTypeDef结构体参数

成员名称描述
uint32_t USART_BaudRateUSART通信波特率,使用公式计算:IntegerDivider = ((PCLKx) / (16 * USART_BaudRate))
uint16_t USART_WordLength指定每帧传输或接收的数据位数量,可以是8位、9位等,取决于@ref USART_Word_Length
uint16_t USART_StopBits指定传输的停止位数量,可以是1位或2位,取决于@ref USART_Stop_Bits
uint16_t USART_Parity指定奇偶校验模式,包括奇校验、偶校验或无校验,取决于@ref USART_Parity
uint16_t USART_Mode指定是否启用接收或发送模式,取决于@ref USART_Mode
uint16_t USART_HardwareFlowControl指定是否启用硬件流控制模式,如RTS/CTS,取决于@ref USART_Hardware_Flow_Control成员名称

1、USART_BaudRate

  • 类型uint32_t(无符号32位整数)
  • 用途配置USART通信的波特率
  • 说明:通过设置这个成员,可以定义USART通信的速率,即每秒传输的比特数。波特率的计算涉及到外设时钟频率(PCLKx)和这个成员的值。计算公式如前所述,用于确定整数除数和分数除数,以设置USART的时钟分频,确保正确的波特率

2、USART_WordLength

  • 类型uint16_t(无符号16位整数)

  • 用途指定每帧传输或接收的数据位数量

  • 说明:这个参数决定了数据帧中数据位的长度。它可以是8位、9位等,具体值取决于@ref USART_Word_Length枚举

    • @ref USART_Word_Length:

    • 配置USART(通用同步/异步收发传输器)的数据位长度

      宏定义

      1. USART_WordLength_8b
        • ((uint16_t)0x0000)
        • 描述:定义了一个8位数据长度的宏。当USART配置为8位数据长度时,每个数据帧包含8个数据位
      2. USART_WordLength_9b
        • ((uint16_t)0x1000)
        • 描述:定义了一个9位数据长度的宏。当USART配置为9位数据长度时,每个数据帧包含9个数据位

      宏函数

      1. IS_USART_WORD_LENGTH(LENGTH)
        • 描述:这是一个宏函数,用于检查给定的数据位长度是否有效
        • 参数LENGTH,代表USART的数据位长度
        • 功能:检查LENGTH是否等于USART_WordLength_8bUSART_WordLength_9b
        • 返回值:如果LENGTH有效,返回1(真),否则返回0(假)

      表格:

      宏定义描述
      USART_WordLength_8b0x00008位数据长度
      USART_WordLength_9b0x10009位数据长度
      宏函数描述
      IS_USART_WORD_LENGTH(LENGTH)检查LENGTH是否为有效的USART数据位长度

3、USART_StopBits

  • 类型uint16_t(无符号16位整数)

  • 用途指定传输的停止位数量

  • 说明:这个参数定义了在数据帧结束后传输的停止位的数量,可以是1位或2位,具体值取决于@ref USART_Stop_Bits枚举

    • @ref USART_Stop_Bits:

    • 定义了USART(通用同步/异步收发传输器)的停止位配置

      宏定义

      1. USART_StopBits_1
        • ((uint16_t)0x0000)
        • 描述:定义了一个1个停止位的宏。在USART通信中,1个停止位是最常见的配置
      2. USART_StopBits_0_5
        • ((uint16_t)0x1000)
        • 描述:定义了0.5个停止位的宏。这种配置不常见,主要用于某些特定的通信协议
      3. USART_StopBits_2
        • ((uint16_t)0x2000)
        • 描述:定义了2个停止位的宏。这种配置用于提高数据传输的可靠性,特别是在噪声较大的通信环境中
      4. USART_StopBits_1_5
        • ((uint16_t)0x3000)
        • 描述:定义了1.5个停止位的宏。这种配置同样不常见,主要用于某些特定的通信协议

      宏函数

      1. IS_USART_STOPBITS(STOPBITS)
        • 描述:这是一个宏函数,用于检查给定的停止位设置是否有效
        • 参数STOPBITS,代表USART的停止位设置
        • 功能:检查STOPBITS是否等于USART_StopBits_1USART_StopBits_0_5USART_StopBits_2USART_StopBits_1_5中的任一个
        • 返回值:如果STOPBITS有效,返回1(真),否则返回0(假)

      表格:

      宏定义描述
      USART_StopBits_10x00001个停止位
      USART_StopBits_0_50x10000.5个停止位
      USART_StopBits_20x20002个停止位
      USART_StopBits_1_50x30001.5个停止位
      宏函数描述
      IS_USART_STOPBITS(STOPBITS)检查STOPBITS是否为有效的USART停止位设置

4、USART_Parity

  • 类型uint16_t(无符号16位整数)

  • 用途指定奇偶校验模式

  • 说明:这个参数决定了是否启用奇偶校验,以及使用哪种类型的校验(奇校验、偶校验或无校验)。当启用校验时,计算出的校验位会被插入到传输数据的最高有效位(MSB)位置。具体值取决于@ref USART_Parity枚举

    • @ref USART_Parity:

    • 定义了USART(通用同步/异步收发传输器)的奇偶校验配置

      宏定义

      1. USART_Parity_No
        • ((uint16_t)0x0000)
        • 描述:定义了一个无奇偶校验的宏。当设置为无奇偶校验时,USART通信不包含校验位
      2. USART_Parity_Even
        • ((uint16_t)0x0400)
        • 描述:定义了一个偶校验的宏。当设置为偶校验时,USART通信中的数据帧会包含一个校验位,使得数据位加上校验位的总和为偶数
      3. USART_Parity_Odd
        • ((uint16_t)0x0600)
        • 描述:定义了一个奇校验的宏。当设置为奇校验时,USART通信中的数据帧会包含一个校验位,使得数据位加上校验位的总和为奇数

      宏函数

      1. IS_USART_PARITY(PARITY)
        • 描述:这是一个宏函数,用于检查给定的奇偶校验设置是否有效
        • 参数PARITY,代表USART的奇偶校验设置
        • 功能:检查PARITY是否等于USART_Parity_NoUSART_Parity_EvenUSART_Parity_Odd中的任一个
        • 返回值:如果PARITY有效,返回1(真),否则返回0(假)

      表格:

      宏定义描述
      USART_Parity_No0x0000无奇偶校验
      USART_Parity_Even0x0400偶校验
      USART_Parity_Odd0x0600奇校验
      宏函数描述
      IS_USART_PARITY(PARITY)检查PARITY是否为有效的USART奇偶校验设置

5、USART_Mode

  • 类型uint16_t(无符号16位整数)

  • 用途指定是否启用接收或发送模式

  • 说明:这个参数决定了USART是处于接收模式、发送模式,还是两者都启用。具体值取决于@ref USART_Mode枚举

    • @ref USART_Mode:

    • 定义了USART(通用同步/异步收发传输器)的工作模式

      宏定义

      1. USART_Mode_Rx
        • ((uint16_t)0x0004)
        • 描述:定义了一个仅接收模式的宏。当设置为仅接收模式时,USART仅用于接收数据
      2. USART_Mode_Tx
        • ((uint16_t)0x0008)
        • 描述:定义了一个仅发送模式的宏。当设置为仅发送模式时,USART仅用于发送数据

      宏函数

      1. IS_USART_MODE(MODE)
        • 描述:这是一个宏函数,用于检查给定的工作模式设置是否有效
        • 参数MODE,代表USART的工作模式设置
        • 功能:检查MODE是否为有效的接收模式、发送模式或两者的组合(即接收和发送模式)。它通过与0xFFF3进行按位与操作来确保只有接收和发送模式位被设置,其他位应为0。同时,也检查MODE不为0,因为0表示没有启用任何模式
        • 返回值:如果MODE有效,返回1(真),否则返回0(假)

      表格:

      宏定义描述
      USART_Mode_Rx0x0004仅接收模式
      USART_Mode_Tx0x0008仅发送模式
      宏函数描述
      IS_USART_MODE(MODE)检查MODE是否为有效的USART工作模式设置

6、USART_HardwareFlowControl

  • 类型uint16_t(无符号16位整数)

  • 用途指定是否启用硬件流控制模式

  • 说明:硬件流控制用于控制数据的传输速率,以防止接收器溢出。这个参数决定了是否启用硬件流控制,如RTS/CTS(请求发送/清除发送)。具体值取决于@ref USART_Hardware_Flow_Control枚举

    • @ref USART_Hardware_Flow_Control:

    • 定义了USART(通用同步/异步收发传输器)的硬件流控制配置

      宏定义解释

      1. USART_HardwareFlowControl_None
        • ((uint16_t)0x0000)
        • 描述:定义了一个无硬件流控制的宏。当设置为无硬件流控制时,USART通信不使用任何硬件流控制信号
      2. USART_HardwareFlowControl_RTS
        • ((uint16_t)0x0100)
        • 描述:定义了一个仅使用请求发送(RTS)信号的硬件流控制宏。RTS信号用于通知接收设备是否准备好接收数据
      3. USART_HardwareFlowControl_CTS
        • ((uint16_t)0x0200)
        • 描述:定义了一个仅使用清除发送(CTS)信号的硬件流控制宏。CTS信号用于通知发送设备是否应该开始发送数据
      4. USART_HardwareFlowControl_RTS_CTS
        • ((uint16_t)0x0300)
        • 描述:定义了一个同时使用RTS和CTS信号的硬件流控制宏。这种配置提供了双向的硬件流控制,以确保数据传输的同步和可靠性

      宏函数

      1. IS_USART_HARDWARE_FLOW_CONTROL(CONTROL)
        • 描述:这是一个宏函数,用于检查给定的硬件流控制设置是否有效
        • 参数CONTROL,代表USART的硬件流控制设置
        • 功能:检查CONTROL是否等于USART_HardwareFlowControl_NoneUSART_HardwareFlowControl_RTSUSART_HardwareFlowControl_CTSUSART_HardwareFlowControl_RTS_CTS中的任一个
        • 返回值:如果CONTROL有效,返回1(真),否则返回0(假)

      表格:

      宏定义描述
      USART_HardwareFlowControl_None0x0000无硬件流控制
      USART_HardwareFlowControl_RTS0x0100使用RTS硬件流控制
      USART_HardwareFlowControl_CTS0x0200使用CTS硬件流控制
      USART_HardwareFlowControl_RTS_CTS0x0300同时使用RTS和CTS硬件流控制
      宏函数描述
      IS_USART_HARDWARE_FLOW_CONTROL(CONTROL)检查CONTROL是否为有效的USART硬件流控制设置

Ⅶ、串口发送数据

#include "stm32f10x.h"                  // Device header
#include <stdio.h>	//为了移植printf
//USART串口
//TX-->PA9


void Serial_Init(void)
{
	//使能GPIO和USART时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	//初始化GPIO
	GPIO_InitTypeDef GPIO_InitStruct;
 	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
 	GPIO_Init(GPIOA, &GPIO_InitStruct);
	//配置USART
	USART_InitTypeDef USART_InitStruct;
	USART_InitStruct.USART_BaudRate = 9600;//配置波特率
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//是否选择硬件流控
	USART_InitStruct.USART_Mode = USART_Mode_Tx;//仅发送模式
	USART_InitStruct.USART_Parity = USART_Parity_No;//无校验
	USART_InitStruct.USART_StopBits = USART_StopBits_1;//1个停止位
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;//数据帧包含8个数据位
	USART_Init(USART1, &USART_InitStruct);//初始化USATR1
	USART_Cmd(USART1, ENABLE);//开启USART串口通信
}

//发送一个字节
void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);//发送一个字节
	//获取USART标志状态(等待)传输数据寄存器空标志
	while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

/**************************************************************************************
 * 名称		Serial_SendArray
 * 功能		通过串口发送数组
 * 参数		uint16_t* Arr数组指针
 * 参数		uint16_t Length数组长度
 * 返回值	无
 *****************************/
void Serial_SendArray(uint8_t* Arr, uint16_t Length)
{
	uint16_t i = 0;
	for(i = 0;i<Length;i++)
	{
		Serial_SendByte(Arr[i]);
	}
}

/**************************************************************************************
 * 名称		Serial_SendString
 * 功能		通过串口发送字符串
 * 参数		char* String("xxxxxxx")
 * 返回值	无
 *****************************/
void Serial_SendString(char* String)
{
	while(*String != '\0')
	{
		Serial_SendByte(*String);
		String++;
	}
}

//返回num的SQ次方(内部函数)
static uint32_t Serial_GetSquare(const int num, int SQ)
{
	uint32_t ret = 1;
	while(SQ--)
		ret *= num;
	return ret;
}
/**************************************************************************************
 * 名称		Serial_SendNum
 * 功能		通过串口发送数字
 * 参数		数字,及数字长
 * 返回值	无
 *****************************/
void Serial_SendNum(uint32_t Num, uint8_t Length)
{
	uint8_t i = 0;
	for(i = 0; i < Length; i++)
	{
		Serial_SendByte((Num / Serial_GetSquare(10, Length - i - 1) % 10) + '0');
		//'\0'是为了偏移,可将数字转换为其对应的ASCII字符
	}
}

//重定向printf,须勾选魔法棒中的Use MicroLlB
int fputc(int ch, FILE *stream)
{
	Serial_SendByte(ch);
	return ch;
}

可变参数函数,模拟实现printf

#include <stdarg.h>

void Serial_Printf(char* format, ...)
{
  char String[200];
  va_list arg;
  va_start(arg, format);
  vsprintf(String, format, arg);
  va_end(arg);
  Serial_SendString(String);
}

Ⅷ、串口接收数据(包含发送)

#include "stm32f10x.h"                  // Device header
#include <stdio.h>	//为了移植printf
//USART串口
//TX-->PA9
//RX-->PA10

uint8_t Serial_RxData = 0;//接收的数据
uint8_t Serial_RxFlag = 0;//标志位


void Serial_Init(void)
{
	//使能GPIO和USART时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	//初始化发送GPIO
	GPIO_InitTypeDef GPIO_InitStruct;
 	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
 	GPIO_Init(GPIOA, &GPIO_InitStruct);
	//初始化接收GPIO
 	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
 	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	//配置USART
	USART_InitTypeDef USART_InitStruct;
	USART_InitStruct.USART_BaudRate = 9600;//配置波特率
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//是否选择硬件流控
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//发送+接收模式
	USART_InitStruct.USART_Parity = USART_Parity_No;//无校验
	USART_InitStruct.USART_StopBits = USART_StopBits_1;//1个停止位
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;//数据帧包含8个数据位
	USART_Init(USART1, &USART_InitStruct);//初始化USATR1
	
	//开启USART的中断
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	//设置NVIC
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//配置嵌套向量中断控制器(NVIC)的优先级分组
    
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;//选择IRQ通道
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//启用这个IRQ通道
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;//设置抢占优先级为1
    NVIC_InitStruct.NVIC_IRQChannelSubPriority  = 1;//设置响应优先级为1
    NVIC_Init(&NVIC_InitStruct);	
	
	USART_Cmd(USART1, ENABLE);//开启USART串口通信
}

//发送一个字节
void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);//发送一个字节
	//获取USART标志状态(等待)传输数据寄存器空标志
	while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

/**************************************************************************************
 * 名称		Serial_SendArray
 * 功能		通过串口发送数组
 * 参数		uint16_t* Arr数组指针
 * 参数		uint16_t Length数组长度
 * 返回值	无
 *****************************/
void Serial_SendArray(uint8_t* Arr, uint16_t Length)
{
	uint16_t i = 0;
	for(i = 0;i<Length;i++)
	{
		Serial_SendByte(Arr[i]);
	}
}

/**************************************************************************************
 * 名称		Serial_SendString
 * 功能		通过串口发送字符串
 * 参数		char* String("xxxxxxx")
 * 返回值	无
 *****************************/
void Serial_SendString(char* String)
{
	while(*String != '\0')
	{
		Serial_SendByte(*String);
		String++;
	}
}

//返回num的SQ次方(内部函数)
static uint32_t Serial_GetSquare(const int num, int SQ)
{
	uint32_t ret = 1;
	while(SQ--)
		ret *= num;
	return ret;
}
/**************************************************************************************
 * 名称		Serial_SendNum
 * 功能		通过串口发送数字
 * 参数		数字,及数字长
 * 返回值	无
 *****************************/
void Serial_SendNum(uint32_t Num, uint8_t Length)
{
	uint8_t i = 0;
	for(i = 0; i < Length; i++)
	{
		Serial_SendByte((Num / Serial_GetSquare(10, Length - i - 1) % 10) + '0');
		//'\0'是为了偏移,可将数字转换为其对应的ASCII字符
	}
}

//重定向printf,须勾选魔法棒中的Use MicroLlB
int fputc(int ch, FILE *stream)
{
	Serial_SendByte(ch);
	return ch;
}
//**********************************************************************************
uint8_t Serial_GetFlag(void)//获取标志
{
	if(Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}


uint8_t Serial_GetData(void)//获取数据
{
	return Serial_RxData;
}

//中断函数
void USART1_IRQHandler(void)
{
	if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET)//判断中断标志位
	{
		Serial_RxData = USART_ReceiveData(USART1);
		Serial_RxFlag = 1;
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}


Ⅸ、USART收发数据包

1、收发HEX数据包

包头:FF

包尾:FE

image-20250102193414382

image-20250102193525005

含包头包尾固定包长的数据包
uint8_t Serial_RxPacketFlag = 0;//数据包标志位

uint8_t Serial_RxPacket[4] = { 0 };//接收数据包的缓冲数组

...
...
//**********************************************************************************
uint8_t Serial_GetPacketFlag(void)//获取数据包标志
{
	if(Serial_RxPacketFlag == 1)
	{
		Serial_RxPacketFlag = 0;
		return 1;
	}
	return 0;
}


/**************************************************************************************
 * 名称		Serial_SendPacket
 * 功能		发送数据量为4字节的HEX数据包
 * 参数		Serial_RxPacket_4bt
 * 返回值	无
 *****************************/
void Serial_SendPacket(uint8_t* Serial_RxPacket_4bt)
{
	Serial_SendByte(0xFF);//发送包头
	Serial_SendArray(Serial_RxPacket_4bt, 4);//发送数据
	Serial_SendByte(0xFE);//发送包尾
}

  
//中断函数
void USART1_IRQHandler(void)
{
	static uint8_t RxState = 0;//初始化状态
	static uint8_t Count = 0;//记录接收数据的个数
	if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET)//判断中断标志位
	{
		uint8_t RxData = USART_ReceiveData(USART1);//获取数据
		if(RxState == 0)//等待包头
		{
			if(RxData == 0xFF)//接收到包头
			{
				RxState = 1;//转移至状态1
				Count = 0;
			}
		}
		else if(RxState == 1)//接收数据
		{
			Serial_RxPacket[Count] = RxData;
			Count++;
			if(Count >= 4)
			{
				RxState = 2;//转移至状态2
				Count = 0;//状态清零
			}
		}
		else if(RxState == 2)//等待包尾
		{
			if(RxData == 0xFE)//接收到包尾
			{
				RxState = 0;//转移至状态0
				Serial_RxPacketFlag = 1;//接收到数据包表标志位
			}			
		}
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}

2、收发文本数据包

包头:@

包尾:换行符

image-20250103161415797

image-20250103161500805

含包头包尾固定包长的数据包
#include "stm32f10x.h"                  // Device header
#include <stdio.h>	//为了移植printf
//USART串口
//TX-->PA9
//RX-->PA10

#define RxPacket_Length_MAX 200	//接收文本数据包的最大长度
uint8_t Serial_RxPacketFlag = 0;//数据包标志位//需要手动清零

char Serial_RxPacket[RxPacket_Length_MAX] = { 0 };//接收文本数据包的缓冲数组


void Serial_Init(void)
{
	//使能GPIO和USART时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	//初始化发送GPIO
	GPIO_InitTypeDef GPIO_InitStruct;
 	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
 	GPIO_Init(GPIOA, &GPIO_InitStruct);
	//初始化接收GPIO
 	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
 	GPIO_Init(GPIOA, &GPIO_InitStruct);
	
	//配置USART
	USART_InitTypeDef USART_InitStruct;
	USART_InitStruct.USART_BaudRate = 9600;//配置波特率
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//是否选择硬件流控
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//发送+接收模式
	USART_InitStruct.USART_Parity = USART_Parity_No;//无校验
	USART_InitStruct.USART_StopBits = USART_StopBits_1;//1个停止位
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;//数据帧包含8个数据位
	USART_Init(USART1, &USART_InitStruct);//初始化USATR1
	
	//开启USART的中断
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	//设置NVIC
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//配置嵌套向量中断控制器(NVIC)的优先级分组
    
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;//选择IRQ通道
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//启用这个IRQ通道
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;//设置抢占优先级为1
    NVIC_InitStruct.NVIC_IRQChannelSubPriority  = 1;//设置响应优先级为1
    NVIC_Init(&NVIC_InitStruct);	
	
	USART_Cmd(USART1, ENABLE);//开启USART串口通信
}

//发送一个字节
void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);//发送一个字节
	//获取USART标志状态(等待)传输数据寄存器空标志
	while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

/**************************************************************************************
 * 名称		Serial_SendArray
 * 功能		通过串口发送数组
 * 参数		uint16_t* Arr数组指针
 * 参数		uint16_t Length数组长度
 * 返回值	无
 *****************************/
void Serial_SendArray(uint8_t* Arr, uint16_t Length)
{
	uint16_t i = 0;
	for(i = 0;i<Length;i++)
	{
		Serial_SendByte(Arr[i]);
	}
}

/**************************************************************************************
 * 名称		Serial_SendString
 * 功能		通过串口发送字符串
 * 参数		char* String("xxxxxxx")
 * 返回值	无
 *****************************/
void Serial_SendString(char* String)
{
	while(*String != '\0')
	{
		Serial_SendByte(*String);
		String++;
	}
}

//返回num的SQ次方(内部函数)
static uint32_t Serial_GetSquare(const int num, int SQ)
{
	uint32_t ret = 1;
	while(SQ--)
		ret *= num;
	return ret;
}
/**************************************************************************************
 * 名称		Serial_SendNum
 * 功能		通过串口发送数字
 * 参数		数字,及数字长
 * 返回值	无
 *****************************/
void Serial_SendNum(uint32_t Num, uint8_t Length)
{
	uint8_t i = 0;
	for(i = 0; i < Length; i++)
	{
		Serial_SendByte((Num / Serial_GetSquare(10, Length - i - 1) % 10) + '0');
		//'\0'是为了偏移,可将数字转换为其对应的ASCII字符
	}
}

//重定向printf,须勾选魔法棒中的Use MicroLlB
int fputc(int ch, FILE *stream)
{
	Serial_SendByte(ch);
	return ch;
}



/**************************************************************************************
 * 名称		Serial_SendHEXPacket
 * 功能		发送数据量为4字节的HEX数据包
 * 参数		Serial_RxPacket_4bt
 * 返回值	无
 *****************************/
void Serial_SendHEXPacket(uint8_t* Serial_RxPacket_4bt)
{
	Serial_SendByte(0xFF);//发送包头
	Serial_SendArray(Serial_RxPacket_4bt, 4);//发送数据
	Serial_SendByte(0xFE);//发送包尾
}


//中断函数
void USART1_IRQHandler(void)
{
	static uint8_t RxState = 0;//初始化状态
	static uint8_t Count = 0;//记录接收数据的个数
	if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET)//判断中断标志位
	{
		uint8_t RxData = USART_ReceiveData(USART1);//获取数据
		if(RxState == 0)//等待包头
		{
			if(RxData == '@' && Serial_RxPacketFlag == 0)//接收到包头(且防止传输过快导致数据错位)
			{
				RxState = 1;//转移至状态1
				Count = 0;
			}
		}
		else if(RxState == 1)//接收数据
		{
			if(RxData == '\r')//判断是否是包尾1
			{
				RxState = 2;//转移至状态2
			}
			else
			{
				Serial_RxPacket[Count] = RxData;
				Count++;
			}
		}
		else if(RxState == 2)//等待包尾
		{
			if(RxData == '\n')//接收到包尾2
			{
				RxState = 0;//转移至状态0
				Serial_RxPacket[Count] = '\0';//添加结束标志位				
				Serial_RxPacketFlag = 1;//接收到数据包标志位
			}
		}
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}


.h头文件

#ifndef __SERIAL_H__
#define __SERIAL_H__
#include "stdint.h"

extern char Serial_RxPacket[];//接收文本数据包的缓冲数组
extern uint8_t Serial_RxPacketFlag;//数据包标志位//需要手动清零

void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t* Arr, uint16_t Length);
void Serial_SendString(char* String);
void Serial_SendNum(uint32_t Num, uint8_t Length);

void Serial_SendHEXPacket(uint8_t* Serial_RxPacket_4bt);//发送数据量为4字节的数据包


#endif


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2276757.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker 安装开源的IT资产管理系统Snipe-IT

一、安装 1、创建docker-compose.yaml version: 3services:snipeit:container_name: snipeitimage: snipe/snipe-it:v6.1.2restart: alwaysports:- "8000:80"volumes:- ./logs:/var/www/html/storage/logsdepends_on:- mysqlenv_file:- .env.dockernetworks:- snip…

达梦8-DMSQL程序设计学习笔记1-DMSQL程序简介

1、DMSQL程序简介 DMSQL程序是达梦数据库对标准SQL语言的扩展&#xff0c;是一种过程化SQL语言。在DMSQL程序中&#xff0c;包括一整套数据类型、条件结构、循环结构和异常处理结构等&#xff0c;DMSQL程序中可以执行SQL语句&#xff0c;SQL语句中也可以使用DMSQL函数。 DMSQ…

NLP中常见的分词算法(BPE、WordPiece、Unigram、SentencePiece)

文章目录 一、基本概念二、传统分词方法2.1 古典分词方法2.2 拆分为单个字符 三、基于子词的分词方法&#xff08;Subword Tokenization&#xff09;3.1 主要思想3.2 主流的 Subword 算法3.3 Subword 与 传统分词方法的比较 四、Byte Pair Encoding (BPE)4.1 主要思想4.2 算法过…

第三十六章 Spring之假如让你来写MVC——拦截器篇

Spring源码阅读目录 第一部分——IOC篇 第一章 Spring之最熟悉的陌生人——IOC 第二章 Spring之假如让你来写IOC容器——加载资源篇 第三章 Spring之假如让你来写IOC容器——解析配置文件篇 第四章 Spring之假如让你来写IOC容器——XML配置文件篇 第五章 Spring之假如让你来写…

PyTorch 深度学习框架快速入门 (小土堆)

PyTorch 深度学习框架快速入门 深度学习框架常用模块数据集存取图片数据处理库 —— PILOS 模块实例 Tensorboard 记录机器学习的过程Transform 进行图像变换数据集的下载DataLoaderModule 自定义网络前向传播卷积层卷积简单应用 最大池化非线性层线性层 简单的整合基于现有网络…

FPGA的 基本结构(Xilinx 公司Virtex-II 系列FPGA )

以Xilinx 公司Virtex-II 系列FPGA 为例&#xff0c;其基本结构由下图所示。它是主要由两大部分组成&#xff1a;可编程输入/输出&#xff08;Programmable I/Os&#xff09;部分和内部可配置&#xff08;Configurable Logic&#xff09;部分。 可编程输入/输出&#xff08;I/Os…

【Elasticsearch】批量操作:优化性能

🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程,高并发设计,Springboot和微服务,熟悉Linux,ESXI虚拟化以及云原生Docker和K8s,热衷于探…

C++、Haskell 和 Rust 三种语言实现 Faster Suffix Sort 算法的比较

对 C、Haskell 和 Rust 三种语言实现 Faster Suffix Sort 算法的比较&#xff1a; 1. 编程效率 C&#xff1a; 优点&#xff1a;C 提供了丰富的标准库&#xff0c;如 std::sort&#xff0c;可以方便地结合自定义比较函数对后缀数组进行排序。使用 Lambda 表达式可以简洁地实现…

校园跑腿小程序---轮播图,导航栏开发

hello hello~ &#xff0c;这里是 code袁~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生…

uniapp实现H5页面内容居中与两边留白,打造类似微信公众号阅读体验

在 UniApp 中&#xff0c;由于需要兼容多端应用&#xff0c;我们通常使用 rpx 作为尺寸单位。然而&#xff0c;在某些情况下&#xff0c;如需要实现内容居中且两边留白时&#xff0c;直接使用 rpx 可能会带来一些限制。这时&#xff0c;我们可以考虑使用 px 或 rem 等单位&…

【Uniapp-Vue3】pages.json页面路由globalStyle的属性

项目的全局配置在pages.json中。 一、导航栏设置 二、下拉刷新设置 下拉就可以看到设置的样式 三、上拉触底 这个页面中&#xff0c;向下滑动页面到底部就会输出“到底了” 现在将触底距离设置为500 走到半路就会输出“到底了”

Type-C双屏显示器方案

在数字化时代&#xff0c;高效的信息处理和视觉体验已成为我们日常生活和工作的关键需求。随着科技的进步&#xff0c;一款结合了便携性和高效视觉输出的设备——双屏便携屏&#xff0c;逐渐崭露头角&#xff0c;成为追求高效工作和娱乐体验人群的新宠。本文将深入探讨双屏便携…

Linux下部署Redis(本地部署超详细)

非docker 1、下载Redis 历史版本&#xff1a; http://download.redis.io/releases 我的&#xff1a; http://download.redis.io/releases/redis-7.0.5.tar.gz 2.安装教程 1.Redis是基于c语言编写的需要安装依赖&#xff0c;需要安装gcc yum install gcc-c 2.查看gcc版…

使用 Multer 上传图片到阿里云 OSS

文件上传到哪里更好&#xff1f; 上传到服务器本地 上传到服务器本地&#xff0c;这种方法在现今商业项目中&#xff0c;几乎已经见不到了。因为服务器带宽&#xff0c;磁盘 IO 都是非常有限的。将文件上传和读取放在自己服务器上&#xff0c;并不是明智的选择。 上传到云储存…

UE5 打包项目

UE5 打包项目 flyfish 通过 “文件”->“打开项目”&#xff0c;然后在弹出的对话框中选择项目文件&#xff08;通常是以.uproject为后缀的文件&#xff09; 选择目标平台&#xff1a; 在 UE5 主界面中&#xff0c;找到 “平台”&#xff08;Platforms&#xff09;。根据…

自然语言转 SQL:通过 One API 将 llama3 模型部署在 Bytebase SQL 编辑器

使用 Open AI 兼容的 API&#xff0c;可以在 Bytebase SQL 编辑器中使用自然语言查询数据库。 出于数据安全的考虑&#xff0c;私有部署大语言模型是一个较好的选择 – 本文选择功能强大的开源模型 llama3。 由于 OpenAI 默认阻止出站流量&#xff0c;为了简化网络配置&#…

杭州铭师堂的云原生升级实践

作者&#xff1a;升学e网通研发部基建团队 公司介绍 杭州铭师堂&#xff0c;是一个致力于为人的全面发展而服务的在线教育品牌。杭州铭师堂秉持“用互联网改变教育&#xff0c;让中国人都有好书读”的使命&#xff0c;致力于用“互联网教育”的科技手段让更多的孩子都能享有优…

STM32 FreeRTOS移植

目录 FreeRTOS源码结构介绍 获取源码 1、 官网下载 2、 Github下载 源码结构介绍 源码整体结构 FreeRTOS文件夹结构 Source文件夹结构如下 portable文件夹结构 RVDS文件夹 MemMang文件夹 FreeRTOS在基于寄存器项目中移植步骤 目录添加源码文件 工程添加源码文件 …

可以进行重复测量的方差分析的AI agent

可以进行重复测量的方差分析的AI agent 前几天做了机器学习的AI agent&#xff0c;把一个糖尿病机器学习模型采用API的形式接入到LLM模型中&#xff0c;结合LLM的智能性和机器学习模型的准确性&#xff0c;利用两者的有点&#xff0c;有可以避免两者的缺点&#xff0c;是一条合…

OpenCV实现Kuwahara滤波

Kuwahara滤波是一种非线性的平滑滤波技术&#xff0c;其基本原理在于通过计算图像模板中邻域内的均值和方差&#xff0c;选择图像灰度值较为均匀的区域的均值来替代模板中心像素的灰度值。以下是Kuwahara滤波的详细原理说明&#xff1a; 一、基本思想 Kuwahara滤波的基本思想…