我运行这个简化的SPI通信程序,在相应的启动板MSP-EXP430FR5969上运行TI MSP430FR5969,并在TX之前和CCS(Code Composer Studio)中的RX之后设置断点。断点标有注释。
我的启动板没有任何连接。 (一旦我想到这一点,我打算将它传达给其他设备以进行真正的通信。)
我不希望收到任何数据,因为启动板没有连接任何东西。但每次发送我只收到一个零。从第一个TX断点开始,以交替顺序命中断点。
为什么我会收到数据?是因为我需要在某些引脚上启用上拉寄存器吗?我相信启动板本身使用的是USCI“A”模块,因此我使用的“B”模块应该没有任何连接。
#include <msp430.h>
int main(void) {
WDTCTL = WDTPW | WDTHOLD;
P1SEL0 &= ~BIT3; // UCB0STE
P1SEL0 &= ~BIT6; // UCB0SIMO
P1SEL0 &= ~BIT7; // UCB0SOMI
P2SEL0 &= ~BIT2; // UCB0CLK
P1SEL1 |= BIT3; // UCB0STE
P1SEL1 |= BIT6; // UCB0SIMO
P1SEL1 |= BIT7; // UCB0SOMI
P2SEL1 |= BIT2; // UCB0CLK
PM5CTL0 &= ~LOCKLPM5;
CSCTL0_H = CSKEY_H;
CSCTL1 &= ~DCORSEL;
CSCTL1 = (CSCTL1 & ~0x000e) | DCOFSEL_0; // 1 MHz
CSCTL3 |= DIVA__1 | DIVS__1 | DIVM__1; // clock dividers = 1
CSCTL0_H = 0;
UCB0CTLW0 |= UCSWRST;
UCB0CTLW0 |= UCCKPH;
UCB0CTLW0 |= UCCKPL;
UCB0CTLW0 |= UCMSB;
UCB0CTLW0 |= UCMST;
UCB0CTLW0 |= UCMODE_2;
UCB0CTLW0 |= UCSYNC;
UCB0CTLW0 |= UCSSEL__SMCLK;
UCB0CTLW0 |= UCSTEM;
// UCB0STATW |= UCLISTEN; // OK, if enabled i receive what i send
UCB0CTLW0 &= ~UCSWRST;
UCB0IE |= UCRXIE;
_enable_interrupts();
_delay_cycles(100000);
int send = 0;
while (1) {
while (!(UCB0IFG & UCTXIFG));
UCB0TXBUF = send; // BREAKPOINT 1
send = (send + 1) % 100;
_delay_cycles(100000);
}
return 0;
}
#pragma vector = USCI_B0_VECTOR
__interrupt void isr_usci_b0 (void) {
static volatile int received = 0;
switch (__even_in_range(UCB0IV, USCI_SPI_UCTXIFG)) {
case USCI_NONE:
break;
case USCI_SPI_UCRXIFG:
received = UCB0RXBUF;
UCB0IFG &= ~UCRXIFG; // BREAKPOINT 2
_no_operation();
break;
case USCI_SPI_UCTXIFG:
break;
}
}
答案 0 :(得分:3)
SPI外设发送一个位,并在每个时钟周期接收一位。不要想知道某个未连接的设备如何发送一个字节,而是认为即使没有任何连接,你的SPI外设也已经接收到了一个接收字节。您收到的字节为0,因为MISO线路恰好是低电平而没有任何连接。
SPI外设不知道数据的含义,也不知道任何特定命令必须发送和接收的字节数。由您的应用程序决定何时发送和接收虚拟字节。例如,如果从器件响应下一个字节中的命令,则应用程序必须传输两个字节(命令字节后跟一个虚拟字节),同时接收两个字节(一个虚拟字节,然后是响应) 。一些从设备可以发送通用状态字节而不是虚拟字节作为所有响应的第一个字节。应用程序可以使用或忽略状态字节。
MSP430的SPI文档不会告诉您何时需要发送/接收虚拟字节。您必须阅读从属设备的SPI文档以获取该信息。每个奴隶可能有不同的要求。一些奴隶我收到一个命令字节并发送回复。其他从站可能在发送回复之前接收命令和地址字节。一些从站可以回复多个字节。您必须对应用程序进行编程以发送/接收适当的字节数。
没有停止位。主机每个时钟都在发送和接收。如果你想停止接收然后停止发送。如果你想继续接收然后发送虚拟字节。
是的,你应该使用RX中断。 RX中断表示应用程序从RX寄存器读取接收到的字节是安全的。 SPI外设正在将每个时钟的接收位移入移位寄存器。但是在接收到一个字节后,SPI外设仍然必须将移位寄存器的内容复制到RX寄存器,然后设置RX中断。在指示RX中断之前,不应该假设可以从RX寄存器读取接收到的字节。
答案 1 :(得分:3)
如果MISO和MOSI被使能,SPI外设会做两件事(当然也是CLK使能)。假设主模式操作,它从MOSI线上的TX移位寄存器中输出数据,同时从MISO线将数据输入到RX移位寄存器。
在您的电路中,MISO输入挂起,因为您没有启用上拉或下拉内部电阻。因此,观察0x00不会与众不同。如果你已经启用了上拉电阻,那么你会在接收缓冲区看到0xFF。
另一个经验法则: 如果使用外设功能,则将MSP430的GPIO引脚配置为输出/输入。 (即MOSI,CLK =输出,MISO = SPI主模式的输入)
回答评论中的问题:
MSP430在列出的代码中配置为SPI主机。我认为使用专用的RX中断服务程序没有什么意义,除非您希望控制器在将数据从TX缓冲区移位到移位寄存器并将数据从RX移位寄存器移位到RX缓冲区之间的时间做其他事情,即一个“字节”传输周期。您也可以像对TX中断一样轮询RX中断。但是你必须等待RX中断。
摘自用户指南:
当数据移动到发送数据缓冲区UCxTXBUF时,eUSCI启动数据传输。当TX移位寄存器为空时,UCxTXBUF数据被移至发送(TX)移位寄存器,从MSB或LSB开始,在UCxSIMO上启动数据传输,具体取决于UCMSB设置。 UCxSOMI上的数据在相反的时钟边沿移入接收移位寄存器。接收到字符时,接收数据从接收(RX)移位寄存器移至接收数据缓冲区UCxRXBUF,接收中断标志UCRXIFG置位,表示RX / TX操作完成。 置位发送中断标志UCTXIFG表示数据已从UCxTXBUF移至TX移位寄存器,UCxTXBUF已准备好接收新数据。它不表示RX / TX完成。 要在主模式下将数据接收到eUSCI,必须将数据写入UCxTXBUF,因为接收和发送操作同时运行。
客户端不会自行将数据发送到MSP430。客户端设备可能需要一些时间来执行主设备刚刚发送的命令。通常是SPI闪存芯片的“擦除闪存”命令。
在这种情况下,主设备(即MSP430)必须轮询客户端设备以查看它是否具有要发送/完成命令的数据。这通常通过轮询客户端设备的状态寄存器(或通过使用专用IRQ中断)来完成。即客户端通过状态字节(或IRQ中断)发出“命令完成”/“数据可用性”的信号。在此事件中,主服务器可以从客户端读取数据。
乍一看,为了读取数据,需要编写数据(虚拟字节)似乎相当直观 - 也许是您混淆的原因:)
也许阅读SPI客户端可能有所帮助。例如this SPI memory。
答案 2 :(得分:2)
您描述的行为是可以预期的。对于SPI,它是 clock 线上的移动,表示存在数据。输入线可以是空闲的,并且将接收数据,因为为了发送一个字节,必须来回切换时钟以锁存发送的数据,但同时,数据被锁存到接收缓冲器中。
答案 3 :(得分:2)
SPI总线旨在成为一条封闭的通道。处理器的TX线路熄灭并以菊花链形式连接到一个或多个从设备,然后环回到RX引脚。时钟线上的每次转换都会驱动一个数据位。这意味着对于每次转换,您的硬件都会将一位移入接收缓冲区。在您开始阅读实际数据之前,您需要知道有多少这些位要丢弃。
您正在阅读0,因为没有任何东西在驱动RX引脚。当您连接到真实设备时,您发送的前几个字节也可能会在您的RX引脚上生成00。通常,您必须向从设备发送某种命令字节,然后该设备将开始发送实际数据。该命令的长度应该被丢弃,因为从器件将不会开始驱动其输出引脚,直到命令字节(字,字符串,等等)完成。