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.

CCS/MSP430FR2633: What is the best way to implement a SPI to UART bridge that reads & transmits 40 bits?

Part Number: MSP430FR2633


Tool/software: Code Composer Studio

I've created a custom board for AllAboutCircuits.com and now it's time to create the firmware. The board has a sensor (muRata SCA103T-D04), an ADC (LTC2380IDE), a MCU (MSP430FR2633), and a UART to USB converter (CP2102N)
I started writing code today, and the first order of business is to read the data over SPI.

There are 40 bits -- the first 24 is the data I really need -- in two's complement notation. The next 16 bits just indicate the number of times the inclinometer was sampled. Useful, but not critical, as I could just perform a single measurement.


I'm not sending any data into the ADC -- so whatever I shift out of the MSP430 is irrelevant.

So my first question is this: what is the "best practice" way to grab those first 24 bits? It appears that the MSP430 RXBuffer has 16-bits in it -- so bit shifting & masking is an option....provided of course I did it correctly.
An array of 3 8-bit numbers is the least complex method? Are there any gotcha's? Is there a better way?

Then I'll need to move the data over UART. I'm just getting it over the debug interface now. What is the matter with my clock configuration? Did I make a stupid mistake or do I have a fundamental misunderstanding? I do realize that the UART portion of the code appears in some places and is absent in others. I backed off and started focusing on just the SPI for now.



//
// ************************** Connection Diagram ******************************
//   CP2102N           ┌──────┬──────┐            ┌────────────┐
// ┌────────────┐      │      │ P1.0 │→ UCB0STE  →│ EN (NC)    │
// │    USB     │→ RX →│ P2.5 │ P1.1 │→ UCB0CLK  →│ CLK        │
// │    TO      │→ TX ←│ P2.6 │ P1.2 │→ UCBSIMO   │ SIMO (NC)  │
// │   UART     │      │      │ P1.3 │← UCBSOMI  ←│ SOMI       │
// └────────────┘      ├──────┼──────┤            │            │
//             ST_IN1 ←│ P2.0 │ P2.4 │← BUSY     ←│ BUSY       │
//             ST_IN2 →│ P2.1 │ P3.0 │→ RDL      →│ RDL        │
//                     │      │ P3.1 │→ CNV      →│ CNV        │
//                     └──────┴──────┘            └────────────┘
//                       MSP430FR2633                 ADC
//
// ****************************************************************************
// Mark Hughes for AllAboutCircuits.com
// This is a one-directional SPI to UART to USB bridge interface
// CNV is toggled one/multiple times (up to 65535) and data is averaged inside
// the ADC until SPI_CLK toggles data out and into MSP430.  Data is then sent
// via UART to CP2102N, and then from the CP2102N to the computer.
// UART communication, self-test and auto-calibration not yet implemented.
//*****************************************************************************
#include <msp430.h>
#include <driverlib.h>
#include <stdio.h>

#define SPI_EN_PIN          BIT0        // P1.0 UCB -- not used at all
#define SPI_CLK_PIN         BIT1        // P1.1 UCB
#define SPI_MOSI_PIN        BIT2        // P1.2 UCB -- not used yet
#define SPI_MISO_PIN        BIT3        // P1.3 UCB

#define UART_RX_PIN         BIT5        // P2.5 eUSCI_A
#define UART_TX_PIN         BIT6        // P2.6 eUSCI_A

#define ADC24_RDL           BIT0        // P3.0 set low always
#define ADC24_CNV           BIT1        // P3.1 toggle to start conversion
#define ADC24_BUSY          BIT4        // P2.4 goes low after conversion

#define ST_IN1              BIT0        // 3PST input 0
#define ST_IN2              BIT1        // 3PST input 1

// Variable Declarations.  Volatile during first phase of programming.
// Will eliminate with proper function calls & data returns later on.

volatile uint8_t byteCounter;           // need 24 bits + 16 bits (5 bytes)
volatile uint8_t counter;               // for various for loops
volatile uint16_t numReads;             // number of times to sample
volatile int16_t angleData;             // Holder for SPI data 32 bits
volatile int32_t ADCData;               // Holder for all SPI data
volatile uint16_t averageData;          // Holder for SPI data 16 bits
volatile int8_t dataBuffer[5];          // Holder for all SPI data

// Function Prototypes
void readADC(uint16_t);                 // Decide how many conversions

