Example Code for Arduino-PWM mode drive DC motor with encoder
This article offers a comprehensive guide to driving a DC motor with an encoder using Arduino's PWM mode. It includes detailed hardware and software preparation, a truth table for understanding PWM control modes, and sample code to control motor rotation speed using PID. The setup uses Romeo ESP32-S3 and TT Motor with Encoder, and the code initializes PWM, sets PID parameters, and reads encoder data to adjust motor speed. Ideal for Arduino enthusiasts looking to optimize motor control.
Hardware Preparation
Software Preparation
- Download Arduino IDE: Click to download Arduino IDE
- Add ESP32 board to Arduino IDE
PWM mode drives the motor
Truth table of PWM control mode
| IN1 | IN2 | OUT1 | OUT2 | explain |
|---|---|---|---|---|
| 0 | 0 | Hi-Z | Hi-Z | Taxiing (H-bridge high impedance) |
| 0 | 1 | L | H | retreat (OUT2 → OUT1) |
| 1 | 0 | H | L | advance (OUT1 → OUT2) |
| 1 | 1 | L | L | Braking (low side slow attenuation) |
Sample Code
The motor rotation speed is controlled by PID
/**
*@brief motorSpeeddetectionbyPWMwithencoder.ino
*@ Motor driver in PWM mode (with encoder)
*/
#include "driver/mcpwm.h"
#include "soc/mcpwm_struct.h"
#include "soc/mcpwm_reg.h"
#include <PID_v1.h>
//IO12->M1_IN1
//IO13->M1_IN2
#define MOTOR_PWM_IN1 12
#define MOTOR_PWM_IN2 13
const byte encoder0pinA = 3;//A pin -> the interrupt pin 3
const byte encoder0pinB = 4;//B pin -> the digital pin 4
byte encoder0PinALast;
double duration,abs_duration;//the number of the pulses
boolean Direction;//the rotation direction
boolean result;
double val_output;//Used to provide PWM power values to the motor.
double Setpoint;
double Kp=0.6, Ki=5, Kd=0;
PID myPID(&abs_duration, &val_output, &Setpoint, Kp, Ki, Kd, DIRECT);
//Initialize the pins needed to generate the PWM signal
void mcpwm_init(void)
{
//The binding needs to output the PWM pin to the PWM channel
mcpwm_gpio_init(MCPWM_UNIT_0,MCPWM0A,MOTOR_PWM_IN1);
mcpwm_gpio_init(MCPWM_UNIT_0,MCPWM0B,MOTOR_PWM_IN2);
//Configure mcpwm information
mcpwm_config_t pwm_config;
pwm_config.frequency = 1000;/*!<Set frequency of MCPWM in Hz*/
pwm_config.cmpr_a = 0;/*!<Set % duty cycle for operator a(MCPWMXA), i.e for 62.3% duty cycle, duty_a = 62.3*/
pwm_config.cmpr_b = 0;/*!<Set % duty cycle for operator b(MCPWMXB), i.e for 48% duty cycle, duty_b = 48.0*/
pwm_config.counter_mode/*!<Set type of MCPWM counter*/ = MCPWM_UP_COUNTER/*!<For asymmetric MCPWM*/;
pwm_config.duty_mode/*!<Set type of duty cycle*/ = MCPWM_DUTY_MODE_0/*!<Active high duty, i.e. duty cycle proportional to high time for asymmetric MCPWM*/;
// Initializes a unit of mcpwm and binds the clock
mcpwm_init(MCPWM_UNIT_0,MCPWM_TIMER_0,&pwm_config);
}
void advance(/*range:0~100*/uint8_t speed)
{
mcpwm_set_duty_type(MCPWM_UNIT_0,MCPWM_TIMER_0,MCPWM_GEN_A,MCPWM_DUTY_MODE_0);
mcpwm_set_signal_low(MCPWM_UNIT_0,MCPWM_TIMER_0,MCPWM_GEN_B);
mcpwm_set_duty(MCPWM_UNIT_0,MCPWM_TIMER_0,MCPWM_GEN_A,speed);
}
void retreat(/*range:0~100*/uint8_t speed)
{
//Give MOTOR_DIRECTION_PIN a continuous low level
mcpwm_set_signal_low(MCPWM_UNIT_0,MCPWM_TIMER_0,MCPWM_GEN_A);
/*
* Call function mcpwm_set_duty_type() every time after mcpwm_set_signal_high()
* or mcpwm_set_signal_low() to resume with previously set duty cycle.
*/
mcpwm_set_duty_type(MCPWM_UNIT_0,MCPWM_TIMER_0,MCPWM_GEN_B,MCPWM_DUTY_MODE_0);
//The MOTOR_STEP_PIN pin outputs a PWM wave with a duty cycle of "speed"
mcpwm_set_duty(MCPWM_UNIT_0,MCPWM_TIMER_0,MCPWM_GEN_B,speed);
}
void stop(void)
{
mcpwm_set_signal_low(MCPWM_UNIT_0,MCPWM_TIMER_0,MCPWM_GEN_A);
mcpwm_set_signal_low(MCPWM_UNIT_0,MCPWM_TIMER_0,MCPWM_GEN_B);
}
void breake(void)
{
mcpwm_set_signal_high(MCPWM_UNIT_0,MCPWM_TIMER_0,MCPWM_GEN_A);
mcpwm_set_signal_high(MCPWM_UNIT_0,MCPWM_TIMER_0,MCPWM_GEN_B);
}
void wheelSpeed()
{
int Lstate = digitalRead(encoder0pinA);
if((encoder0PinALast == LOW) && Lstate==HIGH)
{
int val = digitalRead(encoder0pinB);
if(val == LOW && Direction)
{
Direction = false; //Reverse
}
else if(val == HIGH && !Direction)
{
Direction = true; //Forward
}
}
encoder0PinALast = Lstate;
if(!Direction)
duration++;
else
duration--;
}
void EncoderInit()
{
Direction = true;//default -> Forward
pinMode(encoder0pinB,INPUT);
attachInterrupt(digitalPinToInterrupt(encoder0pinA), wheelSpeed, CHANGE);
}
void setup() {
Serial.begin(115200);//Initialize the serial port
mcpwm_init();
advance(70);
Setpoint =80; //Set the output value of PID
myPID.SetMode(AUTOMATIC);//Set PID to automatic mode
myPID.SetSampleTime(100);//Set the PID sampling frequency to 100ms
EncoderInit();//Initialize the module
}
void loop() {
abs_duration=abs(duration);
result=myPID.Compute();//The return value of PID conversion is 1
if(result)
{
Serial.print("Pluse: ");
Serial.println(duration);
duration = 0; //Count clear and wait for the next count
}
}
Was this article helpful?
