3

I run this simplified program for SPI communication, running on the TI MSP430FR5969 on its corresponding launchpad MSP-EXP430FR5969, and set breakpoints just before TX and just after RX in CCS (Code Composer Studio). The breakpoints are labelled with comments.

My launchpad is not connected to anything. (Once I figure this out I intend to communicate it to some other device for real communication.)

I do not expect to receive any data because the launchpad is not connected to anything. But I receive exactly one zero for every send. The breakpoints are hit in alternate order starting with the first TX breakpoint.

Why am I receiving data? Is it because I need to enable pullup registers on some of the pins? I believe the launchpad itself uses the USCI "A" module(s) so the "B" module that I am using should have nothing connected to it.

#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;
    }
}
necromancer
  • 23,916
  • 22
  • 68
  • 115
  • just edited the line `UCB0CTLW0 |= UCMODE_2` from `UCMODE_1` to `UCMODE_2` (probably not significant because the same problem occurs either way) – necromancer Jul 26 '14 at 01:02
  • @dwelch I didnt put any pull up, and I am not sure if the launchpad has it. The schematic doesn't show any resistors on that line. I believe the MSP430 has internal pullups that need to be enabled by software. My code does not do that. Should I try that? – necromancer Jul 26 '14 at 01:40
  • 1
    Put a scope on the line. See what is happening. – Martin James Jul 26 '14 at 14:03
  • @MartinJames dude, i'm just starting out, and i haven't even purchased my first multimeter yet! aren't scopes like a gazillion $$$$$? – necromancer Jul 26 '14 at 22:49
  • @MartinJames besides, I mention that the launchpad is not connected to anything at all, thus there would be no point probing the disconnected pin (unless possibly if there is a defect and the pin has a mind of its own). As the answers below show, it is a pretty obvious characteristic of the SPI protocol and rather simple thing to figure out. – necromancer Jul 27 '14 at 00:14

4 Answers4

3

The SPI peripheral transmits a bit and receives a bit for every clock cycle. Instead of wondering how some unconnected device has sent a byte, think that your SPI peripheral has clocked in a receive byte even though nothing is connected. The byte you receive is 0 because the MISO line happens to be low while nothing is connected.

The SPI peripheral does not know the meaning of the data and does not know how many bytes must be transmitted and received for any particular command. It's up to your application to know when to transmit and receive dummy bytes. For example, if the slave responds to a command in the next byte then your application has to transmit two bytes (the command byte followed by a dummy byte) and at the same time receive two bytes (a dummy byte, followed by the response). Some slaves may send a generic status byte instead of a dummy byte as the first byte of all responses. It's up to your application to use or ignore the status byte.

The MSP430's SPI documentation is not going to tell you when you need to send/receive dummy bytes. You'll have to read the SPI documentation for the slave device for that information. Each slave may have different requirements. Some slaves my receive a command byte and send a reply. Other slaves may receive a command and address byte before sending a reply. Some slaves may reply with multiple bytes. You'll have to program your application to transmit/receive the appropriate number of bytes.

There are no stop bits. The master is both transmitting and receiving with every clock. If you want to stop receiving then stop transmitting. If you want to continue receiving then transmit dummy bytes.

Yes, you should use the RX interrupt. The RX interrupt indicates that it is safe for your application to read the received byte from the RX register. The SPI peripheral is shifting receive bits into a shift register with each clock. But after a byte has been received the SPI peripheral still has to copy the contents of the shift register to the RX register and then set the RX interrupt. You shouldn't assume that the received byte can be read from the RX register until the RX interrupt is indicated.

necromancer
  • 23,916
  • 22
  • 68
  • 115
kkrambo
  • 6,643
  • 1
  • 17
  • 30
  • thank you, this is a great answer -- it is a hard decision not to accept this as the correct one instead of the other one. +1 – necromancer Jul 27 '14 at 00:14
3

The SPI peripheral does two things if MISO and MOSI are enabled (CLK enabled as well, of course). Assuming Master mode operation, it clocks out data from the TX shift register on the MOSI line and simultaneously clocks in data to the RX shift register from the MISO line.

In your circuit, the MISO input is hanging since you have not enabled either pull-up or pull-down internal resistances. Thus, observing 0x00 would not be out of the ordinary. If you had enabled the pull-up resistance, then you would have seen 0xFF in the receive buffer.

Another rule of thumb: If you are using the peripheral functions then configure the GPIO pins of the MSP430 as output/input. (i.e. MOSI, CLK = output, MISO = input for SPI master mode)

Answer to the questions in the comments:

