The I2C and SPI Education Shield features two devices on the I2C bus, the MCP7940 and AT30TS750A chips, a real time clock and temperature sensor respectively. Let’s combine the two and retrieve the temperature along side a time stamp.

Objectives

  1. Learn how to check for and handle errors.
  2. Understand the error codes that can be returned.
  3. Define the registers necessary to utilize both modules.
  4. Retrieve data from both devices in the shortest time possible.
  5. Send the output to the serial window.

Background
MCP7940 Functionality Overview
AT30TS750A Functionality Overview
Microchip MCP7940 RTC Datasheet
Atmel AT30TS750A Temperature Sensor Datasheet


Schematic
Education Shield – MCP7940 RTCC Subsystem
Education Shield – AT30TS750A Temperature Sensor Subsystem


Setup
For this module, you’ll need the following equipment:

1. Insert a CR2032 button cell battery in the battery holder on the edge of the Education Shield. It should be placed positive side up.

2. Mate the Education Shield with your Arduino UNO R3. If you’re using the MCP7940 Breakout Board with the AT30TS750A Breakout Board, for each board connect 5V to 5V, GND to GND, SCL to Analog 5, and SDA to Analog 4. The MFP pin on the MCP7940 connects to Digital3, and AL on the AT30TS750A connects to Digital2.

3. Connect your Arduino to your USB cable, and use the Arduino IDE to upload the “Bare Minimum” sketch from the Basics section of the Examples.


Arduino I2C Errors

Our architecture presents a single master on the I2C bus and multiple I2C devices, that means we’ll only be able to interact with a single device at a time, but nothing prevents us from interacting with the two devices rapidly in sequence. Provided the bus is allowed to return to the free state in between interactions, data can flow back and forth without issue from different sensors to the master.

Fortunately, the I2C library (and the original Wire library as well) handle establishing the free state on our behalf, meaning we don’t have to do anything very special in our code to rapidly switch between the two. The only thing we have to take into account, is the possibility that an error might occur while dealing with one device, causing the system to lock up.

In the I2C library, each read or write command will return either a 0 telling us that the interaction was successful, or an error code generated by the Atmel TWI library that manages the fundamental interactions. The library checks each step of the process, verifying that no error has occurred. Each address transmission, each ACK/NACK, each data byte, everything is verified for status before moving on to the next step. If an error is returned, or a timeout occurs, the reason for that error is returned from the function and can be used.

The library author, Wayne Truchsess, provides a description for the error codes he assigns to various time out results, in a comment block within the body of the code. “MR” stands for “Master Receiver” (the master is executing a read command), “MT” stands for “Master Transmitter” (the master is executing a write command).

Error CodeDescription
0x00Function executed with no errors
0x01Timed out waiting for successful completion of a Start bit
0x02Timed out waiting for ACK/NACK while addressing slave in transmit mode (MT)
0x03Timed out waiting for ACK/NACK while sending data to the slave
0x04Timed out waiting for successful completion of a Repeated Start
0x05Timed out waiting for ACK/NACK while addressing slave in receiver mode (MR)
0x06Timed out waiting for ACK/NACK while receiving data from the slave
0x07Timed out waiting for successful completion of the Stop bit
0x08 - 0xFFSee datasheet for exact meaning

By now, the descriptions of those first seven errors should be self explanatory, however if you need to review, read through the module describing I2C Signaling. In the event an error is detected, the library will immediately reinitialize the I2C bus using the following C code: TWCR = _BV(TWEN) | _BV(TWEA); All that’s doing is setting the Two Wire Enable and Two Wire Enable Acknowledge bits in the Two Wire Control Register.

The first seven error codes were established by the library author, however the remaining error codes, 0x08 – 0xFF cover a large range of possible values. Here is a table describing them as well…

TWI Status CodeDescription
0x38Arbitration Lost
0x20MT Error - Address + Write transmitted and NACK received
0x30MT Error - Data byte has been transmitted and NACK received
0x48MR Error - Address + Read transmitted and NACK received
0x58MR Error - Data byte has been received and NACK transmitted

These are the possible return values that could appear not originally listed by the author. The reason for the large gaps in the sequences is due to the AVR status codes containing values related to operating the microcontroller as a bus slave, rather than as a bus master, and that is outside the scope of the libraries function.


Arduino I2C Error Handling

