PID library

Working libraries, libraries being ported and related hardware
jansun
Posts: 10
Joined: Fri Nov 02, 2018 9:59 am

PID library

Post by jansun » Sat Jan 05, 2019 1:51 pm

Hi all,

I was making an attempt to use the arduino PID library:

https://github.com/br3ttb/Arduino-PID-Library

i build a setup with first an arduino and then same setup with a stm32f1c8.

Arduino code:

Code: Select all

#include 
#define MotEnable 9 //Motor Enamble pin Runs on PWM signal
#define MotFwd  6  // Motor Forward pin
#define MotRev  7 // Motor Reverse pin
String readString; //This while store the user input data
int User_Input = 0; // This while convert input string into integer
int encoderPin1 = 2; //Encoder Output 'A' must connected with intreput pin of arduino.
int encoderPin2 = 3; //Encoder Otput 'B' must connected with intreput pin of arduino.
volatile int lastEncoded = 0; // Here updated value of encoder store.
volatile long encoderValue = 0; // Raw encoder value
int PPR = 2400;  // Encoder Pulse per revolution.
int angle = 360; // Maximum degree of motion.
int REV = 0;          // Set point REQUIRED ENCODER VALUE
int lastMSB = 0;
int lastLSB = 0;
double kp = 4 , ki = 0 , kd = 1;             // modify for optimal performance
double input = 0, output = 0, setpoint = 0;
PID myPID(&input, &output, &setpoint, kp, ki, kd, DIRECT);  
void setup() {
  pinMode(MotEnable, OUTPUT);
  pinMode(MotFwd, OUTPUT); 
  pinMode(MotRev, OUTPUT); 
  Serial.begin(9600); //initialize serial comunication
   pinMode(encoderPin1, INPUT_PULLUP); 
  pinMode(encoderPin2, INPUT_PULLUP);
  digitalWrite(encoderPin1, HIGH); //turn pullup resistor on
  digitalWrite(encoderPin2, HIGH); //turn pullup resistor on
  //call updateEncoder() when any high/low changed seen
  //on interrupt 0 (pin 2), or interrupt 1 (pin 3) 
  attachInterrupt(0, updateEncoder, CHANGE); 
  attachInterrupt(1, updateEncoder, CHANGE);
  //TCCR1B = TCCR1B & 0b11111000 | 1;  // set 31KHz PWM to prevent motor noise
  myPID.SetMode(AUTOMATIC);   //set PID in Auto mode
  myPID.SetSampleTime(1);  // refresh rate of PID controller
  myPID.SetOutputLimits(-250, 250); // this is the MAX PWM value to move motor, here change in value reflect change in speed of motor.
}//end setup
void loop() {
  while (Serial.available()) { //Check if the serial data is available.
    delay(3);                  // a small delay
    char c = Serial.read();  // storing input data
    readString += c;         // accumulate each of the characters in readString
  }
  if (readString.length() >0) { //Verify that the variable contains information
   Serial.println(readString.toInt());  //printing the input data in integer form
    User_Input = readString.toInt();   // here input data is store in integer form  
  }
/*
  if( input<5){
    User_Input = 360;
  }
  if(input > 2350){
    User_Input = 0;
  }
  */
  REV = map (User_Input, 0, angle, 0, PPR); // mapping degree into pulse
  setpoint = REV;                    //PID while work to achive this value consider as SET value
  input = encoderValue ;           // data from encoder consider as a Process value
  //myPID.Compute();                 // calculate new output
  //pwmOut(output); 
  Serial.print("    enc: ");
  Serial.println(input);
}//end loop
void pwmOut(int out) {                               
  if (out > 0) {                         // if REV > encoderValue motor move in forward direction.    
    analogWrite(MotEnable, out);         // Enabling motor enable pin to reach the desire angle
    forward();                           // calling motor to move forward
  }
  else {
    analogWrite(MotEnable, abs(out));          // if REV < encoderValue motor move in forward direction.                      
    reverse();                            // calling motor to move reverse
  }
  readString=""; // Cleaning User input, ready for new Input
}//End pwmOut
void updateEncoder(){
  int MSB = digitalRead(encoderPin1); //MSB = most significant bit
  int LSB = digitalRead(encoderPin2); //LSB = least significant bit
  int encoded = (MSB << 1) |LSB; //converting the 2 pin value to single number
  int sum  = (lastEncoded << 2) | encoded; //adding it to the previous encoded value
  if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue ++;
  if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue --;
  lastEncoded = encoded; //store this value for next time
}
void forward () {
  digitalWrite(MotFwd, HIGH); 
 digitalWrite(MotRev, LOW); 
}
void reverse () {
  digitalWrite(MotFwd, LOW); 
 digitalWrite(MotRev, HIGH); 
}
void finish () {
  digitalWrite(MotFwd, LOW); 
 digitalWrite(MotRev, LOW); 
}
The code i made with for the stm32:

Code: Select all

/*
 * PID for STM32
 */
#include 
#define MotEnable PA0 //Motor Enamble pin Runs on PWM signal
#define MotFwd  PB15  // Motor Forward pin
#define MotRev  PB14 // Motor Reverse pin
String readString; //This while store the user input data
int User_Input = 0; // This while convert input string into integer
int PPR = 1200;  // Encoder Pulse per revolution.
int angle = 360; // Maximum degree of motion.
int REV = 0;          // Set point REQUIRED ENCODER VALUE
double kp = 1 , ki = 0.05 , kd = 0.75;             // modify for optimal performance
double input = 0, output = 0, setpoint = 0;
PID myPID(&input, &output, &setpoint, kp, ki, kd, DIRECT);  
void setup() { 
  // Encoder, in this case PB6 and PB7:
  HardwareTimer timer4(4);
  timer4.pause();
  TIMER4->regs.adv->CCMR1 |= 0x0101;
  TIMER4->regs.adv->CCER  |= 0x0000;
  TIMER4->regs.adv->SMCR |= 0x0303; //(reads 8 clock cycles to make sure)
  TIMER4->regs.adv->CR1  |= 0x0001;
  timer4.refresh(); 
  timer4.resume();
  //PWM on timer2:
  HardwareTimer timer2(2);
  timer2.pause();
  timer2.setPrescaleFactor(8);
  timer2.setOverflow(249);
  timer2.refresh();
  timer2.resume();
  pinMode(MotEnable,PWM);
  pinMode(MotFwd, OUTPUT); 
  pinMode(MotRev, OUTPUT); 
  Serial.begin(9600); //initialize serial comunication
  //Serial.end();
  myPID.SetMode(AUTOMATIC);   //set PID in Auto mode
  myPID.SetSampleTime(1);  // refresh rate of PID controller
  myPID.SetOutputLimits(-100, 100); // this is the MAX PWM value to move motor, here change in value reflect change in speed of motor.
}//end setup
void loop() {
  while (Serial.available()) { //Check if the serial data is available.
    delay(3);                  // a small delay
    char c = Serial.read();  // storing input data
   Serial.println(c);  //printing the input data in integer form
    User_Input = c;   // here input data is store in integer form  
  }
  REV = map (User_Input, 0, angle, 0, PPR); // mapping degree into pulse
  setpoint = REV;                    //PID while work to achive this value consider as SET value
  input = TIMER4->regs.adv->CNT;           // data from encoder consider as a Process value
  //Serial.print("Enc: ");
  Serial.println(input);
  myPID.Compute();                 // calculate new output
  pwmOut(output); 
  //Serial.print("    out: ");
  //Serial.println(output);
}//end loop
void pwmOut(int out) {                               
  if (out > 0) {                         // if REV > encoderValue motor move in forward direction.    
    pwmWrite(MotEnable, out);         // Enabling motor enable pin to reach the desire angle
    forward();                           // calling motor to move forward
  }
  else {
    pwmWrite(MotEnable, abs(out));          // if REV < encoderValue motor move in forward direction.                      
    reverse();                            // calling motor to move reverse
  }
}//End pwmOut
void forward () {
  digitalWrite(MotFwd, HIGH); 
 digitalWrite(MotRev, LOW); 
}
void reverse () {
  digitalWrite(MotFwd, LOW); 
 digitalWrite(MotRev, HIGH); 
}
void finish () {
  digitalWrite(MotFwd, LOW); 
 digitalWrite(MotRev, LOW); 
}
Now, the arduino works fine and the STM32 code not. I build everything the same and with the stm32 the motor keeps spinning. Icant find anything in the code and for me it looks like the lib code should be compatible with stmduino? Encoder direction are oke, but the output of the PID dont react like the arduino code.

Someone a clue?

User avatar
mrburnette
Posts: 3001
Joined: Mon Apr 27, 2015 12:50 pm
Location: Greater Atlanta
Contact:

Re: PID library

Post by mrburnette » Sat Jan 05, 2019 4:37 pm

jansun wrote:
Sat Jan 05, 2019 1:51 pm
...
Now, the arduino works fine and the STM32 code not. I build everything the same and with the stm32 the motor keeps spinning. Icant find anything in the code and for me it looks like the lib code should be compatible with stmduino? Encoder direction are oke, but the output of the PID dont react like the arduino code.

Someone a clue?
The "clue" is the sketch does not work as expected; therefore, either your code or the library is to blame. If the 8-bit AVR boards & code work, likely the issue is something to do with what is commonly called porting - the code correction and adjustment for the architectural assumptions made by libraries. An example is that an AVR integer is 16-bits and the STM32 integer is 32-bits.

Years ago, a number of common libraries were ported by members: here

Beyond that, members have posted successes in the forum here

IMO, the biggest problem is members that "want" a paticular library but lack the knowledge to execute the port themselves. This is probably where Arduino, as a concept, does the most harm; the library concept allows coders to work beyond their skill level and be satisfied with their mediocre coding skill.

What is broken here is not the AVR library, but your expectation. You just need to jump into learning how libraries work and how to modify library code to match your expectations. This effort will make you far more knowledgeable and a much better programmer able to move across chip architecture and software tool sets.

Or, you could just beg the forum for someone to "fix" the library for you.

Ray

edogaldo
Posts: 432
Joined: Fri Jun 03, 2016 8:19 am

Re: PID library

Post by edogaldo » Sat Jan 05, 2019 5:29 pm

I can see several differences in the two versions, for example the usage of 2 external interrupts in the Arduino version that I can’t see in the STM version, so the two sketches don’t seem equivalent on a first check..

stevestrong
Posts: 3053
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany
Contact:

Re: PID library

Post by stevestrong » Sat Jan 05, 2019 6:05 pm

You did not set the mode of pins PB6/7.

jansun
Posts: 10
Joined: Fri Nov 02, 2018 9:59 am

Re: PID library

Post by jansun » Sat Jan 05, 2019 8:01 pm

stevestrong wrote:
Sat Jan 05, 2019 6:05 pm
You did not set the mode of pins PB6/7.
thanks!
edogaldo wrote:
Sat Jan 05, 2019 5:29 pm
I can see several differences in the two versions, for example the usage of 2 external interrupts in the Arduino version that I can’t see in the STM version, so the two sketches don’t seem equivalent on a first check..
I cant use the encoder code like the arduino code (google direct port manipulation why), this part is indeed different, but it looks like this part works.
mrburnette wrote:
Sat Jan 05, 2019 4:37 pm
What is broken here is not the AVR library, but your expectation. You just need to jump into learning how libraries work and how to modify library code to match your expectations. This effort will make you far more knowledgeable and a much better programmer able to move across chip architecture and software tool sets.
Im working on this. what i already did is copied the lib to a separate tab so i can change the library and "debug" it. I was hoping for a stupidity like stevestrong mentioned, but unfortunately this didnt solve the issue. I dont like to beg for someone else solving my code, but i am for some c++ lessons :geek:

It looks off-topic, but to understand and fix the PID libraray i've made a simple led blink library code and im stuck right now.

.h:

Code: Select all

#ifndef Led_h
#define Led_h
#include "Arduino.h"
class Led{
  public:
    Led(int pin);
    void init();
    void toggleLed();
    void handler_led();
   private:
};
#endif
.cpp:

Code: Select all

#include "Arduino.h"
#include "Led.h"
Led::Led(int pin){
}
void Led::init(){
    pinMode(PA8, OUTPUT);
    HardwareTimer timer (1);
    timer.pause();
    timer.setPrescaleFactor(11);
    timer.setOverflow(599);
    // Set up an interrupt on channel 1
    timer.setChannel1Mode(TIMER_OUTPUT_COMPARE);
    timer.setCompare(TIMER_CH1, 1);  // Interrupt 1 count after each update
    timer.attachCompare1Interrupt(handler_led);
    // Refresh the timer's count, prescale, and overflow
    timer.refresh();
    // Start the timer counting
    timer.resume();
}
void Led::handler_led(void){
  toggleLed();
}
void Led::toggleLed(){
  digitalWrite(PA8, !digitalRead(PA8));
}
right now i get an error on the line

Code: Select all

 timer.attachCompare1Interrupt(handler_led);
Error:

Code: Select all

no matching function for call to 'HardwareTimer::attachCompare1Interrupt()'
Yes, i googled it, cant find out why. Some good c++ teachers here? :geek:

dannyf
Posts: 330
Joined: Wed May 11, 2016 4:29 pm

Re: PID library

Post by dannyf » Sat Jan 05, 2019 9:36 pm

the arduino PID library
you may find it helpful to go back to what you meant by a "library".

in general, you want a library to be a black box with appropriate inputs and outputs. and whenever possible, have a good boundary that separates the software side of things from the hardware side of thing.

A PID routine is one that should be totally implemented in software, with a well defined input and output set of variables. in the MCU environment, such a routine may be called periodically by the hardware to perform data capture, calculation and output attenuation.

it is unhelpful, from that perspective, to have a set of PID library routines that 1) comingles your hardware and software implementaton - such a routine is application specific and hard to port to your next application / platform; and/or 2) has pre-sets that the user cannot affect.

here is an example of my PID implementation: https://dannyelectronics.wordpress.com/ ... -attiny85/

the particular PID routines it used are application and instance independent, meaning that the same code can be invoked on the same platform by different tasks in different instances, to minimize foot-print.

obviously the whole thing hinges on your particular definition of "library". Mine isn't the absolute or only way of doing it.

stevestrong
Posts: 3053
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany
Contact:

Re: PID library

Post by stevestrong » Sat Jan 05, 2019 10:50 pm

jansun wrote:
Sat Jan 05, 2019 8:01 pm
Error:

Code: Select all

no matching function for call to 'HardwareTimer::attachCompare1Interrupt()'
Try

Code: Select all

timer.refresh();
timer.attachInterrupt(TIMER_CH1, handler_led);
timer.resume();
Attach the interrupt always after refresh.

jansun
Posts: 10
Joined: Fri Nov 02, 2018 9:59 am

Re: PID library

Post by jansun » Sun Jan 06, 2019 11:11 am

stevestrong wrote:
Sat Jan 05, 2019 10:50 pm
jansun wrote:
Sat Jan 05, 2019 8:01 pm
Error:

Code: Select all

no matching function for call to 'HardwareTimer::attachCompare1Interrupt()'
Try

Code: Select all

timer.refresh();
timer.attachInterrupt(TIMER_CH1, handler_led);
timer.resume();
Attach the interrupt always after refresh.
Thanks again!

Unfortunately didnt solve it😕

stevestrong
Posts: 3053
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany
Contact:

Re: PID library

Post by stevestrong » Sun Jan 06, 2019 1:57 pm

Ah, that will not work at all because the function you want to attach is a class member. And pointing to a class member function is very tricky...
Make that function standalone and it will work.

MoDu
Posts: 114
Joined: Mon Jul 16, 2018 1:51 pm

Re: PID library

Post by MoDu » Sun Jan 06, 2019 2:26 pm

stevestrong wrote:
Sun Jan 06, 2019 1:57 pm
And pointing to a class member function is very tricky...
Yes, that's another layer of complexity not needed at this stage.

Food for thought, if you're following the C++ gravy train.

Post Reply