Так как его внести в код?
Не надо ничего вносить. Я привел код постом выше. Там даже к аналоговым пинам батарею подключать не нужно
Я смотрю что это код в С, а у меня скетч
Поиском найдите этот файл на диске и исправьте …
Можно и более сложным путем пойти -
если вы понимаете что там и как …
чотаржу.
Давайте-ка я приведу его в виде “типа-библиотеки” чтобы закрыть вопрос раз и навсегда.
Первое, что нужно сделать - это загрузить в контроллер вот такой скетч:
void setup(void) { analogReference (INTERNAL); analogRead (A0); }
void loop(void) {}
запустить его и тщательно измерить напряжение на пине ARef. Лучше несколько раз и усреднить. Полученное значение (оно будет примерно 1.1В), записать в милливольтах, т.е. оно должно быть примерно 1100 (между 1000 и 1200).
Это значение уникально для каждого контроллера, поэтому его надо замерять на конкретном контроллере, который будет использоваться.
Теперь, когда оно у нас есть, можно пользоваться “типа-библиотекой”. Она состоит из основного файла
ADCHelper.h
#ifndef ADCHELPER_H
#define ADCHELPER_H
//
// Функции
//
// inline static void enableADC(const ADCREFFERENCE ref = ADC_VCC)
// Включает ADC и устанавливает рееференс
//
// inline static void disableADC(void)
// Выключает ADC вплоть до электропитания
//
// inline static int batteryVoltage(void)
// Возвращает напряжение питания в десятых долях вольта
//
// inline static int pinVoltage(const ADCPIN pin = ADCVBG)
// Возвращает напряжение на пине в десятых долях вольта
//
//
// Константа adcInternalRefference - внутреннее опорное
// напряжение микросхемы В МИЛЛИВОЛЬТАХ. Константа обязана
// быть определена при помощи #define ДО того, как включён
// этот файл (т.е. до #include "ADCHelper.h")
// Если не определена - будет предупреждение и будет использовано
// значение по умолчанию 1100
//
// Способ определения этой константы
// 1. Загрузить в контроллер программу:
// void setup(void) { analogReference (INTERNAL); analogRead (A0); }
// void loop(void) {}
// 2. когда она загрузится, тщательно измерить напряжение на пине ARef
// лучше несколько раз и усреднить. Это и есть эта константа
//
#ifndef adcInternalRefference
#warning 'adcInternalRefference' constant is not defined. Default value is used.
#define adcInternalRefference 1100
#endif
//
// USE_ADC_NOISE_REDUCTION должна быть true, если нужно использовать
// ADC Noise Reduction и false - если не нужно.
//
#define USE_ADC_NOISE_REDUCTION false
#include "bitmask.h"
#if USE_ADC_NOISE_REDUCTION
#include <avr/sleep.h>
#endif
#if (__AVR_ATtinyDetected__)
enum ADCPIN {
ADC0 = 0,
ADC1 = 1,
ADC2 = 2,
ADC3 = 3,
ADC4 = 15, // Temperature
ADCVBG = 12,
ACDGND = 13
};
// 0x2F
enum ADCREFFERENCE {
ADC_VCC = 0,
ADC_EXTERNAL = 0x40,
ADC_INTERNAL_11 = 0x80,
ADC_INTERNAL_256_NO_CAP = 0x90,
ADC_INTERNAL_256_CAP = 0xD0
};
#else
enum ADCPIN {
ADC0 = 0,
ADC1 = 1,
ADC2 = 2,
ADC3 = 3,
ADC4 = 4,
ADC5 = 5,
ADC6 = 6,
ADC7 = 7,
ADC8 = 8, // Temperature
ADCVBG = 14, // Внутреннее опорное
ACDGND = 15
};
enum ADCREFFERENCE {
ADC_AREF = 0,
ADC_AVCC = 0x40,
ADC_VCC = 0x40,
ADC_INTERNAL_11 = 0xC0
};
#endif
namespace ADCHelperInternals {
static inline void adcEnable(void) { PRR &= ~bitMask(PRADC); ADCSRA = bitMask(ADEN); ADMUX = 0; ADCSRB = 0; }
static inline void adcDisable(void) { ADCSRA = 0; PRR |= bitMask(PRADC); }
static inline void adcRefference(const ADCREFFERENCE source = ADC_VCC) { ADMUX = (ADMUX & 0x2F) | source; }
static inline void adcSetADLAR(void) { ADMUX |= bitMask(ADLAR); }
static inline void adcClearADLAR(void) { ADMUX &= ~bitMask(ADLAR); }
static inline void adcChoosePin(const ADCPIN pin) { ADMUX = (ADMUX & 0xF0) | pin; }
#if USE_ADC_NOISE_REDUCTION
static inline void adcStart(void) { ADCSRA |= bitMask(ADSC, ADIE); }
static inline void adcClearInterruptBit(void) { ADCSRA &= ~ bitMask(ADIE); }
#else
static inline void adcStart(void) { ADCSRA |= bitMask(ADSC); }
#endif
static inline bool adcInProgress(void) { return ADCSRA & bitMask(ADSC); }
static inline bool adcReady(void) { return !adcInProgress(); }
static inline unsigned adcGet(void) { return ADC; }
inline static constexpr uint8_t adcPrescaler(void) {
return
F_CPU >= 16000000 ? bitMask(ADPS2, ADPS1, ADPS0) :
F_CPU >= 8000000 ? bitMask(ADPS2, ADPS1) :
F_CPU >= 4000000 ? bitMask(ADPS2, ADPS0) :
F_CPU >= 2000000 ? bitMask(ADPS2) :
F_CPU >= 1000000 ? bitMask(ADPS1, ADPS0) :
bitMask(ADPS0);
}
inline static int doMeasure(void) {
#if USE_ADC_NOISE_REDUCTION
const uint8_t oldSREG = SREG;
sei();
set_sleep_mode(SLEEP_MODE_ADC);
adcStart();
sleep_mode();
const int res = adcGet();
adcClearInterruptBit();
SREG = oldSREG;
#else
adcStart();
while (adcInProgress());
const int res = adcGet();
#endif
return res;
}
#if USE_ADC_NOISE_REDUCTION
EMPTY_INTERRUPT (ADC_vect);
#endif
// Возвращает напряжение питания в десятых долях вольта
inline static int getBatteryVoltage(void) {
adcChoosePin(ADCVBG);
// 24.4 of DataSheet
// When the bandgap reference voltage is used as input to the ADC,
// it will take a certain time for the voltage to stabilize. If not
// stabilized, the first value read after the first conversion may be wrong.
// Экспериментально установлено: 200 мкс - вроде, нормально, 100 - мало.
_delay_us(250);
const int adc = doMeasure();
return (((adcInternalRefference * 1024L + adc / 2) / adc) + 50) / 100;
}
// Возвращает напряжение на пине в десятых долях вольта
inline static int getPinVoltage(const ADCPIN pin) {
const long vcc = getBatteryVoltage(); // напряжение питания
adcChoosePin(pin);
const int adc = doMeasure(); // напряжение на пине
return (vcc * adc + 512L) / 1024L;
}
}; // namespace ADCHelperInternals
inline static int batteryVoltage(void) {
return ADCHelperInternals::getBatteryVoltage();
}
inline static int pinVoltage(const ADCPIN pin = ADCVBG) {
return ADCHelperInternals::getPinVoltage(pin);
}
inline static void enableADC(const ADCREFFERENCE ref = ADC_VCC) {
ADCHelperInternals::adcEnable();
ADCSRA |= ADCHelperInternals::adcPrescaler();
ADCHelperInternals::adcRefference(ref);
ADCHelperInternals::adcClearADLAR();
_delay_ms(1); // пусть "пропитается"
ADCHelperInternals::adcStart(); // плевать на первый замер
while (ADCHelperInternals::adcInProgress());
}
inline static void disableADC(void) { ADCHelperInternals::adcDisable(); }
#endif // ADCHELPER_H
и вспомогательного
bitmask.h
#ifndef BITMASK_H
#define BITMASK_H
inline constexpr uint8_t bitMask(const uint8_t m1) { return 1 << m1; }
inline constexpr uint8_t bitMask(const uint8_t m1, const uint8_t m2) { return bitMask(m1) | bitMask(m2); }
inline constexpr uint8_t bitMask(const uint8_t m1, const uint8_t m2, const uint8_t m3) { return bitMask(m2, m3) | bitMask(m1); }
inline constexpr uint8_t bitMask(const uint8_t m1, const uint8_t m2, const uint8_t m3, const uint8_t m4) { return bitMask(m2, m3, m4) | bitMask(m1); }
inline constexpr uint8_t bitMask(const uint8_t m1, const uint8_t m2, const uint8_t m3, const uint8_t m4, const uint8_t m5) { return bitMask(m2, m3, m4, m5) | bitMask(m1); }
inline constexpr uint8_t bitMask(const uint8_t m1, const uint8_t m2, const uint8_t m3, const uint8_t m4, const uint8_t m5, const uint8_t m6) { return bitMask(m2, m3, m4, m5, m6) | bitMask(m1); }
inline constexpr uint8_t bitMask(const uint8_t m1, const uint8_t m2, const uint8_t m3, const uint8_t m4, const uint8_t m5, const uint8_t m6, const uint8_t m7) { return bitMask(m2, m3, m4, m5, m6, m7) | bitMask(m1); }
inline constexpr uint8_t bitMask(const uint8_t m1, const uint8_t m2, const uint8_t m3, const uint8_t m4, const uint8_t m5, const uint8_t m6, const uint8_t m7, const uint8_t m8) { return bitMask(m2, m3, m4, m5, m6, m7, m8) | bitMask(m1); }
#endif // BITMASK_H
Вот
Пример использования
#define printVar(x) do { Serial.print(#x "="); Serial.println(x); } while (false)
#define adcInternalRefference 1072
#include "ADCHelper.h"
void setup(void) {
Serial.begin(9600);
enableADC();
const float battery = batteryVoltage() / 10.0;
printVar(battery);
}
void loop(void) {
delay(1000);
const float pinA3 = pinVoltage(ADC3) / 10.0;
printVar(pinA3);
}
В строке №3 примера та самая константа, которую мы измеряли в начале.
В строке №9 замеряется напряжение питания (так я контролирую разряд батареи)
В строке №15 замеряется напряжение на пине А3. Это измерение производится с учётом реального текущего напряжения питания. Т.е. в реальности там измеряется сначала напряжение питания, потом напряжение на пине, а потом всё аккуратно считается.
Описания функций есть в комментариях в начале файла библиотеки.
Тут есть нюанс. При измерении можно “выключать” процессор, чтобы уменьшить влияние его шумов на результат измерений. Но это приводит к остановке таймеров (кроме асинхронного) на время измерения и потому может создать дополнительную головную боль. В библиотеке эта опция (отключение процессора на время измерения) по умолчанию выключена. Чтобы включить, замените false
на true
в строке №43 библиотеки.
Запускайте, проверяйте. Я запитал Нану от лабораторника (через пин 5В), а на третий пин подал независимое напряжение с другого выхода лабораторника. Работает хорошо. От изменения напряжения питания (от 3 до 5 вольт гонял) результат измерения напряжения на пине не зависит никак.
@konog то, что у меня написано в предыдущем посте - лучшее, что Вы сможете сделать с Нано. Но если хотите ещё более по уму, то надо отказаться от Наны и сделать на голом контроллере.
Дело в том, что разработчики Ардуино решили сэкономить на правильном питании аналоговой части контроллера и тупо соединили пины AVCC и VCC. Эсли же Вы хотите, чтобы аналоговая часть работала нормально, то Вам нужно сделать правильное питание аналоговой части, как написано в даташите: