欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 创投人物 > HAL库:串口 不定长数据接收 + 循环收发缓冲区 + 空闲中断 收发数据

HAL库:串口 不定长数据接收 + 循环收发缓冲区 + 空闲中断 收发数据

2024/10/25 7:33:01 来源:https://blog.csdn.net/L_Z_J_I/article/details/141690190  浏览:    关键词:HAL库:串口 不定长数据接收 + 循环收发缓冲区 + 空闲中断 收发数据

目录

HAL库:不定长数据接收 + 循环收发缓冲区 + 空闲中断 收发数据

串口:多指针定位+收发循环使用缓冲区设计

文件架构:

程序部分大体思路

Uart.h

Uart.c

stm32fxx_It.c

main.c


HAL库:不定长数据接收 + 循环收发缓冲区 + 空闲中断 收发数据

串口:多指针定位+收发循环使用缓冲区设计

STM32C8T6单片机一共有20K的RAM

设计介绍:

  • 定义接收、发送缓冲区(2K 2048字节大小) 用于在接收时不频繁进入中断。

  • 定义结构体,成员为 :Start End 指针 用于在一次串口接收完毕,程序进入空闲中断时。使用Start、End 标记这一段字节的起始和节数地址。 用于读取

  • 定义结构体数组 可以存10段上述的一段字节的起始地址。[0:9]

  • 定义结构体指针 In Out 初始化时,In、Out都指向结构体数组的第一个元素。当接收到一段字节后。In跳到结构体数组的第二个元素。准备一下次接收。 当Out和In的地址不同时。代表接收到数据,此时Out可通过第一个元素的Start 和 End 读出这段 字节,再跳到下一个元素,等待下次不相同时读出。

  • 定义结构体指针 END END指针固定指向结构体数组的最后一个元素 [9] 当Out跳到最后一个成员,即Out 或 In = END时、 在写入、读取后回滚到结构体数组的第一个元素。

  • 接收回卷: 约定最大发送值为300。当2048字节的接收缓冲区余量小于最大发送值300时,回卷。重新从起始地址开始缓存

  • 发送回卷: 当发送字节数量 小于当前发送缓冲余量时,进行回卷。最大值为2048。

文件架构:

  • Uart.h :定义了与串口通信相关的数据结构和函数声明,用于管理串口通信中的接收和发送缓冲区,以及串口的初始化操作。
  • Uart.c :主要针对串口 1 进行配置为空闲中断 ,包括初始化参数、设置缓冲区指针、配置 IO 口和使能空闲中断等。 实现了数据收发、中断处理及缓冲区管理,确保串口 1 高效稳定地进行数据通信。
  • stm32fxx_It.c : 主要是 void USART1_IRQHandler(void) 的中断函数:该函数是串口 1 的中断服务函数。首先调用 HAL 库的中断处理函数,后续 检测到串口 1 进入空闲状态时,清除空闲中断标志位,计算接收字节数量并累加,然后终止接收,触发终止回调函数。终止函数在Uart.c中
  • main.c :在主循环中,通过判断接收和发送缓冲区的指针状态,实现数据的接收和发送,并在指针到达末尾时进行回卷操作。当接收缓冲区有数据时,将其拷贝到发送缓冲区并移动输出指针;当发送缓冲区有数据且处于空闲状态时,发送数据并移动输出指针。

程序部分大体思路

当接收到没有超过最大接收量256 - 1 时,会触发空闲中断。 (如果等于256 会同时触发接收完成中断和空闲中断。)

在接受到后, 会进入串口中断中

在中断判断空闲中断标志位,记录空闲前接收了多少的字节

然后终止当前的接收(否则会继续等到完成接收)。

进入终止接收回调函数。

在函数中会对每次传入的 一段 字节 的起始位和结束位做标记。并且会根据指针的位置进行回卷等操作。

在主函数中。会不断循环检查是否接收到一段字节。即Rx_In_ArrPtr 结构体指针的位置是否与Rx_Out_ArrPtr 的地址相同,如果不同就代表已经记录了一个接收到的一段字节的起始位置。就可以把他移动到 发送缓冲区。

