STM32 F1 串口空闲中断 + DMA实现数据发送

DMA实现数据发送

文章目录

  • DMA实现数据发送
  • 前言
  • 一、DMA
  • 二、代码编写
    • 1.DMA
    • 2.USART
    • 3.main


前言

当你遇到通信数据量大的时候,可以使用 空闲中断 + DMA 的方案来减轻 CPU 的压力。
或者
在进行stm32开发时,有时会遇到这种情况:需要在设备间进行数据传输,由于stm32串口RDR和TDR寄存器都是8位有效的,我们往往需要定义传输协议(如一帧数据中,包含包含帧头、帧ID、数据帧、校验帧等若干8位数据)。我们希望可以一次收到一帧数据,并进行解码操作。利DMA+串口空闲中断可以有效完成上述任务。

一、DMA

1、简介
DMA(直接存储器访问)是一种数据传输方法,利用DMA控制器,将数据直接从一个地址空间复制到另一个地址空间。
DMA在硬件ROM和IO设备间开辟直接传输数据的通道,不需要CPU主控芯片控制,也不需要类似中断处理那种保留现场&恢复现场的操作。这大大减小了CPU的负担。
2、使用场景
DMA用在只需要传输数据,不需要处理数据的地方,有三种传输方式:

外设→存储器(例:从串口RDR寄存器写入某数据buf)
存储器→外设(例:从某数据buf写入串口TDR寄存器)
存储器→存储器(例:复制某特别大的数据buf)

DMA相关的参数:1 数据的源地址、2 数据传输的目标地址 、3 传输宽度,4 传输多少字节,5 传输模式。

传输宽度是指一次传输数据的的大小,可以为字节(8b)、半字(16b)、字(32b)

传输模式分为正常模式(一次结束)和循环模式

DMA通道

STM32 最多有 2 个 DMA 控制器(DMA2 仅存在大容量产品中), DMA1 有 7 个通道。 DMA2 有 5个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁起来协调各个 DMA 请求的优先权。每个通道都直接连接专用的硬件 DMA 请求,每个通道都同样支持软件触发。这些功能通过软件来配置。
在这里插入图片描述
FIFO介绍:
DMA接收有两种模式,一种为直接模式,另一种为FIFO模式
FIFO为缓存区,大小为32位(16个字节,8个半字,4个字),独立的源和目标传输宽度(字节、半字、字),在单位上分为三种读取FIFO的方式,为(字节、半字、字)每个数据流都有一个独立的 4 字 FIFO,阈值级别可由软件配置为 1/4、1/2、3/4 或满。

二、代码编写

1.DMA

#include "DMA.h"u16 DMA1_MEM_LEN;//保存DMA每次数据传送的长度         
//DMA1的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_CHx:DMA通道CHx
//cpar:外设地址
//cmar:存储器地址
//cndtr:数据传输量 
void MyDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{DMA_InitTypeDef DMA_InitStructure;DMA1_MEM_LEN=cndtr;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);    //使能DMA传输DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值DMA_InitStructure.DMA_BufferSize=cndtr;//DMA通道的DMA缓存的大小(转运的数据量)DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC;//数据传输方向,从外设读取发送到内存DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;//DMA通道x没有设置为内存到内存传输DMA_InitStructure.DMA_MemoryBaseAddr=cmar;//DMA内存基地址DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;//数据宽度为8位DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//内存地址寄存器递增DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;//工作在正常模式DMA_InitStructure.DMA_PeripheralBaseAddr=cpar;//DMA外设基地址DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;//数据宽度为8位DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设地址寄存器不变DMA_InitStructure.DMA_Priority=DMA_Priority_Medium; //DMA通道 x拥有中优先级 DMA_Init(DMA_CHx,&DMA_InitStructure);USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);//使能外设的DMA通道,这句可以放在对应的外设里
}//开启一次DMA传输
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{ DMA_Cmd(DMA_CHx, DISABLE );  //关闭所指示的通道      DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//重新设定DMA通道的DMA缓存的大小DMA_Cmd(DMA_CHx, ENABLE);  //使能所指示的通道 
}      

