SPI通信的配置那只是基本的底层,要保证通信做得漂亮,设计通信的帧格式将大大降低错误概率。比如,因为我暂时最多传输一个字(16bit)的数据,设计如下7字节的帧格式:
将帧格式对应到数据结构就是一个结构体:
struct st_msg_user {
uint8_t header;
uint8_t type;
uint8_t low_byte;
uint8_t high_byte;
uint8_t rsvd0;
uint8_t rsvd1;
uint8_t checkout; // low_byte ^ high_byte
};
然后,所有对帧的操作都归结到对上面结构体的操作,如:
extern struct st_msg_user assemble_frame(uint8_t type, uint16_t data); // 将数据组成一帧
extern void msg_printf(struct st_msg_user *msg); // 将帧发送出去
extern uint16_t unpack_frame(struct st_msg_user msg); // 解出帧中的数据
首先考虑硬件上要注意的:
-
必需设置1个MCU为主机,另1个为从机,两者接线是CLK、MISO、MOSI、SEL一一对应连接。注意是一一对接,也就是说主机MISO就接到从机的MISO、主机MOSI就接到从机的MOSI
-
除了要接SPI的4根线意外,还必须连接地线(共地),提供参考电势
软件配置上:
-
CLK永远是由主机发出,因此必须使用全双工模式,在读数据的时候同时写一个数据提供总线时钟
-
时钟的空闲状态和时钟的采样沿的主从机配置要保持一致
-
我使用的是TMS320C6713与STM32进行SPI通信,DSP为主机,ARM为从机,则两端的配置如下:
- ARM端
其中影响通信数据的段包括:Datasize;CPOL和CPHA分别表示时钟空闲状态和时钟采样边沿,主从机必须一致;FisrtBit也很重要,否则接收到的数据是倒转的,因为我的DSP是MSB格式,所以ARM端也设成MSB。
- DSP端
DSP端使用McBSP模拟SPI主机功能,其寄存器配置如下:
/* * Config SPI * devNum -- Choose from 0~1 for McBSP0/McBSP1 */ void SPI_Config(MCBSP_Handle *hMcbsp_ch, int devNum) { *hMcbsp_ch = MCBSP_open(devNum, MCBSP_OPEN_RESET); MCBSP_reset(*hMcbsp_ch); /* Configure using structure @SPI_McbspConfig which defined above */ SPI_McbspConfig.spcr = MCBSP_SPCR_DEFAULT | (0 << _MCBSP_SPCR_DLB_SHIFT ) // 回环测试 | (3 << _MCBSP_SPCR_CLKSTP_SHIFT); // CLK_STP=11 SPI_McbspConfig.rcr = MCBSP_RCR_DEFAULT // (1)Single phase frame (2)Frame len=8bit | (1 << _MCBSP_RCR_RFIG_SHIFT ) // ignore first pulse | (1 << _MCBSP_RCR_RDATDLY_SHIFT ); // must be 1 SPI_McbspConfig.xcr = MCBSP_XCR_DEFAULT // (1)Single phase frame (2)Frame len=8bit | (1 << _MCBSP_XCR_XFIG_SHIFT ) // ignore first pulse | (1 << _MCBSP_XCR_XDATDLY_SHIFT ); // must be 1 SPI_McbspConfig.srgr = MCBSP_SRGR_DEFAULT | (1 << _MCBSP_SRGR_CLKSM_SHIFT ) // Sample-rate generator clock <- CPU clock & (~(1 << _MCBSP_SRGR_FSGM_SHIFT )) // DXR-to-XSR copy will generate FSX | (MCBSP_SRGR_CLKGDV_OF(24) << _MCBSP_SRGR_CLKGDV_SHIFT ); // (CPU/2) / (24+1) = 4MHz SPI_McbspConfig.mcr = MCBSP_MCR_DEFAULT; /* Using default value of MCR register */ SPI_McbspConfig.rcer = MCBSP_RCER_DEFAULT; SPI_McbspConfig.xcer = MCBSP_XCER_DEFAULT; SPI_McbspConfig.pcr = MCBSP_PCR_DEFAULT | (1 << _MCBSP_PCR_FSXM_SHIFT ) // FSXM driverd by FSGM bit in SRGR. | (1 << _MCBSP_PCR_FSRM_SHIFT ) // Frame-synchronization signal is generated internally by the sample-rate generator | (1 << _MCBSP_PCR_CLKXM_SHIFT ) // Master | (1 << _MCBSP_PCR_CLKRM_SHIFT ) // CLKR is output, driverd by Sample-rate generate | (1 << _MCBSP_PCR_FSXP_SHIFT ) // SPI/SS is low active & (~(1 << _MCBSP_PCR_CLKRP_SHIFT )) // receive at falling edge, CLKRP=0 & (~(1 << _MCBSP_PCR_CLKXP_SHIFT )); // transmit at rising edge, CLKXP=0 MCBSP_config(*hMcbsp_ch, &SPI_McbspConfig); SPI_Start(g_hMcbsp0); }
主要配置好时钟频率,空闲状态、采样边沿,帧长。
-
另外,考虑到主从机间时钟采样可能存在的问题,在使用SPI发送帧的时候,最后帧的每个发送字节间存在短暂的时延,避免通信错误的发送,如下,我Delay了5us,
void SPI_Write_Str(MCBSP_Handle hMcbsp_ch, const char *str, int32_t n) { while (n > 0) { while (!MCBSP_xrdy(hMcbsp_ch)); MCBSP_write(hMcbsp_ch, (Uint32)(*str++)); n--; Delay_us(5); } }
共1帧的数据:
前4个字节:
后3个字节: