GD32实现串口空闲(IDLE)中断 + DMA机制接收数据

这篇具有很好参考价值的文章主要介绍了GD32实现串口空闲(IDLE)中断 + DMA机制接收数据。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言
  • 串口功能在单片机开发中,是比较常用的外设,熟练使用串口功能也是驱动开发必备的技能之一。
    DMA是一种CPU辅助手段,可以在CPU不参与的情况下,是做一些辅助CPU的事情,如通常的数据搬运。
    在没有DMA之前,数据读取时,需要CPU的处理,在多任务处理时,增加资源紧缺(CPU调度);
    引入DMA之后,数据可以直接先进入DMA中处理,然后通过相应的标志,在需要的时候去DMA拿去即可,这样就极大的减轻CPU负担,提高了CPU的利用效率,有更多的时间去处理其它的事情。

  • 本文讲的即是利用串口空闲(IDLE)中断 + DMA的机制来处理接收的数据。关于空闲的概念我在之前文章模拟串口收发驱动(采用IDLE信号机制),做了提及和介绍,也是在这根据这个概念在模拟情况下也引入这一机制,极大的提高的处理效率。

  • 本文是基于GD32F330芯片做的代码示范,其实STM32或其他ARM芯片也一样可以按照下面流程方式进行配置使用,都有实现过,故总结之。


正文(流程图 + 示例代码)

GD32实现串口空闲(IDLE)中断 + DMA机制接收数据


在使用DMA之前需要通过MCU手册了解到当前外设映射的所在DMA通道;
上图为GD32F330芯片的DMA请求映射关系图,可以看到下面示例的USART0接收(RX)即映射到DMA _CH2上面。文章来源地址https://www.toymoban.com/news/detail-412582.html

  • 框架流程图

  • 初始化配置
#define U1_RX_MAX_SIZE (150u)
unsigned char gb_uart1_rx_frame_flag = 0;//接收数据帧标志
unsigned char uart1_rx_buff[U1_RX_MAX_SIZE] = {0};//开辟的接收数据缓存区,按实际可能接收最大的数据长度来开辟即可。

/*
**串口1 GPIO初始化
*/
void gd32_uart1_gpio_init(void)
{
    /* enable GPIO clock */
    rcu_periph_clock_enable(RCU_GPIOA);
    /* connect port to USART0 tx */
    gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_9);
    /* connect port to USART0 rx */
    gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_10);
    /* configure USART tx as alternate function push-pull */
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_9);
    /* configure USART rx as alternate function push-pull */
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_10); 
}

/*
**串口参数配置
*/
void gd32_uart1_cfg_init(unsigned int baudrate)
{
	  nvic_irq_enable(USART0_IRQn,0, 1);
	
    /* enable USART clock */
    rcu_periph_clock_enable(RCU_USART0);
    /* configure USART */
    usart_deinit(USART0);
    usart_baudrate_set(USART0, baudrate);
    usart_receive_config(USART0, USART_RECEIVE_ENABLE);//打开串口接收功能
    usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);//打开串口发送功能
    usart_dma_receive_config(USART0, USART_DENR_ENABLE);//使能 DMA接收 功能
    usart_enable(USART0);//使能串口

    while (RESET == usart_flag_get(USART0, USART_FLAG_IDLE))
        ;
    usart_flag_clear(USART0, USART_FLAG_IDLE);//清除IDLE空闲标志,防止上电即误触发空闲。
    usart_interrupt_enable(USART0, USART_INT_IDLE);//使能IDLE空闲中断
}