在主函数中调用这个函数, 在往发送缓冲区移动的时候仍然会对这段字节 的起始位和结束位做标记。并且会根据指针的位置进行回卷等操作。

在检查到发送缓冲区的In和Out不同后,有会去判断并发送这一段的 字节。

这只是一个大概的顺序, 具体的 内容 去看 文件吧。里边的注释挺详细的

Uart.h

#ifndef __UART_H
#define __UART_H#include "string.h" //memry操作用的头文件#define U1_RX_SIZE 2048 //接收、发送数组缓冲区长度 宏定义
#define U1_TX_SIZE 2048
#define U1_RX_MAX  256  //约定发送方单次发送字节 最大值 - 1。//否则当接收满了,就会产生完成回调和空闲回调两个回调。typedef struct
{//用于标记接收到的一段字节的起始和结束位置uint8_t* Start;uint8_t* End;
}LCB;    //location Ctrl Block 位置单元组typedef struct
{/*接收缓冲区配置*/uint32_t    Rx_Counter;          //接收缓冲区 累计收到的字节LCB         Rx_LocationArr[10];  //接收缓冲区 字节段 位置单元组 数组LCB        *Rx_In_ArrPtr;           //用于保存 一段字节的 位置LCB        *Rx_Out_ArrPtr;          //用于根据位置 读出缓冲区中的值LCB        *Rx_End_ArrPtr;          //用于定义位置单元组 数组的终点位置,防止指针越界/*发送缓冲区配置*/uint32_t    Tx_Counter;          //发送缓冲区 累计发送的字节。用于计算余量LCB         Tx_LocationArr[10];  //发送缓冲区 字节段 位置单元组 数组LCB        *Tx_In_ArrPtr;        //用于保存 一段字节的 位置LCB        *Tx_Out_ArrPtr;       //用于根据位置 读出缓冲区中的值LCB        *Tx_End_ArrPtr;       //用于定义位置单元组 数组的终点位置,防止指针越界/*串口处理结构体配置*/UART_HandleTypeDef Uart;         //方便一次配置完成。/*发送忙状态*/uint8_t     Tx_State;
}UCB;    //UART Ctrl Block 串口缓冲区单元组//串口1 初始化
void Uart1_Init(uint32_t Bandrate);
//串口1 接收缓冲区 规则  指针位置 初始化
void Uart1_Ptr_Init(void);
//串口1 发送数据
void Uart1_Tx_Data(uint8_t* data, uint8_t len);//UART2初始化
void Uart2_Init(uint32_t Bandrate);
//UART3初始化
void Uart3_Init(uint32_t Bandrate);//外部声明的总控结构体
extern UCB Uart1;
extern UART_HandleTypeDef Uart2;
extern UART_HandleTypeDef Uart3;#endif

Uart.c

