API

struct def_time
#include <user_code_0.h>

Public Members

int tdelay
short int SystemN
short int CBSN
short int sec
short int min
short int hour
short int day
short int weekday
short int month
short int year
struct SDI_t
#include <user_code_0.h>

Public Members

uint32_t t

служебный счетчик тиков

Имена переменных: t. Объявление (как в исходном коде):

uint32_t t;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* служебный счетчик тиков
*

uint8_t enabled

0 - sdi не используется, 1 - включён на in, 2 - включён на out

Имена переменных: enabled. Объявление (как в исходном коде):

uint8_t enabled;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* 0 - sdi не используется, 1 - включён на in, 2 - включён на out
*

uint8_t s

состояние sdi: 0 = пауза, 1 = ожидание стартового бита и т.д., см. конечный автомат в obrabotka_sdi

Имена переменных: s. Объявление (как в исходном коде):

uint8_t s;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* состояние sdi: 0 = пауза, 1 = ожидание стартового бита и т.д., см. конечный автомат в obrabotka_sdi
*

uint8_t byte

принимаемый/передаваемый байт

Имена переменных: byte. Объявление (как в исходном коде):

uint8_t byte;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* принимаемый/передаваемый байт
*

uint8_t bit

флаг принимаемого/передаваемого бита (единица на месте ожидаемого бита)

Имена переменных: bit. Объявление (как в исходном коде):

uint8_t bit;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* флаг принимаемого/передаваемого бита (единица на месте ожидаемого бита)
*

uint8_t ebit

чётность

Имена переменных: ebit. Объявление (как в исходном коде):

uint8_t ebit;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* чётность
*

uint8_t tx_buffer[SDI_TX_BUF]

кольцевой буфер передачи

Имена переменных: tx_buffer. Объявление (как в исходном коде):

uint8_t tx_buffer[SDI_TX_BUF];

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* кольцевой буфер передачи
*

uint8_t tx_wr_index

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: tx_wr_index. Объявление (как в исходном коде):

uint8_t tx_wr_index;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

uint8_t tx_rd_index

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: tx_rd_index. Объявление (как в исходном коде):

uint8_t tx_rd_index;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

uint8_t tx_counter

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: tx_counter. Объявление (как в исходном коде):

uint8_t tx_counter;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

uint8_t out_lines_counter

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: out_lines_counter. Объявление (как в исходном коде):

uint8_t out_lines_counter;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

uint8_t tx_buffer_overflow

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: tx_buffer_overflow. Объявление (как в исходном коде):

uint8_t tx_buffer_overflow;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

uint8_t rx_buffer[SDI_RX_BUF]

кольцевой буфер приёма

Имена переменных: rx_buffer. Объявление (как в исходном коде):

uint8_t rx_buffer[SDI_RX_BUF];

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* кольцевой буфер приёма
*

uint8_t rx_wr_index

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: rx_wr_index. Объявление (как в исходном коде):

uint8_t rx_wr_index;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

uint8_t rx_rd_index

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: rx_rd_index. Объявление (как в исходном коде):

uint8_t rx_rd_index;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

uint8_t rx_counter

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: rx_counter. Объявление (как в исходном коде):

uint8_t rx_counter;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

uint8_t in_lines_counter

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: in_lines_counter. Объявление (как в исходном коде):

uint8_t in_lines_counter;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

uint8_t rx_buffer_overflow

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: rx_buffer_overflow. Объявление (как в исходном коде):

uint8_t rx_buffer_overflow;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

GPIO_TypeDef *port_data

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: port_data. Объявление (как в исходном коде):

GPIO_TypeDef* port_data;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

uint16_t pin_data

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: pin_data. Объявление (как в исходном коде):

uint16_t pin_data;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

GPIO_TypeDef *port_dir

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: port_dir. Объявление (как в исходном коде):

GPIO_TypeDef* port_dir;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

uint16_t pin_dir

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: pin_dir. Объявление (как в исходном коде):

uint16_t pin_dir;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

file main.h
#include «stm32f1xx_hal.h»

: Header for main.c file. This file contains the common defines of the application.

Copyright (c) 2026 STMicroelectronics. All rights reserved.

Attention

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.

Functions

void Error_Handler(void)

This function is executed in case of error occurrence.

Return values:

None

file main.c
#include «main.h»
#include «usb_device.h»
#include «platform.h»
#include «user_code_0.h»
#include «user_code_2.h»

: Main program body

Copyright (c) 2026 STMicroelectronics. All rights reserved.

Attention

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.

Functions

void SystemClock_Config(void)

System Clock Configuration.

Return values:

None

static void MX_GPIO_Init(void)

GPIO Initialization Function.

Параметры:

None

Return values:

None

static void MX_SPI1_Init(void)

SPI1 Initialization Function.

Параметры:

None

Return values:

None

static void MX_USART1_UART_Init(void)

USART1 Initialization Function.

Параметры:

None

Return values:

None

static void MX_SPI2_Init(void)

SPI2 Initialization Function.

Параметры:

None

Return values:

None

static void MX_TIM1_Init(void)

TIM1 Initialization Function.

Параметры:

None

Return values:

None

static void MX_UART5_Init(void)

UART5 Initialization Function.

Параметры:

None

Return values:

None

static void MX_USART3_UART_Init(void)

USART3 Initialization Function.

Параметры:

None

Return values:

None

static void MX_UART4_Init(void)

UART4 Initialization Function.

Параметры:

None

Return values:

None

int main(void)

The application entry point.

Return values:

int

void Error_Handler(void)

This function is executed in case of error occurrence.

Return values:

None

Variables

SPI_HandleTypeDef hspi1
SPI_HandleTypeDef hspi2
TIM_HandleTypeDef htim1
UART_HandleTypeDef huart4
UART_HandleTypeDef huart5
UART_HandleTypeDef huart1
UART_HandleTypeDef huart3
file main_common.h

Defines

APP_RX_DATA_SIZE
APP_TX_DATA_SIZE
USB_RX_BUFFER_SIZE
file platform.h
#include «main_common.h»

Defines

DEF_TIME
TOTAL_CBS
SLAVE_TIMEOUT
SLAVE_PAUSE
ON_12V_MODBUS
OFF_12V_MODBUS
ON_12V_SDI
OFF_12V_SDI
TOTAL_MODBUS_PORTS
TOTAL_MODBUS_DEV
TOTAL_SDI_PORTS
TOTAL_SDI_DEV
KTICK_FBREAK
KTICK_FMARK
KTICK_H
KTICK
KTICK_P
KTICK_S
KTICK_S1
KTICK_W
KTICK_E
SDI_RX_BUF
SDI_TX_BUF
huart_SEN_1
huart_SEN_2
huart_MIK_1
huart_MIK_2
huart_USB
UART_SEN_1_TX_BUFFER_SIZE
UART_SEN_2_TX_BUFFER_SIZE
UART_MIK_1_TX_BUFFER_SIZE
UART_MIK_2_TX_BUFFER_SIZE
UART_LCD_1_TX_BUFFER_SIZE
UART_SEN_1_RX_BUFFER_SIZE
UART_SEN_2_RX_BUFFER_SIZE
UART_MIK_1_RX_BUFFER_SIZE
UART_MIK_2_RX_BUFFER_SIZE
UART_LCD_1_RX_BUFFER_SIZE
BUFFER_SIZE
BUFFER_SLAVE_SIZE
MAXSTRLEN
UART_SEN_1_EOP
UART_SEN_2_EOP
UART_MIK_1_EOP
UART_MIK_2_EOP
UART_LCD_1_EOP
PMIG
TMIG
TMIGE
KMIGE
TMIGEP
TMIGEPL
SF1
SF2
SF3
SF4
SF5
SF6
SF7
NUM_SYS_1
NUM_SYS_2
NUM_SYS_3
NUM_CBS_1
NUM_CBS_2
NUM_CBS_3
SF_SD_CARD
NUM_SYS
NUM_CBS
VD1_ON
VD1_OFF
VD1_TOG
VDT_ON
VDT_OFF
VDT_TOG
VDOra_ON
VDOra_OFF
VDOra_TOG
VDYel_ON
VDYel_OFF
VDYel_TOG
VDBlu_ON
VDBlu_OFF
VDBlu_TOG
F_PORT_DATA_0
F_PIN_DATA_0
F_PORT_DATA_1
F_PIN_DATA_1
F_PORT_DATA_2
F_PIN_DATA_2
F_PORT_DATA_3
F_PIN_DATA_3
F_PORT_DATA_4
F_PIN_DATA_4
F_PORT_DATA_5
F_PIN_DATA_5
F_PORT_DATA_6
F_PIN_DATA_6
F_PORT_DATA_7
F_PIN_DATA_7
UART_SEN_1_RX_ON
UART_SEN_1_RX_OFF
UART_SEN_1_TX_ON
UART_SEN_1_TX_OFF
UART_SEN_2_RX_ON
UART_SEN_2_RX_OFF
UART_SEN_2_TX_ON
UART_SEN_2_TX_OFF
UART_MIK_1_RX_ON
UART_MIK_1_RX_OFF
UART_MIK_1_TX_ON
UART_MIK_1_TX_OFF
UART_MIK_2_RX_ON
UART_MIK_2_RX_OFF
UART_MIK_2_TX_ON
UART_MIK_2_TX_OFF
UART_LCD_1_RX_ON
UART_LCD_1_RX_OFF
UART_LCD_1_TX_ON
UART_LCD_1_TX_OFF
I2C1_SCL_PORT
I2C1_SCL_PIN
I2C1_SDA_PORT
I2C1_SDA_PIN
I2C1_SCL_1
I2C1_SCL_0
I2C1_SDA_1
I2C1_SDA_0
I2C1_SDA_in
I2C2_SCL_PORT
I2C2_SCL_PIN
I2C2_SDA_PORT
I2C2_SDA_PIN
I2C2_SCL_1
I2C2_SCL_0
I2C2_SDA_1
I2C2_SDA_0
I2C2_SDA_in
DS1307_ADDR
EEPROM_ADDR
DS_REG_SEC
DS_REG_MIN
DS_REG_HOUR
DS_REG_DOW
DS_REG_DAY
DS_REG_MON
DS_REG_YEAR
DS_REG_CTRL
file user_code_0.h
#include <string.h>
#include <math.h>

Defines

VER_H
VER_L
FIGA
min(a, b)
max(a, b)
abs(a)
PCA2129_ADDR
PCA_REG_CTRL1
PCA_REG_CTRL2
PCA_REG_CTRL3
PCA_REG_SEC
PCA_REG_MIN
PCA_REG_HOUR
PCA_REG_DAY
PCA_REG_WDAY
PCA_REG_MON
PCA_REG_YEAR
PCA_CTRL1_STOP
PCA_CTRL1_12_24
PCA_SEC_OSF

Functions

uint8_t CDC_Transmit_FS(uint8_t *Buf, uint16_t Len)
int _write(int file, char *ptr, int len)

Низкоуровневая функция записи, используемая для ретаргетинга putchar()/printf().

Передает буфер в USB CDC через CDC_Transmit_FS(). Реализация является блокирующей: выполняется ожидание, пока стек USB примет пакет. Предназначена для Atollic TrueStudio и совместимых конфигураций, где стандартный вывод направляется в Virtual COM Port.

* через _write работают putchar и printf. Не забудь setvbuf вызвать в main после инициализации.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Примечание

Для корректной работы printf() рекомендуется настроить буферизацию (например, через setvbuf() в main).

Предупреждение

Функция выполняет активное ожидание (busy-wait) и может существенно задерживать выполнение программы.

Параметры:
  • file[in] Дескриптор потока (в данной реализации не используется по назначению).

  • ptr[in] Указатель на массив байт, подлежащих передаче.

  • len[in] Количество байт для передачи.

Результат:

Количество байт, принятых к передаче (возвращается len).

void IER(unsigned char er)

Регистрирует внутреннюю ошибку (Internal Error) для последующей индикации.

Добавляет код ошибки в массив mer[] и увеличивает счетчик nmer. Для кодов < 128 подавляется повторная регистрация одинаковых значений. Дальнейшая индикация (мигание светодиодом) выполняется в обработчике таймера.

* Отладочная индикация "Internal Error"
*
* так, на всякий пожарный (нулевую ошибку не промигаешь).
*
* В массиве все ошибки будут разными, а однократные не проверяем (их при желании может быть много одинаковых, если через IER).
*
* Массив заполнен - последняя меняется на новую.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Примечание

Накопленные коды ошибок используются как очередь для световой индикации.

Параметры:

er[in] Код ошибки (0 игнорируется).

void IER1(unsigned char er)

Регистрирует внутреннюю ошибку (Internal Error) без повторов одного и того же кода.

Отличается от IER() тем, что для подавления повторов код сохраняется как (128 + er). Это позволяет отделять „однократные“ ошибки от обычных при дальнейшем отображении.

* Отладочная индикация "Internal Error" без повторов
*
* так, на всякий пожарный.
*
* В массиве все однократные ошибки будут разными (если надо много одинаковых - делай через IER).
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

er[in] Код ошибки (0 игнорируется).

HAL_StatusTypeDef page_erase(size_t addr)

Стирает одну страницу Flash памяти, содержащую указанный адрес.

Выполняет HAL_FLASH_Unlock(), затем HAL_FLASHEx_Erase() для одной страницы и завершает HAL_FLASH_Lock(). Адрес страницы вычисляется выравниванием addr на границу страницы (маска 0x7FF, т.е. предполагается размер страницы 2 КБ).

* Для STM32F105 страницы 2 КБ: выравнивание на 0x800
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях указаны предпосылки по организации Flash.

Параметры:

addr – Адрес внутри страницы Flash, которую необходимо стереть.

Результат:

HAL_StatusTypeDef.

void time_write(size_t addr, struct def_time *port)

Записывает структуру def_time в Flash по заданному адресу.

Запись выполняется словами по 4 байта (FLASH_TYPEPROGRAM_WORD) последовательно от addr. Объем записи равен sizeof(struct def_time).

* Запись структуры параметров CBS и времени.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Примечание

Функция выполняет HAL_FLASH_Unlock()/HAL_FLASH_Lock() внутри.

Предупреждение

Перед вызовом требуется стереть соответствующую страницу Flash (см. page_erase()).

Параметры:
  • addr – Базовый адрес Flash для записи.

  • port – Указатель на структуру, содержащую записываемые данные.

void time_read(size_t addr, struct def_time *port)

Читает структуру def_time из Flash по заданному адресу.

Выполняет копирование sizeof(struct def_time) байт из адресного пространства Flash в структуру, на которую указывает port.

* Чтение структуры параметров CBS и времени.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:
  • addr – Базовый адрес Flash для чтения.

  • port – Указатель на структуру-приемник.

void englower(char *s)

Преобразует латинские буквы строки в нижний регистр.

Изменяет строку на месте, обрабатывая только диапазон символов „A“..“Z“. Остальные байты не изменяются.

* tolower исключительно для английских букв.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

s[in] Нуль-терминированная строка для преобразования.

void delprob(char *s)

Удаляет из строки пробелы и символы табуляции.

Выполняет уплотнение строки на месте, сохраняя порядок остальных символов.

* удаление всех пробелов и табуляций.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

s[inout] Нуль-терминированная строка, из которой требуется удалить „ „ и „\t“.

char str_to_num(char *s, unsigned int *n, unsigned int l_max)

Преобразует десятичную строку в неотрицательное целое с диагностикой ошибок.

Проверяет, что все символы строки - цифры. Контролирует переполнение относительно l_max. При ошибке в n возвращается позиция проблемного символа. При успехе в n записывается значение.

* превращение строки в неотрицательное число с сообщением об ошибке (для USB-диалога).
*
* возврат: 1 - не цифра, 2 - длинновато, в этом случае n - позиция неверного символа в строке.
*
* // Локальный MAXINT.
*   l_max = 1;
*   while (l_max>0) l_max <<= 1;
*   --l_max;
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Примечание

Используется в диалоге по USB/интерфейсных командах для проверки параметров.

Параметры:
  • s[inout] Входная нуль-терминированная строка (ожидаются цифры „0“..“9“).

  • n – Выход: при успехе - полученное значение; при ошибке - индекс некорректного символа.

  • l_max[in] Максимально допустимое значение результата (для контроля переполнения).

Результат:

Код результата: 0 - успех; 1 - обнаружен нецифровой символ; 2 - переполнение относительно l_max.

char n_c(char c)

Преобразует 4-битное значение (0..15) в шестнадцатеричный символ ASCII.

Возвращает „0“..“9“ или „A“..“F“. При выходе за диапазон увеличивает счетчик ошибок preobra_err и возвращает 0.

* 16-ричная цифра -> символ.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

c[in] Значение 0..15.

Результат:

ASCII-символ соответствующей hex-цифры; 0 при ошибке.

void byte_str(char b, char *d)

Преобразует байт в две шестнадцатеричные ASCII-цифры.

Старший полубайт записывается первым, затем младший полубайт.

* байт -> два 16-ричных символа.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:
  • b[in] Байт для преобразования.

  • d[inout] Указатель на буфер минимум из 2 байт для результата.

void long_x(unsigned long ul, char *s)

Преобразует unsigned long в строку из 8 шестнадцатеричных символов.

Записывает ровно 8 символов без добавления завершающего нуля.

* long -> восемь 16-ричных символов.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:
  • ul[in] Преобразуемое значение.

  • s[inout] Буфер минимум из 8 байт для результата.

void n_s(unsigned int d, char *s, char k)

Преобразует десятичное число в строку фиксированной длины.

Записывает k десятичных цифр. При переполнении (число не помещается в k разрядов) помечает старший символ как „*“ и увеличивает счетчик preobra_err.

* десятичное число -> строка (k символов).
*
* переполнение.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:
  • d[in] Число для преобразования.

  • s[inout] Буфер длиной не менее k байт.

  • k – Количество символов в выходном поле.

void long_str(long k, char *s, int n)

Преобразует long в десятичную строку фиксированной ширины (выравнивание вправо).

Заполняет поле шириной n. При наличии свободных позиций заполняет их пробелами. Для отрицательных чисел учитывает знак. При переполнении заполняет поле символами „*“ и увеличивает счетчик preobra_err.

* long -> строка (дес. вид, n символов, выр. вправо).
*
* придётся повозиться с минусом.
*
* переполнение.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:
  • k – Преобразуемое значение.

  • s[inout] Буфер длиной не менее n байт.

  • n – Ширина выходного поля (количество символов).

int x_n(char c)

Преобразует ASCII-символ шестнадцатеричной цифры в число 0..15.

Поддерживаются „0“..“9“, „A“..“F“, „a“..“f“. При ошибке увеличивается preobra_err.

* символ -> 16-ричная цифра.
*
* ret: -1 - ошибка формата.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

c[in] Входной символ.

Результат:

Значение 0..15; -1 при ошибке формата.

int x2_n(char *s)

Преобразует 2 ASCII-символа шестнадцатеричного представления в число 0..255.

При ошибке формата увеличивает preobra_err и возвращает -1.

* два символа -> 16-ричное число.
*
* ret: -1 - ошибка формата.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

s[inout] Указатель на два символа hex-представления.

Результат:

Число 0..255; -1 при ошибке формата.

int x4_n(char *s)

Преобразует 4 ASCII-символа шестнадцатеричного представления в число.

При ошибке формата увеличивает preobra_err и возвращает -1.

* четыре символа -> 16-ричное число.
*
* ret: -1 - ошибка формата.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

s[inout] Указатель на четыре символа hex-представления.

Результат:

Число 0..65535; -1 при ошибке формата.

int d2_n(char *s)

Преобразует 2 ASCII-цифры десятичного представления в число.

Строка должна содержать ровно две цифры „0“..“9“. При ошибке увеличивает preobra_err.

* два символа -> десятичное число.
*
* ret: -1 - ошибка формата.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

s[inout] Указатель на два символа десятичного представления.

Результат:

Число 0..99; -1 при ошибке формата.

int d3_n(char *s)

Преобразует 3 ASCII-цифры десятичного представления в число.

* три символа -> десятичное число.
*
* ret: -1 - ошибка формата.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

s[inout] Указатель на три символа десятичного представления.

Результат:

Число 0..999; -1 при ошибке формата.

int d4_n(char *s)

Преобразует 4 ASCII-цифры десятичного представления в число.

* четыре символа -> десятичное число.
*
* ret: -1 - ошибка формата.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

s[inout] Указатель на четыре символа десятичного представления.

Результат:

Число 0..9999; -1 при ошибке формата.

int d5_n(char *s)

Преобразует 5 ASCII-цифр десятичного представления в число.

* пять символов -> десятичное число.
*
* ret: -1 - ошибка формата.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

s[inout] Указатель на пять символов десятичного представления.

Результат:

Число 0..99999; -1 при ошибке формата.

long str_long(char *s)

Преобразует строку с десятичным числом в long.

Пропускает ведущие пробелы, поддерживает знак „-“. Парсинг прекращается на первом нецифровом символе. Переполнение не контролируется.

* строка (дес.число) до первой не-цифры -> long.
*
* переполнение не проверяется!
*
* знак.
*
* первые пробелы пропустим.
*
* отрицательные тоже можем.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

s[inout] Нуль-терминированная строка.

Результат:

Результат преобразования.

double yatof(char *s)

Преобразует строку в число с плавающей точкой (замена atof).

Пропускает ведущие пробелы, поддерживает знак „-“. Дробная часть может отделяться „.“ или „,“. Парсинг прекращается на первом неподходящем символе.

* замена atof (строка -> double)
*
* мимо лидирующих пробелов
*
* минус учли
*
* мимо пробелов, кои юзеры любят ставить после минуса
*
* считаем, что число кончилось
*
* а теперь там была точка, значит, дробная часть пошла
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

s[inout] Нуль-терминированная строка.

Результат:

Результат преобразования.

char c_e(char c)

Преобразует символ в код BCD*.

Используемое отображение: „0“..“9“ -> 0..9, „*“ -> 0x0A, „+“ -> 0x0B, „#“ -> 0x0C, „@“ или „\0“ -> 0x0F (конец). При ошибке возвращает 0xFF и увеличивает preobra_err.

* символ -> BCD* (FF - ошибка).
*
* конец.
*
* ошибка.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

