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);
		
	}	
}
相关推荐
悠哉悠哉愿意8 小时前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
Lester_110110 小时前
STM32霍尔传感器输入口设置为复用功能输入口时,还能用GPIO函数直接读取IO的状态吗
stm32·单片机·嵌入式硬件·电机控制
LCG元10 小时前
低功耗显示方案:STM32L0驱动OLED,动态波形绘制与优化
stm32·嵌入式硬件·信息可视化
三佛科技-1873661339710 小时前
120W小体积碳化硅电源方案(LP8841SC极简方案12V10A/24V5A输出)
单片机·嵌入式硬件
z203483152010 小时前
STM32F103系列单片机定时器介绍(二)
stm32·单片机·嵌入式硬件
古译汉书12 小时前
【IoT死磕系列】Day 7:只传8字节怎么控机械臂?学习工业控制 CANopen 的“对象字典”(附企业级源码)
数据结构·stm32·物联网·http
Alaso_shuang13 小时前
STM32 核心输入、输出模式
stm32·单片机·嵌入式硬件
脚后跟13 小时前
AI助力嵌入式物联网项目全栈开发
嵌入式硬件·物联网·ai编程
2501_9181269114 小时前
stm32死锁是怎么实现的
stm32·单片机·嵌入式硬件·学习·个人开发
z203483152014 小时前
STM32F103系列单片机定时器介绍(一)
stm32·单片机