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

news2024/11/25 6:28:50

前言

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

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

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

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

请添加图片描述
这是该芯片的DMA请求映射关系图,下面示例的USART0接收(RX)即映射到DMA _CH2。

  • 框架流程图
收到空闲中断标志
接收数据帧标志置位,清除空闲中断标志
读取完后清除缓存以及数据帧标志
接收下一帧数据
初始化串口GPIO
初始化串口配置以及串口中断和DMA配置,使能空闲中断
中断中读取空闲中断标志
关闭DMA
读取串口缓存数据
重新设置DMA缓存大小,并使能DMA

  • 初始化配置
#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;
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/50761.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

R语言分位数回归Quantile Regression分析租房价格

全文链接&#xff1a;http://tecdat.cn/?p18422本文想在R软件中更好地了解分位数回归优化。在查看分位数回归之前&#xff0c;让我们从样本中计算中位数或分位数&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。相关视频中位数考虑一个样本 。要计算中位数&am…

1.聊聊Netty那些事儿之从内核角度看IO模型

从今天开始我们来聊聊Netty的那些事儿&#xff0c;我们都知道Netty是一个高性能异步事件驱动的网络框架。 它的设计异常优雅简洁&#xff0c;扩展性高&#xff0c;稳定性强。拥有非常详细完整的用户文档。 同时内置了很多非常有用的模块基本上做到了开箱即用&#xff0c;用户…

营销投入大没效果?痛点难点一站式解决!

市场经济在不断发展、走向成熟的过程中&#xff0c;也使得市场结构和要素变得愈发复杂。营销一直是企业的必需品&#xff0c;其理念和方法也在不断变化。 营销是一项有组织的活动&#xff0c;它包括创造“价值”&#xff0c;将“价值”通过沟通输送给顾客&#xff0c;以及维系…

【毕业设计】大数据心血管疾病数据分析(医学大数据分析)

文章目录0 前言1 课题背景2 数据处理3 数据可视化4 最后0 前言 &#x1f525; Hi&#xff0c;大家好&#xff0c;这里是丹成学长的毕设系列文章&#xff01; &#x1f525; 对毕设有任何疑问都可以问学长哦! 这两年开始&#xff0c;各个学校对毕设的要求越来越高&#xff0c…

Flink系列之Flink流式计算引擎基础理论

声明&#xff1a; 文章中代码及相关语句为自己根据相应理解编写&#xff0c;文章中出现的相关图片为自己实践中的截图和相关技术对应的图片&#xff0c;若有相关异议&#xff0c;请联系删除。感谢。转载请注明出处&#xff0c;感谢。 By luoyepiaoxue2014 B站&#xff…

[附源码]计算机毕业设计JAVA线上图书销售管理系统

[附源码]计算机毕业设计JAVA线上图书销售管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM my…

电脑卡怎么办?4招帮你解决电脑卡顿的烦恼!

你是不是也遇到过这样的情况&#xff1a;电脑突然卡顿&#xff0c;玩游戏也是卡的不行。首先我们先了解一下电脑卡顿的原理。造成这种现象的原因主要是由于 CPU、显卡等硬件性能老化或者损坏。电脑卡顿对我们来说真的非常痛苦&#xff0c;严重影响了工作和学习效率&#xff0c;…

【使用 BERT 的问答系统】第 3 章 :词嵌入介绍

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

计算机毕业设计-微信小程序文学小说阅读销售系统

项目介绍 制作小说阅读管理系统提供读者的交流平台&#xff0c;让作者可以通过这个方式让跟多的人看到自己的作品&#xff0c;能跟快的让读者看到自己的创作以及读者对自己的一些评论、意见&#xff0c;能够及时的明白读者的想法来调节自己创作思路。也能为读者节省很多买书的…

遗传算法(GA)优化的BP神经网络预测,GA-BP回归预测,多输入单输出模型。

clc; clear all; close all warning off tic %% 导入数据 % 训练集——190个样本 P_train xlsread(data,training set,B2&#xff1a;G191); T_train xlsread(data,training set,H2&#xff1a;H191); % 测试集——44个样本 P_testxlsread(data,test set,B2:G45); T_testxlsr…

[附源码]计算机毕业设计springboot美发店会员管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

SpringBoot日志文件

文章目录日志有什么作用&#xff1f;SpringBoot 的日志日志级别日志级别的分类自定义日志在程序中得到日志对象使用日志对象打印日志日志级别设置日志持久化更简单的日志输出—lombok日志有什么作用&#xff1f; 日志是程序的重要组成部分&#xff0c;想象⼀下&#xff0c;如果…

Android 导航之Navigation 组件的介绍与使用

1、介绍&#xff1a; 在以前的应用中&#xff0c;针对多导航模块的使用&#xff0c;常见的有tabhost或者FragmentTabHost&#xff0c;但是这些在使用的过程中&#xff0c;非常臃肿&#xff0c;包括加载和管理也不如人意。在AndroidX中&#xff0c;官方引入Navigation模块&#…

【信管2.4】项目运行环境与项目经理

项目运行环境与项目经理加餐加餐来了哦&#xff0c;今天的内容在 《信息系统项目管理师教程》 这本教材里是没有的&#xff0c;而且在 PMBOK 第五版中也是没有的。这部分内容是 PMBOK 第六版中新加的两个独立章节&#xff0c;不过内容不多&#xff0c;非常轻松&#xff0c;重点…

AGV移动机器人无人叉车控制器设计

AGV控制器是为移动机器人&#xff08;AMR、智能叉车等&#xff09;设计的通用控制器&#xff0c;为移动机器人提供地图构建、定位导航、模型编辑等核心功能。 本方案将移动机器人的核心组件集成于一体&#xff0c;配合功能强大的客户端软件 &#xff0c;可帮助用户快速实现机器…

【数据结构】Java实现数据结构的前置知识,时间复杂度空间复杂度,泛型类的讲解

文章目录数据结构时间复杂度、空间复杂度包装类、装箱与拆箱泛型擦除机制数据结构 当我们在成为一名程序员的这条道路上努力的时候&#xff0c;我们一定经常听到这个词数据结构。那么究竟什么是数据结构呢&#xff1f;数据结构顾名思义&#xff0c;就是数据结构&#xff0c;数…

[附源码]计算机毕业设计JAVA校园快递管理系统

[附源码]计算机毕业设计JAVA校园快递管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybati…

vtkjs中Sample Distance功能

通过 SetSampleDistance () 来设置每条射线的采样步长&#xff0c;默认值为1&#xff0c;单位为世界坐标系单位&#xff0c;数值越小越精细 通过 SetImageSampleDistance () 来设置图像采样距离&#xff0c;默认值为1&#xff0c;即投射光线的间隔数值越小越精细 核心代码如下…

【OpenCV-Python】教程:3-10 直方图(2)直方图均衡

OpenCV Python 直方图均衡化 【目标】 理解直方图均衡化提升图像的对比度 Histogram Equalization ??? 将图像的直方图拉均衡一些&#xff0c;分散一些&#xff1b;可以提升对比度。 【代码】 通过上面的图像可以看出&#xff0c;高亮度区域较少&#xff0c;需要分散该图像…

Java HashMap 的扩容因子为什么是 0.75

所谓的加载因子&#xff0c;也叫扩容因子或者负载因子&#xff0c;它是用来进行扩容判断的。 假设加载因子是0.5&#xff0c;HashMap初始化容量是16&#xff0c;当HashMap中有16 * 0.58个元素时&#xff0c;HashMap就会进行扩容操作。 而HashMap中加载因子为0.75&#xff0c;…