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.
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?
@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.
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.