Noridc 52832 SPI Flash 驱动+应用+文件系统

SPI Flash 驱动开发

  • sdk_config.h配置
#define NRFX_SPI_ENABLED 1
#define SPI_ENABLED 1
#define SPI0_ENABLED 0
#define SPI1_ENABLED 1
#define SPI1_USE_EASY_DMA 0

注意:因为TWI0和SPI0为同一地址,TWI0已经被G-Sensor使用,所以我们使用SPI1

  • 添加文件
modules\nrfx\drivers\src\nrfx_spi.c
integration\nrfx\legacy\nrf_drv_spi.c

  • 初始化
#define USER_SPI_CONFIG_IRQ_PRIORITY_HIGH 3

#define USRE_NRF_DRV_SPI_FLSH_CONFIG                         \
{                                                            \
    .sck_pin      = SPI_SCK_PIN,                            \
    .mosi_pin     = SPI_MOSI_PIN,                           \
    .miso_pin     = SPI_MISO_PIN,                           \
    .ss_pin       = NRF_DRV_SPI_PIN_NOT_USED,                \
    .irq_priority = USER_SPI_CONFIG_IRQ_PRIORITY_HIGH,         \
    .orc          = 0xFF,                                    \
    .frequency    = NRF_DRV_SPI_FREQ_4M,                   \
    .mode         = NRF_DRV_SPI_MODE_0,                      \
    .bit_order    = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST,         \
}

static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(1);  /**< SPI instance. */

/**
 * @brief SPI user event handler.
 * @param event
 */
static void spi_event_handler(nrf_drv_spi_evt_t const * p_event, void *p_context)
{
    spi_xfer_done = true;
    //NRF_LOG_INFO("Transfer completed.");
    if (spi_rx_buf[0] != 0)
    {
        //NRF_LOG_INFO(" Received:");
        //NRF_LOG_HEXDUMP_INFO(spi_rx_buf, strlen((const char *)spi_rx_buf));
    }
}

void spi_flash_init(void) {
    nrf_drv_spi_config_t spi_config = USRE_NRF_DRV_SPI_FLSH_CONFIG;
    spi_config.ss_pin   = NRF_DRV_SPI_PIN_NOT_USED;

    APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL));
    nrf_delay_ms(10);
    NRF_LOG_INFO("SPI flash init...");
}

注意:

  1. ss_pin需要自己控制;
  2. NRF_DRV_SPI_INSTANCE设置1,对应sdk_config.h中的SPI1;
  3. irq_priority中断优先级一定要设置为3,默认配置是7,不然蓝牙协议栈起来后会没效果!
  • 驱动Flash芯片 GD25Q127C
/*****************************************************************************
** 文件名称:static uint8_t spi_flash_read_status_reg(void)
** 功    能:“读状态寄存器”命令,retValue为读到的数值
** 修改日志:
** 附    录:当CS拉低之后,把05H从DI引脚输入到Flash芯片,CLK上升沿,数据写入Flash,当Flash收到05H后,会把“状态寄存器”的值,从D0引脚输出,CLK下降沿输出。  
******************************************************************************/

static uint8_t spi_flash_read_status_reg(void)
{
    uint8_t retValue = 0;

    CS_L();
    spi_flash_write_one_byte(SPIFLASH_READSR_CMD);
    retValue = spi_flash_read_one_byte();
    CS_H();
    return retValue;
}

/*****************************************************************************
** 文件名称:static uint8_t spi_flash_wait_busy(void)
** 功    能:判断Flash是否busy。
** 修改日志:
:SPIFLASH_WRITE_BUSYBIT 写状态寄存器 
******************************************************************************/
static uint8_t spi_flash_wait_busy(void)
{
    spi_wait_count = 0;
    while((spi_flash_read_status_reg() & SPIFLASH_WRITE_BUSYBIT) == 0x01)  //状态寄存器0位为Busy位
    {  
       if(spi_wait_count >= 100)
          break;
    }

    //NRF_LOG_INFO("spi_wait_count = %d", spi_wait_count);
    return GD25OK;
}

