/* =============================================== Тахометр-мотометр - мои изыскания 20250108 ... 20250206 v.041 tiny2313, 16MHz, Общий Катод - ОК = FUSES = Low - 0xFF; High - 0xDB; Ext - 0xFF ================================================== */ #include #include #include #include #include "pin.h" // ======================================= // ОПРЕДЕЛЯЕМ КОНСТАНТЫ: #define F_QUARTZ 16000000UL // частота кварца #define DIVIDER 64 // делитель таймера T1, задается в инициализации Т1 #define PART_SEC 60000000 / (1000000 / (F_QUARTZ / DIVIDER)) // часть секунды (для пересчета оборотов) // Количество импульсов на 1 оборот коленвала: #define REV_RATED_POWER 3600 // обороты при номинальной мощности (введите свои) #define COUNT_PULSE 1 // 4 - импульса на оборот; 2 - 2 импульса; 1 - 1 импульс // 8ми сегментный 4х разрядный индикатор: #define LEVEL 0 // (1) - Общий Анод, (0) - Общий Катод #define RAZR 4 // количество разрядов индикатора #define BRIGHT 0x7F // яркость свечения сегментов (больше значение - ярче) // Минимальный учетный интервал Моточасов: #define TIME 3600 // 3600=1минута; 216000=1час (60 * 60 * 60 (1сек * 1мин * 1час) #define MEM_CELL 8 // начальная ячейка памяти в которой будут храниться моточасы #define MEANING 0x0A0A0A0A // контрольное слово для проверки памяти #define DELAY_VISIBLE 100 // пауза для визуального отображения #define KOEFF 3 // коэффициент усреднения #define TIME_RESET_MOTO 5000 // задержка сброса моточасов // ======================================= // Рабочие переменные: // Отображаемые на индикаторе символы: uint8_t seg[19] = { 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, // 16 - пусто 0b00000010, // 17 - минус 0b11000110, // 18 - градус }; uint8_t data[RAZR]; // ячейки данных разрядов индикатора uint32_t countMoto = 0; // счетчик моточасов (накопитель оборотов) uint16_t temp = 0; // счетчик оборотов bool flagRev = false; // флаг для индикации bool flagDot = false; // флаг для отображения точки в моточасах uint16_t revOld = 0; // предыдущие обороты двигателя uint32_t myMillis = 0; // счетчик миллисекунд uint32_t myMillisOld = 0; // старый счетчик миллисекунд // ################################################################ // Усредняем показания оборотов: uint16_t filtr(uint16_t rev) { static uint8_t a = pow(2, KOEFF) - 1; (revOld > rev / KOEFF) ? revOld = (a * revOld + rev) >> KOEFF : revOld = rev; return revOld; } //================================================================ // Инициализация прерываний: void INTERRUPT_Init() { // Инициализация T0: TCCR0B |= (1 << CS01) | (1 << CS00); // делитель = 64 OCR0A = BRIGHT; OCR0B = 250; TIMSK |= (1 << TOIE0) | (1 << OCIE0A); // Инициализация T1: TCCR1B |= (1 << CS11) | (1 << CS10); // делитель = 64 // Инициализация INT0: MCUCR |= (1 << ISC01) | (1 << ISC00); //-по фронту//(1 << ISC01);//-по спаду // GIMSK |= (1 << INT0); } //================================================================ // Включение сегментов: 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(TIMER0_COMPA_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(TIMER0_OVF_vect) { static uint8_t s = 0; switch (s) { case 0: segWork(data[0]); (LEVEL) ? PORT_RAZ1 |= (1 << RAZ_1) : PORT_RAZ1 &= ~(1 << RAZ_1); break; case 1: segWork(data[1]); (flagDot && !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(data[2]); (LEVEL) ? PORT_RAZ3 |= (1 << RAZ_3) : PORT_RAZ3 &= ~(1 << RAZ_3); break; case 3: segWork(data[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) { for (uint8_t c = 4; c > 0; c--) { data[c - 1] = vis % 10; vis /= 10; // Гасим "левый" ноль: if (data[0] == 0) { data[0] = 16; } } } //=============================================================== // Подсчет оборотов (по таймеру T1): ISR(INT0_vect) { temp = TCNT1 * COUNT_PULSE; TCNT1 = 0; temp = PART_SEC / temp; countMoto += temp; // flagRev = true; } //=============================================================== // Проверяем включено-ли зажигание: void onIgnition() { if (!(PIN_IGN_ON & (1 << IGN_ON))) { // если - нет, то сохраняем моточасы в EEPROM: eeprom_update_dword((uint32_t *)MEM_CELL + 1, countMoto); // и выключаемся: while (1) ; } } //=============================================================== // Сброс моточасов: void resEeprom() { eeprom_update_dword((uint32_t *)MEM_CELL, MEANING); eeprom_update_dword((uint32_t *)MEM_CELL + 1, 0); } //=============================================================== // Проверка нажатия кнопки сброса моточасов: // void checkPinRes() // { // if (!(PIN_BTN & (1 << BTN))) // { // _delay_ms(5000); // if (!(PIN_BTN & (1 << BTN))) // { // // если 3сек еще нажата, то сброс моточасов // countMoto = 0; // resEeprom(); // } // } // } //=============================================================== // Проверка нажатия кнопки сброса моточасов: void checkPinMode() { static bool pinCondition = false; static bool flagResEeprom = true; static uint32_t pinCount = 0; if (!(PIN_BTN & (1 << BTN)) && !pinCondition) { flagRev = !flagRev; pinCondition = 1; pinCount = myMillis; } if (!(PIN_BTN & (1 << BTN)) && pinCondition) { if (myMillis <= pinCount + TIME_RESET_MOTO) { pinCount++; visible(pinCount); } else { countMoto = 0; if (flagResEeprom) { resEeprom(); } else { flagResEeprom = false; } } } else { pinCondition = false; pinCount = 0; flagResEeprom = true; } } //=============================================================== int main(void) { wdt_reset(); init_pin(); wdt_enable(WDTO_1S); wdt_reset(); //--------------------------------------------------------- // Читаем моточасы из EEPROM: if (eeprom_read_dword((uint32_t *)MEM_CELL) == MEANING) { countMoto = eeprom_read_dword((uint32_t *)MEM_CELL + 1); } else { resEeprom(); } // Инициализируем прерывания: INTERRUPT_Init(); // Разрешаем прерывания: wdt_reset(); sei(); //--------------------------------------------------------- while (1) { checkPinMode(); // Если включен режим тахометра: if (flagRev) { // то отображаем тахометр (и "убиваем" младший разряд) visible(filtr(temp) / 10 * 10); // flagRev = false; flagDot = false; myMillisOld = myMillis; } else { revOld = 0; // если нет - отображаем моточасы uint16_t data; // Если меньше 100 мото/часов - отображаем с минутами: if ((countMoto / REV_RATED_POWER / TIME) < 100) { data = countMoto / REV_RATED_POWER / TIME * 100; data += countMoto / REV_RATED_POWER / (TIME / 60) % 60; flagDot = true; } // если больше или равно 100 мото/часов - отображаем только часы: else { data = countMoto / REV_RATED_POWER / TIME; flagDot = false; } if ((countMoto / REV_RATED_POWER / TIME) >= 1000) countMoto = 0; visible(data); myMillisOld = myMillis; } // задержка для отображения цифр while (myMillis <= myMillisOld + DELAY_VISIBLE) { onIgnition(); wdt_reset(); } } }