mirror of
				https://github.com/IcedRooibos/py32f0-template.git
				synced 2025-10-30 17:32:05 -07:00 
			
		
		
		
	
		
			
				
	
	
		
			453 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			453 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* -----------------------------------------------------------------------------
 | ||
|  * Copyright (c) 2014 - 2019 ARM Ltd.
 | ||
|  *
 | ||
|  * This software is provided 'as-is', without any express or implied warranty.
 | ||
|  * In no event will the authors be held liable for any damages arising from
 | ||
|  * the use of this software. Permission is granted to anyone to use this
 | ||
|  * software for any purpose, including commercial applications, and to alter
 | ||
|  * it and redistribute it freely, subject to the following restrictions:
 | ||
|  *
 | ||
|  * 1. The origin of this software must not be misrepresented; you must not
 | ||
|  *    claim that you wrote the original software. If you use this software in
 | ||
|  *    a product, an acknowledgment in the product documentation would be
 | ||
|  *    appreciated but is not required.
 | ||
|  *
 | ||
|  * 2. Altered source versions must be plainly marked as such, and must not be
 | ||
|  *    misrepresented as being the original software.
 | ||
|  *
 | ||
|  * 3. This notice may not be removed or altered from any source distribution.
 | ||
|  *
 | ||
|  *
 | ||
|  * $Date:        2021-7-1
 | ||
|  * $Revision:    V1.0.0
 | ||
|  *
 | ||
|  * Project:      Flash Programming Functions for Puya PY32F030xx Flash
 | ||
|  * --------------------------------------------------------------------------- */
 | ||
| 
 | ||
| /* History:
 | ||
|  *  Version 1.0.0
 | ||
|  *    Initial release
 | ||
|  */
 | ||
| 
 | ||
| #include "FlashOS.h"        // FlashOS Structures
 | ||
| #include "py32f0xx.h"
 | ||
| 
 | ||
| typedef volatile unsigned char    vu8;
 | ||
| typedef          unsigned char     u8;
 | ||
| typedef volatile unsigned short   vu16;
 | ||
| typedef          unsigned short    u16;
 | ||
| typedef volatile unsigned long    vu32;
 | ||
| typedef          unsigned long     u32;
 | ||
| 
 | ||
| #define M8(adr)  (*((vu8  *) (adr)))
 | ||
| #define M16(adr) (*((vu16 *) (adr)))
 | ||
| #define M32(adr) (*((vu32 *) (adr)))
 | ||
| 
 | ||
| //uint32_t gdw_HSI_FS;
 | ||
| 
 | ||
| //#define USE_HSI_24MHZ
 | ||
| 
 | ||
| ////uiOffset = 0(4MHz), 1(8MHz), 2(16MHz), 3(22.12MHz), 4(24MHz)
 | ||
| //void SetFlashParameter(uint32_t uiOffset)
 | ||
| //{
 | ||
| //    FLASH->TS0 = ((M32(0x1FFF0F1C + uiOffset * 0x14) >> 0) & 0x000000FF);
 | ||
| //    FLASH->TS3 = ((M32(0x1FFF0F1C + uiOffset * 0x14) >> 8) & 0x000000FF);
 | ||
| //    FLASH->TS1 = ((M32(0x1FFF0F1C + uiOffset * 0x14) >> 16) & 0x000001FF);
 | ||
| //    FLASH->TS2P = ((M32(0x1FFF0F20 + uiOffset * 0x14) >> 0) & 0x000000FF);
 | ||
| //    FLASH->TPS3 = ((M32(0x1FFF0F20 + uiOffset * 0x14) >> 16) & 0x000007FF);
 | ||
| //    FLASH->PERTPE = ((M32(0x1FFF0F24 + uiOffset * 0x14) >> 0) & 0x0001FFFF);
 | ||
| //    FLASH->SMERTPE = ((M32(0x1FFF0F28 + uiOffset * 0x14) >> 0) & 0x0001FFFF);
 | ||
| //    FLASH->PRGTPE = ((M32(0x1FFF0F2C + uiOffset * 0x14) >> 0) & 0x0000FFFF);
 | ||
| //    FLASH->PRETPE = ((M32(0x1FFF0F2C + uiOffset * 0x14) >> 16) & 0x0000FFFF);
 | ||
