STM32AD(单通道&多通道)

AD 转换原理图


配置AD转换所需知识
ADC输入通道
        STM32包含16个外部输入通道和两个内部通道(内部温度传感器和内部参考电压VREFINT)。

 注入通道:最多只有4个通道,每个通道都有独立的寄存器

规则通道:最多有16个通道,但是只有一位数据寄存器。所以使用多通道时,只能有一个数据存在数据寄存器中,下一个数据过来时,前一个数据就被覆盖了

ADC触发信号 

1、通用定时器触发ADC转换。

2、外部中断可以产生一个触发脉冲,触发ADC转换。

3、软件自动触发

ADC1和ADC2用于规则通道的外部触发

ADC1和ADC2用于注入通道的外部触发

ADC预分频器
        注意这里APB2预分频器最大是72MHZ,ADCCLK最大只能14MHZ,所以ADC预分频器只能6分频或8分频。

 EOC:规则组转换完成后置标志位为1,也可以去NVIC通过申请中断。

JEOC:注入组转换完成后置标志位为1,也可以去NVIC通过申请中断。

STM32C8T6ADC引脚图

AD转换模式
单次转换:每次AD转换开始前都需要触发AD转换开始信号。

连续转换:只需要触发一次AD转换开始信号。

非扫描模式:只有序列1下的通道可以使用

扫描模式:指定通道数目,通道数目可以多个,并且可以重复。

单次转换,非扫描模式

       触发转换后序列1下指定的通道开始AD转换,转换结果放在数据寄存器,同时给EOC置标志位1。每次转换都需要触发一次AD转换信号。

单次转换,连续扫描模式

        触发转换后指定通道开始AD转换,转换结果放在数据寄存器,转换完成后给EOC置标志位1。每次转换都需要触发一次AD转换信号。

连续转换,非扫描模式

        触发转换后序列1下指定的通道开始AD转换,转换结果放在数据寄存器,同时给EOC置标志位1。不需要再次触发AD转换信号即可开始下一次转换。

连续转换,连续扫描模式

        触发转换后指定通道开始AD转换,转换结果放在数据寄存器,同时给EOC置标志位1。不需要再次触发AD转换信号即可开始下一次转换。

数据对齐
        AD转换后数据的是12位,数据寄存器是16位。所以要把AD转换后的数据转换成16位的。这里一般选择右对齐,这样可以直接读取转换结果。左对齐对导致转换后结果偏大。

 

AD固函数库

配置AD单通道函数
        本次采用规则组通道,以PA0作为ADC1转换的引脚。使用6分频,单次转换非扫描模式。

GPIO配置
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);    //开启GPIOA的时钟
    /*GPIO初始化*/
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);                    //将PA0引脚初始化为模拟输入
 ADC配置
        /*开启时钟*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);    //开启ADC1的时钟
    /*设置ADC时钟*/
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);                        //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
    
    /*规则组通道配置*/
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);        //规则组序列1的位置,配置为通道0
    
    /*ADC初始化*/
    ADC_InitTypeDef ADC_InitStructure;                        //定义结构体变量
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;        //模式,选择独立模式,即单独使用ADC1
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;    //数据对齐,选择右对齐
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;    //外部触发,使用软件触发,不需要外部触发
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;        //连续转换,失能,每转换一次规则组序列后停止
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;            //扫描模式,失能,只转换规则组的序列1这一个位置
    ADC_InitStructure.ADC_NbrOfChannel = 1;                    //通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
    ADC_Init(ADC1, &ADC_InitStructure);                        //将结构体变量交给ADC_Init,配置ADC1
ADC校准
    
    /*ADC校准*/
    ADC_ResetCalibration(ADC1);                                //固定流程,内部有电路会自动执行校准
    while (ADC_GetResetCalibrationStatus(ADC1) == SET);
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1) == SET);
 ADC上电
    /*ADC使能*/
    ADC_Cmd(ADC1, ENABLE);                                    //使能ADC1,ADC开始运行
AD单通道转换初始化完整代码

 
/**
  * 函    数:AD初始化
  * 参    数:无
  * 返 回 值:无
  */
