自学内容网 自学内容网

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

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



原文地址:https://blog.csdn.net/qq_63040946/article/details/145110979

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!