Polling - EEPROM
This example communicates with external flash using the EEPROM mode of the SPI.
The example uses polling to read data from external flash, including reading the flash’s ID information and specified address information.
In EEPROM communication mode, it is generally used to send data to send operation codes and addresses to the EEPROM device. During the transmission of operation codes and addresses, the reception logic is invalid and no data is received. The master continues to send data until the transmit FIFO is empty. When the transmit FIFO is empty, the data on the receive line is valid and stored in the receive FIFO. The transmission continues until the number of data received by the master matches the NDF value configured during initialization.
In EEPROM mode, it is not possible to write to external flash.
Requirements
The sample supports the following development kits:
Hardware Platforms |
Board Name |
---|---|
RTL8752H HDK |
RTL8752H EVB |
For more requirements, please refer to Quick Start.
Wiring
Connect the EVB to the W25Q128 module using DuPont wires: P4_0 (SCK) to CLK, P4_1 (MISO) to DO, P4_2 (MOSI) to DI, and P4_3 (CS) to CS#.
Note
For the module introduction of W25Q128, refer to W25Q128 Hardware Introduction.
Building and Downloading
This sample can be found in the SDK folder:
Project file: board\evb\io_sample\SPI\Polling_eeprom\mdk
Project file: board\evb\io_sample\SPI\Polling_eeprom\gcc
Please follow these steps to build and run the example:
Open sample project file.
To build the target, follow the steps listed on the Generating App Image in Quick Start.
After a successful compilation, the app bin
app_MP_xxx.bin
will be generated in the directorymdk\bin
orgcc\bin
.To download app bin into EVB board, follow the steps listed on the MP Tool Download in Quick Start.
Press reset button on EVB board and it will start running.
Experimental Verification
Read the external flash ID information. Respectively read the flash DEVICE_ID, MF_DEVICE_ID, and JEDEC_ID information, and print the length and content of the received data.
[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
Use the fast read and read commands to read the data at different addresses in the flash, and print the received data content. Since no data has been written, the read data will be all FF.
[io_spi] spi_demo: read_data = FF FF FF ... [io_spi] spi_demo: read_data = FF FF FF ...
Code Overview
This chapter will be introduced according to the following several parts:
Peripheral initialization will be introduced in chapter Initialization.
Functional implementation after initialization will be introduced in chapter Function Implementation.
Source Code Directory
Project directory:
sdk\board\evb\io_sample\SPI\Polling_eeprom
Source code directory:
sdk\src\sample\io_sample\SPI\Polling_eeprom
Source files are currently categorized into several groups as below.
└── 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
Initialization
When the EVB reset is initiated, the main()
function is called, and the following process will be executed:
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;
}
Note
le_gap_init()
, gap_lib_init()
, app_le_gap_init
, and app_le_profile_init
are related to the initialization of the privacy management module. Refer to the initialization process description in LE Peripheral Privacy.
The specific initialization process related to peripherals is as follows:
In
board_init
, executeboard_spi_init
, which is responsible for PAD/PINMUX settings and includes the following process:Configure PAD: Set the pin, PINMUX mode, PowerOn, internal pull-up, and output high.
Configure PINMUX: Assign pins for SPI0_CLK_MASTER, SPI0_MO_MASTER, SPI0_MI_MASTER, and SPI0_SS_N_0_MASTER functions.
After executing
os_sched_start()
to start task scheduling, executedriver_init
in the main taskapp_main_task
to initialize and configure the peripheral drivers.In
driver_init
, executedriver_spi_init
, which initializes the SPI peripheral, including the following steps:Enable the RCC clock.
Configure the SPI transmission mode to EEPROM mode.
Set the data width to 8 bits.
Configure the serial clock to idle high, and capture data on the second clock edge.
Set the clock divider coefficient to 100.
Set the receive data length threshold; in EEPROM mode, the NDF value needs to be set.
Enable the SPI peripheral.
Note
The minimum clock divider for SPI is 2, and it can only be an even number.
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_FullDuplex; 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 = 3 - 1;/* Flash id lenth = 3*/ SPI_InitStruct.SPI_NDF = 3 - 1;/* 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); }
Functional Implementation
Execute
os_sched_start()
in the main function to start task scheduling. When the stack is ready, executeapp_handle_dev_state_evt
andspi_demo
.Execute
spi_flash_read_id
to read DEVICE_ID, MF_DEVICE_ID, and JEDEC_ID information respectively.Execute
spi_flash_read
to read data at the corresponding addresses using the read instruction and fast read instruction respectively.
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)); }
In
spi_flash_read_id
, execute the following steps:Define the send data command, as different IDs require different commands and bytes to be read.
Execute
SPI_SetReadLen()
to set the data length to be read in EEPROM mode.Send the read ID command.
Loop through
SPI_ReceiveData()
to read the data information returned by the 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); } }
In
spi_flash_read
, execute the following steps:Define the data to be sent, with the first byte being the read command and the second, third, and fourth bytes being the address information. Different read commands correspond to different instructions and the length of data to be sent.
Set the length of data to be read in EERPOM mode.
Send the read command to the specified address.
Loop execute
SPI_ReceiveData()
to read the data information replied by the 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); }