Quantcast
Channel: MSP low-power microcontroller forum - Recent Threads
Viewing all articles
Browse latest Browse all 21927

CCS/MSP430F2112: I2C Reads sometimes return 0x00

$
0
0

Part Number:MSP430F2112

Tool/software: Code Composer Studio

Hi All,

I'm writing an interrupt driven MSP430 I2C Slave that features a realt-time clock and battery monitor for logging purposes. It seems to work "most of the time" but occasionally when I'm reading large bursts of data some registers return zero even though they have valid data. I'm running main loop from DCO at 1MHz. If I increase DCO speed to 8, 12 or 16MHz I always get zeros....???

I've implemented a bunch of registers that can be read or written over I2C.

At the same time, I'm also using LFXTAL to drive a timer to generate an accurate 0.5s tick from a connected 32.768kHz xtal.

Any ideas what might be going on?

Code below.

Cheers,

Mike.

#include <msp430.h>

#define ON    1
#define OFF    0
#define TRUE    1
#define FALSE    0


typedef unsigned char REG_SUBADDR_TYPE;
typedef unsigned char bool;

#define REG_STATUS            (REG_SUBADDR_TYPE) 0x00
#define REG_CONTROL1        (REG_SUBADDR_TYPE) 0x01
#define REG_CONTROL2        (REG_SUBADDR_TYPE) 0x02
#define REG_BATTERY_HIGH    (REG_SUBADDR_TYPE) 0x03
#define REG_BATTERY_LOW        (REG_SUBADDR_TYPE) 0x04
#define REG_SW_VERSION        (REG_SUBADDR_TYPE) 0x05
#define REG_YEAR            (REG_SUBADDR_TYPE) 0x06
#define REG_MONTH            (REG_SUBADDR_TYPE) 0x07
#define REG_DATE            (REG_SUBADDR_TYPE) 0x08
#define REG_HOUR            (REG_SUBADDR_TYPE) 0x09
#define REG_MINUTE            (REG_SUBADDR_TYPE) 0x0A
#define REG_SECOND            (REG_SUBADDR_TYPE) 0x0B
#define REG_ALARM_YEAR        (REG_SUBADDR_TYPE) 0x0C
#define REG_ALARM_MONTH        (REG_SUBADDR_TYPE) 0x0D
#define REG_ALARM_DATE        (REG_SUBADDR_TYPE) 0x0E
#define REG_ALARM_HOUR        (REG_SUBADDR_TYPE) 0x0F
#define REG_ALARM_MINUTE    (REG_SUBADDR_TYPE) 0x10
#define REG_ALARM_SECONDS    (REG_SUBADDR_TYPE) 0x1A


typedef enum
{
    IIC_SLAVE_IDLE,
    IIC_SLAVE_RXED_SLAVE_ADDR,
    IIC_SLAVE_SLAVE_TX_BLOCK,
    IIC_SLAVE_SLAVE_RX_BLOCK
} iic_slave_state_type;

static iic_slave_state_type    iic_slave_state;    //I2C State
REG_SUBADDR_TYPE             iic_reg_address;    // Current register pointer


static unsigned char    battery_level_high = 0x00;
static unsigned char    battery_level_low = 0x00;

static unsigned int    reg_year = 17;
static unsigned int    reg_month = 2;
static unsigned int    reg_date = 28;
static unsigned int    reg_hours = 15;
static unsigned int    reg_minutes = 30;
static unsigned int    reg_seconds = 30;

static unsigned int    reg_alarm_year = 17;
static unsigned int    reg_alarm_month = 2;
static unsigned int    reg_alarm_date = 28;
static unsigned int    reg_alarm_hours = 16;
static unsigned int    reg_alarm_minutes = 30;
static unsigned int    reg_alarm_seconds = 30;


