Bootstrap

stm32入门-----ADC模数转换器(实践篇——下)

目录

前言

一、硬件元器件

二、ADC相关函数

 1.ADC时钟分频

 2.初始化配置

3.使能上电

4.校准

5.ADC软件开始转换控制

 6.判断转换是否结束

7.规则组的配置 

8.配置间断模式

9.ADC获取转换值

10.注入组相关函数

三、项目实践

项目一:AD单通道转换

项目二:AD多通道转换


前言

        上一期我们学习了ADC模数转换器的相关理论知识点(上一期:stm32入门-----ADC模数转换器(理论篇——上)-CSDN博客),那么本期就来进行项目的实操练习,本期有两个项目,分别是AD单通道转换和AD多通道转换。(视频:[7-2] AD单通道&AD多通道_哔哩哔哩_bilibili

 本期相关代码我已经上传至百度网盘可自行下载,

链接:https://pan.baidu.com/s/1bPoG6o-p0keTbHuUV5sobA?pwd=0721 
提取码:0721

一、硬件元器件

本期要用到的有滑动电阻和传感器这两者元器件,滑动电阻大家都再熟悉不过了,传感器的话我们之前是用过的,不过与之前不同,之前是用传感器的DO口作为高低电平的输入,而我们本期是需要用到实时电压的输入这时候就需要AO口了,而DO口是不需要的。

 传感器(从左到右依次光敏传感器、温度传感器、对射红外线传感器、红外线反射传感器):

 传感器电路图(传感器本质上电路都是一致的):

二、ADC相关函数

 1.ADC时钟分频

根据ADC的时钟频率要求,频率是小于14MHz的,而我们如果选择内部时钟的话那么至少要选择6分频,才能使得时钟变为12MHz。 

void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);

 2.初始化配置

ADC_DeInit恢复缺省配置、ADC_Init初始化、ADC_StructInit结构体初始化

void ADC_DeInit(ADC_TypeDef* ADCx);
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);

3.使能上电

  ADC_Cmd:这个是用于给ADC上电的,就是ADC基本结构图里的开关控制。

void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);

4.校准

 接下来四个函数分别是复位校准、获取复位校准状态、开始校准、获取开始校准状态,这是用于控制校准的函数。我们使能完成后下面这四个直接去调用就行了。

void ADC_ResetCalibration(ADC_TypeDef* ADCx);//复位校准
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);//获取复位校准完成标志位
void ADC_StartCalibration(ADC_TypeDef* ADCx);//开始校准
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);//获取开始校准完成标志位

5.ADC软件开始转换控制

这里我们调用这个函数的时候就开始触发ADC转换。

void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

 6.判断转换是否结束

下面这个函数是判断是否转换结束的。获取标志位状态,然后参数给EOC的标志位,判断EOC标志位是不是置1了。如果转换结束,EOC标志位置1,然后调用这个函数,判断标志位。这样才是正确的判断转换是否结束的方法。

FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);

这里再提一下,下面这个函数看上去好像是跟转换结束相关的函数,实际上不要被误导,实际上是ADC获取软件开始转换状态的函数,一般不怎么用到。

FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);

7.规则组的配置 

下面这个函数就比较重要的了,是用来配置规则组相关的参数的。

void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
  • ADC_TypeDef* ADCx:是选择ADC(ADC1, ADC2, ADC3)
  • uint8_t ADC_Channel:第二参数是选择指定的通道(通道有16个)
  • uint8_t Rank:是指选择的序列位置(1~16) 是数字
  • uint8_t ADC_SampleTime:是值转换的时间设置

下面看个示例: 

//ADC1  选择通道0   放入序列1  时间为ADC_SampleTime_28Cycles5
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_28Cycles5);

8.配置间断模式

下面这两个函数,是用来配置间断模式的。

void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);//每隔几个通道间断一次。
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);//使能操作,是否开启间断模式

9.ADC获取转换值

这个函数是用来获取指定ADC转换后数据寄存器的值。

uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);

 下面这个是ADC获取双模式转换值,这个是双ADC模式读取转换结果的函数,暂时不用。 

uint32_t ADC_GetDualModeConversionValue(void);

10.注入组相关函数