c[in] Входной символ.

Результат:

Код BCD* (0x00..0x0F), либо 0xFF при ошибке.

char e_c(char c)

Преобразует код BCD* в символ.

При ошибке возвращает 0xFF и увеличивает preobra_err.

* BCD* -> символ (FF - ошибка).
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

c[in] Код BCD*.

Результат:

Соответствующий символ.

char c_d6(char *s, char *r)

Преобразует 6 ASCII-цифр в 3 байта BCD.

Каждая пара цифр упаковывается в один байт: десятки в старшем полубайте, единицы в младшем. При ошибке формата увеличивает preobra_err.

* символы (6 цифр) -> BCD (3 байта).
*
* ret: !=0 - ошибка.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:
  • s[inout] Указатель на 6 символов „0“..“9“.

  • r[inout] Буфер минимум из 3 байт для результата.

Результат:

0 при успехе; ненулевое значение при ошибке формата.

void d3_c(char *s, char *r)

Преобразует 3 байта BCD в строку из 6 ASCII-цифр.

* BCD (3 байта) -> символы (6 цифр).
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:
  • s[inout] Буфер BCD (3 байта).

  • r[inout] Буфер минимум из 6 байт для результата.

void d7_c(char *s, char *r)

Преобразует 7 байт BCD в строку из 14 ASCII-цифр с завершающим нулем.

* BCD (7 байт) -> символы (14 цифр), затем 0x00.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:
  • s[inout] Буфер BCD (7 байт).

  • r[inout] Буфер минимум из 15 байт (14 цифр и завершающий „\0“).

void c7_d(char *s, char *r)

Преобразует 14 ASCII-цифр в 7 байт BCD.

Функция выполняет упаковку двух символов в один байт: старший полубайт формируется из s[2*i] (сдвиг << 4), младший - из s[2*i+1]. Функция предполагает, что входные символы уже подготовлены под такую упаковку.

* символы (14 цифр) -> BCD (7 байт).
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:
  • s[inout] Указатель на 14 символов.

  • r[inout] Буфер минимум из 7 байт для результата.

void I2C2_SDA_to_in(void)

Переключает линию SDA программного I2C2 в режим входа (прием).

Настраивает GPIO как INPUT с подтяжкой вверх (PULLUP). Используется для чтения SDA (ACK/данные).

* Переключение линии I2C2 на приём
*
* на вс.сл.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

void I2C2_SDA_to_out(void)

Переключает линию SDA программного I2C2 в режим выхода (передача).

Настраивает GPIO как PUSH-PULL OUTPUT. Используется для формирования уровней SDA.

* Переключение линии I2C2 на передачу
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

void delay_I2C_q_bit(void)

Программная задержка примерно на четверть битового интервала I2C.

Реализована пустым циклом. Фактическая длительность зависит от частоты CPU и оптимизации компилятора.

* задержка на четверть бита: 0.62 мкс (для 400 кГц) или 2.5 мкс (для 100 кГц) =+= ПОКА для 100 кГц ВЫСТАВЛЕНО ИНТУИТИВНО, БЕЗ РАСЧЁТА
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях фиксируются временные характеристики/измерения.

Предупреждение

Длительность задержки не является строго детерминированной.

void delay_I2C_h_bit(void)

Программная задержка примерно на половину битового интервала I2C.

Реализована пустым циклом. Фактическая длительность зависит от частоты CPU и оптимизации компилятора.

* задержка на полбита: 1.25 мкс (для 400 кГц) или 5 мкс (для 100 кГц) =+= ПОКА для 100 кГц ВЫСТАВЛЕНО ИНТУИТИВНО, БЕЗ РАСЧЁТА
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях фиксируются временные характеристики/измерения.

Предупреждение

Длительность задержки не является строго детерминированной.

void ourI2C_init(int i2c_n)

Инициализирует программную (bit-bang) реализацию I2C.

По параметру i2c_n выбирается логический номер шины. В текущей реализации полноценно настроен вариант i2c_n==2; для i2c_n==1 предусмотрена заготовка. После настройки выдерживается временная пауза.

* Инициализация интерфейса.
*
* здесь для I2C1 надо всё поставить
*
* на вс.сл.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Примечание

Реализация ourI2C является минимальной: repeated start не реализован (см. комментарии в коде).

Параметры:

i2c_n[in] Номер программной шины I2C (ожидается 1 или 2).

void ourI2C_start(int i2c_n)

Формирует условие START на выбранной программной I2C-шине.

* Последовательность "Start"
*
* здесь для I2C1 надо всё поставить
*
* на вс.сл.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

i2c_n[in] Номер программной шины I2C.

void ourI2C_stop(int i2c_n)

Формирует условие STOP на выбранной программной I2C-шине.

* Последовательность "Stop"
*
* на вс.сл.
*
* здесь для I2C1 надо всё поставить
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

i2c_n[in] Номер программной шины I2C.

uint8_t ourI2C_send(int i2c_n, uint8_t b)

Передает один байт по программному I2C и считывает ACK/NAK.

Передает 8 бит (MSB first), затем переводит SDA в вход и считывает ACK. Возвращаемое значение соответствует состоянию SDA в фазе ACK: 0 - ACK, 1 - NAK.

* Передача байта и получение ACK (0) или NAK (1).
*
* как бы NAK (чтобы грязь случайно не вернулась, если войдём с нереализованным i2c_n)
*
* на вс.сл.
*
* здесь для I2C1 надо всё поставить
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях отражены правила обработки ACK/NAK.

Параметры:
  • i2c_n[in] Номер программной шины I2C.

  • b[in] Передаваемый байт.

Результат:

0 при ACK; 1 при NAK.

uint8_t ourI2C_receive(int i2c_n, uint8_t nak)

Принимает один байт по программному I2C и передает ACK/NAK.

Считывает 8 бит (MSB first), затем выставляет на SDA ответный бит: ACK (0) при nak==0 или NAK (1) при nak!=0.

* Получение байта и передача ACK (0) или NAK (1).
*
* здесь для I2C1 надо всё поставить
*
* на вс.сл.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях отражены правила обработки ACK/NAK.

Параметры:
  • i2c_n[in] Номер программной шины I2C.

  • nak[in] 0 - передать ACK; ненулевое значение - передать NAK.

Результат:

Принятый байт.

HAL_StatusTypeDef ourI2C_master_transmit(int i2c_n, uint8_t addr, uint8_t *mas, uint16_t len)

Выполняет транзакцию I2C master transmit: START + SLA+W + data… + STOP.

Если ведомое устройство отвечает NAK на адрес или на данные, транзакция прекращается и возвращается HAL_TIMEOUT. В реализации repeated start отсутствует.

* младший бит - "запись"
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:
  • i2c_n[in] Номер программной шины I2C.

  • addr – Адрес ведомого (7-битный адрес в старших битах байта адреса; младший бит используется как R/W).

  • mas[in] Буфер данных для передачи.

  • len[in] Количество передаваемых байт.

Return values:
  • HAL_OK – Передача завершена успешно.

  • HAL_ERROR – Неверные параметры (len==0).

  • HAL_TIMEOUT – NAK от ведомого на адресе или данных.

Результат:

Статус выполнения (HAL_OK при успехе, HAL_ERROR при len==0, HAL_TIMEOUT при NAK).

HAL_StatusTypeDef ourI2C_master_receive(int i2c_n, uint8_t addr, uint8_t *mas, uint16_t len)

Выполняет транзакцию I2C master receive: START + SLA+R + read… + STOP.

На последнем байте чтения передается NAK (для завершения обмена), на остальных байтах - ACK.

* младший бит - "чтение"
*
* последний байт принимается с NAK, а остальные с ACK - В ОТЛИЧИЕ ОТ БАГИ В HAL, ГДЕ ВСЁ ПРИНИМАЕТСЯ С ACK
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях отражены правила обработки ACK/NAK.

Параметры:
  • i2c_n[in] Номер программной шины I2C.

  • addr – Адрес ведомого (7-битный).

  • mas[out] Буфер приемника.

  • len[in] Количество читаемых байт.

Return values:
  • HAL_OK – Чтение завершено успешно.

  • HAL_ERROR – Неверные параметры (len==0).

  • HAL_TIMEOUT – NAK от ведомого на адресе.

Результат:

Статус выполнения (HAL_OK при успехе, HAL_ERROR при len==0, HAL_TIMEOUT при NAK на адресе).

char sdi_letter(int n)

Преобразует номер устройства SDI-12 в символ адреса.

Отображение: 0..9 -> „0“..“9“, 10..35 -> „A“..“Z“, 36..61 -> „a“..“z“. При выходе n за диапазон [0;61] возвращается символ „*“.

* номер устройства -> адрес (0..9, A..Z, a..z - всего 62 шт)
*
* wrong dev number
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

n – Номер устройства SDI-12 (0..61).

Результат:

char.

int sdi_number(char c)

Преобразует символ адреса SDI-12 в номер устройства.

Поддерживаются „0“..“9“, „A“..“Z“, „a“..“z“. Символ „*“ обрабатывается как специальный случай и возвращает 128.

* адрес устройства -> номер
*
* ну так придумалось.
*
* wrong dev letter
*
* по-прежнему wrong dev letter
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

c[in] Символ адреса.

Результат:

Номер устройства (0..61); 255 при ошибке; 128 при c==“*“.

void F_port_init_one(volatile SDI_t *sdi)

Инициализирует GPIO для одного порта SDI-12.

Настраивает линию данных как вход с pull-down. Если используется отдельная линия направления (dir), она настраивается как выход.

* инициализация пинов GPIO для одного порта SDI12.
*
* (ибо если совпали - сигнал dir не используется).
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

sdi[inout] Указатель на структуру состояния порта SDI-12.

void F_port_init()

Инициализирует GPIO для всех включенных портов SDI-12.

Проходит по массиву sdi_ports[] и вызывает F_port_init_one() для портов, помеченных как enabled.

* (В реализации отсутствуют отдельные поясняющие комментарии.)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

void sdi_to_in(volatile SDI_t *sdi)

Переключает SDI-12 порт в режим приема (GPIO input).

Если порт находился в режиме передачи, перенастраивает data-pin в INPUT с pull-down, а при наличии линии dir переводит направление в режим приема. Поле sdi->enabled обновляется на 1 (in).

* не забыть переставить таймер на 100 мкс, если хочется
*
* он сейчас в out
*
* порт -> приём    эта штука выполняетя около 10 мкс!
*
* (ибо если совпали - сигнал dir не используется).
*
* DIR -> 0: приём (передача B->A)
*
* теперь в in
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях фиксируются временные характеристики/измерения.

Параметры:

sdi[inout] Порт SDI-12.

void sdi_to_out(volatile SDI_t *sdi)

Переключает SDI-12 порт в режим передачи (GPIO output).

Если порт находился в режиме приема, устанавливает направление передачи (dir=1 при наличии) и перенастраивает data-pin в OUTPUT push-pull. Поле sdi->enabled обновляется на 2 (out).

* не забыть переставить таймер на 104.16666 мкс, если он сейчас по 100 мкс
*
* он сейчас в in
*
* (ибо если совпали - сигнал dir не используется).
*
* DIR -> 1 (передача A->B)
*
* порт -> передача    эта штука выполняетя около 10 мкс!
*
* теперь в out
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях фиксируются временные характеристики/измерения.