static uint8_t spi_flash_read_one_byte(void) 
{
    uint8_t len = 1;

    spi_tx_buf[0] = 0xFF;
    spi_xfer_done = false;
    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
    while(!spi_xfer_done);
    return (spi_rx_buf[0]);
}

static void spi_flash_write_one_byte(uint8_t Dat)
{
    uint8_t len = 1;
    ret_code_t ret_code;

    spi_tx_buf[0] = Dat;
    spi_xfer_done = false;
    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
    while(!spi_xfer_done);
}

void spi_flash_write_enable(void)
{
    CS_L();  
    spi_flash_write_one_byte(SPIFLASH_WRITEEN_CMD);
    CS_H();  
}

/*****************************************************************************
** 文件名称:uint8_t spi_flash_read_data(uint8_t *pBuffer,uint32_t ReadAddr,uint32_t ReadBytesNum)
** 功    能:读Flash的某地址ReadAddr,读多大ReadByteNum,的数值。
** 修改日志:
******************************************************************************/
uint8_t spi_flash_read_data(uint8_t *pBuffer, uint32_t ReadAddr, uint32_t ReadBytesNum)
{
    uint8_t len;

    spi_tx_buf[0] = SPIFLASH_READDATA_CMD;
    spi_tx_buf[1] = (uint8_t)((ReadAddr & 0x00ff0000) >> 16);
    spi_tx_buf[2] = (uint8_t)((ReadAddr & 0x0000ff00) >> 8);
    spi_tx_buf[3] = (uint8_t)ReadAddr;

    len = ReadBytesNum + 4;

    CS_L();
    spi_xfer_done = false;
    APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
    while(!spi_xfer_done);
    CS_H();

    memcpy(pBuffer, &spi_rx_buf[4], ReadBytesNum);

    return GD25OK;
}

// 52832 spi flash 的库每次最多只能写或者读取 251个字节,常规页是256字节,所以要特殊处理一下
uint8_t spi_flash_read_data_52832(uint8_t *pBuffer,uint32_t ReadAddr,uint32_t ReadBytesNum)
{
    uint32_t len = ReadBytesNum, len_cut = 0;
    uint32_t addr = ReadAddr;
    uint8_t *pic_point = pBuffer;

    do {
        spi_tx_buf[0] = SPIFLASH_READDATA_CMD;
        spi_tx_buf[1] = (uint8_t)((addr & 0x00ff0000) >> 16);
        spi_tx_buf[2] = (uint8_t)((addr & 0x0000ff00) >> 8);
        spi_tx_buf[3] = (uint8_t)addr;
        len_cut = (len >= (0xFF - 4)) ? (0xFF) : (len + 4);

        CS_L();
        spi_xfer_done = false;
        APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, 4, spi_rx_buf, len_cut));
        while(!spi_xfer_done);
        CS_H();

        memcpy(pic_point, &spi_rx_buf[4], (len_cut - 4));
        addr += (len_cut - 4);
        len -= (len_cut - 4);
        pic_point += (len_cut - 4);
    } while(len > 0);

    return GD25OK;
}

注意:52832的nrf_drv_spi_transfer函数限制了每次读写不能超过251字节,所以要特殊处理一下。读可以任意地址读,写不能跨页写,正常一页256字节每次都需要分成251+5两次写


littlefs文件系统移植

考虑到单片机系统没有文件系统管理,每次读写文件异常麻烦,所以我们移植一个小型嵌入式文件系统(带断电恢复以及磨损平衡)。

  • littlefs简介

Github地址:https://github.com/ARMmbed/littlefs

LittleFS - 一个高度完整的嵌入式文件系统

  • 断电恢复能力 - 要求文件系统保持一致,并将数据刷新到底层存储。
  • 平均磨损 - 通常情况下,存储支持每块数量有限的擦除,因此使用整个存储设备对于可靠性非常重要。
  • 微小的占用资源 - 物联网设备受到ROM和RAM的限制,占用资源小可以节省资金。
  • 配置
lfs_t g_lfs;
lfs_file_t file;
uint8_t lfs_read_buf[256] = {0};
uint8_t lfs_prog_buf[256] = {0};
uint8_t lfs_lookahead_buf[256] = {0};
uint8_t lfs_file_buf[256] = {0};
uint32_t lfs_free_spcae_size = 0;

