 // Файл с низкоуровневыми коммандами для W25Q16JV.
 // v 1.0 от 09/10/20
 // Автор: Сычев А.

 // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
 // №№№№№№№№№№№№№№№№№№№№№№ НИЗКОУРОВНЕВЫЕ НЕБЕЗОПАСНЫЕ ФУНКЦИИ №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
 // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
 #define W25Q16JV_LOWLEVEL
 #include "drivers\flash\base\flash_api_types.h"
 #include "drivers\flash\w25q\common\W25Q_HAL.h"        // аппаратная абстрация от SSP
 #include "drivers\flash\w25q\lowlevel\W25Q16JV_LL.h"        // определения для W25Q16JV
 #include "drivers\flash\w25q\lowlevel\W25Q16JV_LL_func.h"   // прототипы LL-функций

 // ########################################################################################################
 static union
 {
    __flash_protectionregister_t        protSectorFlagsBuffer;
    struct
    {
    __flash_sectorprotectionregister_t  sectorFlagsBuffer1;  // основной
    __flash_sectorprotectionregister_t  sectorFlagsBuffer2;  // копия
    };
    __FLASH_BYTE                        pageBuffer[ __PAGE_SIZE ];
 }
 flash_hal_Buffer;

 // __flash_hal__getBuffer - запросить внутренний буфер низкого уровня
 static void * __flash_hal__getBuffer( __flash_internal_buffer_type_t type, size_t * pRetBufSize )
 {
     void * rValue = NULL; *pRetBufSize = 0;

     switch( type )
     {
         case fibufSectorFlags:      *pRetBufSize = sizeof(flash_hal_Buffer.sectorFlagsBuffer1);    rValue = &flash_hal_Buffer.sectorFlagsBuffer1;
         case fibufSectorFlags_2:    *pRetBufSize = sizeof(flash_hal_Buffer.sectorFlagsBuffer2);    rValue = &flash_hal_Buffer.sectorFlagsBuffer2;
         case fibufProtSectorFlags:  *pRetBufSize = sizeof(flash_hal_Buffer.protSectorFlagsBuffer); rValue = &flash_hal_Buffer.protSectorFlagsBuffer;
         case fibufPageIO:           *pRetBufSize = sizeof(flash_hal_Buffer.pageBuffer);            rValue = &flash_hal_Buffer.pageBuffer[0];
     }

     // обнуление буфера
     if( NULL != rValue && 0 != *pRetBufSize )
     {
         __imp_sys_memzero( rValue, *pRetBufSize );
     }

     return rValue;
 }
 // ########################################################################################################

 // __flash_hal__writeDisable - запрещение записи в чип
 static void __flash_hal__writeDisable();
 // __flash_hal__writeEnable - разрешение записи в чип
 static void __flash_hal__writeEnable();
 // __flash_hal__statusRead1 - чтение регистра статуса 1
 static void __flash_hal__statusRead1( __flash_status1_t * pcReadRegister );    // буфер для чтения статус-регистра
 // __flash_hal__statusRead2 - чтение регистра статуса 3
 static void __flash_hal__statusRead2( __flash_status2_t * pcReadRegister );    // буфер для чтения статус-регистра
 // __flash_hal__statusRead3 - чтение регистра статуса 3
 static void __flash_hal__statusRead3( __flash_status3_t * pcReadRegister );    // буфер для чтения статус-регистра
 // __flash_hal__statusWrite1 - запись регистра статуса 1
 static void __flash_hal__statusWrite1( __flash_status1_t * pcWriteRegister );   // буфер для записи статус-регистра
 // __flash_hal__statusWrite2 - запись регистра статуса 3
 static void __flash_hal__statusWrite2( __flash_status2_t * pcWriteRegister );   // буфер для записи статус-регистра
 // __flash_hal__statusWrite3 - запись регистра статуса 3
 static void __flash_hal__statusWrite3( __flash_status3_t * pcWriteRegister );   // буфер для записи статус-регистра
 // __flash_hal__pageErase - стирание страницы
 static void __flash_hal__pageErase( __FLASH_WORD wPageNum );      // номер стираемой страницы
 // __flash_hal__blockErase - стирание блока
 // static void __flash_hal__blockErase( __FLASH_WORD wBlockNum );     // номер стираемого блока
 // __flash_hal__sectorErase - стирание секторов с номером 1 и выше
 static void __flash_hal__sectorErase( __FLASH_WORD wSector );     // номер сектора 1-63
 // __flash_hal__sleepMode - переход в режим пониженного энергопотребления
 static void __flash_hal__sleepMode();
 // __flash_hal__wakeUp - выход из режима пониженного энергопотребления
 static void __flash_hal__wakeUp();
 // __flash_hal__arrayRead - последовательное чтение (массив)
 static void __flash_hal__arrayRead ( __FLASH_DWORD w24Address,   // адрес начала чтения
                                __FLASH_BYTE * pDataRead,   // указатель на буфер-приемник данных
                                __FLASH_WORD wCntToRead );  // количество данных на чтение
 // __flash_hal__chunkProgram - программирование страницы без стирания блока/страницы
 static void __flash_hal__chunkProgram( __flash_pageprogramptr_t pdata,
                                 __FLASH_DWORD w24Address ); // адрес начала записи
 // __flash_hal__securityRegister1Erase - стирает регистр безопасности
 static void __flash_hal__securityRegister1Erase();
 // __flash_hal__securityRegister2Erase - стирает регистр безопасности
 static void __flash_hal__securityRegister2Erase();
 // __flash_hal__securityRegister3Erase - стирает регистр безопасности
 static void __flash_hal__securityRegister3Erase();
 // __flash_hal__securityRegister1Write - записывает регистр безопасности
 static void __flash_hal__securityRegister1Write( __flash_securityregister_t * contents ); // содержимое регистра безопасности
 // __flash_hal__securityRegister2Write - записывает регистр безопасности
 static void __flash_hal__securityRegister2Write( __flash_securityregister_t * contents ); // содержимое регистра безопасности
 // __flash_hal__securityRegister3Write - записывает регистр безопасности
 static void __flash_hal__securityRegister3Write( __flash_securityregister_t * contents ); // содержимое регистра безопасности
 // __flash_hal__securityRegister1Read - прочитывает регистр безопасности
 static void __flash_hal__securityRegister1Read( __flash_securityregister_t * contents ); // буфер-приемник содержимого регистра блокировки
 // __flash_hal__securityRegister2Read - прочитывает регистр безопасности
 static void __flash_hal__securityRegister2Read( __flash_securityregister_t * contents ); // буфер-приемник содержимого регистра блокировки
 // __flash_hal__securityRegister3Read - прочитывает регистр безопасности
 void __flash_hal__securityRegister3Read( __flash_securityregister_t * contents ); // буфер-приемник содержимого регистра блокировки
 // __flash_hal__manufacturerIdRead - чтение идентификатора производителя
 static __FLASH_DWORD __flash_hal__manufacturerIdRead( __flash_id_t * pManufacturerID );// необязательный параметр-буфер для приема расширенной информации
 // __flash_hal__uniqueIdRegisterRead -
 static void __flash_hal__uniqueIdRegisterRead( __flash_uniqueidregister_t * contents ); // буфер-приемник содержимого регистра UniqueID
 // __flash_hal__uniqueIdRegisterValidate - прочитывает регистр UniqueID и выполняет
 static bool __flash_hal__uniqueIdRegisterValidate( __flash_uniqueidregister_t * contents ); // буфер-приемник содержимого регистра UniqueID
 #if W25QXXX_POWER_MANAGEMENT > 0
 // __flash_hal__powerOn - реализует управление питанием микросхемы памяти (подает питание)
 static void __flash_hal__powerOn();

 // __flash_hal__powerOff - реализует управление питанием микросхемы памяти (снимает питание)
 static void __flash_hal__powerOff();

 // __flash_hal__powerPulse - реализует управление питанием микросхемы - формирует импульс перезагрузки
 static void __flash_hal__powerPulse( __FLASH_WORD nCooldownTime_ms, __FLASH_WORD nStartupTime_ms );
 #endif
 #if W25QXXX_RESET_MANAGEMENT > 0
 // __flash_hal__resetAssert - реализует управление сигналом сброса микросхемы памяти (устанавливает сигнал сброса)
 static void __flash_hal__resetAssert();

 // __flash_hal__resetRelease - реализует управление сигналом сброса микросхемы памяти (снимает сигнал сброса)
 static void __flash_hal__resetRelease();

 // __flash_hal__resetPulse - реализует управление сигналом сброса микросхемы памяти (подает законченный импульс сброса)
 static void __flash_hal__resetPulse();
 #endif
 // __flash_hal__initialize - Низкоуровневая инициализация флешпамяти
 // # возвращает 0, если все успешно, или код ошибки
 static flash_err_t __flash_hal__initialize();
 // __flash_hal__finalizePrepare - Низкоуровневая функция для подготовки флешпамяти к деинициализации драйвера
 static flash_err_t __flash_hal__finalizePrepare();
 // __flash_hal__dirtyDetect - Низкоуровневое определение наличия чипа (доступности)
 // Используется во время первичной инициализации в flash_initialize().
 static flash_err_t __flash_hal__dirtyDetect();
  // __flash_hal__startupDetect - Низкоуровневое определение наличия чипа (доступности)
 // Используется верхним уровнем API для определения модели установленного чипа памяти
 static flash_err_t __flash_hal__startupDetect();
 // __flash_hal__getReadyFast - проверка занятости без ожидания
 static flash_err_t __flash_hal__getReadyFast( );
 // служебная функция для проверки готовности чипа
 static int __flash_smartWaitms( __FLASH_WORD wTimems );
 // __flash_hal__globalLock - установить блокировку записи на все сектора/блоки
 static void __flash_hal__globalLock(); // [W25Q OK]
 // __flash_hal__globalUnlock - снять блокировку записи со всех секторов/блоков
 static void __flash_hal__globalUnlock(); // [W25Q OK]
 // __flash_hal__protectStateWrite - записывает индивидуальную блокировку сектора/блока
 static void __flash_hal__protectStateWrite( size_t nProtSector, bool protectState );
 // __flash_hal__protectStateRead - считывает индивидуальную блокировку сектора/блока
 static bool __flash_hal__protectStateRead( size_t nProtSector );

 // __flash_hal__protectregister_write - записывает регистр защиты (биты индивидуальной защиты секторов/блоков)
 // void __flash_hal__protectregister_write( __flash_protectionregister_t * contents );
 // __flash_hal__protectregister_read - прочитывает регистр защиты (биты индивидуальной защиты секторов/блоков)
 // void __flash_hal__protectregister_read( __flash_protectionregister_t * contents );
 // ########################################################################################################

 const W25Q_LL_Routines_t W25Q_LL_Routines =
 {
     .operations =
     {
        .writeDisable = __flash_hal__writeDisable,
        .writeEnable = __flash_hal__writeEnable,
     },

     .status =
     {
        .statusRead1  = __flash_hal__statusRead1,
        .statusRead2  = __flash_hal__statusRead2,
        .statusRead3  = __flash_hal__statusRead3,
        .statusWrite1 = __flash_hal__statusWrite1,
        .statusWrite2 = __flash_hal__statusWrite2,
        .statusWrite3 = __flash_hal__statusWrite3,
     },

     .security =
     {
        .securityRegister1Erase = __flash_hal__securityRegister1Erase,
        .securityRegister2Erase = __flash_hal__securityRegister2Erase,
        .securityRegister3Erase = __flash_hal__securityRegister3Erase,
        .securityRegister1Write = __flash_hal__securityRegister1Write,
        .securityRegister2Write = __flash_hal__securityRegister2Write,
        .securityRegister3Write = __flash_hal__securityRegister3Write,
        .securityRegister1Read = __flash_hal__securityRegister1Read,
        .securityRegister2Read = __flash_hal__securityRegister2Read,
        .securityRegister3Read = __flash_hal__securityRegister3Read,
     },

     .protection =
     {
        .globalLock = __flash_hal__globalLock,
        .globalUnlock = __flash_hal__globalUnlock,
        .protectStateWrite = __flash_hal__protectStateWrite,
        .protectStateRead = __flash_hal__protectStateRead,
      //.readState = __flash_hal__protectregister_read,
      //.writeState = __flash_hal__protectregister_write,
     },

     .sleep =
     {
        .sleepMode = __flash_hal__sleepMode,
        .wakeUp = __flash_hal__wakeUp,
     },

     .data =
     {
        .arrayRead = __flash_hal__arrayRead,
        .chunkProgram = __flash_hal__chunkProgram,
        .pageErase = __flash_hal__pageErase,
        .sectorErase = __flash_hal__sectorErase,
     },

     .id =
     {
        .manufacturerIdRead = __flash_hal__manufacturerIdRead,
        .uniqueIdRegisterRead = __flash_hal__uniqueIdRegisterRead,
        .uniqueIdRegisterValidate = __flash_hal__uniqueIdRegisterValidate,
     },

     .service =
     {
        .startupDetect = __flash_hal__startupDetect,
        .initialize = __flash_hal__initialize,
        .finalizePrepare = __flash_hal__finalizePrepare,
        .dirtyDetect = __flash_hal__dirtyDetect,
        .getBuf = __flash_hal__getBuffer,
     },

     #if W25QXXX_POWER_MANAGEMENT > 0
     .power =
     {
        .powerOn = __flash_hal__powerOn,
        .powerOff = __flash_hal__powerOff,
        .powerPulse = __flash_hal__powerPulse,
     },
     #endif

     #if W25QXXX_RESET_MANAGEMENT > 0
     .reset =
     {
        .resetAssert = __flash_hal__resetAssert,
        .resetRelease = __flash_hal__resetRelease,
        .resetPulse = __flash_hal__resetPulse,
     },
     #endif

     .ready =
     {
        .getReadyFast = __flash_hal__getReadyFast,
        .smartWaitms = __flash_smartWaitms,
     }
 };

 // ########################################################################################################
 // Глобальные переменные

 #pragma pack( push, 1 )
 #ifdef W25QXXX_FLASH_RAM_PLACE
 _PLACEIN( W25QXXX_FLASH_RAM_PLACE )
 #endif
 static union /* __flash_global */
 {
      __FLASH_BYTE alloc_place[ __FLASH_LL_SVCMEM_SIZE ];

      struct /* __flash_var */
      {
             // __FLASH_BYTE pagebuffer[ __FULL_PAGE_SIZE ];         // НЕ ТРЕБУЕТСЯ! буфер под чтение/запись страниц

             __FLASH_BYTE cmdbuffer [ __FLASH_LL_COMMAND_MAXSIZE ];  // буфер для выполения команд
      };
 };
 #pragma pack( pop )

 //__flash_protectionregister_t * __flash_internal_getbuffer_protect() { return &protectionregister; }

 // ########################################################################################################
 // ########################################################################################################
 // ########################################################################################################
 // ########################################################################################################
 // ########################################################################################################
 // ########################################################################################################

 // служебные функции управления CS

 // установка сигнала ChipSelect в активное состояние
 static inline void chip_sel_active() // [W25Q OK]
 {
        __FLASH_HAL_CS_LO();
        __FLASH_WAITus(_TIME_CSCLR_us);
 }

 // установка сигнала ChipSelect в неактивное состояние
 static inline void chip_sel_inactive() // [W25Q OK]
 {
        __FLASH_WAITus(_TIME_CSSET_us);
        __FLASH_HAL_CS_HI();
        __FLASH_WAITus(_TIME_CSHLD_us);  // минимальное время между командами
 }

