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
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!