Difference between revisions of "Embedded System I2C Tutorial"

From Embedded Systems Learning Academy
Jump to: navigation, search
(Assignment)
 
(17 intermediate revisions by 2 users not shown)
Line 1: Line 1:
  
 +
Socialledge is moving to two portals. 
 +
*  The Wiki will remain here for general references about the SJ-One board, and to document student reports.
 +
*  The bookstack will now be used for SJSU assignments
 +
 +
[http://books.socialledge.com/books/embedded-drivers-real-time-operating-systems/chapter/lesson-i2c This article has been moved here]
 +
 +
<!--
 
== Theory of Operation ==
 
== Theory of Operation ==
 
I2C is prounced "eye-squared see".  It is also known as "TWI" because of the intial patent issues of this BUS.  This is a popular, low throughput (100-1000Khz), half-duplix BUS that only uses two wires regardless of how many devices are on this BUS.  Many sensors use this BUS because of its ease of adding to a system.
 
I2C is prounced "eye-squared see".  It is also known as "TWI" because of the intial patent issues of this BUS.  This is a popular, low throughput (100-1000Khz), half-duplix BUS that only uses two wires regardless of how many devices are on this BUS.  Many sensors use this BUS because of its ease of adding to a system.
Line 107: Line 114:
 
|  [[File:tutorial_i2c_read.png|center|frame|I2C Read Transaction]]
 
|  [[File:tutorial_i2c_read.png|center|frame|I2C Read Transaction]]
 
|}
 
|}
 +
 +
<BR/>
 +
== I2C Slave State Machine Planning ==
 +
Before you jump right into the assignment, do the following:
 +
*  Read and understand how an I2C master performs slave register read and write operation
 +
*:  Look at existing code to see how the master operation handles the I2C state machine function
 +
*:  This is important so you can understand the existing code base
 +
*  Next to each of the master state, determine which slave state is entered when the master enters its state
 +
*  Determine how your slave memory or registers will be read or written
 +
 +
''' It is important to understand the states, and use the datasheet to figure out what to do in the state to reach the next desired state given in the diagrams below. '''
 +
 +
=== Master Write ===
 +
In the diagram below, note that when the master sends the "R#", which is the register to write, the slave state machine should save this data byte as it's INDEX location.  Upon the next data byte, the indexed data byte should be written.
 +
 +
[[File:tutorial_i2c_master_write_state.png|center|frame|I2C Master Write Transaction]]
 +
 +
=== Master Read ===
 +
In the diagram below, the master will write the index location (the first data byte), and then perform a repeat start.  After that, you should start returning your indexed data bytes.
 +
 +
[[File:tutorial_i2c_master_read_state.png|center|frame|I2C Master Read Transaction]]
  
 
== Assignment ==
 
== Assignment ==
 +
 +
=== I2C State Machine Assignment ===
 +
Design your I2C state machine.  Look at the "Master Write" and "Master Read", and do the following:
 +
 +
*Right underneath the Master State, determine which state the slave will enter when the master is in each of the states
 +
*In each slave state, determine the action you will perform for the transaction
 +
*You can design your own state machine, or augment the existing one, whichever method can yield the maximum clarify for your I2C slave state.
 +
 +
=== I2C Code Assignment ===
 +
==== Assignment Outline ====
 +
<b>This is a group of two assignment; submit one copy of code per team and put down the names of your group members as part of the source code you turn in.  Only turn in the new code you added, not the entire file.</b>
 +
* The I2C#2 driver is already implemented and used for on-board sensors.
 +
* Study the existing I2C code: i2c_base.cpp.  Please ask any questions if you have any, but the driver was implemented using the state machine diagrams given at the below wikipedia page.
 +
* On your master board, you can just use i2c terminal command to write a register to the other board which is acting as a slave.  Hence, you only need to write code on one board (I2C slave board).
 +
 +
==== Guidelines ====
 +
Design an I2C slave interface, and use one person's board as MASTER and communicate with the second person's SLAVE board.  Here are the guidelines:
 
Extend the I2C base class to also support slave operation.  Test your I2C driver by using one board as a master, and another board as a slave.
 
Extend the I2C base class to also support slave operation.  Test your I2C driver by using one board as a master, and another board as a slave.
 
#  Study <B><CODE>i2c_base.cpp</CODE></B>, particularly the following methods:
 
#  Study <B><CODE>i2c_base.cpp</CODE></B>, particularly the following methods:
Line 114: Line 159:
 
#*  <B><CODE>i2cStateMachine()</CODE></B>
 
#*  <B><CODE>i2cStateMachine()</CODE></B>
 
#*:  Note that this function is called by the hardware interrupt asynchronously whenever I2C state changes.
 
