8 DWA(二)

文章详细介绍了如何在STM32F10x微控制器中利用DMA进行数据转运,包括DWA数据转运的基本步骤和配置,以及两种不同模式下的DMA+AD多通道传输:单次扫描+单次转运和连续扫描+循环转运。通过示例代码展示了初始化设置和传输过程。
摘要由CSDN通过智能技术生成

8.2 DWA数据转运&DWA&AD多通道

1)DWA数据转运

在这里插入图片描述

流程:

第一步:RCC开启DMA的时钟
第二步:直接调用DMA_Init,初始化参数(外设和存储器站点的起始地址、数据宽度、地址是否自增、方向、传输计数器、是否自动重装、选择触发源、通道优先级)
第三步:配置开关

主函数:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"

uint8_t DataA[] = {0x01, 0x02, 0x03, 0x04};	//源端数组
uint8_t DataB[] = {0, 0, 0, 0};				//目的数组

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);
	
	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);
	}
}

MyDMA.c

#include "stm32f10x.h"                  // Device header

uint16_t MyDMA_Size;

void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{
	MyDMA_Size = Size;
	
	//使能DMA1总线的时钟,为使用DMA通道和DMA传输做好准备
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	DMA_InitTypeDef DMA_InitStructure;
	//配置外设站点
	DMA_InitStructure.DMA_PeripheralBaseAddr = AddrA;						//外设站点的地址
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //以字节的方式传输,uint8_t
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;			//地址自增
	//配置存储器站点
	DMA_InitStructure.DMA_MemoryBaseAddr = AddrB;					//存储器站点的地址
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;	//以字节的方式传输,uint8_t
	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;				//存储器到存储器(Memory to Memory)模式:软件触发
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;	//DMA通道的传输优先级,这样选择传输优先级为中等
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);
	
	//DMA使能
	DMA_Cmd(DMA1_Channel1, DISABLE);
}

//DMA传输函数
void MyDMA_Transfer(void)
{
	//赋值前必须先让DMA失能
	DMA_Cmd(DMA1_Channel1, DISABLE);
	//给DMA重新赋值
	DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);
	//DMA使能
	DMA_Cmd(DMA1_Channel1, ENABLE);
	
	//判断转运完成标志位情况
	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
	//手动清除标志位
	DMA_ClearFlag(DMA1_FLAG_TC1);
}

2)DMA+AD多通道(ADC单次扫描+DMA单次转运的模式)
在这里插入图片描述

主函数:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"


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);
	}
}

AD.c

#include "stm32f10x.h"                  // Device header

uint16_t AD_Value[4];

void AD_Init(void)
{
	//第一步:RCC开启时钟,开启GPIO和ADC的时钟,ADCCLK分频器配置
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	//使能DMA1总线的时钟,为使用DMA通道和DMA传输做好准备
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	//第二部:配置GPIO,把需要用的GPIO口配置成输入模式
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;	//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); //ADC1上配置一个常规通道,使其可以采集ADC通道0的模拟电压值,并且使用55.5个时钟周期作为采样时间
	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转换器,用结构体来配置(AD转换器、AD数据寄存器、触发控制等)
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;					//独立模式,ADC可以单独工作,并不依赖其他STM32内部或外部的时钟或扫描机制。
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;				//采集到的ADC数据会在右对齐后被存入ADC转换缓存区中
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//ADC外部触发转换配置代码,这里表示不使用外部触发方式来启动ADC转换(这里选择软件触发)
	ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;					//ADC连续转换模式配置代码,这里表示非连续转换,即采用单次转换
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;						//ADC扫描模式配置代码,这里表示扫描模式(四个通道)
	ADC_InitStructure.ADC_NbrOfChannel = 4;								//ADC转换通道数量配置代码,使用4个ADC通道进行转换
	ADC_Init(ADC1,&ADC_InitStructure);
	
	//配置DMA
	DMA_InitTypeDef DMA_InitStructure;
	//配置外设站点
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;			//外设站点的地址
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //以半字节的方式传输,16位转运
	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;				//存储器到存储器(Memory to Memory)模式:不使用软件触发
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;	//DMA通道的传输优先级,这样选择传输优先级为中等
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);
	//DMA使能
	DMA_Cmd(DMA1_Channel1, ENABLE);
	ADC_DMACmd(ADC1, ENABLE); //开启DMA触发信号
	ADC_Cmd(ADC1, ENABLE); //调用ADC_Cmd(),开启ADC,然后进行校准
	
	ADC_ResetCalibration(ADC1);							 //在进行ADC模块校准之前,需要先进行复位操作
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);	 //ADC校准复位后,需要等待校准重置完成。使用while语句判断校准复位是否完成
	ADC_StartCalibration(ADC1);							 //校准复位完成后,开始进行ADC模块的校准
	while (ADC_GetCalibrationStatus(ADC1) == SET);		 //ADC校准开始后,需要等待校准完成。使用while语句来判断校准是否完成,完成后继续执行下面的代码。
}

