小熊派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和空闲中断实现串口接收