Main Site Documentation

Own module attempt to use i2c comms


#1

Hi,

I have been playing with a Spider board for some time, and even managed to create a basic module for 2x16 LCD. Now I am trying to get a more complicated setup going where I have another Microprocessor communicate with the Spider on i2c. I have no experience with i2c, but this is sort of the aim - to learn something new. For the other Microprocessor I am using a Arduino NANO.

On the Arduino NANO (my “module”) I have created a basic sketch that does the following:

  • initialise i2c slave mode with an address of “50”
  • define, initialise a LED on pin 13
  • define pin 14 as an interrupt pin from the Arduino to the Spider (to indicate to the Spider that data is ready from the “module”)
  • initialise Serial on 9600
  • the main program consist out of a delay and creates an interupt every 4 seconds (similating data is ready to be read)
  • when a request comes in from the Mainbord (Spider) on the i2c bus, then the Arduino will flash the LED once. Read the byte coming in on the i2c bus, and echo it back on the i2c and also send the same byte out on the Serial (so I can see in a terminal program, what the Arduino receives).

Here is my Arduino code:


 #include <Wire.h>

int led=13;                     //pin 13 has an led connected on most Arduino boards.
int datardy=12;                 //pin 12 is connected to pin 3 (interrupt) on the socket
int rdy=0;                      //counter to create a delay to simulate some sensor readings
byte inByte;


void setup()
{
  Wire.begin(50);               //join i2c bus with address #2
  Wire.onRequest(requestEvent); //fires when theres a request from i2c master
  pinMode(led, OUTPUT);         //initialize digital pin 13 as an output
  pinMode(datardy, OUTPUT);     //initialize digital pin 12 as an output
  Serial.begin(9600);           // start serial for output
  Serial.println("Aduino NANO: ok");
}

void loop()
{
  delay(100);                   //pauses for 100 ms 
  rdy=rdy+1;                    //inc counter to simulate data that is ready to be read

  if(rdy==40)
  {
    digitalWrite(datardy, LOW); //pin down indicating data is ready to be read
    delay(1);                   //pauses for 1 ms
    digitalWrite(datardy, HIGH);//pin up, resetting pin
    rdy=0;                      //reset rdy counter value
  }
}

void requestEvent()
{
  //test 1
  //Wire.write("x");          //send the characted "hello" to master i2c device
  digitalWrite(led, HIGH);      //turn the LED on (HIGH is the voltage level)
  delay(200);
  digitalWrite(led, LOW);       //turn the LED off by making the voltage LOW

  byte c = Wire.read();         //read byte as a character
  Wire.write(inByte);          //echo received char back  
  Serial.println(c, HEX);         // print the character in HEX
}

For the .NET Gadgeteer Module, I have used the standard template, and also tried to implement the recommended structure (according to the “.NET Gadgeteer Module Builder’s Guide version 1.9”). I also tried to make sense of some of the other existing modules software. I must be honest, I am hacker-together and not a programmer, so I can not 100% why this structure is the way to go, and there are just not that many step-by-step explanations that I could get hold of yet to explain thing in more layman’s terms.

My module does the following:

  • sets the socket up
  • defines an interrupt on pin 3 of that socket (that is connected to pin 14 of the Arduino NANO)
  • initialises the i2c at 100kHz
  • when an interrupt happens on pin 3, then the module routine will send the value “0x40” on i2c to the Arduino, and then also read from the Arduino “slave i2c” with address “50”.

Here is my code:


using System;
using Microsoft.SPOT;

using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using GTI = Gadgeteer.Interfaces;

namespace Gadgeteer.Modules.JLR
{
    /// <summary>
    /// A rcdec module for Microsoft .NET Gadgeteer
    /// </summary>
    public class rcdecmeas : GTM.Module
    {
        private GTI.I2CBus _i2c;
        private byte[] tx_data = { 0x40 };        //slave i2c tx data (arduino)
        private byte[] rx_data = { 0 };     //slave i2c rx data (arduino)

        /// <summary>The RC Decoder and Distance Measurement module</summary>
        /// <param name="socketNumber">The socket that this module is plugged in to.</param>
        public rcdecmeas(int socketNumber)
        {
            Socket socket = Socket.GetSocket(socketNumber, true, this, null);

            //slave address "50" (arduino)
            //speed 100kHz
            _i2c = new GTI.I2CBus(socket, 50, 100, this);

            //interrupt on pin 3 of the socket
            //interrupts on falling edge
            this.input = new GTI.InterruptInput(socket, Socket.Pin.Three, GTI.GlitchFilterMode.On, GTI.ResistorMode.Disabled, GTI.InterruptMode.FallingEdge, this);
            this.input.Interrupt += new GTI.InterruptInput.InterruptEventHandler(input_Interrupt);
        }

        private GTI.InterruptInput input;

        void input_Interrupt(GTI.InterruptInput sender, bool value)
        {
            this.rcdecmeasEvent(this);  //raises this rcdecmeasEvent (Get the ball rolling!)
        }

        /// <summary>
        /// Represents the delegate that is used to handle the "dataready" event.
        /// </summary>
        /// <param name="sender">The <see cref="rcdecmeas"/> object that raised the event</param>
        public delegate void rcdecmeasEventHandler(rcdecmeas sender);

