// ================================================================
// подключаем стандартную библиотеку LiquidCrystal
#include <LiquidCrystal.h>
#define TITLE "Multimeter"
#define R2 220.0 // резисторы входного делителя
#define R3 10000.0 // в омах
// Ячейки, используемые АЦП
//
float ADC_Kadc;
float ADC_Vref;
float ADC_Vin;
float ADC_Vmax;
float ADC_Vmid;
float ADC_Vsum;
int ADC_count;
volatile int ADC_result;
// Ячейки, используемые WDT_intr
//
volatile int WDT_count;
// Слово состояния программы
volatile unsigned int SYS_status;
#define SYS_STARTRDY 0x8000
#define SYS_SCREENRDY 0x4000
#define SYS_ADCRDY 0x2000
#define SYS_VOLTMETER 0x0004
// инициализируем объект-экран, передаём использованные
// для подключения контакты на Arduino в порядке:
// RS, E, DB4, DB5, DB6, DB7
//
LiquidCrystal lcd(12, 11, 7, 6, 5, 4);
char s[16];
char adc[16];
char Ver[] = "01.008b";
void setup() {
SYS_status = 0;
pinMode(LED_BUILTIN, OUTPUT); // настройка светодиода индикации
PrintTitle();
cli(); // запрещаем прерывания перед настройкой
SetWatchdogTimer();
SetADC();
sei(); // разрешаем прерывания после настройки
WDT_count = 0;
ADC_Vmax = 0;
ADC_Vmid = 0;
ADC_count = 0;
SYS_status |= SYS_VOLTMETER;
}
void loop() {
// тайм-аут на 3 секунды на заставку
if((SYS_status & SYS_STARTRDY) == 0) {
if(WDT_count > 6) {
SYS_status |= SYS_STARTRDY;
WDT_count = 0;
lcd.clear(); } }
// тайм-аут прошел, можно выполнять программу
else {
// работа в режиме вольтметра
if((SYS_status & SYS_VOLTMETER) != 0) {
// выводим на экран статическую информацию
if((SYS_status & SYS_SCREENRDY) == 0) {
lcd.setCursor(0, 0);
lcd.print(" Vin = V");
lcd.setCursor(0, 1);
lcd.print(" Vmax = V");
SYS_status |= SYS_SCREENRDY; }
// экран готов, можно выводить данные
else {
if((SYS_status & SYS_ADCRDY) != 0) {
ADC_Vin = (ADC_Kadc * ADC_result) / 1000;
if(ADC_Vin > ADC_Vmax) ADC_Vmax = ADC_Vin;
// расчет среднего напряжения
if(ADC_count > 1000) {
// Если число отсчетов больше 1000, то производится сброс среднего значения
ADC_Vmid = 0;
ADC_Vsum = 0;
ADC_count = 0; }
ADC_count++;
ADC_Vsum += ADC_Vin;
ADC_Vmid = ADC_Vsum / ADC_count;
dtostrf(ADC_Vin, 7, 3, adc);
lcd.setCursor(7, 0);
lcd.print(adc);
dtostrf(ADC_Vmax, 7, 3, adc);
lcd.setCursor(7, 1);
lcd.print(adc);
SYS_status &= ~SYS_ADCRDY;
}
}
}
}
}
// Подпрограмма печати заголовка программы
// =======================================
void PrintTitle() {
lcd.begin(16, 2);
lcd.print(TITLE); // печатаем первую строку на дисплее
lcd.setCursor(0, 1);
sprintf(s, "Version %s", Ver);
lcd.print(s); // печатаем вторую строку на дисплее
}
// Подпрограмма настройки прерываний сторожевого таймера
// =====================================================
void SetWatchdogTimer() {
// Настройка сторожевого таймера 0.5 sec
//
WDTCSR = (1 << WDCE) | (1 << WDE); // необходимо для изменения делителя таймера
WDTCSR = (0 << WDP3) | (1 << WDP2) | (0 << WDP1) | (1 << WDP0);// установка делителя 0,5 сек.
WDTCSR |= (1 << WDIE); // разрешение прерываний таймера
}
// Подпрограмма установки режимов АЦП
// ==================================
void SetADC() {
// Настройка делителя входного напряжения АЦП
ADC_Vref = 1100;
ADC_Kadc = (ADC_Vref * (R2 + R3)) / (1024 * R2);
// Установка опорного напряжения 1.1 В. и входа А0
// Устанавливаем только те биты, которые должны быть установлены в "1"
ADMUX = (1 << REFS0) | (1 << REFS1);
// Установка частоты преобразования
// делитель на 128
ADCSRA = (1 << ADEN) | (1 << ADIE) | (1 << ADPS2) | (1 << ADPS1) |(1 << ADPS0);
}
// Программа обработки прерывания сторожевого таймера
// 1. Обрабатывает светодиод
// 2. Обрабатывает запуск АЦП
// ==================================================
ISR(WDT_vect) {
// мигаем светодиодом раз в секунду
//
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
WDT_count++; // увеличиваем счетчик таймера
// В режиме вольтметра запускаем АЦП
if((SYS_status & SYS_VOLTMETER) != 0 & (SYS_status & SYS_ADCRDY) == 0) ADCSRA |= (1 << ADSC);
}
// Программа обработки прерывания АЦП
// ==================================
ISR(ADC_vect) {
SYS_status |= SYS_ADCRDY; // установка флага готовности данных АЦП
ADC_result = ADCL; // сохраняем младший байт результата АЦП
ADC_result += ADCH << 8; // сохраняем старший байт АЦП
}