//******************************************************************************// I2C Master// double byte read// single byte write//// 5 OCT 2010// Comments updated 27 NOV 2010 but still incomplete.// free to use for non-commercial use with attribution.// Some parts from or inspired by TI example code by// Z. Albus, R. B. Elliott and H. Grewal.//// Posted as requested in the TI Launchpad Group// http://groups.google.com/group/ti-launchpad/topics//// About:// Inits a 2x16 display, inits the USI and continually polls the slave for data// to display. In this demo mode it writes a byte to the slave after 50 read transactions// (every 50 start conditions not every 50 bytes) Only accepts 2 bytes from the slave// because I lost my variable byte code. First byte is displayed on the first line and // the second byte on the second line right after each is recieved. The display routines // are called from within the USI ISR which I'm sure has a performance penalty especially // in data transfer rate.// See below for more on display routines and pinout. Could be repurposed easily.// See also: slave code at http://pastebin.ca/2004021//// To do:// Get the various intervals off of a wait loop and onto a timer interrupt// Redevelop the variable length code// // Compiles with CCS to ____bytes flash and uses ____ bytes RAM//******************************************************************************#include <msp430g2231.h>//My display routinesvoid formatAndSend(char data);void dispClear(int arg);void dispSend4(char data);void dispSend8(char data);void dispGo(int row, int col);void wait(int max);char rs=0; //(0,1) register select for displaychar SLV_data = 0x00;char SLV_Addr = 0x50; // Address is 0x48 << 1 bit + 1 for Readchar toggle1; // just a var to switch between first and second bytechar RWbit = 1; // (0,1) 0= writeto slave, 1= read from slavechar counter1=0; //just a counter to insert write cycles for funint I2C_State = 0; // State variablevoid main(void){ volatile unsigned int i; // Use volatile to prevent removal //Clocks WDTCTL = WDTPW + WDTHOLD; // Stop watchdog if (CALBC1_1MHZ ==0xFF || CALDCO_1MHZ == 0xFF){while(1);} // If calibration constants are erased do not load, trap CPU!! BCSCTL1 = CALBC1_1MHZ; // Set DCO DCOCTL = CALDCO_1MHZ; //Pin Setup P1SEL &= 0x06; //clear 39, don't care 0xC0 P1REN = 0xC6; //set 0xC0, clear 0x39 P1DIR |= 0x39; //set 0x39 P2SEL = 0x00; //clear 0xC0 P2DIR = 0xFF; //set 0xC0 P2REN = 0x00; //clear 0xC0 //Display init rs=0; dispSend4(0x20); dispSend8(0x28); dispSend8(0x0e); dispSend8(0x06); rs=1; //Display form dispSend8('1'); dispSend8(':'); dispGo(2,1); dispSend8('2'); dispSend8(':'); dispGo(1,1); //USI init 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 (~120kHz) USICNT |= USIIFGCC; // Disable automatic clear control USICTL0 &= ~USISWRST; // Enable USI USICTL1 &= ~USIIFG; // Clear pending flag _EINT(); while(1){ counter1++; //Counter1 is used to send a write to the Slave once every 50 transactions if(counter1 > 49){ counter1=0; RWbit = 0; SLV_Addr = 0x50; } else{ RWbit = 1; SLV_Addr = 0x51; } dispGo(1,7); formatAndSend(counter1); dispSend8(' '); formatAndSend(SLV_Addr); //Shows the slave address including RWbit USICTL1 |= USIIFG; // Set flag and start communication LPM0; // CPU off, await USI interrupt _NOP(); // Used for IAR for (i = 0; i < 5000; i++); // Dummy delay between communication cycles }}/******************************************************// USI interrupt service routine//// States Function// ----------------- -------------// 0 - 2 - 4 setup always happens// \// 10 nack recieved, end// or// 18 - 20 - 10 Write byte to slave// or// 6 - 14 - 16 - 8 - 10 read bytes from slave//// The main thing to remember about the USI ISR is that it is // most often triggered by the bit counter running out.//*****************************************************/#pragma vector = USI_VECTOR__interrupt void USI_TXRX (void){ switch(I2C_State){ case 0: //Generate start condition //Prep address to send //case 0 is manually tiggered by setting the interrupt flag USISRL = 0x00; // Generate Start Condition USICTL0 |= USIGE+USIOE; USICTL0 &= ~USIGE; USISRL = SLV_Addr;//(SLV_Addr | RWbit); // Build and transmit address, R/W = 1 USICNT = (USICNT & 0xE0) + 0x08; // Bit counter = 8, TX Address I2C_State = 2; // Go to next state: receive address (N)Ack break; case 2: //Byte(address + R/W) just sent //prep to recieve bit from slave, ack or nack 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: //bit just recieved //RWbit=1->Read //Ack ->prep to receive byte and continue communication //Nack->prep for issuing STOP condition //RWbit=0->Write //prep to send byte if (RWbit == 1){ //Read Code if (USISRL & 0x01){ // If Nack received Prep Stop Condition USICTL0 |= USIOE; USISRL = 0x00; USICNT |= 0x01; // Bit counter = 1, SCL high, SDA low I2C_State = 10; // Go to next state: } else{ // Ack received, Receive Data from slave USICNT |= 0x08; // Bit counter = 8, RX data I2C_State = 6; // Go to next state: } } else{ //Write Code USISRL = 77; USICTL0 |= USIOE; // SDA = output USICNT |= 0x08; // TX a byte I2C_State = 18; } break; case 6: //First Byte just recieved //Formated display of data //Prep to send N/Ack bit USICTL0 |= USIOE; // SDA = output dispGo(1,3); formatAndSend(USISRL); USICNT |= 0x01; // Bit counter = 1, send (N)Ack bit I2C_State = 14; // Go to next state break; case 8: //Bit just sent //Prep Stop Condition USICTL0 |= USIOE; // SDA = output USISRL = 0x00; USICNT |= 0x01; // Bit counter = 1, SCL high, SDA low I2C_State = 10; // Go to next state: generate Stop break; case 10: //coming from case 8 or 4 or __ //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; case 14: //Just sent bit //Prep to recieve second byte USICTL0 &= ~USIOE; // SDA = input USICNT |= 0x08; // Bit counter = 8, RX data I2C_State = 16; // Go to next state: Test data and (N)Ack break; case 16: //Second Byte just recieved //Formated display of data //Prep to send N/Ack bit USICTL0 |= USIOE; // SDA = output dispGo(2,3); formatAndSend(USISRL); USICNT |= 0x01; // Bit counter = 1, send (N)Ack bit I2C_State = 8; // Go to next state: prep stop break; case 18: //Just wrote byte to slave //prep to recieve bit USICTL0 &= ~USIOE; // SDA = input USICNT |= 0x01; // Bit counter = 1, receive (N)Ack bit I2C_State = 20; break; case 20: //just recieved bit //prep to stop USICTL0 |= USIOE; USISRL = 0x00; USICNT |= 0x01; // Bit counter = 1, SCL high, SDA low I2C_State = 10; // Go to next state: break; } USICTL1 &= ~USIIFG; // Clear pending flag}//******************************************************************************// Display routines for use with a HD44780 series display driver chip// My display uses a HD44780A00//// PINOUT// P1.0 E enable/clock// P1.3 RS register select ,1=char data, 0=commands// P1.4 DB4 data LSB// P1.5 DB5// P2.6 DB6// P2.7 DB7 data MSB//******************************************************************************void formatAndSend(char data){//To turns an 8-bit unsigned integer value into //three decimal digits and sends to display char temp; temp=(data/100)+0x30; dispSend8(temp); temp=((data/10)%10)+0x30; dispSend8(temp); temp=(data%10)+0x30; dispSend8(temp);}void dispClear(int arg){//arg Action//--- ------------------// 0 clear whole screen// 1 clear first line// 2 clear second line//else clear whole screenchar i=0;switch (arg){ case 1: dispGo(1,1); for(i=0;i<16; i++){ dispSend8(0x20);} dispGo(1,1); break; case 2: dispGo(2,1); for(i=0;i<16; i++){ dispSend8(0x20);} dispGo(2,1); break; case 0: default: rs = 0; dispSend8(0x01); rs = 1; dispGo(1,1); break; }}void dispGo(int row, int col){//Move cursor to row and columnchar send=0; send=(((row-1)*0x40)+(col-1))|0x80; rs=0; dispSend8(send); rs=1;}void dispSend4(char data){//takes the 4 MSB bits of data and formats for //this pin set up int pulsewidth; if (rs==1) {pulsewidth=10;P1OUT |= 0x08;}//set the clock rate and set the RS line else {pulsewidth=50;P1OUT &= 0xf7;} //set up the data output P1OUT = ((0x30 & data) | (0xcf & P1OUT)); P2OUT = data; //six bits have no pins //Pulse the E line wait(pulsewidth); //wait time P1OUT |= 0x01; //clock high wait(pulsewidth); //wait time P1OUT ^= 0x01; //clock low}void dispSend8(char data){//breaks 8 bits into two 4 bit chunks for sending to the display//lower 4 bits of sent data are ignored dispSend4(data); data= data<<4; dispSend4(data); }void wait(int max){ int i=0; for(i=0;i<max; i++); //wait time }