下面是注入组相关函数,本期不用到注入组,了解一下就行了。

void ADC_AutoInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_InjectedDiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_ExternalTrigInjectedConvConfig(ADC_TypeDef* ADCx, uint32_t ADC_ExternalTrigInjecConv);
void ADC_ExternalTrigInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_SoftwareStartInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
FlagStatus ADC_GetSoftwareStartInjectedConvCmdStatus(ADC_TypeDef* ADCx);
void ADC_InjectedChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
void ADC_InjectedSequencerLengthConfig(ADC_TypeDef* ADCx, uint8_t Length);
void ADC_SetInjectedOffset(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel, uint16_t Offset);
uint16_t ADC_GetInjectedConversionValue(ADC_TypeDef* ADCx, uint8_t ADC_InjectedChannel);

三、项目实践

项目一:AD单通道转换

现象:

AD单通道转换

电路连接图:

主要工程文件: 

代码如下

AD.c文件代码:

#include "stm32f10x.h"                  // Device header

void AD_init() {
    //1.开启时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    RCC_ADCCLKConfig(RCC_PCLK2_Div6);//选择时钟分频

    //2.配置GPIO
    GPIO_InitTypeDef GPIO_initstruct;
	GPIO_initstruct.GPIO_Mode=GPIO_Mode_AIN; //选择模拟输入 ,是ADC专属模式,其他是无效的
	GPIO_initstruct.GPIO_Pin=GPIO_Pin_0;
	GPIO_initstruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_initstruct);

    //规则组列表中的第一个通道,序列1写入通道0,
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_28Cycles5);
    // 如果还想添加其他通道,可以复制修改通道和对应填入的序列即可,采样时间也可以单独设置
    // ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_28Cycles5);
    // ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 2, ADC_SampleTime_28Cycles5);
    // ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_28Cycles5);

    // ADC结构体初始化
    ADC_InitTypeDef ADC_initstruct;
    ADC_initstruct.ADC_DataAlign = ADC_DataAlign_Right; //数据对齐,左右可选,这里选择右对齐
    ADC_initstruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//选择外部触发源,这里选择none是用软件触发
    ADC_initstruct.ADC_Mode = ADC_Mode_Independent;//选择ADC的模式,独立和双模式选择,这里选择独立模式
    //选择模式
    ADC_initstruct.ADC_ContinuousConvMode = ENABLE; // 连续/非连续转换模式,这里选择非连续转换
    ADC_initstruct.ADC_ScanConvMode = DISABLE;  // 扫描模式,这里选择非扫描模式
    ADC_initstruct.ADC_NbrOfChannel = 1;//通道数目,当前通道为1,这里选择1
    ADC_Init(ADC1, &ADC_initstruct);

    //后继如果需要配置中断可以选择

    //开启ADC电源
    ADC_Cmd(ADC1, ENABLE);

    //校准
    ADC_ResetCalibration(ADC1); //复位校准
    while (ADC_GetResetCalibrationStatus(ADC1) == SET); //获取复位校准状态,如果复位完成了之后就标志位就会清理
    ADC_StartCalibration(ADC1); //开启校准
    while (ADC_GetCalibrationStatus(ADC1) == SET);//如果标志位清零的话说明校准完成,不然就一直等待校准标志位

    ADC_SoftwareStartConvCmd(ADC1, ENABLE);//选择软件触发,调用一下就能进行软件触发控制,开始转换

}

uint16_t AD_getvalue() {
    //ADC_SoftwareStartConvCmd(ADC1, ENABLE);//选择软件触发,调用一下就能进行软件触发控制,开始转换
    //while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //获取转换结束的标志位,当EOC为1的时候就转换完成
    //获取结果
    return ADC_GetConversionValue(ADC1);//这个函数是去直接读取DR寄存器里面的数据
}

AD.h文件代码 :

#ifndef __AD_h
#define __AD_h

void AD_init();
uint16_t AD_getvalue();

#endif // !__AD_h#define __AD_h

主文件main.c代码:

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

uint16_t AD_value;
float Volatge;