首先,定义了全局变量DMA1_MEM_LEN用于保存每次数据传输的长度。然后,使用MyDMA_Config函数配置DMA通道的参数,包括DMA缓存大小、数据传输方向、内存地址寄存器递增等。其中,cpar表示外设地址,cmar表示存储器地址,cndtr表示数据传输量。在配置完成后,通过USART_DMACmd函数使能外设的DMA通道。

接着,使用MYDMA_Enable函数开启一次DMA传输。该函数首先关闭所指示的DMA通道,然后重新设定DMA缓存的大小,并使能DMA通道。

#ifndef __DMA_H
#define __DMA_H#include "sys.h"#define rx_buff_maxlen 200//定义接受缓存区最长长度
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx);
void MyDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr);#endif

2.USART

#include "sys.h"
#include "usart.h"	  
#include "DMA.h"	  u8 len,Flag=0;
// 	 
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h"					//ucos 使用	 #endif
//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//串口1初始化		   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2012/8/18
//版本:V1.5
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//********************************************************************************
//V1.3修改说明 
//支持适应不同频率下的串口波特率设置.
//加入了对printf的支持
//增加了串口接收命令功能.
//修正了printf第一个字符丢失的bug
//V1.4修改说明
//1,修改串口初始化IO的bug
//2,修改了USART_RX_STA,使得串口最大接收字节数为2的14次方
//3,增加了USART_REC_LEN,用于定义串口最大允许接收的字节数(不大于2的14次方)
//4,修改了EN_USART1_RX的使能方式
//V1.5修改说明
//1,增加了对UCOSII的支持
// 	  //
//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ int handle; }; FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
_sys_exit(int x) 
{ x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   USART1->DR = (u8) ch;      return ch;
}
#endif /*使用microLib的方法*//* 
int fputc(int ch, FILE *f)
{USART_SendData(USART1, (uint8_t) ch);while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}	return ch;
}
int GetKey (void)  { while (!(USART1->SR & USART_FLAG_RXNE));return ((int)(USART1->DR & 0x1FF));
}
*/
#if EN_USART1_RX   //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误   	
u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART_RX_STA=0;       //接收状态标记	  void uart_init(u32 bound){//GPIO端口设置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟//USART1_TX   GPIOA.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9//USART1_RX	  GPIOA.10初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  //Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器//USART 初始化设置USART_InitStructure.USART_BaudRate = bound;//串口波特率USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式USART_Init(USART1, &USART_InitStructure); //初始化串口1// USART_ITConfig(USART1, USART_IT_RXE, ENABLE);//开启串口接受中断USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);    //开启USART1的空闲中断USART_Cmd(USART1, ENABLE);                    //使能串口1 }void USART1_IRQHandler(void)                 //串口1中断服务程序,当接受完毕后便会触发空闲中断{u8 clear=0;if(USART_GetITStatus(USART1, USART_IT_IDLE) == SET)  //接收中断{clear=USART1->DR;//清楚中断标志位Flag=1;//标志一次接受完毕,在main函数中读取flag来判断是否接收完毕,并在主函数中清零len=DMA_GetCurrDataCounter(DMA1_Channel5)-sizeof(rx_buff);//读取剩余未转运的长度}
}void MyUSART_SendByte(u8 Byte)
{USART_SendData(USART1,Byte);while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);//防止发送过快,前一个数据还没发送出去就别覆盖的情况
}void MyUSART_SendString(char* str)//发送字符串
{u16 i;for(i=0;str[i]!='\0';i++){MyUSART_SendByte(str[i]);}}
#endif

要注意的是这里开启的空闲中断
在这里插入图片描述
这里做中断标志位清0
在这里插入图片描述

#ifndef __USART_H
#define __USART_H
#include "stdio.h"	
#include "sys.h" 
#define USART_REC_LEN  			200  	//定义最大接收字节数 200
#define EN_USART1_RX 			1		//使能(1)/禁止(0)串口1接收extern u8 len;
extern u8 Flag;
extern char rx_buff[200];
extern u8  USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u16 USART_RX_STA;         		//接收状态标记	
void uart_init(u32 bound);
#endif

