08:STM32----DMA数据转运

news2024/12/24 8:57:26

目录

1:简历

2:存储器映像

3:DMA基本结构

4: DMA转运的条件 

5:DMA请求

A:DMA数据转运

1:连接图

2:数据转运+DMA

3:函数介绍

4:步骤

5:代码

 B:DMA+AD多通道

1:连接图

2:结构图

 3:函数介绍

4:代码  


1:简历

DMA(Direct Memory Access)直接存储器存取

DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源

12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)

每个通道都支持软件触发和特定的硬件触发  (每隔DMA通道的触发源不一样)

STM32F103C8T6 DMA资源:DMA1(7个通道)

外设(外设存储器)  :  一般是外设的数据寄存器DR , 比如ADC的数据寄存器、串口的数据寄存器等等

存储器  :  这里存储器,指的就是是运行内存SRAM和程序存储器Flash ,  是我们存储变量数组和程序代码的地方

外设和存储器的数据传输---------使用特定的硬件触发(每隔DMA通道的触发源不一样)

存储器和存储器之间的数据传输------软件触发

2:存储器映像

ROM :  只读存储器,是一种非易失性、掉电不丢失的存储器

RAM : 随机存储器,是一种易失性、掉电丢失的存储器

        因为CPU是32位的。所以导址范围就是32位的范围

3:DMA基本结构

下面的参数在配置DMA时使用

方向 : 决定了数据的方向    外设--->储存器 或者 储存器---->外设  或者 储存器---->储存器

        Flash是只读储存器,  所以在储存器--->储存器的传输方向的时候 :只能选择 Flash-------------->SRAM方向的传输

        X.DMA_DIR=指定外设站点(外设寄存器)为数据源还是目的地

传输计数器: 用来指定,我总共需要转运几次的,  这个传输计数器是一个自减计数器

        比如给它写一个5,   那DMA就只能进行5次数据转运 ,  转运过程中,每转运一次,计数器的数就会减1,   当传输计数器减到0之后,DMA就不会再进行数据转运了 ,  它减到0之后,之前自增的地址,也会恢复到起始地址的位置

        X.DMA_BufferSize=传输计数器

自动重装器 :  传输计数器减到0之后 , 是否要恢复到最初的值

        比如最初传输计数器给5,  如果不使用自动重装器,那转运5次后,DMA就结束了.   如果使用自动重装器,那转运5次,  计数器减到0后,就会立即重装到初始值5,  决定了转运的模式        

        不重装,就是正常的单次模式

        重装,就是循环模式

        X.DMA_Mode=自动重装器

起始地址 : 决定了数据从那里来到那里去的

因为stm32是32位的单片机,他的一个内存单元是32位的, 所以起始地址填入的都为32位

数据宽度 :  指定一次转运要按多大的数据宽度来进行

        字节Byte(uint8_t)、半字HalfWord(uint16_t)和字Word(uint32_t)

软件触发和循环模式,不能同时用

        软件触发 : 软件触发的执行逻辑是,以最快的速度,连续不断地触发DMA,  早日把传输计数器清零,完成这一轮的转换

        软件触发就是尽快把传输计数器清零 .  循环模式是清零后自动重装 ,  如果同时用的话,那DMA就停不下来了

4: DMA转运的条件 

1: 开关控制,DMA _Cmd函数必须使能

2: 传输计数器必须大于0

3: 触发源,必须有触发信号

        触发一次,转运一次,传输计数器自减一次 ,  当传输计数器等于0,且没有自动重装时 ,   无论是否触发,DMA都不会再进行转运了,   此时就需要DMA_Cmd函数,给DISABLE,关闭DMA.   再为传输计数器写入一个大于0的数,  再DMA_Cmd,给ENABLE,开启DMA才可以正常工作.

        注意,写传输计数器时,必须要先关闭DMA,再进行,   不能在DMA开启时,写传输计数器

5:DMA请求

特定的硬件触发 : 每个通道的硬件触发源都是不同的 , 需要用ADC1来触发的话一那就必须选择通道1;   定时器2的更新事件来触发的话,那就必须选择通道2

        因为每个通道的硬件触发源都不同 ,  如果你想使用某个硬件触发源的话,  就必须使用它所在的通道

