PID using Hall output not add on Encoder

Hello,
I’ve been search for a answer to a question for a long time and have found nothing that fits.
I have a BLDC Motor with Hall Outputs (x3). I have a motor controller that has a PMW input. The motor controller also has a “signal output” which takes the 3 hall signals and amalgamates them to a single 12 pulses per rotation 2.65V Square wave.

I can control the motor using the PMW output from both and Arduino Uno and an ATTiny 85.

I can also read the RPM with the Arduino.

My problem is that I would like to close the loop and control the RPM of the motor using the PMW signal which is being controlled by the RPM reader.

I have found some PID code but am not sure how to implement it correctly.

My goal is to run the motor at either exactly 6250 RPM (75000 pulse per minute) or alternatively 6000 RPM (72000 pulse per minute)

The question is; Can I do this with and Arduino/ATtiny 85?

If I can, what to I need to change in this code to make it work?

I did not write the code as I have no skills in coding. I do understand enough to change parameters if I know which ones to change (which I don’t in this case)

Can anyone help or point me in the right direction to get help please?

Many thanks

Marek

This is the code I've been using to run the motor at 25% speed (PMW 64) I don't know what IN1 and IN2 are just in case you want to know:

#include <util/atomic.h> // For the ATOMIC_BLOCK macro

#define ENCA 2 // YELLOW
#define PWM 5
#define IN2 6
#define IN1 7

volatile int posi = 0; // specify posi as volatile: https://www.arduino.cc/reference/en/language/variables/variable-scope-qualifiers/volatile/
long prevT = 0;
float eprev = 0;
float eintegral = 0;

void setup() {
  Serial.begin(9600);
  pinMode(ENCA,INPUT);
  attachInterrupt(digitalPinToInterrupt(ENCA),readEncoder,RISING);
  
  pinMode(PWM,OUTPUT);
  pinMode(IN1,OUTPUT);
  pinMode(IN2,OUTPUT);
  
  Serial.println("target pos");
}

void loop() {

  // set target position
  //int target = 1200;
  int target = 250*sin(prevT/1e6);

  // PID constants
  float kp = 1;
  float kd = 0.025;
  float ki = 0.0;

  // time difference
  long currT = micros();
  float deltaT = ((float) (currT - prevT))/( 1.0e6 );
  prevT = currT;

  // Read the position in an atomic block to avoid a potential
  // misread if the interrupt coincides with this code running
  // see: https://www.arduino.cc/reference/en/language/variables/variable-scope-qualifiers/volatile/
  int pos = 0; 
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    pos = posi;
  }
  
  // error
  int e = pos - target;

  // derivative
  float dedt = (e-eprev)/(deltaT);

  // integral
  eintegral = eintegral + e*deltaT;

  // control signal
  float u = kp*e + kd*dedt + ki*eintegral;

  // motor power
  float pwr = fabs(u);
  if( pwr > 64 ){
    pwr = 64;
  }

  // motor direction
  int dir = 1;
  if(u<0){
    dir = -1;
  }

  // signal the motor
  setMotor(dir,pwr,PWM,IN1,IN2);


  // store previous error
  eprev = e;

  Serial.print(target);
  Serial.print(" ");
  Serial.print(pos);
  Serial.println();
}

void setMotor(int dir, int pwmVal, int pwm, int in1, int in2){
  analogWrite(pwm,pwmVal);
  if(dir == 1){
    digitalWrite(in1,HIGH);
    digitalWrite(in2,LOW);
  }
  else if(dir == -1){
    digitalWrite(in1,LOW);
    digitalWrite(in2,HIGH);
  }
  else{
    digitalWrite(in1,LOW);
    digitalWrite(in2,LOW);
  }  
}

void readEncoder(){
    posi++;
  }`Preformatted text`

Here's a video for the motor running: Arduino BLDC with Hall PID Speed Control 001 - YouTube

Have You tried using different "target"? Compile and run at 25%. Do the same for 50% resp. 75% and see if it works.
Expanding the way of the source, setting "target", is endless.

IN1 and IN2 are the names of the signals controlling the motor driver making the motor spin, viewed at the driver.

Thanks for the info. My controller only uses a PMW signal so they must be redundant in my set up.

Cheers

Marek

I'll give it a go and if anything changes I'll let you know.

Thanks again.

You're welcome.

These:

long prevT = 0;
long currT = micros();

Should be unsigned long, when micros() exceeds 2147483647, you will get negative numbers.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.