文章目录
- DMA
- Ⅰ、DMA框图
- Ⅱ、DMA基本结构
- Ⅲ、不同外设的DMA请求
- Ⅳ、DMA函数
- Ⅴ、DMA_InitTypeDef结构体参数
- ①、DMA_PeripheralBaseAddr
- ②、DMA_PeripheralDataSize
- ③、DMA_PeripheralInc
- ④、DMA_MemoryBaseAddr
- ⑤、DMA_MemoryDataSize
- ⑥、DMA_MemoryInc
- ⑦、DMA_DIR
- ⑧、DMA_BufferSize
- ⑨、DMA_Mode
- ⑩、DMA_M2M
- ①①、DMA_Priority
- Ⅵ、DMA配置示例
- DMA+AD多通道
- 1. ADC单次扫描+DMA数据转运
- 2. ADC连续扫描+DMA循环转运
DMA
Ⅰ、DMA框图
Ⅱ、DMA基本结构
Ⅲ、不同外设的DMA请求
Ⅳ、DMA函数
// 重置指定的DMA通道寄存器为默认值
void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx);
// 初始化指定的DMA通道,根据初始化结构体配置参数
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);
// 初始化DMA初始化结构体的默认值
void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);
// 开启或关闭指定的DMA通道
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);
// 开启或关闭DMA通道的中断
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);
// 设置DMA当前数据计数器的值
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);
// 获取DMA当前数据计数器的值
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
// 获取DMA标志状态
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);
// 清除DMA标志
void DMA_ClearFlag(uint32_t DMAy_FLAG);
// 获取DMA中断状态
ITStatus DMA_GetITStatus(uint32_t DMAy_IT);
// 清除DMA中断待处理位
void DMA_ClearITPendingBit(uint32_t DMAy_IT);
Ⅴ、DMA_InitTypeDef结构体参数
①、DMA_PeripheralBaseAddr
指定DMA通道的外设基地址(外设寄存器-起始地址)
- 类型:
uint32_t
- 取值范围:任意有效的外设地址
- 作用:这是DMA操作的外设端的起始地址,例如ADC、USART等外设的数据寄存器地址
②、DMA_PeripheralDataSize
指定外设寄存器的数据宽度 (配置DMA传输时外设数据的大小)
该参数可以是
@ref DMA_peripheral_data_size
:
宏定义解释
DMA_PeripheralDataSize_Byte
- 描述:表示外设数据大小为1字节(8位)
- 每次DMA传输的数据大小为1字节
DMA_PeripheralDataSize_HalfWord
- 描述:表示外设数据大小为半字(16位)
- 每次DMA传输的数据大小为2字节
DMA_PeripheralDataSize_Word
- 描述:表示外设数据大小为1字(32位)
- 每次DMA传输的数据大小为4字节
宏函数
IS_DMA_PERIPHERAL_DATA_SIZE(SIZE)
- 描述:检查给定的外设数据大小设置是否有效
- 参数:
SIZE
,代表DMA的外设数据大小- 功能:检查
SIZE
是否等于DMA_PeripheralDataSize_Byte
、DMA_PeripheralDataSize_HalfWord
或DMA_PeripheralDataSize_Word
中的任一个。- 返回值:如果
SIZE
有效,返回1
(真),否则返回0
(假)
③、DMA_PeripheralInc
指定外设地址寄存器是否递增
- 类型:
uint32_t
- 取值范围:
DMA_PeripheralInc_Disable
:外设地址不递增DMA_PeripheralInc_Enable
:外设地址递增- 作用:控制外设地址在每次数据传输后是否递增
④、DMA_MemoryBaseAddr
指定DMA通道的内存基地址 (存储器-起始地址)
- 类型:
uint32_t
- 取值范围:任意有效的内存地址
- 作用:这是DMA操作的内存端的起始地址,例如一个缓冲区的起始地址
⑤、DMA_MemoryDataSize
指定内存数据宽度(存储器-数据宽度)
- 类型:
uint32_t
- 取值范围:
DMA_MemoryDataSize_Byte
:数据宽度为8位DMA_MemoryDataSize_HalfWord
:数据宽度为16位DMA_MemoryDataSize_Word
:数据宽度为32位- 作用:确定内存数据的大小
⑥、DMA_MemoryInc
指定内存地址寄存器是否递增(指定存储器-是否递增)
- 类型:
uint32_t
- 取值范围:
DMA_MemoryInc_Disable
:内存地址不递增DMA_MemoryInc_Enable
:内存地址递增- 作用:控制内存地址在每次数据传输后是否递增
⑦、DMA_DIR
指定数据传输方向,即外设是数据源还是数据目标(指定外设站点是源还是目标)
- 类型:
uint32_t
- 取值范围:
DMA_DIR_PeripheralSRC
:外设是数据源,内存是数据目标DMA_DIR_PeripheralDST
:外设是数据目标,内存是数据源- 作用:确定数据传输的方向
⑧、DMA_BufferSize
指定DMA通道的缓冲区大小,以数据单元为单位
DMA_InitStruct.DMA_BufferSize = Size;
传递Size个大小为<数据宽度>的数据
- 类型:
uint32_t
- 取值范围:0到65535
- 作用:确定要传输的数据量。数据单元的大小取决于
DMA_PeripheralDataSize
或DMA_MemoryDataSize
的配置,具体取决于传输方向
⑨、DMA_Mode
指定DMAy通道的工作模式(是否要自动重装)
- 类型:
uint32_t
- 取值范围:
DMA_Mode_Normal
:正常模式,传输完成后停止DMA_Mode_Circular
:循环模式(自动重装),传输完成后从头开始- 作用:确定DMA传输的模式。循环模式不适用于内存到内存的传输
⑩、DMA_M2M
是否使用软件触发(存储器到存储器)
- 类型:
uint32_t
- 描述:指定DMA通道是否用于内存到内存的传输
- 取值范围:
DMA_M2M_Disable
:不使用内存到内存传输DMA_M2M_Enable
:使用内存到内存传输- 作用:确定DMA通道是否用于内存到内存的传输
①①、DMA_Priority
指定DMA通道的软件优先级
按需求配置
- 类型:
uint32_t
- 取值范围:
DMA_Priority_Low
:低优先级DMA_Priority_Medium
:中优先级DMA_Priority_High
:高优先级DMA_Priority_VeryHigh
:非常高优先级- 作用:确定DMA通道的优先级,影响多个DMA请求的处理顺序
Ⅵ、DMA配置示例
#include "stm32f10x.h" // Device header
uint32_t Temp_Size;//存储数据计数器的值
void MyDMA_Init(uint32_t Source, uint32_t Dest, uint32_t Size)//参数需为两个数组的数组名
{
Temp_Size = Size;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitTypeDef DMA_InitStruct;
DMA_InitStruct.DMA_PeripheralBaseAddr = Source;//外设寄存器-起始地址
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设寄存器-数据宽度(此处为1byte)
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Enable;//指定外设地址寄存器-是否递增
DMA_InitStruct.DMA_MemoryBaseAddr = Dest;//存储器-起始地址
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//存储器-数据宽度
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;//指定存储器-是否递增
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;//指定外设站点是源还是目标
DMA_InitStruct.DMA_BufferSize = Size;//传递Size个大小为<数据宽度>的数据
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;//指定DMAy通道的工作模式(是否要自动重装)
DMA_InitStruct.DMA_M2M = DMA_M2M_Enable;//是否使用软件触发(存储器到存储器)
DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;//指定DMAy通道的软件优先级
DMA_Init(DMA1_Channel1, &DMA_InitStruct);
DMA_Cmd(DMA1_Channel1, DISABLE);//使能或失能DMA
}
void MyDMA_Transfer(void)//调用函数则开始转运
{
DMA_Cmd(DMA1_Channel1, DISABLE);//失能DMA
DMA_SetCurrDataCounter(DMA1_Channel1, Temp_Size);//设置DMA当前数据计数器的值
DMA_Cmd(DMA1_Channel1, ENABLE);//使能DMA
while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET)//获取DMA标志状态(等待转运完成)
DMA_ClearFlag(DMA1_FLAG_TC1);//清除DMA标志位
}
DMA+AD多通道
1. ADC单次扫描+DMA数据转运
#include "stm32f10x.h" // Device header
//模拟量转换为数字量
//AD多通道(ADC扫描模式+DMA数据转运)
uint16_t Dest_Data[4];
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//开启ADC时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//开启DMA时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//配置ADC时钟(72MHz/6=12MHz)
//配置GPIO
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_41Cycles5);//配置ADC常规通道
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_41Cycles5);//配置ADC常规通道
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_41Cycles5);//配置ADC常规通道
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_41Cycles5);//配置ADC常规通道
//配置ADC
ADC_InitTypeDef ADC_InitStruct;
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;//将ADC配置为独立或操作双模式
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不使用外部触发
ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;//单次转换or连续转换
ADC_InitStruct.ADC_ScanConvMode = ENABLE;//非扫描模式or扫描模式
ADC_InitStruct.ADC_NbrOfChannel = 4;//指定要转换的ADC通道的数量
ADC_Init(ADC1, &ADC_InitStruct);
//配置DMA
DMA_InitTypeDef DMA_InitStruct;
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;//外设寄存器-起始地址
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设寄存器-数据宽度(此处为1byte)
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//指定外设地址寄存器-是否递增
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)Dest_Data;//存储器-起始地址
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//存储器-数据宽度
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;//指定存储器-是否递增
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;//指定外设站点是源还是目标
DMA_InitStruct.DMA_BufferSize = 4;//传递Size个大小为<数据宽度>的数据
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;//指定DMAy通道的工作模式(是否要自动重装)
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;//是否使用软件触发(存储器到存储器)
DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;//指定DMAy通道的软件优先级
DMA_Init(DMA1_Channel1, &DMA_InitStruct);
ADC_DMACmd(ADC1, ENABLE);//开启ADC的DMA功能
DMA_Cmd(DMA1_Channel1, ENABLE);//使能或失能DMA
ADC_Cmd(ADC1, ENABLE);//开启ADC
//ADC复位+校准
ADC_ResetCalibration(ADC1);//重置ADC校准,准备进行校准操作
while(ADC_GetResetCalibrationStatus(ADC1) == SET);//获取ADC重置校准状态,检查是否完成
ADC_StartCalibration(ADC1);//开始ADC校准
while(ADC_GetCalibrationStatus(ADC1) == SET);//获取ADC校准状态,检查是否完成
}
void AD_GetValue(void)//获取模拟值
{
//选择不同的通道或取ADC值
DMA_Cmd(DMA1_Channel1, DISABLE);//失能DMA
DMA_SetCurrDataCounter(DMA1_Channel1, 4);//设置DMA当前数据计数器的值
DMA_Cmd(DMA1_Channel1, ENABLE);//使能DMA
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//开启软件启动ADC转换,用于软件触发转换--启动
while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET)//获取DMA标志状态(等待转运完成)
DMA_ClearFlag(DMA1_FLAG_TC1);//清除DMA标志位
}
2. ADC连续扫描+DMA循环转运
#include "stm32f10x.h" // Device header
//模拟量转换为数字量
//AD多通道(ADC连续扫描+DMA循环转运)
uint16_t Dest_Data[4];
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//开启GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//开启ADC时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//开启DMA时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//配置ADC时钟(72MHz/6=12MHz)
//配置GPIO
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_41Cycles5);//配置ADC常规通道
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_41Cycles5);//配置ADC常规通道
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_41Cycles5);//配置ADC常规通道
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_41Cycles5);//配置ADC常规通道
//配置ADC
ADC_InitTypeDef ADC_InitStruct;
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;//将ADC配置为独立或操作双模式
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;//数据右对齐
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//不使用外部触发
ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;//单次转换or连续转换**********************************************
ADC_InitStruct.ADC_ScanConvMode = ENABLE;//非扫描模式or扫描模式
ADC_InitStruct.ADC_NbrOfChannel = 4;//指定要转换的ADC通道的数量
ADC_Init(ADC1, &ADC_InitStruct);
//配置DMA
DMA_InitTypeDef DMA_InitStruct;
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;//外设寄存器-起始地址
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设寄存器-数据宽度(此处为1byte)
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//指定外设地址寄存器-是否递增
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)Dest_Data;//存储器-起始地址
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//存储器-数据宽度
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;//指定存储器-是否递增
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;//指定外设站点是源还是目标
DMA_InitStruct.DMA_BufferSize = 4;//传递Size个大小为<数据宽度>的数据
DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;//指定DMAy通道的工作模式(是否要自动重装)******************************
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;//是否使用软件触发(存储器到存储器)
DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;//指定DMAy通道的软件优先级
DMA_Init(DMA1_Channel1, &DMA_InitStruct);
ADC_DMACmd(ADC1, ENABLE);//开启ADC的DMA功能
DMA_Cmd(DMA1_Channel1, ENABLE);//使能或失能DMA
ADC_Cmd(ADC1, ENABLE);//开启ADC
//ADC复位+校准
ADC_ResetCalibration(ADC1);//重置ADC校准,准备进行校准操作
while(ADC_GetResetCalibrationStatus(ADC1) == SET);//获取ADC重置校准状态,检查是否完成
ADC_StartCalibration(ADC1);//开始ADC校准
while(ADC_GetCalibrationStatus(ADC1) == SET);//获取ADC校准状态,检查是否完成
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//开启软件启动ADC转换,用于软件触发转换--启动********************************
}
//*****************************************************************************************