void main(void)
{
    printf("Inside Main\r\n");
    WDTCTL = (WDTPW | WDTHOLD);   // Stop watchdog timer
//  Really could stand to test this section better
//    FRCTL0 = FRCTLPW | NWAITS_1;  // Configure one FRAM waitstate per datasheet
//    __bis_SR_register(SCG0);    // Disable Frequency Locked Loop (FLL)
//    CSCTL3 |= SELREF__REFOCLK;  // Set REFO as FLL source
//    CSCTL0 = 0;                 // clear DCO and MOD registers
//    CSCTL1 &= ~(DCORSEL_7);     // Clear DCO frequency select bits
//    CSCTL2 = FLLD_0;// + 487;      // DCOCLKDIV = 16 MHz (from sample code)
//    __delay_cycles(3);          // wait 3/(16 MHz)
//    __bis_SR_register(SCG0);    // Enable Frequency Locked Loop (FLL)
//    while(CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1)); // FLL locked
//
//    CSCTL4 = SELMS__DCOCLKDIV | SELA__REFOCLK; // Set default REFO (~32768Hz)
//    // as ACLK source, ACLK = 32768Hz.  Default DCOCLKDIV as MCLKand SMCLK source
//    // page 117 programming guide.  SELA is ACLK, SELMS is MCLK and SMCLK
//
    PM5CTL0 &= ~LOCKLPM5;   // Disable GPIO power-on default high-impedance mode

    // Select module function for SPI (page 60) and UART (page 63)
    // Port direction does not need to be defined for any pin

    P1SEL0 |= (SPI_MISO_PIN | SPI_MOSI_PIN | SPI_CLK_PIN);
    P1DIR |= (SPI_MOSI_PIN | SPI_CLK_PIN); // programming guide 314 says to set
    //direction.  Datasheet says it doesn't matter.  Which is correct?
    P2SEL0 |= (UART_TX_PIN | UART_RX_PIN);
    P2DIR |= (UART_TX_PIN);

//   ╔════════╦════════╦═══════╦════════╦═══════╦═════════╦═════════╦═══════╗
//   ║   15   ║   14   ║  13   ║   12   ║  11   ║   10    ║    9    ║   8   ║
//   ╠════════╬════════╬═══════╬════════╬═══════╬═════════╬═════════╬═══════╣
//   ║ UCCKPH ║ UCCKPL ║ UCMSB ║ UC7BIT ║ UCMST ║ UCMODEx ║ UCMODEx ║ USYNC ║
//   ╚════════╩════════╩═══════╩════════╩═══════╩═════════╩═════════╩═══════╝
//
//   ╔═════════╦═════════╦══════╦══════╦══════╦══════╦════════╦═════════╗
//   ║    7    ║    6    ║  5   ║  4   ║  3   ║  2   ║   1    ║    0    ║
//   ╠═════════╬═════════╬══════╬══════╬══════╬══════╬════════╬═════════╣
//   ║ UCSSELx ║ UCSSELx ║ RES  ║ RES  ║ RES  ║ RES  ║ UCSTEM ║ UCSWRST ║
//   ╚═════════╩═════════╩══════╩══════╩══════╩══════╩════════╩═════════╝

    // Setup SPI in UCB0 Control Word 0
    UCB0CTLW0 |= UCSWRST;  //place in reset state before modifying settings
    // Master-mode, synchronous clock, inactive state high, MSB first.
    UCB0CTLW0 |= (UCMST | UCSYNC | UCCKPL | UCMSB | UCSSEL__SMCLK);
    // Bit clock prescaler
    UCB0BRW = 0x0002;   // Bit rate clock = SMCLK/2 = 8 MHz
    UCB0CTLW0 &= ~UCSWRST;               // Initialize USCI state machine

    // Select module function for UART -- adding Rx interrupt....may use later
//    P2IES |= UART_RX_PIN;     // Port 2 Interrupt Edge Select
//    P2IFG &= ~UART_RX_PIN;    // Clear Interrupt flag
//    P2IE |= UART_RX_PIN;   // Enable interrupt on RX -- no code implemented yet.

    // Set port pins & directions for ADC and switch control

    P3DIR |= (ADC24_RDL | ADC24_CNV);

    P2IN |= ADC24_BUSY;
    P2REN |= ADC24_BUSY; // Enable pulldown resistor
    P2DIR |= (ST_IN1 | ST_IN2);

    //  P2.0, P2.1 control the Self-Test pins.
    //  Only one self test can or should be activated at a time.
    // Look at ADC convert and read results before activating other self-test.
    //
    //  Use the LED as a status indicator.
    // Consider simple flashing, or more complex signals for status
    //
    //  ┌────┬────┬───┬───┬───┐
    //  │P2.0│P2.1│ST1│ST2│LED│
    //  ├────┼────┼───┼───┼───┤
    //  │ L  │ L  │   │   │   │
    //  │ L  │ H  │   │ * │   │
    //  │ H  │ L  │ * │   │   │
    //  │ H  │ H  │   │   │ * │
    //  └────┴────┴───┴───┴───┘

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

//*****************************************************************************
// Toggle ST_IN1 and ST_IN2 flash LED.  Later use self-test feature.
    for (byteCounter = 1; byteCounter <= 10; byteCounter += 1)
    {
        P2OUT ^= (ST_IN1 | ST_IN2);
        __delay_cycles(50000);
    }
    P2OUT &= ~(ST_IN1 | ST_IN2); // Turn LED off
    printf("Reading ADC:\r\n");
    readADC(4);                // Perform n conversions
    printf("Returned from reading ADC:\r\n");
}