#include "stm32f1xx_hal.h"
#include "Uart.h"//串口1 配置结构体
UCB Uart1;
UART_HandleTypeDef Uart2;
UART_HandleTypeDef Uart3;//定义接收和发送的缓冲区
uint8_t U1_Rx_Buff[U1_RX_SIZE];
uint8_t U1_Tx_Buff[U1_TX_SIZE];UCB uarb1;
//初始化串口1
void Uart1_Init(uint32_t Bandrate)
{Uart1.Uart.Instance = USART1;//选择串口1Uart1.Uart.Init.BaudRate = Bandrate;//波特率Uart1.Uart.Init.WordLength = UART_WORDLENGTH_8B;//数据长度Uart1.Uart.Init.StopBits = UART_STOPBITS_1;//停止位Uart1.Uart.Init.Parity = UART_PARITY_NONE;//奇偶校验Uart1.Uart.Init.Mode = UART_MODE_TX_RX;//收发模式Uart1.Uart.Init.HwFlowCtl = UART_HWCONTROL_NONE;//流控HAL_UART_Init(&Uart1.Uart);/*初始化串口1 缓冲区 规则  指针位置*/Uart1_Ptr_Init();/*使能空闲中断*/__HAL_UART_ENABLE_IT(&Uart1.Uart,UART_IT_IDLE);HAL_UART_Receive_IT(&Uart1.Uart, Uart1.Rx_In_ArrPtr->Start, U1_RX_MAX);//开启接收中断。缓冲区位置为刚才记录的Start。接受的数量为U1_RX_MAX
}
//初始化串口1 缓冲区 规则  指针位置函数
void Uart1_Ptr_Init(void)
{Uart1.Rx_In_ArrPtr  = &Uart1.Rx_LocationArr[0];     //输入 位置数组对 指向 第一个元素Uart1.Rx_Out_ArrPtr = &Uart1.Rx_LocationArr[0];     //输出 位置数组对 指向 第一个元素Uart1.Rx_End_ArrPtr = &Uart1.Rx_LocationArr[9];     //结束 位置指向 最后一个元素Uart1.Rx_Counter = 0;   //用于累加收到的字节数量Uart1.Rx_In_ArrPtr->Start = U1_Rx_Buff;             //记录接收缓冲区的起始地址Uart1.Tx_In_ArrPtr  = &Uart1.Tx_LocationArr[0];     //输入 位置数组对 指向 第一个元素Uart1.Tx_Out_ArrPtr = &Uart1.Tx_LocationArr[0];     //输出 位置数组对 指向 第一个元素Uart1.Tx_End_ArrPtr = &Uart1.Tx_LocationArr[9];     //结束 位置指向 最后一个元素Uart1.Tx_Counter = 0;   //用于累加收到的字节数量Uart1.Tx_In_ArrPtr->Start = U1_Tx_Buff;             //记录接收缓冲区的起始地址
}//串口1 发送数据
void Uart1_Tx_Data(uint8_t* data, uint8_t len)//参数为数据的起始位置,和发送多少
{//判断这次要发送的数据量是不是 小于 剩余缓冲区的大小.是:还能放if( len < (U1_TX_SIZE - Uart1.Tx_Counter) ){Uart1.Tx_In_ArrPtr->Start = &U1_Tx_Buff[Uart1.Tx_Counter]; //记录下一次 开始记录的位置}else    //不够了。回卷{Uart1.Tx_Counter = 0;   //重置计数Uart1.Tx_In_ArrPtr->Start = U1_Tx_Buff; //标记起始地址为缓冲区起始位置}memcpy(Uart1.Tx_In_ArrPtr->Start, data, len);   //拷贝源地址到标记的Start位置Uart1.Tx_Counter += len;    //累加,为下次拷贝做准备Uart1.Tx_In_ArrPtr->End = &U1_Tx_Buff[Uart1.Tx_Counter - 1];//标记EndUart1.Tx_In_ArrPtr++;//准备记录下一次的 起始位置//判断是否指向最后一个元素if(Uart1.Tx_In_ArrPtr == Uart1.Tx_End_ArrPtr){Uart1.Tx_In_ArrPtr = &Uart1.Tx_LocationArr[0];   //回到0号位置}
}//初始化串口2
void Uart2_Init(uint32_t Bandrate)
{Uart2.Instance = USART2;//选择串口2Uart2.Init.BaudRate = Bandrate;//波特率Uart2.Init.WordLength = UART_WORDLENGTH_8B;//数据长度Uart2.Init.StopBits = UART_STOPBITS_1;//停止位Uart2.Init.Parity = UART_PARITY_NONE;//奇偶校验Uart2.Init.Mode = UART_MODE_TX_RX;//收发模式Uart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;//流控HAL_UART_Init(&Uart2);
}//初始化串口3
void Uart3_Init(uint32_t Bandrate)
{Uart3.Instance = USART3;//选择串口3Uart3.Init.BaudRate = Bandrate;//波特率Uart3.Init.WordLength = UART_WORDLENGTH_8B;//数据长度Uart3.Init.StopBits = UART_STOPBITS_1;//停止位Uart3.Init.Parity = UART_PARITY_NONE;//奇偶校验Uart3.Init.Mode = UART_MODE_TX_RX;//收发模式Uart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;//流控HAL_UART_Init(&Uart3);
}//定义 强声明 的IO口和时钟打开的 回调函数
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{//判断传入的结构体中第一个成员  来判断是串口1 、 2、 3if(huart->Instance == USART1){__HAL_RCC_GPIOA_CLK_ENABLE();   //打开GPIOA时钟__HAL_RCC_USART1_CLK_ENABLE();  //打开串口1 的时钟GPIO_InitTypeDef GPIO_InitStructure;//TX初始化 PA9GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;//输出GPIO_InitStructure.Pin = GPIO_PIN_9;GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM;//速度中等为10MHAL_GPIO_Init(GPIOA,&GPIO_InitStructure);//RX初始化 PA10GPIO_InitStructure.Mode = GPIO_MODE_AF_INPUT;//输入GPIO_InitStructure.Pin = GPIO_PIN_10;GPIO_InitStructure.Pull = GPIO_NOPULL;//浮空输入HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);HAL_NVIC_SetPriority(USART1_IRQn,3,0);HAL_NVIC_EnableIRQ(USART1_IRQn);    //配置优先级和使能中断}else if(huart->Instance == USART2)//如果是串口2{__HAL_RCC_GPIOA_CLK_ENABLE();//打开GPIOA(PA2 PA3)时钟__HAL_RCC_USART2_CLK_ENABLE();//打开串口2 的时钟GPIO_InitTypeDef GPIO_InitStructure;//TX初始化 PA2GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;//输出GPIO_InitStructure.Pin = GPIO_PIN_2;GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM;//速度中等为10MHAL_GPIO_Init(GPIOA,&GPIO_InitStructure);//RX初始化 PA3GPIO_InitStructure.Mode = GPIO_MODE_AF_INPUT;//输入GPIO_InitStructure.Pin = GPIO_PIN_3;GPIO_InitStructure.Pull = GPIO_NOPULL;//浮空输入HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);}else if(huart->Instance == USART3)//如果是串口3{__HAL_RCC_GPIOB_CLK_ENABLE();//打开GPIOB(PB10 PB11)时钟__HAL_RCC_USART3_CLK_ENABLE();//打开串口3 的时钟GPIO_InitTypeDef GPIO_InitStructure;//TX初始化 PB10GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;//输出GPIO_InitStructure.Pin = GPIO_PIN_10;GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_MEDIUM;//速度中等为10MHAL_GPIO_Init(GPIOB,&GPIO_InitStructure);//RX初始化 PB11GPIO_InitStructure.Mode = GPIO_MODE_AF_INPUT;//输入GPIO_InitStructure.Pin = GPIO_PIN_11;GPIO_InitStructure.Pull = GPIO_NOPULL;//浮空输入HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);}
}//定义 强声明的 UART 接收完成回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{//判断if(huart->Instance == USART1){}
}//定义 强声明的 UART中断错误回调函数
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{//判断串口if(huart->Instance == USART1){}
}//定义 强声明的 UART发送完成回调函数
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{if(huart->Instance == USART1){Uart1.Tx_State = 0; //退出忙状态}
}//强定义的 串口终止 回调函数
void HAL_UART_AbortReceiveCpltCallback(UART_HandleTypeDef *huart)
{//判断串口if(huart->Instance == USART1){// 标记这次字节段的结束的地址Uart1.Rx_In_ArrPtr->End = &U1_Rx_Buff[Uart1.Rx_Counter - 1]; //从0开始存,所以要减1Uart1.Rx_In_ArrPtr++;   //In指向下一个 位置对结构体 数组的下一个元素//判断是否指向最后一个元素if(Uart1.Rx_In_ArrPtr == Uart1.Rx_End_ArrPtr){Uart1.Rx_In_ArrPtr = &Uart1.Rx_LocationArr[0];   //回到0号位置}//判断缓冲区余量是否小于U1_RX_MAXif( (U1_RX_SIZE - Uart1.Rx_Counter) < U1_RX_MAX ){   //如果小于就回滚Uart1.Rx_Counter = 0;   //计数清零Uart1.Rx_In_ArrPtr->Start = U1_Rx_Buff; //开始指针指向缓冲区起始地址}else//余量还够{//从第一个字节段末尾的下一个地址开始存Uart1.Rx_In_ArrPtr->Start = &U1_Rx_Buff[Uart1.Rx_Counter];   //重新打开中断接收HAL_UART_Receive_IT(&Uart1.Uart, Uart1.Rx_In_ArrPtr->Start, U1_RX_MAX);}}}

stm32fxx_It.c

/*-------------------------------------------------*/
/*                                                 */
/*          实现各种中断服务函数的源文件           */
/*                                                 */
/*-------------------------------------------------*/#include "stm32f1xx_hal.h"   
#include "stm32f1xx_it.h"#include "Uart.h"void EXTI15_10_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_10);
}
void EXTI0_IRQHandler(void)
{HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}void USART1_IRQHandler(void)
{HAL_UART_IRQHandler(&Uart1.Uart);if( __HAL_UART_GET_FLAG(&Uart1.Uart,UART_FLAG_IDLE) )//如果进入空闲状态{__HAL_UART_CLEAR_IDLEFLAG(&Uart1.Uart); //清除空闲中断标志位Uart1.Rx_Counter += (U1_RX_MAX - Uart1.Uart.RxXferCount);   //提前读取Count,计算此次字节数量并累加HAL_UART_AbortReceive_IT(&Uart1.Uart);  //终止接收(会清除记录的接收量:Count。有终止回调函数)}
}/*-------------------------------------------------*/
/*函数名:不可屏蔽中断处理函数                     */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void NMI_Handler(void)
{}/*-------------------------------------------------*/
/*函数名:硬件出错后进入的中断处理函数             */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void HardFault_Handler(void)
{}
/*-------------------------------------------------*/
/*函数名:软中断,SWI 指令调用的处理函数           */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void SVC_Handler(void)
{}
/*-------------------------------------------------*/
/*函数名:可挂起的系统服务处理函数                 */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void PendSV_Handler(void)
{}
/*-------------------------------------------------*/
/*函数名:SysTic系统嘀嗒定时器处理函数             */
/*参  数:无                                       */
/*返回值:无                                       */
/*-------------------------------------------------*/
void SysTick_Handler(void)
{  HAL_IncTick();  
}

main.c

#include "stm32f1xx_hal.h"#include "rcc.h"
#include "led.h"
#include "key.h"#include "Uart.h"
/*函 数 名:HAL库:中断方式。实现UART串口数据收发。 定长  20字节 */int main (void)
{HAL_Init();//初始化HAL库RccClock_Init();//配置时钟树72MLED_Init();Uart1_Init(921600);//初始化串口1while(1){   //如果接收缓冲区 In指针和Out指针不是一个地址。那么就说明接收缓冲区有数据了if(Uart1.Rx_Out_ArrPtr != Uart1.Rx_In_ArrPtr){//拷贝这次接收到的字节 到发送缓冲区 .注意是Out 的地址,不是inUart1_Tx_Data( Uart1.Rx_Out_ArrPtr->Start, (Uart1.Rx_Out_ArrPtr->End - Uart1.Rx_Out_ArrPtr->Start +1));Uart1.Rx_Out_ArrPtr++;  //向后挪//如果等于END  则回卷 if( Uart1.Rx_Out_ArrPtr == Uart1.Rx_End_ArrPtr ){Uart1.Rx_Out_ArrPtr = &Uart1.Rx_LocationArr[0]; //记录下一次 开始记录的位置}}//如果发送缓冲区 In指针和Out指针不是一个地址。那么就说明发送缓冲区有数据了。并且此时处于空闲状态if( (Uart1.Tx_Out_ArrPtr != Uart1.Tx_In_ArrPtr) && (Uart1.Tx_State == 0) )//0为空闲。1 为忙{Uart1.Tx_State = 1; //进入忙状态HAL_UART_Transmit_IT( &Uart1.Uart, Uart1.Tx_Out_ArrPtr->Start, (Uart1.Tx_Out_ArrPtr->End - Uart1.Tx_Out_ArrPtr->Start + 1) );Uart1.Tx_Out_ArrPtr++;  //向后挪//如果等于END  则回卷 if( Uart1.Tx_Out_ArrPtr == Uart1.Tx_End_ArrPtr ){Uart1.Tx_Out_ArrPtr = &Uart1.Tx_LocationArr[0]; //记录下一次 开始记录的位置}}}
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com