AD单通道接线图
滑动变阻器的内部结构
左边和右边的两个引脚接的是电阻的两个固定端,中间这个引脚接的是滑动抽头,电位器外边这里有个十字形状的槽可以拧,往左拧,抽头就往左靠,往右拧,抽头就往右靠。所以外围电路这里,我们把左边的固定端接在负极,右边的固定端接在正极,中间就可以输出从负极到正极可调的电压了,然后右边这里我们把可调的电压输出,接在PA0口,在这里,根据引脚定义表,PA0到PB1这10个引脚是ADC的10个通道,所以这10个引脚你可以任意选,接在哪个都行
根据结构框图写代码
我们把这个结构打通, 在原理上能够让这个外设运转起来,那该怎么配置,自己心里就应该有数了
第一步,开启RCC时钟,包括ADC和GPIO的时钟,另外这里ADCCLK的分频器,也需要配置一下
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //开启ADC1的时钟,ADC都是APB2上的设备
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //因为我们要使用PA0口,所以得开启GPIOA的时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //6分频,这个PCLK2就是APB2时钟的意思,分频之后,ADCCLK=72MHz/6=12MHz
RCC_ADCCLKConfig
/**
* @brief Configures the ADC clock (ADCCLK).
* @param RCC_PCLK2: defines the ADC clock divider. This clock is derived from
* the APB2 clock (PCLK2).
* This parameter can be one of the following values:
* @arg RCC_PCLK2_Div2: ADC clock = PCLK2/2
* @arg RCC_PCLK2_Div4: ADC clock = PCLK2/4
* @arg RCC_PCLK2_Div6: ADC clock = PCLK2/6
* @arg RCC_PCLK2_Div8: ADC clock = PCLK2/8
* @retval None
*/
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2)
第二步,配置GPIO把需要用的GPIO配置成模拟输入的模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入模式,在AIN模式下,GPIO口是无效的,断开GPIO,防止GPIO口的输入输出对模拟电压造成干扰,所以AIN模式就是ADC的专属模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //A0口
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
这样PA0口就被初始化成了模拟输入的引脚了
第三步,配置这里的多路开关,把左边的通道接入到右边的规则组列表里,这个过程就是我们之前说的点菜,把各个通道的菜,列在菜单里。
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
第四步,配置ADC转换器,在库函数里,是用结构体来配置的,可以配置这一大块电路的参数
包括ADC是单次转换还是连续转换、 扫描还是非扫描、 有几个通道,触发源是什么,数据对齐是左对齐还是右对齐,这一大批参数,用一个结构体配置就可以了。
然后上面这些, 如果你需要模拟看门狗,那会有几个函数用来配置阈值和监测通道的,如果你想开启中断,那就在中断输出控制里用ITConfig函数开启对应的中断输出,然后再在NVIC里,配置一下优先级,这样就能触发中断了,不过这一块,模拟看门狗和中断,我们本节暂时不用。
开关控制,调用一下ADC_Cmd函数,开启ADC,这样ADC就配置完成了,就能正常工作了,当然,在开启ADC之后,根据手册里的建议,我们还可以对ADC进行一下校准, 这样可以减小误差,那在ADC工作的时候,如果想要软件触发转换,那会有函数可以触发,如果想读取转换结果, 那也会有函数可以读取结果,这个等会儿介绍库函数的时候就可以看到了
首先我们看一下ADCCLK的配置函数,这是在RCC里面的
void RCC_ADCCLKConfig(uint32_t RCC_PCLK2);// 这个函数是用来配置ADCCLK分频器的,它可以对APB2的72MHZ时钟选择2、4、6、8分频,输入到ADCCLK,这就是这个函数的作用,是在RCC库函数里面的,别忘了配置了
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_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);//给ADC上电,也就是PPT这里的开关控制
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);//开启DMA输出信号,如果使用DMA转运数据,那就得调用这个函数
void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);//中断输出控制,也就是PPT的右上角,用于控制某个中断,能不能通往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);//获取软件开始转换状态,从名字上来看,这个函数好像是判断转换是不是正在进行的,那我们是不是可以调用这个函数,来判断转换是否已经结束呢,答案是不行的,可以看看源码,这个函数就是用来获取CR2的SWSTART这一位,然后再对照手册的寄存器描述,这一位的作用是,开始转换规则通道,由软件设置该位以启动转换,转换开始后硬件马上清除此位,所以这里,ADC_SoftwareStartConvCmd函数就是给SWSTART位置1,以开始转换的,本返回SWSTART的状态,由于SWSTART位在转换开始后立刻就清零了,所以这个函数的返回值跟转换是否结束,毫无关系,那如何才能知道转换是否结束呢?我们需要用到下面的ADC_GetFlagStatus函数,获取标志位状态,然后参数给EOC的标志位,判断EOC标志位是不是置1了,如果转换结束,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);//ADC规则组通道配置,这个函数比较重要,它的作用就是这里(转换模式那个序列i一格一格的),给序列的每个位置填写指定的通道,就是填写点菜菜单的过程,第一个参数是ADCx,第二个ADCChannel 就是你想指定的通道,第三个Rank,就是序列几的位置,然后第四个SampleTime,就是指定通道的采样时间
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模式读取转换结果的函数,我们暂时不用
以上这些函数就是对ADC的一些基本功能和规则组的配置
下面这里有一大批函数,这些函数里面都带了一个Injected,就是注入组的意思,所以这一大批函数都是对ADC注入组进行配置的,那注入组我们暂时不多讲,大家感兴趣的话可以自己看一看
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);
下面这三个函数就是对模拟看门狗进行配置的
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);//上面ADC_GetSoftwareStartConvStatus函数提了一嘴,获取标志位状态
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); //清除中断挂起位