最近在调试CC1310
跟NodeMCU
通过SPI
接口通信,中间遇到很多问题,在此记录一下。
首先是NodeMCU
实现的lua
操作SPI
的API
。目前为止的lua
操作SPI
的API
,是只能工作在MASTER
方式的,这也是我们把CC1310
作为从设备的原因。
其次NodeMCU
实现的默认收发API
中,都是一个字节一个字节循环读取的,不是批量读取。如下:
上面两个API
的示波器波形如下:
上图可以看到,每次操作一个字节之后,时钟都会暂停一段时间,然后操作下一个字节,也就是时钟不是连续的。
如果要实现一次读取多个字节,只能通过
1 |
spi.transaction(id, cmd_bitlen, cmd_data, addr_bitlen, addr_data, mosi_bitlen, dummy_bitlen, miso_bitlen) |
这个API
进行操作。
接下来,我们关注CC1310
作为从设备的情况,一般情况下,我们都是把CC1310
作为主设备来操作的。初始化SPI
的时候,使用默认参数,已经能应付大多数的硬件了。
但是当我们把CC1310
作为从设备使用的时候,需要注意,CC1310
默认情况下初始化SPI
参数的时候,使用如下函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/*! * @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
,SCLK
,CS
四根管脚连接了NodeMCU
上的HSPI
上的四根对应管脚,外加一个中断管脚,这个中断管脚,我们使用了NodeMCU
的D1
管脚。
这种接线的方式,使得我们可以逐个字节接收,不需要一次性读取完成。
另外,两者的上电顺序,也需要考虑一下,目前我们的界线方式是NodeMCU
需要先上电初始化才行,原因在于中断管脚发送中断之前必须确保NodeMCU
初始化完成了。
CC1310
的传输代码参考如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
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
的传输代码参考如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
-- 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
的技术文档参考如下: