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.

I2C communication MSP430G2432 Slave with Stellaris LM3S Master

Other Parts Discussed in Thread: MSP430G2230, MSP430G2432, LM3S6965

Hi all,

I have been trying to implement some code that takes a reading from the ADC10 on the MSP430 (Slave) and then transmits this over I2C to a Stellaris (Master).

The problem I am facing is the MSP430 code I am using is setup to only send 1 byte, but as I understand it the ADC value I want to send is an Integer, so 2 bytes.

The code basically takes 10 readings from the ADC and places them in an array, adds them together and divides by 10, then this is placed in the variable avg_adc.  I also use an itoa function to turn the ADC value into a char string (this was purely created to try and send over I2C as not fully understanding the problem, hence this post).

The Stellaris code is copied after the MSP430 code, I appreciate this is not the correct form for this but gives and overview of the whole handshaking process.  Also the code on the MSP430 will eventually be ported across to the MSP430G2230.

MSP430G CODE

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

// MSP430G2xx2 Demo - I2C Slave Transmitter, single byte

//

// Description: I2C Slave communicates with I2C Master using

// the USI. Slave data is sent and increments from 0x00 with each transmitted

// byte which is verified by the Master.

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

// MSP430G2xx2 Demo - I2C Slave Transmitter, single byte

//

// Description: I2C Slave communicates with I2C Master using

// the USI. Slave data is sent and increments from 0x00 with each transmitted

// byte which is verified by the Master.

// LED off for address or data Ack; LED on for address or data NAck.

// ACLK = n/a, MCLK = SMCLK = Calibrated 1MHz

//

// ***THIS IS THE SLAVE CODE***

//

// Slave Master

// (MSP430G2xx2_usi_06.c)

// MSP430G2xx2 MSP430G2xx2

// ----------------- -----------------

// /|\| XIN|- /|\| XIN|-

// | | | | | |

// --|RST XOUT|- --|RST XOUT|-

// | | | |

// LED <-|P1.0 | | |

// | | | P1.0|-> LED

// | SDA/P1.7|------->|P1.7/SDA |

// | SCL/P1.6|<-------|P1.6/SCL |

//

// Note: internal pull-ups are used in this example for SDA & SCL

//

// D. Dang

// Texas Instruments Inc.

// December 2010

// Built with CCS Version 4.2.0 and IAR Embedded Workbench Version: 5.10

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

#include <msp430g2432.h>

#include "ExtFunc/itoa.h"

unsigned int SLV_Data = avg_adc;

char SLV_Addr = 0x90; // Address is 0x48<<1 for R/W

int I2C_State = 0; // State variable

//////////////////////

int adc[10] = 0;

unsigned int avg_adc = 0;

void adc_Setup() {

ADC10CTL1 = CONSEQ_2 + INCH_1; // Repeat single channel, A1

ADC10CTL0 = ADC10SHT_1 + MSC + ADC10ON + ADC10IE; // Sample & Hold Time + ADC10 ON + Interrupt Enable;

ADC10DTC1 = 0x0A; // 10 conversions

ADC10AE0 |= 0x01; // P1.0 ADC option select

}

void adc_Sam10() {

ADC10CTL0 &= ~ENC; // Disable Conversion

while (ADC10CTL1 & BUSY); // Wait if ADC10 busy

ADC10SA = (int) adc; // Transfers data to next array (DTC auto increments address)

ADC10CTL0 |= ENC + ADC10SC; // Enable Conversion and conversion start

}

//////////////////////

