Talking to MMA7660 using I2C and ATtiny85



Introduction


I was introduced to Atmel tinyAVRs a few years ago, soon after I started playing around with the Arduino platform. Now, here was a computer I could wrap my head around. Although it lacked the bells and whistles (voltage regulator, USB support, modest number of I/O lines, etc.) of an Arduino, it was simple enough to comprehend, and in fact more than sufficient for many projects where using an Ardunio would be overkill. If you wanted to reduce the cost of your project, tinyAVR is a good candidate to look at. I have written previously on programming tinyAVRs – especially the ATtiny84 and ATtiny85. In those projects I used GCC, avrdude, and inexpensive programmers based on USBTiny. Although that does get the job done, I was sorely missing something from my many years as a software developer – a debugger. Arduino supports some manner of debugging by printing strings via the serial port, and although you could do something similar with the tinyAVRs, there are constraints due to reduce capabilities of the hardware. And blinking LEDs gets you only so far. What I was really looking for was the ability to break in the code running on the chip, and take a peek at the variables.


Since I continue to find tinyAVRs to be extermely handy for small projects, and in addition plan to explore Atmel ARM chips for my new projects, I decided to buy an Atmel ICE debugger. To make use of the ICE, you need software that supports it, and Atmel Studio seemed like the apt choice. It irks me that Atmel did not make this tool cross-platform, thanks to which I am forced to leave the comfort of my Macbook (walled garden or not, OS X gets the job done) and use my shitty HP Windows laptop (which although only two months old, is behaving like it’s in an old age home). Rather than talk about developing on Atmel Studio + ICE in the abstract, I decided to try and use it for a real project – talking to a Freescale MMA7660 accelerometer using an Atmel ATtiny85, using the I2C protocol.


Hardware & Connections


For this project, we need an ATtiny85 and an MMA7660. The latter comes in a dimunitive DFN-10 package that is hard to solder. So why did I choose it? Because it’s one of the cheapest accelerometer chips out there, and that’s critical when you are actually trying to build a product. Here is how the hardware is hooked up:


 


Debugging with Atmel Studio 7 and ICE


Now let’s take a look at what’s involved in working with Atmel ICE + Atmel Studio 7. Here’s what the hookup looks like:


 


If you are used to working with USBTiny or similar programmer for AVR, there are a few quirks to using Atmel ICE.


First of all, ICE needs to detect power – so you need to ensure that you power your project externally, and that you connect the VCC line. If you have done it right, the green LED on the left will light up on the ICE.


Next, Atmel ICE comes with two slots – one for ARM and one for AVR, and of course we want the latter for our project. We also want to use the 2×3 SPI header for programming. But the nice people at Atmel have decided to let us grapple in the dark by not including orientation information for every single plug diagram in the ICE documentation. So I’ve modified one here to make it comprehensible to us mere mortals.


 


Now, on to programming and debugging with Atmel ICE using Atmel Studio


  1. If you have used Visual Studio, the IDE should be familiar to you, because in fact Atmel Studio is powered by the same engine.

Build your program and upload it onto the hardware by selecting device programming from the Tools menu.


 


When the above dialog first comes up, you need to select ICE in Tool, ISP in Interface and hit apply. You should be able to now read the signature of the chip (for ATtiny85, it’s 0x1e930b) and see the target voltage of 3.3V. Now, select the memories tab, and you will see options for erasing and programming the chip.


Now to start debugging on chip, choose the Start debugging and break option.


 


Now you will be presented with this lovely dialog:


 


Dare to click Yes and you will be presented with an even lovelier dialog:


 


At this point you need to toggle the circuit power off and on and then click OK. And thus you have commenced debugging on the chip. Note that unless you turn some compiler optimizations off, you will not be able to watch local variables. I “solved” that problem by making everything global. Here’s a session:


 


Now this is actually cool. I can debug on a tinyAVR! Granted, such debugging is not always useful due to change in real-time behavior of microcontrollers. But at the start of a project, such debugging capability is extremely valuable, in my opinion.


Now, here comes the important part – when you quit of of debugging, you need to select the Disable DebugWire and close option:


 


If you don’t do the above, you will not be able to program the chip again. I discovered all this by trial and error and frantic web searches, and I didn’t do any permanent damage to the chip (I think) – but I advise you to exercise caution, nevertheless.


