5、DMA Demo(STM32F407)

DMA简介

DMA 全称Direct Memory Access,即直接存储器访问。 DMA传输将数据从一个地址空间复制到另一个地址空间。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。 DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。

作用:为CPU减负。

DMA原理

STM32F4最多有2个DMA控制器,2个DMA控制器总共有16个数据流(每个控制器8个)。每个DMA控制器都用于管理一个或者多个外设的存储器访问请求。每个数据流总共可以有多达8个通道(或请求),每个通道都有一个仲裁器,用于处理DMA请求间的优先级。

DMA框图

 

DMA配置参数

  1.  通道
  2. 优先级
  3. 数据传输方向
  4. 存储器/外设 数据宽度
  5. 存储器/外设 地址是否增量
  6. 循环模式
  7. 数据传输量

程序源码

dma.h

#ifndef __DMA_H
#define	__DMA_H	   
#include "sys.h"

void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr);//配置DMAx_CHx
void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr);	//使能一次DMA传输		   
#endif

dma.c

#include "dma.h"
#include "delay.h"

// DMAx的各通道配置
// 这里的传输形式是固定的,这点要根据不同的情况来修改
// 从存储器->外设模式/8位数据宽度/存储器增量模式
// DMA_Streamx:DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7
// chx:DMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7
// par:外设地址
// mar:存储器地址
// ndtr:数据传输量
void MYDMA_Config(DMA_Stream_TypeDef *DMA_Streamx, u32 chx, u32 par, u32 mar, u16 ndtr)
{

  DMA_InitTypeDef DMA_InitStructure;

  if ((u32)DMA_Streamx > (u32)DMA2) // 得到当前stream是属于DMA2还是DMA1
  {
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); // DMA2时钟使能
  }
  else
  {
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); // DMA1时钟使能
  }
  DMA_DeInit(DMA_Streamx);

  while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE)
  {
  } // 等待DMA可配置

  /* 配置 DMA Stream */
  DMA_InitStructure.DMA_Channel = chx;                                    // 通道选择
  DMA_InitStructure.DMA_PeripheralBaseAddr = par;                         // DMA外设地址
  DMA_InitStructure.DMA_Memory0BaseAddr = mar;                            // DMA 存储器0地址
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;                 // 存储器到外设模式
  DMA_InitStructure.DMA_BufferSize = ndtr;                                // 数据传输量
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        // 外设非增量模式
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 // 存储器增量模式
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据长度:8位
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         // 存储器数据长度:8位
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           // 使用普通模式
  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;                   // 中等优先级
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;         // 存储器突发单次传输
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; // 外设突发单次传输
  DMA_Init(DMA_Streamx, &DMA_InitStructure);                          // 初始化DMA Stream
}
// 开启一次DMA传输
// DMA_Streamx:DMA数据流,DMA1_Stream0~7/DMA2_Stream0~7
// ndtr:数据传输量
void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx, u16 ndtr)
{

  DMA_Cmd(DMA_Streamx, DISABLE); // 关闭DMA传输

  while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE)
  {
  } // 确保DMA可以被设置

  DMA_SetCurrDataCounter(DMA_Streamx, ndtr); // 数据传输量

  DMA_Cmd(DMA_Streamx, ENABLE); // 开启DMA传输
}

main.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "key.h"
#include "dma.h"

#define SEND_BUF_SIZE 8200 // 发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍.

u8 SendBuff[SEND_BUF_SIZE]; // 发送数据缓冲区
const u8 TEXT_TO_SEND[] = {"ALIENTEK Explorer STM32F4 DMA 串口实验"};