int main(void) {

WDTCTL = WDTPW + WDTHOLD; // Stop watchdog

if (CALBC1_1MHZ == 0xFF) // If calibration constants erased

{

while (1)

; // do not load, trap CPU!!

}

DCOCTL = 0; // Select lowest DCOx and MODx settings

BCSCTL1 = CALBC1_1MHZ; // Set DCO

DCOCTL = CALDCO_1MHZ;

P1OUT = 0xC0; // P1.6 & P1.7 Pullups

P1REN |= 0xC0; // P1.6 & P1.7 Pullups

//P1DIR = 0xFF; // Unused pins as outputs

P2OUT = 0;

P2DIR = 0xFF;

USICTL0 = USIPE6 + USIPE7 + USISWRST; // Port & USI mode setup

USICTL1 = USII2C + USIIE + USISTTIE; // Enable I2C mode & USI interrupts

USICKCTL = USICKPL; // Setup clock polarity

USICNT |= USIIFGCC; // Disable automatic clear control

USICTL0 &= ~USISWRST; // Enable USI

USICTL1 &= ~USIIFG; // Clear pending flag

_EINT();

while (1) {

//LPM0; // CPU off, await USI interrupt

_NOP(); // Used for IAR

////////////////////////

adc_Setup();

adc_Sam10();

// Add all the sampled data and divide by 10 to find average

avg_adc = ((adc[0] + adc[1] + adc[2] + adc[3] + adc[4] + adc[5] + adc[6] + adc[7] + adc[8] + adc[9]) / 10);

////////////////////////

int base = 10; // Sets the base of the conversion to10 i.e. Decimal

unsigned char buffer[4]; // Buffer stores the string

itoa(avg_adc, buffer, base); // Itoa function call with 3 parameter

//ADC10 interrupt service routine

#pragma vector=ADC10_VECTOR

__interrupt void ADC10_ISR(void) {

__bic_SR_register_on_exit(CPUOFF);

// Clear CPUOFF bit from 0(SR)

/////////////////////////

}

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

// USI interrupt service routine

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

#pragma vector = USI_VECTOR

__interrupt void USI_TXRX(void) {

if (USICTL1 & USISTTIFG) // Start entry?

{

P1OUT |= 0x01; // LED on: Sequence start

I2C_State = 2; // Enter 1st state on start

}

switch (I2C_State) {

case 0: //Idle, should not get here

break;

case 2: //RX Address

USICNT = (USICNT & 0xE0) + 0x08; // Bit counter = 8, RX Address

USICTL1 &= ~USISTTIFG; // Clear start flag

I2C_State = 4; // Go to next state: check address

break;

case 4: // Process Address and send (N)Ack

if (USISRL & 0x01) // If read...

SLV_Addr++; // Save R/W bit

USICTL0 |= USIOE; // SDA = output

if (USISRL == SLV_Addr) // Address match?

{

USISRL = 0x00; // Send Ack

P1OUT &= ~0x01; // LED off

I2C_State = 8; // Go to next state: TX data

} else {

USISRL = 0xFF; // Send NAck

P1OUT |= 0x01; // LED on: error

I2C_State = 6; // Go to next state: prep for next Start

}

USICNT |= 0x01; // Bit counter = 1, send (N)Ack bit

break;

case 6: // Prep for Start condition

USICTL0 &= ~USIOE; // SDA = input

SLV_Addr = 0x90; // Reset slave address

I2C_State = 0; // Reset state machine

break;

case 8: // Send Data byte

USICTL0 |= USIOE; // SDA = output

USISRL = SLV_Data; // Send data byte

USICNT |= 0x08; // Bit counter = 8, TX data

I2C_State = 10; // Go to next state: receive (N)Ack

break;

case 10: // Receive Data (N)Ack

USICTL0 &= ~USIOE; // SDA = input

USICNT |= 0x01; // Bit counter = 1, receive (N)Ack

I2C_State = 12; // Go to next state: check (N)Ack

break;

case 12: // Process Data Ack/NAck

if (USISRL & 0x01) // If Nack received...

{

P1OUT |= 0x01; // LED on: error

} else // Ack received

{

P1OUT &= ~0x01; // LED off

//SLV_Data++; // Increment Slave data

}

// Prep for Start condition

USICTL0 &= ~USIOE; // SDA = input

SLV_Addr = 0x90; // Reset slave address

I2C_State = 0; // Reset state machine

break;

}

USICTL1 &= ~USIIFG; // Clear pending flags

}

STELLARIS LM3S6965 CODE

#include "inc/lm3s6965.h"

#include "inc/hw_types.h"

#include "driverlib/debug.h"

#include "driverlib/sysctl.h"

#include "drivers/rit128x96x4.h"

#include "inc/hw_memmap.h"

#include "driverlib/systick.h"

//#include "utils/ustdlib.h"

#include "driverlib/i2c.h"

#include "inc/hw_i2c.h"

#include "driverlib/gpio.h"

#include "itoa.h"

char buff[6]={0};

const char datos[8]={0};

unsigned long i2cdata = 0;

void Setup_i2c_HW()

{

// Turn on I2C0 and Reset to a known state

SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);

// Configure the PortB pins for I2C0

SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

// Enable Port B of GPIO

// SCL 0 Bit 2 Output

// SDA 0 Bit 3 Output

GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_2 | GPIO_PIN_3);

}

