Hello, I am an undergraduate student majoring in robotics.
I am trying to implement FOC using a 3-phase BLDC motor, a DRV8301, and an Arduino board.
The problem is that the SPI communication between the DRV8301 and the Arduino board is not working, so I am facing difficulties.
In the Arduino IDE, it appears that only the setup function is being executed, and neither the loop function nor the FOC_ISR function is running.
Has anyone solved this issue?
Below text is the code I used.
#include <SPI.h>
#include <DueTimer.h>
#include <math.h>
// -- Constant Definitions --
#define TWO_PI 6.28318530718
// -- DRV8301 Pin Definitions --
#define CS_PIN 10
// -- DRV8301 PWM Pin Definitions --
#define INH_A 5
#define INL_A 6
#define INH_B 7
#define INL_B 8
#define INH_C 9
#define INL_C 11
// -- DRV8301 En_Gate Pin Definition --
#define EN_GATE 12
// -- DRV8301 Current Sensing Pin Definitions --
#define SO1_PIN A0
#define SO2_PIN A1
// -- DRV8301 EMF Sensing Pin Definitions --
#define EMF_A A2
#define EMF_B A3
#define EMF_C A4
// -- nFAULT Pin Definition (for monitoring) --
#define NFAULT_PIN 2
// FOC update period (10kHz -> dt = 100 µs)
const float dt = 0.0001; // 100 µs
// Motor Parameters
const float polePairs = 6.0; // e.g., for a 12-pole motor, polePairs = 6
// Constants and Variables for PI Control
volatile float targetSpeedRPM = 200.0; // Target speed (RPM)
volatile float phaseCorrection = 0.0; // Phase correction from PI control (radian/update)
volatile float currentPhase = 0.0; // Current electrical angle (radian)
volatile float measuredSpeedRPM = 0.0; // Measured speed (RPM)
volatile float speedIntegral = 0.0; // PI integral term
const float Kp = 0.01;
const float Ki = 0.001;
// Dead Time (e.g., for 12-bit resolution, 7 counts ≒ approx 170 ns)
const uint32_t deadTimeCounts = 6;
// Motor Power Control Scaling (0.0 ~ 1.0)
volatile float motorScaling = 0.5;
// Interrupt Flag
volatile bool focFlag = false;
// Variables to store ADC measurements
volatile uint16_t adcSO1 = 0, adcSO2 = 0;
volatile uint16_t adcEMF_A = 0, adcEMF_B = 0, adcEMF_C = 0;
/////////////////////// SPI Related Functions ///////////////////////
void writeDRV8301Register(uint8_t regAddr, uint16_t data) {
uint16_t frame = (0 << 15) | ((regAddr & 0xF) << 11) | (data & 0x7FF);
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
digitalWrite(CS_PIN, LOW);
SPI.transfer16(frame);
digitalWrite(CS_PIN, HIGH);
SPI.endTransaction();
}
uint16_t readDRV8301Register(uint8_t regAddr) {
uint16_t frame = (1 << 15) | ((regAddr & 0xF) << 11);
uint16_t data = 0;
// First transaction: send command
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
digitalWrite(CS_PIN, LOW);
SPI.transfer16(frame);
digitalWrite(CS_PIN, HIGH);
SPI.endTransaction();
delay(5);
// Second transaction: dummy read
SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
digitalWrite(CS_PIN, LOW);
data = SPI.transfer16(0x0000);
digitalWrite(CS_PIN, HIGH);
SPI.endTransaction();
return (data & 0x07FF);
}
/////////////////////// DRV8301 Initialization Function ///////////////////////
void initDRV8301() {
// Example settings based on datasheet
writeDRV8301Register(2, 0x068); // Control Register 1
delay(5);
writeDRV8301Register(3, 0x020); // Control Register 2
delay(10);
uint16_t cr1Val = readDRV8301Register(2);
delay(5);
uint16_t cr2Val = readDRV8301Register(3);
Serial.print("CR1: 0x"); Serial.println(cr1Val, HEX);
Serial.print("CR2: 0x"); Serial.println(cr2Val, HEX);
Serial.println("DRV8301 initialization complete.");
}
/////////////////////// Fault Status Monitoring Function ///////////////////////
void checkFaultStatus() {
uint16_t status1 = readDRV8301Register(0);
uint16_t status2 = readDRV8301Register(1);
Serial.print("Status Reg1: 0x"); Serial.println(status1, HEX);
Serial.print("Status Reg2: 0x"); Serial.println(status2, HEX);
if (status1 & (1 << 0)) Serial.println("Fault: PVDD Undervoltage");
if (status1 & (1 << 1)) Serial.println("Fault: DVDD Undervoltage");
if (status1 & (1 << 2)) Serial.println("Fault: GVDD Undervoltage");
if (status1 & (1 << 3)) Serial.println("Fault: GVDD Overvoltage");
if (status1 & (1 << 4)) Serial.println("Fault: Overtemperature Warning");
if (status1 & (1 << 5)) Serial.println("Fault: Overtemperature Shutdown");
}
/////////////////////// Function to Set PWM Frequency ///////////////////////
void setPWMFrequency_All(uint32_t freq) {
const uint32_t resolution = 4096; // 12-bit PWM
uint32_t pwmClock = freq * resolution;
uint32_t divisor = 84000000UL / pwmClock;
if (divisor < 1) divisor = 1;
if (divisor > 255) divisor = 255;
PWM->PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(divisor);
Serial.print("PWM Frequency: ~");
Serial.print((float)84000000UL / (divisor * (float)resolution));
Serial.println(" Hz");
}
/////////////////////// Arduino PWM Initialization Function ///////////////////////
void initPWM() {
analogWriteResolution(12);
setPWMFrequency_All(10000); // 10kHz
Serial.println("PWM initialization complete.");
}
/////////////////////// Arduino ADC Initialization Function ///////////////////////
void initADC() {
analogReadResolution(12);
analogReference(AR_DEFAULT);
Serial.println("ADC initialization: 12-bit, 3.3V reference");
}
/////////////////////// Sensor Transformation Functions ///////////////////////
// Clarke Transformation: 3-phase -> 2-axis (alpha, beta)
void clarkeTransform(float a, float b, float c, float &alpha, float &beta) {
alpha = a;
beta = (a + 2*b) / sqrt(3.0);
}
// Park Transformation: (alpha, beta) -> (d, q) (using rotor angle theta)
void parkTransform(float alpha, float beta, float theta, float &d, float &q) {
d = alpha * cos(theta) + beta * sin(theta);
q = -alpha * sin(theta) + beta * cos(theta);
}
// Inverse Park Transformation: (d, q) -> (alpha, beta)
void inverseParkTransform(float d, float q, float theta, float &alpha, float &beta) {
alpha = d * cos(theta) - q * sin(theta);
beta = d * sin(theta) + q * cos(theta);
}
// Inverse Clarke Transformation: (alpha, beta) -> 3-phase (a, b, c)
void inverseClarkeTransform(float alpha, float beta, float &a, float &b, float &c) {
a = alpha;
b = (-alpha + sqrt(3.0)*beta) / 2.0;
c = (-alpha - sqrt(3.0)*beta) / 2.0;
}
/////////////////////// Simple Sensorless Rotor Angle Estimation Function ///////////////////////
// Estimates rotor angle using back-EMF signals (example: based on atan2)
// In actual implementation, ADC voltage conversion and calibration are required
float estimateRotorAngle(uint16_t emfA, uint16_t emfB, uint16_t emfC) {
float vA = (float)emfA;
float vB = (float)emfB;
float vC = (float)emfC;
// Calculate two-phase equivalent (simplified)
float alpha = vA - 0.5 * (vB + vC);
float beta = (sqrt(3.0)/2.0) * (vB - vC);
float angle = atan2(beta, alpha);
if(angle < 0) angle += TWO_PI;
return angle;
}
/////////////////////// PI Control Update Function ///////////////////////
void updateSpeedPI(float measuredSpeed) {
float error = targetSpeedRPM - measuredSpeed;
speedIntegral += error * dt;
// Simple anti-windup: Limit the integral term
if(speedIntegral > 1000) speedIntegral = 1000;
if(speedIntegral < -1000) speedIntegral = -1000;
phaseCorrection = Kp * error + Ki * speedIntegral;
}
/////////////////////// Fault Safety Handling Function inside FOC ISR ///////////////////////
void handleFaultInISR() {
// Check the nFAULT pin and set PWM output to 0 if a fault occurs
if(digitalRead(NFAULT_PIN) == LOW) {
// Fault detected: set all PWM outputs to 0
analogWrite(INH_A, 0);
analogWrite(INL_A, 0);
analogWrite(INH_B, 0);
analogWrite(INL_B, 0);
analogWrite(INH_C, 0);
analogWrite(INL_C, 0);
}
}
/////////////////////// FOC Interrupt Service Routine (10kHz) ///////////////////////
void FOC_ISR() {
// 1. ADC Sampling (current and back-EMF measurements)
adcSO1 = analogRead(SO1_PIN);
adcSO2 = analogRead(SO2_PIN);
adcEMF_A = analogRead(EMF_A);
adcEMF_B = analogRead(EMF_B);
adcEMF_C = analogRead(EMF_C);
// 2. Fault status check and safety handling (set PWM to 0 on fault)
handleFaultInISR();
// 3. Estimate rotor angle using back-EMF measurements
float rotorAngle = estimateRotorAngle(adcEMF_A, adcEMF_B, adcEMF_C);
// 4. Open-loop based electrical angle update (combining back-EMF estimation and PI correction)
float electricalSpeed = (targetSpeedRPM / 60.0) * TWO_PI * polePairs;
float phaseIncrement = electricalSpeed * dt + phaseCorrection;
currentPhase += phaseIncrement;
if(currentPhase >= TWO_PI) currentPhase -= TWO_PI;
// 5. Simplified speed measurement (based on phaseIncrement)
measuredSpeedRPM = (phaseIncrement / dt) * (60.0 / (TWO_PI * polePairs));
if(isnan(measuredSpeedRPM)) measuredSpeedRPM = targetSpeedRPM;
// 6. PI control update
updateSpeedPI(measuredSpeedRPM);
// 7. Calculate 3-phase PWM duty cycles (using back-EMF and corrected currentPhase)
float dutyA = (sin(currentPhase) + 1.0) / 2.0;
float dutyB = (sin(currentPhase + TWO_PI/3.0) + 1.0) / 2.0;
float dutyC = (sin(currentPhase + 2.0*TWO_PI/3.0) + 1.0) / 2.0;
uint32_t pwmA = (uint32_t)(dutyA * 4095);
uint32_t pwmB = (uint32_t)(dutyB * 4095);
uint32_t pwmC = (uint32_t)(dutyC * 4095);
// 8. Apply deadtime correction
uint32_t pwmA_high = (pwmA > deadTimeCounts) ? pwmA - deadTimeCounts : 0;
uint32_t pwmA_low = (4095 - pwmA > deadTimeCounts) ? (4095 - pwmA) - deadTimeCounts : 0;
uint32_t pwmB_high = (pwmB > deadTimeCounts) ? pwmB - deadTimeCounts : 0;
uint32_t pwmB_low = (4095 - pwmB > deadTimeCounts) ? (4095 - pwmB) - deadTimeCounts : 0;
uint32_t pwmC_high = (pwmC > deadTimeCounts) ? pwmC - deadTimeCounts : 0;
uint32_t pwmC_low = (4095 - pwmC > deadTimeCounts) ? (4095 - pwmC) - deadTimeCounts : 0;
// Apply motor power scaling
pwmA_high = (uint32_t)(pwmA_high * motorScaling);
pwmA_low = (uint32_t)(pwmA_low * motorScaling);
pwmB_high = (uint32_t)(pwmB_high * motorScaling);
pwmB_low = (uint32_t)(pwmB_low * motorScaling);
pwmC_high = (uint32_t)(pwmC_high * motorScaling);
pwmC_low = (uint32_t)(pwmC_low * motorScaling);
// 9. Update 6 PWM outputs
analogWrite(INH_A, pwmA_high);
analogWrite(INL_A, pwmA_low);
analogWrite(INH_B, pwmB_high);
analogWrite(INL_B, pwmB_low);
analogWrite(INH_C, pwmC_high);
analogWrite(INL_C, pwmC_low);
focFlag = true;
}
/////////////////////// Serial Input Processing ///////////////////////
void processSerialInput() {
if(Serial.available() > 0) {
String input = Serial.readStringUntil('\n');
input.trim();
if(input.startsWith("SPEED")) {
float spd = input.substring(6).toFloat();
targetSpeedRPM = spd;
Serial.print("New target speed: ");
Serial.println(targetSpeedRPM);
} else if(input.startsWith("CORR")) {
float corr = input.substring(5).toFloat();
phaseCorrection = corr;
Serial.print("New phase correction: ");
Serial.println(phaseCorrection);
} else if(input.startsWith("POWER")) {
float powerVal = input.substring(6).toFloat();
if(powerVal < 0.0) powerVal = 0.0;
if(powerVal > 1.0) powerVal = 1.0;
motorScaling = powerVal;
Serial.print("New motor scaling: ");
Serial.println(motorScaling);
}
}
}
/////////////////////// Monitor nFAULT Status ///////////////////////
void checkNfaultStatus() {
int nfaultState = digitalRead(NFAULT_PIN);
if(nfaultState == LOW) {
Serial.println("[FAULT] nFAULT LOW: Fault detected!");
} else {
Serial.println("[OK] nFAULT HIGH: No fault.");
}
}
/////////////////////// setup ///////////////////////
void setup() {
Serial.begin(115200);
while(!Serial);
// Configure pins
pinMode(INH_A, OUTPUT); pinMode(INL_A, OUTPUT);
pinMode(INH_B, OUTPUT); pinMode(INL_B, OUTPUT);
pinMode(INH_C, OUTPUT); pinMode(INL_C, OUTPUT);
pinMode(EN_GATE, OUTPUT);
pinMode(CS_PIN, OUTPUT);
pinMode(NFAULT_PIN, INPUT_PULLUP);
Serial.println("Pin configuration completed.");
delay(500);
// Immediately set EN_GATE to HIGH to enable DRV8301 (as per datasheet)
digitalWrite(EN_GATE, LOW);
delay(500);
digitalWrite(CS_PIN, HIGH);
Serial.println("[Setup] EN_GATE enabled and SPI pins configured.");
delay(500);
SPI.begin();
Serial.println("[Setup] SPI initialization complete.");
checkNfaultStatus();
delay(500);
initDRV8301();
Serial.println("[Setup] DRV8301 initialization complete.");
checkNfaultStatus();
delay(500);
initPWM();
Serial.println("[Setup] PWM initialization complete.");
delay(500);
initADC();
Serial.println("[Setup] ADC initialization complete.");
checkNfaultStatus();
delay(500);
digitalWrite(EN_GATE, HIGH);
delay(500);
// Initialize variables
speedIntegral = 0.0;
phaseCorrection = 0.0;
currentPhase = 0.0;
motorScaling = 1.0;
// Start 10kHz interrupt (100 µs period)
// For testing, the period can be reduced if there are issues with the ISR load
Timer3.attachInterrupt(FOC_ISR).start(100);
Serial.println("- - - - - - - - - - - - - -");
Serial.println("[Setup] Setup complete. Starting loop.");
Serial.println("- - - - - - - - - - - - - -");
}
/////////////////////// loop ///////////////////////
void loop() {
processSerialInput();
static unsigned long lastFaultCheck = 0;
if(millis() - lastFaultCheck > 500) {
lastFaultCheck = millis();
Serial.println("------ Fault Status Check ------");
checkNfaultStatus();
checkFaultStatus();
Serial.println("-------------------------------");
}
if(focFlag) {
focFlag = false;
Serial.print("Target RPM: "); Serial.print(targetSpeedRPM);
Serial.print(" Measured RPM: "); Serial.print(measuredSpeedRPM);
Serial.print(" Phase Correction: "); Serial.println(phaseCorrection);
}
delay(10);
}