软件触发 :  使用软件触发的话,那通道就可以任意选择了.  每个通道的软件触发都是一样的

A:DMA数据转运

1:连接图

2:数据转运+DMA

3:函数介绍

 在stm32f10x dmah文件中配置----MDA初始化

void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);

 在stm32f10x dmah文件中配置----开启MDA

void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);

 在stm32f10x dmah文件中配置----数据寄存器设置

void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber); 


uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);

DMA_SetCurrDataCounter : 设置当前数据寄存器 ,  就是给这个传输计数器写数据的,    和配置DMA中X.DMA_Buffersize参数相似

DMA_GetCurrDataCounter :  获取当前数据寄存器,  这个函数就是返回传输计数器的值 ,     在转运完成后把标志位值1

 在stm32f10x dmah文件中配置----清除中断标志位

void DMA_ClearFlag(uint32_t DMAy_FLAG)

DMA_ClearFlag : 清除中断标志位

4:步骤

1:开启RCC时钟-----DMA的时钟在AHB总线上

2:配置DMA

5:代码

使用的是存储器和存储器之间的数据传输------软件触发



#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"
uint16_t MyDMA_Size;
void MyMDA_init(uint32_t AddrA, uint32_t AddrB, uint16_t Size){
	
	MyDMA_Size=Size;
	//开启RCC--DMA是AHB总线的设备 
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	/*
	我们使用的STM32型号为: STMF103
	参数1的第一个选择  : 互连型设备--互联型是STM23F  105/107的型号
	参数1的第一个选择  : 其他设备---103选择这个
	*/
	
	
	//配置DMA
	DMA_InitTypeDef DMA_initstruct;
	//外设备站点的3个参数
    //因为stm32是32位的单片机,他的一个内存单元是32位的
	DMA_initstruct.DMA_PeripheralBaseAddr=AddrA; //外设的起始地址--要求:32位
	DMA_initstruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;外设的数据宽度---我们选择以字节的方式传输(uint8_t)
	DMA_initstruct.DMA_PeripheralInc=DMA_PeripheralInc_Enable;//外设的地址是否自增--自增
	//储存器的3个参数
	DMA_initstruct.DMA_MemoryBaseAddr=AddrB;//储存器的起始地址--要求:32位
	DMA_initstruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;//储存器的数据宽度--我们选择以字节的方式传输(uint8_t)
	DMA_initstruct.DMA_MemoryInc=DMA_MemoryInc_Enable;//储存器的地址是否自增--自增
	//传输方向
	DMA_initstruct.DMA_DIR=DMA_DIR_PeripheralSRC;//指定外设站点(外设寄存器)为数据源还是目的地---数据源
	//缓冲区大小---传输计数器
	DMA_initstruct.DMA_BufferSize=Size;//传输几次
	//传输模式----是否使用自动重装
	DMA_initstruct.DMA_Mode=DMA_Mode_Normal;//正常模式 //传输计数器到0直接停止
	//选择触发模式---硬件触发或者软件触发
	DMA_initstruct.DMA_M2M=DMA_M2M_Enable;  //使用软件触发
	//优先级
	DMA_initstruct.DMA_Priority=DMA_Priority_Medium;
	DMA_Init(DMA1_Channel1,&DMA_initstruct);
	
	
	//开启MDA
	DMA_Cmd(DMA1_Channel1,DISABLE);
	
}


void MyDMA_Transfer(void)
{
	//需要给我传输寄存器重新赋值,首先要使CMD失能
	
	DMA_Cmd(DMA1_Channel1, DISABLE);
	/*
	设置当前数据寄存器 ,  就是给这个传输计数器写数据的
	和配置DMA中X.DMA_BufferSize参数相似
	*/
	DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);
	DMA_Cmd(DMA1_Channel1, ENABLE);
	
	//DMA1_FLAG_TC1---转运完成标志位   转运完成后置1
	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET); 
	DMA_ClearFlag(DMA1_FLAG_TC1);//清除转运完成标志位
}


uint8_t dataA[]={0x01,0x02,0x03,0x04};
uint8_t dataB[]={0,0,0,0};
int main(void)
{
	
	OLED_Init();
	MyMDA_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);	
		
	}
}

