Cleaned up build tool, added stub for ds3231.
This commit is contained in:
parent
d6d6007e62
commit
8b076146cb
|
@ -1 +1,2 @@
|
|||
bin/
|
||||
build
|
27
Makefile
27
Makefile
|
@ -1,27 +0,0 @@
|
|||
all: clean build
|
||||
|
||||
clean:
|
||||
rm -rf bin/
|
||||
|
||||
# The following settings are very important!
|
||||
# -c arduino for flashing to an AVR device with arduino bootloader.
|
||||
# -p atmega328p is for the specific microcontroller.
|
||||
# -b 57600 is the baudrate for transmission. Nano boards ONLY accept 57600, uno boards might accept 115200.
|
||||
# -P specifies the port.
|
||||
flash: build
|
||||
avrdude -c arduino -p atmega328p -P /dev/ttyUSB0 -b 57600 -U flash:w:bin/gympal.hex:i
|
||||
|
||||
build: gympal.hex
|
||||
|
||||
gympal.hex: gympal.o control.o
|
||||
avr-gcc -Os -mmcu=atmega328p -o bin/gympal.elf bin/gympal.o bin/control.o
|
||||
avr-objcopy -O ihex -R .eeprom bin/gympal.elf bin/gympal.hex
|
||||
|
||||
gympal.o: src/gympal.c bin
|
||||
avr-gcc -Wall -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o bin/gympal.o src/gympal.c
|
||||
|
||||
control.o: src/control.c bin
|
||||
avr-gcc -Wall -Os -DF_CPU=16000000UL -mmcu=atmega328p -c -o bin/control.o src/control.c
|
||||
|
||||
bin:
|
||||
mkdir bin
|
|
@ -6,9 +6,13 @@ The GymPal is a lightweight piece of hardware with built-in storage, time-keepin
|
|||
## Development
|
||||
The software for this system is developed in C, using [avr-libc](https://www.nongnu.org/avr-libc/), and the build toolchain is managed by make, using [avr-gcc](https://linux.die.net/man/1/avr-gcc) and [avr-dude](https://github.com/avrdudes/avrdude) to upload firmware to the Atmega328p microcontroller.
|
||||
|
||||
To compile the firmware, you can run `make`.
|
||||
```shell
|
||||
sudo apt install gcc-avr
|
||||
sudo apt install avr-libc
|
||||
sudo apt install avrdude
|
||||
```
|
||||
|
||||
To upload the firmware to an Arduino, run `make flash`.
|
||||
After cloning this repository, you must first compile the build script with `./prepare-build-tools.d`. Then, you can run `./build <command>` to run various build commands. See `./build help` for more information.
|
||||
|
||||
## Hardware
|
||||
Here's a list of the hardware that this project uses, just for reference:
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# Build-Tools
|
||||
This directory contains the sources for a small D program that manages compilation and uploading of code to AVR devices.
|
||||
|
||||
Run `./prepare-build-tools.d` from the main project directory to compile it.
|
|
@ -9,13 +9,9 @@ import std.file;
|
|||
import std.string;
|
||||
import std.conv;
|
||||
import std.path;
|
||||
import std.digest.md;
|
||||
import std.base64;
|
||||
|
||||
const string MCU_ID = "atmega328p";
|
||||
const ulong CPU_FREQ = 16_000_000;
|
||||
const string BOOTLOADER = "arduino";
|
||||
const ulong AVRDUDE_BAUDRATE = 57_600;
|
||||
import util;
|
||||
import hash;
|
||||
|
||||
const string SOURCE_DIR = "src";
|
||||
const string BUILD_DIR = "bin";
|
||||
|
@ -28,12 +24,20 @@ const string[] COMPILER_FLAGS = [
|
|||
"-mmcu=atmega328p"
|
||||
];
|
||||
|
||||
const string[] AVRDUDE_FLAGS = [
|
||||
"-c arduino",
|
||||
"-p m328p",
|
||||
"-P /dev/ttyUSB0",
|
||||
"-b 57600",
|
||||
"-u"
|
||||
];
|
||||
|
||||
alias BuildCommand = int function(string[]);
|
||||
|
||||
int main(string[] args) {
|
||||
string command;
|
||||
if (args.length < 2) {
|
||||
command = "help";
|
||||
command = "build";
|
||||
} else {
|
||||
command = args[1].strip.toLower;
|
||||
}
|
||||
|
@ -57,16 +61,21 @@ int main(string[] args) {
|
|||
|
||||
int helpCommand(string[] args) {
|
||||
writeln("build.d - A simple build script for C files.");
|
||||
writeln("--------------------------------------------");
|
||||
writeln("Usage: ./build.d <command> [args...]");
|
||||
writeln("");
|
||||
writeln("The following commands are available:");
|
||||
writeln("build [-f] - Compiles source code. Use -f to force rebuild.");
|
||||
writeln(" build [-f] Compiles source code. Use -f to force rebuild.");
|
||||
writeln(" By default, sources are hashed, and only built if changes are detected.");
|
||||
writeln("flash [buildArgs] - Flashes code onto a connected AVR device via AVRDude.");
|
||||
writeln("clean - Removes all build files.");
|
||||
writeln("help - Shows this help information.");
|
||||
writeln(" This is also the default command if none is specified.");
|
||||
writeln(" flash Flashes code onto a connected AVR device via AVRDude.");
|
||||
writeln(" clean Removes all build files.");
|
||||
writeln(" help Shows this help information.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int clean(string[] args) {
|
||||
if (!exists(BUILD_DIR)) return 0;
|
||||
rmdirRecurse(BUILD_DIR);
|
||||
return 0;
|
||||
}
|
||||
|
@ -77,9 +86,16 @@ int buildCommand(string[] args) {
|
|||
}
|
||||
|
||||
int flashCommand(string[] args) {
|
||||
string hexFilePath = buildPath(BUILD_DIR, "gympal.hex");
|
||||
if (!exists(hexFilePath)) {
|
||||
writeln("Hex file doesn't exist yet; building it now.");
|
||||
int result = buildCommand(args);
|
||||
if (result != 0) return result;
|
||||
return flashToMCU(buildPath("bin", "gympal.hex"));
|
||||
}
|
||||
string flags = join(AVRDUDE_FLAGS, " ");
|
||||
string cmd = format!"avrdude %s -U flash:w:%s:i"(flags, hexFilePath);
|
||||
writeln(cmd);
|
||||
return run(cmd);
|
||||
}
|
||||
|
||||
int build(bool force = false) {
|
||||
|
@ -101,35 +117,8 @@ int build(bool force = false) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
//-------- Utility functions below here -------
|
||||
|
||||
int run(string shellCommand) {
|
||||
import std.process : Pid, spawnShell, wait;
|
||||
Pid pid = spawnShell(shellCommand);
|
||||
return wait(pid);
|
||||
}
|
||||
|
||||
void runOrQuit(string shellCommand, int[] successExitCodes = [0]) {
|
||||
import core.stdc.stdlib : exit;
|
||||
import std.algorithm : canFind;
|
||||
int result = run(shellCommand);
|
||||
if (!canFind(successExitCodes, result)) exit(result);
|
||||
}
|
||||
|
||||
void quitIfNonZero(int n) {
|
||||
import core.stdc.stdlib : exit;
|
||||
if (n != 0) exit(n);
|
||||
}
|
||||
|
||||
bool shouldCompileSource(string sourcePath) {
|
||||
string name = baseName(sourcePath);
|
||||
name = name[0 .. name.lastIndexOf('.')];
|
||||
string hashPath = buildPath("bin", "hash", name ~ ".md5");
|
||||
string objectPath = buildPath("bin", name ~ ".o");
|
||||
if (!exists(hashPath) || !exists(objectPath)) return true;
|
||||
ubyte[] storedHash = Base64.decode(readText(hashPath).strip());
|
||||
ubyte[16] currentHash = md5Of(readText(sourcePath));
|
||||
return storedHash != currentHash;
|
||||
return !contentMatchesHash(sourcePath);
|
||||
}
|
||||
|
||||
string compileSourceToObject(string sourcePath, bool force = false) {
|
||||
|
@ -137,7 +126,6 @@ string compileSourceToObject(string sourcePath, bool force = false) {
|
|||
string name = baseName(sourcePath);
|
||||
name = name[0 .. name.lastIndexOf('.')];
|
||||
string objectPath = buildPath("bin", name ~ ".o");
|
||||
string hashPath = buildPath("bin", "hash", name ~ ".md5");
|
||||
|
||||
if (!force && !shouldCompileSource(sourcePath)) {
|
||||
writefln!"Not compiling %s because no changes detected."(sourcePath);
|
||||
|
@ -151,12 +139,7 @@ string compileSourceToObject(string sourcePath, bool force = false) {
|
|||
);
|
||||
writeln(cmd);
|
||||
runOrQuit(cmd);
|
||||
|
||||
ubyte[16] hash = md5Of(readText(sourcePath));
|
||||
string hashDir = buildPath("bin", "hash");
|
||||
if (!exists(hashDir)) mkdir(hashDir);
|
||||
std.file.write(hashPath, Base64.encode(hash));
|
||||
|
||||
saveHash(sourcePath);
|
||||
return objectPath;
|
||||
}
|
||||
|
||||
|
@ -184,25 +167,3 @@ string copyToHex(string elfFile) {
|
|||
runOrQuit(cmd);
|
||||
return hexFile;
|
||||
}
|
||||
|
||||
int flashToMCU(string hexFile) {
|
||||
string cmd = format!"avrdude -c %s -p m328p -P /dev/ttyUSB0 -b %d -u -U flash:w:%s:i"(
|
||||
BOOTLOADER,
|
||||
AVRDUDE_BAUDRATE,
|
||||
hexFile
|
||||
);
|
||||
writeln(cmd);
|
||||
return run(cmd);
|
||||
}
|
||||
|
||||
string[] findFiles(string path, string suffix = null) {
|
||||
import std.array;
|
||||
import std.algorithm;
|
||||
auto app = appender!(string[]);
|
||||
foreach (DirEntry entry; dirEntries(path, SpanMode.breadth, false)) {
|
||||
if (entry.isFile && (suffix is null || entry.name.endsWith(suffix))) {
|
||||
app ~= entry.name;
|
||||
}
|
||||
}
|
||||
return app[];
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* Helper for hashing the content of source files to determine if we need to
|
||||
* re-compile certain sources.
|
||||
*/
|
||||
module hash;
|
||||
|
||||
import std.stdio;
|
||||
import std.file;
|
||||
import std.string;
|
||||
import std.digest.md;
|
||||
import std.base64;
|
||||
|
||||
const HASH_FILE = "bin/hash.txt";
|
||||
|
||||
/**
|
||||
* Determines if the content of the source file at the given path matches
|
||||
* the hash we have for it (if possible).
|
||||
* Params:
|
||||
* sourcePath = The path to the source file.
|
||||
* Returns: True if we have a hash for the source file, and it matches the
|
||||
* current hash of the file contents. False if we don't have a hash or it
|
||||
* doesn't match.
|
||||
*/
|
||||
bool contentMatchesHash(string sourcePath) {
|
||||
if (!exists(sourcePath) || !isFile(sourcePath)) return false;
|
||||
string[string] hashes = readHashes();
|
||||
if (sourcePath !in hashes) return false;
|
||||
ubyte[16] rawHash = md5Of(readText(sourcePath));
|
||||
ubyte[] savedHash = Base64.decode(hashes[sourcePath]);
|
||||
return rawHash == savedHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the hash of the given source path to the hash file.
|
||||
* Params:
|
||||
* sourcePath = The source path of the file to save.
|
||||
*/
|
||||
void saveHash(string sourcePath) {
|
||||
if (!exists(sourcePath) || !isFile(sourcePath)) return;
|
||||
ubyte[16] rawHash = md5Of(readText(sourcePath));
|
||||
string hash = Base64.encode(rawHash);
|
||||
string[string] hashes = readHashes();
|
||||
hashes[sourcePath] = hash;
|
||||
saveHashes(hashes);
|
||||
}
|
||||
|
||||
private string[string] readHashes() {
|
||||
string[string] hashes;
|
||||
if (!exists(HASH_FILE)) return hashes;
|
||||
auto file = File(HASH_FILE, "r");
|
||||
foreach (string line; lines(file)) {
|
||||
string[] parts = split(line, ":");
|
||||
string sourcePath = parts[0].strip;
|
||||
string hashBase64 = parts[1].strip;
|
||||
hashes[sourcePath] = hashBase64;
|
||||
}
|
||||
return hashes;
|
||||
}
|
||||
|
||||
private void saveHashes(string[string] hashes) {
|
||||
auto file = File(HASH_FILE, "w");
|
||||
foreach (string sourcePath, string hash; hashes) {
|
||||
file.writefln!"%s:%s"(sourcePath, hash);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
module util;
|
||||
|
||||
/**
|
||||
* Runs the given shell command.
|
||||
* Params:
|
||||
* shellCommand = The command to run.
|
||||
* Returns: The exit code of the command.
|
||||
*/
|
||||
int run(string shellCommand) {
|
||||
import std.process : Pid, spawnShell, wait;
|
||||
Pid pid = spawnShell(shellCommand);
|
||||
return wait(pid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the given command, and exits if an unsatisfactory exit code is returned.
|
||||
* Params:
|
||||
* shellCommand = The command to run.
|
||||
* successExitCodes = The list of exit codes which are considered success.
|
||||
*/
|
||||
void runOrQuit(string shellCommand, int[] successExitCodes = [0]) {
|
||||
import core.stdc.stdlib : exit;
|
||||
import std.algorithm : canFind;
|
||||
int result = run(shellCommand);
|
||||
if (!canFind(successExitCodes, result)) exit(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exits the program if the given number is non-zero.
|
||||
* Params:
|
||||
* n = The number to check.
|
||||
*/
|
||||
void quitIfNonZero(int n) {
|
||||
import core.stdc.stdlib : exit;
|
||||
if (n != 0) exit(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a list of files in a directory which match a given suffix.
|
||||
* Params:
|
||||
* path = The path to look in.
|
||||
* suffix = The suffix to match.
|
||||
* Returns: The list of paths to files that match.
|
||||
*/
|
||||
string[] findFiles(string path, string suffix = null) {
|
||||
import std.array;
|
||||
import std.algorithm;
|
||||
import std.file;
|
||||
auto app = appender!(string[]);
|
||||
foreach (DirEntry entry; dirEntries(path, SpanMode.breadth, false)) {
|
||||
if (entry.isFile && (suffix is null || entry.name.endsWith(suffix))) {
|
||||
app ~= entry.name;
|
||||
}
|
||||
}
|
||||
return app[];
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env rdmd
|
||||
/**
|
||||
* Simple script that just compiles this project's build system to an executable.
|
||||
* Run this before building: `./prepare-build-tools.d`
|
||||
*/
|
||||
module prepare_build_tools;
|
||||
|
||||
import std.process;
|
||||
import std.array;
|
||||
import std.file;
|
||||
import std.algorithm;
|
||||
|
||||
int main() {
|
||||
auto sourceApp = appender!(string[]);
|
||||
foreach (DirEntry entry; dirEntries("build-tools", SpanMode.shallow, false)) {
|
||||
if (entry.isFile && endsWith(entry.name, ".d")) {
|
||||
sourceApp ~= entry.name;
|
||||
}
|
||||
}
|
||||
string[] sources = sourceApp[];
|
||||
string sourcesStr = join(sources, " ");
|
||||
Pid pid = spawnShell("dmd -O " ~ sourcesStr ~ " -release -of=build");
|
||||
scope (exit) {
|
||||
if (exists("build.o")) {
|
||||
std.file.remove("build.o");
|
||||
}
|
||||
}
|
||||
return wait(pid);
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
#include "display.h"
|
||||
|
||||
// Chip Select
|
||||
struct signal cs = { .ddr = &DDRB, .port = &PORTB, .pin = 2 };
|
||||
// Back Light
|
||||
struct signal bl = { .ddr = &DDRB, .port = &PORTB, .pin = 1 };
|
||||
|
|
|
@ -7,13 +7,18 @@
|
|||
#ifndef DISPLAY_H
|
||||
#define DISPLAY_H
|
||||
|
||||
#include "st7735.h"
|
||||
#include "lib/st7735.h"
|
||||
|
||||
/**
|
||||
* @brief Initializes the display.
|
||||
*/
|
||||
void display_init();
|
||||
|
||||
/**
|
||||
* @brief Gets a pointer to the underlying ST7735 LCD struct, which can be used
|
||||
* for calling functions on it manually.
|
||||
* @return A pointer to the ST7735 LCD struct.
|
||||
*/
|
||||
struct st7735* display_get_lcd();
|
||||
|
||||
void display_show_str(char* str);
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
#include "ds3231.h"
|
||||
|
||||
// TODO: Implement this!
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef DS3231_H
|
||||
#define DS3231_H
|
||||
|
||||
// TODO: Implement this!
|
||||
|
||||
void ds3231_init();
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue