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.

SPI buffer problem

Other Parts Discussed in Thread: MSP430F5438A, CC2420, STRIKE

hi,

i'm trying to implement a communication protocol for the MSP430F5438A via the USCI B0 SPI interface (imposed) as part of my uni project. i might have just found out the reason why the communication's not working, though i'm not that sure that i can find the right solution, which is why i'm counting on your help.

now i have the read and write functions (using CCSv4) as follows, and while debugging, the UCB0TXBUF changes when I write into it (though not exactly what i expected), whereas the UCB0RXBUF remains at 0xFF from start to finish, which explains why i've never been able read anything, from the RAM to the FIFO (i'm trying to implement it with CC2420).

when i try to read via the spi, normally one sends 0x00 to the TX buffer. the UCB0RXBUF, though, is always at 0xFF, which is why the RX interrupt flag's always returns 1, which in turn explains why I always get a 0xFF reading.

void spi_write (UINT8 byte)

{    

    UCB0TXBUF = byte;

    while ( UCB0STAT & UCBUSY );

}


UINT8 spi_read (void)

{

UINT8 val;

UCB0TXBUF = 0x00;

while ( UCB0STAT & UCBUSY );

    while ( (!(UCRXIFG & UCB0IFG)) );

    val = UCB0RXBUF;

return val;

}

for example, to read from the RAM, i have:

void cc2420_read_ram (UINT8* p, UINT16 a, UINT8 c) 

{

UINT8 i;

CS_enable();

spi_write( CC2420_RAM_ACCESS | (a & 0x7F) );

  spi_write( ( (a >> 1) & 0xC0 ) | CC2420_RAM_READ_ACCESS );

  for (i=0; i<c; i++)

    {

      ((UINT8*)(p))[i] = spi_read();

    }

CS_disable();

}

i'm pretty sure about the address, so i doubt it comes from there. does it come from the initialization ? please help ! thanks !

