概述:为了减小硬件板子体积,也为了节约成本,现在需要把DWM1000模块从STM32单片机换到N32单片机上(国民技术),但是出现了很多问题,现在已经可以正常驱动了。把问题整理一下,看是否有朋友也遇到相同的问题。
这篇文章是我自己写错,现在来改正一下。纠正写到最后了
单片机和DWM1000的SPI引脚连接
单片机和DWM1000是通过SPI进行通信的,单片机做主机。DWM1000做从机,支持SPI四种工作模式,默认模式0(SCK空闲低,第一个沿采样)
我把DW1000初始化配置完成写好后,运行代码提示初始化失败。于是我看了MCU从dw1000芯片读取的数据是0xff,也就是没有读到,没有通信。于是我就用逻辑分析仪抓了SPI的时序看。
紫色的是SCK,蓝色的是MOSI。数据本来是应该是正常脉冲翻转,但是有时候会突然被拉低或者被抬高一下然后马上恢复回去,这样就相当于有了杂波,数据也不对了。造成这种情况无非就是两种,一种就是电路有杂波(EMI),所有导致数据有问题。另外一直就是dw1000接到MCU的MOSI线的也是输出线,两边都发数据抢线操作。
看了一下DWM1000引脚图,MCU的MOSI接到DWM1000的MISO,MCU的MISO接到DWM1000的MOSI,SPI引脚就是这么接的没问题啊。
然后把板子给硬件工程师帮忙分析一下电路有没有问题。后面确实找到问题了,DWM1000这个模块跟别的器件引脚标的不一样。
别的器件需要把SPI输出对输入这样接,DWM10000的SPI接法是输出对输出,输入对输入,也就是MCU的MOSI接DWM1000MOSI,MCU的MISO接DWM1000的MISO。
应该是模块上面引脚图标的就是MCU的引脚,这里要注意,不要接错了。
引脚换完后又抓了一下波形,这次波形正常了
于是我又重新初始化DW1000芯片,奇怪的是这次还是失败了。
有些单片机的SPI在高速和低速传的数据会不一样,需要注意
明明波形数据都对了,怎么还是有问题呢。我又分析了一下。因为DW1000芯片初始化的时候需要把SPI速率降到3M以下,初始化完成再把速率升起来。是不是因为速率降低了,导致数据传输又错误了。然后我把低速率SPI的数据抓了一下,果然,明明传的0x55变成了0xaa 。这个就很奇怪。
经过反复抓取数据发现了问题。这个应该是单片机的问题。
while(1)
{
GPIO_ResetBits(SPIx_GPIO_INIT,SPIx_CS_INIT); //拉低片选
for(i=0;i<Size;i++)
{
while(SPI_I2S_GetStatus(SPIx_INIT, SPI_I2S_TE_FLAG) == RESET);
SPI_I2S_TransmitData(SPI1, 0x55);
}
GPIO_SetBits(SPIx_GPIO_INIT,SPIx_CS_INIT); //拉高片选
}
while 等待发送缓冲区为空,当满足这个条件时,其实数据还没有完全发完,然后我就把CS拉高了。所以0x55—01010101 ,最后一位1还没发完CS就拉高了,然后拉低CS开始发送下一次的导致剩下的1当成下一次的头 本来01010101 ,前面多了个1 变成 10101010 ,挤出末尾的1也是去到下一次的头。所有就变成0xaa。高速的时候发的快,所以虽然拉高之后,while结束之后还没发完。但是在片选拉高之后也发完了。低速的时候很慢,所有while接收之后到片选拉高还有一位没发完。
所以低速的时候需要在while等待结束后面加一个延时,确保数据发完。
下面是官方和STM32单片机驱动DW1000的代码
//MCU通过SPI写入dw1000数据
int writetospi_serial
(
uint16 headerLength,
const uint8 *headerBuffer,
uint32 bodylength,
const uint8 *bodyBuffer
)
{
int i=0;
decaIrqStatus_t stat ;
stat = decamutexon() ; //关闭DW1000的中断,可以不写
SPIx_CS_GPIO->BRR = SPIx_CS; //拉低片选
for(i=0; i<headerLength; i++)
{
SPIx->DR = headerBuffer;
while ((SPIx->SR & SPI_I2S_FLAG_RXNE) == (uint16_t)RESET);
SPIx->DR ;
}
for(i=0; i<bodylength; i++)
{
SPIx->DR = bodyBuffer;
while((SPIx->SR & SPI_I2S_FLAG_RXNE) == (uint16_t)RESET);
SPIx->DR ;
}
SPIx_CS_GPIO->BSRR = SPIx_CS;
decamutexoff(stat) ;//开启DW1000的中断,可以不写
return 0;
} // end writetospi()
//MCU通过SPI读取dw1000数据
int readfromspi_serial
(
uint16 headerLength,
const uint8 *headerBuffer,
uint32 readlength,
uint8 *readBuffer
)
{
int i=0;
decaIrqStatus_t stat ;
stat = decamutexon() ;
/* Wait for SPIx Tx buffer empty */
//while (port_SPIx_busy_sending());
SPIx_CS_GPIO->BRR = SPIx_CS;
for(i=0; i<headerLength; i++)
{
SPIx->DR = headerBuffer;
//while((SPIx->SR & SPI_I2S_FLAG_RXNE) == (uint16_t)RESET);
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
readBuffer[0] = SPIx->DR ; // Dummy read as we write the header
}
for(i=0; i<readlength; i++)
{
SPIx->DR = 0; // Dummy write as we read the message body
while((SPIx->SR & SPI_I2S_FLAG_RXNE) == (uint16_t)RESET);
readBuffer = SPIx->DR ;//port_SPIx_receive_data(); //this clears RXNE bit
}
SPIx_CS_GPIO->BSRR = SPIx_CS;
decamutexoff(stat) ;
return 0;
} // end readfromspi()
下面是我写的N32(国民技术)驱动DW1000的代码
//MCU通过SPI写入dw1000数据
int writetospi_serial
(
uint16 headerLength,
const uint8 *headerBuffer,
uint32 bodylength,
const uint8 *bodyBuffer
)
{
int i=0;
int stat ;
// stat = UWB_Interrupt_off();
// SPIx_CS_GPIO->BRR = SPIx_CS;
GPIO_ResetBits(SPIx_GPIO_INIT,SPIx_CS_INIT); //拉低CS
for(i=0; i<headerLength; i++)
{
SPIx_INIT->DAT = headerBuffer;
//while(SPI_I2S_GetStatus(SPIx_INIT, SPI_I2S_TE_FLAG) == RESET); //换成这个初始化失败
while ((SPIx_INIT->DAT & SPI_I2S_RNE_FLAG) == (uint16_t)RESET);
Delay_ms(1); //延时必须加,不然失败
SPIx_INIT->DAT ;
}
for(i=0; i<bodylength; i++)
{
SPIx_INIT->DAT = bodyBuffer;
//while(SPI_I2S_GetStatus(SPIx_INIT, SPI_I2S_TE_FLAG) == RESET);//换成这个初始化失败
while((SPIx_INIT->DAT & SPI_I2S_RNE_FLAG) == (uint16_t)RESET);
Delay_ms(1);//延时必须加,不然失败
SPIx_INIT->DAT ;
}
// SPIx_CS_GPIO->BSRR = SPIx_CS;
GPIO_SetBits(SPIx_GPIO_INIT,SPIx_CS_INIT); //拉高CS
//UWB_Interrupt_on(stat) ;
return 0;
}
//MCU通过SPI读取dw1000数据
int readfromspi_serial
(
uint16_t headerLength,
const uint8_t *headerBuffer,
uint32_t readlength,
uint8_t *readBuffer
)
{
int i=0;
int stat ;
//stat = UWB_Interrupt_off() ;
// UWB_DISABLE_IRQ;
/* Wait for SPIx Tx buffer empty */
//while (port_SPIx_busy_sending());
//SPIx_CS_GPIO->BRR = SPIx_CS;
GPIO_ResetBits(SPIx_GPIO_INIT,SPIx_CS_INIT); //拉低CS
for(i=0; i<headerLength; i++)
{
//while(SPI_I2S_GetStatus(SPIx_INIT, SPI_I2S_RNE_FLAG) == RESET);
SPIx_INIT->DAT = headerBuffer;
//while((SPIx_INIT->DAT & SPI_I2S_TE_FLAG) == (uint16_t)RESET);
//while(SPI_I2S_GetStatus(SPIx_INIT, SPI_I2S_TE_FLAG) == RESET);
// while((SPIx_INIT->DAT & SPI_I2S_RNE_FLAG) == (uint16_t)RESET);
while((SPIx_INIT->DAT & SPI_I2S_RNE_FLAG) == (uint16_t)RESET);
//while(SPI_I2S_GetStatus(SPIx_INIT, SPI_I2S_RNE_FLAG) == RESET);//换成这个失败
Delay_ms(1);//延时必须加,不然失败
readBuffer[0] = SPIx_INIT->DAT ; // Dummy read as we write the header
//while(SPI_I2S_GetStatus(SPIx_INIT, SPI_I2S_RNE_FLAG) == RESET);
//Delay_ms(10);
}
for(i=0; i<readlength; i++)
{
//while((SPIx_INIT->DAT & SPI_I2S_RNE_FLAG) == (uint16_t)RESET);
SPIx_INIT->DAT = 0; // Dummy write as we read the message body
while(SPI_I2S_GetStatus(SPIx_INIT, SPI_I2S_TE_FLAG) == RESET);
//while((SPIx_INIT->DAT & SPI_I2S_RNE_FLAG) == (uint16_t)RESET);
while(SPI_I2S_GetStatus(SPIx_INIT, SPI_I2S_RNE_FLAG) == RESET);//这里可以多加一个代替延时
//Delay_ms(1);
/* {
printf("readtospi_2\r\n");
}*/
readBuffer = SPIx_INIT->DAT ;//port_SPIx_receive_data(); //this clears RXNE bit
}
//SPIx_CS_GPIO->BSRR = SPIx_CS;*/
GPIO_SetBits(SPIx_GPIO_INIT,SPIx_CS_INIT); //拉高CS
//UWB_ENABLE_IRQ;
// UWB_Interrupt_on(stat) ;
/* for(i=0;i<readlength; i++)
printf("readBuffer[%d] = %d\r\n",i,readBuffer);*/
return 0;
} // end readfromspi()
总结
遇到DWM1000初始化失败的时候,不一定是硬件或者自己写的代码有问题。也可能是官方的代码写法不适合自己使用的单片机。因为不同的单片机特性和情况不一样。找问题时候不能只看上层,也需要去底层寻找问题。先看看MCU的SPI数据有没有问题,然后看能不能读取DW1000的数据,两者是否能够正常通信。如果MCU那边数据正常但是不能正常读取DW1000数据,可以修改官方底层代码来进行调试。
纠正
其实这个是我自己写错了,SPI加了延迟以后。收发数据太慢了,对于定位测距来说是很大的问题。而且SPI传输需要加延时很明显是有问题的,于是我从头开始找问题。
终于找到问题了:
在while等待那里,我把状态寄存器写成了数据寄存器,所以while是没有效果的,加了延时后因为状态寄存器在延时时候置位了。所以可以正常通信了。
while((SPIx_INIT->DAT & SPI_I2S_RNE_FLAG) == (uint16_t)RESET); //DAT是数据寄存器而不是状态寄存器。
正确写法应该是这样:while((SPIx_INIT->STS & SPI_I2S_RNE_FLAG)
所以国民技术SPI和DW1000通信的正确代码如下:
int writetospi_serial
(
uint16 headerLength,
const uint8 *headerBuffer,
uint32 bodylength,
const uint8 *bodyBuffer
)
{
int i=0;
// stat = UWB_Interrupt_off();
GPIO_ResetBits(SPIx_GPIO_INIT,SPIx_CS_INIT); //拉低CS
for(i=0; i<headerLength; i++)
{
SPIx_INIT->DAT = headerBuffer;
while(SPI_I2S_GetStatus(SPIx_INIT, SPI_I2S_RNE_FLAG) == RESET);
SPIx_INIT->DAT ;
}
for(i=0; i<bodylength; i++)
{
SPIx_INIT->DAT = bodyBuffer;
while(SPI_I2S_GetStatus(SPIx_INIT, SPI_I2S_RNE_FLAG) == RESET);
SPIx_INIT->DAT ;
}
GPIO_SetBits(SPIx_GPIO_INIT,SPIx_CS_INIT); //拉高CS
//UWB_Interrupt_on(stat) ;
return 0;
} // end writetospi()
int readfromspi_serial
(
uint16_t headerLength,
const uint8_t *headerBuffer,
uint32_t readlength,
uint8_t *readBuffer
)
{
int i=0;
//stat = UWB_Interrupt_off() ;
GPIO_ResetBits(SPIx_GPIO_INIT,SPIx_CS_INIT); //拉低CS
for(i=0; i<headerLength; i++)
{
SPIx_INIT->DAT = headerBuffer;
while(SPI_I2S_GetStatus(SPIx_INIT, SPI_I2S_RNE_FLAG) == RESET);
readBuffer[0] = SPIx_INIT->DAT ; // Dummy read as we write the header
}
for(i=0; i<readlength; i++)
{
SPIx_INIT->DAT = 0; // Dummy write as we read the message body
while(SPI_I2S_GetStatus(SPIx_INIT, SPI_I2S_RNE_FLAG) == RESET);
readBuffer = SPIx_INIT->DAT ;//port_SPIx_receive_data(); //this clears RXNE bit
}
GPIO_SetBits(SPIx_GPIO_INIT,SPIx_CS_INIT); //拉高CS
return 0;
} // end readfromspi()
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_46107106/article/details/138178919
|