注:DMA对应的库函数文件讲解
DMA_GetITStatus(uint32_t DMAy_IT)
是一个用于检查DMA(直接存储器访问)中断状态的库函数。它通常在使用STM32系列微控制器及其标准外设库时被调用。此函数的主要作用是返回指定DMA通道的特定中断标志的状态,以帮助开发者确定是否发生了特定类型的DMA事件。
参数 uint32_t DMAy_IT
用来指定你想要检查的DMA中断类型和对应的DMA流或通道。这个参数是一个组合值,通常由两个部分组成:
- DMAx:指明哪个DMA控制器(例如DMA1或DMA2),因为一些STM32芯片可能有多个DMA控制器。
- IT(Interrupt Type):指明具体的中断类型,比如传输完成(Transfer Complete, TC)、半传输完成(Half Transfer, HT)、传输错误(Transfer Error, TE)等。
函数会返回一个位标志,表明所选中断状态是设置(即事件发生)还是清除(即事件未发生)。这对于编写中断服务程序(ISR)非常重要,因为在ISR中你需要知道是什么类型的事件触发了中断,以便可以适当地处理它。
例如,如果你正在等待DMA传输完成中断,你可以使用 DMA_GetITStatus
来检查该中断是否已经发生。如果函数返回值表示中断已被设置,那么你可以安全地假设DMA传输已完成,并且可以继续执行后续的操作,如启动新的DMA传输、处理接收到的数据等。
DMA_ClearITPendingBit(uint32_t DMAy_IT)
是一个用于清除DMA(直接存储器访问)中断挂起位的库函数。在STM32系列微控制器中,当DMA传输过程中发生特定事件(如传输完成、半传输完成或传输错误),DMA硬件会设置相应的中断标志位,并可能触发中断请求。
然而,一旦这些事件被软件处理后,就需要清除相应的中断标志位,以确保相同的中断不会再次被误触发。这就是 DMA_ClearITPendingBit
函数的作用:它允许你手动清除指定DMA通道的特定中断挂起位,表明该事件已经被处理完毕。
参数 uint32_t DMAy_IT
用来指定要清除的DMA中断类型和对应的DMA流或通道。这个参数是一个组合值,通常由两个部分组成:
- DMAx:指明哪个DMA控制器(例如DMA1或DMA2)。
- IT(Interrupt Type):指明具体的中断类型,比如传输完成(Transfer Complete, TC)、半传输完成(Half Transfer, HT)、传输错误(Transfer Error, TE)等。
使用此函数是确保DMA中断系统正确工作的关键步骤之一。如果不清除这些标志位,可能会导致中断不断重复触发,或者新的相同类型的中断无法被正确识别。因此,在你的中断服务程序(ISR)中,你应该在检查并响应了某个DMA事件之后调用 DMA_ClearITPendingBit
来清除对应的中断挂起位。
定义:
DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源 12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道) 每个通道都支持软件触发和特定的硬件触发 STM32F103C8T6 DMA资源:DMA1(7个通道)
SRAM可以读也可以写
DMA基本结构
1.0 数据宽度与对齐
在DMA中是如何解决数据宽度不一致的问题的,如果源端的数据大于目标端的数据,那么将读取出来的高位舍弃掉,然后只取低位。
2.0 ADC与DMA数据转运
连续扫描转运模式
3.0 手册解读
4.0 程序实现
注:本次程序主要实现的是DMA从存储器到存储器的数据转运,使用软件触发的方式
4.0.1 DMA初始化
初始化:主要包括RCC时钟初始化,GPIO初始化
函数初始化实现
#include "stm32f10x.h" // Device headeruint16_t MyDMA_Size;/*** @brief DMA初始化,包括时钟等* @param ADDRA起始地址* @param ADDRB结束地址* @param SIZE大小* @return 无返回值*/
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{MyDMA_Size = Size;// RCC时钟初始化RCC_APB1PeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);// DMA 结构体初始化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_PeripheralDataSize_Byte;// 地址是否自增DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;// 数据的传输方向,外设传输到存储器DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;// 重装计数器的大小DMA_InitStructure.DMA_BufferSize = Size;// DMA模式DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 触发方式DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;// DMA优先级,通道的优先级设置为中等DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;// 初始化DMADMA_Init(DMA1_Channel1, &DMA_InitStructure);// 使能DMA,初始化后会立即工作,等后续手动调用后再开始DMA_Cmd(DMA1_Channel1, DISABLE);
}
4.0.2 数据转运函数
注:该函数的主要作用是,从新设置传输计数器的值
/*** @brief DMA数据传输,重置* @param MULL* @param MULL* @param SIZE大小* @return 无返回值*/
void MyDMA_Transfer(void)
{// DMA失能,在写入传输计数器之前,需要DMA暂停工作DMA_Cmd(DMA1_Channel1, DISABLE);// 写入传输计数器,指定将要转运的次数DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);// 使能DMADMA_Cmd(DMA1_Channel1, ENABLE);// 等待DMA工作完成,工作完成标志位设置为1while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);// 清除工作完成标志位DMA_ClearFlag(DMA1_FLAG_TC1);
}
4.0.3 全部程序
#include "stm32f10x.h" // Device headeruint16_t MyDMA_Size;/*** @brief DMA初始化,包括时钟等* @param ADDRA起始地址* @param ADDRB结束地址* @param SIZE大小* @return 无返回值*/
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{MyDMA_Size = Size;// RCC时钟初始化RCC_APB1PeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);// DMA 结构体初始化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_PeripheralDataSize_Byte;// 地址是否自增DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;// 数据的传输方向,外设传输到存储器DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;// 重装计数器的大小DMA_InitStructure.DMA_BufferSize = Size;// DMA模式DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 触发方式DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;// DMA优先级,通道的优先级设置为中等DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;// 初始化DMADMA_Init(DMA1_Channel1, &DMA_InitStructure);// 使能DMA,初始化后会立即工作,等后续手动调用后再开始DMA_Cmd(DMA1_Channel1, DISABLE);
}/*** @brief DMA数据传输,重置* @param MULL* @param MULL* @param SIZE大小* @return 无返回值*/
void MyDMA_Transfer(void)
{// DMA失能,在写入传输计数器之前,需要DMA暂停工作DMA_Cmd(DMA1_Channel1, DISABLE);// 写入传输计数器,指定将要转运的次数DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);// 使能DMADMA_Cmd(DMA1_Channel1, ENABLE);// 等待DMA工作完成,工作完成标志位设置为1while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);// 清除工作完成标志位DMA_ClearFlag(DMA1_FLAG_TC1);
}
4.0.4 头文件
#ifndef __MYDMA_H
#define __MYDMA_Hvoid MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size);
void MyDMA_Transfer(void);#endif
4.0.5 main函数文件
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"uint8_t DataA[] = {0x01, 0x02, 0x03, 0x04}; // 定义测试数组DataA,为数据源
uint8_t DataB[] = {0, 0, 0, 0}; // 定义测试数组DataB,为数据目的地int main(void)
{/*模块初始化*/OLED_Init(); // OLED初始化MyDMA_Init((uint32_t)DataA, (uint32_t)DataB, 4); // DMA初始化,把源数组和目的数组的地址传入/*显示静态字符串*/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]++;Delay_ms(1000); // 延时1s,观察转运前的现象MyDMA_Transfer();OLED_ShowHexNum(2, 1, DataA[0], 2); // 显示数组DataAOLED_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); // 显示数组DataBOLED_ShowHexNum(4, 4, DataB[1], 2);OLED_ShowHexNum(4, 7, DataB[2], 2);OLED_ShowHexNum(4, 10, DataB[3], 2); // 使用DMA转运数组,从DataA转运到DataBOLED_ShowHexNum(2, 1, DataA[0], 2); // 显示数组DataAOLED_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); // 显示数组DataBOLED_ShowHexNum(4, 4, DataB[1], 2);OLED_ShowHexNum(4, 7, DataB[2], 2);OLED_ShowHexNum(4, 10, DataB[3], 2);Delay_ms(1000); // 延时1s,观察转运后的现象}
}