Interrupt
该示例演示 SPI 与外部 flash 通信的方法。
使用中断的方式对外部 flash 进行数据的读取。分别读取外部 flash 的 DEVICE_ID,MF_DEVICE_ID 和 JEDEC_ID。
在 Debug Analyzer 内,打印 Flash ID 信息。
环境需求
该示例支持以下开发套件:
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\Interrupt\mdk
Project file: board\evb\io_sample\SPI\Interrupt\gcc
请按照以下步骤操作构建并运行该示例:
打开工程文件。
按照 快速入门 中 编译 APP Image 给出的步骤构建目标文件。
编译成功后,在路径
mdk\bin
或gcc\bin
下会生成 app binapp_MP_xxx.bin
文件。按下 reset 按键,开始运行。
测试验证
EVB 启动复位后,打印如下信息:
[io_spi] spi_demo: Read flash id.
主机向外部 flash 分别发送读取 ID 指令信息。主机收到信息后进入中断,打印收到数据的信息:
[app] app_handle_io_msg: spi msg. [io_spi] io_spi_handle_msg: data_length = 4, data = FF FF FF 17 [app] app_handle_io_msg: spi msg. [io_spi] io_spi_handle_msg: data_length = 5, data = FF FF FF EF 17 [app] app_handle_io_msg: spi msg. [io_spi] io_spi_handle_msg: data_length = 3, data = EF 40 18
代码介绍
该章节分为以下几个部分:
源码路径
工程路径:
sdk\board\evb\io_sample\SPI\Interrupt
源码路径:
sdk\src\sample\io_sample\SPI\Interrupt
该工程的工程文件代码结构如下:
└── 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
初始化
当 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 位。
配置串行时钟的稳态悬空高,数据捕获于第二个时钟沿。
设置时钟分频系数为 50。
设置接收数据长度阈值,在 EEPROM 模式下需要设置 NDF 值。
使能 SPI 外设。
配置 SPI 接收数据达到阈值中断
SPI_INT_RXF
。
备注
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 = 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); }
功能实现
在主函数中执行
os_sched_start()
,开启任务调度。当 stack 准备好时,执行app_handle_dev_state_evt
,执行spi_demo
。执行
spi_flash_read_id
读取 DEVICE_ID 信息。在
spi_flash_read_id
内,定义主机需要发送的指令和数据长度。执行
spi_flash_write_read
,设定需要读取的数据长度,并将设定好的数据发送至 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); }
当 SPI 接收到 1 字节的数据时,触发
SPI_INT_RXF
中断,进入中断处理函数SPI0_Handler
。清除中断标志位。
判断直到满足 FIFO 中数据长度等于 Flash_ID 长度,即等待 flash 将所有的数据发送至主机。
获取 FIFO 内的数据并打印。
继续执行
spi_flash_read_id
,读取下一个 ID 类型,重复此过程。
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); } }