eikhner

main.c

Sep 14th, 2025 (edited)
53
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 31.66 KB | None | 0 0
  1. /* USER CODE BEGIN Header */
  2. /**
  3.  ******************************************************************************
  4.  * @file           : main.c
  5.  * @brief          : Main program body (с антишумной обработкой ADC)
  6.  ******************************************************************************
  7.  * @attention
  8.  *
  9.  * © 2025 STMicroelectronics. All rights reserved.
  10.  * This software component is licensed by ST under BSD 3-Clause license,
  11.  * the "License"; You may not use this file except in compliance with the
  12.  * License. You may obtain a copy of the License at:
  13.  *                    opensource.org/licenses/BSD-3-Clause
  14.  *
  15.  ******************************************************************************
  16.  */
  17. /* USER CODE END Header */
  18. /* Includes ------------------------------------------------------------------*/
  19. #include "main.h"
  20.  
  21. /* Private includes ----------------------------------------------------------*/
  22. /* USER CODE BEGIN Includes */
  23. #include <stdio.h>
  24. #include <stdbool.h>
  25. #include <string.h>                 // добавим для использования функции strlen
  26. #include "lcd_shared.h"
  27. #include "button.h"
  28. #include "menu.h"
  29.  
  30. #include "test_i2c.h"
  31. #include "eeprom_health.h"
  32.  
  33. #include "config_version.h"   // CONFIG_VERSION
  34. #include "app_config.h"       // g_cfg (RAM-копия)
  35. #include "config_defaults.h"  // defaults_get, sanitize
  36.  
  37. /* USER CODE END Includes */
  38.  
  39. /* Private typedef -----------------------------------------------------------*/
  40. /* USER CODE BEGIN PTD */
  41. /**
  42.  * @brief Фильтр "скользящее медианное окно" из трёх значений (u16).
  43.  */
  44. typedef struct {
  45.     uint16_t a, b, c;
  46. } median3_t;
  47. /* USER CODE END PTD */
  48.  
  49. /* Private define ------------------------------------------------------------*/
  50. /* USER CODE BEGIN PD */
  51. #define ADC_REFERENCE_VOLTAGE   1.205f       /**< Типовое Vrefint, В (для F1 без калибровки — типовое значение) */
  52. #define ADC_MAX                 0xFFFu       /**< 4095: 12-битный АЦП */
  53.  
  54. #define UART_OUT                           // определим вывод по интерфейсу USART
  55.  
  56. /** @name Настройки антишумной обработки
  57.  *  @{
  58.  */
  59. #define ADC_OSR               64U   /**< Oversampling: число выборок на канал в одном DMA буфере (степень 2) */
  60. #define ADC_EMA_K              3U   /**< EMA: k=3 → α=1/8 (чем больше k — тем плавнее, но медленнее) */
  61. /** @} */
  62. /* USER CODE END PD */
  63.  
  64. /* Private macro -------------------------------------------------------------*/
  65. /* USER CODE BEGIN PM */
  66. #define ARRAY_LEN(x) (sizeof(x)/sizeof((x)[0]))
  67. /* USER CODE END PM */
  68.  
  69. /* Private variables ---------------------------------------------------------*/
  70. ADC_HandleTypeDef hadc1;
  71. DMA_HandleTypeDef hdma_adc1;
  72.  
  73. I2C_HandleTypeDef hi2c1;
  74. I2C_HandleTypeDef hi2c2;
  75.  
  76. TIM_HandleTypeDef htim4;
  77.  
  78. UART_HandleTypeDef huart1;
  79.  
  80. /* USER CODE BEGIN PV */
  81.  
  82. /* _______________________________________________ ОБЪЯВЛЕН�?Е ПЕРЕМЕННЫХ ________________________________________________________ */
  83.  
  84. /** @brief Буфер для преобразования чисел в строку (для LCD/USART). */
  85. char strBuffer[16]; // буфер для преобразования чисел в строковый вид функцией sprintf
  86.  
  87. /** @brief Напряжение питания МК (В) — вычисляется через Vrefint (отфильтрованный). */
  88. float mcuVoltage = 0;
  89.  
  90. /** @brief Последние «мгновенные» отсчёты АЦП (оставлены для совместимости/отладки).
  91.  *  Порядок каналов: [0] — VREFINT, [1] — A0, [2] — A1, [3] — A2.
  92.  */
  93. uint16_t adcData[4] = { 0, }; // 0 - напряж питания . 1 - A0. 2 - A1. 3 - A2
  94.  
  95. /** @brief Канальные напряжения (В) — для A0..A2. */
  96. float adcVoltage_1 = 0;
  97. float adcVoltage_2 = 0;
  98. float adcVoltage_3 = 0;
  99.  
  100. /* ======= Новые переменные для антишумной обработки (всё задокументировано) ======= */
  101.  
  102. /** @brief Большой DMA буфер для oversampling: 4 канала × ADC_OSR выборок (режим Circular). */
  103. static uint16_t adcDMABuf[4U * ADC_OSR];
  104.  
  105. /** @brief Промежуточные суммы oversampling по каждому каналу (набираются между Half/Full колбэками). */
  106. static uint32_t osSum[4] = { 0, 0, 0, 0 };
  107.  
  108. /** @brief Флаг: обработана половина буфера (HalfCplt уже был). */
  109. static bool osGotHalf = false;
  110.  
  111. /** @brief �?стория для медианного фильтра (по 3 значения для каждого канала). */
  112. static median3_t medHist[4] = { 0 };
  113.  
  114. /** @brief Аккумуляторы EMA (u32) по каждому каналу. */
  115. static uint32_t emaAcc[4] = { 0, 0, 0, 0 };
  116.  
  117. /** @brief Отфильтрованные значения АЦП (в отсчётах), после avg → median3 → EMA. */
  118. static volatile uint16_t adcCountsFilt[4] = { 0, 0, 0, 0 };
  119.  
  120. /* _______________________________________________ ОБЪЯВЛЕН�?Е ПЕРЕМЕННЫХ КОНЕЦ _________________________________________________ */
  121.  
  122. /* USER CODE END PV */
  123.  
  124. /* Private function prototypes -----------------------------------------------*/
  125. void SystemClock_Config(void);
  126. static void MX_GPIO_Init(void);
  127. static void MX_I2C1_Init(void);
  128. static void MX_USART1_UART_Init(void);
  129. static void MX_DMA_Init(void);
  130. static void MX_ADC1_Init(void);
  131. static void MX_TIM4_Init(void);
  132. static void MX_I2C2_Init(void);
  133. /* USER CODE BEGIN PFP */
  134.  
  135. // _______________________________________________ ОБЪЯВЛЕН�?Е ФУНКЦ�?Й ________________________________________________________
  136. /**
  137.  * @brief Получим значения ADC и занесем их в переменные (используя отфильтрованные отсчёты).
  138.  */
  139. void GetADCData(void);
  140.  
  141. /**
  142.  * @brief Выведем на дисплей данные «основного экрана».
  143.  * @note  ТВОЙ оригинальный код — сохранён без изменений.
  144.  */
  145. void MainViewOutputOnLCD(void);
  146.  
  147. /* --- Вспомогательные для антишума --- */
  148.  
  149. /**
  150.  * @brief Обработать сегмент DMA-буфера: суммирование по каналам.
  151.  * @param ptr     Указатель на начало сегмента (uint16_t*).
  152.  * @param length  Длина сегмента (кол-во элементов).
  153.  * @note  Данные в буфере идут циклом: VREF, A0, A1, A2, VREF, A0, ...
  154.  */
  155. static void ADC_ProcessSegment(uint16_t *ptr, uint32_t length);
  156.  
  157. /**
  158.  * @brief Медиана из трёх (u16).
  159.  */
  160. static inline uint16_t median3_u16(uint16_t a, uint16_t b, uint16_t c);
  161.  
  162. /**
  163.  * @brief Шаг EMA без float: y += (x - y) >> k.
  164.  * @param acc Аккумулятор (u32), хранит текущий «y».
  165.  * @param x   Новое значение (u16).
  166.  * @param k   Параметр сглаживания (3 → 1/8; 4 → 1/16 и т.д.).
  167.  * @return    Текущее значение EMA (u16).
  168.  */
  169. static inline uint16_t ema_step_u16(uint32_t *acc, uint16_t x, uint8_t k);
  170.  
  171. // _______________________________________________ ОБЪЯВЛЕН�?Е ФУНКЦ�?Й КОНЕЦ __________________________________________________
  172.  
  173. /* USER CODE END PFP */
  174.  
  175. /* Private user code ---------------------------------------------------------*/
  176. /* USER CODE BEGIN 0 */
  177. int _write(int32_t file, uint8_t *ptr, int32_t len) {
  178.     for (int i = 0; i < len; i++) {
  179.         ITM_SendChar(*ptr++);
  180.     }
  181.     return len;
  182. }
  183.  
  184. void HAL_SYSTICK_Callback(void) {
  185.     Button_Update();   // тик кнопок раз в 1 мс
  186. }
  187.  
  188. /* USER CODE END 0 */
  189.  
  190. /**
  191.  * @brief  The application entry point.
  192.  * @retval int
  193.  */
  194. int main(void) {
  195.     /* USER CODE BEGIN 1 */
  196.  
  197.     /* USER CODE END 1 */
  198.  
  199.     /* MCU Configuration--------------------------------------------------------*/
  200.  
  201.     /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  202.     HAL_Init();
  203.  
  204.     /* USER CODE BEGIN Init */
  205.  
  206.     /* USER CODE END Init */
  207.  
  208.     /* Configure the system clock */
  209.     SystemClock_Config();
  210.  
  211.     /* USER CODE BEGIN SysInit */
  212.  
  213.     /* USER CODE END SysInit */
  214.  
  215.     /* Initialize all configured peripherals */
  216.     MX_GPIO_Init();
  217.     MX_I2C1_Init();
  218.     MX_USART1_UART_Init();
  219.     MX_DMA_Init();
  220.     MX_ADC1_Init();
  221.     MX_TIM4_Init();
  222.     MX_I2C2_Init();
  223.     /* USER CODE BEGIN 2 */
  224.  
  225.     HAL_TIM_Base_Start_IT(&htim4);
  226.  
  227.  
  228.     // �?нициализация кнопок и меню
  229.     Button_Init();
  230.  
  231.     HAL_ADCEx_Calibration_Start(&hadc1);
  232.  
  233.     /**
  234.      * @brief Стартуем ADC+DMA в режим circular с большим буфером для oversampling.
  235.      * @note  Раньше было: HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&adcData, 4);
  236.      *        Теперь:      HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adcDMABuf, 4*ADC_OSR);
  237.      *        Порядок данных в буфере сохраняется: Vref, A0, A1, A2, Vref, A0, ...
  238.      */
  239.     HAL_ADC_Start_DMA(&hadc1, (uint32_t*) adcDMABuf, (uint32_t) (4U * ADC_OSR));
  240.  
  241.  
  242.  
  243.     I2C_LCD_Init(MyI2C_LCD);
  244.     I2C_LCD_CreateCustomChar(MyI2C_LCD, 0, gradus);
  245.     I2C_LCD_CreateCustomChar(MyI2C_LCD, 1, blinking);
  246.  
  247.     I2C_LCD_Clear(MyI2C_LCD);
  248.  
  249.     /* USER CODE END 2 */
  250.  
  251.     /* Infinite loop */
  252.     /* USER CODE BEGIN WHILE */
  253.     /**
  254.      * @brief  Главный цикл программы
  255.      * @note   Здесь выполняется вся логика:
  256.      *         - обработка кнопок (Button_Process)
  257.      *         - мигание символом на экране (BlinkSymbol)
  258.      *         - периодическое обновление данных с датчиков и вывод на LCD
  259.      */
  260.     static uint32_t lastUpdate = 0; /**< метка времени последнего обновления */
  261.     static bool needInstantRefresh = true; /**< флаг мгновенного обновления после возврата из меню */
  262.  
  263.     // проведем тест памяти
  264.     {
  265.         extern I2C_HandleTypeDef hi2c2; // твой I2C для EEPROM (он уже есть в проекте)
  266.         const uint8_t EE_ADDR_7BIT = 0x50;
  267.  
  268.         EE_HealthInfo info;
  269.         (void) Eeprom_CheckAndAnnounce(&hi2c2, EE_ADDR_7BIT, CONFIG_VERSION,
  270.                 &info);
  271.         HAL_Delay(7000); // опционально, чтобы прочитать статус на LCD
  272.         I2C_LCD_Clear(MyI2C_LCD);
  273.     }
  274.  
  275.     /* 2) Загрузка конфигурации в рабочую RAM-структуру g_cfg */
  276.  
  277.     uint8_t used_slot = 0, read_ver = 0;
  278.  
  279.     if (!config_store_load(CONFIG_VERSION, &g_cfg, &used_slot, &read_ver)) {
  280.  
  281.         /* если вдруг вся память мертвая
  282.          * Ни один слот не валиден
  283.          * если версии не совпали
  284.          * → работаем на дефолтах из config_defaults.h
  285.          * Нет валидной записи этой версии → берём дефолты и записываем */
  286.         if (!config_defaults_get(CONFIG_VERSION, &g_cfg)) {
  287.             /* fallback на v1, если когда-нибудь будет больше версий */
  288.             // (void)config_defaults_get(CONFIG_VERSION, &g_cfg); // работало в версии архива 10.1
  289.             /* fallback на минимальные дефолты из хранилища */
  290.             config_store_fill_defaults(&g_cfg);
  291.         }
  292.         config_sanitize(&g_cfg);
  293.  
  294.         /* Желательно сразу засевать EEPROM дефолтами (если EEPROM доступна) */
  295.         (void) config_store_save(CONFIG_VERSION, &g_cfg);
  296.  
  297.         /* Сообщение пользователю */
  298.         I2C_LCD_SetCursor(MyI2C_LCD, 0, 2);
  299.         I2C_LCD_WriteString(MyI2C_LCD, RuChar("Defaults used"));
  300.         HAL_Delay(7000); // опционально, чтобы прочитать статус на LCD
  301.         I2C_LCD_Clear(MyI2C_LCD);
  302.     }
  303.  
  304.     // включим питение реле , управляющим контактом горелки
  305.     // если температура превысит доп параметры - пин надо снять
  306.     // в таком случае работа сварочной горелки будет заблокирована
  307.     // и свап будет остывать со включенным вентилятором
  308.     HAL_GPIO_WritePin(GPIOB, BLOCK_WORK_Pin, GPIO_PIN_SET);
  309.  
  310.     while (1) {
  311.  
  312.         /** Обрабатываем кнопки (события из Button_Update) */
  313.         Button_Process();
  314.  
  315.         Menu_Tick();
  316.  
  317.         /** Моргаем символом на дисплее (не блокирующий, сам проверяет main_monitor) */
  318.         BlinkSymbol();
  319.  
  320.         /**
  321.          * Если у нас работает главная страница монитора (вне меню) —
  322.          * обновляем данные и выводим их на LCD с периодом 1500 мс.
  323.          */
  324.         uint32_t now = HAL_GetTick();
  325.         if (main_monitor) {
  326.             if (needInstantRefresh || (now - lastUpdate >= 1500U)) {
  327.                 lastUpdate = now;
  328.                 needInstantRefresh = false;
  329.  
  330.                 /** Получаем значения температурных датчиков (уже сглаженные) */
  331.                 GetADCData();
  332.  
  333.                 /** Выводим на дисплей основной текст (ТВОЙ код — без изменений) */
  334.                 MainViewOutputOnLCD();
  335.             }
  336.         } else {
  337.             /**
  338.              * Если активировано меню — «армируем» мгновенное обновление,
  339.              * чтобы при возврате на монитор обновление произошло сразу.
  340.              */
  341.             needInstantRefresh = true;
  342.         }
  343.  
  344.         /**
  345.          * Лёгкий idle без блокировок: ждём прерываний (SysTick, DMA и т.д.)
  346.          * Экономит CPU и энергию.
  347.          */
  348.         __WFI();
  349.  
  350.         /* USER CODE END WHILE */
  351.  
  352.         /* USER CODE BEGIN 3 */
  353. #ifdef UART_OUT
  354.         // Пример разового вывода: sprintf(strBuffer, "Vdd=%.3fV A0=%.3fV\r\n", mcuVoltage, adcVoltage_1);
  355.         // HAL_UART_Transmit_IT(&huart1, (uint8_t*)strBuffer, strlen(strBuffer));
  356. #endif
  357.  
  358.     }
  359.     /* USER CODE END 3 */
  360. }
  361.  
  362. /**
  363.  * @brief System Clock Configuration
  364.  * @retval None
  365.  */
  366. void SystemClock_Config(void) {
  367.     RCC_OscInitTypeDef RCC_OscInitStruct = { 0 };
  368.     RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 };
  369.     RCC_PeriphCLKInitTypeDef PeriphClkInit = { 0 };
  370.  
  371.     /** Initializes the RCC Oscillators according to the specified parameters
  372.      * in the RCC_OscInitTypeDef structure.
  373.      */
  374.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  375.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  376.     RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  377.     RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  378.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  379.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  380.     RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  381.     if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
  382.         Error_Handler();
  383.     }
  384.     /** Initializes the CPU, AHB and APB buses clocks
  385.      */
  386.     RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
  387.             | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
  388.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  389.     RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  390.     RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  391.     RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  392.  
  393.     if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) {
  394.         Error_Handler();
  395.     }
  396.     PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
  397.     PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
  398.     if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) {
  399.         Error_Handler();
  400.     }
  401. }
  402.  
  403. /**
  404.  * @brief ADC1 Initialization Function
  405.  * @param None
  406.  * @retval None
  407.  */
  408. static void MX_ADC1_Init(void) {
  409.  
  410.     /* USER CODE BEGIN ADC1_Init 0 */
  411.  
  412.     /* USER CODE END ADC1_Init 0 */
  413.  
  414.     ADC_ChannelConfTypeDef sConfig = { 0 };
  415.  
  416.     /* USER CODE BEGIN ADC1_Init 1 */
  417.  
  418.     /* USER CODE END ADC1_Init 1 */
  419.     /** Common config
  420.      */
  421.     hadc1.Instance = ADC1;
  422.     hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
  423.     hadc1.Init.ContinuousConvMode = ENABLE;
  424.     hadc1.Init.DiscontinuousConvMode = DISABLE;
  425.     hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  426.     hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  427.     hadc1.Init.NbrOfConversion = 4;
  428.     if (HAL_ADC_Init(&hadc1) != HAL_OK) {
  429.         Error_Handler();
  430.     }
  431.     /** Configure Regular Channel
  432.      */
  433.     sConfig.Channel = ADC_CHANNEL_VREFINT;
  434.     sConfig.Rank = ADC_REGULAR_RANK_1;
  435.     sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
  436.     if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
  437.         Error_Handler();
  438.     }
  439.     /** Configure Regular Channel
  440.      */
  441.     sConfig.Channel = ADC_CHANNEL_0;
  442.     sConfig.Rank = ADC_REGULAR_RANK_2;
  443.     if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
  444.         Error_Handler();
  445.     }
  446.     /** Configure Regular Channel
  447.      */
  448.     sConfig.Channel = ADC_CHANNEL_1;
  449.     sConfig.Rank = ADC_REGULAR_RANK_3;
  450.     if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
  451.         Error_Handler();
  452.     }
  453.     /** Configure Regular Channel
  454.      */
  455.     sConfig.Channel = ADC_CHANNEL_2;
  456.     sConfig.Rank = ADC_REGULAR_RANK_4;
  457.     if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
  458.         Error_Handler();
  459.     }
  460.     /* USER CODE BEGIN ADC1_Init 2 */
  461.     // На F1 VREFINT включает TSVREFE; HAL делает это автоматически при выборе ADC_CHANNEL_VREFINT.
  462.     /* USER CODE END ADC1_Init 2 */
  463.  
  464. }
  465.  
  466. /**
  467.  * @brief I2C1 Initialization Function
  468.  * @param None
  469.  * @retval None
  470.  */
  471. static void MX_I2C1_Init(void) {
  472.  
  473.     /* USER CODE BEGIN I2C1_Init 0 */
  474.  
  475.     /* USER CODE END I2C1_Init 0 */
  476.  
  477.     /* USER CODE BEGIN I2C1_Init 1 */
  478.  
  479.     /* USER CODE END I2C1_Init 1 */
  480.     hi2c1.Instance = I2C1;
  481.     hi2c1.Init.ClockSpeed = 100000;
  482.     hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  483.     hi2c1.Init.OwnAddress1 = 0;
  484.     hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  485.     hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  486.     hi2c1.Init.OwnAddress2 = 0;
  487.     hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  488.     hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  489.     if (HAL_I2C_Init(&hi2c1) != HAL_OK) {
  490.         Error_Handler();
  491.     }
  492.     /* USER CODE BEGIN I2C1_Init 2 */
  493.  
  494.     /* USER CODE END I2C1_Init 2 */
  495.  
  496. }
  497.  
  498. /**
  499.  * @brief I2C2 Initialization Function
  500.  * @param None
  501.  * @retval None
  502.  */
  503. static void MX_I2C2_Init(void) {
  504.  
  505.     /* USER CODE BEGIN I2C2_Init 0 */
  506.  
  507.     /* USER CODE END I2C2_Init 0 */
  508.  
  509.     /* USER CODE BEGIN I2C2_Init 1 */
  510.  
  511.     /* USER CODE END I2C2_Init 1 */
  512.     hi2c2.Instance = I2C2;
  513.     hi2c2.Init.ClockSpeed = 100000;
  514.     hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2;
  515.     hi2c2.Init.OwnAddress1 = 0;
  516.     hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  517.     hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  518.     hi2c2.Init.OwnAddress2 = 0;
  519.     hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  520.     hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  521.     if (HAL_I2C_Init(&hi2c2) != HAL_OK) {
  522.         Error_Handler();
  523.     }
  524.     /* USER CODE BEGIN I2C2_Init 2 */
  525.  
  526.     /* USER CODE END I2C2_Init 2 */
  527.  
  528. }
  529.  
  530. /**
  531.  * @brief TIM4 Initialization Function
  532.  * @param None
  533.  * @retval None
  534.  */
  535. static void MX_TIM4_Init(void) {
  536.  
  537.     /* USER CODE BEGIN TIM4_Init 0 */
  538.  
  539.     /* USER CODE END TIM4_Init 0 */
  540.  
  541.     TIM_ClockConfigTypeDef sClockSourceConfig = { 0 };
  542.     TIM_MasterConfigTypeDef sMasterConfig = { 0 };
  543.  
  544.     /* USER CODE BEGIN TIM4_Init 1 */
  545.  
  546.     /* USER CODE END TIM4_Init 1 */
  547.     htim4.Instance = TIM4;
  548.     htim4.Init.Prescaler = 7200 - 1;
  549.     htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  550.     htim4.Init.Period = 10000 - 1;
  551.     htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  552.     htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  553.     if (HAL_TIM_Base_Init(&htim4) != HAL_OK) {
  554.         Error_Handler();
  555.     }
  556.     sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  557.     if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK) {
  558.         Error_Handler();
  559.     }
  560.     sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  561.     sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  562.     if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig)
  563.             != HAL_OK) {
  564.         Error_Handler();
  565.     }
  566.     /* USER CODE BEGIN TIM4_Init 2 */
  567.  
  568.     /* USER CODE END TIM4_Init 2 */
  569.  
  570. }
  571.  
  572. /**
  573.  * @brief USART1 Initialization Function
  574.  * @param None
  575.  * @retval None
  576.  */
  577. static void MX_USART1_UART_Init(void) {
  578.  
  579.     /* USER CODE BEGIN USART1_Init 0 */
  580.  
  581.     /* USER CODE END USART1_Init 0 */
  582.  
  583.     /* USER CODE BEGIN USART1_Init 1 */
  584.  
  585.     /* USER CODE END USART1_Init 1 */
  586.     huart1.Instance = USART1;
  587.     huart1.Init.BaudRate = 9600;
  588.     huart1.Init.WordLength = UART_WORDLENGTH_8B;
  589.     huart1.Init.StopBits = UART_STOPBITS_1;
  590.     huart1.Init.Parity = UART_PARITY_NONE;
  591.     huart1.Init.Mode = UART_MODE_TX_RX;
  592.     huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  593.     huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  594.     if (HAL_UART_Init(&huart1) != HAL_OK) {
  595.         Error_Handler();
  596.     }
  597.     /* USER CODE BEGIN USART1_Init 2 */
  598.  
  599.     /* USER CODE END USART1_Init 2 */
  600.  
  601. }
  602.  
  603. /**
  604.  * Enable DMA controller clock
  605.  */
  606. static void MX_DMA_Init(void) {
  607.  
  608.     /* DMA controller clock enable */
  609.     __HAL_RCC_DMA1_CLK_ENABLE();
  610.  
  611.     /* DMA interrupt init */
  612.     /* DMA1_Channel1_IRQn interrupt configuration */
  613.     HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
  614.     HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
  615.  
  616. }
  617.  
  618. /**
  619.  * @brief GPIO Initialization Function
  620.  * @param None
  621.  * @retval None
  622.  */
  623. static void MX_GPIO_Init(void) {
  624.     GPIO_InitTypeDef GPIO_InitStruct = { 0 };
  625.  
  626.     /* GPIO Ports Clock Enable */
  627.     __HAL_RCC_GPIOD_CLK_ENABLE();
  628.     __HAL_RCC_GPIOA_CLK_ENABLE();
  629.     __HAL_RCC_GPIOB_CLK_ENABLE();
  630.  
  631.     /*Configure GPIO pin Output Level */
  632.     HAL_GPIO_WritePin(GPIOB, LedControl_Pin | BLOCK_WORK_Pin, GPIO_PIN_RESET);
  633.  
  634.     /*Configure GPIO pins : bUp_Pin bLeft_Pin bCenter_Pin */
  635.     GPIO_InitStruct.Pin = bUp_Pin | bLeft_Pin | bCenter_Pin;
  636.     GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  637.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  638.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  639.  
  640.     /*Configure GPIO pins : bRight_Pin bDown_Pin */
  641.     GPIO_InitStruct.Pin = bRight_Pin | bDown_Pin;
  642.     GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  643.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  644.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  645.  
  646.     /*Configure GPIO pins : LedControl_Pin BLOCK_WORK_Pin */
  647.     GPIO_InitStruct.Pin = LedControl_Pin | BLOCK_WORK_Pin;
  648.     GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  649.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  650.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  651.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  652.  
  653. }
  654.  
  655. /* USER CODE BEGIN 4 */
  656.  
  657. /* _______________________________________________ ОПРЕДЕЛЕН�?Е ФУНКЦ�?Й _________________________________________________________ */
  658.  
  659. /**
  660.  * @brief TIM периодический колбэк: контрольный LED (1 раз в 2 сек).
  661.  */
  662. void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
  663.     if (htim->Instance == TIM4) //TIM4 управляет контрольным светодиодом , если все норм - светодиод моргает 1 раз в 2 сек
  664.     {
  665.         HAL_GPIO_TogglePin(LedControl_GPIO_Port, LedControl_Pin);
  666.     }
  667. }
  668.  
  669. /**
  670.  * @brief Колбэк DMA: половина буфера заполнена.
  671.  * @details Обнуляем суммы и накапливаем первую половину буфера.
  672.  */
  673. void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc) {
  674.     if (hadc->Instance != ADC1)
  675.         return;
  676.  
  677.     osSum[0] = osSum[1] = osSum[2] = osSum[3] = 0;
  678.     /* половина буфера = 2*ADC_OSR элементов (последовательность Vref,A0,A1,A2,...) */
  679.     ADC_ProcessSegment(&adcDMABuf[0], (uint32_t) (2U * ADC_OSR));
  680.     osGotHalf = true;
  681. }
  682.  
  683. /**
  684.  * @brief Колбэк DMA: буфер заполнен целиком.
  685.  * @details Докапливаем вторую половину; затем считаем средние, медиану и EMA.
  686.  */
  687. void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc) {
  688.     if (hadc->Instance != ADC1)
  689.         return;
  690.  
  691.     ADC_ProcessSegment(&adcDMABuf[2U * ADC_OSR], (uint32_t) (2U * ADC_OSR));
  692.  
  693.     if (osGotHalf) {
  694.         osGotHalf = false;
  695.  
  696.         /* 1) Среднее по каждому каналу */
  697.         uint16_t avg[4];
  698.         for (int ch = 0; ch < 4; ch++) {
  699.             avg[ch] = (uint16_t) (osSum[ch] / ADC_OSR);
  700.         }
  701.  
  702.         /* 2) Медиана из трёх (устранение одиночных выбросов) */
  703.         uint16_t med[4];
  704.         for (int ch = 0; ch < 4; ch++) {
  705.             median3_t *mh = &medHist[ch];
  706.             med[ch] = median3_u16(mh->a, mh->b, avg[ch]);
  707.             mh->a = mh->b;
  708.             mh->b = avg[ch];
  709.             mh->c = med[ch];
  710.         }
  711.  
  712.         /* 3) EMA без float (мягкое сглаживание) */
  713.         for (int ch = 0; ch < 4; ch++) {
  714.             adcCountsFilt[ch] = ema_step_u16(&emaAcc[ch], med[ch], ADC_EMA_K);
  715.         }
  716.  
  717.         /* 4) Обновим «последние сырые 4» для совместимости/отладки */
  718.         for (int ch = 0; ch < 4; ch++) {
  719.             adcData[ch] = avg[ch];
  720.         }
  721.     }
  722. }
  723.  
  724. /**
  725.  * @brief Получим значения ADC и занесем их в переменные ...
  726.  * @note  �?СПОЛЬЗУЕМ ОТФ�?ЛЬТРОВАННЫЕ counts: adcCountsFilt[0..3].
  727.  */
  728. void GetADCData() {
  729.     /* Vdd через Vrefint: Vdd = Vref_typ * 4095 / ADC(Vrefint) */
  730.     uint16_t vref_cnt = adcCountsFilt[0];
  731.     if (vref_cnt == 0)
  732.         vref_cnt = 1; // защита от деления на ноль
  733.  
  734.     mcuVoltage = (float) ADC_MAX * ADC_REFERENCE_VOLTAGE / (float) vref_cnt;
  735.  
  736.     /* Каналы A0..A2 в В: Vin = ADC * Vdd / 4095 */
  737.     adcVoltage_1 = (float) adcCountsFilt[1] * mcuVoltage / (float) ADC_MAX;
  738.     adcVoltage_2 = (float) adcCountsFilt[2] * mcuVoltage / (float) ADC_MAX;
  739.     adcVoltage_3 = (float) adcCountsFilt[3] * mcuVoltage / (float) ADC_MAX;
  740. }
  741.  
  742. /**
  743.  * @brief  Вывод «основного» экрана на LCD 20×4.
  744.  * @details
  745.  *  Размещение по строкам (индексы колонок 0..19):
  746.  *  - Строка 0: "U питания =" (0..10), значение Vdd шириной 6 символов (12..17), символ 'v' в колонке 18,
  747.  *              колонка 19 — под мигающий индикатор BlinkSymbol().
  748.  *  - Строки 1..3: метки "T транс =", "T диода =", "T тирист =" (0..10),
  749.  *                 значение шириной 5 символов (12..16), затем градус (17) и 'C' (18).
  750.  *
  751.  *  Формат чисел:
  752.  *   - Vdd:      %6.3f  → всегда 6 символов (например, " 3.300" или "12.345")
  753.  *   - Темп-ры:  %5.1f  → всегда 5 символов (например, " 25.0" или "123.4")
  754.  *
  755.  *  Важно:
  756.  *   - Убраны завершающие пробелы в форматах, чтобы не оставались «хвосты» старых символов.
  757.  *   - Позиции подобраны так, чтобы число и единицы измерения не налезали друг на друга.
  758.  *   - Мигающий индикатор занимает (19,0), поэтому символ напряжения 'v' ставим в (18,0).
  759.  */
  760. void MainViewOutputOnLCD(void) {
  761.     /* ---------- Строка 0: напряжение питания Vdd ---------- */
  762.     I2C_LCD_SetCursor(MyI2C_LCD, 0, 0);
  763.     I2C_LCD_WriteString(MyI2C_LCD, RuChar("U питания =")); // колонки 0..10
  764.  
  765.     // Число фиксированной ширины: 6 символов в колонках 12..17
  766.     snprintf(strBuffer, sizeof(strBuffer), "%6.3f", mcuVoltage);
  767.     I2C_LCD_SetCursor(MyI2C_LCD, 12, 0);
  768.     I2C_LCD_WriteString(MyI2C_LCD, strBuffer);
  769.  
  770.     // Единица измерения — 'v' в колонке 18 (19-я колонка занята под BlinkSymbol)
  771.     I2C_LCD_SetCursor(MyI2C_LCD, 18, 0);
  772.     I2C_LCD_WriteString(MyI2C_LCD, "v");
  773.  
  774.     /* ---------- Строка 1: температура трансформатора (A0) ---------- */
  775.     I2C_LCD_SetCursor(MyI2C_LCD, 0, 1);
  776.     I2C_LCD_WriteString(MyI2C_LCD, RuChar("T транс   =")); // колонки 0..8..10 (как у тебя)
  777.  
  778.     // Температура = adcVoltage_1 * 100 (как у тебя в исходниках), ширина 5 символов: 12..16
  779.     snprintf(strBuffer, sizeof(strBuffer), "%5.1f", adcVoltage_1 * 100.0f);
  780.     I2C_LCD_SetCursor(MyI2C_LCD, 12, 1);
  781.     I2C_LCD_WriteString(MyI2C_LCD, strBuffer);
  782.  
  783.     // Градус (кастомный символ 0) в колонке 17, затем 'C' в 18
  784.     I2C_LCD_SetCursor(MyI2C_LCD, 17, 1);
  785.     I2C_LCD_PrintCustomChar(MyI2C_LCD, 0);
  786.     I2C_LCD_SetCursor(MyI2C_LCD, 18, 1);
  787.     I2C_LCD_WriteString(MyI2C_LCD, "C");
  788.  
  789.     /* ---------- Строка 2: температура диода (A1) ---------- */
  790.     I2C_LCD_SetCursor(MyI2C_LCD, 0, 2);
  791.     I2C_LCD_WriteString(MyI2C_LCD, RuChar("T диода   ="));
  792.  
  793.     snprintf(strBuffer, sizeof(strBuffer), "%5.1f", adcVoltage_2 * 100.0f);
  794.     I2C_LCD_SetCursor(MyI2C_LCD, 12, 2);
  795.     I2C_LCD_WriteString(MyI2C_LCD, strBuffer);
  796.  
  797.     I2C_LCD_SetCursor(MyI2C_LCD, 17, 2);
  798.     I2C_LCD_PrintCustomChar(MyI2C_LCD, 0);
  799.     I2C_LCD_SetCursor(MyI2C_LCD, 18, 2);
  800.     I2C_LCD_WriteString(MyI2C_LCD, "C");
  801.  
  802.     /* ---------- Строка 3: температура тиристора (A2) ---------- */
  803.     I2C_LCD_SetCursor(MyI2C_LCD, 0, 3);
  804.     I2C_LCD_WriteString(MyI2C_LCD, RuChar("T тирист  ="));
  805.  
  806.     snprintf(strBuffer, sizeof(strBuffer), "%5.1f", adcVoltage_3 * 100.0f);
  807.     I2C_LCD_SetCursor(MyI2C_LCD, 12, 3);
  808.     I2C_LCD_WriteString(MyI2C_LCD, strBuffer);
  809.  
  810.     I2C_LCD_SetCursor(MyI2C_LCD, 17, 3);
  811.     I2C_LCD_PrintCustomChar(MyI2C_LCD, 0);
  812.     I2C_LCD_SetCursor(MyI2C_LCD, 18, 3);
  813.     I2C_LCD_WriteString(MyI2C_LCD, "C");
  814. }
  815.  
  816. /* ======================= Вспомогательные (static) ======================= */
  817.  
  818. /**
  819.  * @brief  Просуммировать значения по каждому каналу в сегменте DMA.
  820.  * @param  ptr     Указатель на начало сегмента.
  821.  * @param  length  Длина сегмента (кол-во uint16_t элементов).
  822.  * @details Данные идут циклом по каналам: [0]VREF, [1]A0, [2]A1, [3]A2, ...
  823.  */
  824. static void ADC_ProcessSegment(uint16_t *ptr, uint32_t length) {
  825.     for (uint32_t i = 0; i < length; i += 4U) {
  826.         osSum[0] += ptr[i + 0];
  827.         osSum[1] += ptr[i + 1];
  828.         osSum[2] += ptr[i + 2];
  829.         osSum[3] += ptr[i + 3];
  830.     }
  831. }
  832.  
  833. /**
  834.  * @brief  Медиана из трёх (u16).
  835.  */
  836. static inline uint16_t median3_u16(uint16_t a, uint16_t b, uint16_t c) {
  837.     if (a > b) {
  838.         uint16_t t = a;
  839.         a = b;
  840.         b = t;
  841.     }
  842.     if (b > c) {
  843.         uint16_t t = b;
  844.         b = c;
  845.         c = t;
  846.     }
  847.     if (a > b) {
  848.         uint16_t t = a;
  849.         a = b;
  850.         b = t;
  851.     }
  852.     return b;
  853. }
  854.  
  855. /**
  856.  * @brief  Шаг EMA без float: y += (x - y) >> k.
  857.  */
  858. static inline uint16_t ema_step_u16(uint32_t *acc, uint16_t x, uint8_t k) {
  859.     if (*acc == 0) {
  860.         *acc = x;
  861.     }                 // инициализация
  862.     int32_t diff = (int32_t) x - (int32_t) (*acc);
  863.     *acc += (diff >> k);
  864.     return (uint16_t) (*acc & 0xFFFF);
  865. }
  866.  
  867. /* _______________________________________________ ОПРЕДЕЛЕН�?Е ФУНКЦ�?Й КОНЕЦ _________________________________________________ */
  868. /* USER CODE END 4 */
  869.  
  870. /**
  871.  * @brief  This function is executed in case of error occurrence.
  872.  * @retval None
  873.  */
  874. void Error_Handler(void) {
  875.     /* USER CODE BEGIN Error_Handler_Debug */
  876.     /* User can add his own implementation to report the HAL error return state */
  877.     __disable_irq();
  878.     while (1) {
  879.     }
  880.     /* USER CODE END Error_Handler_Debug */
  881. }
  882.  
  883. #ifdef  USE_FULL_ASSERT
  884. /**
  885.   * @brief  Reports the name of the source file and the source line number
  886.   *         where the assert_param error has occurred.
  887.   * @param  file: pointer to the source file name
  888.   * @param  line: assert_param error line source number
  889.   * @retval None
  890.   */
  891. void assert_failed(uint8_t *file, uint32_t line)
  892. {
  893.   /* USER CODE BEGIN 6 */
  894.     (void)file; (void)line;
  895.   /* USER CODE END 6 */
  896. }
  897. #endif /* USE_FULL_ASSERT */
  898.  
  899. /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
  900.  
Add Comment
Please, Sign In to add comment