DMA(直接存储器访问)
DMA全称Direct Memory Access,即直接存储器访问。用来提供外设和存储器之间,或者存储器和存储器之间的高速数据传输。无须CPU控制,数据可以通过DMA传输,它通过硬件为RAM与I/O设备之间开辟一条直接传送数据的通道,这节省CPU的资源,使CPU效率提高。
一、STM32 DMA主要特性
二、DMA控制器框图
三、DMA通道请求映像
DMA1控制器从外设(TIMx[x=1、 2、 3、 4]、 ADC1、 SPI1、 SPI/I2S2、 I2Cx[x=1、 2]和USARTx[x=1、 2、 3])产生的7个请求,通过逻辑或输入到DMA1控制器,这意味着同时只能有一个请求有效。外设的DMA请求,可以通过设置相应外设寄存器中的控制位,被独立地开启或关闭。
DMA2控制器从外设(TIMx[5、 6、 7、 8]、 ADC3、 SPI/I2S3、 UART4、 DAC通道1、 2和SDIO)产生的5个请求,经逻辑或输入到DMA2控制器,这意味着同时只能有一个请求有效。外设的DMA请求,可以通过设置相应外设寄存器中的DMA控制位,被独立地开启或关闭
注意:
1. DMA2控制器及相关请求仅存在于大容量产品和互联型产品中。
2. ADC3、SDIO和TIM8的DMA请求只在大容量的产品中存在。
四、DMA仲裁器
仲裁器根据通道请求的优先级来启动外设/存储器的访问。
优先权管理分2个阶段:
● 软件:每个通道的优先权可以在DMA_CCRx寄存器中设置,有4个等级:
[1].最高优先级
[2].高优先级
[3].中等优先级
[4].低优先级
● 硬件:如果2个请求有相同的软件优先级,则较低编号的通道比较高编号的通道有较高的优
先权。举个例子,通道2优先于通道4
注意: 在大容量产品和互联型产品中, DMA1控制器拥有高于DMA2控制器的优先级。
五、DMA中断
每个DMA通道都可以在DMA传输过半、传输完成和传输错误时产生中断。为应用的灵活性考虑,通过设置寄存器的不同位来打开这些中断。
注意: 在大容量产品中, DMA2通道4和DMA2通道5的中断被映射在同一个中断向量上。在互联型产品中, DMA2通道4和DMA2通道5的中断分别有独立的中断向量。所有其他的DMA通道都有自己的中断向量。
六、DMA核心寄存器简述
七、DMA通道配置步骤
设置外设地址,通过寄存器DMA_CPARx设置,只需在这个寄存器里面写入外设地址值。
设置存储器基地址,通过寄存器DMA_CMARx设置,只需在这个寄存器里面写入存储器基地址值。假设把数组sDMA作为存储器,那么在该寄存器写入&sDMA 就行。
通过寄存器DMA_CNDTRx设置DMA通道数据传输量。该寄存器的数值将在DMA启动后自减,每次DMA传输,都重新向该寄存器写入要传输的数据量。
配置信息通过寄存器DMA_CCRx设置。设置存储器和外设传输的数据位宽,设置存储器到外设的存储器地址增量模式,传输的通道优先级等等。
完成以上配置后,使能DMA_CCRx最低位开启DMA传输。
八、例程代码
/******************************************************************************************************************************
本例程功能简述:
1.DMA通道CH4传输,存储器 --> 外设(USART1)
2.Key Up传输19200Byte,Key Down传输15360Byte
4.TFT显示已传输数据、剩余数据、完成比例
5.传输基数据为ASCII为0x20到0x7E
******************************************************************************************************************************/
int main(void)
{
vSystem_Init();
vSendData_DealWith();
while(1)
{
vKeyBoard_Service_Handle();
}
}
/******************************************************************************************************************************
*函数名: vSystem_Init
*描 述: 系统初始化
******************************************************************************************************************************/
static void vSystem_Init(void)
{
SystemInit();
vSysTick_Init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//Priority Grouping 4 bit
vLED_GPIO_Config();
vKEY_GPIO_Config();
vUART_Init();
vTFT_Init();
vDMA_Init();
vTIM_Init();
#if ( USE_SEGGER_RTT_ENABLE > 0 )//Use segger rtt enable
SEGGER_RTT_Init();
SEGGER_RTT_printf(0, "STM32F103ZET6 Start Running......\r\n");
#endif
#if ( USE_KEIL_MACRO_INFO_ENBALE > 0 )//Use Keil macro information enable
vKeil_Macro_Information();
#endif
}
/******************************************************************************************************************************
*函数名: vSendData_DealWith
*描 述:发送数据处理
******************************************************************************************************************************/
static void vSendData_DealWith(void)
{
u8 mDat[ASCII_MAX] = {0};
u8 i = 0;
for(i=0; i<ASCII_MAX; i++)
mDat[i] = 0x20 + i;
mDat[94] = 0x0D;
mDat[95] = 0x0A;
for(i=0; i<SDAT_LEN; i++)
memcpy(&sDMA[ASCII_MAX * i], mDat, ASCII_MAX);
}
/********************************************************************************************
*函数名:vKeyBoard_Service_Handle
*描 述:键盘服务处理
********************************************************************************************/
void vKeyBoard_Service_Handle(void)
{
float remDat = 0;//remaining data
u16 talDat = 0;//total data
if(KeyStr.Modes == Idle)
return;
switch(KeyStr.Modes)
{
case KeyUpClick :
{
LED2_SET();
talDat = ASCII_MAX * SDAT_LEN;
}
break;
case KeyDownClick :
{
LED3_SET();
talDat = ASCII_MAX * 160;
}
break;
case KeyLeftClick :
{
LED5_CPL();
}
break;
case KeyRightClick :
{
LED5_CPL();
}
break;
default : break;
}
if(talDat)
{
vDMA_Transmit_Enable(DMA1_Channel4, talDat);
while(1)
{
if(DMA_GetFlagStatus(DMA1_FLAG_TC4) != RESET)//Get DMA Status Flag(transfer complete)
{
DMA_ClearFlag(DMA1_FLAG_TC4);//Clears DMA flags(transfer complete)
break;
}
remDat = DMA_GetCurrDataCounter(DMA1_Channel4);//Current remaining data
vTFT_Show_Number(130, 120, 16, (talDat - remDat), 5, GREEN, BLUE);//Finish Data
vTFT_Show_Number(130, 140, 16, remDat, 5, GREEN, BLUE);//Residue Data
vTFT_Show_Number(130, 160, 16, ((1-(remDat/talDat)) * 100), 3, GREEN, BLUE);//Percentage
}
remDat = DMA_GetCurrDataCounter(DMA1_Channel4);
vTFT_Show_Number(130, 120, 16, (talDat - remDat), 5, GREEN, BLUE);
vTFT_Show_Number(130, 140, 16, remDat, 5, GREEN, BLUE);
vTFT_Show_Number(130, 160, 16, ((1-(remDat/talDat)) * 100), 3, GREEN, BLUE);
if(KeyStr.Modes == KeyUpClick)
LED2_CLR();
else if(KeyStr.Modes == KeyDownClick)
LED3_CLR();
}
KeyStr.Modes = Idle;
}
/******************************************************************************************************************************
*函数名: vDMA_Config
*描 述:DMA配置
*输 入: chx : DAM传输通道
perAddr : 外设基地址
memAddr : 存储器基地址
******************************************************************************************************************************/
static void vDMA_Config(DMA_Channel_TypeDef* chx, u32 perAddr, u32 memAddr)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //Enable DMA1 clock
DMA_DeInit(chx); //DMA Channel registers reset
DMA_InitStructure.DMA_PeripheralBaseAddr = perAddr; //Peripheral base address
DMA_InitStructure.DMA_MemoryBaseAddr = memAddr; //Memory base address
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //DMA transmit direction
DMA_InitStructure.DMA_BufferSize = 0; //MDA Channel buffer size
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Peripheral address is incremented
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //Memory address is incremented
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //Peripheral data width
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //Memory data width
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //DMAy Channe mode
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //MDA Channel priority
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //Memory-to-memory transfer
DMA_Init(chx, &DMA_InitStructure); //DMA init
}
/******************************************************************************************************************************
*函数名: vDMA_Transmit_Enable
*描 述:DMA传输使能
*输 入: chx : DAM传输通道
dataSize : 传输字节数
******************************************************************************************************************************/
void vDMA_Transmit_Enable(DMA_Channel_TypeDef* chx, u16 dataSize)
{
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);//Enable USART1 DMA
DMA_Cmd(chx, DISABLE); //DMA disable
DMA_SetCurrDataCounter(chx, dataSize); //Current DMA Channel transfer Counter
DMA_Cmd(chx, ENABLE); //DMA enable
}
/******************************************************************************************************************************
函数名: vDMA_Init
描 述:DMA初始化
******************************************************************************************************************************/
u8 sDMA[ASCII_MAX * SDAT_LEN] = {0};
void vDMA_Init(void)
{
vDMA_Config(DMA1_Channel4, ((u32)(&USART1->DR)), (u32)sDMA);
}
#define SDAT_LEN 200
#define ASCII_MAX 96
void vDMA_Transmit_Enable(DMA_Channel_TypeDef* chx, u16 dataSize); //vDMA_Transmit_Enable
void vDMA_Init(void); //vDMA_Init
extern u8 sDMA[ASCII_MAX * SDAT_LEN];