// Тест сделан только для отладки целевого модуля.
// Может применяться для ознокомления с функционалом модуля.


//******************************************** Включаемые файлы *********************************************

#include "submodule.h"
#include "crc.h"
#include "dbg_printf.h"

#include <string.h>
#include <stdio.h>
#include <stdlib.h>

//******************************************* Локальные константы *******************************************

// Позиция полей в памяти субмодуля
#define SU_MEM_POS_VERSION              (0U)
#define SU_MEM_POS_ID                   (2U)
#define SU_MEM_POS_CRC                  (4U)
#define SU_MEM_POS_SN                   (6U)
#define SU_MEM_POS_CALIB0               (12U)
#define SU_MEM_POS_CALIB1               (20U)
#define SU_MEM_POS_CALIB2               (28U)
#define SU_MEM_POS_CALIB3               (36U)


#define SU_SUBUNIT_TYPE_H27     (1U)
#define SU_SUBUNIT_TYPE_A27     (2U)

#define SU_EMPTY_ID             (255)
#define SU_ID_MASK              (0xFF)

// идентификаторы мезонинов
// #define H27__U01      9
// #define H27__U10      10
// #define H27__U20      11
// #define H27__U30      12
// #define H27__U300     13
// #define H27__U100     14
// // 
// #define H27__I5       20
// #define H27__I10      21
// #define H27__I20      22
// #define H27__R100     31
// #define H27__R250     32
// // 
// #define H27__T        40
// #define H27__EMPTY   255
// // 
// #define H27_ID_START   H27__U01
// #define H27_ID_END     H27__T


// // LTA A27
// #define A27__U01      59
// #define A27__U10      60
// #define A27__U20      61
// // 
// #define A27__I5       70
// #define A27__I10      71
// #define A27__I20      72
// // 
// #define A27__R100     81
// #define A27__R250     82
// // 
// #define A27__T        90
// #define A27__EMPTY   255
// // 
// #define A27_ID_START   A27__U01
// #define A27_ID_END     A27__T


/* число элементов в массиве stMezInfoH27 */
#define SU_MINFO_H27_SIZE    (sizeof(stMezInfoH27)/sizeof(stMezInfoH27[0]))
#define SU_MINFO_A27_SIZE    (sizeof(stMezInfoA27)/sizeof(stMezInfoA27[0]))

#define SU_SN_SIZE_MAGIC_NUM           (0xA8)

// Полезные данные памяти субмодуля
#define SU_MEM_HEADER_SIZE              (sizeof(SU_MEZ_MEM_RDCD))


//******************************************** Определения типов ********************************************


// Идентификаторы субмодулей серии H27
typedef enum
{
    SU_ID_H27_START = (u8) 9U,
    // 
    SU_ID_H27_U01   = (u8) 9U,
    SU_ID_H27_U10   = (u8) 10U,
    SU_ID_H27_U20   = (u8) 11U,
    SU_ID_H27_U30   = (u8) 12U,
    SU_ID_H27_U300  = (u8) 13U,
    SU_ID_H27_U100  = (u8) 14U,
    // 
    SU_ID_H27_I5    = (u8) 20U,
    SU_ID_H27_I10   = (u8) 21U,
    SU_ID_H27_I20   = (u8) 22U,
    // 
    SU_ID_H27_R100  = (u8) 31U,
    SU_ID_H27_R250  = (u8) 32U,
    // 
    SU_ID_H27_T     = (u8) 40U,
    // 
    SU_ID_H27_END   = (u8) 40U,
    // 
    SU_ID_H27_EMPTY = (u8) 255U,
} SU_ID_H27;

// Идентификаторы субмодулей серии H27
typedef enum
{
    SU_ID_A27_START = (u8) 50U,
    // 
    SU_ID_A27_U01   = (u8) 50U,
    SU_ID_A27_U10   = (u8) 51U,
    SU_ID_A27_U20   = (u8) 52U,
    // 
    SU_ID_A27_I5    = (u8) 60U,
    SU_ID_A27_I10   = (u8) 61U,
    SU_ID_A27_I20   = (u8) 62U,
    //
    SU_ID_A27_R100  = (u8) 70U,
    SU_ID_A27_R250  = (u8) 71U,
    // 
    SU_ID_A27_T     = (u8) 80U,
    // 
    SU_ID_A27_END   = (u8) 80U,
    // 
    SU_ID_A27_EMPTY = (u8) 255U,
} SU_ID_A27;


// Структура данных субмодулей H27xx (128 байт)
typedef struct 
{
    u16  Version;         // 2
    u16  ID;              // 2
    u16  Crc;             // 2
    u8   SerialNumber[6]; // 6 
    u16  CalibrCoef[4];   // 8
    u8   Comment[108];    // 8 + 12 = 20. Комментарий = 128-20 = 108
} SU_MEZ_MEM_H27;

// Структура данных субмодулей A27xx (128 байт)
typedef struct 
{
    u16  Version;
    u16  ID;
    u16  Crc;
    u8   SerialNumber[6]; // 12
    u16  CalibrCoef_0[4];  
    u16  CalibrCoef_1[4]; 
    u16  CalibrCoef_2[4];
    u16  CalibrCoef_3[4]; 
    u8   Comment[84];     // (12 + (4*2)*4) + 84 (коммент) = 128
} SU_MEZ_MEM_A27;

// Сокращенная структура (reduced), с общей информацией о субмодулях
typedef struct 
{
    i16  nError;
    u16  nVersion;
    u16  nID;
    i8   sName[8];
    i8   sSerialNumber[16];
    u16  nCalibrCoef_0[4];
    u16  nCalibrCoef_1[4];
    u16  nCalibrCoef_2[4];
    u16  nCalibrCoef_3[4];
    // В другом формате для наглядного представления
    d64  fCalibrCoef_0[4];
    d64  fCalibrCoef_1[4];
    d64  fCalibrCoef_2[4];
    d64  fCalibrCoef_3[4];
} SU_MEZ_MEM_RDCD; 

// описание мезонинов
typedef struct 
{
    u16 ID;                          /* идентификатор субмодуля */
    u8 Name[16];                     /* название модуля */
    u8 Unit[16];                     /* измеряемая субмодулем физ.величина */
    double ConvCoeff[2];             /* масштаб и смещение для пересчета кода в физ.величину */
} SU_MEZ_INFO;



//************************************ Глобальные (публичные) переменные ************************************


//************************************* Локальные (приватные) переменные ************************************

u8 pSubUnitData[128];
u8 pWrZeroData[128];
u8 pWrData[15] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};

// -----------------

// перечислены все известные h27-мезонины и их параметры
static const SU_MEZ_INFO stMezInfoH27[] = 
{
    {SU_ID_H27_U01  , "U01\0" , "В\0" , {   2.0/0x8000,  -1.0}},
    {SU_ID_H27_U10  , "U10\0" , "В\0" , {  20.0/0x8000, -10.0}},
    {SU_ID_H27_U20  , "U20\0" , "В\0" , {  20.0/0x8000,   0.0}},
    {SU_ID_H27_U30  , "U30\0" , "В\0" , {  30.0/0x8000,   0.0}},
    {SU_ID_H27_U100 , "U100\0", "В\0" , { 100.0/0x8000,   0.0}},
    {SU_ID_H27_U300 , "U300\0", "В\0" , { 300.0/0x8000,   0.0}},
    {SU_ID_H27_I5   , "I5\0"  , "мА\0", {   5.0/0x8000,   0.0}},
    {SU_ID_H27_I10  , "I10\0" , "мА\0", {  20.0/0x8000, -10.0}},
    {SU_ID_H27_I20  , "I20\0" , "мА\0", {  20.0/0x8000,   0.0}},
    {SU_ID_H27_R100 , "R100\0", "Ом\0", { 100.0/0x8000,   0.0}},
    {SU_ID_H27_R250 , "R250\0", "Ом\0", { 250.0/0x8000,   0.0}},
    {SU_ID_H27_T    , "T\0"   , "мВ\0", { 100.0/0x8000, -25.0}},
    {SU_ID_H27_EMPTY, "EMPTY\0" , "\0", { 100.0/0x8000,   0.0}},
    {SU_ID_H27_EMPTY, "UDEF\0" , "\0" , { 100.0/0x8000,   0.0}}
};

