精华内容
下载资源
问答
  • stm32串口缓冲区大小
    2022-04-17 18:48:14

    同名微信公众号“固件工人”同步发布的文章,欢迎同时关注,及时获取最新文章。

    1.1 环形缓冲区的实现思路

    单片机程序开发一般都会用到UART串口通信,通过通信来实现上位机和单片机程序的数据交互。通信中为了实现正常的收发,一般都会有对应的发送和接收缓存来暂存通信数据。这里使用环形缓冲区的方式来设计数据收发的缓存,即缓冲区溢出后,从缓冲区数组的起始索引处重新进行数据的存储,这样可以比较高效地使用缓冲区。

    环形缓冲区的设计思路参考以下链接。

    https://www.cnblogs.com/zengzy/p/5139582.html

    核心思路摘抄如下。规定以下所有方案,在缓冲区满时不可再写入数据,缓冲区空时不能读数据。

    常规数组环形缓冲区思路:

    设缓冲区大小为N,队头out,队尾in,out、in均是下标表示。

    • 初始时,in = out = 0

    • 队头队尾的更新用取模操作,out = (out + 1) % N,in = (in + 1) % N

    • out == in表示缓冲区空,(in + 1) % N == out表示缓冲区满

    • 入队que[in] = value; in = (in + 1) % N;

    • 出队ret = que[out]; out = (out + 1) % N;

    • 数据长度 len = (in - out + N) % N

    改进版数组环形缓冲区思路:

    同样假设缓冲区大小为N,队头out,队尾in,out、in为数组下标,但数据类型为unsigned int。

    • 初始时,in = out = 0

    • 上调缓冲区大小N为2的幂,假设为M

    • 队头队尾更新不再取模,直接++out,++in

    • out == in表示缓冲区空,(in - out) == M表示缓冲区满

    • 入队que[in & (M - 1)] = value; ++in;

    • 出队ret = que[out & (M - 1)] ; ++out;

    • in - out表示数据长度

    1.2 环形缓冲区的代码实现

    本文对应的工程代码链接如下。该工程基于eclipse IDE开发,编译器使用arm-none-eabi-gcc,使用的硬件是STM32F429I-DISCO开发板。

    https://download.csdn.net/download/goodrenze/85163032

    根据以上的环形缓冲区设计思路,先定义缓存对应的结构体类型如下。

    typedef struct _UartBuf_t
    {
    #if UART_RECORD_LOST_NUM
        uint32_t TxLostNum;
    #endif
        uint8_t* TxBuf;
    #if UART_BUF_SIZE_IS_2POW
        uint16_t TxIn;
        uint16_t TxOut;
        uint16_t TxSize;
    #else
        int16_t TxIn;
        int16_t TxOut;
        int16_t TxSize;
    #endif
    ​
    #if UART_RECORD_LOST_NUM
        uint32_t RxLostNum;
    #endif
        uint8_t* RxBuf;
    #if UART_BUF_SIZE_IS_2POW
        uint16_t RxIn;
        uint16_t RxOut;
        uint16_t RxSize;
    #else
        int16_t RxIn;
        int16_t RxOut;
        int16_t RxSize;
    #endif
    }UartBuf_t;

    以上结构体中,UART_RECORD_LOST_NUM宏定义用于设置是否记录丢失的数据个数,UART_BUF_SIZE_IS_2POW宏定义用于设置收发缓存的长度是否是2的幂,如果缓存长度是2的幂,则缓存索引和长度使用无符号数,否则使用有符号数。TxBuf和RxBuf指针用于指向对应的发送和接收的缓存数组。

    本例程的UART串口发送和接收都是用串口中断来实现的,串口中断处理函数的代码实现如下,只需要在对应的串口中断入口函数中调用该函数进行串口数据的收发处理即可。

    // 以下环形缓冲区的设计思路参考以下链接:
    // https://www.cnblogs.com/zengzy/p/5139582.html
    void UartIrqService(USART_TypeDef* UartX, UartBuf_t* Buf)
    {
      uint32_t SR = UartX->SR;
      uint32_t CR1 = UartX->CR1;
      uint32_t CR3 = UartX->CR3;
    ​
      UNUSED(CR3);
    ​
      while(SR & USART_SR_RXNE)
      {
    #if UART_RECORD_LOST_NUM
        if(SR & USART_SR_ORE)
        {
          Buf->RxLostNum++;
        }
    #endif
    ​
    #if UART_BUF_SIZE_IS_2POW
        if ((Buf->RxIn - Buf->RxOut) != Buf->RxSize)
        {
          Buf->RxBuf[Buf->RxIn & (Buf->RxSize - 1)] = (uint8_t)(UartX->DR & (uint8_t)0x00FF);
          Buf->RxIn++;
        }
    #else
        if (((Buf->RxIn + 1) % Buf->RxSize) != Buf->RxOut)
        {
          Buf->RxBuf[Buf->RxIn++] = (uint8_t)(UartX->DR & (uint8_t)0x00FF);
          Buf->RxIn %= Buf->RxSize;
        }
    #endif
        else
        {
          Buf->RxBuf[Buf->RxIn] = (uint8_t)(UartX->DR & (uint8_t)0x00FF);
    #if UART_RECORD_LOST_NUM
          Buf->RxLostNum++;
    #endif
        }
    ​
        SR = UartX->SR;
      }
    ​
      if((SR & USART_SR_TXE) && (CR1 & USART_CR1_TXEIE))
      {
        if(Buf->TxIn != Buf->TxOut)
        {
    #if UART_BUF_SIZE_IS_2POW
          UartX->DR = (uint8_t)(Buf->TxBuf[Buf->TxOut & (Buf->TxSize - 1)] & (uint8_t)0x00FF);
          Buf->TxOut++;
    #else
          UartX->DR = (uint8_t)(Buf->TxBuf[Buf->TxOut++] & (uint8_t)0x00FF);
          Buf->TxOut %= Buf->TxSize;
    #endif
        }
        else
        {
          CLEAR_BIT(UartX->CR1, USART_CR1_TXEIE);
        }
      }
    }

    以上代码就是上面提到的环形缓冲区思路的具体实现。当缓冲区的数据长度是2的幂的时候,可以省去求余的运算,可以提高代码的执行速度。所以如果要求代码的执行时间尽量短,可以考虑将缓冲区的长度设置成2的幂。

    更多相关内容
  • stm32串口缓冲区源码

    2018-08-27 23:50:16
    指向列队头的指针(Head)是缓冲区可读的数据,指向列队尾的指针(Tail)是缓冲区可写的数据,通过移动这两个指针(Head) &(Tail)即可对缓冲区的数据进行读写操作了,直到缓冲区已满(头尾相接),将数据处理完,可以释放...
  • STM32进阶之串口环形缓冲区实现 FIFO,代码精简,易实现。
  • STM32串口开发之环形缓冲区

    千次阅读 热门讨论 2021-07-07 00:09:24
    后面的文章《STM32使用DMA接收串口数据》和《STM32使用DMA发送串口数据》讲解了如何使用DMA辅助串口收发数据,使用DMA的好处在于不用CPU即可完成串口收发数据,减轻CPU负担,在串口通信频繁且不想频繁中断的应用中...

    01、简介

    在之前的文章《stm32 串口详解》中,我们讲解了串口的基本应用,使用串口中断接收数据,串口中断发送回包(一般可以使用非中断形式发送回包,在数据接收不频繁的应用中。串口接收中断保证串口数据及时响应,使用非中断方式发送回包即可)。

    后面的文章《STM32使用DMA接收串口数据》和《STM32使用DMA发送串口数据》讲解了如何使用DMA辅助串口收发数据,使用DMA的好处在于不用CPU即可完成串口收发数据,减轻CPU负担,在串口通信频繁且不想频繁中断的应用中非常有用。

    除了上述两种场景,还有一种应用场景:串口接收数据长度位置,频率未知,不要求实时处理的场景。如果采用上述方案,接收一帧数据立即处理,那么在处理的时候来的数据包就“丢失”了。这个时候就需要缓冲队列来解决这个问题。

    02、缓冲区

    缓冲区看名字就知道,是缓冲数据用的。实现缓冲区最简单的办法时,定义多个数组,接收一包数据到数组A,就把接收数据的地址换成数组B,每个数据有个标记字节用于表示这个数组是否收到数据,收到数据是否处理完成。

    上述方案是完全可行的,但有缺点:

    ①缓冲数据组数一定,且有多变量,代码结构不太清晰。

    ②接收数据长度可能大于数组大小,也可能小于数组大小。不灵活,需要接收数据很长时容易出错,且内存利用率低。

    解决这个问题的好办法是:环形缓冲区。

    环形缓冲区就是一个带“头指针”和“尾指针”的数组。“头指针”指向环形缓冲区中可读的数据,“尾指针”指向环形缓冲区中可写的缓冲空间。通过移动“头指针”和“尾指针”就可以实现缓冲区的数据读取和写入。在通常情况下,应用程序读取环形缓冲区的数据仅仅会影响“头指针”,而串口接收数据仅仅会影响“尾指针”。当串口接收到新的数组,则将数组保存到环形缓冲区中,同时将“尾指针”加1,以保存下一个数据;应用程序在读取数据时,“头指针”加1,以读取下一个数据。当“尾指针”超过数组大小,则“尾指针”重新指向数组的首元素,从而形成“环形缓冲区”!,有效数据区域在“头指针”和“尾指针”之间。如下图

    图片

    如上面说的,环形缓冲区其实就是一个数组,将其“剪开”,然后“拉直”后如下图

    图片

    环形缓冲区的特性

    1、先进新出。

    2、当缓冲区被使用完,且又有新的数据需要存储时,丢掉历史最久的数据,保存最新数据。

    03、代码实现

    环形缓冲区的实现很简单,只需要简单的几个接口即可。

    首先需要创建一个环形缓冲区

    #define  RINGBUFF_LEN          (500)     //定义最大接收字节数 500
    #define  RINGBUFF_OK           1     
    #define  RINGBUFF_ERR          0   
    typedef struct
    {
        uint16_t Head;           
        uint16_t Tail;
        uint16_t Lenght;
        uint8_t  Ring_data[RINGBUFF_LEN];
    }RingBuff_t;
    RingBuff_t ringBuff;//创建一个ringBuff的缓冲区

    当我们发现环形缓冲区被“冲爆”时,也就是缓冲区满了,但是还有待缓冲的数据时,只需要修改RINGBUFF_LEN的宏定义,增大缓冲区间即可。

    环形缓冲区的初始化

    
    /**
    * @brief  RingBuff_Init
    * @param  void
    * @return void
    * @note   初始化环形缓冲区
    */
    void RingBuff_Init(void)
    {
      //初始化相关信息
      ringBuff.Head = 0;
      ringBuff.Tail = 0;
      ringBuff.Lenght = 0;
    }

    主要是将环形缓冲区的头,尾和长度清零,表示没有任何数据存入。

    环形缓冲区的写入

    /**
    * @brief  Write_RingBuff
    * @param  uint8_t data
    * @return FLASE:环形缓冲区已满,写入失败;TRUE:写入成功
    * @note   往环形缓冲区写入uint8_t类型的数据
    */
    uint8_t Write_RingBuff(uint8_t data)
    {
      if(ringBuff.Lenght >= RINGBUFF_LEN) //判断缓冲区是否已满
      {
        return RINGBUFF_ERR;
      }
      ringBuff.Ring_data[ringBuff.Tail]=data;
      ringBuff.Tail = (ringBuff.Tail+1)%RINGBUFF_LEN;//防止越界非法访问
      ringBuff.Lenght++;
      return RINGBUFF_OK;
    }

    这个接口是写入一个字节到环形缓冲区。这里注意:大家可以根据自己的实际应用修改为一次缓冲多个字节。并且这个做了缓冲区满时报错且防止非法越界的处理,大家可以自行修改为缓冲区满时覆盖最早的数据。

    环形缓冲区的读取

    /**
    * @brief  Read_RingBuff
    * @param  uint8_t *rData,用于保存读取的数据
    * @return FLASE:环形缓冲区没有数据,读取失败;TRUE:读取成功
    * @note   从环形缓冲区读取一个u8类型的数据
    */
    uint8_t Read_RingBuff(uint8_t *rData)
    {
      if(ringBuff.Lenght == 0)//判断非空
      {
        return RINGBUFF_ERR;
      }
      *rData = ringBuff.Ring_data[ringBuff.Head];//先进先出FIFO,从缓冲区头出
      ringBuff.Head = (ringBuff.Head+1)%RINGBUFF_LEN;//防止越界非法访问
      ringBuff.Lenght--;
      return RINGBUFF_OK;
    }

    读取的话也很简单,同样是读取一个字节,大家可以自行修改为读取多个字节。

    04、验证

    光说不练假把式,下面我们就来验证上面的代码可行性。

    串口中断函数中缓冲数据

    void USART1_IRQHandler(void)
    {
      if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE))
      {
        Write_RingBuff(USART_ReceiveData(USART1));
        USART_ClearFlag(USART1, USART_FLAG_RXNE);
      }
    }

    在主循环中,读取缓冲区的数据,然后发送出去,因为是简单的demo,添加了延时模拟CPU处理其他任务。

    
    while (1)
      {
        if(Read_RingBuff(&data))            //从环形缓冲区中读取数据
        {
          USART_SendData(USART1, data);
        }
        SysCtlDelay(1*(SystemCoreClock/3000));
      }

    验证,间隔100ms发送数据。

    图片

    结果显示没有出现丢包问题。如果你的应用场景串口通信速率快,数据量大或处理速度慢导致丢包,建议增大RINGBUFF_LEN的宏定义,增大缓冲区间即可。

    KeilIAR的工程文件下载地址:

    https://github.com/strongercjd/STM32F207VCT6

    点击查看本文所在的专辑,STM32F207教程

    关注公众号,第一时间收到文章更新

    展开全文
  • STM32串口数据接收 --环形缓冲区

    千次阅读 2021-02-01 11:35:57
    STM32串口数据接收 --环形缓冲区 环形缓冲区简介   在单片机中串口通信是我们使用最频繁的,使用串口通信就会用到串口的数据接收与发送,环形缓冲区方式接收数据可以更好的保证数据丢帧率第。   在通信程序中,...

    STM32串口数据接收 --环形缓冲区

    环形缓冲区简介

      在单片机中串口通信是我们使用最频繁的,使用串口通信就会用到串口的数据接收与发送,环形缓冲区方式接收数据可以更好的保证数据丢帧率第。
      在通信程序中,经常使用环形缓冲器作为数据结构来存放通信中发送和接收的数据。环形缓冲区是一个先进先出的循环缓冲区,可以向通信程序提供对缓冲区的互斥访问。
      环形缓冲区的一个有用特性是:当一个数据元素被用掉后,其余数据元素不需要移动其存储位置。相反,一个非圆形缓冲区(例如一个普通的队列)在用掉一个数据元素后,其余数据元素需要向前搬移。换句话说,环形缓冲区适合实现先进先出缓冲区,而非环形缓冲区适合后进先出缓冲区。
    在这里插入图片描述

    STM32环形缓冲区示例

    • 串口配置示例
    #include "usart.h"
    /********************串口初始化函数封装*********************
    ****硬件接口:USART1_TX -- PA9(发送)
    **						USART1-RX --PA10(接收)
    **						USART2_TX -- PA2(发送)
    **						USART2-RX --PA3(接收)
    **						USART3_TX -- PB10(发送)
    **						USART3_RX -- PB11(接收)
    形参:USART_TypeDef *USARTx -- 要配置的哪个串口
    **			u32 baud  --波特率
    **			u32 sysclk --时钟频率(USART1 --72MHZ ,USAT2\USART3 --36MHZ)
    **
    ***********************************************************/
    void Usartx_Init(USART_TypeDef *USARTx,u32 baud,u32 sysclk)
    {
    	if(USART1 == USARTx)
    	{
    			/*1.开时钟*/
    		RCC->APB2ENR|=1<<2;//PA时钟
    		RCC->APB2ENR|=1<<14;//串口时钟
    		RCC->APB2RSTR|=1<<14;//串口复位
    		RCC->APB2RSTR&=~(1<<14);//取消复位
    		/*2.配置GPIO口*/
    		GPIOA->CRH&=0xFFFFF00F;
    		GPIOA->CRH|=0x000008B0;//上下拉输入,复用推挽输出	
    		#ifdef USART1_IQR
    			USART1->CR1|=1<<5;//开启串口接收中断
    			STM32_NVIC_SetPriority(USART1_IRQn,0,1);//设置优先级
    		#endif
    	}
    	else if(USART2 == USARTx)
    	{
    		/*1.开时钟*/
    		RCC->APB2ENR|=1<<2;//PA时钟
    		RCC->APB1ENR|=1<<17;//USART2时钟
    		RCC->APB1RSTR|=1<<17;//开复位时钟
    		RCC->APB1RSTR&=~(1<<17);//取消复位
    		/*2.配置GPIO口*/
    		GPIOA->CRL&=0xFFFF00FF;//清除原来寄存器中的值
    		GPIOA->CRL|=0x00008B00;		
    		#ifdef USART2_IRQ
    			USART2->CR1|=1<<5;//串口2接收中断
    			STM32_NVIC_SetPriority(USART2_IRQn,1,2);//设置优先级
    		#endif
    	}
    	else if(USART3 == USARTx)
    	{
    		/*1.开时钟*/
    		RCC->APB2ENR|=1<<3;//PB时钟
    		RCC->APB1ENR|=1<<18;//USART3时钟
    		RCC->APB1RSTR|=1<<18;//开复位时钟
    		RCC->APB1RSTR&=~(1<<18);//取消复位
    		/*2.配置GPIO口*/
    		GPIOB->CRH&=0xFFFF00FF;
    		GPIOB->CRH|=0x00008B00;	
    		#ifdef USART3_IRQ
    			USART3->CR1|=1<<5;//开启接收中断
    			STM32_NVIC_SetPriority(USART3_IRQn,0,0);//设置优先级
    		#endif
    	}
    	else return;
    	/*3.配置串口核心寄存器*/
    	USARTx->BRR=sysclk*1000000/baud;//设置波特率
    	USARTx->CR1|=1<<2;//接收使能
    	USARTx->CR1|=1<<3;//发送使能
    	USARTx->CR1|=1<<13;//使能串口3
    }
    /************************串口发送字符************************/
    void Usartx_SendString(USART_TypeDef *USARTx,u8 *str,u8 len)
    {
    	while(len--)
    	{
    		USARTx->DR=*str;
    		while((USARTx->SR&1<<7)==0){}//等待数据发送完成
    		str++;
    	}
    }
    /***************printf重定向**************/
    int fputc(int c,FILE *stream)
    {
    	USART1->DR=c;
    	while(!(USART1->SR&1<<7)){}
    	return c;
    }
    
    • 中断接收数据 - - 环形缓冲区接收
    /********************串口接收数据结构体********************/
    #define USART1_LEN 200 //缓冲区大小
    typedef struct 
    {
    	char buff[USART1_LEN];//缓冲区
        u8 usart1_rx_len;//保存的数据长度
        u8 usart1_flag;//数据接收完成标志
    	u8 w;//写
    	u8 r;//读
    }USART1_RX;
    USART1_RX USART1_rx={{0},0,0,0,0};//串口接收数据缓冲区初始化
    void USART1_IRQHandler(void)
    {
    	u8 c;
    	if(USART1->SR&1<<5)
    	{
            c=USART1->DR;
            //当写入的数据长度==缓冲区长度,表示缓冲区满
            if(USART1_rx.usart1_rx_len<USART1_LEN)
            {            
                //写入数据到缓冲区
                USART1_rx.buff[USART1_rx.w]=c;
                USART1_rx.w=(USART1_rx.w+1)%USART1_LEN;//防止地址越界
                USART1_rx.usart1_rx_len++;
                TIM2->CNT=0;//清空计数器值   
    			TIM2->CR1|=1<<0;
            }
            else USART1_rx.usart1_flag=1;//缓冲区满	
    	}
    	USART1->SR=0;//清除标志位
    }
    
    • 读取缓冲区数据
    /**********************从缓冲区读取数据******************
    **
    **形参:u8 *tx_data -- 读取数据保存地址
    **
    *********************************************************/
    u8 Usart1_Annular_txdata(u8 *tx_data)
    {   
        u8 len=0;
        //缓冲区为空 或者 USART1_rx.usart1_flag 数据接收完成标志(为了兼容字符串接收处理)
        if(USART1_rx.usart1_rx_len==0 || USART1_rx.usart1_flag==0)return 0;
        while(USART1_rx.usart1_rx_len)
        {
            *tx_data=USART1_rx.buff[USART1_rx.r];//读取缓冲区数据
            USART1_rx.r= (USART1_rx.r+1)%USART1_LEN;
            USART1_rx.usart1_rx_len--;//缓冲区长度-1
            tx_data++;
            len++;
        }
        USART1_rx.usart1_flag=0;//清除标志位
        *tx_data='\0';//接收到的字符保存为字符串
        return len;//返回读取到的字符个数
    }
    
    • 主函数
    #include "stm32f10x.h"
    #include "usart.h"
    #include "led.h"
    #include "timer.h"
    u8 buff[200];
    int main()
    {
        u8 stat=0;
    	Led_Init();//LED初始化
    	Usartx_Init(USART1,115200,72);
    	TIMx_Init(TIM2,72,20000);//通过定时器2辅助串口接收数据,20ms
    	printf("串口初始化完成\r\n");
    	/*轮询*/
    	while(1)
    	{
            stat=Usart1_Annular_txdata(buff);
            if(stat)
            {
                Usartx_SendString(USART1,buff,stat);
            }
    	}
    }
    
    • 效果展示
      在这里插入图片描述

    示例工程

      示例工程链接:https://download.csdn.net/download/weixin_44453694/14981774

    展开全文
  • STM32串口接收字符串

    2020-11-30 16:30:38
    CPU是STM32F103RBT6,外接8M晶振。 该程序实现了串口1中断接收字符串(必须有换行符结尾,即勾选上串口调试助手上的“换行符”再发送)。方式完毕后,通过按键(GPIO)的方式再将...字符串缓冲区设为20(可自己调节)
  • stm32串口环形缓冲区

    stm32串口环形缓冲区

    一、ringbuff.h

    #ifndef _RING_BUFFER_H_
    #define _RING_BUFFER_H_
    
    enum
    {
    	RING_FALSE,
    	RING_TRUE,
    };
    
    enum
    {
    	RING_ERR,
    	RING_OK,
    };
    
    typedef struct ringBuff{
        unsigned int head;     //队列的头部            
        unsigned int tail;     //队列的尾部          
        unsigned int size;     //队列的大小         
        unsigned char *buffer; //数据缓冲区指针
    }RingBuff_t, *pRingBuff_t;
    
    void RingBuff_Init(RingBuff_t *pRingBuff, unsigned char *pBuff, unsigned int buf_size);
    int RingBuff_ReadNByte (RingBuff_t *pRingBuff, unsigned char *pData, int size);
    int RingBuff_WriteNByte(RingBuff_t *pRingBuff, unsigned char *pData, int size);
    int RingBuff_GetLen(RingBuff_t *pRingBuff);
    unsigned char RingBuff_GetHeadItem(RingBuff_t *pRingBuff);
    unsigned char RingBuff_GetIndexItem(RingBuff_t *pRingBuff, int index);
    
    #endif
    
    

    二、ringbuff.c

    #include "ring_buffer.h"
    #include "main.h"
    #include "stdio.h"
    
    //环形缓冲区初始化
    void RingBuff_Init(RingBuff_t *pRingBuff, unsigned char *pBuff, unsigned int buf_size)
    {
    	if(NULL == pRingBuff || NULL == pBuff)
    		return;
    	
    	pRingBuff->head = 0;
    	pRingBuff->tail = 0;
    	pRingBuff->size = buf_size;
    	pRingBuff->buffer = pBuff;
    }
    
    //判断环形缓冲区是否为空
    int RingBuff_isEmpty(RingBuff_t *pRingBuff) 
    {
    	if(NULL == pRingBuff)
    		return RING_FALSE;
    	
    	if(pRingBuff->head == pRingBuff->tail)
    	{
    		return RING_TRUE;
    	}
    	return RING_FALSE;
    }
    
    //判断环形缓冲区是否为满
    static int RingBuff_isFull(RingBuff_t *pRingBuff)
    {
    	if(NULL == pRingBuff)
    		return RING_FALSE;
    
    	if((pRingBuff->tail+1)%pRingBuff->size == pRingBuff->head)
    	{
    		return RING_TRUE;
    	}
    	return RING_FALSE;
    }
    
    //从环形缓冲区取出一个字节
    int RingBuff_ReadOneByte(RingBuff_t *pRingBuff, unsigned char *pData)
    {
    	if(NULL == pRingBuff || NULL == pData)
    		return RING_ERR;
    
    	//判空
    	if(RING_TRUE == RingBuff_isEmpty(pRingBuff))
    	{
    		//printf("ring buffer is empty!\r\n");
    		return RING_ERR;
    	}
    
    	*pData = pRingBuff->buffer[pRingBuff->head];
    	pRingBuff->head = (pRingBuff->head+1)%pRingBuff->size;
    	return RING_OK;
    }
    
    //向环形缓冲区写入一个字节
    static int RingBuff_WriteOneByte(RingBuff_t *pRingBuff, unsigned char *pData)
    {
    	if(NULL == pRingBuff || NULL == pData)
    		return RING_ERR;
    
    	//判满
    	if(RING_TRUE == RingBuff_isFull(pRingBuff))
    	{
    		//printf("ring buffer is full!\r\n");
    		return RING_ERR;
    	}
    
    	pRingBuff->buffer[pRingBuff->tail] = *pData;
    	pRingBuff->tail = (pRingBuff->tail+1)%pRingBuff->size;
    	return RING_OK;
    }
    
    //从环形缓冲区读多个字节
    int RingBuff_ReadNByte(RingBuff_t *pRingBuff, unsigned char *pData, int size)
    {
    	if(NULL == pRingBuff || NULL == pData)
    		return RING_ERR;
    
    	for(int i = 0; i < size; i++)
    	{
    		RingBuff_ReadOneByte(pRingBuff, pData+i);
    	}
    	return RING_OK;
    }
    
    //向环形缓冲区写多个字节
    int RingBuff_WriteNByte(RingBuff_t *pRingBuff, unsigned char *pData, int size)
    {
    	if(NULL == pRingBuff || NULL == pData)
    		return RING_ERR;
    
    	for(int i = 0; i < size; i++)
    	{
    		RingBuff_WriteOneByte(pRingBuff, pData+i);
    	}
    	return RING_OK;
    }
    
    //获取当前环形缓冲区中数据长度
    int RingBuff_GetLen(RingBuff_t *pRingBuff)
    {
    	if(NULL == pRingBuff)
    		return RING_ERR;
    
    	if(pRingBuff->tail >= pRingBuff->head)
    	{
    		return pRingBuff->tail - pRingBuff->head;
    	}
    	
    	return pRingBuff->tail + pRingBuff->size - pRingBuff->head;
    }
    
    //获取当前头部数据
    unsigned char RingBuff_GetHeadItem(RingBuff_t *pRingBuff)
    {
    	if(NULL == pRingBuff)
    		return RING_ERR;
    	
    	return pRingBuff->buffer[pRingBuff->head];
    }
    
    //获取指定下标数据
    unsigned char RingBuff_GetIndexItem(RingBuff_t *pRingBuff, int index)
    {
    	if(NULL == pRingBuff || index > pRingBuff->size-1)
    		return RING_ERR;
    
    	return pRingBuff->buffer[index%pRingBuff->size];
    }
    

    三、应用例程(配合串口接受DMA使用)

    extern UART_HandleTypeDef huart1;
    RingBuff_t Uart1_RingBuff;
    uint8_t USART1_DMA_Buff[USART1_DMA_SIZE];
    uint8_t USART1_RingBuff[USART1_RING_BUFF_SIZE];
    
    //通讯接口环形缓冲区初始化
    void Data_InterFace_Init(void)
    {
    	RingBuff_Init(&Uart1_RingBuff, USART1_RingBuff, USART1_RING_BUFF_SIZE);
    }
    
    //串口DMA接收完成回调函数
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
    	if(&huart1 == huart)
    	{
    		//将数据拷贝到串口环形缓冲区中
    		RingBuff_WriteNByte(&Uart1_RingBuff, USART1_DMA_Buff, USART1_DMA_SIZE);
    		
    		//使能串口接收DMA
    		HAL_UART_Receive_DMA(&huart1, USART1_DMA_Buff, USART1_DMA_SIZE);
    	}
    }
    //使能串口DMA
    void Board_Init(void)
    {
    	//接口缓冲区初始化
    	Data_InterFace_Init();
    	
    	//使能串口1接收DMA
    	HAL_UART_Receive_DMA(&huart1, USART1_DMA_Buff, USART1_DMA_SIZE);
    }
    
    /* USER CODE END Header_Uart_Dma_Task */
    void Uart_Dma_Task(void const * argument)
    {
      /* USER CODE BEGIN Uart_Dma_Task */
        pParameter_t parameter = NULL;
        pRingBuff_t pRingBuff = NULL;
    	uint8_t data_buff[10];
    	uint8_t flag, t_data;
    	
    	parameter = (pParameter_t)argument;
    	pRingBuff = parameter->Data_InterFace[UART1_NUM].pRingBuffCB;
    	/* Infinite loop */
    	for(;;)
    	{	
    		while(!RingBuff_isEmpty(pRingBuff))
    		{
    			if(RingBuff_GetHeadItem(pRingBuff) == 0xEB
    			 &&RingBuff_GetIndexItem(pRingBuff, pRingBuff->head+1) == 0x90)
    			{
    				if(RingBuff_GetLen(pRingBuff) >= 10)
    				{
    					RingBuff_ReadNByte(pRingBuff, data_buff, 10);
    					flag = 1;
    				}
    				else
    				{
    					break;
    				}
    			}
    			else
    			{
    				RingBuff_ReadOneByte(pRingBuff, &t_data);
    			}
    		}
    		
    		//判断是否有收到报文
    		if(1 == flag)
    		{
    			flag = 0;
    			BSP_Uart1_SendData(data_buff, 10);
    		}
    		
    		osDelay(10);
    	}
      /* USER CODE END Uart_Dma_Task */
    }
    
    展开全文
  • STM32高速串口通信DMA收发实现测试平台平台晶振BSP库串口STM32F030C8T612MHz标准库UART1、UART2STM32F103ZET68MHz标准库UART1、UART2...实现功能收/发环形缓冲区不定长度接收处理高速(1.5Mbps)通信不丢数据关键...
  • STM32串口通信(基于缓冲区

    千次阅读 2020-05-08 19:11:54
    一、串口的初始化和中断设置 1、初始化GPIO: ...当然啦,在使用STM32外设的时候不要忘记打开外设时钟(GPIO和USART的RCC)。 GPIO_InitTypeDef GPIO_InitStructure; //开启串口和GPIO的时钟 RCC_APB2P...
  • 这种处理方式是没有缓冲区的,当数量太大的时候,亦或者当数据接收太快的时候,我们来不及处理已经收到的数据, 那么,当再次收到数据的时候,就会将之前还未处理的数据覆盖掉。那么就会出现丢包的现象了,对我们的...
  • STM32串口DMA接收双缓冲

    千次阅读 热门讨论 2021-01-03 14:52:59
    不再赘述,参见博客 STM32 串口DMA发送+DMA接收+硬件双缓冲区切换功能实现 二、MCU不支持DMA双缓冲,但可通过DMA传输半完成中断替代,以下代码已在F103上验证通过。 1.先通过STM32CubeMX生成串口初始化代码 串口...
  • = pdPASS){//没有收到数据继续读,读到就跳出执行163行后面程序,(读取队列Rx_Message_Queue过程中把读到的数据拷贝到ch缓冲区中,读取成功以后就会将队列中的这条数据删除) if (shell->line_curpos == 0)//一条...
  • STM32 串口环形队列

    2019-02-21 09:20:19
    环形缓冲区,适用于接收外部数据时用作缓冲 STM32 QUEUE_DATA_TYPE* cbWrite(QueueBuffer *cb); QUEUE_DATA_TYPE* cbRead(QueueBuffer *cb); void cbReadFinish(QueueBuffer *cb); void cbWriteFinish(QueueBuffer *...
  • 该工程包含了整个实现代码,并添加了注释,提供了软件计时器多任务创建及调度接口函数,以及环形缓冲区完整接口函数。
  • 从队列到串口缓冲区的实现   串口环形缓冲区收发:在很多入门级教程中,我们知道的串口收发都是:接收一个数据,触发中断,然后把数据发回来。这种处理方式是没有缓冲的,当数量太大的时候,亦或者当数据接收太...
  • 在写串口通信前阅读了STM32中文参考手册,然后满心澎湃地写代码。在这个过程中遇一些让人郁闷的事情,目前这些问题目前已经解决了,特此来总结一番。
  • stm32串口1串口2 DMA方式收发数据

    热门讨论 2014-08-15 22:58:24
    stm32串口1串口2,DMA...使用定时器定时查询DMA接收到的数据,当串口的数据空闲中断,将数据拷贝到缓冲区,交由其他程序处理。可以接收任意大小的数据包。本方法占用CPU时间极少,尤其是波特率很高时,效果更加明显。
  • 适合STM32全系列的串口应用软件包,功能丰富,包含环形缓冲区应用,中断写入缓冲,应用读取缓冲,命令帧解析根据需要自定义,包含从串口终端输入整数/字符串,极大地方便调试测试;
  • 串口1、2、3、4串口内被动发数据,串口之间通信互发数据,读取串口缓冲区进行收发都可以,资源冲突已经解决,可以直接用。口均已启用DMA及空闲中断,支持不定长度数据接收。
  • 目前STM32家族中有些系列支持DMA的双缓冲模式,比如STM32F2/STM32F4/STM32F7等系列。尤其随着人们对STM32F4/F7系列应用不断拓宽和加深,在设计中运用到DMA双缓冲的场合也越来越多。STM32芯片中的DMA又可分为两大类,...
  • STM32 串口 FIFO

    千次阅读 2021-09-24 11:05:25
    使用FIFO实现串口数据的收发功能 FIFO的相关实现参照链接:CSDNhttps://mp.csdn.net/mp_blog/creation/editor/120448361 1、Cubemx串口配置 使用Cubmx对串口进行配置如下: ​​​​​​​ 2、驱动...
  • 空闲中断,DMA,cube,环形缓冲区
  • 下面是串口DMA+环形缓冲区的实现,将读写接口抽象出来,实现不定长度的数据收发。关于环形缓冲区参考:/******************************************************************************* Copyright (C) 2016, ...
  • STM32串口DMA之双缓冲

    千次阅读 2021-04-20 18:59:24
    STM32串口DMA之双缓冲 前言 在前两章节中已经讲述了串口的通常用法, 【STM32】CubeMX+HAL库之串口 ...这时我们增加一个缓冲区来存放来不及处理的数据,就能更好地完成任务。双缓冲还有一个重要的应用
  • STM32F103串口DMA发送

    2019-10-08 16:01:31
    STM32F103单片机上实现,串口1将接收到的数据通过DMA发送出去,串口2通过DMA直接发送缓冲区数据。
  • stm32 usb数据缓冲区疑问,顶点分配说明,stm32 USB开发的时候,关于那个512缓冲区8个顶点buff分配问题。新手会看着代码摸不着头脑。这是居体说明。usb_conf.h文件里面,如#define ENDP0_RXADDR (0x40) #define ENDP0...
  • 通常Arduino中调整串口缓存大小的方法是修改HardwareSerial.h中的常量。 其实根本无需修改系统core中的定义值,只需要在代码最上方添加以下常量定义,抢在HardwareSerial.h之前定义缓存大小就可以了。 #define ...
  • STM32串口通讯协议

    千次阅读 2021-10-10 10:26:09
    下面我来说说STM32中的这个通讯协议: 一般情况下我们一次不会发送一大串太长的东东,所以我们先规定最大接收字节数,一般设为200,可以根据需要调整,大于这个数,我们就判断为接收出错,重新接收,我们接收到的...
  • 基于stm32f407的串口环形队列及DMA收发中断数据处理,连接了串口1的收发DMA通道,组合环形队列实现数据的缓存处理,亲测有效,可能存在变量类型不一致的问题,重新定义一下即可,欢迎交流。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,317
精华内容 2,126
热门标签
关键字:

stm32串口缓冲区大小