Polling - FullDuplex
This example communicates with external flash using the full-duplex mode of SPI.
The example uses polling to read data from the external flash, reading both the flash’s ID information and specified address information.
In full-duplex mode, both the sending and receiving logic are active. Data transmission proceeds normally according to the selected frame format (serial protocol).
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_fullduplex\mdk
Project file: board\evb\io_sample\SPI\Polling_fullduplex\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. Read the flash DEVICE_ID, MF_DEVICE_ID, and JEDEC_ID information separately, and print the length and content of the received data. In full-duplex mode, since both the receiving and sending logic are active and occur simultaneously, the first byte of the data received by the host is always FF, corresponding to the data replied by the slave when the host sends the read ID command.
[io_spi] spi_demo: Read flash id. [io_spi] spi_demo: flash_id_type = 0, data_lenth = 5, flash_id = FF FF FF FF 17 [io_spi] spi_demo: flash_id_type = 1, data_lenth = 6, flash_id = FF FF FF FF EF 17 [io_spi] spi_demo: flash_id_type = 2, data_lenth = 4, flash_id = FF EF 40 18
Read the data at different addresses of the flash using fast read and read commands respectively, and print the content of the received data. Since no data has been written, the read data is 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_fullduplex
Source code directory:
sdk\src\sample\io_sample\SPI\Polling_fullduplex
Source files are currently categorized into several groups as below.
└── Project: polling_fullduplex
└── 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 RCC clock.
Configure SPI transmission mode to full-duplex mode.
Set data width to 8 bits.
Configure serial clock to have idle state high and data capture on the second clock edge.
Set clock division factor to 100.
Enable 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 id[10] = {0}; APP_PRINT_INFO0("[io_spi] spi_demo: Read flash id."); while (flash_id_type < 3) { spi_flash_read_id((Flash_ID_Type)flash_id_type, id); APP_PRINT_INFO3("[io_spi] spi_demo: flash_id_type = %d, data_lenth = %d, data = %b ", flash_id_type, id[0], TRACE_BINARY(id[0], &id[1])); flash_id_type++; memset(id, 0, sizeof(id)); } flash_id_type = 0; uint8_t read_data[105] = {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[4])); 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[5])); }
In
spi_flash_read_id
, execute the following steps:Define the send data instructions, as different IDs require different instructions and byte counts.
Send the read ID instruction.
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 + 1; break; case MF_DEVICE_ID: send_buf[0] = SPI_FLASH_MANU_ID; recv_len = 5 + 1; break; case JEDEC_ID: send_buf[0] = SPI_FLASH_JEDEC_ID; recv_len = 3 + 1; break; default: return; } *pFlashId++ = recv_len; SPI_SendBuffer(FLASH_SPI, send_buf, recv_len); 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 as the read command and the second, third, and fourth bytes as the address information. Different read commands correspond to different commands and lengths of data to be sent.
Send the specified address read command.
Loop to execute
SPI_ReceiveData()
to read the data information replied by the flash. Before reading data each time, a dummy byte needs to be sent to generate the clock signal for receiving data.
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 = 0; 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_SendBuffer(FLASH_SPI, send_buf, send_len); recv_len = vLength + send_len; while (recv_len--) { SPI_SendBuffer(FLASH_SPI, &send_buf[9], 1); while (SPI_GetFlagState(FLASH_SPI, SPI_FLAG_RFNE) == RESET); *pBuffer++ = SPI_ReceiveData(FLASH_SPI); } SPI_SetReadLen(FLASH_SPI, 1); }