GD32F103串口DMA收发


GD32F103串口DMA收发

这两年做嵌入式开发太难了,芯片缺货导致方案不断改改改,之前一直没用过国产MCU,从去年开始也渐渐了解了一下国产MCU。原理大同小异,资料相对来说确实少一点,生态也没那么完善。 这个系列打算把项目过程中的一些经验分享给大家,也作为自己的一个成长记录。如果能给路过的各位带来切实收获,请各位不吝点赞,给作者资以鼓励;如果文章中有错误,也请各位不吝指正,共同学习,共同进步!


串口DMA收发

废话不多说,直接上干货,我用的MCU是GD32F103CBT6,串口使用USART0,对应的GPIO是PA9和PA10。

1.串口IO初始化

代码如下:

static void usart_config(uint32_t baudval)
{
	rcu_periph_clock_enable(RCU_GPIOA);	//enable GPIO clock, PA9/PA10
	rcu_periph_clock_enable(RCU_AF);
    rcu_periph_clock_enable(RCU_USART0);	//enable USART clock
	
    gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);	//PA9--TX0
    gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);	//PA10--RX0

    //USART0: 115200
    usart_deinit(USART0);
    usart_baudrate_set(USART0, baudval);
    usart_word_length_set(USART0, USART_WL_8BIT);
    usart_stop_bit_set(USART0, USART_STB_1BIT);
    usart_parity_config(USART0, USART_PM_NONE);
    usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE);
    usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE);
    usart_receive_config(USART0, USART_RECEIVE_ENABLE);
    usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
    usart_enable(USART0);
}

此处串口波特率采用传参方式在初始化串口时传入,实际使用时我用的串口波特率为115200。

2.DMA初始化

关于DMA通道映射详见UserManual 9.4.9 DMA请求映射及DMA各通道请求表。此处一定要仔细,不能搞错,我们使用的是USART0,由表中可知USART_TX对应的是Channel 3,USART_RX对应的是Channel 4。
DMA请求映射
DMA各通道请求表DMA各通道请求表
代码如下:

uint8_t rxbuffer[2]= {'o','k'};
uint8_t txbuffer[10] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a};

#define USART0_DATA_ADDRESS    ((uint32_t)&USART_DATA(USART0))

static void usart_dma_config(void)
{
    dma_parameter_struct dma_init_struct;
    /* enable DMA0 clock */
    rcu_periph_clock_enable(RCU_DMA0);
    
    /* deinitialize DMA channel3(USART0 tx) */
    dma_deinit(DMA0, DMA_CH3);
    dma_struct_para_init(&dma_init_struct);

    
    dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
    dma_init_struct.memory_addr = NULL;
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number = ARRAYNUM(txbuffer);
    dma_init_struct.periph_addr = USART0_DATA_ADDRESS;
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DMA0, DMA_CH3, &dma_init_struct);
    
    /* deinitialize DMA channel4 (USART0 rx) */
    dma_deinit(DMA0, DMA_CH4);
    dma_struct_para_init(&dma_init_struct);

    dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
    dma_init_struct.memory_addr = (uint32_t)rxbuffer;
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number = ARRAYNUM(rxbuffer);;
    dma_init_struct.periph_addr = USART0_DATA_ADDRESS;
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.memory_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DMA0, DMA_CH4, &dma_init_struct);
  
    /* configure DMA mode */
    dma_circulation_disable(DMA0, DMA_CH3);
    dma_memory_to_memory_disable(DMA0, DMA_CH3);
    dma_circulation_enable(DMA0, DMA_CH4);
    dma_memory_to_memory_disable(DMA0, DMA_CH4);
    
    /* USART DMA0 enable for reception */
    usart_dma_receive_config(USART0, USART_DENR_ENABLE);
    /* enable DMA0 channel4 transfer complete interrupt */
    dma_interrupt_enable(DMA0, DMA_CH4, DMA_INT_FTF);
    /* enable DMA0 channel4 */
    dma_channel_enable(DMA0, DMA_CH4);
		
    /* USART DMA0 enable for transmission */
    usart_dma_transmit_config(USART0, USART_DENT_ENABLE);
    /* enable DMA0 channel3 transfer complete interrupt */
//    dma_interrupt_enable(DMA0, DMA_CH3, DMA_INT_FTF);
//    /* disable DMA0 channel3 */
//    dma_channel_disable(DMA0, DMA_CH3);
}

3. 中断配置

串口接收采用中断方式,所以需要配置中断。
串口发送需要在主函数里根据项目实际需求进行发送,故不采用中断方式。

