I2C Transmission Clock Speed of Start Condition

Hello,

I am trying to connect to a motor driver (MCTA8315A) via I2C using an ATmega328P on a PCB but I keep getting a NAK on transmission of the address (which should be 0x00 by default and I haven't changed it). I connected an oscilloscope to SCL and SDA, and it looks like the microcontroller is holding SCL low for longer than the clock speed of the I2C transmission. Any ideas why this might be?

I am using the sparkfun AVR pocket programmer and MiniCore core to upload programs to the ATmega328P. I am using the programming header to upload the program, but to test I disconnect the programmer and apply 9V to the J1 connector which feeds directly into VM of the motor driver, which supplies power to the microcontroller. I've also confirmed the SDA and SCL pins of the driver and microcontroller are connected and there's no soldering error.

I attached screenshots of my schematic, programming settings for uploading to the ATmega328P, and oscilloscope of SCL and SDA.

Thank you



/**
   Description: Connects to MCTA8315A motor driver and configures driver settings

   Created: 10 MAY 2024
   Updated: 26 MAY 2024

**/



// include libraries
#include <Wire.h>

// define motor driver address and registers
#define MCT8315A_ADDRESS 0x00
#define ISD_CONFIG_ADDRESS 0x00000080

// declare pins
const int LED_PIN = 10;           // ATmega328P pin PB2

// declare and initialize variables and libraries
// time variables
unsigned long current_millis = millis();

// function prototypes
void init_I2C(void);
void init_pins(void);
void init_motor_driver(void);
void blink_LED_x_times(uint8_t num_blinks);



// ********************************************** START PROGRAM ********************************************** //

void setup() {
  delay(100);

  init_pins();
  init_I2C();

//  delay(2000);
  delay(1000);
  // delay 2 seconds before starting program
}

void loop() {

  init_motor_driver();

//  delay(3000);
  delay(1500);
  // delay 3 seconds between loops

}



// ********************************************** FUNCTIONS ********************************************** //

void init_motor_driver(void) {
  // initializes motor driver registers
  
  uint8_t transmission_status;        // stores value returned by Wire.endTransmission(), 0 = success, 1-5 other error messages
  
  Wire.beginTransmission(MCT8315A_ADDRESS);
  transmission_status = Wire.endTransmission();
  if (transmission_status == 0) {
    blink_LED_x_times(8);
  }

  else {
    blink_LED_x_times(transmission_status);
  }
}

void init_I2C(void) {
  Wire.begin();

  // I2C transmission freq = clock / (16 + (2 * TWBR * prescaler)), clock freq = 1 MHz
//  TWBR = 2;        // TWBR (two wire bit rate) register
//  TWSR &= ~(0b11);     // set prescaler in TWSR register to 1
  Wire.setClock(100000);      // set transmission clock speed to 100kHz
}

void init_pins(void) {
  // initializes pins at input/output and pullups
  
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);
}

void blink_LED_x_times(uint8_t num_blinks) {
  // blink LED x number of times
  uint8_t led_state = digitalRead(LED_PIN);
//  const unsigned int led_blink_interval = 250;       // time in ms
  const unsigned int led_blink_interval = 125;       // time in ms
  unsigned long previous_led_flip_time = 0;

  for (uint8_t i = 0; i < num_blinks;) {
    current_millis = millis();
    while (current_millis - previous_led_flip_time < led_blink_interval) {
      current_millis = millis();
//      delay(50);
      delay(25);
    }

    // reached led_blink_interval
    previous_led_flip_time = current_millis;

    if (led_state == LOW) {
      led_state = HIGH;
    } else {
      led_state = LOW;
      i++;    // LED starts LOW, initial state will not be counted
    }
    digitalWrite(LED_PIN, led_state);
  }
}

Google shows nothing........
9v battery, (guessing smoke alarm battery) are you certain it is up to the job?

Perhaps you should supply some more info from your end.

Hey bluejets,

Thanks for the response. My apologies, it's the MCT8315A motor driver from TI. Product page here - MCT8315A data sheet, product information and support | TI.com

The power supply is a regulated benchtop power supply, should be ok.

What other info can I provide to help us find the root cause? Any ideas why the microcontroller pulls SCL line low for ~4 clock cycles?

Thank you

1. How do you know the default device address for the Motor Driver is: 000 0000 (0x00)?

The data shhet says to me something else (Fig-1):


Figure-1:

2. Uplod the following sketch and chcek if your Arduino UNO cna detect the presence of the Motor Driver on the I2C Bus.

#include<Wire.h>
#define deviceAddress 0x60

void setup()
{ 
    Serial.begin(9600);
    Wire.begin();
}

void loop()
{
    Wire.beginTransmission(deviceAddress);
    byte busStatus = Wire.endTransmission();
    if(busStatus !=0)
    {
         Serial.println("Motor Driver is not pesent.");
         while(true):    //wait for ever
    }
    Serial.println("Motor Driver is found.");
    delay(1000);
}

Thanks for the suggestion GolamMostafa.

Page 74 of the MCT8315A datasheet says the target address is "default 0x00, but can be modified by setting I2C_TARGET_ADDR". I think they were just using 0x60 in the example to demonstrate sending an address other than 0x00.