#ifdef DEBUG

void

__error__(char *pcFilename, unsigned long ulLine)

{}

#endif

int main(void)

{

// Setup clock for 8Mhz

SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_8MHZ);

Setup_i2c_HW();

// Initialize the OLED display.

RIT128x96x4Init(1000000);

// Print a string on the OLED

RIT128x96x4StringDraw("", 30, 30, 25); // Arguments Hor axis, Vert axs, 0-15 grey scale value

// This function initialises operation of the I2C Master block by configuring

// the bus speed for the master and enabling the I2C Master block.

// Param - ulBase = I2C0_MASTER_BASE - Enable and Initialise Master module

// Param - ulI2CClk = SysCtlClockGet() - Gets and returns the processor clock rate

//SysCtlClockSet() must be used and a supported clock rate for the I2C peripheral.

// Param - bFast = false - I2C Master Clk set to 100kbps.

I2CMasterInitExpClk(I2C0_MASTER_BASE, SysCtlClockGet(), false);

while(1)

{

I2CMasterSlaveAddrSet(I2C0_MASTER_BASE, 0x48, true);

I2CMasterControl(I2C0_MASTER_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);

while(I2CMasterBusy(I2C0_MASTER_BASE));

i2cdata=I2CMasterDataGet(I2C0_MASTER_BASE);

//

volatile unsigned long ulLoop;

//ic2_read_byte();

//

for(ulLoop = 0; ulLoop < 200000; ulLoop++)

{}

itoa(i2cdata, buff, 10);

RIT128x96x4StringDraw (buff, 30, 34, 15);

//RIT128x96x4StringDraw (i2cdata, 30, 34, 15);

//

for(ulLoop = 0; ulLoop < 200000; ulLoop++)

{}

RIT128x96x4StringDraw ("I2C value is", 30, 24, 15);

}

}

Thanks in advance, 

Ant

  • Hello Ant,

    I am going to make some suggestions as I have never used any of the MSP430 MCUs.

    First, you are calling the the adc_setup() each time inside your loop.  Is that necessary, can't it be set up just once?

    Second, you are type casting the ADC value (int) adc before it is summed and averaged.  For greater accuracy, you will want to type cast the final adc result.

    Third, there should be a step 5? In the ISR that sends the number of bytes to be transmitted.

    Forth, in the ISR you are missing a case 9.  This should be we're you check to see if any more bytes are available and then do step 8 again if there is.

    Wherever you got the code for the ISR from, go back and include all the steps.  They are required doe proper I2C transmission.

    As long as you are using the I2C Master example for the Stellaris, that end should work fine.

    Hope this helps,

  • Hi Greenja,

    Thanks for the prompt response.

    I can see what your saying regarding the ADC being called and the type casting will amend those.  When it comes to the code for the I2C I am lost and really hitting a wall.  the thing that is boggling me as well is the USI will only send 1 byte, how do I send the full 16 bits of the ADC integer.  Some sample code here would be a real god send if you have time?

    Best regards,

    Ant

  • I have written a couple of software I2C software implementations for master only for the CC25xx that are posted.  The slave implementation is almost identical except for the control of the bus lines.  There should be code for it available eithe in the forum or on the net.

    As far as the ADC value, you can break up the integer into bytes by sending the sending the high and low bytes.  The Stellaris as master will send out how many bytes it is requesting.

    There is an excellent tutorial on the I2C from Philips (NXP) on the net.  That is what I used when I was learning how to do the software I2C.  Search the next, there are dozens of examples and tutorials.

    Goog luck,

  • Thanks again Greenja, will look into the search terms you suggest.

**Attention** This is a public forum