When we read or write to our I2C devices, we’ll need to start capturing the status code returned so that we are aware of whether there are errors occurring on the bus. It doesn’t have to be very elaborate, but it does involve making our reads and writes a little more complicated. Here is a small sample…

In here, we create a single byte value to hold the status result of our I2C interaction, then load it with the result of an I2C Read command, and finally check to see if it’s some value other than 0. If it is, then we’ll run a routine called I2CError in which we’ll take some kind of action, based on the errorStatus we’re sending it. If you want to be very granular in your handling, you can also pass the device you were talking to and the register you were working with to the error handler as well, but in this case, we’ll keep it simple and just send the error code.


In this function, we receive the error code generated by the original I2C command, increment an error counter by 1, and just display the error that occurred as well as how many errors have been generated since the last reset. If you were sending the I2C address and register values those could be displayed as well, and if necessary, a chip initialization could be performed, or some configuration register reset, all depending on the values that were sent. Occasionally, when doing initial troubleshooting, I also include a counter for every time an I2C command is executed, then do a quick calculation to determine how many errors are being generated per million executions, essentially the PPM error value. That formula would look like this errorPPM = ((float)errorCount / (float)execCount) * 1000000;.


Depending on how many I2C commands you’re checking, execCount could grow very large very quickly, so it would most likely need to be an unsigned long variable, and if it grows too fast, you might need to specify that the execution count be reset after every error and do the math yourself by adding up all the execution counts that are listed, in order to prevent an overflow.



MCP7940 and AT30TS750A – Time and Temperature

As with all of our sketches, we need to define our registers for ease of use in our code, and establish some global variables that we can access in different functions. For the MCP7940, we’ll need the I2C address and time stamp registers, and for the AT30TS750A we’ll need the I2C Address and the temperature register.

Before working with the code below, review the datasheets and look at your previous sketches to answer the following questions…

Which configuration registers will be needed for the AT30TS750A?


Hover to read answer


Which configuration registers will be needed for the MCP7940?


Hover to read answer


Time and Temp Declarations

All the I2C addresses and registers for our two I2C chips are now loaded, as well as the byte array to hold our time stamp values, and two variables to assist us with error checking. The errorCount variable is an unsigned integer, just in case something goes terribly wrong, we wont overflow the variable too quickly. The names of the register variables have had the name of the chip added because REG_CONFIG is no longer sufficiently explicit to tell us which chip we’re configuring any more, so it has to be AT30TS750_REG_CONFIG.


Time and Temp Setup

The setup looks very similar to any that we’ve performed so far, in that we’re specifying our I2C configuration, initializing the MCP7940 and AT30TS750A chips in turn, and finally setting our RTC with a start time. Notice though, that we’ve begun our error handling by saving the output from the I2c.write() command and checking to make sure it’s a zero value.


Chip Initialization

There is a lot of stuff happening here, but it’s only a combination of the temperature configuration and the RTC configuration from previous sketches. The temperature configuration is merely setting the resolution to 9 bits, and the time configuration is setting the 12/24 hour mode, course trim option, oscillator trim value and enabling the battery backup bit, wrapping it all up by starting the oscillator. As with the function when we set the time back in the Setup function, we’ve added error handling wrappers around each of the I2C commands to make sure we’ll know if something happens.


Time and Temp Loop

In the loop, the we grab the current time stamp and display it to the serial monitor, and we grab the current temperature and display that as well. Everything is neatly wrapped in error handling and the little delay at the bottom is just so we don’t spam the serial monitor with output, although you can eliminate it entirely if you want.

The only thing not shown in the code breakouts is the BCD converters that existed in all the previous sketches and are included here, in the full sketch…


Forcing I2C Errors

If you’re like me, then you won’t be satisfied with having an error handling routine that doesn’t seem to be handling any errors. What happens if one occurs? What will it look like?

Here are a few things you can do to the code above to generate different types of errors.

  1. Change the timeout parameter in the I2C configuration from 250ms to 1ms.
  2. Change either of the I2C addresses to an incorrect value.
  3. Change any of the register addresses to an incorrect value.

Doing any of those three things should generate more than enough errors for you play with and investigate!

I2C Errors
I2C Errors
Previous Post
KiCad BOM Management Part 2: Clean Exports
Next Post
SPI Basics
You might also like
Menu