14 Commits
3633 changed files with 2134658 additions and 475 deletions
-48
View File
@@ -1,48 +0,0 @@
# ---> uVision
# git ignore file for Keil µVision Project
# µVision 5 and µVision 4 Project screen layout file
*.uvguix.*
*.uvgui.*
# Listing Files
*.i
*.lst
*.m51
*.m66
*.map
# Object Files
*.axf
*.b[0-2][0-9]
*.b3[0-1]
*.bak
*.build_log.htm
*.crf
*.d
*.dep
*.elf
*.htm
*.iex
*.lnp
*.o
*.obj
*.sbr
# Firmware Files
*.bin
*.h86
*.hex
# Build Files
.bat
# Debugger Files
.ini
# JLink Files
JLinkLog.txt
# Other Files
/john103C6T6/MDK-ARM/john103C6T6.uvoptx
-11
View File
@@ -1,11 +0,0 @@
[submodule "external_flash_eeprom"]
path = external_flash_eeprom
url = https://git.arktika.cyou/set506/external_flash_eeprom.git
[submodule "labview_proj"]
path = labview_proj
url = https://git.arktika.cyou/Andrey/labview_proj.git
branch = master
[submodule "core/STM32_Modbus"]
path = core/STM32_Modbus
url = https://git.arktika.cyou/set506/STM32_Modbus.git
branch = release
-2
View File
@@ -1,2 +0,0 @@
# ds18b20-MODBUS
-127
View File
@@ -1,127 +0,0 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file rtc.c
* @brief This file provides code for the configuration
* of the RTC instances.
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "rtc.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
RTC_HandleTypeDef hrtc;
/* RTC init function */
void MX_RTC_Init(void)
{
/* USER CODE BEGIN RTC_Init 0 */
/* USER CODE END RTC_Init 0 */
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef DateToUpdate = {0};
/* USER CODE BEGIN RTC_Init 1 */
/* USER CODE END RTC_Init 1 */
/** Initialize RTC Only
*/
hrtc.Instance = RTC;
hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
hrtc.Init.OutPut = RTC_OUTPUTSOURCE_NONE;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN Check_RTC_BKUP */
/* USER CODE END Check_RTC_BKUP */
/** Initialize RTC and set the Time and Date
*/
sTime.Hours = 0x0;
sTime.Minutes = 0x0;
sTime.Seconds = 0x0;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY;
DateToUpdate.Month = RTC_MONTH_JANUARY;
DateToUpdate.Date = 0x1;
DateToUpdate.Year = 0x0;
if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BCD) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN RTC_Init 2 */
/* USER CODE END RTC_Init 2 */
}
void HAL_RTC_MspInit(RTC_HandleTypeDef* rtcHandle)
{
if(rtcHandle->Instance==RTC)
{
/* USER CODE BEGIN RTC_MspInit 0 */
/* USER CODE END RTC_MspInit 0 */
HAL_PWR_EnableBkUpAccess();
/* Enable BKP CLK enable for backup registers */
__HAL_RCC_BKP_CLK_ENABLE();
/* RTC clock enable */
__HAL_RCC_RTC_ENABLE();
/* RTC interrupt Init */
HAL_NVIC_SetPriority(RTC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(RTC_IRQn);
/* USER CODE BEGIN RTC_MspInit 1 */
/* USER CODE END RTC_MspInit 1 */
}
}
void HAL_RTC_MspDeInit(RTC_HandleTypeDef* rtcHandle)
{
if(rtcHandle->Instance==RTC)
{
/* USER CODE BEGIN RTC_MspDeInit 0 */
/* USER CODE END RTC_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_RTC_DISABLE();
/* RTC interrupt Deinit */
HAL_NVIC_DisableIRQ(RTC_IRQn);
/* USER CODE BEGIN RTC_MspDeInit 1 */
/* USER CODE END RTC_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
+78
View File
@@ -0,0 +1,78 @@
# Пины МК
Документ составлен по прошивке:
- `..\new rev\john103C8T6\john103C6T6.ioc`
- `..\new rev\john103C8T6\Core\Inc\main.h`
- `..\new rev\john103C8T6\Core\Src\gpio.c`
- `..\new rev\john103C8T6\Core\Src\usart.c`
- `..\new rev\john103C8T6\Core\Src\can.c`
- `..\new rev\john103C8T6\Core\Src\i2c.c`
- `..\new rev\john103C8T6\Core\Src\spi.c`
- `..\new rev\john103C8T6\Core\Inc\ow_port.h`
МК: `STM32F103C8Tx`, корпус `LQFP48`.
## Основные интерфейсы
| Пин МК | Функция | Настройка | Назначение в проекте |
|---|---|---|---|
| `PB6` | `USART1_TX` | AF push-pull, remap USART1 | Modbus RTU slave TX |
| `PB7` | `USART1_RX` | input, no pull, remap USART1 | Modbus RTU slave RX |
| `PA2` | `USART2_TX` | AF push-pull | Modbus RTU master TX |
| `PA3` | `USART2_RX` | input, no pull | Modbus RTU master RX |
| `PA15` | `One_wire` | GPIO, динамически input/output | Шина 1-Wire для DS18B20 |
| `PA10` | `Relay_dc5v` | GPIO output push-pull | Реле 5 В, управляется из Modbus coil `20` |
| `PC13` | GPIO output | push-pull | Светодиод/индикация, используется в `led_blink()` |
| `PA0` | GPIO input | no pull | Вход, назначение в коде не найдено |
| `PA11` | `CAN_RX` | input | CAN RX |
| `PA12` | `CAN_TX` | AF push-pull | CAN TX |
| `PB8` | `I2C1_SCL` | AF open-drain, remap I2C1 | I2C1 SCL |
| `PB9` | `I2C1_SDA` | AF open-drain, remap I2C1 | I2C1 SDA |
| `PB3` | `SPI1_SCK` | AF push-pull, remap SPI1 | SPI1 SCK |
| `PB4` | `SPI1_MISO` | input, no pull, remap SPI1 | SPI1 MISO |
| `PB5` | `SPI1_MOSI` | AF push-pull, remap SPI1 | SPI1 MOSI |
| `PA13` | `SWDIO` | Serial Wire | Отладка |
| `PA14` | `SWCLK` | Serial Wire | Отладка |
| `PD0` | `OSC_IN` | HSE oscillator | Внешний кварц |
| `PD1` | `OSC_OUT` | HSE oscillator | Внешний кварц |
## GPIO output, сконфигурированные в `MX_GPIO_Init`
| Пин МК | Начальное состояние | Комментарий |
|---|---:|---|
| `PC13` | `RESET` | Индикация, мигает при старте через `led_blink(GPIOC, 13, ...)` |
| `PA1` | `RESET` | Выход сконфигурирован, активного использования в коде не найдено |
| `PA4` | `RESET` | Выход сконфигурирован, активного использования в коде не найдено |
| `PA5` | `RESET` | Выход сконфигурирован, активного использования в коде не найдено |
| `PA6` | `RESET` | Выход сконфигурирован, активного использования в коде не найдено |
| `PA7` | `RESET` | Выход сконфигурирован, активного использования в коде не найдено |
| `PA8` | `RESET` | Выход сконфигурирован, активного использования в коде не найдено |
| `PA9` | `RESET` | Выход сконфигурирован, активного использования в коде не найдено |
| `PA10` | `RESET` | `Relay_dc5v`, в цикле main зависит от `MB_DATA.Coils.coils[1].state_val_bit.state_val_05` |
| `PA15` | `RESET` | 1-Wire, затем драйвер меняет режим пина input/output |
| `PB0` | `RESET` | Выход сконфигурирован, активного использования в коде не найдено |
| `PB1` | `RESET` | Выход сконфигурирован, активного использования в коде не найдено |
| `PB2` | `RESET` | Выход сконфигурирован, активного использования в коде не найдено |
| `PB10` | `RESET` | Выход сконфигурирован, активного использования в коде не найдено |
| `PB11` | `RESET` | Есть macro `GPIOB11_valve`, но прямой записи в `PB11` в текущем коде не найдено |
| `PB12` | `RESET` | Выход сконфигурирован, активного использования в коде не найдено |
| `PB13` | `RESET` | Выход сконфигурирован, активного использования в коде не найдено |
| `PB14` | `RESET` | Выход сконфигурирован, активного использования в коде не найдено |
| `PB15` | `RESET` | Выход сконфигурирован, активного использования в коде не найдено |
## Периферия без внешнего пина
| Периферия | Настройка | Назначение |
|---|---|---|
| `TIM1` | internal clock, period `65535`, prescaler `0` | Тайминги 1-Wire, `OW_TIM` |
| `TIM2` | internal clock, period `65535`, prescaler `7199` | Таймер Modbus slave |
| `TIM4` | internal clock, period `65535`, prescaler `7199` | Таймер Modbus master |
| `ADC1` | `ADC_CHANNEL_VREFINT` | Внутренний Vref, внешних ADC-пинов нет |
| `RTC` | LSI | Часы, доступны через Modbus-регистры |
## Замечания
- В `.ioc` пины `PA2/PA3` отмечены как GPIO output, но в актуальном `usart.c` они инициализируются как `USART2_TX/RX`. Для документации использована фактическая инициализация из исходников.
- Функция `handle_valves()` содержит запись в `GPIOC` pin `14`, но `PC14` не инициализирован в `MX_GPIO_Init()` и сама функция в основном цикле закомментирована.
- Для RS-485 direction control (`DE/RE`) отдельный GPIO в `modbus_config.h` не задан: `RS_EnableReceive()` и `RS_EnableTransmit()` закомментированы.
+201
View File
@@ -0,0 +1,201 @@
# Modbus Map
Документ составлен по прошивке:
- `..\new rev\john103C8T6\Modbus\modbus_config.h`
- `..\new rev\john103C8T6\Modbus\modbus_data.h`
- `..\new rev\john103C8T6\Modbus\modbus_data.c`
- `..\new rev\john103C8T6\Core\Src\main.c`
- `..\new rev\john103C8T6\Core\Inc\PROJ_setup.h`
Адреса ниже указаны в 0-based формате, как они используются в прошивке. В некоторых Modbus-терминалах эти же адреса отображаются как `30001`, `40001`, `00001` и т.п. В таком терминале к адресу обычно надо прибавить `1` и выбрать нужный тип таблицы.
## Общие параметры
| Параметр | Значение |
|---|---|
| Протокол | Modbus RTU |
| Slave ID | `3` |
| Slave UART | `USART1`, `PB6 TX`, `PB7 RX` |
| Slave UART настройки | `115200`, `8N1` |
| Slave таймер | `TIM2` |
| Master UART | `USART2`, `PA2 TX`, `PA3 RX` |
| Master UART настройки | `115200`, `8N1` |
| Master таймер | `TIM4` |
| `MAX_SENSE` | `32` |
| Включенные таблицы | Coils, Holding Registers, Input Registers |
## Input Registers, function `0x04`
Базовый массив: `MB_DATA.InRegs`, адресный диапазон прошивки `0..1999`.
| Адрес | Кол-во | Имя | Тип | Описание |
|---:|---:|---|---|---|
| `0` | `32` | `sens_Temp[0..31]` | `uint16_t` | Температура DS18B20, в коде записывается как `temperature * 10`. Рекомендуется читать как `int16_t / 10.0`, если возможны отрицательные температуры |
| `32` | `968` | `reserve` | `uint16_t[]` | Резерв до адреса `999` |
| `1000` | `128` | `ID.DevAddr[32][8]` | raw bytes as registers | ROM-коды DS18B20: 32 датчика по 8 байт. Для датчика `N`: базовый регистр `1000 + N * 4` |
| `1128` | `72` | `reserve1` | `uint16_t[]` | Резерв до адреса `1199` |
| `1200` | `1` | `num_Tsens` | `uint16_t` | Количество найденных датчиков DS18B20 |
| `1201` | `1` | `rtc.hours` | `uint16_t` | Текущие часы RTC |
| `1202` | `1` | `rtc.minutes` | `uint16_t` | Текущие минуты RTC |
| `1203` | `1` | `rtc.seconds` | `uint16_t` | Текущие секунды RTC |
| `1204` | `1` | `rtc.date` | `uint16_t` | День месяца |
| `1205` | `1` | `rtc.month` | `uint16_t` | Месяц |
| `1206` | `1` | `rtc.year` | `uint16_t` | Год в формате RTC проекта |
| `1207` | `1` | `rtc.weekday` | `uint16_t` | День недели |
| `1208` | `1` | `rtc.apply` | `uint16_t` | Для input-регистров обычно `0` |
| `1209` | `1` | `rtc.status` | `uint16_t` | Статус RTC |
| `1210` | `790` | reserved | `uint16_t[]` | Адреса доступны в общем диапазоне, прикладного поля нет |
## Holding Registers, functions `0x03`, `0x06`, `0x10`
Базовый массив: `MB_DATA.HoldRegs`, адресный диапазон прошивки `0..1999`.
| Адрес | Кол-во | Имя | Тип | Описание |
|---:|---:|---|---|---|
| `0` | `32` | `set_Temp[0..31]` | `uint16_t` | Уставки температуры для датчиков. В текущем коде используются как градусы без умножения на 10 |
| `32` | `68` | `reserve` | `uint16_t[]` | Резерв до адреса `99` |
| `100` | `32` | `set_hyst[0..31]` | `uint16_t` | Гистерезис температуры для датчиков. В текущем коде используется как градусы |
| `132` | `68` | `reserve1` | `uint16_t[]` | Резерв до адреса `199` |
| `200` | `1` | `rtc.hours` | `uint16_t` | Часы для установки RTC |
| `201` | `1` | `rtc.minutes` | `uint16_t` | Минуты для установки RTC |
| `202` | `1` | `rtc.seconds` | `uint16_t` | Секунды для установки RTC |
| `203` | `1` | `rtc.date` | `uint16_t` | День месяца |
| `204` | `1` | `rtc.month` | `uint16_t` | Месяц |
| `205` | `1` | `rtc.year` | `uint16_t` | Год в формате RTC проекта |
| `206` | `1` | `rtc.weekday` | `uint16_t` | День недели |
| `207` | `1` | `rtc.apply` | `uint16_t` | Записать `1`, чтобы применить время RTC |
| `208` | `1` | `rtc.status` | `uint16_t` | Статус установки RTC |
| `209` | `1791` | reserved | `uint16_t[]` | Адреса доступны в общем диапазоне, прикладного поля нет |
RTC status:
| Значение | Имя | Описание |
|---:|---|---|
| `0` | `MB_RTC_STATUS_IDLE` | Нет операции |
| `1` | `MB_RTC_STATUS_SET_OK` | Время установлено |
| `2` | `MB_RTC_STATUS_VALUE_ERROR` | Ошибка значения времени/даты |
| `3` | `MB_RTC_STATUS_HAL_ERROR` | Ошибка HAL RTC |
## Coils, functions `0x01`, `0x05`, `0x0F`
Базовый массив: `MB_DATA.Coils`, адресный диапазон прошивки `0..999`.
| Coil address | Кол-во | Имя | Описание |
|---:|---:|---|---|
| `0` | `48` | `coils[0..2]` | Общие управляющие биты `state_val_01..state_val_16` в трех 16-битных словах |
| `20` | `1` | `coils[1].state_val_05` | Управляет `PA10 / Relay_dc5v` в основном цикле |
| `48` | `80` | `reserve_coils` | Резерв до coil `127` |
| `128` | `32` | `status_tSens[0..1]` | Статусы подключения DS18B20: `Temp1_isConnected..Temp32_isConnected` |
| `160` | `96` | `reserve_status_tSens` | Резерв до coil `255` |
| `256` | `16` | `relay_struct_on` | Расчетные биты включения реле по датчикам `Temp1..Temp16` |
| `272` | `16` | `reserve_relay_struct_on` | Резерв |
| `288` | `16` | `relay_struct_off` | Расчетные биты выключения реле по датчикам `Temp1..Temp16` |
| `304` | `80` | reserved | Резерв до coil `383` |
| `384` | `1` | `init_param` | При записи `1` прошивка применяет `set_Temp[]` и `set_hyst[]`, затем сбрасывает бит |
| `385` | `1` | `init_Tsens` | При записи `1` прошивка повторно ищет DS18B20, затем сбрасывает бит |
| `386` | `1` | `Save_Param_to_Flash` | Поле объявлено, активного использования в текущем `main.c` не найдено |
| `387` | `13` | reserved2 | Резервные биты |
| `400` | `600` | reserved | Адреса доступны в общем диапазоне, прикладного поля нет |
## Привязка температур и реле
В `value_control()` прошивка сравнивает `sens[i].temperature` с уставкой и гистерезисом:
- если `temperature < set_temp - hyst`, выставляется `relay_struct_off` для датчика `i`;
- если `temperature > set_temp + hyst`, выставляется `relay_struct_on` для датчика `i`;
- если `temperature == set_temp`, оба бита для датчика сбрасываются.
Эти расчетные биты лежат в coils `256..271` и `288..303`. Прямая аппаратная привязка этих расчетных битов к GPIO-выходам в текущем `main.c` не найдена. Прямое управление GPIO найдено только для `PA10 / Relay_dc5v` через coil `20`.
## GUI binding to the real STM project
Source project checked: `..\new rev\john103C8T6`.
The PC GUI bridge is bound to the actual STM Modbus layout from:
- `Modbus\modbus_config.h`
- `Modbus\modbus_data.h`
- `Core\Src\main.c`
- `Core\Inc\ds18b20_driver.h`
- `Core\Inc\PROJ_setup.h`
Active STM settings:
| Item | Value |
|---|---:|
| Default slave ID | `3` |
| `MAX_SENSE` in STM | `32` |
| GUI channels used | first `16` |
| STM protocol on COM | Modbus RTU |
| Optional GUI network mode | Modbus TCP gateway/device with the same register map |
GUI runtime map:
| GUI value | STM source | Modbus function | 0-based address |
|---|---|---:|---:|
| Current temperature channel `N` | `MB_DATA.InRegs.sens_Temp[N]` | `0x04` | `0 + N` |
| DS18B20 ROM ID channel `N` | `MB_DATA.InRegs.ID.DevAddr[N][8]` | `0x04` | `1000 + N * 4` |
| Sensor connected channel `N` | `MB_DATA.Coils.status_tSens` | `0x01` | `128 + N` |
| Setpoint channel `N` | `MB_DATA.HoldRegs.set_Temp[N]`, value = `degC * 10` | `0x03` / `0x06` | `0 + N` |
| Apply setpoints | `MB_DATA.Coils.init_param` | `0x05` | `384` |
| Open command/state channel `N` | `MB_DATA.Coils.relay_struct_on` | `0x01` / `0x05` | `256 + N` |
| Close command/state channel `N` | `MB_DATA.Coils.relay_struct_off` | `0x01` / `0x05` | `288 + N` |
Notes:
- GUI channel `1` uses index `N = 0`; GUI channel `16` uses index `N = 15`.
- Setpoints are written as tenths of a degree: GUI `28.5°C` -> Modbus holding value `285`; readback `285` -> GUI `28.5°C`.
- DS18B20 IDs are stored by STM as `uint8_t DevAddr[32][8]` and exposed through 16-bit input registers. The bridge restores each register in little-endian byte order to display the ROM ID correctly.
- The current STM project does not expose analog valve position or opening angle registers. GUI position/angle are therefore derived from binary relay state: open = `100% / 90°`, close = `0% / 0°`.
- `value_control()` in `Core\Src\main.c` calculates `relay_struct_on/off` from temperature, setpoint, and hysteresis. Direct manual coil writes from GUI can be overwritten by that firmware logic unless STM firmware adds a manual override register/coil.
## STM room/channel structure added for GUI
Added to Keil STM project `..\new rev\john103C8T6`.
### Input registers: room status block
Base address: `400`. One room/channel uses `18` registers. Channel `N` uses base `400 + N * 18`, where GUI channel 1 is `N = 0`.
| Offset | Field | Scale / meaning |
|---:|---|---|
| 0 | `channel` | 1-based channel number |
| 1 | `location` | numeric location code |
| 2..5 | `ds18b20_id[4]` | 8-byte DS18B20 ROM ID, little-endian bytes per register |
| 6 | `temperature_x10` | current temperature, degC * 10 |
| 7 | `setpoint_x10` | setpoint, degC * 10 |
| 8 | `hysteresis_x10` | hysteresis, degC * 10 |
| 9 | `valve_position_pct` | valve opening percent, 0..100 |
| 10 | `valve_angle_deg` | opening angle in degrees |
| 11 | `valve_angle_max_deg` | max opening angle, default 90 |
| 12 | `is_connected` | DS18B20 connected flag |
| 13 | `valve_open` | open relay/command state |
| 14 | `valve_close` | close relay/command state |
| 15 | `mode` | 0 auto, 1 manual |
| 16 | `command_state` | 0 stop, 1 open, 2 close |
| 17 | `reserved` | reserved |
### Holding registers: room control block
Base address: `300`. One room/channel uses `8` registers. Channel `N` uses base `300 + N * 8`.
| Offset | Field | Scale / meaning |
|---:|---|---|
| 0 | `setpoint_x10` | writable setpoint, degC * 10 |
| 1 | `hysteresis_x10` | writable hysteresis, degC * 10 |
| 2 | `valve_position_pct` | writable manual opening percent, 0..100 |
| 3 | `valve_angle_max_deg` | max opening angle, default 90 |
| 4 | `mode` | 0 auto, 1 manual |
| 5 | `command` | 0 stop, 1 open, 2 close |
| 6 | `location` | numeric location code |
| 7 | `apply` | write 1 to apply room setpoint/hysteresis |
Compatibility:
- Legacy `set_Temp[0..31]` at holding `0..31` is kept.
- Legacy `set_hyst[0..31]` at holding `100..131` is kept.
- Legacy coils `256..271` and `288..303` are kept.
- The Python bridge writes both the legacy map and the new room control block.
+105
View File
@@ -0,0 +1,105 @@
# Web GUI для датчиков температуры и клапанов
Веб-интерфейс для:
- мониторинга температур,
- установки уставок,
- ручного и автоматического управления клапанами,
- управления позицией клапана в `%`.
## Быстрый старт
1. Запустите демо backend:
```bash
python mock_server.py
```
2. Откройте страницу:
- `http://127.0.0.1:8080/index.html`
3. В поле `API endpoint` оставьте пустым для демо или укажите `http://127.0.0.1:8080`.
Если API не указан, интерфейс переходит в офлайн-режим (демо) и хранит состояние в `localStorage`.
## Привязка к вашему проекту
GUI читает и пишет данные через `API_PATHS` в `app.js`:
1. Датчики: `/api/sensors`, `/sensors`, `/api/data`, `/state`
2. Клапаны: `/api/valves`, `/valves`, `/api/data`, `/state`
3. Запись: сначала `PUT /api/*/:id`, затем `PUT /api/*` (без `:id`)
Если ваш контракт другой, правьте:
- массив `API_PATHS` в `app.js`
- функции `normalizeSensor`, `normalizeValve` в `app.js`
## Работа с COM-портом (MCU)
Для чтения с порта COM запущен `serial_bridge.py`:
```bash
python serial_bridge.py --serial-port COM3 --baudrate 115200
```
По умолчанию сервис доступен на `http://127.0.0.1:8080`.
Поддерживаемые входящие форматы с порта:
1) JSON-пакет:
```json
{"sensors":[{"id":"zone_1","value":24.5,"setpoint":28.0}], "valves":[{"id":"valve_1","mode":"auto","position":32,"targetTemp":28}]}
```
2) Текстовый формат:
```text
T1=24.5;T1_SETPOINT=28.0;V1_MODE=auto;V1_POS=32;V1_TGT=28
```
Исходящие команды от GUI (`PUT`) также отправляются в порт как JSON:
```json
{"type":"sensor","id":"zone_1","setpoint":28.0}
{"type":"valve","id":"valve_1","mode":"manual","position":50}
```
Если нужен другой протокол от/к MCU (Modbus/байт-структура), пришлите пример кадров — подгоню парсер в `serial_bridge.py`.
В GUI:
- поле `COM порт` показывает список `/api/serial/ports`;
- кнопка `Обновить порты` — принудительно перечитывает список COM;
- кнопка `Подключить` / `Отключить` — вызывает `/api/serial/connect` и `/api/serial/disconnect` в `serial_bridge.py`.
## Форматы API
### GET
- `GET /api/sensors` → массив:
```json
[
{ "id": "zone_1", "name": "Термопара 1", "value": 24.6, "setpoint": 28, "unit": "°C", "zone": "1" }
]
```
- `GET /api/valves` → массив:
```json
[
{ "id": "valve_1", "name": "Клапан 1", "zone": "1", "mode": "auto", "position": 40, "targetTemp": 28, "isOpen": true }
]
```
### PUT
- `PUT /api/sensors/{id}`:
```json
{ "setpoint": 29.5 }
```
- `PUT /api/valves/{id}`:
- ручной:
```json
{ "mode": "manual", "position": 50 }
```
- авто:
```json
{ "mode": "auto", "targetTemp": 28.0 }
```
- или отдельные поля (`mode`, `position`, `targetTemp`).
## Файлы
- `index.html` — разметка страницы
- `styles.css` — стили
- `app.js` — логика интерфейса
- `mock_server.py` — демонстрационный backend
- `serial_bridge.py` — backend для COM-порта
+1640
View File
@@ -0,0 +1,1640 @@
const SENSOR_COUNT = 16;
const VALVE_COUNT = 32;
const DEFAULT_OPEN_DEGREES_MAX = 90;
const CHANNEL_LOCATIONS = [
"DUO прав",
"DUO лев",
"TRIO",
"SOLO",
"ОСНОВА 7",
"ОСНОВА 6",
"ОСНОВА 5",
"ОСНОВА 4",
"ОСНОВА 3",
"ОСНОВА 2",
"ОСНОВА 1",
];
function defaultChannelLocation(index) {
return CHANNEL_LOCATIONS[index % CHANNEL_LOCATIONS.length];
}
function makeDefaultSensors() {
return Array.from({ length: SENSOR_COUNT }, (_, index) => {
const number = index + 1;
return {
id: `zone_${number}`,
name: `Датчик ${number}`,
value: 0,
setpoint: 28,
unit: "°C",
zone: String(number),
location: defaultChannelLocation(index),
ds18b20Id: `28-00-00-00-00-00-00-${number.toString(16).toUpperCase().padStart(2, "0")}`,
};
});
}
function makeDefaultValves() {
return Array.from({ length: VALVE_COUNT }, (_, index) => {
const number = index + 1;
const zone = (index % SENSOR_COUNT) + 1;
return {
id: `valve_${number}`,
name: `Клапан ${number}`,
zone: String(zone),
mode: "auto",
position: 0,
targetTemp: 28,
isOpen: false,
openDegrees: 0,
openDegreesMax: DEFAULT_OPEN_DEGREES_MAX,
};
});
}
const defaultSensors = makeDefaultSensors();
const defaultValves = makeDefaultValves();
const API_PATHS = {
sensorsRead: ["/api/sensors", "/sensors", "/api/data", "/state"],
valvesRead: ["/api/valves", "/valves", "/api/data", "/state"],
sensorWrite: [
(id) => `/api/sensors/${encodeURIComponent(id)}`,
(id) => `/sensors/${encodeURIComponent(id)}`,
() => "/api/sensors",
() => "/sensors",
],
valveWrite: [
(id) => `/api/valves/${encodeURIComponent(id)}`,
(id) => `/valves/${encodeURIComponent(id)}`,
() => "/api/valves",
() => "/valves",
],
valveCalibrate: [
(id) => `/api/valves/${encodeURIComponent(id)}/calibrate`,
(id) => `/valves/${encodeURIComponent(id)}/calibrate`,
],
valvesCalibrateAll: ["/api/valves/calibrate-all", "/api/calibration/all", "/calibration/all"],
};
const SERIAL_API_PATHS = {
ports: [
"/api/serial/ports",
"/api/ports",
"/ports",
"/serial/ports",
"/status/ports",
],
status: [
"/api/serial/status",
"/api/state",
"/state",
"/serial/status",
],
connect: [
"/api/serial/connect",
"/api/connect",
"/connect",
"/serial/connect",
],
disconnect: [
"/api/serial/disconnect",
"/api/disconnect",
"/disconnect",
"/serial/disconnect",
],
};
const state = {
sensors: [...defaultSensors],
valves: [...defaultValves],
apiBase: "",
timer: null,
};
let serialConnected = false;
let selectedPort = "";
const sensorsEl = document.getElementById("sensors");
const valvesEl = document.getElementById("valves");
const statusEl = document.getElementById("status");
const globalStatus = document.getElementById("globalStatus");
const apiInput = document.getElementById("apiBase");
const refreshBtn = document.getElementById("refreshBtn");
const saveApiBtn = document.getElementById("saveApiBtn");
const refreshPortsBtn = document.getElementById("refreshPortsBtn");
const connectPortBtn = document.getElementById("connectPortBtn");
const comPortSelect = document.getElementById("comPortSelect");
const modbusTransport = document.getElementById("modbusTransport");
const tcpHost = document.getElementById("tcpHost");
const tcpPort = document.getElementById("tcpPort");
const modbusSlaveId = document.getElementById("modbusSlaveId");
const serialStatus = document.getElementById("serialStatus");
const calibrateAllBtn = document.getElementById("calibrateAllBtn");
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
function parseGuiNumber(value, fallback = 0) {
const parsed = Number(String(value ?? "").replace(",", "."));
return Number.isFinite(parsed) ? parsed : fallback;
}
function storageGet(name, fallback) {
const raw = localStorage.getItem(name);
if (!raw) return fallback;
try {
return JSON.parse(raw);
} catch {
return fallback;
}
}
function storageSet(name, value) {
localStorage.setItem(name, JSON.stringify(value));
}
function getModbusTransport() {
return (modbusTransport?.value || storageGet("modbusTransport", "rtu") || "rtu").toLowerCase();
}
function applyModbusTransportView() {
const transport = getModbusTransport();
const isTcp = transport === "tcp";
document.querySelectorAll(".rtu-field").forEach((element) => {
element.classList.toggle("hidden", isTcp);
});
document.querySelectorAll(".tcp-field").forEach((element) => {
element.classList.toggle("hidden", !isTcp);
});
if (refreshPortsBtn) {
refreshPortsBtn.disabled = isTcp;
}
if (serialStatus && !serialConnected) {
serialStatus.textContent = isTcp ? "Modbus TCP: не подключен" : "COM: не подключен";
}
}
function initModbusTransportControls() {
if (modbusTransport) {
modbusTransport.value = storageGet("modbusTransport", "rtu");
modbusTransport.addEventListener("change", () => {
storageSet("modbusTransport", modbusTransport.value);
applyModbusTransportView();
});
}
if (tcpHost) {
tcpHost.value = storageGet("tcpHost", tcpHost.value || "192.168.0.10");
tcpHost.addEventListener("change", () => storageSet("tcpHost", tcpHost.value.trim()));
}
if (tcpPort) {
tcpPort.value = storageGet("tcpPort", tcpPort.value || "502");
tcpPort.addEventListener("change", () => storageSet("tcpPort", tcpPort.value || "502"));
}
if (modbusSlaveId) {
modbusSlaveId.value = storageGet("modbusSlaveId", modbusSlaveId.value || "3");
modbusSlaveId.addEventListener("change", () => storageSet("modbusSlaveId", modbusSlaveId.value || "3"));
}
applyModbusTransportView();
}
function normalizeApiUrl(value) {
const trimmed = (value || "").trim();
if (trimmed && !/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) {
return normalizeApiUrl(`http://${trimmed}`);
}
if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
try {
const parsed = new URL(trimmed);
return `${parsed.protocol}//${parsed.host}`;
} catch {
return trimmed.endsWith("/") ? trimmed.slice(0, -1) : trimmed;
}
}
return trimmed.endsWith("/") ? trimmed.slice(0, -1) : trimmed;
}
function ensureApiBase() {
if (state.apiBase) {
return state.apiBase;
}
if (window.location?.protocol?.startsWith("http") && window.location.host) {
state.apiBase = `${window.location.protocol}//${window.location.host}`;
} else {
state.apiBase = "http://127.0.0.1:8080";
}
if (apiInput) {
apiInput.value = state.apiBase;
}
storageSet("apiBase", state.apiBase);
return state.apiBase;
}
function endpoint(path) {
if (!state.apiBase) {
throw new Error("demo");
}
return `${state.apiBase}${path}`;
}
async function parseResponseBody(response) {
const body = await response.text();
if (!body) {
return {};
}
try {
return JSON.parse(body);
} catch {
return body;
}
}
async function apiGet(path) {
const response = await fetch(endpoint(path), {
method: "GET",
headers: { accept: "application/json" },
});
if (!response.ok) {
const body = await response.text();
throw new Error(`HTTP ${response.status}: ${body}`);
}
return parseResponseBody(response);
}
async function apiPut(path, payload) {
const response = await fetch(endpoint(path), {
method: "PUT",
headers: { "content-type": "application/json", accept: "application/json" },
body: JSON.stringify(payload),
});
if (!response.ok) {
const body = await response.text();
throw new Error(`HTTP ${response.status}: ${body}`);
}
return parseResponseBody(response);
}
async function apiPost(path, payload) {
const response = await fetch(endpoint(path), {
method: "POST",
headers: { "content-type": "application/json", accept: "application/json" },
body: JSON.stringify(payload),
});
if (!response.ok) {
const body = await response.text();
throw new Error(`HTTP ${response.status}: ${body}`);
}
return parseResponseBody(response);
}
function extractPorts(payload) {
if (Array.isArray(payload)) return payload;
if (Array.isArray(payload?.ports)) return payload.ports;
return [];
}
function extractSelectedPort(payload) {
if (typeof payload?.selected === "string") return payload.selected;
if (typeof payload?.port === "string") return payload.port;
return "";
}
async function apiGetFallback(paths) {
let lastError;
for (const path of paths) {
try {
return await apiGet(path);
} catch (error) {
lastError = error;
}
}
throw lastError || new Error("No available endpoint");
}
async function apiPostFallback(paths, payload) {
let lastError;
for (const path of paths) {
try {
return await apiPost(path, payload);
} catch (error) {
lastError = error;
}
}
throw lastError || new Error("No available endpoint");
}
function degreesFromPosition(position, maxDegrees = DEFAULT_OPEN_DEGREES_MAX) {
const maxValue = Number(maxDegrees) > 0 ? Number(maxDegrees) : DEFAULT_OPEN_DEGREES_MAX;
return Math.round(clamp(position, 0, 100) * maxValue / 100);
}
function positionFromDegrees(degrees, maxDegrees = DEFAULT_OPEN_DEGREES_MAX) {
const maxValue = Number(maxDegrees) > 0 ? Number(maxDegrees) : DEFAULT_OPEN_DEGREES_MAX;
return Math.round(clamp(degrees, 0, maxValue) * 100 / maxValue);
}
async function fetchComPorts() {
if (!state.apiBase) {
return { ports: [], selected: "" };
}
const payload = await apiGetFallback(SERIAL_API_PATHS.ports);
return {
ports: extractPorts(payload),
selected: extractSelectedPort(payload),
};
}
function applyPortOptions(ports, preferredPort = "") {
if (!comPortSelect) return;
const current = comPortSelect.value;
comPortSelect.innerHTML = "<option value=\"\">Порт не выбран</option>";
for (const item of ports) {
const entry = typeof item === "string" ? { device: item, description: "" } : item;
const option = document.createElement("option");
const value = entry.device || entry.port || "";
if (!value) continue;
option.value = value;
option.textContent = `${entry.description ? `${entry.description} (${value})` : value}`;
option.dataset.hwid = entry.hwid || "";
comPortSelect.appendChild(option);
}
const targetPort = preferredPort || current;
if (ports.find((item) => (typeof item === "string" ? item : item.device) === targetPort)) {
comPortSelect.value = targetPort;
} else if (current) {
comPortSelect.value = "";
}
}
async function refreshPortsLegacy(silent = false) {
const payload = await fetchComPorts();
const ports = payload?.ports || [];
applyPortOptions(ports, payload?.selected || "");
if (!silent) {
updateStatus(ports.length ? "Доступные порты обновлены" : "Порты не найдены", ports.length ? "ok" : "warn");
}
}
async function connectSelectedPortLegacyOld() {
if (!state.apiBase) {
updateStatus("Укажите API endpoint, чтобы работать с COM-портами", "warn");
return;
}
const value = comPortSelect.value;
if (!value) {
updateStatus("Выберите COM порт", "warn");
return;
}
selectedPort = value;
try {
const payload = { port: value, baud: 115200, baudrate: 115200, parity: "N", stopbits: 1, bytesize: 8, timeout: 0.3 };
await apiPostFallback(SERIAL_API_PATHS.connect, payload);
serialConnected = true;
serialStatus.textContent = `COM: подключён (${value})`;
serialStatus.className = "status status-ok";
updateStatus(`COM ${value} подключён`, "ok");
} catch (error) {
serialConnected = false;
serialStatus.textContent = "COM: ошибка подключения";
serialStatus.className = "status status-error";
updateStatus(`Ошибка подключения COM: ${error.message}`, "error");
}
}
async function disconnectPortLegacy() {
if (!state.apiBase) return;
try {
await apiPostFallback(SERIAL_API_PATHS.disconnect, {});
} catch {
// ignore
} finally {
serialConnected = false;
serialStatus.textContent = "COM: не подключён";
serialStatus.className = "status status-warn";
selectedPort = "";
}
}
function normalizeSensor(raw = {}) {
const rawId = raw.id ?? raw.sensorId ?? raw.key ?? raw.code;
const zone = String(raw.zone ?? raw.channel ?? raw.sensorZone ?? "").trim() || String(rawId ?? "").replace(/\D+/g, "");
const fallbackZone = zone ? `zone_${zone}` : "zone_1";
const id = rawId || fallbackZone;
return {
id: String(id),
name: String(raw.name ?? raw.label ?? `Термопара ${zone || 1}`),
zone: String(zone || "1"),
value: Number(raw.value ?? raw.current ?? raw.temperature ?? raw.temp ?? 0),
setpoint: Number(raw.setpoint ?? raw.target ?? raw.targetTemp ?? raw.tSet ?? 0),
unit: raw.unit ?? raw.units ?? "°C",
};
}
function normalizeValve(raw = {}) {
const rawId = raw.id ?? raw.valveId ?? raw.key ?? raw.code;
const zone = String(raw.zone ?? raw.channel ?? raw.controlZone ?? "").trim() || String(rawId ?? "").replace(/\D+/g, "");
const id = rawId || `valve_${zone || "1"}`;
const mode = String(raw.mode ?? raw.workMode ?? "auto").toLowerCase() === "manual" ? "manual" : "auto";
const maxOpenDegrees = Number(raw.openDegreesMax ?? raw.maxOpenDegrees ?? raw.degMax ?? DEFAULT_OPEN_DEGREES_MAX);
const maxDegrees = Number.isFinite(maxOpenDegrees) && maxOpenDegrees > 0 ? maxOpenDegrees : DEFAULT_OPEN_DEGREES_MAX;
let position = Number(raw.position ?? raw.pos ?? raw.percent ?? raw.value);
let openDegrees = Number(raw.openDegrees ?? raw.degree ?? raw.posDeg ?? raw.opening ?? raw.openAngle ?? raw.angle);
if (Number.isNaN(position)) {
if (Number.isNaN(openDegrees)) {
position = 0;
openDegrees = 0;
} else {
position = positionFromDegrees(openDegrees, maxDegrees);
}
} else if (Number.isNaN(openDegrees)) {
openDegrees = degreesFromPosition(position, maxDegrees);
} else {
position = positionFromDegrees(openDegrees, maxDegrees);
}
return {
id: String(id),
name: String(raw.name ?? raw.label ?? `Клапан ${zone || 1}`),
zone: String(zone || "1"),
mode,
position: clamp(position, 0, 100),
openDegrees: clamp(Math.round(openDegrees), 0, maxDegrees),
openDegreesMax: maxDegrees,
targetTemp: Number(raw.targetTemp ?? raw.targetTemperature ?? raw.setpoint ?? raw.tSet ?? 0),
isOpen: Boolean(raw.isOpen ?? raw.open ?? position > 0),
};
}
function extractCollection(payload, key) {
if (!payload) return null;
if (Array.isArray(payload)) return payload;
if (Array.isArray(payload[key])) return payload[key];
if (Array.isArray(payload.data?.[key])) return payload.data[key];
if (Array.isArray(payload.result?.[key])) return payload.result[key];
if (Array.isArray(payload.state?.[key])) return payload.state[key];
if (Array.isArray(payload.data?.[`temperature_${key}`])) return payload.data[`temperature_${key}`];
return null;
}
async function fetchCollection(paths, key, normalizer) {
let lastError;
for (const path of paths) {
try {
const payload = await apiGet(path);
const collection = extractCollection(payload, key);
if (!collection || !Array.isArray(collection)) continue;
return collection.map(normalizer);
} catch (error) {
lastError = error;
}
}
throw lastError || new Error(`No available endpoint for ${key}`);
}
async function sendWithFallback(paths, id, payload) {
let lastError;
for (const resolvePath of paths) {
try {
const route = typeof resolvePath === "function" ? resolvePath(id) : resolvePath;
return await apiPut(route, payload);
} catch (error) {
lastError = error;
}
}
throw lastError || new Error("No available write endpoint");
}
async function postWithFallback(paths, id, payload) {
let lastError;
for (const resolvePath of paths) {
try {
const route = typeof resolvePath === "function" ? resolvePath(id) : resolvePath;
return await apiPost(route, payload);
} catch (error) {
lastError = error;
}
}
throw lastError || new Error("No available write endpoint");
}
function writePayloadBase(id, type = "sensor") {
const sensor = findSensorById(id);
const valve = findValveById(id);
const entity = type === "valve" ? valve : sensor;
return {
id,
...(entity ? { zone: entity.zone, name: entity.name } : {}),
};
}
function mergeDefaults(stored, defaults, normalizer = (item) => item) {
const source = Array.isArray(stored) ? stored : [];
const byId = new Map(source.map((item) => [String(item.id), normalizer(item)]));
return defaults.map((item) => ({
...item,
...(byId.get(String(item.id)) || {}),
}));
}
function buildDemos() {
state.sensors = mergeDefaults(storageGet("sensorState", []), defaultSensors, normalizeSensor);
state.valves = mergeDefaults(storageGet("valveState", []), defaultValves, normalizeValve);
storageSet("sensorState", state.sensors);
storageSet("valveState", state.valves);
}
function updateStatus(message, type = "ok") {
globalStatus.textContent = message;
globalStatus.className = `status-${type}`;
}
function setConnectButtonText() {
if (!connectPortBtn) {
return;
}
connectPortBtn.textContent = serialConnected ? "Отключить" : "Подключить";
}
function setSerialBusyState(isBusy, message) {
if (refreshPortsBtn) {
refreshPortsBtn.disabled = isBusy;
}
if (connectPortBtn) {
connectPortBtn.disabled = isBusy;
connectPortBtn.textContent = isBusy ? "..." : (serialConnected ? "Отключить" : "Подключить");
}
if (calibrateAllBtn) {
calibrateAllBtn.disabled = isBusy || !serialConnected;
}
if (message) {
serialStatus.textContent = message;
}
}
function applySerialStateFromPayload(payload = {}) {
const connected = Boolean(payload?.connected);
const transport = String(payload?.transport || getModbusTransport()).toLowerCase();
const isTcp = transport === "tcp";
const tcpAddress = payload?.address || (payload?.host || payload?.ip ? `${payload?.host || payload?.ip}:${payload?.tcpPort || payload?.tcp_port || 502}` : "");
const port = isTcp ? (tcpAddress || payload?.port || "") : (payload?.port || payload?.selected || payload?.com || "");
serialConnected = connected;
selectedPort = connected && port ? port : "";
if (comPortSelect && selectedPort) {
comPortSelect.value = selectedPort;
}
if (serialStatus) {
if (connected && selectedPort) {
serialStatus.textContent = isTcp ? `Modbus TCP: connected (${selectedPort})` : `COM: connected (${selectedPort})`;
serialStatus.className = "status status-ok";
} else {
serialStatus.textContent = isTcp ? "Modbus TCP: disconnected" : "COM: disconnected";
serialStatus.className = "status status-warn";
}
}
if (calibrateAllBtn) {
calibrateAllBtn.disabled = !serialConnected;
}
setConnectButtonText();
storageSet("comPort", selectedPort || "");
}
async function loadState() {
if (!state.apiBase) {
buildDemos();
simulateSensorPhysics();
statusEl.textContent = "Режим: демо (без API)";
statusEl.className = "status status-warn";
globalStatus.textContent = "Демо: все изменения сохраняются в браузере";
return;
}
const [sensors, valves] = await Promise.all([
fetchCollection(API_PATHS.sensorsRead, "sensors", normalizeSensor),
fetchCollection(API_PATHS.valvesRead, "valves", normalizeValve),
]);
state.sensors = sensors;
state.valves = valves;
statusEl.textContent = "Подключено к API";
statusEl.className = "status status-ok";
globalStatus.textContent = `API подключен: ${state.apiBase}`;
}
function findSensorByZone(zone) {
return state.sensors.find((s) => String(s.zone) === String(zone));
}
function findValveByZone(zone) {
return state.valves.find((v) => String(v.zone) === String(zone));
}
function findSensorById(id) {
return state.sensors.find((s) => s.id === id);
}
function findValveById(id) {
return state.valves.find((v) => v.id === id);
}
function renderSensors() {
const sensorsRoot = document.getElementById("sensors");
if (!sensorsRoot) return;
sensorsRoot.innerHTML = "";
const sensorPanel = sensorsRoot.closest(".panel");
if (sensorPanel) {
sensorPanel.hidden = false;
const title = sensorPanel.querySelector("h2");
if (title) title.textContent = "Каналы обработки датчиков";
}
}
function renderValves() {
const separateValvesRoot = document.getElementById("valves");
if (separateValvesRoot) {
separateValvesRoot.innerHTML = "";
const separateValvesPanel = separateValvesRoot.closest(".panel");
if (separateValvesPanel) separateValvesPanel.hidden = true;
}
const valvesRoot = document.getElementById("sensors");
if (!valvesRoot) return;
valvesRoot.innerHTML = "";
const valvePanel = valvesRoot.closest(".panel");
if (valvePanel) {
valvePanel.hidden = false;
const title = valvePanel.querySelector("h2");
if (title) title.textContent = "Каналы обработки датчиков";
}
const sensorLocations = storageGet("sensorLocations", {});
state.sensors.forEach((sensor, index) => {
const channelNumber = index + 1;
const openValve = state.valves[index * 2] || {};
const closeValve = state.valves[index * 2 + 1] || {};
const valve = openValve.id ? openValve : closeValve;
const card = document.createElement("article");
card.className = "item compact-item valve-item channel-card full-channel-card";
card.dataset.id = valve.id || `valve_${channelNumber * 2 - 1}`;
const rawTemp = Number(sensor.value);
const temp = Number.isFinite(rawTemp) ? rawTemp : 0;
const tempText = Number.isFinite(rawTemp) ? temp.toFixed(1) : "--";
let setpoint = Number(valve.targetTemp ?? sensor.setpoint ?? 28);
const positionRaw = Number(valve.position ?? 0);
const position = Number.isFinite(positionRaw) ? Math.max(0, Math.min(100, Math.round(positionRaw))) : 0;
const maxDegreesRaw = Number(valve.openDegreesMax ?? DEFAULT_OPEN_DEGREES_MAX);
const maxDegrees = Number.isFinite(maxDegreesRaw) && maxDegreesRaw > 0 ? maxDegreesRaw : DEFAULT_OPEN_DEGREES_MAX;
const angleRaw = Number(valve.openDegrees ?? ((position / 100) * maxDegrees));
const openDegrees = Number.isFinite(angleRaw) ? Math.max(0, Math.min(maxDegrees, Math.round(angleRaw))) : 0;
const openActive = Boolean(openValve.isOpen || position > 0 || openDegrees > 0);
const closeActive = Boolean(closeValve.isOpen || position <= 0);
const connected = Boolean(valve.connected ?? valve.isConnected ?? sensor.connected ?? state.connected);
const tempFill = Math.max(0, Math.min(100, ((temp + 5) / 55) * 100));
const mode = valve.mode === "manual" ? "manual" : "auto";
const modeText = mode === "manual" ? "ручной" : "авто";
const sensorName = sensor.name || `Датчик ${channelNumber}`;
const sensorId = sensor.id || `zone_${channelNumber}`;
const setpointDrafts = storageGet("setpointDrafts", {});
const draftValue = setpointDrafts[sensorId] ?? setpointDrafts[valve.id];
if (draftValue !== undefined) {
setpoint = parseGuiNumber(draftValue, setpoint);
}
const ds18b20Id = sensor.ds18b20Id || sensor.romId || sensor.rom || sensor.address || "--";
const location = sensorLocations[sensorId] || sensor.location || defaultChannelLocation(index);
const locationOptions = CHANNEL_LOCATIONS.map((name) => (
`<option value="${name}" ${name === location ? "selected" : ""}>${name}</option>`
)).join("");
const openValveId = openValve.id || `valve_${channelNumber * 2 - 1}`;
const closeValveId = closeValve.id || `valve_${channelNumber * 2}`;
const openHex = (Number(String(openValveId).replace(/\D/g, "")) || channelNumber * 2 - 1).toString(16).toUpperCase().padStart(2, "0");
const closeHex = (Number(String(closeValveId).replace(/\D/g, "")) || channelNumber * 2).toString(16).toUpperCase().padStart(2, "0");
const setpointText = Number.isFinite(setpoint) ? setpoint.toFixed(1) : "--";
const delta = Number.isFinite(rawTemp) && Number.isFinite(setpoint) ? temp - setpoint : NaN;
const deltaText = Number.isFinite(delta) ? `${delta > 0 ? "+" : ""}${delta.toFixed(1)}°C` : "--";
const deltaClass = !Number.isFinite(delta) || Math.abs(delta) <= 0.5 ? "ok" : delta > 0 ? "hot" : "cold";
const stateText = openActive ? "открытие" : closeActive ? "закрытие" : "стоп";
card.innerHTML = `
<div class="channel-head">
<div class="channel-title">
<strong>Канал ${channelNumber}</strong>
<small>${sensorName} · ${location} · зона ${sensor.zone || channelNumber}</small>
</div>
<span>связь <i class="channel-lamp ${connected ? "on" : "alarm"}"></i></span>
</div>
<div class="channel-id-grid full-channel-id-grid">
<label>уставка
<input class="targetTemp" type="number" step="0.5" value="${setpoint}">
</label>
<label>Расположение
<select class="sensorLocation" data-id="${sensorId}">
${locationOptions}
</select>
</label>
<label>ID DS18B20
<input readonly value="${ds18b20Id}">
</label>
</div>
<div class="channel-body full-channel-body">
<div class="temperature-widget" aria-label="Температура канала ${channelNumber}">
<div class="temp-scale">
<span>50</span><span>40</span><span>30</span><span>20</span><span>10</span><span>0</span><span>-5</span>
</div>
<div class="temp-bar"><b style="height: ${tempFill}%"></b></div>
<div class="temp-now">${tempText}°C</div>
</div>
<div class="channel-workarea">
<div class="top-metrics-row">
<div class="angle-panel">
<span>угол открытия</span>
<strong>${openDegrees}°</strong>
<small>максимум ${Math.round(maxDegrees)}°</small>
</div>
</div>
<div class="channel-data-grid full-channel-data-grid">
<div><span>температура</span><strong>${tempText}°C</strong></div>
<div><span>уставка</span><strong>${setpointText}°C</strong></div>
<div><span>отклонение</span><strong class="delta ${deltaClass}">${deltaText}</strong></div>
<div><span>расположение</span><strong>${location}</strong></div>
<div><span>ID DS18B20</span><strong>${ds18b20Id}</strong></div>
<div><span>режим</span><strong>${modeText}</strong></div>
<div><span>угол/max</span><strong>${openDegrees}° / ${Math.round(maxDegrees)}°</strong></div>
<div><span>команда</span><strong>${stateText}</strong></div>
<div><span>связь</span><strong>${connected ? "есть" : "нет"}</strong></div>
<div><span>канал</span><strong>${channelNumber}</strong></div>
</div>
<label class="range-control channel-position">положение заслонки
<input class="position" type="range" min="0" max="100" value="${position}">
<strong>${position}%</strong>
</label>
<div class="valve-state-row full-state-row">
<span><i class="channel-lamp ${openActive ? "on" : "off"}"></i> клапан откр ${channelNumber}</span>
<span><i class="channel-lamp ${closeActive ? "on" : "off"}"></i> клапан закр ${channelNumber}</span>
</div>
<div class="channel-actions">
<button class="quickPosition" data-position="100" type="button">откр ${channelNumber}</button>
<button class="quickPosition" data-position="0" type="button">закр ${channelNumber}</button>
</div>
<div class="channel-actions channel-mode-line">
<div class="toggle channel-mode">
<button class="modeBtn valveAuto ${mode === "auto" ? "active" : ""}" data-mode="auto" type="button">авто</button>
<button class="modeBtn valveManual ${mode === "manual" ? "active" : ""}" data-mode="manual" type="button">ручное</button>
</div>
<button class="applyTarget mini-btn" type="button">SP</button>
<button class="applyManual mini-btn" type="button">OK</button>
<button class="calibrateValve mini-btn" type="button">CAL</button>
</div>
</div>
</div>
`;
valvesRoot.appendChild(card);
});
}
function render() {
renderSensors();
renderValves();
}
async function applySetpoint(sensorId) {
const sensor = findSensorById(sensorId);
const input = document.querySelector(`.setpoint[data-id="${sensorId}"]`);
const value = Number(input.value);
if (Number.isNaN(value)) {
updateStatus("Некорректное значение уставки", "warn");
return;
}
if (!state.apiBase) {
sensor.setpoint = value;
storageSet("sensorState", state.sensors);
render();
updateStatus(`Демо: уставка ${sensor.name} = ${value.toFixed(1)} °C`, "ok");
return;
}
const payload = {
...writePayloadBase(sensorId, "sensor"),
setpoint: value,
id: sensorId,
};
try {
await sendWithFallback(API_PATHS.sensorWrite, sensorId, payload);
await loadState();
render();
updateStatus(`Уставка ${sensor.name} обновлена`, "ok");
} catch (error) {
updateStatus(`Ошибка уставки ${sensor.name}: ${error.message}`, "error");
}
}
async function applyValveMode(valveId, mode) {
const valve = findValveById(valveId);
if (!valve) return;
valve.mode = mode;
if (!state.apiBase) {
storageSet("valveState", state.valves);
render();
updateStatus(`Демо: ${valve.name} → ${mode}`, "ok");
return;
}
const payload = {
...writePayloadBase(valveId, "valve"),
mode,
id: valveId,
};
try {
await sendWithFallback(API_PATHS.valveWrite, valveId, payload);
await loadState();
render();
updateStatus(`Режим ${valve.name}: ${mode}`, "ok");
} catch (error) {
updateStatus(`Ошибка режима ${valve.name}: ${error.message}`, "error");
await loadState();
render();
}
}
async function applyValveTarget(valveId) {
const valve = findValveById(valveId);
const input = document.querySelector(`.targetTemp[data-id="${valveId}"]`);
const value = Number(input.value);
if (Number.isNaN(value)) {
updateStatus("Некорректная целевая температура", "warn");
return;
}
if (!state.apiBase) {
valve.targetTemp = value;
storageSet("valveState", state.valves);
updateStatus(`Демо: цель ${valve.name} = ${value.toFixed(1)} °C`, "ok");
return;
}
const payload = {
...writePayloadBase(valveId, "valve"),
targetTemp: value,
id: valveId,
};
try {
await sendWithFallback(API_PATHS.valveWrite, valveId, payload);
await loadState();
render();
updateStatus(`Целевая температура ${valve.name} обновлена`, "ok");
} catch (error) {
updateStatus(`Ошибка цели ${valve.name}: ${error.message}`, "error");
}
}
async function applyValvePosition(valveId) {
const valve = findValveById(valveId);
const input = document.querySelector(`.position[data-id="${valveId}"]`);
const value = Number(input.value);
if (Number.isNaN(value)) {
updateStatus("Некорректная позиция клапана", "warn");
return;
}
valve.position = clamp(Math.round(value), 0, 100);
valve.openDegrees = degreesFromPosition(valve.position, valve.openDegreesMax);
valve.isOpen = valve.position > 0;
if (!state.apiBase) {
storageSet("valveState", state.valves);
render();
updateStatus(`Демо: ручная позиция ${valve.name} = ${value}%`, "ok");
return;
}
const payload = {
...writePayloadBase(valveId, "valve"),
mode: "manual",
position: valve.position,
openDegrees: valve.openDegrees,
openDegreesMax: valve.openDegreesMax,
id: valveId,
};
try {
await sendWithFallback(API_PATHS.valveWrite, valveId, payload);
await loadState();
render();
updateStatus(`Позиция ${valve.name} обновлена`, "ok");
} catch (error) {
updateStatus(`Ошибка позиции ${valve.name}: ${error.message}`, "error");
}
}
async function calibrateValve(valveId) {
const valve = findValveById(valveId);
if (!valve) return;
if (!state.apiBase) {
valve.position = 0;
valve.openDegrees = 0;
valve.isOpen = false;
storageSet("valveState", state.valves);
render();
updateStatus(`Демо: калибровка ${valve.name} выполнена`, "ok");
return;
}
try {
const response = await postWithFallback(API_PATHS.valveCalibrate, valveId, {});
const updated = response?.valve || response;
if (updated && updated.id) {
const index = state.valves.findIndex((item) => item.id === valveId);
if (index !== -1) {
state.valves[index] = normalizeValve(updated);
} else {
await loadState();
}
} else {
await loadState();
}
render();
updateStatus(`Калибровка ${valve.name} выполнена`, "ok");
} catch (error) {
updateStatus(`Ошибка калибровки ${valve.name}: ${error.message}`, "error");
}
}
async function calibrateAllValves(silent = false) {
if (!state.apiBase) {
state.valves = state.valves.map((valve) => ({
...valve,
position: 0,
openDegrees: 0,
isOpen: false,
}));
render();
if (!silent) {
updateStatus("Демо: калибровка всех клапанов выполнена", "ok");
}
return;
}
try {
const response = await apiPostFallback(API_PATHS.valvesCalibrateAll, {});
const payload = Array.isArray(response?.valves) ? response.valves : null;
if (payload) {
state.valves = payload.map(normalizeValve);
} else {
await loadState();
}
render();
if (!silent) {
updateStatus("Калибровка всех клапанов выполнена", "ok");
}
} catch (error) {
if (!silent) {
updateStatus(`Ошибка калибровки всех клапанов: ${error.message}`, "error");
}
}
}
function simulateSensorPhysics() {
if (state.timer) clearInterval(state.timer);
state.timer = setInterval(() => {
for (const sensor of state.sensors) {
const valve = findValveByZone(sensor.zone);
const v = valve || {};
let target;
if (v.mode === "manual") {
target = 20 + (clamp(v.position ?? 0, 0, 100) / 100) * 70;
} else {
target = v.targetTemp ?? sensor.setpoint ?? 30;
}
const drift = target - sensor.value;
const noise = (Math.random() - 0.5) * 0.2;
sensor.value = clamp(sensor.value + drift * 0.08 + noise, -40, 150);
sensor.value = Number(sensor.value.toFixed(2));
}
for (const valve of state.valves) {
const maxOpenDegrees = Number(valve.openDegreesMax) > 0 ? Number(valve.openDegreesMax) : DEFAULT_OPEN_DEGREES_MAX;
valve.openDegrees = Math.round(degreesFromPosition(valve.position ?? 0, maxOpenDegrees));
valve.isOpen = (valve.position ?? 0) > 0;
}
storageSet("sensorState", state.sensors);
storageSet("valveState", state.valves);
render();
}, 2500);
}
function attachEvents() {
refreshBtn.addEventListener("click", () => {
const url = normalizeApiUrl(apiInput.value);
state.apiBase = url;
storageSet("apiBase", state.apiBase);
refreshAll(true);
refreshPorts(false);
});
refreshPortsBtn.addEventListener("click", () => {
refreshPorts();
});
connectPortBtn.addEventListener("click", async () => {
if (serialConnected) {
await disconnectPort();
setConnectButtonText();
return;
}
await connectSelectedPort();
setConnectButtonText();
});
if (calibrateAllBtn) {
calibrateAllBtn.addEventListener("click", () => {
calibrateAllValves(false);
});
}
saveApiBtn.addEventListener("click", () => {
const url = normalizeApiUrl(apiInput.value);
state.apiBase = url;
storageSet("apiBase", url);
if (!url) {
updateStatus("API отключён; переход в демо", "warn");
statusEl.textContent = "Режим: демо (без API)";
statusEl.className = "status status-warn";
} else {
updateStatus(`Сохранён API: ${state.apiBase}`, "ok");
statusEl.textContent = "Сохранён адрес API";
statusEl.className = "status status-ok";
}
render();
refreshPorts(false);
if (!url) {
serialStatus.textContent = "COM: не подключён";
serialStatus.className = "status status-warn";
connectPortBtn.textContent = "Подключить";
selectedPort = "";
serialConnected = false;
}
});
sensorsEl.addEventListener("click", (event) => {
const target = event.target;
if (target.classList.contains("applySetpoint")) {
applySetpoint(target.dataset.id);
}
});
valvesEl.addEventListener("click", (event) => {
const target = event.target;
if (target.classList.contains("modeBtn")) {
const button = target.closest(".toggle");
const valveId = button.dataset.id;
const mode = target.dataset.mode;
const card = button.closest(".item");
if (!card) return;
const autoBlock = card.querySelector(".valveAuto");
const manualBlock = card.querySelector(".valveManual");
const applyManualBtn = card.querySelector(".applyManual");
button.querySelectorAll(".modeBtn").forEach((b) => b.classList.remove("active"));
target.classList.add("active");
if (mode === "auto") {
autoBlock.classList.remove("hidden");
manualBlock.classList.add("hidden");
applyManualBtn.classList.add("hidden");
} else {
autoBlock.classList.add("hidden");
manualBlock.classList.remove("hidden");
applyManualBtn.classList.remove("hidden");
}
applyValveMode(valveId, mode);
}
if (target.classList.contains("applyTarget")) {
applyValveTarget(target.dataset.id);
}
if (target.classList.contains("applyManual")) {
applyValvePosition(target.dataset.id);
}
if (target.classList.contains("calibrateValve")) {
calibrateValve(target.dataset.id);
}
});
valvesEl.addEventListener("input", (event) => {
const target = event.target;
if (target.classList.contains("position")) {
const label = target.closest("label");
const span = label ? label.querySelector("strong") : null;
if (span) span.textContent = `${target.value}%`;
}
});
}
async function refreshAll(silent = false) {
try {
await loadState();
render();
if (!silent) updateStatus("Данные обновлены", "ok");
} catch (error) {
if (state.apiBase) {
statusEl.textContent = `Ошибка API: ${error.message}`;
statusEl.className = "status status-error";
globalStatus.textContent = "Не удалось получить данные с API. Откат в демо";
globalStatus.className = "status-warn";
await refreshPorts();
render();
} else {
updateStatus(`Демо инициализирован: ${error.message}`, "warn");
}
}
}
async function connectSelectedPortLegacy() {
if (!state.apiBase) {
updateStatus("Укажите API endpoint для работы с COM-портами", "warn");
return;
}
const value = comPortSelect.value;
if (!value) {
updateStatus("Выберите COM порт", "warn");
return;
}
const payload = { port: value, baud: 115200, baudrate: 115200, parity: "N", stopbits: 1, bytesize: 8, timeout: 0.3 };
try {
await apiPostFallback(SERIAL_API_PATHS.connect, payload);
selectedPort = value;
serialConnected = true;
storageSet("comPort", value);
serialStatus.textContent = `COM: подключен (${value})`;
serialStatus.className = "status status-ok";
connectPortBtn.textContent = "Отключить";
updateStatus(`COM ${value} подключен`, "ok");
} catch (error) {
serialConnected = false;
serialStatus.textContent = "COM: ошибка подключения";
serialStatus.className = "status status-error";
connectPortBtn.textContent = "Подключить";
storageSet("comPort", "");
selectedPort = "";
updateStatus(`Ошибка подключения COM: ${error.message}`, "error");
}
}
async function disconnectPortLegacy() {
if (!state.apiBase) return;
try {
await apiPostFallback(SERIAL_API_PATHS.disconnect, {});
} catch {
// ignore
} finally {
serialConnected = false;
selectedPort = "";
storageSet("comPort", "");
serialStatus.textContent = "COM: не подключен";
serialStatus.className = "status status-warn";
connectPortBtn.textContent = "Подключить";
}
}
async function restoreSerialUiLegacy() {
if (!state.apiBase) return;
try {
const status = await apiGetFallback(SERIAL_API_PATHS.status);
serialConnected = Boolean(status?.connected);
const port = status?.port || status?.selected;
if (serialConnected && port) {
selectedPort = port;
comPortSelect.value = selectedPort;
serialStatus.textContent = `COM: подключен (${selectedPort})`;
serialStatus.className = "status status-ok";
connectPortBtn.textContent = "Отключить";
storageSet("comPort", selectedPort);
} else {
selectedPort = "";
storageSet("comPort", "");
serialStatus.textContent = "COM: не подключен";
serialStatus.className = "status status-warn";
connectPortBtn.textContent = "Подключить";
}
} catch {
// no-op
}
}
async function refreshPorts(silent = false) {
if (!state.apiBase) {
ensureApiBase();
}
if (!state.apiBase) {
if (!silent) {
updateStatus("Set API base URL first", "warn");
}
return;
}
setSerialBusyState(true, "Scanning COM ports...");
try {
const payload = await fetchComPorts();
const ports = Array.isArray(payload?.ports) ? payload.ports : [];
const preferredPort = payload?.selected || selectedPort || storageGet("comPort", "");
applyPortOptions(ports, preferredPort);
if (!silent) {
updateStatus(
ports.length ? `Found COM ports: ${ports.length}` : "No COM ports found",
ports.length ? "ok" : "warn"
);
}
} catch (error) {
applyPortOptions([], "");
updateStatus(`Port scan error: ${error.message}`, "error");
} finally {
setSerialBusyState(false);
}
}
async function connectSelectedPort() {
if (!state.apiBase) {
updateStatus("Set API base URL first", "warn");
return;
}
const requestedPort = comPortSelect.value;
if (!requestedPort) {
updateStatus("Select COM port", "warn");
return;
}
setSerialBusyState(true, `Connecting ${requestedPort}...`);
try {
const payload = {
port: requestedPort,
baud: 115200,
baudrate: 115200,
parity: "N",
stopBits: 1,
stopbits: 1,
byteSize: 8,
bytesize: 8,
timeout: 0.3,
};
const status = await apiPostFallback(SERIAL_API_PATHS.connect, payload);
applySerialStateFromPayload({
connected: true,
port: status?.port || requestedPort,
selected: status?.selected || requestedPort,
});
await calibrateAllValves(true);
updateStatus(`COM ${requestedPort} connected`, "ok");
await refreshPorts(true);
} catch (error) {
applySerialStateFromPayload({ connected: false });
updateStatus(`COM connect error: ${error.message}`, "error");
} finally {
setSerialBusyState(false);
}
}
async function connectRtuPort() {
if (!state.apiBase) {
updateStatus("Укажи API endpoint для Modbus RTU", "warn");
return;
}
const value = comPortSelect?.value || selectedPort;
if (!value) {
updateStatus("Выберите COM порт", "warn");
return;
}
const unitId = Number(modbusSlaveId?.value || 3);
storageSet("modbusTransport", "rtu");
storageSet("comPort", value);
storageSet("modbusSlaveId", String(unitId));
setSerialBusyState(true, `Modbus RTU: подключение ${value}, slave ${unitId}...`);
try {
const status = await apiPostFallback(SERIAL_API_PATHS.connect, {
transport: "rtu",
mode: "rtu",
port: value,
baud: 115200,
baudrate: 115200,
parity: "N",
stopbits: 1,
bytesize: 8,
timeout: 0.8,
unitId,
slaveId: unitId,
});
applySerialStateFromPayload({
...(status || {}),
connected: Boolean(status?.connected ?? status?.ok),
transport: "rtu",
port: value,
});
updateStatus(`Modbus RTU connected: ${value}, slave ${unitId}`, "ok");
} catch (error) {
applySerialStateFromPayload({ connected: false, transport: "rtu" });
updateStatus(`Modbus RTU error: ${error.message}`, "error");
} finally {
setSerialBusyState(false);
applyModbusTransportView();
}
}
async function connectTcpPort() {
if (!state.apiBase) {
updateStatus("Укажи API endpoint для Modbus TCP", "warn");
return;
}
const host = (tcpHost?.value || "").trim();
const port = Number(tcpPort?.value || 502);
const unitId = Number(modbusSlaveId?.value || 3);
if (!host) {
updateStatus("Укажи IP адрес Modbus TCP", "warn");
return;
}
if (!Number.isInteger(port) || port < 1 || port > 65535) {
updateStatus("Некорректный TCP порт", "warn");
return;
}
storageSet("modbusTransport", "tcp");
storageSet("tcpHost", host);
storageSet("tcpPort", String(port));
storageSet("modbusSlaveId", String(unitId));
setSerialBusyState(true, `Modbus TCP: подключение ${host}:${port}, slave ${unitId}...`);
try {
const status = await apiPostFallback(SERIAL_API_PATHS.connect, {
transport: "tcp",
mode: "tcp",
host,
ip: host,
tcpPort: port,
tcp_port: port,
port,
unitId,
slaveId: unitId,
timeout: 0.8,
});
applySerialStateFromPayload({
...(status || {}),
connected: Boolean(status?.connected ?? status?.ok),
transport: "tcp",
host,
tcpPort: port,
address: `${host}:${port}`,
});
updateStatus(`Modbus TCP connected: ${host}:${port}, slave ${unitId}`, "ok");
} catch (error) {
applySerialStateFromPayload({ connected: false, transport: "tcp" });
updateStatus(`Modbus TCP error: ${error.message}`, "error");
} finally {
setSerialBusyState(false);
applyModbusTransportView();
}
}
if (connectPortBtn) {
connectPortBtn.addEventListener("click", async (event) => {
event.preventDefault();
event.stopImmediatePropagation();
if (serialConnected) {
await disconnectPort();
setConnectButtonText();
applyModbusTransportView();
return;
}
if (getModbusTransport() === "tcp") {
await connectTcpPort();
} else {
await connectRtuPort();
}
setConnectButtonText();
}, true);
}
async function disconnectPort() {
if (!state.apiBase) {
applySerialStateFromPayload({ connected: false });
return;
}
setSerialBusyState(true, "Disconnecting...");
try {
const status = await apiPostFallback(SERIAL_API_PATHS.disconnect, {});
applySerialStateFromPayload(status || { connected: false });
} catch {
// ignore
} finally {
applySerialStateFromPayload({ connected: false });
setSerialBusyState(false);
}
}
async function restoreSerialUi() {
if (!state.apiBase) {
return;
}
try {
const status = await apiGetFallback(SERIAL_API_PATHS.status);
applySerialStateFromPayload(status);
if (status?.connected) {
await calibrateAllValves(true);
}
} catch {
applySerialStateFromPayload({ connected: false });
}
}
// Channel location selector
document.addEventListener("change", (event) => {
const select = event.target.closest(".sensorLocation");
if (!select) return;
const sensorId = select.dataset.id;
const sensor = state.sensors.find((item) => item.id === sensorId);
if (sensor) {
sensor.location = select.value;
storageSet("sensorState", state.sensors);
}
const locations = storageGet("sensorLocations", {});
locations[sensorId] = select.value;
storageSet("sensorLocations", locations);
render();
});
// Controls inside sensor processing channel cards
document.addEventListener("input", (event) => {
const targetInput = event.target.closest(".full-channel-card .targetTemp");
if (targetInput) {
const card = targetInput.closest(".full-channel-card");
const valveId = card?.dataset.id;
const targetTemp = parseGuiNumber(targetInput.value, 0);
const valve = state.valves.find((item) => item.id === valveId);
if (valve) {
valve.targetTemp = targetTemp;
}
const valveNumber = Number(String(valveId || "").replace(/\D/g, "")) || 1;
const sensorIndex = Math.max(0, Math.floor((valveNumber - 1) / 2));
const sensor = state.sensors[sensorIndex];
if (sensor) {
sensor.setpoint = targetTemp;
}
const setpointDrafts = storageGet("setpointDrafts", {});
if (sensor?.id) {
setpointDrafts[sensor.id] = targetInput.value;
}
if (valveId) {
setpointDrafts[valveId] = targetInput.value;
}
storageSet("setpointDrafts", setpointDrafts);
storageSet("sensorState", state.sensors);
storageSet("valveState", state.valves);
return;
}
const positionInput = event.target.closest(".full-channel-card .position");
if (!positionInput) return;
const label = positionInput.closest("label");
const value = Number(positionInput.value || 0);
const strong = label?.querySelector("strong");
if (strong) strong.textContent = `${value}%`;
});
document.addEventListener("click", async (event) => {
const button = event.target.closest(".full-channel-card button");
if (!button) return;
const card = button.closest(".full-channel-card");
const valveId = card?.dataset.id;
if (!card || !valveId) return;
if (button.classList.contains("modeBtn")) {
const mode = button.dataset.mode === "manual" ? "manual" : "auto";
card.querySelectorAll(".modeBtn").forEach((item) => item.classList.remove("active"));
button.classList.add("active");
const valve = state.valves.find((item) => item.id === valveId);
if (valve) valve.mode = mode;
storageSet("valveState", state.valves);
await sendWithFallback(API_PATHS.valveWrite, valveId, {
...writePayloadBase(valveId, "valve"),
id: valveId,
mode,
});
return;
}
if (button.classList.contains("applyTarget")) {
const targetInput = card.querySelector(".targetTemp");
const targetTemp = parseGuiNumber(targetInput?.value, 0);
const valve = state.valves.find((item) => item.id === valveId);
if (valve) valve.targetTemp = targetTemp;
const valveNumber = Number(String(valveId || "").replace(/\D/g, "")) || 1;
const sensorIndex = Math.max(0, Math.floor((valveNumber - 1) / 2));
const sensor = state.sensors[sensorIndex];
if (sensor) sensor.setpoint = targetTemp;
const setpointDrafts = storageGet("setpointDrafts", {});
if (sensor?.id) {
setpointDrafts[sensor.id] = targetInput?.value ?? String(targetTemp);
}
setpointDrafts[valveId] = targetInput?.value ?? String(targetTemp);
storageSet("setpointDrafts", setpointDrafts);
storageSet("sensorState", state.sensors);
storageSet("valveState", state.valves);
await sendWithFallback(API_PATHS.valveWrite, valveId, {
...writePayloadBase(valveId, "valve"),
id: valveId,
targetTemp,
});
await loadState();
return;
}
if (button.classList.contains("applyManual")) {
const positionInput = card.querySelector(".position");
const position = Number(positionInput?.value || 0);
const valve = state.valves.find((item) => item.id === valveId);
if (valve) {
valve.position = position;
valve.isOpen = position > 0;
}
storageSet("valveState", state.valves);
await sendWithFallback(API_PATHS.valveWrite, valveId, {
...writePayloadBase(valveId, "valve"),
id: valveId,
position,
});
await loadState();
return;
}
if (button.classList.contains("calibrateValve")) {
await postWithFallback(API_PATHS.valveCalibrate, valveId, {
...writePayloadBase(valveId, "valve"),
id: valveId,
});
await loadState();
}
});
// Quick manual open/close buttons for channel cards
document.addEventListener("click", (event) => {
const quickButton = event.target.closest(".quickPosition");
if (!quickButton) return;
const card = quickButton.closest(".valve-item");
if (!card) return;
const manualButton = card.querySelector(".valveManual");
if (manualButton && !manualButton.classList.contains("active")) {
manualButton.click();
}
const positionInput = card.querySelector(".position");
if (positionInput) {
positionInput.value = quickButton.dataset.position || "0";
positionInput.dispatchEvent(new Event("input", { bubbles: true }));
}
const applyButton = card.querySelector(".applyManual");
if (applyButton) applyButton.click();
});
window.addEventListener("load", async () => {
apiInput.value = storageGet("apiBase", "");
state.apiBase = normalizeApiUrl(apiInput.value);
initModbusTransportControls();
ensureApiBase();
selectedPort = storageGet("comPort", "");
attachEvents();
await refreshAll(true);
if (getModbusTransport() !== "tcp") {
await refreshPorts(true);
}
render();
if (selectedPort) {
comPortSelect.value = selectedPort;
}
await restoreSerialUi();
setInterval(() => {
if (state.apiBase) {
loadState().then(render).catch(() => {});
}
}, 3000);
});
+72
View File
@@ -0,0 +1,72 @@
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Тепло и клапаны — управление</title>
<link rel="stylesheet" href="styles.css?v=20260625-ds18b20-id-1" />
</head>
<body>
<div class="shell">
<header class="header">
<h1>Панель температуры и клапанов</h1>
<p>Мониторинг датчиков, управление уставками и ручным/авто управлением клапанов.</p>
</header>
<section class="panel">
<label for="apiBase">API endpoint</label>
<div class="api-row">
<input id="apiBase" placeholder="http://127.0.0.1:1923 (пусто = демо)" value="" />
<button id="refreshBtn">Обновить</button>
<button id="saveApiBtn">Сохранить адрес API</button>
<span id="status" class="status">Режим: не подключен</span>
</div>
<p class="hint">Если API не задан, интерфейс работает в автономном демо-режиме с локальным хранением.</p>
<div class="serial-block">
<div class="serial-row">
<label for="modbusTransport">Обмен</label>
<select id="modbusTransport">
<option value="rtu">Modbus RTU / COM</option>
<option value="tcp">Modbus TCP / IP</option>
</select>
<label for="modbusSlaveId">Slave ID</label>
<input id="modbusSlaveId" type="number" min="1" max="247" value="3">
</select>
<label class="rtu-field" for="comPortSelect">COM порт</label>
<select id="comPortSelect" class="rtu-field">
<option value="">Порт не выбран</option>
</select>
<button id="refreshPortsBtn" class="rtu-field">Обновить порты</button>
<label class="tcp-field hidden" for="tcpHost">IP</label>
<input id="tcpHost" class="tcp-field hidden" type="text" value="192.168.0.10" placeholder="192.168.0.10">
<label class="tcp-field hidden" for="tcpPort">TCP порт</label>
<input id="tcpPort" class="tcp-field hidden" type="number" min="1" max="65535" value="502">
<button id="connectPortBtn">Подключить</button>
<button id="calibrateAllBtn">Калибровать все</button>
<span id="serialStatus" class="status status-warn">COM: не подключён</span>
</div>
</div>
</section>
<section class="panel grid">
<article class="card">
<h2>Датчики температуры</h2>
<div id="sensors" class="list"></div>
</article>
<article class="card">
<h2>Клапаны</h2>
<div id="valves" class="list"></div>
</article>
</section>
<footer class="footer">
<span>Статус: </span>
<span id="globalStatus">Ожидание...</span>
</footer>
</div>
<script src="app.js?v=20260625-setpoint-draft-1"></script>
</body>
</html>
@@ -0,0 +1 @@
F:\set\workspace\setcorp\set506\git_project\ds128b20\new rev\john103C8T6\.codex-restored-sessions\codex_sessions_restored_20260609132029
@@ -0,0 +1,16 @@
# Restored Codex sessions
Source archive: `C:\Users\z\Documents\Codex\2026-06-09\c-users-z-codex-sessions\outputs\codex_sessions_restored_20260609132029`
Project CWD: `F:\set\workspace\setcorp\set506\git_project\ds128b20\new rev\john103C8T6`
Generated copy: `2026-06-09T16:29:15.1490867+03:00`
## Dialogues
| Started | Title | Transcript |
|---|---|---|
| 2026-05-28T16:32:05.792Z | собери проект убери ошибки ,если нужны библиотеки подтяни их уровнем выше | [transcript](transcripts/001_14220bef_session.md) |
| 2026-05-28T16:47:52.141Z | настроить RTC с календарем и backup в modbuds сделать регистр времени часы минуты секунды дата | [transcript](transcripts/002_a30c80b2_RTC_backup_modbuds.md) |
## Restored files from patch records
Recovered embedded file contents, if any, are in `files_from_patches/`. Original project files were not overwritten.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,53 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file dma.h
* @brief This file contains all the function prototypes for
* the dma.c file
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __DMA_H__
#define __DMA_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* DMA memory to memory transfer handles -------------------------------------*/
extern DMA_HandleTypeDef hdma_memtomem_dma1_channel1;
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
void MX_DMA_Init(void);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __DMA_H__ */
@@ -0,0 +1,150 @@
/* USER CODE BEGIN Header */
/**
***************************************************************
* 1 2 3 4 5 6 7 8 *
* --- +++ *
* *
*****************************************************************
******************************************************************************
* @file : main.h
* @brief : Header for main.c file.
* This file contains the common defines of the application.
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "Proj_setup.h"
#include "dallas_tools.h"
#include "modbus.h"
typedef enum
{
FuncOK=0,
FuncERROR=1
}FuncStat;
typedef enum {
CONFIG_DEVICE_ID = 0,
CONFIG_BAUDRATE,
CONFIG_CALIBRATION,
CONFIG_SETTINGS
} ConfigParams;
typedef enum {
STATE_OPEN_VALVE = 0, // open
STATE_CLOSE_VALVE= 1 // close
} ValveState;
typedef struct
{
uint32_t id[2];
float temp;
uint16_t location;
uint8_t t_open;
float t_set;
uint8_t t_close;
uint8_t status_T_sense:1 ;
ValveState state;
uint16_t count;
}TEMP_TypeDef;
typedef struct {
uint32_t init_tsens : 1; // Update Interrupt Flag (бит 0) — флаг переполнения/обновления
// ... (другие биты могут быть зарезервированы или использоваться в расширенных таймерах)
} Flags_TypeDef;
/* USER CODE END Includes */
/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */
#define reset_blink_delay 50
#define rest_iter 10
#define GPIOB11_valve MB_DATA.Coils.coils[0].state_val_bit.state_val_01
#define ralay_5v_on GPIOA->ODR&(1<<10);
extern void handle_command(char* cmd);
typedef void (*FunctionPointer)(void);
uint16_t handle_valves(TEMP_TypeDef* temp_sense[MAX_SENSE]);
void init_setpoint_all_T_sense(TEMP_TypeDef* temp_sense, int size_array);
void iwdg_refresh(void);
void led_blink(GPIO_TypeDef *GPIOx,uint16_t GPIO_Pin,uint8_t iter,uint16_t delay);
FuncStat Field_modbus(MB_DataStructureTypeDef* MB_DATA, Flags_TypeDef* flag);
FuncStat packStruct(MB_DataStructureTypeDef* MB_DATA,int sizeARR);
void Check_Tconnect(MB_DataStructureTypeDef* MB_DATA, Flags_TypeDef* flag ,DALLAS_HandleTypeDef* hdallas, int a[50]);
FuncStat value_control(void );
void reinit_t_sens(void);
HAL_StatusTypeDef ModbusMaster_Request(RS_MsgTypeDef *request,
void (*callback)(RS_HandleTypeDef *, RS_MsgTypeDef *));
/* USER CODE END ET */
/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */
/* USER CODE END EC */
/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */
/* USER CODE END EM */
/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);
/* USER CODE BEGIN EFP */
/* USER CODE END EFP */
/* Private defines -----------------------------------------------------------*/
#define Relay_dc5v_Pin GPIO_PIN_10
#define Relay_dc5v_GPIO_Port GPIOA
#define One_wire_Pin GPIO_PIN_15
#define One_wire_GPIO_Port GPIOA
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
#ifdef __cplusplus
}
#endif
#endif /* __MAIN_H */
@@ -0,0 +1,60 @@
/**
******************************************************************************
* @file ow_port.h
* @brief This file includes the driver for port for OneWire purposes
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef ONEWIRE_PORT_H
#define ONEWIRE_PORT_H
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
/* I/O Port ------------------------------------------------------------------*/
//#define LL_Driver ///< использовать CMSIS для управления ножкой
#define CMSIS_Driver ///< использовать CMSIS для управления ножкой
// если ничего не выбрано - используется HAL
/**
* @def OW_GPIO_Port
* @brief Порт вывода для шины 1-Wire.
* @details Указывает порт GPIO, к которому подключена линия данных 1-Wire (например, для DS18B20).
*/
#define OW_GPIO_Port GPIOA
/**
* @def OW_Pin_Numb
* @brief Номер пина в порту OW_GPIO_Port.
* @details Используется для формирования маски пина и настройки ввода/вывода.
*/
#define OW_Pin_Numb 15
/**
* @def OW_Pin
* @brief Маска пина, соответствующая номеру OW_Pin_Numb.
* @details Используется при доступе к регистрам порта для управления состоянием линии 1-Wire.
*/
#define OW_Pin (1<<OW_Pin_Numb)
/**
* @def OW_TIM
* @brief Аппаратный таймер для формирования временных интервалов протокола 1-Wire.
* @details Применяется для создания точных задержек при обмене данными по шине 1-Wire.
*/
#define OW_TIM TIM1
/**
* @def OW_TIM_1US_PERIOD
* @brief Количество тактов таймера OW_TIM, соответствующее 1 микросекунде.
* @details Вычисляется на основе частоты таймера. Например, для таймера с частотой 24 МГц значение будет равно 24.
*/
#define OW_TIM_1US_PERIOD 72
/* OneWire Timings -----------------------------------------------------------*/
void OneWire_Delay_us(uint32_t us);
/* Common Register -----------------------------------------------------------*/
#endif /* ONEWIRE_PORT_H */
@@ -0,0 +1,65 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file rtc.h
* @brief This file contains all the function prototypes for
* the rtc.c file
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __RTC_H__
#define __RTC_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern RTC_HandleTypeDef hrtc;
/* USER CODE BEGIN Private defines */
typedef struct
{
uint16_t hours;
uint16_t minutes;
uint16_t seconds;
uint16_t date;
uint16_t month;
uint16_t year;
uint16_t weekday;
} RTC_CalendarTypeDef;
/* USER CODE END Private defines */
void MX_RTC_Init(void);
/* USER CODE BEGIN Prototypes */
uint8_t RTC_CalendarIsValid(const RTC_CalendarTypeDef *calendar);
HAL_StatusTypeDef RTC_CalendarGet(RTC_CalendarTypeDef *calendar);
HAL_StatusTypeDef RTC_CalendarSet(const RTC_CalendarTypeDef *calendar);
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __RTC_H__ */
@@ -0,0 +1,605 @@
/**
******************************************************************************
* @file stm32f4xx_hal_conf.h
* @author MCD Application Team
* @brief HAL configuration file.
*
* @note modified by ARM
* The modifications allow to use this file as User Code Template
* within the Device Family Pack.
******************************************************************************
* @attention
*
* Copyright (c) 2017 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F4xx_HAL_CONF_H
#define __STM32F4xx_HAL_CONF_H
#ifdef _RTE_
#include "RTE_Components.h" /* Component selection */
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* ########################## Module Selection ############################## */
/**
* @brief This is the list of modules to be used in the HAL driver
*/
#ifdef RTE_DEVICE_HAL_COMMON
#define HAL_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_ADC
#define HAL_ADC_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_CAN
#define HAL_CAN_MODULE_ENABLED
/* #define HAL_CAN_LEGACY_MODULE_ENABLED */
#endif
#ifdef RTE_DEVICE_HAL_CRC
#define HAL_CRC_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_CEC
#define HAL_CEC_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_CRYP
#define HAL_CRYP_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_DAC
#define HAL_DAC_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_DCMI
#define HAL_DCMI_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_DMA
#define HAL_DMA_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_DMA2D
#define HAL_DMA2D_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_ETH
#define HAL_ETH_MODULE_ENABLED
#endif
#if defined (RTE_DEVICE_HAL_FLASH) || defined (RTE_DEVICE_HAL_COMMON)
#define HAL_FLASH_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_NAND
#define HAL_NAND_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_NOR
#define HAL_NOR_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_PCCARD
#define HAL_PCCARD_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_SRAM
#define HAL_SRAM_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_SDRAM
#define HAL_SDRAM_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_HASH
#define HAL_HASH_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_GPIO
#define HAL_GPIO_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_EXTI
#define HAL_EXTI_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_I2C
#define HAL_I2C_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_SMBUS
#define HAL_SMBUS_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_I2S
#define HAL_I2S_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_IWDG
#define HAL_IWDG_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_LTDC
#define HAL_LTDC_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_DSI
#define HAL_DSI_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_PWR
#define HAL_PWR_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_QSPI
#define HAL_QSPI_MODULE_ENABLED
#endif
#if defined (RTE_DEVICE_HAL_RCC) || defined (RTE_DEVICE_HAL_COMMON)
#define HAL_RCC_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_RNG
#define HAL_RNG_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_RTC
#define HAL_RTC_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_SAI
#define HAL_SAI_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_SD
#define HAL_SD_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_SPI
#define HAL_SPI_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_TIM
#define HAL_TIM_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_UART
#define HAL_UART_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_USART
#define HAL_USART_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_IRDA
#define HAL_IRDA_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_SMARTCARD
#define HAL_SMARTCARD_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_WWDG
#define HAL_WWDG_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_CORTEX
#define HAL_CORTEX_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_PCD
#define HAL_PCD_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_HCD
#define HAL_HCD_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_FMPI2C
#define HAL_FMPI2C_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_FMPSMBUS
#define HAL_FMPSMBUS_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_SPDIFRX
#define HAL_SPDIFRX_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_DFSDM
#define HAL_DFSDM_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_LPTIM
#define HAL_LPTIM_MODULE_ENABLED
#endif
#ifdef RTE_DEVICE_HAL_MMC
#define HAL_MMC_MODULE_ENABLED
#endif
/* ########################## HSE/HSI Values adaptation ##################### */
/**
* @brief Adjust the value of External High Speed oscillator (HSE) used in your application.
* This value is used by the RCC HAL module to compute the system frequency
* (when HSE is used as system clock source, directly or through the PLL).
*/
#if !defined (HSE_VALUE)
#define HSE_VALUE 25000000U /*!< Value of the External oscillator in Hz */
#endif /* HSE_VALUE */
#if !defined (HSE_STARTUP_TIMEOUT)
#define HSE_STARTUP_TIMEOUT 100U /*!< Time out for HSE start up, in ms */
#endif /* HSE_STARTUP_TIMEOUT */
/**
* @brief Internal High Speed oscillator (HSI) value.
* This value is used by the RCC HAL module to compute the system frequency
* (when HSI is used as system clock source, directly or through the PLL).
*/
#if !defined (HSI_VALUE)
#define HSI_VALUE 16000000U /*!< Value of the Internal oscillator in Hz */
#endif /* HSI_VALUE */
/**
* @brief Internal Low Speed oscillator (LSI) value.
*/
#if !defined (LSI_VALUE)
#define LSI_VALUE 32000U /*!< LSI Typical Value in Hz */
#endif /* LSI_VALUE */ /*!< Value of the Internal Low Speed oscillator in Hz
The real value may vary depending on the variations
in voltage and temperature. */
/**
* @brief External Low Speed oscillator (LSE) value.
*/
#if !defined (LSE_VALUE)
#define LSE_VALUE 32768U /*!< Value of the External Low Speed oscillator in Hz */
#endif /* LSE_VALUE */
#if !defined (LSE_STARTUP_TIMEOUT)
#define LSE_STARTUP_TIMEOUT 5000U /*!< Time out for LSE start up, in ms */
#endif /* LSE_STARTUP_TIMEOUT */
/**
* @brief External clock source for I2S peripheral
* This value is used by the I2S HAL module to compute the I2S clock source
* frequency, this source is inserted directly through I2S_CKIN pad.
*/
#if !defined (EXTERNAL_CLOCK_VALUE)
#define EXTERNAL_CLOCK_VALUE 12288000U /*!< Value of the External oscillator in Hz*/
#endif /* EXTERNAL_CLOCK_VALUE */
/* Tip: To avoid modifying this file each time you need to use different HSE,
=== you can define the HSE value in your toolchain compiler preprocessor. */
/* ########################### System Configuration ######################### */
/**
* @brief This is the HAL system configuration section
*/
#define VDD_VALUE 3300U /*!< Value of VDD in mv */
#define TICK_INT_PRIORITY 0x0FU /*!< tick interrupt priority */
#define USE_RTOS 0U
#define PREFETCH_ENABLE 0U /* The prefetch will be enabled in SystemClock_Config(), depending on the used
STM32F405/415/07/417 device: RevA (prefetch must be off) or RevZ (prefetch can be on/off) */
#define INSTRUCTION_CACHE_ENABLE 1U
#define DATA_CACHE_ENABLE 1U
#define USE_HAL_ADC_REGISTER_CALLBACKS 0U /* ADC register callback disabled */
#define USE_HAL_CAN_REGISTER_CALLBACKS 0U /* CAN register callback disabled */
#define USE_HAL_CEC_REGISTER_CALLBACKS 0U /* CEC register callback disabled */
#define USE_HAL_CRYP_REGISTER_CALLBACKS 0U /* CRYP register callback disabled */
#define USE_HAL_DAC_REGISTER_CALLBACKS 0U /* DAC register callback disabled */
#define USE_HAL_DCMI_REGISTER_CALLBACKS 0U /* DCMI register callback disabled */
#define USE_HAL_DFSDM_REGISTER_CALLBACKS 0U /* DFSDM register callback disabled */
#define USE_HAL_DMA2D_REGISTER_CALLBACKS 0U /* DMA2D register callback disabled */
#define USE_HAL_DSI_REGISTER_CALLBACKS 0U /* DSI register callback disabled */
#define USE_HAL_ETH_REGISTER_CALLBACKS 0U /* ETH register callback disabled */
#define USE_HAL_HASH_REGISTER_CALLBACKS 0U /* HASH register callback disabled */
#define USE_HAL_HCD_REGISTER_CALLBACKS 0U /* HCD register callback disabled */
#define USE_HAL_I2C_REGISTER_CALLBACKS 0U /* I2C register callback disabled */
#define USE_HAL_FMPI2C_REGISTER_CALLBACKS 0U /* FMPI2C register callback disabled */
#define USE_HAL_FMPSMBUS_REGISTER_CALLBACKS 0U /* FMPSMBUS register callback disabled */
#define USE_HAL_I2S_REGISTER_CALLBACKS 0U /* I2S register callback disabled */
#define USE_HAL_IRDA_REGISTER_CALLBACKS 0U /* IRDA register callback disabled */
#define USE_HAL_LPTIM_REGISTER_CALLBACKS 0U /* LPTIM register callback disabled */
#define USE_HAL_LTDC_REGISTER_CALLBACKS 0U /* LTDC register callback disabled */
#define USE_HAL_MMC_REGISTER_CALLBACKS 0U /* MMC register callback disabled */
#define USE_HAL_NAND_REGISTER_CALLBACKS 0U /* NAND register callback disabled */
#define USE_HAL_NOR_REGISTER_CALLBACKS 0U /* NOR register callback disabled */
#define USE_HAL_PCCARD_REGISTER_CALLBACKS 0U /* PCCARD register callback disabled */
#define USE_HAL_PCD_REGISTER_CALLBACKS 0U /* PCD register callback disabled */
#define USE_HAL_QSPI_REGISTER_CALLBACKS 0U /* QSPI register callback disabled */
#define USE_HAL_RNG_REGISTER_CALLBACKS 0U /* RNG register callback disabled */
#define USE_HAL_RTC_REGISTER_CALLBACKS 0U /* RTC register callback disabled */
#define USE_HAL_SAI_REGISTER_CALLBACKS 0U /* SAI register callback disabled */
#define USE_HAL_SD_REGISTER_CALLBACKS 0U /* SD register callback disabled */
#define USE_HAL_SMARTCARD_REGISTER_CALLBACKS 0U /* SMARTCARD register callback disabled */
#define USE_HAL_SDRAM_REGISTER_CALLBACKS 0U /* SDRAM register callback disabled */
#define USE_HAL_SRAM_REGISTER_CALLBACKS 0U /* SRAM register callback disabled */
#define USE_HAL_SPDIFRX_REGISTER_CALLBACKS 0U /* SPDIFRX register callback disabled */
#define USE_HAL_SMBUS_REGISTER_CALLBACKS 0U /* SMBUS register callback disabled */
#define USE_HAL_SPI_REGISTER_CALLBACKS 0U /* SPI register callback disabled */
#define USE_HAL_TIM_REGISTER_CALLBACKS 0U /* TIM register callback disabled */
#define USE_HAL_UART_REGISTER_CALLBACKS 0U /* UART register callback disabled */
#define USE_HAL_USART_REGISTER_CALLBACKS 0U /* USART register callback disabled */
#define USE_HAL_WWDG_REGISTER_CALLBACKS 0U /* WWDG register callback disabled */
/* ########################## Assert Selection ############################## */
/**
* @brief Uncomment the line below to expanse the "assert_param" macro in the
* HAL drivers code
*/
/* #define USE_FULL_ASSERT 1U */
/* ################## Ethernet peripheral configuration ##################### */
/* Section 1 : Ethernet peripheral configuration */
/* MAC ADDRESS: MAC_ADDR0:MAC_ADDR1:MAC_ADDR2:MAC_ADDR3:MAC_ADDR4:MAC_ADDR5 */
#define MAC_ADDR0 2U
#define MAC_ADDR1 0U
#define MAC_ADDR2 0U
#define MAC_ADDR3 0U
#define MAC_ADDR4 0U
#define MAC_ADDR5 0U
/* Definition of the Ethernet driver buffers size and count */
#define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for receive */
#define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for transmit */
#define ETH_RXBUFNB 4U /* 4 Rx buffers of size ETH_RX_BUF_SIZE */
#define ETH_TXBUFNB 4U /* 4 Tx buffers of size ETH_TX_BUF_SIZE */
/* Section 2: PHY configuration section */
/* DP83848 PHY Address*/
#define DP83848_PHY_ADDRESS 0x01U
/* PHY Reset delay these values are based on a 1 ms Systick interrupt*/
#define PHY_RESET_DELAY 0x000000FFU
/* PHY Configuration delay */
#define PHY_CONFIG_DELAY 0x00000FFFU
#define PHY_READ_TO 0x0000FFFFU
#define PHY_WRITE_TO 0x0000FFFFU
/* Section 3: Common PHY Registers */
#define PHY_BCR ((uint16_t)0x0000) /*!< Transceiver Basic Control Register */
#define PHY_BSR ((uint16_t)0x0001) /*!< Transceiver Basic Status Register */
#define PHY_RESET ((uint16_t)0x8000) /*!< PHY Reset */
#define PHY_LOOPBACK ((uint16_t)0x4000) /*!< Select loop-back mode */
#define PHY_FULLDUPLEX_100M ((uint16_t)0x2100) /*!< Set the full-duplex mode at 100 Mb/s */
#define PHY_HALFDUPLEX_100M ((uint16_t)0x2000) /*!< Set the half-duplex mode at 100 Mb/s */
#define PHY_FULLDUPLEX_10M ((uint16_t)0x0100) /*!< Set the full-duplex mode at 10 Mb/s */
#define PHY_HALFDUPLEX_10M ((uint16_t)0x0000) /*!< Set the half-duplex mode at 10 Mb/s */
#define PHY_AUTONEGOTIATION ((uint16_t)0x1000) /*!< Enable auto-negotiation function */
#define PHY_RESTART_AUTONEGOTIATION ((uint16_t)0x0200) /*!< Restart auto-negotiation function */
#define PHY_POWERDOWN ((uint16_t)0x0800) /*!< Select the power down mode */
#define PHY_ISOLATE ((uint16_t)0x0400) /*!< Isolate PHY from MII */
#define PHY_AUTONEGO_COMPLETE ((uint16_t)0x0020) /*!< Auto-Negotiation process completed */
#define PHY_LINKED_STATUS ((uint16_t)0x0004) /*!< Valid link established */
#define PHY_JABBER_DETECTION ((uint16_t)0x0002) /*!< Jabber condition detected */
/* Section 4: Extended PHY Registers */
#define PHY_SR ((uint16_t)0x0010) /*!< PHY status register Offset */
#define PHY_MICR ((uint16_t)0x0011) /*!< MII Interrupt Control Register */
#define PHY_MISR ((uint16_t)0x0012) /*!< MII Interrupt Status and Misc. Control Register */
#define PHY_LINK_STATUS ((uint16_t)0x0001) /*!< PHY Link mask */
#define PHY_SPEED_STATUS ((uint16_t)0x0002) /*!< PHY Speed mask */
#define PHY_DUPLEX_STATUS ((uint16_t)0x0004) /*!< PHY Duplex mask */
#define PHY_MICR_INT_EN ((uint16_t)0x0002) /*!< PHY Enable interrupts */
#define PHY_MICR_INT_OE ((uint16_t)0x0001) /*!< PHY Enable output interrupt events */
#define PHY_MISR_LINK_INT_EN ((uint16_t)0x0020) /*!< Enable Interrupt on change of link status */
#define PHY_LINK_INTERRUPT ((uint16_t)0x2000) /*!< PHY link status interrupt mask */
/* ################## SPI peripheral configuration ########################## */
/* CRC FEATURE: Use to activate CRC feature inside HAL SPI Driver
* Activated: CRC code is present inside driver
* Deactivated: CRC code cleaned from driver
*/
#define USE_SPI_CRC 1U
/* Includes ------------------------------------------------------------------*/
/**
* @brief Include module's header file
*/
#ifdef HAL_RCC_MODULE_ENABLED
#include "stm32f4xx_hal_rcc.h"
#endif /* HAL_RCC_MODULE_ENABLED */
#ifdef HAL_GPIO_MODULE_ENABLED
#include "stm32f4xx_hal_gpio.h"
#endif /* HAL_GPIO_MODULE_ENABLED */
#ifdef HAL_EXTI_MODULE_ENABLED
#include "stm32f4xx_hal_exti.h"
#endif /* HAL_EXTI_MODULE_ENABLED */
#ifdef HAL_DMA_MODULE_ENABLED
#include "stm32f4xx_hal_dma.h"
#endif /* HAL_DMA_MODULE_ENABLED */
#ifdef HAL_CORTEX_MODULE_ENABLED
#include "stm32f4xx_hal_cortex.h"
#endif /* HAL_CORTEX_MODULE_ENABLED */
#ifdef HAL_ADC_MODULE_ENABLED
#include "stm32f4xx_hal_adc.h"
#endif /* HAL_ADC_MODULE_ENABLED */
#ifdef HAL_CAN_MODULE_ENABLED
#include "stm32f4xx_hal_can.h"
#endif /* HAL_CAN_MODULE_ENABLED */
#ifdef HAL_CAN_LEGACY_MODULE_ENABLED
#include "stm32f4xx_hal_can_legacy.h"
#endif /* HAL_CAN_LEGACY_MODULE_ENABLED */
#ifdef HAL_CRC_MODULE_ENABLED
#include "stm32f4xx_hal_crc.h"
#endif /* HAL_CRC_MODULE_ENABLED */
#ifdef HAL_CRYP_MODULE_ENABLED
#include "stm32f4xx_hal_cryp.h"
#endif /* HAL_CRYP_MODULE_ENABLED */
#ifdef HAL_DMA2D_MODULE_ENABLED
#include "stm32f4xx_hal_dma2d.h"
#endif /* HAL_DMA2D_MODULE_ENABLED */
#ifdef HAL_DAC_MODULE_ENABLED
#include "stm32f4xx_hal_dac.h"
#endif /* HAL_DAC_MODULE_ENABLED */
#ifdef HAL_DCMI_MODULE_ENABLED
#include "stm32f4xx_hal_dcmi.h"
#endif /* HAL_DCMI_MODULE_ENABLED */
#ifdef HAL_ETH_MODULE_ENABLED
#include "stm32f4xx_hal_eth.h"
#endif /* HAL_ETH_MODULE_ENABLED */
#ifdef HAL_FLASH_MODULE_ENABLED
#include "stm32f4xx_hal_flash.h"
#endif /* HAL_FLASH_MODULE_ENABLED */
#ifdef HAL_SRAM_MODULE_ENABLED
#include "stm32f4xx_hal_sram.h"
#endif /* HAL_SRAM_MODULE_ENABLED */
#ifdef HAL_NOR_MODULE_ENABLED
#include "stm32f4xx_hal_nor.h"
#endif /* HAL_NOR_MODULE_ENABLED */
#ifdef HAL_NAND_MODULE_ENABLED
#include "stm32f4xx_hal_nand.h"
#endif /* HAL_NAND_MODULE_ENABLED */
#ifdef HAL_PCCARD_MODULE_ENABLED
#include "stm32f4xx_hal_pccard.h"
#endif /* HAL_PCCARD_MODULE_ENABLED */
#ifdef HAL_SDRAM_MODULE_ENABLED
#include "stm32f4xx_hal_sdram.h"
#endif /* HAL_SDRAM_MODULE_ENABLED */
#ifdef HAL_HASH_MODULE_ENABLED
#include "stm32f4xx_hal_hash.h"
#endif /* HAL_HASH_MODULE_ENABLED */
#ifdef HAL_I2C_MODULE_ENABLED
#include "stm32f4xx_hal_i2c.h"
#endif /* HAL_I2C_MODULE_ENABLED */
#ifdef HAL_SMBUS_MODULE_ENABLED
#include "stm32f4xx_hal_smbus.h"
#endif /* HAL_SMBUS_MODULE_ENABLED */
#ifdef HAL_I2S_MODULE_ENABLED
#include "stm32f4xx_hal_i2s.h"
#endif /* HAL_I2S_MODULE_ENABLED */
#ifdef HAL_IWDG_MODULE_ENABLED
#include "stm32f4xx_hal_iwdg.h"
#endif /* HAL_IWDG_MODULE_ENABLED */
#ifdef HAL_LTDC_MODULE_ENABLED
#include "stm32f4xx_hal_ltdc.h"
#endif /* HAL_LTDC_MODULE_ENABLED */
#ifdef HAL_PWR_MODULE_ENABLED
#include "stm32f4xx_hal_pwr.h"
#endif /* HAL_PWR_MODULE_ENABLED */
#ifdef HAL_RNG_MODULE_ENABLED
#include "stm32f4xx_hal_rng.h"
#endif /* HAL_RNG_MODULE_ENABLED */
#ifdef HAL_RTC_MODULE_ENABLED
#include "stm32f4xx_hal_rtc.h"
#endif /* HAL_RTC_MODULE_ENABLED */
#ifdef HAL_SAI_MODULE_ENABLED
#include "stm32f4xx_hal_sai.h"
#endif /* HAL_SAI_MODULE_ENABLED */
#ifdef HAL_SD_MODULE_ENABLED
#include "stm32f4xx_hal_sd.h"
#endif /* HAL_SD_MODULE_ENABLED */
#ifdef HAL_SPI_MODULE_ENABLED
#include "stm32f4xx_hal_spi.h"
#endif /* HAL_SPI_MODULE_ENABLED */
#ifdef HAL_TIM_MODULE_ENABLED
#include "stm32f4xx_hal_tim.h"
#endif /* HAL_TIM_MODULE_ENABLED */
#ifdef HAL_UART_MODULE_ENABLED
#include "stm32f4xx_hal_uart.h"
#endif /* HAL_UART_MODULE_ENABLED */
#ifdef HAL_USART_MODULE_ENABLED
#include "stm32f4xx_hal_usart.h"
#endif /* HAL_USART_MODULE_ENABLED */
#ifdef HAL_IRDA_MODULE_ENABLED
#include "stm32f4xx_hal_irda.h"
#endif /* HAL_IRDA_MODULE_ENABLED */
#ifdef HAL_SMARTCARD_MODULE_ENABLED
#include "stm32f4xx_hal_smartcard.h"
#endif /* HAL_SMARTCARD_MODULE_ENABLED */
#ifdef HAL_WWDG_MODULE_ENABLED
#include "stm32f4xx_hal_wwdg.h"
#endif /* HAL_WWDG_MODULE_ENABLED */
#ifdef HAL_PCD_MODULE_ENABLED
#include "stm32f4xx_hal_pcd.h"
#endif /* HAL_PCD_MODULE_ENABLED */
#ifdef HAL_HCD_MODULE_ENABLED
#include "stm32f4xx_hal_hcd.h"
#endif /* HAL_HCD_MODULE_ENABLED */
#ifdef HAL_DSI_MODULE_ENABLED
#include "stm32f4xx_hal_dsi.h"
#endif /* HAL_DSI_MODULE_ENABLED */
#ifdef HAL_QSPI_MODULE_ENABLED
#include "stm32f4xx_hal_qspi.h"
#endif /* HAL_QSPI_MODULE_ENABLED */
#ifdef HAL_CEC_MODULE_ENABLED
#include "stm32f4xx_hal_cec.h"
#endif /* HAL_CEC_MODULE_ENABLED */
#ifdef HAL_FMPI2C_MODULE_ENABLED
#include "stm32f4xx_hal_fmpi2c.h"
#endif /* HAL_FMPI2C_MODULE_ENABLED */
#ifdef HAL_FMPSMBUS_MODULE_ENABLED
#include "stm32f4xx_hal_fmpsmbus.h"
#endif /* HAL_FMPSMBUS_MODULE_ENABLED */
#ifdef HAL_SPDIFRX_MODULE_ENABLED
#include "stm32f4xx_hal_spdifrx.h"
#endif /* HAL_SPDIFRX_MODULE_ENABLED */
#ifdef HAL_DFSDM_MODULE_ENABLED
#include "stm32f4xx_hal_dfsdm.h"
#endif /* HAL_DFSDM_MODULE_ENABLED */
#ifdef HAL_LPTIM_MODULE_ENABLED
#include "stm32f4xx_hal_lptim.h"
#endif /* HAL_LPTIM_MODULE_ENABLED */
#ifdef HAL_MMC_MODULE_ENABLED
#include "stm32f4xx_hal_mmc.h"
#endif /* HAL_MMC_MODULE_ENABLED */
/* Exported macro ------------------------------------------------------------*/
#ifdef USE_FULL_ASSERT
/**
* @brief The assert_param macro is used for function's parameters check.
* @param expr If expr is false, it calls assert_failed function
* which reports the name of the source file and the source
* line number of the call that failed.
* If expr is true, it returns no value.
* @retval None
*/
#define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
void assert_failed(uint8_t* file, uint32_t line);
#else
#define assert_param(expr) ((void)0U)
#endif /* USE_FULL_ASSERT */
#ifdef __cplusplus
}
#endif
#endif /* __STM32F4xx_HAL_CONF_H */
@@ -0,0 +1,47 @@
/**
******************************************************************************
* @file Templates/Inc/stm32f4xx_it.h
* @author MCD Application Team
* @brief This file contains the headers of the interrupt handlers.
******************************************************************************
* @attention
*
* Copyright (c) 2017 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F4xx_IT_H
#define __STM32F4xx_IT_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
/* Exported functions ------------------------------------------------------- */
void NMI_Handler(void);
void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void SVC_Handler(void);
void DebugMon_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);
#ifdef __cplusplus
}
#endif
#endif /* __STM32F4xx_IT_H */
@@ -35,6 +35,7 @@ extern "C" {
extern TIM_HandleTypeDef htim1;
extern TIM_HandleTypeDef htim2;
extern TIM_HandleTypeDef htim4;
/* USER CODE BEGIN Private defines */
@@ -42,6 +43,7 @@ extern TIM_HandleTypeDef htim2;
void MX_TIM1_Init(void);
void MX_TIM2_Init(void);
void MX_TIM4_Init(void);
/* USER CODE BEGIN Prototypes */
@@ -33,12 +33,14 @@ extern "C" {
/* USER CODE END Includes */
extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart2;
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
void MX_USART1_UART_Init(void);
void MX_USART2_UART_Init(void);
/* USER CODE BEGIN Prototypes */
@@ -0,0 +1,67 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file dma.c
* @brief This file provides code for the configuration
* of all the requested memory to memory DMA transfers.
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "dma.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/*----------------------------------------------------------------------------*/
/* Configure DMA */
/*----------------------------------------------------------------------------*/
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
DMA_HandleTypeDef hdma_memtomem_dma1_channel1;
/**
* Enable DMA controller clock
* Configure DMA for memory to memory transfers
* hdma_memtomem_dma1_channel1
*/
void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* Configure DMA request hdma_memtomem_dma1_channel1 on DMA1_Channel1 */
hdma_memtomem_dma1_channel1.Instance = DMA1_Channel1;
hdma_memtomem_dma1_channel1.Init.Direction = DMA_MEMORY_TO_MEMORY;
hdma_memtomem_dma1_channel1.Init.PeriphInc = DMA_PINC_ENABLE;
hdma_memtomem_dma1_channel1.Init.MemInc = DMA_MINC_ENABLE;
hdma_memtomem_dma1_channel1.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_memtomem_dma1_channel1.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_memtomem_dma1_channel1.Init.Mode = DMA_NORMAL;
hdma_memtomem_dma1_channel1.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_memtomem_dma1_channel1) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
@@ -25,7 +25,6 @@
/* USER CODE END 0 */
/*----------------------------------------------------------------------------*/
/* Configure GPIO */
/*----------------------------------------------------------------------------*/
@@ -52,11 +51,10 @@ void MX_GPIO_Init(void)
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1|GPIO_PIN_4
|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8
|GPIO_PIN_9|Relay_dc5v_Pin|One_wire_Pin, GPIO_PIN_RESET);
@@ -66,9 +64,9 @@ void MX_GPIO_Init(void)
|GPIO_PIN_15, GPIO_PIN_RESET);
/*Configure GPIO pin : PC13 */
GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14;
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
@@ -78,10 +76,10 @@ void MX_GPIO_Init(void)
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PA1 PA2 PA3 PA4
/*Configure GPIO pins : PA1 PA4
PA5 PA6 PA7 PA8
PA9 Relay_dc5v_Pin One_wire_Pin */
GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4
GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_4
|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8
|GPIO_PIN_9|Relay_dc5v_Pin|One_wire_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
@@ -0,0 +1,763 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "can.h"
#include "i2c.h"
#include "rtc.h"
#include "spi.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "dallas_tools.h"
#include "def.h"
#include <stdio.h>
#include "modbus.h"
#include "eeprom_emul.h"
#include "stdio.h"
#include "flash_ring.h"
#include "string.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
uint16_t iter, cnt = 5;
uint8_t init_retries = 5;
uint8_t ralay_5v_on_var = 0;
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
float temperature;
uint8_t roms[MAX_DEVICES][8];
Flags_TypeDef flag;
//extern uint8_t devices_found ;
uint8_t _debug_init = 0;
TEMP_TypeDef temp_sense[30];
float set_temp_old[30];
char rx_buffer[64];
uint8_t rx_index = 0;
char command_ready = 0;
uint8_t uart_byte = 0;
uint8_t first_in = 1;
DALLAS_SensorHandleTypeDef sens[30];
int init_sens = 0;
FlashRecord_t* record;
uint8_t flash_buff[RECORD_SIZE - 4];
RS_HandleTypeDef hmodbus_master;
RS_MsgTypeDef MODBUS_MASTER_MSG;
static uint8_t hmodbus_master_buffer[MSG_SIZE_MAX];
static uint8_t rtc_modbus_hold_initialized = 0U;
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
int last_page_addr = LAST_PAGE_ADDR;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
static void RTC_ModbusService(MB_DataStructureTypeDef* mb_data);
static void RTC_ReadCalendarFromRegs(const MB_RtcCalendarRegsTypeDef* regs, RTC_CalendarTypeDef* calendar);
static void RTC_WriteCalendarToRegs(MB_RtcCalendarRegsTypeDef* regs, const RTC_CalendarTypeDef* calendar, uint16_t status);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
HAL_StatusTypeDef ModbusMaster_Request(RS_MsgTypeDef *request,
void (*callback)(RS_HandleTypeDef *, RS_MsgTypeDef *))
{
return MODBUS_MasterRequest(&hmodbus_master, request, callback);
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM1_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
MX_TIM2_Init();
MX_TIM4_Init();
MX_ADC1_Init();
MX_CAN_Init();
MX_I2C1_Init();
MX_RTC_Init();
MX_SPI1_Init();
/* USER CODE BEGIN 2 */
led_blink(GPIOC, 13, rest_iter, reset_blink_delay);
MODBUS_FirstInit(&hmodbus1, &mb_huart, &mb_htim);
MODBUS_Config(&hmodbus1, MODBUS_DEVICE_ID, MODBUS_TIMEOUT, MODBUS_MODE_SLAVE);
// Запуск приема Modbus
MODBUS_SlaveStart(&hmodbus1, NULL);
if (MODBUS_FirstInit(&hmodbus_master, &huart2, &htim4) != HAL_OK)
{
Error_Handler();
}
hmodbus_master.pBufferPtr = hmodbus_master_buffer;
if (MODBUS_Config(&hmodbus_master, 0, MODBUS_TIMEOUT, MODBUS_MODE_MASTER) != HAL_OK)
{
Error_Handler();
}
uint8_t uart_byte = 0;
Dallas_BusFirstInit(&htim1);
// ������������� �� ������� ��������� ��������
// ������������� �� ������� (����������� ������ ���������� �������)
reinit_t_sens();
init_setpoint_all_T_sense(temp_sense, hdallas.onewire->RomCnt);
MB_DATA.InRegs.num_Tsens = hdallas.onewire->RomCnt;
RTC_ModbusService(&MB_DATA);
// BufferState_t buffer_state = buffer_init();
for(int i=0;i<RECORD_SIZE;i++)
{
flash_buff[i]=i;
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// if (MB_DATA.Coils.relay_struct[0].state_val_bit.Temp11_relay_isOn)
// {
// MB_DATA.Coils.relay_struct[0].state_val_bit.Temp11_relay_isOn = 0;
// BufferState_t state = buffer_init();
// uint32_t idx = (state.write_index ) % RECORDS_PER_PAGE;
// FlashRecord_t* record = buffer_read_record(idx);
// }
// if (MB_DATA.Coils.relay_struct[0].state_val_bit.Temp10_relay_isOn)
// {
// MB_DATA.Coils.relay_struct[0].state_val_bit.Temp10_relay_isOn = 0;
// FlashRecord_t new_record;
// new_record.timestamp = HAL_GetTick();
// memcpy(new_record.data, flash_buff, sizeof(new_record.data));
// HAL_StatusTypeDef status = buffer_write_record(&new_record, &buffer_state);
// if (status == HAL_OK)
// {
// // printf("Record written successfully\n");
// GPIOC->ODR |= 1 << 13;
// }
// }
temp_sense[0].t_close = 1;
Field_modbus(&MB_DATA, &flag);
Check_Tconnect(&MB_DATA, &flag, &hdallas, 0);
value_control();
init_setpoint_all_T_sense(temp_sense, hdallas.onewire->RomCnt);
// handle_valves(temp_sense[]);
Dallas_StartConvertTAll(&hdallas, DALLAS_WAIT_BUS, 0);
for(int i = 0; i < hdallas.onewire->RomCnt; i++)
{
if(sens[i].isLost)
{
sens[i].lost_cnt ++;
}
Dallas_ReadTemperature(&sens[i]);
MB_DATA.InRegs.sens_Temp[i] = sens[i].temperature * 10;
/////////////////////////заменить на define ralay_5v_on_var GPIOA->ODR|=1<<10;
ralay_5v_on_var = MB_DATA.Coils.coils[1].state_val_bit.state_val_05;
if (ralay_5v_on_var)
{
GPIOA->ODR |= 1 << 10;
}
else
{
GPIOA->ODR &= ~(1 << 10);
}
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
//iwdg_refresh();
//HAL_Delay(200);
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_ADC;
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
static void RTC_ReadCalendarFromRegs(const MB_RtcCalendarRegsTypeDef* regs, RTC_CalendarTypeDef* calendar)
{
if ((regs == NULL) || (calendar == NULL))
{
return;
}
calendar->hours = regs->hours;
calendar->minutes = regs->minutes;
calendar->seconds = regs->seconds;
calendar->date = regs->date;
calendar->month = regs->month;
calendar->year = regs->year;
calendar->weekday = regs->weekday;
}
static void RTC_WriteCalendarToRegs(MB_RtcCalendarRegsTypeDef* regs, const RTC_CalendarTypeDef* calendar, uint16_t status)
{
if ((regs == NULL) || (calendar == NULL))
{
return;
}
regs->hours = calendar->hours;
regs->minutes = calendar->minutes;
regs->seconds = calendar->seconds;
regs->date = calendar->date;
regs->month = calendar->month;
regs->year = calendar->year;
regs->weekday = calendar->weekday;
regs->apply = 0U;
regs->status = status;
}
static void RTC_ModbusService(MB_DataStructureTypeDef* mb_data)
{
RTC_CalendarTypeDef calendar = {0};
uint16_t hold_status = MB_RTC_STATUS_IDLE;
uint8_t copy_current_to_holding = 0U;
if (mb_data == NULL)
{
return;
}
hold_status = mb_data->HoldRegs.rtc.status;
if (mb_data->HoldRegs.rtc.apply == MB_RTC_APPLY_SET)
{
RTC_ReadCalendarFromRegs(&mb_data->HoldRegs.rtc, &calendar);
mb_data->HoldRegs.rtc.apply = 0U;
if (RTC_CalendarIsValid(&calendar) == 0U)
{
hold_status = MB_RTC_STATUS_VALUE_ERROR;
}
else if (RTC_CalendarSet(&calendar) != HAL_OK)
{
hold_status = MB_RTC_STATUS_HAL_ERROR;
}
else
{
hold_status = MB_RTC_STATUS_SET_OK;
copy_current_to_holding = 1U;
}
}
if (RTC_CalendarGet(&calendar) != HAL_OK)
{
mb_data->InRegs.rtc.status = MB_RTC_STATUS_HAL_ERROR;
mb_data->HoldRegs.rtc.status = MB_RTC_STATUS_HAL_ERROR;
return;
}
RTC_WriteCalendarToRegs(&mb_data->InRegs.rtc, &calendar, MB_RTC_STATUS_IDLE);
if ((rtc_modbus_hold_initialized == 0U) || (copy_current_to_holding != 0U))
{
RTC_WriteCalendarToRegs(&mb_data->HoldRegs.rtc, &calendar, hold_status);
rtc_modbus_hold_initialized = 1U;
}
else
{
mb_data->HoldRegs.rtc.status = hold_status;
}
}
void iwdg_refresh(void)
{
IWDG->KR = 0xAAAA; // �������� ������
}
void led_blink(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, uint8_t iter, uint16_t delay)
{
for(int i = 0; i < iter; i++)
{
GPIOx->ODR ^= (1 << GPIO_Pin);
HAL_Delay(delay);
}
}
void Check_Tconnect(MB_DataStructureTypeDef* MB_DATA, Flags_TypeDef* flag, DALLAS_HandleTypeDef* hdallas, int a[0])
{
for(int i = 0; i < hdallas->onewire->RomCnt; i++)
{
if(sens[i].isLost)
{
//init_sens=1;
}
}
if (init_sens || flag->init_tsens)
{
init_sens = 0;
flag->init_tsens = 0;
//Dallas_BusFirstInit(&htim1);
DS18B20_Search(&DS, &OW) ;
reinit_t_sens();
MB_DATA->InRegs.num_Tsens = hdallas->onewire->RomCnt;
}
}
void reinit_t_sens(void)
{
for ( int i = 0; i < hdallas.onewire->RomCnt; i++)
{
// ������������� �� ROM-������
//sens[i].Init.init_func = &Dallas_SensorInitByROM;
// sens[i].Init.InitParam.ROM = rom_address;
sens[i].Init.InitParam.Ind = i;
sens[i].Init.init_func = &Dallas_SensorInitByInd;
sens[i].Init.Resolution = DALLAS_CONFIG_9_BITS; sens[i].set_temp = 20.;
sens[i].hyst = 1;
MB_DATA.HoldRegs.set_Temp[i] = (uint16_t)(sens[i].set_temp * MB_ROOM_TEMP_SCALE);
MB_DATA.HoldRegs.set_hyst[i] = (uint16_t)(sens[i].hyst * MB_ROOM_TEMP_SCALE);
MB_DATA.HoldRegs.room_cfg[i].setpoint_x10 = MB_DATA.HoldRegs.set_Temp[i];
MB_DATA.HoldRegs.room_cfg[i].hysteresis_x10 = MB_DATA.HoldRegs.set_hyst[i];
MB_DATA.HoldRegs.room_cfg[i].valve_position_pct = 0;
MB_DATA.HoldRegs.room_cfg[i].valve_angle_max_deg = MB_ROOM_VALVE_ANGLE_MAX_DEFAULT;
MB_DATA.HoldRegs.room_cfg[i].mode = ROOM_MODE_AUTO;
MB_DATA.HoldRegs.room_cfg[i].command = ROOM_COMMAND_STOP;
MB_DATA.HoldRegs.room_cfg[i].location = 1;
MB_DATA.HoldRegs.room_cfg[i].apply = 0;
Dallas_AddNewSensors(&hdallas, &sens[i]);
}
}
FuncStat packStruct(MB_DataStructureTypeDef* MB_DATA, int sizeARR)
{
for(int i = 0; i < sizeARR; i++)
{
for(int sens_num = 0; sens_num < hdallas.onewire->RomCnt; sens_num++)
{
switch(sens_num)
{
case 0:
MB_DATA->Coils.status_tSens[i].state_val_bit.Temp1_isConnected = sens[i * 16 + sens_num ].isConnected;
break;
case 1:
MB_DATA->Coils.status_tSens[i].state_val_bit.Temp2_isConnected = sens[i * 16 + sens_num ].isConnected;
break;
case 2:
MB_DATA->Coils.status_tSens[i].state_val_bit.Temp3_isConnected = sens[i * 16 + sens_num ].isConnected;
break;
case 3:
MB_DATA->Coils.status_tSens[i].state_val_bit.Temp4_isConnected = sens[i * 16 + sens_num ].isConnected;
break;
case 4:
MB_DATA->Coils.status_tSens[i].state_val_bit.Temp5_isConnected = sens[i * 16 + sens_num ].isConnected;
break;
case 5:
MB_DATA->Coils.status_tSens[i].state_val_bit.Temp6_isConnected = sens[i * 16 + sens_num ].isConnected;
break;
case 6:
MB_DATA->Coils.status_tSens[i].state_val_bit.Temp7_isConnected = sens[i * 16 + sens_num ].isConnected;
break;
case 7:
MB_DATA->Coils.status_tSens[i].state_val_bit.Temp8_isConnected = sens[i * 16 + sens_num ].isConnected;
break;
case 8:
MB_DATA->Coils.status_tSens[i].state_val_bit.Temp9_isConnected = sens[i * 16 + sens_num ].isConnected;
break;
case 9:
MB_DATA->Coils.status_tSens[i].state_val_bit.Temp10_isConnected = sens[i * 16 + sens_num ].isConnected;
break;
case 10:
MB_DATA->Coils.status_tSens[i].state_val_bit.Temp11_isConnected = sens[i * 16 + sens_num ].isConnected;
break;
case 11:
MB_DATA->Coils.status_tSens[i].state_val_bit.Temp12_isConnected = sens[i * 16 + sens_num ].isConnected;
break;
case 12:
MB_DATA->Coils.status_tSens[i].state_val_bit.Temp13_isConnected = sens[i * 16 + sens_num ].isConnected;
break;
case 13:
MB_DATA->Coils.status_tSens[i].state_val_bit.Temp14_isConnected = sens[i * 16 + sens_num ].isConnected;
break;
case 14:
MB_DATA->Coils.status_tSens[i].state_val_bit.Temp15_isConnected = sens[i * 16 + sens_num ].isConnected;
break;
case 15:
MB_DATA->Coils.status_tSens[i].state_val_bit.Temp16_isConnected = sens[i * 16 + sens_num ].isConnected;
break;
}
}
}
return FuncOK;
}
static uint16_t clamp_room_percent(uint16_t value)
{
return value > 100U ? 100U : value;
}
static void update_room_modbus(MB_DataStructureTypeDef* MB_DATA)
{
for (int i = 0; i < MAX_SENSE; i++)
{
MB_RoomInputRegsTypeDef* room = &MB_DATA->InRegs.room[i];
MB_RoomHoldingRegsTypeDef* cfg = &MB_DATA->HoldRegs.room_cfg[i];
uint16_t open_state = 0U;
uint16_t close_state = 0U;
uint16_t position_pct;
uint16_t angle_max;
if (cfg->valve_angle_max_deg == 0U)
{
cfg->valve_angle_max_deg = MB_ROOM_VALVE_ANGLE_MAX_DEFAULT;
}
if (i < 16)
{
open_state = (MB_DATA->Coils.relay_struct_on.all >> i) & 0x1U;
close_state = (MB_DATA->Coils.relay_struct_off.all >> i) & 0x1U;
}
position_pct = clamp_room_percent(cfg->valve_position_pct);
if (cfg->mode == ROOM_MODE_AUTO)
{
if (open_state)
{
position_pct = 100U;
}
else if (close_state)
{
position_pct = 0U;
}
}
cfg->valve_position_pct = position_pct;
angle_max = cfg->valve_angle_max_deg;
room->channel = (uint16_t)(i + 1);
room->location = cfg->location;
for (int reg = 0; reg < 4; reg++)
{
room->ds18b20_id[reg] = ((uint16_t)MB_DATA->InRegs.ID.DevAddr[i][reg * 2]) |
((uint16_t)MB_DATA->InRegs.ID.DevAddr[i][reg * 2 + 1] << 8);
}
room->temperature_x10 = (i < hdallas.onewire->RomCnt) ? (uint16_t)((int16_t)(sens[i].temperature * MB_ROOM_TEMP_SCALE)) : 0U;
room->setpoint_x10 = MB_DATA->HoldRegs.set_Temp[i];
room->hysteresis_x10 = MB_DATA->HoldRegs.set_hyst[i];
room->valve_position_pct = position_pct;
room->valve_angle_deg = (uint16_t)((position_pct * angle_max) / 100U);
room->valve_angle_max_deg = angle_max;
room->is_connected = (i < hdallas.onewire->RomCnt) ? (uint16_t)sens[i].isConnected : 0U;
room->valve_open = open_state;
room->valve_close = close_state;
room->mode = cfg->mode;
room->command_state = cfg->command;
room->reserved = 0U;
}
}
FuncStat Field_modbus(MB_DataStructureTypeDef* MB_DATA, Flags_TypeDef* flag)
{
MB_DATA->InRegs.ID = *hdallas.ds_devices;
RTC_ModbusService(MB_DATA);
flag->init_tsens = MB_DATA->Coils.init_Tsens;
packStruct(MB_DATA, MAX_SENSE / 16);
if (_debug_init || MB_DATA->Coils.init_param)
{
_debug_init = 0;
MB_DATA->Coils.init_param = 0;
for(int i = 0; i < hdallas.onewire->RomCnt; i++)
{ if (MB_DATA->HoldRegs.room_cfg[i].apply)
{
MB_DATA->HoldRegs.set_Temp[i] = MB_DATA->HoldRegs.room_cfg[i].setpoint_x10;
MB_DATA->HoldRegs.set_hyst[i] = MB_DATA->HoldRegs.room_cfg[i].hysteresis_x10;
MB_DATA->HoldRegs.room_cfg[i].apply = 0U;
}
MB_DATA->HoldRegs.room_cfg[i].setpoint_x10 = MB_DATA->HoldRegs.set_Temp[i];
MB_DATA->HoldRegs.room_cfg[i].hysteresis_x10 = MB_DATA->HoldRegs.set_hyst[i];
sens[i].set_temp = ((float)MB_DATA->HoldRegs.set_Temp[i]) / MB_ROOM_TEMP_SCALE;
sens[i].hyst = ((float)MB_DATA->HoldRegs.set_hyst[i]) / MB_ROOM_TEMP_SCALE;
}
}
update_room_modbus(MB_DATA);
return FuncOK;
};
FuncStat value_control(void )
{
for(int i = 0; i < hdallas.onewire->RomCnt; i++)
{
if (i < 16 && MB_DATA.HoldRegs.room_cfg[i].mode == ROOM_MODE_MANUAL)
{
uint16_t manual_pct = clamp_room_percent(MB_DATA.HoldRegs.room_cfg[i].valve_position_pct);
if (MB_DATA.HoldRegs.room_cfg[i].command == ROOM_COMMAND_CLOSE)
{
manual_pct = 0U;
}
else if (MB_DATA.HoldRegs.room_cfg[i].command == ROOM_COMMAND_OPEN && manual_pct == 0U)
{
manual_pct = 100U;
}
MB_DATA.HoldRegs.room_cfg[i].valve_position_pct = manual_pct;
if (manual_pct > 0U)
{
MB_DATA.Coils.relay_struct_off.all &= ~(1 << i);
MB_DATA.Coils.relay_struct_on.all |= 1 << i;
}
else
{
MB_DATA.Coils.relay_struct_on.all &= ~(1 << i);
MB_DATA.Coils.relay_struct_off.all |= 1 << i;
}
continue;
}
if (sens[i].temperature < sens[i].set_temp - sens[i].hyst)
{
MB_DATA.Coils.relay_struct_off.all |= 1 << i;
MB_DATA.Coils.relay_struct_on.all &= ~(1 << i);
}
else
if (sens[i].temperature > sens[i].set_temp + sens[i].hyst)
{
MB_DATA.Coils.relay_struct_off.all &= ~(1 << i);
MB_DATA.Coils.relay_struct_on.all |= 1 << i;
}
else
if (sens[i].temperature == sens[i].set_temp )
{
MB_DATA.Coils.relay_struct_on.all &= ~(1 << i);
MB_DATA.Coils.relay_struct_off.all &= ~(1 << i);
}
}
return FuncOK;
}
uint16_t handle_valves(TEMP_TypeDef* temp_sense[MAX_SENSE] )
{
if (temp_sense[0]->state == STATE_OPEN_VALVE)
{
GPIOC->ODR |= 1 << 14;
}
else if (temp_sense[0]->state == STATE_CLOSE_VALVE)
{
GPIOC->ODR &= ~(1 << 14);
}
return 1;
}
void init_setpoint_all_T_sense(TEMP_TypeDef* temp_sense, int size_array)
{
//ds_search_devices();
for(int i = 0; i < size_array ; i++)
{
temp_sense[i].id[0] = roms[i][0] << 0 | roms[i][1] << 8 | roms[i][2] << 16 | roms[i][3] << 24;
temp_sense[i].id[1] = roms[i][4] << 0 | roms[i][5] << 8 | roms[i][6] << 16 | roms[i][7] << 24;
temp_sense[i].count = i + 1;
temp_sense[i].location = 1;
temp_sense[i].t_open = 22;
temp_sense[i].t_close = 18;
temp_sense[i].status_T_sense = 1;
}
}
/* USER CODE END 4 */
/**
* @brief Period elapsed callback in non blocking mode
* @note This function is called when TIM3 interrupt took place, inside
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
* a global variable "uwTick" used as application time base.
* @param htim : TIM handle
* @retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM3)
{
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
/* USER CODE END Callback 1 */
}
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
@@ -0,0 +1,333 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file rtc.c
* @brief This file provides code for the configuration
* of the RTC instances.
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "rtc.h"
/* USER CODE BEGIN 0 */
#define RTC_BKP_CONFIGURED_MARKER 0x32A5U
#define RTC_BKP_MARKER_REG RTC_BKP_DR1
#define RTC_BKP_YEAR_MONTH_REG RTC_BKP_DR2
#define RTC_BKP_DATE_WEEKDAY_REG RTC_BKP_DR3
static uint8_t RTC_NormalizeYear(uint16_t year, uint8_t *normalized_year)
{
if (year <= 99U)
{
*normalized_year = (uint8_t)year;
return 1U;
}
if ((year >= 2000U) && (year <= 2099U))
{
*normalized_year = (uint8_t)(year - 2000U);
return 1U;
}
return 0U;
}
static uint8_t RTC_IsLeapYearFull(uint16_t year)
{
return (uint8_t)(((year % 4U) == 0U) && (((year % 100U) != 0U) || ((year % 400U) == 0U)));
}
static uint8_t RTC_DaysInMonth(uint16_t year, uint8_t month)
{
static const uint8_t days_in_month[] = { 31U, 28U, 31U, 30U, 31U, 30U, 31U, 31U, 30U, 31U, 30U, 31U };
if ((month == 0U) || (month > 12U))
{
return 0U;
}
if ((month == 2U) && RTC_IsLeapYearFull(year))
{
return 29U;
}
return days_in_month[month - 1U];
}
static uint8_t RTC_IsDateValid(uint16_t year, uint8_t month, uint8_t date)
{
uint8_t normalized_year = 0U;
uint16_t full_year = year;
uint8_t max_day = 0U;
if (RTC_NormalizeYear(year, &normalized_year) == 0U)
{
return 0U;
}
if (year <= 99U)
{
full_year = 2000U + year;
}
max_day = RTC_DaysInMonth(full_year, month);
return (uint8_t)((date >= 1U) && (date <= max_day));
}
static void RTC_SaveDateToBackup(const RTC_DateTypeDef *date)
{
uint32_t year_month = (((uint32_t)date->Year) << 8U) | date->Month;
uint32_t date_weekday = (((uint32_t)date->Date) << 8U) | date->WeekDay;
if ((HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_YEAR_MONTH_REG) != year_month) ||
(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DATE_WEEKDAY_REG) != date_weekday))
{
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_YEAR_MONTH_REG, year_month);
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DATE_WEEKDAY_REG, date_weekday);
}
if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_MARKER_REG) != RTC_BKP_CONFIGURED_MARKER)
{
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_MARKER_REG, RTC_BKP_CONFIGURED_MARKER);
}
}
static uint8_t RTC_RestoreDateFromBackup(void)
{
RTC_DateTypeDef restored_date = {0};
uint32_t year_month = 0U;
uint32_t date_weekday = 0U;
if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_MARKER_REG) != RTC_BKP_CONFIGURED_MARKER)
{
return 0U;
}
year_month = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_YEAR_MONTH_REG);
date_weekday = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DATE_WEEKDAY_REG);
restored_date.Year = (uint8_t)((year_month >> 8U) & 0xFFU);
restored_date.Month = (uint8_t)(year_month & 0xFFU);
restored_date.Date = (uint8_t)((date_weekday >> 8U) & 0xFFU);
restored_date.WeekDay = (uint8_t)(date_weekday & 0xFFU);
if (RTC_IsDateValid(restored_date.Year, restored_date.Month, restored_date.Date) == 0U)
{
return 0U;
}
if (restored_date.WeekDay > RTC_WEEKDAY_SATURDAY)
{
restored_date.WeekDay = RTC_WEEKDAY_MONDAY;
}
hrtc.DateToUpdate = restored_date;
return 1U;
}
/* USER CODE END 0 */
RTC_HandleTypeDef hrtc;
/* RTC init function */
void MX_RTC_Init(void)
{
/* USER CODE BEGIN RTC_Init 0 */
/* USER CODE END RTC_Init 0 */
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef DateToUpdate = {0};
/* USER CODE BEGIN RTC_Init 1 */
uint8_t rtc_backup_ready = 0U;
/* USER CODE END RTC_Init 1 */
/** Initialize RTC Only
*/
hrtc.Instance = RTC;
hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
hrtc.Init.OutPut = RTC_OUTPUTSOURCE_SECOND;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN Check_RTC_BKUP */
rtc_backup_ready = RTC_RestoreDateFromBackup();
/* USER CODE END Check_RTC_BKUP */
/** Initialize RTC and set the Time and Date
*/
if (rtc_backup_ready == 0U)
{
sTime.Hours = 0U;
sTime.Minutes = 0U;
sTime.Seconds = 0U;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
DateToUpdate.WeekDay = RTC_WEEKDAY_MONDAY;
DateToUpdate.Month = RTC_MONTH_JANUARY;
DateToUpdate.Date = 1U;
DateToUpdate.Year = 0U;
if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK)
{
Error_Handler();
}
RTC_SaveDateToBackup(&DateToUpdate);
}
/* USER CODE BEGIN RTC_Init 2 */
/* USER CODE END RTC_Init 2 */
}
void HAL_RTC_MspInit(RTC_HandleTypeDef* rtcHandle)
{
if(rtcHandle->Instance==RTC)
{
/* USER CODE BEGIN RTC_MspInit 0 */
/* USER CODE END RTC_MspInit 0 */
HAL_PWR_EnableBkUpAccess();
/* Enable BKP CLK enable for backup registers */
__HAL_RCC_BKP_CLK_ENABLE();
/* RTC clock enable */
__HAL_RCC_RTC_ENABLE();
/* RTC interrupt Init */
HAL_NVIC_SetPriority(RTC_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(RTC_IRQn);
/* USER CODE BEGIN RTC_MspInit 1 */
/* USER CODE END RTC_MspInit 1 */
}
}
void HAL_RTC_MspDeInit(RTC_HandleTypeDef* rtcHandle)
{
if(rtcHandle->Instance==RTC)
{
/* USER CODE BEGIN RTC_MspDeInit 0 */
/* USER CODE END RTC_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_RTC_DISABLE();
/* RTC interrupt Deinit */
HAL_NVIC_DisableIRQ(RTC_IRQn);
/* USER CODE BEGIN RTC_MspDeInit 1 */
/* USER CODE END RTC_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
uint8_t RTC_CalendarIsValid(const RTC_CalendarTypeDef *calendar)
{
if (calendar == NULL)
{
return 0U;
}
return (uint8_t)((calendar->hours <= 23U) &&
(calendar->minutes <= 59U) &&
(calendar->seconds <= 59U) &&
RTC_IsDateValid(calendar->year, calendar->month, calendar->date));
}
HAL_StatusTypeDef RTC_CalendarGet(RTC_CalendarTypeDef *calendar)
{
RTC_TimeTypeDef time = {0};
RTC_DateTypeDef date = {0};
if (calendar == NULL)
{
return HAL_ERROR;
}
if (HAL_RTC_GetDate(&hrtc, &date, RTC_FORMAT_BIN) != HAL_OK)
{
return HAL_ERROR;
}
if (HAL_RTC_GetTime(&hrtc, &time, RTC_FORMAT_BIN) != HAL_OK)
{
return HAL_ERROR;
}
RTC_SaveDateToBackup(&date);
calendar->hours = time.Hours;
calendar->minutes = time.Minutes;
calendar->seconds = time.Seconds;
calendar->date = date.Date;
calendar->month = date.Month;
calendar->year = 2000U + date.Year;
calendar->weekday = date.WeekDay;
return HAL_OK;
}
HAL_StatusTypeDef RTC_CalendarSet(const RTC_CalendarTypeDef *calendar)
{
RTC_TimeTypeDef time = {0};
RTC_DateTypeDef date = {0};
uint8_t normalized_year = 0U;
if ((calendar == NULL) || (RTC_CalendarIsValid(calendar) == 0U))
{
return HAL_ERROR;
}
if (RTC_NormalizeYear(calendar->year, &normalized_year) == 0U)
{
return HAL_ERROR;
}
time.Hours = (uint8_t)calendar->hours;
time.Minutes = (uint8_t)calendar->minutes;
time.Seconds = (uint8_t)calendar->seconds;
date.Year = normalized_year;
date.Month = (uint8_t)calendar->month;
date.Date = (uint8_t)calendar->date;
date.WeekDay = RTC_WEEKDAY_MONDAY;
if (HAL_RTC_SetTime(&hrtc, &time, RTC_FORMAT_BIN) != HAL_OK)
{
return HAL_ERROR;
}
if (HAL_RTC_SetDate(&hrtc, &date, RTC_FORMAT_BIN) != HAL_OK)
{
return HAL_ERROR;
}
RTC_SaveDateToBackup(&date);
return HAL_OK;
}
/* USER CODE END 1 */
@@ -0,0 +1,86 @@
/**
******************************************************************************
* @file Templates/Src/stm32f4xx_hal_msp.c
* @author MCD Application Team
* @brief HAL MSP module.
*
@verbatim
===============================================================================
##### How to use this driver #####
===============================================================================
[..]
This file is generated automatically by STM32CubeMX and eventually modified
by the user
@endverbatim
******************************************************************************
* @attention
*
* Copyright (c) 2017 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
/** @addtogroup STM32F4xx_HAL_Driver
* @{
*/
/** @defgroup HAL_MSP
* @brief HAL MSP module.
* @{
*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/** @defgroup HAL_MSP_Private_Functions
* @{
*/
/**
* @brief Initializes the Global MSP.
* @param None
* @retval None
*/
void HAL_MspInit(void)
{
/* NOTE : This function is generated automatically by STM32CubeMX and eventually
modified by the user
*/
}
/**
* @brief DeInitializes the Global MSP.
* @param None
* @retval None
*/
void HAL_MspDeInit(void)
{
/* NOTE : This function is generated automatically by STM32CubeMX and eventually
modified by the user
*/
}
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
@@ -0,0 +1,177 @@
/**
******************************************************************************
* @file stm32f4xx_hal_timebase_tim_template.c
* @author MCD Application Team
* @brief HAL time base based on the hardware TIM Template.
*
* This file overrides the native HAL time base functions (defined as weak)
* the TIM time base:
* + Intializes the TIM peripheral generate a Period elapsed Event each 1ms
* + HAL_IncTick is called inside HAL_TIM_PeriodElapsedCallback ie each 1ms
*
******************************************************************************
* @attention
*
* Copyright (c) 2017 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
/** @addtogroup STM32F4xx_HAL_Driver
* @{
*/
/** @addtogroup HAL_TimeBase_TIM
* @{
*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef TimHandle;
/* Private function prototypes -----------------------------------------------*/
void TIM6_DAC_IRQHandler(void);
/* Private functions ---------------------------------------------------------*/
/**
* @brief This function configures the TIM6 as a time base source.
* The time source is configured to have 1ms time base with a dedicated
* Tick interrupt priority.
* @note This function is called automatically at the beginning of program after
* reset by HAL_Init() or at any time when clock is configured, by HAL_RCC_ClockConfig().
* @param TickPriority Tick interrupt priority.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_InitTick (uint32_t TickPriority)
{
RCC_ClkInitTypeDef clkconfig;
uint32_t uwTimclock, uwAPB1Prescaler = 0U;
uint32_t uwPrescalerValue = 0U;
uint32_t pFLatency;
HAL_StatusTypeDef status;
/* Enable TIM6 clock */
__HAL_RCC_TIM6_CLK_ENABLE();
/* Get clock configuration */
HAL_RCC_GetClockConfig(&clkconfig, &pFLatency);
/* Get APB1 prescaler */
uwAPB1Prescaler = clkconfig.APB1CLKDivider;
/* Compute TIM6 clock */
if (uwAPB1Prescaler == RCC_HCLK_DIV1)
{
uwTimclock = HAL_RCC_GetPCLK1Freq();
}
else
{
uwTimclock = 2 * HAL_RCC_GetPCLK1Freq();
}
/* Compute the prescaler value to have TIM6 counter clock equal to 1MHz */
uwPrescalerValue = (uint32_t) ((uwTimclock / 1000000U) - 1U);
/* Initialize TIM6 */
TimHandle.Instance = TIM6;
/* Initialize TIMx peripheral as follow:
+ Period = [(TIM6CLK/1000) - 1]. to have a (1/1000) s time base.
+ Prescaler = (uwTimclock/1000000 - 1) to have a 1MHz counter clock.
+ ClockDivision = 0
+ Counter direction = Up
*/
TimHandle.Init.Period = (1000000U / 1000U) - 1U;
TimHandle.Init.Prescaler = uwPrescalerValue;
TimHandle.Init.ClockDivision = 0U;
TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
status = HAL_TIM_Base_Init(&TimHandle);
if (status == HAL_OK)
{
/* Start the TIM time Base generation in interrupt mode */
status = HAL_TIM_Base_Start_IT(&TimHandle);
if (status == HAL_OK)
{
/* Enable the TIM6 global Interrupt */
HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);
if (TickPriority < (1UL << __NVIC_PRIO_BITS))
{
/* Enable the TIM6 global Interrupt */
HAL_NVIC_SetPriority(TIM6_DAC_IRQn, TickPriority, 0);
uwTickPrio = TickPriority;
}
else
{
status = HAL_ERROR;
}
}
}
/* Return function status */
return status;
}
/**
* @brief Suspend Tick increment.
* @note Disable the tick increment by disabling TIM6 update interrupt.
* @retval None
*/
void HAL_SuspendTick(void)
{
/* Disable TIM6 update Interrupt */
__HAL_TIM_DISABLE_IT(&TimHandle, TIM_IT_UPDATE);
}
/**
* @brief Resume Tick increment.
* @note Enable the tick increment by Enabling TIM6 update interrupt.
* @retval None
*/
void HAL_ResumeTick(void)
{
/* Enable TIM6 Update interrupt */
__HAL_TIM_ENABLE_IT(&TimHandle, TIM_IT_UPDATE);
}
/**
* @brief Period elapsed callback in non blocking mode
* @note This function is called when TIM6 interrupt took place, inside
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
* a global variable "uwTick" used as application time base.
* @param htim TIM handle
* @retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
HAL_IncTick();
}
/**
* @brief This function handles TIM interrupt request.
* @retval None
*/
void TIM6_DAC_IRQHandler(void)
{
HAL_TIM_IRQHandler(&TimHandle);
}
/**
* @}
*/
/**
* @}
*/
@@ -0,0 +1,178 @@
/**
******************************************************************************
* @file Templates/Src/stm32f4xx_it.c
* @author MCD Application Team
* @brief Main Interrupt Service Routines.
* This file provides template for all exceptions handler and
* peripherals interrupt service routine.
*
* @note modified by ARM
* The modifications allow to use this file as User Code Template
* within the Device Family Pack.
******************************************************************************
* @attention
*
* Copyright (c) 2017 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f4xx_it.h"
#ifdef _RTE_
#include "RTE_Components.h" /* Component selection */
#endif
/** @addtogroup STM32F4xx_HAL_Examples
* @{
*/
/** @addtogroup Templates
* @{
*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/******************************************************************************/
/* Cortex-M4 Processor Exceptions Handlers */
/******************************************************************************/
/**
* @brief This function handles NMI exception.
* @param None
* @retval None
*/
void NMI_Handler(void)
{
}
/**
* @brief This function handles Hard Fault exception.
* @param None
* @retval None
*/
void HardFault_Handler(void)
{
/* Go to infinite loop when Hard Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Memory Manage exception.
* @param None
* @retval None
*/
void MemManage_Handler(void)
{
/* Go to infinite loop when Memory Manage exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Bus Fault exception.
* @param None
* @retval None
*/
void BusFault_Handler(void)
{
/* Go to infinite loop when Bus Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles Usage Fault exception.
* @param None
* @retval None
*/
void UsageFault_Handler(void)
{
/* Go to infinite loop when Usage Fault exception occurs */
while (1)
{
}
}
/**
* @brief This function handles SVCall exception.
* @param None
* @retval None
*/
#ifndef RTE_CMSIS_RTOS2_RTX5
void SVC_Handler(void)
{
}
#endif
/**
* @brief This function handles Debug Monitor exception.
* @param None
* @retval None
*/
void DebugMon_Handler(void)
{
}
/**
* @brief This function handles PendSVC exception.
* @param None
* @retval None
*/
#ifndef RTE_CMSIS_RTOS2_RTX5
void PendSV_Handler(void)
{
}
#endif
/**
* @brief This function handles SysTick Handler.
* @param None
* @retval None
*/
#ifndef RTE_CMSIS_RTOS2_RTX5
void SysTick_Handler(void)
{
HAL_IncTick();
}
#endif
/******************************************************************************/
/* STM32F4xx Peripherals Interrupt Handlers */
/* Add here the Interrupt Handler for the used peripheral(s) (PPP), for the */
/* available peripheral interrupt handler's name please refer to the startup */
/* file (startup_stm32f4xx.s). */
/******************************************************************************/
/**
* @brief This function handles PPP interrupt request.
* @param None
* @retval None
*/
/*void PPP_IRQHandler(void)
{
}*/
/**
* @}
*/
/**
* @}
*/
@@ -0,0 +1,747 @@
/**
******************************************************************************
* @file system_stm32f4xx.c
* @author MCD Application Team
* @brief CMSIS Cortex-M4 Device Peripheral Access Layer System Source File.
*
* This file provides two functions and one global variable to be called from
* user application:
* - SystemInit(): This function is called at startup just after reset and
* before branch to main program. This call is made inside
* the "startup_stm32f4xx.s" file.
*
* - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
* by the user application to setup the SysTick
* timer or configure other parameters.
*
* - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
* be called whenever the core clock is changed
* during program execution.
*
*
******************************************************************************
* @attention
*
* Copyright (c) 2017 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/** @addtogroup CMSIS
* @{
*/
/** @addtogroup stm32f4xx_system
* @{
*/
/** @addtogroup STM32F4xx_System_Private_Includes
* @{
*/
#include "stm32f4xx.h"
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)25000000) /*!< Default value of the External oscillator in Hz */
#endif /* HSE_VALUE */
#if !defined (HSI_VALUE)
#define HSI_VALUE ((uint32_t)16000000) /*!< Value of the Internal oscillator in Hz*/
#endif /* HSI_VALUE */
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_TypesDefinitions
* @{
*/
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_Defines
* @{
*/
/************************* Miscellaneous Configuration ************************/
/*!< Uncomment the following line if you need to use external SRAM or SDRAM as data memory */
#if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx) || defined(STM32F417xx)\
|| defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\
|| defined(STM32F469xx) || defined(STM32F479xx) || defined(STM32F412Zx) || defined(STM32F412Vx)
/* #define DATA_IN_ExtSRAM */
#endif /* STM32F40xxx || STM32F41xxx || STM32F42xxx || STM32F43xxx || STM32F469xx || STM32F479xx ||\
STM32F412Zx || STM32F412Vx */
#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\
|| defined(STM32F446xx) || defined(STM32F469xx) || defined(STM32F479xx)
/* #define DATA_IN_ExtSDRAM */
#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || STM32F446xx || STM32F469xx ||\
STM32F479xx */
/* Note: Following vector table addresses must be defined in line with linker
configuration. */
/*!< Uncomment the following line if you need to relocate the vector table
anywhere in Flash or Sram, else the vector table is kept at the automatic
remap of boot address selected */
/* #define USER_VECT_TAB_ADDRESS */
#if defined(USER_VECT_TAB_ADDRESS)
/*!< Uncomment the following line if you need to relocate your vector Table
in Sram else user remap will be done in Flash. */
/* #define VECT_TAB_SRAM */
#if defined(VECT_TAB_SRAM)
#define VECT_TAB_BASE_ADDRESS SRAM_BASE /*!< Vector Table base address field.
This value must be a multiple of 0x200. */
#define VECT_TAB_OFFSET 0x00000000U /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
#else
#define VECT_TAB_BASE_ADDRESS FLASH_BASE /*!< Vector Table base address field.
This value must be a multiple of 0x200. */
#define VECT_TAB_OFFSET 0x00000000U /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
#endif /* VECT_TAB_SRAM */
#endif /* USER_VECT_TAB_ADDRESS */
/******************************************************************************/
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_Macros
* @{
*/
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_Variables
* @{
*/
/* This variable is updated in three ways:
1) by calling CMSIS function SystemCoreClockUpdate()
2) by calling HAL API function HAL_RCC_GetHCLKFreq()
3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency
Note: If you use this function to configure the system clock; then there
is no need to call the 2 first functions listed above, since SystemCoreClock
variable is updated automatically.
*/
uint32_t SystemCoreClock = 16000000;
const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4};
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_FunctionPrototypes
* @{
*/
#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
static void SystemInit_ExtMemCtl(void);
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
/**
* @}
*/
/** @addtogroup STM32F4xx_System_Private_Functions
* @{
*/
/**
* @brief Setup the microcontroller system
* Initialize the FPU setting, vector table location and External memory
* configuration.
* @param None
* @retval None
*/
void SystemInit(void)
{
/* FPU settings ------------------------------------------------------------*/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
/* Configure the Vector Table location -------------------------------------*/
#if defined(USER_VECT_TAB_ADDRESS)
SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#endif /* USER_VECT_TAB_ADDRESS */
}
/**
* @brief Update SystemCoreClock variable according to Clock Register Values.
* The SystemCoreClock variable contains the core clock (HCLK), it can
* be used by the user application to setup the SysTick timer or configure
* other parameters.
*
* @note Each time the core clock (HCLK) changes, this function must be called
* to update SystemCoreClock variable value. Otherwise, any configuration
* based on this variable will be incorrect.
*
* @note - The system frequency computed by this function is not the real
* frequency in the chip. It is calculated based on the predefined
* constant and the selected clock source:
*
* - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
*
* - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
*
* - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**)
* or HSI_VALUE(*) multiplied/divided by the PLL factors.
*
* (*) HSI_VALUE is a constant defined in stm32f4xx_hal_conf.h file (default value
* 16 MHz) but the real value may vary depending on the variations
* in voltage and temperature.
*
* (**) HSE_VALUE is a constant defined in stm32f4xx_hal_conf.h file (its value
* depends on the application requirements), user has to ensure that HSE_VALUE
* is same as the real frequency of the crystal used. Otherwise, this function
* may have wrong result.
*
* - The result of this function could be not correct when using fractional
* value for HSE crystal.
*
* @param None
* @retval None
*/
void SystemCoreClockUpdate(void)
{
uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2;
/* Get SYSCLK source -------------------------------------------------------*/
tmp = RCC->CFGR & RCC_CFGR_SWS;
switch (tmp)
{
case 0x00: /* HSI used as system clock source */
SystemCoreClock = HSI_VALUE;
break;
case 0x04: /* HSE used as system clock source */
SystemCoreClock = HSE_VALUE;
break;
case 0x08: /* PLL used as system clock source */
/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
SYSCLK = PLL_VCO / PLL_P
*/
pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22;
pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
if (pllsource != 0)
{
/* HSE used as PLL clock source */
pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
}
else
{
/* HSI used as PLL clock source */
pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
}
pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2;
SystemCoreClock = pllvco/pllp;
break;
default:
SystemCoreClock = HSI_VALUE;
break;
}
/* Compute HCLK frequency --------------------------------------------------*/
/* Get HCLK prescaler */
tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
/* HCLK frequency */
SystemCoreClock >>= tmp;
}
#if defined (DATA_IN_ExtSRAM) && defined (DATA_IN_ExtSDRAM)
#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\
|| defined(STM32F469xx) || defined(STM32F479xx)
/**
* @brief Setup the external memory controller.
* Called in startup_stm32f4xx.s before jump to main.
* This function configures the external memories (SRAM/SDRAM)
* This SRAM/SDRAM will be used as program data memory (including heap and stack).
* @param None
* @retval None
*/
void SystemInit_ExtMemCtl(void)
{
__IO uint32_t tmp = 0x00;
register uint32_t tmpreg = 0, timeout = 0xFFFF;
register __IO uint32_t index;
/* Enable GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, GPIOH and GPIOI interface clock */
RCC->AHB1ENR |= 0x000001F8;
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN);
/* Connect PDx pins to FMC Alternate function */
GPIOD->AFR[0] = 0x00CCC0CC;
GPIOD->AFR[1] = 0xCCCCCCCC;
/* Configure PDx pins in Alternate function mode */
GPIOD->MODER = 0xAAAA0A8A;
/* Configure PDx pins speed to 100 MHz */
GPIOD->OSPEEDR = 0xFFFF0FCF;
/* Configure PDx pins Output type to push-pull */
GPIOD->OTYPER = 0x00000000;
/* No pull-up, pull-down for PDx pins */
GPIOD->PUPDR = 0x00000000;
/* Connect PEx pins to FMC Alternate function */
GPIOE->AFR[0] = 0xC00CC0CC;
GPIOE->AFR[1] = 0xCCCCCCCC;
/* Configure PEx pins in Alternate function mode */
GPIOE->MODER = 0xAAAA828A;
/* Configure PEx pins speed to 100 MHz */
GPIOE->OSPEEDR = 0xFFFFC3CF;
/* Configure PEx pins Output type to push-pull */
GPIOE->OTYPER = 0x00000000;
/* No pull-up, pull-down for PEx pins */
GPIOE->PUPDR = 0x00000000;
/* Connect PFx pins to FMC Alternate function */
GPIOF->AFR[0] = 0xCCCCCCCC;
GPIOF->AFR[1] = 0xCCCCCCCC;
/* Configure PFx pins in Alternate function mode */
GPIOF->MODER = 0xAA800AAA;
/* Configure PFx pins speed to 50 MHz */
GPIOF->OSPEEDR = 0xAA800AAA;
/* Configure PFx pins Output type to push-pull */
GPIOF->OTYPER = 0x00000000;
/* No pull-up, pull-down for PFx pins */
GPIOF->PUPDR = 0x00000000;
/* Connect PGx pins to FMC Alternate function */
GPIOG->AFR[0] = 0xCCCCCCCC;
GPIOG->AFR[1] = 0xCCCCCCCC;
/* Configure PGx pins in Alternate function mode */
GPIOG->MODER = 0xAAAAAAAA;
/* Configure PGx pins speed to 50 MHz */
GPIOG->OSPEEDR = 0xAAAAAAAA;
/* Configure PGx pins Output type to push-pull */
GPIOG->OTYPER = 0x00000000;
/* No pull-up, pull-down for PGx pins */
GPIOG->PUPDR = 0x00000000;
/* Connect PHx pins to FMC Alternate function */
GPIOH->AFR[0] = 0x00C0CC00;
GPIOH->AFR[1] = 0xCCCCCCCC;
/* Configure PHx pins in Alternate function mode */
GPIOH->MODER = 0xAAAA08A0;
/* Configure PHx pins speed to 50 MHz */
GPIOH->OSPEEDR = 0xAAAA08A0;
/* Configure PHx pins Output type to push-pull */
GPIOH->OTYPER = 0x00000000;
/* No pull-up, pull-down for PHx pins */
GPIOH->PUPDR = 0x00000000;
/* Connect PIx pins to FMC Alternate function */
GPIOI->AFR[0] = 0xCCCCCCCC;
GPIOI->AFR[1] = 0x00000CC0;
/* Configure PIx pins in Alternate function mode */
GPIOI->MODER = 0x0028AAAA;
/* Configure PIx pins speed to 50 MHz */
GPIOI->OSPEEDR = 0x0028AAAA;
/* Configure PIx pins Output type to push-pull */
GPIOI->OTYPER = 0x00000000;
/* No pull-up, pull-down for PIx pins */
GPIOI->PUPDR = 0x00000000;
/*-- FMC Configuration -------------------------------------------------------*/
/* Enable the FMC interface clock */
RCC->AHB3ENR |= 0x00000001;
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN);
FMC_Bank5_6->SDCR[0] = 0x000019E4;
FMC_Bank5_6->SDTR[0] = 0x01115351;
/* SDRAM initialization sequence */
/* Clock enable command */
FMC_Bank5_6->SDCMR = 0x00000011;
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* Delay */
for (index = 0; index<1000; index++);
/* PALL command */
FMC_Bank5_6->SDCMR = 0x00000012;
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* Auto refresh command */
FMC_Bank5_6->SDCMR = 0x00000073;
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* MRD register program */
FMC_Bank5_6->SDCMR = 0x00046014;
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* Set refresh count */
tmpreg = FMC_Bank5_6->SDRTR;
FMC_Bank5_6->SDRTR = (tmpreg | (0x0000027C<<1));
/* Disable write protection */
tmpreg = FMC_Bank5_6->SDCR[0];
FMC_Bank5_6->SDCR[0] = (tmpreg & 0xFFFFFDFF);
#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)
/* Configure and enable Bank1_SRAM2 */
FMC_Bank1->BTCR[2] = 0x00001011;
FMC_Bank1->BTCR[3] = 0x00000201;
FMC_Bank1E->BWTR[2] = 0x0fffffff;
#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx */
#if defined(STM32F469xx) || defined(STM32F479xx)
/* Configure and enable Bank1_SRAM2 */
FMC_Bank1->BTCR[2] = 0x00001091;
FMC_Bank1->BTCR[3] = 0x00110212;
FMC_Bank1E->BWTR[2] = 0x0fffffff;
#endif /* STM32F469xx || STM32F479xx */
(void)(tmp);
}
#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || STM32F469xx || STM32F479xx */
#elif defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
/**
* @brief Setup the external memory controller.
* Called in startup_stm32f4xx.s before jump to main.
* This function configures the external memories (SRAM/SDRAM)
* This SRAM/SDRAM will be used as program data memory (including heap and stack).
* @param None
* @retval None
*/
void SystemInit_ExtMemCtl(void)
{
__IO uint32_t tmp = 0x00;
#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\
|| defined(STM32F446xx) || defined(STM32F469xx) || defined(STM32F479xx)
#if defined (DATA_IN_ExtSDRAM)
register uint32_t tmpreg = 0, timeout = 0xFFFF;
register __IO uint32_t index;
#if defined(STM32F446xx)
/* Enable GPIOA, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG interface
clock */
RCC->AHB1ENR |= 0x0000007D;
#else
/* Enable GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, GPIOH and GPIOI interface
clock */
RCC->AHB1ENR |= 0x000001F8;
#endif /* STM32F446xx */
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOCEN);
#if defined(STM32F446xx)
/* Connect PAx pins to FMC Alternate function */
GPIOA->AFR[0] |= 0xC0000000;
GPIOA->AFR[1] |= 0x00000000;
/* Configure PDx pins in Alternate function mode */
GPIOA->MODER |= 0x00008000;
/* Configure PDx pins speed to 50 MHz */
GPIOA->OSPEEDR |= 0x00008000;
/* Configure PDx pins Output type to push-pull */
GPIOA->OTYPER |= 0x00000000;
/* No pull-up, pull-down for PDx pins */
GPIOA->PUPDR |= 0x00000000;
/* Connect PCx pins to FMC Alternate function */
GPIOC->AFR[0] |= 0x00CC0000;
GPIOC->AFR[1] |= 0x00000000;
/* Configure PDx pins in Alternate function mode */
GPIOC->MODER |= 0x00000A00;
/* Configure PDx pins speed to 50 MHz */
GPIOC->OSPEEDR |= 0x00000A00;
/* Configure PDx pins Output type to push-pull */
GPIOC->OTYPER |= 0x00000000;
/* No pull-up, pull-down for PDx pins */
GPIOC->PUPDR |= 0x00000000;
#endif /* STM32F446xx */
/* Connect PDx pins to FMC Alternate function */
GPIOD->AFR[0] = 0x000000CC;
GPIOD->AFR[1] = 0xCC000CCC;
/* Configure PDx pins in Alternate function mode */
GPIOD->MODER = 0xA02A000A;
/* Configure PDx pins speed to 50 MHz */
GPIOD->OSPEEDR = 0xA02A000A;
/* Configure PDx pins Output type to push-pull */
GPIOD->OTYPER = 0x00000000;
/* No pull-up, pull-down for PDx pins */
GPIOD->PUPDR = 0x00000000;
/* Connect PEx pins to FMC Alternate function */
GPIOE->AFR[0] = 0xC00000CC;
GPIOE->AFR[1] = 0xCCCCCCCC;
/* Configure PEx pins in Alternate function mode */
GPIOE->MODER = 0xAAAA800A;
/* Configure PEx pins speed to 50 MHz */
GPIOE->OSPEEDR = 0xAAAA800A;
/* Configure PEx pins Output type to push-pull */
GPIOE->OTYPER = 0x00000000;
/* No pull-up, pull-down for PEx pins */
GPIOE->PUPDR = 0x00000000;
/* Connect PFx pins to FMC Alternate function */
GPIOF->AFR[0] = 0xCCCCCCCC;
GPIOF->AFR[1] = 0xCCCCCCCC;
/* Configure PFx pins in Alternate function mode */
GPIOF->MODER = 0xAA800AAA;
/* Configure PFx pins speed to 50 MHz */
GPIOF->OSPEEDR = 0xAA800AAA;
/* Configure PFx pins Output type to push-pull */
GPIOF->OTYPER = 0x00000000;
/* No pull-up, pull-down for PFx pins */
GPIOF->PUPDR = 0x00000000;
/* Connect PGx pins to FMC Alternate function */
GPIOG->AFR[0] = 0xCCCCCCCC;
GPIOG->AFR[1] = 0xCCCCCCCC;
/* Configure PGx pins in Alternate function mode */
GPIOG->MODER = 0xAAAAAAAA;
/* Configure PGx pins speed to 50 MHz */
GPIOG->OSPEEDR = 0xAAAAAAAA;
/* Configure PGx pins Output type to push-pull */
GPIOG->OTYPER = 0x00000000;
/* No pull-up, pull-down for PGx pins */
GPIOG->PUPDR = 0x00000000;
#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\
|| defined(STM32F469xx) || defined(STM32F479xx)
/* Connect PHx pins to FMC Alternate function */
GPIOH->AFR[0] = 0x00C0CC00;
GPIOH->AFR[1] = 0xCCCCCCCC;
/* Configure PHx pins in Alternate function mode */
GPIOH->MODER = 0xAAAA08A0;
/* Configure PHx pins speed to 50 MHz */
GPIOH->OSPEEDR = 0xAAAA08A0;
/* Configure PHx pins Output type to push-pull */
GPIOH->OTYPER = 0x00000000;
/* No pull-up, pull-down for PHx pins */
GPIOH->PUPDR = 0x00000000;
/* Connect PIx pins to FMC Alternate function */
GPIOI->AFR[0] = 0xCCCCCCCC;
GPIOI->AFR[1] = 0x00000CC0;
/* Configure PIx pins in Alternate function mode */
GPIOI->MODER = 0x0028AAAA;
/* Configure PIx pins speed to 50 MHz */
GPIOI->OSPEEDR = 0x0028AAAA;
/* Configure PIx pins Output type to push-pull */
GPIOI->OTYPER = 0x00000000;
/* No pull-up, pull-down for PIx pins */
GPIOI->PUPDR = 0x00000000;
#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || STM32F469xx || STM32F479xx */
/*-- FMC Configuration -------------------------------------------------------*/
/* Enable the FMC interface clock */
RCC->AHB3ENR |= 0x00000001;
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN);
/* Configure and enable SDRAM bank1 */
#if defined(STM32F446xx)
FMC_Bank5_6->SDCR[0] = 0x00001954;
#else
FMC_Bank5_6->SDCR[0] = 0x000019E4;
#endif /* STM32F446xx */
FMC_Bank5_6->SDTR[0] = 0x01115351;
/* SDRAM initialization sequence */
/* Clock enable command */
FMC_Bank5_6->SDCMR = 0x00000011;
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* Delay */
for (index = 0; index<1000; index++);
/* PALL command */
FMC_Bank5_6->SDCMR = 0x00000012;
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* Auto refresh command */
#if defined(STM32F446xx)
FMC_Bank5_6->SDCMR = 0x000000F3;
#else
FMC_Bank5_6->SDCMR = 0x00000073;
#endif /* STM32F446xx */
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* MRD register program */
#if defined(STM32F446xx)
FMC_Bank5_6->SDCMR = 0x00044014;
#else
FMC_Bank5_6->SDCMR = 0x00046014;
#endif /* STM32F446xx */
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
timeout = 0xFFFF;
while((tmpreg != 0) && (timeout-- > 0))
{
tmpreg = FMC_Bank5_6->SDSR & 0x00000020;
}
/* Set refresh count */
tmpreg = FMC_Bank5_6->SDRTR;
#if defined(STM32F446xx)
FMC_Bank5_6->SDRTR = (tmpreg | (0x0000050C<<1));
#else
FMC_Bank5_6->SDRTR = (tmpreg | (0x0000027C<<1));
#endif /* STM32F446xx */
/* Disable write protection */
tmpreg = FMC_Bank5_6->SDCR[0];
FMC_Bank5_6->SDCR[0] = (tmpreg & 0xFFFFFDFF);
#endif /* DATA_IN_ExtSDRAM */
#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx || STM32F446xx || STM32F469xx || STM32F479xx */
#if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx) || defined(STM32F417xx)\
|| defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)\
|| defined(STM32F469xx) || defined(STM32F479xx) || defined(STM32F412Zx) || defined(STM32F412Vx)
#if defined(DATA_IN_ExtSRAM)
/*-- GPIOs Configuration -----------------------------------------------------*/
/* Enable GPIOD, GPIOE, GPIOF and GPIOG interface clock */
RCC->AHB1ENR |= 0x00000078;
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIODEN);
/* Connect PDx pins to FMC Alternate function */
GPIOD->AFR[0] = 0x00CCC0CC;
GPIOD->AFR[1] = 0xCCCCCCCC;
/* Configure PDx pins in Alternate function mode */
GPIOD->MODER = 0xAAAA0A8A;
/* Configure PDx pins speed to 100 MHz */
GPIOD->OSPEEDR = 0xFFFF0FCF;
/* Configure PDx pins Output type to push-pull */
GPIOD->OTYPER = 0x00000000;
/* No pull-up, pull-down for PDx pins */
GPIOD->PUPDR = 0x00000000;
/* Connect PEx pins to FMC Alternate function */
GPIOE->AFR[0] = 0xC00CC0CC;
GPIOE->AFR[1] = 0xCCCCCCCC;
/* Configure PEx pins in Alternate function mode */
GPIOE->MODER = 0xAAAA828A;
/* Configure PEx pins speed to 100 MHz */
GPIOE->OSPEEDR = 0xFFFFC3CF;
/* Configure PEx pins Output type to push-pull */
GPIOE->OTYPER = 0x00000000;
/* No pull-up, pull-down for PEx pins */
GPIOE->PUPDR = 0x00000000;
/* Connect PFx pins to FMC Alternate function */
GPIOF->AFR[0] = 0x00CCCCCC;
GPIOF->AFR[1] = 0xCCCC0000;
/* Configure PFx pins in Alternate function mode */
GPIOF->MODER = 0xAA000AAA;
/* Configure PFx pins speed to 100 MHz */
GPIOF->OSPEEDR = 0xFF000FFF;
/* Configure PFx pins Output type to push-pull */
GPIOF->OTYPER = 0x00000000;
/* No pull-up, pull-down for PFx pins */
GPIOF->PUPDR = 0x00000000;
/* Connect PGx pins to FMC Alternate function */
GPIOG->AFR[0] = 0x00CCCCCC;
GPIOG->AFR[1] = 0x000000C0;
/* Configure PGx pins in Alternate function mode */
GPIOG->MODER = 0x00085AAA;
/* Configure PGx pins speed to 100 MHz */
GPIOG->OSPEEDR = 0x000CAFFF;
/* Configure PGx pins Output type to push-pull */
GPIOG->OTYPER = 0x00000000;
/* No pull-up, pull-down for PGx pins */
GPIOG->PUPDR = 0x00000000;
/*-- FMC/FSMC Configuration --------------------------------------------------*/
/* Enable the FMC/FSMC interface clock */
RCC->AHB3ENR |= 0x00000001;
#if defined(STM32F427xx) || defined(STM32F437xx) || defined(STM32F429xx) || defined(STM32F439xx)
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN);
/* Configure and enable Bank1_SRAM2 */
FMC_Bank1->BTCR[2] = 0x00001011;
FMC_Bank1->BTCR[3] = 0x00000201;
FMC_Bank1E->BWTR[2] = 0x0fffffff;
#endif /* STM32F427xx || STM32F437xx || STM32F429xx || STM32F439xx */
#if defined(STM32F469xx) || defined(STM32F479xx)
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FMCEN);
/* Configure and enable Bank1_SRAM2 */
FMC_Bank1->BTCR[2] = 0x00001091;
FMC_Bank1->BTCR[3] = 0x00110212;
FMC_Bank1E->BWTR[2] = 0x0fffffff;
#endif /* STM32F469xx || STM32F479xx */
#if defined(STM32F405xx) || defined(STM32F415xx) || defined(STM32F407xx)|| defined(STM32F417xx)\
|| defined(STM32F412Zx) || defined(STM32F412Vx)
/* Delay after an RCC peripheral clock enabling */
tmp = READ_BIT(RCC->AHB3ENR, RCC_AHB3ENR_FSMCEN);
/* Configure and enable Bank1_SRAM2 */
FSMC_Bank1->BTCR[2] = 0x00001011;
FSMC_Bank1->BTCR[3] = 0x00000201;
FSMC_Bank1E->BWTR[2] = 0x0FFFFFFF;
#endif /* STM32F405xx || STM32F415xx || STM32F407xx || STM32F417xx || STM32F412Zx || STM32F412Vx */
#endif /* DATA_IN_ExtSRAM */
#endif /* STM32F405xx || STM32F415xx || STM32F407xx || STM32F417xx || STM32F427xx || STM32F437xx ||\
STM32F429xx || STM32F439xx || STM32F469xx || STM32F479xx || STM32F412Zx || STM32F412Vx */
(void)(tmp);
}
#endif /* DATA_IN_ExtSRAM && DATA_IN_ExtSDRAM */
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/

Some files were not shown because too many files have changed in this diff Show More