stm32标准库和HAL库的对比学习8.《学习adc模数转换器和DMA》

        本人是大一的学生,学习了一段时间的stm32,此系列博客为个人的学习笔记,方便个人复习,如有错误或问题,非常非常欢迎大家来大力指正。

     HAL库的知识参考阿熊话太多的教程,标准库参考江科大的教材。本视频的部分图也来自阿熊和江科大视频中的图片,代码也是参考他们的大家如果要学习强烈推荐他们的视频

模数转换器,顾名思义是将模拟电压(不断变化的电压值,转化为数字变量)abc为电压转化为数值,dac为数字转化为电压(是dc调光的原理吗???)

有几个属性:分辨率一般是用几位AD值来说明,位数越高量化结果越精细

                        转换时间,从开始到转换完成的时间

                        输入电压范围和输出结果范围

还有模拟看门狗检测电压范围

逐次逼近型ADC:

这个ADC原理图供参考,原理和stm32的一样

in0-7是输入通道(stm32有18个),地址锁存和译码是是输入通道和锁存信号去选择哪个通道。DAC通过数字转换电压输出一个参考电压,与输入的电压进行比较。DAC调整电压到和输入电压近似相等,输入电压就是那个数值(使用二分法进行比较,位数多少次,比较多少次)

ADC的基本流程:注入组是一次去采集4个,然后4个结果可以同时获取。规则组是可以一次去采集16个

ADC引脚与通道的关系

ADC的4种模式,了解一下

因为寄存器16位,数据12位有数据对齐,用右对齐

标准库的实现:

还是一样开启GPIO和ADC的RCC

然后配置ADC的时钟,进行分频

定义GPIO的属性

定义ADC的通道

定义ADC的属性

对ADC进行校准

void adcinit()
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	RCC_ADCCLKConfig(RCC_PCLK2_Div6);//开启分频数,adc最高的是12m所以是6分频
	
	 GPIO_InitTypeDef GPIO_Initstructure;
	 GPIO_Initstructure.GPIO_Pin=GPIO_Pin_0;
	 GPIO_Initstructure.GPIO_Speed=GPIO_Speed_50MHz;
	 GPIO_Initstructure.GPIO_Mode=GPIO_Mode_AIN;//模拟输入
	 GPIO_Init(GPIOA,&GPIO_Initstructure);
	//规则组输入通道
	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);//Rank是规则组序列器的次序,sample time是采样时间
//	ADC_RegularChannelConfig(ADC1,ADC_Channel_0,2,ADC_SampleTime_55Cycles5);想要多通道采样就这样加就可以了
	ADC_InitTypeDef ADC_Initstructure;
	ADC_Initstructure.ADC_ContinuousConvMode=DISABLE;//连续模式开启
	ADC_Initstructure.ADC_Mode=ADC_Mode_Independent;	//配置是单adc还是双adc
	ADC_Initstructure.ADC_DataAlign=ADC_DataAlign_Right;//数据对其模式
	ADC_Initstructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//不使用外部触发,使用内部
	ADC_Initstructure.ADC_ScanConvMode=DISABLE;//扫描模式
	ADC_Initstructure.ADC_NbrOfChannel=1;//通道数目 
	ADC_Init(ADC1,&ADC_Initstructure);
	ADC_Cmd(ADC1,ENABLE);
//校准步骤
	ADC_ResetCalibration(ADC1);//复位校准置1
	while(ADC_GetResetCalibrationStatus(ADC1)==SET);//等待校准完成,硬件复位0
	ADC_StartCalibration(ADC1);//开启校准
	while(ADC_GetCalibrationStatus(ADC1)==SET);//等待校准完成
}

获取ADC的寄存器数值

uint16_t ADC_GET(void)//软件触发转换——转换完成等待ECO置1——读取数据寄存器
{
	ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发转换
	while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);//转换完成等待ECO置1,自动清除
//等待时间15行,定义了55.5+固定转换周期12.5,68周期,第7行6分频12mhz,时间计算1/12m*68
	return ADC_GetConversionValue(ADC1);
}

这样ADC就定义完成了

用DMA实现多通道的ADC

根据江科大的图进行配置

    //memory是存储器,peripheralinc是初始
	ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);	//规则组序列1的位置,配置为通道0
	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);	//规则组序列2的位置,配置为通道1
	ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);	//规则组序列3的位置,配置为通道2
	ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);	//规则组序列4的位置,配置为通道3
	
	/*ADC初始化*/
	ADC_InitTypeDef ADC_InitStructure;											//定义结构体变量
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;							//模式,选择独立模式,即单独使用ADC1
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;						//数据对齐,选择右对齐
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;			//外部触发,使用软件触发,不需要外部触发
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;							//连续转换,使能,每转换一次规则组序列后立刻开始下一次转换
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;								//扫描模式,使能,扫描规则组的序列,扫描数量由ADC_NbrOfChannel确定
	ADC_InitStructure.ADC_NbrOfChannel = 4;										//通道数,为4,扫描规则组的前4个通道
	ADC_Init(ADC1, &ADC_InitStructure);											//将结构体变量交给ADC_Init,配置ADC1
	
	/*DMA初始化*/
	DMA_InitTypeDef DMA_InitStructure;											//定义结构体变量
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;				//外设基地址,给定形参AddrA
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;	//外设数据宽度,选择半字,对应16为的ADC数据寄存器
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;			//外设地址自增,选择失能,始终以ADC数据寄存器为源
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;					//存储器基地址,给定存放AD转换结果的全局数组AD_Value
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;			//存储器数据宽度,选择半字,与源数据宽度对应
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;						//存储器地址自增,选择使能,每次转运后,数组移到下一个位置
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;							//数据传输方向,选择由外设到存储器,ADC数据寄存器转到数组
	DMA_InitStructure.DMA_BufferSize = 4;										//转运的数据大小(转运次数),与ADC通道数一致
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;								//模式,选择循环模式,与ADC的连续转换一致
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;								//存储器到存储器,选择失能,数据由ADC外设触发转运到存储器
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;						//优先级,选择中等
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);								//将结构体变量交给DMA_Init,配置DMA1的通道1
	
	/*DMA和ADC使能*/
	DMA_Cmd(DMA1_Channel1, ENABLE);							//DMA1的通道1使能
	ADC_DMACmd(ADC1, ENABLE);								//ADC1触发DMA1的信号使能
	ADC_Cmd(ADC1, ENABLE);									//ADC1使能
	
	/*ADC校准*/
	ADC_ResetCalibration(ADC1);								//固定流程,内部有电路会自动执行校准
	while (ADC_GetResetCalibrationStatus(ADC1) == SET);
	ADC_StartCalibration(ADC1);
	while (ADC_GetCalibrationStatus(ADC1) == SET);
	
	/*ADC触发*/
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);	//软件触发ADC开始工作,由于ADC处于连续转换模式,故触发一次后ADC就可以一直连续不断地工作

 先开多几个通道

初始化ADC

初始化DMA

开启各个开关

校准ADC

然后AD_Value就被数值了ADC的数值

HAL库的实现

根据标准库的步骤定义

开启RCC

开启ADC

这些选项也是和标准库的定义是一样的

先开启adc的校准

	HAL_ADCEx_Calibration_Start(&hadc1);//adc的校准
	HAL_ADCEx_Calibration_Start(&hadc2);
		HAL_ADC_Start(&hadc1);//软件开启adc
		HAL_ADC_PollForConversion(&hadc1,HAL_MAX_DELAY);//轮询等待ADC转换完成
		DAT[0]=HAL_ADC_GetValue(&hadc1);//DAT获取adc的值
		HAL_ADC_Stop(&hadc1);//关闭ADC
		
//		HAL_ADC_Start(&hadc2);//软件开启adc
//		HAL_ADC_PollForConversion(&hadc2,HAL_MAX_DELAY);//轮询等待ADC转换完成
//		DAT[0]=HAL_ADC_GetValue(&hadc2);//DAT获取adc的值
//		HAL_ADC_Stop(&hadc2);//关闭ADC
		

这样使用ADC,获取adc的值,这样的方法是两个ADC获取两个输入的值

用这种方法可以输出电压值

		sprintf(get,"adc:%f",DAT[0]/4095*3.3);
		HAL_UART_Transmit(&huart1,(uint8_t*)get,strlen(get),100);
		HAL_Delay(1000);

这种方法是一个ADC获取两个通道的值

		for(i=0;i<2;i++)
		{		
		HAL_ADC_Start(&hadc1);//软件开启adc
		HAL_ADC_PollForConversion(&hadc1,HAL_MAX_DELAY);//轮询等待ADC转换完成
		DAT[i]=HAL_ADC_GetValue(&hadc1);//DAT获取adc的值

			}
		HAL_ADC_Stop(&hadc1);//关闭ADC
		
		sprintf(get1,"adc1:%f",DAT[0]/4095*3.3);
		sprintf(get2,"adc2:%f",DAT[1]/4095*3.3);
		HAL_UART_Transmit(&huart1,(uint8_t*)get1,strlen(get1),100);
		HAL_UART_Transmit(&huart1,(uint8_t*)get2,strlen(get2),100);
		HAL_Delay(1000);
		

  • 15
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值