UAVX Multicopter Flight Controller.

Dependencies:   mbed

i2c.c

Committer:
gke
Date:
2011-04-26
Revision:
2:90292f8bd179
Parent:
1:1e3318a30ddd

File content as of revision 2:90292f8bd179:

// ===============================================================================================
// =                              UAVXArm Quadrocopter Controller                                =
// =                           Copyright (c) 2008 by Prof. Greg Egan                             =
// =                 Original V3.15 Copyright (c) 2007 Ing. Wolfgang Mahringer                   =
// =                           http://code.google.com/p/uavp-mods/                               =
// ===============================================================================================

//    This is part of UAVXArm.

//    UAVXArm is free software: you can redistribute it and/or modify it under the terms of the GNU
//    General Public License as published by the Free Software Foundation, either version 3 of the
//    License, or (at your option) any later version.

//    UAVXArm is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without
//    even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//    See the GNU General Public License for more details.

//    You should have received a copy of the GNU General Public License along with this program.
//    If not, see http://www.gnu.org/licenses/

#include "UAVXArm.h"

boolean I2C0AddressResponds(uint8);
#ifdef HAVE_I2C1
boolean I2C1AddressResponds(uint8);
#endif // HAVE_I2C1
void TrackMinI2CRate(uint32);
void ShowI2CDeviceName(uint8);
uint8 ScanI2CBus(void);
boolean ESCWaitClkHi(void);
void ProgramSlaveAddress(uint8);
void ConfigureESCs(void);

uint16 I2CError[256];
uint8 I2CSpeed = 10; // 100KHz
uint32 MinI2CRate = I2C_MAX_RATE_HZ;

//______________________________________________________________________________________________

// Software I2C

#ifdef SW_I2C

void SDelay(uint16 d) { // 1.25 + 0.0475 * n uS ~0.05uS per click

    volatile int16 v;

    for (v = 0; v < d ; v++ ) {};

}  // SDelay

/*
#define SCLLowStartT SDelay(I2CSpeed)
#define DataLowPadT SDelay(I2CSpeed)
#define SCLLowPadT SDelay(I2CSpeed)
#define SCLHighT SDelay(I2CSpeed)
*/

#if ( I2C_MAX_RATE_HZ == 400000 )

#define SCLLowStartT SDelay(10)
#define DataLowPadT SDelay(16) // 10
#define SCLLowPadT SDelay(6)
#define SCLHighT SDelay(10)

#else

#define SCLLowStartT  SDelay(82)
#define DataLowPadT SDelay(82)
#define SCLLowPadT SDelay(82)
#define SCLHighT SDelay(85)

#endif //I2C400KHZ


#define I2CSDALow {I2C0SDA.write(0);I2C0SDA.output();SCLLowPadT;}
#define I2CSDAFloat {I2C0SDA.input();SCLLowPadT;}
#define I2CSCLLow {I2C0SCL.write(0);I2C0SCL.output();}
#define I2CSCLFloat {I2C0SCL.input();SCLHighT;}

void MyI2C::frequency(uint32 f) {
// delay depending on rate

    if ( f == 400000 )
        I2CSpeed = 10;
    else
        I2CSpeed = 80;
} // frequency

void MyI2C::start(void) {

    I2CSDAFloat;
    r = waitclock();
    I2CSDALow;
    SCLLowStartT;
    I2CSCLLow;
} // start

void MyI2C::stop(void) {

    I2CSDALow;
    r = waitclock();
    I2CSDAFloat;
    SCLLowStartT;

} // stop

boolean MyI2C::waitclock(void) {
    static uint32 s;

    I2CSCLFloat;        // set SCL to input, output a high
    s = 0;
    while ( I2C0SCL.read() == 0 )
        if ( ++s > 16000 ) { // ~1mS
            I2CError[0]++;
            // possible add SCL cycles here to attempt to force device reset
            Stats[I2CFailS]++;
            return (false);
        }
    return( true );
} // waitclock

