summaryrefslogtreecommitdiff
path: root/bsp/mm32f327x/Libraries/MM32F327x/HAL_Lib/Src/hal_flash.c
blob: ecdc8be3aed552a6cec106861efc19743c048ce8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
////////////////////////////////////////////////////////////////////////////////
/// @file     hal_flash.c
/// @author   AE TEAM
/// @brief    THIS FILE PROVIDES ALL THE FLASH FIRMWARE FUNCTIONS.
////////////////////////////////////////////////////////////////////////////////
/// @attention
///
/// THE EXISTING FIRMWARE IS ONLY FOR REFERENCE, WHICH IS DESIGNED TO PROVIDE
/// CUSTOMERS WITH CODING INFORMATION ABOUT THEIR PRODUCTS SO THEY CAN SAVE
/// TIME. THEREFORE, MINDMOTION SHALL NOT BE LIABLE FOR ANY DIRECT, INDIRECT OR
/// CONSEQUENTIAL DAMAGES ABOUT ANY CLAIMS ARISING OUT OF THE CONTENT OF SUCH
/// HARDWARE AND/OR THE USE OF THE CODING INFORMATION CONTAINED HEREIN IN
/// CONNECTION WITH PRODUCTS MADE BY CUSTOMERS.
///
/// <H2><CENTER>&COPY; COPYRIGHT MINDMOTION </CENTER></H2>
////////////////////////////////////////////////////////////////////////////////

// Define to prevent recursive inclusion
#define _HAL_FLASH_C_

// Files includes
#include "hal_flash.h"

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup MM32_Hardware_Abstract_Layer
/// @{

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup FLASH_HAL
/// @{

////////////////////////////////////////////////////////////////////////////////
/// @addtogroup FLASH_Exported_Functions
/// @{

