Jump to content
43oh

I2C on a MSP430F2003


Recommended Posts

Hey all,

I've been staring at this for a while and for the life of me I can't figure it out. I've been trying to adapt the library here:
http://jan.rychter.com/enblog/msp430-i2c-usi-library-released
for the F2003, which should be rather straightforward, as most of the register names and functions are similar. However, I'm able to get it to build and compile, but when the code gets around to the first send command, it goes into low power mode and never actually sends it. The pins are set, the LPM0 bits are correct, the interrupt vector name is correct, and a number of other things seem to go as anticipated. It just never actually bothers to start sending the data.

 

I have also tried this with TI sample code, with the exact same results.

I don't have a logic analyzer, but from hooking an LED up to one of the pins (it's not on there when I actually attempt communication), I can be almost certain that it never attempts to send data. It appears both SDA and SCL are kept low the entire time.

Thank you, and any help is appreciated.

 

 

Here's main.cpp

#include <msp430.h> 
#include "usi_i2c.h"


//define addresses
#define MAG_ADDR 0xE
/*
 * main.c
 */

int main(void) {
    WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
    BCSCTL1 = CALBC1_1MHZ;                    // Set DCO
    DCOCTL = CALDCO_1MHZ;


    __enable_interrupt();
    //some sanity checking
    P1DIR |= BIT0;
    P1OUT |= BIT0;
    P1OUT &= ~(BIT0);


    //init i2c
    SD16AE &= ~(BIT6 | BIT7);
    i2c_init(USIDIV_5, USISSEL_2);


    uint8_t stat = 0;
    uint16_t MAG3110_INIT_SEQ1[] = {0x1C, 0x11, 0xFF};
    uint16_t MAG3110_INIT_SEQ2[] = {0x1C, 0x10, 0x81};
    uint16_t MAG3110_READZ[] = {0x1C, 0x02, I2C_RESTART, 0x1D, I2C_READ};


    i2c_send_sequence(MAG3110_INIT_SEQ1,3,&stat,LPM0_bits);
    LPM0;
    _NOP();


    i2c_send_sequence(MAG3110_INIT_SEQ2,3,&stat,LPM0_bits);
    LPM0;


    while (1)
       {
        P1OUT ^= BIT0;
        int i;
        for  (i=0; i<0x3000; i++);
       }


}

and here's the slightly modified usi_i2c.cpp thanks to Jan Rychter

/*
  usi_i2c.c


  Copyright (C) 2013 Jan Rychter
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
*/


#include <msp430.h>
#include <stdint.h>
#include "usi_i2c.h"


// Internal state
static uint16_t *i2c_sequence;
static uint16_t i2c_sequence_length;
static uint8_t *i2c_receive_buffer;
static uint16_t i2c_wakeup_sr_bits;
i2c_state_type i2c_state = I2C_IDLE;


static inline void i2c_prepare_stop();
static inline void i2c_prepare_data_xmit_recv();


void i2c_send_sequence(uint16_t *sequence, uint16_t sequence_length, uint8_t *received_data, uint16_t wakeup_sr_bits) {
  while(i2c_state != I2C_IDLE); // we can't start another sequence until the current one is done
  i2c_sequence = sequence;
  i2c_sequence_length = sequence_length;
  i2c_receive_buffer = received_data;
  i2c_wakeup_sr_bits = wakeup_sr_bits;
  i2c_state = I2C_START;
  USICTL1 |= USIIFG;            // actually start communication
}


static inline void i2c_prepare_stop() {
  USICTL0 |= USIOE;             // SDA = output
  USISRL = 0x00;
  USICNT |=  0x01;              // Bit counter= 1, SCL high, SDA low
  i2c_state = I2C_STOP;
}


static inline void i2c_prepare_data_xmit_recv() {
  if(i2c_sequence_length == 0) {
    i2c_prepare_stop();         // nothing more to do, prepare to send STOP
  } else {
    if(*i2c_sequence == I2C_RESTART) {
      USICTL0 |= USIOE;         // SDA = output
      USISRL = 0xff;            // prepare and send a dummy bit, so that SDA is high
      USICNT = (USICNT & 0xE0) | 1;
      i2c_state = I2C_START;
    }
    else if(*i2c_sequence == I2C_READ) {
      USICTL0 &= ~USIOE;               // SDA = input
      USICNT = (USICNT & 0xE0) | 8;    // Bit counter = 8, RX data
      i2c_state = I2C_RECEIVED_DATA;   // next state: Test data and ACK/NACK
    } else {                           // a write
      // at this point we should have a pure data byte, not a command, so (*i2c_sequence >> 8) == 0
      USICTL0 |= USIOE;                // SDA = output
      USISRL = (char)(*i2c_sequence);  // Load data byte
      USICNT = (USICNT & 0xE0) | 8;    // Bit counter = 8, start TX
      i2c_state = I2C_PREPARE_ACKNACK; // next state: prepare to receive data ACK/NACK
    }
    i2c_sequence++;
    i2c_sequence_length--;
  }
}


