A WIRE.endTransmission Freezes. Arduino's implementation has infinite loop

I have been trying for the past several hours to get my board to communicate with the Adafruit V2 Motor shield. I have everything connected precisely as shown in this tutorial but is still does not work. Adafruit includes a few crafted implementation and header files to use their board. They even provide a sample use-case by way of a sketch example! This is the code I used, but still nothing worked.

I decided to debug the code and find out exactly what is happening. I went from file to file as I determined what was working and what was not. I went from Adafruit’s files to wire.c and then finally I found the problem in twi.c.

As it turns out, this loop in the code is never returning and spins infinitely.

// wait for write operation to complete
  while(wait && (TWI_MTX == twi_state)){
    continue;
  }

As always though, this code is poorly written and is documented very little so trying to debug this is a mess. I normally program these controllers using code written myself and leave out the Arduino code entirely, for various reasons I must use it for now.

Does anyone have any idea what is happening here? The “twi_state” variable is set earlier in the code to be equal to “TWI_MTX”, but is never set elsewhere. That said, I imagine this variable is updated using interrupts, but I did not find any ISRs that may provide deeper insight.

tl;dr : The WIRE library freezes. Specifically, the once the transmission has ended and WIRE.endTransmission() is called, the program freezes in an infinite loop.

Here is the very simple code provided by Adafruit. All it does it make an object defined to represent their shield and initialize it. If one places a print statement following AFMS.begin() you will find that it never prints.

#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"

// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 
// Or, create it with a different I2C address (say for stacking)
// Adafruit_MotorShield AFMS = Adafruit_MotorShield(0x61); 

// Connect a stepper motor with 200 steps per revolution (1.8 degree)
// to motor port #2 (M3 and M4)
Adafruit_StepperMotor *myMotor = AFMS.getStepper(200, 2);


void setup() {
  Serial.begin(9600);           // set up Serial library at 9600 bps
  Serial.println("Stepper test!");

  AFMS.begin();  // create with the default frequency 1.6KHz
  //AFMS.begin(1000);  // OR with a different frequency, say 1KHz
  
  myMotor->setSpeed(10);  // 10 rpm   
}

void loop() {
  Serial.println("Single coil steps");
  myMotor->step(100, FORWARD, SINGLE); 
  myMotor->step(100, BACKWARD, SINGLE); 

  Serial.println("Double coil steps");
  myMotor->step(100, FORWARD, DOUBLE); 
  myMotor->step(100, BACKWARD, DOUBLE);
  
  Serial.println("Interleave coil steps");
  myMotor->step(100, FORWARD, INTERLEAVE); 
  myMotor->step(100, BACKWARD, INTERLEAVE); 
  
  Serial.println("Microstep steps");
  myMotor->step(50, FORWARD, MICROSTEP); 
  myMotor->step(50, BACKWARD, MICROSTEP);
}

Any ideas?


Here is the code trail.

Code from AdaFruit’s library included with the shield

void Adafruit_MotorShield::begin(uint16_t freq) {
  // init PWM w/_freq
  WIRE.begin();
  _pwm.begin();
  _freq = freq;
  _pwm.setPWMFreq(_freq);  // This is the maximum PWM frequency
  Serial.println("Almost done\n"); 
  for (uint8_t i=0; i<16; i++) 
    _pwm.setPWM(i, 0, 0);
}
[code]

[b]More AdaFruit Code[/b]
[code]
void Adafruit_PWMServoDriver::begin(void) {
 WIRE.begin();
 reset();
}

More AdaFruit code

void Adafruit_PWMServoDriver::reset(void) {
 write8(PCA9685_MODE1, 0x0);
}

More AdaFruit code

void Adafruit_PWMServoDriver::write8(uint8_t addr, uint8_t d) {
  WIRE.beginTransmission(_i2caddr);
  WIRE.write(addr);
  WIRE.write(d);
  WIRE.endTransmission();
  Serial.println("Transmission Ended\n");  //  *** This never displays
}

Wire.cpp

uint8_t TwoWire::endTransmission(uint8_t sendStop)
{
  // transmit buffer (blocking)
  uint8_t ret = twi_writeTo(txAddress, txBuffer, txBufferLength, 1, sendStop);
  // reset tx buffer iterator vars
  txBufferIndex = 0;
  txBufferLength = 0;
  // indicate that we are done transmitting
  transmitting = 0;
  return ret;
}

twi.c ** ← This is where it fails. The loop code I posted above is in this section **

uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop)
{
  uint8_t i;

  // ensure data will fit into buffer
  if(TWI_BUFFER_LENGTH < length){
    return 1;
  }

  // wait until twi is ready, become master transmitter
  while(TWI_READY != twi_state){
    continue;
  }
	
  twi_state = TWI_MTX;
  twi_sendStop = sendStop;
  // reset error state (0xFF.. no error occured)
  twi_error = 0xFF;

  // initialize buffer iteration vars
  twi_masterBufferIndex = 0;
  twi_masterBufferLength = length;
  
  // copy data to twi buffer
  for(i = 0; i < length; ++i){
    twi_masterBuffer[i] = data[i];
  }
 
  
  // build sla+w, slave device address + w bit
  twi_slarw = TW_WRITE;
  twi_slarw |= address << 1;
  
  // if we're in a repeated start, then we've already sent the START
  // in the ISR. Don't do it again.
  //
  if (true == twi_inRepStart) {
    // if we're in the repeated start state, then we've already sent the start,
    // (@@@ we hope), and the TWI statemachine is just waiting for the address byte.
    // We need to remove ourselves from the repeated start state before we enable interrupts,
    // since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning
    // up. Also, don't enable the START interrupt. There may be one pending from the 
    // repeated start that we sent outselves, and that would really confuse things.
    twi_inRepStart = false;			// remember, we're dealing with an ASYNC ISR
    TWDR = twi_slarw;				
    TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE);	// enable INTs, but not START
  }
  else
    // send start condition
    TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA);	// enable INTs

  // wait for write operation to complete
  while(wait && (TWI_MTX == twi_state)){
    continue;
  }
  
      PORTB |= (1<<0); 
    _delay_ms(500); 
	PORTB &= ~(1<<0); 
  
  if (twi_error == 0xFF)
    return 0;	// success
  else if (twi_error == TW_MT_SLA_NACK)
    return 2;	// error: address send, nack received
  else if (twi_error == TW_MT_DATA_NACK)
    return 3;	// error: data send, nack received
  else
    return 4;	// other twi error
}

twi.c (16 KB)

twi.h (1.54 KB)