#if W25QXXX_NO_MS_DELAYS == 1
 // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
 // __int_sys_delayms - служебная функция ожидания в мс, если аналог отсутствует у пользователя.
 // Опция W25QXXX_NO_MS_DELAYS позволяет заменить задержку в мс на задержку в мкс.
 static inline void __int_sys_delayms( __FLASH_WORD timeout ) // [W25Q OK]
 {
     __imp_sys_delayus( timeout * 1000 );
 }
#endif

 // --------------------------------------------------------------------------------------------------------
 // __flash_smartWaitms - комбинированая функция ожидания заданного количества миллисекунд с
 // проверкой состояния чипа на предмет того, занят он, или нет. Если не занят,
 // функция возвращает управление сразу же. Если занят все указанное время,
 // функция возвратит управление через указанный таймаут (не меньший, но, возможно, больший).
 //
 // * не изменяет содержимое внутренних SRAM буферов
 // * использует системные вызовы для определения количества прошедшего времени
 // * потоко-небезопасная функция
 // * производит опрос чипа командой "Запрос статусного регистра" (Status Register Read)
 // * функция использует вызов функций бесконечного чтения статуса
 //
 static void __flash_hal__part_statusread_stage1( __flash_status1_t * pcReadRegister );
 static void __flash_hal__part_statusread_stage2( __flash_status1_t * pcReadRegister );
 static void __flash_hal__part_statusread_stage3( __flash_status1_t * pcReadRegister );
 //
 // * функция возвращает 1, если достоверно известно, что флаг BUSY сброшен (устройство готово), иначе 0 (требуется повторная проверка)
 //
 static int __flash_smartWaitms( __FLASH_WORD wTimems ) // [W25Q OK]
 {
     __flash_status1_t r_status;                                                 // статус-регистр

     __flash_hal__part_statusread_stage1( &r_status );                           // начинаем считывать

     // --   --   --   --   --   --   для коротких задержек   --   --   --   --   --   --   --   --   --   --
     if ( !r_status.bBusy )                                                       // сразу проверяем, если устройство свободно, возвращаем управление
     {
        __flash_hal__part_statusread_stage3( 0 );                                // завершаем чтение регистра
        return 1;
     }

     if ( wTimems < W25QXXX_SMART_WAIT_THRESHOLD )                               // для малых времен ожидания накладно опрашивать чип постоянно
     {
        __FLASH_WAITms(wTimems);                                                 // выжидаем указанное время

        __flash_hal__part_statusread_stage3( &r_status );                        // проверяем регистр

        if ( !r_status.bBusy )                                                    // если свободно - вернем 1
        return 1;

        return 0;                                                                // если занято - вернем 0
     }

     // --   --   --   --   --   --   для длинных задержек   --   --   --   --   --   --   --   --   --   --

     __FLASH_DWORD dwStartCounter = __FLASH_SMART_GETTIMER();                    // получаем отметку времени

     for(/*|*/ __FLASH_DWORD dwDeltaCounter = 0;                                 // счетчик задержки
         /*|*/ dwDeltaCounter < wTimems;                                         // проверяем, задержались ли мы на указанное время или еще нет
         /*|*/ dwDeltaCounter = ( __FLASH_SMART_GETTIMER() - dwStartCounter ) )  // обновляем счетчик задержки
     {
         __flash_hal__part_statusread_stage2( &r_status );                       // читаем статус

         if ( !r_status.bBusy )                                                   // проверяем, не освободился ли чип?
         {
             __flash_hal__part_statusread_stage3( 0 );                           // да, освободился, завершаем чтение

             return 1;                                                           // возвращаем 1 (освободился)
         }

         __FLASH_WAITms(1);                                                      // выжидаем квант времени
     }

     __flash_hal__part_statusread_stage3( &r_status );                           // время вышло, завершаем чтение

     if ( !r_status.bBusy )                                                       // последний раз проверяем статус
     return 1;                                                                   // свободен!

     return 0;                                                                   // чип до сих пор занят
 }

 // ########################################################################################################
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__part_statusread - группа скрытых низкоуровневых функций, позволяющая осуществлять постоянное сканирование регистра
 // статуса. Чип предоставляет возможность считывать регистр статуса без отправки команды на чтение при повторном чтении,
 // до установки CS в неактивное положение. Т.О возможно передать команду один раз, затем читать статус бесконечно.
 // Функции должны вызываться в порядке нумерации, при этом стадия 2 может быть пропущена. Функии проверяют параметр
 // pcReadRegister на NULL, если он отличен от NULL, считывают в него содержмое регистра, иначе ничего не считывают.
 // Пример ожидания завершения операции стирания:
 // [code]
 //
 //  __flash_status_t status;                           // статус-регистр
 //
 //  __flash_hal_pageerase( 0 );                        // стираем 0 страницу
 //  __flash_hal__part_statusread_stage1( &status );    // иницируем чтение статуса
 //
 //  while( status.bBusy )
 //  __flash_hal__part_statusread_stage2( &status );    // повторяем чтение статуса
 //
 //  __flash_hal__part_statusread_stage3( 0 );          // завершаем чтение статуса
 //
 // [/code]
 // __flash_hal__part_statusread_stage1 - 1 стадия (начальная) команды бесконечного чтения статуса занятости
 // __flash_hal__part_statusread_stage2 - 2 стадия (промежуточная) команды бесконечного чтения статуса занятости
 // __flash_hal__part_statusread_stage3 - 3 стадия (заключительная) команды бесконечного чтения статуса занятости
 //
 // * потоко-небезопасная функция
 // * НЕ задерживает выполнение: требуется внешний цикл для организации сканирования

 static void __flash_hal__part_statusread_stage1( __flash_status1_t * pcReadRegister ) // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_statusread_t*) cmdbuffer )->opcode  = _ACMD_STATREAD_1;

       // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_statusread_t) );

      if( pcReadRegister )
      // читаем регистр
      __FLASH_HAL_RD( pcReadRegister,  sizeof(__flash_status1_t) );
 }
 //-----      -------     ------    ------  ------  -------   ------
 static void __flash_hal__part_statusread_stage2( __flash_status1_t * pcReadRegister ) // [W25Q OK]
 {
      if( pcReadRegister )
      // читаем регистр
      __FLASH_HAL_RD( pcReadRegister,  sizeof(__flash_status1_t) );
 }
 //-----      -------     ------    ------  ------  -------   ------
 static void __flash_hal__part_statusread_stage3( __flash_status1_t * pcReadRegister ) // [W25Q OK]
 {
      if( pcReadRegister )
      // читаем регистр
      __FLASH_HAL_RD( pcReadRegister,  sizeof(__flash_status1_t) );

      chip_sel_inactive();
 }
 // --------------------------------------------------------------------------------------------------------
 // ########################################################################################################
 // __flash_hal__getReadyFast - проверка занятости без ожидания
 //
 // # Возвращаемое значение: FLERR_SUCCESS если свободен, иначе FLERR_TIMEOUT
 static flash_err_t __flash_hal__getReadyFast( ) // [W25Q OK]
 {
        __flash_status1_t  status;

        __flash_hal__statusRead1( &status );

        if( !status.bBusy )
        return FLERR_SUCCESS;

        return FLERR_TIMEOUT;
 }