Параметры:

sdi[inout] Порт SDI-12.

uint8_t sdi_getbit(volatile SDI_t *sdi)

Считывает состояние линии SDI-12.

Возвращаемое значение используется в инверсной логике протокола, принятой в коде: 0 соответствует mark, 1 соответствует space (см. комментарии в реализации).

* результат: 0 - mark, 1 - space (инверсная логика)
*
* принять data с порта SDI12 - результат отдаём В ИНВЕРСНОЙ ЛОГИКЕ !!!
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

sdi[inout] Порт SDI-12.

Результат:

0 или 1 в принятой в проекте интерпретации.

void sdi_putbit(volatile SDI_t *sdi, uint8_t b)

Формирует уровень на линии SDI-12 (передача одного бита).

Передаваемый параметр b интерпретируется как „прямая логика“ на уровне алгоритма, а на GPIO выставляется инверсное соответствие (mark/space) согласно реализации.

* переданный параметр В ПРЯМОЙ ЛОГИКЕ !!!
*
* mark (т.е. надо 0 в инверсной логике)
*
* выдать 0 (mark = 0) в порт SDI12
*
* space (т.е. надо 1 в инверсной логике)
*
* выдать 1 (space = 1) в порт SDI12
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:
  • sdi[in] Порт SDI-12.

  • b[in] Передаваемый бит (в логике, принятой в алгоритме).

void putsdi(volatile SDI_t *sdi, uint8_t data)

Добавляет байт в буфер передачи SDI-12.

Записывает байт в кольцевой TX-буфер порта. При заполненном буфере выполняет ожидание (spin-wait) освобождения места.

* байт -> буфер SDI12 (для передачи)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях описаны инварианты буферов и условия переполнения.

Предупреждение

Функция может блокировать выполнение при заполненном TX-буфере.

Параметры:
  • sdi[in] Порт SDI-12.

  • data[in] Байт для отправки.

uint8_t getsdi(volatile SDI_t *sdi)

Извлекает байт из буфера приема SDI-12.

При пустом RX-буфере возвращает 0. Из принятого байта очищается бит четности (data &= 0x7F). При чтении символа LF (0x0A) уменьшается счетчик строк in_lines_counter.

* чтение байта из буфера SDI12
*
* Не забудь: в буфере лежат байты с чётностью!
*
* при пустоте буфера возвращаем нолик
*
* чётность отрезали
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях описаны инварианты буферов и условия переполнения.

Параметры:

sdi[inout] Порт SDI-12.

Результат:

Принятый байт (без бита четности) или 0 при пустом буфере.

void wakesdi(volatile SDI_t *sdi)

Запускает передачу SDI-12 с последовательностью wake-up (break/marking) и последующей командой.

Функция переводит конечный автомат порта в состояние wake-up (sdi->s=20).

* старт передачи (wake, spacing, передача буфера (в нём уже должна лежать команда), переключение на приём)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях описаны инварианты буферов и условия переполнения.

Параметры:

sdi[inout] Порт SDI-12.

void sendsdi(volatile SDI_t *sdi)

Запускает передачу буфера SDI-12 без стадии wake-up.

Функция переводит конечный автомат порта в состояние передачи байта (sdi->s=10).

* старт передачи буфера (без wake)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях описаны инварианты буферов и условия переполнения.

Параметры:

sdi[in] Порт SDI-12.

void obrabotka_sdi(volatile SDI_t *sdi)

Обработчик конечного автомата SDI-12 (битовый уровень приема/передачи).

Должен вызываться периодически (обычно из обработчика таймера) для каждого активного порта. Реализует состояния ожидания стартового бита, прием битов и стопового бита, учет четности, а также передачу (start/data/parity/stop) и стадию wake-up. Использует поля структуры SDI_t как состояние автомата и буферы обмена.

* внимание, переделка (см. case 0: case 1:) - датчик слушаем постоянно, и если что-то неожиданно скажет - примем!
*
* if (sdi->enabled) убран сознательно, т.к. переставлен в for, где вызывается.
*
* бит, принятый с линии
*
* пауза, ничего не делаем
*
* сейчас комментируем, чтобы можно было принять "неожиданную" посылку от датчика (а вдруг?)
*
* sdi->t = KTICK_S;                  // просто подготовка, чтобы снаружи лишь sdi->s в единицу поставить осталось.
*
* break;
*
* надо бы принять байт: ожидание стартового бита
*
* реальное переключение занимает аж 10 мкс!
*
* есть бит!
*
* переход к приёму посылки
*
* не дождались...
*
* потому что тут sdi->s точно 0: оттуда провалились сюда, поскольку там break закомментирован
*
* (в sdi->s=1 приходим обязательно с ненулевым sdi->t)
*
* контроль стартового бита
*
* достигли середины стартового бита
*
* сорвался стартовый бит...
*
* принимаем байт по одному биту
*
* достигли середины бита
*
* это бит байта (ещё не стоповый)
*
* инверсная логика
*
* это стоповый бит
*
* if (q) ;                                 // space в стоповом бите - это плохо! =+= (пока игнорим это, считаем стартовым)
*
* else                                     // mark в стоповом бите - всё в порядке, обработаем байт
*
* стоповый бит правильный
*
* здесь новые байты просто пропадают.
*
* =+=
*
* вместе с чётностью!!!
*
* на ожидание следующего стартового бита
*
* if(q)
*
* =+= считаем space в стоповом бите за стартовый бит следующего байта (и на 300 мкс отстутпим, мы ж каждый бит спешим на 33.333 мкс)
*
* надо передать байт
*
* а передавать-то и нечего...
*
* ...значит, пора принимать.
*
* это максимальное время ожидания ответа.
*
* хотим принимать ответ.
*
* начало стартового бита
*
* следующим будем передавать младший бит
*
* передача бита
*
* достигли начала бита
*
* следующей должна быть передана чётность
*
* следующим надо передать стоповый бит
*
* передача стопового бита
*
* достигли начала стопового бита
*
* ожидание конца передачи стопового бита и переход к следующему байту
*
* достигли конца стопового бита
*
* передача break (wake up), затем команды
*
* space
*
* ждём конца передачи break (wake up)
*
* дождались - переходим к marking
*
* ждём конца передачи marking после wake up
*
* дождались
*
* ошибка алгоритма
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях фиксируются временные характеристики/измерения.

Примечание

Обычно вызывается из HAL_TIM_PeriodElapsedCallback() в цикле по массиву sdi_ports[].

Параметры:

sdi[inout] Порт SDI-12.

uint16_t crc16(char *buffer, uint16_t length)

Вычисляет CRC16 по буферу данных (алгоритм, совместимый с Modbus).

Используется табличный алгоритм с инициализацией CRC = 0xFFFF и таблицами старшего/младшего байта. Результат возвращается в виде (crc_hi<<8 | crc_lo).

* high CRC byte initialized
*
* low CRC byte initialized
*
* index into CRC lookup
*
* pass through message buffer
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:
  • buffer – Указатель на буфер данных.

  • length[in] Длина буфера в байтах.

Результат:

uint16_t.

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

Callback HAL по переполнению таймера; реализует периодические задачи системы.

Ветка TIM1 используется как системный тик (в комментариях указана периодичность около 100 мкс) и выполняет обновление счетчиков времени, индикацию ошибок и обслуживание SDI-12 FSM. Функция вызывается HAL автоматически при срабатывании прерывания таймера.

* check if the interrupt comes from TIM1 -- каждые 100 мкс -- сделал 104
*
* индикатор для осциллографа, чтобы замерить длительность прерывания.
*
* "Часы":
*
* Период опроса в тиках таймера по 104 мкс.
*
* МИГАЛКА ошибок IER:
*
* активно мгаем текущую ошибку.
*
* время горения светодиода - часть периода мигания (i после 0 прыгает вверх, затем уменьшается).
*
* миги текущей ошибки завершили - нужна пауза.
*
* после последней ошибки в серии - пауза подольше.
*
* т.е. пауза после мигания ошибки.
*
* отстрелялись, наконец, текущую ошибку домигали, паузу подождали.
*
* это была "одноразовая" ошибка. Удалить её из массива.
*
* ...и снова весь массив ошибок мигать.
*
* это вот сколько по 0.1 мс всего мигание продолжится.
*
* end of if (nmer)
*
* Обработка SDI-12:
*
* всё, отстрелялись)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях фиксируются временные характеристики/измерения.

Параметры:

htim[in] Дескриптор таймера, от которого пришло прерывание.

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)

Callback HAL по завершению передачи UART; продолжает выдачу данных из TX-буферов.

Выбирает соответствующий UART по указателю huart и, при наличии данных, отправляет следующий байт через HAL_UART_Transmit_IT(). Используется для реализации неблокирующей передачи через кольцевые буферы и (при необходимости) управления направлением RS-485.

* (В реализации отсутствуют отдельные поясняющие комментарии.)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

huart[in] UART, по которому завершилась передача.

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

Callback HAL по приему байта UART; помещает байт в RX-буфер и перезапускает прием.

Помещает принятый байт в кольцевой буфер, обновляет счетчики (в т.ч. счетчик строк при CR/LF) и вызывает HAL_UART_Receive_IT() для приема следующего байта. При переполнении RX-буфера выполняется сброс очереди (очистка индексов и счетчиков) с регистрацией события в статистике.

* вычищаем очередь. Можно вычищать только старые непрочитанные байты, но жутко лениво. Игнорировать приходящие тоже стрёмно, потому что если буфер заполнен мусором, там никогда не окажется конца строки, и обработка встанет.
*
* u_e_sen_1=0;
*
* u_e_sen_2=0;
*
* вычищаем очередь. Можно вычищать старые непрочитанные байты, но жутко лениво.
*
* u_e_mik_1=0;
*
* u_e_mik_2=0;
*
* u_e_lcd_1=0;
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях описаны инварианты буферов и условия переполнения.

Параметры:

huart[in] UART, по которому принят байт.

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)

Callback HAL по ошибке UART; диагностирует тип ошибки и восстанавливает прием.

Считывает код ошибки через HAL_UART_GetError(), регистрирует ее и выполняет восстановительные действия (включая перезапуск HAL_UART_Receive_IT()). Разделяет обработку по конкретным UART (SEN/MIK/LCD), что позволяет вести независимую статистику и таймеры восстановления.

* if (er==0) IER (..);                // =+= // (ну а вдруг?) (точно, блин, угадал. Черезжопие библиотеки даёт всегда нулевую ошибку).
*
* (все заняты - заменим последнюю ошибку)
*
* дададада!
*
* это универсальная шняга, чистит сразу все флаги просто чтением пары регистров.
*
* (хотя лишнее это, всё равно там и так ноль, блинский блин!)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

huart[in] UART, на котором произошла ошибка.

uint8_t bcd2dec(uint8_t v)

Преобразует байт из BCD-представления в десятичное значение.

Вычисление: (v>>4)*10 + (v & 0x0F).

* (В реализации отсутствуют отдельные поясняющие комментарии.)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

v[in] BCD-байт (старший полубайт - десятки, младший - единицы).

