I2C using USI on the MSP430
2014-01-09
I've released a tiny library that implements I2C master functionality for MSP430 chips that have the USI module (MSP430G2412 or MSP430G2452 are ones that I use often).
The code is on GitHub, it is MIT-licensed, so you can do whatever you want with it.
From the README:
Features
- Small.
- Works.
- Reads and writes.
- Implements repeated start.
- Uses the Bus Pirate convention.
Rationale
I wrote this out of frustration. There is lots of code floating around, most of which I didn't like. TI supplies examples which seem to have been written by an intern and never looked at again. The examples are overly complex, unusable in practical applications, ugly and badly formatted, and sometimes even incorrect.
The MSP430G2xx2 devices are tiny and inexpensive and could be used in many application requiring I2C, but many people avoid them because it is so annoyingly difficult to use I2C with the USI module.
This code is very, very loosely based on the msp430g2xx2usi16.c example from TI, but if you compare you will notice that:
- the state machine is different (simpler): see doc/usi-i2c-state-diagram.pdf for details,
- it actually has a useful interface,
- it is smaller.
Limitations
This is a simple I2C master that needs to fit on devices that have 128 bytes of RAM, so scale your expectations accordingly. There is no error detection, no arbitration loss detection, only master mode is implemented. Addressing is fully manual: it is your responsibility to shift the 7-bit I2C address to the left and add the R/W bit.
Have fun!
GREAT JOB MAN! Very nice library, simple and working. You saved my life...
Hi Jan, first of all thank you for the library, very useful.
I had to experiment little bit in order to figure out how to use it but now everything works fine.
In my project I have to read a sensor that returns 16 bit of data and there is a bug in the code that prevent to do that.
In the usii2c.c file, in the interrupt function Usitxrx I changed the 1 line (see TheFwGuy comment) :
..................................................................
..................................................................
case I2CRECEIVEDDATA: // received data, send ACK/NACK
*i2creceivebuffer = USISRL;
i2creceivebuffer++;
USICTL0 |= USIOE; // SDA = output
if(i2csequencelength > 1) {
// If this is not the last byte
USISRL = 0x00; // ACK
..................................................................
..................................................................
with
..................................................................
..................................................................
case I2CRECEIVEDDATA: // received data, send ACK/NACK
*i2creceivebuffer = USISRL;
i2creceivebuffer++;
USICTL0 |= USIOE; // SDA = output
if(i2csequencelength > 0) { // TheFwGuy MOD !!!
// If this is not the last byte
USISRL = 0x00; // ACK
..................................................................
..................................................................
With that modification I'm able to read 16 bits (or more) values simply placing in the sequence two I2CREAD consecutive.
Also I did ported the library to be compiled for the mspgcc, minor things.
However I moved the inline function i2cdone() outside the header file, into the code.
In the header file I left the function prototypes.
mspgcc (but also many other compilers) would try to duplicate the function if is present in the header file and the header file is included in more than 1 other file.
Thanks! It seems that this bug is a leftover from migrating my project to GitHub :-(
Will be fixed. Similarly for
i2c_done()
, this needs to be handled better.Any chance you plan on releasing a slave version of this?
No plans at the moment. It would have to be contracted work, as I have no need for an I2C slave, and most applications I can think of are commercial.