GD32VF103_串口发送_printf_接收中断_DMA

文章目录

    • 前言
    • 新建工程
    • 串口发送
    • 添加printf支持
    • 串口中断
    • DMA
    • DMA空闲中断--暂未调试
    • 工程代码

前言

GD32VF103_环境配置_GPIO_外部中断
GD32VF103_定时器中断
我们前两节写了GPIO, EXTI, TIMER的用例, 本节写串口用例, 发送, 接收中断, DMA, printf等. 仍然使用 GD32VF103C-START 板子, 注意板子上JP2要插跳线帽到Usart端(注意!注意!注意!, 不插发不出来信息):
GD32VF103_串口发送_printf_接收中断_DMA_第1张图片
用的串口0(USART0, PA9-TX, PA10-RX), 连接到板子上的CH340E到USB(CN1, 图中的上面的USB).

先安装CH340的驱动: http://www.wch.cn/products/CH340.html.

安装完后, 插上上面的USB(不能供电), 右边GD-Link的USB也要插上(板子供电需要), 可以在设备管理器中看到COM口.

新建工程

  • File -> New -> C/C++ Project, 选择C Managed Build
  • 选择GigaDevice RISC-V Project, 填入工程名
  • MCU默认暂时只有GD32VF103, 然后Demo也没法选, 只有Running_LED, 不变
  • 下一步, 完成.

串口发送

官方的库给了一个发送单uint32_t的函数 void usart_data_transmit(uint32_t usart_periph, uint32_t data), 我们这里用来发送一个字符数组和十六进制的数组:

#include "gd32vf103.h"
#include "systick.h"

void uart0_config(uint32_t baudrate) {
    rcu_periph_clock_enable(RCU_GPIOA);	//enable GPIO clock, PA9/PA10
    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-8-1-N
    usart_deinit(USART0);
    usart_baudrate_set(USART0, baudrate);
    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);
}

void usart0_transmit_arr(uint8_t arr[], uint32_t length) {
	for(uint32_t i=0; i<length; i++) {
		usart_data_transmit(USART0, arr[i]);
		while (usart_flag_get(USART0, USART_FLAG_TBE)== RESET);
	}
}

int main(void)
{
	uart0_config(115200U);

	uint8_t str_arr[] = "USART Example!\n\r";
	uint8_t hex_arr[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};

	uint8_t arr_length = 0;

	arr_length = sizeof(str_arr) / sizeof(*str_arr);
	usart0_transmit_arr(str_arr, arr_length);

	arr_length = sizeof(hex_arr) / sizeof(*hex_arr);
	usart0_transmit_arr(hex_arr, arr_length);

	while(1) {

	}
	return 0;
}

注意初始化 PA10--RX0 配置为 GPIO_MODE_IN_FLOATING!
打开串口调试助手, 选中CH340的串口, 波特率设置 115200-8-1-N, 打开串口, 调试运行, 可以看到:

USART Example!
\0

切换hex显示:

55 53 41 52 54 20 45 78 61 6D 70 6C 65 21 0A 0D 00 01 02 03 04 05 06 

添加printf支持

实测头文件添加 #include , 直接用printf:

int main(void)
{
	uart0_config(115200U);
	printf("\n\rHello, GD32VF103!\n\r");	//\n\r, not \r\n

	while(1) {
	}
	return 0;
}

当然, 官方给的printf例程添加了如下代码:

/* retarget the C library printf function to the USART */
int _put_char(int ch)
{
    usart_data_transmit(USART0, (uint8_t)ch );
    while ( usart_flag_get(USART0, USART_FLAG_TBE)== RESET){
    }

    return ch;
}

放心些, 我们也找个地方把这段供起来.

串口中断

开全局中断, 设置组优先级, 配置串口, 开启串口 USART_INT_RBNE 中断, RBNE: read data buffer not empty interrupt.

接收中断一个字节中断一次, 存到数组, 存满就发给上位机.