Результат:

uint8_t.

HAL_StatusTypeDef pca2129_write(uint8_t reg, const uint8_t *data, uint16_t len)

Записывает последовательность регистров RTC PCA2129 по I2C.

Формирует буфер вида [reg][data…] и передает его одной транзакцией через ourI2C_master_transmit() по шине I2C №2. Текущая реализация ограничивает len значением 16.

* для маленьких записей; при необходимости увеличь
*
* Figure 36: START, SLA+W, reg addr, data..., STOP
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:
  • reg[in] Адрес стартового регистра PCA2129.

  • data[in] Буфер данных для записи.

  • len[in] Количество байт для записи (не более 16).

Результат:

Статус HAL выполнения операции.

HAL_StatusTypeDef pca2129_read(uint8_t reg, uint8_t *data, uint16_t len)

Читает последовательность регистров RTC PCA2129 по I2C.

Выполняет запись адреса регистра (ourI2C_master_transmit), затем чтение данных (ourI2C_master_receive) по шине I2C №2.

* 1) Set register address (write), then STOP
*
* 2) Read transaction (separate START + SLA+R)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:
  • reg[in] Адрес стартового регистра PCA2129.

  • data[out] Буфер приемника.

  • len[in] Количество байт для чтения.

Результат:

Статус HAL выполнения операции.

HAL_StatusTypeDef pca2129_set_time()

Устанавливает время/дату в RTC PCA2129 из глобальной структуры default_time.

Сбрасывает флаг OSF в поле секунд (бит 7), затем выполняет пакетную запись 7 байт в диапазон регистров 03h..09h (секунды, минуты, часы, день, день недели, месяц, год). Записываемые значения предполагаются уже в BCD-представлении.

* OSF сбрасываем (бит7 Seconds), иначе время считается недостоверным
*
* Пакетная запись 03h..09h одним доступом
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Примечание

Функция использует ourI2C_master_transmit() по шине I2C №2 и адрес PCA2129_ADDR.

Результат:

Статус HAL выполнения операции записи.

void pca2129_print_time(void)

Считывает текущее время из PCA2129 и формирует строку time_string.

Если счетчик rtc_hms ненулевой, функция возвращается без обновления (ограничение частоты опроса). Выполняет чтение 7 байт из регистров 03h..09h. При установленном OSF (Seconds bit7) формирует диагностическое сообщение об ошибке. Иначе формирует строку формата «DD/MM/YYYY_HH_MM_SS» в глобальном буфере time_string. В конце устанавливает rtc_hms=2000 для ограничения частоты обновления.

* Сформировать глобальную строку time_string с текущим таймстампом.
*
* рановатенько вызвали
*
* Чтение 03h..09h одним доступом
*
* 24h mode
*
* uint8_t wd = (raw[4] & 0x07);          // 0..6
*
* OSF=1 => время могло быть нарушеноо
*
* т.е. если следующий запрос придёт раньше, чем через 200 мс, строка останется прежней.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Примечание

Таймерный счетчик rtc_hms уменьшается в HAL_TIM_PeriodElapsedCallback().

uint32_t rd_plus(void)

Увеличивает индекс чтения rd_index в кольцевом буфере buffer[].

Выполняет инкремент rd_index с циклическим переходом по модулю BUFFER_SIZE и уменьшает счетчик занятых байт counter.

* (В реализации отсутствуют отдельные поясняющие комментарии.)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Результат:

Новое значение rd_index.

uint32_t wr_plus(void)

Увеличивает индекс записи wr_index в кольцевом буфере buffer[].

Выполняет инкремент wr_index с циклическим переходом по модулю BUFFER_SIZE и увеличивает счетчик занятых байт counter.

* (В реализации отсутствуют отдельные поясняющие комментарии.)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Результат:

Новое значение wr_index.

void wr_buf(uint8_t data)

Записывает один байт в кольцевой буфер buffer[] с контролем переполнения.

Если буфер полон, функция удаляет самую старую строку целиком (строки начинаются с „:“) и затем записывает новый байт. При обнаружении неконсистентности индексов/структуры данных регистрирует ошибки через IER().

* разрушены указатели (должны совпадать при пустом и при полном буфере)
*
* разрушена информация (в буфере должны быть строки целиком, они начинаются с ':')
*
* Выкидываем самую старую строку целиком
*
* Если ничего не считывалось, rd_index обязан стоять на ':'
*
* прибавили 1 к rd_index, потом проверили, что в буфере по этому индексу не ':'
*
* когда дойдем до следующего ':' - это будет началом новой строки
*
* чтобы не встать в раскоряку, если съели весь буфер
*
* выкинули самую старую строку целиком
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях описаны инварианты буферов и условия переполнения.

Предупреждение

При полном буфере данные старейшей строки будут удалены (политика „drop oldest line“).

Параметры:

data[in] Записываемый байт.

uint8_t rd_buf(void)

Читает один байт из кольцевого буфера buffer[].

Если буфер пуст (counter==0), возвращает 0. При неконсистентности индексов/счетчика регистрирует ошибку IER().

* (В реализации отсутствуют отдельные поясняющие комментарии.)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Результат:

Прочитанный байт или 0 при пустом буфере.

void wr_str(char *st)

Записывает одну строку (одну запись датчика) в кольцевой буфер передачи.

Строка должна начинаться с „:“ и содержать ровно одну строку (до CRLF). Функция записывает содержимое до 0x0D, проверяет корректность окончания строки и добавляет CRLF.

* положить в буфер передачи ОДНУ строку (с датчика должна быть одна). Если в st больше одной строки - будет ошибка.
*
* Первый символ строки, которую хотим записать, должен быть ':'
*
* Если мы не встретили конец строки, то записываем символ
*
* если конец строки неполный или после конца лишнее - мгаем ошибку.
*
* Допередаем конец строки
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях описаны инварианты буферов и условия переполнения.

Предупреждение

При обнаружении некорректного формата вызываются IER().

Параметры:

st[inout] Нуль-терминированная строка, содержащая одну запись.

void rd_str(char *st, size_t len)

Считывает одну строку из кольцевого буфера и удаляет завершающие CRLF.

Читает байты из буфера до символа CR (0x0D), затем ожидает LF (0x0A) и завершает строку нулем. При нехватке места в st или при разрушении данных регистрирует ошибки через IER().

* прочесть строку и отрезать от неё 0D0A       // Эта функция вроде как сейчас не нужна...
*
* ибо не фига!
*
* ибо, кроме концевика, туда уже ничего не засунешь.
*
* Нас попросили отдать, а буфер-то пустенький
*
* беда, буфер разрушился
*
* Буфер кончился посередине строки!
*
* конец строки - отрезать (и из буфера вынуть 0A)
*
* Строка из буфера не влезла в st!
*
* встали на последний символ (потом туда 0 положим)
*
* доедаем из буфера оставшийся хвост строки
*
* ну конец строки-то в st нужно внедрить в любом случае, ага?
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях описаны инварианты буферов и условия переполнения.

Параметры:
  • st[inout] Буфер для результата.

  • len[in] Размер буфера.

void print_interface(char *str)

Выводит строку в активные интерфейсы согласно флагам fl_*.

Проверяет флаги активности интерфейсов (fl_sen_1, fl_sen_2, fl_mik_1, fl_mik_2, fl_lcd_1, fl_usb) и отправляет строку в соответствующие драйверы UART/USB. Используется как единая точка маршрутизации ответов командного интерфейса.

* (В реализации отсутствуют отдельные поясняющие комментарии.)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

str – Нуль-терминированная строка для вывода.

void print_interface_char(uint8_t ch)

Выводит один символ в активный интерфейс (UART/USB) согласно флагам fl_*.

* (В реализации отсутствуют отдельные поясняющие комментарии.)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Параметры:

ch[in] Символ для вывода.

int finite_state_machine(void)

Извлекает из кольцевого буфера и выдает в интерфейс ровно один завершенный цикл опроса датчиков.

Считывает данные из buffer[] и передает их в текущий интерфейс (print_interface_char()). Завершение цикла определяется по обнаружению маркера «[end]\r\n» в передаваемом потоке. Если буфер пуст или в нем нет полностью завершенного цикла (например, цикл еще записывается), функция возвращает 1.

* возвращает 1, если буфер изначально оказался пустым или в нём не лежит ни одного полного цикла опроса
*
* буфер пуст, отдавать нечего.
*
* в данный момент идёт запись именно этого цикла, отдавать его рановато.
*
* читаем из кольцевого буфера
*
* буфер, блин, пуст...
*
* отправляем в интерфейс, которому выставлен флаг
*
* Буфер кончился слишком неожиданно! (выскочили по break)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях описаны инварианты буферов и условия переполнения.

Предупреждение

Функция потребляет данные из буфера (rd_index/counter изменяются).

Результат:

0 при успешной выдаче одного цикла; 1 если выдача невозможна (нет данных или нет полного цикла).

void turn_dialog_to_slave(void)

Временно переключает вывод/диалог на интерфейс связи с CBS-slave.

Сохраняет текущие флаги интерфейсов fl_* в fd_*, затем активирует только fl_mik_1 (остальные флаги сбрасывает). Используется для отправки запроса slave-у и приема ответа.

* (В реализации отсутствуют отдельные поясняющие комментарии.)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

void return_dialog_from_slave(void)

Восстанавливает флаги интерфейсов после временного переключения на CBS-slave.

Возвращает значения fl_* из сохраненных fd_*.

* (В реализации отсутствуют отдельные поясняющие комментарии.)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

void interface_dialog(void)

Обрабатывает одну входную строку команды/запроса, размещенную в str_dial.

Нормализует строку (обрезает CR/LF), сохраняет оригинал в str_ori, приводит str_dial к нижнему регистру. Далее выполняет разбор интерфейсных команд (например, „:GET“, „:TIM“ и др.), осуществляет обмен с CBS-slave при необходимости (через turn_dialog_to_slave/return_dialog_from_slave), управляет временем RTC и параметрами системы, формирует ответы через print_interface(). Функция использует глобальные буферы и переменные состояния проекта.

