Using an Arduino as a multichannel signal calibrator - help with program logic

Hi all,

I am new to microcontrollers and I have picked up an arduino Uno r3 and a Leonardo for some experimenting. I have now progressed through a number of the Arduino examples and am now venturing into some pet projects. The 1st project I have is to turn my Arduino into a multi-signal calibrator. The project I have is to take my BLDC motor phase signals (3 signals) which use digital Hall Sensors that have a 0v to 4.3V logic, read them (square wave formation) and then output calibrated signals to an external controller. The signal calibration range will be from ~ 75% through to 100% of the input. Note the hardware in use are sealed units and I have not been unsuccessful at finding any data sheets so I am doing some reverse ‘engineering’ here. The signal time periods between the phases are around 2.6msec at 20% of power reducing into the high microseconds at 100%. The actual time periods of half a wave for a phase is 9msec (20%).

Importantly the phase signals are susceptible to out of synch errors causing the motor to knock if I get the calibration logic out of synch.

To date I have spent time learning about signals and how the Arduino can process these items. I have also experimented with interrupts and debouncing circuits but not polling/timer sequences.

I have a sketch that uses 3 pin change interrupts, based on DuaneB’s multi channel sketch, that passes through the BLDC phase signals. The pins have been selected to work with both the Uno and the Leonardo. This sketch appears to work although there is the slightest of knocking at start up from the motor. In this sketch there are three ISRs which are triggered on a ‘change’, record the pin state and set a flag that an interrupt occurred. The loop function takes a local copy of the pin state & flags and then toggles the output pin based on which interrupt has fired.

The Arduino logic that I think I need for the calibration of the signals goes something like this:

In the ISRs;
• when triggered read the input pin state and make this available to the loop function
• record the start time in micros and make this available to the loop function
• set the ISR flag

In the loop function;
• when the flag is set
• calculate the interval between this trigger and the last - i.e. (previousStart time - currentStart time)
• set up a calibration interval which acts as an extension of the existing output pin state
• the calibration interval is equal to the (modInterval - interval) where modInterval is calculated by (interval/calibration value)
• the cal interval becomes the delay time before the output state is toggled i.e. if the interval is 4msec then the cal interval would be .4msec at a calibration of 90%.

I have spent a lot of time learning the C++ coding syntax, logic code sequences, experimenting with many different techniques and I still have a ways to go so I am looking for some help.

So my question - is my pseudo logic sound. If so can I have some ideas, code snippets etc as to how to achieve the multi signal calibration?

Here is the code I have now.

Variables:

// 3 Channel Signal Calibration

// include the pinchangeint library
#include <PinChangeInt.h>

// Assign channel in pins
const byte phaseOin = 8;
const byte phaseGin = 9;
const byte phaseYin = 10;

// Assign channel out pins
const byte phaseOout = 5;
const byte phaseGout = 6;
const byte phaseYout = 7;

// Setup signal outputs expected by external processor
volatile uint8_t phaseOStateIn;
volatile uint8_t phaseGStateIn;
volatile uint8_t phaseYStateIn;

//Setup calibration & debounce constants
const int debounce = 100;
float calPercent = .85;

// These bit flags are set in FlagsShared to indicate which
// channels have new signals
const byte phaseOflag = 1;
const byte phaseGflag = 2;
const byte phaseYflag = 4;

// holds the update flags defined above
volatile uint8_t FlagsShared;

Setup:

void setup()
{

  //Speed pin setup                      
  pinMode(phaseOout, OUTPUT);    
  pinMode(phaseOin, INPUT);
  digitalWrite (phaseOin, HIGH);
  
  //PhaseO pin setup                      
  pinMode(phaseGout, OUTPUT);    
  pinMode(phaseGin, INPUT);
  digitalWrite (phaseGin, HIGH);

  //PhaseG pin setup                      
  pinMode(phaseYout, OUTPUT);    
  pinMode(phaseYin, INPUT);
  digitalWrite (phaseYin, HIGH);

  // use the PinChangeInt library to attach the interrupts
  // Pins chosen make this interchangeable with an Uno and Leo
  PCintPort::attachInterrupt(phaseOin, calcPhaseO,CHANGE); 
  PCintPort::attachInterrupt(phaseGin, calcPhaseG,CHANGE); 
  PCintPort::attachInterrupt(phaseYin, calcPhaseY,CHANGE); 
}

Output

void loop()
{
  // create local variables to hold a local copies of the channel inputs

  static uint8_t phaseOstate;

  static uint8_t phaseGstate;

  static uint8_t phaseYstate;

  // local copy of update flags
  
  static uint8_t UpdateFlags;

  // check shared update flags to see if any channels have a change in the signal
  
  if(FlagsShared)
  {
    noInterrupts();
    
    // turn interrupts off quickly to take local copies of the shared variables

    // take a local copy of which channels were updated in case we need to use this in the rest of loop
    
    UpdateFlags = FlagsShared;
    
    
    if(UpdateFlags & phaseOflag)
    {
      phaseOstate = phaseOStateIn;
    }

    if(UpdateFlags & phaseGflag)
    {
      phaseGstate = phaseGStateIn;
    }

    if(UpdateFlags & phaseYflag)
    {
      phaseYstate = phaseYStateIn;
    }
                
    // clear shared copy of updated flags
    
    FlagsShared = 0;
    
    interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on

  }
  

  if(UpdateFlags & phaseOflag)
  {
    digitalWrite(phaseOout, phaseOstate);
  }

ISR

//  interrupt service routines

void calcPhaseO()
{
  phaseOStateIn = digitalRead(phaseOin);
  FlagsShared |= phaseOflag;
}

... reducing into the high microseconds at 100%.

How high?

Hi - thanks for reading the lengthy description. I have measured 875 microseconds.

So let me share as part of my learning the couple of approaches that I have tried that don't work.

So if I simply calculate a delay and then add this prior to toggling the output:

    phaseOdelay=((phaseOinterval/calPercent)-phaseOinterval);
    delayMicroseconds(phaseOdelay);
    digitalWrite(phaseOout, phaseOstate);

then it doesn't alter the waveforms period rather it simply 'moves' the wave form in time (sorry if my explanation confuses you but it has been far too many years since I last used technical speak to describe observations).

Secondly if I delay one of the transitions based on the calibration percentage like this:

    if (phaseOstate==LOW)
    {
    phaseOdelay=((phaseOinterval/calPercent)-phaseOinterval);
    delayMicroseconds(phaseOdelay);
    digitalWrite(phaseOout, phaseOstate);
    }
    else
    {
    digitalWrite(phaseOout, phaseOstate);
    }

then again I manage to alter the period of the 'highs' but at the same time shorten the duration of the 'lows' meaning that there is no change to the waveforms period.

For me the effects aren't obvious when reading the code but when you see it on the scope a lightbulb goes off.