//启动转换,获取结果
void AD_GetValue(void)
{
	DMA_Cmd(DMA1_Channel1, DISABLE);
	DMA_SetCurrDataCounter(DMA1_Channel1, 4);
	DMA_Cmd(DMA1_Channel1, ENABLE);
	
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//ADC转换开始前需要启动转换操作。这里使用了ADC_SoftwareStartConvCmd(ADC1, ENABLE)函数来启动ADC1的软件转换操作。
	
	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
	DMA_ClearFlag(DMA1_FLAG_TC1);
}

3)DMA+AD多通道(ADC连续扫描+DMA循环转运的模式)

主函数:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"


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);
	}
}

AD.c

#include "stm32f10x.h"                  // Device header

uint16_t AD_Value[4];

void AD_Init(void)
{
	//第一步:RCC开启时钟,开启GPIO和ADC的时钟,ADCCLK分频器配置
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);
	//使能DMA1总线的时钟,为使用DMA通道和DMA传输做好准备
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	
	//第二部:配置GPIO,把需要用的GPIO口配置成输入模式
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;	//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); //ADC1上配置一个常规通道,使其可以采集ADC通道0的模拟电压值,并且使用55.5个时钟周期作为采样时间
	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转换器,用结构体来配置(AD转换器、AD数据寄存器、触发控制等)
	ADC_InitTypeDef ADC_InitStructure;
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;					//独立模式,ADC可以单独工作,并不依赖其他STM32内部或外部的时钟或扫描机制。
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;				//采集到的ADC数据会在右对齐后被存入ADC转换缓存区中
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	//ADC外部触发转换配置代码,这里表示不使用外部触发方式来启动ADC转换(这里选择软件触发)
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;					//ADC连续转换模式配置代码,这里表示连续转换
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;						//ADC扫描模式配置代码,这里表示扫描模式(四个通道)
	ADC_InitStructure.ADC_NbrOfChannel = 4;								//ADC转换通道数量配置代码,使用4个ADC通道进行转换
	ADC_Init(ADC1,&ADC_InitStructure);
	
	//配置DMA
	DMA_InitTypeDef DMA_InitStructure;
	//配置外设站点
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;			//外设站点的地址
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //以半字节的方式传输,16位转运
	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_Circular;			//循环模式
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;				//存储器到存储器(Memory to Memory)模式:不使用软件触发
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;	//DMA通道的传输优先级,这样选择传输优先级为中等
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);
	
	DMA_Cmd(DMA1_Channel1, ENABLE); //DMA使能
	ADC_DMACmd(ADC1, ENABLE); //开启DMA触发信号
	ADC_Cmd(ADC1, ENABLE); //调用ADC_Cmd(),开启ADC,然后进行校准
	
	ADC_ResetCalibration(ADC1);							 //在进行ADC模块校准之前,需要先进行复位操作
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);	 //ADC校准复位后,需要等待校准重置完成。使用while语句判断校准复位是否完成
	ADC_StartCalibration(ADC1);							 //校准复位完成后,开始进行ADC模块的校准
	while (ADC_GetCalibrationStatus(ADC1) == SET);		 //ADC校准开始后,需要等待校准完成。使用while语句来判断校准是否完成,完成后继续执行下面的代码。
	
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//ADC转换开始前需要启动转换操作。这里使用了ADC_SoftwareStartConvCmd(ADC1, ENABLE)函数来启动ADC1的软件转换操作。
}

启动转换,获取结果
//void AD_GetValue(void)
//{
//	DMA_Cmd(DMA1_Channel1, DISABLE);
//	DMA_SetCurrDataCounter(DMA1_Channel1, 4);
//	DMA_Cmd(DMA1_Channel1, ENABLE);
//	
//	ADC_SoftwareStartConvCmd(ADC1, ENABLE);					//ADC转换开始前需要启动转换操作。这里使用了ADC_SoftwareStartConvCmd(ADC1, ENABLE)函数来启动ADC1的软件转换操作。

//	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
//	DMA_ClearFlag(DMA1_FLAG_TC1);
//}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值