打印
[研电赛技术支持]

GD32F4 USART+cfifo+dma+IDLE 全套使用GD库配置

[复制链接]
1452|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
wiba|  楼主 | 2023-8-15 11:44 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
使用硬件平台GD32F407VET6

1、GDF4串口配置+DMA

/*!
\brief      configure DMA
\param[in]  none
\param[out] none
\retval     none
*/
void dma_config(void)
{
                dma_single_data_parameter_struct dma_init_struct;
                nvic_irq_enable(DMA0_Channel6_IRQn, 0, 0);
                /* enable DMA0 */
                rcu_periph_clock_enable(RCU_DMA0);
                /* deinitialize DMA channel6(USART1 TX) */
                dma_deinit(DMA0, DMA_CH6);
                dma_init_struct.direction = DMA_MEMORY_TO_PERIPH;
                dma_init_struct.memory0_addr = (uint32_t)0;
                dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
                dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
                dma_init_struct.number = 0;
                dma_init_struct.periph_addr = USART1_DATA_ADDRESS;
                dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
                dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
                dma_single_data_mode_init(DMA0, DMA_CH6, &dma_init_struct);
                /* configure DMA mode */
                dma_circulation_disable(DMA0, DMA_CH6);
                dma_channel_subperipheral_select(DMA0, DMA_CH6, DMA_SUBPERI4);
                /* enable DMA0 channel6 transfer complete interrupt */
                dma_interrupt_enable(DMA0, DMA_CH6, DMA_CHXCTL_FTFIE);
               
                /* USART DMA enable for transmission and reception */
                usart_dma_transmit_config(USART1, USART_TRANSMIT_DMA_ENABLE);
               
               
               
               
                nvic_irq_enable(DMA0_Channel5_IRQn, 0, 0);
                dma_deinit(DMA0, DMA_CH5);
                dma_init_struct.direction = DMA_PERIPH_TO_MEMORY;
                dma_init_struct.memory0_addr = (uint32_t)sUartAttr.pReadDma;  
                dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
                dma_init_struct.number = MW_UART_DMA_LEN;   
                dma_init_struct.periph_addr = (uint32_t)&USART_DATA(USART1);
                dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
                dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
                dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
                dma_single_data_mode_init(DMA0, DMA_CH5, &dma_init_struct);
               
                /* configure DMA mode */
                dma_circulation_enable(DMA0, DMA_CH5);  /* 配置DMA的轮询模式,因为dma0的通道5对应RX,所以配置为轮询模式 */
                dma_channel_subperipheral_select(DMA0, DMA_CH5, DMA_SUBPERI4);
                /* enable DMA0 channel2 transfer complete interrupt */
                dma_interrupt_enable(DMA0, DMA_CH5, DMA_CHXCTL_FTFIE);
               
                /* 使能串口的DMA接收 */
                usart_dma_receive_config(USART1, USART_RECEIVE_DMA_ENABLE);
               
                //        /* enable DMA channel6 */
                //        dma_channel_enable(DMA0, DMA_CH6);
}

void USART1_config(void)
{
                nvic_irq_enable(USART1_IRQn, 2, 3);
                /* enable GPIO clock */
                rcu_periph_clock_enable(RCU_GPIOD);
                /* enable USART clock */
                rcu_periph_clock_enable(RCU_USART1);
               
                /* configure the USART0 TX pin and USART0 RX pin */
                gpio_af_set(GPIOD, GPIO_AF_7, GPIO_PIN_5);
                gpio_af_set(GPIOD, GPIO_AF_7, GPIO_PIN_6);
               
                /* configure USART0 TX as alternate function push-pull */
                gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_5);
                gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5);
               
                /* configure USART0 RX as alternate function push-pull */
                gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_6);
                gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
               
                /* USART configure */
                usart_deinit(USART1);
                usart_baudrate_set(USART1, 115200);
                usart_word_length_set(USART1, USART_WL_8BIT);
                usart_stop_bit_set(USART1, USART_STB_1BIT);
                usart_parity_config(USART1, USART_PM_NONE);
                usart_hardware_flow_rts_config(USART1, USART_RTS_DISABLE);
                usart_hardware_flow_cts_config(USART1, USART_CTS_DISABLE);
                usart_receive_config(USART1, USART_RECEIVE_ENABLE);
                usart_transmit_config(USART1, USART_TRANSMIT_ENABLE);
                usart_enable(USART1);
                /* 使能串口的空闲中断 */
                usart_interrupt_enable(USART1, USART_INT_IDLE);
                printf("USART_Init a usart transmit test example!");
               
                //        usart_interrupt_enable(USART1, USART_INT_RBNE);
               
}
/*!
\brief      initialize exmc_norsram_parameter_struct with the default values
\param[in]  none
\param[out] exmc_norsram_init_struct: the initialized struct exmc_norsram_parameter_struct pointer
\retval     none
*/
void USART_Init()
{  
                /************************** 串口初始化 **********************************/
                USART1_config();
                /************************** DMA初始化 ****************** DMA0 CH6 ****************/
                dma_config();
}





