【航芯ACM32G103开发板评测二】 + ADC模数转换器

御坂10032号   2024-1-2 21:52 楼主

今天给大家带来的是模数转换器(ADC)资源验证。首先让我简要的介绍一下航芯这块开发板上板载的ADC资源。(下面的数据来自于航芯ACM32G103_datasheet.pdf)

 

航芯这块ACM32G103开发板具有两个ADC外设,分别是ADC1和ADC2,这两个ADC 支持 2 路 12 位 3Msps 采样率。 同时内部集成了转换电路可以实现内嵌温度传感器实现温度检测。

 

ADC 主要特性如下:


 

  • 最高 12 位分辨率, 可配置 10 位、 8 位或 6 位分辨率
  • 转换速率最高可达 3Msps
  • ADC1 支持 20 个通道,其中 IN0-IN16 可作为外部通道,IN17-IN19 为内部通道。ADC2 支持 20个通道,其中有 18 个为外部通道,IN16-IN17 为内部通道。

                — 内建 BGR 连接到 ADC1
                — 温度传感器连接到 ADC1
                — VBAT 连接到 ADC1
                — The OPA1/2 内部输出连接到 ADC1 输入通道
                — The OPA2/3 内部输出连接到 ADC2 输入通道

  • 支持单端信号转换和差分信号转换
  • 规则组转换结束、注入转换结束和发生模拟看门狗事件时产生中断
  • 支持单次、连续、间断转换模式和同步模式(两个 ADC 设备)
  • 最多支持 16 个规则通道组和 4 个注入通道组
  • 采样时间可以按通道分别编程
  • 规则转换和注入转换均有外部触发选项
  • 规则通道转换时可用 DMA 将结果搬到 SRAM 中
  • 支持过采样:16 位数据寄存器,过采样率支持 2 倍~256 倍
  • 数据寄存器可配置数据对齐方式

那么如何来使用这块开发板上的ADC功能呢? 首先我们需要确定的是ADC1或者ADC2的板载PIN, 其次确认ADC被挂载到了系统的哪一个总线上.

确认ADC_PIN

打开航芯ACM32G103_Datasheet_V1.5.pdf(page34),RCT6采用的是LQFP64的封装。

image.png  

 

在明确封装之后,查看数据手册中的引脚概述, 我们找到如下ADC功能PIN, PA1是ADC的输入通道1

 

   image.png  

 

那么ADC是挂载在哪一个外设总线下的呢?根据功能框图得知, ADC是被挂载到APB bridg2 上的。所以我们可以像上一个帖子一样来初始化ADC的时钟。

image.png   众所周知, hal_rcc.h 这个库是负责板载上时钟的控制的,我们可以在1023行找到这个宏定义__HAL_RCC_ADC12_CLK_ENABLE() 我们可以调用这个方法来初始化ADC的时钟

 

image.png   但是其实上述的时钟并不需要我们自己手动初始化,我们只需要配置好对应的ADC_ChannelConfTypeDef, 在调用对应的HAL初始化方法的时候,库会自动为我们初始化时钟(下面代码参考的来源于官方demo)

 

初始化ADC,并且通道选择通道一即PA1, 我这里移除了其他多余的通道。

