Работа с памятью: V Припаять i2c!!! Сохранение выстрела Загрузка выстрела По аналогии всё остальное IR: V Добавить press and hold Проверка железа: Проверить все PWM Проверить функции управления железом setRollersSpeed setScrewkSpeed setPos stopShooting doShot Логика работы: Правильное переключение выстрелов с учётом repeatCount Переключение выстрелов в программе Переключение программ в макро Правильная пауза Индикация: Текущий буфер индикации Функция отображения скорости Обратный отсчёт Индикация ошибок Звук: Звук приёма IR Звук ошибки Звук включения Ошибки: В некоторый момент PID регулятор выдаёт 0 и двигатель не запускается не зависимо от входного значенияPWM uint32_t GetTimerClockFrequency(TIM_TypeDef *TIMx) { uint32_t clock_frequency = 0; uint32_t sysclk_frequency = 0; uint32_t hclk_frequency = 0; uint32_t apb1_frequency = 0; uint32_t apb2_frequency = 0; // Определяем источник системного тактирования (SYSCLK) switch (RCC->CFGR & RCC_CFGR_SWS) { case RCC_CFGR_SWS_HSI: // HSI используется как системный клок sysclk_frequency = 8000000; // HSI - 8 MHz break; case RCC_CFGR_SWS_HSE: // HSE используется как системный клок sysclk_frequency = HSE_VALUE; // Предположим, что значение HSE_VALUE определено break; case RCC_CFGR_SWS_PLL: // PLL используется как системный клок // Получаем значение входного тактового сигнала PLL if ((RCC->CFGR & RCC_CFGR_PLLSRC) == RCC_CFGR_PLLSRC_HSI_DIV2) { sysclk_frequency = 4000000; // HSI/2 - 4 MHz } else { sysclk_frequency = HSE_VALUE; // HSE_VALUE определено как 8 или 16 MHz } // Получаем множитель PLL uint32_t pll_mul = ((RCC->CFGR & RCC_CFGR_PLLMULL) >> 18) + 2; sysclk_frequency *= pll_mul; break; default: sysclk_frequency = 8000000; // По умолчанию HSI break; } // Определяем частоту шины AHB (HCLK) uint32_t ahb_prescaler = (RCC->CFGR & RCC_CFGR_HPRE) >> 4; if (ahb_prescaler < 8) { hclk_frequency = sysclk_frequency; } else { hclk_frequency = sysclk_frequency >> ((ahb_prescaler - 7)); } // Определяем частоту шины APB1 uint32_t apb1_prescaler = (RCC->CFGR & RCC_CFGR_PPRE1) >> 8; if (apb1_prescaler < 4) { apb1_frequency = hclk_frequency; } else { apb1_frequency = hclk_frequency >> ((apb1_prescaler - 3)); } // Определяем частоту шины APB2 uint32_t apb2_prescaler = (RCC->CFGR & RCC_CFGR_PPRE2) >> 11; if (apb2_prescaler < 4) { apb2_frequency = hclk_frequency; } else { apb2_frequency = hclk_frequency >> ((apb2_prescaler - 3)); } // Определяем частоту для конкретного таймера if (TIMx == TIM1 || TIMx == TIM8) { // Таймеры на шине APB2 clock_frequency = (apb2_prescaler == 0 ? apb2_frequency : apb2_frequency * 2); } else { // Таймеры на шине APB1 clock_frequency = (apb1_prescaler == 0 ? apb1_frequency : apb1_frequency * 2); } return clock_frequency; } // Функция для вычисления целевого значения uint32_t CalculateTargetCount(TIM_TypeDef *TIMx, uint32_t freq) { // Проверяем, что частота не равна нулю if (freq == 0) return 0xFFFFFFFF; // Защита от деления на ноль // Получаем частоту тактового генератора для данного таймера uint32_t clock_frequency = GetTimerClockFrequency(TIMx); // Получаем значение предделителя (PSC) и ARR таймера uint32_t psc = TIMx->PSC; uint32_t arr = TIMx->ARR; // Вычисляем частоту работы таймера uint32_t timer_frequency = clock_frequency / ((psc + 1) * (arr + 1)); // Вычисляем целевое значение счетчика для заданной частоты uint32_t target_count = timer_frequency / freq; return target_count; }