代码如下:

#define RX_BUF_SIZE 10U
uint8_t rxbuffer[RX_BUF_SIZE];
__IO uint16_t rxcount = 0;
__IO FlagStatus g_receive_complete = RESET;

int main(void)
{
	/* USART interrupt configuration */
	eclic_global_interrupt_enable();
	eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1);
	eclic_irq_enable(USART0_IRQn, 1, 0);

	uart0_config(115200U);
	printf("USART RECEIVE INTERRUPT EXAMPLE:\n\r");

	usart_interrupt_enable(USART0, USART_INT_RBNE);

	while(1) {
		if(g_receive_complete == SET) {
			rxcount = 0;
			g_receive_complete = RESET;
			usart_interrupt_enable(USART0, USART_INT_RBNE);

			usart0_transmit_arr(rxbuffer, RX_BUF_SIZE);
		}
	}
	return 0;
}

void USART0_IRQHandler(void) {
    if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)){
        /* receive data */
        rxbuffer[rxcount++] = usart_data_receive(USART0);
        if(rxcount == RX_BUF_SIZE){
        	usart_interrupt_disable(USART0, USART_INT_RBNE);
        	g_receive_complete = SET;
        }
    }
}

调试运行, 我们每1s发送 0123456789, 可以看到原封不动返回:
GD32VF103_串口发送_printf_接收中断_DMA_第2张图片
事实上, 我测试了定时1ms发送01234567890, 10万字节不丢数.

DMA

上面的接收中断一个字节中断一次, 我们可以设置DMA接收, 接收到指定数量的字节再触发一次中断, 这样可以减轻下CPU的负担.

下面的代码中, 使用DMA接收方式接收 abcdefghijklmnopqrstuvwxyz 26个英文字母加上回车换行共28个字节, 接收完一帧就传给上位机, DMA初始配置中开启了循环接收 dma_circulation_enable(DMA0, DMA_CH4);, 所以会自动开始. 代码如下:

#define RX_DMA_BUF_SIZE 28U
uint8_t rx_dma_buffer[RX_DMA_BUF_SIZE];
__IO FlagStatus g_dma_receive_complete = RESET;

void uart0_dma_config(uint8_t buffer[], uint32_t number) {
	dma_parameter_struct dma_init_struct;

	rcu_periph_clock_enable(RCU_DMA0); // enable DMA0 clock first !!!!!!!!!

    eclic_global_interrupt_enable();	/*configure DMA0 interrupt*/
    eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1);
    eclic_irq_enable(DMA0_Channel4_IRQn, 2, 0);

    /* 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)buffer;
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number = number;	//modify this number
    dma_init_struct.periph_addr = (uint32_t)&USART_DATA(USART0);
    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_CH4);
    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);
}

int main(void) {
	uart0_config(115200U);
	uart0_dma_config(rx_dma_buffer, RX_DMA_BUF_SIZE);

	printf("USART printf and DMA interrupt receive example:\n\r");

	while(1) {
		if(g_dma_receive_complete == SET) {
			g_dma_receive_complete = RESET;
			usart0_transmit_arr(rx_dma_buffer, RX_DMA_BUF_SIZE);
		}
	}

	return 0;
}

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);
        g_dma_receive_complete = SET;
//        for(uint32_t i = 0; i < RX_DMA_BUF_SIZE; i++) {
//        	usart_data_transmit(USART0, (uint8_t)(rx_dma_buffer[i]));
//        	while (usart_flag_get(USART0, USART_FLAG_TBE)== RESET);
//        }
    }
}

先清掉开始的printf, 我们10ms循环发送, 可以看到26万字节不丢数:
GD32VF103_串口发送_printf_接收中断_DMA_第3张图片

DMA空闲中断–暂未调试

工程代码

https://download.csdn.net/download/weifengdq/11943876

你可能感兴趣的:(RISC-V)