Помогите с вольтметром SSD1306

Так как его внести в код?

Не надо ничего вносить. Я привел код постом выше. Там даже к аналоговым пинам батарею подключать не нужно

Там делов на 5 сек…

Я смотрю что это код в С, а у меня скетч

Поиском найдите этот файл на диске и исправьте …

Можно и более сложным путем пойти -

если вы понимаете что там и как …

чотаржу.

1 лайк

Давайте-ка я приведу его в виде “типа-библиотеки” чтобы закрыть вопрос раз и навсегда.

Первое, что нужно сделать - это загрузить в контроллер вот такой скетч:

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 вольт гонял) результат измерения напряжения на пине не зависит никак.

3 лайка

@konog то, что у меня написано в предыдущем посте - лучшее, что Вы сможете сделать с Нано. Но если хотите ещё более по уму, то надо отказаться от Наны и сделать на голом контроллере.

Дело в том, что разработчики Ардуино решили сэкономить на правильном питании аналоговой части контроллера и тупо соединили пины AVCC и VCC. Эсли же Вы хотите, чтобы аналоговая часть работала нормально, то Вам нужно сделать правильное питание аналоговой части, как написано в даташите: