[FE training-materials-updates] kernel labs: add i2c communication lab

Michael Opdenacker michael.opdenacker at free-electrons.com
Mon Sep 30 17:43:56 CEST 2013

Repository : git://git.free-electrons.com/training-materials.git

On branch  : kernel-ng
Link       : http://git.free-electrons.com/training-materials/commit/?id=23ef2a461fdcc3ae09f0f88e012dff9f874c4383


commit 23ef2a461fdcc3ae09f0f88e012dff9f874c4383
Author: Michael Opdenacker <michael.opdenacker at free-electrons.com>
Date:   Mon Sep 30 17:43:13 2013 +0200

    kernel labs: add i2c communication lab
    Signed-off-by: Michael Opdenacker <michael.opdenacker at free-electrons.com>


 .../kernel-i2c-communication.tex                   |  127 ++++++++++++++++++++
 1 file changed, 127 insertions(+)

diff --git a/labs/kernel-i2c-communication/kernel-i2c-communication.tex b/labs/kernel-i2c-communication/kernel-i2c-communication.tex
index c181243..0f1cecc 100644
--- a/labs/kernel-i2c-communication/kernel-i2c-communication.tex
+++ b/labs/kernel-i2c-communication/kernel-i2c-communication.tex
@@ -36,3 +36,130 @@ Now look at the Device Tree for the AM335x EVM board
 Edit the \code{arch/arm/boot/dts/am335x-bone-common.dtsi} file and 
 add what's needed to enable pin muxing for \code{i2c1}.
 Don't hesitate to go back to the lectures to understand what to do!
+Rebuild and update your DTB, and eventually reboot the board.
+We will use the \code{i2cdetect} command to make sure that 
+everything works fine for \code{i2c1}:
+# i2cdetect -l
+i2c-0	i2c       	OMAP I2C adapter                	I2C adapter
+i2c-1	i2c       	OMAP I2C adapter                	I2C adapter
+# i2cdetect -F 1
+Functionalities implemented by /dev/i2c-1:
+I2C                              yes
+SMBus Quick Command              no
+SMBus Send Byte                  yes
+SMBus Receive Byte               yes
+SMBus Write Byte                 yes
+SMBus Read Byte                  yes
+SMBus Write Word                 yes
+SMBus Read Word                  yes
+SMBus Process Call               yes
+SMBus Block Write                yes
+SMBus Block Read                 no
+SMBus Block Process Call         no
+SMBus PEC                        yes
+I2C Block Write                  yes
+I2C Block Read                   yes
+\section{Device initialization}
+The next step is to read the state of the nunchuk registers, to find out
+whether buttons are pressed or not, for example.
+Before being able to read nunchuk registers, the first thing to do is 
+to send initialization commands to it. That's also a nice way of making
+sure i2c communication works as expected.
+In the probe routine (run every time a matching device is found):
+\item Using the I2C raw API (see the slides), send two bytes to the
+      device: \code{0xf0} and \code{0x55}. Make sure you check the return value of 
+      the function you're using. This could reveal communication issues.
+      Using LXR, find examples of how to handle failures properly using
+      the same function.
+\item Let the CPU wait for 1 ms by using the \code{udelay()} routine.
+      You may need to use LXR again to find the right C headers to
+      include.
+\item In the same way, send the \code{0xfb} and \code{0x00} bytes now. 
+      This completes the nunchuk initialization.
+Recompile and load the driver, and make sure you have no communication
+\section{Read nunchuk registers}
+The nunchuk exhibits a rather weird behaviour: it seems that it update
+the state of its internal registers only when they have been read.
+As a consequence, we will need to read the registers twice!
+To keep the code simple and readable, let's create a
+\code{nunchuk_read_registers} function to do this. In this function:
+\item Start by putting a \code{10 ms} delay by calling the
+      \code{mdelay()} routine. That's needed to add time between the
+      previous i2c operation and the next one.
+\item Write \code{0x00} to the bus. That will allow us to read
+      the device registers.
+\item Add another \code{10 ms} delay.
+\item Read 6 bytes from the device, still using the I2C raw API.
+      Check the return value as usual. 
+\section{Reading the state of the nunchuk buttons}
+Back to the \code{probe()} function, call your new function twice.
+After the second call, compute the states of the \code{Z} and \code{C}
+buttons, which can be found in the sixth byte that you read.
+As explained on
+\item \code{bit 0 == 0} means that \code{Z} is pressed. 
+\item \code{bit 0 == 1} means that \code{Z} is released. 
+\item \code{bit 1 == 0} means that \code{C} is pressed. 
+\item \code{bit 1 == 1} means that \code{C} is released. 
+Using boolean operators, write code that initializes a \code{zpressed}
+integer variable, which value is \code{1} when the \code{Z} button is
+pressed, and \code{0} otherwise. Create a similar \code{cpressed}
+variable for the \code{C} button.\footnote{You may use the \code{BIT()}
+macro, which will make your life easier. See LXR for details.}
+The last thing is to test the states of these new variables at the end
+of the \code{probe()} function, and log a message to the console
+when one of the buttons is pressed.
+Compile your module, and reload it. No button presses should be
+detected. Remove your module.
+Now hold the \code{Z} and reload and remove your module again:
+insmod /root/nunchuk/nunchuk.ko; rmmod nunchuk
+You should now see the message confirming that the driver found
+out that the \code{Z} button was held.
+Do the same over and over again with various button states.
+At this stage, we just made sure that we could read the state
+of the device registers through the I2C bus. Of course, loading and
+removing the module every time is not an acceptable way of 
+accessing such data. We will give the driver a proper {\em input}
+interface in the next slides.

More information about the training-materials-updates mailing list