summaryrefslogtreecommitdiffstats
path: root/source/qFix/qfixI2C.h
diff options
context:
space:
mode:
Diffstat (limited to 'source/qFix/qfixI2C.h')
-rw-r--r--source/qFix/qfixI2C.h278
1 files changed, 278 insertions, 0 deletions
diff --git a/source/qFix/qfixI2C.h b/source/qFix/qfixI2C.h
new file mode 100644
index 0000000..2597dcc
--- /dev/null
+++ b/source/qFix/qfixI2C.h
@@ -0,0 +1,278 @@
+//------------------------------------------------------------------
+// qfixI2C.h
+//
+// This class is used for low-level I2C communication.
+//
+// For TW_ constants see compat/twi.h
+//
+// Copyright 2005-2006 by KTB mechatronics GmbH
+// Author: Stefan Enderle, Florian Schrapp
+//------------------------------------------------------------------
+
+#ifndef qfixI2C_h
+#define qfixI2C_h
+
+#include "qfix.h"
+#include "compat/twi.h"
+
+const int ACTION_SEND = 1;
+const int ACTION_GET = 2;
+const int ACTION_UNKNOWN = 3;
+
+
+const uint8_t ERROR_NO_ACK = 1;
+const uint8_t ERROR_NO_START = 2;
+const uint8_t ERROR_NOT_SENT = 3; // send() could not send byte(s)
+
+
+
+class I2C
+{
+private:
+ uint8_t err;
+ uint8_t adr;
+
+ void sendStartSLA_W(uint8_t address);
+ void sendStartSLA_R(uint8_t address);
+ void sendByte(uint8_t data);
+ void sendStop();
+ void readByte(uint8_t& data);
+
+public:
+ I2C();
+ uint8_t error();
+
+// master side //
+
+ void send(uint8_t address, uint8_t data);
+ void send(uint8_t address, uint8_t* data, int length);
+ void get(uint8_t address, uint8_t* data, int length);
+
+// slave side //
+
+ void setSlaveAdress(uint8_t address);
+ bool isAction();
+ bool isActionSend(); // true if master sent something
+ bool isActionGet(); // true if master wants to get something
+ int receive(uint8_t* data); // if action is send, receive the bytes
+ void returnBytes(uint8_t* data, int len, bool lastOne);
+};
+
+
+
+I2C::I2C()
+{
+ TWBR = 10; // I2C bitrate (must be 10 or higher)
+ TWCR = 4+64; // TWI enable bit + ackn
+ TWSR = 0; // prescaler
+
+ // I2C pull-ups are set in the board file //
+
+ err=0;
+}
+
+uint8_t I2C::error()
+{
+ return err;
+}
+
+
+void I2C::setSlaveAdress(uint8_t address)
+{
+ TWAR = address<<1; // slave address (shift by one since bit 0 has different meaning)
+}
+
+
+bool I2C::isAction()
+{
+ if (TWCR&(1<<TWINT)) return true;
+ else return false;
+}
+
+
+inline
+bool I2C::isActionGet()
+{
+ return ((TWSR&248)==TW_ST_SLA_ACK); // own I2C address received, read mode, ACK sent
+}
+
+
+inline
+bool I2C::isActionSend()
+{
+ return ((TWSR&248)==TW_SR_SLA_ACK); // own I2C address received, write mode, ACK sent
+}
+
+
+void I2C::sendStartSLA_W(uint8_t address)
+{
+ bool OK = false;
+ int counter = 0;
+
+ do {
+
+ TWCR |= BV(TWINT)|BV(TWSTA); // send start condition
+ while(!(TWCR&BV(TWINT))); // wait for OK
+
+ if ((TWSR&248)!=TW_START) { // start condition was sent -> OK
+ err=ERROR_NO_START;
+ return;
+ }
+
+ TWDR=(address<<1); // slave address + write mode
+ TWCR = BV(TWINT) | BV(TWEN) | BV(TWEA); // generate command
+
+ while(!(TWCR&128)) {}; // wait for OK
+ if ((TWSR&248)==TW_MT_SLA_ACK) OK=true; // SLA+W was sent, ACK received -> OK
+ if ((TWSR&248)==TW_MT_SLA_NACK) { // SLA+W was sent, ACK not received -> ERROR
+ counter++;
+
+ if (counter == 10) { // After 10 tries...
+ err=ERROR_NO_ACK; // ... return with ERROR_NO_ACK
+ return;
+ }
+ }
+
+ } while(!OK);
+
+ err=0; // OK
+}
+
+
+void I2C::sendStartSLA_R(uint8_t address)
+{
+ bool OK=false;
+ while (!OK) {
+ TWCR |= BV(TWINT)|BV(TWSTA); // send start condition
+// sbi(TWCR, TWSTA); // generate start condition
+ while(!(TWCR&128)); // wait for OK
+ if ((TWSR&248)==8); // start condition was sent -> OK
+
+ TWDR=(address<<1) | 1; // slave address + read mode
+ TWCR = BV(TWINT) | BV(TWEN) | BV(TWEA); // generate command
+
+ while(!(TWCR&128)); // wait for send OK + ACK/NACK from slave
+ if ((TWSR&248)==0x38); // Arbitration lost or NOT ACK -> ERROR
+ if ((TWSR&248)==0x40) OK=true; // SLA+R was sent, ACK received -> OK
+ if ((TWSR&248)==0x48); // SLA+W was sent, ACK not received -> ERROR
+ sbi(TWCR, TWINT); // OK
+ }
+}
+
+// This is used by send() called by the master //
+void I2C::sendByte(uint8_t data)
+{
+ TWDR = data; // send one data byte
+ TWCR = (1<<TWINT)|(1<<TWEA)|(1<<TWEN); // start transmission
+
+ while(!(TWCR&(1<<TWINT))); // wait for TWCR to become 0
+ if ((TWSR&248)==TW_MT_DATA_ACK); // data sent, ACK received -> OK
+ if ((TWSR&248)==TW_MT_DATA_NACK); // data sent, ACK not received -> ERROR
+}
+
+
+void I2C::sendStop()
+{
+ TWCR = BV(TWINT) | BV(TWEN) | BV(TWEA) | BV(TWSTO); // generate stop command
+}
+
+// Note: in qfix, the I2C address is the board identifier
+// and the first data byte ist the logical ID
+inline
+void I2C::send(uint8_t address, uint8_t data)
+{
+ send(address, &data, 1);
+}
+
+
+// Note: in qfix, the I2C address is the board identifier
+// and the first data byte ist the logical ID
+void I2C::send(uint8_t address, uint8_t* data, int length)
+{
+ sendStartSLA_W(address);
+ if (err!=0)
+ {
+ sendStop();
+ err = ERROR_NOT_SENT;
+ return;
+ }
+
+ for (int i=0; i<length; i++) {
+ sendByte(data[i]);
+ }
+ sendStop();
+ err = 0; // OK
+}
+
+
+
+int I2C::receive(uint8_t* data)
+{
+ TWCR = (1<<TWINT)|(1<<TWEA)|(1<<TWEN);
+
+ while(!(TWCR&(1<<TWINT))); // wait for something
+
+ int len = 0;
+ while ((TWSR&248)==TW_SR_DATA_ACK) { // data received (in TWDR), ACK sent
+ data[len] = TWDR; // read received byte
+ len++;
+ TWCR = (1<<TWINT)|(1<<TWEA)|(1<<TWEN);
+ while(!(TWCR&(1<<TWINT))) { } // wait for OK
+ }
+
+ if ((TWSR&248)==TW_SR_STOP) { } // STOP (or new START) received -> OK
+ TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA); // generate command
+
+ return len;
+}
+
+
+void I2C::readByte(uint8_t& data)
+{
+ while(!isAction());
+
+ if ((TWSR&248)==0x50); // data received: NACK
+ if ((TWSR&248)==0x58); // data received: ACK
+ data = TWDR;
+ TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA); // generate command
+}
+
+
+void I2C::get(uint8_t address, uint8_t* data, int length)
+{
+ sendStartSLA_R(address);
+ if (err!=0)
+ {
+ sendStop();
+ err = ERROR_NOT_SENT;
+ return;
+ }
+
+ for (int i=0; i<length; i++) {
+ readByte(data[i]);
+ }
+ sendStop();
+}
+
+
+
+
+void I2C::returnBytes(uint8_t* data, int len, bool lastOne)
+{
+ for (int i=0; i<len; i++) {
+ TWDR=data[i]; // byte to send
+
+ if (i==len-1) TWCR = (1<<TWINT) | (1<<TWEN); // command for last byte (no TWEA is correct!)
+ else TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA); // command for all others
+
+ while(!(TWCR&(1<<TWINT))); // wait for OK
+
+ if ((TWSR&248)==0xB8); // data sent, ACK ACK received -> OK
+ if ((TWSR&248)==0xC0); // data sent, ACK not received -> ERROR
+ }
+ TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA); // generate command
+}
+
+
+#endif
+