打印
[MM32软件]

【灵动微MM32】【标准库】【固件函数库】【MM32G0001A1TC学习日记】【4】_ADC 模拟/数字转换

[复制链接]
485|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2024-8-16 08:30 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
一、简介
1.1        A/D转换器
        模拟数字转换器即A/D转换器,或简称ADC。通常的模数转换器是将一个输入电压模拟信号转换为一个输出的数字信号。由于数字信号本身不具有实际意义,仅仅表示一个相对大小。故任何一个模数转换器都需要一个参考模拟量作为转换的标准,比较常见的参考标准为最大的可转换信号大小。而输出的数字量则表示输入信号相对于参考信号的大小 。

        G0001拥有一个12位的逐次逼近式的模拟数字转换器(SAR A/D 转换器)。

        A/D 转换器支持多种工作模式:单次转换和连续转换模式,并且可以选择通道自动扫描。A/D 转换的 启动方式有软件设定、外部引脚触发以及各个定时器启动。窗口比较器 (模拟看门狗) 允许应用程序检测 输入电压是否超出了用户设定的高/低阈值值。ADC 的输入时钟不得超过 16MHz,它是由 PCLK1 经分 频产生。

1.2        主要特征
• 最高 12 位可编程分辨率的 SAR ADC,多达 8 路外部输入通道和 1 路内部通道

• 高达 1Msps 转换速率

• 支持普通工作模式:

        • 单次转换模式: A/D 转换在指定通道完成一次转换

        • 单周期扫描模式: A/D 转换在所有指定通道 (从低序号通道到高序号通道,或从高序号通道            到低序号通道)完成一个周期转换

        • 连续扫描模式: A/D 转换连续执行单周期扫描模式直到软件停止 A/D 转换。若需中途修改            转换通道只能停止 A/D 转换,配置完相关寄存器再重新开启转换。

• 支持任意通道工作模式:

        • 单次转换模式:在指定通道完成一次转换

        • 单周期扫描模式:A/D 转换在所有指定通道 (可按照任意顺序) 完成一个周期转换

        • 连续扫描模式:A/D 转换连续执行单周期扫描模式直到软件停止 A/D 转换。若想修改转换              通道,不必停止转换,可配置相应寄存器,在下一个扫描周期开始将进行新的通道 转换。

• 通道采样时间、分辨率可软件配置

• A/D 转换开始条件

        • 软件启动

        • 外部触发启动,且软件可配置外部触发延时

        • Timer1/3 匹配或 TRGO 信号,外部 EXTI 信号源

• 模拟看门狗功能。转换结果可和指定的值相比较,当转换值和设定值相匹配时,用户可设定是否产生中断请求

二、功能描述
2.1        系统框图





2.2        ADC框图



注: V_SENSOR(内部参考电压) 通道在 ADC 的 AIN8 通道。

2.3        ADC 开关控制
        通过设置 ADCFG 寄存器的 ADEN 位可给 ADC 上电。当第一次设置 ADEN 位时,它将 ADC 从 断电状态下唤醒。

        ADC 上电延迟一段时间后(大约 200ns),设置 ADCR 寄存器的 ADST 位开始进行转换。通过清除 ADST 位可以停止转换,清除 ADEN 位可置于断电模式。

2.4        通道选择
        ADC1 有 8 路外部输入通道和内部 1.2V 参考电压通道。每个外部输入通道都有独立的使能位,可通 过设置 CHANY_NUM,ADC_CHANY0、 ADC_CHANY1 来设置。

三、任意通道工作模式
2.1        单次转换模式
        在单次转换模式下,A/D 转换相应通道上只执行一次,具体流程如下:

        软件设置寄存器 ADC_ANY_CFG,ADC_CHANY0,ADC_CHANY1,设置转换通道,置位 CHANY_MDEN。(单次转换模式,只需设置 CHANY_SEL0)

        通过软件、外部触发输入及定时器溢出置位 ADCR 寄存器的 ADST,开始 A/D 转换。

        A/D 转换完成时,A/D 转换的数据值将存储于数据寄存器 ADDATA 和 ADDRn 中。

        A/D 转换完成时, 状态寄存器 ADSTA 的 ADIF 位置 1。若此时控制寄存器 ADCR 的 ADIE 位置 1,将产生 AD 转换结束中断请求。

        A/D 转换期间,ADST 位保持为 1。A/D 转换结束时,ADST 位自动清 0,A/D 转换器进入空闲模 式。

        若在 A/D 转换过程中,软件更新 ADC_ANY_CFG, ADC_CHANY0, ADC_CHANY1,硬件不会 立即更新这些配置,只会在当前设置的通道都转换结束时更新,然后等待下一次软件置位 ADST。

