Example Code for Arduino-EN/PH mode drive DC motor with encoder
This article offers a comprehensive guide and example code for controlling a DC motor with an encoder using Arduino. It covers hardware preparation with Romeo ESP32-S3 and TT Motor, software setup including Arduino IDE, and explains motor control using the PH/EN mode truth table and PID speed control.
Hardware Preparation
Software Preparation
- Download Arduino IDE: Click to download Arduino IDE
- Add ESP32 board to Arduino IDE
EN/PH mode drives the motor
**PH/EN Control mode Truth Table **
| EN | PH | OUT1 | OUT2 | Explanation |
|---|---|---|---|---|
| 0 | X | L | L | Braking (low side slow attenuation) |
| 1 | 0 | L | H | retreat (OUT2 → OUT1) |
| 1 | 1 | H | L | advance (OUT1 → OUT2) |
Sample Code
The motor rotation speed is controlled by PID
/**
*@brief motorSpeeddetectionbyPH_ENwithencoder.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_STEP_PIN 12
#define MOTOR_DIRECTION_PIN 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)
{
/**
* @brief This function initializes each gpio signal for MCPWM
* @note
* This function initializes one gpio at a time.
*
* @param mcpwm_num set MCPWM unit(0-1)
* @param io_signal set MCPWM signals, each MCPWM unit has 6 output(MCPWMXA, MCPWMXB) and 9 input(SYNC_X, FAULT_X, CAP_X)
* 'X' is timer_num(0-2)
* @param gpio_num set this to configure gpio for MCPWM, if you want to use gpio16, gpio_num = 16
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
mcpwm_gpio_init(MCPWM_UNIT_0,MCPWM0A,MOTOR_STEP_PIN);//The binding needs to output the PWM pin to the PWM channel
mcpwm_gpio_init(MCPWM_UNIT_0,MCPWM0B,MOTOR_DIRECTION_PIN);
//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*/;
/**
* @brief Initialize MCPWM parameters
*
* @param mcpwm_num set MCPWM unit(0-1)
* @param timer_num set timer number(0-2) of MCPWM, each MCPWM unit has 3 timers.
* @param mcpwm_conf configure structure mcpwm_config_t
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
mcpwm_init(MCPWM_UNIT_0,MCPWM_TIMER_0,&pwm_config);//Initializes one of the mcpwm units and binds the clock
}
void advance(/*range:0~100*/uint8_t speed)
{
/**
* @brief Use this function to set MCPWM signal high
*
* @param mcpwm_num set MCPWM unit(0-1)
* @param timer_num set timer number(0-2) of MCPWM, each MCPWM unit has 3 timers
* @param gen set the operator(MCPWMXA/MCPWMXB), 'x' is timer number selected
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
mcpwm_set_signal_high(MCPWM_UNIT_0,MCPWM_TIMER_0,MCPWM_GEN_B);//Give MOTOR_STEP_PIN a continuous high level
/**
* @brief Set duty either active high or active low(out of phase/inverted)
* @note
* Call this function every time after mcpwm_set_signal_high or mcpwm_set_signal_low to resume with previously set duty cycle
*
* @param mcpwm_num set MCPWM unit(0-1)
* @param timer_num set timer number(0-2) of MCPWM, each MCPWM unit has 3 timers
* @param gen set the generator(MCPWMXA/MCPWMXB), 'x' is operator number selected
* @param duty_type set active low or active high duty type
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
mcpwm_set_duty_type(MCPWM_UNIT_0,MCPWM_TIMER_0,MCPWM_GEN_A,MCPWM_DUTY_MODE_0);
/**
* @brief Set duty cycle of each operator(MCPWMXA/MCPWMXB)
*
* @param mcpwm_num set MCPWM unit(0-1)
* @param timer_num set timer number(0-2) of MCPWM, each MCPWM unit has 3 timers
* @param gen set the generator(MCPWMXA/MCPWMXB), 'X' is operator number selected
* @param duty set duty cycle in %(i.e for 62.3% duty cycle, duty = 62.3) of each operator
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
mcpwm_set_duty(MCPWM_UNIT_0,MCPWM_TIMER_0,MCPWM_GEN_A,speed);//The MOTOR_DIRECTION_PIN pin outputs a PWM wave with a duty cycle of "speed"
}
void retreat(/*range:0~100*/uint8_t speed)
{
//Give MOTOR_STEP_PIN a continuous low level
mcpwm_set_signal_low(MCPWM_UNIT_0,MCPWM_TIMER_0,MCPWM_GEN_B);
//The MOTOR_DIRECTION_PIN pin outputs a PWM wave with a duty cycle of "speed"
mcpwm_set_duty_type(MCPWM_UNIT_0,MCPWM_TIMER_0,MCPWM_GEN_A,MCPWM_DUTY_MODE_0);
mcpwm_set_duty(MCPWM_UNIT_0,MCPWM_TIMER_0,MCPWM_GEN_A,speed);
}
void breake(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 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(9600);//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?
