AnsweredAssumed Answered

I2C read not working.  Driver exercises pins, but buffer is not filled.

Question asked by Michael Schwager on May 6, 2014
Latest reply on May 7, 2014 by Michael Schwager

I'm using MQX 4.1 with Tower K60F120M and the on-board MMA8451Q accelerometer.

 

I can get an I2C read (using :ii2c0) to work.  Specifically, I'm reading accelerometer registers 0x0d through 0x11 (WHOAMI through LANDSCAPE registers), which after reset are known to be {1a 00 00 00 80}.  I can clearly see this on the scope so I know the peripheral and presumably the MQX driver is working.

 

The problem is the buffer I pass into the fread() is not populated with this data.

The read does not affect the contents of the memory pointed to by the buffer.


I wound up writing a bunch of stuff around the interrupt-based i2c driver because I found that following the recommended while (fread(...) count++) actually blocks the task and doesn't let anything else run, even with interrupt-i2c.  Imagine that!  So my code tests the i2c stats and calls sched_yield() until the transaction is done.  This allows other tasks to continue a bit more smoothly during a long transaction... however I didn't test this carefully with reads, only writes, so I'm thinking this may be the issue, as I don't understand the interrupt-driven driver code at all.

 

Here is some of my code.

 

...blablabla in main.c...

MQX_FILE_PTR fp;

fp = fopen(p->busName, 0);

ioctl(fp, IO_IOCTL_I2C_SET_BAUD, &(p->baud));

...

map = blablabla some structure with registers and addresses...

bool verified = verify_i2c_device(fp, map);

 

 

...blablabla ...

bool verify_i2c_device(MQX_FILE_PTR fp, i2c_device_map map) {

  uint8_t inbuf[5];

  if (read_bytes(fp, map.addr, map.who_am_i_reg, inbuf, 5) == true) {     // <<<-------- here inbuff does not get any new data.

  if(inbuf[0] == map.who_am_i_val) {

  return true;

  }

  }

  return false;

  // Cannot distinguish between false due to unverified device, or due to bad

  // i2c transaction.

}

 

...blablabla in i2c.c...


bool read_bytes(MQX_FILE_PTR fp, uint8_t addr, uint8_t reg, uint8_t *buf, uint32_t numBytes) {

  uint32_t state;

  bool success = true;

  _mqx_int result;

 

  _io_ioctl(fp, IO_IOCTL_I2C_SET_DESTINATION_ADDRESS, &addr);

 

 

  // Start keeping track of things

  ioctl(fp, IO_IOCTL_I2C_CLEAR_STATISTICS, NULL);

 

 

  if (numBytes == 0) {

  // Write nothing to send the address and try to get an ACK

  (void) fwrite(NULL, 1, 0, fp);

  // Check for ACK/NAK, then STOP

  ioctl(fp, IO_IOCTL_I2C_GET_STATE, &state);

  success = (state == I2C_STATE_FINISHED) ? false : true;

  return stop_and_return(fp, success); }

  else {

  // Write address and slave register

  (void) fwrite(&reg, 1, 1, fp);

  // Check for ACK/NAK, then STOP if fail

  ioctl(fp, IO_IOCTL_I2C_GET_STATE, &state);

  success = (state == I2C_STATE_FINISHED) ? false : true;

  if (success == false) {

  return stop_and_return(fp, false); } }

 

 

  // Write slave register

  //yield_until_done_writing(fp, 2); // addr + reg

  fflush(fp);

 

  // Repeated start, then receive

  ioctl(fp, IO_IOCTL_I2C_SET_RX_REQUEST, &numBytes);

  ioctl(fp, IO_IOCTL_I2C_REPEATED_START, NULL);

 

  // Read bytes

  (void) fread(buf, 1, numBytes, fp);

 

  yield_until_done_reading(fp, numBytes);

 

  return stop_and_return(fp, true);

}

 

void yield_until_done_reading(MQX_FILE_PTR fp, int num) {

  I2C_STATISTICS_STRUCT stats;

  do {

  _sched_yield();

  ioctl(fp, IO_IOCTL_I2C_GET_STATISTICS, &stats);

  } while (stats.RX_PACKETS < num);

}

 

bool stop_and_return(MQX_FILE_PTR fp, bool val) {

  ioctl(fp, IO_IOCTL_I2C_STOP, NULL);

  return val;

}

 

Any clues?

 

I also tried changing the actual fread call so it looks like the official example code, but no luck.  Same results.  Buffer is not populated.  Since it's all interrupt, it impossible to step through this as it tries to steal one byte at a time when stepping through the code, and the fread() call returns before the transaction is complete.

// Read bytes

  int counter=0;

  do {

  counter += fread(buf+result, 1, numBytes-counter, fp);

  } while(counter < numBytes);


I also tried using regular non-interrupt :i2c0 and have the same problem.

Outcomes