void ADC_Init_Polling_Nchannels(void)
{
    ADC_ChannelConfTypeDef ADC_ChannelConf;

    ADC_Handle.Init.ClockPrescaler = ADC_CLOCK_DIV16;       //ADC_CLK分频选择
    ADC_Handle.Init.Resolution     = ADC_RESOLUTION_12B;    //分辨率
    ADC_Handle.Init.DataAlign      = ADC_DATAALIGN_RIGHT;   //数据对齐
    ADC_Handle.Init.ConConvMode    = DISABLE;               //连续转换模式

    ADC_Handle.Init.DiscontinuousConvMode = DISABLE;        //间断模式
    ADC_Handle.Init.NbrOfDiscConversion   =1;               //间断模式通道计数
    ADC_Handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;  //触发模式:外部触发或软件触发
    ADC_Handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;//外部触发边沿选择
    ADC_Handle.Init.DMAMode = ADC_DMAMODE_DISABLE;          //DMA选择
    ADC_Handle.Init.OverMode = ADC_OVERMODE_DISABLE;        //溢出时是否保留上次采样数据

    ADC_Handle.Init.OverSampMode = ADC_OVERSAMPMODE_DISABLE;//过采样使能
    ADC_Handle.Init.Oversampling.Ratio =ADC_OVERSAMPLING_RATIO_2;//过采样率
    ADC_Handle.Init.Oversampling.RightBitShift =ADC_RIGHTBITSHIFT_2;//过采样移位系数
    ADC_Handle.Init.Oversampling.TriggeredMode =0;          //过采样触发模式    
    ADC_Handle.Init.AnalogWDGEn = ADC_ANALOGWDGEN_DISABLE;  //模拟看门狗
    ADC_Handle.Init.ChannelEn = ADC_CHANNEL_1_EN;

    ADC_Handle.Instance = ADC1;  //ADC2

    HAL_ADC_Init(&ADC_Handle);


    /* The total adc regular channels number */
    ADC_Handle.ChannelNum = 1;

    ADC_ChannelConf.Channel = ADC_CHANNEL_1;
    ADC_ChannelConf.Sq = ADC_SEQUENCE_SQ1;
    ADC_ChannelConf.Smp = ADC_SMP_CLOCK_320;
    ADC_ChannelConf.SingleDiff = ADC_SINGLE_ENDED;
    ADC_ChannelConf.OffsetNumber = ADC_OFFSET_NONE;
    ADC_ChannelConf.Offset = 0;	
    HAL_ADC_ConfigChannel(&ADC_Handle,&ADC_ChannelConf);

}

获取参考电压:

uint32_t ADC_GetVrefP(void)
{
    ADC_HandleTypeDef ADC_Handle_Vrefp;
    ADC_ChannelConfTypeDef ADC_ChannelConf;
    uint32_t TrimValue_3v, AdcValue_VrefP[1], VrefP,temp;
    
    //PMU CLK Enable
    RCC->APB1ENR |= 1<<27;
    //VBG1P2
    *(__IO uint32_t*)(0x400070C0) = 0x05;
    
    HAL_SimpleDelay(2000);
    
    ADC_Handle_Vrefp.Init.ClockPrescaler = ADC_CLOCK_DIV16;       //ADC_CLK分频选择
    ADC_Handle_Vrefp.Init.Resolution     = ADC_RESOLUTION_12B;    //分辨率
    ADC_Handle_Vrefp.Init.DataAlign      = ADC_DATAALIGN_RIGHT;   //数据对齐
    ADC_Handle_Vrefp.Init.ConConvMode    = DISABLE;               //连续转换模式

    ADC_Handle_Vrefp.Init.DiscontinuousConvMode = DISABLE;        //间断模式
    ADC_Handle_Vrefp.Init.NbrOfDiscConversion = 1;                //间断模式通道计数
    ADC_Handle_Vrefp.Init.ExternalTrigConv = ADC_SOFTWARE_START;  //触发模式:外部触发或软件触发
    ADC_Handle_Vrefp.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;//外部触发边沿选择
    ADC_Handle_Vrefp.Init.DMAMode = ADC_DMAMODE_DISABLE;          //DMA选择
    ADC_Handle_Vrefp.Init.OverMode = ADC_OVERMODE_DISABLE;        //溢出时是否保留上次采样数据

    ADC_Handle_Vrefp.Init.OverSampMode = ADC_OVERSAMPMODE_DISABLE;//过采样使能设置
    ADC_Handle_Vrefp.Init.Oversampling.Ratio =ADC_OVERSAMPLING_RATIO_2;//过采样率
    ADC_Handle_Vrefp.Init.Oversampling.RightBitShift =ADC_RIGHTBITSHIFT_2;//过采样移位系数
    ADC_Handle_Vrefp.Init.Oversampling.TriggeredMode =0;          //过采样触发模式    
    ADC_Handle_Vrefp.Init.AnalogWDGEn = ADC_ANALOGWDGEN_DISABLE;  //模拟看门狗
    ADC_Handle_Vrefp.Init.ChannelEn = ADC_CHANNEL_VBGR_EN;

    ADC_Handle_Vrefp.Instance = ADC1;  

    HAL_ADC_Init(&ADC_Handle_Vrefp);

    /* The total adc regular channels number */
    ADC_Handle_Vrefp.ChannelNum = 1;
    
    /* Add adc channels */
    ADC_ChannelConf.Channel = ADC_CHANNEL_VBGR;
    ADC_ChannelConf.Sq = ADC_SEQUENCE_SQ1;
    ADC_ChannelConf.Smp = ADC_SMP_CLOCK_320;
    ADC_ChannelConf.SingleDiff = ADC_SINGLE_ENDED;
    ADC_ChannelConf.OffsetNumber = ADC_OFFSET_NONE;
    ADC_ChannelConf.Offset = 0;	
    HAL_ADC_ConfigChannel(&ADC_Handle_Vrefp,&ADC_ChannelConf);
    
    HAL_ADC_Polling(&ADC_Handle_Vrefp, AdcValue_VrefP, ADC_Handle_Vrefp.ChannelNum, 0);
    
    printfS("The adc convert result :  0x%08x[ %d ], VBGR = %dmV\r\n", AdcValue_VrefP[0], AdcValue_VrefP[0] & 0xFFF, ((AdcValue_VrefP[0] & 0xFFF) * 3300 / 4095));
    
    //VBG1P2
    *(__IO uint32_t*)(0x400070C0) &= ~0x05;
    
    TrimValue_3v = *(volatile uint32_t*)(0x00080240); //Read the 1.2v trim value in 3.0v vrefp.

    printfS("The adc 1.2v trim value is : 0x%08x \r\n", TrimValue_3v);

    if(((~TrimValue_3v&0xFFFF0000)>>16) == (TrimValue_3v&0x0000FFFF))
    {
        temp = TrimValue_3v & 0xFFF;
        
        VrefP = (uint32_t)(temp * 3000 / (AdcValue_VrefP[0] & 0xFFF));
      
        return VrefP;
    }
    return 0;
}

 

 

