cortex_m3_stm32嵌入式学习笔记(十九):DMA实验(高速传输)

DMA,全称为: Direct Memory Access,即直接存储器访问。 DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路, 能使 CPU 的效率大为提高。

即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。
一个完整的DMA传输过程必须经过下面的4个步骤。
1.DMA请求 CPU对DMA控制器初始化,并向I/O接口发出操作命令,I/O接口提出DMA请求。
2.DMA响应 DMA控制器对DMA请求判别优选级及屏蔽,向总线裁决逻辑提出总线请求。当CPU执行完当前总线周期即可释放总线控制权。此时,总线裁决逻辑输出总线应答,表示DMA已经响应,通过DMA控制器通知I/O接口开始DMA传输。
3.DMA传输 DMA控制器获得总线控制权后,CPU即刻挂起或只执行内部操作,由DMA控制器输出读写命令,直接控制RAM与I/O接口进行DMA传输。
4.DMA结束 当完成规定的成批数据传送后,DMA控制器即释放总线控制权,并向I/O接口发出结束信号。当I/O接口收到结束信号后,一方面停 止I/O设备的工作,另一方面向CPU提出中断请求,使CPU从不介入的状态解脱,并执行一段检查本次DMA传输操作正确性的代码。最后,带着本次操作结果及状态继续执行原来的程序。

简单的说就是一种高速传输数据的方式啦

从外设(
TIMx、 ADC、 SPIx、 I2Cx 和 USARTx)产生的 DMA 请求,通过逻辑或输入到DMA 控制器,这就意味着同时只能有一个请求有效。外设的 DMA 请求,可以通过设置相应的外设寄存器中的控制位,被独立地开启或关闭。

下面是是DMA1各通道一览表:

本次实验实现串口1的DMA传送,即用到通道4

配置DMA大体步骤:

1)使能 DMA 时钟

2) 初始化 DMA 通道 4 参数

3)使能串口 DMA 发送

4)使能 DMA1 通道 4,启动传输。

5)查询 DMA 传输状态

配置DMA的dma.c

#include "dma.h"
DMA_InitTypeDef DMA_InitStructure;
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)
{
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能 DMA 时钟
	DMA_DeInit(DMA_CHx);//将DMA的通道1寄存器重设为缺省值
	DMA1_MEM_LEN=cndtr;
	DMA_InitStructure.DMA_PeripheralBaseAddr=cpar;//DMA外设串口基地址
	DMA_InitStructure.DMA_MemoryBaseAddr=cmar;//DMA内存基地址
	DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;//数据传输方向内存到外设
	DMA_InitStructure.DMA_BufferSize=cndtr;//DMA通道的DMA缓存的大小
	DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设地址不变
	DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//内存地址寄存器递增
	//数据宽度为8位
	DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
	//数据宽度为8位
	DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;//工作在正常缓存模式
	DMA_InitStructure.DMA_Priority=DMA_Priority_Medium;//DM 通道拥有中优先级
	DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;//非内存到内存传输
	DMA_Init(DMA_CHx,&DMA_InitStructure);//初始化 DMA 的通道
}
//开启一次DMA传输
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{
	DMA_Cmd(DMA_CHx,DISABLE );//关闭 USART1 TX DMA1所指示的通道
	DMA_SetCurrDataCounter(DMA1_Channel4,DMA1_MEM_LEN);//设置 DMA 缓存的大小
	DMA_Cmd(DMA_CHx, ENABLE); //使能 USART1 TX DMA1 所指示的通道
}

看这一句

void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)

这是初始化DMA的语句,第一个参数填DMA的通道CHx ,(本次实验用到的是通道4),第二个参数是外设地址,我们是要向串口发送数据,所以要填串口接受发送数据存储器 USART1->DR 的地址,第三个参数填存储器地址,(可以理解为待发送数据的地址),第四个参数填数据传输量(即待发送数据的大小)

dma.h

#ifndef _DMA_H
#define _DMA_H
#include "sys.h"
void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr);
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx);
#endif