代码如下:

static void nvic_config(void)
{
		nvic_priority_group_set(NVIC_PRIGROUP_PRE3_SUB1);
        nvic_irq_enable(DMA0_Channel4_IRQn, 0, 1);
}

4. 串口初始化

代码如下:

void bsp_usart_init() 
{   
	  usart_config(BAUDRATE);
	  nvic_config();
	  usart_dma_config();		
}

5. 串口发送函数和printf支持实现

代码如下:

void usart0_dma_send(uint8_t *buffer,uint16_t len)
{
	dma_channel_disable(DMA0, DMA_CH3);
	
	dma_memory_address_config(DMA0, DMA_CH3,(uint32_t)buffer);//设置要发送数据的内存地址
	dma_transfer_number_config(DMA0, DMA_CH3, len);//一共发送多少个数据
	
	dma_channel_enable(DMA0, DMA_CH3);
}

int fputc(int ch, FILE *f)
{
    usart_data_transmit(USART0, (uint8_t)ch);
    while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
    return ch;
}

6. 头文件中的宏定义和函数声明如下

代码如下:

#define BAUDRATE	115200U 
//#define BAUDRATE	9600U 
#define ARRAYNUM(arr_name)     (uint32_t)(sizeof(arr_name) / sizeof(*(arr_name)))

 void bsp_usart_init(void);
 
 void usart0_dma_send(uint8_t *buffer,uint16_t len);

 int fputc(int ch, FILE *f);

7. 中断处理

串口接收中断里只需要清对应标志位即可。

void DMA0_Channel4_IRQHandler(void)
{
    if(dma_interrupt_flag_get(DMA0, DMA_CH4, DMA_INT_FLAG_FTF)){     
        dma_interrupt_flag_clear(DMA0, DMA_CH4, DMA_INT_FLAG_G);
    }
}

8. 主函数

主函数里我们采用定时器+计数方式实现1s发送一次txbuffer的数据,当上位机发送数据给串口,数据通过DMA直接存到rxbuffer中。

extern uint8_t txbuffer[10];
extern uint8_t rxbuffer[2];
int main(void)
 {    
	bsp_usart_init();
    SysTick_Init();
	bsp_wdg_init(); 	
	volatile uint8_t usartSend_taskTime = 0;
	for(;;)    
	{ 	
		if(g_u8_taskTimer >= 10)        
		{				
			g_u8_taskTimer = g_u8_taskTimer - 10;					
			bsp_wdg_feed();			
			task_10msCycle();		
			
			usartSend_taskTime++;
			if(usartSend_taskTime >= 100)
			{
				usart0_dma_send(txbuffer, (uint16_t)ARRAYNUM(txbuffer));
//				printf("\n\r%s\n\r", rxbuffer);
				usartSend_taskTime -=100;			
			}				
		}   
	}
}

9. 执行结果

rxbuffer和txbuffer中初始数据如下:

uint8_t rxbuffer[2]= {'o','k'};
uint8_t txbuffer[10] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a};

上位机接收的串口数据:
txbuffer
rxbuff初始数据:
rxbuffer
上位机发送字符"lk"后rxbuffer也随之改变:
rxbuffer
至此,整个串口DMA收发测试成功。


总结

使用DMA进行数据传输的好处毋庸置疑,可以大大减少CPU的负担,CPU效率也会大大提升。

由上面步骤可知,串口用DMA收发主要分以下几步:

1)串口初始化

2)DMA初始化

3)中断配置

4)发送和接收数据


版权声明:本文为CSDN博主「嵌入式全栈工程师成长之路」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/YoungNemoLu/article/details/122129130

生成海报
点赞 0

嵌入式全栈工程师成长之路

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

ESP32-GY_30光照强度传感器

GY_30光照强度传感器介绍 GY-30光强传感器特点及使用介绍 一、连接引脚 GY_30光照强度传感器使用I2C传输数据 。 5根引脚,名称与功能如下; vcc 为外接供电电源输入端 GND 地线 SCL I2C通信模式时钟

初学STM32之串口通信

一、背景知识 1. 处理器与外部通信的两种方式 并行通信:数据各个位同时传输 优点速度快;缺点占用引脚资源多串行通信:数据按位顺序传输 优点占用引脚资源少;缺点速度慢 2.串行通信的

GPIO口详解、HAL库操作按键

本次博客知识来自于韦东山老师的7天物联网课程。 一、GPIO详解 1、STM32F103C8T6一共有48个引脚。 2、按A、B、C分组,每组16个引脚,编号为0~15,STM32F103C8T6