Because of the holidays, TI E2E™ design support forum responses will be delayed from Dec. 25 through Jan. 2. Thank you for your patience.

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.

MSP430FR4133: Using ADC and I2C to connect to an external DAC for analogue output

Part Number: MSP430FR4133

I've been using the ADC and it can convert the voltage reading from an external source. I'm getting number values from ADCMEM0 ranging from 20 to 120. I'm trying to transfer these values using I2C but for some reason they won't work.

I've tried implementing examples such as the an example file TI provide called, eusci_b_i2c_ex3_masterTxMultiple.c. Implementing it as it is, causes the program to get stuck at line 318 in eusci_b_i2c.c, which I don't understand why as the way the interrupts are handled is very complicated. I also used this resource but it's for another MSP430 model and the interrupts for it are different, which I also don't know how to implement. 

Another question I have is that, do I send the ADC result one byte at a time? Or do I specify a certain amount of time where the MSP430 reads in data and stores it, and then transmits it?

Here is the code with the second resource:

//i2c implementation using the TI_I2C functions, gets stuck in the 
//interrupt at the end of the definitions file



#include <msp430.h>
#include <driverlib.h>
#include "TI_USCI_I2C_master.h"
#include <stdio.h>
#include <math.h>

unsigned int ADC_Result = 0;
unsigned char SlaveAddress = 0xC0;
unsigned char char_ADC_result[1] = {0};
unsigned long long numbin_watch,numbin = 0;
int length = 0;
int numbinArray[10];
int count = 0;



long long convert(int num) {
  long long numbin = 0;
  int rem;
  long long i = 1;
  
  while (num!=0) {
    rem = num % 2;
    num /= 2;
    numbin += rem * i;
    i *= 10;
  }
  
  return numbin;
}

int lengthOfInt(unsigned long long numbin){
  
  int count = 0;
  
  while (numbin != 0)
  {
    numbin /= 10;
    count++;
  }
  return count;
}

int main(void)
{
  WDTCTL = WDTPW | WDTHOLD;                                // Stop WDT
  
  // Configure GPIO
  P1DIR |= BIT0;                                           // Set P1.0/LED to output direction
  P1OUT &= ~BIT0;                                          // P1.0 LED off
  
  // Configure ADC A9 pin
  SYSCFG2 |= ADCPCTL9;
  
  
  // Configure Pins for I2C
  //P5SEL0 |= BIT2 | BIT3;                                    // I2C pins, select pin P5.2 for SDA and pin P5.3 for SCL
  
  // Disable the GPIO power-on default high-impedance mode to activate
  // previously configured port settings
  PM5CTL0 &= ~LOCKLPM5;
  
  __enable_interrupt(); //worked without this, uncomment to test with it
  
  // Configure ADC10
  ADCCTL0 |= ADCSHT_2 | ADCON;                             // ADCON, S&H=16 ADC clks----ADCSHTx : 0000b = 4 ADCCLK cycles 0001b = 8 ADCCLK cycles 0010b = 16 ADCCLK cycles 0011b = 32 ADCCLK cycles
  ADCCTL1 |= ADCSHP | ADCSSEL_0 | ADCCONSEQ_2;             // ADCCLK = MODOSC; sampling timer   ADCCONSEQ_2 = Repeat-single-channel ---- remove it for single channel
  ADCCTL2 |= ADCRES_1 ;                                    // 10/8-bit conversion results ADCRES_1 = 10 bit////ADCRES_0 = 8 bit
  ADCMCTL0 |= ADCINCH_9 | ADCSREF_0;                       // A9 ADC input select; pin 8.1 use this for mic setup
  
  ADCIFG &= ~0x01;                                         //clear interrupt flag
  
  
  ADCIE |= ADCIE0;                                         // Enable ADC conv complete interrupt
  
  
//  // Configure USCI_B0 for I2C mode
//  UCB0CTLW0 |= UCSWRST;                                   // put eUSCI_B in reset state
//  UCB0CTLW0 |= UCMODE_3 | UCMST;                          // I2C master mode, SMCLK
//  //UCB0CTLW0 |= UCSLA10 | UCSSEL_2;                      // slave has 7 bit address, source clock is SMCLK
//  UCB0BRW = 0x8;                                          // baudrate = SMCLK / 8
//  UCB0CTLW0 &=~ UCSWRST;                                  // clear reset register
//  UCB0IE |= UCTXIE0 | UCNACKIE;                           // transmit and NACK interrupt enable
     
     
    TI_USCI_I2C_transmitinit(SlaveAddress,0x01);  // init transmitting with USCI and config

  
  while(1)
  {
    ADCCTL0 |= ADCENC | ADCSC;                          // Sampling and conversion start, could use ACCTL0 = 0x03
    __bis_SR_register(LPM0_bits | GIE);               // LPM0, ADC_ISR will force exit LOW POWER MODE 0
    //__no_operation();                                 // For debug only
    if (ADC_Result < 0x00F){
      P1OUT &= ~BIT0;                                 // Clear P1.0 LED off
    }
    else{
      P1OUT |= BIT0;                                   // Set P1.0 LED on
    }
    
    

    char_ADC_result[0] = ADC_Result;

    __delay_cycles(5000);
    
    while ( TI_USCI_I2C_notready() );         // wait for bus to be free
    //if ( TI_USCI_I2C_slave_present(SlaveAddress) )    // slave address may differ from
    //{                                         // initialization
      //while ( TI_USCI_I2C_notready() );         // wait for bus to be free
      TI_USCI_I2C_transmit(1,char_ADC_result);       // start transmitting 
    //}
    
    //__bic_SR_register_on_exit(LPM0_bits);         // Exit LPM0
    //LPM0;                                                     // Remain in LPM0 until all data is TX'd
  }
}

// ADC interrupt service routine
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = ADC_VECTOR
__interrupt void ADC_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(ADC_VECTOR))) ADC_ISR (void)
#else
#error Compiler not supported!
#endif
{
  switch(__even_in_range(ADCIV,ADCIV_ADCIFG))
  {
  case ADCIV_NONE:
    break;
  case ADCIV_ADCOVIFG:
    break;
  case ADCIV_ADCTOVIFG:
    break;
  case ADCIV_ADCHIIFG:
    break;
  case ADCIV_ADCLOIFG:
    break;
  case ADCIV_ADCINIFG:
    break;
  case ADCIV_ADCIFG:
    ADC_Result = ADCMEM0;
    __bic_SR_register_on_exit(LPM0_bits);          // Clear CPUOFF bit from LPM0
    break;
  default:
    break;
  }  
}

Here is the code for the example TI provides :

//uses TI board-specific i2c example, gets stuck in an interrupt again, line 318 in esci_b_i2c.c


#include <msp430.h>
#include <driverlib.h>
#include "Board.h"

#define SLAVE_ADDRESS 0xC0
#define CS_SMCLK_DESIRED_FREQUENCY_IN_KHZ   1000 //Target frequency for SMCLK in kHz
#define CS_SMCLK_FLLREF_RATIO   30 //SMCLK/FLLRef Ratio



unsigned int ADC_Result = 0;

// Pointer to TX data
uint8_t TXData = 0;
uint8_t TXByteCtr;


int main(void)
{
  WDTCTL = WDTPW | WDTHOLD;                                // Stop WDT
  
  // Configure GPIO
  P1DIR |= BIT0;                                           // Set P1.0/LED to output direction
  P1OUT &= ~BIT0;                                          // P1.0 LED off
  
  // Configure ADC A9 pin
  SYSCFG2 |= ADCPCTL9;
   
  //Set DCO FLL reference = REFO
  CS_initClockSignal(
                     CS_FLLREF,
                     CS_REFOCLK_SELECT,
                     CS_CLOCK_DIVIDER_1
                       );
  
  //Set Ratio and Desired MCLK Frequency and initialize DCO
  CS_initFLLSettle(
                   CS_SMCLK_DESIRED_FREQUENCY_IN_KHZ,
                   CS_SMCLK_FLLREF_RATIO
                     );
  
  //Set ACLK = VLO with frequency divider of 1
  CS_initClockSignal(
                     CS_ACLK,
                     CS_VLOCLK_SELECT,
                     CS_CLOCK_DIVIDER_1
                       );
  
  //Set SMCLK = DCO with frequency divider of 1
  CS_initClockSignal(
                     CS_SMCLK,
                     CS_DCOCLKDIV_SELECT,
                     CS_CLOCK_DIVIDER_1
                       );
  
  //Set MCLK = DCO with frequency divider of 1
  CS_initClockSignal(
                     CS_MCLK,
                     CS_DCOCLKDIV_SELECT,
                     CS_CLOCK_DIVIDER_1
                       );
  
  // Configure Pins for I2C , SCL 5.3, SDA 5.2
  GPIO_setAsPeripheralModuleFunctionInputPin(
                                             GPIO_PORT_UCB0SCL,
                                             GPIO_PIN_UCB0SCL,
                                             GPIO_FUNCTION_UCB0SCL
                                               );
  GPIO_setAsPeripheralModuleFunctionInputPin(
                                             GPIO_PORT_UCB0SDA,
                                             GPIO_PIN_UCB0SDA,
                                             GPIO_FUNCTION_UCB0SDA
                                               );
  
  /*
  * Disable the GPIO power-on default high-impedance mode to activate
  * previously configured port settings
  */
  PMM_unlockLPM5();
  
  EUSCI_B_I2C_initMasterParam param = {0};
  param.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK;
  param.i2cClk = CS_getSMCLK();
  param.dataRate = EUSCI_B_I2C_SET_DATA_RATE_400KBPS;
  param.byteCounterThreshold = 0;
  param.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP;
  EUSCI_B_I2C_initMaster(EUSCI_B0_BASE, &param);
  
  //Specify slave address
  EUSCI_B_I2C_setSlaveAddress(EUSCI_B0_BASE,
                              SLAVE_ADDRESS
                                );
  
  //Set Master in receive mode
  EUSCI_B_I2C_setMode(EUSCI_B0_BASE,
                      EUSCI_B_I2C_TRANSMIT_MODE
                        );
  
  //Enable I2C Module to start operations
  EUSCI_B_I2C_enable(EUSCI_B0_BASE);
  
  EUSCI_B_I2C_clearInterrupt(EUSCI_B0_BASE,
                             EUSCI_B_I2C_TRANSMIT_INTERRUPT0 +
                               EUSCI_B_I2C_NAK_INTERRUPT
                                 );
  //Enable master Receive interrupt
  EUSCI_B_I2C_enableInterrupt(EUSCI_B0_BASE,
                              EUSCI_B_I2C_TRANSMIT_INTERRUPT0 +
                                EUSCI_B_I2C_NAK_INTERRUPT
                                  );
  
  // Configure ADC10
  ADCCTL0 |= ADCSHT_2 | ADCON;                             // ADCON, S&H=16 ADC clks----ADCSHTx : 0000b = 4 ADCCLK cycles 0001b = 8 ADCCLK cycles 0010b = 16 ADCCLK cycles 0011b = 32 ADCCLK cycles
  ADCCTL1 |= ADCSHP | ADCSSEL_0 | ADCCONSEQ_2;             // ADCCLK = MODOSC; sampling timer   ADCCONSEQ_2 = Repeat-single-channel ---- remove it for single channel
  ADCCTL2 |= ADCRES_1 ;                                    // 10/8-bit conversion results ADCRES_1 = 10 bit////ADCRES_0 = 8 bit
  ADCMCTL0 |= ADCINCH_9 | ADCSREF_0;                       // A9 ADC input select; pin 8.1 use this for mic setup
  ADCIFG &= ~0x01;                                         //clear interrupt flag
  ADCIE |= ADCIE0;                                         // Enable ADC conv complete interrupt  
  
  while(1)
  {
    ADCCTL0 |= ADCENC | ADCSC;                          // Sampling and conversion start, could use ACCTL0 = 0x03
    __bis_SR_register(LPM0_bits | GIE);               // LPM0, ADC_ISR will force exit LOW POWER MODE 0
    //__no_operation();                                 // For debug only
    if (ADC_Result < 0x00F){
      P1OUT &= ~BIT0;                                 // Clear P1.0 LED off
    }
    else{
      P1OUT |= BIT0;                                   // Set P1.0 LED on
    }
    __delay_cycles(1000);                   // Delay between transmissions
    TXByteCtr = 1;                          // Load TX byte counter
    TXData = ADC_Result;
    
    while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B0_BASE));
    
    EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B0_BASE, TXData++);
    
    //__bis_SR_register(CPUOFF + GIE);        // Enter LPM0 w/ interrupts
    // Remain in LPM0 until all data
    // is TX'd
    // Increment data byte
    
  }
} 



// ADC interrupt service routine
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = ADC_VECTOR
__interrupt void ADC_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(ADC_VECTOR))) ADC_ISR (void)
#else
#error Compiler not supported!
#endif
{
  switch(__even_in_range(ADCIV,ADCIV_ADCIFG))
  {
  case ADCIV_NONE:
    break;
  case ADCIV_ADCOVIFG:
    break;
  case ADCIV_ADCTOVIFG:
    break;
  case ADCIV_ADCHIIFG:
    break;
  case ADCIV_ADCLOIFG:
    break;
  case ADCIV_ADCINIFG:
    break;
  case ADCIV_ADCIFG:
    ADC_Result = ADCMEM0;
    __bic_SR_register_on_exit(LPM0_bits);          // Clear CPUOFF bit from LPM0
    break;
  default:
    break;
  }  
}


//------------------------------------------------------------------------------
// The USCIAB0TX_ISR is structured such that it can be used to transmit any
// number of bytes by pre-loading TXByteCtr with the byte count. Also, TXData
// points to the next byte to transmit.
//------------------------------------------------------------------------------
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=USCI_B0_VECTOR
__interrupt
#elif defined(__GNUC__)
__attribute__((interrupt(USCI_B0_VECTOR)))
#endif
void USCIB0_ISR(void)
{
    switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
  {
        case USCI_NONE:             // No interrupts break;
            break;
        case USCI_I2C_UCALIFG:      // Arbitration lost
            break;
        case USCI_I2C_UCNACKIFG:    // NAK received (master only)
            //resend start if NACK
            EUSCI_B_I2C_masterSendStart(EUSCI_B0_BASE);
            break;
        case USCI_I2C_UCTXIFG0:     // TXIFG0
            // Check TX byte counter
            if (TXByteCtr)
            {
                EUSCI_B_I2C_masterSendMultiByteNext(EUSCI_B0_BASE, TXData++);
                // Decrement TX byte counter
                TXByteCtr--;
            }
            else
            {
                EUSCI_B_I2C_masterSendMultiByteStop(EUSCI_B0_BASE);
                // Exit LPM0
                __bic_SR_register_on_exit(CPUOFF);
            }
            break;
        default:
            break;
  }
}