////////////////////////////////////////////////////////////////////////////////
/// @brief  Sets the code latency value.
/// @note   This function can be used for all MM32 devices.
/// @param  latency: specifies the FLASH Latency value.
///         This parameter can be one of the following values:
/// @arg    FLASH_Latency_0: FLASH Zero Latency cycle
/// @arg    FLASH_Latency_1: FLASH One Latency cycle
/// @arg    FLASH_Latency_2: FLASH Two Latency cycles
/// @arg    FLASH_Latency_3: FLASH Three Latency cycles
/// @retval None.
////////////////////////////////////////////////////////////////////////////////
void FLASH_SetLatency(FLASH_Latency_TypeDef latency)
{
    FLASH->ACR = (FLASH->ACR & (~FLASH_ACR_LATENCY)) | latency;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Enables or disables the Half cycle flash access.
/// @note   This function can be used for all MM32 devices.
/// @param  half_cycle_access: specifies the FLASH Half cycle Access mode.
///         This parameter can be one of the following values:
/// @arg    FLASH_HalfCycleAccess_Enable: FLASH Half Cycle Enable
/// @arg    FLASH_HalfCycleAccess_Disable: FLASH Half Cycle Disable
/// @retval None.
////////////////////////////////////////////////////////////////////////////////
void FLASH_HalfCycleAccessCmd(FLASH_HalfCycleAccess_TypeDef half_cycle_access)
{
    FLASH->ACR &= ~FLASH_ACR_HLFCYA;
    FLASH->ACR |= half_cycle_access;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Enables or disables the Prefetch Buffer.
/// @note   This function can be used for all MM32 devices.
/// @param  prefetch_buffer: specifies the Prefetch buffer status.
///         This parameter can be one of the following values:
/// @arg    FLASH_PrefetchBuffer_Enable: FLASH Prefetch Buffer Enable
/// @arg    FLASH_PrefetchBuffer_Disable: FLASH Prefetch Buffer Disable
/// @retval None.
////////////////////////////////////////////////////////////////////////////////
void FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_TypeDef prefetch_buffer)
{
    FLASH->ACR &= ~FLASH_ACR_PRFTBE;
    FLASH->ACR |= prefetch_buffer;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Locks the FLASH Program Erase Controller.
/// @note   This function can be used for all MM32 devices.
/// @param  None.
/// @retval None.
////////////////////////////////////////////////////////////////////////////////
void FLASH_Lock(void)
{
    FLASH->CR |= FLASH_CR_LOCK;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Unlocks the FLASH Program Erase Controller.
/// @note   This function can be used for all MM32 devices.
/// @param  None.
/// @retval None.
////////////////////////////////////////////////////////////////////////////////
void FLASH_Unlock()
{
    FLASH->KEYR = FLASH_KEY1;
    FLASH->KEYR = FLASH_KEY2;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Enable to program the FLASH Option Byte.
/// @note   This function can be used for all MM32 devices.
/// @param  None.
/// @retval None.
////////////////////////////////////////////////////////////////////////////////
void FLASH_OPTB_Enable(void)
{
    FLASH->OPTKEYR = FLASH_KEY1;
    FLASH->OPTKEYR = FLASH_KEY2;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Erases a specified FLASH page.
/// @note   This function can be used for all MM32 devices.
/// @param  page_address: The page address to be erased.
/// @retval FLASH Status: The returned value can be: FLASH_BUSY,
///         FLASH_ERROR_PG, FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
////////////////////////////////////////////////////////////////////////////////
FLASH_Status FLASH_ErasePage(u32 page_address)
{
    FLASH->CR |= FLASH_CR_PER;
    FLASH->AR = page_address;
    FLASH->CR |= FLASH_CR_STRT;
    return FLASH_WaitForLastOperation(EraseTimeout);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Erases all FLASH pages.
/// @note   This function can be used for all MM32 devices.
/// @param  None.
/// @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,
///         FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
////////////////////////////////////////////////////////////////////////////////
FLASH_Status FLASH_EraseAllPages()
{
    FLASH->AR = FLASH_BASE;
    FLASH->CR |= (FLASH_CR_MER | FLASH_CR_STRT);
    return FLASH_WaitForLastOperation(EraseTimeout);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Erases the FLASH option bytes.
/// @note   This function can be used for all MM32 devices.
/// @param  None.
/// @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,
///         FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
////////////////////////////////////////////////////////////////////////////////
FLASH_Status FLASH_EraseOptionBytes()
{
    FLASH_OPTB_Enable();
    FLASH->AR = OB_BASE;
    FLASH->CR |= (FLASH_CR_OPTER | FLASH_CR_STRT);
    return FLASH_WaitForLastOperation(EraseTimeout);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief  Programs a half word at a specified address.
/// @note   This function can be used for all MM32 devices.
/// @param  address: specifies the address to be programmed.
/// @param  data: specifies the data to be programmed.
/// @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,
///         FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
////////////////////////////////////////////////////////////////////////////////
FLASH_Status FLASH_ProgramHalfWord(u32 address, u16 data)
{
    FLASH->CR |= FLASH_CR_PG;

    *(vu16*)address = data;


    return FLASH_WaitForLastOperation(ProgramTimeout);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Programs a word at a specified address.
/// @note   This function can be used for all MM32 devices.
/// @param  address: specifies the address to be programmed.
/// @param  data: specifies the data to be programmed.
/// @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,
///         FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
////////////////////////////////////////////////////////////////////////////////
FLASH_Status FLASH_ProgramWord(u32 address, u32 data)
{
    FLASH_Status ret = FLASH_ProgramHalfWord(address, data);
    if (ret == FLASH_COMPLETE) {
        ret = FLASH_ProgramHalfWord(address + 2, data >> 16);
    }
    return ret;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Programs a byte at a specified Option Byte Data address.
/// @note   This function can be used for all MM32 devices.
/// @param  address: specifies the address to be programmed.
///         This parameter can be 0x1FFFF804 or 0x1FFFF806.
/// @param  data: specifies the data to be programmed.
/// @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,
///         FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
////////////////////////////////////////////////////////////////////////////////
FLASH_Status FLASH_ProgramOptionByteData(u32 address, u8 data)
{
    FLASH_Status ret;
    __IO u16 temp;
    FLASH_OPTB_Enable();
    FLASH->CR |= FLASH_CR_OPTPG;
    temp = (u16)(~data);
    temp = (temp << 8) & 0xFF00;
    temp = temp | (u16)data;
    address = address & (~0x1);
    *(vu16*)address = temp;
    ret = FLASH_WaitForLastOperation(ProgramTimeout);

    return ret;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Programs a half word at a specified Option Byte Data address.
/// @note   This function can be used for all MM32 devices.
/// @param  address: specifies the address to be programmed.
///         This parameter can be 0x1FFFF804 or 0x1FFFF806.
/// @param  data: specifies the data to be programmed.
/// @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,
///         FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
////////////////////////////////////////////////////////////////////////////////
FLASH_Status FLASH_ProgramOptionHalfWord(u32 address, u16 data)
{
    FLASH_Status ret;
    FLASH_OPTB_Enable();
    FLASH->CR |= FLASH_CR_OPTPG;
    *(vu16*)address = data;
    ret = FLASH_WaitForLastOperation(ProgramTimeout);

    return ret;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Read protection for the specified address
/// @note   This function can be used for all MM32 devices.
/// @retval None.
////////////////////////////////////////////////////////////////////////////////
FLASH_Status FLASH_ProgramProtect(u32 address, u16 data)
{
    return FLASH_ProgramOptionHalfWord(address, data);

//    FLASH_Status ret;
//  ret = FLASH_ProgramOptionHalfWord(address, 0x7F80);
//
//  if (ret == FLASH_COMPLETE) {
//      ret = FLASH_ProgramOptionHalfWord(address + 2, 0xFF00);
//    }
//    return ret;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Write protection for the specified address
/// @note   This function can be used for all MM32 devices.
/// @param  page: specifies the address of the pages to be write
///         protected.
///         This parameter is (0x01 << ((Absolute address - 0x08000000)/0x1000))
/// @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,
///         FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
////////////////////////////////////////////////////////////////////////////////
FLASH_Status FLASH_EnableWriteProtection(u32 page)
{
    FLASH_Status ret;
    u8           i;
    for (i = 0; i < 4; i++) {
        ret = FLASH_ProgramOptionHalfWord((OB_BASE + 8 + i * 2), ~(page >> (i * 8)));
        if (ret != FLASH_COMPLETE) {
            break;
        }
    }
    return ret;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Programs the FLASH User Option Byte: IWDG_SW / RST_STOP / RST_STDBY.
/// @note   This function can be used for all MM32 devices.
/// @param  ob_iwdg: Selects the IWDG mode
/// @param  ob_stop: Reset event when entering STOP mode.
/// @param  standby: Reset event when entering Standby mode.
/// @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,
///         FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
////////////////////////////////////////////////////////////////////////////////
FLASH_Status FLASH_UserOptionByteConfig(OB_IWDG_TypeDef ob_iwdg, OB_STOP_TypeDef ob_stop, OB_STDBY_TypeDef standby)
{
    FLASH_OPTB_Enable();
    FLASH->CR |= FLASH_CR_OPTPG;
    OB->USER = ob_iwdg;
    OB->USER |= ob_stop;
    OB->USER |= standby;
    OB->USER |= 0xF8;
    //    OB->USER = iwdg | stop | stdby | 0xF8;
    return FLASH_WaitForLastOperation(ProgramTimeout);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Returns the FLASH User Option Bytes values.
/// @note   This function can be used for all MM32 devices.
/// @param  None.
/// @retval The FLASH User Option Bytes values:IWDG_SW(Bit0), RST_STOP(Bit1)
///         and RST_STDBY(Bit2).
////////////////////////////////////////////////////////////////////////////////
u32 FLASH_GetUserOptionByte()
{
    return (FLASH->OBR >> 2);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Returns the FLASH Write Protection Option Bytes Register value.
/// @note   This function can be used for all MM32 devices.
/// @param  None.
/// @retval The FLASH Write Protection  Option Bytes Register value.
////////////////////////////////////////////////////////////////////////////////
u32 FLASH_GetWriteProtectionOptionByte()
{
    return (FLASH->WRPR);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief  Checks whether the FLASH Prefetch Buffer status is set or not.
/// @note   This function can be used for all MM32 devices.
/// @param  None.
/// @retval FLASH Prefetch Buffer Status (SET or RESET).
////////////////////////////////////////////////////////////////////////////////
FlagStatus FLASH_GetPrefetchBufferStatus(void)
{
    return (FLASH->ACR & FLASH_ACR_PRFTBS) ? SET : RESET;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief  Enables or disables the specified FLASH interrupts.
/// @note   This function can be used for all MM32 devices.
/// @param  interrupt: specifies the FLASH interrupt sources to be enabled or
///         disabled.
/// @param  state: new state of the specified Flash interrupts.
///         This parameter can be: ENABLE or DISABLE.
/// @retval None.
////////////////////////////////////////////////////////////////////////////////
void FLASH_ITConfig(FLASH_IT_TypeDef interrupt, FunctionalState state)
{
    (state) ? (FLASH->CR |= interrupt) : (FLASH->CR &= ~interrupt);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Checks whether the specified FLASH flag is set or not.
/// @note   This function can be used for all MM32 devices.
/// @param  flag: specifies the FLASH flags to clear.
///         This parameter can be one of the following values:
/// @arg    FLASH_FLAG_BSY: FLASH Busy flag
/// @arg    FLASH_FLAG_PGERR: FLASH Program error flag
/// @arg    FLASH_FLAG_WRPRTERR: FLASH Write protected error flag
/// @arg    FLASH_FLAG_EOP: FLASH End of Operation flag
/// @arg    FLASH_FLAG_OPTERR:  FLASH Option Byte error flag
/// @retval The new state of FLASH_FLAG (SET or RESET).
////////////////////////////////////////////////////////////////////////////////
FlagStatus FLASH_GetFlagStatus(u16 flag)
{
    return ((flag == FLASH_FLAG_OPTERR) ? (FLASH->OBR & FLASH_FLAG_OPTERR) : (FLASH->SR & flag)) ? SET : RESET;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Clears the FLASH's pending flags.
/// @note   This function can be used for all MM32 devices.
/// @param  flag: specifies the FLASH flags to clear.
///         This parameter can be any combination of the following values:
/// @arg    FLASH_FLAG_PGERR: FLASH Program error flag
/// @arg    FLASH_FLAG_WRPRTERR: FLASH Write protected error flag
/// @arg    FLASH_FLAG_EOP: FLASH End of Operation flag
/// @retval None.
////////////////////////////////////////////////////////////////////////////////
void FLASH_ClearFlag(u16 flag)
{
    FLASH->SR = flag;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Returns the FLASH Status.
/// @note   This function can be used for all MM32 devices.
/// @param  None.
/// @retval FLASH Status: The returned value can be: FLASH_BUSY,
///         FLASH_ERROR_PG, FLASH_ERROR_WRP or FLASH_COMPLETE.
////////////////////////////////////////////////////////////////////////////////
FLASH_Status FLASH_GetStatus()
{
    return (FLASH_Status)((FLASH->SR & FLASH_FLAG_BSY))
           ? FLASH_BUSY
           : ((FLASH->SR & FLASH_FLAG_PGERR) ? FLASH_ERROR_PG
              : ((FLASH->SR & FLASH_FLAG_WRPRTERR) ? FLASH_ERROR_WRP : FLASH_COMPLETE));
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Waits for a Flash operation to complete or a TIMEOUT to occur.
/// @note   This function can be used for all MM32 devices
/// @param  time_out: FLASH programming time_out
/// @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,
///         FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
////////////////////////////////////////////////////////////////////////////////
FLASH_Status FLASH_WaitForLastOperation(u32 time_out)
{
    u32          i;
    FLASH_Status ret;
    do {
        ret = FLASH_GetStatus();
        time_out--;
        for (i = 0xFF; i != 0; i--)
            ;
    } while ((ret == FLASH_BUSY) && (time_out != 0x00));

    FLASH->CR = 0;
    FLASH->SR = FLASH_SR_EOP | FLASH_SR_WRPRTERR | FLASH_SR_PGERR;
    return (FLASH_Status)((time_out == 0x00) ? FLASH_TIMEOUT : ret);
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Erases a specified FLASH page.
/// @note   This function can be used for all MM32 devices.
/// @param  Page_Address: The page address to be erased.
/// @retval None.
////////////////////////////////////////////////////////////////////////////////
void exFLASH_EraseEE(u32 page_address)
{
    FLASH_Unlock();
    FLASH_ErasePage(page_address);
    FLASH_Lock();
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Programs a buffer at a specified address.
/// @note   This function can be used for all MM32 devices.
/// @param *buf: the pointer of the buffer to be programmed.
/// @param  addr: specifies the address to be programmed.
/// @param  len: the number of bytes in the buffer.
///         This parameter can only be even.
/// @retval None.
////////////////////////////////////////////////////////////////////////////////
void exFLASH_ProgramEE(u16* buf, u32 addr, u16 len)
{
    u16 i;
    FLASH_Unlock();
    for (i = 0; i < len / 2; i++) {
        FLASH_ProgramHalfWord(addr, *buf);
        addr += 2;
        buf++;
    }
    FLASH_Lock();
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Determine if the data that at the ptr address with the length is len
///         is empty.
/// @note   This function can be used for all MM32 devices.
/// @param *ptr: the pointer of the starting address.
/// @param  len: the number of bytes.
///         This parameter can only be even.
/// @retval 1 presents the data is empty,
///         0 presents the data has been written.
////////////////////////////////////////////////////////////////////////////////
u8 exFLASH_FindEmpty(u16* ptr, u16 len)
{
    u16 i;
    for (i = 0; i < (len / 2); i++) {
        if (*(ptr + i) != 0xffff)
            return 0;
    }
    return 1;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Locate the writable area on the specified address.
/// @note   This function can be used for all MM32 devices.
/// @param  page_address: specifies the beginning of the EEprom.
///         The EEprom can be some continuously pages in the flash.
/// @param  len: the number of bytes to be written.
///         This parameter can only be even.
/// @retval the pointer of the starting address.
////////////////////////////////////////////////////////////////////////////////
void* exFLASH_Locate(u32 page_address, u16 len)
{
    u16 i;
    u16* ptr = (u16*)page_address;
    for (i = 0; i < (0x0800 / len); i++) {
        if (exFLASH_FindEmpty(ptr, len)) {
            if (i == 0)
                return 0;
            break;
        }
        ptr += len / 2;
    }
    return ptr;
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Programs a buffer at a specified address.
/// @note   This function can be used for all MM32 devices.
/// @param *buf: the pointer of the buffer to be programmed.
/// @param  page_address: specifies the beginning of the EEprom.
///         The EEprom can be some continuously pages in the flash.
/// @param  len: the number of bytes in the buffer.
///         This parameter can only be even.
/// @retval None.
////////////////////////////////////////////////////////////////////////////////
void exFLASH_WriteEE(u16* buf, u32 page_address, u16 len)
{
    u16* ptr = exFLASH_Locate(page_address, len);
    if (ptr == 0) {
        exFLASH_EraseEE(page_address + 0x000);
        exFLASH_EraseEE(page_address + 0x400);
        exFLASH_ProgramEE(buf, page_address, len);
    }
    else {
        if (ptr == (u16*)(page_address + ((0x0400 / len) - 1) * len)) {
            exFLASH_EraseEE(page_address + 0x400);
            exFLASH_ProgramEE(buf, (u32)ptr, len);
        }
        else if (ptr == (u16*)(page_address + 0x0800)) {
            exFLASH_EraseEE(page_address + 0x000);
            exFLASH_ProgramEE(buf, (u32)page_address, len);
        }
        else {
            exFLASH_ProgramEE(buf, (u32)ptr, len);
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
/// @brief  Read the beginning address of the last written data.
/// @note   This function can be used for all MM32 devices.
/// @param  page_address: specifies the beginning of the EEprom.
///         The EEprom can be some continuously pages in the flash.
/// @param  len: the number of bytes have been written.
///         This parameter can only be even.
/// @retval the beginning address of the last written data.
///         0 presents that this is the first time to use this as EEprom.
////////////////////////////////////////////////////////////////////////////////
void* exFLASH_ReadEE(u32 page_address, u16 len)
{
    u16* ptr = exFLASH_Locate(page_address, len);
    return (ptr == 0) ? 0 : (ptr - len / 2);
}

/// @}

/// @}

/// @}