Zephyr OS Nordic芯片的Flash 操作

目录

概述

[1. 软硬件环境](#1. 软硬件环境)

[1.1 软件开发环境](#1.1 软件开发环境)

[1.2 硬件环境](#1.2 硬件环境)

[2 Flash操作库函数](#2 Flash操作库函数)

[2.1 nRF52832的Flash](#2.1 nRF52832的Flash)

[2.2 Nordic 特有的 Flash 操作](#2.2 Nordic 特有的 Flash 操作)

[2.2.1 nrfx_nvmc_bytes_write 函数](#2.2.1 nrfx_nvmc_bytes_write 函数)

[2.2.2 nrfx_nvmc_page_erase函数](#2.2.2 nrfx_nvmc_page_erase函数)

[2.2.3 nrfx_nvmc_write_done_check 函数](#2.2.3 nrfx_nvmc_write_done_check 函数)

[3 操作Flash的接口函数](#3 操作Flash的接口函数)

[3.1 接口实现](#3.1 接口实现)

[3.2 函数接口](#3.2 函数接口)

[4 验证](#4 验证)

[4.1 读取Flash的参数](#4.1 读取Flash的参数)

[4.2 大容量数据读写操作](#4.2 大容量数据读写操作)

[5 使用Zephyr的Flash接口实现驱动](#5 使用Zephyr的Flash接口实现驱动)

[5.1 代码实现](#5.1 代码实现)

[5.2 测试代码实现](#5.2 测试代码实现)

[5.3 验证功能](#5.3 验证功能)


概述

本文主要介绍基于 Zephyr RTOS 操作 Nordic 芯片的 Flash 存储器,其主要涉及以下几个方面:Nordic 内部Flash的资源,操作Flash的接口,验证读写数据功能等内容。

1. 软硬件环境

1.1 软件开发环境

nordic提供了基于zephyr平台sdk, 其提供了大量的demo可供开发者参考和使用,同时nordi还提供一个集成的软件库工具,方便开发者安装相应的SDK和编译工具链。集成环境同时包含了其他的一些软件,非常便于进行项目开发。

|---------------------------|----------------------|----------|
| 软件工具 | 功能 | 版本信息 |
| nRF Connect SDK | nordic提供基于zephyr的代码库 | v2.9.0 |
| nRF Connect SDK Toolchain | 代码编译工具 | v2.9.1 |
| VS-CODE | 集成开发环境 | v1.99.3 |
| nRF Connect for Desktop | nordic集成工具链 | v5.1.0 |
| nRF Connect | 手机App | |

手机App下载地址:

bash 复制代码
https://nav.nordicsemi.com/search?query=nRF%20Connect

1.2 硬件环境

本案例是在nRF52832开发板(nRF52-DK)上实现的,该开发板nRF52832的主要特点如下:

1)板载j-link调试接口

2)引出所有 IO接口,用户可根据实际应用,外载其他设备

3)支持4个LED

4)支持4路Key接口

5)板载UART调试接口,方便打印调试信息

2 Flash操作库函数

2.1 nRF52832的Flash

在 Zephyr 的设备树中配置 Flash如下,本文以nRF52832的设备树为例:

具体设备树代码如下:

cpp 复制代码
&flash0 {

	partitions {
		compatible = "fixed-partitions";
		#address-cells = <1>;
		#size-cells = <1>;

		boot_partition: partition@0 {
			label = "mcuboot";
			reg = <0x00000000 0xc000>;
		};
		slot0_partition: partition@c000 {
			label = "image-0";
			reg = <0x0000C000 0x37000>;
		};
		slot1_partition: partition@43000 {
			label = "image-1";
			reg = <0x00043000 0x37000>;
		};
		storage_partition: partition@7a000 {
			label = "storage";
			reg = <0x0007a000 0x00006000>;
		};
	};

2.2 Nordic 特有的 Flash 操作

2.2.1 nrfx_nvmc_bytes_write 函数

nrfx_nvmc_bytes_write 是 Nordic 提供的用于向 Flash 写入数据的底层函数,属于 nrfx 驱动库的一部分。这个函数提供了比 Zephyr 通用 Flash API 更底层的访问方式。

函数原型

cpp 复制代码
nrfx_err_t nrfx_nvmc_bytes_write(uint32_t addr, const void *p_src, uint32_t num_bytes);

参数说明

参数 类型 描述
addr uint32_t Flash 中要写入的目标地址
p_src const void * 包含要写入数据的源缓冲区指针
num_bytes uint32_t 要写入的字节数

返回值

返回 nrfx_err_t 类型,可能的值为:

  • NRFX_SUCCESS - 写入成功完成

  • NRFX_ERROR_INVALID_ADDR - 提供的地址无效

  • NRFX_ERROR_INVALID_LENGTH - 请求的长度无效

重要注意事项

  1. 地址对齐

    • 虽然函数名为 bytes_write,但实际上写入操作是以32位字为单位进行的

    • 地址必须是4字节对齐的(addr % 4 == 0)

    • 长度也必须是4的倍数

  2. Flash状态

    • 目标区域必须已经被擦除(全为0xFF)

    • 只能将1改为0(不能将0改为1)

  3. 中断影响

    • Flash写入期间CPU会被暂停

    • 建议在写入关键代码段时禁用中断

2.2.2 nrfx_nvmc_page_erase函数

nrfx_nvmc_page_erase 是 Nordic nRF 系列芯片提供的用于擦除 Flash 页面的底层函数,属于 nrfx 驱动库的一部分。这个函数执行的是对整个 Flash 页的擦除操作。

函数原型

cpp 复制代码
void nrfx_nvmc_page_erase(uint32_t address);

参数说明

参数 类型 描述
address uint32_t 要擦除的 Flash 页中的任意地址

注意事项:

  1. 页面大小

    • 不同 nRF 芯片的 Flash 页面大小不同

    • nRF51 系列:1024 字节 (1KB)

    • nRF52 系列:4096 字节 (4KB)

    • 可以使用 NRF_FICR->CODEPAGESIZE 获取实际的页面大小

  2. 地址对齐

    • 地址参数不需要严格对齐到页面起始地址

    • 函数会自动对齐到包含该地址的页面起始地址

  3. 擦除效果

    • 擦除后,整个页面的所有位将被设置为 1 (0xFF)

    • 擦除是写入操作的必要前提

2.2.3 nrfx_nvmc_write_done_check 函数

nrfx_nvmc_write_done_check 是 Nordic nRF 系列芯片提供的用于检查 Flash 写入/擦除操作是否完成的辅助函数,属于 nrfx 驱动库的一部分。

函数原型

cpp 复制代码
bool nrfx_nvmc_write_done_check(void);

功能说明

  1. 主要用途

    • 检查 NVMC (Non-Volatile Memory Controller) 是否已完成前一次 Flash 编程或擦除操作

    • 提供非阻塞式的操作完成状态检查机制

  2. 返回值

    • true:表示所有挂起的 Flash 操作已完成

    • false:表示 Flash 操作仍在进行中

  3. 底层原理

    • 通过检查 NVMC 的 READY 寄存器位来确定操作状态

    • 对应寄存器位:NRF_NVMC->READY

3 操作Flash的接口函数

3.1 接口实现

在Zephyr OS框架下使用Flash的相关接口,需要做如下配置:

1)在.conf文件中使能Flash的操作接口

bash 复制代码
CONFIG_NRFX_NVMC=y
CONFIG_FLASH=y

CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y

2)引用相关的头文件

cpp 复制代码
#include <zephyr/kernel.h>
#include <zephyr/drivers/flash.h>
#include <nrfx_nvmc.h>

3.2 函数接口

1)写操作

cpp 复制代码
u32 user_flash_write(u32 addr, const u8 *buf, u32 size)
{
    nrfx_nvmc_bytes_write(addr, buf, size);

    while(nrfx_nvmc_write_done_check() == false)
        ;

    return size;
}

2)读操作

cpp 复制代码
u32 user_flash_read(u32 addr, u8 *buf, u32 size)
{
    u32 i;
    const uint8_t *p = (const uint8_t *)addr;

    for (i = 0; i < size; i ++) {
        *buf ++ = *p ++;
    }

    return size;
}

3)擦除操作

cpp 复制代码
s8 user_flash_erase(u32 addr)
{
    nrfx_err_t err_code = nrfx_nvmc_page_erase(addr);

    if (err_code != NRFX_SUCCESS)
    {
        LOG_ERR("Erase flash page(0x%08x) err_code(0x%08x)", addr, err_code);
    }

    while(nrfx_nvmc_write_done_check() == false)
    {
        // wait for erase to complete
    }

    return 0;
}

4 验证

4.1 读取Flash的参数

实现函数接口如下:

cpp 复制代码
void user_drv_flash_msg( void )
{
    flash_msg.paga_size =  nrfx_nvmc_flash_page_size_get();
    flash_msg.total_bytes = nrfx_nvmc_flash_size_get();
    flash_msg.total_page = nrfx_nvmc_flash_page_count_get();

    printf("Flash page size:        %d bytes \r\n", flash_msg.paga_size );
	printf("Total flash size:       %d bytes \r\n", flash_msg.total_bytes );
	printf("Total flash page count: %d \r\n ", flash_msg.total_page);
}

验证结果如下:

4.2 大容量数据读写操作

实现一个写大容量的数据函数:

cpp 复制代码
#define NOR_FLASH_PAGE_SIZE      4096

u8 wrflash_buff[NOR_FLASH_PAGE_SIZE];
void nor_flash_Write( u32 WriteAddr, u8 *pBuffer, u16 NumByteToWrite)   
{ 
    u32 secpos;
    u16 secoff;
    u16 secremain;
    u16 i;

    secpos = WriteAddr/NOR_FLASH_PAGE_SIZE;
    secoff = WriteAddr%NOR_FLASH_PAGE_SIZE;
    secremain = NOR_FLASH_PAGE_SIZE-secoff;

    if(NumByteToWrite <= secremain)
        secremain = NumByteToWrite;
    
    while(1) 
    {
            user_flash_read(secpos*NOR_FLASH_PAGE_SIZE, wrflash_buff, NOR_FLASH_PAGE_SIZE); 
            for( i=0; i<secremain; i++)
            {
                if(wrflash_buff[secoff+i]!=0XFF)
                     break;  
            }

            if(i<secremain)
            {
                  user_flash_erase(secpos*NOR_FLASH_PAGE_SIZE);
                  for(i=0;i<secremain;i++)
                  {
                          wrflash_buff[i+secoff]=pBuffer[i];
                  }
                  user_flash_write(secpos*NOR_FLASH_PAGE_SIZE,wrflash_buff, NOR_FLASH_PAGE_SIZE);
            }
            else
            {
                  user_flash_write( WriteAddr, pBuffer, secremain);
            }    
            
            if( NumByteToWrite == secremain)
                  break;
            else
            {
                  secpos++;
                  secoff=0;

                  pBuffer += secremain; 
                  WriteAddr += secremain;
                  NumByteToWrite -= secremain;
                  if( NumByteToWrite>NOR_FLASH_PAGE_SIZE )
                       secremain = NOR_FLASH_PAGE_SIZE;
                  else
                      secremain = NumByteToWrite;	
            }
    }
}

验证函数:

cpp 复制代码
u8 write_buff[1024];
u8 read_fuff[1024];
void test_flash()
{
    user_drv_flash_msg();

    for( int i = 0; i < 1024; i++ )
    {
        write_buff[i] = 0x5a;
    }

    u32 address = flash_msg.total_bytes - (flash_msg.total_page-3)*4096 - 12;
    nor_flash_Write(address,write_buff,  1024);

    // read buff 
    user_flash_read(address, read_fuff,  1024);

    for( int i = 0; i < 1024; i++ )
    {
        if( read_fuff[i] != write_buff[i])
        {
             printk(" test_flash: failed \n");   
             return;
        }
    }
    printk(" test_flash: pass \n");   

}

验证结果如下:

5 使用Zephyr的Flash接口实现驱动

5.1 代码实现

使用的主要接口:

cpp 复制代码
#include <drivers/flash.h>

// 获取 Flash 设备
const struct device *flash_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller));

// 读取数据
int flash_read(const struct device *dev, off_t offset, void *data, size_t len);

// 写入数据
int flash_write(const struct device *dev, off_t offset, const void *data, size_t len);

// 擦除扇区
int flash_erase(const struct device *dev, off_t offset, size_t size);

5.2 测试代码实现

cpp 复制代码
u8 write_buff[1024];
u8 read_fuff[1024];

const struct device *flash_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller));

void test_flash( void )
{
    u32 address;

    user_drv_flash_msg();

    address = flash_msg.total_bytes - (flash_msg.total_page-3)*4096;
    for( int i = 0; i < 1024; i++ )
    {
        write_buff[i] = 0x5a;
    }

    // 擦除一个页面 (通常4KB)
    flash_erase(flash_dev, address, 4096);
    
    // 写入数据
    flash_write(flash_dev, address, write_buff, sizeof(write_buff));
    
    // 读取验证
    flash_read(flash_dev, address, read_fuff, sizeof(read_fuff));

    for( int i = 0; i < 1024; i++ )
    {
        if( read_fuff[i] != write_buff[i])
        {
             printk(" test_flash: failed \n");   
             return;
        }
    }
    printk(" test_flash: pass \n");   
}

5.3 验证功能

烧写代码,运行后结果如下:

相关推荐
道亦无名7 分钟前
STM32控制电机
stm32·单片机·嵌入式硬件
happygrilclh1 小时前
STM32的ADC模块中,**采样时机(Sampling Time)**和**转换时机(Conversion Time),获取数据的时机详解
stm32·单片机·嵌入式硬件
真的想上岸啊1 小时前
学习51单片机02
嵌入式硬件·学习·51单片机
sword devil9002 小时前
STM32F407VET6实战:CRC校验
stm32·单片机·嵌入式硬件
厦门辰迈智慧科技有限公司2 小时前
城市综合管廊监测与维护一体化解决方案
物联网·自动化·监测
小智学长 | 嵌入式3 小时前
单片机-STM32部分:18、WiFi模组
stm32·单片机·嵌入式硬件
IP管家4 小时前
物联网设备远程管理:基于代理IP的安全固件更新通道方案
服务器·网络·物联网·网络协议·tcp/ip·安全·ip
搬码临时工4 小时前
远程连接电脑的方法?异地远程桌面连接和三方软件实现
运维·服务器·网络·物联网·电脑·远程工作
KaiGer6665 小时前
AUTOSAR图解==>AUTOSAR_SWS_ICUDriver
单片机·汽车·嵌入式·autosar
SANGEDZ_YYDS6 小时前
三格电子上新了——IO-Link系列集线器
单片机