So that’s how Atmel ICE/Studio work together. Not ideal, but that’s life.


Programming


To communicate with MMA7660 from the ATtiny85, we need to use the I2C protocol. For this I used the excellent I2CMaster library from Peter Fleury. The cool thing about this library is that you can designate any I/O line as the SCL/SDA pin by modifying i2cmaster.S:


;******----- Adapt these SCA and SCL port and pin definition to your target !!
;
#define SDA             4           // SDA Port D, Pin 4   
#define SCL             5           // SCL Port D, Pin 5
#define SDA_PORT        PORTD       // SDA Port D
#define SCL_PORT        PORTD       // SCL Port D         

;******----------------------------------------------------------------------



So to write data to a register, you need to issue a start to the I2C line, do a write to the register, write the data, and then issue a stop.


And here’s code that does just that.


// MMA7660
// set data to given register
// See Figure 12. Single Byte Write - in MMA7660 datasheet
void mma7660_set_data(uint8_t reg, uint8_t data)
{
    // issue START
    i2c_start((0x4C << 1)|0x0);
    // write register
    i2c_write(reg);
    // write data
    i2c_write(data);
    // issue STOP
    i2c_stop();
}

Now here’s actual data read from the circuit during a write operation, captured using a Saleae Logic 8:


 


The above shows the data 0x0b written to 0x07, the MODE register for MMA7660.


Here’s what the datasheet says about reading data from the MMA7660:


 


So start by issuing a start on the SDA line, write the relevant register, issue a repeated start, and then read the data back from the device.


And here’s some code to do that:


// MMA7660
// get data from given register
// See Figure 14. Single Byte Read - in MMA7660 datasheet
void mma7660_get_data(uint8_t reg, uint8_t* data)
{
    // issuse START
    i2c_start((0x4C << 1)|0x0);
    // write register
    i2c_write(reg);
    // issue Repeated START
    i2c_rep_start((0x4C << 1)|0x1);
    // read data and issue STOP
    *data = i2c_readNak();
}

And here’s actual data read from the I2C lines:


 


In the above, you can see the read happening from the 0x0 – the XOUT register of the MMA7660.


The program starts by setting some data into the MMA7660:


 // set MODE to stand by
  mma7660_set_data(0x07,0x00);

  // set up SR register
  mma7660_set_data(0x08,0x00);

  // set up interrupt register
  mma7660_set_data(0x06,0b11100100);

  // tap detection reg
  mma7660_set_data(0x09,11);

  // tap debounce reg
  mma7660_set_data(0x0a,11);

  // count
  mma7660_set_data(0x05, 0xff);

  // set MODE to active
  mma7660_set_data(0x07,0b00011001);

You can refer to the MMA7660 datasheet to figure out the above settings.


Now here’s the main loop:


  // main loop
  while (1) {

    uint8_t x, y, z;
    mma7660_get_data(0x00, &x);
    mma7660_get_data(0x01, &y);
    mma7660_get_data(0x02, &z);

    ax = gLUT[x];
    ay = gLUT[y];
    az = gLUT[z];

    float aSq = ax*ax + ay*ay + az*az;

	// MMA7660 limit is +-1.5g
    if (aSq > 2.0 && aSq < 6.75) {
      // flash# 1:
      // set high
      PORTB |= (0x1 << PB2);
      _delay_ms(20);// set up MMA7660:
      // set low
      PORTB &= ~(0x1 << PB2);
    }
  }

In the above code, we retrieve data from the accelerometer, compute the magnitude of the acceleration value (its square, actually) and when that exceeds a certain threshold, we flash an LED attached to pin 7 (PB2).


So when you shake the board, the LED will light up.


Conclusion


Here are my thoughts after using the Atmel ICE with Atmel Studio 7 on a real project:


  1. Atmel has created a great combo with ICE + Studio. Once you get past the initial quirks, it’s a very useful system.
  2. I am very impressed that Atmel offers ICE in several packages, the cheapest of which (just the board) costs only USD 35. Kudos to Atmel for keeping hobbyists with a tight budget in mind.
  3. If you work with tinyAVRs, you should buy an Atmel ICE rather than struggle with cheap programmers. The combination of ICE and Studio is a much better path to development.
  4. Windows is a pain in the a$$. (Some things never change.)

Downloads


You can get the complete source code for this project here:

https://github.com/electronut/attiny85-mma7660