// перечислены все известные a27-мезонины и их параметры
static const SU_MEZ_INFO stMezInfoA27[] = 
{
    {SU_ID_A27_U01  , "U01\0" , "В\0" , {   2.0/0x8000,  -1.0}},
    {SU_ID_A27_U10  , "U10\0" , "В\0" , {  20.0/0x8000, -10.0}},
    {SU_ID_A27_U20  , "U20\0" , "В\0" , {  20.0/0x8000,   0.0}},
    {SU_ID_A27_I5   , "I5\0"  , "мА\0", {   5.0/0x8000,   0.0}},
    {SU_ID_A27_I10  , "I10\0" , "мА\0", {  20.0/0x8000, -10.0}},
    {SU_ID_A27_I20  , "I20\0" , "мА\0", {  20.0/0x8000,   0.0}},
    {SU_ID_A27_R100 , "R100\0", "Ом\0", { 100.0/0x8000,   0.0}},
    {SU_ID_A27_R250 , "R250\0", "Ом\0", { 250.0/0x8000,   0.0}},
    {SU_ID_A27_T    , "T\0"   , "мВ\0", { 100.0/0x8000, -25.0}},
    {SU_ID_A27_EMPTY, "EMPTY\0" , "\0", { 100.0/0x8000,   0.0}},
    {SU_ID_A27_EMPTY, "UDEF\0" , "\0" , { 100.0/0x8000,   0.0}}
};

// Информация об установленных субмодулях
static SU_MEZ_MEM_RDCD stReducedMemData[SU_SUBUNITS_MAX_QTY];



//********************************* Объявления локальных (приватных) функций ********************************


//-----------------------------------------------------------------------------------------------------------
//! @brief       Проверка ID снимка данных памяти субмодуля на валидность
//! @param[in]   pData - указатель на данные (128 байт)
//! @retval      true - успешно, false - ошибка crc
//-----------------------------------------------------------------------------------------------------------
static bool SU_IsValidID(u8* pData)
{
    bool bFuncRes = false;
    u16 nTmpID;

    memcpy(&nTmpID, (pData + SU_MEM_POS_ID), sizeof(u16));

    if ((nTmpID & SU_ID_MASK) != SU_EMPTY_ID)
    {
        bFuncRes = true;
    }

    return bFuncRes;
}




//-----------------------------------------------------------------------------------------------------------
//! @brief       Проверка CRC снимка данных памяти субмодуля
//! @param[in]   pData - указатель на данные (128 байт)
//! @retval      true - успешно, false - ошибка crc
//-----------------------------------------------------------------------------------------------------------
static bool SU_CheckCrc(u8* pData)
{
    bool bFuncRes = false;
    u16 nTmpCrc = 0;
    u16 nCrc = 0;

    // CRC в H27, A27 расположена одинаково: 4 и 5 байты 
    nTmpCrc = eval_crc16(0,       (pData)+SU_MEM_POS_VERSION, SU_MEM_POS_CRC);
    nTmpCrc = eval_crc16(nTmpCrc, (pData)+SU_MEM_POS_SN,      SU_MAX_MEM_SIZE - SU_MEM_POS_SN);
    
    memcpy(&nCrc, (pData + SU_MEM_POS_CRC), sizeof(u16));

    if(nTmpCrc == nCrc)
    {
        bFuncRes = true;
    }

    return bFuncRes;
}