uint8 MyI2C::read(uint8 ack) {
    static uint8 s, d;

    I2CSDAFloat;
    d = 0;
    s = 8;
    do {
        if ( waitclock() ) {
            d <<= 1;
            if ( I2C0SDA.read() ) {
                d |= 1;
                I2CSCLLow;
                DataLowPadT;
            } else {
                I2CSCLLow;
                SDelay(10);//DataLowPadT;
            }
        } else
            return( 0 );
    } while ( --s );

    if ( ack == I2C_NACK )
        I2C0SDA.write(0xffff); // Port write with mask selecting SDA - messy
    else
        I2C0SDA.write(0);
    I2C0SDA.output();

    SCLLowPadT;

    if ( waitclock() ) {
        I2CSCLLow;
        return( d );
    } else
        return( 0 );

} // read

uint8 MyI2C::write(uint8 d) {
    static uint8 s, r;

    for ( s = 0; s < 8; s++) {
        if ( d & 0x80 ) {
            I2CSDAFloat;
        } else {
            I2CSDALow;
        }

        if ( waitclock() ) {
            I2CSCLLow;
            d <<= 1;
        } else
            return(I2C_NACK);
    }

    I2CSDAFloat;
    if ( waitclock() ) {
        if ( I2C0SDA.read() )
            r = I2C_NACK;
        else
            r = I2C_ACK;
        I2CSDALow;// kill runt pulses
        I2CSCLLow;
        return ( r );
    } else {
//   I2CSCLLow;
        return(I2C_NACK);
    }

} // write

uint8 MyI2C::blockread(uint8 a, char* S, uint8 l) {
    static uint8 b;
    static boolean err;

    I2C0.start();
    err = I2C0.write(a|1) != I2C_ACK;
    for (b = 0; b < (l - 1); b++)
        S[b] = I2C0.read(I2C_ACK);
    S[l-1] = I2C0.read(I2C_NACK);
    I2C0.stop();

    return( err );
} // blockread

boolean MyI2C::blockwrite(uint8 a, const char* S, uint8 l) {
    static uint8 b;

    I2C0.start();
    if ( I2C0.write(a) != I2C_ACK ) goto BlockWriteError; // use this?
    for ( b = 0; b < l; b++ )
        if ( I2C0.write(S[b]) != I2C_ACK ) goto BlockWriteError;
    I2C0.stop();

    return(false);

BlockWriteError:
    I2C0.stop();

    return(true);

} // blockwrite

#endif // SW_I2C

//______________________________________________________________________________________________

void TrackMinI2CRate(uint32 r) {

// CURRENTLY USING DEFINE TO SPECIFY RATE - UAVXArm.h
    if ( r < MinI2CRate )
        MinI2CRate = r;
} // TrackMinI2CRate

void ShowI2CDeviceName(uint8 d) {
    TxChar(' ');
    switch ( d  ) {
        case ADXL345_ID:
            TxString("ADXL345 Acc");
            break;
        case ITG3200_ID:
            TxString("ITG3200 Gyro");
            break;
        case HMC5843_ID:
            TxString("HMC5843 Magnetometer");
            break;
        case HMC6352_ID:
            TxString("HMC6352 Compass");
            break;
        case ADS7823_ID:
            TxString("ADS7823 ADC");
            break;
        case MCP4725_ID_0xCC:
            TxString("MCP4725 DAC");
            break;
        case MCP4725_ID_0xC8:
            TxString("MCP4725 DAC");
            break;
        case BOSCH_ID:
            TxString("Bosch Baro");
            break;
        case TMP100_ID:
            TxString("TMP100 Temp");
            break;
        case PCA9551_ID:
            TxString("PCA9551 LED");
            break;
        case LISL_ID:
            TxString("LIS3L Acc");
            break;
        default:
            break;
    } // switch
    TxChar(' ');

} // ShowI2CDeviceName

boolean I2C0AddressResponds(uint8 s) {
    static boolean r;
    I2C0.start();
    r = I2C0.write(s) == I2C_ACK;
    I2C0.stop();
    return (r);
} // I2C0AddressResponds

#ifdef HAVE_IC1
boolean I2C1AddressResponds(uint8 s) {
    static boolean r;
    I2C1.start();
    r = I2C1.write(s) == I2C_ACK;
    I2C1.stop();
    return (r);
} // I2C1AddressResponds
#endif // HAVE_IC1