2、中断处理函数
/*!
    \brief      this function handles DMA1_Channel7_IRQHandler interrupt
    \param[in]  none
    \param[out] none
    \retval     none
*/
uint8_t tran_OK = 0;
uint8_t tran_OKing = 0;
void DMA0_Channel6_IRQHandler(void)
{
/*
* 配合串口1的发送。功能是继续发送缓冲区未发送的数据。
* 发送完成配置的CNT次数后,会进入此中断函数
* tran_OKing:缓冲区不为空后,自加1。
* tran_OKing:表示缓冲区为空,自加1。
*/
                int32_t TransNum = 0;
                if(dma_interrupt_flag_get(DMA0, DMA_CH6, DMA_INT_FLAG_FTF)) {
                                dma_interrupt_flag_clear(DMA0, DMA_CH6, DMA_INT_FLAG_FTF);
                               
                                MW_UART_ATTR *pUartAttr = &sUartAttr;
                               
                                /*从发送循环缓冲区中获取数据*/
                                TransNum = CfifoBuff_Read(&pUartAttr->SendCFifo,(char *)(pUartAttr->pWriteDma),pUartAttr->DmaSize);
                                if(TransNum > 0)
                                {               
                                                tran_OKing++;
                                                usart1_dma_send(pUartAttr->pWriteDma, TransNum);
                                }
                                else
                                {
                                                tran_OK++;
                                                pUartAttr->TransFlag = MW_TRANS_IDLE;
                                }
                }
}

uint8_t rx_OK = 0;
void DMA0_Channel5_IRQHandler(void)
{  
/*
* 配合串口1的接收空闲中断。功能是复位DMA的偏移量
* 1、为了串口1的空闲中断在处理数据时防止越界,将 pUartAttr->DamOffset置为0;
* 2、DMA为轮询方式进行数据搬运的,当搬运完配置的Cnt后,会进入此中断处理函数,
* 3、当有数据过来时,数据将会拷贝到缓冲区的起始位置;
* 4、注意:如果DMA为正常模式,那么当完成一次拷贝后,DMA会自动disable掉。
*/
                if(dma_interrupt_flag_get(DMA0, DMA_CH5, DMA_INT_FLAG_FTF))
                {
                                dma_interrupt_flag_clear(DMA0, DMA_CH5, DMA_INT_FLAG_FTF);
                                rx_OK++;
                               
                               
                                MW_UART_ATTR *pUartAttr = &sUartAttr;
                                /*复位DMA偏移量*/
                                pUartAttr->DamOffset = 0;
                }
}
/*!
    \brief      this function handles USART interrupt request
    \param[in]  none
    \param[out] none
    \retval     none
*/
void USART1_IRQHandler(void)
{                                
/* 串口1的接收空闲中断方式进行了数据缓存。*/
                int32_t RecvNum = 0;
                int32_t WriteNum = 0;
                int32_t DmaIdleNum = 0;
                if(RESET != usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE))
                {
                                /* clear IDLE flag */
                                usart_data_receive(USART1);
                               
                               
                                MW_UART_ATTR *pUartAttr = &sUartAttr;
                               
                                /*计算在DMA缓冲区需要获取的数据长度*/
                                DmaIdleNum = dma_transfer_number_get(DMA0, DMA_CH5);
                                RecvNum = pUartAttr->DmaSize - DmaIdleNum - pUartAttr->DamOffset;
                                /*将获取到的数据放到数据接收缓冲区中*/                                          
                                WriteNum = CfifoBuff_Write(&pUartAttr->AcceptCFifo,(char *)(pUartAttr->pReadDma + pUartAttr->DamOffset),RecvNum);
                               
                                if(WriteNum != RecvNum)
                                {
                                                printf("Uart ReadFifo is not enough\r\n");
                                }
                                /*计算获取数据位置的偏移量*/
                                pUartAttr->DamOffset += RecvNum;
                }
}



