msp430f2013 I2C frustrations
UPDATE: See this post which shows how I finally got it working.
I’ve been trying to get a ST7032i based display working with a msp430f2013 on a EZ430-F2013. Unfortunately without much success. After hours of debugging I discovered that the glass on the display was fractured… guess I’ll be ordering some more.
Anyway, it did get as far as acknowledging my commands. And I found a working I2C sample. It was hidden pretty well on the TI site, I think in a zip called slac080l.zip. The file I was working from was msp430x20x3_usi_07.c. My hacked version is reproduced below, you can almost feel the frustration I’m sure (it’s an absolute mess). But it does work with the MSP430GCC compiler, and produce working I2C output.
/* --COPYRIGHT--,BSD_EX * Copyright (c) 2012, Texas Instruments Incorporated * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Texas Instruments Incorporated nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ******************************************************************************* * * MSP430 CODE EXAMPLE DISCLAIMER * * MSP430 code examples are self-contained low-level programs that typically * demonstrate a single peripheral function or device feature in a highly * concise manner. For this the code may rely on the device's power-on default * register values and settings such as the clock configuration and care must * be taken when combining code from several examples to avoid potential side * effects. Also see www.ti.com/grace for a GUI- and www.ti.com/msp430ware * for an API functional library-approach to peripheral configuration. * * --/COPYRIGHT--*/ //****************************************************************************** // MSP430F20xx Demo - I2C Master Transmitter, single byte // // Description: I2C Master communicates with I2C Slave using // the USI. Master data is sent and increments from 0x00 with each transmitted // byte which is verified by the slave. // LED off for address or data Ack; LED on for address or data NAck. // ACLK = n/a, MCLK = SMCLK = Calibrated 1MHz // // ***THIS IS THE MASTER CODE*** // // Slave Master // (msp430x20x3_usi_08.c) // MSP430F20x2/3 MSP430F20x2/3 // ----------------- ----------------- // /|\| 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 // // Z. Albus // Texas Instruments Inc. // May 2006 // Built with CCE Version: 3.2.0 and IAR Embedded Workbench Version: 3.41A //****************************************************************************** #include <msp430.h> // commands #define LCD_CLEARDISPLAY 0x01 #define LCD_RETURNHOME 0x02 #define LCD_ENTRYMODESET 0x04 #define LCD_DISPLAYCONTROL 0x08 #define LCD_CURSORSHIFT 0x10 #define LCD_FUNCTIONSET 0x20 #define LCD_SETCGRAMADDR 0x40 #define LCD_SETDDRAMADDR 0x80 #define LCD_EX_SETBIASOSC 0x10 // Bias selection / Internal OSC frequency adjust #define LCD_EX_SETICONRAMADDR 0x40 // Set ICON RAM address #define LCD_EX_POWICONCONTRASTH 0x50 // Power / ICON control / Contrast set(high byte) #define LCD_EX_FOLLOWERCONTROL 0x60 // Follower control #define LCD_EX_CONTRASTSETL 0x70 // Contrast set(low byte) // flags for display entry mode #define LCD_ENTRYRIGHT 0x00 #define LCD_ENTRYLEFT 0x02 #define LCD_ENTRYSHIFTINCREMENT 0x01 #define LCD_ENTRYSHIFTDECREMENT 0x00 // flags for display on/off control #define LCD_DISPLAYON 0x04 #define LCD_DISPLAYOFF 0x00 #define LCD_CURSORON 0x02 #define LCD_CURSOROFF 0x00 #define LCD_BLINKON 0x01 #define LCD_BLINKOFF 0x00 // flags for display/cursor shift #define LCD_DISPLAYMOVE 0x08 #define LCD_CURSORMOVE 0x00 #define LCD_MOVERIGHT 0x04 #define LCD_MOVELEFT 0x00 // flags for function set #define LCD_8BITMODE 0x10 #define LCD_4BITMODE 0x00 #define LCD_2LINE 0x08 #define LCD_1LINE 0x00 #define LCD_5x10DOTS 0x04 #define LCD_5x8DOTS 0x00 #define LCD_EX_INSTRUCTION 0x01 // IS: instruction table select // flags for Bias selection #define LCD_BIAS_1_4 0x08 // bias will be 1/4 #define LCD_BIAS_1_5 0x00 // bias will be 1/5 // flags Power / ICON control / Contrast set(high byte) #define LCD_ICON_ON 0x08 // ICON display on #define LCD_ICON_OFF 0x00 // ICON display off #define LCD_BOOST_ON 0x04 // booster circuit is turn on #define LCD_BOOST_OFF 0x00 // booster circuit is turn off #define LCD_OSC_122HZ 0x00 // [email protected] #define LCD_OSC_131HZ 0x01 // [email protected] #define LCD_OSC_144HZ 0x02 // [email protected] #define LCD_OSC_161HZ 0x03 // [email protected] #define LCD_OSC_183HZ 0x04 // [email protected] #define LCD_OSC_221HZ 0x05 // [email protected] #define LCD_OSC_274HZ 0x06 // [email protected] #define LCD_OSC_347HZ 0x07 // [email protected] // flags Follower control #define LCD_FOLLOWER_ON 0x08 // internal follower circuit is turn on #define LCD_FOLLOWER_OFF 0x00 // internal follower circuit is turn off #define LCD_RAB_1_00 0x00 // 1+(Rb/Ra)=1.00 #define LCD_RAB_1_25 0x01 // 1+(Rb/Ra)=1.25 #define LCD_RAB_1_50 0x02 // 1+(Rb/Ra)=1.50 #define LCD_RAB_1_80 0x03 // 1+(Rb/Ra)=1.80 #define LCD_RAB_2_00 0x04 // 1+(Rb/Ra)=2.00 #define LCD_RAB_2_50 0x05 // 1+(Rb/Ra)=2.50 #define LCD_RAB_3_00 0x06 // 1+(Rb/Ra)=3.00 #define LCD_RAB_3_75 0x07 // 1+(Rb/Ra)=3.75 char MST_Data = 0x40; // Variable for transmitted data char SLV_Addr = 0x7C; // Address is 0x48 << 1 bit + 0 for Write int I2C_State = 0; // State variable int main(void) { volatile unsigned int i; // Use volatile to prevent removal WDTCTL = WDTPW + WDTHOLD; // Stop watchdog __delay_cycles(250000); 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, others to 0 P1REN |= 0xC0; // P1.6 & P1.7 Pullups P1DIR = 0xFF; // Unused pins as outputs P2OUT = 0; P2DIR = 0xFF; USICTL0 = USIPE6+USIPE7+USIMST+USISWRST; // Port & USI mode setup USICTL1 = USII2C+USIIE; // Enable I2C mode & USI interrupt USICKCTL = USIDIV_3+USISSEL_2+USICKPL; // Setup USI clocks: SCL = SMCLK/8 (~125kHz) // was USIDIV_3 USICNT |= USIIFGCC; // Disable automatic clear control USICTL0 &= ~USISWRST; // Enable USI USICTL1 &= ~USIIFG; // Clear pending flag __enable_interrupt(); int a=0; for(int n=0;;n++) { if(n==0) MST_Data = 0x00; if(n==1) MST_Data = LCD_EX_SETBIASOSC | LCD_BIAS_1_5 | LCD_OSC_183HZ; if(n==2) MST_Data = 0x00; if(n==3) MST_Data = LCD_EX_FOLLOWERCONTROL | LCD_FOLLOWER_ON | LCD_RAB_2_00; if(n==4) MST_Data = 0x00; if(n==5) MST_Data = LCD_DISPLAYON | LCD_CURSORON | LCD_BLINKON; if(n==6) MST_Data = 0x00; if(n==7) MST_Data = LCD_ENTRYMODESET | LCD_ENTRYLEFT; if(n==9) MST_Data = 0x00; if(n==10) MST_Data = LCD_EX_CONTRASTSETL | (0x0c & 0x0f); if(n==11) MST_Data = 0x00; if(n==12) MST_Data = LCD_EX_POWICONCONTRASTH | LCD_ICON_ON | LCD_BOOST_ON | ((0x0c >> 4) & 0x03); if(n==14) MST_Data = 0x00; if(n==15) MST_Data = LCD_CLEARDISPLAY; if(n==16) MST_Data = 0x00; if(n==17) MST_Data = 0x40; if(n==18) MST_Data = 'a'+a; USICTL1 |= USIIFG; // Set flag and start communication LPM0; // CPU off, await USI interrupt // __no_operation(); // Used for IAR // for (i = 0; i < 5000; i++); // Dummy delay between communication cycles P1OUT &= ~0x01; // LED off __delay_cycles(500000); if(n==18)n=16; a++; } } /****************************************************** // USI interrupt service routine ******************************************************/ #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector = USI_VECTOR __interrupt void USI_TXRX (void) #elif defined(__GNUC__) void __attribute__ ((interrupt(USI_VECTOR))) USI_TXRX (void) #else #error Compiler not supported! #endif { switch(I2C_State) { case 0: // Generate Start Condition & send address to slave P1OUT &= ~0x01; // LED off USISRL = 0x00; // Generate Start Condition... USICTL0 |= USIGE+USIOE; USICTL0 &= ~USIGE; USISRL = SLV_Addr; // ... and transmit address, R/W = 0 USICNT = (USICNT & 0xE0) + 0x08; // Bit counter = 8, TX Address I2C_State = 2; // Go to next state: receive address (N)Ack break; case 2: // Receive Address Ack/Nack bit USICTL0 &= ~USIOE; // SDA = input USICNT |= 0x01; // Bit counter = 1, receive (N)Ack bit I2C_State = 4; // Go to next state: check (N)Ack break; case 4: // Process Address Ack/Nack & handle data TX USICTL0 |= USIOE; // SDA = output if (USISRL & 0x01) // If Nack received... { // Send stop... USISRL = 0x00; USICNT |= 0x01; // Bit counter = 1, SCL high, SDA low I2C_State = 10; // Go to next state: generate Stop // P1OUT |= 0x01; // Turn on LED: error P1OUT |= 0x01; // Turn on LED: error __delay_cycles(5000000); } else { // Ack received, TX data to slave... USISRL = MST_Data; // Load data byte USICNT |= 0x08; // Bit counter = 8, start TX I2C_State = 6; // Go to next state: receive data (N)Ack P1OUT &= ~0x01; // Turn off LED } break; case 6: // Receive Data Ack/Nack bit USICTL0 &= ~USIOE; // SDA = input USICNT |= 0x01; // Bit counter = 1, receive (N)Ack bit I2C_State = 8; // Go to next state: check (N)Ack break; case 8: // Process Data Ack/Nack & send Stop USICTL0 |= USIOE; if (USISRL & 0x01) // If Nack received... ;// P1OUT |= 0x01; // Turn on LED: error else // Ack received { MST_Data++; // Increment Master data // P1OUT &= ~0x01; // Turn off LED } // Send stop... USISRL = 0x00; USICNT |= 0x01; // Bit counter = 1, SCL high, SDA low I2C_State = 10; // Go to next state: generate Stop break; case 10:// Generate Stop Condition USISRL = 0x0FF; // USISRL = 1 to release SDA USICTL0 |= USIGE; // Transparent latch enabled USICTL0 &= ~(USIGE+USIOE);// Latch/SDA output disabled I2C_State = 0; // Reset state machine for next transmission LPM0_EXIT; // Exit active for next transfer break; } USICTL1 &= ~USIIFG; // Clear pending flag }