ATMEGA328p 将模拟值转换为电压
ATMEGA328p convert analog value to voltage
这是官方 ATMEGA328p data sheet 第 261 页的摘录:
如文档所述
Vin * 1024
ADC = ----------
Vref
我不明白的是模拟引脚可以为我们提供 10 位。这意味着 0x0 和 0x3ff (1023)
之间的值
所以我不明白为什么文档说 Vin * 1024
而不是 1023。因为对我来说 0v = 0 和 5v = 1023?
0 算作有效读数。 1..1023 是 1023 步加上 0 得到 1024。0x000 代表模拟接地,0x3FF 代表所选参考电压减去 1 LSB。
实际上,ADC 并不完美,因为结果可能有 1 个以上的错误计数,使用 1023 或 1024 真的无关紧要。理论上使用 1024 更准确。
是的,这是一个常见的错误。人们认为 10 位 = 2^10 = 1024 因此会有 1024 步,对吗?不,10 位 ADC 最多只能提供 1023 (3FFh) 的输出,因为这是 10 位可以容纳的最大数字。
因此,如果您对 1024 而不是 1023 进行算术运算,则会在计算中引入非常轻微的误差。我自己解决了这个错误,它在生产代码中存在了 10 多年而没有任何人注意到,当我修复这个错误时,也没有人注意到有什么不同 :) 但自然地,我们应该始终努力确保程序的正确性。
5v = 1024,是的,但是你不能用5V参考测量5V,你可以测量的最大值是Vref的1023/1024,即如果Vref=5V,那么最大值是4.995V。这个和所有高于这个的电压将读作 1023 (0x3FF)。
所以,你是对的,Vref 电压需要值 1024,这需要 11 位来存储,但你永远无法使用 ADC 测量该电压。
但是根据the datasheet(第 265 页的 28.9 ADC 特性)ATmega328P 中 ADC 的绝对精度通常为 2.2 LSB,因此您不必担心如果使用 1023 而不是 1LSB 会出现 1LSB 错误1024 作为除数。
但是,使用1024不仅在测量意义上更正确,而且在整数数学上也可以优化,因此不需要复杂的操作,例如除法:
// convert ADC value into millivolts (assuming Vref is 5000 mV)
uint16_t result_in_mv = ((uint32_t)adc_result * 5000UL) >> 10;
这是官方 ATMEGA328p data sheet 第 261 页的摘录:
如文档所述
Vin * 1024
ADC = ----------
Vref
我不明白的是模拟引脚可以为我们提供 10 位。这意味着 0x0 和 0x3ff (1023)
之间的值所以我不明白为什么文档说 Vin * 1024
而不是 1023。因为对我来说 0v = 0 和 5v = 1023?
0 算作有效读数。 1..1023 是 1023 步加上 0 得到 1024。0x000 代表模拟接地,0x3FF 代表所选参考电压减去 1 LSB。 实际上,ADC 并不完美,因为结果可能有 1 个以上的错误计数,使用 1023 或 1024 真的无关紧要。理论上使用 1024 更准确。
是的,这是一个常见的错误。人们认为 10 位 = 2^10 = 1024 因此会有 1024 步,对吗?不,10 位 ADC 最多只能提供 1023 (3FFh) 的输出,因为这是 10 位可以容纳的最大数字。
因此,如果您对 1024 而不是 1023 进行算术运算,则会在计算中引入非常轻微的误差。我自己解决了这个错误,它在生产代码中存在了 10 多年而没有任何人注意到,当我修复这个错误时,也没有人注意到有什么不同 :) 但自然地,我们应该始终努力确保程序的正确性。
5v = 1024,是的,但是你不能用5V参考测量5V,你可以测量的最大值是Vref的1023/1024,即如果Vref=5V,那么最大值是4.995V。这个和所有高于这个的电压将读作 1023 (0x3FF)。
所以,你是对的,Vref 电压需要值 1024,这需要 11 位来存储,但你永远无法使用 ADC 测量该电压。
但是根据the datasheet(第 265 页的 28.9 ADC 特性)ATmega328P 中 ADC 的绝对精度通常为 2.2 LSB,因此您不必担心如果使用 1023 而不是 1LSB 会出现 1LSB 错误1024 作为除数。
但是,使用1024不仅在测量意义上更正确,而且在整数数学上也可以优化,因此不需要复杂的操作,例如除法:
// convert ADC value into millivolts (assuming Vref is 5000 mV)
uint16_t result_in_mv = ((uint32_t)adc_result * 5000UL) >> 10;