void AD_Init(void)
{
 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);    //开启GPIOA的时钟
    /*GPIO初始化*/
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);                    //将PA0引脚初始化为模拟输入
    
        /*开启时钟*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);    //开启ADC1的时钟
    /*设置ADC时钟*/
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);                        //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
    
    /*规则组通道配置*/
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);        //规则组序列1的位置,配置为通道0
    
    /*ADC初始化*/
    ADC_InitTypeDef ADC_InitStructure;                        //定义结构体变量
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;        //模式,选择独立模式,即单独使用ADC1
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;    //数据对齐,选择右对齐
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;    //外部触发,使用软件触发,不需要外部触发
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;        //连续转换,失能,每转换一次规则组序列后停止
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;            //扫描模式,失能,只转换规则组的序列1这一个位置
    ADC_InitStructure.ADC_NbrOfChannel = 1;                    //通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1

    ADC_Init(ADC1, &ADC_InitStructure);                        //将结构体变量交给ADC_Init,配置ADC1
    
    /*ADC使能*/
    ADC_Cmd(ADC1, ENABLE);                                    //使能ADC1,ADC开始运行
    
    /*ADC校准*/
    ADC_ResetCalibration(ADC1);                                //固定流程,内部有电路会自动执行校准
    while (ADC_GetResetCalibrationStatus(ADC1) == SET);
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1) == SET);
}
读取AD转换后的数据
uint16_t AD_GetValue(void)
{
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);                    //软件触发AD转换一次
    while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);    //等待EOC标志位,即等待AD转换结束
    return ADC_GetConversionValue(ADC1);                    //读数据寄存器,得到AD转换的结果
}
 实物演示


 ADC多通道函数
GPIO配置
/*开启时钟*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);    //开启ADC1的时钟
    /*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);                    //将PA0、PA1、PA2和PA3引脚初始化为模拟输入
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);    //开启GPIOA的时钟
ADC配置
        这里采用6分频,多通道,单次非扫描模式

    /*设置ADC时钟*/
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);                        //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
    
    /*不在此处配置规则组序列,而是在每次AD转换前配置,这样可以灵活更改AD转换的通道*/
    
    /*ADC初始化*/
    ADC_InitTypeDef ADC_InitStructure;                        //定义结构体变量
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;        //模式,选择独立模式,即单独使用ADC1
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;    //数据对齐,选择右对齐
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;    //外部触发,使用软件触发,不需要外部触发
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;        //连续转换,失能,每转换一次规则组序列后停止
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;            //扫描模式,失能,只转换规则组的序列1这一个位置
    ADC_InitStructure.ADC_NbrOfChannel = 1;                    //通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
    ADC_Init(ADC1, &ADC_InitStructure);                        //将结构体变量交给ADC_Init,配置ADC1
    
    /*ADC使能*/
    ADC_Cmd(ADC1, ENABLE);                                    //使能ADC1,ADC开始运行
    
    /*ADC校准*/
    ADC_ResetCalibration(ADC1);                                //固定流程,内部有电路会自动执行校准
    while (ADC_GetResetCalibrationStatus(ADC1) == SET);
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1) == SET);
AD多通道转换初始化完整代码 (实际也不完整,没有分别取4路通道AD转化值的部分,而下面文章江科大中有这部分,可以参考2024.3.27)

/**
  * 函    数:AD初始化
  * 参    数:无
  * 返 回 值:无
  */
void AD_Init(void)
{
    /*开启时钟*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);    //开启ADC1的时钟
    /*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);                    //将PA0、PA1、PA2和PA3引脚初始化为模拟输入
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);    //开启GPIOA的时钟
    
    /*设置ADC时钟*/
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);                        //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
    
    /*不在此处配置规则组序列,而是在每次AD转换前配置,这样可以灵活更改AD转换的通道*/
    
    /*ADC初始化*/
    ADC_InitTypeDef ADC_InitStructure;                        //定义结构体变量
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;        //模式,选择独立模式,即单独使用ADC1
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;    //数据对齐,选择右对齐
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;    //外部触发,使用软件触发,不需要外部触发
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;        //连续转换,失能,每转换一次规则组序列后停止
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;            //扫描模式,失能,只转换规则组的序列1这一个位置
    ADC_InitStructure.ADC_NbrOfChannel = 1;                    //通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1

    ADC_Init(ADC1, &ADC_InitStructure);                        //将结构体变量交给ADC_Init,配置ADC1
    
    /*ADC使能*/
    ADC_Cmd(ADC1, ENABLE);                                    //使能ADC1,ADC开始运行
    
    /*ADC校准*/
    ADC_ResetCalibration(ADC1);                                //固定流程,内部有电路会自动执行校准
    while (ADC_GetResetCalibrationStatus(ADC1) == SET);
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1) == SET);
}
读取AD转换后的数据
 
