串口环形收发原理及实现

一、环形收发队列构建
1、构造环形收发数组及其长度
#define buff_size 10
Unsigned char buff[buff_size];
2、定义环形收发读/写位置
u16 R_Buff;
u16 W_Buff;
3、结构体封装

typedef struct Qbuff
{
    u16 write;             //写位置
    u16 read;              //读位置
    u8  buff[buff_size];   //环形缓冲收发数组
}CircularBuff;   

二、案例
假设环形收发数组长度为8。W/R分贝为实际的读写位置,Size为实际存储的数据的长度。
1、写入4个数据,则Write=4; Read=0; Size=4;
在这里插入图片描述
2、读取3个数据,则Write=4; Read=3; Size=4-3=1;
在这里插入图片描述
4、写入7个数据,则Write=(4+7)=11; Read=3; Size=1+7=8;
数组会先写后四个数据,写满后,再从头开些写到第3位置,(4+7)%8=3
在这里插入图片描述
此时,写数据长度Write-读数据长度Read=11-3=8,环形收发数组满了。
5、读取8个数据,则Write=11; Read=3+8=11; Size=11-11=0
在这里插入图片描述
此时,写数据长度Write=读数据长度Read=11,环形收发数组为空。
三、代码
“.h文件”

#define buff_size 500      //环形缓冲队列的容量  	
typedef struct Qbuff
{
    u16 write;             //写位置
    u16 read;              //读位置
    u8  buff[buff_size];   //环形缓冲收发数组
}CircularBuff;   

“.c文件”

1、首先来实现队列
void init_buff(void)       //初始化队列
{
    Cbuff.write=0;
    Cbuff.read=0;
    memset(Cbuff.buff,0,buff_size);
}

u8 write_buff(u8 wdata)     //入队
{
    if(((Cbuff.write-Cbuff.read)==buff_size))  //如果写位置减去读位置等于队列长度,就说明这个环形队列已经满
    {
        return 0;
    }
    Cbuff.buff[Cbuff.write%buff_size]=wdata;              //将数据放入环形队列 
    Cbuff.write=Cbuff.write+1;     //环形队列位置+1,如果超过了队列长度则回到队列头
	return 1;
}

