【WCH蓝牙系列芯片】-基于CH582开发板—SPI发送数据,每个字节间延时大的问题
-------------------------------------------------------------------------------------------------------------------------------------
在使用SPI接口发送数据时,当SPI主机使用过程中,连续发送多个数据时,会发现每个数据之间会产生一个时钟的间隙。两帧数据之间时间间隔都是几乎不变的,这样有时导致SPI接口收发数据产生时序错误,导致SPI通信失败。
因此,针对这个问题,这里采用沁恒微电子的CH582芯片,利用CH582的EVT中SPI例程对这个问题进行说明和优化。
一、在例程中,先写一个通过 SPI 接口发送 16 位数据的函数。发送单个数据是采取SPI0_MasterSendByte库函数进行编写。
void spi_write_test(uint16_t data) { GPIOA_ResetBits(GPIO_Pin_12); //片选信号拉低 SPI0_MasterSendByte(data>>8); //向 SPI0 接口发送数据的高字节。使用 >> 操作符将数据右移 8 位,以获取高 8 位数据。 SPI0_MasterSendByte(data>>0); //向 SPI0 接口发送数据的低字节。使用 >> 操作符将数据右移 0 位,以获取低 8 位数据。 GPIOA_SetBits(GPIO_Pin_12); //片选信号拉高 }
首先,将片选信号置低以选中 SPI 设备,然后依次发送数据的高和低字节,最后将片选信号置高以结束SPI。
二、然后再写一个通过 SPI 接口发送 16 位数据的函数,是使用FIFO连续发送多字节的思路进行编写。
void spi_write_test2(uint16_t data)//通过 FIFO方式 的SPI接口发送 16 位数据 { GPIOA_ResetBits(GPIO_Pin_12); //片选信号拉低 R8_SPI0_CTRL_MOD &= ~RB_SPI_FIFO_DIR; // 设置数据方向为输出 while(!(R8_SPI0_INT_FLAG & RB_SPI_FREE)); // 等待FIFO中的数据全部发送完成 R16_SPI0_TOTAL_CNT = 2; //设置 SPI0 的数据传输总字节数为 2,表示需要发送 2 个字节。 R8_SPI0_FIFO = data>>8; //将数据的高字节写入 SPI0 的 FIFO 寄存器 R8_SPI0_FIFO = data>>0; //将数据的低字节写入 SPI0 的 FIFO 寄存器 while(!(R8_SPI0_INT_FLAG & RB_SPI_FREE)); // 等待FIFO中的数据全部发送完成 GPIOA_SetBits(GPIO_Pin_12); //片选信号拉高 }
与前面的函数相比,使用了FIFO连续发送多字节底层寄存器的方式进行操作,而不是直接调用库函数。其中包括了通过控制寄存器、FIFO 寄存器和中断标志寄存器来进行 SPI 数据发送和等待的操作。
1. 先是将片选信号置低以选中 SPI 设备;
2. 然后设置数据方向为输出,即主设备向从设备发送数据;
3. 之后先判断FIFO中的数据是否全部发送完成;
4. 设置 SPI0 的数据传输总字节数为 2,表示需要发送 2 个字节
5. 将数据的高字节写入 SPI0 的 FIFO 寄存器
6. 将数据的低字节写入 SPI0 的 FIFO 寄存器
7. 再次判断FIFO中的数据是否全部发送完成
8. 最后片选信号置高以结束SPI
将这个两个程序放在while中一直运行,一直发送数据,发送数据0X1234。
while(1) { spi_write_test(0x1234); DelayUs(100); spi_write_test2(0x1234); DelayMs(1); }
通过逻辑分析仪进行数据SPI采集,
如图1,是采用SPI0_MasterSendByte库函数进行编写的SPI发送数据,可以很明显的看出两个数据之间的存在一定的时间间隔,大约在2.4225uS左右。
如图2,是采用FIFO连续发送多个字节底层寄存器的方式,进行编写的SPI发送数据。
发现这样的方式发送数据,每个数据之间虽然还是存在一个时间间隔,但是只有97.5nS的时间间隔,相较于前一种SPI发送数据的方式,每个数据之间的延时就不会很长,不太影响数据发送的过程。
三、在此基础上,将上述两个函数都在放在RAM中进行运行。采用__HIGH_CODE的方式,只针对__HIGH_CODE下面定义的一个函数,这样函数是放在RAM中工作,运行速度会快一些。
__HIGH_CODE修饰下的速度和主频一致,普通位置的是在flash中运行,运行速度在20MHz左右。
#include "CH58x_common.h" void DebugInit(void) { GPIOA_SetBits(GPIO_Pin_9); GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU); GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); UART1_DefInit(); } //通过 SPI 接口发送 16 位数据 __HIGH_CODE void spi_write_test(uint16_t data) { GPIOA_ResetBits(GPIO_Pin_12); //片选信号拉低 SPI0_MasterSendByte(data>>8); //向 SPI0 接口发送数据的高字节。使用 >> 操作符将数据右移 8 位,以获取高 8 位数据。 SPI0_MasterSendByte(data>>0); //向 SPI0 接口发送数据的低字节。使用 >> 操作符将数据右移 0 位,以获取低 8 位数据。 GPIOA_SetBits(GPIO_Pin_12); //片选信号拉高 } __HIGH_CODE void spi_write_test2(uint16_t data)//通过 FIFO方式 的SPI接口发送 16 位数据 { GPIOA_ResetBits(GPIO_Pin_12); //片选信号拉低 R8_SPI0_CTRL_MOD &= ~RB_SPI_FIFO_DIR; // 设置数据方向为输出 while(!(R8_SPI0_INT_FLAG & RB_SPI_FREE)); // 等待FIFO中的数据全部发送完成 R16_SPI0_TOTAL_CNT = 2; //设置 SPI0 的数据传输总字节数为 2,表示需要发送 2 个字节。 R8_SPI0_FIFO = data>>8; //将数据的高字节写入 SPI0 的 FIFO 寄存器 R8_SPI0_FIFO = data>>0; //将数据的低字节写入 SPI0 的 FIFO 寄存器 while(!(R8_SPI0_INT_FLAG & RB_SPI_FREE)); // 等待FIFO中的数据全部发送完成 GPIOA_SetBits(GPIO_Pin_12); //片选信号拉高 } int main() { SetSysClock(CLK_SOURCE_PLL_40MHz); /* 配置串口调试 */ DebugInit(); PRINT("Start @ChipID=%02X\n", R8_CHIP_ID);
/* 主机模式 *//* SPI 0 */ GPIOA_SetBits(GPIO_Pin_12); GPIOA_ModeCfg(GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14, GPIO_ModeOut_PP_5mA); SPI0_MasterDefInit(); while(1) { spi_write_test(0x1234); DelayUs(100); spi_write_test2(0x1234); DelayMs(1); }
通过逻辑分析仪采集SPI数据,观察一下。
如图3,采用SPI0_MasterSendByte库函数的方式,发现两个数据的时间间隔明显减少,
只有1.9975uS.相比于之前没有加__HIGH_CODE之前,有一定的改善。
如图4,采用FIFO连续发送多个字节底层寄存器的方式,发现两个数据的时间间隔只有95nS.也有一定的改善。
所以经过测试,当使用SPI发送数据时候,最好还是采用FIFO连续发送多个字节加上__HIGH_CODE,放在RAM中进行运行,这样能让发送的每个数据之间的时间间隔不至于过大,延时过长,导致在使用SPI发送数据过程中出现错误。