/*
 // --------------------------------------------------------------------------------------------------------
 // ########################################################################################################
 // __flash_hal__getprotectstate - получение состояния общего бита защиты
 //
 // # Возвращаемое значение - FLERR_PROTECT_ENABLED, если бит защиты установлен, иначе FLERR_PROTECT_DISABLED
 static flash_err_t __flash_hal__getprotectstate( ) // [W25Q WRONG]
 {
        __flash_status1_t  status;

        __flash_hal__statusRead1( &status );

        if( status.cBlkProt )
        return FLERR_PROTECT_ENABLED;
        return FLERR_PROTECT_DISABLED;
 }
*/
 // --------------------------------------------------------------------------------------------------------
 // ########################################################################################################
 // __flash_hal__dirtyDetect - Низкоуровневое определение наличия чипа (доступности)
 // Используется во время первичной инициализации в flash_initialize().
 static flash_err_t __flash_hal__dirtyDetect() // [W25Q OK]
 {
    // сначала пытаемся прочитать идентификатор:

    // чтение регистра информации
    __FLASH_DWORD flashID =  __flash_hal__manufacturerIdRead( 0 );

    // определяем наличие флешки по идентификатору
    if( ((__flash_rvid_t*) &flashID)->FullId == __FLASH_INVALID_ID )
    {
        // похоже, что чипа нет, обнаружить не удалось
        return FLERR_LL_INITFAIL_NOTFOUND;
    }

    // еще не все, нужно убедиться, что устройство отвечает:
    // читаем статус
    if( ! __flash_smartWaitms( W25QXXX_API_TIMEOUT ) )
    {
        // либо статус не читается, либо устройство занято.
        // т.к. функция предназначена для использования во время инициализации,
        // предполагается, что чип должен быть свободен.
        // Поэтому возвращаем ошибку
        return FLERR_UNEXPECTED_BUSY;
    }

    return FLERR_SUCCESS;
 }
 // --------------------------------------------------------------------------------------------------------

 // --------------------------------------------------------------------------------------------------------
 // ########################################################################################################
 // __flash_hal__finalizePrepare - Низкоуровневая функция для подготовки флешпамяти к деинициализации драйвера
 // Выполняет действия, направленные на завершение работы с флешпамятью и подготовки ее к выключению
 // Вызывается в момент окончательной остановки работы с флешпамятью
 static flash_err_t __flash_hal__finalizePrepare() // [W25Q OK]
 {
     // Сначала требуется проверить режим защиты записи:

     // если разрешено управление аппаратным сигналом защиты записи
     #if W25QXXX_HW_WR_PROTECT
     // если аппаратная защита записи отключена в настройках
     #if W25QXXX_HW_WR_PROTECT_KEEPUNPROTECTED == 1
     // требуется установить аппаратную защиту записи после деинициализации драйвера
     __imp_flash_hwwrprotect_assert();
     #endif
     #endif

     // Потом проверяем статус готовности
     if( ! (__flash_smartWaitms( W25QXXX_API_TIMEOUT ) ) )
     {
         // устройство занято.
         return FLERR_UNEXPECTED_BUSY;
     }

     return FLERR_SUCCESS;
 }
 // --------------------------------------------------------------------------------------------------------

 // --------------------------------------------------------------------------------------------------------
 // ########################################################################################################
 // __flash_hal__startupDetect - Низкоуровневое детектирование чипа флешпамяти
 // Производит начальную настройку аппаратуры для возможности определения наличия чипа на шине
 static flash_err_t __flash_hal__startupDetect()
 {
// если разрешено управление питанием
      #if W25QXXX_POWER_MANAGEMENT
      // подать питание на микросхему
      __flash_hal__powerOn();

      __FLASH_WAITus( _TIME_STRUP_us );
      #endif

      // если разрешено управление питанием
      #if W25QXXX_RESET_MANAGEMENT
      // снять сигнлал сброса с чипа
      __flash_hal__resetRelease();

      __FLASH_WAITus( _TIME_RSTRC_us );
      #endif

      // пробуждаем чип, если тот находится в режиме сна
      // иначе он не будет воспринимать команды.
      // Неизвестно, был ли чип в режиме гибернации, но это не важно
      __flash_hal__wakeUp();

      // предварительный таймаут запуска - ждем максимальное время до готовности записи во флеш
      __FLASH_WAITms( _TIME_START_ms );

      // производится несколько две попытки достучаться до чипа
      flash_err_t dirty_status = FLERR_GENERIC_ERROR;
      for( size_t attempt = 0; attempt < 2; ++attempt )
      {
          // пробуем "грязное" чтение идентификатора.
          // Результат чтения не сохраняется, требуется обнаружить
          // лишь присуствие на шине и определить готовность
          // Поэтому как остуствие на шине, так и занятость чипа расценивается
          // как ошибка.
          dirty_status = __flash_hal__dirtyDetect();

          if( FLASH_ERROR(dirty_status) )
          {
              // если первая попытка провалена, и __flash_hal__dirtyDetect после сброса
              // дает ошибку - значит возвращаем ошибку
              if( attempt > 0 )
               return dirty_status;

              // если разрешено управление сигналом сброса
              #if W25QXXX_RESET_MANAGEMENT

                // Формируем импульс сброса:
                __flash_hal__resetPulse();

              #else
                // управление сигналом сброса недоступно

                // а управление питанием доступно?
                #if W25QXXX_POWER_MANAGEMENT

                  // Формируем импульс подачи питания
                  __flash_hal__powerPulse( _TIME_COOLDOWN_ms, _TIME_START_ms );

                #else

                  // Проверка доступности вернула ошибку.
                  // Управление питанием и сигналом сброса недоступно
                  // Возвращаем ошибку
                  return dirty_status;

                #endif

              #endif
          } else break;
      }

      if( FLASH_ERROR(dirty_status) )
        return dirty_status;

      // чтение регистра информации
      __FLASH_DWORD flashID =  __flash_hal__manufacturerIdRead( 0 );

      // определяем наличие флешки по идентификатору
      if( ((__flash_rvid_t*) &flashID)->FullId == __FLASH_INVALID_ID )
      {
          return FLERR_LL_INITFAIL_NOTFOUND;
      }

      __flash_uniqueidregister_t uniqueidRegister;
      if( !__flash_hal__uniqueIdRegisterValidate( &uniqueidRegister ) )
      {
          return FLERR_LL_INITFAIL_NOTFOUND;
      }

      // проверяем ID производителя
      #ifdef W25QXXX_FLASH_DESIRED_ID
      if( ((__flash_rvid_t*) &flashID)->LowId != W25QXXX_FLASH_DESIRED_ID )
      {
          return FLERR_LL_INITFAIL_WRONGID;
      }
      #endif

      // проверяем вместимость чипа
      #ifdef W25QXXX_FLASH_DESIRED_DENSITY
      if( ((__flash_rvid_t*) &flashID)->DevId.Density != W25QXXX_FLASH_DESIRED_DENSITY )
      {
          return FLERR_LL_INITFAIL_WRONGDENSITY;
      }
      #endif

      // проверяем семейство чипа
      #if defined(W25QXXX_FLASH_DESIRED_FAMILY_ALT1) || defined(W25QXXX_FLASH_DESIRED_FAMILY_ALT2)
      switch( ((__flash_rvid_t*) &flashID)->DevId.Family )
      {
         #if defined(W25QXXX_FLASH_DESIRED_FAMILY_ALT1)
         case W25QXXX_FLASH_DESIRED_FAMILY_ALT1:
         #endif
         #if defined(W25QXXX_FLASH_DESIRED_FAMILY_ALT2)
         case W25QXXX_FLASH_DESIRED_FAMILY_ALT2:
         #endif
         break;
         default: return FLERR_LL_INITFAIL_WRONGFAMILY;
      }
      #endif

      return FLERR_SUCCESS;
 }

 // --------------------------------------------------------------------------------------------------------
 // ########################################################################################################
 // __flash_hal__initialize - Низкоуровневая инициализация драйвера флешпамяти
 //
 // * определение наличия подключенной микросхемы
 // * чтение идентификатора и определение производителя
 // * контроль правильности:
 //   - идентификатора производителя
 //   - типа устройства, семейства (DevId)
 //   - размера чипа
 // * установка и проверка размера страницы
 //
 // # Возвращаемое значние - код выполнения
 static flash_err_t __flash_hal__initialize() // [W25Q OK]
 {
      // первичная инициализация шины и чипа
      flash_err_t detectStatus = __flash_hal__startupDetect();

      if( FLASH_ERROR(detectStatus) )
        return detectStatus;

      // если разрешено управление аппаратным сигналом защиты записи
      #if W25QXXX_HW_WR_PROTECT
      // если аппаратная защита записи отключена в настройках
      #if W25QXXX_HW_WR_PROTECT_KEEPUNPROTECTED == 1
      // требуется снять аппаратную защиту записи на всё время работы драйвера
      __imp_flash_hwwrprotect_release();
      #endif
      #endif

      return FLERR_SUCCESS;
 }
 // ########################################################################################################
 // ########################################################################################################
 // ########################################################################################################
 // ########################################################################################################
 // ########################################################################################################
 // ########################################################################################################
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__writeEnable - разрешение записи в чип
 //
 // * потоко-небезопасная функция
 // * НЕ задерживает выполнение
 static void __flash_hal__writeEnable() // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_writecontrol_t*) cmdbuffer )->opcode  = _SCMD_WRENABLE;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_writecontrol_t) );

      chip_sel_inactive();
 }
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__writeDisable - запрещение записи в чип
 //
 // * потоко-небезопасная функция
 // * НЕ задерживает выполнение
 static void __flash_hal__writeDisable() // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_writecontrol_t*) cmdbuffer )->opcode  = _SCMD_WRDISABLE;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_writecontrol_t) );

      chip_sel_inactive();
 }
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__statusRead1 - чтение регистра статуса 1
 //
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция
 // * НЕ задерживает выполнение

 static void __flash_hal__statusRead1( __flash_status1_t * pcReadRegister    // буфер для чтения статус-регистра
                              ) // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_statusread_t*) cmdbuffer )->opcode  = _ACMD_STATREAD_1;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_statusread_t) );

      // читаем регистр
      __FLASH_HAL_RD( pcReadRegister,  sizeof(__flash_status1_t) );

      chip_sel_inactive();
 }
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__statusRead2 - чтение регистра статуса 2
 //
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция
 // * НЕ задерживает выполнение

 static void __flash_hal__statusRead2( __flash_status2_t * pcReadRegister    // буфер для чтения статус-регистра
                              ) // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_statusread_t*) cmdbuffer )->opcode  = _ACMD_STATREAD_2;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_statusread_t) );

      // читаем регистр
      __FLASH_HAL_RD( pcReadRegister,  sizeof(__flash_status2_t) );

      chip_sel_inactive();
 }
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__statusRead3 - чтение регистра статуса 3
 //
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция
 // * НЕ задерживает выполнение

 static void __flash_hal__statusRead3( __flash_status3_t * pcReadRegister    // буфер для чтения статус-регистра
                              ) // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_statusread_t*) cmdbuffer )->opcode  = _ACMD_STATREAD_3;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_statusread_t) );

      // читаем регистр
      __FLASH_HAL_RD( pcReadRegister,  sizeof(__flash_status3_t) );

      chip_sel_inactive();
 }
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__statusWrite1 - запись регистра статуса 1
 //
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция
 // * задерживает выполнение на ДЕСЯТКИ МИЛЛИСЕКУНД

 static void __flash_hal__statusWrite1( __flash_status1_t * pcWriteRegister    // буфер для записи статус-регистра
                               ) // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_statuswrite_t*) cmdbuffer )->opcode  = _ACMD_STATWRIT_1;
      ( (__flash_packet_statuswrite_t*) cmdbuffer )->status  = pcWriteRegister->status;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_statuswrite_t) );

      chip_sel_inactive();

      #if W25Q16JV_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_STPRG_ms );
      #endif
 }
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__statusWrite2 - запись регистра статуса 2
 //
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция
 // * задерживает выполнение на ДЕСЯТКИ МИЛЛИСЕКУНД

 static void __flash_hal__statusWrite2( __flash_status2_t * pcWriteRegister    // буфер для записи статус-регистра
                               ) // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_statuswrite_t*) cmdbuffer )->opcode  = _ACMD_STATWRIT_2;
      ( (__flash_packet_statuswrite_t*) cmdbuffer )->status  = pcWriteRegister->status;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_statuswrite_t) );

      chip_sel_inactive();

      #if W25Q16JV_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_STPRG_ms );
      #endif
 }
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__statusWrite3 - запись регистра статуса 3
 //
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция
 // * задерживает выполнение на ДЕСЯТКИ МИЛЛИСЕКУНД
 static void __flash_hal__statusWrite3( __flash_status3_t * pcWriteRegister    // буфер для записи статус-регистра
                               ) // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_statuswrite_t*) cmdbuffer )->opcode  = _ACMD_STATWRIT_3;
      ( (__flash_packet_statuswrite_t*) cmdbuffer )->status  = pcWriteRegister->status;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_statuswrite_t) );

      chip_sel_inactive();

      #if W25Q16JV_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_STPRG_ms );
      #endif
 }
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__arrayRead - последовательное чтение (массив)
 //
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция
 static void __flash_hal__arrayRead ( __FLASH_DWORD w24Address,   // адрес начала чтения
                                __FLASH_BYTE * pDataRead,   // указатель на буфер-приемник данных
                                __FLASH_WORD wCntToRead     // количество данных на чтение
                              ) // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_arrayread_t*) cmdbuffer )->opcode  = _LCMD_ARRYREAD;

      __FLASH_LL_FILLADDRESS( ( (__flash_packet_arrayread_t*) cmdbuffer ), w24Address);

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_arrayread_t) );

      // читаем данные
      __FLASH_HAL_RD( pDataRead, wCntToRead );

      chip_sel_inactive();
 }

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__chunkProgram - программирование страницы без стирания блока/страницы
 //
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция
 // * задерживает выполнение на МИЛЛИСЕКУНДЫ

 static void __flash_hal__chunkProgram( __flash_pageprogramptr_t pdata,
                                        __FLASH_DWORD w24Address )   // адрес начала записи
 {
      chip_sel_active();

      ( (__flash_packet_page_program_t*) cmdbuffer )->opcode  = _WCMD_PGWRIT;

      w24Address &= 0xFFFF00;  // сбрасывается адрес внутри страницы 256 байт

      __FLASH_LL_FILLADDRESS( ( (__flash_packet_page_program_t*) cmdbuffer ), w24Address);

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_page_program_t) );

      // отправляем данные
      __FLASH_HAL_WRCMD( pdata, sizeof(*pdata) );

      chip_sel_inactive();

      #if W25Q16JV_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGPRG_ms );
      #endif
 }

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__pageErase - стирание страницы
 //
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция
 // * задерживает выполнение на СОТНИ МИЛЛИСЕКУНД

 static void __flash_hal__pageErase( __FLASH_WORD wPageNum       // номер стираемой страницы
                             ) // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_pageerase_t*) cmdbuffer )->opcode  = _ECMD_PAGERASE;

      __FLASH_LL_FILLADDRESS( ( (__flash_packet_pageerase_t*) cmdbuffer ), ( wPageNum * (__PAGE_SIZE) ) );

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_pageerase_t) );

      chip_sel_inactive();

      #if W25QXXX_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGERS_ms );
      #endif
 }
 // --------------------------------------------------------------------------------------------------------
 /*
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__blockErase - стирание блока (=страницы)
 //
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция
 // * задерживает выполнение на СОТНИ МИЛЛИСЕКУНД

 static void __flash_hal__blockErase( __FLASH_WORD wBlockNum       // номер стираемого блока
                              ) // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_blockerase_t*) cmdbuffer )->opcode  = _ECMD_PAGERASE;

      __FLASH_LL_FILLADDRESS( ( (__flash_packet_blockerase_t*) cmdbuffer ), ( wBlockNum * (__PAGE_SIZE) ) );

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_pageerase_t) );

      chip_sel_inactive();

      #if W25QXXX_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGERS_ms );
      #endif
 }
 // --------------------------------------------------------------------------------------------------------
 */
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__sectorErase - стирание секторов
 //
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция
 // * задерживает выполнение на СЕКУНДЫ

 static void __flash_hal__sectorErase( __FLASH_WORD wSector       // номер сектора 0-31
                               ) // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_sectorerase_t*) cmdbuffer )->opcode    = _ECMD_SCTERASE;

      __FLASH_LL_FILLADDRESS( ( (__flash_packet_sectorerase_t*) cmdbuffer ), ( wSector * (__SECTOR_STD_SIZE) ) );

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_sectorerase_t) );

      chip_sel_inactive();

      #if W25QXXX_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_SCERS_ms );
      #endif
 }
 // --------------------------------------------------------------------------------------------------------

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__sleepMode - переход в режим пониженного энергопотребления
 //
 // * потоко-небезопасная функция
 // # переводит устройство в режим, когда все команды, кроме __flash_hal__wakeUp игнорируются

 static void __flash_hal__sleepMode() // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_powerdown_t*) cmdbuffer )->opcode  = _ACMD_EPWRDOWN;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_powerdown_t) );

      chip_sel_inactive();

      __FLASH_WAITus( _TIME_EDPDM_us );
 }
 // --------------------------------------------------------------------------------------------------------

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__wakeUp - выход из режима пониженного энергопотребления
 //
 // * потоко-небезопасная функция
 // # выводит устройство из режима, когда все команды, кроме __flash_hal__wakeUp игнорируются

 static void __flash_hal__wakeUp() // [W25Q OK]
 {
      chip_sel_active();

      // если флеш находилась во сне, необходимо ждать _TIME_RDPDM_us
      __FLASH_WAITus(_TIME_RDPDM_us);

      ( (__flash_packet_powerup_t*) cmdbuffer )->opcode  = _ACMD_LPWRDOWN;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_powerup_t) );

      chip_sel_inactive();

      __FLASH_WAITus( _TIME_RDPDM_us );
 }
 // --------------------------------------------------------------------------------------------------------

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__manufacturerIdRead - чтение идентификатора производителя
 //
 // * потоко-небезопасная функция
 // * может использоваться для опеределения типа чипа для выяснения его вместимости
 // * функция принимает параметр @pManufacturerID типа __flash_id_t. Этот параметр будет содержать
 //   информацию о чипе, если параметр не NULL. Кроме того, для того, чтобы прочитать расширенную
 //   информацию производителя (extended device information), поля pManufacturerID->pExtBf и
 //   pManufacturerID->ExtLen должны быть проининциализированны указателем на принимающий буфер
 //   и длинной этого буфера соответственно. При наличии расширенной информации поле ExtLen будет
 //   перезаписано, и будет содержать длинну расширенной информации, сохраненной по указателю pExtBf.
 //   Память по указателю pExtBf должна быть выделена заранее перед вызовом функции. Нуль-символ не
 //   добавляется. Память, начиная с ExtLen байта, не перезаписывается (остается мусор).
 // * параметр @pManufacturerID может быть равен NULL. Для того, чтобы НЕ читать расширенную информацию,
 //   передайте NULL вместо pManufacturerID, или, если передаете pManufacturerID, обнулите в нем поля pExtBf и ExtLen.
 // * функция способна считывать расширенную информацию в формате Serial Flash Discoverable Parameter Structure
 //   (JESD216D.01). Таким образом, кроме стандартного ID производителя длинной 1 байт, производитель записывает
 //   сообщение длинной дл 256 байт [JESD216D.01]. Наличие расширенной информации в выдаваемой структуре
 //   опционально: производитель может не помещать SFDP структуру, либо ее чтение может быть не реализовано.
 //   Для данного формата поле HighId в __flash_id_t не требуется и зарезервировано (всегда равно 0).
 // * вовзращаемое значение - DWORD, "откушенный" по младшему адресу от структуры __flash_id_t - содержит
 //   DevId, LowId, HighId.
 static  __FLASH_DWORD __flash_hal__manufacturerIdRead( __flash_id_t * pManufacturerID // необязательный параметр-буфер для приема расширенной информации
                                               )  // [W25Q OK]
 {
      // локальные переменные функции объединены с возвращаемым значением, чтобы вернуть
      // часть информации в виде DWORD
      union
      {
          struct
          {
              __FLASH_BYTE    LowId;         // стандартный байт ID (не расширенный)
              __FLASH_BYTE   HighId;         // расширенный байт ID (количество 0x7F Continuation Code) (JEDEC JEP-106)

              union
              {
                __FLASH_WORD    DevId;       // код устройства
                struct
                {
                  __FLASH_BYTE LDevId;       // код устройства (Low)
                  __FLASH_BYTE HDevId;       // код устройства (High)
                };
              };
          };

          __FLASH_DWORD ReturnValue;             // возвращаемое значение
      };

      DevId = 0;
      LDevId = 0;
      HDevId = 0;
      LowId = 0;
      HighId = 0;
      ReturnValue = 0;
      //-----------

      chip_sel_active();

      //-----------
      // чтение JEDEC ID начато

      ( (__flash_packet_manufactureridread_t*) cmdbuffer )->opcode  = _ACMD_MFIDREAD;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_manufactureridread_t) );

      // чтение Manufacturer ID
      __FLASH_HAL_RD( &LowId, sizeof( LowId ) );

      // чтение Device ID
      __FLASH_HAL_RD( &LDevId, sizeof( LDevId ) );
      __FLASH_HAL_RD( &HDevId, sizeof( HDevId ) );

      // чтение JEDEC ID закончено
      //-----------

      chip_sel_inactive();

      //-----------
      // если требуется чтение расширенной информации
      // @ExtLen > 0 указывает на выделенный буфер под SFDP регистр
      if( NULL != pManufacturerID && pManufacturerID->ExtLen > 0 )
      {
          chip_sel_active();

          //-----------
          // чтение SFDP начато

          ( (__flash_packet_sfdp_read_t*) cmdbuffer )->opcode  = _ACMD_SFDPREAD;
          ( (__flash_packet_sfdp_read_t*) cmdbuffer )->zero1   = 0;  // A23..A16 must be 0
          ( (__flash_packet_sfdp_read_t*) cmdbuffer )->zero1   = 0;  // A15..A08 must be 0
          ( (__flash_packet_sfdp_read_t*) cmdbuffer )->offset  = 0;  // чтение по нулевому смещению
          ( (__flash_packet_sfdp_read_t*) cmdbuffer )->dummy   = 0;  // фиктивный байт

          // отправляем команду
          __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_sfdp_read_t) );

          // чтение SFDP в буфер
          for( size_t offset = 0; offset < pManufacturerID->ExtLen; offset++ )
          {
            __FLASH_HAL_RD( pManufacturerID->pExtBf + offset, sizeof( uint8_t ) );
          }

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

          chip_sel_inactive();
      }
      //-----------

      return ReturnValue;
 }
 // --------------------------------------------------------------------------------------------------------

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__globalUnlock - снять блокировку записи со всех секторов/блоков / выключить программную защиту от записи/стирания
 //
 // * потоко-небезопасная функция
 static void __flash_hal__globalUnlock() // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_globallockcontrol_t*) cmdbuffer )->opcode  = _SCMD_GLBLUNLK;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_globallockcontrol_t) );

      chip_sel_inactive();

      #if W25QXXX_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGPRG_ms );
      #endif
 }
 // --------------------------------------------------------------------------------------------------------

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__globalLock - установить блокировку записи на все сектора/блоки / включить программную защиту от записи/стирания
 //
 // * потоко-небезопасная функция
 static void __flash_hal__globalLock() // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_globallockcontrol_t*) cmdbuffer )->opcode  = _SCMD_GLBLLK;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_globallockcontrol_t) );

      chip_sel_inactive();

      #if W25QXXX_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGPRG_ms );
      #endif
 }
 // --------------------------------------------------------------------------------------------------------

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__protectStateWrite - записывает индивидуальную блокировку сектора/блока
 //
 // * потоко-небезопасная функция
 // * не проверяет правильность переданных параметров
 // * задерживает выполнение на МИЛЛИСЕКУНДЫ
 static void __flash_hal__protectStateWrite( size_t nProtSector, bool protectState ) // [W25Q OK]
 {
      chip_sel_active();

      if( protectState )
      ( (__flash_packet_protectregister_rw_t*) cmdbuffer )->opcode  = _SCMD_BLKLOCK;
      else
      ( (__flash_packet_protectregister_rw_t*) cmdbuffer )->opcode  = _SCMD_BLKULOCK;

      __FLASH_LL_FILLADDRESS( ( (__flash_packet_protectregister_rw_t*) cmdbuffer ), __PROT_SECTOR_TO_ADDRESS(nProtSector) );

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_protectregister_rw_t) );

      chip_sel_inactive();

      #if W25QXXX_BKGOPERATIONS == 0  //!
      __FLASH_SMART_WAITms( _TIME_PGPRG_ms );
      #endif
 }
 // --------------------------------------------------------------------------------------------------------

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__protectStateRead - считывает индивидуальную блокировку сектора/блока
 //
 // * потоко-небезопасная функция
 // * не проверяет правильность переданных параметров
 static bool __flash_hal__protectStateRead( size_t nProtSector ) // [W25Q OK]
 {
      chip_sel_active();

      uint8_t state = 0;

      ( (__flash_packet_protectregister_rw_t*) cmdbuffer )->opcode  = _SCMD_BLKLKRD;

      __FLASH_LL_FILLADDRESS( ( (__flash_packet_protectregister_rw_t*) cmdbuffer ), __PROT_SECTOR_TO_ADDRESS(nProtSector) );

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_protectregister_rw_t) );

      // считывается бит защиты
      __FLASH_HAL_RD( &state, sizeof( uint8_t ) );

      chip_sel_inactive();

      // LSB бит показывает состояние защиты, 0-нет защиты, 1-защита включена
      return ((bool)(state & 1));
 }
 // --------------------------------------------------------------------------------------------------------

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__securityRegister1Write - записывает регистр безопасности
 //
 // * потоко-небезопасная функция
 // * параметр @contents не может быть равен NULL
 // * ИЗМЕНЯЕТ СОДЕРЖИМОЕ ВСТРОЕННОГО РЕГИСТРА БЕЗОПАСНОСТИ 1