| //}
 | ||
| 
 | ||
| //void InitRccAndFlashParam(void)
 | ||
| //{
 | ||
| //#ifdef USE_HSI_24MHZ
 | ||
| //    gdw_HSI_FS = READ_BIT(RCC->ICSCR, RCC_ICSCR_HSI_FS);
 | ||
| //    MODIFY_REG(RCC->ICSCR, RCC_ICSCR_HSI_FS, RCC_ICSCR_HSI_FS_2);//HSI_24MHz
 | ||
| //    MODIFY_REG(RCC->ICSCR, RCC_ICSCR_HSI_TRIM, M32(0x1FFF0F10)&RCC_ICSCR_HSI_TRIM);
 | ||
| //    while (RCC_CR_HSIRDY != READ_BIT(RCC->CR, RCC_CR_HSIRDY));
 | ||
| //    SetFlashParameter(4);
 | ||
| //#else
 | ||
| //    uint32_t dwOffset;
 | ||
| //    switch (READ_BIT(RCC->ICSCR, RCC_ICSCR_HSI_FS))
 | ||
| //    {
 | ||
| //    default://4MHz
 | ||
| //        dwOffset = 0x00;
 | ||
| //        break;
 | ||
| //    case RCC_ICSCR_HSI_FS_0://8MHz
 | ||
| //        dwOffset = 0x01;
 | ||
| //        break;
 | ||
| //    case RCC_ICSCR_HSI_FS_1://16MHz
 | ||
| //        dwOffset = 0x02;
 | ||
| //        break;
 | ||
| //    case (RCC_ICSCR_HSI_FS_1|RCC_ICSCR_HSI_FS_0)://22.12MHz
 | ||
| //        dwOffset = 0x03;
 | ||
| //        break;
 | ||
| //    case RCC_ICSCR_HSI_FS_2://24MHz
 | ||
| //        dwOffset = 0x04;
 | ||
| //        break;
 | ||
| //    }
 | ||
| //    MODIFY_REG(RCC->ICSCR, RCC_ICSCR_HSI_TRIM, M32(0x1FFF0F00 + dwOffset * 4)&RCC_ICSCR_HSI_TRIM);
 | ||
| //    WRITE_REG(FLASH->TS0, ((M32(0x1FFF0F1C + dwOffset * 0x14) >> 0) & 0x000000FF));
 | ||
| //    WRITE_REG(FLASH->TS1, ((M32(0x1FFF0F1C + dwOffset * 0x14) >> 8) & 0x000000FF));
 | ||
| //    WRITE_REG(FLASH->TS3, ((M32(0x1FFF0F1C + dwOffset * 0x14) >> 16) & 0x000001FF));
 | ||
| //    WRITE_REG(FLASH->TS2P, ((M32(0x1FFF0F20 + dwOffset * 0x14) >> 0) & 0x000000FF));
 | ||
| //    WRITE_REG(FLASH->TPS3, ((M32(0x1FFF0F20 + dwOffset * 0x14) >> 16) & 0x000007FF));
 | ||
| //    WRITE_REG(FLASH->PERTPE, ((M32(0x1FFF0F24 + dwOffset * 0x14) >> 0) & 0x0001FFFF));
 | ||
| //    WRITE_REG(FLASH->SMERTPE, ((M32(0x1FFF0F28 + dwOffset * 0x14) >> 0) & 0x0001FFFF));
 | ||
| //    WRITE_REG(FLASH->PRGTPE, ((M32(0x1FFF0F2C + dwOffset * 0x14) >> 0) & 0x0000FFFF));
 | ||
| //    WRITE_REG(FLASH->PRETPE, ((M32(0x1FFF0F2C + dwOffset * 0x14) >> 16) & 0x00000FFF));
 | ||
| //#endif
 | ||
| //}
 | ||
| 
 | ||
| //void UnInitRccAndFlashParam(void)
 | ||
| //{
 | ||
| //#ifdef USE_HSI_24MHZ
 | ||
| //    MODIFY_REG(RCC->ICSCR, RCC_ICSCR_HSI_FS, gdw_HSI_FS);
 | ||
| //    switch (gdw_HSI_FS)
 | ||
| //    {
 | ||
| //    case RCC_ICSCR_HSI_FS_2://24MHz
 | ||
| //        MODIFY_REG(RCC->ICSCR, RCC_ICSCR_HSI_TRIM, M32(0x1FFF0F10)&RCC_ICSCR_HSI_TRIM);
 | ||
| //        break;
 | ||
| //    case (RCC_ICSCR_HSI_FS_1|RCC_ICSCR_HSI_FS_0)://22.12MHz
 | ||
| //        MODIFY_REG(RCC->ICSCR, RCC_ICSCR_HSI_TRIM, M32(0x1FFF0F0C)&RCC_ICSCR_HSI_TRIM);
 | ||
| //        break;
 | ||
| //    case RCC_ICSCR_HSI_FS_1://16MHz
 | ||
| //        MODIFY_REG(RCC->ICSCR, RCC_ICSCR_HSI_TRIM, M32(0x1FFF0F08)&RCC_ICSCR_HSI_TRIM);
 | ||
| //        break;
 | ||
| //    case RCC_ICSCR_HSI_FS_0://8MHz
 | ||
| //        MODIFY_REG(RCC->ICSCR, RCC_ICSCR_HSI_TRIM, M32(0x1FFF0F04)&RCC_ICSCR_HSI_TRIM);
 | ||
| //        break;
 | ||
| //    default://4MHz
 | ||
| //        MODIFY_REG(RCC->ICSCR, RCC_ICSCR_HSI_TRIM, M32(0x1FFF0F00)&RCC_ICSCR_HSI_TRIM);
 | ||
| //        break;
 | ||
| //    }
 | ||
| //    while (RCC_CR_HSIRDY != READ_BIT(RCC->CR, RCC_CR_HSIRDY));
 | ||
| //#endif
 | ||
| //}
 | ||
| 
 | ||
| /*
 | ||
|  *  Initialize Flash Programming Functions
 | ||
|  *    Parameter:      adr:  Device Base Address
 | ||
|  *                    clk:  Clock Frequency (Hz)
 | ||
|  *                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
 | ||
|  *    Return Value:   0 - OK,  1 - Failed
 | ||
|  */
 | ||
| 
 | ||
| #ifdef FLASH_MEM
 | ||
| int Init(unsigned long adr, unsigned long clk, unsigned long fnc)
 | ||