// Declare function prototypes
void delay ( unsigned int );
void Update_Half_Seconds(void);
void Update_Clock_24hr(void);
void Update_Display(void);
void Update_Mode(void);
void Check_Alarm(void);
void Check_Battery(void);
int REG_read_register(REG_SUBADDR_TYPE);
int REG_write_register(REG_SUBADDR_TYPE, int);
REG_SUBADDR_TYPE REG_inc_register(REG_SUBADDR_TYPE);


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

    if (CALBC1_1MHZ ==0xFF || CALDCO_1MHZ == 0xFF)
    {
      while(1);                               // Halt if calibration constants erased

    }
    //1Mhz
    BCSCTL1 = CALBC1_1MHZ;              // Set range
    DCOCTL = CALDCO_1MHZ;               // Set DCO step + modulation */

    //BCSCTL2 |= DIVS1 + DIVS0;            // SMCLK /8 divider
    BCSCTL3 = 0x0C;                        // 32.768kHz, 12.5pF Load Caps

    P3SEL |= 0x06;                        // Assign I2C pins to USCI_B0
    UCB0CTL1 |= UCSWRST;    // Hold USCI Logic in Reset State, SMCLK
    UCB0CTL0 = UCMODE_3 + UCSYNC;        // USCI=I2C, Synchronous mode
    UCB0I2COA = 0x18;                    // Own Address is 0x18
    UCB0CTL1 &= ~UCSWRST;                // Release USCI Logic Reset
    UCB0I2CIE |= UCSTPIE + UCSTTIE;        // Enable STT and STP interrupt
    IE2 |= UCB0RXIE + UCB0TXIE;            // Enable RX & TX interrupts
    iic_slave_state = IIC_SLAVE_IDLE;    // initialise I2C State Machine

    P1DIR = 0x3D;
    P1OUT = 0x00;

    P2DIR = 0x20;                        //P2.5 as OP
    P2IE |= 0x18;                        // P2.3, P2.4 Interrupt enabled
    P2IES |= 0x18;                        // Hi/lo edge
    P2IFG &= ~0x18;                        // IFG cleared
    P2OUT |= BIT5;                        //DEBUG - Switch on 5V_AUX_EN to enable DC-DC

    P3DIR |= 0xF9;
    P3OUT = 0x00;

    TA1CTL = TASSEL_1 + MC_1;            // ACLK (32.678kHz), upmode
    TA1CCR0 = 0x3FFF;                    // 3FFF equates to 0.5s at 32.768kHz
    TA1CCTL0 = CCIE;                    // TACCR0 interrupt enabled

    __enable_interrupt();
    //__bis_SR_register(CPUOFF + GIE);        // Enter LPM0 w/ interrupts

    while(1);
}


//------------------------------------------------------------------------------
// I2C Bus State-Change Interrupts are handled here
// Tx IFG is cleared when UCB0TXBUF is loaded with a byte
// Rx IFG is cleared upon reading UCB0RXBUF
//------------------------------------------------------------------------------
#pragma vector = USCIAB0TX_VECTOR
__interrupt void USCIAB0TX_ISR(void)
{
  if (IFG2 & UCB0RXIFG)    // Master is writing to us
  {
      if (iic_slave_state == IIC_SLAVE_IDLE)            //If we were idle will be register host wants to read or write
      {
          iic_reg_address = UCB0RXBUF;                    //Will be Sub-Address
          iic_slave_state = IIC_SLAVE_RXED_SLAVE_ADDR;    //Move to next state
      }
      else if (iic_slave_state == IIC_SLAVE_RXED_SLAVE_ADDR)
      {
          REG_write_register(iic_reg_address, UCB0RXBUF);
          iic_reg_address = (REG_SUBADDR_TYPE) REG_inc_register(iic_reg_address);     //Calculate next address
      }
  }
  else    //Else, Master wants to read data
  {
      UCB0TXBUF = REG_read_register(iic_reg_address);                            //Load the contents of previously set sub-address
      iic_reg_address = (REG_SUBADDR_TYPE) REG_inc_register(iic_reg_address);     //Calculate next address
  }
}