开启RRC时钟 

        开启RCC--DMA是AHB总线的设备

        RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
    /*
    我们使用的STM32型号为: STMF103
    参数1的第一个选择  : 互连型设备--互联型是STM23F  105/107的型号
    参数1的第一个选择  : 其他设备---103选择这个
    */

字节问题

        因为要参数的数据是dataA和dataB都是uint8_t的大小,  所以在配置DMA时宽度的参数(X.DMA_PeripheralDataSize)选择DMA_PeripheralDataSize_Byte(uint8_t)

        在配置DMA起始地址时选uint32_t:  因为stm32是32位的单片机,他的一个内存单元是32位的

 B:DMA+AD多通道

1:连接图

和07:STM32----ADC模数转化器 --- B:AD多通道一致

2:结构图

 3:函数介绍

在文件stm32f10x_adc.c中-----开启ADC到DMA的输出

void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState)

4:代码  

        在07:STM32----ADC模数转化器中    我们使用了没有使用扫描模式,  是由于ADC的数据覆盖问题,在下面的代码中我们使用DMA进行数据转运,    所以在ADC使用扫描模式的情况下必须使用DMA进行数据转运

        ADC单次扫描+DMA单次转运的模式

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
//ADC单次扫描+DMA单次转运的模式
uint16_t AD_Value[4];

void AD_init(void){
	//RCC开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	/*
	我们使用的STM32型号为: STMF103
	参数1的第一个选择  : 互连型设备--互联型是STM23F  105/107的型号
	参数1的第一个选择  : 其他设备---103选择这个
	*/
	

	
	//配置ADCCLK
	//APB2时钟72MHz时钟信号然后通过ADC预分频器进行分频,得到ADCCLK钟信号
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72Mhz/6=12Mhz
	
	//配置GPIO
	GPIO_InitTypeDef GPIO_initstruct;
	GPIO_initstruct.GPIO_Mode=GPIO_Mode_AIN;  //模拟输入,可以理解为ADC的专属模式
	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_Channel_0  --通道o
	1----1~16的范围规则组第几个序列
	ADC_SampleTime_55Cycles5-----指定通道的采样时间
	*/
	//选择AD转化器----我们选择规则组的输入通道 因为启动了应该组,所以是下面改为扫描模式
	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
	ADC_InitTypeDef ADC_initstruct;
	ADC_initstruct.ADC_ContinuousConvMode=DISABLE;//选择是连续转换还是单次转换---单次
	ADC_initstruct.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐---右对齐
	ADC_initstruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//触发控制的触发源---不使用外部触发,使用内部软件触发
	ADC_initstruct.ADC_Mode=ADC_Mode_Independent;//ADC的工作模式---独立模式
	ADC_initstruct.ADC_NbrOfChannel=4;  //通道数目--指定在扫描模式下,总共会用到几个通道
	ADC_initstruct.ADC_ScanConvMode=ENABLE;//可以选择是扫描模式还是非扫描模式---扫描模式
	ADC_Init(ADC1,&ADC_initstruct);
	//ADC在完成后面数据写在DR寄存器中
	



	//配置DMA
	//外设备站点的3个参数
	//因为stm32是32位的单片机,他的一个内存单元是32位的
	DMA_InitTypeDef DMA_initstruct;
	DMA_initstruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR; //外设的起始地址--要求:32位
	DMA_initstruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;外设的数据宽度---我们选择以字节的方式传输(uint16_t)
	DMA_initstruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设的地址是否自增--不自增
//	//储存器的3个参数
	DMA_initstruct.DMA_MemoryBaseAddr=(uint32_t)AD_Value;//储存器的起始地址--要求:32位
	DMA_initstruct.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//储存器的数据宽度--我们选择以字节的方式传输(uint16_t)
	DMA_initstruct.DMA_MemoryInc=DMA_MemoryInc_Enable;//储存器的地址是否自增--自增
	//传输方向
	
	DMA_initstruct.DMA_DIR=DMA_DIR_PeripheralSRC;//指定外设站点(外设寄存器)为数据源还是目的地---数据源
	//缓冲区大小---传输计数器
	DMA_initstruct.DMA_BufferSize=4;//传输几次
	//传输模式----是否使用自动重装
	DMA_initstruct.DMA_Mode=DMA_Mode_Normal;//普通模式    //传输计数器到0直接停止  //DMA_Mode_Circular--循环模式   DMA_Mode_Normal
	//选择触发模式---硬件触发或者软件触发
	DMA_initstruct.DMA_M2M=DMA_M2M_Disable;  //使用硬件触发
	//优先级
	DMA_initstruct.DMA_Priority=DMA_Priority_Medium;
	DMA_Init(DMA1_Channel1,&DMA_initstruct);
	

	//开启MDA
	DMA_Cmd(DMA1_Channel1,ENABLE);
	//开启ADC到DMA的输出
	
	ADC_DMACmd(ADC1,ENABLE);
	
	//开启ADC
	ADC_Cmd(ADC1,ENABLE);
	
	//校准ADC
	
	//复位校准
	ADC_ResetCalibration(ADC1);//---把CR2_RSTCAL_Set这一位置一
	//等待复位校准完成--ADC_GetResetCalibrationStatus作用:返回复位校准的状态
	while (ADC_GetResetCalibrationStatus(ADC1)==SET);   //SET=1
	/*获取的是这个CR2_RSTCAL_Set的标志位 ,该位由软件设置并由硬件清除
	在校准寄存器被初始化后该位将被清除,所以该位的用法就是:
	你软件置该位为1,那硬件就会开始复位校准 , 当复位校准完成后,该位就会由硬件自动清0
	*/
	//开始校准
	ADC_StartCalibration(ADC1);
	//获取校准状态
	while(ADC_GetCalibrationStatus(ADC1)==SET);
	
	
}