| {
 | ||
| 
 | ||
| //    InitRccAndFlashParam();
 | ||
| 
 | ||
|     FLASH->KEYR = FLASH_KEY1;                             // Unlock Flash
 | ||
|     FLASH->KEYR = FLASH_KEY2;
 | ||
| 
 | ||
|     FLASH->ACR  = 0x00000000;                             // Zero Wait State, no Prefetch
 | ||
|     FLASH->SR  |= FLASH_SR_EOP;                              // Reset FLASH_EOP
 | ||
| 
 | ||
|     if ((FLASH->OPTR & FLASH_OPTR_IWDG_SW) == 0x00)                   // Test if IWDG is running (IWDG in HW mode)
 | ||
|     {
 | ||
|         // Set IWDG time out to ~32.768 second
 | ||
|         IWDG->KR  = 0x5555;                                 // Enable write access to IWDG_PR and IWDG_RLR
 | ||
|         IWDG->PR  = 0x06;                                   // Set prescaler to 256
 | ||
|         IWDG->RLR = 0xFFF;                                   // Set reload value to 4095
 | ||
|     }
 | ||
| 
 | ||
|     return (0);
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| #ifdef FLASH_OPT
 | ||
| int Init(unsigned long adr, unsigned long clk, unsigned long fnc)
 | ||
| {
 | ||
| 
 | ||
|     InitRccAndFlashParam();
 | ||
| 
 | ||
|     FLASH->KEYR = FLASH_KEY1;                             // Unlock Flash
 | ||
|     FLASH->KEYR = FLASH_KEY2;
 | ||
| 
 | ||
|     FLASH->OPTKEYR = FLASH_OPTKEY1;                       // Unlock Option Bytes
 | ||
|     FLASH->OPTKEYR = FLASH_OPTKEY2;
 | ||
| 
 | ||
|     FLASH->ACR  = 0x00000000;                             // Zero Wait State, no Prefetch
 | ||
|     FLASH->SR  |= FLASH_SR_EOP;                              // Reset FLASH_EOP
 | ||
| 
 | ||
| 
 | ||
|     if ((FLASH->OPTR & 0x1000) == 0x00)                   // Test if IWDG is running (IWDG in HW mode)
 | ||
|     {
 | ||
|         // Set IWDG time out to ~32.768 second
 | ||
|         IWDG->KR  = 0x5555;                                 // Enable write access to IWDG_PR and IWDG_RLR
 | ||
|         IWDG->PR  = 0x06;                                   // Set prescaler to 256
 | ||
|         IWDG->RLR = 0xFFF;                                  // Set reload value to 4095
 | ||
|     }
 | ||
| 
 | ||
|     return (0);
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| 
 | ||
| /*
 | ||
|  *  De-Initialize Flash Programming Functions
 | ||
|  *    Parameter:      fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
 | ||
|  *    Return Value:   0 - OK,  1 - Failed
 | ||
|  */
 | ||
| 
 | ||
| #ifdef FLASH_MEM
 | ||
| int UnInit(unsigned long fnc)
 | ||
| {
 | ||
| 
 | ||
| //    UnInitRccAndFlashParam();
 | ||
| 
 | ||
|     FLASH->CR    |=  FLASH_CR_LOCK;                          // Lock Flash
 | ||
| 
 | ||
|     return (0);
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| #ifdef FLASH_OPT
 | ||
| int UnInit(unsigned long fnc)
 | ||
| {
 | ||
| 
 | ||
|     UnInitRccAndFlashParam();
 | ||
| 
 | ||
|     FLASH->CR    |=  FLASH_CR_LOCK;                          // Lock Flash
 | ||
|     FLASH->CR    |=  FLASH_CR_OPTLOCK;                       // Lock Option Bytes
 | ||
| 
 | ||
|     return (0);
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| /*
 | ||
|  *  Erase complete Flash Memory
 | ||
|  *    Return Value:   0 - OK,  1 - Failed
 | ||
|  */
 | ||
| 
 | ||
| #ifdef FLASH_MEM
 | ||
| int EraseChip(void)
 | ||
| {
 | ||
|     FLASH->SR  |= FLASH_SR_EOP;                              // Reset FLASH_EOP
 | ||
| 
 | ||
|     FLASH->CR |=  FLASH_CR_MER;                              // Mass Erase Enabled
 | ||
|     FLASH->CR |=  FLASH_CR_EOPIE;
 | ||
|     M32(0x08000000) = 0xFF;
 | ||
|     __asm("DSB");
 | ||
| 
 | ||
|     while (FLASH->SR & FLASH_SR_BSY)
 | ||
|     {
 | ||
|         IWDG->KR = 0xAAAA;                                  // Reload IWDG
 | ||
|     }
 | ||
| 
 | ||
|     FLASH->CR &= ~FLASH_CR_MER;                              // Mass Erase Disabled
 | ||
|     FLASH->CR &= ~FLASH_CR_EOPIE;                            // Reset FLASH_EOPIE
 | ||
| 
 | ||
|     if (FLASH_SR_EOP != (FLASH->SR & FLASH_SR_EOP))             // Check for FLASH_SR_EOP
 | ||
|     {
 | ||
|         FLASH->SR |= FLASH_SR_EOP;
 | ||
|         return (1);                                         // Failed
 | ||
|     }
 | ||
| 
 | ||
|     return (0);                                           // Done
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| #ifdef FLASH_OPT
 | ||
| int EraseChip(void)
 | ||
| {
 | ||
| 
 | ||
|     /* erase chip is not needed for
 | ||
|        - Flash Option bytes
 | ||
|        - Flash One Time Programmable bytes
 | ||
|     */
 | ||
|     return (0);                                           // Done
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| /*
 | ||
|  *  Erase Sector in Flash Memory
 | ||
|  *    Parameter:      adr:  Sector Address
 | ||
|  *    Return Value:   0 - OK,  1 - Failed
 | ||
|  */
 | ||
| 
 | ||
| #ifdef FLASH_MEM
 | ||
| int EraseSector(unsigned long adr)
 | ||
| {
 | ||
| 
 | ||
|     FLASH->SR  |= FLASH_SR_EOP;                              // Reset FLASH_EOP
 | ||
| 
 | ||
|     FLASH->CR  |=  FLASH_CR_SER;                             // Sector Erase Enabled
 | ||
|     FLASH->CR  |=  FLASH_CR_EOPIE;
 | ||
|     M32(adr) = 0xFF;                                      // Sector Address
 | ||
|     __asm("DSB");
 | ||
| 
 | ||
|     while (FLASH->SR  & FLASH_SR_BSY)
 | ||
|     {
 | ||
|         IWDG->KR = 0xAAAA;                                  // Reload IWDG
 | ||
|     }
 | ||
| 
 | ||
|     FLASH->CR  &= ~FLASH_CR_SER;                             // Sector Erase Disabled
 | ||
|     FLASH->CR  &= ~FLASH_CR_EOPIE;                           // Reset FLASH_EOPIE
 | ||
| 
 | ||
| //  if (FLASH_EOP != (FLASH->SR & FLASH_EOP)) {           // Check for FLASH_SR_EOP
 | ||
| //    FLASH->SR |= FLASH_EOP;
 | ||
| //    return (1);                                         // Failed
 | ||
| //  }
 | ||
| 
 | ||
|     return (0);                                           // Done
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| 
 | ||
| #ifdef FLASH_OPT
 | ||
| int EraseSector(unsigned long adr)
 | ||
| {
 | ||
|     /* erase sector is not needed for
 | ||
|        - Flash Option bytes
 | ||
|        - Flash One Time Programmable bytes
 | ||
|     */
 | ||
|     return (0);                                           // Done
 | ||
| }
 | ||
| #endif
 | ||
| 
 | ||
| /*
 | ||
|  *  Blank Check Checks if Memory is Blank
 | ||
|  *    Parameter:      adr:  Block Start Address
 | ||
|  *                    sz:   Block Size (in bytes)
 | ||
|  *                    pat:  Block Pattern
 | ||
|  *    Return Value:   0 - OK,  1 - Failed
 | ||
|  */
 | ||
| 
 | ||
| int BlankCheck(unsigned long adr, unsigned long sz, unsigned char pat)
 | ||
| {
 | ||
|     return (1);                                            // Always Force Erase
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| /*
 | ||
|  *  Program Page in Flash Memory
 | ||
|  *    Parameter:      adr:  Page Start Address
 | ||
|  *                    sz:   Page Size
 | ||
|  *                    buf:  Page Data
 | ||
|  *    Return Value:   0 - OK,  1 - Failed
 | ||
|  */
 | ||
| 
 | ||
| #ifdef FLASH_MEM
 | ||
| int ProgramPage(unsigned long adr, unsigned long sz, unsigned char *buf)
 | ||
| {
 | ||
| 
 | ||
|     sz = (sz + 127) & ~127;                               // Adjust size for 32 Words
 | ||
| 
 | ||
|     FLASH->SR  |= FLASH_SR_EOP;                              // Reset FLASH_EOP
 | ||
| 
 | ||
|     while (sz)
 | ||
|     {
 | ||
|         FLASH->CR  |=  FLASH_CR_PG;                            // Programming Enabled
 | ||
|         FLASH->CR  |=  FLASH_CR_EOPIE;
 | ||
| 
 | ||
|         for (u8 i = 0; i < 32; i++)
 | ||
|         {
 | ||
| 
 | ||
|             M32(adr + i * 4) = *((u32 *)(buf + i * 4));       // Program the first word of the Double Word
 | ||
|             if (i == 30)
 | ||
|             {
 | ||
|                 FLASH->CR  |= FLASH_CR_PGSTRT;
 | ||
|             }
 | ||
|         }
 | ||
|         __asm("DSB");
 | ||
| 
 | ||
|         while (FLASH->SR & FLASH_SR_BSY)
 | ||
|         {
 | ||
|             IWDG->KR = 0xAAAA;                                // Reload IWDG
 | ||
|         }
 | ||
| 
 | ||
|         FLASH->CR  &= ~FLASH_CR_PG;                            // Programming Disabled
 | ||
|         FLASH->CR  &= ~FLASH_CR_EOPIE;                         // Reset FLASH_EOPIE
 | ||
| 
 | ||
|         //Ϊ<><CEAA><EFBFBD>ܹ<EFBFBD>Program SRAM<41><4D><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>EOP<4F><50><EFBFBD><EFBFBD>
 | ||
| //      if (FLASH_EOP != (FLASH->SR & FLASH_EOP)) {         // Check for FLASH_SR_EOP
 | ||
| //          FLASH->SR |= FLASH_EOP;
 | ||
| //          return (1);                                       // Failed
 | ||
| //      }
 | ||
| 
 | ||
|         adr += 128;                                         // Go to next Page
 | ||
|         buf += 128;
 | ||
|         sz  -= 128;
 | ||
|     }
 | ||
| 
 | ||
|     return (0);                                           // Done
 | ||
| }
 | ||
| #endif // PY32F030xx_64 || FLASH_OTP
 | ||
| 
 | ||
| #ifdef FLASH_OPT
 | ||
| int ProgramPage(unsigned long adr, unsigned long sz, unsigned char *buf)
 | ||
| {
 | ||
| 
 | ||
|     u32 optr;
 | ||
|     u32 sdkr;
 | ||
|     u32 wrpr;
 | ||
| 
 | ||
|     optr = *((u32 *)(buf + 0x00));
 | ||
|     sdkr = *((u32 *)(buf + 0x04));
 | ||
|     wrpr = *((u32 *)(buf + 0x0C));
 | ||
| 
 | ||
|     FLASH->SR |= FLASH_SR_EOP;                            // Reset FLASH_EOP
 | ||
| 
 | ||
|     FLASH->OPTR = (optr & 0x0000FFFF);                 // Write OPTR values
 | ||
|     FLASH->SDKR = (sdkr & 0x0000FFFF);                 // Write SDKR values
 | ||
|     FLASH->WRPR = (wrpr & 0x0000FFFF);                 // Write WRPR values
 | ||
| 
 | ||
|     FLASH->CR |= FLASH_CR_OPTSTRT;
 | ||
|     FLASH->CR   |= FLASH_CR_EOPIE;
 | ||
|     M32(0x40022080) = 0xFF;
 | ||
|     __asm("DSB");
 | ||
| 
 | ||
|     FLASH->CR  &= ~FLASH_CR_OPTSTRT;                      // Programming Disabled
 | ||
|     FLASH->CR  &= ~FLASH_CR_EOPIE;                        // Reset FLASH_EOPIE
 | ||
| 
 | ||
| #ifdef FLASH_OPT_OBL_LAUNCH
 | ||
|     FLASH->CR |= FLASH_CR_OBL_LAUNCH;
 | ||
| #endif
 | ||
| 
 | ||
|     return (0);                                           // Done
 | ||
| }
 | ||
| #endif //  FLASH_OPT
 | ||
| 
 | ||
| 
 | ||
| /*
 | ||
|  *  Verify Flash Contents
 | ||
|  *    Parameter:      adr:  Start Address
 | ||
|  *                    sz:   Size (in bytes)
 | ||
|  *                    buf:  Data
 | ||
|  *    Return Value:   (adr+sz) - OK, Failed Address
 | ||
|  */
 | ||
| 
 | ||
| #ifdef FLASH_OPT
 | ||
| unsigned long Verify(unsigned long adr, unsigned long sz, unsigned char *buf)
 | ||
| {
 | ||
|     u32 optr;
 | ||
|     u32 sdkr;
 | ||
|     u32 wrpr;
 | ||
| 
 | ||
|     optr = *((u32 *)(buf +  0x00));
 | ||
|     sdkr = *((u32 *)(buf +  0x04));
 | ||
|     wrpr = *((u32 *)(buf +  0x0C));
 | ||
| 
 | ||
|     if (M32(adr + 0x00) != optr)
 | ||
|     {
 | ||
|         return (adr + 0x00);
 | ||
|     }
 | ||
|     if (M32(adr + 0x04) != sdkr)
 | ||
|     {
 | ||
|         return (adr + 0x04);
 | ||
|     }
 | ||
|     if (M32(adr + 0x0C) != wrpr)
 | ||
|     {
 | ||
|         return (adr + 0x0C);
 | ||
|     }
 | ||
| 
 | ||
|     return (adr + sz);
 | ||
| }
 | ||
| #endif // FLASH_OPT
 | 
