10 位 ADC 值电压测量
10 bit ADC Value to Voltage measurement
我正在尝试使用 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。为什么?以及如何解决这个问题?
提前致谢。
REF_ADC_Volt和ADC_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;
此外,可能不需要使用双精度值。我相信单精度值(浮点数)就足够了,但这取决于您的应用程序,并且很难从您的最小示例中判断。
之前的答案完全正确,但如果代码执行时间和优化对您很重要,您可以使用 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;
现在只有一个慢"double"师。注意类型转换为 uint32_t
以避免溢出。并注意 (double)1000.0
单独的 .0
只会转换为 float 而不是 double。
我正在尝试使用 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。为什么?以及如何解决这个问题?
提前致谢。
REF_ADC_Volt和ADC_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;
此外,可能不需要使用双精度值。我相信单精度值(浮点数)就足够了,但这取决于您的应用程序,并且很难从您的最小示例中判断。
之前的答案完全正确,但如果代码执行时间和优化对您很重要,您可以使用 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;
现在只有一个慢"double"师。注意类型转换为 uint32_t
以避免溢出。并注意 (double)1000.0
单独的 .0
只会转换为 float 而不是 double。