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

 // используйте следующие макросы перед подключением этого заголовочного файла
 // W25Q16JV_APILEVEL - для доступа к структурам и типам низокуровневых функций
 // W25Q16JV_LOWLEVEL - для доступа ко всем низкоуровневым определениям
 //
 // например: # файл c API для работы с флешкой использует
 //             только интерфейсы, ему достаточно только W25Q16JV_APILEVEL
 //           # файл, который реализует низкоуровневые команды требует W25Q16JV_LOWLEVEL
 //           # пользовательский файл высокоуровневой программы вообще не требует этих макросов

#ifndef W25Q16JV_H
#define W25Q16JV_H

     // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
     // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ ВВЕДЕНИЕ №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
     // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№

     // ВНИМАНИЕ! Здесь и далее вводится замена "Сектор" => "Блок" и наоборот, т.к. данный драйвер основан
     // драйвере для чипа AT45, где под сектором понимается самый большой кусок данных, под блоком - кусок
     // по меньше, и под страницей - самый меньший. В W25Q под блоком наоборт - самый большой, а под сектором
     // самый малый, а страниц нет вообще. Для унификации для W25Q вводим замену понятия Блок - в даташите
     // блок это самый большой кусок, а в драйвере - будет самый малый. В даташите - сектор - самый малый
     // в данном драйвере - самый большой.
     // Такая замена необходима для сохранения внутренней структуры драйвера, функций и макросов.

     // W25Q16JV - чип энергонезависимой памяти емкостью 16 мегабит.
     // Чип W25Q16JV состоит из 32 секторов, 2 особенных и 30 обычных. Каждый сектор делится на 16 блоков
     // размером 4KB. Особенность 2 секторов заключается в том, как работает блочная защита от записи.
     // Особенные сектора расположены: один в начале памяти, один в конце.
     // Чип обладает 2 программными способами защиты от записи: индивидуальная защита секторов и групповая.
     // Индивидуальная защита позволяет заблокировать независимо каждый из 30 обычных секторов и независимо
     // каждый блок из 2 особенных секторов. В итоге есть возможность защитить 62 элемента: 30 по секторам
     // (все 16 блоков каждого из 30 секторов) и 32 по блокам (16+16 из 2 секторов). Групповая защита
     // строится на регистре защиты, где выбирается определенная часть защищаемого пространства от начала
     // памяти или от конца памяти. По умолчанию выбрана групповая схема защиты.
     //
     // Защита от записи:
     // -переходит в состояние сбороса если питание снижено до предела
     // -разрешает запись только после прошествии защитного итервала после выхода их сброса (либо при подаче питания)
     // -программная защита от записи активируется после каждой операции записи/стирания
     // -имеется HW и SW защита регистра статуса
     // -возможность индивидуальной защиты блока
     // -возможность защиты по защелке: защита снимается только после Power-up (пробуждения)
     // -возможность одноразового программирования: OTP
     //
     // Производитель обращает внимание на особенность, связанную с защитой от записи при формировании
     // сигнала выбора (CS): управляющий контроллер должен отслеживать уровень питания чипа с тем,
     // чтобы выставлять CS только когда уровень питания находится в допустимой зоне после старта,
     // а также перед выключнием, чтобы исключить выбор устройства вблизи границы напряжения питания.
     //
     // Чип позволяет использовать дополнительные линии передачи данных совместно с DO,
     // а именно Dual-Output и Quad-Output, для чего использутся отдельные команды.
     // Данная библиотека НЕ ПОДДЕРЖИВАЕТ данные режимы.

/* <CONFIG> */
     #include <stdint.h>
     #include <stddef.h>
     #include "drivers\flash\w25q\config\W25Q16JV_CONF.h"
     #include "drivers\flash\base\bitbuffer.h"

     #ifndef W25QXXX_API_TIMEOUT
     #define W25QXXX_API_TIMEOUT _TIME_FLASHAPI_TIMEOUT
     #endif
/* </CONFIG> */

     // Библиотека позволяет исключить циклы ожидания после
     // длительных команд, переложив ожидание ПОСЛЕ команды
     // в ожидание ПЕРЕД командой - активное ожидание освобождения
     // устройства через сканирования флага BUSY в статусном регистре.
     // Библиотека в любом случае использует чтение флага перед
     // каждой низкоуровневой командой, но делает это обычно 1 раз,
     // возвращая ошибку после неудачи. Используйте макрос W25QXXX_BKGOPERATIONS=1,
     // для того, чтобы использовать постоянное сканирование флага
     // перед командой вместо ожидания в конце команды. Используя данный режим,
     // будьте внимательны при сохранении данных перед выключением питания.

     // Библиотека позволяет использовать "умные" задержки после длительных
     // операций. Это означает, что ожидание после команды представляет собой
     // периодическое сканирование флага BUSY. Заданная в спецификации задержка
     // разбивается на квантованные промежутки времени, через которые происходит
     // опрос занятости устройства. Так предположительно длительная задержка
     // может быть сокращена при досрочном освобождении устройства. По умолчанию
     // режим "умных" задержек включен. Для выключения объявите макрос W25Q16JV_DONTUSESMARTDELAY=1
     // Режим "умных" задержек используется для обеспечения МИЛЛИСЕКУНДНЫХ задержек более W25QXXX_SMART_WAIT_THRESHOLD миллисекунд

     #define __FLASH_PAGE_WRITECYCLES            100000   // main datasheet
     #define __FLASH_STATUSREGISTER_WRITECYCLES  100000   // AN0000016 August 7, 2019 Revision 2.0
