自定义的USART协议源码阅读

预备知识:

0x0d(回车)0x0a(换行)
整个数据流过程中,这两个一定是一起出现的,如果不是,则说明这串数据出错了,需要重置所有数据

预定义部分

#define USART_REC_LEN  			200  	
#define EN_USART1_RX 			1		
	  	
extern u8  USART_RX_BUF[USART_REC_LEN]; 
extern u16 USART_RX_STA;         		
u8 USART_RX_BUF[USART_REC_LEN]; 


u16 USART_RX_STA=0; 

//USART_RX_STA 取第15位(&0x8000),判断是不是接收到了0x0a,如果是强行设置(|=0x8000)
//USART_RX_STA 取第14位(&0x4000),判断是不是接收到了0x0d,如果是强行设置(|=0x4000)
//USART_RX_STA 第0-13位,数据有效位

框架定义

void USART1_IRQHandler(void)                	
{
	    u8 Res;  //要存到USART_RX_BUF中的数据
        #if SYSTEM_SUPPORT_OS 		
        	OSIntEnter();    
        #endif
        //USARTx:USART 外设的指针。这里 USARTx 可以是你使用的具体 USART 外设,例如 USART1、USART2 等

        //USART_IT:指定要检查的中断源。通常,STM32 的 USART 中断是通过枚举值来表示的,
        //例如:USART_IT_RXNE:接收数据寄存器非空中断(即接收寄存器有新数据可读取)。USART_IT_TXE:发送数据寄存器空中断(即发送寄存器空,可以写入数据)。

        //该函数返回一个 FlagStatus 类型的值,它是一个宏定义,表示当前中断状态:SET:表示指定的中断已触发。RESET:表示指定的中断未触发。
	    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
        {
            //自定义部分
        } 
} 
#if SYSTEM_SUPPORT_OS 
	OSIntExit();  											 
#endif
} 

自定义部分

用两个数据表示接收结束(0x0d,0x0a)

		Res =USART_ReceiveData(USART1);	
		
		if((USART_RX_STA&0x8000)==0)  //标志位的第15位是不是被设置为1,如果是,结束接收
		{
            //标志位的第14位是不是被设置为1,如果是,倒数第二个数据(0x0d)已经接收到了,标志位重置过了
			if(USART_RX_STA&0x4000)  
			{
                //倒数第二个数据(0x0d)已经接收到了,Res也已经接收了下一个数据,
                //判断最后一个是不是0x0a,!=不是,说明整个数据出错了
				if(Res!=0x0a)  USART_RX_STA=0;
                //是,说明整个数据没有问题,USART_RX_STA第15位标志位设置为1,下次入口判断为否,数据接收完成
				else USART_RX_STA|=0x8000;
			}
			else  //连倒数第二个数据0x0d都没到,还在中间传输过程中
			{	
                //0x0d接收到,表示已经到倒数第一个数据了,设置第14位标志位,下次接收到数据判断是不是最后一位(0x0a)即可
				if(Res==0x0d) USART_RX_STA|=0x4000;

                //还在中间数据的记录中
				else
				{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;//把数据存在USART_RX_BUF的第USART_RX_STA位上
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0; //数据超过长度,并且还没有到结束判断位(0x0d,0x0a),不合理的数据,舍弃重新从0开始接收
				}		 
			}
		} 

//其中USART_ReceiveData函数
uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
{
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  
  /* Receive Data */
  return (uint16_t)(USARTx->DR & (uint16_t)0x01FF);
}

typedef struct
{
  //SR(Status Register,状态寄存器):用于指示外设的当前状态,包括是否有数据可读、发送缓冲区是否空闲、是否发生错误等。
  //常见标志位:TXE(发送缓冲区空)、RXNE(接收缓冲区非空)、TC(发送完成)等。
  //RESERVED0:这是一个占位符,用于保持寄存器地址的对齐,
  __IO uint16_t SR;
  uint16_t  RESERVED0;

  //用于收发数据。写入此寄存器的数据会被发送,读取此寄存器可获取接收到的数据。
  __IO uint16_t DR;
  uint16_t  RESERVED1;
  __IO uint16_t BRR;
  uint16_t  RESERVED2;
  __IO uint16_t CR1;
  uint16_t  RESERVED3;
  __IO uint16_t CR2;
  uint16_t  RESERVED4;
  __IO uint16_t CR3;
  uint16_t  RESERVED5;
  __IO uint16_t GTPR;
  uint16_t  RESERVED6;
} USART_TypeDef;

SR寄存器中的TXE(Transmit Data Register Empty,发送数据寄存器空)标志并不表示接收数据完成,而是用于指示发送数据寄存器是否为空。
TXE = 1:发送数据寄存器空闲,可以写入新的数据进行发送。这表示上一次的数据已经从发送寄存器转移到发送移位寄存器,硬件准备好接受新的数据。
TXE = 0:发送数据寄存器仍然忙碌,不能写入新数据。需要等待该标志位被硬件置为1后再写数据。

