Skip to content

Commit

Permalink
Add HOLLY and LPM HOLLY firmware
Browse files Browse the repository at this point in the history
  • Loading branch information
jaygreco committed Oct 2, 2023
1 parent 61522df commit 0082e96
Show file tree
Hide file tree
Showing 29 changed files with 2,997 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.build
build/
2 changes: 0 additions & 2 deletions README.md

This file was deleted.

23 changes: 23 additions & 0 deletions build_lpm_firmware.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash -e

# This script is used to compile the holly low-power mode firmware using the pico-sdk.
# It then exports it to 'flash.h' for inclusion in the QMK firmware.
# This does not need to be re-run unless the low-power firmware is changed.

# push into the script directory so that relative paths are correct
SCRIPT_PATH=$(dirname "$(realpath -s "$0")")
pushd "$SCRIPT_PATH" || exit 1
pushd holly_lpm || exit 1

# build the firmware
mkdir -p build && pushd build && cmake .. -DPICO_BOARD="holly"; make -j"$(nproc)"
LPM_FW_PATH="$(realpath holly_lpm.bin)"
popd || exit 1

# get the path to uf2conv.py
UF2CONV="$(realpath util/uf2conv.py)"
popd || exit 1
python3 "$UF2CONV" "$LPM_FW_PATH" --carray

# build the QMK firmware
# qmk compile -kb nullbitsco/holly -km via
26 changes: 26 additions & 0 deletions config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
Copyright 2021 Jay Greco
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/* NOTE: This config file is specific to RP2040 builds. */

#pragma once

#define RGBLIGHT_DEFAULT_MODE RGBLIGHT_MODE_CHRISTMAS

/* RP2040-specific defines*/
#define PICO_XOSC_STARTUP_DELAY_MULTIPLIER 64
#define RP2040_FLASH_GENERIC_03H
938 changes: 938 additions & 0 deletions flash.h

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions halconf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright 2022 Jay Greco
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#define HAL_USE_ADC TRUE

#include_next <halconf.h>
74 changes: 74 additions & 0 deletions holly.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2022 Jay Greco
// SPDX-License-Identifier: GPL-2.0-or-later

#include "holly.h"
#include "flash.h"
#include "usb_util.h"
#include "analog.h"

static void disable_interrupts(void)
{
SysTick_CTRL &= ~1;

NVIC_ICER = 0xFFFFFFFF;
NVIC_ICPR = 0xFFFFFFFF;
}

static void reset_peripherals(void)
{
RESET = ~(
RESETS_RESET_IO_QSPI_BITS |
RESETS_RESET_PADS_QSPI_BITS |
RESETS_RESET_SYSCFG_BITS |
RESETS_RESET_PLL_SYS_BITS
);
}

static void jump_to_vtor(uint32_t vtor)
{
uint32_t reset_vector = *(volatile uint32_t *)(vtor + 0x04);

SCB_VTOR = (volatile uint32_t)(vtor);

asm volatile("msr msp, %0"::"g" (*(volatile uint32_t *)vtor));
asm volatile("bx %0"::"r" (reset_vector));
}

void keyboard_post_init_kb(void) {
#ifdef CONSOLE_ENABLE
debug_enable = true;
debug_matrix = true;
#endif

keyboard_post_init_user();

#define SAMPLE_PERIODS 10
#define MIN_READINGS 800

int rolling_sum = 0;
for (int i=0; i<SAMPLE_PERIODS; i++) {
uint16_t reading = analogReadPin(VIN_PIN);
rolling_sum += (int)reading;
dprintf("adc reading: %u\n", reading);
wait_ms(100);
}

rolling_sum /= SAMPLE_PERIODS;
dprintf("final avg: %d\n", rolling_sum);

// If either of the following are true, assume that we're running on battery power
// Jump into the low-power optimized firmware, which will last much longer
if (!usb_connected_state() || rolling_sum < MIN_READINGS) {
dprintf("jumping to: %lu\n", (uint32_t)&bindata);
wait_ms(100);
disable_interrupts();
reset_peripherals();
jump_to_vtor((uint32_t)&bindata);
} else {
// Turn on the RGB LED power supplies
setPinOutput(LED_VCC1);
setPinOutput(LED_VCC2);
writePinHigh(LED_VCC1);
writePinHigh(LED_VCC2);
}
}
22 changes: 22 additions & 0 deletions holly.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2022 Jay Greco
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "quantum.h"
#include <hal.h>

#define LED_VCC1 GP7
#define LED_VCC2 GP6
#define VIN_PIN GP29

#define RESETS_RESET_IO_QSPI_BITS 0x00000040
#define RESETS_RESET_PADS_QSPI_BITS 0x00000200
#define RESETS_RESET_SYSCFG_BITS 0x00040000
#define RESETS_RESET_PLL_SYS_BITS 0x00001000

#define SysTick_CTRL (*(uint32_t *)(0xE0000000UL + 0xE010UL))
#define NVIC_ICER (*(uint32_t *)(0xE0000000UL + 0xE180UL))
#define NVIC_ICPR (*(uint32_t *)(0xE0000000UL + 0xE280UL))
#define RESET (*(uint32_t *)(0x4000C000UL + 0x2000UL))
#define SCB_VTOR (*(uint32_t *)(0xE0000000UL + 0xED08UL))
46 changes: 46 additions & 0 deletions holly_lpm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
cmake_minimum_required(VERSION 3.13)

set(PICO_SDK_FETCH_FROM_GIT on)

include(pico_sdk_import.cmake)
project(test_project C CXX ASM)
set(PICO_BOARD_HEADER_DIRS ${CMAKE_SOURCE_DIR})

pico_sdk_init()

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

add_executable(holly_lpm holly_lpm.c)

target_sources(holly_lpm PUBLIC
${CMAKE_CURRENT_LIST_DIR}/clocks.c
${CMAKE_CURRENT_LIST_DIR}/peripherals.c
${CMAKE_CURRENT_LIST_DIR}/sleep.c
)

target_link_libraries(holly_lpm pico_stdlib hardware_pio hardware_rtc hardware_clocks)

pico_add_extra_outputs(holly_lpm)

pico_enable_stdio_usb(holly_lpm 0)
pico_enable_stdio_uart(holly_lpm 0)

# Check if the -DBOOT2 flag is defined
# -DBOOT2 enables boot2
if (DEFINED BOOT2 AND BOOT2 EQUAL 1)
message(STATUS "BOOT2 flag is ${BOOT2}, building with boot2")
else ()
message(STATUS "BOOT2 flag is not set, building without boot2")
pico_set_linker_script(holly_lpm ${CMAKE_CURRENT_LIST_DIR}/memmap_custom.ld)
endif ()

# Check if the -DDEBUG flag is defined
# -DDEBUG enables DEBUG and turns on debug via USB
if (DEFINED DEBUG AND DEBUG EQUAL 1)
message(STATUS "DEBUG flag is ${DEBUG}, building with debug")
pico_enable_stdio_usb(holly_lpm 1)
set_target_properties(holly_lpm PROPERTIES OUTPUT_NAME "holly_lpm_debug")
else ()
message(STATUS "DEBUG flag is not set, building NODEBUG")
endif ()
163 changes: 163 additions & 0 deletions holly_lpm/clocks.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
#include "clocks.h"

#include <stdio.h>
#include <stdlib.h>
#include "pico/stdlib.h"

#include "hardware/clocks.h"
#include "hardware/pll.h"
#include "hardware/xosc.h"
#include "hardware/structs/rosc.h"

const uint32_t clocks_hw_wake_en0 = ~(
CLOCKS_WAKE_EN0_CLK_SYS_SPI1_BITS |
CLOCKS_WAKE_EN0_CLK_PERI_SPI1_BITS |
CLOCKS_WAKE_EN0_CLK_SYS_SPI0_BITS |
CLOCKS_WAKE_EN0_CLK_PERI_SPI0_BITS |
CLOCKS_WAKE_EN0_CLK_SYS_PWM_BITS |
CLOCKS_WAKE_EN0_CLK_SYS_PIO1_BITS |
CLOCKS_WAKE_EN0_CLK_SYS_JTAG_BITS |
CLOCKS_WAKE_EN0_CLK_SYS_I2C1_BITS |
CLOCKS_WAKE_EN0_CLK_SYS_I2C0_BITS |
CLOCKS_WAKE_EN0_CLK_SYS_DMA_BITS |
CLOCKS_WAKE_EN0_CLK_SYS_ADC_BITS |
CLOCKS_WAKE_EN0_CLK_ADC_ADC_BITS );

const uint32_t clocks_hw_wake_en1 = ~(
CLOCKS_WAKE_EN1_CLK_SYS_WATCHDOG_BITS |
#if !LIB_PICO_STDIO_USB
CLOCKS_WAKE_EN1_CLK_USB_USBCTRL_BITS |
CLOCKS_WAKE_EN1_CLK_SYS_USBCTRL_BITS |
#endif
CLOCKS_WAKE_EN1_CLK_SYS_UART0_BITS |
CLOCKS_WAKE_EN1_CLK_PERI_UART0_BITS );

const uint32_t clocks_hw_sleep_en0 = ~(
CLOCKS_SLEEP_EN0_CLK_SYS_SPI1_BITS |
CLOCKS_SLEEP_EN0_CLK_PERI_SPI1_BITS |
CLOCKS_SLEEP_EN0_CLK_SYS_SPI0_BITS |
CLOCKS_SLEEP_EN0_CLK_PERI_SPI0_BITS |
CLOCKS_SLEEP_EN0_CLK_SYS_ROM_BITS |
CLOCKS_SLEEP_EN0_CLK_SYS_SIO_BITS |
CLOCKS_SLEEP_EN0_CLK_SYS_PWM_BITS |
CLOCKS_SLEEP_EN0_CLK_SYS_PSM_BITS |
CLOCKS_SLEEP_EN0_CLK_SYS_PIO1_BITS |
CLOCKS_SLEEP_EN0_CLK_SYS_PADS_BITS |
CLOCKS_SLEEP_EN0_CLK_SYS_JTAG_BITS |
CLOCKS_SLEEP_EN0_CLK_SYS_I2C1_BITS |
CLOCKS_SLEEP_EN0_CLK_SYS_I2C0_BITS |
CLOCKS_SLEEP_EN0_CLK_SYS_DMA_BITS |
CLOCKS_SLEEP_EN0_CLK_SYS_ADC_BITS |
CLOCKS_SLEEP_EN0_CLK_ADC_ADC_BITS );

const uint32_t clocks_hw_sleep_en1 = ~(
CLOCKS_SLEEP_EN1_CLK_SYS_XIP_BITS |
CLOCKS_SLEEP_EN1_CLK_SYS_WATCHDOG_BITS |
#if !LIB_PICO_STDIO_USB
CLOCKS_SLEEP_EN1_CLK_USB_USBCTRL_BITS |
CLOCKS_SLEEP_EN1_CLK_SYS_USBCTRL_BITS |
#endif
CLOCKS_SLEEP_EN1_CLK_SYS_UART1_BITS |
CLOCKS_SLEEP_EN1_CLK_PERI_UART1_BITS |
CLOCKS_SLEEP_EN1_CLK_SYS_UART0_BITS |
CLOCKS_SLEEP_EN1_CLK_PERI_UART0_BITS );

static inline void rosc_write(io_rw_32 *addr, uint32_t value) {
hw_clear_bits(&rosc_hw->status, ROSC_STATUS_BADWRITE_BITS);
*addr = value;
};

static void rosc_set_range(uint range) {
rosc_write(&rosc_hw->ctrl, (ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB) | range);
}

void measure_freqs(void) {
uint f_pll_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_SYS_CLKSRC_PRIMARY);
uint f_pll_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_USB_CLKSRC_PRIMARY);
uint f_rosc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC_PH);
uint f_xosc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_XOSC_CLKSRC);
uint f_clk_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_SYS);
uint f_clk_peri = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_PERI);
uint f_clk_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_USB);
uint f_clk_adc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_ADC);
uint f_clk_rtc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_RTC);

DBG_PRINTF("pll_sys = %dkHz\n", f_pll_sys);
DBG_PRINTF("pll_usb = %dkHz\n", f_pll_usb);
DBG_PRINTF("rosc = %dkHz\n", f_rosc);
DBG_PRINTF("xosc = %dkHz\n", f_xosc);
DBG_PRINTF("clk_sys = %dkHz\n", f_clk_sys);
DBG_PRINTF("clk_peri = %dkHz\n", f_clk_peri);
DBG_PRINTF("clk_usb = %dkHz\n", f_clk_usb);
DBG_PRINTF("clk_adc = %dkHz\n", f_clk_adc);
DBG_PRINTF("clk_rtc = %dkHz\n", f_clk_rtc);

DBG_PRINTF("sysclk_get_hz: %d\n", clock_get_hz(clk_sys));
}

void configure_clocks(void) {
#if LIB_PICO_STDIO_USB
stdio_init_all();
sleep_ms(1000);
#endif

// Bump up ROSC freq before switching over
// rosc_set_range(ROSC_CTRL_FREQ_RANGE_VALUE_MEDIUM);
uint f_rosc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC);

// CLK_REF = XOSC or ROSC
clock_configure(clk_ref,
CLOCKS_CLK_REF_CTRL_SRC_VALUE_ROSC_CLKSRC_PH,
0, // No aux mux
f_rosc * KHZ,
f_rosc * KHZ);

// CLK SYS = CLK_REF
clock_configure(clk_sys,
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLK_REF,
0, // Using glitchless mux
f_rosc * KHZ,
f_rosc * KHZ);

#if !LIB_PICO_STDIO_USB
clock_stop(clk_usb);
#endif
clock_stop(clk_adc);

// CLK RTC = clk_sys
clock_configure(clk_rtc,
0, // No GLMUX
CLOCKS_CLK_RTC_CTRL_AUXSRC_VALUE_ROSC_CLKSRC_PH,
f_rosc * KHZ,
46875);

// CLK PERI = clk_sys. Used as reference clock for Peripherals. No dividers so just select and enable
clock_configure(clk_peri,
0,
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
f_rosc * KHZ,
f_rosc * KHZ);

pll_deinit(pll_sys);
#if !LIB_PICO_STDIO_USB
pll_deinit(pll_usb);
xosc_disable();
#else
stdio_init_all();
sleep_ms(1000);
measure_freqs();
#endif

// Set default sleep and wake clocks
clocks_hw->wake_en0 = clocks_hw_wake_en0;
clocks_hw->wake_en1 = clocks_hw_wake_en1;

clocks_hw->sleep_en0 = clocks_hw_sleep_en0;
clocks_hw->sleep_en1 = clocks_hw_sleep_en1;
}

__force_inline void restore_clocks(void) {
// Reset sleep-mode clocks back to default
clocks_hw->sleep_en0 = clocks_hw_sleep_en0;
clocks_hw->sleep_en1 = clocks_hw_sleep_en1;
}
5 changes: 5 additions & 0 deletions holly_lpm/clocks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#pragma once

void measure_freqs(void);
void configure_clocks(void);
void restore_clocks(void);
Loading

0 comments on commit 0082e96

Please sign in to comment.