STM32F1与STM32CubeIDE快速入门-M25P16串行闪存驱动

M25P16串行闪存驱动

M25P16 是一款 16 Mbit (2 Mbit × 8) 串行闪存,具有先进的写保护机制,可通过高速 SPI 兼容总线访问。 使用页编程指令可以一次对存储器进行 1 到 256 个字节的编程。

内存由 32 个扇区组成,每个扇区包含 256 页。 每页宽 256 字节。 因此,整个内存可以被视为由 8192 个页面或 2097152 个字节组成。

M25P16的特性如下:

  • 16 Mb 闪存。
  • 页程序(最多 256 个字节)在 0.64 毫秒(典型值)内。
  • 扇区擦除 (512 Kbit) 在 0.6 秒内(典型值)。
  • 批量擦除 (16 Mbit) 在 13 秒内(典型值)。
  • 2.7 V 至 3.6 V 单电源电压。
  • SPI 总线兼容的串行接口。
  • 75 MHz 时钟频率(最大值)。
  • 深度掉电模式 1 μA(典型值)。
  • 电子签名
    • JEDEC 标准两字节签名(2015h)。
    • 具有 16 字节只读的唯一 ID 代码 (UID),可根据客户要求提供。
    • RES 指令,单字节,签名 (14h),用于向后兼容。
  • 每个扇区超过 100,000 次擦除/编程周期。
  • 硬件写保护:由三个非易失性位(BP0、BP1 和 BP2)定义的保护区大小。
  • 超过 20 年的数据保留。

在这里插入图片描述

本次实例将实现M25P16串行闪存驱动。

1、SPI配置

在这里插入图片描述

创建工程及系统时钟配置请参考前面文章,在这里不再做介绍。

M25P16与STM32接线图如下:

1)SPI参数配置

在这里插入图片描述

保存配置并生成代码。

2、代码实现

2.1 M25P16基本定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// m25p16.h
#ifndef \_\_DRIVER\_M25P16\_H\_
#define \_\_DRIVER\_M25P16\_H\_


#include "stm32f1xx.h"

void m25p16_drv_init (void);
HAL_StatusTypeDef m25p16_read_id (uint32_t \*id);
HAL_StatusTypeDef m25p16_erase_sector (uint32_t address);
HAL_StatusTypeDef m25p16_erase_bulk (void);
HAL_StatusTypeDef m25p16_read (uint32_t address, uint8_t\* buff, uint32_t len);
HAL_StatusTypeDef m25p16_write_page (uint32_t address, uint8_t\* buff, uint32_t len);
HAL_StatusTypeDef m25p16_write (uint32_t address, uint8_t\* buff, uint32_t len);


#endif /\* \_\_DRIVER\_N25P16\_H\_ \*/


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
#include "m25p16.h"
#include "stm32f1xx\_hal\_gpio.h"
#include "stm32f1xx\_hal\_spi.h"
#include <stdio.h>

extern SPI_HandleTypeDef hspi1;
// SPI操作函数封装
typedef struct _base_func_t {
HAL_StatusTypeDef (\*init)(SPI_HandleTypeDef \*hspi);
// SPI发送数据
HAL_StatusTypeDef (\*send)(SPI_HandleTypeDef \*hspi, uint8_t \*pData,
uint16_t Size, uint32_t Timeout);
// SPI接收数据
HAL_StatusTypeDef (\*recv)(SPI_HandleTypeDef \*hspi, uint8_t \*pData,
uint16_t Size, uint32_t Timeout);
// 片选引脚低电平
void (\*cs_low)();
// 片选引脚高电平
void (\*cs_high)();
} _base_func;

// SPI总线封装
typedef struct _spi_bus_t {
SPI_HandleTypeDef handle;
_base_func func; // SPI操作函数
GPIO_TypeDef \*port;
GPIO_InitTypeDef mio;
} _spi_bus;