void ad_getvalue(void){
	
		
	//重新写一下传输寄存器
	DMA_Cmd(DMA1_Channel1, DISABLE);
	/*
	设置当前数据寄存器 ,  就是给这个传输计数器写数据的
	和配置DMA中X.DMA_BufferSize参数相似
	*/
	DMA_SetCurrDataCounter(DMA1_Channel1, 4);
	DMA_Cmd(DMA1_Channel1, ENABLE);
	
	
	
	//单次模式-----软件触发转换
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);
	
	//这里因为转运(DMA)总是在转换之后的
	//等待MDA完成的代码
	//DMA1_FLAG_TC1---转运完成标志位   转运完成后置1
	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET); 
	DMA_ClearFlag(DMA1_FLAG_TC1);//清除转运完成标志位


}




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_getvalue函数

        因为在这里面的ADC是单次转化,  所以每次转化时都需要软件触发一次,  每转化一次都需要调用一次这个的函数----ADC_SoftwareStartConvCmd(ADC1,ENABLE);

        因为DMA也是正常的单次模式:  所以在触发ADC之前,需要再重新写入一下传输计数器

//重新写一下传输寄存器
	DMA_Cmd(DMA1_Channel1, DISABLE);
	/*
	设置当前数据寄存器 ,  就是给这个传输计数器写数据的
	和配置DMA中X.DMA_BufferSize参数相似
	*/
	DMA_SetCurrDataCounter(DMA1_Channel1, 4);
	DMA_Cmd(DMA1_Channel1, ENABLE)

        最后,等待ADC转换和DMA转运完成,  这里因为转运总是在转换之后的 ,  所以我们只需要写等待DMA完成的代码, 不用写等待ADC完成的代码

//这里因为转运(DMA)总是在转换之后的
	//等待MDA完成的代码
	//DMA1_FLAG_TC1---转运完成标志位   转运完成后置1
	while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET); 
	DMA_ClearFlag(DMA1_FLAG_TC1);//清除转运完成标志位

启动扫描模式

        就是在选择组的时候多选几个,然后在配置ADC的时候选择扫描模式,  通道要和一引脚对应.  ADC配置的通道数目填入相应的通道数目

	/*ADC_Channel_0  --通道o
	1----1~16的范围规则组第几个序列
	ADC_SampleTime_55Cycles5-----指定通道的采样时间
	*/
	//选择AD转化器----我们选择规则组的输入通道 因为启动了应该组,所以是下面改为扫描模式
	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_initstruct.ADC_ScanConvMode=ENABLE;//可以选择是扫描模式还是非扫描模式---扫描模式
    ADC_initstruct.ADC_NbrOfChannel=4;  //通道数目--指定在扫描模式下,总共会用到几个通道

 DR寄存器