3、缓冲区的代码实现
3.1 cfifo_usart_dma.c
#include "cfifo_usart_dma.h"
#include "gd32f4xx_libopt.h"




/* 发送接收DMA的数据缓存 */
static uint8_t Uart1TxDma[MW_UART_DMA_LEN] = {0};        //DMA发送缓存区
static uint8_t Uart1RxDma[MW_UART_DMA_LEN] = {0}; //DMA接受缓存区



/* 一个串口的维护发送、接收、缓存的数据结构 */
MW_UART_ATTR sUartAttr;  


/*!
    \brief      串口1进行DMA的数据发送
    \param[in]  buffer:发送缓冲区
    \param[in]  len:发送缓冲区大小
    \param[out] none
    \retval     none
*/
void usart1_dma_send(uint8_t *buffer,uint16_t len)
{
        dma_channel_disable(DMA0, DMA_CH6);
       
        dma_memory_address_config(DMA0, DMA_CH6,0,(uint32_t)buffer);
       
        dma_transfer_number_config(DMA0, DMA_CH6, len);
       
        /*清除DMA0_CH6 发送完成的标志*/
        dma_flag_clear(DMA0, DMA_CH6,DMA_INTC_FTFIFC);
       
        dma_channel_enable(DMA0, DMA_CH6);
}

/*!
    \brief      初始化串口的缓冲区指针、DMA的空间大小、DMA偏移量、发送接收队列的初始化
    \param[in]  none
    \param[out] none
    \retval     MW_SUCCESS:成功  MW_FAIL:失败
*/
int8_t MW_UART_Init()
{
  /*为属性的参数附初值*/
        MW_UART_ATTR *pUartAttr = &sUartAttr;         
        pUartAttr->DamOffset = 0;
        pUartAttr->TransFlag = MW_TRANS_IDLE;
        pUartAttr->DmaSize = MW_UART_DMA_LEN;
        pUartAttr->pReadDma = Uart1RxDma;
        pUartAttr->pWriteDma = Uart1TxDma;
        CfifoBuff_Init(&pUartAttr->AcceptCFifo);  //用于数据接受
        CfifoBuff_Init(&pUartAttr->SendCFifo);    //用于数据发送
         
        return MW_SUCCESS;
}

/*!
    \brief      通过循环队列的方式进行数据的发送
    \param[in]  buffer:发送缓冲区
    \param[out] len:发送缓冲区大小
    \retval     写入发送缓冲区的长度
*/
int32_t MW_UART_Transmit(uint8_t* buffer,int32_t len)
{
        int32_t TransNum = 0;
        int32_t TransLen = 0;
        MW_UART_ATTR *pUartAttr = &sUartAttr;
        /*将要发送的数据先写入循环缓冲区*/
        TransNum = CfifoBuff_Write(&pUartAttr->SendCFifo, (char *) buffer, len);

        /*如果发送DMA未在发送中,则使能发送*/
        if(pUartAttr->TransFlag == MW_TRANS_IDLE)
        {
                TransLen = CfifoBuff_Read(&pUartAttr->SendCFifo,(char *)(pUartAttr->pWriteDma),pUartAttr->DmaSize);
                if(TransLen > 0)
                {
                        pUartAttr->TransFlag = MW_TRANS_BUSY;
                        usart1_dma_send(pUartAttr->pWriteDma, TransLen);
                }                               
        }
         
        return TransNum;
}










3.2 cfifo_usart_dma.h
#ifndef _CFIFO_USART_DMA_H_
#define _CFIFO_USART_DMA_H_

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/

/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdio.h>
#include "cfifo.h"
/* USER CODE END Includes */

/* USER CODE BEGIN Private defines */
#define MW_FAIL              0
#define MW_SUCCESS           1

