STM32--DMA
STM32的DMA配置
结构体成员分析
首先我们看一下DMA_Init();初始化里面的结构体成员:
DMA_Channel:这个是DMA的通道选择,可选通道 0 至通道 7,每一个外设对应固定的通道。这个直接控制寄存器的DMA_SxCR寄存器的 CHSEL[2:0] 位的值。即:
DMA_PeripheralBaseAddr:外设地址。这里直接控制的是寄存DMA_SxPAR 寄存器的值;一般设置为外设的数据寄存器地址。
DMA_Memory0BaseAddr:存储器 0 地址,设定DMA_SxM0AR寄存器值;一般设置为我们自定义存储区的首地址。我们程序先自定义一个 16 位无符号整形数组用来存放每个通道的值,所以把数组 首 地 址 (直 接 使 用 数 组 名 即 可) 赋值给DMA_Memory0BaseAddr。
DMA_DIR:传输方向选择,可选外设到存储器、存储器到外设以及存储器到存储器。它设定DMA_SxCR 寄存器的 DIR[1:0] 位的值。
DMA_BufferSize:设定待传输数据数目,初始化设定DMA_SxNDTR寄存器的值。
DMA_PeripheralInc:如果配置为 DMA_PeripheralInc_Enable,使能外设地址自动递增功能,它设定DMA_SxCR寄存器的 PINC 位的值;一般外设都是只有一个数据寄存器,所以一般不会使能该位。ADC3 的数据寄存器地址是固定并且只有一个所以不使能外设地址递增。
DMA_MemoryInc:如果配置为 DMA_MemoryInc_Enable,使能存储器地址自动递增功能,它设定DMA_SxCR寄存器的 MINC 位的值;我们自定义的存储区一般都是存放多个数据的,所以使能存储器地址自动递增功能。我们之前已经定义了一个包含 4 个元素的数字用来存放数据,使能存储区地址递增功能,自动把每个通道数据存放到对应数组元素内。
DMA_PeripheralDataSize:外设数据宽度,可选字节 (8 位)、半字 (16 位) 和字 (32 位),它设定DMA_SxCR 寄存器的 PSIZE[1:0] 位的值。ADC 数据寄存器只有低 16 位数据有效,使用半字数据宽度。
DMA_MemoryDataSize:存储器数据宽度,可选字节 (8 位)、半字 (16 位) 和字 (32 位),它设定DMA_SxCR 寄存器的 MSIZE[1:0] 位的值。保存 ADC 转换数据也要使用半字数据宽度,这跟我们定义的数组是相对应的。
DMA_Mode:DMA传输模式选择,可选一次传输或者循环传输,它设定 DMA_SxCR寄存器的 CIRC 位的值。我们希望 ADC 采集是持续循环进行的,所以使用循环传输模式。
DMA_Priority:软件设置数据流的优先级,有 4 个可选优先级分别为非常高、高、中和低,它设定DMA_SxCR 寄存器的 PL[1:0] 位的值。DMA 优先级只有在多个 DMA 数据流同时使用时才有意义,这里我们设置为非常高优先级就可以了。
DMA_FIFOMode:FIFO模式使能,如果设置为DMA_FIFOMode_Enable表示使能 FIFO 模式功能;它设定DMA_SxFCR 寄存器的 DMDIS 位。
DMA_FIFOThreshold:FIFO阈值选择,可选 4 种状态分别为 FIFO 容量的 1/4、1/2、3/4 和满;它设定DMA_SxFCR寄存器的 FTH[1:0] 位;DMA_FIFOMode 设置为DMA_FIFOMode_Disable,那么DMA_FIFOThreshold值无效。
DMA_MemoryBurst:存储器突发模式选择,可选单次模式、4 节拍的增量突发模式、8 节拍的增量突发模式或 16 节拍的增量突发模式,它设定DMA_SxCR寄存器的 MBURST[1:0] 位的值。
DMA_PeripheralBurst:外设突发模式选择,可选单次模式、4 节拍的增量突发模式、8 节拍的增量突发模式或 16 节拍的增量突发模式,它设定DMA_SxCR 寄存器的 PBURST[1:0] 位的值。
DMA完整的配置
这些结构体分析完了。我们再看看一般情况下怎么完整的配置DMA呢?
本文以stm32f4的存储器到外设的模式为例。这里用到usart1。
我们查阅DMA2的各个通道的请求映像知道USART1对应DMA2的数据流7通道4。
首先我们要开启DMA对应的时钟,例:开启DMA2时钟:
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
将DMAyChannelx寄存器的初始化为其默认值。即复位初始化DMA的数据流:
DMA_DeInit(DMA2_Stream7);
等待数据流复位完成
while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE);
配置DMA_Init()函数
u16 SENDBUFF_SIZE=5000; //发送的数据量
uint8_t SendBuff[SENDBUFF_SIZE];
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_Channel=DMA_Channel_4;//通道4
DMA_InitStructure.DMA_PeripheralBaseAddr = (USART1_BASE+0x04); //设置DMA源,串口DMA的数据寄存器地址
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)SendBuff; //配置内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //方向:存储器到外设
DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;//传输大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址自增
DMA_InitStructure.DMA_PeripheralDataSize =DMA_PeripheralDataSize_Byte;//外设数据单位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //内存数据单位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //DMA模式《不断循环》
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //优先级为中
/*禁用FIFO*/
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//存储器突发传输16个节拍
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//外设突发传输1个节拍
DMA_Init(DMA2_Stream7, &DMA_InitStructure);
初始化完之后别忘了要对DMA进行使能:
DMA_Cmd(DMA2_Stream7, ENABLE);
最后在等待数据流有效:
while(DMA_GetCmdStatus(DMA2_Stream7) != ENABLE);
这样就完整的配置完了DMA在串口usart1上的配置了。