DMA介绍
- DMA直接存储器存取
- 储存器可以是SRAM或者FLASH
- 一共有两个DMA DMA1和DMA2 DMA2只存在于大容量的单片机中
- DMA的一个通道在同一时间一般只允许一个外设进行数据的传输
DMA的使用
1.DMA进行串口数据的搬运
上面的表格阐述了DMA1的通道的使用
对串口进行数据的传输,需要使用的是DMA的通道四和通道五
首先进行初始化DMA的操作
设置宏定义
#define DMA_CHANNEL4 DMA1_Channel4
#define DMA_CHANNEL5 DMA1_Channel5 //表示的是DMA1的5通道
#define DMA_CLOCK RCC_AHBPeriph_DMA1 //DMA的时钟在AHB总线上面
//#define DMA_FLAG_TC DMA1_FLAG_TC4
#define RECEIVEBUFFER_SIZE 100
#define USART_DR_ADDRESS (USART1_BASE+0x04) //串口的基地址加上数据寄存器的偏移地址 构成了外设的地址
//同时初始化两个DMA通道,分别作为接收和发送
//首先初始化DMA的通道5 DMA的通道5对应的是串口的USART_RX
//使用串口的DMA的时候 需要启动串口DMA的接收和发送
// USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); //启DMA发送
// USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); //启DMA接收
DMA_InitTypeDef DMA_Structure;
//初始化时钟 DMA的时钟在AHB总线上面
RCC_AHBPeriphClockCmd(DMA_CLOCK,ENABLE);
//配置DMA结构体
//配置谁给谁穿 传输的方向 这里的外设是源地址 从外设读取数据到内存中
DMA_Structure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;//串口1 相对于串口1的基地址的偏移值的地址 当作外设的地址
DMA_Structure.DMA_MemoryBaseAddr = (uint32_t)ReceiveBuff; //内部的SRAM作为 储存器的地址
DMA_Structure.DMA_DIR = DMA_DIR_PeripheralSRC; // 方向:串口是外设,作为源地址,进行数据的发送,因此是SRC
//配置传送的数据大小 数量
DMA_Structure.DMA_BufferSize = RECEIVEBUFFER_SIZE ; //传输元素的数目
DMA_Structure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不增
DMA_Structure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址自增
DMA_Structure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //设置外设数据宽度 一个字节
DMA_Structure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//设置储存器数据宽度
//配置模式
DMA_Structure.DMA_Mode = DMA_Mode_Normal;//设置模式 循环模式
DMA_Structure.DMA_Priority = DMA_Priority_Medium; //通道优先级选择
DMA_Structure.DMA_M2M = DMA_M2M_Disable; //配置储存器到储存器 不使用M2M 禁止内存到内存之间传输
DMA_Init(DMA_CHANNEL5,&DMA_Structure); //初始化DMA
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE); //启DMA发送
DMA_Cmd(DMA_CHANNEL5,ENABLE); //使能DMA1的通道5
//初始化DMA的通道4作为USART_TX
//配置谁给谁穿 传输的方向 这里的外设是源地址 从外设读取数据到内存中
DMA_Structure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;//串口1 相对于串口1的基地址的偏移值的地址 当作外设的地址
DMA_Structure.DMA_MemoryBaseAddr = (uint32_t)SendBuff; //内部的SRAM作为 储存器的地址
DMA_Structure.DMA_DIR = DMA_DIR_PeripheralDST; // 方向:串口是外设,作为目标地址,进行数据的接收,因此是DST
//配置传送的数据大小 数量
DMA_Structure.DMA_BufferSize = SENDBUFFER_SIZE ; //传输元素的数目
DMA_Structure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不增
DMA_Structure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址自增
DMA_Structure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //设置外设数据宽度 一个字节
DMA_Structure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//设置储存器数据宽度
//配置模式
DMA_Structure.DMA_Mode = DMA_Mode_Normal;//设置模式 循环模式
DMA_Structure.DMA_Priority = DMA_Priority_Medium; //通道优先级选择
DMA_Structure.DMA_M2M = DMA_M2M_Disable; //配置储存器到储存器 不使用M2M 禁止内存到内存之间传输
DMA_Init(DMA_CHANNEL4,&DMA_Structure); //初始化DMA
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); //启DMA接收
DMA_Cmd(DMA_CHANNEL4,ENABLE); //使能DMA的通道4
初始化完成两个DMA的通道
在主函数中,当向发送数据的数组中进行赋值的时候,DMA就会把赋值的数发送到串口的TX 通过串口助手,进行查看
串口的中断服务函数
/*
DMA在接收数据的时候,串口接收DMA在初始化的时候就处于开启状态,一直等待数据的到来,
在软件上无需做任何事情,只要在初始化配置的时候设置好配置就可以了。等到接收到数据的时候,告诉CPU去处理即可。
当我们检测到触发了串口总线空闲中断的时候,我们就知道这一波数据传输完成了,然后我们就能得到这些数据,去进行处理即可。
这种方法是最简单的,根本不需要我们做多的处理,只需要配置好,串口就等着数据的到来,dma也是处于工作状态的,来一个数据就自动搬运一个数据。
暂时关闭串口接收DMA通道,有两个原因:1.防止后面又有数据接收到,产生干扰,因为此时的数据还未处理。2.DMA需要重新配置。
*/
void DEBUG_USART_IRQHandler(void)
{
// printf("OK\r\n");
uint16_t aa;
//检查中断是否发生
if(USART_GetITStatus(DEBUG_USARTx,USART_IT_IDLE) != RESET){
DMA_Cmd(DMA_CHANNEL5,DISABLE);//首先关闭DMA传输
aa = DMA_GetCurrDataCounter(DMA_CHANNEL5);//获取该通道剩余的信息数目
Usart_SendArray(DEBUG_USARTx,ReceiveBuff,RECEIVEBUFFER_SIZE-aa);
if(strcmp((char *)ReceiveBuff,"open")==0){
printf("OPEN\r\n");
}
DMA_SetCurrDataCounter(DMA_CHANNEL5,RECEIVEBUFFER_SIZE);
DMA_Cmd(DMA_CHANNEL5,ENABLE); //开启DMA传输
USART_ReceiveData(DEBUG_USARTx); //读取一次数据,不然会一直进中断
USART_ClearFlag(DEBUG_USARTx,USART_FLAG_IDLE); //清除串口空闲中断标志位
}
}