单次转换模式时序图:



2.2        单周期扫描模式
        在单周期扫描模式下,A/D 转换通道依软件配置执行一遍,具体流程如下:

        软件设置寄存器 ADC_ANY_CFG,ADC_CHANY0,ADC_CHANY1,将需要转换的通道、数量设置好,然后置位 CHANY_MDEN。

        通过软件、外部触发置位 ADCR 寄存器的 ADST,外部触发可软件配置触发延时,A/D 转换方向从 CHANY_SEL0 到 CHANY_SEL8 转换通道数量由 CHANY_NUM 配置, 且 CHANY_SEL0 到 CHANY_SEL8 是任意配置的,可以完全相同,或完全不相同。

        每路 A/D 转换完成时,A/D 转换的数据值将有序装载到相应通道的数据寄存器中,ADIF 转换结束 标志被设置,若此时控制寄存器 ADCR 的 ADIE 位置 1,将产生 AD 转换结束中断请求。

        A/D 转换结束后,ADST 位自动清 0,A/D 转换器进入空闲模式。

        若在 A/D 转换过程中,软件更新 ADC_ANY_CFG, ADC_CHANY0, ADC_CHANY1,硬件不会 立即更新这些配置,只会在当前设置的通道都转换结束时更新,然后等待下一次软件软件置位 ADST。

单周期扫描下通道转换时序图:



2.3        连续扫描模式
        在连续扫描模式下,A/D 转换通道依软件配置一直执行,直到软件禁止。具体流程如下:

        软件设置寄存器 ADC_ANY_CFG,ADC_CHANY0,ADC_CHANY1,将需要转换的通道、数量设 置好,然后置位 CHANY_MDEN

        通过软件、外部触发置位 ADCR 寄存器的 ADST,外部触发可软件配置触发延时,A/D 转换方向从 CHANY_SEL0 到 CHANY_SEL8 转 换 通 道 数 量 由 CHANY_NUM 配 置 ,且 CHANY_SEL0 到 CHANY_SEL8 是任意配置的,可以完全相同,或完全不相同。

        每路 A/D 转换完成时,A/D 转换的数据值将有序装载到相应通道的数据寄存器中,ADIF 转换结束 标志被设置,若此时控制寄存器 ADCR 的 ADIE 位置 1,将产生 AD 转换结束中断请求。

        只要 ADST 位保持为 1,持续进行 A/D 转换。当 ADST 位被清 0,当前 A/D 转换完成后停止, A/D 转换器进入空闲状态。

        若在 A/D 转换过程中,软件更新 ADC_ANY_CFG,ADC_CHANY0,ADC_CHANY1,硬件不会立 即更新这些配置,只会在当前设置的通道都转换结束时更新,即下一个扫描周期开始新的通道转换。

连续扫描模式通道转换时序图:



连续扫描模式动态更新配置时序图:



四、寄存器
4.1        寄存器总览



4.2        A/D 数据寄存器 (ADC_ADDATA)
地址偏移:0x00

复位值:0x00000000





4.3        A/D 配置寄存器 (ADC_ADCFG)
地址偏移:0x04

复位值:0x00000000





4.4        A/D 控制寄存器 (ADC_ADCR)
地址偏移:0x08

复位值:0x00000000





4.5        A/D 窗口比较寄存器 (ADC_ADCMPR)
地址偏移:0x10

复位值:0x00000000



4.6        A/D 状态寄存器 (ADC_ADSTA)
地址偏移:0x14

复位值:0x00000000




4.7        A/D 数据寄存器 (ADC_ADDR0 ~ 8)
地址偏移:0x18~0x38

复位值:0x00000000




4.8        A/D 任意通道通道选择寄存器 0(ADC_CHANY0)
地址偏移:0x5C

复位值:0x00000000



        注:单周期扫描或连续扫描模式下,硬件会启动 ADC_CHANY0 影子寄存器,在 ADC 未开始工作 时,软件写 ADC_CHANY0 的话,也会写到其影子寄存器;在 ADC 工作期间,若更改 ADC_CHANY0 的值,只会更新其影子寄存器,且当 ADC 开始转换最后一个通道时,影子寄存器的值会更新至 ADC_CHANY0,这样即可完成动态切换通道。

