电子产业一站式赋能平台

PCB联盟网

搜索
查看: 119|回复: 0
收起左侧

CH32内部参考电压的自学笔记

[复制链接]

395

主题

395

帖子

2304

积分

三级会员

Rank: 3Rank: 3

积分
2304
发表于 2023-12-13 21:01:00 | 显示全部楼层 |阅读模式

ysugfej5t2w64029370515.png

ysugfej5t2w64029370515.png

- L& y9 t, p& d. Y* T$ m
5 k6 w( e" E/ g/ N前言CH32V/F单片机能够在一定的电压范围内进行工作,以CH32V203C8T6 芯片为例,在不使用 USB 外设时,最低工作电压能够达到 2.4V。较为宽泛的工作电压,允许单片机直接使用电池供电,但由于 CH32V203C8T6 芯片没有独立的 Vref 引脚,使用 ADC 的过程中无法换算出真实的电压。为解决无法获得真实电压的问题,可以使用内置参考电压换算当前供电电压(即 ADC参考电压)。对于项目要求精确测量时,也可尝试使用该方法对 ADC 进行校准。
- t: j4 c8 \) l2 Y  |  v 1 z3 O4 |- Y/ y* @6 `; a' C! t6 o
电源电压的换算CH32V203C8T6 芯片内部参考电压是典型值为 1.2V,正负偏差为 0.04V 的电压范围,在 ADC 转换精度要求不高的应用场景下,可以直接使用 1.2V 换算芯片供电电压。8 l# t/ g0 y+ }$ ^/ _' I, ~

rkcarpax15264029370615.png

rkcarpax15264029370615.png

. B" @& f; f' T" t如果需要更加精确的转换结果,就应在稳定的供电条件下,先对内部参考电压进行测量并将结果保存在 Flash 中,实际的使用过程中,再根据已知的内部参考电压进行换算。
& T! @* n8 |- ^0 D# ]  \

0n2m1ke4fpi64029370715.png

0n2m1ke4fpi64029370715.png
7 B# R& s, [- f9 n  g

& p) E  c3 x3 {3 C实现上述操作,可参考以下代码:: P, y, o/ B4 @5 j4 B  x# r9 f
  • u16 ADC_val = 0;s32 val_mv = 0;u16 Vref = 0;               // Flash中存储的内部参考电压实测值s32 Vref_To_VDD = 0;        // 由Vref的实测值换算出的电源电压值 5 h1 R$ i% w$ w) I
    if ( *(u32*)(FAST_FLASH_PROGRAM_START_ADDR) == 0xe339e339 ) {       // 判断Flash中是否有内部参考电压的实测值    printf("Address:0x%08x -> %08x\r5 y1 H0 W  X' G2 {9 b* m
    ", FAST_FLASH_PROGRAM_START_ADDR, *(u32*)(FAST_FLASH_PROGRAM_START_ADDR));    // 获取内部参考电压实测值,此时务必保证电源电压或参考电压(如果有)的准确    ADC_val = Get_ADC_Average(ADC_Channel_Vrefint, 255);            // 255次取平均    ADC_val = Get_ConversionVal(ADC_val);    val_mv = (ADC_val * 3300 / 4096);    printf("Vref_mv -> %d\r
    & X4 N8 l5 Q# y5 m", val_mv);    // 将测得的结果存储在Flash中    buf[0] = val_mv;    FLASH_Unlock_Fast();    FLASH_ProgramPage_Fast(FAST_FLASH_PROGRAM_START_ADDR, buf);    FLASH_Lock_Fast();    printf("Address:0x%08x -> %08x\r; K' p2 o+ Y( f" K2 j$ x5 ~
    ", FAST_FLASH_PROGRAM_START_ADDR, *(u32*)(FAST_FLASH_PROGRAM_START_ADDR));} else {    printf("Address:0x%08x -> %08x\r
    ( `3 Q5 W4 @: Q- Y", FAST_FLASH_PROGRAM_START_ADDR, *(u32*)(FAST_FLASH_PROGRAM_START_ADDR));    Vref = *(u32*)(FAST_FLASH_PROGRAM_START_ADDR);    ADC_val = Get_ADC_Average(ADC_Channel_Vrefint, 255);            // 255次取平均    ADC_val = Get_ConversionVal(ADC_val);    Vref_To_VDD = (4096 * Vref / ADC_val);    printf("Vref_To_VDD_mV -> %d\r
    # U  J! @' }% }* ^$ ?/ o1 Z", Vref_To_VDD);    }) B" B( j: g$ O: A2 [, g4 Y. l
    % \( }! `, M: _! ]9 b7 X- c2 X+ V# z0 g
    ADC 初始化过程中的校准
    6 Z- Z& H$ P' g$ LADC 初始化函数中完成了一次校准过程,经过校准环节可大幅减小因内部电容器组的变化而造成的精准度误差。校准过程中 ADC 仅获取了 Vcc 的采样值,与实际电压大小无关,因此,在浮动电压供电的场景中,不会引入额外的误差。获取校准值函数,通过写 ADC_CTLR2 寄存器的 RSTCAL 位置 1 初始化校准寄存器,等待 RSTCAL 硬件清 0完成初始化。置位 CAL 位,启动校准功能,校准结束后,硬件自动清除 CAL 位,将校准码存储到 ADC_RDATAR 中。使用多次校准结果,计算 ADC 补偿。: ^, o# ~) |. N% ~1 w2 M  {8 {
  • int16_t Get_CalibrationValue(ADC_TypeDef *ADCx){    __IO uint8_t  i, j;    uint16_t      buf[10];    __IO uint16_t t;#if defined (CH32V20x_D6)    __IO uint16_t p;#endif
    / I4 y/ `/ U! u; H8 W& H+ J    for(i = 0; i 10; i++){        ADC_ResetCalibration(ADCx);        while(ADC_GetResetCalibrationStatus(ADCx));        ADC_StartCalibration(ADCx);        while(ADC_GetCalibrationStatus(ADCx));        buf = ADCx->RDATAR;//        printf("CalibrationValue[%d]->%d\r
    * _/ w$ y6 O. A: Z", i, buf);    }: @4 o+ X. D/ ]- |  p
        for(i = 0; i 10; i++){        for(j = 0; j 9; j++){            if(buf[j] > buf[j + 1])            {                t = buf[j];                buf[j] = buf[j + 1];                buf[j + 1] = t;            }        }    }
    , ^; H6 v% _+ M3 j2 B5 W4 [! Y#if defined (CH32V20x_D8) || defined (CH32V20x_D8W)    t = 0;    for( i = 0; i 6; i++ ) {        t += buf[i + 2];    }  T' U7 x1 O. W9 D0 R5 T
        t = ( t / 6 ) + ( ( t % 6 ) / 3 );1 a: {: l- @$ I, A+ H6 P( m
        return ( int16_t )( 2048 - ( int16_t )t );#else    t = 0;    p = 0;    /* 1024 */    for(i = 0; i 6; i++ ){            if(buf[i+2] > 1536) break;            t += buf[i+2];    }
    ' T/ v+ D$ B% P: a9 O6 _    if(i > 0){            t = ( t / i ) + ( (( t % i )*2) / i );    }    else t = 1024;
    & J5 b1 @. L( c& v    /* 2048 */    j = 6-i;    if(j > 0){        for(; i 6; i++ ){                p += buf[i+2];        }8 C- w* ^" T4 \; k( ]
            p = ( p / j ) + ( (( p % j )*2) / j );    }    else p = 2048;9 C/ S7 Y. z$ P) ?! e1 E# ^8 R$ R! i
        return ( int16_t )(((( int16_t )( 1024 - ( int16_t )t ) + ( int16_t )( 2048 - ( int16_t )p ))/2) + ((( int16_t )( 1024 - ( int16_t )t ) + ( int16_t )( 2048 - ( int16_t )p ))%2));
    , I% R3 y/ \) x* T, t. N! O  l#endif}可以在校准值转换的 for 循环中添加打印,观察每次校准值结果是否随芯片供电电压(即 ADC 参考电压)的改变而改变。
    : C4 E* c/ I! q$ b9 V

    hnttsttqdjp64029370815.png

    hnttsttqdjp64029370815.png
    & P/ x! X) u* Q' M; g9 G
    ; D/ \' t  r4 R: O9 }
    ==========
    9 z3 k3 j. I1 f8 D0 C! @往期回顾:9 n3 M! ?8 v1 c4 Y
    strlen和sizeof的异同
    + P1 ]1 L; Y' m8 V8 n; }STM32CubeMx的串口DMA收发数据
    ; O9 J) \8 l, b, b! q好看的PCB也是产品的优势. K) @* s$ m0 F, s! p
    STM32的DMA的五大问题3 ^  f6 K$ [+ [! W2 t
    单片机的各个通信协议的波特率
    . S/ p0 |" P2 B. W==========
    2 y+ a# `' `( T; I4 n7 T原文链接:点击阅读原文: I* K4 b3 M- s5 ]- x+ ^
    平台:博客园# X, a* P' U& N" D' U
    作者:wchmcu
    % o& n  h. e* H+ }' C4 U' b' B+ _; H1 ]5 w, B  q+ c" U

    k50dntzbp4d64029370915.png

    k50dntzbp4d64029370915.png
    2 I8 f, g, {2 J
    7 {: ]1 c+ w6 w0 y" N

    5kl4jenrk4m64029371015.png

    5kl4jenrk4m64029371015.png
    2 z% [7 V9 ?3 y
    & ?) A$ @- U  G6 W9 H

    mvpopbwligv64029371116.png

    mvpopbwligv64029371116.png
  • 回复

    使用道具 举报

    发表回复

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则


    联系客服 关注微信 下载APP 返回顶部 返回列表