From 823f030974091aaf30d8fec3d5e1175e348cd1fe Mon Sep 17 00:00:00 2001 From: sid Date: Wed, 13 May 2026 18:23:02 +0200 Subject: [PATCH] initial commit --- .envrc | 1 + .gitignore | 5 ++ Makefile | 67 ++++++++++++++++++++++ README.md | 1 + build.sh | 6 ++ flake.lock | 137 +++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.c | 85 ++++++++++++++++++++++++++++ 8 files changed, 461 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 build.sh create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 src/main.c diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5f20868 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.cache/ +.direnv/ +build/ +compile_commands.json +result diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..480f636 --- /dev/null +++ b/Makefile @@ -0,0 +1,67 @@ +PREFIX ?= arm-none-eabi- +CC = $(PREFIX)gcc +OBJCOPY = $(PREFIX)objcopy +SIZE = $(PREFIX)size + +PNAME := bincnt +BUILD := build + +# libopencm3 is provided by the Nix shell +OPENCM3_DIR ?= "" + +SRCS := src/main.c +OBJS := $(addprefix $(BUILD)/, $(SRCS:.c=.o)) + +# STM32G4 +CPU_FLAGS := -mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 + +CFLAGS := $(CPU_FLAGS) \ + -DSTM32G4 \ + -Os -Wall -Wextra \ + -ffunction-sections -fdata-sections \ + -I$(OPENCM3_DIR)/include + +LDFLAGS := $(CPU_FLAGS) \ + -nostartfiles \ + -Wl,--gc-sections \ + -L$(OPENCM3_DIR)/lib \ + -lopencm3_stm32g4 + +ELF := $(BUILD)/$(PNAME).elf +BIN := $(BUILD)/$(PNAME).bin +HEX := $(BUILD)/$(PNAME).hex + +.PHONY: all clean flash size + +all: $(BIN) $(HEX) size + +$(BUILD)/%.o: %.c + @mkdir -p $(@D) + $(CC) $(CFLAGS) -c $< -o $@ + +LDSCRIPT := $(BUILD)/stm32g474xe.ld + +$(LDSCRIPT): + @mkdir -p $(@D) + $(CC) -I$(OPENCM3_DIR)/include $(shell python3 $(OPENCM3_DIR)/scripts/genlink.py $(OPENCM3_DIR)/ld/devices.data stm32g474xe DEFS) -P -E $(OPENCM3_DIR)/ld/linker.ld.S -o $@ + +$(ELF): $(OBJS) $(LDSCRIPT) + $(CC) $(OBJS) $(LDFLAGS) -T$(LDSCRIPT) -o $@ + +$(BIN): $(ELF) + $(OBJCOPY) -O binary $< $@ + +$(HEX): $(ELF) + $(OBJCOPY) -O ihex $< $@ + +size: $(ELF) + $(SIZE) $< + +flash: $(BIN) + openocd \ + -f interface/stlink.cfg \ + -f target/stm32g4x.cfg \ + -c "program $(BIN) verify reset exit 0x08000000" + +clean: + rm -rf $(BUILD) diff --git a/README.md b/README.md new file mode 100644 index 0000000..d7c54a4 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Binary counter on status LEDs `LED_OR`, `LED_GR`, and `LED_RD` using [libopencm3](https://github.com/libopencm3/libopencm3). diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..ae17afe --- /dev/null +++ b/build.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +make clean +mkdir -p build +bear -- make diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..ec8afbf --- /dev/null +++ b/flake.lock @@ -0,0 +1,137 @@ +{ + "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", + "owner": "NixOS", + "repo": "flake-compat", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "flake-compat", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "libopencm3-src": { + "flake": false, + "locked": { + "lastModified": 1776882709, + "narHash": "sha256-P1MauqEzbra2asJf1uMU/7tUeAfYzYbyDltmbm86SLU=", + "owner": "libopencm3", + "repo": "libopencm3", + "rev": "fe769eef14a7930089d1cd729834facd589f5eee", + "type": "github" + }, + "original": { + "owner": "libopencm3", + "repo": "libopencm3", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1778124196, + "narHash": "sha256-pYEytCNic/czazbV9r3tbQ6BZzqRBg/41x2dIC5ymOo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "68a8af93ff4297686cb68880845e61e5e2e41d92", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixpkgs-unstable", + "type": "indirect" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776796298, + "narHash": "sha256-PcRvlWayisPSjd0UcRQbhG8Oqw78AcPE6x872cPRHN8=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "3cfd774b0a530725a077e17354fbdb87ea1c4aad", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "libopencm3-src": "libopencm3-src", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks", + "utils": "utils" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..13fe1a4 --- /dev/null +++ b/flake.nix @@ -0,0 +1,159 @@ +{ + inputs = { + nixpkgs.url = "nixpkgs/nixpkgs-unstable"; + utils.url = "github:numtide/flake-utils"; + libopencm3-src = { + url = "github:libopencm3/libopencm3"; + flake = false; + }; + pre-commit-hooks = { + url = "github:cachix/pre-commit-hooks.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + utils, + libopencm3-src, + pre-commit-hooks, + ... + }: + { + overlays.default = final: prev: { + libopencm3-stm32g4 = final.stdenvNoCC.mkDerivation { + pname = "libopencm3-stm32g4"; + version = "latest"; + + src = libopencm3-src; + + postPatch = '' + patchShebangs scripts + ''; + + nativeBuildInputs = [ + final.gcc-arm-embedded + final.python3 + final.gnumake + ]; + + enableParallelBuilding = true; + + makeFlags = [ + "PREFIX=arm-none-eabi-" + ]; + + TARGETS = "stm32/g4"; + + installPhase = '' + runHook preInstall + + mkdir -p $out + cp -r include lib mk scripts ld $out/ + + patchShebangs $out/scripts + + runHook postInstall + ''; + + meta = with final.lib; { + description = "Open source ARM Cortex-M microcontroller library (STM32G4 target)"; + homepage = "http://libopencm3.org/"; + license = licenses.lgpl3Plus; + platforms = platforms.all; + }; + }; + }; + } + // utils.lib.eachDefaultSystem ( + system: + let + pkgs = import nixpkgs { + inherit system; + overlays = [ self.overlays.default ]; + }; + pname = "bincnt"; + version = "0.1.0"; + in + { + packages = { + inherit (pkgs) libopencm3-stm32g4; + default = pkgs.stdenvNoCC.mkDerivation { + inherit pname version; + src = pkgs.lib.cleanSource ./.; + + nativeBuildInputs = with pkgs; [ + gcc-arm-embedded + gnumake + python3 + ]; + + makeFlags = [ + "OPENCM3_DIR=${pkgs.libopencm3-stm32g4}" + "PREFIX=arm-none-eabi-" + ]; + + installPhase = '' + runHook preInstall + mkdir -p $out/bin + cp build/${pname}.elf $out/bin/ + cp build/${pname}.bin $out/bin/ + cp build/${pname}.hex $out/bin/ + runHook postInstall + ''; + }; + }; + + devShells.default = + let + # FIXME: 'gnu/stubs-32.h' file not found + clangdWrapper = pkgs.writeShellScriptBin "clangd" '' + exec ${pkgs.clang-tools}/bin/clangd \ + --query-driver=${pkgs.gcc-arm-embedded}/bin/arm-none-eabi-* \ + "$@" + ''; + in + pkgs.mkShell { + name = "stm32-blink"; + packages = self.packages.${system}.default.nativeBuildInputs ++ [ + clangdWrapper + + pkgs.bear + pkgs.openocd + pkgs.stlink + ]; + OPENCM3_DIR = pkgs.libopencm3-stm32g4; + PREFIX = "arm-none-eabi-"; + }; + + formatter = + let + inherit (self.checks.${system}.pre-commit-check.config) package configFile; + script = '' + ${pkgs.lib.getExe package} run --all-files --config ${configFile} + ''; + in + pkgs.writeShellScriptBin "pre-commit-run" script; + + checks = { + build-packages = pkgs.linkFarm "flake-packages-${system}" self.packages.${system}; + + pre-commit-check = pre-commit-hooks.lib.${system}.run { + src = ./.; + hooks = { + nixfmt.enable = true; + clang-format = { + enable = true; + types_or = pkgs.lib.mkForce [ + "c" + "c++" + ]; + }; + }; + }; + }; + } + ); +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..89db70e --- /dev/null +++ b/src/main.c @@ -0,0 +1,85 @@ +#include +#include +#include + +#define LED_OR_PORT GPIOC +#define LED_OR_PIN GPIO10 + +#define LED_GR_PORT GPIOC +#define LED_GR_PIN GPIO11 + +#define LED_RD_PORT GPIOC +#define LED_RD_PIN GPIO12 + +#define COUNT_PERIOD_MS 500 + +static volatile uint32_t s_ticks = 0; + +void sys_tick_handler(void) { s_ticks++; } + +static void delay_ms(uint32_t ms) { + uint32_t until = s_ticks + ms; + while (s_ticks < until) + ; +} + +static void clock_setup(void) { + rcc_clock_setup_pll(&rcc_hsi_configs[RCC_CLOCK_3V3_170MHZ]); +} + +static void systick_setup(void) { + /* 1 ms tick at 170 MHz core clock */ + systick_set_reload(rcc_ahb_frequency / 1000 - 1); + systick_set_clocksource(STK_CSR_CLKSOURCE_AHB); + systick_counter_enable(); + systick_interrupt_enable(); +} + +static void gpio_setup(void) { + rcc_periph_clock_enable(RCC_GPIOC); + + gpio_mode_setup(LED_OR_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_OR_PIN); + gpio_mode_setup(LED_GR_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_GR_PIN); + gpio_mode_setup(LED_RD_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LED_RD_PIN); + + gpio_set_output_options(LED_OR_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_LOW, + LED_OR_PIN); + gpio_set_output_options(LED_GR_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_LOW, + LED_GR_PIN); + gpio_set_output_options(LED_RD_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_LOW, + LED_RD_PIN); + + gpio_clear(LED_OR_PORT, LED_OR_PIN); + gpio_clear(LED_GR_PORT, LED_GR_PIN); + gpio_set(LED_RD_PORT, LED_RD_PIN); // active low +} + +static void gpio_write(uint32_t gpioport, uint16_t gpios, uint8_t value) { + if (value == 0) { + gpio_clear(gpioport, gpios); + } else { + gpio_set(gpioport, gpios); + } +} + +static void set_binary_counter(uint8_t value) { + gpio_write(LED_OR_PORT, LED_OR_PIN, value & 0x04); + gpio_write(LED_GR_PORT, LED_GR_PIN, value & 0x02); + gpio_write(LED_RD_PORT, LED_RD_PIN, value & 0x01); +} + +int main(void) { + clock_setup(); + systick_setup(); + gpio_setup(); + + uint8_t counter = 0; + + while (1) { + set_binary_counter(counter); + delay_ms(COUNT_PERIOD_MS); + counter++; // overflow is fine + } + + return 0; +}