static  void __flash_hal__securityRegister1Write( __flash_securityregister_t * contents  // содержимое регистра безопасности
                                          ) // [W25Q OK]
 {
      if( !contents ) return;

      chip_sel_active();

      ( (__flash_packet_securityregisterwrite_t*) cmdbuffer )->opcode     = _SCMD_SECRGWRT;
      ( (__flash_packet_securityregisterwrite_t*) cmdbuffer )->zero = 0;
      ( (__flash_packet_securityregisterwrite_t*) cmdbuffer )->regSelector = 1;
      ( (__flash_packet_securityregisterwrite_t*) cmdbuffer )->zero_2 = 0;
      ( (__flash_packet_securityregisterwrite_t*) cmdbuffer )->byteSelector = 0;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_securityregisterwrite_t) );
       // записываем регистр безопасности
      __FLASH_HAL_WR( contents, sizeof(__flash_securityregister_t) );

      #if W25QXXX_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGPRG_ms );
      #endif

      chip_sel_inactive();
 }
 // --------------------------------------------------------------------------------------------------------

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__securityRegister2Write - записывает регистр безопасности
 //
 // * потоко-небезопасная функция
 // * параметр @contents не может быть равен NULL
 // * ИЗМЕНЯЕТ СОДЕРЖИМОЕ ВСТРОЕННОГО РЕГИСТРА БЕЗОПАСНОСТИ 2
 static void __flash_hal__securityRegister2Write( __flash_securityregister_t * contents  // содержимое регистра безопасности
                                          ) // [W25Q OK]
 {
      if( !contents ) return;

      chip_sel_active();

      ( (__flash_packet_securityregisterwrite_t*) cmdbuffer )->opcode     = _SCMD_SECRGWRT;
      ( (__flash_packet_securityregisterwrite_t*) cmdbuffer )->zero = 0;
      ( (__flash_packet_securityregisterwrite_t*) cmdbuffer )->regSelector = 2;
      ( (__flash_packet_securityregisterwrite_t*) cmdbuffer )->zero_2 = 0;
      ( (__flash_packet_securityregisterwrite_t*) cmdbuffer )->byteSelector = 0;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_securityregisterwrite_t) );
       // записываем регистр безопасности
      __FLASH_HAL_WR( contents, sizeof(__flash_securityregister_t) );

      #if W25QXXX_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGPRG_ms );
      #endif

      chip_sel_inactive();
 }
 // --------------------------------------------------------------------------------------------------------

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__securityRegister3Write - записывает регистр безопасности
 //
 // * потоко-небезопасная функция
 // * параметр @contents не может быть равен NULL
 // * ИЗМЕНЯЕТ СОДЕРЖИМОЕ ВСТРОЕННОГО РЕГИСТРА БЕЗОПАСНОСТИ 3
 static void __flash_hal__securityRegister3Write( __flash_securityregister_t * contents  // содержимое регистра безопасности
                                          ) // [W25Q OK]
 {
      if( !contents ) return;

      chip_sel_active();

      ( (__flash_packet_securityregisterwrite_t*) cmdbuffer )->opcode     = _SCMD_SECRGWRT;
      ( (__flash_packet_securityregisterwrite_t*) cmdbuffer )->zero = 0;
      ( (__flash_packet_securityregisterwrite_t*) cmdbuffer )->regSelector = 3;
      ( (__flash_packet_securityregisterwrite_t*) cmdbuffer )->zero_2 = 0;
      ( (__flash_packet_securityregisterwrite_t*) cmdbuffer )->byteSelector = 0;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_securityregisterwrite_t) );
       // записываем регистр безопасности
      __FLASH_HAL_WR( contents, sizeof(__flash_securityregister_t) );

      #if W25QXXX_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGPRG_ms );
      #endif

      chip_sel_inactive();
 }
 // --------------------------------------------------------------------------------------------------------


 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__securityRegister1Erase - стирает регистр безопасности
 //
 // * потоко-небезопасная функция
 // * параметр @contents не может быть равен NULL
 // * СТИРАЕТ СОДЕРЖИМОЕ ВСТРОЕННОГО РЕГИСТРА БЕЗОПАСНОСТИ 1
 static void __flash_hal__securityRegister1Erase() // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_securityregistererase_t*) cmdbuffer )->opcode     = _SCMD_SECRGERS;
      ( (__flash_packet_securityregistererase_t*) cmdbuffer )->zero = 0;
      ( (__flash_packet_securityregistererase_t*) cmdbuffer )->regSelector = 1;
      ( (__flash_packet_securityregistererase_t*) cmdbuffer )->zero_2 = 0;
      ( (__flash_packet_securityregistererase_t*) cmdbuffer )->zero_3 = 0;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_securityregisterwrite_t) );

      #if W25QXXX_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGERS_ms );
      #endif

      chip_sel_inactive();
 }
 // --------------------------------------------------------------------------------------------------------

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__securityRegister2Erase - стирает регистр безопасности
 //
 // * потоко-небезопасная функция
 // * параметр @contents не может быть равен NULL
 // * СТИРАЕТ СОДЕРЖИМОЕ ВСТРОЕННОГО РЕГИСТРА БЕЗОПАСНОСТИ 2
 static void __flash_hal__securityRegister2Erase() // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_securityregistererase_t*) cmdbuffer )->opcode     = _SCMD_SECRGERS;
      ( (__flash_packet_securityregistererase_t*) cmdbuffer )->zero = 0;
      ( (__flash_packet_securityregistererase_t*) cmdbuffer )->regSelector = 2;
      ( (__flash_packet_securityregistererase_t*) cmdbuffer )->zero_2 = 0;
      ( (__flash_packet_securityregistererase_t*) cmdbuffer )->zero_3 = 0;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_securityregisterwrite_t) );

      #if W25QXXX_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGERS_ms );
      #endif

      chip_sel_inactive();
 }
 // --------------------------------------------------------------------------------------------------------

  // --------------------------------------------------------------------------------------------------------
 // __flash_hal__securityRegister3Erase - стирает регистр безопасности
 //
 // * потоко-небезопасная функция
 // * параметр @contents не может быть равен NULL
 // * СТИРАЕТ СОДЕРЖИМОЕ ВСТРОЕННОГО РЕГИСТРА БЕЗОПАСНОСТИ 3
 static void __flash_hal__securityRegister3Erase() // [W25Q OK]
 {
      chip_sel_active();

      ( (__flash_packet_securityregistererase_t*) cmdbuffer )->opcode     = _SCMD_SECRGERS;
      ( (__flash_packet_securityregistererase_t*) cmdbuffer )->zero = 0;
      ( (__flash_packet_securityregistererase_t*) cmdbuffer )->regSelector = 3;
      ( (__flash_packet_securityregistererase_t*) cmdbuffer )->zero_2 = 0;
      ( (__flash_packet_securityregistererase_t*) cmdbuffer )->zero_3 = 0;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_securityregisterwrite_t) );

      #if W25QXXX_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGERS_ms );
      #endif

      chip_sel_inactive();
 }
 // --------------------------------------------------------------------------------------------------------


 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__securityRegister1Read - прочитывает регистр безопасности #1
 //
 // * потоко-небезопасная функция
 // * параметр @contents не может быть NULL - укажите буфер-приемние для сохранения содержимого регистра блокировки (см. __flash_securityregister_t)
 // * считывает как пользовательскую часть, так и запрограммированную производителем
 static void __flash_hal__securityRegister1Read( __flash_securityregister_t * contents  // буфер-приемник содержимого регистра блокировки
                                         ) // [W25Q OK]
 {
      if( !contents ) return;

      chip_sel_active();

      ( (__flash_packet_securityregisterread_t*) cmdbuffer )->opcode     = _SCMD_SECRGRD;
      ( (__flash_packet_securityregisterread_t*) cmdbuffer )->zero = 0;
      ( (__flash_packet_securityregisterread_t*) cmdbuffer )->regSelector = 1;
      ( (__flash_packet_securityregisterread_t*) cmdbuffer )->zero_2 = 0;
      ( (__flash_packet_securityregisterread_t*) cmdbuffer )->byteSelector = 0;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_securityregisterread_t) );
       // читаем регистр безопасности
      __FLASH_HAL_RD( contents, sizeof(__flash_securityregister_t) );

      chip_sel_inactive();
 }
 // --------------------------------------------------------------------------------------------------------

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__securityRegister2Read - прочитывает регистр безопасности #1
 //
 // * потоко-небезопасная функция
 // * параметр @contents не может быть NULL - укажите буфер-приемние для сохранения содержимого регистра блокировки (см. __flash_securityregister_t)
 // * считывает как пользовательскую часть, так и запрограммированную производителем
 static void __flash_hal__securityRegister2Read( __flash_securityregister_t * contents  // буфер-приемник содержимого регистра блокировки
                                         ) // [W25Q OK]
 {
      if( !contents ) return;

      chip_sel_active();

      ( (__flash_packet_securityregisterread_t*) cmdbuffer )->opcode     = _SCMD_SECRGRD;
      ( (__flash_packet_securityregisterread_t*) cmdbuffer )->zero = 0;
      ( (__flash_packet_securityregisterread_t*) cmdbuffer )->regSelector = 2;
      ( (__flash_packet_securityregisterread_t*) cmdbuffer )->zero_2 = 0;
      ( (__flash_packet_securityregisterread_t*) cmdbuffer )->byteSelector = 0;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_securityregisterread_t) );
       // читаем регистр безопасности
      __FLASH_HAL_RD( contents, sizeof(__flash_securityregister_t) );

      chip_sel_inactive();
 }
 // --------------------------------------------------------------------------------------------------------

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__securityRegister3Read - прочитывает регистр безопасности #3
 //
 // * потоко-небезопасная функция
 // * параметр @contents не может быть NULL - укажите буфер-приемние для сохранения содержимого регистра блокировки (см. __flash_securityregister_t)
 // * считывает как пользовательскую часть, так и запрограммированную производителем
 static void __flash_hal__securityRegister3Read( __flash_securityregister_t * contents  // буфер-приемник содержимого регистра блокировки
                                         ) // [W25Q OK]
 {
      if( !contents ) return;

      chip_sel_active();

      ( (__flash_packet_securityregisterread_t*) cmdbuffer )->opcode     = _SCMD_SECRGRD;
      ( (__flash_packet_securityregisterread_t*) cmdbuffer )->zero = 0;
      ( (__flash_packet_securityregisterread_t*) cmdbuffer )->regSelector = 3;
      ( (__flash_packet_securityregisterread_t*) cmdbuffer )->zero_2 = 0;
      ( (__flash_packet_securityregisterread_t*) cmdbuffer )->byteSelector = 0;

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_securityregisterread_t) );
       // читаем регистр безопасности
      __FLASH_HAL_RD( contents, sizeof(__flash_securityregister_t) );

      chip_sel_inactive();
 }
 // --------------------------------------------------------------------------------------------------------

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__uniqueIdRegisterRead - прочитывает регистр уникального идентификатора (UniqueID)
 //
 // * потоко-небезопасная функция
 // * параметр @contents не может быть NULL - укажите буфер-приемнике для сохранения содержимого регистра UniqueID (см. __flash_uniqueidregister_t)
 static void __flash_hal__uniqueIdRegisterRead( __flash_uniqueidregister_t * contents  // буфер-приемник содержимого регистра UniqueID
                                        ) // [W25Q OK]
 {
      if( !contents ) return;

      chip_sel_active();

      ( (__flash_packet_uid_read_t*) cmdbuffer )->opcode  = _ACMD_UIDRREAD;
      ( (__flash_packet_uid_read_t*) cmdbuffer )->zero1   = 0; // передается фиктивный байт
      ( (__flash_packet_uid_read_t*) cmdbuffer )->zero2   = 0; // передается фиктивный байт
      ( (__flash_packet_uid_read_t*) cmdbuffer )->zero3   = 0; // передается фиктивный байт
      ( (__flash_packet_uid_read_t*) cmdbuffer )->zero4   = 0; // передается фиктивный байт

      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_uid_read_t) );

       // читаем регистр UniqueID
      __FLASH_HAL_RD( contents, sizeof(__flash_uniqueidregister_t) );

      chip_sel_inactive();
 }
 // --------------------------------------------------------------------------------------------------------

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__uniqueIdRegisterValidate - прочитывает регистр UniqueID и выполняет
 // проверку осмысленности прочитанного заводского идентификатора.
 // Функция расценивает индентификатор состоящий из одних 0xFF или 0x00 как невозможный.
 // В этом случае имеет место ошибка, функция возвращает false.
 static bool __flash_hal__uniqueIdRegisterValidate( __flash_uniqueidregister_t * contents  // буфер-приемник содержимого регистра UniqueID
                                            )  // [W25Q OK]
 {
      if( NULL != contents )
      {
          __flash_hal__uniqueIdRegisterRead( contents );

          __FLASH_DWORD n00 = 0;
          __FLASH_DWORD nFF = 0;
          for( __FLASH_DWORD i = 0; i < sizeof(contents->uid); ++i )
          {
              if( 0xFF == contents->uid[i] ) ++nFF;
              if( 0x00 == contents->uid[i] ) ++n00;
          }

          if( (n00 != sizeof(contents->uid))
              &&
              (nFF != sizeof(contents->uid)) )
          {
              return true;
          }
      }

      return false;
 }
 // --------------------------------------------------------------------------------------------------------

 #if W25QXXX_POWER_MANAGEMENT > 0
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__powerOn - реализует управление питанием микросхемы памяти (подает питание)
 //
 static void __flash_hal__powerOn() // [W25Q OK]
 {
     // снять сигнал выбора чипа (чип не выбран, режим idle)
     chip_sel_inactive();

     // подать питание
     __imp_flash_poweron();
 }

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__powerOff - реализует управление питанием микросхемы памяти (снимает питание)
 //
 static void __flash_hal__powerOff() // [W25Q OK]
 {
     // снять питание
     __imp_flash_poweroff();
 }

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__powerPulse - реализует управление питанием микросхемы - формирует импульс перезагрузки
 static void __flash_hal__powerPulse( __FLASH_WORD nCooldownTime_ms, __FLASH_WORD nStartupTime_ms ) // [W25Q OK]
 {
     // снять питание
     __imp_flash_poweroff();

     // снять сигнал выбора чипа (чип не выбран, режим idle)
     chip_sel_inactive();

     if( nCooldownTime_ms > 0 )
     {
         // немного подождем, пока остынет :)
         __FLASH_WAITms( _TIME_COOLDOWN_ms );
     }
     else
     {
         // ждем совсем немного
         __FLASH_WAITus( _TIME_STRUP_us );
     }

     // подать питание
     __imp_flash_poweron();

     // если разрешено управление режимом сброса
     #if W25QXXX_RESET_MANAGEMENT
     // сформировать импульс сброса микросхемы
     __flash_hal__resetPulse();
     #endif

     if( nStartupTime_ms > 0 )
     {
         // ожидать готовности
         __FLASH_WAITms( _TIME_START_ms );
     }
     else
     {
         // ждем совсем немного
         __FLASH_WAITus( _TIME_STRUP_us );
     }
 }
 #endif

 #if W25QXXX_RESET_MANAGEMENT > 0
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__resetAssert - реализует управление сигналом сброса микросхемы памяти (устанавливает сигнал сброса)
 //
 static void __flash_hal__resetAssert() // [W25Q OK]
 {
     // снять сигнал выбора чипа (чип не выбран, режим idle)
     chip_sel_inactive();

     // установить сигнал сброса
     __imp_flash_reset_assert();
 }

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__resetRelease - реализует управление сигналом сброса микросхемы памяти (снимает сигнал сброса)
 //
 static void __flash_hal__resetRelease() // [W25Q OK]
 {
     // снять сигнал сброса
     __imp_flash_reset_release();
 }

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__resetPulse - реализует управление сигналом сброса микросхемы памяти (подает законченный импульс сброса)
 //
 static void __flash_hal__resetPulse() // [W25Q OK]
 {
    // формируем импульс сброса

    // сначала снимаем сигнал сброса
    __imp_flash_reset_release();

    // после снятия сигнала сброса необходимо ожидать время восстановления
    __FLASH_WAITus(_TIME_RSTRC_us);

    // потом ставим сигнал сброса
    __flash_hal__resetAssert();

    // ожидание: требуется время для реакции на сигнал сброса
    __FLASH_WAITus(_TIME_RSTWD_us);

    // потом снова снимаем
    __flash_hal__resetRelease();

    // после снятия сигнала сброса необходимо ожидать время восстановления
    __FLASH_WAITus(_TIME_RSTRC_us);
 }
 #endif