 // Файл с низкоуровневыми коммандами для AT45DB321D.
 // v 1.4 от 28/07/15
 // Автор: Сычев А.

 // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
 // №№№№№№№№№№№№№№№№№№№№№№ НИЗКОУРОВНЕВЫЕ НЕБЕЗОПАСНЫЕ ФУНКЦИИ №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
 // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ 
 #define AT45DB321D_LOWLEVEL 
 #include "drivers\flash\common\AT45DBXXX_TYP.h" 
 #include "drivers\flash\common\AT45DBXXX_HAL.h"          // аппаратная абстрация от SSP  
 #include "drivers\flash\lowlevel\AT45DB321D_LL.h"        // определения для AT45DB321D   
 #include "drivers\flash\lowlevel\AT45DB321D_LL_func.h"   // прототипы LL-функций
  
 // ########################################################################################################
 // Глобальные переменные

 #pragma pack( push, 1 )
 #ifdef FLASH_RAM_PLACE
 _PLACEIN( 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 ];         // НЕ ТРЕБУЕТСЯ! буфер под чтение/запись страниц

             union
             {
                __FLASH_BYTE protectbuffer[ __SECTORS_TYPICAL ];     // буфер под чтение/запись байтов защиты секторов
                __flash_protectionregister_t protectionregister;
             };

             __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()
 {
        __FLASH_HAL_CS_LO();
        __FLASH_WAITus(_TIME_CSCLR_us);
 }

 // установка сигнала ChipSelect в неактивное состояние
 static inline void chip_sel_inactive()
 {
        __FLASH_WAITus(_TIME_CSSET_us);
        __FLASH_HAL_CS_HI();
        __FLASH_WAITus(_TIME_CSHLD_us);  // минимальное время между командами
 }
 