I tried the address 0x60, but that also returned a NAK and endTransmission() returned 2: received NACK on transmit of address. I also don't have a USB port connected to the PCB, only a 6 pin programming header, so I don't think I can get a serial port open in the Arduino IDE.

I actually think the issue is independent of the address, I believe the issue is related to the ATmega328P holding SCL low for 4 clock cycles during both the START and STOP conditions. Any ideas why this might be happening?

Thank you

I2C address of 0 indicates a General call (broadcast), not a valid slave address. Change the address to 8 at least and run an I2C
sniffer.

It's the slaves which can hold SCL low as long as required, AKA clock stretching.

@sbibat2
Have you checked the presence of your Motor Driver in the I2C Bus using Arduino UNO as per post #4 using device address 0x00 (the default one)?

I think that you need a software I2C that does not NACK at addressing I2C address 0 and inserts the required 100 µs delay between transferred bytes. Only then you can write the new address. This has to be done after each reset!

After all this IC seems to be so non-conforming that I'd try to find a correct one.

1 Like

Thanks for pointing out clock stretching to me. It looks like the MCT8315A can stretch the clock for a number of reasons on page 78 of the datasheet. It could be something going on with the driver processing interrupts.

Yes, this is what the program in the original post is attempting to do. And I'm also unable to run the program suggested in post #4 because I don't have a USB port connected to the PCB, only a 6 pin programming header, so I can't get a serial port open in the Arduino IDE. But I did run the program below trying all possible 7-bit addresses and didn't get any hits on an address. I also tried setting the I2C transmission rate at 100kHz, 200kHz, and 400kHz, with no success unfortunately.

/**
   Description: Connects to MCT8315A motor driver and configures driver settings

   Created: 10 MAY 2024
   Updated: 27 MAY 2024

**/



// include libraries
#include <Wire.h>

// define motor driver address and registers
//#define MCT8315A_ADDRESS 0x00
#define CPU_CLOCK_FREQ 8  // MHz

// declare pins
const int LED_PIN = 10;           // ATmega328P pin PB2

// declare and initialize variables and libraries
// time variables
unsigned long current_millis = millis();

// function prototypes
void init_I2C(void);
void init_pins(void);
void init_motor_driver(void);
void blink_LED_x_times(uint8_t num_blinks);



// ********************************************** START PROGRAM ********************************************** //

void setup() {
  delay(100 / CPU_CLOCK_FREQ);

  init_pins();
  init_I2C();

  delay(2000 / CPU_CLOCK_FREQ);
  // delay 2 seconds before starting program
}

void loop() {

  init_motor_driver();

  delay(3000 / CPU_CLOCK_FREQ);
  // delay 3 seconds between loops

}



// ********************************************** FUNCTIONS ********************************************** //

void init_motor_driver(void) {
  // initializes motor driver registers

  blink_LED_x_times(2);

  uint8_t transmission_status;        // stores value returned by Wire.endTransmission(), 0 = success, 1-5 other error messages

  for (uint8_t motor_driver_address = 0x00; motor_driver_address < 0x7F; motor_driver_address++) {
    // 0b01111111 = 0x7F

    Wire.beginTransmission(motor_driver_address);
    transmission_status = Wire.endTransmission();
    if (transmission_status == 0) {
      while (1) {
        // signal got a hit on an address
        digitalWrite(LED_PIN, HIGH);
        delay(3000 / CPU_CLOCK_FREQ);
        digitalWrite(LED_PIN, LOW);

        // blink address + 1 times in case address is 0
        blink_LED_x_times(motor_driver_address + 1);

        delay(3000 / CPU_CLOCK_FREQ);
      }
    }

    delay(50 / CPU_CLOCK_FREQ);

  }
}

void init_I2C(void) {
  Wire.begin();

  // I2C transmission freq = clock / (16 + (2 * TWBR * prescaler)), clock freq = 1 MHz
  //  TWBR = 2;        // TWBR (two wire bit rate) register
  //  TWSR &= ~(0b11);     // set prescaler in TWSR register to 1
  Wire.setClock(100000);      // set transmission clock speed to 100kHz
}

void init_pins(void) {
  // initializes pins at input/output and pullups

  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);
}

void blink_LED_x_times(uint8_t num_blinks) {
  // blink LED x number of times
  uint8_t led_state = digitalRead(LED_PIN);
  const unsigned int led_blink_interval = 250 / CPU_CLOCK_FREQ;     // time in ms
  unsigned long previous_led_flip_time = 0;

  for (uint8_t i = 0; i < num_blinks;) {
    current_millis = millis();
    while (current_millis - previous_led_flip_time < led_blink_interval) {
      current_millis = millis();
      delay (50 / CPU_CLOCK_FREQ);
    }

    // reached led_blink_interval
    previous_led_flip_time = current_millis;

    if (led_state == LOW) {
      led_state = HIGH;
    } else {
      led_state = LOW;
      i++;    // LED starts LOW, initial state will not be counted
    }
    digitalWrite(LED_PIN, led_state);
  }
}

I think this is correct, and I appreciate your help trying to troubleshoot. Could be a lost cause at this point.

I requested you to us an Arduino UNO (Fig-1) to check the presence of your Motor Driver.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.