Suggestions for other solutions are welcome or any other resources I should read to try and get this to work would be very helpful. Thank you in advance for your time and for reading.

  • What DAC are you using?

    > #define SLAVE_ADDRESS 0xC0

    I can tell by looking at this that it is incorrect, since an I2C address is only 7 bits. Maybe it should be 0x60? The DAC data sheet should say.

    The DAC data sheet should also describe how you should pace the data -- whether it has an internal clock to generate a fixed output rate, or if you need to do that in your program using e.g. a timer.

  • I'm using this breakout board (BOB-12918) which already has pullup resistors and just makes it easier to use the DAC, which is the MCP4725.

    The address bit is 1 1 0 0 A2 A1 A0 0, where A2 and A1 are 0 by default, A0 is wired to ground and the last 0 is the R/Wbar bit. You're right that it's 0x60 if I ignore the R/Wbar bit, I didn't know that it should be ignored. Wouldn't the SCL line be the clock that also processes the data in the DAC?

    Thank you very much for taking the time to read my post and replying. Any further help would be greatly appreciated.

  • Yes, it looks like you want 0x60 for the address. (The R/W bit isn't part of the address.) 

    According to the DAC data sheet [Fig 6-1] you need to send two bytes (the EUSCI sends the first one for you, you send the other two) with the 12-bit value right-justified. The DAC output is updated (immediately) when the final byte is complete [Note 2]. That means that your program needs to do the pacing. Your __delay_cycles(5000) will get you started (~200Hz), but long-term you probably want a timer. 

  • So for each 12 bits of data I will send 2 bytes. Based on the datasheet, first byte will have 0 0 PD1 PD0 4 data bits then another byte with the remaining 8 bits of data? Then for the next batch of data these two bytes are repeated?

  • Yes. In theory you could just send the ADCMEM value directly (PD=0), though you might want to scale the 10-bit value (shift left by 2) to get full-scale out of the DAC.

    Fig 6-1 also mentions an option to send two extra bytes in the same transaction, which will (I think?) override the previous two bytes. I'm not sure what they have in mind for that feature; I suspect you don't want to do that.

  • Thank you again for taking the time to reply to my beginner questions. If I manage to figure it out I'll mark the post as resolved, but if not I'll keep everything under one post.

  • Hi Yiannis,

    Does this question have been resolved? Or do you still need some support?

    Thanks!

    Best Regards

    Johnson

  • I tested my circuit with the board today and it's still not working. Do I make a new post or do I reply to the comments here?

  • I'll reply here with my issue to save some time.

    //uses TI board-specific i2c example, gets stuck in an interrupt again, line 318 in esci_b_i2c.c, multibyte
    
    
    #include "project_definitions.h"
    
    
    int order2 = 0;
    
    
    int main(void)
    {
      WDTCTL = WDTPW | WDTHOLD;                                // Stop WDT
      
      // Configure GPIO
      P1DIR |= BIT0;                                           // Set P1.0/LED to output direction
      P1OUT &= ~BIT0;                                          // P1.0 LED off
      P5REN |= BIT0;
      P5OUT |= BIT0;
      
      // Configure ADC A9 pin
      SYSCFG2 |= ADCPCTL9;
      
      //Set DCO FLL reference = REFO
      CS_initClockSignal(CS_FLLREF, CS_REFOCLK_SELECT, CS_CLOCK_DIVIDER_1);
      
      //Set Ratio and Desired MCLK Frequency and initialize DCO
      CS_initFLLSettle(CS_SMCLK_DESIRED_FREQUENCY_IN_KHZ, CS_SMCLK_FLLREF_RATIO);
      
      //Set ACLK = VLO with frequency divider of 1
      CS_initClockSignal(CS_ACLK, CS_VLOCLK_SELECT, CS_CLOCK_DIVIDER_1);
      
      //Set SMCLK = DCO with frequency divider of 1
      CS_initClockSignal(CS_SMCLK, CS_DCOCLKDIV_SELECT, CS_CLOCK_DIVIDER_1);
      
      //Set MCLK = DCO with frequency divider of 1
      CS_initClockSignal(CS_MCLK,CS_DCOCLKDIV_SELECT,CS_CLOCK_DIVIDER_1);
      
      // Configure Pins for I2C , SCL 5.3, SDA 5.2
      GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_UCB0SCL, GPIO_PIN_UCB0SCL, GPIO_FUNCTION_UCB0SCL);
      GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_UCB0SDA, GPIO_PIN_UCB0SDA, GPIO_FUNCTION_UCB0SDA);
      
      /*
      * Disable the GPIO power-on default high-impedance mode to activate
      * previously configured port settings
      */
      PMM_unlockLPM5();
      
      EUSCI_B_I2C_initMasterParam param = {0};
      param.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK;
      param.i2cClk = CS_getSMCLK();
      param.dataRate = EUSCI_B_I2C_SET_DATA_RATE_400KBPS;
      param.byteCounterThreshold = 255;
      param.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP;
      EUSCI_B_I2C_initMaster(EUSCI_B0_BASE, &param);
      
      //Specify slave address
      EUSCI_B_I2C_setSlaveAddress(EUSCI_B0_BASE,SLAVE_ADDRESS);
      
      //Set Master in receive mode
      EUSCI_B_I2C_setMode(EUSCI_B0_BASE, EUSCI_B_I2C_TRANSMIT_MODE);
      
      //Enable I2C Module to start operations
      EUSCI_B_I2C_enable(EUSCI_B0_BASE);
      EUSCI_B_I2C_clearInterrupt(EUSCI_B0_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT0 + EUSCI_B_I2C_NAK_INTERRUPT);
      
      //Enable master Receive interrupt
      EUSCI_B_I2C_enableInterrupt(EUSCI_B0_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT0 + EUSCI_B_I2C_NAK_INTERRUPT);
      
      //set a timeout for 34ms
      EUSCI_B_I2C_setTimeout(EUSCI_B0_BASE,EUSCI_B_I2C_TIMEOUT_34_MS);
      
      // Configure ADC10
      ADCCTL0 |= ADCSHT_2 | ADCON;                             // ADCON, S&H=16 ADC clks----ADCSHTx : 0000b = 4 ADCCLK cycles 0001b = 8 ADCCLK cycles 0010b = 16 ADCCLK cycles 0011b = 32 ADCCLK cycles
      ADCCTL1 |= ADCSHP | ADCSSEL_0 | ADCCONSEQ_2;             // ADCCLK = MODOSC; sampling timer   ADCCONSEQ_2 = Repeat-single-channel ---- remove it for single channel
      ADCCTL2 |= ADCRES_1 ;                                    // 10/8-bit conversion results ADCRES_1 = 10 bit////ADCRES_0 = 8 bit
      ADCMCTL0 |= ADCINCH_9 | ADCSREF_0;                       // A9 ADC input select; pin 8.1 use this for mic setup
      ADCIFG &= ~0x01;                                         //clear interrupt flag
      ADCIE |= ADCIE0;                                         // Enable ADC conv complete interrupt  
      
      
      
      while(1)
      {
        ADCCTL0 |= ADCENC | ADCSC;                          // Sampling and conversion start, could use ACCTL0 = 0x03
        __bis_SR_register(LPM0_bits | GIE);               // LPM0, ADC_ISR will force exit LOW POWER MODE 0
        //__no_operation();                                 // For debug only
        if (ADC_Result < 0x00F){
          P1OUT &= ~BIT0;                                 // Clear P1.0 LED off
        }
        else{
          P1OUT |= BIT0;                                   // Set P1.0 LED on
        }
        __delay_cycles(5000);                   // Delay between transmissions
    
        //convert the adc result to binary
        numbin = convertIntToBin(ADC_Result);
        
        //add the binary number to the numbinArray
        ptr_numbinArray = addToArray(numbin);
        i = 0;
        for (i = 0;i<=10; i++){ //to shift the binary 2 to the left make i = 2; and i<= 12;
          numbinArray[i] = *(ptr_numbinArray + i); 
        }
        
        //split numbinArray up to the two required bytes
        //byte 1
        i = 8;
        c = 7;
        for (i = 8; i<=11; i++){
          byte1[c] = numbinArray[i];
          c--;
        }
        
        //byte 2
        i = 0;
        c = 7;
        for (i = 0; i<=7; i++){ 
          byte2[c] = numbinArray[i];
          c--;
        }
        
        data1 = convertBinToInt(ptr_byte1);
        data2 = convertBinToInt(ptr_byte2);
        
        TXByteCtr = 1000;                          // Load TX byte counter
        
        if (order == 1){
          TXData = data1;
          order = 2;
          byteNum++;
          data1 = 0;
        }
        else if (order == 2){
          TXData = data2;
          order = 1;
          byteNum++;
          data2 = 0;
        }
        
        
        while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B0_BASE));
        
        if(order2 == 0){
        EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B0_BASE, TXData);
        order2++;
        }
        else if (order2 == 1){
        EUSCI_B_I2C_masterSendMultiByteNext(EUSCI_B0_BASE, TXData);
        }
        else if (byteNum == 900){
        EUSCI_B_I2C_masterSendMultiByteFinish(EUSCI_B0_BASE, TXData);
        byteNum = 0;
        }
        
        //__bis_SR_register(LPM0_bits | GIE);        // Enter LPM0 w/ interrupts, changed from CPUOFF + GIE
        // Remain in LPM0 until all data
        // is TX'd
        // Increment data byte
      }
    }
    
    
    
    // ADC interrupt service routine
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = ADC_VECTOR
    __interrupt void ADC_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(ADC_VECTOR))) ADC_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
      switch(__even_in_range(ADCIV,ADCIV_ADCIFG))
      {
      case ADCIV_NONE:
        break;
      case ADCIV_ADCOVIFG:
        break;
      case ADCIV_ADCTOVIFG:
        break;
      case ADCIV_ADCHIIFG:
        break;
      case ADCIV_ADCLOIFG:
        break;
      case ADCIV_ADCINIFG:
        break;
      case ADCIV_ADCIFG:
        ADC_Result = ADCMEM0;
        __bic_SR_register_on_exit(LPM0_bits);          // Clear CPUOFF bit from LPM0
        break;
      default:
        break;
      }  
    }
    
    
    //------------------------------------------------------------------------------
    // The USCIAB0TX_ISR is structured such that it can be used to transmit any
    // number of bytes by pre-loading TXByteCtr with the byte count. Also, TXData
    // points to the next byte to transmit.
    //------------------------------------------------------------------------------
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=USCI_B0_VECTOR
    __interrupt
    #elif defined(__GNUC__)
    __attribute__((interrupt(USCI_B0_VECTOR)))
    #endif
    void USCIB0_ISR(void)
    {
      switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
      {
      case USCI_NONE:             // No interrupts break;
        break;
      case USCI_I2C_UCALIFG:      // Arbitration lost
        break;
      case USCI_I2C_UCNACKIFG:    // NAK received (master only)
        //resend start if NACK
        EUSCI_B_I2C_masterSendStart(EUSCI_B0_BASE);
        break;
      case USCI_I2C_UCTXIFG0:     // TXIFG0
        // Check TX byte counter
        if (TXByteCtr)
        {
          EUSCI_B_I2C_masterSendMultiByteNext(EUSCI_B0_BASE, TXData);
          // Decrement TX byte counter
          TXByteCtr--;
        }
        else
        {
          EUSCI_B_I2C_masterSendMultiByteStop(EUSCI_B0_BASE);
          // Exit LPM0
          __bic_SR_register_on_exit(LPM0_bits);
        }
        break;
      default:
        break;
      }
    }
    
    

    This is the code I have so far. The values go into the TXBUF but I get stuck on this interrupt (in the while loop) in the "EUSCI_B_I2C_masterSendMultiByteNext" function.

        //If interrupts are not used, poll for flags
        if (!(HWREG16(baseAddress + OFS_UCBxIE) & UCTXIE)){
            //Poll for transmit interrupt flag.
            while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;
        }
    

    It used to get stuck in the "EUSCI_B_I2C_masterSendMultiByteStart" at this point:

        //Poll for transmit interrupt flag.
        while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;
    

    which is the same point as before but at a function that gets called after the other one. Lines 144 and 148.

    project_definitions.h:

    #include <msp430.h>
    #include <driverlib.h>
    #include "Board.h"
    #include <math.h>
    
    
    #define SLAVE_ADDRESS 0xC0
    #define CS_SMCLK_DESIRED_FREQUENCY_IN_KHZ   1000 //Target frequency for SMCLK in kHz
    #define CS_SMCLK_FLLREF_RATIO   30 //SMCLK/FLLRef Ratio
    
    int c = 0, i = 0;
    int order = 1, byteNum = 1;
    unsigned int ADC_Result = 0;
    long long numbin = 0;
    
    // Pointer to TX data
    uint8_t TXData = 0;
    uint16_t TXByteCtr;
    
    uint8_t* ptr_numbinArray;
    uint8_t numbinArray[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
    
    uint8_t byte1[8] = {0,0,0,0,0,0,0,0};  //postions 5,6,7,8 are 0 for PD0, PD1, C2, C1 respectively
    uint8_t byte2[8] = {0,0,0,0,0,0,0,0};
    uint8_t* ptr_byte1 = byte1;
    uint8_t* ptr_byte2 = byte2;
    
    uint8_t data1 = 0;
    uint8_t data2 = 0;
    
    long long convertIntToBin(int num) {
      long long numbin = 0;
      int rem;
      long long i = 1;
      
      while (num!=0) {
        rem = num % 2;
        num /= 2;
        numbin += rem * i;
        i *= 10;
      }
      return numbin;
    }
    
    uint8_t* addToArray(long long numbin){
      int pos = 0;  
      static uint8_t numbinArray[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
      while (numbin != 0){
        numbinArray[pos] = numbin % 10;
        numbin /= 10;
        pos++;
      }    
      return numbinArray;
    }
    
    uint8_t convertBinToInt(uint8_t bin[8]){
      uint8_t num = 0;
      int i = 7;
      int c = 0;
      for(i = 7; i>=0; i--){
        num += bin[i] * pow(2,c);
        c++;
      }
      return num;
    }
    

  • I posted more code if you would like to further assist me. Thank you.

  • 1) Just glancing over this, you appear to be sending 1000 bytes in a transaction, while the device expects 2. The data sheet doesn't say what happens if you send more than 4 -- a NACK is one possibility.

    2) The SendMultiByte functions are designed to operate properly without interrupts. I think your operation could be as simple as (untested!):

    unsigned dacval;
    unsigned char dachi, daclo;
    dacval = ADCMEM0;
    dacval <<= 2;  // (optional) 10-bit->12-bit range
    dachi = (dacval >> 8) & 0xFF; // high byte (first)
    daclo = (dacval >> 0) & 0xFF; // low byte (second)
    EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B0_BASE, dachi);
    EUSCI_B_I2C_masterSendMultiByteFinish(EUSCI_B0_BASE, daclo);

    Then don't enable any interrupts. the SendMultiByte functions don't deal well with NACKs, so you could also try the "WithTimeout" variants if that becomes a problem.

    [Edit: Fixed B1->B0]

  • I am aware I am sending more than two bytes but that's because I'm dealing with a continuously changing ADC value. The input is from the output of a microphone circuit and the output of the DAC drives a speaker circuit. Both circuits were tested and they work so the problem is the code. How would I go about continuously sending values so that I can get a voltage out the DAC and possibly a sound out of a speaker? Again, the speaker system works, so the main issue is continuously sending the data.

  • You don't want to update the DAC "continuously" -- you want to update it once for each ADC sample. Then you adjust (increase) the ADC sample rate to get the fidelity you want.

    So your loop consists of: (1) get one sample from the ADC (2) write that value to the DAC [see above] (3) pause for timing (as needed) (4) repeat. There are ways of streamlining this, but I suggest you start by getting this running.

    With a 400kHz I2C clock, the maximum (theoretical) DAC update rate is 400k/(3*9bits)=14.8ksps, and you shouldn't count on getting anywhere near that. 4ksps is probably feasible, maybe even 6-8ksps if you're careful.

  • But wouldn't this send an address byte each time there's a new sample? Or is there no way around that because the data changes?

    Edit: Also what about the EUSCI_B_I2C_masterSendMultiByteNext function? Or is that not needed because the DAC only accepts two bytes at a time?

  • Fig 6-1 indicates that each DAC update is a separate I2C transaction (1 address+2 data bytes).

    Since you're only sending two (data) bytes, you wouldn't use the "Next" function.

  • Thank you again for assisting me with this. My code is now

    #include "project_definitions.h"
    
    
    unsigned DACval;
    unsigned char data_1, data_2;
    
    
    int main(void)
    {
      WDTCTL = WDTPW | WDTHOLD;                                // Stop WDT
      
      // Configure GPIO
      P1DIR |= BIT0;                                           // Set P1.0/LED to output direction
      P1OUT &= ~BIT0;                                          // P1.0 LED off
      P5REN |= BIT0;
      P5OUT |= BIT0;
      
      // Configure ADC A9 pin
      SYSCFG2 |= ADCPCTL9;
      
      //Set DCO FLL reference = REFO
      CS_initClockSignal(CS_FLLREF, CS_REFOCLK_SELECT, CS_CLOCK_DIVIDER_1);
      
      //Set Ratio and Desired MCLK Frequency and initialize DCO
      CS_initFLLSettle(CS_SMCLK_DESIRED_FREQUENCY_IN_KHZ, CS_SMCLK_FLLREF_RATIO);
      
      //Set ACLK = VLO with frequency divider of 1
      CS_initClockSignal(CS_ACLK, CS_VLOCLK_SELECT, CS_CLOCK_DIVIDER_1);
      
      //Set SMCLK = DCO with frequency divider of 1
      CS_initClockSignal(CS_SMCLK, CS_DCOCLKDIV_SELECT, CS_CLOCK_DIVIDER_1);
      
      //Set MCLK = DCO with frequency divider of 1
      CS_initClockSignal(CS_MCLK,CS_DCOCLKDIV_SELECT,CS_CLOCK_DIVIDER_1);
      
      // Configure Pins for I2C , SCL 5.3, SDA 5.2
      GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_UCB0SCL, GPIO_PIN_UCB0SCL, GPIO_FUNCTION_UCB0SCL);
      GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_UCB0SDA, GPIO_PIN_UCB0SDA, GPIO_FUNCTION_UCB0SDA);
      
      /*
      * Disable the GPIO power-on default high-impedance mode to activate
      * previously configured port settings
      */
      PMM_unlockLPM5();
      
      EUSCI_B_I2C_initMasterParam param = {0};
      param.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK;
      param.i2cClk = CS_getSMCLK();
      param.dataRate = EUSCI_B_I2C_SET_DATA_RATE_400KBPS;
      param.byteCounterThreshold = 255;
      param.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP;
      EUSCI_B_I2C_initMaster(EUSCI_B0_BASE, &param);
      
      //Specify slave address
      EUSCI_B_I2C_setSlaveAddress(EUSCI_B0_BASE,SLAVE_ADDRESS);
      
      //Set Master in receive mode
      EUSCI_B_I2C_setMode(EUSCI_B0_BASE, EUSCI_B_I2C_TRANSMIT_MODE);
      
      //Enable I2C Module to start operations
      EUSCI_B_I2C_enable(EUSCI_B0_BASE);
      EUSCI_B_I2C_clearInterrupt(EUSCI_B0_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT0 + EUSCI_B_I2C_NAK_INTERRUPT);
      
      //Enable master Receive interrupt
      EUSCI_B_I2C_enableInterrupt(EUSCI_B0_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT0 + EUSCI_B_I2C_NAK_INTERRUPT);
      
      //set a timeout for 34ms
      EUSCI_B_I2C_setTimeout(EUSCI_B0_BASE,EUSCI_B_I2C_TIMEOUT_34_MS);
      
      // Configure ADC10
      ADCCTL0 |= ADCSHT_2 | ADCON;                             // ADCON, S&H=16 ADC clks----ADCSHTx : 0000b = 4 ADCCLK cycles 0001b = 8 ADCCLK cycles 0010b = 16 ADCCLK cycles 0011b = 32 ADCCLK cycles
      ADCCTL1 |= ADCSHP | ADCSSEL_0 | ADCCONSEQ_2;             // ADCCLK = MODOSC; sampling timer   ADCCONSEQ_2 = Repeat-single-channel ---- remove it for single channel
      ADCCTL2 |= ADCRES_1 ;                                    // 10/8-bit conversion results ADCRES_1 = 10 bit////ADCRES_0 = 8 bit
      ADCMCTL0 |= ADCINCH_9 | ADCSREF_0;                       // A9 ADC input select; pin 8.1 use this for mic setup
      ADCIFG &= ~0x01;                                         //clear interrupt flag
      ADCIE |= ADCIE0;                                         // Enable ADC conv complete interrupt  
      
      
      
      while(1)
      {
        ADCCTL0 |= ADCENC | ADCSC;                          // Sampling and conversion start, could use ACCTL0 = 0x03
        __bis_SR_register(LPM0_bits | GIE);               // LPM0, ADC_ISR will force exit LOW POWER MODE 0
        //__no_operation();                                 // For debug only
        if (ADC_Result < 0x00F){
          P1OUT &= ~BIT0;                                 // Clear P1.0 LED off
        }
        else{
          P1OUT |= BIT0;                                   // Set P1.0 LED on
        }
        __delay_cycles(5000);                   // Delay between transmissions
        
        DACval = ADC_Result;
        DACval <<= 2;  // (optional) 10-bit->12-bit range
        data_1 = (DACval >> 8) & 0xFF; // high byte (first)
        data_2 = (DACval >> 0) & 0xFF; // low byte (second)
        
        //while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B0_BASE));
        
        EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B0_BASE, data1);
        EUSCI_B_I2C_masterSendMultiByteFinish(EUSCI_B0_BASE, data2);
        
      }
    }
    
    
    
    // ADC interrupt service routine
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = ADC_VECTOR
    __interrupt void ADC_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(ADC_VECTOR))) ADC_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
      switch(__even_in_range(ADCIV,ADCIV_ADCIFG))
      {
      case ADCIV_NONE:
        break;
      case ADCIV_ADCOVIFG:
        break;
      case ADCIV_ADCTOVIFG:
        break;
      case ADCIV_ADCHIIFG:
        break;
      case ADCIV_ADCLOIFG:
        break;
      case ADCIV_ADCINIFG:
        break;
      case ADCIV_ADCIFG:
        ADC_Result = ADCMEM0;
        __bic_SR_register_on_exit(LPM0_bits);          // Clear CPUOFF bit from LPM0
        break;
      default:
        break;
      }  
    }
    
    
    //#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    //#pragma vector=USCI_B0_VECTOR
    //__interrupt
    //#elif defined(__GNUC__)
    //__attribute__((interrupt(USCI_B0_VECTOR)))
    //#endif
    //void USCIB0_ISR(void)
    //{
    //  switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
    //  {
    //  case USCI_NONE:             // No interrupts break;
    //    break;
    //  case USCI_I2C_UCALIFG:      // Arbitration lost
    //    break;
    //  case USCI_I2C_UCNACKIFG:    // NAK received (master only)
    //    //resend start if NACK
    //    EUSCI_B_I2C_masterSendStart(EUSCI_B0_BASE);
    //    break;
    //  case USCI_I2C_UCTXIFG0:     // TXIFG0
    //    // Check TX byte counter
    //    if (TXByteCtr)
    //    {
    //      EUSCI_B_I2C_masterSendMultiByteNext(EUSCI_B0_BASE, TXData);
    //      // Decrement TX byte counter
    //      TXByteCtr--;
    //    }
    //    else
    //    {
    //      EUSCI_B_I2C_masterSendMultiByteStop(EUSCI_B0_BASE);
    //      // Exit LPM0
    //      __bic_SR_register_on_exit(LPM0_bits);
    //    }
    //    break;
    //  default:
    //    break;
    //  }
    //}
    //

    I will test it soon and get back to you if there are any problems.

  • >  EUSCI_B_I2C_enableInterrupt(EUSCI_B0_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT0 + EUSCI_B_I2C_NAK_INTERRUPT);

    Remove this. The ISR will just get in your way.

    -----------

    Also, did you fix the SLAVE_ADDRESS? It still looks incorrect in the header file.

  • I removed it and yes I fixed it now. Thank you again. I'll test it tomorrow and get back to you.

  • Any update here for your test?

    Best Regards

    Johnson

  • I tested it out this morning. If I comment out the following parts in eusci_b_i2c.c then I can see something on the oscilloscope from the SDA pin:

    void EUSCI_B_I2C_masterSendMultiByteStart (uint16_t baseAddress,
        uint8_t txData
        )
    {
        //Store current transmit interrupt enable
        uint16_t txieStatus = HWREG16(baseAddress + OFS_UCBxIE) & UCTXIE;
    
        //Disable transmit interrupt enable
        HWREG16(baseAddress + OFS_UCBxIE) &= ~(UCTXIE);
    
        //Send start condition.
        HWREG16(baseAddress + OFS_UCBxCTLW0) |= UCTR +  UCTXSTT;
    
        //Poll for transmit interrupt flag.
        //while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;
    
        //Send single byte data.
        HWREG16(baseAddress + OFS_UCBxTXBUF) = txData;
    
        //Reinstate transmit interrupt enable
        HWREG16(baseAddress + OFS_UCBxIE) |= txieStatus;
    }
    

    void EUSCI_B_I2C_masterSendMultiByteFinish (uint16_t baseAddress,
        uint8_t txData
        )
    {
        //If interrupts are not used, poll for flags
    //    if (!(HWREG16(baseAddress + OFS_UCBxIE) & UCTXIE)){
    //        //Poll for transmit interrupt flag.
    //        while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;
    //    }
    
        //Send single byte data.
        HWREG16(baseAddress + OFS_UCBxTXBUF) = txData;
    
        //Poll for transmit interrupt flag.
        //while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;
    
        //Send stop condition.
        HWREG16(baseAddress + OFS_UCBxCTLW0) |= UCTXSTP;
    }
    
    

    To sum up the point above, in the two functions i am using (code pasted at the end of my comment) ,which are "EUSCI_B_I2C_masterSendMultiByteStart" and "EUSCI_B_I2C_masterSendMultiByteFinish", the always get stuck where it says:

        //Poll for transmit interrupt flag.
        //while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;
    

    which I really don't understand. But at the very least data is sent through the SDA pin and can be detected. There is no voltage out from DAC even though it's receiving data. Any ideas how to test what's wrong with the DAC? The format is correct so I am at a loss as to what to do. I'm thinking of purchasing another DAC chip and just using the pullup resistors of the MSP430 with it if I can do that, but I would like to get this to work since I've spent so much time on it already.

    project_definitions.h (most of the code here is unused due to new main code )

    #include <msp430.h>
    #include <driverlib.h>
    #include "Board.h"
    #include <math.h>
    
    
    #define SLAVE_ADDRESS 0x60
    #define CS_SMCLK_DESIRED_FREQUENCY_IN_KHZ   1000 //Target frequency for SMCLK in kHz
    #define CS_SMCLK_FLLREF_RATIO   30 //SMCLK/FLLRef Ratio
    
    int c = 0, i = 0;
    int order = 1, byteNum = 1;
    unsigned int ADC_Result = 0;
    long long numbin = 0;
    
    // Pointer to TX data
    uint8_t TXData = 0;
    uint16_t TXByteCtr;
    
    uint8_t* ptr_numbinArray;
    uint8_t numbinArray[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
    
    uint8_t byte1[8] = {0,0,0,0,0,0,0,0};  //postions 5,6,7,8 are 0 for PD0, PD1, C2, C1 respectively
    uint8_t byte2[8] = {0,0,0,0,0,0,0,0};
    uint8_t* ptr_byte1 = byte1;
    uint8_t* ptr_byte2 = byte2;
    
    uint8_t data1 = 0;
    uint8_t data2 = 0;
    
    long long convertIntToBin(int num) {
      long long numbin = 0;
      int rem;
      long long i = 1;
      
      while (num!=0) {
        rem = num % 2;
        num /= 2;
        numbin += rem * i;
        i *= 10;
      }
      return numbin;
    }
    
    uint8_t* addToArray(long long numbin){
      int pos = 0;  
      static uint8_t numbinArray[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
      while (numbin != 0){
        numbinArray[pos] = numbin % 10;
        numbin /= 10;
        pos++;
      }    
      return numbinArray;
    }
    
    uint8_t convertBinToInt(uint8_t bin[8]){
      uint8_t num = 0;
      int i = 7;
      int c = 0;
      for(i = 7; i>=0; i--){
        num += bin[i] * pow(2,c);
        c++;
      }
      return num;
    }
    

    main multi byte send.c

    
    
    #include "project_definitions.h"
    
    
    unsigned DACval;
    unsigned char data_1, data_2;
    
    
    int main(void)
    {
      WDTCTL = WDTPW | WDTHOLD;                                // Stop WDT
      
      // Configure GPIO
      P1DIR |= BIT0;                                           // Set P1.0/LED to output direction
      P1OUT &= ~BIT0;                                          // P1.0 LED off
      //P5REN |= BIT0;
      //P5OUT |= BIT0;
      
      // Configure ADC A9 pin
      SYSCFG2 |= ADCPCTL9;
      
      //Set DCO FLL reference = REFO
      CS_initClockSignal(CS_FLLREF, CS_REFOCLK_SELECT, CS_CLOCK_DIVIDER_1);
      
      //Set Ratio and Desired MCLK Frequency and initialize DCO
      CS_initFLLSettle(CS_SMCLK_DESIRED_FREQUENCY_IN_KHZ, CS_SMCLK_FLLREF_RATIO);
      
      //Set ACLK = VLO with frequency divider of 1
      CS_initClockSignal(CS_ACLK, CS_VLOCLK_SELECT, CS_CLOCK_DIVIDER_1);
      
      //Set SMCLK = DCO with frequency divider of 1
      CS_initClockSignal(CS_SMCLK, CS_DCOCLKDIV_SELECT, CS_CLOCK_DIVIDER_1);
      
      //Set MCLK = DCO with frequency divider of 1
      CS_initClockSignal(CS_MCLK,CS_DCOCLKDIV_SELECT,CS_CLOCK_DIVIDER_1);
      
      // Configure Pins for I2C , SCL 5.3, SDA 5.2
      GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_UCB0SCL, GPIO_PIN_UCB0SCL, GPIO_FUNCTION_UCB0SCL);
      GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_UCB0SDA, GPIO_PIN_UCB0SDA, GPIO_FUNCTION_UCB0SDA);
      
      /*
      * Disable the GPIO power-on default high-impedance mode to activate
      * previously configured port settings
      */
      PMM_unlockLPM5();
      
      EUSCI_B_I2C_initMasterParam param = {0};
      param.selectClockSource = EUSCI_B_I2C_CLOCKSOURCE_SMCLK;
      param.i2cClk = CS_getSMCLK();
      param.dataRate = EUSCI_B_I2C_SET_DATA_RATE_400KBPS;
      param.byteCounterThreshold = 255;
      param.autoSTOPGeneration = EUSCI_B_I2C_NO_AUTO_STOP;
      EUSCI_B_I2C_initMaster(EUSCI_B0_BASE, &param);
      
      //Specify slave address
      EUSCI_B_I2C_setSlaveAddress(EUSCI_B0_BASE,SLAVE_ADDRESS);
      
      //Set Master in receive mode
      EUSCI_B_I2C_setMode(EUSCI_B0_BASE, EUSCI_B_I2C_TRANSMIT_MODE);
      
      //Enable I2C Module to start operations
      EUSCI_B_I2C_enable(EUSCI_B0_BASE);
      EUSCI_B_I2C_clearInterrupt(EUSCI_B0_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT0 + EUSCI_B_I2C_NAK_INTERRUPT);
      
      //Enable master Receive interrupt
      //EUSCI_B_I2C_enableInterrupt(EUSCI_B0_BASE, EUSCI_B_I2C_TRANSMIT_INTERRUPT0 + EUSCI_B_I2C_NAK_INTERRUPT);
      
      //set a timeout for 34ms
      //EUSCI_B_I2C_setTimeout(EUSCI_B0_BASE,EUSCI_B_I2C_TIMEOUT_34_MS);
      
      // Configure ADC10
      ADCCTL0 |= ADCSHT_2 | ADCON;                             // ADCON, S&H=16 ADC clks----ADCSHTx : 0000b = 4 ADCCLK cycles 0001b = 8 ADCCLK cycles 0010b = 16 ADCCLK cycles 0011b = 32 ADCCLK cycles
      ADCCTL1 |= ADCSHP | ADCSSEL_0 | ADCCONSEQ_2;             // ADCCLK = MODOSC; sampling timer   ADCCONSEQ_2 = Repeat-single-channel ---- remove it for single channel
      ADCCTL2 |= ADCRES_1 ;                                    // 10/8-bit conversion results ADCRES_1 = 10 bit////ADCRES_0 = 8 bit
      ADCMCTL0 |= ADCINCH_9 | ADCSREF_0;                       // A9 ADC input select; pin 8.1 use this for mic setup
      ADCIFG &= ~0x01;                                         //clear interrupt flag
      ADCIE |= ADCIE0;                                         // Enable ADC conv complete interrupt  
      
      
      
      while(1)
      {
        ADCCTL0 |= ADCENC | ADCSC;                          // Sampling and conversion start, could use ACCTL0 = 0x03
        __bis_SR_register(LPM0_bits | GIE);               // LPM0, ADC_ISR will force exit LOW POWER MODE 0
        //__no_operation();                                 // For debug only
        if (ADC_Result < 0x00F){
          P1OUT &= ~BIT0;                                 // Clear P1.0 LED off
        }
        else{
          P1OUT |= BIT0;                                   // Set P1.0 LED on
        }
        __delay_cycles(5000);                   // Delay between transmissions
        
        DACval = ADC_Result;
        DACval <<= 2;  // (optional) 10-bit->12-bit range
        data_1 = (DACval >> 8) & 0xFF; // high byte (first)
        data_2 = (DACval >> 0) & 0xFF; // low byte (second)
        
        //while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B0_BASE));
        
    //        //convert the adc result to binary
    //    numbin = convertIntToBin(ADC_Result);
    //    
    //    //add the binary number to the numbinArray
    //    ptr_numbinArray = addToArray(numbin);
    //    i = 0;
    //    for (i = 0;i<=10; i++){ //to shift the binary 2 to the left make i = 2; and i<= 12;
    //      numbinArray[i] = *(ptr_numbinArray + i); 
    //    }
    //    
    //    //split numbinArray up to the two required bytes
    //    //byte 1
    //    i = 8;
    //    c = 7;
    //    for (i = 8; i<=11; i++){
    //      byte1[c] = numbinArray[i];
    //      c--;
    //    }
    //    
    //    //byte 2
    //    i = 0;
    //    c = 7;
    //    for (i = 0; i<=7; i++){ 
    //      byte2[c] = numbinArray[i];
    //      c--;
    //    }
    //    
    //    data1 = convertBinToInt(ptr_byte1);
    //    data2 = convertBinToInt(ptr_byte2);
        
            
        //while (EUSCI_B_I2C_SENDING_STOP == EUSCI_B_I2C_masterIsStopSent(EUSCI_B0_BASE));
        
            //while(EUSCI_B_I2C_SENDING_START == EUSCI_B_I2C_masterIsStartSent(EUSCI_B0_BASE));
    
        
        
        EUSCI_B_I2C_masterSendMultiByteStart(EUSCI_B0_BASE, data_1);
        EUSCI_B_I2C_masterSendMultiByteFinish(EUSCI_B0_BASE, data_2);
        byteNum++;
      }
    }
    
    
    
    // ADC interrupt service routine
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector = ADC_VECTOR
    __interrupt void ADC_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(ADC_VECTOR))) ADC_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
      switch(__even_in_range(ADCIV,ADCIV_ADCIFG))
      {
      case ADCIV_NONE:
        break;
      case ADCIV_ADCOVIFG:
        break;
      case ADCIV_ADCTOVIFG:
        break;
      case ADCIV_ADCHIIFG:
        break;
      case ADCIV_ADCLOIFG:
        break;
      case ADCIV_ADCINIFG:
        break;
      case ADCIV_ADCIFG:
        ADC_Result = ADCMEM0;
        __bic_SR_register_on_exit(LPM0_bits);          // Clear CPUOFF bit from LPM0
        break;
      default:
        break;
      }  
    }
    
    
    //#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    //#pragma vector=USCI_B0_VECTOR
    //__interrupt
    //#elif defined(__GNUC__)
    //__attribute__((interrupt(USCI_B0_VECTOR)))
    //#endif
    //void USCIB0_ISR(void)
    //{
    //  switch(__even_in_range(UCB0IV, USCI_I2C_UCBIT9IFG))
    //  {
    //  case USCI_NONE:             // No interrupts break;
    //    break;
    //  case USCI_I2C_UCALIFG:      // Arbitration lost
    //    break;
    //  case USCI_I2C_UCNACKIFG:    // NAK received (master only)
    //    //resend start if NACK
    //    EUSCI_B_I2C_masterSendStart(EUSCI_B0_BASE);
    //    break;
    //  case USCI_I2C_UCTXIFG0:     // TXIFG0
    //    // Check TX byte counter
    //    if (TXByteCtr)
    //    {
    //      EUSCI_B_I2C_masterSendMultiByteNext(EUSCI_B0_BASE, TXData);
    //      // Decrement TX byte counter
    //      TXByteCtr--;
    //    }
    //    else
    //    {
    //      EUSCI_B_I2C_masterSendMultiByteStop(EUSCI_B0_BASE);
    //      // Exit LPM0
    //      __bic_SR_register_on_exit(LPM0_bits);
    //    }
    //    break;
    //  default:
    //    break;
    //  }
    //}
    //
    

  • Hi Johnson. I replied to Bruce with my results below. Thank you for showing interest in my post.

  • Can you describe what you see on SDA? If your scope has only one probe, SCL may be more interesting at the moment.

    A failure to see a TXIFG at the beginning of a transaction typically indicates some very basic bus failure, preventing the master from generating a Start. The usual suspects are (1) the bus pullups -- you might check the "pullups" bridge on the bottom of the board for damage (2) the slave is holding SCL and/or SDA low -- in this case try power-cycling the slave (just disconnect/re-connect the Vcc pin).

  • I tried replicating this morning the test I made yesterday. Unfortunately, the results weren't the same. It keeps getting stuck at the

        //Poll for transmit interrupt flag.
        while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;

    for both functions and no useable readings were on the oscilloscope. I'll try again to connect everything for more tests soon and I'll let you know.

  • Further tests this afternoon concluded that it gets past the

        //Poll for transmit interrupt flag.
        while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;

    in the "EUSCI_B_I2C_masterSendMultiByteStart" function during the first cycle but it gets stuck at the same code in the "EUSCI_B_I2C_masterSendMultiByteFinish" function. If I comment out the code in the second function then it gets stuck in the same code in the first function. So the START condition might be getting sent but it can't continue to the second byte? And then I'm guessing because no STOP condition is sent, it messes up the programm because it's trying to send a START condition again. Any ways to solve this do you think?

    I did the power-cycling and checked the slave for any errors or problems and it all seemed fine or just as it always was.

  • Should I make a new thread more specific to my newer problem? Or keep this one and reply to comments?

  • Can you describe the scope traces (even the not-usable ones). The first thing of interest is: Is SCL high or low? How about SDA?

    When you get stuck at one of those loops, it's probably useful to look at the USCI_B0 status in the Registers view. UCB0CTL0/1, UCB0IFG, and UCB0STAT are points of interest.

  • Did some more tests today. SCL and SDA are at the same level on the oscilloscope which is at the supply voltage of the DAC. In this case it was 4 V. They do not seem to be oscillating at all and are constantly at that vlaue. However when I zoom in at SDA and speek in the microphone I can see something small happening. However, I don't know if that's random noise from the rest of the circuit and I'm interpereting it as such but small fluctuations seem to be happening.

    I will now describe what happened today. I logged the memory registers you mentioned and I noted where it gets stuck.

    Program starts
    Does one succesful cycle, goes through both functions
    Next cycle, gets stuck at

        //Poll for transmit interrupt flag.
        while (!(HWREG16(baseAddress + OFS_UCBxIFG) & UCTXIFG)) ;

    in the "EUSCI_B_I2C_masterSendMultiByteFinish" function, at line 401.
    If I comment that out and rerun it, the same thing happens but it gets stuck at the same code at line 408.
    If I comment that out and rerun it, it gets stuck in the same code snippet but in "EUSCI_B_I2C_masterSendMultiByteStart" during the third cycle.
    If I comment out all the problematic areas, the MSP430 ADC samples correctly but at some point the values always show at 0 on the watchlist. Disconnecting and reconnecting SDA & SCL fixes this.

    When it's stuck on a while loop the vlaues of the memory registers are:
    UCB0CTL0: 0x0F
    UCB0CTL1: 0x92
    UCB0STAT: Error
    UCBxTXBUF: Error
    UCB0IFG: 10

    When everything is commented and it's iterating they are:
    (rest are the same)
    UCB0IFG: 8

    Thank you again for taking the time to help.

  • I think it's significant that you succeed with the first transaction.  The only pacing restriction I see is Tbuf [Table 7-1], which is at worst <5 microseconds and it looks like you're giving it >5 milliseconds. I also wonder why you aren't seeing any bus activity on your scope.

    One fairly standard debugging technique is to slow the bus down. I don't have a hypothesis, but this can ease the effects of e.g. long-ish wires. Instead of EUSCI_B_I2C_SET_DATA_RATE_400KBPS, try using [...]100KBS. (This is defined to be the number 100000, so you could, e.g. substitute 50000 for 50kHz if you chose to.)

  • Okay I will try this and update you in a couple of days as the university is closed.

  • Hi Bruce. Nothing changed with decreasing the data rate. Should I try and use a byte counter or a timeout? Sometimes it iterates once and sometimes it gets stuck on the first iteration regardless of what reading it receives. It gets stuck at the same while loop as previously discussed. UCBxTXBUF is still displaying an error.

  • Unfortunately, I don't have your equipment so all I can do is guess.

    It sounds as though the first transaction is (somehow) leaving the bus in an inappropriate state. If you power-cycle the slave before each run, do you consistently succeed with the first transaction? What does your scope show in between?

    I suppose another experiment is to send (according to Fig 6-1) the first two bytes, then repeat those two bytes (total 4) as a single transaction. As I said, I'm not sure what that alternate form is supposed to do for you, but it's something to try.

    You're referring to the name "UCBxTXBUF", which doesn't (normally) exist. Are you getting these values from the "Registers" view? (UCB0TXBUF does exist, though it's not usually very useful in a case like this.)

  • Hi Bruce. I ended up going mny own route and usign some other functions. I can see some waves happening on the oscilloscope now with SDA. I ended just sending the data completely manually using this code:

        EUSCI_B_I2C_masterSendStart(EUSCI_B0_BASE);
        EUSCI_B_I2C_slavePutData(EUSCI_B0_BASE, data_0);   
        EUSCI_B_I2C_slavePutData(EUSCI_B0_BASE, data_2);
        EUSCI_B_I2C_slavePutData(EUSCI_B0_BASE, data_1);
        HWREG16(EUSCI_B0_BASE + OFS_UCBxCTLW0) |= UCTXSTP; //send stop sondition

    I have some questions that I would like clarified if you can. Do I have to manually send the the byte with the address and the R/W bit? The START bit is sent with line 1 from the snippet. In line 2, data_0 is 0xC0 to agree with the first byte of data that must be sent to the DAC (Figure 6-1), which also includes the address and the R/W bit. Is that necessary? Lines 3 and 4 are the rest of the data along with C0, C1, PD1 and PD0. The final line should be the STOP condition.

    Another question I have is what do I do with the ACK that gets sent after each byte? Is there a way I can check it's being received? Is there anything I can do with it?

    No output from the DAC so I'm guessing it has something to do with the way I'm sending it.

    Thank you again for trying to assist.

  • I took another pass over the DAC data sheet, and noticed Table 5-3, which suggests that, when the DAC first powers up, it should put out Vcc/2,  or about 1.7V. You said you see no output, which I take to mean 0V. If so, that suggests something is happening separate from the I2C communication. What is Vout connected to?

    The slavePutData function doesn't check for TXIFG, so, while it won't hang if TXIFG doesn't appear, there's a good chance the bytes are getting lost.

    To answer your questions:

    1) You don't send the address+R/W (SLA) byte, the EUSCI generates that for you.

    2) You don't get an explicit ACK indication. If the device  sends a NACK (really just the absence of an ACK), UCNACKIFG is set. [Ref User Guide (SLAU445I) Fig 24-12, roughly in the middle]

  • Thank you for answering my questions, it cleared a lot of things up. Would setting up an interrupt for UCNACKIFG be a good idea? I can check better what's happening with the DAC that way. Yes the output is ~Vcc/2, apologies, I meant to say there's no voltage fluctuation/activity in Vout. I will test again tomorrow and let you know.

  • You could enable for (only) UCNACKIE, and catch it in an ISR. Given the rest of the program, you wouldn't be able to fix anything, but you could set a breakpoint there and see if you Ever see a NACK. Your earlier reports didn't indicate the UCNACKIFG (0x20) was set.

    Are you able to post any of your scope waveforms?

  • I tried connecting your code with a very simple I2C slave (running on another Launchpad) I wrote for another project. In this experiment, your code seemed to run fine -- I stopped after 1200+ transactions.

    This also seems to point to something outside the code itself.  Have you checked your wiring recently? A photo of your setup might be useful.

  • Thank you for connecting it and doing all this. Did you use the EUSCI_B_I2C_slavePutData function or the other we were using before? I can send a picture of my set up no problem, right now we are busy soldering the PCBs for the circuit and once I have that I can connect it again to the MSP430 and let you know!

  • Hi Bruce. Fortunately I had the idea of using a timeout since I noticed it affects the part of the code that the program gets stuck in. So with the timeout it seems that it works in a way. I have some voltage coming from the DAC and it is in the picture of the oscilloscope bellow.

    From the pictures below you can see the setup. Everything is powered by battery packs (used a variable supply voltage before).

    Wires:
    white wire is SDA (from PIN5.2 to SDA of DAC)
    black wire is SCL (from PIN5.3 to SCL of DAC)
    brown is the gound from the mic circuit (goes into ground of MSP)
    red is the input voltage from mic circuit (goes into PIN8.1 for ADC)
    yellow is Vout from the DAC (attatched to oscilloscope probe)
    orange is DAC voltage supply
    remaining two wires are grounds

    I'm not sure why the timeout works and not the putData function, I'm guessing it's because it does some other things with the interrupts? But I'm not using interrupts so I that can't be it. Is the reason timeout is needed, that data isn't getting sent correctly per cycle? Is there a technical limitation do you think? I'm guessing we wouldn't hear what we actually spoke into the mic but some output would probably make some noise come out the other side.

  • The putData function doesn't check for TXIFG, so some or all of the bytes that you "put" will get lost.

    When you say "timeout" do you mean the "masterSendMultiByteXXXwithTimeout" variants? 

    Seeing something on Vout is encouraging, but it's hard to know whether it's right. One experiment would be a small code change to ignore the ADC result and just send a constant (maybe 4096/4, i.e. Vcc/4) every time and see if Vout reflects that. You might also consider disconnecting the microphone circuit entirely, to see if that's causing some electrical disturbance on the I2C.

  • Yes those are the variants I am using and that I got an output out of. I will check what kind of sound that makes out of the speaker end on Tuesday and also try the Vcc/4 test you suyggested. Thank you.

  • FINAL UPDATE ON THE ISSUE.

    submitted the project so i can't really do any more on it. The reason no voltage was detected on the speaker end was because the values that were read were way too small and did not induce enough voltage by the DAC for the speaker circuit. To solve that I multiplied the read value by 5 and that gave some voltage output. A timeout was always needed and I'm guessing that has something to do with the internal clocks and interrupts of the MSP430FR4133 not being able to process the ADC input and I2C output at the same time. Or maybe I didn't set the correct clocks for the correct purpose. I don't really know but the general idea of the project works now. So I will mark my post as solved.

**Attention** This is a public forum