I2C Communication

This forum is dedicated to software development related to MultiWii.
It is not the right place to submit a setup problem.
Software download
Post Reply
copterrichie
Posts: 2261
Joined: Sat Feb 19, 2011 8:30 pm

I2C Communication

Post by copterrichie »

Hey Alex (WiiFamily),

I have a question that I hoping someone can help with. I would like to modify this segment of Alex's code to function as a slave verse as the Master. Reason, I would like add this code to a slave Anduino (ODS) and have the Master send Telemetry information via the I2C bus version the serial com port. I know it can be done using the wire library but Alex's code is so much more efficient.


Code: Select all

#define LISTENING_ADDRESS xxxx

#define I2C_SPEED 100000L     //100kHz normal mode, this value must be used for a genuine WMP
uint8_t rawADC[6];
static uint32_t neutralizeTime = 0;
// ************************************************************************************************************
// I2C general functions
// ************************************************************************************************************

// Mask prescaler bits : only 5 bits of TWSR defines the status of each I2C request
#define TW_STATUS_MASK   (1<<TWS7) | (1<<TWS6) | (1<<TWS5) | (1<<TWS4) | (1<<TWS3)
#define TW_STATUS       (TWSR & TW_STATUS_MASK)

void i2c_init(void) {
 
  TWSR = 0;        // no prescaler => prescaler = 1
  TWBR = ((16000000L / I2C_SPEED) - 16) / 2; // change the I2C clock rate
  TWCR = 1<<TWEN;  // enable twi module, no interrupt
}

void i2c_rep_start(uint8_t address) {
  TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN) | (1<<TWSTO); // send REPEAT START condition
  waitTransmissionI2C(); // wait until transmission completed
  TWDR = address; // send device address
  TWCR = (1<<TWINT) | (1<<TWEN);
  waitTransmissionI2C(); // wail until transmission completed
  }

void i2c_rep_stop(void) {
  TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
  waitTransmissionI2C();
 
}

void i2c_write(uint8_t data ) {   
  TWDR = data; // send data to the previously addressed device
  TWCR = (1<<TWINT) | (1<<TWEN);
  waitTransmissionI2C(); // wait until transmission completed
 
}

uint8_t i2c_readAck() {
  TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
  waitTransmissionI2C();
  return TWDR;
}

uint8_t i2c_readNak(void) {
  TWCR = (1<<TWINT) | (1<<TWEN);
  waitTransmissionI2C();
  return TWDR;
}

void waitTransmissionI2C() {
  uint8_t count = 255;
  while (count-->0 && !(TWCR & (1<<TWINT)) );
  if (count<2) { //we are in a blocking state => we don't insist
    TWCR = 0;  //and we force a reset on TWINT register
    neutralizeTime = micros(); //we take a timestamp here to neutralize the value during a short delay after the hard reset
  }
}

void i2c_getSixRawADC(uint8_t add, uint8_t reg) {
  i2c_rep_start(add);
  i2c_write(reg);         // Start multiple read at the reg register
  i2c_rep_start(add +1);  // I2C read direction => I2C address + 1
  for(uint8_t i = 0; i < 5; i++) {
    rawADC[i]=i2c_readAck();}
  rawADC[5]= i2c_readNak();
}

void i2c_writeReg(uint8_t add, uint8_t reg, uint8_t val) {
  i2c_rep_start(add+0);  // I2C write direction
  i2c_write(reg);        // register selection
  i2c_write(val);        // value to write in register
}

uint8_t i2c_readReg(uint8_t add, uint8_t reg) {
  i2c_rep_start(add+0);  // I2C write direction
  i2c_write(reg);        // register selection
  i2c_rep_start(add+1);  // I2C read direction
  return i2c_readNak();  // Read single register and return value
}


What I believe is required is something similar to this to make the code work. The wire library to my understanding is Interrupt driven and because of the timing issue with the ODS, this interrupt would create problems.

Code: Select all

Wire.begin(LISTENING_ADDRESS);                // join i2c bus with address
  Wire.onReceive(receiveEvent); // register event

copterrichie
Posts: 2261
Joined: Sat Feb 19, 2011 8:30 pm

Re: I2C Communication

Post by copterrichie »

Follow-up, after giving this more thought, what I need to know is how to mask for the I2C address. I guess I need to learn how the I2C bus communicate. :?

ziss_dm
Posts: 529
Joined: Tue Mar 08, 2011 5:26 am

Re: I2C Communication

Post by ziss_dm »

Hi,

What kind of protocol you going to implement?

You can try to use it as skeletton:

Code: Select all

#include <util/twi.h>

#define TWI_BUS_ERR_1   0x00
#define TWI_BUS_ERR_2   0xF8

// Status Slave RX Mode
#define SR_SLA_ACK      0x60
#define SR_LOST_ACK     0x68
#define SR_GEN_CALL_ACK 0x70
#define GEN_LOST_ACK    0x78
#define SR_PREV_ACK     0x80
#define SR_PREV_NACK    0x88
#define GEN_PREV_ACK    0x90
#define GEN_PREV_NACK   0x98
#define STOP_CONDITION  0xA0
#define REPEATED_START  0xA0

// Status Slave TX mode
#define SW_SLA_ACK      0xA8
#define SW_LOST_ACK     0xB0
#define SW_DATA_ACK     0xB8
#define SW_DATA_NACK    0xC0
#define SW_LAST_ACK     0xC8


static uint8_t _buff;
static uint8_t _cnt=0;

void InitI2CSlave(uint8_t adr){
  TWAR = adr;
  TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWIE) | (1<<TWEA);
}

ISR (TWI_vect){   
 switch (TWSR & 0xF8) { 
  case SR_SLA_ACK: 
    TWCR |= (1<<TWINT);
    _cnt = 0;
    return;
  case SR_PREV_ACK:
    // Recv byte
    _buff = TWDR;
    TWCR |= (1<<TWINT);
    return;
  case SW_DATA_ACK: 
  case SW_SLA_ACK:
    // Send byte
    TWDR = 0xDE;
    TWCR |= (1<<TWINT);
    return;
  case TWI_BUS_ERR_2:
    TWCR |=(1<<TWSTO) | (1<<TWINT);
  case TWI_BUS_ERR_1:
     TWCR |=(1<<TWSTO) | (1<<TWINT);
  }
  TWCR =(1<<TWEA) | (1<<TWINT) | (1<<TWEN) | (1<<TWIE); // TWI Reset
}


void setup() {
}

void loop() {
}


Also Atmel library:
https://www.das-labor.org/trac/browser/ ... twi_slave/

regards,
ziss_dm

copterrichie
Posts: 2261
Joined: Sat Feb 19, 2011 8:30 pm

Re: I2C Communication

Post by copterrichie »

Thank you Ziss,

There is this great DIY ODS project created by Dennis Frie on RCG that I would like to modify to work with the MWC.

Here is the link to the project. http://www.rcgroups.com/forums/showthre ... st19398151

I believe adding the ability for this ODS to communicate over the I2C bus verse serial is a much better solution. The present version, the serial port is being used for GPS and IMO, this is how the GPS interface should be handled for the MWC. However, what I would like to do is, send the existing Telemetry information from the WMC to the ODS visa I2C bus and display this information in real time. I don't believe this will have an impact on the MWC and may actually improve performance when telemetry communications are required.

As for protocol, I believe using the Existing data structure presently used in the MWC to send data.

Code: Select all

case 'O':  // arduino to OSD data - contribution from MIS
      point=0;
      serialize8('O');
      for(i=0;i<3;i++) serialize16(accSmooth[i]);
      for(i=0;i<3;i++) serialize16(gyroData[i]);
      serialize16(altitudeSmooth);
      serialize16(heading); // compass - 16 bytes
      for(i=0;i<2;i++) serialize16(angle[i]); //20
      for(i=0;i<6;i++) serialize16(motor[i]); //32
      for(i=0;i<6;i++) {serialize16(rcHysteresis[i]);} //44
      serialize8(nunchukPresent|accPresent<<1|baroPresent<<2|magPresent<<3);
      serialize8(accMode|baroMode<<1|magMode<<2);
      serialize8(vbat);     // Vbatt 47
      serialize8(17);  // MultiWii Firmware version
      serialize8('O'); //49
      UartSendData();
      break;


Change the UartSendData() routine to send the data via the I2C bus verse serial.

timecop
Posts: 1880
Joined: Fri Sep 02, 2011 4:48 pm

Re: I2C Communication

Post by timecop »

Pretty cool idea, but how much more noise are you going to put on this slow bus? You are already reading i2c sensors etc... And I guess you want OSD to be slave, not mwii?
anyway I think you need to set TWAR register for address you want to listen on.

copterrichie
Posts: 2261
Joined: Sat Feb 19, 2011 8:30 pm

Re: I2C Communication

Post by copterrichie »

We will soon find out. I have two arduinos talking to each other using the wire library, now I need to make the modifications to the WMC to send the data. I am thinking during the design phrase, I will run the bus at 100, then 400 however, I read here, the Bus can run much faster. I am not sure the Arduinos can run at this higher speeds, will have to research that.

The I²C reference design has a 7-bit address space with 16 reserved addresses, so a maximum of 112 nodes can communicate on the same bus. Common I²C bus speeds are the 100 kbit/s standard mode and the 10 kbit/s low-speed mode, but arbitrarily low clock frequencies are also allowed. Recent revisions of I²C can host more nodes and run at faster speeds (400 kbit/s Fast mode, 1 Mbit/s Fast mode plus or Fm+, and 3.4 Mbit/s High Speed mode). These speeds are more widely used on embedded systems than on PCs. There are also other features, such as 16-bit addressing.
http://en.wikipedia.org/wiki/I%C2%B2C

I would assume, the bus speed can be changed to the higher rate if needed when communicating with the slave Arduino.

mr.rc-cam
Posts: 457
Joined: Wed Jul 27, 2011 11:36 pm

Re: I2C Communication

Post by mr.rc-cam »

You can freely change the I2C bus speed in your code as long as your speed choices are valid for the parts used. For examples just search the Sensors source file for TWBR.

Post Reply