3.main

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"#include "DMA.h"int main(void){		u16 t;  char rx_buff[200]={'\0'};u16 len;	u16 times=0;delay_init();	    	 //延时函数初始化	  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级uart_init(115200);	 //串口初始化为115200LED_Init();			     //LED端口初始化KEY_Init();          //初始化与按键连接的硬件接口MyDMA_Config(DMA1_Channel5,(u32)&USART1->DR,(u32)rx_buff,rx_buff_maxlen);MYDMA_Enable(DMA1_Channel5);while(1){if(Flag){Flag=0;MyUSART_SendString(rx_buff);MyUSART_SendString("\r\n");printf("len=%d\r\n",len);MYDMA_Enable(DMA1_Channel5);}}	 }

在这里插入图片描述

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

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

相关文章

1.1 C语言之入门:使用Visual Studio Community 2022运行hello world

1.1 使用Visual Studio Community 2022运行c语言的hello world 一、下载安装Visual Studio Community 2022 与 新建项目二、编写c helloworld三、编译、链接、运行 c helloworld1. 问题记录:无法打开源文件"stdio.h"2. 问题记录:调试和执行按钮…

【Linux】bash 终端指令

进程 $ ps aux | grep pwd work 63317 0.0 0.0 51192 612 pts/9 S 14:22 0:00 grep /home/work/search/1000000.dyenv-user-diaoyan-baiseCliPlus-baisePlus-195522.diaoyan.yq/ala-ac/output_root端口 查看本机端口开放情况 netstat -tln | grep :31 tcp …

Unsupervised Skill Discovery via Recurrent Skill Training论文笔记

Zheyuan Jiang, Jingyue Gao, Jianyu Chen (2022). Unsupervised Skill Discovery via Recurrent Skill Training. In Conference on Neural Information Processing Systems (NeurIPS), 2022. 通过循环技能训练发现无监督技能 1、Motivation 以往的无监督技能发现方法主要使…

可燃气体监测仪助力燃气管网安全监测,效果一览

城市地下管线是指城市范围内供应水、排放水、燃气等各类管线及其附属设施,它们是保障城市正常运转的重要基础设施且影响着城市生命线。其中燃气引发的事故近些年不断增加,由于燃气管线深埋地下环境复杂,所以仅仅依赖人工巡查难以全面有效地防…

17. Python 数据库操作之MySQL和SQLite实例

目录 1. 简介2. 使用PyMySQL2. 使用SQLite 1. 简介 数据库种类繁多,每种数据库的对外接口实现各不相同,为了方便对数据库进行统一的操作,大部分编程语言都提供了标准化的数据库接口,用户不需要了解每种数据的接口实现细节&#x…

【每日一题】1457. 二叉树中的伪回文路径-2023.11.25

题目: 1457. 二叉树中的伪回文路径 给你一棵二叉树,每个节点的值为 1 到 9 。我们称二叉树中的一条路径是 「伪回文」的,当它满足:路径经过的所有节点值的排列中,存在一个回文序列。 请你返回从根到叶子节点的所有路…

Re55:读论文 Entities as Experts: Sparse Memory Access with Entity Supervision

诸神缄默不语-个人CSDN博文目录 诸神缄默不语的论文阅读笔记和分类 论文名称:Entities as Experts: Sparse Memory Access with Entity Supervision 模型名称:Entities as Experts (EaE) ArXiv网址:https://arxiv.org/abs/2004.07202 本文…

人工智能基础_机器学习050_对比sigmoid函数和softmax函数的区别_两种分类器算法的区别---人工智能工作笔记0090

可以看到最上面是softmax的函数对吧,但是如果当k = 2 那么这个时候softmax的函数就可以退化为sigmoid函数,也就是 逻辑斯蒂回归了对吧 我们来看一下推导过程,可以看到上面是softmax的函数 可以看到k=2 表示,只有两个类别对吧,两个类别的分类不就是sigmoid函数嘛对吧,所以说 …

