This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] MSP430FR2355:以错误的顺序在 SPI 上接收数据字节

Guru**** 2516170 points
Other Parts Discussed in Thread: MSP430FR2355

请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1120716/msp430fr2355-receive-data-bytes-on-spi-in-the-wrong-order

器件型号:MSP430FR2355

我有一 个24位 ADC、我正在尝试通过 MSP430FR2355 LaunchPad 上的 SPI 进行原 型通信。  操作相当简单:

按下按钮触发 IRQ 后、MCU 的 SIMO 首先通过写入 MSP430的 UCA0TXBUF 寄存器向从 ADC 输入发送命令字节(0b01000101)。  为了响应命令字节、ADC 首先向   MCU 的 SOMI 线输出一个状态字节(0x17)、然后输出一 个配置字节0x33、只要我继续写入 UCA0TXBUF (使用一个虚拟字节)、同时保持 STE 线处于活动状态、此字节就会被重复发送 ( 我在 GPIO 上手动执行的操作)。  在我的代码中、我在 命令字节之后发送三个虚拟字节、我希望按以下顺序读取 UCA0RXBUF

(1)向  UCA0TXBUF 写入命令字节(0x01000101)、在  UCA0RXBUF 中读取0x17 并保存在接收[0]中
(2)将第1 个虚拟字节(0x00)写入  UCA0TXBUF、在  UCA0RXBUF 中读取0x33 并保存在接收中[1]
(3)将第2 个虚拟字节(0x00)写入  UCA0TXBUF、在  UCA0RXBUF 中读取0x33 并保存到接收[2]中
(4)将第3 个虚拟字节(0x00)写入  UCA0TXBUF、在  UCA0RXBUF 中读取0x33 并保存到接收[3]

当 我在 CCS 中调试程序并逐行执行时、我确实得到 了我所期望的结果-变量数组 receive[4]= {0x17、0x33、0x33、0x33}。  
但是、如果我让调试运行并在 SPI 事件后任意暂停程序以读取缓冲变量、我会得到接收[4]= {0x33、0x33、0x17、0x33}<-错误顺序

我重复了几次、这些结果是一致的。  我还使用混合示波器监视 SOMI、以确保 ADC 确实按照 我预期的顺序输出{0x17、0x33、0x33}-这是这样的。  因此、我不知道可以在哪里解决此问题。  

我希望有人能够帮助解释为什么会发生这种情况、或者我应该尝试自行解决这种问题。 谢谢、

#include <msp430.h> 

char send[] =
{
    0b01000101,                 // command
    0x00, 0x00, 0x00            // dummy
};

unsigned int position;
unsigned int receive[4];

int main(void)
{
    WDTCTL = WDTPW | WDTHOLD;   // stop watchdog timer

    UCA0CTLW0 |= UCSWRST;       // put eUSCI_A0 into SW reset

    UCA0CTLW0 |= UCSSEL__SMCLK; // choose 1MHz SMCLK as BRCLK
    UCA0BRW = 10;               // 1MHz divide by 10 = 100kHz

    UCA0CTLW0 |= UCSYNC;        // set bit to SPI mode
    UCA0CTLW0 |= UCMST;         // set bit to SPI master mode
    UCA0CTLW0 |= UCMSB_1;       // UCMSB_0=LSB; UCMSB_1=MSB
    //UCA0CTLW0 |= UCMODE1;       // 0b10:4-pin with STE active LOW
    //UCA0CTLW0 &= ~UCMODE0;      // ..
    //UCA0CTLW0 |= UCSTEM;        // enable STE output pin
    UCA0CTLW0 &= ~UCMODE1;       // 0b00:3-pin SPI (handle P1.4 STE manually as a port)
    UCA0CTLW0 &= ~UCMODE0;
    UCA0CTLW0 &= ~UCSTEM;

    P1SEL1 &= ~BIT5;            // P1.5 (UCA0SCLK)
    P1SEL0 |= BIT5;             // P1.5 ..
    P1SEL1 &= ~BIT7;            // P1.7 (UCA0SIMO)
    P1SEL0 |= BIT7;             // P1.7 ..
    P1SEL1 &= ~BIT6;            // P1.6 (UCA0SOMI)
    P1SEL0 |= BIT6;             // P1.6 ..

    //P1SEL1 &= ~BIT4;            // P1.4 (STE)
    //P1SEL0 |= BIT4;             // P1.4 ..
    P1DIR |= BIT4;              // P1.4 set as output (manual STE)
    P1OUT |= BIT4;              // P1.4 stays HIGH by default (to be cleared LOW to initiate SPI)

    P4DIR &= ~BIT1;             // P4.1 (SW1) clear as input
    P4REN |= BIT1;              // P4.1 (SW1) enable pull-up/down resistance
    P4OUT |= BIT1;              // P4.1 (SW1) pull-up
    P4IES |= BIT1;              // P4.1 (SW) interrupt edge select: high-to-low transition

    PM5CTL0 &= ~LOCKLPM5;       // get out of LPM
    UCA0CTLW0 &= ~UCSWRST;      // get out of SW reset

    P4IFG &= ~BIT1;             // P4.1 (SW1) clear IFG
    P4IE |= BIT1;               // P4.1 (SW1) enable IRQ

    UCA0IFG &= ~UCTXIFG;        // clear SPI Tx IFG
    UCA0IE |= UCTXIE;           // enable SPI TX IRQ

    __enable_interrupt();

    while(1)
    {
        // do nothing
    }

    return 0;
}