4.9        A/D 任意通道通道选择寄存器 1(ADC_CHANY1)
地址偏移:0x60

复位值:0x00000000



        注:单周期扫描或连续扫描模式下,硬件会启动 ADC_CHANY1 影子寄存器,在 ADC 未开始工作 时,软件写 ADC_CHANY1 的话,也会写到其影子寄存器;在 ADC 工作期间,若更改 ADC_CHANY1 的值,只会更新其影子寄存器,且当 ADC 开始转换最后一个通道时,影子寄存器的值会更新至 ADC_CHANY1,这样即可完成动态切换通道。

4.10        A/D 任意通道配置寄存器 (ADC_ANY_CFG)
地址偏移:0x64

复位值:0x00000000




        注:单周期扫描或连续扫描模式下,硬件会启动 ADC_NUM 影子寄存器,在 ADC 未开始工作时, 软件写 ADC_NUM 的话,也会写到其影子寄存器;在 ADC 工作期间,若更改 ADC_NUM 的值,只会 更新其影子寄存器,且当 ADC 开始转换最后一个通道时,影子寄存器的值会更新至 ADC_NUM,这样即 可完成动态切换通道。

4.11        A/D 任意通道控制寄存器 (ADC_ANY_CR)
地址偏移:0x68

复位值:0x00000000



        注:在任意通道模式,且单周期/连续扫描模式下,关闭 ADC 时,必须先禁止 ADC_ADCR 的 ADST 位,然后判断 ADC_ADSTA 的 BUSY 位是否为 0,即等到 ADC 转换完成,再禁止 ADC_ANY_CR 的 CHANY_MDEN 位。

五、补充说明
5.1        数据对齐
        ADCR 寄存器中的 ALIGN 位选择转换后数据储存的对齐方式。数据可以左对齐或右对齐, 如下图 所示。



5.2        可编程分辨率
        ADC 转换有效位数可通过 ADC_CFG 寄存器中的 RSLTCTL[2:0] 位更改,以便加快数据转换速率, 有效数据位是在 12 位数据高位对齐。

5.3        可编程采样时间
        ADC 的时钟 ADCLK 由 PCLK1 分频得到,分频系数可通过设置 ADCFG 寄存器的 ADCPRE 位 来确定,即 PCLK1 / (N + 2) 分频后作为 ADC 时钟。ADC 使用若干个 ADC_CLK 周期对输入电压采 样,采样周期数目可以通过 ADC_CFG 寄存器中的 SAMCTL[3:0] 位更改。

        设置 ADC 分辨率为 n 位 (n=8,9,10,11,12),每个通道采样周期为 m。采样频率采样时间计算如下:

Fsample = FADCLK / (m + n + 0.5) 。

        假设分辨率配置为 12Bit,每个通道采样周期为 3.5T,则 Fsample = FADCLK / 16。总转换时间如 下计算:

TCONV = 采样时间 + 12.5 个转换周期

例如:

        当 ADCCLK = 16MHz,采样时间为 3.5 周期 TCONV = 3.5 + 12.5 = 16 ,周期 = 1µs

5.4        外部触发转换
        ADC 转换可以由外部事件触发 (例如定时器捕获,EXTI 线)。

        在触发信号产生后,延时 N 个 PCLK1 的时钟周期再开始采样。如果是触发扫描模式,只有第一个 通道采样被延时,其余通道是在上一个采样结束后立即开始。

        如果设置了 ADCR 寄存器的 TRGEN 位,就可以使用外部事件触发转换。通过设置 TRGSEL 位可 以选择外部触发源。

        具体的外部触发源选择情况,可以参考 AD 控制寄存器(ADCR.TRGSEL)相关位的描述。

        外部触发可设置延时控制,具体参考 AD 控制寄存器 (ADCR.TRGSHIFT) 相关位的描述。

5.5        内部基准参考电压
        ADC 的内部信号源通道连接了一个内部基准参考电压,大小为 1.2V,此通道把 1.2v 的参考电压输出转换为数字值。

        内部参考电压有单独的始能位,可通过设置寄存器的相应位开启或关闭。

5.6        窗口比较器模式下 AD 转换结果监控

        比较模式下提供了上限和下限两个比较寄存器。可通过软件设定 CMPCH 位选择监控通道。当 CPMHDATA ≥ CPMLDATA 时,比较结果大于或等于 ADCMPR 寄存器的 CMPHDATA 指定值或者小于 CMPLDATA 指定值,状态寄存器 ADSTA 的 ADWIF 位置 1。

        当 CPMHDATA < CPMLDATA 时,比较结果如果等于 CPMHDATA 指定值或者处于两个指定值之间, 则状态寄存器 ADSTA 的 ADWIF 位置 1。

        如果控制寄存器 ADCR 的 ADWIE 置位,将产生中断请求。

