// Файл с API функциями работы с FLASH-чипом W25Q16JV // v 1.0 от 14/10/20 // Автор: Сычев А. #define W25Q16JV_API_C #define W25Q16JV_APILEVEL // использование интерфейса низкоуровневых функций #include "drivers\flash\w25q\config\W25Q16JV_CONF.h" #include "drivers\flash\base\flash_api_error.h" #include "drivers\flash\base\flash_api_types.h" #include "drivers\flash\base\flash_api_base.h" #include "drivers\flash\w25q\lowlevel\W25Q16JV_GLOB.h" #include "drivers\flash\w25q\lowlevel\W25Q16JV_LL_func.h" #include "drivers\flash\w25q\common\W25Q_HAL.h" // внутренний дескриптор LL-буфера typedef struct { union { void * rawptr; __flash_sectorprotectionregister_t * pSectorProtectionRegister; __FLASH_BYTE * pageBuffer; }; size_t size; } flash_LL_buf_t; static const char W25QFLASH_TEXTUAL_DESCRIPTION[] = "W25Q16JV"; // дескриптор конфигурации памяти static const flash_properties_t FlashProperties_W25Q16JV = { .minAddress = W25QFLASH_MINIMUM_ADDRESS, .maxAddress = W25QFLASH_MAXIMUM_ADDRESS, .maxSectors = W25QFLASH_SECTORS, .sectorSize = (W25QFLASH_PAGES_PER_SECTOR * W25QFLASH_PAGE_SIZE), .pChipDescription = W25QFLASH_TEXTUAL_DESCRIPTION }; // flash_api_initialize - инициализация драйвера и API работы с устройством static flash_err_t flash_api_initialize( bool detectOnly ); // flash_api_getready - проверка готовности устройства (со стандартным таймаутом) static flash_err_t flash_api_getready(); // flash_api_service - функция получения общих сведений об флеш-памяти. static flash_err_t flash_api_service( flash_service_info_t si, void * pdata, __FLASH_WORD * pbufsize ); // flash_api_protect - установить защиту секторов static flash_err_t flash_api_protect( flash_api_protect_t * content ); // flash_api_unprotect - снять защиту секторов static flash_err_t flash_api_unprotect( flash_api_protect_t * content ); // flash_api_protect_ex - установить/снять защиту секторов static flash_err_t flash_api_protect_ex( flash_api_protect_t * content, flash_api_protect_bits mode ); // flash_api_getprotect - получить информацию о защищенных секторах. static flash_err_t flash_api_getprotect( flash_api_getprotect_t * content ); // flash_api_erase - мультирежимное стирание данных static flash_err_t flash_api_erase( flash_erase_mode_t mode, __FLASH_DWORD start, __FLASH_DWORD count ); // flash_api_write - мультирежимная запись данных по адресу static flash_err_t flash_api_write( flash_address_t address, __FLASH_BYTE * pBuffer, flash_address_t size, flash_write_mode_t mode ); // flash_api_read - чтение данных по адресу static flash_err_t flash_api_read( flash_address_t address, __FLASH_BYTE * pBuffer, flash_address_t size ); // flash_api_sleepmode - переход в режим пониженного энергопотребления static flash_err_t flash_api_sleepmode(); // flash_api_wakeup - выход из режима пониженного энергопотребления static flash_err_t flash_api_wakeup(); // flash_api_finalize - деинициализация драйвера static flash_err_t flash_api_finalize( bool bHibernate, bool bForce ); // дескриптор API const flash_api_descriptor_t W25Q16JV_API = { .routines = { .flashInitialize = flash_api_initialize, .flashGetReady = flash_api_getready, .flashService = flash_api_service, .flashProtect = flash_api_protect, .flashUnprotect = flash_api_unprotect, .flashProtectEx = flash_api_protect_ex, .flashGetProtect = flash_api_getprotect, .flashErase = flash_api_erase, .flashWrite = flash_api_write, .flashRead = flash_api_read, .flashSleepMode = flash_api_sleepmode, .flashWakeup = flash_api_wakeup, .flashFinalize = flash_api_finalize, }, .flashProperties = &FlashProperties_W25Q16JV }; // Enter() и Return(.) - макросы блокировки ресурса в многозадачных ОС // При входе в критический участок кода - выполнение API функции - вызывается Enter(), // блокируя ресурс, при выходе - Leave() или Return(.) ( для выхода из функции ) #define Enter() { __FLASH_LOCK(); } #define Leave() { __FLASH_UNLOCK(); } #define Return(code) { __FLASH_UNLOCK(); return (code); } //#define ret __FLASH_UNLOCK(); return // Done: // flash_api_initialize(...) // Высокоуровневая инициализация памяти (установка размера страницы, установка параметров защиты секторов...) // flash_api_erase(...) // стирание памяти (блоками, страницами или секторами) // flash_api_read(...) // чтение памяти по адресу // flash_api_protect(...) // защита секторов (модификация конфигурации защищаемых секторов) // flash_api_unprotect(...) // снятие защиты (модификация конфигурации защищаемых секторов) // flash_api_service(...) // получение информации о производителе, расширенной информации, размере памяти, размере страницы, количестве секторов, блоков, страниц и т.п. // flash_api_write(...) // запись памяти по адресу // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ // flash_api_getready - проверка готовности устройства (со стандартным таймаутом) // // # Функция возвращает FLERR_SUCCESS, еслиу устройство готово, иначе FLERR_TIMEOUT static flash_err_t flash_api_getready() // [W25Q OK] { Enter(); if( W25Q_LL_Routines.ready.smartWaitms( W25QXXX_API_TIMEOUT ) ) { Return(FLERR_SUCCESS); } Return(FLERR_TIMEOUT); } static flash_err_t flash_getready_time( __FLASH_WORD timeout ) // [W25Q OK] { Enter(); if( W25Q_LL_Routines.ready.smartWaitms( timeout ) ) { Return(FLERR_SUCCESS); } Return(FLERR_TIMEOUT); } // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ // flash_modeSetup - Настройка общих параметров работы на этапе инициализации // - настройка режима SPI (Отключение Quad-режима) // - сброс режима комплиментарности групповой защиты (фактически выключение групповой защиты в купе со сбросом индикаторов BP) // - проверка блокировки статусных регистров // - проверка отсутствия режима suspend // - инициализация режима индивидуальной защиты секторов // - режим групповой защиты секторов будет отключен, т.к. он не удовлетворяет API // - настройка тока выходного драйвера // - настройка вывода управления ~WP: включение/отключение защиты по ~WP в зависимости от W25QXXX_HW_WR_PROTECT // - сброс индикаторов групповой защиты (BP) // * Достигается состояние, при котором при выборе режима групповой защиты (WPS=0) защита секторов выключается, что // позволяет использовать WPS как общий бит защиты секторов: при WPS=0 защита выключена, при WPS=1 включена индивидуально // # Функция возвращает FLERR_SUCCESS. Только в этом случае гарантируется нормальная работа. // # Функция возвращает ошибку инициализации при появлении сбоев или при непрохождении проверки static flash_err_t flash_modeSetup() // [W25Q OK] { flash_err_t success = FLERR_SUCCESS; // ------------------------------------------------------------------------- // - настройка режима SPI (Отключение Quad-режима) // - проверка блокировки статусных регистров // - проверка отсутствия режима suspend { __flash_status2_t status; __flash_status2_t status_bak; // чтение текущего режима W25Q_LL_Routines.status.statusRead2( &status ); // копируем состояние регистра для последующей сверки status_bak.status = status.status; // проверка блокировки регистров if( __FLASH_SRL_LOCKED == status.bStatLock ) { // запись в статусные регистры запрещена по следующего цикла включения питания #if W25QXXX_POWER_MANAGEMENT // если разрешено управление питанием, пробуем разрешить проблему циклом подачи питания W25Q_LL_Routines.powerPulse(); // повторное чтение регистра W25Q_LL_Routines.status.statusRead2( &status ); // повторная проверка if( __FLASH_SRL_LOCKED == status.bStatLock ) { // если без изменений - результат: ошибка #endif // ошибка настройки: регистры блокированы success = FLERR_DEVICE_MALFUNCTION; return(success); #if W25QXXX_POWER_MANAGEMENT } #endif } // проверка режима suspend if( __FLASH_SYS_ENABLED == status.bSuspended ) { // задействован режим suspend, нужно сбросить #if W25QXXX_POWER_MANAGEMENT // если разрешено управление питанием, пробуем разрешить проблему циклом подачи питания W25Q_LL_Routines.powerPulse(); // повторное чтение регистра W25Q_LL_Routines.status.statusRead2( &status ); // повторная проверка if( __FLASH_SYS_ENABLED == status.bSuspended ) { // если без изменений - результат: ошибка #endif // ошибка настройки: регистры блокированы success = FLERR_DEVICE_MALFUNCTION; return(success); #if W25QXXX_POWER_MANAGEMENT } #endif } // отключение режима Quad, если возможно. // На чипах IQ и JQ (W25QFLASH_FAMILYDATAFLASHQ) режим Quad заблокирован от выключения. __FLASH_DWORD flashID = W25Q_LL_Routines.id.manufacturerIdRead(0); if( W25QFLASH_FAMILYDATAFLASHQ != ((__flash_rvid_t*) &flashID)->DevId.Family ) { // проверка режима Quad if( __FLASH_QUAD_ENABLED == status.bQuadEn ) { // отключаем режим QUAD status.bQuadEn = __FLASH_QUAD_DISABLED; } } // сброс режима комплиментарности групповой защиты (фактически выключение групповой защиты) if( __FLASH_QUAD_ENABLED == status.bCmpProt ) { // отключаем режим комплиментарности status.bCmpProt = __FLASH_CMP_DISABLE; } // проверка, есть ли измененные настройки, которые нужно записать? if( status_bak.status ^ status.status ) { // включение разрешения инструкций записи W25Q_LL_Routines.operations.writeEnable(); // запись регистра (сохранение параметров режима защиты) W25Q_LL_Routines.status.statusWrite2( &status ); // проверка результата: повторное чтение W25Q_LL_Routines.status.statusRead2( &status_bak ); } // проверка установленных параметров if( status_bak.status ^ status.status ) { // ошибка настройки: не удалось записать требуемые настройки success = FLERR_DEVICE_MALFUNCTION; return(success); } } // ------------------------------------------------------------------------- // - инициализация режима индивидуальной защиты секторов // - режим групповой защиты секторов будет отключен, т.к. он не удовлетворяет API // - настройка тока выходного драйвера { __flash_status3_t status; __flash_status3_t status_bak; // чтение текущего режима W25Q_LL_Routines.status.statusRead3( &status ); // копируем состояние регистра для последующей сверки status_bak.status = status.status; if( __FLASH_WPS_GROUP_PROTECT == status.bWPSel ) { // выбран групповой режим защиты: вЫключаем его status.bWPSel = __FLASH_WPS_INDIVIDUAL_PROTECT; // переключение режима защиты от записи: индивидуальная защита } if( __FLASH_DRS_DEFAULT != status.cDrvStr ) { // выбрано неверное значение тока ключа status.cDrvStr = __FLASH_DRS_DEFAULT; // переключение выходного тока ключа } // проверка, есть ли измененные настройки, которые нужно записать? if( status_bak.status ^ status.status ) { // включение разрешения инструкций записи W25Q_LL_Routines.operations.writeEnable(); // запись регистра (сохранение параметров режима защиты) W25Q_LL_Routines.status.statusWrite3( &status ); // проверка результата: повторное чтение W25Q_LL_Routines.status.statusRead3( &status_bak ); } // проверка установленных параметров if( status_bak.status ^ status.status ) { // ошибка настройки: не удалось записать требуемые настройки success = FLERR_DEVICE_MALFUNCTION; return(success); } } // ------------------------------------------------------------------------- // - настройка вывода управления ~WP: включение/отключение защиты по ~WP в зависимости от W25QXXX_HW_WR_PROTECT // - сброс индикаторов групповой защиты { __flash_status1_t status; __flash_status1_t status_bak; // чтение текущего режима W25Q_LL_Routines.status.statusRead1( &status ); // копируем состояние регистра для последующей сверки status_bak.status = status.status; if( __FLASH_SRP_WP_DEFAULT != status.bStatProt ) { // Настройка выводв ~WP отличается от требуемой status.bStatProt = __FLASH_SRP_WP_DEFAULT; // переключение режима ~WP } if( __FLASH_BP_RESET != status.cBlkProt ) { // сброс индикаторов групповой защиты status.cBlkProt = __FLASH_BP_RESET; // переключение режима защиты } // проверка, есть ли измененные настройки, которые нужно записать? if( status_bak.status ^ status.status ) { // включение разрешения инструкций записи W25Q_LL_Routines.operations.writeEnable(); // запись регистра (сохранение параметров режима защиты) W25Q_LL_Routines.status.statusWrite1( &status ); // проверка результата: повторное чтение W25Q_LL_Routines.status.statusRead1( &status_bak ); } // проверка установленных параметров if( status_bak.status ^ status.status ) { // ошибка настройки: не удалось записать требуемые настройки success = FLERR_DEVICE_MALFUNCTION; return(success); } } return success; } // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ // flash_api_initialize - инициализация драйвера и API работы с устройством // * Если @detectOnly=true, производится только детектирование наличия чипа! // # Функция возвращает FLERR_SUCCESS. Только в этом случае гарантируется нормальная работа. // # Функция возвращает ошибку инициализации при появлении сбоев или при непрохождении проверки (см файл конфигурации AT45DB161E_CONF.h) static flash_err_t flash_api_initialize( bool detectOnly ) // [W25Q OK] { // производится определение наличия чипа на шине if( detectOnly ) { return W25Q_LL_Routines.service.startupDetect(); } // производим нисзкоуровневую инициализацию: подаем питание и будим, проверяем наличие чипа, серию, производителя, размер... flash_err_t success = W25Q_LL_Routines.service.initialize(); if( FLASH_ERROR(success) ) { // что-то пошло не так: ай печаль-печаль. return(success); } // ------------------------------------------------------------------------- // конфигурирование режима работы success = flash_modeSetup(); if( FLASH_ERROR(success) ) { // что-то пошло не так: ай печаль-печаль. return(success); } // ------------------------------------------------------------------------- // отключаем защиту секторов при инициализации (разрешение записи во все сектора) success = flash_api_unprotect( NULL ); if( FLASH_ERROR(success) ) { // что-то пошло не так: ай печаль-печаль. return(success); } // считываем статус if( ! W25Q_LL_Routines.ready.smartWaitms( W25QXXX_API_TIMEOUT ) ) { // что-то пошло не так: ай печаль-печаль. return(success); } return(FLERR_SUCCESS); } // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ // flash_api_finalize - безопасная де-инициализация драйвера // Функция завершает работу с флеш-памятью, отключаяя ее или переводя в режим гибернации // @bHibernate: булевый флаг, показывает, что нужно перевести флеш-память в режим гибернации, // если false - драйвер попытается отключить питание микросхемы, если разрешено управление питанием (AT45DBXXX_POWER_MANAGEMENT) // если false и управление питанием запрещено, будет возвращена ошибка FLERR_INVALID_PARAMETER. // @bForce: насильное выполнение финализации в случае, если флеш-память занята незаконченной операцией. // если false и драйвер детектирует незаконченную операцию, будет возвращена ошибка FLERR_UNEXPECTED_BUSY. // Если выбран режим гибернации, после выдачи команды функция проверяет, уснула ли микросхема через чтение статуса. // Если в этом случае обнаруживается, что статус все еще считывается, генерируется ошибка FLERR_GENERIC_ERROR. // Данную проверку можно отключить опцией W25QXXX_DONOTCHECK_HIBERNATE=1. // Внимание: проверка не реализуется если bHibernate=false. // # Функция возвращает FLERR_SUCCESS всегда, за исключением вышеописанных случаев. static flash_err_t flash_api_finalize( bool bHibernate, bool bForce ) // [W25Q OK] { Enter(); // подготовка к деинициализации flash_err_t status = W25Q_LL_Routines.service.finalizePrepare(); if( FLASH_ERROR(status) ) { // ошибка, обнаружена проблема - устройство не готово // проверяем, разрешено ли принудительное завершение? if( bForce ) { // разрешено, сначала производим сброс // доступно управление сигналом сброса? #if W25QXXX_RESET_MANAGEMENT // да, производим сброс чипа W25Q_LL_Routines.reset.resetPulse(); #else // нет, а доступно управление питанием? #if W25QXXX_POWER_MANAGEMENT // да, производим быстрый сброс чипа по питанию W25Q_LL_Routines.power.powerPulse( 0, 0 ); #else // нет, управление питанием тоже недоступно // возвращаем ошибку FLERR_INVALID_PARAMETER - невозможно выполнить принудительное отключение Return(FLERR_INVALID_PARAMETER); #endif #endif } else { // нет, принудительное завершение запрещено // возвращаем ошибку Return(status); } } // все нормально, продолжаем // выбран режим гибернации? if( bHibernate ) { // отправляем микросхему поспать W25Q_LL_Routines.sleep.sleepMode(); #if W25QXXX_DONOTCHECK_HIBERNATE == 0 // проверяем, ответит ли микросхема? if( W25Q_LL_Routines.ready.smartWaitms( W25QXXX_API_TIMEOUT ) ) { // отвечает - бит готовности прочитался... // Может это просто подтянутый сигнал к +VCC? // попробуем по другому: if( FLASH_SUCCESS(W25Q_LL_Routines.service.detect()) ) { // ответила! значит точно не ложилась спать - ошибка Return(FLERR_DEVICE_MALFUNCTION); } } #endif } else { // доступно управление питанием? #if W25QXXX_POWER_MANAGEMENT == 0 // нет, недоступно, нечего делать, генерируем ошибку Return(FLERR_INVALID_PARAMETER); #else // Доступно, отключаем питание. W25Q_LL_Routines.power_off(); // не проверяем доступность, доверяем функции отключения питания #endif } Return(FLERR_SUCCESS); } // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ // flash_api_service - функция получения общих сведений об флеш-памяти. // Полный список возможных запросов приведен ниже: // ЗАПРОС РАЗМЕР ТИП ТИП ИНФОРМАИИ // БУФЕРА ЗНАЧНИЯ // fsvc_getVendorId 2 __FLASH_WORD ID производителя // fsvc_getDeviceId 2 flash_deviceid_t ID устройства // fsvc_getExtInfo * __FLASH_BYTE[] расширенную информацию производителя // fsvc_getExtInfoLen 1 __FLASH_BYTE длинну расширенной информации производителя // fsvc_getCapacity 4 __FLASH_DWORD заявляемая по документации емкость устройства (MBit-s) // fsvc_getFullCapacity 4 __FLASH_DWORD полная емкость устройства (в байтах) // fsvc_getDataCapacity 4 __FLASH_DWORD доступная емкость устройства (в байтах) // fsvc_getProtect 1 __FLASH_BYTE информацию о защите секторов (общий бит защиты 0x00 или 0x01) // fsvc_getSectorsCount 2 __FLASH_WORD количество секторов // fsvc_getPagesCount 2 __FLASH_WORD количество страниц // fsvc_getBlocksCount 2 __FLASH_WORD количество блоков // fsvc_getPageSize 2 __FLASH_WORD размер страницы // fsvc_getReadyState 1 __FLASH_BYTE готовность устройства // fsvc_getMinAddress 4 __FLASH_DWORD минимально возможный линейный адрес // fsvc_getMaxAddress 4 __FLASH_DWORD максимально возможный линейный адрес // // * Параметр @si задает тип запроса (см. flash_service_info_t) // * Параметр @pdata должен указывать на заранее выделенный буфер памяти для сохранения // получаемых данных. Размер буфера зависит от типа запроса. Параметр не может быть NULL // * Параметр @pbufsize должен указывать на размер буфера, на который указывает pdata. // Параметр не может быть 0. Значение по указателю @pbufsize не может быть 0. // При успешном выполнении функции, по указателю @pbufsize будет записано фактическое количество // записанных в буфер данных. // // * Функция НЕ ОБНУЛЯЕТ память по указателю @pdata в случае, если передан слишком большой буфер. // // # Функция вернет ошибку FLERR_INVALID_PARAMETER при некорректном запросе @si // # Функция вернет ошибку FLERR_INVALID_PARAMETER если @pdata или @bufsize равны 0. // # Функция вернет ошибку FLERR_INVALID_BUFFER если размер буфера @size не достаточен для сохранения данных // // # Функция вернет FLERR_SUCCESS при отсутствии ошибок // static flash_err_t flash_api_service( flash_service_info_t si, void * pdata, __FLASH_WORD * pbufsize ) // [W25Q OK] { if( !pdata || !pbufsize || !*pbufsize ) return(FLERR_INVALID_PARAMETER); Enter(); switch( si ) { case fsvc_getVendorId: // ID производителя case fsvc_getDeviceId: // ID устройства case fsvc_getCapacity: // заявляемая по документации емкость устройства (MBit-s) case fsvc_getDataCapacity: // доступная емкость устройства case fsvc_getFullCapacity: // полная емкость устройства { __FLASH_DWORD flashID = W25Q_LL_Routines.id.manufacturerIdRead( 0 ); switch( si ) { case fsvc_getVendorId: if( sizeof( __FLASH_WORD ) > *pbufsize ) Return(FLERR_INVALID_BUFFER); *((__FLASH_WORD*)(pdata)) = ((__flash_rvid_t*) &flashID)->FullId; *pbufsize = sizeof(__FLASH_WORD); Return(FLERR_SUCCESS); //------------------------- case fsvc_getDeviceId: if( sizeof( __FLASH_WORD ) > *pbufsize ) Return(FLERR_INVALID_BUFFER); *((flash_deviceid_t*)(pdata)) = ((__flash_rvid_t*) &flashID)->DevId; *pbufsize = sizeof(__FLASH_WORD); Return(FLERR_SUCCESS); //------------------------- case fsvc_getCapacity: if( sizeof( __FLASH_DWORD ) > *pbufsize ) Return(FLERR_INVALID_BUFFER); *((__FLASH_DWORD*)(pdata)) = __FLASH_DENSITY2CAPACITY( ((flash_deviceid_t*)&((__flash_rvid_t*) &flashID)->DevId)->Density ); *pbufsize = sizeof(__FLASH_DWORD); Return(FLERR_SUCCESS); //------------------------- case fsvc_getDataCapacity: if( sizeof( __FLASH_DWORD ) > *pbufsize ) Return(FLERR_INVALID_BUFFER); *((__FLASH_DWORD*)(pdata)) = (W25QFLASH_MAXIMUM_ADDRESS - W25QFLASH_MINIMUM_ADDRESS); *pbufsize = sizeof(__FLASH_DWORD); Return(FLERR_SUCCESS); //------------------------- case fsvc_getFullCapacity: if( sizeof( __FLASH_DWORD ) > *pbufsize ) Return(FLERR_INVALID_BUFFER); *((__FLASH_DWORD*)(pdata)) = (W25QFLASH_MAXIMUM_ADDRESS); *pbufsize = sizeof(__FLASH_DWORD); Return(FLERR_SUCCESS); } } break; case fsvc_getMinAddress: { if( sizeof( __FLASH_DWORD ) > *pbufsize ) Return(FLERR_INVALID_BUFFER); *((__FLASH_DWORD*)(pdata)) = (W25QFLASH_MINIMUM_ADDRESS); *pbufsize = sizeof(__FLASH_DWORD); Return(FLERR_SUCCESS); } break; case fsvc_getMaxAddress: { if( sizeof( __FLASH_DWORD ) > *pbufsize ) Return(FLERR_INVALID_BUFFER); *((__FLASH_DWORD*)(pdata)) = (W25QFLASH_MAXIMUM_ADDRESS); *pbufsize = sizeof(__FLASH_DWORD); Return(FLERR_SUCCESS); } break; //------------------------- case fsvc_getExtInfoLen: // длинну расширенной информации производителя case fsvc_getExtInfo: // расширенную информацию производителя { __flash_id_t mid; switch( si ) { case fsvc_getExtInfoLen: if( sizeof( __FLASH_BYTE ) > *pbufsize ) Return(FLERR_INVALID_BUFFER); mid.pExtBf = 0; mid.ExtLen = 0; // получение ДЛИННЫ расширенной информации W25Q_LL_Routines.id.manufacturerIdRead( &mid ); *((__FLASH_BYTE*)(pdata)) = mid.ExtLen; *pbufsize = sizeof(__FLASH_BYTE); Return(FLERR_SUCCESS); case fsvc_getExtInfo: if( 1 * sizeof( __FLASH_BYTE ) > *pbufsize ) Return(FLERR_INVALID_BUFFER); mid.pExtBf = pdata; mid.ExtLen = *pbufsize; // получение расширенной информации W25Q_LL_Routines.id.manufacturerIdRead( &mid ); *pbufsize = mid.ExtLen; Return(FLERR_SUCCESS); } } Return(FLERR_INVALID_BUFFER); case fsvc_getReadyState: // готовность устройства { if( sizeof( __FLASH_BYTE ) <= *pbufsize ) { *((__FLASH_BYTE*)(pdata)) = ( FLASH_SUCCESS( W25Q_LL_Routines.ready.getReadyFast() ) )? 0x01: 0x00; *pbufsize = sizeof(__FLASH_BYTE); Return(FLERR_SUCCESS); } } Return(FLERR_INVALID_BUFFER); case fsvc_getProtect: // информацию о защите секторов { if( sizeof( __FLASH_BYTE ) <= *pbufsize ) { flash_api_getprotect_t content; flash_err_t status = flash_api_getprotect( &content ); if( FLASH_SUCCESS(status) ) { // любой из секторов защищен? if( content.sectors.hiprotect != 0 && content.sectors.loprotect != 0 ) { // как минимум один из секторов защищен *pbufsize = sizeof(__FLASH_BYTE); *((__FLASH_BYTE*)(pdata)) = 1; } else { // ни один из секторов не защищен *pbufsize = sizeof(__FLASH_BYTE); *((__FLASH_BYTE*)(pdata)) = 0; } Return(FLERR_SUCCESS); } Return(status); // Ошибка? } } Return(FLERR_INVALID_BUFFER); case fsvc_getPageSize: // размер страницы { if( sizeof( __FLASH_WORD ) >= *pbufsize ) { *((__FLASH_WORD*)(pdata)) = W25QFLASH_PAGE_SIZE; *pbufsize = sizeof(__FLASH_WORD); Return(FLERR_SUCCESS); } } Return(FLERR_INVALID_BUFFER); case fsvc_getSectorsCount: // количество секторов { if( sizeof( __FLASH_WORD ) <= *pbufsize ) { *((__FLASH_WORD*)(pdata)) = W25QFLASH_SECTORS; *pbufsize = sizeof(__FLASH_WORD); Return(FLERR_SUCCESS); } } Return(FLERR_INVALID_BUFFER); case fsvc_getPagesCount: // количество страниц { if( sizeof( __FLASH_WORD ) >= *pbufsize ) { *((__FLASH_WORD*)(pdata)) = W25QFLASH_PAGES; *pbufsize = sizeof(__FLASH_WORD); Return(FLERR_SUCCESS); } } Return(FLERR_INVALID_BUFFER); case fsvc_getBlocksCount: // количество блоков { if( sizeof( __FLASH_WORD ) >= *pbufsize ) { *((__FLASH_WORD*)(pdata)) = W25QFLASH_BLOCKS; *pbufsize = sizeof(__FLASH_WORD); Return(FLERR_SUCCESS); } } Return(FLERR_INVALID_BUFFER); } Return(FLERR_INVALID_PARAMETER); } // ----- static flash_err_t flash_api_sectorprotectregister_read( __flash_sectorprotectionregister_t * contents ); static flash_err_t flash_api_sectorprotectregister_write( __flash_sectorprotectionregister_t * contents ); static flash_err_t flash_api_writeprotection_disable(); // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ // flash_api_protect_ex - установить/снять защиту секторов // Поробности см. описание flash_api_protect() / flash_api_unprotect() // // @mode: flash_sector_unprotected - вызов из flash_api_unprotect() // @mode: flash_sector_protected - вызов из flash_api_protect() // static flash_err_t flash_api_protect_ex( flash_api_protect_t * content, flash_api_protect_bits mode ) // [W25Q WRONG] { Enter(); // проверяем устройство на занятость if( FLERR_TIMEOUT == flash_api_getready() ) Return(FLERR_TIMEOUT); // проверяем на NULL переданный параметр if( NULL == content ) { // попытка вызова без указания схемы защиты // допустимо только если mode===flash_sector_unprotected // НЕ допустимо, если mode===flash_sector_protected // проверка режима: if( mode == flash_sector_protected ) { // параметр NULL, схема защиты не указана, нужно бы вернуть ошибку, т.к. // неизвестно, какие сектора нужно защищать, но для совместимости с драйверами чипов, // имеющих глобальный флаг защиты, здесь возвращается УСПЕХ, т.к. предполагается // что индивидуальные флаги защиты уже были проставлены на предыдущем этапе. // Для W25Q именно индивидуальные флаги будут использованы, а данный вызов - лишь заглушка // оставленная для совместимости Return(FLERR_SUCCESS); } // снятие защиты: если схема не указана (content=null), разрешаем запись во все сектора Return(flash_api_writeprotection_disable()); } else { // указана схема модификации индивидуальных битов защиты секторов (content != NULL) // проверка режима модификации общего бита защиты switch( content->protection_modify ) { // без модификации общего бита защиты case flash_sector_protect_nomodify: // не трогаем общий бит защиты break; // модификация общего бита защиты case flash_sector_protect_modify: if( mode != flash_sector_unprotected ) { // модификация общего бита защиты при установке защиты // с указанием схемы - недоступна. Return(FLERR_INVALID_PARAMETER); } // flash_sector_protected if( content->protection_enabled ) { // попытка установки общего флага защиты вместе с указанием схемы защиты // не поддерживается Return(FLERR_INVALID_PARAMETER); } // flash_sector_unprotected else { // попытка снятия общего флага защиты вместе с указанием схемы защиты // схема защиты - игнорируются // разрешается запись во все сектора Return(flash_api_writeprotection_disable()); } break; default: // неправильный параметр в поле protection_modify Return(FLERR_INVALID_PARAMETER); } // LL-буфер flash_LL_buf_t buf; // получаем указатель на буфер драйвера // зануляем память (буфер занулен по умолчанию) buf.rawptr = W25Q_LL_Routines.service.getBuf( fibufSectorFlags, &buf.size ); // считываем флаги защиты секторов flash_api_sectorprotectregister_read( buf.pSectorProtectionRegister ); //------------------------------------------------------ // модифицируем флаги защиты for( size_t nSector = 0; nSector < __SECTORS; ++nSector ) { if( FLASH_PROTECTION_REGISTER_CHECK( content->sectors.bytes, nSector ) ) { // сектор выбран: устанавливаем или снимаем флаги защиты if( mode == flash_sector_unprotected ) { FLASH_PROTECTION_REGISTER_CLEAR( buf.pSectorProtectionRegister->bSectors, nSector ); } else { FLASH_PROTECTION_REGISTER_SET( buf.pSectorProtectionRegister->bSectors, nSector ); } } else { /* не модифицировать статус защиты сектора */ } } //------------------------------------------------------ // записываем флаги защиты секторов flash_api_sectorprotectregister_write( buf.pSectorProtectionRegister ); //------------------------------------------------------ { // №№№№№№ ПРОВЕРКА №№№№№№ // считываем флаги защиты секторов обратно для сравнения flash_api_sectorprotectregister_read( buf.pSectorProtectionRegister ); // проверяем флаги защиты for( size_t nSector = 0; nSector < __SECTORS_TYPICAL; ++nSector ) { if( FLASH_PROTECTION_REGISTER_CHECK( content->sectors.bytes, nSector ) ) { // сектор выбран: проверяем биты защиты if( mode == flash_sector_unprotected ) { if( FLASH_PROTECTION_REGISTER_CHECK( buf.pSectorProtectionRegister->bSectors, nSector ) ) Return(FLERR_GENERIC_ERROR); } else { if( ! FLASH_PROTECTION_REGISTER_CHECK( buf.pSectorProtectionRegister->bSectors, nSector ) ) Return(FLERR_GENERIC_ERROR); } } } } //------------------------------------------------------ // успех. Return(FLERR_SUCCESS); } } // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ // flash_api_protect - установить защиту секторов // // Функция модифицирует флаги защиты сектров выбранных пользователем в состояние "защищен". // Установите соответствующие флаги в структуре @content в состояние "1" для защиты сектора. // Функция не обрабатывает модификацию общего флага защиты, т.к. в W25QXXX он отсутствует. // Функция не может защитить выбранные сектора без указания схемы защиты, поскольку регистры индивидуальной // защиты секторов не сохраняются при отключении питания. Поэтому передача схемы защиты при вызове функции // обязательна, в отличие от flash_api_unprotect, которая просто разрешает запись во все сектора сразу. // Поскольку схема защиты обязательна, общий флаг защиты при вызове функции упразднен, т.к. не имеет смысла. // Для сохранения сингатуры вызова функции (совместимость с драйвером AT45), поле protection_modify должно // быть установлено в значение flash_sector_protect_nomodify, в противном случае функция вернет // ошибку FLERR_INVALID_PARAMETER. Значение protection_enabled будет игнорироваться. // // * установите поле protection_modify в состояние "flash_sector_protect_nomodify". // * поле protection_enabled игнорируется // * параметр @content может быть равен NULL (совместимость с драйверами с общим флагом защиты) // * если @content равен NULL, функция всегда возвращает FLERR_SUCCESS для совместимости драйвера с драйверами // чипов, имеющих общий флаг защиты. Предполагается, что этому предшествовал вызов с @content!=NULL, // где указана схема защиты, которая и будет использована. // // # Функция вернет FLERR_SUCCESS, если в процессе операции не произошло ошибок. // # Функция вернет FLERR_INVALID_PARAMETER в случае, если обнаружит ошибку в переданном параметре // # Функция вернет FLERR_UNEXPECTED_BUSY в случае, если процесс выполнения был прерван из-за неожиданного отказа устройства // # Функция вернет FLERR_GENERIC_ERROR в случае, если общий бит защиты не был установлен из-за ошибки // # Функция вернет FLERR_GENERIC_ERROR в случае, если хотя бы один из выбранных битов защиты не был установлен/сброшен // static flash_err_t flash_api_protect( flash_api_protect_t * content ) // [W25Q OK] { return( flash_api_protect_ex( content, flash_sector_protected )); } // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ // flash_api_unprotect - снять защиту секторов. Функция аналогичная flash_api_protect(), // только имеет обратный эффект - снятие защиты с выбранных секторов. // // Функция модифицирует флаги защиты сектров выбранных пользователем в состояние "не защищен". // Установите соответствующие флаги в структуре @content в состояние "1" для снятия защиты сектора. // Функция также обрабатывает модификацию общего флага защиты выбранных секторов, но особым образом. // Т.к. в W25QXXX отсутствует общий флаг защиты, функция просто разрешает запись во все секторы при попытке // сброса общего бита защиты. Установка общего бита защиты недоступна. // При вызове поддерживается сочетание protection_modify=flash_sector_protect_modify и protection_enabled=0, // либо protection_modify=flash_sector_protect_nomodify, в противном случае будет возвращена ошибка FLERR_INVALID_PARAMETER. // // * Флаги защиты секторов могут быть сброшены индивидуально, либо все сразу (сбросом изменения общего флага защиты) // * Для сброса защиты всех секторов установите поле protection_modify в состояние "flash_sector_protect_modify", а // а поле protection_enabled=0, при этом значения частных флагов защиты секторов игнорируются // // * параметр @content может быть равен NULL, в этом случае разрешается запись во все сектора // // * Для снятия частных флагов защиты сектров: // - установите поля частных флагов защиты требуемых секторов (s0...s63) в состояние flash_sector_protect_modify, // и установите protection_modify в значение "flash_sector_protect_nomodify". Поле protection_enabled игнорируется. // // # Функция вернет FLERR_SUCCESS, если в процессе операции не произошло ошибок. // # Функция вернет FLERR_INVALID_PARAMETER в случае, если обнаружит ошибку в переданном параметре // # Функция вернет FLERR_UNEXPECTED_BUSY в случае, если процесс выполнения был прерван из-за неожиданного отказа устройства // # Функция вернет FLERR_GENERIC_ERROR в случае, если общий бит защиты не был установлен из-за ошибки // # Функция вернет FLERR_GENERIC_ERROR в случае, если хотя бы один из выбранных битов защиты не был установлен/сброшен // static flash_err_t flash_api_unprotect( flash_api_protect_t * content ) // [W25Q OK] { return( flash_api_protect_ex( content, flash_sector_unprotected )); } // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ // flash_api_getprotect - получить информацию о защищенных секторах. // * Указатель @content может быть NULL. // # Возвращаемое значение - FLERR_PROTECT_ENABLED если хотя бы один из секторов защищен от записи. // Если @content не NULL, то в @content будет записана структура типа flash_api_getprotect_t, содержащая детальную // информацию о защите сектора (один сектор на один бит), 1b значит, что сектор защищен, 0b - не защищен, а поле // protection_enabled будет равняться возвращаемому значению static flash_err_t flash_api_getprotect( flash_api_getprotect_t * content ) // [W25Q OK] { flash_err_t retVal = FLERR_INVALID_PARAMETER; Enter(); { if( FLERR_TIMEOUT == flash_api_getready() ) Return(FLERR_TIMEOUT); // LL-буфер flash_LL_buf_t buf; // получаем указатель на буфер драйвера // зануляем память (буфер занулен по умолчанию) buf.rawptr = W25Q_LL_Routines.service.getBuf( fibufSectorFlags, &buf.size ); // считываем флаги защиты секторов flash_api_sectorprotectregister_read( buf.pSectorProtectionRegister ); retVal = FLERR_PROTECT_DISABLED; if( NULL != content ) { content->protection_enabled = flash_sector_unprotected; } // проверяем флаги защиты for( size_t nSector = 0; nSector < __SECTORS; ++nSector ) { // проверяем биты защиты if( FLASH_PROTECTION_REGISTER_CHECK( buf.pSectorProtectionRegister->bSectors, nSector ) ) { retVal = FLERR_PROTECT_ENABLED; if( NULL != content ) { content->protection_enabled = flash_sector_protected; FLASH_PROTECTION_REGISTER_SET( content->sectors.bytes, nSector ); } else break; // код @retVal установлен, content=NULL, здесь больше нечего делать } else { if( NULL != content ) { FLASH_PROTECTION_REGISTER_CLEAR( content->sectors.bytes, nSector ); } } } } Return(retVal); } // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ // flash_api_erase - мультирежимное стирание данных // * Параметр @mode задает режим стирания: // mode==fem_byaddress - начальное смещение задается адресом в параметре @start, длинна отрезка - @count в байтах // При этом минимальный стираемый диапазон - размер страницы. // Страница, которой принадлежит адрес @start стирается полностью // Страница, которой принадлежит адрес @start + @count также стирается полность // mode==fem_bypage - начальное смещение задается номером страницы в параметре @start. // Количество стираемых страниц задается параметром @count // // mode==fem_byblock - не поддерживается в W25Q. установка этого значения эквивалентна fem_bypage // // mode==fem_bysector - начальное смещение задается номером сектора в параметре @start // Количество стираемых секторов задается параметром @count. // // // * Параметр @count не может быть 0. // // ! Функция НЕ проверяет защиту секторов ! // // # Функция возвращает FLERR_SUCCESS если не произошло ошибок. // # Функция вернет FLERR_INVALID_PARAMETER если диапазон стирания выходит за границу адресного пространства // # Функция вернет FLERR_INVALID_PARAMETER если @count равняется 0. // # Функция вернет FLERR_INVALID_PARAMETER если @mode имеет значение, отличное от перечисленных. // # Функция вернет FLERR_UNEXPECTED_BUSY если во время операции устройство перестало отвечать // static flash_err_t flash_api_erase( flash_erase_mode_t mode, __FLASH_DWORD start, __FLASH_DWORD count ) // [W25Q WRONG] { if( 0 == count ) return(FLERR_INVALID_PARAMETER); Enter(); switch( mode ) { case fem_byaddress: // стирание по адресу #if W25QFLASH_MINIMUM_ADDRESS > 0 if( start < W25QFLASH_MINIMUM_ADDRESS ) Return(FLERR_INVALID_PARAMETER); #endif if( start + count > W25QFLASH_MAXIMUM_ADDRESS ) Return(FLERR_INVALID_PARAMETER); case fem_bypage: // стирание по страницам case fem_byblock: // стирание по блокам (не поддерживается, эквивалентно fem_bypage) { __FLASH_DWORD PagesToErase=0; __FLASH_DWORD PageBegin =0; if( mode == fem_byaddress ) { // стирание по адресу PagesToErase = __FLASH_PAGESINRANGE( start , count ); PageBegin = __FLASH_ADDRESS2PAGE( start ); } else { // стирание по страницам #if W25QFLASH_MINIMUM_ADDRESS > 0 if( start < __FLASH_ADDRESS2PAGE(W25QFLASH_MINIMUM_ADDRESS) ) Return(FLERR_INVALID_PARAMETER); #endif if( start + count > __FLASH_ADDRESS2PAGE(W25QFLASH_MAXIMUM_ADDRESS) ) Return(FLERR_INVALID_PARAMETER); PagesToErase = count; PageBegin = start; } while( PagesToErase ) { // команда разрешения операций стирания/записи W25Q_LL_Routines.operations.writeEnable(); // проверяем, нельзя ли использовать секторное стирание? if( __FLASH_PAGE_ARRAGED_BY_SECTOR ( PageBegin ) && // проверяем выравниваение ( PagesToErase >= W25QFLASH_PAGES_PER_SECTOR ) ) // проверяем количество страниц { // можно! // стираем сектор - это быстрее W25Q_LL_Routines.data.sectorErase( __FLASH_PAGE2SECTOR( PageBegin ) ); #if W25QXXX_BKGOPERATIONS if( FLERR_TIMEOUT == flash_getready_time( _TIME_SCERS_ms ) ) #else if( FLERR_TIMEOUT == flash_api_getready() ) #endif { // фатальная ошибка: устройство занято дольше обычного Return(FLERR_UNEXPECTED_BUSY); } PageBegin += __PAGES_PER_SECTOR; PagesToErase -= __PAGES_PER_SECTOR; } else { // нельзя W25Q_LL_Routines.data.pageErase( PageBegin ); #if W25QXXX_BKGOPERATIONS if( FLERR_TIMEOUT == flash_getready_time( _TIME_PGERS_ms ) ) #else if( FLERR_TIMEOUT == flash_api_getready() ) #endif { // фатальная ошибка: устройство занято дольше обычного Return(FLERR_UNEXPECTED_BUSY); } PageBegin ++; PagesToErase --; } } } break; case fem_bysector: // стирание по секторам { if( start <= __FLASH_ADDRESS2SECTOR(W25QFLASH_MINIMUM_ADDRESS) ) Return(FLERR_INVALID_PARAMETER); if( start + count > __FLASH_ADDRESS2SECTOR(W25QFLASH_MAXIMUM_ADDRESS) ) Return(FLERR_INVALID_PARAMETER); while( count ) { // команда разрешения операций стирания/записи W25Q_LL_Routines.operations.writeEnable(); W25Q_LL_Routines.data.sectorErase( start ); #if W25QXXX_BKGOPERATIONS if( FLERR_TIMEOUT == flash_getready_time( _TIME_SCERS_ms ) ) #else if( FLERR_TIMEOUT == flash_api_getready() ) #endif { // фатальная ошибка: устройство занято дольше обычного Return(FLERR_UNEXPECTED_BUSY); } start ++; count --; } } break; default: Return(FLERR_INVALID_PARAMETER); } // успех. Return(FLERR_SUCCESS); } // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ // flash_api_write - мультирежимная запись данных по адресу // // * Параметр @address - указывает линейный адрес в адресном пространстве чипа. // * Параметр @pBuffer - должен указывать на буфер-источник данных для записи. Не может быть NULL. // * Параметр @size - должен указывать на размер данных на запись. Не может быть равен 0. // * Параметр @mode - задает режим записи. Доступны следующий режимы: // - fwm_safewrite - режим безопасной записи. Медленный режим, когда запись/стирание происходит // постранично с верификацией. В случае ошибки верификации запись прекращается, // остальная (незаписанная) часть данных не переписывается/не стрирается, // при этом функция возвращает ошибку FLERR_VERIFY_ERROR // // - fwm_fastwrite - режим быстрой записи. Стирание происходит по блокам (8 станиц, см. заголовочный файл) // Запись постраничная. Верификация данных не проводится. // // - fwm_compatible НЕ ПОДДЕРЖИВАЕТСЯ, возвращается ошибка FLERR_INVALID_PARAMETER // // // # Функция возвращает FLERR_SUCCESS, если не произшло никаких ошибок. // # Функция возвращает FLERR_UNEXPECTED_BUSY, если во время записи произошел отказ устройства. // # Функция возвращает FLERR_VERIFY_ERROR, если обнаружена ошибка верификации данных. // # Функция возвращает FLERR_INVALID_PARAMETER, если указан неверный параметр: // - @address указывает на зарезервированную область, либо область записи выходит за границу адресного пространства; // - @pBuffer или @size равны 0; // - @mode имеет значение, отличное от перечисленных // // # Для быстрой работы требует включения режима W25QXXX_BKGOPERATIONS // Функция использует две различные монорежимные функции записи: //---------- static flash_err_t flash_api_buffer_programm( flash_LL_buf_t * buf, __FLASH_DWORD page ); //---------- static flash_err_t flash_api_write( flash_address_t address, __FLASH_BYTE * pBuffer, flash_address_t size, flash_write_mode_t mode ) // [W25Q OK] { #if W25QFLASH_MINIMUM_ADDRESS > 0 if( address < W25QFLASH_MINIMUM_ADDRESS ) return(FLERR_INVALID_PARAMETER); #endif if( !size || !pBuffer ) return(FLERR_INVALID_PARAMETER); if( address + size > W25QFLASH_MAXIMUM_ADDRESS ) return(FLERR_INVALID_PARAMETER); bool verify = false;; switch( mode ) { case fwm_safewrite: //режим безопасной записи. // > Данные страницы кешируются в низкоуровневом программном буфере // > Производится модификация данных в буфере (запись в буфер со смещением) // > Используется команда стирание страницы // > Используется команда запись страницы // > Производится чтение данных по смещению в буфер // > Производится данных буфера и переданных данных // > Если данные совпадают - запись без ошибок /* ______________________ / \ | Slow Moderate Fast | | \ | / | | \ | | \ | | \ | | __\____ | \_____________________/ */ verify = true; break; case fwm_fastwrite: //режим быстрой записи. // > Данные страницы кешируются в низкоуровневом программном буфере // > Производится модификация данных в буфере (запись в буфер со смещением) // > Используется команда стирание страницы/сектора // > Постранично записываем данные в чип // > Готово. /* ______________________ / \ | Slow Moderate Fast | | \ | / | | / | | / | | / | | _____/_ [!] | \_____________________/ */ break; default: return(FLERR_INVALID_PARAMETER); } // > Данные страницы кешируются в низкоуровневом программном буфере // > Производится модификация данных в буфере (запись в буфер со смещением) // > Используется команда стирание страницы // > Используется команда запись страницы // > Если включена верификация { // >>> Производится чтение данных по смещению в буфер // >>> Производится данных буфера и переданных данных // > } // > Если данные совпадают - запись без ошибок __FLASH_DWORD pageBegin = __FLASH_ADDRESS2PAGE( address ); // вычисляем номер первой страницы __FLASH_DWORD PagesToWrite = __FLASH_PAGESINRANGE( address, size ); // вычисляем количество затрагиваемых страниц __FLASH_DWORD BytesToWrite = __FLASH_DATAREMAININGINPAGE( address ); // вычисляем для первой страницы Enter(); flash_LL_buf_t buf; // запрос буфера драйвера LL buf.rawptr = W25Q_LL_Routines.service.getBuf( fibufPageIO, &buf.size ); do { if( size < BytesToWrite ) BytesToWrite = size; //--------------------- // ПЕРВАЯ и ПОСЛЕДНЯЯ страницы могут записываться частично: требуется предварительное чтение if( __FLASH_ADDRESSOFFSETPAGE( address ) || PagesToWrite<=1 ) { // буферизация страницы в программном буфере W25Q_LL_Routines.data.arrayRead( address - __FLASH_ADDRESSOFFSETPAGE(address), buf.pageBuffer, buf.size ); // Производится модификация данных в буфере (запись в буфер со смещением) __imp_sys_memcpy( buf.pageBuffer + __FLASH_ADDRESSOFFSETPAGE( address ), pBuffer, BytesToWrite ); } //--------------------- // идет запись: разрешение стирания страницы W25Q_LL_Routines.operations.writeEnable(); // идет запись: СТИРАНИЕ W25Q_LL_Routines.data.pageErase( pageBegin ); if( FLASH_ERROR( W25Q_LL_Routines.ready.smartWaitms( _TIME_PGERS_ms ) ) ) Return(FLERR_UNEXPECTED_BUSY); // идет запись: ПРОГРАММИРОВАНИЕ if( FLASH_ERROR( flash_api_buffer_programm( &buf, pageBegin ) ) ) Return(FLERR_UNEXPECTED_BUSY); //--------------------- if( verify ) { // чтение записанного куска в буфер W25Q_LL_Routines.data.arrayRead( address, buf.pageBuffer, BytesToWrite ); //--------------------- // Производится сравнение записанной страницы с буфером if( 0 != __imp_sys_memcmp( buf.pageBuffer, pBuffer, BytesToWrite ) ) Return(FLERR_VERIFY_ERROR); } //--------------------- pageBegin++; address += BytesToWrite; pBuffer += BytesToWrite; size -= BytesToWrite; BytesToWrite = __FLASH_DATAREMAININGINPAGE( address ); } while( --PagesToWrite ); Return(FLERR_SUCCESS); } //---------- // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ // flash_api_read - чтение данных по адресу // // * Функция проверяет правильность переданных параметров // * Параметр @address задает линейный адрес внутри адресного пространства устройства. // Параметр @pvBuffer должен указывать на буфер-приемник для прочитанных данных. // Параметр @size должен указывать на размер буфер-приемника. Параметр показывает // количество байт для чтения в буфер. // # Если параметр @address указывает на адрес в диапазоне 0...W25QFLASH_MINIMUM_ADDRESS, функция вернет ошибку FLERR_INVALID_PARAMETER // # Если параметр @pvBuffer равен NULL, функция вернет ошибку FLERR_INVALID_PARAMETER // # Если параметр @size равен 0, функция вернет ошибку FLERR_INVALID_PARAMETER static flash_err_t flash_api_read( flash_address_t address, __FLASH_BYTE * pBuffer, flash_address_t size ) // [W25Q OK] { // проверяем входные параметры #if W25QFLASH_MINIMUM_ADDRESS > 0 if( address < W25QFLASH_MINIMUM_ADDRESS ) return(FLERR_INVALID_PARAMETER); #endif if( NULL == pBuffer || 0 == size ) return(FLERR_INVALID_PARAMETER); if( address + size > __FULL_CHIP_SIZE ) return(FLERR_INVALID_PARAMETER); Enter(); // проверям готовность устройства if( FLERR_TIMEOUT == flash_api_getready() ) Return(FLERR_TIMEOUT); W25Q_LL_Routines.data.arrayRead( address, pBuffer, size ); Return(FLERR_SUCCESS); } // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ // flash_api_sleepmode - переход в режим пониженного энергопотребления // # переводит устройство в режим, когда все команды, кроме flash_api_wakeup игнорируются static flash_err_t flash_api_sleepmode() // [W25Q OK] { Enter(); W25Q_LL_Routines.sleep.sleepMode(); Return(FLERR_SUCCESS); } // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ // flash_api_wakeup - выход из режима пониженного энергопотребления // # выводит устройство из режима, когда все команды, кроме flash_api_wakeup игнорируются static flash_err_t flash_api_wakeup() // [W25Q OK] { Enter(); W25Q_LL_Routines.sleep.wakeUp(); if( W25Q_LL_Routines.ready.smartWaitms( W25QXXX_API_TIMEOUT ) ) { Return(FLERR_SUCCESS); } Return(FLERR_TIMEOUT); } // №№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№№ /* // -------------------------------------------------------------------------------------------------------- // __flash_hal__protectregister_write - записывает регистр защиты (биты индивидуальной защиты секторов/блоков) // // * потоко-небезопасная функция // * параметр @contents не может быть NULL - укажите содержимое регистра защиты (см. __flash_protectionregister_t) // * задерживает выполнение на ДЕСЯТКИ МИЛЛИСЕКУНД static void __flash_hal__protectregister_write( __flash_protectionregister_t * contents // содержимое регистра защиты для записи ) // [W25Q WRONG] { if( !contents ) return; for( size_t nProtSector = 0; nProtSector < __PROT_SECTOR_FLAGS; ++nProtSector ) { __flash_hal__protectStateWrite( nProtSector, contents->Sectors[ nProtSector ] ); } } // -------------------------------------------------------------------------------------------------------- */ /* // -------------------------------------------------------------------------------------------------------- // __flash_hal__protectregister_read - прочитывает регистр защиты (биты индивидуальной защиты секторов/блоков) // // * потоко-небезопасная функция // * параметр @contents не может быть NULL - укажите буфер для содержимого регистра защиты (см. __flash_protectionregister_t) static void __flash_hal__protectregister_read( __flash_protectionregister_t * contents // буфер-приемник содержимого регистра защиты ) // [W25Q WRONG] { if( !contents ) return; for( size_t nProtSector = 0; nProtSector < __PROT_SECTOR_FLAGS; ++nProtSector ) { contents->Sectors[ nProtSector ] = __flash_hal__protectStateRead( nProtSector ); } } // -------------------------------------------------------------------------------------------------------- */ // -------------------------------------------------------------------------------------------------------- // flash_api_sectorprotectregister_write - записывает регистр защиты (биты индивидуальной защиты СЕКТОРОВ 64К) // // * потоко-небезопасная функция // * параметр @contents не может быть NULL - укажите содержимое регистра защиты (см. __flash_sectorprotectionregister_t) // * задерживает выполнение на ДЕСЯТКИ МИЛЛИСЕКУНД static flash_err_t flash_api_sectorprotectregister_write( __flash_sectorprotectionregister_t * contents // содержимое регистра защиты для записи ) // [W25Q OK] { if( NULL == contents ) return FLERR_INVALID_PARAMETER; // цикл по секторам 64КБ for( size_t nSector = 0; nSector < __SECTORS; ++nSector ) { size_t nProtSectorCount = 0; size_t nProtSectorStart = __SECTOR_TO_PROTSECTOR(nSector, nProtSectorCount); // цикл по индивидуальным битам блоков/секторов внутри сектора 64КБ for( size_t nProtSector = nProtSectorStart; nProtSector < nProtSectorStart + nProtSectorCount; ++nProtSector ) { bool state = FLASH_PROTECTION_REGISTER_CHECK( contents->bSectors, nSector ); W25Q_LL_Routines.operations.writeEnable(); W25Q_LL_Routines.protection.protectStateWrite( nProtSector, state ); // проверяем готовность устройства #if W25QXXX_BKGOPERATIONS if( FLERR_TIMEOUT == flash_getready_time( _TIME_PGPRG_ms ) ) #else if( FLERR_TIMEOUT == flash_api_getready() ) #endif return FLERR_TIMEOUT; } } return FLERR_SUCCESS; } // -------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------- // flash_api_sectorprotectregister_read - прочитывает регистр защиты (биты индивидуальной защиты СЕКТОРОВ 64К) // // * потоко-небезопасная функция // * параметр @contents не может быть NULL - укажите буфер для содержимого регистра защиты (см. __flash_sectorprotectionregister_t) static flash_err_t flash_api_sectorprotectregister_read( __flash_sectorprotectionregister_t * contents // буфер-приемник содержимого регистра защиты ) // [W25Q OK] { if( NULL == contents ) return FLERR_INVALID_PARAMETER; // цикл по секторам 64КБ for( size_t nSector = 0; nSector < __SECTORS; ++nSector ) { size_t nProtSectorCount = 0; size_t nProtSectorStart = __SECTOR_TO_PROTSECTOR(nSector, nProtSectorCount); bool state = false; // цикл по индивидуальным битам блоков/секторов внутри сектора 64КБ for( size_t nProtSector = nProtSectorStart; nProtSector < nProtSectorStart + nProtSectorCount; ++nProtSector ) { if( W25Q_LL_Routines.protection.protectStateRead(( nProtSector ) ) ) { state = true; // как минимум один блок особенного сектора защищен break; } } if( state ) FLASH_PROTECTION_REGISTER_SET(contents->bSectors, nSector); else FLASH_PROTECTION_REGISTER_CLEAR(contents->bSectors, nSector); } return FLERR_SUCCESS; } // -------------------------------------------------------------------------------------------------------- // flash_api_writeprotection_disable - снимает защиту от записи во всех секторах // // * потоко-небезопасная функция static flash_err_t flash_api_writeprotection_disable() // [W25Q OK] { // разрешаем опрерации записи/стирания W25Q_LL_Routines.operations.writeEnable(); // команда снятия защиты со всех секторов/блоков W25Q_LL_Routines.protection.globalUnlock(); #if W25QXXX_BKGOPERATIONS // Global Unlock требует ожидания? if( FLERR_TIMEOUT == flash_getready_time( _TIME_PGPRG_ms ) ) #else if( FLERR_TIMEOUT == flash_api_getready() ) #endif return FLERR_TIMEOUT; flash_LL_buf_t buf; // запрос буфера драйвера LL buf.rawptr = W25Q_LL_Routines.service.getBuf( fibufSectorFlags, &buf.size ); // проверка буфера if( NULL == buf.pSectorProtectionRegister || buf.size < sizeof(__flash_sectorprotectionregister_t) ) { Return(FLERR_INVALID_BUFFER); } // чтение флагов защиты секторов flash_api_sectorprotectregister_read( buf.pSectorProtectionRegister ); // проверка флагов всех секторов for( size_t nSector = 0; nSector < __SECTORS; ++nSector ) { if( FLASH_PROTECTION_REGISTER_CHECK( buf.pSectorProtectionRegister->bSectors, nSector ) ) { // найден защищенный сектор, хотя такого быть не должно: ошибка выполнения Return(FLERR_GENERIC_ERROR); } } // теперь защита от записи снята для всех секторов. команда выполнена Return(FLERR_SUCCESS); } // -------------------------------------------------------------------------------------------------------- // flash_api_buffer_programm - запись страничного буфера (4K) // * внутренняя функция // * не стирает страницу перед записью буфера // * не проверяет переданные параметры // * потоко-небезопасная функция // # Возвращает либо FLERR_SUCCESS если ошибок не произошло, либо FLERR_UNEXPECTED_BUSY // если чип непредвиденно занят до или после операции записи static flash_err_t flash_api_buffer_programm( flash_LL_buf_t * buf, __FLASH_DWORD page ) { __flash_pageprogramptr_t chunkBuffer = (__flash_pageprogramptr_t)buf->rawptr; __FLASH_DWORD w24Address = __FLASH_PAGE2ADDRESS(page); // проверка занятости if( FLASH_ERROR( W25Q_LL_Routines.ready.getReadyFast() ) ) Return(FLERR_UNEXPECTED_BUSY); for( __FLASH_DWORD byteWritten = 0; byteWritten < buf->size; ) { // разрешаем опрерации записи/стирания W25Q_LL_Routines.operations.writeEnable(); // производится запись W25Q_LL_Routines.data.chunkProgram( chunkBuffer, w24Address ); #if W25QXXX_BKGOPERATIONS if( FLASH_ERROR( W25Q_LL_Routines.ready.smartWaitms( _TIME_PGPRG_ms ) ) ) Return(FLERR_UNEXPECTED_BUSY); #else if( FLASH_ERROR( W25Q_LL_Routines.ready.getReadyFast() ) ) Return(FLERR_UNEXPECTED_BUSY); #endif w24Address += sizeof(*chunkBuffer); byteWritten += sizeof(*chunkBuffer); chunkBuffer ++; // переход к следующему куску (chunk, 256bytes) } return FLERR_SUCCESS; }