|
## 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文件系统移植
考虑到单片机系统没有文件系统管理,每次读写文件异常麻烦,所以我们移植一个小型嵌入式文件系统(带断电恢复以及磨损平衡)。
* **littlefs简介**
Github地址:[https://github.com/ARMmbed/littlefs](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);
}
```
|
|