Prerequisites
If not already done so already, install Arduino software and make basic connections, as described in page for MRMS ESP32: Arduino, IMU, eFuse, BT, WiFi, CAN Bus.
Task
PWM control is a native method for almost any motor driver. It is implemented by using a (full) H bridge, which accepts PWM signal. Some local logic is needed in order to avoid problems, such as shoot through, when low- and high-side FETs are open. So, the simplest motor controller boards accept PWM signals. Either 2 PWM (PWM/PWM), or a PWM and one digital (PWM/DIR). ML-R offers controllers of the both types. PWM/PWM is a little more flexible but it demands 2 times more PWM pins which may be scarce. In most aspects they are similar. Here we will make a program which uses such a simple PWM/PWM controller.
Connections
Starting from basic connection, enhance the setup with ML-R Motor Driver 4x3.6A B:
In the picture there are no motors, but you may want to attach some to the green output connectors of the motor board.
Program
The program will drive all the 4 motors, one by one, first gradually to maximum rotation, then gradually to maximum opposite rotation, and in the end towards stop.
#define GPIO_A1 32
#define GPIO_A2 33
#define GPIO_B1 25
#define GPIO_B2 26
#define GPIO_C1 27
#define GPIO_C2 14
#define GPIO_D1 12
#define GPIO_D2 13
#define FREQUENCY_HZ 500
#define RESOLUTION_BITS 7 // 1 - 16
#define MAXIMUM_DUTY_CYCLE ((1 << RESOLUTION_BITS) - 1)
uint8_t pins[][2] = {{GPIO_A1, GPIO_A2}, {GPIO_B1, GPIO_B2}, {GPIO_C1, GPIO_C2}, {GPIO_D1, GPIO_D2}};
void setSpeed(uint8_t motor, int8_t speed){
ledcWrite(2 * motor, speed > 0 ? speed : 0);
ledcWrite(2 * motor + 1, speed < 0 ? -speed : 0);
}
void setup() {
for (uint8_t motor = 0; motor < 4; motor++){
ledcSetup(2 * motor, FREQUENCY_HZ, RESOLUTION_BITS);
ledcSetup(2 * motor + 1, FREQUENCY_HZ, RESOLUTION_BITS);
ledcAttachPin(pins[motor][0], 2 * motor);
ledcAttachPin(pins[motor][1], 2 * motor + 1);
}
}
void loop() {
for (uint16_t motor = 0; motor < 4; motor++){
int8_t step = 1;
for (int8_t speed = 1; !(speed == 0 && step == 1); speed += step){
setSpeed(motor, speed);
delay(30);
if (abs(speed) == MAXIMUM_DUTY_CYCLE)
step = -step;
}
}
}
First section defines GPIO pins as in the picture. A1 and A2 are 2 pins for the first motor, B2 and B2 for the second, etc. Frequency, FREQUENCY_HZ, will be 500 Hz. Original Arduino boards use frequencies from around 500 till 1000 Hz. 500 is more than enough because the motor has a high rotational inertia and it is not possible to change speed more than 500 times per second. RESOLUTION_BITS 7 defines speed range between -127 (=27-1) and 127. It defines MAXIMUM_DUTY_CYCLE as 127. Bit shifting 1 will do the math for us.
Next is defined an array consisting of GPIOs defined above. It is easier to use an array then a list of constants because we can iterate the array.
Function setSpeed(motor, speed) sets speed speed for motor number motor. speed must be in range -127 to 127, motor 0 - 3. No input values validation is done anywhere in our examples to keep them verbatim. Doing no input parameter checking is a big no-no for a production code. PWM/PWM motor drivers usually accept 0 V on pin 1 and pin 2 expects a variable PWM signal width in order to spin the motor in one direction. To spin it in the other direction, pin 1 accepts variable signal and pin 2 is at 0 V.
setup() iterates 4 motors, 2 pins each, and sets PWM configuration, according to our selected constants, in the first 2 loop's commands. They use ledcSetup() functions, which accept channel number as first argument. Here we just choose increasing integers, from 0 up. They have to be different as every pin will use its own PWM signal. The last 2 use ledcAttachPin() function to attach PWM signal to selected pins.
loop() is a standard C code so there are no new things to learn about the hardware here. 2 nested loops and the inner one sets motor's speed.
Limitations
Some pins are not available for PWM output. Check this list.