六、单次转换模式
6.1        ADC初始化配置(三个通道1、3、7)
void ADC_Configure(void)
{
    ADC_InitTypeDef  ADC_InitStruct;
    GPIO_InitTypeDef GPIO_InitStruct;

    RCC_APB1PeriphClockCmd(RCC_APB1PERIPH_ADC1, ENABLE);

    ADC_StructInit(&ADC_InitStruct);
    ADC_InitStruct.ADC_Resolution       = ADC_Resolution_12b;
    ADC_InitStruct.ADC_Prescaler        = ADC_Prescaler_16;
    ADC_InitStruct.ADC_Mode             = ADC_Mode_Imm;
    ADC_InitStruct.ADC_DataAlign        = ADC_DataAlign_Right;
    ADC_Init(ADC1, &ADC_InitStruct);

    ADC_SampleTimeConfig(ADC1, ADC_SampleTime_240_5);

    RCC_AHBPeriphClockCmd(RCC_AHBPERIPH_GPIOA, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPERIPH_GPIOB, ENABLE);

    /* PA12(RV1) PB0(RV2) PA7(RV3) */
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_7 | GPIO_Pin_12;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AIN;
    GPIO_Init(GPIOB, &GPIO_InitStruct);

    ADC_Cmd(ADC1, ENABLE);
}


6.2        main主函数
float ADC_GetChannelVoltage(uint32_t Channel)
{
    uint16_t Value = 0;
    float    Voltage;

    /* Number Of Any Channel Mode */
    MODIFY_REG(ADC1->ANY_CFG, ADC_ANY_CFG_CHANY_NUM, 0x00U << ADC_ANY_CFG_CHANY_NUM_Pos);

    /* Arbitrary Channel Selection */
    MODIFY_REG(ADC1->CHANY0, ADC_CHANY0_CHANY_SEL0, Channel << ADC_CHANY0_CHANY_SEL0_Pos);

    /* Arbitrary Channel Configuration Mode Enable */
    SET_BIT(ADC1->ANY_CR, ADC_ANY_CR_CHANY_MDEN);

    /* ADC Conversion Start */
    SET_BIT(ADC1->ADCR, ADC_ADCR_ADST);

    while (READ_BIT(ADC1->ADSTA, ADC_ADSTA_ADIF) == 0)
    {
    }

    /* Clear ADC Conversion Completed Flag */
    SET_BIT(ADC1->ADSTA, ADC_ADSTA_ADIF);

    /* Read ADC Conversion Result */
    Value = (READ_REG(ADC1->ADDATA) & ADC_ADDATA_DATA_Msk) >> ADC_ADDATA_DATA_Pos;

    /* Arbitrary Channel Configuration Mode Disable */
    CLEAR_BIT(ADC1->ANY_CR, ADC_ANY_CR_CHANY_MDEN);

    Voltage = (float)Value / (float)4096 * (float)3.3;

    return (Voltage);
}

int main(void)
{
    ADC_Configure();

    while (1)
    {
        ADC_GetChannelVoltage(3),
        ADC_GetChannelVoltage(1),
        ADC_GetChannelVoltage(7));

        PLATFORM_DelayMS(500);
    }
}

七、单周期扫描模式
7.1        ADC初始化配置
void ADC_Configure(void)
{
    ADC_InitTypeDef  ADC_InitStruct;
    GPIO_InitTypeDef GPIO_InitStruct;

    RCC_APB1PeriphClockCmd(RCC_APB1PERIPH_ADC1, ENABLE);

    ADC_StructInit(&ADC_InitStruct);
    ADC_InitStruct.ADC_Resolution       = ADC_Resolution_12b;
    ADC_InitStruct.ADC_Prescaler        = ADC_Prescaler_16;
    ADC_InitStruct.ADC_Mode             = ADC_Mode_Scan;
    ADC_InitStruct.ADC_DataAlign        = ADC_DataAlign_Right;
    ADC_Init(ADC1, &ADC_InitStruct);

    ADC_SampleTimeConfig(ADC1, ADC_SampleTime_240_5);

    ADC_AnyChannelNumCfg(ADC1, 2);
    ADC_AnyChannelSelect(ADC1, ADC_AnyChannel_0, ADC_Channel_3);
    ADC_AnyChannelSelect(ADC1, ADC_AnyChannel_1, ADC_Channel_1);
    ADC_AnyChannelSelect(ADC1, ADC_AnyChannel_2, ADC_Channel_7);
    ADC_AnyChannelCmd(ADC1, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBPERIPH_GPIOA, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPERIPH_GPIOB, ENABLE);

    /* PA12(RV1) PB0(RV2) PA7(RV3) */
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_7 | GPIO_Pin_12;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AIN;
    GPIO_Init(GPIOB, &GPIO_InitStruct);

    ADC_Cmd(ADC1, ENABLE);
}

