第十章 ADC:逐次逼近、采样时间与多通道转换

1520 字 · 约 4 分钟学习笔记#STM32#嵌入式#学习笔记#单片机

10.1 [ADC]逐次逼近型ADC

ADC是什么

Analog(模拟信号)、Digital(数字信号)、Converter(转换器)

单片机里的ADC模块

采样深度的概念

采样深度:用多少位二进制数表示转换结果

逐次逼近型ADC简介

采样保持电路

闭合开关:采样阶段

断开开关:保持阶段

案例

10.2 [ADC]ADC模块的结构框图

ADC的多路复用

常规序列

注入序列

案例

10.3 [ADC]采样时间和转换时间

ADC的时钟频率

若不改变初始设置,PCLK2 = 72MHz / 6 = 12MHz

转换时间的计算方法

采样时间和信号源内阻的关系

信号源内阻的计算方法

采样时间的计算方法

10.4 [ADC]常规单通道转换

初始化IO引脚

配置ADC的时钟

ADC的编程接口

序号 分类 编程接口 作用
1


通用
ADC_Init 设置 ADC 的基本参数
2 ADC_Cmd 总开关
3 ADC_GetFlagStatus 获取标志位的值
4 ADC_ClearFlag 清除标志位
5 ADC_ITConfig 配置中断
6 ADC_DMACmd 配置 DMA 请求
7 ADC_TempSensorVrefintCmd 温度计和参考电压开关
8

常规序列
ADC_RegularChannelConfig 配置常规序列的通道
9 ADC_ExternalTrigConvCmd 外部触发开关(常规)
10 ADC_SoftwareStartConvCmd 软件启动(常规)
11 ADC_GetConversionValue 读取 DR 寄存器的值
12


注入序列
ADC_InjectedSequencerLengthConfig 设置注入序列的长度
13 ADC_InjectedChannelConfig 配置注入序列的通道
14 ADC_ExternalTrigInjectedConvCmd 外部触发开关(注入)
15 ADC_ExternalTrigInjectedConvConfig 选择注入序列的外部触发
16 ADC_SetInjectedOffset 设置注入序列的通道偏置
17 ADC_SoftwareStartInjectedConvCmd 软件启动(注入)
18 ADC_GetInjectedConversionValue 读取 JDRx 寄存器的值
19
校准相关
ADC_ResetCalibration 复位校准寄存器
20 ADC_GetResetCalibrationStatus 查询复位是否完成
21 ADC_StartCalibration 启动校准
22 ADC_GetCalibrationStatus 查询校准是否完成

初始化ADC的基本参数

一般使用右对齐

配置常规序列

闭合ADC的总开关

启动并读取转换结果

完整实验代码

#include "stm32f10x.h"

void App_ADC1_Init(void);
void App_OnBoardLED_Init(void);

int main(void)
{
    App_ADC1_Init();
    App_OnBoardLED_Init();
    
    while(1)
    {
        // #1. 清除EOC标志位
        ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
        
        // #2. 通过软件启动的方式发送脉冲
        ADC_SoftwareStartConvCmd(ADC1, ENABLE);
        
        // #3. 等待常规序列转换完成
        while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
        
        // #4. 读取转换的结果
        uint16_t dr = ADC_GetConversionValue(ADC1);

        // #5. 把结果转换成电压
        float voltage = dr * (3.3f / 4095);
        
        if(voltage > 1.5)
        {
            GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
        }
        else
        {
            GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
        }
    }
}

void App_ADC1_Init(void)
{
    // #1. 初始化PA0引脚,模拟模式
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // #2. 配置ADC模块的时钟
    RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 六分频
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    
    // #3. 初始化ADC的基本参数
    ADC_InitTypeDef ADC_InitStruct = {0};
    
    ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; // 关闭连续模式
    ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; // 右对齐
    ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; // 独立模式
    ADC_InitStruct.ADC_NbrOfChannel = 1; // 常规序列1个通道
    ADC_InitStruct.ADC_ScanConvMode = DISABLE;
    
    ADC_Init(ADC1, &ADC_InitStruct);

    // #4. 配置常规序列
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_13Cycles5);
    
    ADC_ExternalTrigConvCmd(ADC1, ENABLE);
    
    // #5. 闭合ADC的总开关
    ADC_Cmd(ADC1, ENABLE);
}

void App_OnBoardLED_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
    
    GPIO_Init(GPIOC, &GPIO_InitStruct);
}

10.5 [ADC]定时器触发

连接电路

初始化串口

配置定时器1的TRGO

注入序列的编程接口

序号 分类 编程接口 作用
1


通用
ADC_Init 设置 ADC 的基本参数
2 ADC_Cmd 总开关
3 ADC_GetFlagStatus 获取标志位的值
4 ADC_ClearFlag 清除标志位
5 ADC_ITConfig 配置中断
6 ADC_DMACmd 配置 DMA 请求
7 ADC_TempSensorVrefintCmd 温度计和参考电压开关
8

常规序列
ADC_RegularChannelConfig 配置常规序列的通道
9 ADC_ExternalTrigConvCmd 外部触发开关(常规)
10 ADC_SoftwareStartConvCmd 软件启动(常规)
11 ADC_GetConversionValue 读取 DR 寄存器的值
12


注入序列
ADC_InjectedSequencerLengthConfig 设置注入序列的长度
13 ADC_InjectedChannelConfig 配置注入序列的通道
14 ADC_ExternalTrigInjectedConvCmd 外部触发开关(注入)
15 ADC_ExternalTrigInjectedConvConfig 选择注入序列的外部触发
16 ADC_SetInjectedOffset 设置注入序列的通道偏置
17 ADC_SoftwareStartInjectedConvCmd 软件启动(注入)
18 ADC_GetInjectedConversionValue 读取 JDRx 寄存器的值
19
校准相关
ADC_ResetCalibration 复位校准寄存器
20 ADC_GetResetCalibrationStatus 查询复位是否完成
21 ADC_StartCalibration 启动校准
22 ADC_GetCalibrationStatus 查询校准是否完成

初始化ADC(注入序列)

初始化IO引脚

配置时钟

配置ADC的基本参数

配置注入序列

闭合总开关

读取并发送测量结果

完整实验代码

#include "stm32f10x.h"
#include "usart.h"

void App_USART1_Init(void);
void App_TIM1_Init(void);
void App_ADC_Init(void);

int main(void)
{
    App_USART1_Init();
    App_TIM1_Init();
    App_ADC_Init();
    
    while(1)
    {
        // #1. 等待注入序列转换完成
        while(ADC_GetFlagStatus(ADC1, ADC_FLAG_JEOC) == RESET);
        
        // #2. 读取转换的结果
        uint16_t jdr1 = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);
        
        // #3. 清除JEOC标志位
        ADC_ClearFlag(ADC1, ADC_FLAG_JEOC);
        
        // #4. 把结果转换成电压
        float voltage = jdr1 * (3.3f / 4095);
        
        // #5. 通过串口把结果发送出去
        My_USART_Printf(USART1, "%.3f\n", voltage);
    }
}

void App_USART1_Init(void)
{
    // #1. 初始化IO引脚,PA9
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // #2. 开启USART1的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    // #3. 配置串口的参数
    USART_InitTypeDef USART_InitStruct = {0};
    
    USART_InitStruct.USART_BaudRate = 115200;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStruct.USART_Mode = USART_Mode_Tx;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    USART_Init(USART1, &USART_InitStruct);

    // #4. 闭合串口的总开关
    USART_Cmd(USART1, ENABLE);
}

void App_TIM1_Init(void)
{
    // #1. 使能TIM1的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
    
    // #2. 配置TIM1的时基单元
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct = {0};
    
    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStruct.TIM_Period = 999;
    TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
    TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;

    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStruct);

    // #3. TRGO:Update
    TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);
    
    // #4. 开启定时器1的总开关
    TIM_Cmd(TIM1, ENABLE);
}

void App_ADC_Init(void)
{
    // #1. 初始化IO引脚
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;	// 模拟模式

    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // #2. 配置ADC模块的时钟
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

    // #3. 配置ADC的基本参数
    ADC_InitTypeDef ADC_InitStruct = {0};
    
    ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStruct.ADC_NbrOfChannel = 1;
    ADC_InitStruct.ADC_ScanConvMode = DISABLE;
    
    ADC_Init(ADC1, &ADC_InitStruct);

    // #4. 配置注入序列的额外参数
    ADC_InjectedSequencerLengthConfig(ADC1, 1);
    
    ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_T1_TRGO);
    
    ADC_ExternalTrigInjectedConvCmd(ADC1, ENABLE);

    ADC_InjectedChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_13Cycles5);

    // #5. 闭合总开关
    ADC_Cmd(ADC1, ENABLE);
}

10.6 [ADC]扫描模式

完整实验代码

#include "stm32f10x.h"
#include "usart.h"

void App_USART1_Init(void);
void App_TIM1_Init(void);
void App_ADC1_Init(void);

int main(void)
{
    App_USART1_Init();
    App_TIM1_Init();
    App_ADC1_Init();
    
    while(1)
    {
        // #1. 等待转换完成 JEOC
        while(ADC_GetFlagStatus(ADC1, ADC_FLAG_JEOC) == RESET);
        
        // #2. 把转化的结果读出来
        uint16_t jdr1 = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);
        uint16_t jdr2 = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_2);
        
        float v1, v2;
        
        v1 = jdr1 * (3.3f / 4095);
        v2 = jdr2 * (3.3f / 4095);
        
        // #3. 把结果通过串口发送出去
        My_USART_Printf(USART1, "%.3f,%.3f\n", v1, v2);
    }
}

void App_USART1_Init(void)
{
    // #1. 初始化IO引脚,PA9 AF_PP
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
    
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // #2. 开启USART1的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    
    // #3. 配置USART1的参数
    USART_InitTypeDef USART_InitStruct = {0};
    
    USART_InitStruct.USART_BaudRate = 115200;
    USART_InitStruct.USART_Mode = USART_Mode_Tx;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    
    USART_Init(USART1, &USART_InitStruct);

    // #4. 闭合USART1的总开关
    USART_Cmd(USART1, ENABLE);
}

void App_TIM1_Init(void)
{
    // #1. 开启定时器1的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);

    // #2. 设置时基单元的参数
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct = {0};

    TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInitStruct.TIM_Period = 999;
    TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
    TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;

    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStruct);

    // #3. 将TRGO设置为Update模式
    TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);
    
    // #4. 闭合TIM1的总开关
    TIM_Cmd(TIM1, ENABLE);
}

void App_ADC1_Init(void)
{
    // #1. 初始化IO引脚 PA0 PA1 -> AIN
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;

    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // #2. 配置ADC的时钟
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

    // #3. 为ADC1初始化基本参数
    ADC_InitTypeDef ADC_InitStruct = {0};
    
    ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStruct.ADC_NbrOfChannel = 1;
    ADC_InitStruct.ADC_ScanConvMode = ENABLE;
    
    ADC_Init(ADC1, &ADC_InitStruct);

    // #4. 配置注入序列
    ADC_InjectedSequencerLengthConfig(ADC1, 2);
    
    ADC_InjectedChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_13Cycles5);
    ADC_InjectedChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_13Cycles5);
    
    ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_T1_TRGO);
    
    ADC_ExternalTrigInjectedConvCmd(ADC1, ENABLE);
    
    // #5. 闭合ADC总开关
    ADC_Cmd(ADC1, ENABLE);
}

本文由 FormerlyXZ 创作, 采用CC BY-NC-SA 4.0 许可协议。转载请注明出处,禁止商业使用。

作者

评论

评论由 Giscus提供。首次评论需要在评论框里登录一次 GitHub(与页头的站点登录互相独立,这是浏览器安全限制), 之后当前浏览器会记住,不用反复登录;评论框里可以直接粘贴图片、拖入 PDF 等附件(GitHub 会帮你存好)。

还没有在播放

点碟片开始播放

0:00
0:00