3

我运行这个简化的 SPI 通信程序,在 TI MSP430FR5969 上的相应启动板 MSP-EXP430FR5969 上运行,并在 CCS (Code Composer Studio) 中的 TX 之前和 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;
    }
}
4

4 回答 4

3

SPI 外设在每个时钟周期发送一个位并接收一个位。与其想知道某些未连接的设备是如何发送一个字节的,不如认为您的 SPI 外围设备已在接收字节中计时,即使没有连接任何东西。您收到的字节为 0,因为 MISO 线恰好是低电平,而没有任何连接。

SPI 外设不知道数据的含义,也不知道对于任何特定命令必须发送和接收多少字节。何时发送和接收虚拟字节取决于您的应用程序。例如,如果从机在下一个字节中响应命令,那么您的应用程序必须发送两个字节(命令字节后跟一个虚拟字节)并同时接收两个字节(一个虚拟字节,然后是响应) . 一些从机可能会发送一个通用状态字节而不是一个虚拟字节作为所有响应的第一个字节。使用或忽略状态字节取决于您的应用程序。

MSP430 的 SPI 文档不会告诉您何时需要发送/接收虚拟字节。您必须阅读从设备的 SPI 文档以获取该信息。每个从站可能有不同的要求。一些从机接收到命令字节并发送回复。其他从机可能会在发送回复之前收到命令和地址字节。一些从机可能会回复多个字节。您必须对应用程序进行编程以发送/接收适当数量的字节。

没有停止位。主机在每个时钟都在发送和接收。如果您想停止接收,请停止发送。如果要继续接收,则发送虚拟字节。

是的,您应该使用 RX 中断。RX 中断表明您的应用程序可以安全地从 RX 寄存器读取接收到的字节。SPI 外设使用每个时钟将接收位移入移位寄存器。但是在接收到一个字节后,SPI 外设仍然需要将移位寄存器的内容复制到 RX 寄存器,然后设置 RX 中断。在指示 RX 中断之前,您不应假设可以从 RX 寄存器中读取接收到的字节。

于 2014-07-26T15:13:14.187 回答
3

如果启用了 MISO 和 MOSI(当然也启用了 CLK),则 SPI 外设会做两件事。假设主模式操作,它从 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) 移位寄存器,根据 UCMSB 设置从 MSB 或 LSB 开始在 UCxSIMO 上启动数据传输。UCxSOMI 上的数据在相反的时钟沿移入接收移位寄存器。当接收到字符时,接收数据从接收(RX)移位寄存器移动到接收数据缓冲器UCxRXBUF,并且接收中断标志UCXRIFG被置位,表示RX/TX操作完成。置位的发送中断标志 UCTXIFG 表示数据已从 UCxTXBUF 移至 TX 移位寄存器,并且 UCxTXBUF 已准备好接收新数据。它并不表示 RX/TX 完成。要在主模式下将数据接收到 eUSCI,

客户端不会自行向 MSP430 发送数据。客户端设备可能需要一些时间来执行主控方刚刚发送的命令。通常是 SPI 闪存芯片的“擦除闪存”命令。

在这种情况下,主设备,即 MSP430,必须轮询客户端设备以查看它是否有数据要发送/完成命令。这通常通过轮询客户端设备的状态寄存器(或通过使用专用 IRQ 中断)来完成。即客户端通过状态字节(或IRQ中断)发出“命令完成”/“数据可用性”信号。在这种情况下,主设备可以从客户端读取数据。

乍一看,需要写入数据(虚拟字节)才能读取数据,这似乎与直觉相悖——也许你也是混淆的根源:)

也许阅读有关 SPI 客户端的信息可能会有所帮助。例如这个 SPI 存储器

于 2014-07-26T15:36:01.543 回答
2

SPI 总线旨在成为封闭路径。来自您的处理器的 TX 线出去并以菊花链形式连接到一个或多个从设备,然后环回到您的 RX 引脚。时钟线上的每次转换都会驱动一个数据位。这意味着对于每次转换,您的硬件都会将一位移入您的接收缓冲区。在开始读取真实数据之前,由您的代码知道要丢弃多少位。

您正在读取 0,因为没有任何东西在驱动 RX 引脚。当您连接到真实设备时,您发送的前几个字节也可能会在您的 RX 引脚上生成 00。通常,您必须向从设备发送某种命令字节,然后从设备开始发送真实数据。该命令的长度应该被丢弃,因为在命令字节(字、字符串等)完成之前,从设备不会开始驱动其输出引脚。

于 2014-07-26T04:34:52.877 回答
2

您描述的行为是可以预期的。对于 SPI,时钟线上的运动表明数据的存在。输入线可以空闲,并且将接收数据,因为为了发送一个字节,时钟必须来回切换以锁存发送的数据,但同时,数据被锁存到接收缓冲区中。

于 2014-07-26T03:25:48.330 回答