ubuntu 安装 jetbrains-toolbox

ubuntu 安装 jetbrains-toolbox 官网下载 jetbrains-toolbox jetbrains 官网 jetbrains 官网:https://www.jetbrains.com/ jetbrains-toolbox 官网下载页面 在下载页面点击 Download 安装 jetbrains-toolbox 解压 jetbrains-toolbox 安装包 到指定目录 本案例将…

STM32 默认时钟更改 +debug调试

STM32时钟 文章目录 STM32时钟前言一、修改系统时钟二、DEBUG 前言 为什么我们要改STM32的时钟呢,打个比方在做SPI驱动的时候,需要16M的时钟,但是stm32默认是72的分频分不出来,这个时候我们就要改系统时钟了,那么怎么…

[科普] 无刷直流电机驱动控制原理图解

Title: [科普] 无刷直流电机驱动控制原理图解 文章目录 I. 引言II. 直流电机的原理1. 有刷直流电机和无刷直流电机的区别2. 有刷直流电机的运行原理3. 既是电动机又是发电机 III. 无刷直流电机的原理1. 无刷直流电机与永磁同步电机的区别2. 无刷直流电机的换向控制原理3. 无刷直…

python 笔记 根据用户轨迹+基站位置,估计基站轨迹+RSRP

1 问题描述 已知用户实际的轨迹,和基站的位置,能不能得到用户所连接的基站,以及基站的信号强度RSRP? 1.1 几个假设 这里我们做几个假设: 每个用户有80%的概率连接最近的基站,有20%的概率选择其他的基站连…

4/5G互操作 EPSFB讲解

今天我们来讲一下4/5G之间之间互操作,以及5G的EPSFB是基于什么实现的~ 目录 4/5G互操作 重选 切换 基于覆盖的切换 基于业务的切换 两个面试问题 想要加快4G切换5G的速度,调哪个参数怎么调高效? 想要减慢5G切换4G的速度调哪个参数怎…

2018年5月23日 Go生态洞察:更新Go行为准则

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…

knime 中没有column expressions,怎么下载

knime 中没有column expressions,怎么下载 1、打开view,然后找到knime hub,column expression 2、往里面拖动,就可以安装了 3、然后会出现重启,搜索就可以出现啦

【SpringCloud】从单体架构到微服务架构

今天来看看架构的演变过程 一、单体架构 从图中可以看到,所有服务耦合在一起,数据库存在单点,一旦其中一个服务出现问题时,整个工程都需要重新发布,从而导致整个业务不能提供响应 这种架构对于小项目而言是没有什么…

OSG编程指南<十二>:OSG二三维文字创建及文字特效

1、字体基础知识 适当的文字信息对于显示场景信息是非常重要的。在 OSG 中,osgText提供了向场景中添加文字的强大功能,由于有第三方插件 FreeType 的支持,它完全支持TrueType 字体。很多人可能对 FreeType 和 TrueType 还不太了解&#xff0c…

【AUTOSAR】【通信栈】ComXf

AUTOSAR专栏——总目录_嵌入式知行合一的博客-CSDN博客文章浏览阅读292次。本文主要汇总该专栏文章,以方便各位读者阅读。https://xianfan.blog.csdn.net/article/details/132072415 目录 一、概述 二、限制说明

深度学习第3天:CNN卷积神经网络

☁️主页 Nowl 🔥专栏《机器学习实战》 《机器学习》 📑君子坐而论道,少年起而行之 ​ 文章目录 介绍 CNN的主要结构 卷积层 激励层 池化层 Kears搭建CNN 搭建代码 直观感受卷积的作用 结语 介绍 卷积神经网络(Convol…

vs2019中出现Debug Error的原因

一般出现这种错误表示你的某个变量没有正确赋值&#xff0c;或者说本身在你的C程序中加了assert断言&#xff0c;assert的作用是先计算表达式expression,如果其值为假&#xff0c;那么它会打印一条错误信息 #include<assert.h> void assert(int expression); 例子&…