Tool/software:
Hello TI Community,
I'm currently working on a project involving the DRV8244-Q1 motor driver, interfacing it with an ESP32 via SPI. I'm having trouble getting the driver to respond to any SPI commands other than 'clear fault' and 'read device ID'. After issuing the 'clear fault' command, I can successfully read the device ID, but any subsequent attempt to write configuration registers or control commands results in the chip not responding as expected.
Here is an overview of the setup and the key issues:
System Overview:
Microcontroller: ESP32
Motor Driver: DRV8244-Q1
SPI Clock Frequency: 1 MHz
SPI Settings: MSBFIRST, SPI Mode 1
Wiring: I have confirmed all connections to be correct, and the nSCS pin is controlled properly during transactions.
No Response to Other Commands: If I attempt to send any other command apart from 'clear fault' and 'read device ID', the chip only responds with 0x0
when reading from FAULT_SUMMARY_REGISTER
or STATUS1_REGISTER
. This seems to indicate that the chip is either not processing these commands correctly or is locked in an unknown state. 2. DRV8244 Output Control: The chip seems to be unresponsive when attempting to control the motor outputs. I have ensured that the nSLEEP
and DRVOFF
pins are properly set to enable the active state, but I still cannot get the motor driver to actively respond to control signals.
What I've Tried:
SPI Timing Compliance: I have reviewed the SPI timing requirements from the datasheet and made sure my delays for
nSCS
and clock timing are compliant.Unlocking Registers: I have added explicit commands to unlock the configuration registers before making any changes, as per the datasheet recommendations.
Status and Fault Checks: After attempting a configuration write, I read back the
FAULT_SUMMARY_REGISTER
and always receive0x0
, which indicates no active communication or fault status.
Specific Questions:
SPI Timing: Are there any specific timing nuances that are commonly overlooked when dealing with DRV8244 that could cause the chip to be unresponsive?
Configuration Locking Mechanism: Am I missing a step related to the locking and unlocking mechanism of the configuration registers? I explicitly unlock with the
COMMAND_REGISTER
but it seems the device still doesn't respond to configuration changes.Control Mode Settings: I have attempted to set
CONFIG4_REGISTER
to0b00000000
to configure the device for PWM control, but it doesn't seem to enable the output. Could there be an issue with the values I am using to configure the registers for active PWM control?EN/IN Pins for PWM: According to the datasheet, both
EN/IN1
andPH/IN2
must be set high for active operation. I have tried doing this, but the output remains inactive. Is there something else I should be configuring in the registers to properly drive the motor outputs in PWM mode?
I've attached my code below for reference. Any insights or suggestions would be greatly appreciated. I'm really stuck at this point and could use some expert guidance.
Thank you for your help!
/*
* Register Settings Translation
* ----------------------------
* COMMAND_REGISTER (0x08): Used to send commands to the device, such as clearing faults.
* CONFIG1_REGISTER (0x0A): Configuration register for basic device settings (e.g., open load detection, mode selection).
* - `0x80`: Enable open load detection
* - `0x40`: Set mode to standby
* CONFIG4_REGISTER (0x0D): Configuration register for setting over-current protection thresholds, filter time, and other settings.
* - `0x00`: TOCP_SEL = 6 µsec, OCP_SEL = 100%, DRVOFF_SEL = OR, EN_IN1_SEL = OR, PH_IN2_SEL = OR
* - `0x10`: TOCP_SEL = 6 µsec, OCP_SEL = 75%, DRVOFF_SEL = OR, EN_IN1_SEL = OR, PH_IN2_SEL = OR
* - `0x20`: TOCP_SEL = 6 µsec, OCP_SEL = 50%, DRVOFF_SEL = OR, EN_IN1_SEL = OR, PH_IN2_SEL = OR
* - `0x50`: TOCP_SEL = 3 µsec, OCP_SEL = 75%, DRVOFF_SEL = AND, EN_IN1_SEL = OR, PH_IN2_SEL = OR
* FAULT_SUMMARY_REGISTER (0x01): Reports fault conditions in the system, such as over-current or thermal shutdown.
* - `0x80`: SPI communication fault detected
* - `0x40`: Power-On Reset detected
* - `0x20`: General fault present
* - `0x10`: VM Over Voltage detected
* - `0x08`: VM Under Voltage detected
* - `0x04`: Over Current Protection triggered
* - `0x02`: Thermal Shutdown detected
* - `0x01`: Open Load Active detected
* STATUS1_REGISTER (0x02): Reports status conditions like open load detection, device active state, etc.
* - `0x80`: Open Load Active detected on OUT1
* - `0x40`: Open Load Active detected on OUT2
* - `0x20`: ITRIP comparator triggered
* - `0x10`: Device in ACTIVE state
*/
#include <Wire.h> // For HUSB238 I2C communication
#include "Adafruit_HUSB238.h" // HUSB238 library
#include <SPI.h> // For DRV8244 SPI communication
// SPI macros for bit manipulation
#define SPI_ADDRESS_MASK 0x3F00
#define SPI_ADDRESS_POS 8
#define SPI_DATA_MASK 0x00FF
#define SPI_DATA_POS 0
#define SPI_RW_BIT_MASK 0x8000
// Pin definitions
#define SDA_PIN 10
#define SCL_PIN 8
#define SCLK_PIN 2
#define SDI_PIN 3
#define SDO_PIN 4
#define NSCS_PIN 5
#define IN1_PIN 0 // PH/IN1 pin for motor direction control
#define IN2_PIN 1 // PH/IN2 pin for motor direction control
#define PWM_CHANNEL 0
#define PWM_FREQ 3000 // Set PWM frequency to 20 kHz
#define PWM_RESOLUTION 8 // 8-bit resolution (0-255)
Adafruit_HUSB238 husb238;
// SPI settings for DRV8244 with SPI Mode 1
SPISettings spiSettings(1000000, MSBFIRST, SPI_MODE1);
// Register addresses (configurable variables)
const uint8_t COMMAND_REGISTER = 0x08;
const uint8_t CONFIG1_REGISTER = 0x0A;
const uint8_t CONFIG4_REGISTER = 0x0D;
const uint8_t FAULT_SUMMARY_REGISTER = 0x01;
const uint8_t STATUS1_REGISTER = 0x02;
// Function to send a 16-bit frame to the DRV8244
uint16_t drv8244SPITransaction(uint8_t command, uint8_t data) {
uint16_t sdi = ((command << SPI_ADDRESS_POS) & SPI_ADDRESS_MASK) | ((data << SPI_DATA_POS) & SPI_DATA_MASK); // Combine command and data
uint16_t sdo = 0;
digitalWrite(NSCS_PIN, HIGH); // Ensure nSCS is high before starting a new transaction
delayMicroseconds(1); // Ensure nSCS is high for at least 300 ns (delayMicroseconds(1) = 1000 ns)
digitalWrite(NSCS_PIN, LOW); // Select the device
delayMicroseconds(1); // Small delay to ensure proper setup time (tSU_nSCS = 25 ns)
SPI.beginTransaction(spiSettings);
// Send the 16-bit SDI word and receive the SDO word
sdo = SPI.transfer16(sdi);
SPI.endTransaction();
digitalWrite(NSCS_PIN, HIGH); // Deselect the device
delayMicroseconds(1); // Ensure nSCS remains high for stability (tHI_nSCS >= 300 ns)
return sdo;
}
// Function to write to a register
void drv8244WriteRegister(uint8_t address, uint8_t value) {
// Construct the command using address and set write bit (B15=0 for write)
uint16_t command = ((address << SPI_ADDRESS_POS) & SPI_ADDRESS_MASK) | (value & SPI_DATA_MASK); // Properly mask address and data
// Send the command and value in a 16-bit frame
drv8244SPITransaction(command >> SPI_ADDRESS_POS, value);
delay(50); // Allow some time after writing register to ensure proper handling
}
// Function to read from a register
uint8_t drv8244ReadRegister(uint8_t address) {
// Construct the command using address and set read bit (B15=1 for read)
uint16_t command = SPI_RW_BIT_MASK | ((address << SPI_ADDRESS_POS) & SPI_ADDRESS_MASK); // Set read bit and mask address
uint16_t response = drv8244SPITransaction(command >> SPI_ADDRESS_POS, 0x00);
// Extract the report byte from the response
return (uint8_t)(response & SPI_DATA_MASK);
}
// Function to read the Device ID
void readDeviceID() {
uint8_t deviceID = drv8244ReadRegister(0x00); // Assuming Device ID is at register 0x00
Serial.print("Device ID: 0x");
Serial.println(deviceID, HEX);
if (deviceID == 0x00) {
Serial.println("Warning: Device ID read as 0x00. SPI communication may be failing.");
} else {
Serial.println("Device ID read successfully.");
}
}
// Initialization function for DRV8244
void setupDRV8244() {
SPI.begin(SCLK_PIN, SDO_PIN, SDI_PIN, NSCS_PIN);
pinMode(NSCS_PIN, OUTPUT);
digitalWrite(NSCS_PIN, HIGH); // Set CS high initially
// Clear faults
drv8244WriteRegister(COMMAND_REGISTER, 0b10000000); // Clear faults command
delay(100); // Allow more time for fault clearance
Serial.println("Cleared faults using COMMAND register.");
delay(100); // Allow more time for fault clearance
// Explicitly unlock configuration and SPI_IN registers
drv8244WriteRegister(COMMAND_REGISTER, 0b10010000); // Unlock configuration and SPI_IN registers
delay(100); // Small delay to ensure unlock takes effect
// Verify unlock status
uint8_t unlockStatus = drv8244ReadRegister(STATUS1_REGISTER);
Serial.print("STATUS1 Register after unlock attempt: 0x");
Serial.println(unlockStatus, HEX);
Serial.println("Unlocked configuration and SPI_IN registers for modification.");
delay(100); // Small delay to ensure unlock takes effect
// Verify if configuration is unlocked by reading back a register
uint8_t statusAfterUnlock = drv8244ReadRegister(STATUS1_REGISTER);
Serial.print("STATUS1 Register after unlock: 0x");
Serial.println(statusAfterUnlock, HEX);
// Write CONFIG4 register to configure for PWM control of IN1 and IN2
drv8244WriteRegister(CONFIG4_REGISTER, 0b00000000); // Set DRVOFF_SEL = OR, EN_IN1_SEL = OR, PH_IN2_SEL = OR for PWM control, configure OCP
Serial.println("CONFIG4 register written for PWM control of EN_IN1 and PH_IN2 and OCP settings.");
delay(100); // Allow more time for the settings to take effect
// Set DRVOFF to 0 to enable device output
drv8244WriteRegister(COMMAND_REGISTER, 0b00000001); // Set DRVOFF to 0
Serial.println("DRVOFF set to 0 to enable outputs.");
delay(100); // Allow more time for the settings to take effect
// Read FAULT_SUMMARY register after attempting to write CONFIG4
uint8_t faultSummaryAfterConfig = drv8244ReadRegister(FAULT_SUMMARY_REGISTER);
Serial.print("FAULT_SUMMARY Register after CONFIG4 write: 0x");
Serial.println(faultSummaryAfterConfig, HEX);
// Lock the configuration registers after modification
drv8244WriteRegister(COMMAND_REGISTER, 0b10000000); // Lock configuration registers
delay(100); // Ensure lock takes effect
Serial.println("Locked configuration registers after modification.");
delay(100); // Ensure lock takes effect before proceeding
// Read Device ID to verify communication
readDeviceID();
delay(100); // Allow time for device ID readout
}
// Function to initialize PWM for motor control
void setupPWM() {
// Set nSLEEP to HIGH to enable active state
pinMode(NSCS_PIN, OUTPUT);
digitalWrite(NSCS_PIN, HIGH); // Ensure nSLEEP is HIGH
delay(10); // Delay to ensure device comes out of sleep state
pinMode(IN1_PIN, OUTPUT);
pinMode(IN2_PIN, OUTPUT);
// Attach IN1 to PWM and set initial state for IN2
ledcSetup(PWM_CHANNEL, PWM_FREQ, PWM_RESOLUTION);
ledcAttachPin(IN1_PIN, PWM_CHANNEL);
digitalWrite(IN2_PIN, LOW); // Set IN2 to LOW for forward direction
// Start with PWM off
ledcWrite(PWM_CHANNEL, 0);
}
// Function for ramp-up motor start
void startMotorWithRampUp(uint8_t targetDutyCycle, uint16_t rampDurationMs = 2000) {
Serial.println("Starting motor with ramp-up...");
// Calculate the step size and delay between increments
uint8_t currentDutyCycle = 0;
uint16_t stepDelayMs = rampDurationMs / targetDutyCycle;
// Gradually increase the PWM duty cycle
for (currentDutyCycle = 0; currentDutyCycle <= targetDutyCycle; currentDutyCycle++) {
ledcWrite(PWM_CHANNEL, currentDutyCycle);
delay(stepDelayMs); // Small delay for ramp-up
}
Serial.print("Motor reached target duty cycle: ");
Serial.print(targetDutyCycle);
Serial.println("%.");
}
void setup() {
Serial.begin(115200); // Initialize Serial
while (!Serial)
delay(10);
Serial.println("Starting HUSB238 with DRV8244 Integration...");
// Initialize I2C for HUSB238
Serial.println("Initializing HUSB238...");
Wire.begin(SDA_PIN, SCL_PIN);
if (!husb238.begin(HUSB238_I2CADDR_DEFAULT, &Wire)) {
Serial.println("Failed to initialize HUSB238. Check wiring!");
while (1) {
delay(1000); // Stay in a loop if initialization fails
}
}
Serial.println("HUSB238 initialized successfully.");
// Request 20V from HUSB238
Serial.println("Requesting 20V...");
husb238.selectPD(PD_SRC_20V); // Select 20V profile
husb238.requestPD(); // Request selected profile
delay(100); // Allow time for request
if (husb238.getPDResponse() == SUCCESS) {
Serial.println("20V successfully requested and applied.");
} else {
Serial.println("Failed to apply 20V.");
while (1); // Halt on failure
}
// Initialize DRV8244
setupDRV8244();
// Initialize PWM for motor control
setupPWM();
}
void loop() {
static bool motorStarted = false;
// Periodic status check
delay(1000);
Serial.println("Checking HUSB238 status...");
if (!husb238.isAttached()) {
Serial.println("HUSB238 is not attached to power!");
return;
}
if (husb238.getPDResponse() != SUCCESS) {
Serial.println("Voltage not maintained. Re-requesting...");
husb238.selectPD(PD_SRC_12V); // Re-select 12V
husb238.requestPD(); // Re-request PD
if (husb238.getPDResponse() == SUCCESS) {
Serial.println("12V successfully reapplied.");
} else {
Serial.println("Failed to reapply 12V.");
}
} else {
Serial.println("12V is maintained.");
}
if (!motorStarted) {
// Start motor with ramp-up to 50% duty cycle
startMotorWithRampUp(200, 8000); // Ramp up to 50% duty cycle over 3 seconds
motorStarted = true;
}
// Read FAULT_SUMMARY register
uint8_t faultSummary = drv8244ReadRegister(FAULT_SUMMARY_REGISTER);
Serial.print("FAULT_SUMMARY Register: 0x");
Serial.println(faultSummary, HEX);
// Read STATUS1 register
uint8_t status1 = drv8244ReadRegister(STATUS1_REGISTER);
Serial.print("STATUS1 Register: 0x");
Serial.println(status1, HEX);
}