判断接收数据是否完成,通常查看SR寄存器中的RXNE(Receive Data Register Not Empty,接收数据寄存器非空)标志位:
RXNE = 1:接收数据寄存器中有数据可以读取。说明接收完成,可以从数据寄存器(DR)中读取数据。RXNE = 0:接收数据寄存器为空,当前没有可读取的数据。

//自带的函数也是用SR标志位判断的
USART_GetFlagStatus(USART1, USART_FLAG_TC)

typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG)
{
  FlagStatus bitstatus = RESET;
  /* Check the parameters */
  assert_param(IS_USART_ALL_PERIPH(USARTx));
  assert_param(IS_USART_FLAG(USART_FLAG));
  /* The CTS flag is not available for UART4 and UART5 */
  if (USART_FLAG == USART_FLAG_CTS)
  {
    assert_param(IS_USART_123_PERIPH(USARTx));
  }  
  
  if ((USARTx->SR & USART_FLAG) != (uint16_t)RESET)
  {
    bitstatus = SET;
  }
  else
  {
    bitstatus = RESET;
  }
  return bitstatus;
}

引脚等初始化设置

接口需要传入想要的波特率

void uart_init(u32 bound){

    GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

    //用于管理和控制中断的硬件模块。NVIC 允许微控制器处理外部或内部的中断事件,并能够灵活地控制中断的优先级、响应和屏蔽等特性。
	NVIC_InitTypeDef NVIC_InitStructure;
	
    //用于控制 APB2 总线外设时钟 的函数 
    //原型:void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
    //RCC_APB2Periph: 该参数指定要启用或禁用的 APB2 外设时钟。它是一个按位 OR(或运算)组合的掩码,表示不同外设的时钟。NewState: 该参数指定是否启用或禁用外设时钟。
    //RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA  这段代码会同时启用 USART1 和 GPIOA 的时钟。
    //使能时钟后,外设能够正常工作。例如启用定时器时钟后,定时器可以正常计数;启用 USART 时钟后,串口通信可以正常进行。
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	

  
	//USART1_TX   GPIOA.9
    //GPIO_Pin_9对应HAL(硬件抽象层)库中,表示 GPIO 引脚 9
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    //它使得 GPIO 引脚能够作为外部设备(如 USART、SPI、I2C 等)的 复用功能引脚,并且具有 推挽输出 的特性。
    //表示 复用推挽输出模式(Alternate Function Push-Pull)
    //Push-Pull 是一种输出模式,意味着引脚可以输出 高电平(Vcc)或 低电平(GND)。
    //在推挽模式下,GPIO 引脚有两个驱动源(一个源驱动高电平,一个源驱动低电平),能够提供更强的驱动能力,推挽模式可以输出更稳定的电平。
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	

    GPIO_Init(GPIOA, &GPIO_InitStructure);
   
    //USART1_RX	  GPIOA.10
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;

    //指示该引脚为 浮空输入,即该引脚没有内部上拉电阻或下拉电阻的连接,处于“浮空”状态,电平状态由外部电路决定。
    //引脚的电平状态完全依赖于外部电路的驱动。因此,浮空输入模式通常适用于那些由外部设备驱动的输入引脚,或者输入信号本身已经通过外部电路完成了电平拉升或拉低。
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    //Usart1 NVIC
    //USART1_IRQn 是一个 STM32 中的中断请求通道(IRQ channel),它代表 USART1 的中断请求
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;

    //优先级设置抢占优先级和子优先级的设置
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			
	NVIC_Init(&NVIC_InitStructure);
  
    //USART

	USART_InitStructure.USART_BaudRate = bound;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    
    //停止位主要用来标记数据帧的结束,并为接收端提供时间来处理接收到的数据。根据协议的不同,停止位的数量可以为 1、1.5 或 2 个。
	USART_InitStructure.USART_StopBits = USART_StopBits_1;

    //配置中设置奇偶校验(Parity)选项
	USART_InitStructure.USART_Parity = USART_Parity_No;、

    //配置 硬件流控制 的选项
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

    //配置中设置 串口通信模式 的一部分。它表示 USART 外设同时启用 接收(Rx) 和 发送(Tx) 模式,即该串口将能够接收和发送数据
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	

    USART_Init(USART1, &USART_InitStructure);
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    USART_Cmd(USART1, ENABLE);                   

}

复用功能通常包括:

USART1_TX:USART1 传输线
USART1_RX:USART1 接收线
SPI1_SCK:SPI 时钟线
SPI1_MISO:SPI 主输入从输出线
I2C1_SCL:I2C 时钟线
I2C1_SDA:I2C 数据线

引脚可以配置为不同的输入模式

