IN this tutorial we will see how to control one axis but using BRUSHED DC MOTORS. Usually CNC axis will move using stepper motors in an open loop control. Here we will see an example of close loop control with an optic encoder as feedback. Why do this? well, for bigger motors, the price of stepper motors and drivers could get very high. Using DC brushed motors with a gear box and close loop control could get cheaper. Lets see how to do this.
I will make just only one axis. See full part list also here on this link. For that we need the parts below. The motor with the encoder in quadrature, the H bridge driver, the Arduino with a potentiometer and the rest of components. For the motor you could get only the motor and then make your own gear system or buy directly the motor witha gear box. See the scheamtic for all the connections.
I've mounted everything on a plywood baord. We also need some M3 screws for that or maybe some glue. Some lubrificant for the rods and lead screw also would be good.
Make the connections as below. Supply a different connection to the H bridge with enough current. Remember to share GND with all the modules. The End stop swithc will need a pullup and give a low pulse when the siwthc is pressed, otherwise you will need to change that in the code. The encoder needs 5V, GND and two encoder outputs.
Copy the code from below or download it from the link. You will need to install the needed libraries. Go to the link below and find those libraries and install them to your Arduino IDE. Read all the lines in the code to understand more. The setpoint is given with the potentiometer but you could use Serial read for example and give precise values for the setpoint and see the results.
//Include the libraries we need
#include <PinChangeInterrupt.h>
#include <PinChangeInterruptBoards.h>
#include <PinChangeInterruptPins.h>
#include <PinChangeInterruptSettings.h>
#include <TimerOne.h> // Timer one interrupts
//////////////////////////////////////////////////////////////
/////////////////////DEFINE PID constants/////////////////////
//////////////////////////////////////////////////////////////
int kp = 2, ki = 0.01, kd = 0.02;
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
int input, output, setpoint;
int iTerm = 0, lastInput = 0, dInput = 0, error = 0;
int outMin = -255, outMax = 255;
int sampleTime = 10; //This values is is milliseconds
volatile long encoderPos = 0;
//////////////////////////////////////////////////////////////
////////////////////Define the pins we use////////////////////
//////////////////////////////////////////////////////////////
#define Encoder_A 2 // Quadrature encoder A pin
#define Encoder_B 8 // Quadrature encoder B pin
#define Motor_CW 11 // PWM outputs to L298N H bridge motor driver module
#define Motor_CCW 3
#define led 13
#define END_stop 4
void setup(void)
{
pinMode(Encoder_A, INPUT); // quadrature encoder input A
pinMode(Encoder_B, INPUT); // quadrature encoder input B
pinMode(END_stop, INPUT); // Input from the end stop switch
pinMode(led, OUTPUT);
attachPCINT(digitalPinToPCINT(Encoder_A), encoder, FALLING); // We update encoder position each falling edge detected in the interrumption
TCCR2B = TCCR2B & 0b11111000 | 1; // set 31Kh PWM to prevent motor whine (timer 2)
Timer1.initialize(sampleTime * 1000); // setup timer 1
Timer1.attachInterrupt(Compute);
Serial.begin(115200); //Just for debugging if you want to print values
while(digitalRead(END_stop))
{
analogWrite(Motor_CW, 0); // Rotate the motor CCW
analogWrite(Motor_CCW, 255);
}
analogWrite(Motor_CW, 0); // Stop the motor
analogWrite(Motor_CCW, 0);
}
void Compute()
{
setpoint = map(analogRead(0),0,1024,1024,0) * 110; // setpoint position is made with a potentiometer but could be given by serial monitor or other...
input = encoderPos; // we get the data from the encoder interrumption
error = setpoint - input;
iTerm += ki * error * sampleTime;
if (iTerm > outMax) iTerm = outMax; // prevent that the I term from PID gets too big
else if (iTerm < outMin) iTerm = outMin;
dInput = (input - lastInput) / sampleTime;
output = kp * error + iTerm - kd * dInput; // The PID output is the sum of P I and D values
if (output > outMax) output = outMax; // limit output to 0 and 255 for the analog write
else if (output < outMin) output = outMin;
lastInput = input; //Remember to save the last input value for the next loop
pwmOut(output); //Change the analog write for the motor control
}
void pwmOut(int out) { // to H-Bridge board
if (out > 0) {
analogWrite(Motor_CW, out); // Rotate the motor CW
analogWrite(Motor_CCW, 0);
}
else {
analogWrite(Motor_CW, 0);
analogWrite(Motor_CCW, abs(out)); // Rotate the motor CCW
}
}
void encoder() { // pulse and direction, direct port reading to save cycles
if (PINB & 0b00000001) encoderPos++; // if(digitalRead(encodPinB1)==HIGH) count ++; //We increase steps by 1
else encoderPos--; // if (digitalRead(encodPinB1)==LOW) count --; //We decrease steps by 1
}
void loop(void)
{
digitalWrite(led, !digitalRead(led)); // blink led or do something else...
delay(200);
}
See the video for more information and see what options you have for the encoder, how to detect the pulses and so on. I hope you will like this project. Comment below and help the community. COnsider supporting me on PATREON.