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.

Need help with SPI

I am a beginer.I have some trouble in writing and reading data when controling the RF chip,SI4432.The problem is,i read what i write to the slaver,and cannot read the right value of the selected register.Here are some related codes.

SPI initial

void Spi_Init()
{
U0CTL = CHAR + SYNC + MM + SWRST; // 8-bit, SPI, Master
U0TCTL = SSEL1 + STC; // Polarity, SMCLK, 3-wire
U0BR0 = 0x08; // SPICLK = SMCLK/2
U0BR1 = 0x000;
U0MCTL = 0x000;
ME1 |= USPIE0; // Module enable
U0CTL &= ~SWRST; // SPI enable
P3DIR |= (BIT1 + BIT3 + BIT5 + BIT4);
P3DIR &= ~BIT2;
}

Write one byte to the selected register

void SpiWriteRegister(uint8 addr, uint8 value)
{
RF4432_SEL_0;
IFG1 &= ~UTXIFG0;
TXBUF0 = (addr|WR);
while ((IFG1 & UTXIFG0) == 0);
IFG1 &= ~UTXIFG0;
TXBUF0 = value;
while ((IFG1 & UTXIFG0) == 0);
RF4432_SEL_1;
}

Read one byte from the selected register

uint8 SpiReadRegister(uint8 addr)
{
uint8 value=0;
RF4432_SEL_0;
IFG1 &= ~UTXIFG0;
TXBUF0 = (addr|RR);
while (!(IFG1 & UTXIFG0));
IFG1 &= ~UTXIFG0;
TXBUF0 = 0xff;
while (!(IFG1 & URXIFG0));
value = U0RXBUF;
while (!(IFG1 & UTXIFG0));
RF4432_SEL_1;
return value;
}

When excuting SpiReadRegister(0x03),for example,the return value is 0x03,not the value of  this register.I wonder if there is someone can help,many thanks!

  • Don't know if that solves your problem, but what I see at first sight:

    void SpiWriteRegister( uint8 addr, uint8 value )
    {
      RF4432_SEL_0;
      IFG1 &= ~UTXIFG0;
      TXBUF0 = (addr|WR);
      while( (IFG1 & UTXIFG0) == 0);
      IFG1 &= ~UTXIFG0;
      TXBUF0 = value;
      while( (IFG1 & UTXIFG0) == 0);
      RF4432_SEL_1;
    }

    You do the following:

    • You are selecting the device
    • You clear the TX-flag which is OK here, but not necessary as it is cleared when writing data to the TX-buffer
    • You put your data into the TX-buffer
    • You wait until the TX-flag is set for signaling the buffer is free
    • You are clearing the TX-flag which is again not necessary, but it is OK
    • You put your next data into the buffer
    • MISTAKE: You are waiting for the flag to be set again before you...
    • ...unselect your device again

    But a set TX-flag does not mean the transmission has finished, it just says the buffer is ready for fetching a byte that is transmitted after the current byte that is transmiited at the moment has finished. So you are disabling your device before it has received both bytes. The TX-flag gets set when the byte is moved from the TX-buffer to the transmit shift-register. You cancel your transmission after the SPI is starting to send the byte.

    You should poll the RX-flag instead to be sure that the byte has been transmitted completely. Note: A SPI transmission is always a transmit-receive-pair. If you transmit 8 bits you also receive 8 bits. Of course during sending you don't care about the received data. Do it in that way:

    void SpiWriteRegister( uint8 addr, uint8 value )
    {
      RF4432_SEL_0;
      IFG1 &= ~UTXIFG0;              // Not necessary here
      TXBUF0 = (addr|WR);
      while( (IFG1 & UTXIFG0) == 0);
      IFG1 &= ~URXIFG0;              // Clear the receive flag
      TXBUF0 = value;
      while( (IFG1 & URXIFG0) == 0); // Wait for the receive-flag to be set
      RF4432_SEL_1;
    }

    Dennis

  • Instead of polling the RX flag (which might bee too early, depending on used phase), one can also poll the UCBUSY bit. However, the standard USCI doesn't provide an interrupt trigger for UCBUSY (IIRC, the eUSCI does), so it must be polled in a busy-loop.
  • As you know when to stop data transfer, spi data length counter have reached 0 etc (it's the TX IRQ that is subtraction it)
    I always use RX to set nCS as polling in a loop is not allowed in my multitasking state-machine programming style.
     

            bic.b	#UCA0RXIFG,&IFG2			; Clear UCSI IRQ flag, a read would also clear it.
    		tst.w	&spiLen			        	; though there is no data we are interested in command mode
    		jnz 	_exit_a0_rx			  	    ; we use read_irq anyway as to set CSn
    		bis.b	#WlanCSpin,&WlanCSport		; chip select high = off
    _exit_a0_rx	reti

  • Well, a polling loop isn't the best, agreed. However, some SPI slaves need some time after the last clock edge before CS may be released. It all depends on relation of SPI clock speed and CPU clock speed and interrupt latency etc.
    A possible (clean) solution if you already have a runtime environment, would be a timer event that releases CS a defined time after the last transfer. Or you notify the thread that has initiated the transfer, that data has been sent, and the thread can then decide whether to release CS or continue the transfer with new data without releasing CS. I'd say this is the most flexible version. And in fact, I do something similar in one of my projects.
  • >some SPI slaves need some time after the last clock edge before CS may be released

    true, my programming style does allow for uS delays using loop dec R15 , jnz loop
    as they possible can not hang as eventually it will be zero even if by mistake  R15 started at 0.
    I created a macro called delay (uS, register) that creates a hard loop and is only used for under 100uS pauses
    Selecting register used is important as scratch registers used in interrupts are not the same allowed in main code
    my interrupts don't use the stack to restore registers to save cycles and power drain.
     

    bic.b #WlanCSpin,&WlanCSport ; chip select low
    delay 50, R15                ; 50uS delay use r15

**Attention** This is a public forum