#pragma vector = PORT4_VECTOR
__interrupt void ISR_Port4_S1(void)
{
    position = 0;
    P1OUT &= ~BIT4;             // P1.4 (manual STE) pull LOW to initiate SPI
    UCA0TXBUF = send[position];
    receive[0] = UCA0RXBUF;

    P4IFG &= ~BIT1;
}

#pragma vector = EUSCI_A0_VECTOR
__interrupt void ISR_EUSCI_A0(void)
{
    position++;
    if(position < sizeof(send))
    {
        UCA0TXBUF = send[position];
        receive[position] = UCA0RXBUF;
    }
    else
    {
        UCA0IFG &= ~UCTXIFG;

        // add delays to ensure last UCA0RXBUF is read before P1.4 (STE) is set HIGH
        // P1.4 (manual STE) set HIGH to stop SPI
        __delay_cycles(200);
        P1OUT |= BIT4;
    }
}

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    代码:

            UCA0TXBUF = send[position];
            receive[position] = UCA0RXBUF;

    是个问题。 这将开始传输某个数据、然后立即读取在前一次传输期间接收到的数据。  更糟糕的是、结合 ISR、从 RXBUF 读取的第二个值很有可能相同。 因为当 TXIFG 被置位时、第一个字节将被移出。 更常见的操作是等待 RXIFG 被置位、然后读取 RXBUF。

    此外、如果从器件不希望在字节之间否定该信号、则让 SPI 硬件自动处理从器件选择信号会导致问题。 即使您保持 TXBUF 为满、数据表中实际上并未说明 STE 何时被否定、因此不管怎样、它可能会发生。

    不确定为什么在这里使用中断。 除非出于某种原因确实需要中断、否则我通常更喜欢轮询方法。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    只需添加一个使用基于 ISR 的 SPI 的示例、我们有 一个 SPI 代码示例、此代码提供了处理 RX 和 TX 标志的参考、以防止 David 描述的接收/发送条件。

    此致、

    Luke

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    你是对的。 我想我将放弃 中断。   当我在主循环中处理它们时、写入或读取现在可以正常工作、尽管有一些 while 循环在 TXIFG 和 RXIFG 之间进行轮询。  我不确定这是正确还是干净的方法、但它可以正常工作。  谢谢你。

        while(1)
        {
            while(start_SPI == true)
            {
                if(position < sizeof(command))
                {
                    // UCTXIFG is cleared when a byte is written to UCA0TXBUF
                    UCA0TXBUF = command[position];      
    
                    // Wait until UCTXIFG is set when UCA0TXBUF is ready to accept another byte
                    while((UCA0IFG & UCTXIFG) == 0) {}  // do nothing
    
                    while(1)
                    {
                        // UCRXIFG is set when a byte is received and loaded into UCA0RXBUF
                        if ((UCA0IFG & UCRXIFG) != 0)   
                        {
                            // read UCA0RXBUF to clear UCRXIFG bit in UCA0IFG register
                            result[position] = UCA0RXBUF;
                            break;
                        }
                    }
                }
                else
                {
                    P1OUT |= BIT4;          // P1.4 (manual STE) set HIGH to stop SPI
                    start_SPI = false;
                }
                position++;
            }
        }