UART 串口空闲中断+DMA传输半完成、完成中断

文章目录

  • UART 串口空闲中断+DMA传输半完成、完成中断
  • 简介
  • 协议格式
  • 波特率
  • 起始位
  • 停止位
  • 校验位(非必须)
  • 数据位
  • 硬件连接
  • 实现方式
  • 程序实现
  • 缓冲区定义
  • gpio初始化
  • uart初始化
  • usart_dma 配置
  • 中断优先级 配置
  • 初始化调用
  • 中断服务函数
  • 串口DMA及其他函数
  • 使用例程
  • 使用说明
  • 发送函数接口为
  • 数据接收使用例程
  • 缓冲区支持
  • UART 串口空闲中断+DMA传输半完成、完成中断

    简介

    UART(Universal Asynchronous Receiver/Transmitter)通用异步收发传输器,UART 作为异步串口通信协议提供标准异步收发模式。是在应用程序开发过程中使用频率最高的数据总线。

    UART串口的特点是将数据一位一位地顺序传送,只要 2 根传输线就可以实现双向通信,一根线发送数据的同时用另一根线接收数据。UART 串口通信有几个重要的参数,分别是波特率、起始位、数据位、停止位和奇偶检验位,对于两个使用 UART 串口通信的端口,这些参数必须匹配,否则通信将无法正常完成。UART 串口传输的数据格式如下图所示,仅为8 数据位和 1 停止位模式,其他不做介绍

    协议格式

    波特率

    数据位、起始位、停止位的维持电平时间的倒数,例如波特率为115200bps时,数据位宽度约为8.68us

    起始位

    起始位为一个周期宽度的低电平

    停止位

    停止位为一个周期宽度的高电平,根据芯片不同有时可以配置成0.5、1、1.5或者2个停止位。

    校验位(非必须)

    偶校验位,奇校验位,无校验位,上图中为无校验。

    数据位

    数据位一般是8位,根据芯片不同可能有4、5、6、7、8、9等数量;

    硬件连接

    实现方式

    为减轻处理器的负担,可以采用串口中断搭配DMA访问数据缓冲区。

  • 数据发送

    1. 串口发送完成中断
    2. 串口DMA传输
  • 数据接收

    方式 优点 缺点
    串口RXNE 实现简单 每收一个字节中断一次,在数据量大时,会频繁触发中断浪费CPU资源
    串口空闲中断+DMA传输 可以接收不定长数据,将连续数据接收完成后才触发中断 如果单次接收数据长度超过DMA配置长度就会有数据丢失,对单片机缓冲区和数据发送方有要求。
    串口空闲中断+DMA传输完成、半完成中断 可以接收不定长数据,DMA数据传输完成一半、传输完成时触发DMA中断处理数据;串口空闲中断触发时处理数据。 实现复杂
  • 这里我采用发送方式为串口DMA;接收方式为串口空闲中断+DMA传输完成、半完成中断。

    实际上DMA传输半完成中断和完成中断的实现类似于双缓冲区接收,只不过使用同一块连续内存,半完成使用前半部分,完成使用后半部分。仅使用DMA中断还存在一个隐患,当尾数据数量不足导致半完成中断始终未触发,此时就需要借助串口空闲中断将尾数据提取出来。

    例如上图中DMA长度为4,假如串口接收数据为byte0-byte8,共9个字节,会触发两次DMA半完成中断,两次DMA完成中断,这样可以收取前8字节,尾数据仅剩byte8是无法通过DMA中断来收取,这时引入串口空闲中断来收尾即可。

    程序实现

    STM32F407VET6+标准库

    缓冲区定义
    // 串口发送缓冲区大小
    #define FK_LOG_TX_BUFFER       512        // 根据自己需求配置
    #define FK_LOG_TX_DMA_BUFFER   64       // dma发送buffer
    
    // 串口接收缓冲区大小
    #define FK_LOG_RX_BUFFER       512     // 根据自己需求配置
    #define FK_LOG_RX_DMA_BUFFER   64      // dma接收buffer
    
    // dma发送缓冲区
    struct log_tx_buffer
    {
        struct fk_ringbuffer ringbuffer;                    // 环队
        int                  flag;                          // 发送完成标志
        fk_uint8_t           buff[FK_LOG_TX_BUFFER];        // 环队空间申请
        char                 dma_buff[FK_LOG_TX_DMA_BUFFER];// dma发送缓冲区
    };
    
    // dma接收缓冲区
    struct log_rx_buffer
    {
        struct fk_ringbuffer ringbuffer;                    // 环队
        int                  flag;                          // 发送完成标志
        fk_uint8_t           buff[FK_LOG_RX_BUFFER];        // 环队空间申请
        char                 dma_buff[FK_LOG_RX_DMA_BUFFER];// dma接收缓冲区
    };
    #define LOG_BUFF_DEFAULT    {.flag=1};
    
    static struct log_tx_buffer log_tx = LOG_BUFF_DEFAULT;
    static struct log_rx_buffer log_rx = LOG_BUFF_DEFAULT;
    
    gpio初始化
    static void usart_gpio_config(void)
    {//GPIO
        GPIO_InitTypeDef  GPIO_InitStructure;
    
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
        // 复用功能配置
        GPIO_PinAFConfig(GPIOA, GPIO_PinSource9,  GPIO_AF_USART1);
        GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
    
        GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
        GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;  
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    
        // PA9 USART1-TX
        GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
    
        // PA10 USART1-RX
        GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10;
        GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;  
        GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
    }
    
    uart初始化
    static void usart_config(void)
    {// USART
        USART_InitTypeDef USART_InitStructure;
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
        USART_DeInit( USART1 );
    
        USART_InitStructure.USART_BaudRate      = 115200;
        USART_InitStructure.USART_WordLength    = USART_WordLength_8b;
        USART_InitStructure.USART_StopBits      = USART_StopBits_1;
        USART_InitStructure.USART_Parity        = USART_Parity_No;
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
        USART_InitStructure.USART_Mode          = USART_Mode_Rx | USART_Mode_Tx;
    
        USART_Init( USART1, &USART_InitStructure );
    
        USART_ClearFlag(USART1, USART_FLAG_TC);
        USART_ClearFlag(USART1, USART_FLAG_RXNE);
    
        USART_Cmd( USART1, ENABLE);
    }
    
    usart_dma 配置
    static void usart_dma_config(DMA_Stream_TypeDef *DMAx_Streamx   ,
                                 uint32_t            DMA_Channel_x  ,
                                 uint32_t            DMA_PAddr      ,
                                 uint32_t            DMA_dir        )
    {
        DMA_InitTypeDef DMA_InitStructure;
    //DMA TX    DMA2-CH4
        // DMA时钟
        if( DMAx_Streamx<=DMA1_Stream7 )
        {
            RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
        }
        else
        {
            RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
        }
    
        // 复位初始化DMA数据流
        DMA_DeInit(DMAx_Streamx);
        // 确保DMA数据流复位完成
        while (DMA_GetCmdStatus(DMAx_Streamx) != DISABLE)  {
        }
    
        // usart1 tx对应dma2,通道4,数据流7
        DMA_InitStructure.DMA_Channel = DMA_Channel_x;  
        // 设置DMA源:串口数据寄存器地址
        DMA_InitStructure.DMA_PeripheralBaseAddr = DMA_PAddr;
        // 方向:从内存到外设
        DMA_InitStructure.DMA_DIR = DMA_dir;
        // 外设地址不增
        DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 
        // 内存地址自增
        DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
        // 外设数据单位-1Byte
        DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
        // 内存数据单位-1Byte
        DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
        // DMA模式:不循环
        DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
        // 优先级:low
        DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
        /*禁用FIFO*/
        DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
        DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
        // 存储器突发传输 16个节拍
        DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
        // 外设突发传输 1个节拍
        DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    
        // 内存地址-要传输的变量的指针
        DMA_InitStructure.DMA_Memory0BaseAddr = 0;
        // 传输大小
        DMA_InitStructure.DMA_BufferSize = 0;
    
        // 配置DMA2的数据流7
        DMA_Init(DMAx_Streamx, &DMA_InitStructure);
    
        /*使能DMA*/
        DMA_Cmd(DMAx_Streamx, DISABLE);
    //    DMA_Cmd(DMA2_Stream7, ENABLE);
    
     //   LOG_D( "USART1 init" );
    }
    
    中断优先级 配置
    static void nvic_irq_config(uint8_t NVIC_IRQChannel, uint8_t Priority)
    {
        NVIC_InitTypeDef NVIC_InitStructure;
    
        NVIC_InitStructure.NVIC_IRQChannel = NVIC_IRQChannel;//串口1发送中断通道
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = Priority;   //优先级
        NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
    }
    
    初始化调用
    static void usart_init(void)
    {
        // uart配置
        usart_gpio_config();
        usart_config();
    
        //RX
        // dma
        usart_dma_config(   DMA2_Stream5               ,
                            DMA_Channel_4              ,
                            (uint32_t)(&USART1->DR)    ,
                            DMA_DIR_PeripheralToMemory );
        // 传输完成、半完成中断
        nvic_irq_config(DMA2_Stream5_IRQn, 15);
        DMA_ITConfig(DMA2_Stream5, DMA_IT_TC, ENABLE);
        DMA_ITConfig(DMA2_Stream5, DMA_IT_HT, ENABLE);
        usart1_dma_recv( log_rx.dma_buff, sizeof(log_rx.dma_buff) );
        // 空闲中断
        nvic_irq_config(USART1_IRQn, 10);
        USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
    //    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    
    
        //TX
        // dma
        usart_dma_config(   DMA2_Stream7               ,
                            DMA_Channel_4              ,
                            (uint32_t)(&USART1->DR)    ,
                            DMA_DIR_MemoryToPeripheral );
        // 使能中断
        nvic_irq_config(DMA2_Stream7_IRQn, 15);
        // 传输完成中断
        DMA_ITConfig(DMA2_Stream7, DMA_IT_TC, ENABLE);
    
        // uart启动
        USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
        USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
    }
    
    中断服务函数
    void USART1_IRQHandler(void)
    {
        fk_size_t length = 0;
    
        if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)//接收到一帧数据 空闲中断触发
        {
            USART1->SR;//先读SR
            USART1->DR;//再读DR
            USART_ClearFlag(USART1,USART_FLAG_IDLE);
    
            // 接收数据入队
            length = sizeof(log_rx.dma_buff) - DMA_GetCurrDataCounter(DMA2_Stream5);
    
            fk_ringbuffer_put(  &log_rx.ringbuffer, \
                                ( fk_uint8_t *)log_rx.dma_buff, \
                                length);
    
            // 启动下次dma传输
            DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_TCIF5);     //清除DMA2_Steam7传输完成标志
            usart1_dma_recv( log_rx.dma_buff, sizeof(log_rx.dma_buff) );
    
        }
    
    //    if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)//接收到一帧数据 空闲中断触发
    //    {
    //        USART_ClearFlag(USART1,USART_FLAG_RXNE);
    //        
    //        
    //    }
    
    }
    // 发送中断
    void DMA2_Stream7_IRQHandler(void)
    {
        fk_size_t length = 0;
        //清除标志
        if(DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7)!=RESET)//等待DMA2_Steam7传输完成
        {
            DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7);     //清除DMA2_Steam7传输完成标志
            DMA_Cmd(DMA2_Stream7,DISABLE);                  //关闭使能
    
            // 串口空闲
            log_tx.flag=1;
    
            // 获取缓冲区剩余数据,再次发送
            length = fk_ringbuffer_get(&log_tx.ringbuffer, (fk_uint8_t*)log_tx.dma_buff, sizeof(log_tx.dma_buff));
            if( length )
            {
                usart1_dma_send( log_tx.dma_buff, length);
                log_tx.flag=0;  // 串口忙碌
            }
        }
    }
    // 接收
    void DMA2_Stream5_IRQHandler(void)
    {
        //清除标志
        if(DMA_GetFlagStatus(DMA2_Stream5,DMA_FLAG_TCIF5)!=RESET)//等待DMA2_Steam7传输完成
        {
            DMA_Cmd(DMA2_Stream5,DISABLE);                  //关闭使能
    
    //        log_rx.flag=1;
    
            // 接收数据入队 (log_rx.dma_buff+sizeof(log_rx.dma_buff)/2)
            fk_ringbuffer_put(  &log_rx.ringbuffer, \
                                ( fk_uint8_t *)(log_rx.dma_buff+sizeof(log_rx.dma_buff)/2), \
                                sizeof(log_rx.dma_buff));
            usart1_dma_recv( log_rx.dma_buff, sizeof(log_rx.dma_buff) );
    
            DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_TCIF5);     //清除DMA2_Steam7传输完成标志
        }
        if(DMA_GetFlagStatus(DMA2_Stream5,DMA_FLAG_HTIF5)!=RESET)// 半完成
        {
            // 接收数据入队
            fk_ringbuffer_put(  &log_rx.ringbuffer, \
                                ( fk_uint8_t *)log_rx.dma_buff, \
                                sizeof(log_rx.dma_buff)/2);
    
            DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_HTIF5);
        }
    }
    
    串口DMA及其他函数
    /***************************** DMA 发送 ********************************/
    static void usart1_dma_send(const char *add, unsigned int length)
    {
        while( !log_tx.flag ) { }
        log_tx.flag=0;
        //关闭DMA传输
        DMA_Cmd(DMA2_Stream7, DISABLE);
        DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7);
        DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7);     //清除DMA2_Steam7传输完成标志
        //确保DMA可以被设置
        while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE){}
    
    //    DMA2_Stream7->M0AR = (uint32_t)add;
        DMA_MemoryTargetConfig(DMA2_Stream7, (uint32_t) add, DMA_Memory_0);
        //数据传输量
        DMA_SetCurrDataCounter(DMA2_Stream7, length);
        //开启DMA传输
        DMA_Cmd(DMA2_Stream7, ENABLE);
    }
    
    /***************************** DMA 接收 ********************************/
    static void usart1_dma_recv(const char *add, unsigned int length)
    {
        //关闭DMA传输
        DMA_Cmd(DMA2_Stream5, DISABLE);
        DMA_ClearFlag(DMA2_Stream5,DMA_FLAG_TCIF5);     //清除DMA2_Steam7传输完成标志
        //确保DMA可以被设置
        while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE){}
    
        DMA2_Stream7->M0AR = (uint32_t)add;
        DMA_MemoryTargetConfig(DMA2_Stream5, (uint32_t) add, DMA_Memory_0);
        //数据传输量
        DMA_SetCurrDataCounter(DMA2_Stream5, length);
        //开启DMA传输
        DMA_Cmd(DMA2_Stream5, ENABLE);
    }
    
    // fk_printf 重新实现
    #include <string.h>
    void fk_hw_console_output(const char *str)
    {
        usart1_dma_send( str, strlen(str));
    }
    int fk_printf(const char *fmt, ...)
    {
        char log_buff[FK_LOG_TX_BUFFER];
        va_list args;
        fk_size_t length = 0;
        fk_size_t len = 0;
    
        va_start(args, fmt);
    
    // 阻塞
    //    while( fk_ringbuffer_data_len(&log_queue) )
    //    {
    //    }
    
    // 非阻塞
        length = log_tx.ringbuffer.buffer_size-fk_ringbuffer_data_len(&log_tx.ringbuffer);
     //   FK_ASSERT(length<=FK_LOG_BUFFER);
    
        // 2.字符串格式化 - 有可能缓冲区(FK_LOG_TX_BUFFER)不足导致丢数
        length = vsnprintf(log_buff, length, fmt, args);
    
        // 3.入队
        len = fk_ringbuffer_put(&log_tx.ringbuffer, (const fk_uint8_t *)log_buff, length);
    
        // 4.串口空闲?
        if(log_tx.flag)
        {// 串口空闲,发起dma
            length = fk_ringbuffer_get(&log_tx.ringbuffer, (fk_uint8_t*)log_tx.dma_buff, sizeof(log_tx.dma_buff));
            usart1_dma_send( log_tx.dma_buff, length);
        }
    
        va_end(args);
    
        // 实际数量
        return len;
    }
    
    int fk_send(const fk_uint8_t *add, unsigned int length)
    {
        fk_size_t len = 0;
    
    // 阻塞
    //    while( fk_ringbuffer_data_len(&log_queue) )
    //    {
    //    }
    
    // 非阻塞
    
        // 3.入队
        len = fk_ringbuffer_put(&log_tx.ringbuffer, (const fk_uint8_t *)add, length);
    
        // 4.串口空闲?
        if(log_tx.flag)
        {// 串口空闲,发起dma
            length = fk_ringbuffer_get(&log_tx.ringbuffer, (fk_uint8_t*)log_tx.dma_buff, sizeof(log_tx.dma_buff));
            usart1_dma_send( log_tx.dma_buff, length);
        }
    
        return len;
    }
    
    // 串口接收数据回显
    int fk_recv_test(void)
    {
        fk_size_t  len;
        static fk_uint8_t buff[512];
    
        // 缓冲区数据获取
        len = fk_ringbuffer_get(&log_rx.ringbuffer, (fk_uint8_t*)buff, sizeof(buff));
    
        if( len )
        {// 获取成功发送
            fk_send( buff, len);
        }
    }
    
    
    /********************************* 环境初始化 ********************************/
    
    int board_env_start_up(void)
    {
        NVIC_PriorityGro    upConfig( NVIC_PriorityGroup_4 );
    // 0. 时钟
    // 1. dbg串口 // dbg uart
    //    log_tx.flag = 1;// 空闲
    //    log_rx.flag = 1;
        fk_ringbuffer_init  (&log_tx.ringbuffer,log_tx.buff,sizeof(log_tx.buff));
        fk_ringbuffer_reset (&log_tx.ringbuffer);
        fk_ringbuffer_init  (&log_rx.ringbuffer,log_rx.buff,sizeof(log_rx.buff));
        fk_ringbuffer_reset (&log_rx.ringbuffer);
    
        usart_init();
    
    // 2. systick
    //    systick_config();
    
    
    
        return 0;
    }
    
    使用例程
    int main(void){
    
        board_env_start_up();
    
        fk_printf("fk_printf:hello world!!!\r\n");
        fk_send("QWER\r\n",6);
    
        while ( 1 )
        {
            fk_recv_test();
        }
    }
    
    使用说明
    发送函数接口为
    int fk_printf(const char *fmt, ...);
    int fk_send(const fk_uint8_t *add, unsigned int length);
    
    数据接收使用例程
    int fk_recv_test(void)
    {
        fk_size_t  len;
        static fk_uint8_t buff[512];
    
        // 缓冲区数据获取
        len = fk_ringbuffer_get(&log_rx.ringbuffer, (fk_uint8_t*)buff, sizeof(buff));
    
        if( len )
        {
            // len非0 获取数据成功,数据处理
            fk_send( buff, len);
        }
    }
    
    缓冲区支持

    以上操作中用到了环形缓冲区(FIFO),用作串口接收发送的缓冲空间

    // ringbuffer.h
    #ifndef __RING_BUFFER_H__
    #define __RING_BUFFER_H__
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    //#include <framework.h>
    
        typedef signed   char                   fk_int8_t;      /**<  8bit integer type */
        typedef signed   short                  fk_int16_t;     /**< 16bit integer type */
        typedef signed   int                    fk_int32_t;     /**< 32bit integer type */
        typedef unsigned char                   fk_uint8_t;     /**<  8bit unsigned integer type */
        typedef unsigned short                  fk_uint16_t;    /**< 16bit unsigned integer type */
        typedef unsigned int                    fk_uint32_t;    /**< 32bit unsigned integer type  */  
        typedef fk_ubase_t                      fk_size_t;      /**< Type for size number */
        typedef fk_base_t                       fk_ssize_t;     /**< Used for a count of bytes or an error indication */
        #define fk_inline                   static __inline
    
    
    /* ring buffer */
    struct fk_ringbuffer
    {
        fk_uint8_t *buffer_ptr;
        /* use the msb of the {read,write}_index as mirror bit. You can see this as
         * if the buffer adds a virtual mirror and the pointers point either to the
         * normal or to the mirrored buffer. If the write_index has the same value
         * with the read_index, but in a different mirror, the buffer is full.
         * While if the write_index and the read_index are the same and within the
         * same mirror, the buffer is empty. The ASCII art of the ringbuffer is:
         *
         *       mirror = 0  当前缓冲区       mirror = 1 镜像缓冲区
         * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+
         * | 0 | 1 | 2 | 3 | 4 | 5 | 6 ||| 0 | 1 | 2 | 3 | 4 | 5 | 6 | Full
         * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+
         *  read_idx-^                   write_idx-^
         *
         * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+
         * | 0 | 1 | 2 | 3 | 4 | 5 | 6 ||| 0 | 1 | 2 | 3 | 4 | 5 | 6 | Empty
         * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+
         * read_idx-^^-write_idx
         */
    
        fk_uint32_t read_mirror : 1;
        fk_uint32_t read_index : 31;
        fk_uint32_t write_mirror : 1;
        fk_uint32_t write_index : 31;
        /* as we use msb of index as mirror bit, the size should be signed and
         * could only be positive. */
        fk_int32_t buffer_size;
    };
    
    enum ringbuffer_state
    {
        RINGBUFFER_EMPTY,
        RINGBUFFER_FULL,
        /* half full is neither full nor empty */
        RINGBUFFER_HALFFULL,
    };
    
    void        fk_ringbuffer_init          (struct fk_ringbuffer *rb, fk_uint8_t *pool, fk_int32_t size);
    void        fk_ringbuffer_reset         (struct fk_ringbuffer *rb);
    fk_size_t   fk_ringbuffer_put           (struct fk_ringbuffer *rb, const fk_uint8_t *ptr, fk_uint32_t length);
    fk_size_t   fk_ringbuffer_put_force     (struct fk_ringbuffer *rb, const fk_uint8_t *ptr, fk_uint32_t length);
    fk_size_t   fk_ringbuffer_putchar       (struct fk_ringbuffer *rb, const fk_uint8_t ch);
    fk_size_t   fk_ringbuffer_putchar_force (struct fk_ringbuffer *rb, const fk_uint8_t ch);
    fk_size_t   fk_ringbuffer_get           (struct fk_ringbuffer *rb, fk_uint8_t *ptr, fk_uint32_t length);
    fk_size_t   fk_ringbuffer_peek          (struct fk_ringbuffer *rb, fk_uint8_t **ptr);
    fk_size_t   fk_ringbuffer_getchar       (struct fk_ringbuffer *rb, fk_uint8_t *ch);
    fk_size_t   fk_ringbuffer_data_len      (struct fk_ringbuffer *rb);
    
    /**
     * @brief Get the buffer size of the ring buffer object.
     *
     * @param rb        A pointer to the ring buffer object.
     *
     * @return  Buffer size.
     */
    fk_inline fk_uint32_t fk_ringbuffer_get_size(struct fk_ringbuffer *rb)
    {
        FK_ASSERT(rb != FK_NULL);
        return rb->buffer_size;
    }
    
    /** return the size of empty space in rb */
    #define fk_ringbuffer_space_len(rb) ((rb)->buffer_size - fk_ringbuffer_data_len(rb))
    
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif
    

    ringbuffer.c

    #include <ringbuffer.h>
    
    #include <string.h>     // memcpy
    #include <stdlib.h>     // 动态内存
    #define fk_memcpy   memcpy
    
    
    // 静态 获取队列当前状态
    fk_inline enum ringbuffer_state fk_ringbuffer_status(struct fk_ringbuffer *rb)
    {
    //  队列头尾索引相同
        if (rb->read_index == rb->write_index)
        {
            if (rb->read_mirror == rb->write_mirror)
                return RINGBUFFER_EMPTY;
            else
                return RINGBUFFER_FULL;
        }
        return RINGBUFFER_HALFFULL;
    }
    
    /**
     * @brief Initialize the ring buffer object.
     *
     * @param rb        A pointer to the ring buffer object.
     * @param pool      A pointer to the buffer.
     * @param size      The size of the buffer in bytes.
     */
    // 初始化缓冲区
    void fk_ringbuffer_init(struct fk_ringbuffer        *rb,
                            fk_uint8_t                  *pool,
                            fk_int32_t                  size)
    {
        FK_ASSERT(rb != FK_NULL);
        FK_ASSERT(size > 0);
    
        /* initialize read and write index */
        rb->read_mirror = rb->read_index = 0;
        rb->write_mirror = rb->write_index = 0;
    
        /* set buffer pool and size */
        rb->buffer_ptr = pool;
        rb->buffer_size = FK_ALIGN_DOWN(size, FK_ALIGN_SIZE);
    }
    
    /**
     * @brief Put a block of data into the ring buffer. If the capacity of ring buffer is insufficient, it will discard out-of-range data.
     *
     * @param rb            A pointer to the ring buffer object.
     * @param ptr           A pointer to the data buffer.
     * @param length        The size of data in bytes.
     *
     * @return Return the data size we put into the ring buffer.
     */
    // 将 ptr 入队
    fk_size_t fk_ringbuffer_put(struct fk_ringbuffer        *rb,
                                const fk_uint8_t            *ptr,
                                fk_uint32_t                 length)
    {
        fk_uint32_t size;
    
        FK_ASSERT(rb != FK_NULL);
    
        /* whether has enough space */
    // 获取剩余空间
        size = fk_ringbuffer_space_len(rb);
    
        /* no space */
        if (size == 0)
            return 0;
    
    // 丢弃多余数据
        /* drop some data */
        if (size < length)
            length = size;
    
    // 剩余连续空间足够,直接写入
        if (rb->buffer_size - rb->write_index > length)
        {
            /* read_index - write_index = empty space */
            fk_memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
            /* this should not cause overflow because there is enough space for
             * length of data in current mirror */
            rb->write_index += length;
            return length;
        }
    
    // 剩余连续空间不够,分段写入
        fk_memcpy(&rb->buffer_ptr[rb->write_index],
                  &ptr[0],
                  rb->buffer_size - rb->write_index);
        fk_memcpy(&rb->buffer_ptr[0],
                  &ptr[rb->buffer_size - rb->write_index],
                  length - (rb->buffer_size - rb->write_index));
    
    // 写入使用镜像空间,
        /* we are going into the other side of the mirror */
        rb->write_mirror = ~rb->write_mirror;
        rb->write_index = length - (rb->buffer_size - rb->write_index);
    
        return length;
    }
    
    /**
     * @brief Put a block of data into the ring buffer. 
    If the capacity of ring buffer is insufficient, 
    it will overwrite the existing data in the ring buffer.
     *
     * @param rb            A pointer to the ring buffer object.
     * @param ptr           A pointer to the data buffer.
     * @param length        The size of data in bytes.
     *
     * @return Return the data size we put into the ring buffer.
     */
    //强制写入,写不下就覆盖
    fk_size_t fk_ringbuffer_put_force(struct fk_ringbuffer      *rb,
                                      const fk_uint8_t          *ptr,
                                      fk_uint32_t               length)
    {
        fk_uint32_t space_length;
    
        FK_ASSERT(rb != FK_NULL);
    
    // 获取剩余空间
        space_length = fk_ringbuffer_space_len(rb);
    
        if (length > rb->buffer_size)
        {
            ptr = &ptr[length - rb->buffer_size];
            length = rb->buffer_size;
        }
    
        if (rb->buffer_size - rb->write_index > length)
        {
            /* read_index - write_index = empty space */
            fk_memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
            /* this should not cause overflow because there is enough space for
             * length of data in current mirror */
            rb->write_index += length;
    
            if (length > space_length)
                rb->read_index = rb->write_index;
    
            return length;
        }
    
        fk_memcpy(&rb->buffer_ptr[rb->write_index],
                  &ptr[0],
                  rb->buffer_size - rb->write_index);
        fk_memcpy(&rb->buffer_ptr[0],
                  &ptr[rb->buffer_size - rb->write_index],
                  length - (rb->buffer_size - rb->write_index));
    
        /* we are going into the other side of the mirror */
        rb->write_mirror = ~rb->write_mirror;
        rb->write_index = length - (rb->buffer_size - rb->write_index);
    
        if (length > space_length)
        {
            if (rb->write_index <= rb->read_index)
                rb->read_mirror = ~rb->read_mirror;
            rb->read_index = rb->write_index;
        }
    
        return length;
    }
    
    /**
     * @brief Get data from the ring buffer.
     *
     * @param rb            A pointer to the ring buffer.
     * @param ptr           A pointer to the data buffer.
     * @param length        The size of the data we want to read from the ring buffer.
     *
     * @return Return the data size we read from the ring buffer.
     */
    fk_size_t fk_ringbuffer_get(struct fk_ringbuffer        *rb,
                                fk_uint8_t                  *ptr,
                                fk_uint32_t                 length)
    {
        fk_size_t size;
    
        FK_ASSERT(rb != FK_NULL);
    
        /* whether has enough data  */
        size = fk_ringbuffer_data_len(rb);
    
        /* no data */
        if (size == 0)
            return 0;
    
        /* less data */
        if (size < length)
            length = size;
    
        if (rb->buffer_size - rb->read_index > length)
        {
            /* copy all of data */
            fk_memcpy(ptr, &rb->buffer_ptr[rb->read_index], length);
            /* this should not cause overflow because there is enough space for
             * length of data in current mirror */
            rb->read_index += length;
            return length;
        }
    
        fk_memcpy(&ptr[0],
                  &rb->buffer_ptr[rb->read_index],
                  rb->buffer_size - rb->read_index);
        fk_memcpy(&ptr[rb->buffer_size - rb->read_index],
                  &rb->buffer_ptr[0],
                  length - (rb->buffer_size - rb->read_index));
    
        /* we are going into the other side of the mirror */
        rb->read_mirror = ~rb->read_mirror;
        rb->read_index = length - (rb->buffer_size - rb->read_index);
    
        return length;
    }
    
    /**
     * @brief Get the first readable byte of the ring buffer.
     *
     * @param rb        A pointer to the ringbuffer.
     * @param ptr       When this function return, *ptr is a pointer to the first readable byte of the ring buffer.
     *
     * @note It is recommended to read only one byte, otherwise it may cause buffer overflow.
     *
     * @return Return the size of the ring buffer.
     */
    fk_size_t fk_ringbuffer_peek(struct fk_ringbuffer *rb, fk_uint8_t **ptr)
    {
        fk_size_t size;
    
        FK_ASSERT(rb != FK_NULL);
    
        *ptr = FK_NULL;
    
        /* whether has enough data  */
        size = fk_ringbuffer_data_len(rb);
    
        /* no data */
        if (size == 0)
            return 0;
    
        *ptr = &rb->buffer_ptr[rb->read_index];
    
        if ((fk_size_t)(rb->buffer_size - rb->read_index) > size)
        {
            rb->read_index += size;
            return size;
        }
    
        size = rb->buffer_size - rb->read_index;
    
        /* we are going into the other side of the mirror */
        rb->read_mirror = ~rb->read_mirror;
        rb->read_index = 0;
    
        return size;
    }
    
    /**
     * @brief Put a byte into the ring buffer. If ring buffer is full, this operation will fail.
     *
     * @param rb        A pointer to the ring buffer object.
     * @param ch        A byte put into the ring buffer.
     *
     * @return Return the data size we put into the ring buffer. The ring buffer is full if returns 0. Otherwise, it will return 1.
     */
    fk_size_t fk_ringbuffer_putchar(struct fk_ringbuffer *rb, const fk_uint8_t ch)
    {
        FK_ASSERT(rb != FK_NULL);
    
        /* whether has enough space */
        if (!fk_ringbuffer_space_len(rb))
            return 0;
    
        rb->buffer_ptr[rb->write_index] = ch;
    
        /* flip mirror */
        if (rb->write_index == rb->buffer_size - 1)
        {
            rb->write_mirror = ~rb->write_mirror;
            rb->write_index = 0;
        }
        else
        {
            rb->write_index++;
        }
    
        return 1;
    }
    
    /**
     * @brief Put a byte into the ring buffer. If ring buffer is full, it will discard an old data and put into a new data.
     *
     * @param rb        A pointer to the ring buffer object.
     * @param ch        A byte put into the ring buffer.
     *
     * @return Return the data size we put into the ring buffer. Always return 1.
     */
    fk_size_t fk_ringbuffer_putchar_force(struct fk_ringbuffer *rb, const fk_uint8_t ch)
    {
        enum ringbuffer_state old_state;
    
        FK_ASSERT(rb != FK_NULL);
    
        old_state = fk_ringbuffer_status(rb);
    
        rb->buffer_ptr[rb->write_index] = ch;
    
        /* flip mirror */
        if (rb->write_index == rb->buffer_size - 1)
        {
            rb->write_mirror = ~rb->write_mirror;
            rb->write_index = 0;
            if (old_state == RINGBUFFER_FULL)
            {
                rb->read_mirror = ~rb->read_mirror;
                rb->read_index = rb->write_index;
            }
        }
        else
        {
            rb->write_index++;
            if (old_state == RINGBUFFER_FULL)
                rb->read_index = rb->write_index;
        }
    
        return 1;
    }
    
    /**
     * @brief Get the size of data in the ring buffer in bytes.
     *
     * @param rb        The pointer to the ring buffer object.
     *
     * @return Return the size of data in the ring buffer in bytes.
     */
    fk_size_t fk_ringbuffer_data_len(struct fk_ringbuffer *rb)
    {
        switch (fk_ringbuffer_status(rb))
        {
        case RINGBUFFER_EMPTY:
            return 0;
        case RINGBUFFER_FULL:
            return rb->buffer_size;
        case RINGBUFFER_HALFFULL:
        default:
        {
            fk_size_t wi = rb->write_index, ri = rb->read_index;
    
            if (wi > ri)
                return wi - ri;
            else
                return rb->buffer_size - (ri - wi);
        }
        }
    }
    
    /**
     * @brief Reset the ring buffer object, and clear all contents in the buffer.
     *
     * @param rb        A pointer to the ring buffer object.
     */
    void fk_ringbuffer_reset(struct fk_ringbuffer *rb)
    {
        FK_ASSERT(rb != FK_NULL);
    
        rb->read_mirror = 0;
        rb->read_index = 0;
        rb->write_mirror = 0;
        rb->write_index = 0;
    }
    

    作者:toyoutoyou001

    物联沃分享整理
    物联沃-IOTWORD物联网 » UART 串口空闲中断+DMA传输半完成、完成中断

    发表回复