#define MW_TRANS_IDLE        0
#define MW_TRANS_BUSY        1

#define MW_UART_DMA_LEN      512

/* USER CODE END Private defines */
typedef struct
{  
         int16_t TransFlag;                      /*数据发送标志位*/      
         int32_t DmaSize;                        /*DMA缓冲区的大小*/
         int32_t DamOffset;                      /*获取数据在DMA缓冲区的偏移量*/
         uint8_t *pReadDma;                      /*指向接收DMA缓冲区的首地址*/
         uint8_t *pWriteDma;                     /*指向发送DMA缓冲区的首地址*/
         CfifoBuff AcceptCFifo;                  /*接受数据的循环缓冲区*/
         CfifoBuff SendCFifo;                    /*发送数据的循环缓冲区*/
}MW_UART_ATTR;       

extern MW_UART_ATTR sUartAttr;

/* USER CODE BEGIN Prototypes */
int8_t MW_UART_Init();        //初始化
int32_t MW_UART_Transmit(uint8_t* buffer,int32_t len);  //CFIFO+DMA+Usart数据发送
void usart1_dma_send(uint8_t *buffer,uint16_t len);
/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif
#endif /* _CFIFO_USART_DMA_H_ */




3.3 cfifo.c
#include "cfifo.h"


/*环形CFIFO初始化*/
void CfifoBuff_Init(CfifoBuff * Cfifo_pointer)
{
   //初始化相关信息
   Cfifo_pointer->Head = 0;
   Cfifo_pointer->Tail = 0;
   Cfifo_pointer->Lenght = 0;
   memset(Cfifo_pointer->BUFF,'\0',CFIFO_SIZE);  //环形CFIFO缓存区初始化
}

/*环形CFIFO数据清除*/
void CfifoBuff_Clear(CfifoBuff * Cfifo_pointer)
{
   //清除相关信息
   Cfifo_pointer->Head = 0;
   Cfifo_pointer->Tail = 0;
   Cfifo_pointer->Lenght = 0;
   memset(Cfifo_pointer->BUFF,'\0',CFIFO_SIZE); //环形CFIFO缓存区清除
}



/*环形CFIFO数据写入*/
/*
*参数说明:
*         Cfifo_pointer————环形CFIFO结构体
*         User_buff————待写入数据
*         num————写入数据长度
*
*返回值说明:正确写入到FIFO缓存区中的数据长度
*
*功能说明:将User_buff中的数据写入到环形CFIFO缓存区
*
*/
int16_t CfifoBuff_Write(CfifoBuff * Cfifo_pointer,char * User_buff,uint16_t num)
{
        int16_t i , wrint_num;
       
    if(Cfifo_pointer->Lenght >= CFIFO_SIZE) //判断缓存区是否已满
    {
                 wrint_num=-1;
                 
         return wrint_num;       //数据溢出
    }
               
    if(Cfifo_pointer->Lenght+num<CFIFO_SIZE)  //判断写入的数据长度是否超出当前可写入的最大值
        {
                wrint_num=num;
        }
        else
        {
                wrint_num=CFIFO_SIZE-Cfifo_pointer->Lenght;          
        }
               
        for(i=0;i<wrint_num;i++)
        {
            Cfifo_pointer->BUFF[Cfifo_pointer->Tail]=*(User_buff+i);
                       
                Cfifo_pointer->Tail = (Cfifo_pointer->Tail+1)%CFIFO_SIZE;//防止越界非法访问
        }       
               
    Cfifo_pointer->Lenght+=wrint_num;
               
    return wrint_num;  //返回正确写入的数据长度
}


