summaryrefslogtreecommitdiffstats
path: root/lib/msp430/twi.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/msp430/twi.c')
-rw-r--r--lib/msp430/twi.c850
1 files changed, 850 insertions, 0 deletions
diff --git a/lib/msp430/twi.c b/lib/msp430/twi.c
new file mode 100644
index 0000000..b92a6fe
--- /dev/null
+++ b/lib/msp430/twi.c
@@ -0,0 +1,850 @@
+/*
+ ************************************************************************
+ * twi.c
+ *
+ * Arduino core files for MSP430
+ * Copyright (c) 2012 Robert Wessels. All right reserved.
+ *
+ *
+ ***********************************************************************
+ Derived from:
+ twi.c - TWI/I2C library for Wiring & Arduino
+ Copyright (c) 2006 Nicholas Zambetti. All right reserved.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include <math.h>
+#include <stdlib.h>
+#include "Energia.h" // for digitalWrite
+
+#ifndef cbi
+#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
+#endif
+
+#ifndef sbi
+#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
+#endif
+
+#include "wiring_private.h"
+#include "pins_energia.h"
+#include "twi.h"
+#include "usci_isr_handler.h"
+
+static volatile uint8_t twi_state;
+static volatile uint8_t twi_sendStop; // should the transaction end with a stop
+static volatile uint8_t twi_inRepStart; // in the middle of a repeated start
+
+static void (*twi_onSlaveTransmit)(void);
+static void (*twi_onSlaveReceive)(uint8_t*, int);
+
+static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH];
+static volatile uint8_t twi_masterBufferIndex;
+static uint8_t twi_masterBufferLength;
+
+static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH];
+static volatile uint8_t twi_txBufferIndex;
+static volatile uint8_t twi_txBufferLength;
+#if (defined(__MSP430_HAS_USCI__) || defined(__MSP430_HAS_EUSCI_B0__))
+static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH];
+static volatile uint8_t twi_rxBufferIndex;
+#endif
+
+static volatile uint8_t twi_error;
+
+#ifdef __MSP430_HAS_USI__
+static uint8_t twi_slarw;
+static uint8_t twi_my_addr;
+
+#endif
+
+#ifdef __MSP430_HAS_USCI__
+#endif
+
+#ifdef __MSP430_HAS_EUSCI_B0__
+#endif
+
+/*
+ * Function twi_init
+ * Desc readys twi pins and sets twi bitrate
+ * Input none
+ * Output none
+ */
+void twi_init(void)
+{
+ // initialize state
+ twi_state = TWI_IDLE;
+ twi_sendStop = true; // default value
+ twi_inRepStart = false;
+
+#ifdef __MSP430_HAS_USI__
+
+ /* 100 KHz for all */
+#if (F_CPU >= 16000000L) || (F_CPU >= 12000000L)
+ USICKCTL = USIDIV_7;
+#elif defined(CALBC1_8MHZ_) && (F_CPU >= 8000000L)
+ USICKCTL = USIDIV_6;
+#elif defined(CALBC1_1MHZ_) && (F_CPU >= 1000000L)
+ USICKCTL = USIDIV_3;
+#endif
+ /* Enable USI I2C mode. */
+ USICTL1 = USII2C;
+ /* SDA/SCL port enable and hold in reset */
+ USICTL0 = (USIPE6 | USIPE7 | USISWRST);
+ /* SMCLK and SCL inactive state is high */
+ USICKCTL |= (USISSEL_2 | USICKPL);
+ /* Disable automatic clear control */
+ USICNT |= USIIFGCC;
+ /* Enable USI */
+ USICTL0 &= ~USISWRST;
+ /* Counter interrupt enable */
+ USICTL1 |= USIIE;
+#endif
+
+#ifdef __MSP430_HAS_USCI__
+ /* Calling this dummy function prevents the linker
+ * from stripping the USCI interupt vectors.*/
+ usci_isr_install();
+
+ P1SEL |= BIT6 + BIT7; // Assign I2C pins to USCI_B0
+ P1SEL2|= BIT6 + BIT7; // Assign I2C pins to USCI_B0
+
+ //Disable the USCI module and clears the other bits of control register
+ UCB0CTL1 = UCSWRST;
+
+ /*
+ * Configure as I2C Slave.
+ * UCMODE_3 = I2C mode
+ * UCSYNC = Synchronous mode
+ * UCCLK = SMCLK
+ */
+ UCB0CTL0 = UCMODE_3 | UCSYNC;
+ /*
+ * Compute the clock divider that achieves less than or
+ * equal to 100kHz. The numerator is biased to favor a larger
+ * clock divider so that the resulting clock is always less than or equal
+ * to the desired clock, never greater.
+ */
+ UCB0BR0 = (unsigned char)((F_CPU / TWI_FREQ) & 0xFF);
+ UCB0BR1 = (unsigned char)((F_CPU / TWI_FREQ) >> 8);
+
+ UCB0CTL1 &= ~(UCSWRST);
+
+ /* Set I2C state change interrupt mask */
+ UCB0I2CIE |= (UCALIE|UCNACKIE|UCSTTIE|UCSTPIE);
+ /* Enable state change and TX/RX interrupts */
+ UC0IE |= UCB0RXIE | UCB0TXIE;
+#endif
+#ifdef __MSP430_HAS_EUSCI_B0__
+
+ P1SEL1 |= BIT6 + BIT7; // Pin init
+
+ //Disable the USCI module and clears the other bits of control register
+ UCB0CTLW0 = UCSWRST;
+
+ //Configure Automatic STOP condition generation
+ UCB0CTLW1 &= ~UCASTP_3;
+ //UCB0CTLW1 |= autoSTOPGeneration;
+
+ //Byte Count Threshold
+ //UCB0TBCNT = byteCounterThreshold;
+ /*
+ * Configure as I2C master mode.
+ * UCMST = Master mode
+ * UCMODE_3 = I2C mode
+ * UCSYNC = Synchronous mode
+ * UCCLK = SMCLK
+ */
+ UCB0CTLW0 = UCMODE_3 | UCSSEL__SMCLK | UCSYNC | UCSWRST;
+
+ /*
+ * Compute the clock divider that achieves the fastest speed less than or
+ * equal to the desired speed. The numerator is biased to favor a larger
+ * clock divider so that the resulting clock is always less than or equal
+ * to the desired clock, never greater.
+ */
+ UCB0BRW = (unsigned short)(F_CPU / 400000);
+ UCB0CTLW0 &= ~(UCSWRST);
+ UCB0IE |= (UCRXIE0|UCTXIE0|UCSTTIE|UCSTPIE); // Enable I2C interrupts
+#endif
+}
+
+/*
+ * Function twi_setAddress
+ * Desc sets slave address and enables interrupt
+ * Input none
+ * Output none
+ */
+void twi_setAddress(uint8_t address)
+{
+#ifdef __MSP430_HAS_USI__
+ twi_my_addr = address << 1;
+#endif
+#ifdef __MSP430_HAS_USCI__
+ /* UCGCEN = respond to general Call */
+ UCB0I2COA = (address | UCGCEN);
+#endif
+#ifdef __MSP430_HAS_EUSCI_B0__
+ /* UCGCEN = respond to general Call */
+ UCB0I2COA0 = (address | UCOAEN | UCGCEN);
+#endif
+}
+
+/*
+ * Function twi_readFrom
+ * Desc attempts to become twi bus master and read a
+ * series of bytes from a device on the bus
+ * Input address: 7bit i2c device address
+ * data: pointer to byte array
+ * length: number of bytes to read into array
+ * Output number of bytes read
+ */
+uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop)
+{
+ uint8_t i;
+
+#ifdef __MSP430_HAS_USI__
+ /* Disable START condition interrupt */
+ USICTL1 &= ~USISTTIE;
+ /* I2C master mode */
+ USICTL0 |= USIMST;
+#endif
+#ifdef __MSP430_HAS_USCI__
+ UCB0CTL1 = UCSWRST; // Enable SW reset
+ UCB0CTL1 |= (UCSSEL_2); // I2C Master, synchronous mode
+ UCB0CTL0 |= (UCMST | UCMODE_3 | UCSYNC); // I2C Master, synchronous mode
+ UCB0CTL1 &= ~(UCTR); // Configure in receive mode
+ UCB0I2CSA = address; // Set Slave Address
+ UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
+ UCB0I2CIE |= (UCALIE|UCNACKIE|UCSTPIE); // Enable I2C interrupts
+ UC0IE |= (UCB0RXIE | UCB0TXIE); // Enable I2C interrupts
+#endif
+#ifdef __MSP430_HAS_EUSCI_B0__
+ UCB0CTLW0 = UCSWRST; // Enable SW reset
+ UCB0CTLW0 |= (UCMST | UCMODE_3 | UCSYNC | UCSSEL__SMCLK); // I2C Master, synchronous mode
+ UCB0CTLW0 &= ~(UCTR); // Configure in receive mode
+ UCB0I2CSA = address; // Set Slave Address
+ UCB0CTLW0 &= ~UCSWRST; // Clear SW reset, resume operation
+ UCB0IE |= (UCRXIE0|UCALIE|UCNACKIFG|UCSTTIFG|UCSTPIFG); // Enable I2C interrupts
+#endif
+ // ensure data will fit into buffer
+ if(TWI_BUFFER_LENGTH < length){
+ return 0;
+ }
+
+ // initialize buffer iteration vars
+ twi_masterBufferIndex = 0;
+ twi_masterBufferLength = length-1; // This is not intuitive, read on...
+ // On receive, the previously configured ACK/NACK setting is transmitted in
+ // response to the received byte before the interrupt is signalled.
+ // Therefor we must actually set NACK when the _next_ to last byte is
+ // received, causing that NACK to be sent in response to receiving the last
+ // expected byte of data.
+
+#ifdef __MSP430_HAS_USI__
+ /* build sla+w, slave device address + w bit */
+ twi_slarw = 1;
+ twi_slarw |= address << 1;
+
+ // send start condition
+ twi_state = TWI_SND_START;
+ // this will trigger an interrupt kicking off the state machine in the isr
+ USICTL1 |= USIIFG;
+#endif
+#ifdef __MSP430_HAS_USCI__
+ twi_state = TWI_MRX; // Master receive mode
+ UCB0CTL1 |= UCTXSTT; // I2C start condition
+#endif
+#ifdef __MSP430_HAS_EUSCI_B0__
+ twi_state = TWI_MRX; // Master receive mode
+ while (UCB0CTLW0 & UCTXSTP); // Ensure stop condition got sent
+ UCB0CTLW0 |= UCTXSTT; // I2C start condition
+#endif
+
+ /* Wait in low power mode for read operation to complete */
+ while(twi_state != TWI_IDLE){
+ __bis_SR_register(LPM0_bits);
+ }
+
+ if (twi_masterBufferIndex < length)
+ length = twi_masterBufferIndex;
+
+ for(i = 0; i < length; ++i){
+ data[i] = twi_masterBuffer[i];
+ }
+
+#ifdef __MSP430_HAS_USCI__
+ /* Ensure stop condition got sent before we exit. */
+ while (UCB0CTL1 & UCTXSTP);
+#endif
+ return length;
+}
+
+/*
+ * Function twi_writeTo
+ * Desc attempts to become twi bus master and write a
+ * series of bytes to a device on the bus
+ * Input address: 7bit i2c device address
+ * data: pointer to byte array
+ * length: number of bytes in array
+ * wait: boolean indicating to wait for write or not
+ * Output 0 .. success
+ * 1 .. length to long for buffer
+ * 2 .. address send, NACK received
+ * 3 .. data send, NACK received
+ * 4 .. other twi error (lost bus arbitration, bus error, ..)
+ */
+uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop)
+{
+ uint8_t i;
+ twi_error = TWI_ERRROR_NO_ERROR;
+ twi_sendStop = sendStop;
+
+#ifdef __MSP430_HAS_USI__
+ /* Disable START condition interrupt */
+ USICTL1 &= ~USISTTIE;
+ /* I2C master mode */
+ USICTL0 |= USIMST;
+#endif
+#ifdef __MSP430_HAS_USCI__
+ UCB0CTL1 = UCSWRST; // Enable SW reset
+ UCB0CTL1 |= UCSSEL_2; // SMCLK
+ UCB0CTL0 |= (UCMST | UCMODE_3 | UCSYNC); // I2C Master, synchronous mode
+ UCB0CTL1 |= UCTR; // Configure in transmit mode
+ UCB0I2CSA = address; // Set Slave Address
+ UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
+ UCB0I2CIE |= (UCALIE|UCNACKIE|UCSTPIE); // Enable I2C interrupts
+ UC0IE |= UCB0TXIE; // Enable I2C interrupts
+#endif
+#ifdef __MSP430_HAS_EUSCI_B0__
+ UCB0CTLW0 = UCSWRST; // Enable SW reset
+ UCB0CTLW0 |= (UCMST | UCMODE_3 | UCSSEL__SMCLK | UCSYNC); // I2C Master, synchronous mode
+ UCB0CTLW0 |= UCTR; // Configure in transmit mode
+ UCB0I2CSA = address; // Set Slave Address
+ UCB0CTLW0 &= ~UCSWRST; // Clear SW reset, resume operation
+ UCB0IE |= (UCTXIE0|UCALIE|UCNACKIE|UCSTPIE); // Enable I2C interrupts
+#endif
+ if(length == 0) {
+ return 0;
+ }
+
+ /* Ensure data will fit into buffer */
+ if(length > TWI_BUFFER_LENGTH){
+ return TWI_ERROR_BUF_TO_LONG;
+ }
+
+
+ /* initialize buffer iteration vars */
+ twi_masterBufferIndex = 0;
+ twi_masterBufferLength = length;
+
+ for(i = 0; i < length; ++i){
+ twi_masterBuffer[i] = data[i];
+ }
+
+#ifdef __MSP430_HAS_USI__
+ /* build sla+w, slave device address + w bit */
+ twi_slarw = 0;
+ twi_slarw |= address << 1;
+
+ twi_state = TWI_SND_START;
+ /* This will trigger an interrupt kicking off the state machine in the isr */
+ USICTL1 |= USIIFG;
+#endif
+#ifdef __MSP430_HAS_USCI__
+ twi_state = TWI_MTX; // Master Transmit mode
+ UCB0CTL1 |= UCTXSTT; // I2C start condition
+#endif
+#ifdef __MSP430_HAS_EUSCI_B0__
+ twi_state = TWI_MTX; // Master Transmit mode
+ while (UCB0CTLW0 & UCTXSTP); // Ensure stop condition got sent
+ UCB0CTLW0 |= UCTXSTT; // I2C start condition
+#endif
+
+ /* Wait for the transaction to complete */
+ while(twi_state != TWI_IDLE) {
+ __bis_SR_register(LPM0_bits);
+ }
+
+#ifdef __MSP430_HAS_USCI__
+ /* Ensure stop condition got sent before we exit. */
+ while (UCB0CTL1 & UCTXSTP);
+#endif
+
+ return twi_error;
+}
+
+/*
+ * Function twi_transmit
+ * Desc fills slave tx buffer with data
+ * must be called in slave tx event callback
+ * Input data: pointer to byte array
+ * length: number of bytes in array
+ * Output 1 length too long for buffer
+ * 2 not slave transmitter
+ * 0 ok
+ */
+uint8_t twi_transmit(const uint8_t* data, uint8_t length)
+{
+ uint8_t i;
+
+ twi_state = TWI_STX; // Slave transmit mode
+
+ // ensure data will fit into buffer
+ if(TWI_BUFFER_LENGTH < length){
+ return 1;
+ }
+ // set length and copy data into tx buffer
+ twi_txBufferLength = length;
+ for(i = 0; i < length; ++i){
+ twi_txBuffer[i] = data[i];
+ }
+
+ return 0;
+}
+
+/*
+ * Function twi_attachSlaveRxEvent
+ * Desc sets function called before a slave read operation
+ * Input function: callback function to use
+ * Output none
+ */
+void twi_attachSlaveRxEvent( void (*function)(uint8_t*, int) )
+{
+ twi_onSlaveReceive = function;
+}
+
+/*
+ * Function twi_attachSlaveTxEvent
+ * Desc sets function called before a slave write operation
+ * Input function: callback function to use
+ * Output none
+ */
+void twi_attachSlaveTxEvent( void (*function)(void) )
+{
+ twi_onSlaveTransmit = function;
+}
+
+void send_start()
+{
+#ifdef __MSP430_HAS_USI__
+ USISRL = 0x00;
+ USICTL0 |= USIGE+USIOE;
+ USICTL0 &= ~USIGE;
+ USISRL = twi_slarw;
+ USICNT = (USICNT & 0xE0) + 0x08;
+#endif
+}
+
+#ifdef __MSP430_HAS_USI__
+__attribute__((interrupt(USI_VECTOR)))
+void USI_ISR(void)
+{
+ if (!(USICTL0 & USIMST) && (USICTL1 & USISTTIFG)) {
+ twi_state = TWI_SL_START;
+ }
+
+ switch(twi_state){
+ /* Master transmit / receive */
+ case TWI_SND_START:
+ send_start();
+ twi_state = TWI_PREP_SLA_ADDR_ACK;
+ break;
+ case TWI_PREP_SLA_ADDR_ACK: // reveive (N)ACK
+ USICTL0 &= ~USIOE; // SDA = input
+ USICNT |= 0x01; // Bit counter=1
+ twi_state = TWI_MT_PROC_ADDR_ACK;
+ break;
+ case TWI_MT_PROC_ADDR_ACK:
+ if (USISRL & 0x01) {
+ twi_error = TWI_ERROR_ADDR_NACK;
+ USICTL0 |= USIOE;
+ USISRL = 0x00;
+ USICNT |= 0x01;
+ twi_state = TWI_EXIT;
+ break;
+ }
+
+ if(twi_slarw & 1)
+ goto mtre;
+ else
+ goto mtpd;
+
+ break;
+ /* Prepare to receive data (N)ACK */
+ case TWI_MT_PREP_DATA_ACK:
+ /* SDA = input */
+ USICTL0 &= ~USIOE;
+ /* Bit counter = 1 */
+ USICNT |= 0x01;
+ twi_state = TWI_MT_PROC_DATA_ACK;
+ break;
+ case TWI_MT_PROC_DATA_ACK:
+mtpd:
+ if (USISRL & 0x01) {
+ twi_error = TWI_ERROR_DATA_NACK;
+ USICTL0 |= USIOE;
+ USISRL = 0x00;
+ USICNT |= 0x01;
+ twi_state = TWI_EXIT;
+ break;
+ }
+
+ if(twi_masterBufferIndex == twi_masterBufferLength) {
+ USICTL0 |= USIOE;
+ USISRL = 0x00;
+ USICNT |= 0x01;
+ twi_state = TWI_EXIT;
+ break;
+ }
+
+ USICTL0 |= USIOE;
+ USISRL = twi_masterBuffer[twi_masterBufferIndex++];
+ USICNT |= 0x08;
+ twi_state = TWI_MT_PREP_DATA_ACK;
+ break;
+ // Master receiver
+mtre:
+ case TWI_MR_PREP_DATA_RECV:
+ /* SDA input */
+ USICTL0 &= ~USIOE;
+ /* bit counter = 8 */
+ USICNT |= 0x08;
+ twi_state = TWI_MR_PROC_DATA_RECV;
+ break;
+ case TWI_MR_PROC_DATA_RECV:
+ /* SDA output */
+ USICTL0 |= USIOE;
+
+ twi_masterBuffer[twi_masterBufferIndex++] = USISRL;
+ if(twi_masterBufferIndex > twi_masterBufferLength ) {
+ USISRL = 0xFF; // that was the last byte send NACK
+ twi_state = TWI_MR_PREP_STOP;
+ } else {
+ USISRL = 0x00; // keep on receiving and send ACK
+ twi_state = TWI_MR_PREP_DATA_RECV;
+ }
+
+ USICNT |= 0x01;
+ break;
+ case TWI_MR_PREP_STOP:
+ USICTL0 |= USIOE;
+ USISRL = 0x00;
+ USICNT |= 0x01;
+ twi_state = TWI_EXIT;
+ break;
+ /* All */
+ case TWI_EXIT:
+ /* Load FF into output shift register */
+ USISRL = 0x0FF;
+ /* Output latch transparant. MSB of USISR to the SDO pin. */
+ USICTL0 |= USIGE;
+ /* Latch disabled and make SDA input */
+ USICTL0 &= ~(USIGE+USIOE);
+ twi_state = TWI_IDLE;
+ break;
+ case TWI_IDLE:
+ /* Nothing to do. Fall thru and clear interrupt flag. */
+ default:
+ break;
+ }
+
+ /* Clear counter interrupt */
+ USICTL1 &= ~USIIFG;
+
+}
+#endif /* __MSP430_HAS_USI__ */
+
+#ifdef __MSP430_HAS_USCI__
+void i2c_txrx_isr(void) // RX/TX Service
+{
+ /* USCI I2C mode. USCI_B0 receive interrupt flag.
+ * UCB0RXIFG is set when UCB0RXBUF has received a complete character. */
+ if (UC0IFG & UCB0RXIFG){
+ /* Master receive mode. */
+ if (twi_state == TWI_MRX) {
+ twi_masterBuffer[twi_masterBufferIndex++] = UCB0RXBUF;
+ if(twi_masterBufferIndex == twi_masterBufferLength )
+ /* Only one byte left. Generate STOP condition.
+ * In master mode a STOP is preceded by a NACK */
+ UCB0CTL1 |= UCTXSTP;
+ if(twi_masterBufferIndex > twi_masterBufferLength ) {
+ /* All bytes received. We are idle*/
+ __bic_SR_register(LPM0_bits);
+ twi_state = TWI_IDLE;
+ }
+ /* Slave receive mode. (twi_state = TWI_SRX) */
+ } else {
+ // if there is still room in the rx buffer
+ if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){
+ // put byte in buffer and ack
+ twi_rxBuffer[twi_rxBufferIndex++] = UCB0RXBUF;
+ }else{
+ // otherwise nack
+ UCB0CTL1 |= UCTXNACK; // Generate NACK condition
+ }
+ }
+ }
+ /* USCI I2C mode. USCI_B0 transmit interrupt flag.
+ * UCB0TXIFG is set when UCB0TXBUF is empty.*/
+ if (UC0IFG & UCB0TXIFG){
+ /* Master transmit mode */
+ if (twi_state == TWI_MTX) {
+ // if there is data to send, send it, otherwise stop
+ if(twi_masterBufferIndex < twi_masterBufferLength){
+ // Copy data to output register and ack.
+ UCB0TXBUF = twi_masterBuffer[twi_masterBufferIndex++];
+ }else{
+ if (twi_sendStop) {
+ /* All done. Generate STOP condition and IDLE */
+ UCB0CTL1 |= UCTXSTP;
+ twi_state = TWI_IDLE;
+ __bic_SR_register(LPM0_bits);
+ } else {
+ twi_inRepStart = true; // we're gonna send the START
+ // don't enable the interrupt. We'll generate the start, but we
+ // avoid handling the interrupt until we're in the next transaction,
+ // at the point where we would normally issue the start.
+ UCB0CTL1 |= UCTXSTT;
+ twi_state = TWI_IDLE;
+ __bic_SR_register(LPM0_bits);
+ }
+ }
+ /* Slave transmit mode (twi_state = TWI_STX) */
+ } else {
+ // copy data to output register
+ UCB0TXBUF = twi_txBuffer[twi_txBufferIndex++];
+ // if there is more to send, ack, otherwise nack
+ if(twi_txBufferIndex < twi_txBufferLength){
+ }else{
+ UCB0CTL1 |= UCTXNACK; // Generate NACK condition
+ }
+ }
+ }
+}
+
+void i2c_state_isr(void) // I2C Service
+{
+ /* Arbitration lost interrupt flag */
+ if (UCB0STAT & UCALIFG) {
+ UCB0STAT &= ~UCALIFG;
+ /* TODO: Handle bus arbitration lost */
+ }
+ /* Not-acknowledge received interrupt flag.
+ * UCNACKIFG is automatically cleared when a START condition is received.*/
+ if (UCB0STAT & UCNACKIFG) {
+ UCB0STAT &= ~UCNACKIFG;
+ UCB0CTL1 |= UCTXSTP;
+ twi_state = TWI_IDLE;
+ /* TODO: This can just as well be an address NACK.
+ * Figure out a way to distinguish between ANACK and DNACK */
+ twi_error = TWI_ERROR_DATA_NACK;
+ __bic_SR_register(LPM0_bits);
+ }
+ /* Start condition interrupt flag.
+ * UCSTTIFG is automatically cleared if a STOP condition is received. */
+ if (UCB0STAT & UCSTTIFG) {
+ UCB0STAT &= ~UCSTTIFG;
+ /* UCTR is automagically set by the USCI module upon a START condition. */
+ if (UCB0CTL1 & UCTR) {
+ /* Slave TX mode. */
+ twi_state = TWI_STX;
+ /* Ready the tx buffer index for iteration. */
+ twi_txBufferIndex = 0;
+ /* Set tx buffer length to be zero, to verify if user changes it. */
+ twi_txBufferLength = 0;
+ /* Request for txBuffer to be filled and length to be set. */
+ /* note: user must call twi_transmit(bytes, length) to do this */
+ twi_onSlaveTransmit();
+ /* If they didn't change buffer & length, initialize it
+ * TODO: Is this right? Shouldn't we reply with a NACK if there is no data to send? */
+ if (0 == twi_txBufferLength) {
+ twi_txBufferLength = 1;
+ twi_txBuffer[0] = 0x00;
+ }
+ } else {
+ /* Slave receive mode. */
+ twi_state = TWI_SRX;
+ /* Indicate that rx buffer can be overwritten and ACK */
+ twi_rxBufferIndex = 0;
+ }
+ }
+ /* Stop condition interrupt flag.
+ * UCSTPIFG is automatically cleared when a START condition is received. */
+ if (UCB0STAT & UCSTPIFG) {
+ UCB0STAT &= ~UCSTPIFG;
+ if (twi_state == TWI_SRX) {
+ /* Callback to user defined callback */
+ twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex);
+ }
+ twi_state = TWI_IDLE;
+ }
+}
+#endif
+
+#ifdef __MSP430_HAS_EUSCI_B0__
+__attribute__((interrupt(USCI_B0_VECTOR)))
+void USCI_B0_ISR(void)
+{
+// switch(twi_state){
+ switch(UCB0IV){
+ case USCI_NONE: // No Interrupt pending
+ break;
+ case USCI_I2C_UCALIFG: // USCI I2C Mode: UCALIFG
+ // enter slave transmitter mode
+ twi_state = TWI_STX;
+ // ready the tx buffer index for iteration
+ twi_txBufferIndex = 0;
+ // set tx buffer length to be zero, to verify if user changes it
+ twi_txBufferLength = 0;
+ // request for txBuffer to be filled and length to be set
+ // note: user must call twi_transmit(bytes, length) to do this
+ twi_onSlaveTransmit();
+ // if they didn't change buffer & length, initialize it
+ if(0 == twi_txBufferLength){
+ twi_txBufferLength = 1;
+ twi_txBuffer[0] = 0x00;
+ }
+ // transmit first byte from buffer, fall
+ // copy data to output register
+ UCB0TXBUF = twi_txBuffer[twi_txBufferIndex++];
+ // if there is more to send, ack, otherwise nack
+ if(twi_txBufferIndex < twi_txBufferLength){
+ //twi_reply(1);
+ }else{
+ UCB0CTLW0 |= UCTXNACK; // Generate NACK condition
+ }
+ // leave slave receiver state
+ twi_state = TWI_IDLE;
+
+ break;
+ case USCI_I2C_UCNACKIFG: // USCI I2C Mode: UCNACKIFG
+ // leave slave receiver state
+ twi_state = TWI_IDLE;
+ twi_error = TWI_ERROR_DATA_NACK;
+ __bic_SR_register_on_exit(CPUOFF); // Exit LPM0
+ break;
+ case USCI_I2C_UCSTTIFG: // USCI I2C Mode: UCSTTIFG
+ UCB0IFG &= ~UCSTTIFG;
+ if (twi_state == TWI_IDLE){
+ if (UCB0CTLW0 & UCTR){
+ twi_state = TWI_STX; // Slave Transmit mode
+ // ready the tx buffer index for iteration
+ twi_txBufferIndex = 0;
+ // set tx buffer length to be zero, to verify if user changes it
+ twi_txBufferLength = 0;
+ // request for txBuffer to be filled and length to be set
+ // note: user must call twi_transmit(bytes, length) to do this
+ twi_onSlaveTransmit();
+ // if they didn't change buffer & length, initialize it
+ if(0 == twi_txBufferLength){
+ twi_txBufferLength = 1;
+ twi_txBuffer[0] = 0x00;
+ }
+ }else{
+ twi_state = TWI_SRX; // Slave receive mode
+ // indicate that rx buffer can be overwritten and ack
+ twi_rxBufferIndex = 0;
+ }
+ }
+ break;
+ case USCI_I2C_UCSTPIFG: // USCI I2C Mode: UCSTPIFG
+ UCB0IFG &= ~UCSTPIFG;
+ if (twi_state == TWI_SRX){
+ // callback to user defined callback
+ twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex);
+ }
+ twi_state = TWI_IDLE; // IDLE mode
+ /* Work around for:
+ * If the master does a read and then a write the START interrupt occurs
+ * but the RX interrupt never fires. Clearing bit 4 and 5 of UCB0CTLW0 solves this.
+ * bit 4 and 5 are however marked as reserved in the datasheet.
+ */
+ UCB0CTLW0 &= ~0x18;
+
+ __bic_SR_register_on_exit(CPUOFF); // Exit LPM0
+ break;
+ case USCI_I2C_UCRXIFG3: // USCI I2C Mode: UCRXIFG3
+ break;
+ case USCI_I2C_UCTXIFG3: // USCI I2C Mode: UCTXIFG3
+ break;
+ case USCI_I2C_UCRXIFG2: // USCI I2C Mode: UCRXIFG2
+ break;
+ case USCI_I2C_UCTXIFG2: // USCI I2C Mode: UCTXIFG2
+ break;
+ case USCI_I2C_UCRXIFG1: // USCI I2C Mode: UCRXIFG1
+ break;
+ case USCI_I2C_UCTXIFG1: // USCI I2C Mode: UCTXIFG1
+ break;
+ case USCI_I2C_UCRXIFG0: // USCI I2C Mode: UCRXIFG0
+ UCB0IFG &= ~UCRXIFG; // Clear USCI_B0 TX int flag
+ if (twi_state == TWI_MRX) { // Master receive mode
+ twi_masterBuffer[twi_masterBufferIndex++] = UCB0RXBUF; // Get RX data
+ if(twi_masterBufferIndex == twi_masterBufferLength )
+ UCB0CTLW0 |= UCTXSTP; // Generate I2C stop condition
+ if(twi_masterBufferIndex > twi_masterBufferLength ) {
+ twi_state = TWI_IDLE; //Idle
+ } else {
+ }
+ } else {
+ // if there is still room in the rx buffer
+ if(twi_rxBufferIndex < TWI_BUFFER_LENGTH){
+ // put byte in buffer and ack
+ twi_rxBuffer[twi_rxBufferIndex++] = UCB0RXBUF;
+ }else{
+ // otherwise nack
+ UCB0CTLW0 |= UCTXNACK; // Generate NACK condition
+ }
+ }
+ break;
+ case USCI_I2C_UCTXIFG0: // USCI I2C Mode: UCTXIFG0
+ UCB0IFG &= ~UCTXIFG; // Clear USCI_B0 TX int flag
+ if (twi_state == TWI_MTX) { // Master receive mode
+ // if there is data to send, send it, otherwise stop
+ if(twi_masterBufferIndex < twi_masterBufferLength){
+ // copy data to output register and ack
+ UCB0TXBUF = twi_masterBuffer[twi_masterBufferIndex++]; // Transmit data at address PTxData
+ }else{
+ if (twi_sendStop)
+ UCB0CTLW0 |= UCTXSTP; // Generate I2C stop condition
+ else {
+ twi_inRepStart = true; // we're gonna send the START
+ // don't enable the interrupt. We'll generate the start, but we
+ // avoid handling the interrupt until we're in the next transaction,
+ // at the point where we would normally issue the start.
+ UCB0CTLW0 |= UCTXSTT;
+ twi_state = TWI_IDLE;
+ }
+ }
+ } else {
+ // copy data to output register
+ UCB0TXBUF = twi_txBuffer[twi_txBufferIndex++];
+ // if there is more to send, ack, otherwise nack
+ if(twi_txBufferIndex < twi_txBufferLength){
+ }else{
+ UCB0CTLW0 |= UCTXNACK; // Generate NACK condition
+ }
+ }
+ break;
+ case USCI_I2C_UCBCNTIFG: // USCI I2C Mode: UCBCNTIFG
+ break;
+ case USCI_I2C_UCCLTOIFG: // USCI I2C Mode: UCCLTOIFG/
+ break;
+ case USCI_I2C_UCBIT9IFG: // USCI I2C Mode: UCBIT9IFG
+ break;
+ }
+}
+#endif
+