//-----------------------------------------------------------------------------------------------------------
//! @brief       Чтение калибровочных коэффициентов субмодуля в указанную структуру
//! @param[in]   pData      - указатель на данные (128 байт)
//! @param[out]  pMezMemStr - структура
//! @param[in]   nUnitType  - тип субмодуля
//! @retval      -
//-----------------------------------------------------------------------------------------------------------
static void SU_CopySingleCalibr(const u8* const pData, 
                                SU_MEZ_MEM_RDCD* const pMezMemStr,
                                const u8 nNum)
{
    u16 pTmpCalib[4];
    u16 offset;
    u16 scale;

    switch(nNum)
    {
        case(0):
            memcpy(&pTmpCalib, (pData + SU_MEM_POS_CALIB0), sizeof(pTmpCalib));
            
            for (int j = 0; j < 4; j+= 2) 
            {
                // Сохранение в текущем виде
                pMezMemStr->nCalibrCoef_0[j+0] = pTmpCalib[j+0];
                pMezMemStr->nCalibrCoef_0[j+1] = pTmpCalib[j+1];

                // Сохранение в формате 1\15
                offset = pTmpCalib[j+1];
                scale = pTmpCalib[j+0];

                pMezMemStr->fCalibrCoef_0[j+0] = (double)scale / 0x8000;
                
                if (offset < 0xC000) 
                {
                    pMezMemStr->fCalibrCoef_0[j+1] = (double)offset;
                } 
                else 
                {
                    pMezMemStr->fCalibrCoef_0[j+1] = (double)(*(i16 *)&offset);
                }
            }
        break;

        case(1):
            memcpy(&pTmpCalib, (pData + SU_MEM_POS_CALIB1), sizeof(pTmpCalib));
            
            for (int j = 0; j < 4; j+= 2) 
            {
                // Сохранение в текущем виде
                pMezMemStr->nCalibrCoef_1[j+0] = pTmpCalib[j+0];
                pMezMemStr->nCalibrCoef_1[j+1] = pTmpCalib[j+1];

                // Сохранение в формате 1\15
                offset = pTmpCalib[j+1];
                scale = pTmpCalib[j+0];

                pMezMemStr->fCalibrCoef_1[j+0] = (double)scale / 0x8000;
                
                if (offset < 0xC000) 
                {
                    pMezMemStr->fCalibrCoef_1[j+1] = (double)offset;
                } 
                else 
                {
                    pMezMemStr->fCalibrCoef_1[j+1] = (double)(*(i16 *)&offset);
                }
            }
        break;

        case(2):
            memcpy(&pTmpCalib, (pData + SU_MEM_POS_CALIB2), sizeof(pTmpCalib));
            
            for (int j = 0; j < 4; j+= 2) 
            {
                // Сохранение в текущем виде
                pMezMemStr->nCalibrCoef_2[j+0] = pTmpCalib[j+0];
                pMezMemStr->nCalibrCoef_2[j+1] = pTmpCalib[j+1];

                // Сохранение в формате 1\15
                offset = pTmpCalib[j+1];
                scale = pTmpCalib[j+0];

                pMezMemStr->fCalibrCoef_2[j+0] = (double)scale / 0x8000;
                
                if (offset < 0xC000) 
                {
                    pMezMemStr->fCalibrCoef_2[j+1] = (double)offset;
                } 
                else 
                {
                    pMezMemStr->fCalibrCoef_2[j+1] = (double)(*(i16 *)&offset);
                }
            }
        break;

        case(3):
            memcpy(&pTmpCalib, (pData + SU_MEM_POS_CALIB3), sizeof(pTmpCalib));
            
            for (int j = 0; j < 4; j+= 2) 
            {
                // Сохранение в текущем виде
                pMezMemStr->nCalibrCoef_3[j+0] = pTmpCalib[j+0];
                pMezMemStr->nCalibrCoef_3[j+1] = pTmpCalib[j+1];

                // Сохранение в формате 1\15
                offset = pTmpCalib[j+1];
                scale = pTmpCalib[j+0];

                pMezMemStr->fCalibrCoef_3[j+0] = (double)scale / 0x8000;
                
                if (offset < 0xC000) 
                {
                    pMezMemStr->fCalibrCoef_3[j+1] = (double)offset;
                } 
                else 
                {
                    pMezMemStr->fCalibrCoef_3[j+1] = (double)(*(i16 *)&offset);
                }
            }
        break;

        default:
        break;
    }

}



//-----------------------------------------------------------------------------------------------------------
//! @brief       Чтение калибровочных коэффициентов субмодуля в указанную структуру
//! @param[in]   pData      - указатель на данные (128 байт)
//! @param[out]  pMezMemStr - структура
//! @param[in]   nUnitType  - тип субмодуля
//! @retval      -
//-----------------------------------------------------------------------------------------------------------
static void SU_CopyAllCalibr(const u8* const pData, 
                             SU_MEZ_MEM_RDCD* const pMezMemStr,
                             const u8 nUnitType)
{    
    // Для H27 только 1 калибровка
    SU_CopySingleCalibr(pData, pMezMemStr, 0U);

    if (nUnitType == SU_SUBUNIT_TYPE_A27)
    {
        // Поддержка до 4 калибровок
        SU_CopySingleCalibr(pData, pMezMemStr, 1U);
        SU_CopySingleCalibr(pData, pMezMemStr, 2U);
        SU_CopySingleCalibr(pData, pMezMemStr, 3U);
    }
}