在ADC会把数据写入到ADC的DR寄存器中,  使用在配置DMA的外设起始地址应该是

DMA_initstruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR; //外设的起始地址--要求:32位

 数据源外设地址不自增,  寄存器地址自增

        在这里外面使用的ADC模式为扫描模式,  就是启动了一个组在组中写通道,   如果地址自增就会跑到其他的组中去.        寄存器地址自增 : 外设中组中通道的每个数据对应的是寄存器中不同的地方,  这样才可以实现数据的转移

其他注意

        在开启ADC前: 要打开ADC到DMA的输出

DMA_Cmd(DMA1_Channel1,ENABLE);

        在配置DMA的传输计数器是也要改变为相应的传输次数

	DMA_initstruct.DMA_BufferSize=4;//传输几次

        配置DMA的触发方式改为硬件触发

	DMA_initstruct.DMA_M2M=DMA_M2M_Disable;  //使用硬件触发

  ADC连续扫描+DMA循环转运模式

         因为使用的是连续---所以软件触发只需在初始化的时候触发一次即可. 

        当ADC触发之后,ADC连续转换,DMA循环转运 ,  两者一直在工作,始终把最新的转换结果,刷新到SRAM数组里,   我们想要数据的时候,随时去数组里取就行了


#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t AD_Value[4];