#ifndef __imp_ssp_io_rev 
// Функция обращения порядка байтов и передачи по SPI
//
// @count не может быть 0. Функция не проверяет параметр!
static void __int_ssp_io_rev( __FLASH_BYTE * pBuffer, __FLASH_WORD count )
{
       // обмениваем половину из count байт, т.к. вторая будет обменяна автоматически 
       // вычитаем count-- чтобы прерватить count в индекс конеца буфера
       __FLASH_DWORD c = (count--) >> 1;
       
       // начианем обмен 
       for( int i = 0; i < c; ++i )
       {
            // Обмениваем pBuffer[i] с pBuffer[count-i]
            
            pBuffer[i] ^=  pBuffer[count-i];
            
            pBuffer[count-i] ^=  pBuffer[i];
            
            pBuffer[i] ^=  pBuffer[count-i];
       }
              
       // используем штатую функцию передачи байт по SPI
       __FLASH_HAL_WR( pBuffer, ++count );
}
#endif

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

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

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

     // --   --   --   --   --   --   для коротких задержек   --   --   --   --   --   --   --   --   --   --        
     #ifdef FLASH_CHECKSTATUS_DENSITYCODE
     #if    FLASH_CHECKSTATUS_DENSITYCODE == 1
     if ( r_status.cDensity == __FLASH_STATUS_DENSITY_SIGN )                     // Дополнительная проверка на код Density
     #endif
     #endif
     if ( r_status.bReady )                                                      // сразу проверяем, если устройство свободно, возвращаем управление
     {
        __flash_hal__part_statusread_stage3( 0 );                                // завершаем чтение регистра
        return 1;
     }
     
     if ( wTimems < __FLASH_SMART_WAIT_THRESHOLD )                               // для малых времен ожидания накладно опрашивать чип постоянно
     {
        __FLASH_WAITms(wTimems);                                                 // выжидаем указанное время

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

        #ifdef FLASH_CHECKSTATUS_DENSITYCODE
        #if    FLASH_CHECKSTATUS_DENSITYCODE == 1
        if ( r_status.cDensity == __FLASH_STATUS_DENSITY_SIGN )                  // Дополнительная проверка на код Density
        #endif
        #endif
        if ( r_status.bReady )                                                   // если свободно - вернем 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 );                       // читаем статус

         #ifdef FLASH_CHECKSTATUS_DENSITYCODE
         #if    FLASH_CHECKSTATUS_DENSITYCODE == 1
         if ( r_status.cDensity == __FLASH_STATUS_DENSITY_SIGN )                 // Дополнительная проверка на код Density
         #endif
         #endif
         if ( r_status.bReady )                                                  // проверяем, не освободился ли чип?
         {
             __flash_hal__part_statusread_stage3( 0 );                           // да, освободился, завершаем чтение

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

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

     __flash_hal__part_statusread_stage3( &r_status );                           // время вышло, завершаем чтение
     
     if ( r_status.bReady )                                                      // последний раз проверяем статус
     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.bReady )
 //  __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_status_t * pcReadRegister )  
 {
      chip_sel_active();

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

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

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

      chip_sel_inactive();         
 }
 // --------------------------------------------------------------------------------------------------------  
 // ########################################################################################################
 // __flash_hal__getpagesize - получение размера страницы 512/528 байт
 //
 __flash_pagesize_t __flash_hal__getpagesize( )
 {
        __flash_status_t  status;

        __flash_hal__statusread( &status );

        if( status.bPageSize == __fps_512 )
        return __fps_512;

        return __fps_528;
 }
 // --------------------------------------------------------------------------------------------------------  
 // ########################################################################################################
 // __flash_hal__getreadyfast - проверка занятости без ожидания
 //
 // # Возвращаемое значение: FLERR_SUCCESS если свободен, иначе FLERR_TIMEOUT
 flash_err_t __flash_hal__getreadyfast( )
 {
        __flash_status_t  status;

        __flash_hal__statusread( &status );

        if( status.bReady )
        return FLERR_SUCCESS;

        return FLERR_TIMEOUT;
 }
 // --------------------------------------------------------------------------------------------------------  
 // ########################################################################################################
 // __flash_hal__getcompareresult - проверка результата сравнения (бит COMP / bMismatch)
 //
 // # Возвращаемое значение: FLERR_SUCCESS если совпадает, иначе FLERR_VERIFY_ERROR
 flash_err_t __flash_hal__getcompareresult( )
 {
        __flash_status_t  status;

        __flash_hal__statusread( &status );

        if( status.bMismatch )
        return FLERR_VERIFY_ERROR;

        return FLERR_SUCCESS;
 }
 // --------------------------------------------------------------------------------------------------------  
 // ########################################################################################################
 // __flash_hal__getprotectstate - получение состояния общего бита защиты
 //
 // # Возвращаемое значение - FLERR_PROTECT_ENABLED, если бит защиты установлен, иначе FLERR_PROTECT_DISABLED
 flash_err_t __flash_hal__getprotectstate( )
 {
        __flash_status_t  status;
                        
        __flash_hal__statusread( &status );

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

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

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

    __flash_securityregister_t securityRegister;

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

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

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

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

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

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

 void __flash_hal__use512page(); 
 // --------------------------------------------------------------------------------------------------------  
 // ########################################################################################################
 // __flash_hal__initialize - Низкоуровневая инициализация драйвера флешпамяти 
 //
 // * определение наличия подключенной микросхемы
 // * чтение идентификатора и определение производителя 
 // * контроль правильности:
 //   - идентификатора производителя 
 //   - типа устройства, семейства (DevId)
 //   - размера чипа
 // * установка и проверка размера страницы
 // 
 // # Возвращаемое значние - код выполнения 
 flash_err_t __flash_hal__initialize()
 {     
      // если разрешено управление питанием
      #if AT45DB321D_POWER_MANAGEMENT
      // подать питание на микросхему
      __flash_hal__power_on();

      __FLASH_WAITus( _TIME_STRUP_us );
      #endif

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

      __FLASH_WAITus( _TIME_RSTRC_us );
      #endif

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

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

      // пробуем "грязное" чтение идентификатора.
      // Результат чтения не сохраняется, требуется обнаружить
      // лишь присуствие на шине и определить готовность
      // Поэтому как остуствие на шине, так и занятость чипа расценивается
      // как ошибка.
      flash_err_t dirty_status = __flash_hal__detect();

      if( FLASH_ERROR(dirty_status) )
      {
          // если разрешено управление сигналом сброса
          #if AT45DB321D_RESET_MANAGEMENT

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

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

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

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

            #else

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

            #endif

          #endif 
      }

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

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

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

      // проверяем семейство чипа
      #ifdef FLERR_LL_INITFAIL_WRONGFAMILY
      if( ((__flash_rvid_t*) &flashID)->DevId.Family != FLASH_DESIRED_FAMILY )
      {
          return FLERR_LL_INITFAIL_WRONGFAMILY;
      }
      #endif
      
      // если разрешено управление аппаратным сигналом защиты записи
      #if AT45DB321D_HW_WR_PROTECT 
      // если аппаратная защита записи отключена в настройках
      #if AT45DB321D_HW_WR_PROTECT_KEEPUNPROTECTED == 1
      // требуется снять аппаратную защиту записи на всё время работы драйвера
      __imp_flash_hwwrprotect_release();
      #endif
      #endif

      // определить размер страницы: если выбран AT45DB321D_EMU512, а во флешке AT45DB321D_PRM512 - лочимся
      // установить размер страницы если выбран AT45DB321D_PRM512
      #ifdef AT45DB321D_PRM512

        // выбран режим страницы 512.
        // перепрограммируем чип на страницы по 512 байт.
        // операция не отменяемая, чип программируется один раз.

        // проверяем текущий размер страницы.
        if( __fps_512 != __flash_hal__getpagesize();
        {  
           // если 528 - перепрограммируем
           __flash_hal__use512page();

           #if AT45DB321D_BKGOPERATIONS
           __FLASH_SMART_WAITms(_TIME_PGPRG_ms);
           #endif
           
           // для изменения размера страницы по документации требуется
           // отключить питание чипа и подать снова.
           #if AT45DB321D_POWER_MANAGEMENT
               // если доступно управление питанием, используем его
               __flash_hal__power_pulse( _TIME_COOLDOWN_ms, _TIME_START_ms );
           #else
               // если управление питанием недоступно, используем callback-процедуру пользователя

               // функция вызывается для отключения питания чипа в случае если программа
               // обнаруживает, что установлен режим не 512 байт. Сначала функция __flash_hal__use512page 
               // переключает размер страницы, затем вызывает эту функцию. 
               __imp_flash_powercycle();

               // ожидание готовности запуска
               __FLASH_WAITms( _TIME_START_ms );
           #endif
        }
        
        // # для применения нового размера требуется ОТКЛЮЧЕНИЕ ПИТАНИЯ!

        // проверяем текущий размер страницы.
        if( __fps_512 != __flash_hal__getpagesize();
        {  
           // Ошибку выдаем: просили установить 512 байт, но не сработало.
           return FLERR_LL_INITFAIL_PAGESIZE;
        }

      #else

        #ifdef AT45DB321D_EMU512
        // выбран режим ЭМУЛЯЦИИ страниц по 512 байт
        // Если в чипе прошит размер 512 байт, то это уже не эмуляция.        
        if( __fps_512 == __flash_hal__getpagesize() )
        {  
           // Но ошибку не выдаем, т.к. все должно работать.
        }
        #else
        // выбран обычный режим без эмуляции размера страницы 512 байт.
        // предполагается использование странцы 528 байт.
        if( __fps_512 == __flash_hal__getpagesize() )
        {
            // в чипе прошит размер 512 - выдем ошибку, т.к. невозможно перенастроить чип
            return FLERR_LL_INITFAIL_PAGESIZE;
        }
        #endif

      #endif
      
      return FLERR_SUCCESS;
 } 
 // ########################################################################################################
 // ########################################################################################################
 // ########################################################################################################
 // ########################################################################################################
 // ########################################################################################################
 // ########################################################################################################
 // --------------------------------------------------------------------------------------------------------  
 // __flash_hal__statusread - чтение регистра статуса
 //  
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция
 // * НЕ задерживает выполнение

 void __flash_hal__statusread( __flash_status_t * pcReadRegister    // буфер для чтения статус-регистра
                             )  
 {
      chip_sel_active();

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

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

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

      chip_sel_inactive();     
 }
 // -------------------------------------------------------------------------------------------------------- 
 
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__page_read - чтение одной страницы
 // 
 // * не изменяет содержимое внутренних SRAM буферов
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция
 //
 void __flash_hal__page_read( __FLASH_WORD wPageNum,      // номер страницы для чтения
                              __FLASH_WORD wPageOfs,      // адрес начала чтения в странице
                              __FLASH_BYTE * pDataRead,   // указатель на буфер-приемник данных
                              __FLASH_WORD wCntToRead )   // количество данных на чтение
 {
      chip_sel_active();

      ( (__flash_packet_pageread_t*) cmdbuffer )->opcode  = _RCMD_PAGEREAD;
      ( (__flash_packet_pageread_t*) cmdbuffer )->page    = wPageNum;
      ( (__flash_packet_pageread_t*) cmdbuffer )->offset  = wPageOfs;

      ( (__flash_packet_pageread_t*) cmdbuffer )->__reserved  = 0;
      ( (__flash_packet_pageread_t*) cmdbuffer )->__reserved2 = 0;

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

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

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

 #ifndef AT45DB321D_EMU512
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__array_read - последовательное чтение (массив)
 // 
 // * не изменяет содержимое внутренних SRAM буферов
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция
 // * недоступна в режиме программной эмуляции 512-байтных страниц (AT45DB321D_EMU512)

 void __flash_hal__array_read ( __FLASH_WORD wPageNum,      // номер начальной страницы для чтения
                                __FLASH_WORD wPageOfs,      // адрес начала чтения в начальной странице
                                __FLASH_BYTE * pDataRead,   // указатель на буфер-приемник данных
                                __FLASH_WORD wCntToRead )   // количество данных на чтение
 {
      chip_sel_active();

      ( (__flash_packet_arrayread_t*) cmdbuffer )->opcode  = _RCMD_ARRYREAD;
      ( (__flash_packet_arrayread_t*) cmdbuffer )->page    = wPageNum;
      ( (__flash_packet_arrayread_t*) cmdbuffer )->offset  = wPageOfs;

      ( (__flash_packet_arrayread_t*) cmdbuffer )->__reserved  = 0;
      ( (__flash_packet_arrayread_t*) cmdbuffer )->__reserved2 = 0;
      
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_arrayread_t) );

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

      chip_sel_inactive();
 }
 // --------------------------------------------------------------------------------------------------------
 #else
 #warning В режиме эмуляции страницы 512 байт функция последовательного чтения недоступна.
 // Эмуляция размера страницы в 512 байт достигается неполной записью и неполным чтением страницы микросхемы.
 // При этом операции чтения/записи буферизируются драйвером памяти таким образом, что для программы высокого
 // уровня этот процесс остается прозрачным. Однако команда последовательного чтения использует иной алгоритм
 // чтения: автоматическое инкрементирование указателя чтения без буферизации. Таким образом при использовании
 // команды последовательного чтения в режиме эмуляции на каждую читаемую страницу будет приходиться дополнительные
 // 16 байт мусорной информации, сохраненной в чипе, но никогда не перезаписываемой драйвером (в режиме эмуляции).
 // Поэтому без специальной обработки (прореживания) команда не имеет смысла - вместо прореживания используется
 // повторная передача адреса уже следующей страницы на чтение. Таким образом осуществляется чтение больших
 // объемов данных в режиме эмуляции.
 //
 //   Реальное положение       page N   page N+1   page N+2 ......
 //   дел. Распределение   +----------+----------+----------+
 //   данных по страницам. | 512 | 16 | 512 | 16 | 512 | 16 | ....
 //                        +----------+----------+----------+
 //                         \     \   |     |   /     /
 //                          \     \  |     |  /     /
 //  ----------------------------------------------------------------------------------
 //                            \     \|     |/     /
 //  Эмуляция размера страниц   +-----+-----+-----+     Так представляются страницы чипа в режиме
 //  режим эмуляции 512 байт.   | 512 | 512 | 512 |     эмуляции. Однако последовательное чтение
 //                             +-----+-----+-----+     выдает 16 байтное дополнение м/у ними.
 //                             page N     page N+2
 //                                 page N+1


 #endif

 #ifdef  AT45DB321D_PRM512
 #ifndef AT45DB321D_EMU512
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__use512page - сконфигурировать чип на работу со стандартной страницей 512 байт
 //  
 // # НАВСЕГДА изменяет размер страницы!
 // * потоко-небезопасная функция
 // * недоступна в режиме программной эмуляции 512-байтных страниц (AT45DB321D_EMU512)
 // # для применения нового размера требуется ОТКЛЮЧЕНИЕ ПИТАНИЯ!
 void __flash_hal__use512page()
 {
     chip_sel_active();

     ( (__flash_packet_pagecnfg_t*) cmdbuffer )->opcode  = _PCMD_PGSZCNFG;

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

     chip_sel_inactive();

     #if AT45DB321D_BKGOPERATIONS == 0
     __FLASH_SMART_WAITms(_TIME_PGPRG_ms);
     #endif
 } 
 #endif
 #endif
 // --------------------------------------------------------------------------------------------------------
 
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__buffer1_read - чтение буфера 1 (up to 66 MHz)
 // 
 // * не изменяет содержимое внутренних SRAM буферов
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция 

 void __flash_hal__buffer1_read( __FLASH_WORD wBuffOfs,      // адрес начала чтения в буфере
                                 __FLASH_BYTE * pDataRead,   // указатель на буфер-приемник данных
                                 __FLASH_WORD wCntToRead )   // количество данных на чтение
 {
      chip_sel_active();

      ( (__flash_packet_bufferread_fast_t*) cmdbuffer )->opcode  = _RCMD_BUF1READ;      
      ( (__flash_packet_bufferread_fast_t*) cmdbuffer )->offset  = wBuffOfs;

      ( (__flash_packet_bufferread_fast_t*) cmdbuffer )->__reserved  = 0;
      ( (__flash_packet_bufferread_fast_t*) cmdbuffer )->__reserved2 = 0;
      
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_bufferread_fast_t) );

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

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

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__buffer2_read - чтение буфера 2 (up to 66 MHz)
 // 
 // * не изменяет содержимое внутренних SRAM буферов
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция 

 void __flash_hal__buffer2_read( __FLASH_WORD wBuffOfs,      // адрес начала чтения в буфере
                                 __FLASH_BYTE * pDataRead,   // указатель на буфер-приемник данных
                                 __FLASH_WORD wCntToRead )   // количество данных на чтение
 {
      chip_sel_active();

      ( (__flash_packet_bufferread_fast_t*) cmdbuffer )->opcode  = _RCMD_BUF2READ;      
      ( (__flash_packet_bufferread_fast_t*) cmdbuffer )->offset  = wBuffOfs;

      ( (__flash_packet_bufferread_fast_t*) cmdbuffer )->__reserved  = 0;
      ( (__flash_packet_bufferread_fast_t*) cmdbuffer )->__reserved2 = 0;
      
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_bufferread_fast_t) );

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

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

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__buffer1_read_slow - чтение буфера 1 (up to 33 MHz)
 // 
 // * не изменяет содержимое внутренних SRAM буферов
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция 

 void __flash_hal__buffer1_read_slow( __FLASH_WORD wBuffOfs,      // адрес начала чтения в буфере
                                      __FLASH_BYTE * pDataRead,   // указатель на буфер-приемник данных
                                      __FLASH_WORD wCntToRead )   // количество данных на чтение
 {
      chip_sel_active();

      ( (__flash_packet_bufferread_slow_t*) cmdbuffer )->opcode  = _RCMD_BUF1READ;      
      ( (__flash_packet_bufferread_slow_t*) cmdbuffer )->offset  = wBuffOfs;

      ( (__flash_packet_bufferread_slow_t*) cmdbuffer )->__reserved  = 0;      
      
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_bufferread_slow_t) );

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

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

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__buffer2_read_slow - чтение буфера 2 (up to 33 MHz)
 // 
 // * не изменяет содержимое внутренних SRAM буферов
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция 

 void __flash_hal__buffer2_read_slow( __FLASH_WORD wBuffOfs,      // адрес начала чтения в буфере
                                      __FLASH_BYTE * pDataRead,   // указатель на буфер-приемник данных
                                      __FLASH_WORD wCntToRead )   // количество данных на чтение
 {
      chip_sel_active();

      ( (__flash_packet_bufferread_slow_t*) cmdbuffer )->opcode  = _RCMD_BUF2READ;      
      ( (__flash_packet_bufferread_slow_t*) cmdbuffer )->offset  = wBuffOfs;

      ( (__flash_packet_bufferread_slow_t*) cmdbuffer )->__reserved  = 0;      
      
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_bufferread_slow_t) );

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

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

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__buffer1_write - запись буфера 1
 // 
 // * ИЗМЕНЯЕТ содержимое внутренних SRAM буферов
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция 

 void __flash_hal__buffer1_write( __FLASH_WORD wBuffOfs,      // адрес начала записи в буфере-приемнике (чип)
                                  __FLASH_BYTE * pDataWrite,  // указатель на буфер-источник данных
                                  __FLASH_WORD wCntToWrite )  // количество данных на запись
 {
      chip_sel_active();

      ( (__flash_packet_bufferwrite_t*) cmdbuffer )->opcode  = _WCMD_BUF1WRIT;      
      ( (__flash_packet_bufferwrite_t*) cmdbuffer )->offset  = wBuffOfs;      

      ( (__flash_packet_bufferwrite_t*) cmdbuffer )->__reserved = 0;      
      
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_bufferwrite_t) );

      // пишем данные
      __FLASH_HAL_WR( pDataWrite,  wCntToWrite );

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

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__buffer2_write - запись буфера 2
 // 
 // * ИЗМЕНЯЕТ содержимое внутренних SRAM буферов
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция 

 void __flash_hal__buffer2_write( __FLASH_WORD wBuffOfs,      // адрес начала записи в буфере-приемнике (чип)
                                  __FLASH_BYTE * pDataWrite,  // указатель на буфер-источник данных
                                  __FLASH_WORD wCntToWrite )  // количество данных на запись
 {
      chip_sel_active();

      ( (__flash_packet_bufferwrite_t*) cmdbuffer )->opcode  = _WCMD_BUF2WRIT;      
      ( (__flash_packet_bufferwrite_t*) cmdbuffer )->offset  = wBuffOfs;      

      ( (__flash_packet_bufferwrite_t*) cmdbuffer )->__reserved = 0;      
      
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_bufferwrite_t) );

      // пишем данные
      __FLASH_HAL_WR( pDataWrite,  wCntToWrite );

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

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

 void __flash_hal__buffer1_program( __FLASH_WORD wPageNum       // номер страницы, куда произвести программирование
                                  )  
 {
      chip_sel_active();

      ( (__flash_packet_bufferprogram_t*) cmdbuffer )->opcode  = _WCMD_BUF1PROG;      
      ( (__flash_packet_bufferprogram_t*) cmdbuffer )->page    = wPageNum;      

      ( (__flash_packet_bufferprogram_t*) cmdbuffer )->__reserved  = 0;      
      ( (__flash_packet_bufferprogram_t*) cmdbuffer )->__reserved2 = 0;

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

      chip_sel_inactive();

      #if AT45DB321D_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGEPR_ms );
      #endif
 }
 // -------------------------------------------------------------------------------------------------------- 

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

 void __flash_hal__buffer2_program( __FLASH_WORD wPageNum       // номер страницы, куда произвести программирование
                                  )  
 {
      chip_sel_active();

      ( (__flash_packet_bufferprogram_t*) cmdbuffer )->opcode  = _WCMD_BUF2PROG;      
      ( (__flash_packet_bufferprogram_t*) cmdbuffer )->page    = wPageNum;      

      ( (__flash_packet_bufferprogram_t*) cmdbuffer )->__reserved  = 0;      
      ( (__flash_packet_bufferprogram_t*) cmdbuffer )->__reserved2 = 0;

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

      chip_sel_inactive();

      #if AT45DB321D_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGEPR_ms );
      #endif
 }
 // -------------------------------------------------------------------------------------------------------- 

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

 void __flash_hal__buffer1_program_wo_erase( __FLASH_WORD wPageNum       // номер страницы, куда произвести программирование
                                           )  
 {
      chip_sel_active();

      ( (__flash_packet_bufferprogram_t*) cmdbuffer )->opcode  = _WCMD_BUF1PRGx;      
      ( (__flash_packet_bufferprogram_t*) cmdbuffer )->page    = wPageNum;      

      ( (__flash_packet_bufferprogram_t*) cmdbuffer )->__reserved  = 0;      
      ( (__flash_packet_bufferprogram_t*) cmdbuffer )->__reserved2 = 0;

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

      chip_sel_inactive();

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

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

 void __flash_hal__buffer2_program_wo_erase( __FLASH_WORD wPageNum       // номер страницы, куда произвести программирование
                                           )  
 {
      chip_sel_active();

      ( (__flash_packet_bufferprogram_t*) cmdbuffer )->opcode  = _WCMD_BUF2PRGx;      
      ( (__flash_packet_bufferprogram_t*) cmdbuffer )->page    = wPageNum;      

      ( (__flash_packet_bufferprogram_t*) cmdbuffer )->__reserved  = 0;      
      ( (__flash_packet_bufferprogram_t*) cmdbuffer )->__reserved2 = 0;

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

      chip_sel_inactive();

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

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

 void __flash_hal__page_erase( __FLASH_WORD wPageNum       // номер стираемой страницы
                             )  
 {
      chip_sel_active();

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

      ( (__flash_packet_pageerase_t*) cmdbuffer )->__reserved  = 0;      
      ( (__flash_packet_pageerase_t*) cmdbuffer )->__reserved2 = 0;

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

      chip_sel_inactive();

      #if AT45DB321D_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGERS_ms );
      #endif
 }
 // -------------------------------------------------------------------------------------------------------- 

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

 void __flash_hal__block_erase( __FLASH_WORD wBlockNum       // номер стираемого блока
                              )  
 {
      chip_sel_active();

      ( (__flash_packet_blockerase_t*) cmdbuffer )->opcode  = _ECMD_BLKERASE;      
      ( (__flash_packet_blockerase_t*) cmdbuffer )->block   =  wBlockNum;      

      ( (__flash_packet_blockerase_t*) cmdbuffer )->__reserved  = 0;      
      ( (__flash_packet_blockerase_t*) cmdbuffer )->__reserved2 = 0;

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

      chip_sel_inactive();

      #if AT45DB321D_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_BKERS_ms );
      #endif
 }
 // -------------------------------------------------------------------------------------------------------- 

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__sector0_erase - стирание секторов 0a и 0b
 //  
 // * не проверяет правильность переданных параметров
 // * потоко-небезопасная функция
 // * задерживает выполнение на СЕКУНДЫ

 void __flash_hal__sector0_erase( __FLASH_WORD wSubSector       // номер подсектора 0 или 1 
                                )
 {
      chip_sel_active();

      ( (__flash_packet_sector0erase_t*) cmdbuffer )->opcode    = _ECMD_SCTERASE;      
      ( (__flash_packet_sector0erase_t*) cmdbuffer )->subsector = wSubSector;

      ( (__flash_packet_sector0erase_t*) cmdbuffer )->__reserved  = 0;
      ( (__flash_packet_sector0erase_t*) cmdbuffer )->__reserved2 = 0;
      ( (__flash_packet_sector0erase_t*) cmdbuffer )->__reserved3 = 0;

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

      chip_sel_inactive();

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

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

 void __flash_hal__sector_erase( __FLASH_WORD wSector       // номер сектора 1-63 
                               )
 {
      chip_sel_active();

      ( (__flash_packet_sectorerase_t*) cmdbuffer )->opcode    = _ECMD_SCTERASE;      
      ( (__flash_packet_sectorerase_t*) cmdbuffer )->sector    = wSector;
      
      ( (__flash_packet_sectorerase_t*) cmdbuffer )->__reserved  = 0;
      ( (__flash_packet_sectorerase_t*) cmdbuffer )->__reserved2 = 0;
      
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_sectorerase_t) );

      chip_sel_inactive();

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

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

 void __flash_hal__page_write_via_buffer1(  __FLASH_WORD wBuffOfs,      // адрес начала записи в буфере-приемнике (чип)
                                            __FLASH_BYTE * pDataWrite,  // указатель на буфер-источник данных
                                            __FLASH_WORD wCntToWrite,   // количество данных на запись
                                            __FLASH_WORD wPageNum       // номер страницы, куда произвести программирование
                                         )
 {
      chip_sel_active();

      ( (__flash_packet_pagewriteviabuffer_t*) cmdbuffer )->opcode    = _WCMD_PGB1WRIT;      
      ( (__flash_packet_pagewriteviabuffer_t*) cmdbuffer )->offset    = wBuffOfs;
      ( (__flash_packet_pagewriteviabuffer_t*) cmdbuffer )->page      = wPageNum;
      
      ( (__flash_packet_pagewriteviabuffer_t*) cmdbuffer )->__reserved= 0;
            
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_pagewriteviabuffer_t) );

      // пишем данные
      __FLASH_HAL_WR( pDataWrite,  wCntToWrite );

      chip_sel_inactive();

      #if AT45DB321D_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGEPR_ms );
      #endif
 }
 // -------------------------------------------------------------------------------------------------------- 

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

 void __flash_hal__page_write_via_buffer2(  __FLASH_WORD wBuffOfs,      // адрес начала записи в буфере-приемнике (чип)
                                            __FLASH_BYTE * pDataWrite,  // указатель на буфер-источник данных
                                            __FLASH_WORD wCntToWrite,   // количество данных на запись
                                            __FLASH_WORD wPageNum       // номер страницы, куда произвести программирование
                                         )
 {
      chip_sel_active();

      ( (__flash_packet_pagewriteviabuffer_t*) cmdbuffer )->opcode    = _WCMD_PGB2WRIT;      
      ( (__flash_packet_pagewriteviabuffer_t*) cmdbuffer )->offset    = wBuffOfs;
      ( (__flash_packet_pagewriteviabuffer_t*) cmdbuffer )->page      = wPageNum;
      
      ( (__flash_packet_pagewriteviabuffer_t*) cmdbuffer )->__reserved= 0;
            
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_pagewriteviabuffer_t) );

      // пишем данные
      __FLASH_HAL_WR( pDataWrite,  wCntToWrite );

      chip_sel_inactive();

      #if AT45DB321D_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGEPR_ms );
      #endif
 }
 // -------------------------------------------------------------------------------------------------------- 

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

 void __flash_hal__pagetobuffer1(  __FLASH_WORD wPageNum       // номер страницы для загрузки
                                )
 {
      chip_sel_active();

      ( (__flash_packet_bufferload_t*) cmdbuffer )->opcode    = _ACMD_BUF1LOAD;      
      ( (__flash_packet_bufferload_t*) cmdbuffer )->page      = wPageNum;
      
      ( (__flash_packet_bufferload_t*) cmdbuffer )->__reserved =0;
      ( (__flash_packet_bufferload_t*) cmdbuffer )->__reserved2=0;
       
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_bufferload_t) );
     
      chip_sel_inactive();

      #if AT45DB321D_BKGOPERATIONS == 0
      __FLASH_WAITus( _TIME_BLDCM_us );
      #endif
 }
 // -------------------------------------------------------------------------------------------------------- 

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

 void __flash_hal__pagetobuffer2(  __FLASH_WORD wPageNum       // номер страницы для загрузки
                                )
 {
      chip_sel_active();

      ( (__flash_packet_bufferload_t*) cmdbuffer )->opcode    = _ACMD_BUF2LOAD;      
      ( (__flash_packet_bufferload_t*) cmdbuffer )->page      = wPageNum;
      
      ( (__flash_packet_bufferload_t*) cmdbuffer )->__reserved =0;
      ( (__flash_packet_bufferload_t*) cmdbuffer )->__reserved2=0;
            
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_bufferload_t) );
     
      chip_sel_inactive();

      #if AT45DB321D_BKGOPERATIONS == 0
      __FLASH_WAITus( _TIME_BLDCM_us );
      #endif
 }
 // -------------------------------------------------------------------------------------------------------- 

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

 void __flash_hal__buffer1_compare(  __FLASH_WORD wPageNum       // номер страницы для сравнения
                                 )
 {
      chip_sel_active();

      ( (__flash_packet_buffercompare_t*) cmdbuffer )->opcode    = _ACMD_BUF1CMPR;      
      ( (__flash_packet_buffercompare_t*) cmdbuffer )->page      = wPageNum;
      
      ( (__flash_packet_buffercompare_t*) cmdbuffer )->__reserved =0;
      ( (__flash_packet_buffercompare_t*) cmdbuffer )->__reserved2=0;
            
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_buffercompare_t) );
     
      chip_sel_inactive();

      #if AT45DB321D_BKGOPERATIONS == 0
      __FLASH_WAITus( _TIME_BLDCM_us );
      #endif
 }
 // -------------------------------------------------------------------------------------------------------- 

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

 void __flash_hal__buffer2_compare(  __FLASH_WORD wPageNum       // номер страницы для сравнения
                                 )
 {
      chip_sel_active();

      ( (__flash_packet_buffercompare_t*) cmdbuffer )->opcode    = _ACMD_BUF2CMPR;      
      ( (__flash_packet_buffercompare_t*) cmdbuffer )->page      = wPageNum;
      
      ( (__flash_packet_buffercompare_t*) cmdbuffer )->__reserved =0;
      ( (__flash_packet_buffercompare_t*) cmdbuffer )->__reserved2=0;
            
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_buffercompare_t) );
     
      chip_sel_inactive();

      #if AT45DB321D_BKGOPERATIONS == 0
      __FLASH_WAITus( _TIME_BLDCM_us );
      #endif
 }
 // -------------------------------------------------------------------------------------------------------- 

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

 void __flash_hal__autopagerewrite1( __FLASH_WORD wPageNum       // номер обновляемой страницы
                                   )  
 {
      chip_sel_active();

      ( (__flash_packet_autopagerewrite_t*) cmdbuffer )->opcode  = _ACMD_PAGEREF1;      
      ( (__flash_packet_autopagerewrite_t*) cmdbuffer )->page    = wPageNum;       

      ( (__flash_packet_autopagerewrite_t*) cmdbuffer )->__reserved  = 0;      
      ( (__flash_packet_autopagerewrite_t*) cmdbuffer )->__reserved2 = 0;

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

      chip_sel_inactive();

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

 void __flash_hal__autopagerewrite2( __FLASH_WORD wPageNum       // номер обновляемой страницы
                                   )  
 {
      chip_sel_active();

      ( (__flash_packet_autopagerewrite_t*) cmdbuffer )->opcode  = _ACMD_PAGEREF2;      
      ( (__flash_packet_autopagerewrite_t*) cmdbuffer )->page    = wPageNum;       

      ( (__flash_packet_autopagerewrite_t*) cmdbuffer )->__reserved  = 0;      
      ( (__flash_packet_autopagerewrite_t*) cmdbuffer )->__reserved2 = 0;

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

      chip_sel_inactive();

      #if AT45DB321D_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGEPR_ms );
      #endif
 }
 // --------------------------------------------------------------------------------------------------------  
 
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__sleepmode - переход в режим пониженного энергопотребления
 //  
 // * потоко-небезопасная функция
 // # переводит устройство в режим, когда все команды, кроме __flash_hal__wakeup игнорируются

 void __flash_hal__sleepmode()  
 {
      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 игнорируются

 void __flash_hal__wakeup()  
 {
      chip_sel_active();

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

      ( (__flash_packet_powerdown_t*) cmdbuffer )->opcode  = _ACMD_LPWRDOWN;            
    
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_powerdown_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.
 // * функция способна определять расширенный Manufacturer ID согласно JEDEC JEP-106. Таким образом, кроме
 //   стандартного ID производителя длинной 1 байт, производитель может использовать т.н. Continuation Code - 0x7F
 //   Если стандартный код читается как 7F, функия продолжает чтение идентификатора до тех пор, пока не будет
 //   принят байт, отличный от 7F, причем количество таких кодов 7F будет сохранено  (не более 255)
 //   и интерпретироваться как HighId. Если функция обнаружит более 255 кодов 7F, они подсчитаны не будут, при этом
 //   HighId будет равен 255, а LowId - первый найденный не-7F код (даже после более 255 кодов 7F).
 // * вовзращаемое значение - DWORD, "откушенный" по младшему адресу от структуры __flash_id_t - содержит
 //   DevId, LowId, HighId.  
  __FLASH_DWORD __flash_hal__manufactureridread( __flash_id_t * pManufacturerID // необязательный параметр-буфер для приема расширенной информации
                                               )  
 {    
      // локальные переменные функции объединены с возвращаемым значением, чтобы вернуть
      // часть информации в виде 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();

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

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

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

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

          if( HighId < 255 ) HighId += ( (__FLASH_MANIDEX_OPCODE == LowId)? 1 : 0 );
      }
      while( (__FLASH_MANIDEX_OPCODE == LowId) );

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

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

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

      // чтение расширенной информации только если нам передали структуру для заполнения
      if( pManufacturerID )
      {            
          // заполняем поля в переданной нам структуре
          pManufacturerID->DevId  =  DevId;  // device ID
          pManufacturerID->LowId  =  LowId;  // Manufacturer ID (Standart)
          pManufacturerID->HighId = HighId;  // Manufacturer ID (Extended - Continuation Code 0x7F count)

          // записываем длинну расширенной информации          
          {
              __FLASH_BYTE ExtLen;

              // читаем длинну расширенной информации
              __FLASH_HAL_RD( &ExtLen, sizeof( ExtLen ) );

              // записываем длинну расширенной информации          
              pManufacturerID->ExtLen = ExtLen;
              
              // если есть куда записать расширенную информацию
              if( pManufacturerID->pExtBf )
              {
                    // если на чипе есть расширенная информация
                    if( ExtLen )
                    {  
                        // ограничиваем емкость буфера по размеру расширенной информации
                        if( pManufacturerID->ExtLen > ExtLen )
                        {
                            pManufacturerID->ExtLen = ExtLen;
                        }
                     
                        // сначала считываем доступное количество байт (определяется значением ExtLen в ВЫХОДНОЙ структуре
                        __FLASH_HAL_RD( pManufacturerID->pExtBf, sizeof( pManufacturerID->ExtLen ) );

                        // затем перезаписываем длинну в выходную структуру
                        pManufacturerID->ExtLen = ExtLen;
                    }
                    else
                    // нет расширенной информации
                    {
                       pManufacturerID->ExtLen = 0; // обнуляем длинну расширенной информации в выходной структуре
                    }
              }
          }
      } 
     
      //-----------

      chip_sel_inactive(); 

      //-----------
      
      return ReturnValue;
 }
 // --------------------------------------------------------------------------------------------------------  
 
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__writeprotect_enable - включить программную защиту от записи/стирания
 //  
 // * потоко-небезопасная функция
 // # переводит устройство в режим, когда невозможно стирать/записывать некоторые секторы

 void __flash_hal__writeprotect_enable()  
 {
      chip_sel_active();

      ( (__flash_packet_protecttoggle_t*) cmdbuffer )->opcode  = _SCMD_4SCTPREN;            
    
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_protecttoggle_t) );

      chip_sel_inactive();  

      // если разрешено управление аппаратным сигналом защиты записи
      #if AT45DB321D_HW_WR_PROTECT
      // если аппаратная защита записи не отключена в настройках
      #if AT45DB321D_HW_WR_PROTECT_KEEPUNPROTECTED == 0
      // установить аппаратную защиту записи
      __imp_flash_hwwrprotect_assert();
      #endif
      #endif    
 }
 // --------------------------------------------------------------------------------------------------------  
 
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__writeprotect_disable - выключить программную защиту от записи/стирания
 //  
 // * потоко-небезопасная функция
 // # выводит устройство из режима, когда невозможно стирать/записывать некоторые секторы (если нет аппаратной защиты)

 void __flash_hal__writeprotect_disable()  
 {
      // если разрешено управление аппаратным сигналом защиты записи
      #if AT45DB321D_HW_WR_PROTECT 
      // если аппаратная защита записи не отключена в настройках
      #if AT45DB321D_HW_WR_PROTECT_KEEPUNPROTECTED == 0
      // снять аппаратную защиту записи
      __imp_flash_hwwrprotect_release();
      #endif
      #endif 

      chip_sel_active();

      ( (__flash_packet_protecttoggle_t*) cmdbuffer )->opcode  = _SCMD_4SCTPRDS;            
    
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_protecttoggle_t) );

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

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__protectregister_erase - стирает регистр защиты, помечая ВСЕ сектора как защищенные
 //  
 // * потоко-небезопасная функция
 // # после выдачи этой команды и последующей __flash_hal__writeprotect_enable все сектора будут защищены от записи/стирания
 // # если включена программная защита от записи, все операции стирания/записи для всех секторов будут заблокированы до ее отключения
 // * задерживает выполнение на ДЕСЯТКИ МИЛЛИСЕКУНД
 void __flash_hal__protectregister_erase()  
 {
      chip_sel_active();

      ( (__flash_packet_protectregistererase_t*) cmdbuffer )->opcode  = _SCMD_4PRREGER;            
    
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_protectregistererase_t) );

      chip_sel_inactive();

      #if AT45DB321D_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGERS_ms );
      #endif     
 }
 // --------------------------------------------------------------------------------------------------------  

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__protectregister_write - записывает регистр защиты
 //  
 // * потоко-небезопасная функция
 // * параметр @contents не может быть NULL - укажите содержимое регистра защиты (см. __flash_protectionregister_t)
 // # требуется предварительное стирание! (__flash_hal__protectregister_erase)
 // * задерживает выполнение на МИЛЛИСЕКУНДЫ
 // * ИЗМЕНЯЕТ СОДЕРЖИМОЕ ВСТРОЕННОГО БУФЕРА 1
 void __flash_hal__protectregister_write( __flash_protectionregister_t * contents  // содержимое регистра защиты для записи
                                        )  
 {
      if( !contents ) return;

      chip_sel_active();

      ( (__flash_packet_protectregisterwrite_t*) cmdbuffer )->opcode  = _SCMD_4PRREGWR;            
    
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_protectregisterwrite_t) );
       // пишем регистр защиты
      __FLASH_HAL_WR( contents, sizeof(__flash_protectionregister_t) );

      chip_sel_inactive();

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

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__protectregister_read - прочитывает регистр защиты
 //  
 // * потоко-небезопасная функция
 // * параметр @contents не может быть NULL - укажите буфер для содержимого регистра защиты (см. __flash_protectionregister_t)
 void __flash_hal__protectregister_read( __flash_protectionregister_t * contents // буфер-приемник содержимого регистра защиты
                                       )  
 {
      if( !contents ) return;

      chip_sel_active();

      ( (__flash_packet_protectregisterread_t*) cmdbuffer )->opcode     = _SCMD_PRTREGRD; 
      ( (__flash_packet_protectregisterread_t*) cmdbuffer )->__reserved = 0;      
    
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_protectregisterwrite_t) );
       // читаем регистр защиты
      __FLASH_HAL_RD( contents, sizeof(__flash_protectionregister_t) );

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

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__sectorlockdown - постоянная блокировка сектора от записи/стирания
 //  
 // * потоко-небезопасная функция
 // # блокирует сектор навсегда: он становится "только для чтения"
 // * укажите адрес, принадлежащий сектору в параметре @address
 // * задерживает выполнение на МИЛЛИСЕКУНДЫ
 void __flash_hal__sectorlockdown( __FLASH_DWORD address   // адрес внутри блокируемого сектора
                                 )  
 {
      chip_sel_active();

      ( (__flash_packet_sectorlockdown_t*) cmdbuffer )->opcode   = _SCMD_4SCTLKDN; 

      // заполняем адрес
      ( (__flash_packet_sectorlockdown_t*) cmdbuffer )->laddress =   address        & 0x0000FFFF;
      ( (__flash_packet_sectorlockdown_t*) cmdbuffer )->haddress =  (address >> 16) & 0x000000FF;
    
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_sectorlockdown_t) );

      chip_sel_inactive(); 

      #if AT45DB321D_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGPRG_ms );
      #endif     
 }
 // --------------------------------------------------------------------------------------------------------  
 
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__lockdownregister_read - прочитывает регистр блокировки
 //  
 // * потоко-небезопасная функция
 // * параметр @contents не может быть NULL - укажите буфер для содержимого регистра блокировки (см. __flash_lockdownregister_t)
 void __flash_hal__lockdownregister_read( __flash_lockdownregister_t * contents  // буфер-приемник содержимого регистра блокировки 
                                        )  
 {
      if( !contents ) return;

      chip_sel_active();

      ( (__flash_packet_lockdownregisterread_t*) cmdbuffer )->opcode     = _SCMD_LKDNRGRD; 
      ( (__flash_packet_lockdownregisterread_t*) cmdbuffer )->__reserved = 0;      
    
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_lockdownregisterread_t) );
       // читаем регистр блокировки
      __FLASH_HAL_RD( contents, sizeof(__flash_lockdownregister_t) );

      chip_sel_inactive();   
 }
 // --------------------------------------------------------------------------------------------------------  
 
 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__securityregister_write - записывает регистр безопасности
 //  
 // * потоко-небезопасная функция
 // * параметр @contents не может быть равен NULL
 // * ИЗМЕНЯЕТ СОДЕРЖИМОЕ ВСТРОЕННОГО БУФЕРА 1
 // * записывает только ПОЛЬЗОВАТЕЛЬСКУЮ часть
 // # РЕГИСТР БЕЗОПАСНОСТИ МОЖЕТ БЫТЬ ЗАПИСАН ТОЛЬКО ОДИН РАЗ!
 void __flash_hal__securityregister_write( __flash_usersecurityregister_t * contents  // содержимое регистра безопасности (часть, доступная для записи пользователю) 
                                         )  
 {
      if( !contents ) return;

      chip_sel_active();

      ( (__flash_packet_securityregisterwrite_t*) cmdbuffer )->opcode     = _SCMD_4SECRGWR;       
    
      // отправляем команду
      __FLASH_HAL_WRCMD( &cmdbuffer, sizeof(__flash_packet_securityregisterwrite_t) );
       // записываем регистр безопасности
      __FLASH_HAL_WR( contents, sizeof(__flash_usersecurityregister_t) );
      
      #if AT45DB321D_BKGOPERATIONS == 0
      __FLASH_SMART_WAITms( _TIME_PGPRG_ms );
      #endif

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

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

      chip_sel_active();

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

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

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

          __FLASH_DWORD n00 = 0;
          __FLASH_DWORD nFF = 0;
          for( __FLASH_DWORD i = 0; i < __FLASH_FACT_SECURITY_BYTES; ++i )
          {
              if( 0xFF == contents->FactoryPart.UserId[i] ) ++nFF;
              if( 0x00 == contents->FactoryPart.UserId[i] ) ++n00;
          }
          
          if( (n00 != __FLASH_FACT_SECURITY_BYTES)
              &&
              (nFF != __FLASH_FACT_SECURITY_BYTES) )
          {
              return true;
          }
      }

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

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

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

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

 // --------------------------------------------------------------------------------------------------------
 // __flash_hal__power_pulse - реализует управление питанием микросхемы - формирует импульс перезагрузки
 void __flash_hal__power_pulse( __FLASH_WORD nCooldownTime_ms, __FLASH_WORD nStartupTime_ms )
 {
     // снять питание
     __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 AT45DB321D_RESET_MANAGEMENT
     // сформировать импульс сброса микросхемы
     __flash_hal__reset_pulse();
     #endif

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

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

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

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

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

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

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

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

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

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