The MSP430 is configured in the listed code to be the SPI master. I see little point in the using a dedicated RX interrupt service routine, unless you want the controller to do something else in the time between shifting data from the TX buffer to the shift register and shifting data from the RX shift register to the RX buffer, i.e. one "byte" transfer period. You could as well have polled for the RX interrupt as you have for the TX interrupt. But you must wait for the RX interrupt.

Excerpt from the user guide:

The eUSCI initiates data transfer when data is moved to the transmit data buffer UCxTXBUF. The UCxTXBUF data is moved to the transmit (TX) shift register when the TX shift register is empty, initiating data transfer on UCxSIMO starting with either the MSB or LSB, depending on the UCMSB setting. Data on UCxSOMI is shifted into the receive shift register on the opposite clock edge. When the character is received, the receive data is moved from the receive (RX) shift register to the received data buffer UCxRXBUF and the receive interrupt flag UCRXIFG is set, indicating the RX/TX operation is complete. A set transmit interrupt flag, UCTXIFG, indicates that data has moved from UCxTXBUF to the TX shift register and UCxTXBUF is ready for new data. It does not indicate RX/TX completion. To receive data into the eUSCI in master mode, data must be written to UCxTXBUF, because receive and transmit operations operate concurrently.

The client will not send data by itself to the MSP430. The client device may need some time to execute the command the master just sent. Typically an "erase flash" command for SPI Flash chips.

In this case the master, i.e. MSP430, must poll the client device to see if it has data to send/completed the command. This is done typically either by polling a status register of the client device (or by using a dedicated IRQ interrupt). i.e. the client signals "completion of command"/"availability of data" via the status byte (or IRQ interrupt). On this event, the master could read out data from the client.

At first glance it may seem rather counter intuitive that data (dummy bytes) needs to be written in order to read data - perhaps your source of confusion as well :)

Perhaps reading about an SPI client may help. For example this SPI memory.

ka05
  • 594
  • 1
  • 5
  • 12
  • Yes, the critical insight is that if the client will respond to a command in the same frame then the master must keep sending dummy data after the command because otherwise the client cannot send a response. (very counterintuitive to human communication -- we stop speaking after a question to hear the answer). I guess a telephone call would be a better analogy, we don't hang up but let the ambient noise from our side be transmitted so that we can hear the other person speak. – necromancer Jul 26 '14 at 23:52
  • Just one correction regarding configuring GPIO as output/input, the datasheet explicitly says that the direction is controlled by the USCI module and there is an X for the direction setting bit: http://www.ti.com/product/MSP430FR5969/datasheet/detailed_description?search=ucb0simo (unrelated I wonder why footnote (2) refers to eUSCI_A0 when the setting is for UCB0xxxx) – necromancer Jul 26 '14 at 23:56
2

The behavior you describe is to be expected. With SPI, it is movement on the clock line that indicates the presence of data. The input line can be idle, and data will be received because, in order to send a byte, the clock must be toggled back and forth to latch the transmitted data, but at same time, data is latched into the receive buffer.

benpro
  • 4,325
  • 4
  • 19
  • 17
  • +1 thank you. I have an identical followup question (posted as comment on answer by @ChrisRyding). I'd appreciate any insights. Thanks! – necromancer Jul 26 '14 at 04:51
2

An SPI bus is intended to be a closed pathway. The TX line from your processor goes out and is daisy-chained to one or more slave devices and then looped back to your RX pin. Every transition on the clock line drives a data bit. This means that for every transition your hardware will shift one bit into your receive buffer. It's up to your code to know how many of those bits to discard before you start reading real data.

You are reading 0's because nothing is driving the RX pin. When you're connected to a real device, the first several bytes you send will also likely generate 00's on your RX pin. Usually you'll have to send some sort of command byte to the slave device which then will start sending real data. The length of that command should be discarded because the slave will not have started driving its output pin until the command byte (word, string, whatever) is complete.

Chris Ryding
  • 1,508
  • 11
  • 16
  • +1 thank you. A question that bothers me then is how does the master know to keep the slave-transmit-line enabled after it has sent the command? When I read the user's guide for the MSP430 SPI module it didn't seem to specify that (but I may have missed a few paragraphs). Additionally, if the MSP430 is in master mode, is there any use for the RX interrupt, since I am assuming the client must send the response immediately upon command? So the master sends the commands then receives until some sort of stop bits, eliminates the number of command bytes and uses the rest. No need for RX interrupt? – necromancer Jul 26 '14 at 04:50
  • 1
    I looked at the MSP430 user guide and section 23.3 details how the slave transmit enable line works. You'll have to read through that and figure out what is appropriate to your setup. As far as the RX interrupt, it's a way to send out data and then have your code go off and do other things while you're waiting for data to come back. The interrupt tells you when something has been received. If you want to just wait and poll for an RX complete then you don't need the interrupt. – Chris Ryding Jul 26 '14 at 22:06