1.问题描述
用nRF52832(SDK:Nordic SDK 17.0.2.)的SPIM和w5500通信时,发现网口初始化无法通过,用示波器分别捅了一下片选、时钟、MOSI、MISO,发现时钟居然是1byte和2byte交替出现的,MOSI数据也出错了:
蓝色是时钟,绿色是片选,黄色是MOSI。每当出现2byte的数据时,MOSI后面凭空跟了个0xff在后面,但是我的SPIM收发函数是1byte的:
/*****************************************************************************
** 描 述:写入、读出一个字节
** 参 数:Dat:待写入的数据
** 返回值:读出的字节
******************************************************************************/
uint8_t Spi_WriteReadOneByte(uint8_t Dat)
{
uint8_t len = 1;
spi_tx_buf[0] = Dat;
spi_xfer_done = false;
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi, spi_tx_buf, len, spi_rx_buf, len));
while(!spi_xfer_done);
return (spi_rx_buf[0]);
}
2.原因查找
经检查不是函数的问题,于是去翻阅了一下开发指南,找到了这样两段话:
符合我遇见的现象,于是去代码中查看了SPI的配置:
orc确实是0xff,于是把orc随意改成一个其他的值,再用示波器测量,发现MOSI发出的数据也相应发生了变化。但是我明明只调用了1byte数据收发函数,TXD.MAXCNT和RXD.MAXCNT都是1。
去Nordic论坛查找,发现有人和我出现了一样的问题,在官网上有一个纠错手册《Errata》,上面有方法解决这个问题(没想到竟是芯片自身的问题):
文件下载:Nordic Semiconductor Infocenter
3.问题解决
根据纠错手册提供的解决方法是:使用52832的PPI,将SPIM的CLK电平翻转作为事件端点,将SPIM关闭作为任务端点。每当CLK发生电平变化时,便会关闭SPIM。但是52832的SPIM无法在1byte数据没有传输完成时关闭,SPIM会等到1byte数据传输完成之后再关闭,进而解决了多余字节的问题。手册提供的函数如下:
/**
* @brief Work-around for transmitting 1 byte with SPIM.
*
* @param spim: The SPIM instance that is in use.
* @param ppi_channel: An unused PPI channel that will be used by the workaround.
* @param gpiote_channel: An unused GPIOTE channel that will be used by the workaround.
*
* @warning Must not be used when transmitting multiple bytes.
* @warning After this workaround is used, the user must reset the PPI channel and the
GPIOTE channel before attempting to transmit multiple bytes.
*/
void setup_workaround_for_ftpan_58(NRF_SPIM_Type * spim, uint32_t ppi_channel, uint32_t gpiote_channel)
{
// Create an event when SCK toggles.
NRF_GPIOTE->CONFIG[gpiote_channel] = (
GPIOTE_CONFIG_MODE_Event <<
GPIOTE_CONFIG_MODE_Pos
) | (
spim->PSEL.SCK <<
GPIOTE_CONFIG_PSEL_Pos
) | (
GPIOTE_CONFIG_POLARITY_Toggle <<
GPIOTE_CONFIG_POLARITY_Pos
);
// Stop the spim instance when SCK toggles.
NRF_PPI->CH[ppi_channel].EEP = (uint32_t)&NRF_GPIOTE->EVENTS_IN[gpiote_channel];
NRF_PPI->CH[ppi_channel].TEP = (uint32_t)&spim->TASKS_STOP;
NRF_PPI->CHENSET = 1U << ppi_channel;
// The spim instance cannot be stopped mid-byte, so it will finish
// transmitting the first byte and then stop. Effectively ensuring
// that only 1 byte is transmitted.
}
这样调用即可:
void spi_ppi_set(void)
{
setup_workaround_for_ftpan_58(NRF_SPIM0, 6, 0);
}
注意点:1.一旦建立起这个PPI通道,就只能单字节地收发数据了,若是想要多字节收发,需要关闭该通道;
2.需要使用硬件控制CS电平;
3.需要将orc改成0x00,不然数据还是会出错。