/*
**串口0 DMA(发送通道DMA_CH1,接收通道DMA_CH2)配置初始化
*/
void gd32_uart1_dma_init(void)
{
    dma_parameter_struct dma_init_struct;

    rcu_periph_clock_enable(RCU_DMA);

    /* deinitialize DMA channel2 (USART0 rx) */
    dma_deinit(DMA_CH2);
    dma_struct_para_init(&dma_init_struct);
    dma_init_struct.direction   = DMA_PERIPHERAL_TO_MEMORY;//数据是外设到内存(缓存)
    dma_init_struct.memory_addr = (uint32_t)uart1_rx_buff;//数据放置的内存(缓存)地址
    dma_init_struct.memory_inc  = DMA_MEMORY_INCREASE_ENABLE;//内存(缓存)地址增加开启
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;//内存(缓存)数据宽度是8bit,即1字节的存储。
    dma_init_struct.number       = U1_RX_MAX_SIZE;//开辟的内存(缓存)的大小
    dma_init_struct.periph_addr  = (uint32_t)&USART_RDATA(USART0);//数据来源的外设地址(串口接收寄存器)
    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);
    /* configure DMA mode */
    dma_circulation_disable(DMA_CH2);//禁止DMA循环接收
	dma_memory_to_memory_disable(DMA_CH2);//关闭内存到内存方式。
    /* enable DMA channel2 */
    dma_channel_enable(DMA_CH2);//使能DMA通道。
}

/*
**串口1初始化
*/
void gd32_uart1_init(void)
{
    gd32_uart1_dma_init();
    gd32_uart1_gpio_init();
    gd32_uart1_cfg_init(115200); 
}

/*
**DMA读取接收的数据长度
*/
unsigned int uart1_dma_read(void)
{
    /*
    dma_transfer_number_get(DMA_CH2);是获取当前指针计数值,
    用内存缓冲区大小 - 此计数值 = 接收到的数据长度(这里单位为字节)。
    需要说明下在读取数据长度的时候需要先把接收DMA关闭,读取完了或者是数据处理完了在打开接收DMA,防止在处理的过程中有数据到来而出错。
    */
    return U1_RX_MAX_SIZE - (dma_transfer_number_get(DMA_CH2));
}


/*
**DMA重配置缓存大小,并使能DMA
*/
void uart1_dma_refcg(void)
{
    dma_transfer_number_config(DMA_CH2, U1_RX_MAX_SIZE); //重载缓存大小
    dma_channel_enable(DMA_CH2);
}

  • 中断接收处理
void USART0_IRQHandler(void)
{
    if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE)) 
    {
        /* disable DMA and reconfigure */
        dma_channel_disable(DMA_CH2);	//关闭DMA,在没有读取该接收帧数据之前禁止DMA再接收数据。
         /* number of data received */
        gb_uart1_rx_frame_flag = 1;//接收数据帧标志置位       		
        /* clear IDLE flag */
        usart_interrupt_flag_clear(USART0, USART_INT_FLAG_IDLE);//

    }
}
  • 串口读缓存数据操作
/*
**串口读函数
*/
unsigned char *serial_read(const unsigned char port_num,unsigned int *const plen)
{
	switch(port_num)
	{
		case 0:
			if(gb_uart1_rx_frame_flag)
			{
				*plen = uart1_dma_read();//取数据长度	
				return uart1_rx_buff;//取数据指针
			}		
		break;
		case 1:	
		break;	
        defualt:
        break;	
	}
  *plen =0;
  return NULL;
}
  • 串口清缓存数据操作
/*
**串口缓存清除
*/
void serial_flush(const unsigned char port_num,unsigned int flush_sz)
{
    switch(port_num)
	{
		case 0:
            if(gb_uart1_rx_frame_flag)
            {
                for(unsigned int i=0;i<U1_RX_MAX_SIZE;i++)//U1_RX_MAX_SIZE<=flush_sz?U1_RX_MAX_SIZE:flush_sz
                {
                    uart1_rx_buff[i]=0x00;//清除缓存	
                }
                gb_uart1_rx_frame_flag=0;
                uart1_dma_refcg();//重配置DMA				
            }
		break;
		case 1:
		break;
        default:
        break;
	}
}

  • 调用
/*
**设备串口通信
*/
unsigned char dev_com_task(xxr_un *const rxd_me)
{
	unsigned char ack_code = ACK_OK; //响应值
	unsigned char shk_inf[13] = {0};
	unsigned int dat_len = 0;
	const unsigned char *p = com_seial_read(&dat_len); //读取串口数据

	if (0 == dat_len || NULL == p)
		return 0; /*数据为空,直接返回*/

	(MAX_PPT_RX_UDAT_LEN + 9) < dat_len ? dat_len = (MAX_PPT_RX_UDAT_LEN + 9) : 0;
	for (unsigned short int i = 0; i < dat_len; i++)
	{
		rxd_me->buff[i] = *p++;
	}
	/*协议头判断*/
	if (xx_dev_recv_msg_header_is(rxd_me->frame.sync_header))
		goto __END_HANDLE;
	/*负载长度信息错误*/
	if (MAX_PPT_RX_UDAT_LEN < rxd_me->frame.len)
		goto __END_HANDLE;
	const unsigned char msg_ckv = (xx_msg_chksum(&rxd_me->buff[0], 8) + xx_msg_chksum(&rxd_me->frame.udat[0], rxd_me->frame.len)) & 0xFF;

	if (rxd_me->frame.chksum != msg_ckv)
		goto __END_HANDLE; /*非协议帧数据,直接结束处理*/

	switch (rxd_me->frame.cmd)
	{
	case EQ_SHK_SET_CMD: /*设备握手指令*/
		if (rxd_me->frame.udat[0] & 0x1)
		{
			/*SET THE COM IS CNNING STATUS*/
			dev_com_status_set(CNN_STATE);
			/*BATT VOLTAGE INFO*/
			union
			{
				float volt;
				struct
				{
					unsigned char dat[4];
				};
			} batt;
			batt.volt = BATT_VOLTAGE_CONVERT(gb_adc_value) + 0.7F;
			shk_inf[12] = batt.dat[0];
			shk_inf[11] = batt.dat[1];
			shk_inf[10] = batt.dat[2];
			shk_inf[9] = batt.dat[3];
			/*GET SN INFO*/
			dev_sn_code_read_from_flash(&shk_inf[4], DEV_SN_INF_LEN); //读取设备SN码
			/*GET VERSION INFO*/
			// V01.00.13
			shk_inf[3] = *(volatile unsigned int *)(GD32_FLASH_APP_FILE_INF_START_ADDR + 0x0A); // 13
			shk_inf[2] = *(volatile unsigned int *)(GD32_FLASH_APP_FILE_INF_START_ADDR + 0x09); // 00
			shk_inf[1] = *(volatile unsigned int *)(GD32_FLASH_APP_FILE_INF_START_ADDR + 0x08); // 01
			/*OK*/
			shk_inf[0] = 0x80; /*回复标志*/
			xx_dev_msg_make_and_send(rxd_me->frame.cmd, sizeof(shk_inf), (unsigned char *)&shk_inf[0], serial_write);
			goto __END_HANDLE;
		}
		else
		{
			dev_com_status_set(DISCNN_STATE);
			goto __ACK_HANDLE;
		}
		break;
	default:
		goto __END_HANDLE;
	}
__ACK_HANDLE: //设备回应
	xx_dev_msg_make_and_send(rxd_me->frame.cmd, sizeof(ack_code), &ack_code, serial_write);
__END_HANDLE: //清除串口接收数据缓存
	serial_flush(0);
	dat_len = 0;
	return 0;
}


