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

使用硬件平台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)
{  	 
	int32_t RecvNum = 0;
    int32_t WriteNum = 0;
/* 
* 配合串口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缓冲马上传输完成剩余最后一截子的数据,拷贝到缓冲区中,在进行偏移量的复位 */
				RecvNum = pUartAttr->DmaSize - pUartAttr->DamOffset;	
				/*将获取到的数据放到数据接收缓冲区中*/                                           
		        WriteNum = CfifoBuff_Write(&pUartAttr->AcceptCFifo,(char *)(pUartAttr->pReadDma + pUartAttr->DamOffset), RecvNum);  
				if(WriteNum != RecvNum)
		        {
		            printf("Uart ReadFifo is not enough\r\n");
		        }


				/*复位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);  
} 

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
gd32f4固件是为GD32F4系列微控制器开发的一套软件开发工具,可以帮助开发者更快速、便捷地进行嵌入式系统的开发。下面是关于gd32f4固件使用指南: 1. 安装固件:首先,需要从官方网站上下载gd32f4固件的安装包。安装包中包含了固件、示例代码和用户手册。安装完成后,可以将固件添加到集成开发环境(IDE)中。 2. 创建项目:在IDE中创建一个新的项目,选择gd32f4的目标芯片型号。然后,添加所需的固件文件到项目中。 3. 配置系统时钟:在项目初始化的阶段,需要配置系统时钟。可以根据实际需求选择不同的时钟源和分频系数,以满足系统性能和功耗要求。 4. 配置外设:根据项目需求,配置芯片内部的外设,如GPIO、USART、SPI等。可以通过固件提供的函数进行配置,例如设置引脚的输入输出模式、设置中断等。 5. 编写应用程序:在main函数中编写自己的应用程序。可以使用固件提供的函数来操作外设,读写寄存器,实现各种功能。 6. 调试与下载:在调试过程中,可以使用IDE提供的调试工具来单步调试程序,观察变量值和寄存器状态。完成调试后,通过编译生成的可执行文件,将程序下载到目标芯片中进行测试或部署。 以上是gd32f4固件使用指南,通过学习和了解这些内容,开发者可以快速上手和开发基于GD32F4系列微控制器的嵌入式系统。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shuangwei我是伟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值