GD32 笔记 04:如何使用串口中断,配合 DMA去接收不定长数据

一. 前言

二. 主要代码

1. 宏定义与变量定义

#define USART0_TDATA_ADDRESS (USART0 + 0x00000028U)// 串口TX外设地址
#define USART0_RDATA_ADDRESS (USART0 + 0x00000024U)// 串口RX外设地址

static uint8_t dma_recv_buf[32];// dma 接收缓存区
static uint16_t dma_recv_count; // dma 接收数据个数

2. 初始化配置函数(GPIO,USART,DMA,NVIC)

static void usart_init(void)
{
	dma_parameter_struct dma_init_struct;// 结构体 dma_parameter_struct
	
	rcu_periph_clock_enable(RCU_GPIOA); // 使能外设时钟
	rcu_periph_clock_enable(RCU_USART0);// 使能外设时钟
	rcu_periph_clock_enable(RCU_DMA);   // 使能外设时钟
	
	gpio_af_set            (GPIOA, GPIO_AF_1,     GPIO_PIN_9);                    // 设置GPIO的备用功能 tx
	gpio_af_set            (GPIOA, GPIO_AF_1,     GPIO_PIN_10);                   // 设置GPIO的备用功能 rx
	gpio_mode_set          (GPIOA, GPIO_MODE_AF,  GPIO_PUPD_PULLUP,  GPIO_PIN_9); // 设置GPIO模式
	gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_9); // 设置GPIO输出模式和速度
	gpio_mode_set          (GPIOA, GPIO_MODE_AF,  GPIO_PUPD_PULLUP,  GPIO_PIN_10);// 设置GPIO模式
	gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_10);// 设置GPIO输出模式和速度
	
	usart_deinit         (USART0);                        // 复位外设USARTx                               
	usart_word_length_set(USART0, USART_WL_8BIT);         // 配置USART字长
	usart_stop_bit_set   (USART0, USART_STB_1BIT);        // 配置USART停止位
	usart_parity_config  (USART0, USART_PM_NONE);         // 配置USART奇偶校验
	usart_baudrate_set   (USART0, 115200U);               // 配置USART波特率
	usart_receive_config (USART0, USART_RECEIVE_ENABLE);  // USART接收器配置
	usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); // USART发送器配置
	usart_enable         (USART0);                        // 使能USART
	
	dma_deinit                  (DMA_CH2);                     // 复位DMA通道x的所有寄存器
	dma_init_struct.direction    = DMA_PERIPHERAL_TO_MEMORY;   // DMA通道数据传输方向   
	dma_init_struct.memory_addr  = (uint32_t)dma_recv_buf;     // 存储器基地址        
	dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE; // 存储器地址生成算法模式
	dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;      // 存储器数据传输宽度    
	dma_init_struct.number       = sizeof(dma_recv_buf);       // DMA通道数据传输数量  
	dma_init_struct.periph_addr  = USART0_RDATA_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通道传输软件优先级
	dma_init                    (DMA_CH2,&dma_init_struct);    // 初始化DMA通道x
	dma_circulation_disable     (DMA_CH2);                     // DMA循环模式禁能
    dma_memory_to_memory_disable(DMA_CH2);                     // 存储器到存储器DMA传输禁能
	dma_channel_enable          (DMA_CH2);                     // DMA通道x传输使能
	
    usart_dma_receive_config(USART0, USART_DENR_ENABLE); // 配置USART DMA接收功能
	
	nvic_irq_enable           (USART0_IRQn, 0);                  // 使能中断,配置中断的优先级
	usart_interrupt_enable    (USART0,      USART_INT_TC);       // 使能发送完成中断
	usart_interrupt_enable    (USART0,      USART_INT_IDLE);     // IDLE线检测中断
}

3. 串口发送数据函数

static void usart_send(uint8_t* data, uint16_t len)
{
	for(int i=0; i<len; i++)
	{
		usart_data_transmit(USART0, *(data+i));                // USART发送数据功能
        while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));// 获取USART STAT/CHC/RFCS寄存器标志位
	}
}

4. 串口中断服务函数

void USART0_IRQHandler(void)
{
	/* 发送完成中断 */
	if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_TC))
	{ 
		SEGGER_RTT_printf(0, "Enter TC interrupt!\n");
		
		usart_interrupt_flag_clear(USART0, USART_INT_FLAG_TC);// 清除USART中断标志位状态
		
		SEGGER_RTT_printf(0, "Leave TC interrupt!\n");
	}
	
	/* IDLE线检测中断 */
	if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE))
	{ 
		SEGGER_RTT_printf(0, "Enter IDLE interrupt!\n");
		
		usart_interrupt_flag_clear(USART0, USART_INT_FLAG_IDLE);                            // 清理中断标志位
		dma_recv_count = sizeof(dma_recv_buf) - (uint16_t)dma_transfer_number_get(DMA_CH2); // 计算接收到了多少数据
		
		/* 因为使能中断之后,总会自动进入中断一次
		   而此时并没有接收到数据,所以设置以下代码,排除掉第一次进入中断的干扰。 */
		if(dma_recv_count > 0)
		{
			dma_channel_disable    (DMA_CH2);               // DMA通道x传输禁能
			usart_interrupt_disable(USART0, USART_INT_IDLE);// 失能USART中断
		}
		
		SEGGER_RTT_printf(0, "Leave IDLE interrupt!\n");// debug
	}
}

5. 主函数

int main()
{
	systick_config();
		
	usart_init();
	
	while (1)
	{
		if(dma_recv_count > 0)
		{
			SEGGER_RTT_printf(0, "handle data start!\n");
			
			usart_send(dma_recv_buf, dma_recv_count);
			dma_recv_count=0;
		
			dma_memory_address_config(DMA_CH2,(uint32_t)dma_recv_buf);// DMA通道x传输的存储器基地址配置
			dma_transfer_number_config(DMA_CH2,sizeof(dma_recv_buf));// 配置DMA通道x还有多少数据要传输
			dma_channel_enable(DMA_CH2);// DMA通道x传输使能
			usart_interrupt_enable(USART0, USART_INT_IDLE);// 使能USART中断
			
			SEGGER_RTT_printf(0, "handle data over!\n");
		}
	};
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Simple Man ZHR

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

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

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

打赏作者

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

抵扣说明:

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

余额充值