从DAC buffer中获取转换后的电压

void ADC_Test_Polling_Nchannels(void)
{
    uint32_t i, VrefP, Voltage;
    
    uint32_t lu32_COM_OK  = 0;

    printfS("The ADC test ADC_Test_Polling_Nchannels start.");
    
    VrefP = ADC_GetVrefP();
    printfS("The VrefP value is : %d \r\n", VrefP);
    
    ADC_Init_Polling_Nchannels();
    
    while(1)
    {
        for (i = 0; i < BUFFER_LENGTH; i++)
        {
            gadcBuffer[i] = 0;
        }

        HAL_ADC_Polling(&ADC_Handle, gadcBuffer, ADC_Handle.ChannelNum, 0);
       
        
        for (i = 0; i < ADC_Handle.ChannelNum; i++)
        {
            printfS("The adc convert result : Channel %d = %d\r\n", gadcBuffer[i]>>16 & 0xFF,gadcBuffer[i]&0xFFF);
            Voltage = (gadcBuffer[i]&0xFFF)*VrefP/4095;
            printfS("The Voltage is: %d mV \r\n", Voltage);
           
        } 
        HAL_DelayMs(1000);     
    }
}

 

我这里直接用杜邦线将PA1连接到了板载的3v3,如下为串口的转换结果输出

image.png   上述代码可以在官方提供的demo中的dac_polling 中找到。原本打算自己写这个dac的代码的,但是结构体内的一些定义自己搞不明白。所以无奈只好借鉴了官方的demo,少做修改便完成了这次测试。

 

杂牌万用表的测试结果:

 

35eb80c4deabaa1a0a6d5af11503210.jpg  

 

 

本帖最后由 御坂10032号 于 2024-1-2 23:14 编辑

回复评论 (2)

楼主大佬,我跑例程是可以,移植到任务系统里面就不行,什么时候帮我看看怎么回事。

点赞  2024-1-3 20:29
引用: lugl4313820 发表于 2024-1-3 20:29 楼主大佬,我跑例程是可以,移植到任务系统里面就不行,什么时候帮我看看怎么回事。

我没那么厉害,我建议你可以示波器看看输出

点赞  2024-1-4 22:59
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复