Need assistance optimizing H-bridge driver code

Status
Not open for further replies.

willyman

Member
Hello,
I have a Teensy controlling an H bridge driver board I designed, which drives a linear motor. The board is used in production for burning in the motors. I'm having some trouble with the code, I'm getting significant delay (off time) when the bridge switches. The frequency of the motor is 95 hz, which gives around 10ms for one complete sine wave cycle. I'm getting between 200-500 us delay when the bridge switches. I'm using 8 bit PWM to generate the sine wave, and an IntervalTimer to trigger clock cycles, where the PWM is updated from lookup table values. The delay during the bridge switch is unacceptable for my application, so I'm looking for some pointers on how I could implement the code better. I'm baffled as to why its behaving this way. I've checked the FET drivers and FETs with a scope, the delay is from the Teensy, not the drivers or FETs. Here is a scope shot of what it looks like when the bridge switches:


The code for the sketch is here:
Here there is also code for a rotary encoder, which controls one of the Teensy DACs. The DAC provides feedback to a switching supply to control the bridge voltage. I have tried removing the Encoder code and it had no effect on the bridge timing. If I place the pin writes withing the timer ISR, the bridge delay improves to only 200 us, but still to much. Thanks for any help!!

Code:
#include "sin_180_10.h"   // ideal for open housing
#define ENCODER_OPTIMIZE_INTERRUPTS    // this causes Encoder.h to use optimized addembly written interrupts
#include <avr/io.h>
#include <avr/interrupt.h>
#include <Encoder.h>


#define DAC0 A21    // DAC that controls power supply
#define DAC1 A22
#define Encoder_A A8
#define Encoder_B A9
const int GAH=30; // Gate A High pin
const int GAL=29; // Gate A Low pin
const int GBH=6; // Gate B High pin
const int GBL=4; // Gate B Low pin
const int TRIGGER = 38;  // used for diagnostics, triggering the scope at start of every cycle

int GAH_Val = 0;
int GAL_Val = 0;
int GBH_Val = 0;
int GBL_Val = 0;
int TRIGGER_Val = 0;

#define NUM_LUT_ELEMENTS 180
int degree_position_start = 1;
int degree_position_end = 180;
const int* DICTIONARY_LUT =sin_180_10;
int WORKING_LUT[NUM_LUT_ELEMENTS];
const int PWM_Resolution=8; // Change this to your desired resolution
const int Base_Frequency = 95; // in cycles per second (hertz)

// unsigned int PWM_Max_Value=2^PWM_Resolution - 1;
unsigned int PWM_Max_Value=pow(2,PWM_Resolution)-1; 
const int ON = PWM_Max_Value;
const int OFF = 0;

const float one_cycle = (1e6)/((float)Base_Frequency); // cycle time in microseconds
const float half_cycle = one_cycle/2.0;
const float clock_interval = one_cycle/(NUM_LUT_ELEMENTS*2); // 526 steps per half cycle in sine look up table
const float cycle_time = ((1e6)/Base_Frequency); // gives the cycle time in microseconds
const float half_cycle_time = cycle_time/2.0;

int HIGH_SIDE = 0; // keeps track of which bridge side is on
int LOW_SIDE = 0;
boolean HIGH_SIDE_ON=false;

IntervalTimer sine_clock;

long CPU_Frequency=F_CPU/1E6; // gets the CPU frequency
float PWM_ideal_freq[15]
 // Teensy 3.5 and 3.6 ideal frequencies per PWM resolution below
//  2bits,   3bits,   4bits, 5bits, 6bits, 7bits, 8bits, 9bits,   10bits,   11bits,  12bits,   13bits,  14bits,  15bits,   16bits 
  {15000000,7500000,3750000,1875000,937500,468750,234375,117187.5,58593.75,29296.875,14648.437,7324.219,3662.109,1831.055,915.527};

float Set_Freq; // calculated frequency for selected PWM bit resolution. Higher resolution == lower carrier frequency
int amplitude_index = 0; // amplitude_index tracks position in sine look up table (only 0-180 degrees of wave, top half of sine)
int cycle_index = 0; // tracks complete 360 degree cycle of wave

Encoder knob(Encoder_A, Encoder_B);
long knob_position = 0;
long new_knob_position = 0;
int DAC_value;

void setup() {
  Serial.begin(115200);
  while(!Serial && millis()<4000){}
  Set_Freq = PWM_ideal_freq[PWM_Resolution-2];  // subtracting 2 because array index starts at 0, and bit resolution begins at 2
  Print_PWM_Data();
  for(int i=0; i<NUM_LUT_ELEMENTS; i++)
  {
      WORKING_LUT[i] = DICTIONARY_LUT[i];

  }
  delayMicroseconds(10);
  pinMode(GAH,OUTPUT); 
  pinMode(GAL,OUTPUT);
  pinMode(GBH,OUTPUT); 
  pinMode(GBL,OUTPUT);
  pinMode(DAC0,OUTPUT);
  pinMode(TRIGGER,OUTPUT); // scope trigger for monitoring waveform
 
  analogWriteFrequency(GAH,Set_Freq); // sets PWM hardware frequency for this pin
  analogWriteFrequency(GBH,Set_Freq); // sets PWM hardware frequency for this pin
  analogWriteResolution(PWM_Resolution); // sets bit resolution. The duty cycle can be set to 2^(num_bits)  discrete values

  digitalWrite(GAH, LOW); // Initialize all FETS low
  digitalWrite(GBH, LOW);
  digitalWrite(GAL, LOW);
  digitalWrite(GBL, LOW);
  analogWrite(DAC0, 254);

  delayMicroseconds(10);
  knob.write(254);
  delayMicroseconds(10);
  new_knob_position = knob.read();
  knob_position = new_knob_position;
  
  // Initialize Bridge to B side on first
  HIGH_SIDE = GBH;
  LOW_SIDE = GAL;
  digitalWrite(LOW_SIDE, ON);

  sine_clock.priority(0);
  sine_clock.begin(Clock_Tick,clock_interval); // callback function "Clock_Tick" will be called every "clock_interval" (in microseconds)
}

FASTRUN void loop() { 

    Check_Knob_Position();
}

FASTRUN void Check_Knob_Position()
{
    new_knob_position = knob.read();
    if (new_knob_position != knob_position) 
    {
      if(new_knob_position < 0)
      {
        knob.write(0);
        new_knob_position = 0;
      }
      if(new_knob_position > 255)
      {
        knob.write(254);
        new_knob_position = 255;
      }

      DAC_value = knob_position;
      analogWrite(DAC0, DAC_value);
      knob_position = new_knob_position;
    }
}

FASTRUN void Clock_Tick()
{ // Total of 180 elements in LUT array (29.4 us per step)== 5292 us  (half of sine cycle @ 95 hz)
  // 5292*2 ==10,584us. Complete cycle is 10,526us (10.526 ms)
  
  cli();                // disables interrupts during clock increment
  amplitude_index++;  // amplitude_index counter restarts every half cycle
  cycle_index++;      // cycle_index restarts every complete cycle
  //sei();
  if(amplitude_index >= NUM_LUT_ELEMENTS)
  {
      if(cycle_index == amplitude_index) //midway through the cycle, completed top half of sine wave, time to switch bridge polarity
      {
        GBH_Val = 2;
        GBL_Val = OFF;
        GAH_Val = 1;
        GAL_Val = HIGH;
        HIGH_SIDE = GBH;
      }
      else // time to start cycle over at beginning of LUT
      {
        GAH_Val = 2;
        GAL_Val = OFF;
        GBH_Val = 1;
        GBL_Val = HIGH;
        cycle_index = 0; 
        HIGH_SIDE = GAH;
        //LOW_SIDE = GBL;
      }
      amplitude_index = 0;
  }
  else 
  {
    if(HIGH_SIDE == GBH)
    {
        GBH_Val = WORKING_LUT[amplitude_index];
        GAH_Val = 0;
    }
    if(HIGH_SIDE == GAH)
    {
        GAH_Val = WORKING_LUT[amplitude_index];
        GBH_Val = 0;
    }
  }
  if(degree_position_start == amplitude_index) TRIGGER_Val = HIGH;
  if(degree_position_end == amplitude_index-1) TRIGGER_Val = LOW;
  sei(); // re-enables interrupts
  Sine_Write();
}

void Sine_Write()
{
  analogWrite(GAH, GAH_Val);
  analogWrite(GBH, GBH_Val);
  digitalWriteFast(GAL, GAL_Val);
  digitalWriteFast(GBL, GBL_Val);
  digitalWriteFast(TRIGGER, TRIGGER_Val);
  
}


void Print_PWM_Data()
{
          delayMicroseconds(10);
  Serial.println("***********************************"); 
  Serial.print("CPU Frequency [MHZ] : ");
  Serial.println(CPU_Frequency);
  Serial.print("PWM Resolution: ");
  Serial.println(PWM_Resolution);
  Serial.print("PWM Set frequency: ");
  Serial.println(Set_Freq);  
  Serial.print("PWM Max Value: ");
  Serial.println(PWM_Max_Value);  
  Serial.println("***********************************\n"); 

  Serial.print("one cycle: \t\t"); Serial.print(one_cycle); Serial.print(" us\t("); Serial.print(one_cycle/1000.0); Serial.println(" ms)");
  Serial.print("half cycle: \t\t"); Serial.print(half_cycle); Serial.print(" us\t("); Serial.print(half_cycle/1000.0); Serial.println(" ms)");
  Serial.print("clock tick: \t\t"); Serial.print(clock_interval); Serial.println(" us");
  Serial.print("#LUT points(180 deg): \t"); Serial.println(NUM_LUT_ELEMENTS);
  Serial.print("LUT PWM Max Value: \t"); 
      int max_val =0;
    for(int i=0; i<NUM_LUT_ELEMENTS; i++)
    {
        int LUT_val =  DICTIONARY_LUT[i];
        if(LUT_val > max_val) 
        {
          max_val = LUT_val;
        }
    }
  Serial.println(max_val);
  delayMicroseconds(10);
  Serial.print("\nWave period should be:\t"); Serial.print(one_cycle); Serial.print(" us\t("); Serial.print(one_cycle/1000); Serial.println(" ms)");
  float calc_freq = 1/(one_cycle/1e6);
  Serial.print("Calculated Frequency: \t"); Serial.print(calc_freq); Serial.println(" hz");
  Serial.print("Set Frequency: \t\t"); Serial.print(Base_Frequency); Serial.println(" cycles per second");

    Serial.print("\nDictionary LUT vals:\n"); 
    int val =0;
    for(int i=0; i<NUM_LUT_ELEMENTS; i++)
    {
        int LUT_val =  DICTIONARY_LUT[i];

        Serial.print(LUT_val); Serial.print("\t");
        if((i+1)%10 == 0) 
        {
          Serial.println();
        }
                delayMicroseconds(1);
    }
    Serial.println();
    Serial.flush();
}
 
It might be good if you told us what the traces in the scope shot are along with the horizontal scale. Also, perhaps provide a schematic or block diagram. And, a clear drawing of what you want the traces to look like. Oh, and what Teensy board?
 
Status
Not open for further replies.
Back
Top