//------------------------------------------------------------------------------
// I2C Bus State-Change Interrupts are handled here
//------------------------------------------------------------------------------
#pragma vector = USCIAB0RX_VECTOR
__interrupt void USCIAB0RX_ISR(void)
{
    if (UCB0STAT & UCSTPIFG) //If STOP INT
    {
        iic_slave_state = IIC_SLAVE_IDLE;
        iic_reg_address = 0x00;
        UCB0STAT &= ~UCSTPIFG;
    }

    else if (UCB0STAT & UCSTTIFG) //If START INT
    {
        UCB0STAT &= ~UCSTTIFG;
    }
}



void delay(unsigned int ms)
{
 while (ms--)
    {
        __delay_cycles(1000); // set for 16Mhz change it to 1000 for 1 Mhz
    }
}



// Timer1_A0 interrupt service routine
#pragma vector=TIMER1_A0_VECTOR
__interrupt void Timer1_A0 (void)
{
  Update_Half_Seconds();
}



/*This function should get called every 1/512th of a second from an interupt*/
/*All functions inside this interupt should be completed within 1ms and therefore interupts are not disabled!*/
void Update_Half_Seconds(void)
{
    static char half_seconds = 0;

    P3OUT ^= BIT6;    //DEBUG: Flash Green LED

    half_seconds++;

    if (half_seconds>=2)
    {
        Update_Clock_24hr();
        half_seconds=0;
    }
}



/*This is the only function where the actual real-time is updated*/
/*This function should be called once every second*/
void Update_Clock_24hr(void)
{
    reg_seconds++;
    Check_Alarm();    //Check for alarm if bit set

    if (reg_seconds >= 60)
    {
        reg_seconds =0;        //Reset reg_seconds

        reg_minutes++;

        if (reg_minutes >= 60)
        {
            reg_minutes = 0;
            reg_hours++;
        }
        if (reg_hours >= 24)
        {
            reg_hours = 0;
        }

        Check_Battery();    //Battery voltage is updated every minute
    }
}


/*This function is called every time the reg_minutes counter is updated
 *
 */
void Check_Battery(void)
{
    static long ADC_Temp = 0.0;

    ADC10CTL1 = INCH_1;                                              //CH1
    ADC10CTL0 = SREF_1 + ADC10SHT_2 + REFON + REF2_5V + ADC10ON;    //VREF, 16 ADC CLK, 2.5V REF ON,
    P3OUT|= BIT3;                                                    //Switch on battery ADC path
    delay(5);                                                        //Settling time
    ADC10CTL0 |= ENC + ADC10SC;                                     //Sampling and conversion start
    while (ADC10CTL1 & BUSY);                                        //Wait for ADC10 to finish
    ADC_Temp = ADC10MEM;                                            //Assigns the value held in ADC10MEM
    P3OUT &= ~BIT3;                                                    //Switch off battery ADC path

    battery_level_low = (ADC_Temp & 0x00FF);        //Store Lower Bits
    battery_level_high = (ADC_Temp >> 8);            //Shift right and store Upper bits

    if (ADC_Temp >= 302)                                 //4.2V
    {
        ;
    }
    else if ( (ADC_Temp >= 259) && (ADC_Temp <= 301 ) ) //3.6V-->4.2V
    {
        ;
    }
    else if ( (ADC_Temp >= 223) && (ADC_Temp <= 258) ) //3.1V-->3.6V
    {
        ;
    }
    else if ( ADC_Temp <= 216)                            //<3.1V
    {
        ;
    }
}



/*This function is called every time the reg_minutes counter is updated
 *
 */
void Check_Alarm(void)
{
    if ( (reg_hours == reg_alarm_hours) && (reg_minutes == reg_alarm_minutes) &&
          (reg_seconds == reg_alarm_seconds))
    {
        ;
    }
}


/*I2C READ Register Function
 *
 */