int main(void)
{
	u16 i;
	u8 t = 0;
	u8 j, mask = 0;
	float pro = 0;									// 进度
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置系统中断优先级分组2
	delay_init(168);								// 初始化延时函数
	uart_init(115200);								// 初始化串口波特率为115200
	LED_Init();										// 初始化LED
	LCD_Init();										// LCD初始化
	KEY_Init();										// 按键初始化
	// 配置DMA,使用DMA2的第7个数据流和第4个通道,将USART1的数据寄存器作为外设,SendBuff作为存储器,传输长度为SEND_BUF_SIZE
	MYDMA_Config(DMA2_Stream7, DMA_Channel_4, (u32)&USART1->DR, (u32)SendBuff, SEND_BUF_SIZE); // DMA2,STEAM7,CH4,外设为串口1,存储器为SendBuff,长度为:SEND_BUF_SIZE.
	POINT_COLOR = RED;
	LCD_ShowString(30, 50, 200, 16, 16, "Explorer STM32F4");
	LCD_ShowString(30, 70, 200, 16, 16, "DMA TEST");
	LCD_ShowString(30, 90, 200, 16, 16, "ATOM@ALIENTEK");
	LCD_ShowString(30, 110, 200, 16, 16, "2014/5/6");
	LCD_ShowString(30, 130, 200, 16, 16, "KEY0:Start");
	POINT_COLOR = BLUE; // 设置字体为蓝色
	// 显示提示信息
	j = sizeof(TEXT_TO_SEND);			// 获取TEXT_TO_SEND字符串的长度,并赋值给变量j
	for (i = 0; i < SEND_BUF_SIZE; i++) // 循环从0到SEND_BUF_SIZE-1,填充SendBuff数组
	{
		if (t >= j) // 加入换行符
		{
			if (mask)
			{
				SendBuff[i] = 0x0a; // 插入换行符
				t = 0;
			}
			else
			{
				SendBuff[i] = 0x0d; // 插入回车符
				mask++;
			}
		}
		else // 复制TEXT_TO_SEND语句
		{
			mask = 0;
			SendBuff[i] = TEXT_TO_SEND[t];
			t++;
		}
	}
	POINT_COLOR = BLUE; // 设置字体为蓝色
	i = 0;
	while (1)
	{
		t = KEY_Scan(0);	// 清除按键标记
		if (t == KEY0_PRES) // KEY0按下
		{
			printf("\r\nDMA DATA:\r\n");
			LCD_ShowString(30, 150, 200, 16, 16, "Start Transimit....");
			LCD_ShowString(30, 170, 200, 16, 16, "   %");  // 显示百分号
			USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); // 使能串口1的DMA发送
			MYDMA_Enable(DMA2_Stream7, SEND_BUF_SIZE);	   // 启动一次DMA传输
			// 等待DMA传输完成,此时我们来做另外一些事,点灯
			// 实际应用中,传输数据期间,可以执行另外的任务
			while (1)
			{
				// 通过DMA_GetCurrDataCounter函数获取当前还剩余多少个数据未传输,然后计算传输的百分比
				if (DMA_GetFlagStatus(DMA2_Stream7, DMA_FLAG_TCIF7) != RESET) // 等待DMA2_Steam7传输完成
				{
					DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7); // 清除DMA2_Steam7传输完成标志
					break;
				}
				pro = DMA_GetCurrDataCounter(DMA2_Stream7); // 得到当前还剩余多少个数据
				pro = 1 - pro / SEND_BUF_SIZE;				// 得到百分比
				pro *= 100;									// 扩大100倍
				LCD_ShowNum(30, 170, pro, 3, 16);
			}
			LCD_ShowNum(30, 170, 100, 3, 16);							 // 显示100%
			LCD_ShowString(30, 150, 200, 16, 16, "Transimit Finished!"); // 提示传送完成
		}
		i++;
		delay_ms(10);
		if (i == 20)
		{
			LED0 = !LED0; // 提示系统正在运行
			i = 0;
		}
	}
}

实验效果

说明:

串口首先打印TFT屏的驱动IC(按下RESET键复位)

按下KEY0后,TFT屏显示“Transimit Finished!”并显示传输进度,串口重复打印“ALIENTEK Explorer STM32F4 DMA 串口实验”直至达到SEND_BUF_SIZE的值

DMA实验效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值