u8 read_buff(u8 *rdata)    //出队
{
    if(Cbuff.read==Cbuff.write)                //如果写位置和读的位置相等,就说明这个环形队列为空,则从头开始重新存
    {
        init_buff();
        return 0;
    }
    *rdata=Cbuff.buff[Cbuff.read%buff_size];
    Cbuff.read=Cbuff.read+1;
	return 1;
}
2、主循环做数据出队与数据包拆分
void move_data(void)
{
    int i=0;
    u16 size1,size2,size3;         //记录三包数据的切分位置
    u16 len1,len2,len3;            //记录三包数据的长度
    u8 CrcBuff1[100],CrcBuff2[100],CrcBuff3[100];               //CRC切分好的数据包
	if(ok_flag)                    //连续10ms没有接收到数据,ok_flag置1
	{	
        ok_flag=Cbuff.write;       //将接收到的数据长度赋值给ok_flag
        for(i=0;i<ok_flag;i++)     //循环读取所有数据
        {
            if(read_buff(&data))   //缓冲非空,若有3个数据包
            {
                BuffHandle[i]=data;                
            }          
        } 
        for(i=0;i<ok_flag;i++)     //数据包切分,最大三包数据
        {
            if((BuffHandle[i-2]==0xF0)&&(BuffHandle[i-1]==0x10)) //判断数据头0xF0 0x10
            {
                 size1=i-2;                                      //记录切分位置
                 len1=BuffHandle[i];                             //记录数据包长度
                 memcpy(CrcBuff1,BuffHandle+size1,len1);         //获取切分好的数据包
            }
            if((BuffHandle[i-2]==0xF0)&&(BuffHandle[i-1]==0x0F)) //判断数据头0xF0 0x0F
            {
                 size2=i-2;                                      //记录切分位置
                 len2=BuffHandle[i];                             //记录数据包长度     
                 memcpy(CrcBuff2,BuffHandle+size2,len2);         //获取切分好的数据包
            }	
            if((BuffHandle[i-2]==0xF0)&&(BuffHandle[i-1]==0x0F)) //判断数据头0xF0 0x0F
            {
                 size3=i-2;                                      //记录切分位置
                 len3=BuffHandle[i];                             //记录数据包长度
                 memcpy(CrcBuff3,BuffHandle+size3,len3);         //获取切分好的数据包        
            }
        }
	}
}
3、串口接收中断入队
void USART1_IRQHandler(void)
{
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
	{	
		USART_ClearITPendingBit(USART1,USART_IT_RXNE); //清除标志位
		write_buff(USART_ReceiveData(USART1));//读取接收到的数据
	}
}
下面是一个示例代码,用于在单片机实现串口接收和发送数据的环形队列: ```c #define BUFFER_SIZE 64 volatile uint8_t rx_buffer[BUFFER_SIZE]; volatile uint8_t tx_buffer[BUFFER_SIZE]; volatile uint8_t rx_head = 0; volatile uint8_t rx_tail = 0; volatile uint8_t tx_head = 0; volatile uint8_t tx_tail = 0; void USART_IRQHandler(void) { if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { uint8_t data = USART_ReceiveData(USART1); // 将接收到的数据存入接收缓冲区 rx_buffer[rx_head] = data; rx_head = (rx_head + 1) % BUFFER_SIZE; } if (USART_GetITStatus(USART1, USART_IT_TXE) != RESET) { if (tx_head != tx_tail) { // 从发送缓冲区读取数据发送 USART_SendData(USART1, tx_buffer[tx_tail]); tx_tail = (tx_tail + 1) % BUFFER_SIZE; } else { // 发送缓冲区为空,禁用发送中断 USART_ITConfig(USART1, USART_IT_TXE, DISABLE); } } } // 在初始化函数中启用串口中断 void USART_Init() { // 初始化串口配置 // 使能串口接收和发送中断 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); USART_ITConfig(USART1, USART_IT_TXE, ENABLE); // 启用串口接收和发送中断 NVIC_EnableIRQ(USART1_IRQn); } // 将数据写入发送缓冲区 void write_data_to_buffer(uint8_t data) { tx_buffer[tx_head] = data; tx_head = (tx_head + 1) % BUFFER_SIZE; // 启用发送中断 USART_ITConfig(USART1, USART_IT_TXE, ENABLE); } // 从接收缓冲区读取数据 uint8_t read_data_from_buffer() { if (rx_head == rx_tail) { // 队列为空,没有数据可读 return 0; } uint8_t data = rx_buffer[rx_tail]; rx_tail = (rx_tail + 1) % BUFFER_SIZE; return data; } // 主函数中的示例使用 int main(void) { // 初始化串口环形队列 while (1) { if (rx_head != rx_tail) { uint8_t data = read_data_from_buffer(); // 处理接收到的数据 // ... } // 其他任务 // 向发送缓冲区写入数据 write_data_to_buffer(0x55); // ... } } ``` 上述代码中,在串口中断处理函数 `USART_IRQHandler` 中,首先判断是否有数据接收到,并将接收到的数据存入接收缓冲区 `rx_buffer`。然后判断是否可以发送数据,如果发送缓冲区不为空,则从发送缓冲区 `tx_buffer` 中读取数据并发送出去。 在初始化函数中,启用串口的接收和发送中断,并启用对应的中断向量。在主函数中,通过检查接收缓冲区的头部和尾部指针,判断是否有新的数据接收到,然后从队列中读取数据进行处理。同时,也可以通过调用 `write_data_to_buffer` 函数向发送缓冲区写入数据。 需要根据具体的单片机型号和串口配置进行相应的修改和适配。同时,为了保证数据的正确性和并发安全,在多任务环境下可能需要使用适当的同步机制(如互斥锁)来访问和操作环形队列
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值