本文最后更新于231天前,其中的信息可能已经有所发展或是发生改变。
AD单通道&AD多通道应用程序示例
ADC 常用库函数
- ADC的RCC时钟配置函数
该配置函数定义存放在stm32f10x_rcc.h
文件中,用来配置ADCCLK分频器。它可以对APB2的72MHz时钟选择2、4、6、8分频,输出到ADCCLK。 void RCC_ADCCLKConfig(uint32_t RCC_PCLK2) // 恢复ADC缺省配置
// 恢复ADC缺省配置
void ADC_DeInit(ADC_TypeDef* ADCx);
// ADC初始化
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
// ADC配置结构体初始化
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);
// ADC上电工作函数,即开关控制函数
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
// ADC开启DMA输出信号
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);
// ADC中断输出控制函数
void ADC_ITConfig(ADC_TypeDef* ADCx, uint16_t ADC_IT, FunctionalState NewState);
// 下面4个函数用于ADC工作前的校准操作,在ADC初始化完成后依次调用即可
// ADC复位校准
void ADC_ResetCalibration(ADC_TypeDef* ADCx);
// ADC获取复位校准状态
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);
// ADC开始校准
void ADC_StartCalibration(ADC_TypeDef* ADCx);
// ADC获取开始校准状态
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);
// ADC软件触发转换,给CR2的SWSTART置1(开始转换后立即自动清0)
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
// ADC获取软件触发状态,获取CR2的SWSTART(开始转换规则通道)位
// 不能用它判断转换是否结束,一般不用,了解即可
FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);
// ADC规则组通道配置,给转换序列的每个位置填写指定的通道
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获取转换值,获取AD转换的数据寄存器
uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx);
// ADC获取双模式转换值,读取双ADC模式下ADC的转换结果
uint32_t ADC_GetDualModeConversionValue(void);
// ADC温度传感器、内部参考电压控制,开启内部的两个转换通道
void ADC_TempSensorVrefintCmd(FunctionalState NewState);
// 下面的函数与操作标志位寄存器状态有关
// ADC获取标志位状态,可通过获取EOC标志位判断转换是否结束
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);
- 模拟看门狗配置(本节暂不涉及,需要可以了解)
// 对模拟看门狗进行配置
// 是否启动模拟看门狗
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_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);
- ADC间断模式配置
// 下面两个函数用来配置STM32中ADC的间断模式
// 配置每隔几个通道间断依次
void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, uint8_t Number);
// 开启间断模式
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
ADC单通道
AD.c
#include "stm32f10x.h" // Device header
/**
* @brief ADC初始化函数(单次转换非扫描实现多通道转换,软件触发,且这里不使用模拟看门狗和中断)
* @param 无
* @retval 无
*/
void AD_Init(void)
{
//配置时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
//配置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;
GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_Initstructure);
//选择规则组的输入通道
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);//ADC1,选择通道0,选择序列1,设置采样时间就是55.5个ADCCLK的周期
/*
如果你还想在序列列2的位置与入其他的通道,那就复制一下这个代码,把这个序列数改成2,然后指定你想要的通道,比如通道3/通道8/通道10,
如果还想继续填充菜单,那就再复制,修改序列和通道,这样就可以了
ADC_RegularChannelConfig(ADC1,ADC_Channel_3,2,ADC_SampleTime_55Cycles5);
*/
//用结构体初始化ADC
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式,配置ADC是工作在独立模式,还是双ADC模式
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发转换选择,即触发控制的触发源,外部触发None,就是不使用外部触发,也就是使用内部软件触发的意思,本小节使用使用软件触发,
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //连续转换模式,这个可以选择是连续转换还是单次转换
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //扫描转换模式,这个可以选择是扫描模式还是非扫描模式
ADC_InitStructure.ADC_NbrOfChannel = 1; //通道数目,这个是指定在扫描模式下,总共会用到几个通道,在非扫描的模式下,无论写多少数目,最终都是只有序列1的位置有效。
ADC_Init(ADC1,&ADC_InitStructure);
//中断和模拟看门狗,你如果需要的话,可以在这里继续配置
//开启ADC电源
ADC_Cmd(ADC1,ENABLE);
//ADC校准
/*
第一步,调用第一个函数,复位校准。void ADC_ResetCalibration(ADC_TypeDef* ADCx);
第二步,调用第二个函数,等待复位校准完成 FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);
第三步,调用第三个函数,开始校准 void ADC_StartCalibration(ADC_TypeDef* ADCx);
第四步,调用第四个函数,等待校准完成 FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);
*/
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)==SET) ;
//获取的标志位和是否校准完成是怎样的对应关系
//返回值说明是,ADC复位校准寄存器的状态,SET或RESET. 它获取的就是CR2寄存器里的RSTCAL标志位,该位由软件设置并由硬件清除。在校准寄存器被初始化后该位将被清除。
//软件置该位为1,那硬件就会开始复位校准,当复位校准完成后,该位就会由硬件自动清0,
//所以在读取这一位的时候,如果它是1,那就需要一直空循环等待
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1)==SET) ;
}
/**
* @brief ADC结果读取函数(单次转换非扫描实现多通道转换,软件触发)
* @param 无
* @retval 转换之后的结果
*/
uint16_t AD_GetValue(void) //按单次转换,非扫描模式进行
{
//首先,软件触发转换。然后等待转换完成,也就是等待EOC标志位置1。最后,读取ADC数据寄存器
ADC_SoftwareStartConvCmd(ADC1,ENABLE); //软件触发转换的函数
while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==RESET);
/*
返回值的标志位SET、RESET和转换是否完成的对应关系是怎样的呢
手册寄存器描述,状态寄存器里,有EOC转换结束标志位。
获取的就是这个EOC标志位,硬件在(规则或注入)通道组转换结束时设置,
这一位由软件清除或由读取ADC_DR时清除.
一般EOC标志位置1,我们就会来读取数据.
通道的采样周期是55.5,转换周期是固定的12.5。共68个周期,
前边配置的ADCCLK是72MHz的6分频,就是12MHz,12MHz进行68个周期,转换才能完成 1/12M*68≈5.6us。
*/
return ADC_GetConversionValue(ADC1);
}
单通道连续转换,非扫描模式
将 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
改为 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
连续转换仅需要在最始触发一次就行,
所以将 软件触发转换 ADC_SoftwareStartConvCmd(ADC1,ENABLE);改到初始化的最后,
在初始化完成之后,触发一次就行了 这时,内部的ADC就会一次接着一次地、连续不断地 对我们指定的通道0进行转换,转换结果放在数据寄存器里,转换结果放在数据寄存器里 此时数据寄存器会不断地刷新最新的转换结果 所以在获取AD值时,不需要判断标志位了
删除
main.c 主.c
#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:00.00V");
while (1)
{
ADValue = AD_GetValue();
Voltage = (float)ADValue/4095 * 3.3; // 整数除以小数会舍弃小数部分
OLED_ShowNum(1,9,ADValue,4);
OLED_ShowNum(2,9,Voltage,2); // 显示整数部分
OLED_ShowNum(2,12,(uint16_t)(Voltage * 100) % 100,2); // 显示小数部分
Delay_ms(100);
}
}
ADC 多通道转换
AD.c
#include "stm32f10x.h" // Device header
/**
* @brief ADC初始化函数(单次转换非扫描实现多通道转换,软件触发,且这里不使用模拟看门狗和中断)
* @param 无
* @retval 无
*/
void AD_Init(void)
{
// 1. RCC开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6); // ADCCLK = 72MHz / 6 = 12MHz
// 2. 配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 3. 将指定的GPIO端口接入规则组列表中
/* 这里要在每一次转换前都更改转换列表中要转换的GPIO端口 */
// 4. 配置ADC
ADC_InitTypeDef ADC_InitStruct;
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; // ADC模式(独立模式或双ADC模式):独立模式
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; // ADC数据对齐:右对齐
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // ADC外部触发源选择:不使用外部源触发(这里使用软件触发)
ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; // ADC连续转换模式:单次转换
ADC_InitStruct.ADC_ScanConvMode = DISABLE; // ADC扫描模式:非扫描
ADC_InitStruct.ADC_NbrOfChannel = 1; // 扫描模式下通道的数量
ADC_Init(ADC1, &ADC_InitStruct);
/* 中断和模拟看门狗在此配置 */
// 5. 开关控制
ADC_Cmd(ADC1, ENABLE);
// 6. 对ADC进行校准
ADC_ResetCalibration(ADC1); // 复位校准
while (ADC_GetResetCalibrationStatus(ADC1) == SET); // 等待复位校准完成
ADC_StartCalibration(ADC1); // 开始校准
while (ADC_GetCalibrationStatus(ADC1) == SET); // 等待校准完成
}
/**
* @brief ADC结果读取函数(单次转换非扫描实现多通道转换,软件触发)
* @param 无
* @retval 转换之后的结果
*/
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5); // 把通道作为参数填入序列1中,通道的采样周期是55.5个ADCCLK的周期
// 1. 软件触发开启转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
// 2. 等待转换完成(获取标志位状态,等待EOC标志位置1)
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); // 转换未完成则等待(55.5T + 12.5T = 68T,结果大概为5.6us)
// 3. 读取ADC数据寄存器并返回
return ADC_GetConversionValue(ADC1); // 读取之后会自动清除EOC标志位
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t AD0, AD1, AD2, AD3;
int main()
{
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);
}
}
点击数:10