commit 823f030974091aaf30d8fec3d5e1175e348cd1fe Author: sid Date: Wed May 13 18:23:02 2026 +0200 initial commit 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; +}