#include #include #include // Инициализация дисплея SH1106 U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); // Назначение пинов #define PIN_CONFIRM PB3 // Кнопка подтверждения (ОК) #define PIN_BACK PB4 // Кнопка возврата (BACK) #define ENC_CLK PB5 // Энкодер (CLK) #define ENC_DT PB8 // Энкодер (DT) #define ENC_SW PB9 // Кнопка энкодера (пока не используется, но настроена) #define FREQ_PIN PA0 // Вход частотомера // Основные переменные uint32_t prevCount = 0; double real_frequency = 0.0; bool isCalibrating = false; double calibrationFactor = 1.0; uint32_t gateTime = 1000; uint32_t lastDrawTime = 0; uint32_t lastMeasureTime = 0; void setup() { Serial.begin(115200); // ЖЕСТКОЕ ОТКЛЮЧЕНИЕ JTAG ДЛЯ ПИНОВ PB3 И PB4 НА УРОВНЕ РЕГИСТРОВ // Включаем тактирование альтернативных функций и отключаем JTAG (оставляем SWD) RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; AFIO->MAPR = (AFIO->MAPR & ~AFIO_MAPR_SWJ_CFG) | AFIO_MAPR_SWJ_CFG_JTAGDISABLE; // Теперь пины PB3 и PB4 работают как обычные входы/выходы! pinMode(PIN_CONFIRM, INPUT_PULLUP); pinMode(PIN_BACK, INPUT_PULLUP); pinMode(ENC_CLK, INPUT_PULLUP); pinMode(ENC_DT, INPUT_PULLUP); pinMode(ENC_SW, INPUT_PULLUP); pinMode(FREQ_PIN, INPUT); Wire.begin(); if (!u8g2.begin()) { Serial.println("Ошибка инициализации дисплея!"); while(1); } // Настройка таймера TIM2 RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; TIM2->SMCR = TIM_SMCR_SMS_2 | TIM_SMCR_ECE; TIM2->CCMR1 = TIM_CCMR1_CC1S_0; TIM2->PSC = 0; TIM2->CR1 |= TIM_CR1_CEN; prevCount = TIM2->CNT; lastMeasureTime = millis(); } void loop() { uint32_t currentTime = millis(); // 1. Измерение частоты if (currentTime - lastMeasureTime >= gateTime) { uint32_t currentCount = TIM2->CNT; uint32_t elapsed = currentTime - lastMeasureTime; lastMeasureTime = currentTime; int32_t diff = (int32_t)(currentCount - prevCount); if (diff < 0) diff += 0x10000; prevCount = currentCount; real_frequency = (((double)diff * 1000.0) / elapsed) * calibrationFactor; } // 2. Исправленная и надежная обработка энкодера static int lastClkState = HIGH; int currentClkState = digitalRead(ENC_CLK); if (currentClkState != lastClkState) { // Ловим переход из HIGH в LOW (спад импульса) if (currentClkState == LOW) { // Если DT отличается от CLK, крутим в одну сторону if (digitalRead(ENC_DT) != LOW) { if (isCalibrating) { calibrationFactor += 0.000001; } else { if (gateTime < 10000) gateTime *= 10; } } // Иначе крутим в другую сторону else { if (isCalibrating) { calibrationFactor -= 0.000001; } else { if (gateTime > 100) gateTime /= 10; } } } lastClkState = currentClkState; } // 3. Обработка кнопки CONFIRM (Вход/Выход из калибровки) if (digitalRead(PIN_CONFIRM) == LOW) { delay(50); // Антидребезг чуть побольше if (digitalRead(PIN_CONFIRM) == LOW) { isCalibrating = !isCalibrating; // Переключаем режим while(digitalRead(PIN_CONFIRM) == LOW); // Ждем, пока отпустишь кнопку } } // 4. Обработка кнопки BACK (Сброс калибровки в 1.0) if (digitalRead(PIN_BACK) == LOW) { delay(50); if (digitalRead(PIN_BACK) == LOW) { calibrationFactor = 1.0; // Сбрасываем коэффициент isCalibrating = false; // Выходим из режима калибровки while(digitalRead(PIN_BACK) == LOW); // Ждем отпускания } } // 5. Вывод на экран без мерцания (раз в 150 мс для отзывчивости энкодера) if (currentTime - lastDrawTime >= 150) { lastDrawTime = currentTime; u8g2.clearBuffer(); u8g2.setFont(u8g2_font_6x10_tf); u8g2.drawStr(0, 12, "Freq Hz:"); u8g2.setCursor(60, 12); u8g2.print(real_frequency, 2); u8g2.drawStr(0, 28, "Gate ms:"); u8g2.setCursor(60, 28); u8g2.print(gateTime); u8g2.drawStr(0, 44, "Calib F:"); u8g2.setCursor(60, 44); u8g2.print(calibrationFactor, 6); // Показываем 6 знаков после запятой! u8g2.drawStr(0, 60, "Mode:"); u8g2.setCursor(40, 60); u8g2.print(isCalibrating ? "CALIBRATE" : "NORMAL"); u8g2.sendBuffer(); } }