本笔记学习 Zephyr 的 Flash 驱动,用于内部 Flash 编程操作。
zephyr/samples/drivers/flash_shell/
| 特性 | 说明 |
|---|---|
| 擦除单位 | 扇区 (Sector) |
| 写入单位 | 页 (Page) 或字节 |
| 擦除后状态 | 全 1 (0xFF) |
| 写入后状态 | 只能从 1 变为 0 |
| 重写 | 需要先擦除 |
nRF54L15 内部 Flash 大小约 1.5 MB。
#include <zephyr/drivers/flash.h>
const struct device *const flash_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash));
struct flash_pages_info info;
uint32_t sector_size;
flash_get_page_info_by_offs(flash_dev, 0, &info);
sector_size = info.size; // 扇区大小
size_t sector_count = flash_get_page_count(flash_dev);
int flash_erase(const struct device *dev, off_t offset, size_t size);
// 擦除 offset 0 开始的一个扇区
int ret = flash_erase(flash_dev, 0, sector_size);
if (ret != 0) {
printk("Flash erase failed! ret = %d\n", ret);
}
int flash_write(const struct device *dev, off_t offset,
const void *data, size_t len);
uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
off_t offset = 0;
int ret = flash_write(flash_dev, offset, data, sizeof(data));
if (ret != 0) {
printk("Flash write failed! ret = %d\n", ret);
}
int flash_read(const struct device *dev, off_t offset,
void *data, size_t len);
uint8_t buf[4];
off_t offset = 0;
int ret = flash_read(flash_dev, offset, buf, sizeof(buf));
if (ret != 0) {
printk("Flash read failed! ret = %d\n", ret);
} else {
printk("Data read: %02x %02x %02x %02x\n",
buf[0], buf[1], buf[2], buf[3]);
}
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/sys/printk.h>
#define TEST_OFFSET 0
#define TEST_DATA 0xAA
int main(void)
{
const struct device *const flash_dev =
DEVICE_DT_GET(DT_CHOSEN(zephyr_flash));
struct flash_pages_info info;
uint8_t buf[1];
int ret;
if (!device_is_ready(flash_dev)) {
printk("Flash device not ready\n");
return 0;
}
// 获取扇区信息
flash_get_page_info_by_offs(flash_dev, TEST_OFFSET, &info);
printk("Sector size: %u bytes\n", info.size);
// 擦除
ret = flash_erase(flash_dev, TEST_OFFSET, info.size);
if (ret != 0) {
printk("Erase failed: %d\n", ret);
return 0;
}
printk("Sector erased\n");
// 写入
buf[0] = TEST_DATA;
ret = flash_write(flash_dev, TEST_OFFSET, buf, 1);
if (ret != 0) {
printk("Write failed: %d\n", ret);
return 0;
}
printk("Data written: 0x%02X\n", TEST_DATA);
// 读取验证
ret = flash_read(flash_dev, TEST_OFFSET, buf, 1);
if (ret != 0) {
printk("Read failed: %d\n", ret);
return 0;
}
if (buf[0] == TEST_DATA) {
printk("Verification OK: 0x%02X\n", buf[0]);
} else {
printk("Verification FAILED: 0x%02X\n", buf[0]);
}
return 0;
}
如果启用了 CONFIG_FLASH_SHELL,可以使用 shell 命令:
# 擦除扇区
flash erase 0 4096
# 写入数据
flash write 0 deadbeef
# 读取数据
flash read 0 16
# 获取扇区信息
flash page_info 0
/ {
chosen {
zephyr,flash = &flash0;
};
};
&flash0 {
status = "okay";
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
storage_partition: partition@0 {
label = "storage";
reg = <0x00000000 0x00010000>;
};
};
};
上面的分区地址和大小只是示例,实际项目里要避开引导区、镜像区和已有存储分区。
cd /home/wangpei/ncs-sdk
west build -p -b nrf54l15dk/nrf54l15/cpuapp zephyr/samples/drivers/flash_shell
west flash
# prj.conf
CONFIG_FLASH=y
CONFIG_FLASH_SHELL=y
CONFIG_FLASH_PAGE_LAYOUT=y
// 使用 Flash 地图 API 更安全
#include <zephyr/storage/flash_map.h>
#define STORAGE_PARTITION storage_partition
// 获取分区信息
const struct flash_area *fa;
flash_area_open(FIXED_PARTITION_ID(STORAGE_PARTITION), &fa);
// 在分区内操作
flash_area_erase(fa, 0, fa->fa_size);
flash_area_write(fa, 0, data, len);
flash_area_read(fa, 0, buf, len);
flash_area_close(fa);
*小白 🤖 - 2026-03-16*