【问题标题】:10 bit ADC Value to Voltage measurement10 位 ADC 值到电压测量
【发布时间】:2018-09-25 04:04:10
【问题描述】:

我正在尝试使用 ATMEGA8 进行 ADC 并从电位器接收 ADC 值。因为它是 10 位 ADC,所以我能收到的最高值是 1024。 现在我想将此值转换为实际电压并使用串行在终端上查看。我的参考电压是5V。

这就是我正在做的事情

#define REF_ADC_Volt    5000
#define ADC_Div_Factor  1023

//init ADC
void Init_ADC()
{
    ADMUX  |= (1<<REFS0);                   //Reference voltage set at AREF pin
    ADCSRA |= (1 << ADEN);                //Enable ADC
    ADCSRA |= (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2);    //set prescale by 128 div factor
}
//Read ADC
uint16_t Read_ADC(uint8_t ch)
{
    ch = ch & 0x07;
    ADMUX |= ch;                        //Setting ADC Channel
    ADCSRA |= (1<<ADSC);                //ADC start conversion 
    while (! (ADCSRA & (1<<ADIF)) );    //Wait till conversion is over
    ADCSRA |= (1<<ADIF);                //Clear ADC Flag    
    return(ADCW);                       //Return ADC value 10 bit
}

int main(void)
{
    _delay_ms(2000);
    Init_ADC();
    USART_Init(103);
    double ADC_Val,Res_ADC_Val;
    char *number_string="00000";

    USART_Transmit_String("ACS712 Current Sensor ADC Value: \r\n");
    while (1) 
    {
        ADC_Val = Read_ADC(0);

        Res_ADC_Val = ((REF_ADC_Volt / ADC_Div_Factor) * ADC_Val)/1000;
        dtostrf(Res_ADC_Val,1,2,number_string);
        USART_Transmit_String(number_string);
        itoa(ADC_Val,number_string,10);
        USART_Transmit(' ');
        USART_Transmit_String(number_string);
        USART_Transmit_String("\r\n");

        ClearBuffer(number_string);
        _delay_ms(1000);
    }
}

现在的问题是转换后我得到的最高电压是 4.09V,ADC 值为 1023。但应该是 5V 对吧??

根据这个计算

Res_ADC_Val = ((REF_ADC_Volt / ADC_Div_Factor) * ADC_Val)/1000;

在哪里

REF_ADC_Volt  = 5000mV
ADC_Div_Factor = 1023
ADC_Val = 1023

我很困惑,因为当我使用我的计算器时,它只有 5V,但我得到的是 4.09。为什么?以及如何解决这个问题?

提前致谢。

【问题讨论】:

    标签: atmega adc


    【解决方案1】:

    REF_ADC_VoltADC_Div_Factor 都是两个 整数 文字。

    因此,第一个除法产生一个整数结果(很可能是 4)。

    然后,将此除法 (4) 的结果乘以 ADC_Val。

    这意味着 4 * 1023 = 4.092。

    你应该将你的文字提升为浮点数:

    #define REF_ADC_Volt    5000.0
    #define ADC_Div_Factor  1023.0
    

    或重新排列表达式以允许隐式转换起作用,例如:

    Res_ADC_Val = REF_ADC_Volt * ADC_Val / ADC_Div_Factor / 1000.0;
    

    编辑#1:

    优化提示

    正如在其他答案中指出的那样,上面的实现是次优的。优化不是答案的主题,但讨论这些事情总是很有趣。

    请注意,其他答案中提出的解决方案也不是最有效的。

    事实上,没有必要执行所有这些划分,因为它们都涉及常量值。

    您可以将一个常数定义为标量,每次只执行一个乘法:

    #define ADC_TO_VOLT 0.00488758553275 // (5000.0 / 1023.0) / 1000.0
    
    
    Res_ADC_Val = ADC_Val * ADC_TO_VOLT;
    

    此外,可能不需要使用双精度值。我相信单精度值(浮点数)就足够了,但这取决于您的应用程序,并且很难从您的最小示例中判断。

    【讨论】:

      【解决方案2】:

      之前的答案完全正确,但如果代码执行时间和优化对您很重要,您可以在 int 中进行大部分计算,而不是在 double 中。

      #define REF_ADC_mVolt  = 5000
      #define ADC_Div_Factor = 1023
      
      double Res_ADC_Val;
      uint16_t ADC_Val; //notice the uint16_t instead double
      
      ADC_Val = Read_ADC(0);
      
      Res_ADC_Val = (((uint32_t)REF_ADC_mVolt * ADC_Val)/ ADC_Div_Factor )/(double)1000.0;
      

      现在只有一个缓慢的“双”除法。注意对uint32_t 的类型转换以避免溢出。并注意(double)1000.0 单独的.0 只会转换为浮动而不是加倍。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-10-27
        • 1970-01-01
        • 2020-11-02
        • 1970-01-01
        • 2016-10-16
        • 2014-06-16
        • 1970-01-01
        相关资源
        最近更新 更多