diff options
| author | vlapa <vlapa@ya.ru> | 2026-06-13 17:15:14 +0300 |
|---|---|---|
| committer | vlapa <vlapa@ya.ru> | 2026-06-13 17:15:14 +0300 |
| commit | a147451733364217c9f380e0a22099b58155383f (patch) | |
| tree | ed743dbf05c102d08c740ff19dee05f9dab24a37 | |
Firstmain
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | description | bin | 0 -> 74 bytes | |||
| -rw-r--r-- | platformio.ini | 19 | ||||
| -rw-r--r-- | src/main.cpp | 456 | ||||
| -rw-r--r-- | src/pin.cpp | 44 | ||||
| -rw-r--r-- | src/pin.h | 65 |
6 files changed, 587 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bd997cc --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.pio +.vscode + diff --git a/description b/description Binary files differnew file mode 100644 index 0000000..72864f6 --- /dev/null +++ b/description diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..7368991 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,19 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:ATmega8] +platform = atmelavr +board = ATmega8 +framework = arduino + +upload_speed = 115200 +monitor_speed = 115200 + +upload_protocol = usbasp
\ No newline at end of file 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 |