7.2        main主函数
int main(void)
{
    float RVxVoltage[3];

    ADC_Configure();

    while (1)
    {
         /* ADC Conversion Start */
        SET_BIT(ADC1->ADCR, ADC_ADCR_ADST);

        while (READ_BIT(ADC1->ADSTA, ADC_ADSTA_ADIF) == 0)
        {
        }

        /* Clear ADC Conversion Completed Flag */
        SET_BIT(ADC1->ADSTA, ADC_ADSTA_ADIF);

        RVxVoltage[0] = (float)((READ_REG(ADC1->ADDR3) & ADC_ADDR_DATA) >> ADC_ADDR_DATA_Pos) * (float)3.3 / (float)4096.0;
        RVxVoltage[1] = (float)((READ_REG(ADC1->ADDR1) & ADC_ADDR_DATA) >> ADC_ADDR_DATA_Pos) * (float)3.3 / (float)4096.0;
        RVxVoltage[2] = (float)((READ_REG(ADC1->ADDR7) & ADC_ADDR_DATA) >> ADC_ADDR_DATA_Pos) * (float)3.3 / (float)4096.0;

        //printf("\r\nRV1 Voltage = %0.2f  \tRV2 Voltage = %0.2f  \tRV3 Voltage = %0.2f", RVxVoltage[0], RVxVoltage[1], RVxVoltage[2]);

        PLATFORM_DelayMS(500);
    }
}

八、连续扫描模式
8.1        ADC初始化配置
void ADC_Configure(void)
{
    ADC_InitTypeDef  ADC_InitStruct;
    GPIO_InitTypeDef GPIO_InitStruct;

    RCC_APB1PeriphClockCmd(RCC_APB1PERIPH_ADC1, ENABLE);

    ADC_StructInit(&ADC_InitStruct);
    ADC_InitStruct.ADC_Resolution       = ADC_Resolution_12b;
    ADC_InitStruct.ADC_Prescaler        = ADC_Prescaler_16;
    ADC_InitStruct.ADC_Mode             = ADC_Mode_Continue;
    ADC_InitStruct.ADC_DataAlign        = ADC_DataAlign_Right;
    ADC_Init(ADC1, &ADC_InitStruct);

    ADC_SampleTimeConfig(ADC1, ADC_SampleTime_240_5);

    ADC_AnyChannelNumCfg(ADC1, 2);
    ADC_AnyChannelSelect(ADC1, ADC_AnyChannel_0, ADC_Channel_3);
    ADC_AnyChannelSelect(ADC1, ADC_AnyChannel_1, ADC_Channel_1);
    ADC_AnyChannelSelect(ADC1, ADC_AnyChannel_2, ADC_Channel_7);
    ADC_AnyChannelCmd(ADC1, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBPERIPH_GPIOA, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPERIPH_GPIOB, ENABLE);

    /* PA12(RV1) PB0(RV2) PA7(RV3) */
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_7 | GPIO_Pin_12;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AIN;
    GPIO_Init(GPIOB, &GPIO_InitStruct);

    ADC_Cmd(ADC1, ENABLE);
}

8.2        main主函数
int main(void)
{
    float RVxVoltage[3];

    //printf("\r\nTest %s", __FUNCTION__);

    ADC_Configure();

    /* ADC Conversion Start */
    SET_BIT(ADC1->ADCR, ADC_ADCR_ADST);

    while (1)
    {
        if (READ_BIT(ADC1->ADSTA, ADC_ADSTA_ADIF))
        {

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

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/2201_75974811/article/details/140796296

使用特权

评论回复
沙发
菜鸟的第一步| | 2024-8-16 15:55 | 只看该作者
好详细的学习笔记。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1735

主题

15127

帖子

10

粉丝