主函数

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "lcd.h"
#include "key.h"
#include "dma.h"
const u8 TEXT_TO_SEND[]={"DMA test~~~~~~"};
#define TEXT_LENTH sizeof(TEXT_TO_SEND)-1
u8 SendBuff[(TEXT_LENTH+2)*100];
u16 i;u8 t;float pro=0;
void init(void)
{
	delay_init();
	uart_init(9600);
	LED_Init();
	KEY_Init();
	LCD_Init();
	//DMA1通道4,外设为串口1,存储器为SendBuff,长(TEXT_LENTH+2)*100.
	MYDMA_Config(DMA1_Channel4,(u32)&USART1->DR,(u32)SendBuff,(TEXT_LENTH+2)*100);
	POINT_COLOR=RED;
	LCD_ShowString(60,40,200,24,24," DMA Test~^~");
	LCD_ShowString(60,70,200,16,16," M Difficult");
	LCD_ShowString(60,90,200,16,16," By--yh");
	LCD_ShowString(60,110,200,16,16," 2015/1/25");
	LCD_ShowString(60,130,200,16,16,"KEY0:Start");
	t=0;
	for(i=0;i<(TEXT_LENTH+2)*100;i++)//填充缓存区(生成待发送数据)
	{
		if(t>=TEXT_LENTH)//添加换行
		{
			SendBuff[i++]=0x0d;
			SendBuff[i]=0x0a;
			t=0;
		}
		else
			SendBuff[i++]=TEXT_TO_SEND[t++];
	}
}
 int main(void)
{
	init();i=0;
	POINT_COLOR=BLUE;//设置字体为蓝色
	while(1)
	{
		t=KEY_Scan(0);
		if(t==KEY0_PRES)
		{
			LCD_ShowString(60,150,200,16,16,"Start Transimit....");
			LCD_ShowString(60,170,200,16,16,"   %");//显示百分号
			printf("\r\nDMA DATA: \r\n ");
			USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
			MYDMA_Enable(DMA1_Channel4);//开始一次DMA传输!
			//等待 DMA 传输完成,此时我们来做另外一些事,点灯
			//实际应用中,传输数据期间,可以执行另外的任务
			while(1)
			{
				if(DMA_GetFlagStatus(DMA1_FLAG_TC4)!=RESET)//等待通道 4 传输完成
				{
					DMA_ClearFlag(DMA1_FLAG_TC4);//清除通道 4 传输完成标志
					break;
				}
				pro=DMA_GetCurrDataCounter(DMA1_Channel4);//当前还剩余数据量
				pro=1-pro/((TEXT_LENTH+2)*100);//得到百分比
				pro*=100; //扩大 100 倍
				LCD_ShowNum(60,170,pro,3,16);
				//LED1=!LED1;
			}
			LCD_ShowNum(60,170,100,3,16);//显示100%
			LCD_ShowString(60,150,200,16,16,"Transimit Finished!");//传送完成
		}
		i++;
		delay_ms(10);
		if(i==20)
		{
			LED0=!LED0;
			i=0;
		}
	}
}

有一个函数

uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx)

获得当前数据量剩余大小(还有多少没发送完)

其实最重要的当传输数据时MCU的CPU是可以干别的事情的,这才我们利用DMA传输的优势(虽然本实验没有体现出来)

我们可以在串口助手里看到如下内容

说明串口接收到了DMA传输的数据了。。

此实验可以对比之前的串口通信的实验,那次是普通的发送

时间: 2024-08-24 09:22:42

cortex_m3_stm32嵌入式学习笔记(十九):DMA实验(高速传输)的相关文章

cortex_m3_stm32嵌入式学习笔记(九):PWM 输出实验

PWM 简介 脉冲宽度调制(PWM),是英文"Pulse Width Modulation" 的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术.简单一点,就是对脉冲宽度的控制. STM32 的定时器除了 TIM6 和 7.其他的定时器都可以用来产生 PWM 输出.其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出.而通用定时器也能同时产生多达 4路的 PWM 输出,这样, STM32 最多可以同时产生 30 路 PWM

angular学习笔记(十九)

