HIDE NAV

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);
}