initial commit: basic control working
This commit is contained in:
commit
c667006ca0
|
@ -0,0 +1,2 @@
|
|||
.vscode
|
||||
out
|
|
@ -0,0 +1,106 @@
|
|||
# Makefile to build an Arduino project
|
||||
# Thanks to <http://theonlineshed.com/arduino-without-the-ide-an-intro-to-unix-make>
|
||||
# Determine path of arduino core libraries
|
||||
ARDUINO_HW := /usr/share/arduino/hardware
|
||||
# On Arch Linux, the path is /usr/share/arduino/hardware/archlinux-arduino, so
|
||||
# we use a wildcard to handle this case
|
||||
# TODO: allow overriding ARDUINO_LIB from env variables
|
||||
ARDUINO_LIB := $(firstword $(wildcard ${ARDUINO_HW}/*arduino))
|
||||
|
||||
# Arduino core libraries
|
||||
VPATH := \
|
||||
${ARDUINO_LIB}/avr/cores/arduino \
|
||||
${ARDUINO_LIB}/avr/libraries/Wire/src \
|
||||
|
||||
VARIANTS := ${ARDUINO_LIB}/avr/variants/standard
|
||||
# Only used for VSCode Intellisense configuration
|
||||
AVR_INC := /usr/avr/include
|
||||
|
||||
# Include all subdirectories from libraries we need
|
||||
VPATH := $(foreach path, ${VPATH}, ${path} $(shell find ${path} -type d))
|
||||
|
||||
# Add the local directory into VPATH. Potential local subdirectories also go here
|
||||
# This is to prevent the command above from including directories like .git into
|
||||
# the VPATH list
|
||||
VPATH += ${PWD} \
|
||||
|
||||
# Build parameters
|
||||
BUILD_DIR := out
|
||||
CC := avr-gcc
|
||||
CXX := avr-g++
|
||||
OBJCOPY := avr-objcopy
|
||||
AVRDUDE := avrdude
|
||||
MMCU := -mmcu=atmega328p
|
||||
INCS := \
|
||||
$(foreach path, ${VPATH}, -I ${path}) \
|
||||
$(foreach path, ${VARIANTS}, -I ${path}) \
|
||||
|
||||
CFLAGS := -Os -DF_CPU=16000000UL ${MMCU} ${INCS}
|
||||
# Generate sections for each function and variable
|
||||
# so that LD can eliminate unused functions and variables
|
||||
CFLAGS += -ffunction-sections -fdata-sections
|
||||
# GCC LD options to eliminate unused sections
|
||||
LDFLAGS := ${MMCU} -Wl,--gc-sections
|
||||
|
||||
# List all source files
|
||||
SOURCES := $(foreach path, $(VPATH), \
|
||||
$(wildcard ${path}/*.c) $(wildcard ${path}/*.cpp) $(wildcard ${path}/*.S))
|
||||
# Remove main.cpp from the Arduino core libraries (instead of the local project)
|
||||
# This allows us to use the proper main() function instead of setup() and loop()
|
||||
SOURCES := $(filter-out ${ARDUINO_HW}%main.cpp, ${SOURCES})
|
||||
# Strip path from the file names, since they are in VPATH, which means
|
||||
# we don't need to include the full paths
|
||||
SOURCES := $(foreach file, ${SOURCES}, $(notdir ${file}))
|
||||
# Translate everything to target object files
|
||||
OBJS := ${SOURCES}
|
||||
OBJS := $(patsubst %.c, ${BUILD_DIR}/%.o, ${OBJS})
|
||||
OBJS := $(patsubst %.cpp, ${BUILD_DIR}/%.o, ${OBJS})
|
||||
OBJS := $(patsubst %.S, ${BUILD_DIR}/%_asm.o, ${OBJS})
|
||||
|
||||
PROGRAM := arduino-otp
|
||||
|
||||
# Build rules for different source types
|
||||
${BUILD_DIR}/%.o: %.c
|
||||
mkdir -p ${BUILD_DIR}
|
||||
${CC} ${CFLAGS} -c $< -o $@
|
||||
|
||||
${BUILD_DIR}/%.o: %.cpp
|
||||
mkdir -p ${BUILD_DIR}
|
||||
${CXX} ${CFLAGS} -c $< -o $@
|
||||
|
||||
${BUILD_DIR}/%_asm.o: %.S
|
||||
mkdir -p ${BUILD_DIR}
|
||||
${CC} ${CFLAGS} -c $< -o $@
|
||||
|
||||
${BUILD_DIR}/${PROGRAM}.elf: ${OBJS}
|
||||
${CC} ${LDFLAGS} $^ -o $@
|
||||
|
||||
${BUILD_DIR}/${PROGRAM}.hex: ${BUILD_DIR}/${PROGRAM}.elf
|
||||
${OBJCOPY} -O ihex -R .eeprom $< $@
|
||||
|
||||
define to-json-array
|
||||
$(shell echo '$(foreach path, $1, "${path}",)' | sed "s/,$$//")
|
||||
endef
|
||||
|
||||
# vscode intellisense configuration file
|
||||
# note that vscode will automatically format the generated file
|
||||
.vscode/c_cpp_properties.json: c_cpp_properties.json.template
|
||||
mkdir -p .vscode
|
||||
sed 's@__includePaths__@$(call to-json-array, ${AVR_INC} ${VPATH} ${VARIANTS})@' c_cpp_properties.json.template > .vscode/c_cpp_properties.json
|
||||
|
||||
.PHONY: all clean upload vscode
|
||||
|
||||
all: ${BUILD_DIR}/${PROGRAM}.hex
|
||||
|
||||
clean:
|
||||
rm -rf ${BUILD_DIR}
|
||||
|
||||
upload: all
|
||||
ifndef PORT
|
||||
$(error PORT is undefined)
|
||||
endif
|
||||
${AVRDUDE} -F -V -c arduino -p ATMEGA328P -P ${PORT} -b 115200 -D -U flash:w:${BUILD_DIR}/${PROGRAM}.hex
|
||||
|
||||
vscode:
|
||||
rm -rf .vscode/c_cpp_properties.json
|
||||
make .vscode/c_cpp_properties.json
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "AVR",
|
||||
"includePath": [__includePaths__],
|
||||
"intelliSenseMode": "gcc-x64",
|
||||
"compilerPath": "/usr/bin/avr-g++",
|
||||
"compilerArgs": [
|
||||
"-mmcu=atmega328p"
|
||||
],
|
||||
"defines": [
|
||||
"__ATmega328P__",
|
||||
"__AVR_ATmega328P__"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
#include <Arduino.h>
|
||||
#include "motor_control.h"
|
||||
|
||||
MotorControl motors[4] = {
|
||||
MotorControl(5, 2),
|
||||
MotorControl(6, 3),
|
||||
MotorControl(7, 4),
|
||||
MotorControl(13, 12)
|
||||
};
|
||||
|
||||
int main() {
|
||||
init();
|
||||
// Enable the motor drivers
|
||||
pinMode(8, OUTPUT);
|
||||
digitalWrite(8, LOW);
|
||||
|
||||
// Initialize the controllers
|
||||
for (int i = 0; i < 4; i++) {
|
||||
motors[i].Init();
|
||||
}
|
||||
|
||||
// Test: play A4 (440 Hz) on each successive motor every second
|
||||
int cur_motor = -1;
|
||||
|
||||
while (true) {
|
||||
unsigned long cur_micros = micros();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
motors[i].Tick(cur_micros);
|
||||
}
|
||||
|
||||
int new_motor = (cur_micros / 1000 / 1000) % 4;
|
||||
|
||||
if (new_motor != cur_motor) {
|
||||
motors[new_motor].TickOn(2272);
|
||||
|
||||
if (cur_motor != -1) {
|
||||
motors[cur_motor].TickOff();
|
||||
}
|
||||
|
||||
cur_motor = new_motor;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#include <Arduino.h>
|
||||
#include "motor_control.h"
|
||||
|
||||
MotorControl::MotorControl(int pin_dir, int pin_step) :
|
||||
pin_dir(pin_dir), pin_step(pin_step),
|
||||
last_tick_micros(0), tick_period_micros(0)
|
||||
{
|
||||
// No actual constructor logic -- initialization is in Init()
|
||||
}
|
||||
|
||||
void MotorControl::Init() {
|
||||
// Initialize the pin state
|
||||
pinMode(pin_dir, OUTPUT);
|
||||
pinMode(pin_step, OUTPUT);
|
||||
digitalWrite(pin_dir, LOW);
|
||||
digitalWrite(pin_step, LOW);
|
||||
}
|
||||
|
||||
void MotorControl::TickOn(unsigned long period_micros) {
|
||||
tick_period_micros = period_micros;
|
||||
// Force the next tick to happen
|
||||
last_tick_micros = 0;
|
||||
}
|
||||
|
||||
void MotorControl::TickOff() {
|
||||
tick_period_micros = 0;
|
||||
}
|
||||
|
||||
void MotorControl::Tick(unsigned long cur_micros) {
|
||||
if (cur_micros == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tick_period_micros == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (last_tick_micros == 0 || (cur_micros - last_tick_micros) >= tick_period_micros) {
|
||||
DoTick();
|
||||
last_tick_micros = cur_micros;
|
||||
}
|
||||
}
|
||||
|
||||
void MotorControl::DoTick() {
|
||||
// Pulse the step pin once
|
||||
digitalWrite(pin_step, HIGH);
|
||||
digitalWrite(pin_step, LOW);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
class MotorControl {
|
||||
private:
|
||||
// Control pins of the stepper motor
|
||||
int pin_dir, pin_step;
|
||||
// Timestamp of the last motor tick
|
||||
unsigned long last_tick_micros;
|
||||
// Interval of motor ticking; 0 to disable the motor
|
||||
unsigned long tick_period_micros;
|
||||
|
||||
void DoTick();
|
||||
public:
|
||||
// Constructor
|
||||
MotorControl(int pin_dir, int pin_step);
|
||||
// Must be called before using any other functionality (but after enabling the controllers)
|
||||
void Init();
|
||||
// Set the motor to tick at a given interval (inverse of frequency)
|
||||
void TickOn(unsigned long period_micros);
|
||||
// Turn off the motor
|
||||
void TickOff();
|
||||
// Perform a tick if necessary
|
||||
// This should be called from the main event loop
|
||||
void Tick(unsigned long cur_micros);
|
||||
};
|
Loading…
Reference in New Issue