int user_provided_block_device_read(const struct lfs_config *c, lfs_block_t block,
        lfs_off_t off, void *buffer, lfs_size_t size) {

    ASSERT(block < c->block_count);
    spi_flash_read_data_52832((uint8_t *)buffer, (block * c->block_size + off), size);
    return 0;
}

int user_provided_block_device_prog(const struct lfs_config *c, lfs_block_t block,
        lfs_off_t off, const void *buffer, lfs_size_t size) {

    ASSERT(block < c->block_count);
    spi_flash_write_page_more((uint8_t *)buffer, (block * c->block_size + off), size);
    return 0;
}

int user_provided_block_device_erase(const struct lfs_config *c, lfs_block_t block) {
    ASSERT(block < c->block_count);
    spi_flash_erase_addr(block * c->block_size);
    return 0;
}

int user_provided_block_device_sync(const struct lfs_config *c) {
    return 0;
}

// configuration of the filesystem is provided by this struct
const struct lfs_config cfg = {
    // block device operations
    .read  = user_provided_block_device_read,
    .prog  = user_provided_block_device_prog,
    .erase = user_provided_block_device_erase,
    .sync  = user_provided_block_device_sync,

    // block device configuration
    .read_size =    256,
    .prog_size =    256,
    .block_size =   4096,
    .block_count =  4096,
    .lookahead =    256,

    .read_buffer =          lfs_read_buf,
    .prog_buffer =          lfs_prog_buf,
    .lookahead_buffer =     lfs_lookahead_buf,
    .file_buffer =          lfs_file_buf,
};

注意:Flash芯片为128M,只配置了其中的16M

  • 初始化
void spi_flash_littlefs_init(void) {
    // mount the filesystem
    int err = lfs_mount(&g_lfs, &cfg);

    // reformat if we can't mount the filesystem
    // this should only happen on the first boot
    if(err) {
        spi_flash_erase_chip();
        err = lfs_format(&g_lfs, &cfg);
        err = lfs_mount(&g_lfs, &cfg);
    }

    NRF_LOG_INFO("spi flash littlefs done");
}

  • 测试
void spi_flash_littlefs_test(void) {

    // read current count
    int i;
    uint32_t buf_len = 0;
    uint8_t test_buf[1024] = {'\0'};

    spi_flash_littlefs_init();

    //lfs_file_open(&g_lfs, &file, "boot_count",  LFS_O_WRONLY | LFS_O_TRUNC);
    //NRF_LOG_INFO("lfs_file_size: %d", lfs_file_size(&g_lfs, &file));
    //lfs_file_close(&g_lfs, &file);

    lfs_file_open(&g_lfs, &file, "boot_count",  LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND);
    for (i = 0; i < 1024; i++) {
        lfs_file_write(&g_lfs, &file, (const void*)"0123456789", strlen("0123456789"));
    }
    lfs_file_close(&g_lfs, &file);

    lfs_file_open(&g_lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
    buf_len = lfs_file_size(&g_lfs, &file);
    NRF_LOG_INFO("lfs_file_size: %d", buf_len);
    lfs_file_seek(&g_lfs, &file, buf_len - 1024, LFS_SEEK_SET);
    lfs_file_read(&g_lfs, &file, (void*)test_buf, sizeof(test_buf));
    lfs_file_close(&g_lfs, &file);
    NRF_LOG_HEXDUMP_INFO(test_buf, 7);

    // release any resources we were using
    lfs_unmount(&g_lfs);
}

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 借一方宝地,和小盆友享受闹市的静谧和夏日的清凉
    简行0阅读 89评论 0 0
  • “不是上帝造了人,而是人造了上帝。” ——费尔巴哈 费尔巴哈认为,把人类了解透彻...
    打开今天阅读 184评论 0 1
  • 石头大哥: 上午单位组织看电影,我因为怕影院的重低音影响宝宝,决定不去,翘班去图书馆还书。早上我埋怨你不...
    窚煊阅读 420评论 0 4