//-----------------------------------------------------------------------------------------------------------
//! @brief       Чтение серийного номера субмодуля в указанную структуру
//! @param[in]   pData - указатель на данные (128 байт)
//! @param[out]  pMezMemStr - структура
//! @retval      -
//-----------------------------------------------------------------------------------------------------------
static void SU_CopySN(const u8* const pData, 
                      SU_MEZ_MEM_RDCD* const pMezMemStr)
{
    u8 pTmpSN[6];
    memcpy(&pTmpSN,  (pData + SU_MEM_POS_SN), sizeof(pTmpSN));

    // младшие 3 бита последнего байта = размеру SN.
    // Нужно, чтобы заполнить нулями конечныый SN
    u8 nNewFormatSN = pTmpSN[5] & 0xF8; 
    
    if (nNewFormatSN == SU_SN_SIZE_MAGIC_NUM) 
    {
        char sernum_text[9];
        char zero_padding[9];

        int ser = pTmpSN[2] |
            ((u32)pTmpSN[3] << 8) |
            ((u32)pTmpSN[4] << 16);

        /* читаем с учётом длины серийника */
        size_t len = pTmpSN[5] & 0x07;
        sprintf(sernum_text, "%d", ser);
        size_t lenData = strlen(sernum_text);
        zero_padding[0] = 0;
        if (lenData < len) 
        {
            for(u32 j = 0; j < (len - lenData); ++j) 
            {
                zero_padding[j] = 48;
                zero_padding[j+1] = 0;
            }
        }
        sprintf((char *)pMezMemStr->sSerialNumber,
                "%i%c%s%s", pTmpSN[0], pTmpSN[1],
                zero_padding, sernum_text);
    } 
    else 
    {
        /* читаем по старому */
        int ser = pTmpSN[2] |
                ((u32)pTmpSN[3] << 8) |
                ((u32)pTmpSN[4] << 16);
        sprintf((char *)pMezMemStr->sSerialNumber,
            "%i%c%d", pTmpSN[0], pTmpSN[1],
            ser);
    }

    // Замена пробелов нулями (для отдельных вариантов субмодулей)
    for (int j = 2; j < 8; ++j) 
    {
        if (pMezMemStr->sSerialNumber[j] == 32)
        {
            pMezMemStr->sSerialNumber[j] = 48;
        }
    }

}



//-----------------------------------------------------------------------------------------------------------
//! @brief       Чтение данных о субмодуле в указанную структуру
//! @param[in]   pData - указатель на данные (128 байт)
//! @retval      true - успешно, false - ошибка crc
//-----------------------------------------------------------------------------------------------------------
static bool SU_GetUnitData(const u8* const pData, 
                              SU_MEZ_MEM_RDCD* const pMezMemStr)
{
    bool bFuncRes = false;
    u16 nTmpID;
    
    memcpy(&nTmpID, (pData + SU_MEM_POS_ID), sizeof(u16));
    nTmpID = (nTmpID & SU_ID_MASK);

    if ((nTmpID >= SU_ID_A27_START) && (SU_ID_A27_END >= nTmpID))
    {
        // Это модуль серии A27
        for (u16 i = 0; i < SU_MINFO_A27_SIZE; i++)
        {
            if (stMezInfoA27[i].ID == nTmpID) 
            {
                if (stMezInfoA27[i].ID == nTmpID) 
                {
                    // ID
                    pMezMemStr-> nID = stMezInfoA27[i].ID;
                    
                    // Name
                    strcpy((char*) pMezMemStr->sName, stMezInfoA27[i].Name);

                    // Version
                    memcpy(&(pMezMemStr-> nVersion), (pData + SU_MEM_POS_VERSION), sizeof(u16));
                    pMezMemStr-> nVersion = pMezMemStr-> nVersion + '0';

                    // Calibrs
                    SU_CopyAllCalibr(pData, pMezMemStr, SU_SUBUNIT_TYPE_A27);

                    // SN
                    SU_CopySN(pData, pMezMemStr);

                    bFuncRes = true;
                    break;
                } // if
            }
        } // for
    } // if
    else if ((nTmpID >= SU_ID_H27_START) && (SU_ID_H27_END >= nTmpID))
    {
        // Это модуль серии H27
        for (u16 i = 0; i < SU_MINFO_H27_SIZE; i++)
        {
            if (stMezInfoH27[i].ID == nTmpID) 
            {
                // ID
                pMezMemStr-> nID = stMezInfoH27[i].ID;
                
                // Name
                strcpy((char*) pMezMemStr->sName, stMezInfoH27[i].Name);

                // Version
                memcpy(&(pMezMemStr-> nVersion), (pData + SU_MEM_POS_VERSION), sizeof(u16));
                pMezMemStr-> nVersion = pMezMemStr-> nVersion + '0';

                // Calibrs
                SU_CopyAllCalibr(pData, pMezMemStr, SU_SUBUNIT_TYPE_H27);
                
                // SN
                SU_CopySN(pData, pMezMemStr);
                
                bFuncRes = true;
                break;
            } // if
        } // for
    } // else if
    else
    {
        // Модуль не поддерживается
    }

    return bFuncRes;
}