uint8 ScanI2CBus(void) {
    uint8 s;
    uint8 d;

    d = 0;
    TxString("Buss 0\r\n");

#ifdef SW_I2C
    TxString("I2C Ver.:\tSoftware\r\n");
#else
    TxString("I2C Ver.:\tHardware (CAUTION)\r\n");
#endif // SW_I2C

#if ( I2C_MAX_RATE_HZ == 400000 )
    TxString("Rate:\t400KHz\r\n");
#else
    TxString("Rate:\t100KHz\r\n");
#endif
    TxString("SCL Hangs:\t");
    TxVal32(I2CError[0], 0, 0);
    TxNextLine();
    for ( s = 0x10 ; s <= 0xf6 ; s += 2 ) {
        if (  I2C0AddressResponds(s) ) {
            d++;
            TxString("\t0x");
            TxValH(s);
            TxChar(HT);
            TxVal32(I2CError[s], 0, HT);
            ShowI2CDeviceName( s );
            TxNextLine();
        }
        Delay1mS(2);
    }

#ifdef HAVE_I2C1
    TxString("Buss 1\r\n");

#ifdef SW_I2C
    TxString("I2C Ver.:\tSoftware\r\n");
#else
    TxString("I2C Ver.:\tHardware (CAUTION)\r\n");
#endif // SW_I2C

#if ( I2C_MAX_RATE_HZ == 400000 )
    TxString("Rate:\t400KHz\r\n");
#else
    TxString("Rate:\t100KHz\r\n");
#endif
    TxString("SCL Hangs:\t");
    TxNextLine();
    for ( s = 0x10 ; s <= 0xf6 ; s += 2 ) {
        if ( I2C0AddressResponds(s) ) {
            d++;
            TxString("\t0x");
            TxValH(s);
            TxChar(HT);
            TxVal32(I2CError[s], 0, HT);
            ShowI2CDeviceName( s );
            TxNextLine();
        }
        Delay1mS(2);
    }
#endif // HAVE_I2C1

    PCA9551Test();

    return(d);
} // ScanI2CBus

void ProgramSlaveAddress(uint8 a) {
    static uint8 s;

    for (s = 0x10 ; s < 0xf0 ; s += 2 ) {
        I2CESC.start();
        if ( I2CESC.read(s) == I2C_ACK )
            if ( s == a ) {   // ESC is already programmed OK
                I2CESC.stop();
                TxString("\tESC at SLA 0x");
                TxValH(a);
                TxString(" is already programmed OK\r\n");
                return;
            } else {
                if ( I2CESC.read(0x87) == I2C_ACK ) // select register 0x07
                    if ( I2CESC.write( a ) == I2C_ACK ) { // new slave address
                        I2CESC.stop();
                        TxString("\tESC at SLA 0x");
                        TxValH(s);
                        TxString(" reprogrammed to SLA 0x");
                        TxValH(a);
                        TxNextLine();
                        return;
                    }
            }
        I2CESC.stop();
    }
    TxString("\tESC at SLA 0x");
    TxValH(a);
    TxString(" no response - check cabling and pullup resistors!\r\n");
} // ProgramSlaveAddress

boolean CheckESCBus(void) {
    return ( true );
} // CheckESCBus

void ConfigureESCs(void) {
    int8 m;

    if ( (int8)P[ESCType] == ESCYGEI2C ) {
        TxString("\r\nProgram YGE ESCs\r\n");
        for ( m = 0 ; m < NoOfI2CESCOutputs ; m++ ) {
            TxString("Connect ONLY M");
            TxChar('1' + m);
            TxString(" ESC, then press any key \r\n");
            while ( PollRxChar() != 'x' ); // UAVPSet uses 'x' for any key button
            //    TxString("\r\n");
            ProgramSlaveAddress( 0x62 + ( m*2 ));
        }
        TxString("\r\nConnect ALL ESCs and power-cycle the Quadrocopter\r\n");
    } else
        TxString("\r\nYGEI2C not selected as ESC?\r\n");
} // ConfigureESCs

void InitI2C(void) {

    uint8 i;

    for ( i = 0; i < 255; i++ )
        I2CError[i] = 0;

} // InitI2C