back to where we began, now with rpi

This commit is contained in:
Dominic DiTaranto 2026-02-20 17:59:59 -05:00
parent d4f8c94cd4
commit 90915b89f6
6 changed files with 211 additions and 86 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "lib/pico-ssd1306"]
path = lib/pico-ssd1306
url = https://github.com/Harbys/pico-ssd1306

View file

@ -23,11 +23,17 @@ add_executable(clock
pico_enable_stdio_usb(clock 1)
pico_enable_stdio_uart(clock 0)
target_include_directories(clock PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/lib)
add_subdirectory(lib/pico-ssd1306)
# Pull in standard library and hardware abstraction
target_link_libraries(clock
pico_stdlib
hardware_gpio
hardware_i2c
pico_multicore
pico_ssd1306
)
# Create the .uf2 file

View file

@ -1,47 +1,35 @@
#ifndef GLOBALS_H
#define GLOBALS_H
#include "pico/stdlib.h"
#include <cstdint>
static constexpr uint8_t OUT_1_PIN = 0;
static constexpr uint8_t OUT_2_PIN = 2;
static constexpr uint8_t OUT_3_PIN = 4;
static constexpr uint8_t OUT_4_PIN = 6;
static constexpr uint8_t OUT_5_PIN = 8;
static constexpr uint8_t OUT_6_PIN = 10;
static constexpr uint8_t OUT_7_PIN = 12;
static constexpr uint8_t OUT_8_PIN = 14;
static constexpr uint8_t SCREEN_SCL_PIN = 18;
static constexpr uint8_t SCREEN_SDA_PIN = 19;
static constexpr uint8_t ENCODER_CLK_PIN = 20;
static constexpr uint8_t ENCODER_DT_PIN = 21;
static constexpr uint8_t ENCODER_SW_PIN = 22;
extern volatile uint8_t BPM;
static constexpr uint32_t MINUTE_US = 60000000;
static constexpr uint8_t PPQN = 96;
inline uint32_t millis() {
return to_ms_since_boot(get_absolute_time());
}
inline uint32_t micros() {
return to_us_since_boot(get_absolute_time());
}
#endif // GLOBALS_H
/*
TODO:
PRE-DAC:
[x] Figure out multiplicative beats X2 X4 X8 X16 X32?
[ ] Swing/Phase (same thing)
[x] Probability
[ ] Humanization
[ ] Euclidian Rhythms
[ ] Steps - # of steps for a full pattern
[ ] Hits - how many hits across the steps, must be less than steps
[ ] Offset - move the starting point of the pattern
[ ] Logic (NO | AND | OR | XOR)
[ ] Mute
[ ] Save
[ ] Load
POST-DAC:
[ ] Different Wave Forms
[ ] Different Voltage levels
[ ] v/oct?
100BPM
4800BPM
POSSIBLE DIVISIONS:
1: x48
16: x32
32: x16
40: x8
44: x4
46: x2
---
48*1: 1 ** THIS NEEDS TO BE PASSED IN AS DIVIDE MODE
48*2 = 96: /2
48*4 = 192: /4
48*8 = /8
48*16 = /16
*/

1
lib/pico-ssd1306 Submodule

@ -0,0 +1 @@
Subproject commit 2cec467a06bb8cd89363e12091bfd2318f7feab6

View file

@ -32,10 +32,10 @@ void Gate::setLen(uint32_t currentPeriod) {
void Gate::setDiv(uint16_t modifier, uint8_t divide) {
if (divide == 1) {
div = ppqn * modifier;
div = PPQN * modifier;
divString = "/" + std::to_string(modifier);
} else {
div = ppqn / modifier;
div = PPQN / modifier;
divString = "x" + std::to_string(modifier);
}
divideMode = divide;
@ -45,9 +45,9 @@ void Gate::setDiv(uint16_t modifier, uint8_t divide) {
void Gate::setWidth(uint16_t newWidth) {
width = newWidth;
if (divideMode == 1) {
len = (uint32_t)((double)(minute / BPM) * (width / 100.0) / 1000.0);
len = (uint32_t)((double)(MINUTE_US / BPM) * (width / 100.0) / 1000.0);
} else {
len = (uint32_t)((double)(minute / BPM / modifier) * (width / 100.0) / 1000.0);
len = (uint32_t)((double)(MINUTE_US / BPM / modifier) * (width / 100.0) / 1000.0);
}
};
@ -69,7 +69,7 @@ void Gate::turnOn() {
if (pRes == 1) {
state = 1;
digitalWrite(pin, state);
gpio_put(pin, state);
dur = millis();
}
cycle = 0;
@ -79,7 +79,7 @@ void Gate::turnOn() {
void Gate::turnOff() {
if (state == 1 && millis() - dur >= len) {
state = 0;
digitalWrite(pin, state);
gpio_put(pin, state);
dur = 0;
};
}

View file

@ -1,37 +1,24 @@
#include <cstdint>
#include <cstdio>
#include <stdio.h>
#include "hardware/i2c.h"
#include "hardware/structs/rosc.h"
#include "pico/multicore.h"
#include "pico/stdlib.h"
#include "pico/time.h"
#include <cstdint>
#include "pico-ssd1306/ssd1306.h"
#include "pico-ssd1306/textRenderer/TextRenderer.h"
#include "globals.h"
#include "Gate.h"
static constexpr uint8_t OUT_1_PIN = 0;
static constexpr uint8_t OUT_2_PIN = 2;
static constexpr uint8_t OUT_3_PIN = 4;
static constexpr uint8_t OUT_4_PIN = 6;
static constexpr uint8_t OUT_5_PIN = 8;
static constexpr uint8_t OUT_6_PIN = 10;
static constexpr uint8_t OUT_7_PIN = 12;
static constexpr uint8_t OUT_8_PIN = 14;
static constexpr uint8_t SCREEN_SCL_PIN = 18;
static constexpr uint8_t SCREEN_SDA_PIN = 19;
static constexpr uint8_t ENCODER_CLK_PIN = 20;
static constexpr uint8_t ENCODER_DT_PIN = 21;
static constexpr uint8_t ENCODER_SW_PIN = 22;
struct repeating_timer bpm_timer = {0};
volatile uint8_t BPM = 60;
volatile uint8_t PLAY = 1;
volatile uint8_t BPM = 100;
static constexpr uint32_t MINUTE_US = 60000000;
static constexpr uint8_t PPQN = 96;
volatile uint32_t period_us = 0;
struct repeating_timer bpm_timer;
volatile bool beatToggle = false;
Gate out1(OUT_1_PIN);
@ -43,6 +30,13 @@ Gate out6(OUT_6_PIN);
Gate out7(OUT_7_PIN);
Gate out8(OUT_8_PIN);
volatile bool updateScreen = 1;
pico_ssd1306::SSD1306* display = nullptr;
volatile uint8_t encoder_pos = 0;
volatile bool button_pressed = 0;
volatile uint16_t clk_last_state;
bool timer_callback(struct repeating_timer *t) {
if (PLAY == 1) {
@ -64,33 +58,166 @@ void update_period() {
}
void update_BPM(bool up) {
if (up) {
BPM++;
} else {
BPM--;
}
update_period();
}
void gpio_callback(uint gpio, uint32_t events) {
if (gpio == ENCODER_SW_PIN) { // handle button press
} else if (gpio == ENCODER_CLK_PIN || gpio == ENCODER_DT_PIN) { // handle encoder turn
uint16_t clk_state = gpio_get(ENCODER_CLK_PIN);
uint16_t dt_state = gpio_get(ENCODER_DT_PIN);
if (clk_state != clk_last_state && clk_state == 1)
if (dt_state != clk_state) {
encoder_pos++; // Clockwise
update_BPM(1);
} else {
encoder_pos--; // Counter-clockwise
update_BPM(0);
}
updateScreen = 1;
clk_last_state = clk_state;
}
}
void setup_encoder() {
gpio_init(ENCODER_SW_PIN);
gpio_set_dir(ENCODER_SW_PIN, GPIO_IN);
gpio_pull_up(ENCODER_SW_PIN);
gpio_init(ENCODER_CLK_PIN);
gpio_set_dir(ENCODER_CLK_PIN, GPIO_IN);
gpio_pull_up(ENCODER_CLK_PIN);
gpio_init(ENCODER_DT_PIN);
gpio_set_dir(ENCODER_DT_PIN, GPIO_IN);
gpio_pull_up(ENCODER_DT_PIN);
clk_last_state = gpio_get(ENCODER_CLK_PIN);
gpio_set_irq_enabled_with_callback(20, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &gpio_callback);
gpio_set_irq_enabled(21, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true);
gpio_set_irq_enabled(22, GPIO_IRQ_EDGE_FALL, true);
}
void setup_screen() {
i2c_init(i2c1, 400 * 1000);
gpio_set_function(SCREEN_SDA_PIN, GPIO_FUNC_I2C);
gpio_set_function(SCREEN_SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(SCREEN_SDA_PIN);
gpio_pull_up(SCREEN_SCL_PIN);
display = new pico_ssd1306::SSD1306(i2c1, 0x3C, pico_ssd1306::Size::W128xH64);
display->setOrientation(0);
}
void core1_entry() {
multicore_fifo_pop_blocking();
char buffer[32];
while (true) {
if (updateScreen) {
display->clear();
snprintf(buffer, sizeof(buffer), "BPM: %u", BPM);
pico_ssd1306::drawText(display, font_12x16, buffer, 0, 0);
display->sendBuffer();
updateScreen = 0;
}
}
}
void setup_outs() {
Gate* my_outputs[] = {&out1, &out2, &out3, &out4, &out5, &out6, &out7, &out8};
for (auto g : my_outputs) {
gpio_init(g->pin);
gpio_set_dir(g->pin, GPIO_OUT);
g->setLen(period_us);
}
// manual setup
out1.setDiv(8, 0);
out1.setWidth(50);
out2.setDiv(4, 0);
out2.setWidth(50);
out3.setDiv(2, 0);
out3.setWidth(50);
out4.setDiv(1);
out4.setWidth(50);
out5.setDiv(2);
out5.setWidth(50);
out6.setDiv(4);
out6.setWidth(50);
out7.setDiv(8);
out7.setWidth(50);
out8.setDiv(16);
out8.setWidth(50);
}
int main() {
stdio_init_all();
srand(rosc_hw->randombit);
gpio_init(out1.pin);
gpio_init(out2.pin);
gpio_init(out3.pin);
gpio_init(out4.pin);
gpio_init(out5.pin);
gpio_init(out6.pin);
gpio_init(out7.pin);
gpio_init(out8.pin);
setup_screen();
multicore_launch_core1(core1_entry);
multicore_fifo_push_blocking(1);
gpio_set_dir(out1.pin, GPIO_OUT);
gpio_set_dir(out2.pin, GPIO_OUT);
gpio_set_dir(out3.pin, GPIO_OUT);
gpio_set_dir(out4.pin, GPIO_OUT);
gpio_set_dir(out5.pin, GPIO_OUT);
gpio_set_dir(out6.pin, GPIO_OUT);
gpio_set_dir(out7.pin, GPIO_OUT);
gpio_set_dir(out8.pin, GPIO_OUT);
setup_encoder();
setup_outs();
update_period();
while (true) {
if (beatToggle) {
out1.turnOn();
out2.turnOn();
out3.turnOn();
out4.turnOn();
out5.turnOn();
out6.turnOn();
out7.turnOn();
out8.turnOn();
beatToggle = false;
}
out1.turnOff();
out2.turnOff();
out3.turnOff();
out4.turnOff();
out5.turnOff();
out6.turnOff();
out7.turnOff();
out8.turnOff();
}
}