* чтобы каждый раз не обрабатывать, просто отрежем. Багофича: заодно отрежем всё после crlf.
*
* чтобы в сенсоры пихать без изменения - если, например, в датчик sdi что-то говорим командой sdi.
*
* для удобства strcmp.
*
* Костыль для RS-485 на скорости 9600: мы только что приняли 0D (конец строки). А пока юзер говорит 0A, надо молчать в тряпочку во избежание коллизии. Без этой задержки команда успевает обработаться и попроситься в порт раньше конца 0A, и у юзера наблюдается мусор.
*
* своими соплями не давимся, а чужими тем более, даааааа...
*
* ////////// ИНТЕРФЕЙСНЫЕ КОМАНДЫ
*
* Интерфейсная команда
*
* ////// Варианты про Master ///////
*
* GET
*
* Значит, мы CBS-master, и опрос должен быть циклическим.
*
* пони бегает по кругу...
*
* Значит, это индивидуальный опрос через мастера (например: ":GET01-04")
*
* моя твоя не понимай...
*
* ОПРОС СИСТЕМЫ n_sys_dial:n_cbs_dial
*
* это мы сами, отправим свой буфер конечным автоматом
*
* в буфере было пусто - надо отправить холостой таймстамп с [end]
*
* ДОПРОСИТЬ СЛЕЙВА И ОТПРАВИТЬ ОТВЕТ НА :GET
*
* <-------------- теперь диалог переключен на UART_MIK_1
*
* подождём ответа
*
* ага, отвечает!
*
* Принять всё, что отвечает, в накопительный буфер buffer_slave (тек. длина buffer_slave_counter).
*
* Переполнение буфера приёма информации от slave по :GET
*
* просто доедим остаток
*
* не ответил...
*
* пока - просто пропускаем
*
* чтобы лишнюю грязь в интерфейс не засылал
*
* Отдать буфер запрашивающему мастеру.
*
* <-------------- теперь диалог вёрнут обратно
*
* В накопительном буфере что-то есть.
*
* Всё послали запрашивающему.
*
* В накопительном буфере ничего нет.
*
* end of ДОПРОСИТЬ СЛЕЙВА
*
* end of ОПРОС СИСТЕМЫ
*
* end of if "GET"
*
* Значит, мы CBS-master, и от нас требуют какой-либо установки
*
* ...
*
* Значит, мы устанавливаем время всей системе
*
* Сначала устанавливаем время на самой CBS
*
* (чтобы не потерять номер теплицы-квадрата)
*
* Переводим 14 символов -> 7 байт bcd. Формат ввода времени: ": tim ss mm hh DW DD MM YY" (без пробелов, всего 1 символ, 3 буквы и 14 цифр)
*
* у нас при приёме отрезаются \r\n
*
* Вернуть туда откуда пришло
*
* проверка формата строки
*
* вот тут надо и в таймер, и во flash записать.
*
* 14 символов -> 7 байт bcd
*
* там tdelay должен быть.
*
* (ошибка формата)
*
* tdelay теперь int, столько вполне влезет
*
* Теперь посылаем время другим CBS
*
* (против "здравствуй, зеркало")
*
* Слушаем и накапливаем ответ.
*
* Переполнение буфера приёма информации от slave по :TIM
*
* end of if (i != default_time.CBSN)
*
* end of for (i=1; i <= TOTAL_CBS; ++i)
*
* end of запись в таймер и во флеш
*
* end of проверка формата строки
*
* end of Установка времени всей системе
*
* /////// Варианты про Slave ///////
*
* Значит, мы, возможно, услышали команду от CBS-master
*
* Значит, это наш номер, надо посмотреть команду и выполнить её
*
* ОБРАБОТКА GET (slave)
*
* Значит от нас требуют наш буфер
*
* ОБРАБОТКА tim (slave)
*
* Значит, надо установить время.
*
* /// Тут почти то же самое. Сдвиг на 5 символов и чуть другой формат ответа. Можно сделать вложенную функцию, но для этого надо будет менять вид команд, приходящих извне.
*
* Переводим 14 символов -> 7 байт bcd. Формат ввода времени: tim ss mm hh DOW DD MM YY (без пробелов, всего 3 буквы и 14 цифр)
*
* поскольку tdelay теперь int, такое вполне влезет
*
* Установка какая-нибудь
*
* end of Варианты про slave
*
* Неверная интерфейсная команда!
*
* end of Интерфейсная команда (которая начинается с двоеточия)
*
* ////////// ПОЛЬЗОВАТЕЛЬСКИЕ КОМАНДЫ
*
* Чтобы сразу закончить отсчет tdelay
*
* ЭТО ДАТЧИК ДАВЛЕНИЯ ВОЗДУХА!!!
*
* чтение регистра из сенсора modbus: modrPnnnAAAA (P - порт x/y, nnn - десятичный адрес slave, AAAA - 16-ричный адрес регистра)
*
* адрес устройства Modbus (slave address)
*
* команда 0x03: Read Holding Registers
*
* адрес регистра (старший байт)
*
* адрес регистра (младший байт)
*
* количество регистров (старший байт)
*
* количество регистров (младший байт): 1
*
* CRC16 по первым 6 байтам
*
* Отправляем запрос
*
* это ожидание начала ответа. Giant521 в реальности отвечает за 18..24 мс, иногда несколько дольше.
*
* Сенсор начал отвечать.
*
* а это ожидание окончания ответа (пауза между байтами 3 мс).
*
* против переполнения: остаток выкидываетя.
*
* 2 байта данных
*
* Запрос пошёл!
*
* против переполнения: обрезаем остаток.
*
* вот и ответили, наконец-то, юзеру
*
* запись регистра из сенсора modbus: modwPnnnAAAAkkk (P - порт x/y, nnn - десятичный адрес slave, AAAA - 16-ричный адрес регистра, kkk - десятичное значение для записи)
*
* команда 0x06: Write Single Holding Register
*
* значение (старший байт)
*
* значение (младший байт)
*
* обрежем остаток при переполнении.
*
* и опять защищаемся от переполнения.
*
* поскольку это чисто отладочное, о guard-символе (FIGA) не заботимся.
*
* __disable_irq();
*
* __enable_irq();
*
* Диалог с запросившим портом закончен, его флаг выключится в handler_all.
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: В комментариях указаны предпосылки по организации Flash. В комментариях описаны инварианты буферов и условия переполнения. В комментариях упоминается управление направлением/таймингами RS-485.

Предупреждение

Функция опирается на глобальные переменные (str_dial, default_time, fl_*, буферы и счетчики) и предназначена для использования внутри общего обработчика handler_all().

void handler_all(void)

Главный обработчик ввода: выбирает интерфейс, извлекает строки и вызывает interface_dialog().

Проверяет счетчики строк lines_counter для USB и UART-портов. Пока в выбранном интерфейсе есть полные строки, читает строку функцией ), вызывает interface_dialog(), затем возвращает управление в основной цикл.

* строка непуста (ведь 0a после 0d превращается в лишнюю пустую строку, которую обрабатывать незачем)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

Примечание

Предназначен для вызова в основном цикле программы (main loop).

Variables

struct def_time default_time
char time_string[MAXSTRLEN] = {0}

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: time_string. Объявление (как в исходном коде):

char time_string[MAXSTRLEN] = {0};

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

int n_cbs = 0

Номер последней slave-системы, опрошенной master’ом

Имена переменных: n_cbs. Объявление (как в исходном коде):

int n_cbs=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* Номер последней slave-системы, опрошенной master'ом
*

int was_command_go = 0

флаг команды go (выйти из паузы между запросами, которая tdelay)

Имена переменных: was_command_go. Объявление (как в исходном коде):

int was_command_go=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* флаг команды go (выйти из паузы между запросами, которая tdelay)
*

volatile uint8_t buffer[BUFFER_SIZE]

Буфер накопления информации с сенсоров

Имена переменных: buffer. Объявление (как в исходном коде):

volatile uint8_t  buffer[BUFFER_SIZE];

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* Буфер накопления информации с сенсоров
*

volatile uint32_t wr_index = 0

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: wr_index, rd_index, counter. Объявление (как в исходном коде):

volatile uint32_t wr_index=0, rd_index=0, counter=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

Примечание

Для корректного раздельного отображения в Doxygen рекомендуется разнести переменные по отдельным строкам (без изменения логики).

volatile uint32_t rd_index = 0
volatile uint32_t counter = 0
volatile uint32_t last_beg_wr_index = -1

индекс в буфере, откуда начался последний цикл опроса - типа, «этот байт занят!», чтобы не начать ненароком выдавать последний цикл, когда опрос ещё не завершён. -1 - это «NULL» (все байты свободны).

Имена переменных: last_beg_wr_index. Объявление (как в исходном коде):

volatile uint32_t last_beg_wr_index=-1;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* индекс в буфере, откуда начался последний цикл опроса - типа, "этот байт занят!", чтобы не начать ненароком выдавать последний цикл, когда опрос ещё не завершён. -1 - это "NULL" (все байты свободны).
*

volatile uint8_t buffer_slave[BUFFER_SLAVE_SIZE]

Буфер накопления информации с CBS-SLAVE.

Имена переменных: buffer_slave. Объявление (как в исходном коде):

volatile uint8_t  buffer_slave[BUFFER_SLAVE_SIZE];

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* Буфер накопления информации с CBS-SLAVE
*

volatile uint32_t buffer_slave_counter = 0

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: buffer_slave_counter. Объявление (как в исходном коде):

volatile uint32_t buffer_slave_counter=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

volatile uint32_t fl_sen_1 = 0

Флаги активности интерфейсов (1 в том флаге, откуда пришёл запрос и куда надо отдавать ответ).

Имена переменных: fl_sen_1, fl_sen_2, fl_mik_1, fl_mik_2, fl_lcd_1, fl_usb. Объявление (как в исходном коде):

volatile uint32_t fl_sen_1=0, fl_sen_2=0, fl_mik_1=0, fl_mik_2=0, fl_lcd_1=0, fl_usb=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* Флаги активности интерфейсов (1 в том флаге, откуда пришёл запрос и куда надо отдавать ответ)
*

Примечание

Для корректного раздельного отображения в Doxygen рекомендуется разнести переменные по отдельным строкам (без изменения логики).

volatile uint32_t fl_sen_2 = 0
volatile uint32_t fl_mik_1 = 0
volatile uint32_t fl_mik_2 = 0
volatile uint32_t fl_lcd_1 = 0
volatile uint32_t fl_usb = 0
volatile uint32_t fd_sen_1 = 0

бэкап флагов для временного переключения на диалог с CBS-slave

Имена переменных: fd_sen_1, fd_sen_2, fd_mik_1, fd_mik_2, fd_lcd_1, fd_usb. Объявление (как в исходном коде):

volatile uint32_t fd_sen_1=0, fd_sen_2=0, fd_mik_1=0, fd_mik_2=0, fd_lcd_1=0, fd_usb=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* бэкап флагов для временного переключения на диалог с CBS-slave
*

Примечание

Для корректного раздельного отображения в Doxygen рекомендуется разнести переменные по отдельным строкам (без изменения логики).

volatile uint32_t fd_sen_2 = 0
volatile uint32_t fd_mik_1 = 0
volatile uint32_t fd_mik_2 = 0
volatile uint32_t fd_lcd_1 = 0
volatile uint32_t fd_usb = 0
char str_dial[MAXSTRLEN]

Строки для диалога, обработки команд и прочих применений

Имена переменных: str_dial, str_ori, str, stro, stroch. Объявление (как в исходном коде):

char str_dial[MAXSTRLEN], str_ori[MAXSTRLEN], str[MAXSTRLEN], stro[MAXSTRLEN], stroch[MAXSTRLEN];

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* Строки для диалога, обработки команд и прочих применений
*

Примечание

Для корректного раздельного отображения в Doxygen рекомендуется разнести переменные по отдельным строкам (без изменения логики).

char str_ori[MAXSTRLEN]
char str[MAXSTRLEN]
char stro[MAXSTRLEN]
char stroch[MAXSTRLEN]
int volatile p_in_sen_1 = 0

статистика по портам

Имена переменных: p_in_sen_1, p_out_sen_1, p_inminlen_sen_1, p_inmaxlen_sen_1, p_outminlen_sen_1, p_outmaxlen_sen_1. Объявление (как в исходном коде):

