DMA-搬运工
1.
DMA(Direct Memory Access)直接内存访问,可以把数据从一个地方传输到另一个地方,不会占用我们的cpu。
CPU根据代码内容执行指令,
这些众多指令中,有的用于计算、有的用于控制程序、有的用于转移数据等。其中转移数据的指令,尤其
是转移大量数据,会占用大量CPU。如果是把外设A的数据,传给外设B,这种情况其实不需要CPU一直参
与,只需在A、B之间创建个通道,让它们自己传输即可。这就是DMA设计的目的,减少大量数据转移指令。
DMA主要实现将A处的数据直接搬运到B处,这里的A、B可以是内存(内/外部SRAM等),也可以是外
设(UART、I2C、SPI、ADC等),因此所有的场景如下三种:
内存到外设
外设到内存
内存到内存
DMA1有7个通道,DMA2有5个 通道,总计12个通道。
可以把DMA分为三部分。
后,DMA控制器根据通道优先级依次处理请求,当轮到该外设时,返回应答信号给该外设,该外设收到
应答信号,释放该请求,进行DMA数据传输,直至DMA传输结束;
②DMA通道:不同的外设,向不同DMA的不同通道发送请求,如图 20.1.2 和图 20.1.3 所示。比如ADC1
想使用DMA,应向DMA1的通道1发送请求。DMA1的通道1,可以接收多个外设的请求(ADC1、TIM2_CH3、
TIM4_CH1),但同一时间只能接收一个;
③DMA优先级:当多个DMA通道,同时发来请求时,获取软件配置DMA_CCRx寄存器设置的优先级,
依次响应。当软件配置优先级相同时,硬件优先级高的(通道编号小的)优先响应。有DMA2的产品中,DMA1 的优先级高于DMA2。
内存到内存所有通道都可以用。
多个DMA同时请求:
1.软件阶段:DMA_CCRx DMA通道配置寄存器PL->设置通道优先级
2.硬件阶段: 通道编号小的优先级大,DMA1优先级大于DMA2
相关寄存器
void DCMI_DMA_Init(void)
{
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能
DMA_DeInit(DMA2_Stream1);
while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE){}//等待DMA2_Stream1可配置
DMA_InitStructure.DMA_Channel = DMA_Channel_1; //通道1 DCMI通道
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&DCMI->DR;//外设地址为:DCMI->DR 摄像头
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)&LCD->LCD_RAM;//DMA 存储器0地址 lcd
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;//外设到存储器模式 搬运数据:摄像头->lcd
DMA_InitStructure.DMA_BufferSize = 10;//数据传输量
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;//存储器增量模式 关闭
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//外设数据长度:32位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord ;//存储器数据长度 16bit
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;// 使用循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;//高优先级
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; //FIFO模式
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;//使用全FIFO
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//外设突发单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//存储器突发单次传输
DMA_Init(DMA2_Stream1, &DMA_InitStructure);//初始化DMA Stream
DMA_ITConfig(DMA2_Stream1,DMA_IT_TC,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel= DMA2_Stream1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
}
//DCMI,启动传输
void DCMI_Start(void)
{
LCD_Scan_Dir(U2D_L2R); //U2D_L2R //扫描方式从上到下,从左到右,一般模式
LCD_Set_Window(0,0,320,240); //LCD设置显示窗口,如果改变了分辨率,这里需要更改
LCD_SetCursor(0,0); //设置光标
LCD_WriteRAM_Prepare(); //开始写入GRAM
DMA_Cmd(DMA2_Stream1, ENABLE);//开启DMA2,Stream1
DCMI_CaptureCmd(ENABLE);//DCMI捕获使能
}
//DCMI,关闭传输
void DCMI_Stop(void)
{
DCMI_CaptureCmd(DISABLE);//DCMI捕获使关闭
while(DCMI->CR&0X01); //等待传输结束
DMA_Cmd(DMA2_Stream1,DISABLE);//关闭DMA2,Stream1
}
//DCMI中断服务函数
void DCMI_IRQHandler(void)
{
if(DCMI_GetITStatus(DCMI_IT_LINE)==SET)//捕获到行
{
DCMI_ClearITPendingBit(DCMI_IT_LINE);//清除中断
ov_frame++;
}
}