STM32编写ADC功能,实现单路测量电压值(OLED显示)
先来看看本次实验的结果吧:stm32点电压测量范围为0-3.3V,数值为:0-4095
来看看这个工程的文件布局吧:
实现ADC功能总共分为六步:
第一步:开始RCC时钟,包括ADC和GPIO的时钟,ADCCLK的分频器也需要配置一下。
第二步:配置GPIO,把需要用的GPIO配置成模拟输入的模式
第三步:配置多路开关,把左边的通道接入规则组列表里
第四步:配置ADC转换器,用结构体配置,一大块的参数。
第五步:打开ADC_CMD()开启ADC。
第六步:校准ADC
接着来学习一下ADC相关的库函数:(看起来好像有点乱,后面有截图)
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2); 用来配置ADCCLK分频器的 void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState); 这个是用来给ADC上电的函数 void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState); 用来开启DMA输出信号的 void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState); 中断输出控制 void ADC_ResetCalibration(ADC_TypeDef* ADCx); 复位校准 FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx); 获取复位校准状态 void ADC_StartCalibration(ADC_TypeDef* ADCx); 开始校准 FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx); 获取开始校准状态 void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState); ADC软件开始转换控制 软件触发的函数 FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx); ADC获取软件开始转换状态 FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG); 获取转换完成标志位状态,判断EOC标志位是不是置1了。 void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number); 间隔几个通道采集信
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState); 是否启用间隔模式 void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime); 规则组通道配置 void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState); ADC 外部触发转换控制,就是是否允许外部触发转换 uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx); ADC获取转换值 uint32_t ADC_GetDualModeConversionValue(void); ADC获取双模式转换值,这个是双ADC模式读取转换结果 void ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx, uint32_t ADC_AnalogWatchdog); //是否启动看门口 void ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold, uint16_t LowThreshold); //配置高低阈值 void ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel); //配置看门的通道 配置模拟看门狗的三个函数 void ADC_TempSensorVrefintCmd(FunctionalState NewState); //ADC温度传感器内部参考电压控制,用来开启内部通道 FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, uint8_t ADC_FLAG); //获取标志位状态 void ADC_ClearFlag(ADC_TypeDef* ADCx, uint8_t ADC_FLAG); // 清楚标志位状态 ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, uint16_t ADC_IT); //获取中断状态 void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, uint16_t ADC_IT); //清楚中断挂起位
接下来就是每个文件的源码了:
MyADC.c文件:
#include "stm32f10x.h" // Device header
void MyADC_Init(void)
{
//第一步:开始RCC时钟,包括ADC和GPIO的时钟,ADCCLK的分频器也需要配置一下。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //开启ADC1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6); // ADC的时钟选择6分频,也就是72M/6=12M
//第二步:配置GPIO,把需要用的GPIO配置成模拟输入的模式
GPIO_InitTypeDef GPIO_InitStruct; //GPIO初始化的结构体
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN; // 模式为模拟输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; // 初始化端口0
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //端口频率50M
GPIO_Init(GPIOA, &GPIO_InitStruct); // GPIOA初始化
//第三步:配置多路开关,把左边的通道接入规则组列表里
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_28Cycles5); //ADC1规则配置(ADC1,通道1,列表1,转换时间28.5个时钟(12M))
//第四步:配置ADC转换器,用结构体配置,一大块的参数。
ADC_InitTypeDef ADC_InitStruct; //ADC初始化结构体
ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; //连续转换模式:关闭
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; // 数据对齐:右对齐
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //ADC中断触发:空,也就是软件触发
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; // ADC模式:独立模式
ADC_InitStruct.ADC_NbrOfChannel = 1; //规则组中的通道数:1
ADC_InitStruct.ADC_ScanConvMode = DISABLE; //扫描模式:关闭
ADC_Init(ADC1, &ADC_InitStruct); //ADC初始化
//第五步:打开ADC_CMD()开启ADC。
ADC_Cmd(ADC1, ENABLE); // ADC开启
//第六步:校准ADC
ADC_ResetCalibration(ADC1); // 复位校准ADC
while(ADC_GetResetCalibrationStatus(ADC1) == SET); // 等待校准标志位置0
ADC_StartCalibration(ADC1); // 开始复位校准ADC
while(ADC_GetCalibrationStatus(ADC1) == SET); // 等待开始校准结束标志位置0
}
uint16_t Get_val(void)
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 软件触发ADC使能
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); // 等待获取EOC标志位置1
return ADC_GetConversionValue(ADC1); // 返回ADC转换的结果值
}
MyADC.h文件:
#ifndef __MYADC_H
#define __MYADC_H
void MyADC_Init(void);
uint16_t Get_val(void);
#endif
主函数main.c文件:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t ADValue; //定义AD值变量
float Voltage; //定义电压变量
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
AD_Init(); //AD初始化
/*显示静态字符串*/
OLED_ShowString(1, 1, "ADValue:");
OLED_ShowString(2, 1, "Voltage:0.00V");
while (1)
{
ADValue = AD_GetValue(); //获取AD转换的值
Voltage = (float)ADValue / 4095 * 3.3; //将AD值线性变换到0~3.3的范围,表示电压
OLED_ShowNum(1, 9, ADValue, 4); //显示AD值
OLED_ShowNum(2, 9, Voltage, 1); //显示电压值的整数部分
OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2); //显示电压值的小数部分
Delay_ms(100); //延时100ms,手动增加一些转换的间隔时间
}
}
还有一个OLED的代码就不展示了,没有的可以在之前的博文中找到,都有公布过,自己挨片的看就能找到的,编译后写入到STM32中就能看到实验结果了,调整电位器就能看到电压和数值的变化,电位器中间抽头接A0,另外两个头中的一个接3.3V电源,这就是整个的接线图了。