a vim plugin that displays stuff on an led matrix

feat: add inital firmware

dunkirk.sh 379f8b0a 079884a4

verified
+71
.clang-format
···
+
---
+
Language: Cpp
+
BasedOnStyle: Google
+
IndentWidth: 2
+
TabWidth: 2
+
UseTab: Never
+
ColumnLimit: 100
+
AlignAfterOpenBracket: Align
+
AlignConsecutiveAssignments: false
+
AlignConsecutiveDeclarations: false
+
AlignEscapedNewlines: Left
+
AlignOperands: true
+
AlignTrailingComments: true
+
AllowAllParametersOfDeclarationOnNextLine: false
+
AllowShortBlocksOnASingleLine: false
+
AllowShortCaseLabelsOnASingleLine: false
+
AllowShortFunctionsOnASingleLine: Inline
+
AllowShortIfStatementsOnASingleLine: false
+
AllowShortLoopsOnASingleLine: false
+
AlwaysBreakAfterReturnType: None
+
AlwaysBreakBeforeMultilineStrings: true
+
AlwaysBreakTemplateDeclarations: Yes
+
BinPackArguments: true
+
BinPackParameters: true
+
BreakBeforeBinaryOperators: None
+
BreakBeforeBraces: Attach
+
BreakBeforeTernaryOperators: true
+
BreakConstructorInitializers: BeforeColon
+
BreakInheritanceList: BeforeColon
+
BreakStringLiterals: true
+
CompactNamespaces: false
+
ConstructorInitializerAllOnOneLineOrOnePerLine: true
+
ConstructorInitializerIndentWidth: 4
+
ContinuationIndentWidth: 4
+
Cpp11BracedListStyle: true
+
DerivePointerAlignment: false
+
FixNamespaceComments: true
+
IncludeBlocks: Regroup
+
IncludeCategories:
+
- Regex: '^<.*\.h>'
+
Priority: 1
+
- Regex: '^<.*'
+
Priority: 2
+
- Regex: '.*'
+
Priority: 3
+
IndentCaseLabels: true
+
IndentPPDirectives: None
+
IndentWrappedFunctionNames: false
+
KeepEmptyLinesAtTheStartOfBlocks: false
+
MaxEmptyLinesToKeep: 1
+
NamespaceIndentation: None
+
PointerAlignment: Left
+
ReflowComments: true
+
SortIncludes: true
+
SortUsingDeclarations: true
+
SpaceAfterCStyleCast: false
+
SpaceAfterTemplateKeyword: true
+
SpaceBeforeAssignmentOperators: true
+
SpaceBeforeCpp11BracedList: false
+
SpaceBeforeCtorInitializerColon: true
+
SpaceBeforeInheritanceColon: true
+
SpaceBeforeParens: ControlStatements
+
SpaceBeforeRangeBasedForLoopColon: true
+
SpaceInEmptyParentheses: false
+
SpacesBeforeTrailingComments: 2
+
SpacesInAngles: false
+
SpacesInContainerLiterals: false
+
SpacesInCStyleCastParentheses: false
+
SpacesInParentheses: false
+
SpacesInSquareBrackets: false
+
Standard: Cpp11
+1
.envrc
···
+
use flake
+3
.gitignore
···
+
.direnv
+
result
+
build/
+69
CRUSH.md
···
+
# CRUSH.md - Daedalus Project Guidelines
+
+
## Build Commands
+
- Setup environment: `direnv allow`
+
- Build firmware: `cd firmware && make`
+
- Flash to RP2350: `cd firmware && make flash`
+
- Disassemble: `cd firmware && make disasm`
+
- Clean build: `cd firmware && make clean`
+
- Format code: `clang-format -i firmware/src/*.cpp firmware/src/*.h`
+
- Lint code: `cppcheck --enable=all firmware/src/`
+
+
## Test Commands
+
- Run all tests: `ctest -S build`
+
- Run single test: `ctest -R <test_name> -V`
+
+
## Code Style Guidelines
+
+
### General
+
- Use 2-space indentation
+
- Max line length: 100 characters
+
- UTF-8 encoding for all files
+
- Follow Google C++ Style Guide with modifications in .clang-format
+
+
### Naming Conventions
+
- Classes: PascalCase (e.g., `LedController`)
+
- Functions: camelCase (e.g., `initializeHardware()`)
+
- Variables: camelCase (e.g., `ledPin`)
+
- Constants: UPPER_SNAKE_CASE (e.g., `MAX_BUFFER_SIZE`)
+
- Files: snake_case.ext (e.g., `led_controller.cpp`)
+
+
### Error Handling
+
- Use return codes for error reporting
+
- Check return values from SDK functions
+
- Use assertions for invariants
+
- Avoid dynamic memory allocation when possible
+
+
### Nix Development
+
- Pico SDK is provided via nixpkgs with withSubmodules override
+
- All tools are managed through Nix
+
- Use `nix flake update` to update dependencies
+
+
## Code Style Guidelines
+
+
### General
+
- Use 2-space indentation
+
- Max line length: 100 characters
+
- UTF-8 encoding for all files
+
+
### Naming Conventions
+
- Functions/methods: camelCase
+
- Variables: camelCase
+
- Constants: UPPER_SNAKE_CASE
+
- Classes: PascalCase
+
- Files: kebab-case.ext
+
+
### Error Handling
+
- Use explicit error handling (no silent failures)
+
- Log errors with appropriate context
+
- Return meaningful error messages
+
+
### Comments
+
- Use JSDoc-style comments for functions
+
- Keep comments current with code changes
+
- Explain "why" not "what" in comments
+
+
### Git Practices
+
- Commit messages: Start with verb in present tense
+
- Keep commits focused on single changes
+
- Reference issues in commit messages when applicable
+36
firmware/CMakeLists.txt
···
+
cmake_minimum_required(VERSION 3.25)
+
+
# Need to include pico-sdk cmake support. Must happen before project.
+
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
+
+
project(daedalus_firmware
+
LANGUAGES C CXX ASM
+
VERSION 1.0
+
)
+
+
set(CMAKE_C_STANDARD 11)
+
set(CMAKE_CXX_STANDARD 17)
+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Get compilation DB
+
+
# initialize the Raspberry Pi Pico SDK
+
pico_sdk_init()
+
+
# Tell CMake where to find the executable source file
+
add_executable(${PROJECT_NAME}
+
src/main.cpp
+
)
+
+
# Create map/bin/hex/uf2 files
+
pico_add_extra_outputs(${PROJECT_NAME})
+
+
# Link to pico_stdlib (gpio, time, etc. functions)
+
target_link_libraries(${PROJECT_NAME}
+
pico_stdlib
+
hardware_gpio
+
hardware_uart
+
pico_multicore
+
)
+
+
# Choice of available stdio outputs
+
pico_enable_stdio_usb(${PROJECT_NAME} 1) # ~13880 extra bytes
+
pico_enable_stdio_uart(${PROJECT_NAME} 0) # ~1176 extra bytes
+30
firmware/Makefile
···
+
# Little driver makefile to make the complicated bootloader less of a hassle.
+
+
PROJECT=daedalus_firmware
+
+
TOOLCHAIN_PREFIX=arm-none-eabi-
+
+
# Check if FORCE_FLASH is set
+
PICOTOOL_OPTS = $(if $(FORCE_FLASH),-f,)
+
+
build/$(PROJECT).uf2:
+
+
flash : build/$(PROJECT).uf2
+
picotool load $(PICOTOOL_OPTS) $<
+
picotool reboot
+
+
build/$(PROJECT).uf2 build/$(PROJECT).elf: build FORCE
+
$(MAKE) -C build
+
$(TOOLCHAIN_PREFIX)size build/$(PROJECT).elf
+
$(TOOLCHAIN_PREFIX)nm --print-size --size-sort --radix=d build/$(PROJECT).elf | awk '{printf("%5d %s\n", $$2, $$4);}' | sort -nr | head -20
+
+
disasm: build/$(PROJECT).elf
+
$(TOOLCHAIN_PREFIX)objdump -C -S build/$(PROJECT).elf
+
+
build:
+
cmake -B build -DCMAKE_VERBOSE_MAKEFILE=ON -DPICO_BOARD=pico2
+
+
clean:
+
rm -rf build
+
+
FORCE:
+33
firmware/README.md
···
+
# Daedalus Firmware
+
+
This directory contains the firmware for the RP2350 board.
+
+
## Building
+
+
1. Make sure you have the Pico SDK set up (automatically handled by the Nix flake)
+
2. Configure the build:
+
```
+
cmake -B build -S .
+
```
+
3. Build the firmware:
+
```
+
cmake --build build
+
```
+
4. The output files will be in the `build` directory:
+
- `daedalus_firmware.uf2`: UF2 file for drag-and-drop programming
+
- `daedalus_firmware.elf`: ELF file for debugging
+
- `daedalus_firmware.hex`: HEX file for programming with external tools
+
+
## Flashing
+
+
Connect the RP2350 board in bootloader mode (hold BOOTSEL while connecting USB) and copy the UF2 file:
+
+
```
+
cp build/daedalus_firmware.uf2 /path/to/rp2350/
+
```
+
+
Or use picotool:
+
+
```
+
picotool load -x build/daedalus_firmware.uf2
+
```
+30
firmware/STYLE.md
···
+
# C++ style guide for Daedalus firmware
+
+
## Formatting
+
- Use clang-format with the provided .clang-format file
+
- Run before committing: `clang-format -i firmware/src/*.cpp firmware/src/*.h`
+
+
## Naming
+
- Classes: PascalCase (e.g., `LedController`)
+
- Functions: camelCase (e.g., `initializeHardware()`)
+
- Variables: camelCase (e.g., `ledPin`)
+
- Constants: UPPER_SNAKE_CASE (e.g., `MAX_BUFFER_SIZE`)
+
- Macros: UPPER_SNAKE_CASE (e.g., `DEBUG_ENABLE`)
+
- Files: snake_case (e.g., `led_controller.cpp`)
+
+
## Headers
+
- Use `#pragma once` for header guards
+
- Order includes: standard library, Pico SDK, project headers
+
+
## Comments
+
- Use Doxygen-style comments for functions and classes
+
- Comment complex algorithms and non-obvious code
+
+
## Error handling
+
- Use return codes for error reporting
+
- Check return values from SDK functions
+
- Use assertions for invariants
+
+
## Memory management
+
- Avoid dynamic memory allocation when possible
+
- If needed, use RAII principles with smart pointers
+65
firmware/pico_sdk_import.cmake
···
+
# This file is copied from the Pico SDK
+
# It allows the CMake project to find the Pico SDK
+
+
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
+
+
# This can be dropped into an external project to help locate the Pico SDK
+
# It should be included prior to project()
+
+
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
+
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
+
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
+
endif ()
+
+
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
+
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
+
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
+
endif ()
+
+
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
+
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
+
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
+
endif ()
+
+
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
+
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
+
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
+
+
if (NOT PICO_SDK_PATH)
+
if (PICO_SDK_FETCH_FROM_GIT)
+
include(FetchContent)
+
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
+
if (PICO_SDK_FETCH_FROM_GIT_PATH)
+
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
+
endif ()
+
FetchContent_Declare(
+
pico_sdk
+
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
+
GIT_TAG master
+
)
+
if (NOT pico_sdk)
+
message("Downloading Raspberry Pi Pico SDK")
+
FetchContent_Populate(pico_sdk)
+
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
+
endif ()
+
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
+
else ()
+
message(FATAL_ERROR
+
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
+
)
+
endif ()
+
endif ()
+
+
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
+
if (NOT EXISTS ${PICO_SDK_PATH})
+
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
+
endif ()
+
+
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
+
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
+
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
+
endif ()
+
+
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
+
+
include(${PICO_SDK_INIT_CMAKE_FILE})
+122
firmware/src/main.cpp
···
+
#include <stdlib.h>
+
+
#include <cstring>
+
+
#include "pico/multicore.h"
+
#include "pico/stdlib.h"
+
+
// Define GPIO pins for rows and columns
+
const uint ROW_PINS[4] = {12, 13, 14, 15};
+
const uint COL_PINS[6] = {17, 18, 19, 20, 21, 22};
+
+
#define ROWS 4
+
#define COLS 6
+
+
// LED state matrix
+
uint8_t led_states[ROWS][COLS] = {0};
+
+
void setup_pins() {
+
// Set up row pins
+
for (int i = 0; i < ROWS; i++) {
+
gpio_init(ROW_PINS[i]);
+
gpio_set_dir(ROW_PINS[i], GPIO_OUT);
+
gpio_put(ROW_PINS[i], 0); // Start with LEDs off
+
}
+
+
// Set up column pins
+
for (int j = 0; j < COLS; j++) {
+
gpio_init(COL_PINS[j]);
+
gpio_set_dir(COL_PINS[j], GPIO_OUT);
+
gpio_put(COL_PINS[j], 1); // Start with LEDs off
+
}
+
}
+
+
// Function to update the display at variable frequencies
+
void update_led_display(uint32_t refresh_rate_hz) {
+
static uint32_t last_update = 0;
+
uint32_t current_time = to_ms_since_boot(get_absolute_time());
+
uint32_t update_interval = 1000 / refresh_rate_hz;
+
+
// Only update at the specified frequency
+
if (current_time - last_update < update_interval) {
+
return;
+
}
+
+
last_update = current_time;
+
+
// Display the current state
+
for (int row = 0; row < ROWS; row++) {
+
// Set row active
+
gpio_put(ROW_PINS[row], 1);
+
+
// Set column states for this row
+
for (int col = 0; col < COLS; col++) {
+
// LED on = column LOW, LED off = column HIGH
+
gpio_put(COL_PINS[col], led_states[row][col] ? 0 : 1);
+
}
+
+
// Small delay for this row to be visible
+
sleep_us(100);
+
+
// Deactivate row
+
gpio_put(ROW_PINS[row], 0);
+
}
+
}
+
+
// Function to set a specific LED state
+
void set_led(int row, int col, bool state) {
+
if (row >= 0 && row < ROWS && col >= 0 && col < COLS) {
+
led_states[row][col] = state ? 1 : 0;
+
}
+
}
+
+
// Function to set the entire matrix state
+
void set_matrix_state(const uint8_t new_state[ROWS][COLS]) {
+
memcpy(led_states, new_state, sizeof(led_states));
+
}
+
+
void random_state(float density) {
+
// clamp
+
if (density > 1.0f)
+
density = 1.0f;
+
if (density < 0.0f)
+
density = 0.0f;
+
+
for (int row = 0; row < ROWS; row++) {
+
for (int col = 0; col < COLS; col++) {
+
float r = (float)rand() / (float)RAND_MAX;
+
led_states[row][col] = (r < density) ? 1 : 0;
+
}
+
}
+
}
+
+
const uint8_t ALL_ON[ROWS][COLS] = {{1, 1, 1, 1, 1, 1},
+
{1, 1, 1, 1, 1, 1},
+
{1, 1, 1, 1, 1, 1},
+
{1, 1, 1, 1, 1, 1}};
+
+
const uint8_t ALL_OFF[ROWS][COLS] = {{0, 0, 0, 0, 0, 0},
+
{0, 0, 0, 0, 0, 0},
+
{0, 0, 0, 0, 0, 0},
+
{0, 0, 0, 0, 0, 0}};
+
+
void core1_main() {
+
while (true) {
+
update_led_display(1000);
+
}
+
}
+
+
int main() {
+
stdio_init_all();
+
setup_pins();
+
+
multicore_launch_core1(core1_main);
+
+
while (true) {
+
random_state(0.5); // Turn random LEDs ON
+
// set_matrix_state(ALL_ON);
+
sleep_ms(200);
+
// set_matrix_state(ALL_OFF);
+
// sleep_ms(500);
+
}
+
}
+61
flake.lock
···
+
{
+
"nodes": {
+
"flake-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"
+
}
+
},
+
"nixpkgs": {
+
"locked": {
+
"lastModified": 1756542300,
+
"narHash": "sha256-tlOn88coG5fzdyqz6R93SQL5Gpq+m/DsWpekNFhqPQk=",
+
"owner": "NixOS",
+
"repo": "nixpkgs",
+
"rev": "d7600c775f877cd87b4f5a831c28aa94137377aa",
+
"type": "github"
+
},
+
"original": {
+
"owner": "NixOS",
+
"ref": "nixos-unstable",
+
"repo": "nixpkgs",
+
"type": "github"
+
}
+
},
+
"root": {
+
"inputs": {
+
"flake-utils": "flake-utils",
+
"nixpkgs": "nixpkgs"
+
}
+
},
+
"systems": {
+
"locked": {
+
"lastModified": 1681028828,
+
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+
"owner": "nix-systems",
+
"repo": "default",
+
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+
"type": "github"
+
},
+
"original": {
+
"owner": "nix-systems",
+
"repo": "default",
+
"type": "github"
+
}
+
}
+
},
+
"root": "root",
+
"version": 7
+
}
+66
flake.nix
···
+
{
+
description = "Daedalus - RP2350 firmware and Vim plugin";
+
+
inputs = {
+
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+
flake-utils.url = "github:numtide/flake-utils";
+
};
+
+
outputs =
+
{
+
nixpkgs,
+
flake-utils,
+
...
+
}:
+
flake-utils.lib.eachDefaultSystem (
+
system:
+
let
+
pkgs = import nixpkgs {
+
inherit system;
+
};
+
+
# Needs to be synced past
+
# https://github.com/NixOS/nixpkgs/pull/321786
+
local-pico-sdk = pkgs.pico-sdk.override {
+
withSubmodules = true;
+
};
+
in
+
{
+
devShells.default = pkgs.mkShell {
+
buildInputs = with pkgs; [
+
# C/C++ development
+
gcc
+
gcc-arm-embedded
+
cmake
+
gnumake
+
+
# Pico SDK dependencies
+
git
+
python3
+
python3Packages.pip
+
python3Packages.setuptools
+
picotool
+
+
# Debugging tools
+
gdb
+
openocd
+
+
# Utilities
+
pkg-config
+
minicom
+
+
# Code quality
+
clang-tools # clang-format, clang-tidy
+
cppcheck
+
];
+
+
shellHook = ''
+
# Set environment variables
+
export PICO_SDK_PATH="${local-pico-sdk}/lib/pico-sdk"
+
+
echo "Daedalus development environment ready!"
+
'';
+
};
+
}
+
);
+
}