279 lines
6.7 KiB
C
279 lines
6.7 KiB
C
![]() |
//------------------------------------------------------------------
|
||
|
// 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
|
||
|
|