/*环形CFIFO读取*/
/*
*参数说明:
*         Cfifo_pointer————环形CFIFO结构体
*         User_buff————读取数据存放地
*         num————读取数据长度
*
*返回值说明:正确读取到User_buff的数据长度
*
*功能说明:将环形CFIFO缓存区的数据读取到User_buff
*
*/
int16_t CfifoBuff_Read(CfifoBuff * Cfifo_pointer,char * User_buff,uint16_t num)
{
        int16_t i , read_num;
       
    if(Cfifo_pointer->Lenght == 0) //判断非空
    {
        read_num=-1;
                 
        return read_num;       //没有数据
    }       
       
   if(Cfifo_pointer->Lenght-num>=0)       //判断读取的数据长度是否超出当前可读取的最大值
   {
                read_num=num;
   }
   else
   {
            read_num=Cfifo_pointer->Lenght;          
   }
               
   for(i=0;i<read_num;i++)
   {
            *(User_buff+i)=Cfifo_pointer->BUFF[Cfifo_pointer->Head];
                       
                Cfifo_pointer->Head = (Cfifo_pointer->Head+1)%CFIFO_SIZE;//防止越界非法访问
   }       
               
    Cfifo_pointer->Lenght-=read_num;
               
    return read_num;  //返回正确写入的数据长度
}





3.4 cfifo.h
#ifndef _CFIFO_H_
#define _CFIFO_H_

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/

/* USER CODE BEGIN Includes */
#include <string.h>
#include <stdio.h>
#include "stdint.h"
/* USER CODE END Includes */

/* USER CODE BEGIN Private defines */

#define CFIFO_SIZE 2048           //环形队列CFIFO大小


/*环形CFIFO结构体*/
typedef struct
{
        uint16_t Head;       //环形CFIFO队列头
        uint16_t Tail;             //环形CFIFO队列尾
  uint16_t Lenght;     //环形CFIFO数据长度
        uint8_t  BUFF[CFIFO_SIZE];     //环形CFIFO缓存区
}CfifoBuff;


/* USER CODE END Private defines */


/* USER CODE BEGIN Prototypes */
void CfifoBuff_Init(CfifoBuff * Cfifo_pointer);  //CFIFO初始化
void CfifoBuff_Clear(CfifoBuff * Cfifo_pointer); //CFIFO数据清除
int16_t CfifoBuff_Write(CfifoBuff * Cfifo_pointer,char * User_buff,uint16_t num); //CFIFO数据写人
int16_t CfifoBuff_Read(CfifoBuff * Cfifo_pointer,char * User_buff,uint16_t num); //CFIFO数据读出
/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif

#endif /* _CFIFO_H_ */



4、调用循序
先调用初始化函数MW_UART_Init , 再调用调用初始化函数USART_Init 。

5、自定义打印函数
#include "string.h"
#include "stdio.h"
#include "stdarg.h

void DbgPrintf(const char *format,...)
{
        uint16_t len;
        char sbuf[128];
        va_list args;       
        va_start(args, format);
        len = vsnprintf((char *)sbuf, sizeof(sbuf)-1, (char *)format, args);
        va_end(args);
       
        MW_UART_Transmit(sbuf, len);  
}


————————————————
版权声明:本文为CSDN博主「shuangwei我是伟」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_50089156/article/details/131791100

使用特权

评论回复
沙发
tpgf| | 2024-2-4 16:48 | 只看该作者
请问什么叫做cfifo?和普通的fifo有什么区别呢

使用特权

评论回复
板凳
晓伍| | 2024-2-4 17:27 | 只看该作者
一般情况下串口数据是临时存放在哪里呢

使用特权

评论回复
地板
wowu| | 2024-2-4 22:02 | 只看该作者
调用循环的时候需要对循环次数进行限制吗

使用特权

评论回复
5
xiaoqizi| | 2024-2-4 22:38 | 只看该作者
如果使用dma的话 所有的配置都应该在一个函数里边配置完成吗

使用特权

评论回复
6
木木guainv| | 2024-2-4 23:07 | 只看该作者
串口使用dma是不是仅仅解放了单片机而不能提高通讯速率啊

使用特权

评论回复
7
磨砂| | 2024-2-4 23:46 | 只看该作者
idle是什么功能 是说的一种空闲模式吗

使用特权

评论回复
8
申小林一号| | 2024-4-30 16:39 | 只看该作者
非常不错的帖子,值得推广扩散!!!

使用特权

评论回复
9
为你转身| | 2024-6-30 15:23 | 只看该作者
我还会注意添加边界检查或断言,以确保对缓冲区溢出或下溢等潜在问题的安全性处理。

使用特权

评论回复
10
wanduzi| | 2024-6-30 16:47 | 只看该作者
GD的库好用不

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

77

主题

3305

帖子

3

粉丝