DMA有3种实现方式:内存->内存;内存->外设;外设->内存
(外设->内存的DMA属于ADC)
stm32有俩个DMA控制器:
DMA1:7通道
DMA2:5通道,只存在于大容量(256K-512K)和互联型产品(f105和f107系列)
rbt6开发板属于中容量,标准型,只有DMA1;
由图可知,内存->外设的方式,只能使用对应的通道,不能使用其他的。
而内存->内存的方式,可以使用所有的通道。
DMA一次可以传输2^16个字节的数据
那么,如果多个通道请求到来,应该怎么办?
这时,需要DMA的仲裁器进行仲裁:
1.首先,判断DMA通道x配置寄存器(DMA_CCRx)的PL位;
2.其次,如果PL位相同,判断通道的编号,编号越小,优先级越高。(同时,DMA1的优先级大于DMA2)
typedef struct
{
uint32_t DMA_PeripheralBaseAddr;//外设地址
uint32_t DMA_MemoryBaseAddr; //存储器地址
uint32_t DMA_DIR; //传输方向
//这三个成员共同决定了内存->外设;外设->内存这俩种传输模式
uint32_t DMA_BufferSize; //传输的数量,单位由外设和存储器数据宽度决定
uint32_t DMA_PeripheralInc; //外设地址是否递增
uint32_t DMA_MemoryInc; //存储器地址是否递增
uint32_t DMA_PeripheralDataSize;//外设数据宽度
uint32_t DMA_MemoryDataSize; //存储器数据宽度
uint32_t DMA_Mode; //只发送一遍还是循环发送
uint32_t DMA_Priority; //优先级
uint32_t DMA_M2M; //存储器到存储器模式
}DMA_InitTypeDef;
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);//DMA初始化函数
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);//获取DMA的状态
//共有3种状态:传输完成(TC),传输过半(HT),传输出错(TE)
//除此之外,还有一个全局标志(GL)(不知道什么用)
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);//DMA的使能函数
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);//如果要使用中断,需要相应的DMA使能中断
一、内存->内存(Flash->SRAM)
在嵌入式中,Flash一般用来存储代码,如果要存放数据,加一个const,表示这个数据是常量,就可以存放在Flash当中;SRAM一般用来存储变量`。
那么,内存->内存的方式可不可以是其他的?
显然,由于Flash中的数据必须是常量,肯定不能作为目的地址。
而SRAM->SRAM则完全没必要。
我们一般认为,SRAM是内存,Flash是外存,所以,也可以认为Flash是外设。因此在初始化结构体时,可以认为是从内存到外设。
步骤:
由于是内存到内存的DMA方式,所以需要初始化DMA
1.初始化DMA的结构体
2.打开DMA的时钟
3.调用初始化DMA函数
4.使能DMA
由于DMA并没有使用任何的引脚,所以不需要初始化GPIO。
dma_m2m.h
#ifndef _DMA_M2M_H
#define _DMA_M2M_H
#include "stm32f10x.h"
#define BUFFER_SIZE 20
void dma_mtom_Init(void);
int strcmps(const uint32_t *buffer,uint32_t *buffers,int size);
#endif
dma_m2m.c
#include "dma_m2m.h"
//Flash中定义的数据
const uint32_t data_SRC[BUFFER_SIZE]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
//SRAM中用来接受数据的变量
uint32_t data_DST[BUFFER_SIZE];
void dma_mtom_Init()
{
DMA_InitTypeDef DMA_InitStruct;
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)data_SRC;
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)data_DST;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;//外设Flash是源地址
DMA_InitStruct.DMA_BufferSize = BUFFER_SIZE;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_InitStruct.DMA_M2M = DMA_M2M_Enable;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_Init( DMA1_Channel6, &DMA_InitStruct);
DMA_Cmd(DMA1_Channel6, ENABLE);
}
int strcmps(const uint32_t *buffer,uint32_t *buffers,int size)
{
while(size--)
{
if(*buffer!=*buffers)
{
return 0;
}
buffer++;
buffers++;
}
return 1;
}
led.h
#ifndef _LED_H
#define _LED_H
#include "stm32f10x.h"
#define LED0_GPIO_CLK RCC_APB2Periph_GPIOC
void LED_GPIO_Config(void);
#endif
led.c
#include "led.h"
void LED_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStruct.GPIO_Pin= GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode= GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin= GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode= GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStruct);
}
main.c
#include "stm32f10x.h"
#include "led.h"
#include "dma_m2m.h"
extern const uint32_t data_SRC[BUFFER_SIZE];
extern uint32_t data_DST[BUFFER_SIZE];
int main()
{
int status;
LED_GPIO_Config();
dma_mtom_Init();//初始化后,数据会由DMA方式自动传输
status=strcmps(data_SRC,data_DST,BUFFER_SIZE);
while(DMA_GetFlagStatus(DMA1_FLAG_TC6)==RESET);
if(status) //传输成功,熄灭LED3
{
GPIO_SetBits(GPIOC,GPIO_Pin_10);
}
else //传输失败,熄灭LED2
{
GPIO_SetBits(GPIOC,GPIO_Pin_9);
}
}
二、内存到外设(SRAM->UART->计算机)
步骤:
首先,由于是内存到内存的DMA方式,所以需要初始化DMA
1.初始化DMA的结构体
2.打开DMA的时钟
3.调用初始化DMA函数
4.使能DMA
其次,由于是和外设进行通信,需要初始化外设(这里使用UART2)
1.初始化UART2的结构体(由最开始那个表可知,UART2使用DMA1的通道6和7)
2.打开UART2的时钟
3.调用初始化UART2函数
4.使能UART2
最后,UART2使用了GPIO的引脚,需要初始化GPIO
1.初始化GPIOA引脚2和3的结构体
2.打开UART2的时钟
3.调用初始化GPIO函数
dma_m2p.h
#ifndef _DMA_M2P_H
#define _DMA_M2P_H
#include "stm32f10x.h"
#define BUFFER_SIZE 500
void dma_mtop_Init(void);
void usrt_init(void);
#endif
dma_m2p.c
#include "dma_m2p.h"
//SRAM中用来接受数据的变量
uint8_t data_SRC[BUFFER_SIZE];
void dma_mtop_Init()
{
DMA_InitTypeDef DMA_InitStruct;
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)(USART2_BASE+0x04);
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)data_SRC;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST;//外设Flash是目的地址
DMA_InitStruct.DMA_BufferSize = BUFFER_SIZE;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_Init( DMA1_Channel7, &DMA_InitStruct);
DMA_ClearFlag(DMA1_FLAG_TC7);
DMA_Cmd(DMA1_Channel7, ENABLE);
}
void usrt_init()
{
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef UART_InitStruct;
//配置uart的GPIO口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//配置Tx
GPIO_InitStruct.GPIO_Pin= GPIO_Pin_2;
GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//配置usrt
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
UART_InitStruct.USART_BaudRate= 115200;
UART_InitStruct.USART_WordLength= USART_WordLength_8b;
UART_InitStruct.USART_StopBits= USART_StopBits_1;
UART_InitStruct.USART_Parity= USART_Parity_No;
UART_InitStruct.USART_Mode= USART_Mode_Rx|USART_Mode_Tx;
UART_InitStruct.USART_HardwareFlowControl= USART_HardwareFlowControl_None;
USART_Init(USART2,&UART_InitStruct);
//串口使能
USART_Cmd(USART2,ENABLE);
}
main.c
#include "stm32f10x.h"
#include "led.h"
#include "dma_m2p.h"
extern uint8_t data_SRC[BUFFER_SIZE];
int main()
{
uint16_t i=0;
for(i=0;i<BUFFER_SIZE;i++)
{
data_SRC[i]='r';
}
usrt_init();
dma_mtop_Init();//初始化后,数据会由DMA方式自动传输
USART_DMACmd(USART2,USART_DMAReq_Tx,ENABLE);
}**