Ti CC1310作为从设备与NodeMCU通过SPI总线进行通信

最近在调试CC1310NodeMCU通过SPI接口通信,中间遇到很多问题,在此记录一下。

首先是NodeMCU实现的lua操作SPIAPI。目前为止的lua操作SPIAPI,是只能工作在MASTER方式的,这也是我们把CC1310作为从设备的原因。

其次NodeMCU实现的默认收发API中,都是一个字节一个字节循环读取的,不是批量读取。如下:

上面两个API的示波器波形如下:
上图可以看到,每次操作一个字节之后,时钟都会暂停一段时间,然后操作下一个字节,也就是时钟不是连续的

如果要实现一次读取多个字节,只能通过

spi.transaction(id, cmd_bitlen, cmd_data, addr_bitlen, addr_data, mosi_bitlen, dummy_bitlen, miso_bitlen)

这个API进行操作。

接下来,我们关注CC1310作为从设备的情况,一般情况下,我们都是把CC1310作为主设备来操作的。初始化SPI的时候,使用默认参数,已经能应付大多数的硬件了。

但是当我们把CC1310作为从设备使用的时候,需要注意,CC1310默认情况下初始化SPI参数的时候,使用如下函数:

/*!
 *  @brief  Function to initialize the SPI_Params struct to its defaults
 *
 *  @param  params      An pointer to SPI_Params structure for
 *                      initialization
 *
 *  Defaults values are:
 *      transferMode        = SPI_MODE_BLOCKING
 *      transferTimeout     = SPI_WAIT_FOREVER
 *      transferCallbackFxn = NULL
 *      mode                = SPI_MASTER
 *      bitRate             = 1000000 (Hz)
 *      dataSize            = 8 (bits)
 *      frameFormat         = SPI_POL0_PHA0
 */
extern void SPI_Params_init(SPI_Params *params);

可以看到,默认参数中的frameFormat被初始化成了SPI_POL0_PHA0

此时我们按照默认方式操作的时候,会发现一个很要命的问题,就是,两者通信的时候,只传递了第一个字节,后续字节CC1310不再发送。这点可以通过示波器确认。

研读CC1310技术文档,我们可以看到如下内容:

两页都注意一下For continuous back-to-back transmissions这部分,尤其是仔细看一下时序图

根据时序图,我们可以看到,当配置为SPI_POL0_PHA0模式的时候,每次发送完成一个字节,片选管脚SSln_FSS必须发生一次高低切换,才能发送第二个字节。而当配置为SPI_POL0_PHA1模式的时候,默认片选管脚SSln_FSS一直保持低电平就可以了。

如果两者要正常通信,目前只能把CC1310的报文格式设置为SPI_POL0_PHA1,并且必须接上片选管脚。

实际的接线,我们使用了5根线来处理,CC1310上的MOSI,MISO,SCLKCS四根管脚连接了NodeMCU上的HSPI上的四根对应管脚,外加一个中断管脚,这个中断管脚,我们使用了NodeMCUD1管脚。

这种接线的方式,使得我们可以逐个字节接收,不需要一次性读取完成。

另外,两者的上电顺序,也需要考虑一下,目前我们的界线方式是NodeMCU需要先上电初始化才行,原因在于中断管脚发送中断之前必须确保NodeMCU初始化完成了。

CC1310的传输代码参考如下:

SPI_Handle slaveSpi;

SPI_Params slaveSpiParams;

SPI_Transaction slaveTransaction;

bool transferOK;

/* Initialize SPI handle with slave mode */

SPI_Params_init(&slaveSpiParams);

slaveSpiParams.mode = SPI_SLAVE;   //SLAVE MODE

/*重点注意此处*/
slaveSpiParams.frameFormat = SPI_POL0_PHA1;

slaveSpi = SPI_open(Board_SPI1, &slaveSpiParams);

if (slaveSpi == NULL) {
	System_abort("Error initializing SPI\n");

}else {
	System_printf("SPI initialized\n");
}

/* Initialize slave SPI transaction structure */

slaveTransaction.count = SPI_MSG_LENGTH;

slaveTransaction.txBuf = (Ptr)slaveTxBuffer;

slaveTransaction.rxBuf = (Ptr)slaveRxBuffer;

/* Initiate SPI transfer */

transferOK = SPI_transfer(slaveSpi, &slaveTransaction);

if(transferOK) {

	/* Print contents of slave receive buffer */

	System_printf("Slave: %s\n", slaveRxBuffer);
} else {
	System_printf("Unsuccessful slave SPI transfer");
}

/* Deinitialize SPI */

SPI_close(slaveSpi);

上述代码仅供参考,真实环境中,我这把是把传输过程设置为异步模式,接着调用SPI_transfer发送完整的数据报文头给硬件(包含报文长度,类型等信息,报文头部长度固定,内容变长,整个提交过程,要求在一次传输中完成,不要异步分两次发送,原因在于两次切换之间存在时间间隔,哪怕非常小,都有可能导致数据出错,比如读取方在还没准备好就发送了读取命令),然后调用PIN_setOutputValue触发NodeMCU的中断管脚。

此处需要注意,一定是先提交数据,后发送中断,反过来,可能对端读取数据的时候,CC1310的硬件还没有准备就绪,导致传输错误。

CC1310中使用snprintf的时候遇到一个非常棘手的问题,就是经常出现发送的数据是不正确的现象,当不使用snprintf的时候,一切正常,经过不断排查,发现如果要使用snprintf,那么预留的线程栈空间不能小于2KB,这个栈需求确实有些偏高了。

NodeMCU的传输代码参考如下:

-- spi bus for CC1310 ,  1024Kbps
-- spi.setup(id, mode, cpol, cpha, databits, clock_div[, duplex_mode])
-- id SPI ID number: 0 for SPI, 1 for HSPI
-- mode select master or slave mode spi.MASTER spi.SLAVE - not supported currently
-- cpol clock polarity selection spi.CPOL_LOW spi.CPOL_HIGH
-- cpha clock phase selection spi.CPHA_LOW spi.CPHA_HIGH
-- databits number of bits per data item 1 - 32
-- clock_div SPI clock divider, f(SPI) = 80 MHz / clock_div, 1 .. n (0 defaults to divider 8)
-- duplex_mode duplex mode spi.HALFDUPLEX (default when omitted) spi.FULLDUPLEX
HSPI = 1
spi.setup(HSPI, spi.MASTER, spi.CPOL_LOW, spi.CPHA_LOW, spi.DATABITS_8, 64)

--gpio trigger for spi slave
local spi_trig_pin = 1
gpio.mode(spi_trig_pin,gpio.INT)

function spiIntPinCb(level,when,eventcount)
	r = spi.recv(HSPI,  3)
	r1 = spi.recv(HSPI, 1)
	print(string.format("v1 =%d,v2=%d,v3=%d" , string.byte(r,1),string.byte(r,2),string.byte(r,3)))
	print(string.format("r1 =%d" , string.byte(r1,1)))
end

gpio.trig(spi_trig_pin,"down",spiIntPinCb)

完整的CC1310的技术文档参考如下:

参考链接


发布者

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注