/**
  * 函    数:获取AD转换的值
  * 参    数:ADC_Channel 指定AD转换的通道,范围:ADC_Channel_x,其中x可以是0/1/2/3
  * 返 回 值:AD转换的值,范围:0~4095
  */
uint16_t AD_GetValue(uint8_t ADC_Channel)
{
    ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);    //在每次转换前,根据函数形参灵活更改规则组的通道1
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);                    //软件触发AD转换一次
    while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);    //等待EOC标志位,即等待AD转换结束
    return ADC_GetConversionValue(ADC1);                    //读数据寄存器,得到AD转换的结果
}
实物演示

————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/ww20200711/article/details/136744113

 【江科大】STM32:ADC转换(单通道+多通道)

文章目录
ADC(Analog-Digital Converter)模拟-数字转换器
DAC的实现原理
逐次逼近的过程
知识点补充:
RC振荡器和锁相环(PLL)
晶体振荡器
RTC(Real-Time Clock)即实时时钟
Reset and clock control (RCC),即复位与时钟控制,主要是通过寄存器配置时钟源。
STM32的时钟源
ADC预分频器来自于RCC ,2,4分频后分别是32,和18,最大16MHZ 因此只能选择6 和8 12/9
模拟看门狗
ADC基本结构
输入通道
规则组4种转换模式
1. 单次转换,非扫描模式
2. 连续转换,非扫描模式
3. 单次转换,扫描模式
4. 连续转换,扫描模式
触发控制(触发源)
1.触发源选择
2.数据对齐————右对齐
3. AD转换的步骤:转换时间的计算
4. 校准:固定的,只需要在初始化完ADC,加几条代码即可
5. 数据波动的处理方案
6. ADC 选择和DMA结合,为什么不可以手动转运
7. 硬件电路
ADC+单通道
显示电压值uint16_t ADValue;
连续转换
ADC+多通道
多通道 利用单通道非扫描完成
AD.c
ADC库函数


ADC(Analog-Digital Converter)模拟-数字转换器
ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁
12位(分辨率)逐次逼近型ADC,1us转换时间(最大1MHZ)
输入电压范围:0~3.3V,转换结果范围:0–4095 (位数越高,分辨率越高) 0~3.3V 一一对应
18个输入通道,可测量16个外部(GPIO)和2个内部信号源(温度传感器,内部参考电压(基准电压,不会受外部电压影响))
规则组(用于常规事件)和注入组(用于突发事件)两个转换单元(一次启动一个组,)
模拟看门狗自动监测输入电压范围(判断AD值高于某个阈值或低于。。后,执行操作)
STM32F103C8T6 ADC资源:ADC1、ADC2,10个外部输入通道
DAC的实现原理
DAC:电压输出端:给他一个数据,它就能输出数据对应的电压,DAC内部使用加权电阻网络来实现的转换。(具体看51单片机ADDA)
外部通道输入一个未知编码的电压,DAC输出的是已知编码的电压,在电压比较器进行比较
如果DAC输出的电压比较小,就增大DAC数据,反之亦然,直到它和外部输入的基本相同。这样DAC输入的数据即使外部电压的编码数据了。
电压调节的过程就是通过逐次逼近寄存器SAR完成。
逐次逼近的过程
为了尽快找外部未知电压的编码,通常采用二分法进行寻找。
比如图中8位ADC(0–255),第一次输入255的一半,128看谁大谁小,如果大了就64.。。。。
如果用二进制来看,128,64,36这些数其实就是位权
这个判断过程就是从高位到低位依次判断1还是0的过程。1000 0000 =>0100 0000 => 0010 0000
对于8位,判断8此就可以得出DAC编码,12位就是12次.

clock:驱动内部逐次比较的时钟


如,硬件出发,ADC需要过一个固定时间转换一次,如1ms,那么定时器每隔1ms申请一次中断来触发转发转换。
由于频繁进中断会影响主程序的进行,且中断有优先级,这也会影响ADC的转换,因此必须提供硬件支持,比如TIM1_CH1定时1ms由触发信号,就产生更新事件,连接到TIM3的TRGO口,,因此TIM3的更新事件就可以通过硬件自动触发ADC转换了。


知识点补充:
RC振荡器和锁相环(PLL)
单片机内部是不会放晶振的,只有RC振荡器!

在振荡电路中的频率选择部分可以只用电阻R和电容C构成。 这种只用电阻和电容构成的振荡器称为RC振荡器 。

优点:是实现的成本比较低,毕竟就是一个电阻电容。缺点是由于电阻电容的精度问题所以RC振荡器的震荡频率会有误差,同时受到温度、湿度的影响,这个跟元器件的工艺有关。