void AD_init(void){
	//RCC开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
	/*
	我们使用的STM32型号为: STMF103
	参数1的第一个选择  : 互连型设备--互联型是STM23F  105/107的型号
	参数1的第一个选择  : 其他设备---103选择这个
	*/
	

	
	//配置ADCCLK
	//APB2时钟72MHz时钟信号然后通过ADC预分频器进行分频,得到ADCCLK钟信号
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72Mhz/6=12Mhz
	
	//配置GPIO
	GPIO_InitTypeDef GPIO_initstruct;
	GPIO_initstruct.GPIO_Mode=GPIO_Mode_AIN;  //模拟输入,可以理解为ADC的专属模式
	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_Channel_0  --通道o
	1----1~16的范围规则组第几个序列
	ADC_SampleTime_55Cycles5-----指定通道的采样时间
	*/
	//选择AD转化器----我们选择规则组的输入通道 因为启动了应该组,所以是下面改为扫描模式
	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
	ADC_InitTypeDef ADC_initstruct;
	ADC_initstruct.ADC_ContinuousConvMode=ENABLE;//选择是连续转换还是单次转换--连续
	ADC_initstruct.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐---右对齐
	ADC_initstruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//触发控制的触发源---不使用外部触发,使用内部软件触发
	ADC_initstruct.ADC_Mode=ADC_Mode_Independent;//ADC的工作模式---独立模式
	ADC_initstruct.ADC_NbrOfChannel=4;  //通道数目--指定在扫描模式下,总共会用到几个通道
	ADC_initstruct.ADC_ScanConvMode=ENABLE;//可以选择是扫描模式还是非扫描模式---扫描模式
	ADC_Init(ADC1,&ADC_initstruct);
	//ADC在完成后面数据写在DR寄存器中
	



	//配置DMA
	//外设备站点的3个参数
	//因为stm32是32位的单片机,他的一个内存单元是32位的
	DMA_InitTypeDef DMA_initstruct;
	DMA_initstruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR; //外设的起始地址--要求:32位
	DMA_initstruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;外设的数据宽度---我们选择以字节的方式传输(uint16_t)
	DMA_initstruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设的地址是否自增--不自增
//	//储存器的3个参数
	DMA_initstruct.DMA_MemoryBaseAddr=(uint32_t)AD_Value;//储存器的起始地址--要求:32位
	DMA_initstruct.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//储存器的数据宽度--我们选择以字节的方式传输(uint16_t)
	DMA_initstruct.DMA_MemoryInc=DMA_MemoryInc_Enable;//储存器的地址是否自增--自增
	//传输方向
	
	DMA_initstruct.DMA_DIR=DMA_DIR_PeripheralSRC;//指定外设站点(外设寄存器)为数据源还是目的地---数据源
	//缓冲区大小---传输计数器
	DMA_initstruct.DMA_BufferSize=4;//传输几次
	//传输模式----是否使用自动重装
	DMA_initstruct.DMA_Mode=DMA_Mode_Circular;//循环模式    //传输计数器到0直接停止  //DMA_Mode_Circular--循环模式   DMA_Mode_Normal
	//选择触发模式---硬件触发或者软件触发
	DMA_initstruct.DMA_M2M=DMA_M2M_Disable;  //使用硬件触发
	//优先级
	DMA_initstruct.DMA_Priority=DMA_Priority_Medium;
	DMA_Init(DMA1_Channel1,&DMA_initstruct);
	

	//开启MDA
	DMA_Cmd(DMA1_Channel1,ENABLE);
	//开启ADC到DMA的输出
	
	ADC_DMACmd(ADC1,ENABLE);
	
	//开启ADC
	ADC_Cmd(ADC1,ENABLE);
	
	//校准ADC
	
	//复位校准
	ADC_ResetCalibration(ADC1);//---把CR2_RSTCAL_Set这一位置一
	//等待复位校准完成--ADC_GetResetCalibrationStatus作用:返回复位校准的状态
	while (ADC_GetResetCalibrationStatus(ADC1)==SET);   //SET=1
	/*获取的是这个CR2_RSTCAL_Set的标志位 ,该位由软件设置并由硬件清除
	在校准寄存器被初始化后该位将被清除,所以该位的用法就是:
	你软件置该位为1,那硬件就会开始复位校准 , 当复位校准完成后,该位就会由硬件自动清0
	*/
	//开始校准
	ADC_StartCalibration(ADC1);
	//获取校准状态
	while(ADC_GetCalibrationStatus(ADC1)==SET);
	
		ADC_SoftwareStartConvCmd(ADC1,ENABLE);

}




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)
	{
		


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

	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/957310.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

讲讲几道关于 TCP/UDP 通信的面试题

TCP (1)TCP 的 accept 发生在三次握手的哪个阶段? 如下图connect和accept的关系: accept过程发生在三次握手之后,三次握手完成后,客户端和服务器就建立了tcp连接并可以进行数据交互了。这时可以调用accep…

每日一题(链表的中间节点)

每日一题(链表的中间节点) 876. 链表的中间结点 - 力扣(LeetCode) 思路: 如下图:可以定义两个结构体指针均从链表的头节点开始向后遍历,fast指针一次走两步,slow指针一次走一步&a…

RC电路(二):耦合

耦合仿真电路及波形 数值与输入方波宽度 之间满足:,将变成一个 耦合电路,输出波形可以跟随输入波形,电路如下图所示。 上图红框部分放大后如下图所示: 在 时, 由 ,因电容电压不能突变(来不及…

第一方支付、第二方支付、第三方支付、第三方支付是什么?

我相信关于支付行业大家多多少少都有一些自己的理解,但是具体的一些名词如标题中的这些,第一方、第二方、第三方支付,到底指的是什么? 第一方支付 也就是现金支付,其本质的意义就是指货币支付,从最早出现货…

IDEA配置使用Gradle

IDEA如何配置 Gradle(详细版) 使用国内镜像仓库加速下载依赖包

docker-compose 部署nacos 整合 postgresql 为DB

标题docker-compose 部署nacos 整合 postgresql 为DB 前提: 已经安装好postgresql数据库 先创建好一个数据库 nacos,执行以下sql: /** Copyright 1999-2018 Alibaba Group Holding Ltd.** Licensed under the Apache License, Version 2.0 (the "…

为什么人与人之间的差距这么大?

前言 首先要明确的是,与身边的人相比,每个人的生活情况和经历都是不同的,有差距是非常正常的。因此,不需要过度关注自己与他人之间的差距。个人感受 在生活中,工作中,学习中,不免遇上一些各方…

2023年全国职业院校技能大赛信息安全管理与评估网络安全渗透任务书

全国职业院校技能大赛 高等职业教育组 信息安全管理与评估 任务书 模块三 网络安全渗透、理论技能与职业素养 比赛时间及注意事项 本阶段比赛时长为180分钟,时间为9:00-12:00。 【注意事项】 (1)通过找到正确的flag值来获取得分,f…

【防火墙】防火墙NAT Server的配置

Web举例:公网用户通过NAT Server访问内部服务器 介绍公网用户通过NAT Server访问内部服务器的配置举例。 组网需求 某公司在网络边界处部署了FW作为安全网关。为了使私网Web服务器和FTP服务器能够对外提供服务,需要在FW上配置NAT Server功能。除了公网…

java基于微信小程序的讲座预约系统的研究与实现

博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 1 简介2 技术栈第三章 系统分析3.1初步需求分析 3.2 系统用例分析3.2.1 公告管理用例分析3.2.2 系…

Opencv手工选择图片区域去水印

QT 插件化图像算法研究平台的功能在持续完善,补充了一个人工选择图片区域的功能。 其中,图片选择功能主要代码如下: QRect GLImageWidget::getSeleted() {QRect ajust(0,0,0,0);if(image.isNull() || !hasSelection)return ajust;double w1…

数据转换器与其它工具格式转换功能的对比

网上看到有几篇介绍将KML数据转换为GIS其它格式或者将GIS其它格式转换为KML数据方法的文章,感觉好多的步骤,技巧和注意呀!头晕!其中还不包括工具的安装,破解!做个数据转换也真的是烧脑啊。下面也我介绍一下我们的工具GIS数据转换器-矢量的转换方法。我只…

微信仿H5支付

仿H5支付是指一种模拟原生H5支付流程的非官方支付方式。这种支付方式通常是由第三方支付服务提供商开发和维护的,目的是为了绕过官方支付渠道的限制,如费率、审核等问题。然而,由于仿H5支付并非官方授权和认可的支付方式,其安全性…

MPI之组通信

在前面的文章中,对点对点通信API进行了介绍,本文将对MPI组通信相关API进行介绍 一对多 Broadcast 将一个进程的数据广播到所有其他进程中,函数原型: int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root…

开源django+mysql+vue3前后端分离商城baykeShop使用指南

baykeShop开源商城系统 项目简介 baykeShop(拜客商城系统)是一款全开源Python栈商城系统,管理后台完全前后端分离重写以适配项目,前后端100%开源,后台前端采用开源SCUI开源库对接开发,美观、易用、符合当…

深入探索C语言自定义类型:打造你的编程世界

一、什么是自定义类型 C语言提供了丰富的内置类型,常见的有int, char, float, double, 以及各种指针。 除此之外,我们还能自己创建一些类型,这些类型称为自定义类型,如数组,结构体,枚举类型和联合体类型。 …

软件架构模式+系统架构

架构模式对比 分层模式 一般信息系统中最常见的4层划分如下: Presentation layer 表示层(也就是UI层)Application layer 应用层(也就是服务层)Business logic layer 业务逻辑层(也就是领域层)…

NS2安装及入门实例——(ns2.35 / Ubuntu20.04)

文章目录 一、ns2安装1、更新系统源2、准备工作3、下载安装包4、安装5、问题① 问题1② 问题2③ 问题3 6、安装成功7、环境配置 二、nam安装1、安装2、问题 三、实例 一、ns2安装 1、更新系统源 sudo apt-get update sudo apt-get upgrade2、准备工作 sudo apt-get install …

图像生成模型【自编码器、RNN、VAE、GAN、Diffusion、AIGC等】

目录 监督学习 与 无监督学习 生成模型 自编码器 从线性维度压缩角度: 2D->1D 线性维度压缩: 3D->2D 推广线性维度压缩 流形 自编码器:流形数据的维度压缩 全图像空间 自然图像流形 自编码器的去噪效果 自编码器的问题 图像预测 (“结构化预测”…

CSS判断手机暗黑模式

手机有个功能到了晚上会自动变成深色也就是暗黑模式.这种情况下网页会自动变颜色.如果想自由控制暗黑模式下的html样式的话,可以用如下方式: media (prefers-color-scheme: dark) {/*html, body {*//*filter: invert(1) hue-rotate(180deg);*//*}*/.maill{margin-left: 0;marg…