Society of Robots - Robot Forum
Software => Software => Topic started by: jzaun on April 10, 2012, 07:14:53 PM
-
I've got an i2c device froms sparkfun, 9 Degrees of Freedom - Sensor Stick (http://www.sparkfun.com/products/10724). I'm trying to get it to do something. Things are complicated as I can't use webbotlib. So I'm trying to use as much of the old axon code as I can. Issues arise as I'm using newer gcc versions than other people are. At any rate, I've got the following i2c code:
/*************************************************************************
* Title: I2C master library using hardware TWI interface
* Software: AVR-GCC 3.4.3 / avr-libc 1.2.3
* Target: any AVR device with hardware TWI
* Usage: API compatible with I2C Software Library i2cmaster.h
**************************************************************************/
#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <compat/twi.h>
#include <stdio.h>
#include "i2c.h"
#include "util.h"
// Standard I2C bit rates are:
// 100KHz for slow speed
// 400KHz for high speed
#define I2C_DEBUG
// I2C state and address variables
static volatile eI2cStateType I2cState;
static uint8_t I2cDeviceAddrRW;
// send/transmit buffer (outgoing data)
static uint8_t I2cSendData[I2C_SEND_DATA_BUFFER_SIZE];
static uint8_t I2cSendDataIndex;
static uint8_t I2cSendDataLength;
// receive buffer (incoming data)
static uint8_t I2cReceiveData[I2C_RECEIVE_DATA_BUFFER_SIZE];
static uint8_t I2cReceiveDataIndex;
static uint8_t I2cReceiveDataLength;
// function pointer to i2c receive routine
//! I2cSlaveReceive is called when this processor
// is addressed as a slave for writing
static void (*i2cSlaveReceive)(uint8_t receiveDataLength, uint8_t* recieveData);
//! I2cSlaveTransmit is called when this processor
// is addressed as a slave for reading
static uint8_t (*i2cSlaveTransmit)(uint8_t transmitDataLengthMax, uint8_t* transmitData);
// functions
void i2cInit(void)
{
#ifdef I2C_DEBUG
printf("I2C: INIT\r\n");
#endif
// set pull-up resistors on I2C bus pins
PORTD |= _BV(0); // i2c SCL on 640
PORTD |= _BV(1); // i2c SDA on 640
cli();
// clear SlaveReceive and SlaveTransmit handler to null
i2cSlaveReceive = 0;
i2cSlaveTransmit = 0;
// set i2c bit rate to 100KHz
i2cSetBitrate(100);
// enable TWI (two-wire interface)
TWCR |= _BV(TWEN);
// set state
I2cState = I2C_IDLE;
// enable TWI interrupt and slave address ACK
TWCR |= (TWCR & TWCR_CMD_MASK) | _BV(TWINT) | _BV(TWIE);
TWCR |= _BV(TWEA);
//TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
// enable interrupts
sei();
}
void i2cSetBitrate(uint16_t bitrateKHz)
{
uint8_t bitrate_div;
// set i2c bitrate
// SCL freq = F_CPU/(16+2*TWBR))
#ifdef TWPS0
// for processors with additional bitrate division (mega128)
// SCL freq = F_CPU/(16+2*TWBR*4^TWPS)
// set TWPS to zero
TWSR &= ~(BV(TWPS0));
TWSR &= ~(BV(TWPS1));
#endif
// calculate bitrate division
bitrate_div = ((F_CPU/1000l)/bitrateKHz);
if(bitrate_div >= 16)
bitrate_div = (bitrate_div-16)/2;
TWBR = bitrate_div;
}
void i2cSetLocalDeviceAddr(uint8_t deviceAddr, uint8_t genCallEn)
{
// set local device address (used in slave mode only)
TWAR = ((deviceAddr&0xFE) | (genCallEn?1:0));
}
void i2cSetSlaveReceiveHandler(void (*i2cSlaveRx_func)(uint8_t receiveDataLength, uint8_t* recieveData))
{
i2cSlaveReceive = i2cSlaveRx_func;
}
void i2cSetSlaveTransmitHandler(uint8_t (*i2cSlaveTx_func)(uint8_t transmitDataLengthMax, uint8_t* transmitData))
{
i2cSlaveTransmit = i2cSlaveTx_func;
}
void i2cSendStart(void)
{
//WRITE_sda();
// send start condition
TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN);
}
void i2cSendStop(void)
{
// transmit stop condition
TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO);
}
void i2cWaitForComplete(void)
{
// wait for i2c interface to complete operation
while( !(TWCR & _BV(TWINT)) );
}
void i2cSendByte(unsigned char data)
{
//WRITE_sda();
// save data to the TWDR
TWDR = data;
// begin send
TWCR = (TWCR & TWCR_CMD_MASK) | _BV(TWINT);
}
inline void i2cReceiveByte(uint8_t ackFlag)
{
// begin receive over i2c
if(ackFlag)
{
// ackFlag = TRUE: ACK the recevied data
TWCR = (TWCR & TWCR_CMD_MASK) | _BV(TWINT) | _BV(TWEA);
}
else
{
// ackFlag = FALSE: NACK the recevied data
TWCR = (TWCR & TWCR_CMD_MASK) | _BV(TWINT);
}
}
inline uint8_t i2cGetReceivedByte(void)
{
// retieve received data byte from i2c TWDR
return TWDR;
}
inline uint8_t i2cGetStatus(void)
{
// retieve current i2c status from i2c TWSR
return TWSR;
}
void i2cMasterSend(uint8_t deviceAddr, uint8_t length, uint8_t* data)
{
uint8_t i;
// wait for interface to be ready
while(I2cState);
// set state
I2cState = I2C_MASTER_TX;
// save data
I2cDeviceAddrRW = (deviceAddr & 0xFE); // RW cleared: write operation
for(i=0; i<length; i++)
I2cSendData[i] = *data++;
I2cSendDataIndex = 0;
I2cSendDataLength = length;
// send start condition
i2cSendStart();
}
void i2cMasterReceive(uint8_t deviceAddr, uint8_t length, uint8_t* data)
{
uint8_t i;
// wait for interface to be ready
while(I2cState);
// set state
I2cState = I2C_MASTER_RX;
// save data
I2cDeviceAddrRW = (deviceAddr|0x01); // RW set: read operation
I2cReceiveDataIndex = 0;
I2cReceiveDataLength = length;
// send start condition
i2cSendStart();
// wait for data
while(I2cState);
// return data
for(i=0; i<length; i++)
*data++ = I2cReceiveData[i];
}
uint8_t i2cMasterSendNI(uint8_t deviceAddr, uint8_t length, uint8_t* data)
{
uint8_t retval = I2C_OK;
// disable TWI interrupt
TWCR &= ~(BV(TWIE));
// send start condition
i2cSendStart();
i2cWaitForComplete();
// send device address with write
i2cSendByte( deviceAddr & 0xFE );
i2cWaitForComplete();
// check if device is present and live
if(TWSR == TW_MT_SLA_ACK)
{
// send data
while(length)
{
i2cSendByte( *data++ );
i2cWaitForComplete();
length--;
}
}
else
{
// device did not ACK it's address,
// data will not be transferred
// return error
retval = I2C_ERROR_NODEV;
}
// transmit stop condition
// leave with TWEA on for slave receiving
i2cSendStop();
while(!(TWCR & _BV(TWSTO)));
// enable TWI interrupt
TWCR |= _BV(TWIE);
return retval;
}
uint8_t i2cMasterReceiveNI(uint8_t deviceAddr, uint8_t length, uint8_t *data)
{
uint8_t retval = I2C_OK;
// disable TWI interrupt
TWCR &= ~(BV(TWIE));
// send start condition
i2cSendStart();
i2cWaitForComplete();
// send device address with read
i2cSendByte( deviceAddr | 0x01 );
i2cWaitForComplete();
// check if device is present and live
if(TWSR == TW_MR_SLA_ACK)
{
// accept receive data and ack it
while(length > 1)
{
i2cReceiveByte(TRUE);
i2cWaitForComplete();
*data++ = i2cGetReceivedByte();
// decrement length
length--;
}
// accept receive data and nack it (last-byte signal)
i2cReceiveByte(FALSE);
i2cWaitForComplete();
*data++ = i2cGetReceivedByte();
}
else
{
// device did not ACK it's address,
// data will not be transferred
// return error
retval = I2C_ERROR_NODEV;
}
// transmit stop condition
// leave with TWEA on for slave receiving
i2cSendStop();
// enable TWI interrupt
TWCR |= _BV(TWIE);
return retval;
}
//! I2C (TWI) interrupt service routine
SIGNAL(TWI_vect)
{
// read status bits
uint8_t status = TWSR & TWSR_STATUS_MASK;
switch(status)
{
// Master General
case TW_START: // 0x08: Sent start condition
case TW_REP_START: // 0x10: Sent repeated start condition
#ifdef I2C_DEBUG
printf("I2C: M->START\r\n");
#endif
// send device address
i2cSendByte(I2cDeviceAddrRW);
break;
// Master Transmitter & Receiver status codes
case TW_MT_SLA_ACK: // 0x18: Slave address acknowledged
case TW_MT_DATA_ACK: // 0x28: Data acknowledged
#ifdef I2C_DEBUG
printf("I2C: MT->SLA_ACK or DATA_ACK\r\n");
#endif
if(I2cSendDataIndex < I2cSendDataLength)
{
// send data
i2cSendByte( I2cSendData[I2cSendDataIndex++] );
}
else
{
// transmit stop condition, enable SLA ACK
i2cSendStop();
// set state
I2cState = I2C_IDLE;
}
break;
case TW_MR_DATA_NACK: // 0x58: Data received, NACK reply issued
#ifdef I2C_DEBUG
printf("I2C: MR->DATA_NACK\r\n");
#endif
// store final received data byte
I2cReceiveData[I2cReceiveDataIndex++] = TWDR;
// continue to transmit STOP condition
case TW_MR_SLA_NACK: // 0x48: Slave address not acknowledged
case TW_MT_SLA_NACK: // 0x20: Slave address not acknowledged
case TW_MT_DATA_NACK: // 0x30: Data not acknowledged
#ifdef I2C_DEBUG
printf("I2C: MTR->SLA_NACK or MT->DATA_NACK\r\n");
#endif
// transmit stop condition, enable SLA ACK
i2cSendStop();
// set state
I2cState = I2C_IDLE;
break;
case TW_MT_ARB_LOST: // 0x38: Bus arbitration lost
//case TW_MR_ARB_LOST: // 0x38: Bus arbitration lost
#ifdef I2C_DEBUG
printf("I2C: MT->ARB_LOST\r\n");
#endif
// release bus
TWCR = (TWCR & TWCR_CMD_MASK) | _BV(TWINT);
// set state
I2cState = I2C_IDLE;
// release bus and transmit start when bus is free
//outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWSTA));
break;
case TW_MR_DATA_ACK: // 0x50: Data acknowledged
#ifdef I2C_DEBUG
printf("I2C: MR->DATA_ACK\r\n");
#endif
// store received data byte
I2cReceiveData[I2cReceiveDataIndex++] = TWDR;
// fall-through to see if more bytes will be received
case TW_MR_SLA_ACK: // 0x40: Slave address acknowledged
#ifdef I2C_DEBUG
printf("I2C: MR->SLA_ACK\r\n");
#endif
if(I2cReceiveDataIndex < (I2cReceiveDataLength-1))
// data byte will be received, reply with ACK (more bytes in transfer)
i2cReceiveByte(TRUE);
else
// data byte will be received, reply with NACK (final byte in transfer)
i2cReceiveByte(FALSE);
break;
// Slave Receiver status codes
case TW_SR_SLA_ACK: // 0x60: own SLA+W has been received, ACK has been returned
case TW_SR_ARB_LOST_SLA_ACK: // 0x68: own SLA+W has been received, ACK has been returned
case TW_SR_GCALL_ACK: // 0x70: GCA+W has been received, ACK has been returned
case TW_SR_ARB_LOST_GCALL_ACK: // 0x78: GCA+W has been received, ACK has been returned
#ifdef I2C_DEBUG
printf("I2C: SR->SLA_ACK\r\n");
#endif
// we are being addressed as slave for writing (data will be received from master)
// set state
I2cState = I2C_SLAVE_RX;
// prepare buffer
I2cReceiveDataIndex = 0;
// receive data byte and return ACK
TWCR = (TWCR & TWCR_CMD_MASK) | _BV(TWINT) | _BV(TWEA);
break;
case TW_SR_DATA_ACK: // 0x80: data byte has been received, ACK has been returned
case TW_SR_GCALL_DATA_ACK: // 0x90: data byte has been received, ACK has been returned
#ifdef I2C_DEBUG
printf("I2C: SR->DATA_ACK\r\n");
#endif
// get previously received data byte
I2cReceiveData[I2cReceiveDataIndex++] = TWDR;
// check receive buffer status
if(I2cReceiveDataIndex < I2C_RECEIVE_DATA_BUFFER_SIZE)
{
// receive data byte and return ACK
i2cReceiveByte(TRUE);
//outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT)|BV(TWEA));
}
else
{
// receive data byte and return NACK
i2cReceiveByte(FALSE);
//outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
}
break;
case TW_SR_DATA_NACK: // 0x88: data byte has been received, NACK has been returned
case TW_SR_GCALL_DATA_NACK: // 0x98: data byte has been received, NACK has been returned
#ifdef I2C_DEBUG
printf("I2C: SR->DATA_NACK\r\n");
#endif
// receive data byte and return NACK
i2cReceiveByte(FALSE);
//outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK)|BV(TWINT));
break;
case TW_SR_STOP: // 0xA0: STOP or REPEATED START has been received while addressed as slave
#ifdef I2C_DEBUG
printf("I2C: SR->SR_STOP\r\n");
#endif
// switch to SR mode with SLA ACK
TWCR = (TWCR & TWCR_CMD_MASK) | _BV(TWINT) | _BV(TWEA);
// i2c receive is complete, call i2cSlaveReceive
if(i2cSlaveReceive) i2cSlaveReceive(I2cReceiveDataIndex, I2cReceiveData);
// set state
I2cState = I2C_IDLE;
break;
// Slave Transmitter
case TW_ST_SLA_ACK: // 0xA8: own SLA+R has been received, ACK has been returned
case TW_ST_ARB_LOST_SLA_ACK: // 0xB0: GCA+R has been received, ACK has been returned
#ifdef I2C_DEBUG
printf("I2C: ST->SLA_ACK\r\n");
#endif
// we are being addressed as slave for reading (data must be transmitted back to master)
// set state
I2cState = I2C_SLAVE_TX;
// request data from application
if(i2cSlaveTransmit) I2cSendDataLength = i2cSlaveTransmit(I2C_SEND_DATA_BUFFER_SIZE, I2cSendData);
// reset data index
I2cSendDataIndex = 0;
// fall-through to transmit first data byte
case TW_ST_DATA_ACK: // 0xB8: data byte has been transmitted, ACK has been received
#ifdef I2C_DEBUG
printf("I2C: ST->DATA_ACK\r\n");
#endif
// transmit data byte
TWDR = I2cSendData[I2cSendDataIndex++];
if(I2cSendDataIndex < I2cSendDataLength)
// expect ACK to data byte
TWCR = (TWCR & TWCR_CMD_MASK) | _BV(TWINT) | _BV(TWEA);
else
// expect NACK to data byte
TWCR = (TWCR & TWCR_CMD_MASK) | _BV(TWINT);
break;
case TW_ST_DATA_NACK: // 0xC0: data byte has been transmitted, NACK has been received
case TW_ST_LAST_DATA: // 0xC8:
#ifdef I2C_DEBUG
printf("I2C: ST->DATA_NACK or LAST_DATA\r\n");
#endif
// all done
// switch to open slave
TWCR = (TWCR & TWCR_CMD_MASK) | _BV(TWINT) | _BV(TWEA);
// set state
I2cState = I2C_IDLE;
break;
// Misc
case TW_NO_INFO: // 0xF8: No relevant state information
// do nothing
#ifdef I2C_DEBUG
printf("I2C: NO_INFO\r\n");
#endif
break;
case TW_BUS_ERROR: // 0x00: Bus error due to illegal start or stop condition
#ifdef I2C_DEBUG
printf("I2C: BUS_ERROR\r\n");
#endif
// reset internal hardware and release bus
TWCR = (TWCR & TWCR_CMD_MASK) | _BV(TWINT) | _BV(TWSTO) | _BV(TWEA);
// set state
I2cState = I2C_IDLE;
break;
}
}
eI2cStateType i2cGetState(void)
{
return I2cState;
}
For my device I've got:
#include <stdio.h>
#include "adxl345.h"
#include "util.h"
#include "i2c.h"
uint16_t x_accel(void)
{
//0xA6 for a write
//0xA7 for a read
uint8_t dummy, xh, xl;
uint16_t xo;
//0x32 data registers
i2cSendStart();
i2cWaitForComplete();
i2cSendByte(0xA6); //write to ADXL
i2cWaitForComplete();
i2cSendByte(0x32); //X0 data register
i2cWaitForComplete();
i2cSendStop(); //repeat start
i2cSendStart();
i2cWaitForComplete();
i2cSendByte(0xA7); //read from ADXL
i2cWaitForComplete();
i2cReceiveByte(TRUE);
i2cWaitForComplete();
xl = i2cGetReceivedByte(); //x low byte
i2cWaitForComplete();
i2cReceiveByte(FALSE);
i2cWaitForComplete();
dummy = i2cGetReceivedByte(); //must do a multiple byte read?
i2cWaitForComplete();
i2cSendStop();
//0x33 data registers
i2cSendStart();
i2cWaitForComplete();
i2cSendByte(0xA6); //write to ADXL
i2cWaitForComplete();
i2cSendByte(0x33); //X1 data register
i2cWaitForComplete();
i2cSendStop(); //repeat start
i2cSendStart();
i2cWaitForComplete();
i2cSendByte(0xA7); //read from ADXL
i2cWaitForComplete();
i2cReceiveByte(TRUE);
i2cWaitForComplete();
xh = i2cGetReceivedByte(); //x high byte
i2cWaitForComplete();
i2cReceiveByte(FALSE);
i2cWaitForComplete();
dummy = i2cGetReceivedByte(); //must do a multiple byte read?
i2cWaitForComplete();
i2cSendStop();
xo = xl|(xh << 8);
return xo;
}
uint16_t y_accel(void)
{
//0xA6 for a write
//0xA7 for a read
uint8_t dummy, yh, yl;
uint16_t yo;
//0x34 data registers
i2cSendStart();
i2cWaitForComplete();
i2cSendByte(0xA6); //write to ADXL
i2cWaitForComplete();
i2cSendByte(0x34); //Y0 data register
i2cWaitForComplete();
i2cSendStop(); //repeat start
i2cSendStart();
i2cWaitForComplete();
i2cSendByte(0xA7); //read from ADXL
i2cWaitForComplete();
i2cReceiveByte(TRUE);
i2cWaitForComplete();
yl = i2cGetReceivedByte(); //x low byte
i2cWaitForComplete();
i2cReceiveByte(FALSE);
i2cWaitForComplete();
dummy = i2cGetReceivedByte(); //must do a multiple byte read?
i2cWaitForComplete();
i2cSendStop();
//0x35 data registers
i2cSendStart();
i2cWaitForComplete();
i2cSendByte(0xA6); //write to ADXL
i2cWaitForComplete();
i2cSendByte(0x35); //Y1 data register
i2cWaitForComplete();
i2cSendStop(); //repeat start
i2cSendStart();
i2cWaitForComplete();
i2cSendByte(0xA7); //read from ADXL
i2cWaitForComplete();
i2cReceiveByte(TRUE);
i2cWaitForComplete();
yh = i2cGetReceivedByte(); //y high byte
i2cWaitForComplete();
i2cReceiveByte(FALSE);
i2cWaitForComplete();
dummy = i2cGetReceivedByte(); //must do a multiple byte read?
i2cWaitForComplete();
i2cSendStop();
yo = yl|(yh << 8);
return yo;
}
uint16_t z_accel(void)
{
//0xA6 for a write
//0xA7 for a read
uint8_t dummy, zh, zl;
uint16_t zo;
//0x36 data registers
i2cSendStart();
i2cWaitForComplete();
i2cSendByte(0xA6); //write to ADXL
i2cWaitForComplete();
i2cSendByte(0x36); //Z0 data register
i2cWaitForComplete();
i2cSendStop(); //repeat start
i2cSendStart();
i2cWaitForComplete();
i2cSendByte(0xA7); //read from ADXL
i2cWaitForComplete();
i2cReceiveByte(TRUE);
i2cWaitForComplete();
zl = i2cGetReceivedByte(); //z low byte
i2cWaitForComplete();
i2cReceiveByte(FALSE);
i2cWaitForComplete();
dummy = i2cGetReceivedByte(); //must do a multiple byte read?
i2cWaitForComplete();
i2cSendStop();
//0x37 data registers
i2cSendStart();
i2cWaitForComplete();
i2cSendByte(0xA6); //write to ADXL
i2cWaitForComplete();
i2cSendByte(0x37); //Z1 data register
i2cWaitForComplete();
i2cSendStop(); //repeat start
i2cSendStart();
i2cWaitForComplete();
i2cSendByte(0xA7); //read from ADXL
i2cWaitForComplete();
i2cReceiveByte(TRUE);
i2cWaitForComplete();
zh = i2cGetReceivedByte(); //z high byte
i2cWaitForComplete();
i2cReceiveByte(FALSE);
i2cWaitForComplete();
dummy = i2cGetReceivedByte(); //must do a multiple byte read?
i2cWaitForComplete();
i2cSendStop();
zo = zl|(zh << 8);
return zo;
}
and my main I've got:
#define UART_USB UART2
int main(void) {
delay_ms(200);
uartInit(UART_USB, 9600);
uartSetStandardOut(UART_USB);
printf("\n\n--=[ Start of Execution ]=--\n");
i2cInit();
delay_ms(200);
uint16_t accelX = -1;
uint16_t accelY = -1;
uint16_t accelZ = -1;
printf("Accel (x, y, z): (%u, %u, %u)\n\n", accelX, accelY, accelZ);
while(1)
{
accelX = x_accel();
accelY = y_accel();
accelZ = z_accel();
printf("Accel (x, y, z): (%d, %d, %d)\n", accelX, accelY, accelZ);
delay_ms(1000);
}
}
All I get for output is:
--=[ Start of Execution ]=--
I2C: INIT
Accel (x, y, z): (65535, 65535, 65535)
Accel (x, y, z): (0, 0, 0)
Accel (x, y, z): (0, 0, 0)
Accel (x, y, z): (0, 0, 0)
Accel (x, y, z): (0, 0, 0)
Accel (x, y, z): (0, 0, 0)
Accel (x, y, z): (0, 0, 0)
I assume I should be getting more i2c debug messages as I turned them on. When I move the sparkfun board around, its all stay 0 so something isn't working. Is there something I'm missing. I've been googling for a week and can't get anything other than my uart code to work.
-
Are you using external pull-up resistors on the data and clock lines? I don't think the internal pullups will work except at low speeds. The data sheet recommends external pull-ups. 4.7K is good.
Joe
-
No, I has assumed the internal pullups would work, or why have em :)
I'll have to try external pullups I guess.
-
As part of WebbotLib Studio I have compiled an up-to-date version of avr-gcc etc (4.7.0). Am busy fixing the errors they show. So hopefully this problem will go away