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.

MSP430FR5728 - SPI Difficulties

Other Parts Discussed in Thread: MSP430FR5728

I have been attemting to Coordinate my MSP430 with a seperate Flash chip known as the N25Q00AA.  Over a month ago I posted a problem I was having on the forum as can be seen here for anyone that wishes to look back and see what was previously discussed.


Since then, I feel that progress has been made.  However, a new problem has arisen since then, making the SPI functionality of my chip work properly.  While using a Oscilloscope to determine what was outputting from my system, I noticed that the SPI outputs of my device were all showing only noise, and no visible pattern could be observed.  This is clearly a problem, as the system should be outputting at least something from the SPI clock output.


To clarify, I wish to use my MSP430FR5728 in SPI B0 mode, and have the MSP be the master device with a separate flash memory chip being the slave device.  I was not 100% sure how to select which clock source to use, so to be safe I had the ACLK, SMCLK, AND MCLK all operate at the same frequency of ~1MHz, which is well within the operating parameters of both of my devices.  The MSP should also be outputting the command to the slave device, however I again am only seeing noise. 

It is because of this that I believe that the problem is that I am not properly declaring what the pins functions should be.  Within the MSP430FR57xx family user guide, page 295 details setting each pin for their function.  I am sure that I selected the proper function setting for each SPI pin, but there is a possibility that I did not select the right state.  The problem could lie elsewhere, and as such I will link my code below for everyone to see.  If anyone could find the problem, I would greatly appreciate the help.  Hope I used the insert code button correctly too

/***********************************************************
Code Goal:  Write a Byte of data to address of N25Q00AA.
After which, read the same address to prove that the
data byte was properly recorded to the N25Q00AA.  

Write:  Datasheet states need to Enable Write, send 3 address 
bytes, and requires the READ FLAG STATUS REGISTER command being 
issued with at least one byte output.

Read: send 3 address bytes of where want to read

Program: IAR Embedded Workbench IDE
MCU: MSP430FR5728
SPI: B0 mode selected from device
UCB0SIMO: P1.6
UCB0S0MI: P1.7
UCB0CLK: P2.2
CSn: PJ.1
***********************************************************/

#include "string.h"
#include "msp430.h"
#include <stdio.h>

char b1 = 0x03;          // Flash Addr Part 1 for Write
char b2 = 0xFF;          // Flash Addr Part 2 for Write
char b3 = 0xFF;          // Flash Addr Part 3 for Write

char b4 = 0x03;          // Flash Addr Part 1 for Read
char b5 = 0xFF;          // Flash Addr Part 2 for Read
char b6 = 0xFF;          // Flash Addr Part 3 for Read

char Q;

unsigned char RXData =0;
unsigned char TXData;

/***************************************************
                     Flash Code
***************************************************/

char FlashByte(char S){       // General FlashByte Transmit
  while (!(UCB0IFG & UCTXIFG));
  UCB0TXBUF = S;
  return (UCB0RXBUF);
}
  
char FlashComW(char x)       // Write Command and Address
{
  FlashByte(x);            // Send Command and Address
  FlashByte(b1);
  FlashByte(b2);
  FlashByte(b3);
  Q = FlashByte(0xAA);    // Send value to write
  return(Q);
}

char FlashComR(char x)        // Read Command and Address
{
  FlashByte(x);    // Send Command and Address
  FlashByte(b4);
  FlashByte(b5);
  FlashByte(b6); 
  Q = FlashByte(0xFF);    // Send dummy value so read
  return(Q);
}

/***************************************************
                     Main Code
***************************************************/

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

  PJDIR |= 0x02;                               
  PJOUT |= 0x02;
  
  
  // Clock Setup For SPI
  CSCTL0_H = 0xA5;
  CSCTL1 |= DCOFSEL0 + DCOFSEL1;                // Set DCO = 8MHz
  CSCTL2 = SELA_3 + SELS_3 + SELM_3;            // set ACLK = SMCLK = MCLK = DCO
  CSCTL3 = DIVA_2 + DIVS_3 + DIVM_3;            // ACLK -= SMCLK = MCLK = 1MHz
  
  
  // Configure SPI
  // Page 296 - Function Select Register
  P1SEL0 |= BIT6 + BIT7;                        // Set pins for primary purpose
  P2SEL0 |= BIT2;                               // Set pin for primary purpose
  UCB0CTLW0 = UCMST|UCSYNC|UCMSB|UCSSEL_1|UCSWRST;
  UCB0BR0 = 0x02;                           // /2
  UCB0BR1 = 0;                              //
  UCB0CTLW0 &= ~UCSWRST;

  
  for(;;){ 
    
    /****** Write ******/
    // Write Enable Command
    PJOUT &= ~BIT1;
    FlashByte(0x06);
    PJOUT |= BIT1;
    // Write Command/Address
    PJOUT &= ~BIT1;
    FlashComW(0x02);
    PJOUT |= BIT1;
    // Write Disabled
    PJOUT &= ~BIT1;
    FlashByte(0x04);
    PJOUT |= BIT1;
    // Read Flag Status Register
    PJOUT &= ~BIT1;
    FlashByte(0x70);
    PJOUT |= BIT1;
    
    /****** Read ******/
    // Read Command/Address
    PJOUT &= ~BIT1;
    printf("check = %X\r\n",FlashComR(0x03));
    PJOUT |= BIT1;    
    
    /****** Erase ******/
    // Write Enable Command
    PJOUT &= ~BIT1;
    FlashByte(0x06);
    PJOUT |= BIT1;
    // Erase Command/Address
    PJOUT &= ~BIT1;
    FlashComW(0x20);
    PJOUT |= BIT1;
    // Write Disabled
    PJOUT &= ~BIT1;
    FlashByte(0x04);
    PJOUT |= BIT1;
  }    
}