int main(void)
{
	LED_init();
	OLED_Init();
	AD_init();
	OLED_ShowString(1, 1, "ADvalue:");
	OLED_ShowString(2, 1, "Volatge:0.00V");
	while (1) {
		if (Volatge < 1.3) {
			LED1_ON();
		}
		else
			LED1_OFF();
		
		AD_value = AD_getvalue();
		Volatge = (float)AD_value / 4095 * 3.3;
		OLED_ShowNum(1, 9, AD_value, 4);
		//显示浮点数
		OLED_ShowNum(2, 9, Volatge, 1);
		OLED_ShowNum(2, 11, (uint16_t)(Volatge * 100) % 100, 2);
		Delay_ms(100);

	}
}


 

项目二:AD多通道转换

现象:

电路连接图:

 主要工程文件: 

代码如下

AD.c文件代码:

#include "stm32f10x.h"                  // Device header

void AD_init() {
    //1.开启时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    RCC_ADCCLKConfig(RCC_PCLK2_Div6);//选择时钟分频

    //2.配置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);

    //规则组列表中的第一个通道,序列1写入通道0,
    
    // 如果还想添加其他通道,可以复制修改通道和对应填入的序列即可,采样时间也可以单独设置
    // ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_28Cycles5);
    // ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 2, ADC_SampleTime_28Cycles5);
    // ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_28Cycles5);

    // 3.ADC结构体初始化
    ADC_InitTypeDef ADC_initstruct;
    ADC_initstruct.ADC_DataAlign = ADC_DataAlign_Right; //数据对齐,左右可选,这里选择右对齐
    ADC_initstruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//选择外部触发源,这里选择none是用软件触发
    ADC_initstruct.ADC_Mode = ADC_Mode_Independent;//选择ADC的模式,独立和双模式选择,这里选择独立模式
            //选择模式
    ADC_initstruct.ADC_ContinuousConvMode = DISABLE; //连续转换,这里选择非连续转换
    ADC_initstruct.ADC_ScanConvMode = DISABLE;  // 扫描模式,这里选择非扫描模式
    ADC_initstruct.ADC_NbrOfChannel = 1;//通道数目,当前通道为1,这里选择1

    ADC_Init(ADC1, &ADC_initstruct);

    //后继如果需要配置中断可以选择

    //4.开启ADC电源
    ADC_Cmd(ADC1, ENABLE);

    //5.校准
    ADC_ResetCalibration(ADC1); //复位校准
    while (ADC_GetResetCalibrationStatus(ADC1) == SET); //获取复位校准状态,如果复位完成了之后就标志位就会清理
    ADC_StartCalibration(ADC1); //开启校准
    while (ADC_GetCalibrationStatus(ADC1) == SET);//如果标志位清零的话说明校准完成,不然就一直等待校准标志位


}

//这里只需要改为传入通道的参数,然后对指定通道去获取AD转换的结果
uint16_t AD_getvalue(uint8_t ADC_Channel) {

    //ADC1  选择通道0   放入序列1  时间为ADC_SampleTime_28Cycles5
    ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_28Cycles5);

    ADC_SoftwareStartConvCmd(ADC1, ENABLE);//选择软件触发,调用一下就能进行软件触发控制,开始转换
    while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //获取转换结束的标志位,当EOC为1的时候就转换完成
    //获取结果
    return ADC_GetConversionValue(ADC1);//这个函数是去直接读取DR寄存器里面的数据
}

AD.h文件代码:

#ifndef __AD_h
#define __AD_h

void AD_init();
uint16_t AD_getvalue(uint8_t ADC_Channel);
#endif // !__AD_h#define __AD_h

主文件main.c代码:

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

uint16_t AD0, AD1, AD2, AD3;


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) {
		AD0 = AD_getvalue(ADC_Channel_0);
		AD1 = AD_getvalue(ADC_Channel_1);
		AD2 = AD_getvalue(ADC_Channel_2);
		AD3 = AD_getvalue(ADC_Channel_3);


		OLED_ShowNum(1, 5, AD0, 4);
		OLED_ShowNum(2, 5, AD1, 4);
		OLED_ShowNum(3, 5, AD2, 4);
		OLED_ShowNum(4, 5, AD3, 4);
		Delay_ms(100);

	}
}


 

以上就是本期的全部内容了,我们下次见!

今日壁纸:

;