This repository has been archived on 2025-03-02. You can view files and clone it, but cannot push or open issues or pull requests.
rc2007-soccer/source/qFix/qfixI2C.h

279 lines
6.7 KiB
C
Raw Normal View History

//------------------------------------------------------------------
// 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