1.DMA简介
学习过操作系统都知道,DMA就是直接存储器访问。就是当CPU给予DMA指命,DMA就会将数据从一个地址复制到另外一个地址空间。为什么需要呢,因为CPU是一个很重要的资源,在等待取数据和等待数据发送完成,是极其不划算的,所以CPU直接给DMA指命,DMA就去干数据移动。为CPU减少了很多工作量。
STM32F最多有2个DMA寄存器,2个DMA寄存器总共有16个数据流(每个控制器8个)。每个DMA控制器都用于管理一个或者多个外设的存储器访问请求,而每个数据流总共有多达8个通道,有一个仲裁器,用于处理DMA请求间的优先级。
2个DMA寄存器>每个DMA寄存器16个数据流>每个数据流最多可多达8个通道
有一个仲裁器
2.DMA特性
1.每个DMA控制器都用于管理一个或者多个外设的存储器访问请求,而每个数据流总共有多达8个通道
2.每个数据流都有单独的四级32位先进先出的存储缓冲区,可以用于FIFO模式或之间模式
FIFO模式:可以通道软件将阈值分别选为FIFO大小的1/4,1/2,3/4
直接模式:每个DMA请求会立即启动对存储器的转输,当在直接模式下将DMA的请求配置以存储器到外设模式转输数据时,DMA仅会将一个数据从存储器预加到内部FIFO,从而确保一旦外设触发DMA请求时立即转输数据
通过硬件可以将每个数据流配置为
支持外设到存储器,存储器到外设,存储器到存储器的常规通道
支持在存储器双缓冲的双缓冲区通道
3.8个数据流中每一个都哟偶一个两节到专用的DMA通道
4.DMA数据流请求之前的优先级可以用软件编程(非常高,高,中,低),在软件优先级相同的情况下,可以通过硬件决定优先级(请求优先级0高于优先级1)
5.每个数据流也支持通过软件触发存储器到存储器的转输
6.可供数据流选择的通道请求多达8个,可以通过软件配置。
7.要转输的数目可以由DMA控制器或外设管理
6.对于数据位宽不对的地方,有一个数据缓存器
7.5个事件标志位
3.DMA框图
很明显,有8个数据流(REQ_STREAM0-REQ_STREAM8)
每个数据;流对应最多8个通道,
通道的选择
DMA的通道与数据流
DMA的数据流
8个DMA控制器数据能够提供源和目标之间的单向转输。
可配置
外设到存储器,存储器到外设,存储器到存储器
双缓冲,使用存储器的两个存储器指针的双缓冲转输
要转输的数据量可以编程
DMA事务
DMA仲裁器
DMA指针递增
就是转输数据指针一直加一
DMA循环模式
转输数据完后,重新回到开头继续转输
可编程数据宽度
单次或多次转输
可以一个字节一个字节转输,也可以一次多个字节
双缓冲模式
就是两个地址转入数据到一个地址,一个在传数据中,可以准备另一个缓冲的准备。
DMA中断
开启对应的中断需要对应的使能位
4.数据流配置
1.将DMA_SxCR中的EN位置0,就是禁止转输数据了,我要开始配置了的意思。
2.外设基地址
3.存储器基地址
4.数据数,一次转输数据大小
5.选择DMA通道
6.
7.配置数据流优先级
8.配置FIFO
9.数据转输方向,中断等
10DMA_SxCR中的EN位置1,开始转输
5.DMA的HAL库函数
1.DMA初始化函数
HAL_StatusTypeDef HAL_DMA_Init(DMA_HandleTypeDef *hdma);
Instance设置了数据流与DMA选择,
Init配置数据流方向,
外设增量,存储器增量(每次转输时候是否地址移动)
外设长度,存储器长度(一次转输的内容长度)
模式
优先级
FIFO模式
存储器与外设突发模式
2.开启DMA通道
开启的时候是否开启中断
第一个正在转输时候中断
第二个等待中断
第三个中断通用处理函数
还有一个回调函数,但是在结构体里面
3.双缓冲DMA通道
4.DMA通道连接
就是说相关结构体中专门有一个变量,可以用于指向DMA结构体变量,用于将两方软件连接
4.DMA外设相关函数
5.DMA配置相关步骤
6.DMA的程序
通过DMA,将内存中一个数组连续发送至串口,而且显示当前转输百分比
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "lcd.h"
#include "usart.h"
DMA_HandleTypeDef DMAhandle_2;
float pro=0; //进度
#define SEND_BUF_SIZE 7800 //发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍.
u8 SendBuff[SEND_BUF_SIZE]; //发送数据缓冲区
const u8 TEXT_TO_SEND[]={"ALIENTEK Apollo STM32F4 DMA 串口实验"};
void MY_DMAInit(void) //DMA2数据流7通道4
{
__HAL_RCC_DMA2_CLK_ENABLE();
__HAL_LINKDMA(&UART1_Handler,hdmatx,DMAhandle_2); //第一个参数外设的句柄,第二个赋值的地方,第三个DMA句柄
DMAhandle_2.Instance=DMA2_Stream7;
DMAhandle_2.Init.Channel =DMA_CHANNEL_4;
DMAhandle_2.Init.Direction=DMA_MEMORY_TO_PERIPH;
DMAhandle_2.Init.FIFOMode= DMA_FIFOMODE_DISABLE; //不使用FIFO
DMAhandle_2.Init.FIFOThreshold=DMA_FIFO_THRESHOLD_FULL;
DMAhandle_2.Init.MemBurst=DMA_MBURST_SINGLE; //存储器单次转输
DMAhandle_2.Init.PeriphBurst=DMA_PBURST_SINGLE; //外设单次转输
DMAhandle_2.Init.MemDataAlignment= DMA_MDATAALIGN_BYTE; //存储器数据宽度
DMAhandle_2.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE; //外设数据宽度
DMAhandle_2.Init.MemInc=DMA_MINC_ENABLE; //存储器递增
DMAhandle_2.Init.PeriphInc=DMA_PINC_DISABLE; //外设不递增
DMAhandle_2.Init.Mode=DMA_NORMAL; //正常模式
DMAhandle_2.Init.Priority= DMA_PRIORITY_MEDIUM;
HAL_DMA_DeInit(&DMAhandle_2); //初始化为普通配置
HAL_DMA_Init(&DMAhandle_2);
}
void MYDMA_USART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
HAL_DMA_Start(huart->hdmatx, (u32)pData, (uint32_t)&huart->Instance->DR, Size);//开启DMA传输
huart->Instance->CR3 |= USART_CR3_DMAT;//使能串口DMA发送
}
int main(void)
{
u16 i;
u8 t=0;
u8 j,mask=0;
float pro=0; //进度
Stm32_Clock_Init(360,25,2,8); //设置时钟,180Mhz
delay_init(180); //初始化延时函数
uart_init(115200); //初始化USART
LED_Init(); //初始化LED
LCD_Init(); //LCD初始化
printf("转输1");
MY_DMAInit();
printf("转输2");
j=sizeof(TEXT_TO_SEND);
for(i=0;i<SEND_BUF_SIZE;i++)//填充ASCII字符集数据
{
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++;
}
}
i=0;
printf("转输3");
while(1)
{
printf("转输开始");
MYDMA_USART_Transmit(&UART1_Handler,(u8*)SendBuff,SEND_BUF_SIZE); //启动传输
while(1)
{
pro=__HAL_DMA_GET_COUNTER(&DMAhandle_2);//得到当前还剩余多少个数据
pro=1-pro/SEND_BUF_SIZE; //得到百分比
pro*=100;
printf("%f",pro);
if(__HAL_DMA_GET_FLAG(&DMAhandle_2,DMA_FLAG_TCIF3_7))//等待DMA2_Steam7传输完成
{
__HAL_DMA_CLEAR_FLAG(&DMAhandle_2,DMA_FLAG_TCIF3_7);//清除DMA2_Steam7传输完成标志
HAL_UART_DMAStop(&UART1_Handler); //传输完成以后关闭串口DMA
break;
}
}
printf("转输完成");
delay_ms(10000);
}
}