图片显示ADC的IN0到IN9通道在GPIO对应的端口:

图中第十行中ADC12_IN0的意思是ACD1和ADC2的IN0通道都对应着这个PA0

首先看一下ADCCLK的配置函数,这个函数rcc库函数文件中的
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);
这个函数是用来配置ADCCLK分频器的,它可以对APB2的72MHz时钟选择2、4、6、8分频,输入到ADCCLK。
接着是ADC库函数文件里的主要函数:
void ADC_DeInit(ADC_TypeDef* ADCx);//缺省配置
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);//初始化
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);//结构体初始化赋值
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);//用于开启DMA输出信号的
如果使用DMA转运数据,那就要用到这个函数了
void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);//中断输出控制,在上图中有对应,用于控制某个中断能不能通往NVIC
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);//软件开始转换控制,用于软件触发,对应上图的触发控制
FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);//获取软件开始转换状态
以下两个函数是用来配置间断模式的
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);//ADC规则组通道配置
作用是给序列的每个位置填写指定的通道
void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);//ADC外部触发转换控制,选择是否允许外部触发转换。
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);//ADC获取转换值
获取AD转换的数据寄存器,读取转换结果就是使用这个函数
uint32_t ADC_GetDualModeConversionValue(void);//ADC获取双模式转换值,双ADC模式读取转换结果的函数
剩下的带Inject的就是注入组的配置,这次没用到。
接下来是对模拟看门狗进行配置的:
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);//清除中断挂起位
初始化函数:
#include “stm32f10x.h” // Device header
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//ADC都是APB2上的设备
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//选择模拟输入模式,即ADC专属模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//配置ADC是独立模式,还是双ADC模式
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//指定ADC是左对齐还是右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//外部触发转换选择,就是触发控制的触发源,我们使用软件触发,所以就不启用外部触发
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//连续转换模式,可以选择连续转换还是单次转换
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//扫描转换模式,可以选择扫描模式还是非扫描模式
ADC_InitStructure.ADC_NbrOfChannel = 1;//通道数目,指定在扫描模式下总共会用到几个通道,非扫描模式下,这个参数没有用
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);//开始复位校准,会把CR2_RSTCAL_Set置一
while(ADC_GetResetCalibrationStatus(ADC1));//返回复位校准状态,所以要等待复位校准完成的话还需要加上while循环,会读取CR2_RSTAL_Set,如果变为0说明复位校准完成。
ADC_StartCalibration(ADC1);//启动校准
while(ADC_GetCalibrationStatus(ADC1));//等待校准完成
}
uint16_t AD_GetValue(void)
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//软件触发ADC
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));//规则组转换成功标志位,会在读取ADC_DR即结果后自动清除
return ADC_GetConversionValue(ADC1);//会自动清零EOC位
}
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//配置ADC是独立模式,还是双ADC模式
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//指定ADC是左对齐还是右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//外部触发转换选择,就是触发控制的触发源,我们使用软件触发,所以就不启用外部触发
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//连续转换模式,这里开启连续转换
ADC_InitStructure.ADC_ScanConvMode = DISABLE;//扫描转换模式,可以选择扫描模式还是非扫描模式
ADC_InitStructure.ADC_NbrOfChannel = 1;//通道数目,指定在扫描模式下总共会用到几个通道,非扫描模式下,这个参数没有用
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);//开始复位校准,会把CR2_RSTCAL_Set置一
while(ADC_GetResetCalibrationStatus(ADC1));//返回复位校准状态,所以要等待复位校准完成的话还需要加上while循环,会读取CR2_RSTAL_Set,如果变为0说明复位校准完成。
ADC_StartCalibration(ADC1);//启动校准
while(ADC_GetCalibrationStatus(ADC1));//等待校准完成
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//软件触发ADC,连续模式,只需要触发一次就可以了
}
uint16_t AD_GetValue(void)
{
return ADC_GetConversionValue(ADC1);//不需要判断标志位了,数据寄存器会不断刷新最新的转换结果
}
也可以通过连续转换模式,自动开始下一轮AD转换。就不需要再判断这次转换是否完成了。
而主函数的话都是一样的:
#include “stm32f10x.h” // Device header
#include “Delay.h”
#include “OLED.h”
#include “AD.h”
uint16_t ADValue;
float Voltage;
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1, 1, “ADValue:”);
OLED_ShowString(2, 1, “Voltage:0.00V”);
while (1)
{
ADValue = AD_GetValue();
Voltage = (float)ADValue / 4095 * 3.3;
OLED_ShowNum(1, 9, ADValue, 4);
OLED_ShowNum(2, 9, Voltage, 1);
OLED_ShowNum(2, 11, (uint16_t)(Voltage * 100) % 100, 2);
Delay_ms(100);
}
}
输出后的值会产生抖动,如果想对这个值进行判断,再执行一些操作,比如光线的AD值小于某一阈值时,就开灯,大于某一阈值就开灯,可能会出先这样的情况,比如光线逐渐变暗,AD值逐渐变小,但由于波动,AD值会在判断阈值附近来回跳变,会导致输出产生抖动,来回开灯关灯。
可以使用迟滞比较的方法来完成,设置两个阈值,低于下限时,开灯,高于上阈值时再关灯,可以避免输出抖动的问题,跟我们的GPIO那一节的施密特触发器是一个原理
还可以采用滤波的方法,让AD值平滑一些,比如均值滤波,读取10个或20个值,取平均值,作为滤波的AD值,或者还可以裁剪分辨率,把数据的尾数去掉。
使用单次转换,非扫描模式来实现多通道,只需要在每次触发转换之前,手动更改一下列表第一个位置的通道就行了
初始化函数部分区别不大,只是把获取数据的函数更改了一下,改成了单独获取某一通道的数据的函数:
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//软件触发ADC
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));//规则组转换成功标志位,会在读取ADC_DR即结果后自动清除
return ADC_GetConversionValue(ADC1);
}
最后再用主函数调用:
#include “stm32f10x.h” // Device header
#include “Delay.h”
#include “OLED.h”
#include “AD.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);
}
}

这两天还是学的比平时多的,毕竟周末没课,要好好把握。其实周六晚上还是放松了一下,一天也只学了两个课时,今天周日就学的多一些,三个课时,内容ADC也比昨天稍微复杂一些。但感觉还是有一些时间没有利用好,比如午觉感觉睡得太久了,睡了有两个小时了,起来迷迷糊糊又刷了一会手机,好多时间就流逝了。以后周末要注意一下,要不就不睡午觉也行,晚上多睡一会。但我想下午去跑跑步,不睡觉又怕跑步状态不好。感觉还是控制一下午觉时间吧,最好能够一小时以内。话说这两天没把笔记内容上传上来,可不是没记啊,还是认认真真记了的。只是刚好又要写代码部分的就没传。

今日推荐歌曲:ホーリーダンス——sakanaction(点击左下角倒数第十首即可收听)