        /// <summary>
        /// Raised when <see cref="rcdecmeas"/> has data measurements ready for reading
        /// </summary>
        public event rcdecmeasEventHandler DataReady;

        private rcdecmeasEventHandler _DataReady;

        /// <summary>
        /// Raises the <see cref="DataReady"/> event
        /// </summary>
        /// <param name="sender">The <see cref="rcdecmeas"/> that raised the event</param>
        protected virtual void rcdecmeasEvent(rcdecmeas sender)
        {
            if (_DataReady == null)
            {
                _DataReady = new rcdecmeasEventHandler(rcdecmeasEvent);
            }

            if (Program.CheckAndInvoke(DataReady, _DataReady, sender))
            {
                //_i2c.WriteRead(tx_data, rx_data, 1000);
                _i2c.Write(tx_data, 100);
                _i2c.Read(rx_data, 100);
            }
        }

        /// <summary>
        /// Returns the data that was received from the module
        /// </summary>
        /// <returns>Returns the rx_data train</returns>
        public byte[] GetRxData()
        {
            return rx_data;
        }
    }
}

Now here is what I will really appreciate some advise and feedback on:

  • interrupt sequence works fine. Arduino sends interrupt simulating that data is ready. The “module” routine receives the interrupt and sends the value 0x40 to the Arduino slave with address 50. Arduino receives this and flashes the LED. But there is no result that ends up back in the “module”. On the serial/terminal is shows “FF”. This is achieve with the _i2c.Write and _i2c.Read commands.
  • If I just send a predefined value such as “hi” instead of echoing the value back, then the value “hi” is received in the “module” via the i2c bus from the Arduino.
  • If I use the _i2c.WriteRead command, then nothing happens. The Arduino does not receive anything, and it does not transmits anything back.

Some further information:

  • Since the Spider is a 3v3 device and the Arduino is 5v, I have included a voltage divider on ouput pin 12 (Arduino) towards socket pin 3 (Gadgeteer).
  • I did not put any voltage divider between the Arduino and Spider for the i2c pins (SDA and SCL) as I have read that the Spider is 5v tolerant.

Any advise on what I can test, or try will be really appreciated. If there are any further tutorials or blogs that explains why the specific coding patterns are recommended for modules and how to implement i2c and spi between the Gadgeteer any other device will really be welcome.

Apologies for the long winded post - I am really stuck at this point and have run out of google-searches to try for futher tests to get this working.

Thanks in advance!


#2

So the usual challenge with I2C is the 7-bit address versus the 8-bit address. NetMF uses 7-bit addresses, so you need to make sure that the address you use is correct. The actual address will get the read and write bits set as 0/1 as needed, to make a full 8-bit address. http://www.ghielectronics.com/docs/12/i2c

Can you confirm your 7-bits of your address ? is “50” meant to be 50 decimal or 0x50, or what?


#3

Hi Brett,

Yes I’ve read through multiple forums that talked about this 7 vs 8 bit address issue. The Arduino also uses a 7 bit address so I figured that if I specify the slave address in exactly the same format in both Gadgeteer and Arduino, then I should be safe. So I made sure that my address is less than 128 and more than 10 (read somewhere about an issue with addresses lower than 10), so this is how I got to the decimal 50. In reality, I recon the slave Arduino has the address of 0x19 (or decimal 25).

To eliminate the address issue, I changed the address in my Gadgeteer routine to anything but 50, and then if I issue the _i2c.Read command then I will not get any reaction from the Arduino (no LED flashing that indicates a successful Wire.onrequest event on the Arduino). I only get a LED flash with the address 50 - but the actual data I am sending is not recognised, only the device address because what I see on the terminal print is always “FF”.

It is almost as if the address from Gadgeteer to Slave only recognise communication from the master i2c with the _i2c.Read, but not during _i2c.Write or _i2c.WriteRead.

Thanks for the link, I will study it in detail.


#4

Do you have pull-ups wired to your SDA and SCL lines?


#5

No pull-ups. My understanding is that the Spider board already have pull-ups. Should there be?


#6

I dont recall if the spider does or not. I dont think it does since those pins can be used for things besides I2C, and the module builders guide suggests puting switchable pull-ups on the modules instead of the mainboard.

Either way it wont hurt anything to add some ~5k pull-ups.


#7

@ soldermonkey - from the module guide…

 For socket type I, mainboards should include I2C bus pull-ups of 2,200 ohms on both SDA and SCL pins. Modules must not include pull-ups on these lines.

In addition to hardware I2C (i.e. I2C with hardware support on mainboard processors) that is provided through Socket type I, .NET Gadgeteer supports Software I2C using GPIOs on socket types X or Y. Note that while Hardware I2C (Type I) modules MUST NOT have pull up resistors, Software I2C modules MUST have pull up resistors (2.2kohm – 10kohm), so a module cannot be transparently used with both. A module can be made compatible with both by providing switchable pull-ups.


#8

OK, I had it totally backwards.
I have been building software I2C modules which need the pullups…

EIther way it still wont hurt anything to put a couple of 5k-10k pullups in there. If there is a lot of capacitance on the bus ( very long wires ) you will need the faster rise times.