赞
踩
本篇博客重点在于标准库函数的理解与使用,搭建一个框架便于快速开发
目录
- DMA(Direct Memory Access)直接存储器存取 ,可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预。这使CPU可以去做其他复杂的事情。
- 12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)
- 每个通道都支持软件触发和特定的硬件触发
STM32F103C8T6 DMA资源:DMA1(7个通道),具体DMA资源可以查看参考手册
DMA框图
已知DMA在AHB总线(如图)
由RCC时钟树知,应使能外部时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
外设和存储器只是个名字,我们要确定数据从哪里来到哪里去,配置数据来源和去向的地址即可
实现外设存储器,存储器到存储器,存储器到外设的转运
闪存、SRAM、外设的SRAM、APB1、APB2和AHB外设均可作为访问的源和目标
- //地址均为32位
-
- //外设存储器的基地址
- DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;
- //外设存储器的指针自增使能(转运后将地址指向下一个数据) Enable-自增 Dissable-不自增
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
-
- //存储器的基地址
- DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;
- //存储器的指针自增使能(转运后将地址指向下一个),与外设存储器一样来理解
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
一次转运次数结束后,自增的指针也会回到最初的位置,以便下一次转运。
转运并不会把原有数据拿走,是复制了一份
在传输过程中,当开启通道(DMA_CCRx的EN=1)时,不能写更改地址。需关闭通道后再更改地址。
DMA_DIR_PeripheralDST | peripheral: source | 存储器到外设 |
DMA_DIR_PeripheralSRC | peripheral : destination | 外设到存储器 |
外设和存储器只是个名字而已,重要的是你填的地址
- //转运方向:外设到存储器还是存储器到外设
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
外设和存储器的传输宽度
字节(Byte) | 8位 |
半字(HalfWord) | 16位 |
全字(Word) | 32位 |
- //外设存储器的数据宽度
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
- //储存器的数据宽度
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
可编程的数据传输数目:0~65535
这个寄存器只能在通道不工作(DMA_CCRx的EN=0)时写入。通道开启后该寄存器变为只读,指示剩余的待传输字节数目。寄存器内容在每次DMA传输后递减。 数据传输结束后,寄存器的内容或者变为0;或者当该通道配置为自动重加载模式时,寄存器的内容将被自动重新加载为之前配置时的数值。 当寄存器的内容为0时,无论通道是否开启,都不会发生任何数据传输
每转运一次就会减去一,为0停止
- //转运次数
- DMA_InitStructure.DMA_BufferSize = Size;
每个通道都同样支持软件触发,每个通道都直接连接专用的硬件DMA请求。
软件触发:
软件触发常用于存储器与存储器之间转运,其不能与循环模式同时使用
- //软件触发使能
- DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
硬件触发
硬件生成DMA请求常用于非存储器与存储器之间转运,其能与循环模式同时使用
-
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //软件触发不使能 (硬件触发,需调用对应外设请求)
-
- //然后再使能外设到DMA的请求的函数,比如ADC
- ADC_DMACmd(ADC1, ENABLE);
如上面DMA框图所示,由外设发出DMA请求
硬件触发请求DMA转运前,需要初始化特定通道,如ADC1的外设请求信号需要DMA1的通道1.
DMA_Mode_Normal | 正常模式 | 转运到传输次数为0,停止转运 |
DMA_Mode_Circular | 循环模式 | 传输次数为0后变为设定值(自动重装) |
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
当通道配置为非循环模式(正常模式)时,传输结束后(即传输次数变为0)将不再产生DMA转运。要开始新的DMA转运,需要在关闭DMA通道的情况下,在DMA_CNDTRx寄存器中重新写入传输次数。
在循环模式下,最后一次传输结束时,DMA_CNDTRx寄存器的内容会自动地被重新加载为其初始数值,内部的当前外设/存储器地址寄存器也被重新加载DMA_CPARx/DMA_CMARx寄存器设定的初始基地址。
在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、 中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推)
仲裁器
每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。
- //转运通道优先级:有四个
- DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA1的通道1
- //将上面参数配置到通道1
- DMA_Init(DMA1_Channel1, &DMA_InitStructure);
- DMA_InitTypeDef DMA_InitStructure;
- DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
- DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
- DMA_InitStructure.DMA_BufferSize = Size;
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
- DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
- DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
- DMA_Init(DMA1_Channel1, &DMA_InitStructure);
使能后将开始转运且不可更改转运地址和转运次数,这里先不使能
DMA_Cmd(DMA1_Channel1, DISABLE);//不开启通道
进入函数之前,DMA未启动或者已经启动转运了但转运次数为0,如果DMA使能后要更改转运次数,应先失能,更改转运次数后再使能,最后启动转运
调用该函数一次,设置的转运次数变为0,即转运完成
- void MyDMA_Transfer(void)
- {
- DMA_Cmd(DMA1_Channel1, DISABLE);
- DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);//设置DMAx通道y的转运次数
- DMA_Cmd(DMA1_Channel1, ENABLE);
-
- while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);//等待转运完成
- DMA_ClearFlag(DMA1_FLAG_TC1);//清除转运完成标志位
- }
- #ifndef __MYDMA_H
- #define __MYDMA_H
-
- void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size);
- void MyDMA_Transfer(void);
-
- #endif
- #include "stm32f10x.h" // Device header
-
- uint16_t MyDMA_Size;
-
- void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
- {
- MyDMA_Size = Size;
-
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
-
- DMA_InitTypeDef DMA_InitStructure;
- DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
- DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
- DMA_InitStructure.DMA_BufferSize = Size;
- DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
- DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
- DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
- DMA_Init(DMA1_Channel1, &DMA_InitStructure);
-
- DMA_Cmd(DMA1_Channel1, DISABLE);
- }
-
- void MyDMA_Transfer(void)
- {
- DMA_Cmd(DMA1_Channel1, DISABLE);
- DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);
- DMA_Cmd(DMA1_Channel1, ENABLE);
-
- while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
- DMA_ClearFlag(DMA1_FLAG_TC1);
- }
- #include "stm32f10x.h" // Device header
- #include "Delay.h"
- #include "OLED.h"
- #include "MyDMA.h"
-
- uint8_t DataA[] = {0x01, 0x02, 0x03, 0x04};
- uint8_t DataB[] = {0, 0, 0, 0};
-
- int main(void)
- {
- OLED_Init();
-
- MyDMA_Init((uint32_t)DataA, (uint32_t)DataB, 4);
-
- OLED_ShowString(1, 1, "DataA");
- OLED_ShowString(3, 1, "DataB");
- OLED_ShowHexNum(1, 8, (uint32_t)DataA, 8);
- OLED_ShowHexNum(3, 8, (uint32_t)DataB, 8);
-
- while (1)
- {
- DataA[0] ++;
- DataA[1] ++;
- DataA[2] ++;
- DataA[3] ++;
-
- OLED_ShowHexNum(2, 1, DataA[0], 2);
- OLED_ShowHexNum(2, 4, DataA[1], 2);
- OLED_ShowHexNum(2, 7, DataA[2], 2);
- OLED_ShowHexNum(2, 10, DataA[3], 2);
- OLED_ShowHexNum(4, 1, DataB[0], 2);
- OLED_ShowHexNum(4, 4, DataB[1], 2);
- OLED_ShowHexNum(4, 7, DataB[2], 2);
- OLED_ShowHexNum(4, 10, DataB[3], 2);
-
- Delay_ms(1000);
-
- MyDMA_Transfer();
-
- OLED_ShowHexNum(2, 1, DataA[0], 2);
- OLED_ShowHexNum(2, 4, DataA[1], 2);
- OLED_ShowHexNum(2, 7, DataA[2], 2);
- OLED_ShowHexNum(2, 10, DataA[3], 2);
- OLED_ShowHexNum(4, 1, DataB[0], 2);
- OLED_ShowHexNum(4, 4, DataB[1], 2);
- OLED_ShowHexNum(4, 7, DataB[2], 2);
- OLED_ShowHexNum(4, 10, DataB[3], 2);
-
- Delay_ms(1000);
- }
- }
代码参考:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。