本篇主要介绍angular使用指令修改DOM: 使用angular指令可以自己扩展html语法,还可以做很多自定义的事情.在后面会专门讲解这一块的知识,这一篇只是起到了解入门的作用. 与控制器,过滤器,服务,一样,可以通过模块实例的directive的方法来创建指令: var someModule = angular.module('SomeModule',[]); someModule.directive('directiveName',function(){ return { link: f

cortex_m3_stm32嵌入式学习笔记(十):输入捕捉实验(定时器的输入捕捉)

输入捕获模式可以用来测量脉冲宽度或者测量频率. STM32 的定时器,除了 TIM6 和 TIM7,其他定时器都有输入捕获功能. STM32 的输入捕获,简单的说就是通过检测 TIMx_CHx 上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值( TIMx_CNT)存放到对应的通道的捕获/比较寄存器( TIMx_CCRx)里面,完成一次捕获.同时还可以配置捕获时是否触发中断/DMA 等. 本章我们用到 TIM2_CH1 来捕获高电平脉宽,也就是要先设置输入捕获为上升沿

cortex_m3_stm32嵌入式学习笔记(十八):DAC实验(数模转换)

STM32 的 DAC 模块(数字/模拟转换模块)是 12 位数字输入,电压输出型的 DAC. DAC可以配置为 8 位或 12 位模式,也可以与 DMA 控制器配合使用. DAC 工作在 12 位模式时,数据可以设置成左对齐或右对齐. DAC 模块有 2 个输出通道,每个通道都有单独的转换器.在双 DAC 模式下, 2 个通道可以独立地进行转换,也可以同时进行转换并同步地更新 2 个通道的输出. 本节实验,我们将利用按键(或 USMART) 控制 STM32 内部 DAC1来输出电压,通过 A

cortex_m3_stm32嵌入式学习笔记(十六):ADC实验(模数转换)

之前没学过数模电,对A/D D/A转换一窍不通,也百度了很多资料大都深奥难懂..算了,先自以为是一下吧,等以后学了专业课再说..(寒假回家一定要学..恩 就这么决定了)看了那么多资料,感觉 A/D转换就是将电压(或者是其他模拟量:如 压力,图像等)转换为数字,D/A就是反过来,而ADC就是A/D转换器,他可以采集外部电压转化为数字.本节实验通过ADC采集外部电压转换为数字显示在屏幕上. STM32 拥有 1~3 个 ADC( STM32F101/102 系列只有 1 个 ADC),这些 ADC

cortex_m3_stm32嵌入式学习笔记(十五):待机唤醒实验(WK_UP外部中断)

很多单片机都有低功耗模式, STM32 也不例外.在系统或电源复位以后,微控制器处于运行状态.运行状态下的 HCLK 为 CPU 提供时钟,内核执行程序代码.当 CPU 不需继续运行时,可以利用多个低功耗模式来节省功耗,例如等待某个外部事件时.用户需要根据最低电源消耗,最快速启动时间和可用的唤醒源等条件,选定一个最佳的低功耗模式. STM32 的低功耗模式有 3 种: 1)睡眠模式( CM3 内核停止,外设仍然运行) 2)停止模式(所有时钟都停止) 3)待机模式( 1.8V 内核电源关闭) 在这

cortex_m3_stm32嵌入式学习笔记(二十二):触摸屏实验(触摸屏驱动)

目前最常用的触摸屏有两种:电阻式触摸屏与电容式触摸屏. 电阻式触摸屏 在 Iphone 面世之前,几乎清一色的都是使用电阻式触摸屏, 电阻式触摸屏利用压力感应进行触点检测控制,需要直接应力接触, 通过检测电阻来定位触摸位置. 电阻触摸屏的主要部分是一块与显示器表面非常配合的电阻薄膜屏,这是一种多层的复合薄膜,它以一层玻璃或硬塑料平板作为基层,表面涂有一层透明氧化金属(透明的导电电阻)导电层,上面再盖有一层外表面硬化处理.光滑防擦的塑料层.它的内表面也涂有一层涂层.在他们之间有许多细小的(小于 1

cortex_m3_stm32嵌入式学习笔记(二十):IIC实验(I2C串行总线)

IIC(Inter- Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器及其外围设备.它是由数据线SDA 和时钟SCL构成的串行总线,可发送和接收数据.在 CPU 与被控 IC 之间. IC 与 IC 之间进行双向传送, 高速 IIC 总线一般可达 400kbps 以上. ALIENTEK MiniSTM32 开发板板载的 EEPROM 芯片型号为 24C02.该芯片的总容量是 256个字节,该芯片通过 IIC 总线与外部连接,我们本章

cortex_m3_stm32嵌入式学习笔记(二十四):内存管理实验(动态内存)

有用过C语言编程的童鞋对动态管理内存肯定有点了解..好处就不多说了 今天实现STM32的动态内存管理 内存管理,是指软件运行时对计算机内存资源的分配和使用的技术.其最主要的目的是如何高效,快速的分配,并且在适当的时候释放和回收内存资源. 内存管理的实现方法有很多种,他们其实最终都是要实现两个函数:malloc 和 free(好熟悉): malloc 函数用于内存申请, free 函数用于内存释放. 实现方式:分块式内存管理 从上图可以看出,分块式内存管理由内存池和内存管理表两部分组成.内存池被等