// Получение информации о всех установленных субмодулях
static void f_update_subunits_info(void)
{
    u8 pTmpBuf[SU_MAX_MEM_SIZE];

    for (u8 j = 0U; j < SU_SUBUNITS_MAX_QTY; j++)
    {
        // Чтение всей памяти
        subm_read(j, 0U, pTmpBuf, SU_MAX_MEM_SIZE);

        // Проверка ID
        if (true == SU_IsValidID(pTmpBuf))
        {
            // Проверка CRC
            if (true == SU_CheckCrc(pTmpBuf))
            {
                // Чтение данных субмодуля
                if (true == SU_GetUnitData(pTmpBuf, &stReducedMemData[j]))
                {
                    // Модуль идентифицирован
                    stReducedMemData[j].nError = SU_ERR_OK;
                    DBG_LOGD("OK, slot= %d, name= %s\n", j, stReducedMemData[j].sName); 
                }
                else
                {
                    // Субмодуль не поддерживается данной версией ПО
                    stReducedMemData[j].nError = SU_ERR_UNIT_ID_NOT_SUP;
                    DBG_LOGD("NOT SUPPORT, slot= %d\n", j);
                }
            } // if 
            else
            {
                // Ошибка CRC
                stReducedMemData[j].nError = SU_ERR_UNIT_CRC;
                DBG_LOGD("CRC ERROR, slot= %d\n", j);
            }
        } // if 
        else
        {
            // Память модуля пуста или нет модуля
            stReducedMemData[j].nError = SU_ERR_UNIT_ID_NOT_VALID;
            DBG_LOGD("FLASH IS EMPTY, slot= %d\n", j);
        }
    } // for
}


//***********************************************************************************************************
//******************************* Определения глобальных (публичных) функций ********************************
//***********************************************************************************************************


//-----------------------------------------------------------------------------------------------------------
// Тест чтения\записи\стирания во все ячейки всех субмодулей
//-----------------------------------------------------------------------------------------------------------
void subm_tst_read_write_erase(void)
{
    
    for (int i = 0; i < 128; i++)
    {
        pWrZeroData[i] = 0xFF;
    }

    // Чтение
    subm_read(0, 0, pSubUnitData, 128);
    subm_read(1, 0, pSubUnitData, 128);
    subm_read(2, 0, pSubUnitData, 128);
    subm_read(3, 0, pSubUnitData, 128);
    subm_read(4, 0, pSubUnitData, 128);
    subm_read(5, 0, pSubUnitData, 128);
    subm_read(6, 0, pSubUnitData, 128);
    subm_read(7, 0, pSubUnitData, 128);
    
    // Запись 
    subm_write(0, 0, &pWrData[0], 1);
    subm_write(1, 0, &pWrData[1], 1);
    subm_write(2, 0, &pWrData[2], 1);
    subm_write(3, 0, &pWrData[3], 1);
    subm_write(4, 0, &pWrData[4], 1);
    subm_write(5, 0, &pWrData[5], 1);
    subm_write(6, 0, &pWrData[6], 1);
    subm_write(7, 0, &pWrData[7], 1);

    // 
    subm_read(0, 0, pSubUnitData, 128);
    subm_read(1, 0, pSubUnitData, 128);
    subm_read(2, 0, pSubUnitData, 128);
    subm_read(3, 0, pSubUnitData, 128);
    subm_read(4, 0, pSubUnitData, 128);
    subm_read(5, 0, pSubUnitData, 128);
    subm_read(6, 0, pSubUnitData, 128);
    subm_read(7, 0, pSubUnitData, 128);
    
    // Стирание
    subm_write(0, 0, pWrZeroData, 128);
    subm_write(1, 0, pWrZeroData, 128);
    subm_write(2, 0, pWrZeroData, 128);
    subm_write(3, 0, pWrZeroData, 128);
    subm_write(4, 0, pWrZeroData, 128);
    subm_write(5, 0, pWrZeroData, 128);
    subm_write(6, 0, pWrZeroData, 128);
    subm_write(7, 0, pWrZeroData, 128);
}



//-----------------------------------------------------------------------------------------------------------
// Получение информации об установленных субмодулях
//-----------------------------------------------------------------------------------------------------------
void subm_tst_get_info(void)
{
    f_update_subunits_info();
}





//***********************************************************************************************************
//******************************** Определения локальных (приватных) функций ********************************
//***********************************************************************************************************



//*********************************************** КОНЕЦ ФАЙЛА ***********************************************
