11. Output Devices¶
This week I worked on defining my final project idea and started to getting used to the documentation process.
All final code files can be found here
Stepper Motor¶
I used a NEMA 17HS4401S stepper motor and a L298N motor driver.
Arduino Test¶
I first did a test with an arduino before milling a board to make sure I can get it working.
First, I tried to directly manipulate the stepper motor without the arduino library since I remember trying to use the stepper motor library and failing a couple years ago. I used this site with the following diagram to get the phases of the stepper motor.
To wire the stepper motor correctly, I used diagrams from these sites: https://blog.moonsindustries.com/2018/12/19/how-to-connect-stepper-motor-to-your-drive/, and https://datasheetspdf.com/pdf/1310364/Handson/17HS4401S/1.
I connected the black, green, red, and blue wires of the stepper motor to outputs 1, 2, 3, and 4 of the motor driver respectively. I connected pins 0, 1, 2, and 3 of the arduino mega into inputs 1, 2, 3, and 4 of the motor driver respectively. Lastly, I connected a 5 volt 10 amp power source into the power of the motor driver and shared a ground with the arduino.
I uploaded the following code controlling the stepper motor directly without any libraries, but it didn’t work and just continuously did 1 step forward then 1 step back.
void setup() {
// put your setup code here, to run once:
for (int i = 0; i < 4; ++i) {
pinMode(i, OUTPUT);
}
Serial.begin(9600);
}
int stepPos = 0;
void loop() {
// put your main code here, to run repeatedly:
for (int i = 0; i < 50; ++i) {
stepForward(stepPos, 0, 1, 2, 3);
}
delay(1000);
for (int i = 0; i < 50; ++i) {
stepBack(stepPos, 0, 1, 2, 3);
//Serial.println(stepPos);
}
delay(1000);
}
void stepForward(int& stepPos, int pin1, int pin2, int pin3, int pin4) {
stepPos = (stepPos + 1) % 4;
switch(stepPos) {
case 0:
digitalWrite(pin1, HIGH);
digitalWrite(pin2, LOW);
digitalWrite(pin3, HIGH);
digitalWrite(pin4, LOW);
break;
case 1:
digitalWrite(pin1, LOW);
digitalWrite(pin2, HIGH);
digitalWrite(pin3, HIGH);
digitalWrite(pin4, LOW);
break;
case 2:
digitalWrite(pin1, LOW);
digitalWrite(pin2, HIGH);
digitalWrite(pin3, LOW);
digitalWrite(pin4, HIGH);
break;
case 3:
digitalWrite(pin1, HIGH);
digitalWrite(pin2, LOW);
digitalWrite(pin3, LOW);
digitalWrite(pin4, HIGH);
break;
}
delay(100);
}
void stepBack(int& stepPos, int pin1, int pin2, int pin3, int pin4) {
--stepPos;
if (stepPos < 0) stepPos += 4;
switch(stepPos) {
case 0:
digitalWrite(pin1, HIGH);
digitalWrite(pin2, LOW);
digitalWrite(pin3, HIGH);
digitalWrite(pin4, LOW);
break;
case 1:
digitalWrite(pin1, LOW);
digitalWrite(pin2, HIGH);
digitalWrite(pin3, HIGH);
digitalWrite(pin4, LOW);
break;
case 2:
digitalWrite(pin1, LOW);
digitalWrite(pin2, HIGH);
digitalWrite(pin3, LOW);
digitalWrite(pin4, HIGH);
break;
case 3:
digitalWrite(pin1, HIGH);
digitalWrite(pin2, LOW);
digitalWrite(pin3, LOW);
digitalWrite(pin4, HIGH);
break;
}
delay(100);
}
I then tried to use the library with the following code:
#include <Stepper.h>
Stepper myStepper(200, 0, 1, 2, 3);
void setup() {
// put your setup code here, to run once:
myStepper.setSpeed(60);
}
void loop() {
// put your main code here, to run repeatedly:
myStepper.step(200);
delay(1000);
myStepper.step(-200);
delay(1000);
}
After uploading, it did work.
Upon getting the code with the library working, I went back to writing to the stepper motor without the library, and I tried changing the pins I used to PWM pins, and it worked.
Attiny1614¶
Kicad Design¶
I started off with the schematic, and since I just needed to connect it to the motor driver with jumper wires, I just needed to have some header pins. I made a section of 3 pins for the programmer as well as a section of 3 connected pins for sharing a ground. To allow this board to be used more for other things, I added additional pins so that I could use this board for other things and for testing. To prevent the electrical rules checker from giving errors, I added power flags and made all the unused pins of the 1614 disconnected and invisible in the symbols editor.
I then loaded it into the PCB editor and ended up with the following design after rounding the edges of the edge cut.
After exporting the gerbers, I pulled them up in the gerber view to make sure everything looked right.
I then milled it and soldered the components on it.
I uploaded the following code written with baremetal to control the stepper motor. I used function pointers as parameters since the stepper was using pins from different ports, so I couldn’t just pass a register and different shifts for the pins.
#define STEPPERARGS stepPos, &pin1out, &pin1clr, &pin2out, &pin2clr, &pin3out, &pin3clr, &pin4out, &pin4clr
int stepPos = 0;
void setup() {
// put your setup code here, to run once:
PORTA.DIRSET |= 1 << 4;
PORTA.DIRSET |= 1 << 5;
PORTA.DIRSET |= 1 << 3;
PORTB.DIRSET |= 1 << 2;
}
void loop() {
// put your main code here, to run repeatedly:
for (int i = 0; i < 200; ++i) {
stepCW(STEPPERARGS);
}
delay(1000);
for (int i = 0; i < 200; ++i) {
stepCCW(STEPPERARGS);
}
delay(1000);
}
void pin1out() {
PORTA.OUT |= 1 << 4;
}
void pin1clr() {
PORTA.OUT &= ~(1 << 4);
}
void pin2out() {
PORTA.OUT |= 1 << 5;
}
void pin2clr() {
PORTA.OUT &= ~(1 << 5);
}
void pin3out() {
PORTB.OUT |= 1 << 2;
}
void pin3clr() {
PORTB.OUT &= ~(1 << 2);
}
void pin4out() {
PORTA.OUT |= 1 << 3;
}
void pin4clr() {
PORTA.OUT &= ~(1 << 3);
}
void stepCW(int& stepPos, void (*out1)(), void (*clear1)(), void (*out2)(), void (*clear2)(), void (*out3)(), void (*clear3)(), void (*out4)(), void (*clear4)()) {
stepPos = (stepPos + 1) % 4;
switch (stepPos) {
case 0:
out1();
clear2();
out3();
clear4();
break;
case 1:
clear1();
out2();
out3();
clear4();
break;
case 2:
clear1();
out2();
clear3();
out4();
break;
case 3:
out1();
clear2();
clear3();
out4();
break;
}
delay(10);
}
void stepCCW(int& stepPos, void (*out1)(), void (*clear1)(), void (*out2)(), void (*clear2)(), void (*out3)(), void (*clear3)(), void (*out4)(), void (*clear4)()) {
--stepPos;
if (stepPos < 0) stepPos = 3;
switch (stepPos) {
case 0:
out1();
clear2();
out3();
clear4();
break;
case 1:
clear1();
out2();
out3();
clear4();
break;
case 2:
clear1();
out2();
clear3();
out4();
break;
case 3:
out1();
clear2();
clear3();
out4();
break;
}
delay(10);
}
I had it do a full revolution back and forth, and it worked.
My teacher, Mr. Durrett, recommended that I put this all in a class instead to make the code make more sense and eliminate all the function pointers, and so I ended up with the following code.
class Stepper {
private:
int pin1shift, pin2shift, pin3shift, pin4shift;
volatile byte* reg1;
volatile byte* reg2;
volatile byte* reg3;
volatile byte* reg4;
int stepPos;
int delayTime;
public:
Stepper(volatile byte* r1, int p1, volatile byte* r2, int p2, volatile byte* r3, int p3, volatile byte* r4, int p4, int del): pin1shift(p1), reg1(r1), pin2shift(p2), reg2(r2), pin3shift(p3), reg3(r3), pin4shift(p4), reg4(r4), delayTime(del) {
stepPos = 0;
}
private:
void out1() {
*reg1 |= 1 << pin1shift;
}
void clear1() {
*reg1 &= ~(1 << pin1shift);
}
void out2() {
*reg2 |= 1 << pin2shift;
}
void clear2() {
*reg2 &= ~(1 << pin2shift);
}
void out3() {
*reg3 |= 1 << pin3shift;
}
void clear3() {
*reg3 &= ~(1 << pin3shift);
}
void out4() {
*reg4 |= 1 << pin4shift;
}
void clear4() {
*reg4 &= ~(1 << pin4shift);
}
void runStep() {
switch (stepPos) {
case 0:
out1();
clear2();
out3();
clear4();
break;
case 1:
clear1();
out2();
out3();
clear4();
break;
case 2:
clear1();
out2();
clear3();
out4();
break;
case 3:
out1();
clear2();
clear3();
out4();
break;
}
delay(delayTime);
}
public:
void stepCW(int numSteps) {
for (int i = 0; i < numSteps; ++i) {
stepPos = (stepPos + 1) % 4;
runStep();
}
}
void stepCCW(int numSteps) {
for (int i = 0; i < numSteps; ++i) {
--stepPos;
if (stepPos < 0) stepPos = 3;
runStep();
}
}
};
void setup() {
// put your setup code here, to run once:
PORTA.DIRSET |= 1 << 4;
PORTA.DIRSET |= 1 << 5;
PORTA.DIRSET |= 1 << 3;
PORTB.DIRSET |= 1 << 2;
}
Stepper myStepper(&PORTA.OUT, 4, &PORTA.OUT, 5, &PORTB.OUT, 2, &PORTA.OUT, 3, 10);
void loop() {
// put your main code here, to run repeatedly:
myStepper.stepCW(50);
delay(1000);
myStepper.stepCCW(50);
delay(1000);
}
To further optimize the code, I changed the volatile byte pointers to volatile byte references so that the code wouldn’t have to go through the step of dereferencing the pointer.
class Stepper {
private:
int pin1shift, pin2shift, pin3shift, pin4shift;
volatile byte& reg1;
volatile byte& reg2;
volatile byte& reg3;
volatile byte& reg4;
int stepPos;
int delayTime;
public:
Stepper(volatile byte& r1, int p1, volatile byte& r2, int p2, volatile byte& r3, int p3, volatile byte& r4, int p4, int del): pin1shift(p1), reg1(r1), pin2shift(p2), reg2(r2), pin3shift(p3), reg3(r3), pin4shift(p4), reg4(r4), delayTime(del) {
stepPos = 0;
}
private:
void out1() {
reg1 |= 1 << pin1shift;
}
void clear1() {
reg1 &= ~(1 << pin1shift);
}
void out2() {
reg2 |= 1 << pin2shift;
}
void clear2() {
reg2 &= ~(1 << pin2shift);
}
void out3() {
reg3 |= 1 << pin3shift;
}
void clear3() {
reg3 &= ~(1 << pin3shift);
}
void out4() {
reg4 |= 1 << pin4shift;
}
void clear4() {
reg4 &= ~(1 << pin4shift);
}
void runStep() {
switch (stepPos) {
case 0:
out1();
clear2();
out3();
clear4();
break;
case 1:
clear1();
out2();
out3();
clear4();
break;
case 2:
clear1();
out2();
clear3();
out4();
break;
case 3:
out1();
clear2();
clear3();
out4();
break;
}
delay(delayTime);
}
public:
void stepCW(int numSteps) {
for (int i = 0; i < numSteps; ++i) {
stepPos = (stepPos + 1) % 4;
runStep();
}
}
void stepCCW(int numSteps) {
for (int i = 0; i < numSteps; ++i) {
--stepPos;
if (stepPos < 0) stepPos = 3;
runStep();
}
}
};
void setup() {
// put your setup code here, to run once:
PORTA.DIRSET |= 1 << 4;
PORTA.DIRSET |= 1 << 5;
PORTA.DIRSET |= 1 << 3;
PORTB.DIRSET |= 1 << 2;
}
Stepper myStepper(PORTA.OUT, 4, PORTA.OUT, 5, PORTB.OUT, 2, PORTA.OUT, 3, 10);
void loop() {
// put your main code here, to run repeatedly:
myStepper.stepCW(200);
delay(1000);
myStepper.stepCCW(200);
delay(1000);
}
Group Work¶
Our group work for this week can be found here.
I worked as part of group A1 on testing a water pump and made a graph for the results with python and matplotlib.