How to set motor speed without Commander?

Hi all!

I have a BLDC motor driven by a L298N dual H bridge and an Arduino Duo board running Simple FOC on it. After some troubles I am able to run and monitor the motor with the SimpleFOC Commander via PC.

Now I would like to set the speed and some other values from within the Arduino. I tried to simply set the motor.target variable within the main loop but the motor does not turn. This is my current code:

/* INCLUDES */
#include <SimpleFOC.h>
#include "src/defaults.h"

/* BLDC */
BLDCMotor motor = BLDCMotor(NUM_POLEPAIRS);                                     // BLDC motor init
BLDCDriver3PWM driver = BLDCDriver3PWM(PIN_PWM1, PIN_PWM2, PIN_PWM3);           // BLDC PWM init
HallSensor sensor = HallSensor(PIN_HALL1, PIN_HALL2, PIN_HALL3, NUM_POLEPAIRS); // BLDC hall init

/* ISR HANDLER */
void doA(){sensor.handleA();}
void doB(){sensor.handleB();}
void doC(){sensor.handleC();}

/* GLOBAL VARIABLES */
float target_velocity = 0;


void setup()
{
  Serial.begin(115200);                               // start serial in-/output

  /* BLDC HALL SENSOR SETUP */
  sensor.pullup = Pullup::USE_INTERN;                 // use internal pullup for hall inputs
  sensor.init();
  sensor.enableInterrupts(doA, doB, doC);             // initialize hall sensor hardware

  /* BLDC DRIVER SETUP */
  driver.voltage_power_supply = 24;                   // power supply voltage [V]
  driver.init();

  /* BLDC MOTOR SETUP */
  motor.linkSensor(&sensor);                          // link the motor to the sensor
  motor.voltage_sensor_align = 5;                     // motor align procedure voltage [V]
  motor.linkDriver(&driver);                          // link the motor and the driver

  /* BLDC Important settings */
    motor.foc_modulation = FOCModulationType::SinePWM;  // use sinusoidal PWM modulation
    motor.torque_controller = TorqueControlType::voltage;     // set torque control loop (inner control loop) to be used
    motor.controller = MotionControlType::torque;       // set motion control loop (outer control loop) to be used
    motor.voltage_limit = 20;                           // motor voltage limit   
  /* END  BLDC Important settings */

  /* BLDC initializations */
  motor.init();
  // motor.initFOC(2.09, Direction::CW);              // give specific values to skip initialization
  motor.initFOC();                                    // init FOC without specified values -> initialization routine detects values

  TC_init();                                          // initialize timer/counter

  motor.enable();
  driver.enable();

  Serial.println("Linear Stage ready.");
  _delay(100);
}



void loop()
{
  delay(1000);
  motor.target = 2;
  delay(1000);
  motor.target = -2;
}



void TC_init()    // initialize timer with 1MHz
{
  PMC->PMC_PCER1 |= PMC_PCER1_PID35;                        // TC8 power ON : Timer Counter 2 channel 2 IS TC8

  TC2->TC_CHANNEL[2].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1    // TIMER_CLOCK1 = MCK/2 = 42MHz
                              | TC_CMR_WAVE                 // Waveform mode: rising edge
                              | TC_CMR_WAVSEL_UP_RC;        // UP mode with automatic trigger on RC Compare

  TC2->TC_CHANNEL[2].TC_RC = 42;                            // Frequency = (Mck/2)/TC_RC  Hz (42MHz/42=1MHz)

  TC2->TC_CHANNEL[2].TC_IER = TC_IER_CPCS;                  // Set interrupt comparing to Register C (RC)
  NVIC_EnableIRQ(TC8_IRQn);                                 // Enable interrupt for TC8
  TC2->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN;  // Software trigger TC2 counter and enable
}



__attribute__ ((weak))    // overwrite generic TC8 handler from SimpleFOC library
void TC8_Handler()
{

  static uint32_t counter1;
  static uint32_t counter2;
  static uint32_t counter3;

  TC2->TC_CHANNEL[2].TC_SR;                                 // Read and clear status register

  if(counter1++ == 100)                                     // 10kHz
  {
    counter1 = 0;
    
    motor.loopFOC();                                        // loop FOC algorithm with 10kHz
  }

  if(counter2++ == 500)                                     // 2kHz
  {
    counter2 = 0;
    
    //lin_encoder.sampleVelocity();                         // sample velocity with the desired 2kHz
  }

 
  if(counter3++ == 2500)                                    // 400Hz
  {
    counter3 = 0;
    
//    motor.move(target_velocity);                            // loop motor moving function with 400Hz
  }
}

Is there something wrong with my code or can’t the motor’s speed be modified simply with the motor.target variable? Do I have to modify the argument of motor.move()?

I would be glad for any help!

Best,
Florian

motor.move(motor. target);

I like the way you use a timer to invoke things at fixed frequency!

Yeah, you have to call ‘motor.move(target)’ if you want the motor to move…

Thanks! I like it better when hardware drivers have their fixed shedule.

I modified the code from my first post by using the following code in the main loop:

  delay(1000);
  motor.move(2);
  delay(1000);
  motor.move(-2);

The motor is still not moving. The initialization works.
Why does the motor not turn?

No sorry, I was unclear…

The motor.move() has to be in the part with he timers. Calling it at a frequency of about half to one tenth of the loopFOC() function is probably good.

In the loop function you can use motor.target = 2.0f to set the target value.

My main loop now looks like this:

  delay(1000);
  motor.target = 2.0f;
  delay(1000);
  motor.target = -2.0f;

And the command motor.move(); is lopped with 2kHz.
Still no movement… Is my code correct so far?

The output when monitoring is the following:

MOT: Monitor enabled!
MOT: Init
MOT: Enable driver.
MOT: Align sensor.
MOT: sensor_direction==CW
MOT: PP check: OK!
MOT: Zero elec. angle: 2.09
MOT: No current sense.
MOT: Ready.

Yes this would be correct from my point of view, and the intialization looks good.

Are you sure the timer is working as it should? Perhaps you can toggle a pin to check the interrupt is being called?

I have to say that using the timers in this way is not a common pattern for us, I don’t think many people do it.

There are several subtleties to this approach. One thing is the transfer of the values from the main loop to the interrupt routine. While this should be ok from the point of view of concurrency (I think 32bit transfers would be atomic on STM32) I think you might need to declare the target value volatile, which it isn’t at the moment.

Another is reading the sensor - SPI will probably depend on interrupts itself, which will not run while in the interrupt routine of the timer.
One approach could be to use the timer to trigger the calls, but actually execute them in the main loop. E.g. the timer set a volatile variable like “runMove = true”, and the main loop, which should run as fast as possible (no delays) does a check like

if (runMove) { 
   motor.move(); 
   runMove = false; 
}

In this way you avoid having to deal with interrupts within interrupts, which can be very messy.

Thanks for your reply and sorry for the late response, I was busy with other projects…

It was indeed the Timer-Interrupt which wasn’t triggered. The reason was the Timer-Interrupt function TC8_Handler was already used in the file SimpleFOC\src\drivers\hardware_specific\due_mcu.cpp.
The statement __attribute__ ((weak)) wasn’t enough and I had to uncomment the respecting code lines.

Now everything works like expected and the Timer-Interrupt is triggered!

1 Like