Polling - EEPROM
该示例通过使用 SPI 的 EEPROM 模式与外部 flash 进行通信。
示例中使用轮询的方式,对外部 flash 进行数据的读取,分别读取 flash 的 ID 信息和指定地址信息。
在 EEPROM 通信模式下,一般用于发送数据用于向 EEPROM 设备发送操作码和地址。 在传输操作码和地址期间,接收逻辑无效,不会进行接收数据。 主站继续发送数据,直到发送 FIFO 为空。当发送 FIFO 为空时,接收线路上的数据有效,并存储在接收 FIFO 中。 传输继续进行,直到主站接收到的数据数量与初始化中配置的 NDF 值一致。
在 EEPROM 模式下,无法对外部 flash 进行写操作。
环境需求
该示例支持以下开发套件:
Hardware Platforms |
Board Name |
---|---|
RTL8752H HDK |
RTL8752H EVB |
更多信息请参考 快速入门。
硬件连线
EVB 外接 W25Q128 模块,使用杜邦线连接 P4_0(SCK)和 CLK,P4_1(MISO)和 DO,P4_2(MOSI)和 DI,P4_3(CS)和 CS#。
备注
W25Q128 的模块介绍参考 W25Q128 硬件介绍 。
编译和下载
该示例的工程路径如下:
Project file: board\evb\io_sample\SPI\Polling_eeprom\mdk
Project file: board\evb\io_sample\SPI\Polling_eeprom\gcc
请按照以下步骤操作构建并运行该示例:
打开工程文件。
按照 快速入门 中 编译 APP Image 给出的步骤构建目标文件。
编译成功后,在路径
mdk\bin
或gcc\bin
下会生成 app binapp_MP_xxx.bin
文件。按下 reset 按键,开始运行。
测试验证
读取外部 flash ID 信息。分别读取 flash DEVICE_ID,MF_DEVICE_ID 和 JEDEC_ID 信息,并打印收到的数据长度和内容。
[io_spi] spi_demo: flash_id_type = 0, data_lenth = 4, flash_id = FF FF FF 17 [io_spi] spi_demo: flash_id_type = 1, data_lenth = 5, flash_id = FF FF FF EF 17 [io_spi] spi_demo: flash_id_type = 2, data_lenth = 3, flash_id = EF 40 18
分别以 fast read 和 read 指令读取 flash 不同地址下的数据,打印收到的数据内容。由于没有写入数据,因此读到的数据为全 FF。
[io_spi] spi_demo: read_data = FF FF FF ... [io_spi] spi_demo: read_data = FF FF FF ...
代码介绍
该章节分为以下几个部分:
源码路径
工程路径:
sdk\board\evb\io_sample\SPI\Polling_eeprom
源码路径:
sdk\src\sample\io_sample\SPI\Polling_eeprom
该工程的工程文件代码结构如下:
└── Project: polling_eeprom
└── secure_only_app
└── include
├── app_define.h
└── rom_uuid.h
├── cmsis includes CMSIS header files and startup files
├── overlay_mgr.c
├── system_rtl876x.c
└── startup_rtl876x.s
├── lib includes all binary symbol files that user application is built on
├── rtl8752h_sdk.lib
├── gap_utils.lib
└── ROM.lib
├── peripheral includes all peripheral drivers and module code used by the application
├── rtl876x_rcc.c
├── rtl876x_pinmux.c
├── rtl876x_nvic.c
└── rtl876x_spi.c
├── profile
└── app includes the ble_peripheral user application implementation
├── main.c
├── ancs.c
├── app.c
├── app_task.c
├── spi_flash.c
└── io_spi.c
初始化
当 EVB 复位启动时,调用 main()
函数,将执行以下流程:
int main(void)
{
extern uint32_t random_seed_value;
srand(random_seed_value);
global_data_init();
board_init();
le_gap_init(APP_MAX_LINKS);
gap_lib_init();
app_le_gap_init();
app_le_profile_init();
pwr_mgr_init();
task_init();
os_sched_start();
return 0;
}
备注
le_gap_init()
,gap_lib_init()
,app_le_gap_init
,app_le_profile_init
等为 privacy 管理模块相关的初始化,参考 LE Peripheral Privacy 中的初始化流程介绍。
与外设相关的初始化流程具体如下:
在
board_init
中,执行board_spi_init
,该函数为 PAD/PINMUX 设置,包含如下流程:配置 PAD:设置引脚,PINMUX 模式,PowerOn,内部上拉,输出高。
配置 PINMUX:分配引脚分别为 SPI0_CLK_MASTER、SPI0_MO_MASTER、SPI0_MI_MASTER、SPI0_SS_N_0_MASTER 功能。
在执行
os_sched_start()
开启任务调度后,在app_main_task
主任务内,执行driver_init
对外设驱动进行初始化配置。在
driver_init
中执行driver_spi_init
,该函数为 SPI 外设的初始化,包含如下流程:使能 RCC 时钟。
配置 SPI 的传输模式为 EEPROM 模式。
配置数据宽度为 8 位。
配置串行时钟的稳态悬空高,数据捕获于第二个时钟沿。
设置时钟分频系数为 100。
设置接收数据长度阈值,在 EEPROM 模式下需要设置 NDF 值。
使能 SPI 外设。
备注
SPI 的时钟分频系数最小为 2,且只能为偶数。
void driver_spi_init(void) { RCC_PeriphClockCmd(APBPeriph_SPI0, APBPeriph_SPI0_CLOCK, ENABLE); SPI_InitTypeDef SPI_InitStruct; SPI_StructInit(&SPI_InitStruct); SPI_InitStruct.SPI_Direction = SPI_Direction_EEPROM; SPI_InitStruct.SPI_Mode = SPI_Mode_Master; SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; SPI_InitStruct.SPI_CPOL = SPI_CPOL_High; SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge; SPI_InitStruct.SPI_BaudRatePrescaler = 100; /* SPI_Direction_EEPROM mode read data lenth. */ SPI_InitStruct.SPI_RxThresholdLevel = 1;/* Flash id lenth = 3*/ SPI_InitStruct.SPI_NDF = 0;/* Flash id lenth = 3*/ /* cause SPI_INT_RXF interrupt if data length in receive FIFO >= SPI_RxThresholdLevel + 1*/ SPI_InitStruct.SPI_FrameFormat = SPI_Frame_Motorola; SPI_Init(FLASH_SPI, &SPI_InitStruct); SPI_Cmd(FLASH_SPI, ENABLE); }
功能实现
在主函数中执行
os_sched_start()
,开启任务调度。当 stack 准备好时,执行app_handle_dev_state_evt
,执行spi_demo
。执行
spi_flash_read_id
,分别读取 DEVICE_ID,MF_DEVICE_ID 和 JEDEC_ID 信息。执行
spi_flash_read
,分别以 read 指令和 fast read 指令读取相应地址下的数据。
void spi_demo(void) { uint8_t flash_id[10] = {0}; while (flash_id_type < 3) { spi_flash_read_id((Flash_ID_Type)flash_id_type, flash_id); APP_PRINT_INFO3("[io_spi] spi_demo: flash_id_type = %d, data_lenth = %d, flash_id = %b ", flash_id_type, flash_id[0], TRACE_BINARY(flash_id[0], &flash_id[1])); flash_id_type++; memset(flash_id, 0, sizeof(flash_id)); } flash_id_type = 0; uint8_t read_data[100] = {0}; spi_flash_read(SPI_FLASH_READ_DATA, 0x000101, read_data, 100); APP_PRINT_INFO1("[io_spi] spi_demo: read_data = %b,", TRACE_BINARY(100, read_data)); spi_flash_read(SPI_FLASH_FAST_READ, 0x000210, read_data, 100); APP_PRINT_INFO1("[io_spi] spi_demo: read_data = %b,", TRACE_BINARY(100, read_data)); }
在
spi_flash_read_id
内,执行以下内容:定义发送数据指令,读取不同 ID 需要发送的指令和需要读取的字节数不同。
执行
SPI_SetReadLen()
,设定 EEPROM 模式下需要读取的数据长度。发送读 ID 指令。
循环执行
SPI_ReceiveData()
,读取 flash 回复的数据信息。
void spi_flash_read_id(Flash_ID_Type vFlashIdType, uint8_t *pFlashId) { uint8_t send_buf[4] = {SPI_FLASH_JEDEC_ID, 0, 0, 0}; uint8_t recv_len = 3; switch (vFlashIdType) { case DEVICE_ID: send_buf[0] = SPI_FLASH_DEVICE_ID; recv_len = 4; break; case MF_DEVICE_ID: send_buf[0] = SPI_FLASH_MANU_ID; recv_len = 5; break; case JEDEC_ID: send_buf[0] = SPI_FLASH_JEDEC_ID; recv_len = 3; break; default: return; } *pFlashId++ = recv_len; SPI_SetReadLen(FLASH_SPI, recv_len); SPI_SendBuffer(FLASH_SPI, send_buf, 1); while (recv_len--) { while (RESET == SPI_GetFlagState(FLASH_SPI, SPI_FLAG_RFNE)); *pFlashId++ = SPI_ReceiveData(FLASH_SPI); } }
在
spi_flash_read
内,执行以下内容:定义发送数据,第一个字节为读指令,第二三四字节为地址信息。不同读指令对应的指令和需要发送的数据长度不同。
设定 EERPOM 模式下需要读取的数据长度。
发送指定地址读指令。
循环执行
SPI_ReceiveData()
,读取 flash 回复的数据信息。
void spi_flash_read(uint8_t vReadCmd, uint32_t vReadAddr, uint8_t *pBuffer, uint16_t vLength) { uint8_t send_buf[10] = {0}; uint8_t send_len = 0; uint16_t recv_len = vLength; if (SPI_FLASH_READ_DATA == vReadCmd) { send_len = 4; } else if (SPI_FLASH_FAST_READ == vReadCmd) { send_len = 5; } send_buf[0] = vReadCmd; send_buf[1] = (vReadAddr >> 16) & 0xFF; send_buf[2] = (vReadAddr >> 8) & 0xFF; send_buf[3] = (vReadAddr) & 0xFF; SPI_SetReadLen(FLASH_SPI, recv_len); SPI_SendBuffer(FLASH_SPI, send_buf, send_len); while (recv_len--) { while (SPI_GetFlagState(FLASH_SPI, SPI_FLAG_RFNE) == RESET); *pBuffer++ = SPI_ReceiveData(FLASH_SPI); } SPI_SetReadLen(FLASH_SPI, 1); }