#*:  Note that this function is called by the hardware interrupt asynchronously whenever I2C state changes.
#*:  The I2C driver simply "kicks off" the START state, and this function carries the hardware through its states to carry out the transaction.
+
#*:  The other I2C master will simply "kick off" the START state, and this function carries the hardware through its states to carry out the transaction.
 +
#*  The functions you add to this base class are accessible by the I2C2 instance.
 
#  Add <B><CODE>initSlave()</CODE></B> method to the I2C to initialize the slave operation.
 
#  Add <B><CODE>initSlave()</CODE></B> method to the I2C to initialize the slave operation.
#*  You decide what parameters the user can supply (ie: slave address).
+
#*  Allow the user to supply a memory to be read or written by another master.
#*  Hint: Maybe you can use a callback function that is called upon each byte read/written byI2C.
 
#*  Hint: Or you can have the user supply memory pointer which is read/written.
 
 
#  Extend the state machine for I2C slave operation.
 
#  Extend the state machine for I2C slave operation.
 
#*  Study the CPU user manual first, and create a state machine diagram on paper.
 
#*  Study the CPU user manual first, and create a state machine diagram on paper.
#*  I recommend designing a "slave write" first since that should be easier.
+
#*  The first register supplied after the slave address should be used as an "offset" of the memory to read or write.
#*  Hint: Print out state number whenever <B><CODE>i2cStateMachine()</CODE></B> is called.
 
#*:  This way, you will know what happens when the other master writes data to your slave.
 
 
#  Demonstrate the following :
 
#  Demonstrate the following :
#*  For full credit, allow your slave to be written to.
+
#*  Demonstrate that you are able to read and write the slave memory.
#*:  You can print the data out, or maybe light up LEDs based on what the master sends.
+
#*  For extra credit and bragging rights, create state machine diagrams, and if you can make better ones, I will use your diagrams at this wikipedia page :)
#*  For extra credit, allow your slave to be read.
+
 
#*  For super extra credit and bragging rights, create state machine diagrams, and if you can make better ones, I will use your diagrams at this wikipedia page :)
+
=== Sample Code ===
 +
<syntaxhighlight lang="cpp">
 +
#include "i2c2.hpp"
 +
#include <stdint.h>
 +
#include <stdio.h>
 +
 
 +
int main(void)
 +
{
 +
    I2C2& i2c = I2C2::getInstance(); // Get I2C driver instance
 +
    const uint8_t slaveAddr = 0xC0;  // Pick any address other than the used used at i2c2.hpp
 +
    uint8_t buffer[256] = { 0 };    // Our slave read/write buffer
 +
 
 +
    // high_level_init() will init() I2C, let's init slave
 +
    i2c.initSlave(slaveAddr, &buffer, sizeof(buffer));
 +
 
 +
    // I2C interrupt will (should) modify our buffer.
 +
    // So just monitor our buffer, and print and/or light up LEDs
 +
    // ie: If buffer[0] == 0, then LED ON, else LED OFF
 +
    uint8_t prev = buffer[0];
 +
    while(1)
 +
    {
 +
        if (prev != buffer[0]) {
 +
            prev = buffer[0];
 +
            printf("buffer[0] changed to %#x\n", buffer[0]);
 +
        }
 +
    }
 +
 
 +
    return 0;
 +
}
 +
</syntaxhighlight>
  
 
=== Warning ===
 
=== Warning ===
 
Since the I2C state machine function is called from inside an interrupt, you may not be able to to use <B><CODE>printf()</CODE></B>, especially if you are running FreeRTOS.  As an alternative, use the debug printf methods from the <B><CODE>printf_lib.h</CODE></B> file.
 
Since the I2C state machine function is called from inside an interrupt, you may not be able to to use <B><CODE>printf()</CODE></B>, especially if you are running FreeRTOS.  As an alternative, use the debug printf methods from the <B><CODE>printf_lib.h</CODE></B> file.
 +
 +
=== Hints ===
 +
*  Start by hooking up master microcontroller running the default, unmodified sample FreeRTOS project
 +
*  Familiarize yourself with the master microcontroller I2C commands: "help i2c"
 +
*  Initialize the slave microcontroller's slave address such that you will get an interrupt when your address is sent by the master microcontroller
 +
*  Add printfs to the I2C state machine code to identify what states you enter when the other master microcontroller is trying to do an I2C transaction
 +
*  Follow your diagram and figure out how to make the I2C slave state machine walk through to read and write registers
 +
<BR/>
 +
-->

Latest revision as of 20:06, 25 January 2019

Socialledge is moving to two portals.

  • The Wiki will remain here for general references about the SJ-One board, and to document student reports.
  • The bookstack will now be used for SJSU assignments

This article has been moved here