void readADC(uint16_t numReads)
{
    // Start conversion, wait for busy flag to turn off,
    // and start the next conversion.
    printf("Inside readADC\r\n");
    printf("Initialize Conversion: ");
    for (counter = 1; counter <= numReads; counter++)
    {
        printf("%d H - ", counter);
        P3OUT ^= ADC24_CNV;     // Toggle convert flag high to start measurement
        __delay_cycles(4);      // Convert needs 20 ns of logic high to activate
        P3OUT ^= ADC24_CNV;     // 1/16MHz * 4 cycles = 250 ns at 16MHz
        printf("L- ");
        if (P2IN & ADC24_BUSY != 0)
        {   // Wait to let conversion finish -- likely will never enter
            printf("ADC Reading: BUSY\r\n");
            __delay_cycles(50);     // 1/16Mhz = 62.5 ns
        }
        printf("\r\n");
    }
// Trial 1 -- two variables & bitshifts
// results:  got data -- but data appeared inaccurate
//    printf("\n\rEntering SPI Readout Routine\n\r");
//
//    for (byteCounter = 1; byteCounter <= 5; byteCounter++)
//    { // started at 1 instead of 0.
//        UCB0IFG ^= ~UCB0IFG;            // Clear Rx flag
//            UCB0TXBUF = 0xFF;           // Dummy Byte to shift out
//            printf("Inside receive Code Section\r\n");     // Wait for RX flag
//            // Store received byte
//            if (byteCounter <= 3)
//            {
//                printf("Storing UCB0RXBUF");
//                angleData |= UCB0RXBUF;
//                angleData <<= 8;
//                printf("RXBUF: %u \r\n", UCB0RXBUF);
//                printf("angle: %u \r\n", angleData);
//            }
//            if (byteCounter >= 4 & byteCounter <= 5)
//            {
//                printf("Storing UCB0RXBUF");
//                averageData |= UCB0RXBUF;
//                averageData <<= 8;
//                printf("RXBUF: %u \r\n", UCB0RXBUF);
//                printf("Average: %u \r\n", averageData);
//            }
// Trial 2 -- one big variable to hold everything.
// results:  got data -- but data appeared inaccurate
//    for (byteCounter = 1; byteCounter <= 3; byteCounter++)
//    { // started at 1 instead of 0.
//        //UCB0IFG ^= ~UCB0IFG;            // Clear Rx flag
//            UCB0TXBUF = 0xCCCC;           // Dummy 16-bits to shift out
//            printf("Inside receive Code Section\r\n");     // Wait for RX flag
//            // Store received byte
//            if (byteCounter <= 2)
//            {
//                printf("Storing UCB0RXBUF\r\n");
//                ADCData |= UCB0RXBUF;
//                ADCData <<= 16;
//                printf("RXBUF: %u \r\n", UCB0RXBUF);
//                printf("ADCData: %llu \r\n", ADCData);
//            }else
//            {
//                ADCData |= UCB0RXBUF;
//                printf("RXBUF: %u \r\n", UCB0RXBUF);
//                printf("ADCData: %llu \r\n", ADCData);
//            }
//
// Trial 3 -- use an array to hold bytes.
// Obviously the easier way to go -- but again, data appears inaccurate.
    for (byteCounter=1; byteCounter <=5; byteCounter++){
        dataBuffer[byteCounter]|=UCB0RXBUF;
    }
    printf("ADCData: %p\n", &dataBuffer);
//Read ADC -- variables are volatile right now, so no need to return data
return;
}

  • > So my first question is this: what is the "best practice" way to grab those first 24 bits? It appears that the MSP430 RXBuffer has 16-bits in it

    Put it into an array, since you'll be serializing it out over the UART anyway. UCB0RXBUF is 16 bits, but only 8 of them are used.

    >    for (byteCounter=1; byteCounter <=5; byteCounter++){

    >        dataBuffer[byteCounter]|=UCB0RXBUF;

    UCB0RXBUF isn't filled just by reading it, you need to send something and that is exchanged for the next byte. Here's a function I seem to keep writing over and over (since that's quicker than finding an old one):

    uint8_t spix(uint8_t c)
    {
      while (!(UCB0IFG & UCTXIFG)) /*EMPTY*/;
      UCB0TXBUF = c;
      while (!(UCB0IFG & UCRXIFG)) /*EMPTY*/;
      c = UCB0RXBUF;
      return(c);
    }

    That turns your second line into

    > dataBuffer[byteCounter] = spix(0xFF);   // 0xFF is a dummy byte just to push out a result

    [Edit: Sorry, I can't seem to fix the formatting.]

  • Unsolicited:
    > P2IN |= ADC24_BUSY;
    Set P2OUT, not P2IN to set the "pull" direction. (Opposite to the AVR.)
    > P2OUT |= ADC24_BUSY; // Pull Up, not Down

    > P2DIR |= (ST_IN1 | ST_IN2);
    P2OUT is undefined after reset, so you might be outputing any-old-thing here. Preface:
    > P2OUT &= ~(ST_IN1|ST_IN2); // IN1/IN2 initially low

    > if (P2IN & ADC24_BUSY != 0)
    This is testing P2.0, which I think wasn't the intent. Try:
    > if ((P2IN & ADC24_BUSY) != 0)
  • I think there are some typos in your clock-setting transcription. Being lazy, I just copy/paste from one of the example programs. This is lifted from example CS_03.c:

        FRCTL0 = FRCTLPW | NWAITS_1;
        __bis_SR_register(SCG0);                           // disable FLL
        CSCTL3 |= SELREF__REFOCLK;                         // Set REFO as FLL reference source
        CSCTL0 = 0;                                        // clear DCO and MOD registers
        CSCTL1 &= ~(DCORSEL_7);                            // Clear DCO frequency select bits first
        CSCTL1 |= DCORSEL_5;                               // Set DCO = 16MHz
        CSCTL2 = FLLD_0 + 487;                             // DCOCLKDIV = 16MHz
        __delay_cycles(3);  
        __bic_SR_register(SCG0);                           // enable FLL
        while(CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1));         // FLL locked
        
        CSCTL4 = SELMS__DCOCLKDIV | SELA__REFOCLK;         // set default REFO(~32768Hz) as ACLK source, ACLK = 32768Hz
                                                           // default DCOCLKDIV as MCLK and SMCLK source

  • Wow Bruce -- that was fast. I appreciate it. I'm updating CCS at the moment -- but I'll incorporate the changes and let you know how it all went tomorrow sometime.
  • Bruce -- good news / bad news sort of thing.  The good news is that I was able to include all of your suggestions successfully -- the code compiles and runs.  The bad news is that my data is consistently "0".  It's getting a bit late tonight, so I'm going to wrap things up.  But tomorrow I'll double check the code, and then pull out the old oscilloscope.  Here's where we stand as of tonight.  Tomorrow I'll pull out those sloppy volatiles and refine some of the functions.  Maybe I'll get lucky and read some data.

    Thanks again!  I do appreciate it.

    // ************************** Connection Diagram ******************************
    //   CP2102N           ┌──────┬──────┐            ┌────────────┐
    // ┌────────────┐      │      │ P1.0 │→ UCB0STE  →│ EN (NC)    │
    // │    USB     │→ RX →│ P2.5 │ P1.1 │→ UCB0CLK  →│ CLK        │
    // │    TO      │→ TX ←│ P2.6 │ P1.2 │→ UCBSIMO  →│ SIMO (NC)  │
    // │   UART     │      │      │ P1.3 │← UCBSOMI  ←│ SOMI       │
    // └────────────┘      ├──────┼──────┤            │            │
    //             ST_IN1 ←│ P2.0 │ P2.4 │← BUSY     ←│ BUSY       │
    //             ST_IN2 →│ P2.1 │ P3.0 │→ RDL      →│ RDL        │
    //                     │      │ P3.1 │→ CNV      →│ CNV        │
    //                     └──────┴──────┘            └────────────┘
    //                       MSP430FR2633                 ADC
    //
    // ****************************************************************************
    // Mark Hughes for AllAboutCircuits.com
    // This is a one-directional SPI to UART to USB bridge interface
    // CNV is toggled one/multiple times (up to 65535) and data is averaged inside
    // the ADC until SPI_CLK toggles data out and into MSP430.  Data is then sent
    // via UART to CP2102N, and then from the CP2102N to the computer.
    // UART communication, self-test and auto-calibration not yet implemented.
    //*****************************************************************************
    #include <msp430.h>
    #include <driverlib.h>
    #include <stdio.h>
    
    #define SPI_EN_PIN          BIT0        // P1.0 UCB -- not used at all
    #define SPI_CLK_PIN         BIT1        // P1.1 UCB
    #define SPI_MOSI_PIN        BIT2        // P1.2 UCB -- not used yet
    #define SPI_MISO_PIN        BIT3        // P1.3 UCB
    
    #define UART_RX_PIN         BIT5        // P2.5 eUSCI_A
    #define UART_TX_PIN         BIT6        // P2.6 eUSCI_A
    
    #define ADC24_RDL           BIT0        // P3.0 set low always
    #define ADC24_CNV           BIT1        // P3.1 toggle to start conversion
    #define ADC24_BUSY          BIT4        // P2.4 goes low after conversion
    
    #define ST_IN1              BIT0        // 3PST input 0
    #define ST_IN2              BIT1        // 3PST input 1
    
    // Variable Declarations.  Volatile during first phase of programming.
    // Will eliminate with proper function calls & data returns later on.
    
    volatile uint8_t byteCounter;           // need 24 bits + 16 bits (5 bytes)
    volatile uint8_t counter;               // for various for loops
    volatile uint16_t numReads;             // number of times to sample
    volatile int16_t angleData;             // Holder for SPI data 32 bits
    volatile int32_t ADCData;               // Holder for all SPI data
    volatile uint16_t averageData;          // Holder for SPI data 16 bits
    volatile uint8_t dataBuffer[5];          // Holder for all SPI data
    
    // Function Prototypes
    void readADC(uint16_t);                 // Decides how many conversions
    uint8_t spix(uint8_t);
    
    void main(void)
    {
        printf("Inside Main\r\n");
        WDTCTL = (WDTPW | WDTHOLD);         // Stop watchdog timer
    
        FRCTL0 = FRCTLPW | NWAITS_1;
        __bis_SR_register(SCG0);            // disable FLL
        CSCTL3 |= SELREF__REFOCLK;          // Set REFO as FLL reference source
        CSCTL0 = 0;                         // clear DCO and MOD registers
        CSCTL1 &= ~(DCORSEL_7);             // Clear DCO frequency select bits first
        CSCTL1 |= DCORSEL_5;                // Set DCO = 16MHz
        CSCTL2 = FLLD_0 + 487;              // DCOCLKDIV = 16MHz
        __delay_cycles(3);
        __bic_SR_register(SCG0);            // enable FLL
        while (CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1));// FLL locked
        // set default REFO(~32768Hz) as ACLK source, ACLK = 32768Hz
        // default DCOCLKDIV as MCLK and SMCLK source
        CSCTL4 = SELMS__DCOCLKDIV | SELA__REFOCLK;
    
        PM5CTL0 &= ~LOCKLPM5;   // Disable GPIO power-on default high-impedance mode
    
        // Select module function for SPI (page 60) and UART (page 63)
        // Port direction does not need to be defined for any pin
    
        P1SEL0 |= (SPI_MISO_PIN | SPI_MOSI_PIN | SPI_CLK_PIN);
        P1DIR |= (SPI_MOSI_PIN | SPI_CLK_PIN); // programming guide 314 says to set
        //direction.  Datasheet says it doesn't matter.  Which is correct?
        P2SEL0 |= (UART_TX_PIN | UART_RX_PIN);
        P2DIR |= (UART_TX_PIN);
    
    //   ╔════════╦════════╦═══════╦════════╦═══════╦═════════╦═════════╦═══════╗
    //   ║   15   ║   14   ║  13   ║   12   ║  11   ║   10    ║    9    ║   8   ║
    //   ╠════════╬════════╬═══════╬════════╬═══════╬═════════╬═════════╬═══════╣
    //   ║ UCCKPH ║ UCCKPL ║ UCMSB ║ UC7BIT ║ UCMST ║ UCMODEx ║ UCMODEx ║ USYNC ║
    //   ╚════════╩════════╩═══════╩════════╩═══════╩═════════╩═════════╩═══════╝
    //
    //   ╔═════════╦═════════╦══════╦══════╦══════╦══════╦════════╦═════════╗
    //   ║    7    ║    6    ║  5   ║  4   ║  3   ║  2   ║   1    ║    0    ║
    //   ╠═════════╬═════════╬══════╬══════╬══════╬══════╬════════╬═════════╣
    //   ║ UCSSELx ║ UCSSELx ║ RES  ║ RES  ║ RES  ║ RES  ║ UCSTEM ║ UCSWRST ║
    //   ╚═════════╩═════════╩══════╩══════╩══════╩══════╩════════╩═════════╝
    
        // Setup SPI in UCB0 Control Word 0
        UCB0CTLW0 |= UCSWRST;  //place in reset state before modifying settings
        // Master-mode, synchronous clock, inactive state high, MSB first.
        UCB0CTLW0 |= (UCMST | UCSYNC | UCCKPL | UCMSB | UCSSEL__SMCLK);
        // Bit clock prescaler
        UCB0BRW = 0x0002;   // Bit rate clock = SMCLK/2 = 8 MHz
        UCB0CTLW0 &= ~UCSWRST;               // Initialize USCI state machine
    
        // Select module function for UART -- adding Rx interrupt....may use later
    //    P2IES |= UART_RX_PIN;     // Port 2 Interrupt Edge Select
    //    P2IFG &= ~UART_RX_PIN;    // Clear Interrupt flag
    //    P2IE |= UART_RX_PIN;   // Enable interrupt on RX -- no code implemented yet.
    
        // Set port pins & directions for ADC and switch control
    
        P3DIR |= (ADC24_RDL | ADC24_CNV);
        P3DIR |= ~(ADC24_RDL | ADC24_CNV);  // RDL & CNV low
    
        P2IN |= ADC24_BUSY;
    
        P2DIR |= (ST_IN1 | ST_IN2);
        P2OUT &= ~(ST_IN1|ST_IN2); // IN1/IN2 initially low
    
        //  P2.0, P2.1 control the Self-Test pins.
        //  Only one self test can or should be activated at a time.
        // Look at ADC convert and read results before activating other self-test.
        //
        //  Use the LED as a status indicator.
        // Consider simple flashing, or more complex signals for status
        //
        //  ┌────┬────┬───┬───┬───┐
        //  │P2.0│P2.1│ST1│ST2│LED│
        //  ├────┼────┼───┼───┼───┤
        //  │ L  │ L  │   │   │   │
        //  │ L  │ H  │   │ * │   │
        //  │ H  │ L  │ * │   │   │
        //  │ H  │ H  │   │   │ * │
        //  └────┴────┴───┴───┴───┘
    
    //*****************************************************************************
    
    //*****************************************************************************
    // Toggle ST_IN1 and ST_IN2 flash LED.  Later use self-test feature.
        for (byteCounter = 1; byteCounter <= 10; byteCounter += 1)
        {
            P2OUT ^= (ST_IN1 | ST_IN2);
            __delay_cycles(50000);
        }
        P2OUT &= ~(ST_IN1 | ST_IN2); // Turn LED off
        printf("Reading ADC:\r\n");
        readADC(4);                // Perform n conversions
        printf("Returned from reading ADC:\r\n");
    }
    
    void readADC(uint16_t numReads)
    {
        // Start conversion, wait for busy flag to turn off,
        // and start the next conversion.
        printf("Inside readADC\r\n");
        printf("Initialize Conversion: ");
        P3OUT |= ~ADC24_CNV;
        for (counter = 1; counter <= numReads; counter++)
        {
            P3OUT ^= ADC24_CNV;     // Toggle convert flag high to start measurement
            __delay_cycles(4);      // Convert needs 20 ns of logic high to activate
            P3OUT ^= ADC24_CNV;     // 1/16MHz * 4 cycles = 250 ns at 16MHz
            if ((P2IN & ADC24_BUSY) != 0)
            {   // Wait to let conversion finish -- likely will never enter
                __delay_cycles(50);     // 1/16Mhz = 62.5 ns
            }
        }
    
        for (byteCounter = 1; byteCounter <= 5; byteCounter++)
        {
            dataBuffer[byteCounter] = spix(0xFF);
            printf("%i\t",dataBuffer[byteCounter]);
        }
        printf("\n\r");
        printf("dataBuffer: %i\n\r",*dataBuffer);
    //Read ADC -- tempData is volatile right now, so no need to return value to
        //main.  Need to fix that.
        return;
    }
    
    uint8_t spix(uint8_t c)
    {
      while (!(UCB0IFG & UCTXIFG)) /*EMPTY*/;
      UCB0TXBUF = c;
      while (!(UCB0IFG & UCRXIFG)) /*EMPTY*/;
      c = UCB0RXBUF;
      return(c);
    }

  • > P3DIR |= ~(ADC24_RDL | ADC24_CNV); // RDL & CNV low
    > P3OUT |= ~ADC24_CNV;
    I think you want "&=" not "|=" here.

    > for (byteCounter = 1; byteCounter <= 5; byteCounter++)
    dataBuffer only has 5 elements, so this will overrun it. Try:
    > for (byteCounter = 0; byteCounter < 5; byteCounter++)

    > P2IN |= ADC24_BUSY;
    This doesn't do anything useful, but I can't figure out what you intended. Anyway I think you want something else (or maybe nothing) here.

    Scanning the ADC data sheet it's not at all clear to me whether it wants clock polarity (CPOL/UCCKPL) =1 or =0. You may want to try both.
  • Bruce,

         Thanks for the updated error-checking.  Well, I have more good news, bad news for you.  I plugged in your changes to the code, compiled and ran it, and the very first time it produced what appeared to be valid data -- the angle and number of conversions.  Then out of excitement, I recompiled and ran it again -- and got all zeros.  That leads me to believe i might have a pin in an undefined state, or a power supply issue.  So I'm going to spend some time this morning going back through the code, clean up the functions, and double check the pin state assignments.

    Thanks again for your continued support!

    Mark

  • Hey again Bruce,
    I haven't cleaned up the functions yet -- but I do have working code that can read the ADC. Thanks for the help! I'll try to start the UART implementation tomorrow.
    Mark
  • Part Number: MSP430FR2633

    Tool/software: Code Composer Studio

    Alright -- so I started tinkering around with the UART implementation this afternoon and I realize I need to read the manual a bit more carefully.  Most of the code examples are for full UART implementations with interrupts -- something I don't require.  I just need to capture the data from the SPI (which I can do reliably now thanks to Bruce), and push it out over UART.

    So I'm going to go re-read and try to better understand the timing settings.  I'll be using the onboard oscillator.  Is there anything I need to know to push data out over the UART interface?  

    //
    // ************************** Connection Diagram ******************************
    //   CP2102N           ┌──────┬──────┐            ┌────────────┐
    // ┌────────────┐      │      │ P1.0 │→ UCB0STE  →│ EN (NC)    │
    // │    USB     │→ RX →│ P2.5 │ P1.1 │→ UCB0CLK  →│ CLK        │
    // │    TO      │→ TX ←│ P2.6 │ P1.2 │→ UCBSIMO  →│ SIMO (NC)  │
    // │   UART     │      │      │ P1.3 │← UCBSOMI  ←│ SOMI       │
    // └────────────┘      ├──────┼──────┤            │            │
    //             ST_IN1 ←│ P2.0 │ P2.4 │← BUSY     ←│ BUSY       │
    //             ST_IN2 →│ P2.1 │ P3.0 │→ RDL      →│ RDL        │
    //                     │      │ P3.1 │→ CNV      →│ CNV        │
    //                     └──────┴──────┘            └────────────┘
    //                       MSP430FR2633                 ADC
    //
    // ****************************************************************************
    // Mark Hughes for AllAboutCircuits.com
    // This is a one-directional SPI to UART to USB bridge interface
    // CNV is toggled one/multiple times (up to 65535) and data is averaged inside
    // the ADC until SPI_CLK toggles data out and into MSP430.  Data is then sent
    // via UART to CP2102N, and then from the CP2102N to the computer.
    // UART communication, self-test and auto-calibration not yet implemented.
    //*****************************************************************************
    #include <msp430.h>
    #include <driverlib.h>
    #include <stdio.h>
    
    #define UART_RX_PIN     BIT5            // P2.5 eUSCI_A
    #define UART_TX_PIN     BIT6            // P2.6 eUSCI_A
    
    #define SPI_EN_PIN      BIT0            // P1.0 eUSCI_B -- not used
    #define SPI_CLK_PIN     BIT1            // P1.1 eUSCI_B
    #define SPI_MOSI_PIN    BIT2            // P1.2 eUSCI_B -- not used yet
    #define SPI_MISO_PIN    BIT3            // P1.3 eUSCI_B
    
    #define ADC24_RDL       BIT0            // P3.0 set low always
    #define ADC24_CNV       BIT1            // P3.1 L->H->L (20 ns) to convert
    #define ADC24_BUSY      BIT4            // P2.4 goes low after conversion
    
    #define ST_IN1          BIT0            // 3PST switch input 0
    #define ST_IN2          BIT1            // 3PST switch input 1
    
    // Variable Declarations.  Volatile during first phase of programming.
    // Will eliminate with proper function calls & data returns later on.
    
    volatile uint8_t byteCounter;    // need 24 bits + 16 bits (5 bytes)
    volatile uint8_t counter;        // for various for loops
    volatile uint16_t numReads;       // number of times to sample
    volatile int16_t angleData;      // Holder for SPI data 32 bits
    volatile int32_t ADCData;        // Holder for all SPI data
    volatile uint16_t averageData;    // Holder for SPI data 16 bits
    volatile uint8_t dataBuffer[5];  // Holder for all SPI data
    
    // Function Prototypes
    void readADC(uint16_t);                 // Decides how many conversions
    uint8_t spix(uint8_t);
    void uartx(uint8_t);
    
    void main(void)
    {
        //********************* Begin Configuration ***********************************
    
        WDTCTL = (WDTPW | WDTHOLD);         // Stop watchdog timer
        FRCTL0 = FRCTLPW | NWAITS_1;
        __bis_SR_register(SCG0);            // disable FLL
        CSCTL3 |= SELREF__REFOCLK;          // Set REFO as FLL reference source
        CSCTL0 = 0;                         // clear DCO and MOD registers
        CSCTL1 &= ~(DCORSEL_7);             // Clear DCO frequency select bits first
        CSCTL1 |= DCORSEL_5;                // Set DCO = 16MHz
        CSCTL2 = FLLD_0 + 487;              // DCOCLKDIV = 16MHz
        __delay_cycles(3);
        __bic_SR_register(SCG0);            // enable FLL
        while (CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1))
            ;            // FLL locked
        // default DCOCLKDIV as MCLK and SMCLK source
        CSCTL4 = SELMS__DCOCLKDIV | SELA__REFOCLK;
    
        // Disable GPIO power-on default high-impedance mode
        PM5CTL0 &= ~LOCKLPM5;
        // PxDIR: 0(In) 1(Out)
        // PxSEL: Function Select Register
        // PxOUT: 0(L) 1(H): Output Register
        // PxREN: 0(L) 1(En):
    
        // Select module function for SPI (page 60)
        P1SEL0 |= (SPI_MISO_PIN | SPI_MOSI_PIN | SPI_CLK_PIN);
        // Set MOSI and CLK as outputs.
        P1DIR |= (SPI_MOSI_PIN | SPI_CLK_PIN);
        P2OUT &= ~(SPI_MOSI_PIN | SPI_CLK_PIN);
    
        // Select module function for UART (page 63)
        P2SEL0 |= (UART_TX_PIN | UART_RX_PIN);
        // Set port pins for UART and SP3T Switch
        P2DIR |= (UART_TX_PIN | ST_IN1 | ST_IN2);
        // IN1/IN2 initially low to open SPST switch.
        P2OUT &= ~(ST_IN1 | ST_IN2);
    
        // Set port pins for ADC
        P3SEL0 &= ~(ADC24_RDL | ADC24_CNV);
        // Set direction for RDL and CNV
        P3DIR |= (ADC24_RDL | ADC24_CNV);
        // Set port output low for RDL and CNV
        P3OUT &= ~(ADC24_RDL | ADC24_CNV);
    
        // Setup SPI in UCB0 Control Word 0
        //place in reset state before modifying settings
    
        UCB0CTLW0 |= UCSWRST;
        // Master-mode, synchronous clock, inactive state high, MSB first.
        UCB0CTLW0 |= (UCMST | UCMSB | UCCKPH | UCSYNC | UCMSB | UCSSEL__SMCLK);
        // Bit clock prescaler
        UCB0BRW = 0x0002;   // Bit rate clock = SMCLK/2 = 8 MHz
        // Release reset and initialize state machine
        UCB0CTLW0 &= ~UCSWRST;
    
        // Select module function for UART
        // Put UCA state machine in reset
        UCA0CTLW0 |= UCSWRST;
        // Select SMCLK
        UCA0CTLW0 |= UCSSEL__SMCLK;
        // Baud rate setting for 9600 BAUD from slau445h pg590
        // How to implement UCBRx and UCBRF?
    
        // processors.wiki.ti.com/.../USCI_UART_Baud_Rate_Gen_Mode_Selection
        UCA0BR0 = 104;
        UCA0BR1 = 0xD6;
        UCA0MCTLW = 0x53 | UCOS16 | UCBRF_2;
    
        UCA0CTLW0 &= ~UCSWRST;
    
        //  P2.0, P2.1 control the Self-Test pins.
        //  Only one self test can or should be activated at a time.
        // Look at ADC convert and read results before activating other self-test.
        //
        //  Use the LED as a status indicator.
        // Consider simple flashing, or more complex signals for status
        //
        //  ┌────┬────┬───┬───┬───┐
        //  │P2.0│P2.1│ST1│ST2│LED│
        //  ├────┼────┼───┼───┼───┤
        //  │ L  │ L  │   │   │   │
        //  │ L  │ H  │   │ * │   │
        //  │ H  │ L  │ * │   │   │
        //  │ H  │ H  │   │   │ * │
        //  └────┴────┴───┴───┴───┘
    
    //*****************************************************************************
    
    //*****************************************************************************
    
    // Toggle ST_IN1 and ST_IN2 flash LED.  Later use self-test feature.
        for (byteCounter = 1; byteCounter <= 4; byteCounter += 1)
        {
            P2OUT ^= (ST_IN1 | ST_IN2);
            __delay_cycles(5000000);
        }
    
        readADC(4);                // Perform n conversions
    }
    
    void readADC(uint16_t numReads)
    {
        // Start conversion, wait for busy flag to turn off,
        // and start the next conversion.
        for (counter = 1; counter <= numReads; counter++)
        {
            P3OUT ^= ADC24_CNV;     // Toggle convert flag high to start measurement
            __delay_cycles(4);      // Convert needs 20 ns of logic high to activate
            P3OUT ^= ADC24_CNV;     // 1/16MHz * 4 cycles = 250 ns at 16MHz
            if ((P2IN & ADC24_BUSY) != 0)
            {   // Wait to let conversion finish -- likely will never enter
                __delay_cycles(50);     // 1/16Mhz = 62.5 ns
            }
        }
        //P3OUT &= ~ADC24_CNV;        // Ensure CNV is low after conversion
        for (byteCounter = 0; byteCounter < 5; byteCounter++)
        {
            dataBuffer[byteCounter] = spix(0xFF);
            printf("%i\t", dataBuffer[byteCounter]);
        }
        for (byteCounter = 0; byteCounter < 5; byteCounter++)
        {
            uartx(0xCC);
        }
    //Read ADC -- tempData is volatile right now, so no need to return value to
        //main.  Need to fix that.
        return;
    }
    
    uint8_t spix(uint8_t c)
    {
        while (!(UCB0IFG & UCTXIFG))
            /*EMPTY*/;
        UCB0TXBUF = c;
        while (!(UCB0IFG & UCRXIFG))
            /*EMPTY*/;
        c = UCB0RXBUF;
        return (c);
    }
    
    void uartx(uint8_t c)
    {
        while (!(UCA0IFG & UCTXIFG))
            /*EMPTY*/;
        UCA0TXBUF = c;
        return;
    }
  • Okay -- quick update -- I don't know what the heck I was thinking earlier -- those UART pins are on A1, not A0. So the code above is wrong. I still need to figure out the appropriate clock settings.
  • > UCA0BR1 = 0xD6;
    BR1 is just the high order byte (BR0 is the low byte) of BRW, so this makes a much-too-big divisor. Try:
    > UCA0BR1 = 0; // aka BRW=104

    > uartx(0xCC);
    I'm supposing you're going to replace that 0xCC with dataBuffer[byteCounter] or some such. I was just going to point out that what you're sending isn't ASCII, so make sure the other end of the wire knows what to do with it.
  • I'm happy to do that. I was trying to make sense of the online calculator as well as the datasheet.
    And right now, the other end of the line is just my trusty oscilloscope. But I'm sure your comment saved me some grief somewhere down the road.

    What should the rest of the configuration settings be? Just so you know, I'm not using an external crystal.
    //Current settings
    UCA1CTLW0 |= UCSWRST;
    UCA1CTLW0 |= UCSSEL__SMCLK | UCOS16;
    UCA1BR0 = 0xD6;
    UCA1BR1 = 0;
    UCA1MCTLW = UCBRS0+UCBRS2;
    UCA1CTLW0 &= ~UCSWRST;

    Thanks
  • //   Per Recommended Settings Table  for SMCLK=16MHz, BitRate=9600bps
    //   [User Guide (SLAU445H) Table 22-5]
    UCA1CTLW0 = UCMODE_0 | UCSSEL__SMCLK | UCSWRST; // UART, SMCLK, Reset
    UCA1BRW = 104;                 // 16M/(UCOS)16/9600
    UCA1MCTLW = (0xD6 << 8) | UCOS16 | UCBRF_2;     // UCBRS=0xD6, 16x oversample, UCBRF=2
    UCA1CTLW0 &= ~UCSWRST;         // out of Reset
    

  • Again -- I owe you thanks. That did it. I definitely need to develop a deeper understanding of the MCTLW bitmap.
  • Just in case anybody needs it later.  Here's the final code.  This takes data in from SPI interface and sends it back out on UART interface

    MSP430FR2633 with LTC2380IDE and SCA103T-D04

    More details will be at AllAboutCircuits.com once the article series is published ( ~December 2018).

    
    //         _ _          _                 _    _____ _                _ _
    //   /\   | | |   /\   | |               | |  / ____(_)              (_) |
    //  /  \  | | |  /  \  | |__   ___  _   _| |_| |     _ _ __ ___ _   _ _| |_ ___
    // / /\ \ | | | / /\ \ | '_ \ / _ \| | | | __| |    | | '__/ __| | | | | __/ __|
    /// ____ \| | |/ ____ \| |_) | (_) | |_| | |_| |____| | | | (__| |_| | | |_\__ \
    //_/    \_\_|_/_/    \_\_.__/ \___/ \__,_|\__|\_____|_|_|  \___|\__,_|_|\__|___/
    //                      _____         _         __    _____         _
    //                     |     |___ ___| |_    __|  |  |  |  |_ _ ___| |_ ___ ___
    //                     | | | | .'|  _| '_|  |  |  |  |     | | | . |   | -_|_ -|
    //                     |_|_|_|__,|_| |_,_|  |_____|  |__|__|___|_  |_|_|___|___|
    //                     w/ support by Bruce McKenney TI FAE     |___|  2018/11/09
    //            _ __
    //           ' )  )   _/_
    //  ______ . ./--'__. / __.
    // / / / <(_//  \(_/|<_(_/|_
    //  __ __ _  ___ __   __ __ _  __  _  _ ____ ____ ____ ____
    // (  |  ( \/ __|  ) (  |  ( \/  \( \/ |  __|_  _|  __|  _ \
    //  )(/    ( (__/ (_/\)(/    (  O ) \/ \) _)  )(  ) _) )   /
    // (__)_)__)\___)____(__)_)__)\__/\_)(_(____)(__)(____|__\_)
    //
    // ************************** Connection Diagram ******************************
    //   CP2102N           ┌──────┬──────┐            ┌────────────┐
    // ┌────────────┐      │      │ P1.0 │→ UCB0STE  →│ EN (NC)    │
    // │    USB     │→ RX →│ P2.5 │ P1.1 │→ UCB0CLK  →│ CLK        │
    // │    TO      │→ TX ←│ P2.6 │ P1.2 │→ UCBSIMO  →│ SIMO (NC)  │
    // │   UART     │      │      │ P1.3 │← UCBSOMI  ←│ SOMI       │
    // └────────────┘      ├──────┼──────┤            │            │
    //             ST_IN1 ←│ P2.0 │ P2.4 │← BUSY     ←│ BUSY       │
    //             ST_IN2 →│ P2.1 │ P3.0 │→ RDL      →│ RDL        │
    //                     │      │ P3.1 │→ CNV      →│ CNV        │
    //                     └──────┴──────┘            └────────────┘
    //                       MSP430FR2633                 ADC
    //
    // ****************************************************************************
    // Mark Hughes for AllAboutCircuits.com
    // This is a one-directional SPI to UART to USB bridge interface
    // CNV is toggled one/multiple times (up to 65535) and data is averaged inside
    // the ADC until SPI_CLK toggles data out and into MSP430.  Data is then sent
    // via UART to CP2102N, and then from the CP2102N to the computer.
    // The data is sent over UART in Binary format MSB first using ASCII characters.
    // self-test and auto-calibration not yet implemented.
    //*****************************************************************************
    #include <msp430.h>
    #include <driverlib.h>
    #include <stdio.h>
    
    #define UART_RX_PIN     BIT5            // P2.5 eUSCI_A1
    #define UART_TX_PIN     BIT6            // P2.6 eUSCI_A1
    
    #define SPI_EN_PIN      BIT0            // P1.0 eUSCI_B0 -- not used
    #define SPI_CLK_PIN     BIT1            // P1.1 eUSCI_B0
    #define SPI_MOSI_PIN    BIT2            // P1.2 eUSCI_B0 -- not used yet
    #define SPI_MISO_PIN    BIT3            // P1.3 eUSCI_B0
    
    #define ADC24_RDL       BIT0            // P3.0 set low always
    #define ADC24_CNV       BIT1            // P3.1 L->H->L (20 ns) to convert
    #define ADC24_BUSY      BIT4            // P2.4 goes low after conversion
    
    #define ST_IN1          BIT0            // 3PST switch input 0
    #define ST_IN2          BIT1            // 3PST switch input 1
    
    // Variable Declarations.  Volatile during first phase of programming.
    // Will eliminate with proper function calls & data returns later on.
    uint8_t bitCounter;                     // 0-8 counter for bits.
    uint8_t byteCounter;                    // need 24 bits + 16 bits (5 bytes)
    uint16_t numTrials;                     //
    uint16_t counter;                       // Temporary counter
    uint16_t numReads;                      // Number of times to sample
    volatile uint8_t dataBuffer[5];         // Holder for all SPI data
    
    // Function Prototypes
    void readADC(uint16_t);                 // Decides number of conversions
    uint8_t spix(uint8_t);                  // SPI interface
    void uartx(uint8_t);                    // UART interface
    
    void main(void)
    {
        //********************* Begin Configuration ********************************
    
        WDTCTL = (WDTPW | WDTHOLD);         // Stop watchdog timer
        FRCTL0 = FRCTLPW | NWAITS_1;        // FRAM configuration for > 8 MHz
        __bis_SR_register(SCG0);            // Disable Frequency Locked Loop (FLL)
        CSCTL3 |= SELREF__REFOCLK;          // Set REFO as FLL reference source
        CSCTL0 = 0;                         // clear DCO and MOD registers
        CSCTL1 &= ~(DCORSEL_7);             // Clear DCO frequency select bits first
        CSCTL1 |= DCORSEL_5;                // Set DCO = 16MHz
        CSCTL2 = FLLD_0 + 487;              // DCOCLKDIV = 16MHz
        __delay_cycles(3);                  // Wait to allow stabilization of clock
        __bic_SR_register(SCG0);            // Reenable FLL
        while (CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1))
            /*EMPTY*/;            // FLL locked
        // default DCOCLKDIV as MCLK and SMCLK source
        CSCTL4 = SELMS__DCOCLKDIV | SELA__REFOCLK;
        // Disable GPIO power-on default high-impedance mode
        PM5CTL0 &= ~LOCKLPM5;   // Disable GPIO power-on default high-impedance mode
        // PxDIR: 0(In) 1(Out)
        // PxSEL: Function Select Register (see datasheet)
        // PxOUT: 0(L) 1(H): Output Register
        // PxREN: 0(L) 1(En):
    
        // Select SPI module function for SPI (page 60)
        P1SEL0 |= (SPI_MISO_PIN | SPI_MOSI_PIN | SPI_CLK_PIN);
        // Set MOSI and CLK as outputs.
        P1DIR |= (SPI_MOSI_PIN | SPI_CLK_PIN);
    
        // Select module function for UART (page 63)
        P2SEL0 |= (UART_TX_PIN | UART_RX_PIN);
        // Set port pins for UART and SP3T Switch
        P2DIR |= (UART_TX_PIN | ST_IN1 | ST_IN2);
        // IN1/IN2 initially low to open SPST switch.
        P2OUT &= ~(ST_IN1 | ST_IN2);
    
        // Set port pins for ADC
        P3SEL0 &= ~(ADC24_RDL | ADC24_CNV);
        // Set direction for RDL and CNV
        P3DIR |= (ADC24_RDL | ADC24_CNV);
        // Set port output low for RDL and CNV
        P3OUT &= ~(ADC24_RDL | ADC24_CNV);
    
        // Setup SPI in UCB0 Control Word 0
        // Place UCB in reset state before modifying settings
    
        UCB0CTLW0 |= UCSWRST;
        // Master-mode, synchronous clock, inactive state high, MSB first.
        UCB0CTLW0 |= (UCMST | UCMSB | UCCKPH | UCSYNC | UCMSB | UCSSEL__SMCLK);
        // Bit clock prescaler
        UCB0BRW = 0x0002;   // Bit rate clock = SMCLK/2 = 8 MHz
        // Release reset and initialize state machine
        UCB0CTLW0 &= ~UCSWRST;
    
        // Put UCA state machine in reset, select mode and clock.
        UCA1CTLW0 = UCMODE_0 | UCSSEL__SMCLK | UCSWRST; // UART, SMCLK, Reset
        // 16M/(UCOS)16/9600
        UCA1BRW = 104;
        // UCBRS=0xD6, 16x oversample, UCBRF=2
        UCA1MCTLW = (0xD6 << 8) | UCOS16 | UCBRF_2;
        // Release reset and initialize state machine
        UCA1CTLW0 &= ~UCSWRST;
    
    //********************** End Configuration *************************************
    
    //******************************************************************************
    // Toggle ST_IN1 and ST_IN2 flash LED with each read.
        for (numTrials = 1; numTrials <= 1000; numTrials++)
        {
            P2OUT ^= (ST_IN1 | ST_IN2);     // Toggle LEDs On
            readADC(0b0111111111111111);    // Perform n conversions
            P2OUT ^= (ST_IN1 | ST_IN2);     // Toggle LEDs Off
            // Leave off long enough for user to see toggle.
            __delay_cycles(5000000);
        }
        return;                             // End main.
    }
    
    void readADC(uint16_t numReads)
    {
        // Start conversion, wait for busy flag to turn off,
        // and start the next conversion.
        for (counter = 1; counter <= numReads; counter++)
        {
            P3OUT ^= ADC24_CNV;     // Toggle convert flag high to start measurement
            __delay_cycles(2);      // Convert needs 20 ns of logic high to activate
            P3OUT ^= ADC24_CNV;     // 1/16MHz * 4 cycles = 250 ns at 16MHz
            // Wait to let conversion finish - might never enter function
            if ((P2IN & ADC24_BUSY) != 0)
            {
                __delay_cycles(50);     // 1/16Mhz = 62.5 ns
            }
        }
    
        P3OUT &= ~ADC24_CNV;        // Ensure CNV is low after exit
    
        // Shift out dummy bytes to allow ADC data to shift into dataBuffer[]
        for (byteCounter = 0; byteCounter < 5; byteCounter++)
        {   // Shift out useless data to allow shift in of ADC data.
            dataBuffer[byteCounter] = spix(0xFF);
        }
        // Find binary equivalent of each dataBuffer byte.  Send it out over UART.
        // Each byte is shifted to the right n-bits and the LSB is read as 1 or 0
        // (7 - bitCounter) is used to change Endianess.
        for (byteCounter = 0; byteCounter < 5; byteCounter++) //0,1,2,3,4
        {
            for (bitCounter = 0; bitCounter < 8; bitCounter++)
            {
                if (((dataBuffer[byteCounter] >> (7 - bitCounter)) & 0b1) == 1)
                {
                    uartx('0'); // ASCII 0
                }
                else
                {
                    uartx('1'); // Ascii 1
                }
            }
            // After each byte, send a space for readability.
            uartx(' '); // Ascii space
            if (byteCounter == 2)
            {
            // After the three data-bytes, add a tab to separate number of reads.
                uartx('\t'); // Ascii tab
            }
            // After the last byte is out, send a carriage return and line feed.
            if (byteCounter == 4)
            {
                uartx('\n\); // Line Feed
                uartx('\r'); // Return
            }
        }
        return;
    }
    
    uint8_t spix(uint8_t c)
    {
        while (!(UCB0IFG & UCTXIFG))
            /*EMPTY*/;
        UCB0TXBUF = c;
        while (!(UCB0IFG & UCRXIFG))
            /*EMPTY*/;
        c = UCB0RXBUF;
        return (c);
    }
    
    void uartx(uint8_t c)
    {
        while (!(UCA1IFG & UCTXIFG))
            /*EMPTY*/;
        UCA1TXBUF = c;
        return;
    }
  • Unsolicited [for your didactic goals]:
    > if ((dataBuffer[byteCounter] >> (7 - bitCounter)) & 0b1 == 1)
    This only works by accident, since (0b1==1) equals 1. I suggest:
    > if (((dataBuffer[byteCounter] >> (7 - bitCounter)) & 0b1) == 1)

    > uartx(49); // ASCII 0
    You can use the characters directly, which should make this more transparent to the reader:
    > uartx('1'); // ASCII 0

    [Edit: Also, '\t'==9, '\n' == 10, '\r' == 13, ' ' == 32 (that's a space in between the quotes)]

  • You sir are a lean, mean, coding machine. I'll update. I somehow misplaced a parenthesis when I pruned the code. Nice catch.

**Attention** This is a public forum