Polling - FullDuplex
该示例通过使用 SPI 的全双工模式与外部 flash 进行通信。
示例中使用轮询的方式,对外部 flash 进行数据的读取,分别读取 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\Polling_fullduplex\mdk
Project file: board\evb\io_sample\SPI\Polling_fullduplex\gcc
请按照以下步骤操作构建并运行该示例:
打开工程文件。
按照 快速入门 中 编译 APP Image 给出的步骤构建目标文件。
编译成功后,在路径
mdk\bin
或gcc\bin
下会生成 app binapp_MP_xxx.bin
文件。按下 reset 按键,开始运行。
测试验证
读取外部 flash ID 信息。分别读取 flash DEVICE_ID,MF_DEVICE_ID 和 JEDEC_ID 信息,并打印收到的数据长度和内容。在全双工模式下,由于接收和发送逻辑均有效且同时进行,因此主机端收到的数据中第一个字节均为 FF,对应主机端发送读 ID 指令时从机端回复的数据。
[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
分别以 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_fullduplex
源码路径:
sdk\src\sample\io_sample\SPI\Polling_fullduplex
该工程的工程文件代码结构如下:
└── 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
初始化
当 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 模式,Power On,内部上拉,输出高。
配置 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 的传输模式为全双工模式。
配置数据宽度为 8 位。
配置串行时钟的稳态悬空高,数据捕获于第二个时钟沿。
设置时钟分频系数为 100。
使能 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_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); }
功能实现
在主函数中执行
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 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])); }
在
spi_flash_read_id
内,执行以下内容:定义发送数据指令,读取不同 ID 需要发送的指令和字节数不同。
发送读 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 + 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); } }
在
spi_flash_read
内,执行以下内容:定义发送数据,第一个字节为读指令,第二三四字节为地址信息。不同读指令对应的指令和需要发送的数据长度不同。
发送指定地址读指令。
循环执行
SPI_ReceiveData()
,读取 flash 回复的数据信息。在每次读取数据之前,需要发送一个字节的 dummy 数据,用于发送时钟信号以接收数据。
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); }