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.

MSP430FR5729: Problems with implementing MCP23S08 8 bit Port Expander on MSP430FR5729

Anonymous
Anonymous
Guru 17045 points
Part Number: MSP430FR5729

Hello,

I'm using a MCP23S08 8-Bit I/O Expander with Serial Interface.

I want flash the LED with an SPI Microchip "MCP23S08".

 My C Code:

#include <msp430.h>

#define MCP_IODIR   0x00
#define MCP_IPOL    0x01
#define MCP_GPINTEN 0x02
#define MCP_DEFVAL  0x03
#define MCP_INTCON  0x04
#define MCP_IOCON   0x05
#define MCP_GPPU    0x06
#define MCP_INTF    0x07
#define MCP_INTCAP  0x08
#define MCP_GPIO    0x09
#define MCP_OLAT    0x0a
#define OPCODE      0x40 //0100A2A1 (01000000)

void SPI_init(void);              // initialize the SPI communication
void SPI_send(UChar data);        // send a SPI command
void Timing_init(void);

int main(void)
{
    WDTCTL = WDTPW + WDTHOLD;    // stop Watchdog timer

    Timing_init();              // set Timing

    SPI_init();                 // Initialize SPI-Module UCA1

    while(1)
    {
        P3OUT &= ~BIT2;    // CS set low
        __delay_cycles(200);
        while ((UCA1IFG & UCTXIFG) == 0);
        SPI_send(MCP_IODIR); // init IODIR
        P3OUT |= BIT2; // CS set high
        __delay_cycles(200);


        P3OUT &= ~BIT2;    // CS set low
        __delay_cycles(200);
        while ((UCA1IFG & UCTXIFG) == 0);
        SPI_send(OPCODE); // init OPCODE
        P3OUT |= BIT2; // CS set high
        __delay_cycles(200);


        P3OUT &= ~BIT2;    // CS set low
        __delay_cycles(200);
        while ((UCA1IFG & UCTXIFG) == 0);
        SPI_send(MCP_IODIR); // init MCP_IODIR
        P3OUT |= BIT2; // CS set high
        __delay_cycles(200);


        P3OUT &= ~BIT2;    // CS set low
        __delay_cycles(200);
        while ((UCA1IFG & UCTXIFG) == 0);
        SPI_send(0x00); // init 0x00 LED0
        P3OUT |= BIT2; // CS set high
        __delay_cycles(200);

    }
}

void SPI_init(void)
{
    P2SEL1 |= BIT4;               // Select secondary function of P2.4 (UCA1CLK)
    P2SEL1 |= BIT5;               // Select secondary function of P2.5 (UCA1SIMO)
    P2SEL1 |= BIT6;               // Select secondary function of P2.6 (UCA1SOMI)

    P3DIR |= BIT2;                // Declare P3.2 as Select Slave port
    P3OUT |= BIT2;                // Start with high on Select Slave P3.2

    UCA1CTLW0 |= UCCKPH;          // select clock phase to rising clock phase
    UCA1CTLW0 |= UCMSB;           // direction of shift register
    UCA1CTLW0 |= UCMST;           // SPI master
    UCA1CTLW0 |= UCSYNC;          // Synchronous mode enable
    UCA1CTLW0 |= UCSSEL_2;        // SMCLK clock selected
    UCA1CTLW0 |= UCSWRST;         // Software reset enable. Have to be selected here

    UCA1CTL1 ^= UCSWRST;          // Software reset disable. Set this way UCSWRST = 0
}

void SPI_send(UChar data)
{
//    P3OUT &= ~BIT2;                // Select Slave with setting P3.2 as low

    UCA1TXBUF = data;                // send data

//    P3OUT |= BIT2;                // End Select Slave with setting P3.2 as high
}

void Timing_init(void)
{
    // set up Clock System
    CSCTL0 = CSKEY;                                         // enable clock system
    CSCTL1 = DCOFSEL_3;                                     // frequency = 8,0 MHz
    CSCTL2 = SELA__XT1CLK | SELS__DCOCLK | SELM__DCOCLK;    // select clock sources
    CSCTL3 = DIVA__8 | DIVS__32 | DIVM__8;                  // set frequency divider
    CSCTL4 = XT2OFF | XTS | XT1DRIVE_0;                     // XT2 disabled, XT1: HF mode, low power, no bypass
    CSCTL0_H = 0;                                           // disable clock system
}

Could someone show me the spot I did wrong, or tell me what I might have forgotten.

I tried to study datasheet but it not confortable to get that chip working.

  • As I read the Data Sheet (21919E) section 1.3.3.1, a register write operation takes 3 bytes between /CS transitions: (a) an address/direction byte (0x00 for your Write), (b) a register address (IODIR), (c) a value (OPCODE).

    If you decide later you want to read a register, the address/direction byte ((a) above) would be 0x01, and for (c) you'd write a "dummy" byte and capture the RXBUF result.

    It's unfortunate that they don't have a better diagram for SPI mode, but it's similar (at least analogous) to the I2C sequence.
    --------------------
    > UCA1CTLW0 |= UCSWRST; // Software reset enable. Have to be selected here

    This should be done at the beginning of the initialization sequence, not near the end. Some fields aren't settable when UCSWRST=0.
  • Thanks for the answer.
    I have implemented your advice.
    Do you know why the LEDs do not light or why I do not get a signal?
    Did I forget anything else?
  • What does it do now? What does your code look like?

    I would expect to see it set the IODIR register (0x00) once [0x40, 0x00, 0x40]. (It looks like IODIR is initially 0xFF [per Table 1-3], so this may not even be needed.)

    Then you would alternately set the GPIO register (0x09) to 0x40 then 0x00 with [0x40, 0x09, 0x40] and [0x40, 0x09, 0x00]. Be sure to insert a sufficient delay (maybe a half-second) between the settings so you can see it change.

    Each of the transactions (within "[]" above should be bracketed by lowering, then raising CS.

    -------------------------------------------------

    You'll make your life easier if you encapsulate some of these sequences into functions, maybe one to exchange a byte and another to do a full transaction (using the first function 3 times).

    Here's a function I keep finding myself writing. It eXchanges a single byte over the SPI. (If you're only transmitting, you can throw away the function result.)

    uint8_t spix(uint8_t c)
    {
      while (!(UCA1IFG & UCTXIFG)) /*EMPTY*/;
      UCA1TXBUF = c;
      while (!(UCA1IFG & UCRXIFG)) /*EMPTY*/;
      c = UCA1RXBUF;
      return(c);
    }

    [Edit: I just noticed (Fig 1-5) that the address byte for a Write should be 0x40, not 0x00. I guess that's  what OPCODE was supposed to be?]

  • test

  • I don't have your equipment, so all I can do is work from the data sheet.

    I think you'll want a function to write a register in the device, something like:

    void set_reg(uint8_t reg, uint8_t value)
    {
        P3OUT &=  ~BIT2;  // CS low
        spix(0x40);       // Address byte with RW=0 [ref data sheet (21919E) Figure 1-5]
        spix(reg);        // Register to set
        spix(value);      // Value to set it to
        P3OUT |=  BIT2;   // CS high
        return;
    }

    and then call it something like:

    set_reg(GPIO,0x40);    // LED on pin 6 on
    set_reg(GPIO,0x00);    // LED on pin 6 off