Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# for BluePill
# runner = "probe-run --probe C296C294070132124647524B4E00 --chip STM32F103C8"
# for BigTreeTech v1.1
runner = "probe-run --probe 15003200090000524A51544E --chip STM32F103RE"
rustflags = [
"-C", "linker=flip-link",
Expand Down
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ name = "drawer-robot"
version = "0.1.0"
edition = "2021"

[build-dependencies]
envmnt = "0.10.4"
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9"
glob = "0.3"
cc = "1.0.74"
bindgen = "0.68.1"

[dependencies]
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
critical-section = "1.1.1"
Expand Down
86 changes: 86 additions & 0 deletions arduino.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
arduino_home: $HOME/Library/Arduino15
external_home: $HOME/Documents/Arduino

core_version: STM32F1
# for BluePill
variant: generic_stm32f103c
# for BigTreeTech v1.1
# variant: generic_stm32f103r

arm_none_eabi_gcc_version: 4.8.3-2014q1
arduino_libraries:
external_libraries:
- TMCStepper

definitions:
ARDUINO: '10607'
F_CPU: 72000000L
# TODO: maybe I would need to add that, but better in code, manually, cause we have the same value in 'variant'
# ARDUINO_GENERIC_STM32F103C: '1'
__STM32F1__: '1'
ARDUINO_ARCH_STM32F1: '1'
MCU_STM32F103C8: '1'
# MCU_STM32F103RE: '1'
flags:
- '-mcpu=cortex-m3'


bindgen_lists:
allowlist_function:
- TMCStepper.*
- TMC2209Stepper.*
allowlist_type:
- TMCStepper.*
- TMC2209Stepper.*
blocklist_function:
- Print.*
- String.*
- Stream_readString
- Stream_readStringUntil
blocklist_type:
- Print.*
- String.*


# Arduino IDE shows me this line:
#
# /Users/pronvis/Library/Arduino15/packages/arduino/tools/arm-none-eabi-gcc/4.8.3-2014q1/bin/arm-none-eabi-gcc
# -c
# -g
# -Os
# -w
# -DDEBUG_LEVEL=DEBUG_NONE
# -std=gnu11
# -MMD
# -ffunction-sections
# -fdata-sections
# -nostdlib
# --param
# max-inline-insns-single=500
# -DBOARD_generic_stm32f103c
# -DVECT_TAB_ADDR=0x8000000
# -DERROR_LED_PORT=GPIOC
# -DERROR_LED_PIN=13
# -mcpu=cortex-m3
# -DF_CPU=72000000L
# -DARDUINO=10607
# -DARDUINO_GENERIC_STM32F103C
# -DARDUINO_ARCH_STM32F1
# -DCONFIG_MAPLE_MINI_NO_DISABLE_DEBUG=1
# -DSERIAL_USB
# -DGENERIC_BOOTLOADER
# -D
# -mthumb
# -march=armv7-m
# -D__STM32F1__
# -I/Users/pronvis/Documents/Arduino/hardware/Arduino_STM32/STM32F1/system/libmaple
# -I/Users/pronvis/Documents/Arduino/hardware/Arduino_STM32/STM32F1/system/libmaple/include
# -I/Users/pronvis/Documents/Arduino/hardware/Arduino_STM32/STM32F1/system/libmaple/stm32f1/include
# -I/Users/pronvis/Documents/Arduino/hardware/Arduino_STM32/STM32F1/system/libmaple/usb/stm32f1
# -I/Users/pronvis/Documents/Arduino/hardware/Arduino_STM32/STM32F1/system/libmaple/usb/usb_lib
# -I/Users/pronvis/Documents/Arduino/hardware/Arduino_STM32/STM32F1/cores/maple
# -I/Users/pronvis/Documents/Arduino/hardware/Arduino_STM32/STM32F1/variants/generic_stm32f103c
# /Users/pronvis/Documents/Arduino/hardware/Arduino_STM32/STM32F1/variants/generic_stm32f103c/wirish/syscalls.c
# -o
# /private/var/folders/n3/xdk6njln73sg_kmlcd076cth0000gn/T/arduino/sketches/879DAD20BC9E9FAD6E52CDDC4903C2B9/core/wirish/syscalls.c.o

264 changes: 264 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,267 @@
use bindgen::{Bindings, Builder};
use cc::Build;
use glob::glob;
use serde::Deserialize;
use std::collections::HashMap;
use std::path::PathBuf;

const CONFIG_FILE: &str = "arduino.yaml";

#[derive(Debug, Deserialize)]
struct BindgenLists {
pub allowlist_function: Vec<String>,
pub allowlist_type: Vec<String>,
pub blocklist_function: Vec<String>,
pub blocklist_type: Vec<String>,
}

#[derive(Debug, Deserialize)]
struct Config {
pub arduino_home: String,
pub external_home: String,
pub core_version: String,
pub variant: String,
pub arm_none_eabi_gcc_version: String,
pub arduino_libraries: Vec<String>,
pub external_libraries: Vec<String>,
pub definitions: HashMap<String, String>,
pub flags: Vec<String>,
pub bindgen_lists: BindgenLists,
}

impl Config {
fn arduino_package_path(&self) -> PathBuf {
let expanded = envmnt::expand(&self.arduino_home, None);
let arduino_home_path = PathBuf::from(expanded);
arduino_home_path.join("packages").join("arduino")
}

fn core_path(&self) -> PathBuf {
let expanded = envmnt::expand(&self.external_home, None);
PathBuf::from(expanded)
.join("hardware")
.join("Arduino_STM32")
.join(&self.core_version)
}

fn arm_none_eabi_gcc_home(&self) -> PathBuf {
self.arduino_package_path()
.join("tools")
.join("arm-none-eabi-gcc")
.join(&self.arm_none_eabi_gcc_version)
}

fn arm_none_eabi_gcc(&self) -> PathBuf {
self.arm_none_eabi_gcc_home()
.join("bin")
.join("arm-none-eabi-g++")
}

fn arduino_core_path(&self) -> PathBuf {
self.core_path().join("cores")
}

fn arduino_include_dirs(&self) -> Vec<PathBuf> {
let variant_path = self.core_path().join("variants").join(&self.variant);
let arm_none_eabi_gcc_include_path = self
.arm_none_eabi_gcc_home()
.join("arm-none-eabi")
.join("include");
vec![
self.arduino_core_path(),
variant_path,
arm_none_eabi_gcc_include_path,
]
}

fn arduino_libraries_path(&self) -> Vec<PathBuf> {
let library_root = self.core_path().join("libraries");
let mut result = vec![];
for library in &self.arduino_libraries {
result.push(library_root.join(library).join("src"))
}
result
}

fn external_libraries_path(&self) -> Vec<PathBuf> {
let expanded = envmnt::expand(&self.external_home, None);
let external_library_root = PathBuf::from(expanded).join("libraries");
let mut result = vec![];
for library in &self.external_libraries {
result.push(external_library_root.join(library).join("src"))
}
result
}

fn include_dirs(&self) -> Vec<PathBuf> {
let mut result = self.arduino_include_dirs();
result.extend(self.arduino_libraries_path());
result.extend(self.external_libraries_path());
result.push(self.arduino_core_path().join("maple")); //for 'Arduino.h'
result.push(self.core_path().join("system/libmaple/include")); // for 'libmaple/stm32.h'
result.push(self.core_path().join("system/libmaple")); // for 'stm32f1/include/series/stm32.h'
result.push(self.core_path().join("system/libmaple/stm32f1/include")); // for 'series/usart.h'
result.push(self.core_path().join("libraries/SPI/src")); // for 'SPI.h'
result
}

fn project_files(&self, patten: &str) -> Vec<PathBuf> {
let mut result =
files_in_folder(self.arduino_core_path().to_string_lossy().as_ref(), patten);
let mut libraries = self.arduino_libraries_path();
libraries.extend(self.external_libraries_path());

let pattern = format!("**/{}", patten);
for library in libraries {
let lib_sources = files_in_folder(library.to_string_lossy().as_ref(), &pattern);
result.extend(lib_sources);
}

result
}

fn cpp_files(&self) -> Vec<PathBuf> {
self.project_files("*.cpp")
}

fn c_files(&self) -> Vec<PathBuf> {
self.project_files("*.c")
}

fn bindgen_headers(&self) -> Vec<PathBuf> {
let mut result = vec![];
for library in self.external_libraries_path() {
let lib_headers = files_in_folder(library.to_string_lossy().as_ref(), "*.h");
result.extend(lib_headers);
}
result
}
}

fn files_in_folder(folder: &str, pattern: &str) -> Vec<PathBuf> {
let cpp_pattern = format!("{}/{}", folder, pattern);
let mut results = vec![];
for cpp_file in glob(&cpp_pattern).unwrap() {
let file = cpp_file.unwrap();
if !file.ends_with("main.cpp") {
results.push(file);
}
}
results
}

fn configure_arduino(config: &Config) -> Build {
let mut builder = Build::new();
for (k, v) in &config.definitions {
builder.define(k, v.as_str());
}
for flag in &config.flags {
builder.flag(flag);
}
builder
.compiler(config.arm_none_eabi_gcc())
.flag("-Os")
.cpp_set_stdlib(None)
.flag("-fno-exceptions")
.flag("-ffunction-sections")
.flag("-fdata-sections");

for include_dir in config.include_dirs() {
builder.include(include_dir);
}
builder
}

pub fn add_source_file(builder: &mut Build, files: Vec<PathBuf>) {
for file in files {
println!("cargo:rerun-if-changed={}", file.to_string_lossy());
builder.file(file);
}
}

fn compile_arduino(config: &Config) {
if config.cpp_files().len() > 0 {
let mut builder = configure_arduino(&config);
builder
.cpp(true)
.flag("-std=gnu++11")
.flag("-fpermissive")
.flag("-fno-threadsafe-statics");
add_source_file(&mut builder, config.cpp_files());
builder.compile("libarduino_c++.a");
println!("cargo:rustc-link-lib=static=arduino_c++");
}

//Looks like in my concrete case there is no 'C' files.
if config.c_files().len() > 0 {
let mut builder = configure_arduino(&config);
builder.flag("-std=gnu11");
add_source_file(&mut builder, config.c_files());
builder.compile("libarduino_c.a");
println!("cargo:rustc-link-lib=static=arduino_c");
}
}

fn configure_bindgen_for_arduino(config: &Config) -> Builder {
let mut builder = Builder::default();
for (k, v) in &config.definitions {
builder = builder.clang_arg(&format!("-D{}={}", k, v));
}
for flag in &config.flags {
builder = builder.clang_arg(flag);
}
builder = builder
.clang_args(&["-x", "c++", "-std=gnu++11"])
.use_core()
.layout_tests(false)
.parse_callbacks(Box::new(bindgen::CargoCallbacks));

for include_dir in config.include_dirs() {
builder = builder.clang_arg(&format!("-I{}", include_dir.to_string_lossy()));
}
for header in config.bindgen_headers() {
builder = builder.header(header.to_string_lossy());
}
for item in &config.bindgen_lists.allowlist_function {
builder = builder.allowlist_function(item);
}
for item in &config.bindgen_lists.allowlist_type {
builder = builder.allowlist_type(item);
}
for item in &config.bindgen_lists.blocklist_function {
builder = builder.blocklist_function(item);
}
for item in &config.bindgen_lists.blocklist_type {
builder = builder.blocklist_type(item);
}

builder
}

fn generate_bindings(config: &Config) {
let bindings: Bindings = configure_bindgen_for_arduino(&config)
.generate()
.expect("Unable to generate bindings");
let project_root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("src")
.join("arduino.rs");
bindings
.write_to_file(project_root)
.expect("Couldn't write bindings!");
}

fn main() {
println!("cargo:rustc-env=DEFMT_LOG=trace");

println!("cargo:rerun-if-changed={}", CONFIG_FILE);
let config_string = std::fs::read_to_string(CONFIG_FILE)
.unwrap_or_else(|e| panic!("Unable to read {} file: {}", CONFIG_FILE, e));
let config: Config = serde_yaml::from_str(&config_string)
.unwrap_or_else(|e| panic!("Unable to parse {} file: {}", CONFIG_FILE, e));

println!("Arduino configuration: {:#?}", config);

compile_arduino(&config);
println!("Arduino compled successfully! Generate buildings...");
generate_bindings(&config);
}
Loading