summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorvlapa <vlapa@ya.ru>2026-06-13 17:15:14 +0300
committervlapa <vlapa@ya.ru>2026-06-13 17:15:14 +0300
commita147451733364217c9f380e0a22099b58155383f (patch)
treeed743dbf05c102d08c740ff19dee05f9dab24a37 /src
Firstmain
Diffstat (limited to 'src')
-rw-r--r--src/main.cpp456
-rw-r--r--src/pin.cpp44
-rw-r--r--src/pin.h65
3 files changed, 565 insertions, 0 deletions
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..9c2e009
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,456 @@
+/* ===============================================
+ Тахометр-мотометр - мои изыскания
+ 20250408 ... 20260330
+ v.6.3.30
+
+ AtMega8, 16MHz, Общий Катод - ОК
+
+ = FUSES = Low - 0x9E; High - 0xDF; Ext - 0xFF
+
+================================================== */
+
+#include <avr/interrupt.h>
+#include <util/delay.h>
+#include <avr/eeprom.h>
+#include <avr/wdt.h>
+#include "pin.h"
+
+// =======================================
+// ОПРЕДЕЛЯЕМ КОНСТАНТЫ:
+#define F_QUARTZ 16000000UL // частота кварца
+#define DIVIDER 64 // делитель таймера T2, задается в инициализации Т1
+#define PART_SEC 60000000UL / (1000000 / (F_QUARTZ / DIVIDER)) // часть секунды (для пересчета оборотов)
+
+// Количество импульсов на 1 оборот коленвала:
+#define REV_RATED_POWER 3600 // обороты при номинальной мощности (введите свои)
+
+// 8ми сегментный 4х разрядный индикатор:
+#define LEVEL 1 // (1) - Общий Анод, (0) - Общий Катод
+#define RAZR 4 // количество разрядов индикатора
+#define BRIGHT 0x7F // яркость свечения сегментов (больше значение - ярче)
+
+// Минимальный учетный интервал Моточасов:
+#define TIME_REV 60 // 3600 = 1минута; 216000 = 1час (60 * 60 * 60 (1сек * 1мин * 1час)
+#define MEM_CELL 0 // начальная ячейка памяти в которой будут храниться моточасы
+#define MEANING 0x0A0A0A0A // контрольное слово для проверки памяти
+#define DELAY_VISIBLE 100 // пауза для визуального отображения
+#define KOEFF 1 // коэффициент усреднения
+#define TIME_RESET_MOTO 5000 // задержка сброса моточасов
+#define MODE 3 // кол-во режимов отображения (моточасы -> тахометр -> вольтметр)
+#define TIME_STEP 10 // шаг измерения времени нажатия кнопки (мс)
+#define FIRST_MOTO_HOUR 142 // установка первоначального значения моточасов
+
+// =======================================
+// Рабочие переменные:
+// Отображаемые на индикаторе символы:
+uint8_t seg[22] = {
+ 0b11111100, // 0
+ 0b01100000, // 1
+ 0b11011010, // 2
+ 0b11110010, // 3
+ 0b01100110, // 4
+ 0b10110110, // 5
+ 0b10111110, // 6
+ 0b11100000, // 7
+ 0b11111110, // 8
+ 0b11110110, // 9
+ 0b11101110, // A
+ 0b00111110, // B
+ 0b00011010, // C
+ 0b01111010, // D
+ 0b10011110, // E
+ 0b10001110, // F
+ 0b00000000, // 10 - пусто
+ 0b00000010, // 11 - минус
+ 0b11000110, // 12 - градус
+ 0b00111000, // 13 - v
+ 0b00001010, // 14 - R
+ 0b10110110, // 15 - S
+};
+uint8_t dataIndic[RAZR]; // ячейки данных разрядов индикатора
+uint32_t countMoto = 0; // счетчик моточасов (накопитель оборотов) - 2
+uint32_t temp = 0; // счетчик оборотов
+uint8_t indicatorMode = 0; // счетчик для выбора режима индикации
+bool flagDot = false; // флаг для отображения точки
+bool flagFirstTaho = true; // флаг первого перехода на тахометр после запуска
+volatile uint32_t myMillis = 0; // счетчик миллисекунд
+uint32_t myMillisOld = 0; // старый счетчик миллисекунд
+volatile bool flagAdcConv = true;
+uint32_t adcResultOld = 0;
+
+// физическое состояние кнопки
+enum ButtonResult
+{
+ buttonNotPress, // код если кнопка не нажата
+ buttonShortPress, // код короткого нажатия
+ buttonLongPress // код длинного нажатия
+};
+
+// ################################################################
+// Усредняем показания оборотов:
+uint16_t filtr(uint16_t rev)
+{
+ static uint16_t revOld = 0; // предыдущие обороты двигателя
+ static uint8_t a = pow(2, KOEFF) - 1;
+ (revOld > rev / KOEFF) ? revOld = (a * revOld + rev) >> KOEFF : revOld = rev;
+ return revOld;
+}
+
+//================================================================
+// Усредняем показания напряжения:
+uint16_t filtrVcc(uint16_t revVcc)
+{
+ static uint16_t revOldVcc = 0; // предыдущее напряжение
+ static uint8_t a = (1 << KOEFF) - 1;
+ (revOldVcc > revVcc / (KOEFF)) ? revOldVcc = (a * revOldVcc + revVcc) >> (KOEFF) : revOldVcc = revVcc;
+ return revOldVcc;
+}
+
+//================================================================
+// Инициализация прерываний:
+void INTERRUPT_Init()
+{
+ // Инициализация T1:
+ TCCR1B |= (1 << CS11) | (1 << CS10); // делитель = 64
+ // Инициализация T2:
+ TCCR2 |= (1 << CS22); //(1 << CS21) | (1 << CS20); // делитель = 32
+ OCR2 = BRIGHT;
+ TIMSK |= (1 << OCIE2) | (1 << TOIE2);
+ // Инициализация INT0:
+ MCUCR |= (1 << ISC01) | (1 << ISC00); //-по фронту//(1 << ISC01);//-по спаду
+ GICR |= (1 << INT0);
+
+ // Инициализация ADC:
+ ADMUX |= (1 << REFS0) | (1 << REFS1) // опорное = 5в
+ | (1 << MUX2) | (1 << MUX0) // измеряем на ADC5
+ | (1 << ADLAR);
+ ADCSRA |= (1 << ADEN) // разрешение АЦП
+ | (1 << ADSC) // запуск преобразования
+ | (1 << ADPS2) | (1 << ADPS1) // делитель на 64 (250kHz)
+ | (1 << ADIE); // разрешение прерывания от АЦП
+}
+
+//================================================================
+// Включение сегментов:
+void segWork(uint8_t sTem)
+{
+ uint8_t segments = seg[sTem];
+ (LEVEL) ? segments = ~segments : segments = segments;
+ (segments & (1 << 7)) ? PORT_A |= (1 << SEG_A) : PORT_A &= ~(1 << SEG_A);
+ (segments & (1 << 6)) ? PORT_B |= (1 << SEG_B) : PORT_B &= ~(1 << SEG_B);
+ (segments & (1 << 5)) ? PORT_C |= (1 << SEG_C) : PORT_C &= ~(1 << SEG_C);
+ (segments & (1 << 4)) ? PORT_D |= (1 << SEG_D) : PORT_D &= ~(1 << SEG_D);
+ (segments & (1 << 3)) ? PORT_E |= (1 << SEG_E) : PORT_E &= ~(1 << SEG_E);
+ (segments & (1 << 2)) ? PORT_F |= (1 << SEG_F) : PORT_F &= ~(1 << SEG_F);
+ (segments & (1 << 1)) ? PORT_G |= (1 << SEG_G) : PORT_G &= ~(1 << SEG_G);
+ (segments & (1 << 0)) ? PORT_H |= (1 << SEG_H) : PORT_H &= ~(1 << SEG_H);
+}
+
+//================================================================
+// Гашение всех разрядов перед установкой сегментов:
+ISR(TIMER2_COMP_vect)
+{
+ (LEVEL) ? PORT_RAZ1 &= ~(1 << RAZ_1) : PORT_RAZ1 |= (1 << RAZ_1);
+ (LEVEL) ? PORT_RAZ2 &= ~(1 << RAZ_2) : PORT_RAZ2 |= (1 << RAZ_2);
+ (LEVEL) ? PORT_RAZ3 &= ~(1 << RAZ_3) : PORT_RAZ3 |= (1 << RAZ_3);
+ (LEVEL) ? PORT_RAZ4 &= ~(1 << RAZ_4) : PORT_RAZ4 |= (1 << RAZ_4);
+}
+
+//================================================================
+// Основная индикация:
+ISR(TIMER2_OVF_vect)
+{
+ static uint8_t s = 0;
+
+ switch (s)
+ {
+ case 0:
+ segWork(dataIndic[0]);
+ (LEVEL) ? PORT_RAZ1 |= (1 << RAZ_1) : PORT_RAZ1 &= ~(1 << RAZ_1);
+ break;
+
+ case 1:
+ segWork(dataIndic[1]);
+ if (flagDot)
+ {
+ (!LEVEL) ? PORT_H |= (1 << SEG_H) : PORT_H &= ~(1 << SEG_H);
+ }
+ else
+ {
+ (LEVEL) ? PORT_H |= (1 << SEG_H) : PORT_H &= ~(1 << SEG_H);
+ }
+
+ (LEVEL) ? PORT_RAZ2 |= (1 << RAZ_2) : PORT_RAZ2 &= ~(1 << RAZ_2);
+ break;
+
+ case 2:
+ segWork(dataIndic[2]);
+ (LEVEL) ? PORT_RAZ3 |= (1 << RAZ_3) : PORT_RAZ3 &= ~(1 << RAZ_3);
+ break;
+
+ case 3:
+ segWork(dataIndic[3]);
+ (LEVEL) ? PORT_RAZ4 |= (1 << RAZ_4) : PORT_RAZ4 &= ~(1 << RAZ_4);
+ break;
+ }
+ (s == 3) ? s = 0 : s++; // крутим активный разряд
+
+ // почти = 1мс (976 мкс)
+ myMillis++;
+}
+
+//================================================================
+// Запись данных в массив для отображения:
+void visible(uint16_t vis)
+{
+ dataIndic[3] = vis % 10;
+ dataIndic[2] = vis / 10 % 10;
+ dataIndic[1] = vis / 100 % 10;
+ dataIndic[0] = vis / 1000;
+
+ // Гасим "левый" ноль:
+ if (dataIndic[0] == 0)
+ {
+ dataIndic[0] = 0x10;
+ if (dataIndic[1] == 0)
+ {
+ dataIndic[1] = 0x10;
+ if (dataIndic[2] == 0)
+ {
+ dataIndic[2] = 0x10;
+ }
+ }
+ }
+}
+
+//===============================================================
+// Подсчет оборотов (по таймеру T2):
+ISR(INT0_vect)
+{
+ static uint32_t countRev = 0;
+
+ temp = TCNT1;
+ TCNT1 = 0;
+
+ if (countRev < TIME_REV) // если меньше учетного интервала времени - увеличиваем счетчик
+ {
+ countRev++;
+ }
+ else // иначе - увеличиваем счетчик моточасов
+ {
+ countRev = 0;
+ countMoto++;
+ }
+
+ temp = PART_SEC / temp;
+
+ if (flagFirstTaho)
+ {
+ indicatorMode = 1; // при первых оборотах двс, однократно включаем отображение тахометра
+ flagFirstTaho = false;
+ }
+}
+
+//===============================================================
+// Измеряем напряжение:
+void vccMeasurement()
+{
+ ADCSRA |= (1 << ADEN) | (1 << ADSC) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) | (1 << ADIE);
+ flagAdcConv = false;
+}
+
+//===============================================================
+// Сброс моточасов:
+void resEeprom()
+{
+ eeprom_update_dword((uint32_t *)MEM_CELL, MEANING);
+ eeprom_update_dword((uint32_t *)MEM_CELL + 1, 0);
+}
+
+//===============================================================
+// Читаем моточасы из EEPROM:
+bool readEeprom()
+{
+ if (eeprom_read_dword((uint32_t *)MEM_CELL) == MEANING)
+ {
+ countMoto = eeprom_read_dword((uint32_t *)MEM_CELL + 1);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+//===============================================================
+// ADC (измеряем напряжение):
+ISR(ADC_vect)
+{
+ ADCSRA = 0; // выключаем АЦП
+ uint16_t mes = ADCH; // filtrVcc(ADCH);
+ // если напряжение < 7v:
+ if (mes < 164)
+ {
+ // запрещаем прерывания:
+ cli();
+ (LEVEL) ? PORT_RAZ1 &= ~(1 << RAZ_1) : PORT_RAZ1 |= (1 << RAZ_1);
+ (LEVEL) ? PORT_RAZ2 &= ~(1 << RAZ_2) : PORT_RAZ2 |= (1 << RAZ_2);
+ (LEVEL) ? PORT_RAZ3 &= ~(1 << RAZ_3) : PORT_RAZ3 |= (1 << RAZ_3);
+ (LEVEL) ? PORT_RAZ4 &= ~(1 << RAZ_4) : PORT_RAZ4 |= (1 << RAZ_4);
+ // сохраняем моточасы в EEPROM:
+ eeprom_update_dword((uint32_t *)MEM_CELL + 1, countMoto);
+ for (uint8_t i = 0; i < RAZR; ++i)
+ {
+ dataIndic[i] = 16;
+ }
+ TCNT1 = 0; // сбрасываем счетчик оборотов
+
+ // и выключаемся:
+ while (1)
+ ;
+ }
+
+ adcResultOld = mes; //(mes - 164) * 10 + 700; // пересчет показаний напряжения
+ flagAdcConv = true;
+}
+
+//===============================================================
+// Обработка нажатия кнопки:
+enum ButtonResult readBtnPress(void)
+{
+ uint16_t btnPressTime = 0;
+ while (!(PIN_BTN & (1 << BTN)))
+ {
+ _delay_ms(TIME_STEP);
+ btnPressTime += TIME_STEP;
+ if (btnPressTime > TIME_RESET_MOTO)
+ {
+ btnPressTime = TIME_RESET_MOTO;
+ break;
+ }
+ }
+
+ if (btnPressTime >= TIME_RESET_MOTO)
+ return buttonLongPress;
+
+ if (btnPressTime >= 30)
+ return buttonShortPress;
+
+ return buttonNotPress;
+}
+
+//===============================================================
+int main(void)
+{
+ init_pin();
+ dataIndic[0] = 0x11;
+ dataIndic[1] = 0x11;
+ dataIndic[2] = 0x11;
+ dataIndic[3] = 0x11;
+
+ // Инициализируем прерывания:
+ INTERRUPT_Init();
+ // Разрешаем прерывания:
+ sei();
+
+ // проверяем нажатие кнопки для сброса:
+ if (readBtnPress() == buttonLongPress)
+ {
+ resEeprom();
+ for (uint8_t j = 0; j < 3; ++j)
+ {
+ dataIndic[0] = 0x14;
+ dataIndic[1] = 0xE;
+ dataIndic[2] = 0x15;
+ dataIndic[3] = 0x11;
+ _delay_ms(200);
+ dataIndic[0] = 0x10;
+ dataIndic[1] = 0x10;
+ dataIndic[2] = 0x10;
+ dataIndic[3] = 0x10;
+ _delay_ms(200);
+ }
+ }
+
+ // Запись начального значения моточасов:
+ if (FIRST_MOTO_HOUR > 0)
+ {
+ if (eeprom_read_dword((uint32_t *)MEM_CELL) != MEANING)
+ {
+ eeprom_update_dword((uint32_t *)MEM_CELL, MEANING);
+ eeprom_update_dword((uint32_t *)MEM_CELL + 1, FIRST_MOTO_HOUR);
+ }
+ }
+
+ // Читаем EEPROM:
+ if (!readEeprom())
+ {
+ resEeprom();
+ }
+
+ TCNT1 = 0;
+
+ //---------------------------------------------------------
+ while (1)
+ { // проверяем нажатие кнопки управления режимами:
+ if (readBtnPress() == buttonShortPress)
+ (indicatorMode < (MODE - 1)) ? indicatorMode++ : indicatorMode = 0;
+
+ switch (indicatorMode)
+ {
+ case 0:
+ { // отображаем моточасы:
+ flagDot = false;
+
+ if (countMoto > 9999)
+ countMoto = 0;
+
+ visible(countMoto);
+ myMillisOld = myMillis;
+ }
+ break;
+ case 1:
+ { // отображаем тахометр (и "убиваем" младший разряд):
+ uint16_t temp2 = filtr(temp);
+ if (temp2 < REV_RATED_POWER + 2000)
+ {
+ visible(temp2 / 10 * 10);
+ }
+ else
+ {
+ dataIndic[0] = 0x11;
+ dataIndic[1] = 0x11;
+ dataIndic[2] = 0x11;
+ dataIndic[3] = 0x11;
+ }
+ flagDot = false;
+ myMillisOld = myMillis;
+ }
+ break;
+ case 2:
+ { // отображаем вольтметр:
+ dataIndic[3] = 0x13;
+ // dataIndic[3] = adcResultOld % 10;
+ dataIndic[2] = adcResultOld / 10 % 10;
+ dataIndic[1] = adcResultOld / 100 % 10;
+ dataIndic[0] = adcResultOld / 1000;
+
+ flagDot = true;
+ // Гасим "левый" ноль:
+ if (dataIndic[0] == 0)
+ {
+ dataIndic[0] = 0x10;
+ }
+
+ myMillisOld = myMillis;
+ }
+ break;
+ }
+
+ // задержка для отображения цифр
+ while (myMillis <= myMillisOld + DELAY_VISIBLE)
+ {
+ vccMeasurement();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/pin.cpp b/src/pin.cpp
new file mode 100644
index 0000000..d0e1e98
--- /dev/null
+++ b/src/pin.cpp
@@ -0,0 +1,44 @@
+#include <avr/io.h>
+#include "pin.h"
+
+//===============================================================
+// Инициализация всех ПИНов:
+void init_pin()
+{
+ DDR_A |= (1 << SEG_A);
+ PORT_A |= (1 << SEG_A);
+ DDR_B |= (1 << SEG_B);
+ PORT_B |= (1 << SEG_B);
+ DDR_C |= (1 << SEG_C);
+ PORT_C |= (1 << SEG_C);
+ DDR_D |= (1 << SEG_D);
+ PORT_D |= (1 << SEG_D);
+ DDR_E |= (1 << SEG_E);
+ PORT_E |= (1 << SEG_E);
+ DDR_F |= (1 << SEG_F);
+ PORT_F |= (1 << SEG_F);
+ DDR_G |= (1 << SEG_G);
+ PORT_G |= (1 << SEG_G);
+ DDR_H |= (1 << SEG_H);
+ PORT_H |= (1 << SEG_H);
+
+ DDR_RAZ1 |= (1 << RAZ_1);
+ PORT_RAZ1 |= (1 << RAZ_1);
+ DDR_RAZ2 |= (1 << RAZ_2);
+ PORT_RAZ2 |= (1 << RAZ_2);
+ DDR_RAZ3 |= (1 << RAZ_3);
+ PORT_RAZ3 |= (1 << RAZ_3);
+ DDR_RAZ4 |= (1 << RAZ_4);
+ PORT_RAZ4 |= (1 << RAZ_4);
+
+ DDR_TAHO &= ~(1 << BTN);
+ PORT_TAHO |= (1 << BTN);
+
+ DDR_BTN &= ~(1 << BTN);
+ PORT_BTN |= (1 << BTN);
+
+ DDR_IGN_ON &= ~(1 << IGN_ON);
+ PORT_IGN_ON |= (1 << IGN_ON);
+}
+
+//===============================================================
diff --git a/src/pin.h b/src/pin.h
new file mode 100644
index 0000000..2d339e7
--- /dev/null
+++ b/src/pin.h
@@ -0,0 +1,65 @@
+#ifndef PIN_H_
+#define PIN_H_
+
+// ЗАДАЕМ ИСПОЛЬЗУЕМЫЕ ПИНы:
+// СЕГМЕНТЫ:
+#define SEG_A PC3
+#define DDR_A DDRC
+#define PORT_A PORTC
+#define SEG_B PB0
+#define DDR_B DDRB
+#define PORT_B PORTB
+#define SEG_C PB4
+#define DDR_C DDRB
+#define PORT_C PORTB
+#define SEG_D PC0
+#define DDR_D DDRC
+#define PORT_D PORTC
+#define SEG_E PC1
+#define DDR_E DDRC
+#define PORT_E PORTC
+#define SEG_F PC2
+#define DDR_F DDRC
+#define PORT_F PORTC
+#define SEG_G PB3
+#define DDR_G DDRB
+#define PORT_G PORTB
+#define SEG_H PB5
+#define DDR_H DDRB
+#define PORT_H PORTB
+// РАЗРЯДЫ:
+#define RAZ_1 PC4
+#define DDR_RAZ1 DDRC
+#define PORT_RAZ1 PORTC
+#define RAZ_2 PB1
+#define DDR_RAZ2 DDRB
+#define PORT_RAZ2 PORTB
+#define RAZ_3 PD7
+#define DDR_RAZ3 DDRD
+#define PORT_RAZ3 PORTD
+#define RAZ_4 PB2
+#define DDR_RAZ4 DDRB
+#define PORT_RAZ4 PORTB
+
+// КНОПКА:
+#define BTN PD0
+#define DDR_BTN DDRD
+#define PORT_BTN PORTD
+#define PIN_BTN PIND
+
+// ЗАЖИГАНИЕ / ВОЛЬТМЕТР:
+#define IGN_ON PC5
+#define DDR_IGN_ON DDRC
+#define PORT_IGN_ON PORTC
+#define PIN_IGN_ON PINC
+
+// ВХОД ТАХОМЕТРА:
+#define PIN_TAHO PD2
+#define DDR_TAHO DDRD
+#define PORT_TAHO PORTD
+
+//-----------------------------------
+// Инициализация PIN:
+void init_pin(void);
+
+#endif \ No newline at end of file