晶体振荡器
优点是相对来说震荡频率一般都比较稳定。缺点的话就是价格要稍微高点了,还有用晶体振荡器一般还需要接两个 15-33pF起振电容。
一般单片机中很少用RC振荡器,可能在实验室环境会用,而在实际的工程、工业上很少用到,常用的也就是晶体振荡器。因为很多时候单片机需要一个精度的机器周期作定时、通讯等用途,如果震荡频

RTC(Real-Time Clock)即实时时钟
目前实时时钟芯片大多采用精度较高的晶体振荡器作为时钟源。任何实时时钟的核心都是晶振,此时晶振的作用是提供基准频率,频率为32.768kHz。相比较晶振只可以产生稳定的频率,实时时钟是以输入频率做基础,再依此作除频、倍频、PLL等等,产生出处理器与主板各部分所需的频率。
RTC是单片机的实时时钟,它需要时钟源,stm32中32 kHz 低速内部 RC (LSI RC),可选择提供给 RTC 用于停机/待机模式下的自动唤醒。32.768 kHz 低速外部晶振(LSE 晶振),用于驱动 RTC 时钟 (RTCCLK)

Reset and clock control (RCC),即复位与时钟控制,主要是通过寄存器配置时钟源。
STM32的时钟源
有五个时钟源,为HSI、HSE、LSI、LSE、PLL:
①HSI是高速内部时钟,RC振荡器,频率为8MHz。
②HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。
③LSI是低速内部时钟,RC振荡器,频率为40kHz。
④LSE是低速外部时钟,接频率为32.768kHz的石英晶体。
⑤PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。

ADC预分频器来自于RCC ,2,4分频后分别是32,和18,最大16MHZ 因此只能选择6 和8 12/9


模拟看门狗

STM32 中有双ADC模式 可以组成同步交叉模式,交叉对一个通道采样。

ADC基本结构


输入通道


规则组4种转换模式
1. 单次转换,非扫描模式
1.选择要转换的通道
2.转换完成,将转换结果放入寄存器,对于EOC置为1


2. 连续转换,非扫描模式
一次转换完成后,会接着连续不断的转换。不需要手动开启转换


3. 单次转换,扫描模式
完成一次转换,需要再次触发,才能继续转换
由于是扫描模式,需要用到菜单列表,
通道数目,如果写7,就依次对前7个通道ADC转换。防止数据被覆盖,需要及时将数据挪走。转换完成,触发EOC


4. 连续转换,扫描模式


触发控制(触发源)
1.触发源选择


2.数据对齐————右对齐
间断转换 (了解即可)数据对齐


3. AD转换的步骤:转换时间的计算
(采样,保持):在量化编码之前需要设置一个采样开关,先打开开关,收集外部电压,断开采样开关。可以保证比较时电压稳定。
(量化,编码):ADC逐次比较的过程
STM32 ADC的总转换时间为:
TCONV = 采样时间 + 12.5个ADC周期(12位)
例如:当ADCCLK=14MHz,采样时间为1.5个ADC周期
TCONV = 1.5 + 12.5 = 14个ADC周期 = 1μs

4. 校准:固定的,只需要在初始化完ADC,加几条代码即可
ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差
建议在每次上电后执行一次校准
启动校准前, ADC必须处于关电状态超过至少两个ADC时钟周期
5. 数据波动的处理方案
采用阈值,高低阈值,利用和施密特触发器一样的原理,得到平稳的正交波形
过滤器,取多个AD值的平均值,
裁剪分辨率
6. ADC 选择和DMA结合,为什么不可以手动转运
因为ADC完成转换后没有任何的标志位,不知道某一通道谁转换完了,只有在整个列表都转换完了,返回一个EOC结束标志,才触发中断
转换一个数据只需要几微秒,因此很难做到数据不会丢失。
但是可以采用间断模式,每转换一个通道就是暂停,等数据转运走了,再继续转换。

7. 硬件电路


ADC+单通道


void AD_Init(void)
{
    //给ADC和GPIO口使能
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);
    
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN; //在AIN模式下GPIO口是无效的,因此是ADC的专属模式
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;    
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    
    GPIO_Init(GPIOA,&GPIO_InitStruct);
    
    //给序列的每个位置填写对应的通道
    ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5);
    ADC_InitTypeDef ADC_InitStruect;
    ADC_InitStruect.ADC_ContinuousConvMode =DISABLE;     //单次转换还是连续
    ADC_InitStruect.ADC_DataAlign = ADC_DataAlign_Right;  //右对齐
    ADC_InitStruect.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None  ;   //软件触发,不使用外部触发启动ADC转换,触发控制的触发源
    ADC_InitStruect.ADC_Mode = ADC_Mode_Independent;//独立模式   
    ADC_InitStruect.ADC_NbrOfChannel = 1;     //通道数目
    ADC_InitStruect.ADC_ScanConvMode = DISABLE;  //扫描还是非扫描
    
    ADC_Init(ADC1,&ADC_InitStruect);
    ADC_Cmd(ADC1,ENABLE);
    
    //对ADC进行校准 复位校准
    ADC_ResetCalibration(ADC1);  //
    while(ADC_GetResetCalibrationStatus(ADC1) == SET);
    //启动校准
    ADC_StartCalibration(ADC1);
    //获取校准状态 ---是否校准完成
    while(ADC_GetCalibrationStatus(ADC1) == SET);
}

uint16_t AD_GetValue(void)//启动转换,获取结果
{
    //软件触发
    ADC_SoftwareStartConvCmd(ADC1,ENABLE);//不断初始化
    //获取标志位状态  ,在转换后获取             ==0 表示转换未完成
    while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)  == RESET);//滚则组是否转换完成
    //获取ADC转换值 ,就是读取DR数据寄存器的值
    return ADC_GetConversionValue(ADC1);  //获取已经完成转换的AD值
}
  //显示AD值
uint8_t ADValue;
int main(void)
{

    OLED_Init();
    AD_Init();
    
    OLED_ShowString(1,1,"ADValue:");

    while(1)
    {
        ADValue =AD_GetValue();
        OLED_ShowNum(1,9,ADValue,4);  //显示AD值
    }
}


显示电压值uint16_t ADValue;
float Voltage;
int main(void)
{

    OLED_Init();
    AD_Init();
    
    OLED_ShowString(1,1,"ADValue:");

    while(1)
    {
        ADValue =AD_GetValue();
        Voltage = (float)ADValue / 4095 *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);   //显示电压值,就得到小数位了
        
    }
    
}


连续转换
    //软件触发
    ADC_SoftwareStartConvCmd(ADC1,ENABLE);//每次转换完成,数据会存到数据寄存器中,然后继续执行转换
uint16_t AD_GetValue(void)  //启动转换,获取结果
{
    //获取标志位状态  ,在转换后获取             ==0 表示转换未完成
    while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)  == RESET);//
    //获取ADC转换值 ,就是读取DR数据寄存器的值
    return ADC_GetConversionValue(ADC1);  //获取已经完成转换的AD值
}

ADC+多通道


多通道 利用单通道非扫描完成
AD.c


uint16_t AD_GetValue(uint8_t ADC_Channel)
{
    //给序列的每个位置填写对应的通道
   ADC_RegularChannelConfig(ADC1,ADC_Channel,1,ADC_SampleTime_55Cycles5);
    //软件触发
    ADC_SoftwareStartConvCmd(ADC1,ENABLE);
    //获取标志位状态  ,在转换后获取             ==0 表示转换未完成
    while(ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)  == RESET);
    //获取ADC转换值 ,就是读取DR数据寄存器的值
    return ADC_GetConversionValue(ADC1);  //获取已经完成转换的AD值
}
uint16_t AD0;
uint16_t AD1;
uint16_t AD2;
uint16_t AD3;

int main(void)
{

    OLED_Init();
    AD_Init();
    
    OLED_ShowString(1,1,"AD0:");
    OLED_ShowString(1,1,"AD1:");
    OLED_ShowString(1,1,"AD2:");
    OLED_ShowString(1,1,"AD3:");

    {
        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,9,AD0,4);  //显示AD值
    OLED_ShowNum(1,9,AD1,4);  //显示AD值
        OLED_ShowNum(1,9,AD2,4);  //显示AD值
        OLED_ShowNum(1,9,AD3,4);  //显示AD值

        
    }
    
}

ADC库函数
//恢复初始状态
void ADC_DeInit(ADC_TypeDef* ADCx);
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
//给结构体一个默认值
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);
//开关控制就是给ADC上电
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
//用来开启DMA输出信号,如果是用DMA转运数据用
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);
//中断输出控制,用来控制某个中断,能不能通往NVIC
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);
//ADC软件开始转换控制   ---软件触发的函数
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);

//ADC 获取开启转换状态  ---不能判断是否转换结束 返回SWSTART的状态,
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转换值
uint32_t ADC_GetDualModeConversionValue(void);
//下面都是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);
//ADC 配置高低阈值   (看门狗)
void ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, uint16_t HighThreshold, uint16_t LowThreshold);
//配置看门通道
void ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel);
//ADC温度传感器  ,内部参考电压配置
void ADC_TempSensorVrefintCmd(FunctionalState NewState);

//获取EOC标志位是否变为1,是1就是转换结束  --判断转换是否结束
//获取标志位
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);

————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/weixin_52958292/article/details/135762821

### STM32 双通道 ADC 配置与使用 对于STM32微控制器系列中的双通道ADC配置,具体实现涉及多个方面,包括但不限于时钟源的选择、过采样的设定以及外部触发机制的定义。当涉及到特定需求如采用内部专用14MHz振荡器作为ADC时钟源,并设置过采样为7.5个ADC周期,同时利用定时器中断(例如TIM1)来启动转换并借助DMA完成数据传输时,可以遵循以下指导。 #### 一、初始化硬件资源 为了使能ADC模块及其关联外设的工作,在程序开始部分应当先开启相应的时钟树节点: ```c RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_TIM1, ENABLE); // 启用ADC1和TIM1的APB2总线时钟 ``` 接着,针对所使用的内核频率调整ADC预分频因子以确保最终得到期望的14MHz工作速率[^1]。 #### 二、配置ADC参数 接下来是对ADC本身属性进行细致调节的部分,这里主要关注输入通道选择、分辨率设定、连续模式启用与否等方面的内容。特别注意的是要按照题目描述指定三个独立模拟量输入端口——`ADC1IN1`, `ADC1IN2`, 和 `ADC1IN3`—参与测量过程;另外还需激活过采样功能并将倍数固定至7.5次循环平均处理。 ```c ADC_InitTypeDef ADC_InitStructure; ADC_StructInit(&ADC_InitStructure); /* 设置ADC分辨率为12位 */ ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; /* 开启扫描模式以便于多路采集 */ ADC_InitStructure.ADC_ScanConvMode = ENABLE; /* 关闭连续转换模式 */ ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; /* 过采样率设置为7.5 */ ADC_InitStructure.ADC_OversamplingRatio = ADC_OversamplingRatio_8; /* 实际上最接近7.5的有效值 */ /* 将上述结构体应用到实际设备中 */ ADC_Init(ADC1, &ADC_InitStructure); ``` 在此基础上进一步指明哪些物理引脚对应着计划内的传感接口,并通过调用API函数将其加入序列化列表里去等待后续读取操作。 ```c ADC_ChannelConfTypeDef sConfig = {0}; sConfig.Channel = ADC_CHANNEL_1; // 对应PA0即ADC1_IN1 sConfig.Rank = 1; HAL_ADC_ConfigChannel(&hadc1, &sConfig); sConfig.Channel = ADC_CHANNEL_2; // 对应PA1即ADC1_IN2 sConfig.Rank = 2; HAL_ADC_ConfigChannel(&hadc1, &sConfig); sConfig.Channel = ADC_CHANNEL_3; // 对应PA2即ADC1_IN3 sConfig.Rank = 3; HAL_ADC_ConfigChannel(&hadc1, &sConfig); ``` #### 三、建立触发关系 为了让ADC能够在接收到外部事件通知之后自动发起新一轮的数据获取动作而不是单纯依靠软件轮询方式运作起来,则有必要建立起两者之间的联系。本案例选用通用型计时器组件之一TIM1所产生的更新脉冲充当起始条件,从而达到同步控制的目的。 ```c ADC_EXTERNALTRIGCONV_Timing TIM_TRGSelection = ADC_EXTERNALTRIGCONV_T1_CC1; ADC📐⚗📐 📐📐📐⚗⚗⚗📐📐📐 配置完成后,还需要编写必要的回调函数用于响应来自定时器产生的中断请求,并从中提取最新一轮测量所得的结果集交给DMA引擎负责搬运存储。 最后一步就是正式开启整个链路上各个组成部分的服务状态了,这通常意味着允许全局中断的同时也解除了之前处于待命阶段的各项设施间的交互限制。 ```c __HAL_RCC_DMA2_CLK_ENABLE(); // DMA2时钟使能 HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 1); HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn); HAL_ADC_Start_DMA(&hadc1, (uint32_t*)aBuffer, BUFFER_SIZE); ``` 以上便是基于给定条件下关于如何在STM32平台上搭建一套完整的双通道ADC系统的简要说明。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值