到了这里,关于GD32实现串口空闲(IDLE)中断 + DMA机制接收数据的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 嵌入式开发--STM32用DMA+IDLE中断方式串口接收不定长数据

    之前讲过用 利用IDLE空闲中断来接收不定长数据 ,但是没有用到DMA,其实用DMA会更加的高效,MCU也可以腾出更多的性能去处理应该做的事情。 IDLE顾名思义,就是空闲的意思,即当监测到串口空闲超过1个串口的数据帧时,会使状态寄存器(SR或ISR)的IDLE位置位,如果此时控制

    2024年04月17日
    浏览(64)
  • 环形队列+DMA空闲中断+接收串口数据

    本次实验利用环形队列+DMA空闲中断+串口。。通过这个实验可以非常深入的理解队列,DMA,串口的知识。如果你能自己实现掌握这个实验,那么你应该基本掌握了队列,DMA,串口的知识。 本次使用的是用环形队列当缓冲器区接收串口数据。我们可以先区了解DMA的空闲中断。本次

    2024年02月13日
    浏览(44)
  • STM32 F4串口空闲中断 + DMA实现数据发送

    最近在做 STM32 + ROS车的项目,STM32与ROS之间通信由于数据量大,所以在 STM32端 使用 空闲中断 + DMA 的方案来减轻 CPU 的压力。 一、空闲中断 空闲中断 顾名思义为空了,闲了,没事了进的中断,在 没有数据流 的时候会进入进行读取。 在我们串口进行发送时实则为连续发送,两

    2024年02月16日
    浏览(64)
  • STM32 UART串口通信IDLE空闲中断的使用步骤

    参考了各路大神的资料,蒙蔽了半天,终于学会了,记录一下,以后忘了可以回来复习参考。 一、首先在stm32cube中配置打开对应uart串口的中断 二、工程main函数调用 __HAL_UART_ENABLE_IT(huart1,UART_IT_IDLE);//hal库宏定义,使能串口空闲中断     HAL_UART_Receive_DMA(huart1,data,sizeof(data));//使

    2024年02月12日
    浏览(44)
  • 衔尾法解决当无法使用空闲中断以及DMA中断时配置DMA接收串口不定长数据

    问题:类似K线与蓝牙接收模块,要求由原来的接收串口中断改为DMA接收。据说要用到空闲中断与DMA中断,但是经仿真发现DMA每完成传输一个数据(比如1BYTE)就会进入空闲中断(k线发现这种情况),考虑到这样进入中断的频率和以前串口接收中断的频率差不多,所以放弃此方案,

    2024年02月09日
    浏览(56)
  • HAL库 STM32运用DMA与IDLE中断实现高效串口通信 (附代码)

    最近想做一个控制电机的项目,其中会用到Pytho与单片机STM32之间的互同,最近也在看一些关于数据通信和拆包的相关知识,所以记录一下这段时间里对两者之间的互通所做的事情和发现的问题,以供自己和大家参考。 单片机的串口是我们常用的与电脑通信的外设,本次与P

    2024年01月22日
    浏览(57)
  • GD32或STM32:DMA循环模式与普通模式(串口IDLE)

    USART+DMA+循环队列接收不定长数据-CSDN博客 STM32 DMA 循环模式DMA_Mode_Circular详解-CSDN博客 推荐以上两个链接。 Normal(普通)模式的DMA+串口IDLE中断,流程如下: 1、初始化时:开启串口IDLE中断;dma_circulation_disable,失能DMA的循环; 2、在串口IDLE中断里面: (1) 清除idle的flag;dma_c

    2024年02月21日
    浏览(47)
  • stm32串口空闲中断+DMA传输接受不定长数据+letter shell 实现命令行

    空闲中断(IDLE),俗称帧中断,即第一帧数据接收完毕到第二帧数据开始接收期间存在一个空闲状态(每接收一帧数据后空闲标志位置1),检测到此空闲状态后即执行中断程序。 空闲中断的优点在于省去了帧头帧尾的检测 ,进入中断程序即意味着已经接收到一组完整数据,仅需

    2024年02月03日
    浏览(60)
  • 【STM32 CubeMX】串口编程DMA+IDLE中断

    在嵌入式系统中,串口通信是一项关键的任务,而使用DMA(直接内存访问)结合IDLE中断进行串口编程,尤其是在STM32 CubeMX环境中,能够提高系统的效率和性能。STM32 CubeMX为STM32微控制器提供了图形化的配置工具,可以简化初始化代码的生成过程,使得串口编程变得更加容易。

    2024年02月20日
    浏览(59)
  • STM32学习笔记(五)串口空闲中断+DMA实现不定长收发(stm32c8t6)

    记录一下学习过程 DMA DMA,全称为: Direct Memory Access,即直接存储器访问, DMA 传输将数据从一个 地址空间复制到另外一个地址空间。 这一过程无需cpu的参与,从而提高cpu使用的效率 DMA相关的参数:1 数据的源地址、2 数据传输的目标地址 、3 传输宽度,4 传输多少字节,5 传

    2024年02月14日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包