diff --git a/Inc/modbus.h b/Inc/modbus.h index 879b030..7613ffa 100644 --- a/Inc/modbus.h +++ b/Inc/modbus.h @@ -107,6 +107,10 @@ #ifdef MODBUS_ENABLE_DIAGNOSTICS #include "modbus_diag.h" #endif +#ifdef MODBUS_ENABLE_OSCIL +#include "modbus_oscil.h" +#endif +#include "modbus_data.h" #ifdef MODBUS_ENABLE_MASTER diff --git a/Inc/modbus_core.h b/Inc/modbus_core.h index d87cd76..48f32b4 100644 --- a/Inc/modbus_core.h +++ b/Inc/modbus_core.h @@ -27,7 +27,6 @@ #define __MODBUS_CORE_H_ #include "modbus_config.h" -#include "modbus_data.h" #include "__crc_algs.h" /** diff --git a/Inc/modbus_oscil.h b/Inc/modbus_oscil.h new file mode 100644 index 0000000..3e242f9 --- /dev/null +++ b/Inc/modbus_oscil.h @@ -0,0 +1,108 @@ +/** +******************************************************************************* +* @file modbus_oscil.h +* @brief Заголовочный файл модуля осциллографа через MODBUS. +******************************************************************************* +* @addtogroup MODBUS_OSCIL Modbus Oscilloscope +* @ingroup MODBUS +* @brief Модуль для сбора и хранения данных осциллографа +* @details +* Реализует кольцевой буфер для хранения данных с нескольких каналов +* с возможностью чтения через Modbus. Данные 8-битные, упаковываются +* по 2 сэмпла в 16-битный регистр Modbus. +* +* Карта Modbus регистров (адрес относительный): +* | Адрес | Биты | Назначение | Диапазон | Описание | +* |--------|------|------------|----------|----------| +* | 0x0000 | 0-3 | channels | 1-16 | Количество каналов | +* | | 4-7 | reserved | 0 | Зарезервировано | +* | | 8-15 | buffer_size| 1-125 | Размер буфера в регистрах | +* | 0x0001 | 0-7 | tail | 0-124 | Начало новых данных | +* | | 8-15 | head | 0-124 | Конец новых данных | +* | 0x0002...| 0-15| data | - | Буфер данных | +* @{ +******************************************************************************/ +#ifndef __MODBUS_OSCIL_H_ +#define __MODBUS_OSCIL_H_ + +#include "modbus_core.h" + +#ifdef MODBUS_ENABLE_OSCIL + + +/** + * @brief Структура конфигурации осциллографа + */ +typedef struct +{ + uint16_t Overrun:1; ///< Overrun + uint16_t NumbOfChannels:4; ///< Количество каналов (1-16) + uint16_t BufferSize:7; ///< Размер буфера в регистрах (1-125) + uint16_t SampleTime; ///< Время между сэмплами в мкс/мс + uint32_t LastTick; ///< Время последнего добавления +} MB_ConfigTypeDef; + +/** + * @brief Структура указателей буфера + */ +typedef struct +{ + uint16_t Head:8; ///< Указатель на последний записанный байт + uint16_t Tail:8; ///< Указатель на начало непрочитанных данных (байты) +} MB_PreambleTypeDef; + +/** + * @brief Основная структура осциллографа + */ +typedef struct +{ + MB_ConfigTypeDef Config; ///< Конфигурация + uint16_t User[6]; + MB_PreambleTypeDef Preamble; ///< Указатели буфера + uint8_t Data[MbData_size*2-1]; ///< Буфер данных (в байтах) +} MB_OscilTypeDef; + + + +/** + * @addtogroup MODBUS_OSCIL_API API for Oscilloscope + * @ingroup MODBUS_OSCIL + * @brief API для работы с буфером осциллографа + * @details Примеры использования: + * @code + * MB_OscilTypeDef oscil; + * + * // Инициализация осциллографа + * MB_Oscil_Init(&oscil, 120, 4, 1000); + * + * // В цикле сбора данных + * uint8_t ch_data[4] = {adc1, adc2, adc3, adc4}; + * MB_Oscil_Add(&oscil, ch_data); + * + * // После чтения данных через Modbus + * MB_Oscil_UpdateTail(&oscil); + * @endcode + * @{ + */ + +/* Инициализация структуры осциллографа */ +HAL_StatusTypeDef MB_Oscil_Init(MB_OscilTypeDef *oscil, uint8_t BufferSize, uint8_t NumbOfChannels, uint16_t SampleTime); + +/* Добавление данных всех каналов в буфер */ +HAL_StatusTypeDef MB_Oscil_Add(MB_OscilTypeDef *oscil, uint8_t *Data); + +/* Обновление указателя Tail */ +HAL_StatusTypeDef MB_Oscil_UpdateTail(MB_OscilTypeDef *oscil); + +/** MODBUS_OSCIL_API + * @} + */ + +#endif //MODBUS_ENABLE_OSCIL + +#endif //__MODBUS_OSCIL_H_ + +/** MODBUS_OSCIL + * @} + */ + \ No newline at end of file diff --git a/Src/modbus_holdregs.c b/Src/modbus_holdregs.c index 90b1c96..c7f0174 100644 --- a/Src/modbus_holdregs.c +++ b/Src/modbus_holdregs.c @@ -107,7 +107,7 @@ uint8_t MB_Process_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg) for (i = 0; iQnt; i++) { modbus_msg->MbData[i] = *(pHoldRegs++); - } + } return 1; } diff --git a/Src/modbus_oscil.c b/Src/modbus_oscil.c new file mode 100644 index 0000000..330a7d7 --- /dev/null +++ b/Src/modbus_oscil.c @@ -0,0 +1,145 @@ +/** +******************************************************************************* +* @file modbus_oscil.c +* @brief Модуль для реализации осциллографа через MODBUS. +******************************************************************************* +* @details +* Файл содержит реализацию функций для работы с буфером осциллографа. +* +* @section oscil_functions Функции +* - MB_Oscil_Init() — Инициализация структуры осциллографа +* - MB_Oscil_Add() — Добавление данных в буфер +* - MB_Oscil_UpdateTail() — Обновление указателя Tail +******************************************************************************/ +#include "modbus_oscil.h" + +#ifdef MODBUS_ENABLE_OSCIL + + + +/** + * @brief Инициализация структуры осциллографа + * @param oscil Указатель на структуру осциллографа + * @param BufferSize Размер буфера в Modbus регистрах (max 125) + * @param NumbOfChannels Количество каналов (должно быть четным) + * @param SampleTime Период дискретизации + * @return HAL Status + */ +HAL_StatusTypeDef MB_Oscil_Init(MB_OscilTypeDef *oscil, uint8_t BufferSize, uint8_t NumbOfChannels, uint16_t SampleTime) +{ + if(!oscil || (BufferSize >= MbData_size) || (NumbOfChannels > 15) || !SampleTime) + return HAL_ERROR; + + if(NumbOfChannels % 2 != 0) + return HAL_ERROR; + + oscil->Config.BufferSize = BufferSize; + oscil->Config.NumbOfChannels = NumbOfChannels; + oscil->Config.SampleTime = SampleTime; + oscil->Config.LastTick = 0; + oscil->Preamble.Head = 0; + oscil->Preamble.Tail = 0; + oscil->Config.Overrun = 0; + + return HAL_OK; +} + + +/** + * @brief Инициализация структуры осциллографа + * @param oscil Указатель на структуру осциллографа + * @param BufferSize Размер буфера в Modbus регистрах (max 125) + * @param NumbOfChannels Количество каналов (должно быть четным) + * @param SampleTime Период дискретизации + * @return HAL Status + */ +HAL_StatusTypeDef MB_Oscil_Set_SampleTime(MB_OscilTypeDef *oscil, uint16_t SampleTime) +{ + if(!oscil || !SampleTime) + return HAL_ERROR; + + oscil->Config.SampleTime = SampleTime; + return HAL_OK; +} + + +/** + * @brief Добавление данных всех каналов в буфер + * @param oscil Указатель на структуру осциллографа + * @param Data Массив с данными каналов (длина = NumbOfChannels) + * @return HAL Status + */ +HAL_StatusTypeDef MB_Oscil_Add(MB_OscilTypeDef *oscil, uint8_t *Data) +{ + if(!oscil || !Data) + return HAL_ERROR; + + if(local_time() - oscil->Config.LastTick < oscil->Config.SampleTime) + return HAL_BUSY; + + oscil->Config.LastTick = local_time(); + + uint8_t head_max = oscil->Config.BufferSize * 2; + uint8_t start_pos = oscil->Preamble.Head; + uint8_t channels = oscil->Config.NumbOfChannels; + uint8_t tail = oscil->Preamble.Tail; + + // Пишем данные + for(uint8_t i = 0; i < channels; i++) + { + uint8_t write_pos = (start_pos + i) % head_max; + oscil->Data[write_pos] = Data[i]; + } + + // Новое положение Head + uint8_t new_head = (start_pos + channels) % head_max; + + // Проверяем, есть ли непрочитанные данные (Tail != Head) +// if(tail != start_pos) { + // Если есть непрочитанные данные, проверяем не перетерли ли мы их + if(start_pos < new_head) { + // Линейный случай + if(tail > start_pos && tail <= new_head) { + oscil->Preamble.Tail = (new_head + 1) % head_max; + oscil->Config.Overrun = 1; + } + } else if(start_pos > new_head) { + // Кольцевой случай (было переполнение) + if(tail > start_pos || tail <= new_head) { + oscil->Preamble.Tail = (new_head + 1) % head_max; + oscil->Config.Overrun = 1; + } + } +// } + oscil->Preamble.Head = new_head; + + return HAL_OK; +} + +/** + * @brief Обновление указателя Tail + * @param oscil Указатель на структуру осциллографа + * @return HAL Status + * + * @details Вызывается после того, как данные были прочитаны через Modbus. + * Tail устанавливается на следующий байт за Head. + */ +HAL_StatusTypeDef MB_Oscil_UpdateTail(MB_OscilTypeDef *oscil) +{ + if(!oscil) + return HAL_ERROR; + + // Сдвигаем Tail на следующий байт за Head + uint8_t head_max = oscil->Config.BufferSize * 2; + oscil->Preamble.Tail = oscil->Preamble.Head; + + if(oscil->Preamble.Tail >= head_max) + oscil->Preamble.Tail = 0; + + oscil->Config.Overrun = 0; + + return HAL_OK; +} + + +#endif //MODBUS_ENABLE_OSCIL \ No newline at end of file diff --git a/Src/modbus_slave.c b/Src/modbus_slave.c index 7d81c21..310abf6 100644 --- a/Src/modbus_slave.c +++ b/Src/modbus_slave.c @@ -49,6 +49,11 @@ RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *mod // Read Hodling Registers case FC_R_HOLD_REGS: hmodbus->f.MessageHandled = MB_Process_Read_Hold_Regs(hmodbus->pMessagePtr); +#ifdef MODBUS_ENABLE_OSCIL + int is_oscil_data_register = ((hmodbus->pMessagePtr->Addr >= R_HOLDING_OSCIL_ADDR+4) && (hmodbus->pMessagePtr->Addr + hmodbus->pMessagePtr->Qnt <= (R_HOLDING_OSCIL_ADDR+5+(sizeof(MB_OscilTypeDef)/2)))); + if(is_oscil_data_register) + MB_Oscil_UpdateTail(&MB_INTERNAL.oscil); +#endif break; case FC_R_IN_REGS: hmodbus->f.MessageHandled = MB_Process_Read_Input_Regs(hmodbus->pMessagePtr);