typedef enum
{ 
  //名称: 模拟输入模式 (Analog Input)用途: 用于 ADC(模数转换器)等模拟功能。说明: 该模式下引脚连接到内部模拟电路,不受数字输入或输出逻辑控制。
  GPIO_Mode_AIN = 0x0, 
  //名称: 浮空输入模式 (Input Floating)用途: 用作数字输入,但没有内部上下拉电阻。说明: 输入引脚未连接到任何固定的逻辑电平,易受外部噪声影响。适用场景: 外部电路已经提供了明确的电平(如按键有外部上下拉电阻)
  GPIO_Mode_IN_FLOATING = 0x04,
  //名称: 下拉输入模式 (Input Pull-Down)用途: 用作数字输入,内部下拉电阻将引脚默认拉低。说明: 防止浮空状态,未接信号时输入电平为低。适用场景: 信号线需要默认低电平(如按钮未按下时默认低电平)
  GPIO_Mode_IPD = 0x28,
  //名称: 上拉输入模式 (Input Pull-Up)用途: 用作数字输入,内部上拉电阻将引脚默认拉高。说明: 防止浮空状态,未接信号时输入电平为高。适用场景: 信号线需要默认高电平(如按钮未按下时默认高电平)
  GPIO_Mode_IPU = 0x48,
  //名称: 开漏输出模式 (Output Open-Drain)用途: GPIO 引脚作为开漏输出,可用于实现线与(逻辑与)或连接外部上拉电阻。说明:输出低电平时,电流通过 GPIO 引脚流向 GND。输出高电平时,GPIO 引脚处于高阻态,电平由外部上拉电阻决定。
  //适用场景: I²C 总线通信等需要共享总线的场景。
  GPIO_Mode_Out_OD = 0x14,
  //名称: 推挽输出模式 (Output Push-Pull)用途: GPIO 引脚作为标准数字输出。说明:输出低电平时,GPIO 引脚直接连接到 GND。输出高电平时,GPIO 引脚直接连接到 VDD。
  //适用场景: 控制 LED、驱动简单的数字逻辑电路等。
  GPIO_Mode_Out_PP = 0x10,
  //名称: 复用功能开漏输出 (Alternate Function Open-Drain)用途: 用于外设功能(如 I²C 或其他需要开漏特性的外设)。说明:与 GPIO_Mode_Out_OD 类似,但用于外设功能。可实现多设备连接的总线控制。
  //适用场景: I²C 等需要复用开漏功能的场景。
  GPIO_Mode_AF_OD = 0x1C,
  //名称: 复用功能推挽输出 (Alternate Function Push-Pull)用途: 用于外设功能(如 USART、SPI 等需要推挽特性的外设)。说明:与 GPIO_Mode_Out_PP 类似,但用于外设功能。输出特性由外设控制。
  //适用场景: 串口通信(USART)、SPI 通信等需要高速推挽输出的场景。
  GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeDef;

上拉输入
下拉输入
浮空输入

STM32 的中断优先级是基于 4 位的系统,

优先级范围从 0 到 15。
抢占优先级和子优先级的位数是由系统的优先级分组方式决定的。
STM32 的中断优先级通常是通过 NVIC_InitTypeDef 结构中的两个字段设置的:

抢占优先级:决定了一个中断能否打断另一个中断。
子优先级:如果多个中断具有相同的抢占优先级,则由子优先级来决定执行的先后顺序。

抢占优先级,其值越小,优先级越高。例如,0 是最高的优先级,15 是最低的优先级。

硬件流控制?

硬件流控制是串行通信中的一种机制,用于管理数据流,以避免接收方因为接收缓冲区满而丢失数据。
它通过使用额外的控制信号(如 RTS 和 CTS)来动态控制数据传输的速率。

RTS(Request To Send):由发送方发出,用于告诉接收方准备好接收数据。
CTS(Clear To Send):由接收方发出,用于告诉发送方它可以发送数据。

当启用硬件流控制时,接收方可以使用 CTS 信号告诉发送方它的接收缓冲区已满,需要暂停发送数据。发送方可以使用 RTS 信号来请求接收方准备好接收数据。

SYSTEM_SUPPORT_OS 宏定义,

通常用于在嵌入式系统中决定是否启用操作系统支持。
它作为条件编译的标志,用于根据是否启用操作系统(OS)来选择性地编译代码。

在嵌入式系统中,许多项目需要区分 是否使用操作系统。
有时系统会运行 裸机(bare-metal) 应用程序,也就是不依赖于操作系统;

裸机环境(Bare-metal environment)是指不依赖操作系统的嵌入式编程环境。即程序直接与硬件交互,没有操作系统(如 Linux、FreeRTOS 或其他实时操作系统)来管理资源、调度任务和提供服务。在裸机编程中,程序员负责所有的硬件初始化、任务调度、内存管理和中断处理等。

而在其他情况下,可能会使用如 FreeRTOS、CMSIS-RTOS 或 RTX 这样的实时操作系统(RTOS)。
为了管理这些差异,开发人员通常使用 SYSTEM_SUPPORT_OS 这样的宏来切换不同的编译逻辑。

posted @   流光最璀璨i  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示