Interrupt
This example demonstrates the method of SPI communication with external flash.
Data is read from the external flash using interrupts. The DEVICE_ID, MF_DEVICE_ID, and JEDEC_ID of the external flash are read separately.
Flash ID information is printed in the Debug Analyzer.
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\Interrupt\mdk
Project file: board\evb\io_sample\SPI\Interrupt\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
After EVB reset startup, the following information is printed:
[io_spi] spi_demo: Read flash id.
The host sends read ID command information to the external flash respectively. After the host receives the information, it enters an interrupt and prints the received data information:
[app] app_handle_io_msg: spi msg. [io_spi] io_spi_handle_msg: data_lenth = 4, data = FF FF FF 17 [app] app_handle_io_msg: spi msg. [io_spi] io_spi_handle_msg: data_lenth = 5, data = FF FF FF EF 17 [app] app_handle_io_msg: spi msg. [io_spi] io_spi_handle_msg: data_lenth = 3, data = EF 40 18
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\Interrupt
Source code directory:
sdk\src\sample\io_sample\SPI\Interrupt
Source files are currently categorized into several groups as below.
└── Project: interrupt
└── 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
. This function sets up the PAD/PINMUX for SPI related pins, including the following steps: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 UART peripheral, including the following steps:Enable the RCC clock.
Configure the SPI transfer mode to EEPROM mode.
Set the data width to 8 bits.
Configure the serial clock to idle high, and data capture on the second clock edge.
Set the clock division factor to 50.
Set the receive data length threshold, the NDF value needs to be set in EEPROM mode.
Enable the SPI peripheral.
Configure the SPI receive data threshold interrupt:
SPI_INT_RXF
.
Note
The minimum clock division factor 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_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 = 50; /* SPI_Direction_EEPROM mode read data lenth. */ SPI_InitStruct.SPI_RxThresholdLevel = 1 - 1;/* Flash id lenth = 3*/ SPI_InitStruct.SPI_NDF = 1 - 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(SPI0, &SPI_InitStruct); SPI_Cmd(SPI0, ENABLE); /* detect receive data */ SPI_INTConfig(SPI0, SPI_INT_RXF, ENABLE); /* Config SPI interrupt */ NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel = SPI0_IRQn; NVIC_InitStruct.NVIC_IRQChannelPriority = 3; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStruct); }
Functional Implementation
In the main function, execute
os_sched_start()
to start task scheduling. When the stack is ready, executeapp_handle_dev_state_evt
, and then executespi_demo
.Execute
spi_flash_read_id
to read the DEVICE_ID information.In
spi_flash_read_id
, define the command and data length that the host needs to send.Execute
spi_flash_write_read
, set the data length to be read, and send the set data to the flash.
void spi_demo(void) { uint8_t id[10]; APP_PRINT_INFO0("[io_spi] spi_demo: Read flash id."); spi_flash_read_id(DEVICE_ID, id); flash_id_type = 0; } void spi_flash_read_id(Flash_ID_Type vIdType, uint8_t *pId) { uint8_t write_data = 0; uint16_t write_length = 1; switch (vIdType) { case DEVICE_ID: write_data = SPI_FLASH_DEVICE_ID; Flash_ID_Length = 4; break; case MF_DEVICE_ID: write_data = SPI_FLASH_MANU_ID; Flash_ID_Length = 5; break; case JEDEC_ID: write_data = SPI_FLASH_JEDEC_ID; Flash_ID_Length = 3; break; default: return; } spi_flash_write_read(&write_data, write_length, &pId[1], Flash_ID_Length); pId[0] = Flash_ID_Length; } void spi_flash_write_read(uint8_t *pWriteBuf, uint16_t vWriteLen, uint8_t *pReadBuf, uint16_t vReadLen) { SPI_SetReadLen(FLASH_SPI, vReadLen); SPI_SendBuffer(FLASH_SPI, pWriteBuf, vWriteLen); }
When the SPI receives 1 byte of data, it triggers the
SPI_INT_RXF
interrupt and enters the interrupt handler functionSPI0_Handler
.Clear the interrupt flag.
Determine until the FIFO data length equals the Flash_ID length, i.e., wait for the flash to send all data to the host.
Retrieve and print the data in the FIFO.
Continue executing
spi_flash_read_id
to read the next ID type and repeat this process.
void SPI0_Handler(void) { volatile uint8_t data_len = 0; volatile uint8_t SPI_ReadINTBuf[70] = {0}; if (SPI_GetINTStatus(SPI0, SPI_INT_RXF) == SET) { SPI_ClearINTPendingBit(SPI0, SPI_INT_RXF); while (SPI_GetRxFIFOLen(SPI0) < Flash_ID_Length); data_len = SPI_GetRxFIFOLen(SPI0); Flash_Data[0] = data_len; for (uint8_t i = 0; i < data_len; i++) { /* Must read all data in receive FIFO , otherwise cause SPI_INT_RXF interrupt again. */ Flash_Data[1 + i] = SPI_ReceiveData(SPI0); } T_IO_MSG int_spi_msg; int_spi_msg.type = IO_MSG_TYPE_SPI; int_spi_msg.subtype = 0; int_spi_msg.u.buf = (void *)(Flash_Data); if (false == app_send_msg_to_apptask(&int_spi_msg)) { APP_PRINT_ERROR0("[io_spi] SPI0_Handler: Send int_spi_msg failed!"); SPI_ClearINTPendingBit(SPI0, SPI_INT_RXF); //Add user code here! return; } } } void io_spi_handle_msg(T_IO_MSG *io_spi_msg) { uint8_t *p_buf = io_spi_msg->u.buf; uint8_t data_lenth = p_buf[0]; APP_PRINT_INFO2("[io_spi] io_spi_handle_msg: data_lenth = %d, data = %b ", data_lenth, TRACE_BINARY(data_lenth, &p_buf[1])); uint8_t id[1]; flash_id_type++; if (flash_id_type < 3) { spi_flash_read_id((Flash_ID_Type)flash_id_type, id); } }