小熊派gd32f303学习之旅(4)—使用DMA实现串口打印

小熊派gd32f303学习之旅(4)—使用DMA实现串口打印

一、前言

在上一篇文章(串口打印第一个Hello world程序)我们使用重定义fputc函数的方式实现了串口打印,但是这种方式本质上是在循环输出字符,比较浪费CPU资源。
解决这个问题的方法就是使用DMA进行发送。
通过串口gd32f30x的用户手册,可以看到,uart0的发送使用的的DMA0的通道3
在这里插入图片描述

二、添加DMA驱动

在之前的uart0_init()函数中添加DMA的初始化,如下:

	/* 定义一个DMA配置结构体 */
	dma_parameter_struct dma_init_struct;
    /* 使能 DMA 时钟 */
    rcu_periph_clock_enable(RCU_DMA0);
	
	/* 初始化 DMA0 通道3 */
    dma_deinit(DMA0, DMA_CH3);
    dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;		/* 存储器到外设方向 */
    dma_init_struct.memory_addr = (uint32_t)UART0_TX_BUF;		/* 存储器基地址 */
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;	/* 存储器地址自增 */
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;		/* 存储器位宽为8位 */
    dma_init_struct.number = UART0_TX_LEN;						/* 传输数据个数 */
    dma_init_struct.periph_addr = ((uint32_t)0x40013804);		/* 外设基地址,即USART数据寄存器地址 */
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;	/* 外设地址固定不变 */
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;	/* 外设数据位宽为8位 */
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;			/* 软件优先级为极高*/
    dma_init(DMA0, DMA_CH3, &dma_init_struct);
    
    /* DMA循环模式配置,不使用循环模式 */
    dma_circulation_disable(DMA0, DMA_CH3);
	/* DMA存储器到存储器模式模式配置,不使用存储器到存储器模式*/
    dma_memory_to_memory_disable(DMA0, DMA_CH3);

    /* USART DMA 发送使能 */
    usart_dma_transmit_config(USART0, USART_DENT_ENABLE);
	/* DMA0 通道3 中断优先级设置并使能 */
	nvic_irq_enable(DMA0_Channel3_IRQn, 0, 0);
	/* 使能 DMA0 通道3 传输完成、传输错误中断 */
    dma_interrupt_enable(DMA0, DMA_CH3, DMA_INT_FTF|DMA_INT_ERR);
    /* 使能 DMA0 通道3 */
    dma_channel_enable(DMA0, DMA_CH3);

然后添加几个全局变量如下:

/* 发送缓存 */
#define UART0_TX_LEN		256				/* 单次最大发送缓存字节数 */
uint8_t UART0_TX_BUF[UART0_TX_LEN]; 		/* 发送缓冲区 */
uint8_t DMA_BUF_BUSY = 0 ;					/* 缓冲区是否已被占用 */

自定义uart0打印函数:

/* 自定义UART0 printf 函数
 * 参数:带发送的字符串,确保一次发送数据不超过UART0_TX_LEN字节
 * 返回值:无	*/
void u1_printf(char* fmt,...)  
{
	uint32_t i;
	va_list ap; 
	va_start(ap,fmt);
	
	while(DMA_BUF_BUSY == 1);
	DMA_BUF_BUSY = 1;
	
	vsprintf((char*)UART0_TX_BUF,fmt,ap);
	va_end(ap);
	UART0_TX_BUF[UART0_TX_LEN-1] = '\0';
	
	/* 计算此次发送数据的长度 */
	i=strlen((const char*)(UART0_TX_BUF));
	
	/* 设置DMA传输 */
	dma_channel_disable(DMA0, DMA_CH3);		/* 关闭DMA传输才可以进行设置 */
	dma_memory_address_config(DMA0,DMA_CH3,(uint32_t)(UART0_TX_BUF));
	dma_transfer_number_config(DMA0,DMA_CH3,i);
	dma_channel_enable(DMA0, DMA_CH3);		/* 开启DMA传输 */
}

因为使用到了DMA中断,使用编写DMA0通道3中断服务函数

/* DMA0 通道3 中断服务函数
 * 参数:无
 * 返回值:无	*/
void DMA0_Channel3_IRQHandler(void)
{
	/* 清除DMA0 通道3 中断标志位 */
	dma_interrupt_flag_clear(DMA0, DMA_CH3, DMA_INT_FLAG_G);
	/* 进入中断,表示已经传输完成缓冲区,释放缓冲区 */
	if(DMA_BUF_BUSY == 1) DMA_BUF_BUSY = 0;
}

三、修改主函数

将主函数的while循环修改为如下所示

    while(1)
	{
		/* 通过串口打印 Hello world! */
		u1_printf("Hello world! ");
		u1_printf("I am William. \r\n");
		
        /* turn on LED */
        LED(1);
        delay_1ms(500);

        /* turn off LED */
        LED(0);
        delay_1ms(500);
    }

四、功能验证

编译链接烧录到小熊派开发板,通过串口调试助手可以看到成功打印:
在这里插入图片描述

五、附录

完整代码我存放在码云,可以查看:https://gitee.com/william_william/BearPi-GD32F303RGT6.git
上一篇:小熊派gd32f303学习之旅(3)—串口打印第一个Hello world程序
下一篇:小熊派gd32f303学习之旅(5)—使用DMA和空闲中断实现串口接收

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页