Other Parts Discussed in Thread: INA260, MSP430F2619, ENERGIA
Tool/software: Code Composer Studio
Team,
I am a filed appications rotator working on getting a new sensor demo up and running for a customer. The sensor requires i2c and we are demoing on the MSP430F5529. Currently I am struggling just to get the i2c working so that I can write to and read from registers. I am not sure the sensor I am supposed to be demoing works correctly so currently I am just trying to read and write to/from a INA260 EVM over i2c. I have tried multiple approaches so far. I tried basing code off the USCIB0 read and write multiple bytes "examples". I also tried porting other peoples I2c code from other microcontrollers to the 5529 but have had little success. I believe my main issue is writing the ISR, I have found no good examples for this.
Question #1:
Does TI have any good I2C examples for reading/writing to a device?
Question#2:
I included my 3 approaches below. If any of them seem close I would greatly appreciate your input on how I can get up and running with I2C. I have used I2C before on microchip controllers and have read all the TI literature I can find (User guide, datasheet, how to use I2C, basics of I2C, and mspware). I am also now familiar with the USCI library.
Approach 1: wrote i2c based on the example code for the MSP430F2619.
main.c
#include <stdio.h>
#include <msp430f5529.h>
#include <lib5529i2c.h>
unsigned char array[5] = { 0x00, 0x07, 0x27};
unsigned char array2[5];
signed char byteCtr;
unsigned char *TI_receive_field;
unsigned char *TI_transmit_field;
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Disable Watchdog
P1DIR |= 0x01;
P1OUT= 0x00;// configure P1.0 as output
_EINT(); // enable interrupts
__enable_interrupt();
TI_USCI_I2C_transmitinit(0x40, 0x12); // initialize USCI
while ( TI_USCI_I2C_notready() );
TI_USCI_I2C_transmit(3,array); // transmit the first 3 bytes of array
//LPM0; // put CPU to sleep during communication
TI_USCI_I2C_receiveinit(0x40,0x12);
while ( TI_USCI_I2C_notready() );
TI_USCI_I2C_receive(3,array2);
}
lib5529i2c.h
/* * lib5529i2c.h * * Created on: Feb 9, 2018 * Author: a0226654 */ #ifndef LIB5529I2C_H_ #define LIB5529I2C_H_ #define SDA_PIN BIT0 // msp430x261x UCB0SDA pin #define SCL_PIN BIT1 // msp430x261x UCB0SCL pin void TI_USCI_I2C_receiveinit(unsigned char slave_address, unsigned char prescale); void TI_USCI_I2C_transmitinit(unsigned char slave_address, unsigned char prescale); void TI_USCI_I2C_receive(unsigned char byteCount, unsigned char *field); void TI_USCI_I2C_transmit(unsigned char byteCount, unsigned char *field); unsigned char TI_USCI_I2C_slave_present(unsigned char slave_address); unsigned char TI_USCI_I2C_notready(); #endif /* LIB5529I2C_H_ */
lib5529i2c.c
//******************************************************************************
// MSP430 USCI I2C Transmitter and Receiver
//
// Description: This code configures the MSP430's USCI module as
// I2C master capable of transmitting and receiving bytes.
//
// ***THIS IS THE MASTER CODE***
//
// Master
// MSP430F2619
// -----------------
// /|\| XIN|-
// | | |
// --|RST XOUT|-
// | |
// | |
// | |
// | SDA/P3.1|------->
// | SCL/P3.2|------->
//
// Note: External pull-ups are needed for SDA & SCL
//
// Uli Kretzschmar
// Texas Instruments Deutschland GmbH
// November 2007
// Built with IAR Embedded Workbench Version: 3.42A
//******************************************************************************
#include "msp430f5529.h" // device specific header
//#include "msp430x22x4.h"
//#include "msp430x23x0.h"
//#include "msp430xG46x.h"
// ... // more devices are possible
#include "lib5529i2c.h"
signed char byteCtr;
unsigned char *TI_receive_field;
unsigned char *TI_transmit_field;
int q=0;
//------------------------------------------------------------------------------
// void TI_USCI_I2C_receiveinit(unsigned char slave_address,
// unsigned char prescale)
//
// This function initializes the USCI module for master-receive operation.
//
// IN: unsigned char slave_address => Slave Address
// unsigned char prescale => SCL clock adjustment
//-----------------------------------------------------------------------------
void TI_USCI_I2C_receiveinit(unsigned char slave_address,
unsigned char prescale){
P3SEL |= SDA_PIN + SCL_PIN; // Assign I2C pins to USCI_B0
UCB0CTL1 = UCSWRST; // Enable SW reset
UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode
UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
UCB0BR0 = prescale; // set prescaler
UCB0BR1 = 0;
UCB0I2CSA = slave_address; // set slave address
UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
UCB0IE = UCNACKIE;
UCB0IE = UCRXIE; // Enable RX interrupt
}
//------------------------------------------------------------------------------
// void TI_USCI_I2C_transmitinit(unsigned char slave_address,
// unsigned char prescale)
//
// This function initializes the USCI module for master-transmit operation.
//
// IN: unsigned char slave_address => Slave Address
// unsigned char prescale => SCL clock adjustment
//------------------------------------------------------------------------------
void TI_USCI_I2C_transmitinit(unsigned char slave_address,
unsigned char prescale){
P3SEL |= SDA_PIN + SCL_PIN; // Assign I2C pins to USCI_B0
UCB0CTL1 |= UCSWRST; // Enable SW reset
UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode
UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
UCB0BR0 = prescale; // set prescaler
UCB0BR1 = 0;
UCB0I2CSA = slave_address; // Set slave address
UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
UCB0IE = UCNACKIE;
UCB0IE |= UCTXIE; // Enable TX ready interrupt
}
//------------------------------------------------------------------------------
// void TI_USCI_I2C_receive(unsigned char byteCount, unsigned char *field)
//
// This function is used to start an I2C commuincation in master-receiver mode.
//
// IN: unsigned char byteCount => number of bytes that should be read
// unsigned char *field => array variable used to store received data
//------------------------------------------------------------------------------
void TI_USCI_I2C_receive(unsigned char byteCount, unsigned char *field){
TI_receive_field = field;
if ( byteCount == 1 ){
byteCtr = 0 ;
__disable_interrupt();
UCB0CTL1 |= UCTXSTT; // I2C start condition
while (UCB0CTL1 & UCTXSTT); // Start condition sent?
UCB0CTL1 |= UCTXSTP; // I2C stop condition
__enable_interrupt();
} else if ( byteCount > 1 ) {
byteCtr = byteCount - 2 ;
UCB0CTL1 |= UCTXSTT; // I2C start condition
} else
while (1); // illegal parameter
}
//------------------------------------------------------------------------------
// void TI_USCI_I2C_transmit(unsigned char byteCount, unsigned char *field)
//
// This function is used to start an I2C commuincation in master-transmit mode.
//
// IN: unsigned char byteCount => number of bytes that should be transmitted
// unsigned char *field => array variable. Its content will be sent.
//------------------------------------------------------------------------------
void TI_USCI_I2C_transmit(unsigned char byteCount, unsigned char *field){
TI_transmit_field = field;
byteCtr = byteCount;
UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition
}
//------------------------------------------------------------------------------
// unsigned char TI_USCI_I2C_slave_present(unsigned char slave_address)
//
// This function is used to look for a slave address on the I2C bus.
//
// IN: unsigned char slave_address => Slave Address
// OUT: unsigned char => 0: address was not found,
// 1: address found
//------------------------------------------------------------------------------
unsigned char TI_USCI_I2C_slave_present(unsigned char slave_address){
unsigned char ie2_bak, slaveadr_bak, ucb0i2cie, returnValue;
ucb0i2cie = UCB0IE; // restore old UCB0I2CIE
ie2_bak = UCB0IE; // store IE2 register
slaveadr_bak = UCB0I2CSA; // store old slave address
UCB0IE &= ~ UCNACKIE; // no NACK interrupt
UCB0I2CSA = slave_address; // set slave address
UCB0IE &= ~(UCTXIE + UCRXIE); // no RX or TX interrupts
__disable_interrupt();
UCB0CTL1 |= UCTR + UCTXSTT + UCTXSTP; // I2C TX, start condition
while (UCB0CTL1 & UCTXSTP); // wait for STOP condition
returnValue = !(UCB0STAT & UCNACKIFG);
__enable_interrupt();
UCB0IE = ie2_bak; // restore IE2
UCB0I2CSA = slaveadr_bak; // restore old slave address
UCB0IE = ucb0i2cie; // restore old UCB0CTL1
return returnValue; // return whether or not
// a NACK occured
}
//------------------------------------------------------------------------------
// unsigned char TI_USCI_I2C_notready()
//
// This function is used to check if there is commuincation in progress.
//
// OUT: unsigned char => 0: I2C bus is idle,
// 1: communication is in progress
//------------------------------------------------------------------------------
unsigned char TI_USCI_I2C_notready(){
return (UCB0STAT & UCBBUSY);
}
#pragma vector = USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void)
{
switch (__even_in_range(UCB0IV, 12))
{
case 10:
q=10;// Vector 10: RXIFG -- I am not using it yet (but I'll need it)
if (UCB0STAT & UCNACKIFG){ // send STOP if slave sends NACK
UCB0CTL1 |= UCTXSTP;
UCB0STAT &= ~UCNACKIFG;
}
break;
case 12:
q=12;// Vector 12: TXIFG
if (P2IFG & UCRXIFG){
if ( byteCtr == 0 ){
UCB0CTL1 |= UCTXSTP; // I2C stop condition
*TI_receive_field = UCB0RXBUF;
TI_receive_field++;
}
else {
*TI_receive_field = UCB0RXBUF;
TI_receive_field++;
byteCtr--;
}
}
else {
if (byteCtr == 0){
UCB0CTL1 |= UCTXSTP; // I2C stop condition
P2IFG &= ~UCTXIFG; // Clear USCI_B0 TX int flag
}
else {
UCB0TXBUF = *TI_transmit_field;
TI_transmit_field++;
byteCtr--;
}
}
break;
default:
break;
}
}
Approach 2: Wrote library based off 5529 read/write example code
#include "msp430f5529.h" // device specific header
//#include "msp430x22x4.h"
//#include "msp430x23x0.h"
//#include "msp430xG46x.h"
// ... // more devices are possible
#include "lib5529i2c.h"
signed char byteCtr;
unsigned char *TI_receive_field;
unsigned char *TI_transmit_field;
unsigned char TXByteCtr=0;
unsigned char RXByteCtr=0;
void TI_USCI_I2C_receiveinit(unsigned char slave_address,
unsigned char prescale){
P3SEL |= SDA_PIN + SCL_PIN; // Assign I2C pins to USCI_B0
UCB0CTL1 = UCSWRST; // Enable SW reset
UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode
UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
UCB0BR0 = prescale; // set prescaler
UCB0BR1 = 0;
UCB0I2CSA = slave_address; // set slave address
UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
UCB0IE = UCNACKIE;
UCB0IE = UCRXIE; // Enable RX interrupt
}
void TI_USCI_I2C_transmitinit(unsigned char slave_address,
unsigned char prescale){
P3SEL |= SDA_PIN + SCL_PIN; // Assign I2C pins to USCI_B0
UCB0CTL1 |= UCSWRST; // Enable SW reset
UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode
UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
UCB0BR0 = prescale; // set prescaler
UCB0BR1 = 0;
UCB0I2CSA = slave_address; // Set slave address
UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
UCB0IE = UCNACKIE;
UCB0IE |= UCTXIE; // Enable TX ready interrupt
}
void TI_USCI_I2C_receive(unsigned char byteCount, unsigned char *field){
TI_receive_field = field;
if ( byteCount == 1 ){
RXByteCtr = 0 ;
__disable_interrupt();
UCB0CTL1 |= UCTXSTT; // I2C start condition
while (UCB0CTL1 & UCTXSTT); // Start condition sent?
UCB0CTL1 |= UCTXSTP; // I2C stop condition
__enable_interrupt();
} else if ( byteCount > 1 ) {
RXByteCtr = byteCount - 2 ;
UCB0CTL1 |= UCTXSTT; // I2C start condition
} else
while (1); // illegal parameter
}
void TI_USCI_I2C_transmit(unsigned char byteCount, unsigned char *field){
TI_transmit_field = field;
TXByteCtr = byteCount;
UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition
}
unsigned char TI_USCI_I2C_slave_present(unsigned char slave_address){
unsigned char ie2_bak, slaveadr_bak, ucb0i2cie, returnValue;
ucb0i2cie = UCB0IE; // restore old UCB0I2CIE
ie2_bak = UCB0IE; // store IE2 register
slaveadr_bak = UCB0I2CSA; // store old slave address
UCB0IE &= ~ UCNACKIE; // no NACK interrupt
UCB0I2CSA = slave_address; // set slave address
UCB0IE &= ~(UCTXIE + UCRXIE); // no RX or TX interrupts
__disable_interrupt();
UCB0CTL1 |= UCTR + UCTXSTT + UCTXSTP; // I2C TX, start condition
while (UCB0CTL1 & UCTXSTP); // wait for STOP condition
returnValue = !(UCB0STAT & UCNACKIFG);
__enable_interrupt();
UCB0IE = ie2_bak; // restore IE2
UCB0I2CSA = slaveadr_bak; // restore old slave address
UCB0IE = ucb0i2cie; // restore old UCB0CTL1
return returnValue; // return whether or not
// a NACK occured
}
unsigned char TI_USCI_I2C_notready(){
return (UCB0STAT & UCBBUSY);
}
#pragma vector = USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void)
{
switch (__even_in_range(UCB0IV, 12))
{
case 10:
RXByteCtr--; // Decrement RX byte counter
if (RXByteCtr)
{
*TI_receive_field = UCB0RXBUF; // Move RX data to address PRxData
if (RXByteCtr == 1) // Only one byte left?
UCB0CTL1 |= UCTXSTP; // Generate I2C stop condition
}
else
{
*TI_receive_field = UCB0RXBUF; // Move final RX data to PRxData
__bic_SR_register_on_exit(LPM0_bits); // Exit active CPU
}
case 12:
if (TXByteCtr) // Check TX byte counter
{
UCB0TXBUF = *TI_transmit_field; // Load TX buffer
TXByteCtr--; // Decrement TX byte counter
}
else
{
UCB0CTL1 |= UCTXSTP; // I2C stop condition
UCB0IFG &= ~UCTXIFG; // Clear USCI_B0 TX int flag
__bic_SR_register_on_exit(LPM0_bits); // Exit LPM0
}
default:
break;
}
}
/*
* Here's an example using an MSP430G2553 to read/write using I2C to an 24LC512 and DS3231 RTC.
* Two functions are provided for read and write.
*
* The code avoids any while loops, and relies completely on interrupts and low power modes of the MSP430 to reduce the power consumption to the
* minimum.
*
* Author: Ahmed Talaat (aa_talaat@yahoo.com)
* Date: 19 October 2012
*/
#include "msp430f5529.h"
#define ONEBYTEADDR 1
#define TWOBYTEADDR 2
#define WRITE 0 // ISR mode WRITE or READ
#define READ 1
#define NOACK 2
#define EEPROM_ADDR 0x40
#define DS3231_ADDR 0x00
unsigned char txdataEEPROM[2];
unsigned char txdataDS3231[7] = {0x20, 0x30, 0x20, 0x04, 0x17, 0x10, 0x12};
unsigned char rxdata[150];
typedef struct {
volatile unsigned char *data_buf; // address of tx or rx data buffer
volatile unsigned char buf_size; // size of the buffer
volatile unsigned char buf_index; // index in the buffer
volatile unsigned char addr_index; // index of the byte address (0,1)
volatile unsigned char isr_mode; // Tx or Rx affects the interrupt logic
volatile unsigned char addr_high_byte; // High byte of the address to read/write to
volatile unsigned char addr_low_byte; // Low byte of the address to read/write to
volatile unsigned char addr_type; // two bytes like eeprom or 1 byte like RTC for example
} i2c_t;
i2c_t i2c_packet;
void i2c_init(void);
void i2c_tx(unsigned char, unsigned char *, unsigned char, unsigned char, unsigned char, unsigned char);
void i2c_rx(unsigned char, unsigned char *, unsigned char, unsigned char, unsigned char, unsigned char);
void usdelay(int);
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop WDT
i2c_init(); // Initialize I2C
txdataEEPROM[0]=0x61;
txdataEEPROM[1]=0x3F;
i2c_tx(EEPROM_ADDR, txdataEEPROM, sizeof(txdataEEPROM)-1,ONEBYTEADDR,0x00,0x00);//i2c TX 115 bytes starting @ address 01:00
i2c_rx(EEPROM_ADDR, rxdata, 2,ONEBYTEADDR,0x00,0x00);//i2c RX 115 bytes from EEPROM starting @ address 01:00
LPM0; // Enter LPM0 w/ interrupts
}
void i2c_init(void){
P3SEL |= BIT0 + BIT1; //Set I2C pins
//P1SEL2|= BIT6 + BIT7;
UCB0CTL1 |= UCSWRST; //Enable SW reset
UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; //I2C Master, synchronous mode
UCB0CTL1 = UCSSEL_2 + UCSWRST; //Use SMCLK, keep SW reset
UCB0BR0 = 10; //fSCL = SMCLK/11 = ~100kHz
UCB0BR1 = 0;
UCB0CTL1 &= ~UCSWRST; //Clear SW reset, resume operation
UCB0IE |= UCTXIE; //Enable TX interrupt
UCB0IE |= UCRXIE; //Enable RX interrupt
UCB0IE |= UCNACKIE; //Need to enable the status change interrupt
__enable_interrupt(); //Enable global interrupt
}
void i2c_tx(unsigned char slave_addr, unsigned char *txdata, unsigned char bufSize, unsigned char addr_size,
unsigned char high_byte_addr, unsigned char low_byte_addr) {
i2c_packet.isr_mode=WRITE;
i2c_packet.data_buf=txdata;
i2c_packet.buf_size=bufSize;
i2c_packet.buf_index=0;
i2c_packet.addr_type=addr_size;
i2c_packet.addr_high_byte=high_byte_addr;
i2c_packet.addr_low_byte=low_byte_addr;
i2c_packet.addr_index=0;
UCB0I2CSA = slave_addr; //Slave Address
while (1) {
UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition
LPM0; // Enter LPM0
// and remain until all data is TX'd
if (i2c_packet.isr_mode == NOACK){ // If no ack received, then sleep for 0.5ms and try again
i2c_packet.isr_mode = WRITE;
i2c_packet.addr_index=0; // Reset the address index for the next write operation
usdelay(500);
} else {
break; // Successful write, then quit
}
}
}
/*
* This functions reads from any I2C device. It takes as parameters:
* - The slave address (7 bits)
* - Pointer to the data buffer to fill with data read.
* - Size of the buffer
* - Size of the address location to start writing at. Being 2 bytes for some of the EEPROMs and single byte
* for other devices. Please note that the ISR of this function assumes that the address bytes are written high
* byte first, then low byte second.
* In case of single byte address device, only the high byte will be used to set the address.
* - The high byte and low byte address for the location to start reading at.
*
* The function starts with a write operation to specify the address at which the read operation with start
* In case the start condition of the write operation is not ack (for example EEPROM busy with a a previous write cycle),
* the corresponding interrupts detects this condition, generates a stop signal, and a Timer A1 (not Timer A0)
* is activated for 0.5 ms, then the trial for writing is repeated.
*
* Once the write address is successful, the functions switch to read mode, and fills the buffer provided
*
*/
void i2c_rx(unsigned char slave_addr, unsigned char *rxdata, unsigned char bufSize, unsigned char addr_size,
unsigned char high_byte_addr, unsigned char low_byte_addr) {
i2c_packet.isr_mode=READ; // The ISR will send the address bytes, then wake CPU.
i2c_packet.addr_type=addr_size;
i2c_packet.addr_high_byte=high_byte_addr;
i2c_packet.addr_low_byte=low_byte_addr;
i2c_packet.addr_index=0;
UCB0I2CSA = slave_addr; // Slave Address
while (1) {
UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition
LPM0; // Enter LPM0
// and remain until all data is TX'd
if (i2c_packet.isr_mode == NOACK){ // If no ack received, then sleep for 0.5ms and try again
i2c_packet.isr_mode = READ;
i2c_packet.addr_index=0; // Reset the address index for the next write operation
usdelay(500);
} else {
break; // Successful write, then quit
}
}
// We wrote already the address, so now read only data.
i2c_packet.addr_index=i2c_packet.addr_type;
i2c_packet.data_buf=rxdata;
i2c_packet.buf_size=bufSize;
i2c_packet.buf_index=0;
UCB0CTL1 &= ~UCTR; // I2C RX
UCB0CTL1 |= UCTXSTT; // I2C re-start condition
LPM0; // Enter LPM0
// and remain until all data is received
}
//interrupt(USCIAB0RX_VECTOR) state change to trap the no_Ack from slave case
/*
* This interrupt is called each time the UCSI_B module is either ready to get a new byte in UCB0TXBUF to send to the I2C device, or
* a new byte is read into UCB0RXBUF and we should pick it up.
* The interrupt is called as both UCB0TXIE and UCB0RXIE are enabled. To stop this interrupt being called indefinitely, the corresponding
* interrupt flag should be cleared.
* These flags are automatically clearly by the USCI_B module if the UCB0XXBUF is access. However, if we are to do something different than reading
* or writing a byte to/from the UCB0XXBUF, we need to clear the corresponding flag by ourselves or the ISR will be called for ever,
* and the whole program will hang.
*/
#pragma vector = USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void)
{
switch (__even_in_range(UCB0IV, 12))
{
case 10:
RXByteCtr--; // Decrement RX byte counter
if (RXByteCtr)
{
*PRxData++ = UCB0RXBUF; // Move RX data to address PRxData
if (RXByteCtr == 1) // Only one byte left?
UCB0CTL1 |= UCTXSTP; // Generate I2C stop condition
}
else
{
*PRxData = UCB0RXBUF; // Move final RX data to PRxData
__bic_SR_register_on_exit(LPM0_bits); // Exit active CPU
}
case 12:
// Transmit address bytes irrespective of send or receive mode.*/
if (i2c_packet.addr_index==0){
UCB0TXBUF = i2c_packet.addr_high_byte;
i2c_packet.addr_index++;
}
else if (i2c_packet.addr_index==1 && i2c_packet.addr_type==TWOBYTEADDR){
UCB0TXBUF = i2c_packet.addr_low_byte;
i2c_packet.addr_index++;
}
else if(UCTXIFG & P2IFG && i2c_packet.isr_mode==READ) {
// USCI_B is ready to get a new data byte to transmit it, and we are in READ mode.
// So, we should not continue writing, but should exit to the calling function to
// switch the USCI_B into read mode
P2IFG &= ~UCTXIFG; // Clear USCI_B0 TX int flag manually as we did not write to the UCB0TXBUF
LPM0_EXIT; // Exit LPM0
}
else if(UCTXIFG & P2IFG && i2c_packet.isr_mode==WRITE){// USCI_B is ready to get a new data byte to transmit it, and we are in write mode.
if(i2c_packet.buf_index == i2c_packet.buf_size){ // If no more data to transmit, then issue stop condition and wake CPU.
P2IFG &= ~UCTXIFG; // Clear USCI_B0 TX int flag manually as we did not write to the UCB0TXBUF
UCB0CTL1 |= UCTXSTP; // I2C stop condition
LPM0_EXIT; // Exit LPM0
} else {
UCB0TXBUF = i2c_packet.data_buf[i2c_packet.buf_index];
i2c_packet.buf_index++; // Increment TX byte counter
}
}
else if (UCRXIFG & P2IFG && i2c_packet.addr_index==i2c_packet.addr_type) {
// Read mode, and we already completed writing the address
i2c_packet.data_buf[i2c_packet.buf_index]= UCB0RXBUF;
i2c_packet.buf_index++; // Increment RX byte counter
if(i2c_packet.buf_index == i2c_packet.buf_size){ // If last byte to receive, then issue stop condition and wake CPU.
P2IFG &= ~UCRXIFG; // Clear USCI_B0 RX int flag
UCB0CTL1 |= UCTXSTP; // I2C stop condition here to avoid reading any extra bytes
LPM0_EXIT; // Exit LPM0
}
}
/*
break;
default:
break;
*/
}
}
//------------------------------------------------------------------------------
// micro seconds delays
//
void usdelay(int interval){
// Setup TimerA
TA1CCTL0 = CCIE; // interrupt enabled
TA1CCR0 = TA1R + interval; // micro secs @ 1Mhz Clock
TA1CTL = TASSEL_2 + MC_2; // SMCLK, continuous mode.
LPM0; // suspend CPU
}
// Timer A1 interrupt service routine. TIMERx_Ay_VECTOR.(x being the index of the timer, y of the vector for this timer)
#pragma vector=TIMER1_A0_VECTOR
__interrupt void Timer1_A0 (void)
{
TA1CTL = 0; // Stop Timer_A1
LPM0_EXIT; // Return active
}
//******************************************************************************// MSP430 USCI I2C Transmitter and Receiver//// Description: This code configures the MSP430's USCI module as// I2C master capable of transmitting and receiving bytes.//// ***THIS IS THE MASTER CODE***//// Master// MSP430F2619// -----------------// /|\| XIN|-// | | |// --|RST XOUT|-// | |// | |// | |// | SDA/P3.1|------->// | SCL/P3.2|------->//// Note: External pull-ups are needed for SDA & SCL//// Uli Kretzschmar// Texas Instruments Deutschland GmbH// November 2007// Built with IAR Embedded Workbench Version: 3.42A//******************************************************************************/*#include "msp430f5529.h" // device specific header//#include "msp430x22x4.h"//#include "msp430x23x0.h"//#include "msp430xG46x.h"// ... // more devices are possible
#include "lib5529i2c.h"
signed char byteCtr;unsigned char *TI_receive_field;unsigned char *TI_transmit_field;int q=0;//------------------------------------------------------------------------------// void TI_USCI_I2C_receiveinit(unsigned char slave_address,// unsigned char prescale)//// This function initializes the USCI module for master-receive operation.//// IN: unsigned char slave_address => Slave Address// unsigned char prescale => SCL clock adjustment//-----------------------------------------------------------------------------void TI_USCI_I2C_receiveinit(unsigned char slave_address, unsigned char prescale){ P3SEL |= SDA_PIN + SCL_PIN; // Assign I2C pins to USCI_B0 UCB0CTL1 = UCSWRST; // Enable SW reset UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset UCB0BR0 = prescale; // set prescaler UCB0BR1 = 0; UCB0I2CSA = slave_address; // set slave address UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation UCB0IE = UCNACKIE; UCB0IE = UCRXIE; // Enable RX interrupt}
//------------------------------------------------------------------------------// void TI_USCI_I2C_transmitinit(unsigned char slave_address,// unsigned char prescale)//// This function initializes the USCI module for master-transmit operation.//// IN: unsigned char slave_address => Slave Address// unsigned char prescale => SCL clock adjustment//------------------------------------------------------------------------------void TI_USCI_I2C_transmitinit(unsigned char slave_address, unsigned char prescale){ P3SEL |= SDA_PIN + SCL_PIN; // Assign I2C pins to USCI_B0 UCB0CTL1 |= UCSWRST; // Enable SW reset UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // I2C Master, synchronous mode UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset UCB0BR0 = prescale; // set prescaler UCB0BR1 = 0; UCB0I2CSA = slave_address; // Set slave address UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation UCB0IE = UCNACKIE; UCB0IE |= UCTXIE; // Enable TX ready interrupt}
//------------------------------------------------------------------------------// void TI_USCI_I2C_receive(unsigned char byteCount, unsigned char *field)//// This function is used to start an I2C commuincation in master-receiver mode.//// IN: unsigned char byteCount => number of bytes that should be read// unsigned char *field => array variable used to store received data//------------------------------------------------------------------------------void TI_USCI_I2C_receive(unsigned char byteCount, unsigned char *field){ TI_receive_field = field; if ( byteCount == 1 ){ byteCtr = 0 ; __disable_interrupt(); UCB0CTL1 |= UCTXSTT; // I2C start condition while (UCB0CTL1 & UCTXSTT); // Start condition sent? UCB0CTL1 |= UCTXSTP; // I2C stop condition __enable_interrupt(); } else if ( byteCount > 1 ) { byteCtr = byteCount - 2 ; UCB0CTL1 |= UCTXSTT; // I2C start condition } else while (1); // illegal parameter}
//------------------------------------------------------------------------------// void TI_USCI_I2C_transmit(unsigned char byteCount, unsigned char *field)//// This function is used to start an I2C commuincation in master-transmit mode.//// IN: unsigned char byteCount => number of bytes that should be transmitted// unsigned char *field => array variable. Its content will be sent.//------------------------------------------------------------------------------void TI_USCI_I2C_transmit(unsigned char byteCount, unsigned char *field){ TI_transmit_field = field; byteCtr = byteCount; UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition}
//------------------------------------------------------------------------------// unsigned char TI_USCI_I2C_slave_present(unsigned char slave_address)//// This function is used to look for a slave address on the I2C bus.//// IN: unsigned char slave_address => Slave Address// OUT: unsigned char => 0: address was not found,// 1: address found//------------------------------------------------------------------------------unsigned char TI_USCI_I2C_slave_present(unsigned char slave_address){ unsigned char ie2_bak, slaveadr_bak, ucb0i2cie, returnValue; ucb0i2cie = UCB0IE; // restore old UCB0I2CIE ie2_bak = UCB0IE; // store IE2 register slaveadr_bak = UCB0I2CSA; // store old slave address UCB0IE &= ~ UCNACKIE; // no NACK interrupt UCB0I2CSA = slave_address; // set slave address UCB0IE &= ~(UCTXIE + UCRXIE); // no RX or TX interrupts __disable_interrupt(); UCB0CTL1 |= UCTR + UCTXSTT + UCTXSTP; // I2C TX, start condition while (UCB0CTL1 & UCTXSTP); // wait for STOP condition
returnValue = !(UCB0STAT & UCNACKIFG); __enable_interrupt(); UCB0IE = ie2_bak; // restore IE2 UCB0I2CSA = slaveadr_bak; // restore old slave address UCB0IE = ucb0i2cie; // restore old UCB0CTL1 return returnValue; // return whether or not // a NACK occured}
//------------------------------------------------------------------------------// unsigned char TI_USCI_I2C_notready()//// This function is used to check if there is commuincation in progress.//// OUT: unsigned char => 0: I2C bus is idle,// 1: communication is in progress//------------------------------------------------------------------------------unsigned char TI_USCI_I2C_notready(){ return (UCB0STAT & UCBBUSY);}
#pragma vector = USCI_B0_VECTOR__interrupt void USCI_B0_ISR(void){ switch (__even_in_range(UCB0IV, 12)) {
case 10: q=10;// Vector 10: RXIFG -- I am not using it yet (but I'll need it) if (UCB0STAT & UCNACKIFG){ // send STOP if slave sends NACK UCB0CTL1 |= UCTXSTP; UCB0STAT &= ~UCNACKIFG; } break; case 12: q=12;// Vector 12: TXIFG if (P2IFG & UCRXIFG){ if ( byteCtr == 0 ){ UCB0CTL1 |= UCTXSTP; // I2C stop condition *TI_receive_field = UCB0RXBUF; TI_receive_field++; } else { *TI_receive_field = UCB0RXBUF; TI_receive_field++; byteCtr--; } } else { if (byteCtr == 0){ UCB0CTL1 |= UCTXSTP; // I2C stop condition P2IFG &= ~UCTXIFG; // Clear USCI_B0 TX int flag } else { UCB0TXBUF = *TI_transmit_field; TI_transmit_field++; byteCtr--; } } break; default: break;
}
}
*/