Main Content

Design Microcontroller Using the I2C Communication Protocol

This example shows how to design, implement, and test a microcontroller driver to configure and read magnetometer data using the I2C communications protocol. This example progresses through a model-based design cycle, from requirements to code generation.

Open the Project

Open the project from the command line.

i2cstateflow = openProject("i2cstateflow");

Opening the project also opens the top model.

A model of a microcontroller that uses the I2C protocol. The model contains, in order from left to right, an area named uController Compiler Provided Functions, a subsystem named Magnetometer, and a subsystem named uController.

Model Architecture and Conventions

The top model consists of three major components:

  1. Compiler provided functions: The compiler for the microcontroller provides a set of high-level functions to interact with the I2C peripheral. Use Simulink® functions to interact with the magnetometer reference subsystem.

  2. Magnetometer: This reference subsystem implements a state machine that replicates the behavior of an I2C peripheral, particularly a magnetometer.

  3. Microcontroller: This reference model implements the necessary state machine in a Stateflow® chart for the peripheral configuration and continuous operation to receive magnetometer readings via I2C.

The microcontroller implements the I2C registers as global data stores via Simulink.Signal objects in the base workspace.

The magnetometer sensor is the HMC5983 Honeywell 3-Axis Digital Compass. The microcontroller is modeled after the Microchip Digital Signal Controller for the dsPIC 33F family.

Simulate the Example

In this example, you can use the Project tab to access requirements, modeling, testing, and code generation.

Requirements Review

With the Requirements Toolbox™ you can author, analyze, and manage requirements. As a starting point for this example, review twenty eight functional requirements defined for the I2C driver implementation. To open the requirements set, in the Project Shortcuts tab, click Requirements Review.

The requirements set for the microcontroller driver. It contains two columns named Index and Summary. A requirement named Register B Address is selected.

These requirements are in two groups:

  1. Device Configuration: These requirements describe the requirements for the magnetometer configuration.

  2. Device Operation: These requirements describe how the model continuously receives the magnetometer readings.

Model Design

To open the top model, in the Project Shortcuts tab, click Open Top Model. The two key components of the model are the magnetometer and the microcontroller I2C driver.

Magnetometer Model

In the top model, open the magnetometer subsystem to see the magnetometer model. The magnetometer mimics the I2C transactions, as these would happen when interacting with the physical device. It is important to note that this is a logical implementation; this model aims to respond to events in the I2C bus and generate I2C interrupts, which are represented by function calls, instead of representing the actual states of the SDA and SCL lines.

The Magnetometer subsystem. In the top half of the subsystem, five inputs connect to a chart named magnetometer, which in turn connects to generateMagInterrupts. In the bottom half of the subsystem, an area named Magnetometer Helper Functions contains five Simulink functions.

The magnetometer chart receives I2C events in the form of messages. Depending on the current state of the I2C transaction and the microcontroller, the magnetometer generates an I2C function call, a Data Ready function call, or both.

Microcontroller Model

In the top model, open the uController subsystem to see the microcontroller model. The microcontroller model is meant to be used for both, simulation and deployment in a microcontroller as an I2C driver. The objective is for it to design and test the model in simulation and Software-In-the-Loop (SIL), and to generate code for microcontroller deployment.

The uController subsystem. Two inputs named i2cEvent and DRDY combine into a bus. The bus acts as a function-call input event trigger for a chart named Driver.

The event-triggered Driver chart wakes up for every I2C or Data Ready event. At the end of a successful I2C transaction, the readingReady output is 1 and the magReading port contains the 6 bytes corresponding to 2 bytes for each of the X, Y, and Z magnetometer reading. The chart uses functions provided by the microcontroller compiler to interact with the I2C peripheral.

The Driver chart has two parent states: Startup and Reading. While the Startup state is active, the chart goes through the process of configuring the magnetometer registers as established in the requirements. Once the configuration is done, the Reading state becomes active. Every time the chart receives a Data Ready (DRDY) event, it starts a new I2C reading transaction with the magnetometer to get the new reading.

The Driver chart. It contains, in order from top to bottom, two states named Startup and Reading. Each state contains several child states.

The chart makes use of variant transitions to optionally incorporate error handling when there are bus collisions in the I2C bus.

Examine Links Between Requirements and Model

You can use the Requirements Toolbox to link each individual requirement to the Simulink model component that implements that requirement. Open the i2cStateflowMicrocontroller model.

open_system('i2cStateflowMicrocontroller')

After the model opens, enter the Requirements Perspective. Click the Show Perspectives views button in the lower-right corner of the model canvas.

The Show Perspectives views button.

Then click Requirements.

The Show Perspectives views user interface, which includes a button named Requirements.

In the Requirements perspective, open the Stateflow chart to see how some of the requirements are implemented.

open_system('i2cStateflowMicrocontroller/Driver')

The shaded text boxes show the requirement links between the chart elements and the requirements they implement.

The Driver chart with six requirements links superimposed over the states. Arrows point from certain state or transitions to the associated requirement link. The arrows are labeled with the word IMPLEMENTS.

Alternatively, you can navigate from a listed requirement to its implementation. To open the Requirements Editor, in the Project Shortcuts tab, click Requirements Review.

Select a functional requirement and, in the right pane, expand the Links section. The Implemented by section shows the model elements that implement the requirement.

The requirements set for the microcontroller driver. It contains three columns named, in order from left to right, Index, Summary, and Implemented. The Implemented column shows that the model incorporates every requirement in the set. Additionally, a requirement named 'Configure upon startup' is selected. The Links section shows that the requirement is implemented by a state named Startup in the Driver chart.

For example, for the above selected requirement, clicking the Startup link takes you to the state that implements this requirement in the Driver chart.

Run the Model

Reopen the top simulation model by clicking the Open Top Model shortcut in the Project Shortcut tab. Click Run. The model runs and completes a few I2C transactions.

To see how the state machine progresses through the transaction:

  1. Open the Driver chart in the i2cMicrocontroller model.

  2. From the Debug tab, select Animation Speed dropdown menu, and click Slow.

  3. Run the model.

The model steps through the chart as the peripheral configuration occurs and receives magnetometer readings.

The Driver chart mid-simulation. In the Startup state, a child state named WaitForAck is active. The chart is evaluating the transition from the WaitForAck child state to another child state named ConfigureRegisterB.

Another way to view how the I2C transaction occurs, is to open the Sequence Viewer at the top model level. In the top model, in the Simulation tab, click Sequence Viewer to open a sequence diagram that shows how the events occur in the simulation. For example, the image below shows the first few interactions between the magnetometer and the microcontroller when performing a sensor reading.

A sequence diagram for the microcontroller, with ten model elements displayed in columns along the top of the diagram. The diagram uses labelled arrows to show how the model elements send events to each other.

Error Handling

The I2C protocol defines multiple error conditions that can happen during a transaction. This example models two of such errors: a write collision error and an overwrite error. The write collision error happens when two devices try to write at the same time in the bus. This typically happens when two devices try to initiate a transaction at the exact same time. The overwrite error happens when a peripheral sends a byte to the controller device before the controller can read the previous byte and the read buffer is overwritten.

To see what can happen when there is no error handling, disable the error handling variant transitions in the Driver chart and enable the block that creates the error conditions.

ERRHDL.Value = false;
ERRGEN.Value = true;

Run the simulation. After the successfully receiving the first magnetometer reading (readingReady), an overwrite error occurs (I2COV). The device never recovers from this error condition and does not receive any other readings.

A Simulink Data Inspector graph with two variables named I2COV and Reading Ready. Reading Ready becomes active from 0.5 seconds to 0.7 seconds. Then, at 0.8 seconds, I2COV becomes active for a single time step.

Now enable the error handling for variant transitions.

ERRHDL.Value = true;

With error handling enabled, the model recovers from error and continues receiving sensor readings.

A Simulink Data Inspector graph with two variables named I2COV and Reading Ready. The graph shows a repeated pattern. First, Reading Ready activates with a value of 1. After Reading Ready deactivates, I2COV activates with a value of 1. In the next time step, Reading Ready enters an error recovery mode with a value of -1. Then, Reading Ready returns to 0. Finally, I2COV deactivates.

Disable error handling.

ERRHDL.Value = false;
ERRGEN.Value = false;

Test the Model

This example includes a test harness and a Simulink Test™ test suite that tests the behavior of the Driver chart.

Test Harness

The test harness is an independent model that progresses the chart through the Startup and Reading states and verifies that the chart makes the correct I2C functions calls as it conducts I2C transactions.

To open the test harness, in the Project Shortcuts tab, click Open Test Harness.

A test harness for the uController subsystem. It tests the subsystem using four inputs named, in order from top to bottom, ack, nack, recEn, and byte.

The Test Sequence block contains eight different scenarios. Seven of the scenarios progressively test more sections of the Driver chart. The first scenario tests a complete sequence from power up to complete sensor reading. Each scenario uses verify statements to ensure that the chart progresses as expected.

A list of scenarios for the uController test harness. The CfgRegA scenario is selected. The scenario wakes up the chart, writes to an address, and then sends the address to another model component.

Test Suite

This example includes eight tests that automate testing the model to verify each of the requirements are met. To see how these tests are implemented, click the Open Test Suite shortcut in the Project Shortcut tab. This opens the test suite in the Test Manager. Each test is responsible to verify a subset of the requirements are met by running the test harness on a given Test Sequence scenario. If all verify statements are true, the test passes.

To run the complete test suite, select i2cMagnetometerDriverTests in the test browser, and click the Run. All tests pass.

The results of the test suite named i2cMagnetometerDriverTests. Every test passes.

Requirement Validation

As a final step in testing the model, open the Requirements Editor by clicking the Requirements Review project shortcut and select Implementation Status and Validation Status from the toolstrip.

The toolstrip with the Implementation Status and Validation Status menu options enabled.

The Requirements Editor shows that the tests are implemented and verified.

The requirements set for the microcontroller driver. It contains four columns named, in order from left to right, Index, Summary, and Implemented, and Verified. The Verified column shows that every requirement in the model has passed a test. Additionally, a requirement named 'Write Address' is selected.

Generate Code

Next, generate code for the Driver chart to deploy to an embedded system.

  1. Open the microcontroller model. In the Project Shortcuts tab, click Open uC Model.

  2. In the Apps tab, click Embedded Coder.

  3. In the C Code tab, click Generate Code.

The generated code has 3 entry point functions.

  1. void i2cStateflowMicrocontroller_initialize(void): This function initializes the necessary variables and the state machine. This function should be called once after device startup.

  2. void DRDY(void) and void i2cEvent(void): Call these functions when either an Data Ready or a I2C interrupts are triggered.

See Also

(Simulink)