int REG_read_register(REG_SUBADDR_TYPE    subaddr)
{
    switch (subaddr)
    {
        case REG_BATTERY_LOW:
            return battery_level_low;

        case REG_BATTERY_HIGH:
            return battery_level_high;

        case REG_SW_VERSION:
            return 0x01;

        case REG_YEAR:
            return reg_year;

        case REG_MONTH:
            return reg_month;

        case REG_DATE:
            return reg_date;

        case REG_HOUR:
            return reg_hours;

        case REG_MINUTE:
            return reg_minutes;

        case REG_SECOND:
            return reg_seconds;

        case REG_ALARM_YEAR:
            return reg_alarm_year;

        case REG_ALARM_MONTH:
            return reg_alarm_month;

        case REG_ALARM_DATE:
            return reg_alarm_date;

        case REG_ALARM_HOUR:
            return reg_alarm_hours;

        case REG_ALARM_MINUTE:
            return reg_alarm_minutes;

        case REG_ALARM_SECONDS:
            return reg_alarm_seconds;

        default:
            return 0;
    }
}


/* I2C WRITE Register Function
 *
 */
int REG_write_register(REG_SUBADDR_TYPE subaddr, int value)
{
    switch (subaddr)
    {
        case REG_BATTERY_LOW:
            return FALSE;

        case REG_BATTERY_HIGH:
            return FALSE;

        case REG_SW_VERSION:
            return FALSE;

        case REG_YEAR:
            reg_year = value;
            return TRUE;

        case REG_MONTH:
            reg_month = value;
            return TRUE;

        case REG_DATE:
            reg_date = value;
            return TRUE;

        case REG_HOUR:
            reg_hours = value;
            return TRUE;

        case REG_MINUTE:
            reg_minutes = value;
            return TRUE;

        case REG_SECOND:
            reg_seconds = value;
            return TRUE;

        case REG_ALARM_YEAR:
            reg_alarm_year = value;
            return TRUE;

        case REG_ALARM_MONTH:
            reg_alarm_month = value;
            return TRUE;

        case REG_ALARM_DATE:
            reg_alarm_date = value;
            return TRUE;

        case REG_ALARM_HOUR:
            reg_alarm_hours = value;
            return TRUE;

        case REG_ALARM_MINUTE:
            reg_alarm_minutes = value;
            return TRUE;

        case REG_ALARM_SECONDS:
            reg_alarm_seconds = value;
            return TRUE;

        default:
            return FALSE;
    }
}


/* Determine next I2C register in a group
 *
 */
REG_SUBADDR_TYPE REG_inc_register(REG_SUBADDR_TYPE subaddr)
{
    switch(subaddr)
    {
        case 0x00: return(REG_SUBADDR_TYPE)(0x03);  // Reserved
        case 0x01: return(REG_SUBADDR_TYPE)(0x03);  // Reserved
        case 0x02: return(REG_SUBADDR_TYPE)(0x03);  // Reserved

        case 0x03: return(REG_SUBADDR_TYPE)(0x04);  // Battery High
        case 0x04: return(REG_SUBADDR_TYPE)(0x03);  // Battery Low

        case 0x05: return(REG_SUBADDR_TYPE)(0x05);  // FW Version

        case 0x06: return(REG_SUBADDR_TYPE)(0x07);  // Year
        case 0x07: return(REG_SUBADDR_TYPE)(0x08);  // Month
        case 0x08: return(REG_SUBADDR_TYPE)(0x09);  // Date
        case 0x09: return(REG_SUBADDR_TYPE)(0x0A);  // Hour
        case 0x0A: return(REG_SUBADDR_TYPE)(0x0B);  // Minute
        case 0x0B: return(REG_SUBADDR_TYPE)(0x06);  // Second

        case 0x0C: return(REG_SUBADDR_TYPE)(0x0D);  // Alarm_Year
        case 0x0D: return(REG_SUBADDR_TYPE)(0x0E);  // Alarm_Month
        case 0x0E: return(REG_SUBADDR_TYPE)(0x0F);  // Alarm_Date
        case 0x0F: return(REG_SUBADDR_TYPE)(0x10);     // Alarm_Hour
        case 0x10: return(REG_SUBADDR_TYPE)(0x11);     // Alarm_Minute
        case 0x11: return(REG_SUBADDR_TYPE)(0x0C);     // Alarm_Second
     }
     return(REG_SUBADDR_TYPE)(subaddr);  /* Else we return the same register! */
}




Viewing all articles
Browse latest Browse all 21927

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>