int volatile p_in_sen_1=0, p_out_sen_1=0, p_inminlen_sen_1=0, p_inmaxlen_sen_1=0, p_outminlen_sen_1=0, p_outmaxlen_sen_1=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* статистика по портам
*

Примечание

Для корректного раздельного отображения в Doxygen рекомендуется разнести переменные по отдельным строкам (без изменения логики).

int volatile p_out_sen_1 = 0
int volatile p_inminlen_sen_1 = 0
int volatile p_inmaxlen_sen_1 = 0
int volatile p_outminlen_sen_1 = 0
int volatile p_outmaxlen_sen_1 = 0
int volatile p_in_sen_2 = 0

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: p_in_sen_2, p_out_sen_2, p_inminlen_sen_2, p_inmaxlen_sen_2, p_outminlen_sen_2, p_outmaxlen_sen_2. Объявление (как в исходном коде):

int volatile p_in_sen_2=0, p_out_sen_2=0, p_inminlen_sen_2=0, p_inmaxlen_sen_2=0, p_outminlen_sen_2=0, p_outmaxlen_sen_2=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

Примечание

Для корректного раздельного отображения в Doxygen рекомендуется разнести переменные по отдельным строкам (без изменения логики).

int volatile p_out_sen_2 = 0
int volatile p_inminlen_sen_2 = 0
int volatile p_inmaxlen_sen_2 = 0
int volatile p_outminlen_sen_2 = 0
int volatile p_outmaxlen_sen_2 = 0
int volatile p_in_mik_1 = 0

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: p_in_mik_1, p_out_mik_1, p_inminlen_mik_1, p_inmaxlen_mik_1, p_outminlen_mik_1, p_outmaxlen_mik_1. Объявление (как в исходном коде):

int volatile p_in_mik_1=0, p_out_mik_1=0, p_inminlen_mik_1=0, p_inmaxlen_mik_1=0, p_outminlen_mik_1=0, p_outmaxlen_mik_1=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

Примечание

Для корректного раздельного отображения в Doxygen рекомендуется разнести переменные по отдельным строкам (без изменения логики).

int volatile p_out_mik_1 = 0
int volatile p_inminlen_mik_1 = 0
int volatile p_inmaxlen_mik_1 = 0
int volatile p_outminlen_mik_1 = 0
int volatile p_outmaxlen_mik_1 = 0
int volatile p_in_mik_2 = 0

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: p_in_mik_2, p_out_mik_2, p_inminlen_mik_2, p_inmaxlen_mik_2, p_outminlen_mik_2, p_outmaxlen_mik_2. Объявление (как в исходном коде):

int volatile p_in_mik_2=0, p_out_mik_2=0, p_inminlen_mik_2=0, p_inmaxlen_mik_2=0, p_outminlen_mik_2=0, p_outmaxlen_mik_2=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

Примечание

Для корректного раздельного отображения в Doxygen рекомендуется разнести переменные по отдельным строкам (без изменения логики).

int volatile p_out_mik_2 = 0
int volatile p_inminlen_mik_2 = 0
int volatile p_inmaxlen_mik_2 = 0
int volatile p_outminlen_mik_2 = 0
int volatile p_outmaxlen_mik_2 = 0
int volatile p_in_lcd_1 = 0

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: p_in_lcd_1, p_out_lcd_1, p_inminlen_lcd_1, p_inmaxlen_lcd_1, p_outminlen_lcd_1, p_outmaxlen_lcd_1. Объявление (как в исходном коде):

int volatile p_in_lcd_1=0, p_out_lcd_1=0, p_inminlen_lcd_1=0, p_inmaxlen_lcd_1=0, p_outminlen_lcd_1=0, p_outmaxlen_lcd_1=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

Примечание

Для корректного раздельного отображения в Doxygen рекомендуется разнести переменные по отдельным строкам (без изменения логики).

int volatile p_out_lcd_1 = 0
int volatile p_inminlen_lcd_1 = 0
int volatile p_inmaxlen_lcd_1 = 0
int volatile p_outminlen_lcd_1 = 0
int volatile p_outmaxlen_lcd_1 = 0
long long volatile p_insum_sen_1 = 0

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: p_insum_sen_1, p_insum_sen_2, p_insum_mik_1, p_insum_mik_2, p_insum_lcd_1. Объявление (как в исходном коде):

long long volatile p_insum_sen_1=0, p_insum_sen_2=0, p_insum_mik_1=0, p_insum_mik_2=0, p_insum_lcd_1=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

Примечание

Для корректного раздельного отображения в Doxygen рекомендуется разнести переменные по отдельным строкам (без изменения логики).

long long volatile p_insum_sen_2 = 0
long long volatile p_insum_mik_1 = 0
long long volatile p_insum_mik_2 = 0
long long volatile p_insum_lcd_1 = 0
long long volatile p_outsum_sen_1 = 0

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: p_outsum_sen_1, p_outsum_sen_2, p_outsum_mik_1, p_outsum_mik_2, p_outsum_lcd_1. Объявление (как в исходном коде):

long long volatile p_outsum_sen_1=0, p_outsum_sen_2=0, p_outsum_mik_1=0, p_outsum_mik_2=0, p_outsum_lcd_1=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

Примечание

Для корректного раздельного отображения в Doxygen рекомендуется разнести переменные по отдельным строкам (без изменения логики).

long long volatile p_outsum_sen_2 = 0
long long volatile p_outsum_mik_1 = 0
long long volatile p_outsum_mik_2 = 0
long long volatile p_outsum_lcd_1 = 0
char volatile dumpstatus = 0

1 - выводим в USB дамп всех сообщений (и исходных, и обработанных).

Имена переменных: dumpstatus. Объявление (как в исходном коде):

char volatile dumpstatus=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* 1 - выводим в USB дамп всех сообщений (и исходных, и обработанных).
*

char volatile dumpstatus_uart = 0

1 - выводим в порт, с которого пришла «on», дамп всех сообщений.

Имена переменных: dumpstatus_uart. Объявление (как в исходном коде):

char volatile dumpstatus_uart=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* 1 - выводим в порт, с которого пришла "on", дамп всех сообщений.
*

char volatile gostatus = 1

1 - штатная работа (циклический опрос сенсоров).

Имена переменных: gostatus. Объявление (как в исходном коде):

char volatile gostatus=1;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* 1 - штатная работа (циклический опрос сенсоров).
*

char volatile USB_getline_echo = 1

1 - эхо ввода через терминал USB.

Имена переменных: USB_getline_echo. Объявление (как в исходном коде):

char volatile USB_getline_echo=1;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* 1 - эхо ввода через терминал USB.
*

char volatile UART_SEN_1_getline_echo = 0

0 - эхо отсутствует, не 0 - присутствует

Имена переменных: UART_SEN_1_getline_echo. Объявление (как в исходном коде):

char volatile UART_SEN_1_getline_echo=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* 0 - эхо отсутствует, не 0 - присутствует
*

char volatile UART_SEN_2_getline_echo = 0

0 - эхо отсутствует

Имена переменных: UART_SEN_2_getline_echo. Объявление (как в исходном коде):

char volatile UART_SEN_2_getline_echo=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* 0 - эхо отсутствует
*

char volatile UART_MIK_1_getline_echo = 0

0 - эхо отсутствует

Имена переменных: UART_MIK_1_getline_echo. Объявление (как в исходном коде):

char volatile UART_MIK_1_getline_echo=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* 0 - эхо отсутствует
*

char volatile UART_MIK_2_getline_echo = 0

0 - эхо отсутствует

Имена переменных: UART_MIK_2_getline_echo. Объявление (как в исходном коде):

char volatile UART_MIK_2_getline_echo=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* 0 - эхо отсутствует
*

char volatile UART_LCD_1_getline_echo = 0

0 - эхо отсутствует

Имена переменных: UART_LCD_1_getline_echo. Объявление (как в исходном коде):

char volatile UART_LCD_1_getline_echo=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* 0 - эхо отсутствует
*

int volatile mig = 0

для мигалок IER

Имена переменных: mig, migp. Объявление (как в исходном коде):

int volatile mig=0, migp=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* для мигалок IER
*

Примечание

Для корректного раздельного отображения в Doxygen рекомендуется разнести переменные по отдельным строкам (без изменения логики).

int volatile migp = 0
unsigned long volatile dms = 0

для таймера (плюс паузы и флаги приходящих пакетов)

Имена переменных: dms, cms, ms, timeout_hms, delay_hms, dms_total, rtc_hms, delay_period. Объявление (как в исходном коде):

unsigned long volatile dms=0, cms=0, ms=0, timeout_hms, delay_hms, dms_total=0, rtc_hms=0, delay_period=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* для таймера (плюс паузы и флаги приходящих пакетов)
*

Примечание

Для корректного раздельного отображения в Doxygen рекомендуется разнести переменные по отдельным строкам (без изменения логики).

unsigned long volatile cms = 0
unsigned long volatile ms = 0
unsigned long volatile timeout_hms
unsigned long volatile delay_hms
unsigned long volatile dms_total = 0
unsigned long volatile rtc_hms = 0
unsigned long volatile delay_period = 0
long volatile tm_sen_1 = 0

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: tm_sen_1, tm_sen_2, tm_mik_1, tm_mik_2, tm_lcd_1, tmu. Объявление (как в исходном коде):

long volatile tm_sen_1=0, tm_sen_2=0, tm_mik_1=0, tm_mik_2=0, tm_lcd_1=0, tmu=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

Примечание

Для корректного раздельного отображения в Doxygen рекомендуется разнести переменные по отдельным строкам (без изменения логики).

long volatile tm_sen_2 = 0
long volatile tm_mik_1 = 0
long volatile tm_mik_2 = 0
long volatile tm_lcd_1 = 0
long volatile tmu = 0
char volatile u_yes_sen_1 = 0

long volatile u_e_mik_2=UART_MIK_2_EOP+1, u_e_lcd_1=UART_LCD_1_EOP+1;

Имена переменных: u_yes_sen_1, u_yes_sen_2, u_yes_mik_1, u_yes_mik_2, u_yes_lcd_1, uuyes. Объявление (как в исходном коде):

char volatile u_yes_sen_1=0, u_yes_sen_2=0, u_yes_mik_1=0, u_yes_mik_2=0, u_yes_lcd_1=0, uuyes=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* long volatile u_e_sen_1=UART_SEN_1_EOP+1, u_e_sen_2=UART_SEN_2_EOP+1, u_e_mik_1=UART_MIK_1_EOP+1;     // =+= про паузу между пакетами на портах. Сейчас не используем. Тут надо ставить ifdef на каждый порт!
* long volatile u_e_mik_2=UART_MIK_2_EOP+1, u_e_lcd_1=UART_LCD_1_EOP+1;
*

Примечание

Для корректного раздельного отображения в Doxygen рекомендуется разнести переменные по отдельным строкам (без изменения логики).

char volatile u_yes_sen_2 = 0
char volatile u_yes_mik_1 = 0
char volatile u_yes_mik_2 = 0
char volatile u_yes_lcd_1 = 0
char volatile uuyes = 0
char volatile delay_period_elapsed = 0

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: delay_period_elapsed. Объявление (как в исходном коде):

char volatile delay_period_elapsed=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

