目录
前言
上一期我们学习了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。
cpp
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);
2.初始化配置
ADC_DeInit恢复缺省配置、ADC_Init初始化、ADC_StructInit结构体初始化
cpp
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基本结构图里的开关控制。
cpp
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
4.校准
接下来四个函数分别是复位校准、获取复位校准状态、开始校准、获取开始校准状态,这是用于控制校准的函数。我们使能完成后下面这四个直接去调用就行了。
cpp
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转换。
cpp
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
6.判断转换是否结束
下面这个函数是判断是否转换结束的。获取标志位状态,然后参数给EOC的标志位,判断EOC标志位是不是置1了。如果转换结束,EOC标志位置1,然后调用这个函数,判断标志位。这样才是正确的判断转换是否结束的方法。
cpp
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG);
这里再提一下,下面这个函数看上去好像是跟转换结束相关的函数,实际上不要被误导,实际上是ADC获取软件开始转换状态的函数,一般不怎么用到。
cpp
FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);
7.规则组的配置
下面这个函数就比较重要的了,是用来配置规则组相关的参数的。
cpp
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:是值转换的时间设置
下面看个示例:
cpp
//ADC1 选择通道0 放入序列1 时间为ADC_SampleTime_28Cycles5
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_28Cycles5);
8.配置间断模式
下面这两个函数,是用来配置间断模式的。
cpp
void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);//每隔几个通道间断一次。
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);//使能操作,是否开启间断模式
9.ADC获取转换值
这个函数是用来获取指定ADC转换后数据寄存器的值。
cpp
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
下面这个是ADC获取双模式转换值,这个是双ADC模式读取转换结果的函数,暂时不用。
cpp
uint32_t ADC_GetDualModeConversionValue(void);
10.注入组相关函数
下面是注入组相关函数,本期不用到注入组,了解一下就行了。
cpp
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文件代码:
cpp
#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文件代码 :
cpp
#ifndef __AD_h
#define __AD_h
void AD_init();
uint16_t AD_getvalue();
#endif // !__AD_h#define __AD_h
主文件main.c代码:
cpp
#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文件代码:
cpp
#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文件代码:
cpp
#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代码:
cpp
#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);
}
}
以上就是本期的全部内容了,我们下次见!
今日壁纸: