From: <pj...@us...> - 2011-09-05 17:30:28
|
Revision: 73 http://firmata.svn.sourceforge.net/firmata/?rev=73&view=rev Author: pjs Date: 2011-09-05 17:30:22 +0000 (Mon, 05 Sep 2011) Log Message: ----------- Add I2C to StandardFirmata Modified Paths: -------------- arduino/trunk/Firmata/examples/StandardFirmata/StandardFirmata.pde Modified: arduino/trunk/Firmata/examples/StandardFirmata/StandardFirmata.pde =================================================================== --- arduino/trunk/Firmata/examples/StandardFirmata/StandardFirmata.pde 2011-09-03 23:36:10 UTC (rev 72) +++ arduino/trunk/Firmata/examples/StandardFirmata/StandardFirmata.pde 2011-09-05 17:30:22 UTC (rev 73) @@ -11,6 +11,9 @@ /* Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved. + Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved. + Copyright (C) 2009 Shigeru Kobayashi. All rights reserved. + Copyright (C) 2009-2011 Jeff Hoefs. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -27,8 +30,22 @@ */ #include <Servo.h> +#include <Wire.h> #include <Firmata.h> +// move the following defines to Firmata.h? +#define I2C_WRITE B00000000 +#define I2C_READ B00001000 +#define I2C_READ_CONTINUOUSLY B00010000 +#define I2C_STOP_READING B00011000 +#define I2C_READ_WRITE_MODE_MASK B00011000 +#define I2C_10BIT_ADDRESS_MODE_MASK B00100000 + +#define MAX_QUERIES 8 +#define MINIMUM_SAMPLING_INTERVAL 10 + +#define REGISTER_NOT_SPECIFIED -1 + /*============================================================================== * GLOBAL VARIABLES *============================================================================*/ @@ -50,12 +67,70 @@ unsigned long previousMillis; // for comparison with currentMillis int samplingInterval = 19; // how often to run the main loop (in ms) +/* i2c data */ +struct i2c_device_info { + byte addr; + byte reg; + byte bytes; +}; + +/* for i2c read continuous more */ +i2c_device_info query[MAX_QUERIES]; + +byte i2cRxData[32]; +boolean readingContinuously = false; +boolean isI2CEnabled = false; +byte queryIndex = 0; +unsigned int i2cReadDelayTime = 0; // default delay time between i2c read request and Wire.requestFrom() + Servo servos[MAX_SERVOS]; - /*============================================================================== * FUNCTIONS *============================================================================*/ +void readAndReportData(byte address, int theRegister, byte numBytes) { + // allow I2C requests that don't require a register read + // for example, some devices using an interrupt pin to signify new data available + // do not always require the register read so upon interrupt you call Wire.requestFrom() + if (theRegister != REGISTER_NOT_SPECIFIED) { + Wire.beginTransmission(address); + #if ARDUINO >= 100 + Wire.write((byte)theRegister); + #else + Wire.send((byte)theRegister); + #endif + Wire.endTransmission(); + delayMicroseconds(i2cReadDelayTime); // delay is necessary for some devices such as WiiNunchuck + } else { + theRegister = 0; // fill the register with a dummy value + } + + Wire.requestFrom(address, numBytes); // all bytes are returned in requestFrom + + // check to be sure correct number of bytes were returned by slave + if(numBytes == Wire.available()) { + i2cRxData[0] = address; + i2cRxData[1] = theRegister; + for (int i = 0; i < numBytes; i++) { + #if ARDUINO >= 100 + i2cRxData[2 + i] = Wire.read(); + #else + i2cRxData[2 + i] = Wire.receive(); + #endif + } + } + else { + if(numBytes > Wire.available()) { + Firmata.sendString("I2C Read Error: Too many bytes received"); + } else { + Firmata.sendString("I2C Read Error: Too few bytes received"); + } + } + + // send slave address, register and received bytes + Firmata.sendSysex(SYSEX_I2C_REPLY, numBytes + 2, i2cRxData); +} + void outputPort(byte portNumber, byte portValue, byte forceSend) { // pins not configured as INPUT are cleared to zeros @@ -99,6 +174,13 @@ */ void setPinModeCallback(byte pin, int mode) { + if (pinConfig[pin] == I2C && mode != I2C) { + isI2CEnabled = false; + // uncomment the following once end() method is added to Wire library + // Wire.end(); + Firmata.sendString("Once I2C is enabled, the I2C pins cannot be changed to another mode"); + return; + } if (IS_PIN_SERVO(pin) && mode != SERVO && servos[PIN_TO_SERVO(pin)].attached()) { servos[PIN_TO_SERVO(pin)].detach(); } @@ -155,8 +237,11 @@ } break; case I2C: - pinConfig[pin] = mode; - Firmata.sendString("I2C mode not yet supported"); + if (IS_PIN_I2C(pin)) { + pinConfig[pin] = I2C; + // TODO: if not enabled call enableI2CPins()? + // or does user have to call I2C config again in order to reenable I2C? + } break; default: Firmata.sendString("Unknown pin mode"); // TODO: put error msgs in EEPROM @@ -243,7 +328,87 @@ void sysexCallback(byte command, byte argc, byte *argv) { + byte mode; + byte slaveAddress; + byte slaveRegister; + byte data; + unsigned int delayTime; + switch(command) { + case I2C_REQUEST: + mode = argv[1] & I2C_READ_WRITE_MODE_MASK; + if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) { + Firmata.sendString("10-bit addressing mode is not yet supported"); + return; + } + else { + slaveAddress = argv[0]; + } + + switch(mode) { + case I2C_WRITE: + Wire.beginTransmission(slaveAddress); + for (byte i = 2; i < argc; i += 2) { + data = argv[i] + (argv[i + 1] << 7); + #if ARDUINO >= 100 + Wire.write(data); + #else + Wire.send(data); + #endif + } + Wire.endTransmission(); + delayMicroseconds(70); + break; + case I2C_READ: + if (argc == 6) { + // a slave register is specified + slaveRegister = argv[2] + (argv[3] << 7); + data = argv[4] + (argv[5] << 7); // bytes to read + readAndReportData(slaveAddress, (int)slaveRegister, data); + } + else { + // a slave register is NOT specified + data = argv[2] + (argv[3] << 7); // bytes to read + readAndReportData(slaveAddress, (int)REGISTER_NOT_SPECIFIED, data); + } + break; + case I2C_READ_CONTINUOUSLY: + if ((queryIndex + 1) >= MAX_QUERIES) { + // too many queries, just ignore + Firmata.sendString("too many queries"); + break; + } + query[queryIndex].addr = slaveAddress; + query[queryIndex].reg = argv[2] + (argv[3] << 7); + query[queryIndex].bytes = argv[4] + (argv[5] << 7); + readingContinuously = true; + queryIndex++; + break; + case I2C_STOP_READING: + readingContinuously = false; + queryIndex = 0; + break; + default: + break; + } + break; + case I2C_CONFIG: + delayTime = (argv[4] + (argv[5] << 7)); // MSB + delayTime = (delayTime << 8) + (argv[2] + (argv[3] << 7)); // add LSB + + if(delayTime > 0) { + i2cReadDelayTime = delayTime; + } + + if(argc > 6) { + // If you extend I2C_Config, handle your data here + } + + if (!isI2CEnabled) { + enableI2CPins(); + } + + break; case SERVO_CONFIG: if(argc > 4) { // these vars are here for clarity, they'll optimized away by the compiler @@ -263,6 +428,9 @@ case SAMPLING_INTERVAL: if (argc > 1) samplingInterval = argv[0] + (argv[1] << 7); + if (samplingInterval < MINIMUM_SAMPLING_INTERVAL) { + samplingInterval = MINIMUM_SAMPLING_INTERVAL; + } else Firmata.sendString("Not enough data"); break; @@ -296,6 +464,10 @@ Serial.write(SERVO); Serial.write(14); } + if (IS_PIN_I2C(pin)) { + Serial.write(I2C); + Serial.write(1); // to do: determine appropriate value + } Serial.write(127); } Serial.write(END_SYSEX); @@ -326,7 +498,32 @@ } } +void enableI2CPins() +{ + byte i; + // is there a faster way to do this? would probaby require importing + // Arduino.h to get SCL and SDA pins + for (i=0; i < TOTAL_PINS; i++) { + if(IS_PIN_I2C(i)) { + // mark pins as i2c so they are ignore in non i2c data requests + setPinModeCallback(i, I2C); + } + } + + isI2CEnabled = true; + + // is there enough time before the first I2C request to call this here? + Wire.begin(); +} +void systemResetCallback() +{ + // clear i2c read continuous data + readingContinuously = false; + queryIndex = 0; +} + + /*============================================================================== * SETUP() *============================================================================*/ @@ -342,6 +539,7 @@ Firmata.attach(REPORT_DIGITAL, reportDigitalCallback); Firmata.attach(SET_PIN_MODE, setPinModeCallback); Firmata.attach(START_SYSEX, sysexCallback); + Firmata.attach(SYSTEM_RESET, systemResetCallback); // TODO: load state from EEPROM here @@ -365,7 +563,7 @@ analogInputsToReport = 0; Firmata.begin(57600); - + /* send digital inputs to set the initial state on the host computer, * since once in the loop(), this firmware will only send on change */ for (i=0; i < TOTAL_PORTS; i++) { @@ -405,5 +603,11 @@ } } } + // report i2c data if read continuous mode is enabled + if (readingContinuously) { + for (byte i = 0; i < queryIndex; i++) { + readAndReportData(query[i].addr, query[i].reg, query[i].bytes); + } + } } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |