DMA:用于实现外设与储存器之间或者存储器与存储器之间数据传输的高效性,无需CPU直接操作
特性:12个独立可配置的通道(请求):DMA1:7个通道;DMA2:5个通道
每个通道都直接连接专用的硬件DMA请求;每个通道都同样支持软件触发;多个请求间的优先权可通过编程设置(很高;高;中等和低)优先权的设置相等时,由硬件决定(请求0优先于请求1)
在一个通道中同一时间只能产生一个外设
软件设计流程:
- 从哪里来那里出
要传多少;单位是什么。硬件阶段-通道编号。外设-存储器;存储器-外设;存储器-存储器
- 初始化DMA通道
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA1时钟使能
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; /*!< 指定DMAy通道x的外设基地址 */
uint32_t DMA_MemoryBaseAddr; /*!< 指定DMAy通道x的内存基地址 */
uint32_t DMA_DIR; /*!< 指定外设是源还是目标
此参数可以是@ref DMA_data_transfer_direction中的值 */
uint32_t DMA_BufferSize; /*!< 指定指定通道的缓冲区大小,以数据单元为单位。
数据单元等于根据传输方向在DMA_PeripheralDataSize
或DMA_MemoryDataSize成员中设置的配置。 */
uint32_t DMA_PeripheralInc; /*!< 指定外设地址寄存器是否递增。
此参数可以是@ref DMA_peripheral_incremented_mode中的值 */
uint32_t DMA_MemoryInc; /*!< 指定内存地址寄存器是否递增。
此参数可以是@ref DMA_memory_incremented_mode中的值 */
uint32_t DMA_PeripheralDataSize; /*!< 指定外设数据宽度。
此参数可以是@ref DMA_peripheral_data_size中的值 */
uint32_t DMA_MemoryDataSize; /*!< 指定内存数据宽度。
此参数可以是@ref DMA_memory_data_size中的值 */
uint32_t DMA_Mode; /*!< 指定DMAy通道x的操作模式。
此参数可以是@ref DMA_circular_normal_mode中的值。
@note: 如果在所选通道上配置了内存到内存的数据传输,
则不能使用循环缓冲区模式 */
uint32_t DMA_Priority; /*!< 指定DMAy通道x的软件优先级。
此参数可以是@ref DMA_priority_level中的值 */
uint32_t DMA_M2M; /*!< 指定DMAy通道x是否用于内存到内存传输。
此参数可以是@ref DMA_memory_to_memory中的值 */
} DMA_InitTypeDef;
- 使能外设DMA功能(DMA请求映射图对应外设)
/**
* @brief 启用或禁用指定的DMA通道
* @param DMAy_Channelx: 指向DMA通道结构体的指针
* 例如: DMA1_Channel1, DMA1_Channel2, ...
* @param NewState: 新状态
* 取值范围: ENABLE 或 DISABLE
* @retval 无
*/
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState)
/**
* @brief 设置DMA通道的当前数据传输计数器值
* @param DMAy_Channelx: 指向DMA通道结构体的指针
* 例如: DMA1_Channel1, DMA1_Channel2, ...
* @param DataNumber: 要传输的数据单元数量
* 取值范围: 0x0001 ~ 0xFFFF
* @note 此值会被加载到DMA_CNDTRx寄存器中
* 当DMA传输时,此计数器会递减至0
* @retval 无
*/
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber)
- 开启DMA的通道传输()
/**
* @brief 启用或禁用USART的DMA请求
* @param USARTx: 指向USART外设结构体的指针
* 例如: USART1, USART2, ...
* @param USART_DMAReq: 指定要配置的DMA请求
* 取值范围:
* - USART_DMAReq_Tx: 发送缓冲区空中断触发DMA请求
* - USART_DMAReq_Rx: 接收缓冲区非空中断触发DMA请求
* @param NewState: 新状态
* 取值范围: ENABLE 或 DISABLE
* @note 此函数会设置或清除USART_CR3寄存器中的DMAT/DMAR位
* @retval 无
*/
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState)
- 查询DMA传输状态
/**
* @brief 检查指定的DMA标志位状态
* @param DMAy_FLAG: 指定要检查的DMA标志位
* 取值范围:
* - DMA1_FLAG_GL1: DMA1通道1全局标志
* - DMA1_FLAG_TC1: DMA1通道1传输完成标志
* - DMA1_FLAG_HT1: DMA1通道1半传输完成标志
* - DMA1_FLAG_TE1: DMA1通道1传输错误标志
* - ... 以此类推,支持DMA1所有通道和DMA2相关标志
* @retval FlagStatus: 标志位状态
* - SET: 标志位被设置(事件发生)
* - RESET: 标志位未被设置(事件未发生)
* @note 不同DMA通道的标志位位于不同的寄存器中
* 例如: DMA1通道1-4的标志在DMA1_ISR寄存器中
*/
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)
/**
* @brief 清除指定的DMA标志位
* @param DMAy_FLAG: 指定要清除的DMA标志位
* 取值范围:
* - DMA1_FLAG_GL1: DMA1通道1全局标志
* - DMA1_FLAG_TC1: DMA1通道1传输完成标志
* - DMA1_FLAG_HT1: DMA1通道1半传输完成标志
* - DMA1_FLAG_TE1: DMA1通道1传输错误标志
* - ... 以此类推,支持DMA1所有通道和DMA2相关标志
* @retval 无
* @note 不同DMA通道的标志位清除需写入对应的DMA_IFCR寄存器
* 例如: 清除DMA1通道1的标志需写入DMA1_IFCR寄存器
*/
void DMA_ClearFlag(uint32_t DMAy_FLAG)
实际代码(DMA-USART1外设发送数据)
#include "dma.h"
/*******************************************************************************
* 函 数 名 : DMAx_Init
* 函数功能 : DMA初始化函数
* 输 入 :
DMAy_Channelx:DMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7
par:外设地址
mar:存储器地址
ndtr:数据传输量
* 输 出 : 无
*******************************************************************************/
void DMAx_Init(DMA_Channel_TypeDef* DMAy_Channelx,u32 par,u32 mar,u16 ndtr)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA1时钟使能
//DMA_DeInit(DMAy_Channelx);
/* 配置 DMA */
DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA外设地址
DMA_InitStructure.DMA_MemoryBaseAddr = mar;//DMA 存储器0地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//存储器到外设模式
DMA_InitStructure.DMA_BufferSize = ndtr;//数据传输量
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据长度:8位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器数据长度:8位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 使用普通模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输
DMA_Init(DMAy_Channelx, &DMA_InitStructure);//初始化DMA
}
/*******************************************************************************
* 函 数 名 : DMAx_Enable
* 函数功能 : 开启一次DMA传输
* 输 入 : DMAy_Channelx:DMA通道选择,@ref DMA_channel DMA_Channel_0~DMA_Channel_7
ndtr:数据传输量
* 输 出 : 无
*******************************************************************************/
void DMAx_Enable(DMA_Channel_TypeDef *DMAy_Channelx,u16 ndtr)
{
DMA_Cmd(DMAy_Channelx, DISABLE); //关闭DMA传输
DMA_SetCurrDataCounter(DMAy_Channelx,ndtr); //数据传输量
DMA_Cmd(DMAy_Channelx, ENABLE); //开启DMA传输
}
主函数代码
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart1.h"
#include "key.h"
#include "dma.h"
#define send_buf_len 5000
u8 send_buf[send_buf_len];
/*******************************************************************************
* 函 数 名 : Send_Data
* 函数功能 : 要发送的数据
* 输 入 : p:指针变量
* 输 出 : 无
*******************************************************************************/
void Send_Data(u8 *p)
{
u16 i;
for(i=0;i<send_buf_len;i++)
{
*p='5';
p++;
}
}
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
int main()
{
u8 i=0;
u8 key;
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2组
LED_Init();
USART1_Init(115200);
KEY_Init();
DMAx_Init(DMA1_Channel4,(u32)&USART1->DR,(u32)send_buf,send_buf_len);//DMA1通道4,外设USART1-TX,数组首地址,数组长度
Send_Data(send_buf);
while(1)
{
key=KEY_Scan(0);
if(key==KEY_UP_PRESS)
{
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口1的DMA发送
DMAx_Enable(DMA1_Channel4,send_buf_len); //开始一次DMA传输!
//等待DMA传输完成,此时我们来做另外一些事
//实际应用中,传输数据期间,可以执行另外的任务
while(1)
{
if(DMA_GetFlagStatus(DMA1_FLAG_TC4)!=0)//判断通道4传输完成
{
DMA_ClearFlag(DMA1_FLAG_TC4);
break;
}
LED2=!LED2;
delay_ms(300);
}
}
i++;
if(i%20==0)
{
LED1=!LED1;
}
delay_ms(10);
}
}