volatile SDI_t sdi_ports[TOTAL_SDI_PORTS]

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: sdi_ports. Объявление (как в исходном коде):

volatile SDI_t sdi_ports[TOTAL_SDI_PORTS];

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

volatile int sdi_totaldev

количество обнаруженных устройств sdi

Имена переменных: sdi_totaldev. Объявление (как в исходном коде):

volatile int sdi_totaldev;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* количество обнаруженных устройств sdi
*

char sdi_act[sizeof(sdi_ports) / sizeof(sdi_ports[0])][62]
char sdi_name[TOTAL_SDI_DEV][40]

максимум TOTAL_SDI_DEV устройств: порт адр id (пример: «42013INFWIN ECTDS A.0ECTDS10-5C002», 1+1+33 байта +5 запас) (0 в первом байте - строка свободна) (отсылать id имеет смысл с sdi_name[n][3] до конца строки)

Имена переменных: sdi_name. Объявление (как в исходном коде):

char sdi_name[TOTAL_SDI_DEV][40];

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* максимум TOTAL_SDI_DEV устройств: порт адр id (пример: "42013INFWIN  ECTDS A.0ECTDS10-5C002", 1+1+33 байта +5 запас) (0 в первом байте - строка свободна) (отсылать id имеет смысл с sdi_name[n][3] до конца строки)
*

volatile int modbus_totaldev

количество обнаруженных устройств modbus

Имена переменных: modbus_totaldev. Объявление (как в исходном коде):

volatile int modbus_totaldev;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* для MODBUS
* количество обнаруженных устройств modbus
*

uint8_t modbus_act[TOTAL_MODBUS_PORTS][256]

таблица активных устройств (0 - устройства нет, 1 - есть). [порт][адреса]

Имена переменных: modbus_act. Объявление (как в исходном коде):

uint8_t modbus_act[TOTAL_MODBUS_PORTS][256];

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* таблица активных устройств (0 - устройства нет, 1 - есть). [порт][адреса]
*

uint8_t modbus_dev[TOTAL_MODBUS_DEV][2]

[0]порт, [1]адр

Имена переменных: modbus_dev. Объявление (как в исходном коде):

uint8_t modbus_dev[TOTAL_MODBUS_DEV][2];

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* [0]порт, [1]адр
*

unsigned char volatile mer[64]

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: mer, nmer, imer. Объявление (как в исходном коде):

unsigned char volatile mer[64], nmer, imer;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

Примечание

Для корректного раздельного отображения в Doxygen рекомендуется разнести переменные по отдельным строкам (без изменения логики).

unsigned char volatile nmer
unsigned char volatile imer
uint32_t UART_SEN_1_er[8]

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: UART_SEN_1_er, UART_SEN_2_er, UART_MIK_1_er, UART_MIK_2_er, UART_LCD_1_er. Объявление (как в исходном коде):

uint32_t UART_SEN_1_er[8], UART_SEN_2_er[8], UART_MIK_1_er[8], UART_MIK_2_er[8], UART_LCD_1_er[8];

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

Примечание

Для корректного раздельного отображения в Doxygen рекомендуется разнести переменные по отдельным строкам (без изменения логики).

uint32_t UART_SEN_2_er[8]
uint32_t UART_MIK_1_er[8]
uint32_t UART_MIK_2_er[8]
uint32_t UART_LCD_1_er[8]
unsigned char preobra_err = 0

количество ошибок преобразования.

Имена переменных: preobra_err. Объявление (как в исходном коде):

unsigned char  preobra_err=0;

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* количество ошибок преобразования.
*

const uint8_t table_crc_hi[256] = {0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40}

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: table_crc_hi. Объявление (как в исходном коде):

const uint8_t table_crc_hi[256] =

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

const uint8_t table_crc_lo[256] = {0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40}

Глобальная переменная/группа переменных модуля user_code_0.

Имена переменных: table_crc_lo. Объявление (как в исходном коде):

const uint8_t table_crc_lo[256] =

Доходчивое объяснение

Комментарии, которые сопровождают данное объявление в исходнике:

* (Рядом с объявлением отсутствуют поясняющие комментарии.)
*

file user_code_2.h

Functions

ourI2C_init (1)
ourI2C_init (2)
memset ((void *) sdi_ports, 0, sizeof(sdi_ports))
F_port_init()

Инициализирует GPIO для всех включенных портов SDI-12.

Проходит по массиву sdi_ports[] и вызывает F_port_init_one() для портов, помеченных как enabled.

* (В реализации отсутствуют отдельные поясняющие комментарии.)
*
Доходчивое объяснение

Ниже приведены комментарии, присутствующие в исходной реализации функции. Они продублированы здесь для сохранения контекста.

Дополнение: При чтении исходника эти комментарии следует сопоставлять с описанием алгоритма в

setvbuf (stdout, NULL, _IONBF, 0)
HAL_Delay (300)
HAL_Delay (50)
HAL_Delay (100)
HAL_Delay (1000)
time_read (DEF_TIME, &default_time)
if (default_time.SystemN !=NUM_SYS||default_time.CBSN !=NUM_CBS)
IER (128+VER_H)
memset (sdi_name, 0, sizeof(sdi_name))
memset (sdi_act, 0, sizeof(sdi_act))
for (i=0;i< sizeof(sdi_ports)/sizeof(sdi_ports[0]);++i)
for (i=0;i< sizeof(sdi_ports)/sizeof(sdi_ports[0]);++i) if(sdi_ports[i].enabled) while(sdi_ports[i].rx_counter) getsdi(&sdi_ports[i])
HAL_Delay (400)
if (sdi_totaldev > sizeof(sdi_name)/sizeof(sdi_name[0]))
if (nmer==0) VD1_TOG
if (modbus_totaldev > sizeof(modbus_dev)/sizeof(modbus_dev[0]))
IER1 (2)
IER1(modbus_totaldev)
while (1)

Variables

volatile int i
volatile int j
volatile int k
volatile int n = 0
uint16_t crcr
uint8_t port
uint8_t addr
nmer   =imer=0
VD1_OFF
VD1_ON
HAL_TIM_Base_Start_IT &htim1
dumpstatus   =0
dumpstatus_uart   =0
sdi_totaldev   =0
j< 62;j++) if(sdi_act[i][j]) { if(n==sdi_totaldev) break;putsdi(&sdi_ports[i], sdi_letter(j));putsdi(&sdi_ports[i], 'I');putsdi(&sdi_ports[i], '!');wakesdi(&sdi_ports[i]);ms=0;while(ms< 100);if(sdi_ports[i].rx_counter) { while(sdi_ports[i].s);k=0;while(sdi_ports[i].rx_counter) if(k< sizeof(str) -2) str[k++]=getsdi(&sdi_ports[i]);if(k >1 &&str[k-2]==0x0D &&str[k-1]==0x0A) str[k-2]=0, k-=2;if(k > sizeof(sdi_name[0]) -3) k=sizeof(sdi_name[0]) -3;str[k]=0;str[k+1]=0;sdi_name[n][0]=sdi_letter(i);sdi_name[n][1]=sdi_letter(j);strcpy(sdi_name[n]+2, str+1);++n;if(nmer==0) {VD1_ON;HAL_Delay(1);VD1_OFF;} } else IER(12);} IER1(2);IER1(sdi_totaldev);for(k=0;k< sdi_totaldev;++k) { if(memcmp(&sdi_name[k][12],"DGGCD", 5)==0||memcmp(&sdi_name[k][12],"DGGOX", 5)==0) { port=sdi_number(sdi_name[k][0]);addr=sdi_name[k][1];sdi_act[port][sdi_number(addr)]=2;snprintf(str, sizeof(str), "%cXW_WUT_%d!", addr, sdi_name[k][15]=='C'? 6:2);for(i=0;i< strlen(str);++i) putsdi(&sdi_ports[port], str[i]);wakesdi(&sdi_ports[port]);ms=0;while(ms< 1500 &&sdi_ports[port].rx_counter==0);if(sdi_ports[port].rx_counter) { while(sdi_ports[port].s);while(sdi_ports[port].rx_counter) getsdi(&sdi_ports[port]);} else { } } } memset(modbus_act, 0, sizeof(modbus_act));memset(modbus_dev, 0, sizeof(modbus_dev));modbus_totaldev=0;for(addr=1;addr<=247;++addr) { str[0]=addr;str[1]=3;str[2]=0x00;str[3]=0x22;str[4]=0x00;str[5]=0x01;crcr=crc16(str, 6);str[6]=crcr > j< 62;j++) if(sdi_act[i][j]) { if(n==sdi_totaldev) break;putsdi(&sdi_ports[i], sdi_letter(j));putsdi(&sdi_ports[i], 'I');putsdi(&sdi_ports[i], '!');wakesdi(&sdi_ports[i]);ms=0;while(ms< 100);if(sdi_ports[i].rx_counter) { while(sdi_ports[i].s);k=0;while(sdi_ports[i].rx_counter) if(k< sizeof(str) -2) str[k++]=getsdi(&sdi_ports[i]);if(k >1 &&str[k-2]==0x0D &&str[k-1]==0x0A) str[k-2]=0, k-=2;if(k > sizeof(sdi_name[0]) -3) k=sizeof(sdi_name[0]) -3;str[k]=0;str[k+1]=0;sdi_name[n][0]=sdi_letter(i);sdi_name[n][1]=sdi_letter(j);strcpy(sdi_name[n]+2, str+1);++n;if(nmer==0) {VD1_ON;HAL_Delay(1);VD1_OFF;} } else IER(12);} IER1(2);IER1(sdi_totaldev);for(k=0;k< sdi_totaldev;++k) { if(memcmp(&sdi_name[k][12],"DGGCD", 5)==0||memcmp(&sdi_name[k][12],"DGGOX", 5)==0) { port=sdi_number(sdi_name[k][0]);addr=sdi_name[k][1];sdi_act[port][sdi_number(addr)]=2;snprintf(str, sizeof(str), "%cXW_WUT_%d!", addr, sdi_name[k][15]=='C'? 6:2);for(i=0;i< strlen(str);++i) putsdi(&sdi_ports[port], str[i]);wakesdi(&sdi_ports[port]);ms=0;while(ms< 1500 &&sdi_ports[port].rx_counter==0);if(sdi_ports[port].rx_counter) { while(sdi_ports[port].s);while(sdi_ports[port].rx_counter) getsdi(&sdi_ports[port]);} else { } } } memset(modbus_act, 0, sizeof(modbus_act));memset(modbus_dev, 0, sizeof(modbus_dev));modbus_totaldev=0;for(addr=1;addr<=247;++addr) { str[0]=addr;str[1]=3;str[2]=0x00;str[3]=0x22;str[4]=0x00;str[5]=0x01;crcr=crc16(str, 6);str[6]=crcr >
str [7]  = crcr & 0xFF
ms   =0
dir /Users/aleksandra/Desktop/Забава/CSB/Soft/F105_100/Core
dir /Users/aleksandra/Desktop/Забава/CSB/Soft/F105_100
dir /Users/aleksandra/Desktop/Забава/CSB/Soft/F105_100/Core/Inc
dir /Users/aleksandra/Desktop/Забава/CSB/Soft/F105_100/Core/Src