STM32——DMA数据转换、DMA+AD多通道(十五)

一、库函数

1、void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState)

核心功能 :配置 DMA 通道的中断使能 / 失能(开启或关闭某个 DMA 中断类型)。

2、void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber)

核心功能 :设置 DMA 通道的当前剩余传输数据个数(DMA 的核心计数器,传输 1 个数据,计数器自动减 1)。

3、uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx)

核心功能 :获取 DMA 通道的当前剩余传输数据个数(读取计数器的实时值),返回值为剩余个数。

4、FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)

核心功能 :查询 DMA 的状态标志位是否置 1(即某个事件是否发生,如传输完成、半传输、错误),返回标志位状态。

5、void DMA_ClearFlag(uint32_t DMAy_FLAG)

核心功能 :手动清除 DMA 的状态标志位(将置 1 的标志位清零)。

6、ITStatus DMA_GetITStatus(uint32_t DMAy_IT)

核心功能 :查询 DMA 的中断挂起位是否置 1(即某个中断事件是否发生,且该中断已开启),返回中断状态。

7、void DMA_ClearITPendingBit(uint32_t DMAy_IT)

核心功能 :清除 DMA 的中断挂起位(将置 1 的中断挂起位清零)。

二、数据转换

将数组A中的数据转换到数组B中

uint8_t DataA\[\] = {0x01, 0x02, 0x03, 0x04};

uint8_t DataB\[\] = {0, 0, 0, 0};

1、初始化配置
复制代码
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{
	MyDMA_Size = Size;
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	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_MemoryDataSize_Byte;
	DMA_InitStructure .DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA_InitStructure.DMA_BufferSize = Size;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
	DMA_Init(DMA1_Channel1,&DMA_InitStructure);
	
	DMA_Cmd(DMA1_Channel1,DISABLE);
}

站点地址

将要转换数组的地址和数据个数传入。

复制代码
void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)

打开DMA时钟

复制代码
 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

配置外设站点

配置外设站点地址,数据宽度:以字节传输,地址是否自增:是。

复制代码
    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_MemoryDataSize_Byte;
    DMA_InitStructure .DMA_MemoryInc = DMA_MemoryInc_Enable;

配置传输方向和数据个数

配置为从外部站点传输到存储器站点,数据个数Size。

复制代码
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = Size;

配置重装,触发、优先级

配置为不重装,软件触发,中等优先级(在这个程序中可任意配置)

复制代码
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;

关闭DMA1

复制代码
    DMA_Cmd(DMA1_Channel1,DISABLE);
2、再次启动DMA

当数组A的数据变化后,再次启动DMA转换数据到数组B。

复制代码
void MyDMA_Transfer(void)
{
	DMA_Cmd(DMA1_Channel1,DISABLE);
	DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size);
	DMA_Cmd(DMA1_Channel1,ENABLE);
	
	while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
	DMA_ClearFlag(DMA1_FLAG_TC1);
}

再次启动DMA要重装计数器,重装计数器要先关闭DMA,MyDMA_Size在前面Init 中有被执行MyDMA_Size = Size;

复制代码
    DMA_Cmd(DMA1_Channel1,DISABLE);
	DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size);
	DMA_Cmd(DMA1_Channel1,ENABLE);

等待DMA通道1转换完成标志位置1,然后手动清除标志位。

复制代码
    while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
	DMA_ClearFlag(DMA1_FLAG_TC1);
3、调用

让数组A的元素++,显示两个数组的元素值,延时一段时间,然后在显示更新后的值。

复制代码
int main(void)
{
	OLED_Init();
	
	MyDMA_Init((uint32_t)DataA,(uint32_t)DataB,4);
	
	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);
	
	OLED_ShowHexNum(2,1,DataA[0],2);
	OLED_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);
	OLED_ShowHexNum(4,4,DataB[1],2);
	OLED_ShowHexNum(4,7,DataB[2],2);
	OLED_ShowHexNum(4,10,DataB[3],2);
		
	while(1)
	{
		DataA[0] ++;
		DataA[1] ++;
		DataA[2] ++;
		DataA[3] ++;
		
		OLED_ShowHexNum(2,1,DataA[0],2);
		OLED_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);
		OLED_ShowHexNum(4,4,DataB[1],2);
		OLED_ShowHexNum(4,7,DataB[2],2);
		OLED_ShowHexNum(4,10,DataB[3],2);
		
		Delay_ms(1000);
		
		MyDMA_Transfer();
		OLED_ShowHexNum(2,1,DataA[0],2);
		OLED_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);
		OLED_ShowHexNum(4,4,DataB[1],2);
		OLED_ShowHexNum(4,7,DataB[2],2);
		OLED_ShowHexNum(4,10,DataB[3],2);

		Delay_ms(1000);
	}	
}

三、DMA+AD多通道

1、接线图
2、初始化配置

配置ADC:单ADC工作、数据右对齐、软件触发、关闭连续转换、扫描模式、扫描通道数目4

配置DMA:

外部站点:地址是ADC1的DR寄存器(ADC转化结果存放在这个寄存器)、数据带宽16位、地址不递增。

存储器站点:地址是全局数组AD_Value、数据带宽16位、地址递增。

搬运方向外设→内存、搬运次数4次、单次搬运、ADC硬件触发、DMA优先级:中等(无其他DMA时随意)

复制代码
void AD_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	
	GPIO_InitTypeDef GPIO_Initstructure;
	GPIO_Initstructure.GPIO_Mode = GPIO_Mode_AIN;//此时GPIO口只受到ADC的控制
	GPIO_Initstructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1| GPIO_Pin_2 | GPIO_Pin_3;
	GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_Initstructure);
	
	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5);
	
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;
	ADC_InitStructure.ADC_NbrOfChannel = 4;
	ADC_Init(ADC1,&ADC_InitStructure);
	
	DMA_InitTypeDef DMA_InitStructure;
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA_InitStructure.DMA_BufferSize = 4;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
	DMA_Init(DMA1_Channel1,&DMA_InitStructure);
	
	DMA_Cmd(DMA1_Channel1,ENABLE);
	
	ADC_DMACmd(ADC1, ENABLE);
	ADC_Cmd(ADC1,ENABLE);
	
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1) == SET);
	
}

打开ADC和DMA的通道

开启ADC1的DMA请求(ADC转换完成后,主动通知DMA搬运)

复制代码
ADC_DMACmd(ADC1, ENABLE);
3、再启动

需要采集数据时,在主函数中调用一次就执行一次 4 通道扫描 ,采集完成后结果自动存入AD_Value数组

复制代码
void AD_GetValue(void)
{
	DMA_Cmd(DMA1_Channel1,DISABLE);
	DMA_SetCurrDataCounter(DMA1_Channel1,4);
	DMA_Cmd(DMA1_Channel1,ENABLE);
	
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);
	while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
	DMA_ClearFlag(DMA1_FLAG_TC1);
}
4、调用
复制代码
int main(void)
{
	OLED_Init();
	AD_Init();
	
	OLED_ShowString(1,1,"AD0:");
	OLED_ShowString(2,1,"AD1:");
	OLED_ShowString(3,1,"AD2:");
	OLED_ShowString(4,1,"AD3:");
	
	while(1)
	{
		AD_GetValue();
		
		OLED_ShowNum(1,5,AD_Value[0],4);
		OLED_ShowNum(2,5,AD_Value[1],4);
		OLED_ShowNum(3,5,AD_Value[2],4);
		OLED_ShowNum(4,5,AD_Value[3],4);
		
		Delay_ms(100);
		
	}	
}
相关推荐
AlanAshley18 分钟前
电脑烧屏的软件与硬件原因全解析
单片机·嵌入式硬件·电脑
ScilogyHunter1 小时前
Hercules系列安全MCU功能安全原理详解
java·单片机·安全·hercules
szxinmai主板定制专家1 小时前
AMR 项目落地:机器人控制器硬件定制化需求复盘
arm开发·单片机·fpga开发·系统架构·机器人·am62x
笨笨饿1 小时前
#60_# 一、Photoshop 2025 如何批量导出 PSD 文件中的所有图层为独立 PNG 图片
stm32·单片机·嵌入式硬件·物联网·ui·51单片机·photoshop
碧海银沙音频科技研究院11 小时前
基于炬芯ATS308X 设计的上位机usb 串口音乐EQ调节工具
stm32·单片机·嵌入式硬件
三佛科技-1341638421214 小时前
过认证72W碳化硅方案(12V6A)主控HN1562+HN36228V 典型应用电路、变压器参数分享
单片机·嵌入式硬件·物联网·智能家居·pcb工艺
Zwawa15 小时前
串口为什么会丢字节:从轮询到中断和环形缓冲区
c语言·单片机·嵌入式
Brilliantwxx15 小时前
【STM32】 内部 Flash 详解:寄存器逐位拆解 + 工作流程顺序全讲 + EEPROM再实现
stm32·单片机·嵌入式硬件·1024程序员节
木子单片机15 小时前
基于单片机的DS18B20温度传感器测温系统设计
单片机·嵌入式硬件·51单片机·keil
华清远见IT开放实验室16 小时前
实验室建设案例 | 安徽三联学院物联网实验室
stm32·单片机·嵌入式硬件·物联网·华清远见