VZ

  • Your descriptions seems to indicate that you missedor misinterpreted some details of the SPI protocol.

    Ven Zallow said:
    when i try to read via the spi, normally one sends 0x00 to the TX buffer.

    Yes. You can also write 0xff or anything else. In fact, some slaves expect 0xff as a 'NOP' instruction during a slave->master transfer.

    Ven Zallow said:
    the UCB0RXBUF, though, is always at 0xFF, which is why the RX interrupt flag's always returns 1, which in turn explains why I always get a 0xFF reading

    Here it gets fuzzy.

    SPI is bidirectional-Synchronous. That means, on every clock pulse, one bit is sent and one bit is received. Without exception. If nobody is listening, teh master will send into the void and receives '1' bits from an idle line. In fact, is the SOMI (slave-out-master-in) signal is not connected to anything or even the port pin is not configured properly, the master will still 'receive' something. There is no way for the master to verify whether there is someone answering at all. Teh software cna do a probability check by the received data (and that is exactly what you have done manually, finding that there mus tbe somethign wrong when you got 0xff all the time) but that's high-level abd beyond the SPI protocol itself.

    After 8 bits have been clocked out, 8 bits have been received and RXIFG is set. The only difference is the clock edge at which data is sent/received, so the moment the last bit is sent and the next byte is moved from TXBUF (if not empty) to the output shift register, differs by 1/2 clock cycle from the moment the received byte is moved from input shift register to RXBUF and RXIFG is set.

    Your code does not contain the setup for the USCI, so I don't know whether you have configured the port pins properly. You must set the proper bits in the corect PxSEL register or the USCI is not connected to the physical pins at all. (if you set the loopback bit.

    Ven Zallow said:
    does it come from the initialization ?

    That's the most likely explanation. Either the port configurtion is wrong or missing, or you should experiment with the CKPL and CKPH bits. In many cases, setting CKPL (clock polarity) bit is necessary for a proper timing.

    However, there are some minor issues with the code.

    In SPI_write, you should check for UCTXIFG before you  write to TXBUF and then just exit after the write. This greatly increases throughput.
    In SPI_read, it is similar. Wait for UCTXIFG before your do the dummy write, then wait for RXIFG to be set and do the read, then exit.
    In cc2420_read_ram, you should add another dummy write after sendign the command, then do the reads. This also greatly increases throughput (on higher SPI baudrates), since the next byte is being received wheil you're processing the last. In this case, however, it is recomended to first wait for RXIFG and do the read, then send the next dummy write, so any interrupt that may occur between the dummy write and the following read won't lead to a receive overflow.
    If your last operation was a read, then you can just de-assert CS after you got your result. This will shut the slave off from the bus even if the last dummy write is still under progress, but this doesn't matter. You just have to ensure that UCBUSY is clear before you assert CS the next time.
    If the last transmission was a write, you should wait for UCBUSY to be clear before you de-assert CS, to ensure the slave has properly received the last byte you sent.

    I implemented sets of macros for this: read_first (which does just a dummy write) read_next (which blocks interrupts, sets the next dummy write, then waits for the read and enables interrupts again) and read_last (which just waits for the received byte form the last dummy write). This allows maximum throughput (2MB/s continuous transfer on 16MHz MCLK/SPICLK)

  • hi jens-michael,

    thanks for your explanation on the SPI. now, using some of the suggestions that you've posted above, i've added some "dummy write" after each sent command to increase the throughput. i'm currently running on SMCLK at 8 MHz (verified by the oscilloscope), with the baud rate divided by 8 to have 1 MHz on the SPI tx/rx. somehow i still get 0xFF on each rx buffer. all the while i've been able to write on the tx buffer (i've verified the change in the tx buffer value) but never the rx buffer (always at 0xFF from start till finish). 

    here's my init below. i've tried the combinations of 00, 01, 10 and 11 on the clock polarity and clock phase, but nothing seem to change. 

    void spiInit (void)

    {

    /**********************************/

    /* P3.0 : CSn (0)       -> output */

    /* P3.1 : UCB0SIMO (1) -> output */

    /* P3.2 : UCB0SOMI (1) -> input  */

    /* P3.3 : UCB0CLK (1)   -> output */

    /* P3.4 : unused (0) -> output */

    /* P3.5 : unused (0) -> output */

    /* P3.6 : unused (0) -> output */

    /* P3.7 : unused (0) -> output */

    /**********************************/

    P3SEL = 0x0E; // 0000 1110

    P3DIR = 0xFB; // 1111 1011

    UCB0CTL0 = UCSWRST; // software reset

    UCB0CTL1 = UCSWRST;

    /*********************************************************************************************/

    /* Bit 7 : UCCKPH 1 - sampling on 2nd clock edge 0 - sampling on 1st clock edge       */

    /* Bit 6 : UCCKPL 1 - inactive state high 0 - inactive state low               */

    /* Bit 5 : UCMSB   1 - MSB first 0 - LSB first                                        */

    /* Bit 4 : UC7BIT 1 - 7-bit data 0 - 8-bit data                                       */

    /* Bit 3 : UCMST   1 - master mode 0 - slave mode                                       */

    /* Bit 2-1 : UCMODEx 00 - 3-pin SPI, 01 - 4-pin SPI (SE1), 10 - 4-pin SPI (SE0), 11 - I²C */

    /* Bit 0 : UCSYNC 1 - synchronous (SPI is synchronous) 0 - asynchronous                */

    /*********************************************************************************************/

    UCB0CTL0 = 0xCB; // 1100 1011 : Clock phase = 1, clock polarity = 1, LSB first (0), 8-bit (0), master mode (1), 4-pin SPI (01), synchronous (1)

    /********************************************************************/

    /* Bit 7-6 : UCSSELx 00 - N/A, 01 - ACLK, 10 - SMCLK, 11 - SMCLK */

    /* Bit 5-1 : unused                                                 */

    /* Bit 0 : UCSWRST 1 - software reset enabled 0 - disabled     */

    /********************************************************************/

    UCB0CTL1 = 0x80; // 10000000 : SMCLK

    /***************************************************/

    /* Prescaler value = { UCB0BR0 + (UCB0BR1 x 256) } */

    /***************************************************/

    UCB0BR0 = 0x08; // SPI CLK set baud (SMCLK/8)

    UCB0BR1 = 0x00;

    UCB0CTL0 &= ~UCSWRST; // clear reset

    UCB0CTL1 &= ~UCSWRST;

    }

    i've followed your instructions and added the tx interrupt flag before each tx buffer write, as seen below:

    void spi_write (UINT8 byte)

    {    

        while ((!(UCTXIFG & UCB0IFG)));

        UCB0TXBUF = byte;

    }

    UINT8 spi_read (void)

    {

    UINT8 val;

    while ( (!(UCTXIFG & UCB0IFG)) );

    UCB0TXBUF = 0x00;

    while ( (!(UCRXIFG & UCB0IFG)) );

    val = UCB0RXBUF;

    return val;

    }

    i've added dummy writes after each command, as seen below:

    void cc2420_read_ram (UINT8* p, UINT16 a, UINT8 c) 

    {

    UINT8 i;

    CS_enable();

    spi_write( CC2420_RAM_ACCESS | (a & 0x7F) );

    while ( (!(UCTXIFG & UCB0IFG)) );

    UCB0TXBUF = 0x00;

      spi_write( ( (a >> 1) & 0xC0 ) | CC2420_RAM_READ_ACCESS );

      for (i=0; i<c; i++)

        {

          while ( (!(UCTXIFG & UCB0IFG)) );

    UCB0TXBUF = 0x00;

          ((UINT8*)(p))[i] = spi_read();

        }

    CS_disable();

    }

    knowing that CS is defines as:

    /**********************************/

    /* Chip select for USCI B0 (P3.0) */

    /**********************************/

    #define CS_enable() P3OUT &= ~BIT0

    #define CS_disable() P3OUT |= BIT0

    basically i've read and reread the codes, and nothing seems to strike me as being wrong, unless i've missed a trick or two, or that i've completely misunderstood the SPI...

    Jens-Michael Gross said:

    Your code does not contain the setup for the USCI, so I don't know whether you have configured the port pins properly. You must set the proper bits in the corect PxSEL register or the USCI is not connected to the physical pins at all. (if you set the loopback bit.

    does it come from the initialization ?

    That's the most likely explanation. Either the port configurtion is wrong or missing, or you should experiment with the CKPL and CKPH bits. In many cases, setting CKPL (clock polarity) bit is necessary for a proper timing.

    [/quote]

    CHECK. i've tried different values on usci, the polarity/phase angle, but nothing changes. is there any way to initialize the tx/rx buffer, by the way ?

    Jens-Michael Gross said:

    In SPI_write, you should check for UCTXIFG before you  write to TXBUF and then just exit after the write. This greatly increases throughput.

    CHECK.

    Jens-Michael Gross said:

    In SPI_read, it is similar. Wait for UCTXIFG before your do the dummy write, then wait for RXIFG to be set and do the read, then exit.

    CHECK.

    Jens-Michael Gross said:

    In cc2420_read_ram, you should add another dummy write after sendign the command, then do the reads. This also greatly increases throughput (on higher SPI baudrates), since the next byte is being received wheil you're processing the last. In this case, however, it is recomended to first wait for RXIFG and do the read, then send the next dummy write, so any interrupt that may occur between the dummy write and the following read won't lead to a receive overflow.

    CHECK.

    i've actually checked with a multimeter, and the dedicated CS pin (P3.0) changes it's value every time i set or unset the CS, which means that somehow the usci and the port's working, electrical-wise. 

    now, while wondering why the rx buffer is always at the default value of 0xFF, i was wondering, why do we usually send a dummy write (sending 0x00 to the tx buffer) to be able to read, knowing that we could always send anything or not at all and the next thing we'll receive is whatever in the rx buffer ? it's like you said, a bi-directional interface. does that mean that if we send nothing, we'll receive nothing even though usci's powered up ?

    thanks !

    VZ

  • actually, in addition to what i've said above, the problem with USCI B0 RX buffer (UCB0RXBUF)  being at 0xFF starts when i tried to start the oscillator for the CC2420. in my init for the CC2420, the values change right after the line cc2420_cmd_strobe(CC2420_STROBE_XOSCON); where the command strobe function is defined as :

    void cc2420_cmd_strobe(UINT8 command)

    {

    CS_enable();

    spi_write(command);

    CS_disable();

    }

    before:

    UCBORXBUF = 0x00

    UCBOTXBUF = 0x00

    UCB0ICTL = 0x0200

    UCB0IFG = 0x02

    after:

    UCBORXBUF = 0xFF

    UCBOTXBUF = 0x01

    UCB0ICTL = 0x0300

    UCB0IFG = 0x03

    from then on, the RX buffer remains at 0xFF regardless of what i send to the TX buffer...

  • Ven Zallow said:
    UCB0ICTL = 0x0300
    UCB0IFG = 0x03

    UCB0ICTL contains UCB0IFG and UCB0IE. So this is the same register you're looking at. It basically measn that after the UCRXIFG bit is set adn you received something. And the TXBUF is ready for new data.

    The change in RXBUF from 0x00 to 0xff means that 0xff is what you received. For soem reason, teh input line was high all teh time whil the USCI was sendign and receiving. The slave didn't emit anything.

    Ven Zallow said:
    now, while wondering why the rx buffer is always at the default value of 0xFF

    There is no default value. RXBUF is either undefined (before first use after power-up, btu most likely 0x00), or it contains the last received value. Or you deliberately wrote something into it. :)

    Ven Zallow said:
    i was wondering, why do we usually send a dummy write

    because SPi is receiving while sending. There are only three signals. Ougoing, incoming and clock. On each clock pulse, one bit is sent and one is received. Writing a dummy into TXBUF starts the clocking operation for 8 bits, when in master mode. In slave mode, the master provides the clock. When the clock comes, something is received and sent, even if you failed to stuff TXBUF in time. (this is why an SPI slave is difficult to program)

    Ven Zallow said:
    does that mean that if we send nothing, we'll receive nothing even though usci's powered up ?

    Yep. If we don't write to TXBUF (in master mode), no clock signal is generated. You may have noticed that there is no 'start receiving' control. And it is synchronous, that means, teh clock is provided by teh master when teh master wants to send or receive. In asynchronous transfers (UART), each one is sending master and receiving slave. So if one wants to send, he sends. And if something is sent by teh otehr side, it is received. In synchronous mode, the master controls both directions by providing a clock if a transfer shall happen. And the this clock providing is triggered by the write to TXBUF.

    Ven Zallow said:
    've actually checked with a multimeter, and the dedicated CS pin (P3.0) changes it's value every time i set or unset the CS,

    Fine. Did you also check the SOMI pin? Does it have any signal (incoming)? and what about to SIMO pin? Do you see the 'dummy value' going out?
    One thing that might be wrong is that you twisted the two signals, so both, amster and slave, are talking on the same lien and listening on teh same line (which then is of course always high and both only read 0xff). And is the clock signal going out?

    Ven Zallow said:
    UCB0CTL0 = UCSWRST; // software reset
    UCB0CTL1 = UCSWRST;

    UCB0CTL0 does not have UCSWRST. You're setting (and later clearing) UCSYNC here. Doesn't hurt since UCSYNC is read-only and always set on USCIBx.

    You shouldn't set 4-pin SPI. It is for multi-master or master/slave mode and switches from master to slave mode if STE is pulled. What you need is plain 3pin SPI mode (the CS line is NOT counted here, as there cna be any number of CS lines and their use is plain high-level controlled by the software)

    Ven Zallow said:
    UCB0CTL1 = 0x80; // 10000000 : SMCLK

    Here, you set the clock to SMCLOCK, but at the same time prematurely clear SWRST. It may lead to an erraneous first transfer or even a baudrate of SMCLK instead of SMCLK/8, but otherwise, the initialization seems correct.

    If you don't have a slave attached (or do not assert its CS signal), you can shortcut the SOMI and SIMO pins and should see the TXBUF value reappearing in RXBUF (loopback).

     

     

**Attention** This is a public forum