// M25P16设备定义
typedef struct _m25p16_dev_t {
// SPI总线
_spi_bus spi_bus;
// 发送命令
HAL_StatusTypeDef (\*command_send)(uint8_t, uint8_t\*, uint8_t);
// 使能写
HAL_StatusTypeDef (\*write_enable)();
// 读取状态寄存器
HAL_StatusTypeDef (\*read_stat_reg)(uint8_t\*);
// 等待指定时长
HAL_StatusTypeDef (\*wait_until_to)(uint32_t);

// 读取M25P16序列号
HAL_StatusTypeDef (\*read_id)(uint8_t\*, uint8_t);
// 数据数据
HAL_StatusTypeDef (\*read_data)(uint8_t\*, uint8_t\*, uint32_t);
// 擦除扇区
HAL_StatusTypeDef (\*erase_sector)(uint8_t\*);
// 批量擦除
HAL_StatusTypeDef (\*erase_bulk)();
// 按页面编程
HAL_StatusTypeDef (\*page_program)(uint8_t\*, uint8_t\*, uint32_t);
} _m25p16_dev;


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
#define M25P16\_DBG(arg) printf("[SPI-M25P16] %s(), %s.", \_\_func\_\_, arg)

// M25P16寄存和命令定义
#define M25P16\_PAGE\_SIZE ( 256 ) /\* 页大小 256B \*/
#define M25P16\_PAGE\_MASK ( 0xFFFFFF00 )
#define M25P16\_SECTOR\_SIZE ( 0x00010000 ) /\* 扇区大小 64KB \*/
#define M25P16\_SECTOR\_MASK ( 0xFFFF0000 )
#define M25P16\_MAX\_SIZE ( 0x00200000 )

#define COMMAND\_INSTR\_WREN ( 0x06 )
#define COMMAND\_INSTR\_WRDI ( 0x04 )
#define COMMAND\_INSTR\_RDID ( 0x9F )
#define COMMAND\_INSTR\_RDSR ( 0x05 )
#define COMMAND\_INSTR\_WRSR ( 0x01 )
#define COMMAND\_INSTR\_READ ( 0x03 )
#define COMMAND\_INSTR\_FAST\_READ ( 0x0B )
#define COMMAND\_INSTR\_PP ( 0x02 )
#define COMMAND\_INSTR\_SE ( 0xD8 )
#define COMMAND\_INSTR\_BE ( 0xC7 )
#define COMMAND\_INSTR\_DP ( 0xB9 )
#define COMMAND\_INSTR\_RES ( 0xAB )

#define STATUS\_REG\_WIP\_Pos ( 0 )
#define STATUS\_REG\_WIP\_Mask ( 1 << STATUS\_REG\_WIP\_Pos )
#define STATUS\_REG\_WIP ( STATUS\_REG\_WIP\_Mask )
#define STATUS\_REG\_WEL\_Pos ( 1 )
#define STATUS\_REG\_WEL\_Mask ( 1 << STATUS\_REG\_WEL\_Pos )
#define STATUS\_REG\_WEL ( STATUS\_REG\_WEL\_Mask )
#define STATUS\_REG\_BP0\_Pos ( 2 )
#define STATUS\_REG\_BP0\_Mask ( 1 << STATUS\_REG\_BP0\_Pos )
#define STATUS\_REG\_BP0 ( STATUS\_REG\_BP0\_Mask )
#define STATUS\_REG\_BP1\_Pos ( 3 )
#define STATUS\_REG\_BP1\_Mask ( 1 << STATUS\_REG\_BP1\_Pos )
#define STATUS\_REG\_BP1 ( STATUS\_REG\_BP1\_Mask )
#define STATUS\_REG\_BP2\_Pos ( 4 )
#define STATUS\_REG\_BP2\_Mask ( 1 << STATUS\_REG\_BP2\_Pos )
#define STATUS\_REG\_BP2 ( STATUS\_REG\_BP2\_Mask )
#define STATUS\_REG\_SRWD\_Pos ( 7 )
#define STATUS\_REG\_SRWD\_Mask ( 1 << STATUS\_REG\_SRWD\_Pos )
#define STATUS\_REG\_SRWD ( STATUS\_REG\_SRWD\_Mask )

2.2 SPI总线及配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static _m25p16_dev _g_m26p16_flash;

static void \_spi\_mio\_cfg(void) {

}
static void \_spi\_mio\_cs\_low() {
HAL\_GPIO\_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
}
static void \_spi\_mio\_cs\_high() {
HAL\_GPIO\_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
}
static void \_spi\_hal\_cfg(void) {
_spi_bus \*spi_bus = &_g_m26p16_flash.spi_bus;
spi_bus->handle = hspi1;
}

2.3 SPI总线发送数据

1
2
3
4
5
6
7
8
9
10
HAL_StatusTypeDef \_\_m25p16\_data\_recv(uint8_t \*buff, uint32_t len) {
if (_g_m26p16_flash.spi_bus.func.recv(&_g_m26p16_flash.spi_bus.handle, buff,
len, 300) != HAL_OK) {
M25P16\_DBG("[SPI] Fail to receive data");
return (HAL_ERROR);
}

return (HAL_OK);
}

2.4 SPI总线接收数据

1
2
3
4
5
6
7
8
9
10
HAL_StatusTypeDef \_\_m25p16\_data\_send(uint8_t \*buff, uint32_t len) {
if (_g_m26p16_flash.spi_bus.func.send(&_g_m26p16_flash.spi_bus.handle, buff,
len, 300) != HAL_OK) {
M25P16\_DBG("[SPI] Fail to send data");
return (HAL_ERROR);
}

return (HAL_OK);
}

2.5 M25P16发送指令

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
 /\*\* 
\* 内部接口,通过 SPI 总线发送指令以及后续的偏移地址
\* in: command Flash 指令
\* in: data 3Byte 长度的地址信息,可选
\* in: len 地址长度,可选
\*/
HAL_StatusTypeDef \_m25p16\_command\_send(uint8_t command, uint8_t \*data,
uint8_t len) {
uint8_t comm[1] = { command };

if (\_\_m25p16\_data\_send(comm, 1) != HAL_OK) {
goto _error;
}

if (data == NULL || len == 0) {
goto _success;
}

if (\_\_m25p16\_data\_send(data, len) != HAL_OK) {
goto _error;
}

_success: return (HAL_OK);

_error:
M25P16\_DBG("failed to send data");
return (HAL_ERROR);
}

2.6 M25P16发送使能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/\*
\* 内部接口,发送写使能指令,在擦除、写页等指令执行前需要先发送写使能指令
\*/
HAL_StatusTypeDef \_m25p16\_write\_enable(void) {
_g_m26p16_flash.spi_bus.func.cs\_low();

uint8_t command = COMMAND_INSTR_WREN;
if (\_m25p16\_command\_send(command, NULL, 0) != HAL_OK) {
M25P16\_DBG("failed to send command");
goto _error;
}

_g_m26p16_flash.spi_bus.func.cs\_high();
return (HAL_OK);

_error: _g_m26p16_flash.spi_bus.func.cs\_high();
return (HAL_ERROR);
}


2.7 读取状态寄存器

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
/\*
\* 内部接口,读取 Flash 状态寄存器
\* out: data 读取、存储状态寄存器内容
\*/
HAL_StatusTypeDef \_m25p16\_read\_stat\_reg(uint8_t \*data) {
if (data == NULL) {
// debug info
goto _error;
}

_g_m26p16_flash.spi_bus.func.cs\_low();

uint8_t command = COMMAND_INSTR_RDSR;
if (\_m25p16\_command\_send(command, NULL, 0) != HAL_OK) {
M25P16\_DBG("failed to send command");
goto _error;
}

if (\_\_m25p16\_data\_recv(data, 1) != HAL_OK) {
M25P16\_DBG("failed to receive data");
goto _error;
}

_g_m26p16_flash.spi_bus.func.cs\_high();
return (HAL_OK);

_error: _g_m26p16_flash.spi_bus.func.cs\_high();
return (HAL_ERROR);
}

2.8 M25P16忙状态检测

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
/\*
\* 内部接口,读取 Flash 状态寄存器,判断 Flash 是否正忙并带有超时限制
\* out: timeout 超时事件,单位毫秒
\*/
HAL_StatusTypeDef \_m25p16\_wait\_flash\_until\_to(uint32_t timeout) {
uint8_t status_reg = 0;
uint32_t tick_old = HAL\_GetTick();
uint32_t to = timeout;

do {
if (\_m25p16\_read\_stat\_reg(&status_reg) != HAL_OK) {
M25P16\_DBG("failed to read stat reg");
goto _error;
}

if ((status_reg & STATUS_REG_WIP_Mask) == 0) {
break;
}

} while (HAL\_GetTick() - tick_old < to);

if (HAL\_GetTick() - tick_old >= to) {
M25P16\_DBG("check flash busy, and timeout");
goto _error;
}

return (HAL_OK);

_error: return (HAL_ERROR);
}

2.9 读取M25P16序列号

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
/\*
\* 内部接口,用于读取 Flash ID
\* out: buff 缓冲指针
\* out: len 读取的数据长度,长度 <= 3Byte
\*/
HAL_StatusTypeDef \_m25p16\_read\_id(uint8_t \*buff, uint8_t len) {
_g_m26p16_flash.spi_bus.func.cs\_low();

// send command
uint8_t command = COMMAND_INSTR_RDID;
if (\_m25p16\_command\_send(command, NULL, 0) != HAL_OK) {
M25P16\_DBG("failed to send command");
goto _error;
}

// read data
if (\_\_m25p16\_data\_recv(buff, len) != HAL_OK) {
M25P16\_DBG("failed to receive data");
goto _error;
}

_g_m26p16_flash.spi_bus.func.cs\_high();
return (HAL_OK);

_error: _g_m26p16_flash.spi_bus.func.cs\_high();
return (HAL_ERROR);
}

2.10 M25P16擦除指定地址扇区

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
/\*
\* 内部接口,擦除指定地址所在的扇区(64KB)
\*/
HAL_StatusTypeDef \_m25p16\_erase\_sector(uint8_t \*address) {
if (\_m25p16\_write\_enable() != HAL_OK) {
M25P16\_DBG("failed to write enable");
goto _error2;
}

_g_m26p16_flash.spi_bus.func.cs\_low();

uint8_t command = COMMAND_INSTR_SE;
if (\_m25p16\_command\_send(command, address, 3) != HAL_OK) {
M25P16\_DBG("failed to send command");
goto _error;
}

_g_m26p16_flash.spi_bus.func.cs\_high();

if (\_m25p16\_wait\_flash\_until\_to(800) != HAL_OK) {
M25P16\_DBG("check flash busy, and timeout");
goto _error2;
}

return (HAL_OK);

_error: _g_m26p16_flash.spi_bus.func.cs\_high();
_error2: return (HAL_ERROR);
}


2.11 M25P16擦除整个储存区域

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
/\*
\* 内部接口,对 Flash 进行全片擦除
\*/
HAL_StatusTypeDef \_m25p16\_erase\_bulk(void) {
if (\_m25p16\_write\_enable() != HAL_OK) {
M25P16\_DBG("failed to write enable");
goto _error2;
}

_g_m26p16_flash.spi_bus.func.cs\_low();

uint8_t command = COMMAND_INSTR_BE;
if (\_m25p16\_command\_send(command, NULL, 0) != HAL_OK) {
M25P16\_DBG("failed to send command");
goto _error;
}

_g_m26p16_flash.spi_bus.func.cs\_high();

if (\_m25p16\_wait\_flash\_until\_to(20 \* 1000) != HAL_OK) {
M25P16\_DBG("check flash busy, and timeout");
goto _error2;
}

return (HAL_OK);

_error: _g_m26p16_flash.spi_bus.func.cs\_high();
_error2: return (HAL_ERROR);
}

2.12 M25P16页面编程

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
/\*
\* 内部接口,通过页编程 PP 写 Flash,最大写入 256B,偏移地址不是页对齐,会出现卷绕。
\* in: address 欲写入的地址,地址长度为 3Byte
\* in: buff 写入的内容指针
\* in: len 数据长度
\*/
HAL_StatusTypeDef \_m25p16\_page\_program(uint8_t \*address, uint8_t \*buff,
uint32_t len) {
if (\_m25p16\_write\_enable() != HAL_OK) {
M25P16\_DBG("failed to write enable");
goto _error2;
}

_g_m26p16_flash.spi_bus.func.cs\_low();

uint8_t command = COMMAND_INSTR_PP;
if (\_m25p16\_command\_send(command, address, 3) != HAL_OK) {
M25P16\_DBG("failed to send command");
goto _error;
}

if (\_\_m25p16\_data\_send(buff, len) != HAL_OK) {
M25P16\_DBG("failed to send data");
goto _error;
}

_g_m26p16_flash.spi_bus.func.cs\_high();

if (\_m25p16\_wait\_flash\_until\_to(800) != HAL_OK) {
M25P16\_DBG("check flash busy, and timeout");
goto _error2;
}

return (HAL_OK);

_error: _g_m26p16_flash.spi_bus.func.cs\_high();

_error2: return (HAL_ERROR);
}

2.13 M25P16设备初始化

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
/\*
\* 外部接口,初始化 SPI 总线接口,并注册 Flash 相关回调函数。
\*/
void m25p16\_drv\_init(void) {
_spi_bus \*spi_bus = &_g_m26p16_flash.spi_bus;

// reg func hook
spi_bus->func.init = HAL_SPI_Init;
spi_bus->func.recv = HAL_SPI_Receive;
spi_bus->func.send = HAL_SPI_Transmit;
spi_bus->func.cs_high = _spi_mio_cs_high;
spi_bus->func.cs_low = _spi_mio_cs_low;

/\* low level init \*/
\_spi\_mio\_cfg();

/\* spi reg init \*/
\_spi\_hal\_cfg();

/\* 默认拉高 SPI 片选 \*/
spi_bus->func.cs\_high();

/\* M25P16 内部回调注册,外部接口只能够调用已注册的回调 \*/
_g_m26p16_flash.command_send = _m25p16_command_send;
_g_m26p16_flash.write_enable = _m25p16_write_enable;
_g_m26p16_flash.wait_until_to = _m25p16_wait_flash_until_to;

_g_m26p16_flash.read_id = _m25p16_read_id;
_g_m26p16_flash.read_stat_reg = _m25p16_read_stat_reg;
_g_m26p16_flash.read_data = _m25p16_read_data;
_g_m26p16_flash.erase_sector = _m25p16_erase_sector;
_g_m26p16_flash.erase_bulk = _m25p16_erase_bulk;
_g_m26p16_flash.page_program = _m25p16_page_program;
}


2.14 读取序列号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/\*
\* 外部接口,用于读取 Flash 的内部唯一 ID。
\* out: id 读取ID后保存到此地址所指定的位置
\*/
HAL_StatusTypeDef m25p16\_read\_id(uint32_t \*id) {
uint8_t buff[3] = { 0 };
if (_g_m26p16_flash.read_id == NULL) {
// debug info
return (HAL_ERROR);
}

if (_g_m26p16_flash.read\_id(buff, 3) != HAL_OK) {
// debug info
return (HAL_ERROR);
}

\*id = (buff[0] << 16) + (buff[1] << 8) + buff[2];
return (HAL_OK);
}

2.15 擦除指定地址所在扇区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/\*
\* 外部接口,用于擦除指定地址所在扇区(64KB)的内容。
\* in: address 欲擦出的 Flash 扇区地址,地址若非 64KB 对齐,会启动向前对齐。
\*/
HAL_StatusTypeDef m25p16\_erase\_sector(uint32_t address) {
// 将传入的地址向前进行扇区对齐
uint32_t sector_start_addr = address & M25P16_SECTOR_MASK;
uint8_t tmp_addr[3] = { ((sector_start_addr & 0x00ff0000) >> 16) & 0xff,
((sector_start_addr & 0x0000ff00) >> 8) & 0xff, ((sector_start_addr
& 0x000000ff)) & 0xff };

if (_g_m26p16_flash.erase_sector != NULL)
return _g_m26p16_flash.erase\_sector(tmp_addr);

return (HAL_ERROR);
}

2.16 擦除整个芯片

1
2
3
4
5
6
7
8
9
10
11
/\*
\* 外部接口,擦除整个芯片,此操作大致会耗时 18s,谨慎操作。
\*/
HAL_StatusTypeDef m25p16\_erase\_bulk() {
if (_g_m26p16_flash.erase_bulk != NULL)
return _g_m26p16_flash.erase\_bulk();

return (HAL_ERROR);
}


2.17 从指定地址读取指定长度数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/\*
\* 外部接口,从指定地址读取一定长度的数据。
\* in: address Flash 偏移地址
\* in: buff 数据缓冲指针
\* in: len 读取长度
\*/
HAL_StatusTypeDef m25p16\_read(uint32_t address, uint8_t \*buff, uint32_t len) {
uint8_t tmp_addr[3] = { ((address & 0x00ff0000) >> 16) & 0xff, ((address
& 0x0000ff00) >> 8) & 0xff, ((address & 0x000000ff)) & 0xff };

if (_g_m26p16_flash.read_data != NULL) {
return _g_m26p16_flash.read\_data(tmp_addr, buff, len);
}

return (HAL_ERROR);
}

2.18 写页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/\*
\* 外部接口,写入数据到 Flash,写入之前需要先进性擦除操作
\* 注意:写入长度最大不能超过 PAGE\_SIZE,并且会自动将超出页边界之后的裁剪掉。
\* in: address Flash 偏移地址
\* in: buff 外部数据指针
\* in: len 写入长度,长度 <= PAGE\_SIZE
\*/
HAL_StatusTypeDef m25p16\_write\_page(uint32_t address, uint8_t \*buff,
uint32_t len) {
uint8_t offset_addr[3] = { ((address & 0x00ff0000) >> 16) & 0xff, ((address
& 0x0000ff00) >> 8) & 0xff, ((address & 0x000000ff)) & 0xff };

/\* 校验确保写入的数据再一个页(256B)之内,而且不能回卷 \*/
uint32_t page_end_addr = (address & M25P16_PAGE_MASK) + M25P16_PAGE_SIZE;
uint32_t valid_len =
(address + len < page_end_addr) ? (len) : (page_end_addr - address);

if (_g_m26p16_flash.page_program != NULL)
return _g_m26p16_flash.page\_program(offset_addr, buff, valid_len);

return (HAL_ERROR);
}

2.19 指定地址写入指定长度数据

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
/\*
\* 外部接口,写入数据到 Flash,写入之前需要先进性擦除操作
\* in: address Flash 偏移地址
\* in: buff 外部数据指针
\* in: len 写入长度,数据长度不受页长度限制
\*/
HAL_StatusTypeDef m25p16\_write(uint32_t address, uint8_t \*buff, uint32_t len) {
if ((address + len >= M25P16_MAX_SIZE) || (buff == NULL)) {
// debug info
goto _error;
}

uint32_t remaining_len = len;
uint32_t current_addr = address;
uint8_t \*buff_pos = buff;

// debug info

while (remaining_len > 0) {
uint32_t tmp_page_end = (current_addr & M25P16_PAGE_MASK)
+ M25P16_PAGE_SIZE;
uint32_t tmp_len =
(tmp_page_end < (current_addr + remaining_len)) ?
(tmp_page_end - current_addr) : (remaining_len);

if (m25p16\_write\_page(current_addr, buff_pos, tmp_len) != HAL_OK) {
M25P16\_DBG("failed to write page");
goto _error;
}

remaining_len -= tmp_len;
current_addr += tmp_len;
buff_pos += tmp_len;
}

return (HAL_OK);

_error: return (HAL_ERROR);
}

2.20 主程序

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
/\* USER CODE END Header \*/
/\* Includes ------------------------------------------------------------------\*/
#include "main.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"

/\* Private includes ----------------------------------------------------------\*/
/\* USER CODE BEGIN Includes \*/
#include "m25p16/m25p16.h"
#include <stdio.h>
/\* USER CODE END Includes \*/

/\* Private typedef -----------------------------------------------------------\*/
/\* USER CODE BEGIN PTD \*/
uint8_t data[4] = { "ABCD" };
uint8_t output[4];
//flash\_identification\_t flash\_info;
uint32_t flash_id;
/\* USER CODE END PTD \*/

int main(void)
{
/\* USER CODE BEGIN 1 \*/

/\* USER CODE END 1 \*/

/\* MCU Configuration--------------------------------------------------------\*/

/\* Reset of all peripherals, Initializes the Flash interface and the Systick. \*/
HAL\_Init();

/\* USER CODE BEGIN Init \*/

/\* USER CODE END Init \*/

/\* Configure the system clock \*/
SystemClock\_Config();

/\* USER CODE BEGIN SysInit \*/

/\* USER CODE END SysInit \*/

/\* Initialize all configured peripherals \*/
MX\_GPIO\_Init();
MX\_SPI1\_Init();
MX\_USART1\_UART\_Init();
/\* USER CODE BEGIN 2 \*/
//M25P16\_Init();
// flash\_id = M25P16\_Read\_Id(&flash\_info);
// printf(
// "flash\_info:\r\nmanufacturer:%d\r\nmemory\_type:%d\r\nmemory\_capacity:%d\r\ncfd\_length:%d\r\ncfd\_content:%s\r\n",
// flash\_info.manufacturer, flash\_info.memory\_type,
// flash\_info.memory\_capacity, flash\_info.cfd\_length,
// flash\_info.cfd\_content);
m25p16\_drv\_init();
m25p16\_read\_id(&flash_id);
printf("flash id = %u\r\n", flash_id);
m25p16\_write(0x00, data, 4);
m25p16\_read(0x00,output,4);
printf("read:%s\r\n",output);
/\* USER CODE END 2 \*/

/\* Infinite loop \*/
/\* USER CODE BEGIN WHILE \*/
while (1) {
/\* USER CODE END WHILE \*/

/\* USER CODE BEGIN 3 \*/
}
/\* USER CODE END 3 \*/
}


3、运行结果

在这里插入图片描述

驱动代码参考:

文章来源: https://iotsmart.blog.csdn.net/article/details/123565384