DMA循环模式ringbuf缓冲区搭建

一、为什么需要

        最简单的串口数据处理机制是数据接收并原样回发的机制是:成功接收到一个数,触发进入中断,在中断函数中将数据读取出来,然后立即。这一种数据处理机制是“非缓冲中断方式”,虽然这种数据处理方式不消耗时间,但是这种数据处理方式严重的缺点是:数据无缓冲区,如果先前接收的的数据如果尚未发送完成(处理完成),然后串口又接收到新的数据,新接收的数据就会把尚未处理的数据覆盖,从而导致“数据丢包”。

  对于“数据丢包”,最简单的办法就是使用一个数组来接收数据:每接收一个数据,数组下标偏移。虽然这样的做法能起到一定的“缓冲效果”,但是数组的空间得不到很好的利用,已处理的数据仍然会占据原有的数据空间,直到该数组“满载”(数组的每一个元素都保存了有效的数据),将整个数组的数据处理完成后,重新清零数组,才能开启新一轮的数据接收。

  那么,有什么好的数据接收处理机制既能起到“缓冲”效果,又能有效地利用“数组空间”?答案是:有的,那就是“环形缓冲区”。

二、是什么

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

关于循环缓冲区的介绍详见http://t.csdn.cn/lOieP

三、怎么做

直接上代码 , 函数说明在read函数下面

原理请看注释

 /**
  * @brief  接收字符串.
  * @param  uart_port: uart 端口处理结构的地址.
  * @param  buff: 接收缓冲区.
  * @param  read_len: 读取长度.
  * @retval 取出的数据长度.
  */
uint16_t uart_read_data_unit(UART_HandleTypeDef * huart, uint8_t * buff, uint16_t read_len)
{
    if (huart == NULL)
    {
        return 0;
    }

    usart_dma2ringbuf_t * usart_dma2ringbuf = get_usart_dma2ringbuf(huart);   //获取串口的专属缓冲区地址
    usart_dma2ringbuf->tail = usart_dma2ringbuf->uart_dma_rx_buff_size - __HAL_DMA_GET_COUNTER(huart->hdmarx);//获取缓冲区的尾

    uint16_t head = usart_dma2ringbuf->head;  //  头
    uint16_t tail = usart_dma2ringbuf->tail;    //  尾
    uint16_t size = usart_dma2ringbuf->uart_dma_rx_buff_size;   //循环缓冲区的大小
    uint16_t len = 0;   // 

    uint8_t* rx_buff = get_uart_rx_buffer(huart);    // 这里的意思就是用数组的方式去存取DMA总线上的数据,将串口地址映射到
                                                     //数组首地址

    if (head < tail)    //判断是否溢出
    {
        /* 长度 */
        len = tail - head; //   当前数据长度

        if (len >= read_len)    //   判断可读数据长度(当前数据长度)是否大于等于数据长度,读取的长度其实就是定义的   512
        {                       //   也就是当缓冲中的数据一次读不完的时候,进行头变量偏移,以至于下一次可以定位到未读完的数据的数据头
            /* Oversupply. */
            memcpy(buff, rx_buff + head, read_len);       
            head += read_len;   //  头变量偏移  读一位向后偏移一位
            len = read_len;     //  修正长度
        }
        else
        {
            /* Less than demand */
            memcpy(buff, rx_buff + head, len);   //当数据可以一次性就读完
            head = tail;    //  头尾重合就意味着缓冲区为空
        }
    }
    else if (head > tail)    // 原因是读的速度和存的速度不一致导致的   //可以理解为它虽然是一个循环缓冲区,但采用的方式是变量偏移而不是指针偏移
                            //  所以有溢出的概念
    {
        /* 溢出前的长度 */
        len = size - head;   // 计算溢出前未读取的数据长度

        if (len >= read_len)     
        {
            /* Oversupply. */
            memcpy(buff, rx_buff + head, read_len);   // 读取数据
            head += read_len;    //变量偏移
            len = read_len;
        }
        else
        {
            /* Less than demand */

            /* 首先将数据处理到缓冲区的末尾 */
            memcpy(buff, rx_buff + head, len);

            if (tail + len >= read_len)
            {
                /* Oversupply. */

                /* 继续从缓冲区开始 */
                memcpy(buff + len, rx_buff, (read_len - len));
                head = (read_len - len);
                len = read_len;
            }
            else
            {
                /* Less than demand */
                /* 继续从缓冲区开始 */
                memcpy(buff + len, rx_buff, tail);
                /* 累加溢出后的长度 */
                len += tail;
                head = tail;
            }
        }
    }

    /* 更新结构体指针 */
    usart_dma2ringbuf->head = head;
    usart_dma2ringbuf->tail = tail;
    usart_dma2ringbuf->available -= len;
    return len;
}    



//************相应函数说明



//选取相应串口,返回串口地址

usart_dma2ringbuf_t* get_usart_dma2ringbuf(UART_HandleTypeDef *huart) {
    if(huart->Instance == USART1)
    {
        return &husart_dma2ringbuf1;
    }
    else if(huart->Instance == USART2)
    {
        return &husart_dma2ringbuf2;
    }
    else if(huart->Instance == USART3)
    {
        return &husart_dma2ringbuf3;
    }
    else if(huart->Instance == USART6)
    {
        return &husart_dma2ringbuf6;
    }
    return 0;
}
 

 
// DMA循环缓冲区结构体
typedef struct 
{
    UART_HandleTypeDef* huart; 		/* uart instance pointer */ 
    uint16_t uart_dma_rx_buff_size; /* DMA ring buffer size */
    uint16_t head; 					/* DMA ring buffer head */
    uint16_t tail; 					/* DMA ring buffer tail */
	uint16_t available;				/* number of bytes already get */
	uint16_t last_space;
}usart_dma2ringbuf_t ;    
    

//开始循环缓冲区
void start_usart_ringbuf(UART_HandleTypeDef * uart_port)
{
    /* Enable DMA rx complete interupt */
    if(uart_port->hdmarx !=NULL )
    {
		//__HAL_DMA_ENABLE_IT(uart_port->hdmarx, DMA_IT_TC);

		/* Disable Half rx Interrupt */
		__HAL_DMA_DISABLE_IT(uart_port->hdmarx, DMA_IT_HT);

		usart_dma2ringbuf_t *usart_dma2ringbuf = get_usart_dma2ringbuf(uart_port);

		uint8_t *rx_buffer = get_uart_rx_buffer(uart_port);

		/* Set rx ringbuffer */
		HAL_UART_Receive_DMA(uart_port, rx_buffer,usart_dma2ringbuf->uart_dma_rx_buff_size);
    }

    /* Enable DMA tx complete interupt */
    if(uart_port->hdmatx !=NULL )
    {
    	__HAL_DMA_DISABLE_IT(uart_port->hdmatx, DMA_IT_TC);
        /* Disable Half tx Interrupt */
        __HAL_DMA_DISABLE_IT(uart_port->hdmatx, DMA_IT_HT);
    }
}

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值