#pragma vector = USI_VECTOR
__interrupt void USI_TXRX(void)
{
  switch(__even_in_range(i2c_state,12)) {
  case I2C_IDLE:
    break;


  case I2C_START:               // generate start condition
    USISRL = 0x00;
    USICTL0 |= (USIGE|USIOE);
    USICTL0 &= ~USIGE;
    i2c_prepare_data_xmit_recv();
    break;


  case I2C_PREPARE_ACKNACK:      // prepare to receive ACK/NACK
    USICTL0 &= ~USIOE;           // SDA = input
    USICNT |= 0x01;              // Bit counter=1, receive (N)Ack bit
    i2c_state = I2C_HANDLE_RXTX; // Go to next state: check ACK/NACK and continue xmitting/receiving if necessary
    break;


  case I2C_HANDLE_RXTX:         // Process Address Ack/Nack & handle data TX
    if((USISRL & BIT0) != 0) {  // did we get a NACK?
      i2c_prepare_stop();
    } else {
      i2c_prepare_data_xmit_recv();
    }
    break;


  case I2C_RECEIVED_DATA:       // received data, send ACK/NACK
    *i2c_receive_buffer = USISRL;
    i2c_receive_buffer++;
    USICTL0 |= USIOE;           // SDA = output
    if(i2c_sequence_length > 1) {
      // If this is not the last byte
      USISRL = 0x00;                // ACK
      i2c_state = I2C_HANDLE_RXTX;  // Go to next state: data/rcv again
    } else {                        // last byte: send NACK
      USISRL = 0xff;                // NACK
      i2c_state = I2C_PREPARE_STOP; // stop condition is next
    }
    USICNT |= 0x01;             // Bit counter = 1, send ACK/NACK bit
    break;


  case I2C_PREPARE_STOP:        // prepare stop condition
    i2c_prepare_stop();         // prepare stop, go to state 14 next
    break;


  case I2C_STOP:                // Generate Stop Condition
    USISRL = 0x0FF;             // USISRL = 1 to release SDA
    USICTL0 |= USIGE;           // Transparent latch enabled
    USICTL0 &= ~(USIGE+USIOE);  // Latch/SDA output disabled
    i2c_state = I2C_IDLE;       // Reset state machine for next xmt
    if(i2c_wakeup_sr_bits) {
      _bic_SR_register_on_exit(i2c_wakeup_sr_bits); // exit active if prompted to
    }
    break;
  }
  USICTL1 &= ~USIIFG;           // Clear pending flag
}


void i2c_init(uint16_t usi_clock_divider, uint16_t usi_clock_source) {
  _disable_interrupts();
  USICTL0 = USIPE6+USIPE7+USIMST+USISWRST;  // Port & USI mode setup
  USICTL1 = USII2C+USIIE;                   // Enable I2C mode & USI interrupt
  USICKCTL = usi_clock_divider + usi_clock_source + USICKPL;
  USICNT |= USIIFGCC;                       // Disable automatic clear control
  USICTL0 &= ~USISWRST;                     // Enable USI
  USICTL1 &= ~USIIFG;                       // Clear pending flag
  _enable_interrupts();
}
Edited by waterlogged
Link to post
Share on other sites
  • 2 weeks later...

BlueHash, thanks for the bump. I'm always reluctant to do it myself...

 

Roadrunner, yep. I've tried a couple of values of pull-up resistor, and have left 4.7k resistors in. I have the circuit working using a parallax propeller board. In the time since I've posted, I also tried creating my own i2c code, and the chip hangs in the same place; right when I tell it to start transferring and start waiting for the interrupt saying it is finished.

 

Any ideas for tests or fixes would be extremely appreciated.

Link to post
Share on other sites

I have been frustrated so much by the interrupt drive iic master side that I now just bit bang the whole stinking thing - no interrupts at all.

 

Do you have the Davies book? It will cover the 200x series iic port with sample code.

 

Do you have a logic analyzer? It would be nice to see the waveforms on the clock and data pins.

 

Can you single step debug your way through the code and examine the registers for expected values?

 

I do not see where your code looks for ACK bits from the slave. Maybe I am blind tonight.

Link to post
Share on other sites

Zeke,

 

I don't have the book, but I'll see if I can grab a copy. Unfortunately, no logic analyzer either. 

 

I have stepped through the code a couple of times. It goes into this call:

 

i2c_send_sequence(MAG3110_INIT_SEQ1,3,&stat,LPM0_bits);

 

And the switch case in usi_i2c.cpp goes through the first case and the second case ( I2C_START )  and freezes immediately after. If I pause while its frozen, it comes out at the call to go into the LPM0 call in main.

Link to post
Share on other sites

Well, my intuition tells me to check the following:

1. Verify the correct iic address for the slave is being sent.

2. Examine the operation of the state machine by single stepping through it in debug mode

3. Examine the operation of the state machine helper functions by putting breakpoints at key decision lines of code.

 

I immediately want to check out the operation of i2c_prepare_data_xmit_recv() to see what it is doing. Is it getting a stop condition right away?

 

Next, I would verify the voltage levels of the SDA and SCL lines when I think the code has frozen. Just use a multimeter to see what voltage they are at. If the bus has frozen then their states will give us clues why.

 

A logic analyzer can be had for little money these days. Seeedstudio sells the DangerousPrototypes version at an affordable price. Check aliexpress too.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...