/* <CONFIG> */
     //#define W25Q16JV_DONTUSESMARTDELAY 0 // отключение "умной" задержки
     //#define W25QXXX_BKGOPERATIONS      0 // отключение задержек операций | ВНИМАНИЕ! Только для ОПЫТНЫХ пользователей (-: !!!

/* </CONFIG> */
     //--------------------------------------------------------------
     // Иерархия распределения блоков и секторов
     // Номера секторов - отнисительно блока секторов
     //--------------------------------------------------------------

     //  W25Q16JV
     //  _________
     //      |
     //      |-[-] Сектор 0 / Специальный сектор, содержит 16 блоков 4КБ
     //      |  |                                                                      _____
     //      |  |___ Блок 0   }-----------------------------------------------+ [ 0] |   |
     //      |  |___ Блок 1   }-----------------------------------------------+ [ 1] |   |
     //      |  .                                                             |      | I |
     //      |  .                                                             .      | N |
     //      |  .                                                             |      | D |
     //      |  |___ Блок 15  }-----------------------------------------------+ [15] | I |
     //      |                                                                |      | V |
     //      |                                                                |      | I |
     //      |-[-] Сектор 1 / Обычный Сектор, содержит 16 блоков 4КБ   }------+ [16] | D |
     //      |  |                                                             |      | U |
     //      |  |___ Блок 0                                                   |      | A |
     //      |  |___ Блок 1                                                   |      | L |
     //      |  .                                                             .      |   |
     //      |  .                                                             .      | S |
     //      |  .                                                             .      | E |
     //      |  |___ Блок 15                                                  |      | C |
     //      |                                                                |      | T |
     //      |                                                                |      | O |
     //      |-[-] Сектор 2 / Обычный Сектор, содержит 16 блоков 4КБ   }------+ [17] | R |
     //      |  |                                                             |      | / |
     //      |  |___ Блок 0                                                   |      | B |
     //      |  |___ Блок 1                                                   |      | L |
     //      |  .                                                             .      | O |
     //      |  .                                                             .      | C |
     //      |  .                                                             .      | K |
     //      |  |___ Блок 15                                                  |      |   |
     //      .                                                                .      | P |
     //      .                                                                .      | R |
     //      .                                                                .      | O |
     //      |-[-] Сектор 30 / Обычный Сектор, содержит 16 блоков 4КБ  }------+ [58] | T |
     //      |  |                                                             |      | E |
     //      |  |___ Блок 0                                                   |      | C |
     //      |  |___ Блок 1                                                   |      | T |
     //      |  .                                                             .      | I |
     //      |  .                                                             .      | O |
     //      |  .                                                             .      | N |
     //      |  |___ Блок 15                                                  |      |   |
     //      |                                                                |      | R |
     //      |                                                                |      | E |
     //      |-[-] Сектор 31 / Специальный Сектор, содержит 16 блоков 4КБ     |      | G |
     //      |  |                                                             |      | I |
     //      |  |___ Блок 0   }-----------------------------------------------+ [59] | S |
     //      |  |___ Блок 1   }-----------------------------------------------+ [60] | T |
     //      |  .                                                             .      | E |
     //      |  .                                                             .      | R |
     //      |  .                                                             .      |   |
     //      |  |___ Блок 15  }-----------------------------------------------+ [61] |___|
     //
     //----------------------------------------------------------------------------------------------------------

     // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
     // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ Внутренние определения №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
     // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№

     // ##__##__##__##__##__##__##__##__##__##__##__##__##__##__##__##
     // ##                                                          ##
     // ##                 Распределение памяти                     ##
     // ##                                                          ##
     // ##__##__##__##__##__##__##__##__##__##__##__##__##__##__##__##

     // размер страницы
         #define __FULL_PAGE_SIZE  4096
         #define __PROG_PAGE_SIZE  256     // размер страницы при программировании
         // страницы (=сектора) стандартные - каждая страница имеет 4096 байт данных

         #define __PAGE_SIZE        __FULL_PAGE_SIZE // размер страницы: для API
         #define __PAGE_SIZE_USER   __PAGE_SIZE      // размер страницы: для пользователя

     // размер блока
         #define __PAGES_PER_BLOCK  1
         #define __BLOCK_SIZE       ( __PAGES_PER_BLOCK * __PAGE_SIZE )
         #define __BLOCK_SIZE_USER  ( __PAGES_PER_BLOCK * __PAGE_SIZE_USER )

     // Количество секторов, блоков
         #define __SECTORS_TYPICAL  30    // количество обычных секторов
         #define __SECTORS_SPECIAL  2     // количество особенных секторов
         #define __SECTORS_FULL     (__SECTORS_TYPICAL + __SECTORS_SPECIAL) // полное количество секторов
         #define __SECTORS          __SECTORS_FULL
         #define __BLOCKS            512  // полное количество блоков
         #define __BLOCKS_PER_SECTOR 16   // количество блоков в секторе
         #define __FULL_PAGES        (__BLOCKS * __PAGES_PER_BLOCK)
         #define __PAGES_PER_SECTOR  (__BLOCKS_PER_SECTOR * __PAGES_PER_BLOCK )
         #define __PROT_SECTOR_FLAGS (__SECTORS_TYPICAL + (__SECTORS_SPECIAL) * (__BLOCKS_PER_SECTOR) )
         #define __PROT_SECTOR_TO_ADDRESS(n) W25Q16JV_prot_sector_to_address(n)
         #define __SECTOR_TO_PROTSECTOR(n,m) W25Q16JV_sector_to_protsector(n,&m)

     // размеры секторов
         #define __SECTOR_STD_SIZE  0x10000       // 64K

         #define __SECTOR0_SIZE     __SECTOR_STD_SIZE
         #define __SECTOR1_SIZE     __SECTOR_STD_SIZE
         #define __SECTOR2_SIZE     __SECTOR_STD_SIZE
         #define __SECTOR3_SIZE     __SECTOR_STD_SIZE
         #define __SECTOR4_SIZE     __SECTOR_STD_SIZE
         #define __SECTOR5_SIZE     __SECTOR_STD_SIZE
         #define __SECTOR6_SIZE     __SECTOR_STD_SIZE
         #define __SECTOR7_SIZE     __SECTOR_STD_SIZE
         #define __SECTOR8_SIZE     __SECTOR_STD_SIZE
         #define __SECTOR9_SIZE     __SECTOR_STD_SIZE
         #define __SECTOR10_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR11_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR12_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR13_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR14_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR15_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR16_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR17_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR18_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR19_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR20_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR21_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR22_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR23_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR24_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR25_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR26_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR27_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR28_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR29_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR30_SIZE    __SECTOR_STD_SIZE
         #define __SECTOR31_SIZE    __SECTOR_STD_SIZE

         #define __FULL_CHIP_SIZE   (__BLOCKS * __PAGES_PER_BLOCK * __FULL_PAGE_SIZE)
         #define __FULL_DATA_SIZE   (__FULL_CHIP_SIZE)

         // переводит НОМЕР защищаемого сектора в адрес
         // Защищаемый сектор - не всегда сектор 64КБ, для 0 и 31 секторов 64К из них
         // выводятся биты защиты для каждого блока 4К
         //
         // используется для команд индивидуальной защиты секторов/блоков
         static inline size_t W25Q16JV_prot_sector_to_address(size_t protsector)
         {
             size_t w24Address = 0;
             if( protsector > 0 && protsector < __PROT_SECTOR_FLAGS )
             {
                 // проверка на первый специальный сектор, состоящий из __BLOCKS_PER_SECTOR
                 // каждый блок такого сектора представлен отдельным битом защиты
                 // Это сектор 0
                 if( protsector < __BLOCKS_PER_SECTOR )
                 {
                     // адрес наращивается для каждого блока 4К в секторе 64К
                     w24Address = (protsector * __BLOCK_SIZE);
                 }
                 else
                 // Это обычные сектора 1..30, каждый сектора представлен одним битом защиты, без разбиения на блоки
                 if( protsector >= __BLOCKS_PER_SECTOR && protsector < __BLOCKS_PER_SECTOR + __SECTORS_TYPICAL )
                 {
                     w24Address = (__SECTOR0_SIZE) + (protsector - __BLOCKS_PER_SECTOR) * (__SECTOR_STD_SIZE);
                 }
                 // Это опять особенный, последний, сектор, разделенный на защищаемые блоки
                 else
                 {
                     w24Address = (protsector - __BLOCKS_PER_SECTOR - __SECTORS_TYPICAL) * (__BLOCK_SIZE) + (__SECTOR_STD_SIZE)*(__SECTORS - 1);
                 }
             }
             return w24Address;
         }

         // вычисляет начальный номер protection-sector-block для сектора 64КБ
         // используется для команд индивидуальной защиты секторов/блоков
         static inline size_t W25Q16JV_sector_to_protsector(size_t nSector, size_t * pCount )
         {
            size_t nStartProtSector = 0;
            
            if( nSector == 0 ) 
            {
               nStartProtSector = 0;
              *pCount = __BLOCKS_PER_SECTOR; // количество protsectors в секторе 64КБ = 16 для особенного сектора
            } 
            else if( nSector == __SECTORS - 1 )
            {
               nStartProtSector = __BLOCKS_PER_SECTOR + __SECTORS_TYPICAL;
              *pCount = __BLOCKS_PER_SECTOR; // количество protsectors в секторе 64КБ = 16 для особенного сектора
            }
            else
            {
               nStartProtSector = __BLOCKS_PER_SECTOR + nSector - 1;
              *pCount = 1; // количество protsectors в секторе 64КБ = 1 для обычного сектора
            }
            return nStartProtSector; // начальный номер protsector-а для сектора 64КБ
         }

         #include "drivers\flash\base\flash_api_types.h"

#ifdef W25Q16JV_LOWLEVEL
     //---------------------------------------------------------------
     // Чип имеет 2 встроенных SRAM буфера по 528 байт для проведения
     // операций чтения/записи и сравнения. Чип позволяет читать
     // данные последовательно, страницу за страницей (послед. чтение).
     // Также возможна запись содержимого буфера в память со стиранием,
     // при этом стирание происходит автоматически.

     //---------------------------------------------------------------
     // ##__##__##__##__##__##__##__##__##__##__##__##__##__##__##__##
     // ##                                                          ##
     // ##                   команды обмена                         ##
     // ##                                                          ##
     // ##__##__##__##__##__##__##__##__##__##__##__##__##__##__##__##

     // чтение
       #define _RCMD_ARRYREAD     0x03            // последовательное чтение
       #define _LCMD_ARRYREAD     0x03            // последовательное чтение (до 50MHz)
       #define _HCMD_ARRYREAD     0x0B            // последовательное чтение (до 133MHz)

     // запись
       #define _WCMD_PGWRIT       0x02            // Запись страницы

     // Стирание: Чип позволяет стирать память по-странично, и по-блочно
       #define _ECMD_PAGERASE     0x20            // Стирание страницы
       #define _ECMD_BLKERASE32   0x52            // Стирание 8 страниц

     // по-секторно
       #define _ECMD_SCTERASE     0xD8            // Стирание сектора

     // или целиком (последовательность 4 команд-байт)
       #define _ECMD_CHPERASE     0xC7            // Стрирание чипа

       // энергосберегающий режим, сброс
       #define _ACMD_EPWRDOWN     0xB9            // переход в спящий режим
       #define _ACMD_LPWRDOWN     0xAB            // выход их спящего режима
       #define _ACMD_ENARESET     0x66            // разрешение сброса чипа
       #define _ACMD_DORESET      0x99            // команда сброса чипа (Требуется предварительная 0x66)


       // команды чтения/записи "статусных" регистров
       #define _ACMD_STATREAD_1   0x05            // чтение статусного регистра 1
       #define _ACMD_STATREAD_2   0x35            // чтение статусного регистра 2
       #define _ACMD_STATREAD_3   0x15            // чтение статусного регистра 3
       #define _ACMD_STATWRIT_1   0x01            // запись статусного регистра 1
       #define _ACMD_STATWRIT_2   0x31            // запись статусного регистра 2
       #define _ACMD_STATWRIT_3   0x11            // запись статусного регистра 3

       // команды идентификации
       #define _ACMD_MFIDREAD     0x9F            // чтение информации о производителе и ID микросхемы
       #define _ACMD_SFDPREAD     0x5A            // чтение регистра Serial Flash Discoverable Parameter (Информация о производителе и устройстве)
       #define _ACMD_UIDRREAD     0x4B            // чтение регистра Unique ID


       // команды защиты
       #define _SCMD_WRENABLE     0x06            // разрешение записи
       #define _SCMD_WRDISABLE    0x04            // разрешение записи
       #define _SCMD_WRENVOL      0x50            // разрешение записи (только для статусных регистров, без сохранения)
       #define _SCMD_SECRGERS     0x44            // стирание регистра защиты
       #define _SCMD_SECRGWRT     0x42            // запись регистра защиты
       #define _SCMD_SECRGRD      0x48            // чтение регистра защиты
       #define _SCMD_GLBLLK       0x7E            // глобальная блокировка блоков
       #define _SCMD_GLBLUNLK     0x98            // глобальная разблокировка блоков
       #define _SCMD_BLKLKRD      0x3D            // чтение регистра индивидуальной блокировки сектора/блока
       #define _SCMD_BLKLOCK      0x36            // индивидуальная блокировка сектора/блока
       #define _SCMD_BLKULOCK     0x39            // индивидуальная разблокировка сектора/блока
       #define _SCMD_ERPGSUSP     0x75            // заморозить процесс стирания/записи
       #define _SCMD_ERPGRESM     0x7A            // разморозить процесс стирания/записи

#endif
#if defined(W25Q16JV_APILEVEL) || defined(W25Q16JV_LOWLEVEL)

     // ##__##__##__##__##__##__##__##__##__##__##__##__##__##__##__##
     // ##                                                          ##
     // ##                   максимальные времена                   ##
     // ##                                                          ##
     // ##__##__##__##__##__##__##__##__##__##__##__##__##__##__##__##
     //
     // из колонки MAX таблицы временных характеристик чипа
       #define _TIME_CSSET_us     1               // время на установление CS в 1
       #define _TIME_CSCLR_us     1               // время на установление CS в 0
       #define _TIME_CSHLD_us     1               // минимальное время CS в неактивном состоянии м/у командами
       #define _TIME_EDPDM_us     3               // вход в спящий режим
       #define _TIME_RDPDM_us     4               // выход из спящего режима

       #define _TIME_RSTRC_us     30              // время восстановления после сброса
       #define _TIME_RSTWD_us     10              // время удержания сигнала сброса
       #define _TIME_STRUP_us     20              // время готовности после подачи питания (но чип все еще не готов записывать данные)

       #define _TIME_PGPRG_ms     3               // программирование страницы
       #define _TIME_STPRG_ms     15              // программирование статусного регистра
       #define _TIME_START_ms     7               // ожидание запуска чипа до полной готовности
       #define _TIME_COOLDOWN_ms  20              // ожидание "остывания" чипа после выключения питания
       #define _TIME_PGERS_ms     400             // стирание страницы  (=блока 4KB)
       #define _TIME_BKERS_ms     400             // стирание блока (=страницы 4KB)
       #define _TIME_SCERS_ms     2000            // стирание сектора (64KB)

       #define _TIME_FLASHAPI_TIMEOUT  10         // стандартный таймаут ожидания API-функций
#endif

#if defined(W25Q16JV_LOWLEVEL) || defined(W25Q16JV_APILEVEL)
     // ##__##__##__##__##__##__##__##__##__##__##__##__##__##__##__##
     // ##                                                          ##
     // ##                     ДРУГИЕ ОПРЕДЕЛЕНИЯ                   ##
     // ##                                                          ##
     // ##__##__##__##__##__##__##__##__##__##__##__##__##__##__##__##

/* <CONFIG> */

      #include "drivers\flash\w25q\config\W25Q16JV_CONF.h"

/* </CONFIG> */
     // чтение идентификатора
       #define __FLASH_WINBOND_ID                0xEF    // индентификатор фирмы WINBOND в поле Manufacturer ID
       #define __FLASH_INVALID_ID                0xFF    // неверно прочитанный идентификатор Manufacturer ID

       #define __FLASH_DENSITY_16MB              0x15    // идентификатор объема накопителя 16МБит
       #define __FLASH_MEMORYID_Q                0x40    // идентификатор типа чипа W25Q16JV-IQ/JQ
       #define __FLASH_MEMORYID_M                0x70    // идентификатор типа чипа W25Q16JV-IM*/JM*
#endif

#if defined(W25Q16JV_APILEVEL) || defined(W25Q16JV_LOWLEVEL)
     // регистр защиты - байты защиты
       #define __FLASH_PROTECTION_REG_ENABLE     0xFF    // байт регистра защиты - сектор защищен
       #define __FLASH_PROTECTION_REG_DISABLE    0x00    // байт регистра защиты - сектор НЕ защищен
#endif

#ifdef W25Q16JV_LOWLEVEL

     // оперативная память
       #define __FLASH_LL_COMMAND_MAXSIZE  8    // максимальное количество байт, выделяемое для буфера команд
                                                // максимальное количество байт, выделяемое для буфера команд + буфера страницы

       #define __FLASH_LL_SVCMEM_SIZE      ( __FLASH_LL_COMMAND_MAXSIZE + __SECTORS_FULL ) // alloc_place содержит protectbuffer[] байт
#endif

#if defined(W25Q16JV_APILEVEL) || defined(W25Q16JV_LOWLEVEL)
     // регистр безопасности
       #define __FLASH_USER_SECURITY_BYTES       256      // количество байт, доступных для программирования в регистре безопасности
#endif

     // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
     // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ структуры и типы №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
     // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
     //
#if defined(W25Q16JV_LOWLEVEL) || defined(W25Q16JV_APILEVEL)
     //-------------------------------------------
     #pragma pack( push, 1 )
     // РЕГИСТРЫ СТАТУСА
     union __flash_status_register_st_1 { // [W25Q OK]
     struct
     {
          __FLASH_BYTE    bBusy     :  1;    // BUSY бит занятости BUSY: занят(1)/свободен(0), WIP (Write In Progress)
          __FLASH_BYTE    bWrEnLatch:  1;    // WEL индикатор возможности записи после Write Enable Instruction: (1) разрешена / (0) запрещена
          __FLASH_BYTE    cBlkProt  :  3;    // BP индикаторы и установки групповой защиты блоков
          __FLASH_BYTE    bTopBot   :  1;    // TB бит переключения групповой защиты с начала (bTopBot=0) или конца (bTopBot=1) памяти
          __FLASH_BYTE    bSectBlk  :  1;    // SEC бит переключения групповой защиты на блоки 4КБ (bSectBlk=1) или 64КБ секторы (bSectBlk=0) [sic!]
          __FLASH_BYTE    bStatProt :  1;    // SRP бит аппаратной защиты статусного регистра: (1) - запись возможна если аппаратный сигнал ~WP=0 (asserted), (0) - без защиты
     };
          __FLASH_BYTE    status;
     };
     union __flash_status_register_st_2 { // [W25Q OK]
     struct
     {
          __FLASH_BYTE    bStatLock :  1;    // SRL блокировка стаусного регистра: (1) - заблокировать до следующего цикла подачи питания, (0) - нет блокировки
          __FLASH_BYTE    bQuadEn   :  1;    // разрешение режима Quad SPI (обмен по 4 линиям IO). (1) - разрешен, (0) - запрещен
          __FLASH_BYTE    bRes1     :  1;    // зарезервировано, RO, =0
          __FLASH_BYTE    bLockSec1 :  1;    // LB1 бит защиты Security Register 1: блокировка записи SR1, OTP, не снимается!
          __FLASH_BYTE    bLockSec2 :  1;    // LB2 бит защиты Security Register 2: блокировка записи SR2, OTP, не снимается!
          __FLASH_BYTE    bLockSec3 :  1;    // LB3 бит защиты Security Register 3: блокировка записи SR3, OTP, не снимается!
          __FLASH_BYTE    bCmpProt  :  1;    // CMP бит комплиментарности групповой защиты: позволяет проинвертировать признаки групповой защиты блоков, CMP=0 - нет инверсии
          __FLASH_BYTE    bSuspended:  1;    // SYS индикатор Suspend State, SYS=1 если одна из команд записи/стирания находится на паузе
     };
          __FLASH_BYTE    status;
     };
     union __flash_status_register_st_3 { // [W25Q OK]
     struct
     {
          __FLASH_BYTE    cRes_0    :  2;    // зарезервировано, RO, =0
          __FLASH_BYTE    bWPSel    :  1;    // WPS выбор схемы защиты от записи, WPS=0 групповая защита {CMP, SEC, TB, BP[2:0]}, WPS=1 индивидуальная защита секторов/блоков
          __FLASH_BYTE    cRes_1    :  2;    // зарезервировано, RO, =0
          __FLASH_BYTE    cDrvStr   :  2;    // DRV настройка тока выходного драйвера: 00 = 100%, 11 = 25% (по умолчанию)
          __FLASH_BYTE    bRes      :  1;    // зарезервировано, RO, =0
     };
          __FLASH_BYTE    status;
     };
     #define __FLASH_WPS_GROUP_PROTECT       0   // bWPSel=0, групповая защита секторов
     #define __FLASH_WPS_INDIVIDUAL_PROTECT  1   // bWPSel=1, индивидуальная защита секторов
     #define __FLASH_DRS_DEFAULT             3   // cDrvStr=11, Driver Strength = 25%
     #define __FLASH_SRP_WP_ENABLE           1   // bStatProt=1, разрешение аппаратного ~WP
     #define __FLASH_SRP_WP_DISABLE          0   // bStatProt=0, запрещение аппаратного ~WP
     #if W25QXXX_HW_WR_PROTECT
     #define __FLASH_SRP_WP_DEFAULT          __FLASH_SRP_WP_ENABLE
     #else
     #define __FLASH_SRP_WP_DEFAULT          __FLASH_SRP_WP_DISABLE
     #endif
     #define __FLASH_SRL_LOCKED              1   // bStatLock=1, запись в статусные регистры запрещена по следующего цикла включения питания
     #define __FLASH_SRL_UNLOCKED            0   // bStatLock=0, запись в статусные регистры разрешена
     #define __FLASH_QUAD_ENABLED            1   // bQuadEn=1, разрешен режим Quad SPI
     #define __FLASH_QUAD_DISABLED           0   // bQuadEn=0, запрещен режим Quad SPI
     #define __FLASH_SYS_ENABLED             1   // bSuspended=1, задействован режим suspend
     #define __FLASH_BP_RESET                0   // cBlkProt=0, все индикаторы групповой защиты сброшены
     #define __FLASH_BP_SET                  7   // cBlkProt=7, все индикаторы групповой защиты установлены
     #define __FLASH_CMP_ENABLE              1   // bCmpProt=1, режим комплиментарной защиты включен
     #define __FLASH_CMP_DISABLE             0   // bCmpProt=0, режим комплиментарной защиты выключен

     #pragma pack( pop )
     typedef union __flash_status_register_st_1 __flash_status1_t;
     typedef union __flash_status_register_st_2 __flash_status2_t;
     typedef union __flash_status_register_st_3 __flash_status3_t;

     //-------------------------------------------
     #pragma pack( push, 1 )
     // ИНДЕТИФИКАТОР ПРОИЗВОДИТЕЛЯ
     struct __flash_deviceid_st // [W25Q OK]
     {
          __FLASH_BYTE    Family;            // семейство памяти, в зависимости от модели
          __FLASH_BYTE    Density : 6;       // вместимость чипа, для линейки W25Q: значение [10h ... 20h]
          __FLASH_BYTE    Reserved: 2;       //          
     };
     typedef struct __flash_deviceid_st flash_deviceid_t;

     struct __flash_manufacturerid_st // [W25Q OK]
     {
       union {
       struct {
          __FLASH_BYTE          LowId;       // стандартный байт ID (не расширенный)
          __FLASH_BYTE       Reserved;       // зарезервировано, всегда 0.
                                             // Поле использовалось под расширенный байт ID, Continuation Code, JEDEC JEP-106,
                                             // но семейство W25Q не поддерживает подобный формат.
       };
          __FLASH_WORD         FullId;       // полный ID
       };
          __FLASH_WORD          DevId;       // код устройства [flash_deviceid_t]

          // W25Q не поддерживает расширение поля ID в соответствии с JEDEC JEP-106,
          // однако поддерживает другой стандарт Serial Flash Discoverable Parameter Structure (JESD216D.01)
          // Поля ExtLen и pExtBf будут использованы для выдачи информации о чипе в этом формате
          __FLASH_BYTE         ExtLen;       // длинна расширенной информации
          __FLASH_BYTE *       pExtBf;       // указатель на буфер приемник расширенной информации
     };

     struct __flash_manufacturerid_ex_st // [W25Q OK]
     {
       union {
       struct {
          __FLASH_BYTE          LowId;       // стандартный байт ID (не расширенный)
          __FLASH_BYTE       Reserved;       // зарезервировано, всегда 0.
                                             // Поле использовалось под расширенный байт ID, Continuation Code, JEDEC JEP-106,
                                             // но семейство W25Q не поддерживает подобный формат.
       };
          __FLASH_WORD         FullId;       // полный ID
       };
          flash_deviceid_t      DevId;       // код устройства

          // W25Q не поддерживает расширение поля ID в соответствии с JEDEC JEP-106,
          // однако поддерживает другой стандарт Serial Flash Discoverable Parameter Structure (JESD216D.01)
          // Поля ExtLen и pExtBf будут использованы для выдачи информации о чипе в этом формате
          __FLASH_BYTE         ExtLen;       // длинна расширенной информации
          __FLASH_BYTE *       pExtBf;       // указатель на буфер приемник расширенной информации
     };

     struct __flash_manufacturerid_returnvalue_st // [W25Q OK]
     {
       union {
       struct {
          __FLASH_BYTE          LowId;       // стандартный байт ID (не расширенный)
          __FLASH_BYTE         HighId;       // зарезервировано, всегда 0.
       };
          __FLASH_WORD         FullId;       // полный ID
       };

         flash_deviceid_t      DevId;       // код устройства
     };

     typedef struct __flash_manufacturerid_returnvalue_st __flash_rvid_t;
     typedef struct __flash_manufacturerid_st             __flash_id_t;
     typedef struct __flash_manufacturerid_ex_st          __flash_exid_t;

     #pragma pack( pop )
     
     #pragma pack( push, 1 )
     // РЕГИСТРЫ ЗАЩИТЫ/БЛОКИРОВКИ СЕКТОРОВ/БЛОКОВ
     struct __flash_protect_register_st            // [W25Q OK]
     {
          // байты защиты секторов 0-62 (__FLASH_PROTECTION_REG_ENABLE / __FLASH_PROTECTION_REG_DISABLE )
          __FLASH_BYTE          bSectors[ BITBUFFER_SIZE(__PROT_SECTOR_FLAGS) ];
     };
     typedef struct __flash_protect_register_st __flash_protectionregister_t;

     struct __flash_protect_sector_register_st    // [W25Q OK]
     {
          // байты защиты секторов 0-31 (__FLASH_PROTECTION_REG_ENABLE / __FLASH_PROTECTION_REG_DISABLE )
          // Группировка по секторам 64К !
          __FLASH_BYTE          bSectors[ BITBUFFER_SIZE(__SECTORS) ];
     };
     typedef struct __flash_protect_sector_register_st __flash_sectorprotectionregister_t;
     #pragma pack( pop )

     #define FLASH_PROTECTION_REGISTER_CHECK(bSectors,n)    BITBUFFER_CHECK(bSectors,n)
     #define FLASH_PROTECTION_REGISTER_SET(  bSectors,n)    BITBUFFER_SET(  bSectors,n)
     #define FLASH_PROTECTION_REGISTER_CLEAR(bSectors,n)    BITBUFFER_CLEAR(bSectors,n)
     //-------------------------------------------
     // РЕГИСТРЫ БЕЗОПАСНОСТИ ПОЛЬЗОВАТЕЛЯ
     #pragma pack( push, 1 )
     struct __flash_security_register_st // [W25Q OK]
     {
            __FLASH_BYTE       content[ __FLASH_USER_SECURITY_BYTES ];      // доступные для однократного программирования байты в регистре безопасности
     };
     typedef struct __flash_security_register_st     __flash_securityregister_t;
     #pragma pack( pop )
     //-------------------------------------------
     // РЕГИСТРЫ ИДЕНТИФИКАТОРОВ
     #pragma pack( push, 1 )
     struct __flash_uniqueid_register_st // [W25Q OK]
     {
            __FLASH_BYTE uid[8];  // 8-байтовый уникальный идентификатор в пределах модели
     };
     typedef struct __flash_uniqueid_register_st     __flash_uniqueidregister_t;
     #pragma pack( pop )
     //-------------------------------------------
     // ВНУТРЕННИЕ СЛУЖЕБНЫЕ СТРУКТУРЫ И ТИПЫ
     typedef const __FLASH_BYTE (*__flash_pageprogramptr_t)[ __PROG_PAGE_SIZE ];
     typedef enum
     {
          fibufSectorFlags,                // буфер флагов защиты секторов 64К
          fibufSectorFlags_2,              // буфер флагов защиты секторов 64К (еще один буфер)
          fibufProtSectorFlags,            // буфер флагов защиты секторов/блоков (protSector)
          fibufPageIO,                     // буфер ввода/вывода страницы
     }
     __flash_internal_buffer_type_t;
     // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
     // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ преобразования адресов №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
     // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
     //
     #if defined(W25Q16JV_LOWLEVEL) || defined(W25Q16JV_APILEVEL)

       #define __FLASH_ADDRESS2SECTOR(addr)       ((addr)/__SECTOR_STD_SIZE)   // __FLASH_ADDRESS2SECTOR - преобразует адрес в номер сектора с учетом используемого размера страницы
       #define __FLASH_ADDRESS2PAGE(addr)         ((addr)/__PAGE_SIZE_USER)    // __FLASH_ADDRESS2PAGE - преобразует адрес в номер страницы с учетом используемого размера страницы
       #define __FLASH_PAGE2ADDRESS(page)         ((page)*__PAGE_SIZE_USER)    // __FLASH_PAGE2ADDRESS - преобразует номер страницы в адрес с учетом используемого размера страницы 
       #define __FLASH_PAGE2BLOCK(page)           ((page)/__PAGES_PER_BLOCK)   // __FLASH_PAGE2BLOCK - преобразует номер страницы в номер блока
       #define __FLASH_PAGE2SECTOR(page)          ((page)/__PAGES_PER_SECTOR)  // __FLASH_PAGE2SECTOR - преобразует номер страницы в номер сектора
       #define __FLASH_ADDRESSOFFSETPAGE(addr)    ((addr)%__PAGE_SIZE_USER)    // __FLASH_ADDRESSOFFSETPAGE - получает смещение адреса относительно начала страницы
                                                                               // __FLASH_DATAREMAININGINPAGE - вычисляет, какое количество байт осталось до конца страницы
       #define __FLASH_DATAREMAININGINPAGE(addr)  ( __PAGE_SIZE_USER - __FLASH_ADDRESSOFFSETPAGE( address ) )

       // В линейке чипов Winbind серии W25Q существуют чипы емкостью от 1МБит до 512МБит
       // Код емкости: 10h - 512KBit, 11h - 1Mbit, 12h - 2Mbit, ..., 19h - 256MBit, и 20h - 512MBit
       // Чип емкостью 512МБит выбивается из общего правила, т.к. имеет код не 1Ah, а 20h.
       // __W25Q_density
       // Возвращает емкость в Мегабитах
       static inline int __W25Q_density(int density)
       {
           if( 0x11 <= density && density <= 0x19 )
             return (1 << ((density & 0xF) - 1));

           switch( density )
           {
              case 0x10: return 0; // 512KBit
              case 0x20: return 512; // 512MBit
           }

           return 0; // undefined
       }
       #define __FLASH_DENSITY2CAPACITY(density)  __W25Q_density(density)

       // __FLASH_PAGESINRANGE - вычисляет, какое количество страниц находится на промежутке длинной size начиная с адреса address
       #define __FLASH_PAGESINRANGE(address,size) \
                                              ((address+size)/__PAGE_SIZE_USER - \
                                              (address)/__PAGE_SIZE_USER + \
                                              (((address+size)%__PAGE_SIZE_USER)?1:0))  //

       // __FLASH_PAGE_ARRAGED_BY_BLOCK - проверяет, выровнена ли страница на размер блока
       #define __FLASH_PAGE_ARRAGED_BY_BLOCK(pg)   (!( pg % W25QFLASH_PAGES_PER_BLOCK ))

       // __FLASH_PAGE_ARRAGED_BY_SECTOR - проверяет, выровнена ли страница на размер сектора
       #define __FLASH_PAGE_ARRAGED_BY_SECTOR(pg)   (!( pg % W25QFLASH_PAGES_PER_SECTOR ))
     #endif
     // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
     // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ экпортируемые макросы №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
     // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№
     //
     // для экспорта функций используйте заголовочный файл "W25Q16JV_LL_func.h"

     #define W25QFLASH_WINBOND_ID       __FLASH_WINBOND_ID        // Winbond' Manufacturer ID
     #define W25QFLASH_DENSITY_16MB     __FLASH_DENSITY_16MB      // Winbond' Density for 16Mbit
     #define W25QFLASH_FAMILYDATAFLASHQ __FLASH_MEMORYID_Q        // Winbond' Family code
     #define W25QFLASH_FAMILYDATAFLASHM __FLASH_MEMORYID_M        // Winbond' Family code
     #define W25QFLASH_CHIP_SIZE        __FULL_CHIP_SIZE          // полный объем чипа
     #define W25QFLASH_DATA_SIZE        __FULL_DATA_SIZE          // полный объем данных (с учетом или без учета сервисных байтов)
     #define W25QFLASH_SECTORS          __SECTORS                 // количество секторов
     #define W25QFLASH_PAGE_SIZE        __PAGE_SIZE_USER          // размер страницы (зависит от использования сервисных секторов)
     #define W25QFLASH_BLOCKS           __BLOCKS                  // количество блоков
     #define W25QFLASH_BLOCK_SIZE       __BLOCK_SIZE_USER         // размер блока    (зависит от использования сервисных секторов)
     #define W25QFLASH_PAGES            __FULL_PAGES              // количество страниц
     #define W25QFLASH_BLOCKS_PER_SECTOR __BLOCKS_PER_SECTOR      // количество блоков в секторе
     #define W25QFLASH_PAGES_PER_SECTOR __PAGES_PER_SECTOR        // количество страниц в стандартном секторе
     #define W25QFLASH_PAGES_PER_BLOCK  __PAGES_PER_BLOCK

     //-------------------------------------------
     // размеры секторов
     #define W25QFLASH_SECTOR0_SIZE    __SECTOR0_SIZE
     #define W25QFLASH_SECTOR1_SIZE    __SECTOR1_SIZE
     #define W25QFLASH_SECTOR2_SIZE    __SECTOR2_SIZE
     #define W25QFLASH_SECTOR3_SIZE    __SECTOR3_SIZE
     #define W25QFLASH_SECTOR4_SIZE    __SECTOR4_SIZE
     #define W25QFLASH_SECTOR5_SIZE    __SECTOR5_SIZE
     #define W25QFLASH_SECTOR6_SIZE    __SECTOR6_SIZE
     #define W25QFLASH_SECTOR7_SIZE    __SECTOR7_SIZE
     #define W25QFLASH_SECTOR8_SIZE    __SECTOR8_SIZE
     #define W25QFLASH_SECTOR9_SIZE    __SECTOR9_SIZE
     #define W25QFLASH_SECTOR10_SIZE   __SECTOR10_SIZE
     #define W25QFLASH_SECTOR11_SIZE   __SECTOR11_SIZE
     #define W25QFLASH_SECTOR12_SIZE   __SECTOR12_SIZE
     #define W25QFLASH_SECTOR13_SIZE   __SECTOR13_SIZE
     #define W25QFLASH_SECTOR14_SIZE   __SECTOR14_SIZE
     #define W25QFLASH_SECTOR15_SIZE   __SECTOR15_SIZE
     #define W25QFLASH_SECTOR16_SIZE   __SECTOR16_SIZE
     #define W25QFLASH_SECTOR17_SIZE   __SECTOR17_SIZE
     #define W25QFLASH_SECTOR18_SIZE   __SECTOR18_SIZE
     #define W25QFLASH_SECTOR19_SIZE   __SECTOR19_SIZE
     #define W25QFLASH_SECTOR20_SIZE   __SECTOR20_SIZE
     #define W25QFLASH_SECTOR21_SIZE   __SECTOR21_SIZE
     #define W25QFLASH_SECTOR22_SIZE   __SECTOR22_SIZE
     #define W25QFLASH_SECTOR23_SIZE   __SECTOR23_SIZE
     #define W25QFLASH_SECTOR24_SIZE   __SECTOR24_SIZE
     #define W25QFLASH_SECTOR25_SIZE   __SECTOR25_SIZE
     #define W25QFLASH_SECTOR26_SIZE   __SECTOR26_SIZE
     #define W25QFLASH_SECTOR27_SIZE   __SECTOR27_SIZE
     #define W25QFLASH_SECTOR28_SIZE   __SECTOR28_SIZE
     #define W25QFLASH_SECTOR29_SIZE   __SECTOR29_SIZE
     #define W25QFLASH_SECTOR30_SIZE   __SECTOR30_SIZE
     #define W25QFLASH_SECTOR31_SIZE   __SECTOR31_SIZE

     // количество циклов перезаписи регистра статуса
     #define W25QFLASH_STATREG_WRITECOUNT  __FLASH_STATUSREGISTER_WRITECYCLES

     // количество циклов перезаписи страницы 4К
     #define W25QFLASH_PAGE_WRITECOUNT  __FLASH_PAGE_WRITECYCLES

     // зарезервированная область в начале адресного пространства
     #define W25QFLASH_MINIMUM_ADDRESS  (0x20000)   // указано 128K для совместимости с AT45, но если не требуется => указать 0

     // максимально возможный адрес
     #define W25QFLASH_MAXIMUM_ADDRESS  (W25QFLASH_PAGES * __PAGE_SIZE_USER )
     //-------------------------------------------
#endif
#endif

