Microcontroller Unit Lab 2
H-Bridge Motor Controller
with Optional Pulse Width Modulation Speed Control
Summary
Build a dc motor control device with reversible direction and speed control.
Connections
- One H-Bridge with one DC Motor connected
* There should be a slider switch to turn off the H-Bridge only for USB programming
NOTE: Deactivate H-Bridge during Programming, Use a Bench Power Supply when testing an actual motor!
- Three Buttons - Active Low (each with Pull Up Resistor)
* mode: cycles STOP - FORWARD - STOP – REVERSE
* speed up: increments speed up only when in forward or reverse, caps at 100% duty cycle
* speed down: decrements speed only when in forward or reverse, has a lower limit that still rotates the motor
Required Equipment and Supplies
- 5v Brushed DC Motor
- L9110 Dual H-Bridge PCB Module datasheet
- Slider Switch
- Momentary Switch (Push Button) [x3]
- 15kΩ Resistor [x3]
- Rasperry Pi Pico with Headers
- USB Micro Cable
- Breadboard
- Cables and 22ga wire as needed
- Bench-top DC Power Supply
Wiring
solution to be posted in following classes

Code
Part 1 - Just the H-Bridge is connected (no buttons) and the motor spins forward and backward in a CPU timed sequence.
main.c
#include "pico/stdlib.h"
#define FWD_PIN 14
#define REV_PIN 15
#define DELAY 500
int main() {
// setup & initialize
gpio_init(FWD_PIN);
gpio_set_dir(FWD_PIN, GPIO_OUT);
gpio_init(REV_PIN);
gpio_set_dir(REV_PIN, GPIO_OUT);
gpio_put(FWD_PIN,0);
gpio_put(REV_PIN,0);
// primary loop
while (true) {
// off
gpio_put(FWD_PIN,0);
gpio_put(REV_PIN,0);
sleep_ms(DELAY);
// fwd
gpio_put(FWD_PIN,1);
gpio_put(REV_PIN,0);
sleep_ms(DELAY);
// off
gpio_put(FWD_PIN,0);
gpio_put(REV_PIN,0);
sleep_ms(DELAY);
//rev
gpio_put(FWD_PIN,0);
gpio_put(REV_PIN,1);
sleep_ms(DELAY);
}
}
Part II - A single button is added to switch modes.
main.c
#include "pico/stdlib.h"
#define FWD_PIN 14
#define REV_PIN 15
#define BTN_MODE 18
#define DELAY 500
int mode = 0; // 0 - 3, 4 states
bool do_debounce_delay = false;
int main() {
// setup & initialize
gpio_init(FWD_PIN);
gpio_set_dir(FWD_PIN, GPIO_OUT);
gpio_init(REV_PIN);
gpio_set_dir(REV_PIN, GPIO_OUT);
gpio_init(BTN_MODE);
gpio_set_dir(BTN_MODE, GPIO_IN);
gpio_put(FWD_PIN,0);
gpio_put(REV_PIN,0);
// primary loop
while (true) {
//check buttons
if(gpio_get(BTN_MODE) == 0){
mode++;
if(mode > 3) mode = 0;
do_debounce_delay = true;
}
//update motor
switch (mode){
case 0:
// off
gpio_put(FWD_PIN,0);
gpio_put(REV_PIN,0);
break;
case 1:
//fwd
gpio_put(FWD_PIN,1);
gpio_put(REV_PIN,0);
break;
case 2:
// off
gpio_put(FWD_PIN,0);
gpio_put(REV_PIN,0);
break;
case 3:
//rev
gpio_put(FWD_PIN,0);
gpio_put(REV_PIN,1);
break;
}
if(do_debounce_delay) {
sleep_ms(100);
do_debounce_delay = false;
}
}
}
Part III - Two more buttons are added to speed up and down.
Speed is controlled by software based Pulse Width Modulation.
main.c
#include "pico/stdlib.h"
const uint fwd_pin = 14;
const uint rev_pin = 15;
const uint btn_mode = 18;
const uint btn_spd_up = 17;
const uint btn_spd_dn = 16;
bool pwm_on_state = false;
const uint pwm_period = 1000;
uint pwm_on_delay = 750, pwm_off_delay = 250, pwm_delay_increment = 50;
#define DELAY 500
int mode = 0; // 0 - 3, 4 states
bool do_debounce_delay = false;
int main() {
// setup & initialize
gpio_init(fwd_pin);
gpio_set_dir(fwd_pin, GPIO_OUT);
gpio_init(rev_pin);
gpio_set_dir(rev_pin, GPIO_OUT);
gpio_init(btn_mode);
gpio_set_dir(btn_mode, GPIO_IN);
gpio_init(btn_spd_up);
gpio_set_dir(btn_spd_up, GPIO_IN);
gpio_init(btn_spd_dn);
gpio_set_dir(btn_spd_dn, GPIO_IN);
/// make sure motor is totally off at beginning...
gpio_put(fwd_pin,0);
gpio_put(rev_pin,0);
// primary loop
while (true) {
//check mode button
if(gpio_get(btn_mode) == 0){
mode++;
if(mode > 3) mode = 0;
do_debounce_delay = true;
}
if(gpio_get(btn_spd_up) == 0){
if(pwm_on_delay < pwm_period){
pwm_on_delay += pwm_delay_increment;
pwm_off_delay -= pwm_delay_increment;
do_debounce_delay = true;
}
}
if(gpio_get(btn_spd_dn) == 0){
if(pwm_on_delay > pwm_delay_increment){
pwm_on_delay -= pwm_delay_increment;
pwm_off_delay += pwm_delay_increment;
do_debounce_delay = true;
}
}
//update motor
switch (mode){
case 0:
// off
gpio_put(fwd_pin,0);
gpio_put(rev_pin,0);
break;
case 1:
//fwd
gpio_put(fwd_pin,pwm_on_state);
gpio_put(rev_pin,0);
break;
case 2:
// off
gpio_put(fwd_pin,0);
gpio_put(rev_pin,0);
break;
case 3:
//rev
gpio_put(fwd_pin,0);
gpio_put(rev_pin,pwm_on_state);
break;
}
//pwm delay
if(pwm_on_state == true) {
sleep_us(pwm_on_delay);
pwm_on_state = false;
} else {
sleep_us(pwm_off_delay);
pwm_on_state = true;
}
//switch debounce delay
if(do_debounce_delay) {
sleep_ms(100);
do_debounce_delay = false;
}
}
}
Part IV - Speed is now controlled by A Hardware PWM Module to generate the Pulse Width Modulation signal.
- Edit your CMakeLists.txt to add hardware_pwm
totarget_link_libraries()
.
- Complete the following code where TODO comments indicate
main.c
#include "pico/stdlib.h"
// NOTE: you need to add hardware_pwm to cmake target_link_libraries for the pwm include
#include "hardware/pwm.h"
///////////////////////////////////////////
//TODO add GPIO Port Numbers for Each Item
///////////////////////////////////////////
const uint MOTOR1_FWD = ;
const uint MOTOR1_REV = ;
const uint BTN_MODE = ;
const uint BTN_SPEEDUP = ;
const uint BTN_SPEEDDN = ;
const uint LED_BUILTIN = ;
//partially controls PWM freqency with other settings
//this is approx 20kHz
const uint16_t PWM_WRAP_COUNT = 6000;
// use this to trigger a time delay if a button is pressed
bool button_debounce_delay = false;
// state variables
int mode = 0;
float speed = 0.0f;
const float SPEED_INCREMENT = 0.025f;
const float SPEED_LOWER_LIMIT = 0.7f; // lower limit should be able to start rotation
const float SPEED_UPPER_LIMIT = 1.00f;
//Function Prototypes
//initialization
void setup_buttons(); //TODO - student coded below
void setup_pwm(); // complete, given here
void check_buttons(); //TODO - student coded below
void motor1_stop_pwm(); // complete, given here
void motor1_forward_pwm(float duty); // duty 0.0 -> 1.0 complete, given here
void motor1_reverse_pwm(float duty); // duty 0.0 -> 1.0. complete, given here
void main() {
////////////////////////////////////////////////////////////////////
//TODO: student codes initialization routine.
// call setup functions and blink onboard light for okay status
////////////////////////////////////////////////////////////////////
while (true) {
////////////////////////////////////////////
//TODO: student codes logic for main loop
////////////////////////////////////////////
//switch debounce - feel free to use this by setting the button_debounce to true
if(button_debounce_delay == true){
sleep_ms(200);
button_debounce_delay = false;
}
}
}
void setup_buttons(){
////////////////////////////////////////////
//TODO: student codes setup for gpio pin
// you can also setup the built-in led here
////////////////////////////////////////////
}
//complete - sets pwm function and frequency for h-bridge pins
void setup_pwm(){
gpio_set_function(MOTOR1_FWD, GPIO_FUNC_PWM);
uint pwm_fwd_slice = pwm_gpio_to_slice_num(MOTOR1_FWD);
pwm_set_enabled(pwm_fwd_slice, true);
pwm_set_wrap(pwm_fwd_slice, PWM_WRAP_COUNT);
gpio_set_function(MOTOR1_REV, GPIO_FUNC_PWM);
uint pwm_rev_slice = pwm_gpio_to_slice_num(MOTOR1_REV);
pwm_set_enabled(pwm_rev_slice, true);
pwm_set_wrap(pwm_rev_slice, PWM_WRAP_COUNT);
}
void check_buttons(){
////////////////////////////////////////////
//TODO: student codes logic to check if buttons are pressed
// try to also use the debounce variable to trigger a debounce sleep timer
////////////////////////////////////////////
}
//complete
void motor1_stop_pwm(){
pwm_set_gpio_level(MOTOR1_FWD, 0);
pwm_set_gpio_level(MOTOR1_REV, 0);
}
//complete
void motor1_forward_pwm(float duty){
if(duty > 1.0f) duty = 1.0f;
if(duty < 0.0f) duty = 0.0f;
uint level = PWM_WRAP_COUNT * duty;
pwm_set_gpio_level(MOTOR1_REV, 0);
pwm_set_gpio_level(MOTOR1_FWD, level);
}
//complete
void motor1_reverse_pwm(float duty){
if(duty > 1.0f) duty = 1.0f;
if(duty < 0.0f) duty = 0.0f;
uint level = PWM_WRAP_COUNT * duty;
pwm_set_gpio_level(MOTOR1_FWD, 0);
pwm_set_gpio_level(MOTOR1_REV, level);
}