It may also be worth noting two more things.  First is that I lacked a launchpad to use for testing this particular chip, however I did use the method show on the following website found here and I did test it with simpler codes that did work using the same setup.  The second thing to mention is that the program I am using is IAR embedded workbench.

  • Hey Chris2,

    Chris2 said:
    P1SEL0 |= BIT6 + BIT7; // Set pins for primary purpose P2SEL0 |= BIT2; // Set pin for primary purpose

    Very close but it should be register P1SEL1 and P2SEL1.  This is your biggest problem concerning getting the SPI to work.

    Chris2 said:
    CSCTL2 = SELA_3 + SELS_3 + SELM_3; // set ACLK = SMCLK = MCLK = DCO CSCTL3 = DIVA_2 + DIVS_3 + DIVM_3; // ACLK -= SMCLK = MCLK = 1MHz

    Typically you want to set ACLK = XT1 (SELA_0) for low-power mode operation and leave MCLK = SMCLK = DCO.  In this instance you would not divide ACLK (DIVA_0) and you would add the following lines:

    CSCTL4 |= XT1DRIVE_0;
    CSCTL4 &= ~XT1OFF;

    do
    {
    CSCTL5 &= ~XT1OFFG;
    // Clear XT1 fault flag
    SFRIFG1 &= ~OFIFG;
    }while (SFRIFG1&OFIFG); // Test oscillator fault flag

    However this is all optional, especially if you do not have an external 32768 MHz crystal handy.  Typically you would just use SMCLK as your clock source if operating from the DCO (UCSSEL_2 or UCSSEL_3 in the UCB0CTLW0 register).  You will also want to utilize the USCI_B0 RX interrupt if you plan on receiving anything from the N25Q00AA device (UCB0IE |= UCRXIE; and corresponding UCA_B0_ISR).

    Chris2 said:
    UCB0CTLW0 = UCMST|UCSYNC|UCMSB|UCSSEL_1|UCSWRST;

    The trickiest part of SPI communication involves setting the clock polarity and phase select.  The N25Q00AA Datasheet specifies that either polarity be low and data be captured on the first UCLK edge or the polarity be high and data be captured on the following UCLK edge.  For the UCB0CTLW0 register this means that either the UCCKPH or UCCKLP bit be set, but not both.  Otherwise you will probably not be communicating correctly with the device.

    Regards,

    Ryan

  • I think Ryan Brown1 has pointed to the cause for your immediate symptom. The next thing you'll run into is that you're not waiting for the SPI byte "trade" to complete. Between storing UCB0TXBUF and fetching UCB0RXBUF, you should wait for UCRXIFG to go high.

  • Thanks Ryan!  That did solve the problem I was having.  Sorry I didn't respond sooner.

    Bruce I do want to ask you though if something like this code would work in regards to first writing to the TXBUF, and then fetching/reading the RXBUF:

    while (!(UCB0IFG & UCTXIFG));
    UCB0TXBUF = S;
    while (!(UCB0IFG & UCRXIFG));
    X = UCB0RXBUF;
    return X;

    I only ask this because I do believe I attempted something similar to this before, but the code could never progress beyond that point.  It could just be that the slave device is defective and a new one would work properly.  What do you think?

  • Yes, this should work. On SPI, the USCI doesn't care for a slave being physically present. As long as there is a clock signal available, it will send out what you wrote to TXBUF and receive 'something' in RXBUF.
    However, there are two pitfalls: If you want maximum throughput, you will write the second byte to TXBUF while the first one hasn't been fully sent. And nothing has been received. So TX and RX are more than one byte apart. If you start with an empty TXBUF and always wait for the RX event after writing to TXBUF before you proceed, this is no problem, but you're wasting time. When not waiting for RXIFG, then you need to take this interleave into account.
    The second problem is that depending on phase and slave reaction speed, you might de-assert your chip select before the last bit has been captured by the slave. Or the slave has handled the reception of the last byte. This may cancel the current high-level protocol.
    This is especially true if you jsut synchronize your sending with TXIFG (so one byte is still in the queue)