Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

i2c.c

Go to the documentation of this file.
00001 /*! \file i2c.c \brief I2C interface using AVR Two-Wire Interface (TWI) hardware. */
00002 //*****************************************************************************
00003 //
00004 // File Name    : 'i2c.c'
00005 // Title        : I2C interface using AVR Two-Wire Interface (TWI) hardware
00006 // Author       : Pascal Stang - Copyright (C) 2002-2003
00007 // Created      : 2002.06.25
00008 // Revised      : 2003.03.02
00009 // Version      : 0.9
00010 // Target MCU   : Atmel AVR series
00011 // Editor Tabs  : 4
00012 //
00013 // This code is distributed under the GNU Public License
00014 //      which can be found at http://www.gnu.org/licenses/gpl.txt
00015 //
00016 //*****************************************************************************
00017 
00018 #include <avr/io.h>
00019 #include <avr/signal.h>
00020 #include <avr/interrupt.h>
00021 
00022 #include "i2c.h"
00023 
00024 #include "rprintf.h"    // include printf function library
00025 #include "uart2.h"
00026 
00027 // Standard I2C bit rates are:
00028 // 100KHz for slow speed
00029 // 400KHz for high speed
00030 
00031 //#define I2C_DEBUG
00032 
00033 // I2C state and address variables
00034 static volatile eI2cStateType I2cState;
00035 static u08 I2cDeviceAddrRW;
00036 // send/transmit buffer (outgoing data)
00037 static u08 I2cSendData[I2C_SEND_DATA_BUFFER_SIZE];
00038 static u08 I2cSendDataIndex;
00039 static u08 I2cSendDataLength;
00040 // receive buffer (incoming data)
00041 static u08 I2cReceiveData[I2C_RECEIVE_DATA_BUFFER_SIZE];
00042 static u08 I2cReceiveDataIndex;
00043 static u08 I2cReceiveDataLength;
00044 
00045 // function pointer to i2c receive routine
00046 //! I2cSlaveReceive is called when this processor
00047 // is addressed as a slave for writing
00048 static void (*i2cSlaveReceive)(u08 receiveDataLength, u08* recieveData);
00049 //! I2cSlaveTransmit is called when this processor
00050 // is addressed as a slave for reading
00051 static u08 (*i2cSlaveTransmit)(u08 transmitDataLengthMax, u08* transmitData);
00052 
00053 // functions
00054 void i2cInit(void)
00055 {
00056     // set pull-up resistors on I2C bus pins
00057     // TODO: should #ifdef these
00058     sbi(PORTC, 0);  // i2c SCL on ATmega163,323,16,32,etc
00059     sbi(PORTC, 1);  // i2c SDA on ATmega163,323,16,32,etc
00060     sbi(PORTD, 0);  // i2c SCL on ATmega128,64
00061     sbi(PORTD, 1);  // i2c SDA on ATmega128,64
00062 
00063     // clear SlaveReceive and SlaveTransmit handler to null
00064     i2cSlaveReceive = 0;
00065     i2cSlaveTransmit = 0;
00066     // set i2c bit rate to 100KHz
00067     i2cSetBitrate(100);
00068     // enable TWI (two-wire interface)
00069     sbi(TWCR, TWEN);
00070     // set state
00071     I2cState = I2C_IDLE;
00072     // enable TWI interrupt and slave address ACK
00073     sbi(TWCR, TWIE);
00074     sbi(TWCR, TWEA);
00075     //outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
00076     // enable interrupts
00077     sei();
00078 }
00079 
00080 void i2cSetBitrate(u16 bitrateKHz)
00081 {
00082     u08 bitrate_div;
00083     // set i2c bitrate
00084     // SCL freq = F_CPU/(16+2*TWBR))
00085     #ifdef TWPS0
00086         // for processors with additional bitrate division (mega128)
00087         // SCL freq = F_CPU/(16+2*TWBR*4^TWPS)
00088         // set TWPS to zero
00089         cbi(TWSR, TWPS0);
00090         cbi(TWSR, TWPS1);
00091     #endif
00092     // calculate bitrate division   
00093     bitrate_div = ((F_CPU/1000l)/bitrateKHz);
00094     if(bitrate_div >= 16)
00095         bitrate_div = (bitrate_div-16)/2;
00096     outb(TWBR, bitrate_div);
00097 }
00098 
00099 void i2cSetLocalDeviceAddr(u08 deviceAddr, u08 genCallEn)
00100 {
00101     // set local device address (used in slave mode only)
00102     outb(TWAR, ((deviceAddr&0xFE) | (genCallEn?1:0)) );
00103 }
00104 
00105 void i2cSetSlaveReceiveHandler(void (*i2cSlaveRx_func)(u08 receiveDataLength, u08* recieveData))
00106 {
00107     i2cSlaveReceive = i2cSlaveRx_func;
00108 }
00109 
00110 void i2cSetSlaveTransmitHandler(u08 (*i2cSlaveTx_func)(u08 transmitDataLengthMax, u08* transmitData))
00111 {
00112     i2cSlaveTransmit = i2cSlaveTx_func;
00113 }
00114 
00115 inline void i2cSendStart(void)
00116 {
00117     // send start condition
00118     outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWSTA));
00119 }
00120 
00121 inline void i2cSendStop(void)
00122 {
00123     // transmit stop condition
00124     // leave with TWEA on for slave receiving
00125     outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA)|BV(TWSTO));
00126 }
00127 
00128 inline void i2cWaitForComplete(void)
00129 {
00130     // wait for i2c interface to complete operation
00131     while( !(inb(TWCR) & BV(TWINT)) );
00132 }
00133 
00134 inline void i2cSendByte(u08 data)
00135 {
00136     // save data to the TWDR
00137     outb(TWDR, data);
00138     // begin send
00139     outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
00140 }
00141 
00142 inline void i2cReceiveByte(u08 ackFlag)
00143 {
00144     // begin receive over i2c
00145     if( ackFlag )
00146     {
00147         // ackFlag = TRUE: ACK the recevied data
00148         outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
00149     }
00150     else
00151     {
00152         // ackFlag = FALSE: NACK the recevied data
00153         outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
00154     }
00155 }
00156 
00157 inline u08 i2cGetReceivedByte(void)
00158 {
00159     // retieve received data byte from i2c TWDR
00160     return( inb(TWDR) );
00161 }
00162 
00163 inline u08 i2cGetStatus(void)
00164 {
00165     // retieve current i2c status from i2c TWSR
00166     return( inb(TWSR) );
00167 }
00168 
00169 void i2cMasterSend(u08 deviceAddr, u08 length, u08* data)
00170 {
00171     u08 i;
00172     // wait for interface to be ready
00173     while(I2cState);
00174     // set state
00175     I2cState = I2C_MASTER_TX;
00176     // save data
00177     I2cDeviceAddrRW = (deviceAddr & 0xFE);  // RW cleared: write operation
00178     for(i=0; i<length; i++)
00179         I2cSendData[i] = *data++;
00180     I2cSendDataIndex = 0;
00181     I2cSendDataLength = length;
00182     // send start condition
00183     i2cSendStart();
00184 }
00185 
00186 void i2cMasterReceive(u08 deviceAddr, u08 length, u08* data)
00187 {
00188     u08 i;
00189     // wait for interface to be ready
00190     while(I2cState);
00191     // set state
00192     I2cState = I2C_MASTER_RX;
00193     // save data
00194     I2cDeviceAddrRW = (deviceAddr|0x01);    // RW set: read operation
00195     I2cReceiveDataIndex = 0;
00196     I2cReceiveDataLength = length;
00197     // send start condition
00198     i2cSendStart();
00199     // wait for data
00200     while(I2cState);
00201     // return data
00202     for(i=0; i<length; i++)
00203         *data++ = I2cReceiveData[i];
00204 }
00205 
00206 u08 i2cMasterSendNI(u08 deviceAddr, u08 length, u08* data)
00207 {
00208     u08 retval = I2C_OK;
00209 
00210     // disable TWI interrupt
00211     cbi(TWCR, TWIE);
00212 
00213     // send start condition
00214     i2cSendStart();
00215     i2cWaitForComplete();
00216 
00217     // send device address with write
00218     i2cSendByte( deviceAddr & 0xFE );
00219     i2cWaitForComplete();
00220 
00221     // check if device is present and live
00222     if( inb(TWSR) == TW_MT_SLA_ACK)
00223     {
00224         // send data
00225         while(length)
00226         {
00227             i2cSendByte( *data++ );
00228             i2cWaitForComplete();
00229             length--;
00230         }
00231     }
00232     else
00233     {
00234         // device did not ACK it's address,
00235         // data will not be transferred
00236         // return error
00237         retval = I2C_ERROR_NODEV;
00238     }
00239 
00240     // transmit stop condition
00241     // leave with TWEA on for slave receiving
00242     i2cSendStop();
00243     while( !(inb(TWCR) & BV(TWSTO)) );
00244 
00245     // enable TWI interrupt
00246     sbi(TWCR, TWIE);
00247 
00248     return retval;
00249 }
00250 
00251 u08 i2cMasterReceiveNI(u08 deviceAddr, u08 length, u08 *data)
00252 {
00253     u08 retval = I2C_OK;
00254 
00255     // disable TWI interrupt
00256     cbi(TWCR, TWIE);
00257 
00258     // send start condition
00259     i2cSendStart();
00260     i2cWaitForComplete();
00261 
00262     // send device address with read
00263     i2cSendByte( deviceAddr | 0x01 );
00264     i2cWaitForComplete();
00265 
00266     // check if device is present and live
00267     if( inb(TWSR) == TW_MR_SLA_ACK)
00268     {
00269         // accept receive data and ack it
00270         while(length > 1)
00271         {
00272             i2cReceiveByte(TRUE);
00273             i2cWaitForComplete();
00274             *data++ = i2cGetReceivedByte();
00275             // decrement length
00276             length--;
00277         }
00278 
00279         // accept receive data and nack it (last-byte signal)
00280         i2cReceiveByte(FALSE);
00281         i2cWaitForComplete();
00282         *data++ = i2cGetReceivedByte();
00283     }
00284     else
00285     {
00286         // device did not ACK it's address,
00287         // data will not be transferred
00288         // return error
00289         retval = I2C_ERROR_NODEV;
00290     }
00291 
00292     // transmit stop condition
00293     // leave with TWEA on for slave receiving
00294     i2cSendStop();
00295 
00296     // enable TWI interrupt
00297     sbi(TWCR, TWIE);
00298 
00299     return retval;
00300 }
00301 /*
00302 void i2cMasterTransferNI(u08 deviceAddr, u08 sendlength, u08* senddata, u08 receivelength, u08* receivedata)
00303 {
00304     // disable TWI interrupt
00305     cbi(TWCR, TWIE);
00306 
00307     // send start condition
00308     i2cSendStart();
00309     i2cWaitForComplete();
00310 
00311     // if there's data to be sent, do it
00312     if(sendlength)
00313     {
00314         // send device address with write
00315         i2cSendByte( deviceAddr & 0xFE );
00316         i2cWaitForComplete();
00317         
00318         // send data
00319         while(sendlength)
00320         {
00321             i2cSendByte( *senddata++ );
00322             i2cWaitForComplete();
00323             sendlength--;
00324         }
00325     }
00326 
00327     // if there's data to be received, do it
00328     if(receivelength)
00329     {
00330         // send repeated start condition
00331         i2cSendStart();
00332         i2cWaitForComplete();
00333 
00334         // send device address with read
00335         i2cSendByte( deviceAddr | 0x01 );
00336         i2cWaitForComplete();
00337 
00338         // accept receive data and ack it
00339         while(receivelength > 1)
00340         {
00341             i2cReceiveByte(TRUE);
00342             i2cWaitForComplete();
00343             *receivedata++ = i2cGetReceivedByte();
00344             // decrement length
00345             receivelength--;
00346         }
00347 
00348         // accept receive data and nack it (last-byte signal)
00349         i2cReceiveByte(TRUE);
00350         i2cWaitForComplete();
00351         *receivedata++ = i2cGetReceivedByte();
00352     }
00353     
00354     // transmit stop condition
00355     // leave with TWEA on for slave receiving
00356     i2cSendStop();
00357     while( !(inb(TWCR) & BV(TWSTO)) );
00358 
00359     // enable TWI interrupt
00360     sbi(TWCR, TWIE);
00361 }
00362 */
00363 
00364 //! I2C (TWI) interrupt service routine
00365 SIGNAL(SIG_2WIRE_SERIAL)
00366 {
00367     // read status bits
00368     u08 status = inb(TWSR) & TWSR_STATUS_MASK;
00369 
00370     switch(status)
00371     {
00372     // Master General
00373     case TW_START:                      // 0x08: Sent start condition
00374     case TW_REP_START:                  // 0x10: Sent repeated start condition
00375         #ifdef I2C_DEBUG
00376         rprintfInit(uart1AddToTxBuffer);
00377         rprintf("I2C: M->START\r\n");
00378         rprintfInit(uart1SendByte);
00379         #endif
00380         // send device address
00381         i2cSendByte(I2cDeviceAddrRW);
00382         break;
00383     
00384     // Master Transmitter & Receiver status codes
00385     case TW_MT_SLA_ACK:                 // 0x18: Slave address acknowledged
00386     case TW_MT_DATA_ACK:                // 0x28: Data acknowledged
00387         #ifdef I2C_DEBUG
00388         rprintfInit(uart1AddToTxBuffer);
00389         rprintf("I2C: MT->SLA_ACK or DATA_ACK\r\n");
00390         rprintfInit(uart1SendByte);
00391         #endif
00392         if(I2cSendDataIndex < I2cSendDataLength)
00393         {
00394             // send data
00395             i2cSendByte( I2cSendData[I2cSendDataIndex++] );
00396         }
00397         else
00398         {
00399             // transmit stop condition, enable SLA ACK
00400             i2cSendStop();
00401             // set state
00402             I2cState = I2C_IDLE;
00403         }
00404         break;
00405     case TW_MR_DATA_NACK:               // 0x58: Data received, NACK reply issued
00406         #ifdef I2C_DEBUG
00407         rprintfInit(uart1AddToTxBuffer);
00408         rprintf("I2C: MR->DATA_NACK\r\n");
00409         rprintfInit(uart1SendByte);
00410         #endif
00411         // store final received data byte
00412         I2cReceiveData[I2cReceiveDataIndex++] = inb(TWDR);
00413         // continue to transmit STOP condition
00414     case TW_MR_SLA_NACK:                // 0x48: Slave address not acknowledged
00415     case TW_MT_SLA_NACK:                // 0x20: Slave address not acknowledged
00416     case TW_MT_DATA_NACK:               // 0x30: Data not acknowledged
00417         #ifdef I2C_DEBUG
00418         rprintfInit(uart1AddToTxBuffer);
00419         rprintf("I2C: MTR->SLA_NACK or MT->DATA_NACK\r\n");
00420         rprintfInit(uart1SendByte);
00421         #endif
00422         // transmit stop condition, enable SLA ACK
00423         i2cSendStop();
00424         // set state
00425         I2cState = I2C_IDLE;
00426         break;
00427     case TW_MT_ARB_LOST:                // 0x38: Bus arbitration lost
00428     //case TW_MR_ARB_LOST:              // 0x38: Bus arbitration lost
00429         #ifdef I2C_DEBUG
00430         rprintfInit(uart1AddToTxBuffer);
00431         rprintf("I2C: MT->ARB_LOST\r\n");
00432         rprintfInit(uart1SendByte);
00433         #endif
00434         // release bus
00435         outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
00436         // set state
00437         I2cState = I2C_IDLE;
00438         // release bus and transmit start when bus is free
00439         //outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWSTA));
00440         break;
00441     case TW_MR_DATA_ACK:                // 0x50: Data acknowledged
00442         #ifdef I2C_DEBUG
00443         rprintfInit(uart1AddToTxBuffer);
00444         rprintf("I2C: MR->DATA_ACK\r\n");
00445         rprintfInit(uart1SendByte);
00446         #endif
00447         // store received data byte
00448         I2cReceiveData[I2cReceiveDataIndex++] = inb(TWDR);
00449         // fall-through to see if more bytes will be received
00450     case TW_MR_SLA_ACK:                 // 0x40: Slave address acknowledged
00451         #ifdef I2C_DEBUG
00452         rprintfInit(uart1AddToTxBuffer);
00453         rprintf("I2C: MR->SLA_ACK\r\n");
00454         rprintfInit(uart1SendByte);
00455         #endif
00456         if(I2cReceiveDataIndex < (I2cReceiveDataLength-1))
00457             // data byte will be received, reply with ACK (more bytes in transfer)
00458             i2cReceiveByte(TRUE);
00459         else
00460             // data byte will be received, reply with NACK (final byte in transfer)
00461             i2cReceiveByte(FALSE);
00462         break;
00463 
00464     // Slave Receiver status codes
00465     case TW_SR_SLA_ACK:                 // 0x60: own SLA+W has been received, ACK has been returned
00466     case TW_SR_ARB_LOST_SLA_ACK:        // 0x68: own SLA+W has been received, ACK has been returned
00467     case TW_SR_GCALL_ACK:               // 0x70:     GCA+W has been received, ACK has been returned
00468     case TW_SR_ARB_LOST_GCALL_ACK:      // 0x78:     GCA+W has been received, ACK has been returned
00469         #ifdef I2C_DEBUG
00470         rprintfInit(uart1AddToTxBuffer);
00471         rprintf("I2C: SR->SLA_ACK\r\n");
00472         rprintfInit(uart1SendByte);
00473         #endif
00474         // we are being addressed as slave for writing (data will be received from master)
00475         // set state
00476         I2cState = I2C_SLAVE_RX;
00477         // prepare buffer
00478         I2cReceiveDataIndex = 0;
00479         // receive data byte and return ACK
00480         outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
00481         break;
00482     case TW_SR_DATA_ACK:                // 0x80: data byte has been received, ACK has been returned
00483     case TW_SR_GCALL_DATA_ACK:          // 0x90: data byte has been received, ACK has been returned
00484         #ifdef I2C_DEBUG
00485         rprintfInit(uart1AddToTxBuffer);
00486         rprintf("I2C: SR->DATA_ACK\r\n");
00487         rprintfInit(uart1SendByte);
00488         #endif
00489         // get previously received data byte
00490         I2cReceiveData[I2cReceiveDataIndex++] = inb(TWDR);
00491         // check receive buffer status
00492         if(I2cReceiveDataIndex < I2C_RECEIVE_DATA_BUFFER_SIZE)
00493         {
00494             // receive data byte and return ACK
00495             i2cReceiveByte(TRUE);
00496             //outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
00497         }
00498         else
00499         {
00500             // receive data byte and return NACK
00501             i2cReceiveByte(FALSE);
00502             //outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
00503         }
00504         break;
00505     case TW_SR_DATA_NACK:               // 0x88: data byte has been received, NACK has been returned
00506     case TW_SR_GCALL_DATA_NACK:         // 0x98: data byte has been received, NACK has been returned
00507         #ifdef I2C_DEBUG
00508         rprintfInit(uart1AddToTxBuffer);
00509         rprintf("I2C: SR->DATA_NACK\r\n");
00510         rprintfInit(uart1SendByte);
00511         #endif
00512         // receive data byte and return NACK
00513         i2cReceiveByte(FALSE);
00514         //outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
00515         break;
00516     case TW_SR_STOP:                    // 0xA0: STOP or REPEATED START has been received while addressed as slave
00517         #ifdef I2C_DEBUG
00518         rprintfInit(uart1AddToTxBuffer);
00519         rprintf("I2C: SR->SR_STOP\r\n");
00520         rprintfInit(uart1SendByte);
00521         #endif
00522         // switch to SR mode with SLA ACK
00523         outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
00524         // i2c receive is complete, call i2cSlaveReceive
00525         if(i2cSlaveReceive) i2cSlaveReceive(I2cReceiveDataIndex, I2cReceiveData);
00526         // set state
00527         I2cState = I2C_IDLE;
00528         break;
00529 
00530     // Slave Transmitter
00531     case TW_ST_SLA_ACK:                 // 0xA8: own SLA+R has been received, ACK has been returned
00532     case TW_ST_ARB_LOST_SLA_ACK:        // 0xB0:     GCA+R has been received, ACK has been returned
00533         #ifdef I2C_DEBUG
00534         rprintfInit(uart1AddToTxBuffer);
00535         rprintf("I2C: ST->SLA_ACK\r\n");
00536         rprintfInit(uart1SendByte);
00537         #endif
00538         // we are being addressed as slave for reading (data must be transmitted back to master)
00539         // set state
00540         I2cState = I2C_SLAVE_TX;
00541         // request data from application
00542         if(i2cSlaveTransmit) I2cSendDataLength = i2cSlaveTransmit(I2C_SEND_DATA_BUFFER_SIZE, I2cSendData);
00543         // reset data index
00544         I2cSendDataIndex = 0;
00545         // fall-through to transmit first data byte
00546     case TW_ST_DATA_ACK:                // 0xB8: data byte has been transmitted, ACK has been received
00547         #ifdef I2C_DEBUG
00548         rprintfInit(uart1AddToTxBuffer);
00549         rprintf("I2C: ST->DATA_ACK\r\n");
00550         rprintfInit(uart1SendByte);
00551         #endif
00552         // transmit data byte
00553         outb(TWDR, I2cSendData[I2cSendDataIndex++]);
00554         if(I2cSendDataIndex < I2cSendDataLength)
00555             // expect ACK to data byte
00556             outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
00557         else
00558             // expect NACK to data byte
00559             outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
00560         break;
00561     case TW_ST_DATA_NACK:               // 0xC0: data byte has been transmitted, NACK has been received
00562     case TW_ST_LAST_DATA:               // 0xC8:
00563         #ifdef I2C_DEBUG
00564         rprintfInit(uart1AddToTxBuffer);
00565         rprintf("I2C: ST->DATA_NACK or LAST_DATA\r\n");
00566         rprintfInit(uart1SendByte);
00567         #endif
00568         // all done
00569         // switch to open slave
00570         outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
00571         // set state
00572         I2cState = I2C_IDLE;
00573         break;
00574 
00575     // Misc
00576     case TW_NO_INFO:                    // 0xF8: No relevant state information
00577         // do nothing
00578         #ifdef I2C_DEBUG
00579         rprintfInit(uart1AddToTxBuffer);
00580         rprintf("I2C: NO_INFO\r\n");
00581         rprintfInit(uart1SendByte);
00582         #endif
00583         break;
00584     case TW_BUS_ERROR:                  // 0x00: Bus error due to illegal start or stop condition
00585         #ifdef I2C_DEBUG
00586         rprintfInit(uart1AddToTxBuffer);
00587         rprintf("I2C: BUS_ERROR\r\n");
00588         rprintfInit(uart1SendByte);
00589         #endif
00590         // reset internal hardware and release bus
00591         outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWSTO)|BV(TWEA));
00592         // set state
00593         I2cState = I2C_IDLE;
00594         break;
00595     }
00596 }
00597 
00598 eI2cStateType i2cGetState(void)
00599 {
00600     return I2cState;
00601 }

Generated on Tue Sep 20 03:11:42 2005 for Procyon AVRlib by  doxygen 1.4.2