Appearance
DMA
Direct Memory Access直接存储器访问, 主要的功能是数据的搬运, 不占用CPU, 通过硬件为RAM和IO开辟一条通道
DMA1: 有七个通道, 可以实现P->M, M->P, M->M(M是内存, P是外设)
DMA2: 五个通道, 只存在于大容量或者互联型
STM实现
通过总线矩阵访问外设或者SRAM, 不通过CPU
不同的外设都有自己对应的特殊的外设
仲裁器可以控制有多个响应的时候优先处理哪一个
- 实现过程
每个通道用来管理来自于一个或多个外设对存储器访问的请求。且都有一个仲裁器,用于处理DMA请求间的优先级。
在使用M->M的时候所有的通道都可以使用
在实际使用的时候涉及优先级, 可以使用软件控制DMA_CCRx的PL, 之后还有硬件优先级, DMA1优先级高于DMA2
寄存器
- DMA_ISR: 中断状态寄存器
- DMA_IFCR: 中断标志位清除寄存器
- DMA_CCRx: 通道x配置寄存器
- DMA_CNDTRx: 传输数量寄存器, 在DMA关闭的时候可以写入
数据传输结束后,寄存器的内容或者变为0;或者当该通道配置为自动重加载模式时,寄存 器的内容将被自动重新加载为之前配置时的数值, 寄存器的数字为0的时候停止传输
- DMA_CPARx: DMA通道x外设地址寄存器, 数据的来源
- DMA_CMARx: DMA通道x存储器地址寄存器, 数据的目标
固件库
c
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; /*!< Specifies the peripheral base address for DMAy Channelx. 外设的地址*/
uint32_t DMA_MemoryBaseAddr; /*!< Specifies the memory base address for DMAy Channelx. 目标的地址*/
uint32_t DMA_DIR; /*!< Specifies if the peripheral is the source or destination.
This parameter can be a value of @ref DMA_data_transfer_direction传输的方向
实际的参数
#define DMA_DIR_PeripheralDST ((uint32_t)0x00000010), Periphra作为目的地
#define DMA_DIR_PeripheralSRC ((uint32_t)0x00000000), Periphra作为初始地址*/
uint32_t DMA_BufferSize; /*!< Specifies the buffer size, in data unit, of the specified Channel.
The data unit is equal to the configuration set in DMA_PeripheralDataSize
or DMA_MemoryDataSize members depending in the transfer direction. 数据处传 输的位数*/
uint32_t DMA_PeripheralInc; /*!< Specifies whether the Peripheral address register is incremented or not.
This parameter can be a value of @ref DMA_peripheral_incremented_mode
传输的时候外设地址是否增量*/
uint32_t DMA_MemoryInc; /*!< Specifies whether the memory address register is incremented or not.
This parameter can be a value of @ref DMA_memory_incremented_mode
传输的时候存储器的地址是否增加*/
uint32_t DMA_PeripheralDataSize; /*!< Specifies the Peripheral data width.
This parameter can be a value of @ref DMA_peripheral_data_size
外设的数据宽度*/
uint32_t DMA_MemoryDataSize; /*!< Specifies the Memory data width.
This parameter can be a value of @ref DMA_memory_data_size
目的地的传输的宽度*/
uint32_t DMA_Mode; /*!< Specifies the operation mode of the DMAy Channelx.
This parameter can be a value of @ref DMA_circular_normal_mode.
@note: The circular buffer mode cannot be used if the memory-to-memory
data transfer is configured on the selected Channel
传输的时候什么时候结束, 是否为循环操作*/
uint32_t DMA_Priority; /*!< Specifies the software priority for the DMAy Channelx.
This parameter can be a value of @ref DMA_priority_level 设置优先级*/
uint32_t DMA_M2M; /*!< Specifies if the DMAy Channelx will be used in memory-to-memory transfer.
This parameter can be a value of @ref DMA_memory_to_memory
设置是否为内存之间转移*/
}DMA_InitTypeDef;
数据的宽度不相同的时候, 源比较大, 其他的直接丢弃, 源比较小, 剩余的位置设置为空
实际使用
实现地址之间的传输
c
#include "bsp_dma.h"
//原始数据
const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= {
0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,
0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80};
//目标
uint32_t aDST_Buffer[BUFFER_SIZE];
void DMA_MtoM_Config(void)
{
//初始化时钟
RCC_AHBPeriphClockCmd(MTM_DMA_CLK, ENABLE);
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer; //源地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)aDST_Buffer; //目标地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable; //递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //设置为递增
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; //设置长度
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //设置不循环
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //设置优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
DMA_Init(MTM_DMA_CHANNEL, &DMA_InitStructure);
//清除标志位
DMA_ClearFlag(DMA1_FLAG_TC6);
//使能
DMA_Cmd(MTM_DMA_CHANNEL, ENABLE);
}
uint8_t Buffercmp(const uint32_t* pBuffer,
uint32_t* pBuffer1, uint16_t BufferLength)
{
/* 数据长度递减 */
while(BufferLength--)
{
/* 判断两个数据源是否对应相等 */
if(*pBuffer != *pBuffer1)
{
/* 对应数据源不相等马上退出函数,并返回0 */
return 0;
}
/* 递增两个数据源的地址指针 */
pBuffer++;
pBuffer1++;
}
/* 完成判断并且对应数据相对 */
return 1;
}
c
LED_GPIO_Config();
LED_R(OFF)
LED_B(OFF)
LED_G(OFF)
uint8_t status = 0;
DMA_MtoM_Config();
//检测是否完成
while(DMA_GetFlagStatus(DMA1_FLAG_TC6)==RESET);
status = Buffercmp(aSRC_Const_Buffer, aDST_Buffer, BUFFER_SIZE);
if(status == 0)
{
LED_R(ON)
}else
LED_G(ON)
实现串口的传输
- 实现初始化
- 串口发出请求
c
LED_GPIO_Config();
LED_R(OFF)
LED_B(OFF)
LED_G(OFF)
LED_B(ON)
USART_Config();
//初始化数组
for(uint16_t i=0; i<SENDBUFF_SIZE;i++)
{
SendBuff[i] = 'a';
}
DMA_USART_Config();
//发送信号
USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE);
while(1){
LED_G_TOGGLE
for(uint16_t i = 0; i< 0xfff;i++)
for(uint16_t b = 0; b< 0xff;b++);
};
c
void DMA_USART_Config(void)
{
//初始化时钟
RCC_AHBPeriphClockCmd(USART_TX_DMA_CLK, ENABLE);
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS; //外设地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SendBuff; //内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //不增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);
DMA_ClearFlag(USART_TX_DMA_FLAG_TCL);
//使能
DMA_Cmd(USART_TX_DMA_CHANNEL, ENABLE);
}