Compare commits
No commits in common. "e4ea8670463b15f8dc17a43ea83e03de088450c1" and "6532b218fe5807f4963878404430894071e9ee9c" have entirely different histories.
e4ea867046
...
6532b218fe
10 changed files with 153 additions and 428 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,3 +0,0 @@
|
|||
.clangd
|
||||
compile_commands.json
|
||||
|
||||
|
|
@ -29,13 +29,10 @@ target_include_directories(clock PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/lib)
|
|||
|
||||
add_subdirectory(lib/pico-ssd1306)
|
||||
|
||||
pico_generate_pio_header(clock ${CMAKE_CURRENT_LIST_DIR}/quadrature_encoder.pio)
|
||||
|
||||
# Pull in standard library and hardware abstraction
|
||||
target_link_libraries(clock
|
||||
pico_stdlib
|
||||
hardware_gpio
|
||||
hardware_pio
|
||||
hardware_i2c
|
||||
pico_multicore
|
||||
pico_ssd1306
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@
|
|||
|
||||
class EncoderHandler {
|
||||
private:
|
||||
uint sm;
|
||||
uint last_count;
|
||||
|
||||
|
||||
public:
|
||||
|
|
@ -25,7 +23,6 @@ class EncoderHandler {
|
|||
void setup();
|
||||
static void gpio_callback(uint gpio, uint32_t events);
|
||||
void moveCursor(bool dir = 1);
|
||||
void update();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -3,14 +3,16 @@
|
|||
#define Gate_h
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
|
||||
class Gate {
|
||||
private:
|
||||
bool state;
|
||||
int16_t cycle;
|
||||
uint32_t dur;
|
||||
uint32_t len;
|
||||
uint32_t lastTriggerTick = 0xFFFFFFFF;
|
||||
uint16_t div;
|
||||
|
||||
public:
|
||||
Gate(uint8_t pin);
|
||||
|
|
@ -19,8 +21,6 @@ class Gate {
|
|||
int8_t modifierSelectionIndex;
|
||||
uint8_t divideMode;
|
||||
uint16_t modifier;
|
||||
uint16_t tickInterval;
|
||||
bool isEnabled;
|
||||
uint8_t width;
|
||||
uint8_t p;
|
||||
|
||||
|
|
@ -29,6 +29,9 @@ class Gate {
|
|||
void setLen(uint32_t currentPeriod);
|
||||
void setDiv(uint8_t modifier_selection_index);
|
||||
void setWidth(uint16_t newWidth);
|
||||
void setP(uint16_t prob);
|
||||
|
||||
bool getState();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@
|
|||
#include <array>
|
||||
#include <string>
|
||||
|
||||
|
||||
// PINS
|
||||
static constexpr uint8_t OUT_1_PIN = 0;
|
||||
static constexpr uint8_t OUT_2_PIN = 2;
|
||||
static constexpr uint8_t OUT_3_PIN = 4;
|
||||
|
|
@ -20,36 +18,29 @@ static constexpr uint8_t OUT_8_PIN = 14;
|
|||
static constexpr uint8_t SCREEN_SCL_PIN = 18;
|
||||
static constexpr uint8_t SCREEN_SDA_PIN = 19;
|
||||
|
||||
// Modify moves per detent if your encoder is acting weird
|
||||
// for me, with 20 detents per full rotation, 4 works
|
||||
static constexpr uint8_t TICKS_PER_DETENT = 4;
|
||||
static constexpr uint8_t ENCODER_CLK_PIN = 20;
|
||||
static constexpr uint8_t ENCODER_DT_PIN = 21;
|
||||
static constexpr uint8_t ENCODER_SW_PIN = 22;
|
||||
|
||||
// TIME BASED
|
||||
extern volatile uint8_t BPM;
|
||||
|
||||
static constexpr uint32_t MINUTE_US = 60000000;
|
||||
static constexpr uint8_t PPQN = 96;
|
||||
extern volatile uint32_t MASTER_TICK;
|
||||
|
||||
static uint32_t MASTER_TICK = 0;
|
||||
|
||||
// Modifiers in UI order
|
||||
static std::array<uint8_t, 17> MODIFIERS = {32, 16, 12, 8, 6, 4, 3, 2, 0, 1, 2, 3, 4, 6, 8, 16, 32};
|
||||
static std::array<uint8_t, 10> MODIFIERS = {8, 4, 2, 0, 1, 2, 3, 4, 8, 16};
|
||||
// Modifier type; 0 = multiplicaton, 1 = division; matched with MODIFIERS
|
||||
static std::array<uint8_t, 17> MOD_TYPES = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1};
|
||||
static std::array<uint8_t, 10> MOD_TYPES = {0, 0, 0, 0, 1, 1, 1, 1, 1, 1};
|
||||
// Modifier string
|
||||
static std::array<std::string, 17> MODIFIER_STRINGS = {"x32", "x16", "x12", "x8", "x6", "x4", "x3", "x2", "x0", "/1", "/2", "/3", "/4", "/6", "/8", "/16", "/32"};
|
||||
|
||||
static std::array<std::string, 10> MODIFIER_STRINGS = {"x8", "x4", "x2", "x0", "/1", "/2", "/3", "/4", "/8", "/16"};
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,145 +0,0 @@
|
|||
;
|
||||
; Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
|
||||
;
|
||||
; SPDX-License-Identifier: BSD-3-Clause
|
||||
;
|
||||
.pio_version 0 // only requires PIO version 0
|
||||
|
||||
.program quadrature_encoder
|
||||
|
||||
; the code must be loaded at address 0, because it uses computed jumps
|
||||
.origin 0
|
||||
|
||||
|
||||
; the code works by running a loop that continuously shifts the 2 phase pins into
|
||||
; ISR and looks at the lower 4 bits to do a computed jump to an instruction that
|
||||
; does the proper "do nothing" | "increment" | "decrement" action for that pin
|
||||
; state change (or no change)
|
||||
|
||||
; ISR holds the last state of the 2 pins during most of the code. The Y register
|
||||
; keeps the current encoder count and is incremented / decremented according to
|
||||
; the steps sampled
|
||||
|
||||
; the program keeps trying to write the current count to the RX FIFO without
|
||||
; blocking. To read the current count, the user code must drain the FIFO first
|
||||
; and wait for a fresh sample (takes ~4 SM cycles on average). The worst case
|
||||
; sampling loop takes 10 cycles, so this program is able to read step rates up
|
||||
; to sysclk / 10 (e.g., sysclk 125MHz, max step rate = 12.5 Msteps/sec)
|
||||
|
||||
; 00 state
|
||||
JMP update ; read 00
|
||||
JMP decrement ; read 01
|
||||
JMP increment ; read 10
|
||||
JMP update ; read 11
|
||||
|
||||
; 01 state
|
||||
JMP increment ; read 00
|
||||
JMP update ; read 01
|
||||
JMP update ; read 10
|
||||
JMP decrement ; read 11
|
||||
|
||||
; 10 state
|
||||
JMP decrement ; read 00
|
||||
JMP update ; read 01
|
||||
JMP update ; read 10
|
||||
JMP increment ; read 11
|
||||
|
||||
; to reduce code size, the last 2 states are implemented in place and become the
|
||||
; target for the other jumps
|
||||
|
||||
; 11 state
|
||||
JMP update ; read 00
|
||||
JMP increment ; read 01
|
||||
decrement:
|
||||
; note: the target of this instruction must be the next address, so that
|
||||
; the effect of the instruction does not depend on the value of Y. The
|
||||
; same is true for the "JMP X--" below. Basically "JMP Y--, <next addr>"
|
||||
; is just a pure "decrement Y" instruction, with no other side effects
|
||||
JMP Y--, update ; read 10
|
||||
|
||||
; this is where the main loop starts
|
||||
.wrap_target
|
||||
update:
|
||||
MOV ISR, Y ; read 11
|
||||
PUSH noblock
|
||||
|
||||
sample_pins:
|
||||
; we shift into ISR the last state of the 2 input pins (now in OSR) and
|
||||
; the new state of the 2 pins, thus producing the 4 bit target for the
|
||||
; computed jump into the correct action for this state. Both the PUSH
|
||||
; above and the OUT below zero out the other bits in ISR
|
||||
OUT ISR, 2
|
||||
IN PINS, 2
|
||||
|
||||
; save the state in the OSR, so that we can use ISR for other purposes
|
||||
MOV OSR, ISR
|
||||
; jump to the correct state machine action
|
||||
MOV PC, ISR
|
||||
|
||||
; the PIO does not have a increment instruction, so to do that we do a
|
||||
; negate, decrement, negate sequence
|
||||
increment:
|
||||
MOV Y, ~Y
|
||||
JMP Y--, increment_cont
|
||||
increment_cont:
|
||||
MOV Y, ~Y
|
||||
.wrap ; the .wrap here avoids one jump instruction and saves a cycle too
|
||||
|
||||
|
||||
|
||||
% c-sdk {
|
||||
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/gpio.h"
|
||||
|
||||
// max_step_rate is used to lower the clock of the state machine to save power
|
||||
// if the application doesn't require a very high sampling rate. Passing zero
|
||||
// will set the clock to the maximum
|
||||
|
||||
static inline void quadrature_encoder_program_init(PIO pio, uint sm, uint pin, int max_step_rate)
|
||||
{
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, pin, 2, false);
|
||||
pio_gpio_init(pio, pin);
|
||||
pio_gpio_init(pio, pin + 1);
|
||||
|
||||
gpio_pull_up(pin);
|
||||
gpio_pull_up(pin + 1);
|
||||
|
||||
pio_sm_config c = quadrature_encoder_program_get_default_config(0);
|
||||
|
||||
sm_config_set_in_pins(&c, pin); // for WAIT, IN
|
||||
sm_config_set_jmp_pin(&c, pin); // for JMP
|
||||
// shift to left, autopull disabled
|
||||
sm_config_set_in_shift(&c, false, false, 32);
|
||||
// don't join FIFO's
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_NONE);
|
||||
|
||||
// passing "0" as the sample frequency,
|
||||
if (max_step_rate == 0) {
|
||||
sm_config_set_clkdiv(&c, 1.0);
|
||||
} else {
|
||||
// one state machine loop takes at most 10 cycles
|
||||
float div = (float)clock_get_hz(clk_sys) / (10 * max_step_rate);
|
||||
sm_config_set_clkdiv(&c, div);
|
||||
}
|
||||
|
||||
pio_sm_init(pio, sm, 0, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
|
||||
static inline int32_t quadrature_encoder_get_count(PIO pio, uint sm)
|
||||
{
|
||||
uint ret;
|
||||
int n;
|
||||
|
||||
// if the FIFO has N entries, we fetch them to drain the FIFO,
|
||||
// plus one entry which will be guaranteed to not be stale
|
||||
n = pio_sm_get_rx_fifo_level(pio, sm) + 1;
|
||||
while (n > 0) {
|
||||
ret = pio_sm_get_blocking(pio, sm);
|
||||
n--;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
%}
|
||||
|
|
@ -10,6 +10,8 @@
|
|||
#include "pico-ssd1306/shapeRenderer/ShapeRenderer.h"
|
||||
#include "pico-ssd1306/textRenderer/TextRenderer.h"
|
||||
|
||||
//TODO:
|
||||
// the pulses are not even... they occur independently of eachother.
|
||||
|
||||
pico_ssd1306::SSD1306* display = nullptr;
|
||||
extern void update_BPM(bool up);
|
||||
|
|
@ -78,41 +80,8 @@ void DisplayHandler::moveCursor(bool dir) {
|
|||
outputs[currentOut]->modifierSelectionIndex = std::size(MOD_TYPES) - 1;
|
||||
}
|
||||
|
||||
} else if (currentScreen == 2) { // width control
|
||||
outputs[currentOut]->editing = 1;
|
||||
if (dir == 1) {
|
||||
outputs[currentOut]->width++;
|
||||
} else {
|
||||
outputs[currentOut]->width--;
|
||||
}
|
||||
|
||||
if (outputs[currentOut]->width > 100) {
|
||||
outputs[currentOut]->width = 100;
|
||||
}
|
||||
|
||||
if (outputs[currentOut]->width < 1) {
|
||||
outputs[currentOut]->width = 1;
|
||||
}
|
||||
|
||||
outputs[currentOut]->setWidth(outputs[currentOut]->width);
|
||||
|
||||
} else if (currentScreen == 3) {
|
||||
|
||||
outputs[currentOut]->editing = 1;
|
||||
if (dir == 1) {
|
||||
outputs[currentOut]->p++;
|
||||
} else {
|
||||
outputs[currentOut]->p--;
|
||||
}
|
||||
|
||||
if (outputs[currentOut]->p > 100) {
|
||||
outputs[currentOut]->p = 100;
|
||||
}
|
||||
|
||||
if (outputs[currentOut]->p < 0) {
|
||||
outputs[currentOut]->p = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
@ -144,17 +113,16 @@ void DisplayHandler::handleClick() {
|
|||
|
||||
if (onOutScreen) {
|
||||
if (currentScreen == 0) { // exit screen
|
||||
cursorPosition = currentOut + 1;
|
||||
cursorPosition = currentOut;
|
||||
currentOut = -1;
|
||||
currentScreen = 0;
|
||||
onOutScreen = 0;
|
||||
cursorClick = false;
|
||||
}
|
||||
|
||||
if (currentScreen == 1 && outputs[currentOut]->editing == 1) {
|
||||
outputs[currentOut]->setDiv(outputs[currentOut]->modifierSelectionIndex);
|
||||
cursorClick = 0;
|
||||
outputs[currentOut]->editing = 0;
|
||||
cursorClick = false;
|
||||
}
|
||||
} else {
|
||||
|
||||
|
|
@ -232,8 +200,7 @@ void DisplayHandler::renderMainPage() {
|
|||
|
||||
|
||||
void DisplayHandler::renderOutPage() {
|
||||
uint8_t visualOut = currentOut + 1;
|
||||
std::string title = std::to_string(visualOut) + "| " + out_pages[currentScreen];
|
||||
std::string title = std::to_string(currentOut) + "| " + out_pages[currentScreen];
|
||||
pico_ssd1306::drawText(display, font_12x16, title.c_str(), 1, 2);
|
||||
|
||||
std::string param_string;
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@
|
|||
#include "globals.h"
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include "hardware/pio.h"
|
||||
#include "quadrature_encoder.pio.h"
|
||||
|
||||
|
||||
static EncoderHandler* self = nullptr;
|
||||
|
|
@ -21,53 +19,53 @@ EncoderHandler::EncoderHandler(DisplayHandler* display_handler) {
|
|||
|
||||
|
||||
void EncoderHandler::gpio_callback(uint gpio, uint32_t events) {
|
||||
uint64_t now = to_us_since_boot(get_absolute_time());
|
||||
static uint64_t last_sw_time = 0;
|
||||
static uint64_t last_rotate_time = 0;
|
||||
uint64_t now = to_us_since_boot(get_absolute_time());
|
||||
|
||||
if (gpio == ENCODER_SW_PIN) {
|
||||
if (now - last_sw_time > 200000) { // 200ms debounce
|
||||
if (gpio == ENCODER_SW_PIN) { // handle button press
|
||||
if (now - last_sw_time > 200000) {
|
||||
self->display_handler->handleClick();
|
||||
last_sw_time = now;
|
||||
}
|
||||
} else if (gpio == ENCODER_CLK_PIN) { // handle encoder turn
|
||||
|
||||
if (now - last_rotate_time < 5000) return;
|
||||
|
||||
if (events & GPIO_IRQ_EDGE_FALL) {
|
||||
if (gpio_get(ENCODER_CLK_PIN) == 0) {
|
||||
|
||||
uint16_t dt_state = gpio_get(ENCODER_DT_PIN);
|
||||
|
||||
if (dt_state) {
|
||||
self->display_handler->moveCursor();
|
||||
} else {
|
||||
self->display_handler->moveCursor(0);
|
||||
}
|
||||
}
|
||||
|
||||
last_rotate_time = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void EncoderHandler::setup() {
|
||||
self = this;
|
||||
|
||||
gpio_init(ENCODER_SW_PIN);
|
||||
gpio_set_dir(ENCODER_SW_PIN, GPIO_IN);
|
||||
gpio_pull_up(ENCODER_SW_PIN);
|
||||
|
||||
gpio_set_irq_enabled_with_callback(ENCODER_SW_PIN, GPIO_IRQ_EDGE_FALL, true, &EncoderHandler::gpio_callback);
|
||||
|
||||
PIO pio = pio0;
|
||||
uint offset = pio_add_program(pio, &quadrature_encoder_program);
|
||||
this->sm = pio_claim_unused_sm(pio, true);
|
||||
|
||||
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);
|
||||
|
||||
quadrature_encoder_program_init(pio, sm, ENCODER_CLK_PIN, 0);
|
||||
clk_last_state = gpio_get(ENCODER_CLK_PIN);
|
||||
|
||||
this->last_count = 0;
|
||||
}
|
||||
|
||||
void EncoderHandler::update() {
|
||||
int32_t current_count = quadrature_encoder_get_count(pio0, this->sm);
|
||||
|
||||
int32_t delta = current_count - last_count;
|
||||
|
||||
if (abs(delta) >= TICKS_PER_DETENT) {
|
||||
|
||||
if (delta < 0) {
|
||||
display_handler->moveCursor();
|
||||
} else {
|
||||
display_handler->moveCursor(0);
|
||||
}
|
||||
|
||||
last_count = current_count - (delta % TICKS_PER_DETENT);
|
||||
}
|
||||
gpio_set_irq_enabled_with_callback(20, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &EncoderHandler::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);
|
||||
}
|
||||
|
|
|
|||
191
src/Gate.cpp
191
src/Gate.cpp
|
|
@ -1,18 +1,20 @@
|
|||
// Gate.cpp
|
||||
#include "pico/stdlib.h"
|
||||
#include "Gate.h"
|
||||
#include "globals.h"
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
|
||||
|
||||
Gate::Gate(uint8_t pin) {
|
||||
this->pin = pin;
|
||||
state = 0;
|
||||
|
||||
editing = 0;
|
||||
|
||||
modifierSelectionIndex = 3;
|
||||
editing = 0;
|
||||
divideMode = 0; // 1 divison | 0 multiplication
|
||||
modifier = 0; // divide mode modifier (4x, /32, etc)
|
||||
div = 0; // cycles needed before a pulse based on divide mode and modifier
|
||||
cycle = 0; // how many cycles have passed since last pulse
|
||||
|
||||
dur = 0; // how long pulse is on
|
||||
width = 50; // pulse width
|
||||
|
|
@ -21,176 +23,73 @@ Gate::Gate(uint8_t pin) {
|
|||
p = 100; // probability of a pulse
|
||||
}
|
||||
|
||||
bool Gate::getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
void Gate::setLen(uint32_t currentPeriod) {
|
||||
len = (uint32_t)((double)currentPeriod * (width / 100.0) / 1000.0);
|
||||
}
|
||||
|
||||
|
||||
void Gate::setDiv(uint8_t modifier_selecton_index) {
|
||||
switch(modifier_selecton_index) {
|
||||
case 0: // x32
|
||||
tickInterval = 3;
|
||||
isEnabled = true;
|
||||
divideMode = 0;
|
||||
modifier = 32;
|
||||
break;
|
||||
case 1: // x16
|
||||
tickInterval = 6;
|
||||
isEnabled = true;
|
||||
divideMode = 0;
|
||||
modifier = 16;
|
||||
break;
|
||||
case 2: // x12
|
||||
tickInterval = 8;
|
||||
isEnabled = true;
|
||||
divideMode = 0;
|
||||
modifier = 12;
|
||||
break;
|
||||
case 3: // x8
|
||||
tickInterval = 12;
|
||||
isEnabled = true;
|
||||
divideMode = 0;
|
||||
modifier = 8;
|
||||
break;
|
||||
case 4: // x6
|
||||
tickInterval = 16;
|
||||
isEnabled = true;
|
||||
divideMode = 0;
|
||||
modifier = 6;
|
||||
break;
|
||||
case 5: // x4
|
||||
tickInterval = 24;
|
||||
isEnabled = true;
|
||||
divideMode = 0;
|
||||
modifier = 4;
|
||||
break;
|
||||
case 6: // x3
|
||||
tickInterval = 32;
|
||||
isEnabled = true;
|
||||
divideMode = 0;
|
||||
modifier = 3;
|
||||
break;
|
||||
case 7: // x2
|
||||
tickInterval = 48;
|
||||
isEnabled = true;
|
||||
divideMode = 0;
|
||||
modifier = 2;
|
||||
break;
|
||||
case 8: // 0 (OFF)
|
||||
tickInterval = 0;
|
||||
isEnabled = false;
|
||||
divideMode = 0;
|
||||
modifier = 0;
|
||||
break;
|
||||
case 9: // /1
|
||||
tickInterval = 96;
|
||||
isEnabled = true;
|
||||
divideMode = 1;
|
||||
modifier = 1;
|
||||
break;
|
||||
case 10: // /2
|
||||
tickInterval = 192;
|
||||
isEnabled = true;
|
||||
divideMode = 1;
|
||||
modifier = 2;
|
||||
break;
|
||||
case 11: // /3
|
||||
tickInterval = 288;
|
||||
isEnabled = true;
|
||||
divideMode = 1;
|
||||
modifier = 3;
|
||||
break;
|
||||
case 12: // /4
|
||||
tickInterval = 384;
|
||||
isEnabled = true;
|
||||
divideMode = 1;
|
||||
modifier = 4;
|
||||
break;
|
||||
case 13: // /6
|
||||
tickInterval = 576;
|
||||
isEnabled = true;
|
||||
divideMode = 1;
|
||||
modifier = 4;
|
||||
break;
|
||||
case 14: // /8
|
||||
tickInterval = 768;
|
||||
isEnabled = true;
|
||||
divideMode = 1;
|
||||
modifier = 8;
|
||||
break;
|
||||
case 15: // /16
|
||||
tickInterval = 1536;
|
||||
isEnabled = true;
|
||||
divideMode = 1;
|
||||
modifier = 16;
|
||||
break;
|
||||
case 16: // /32
|
||||
tickInterval = 3072;
|
||||
isEnabled = true;
|
||||
divideMode = 1;
|
||||
modifier = 16;
|
||||
break;
|
||||
default:
|
||||
tickInterval = 96;
|
||||
isEnabled = true;
|
||||
divideMode = 1;
|
||||
printf("HOW ABOUT HERE\n");
|
||||
|
||||
uint8_t modifier = MODIFIERS[modifier_selecton_index];
|
||||
uint8_t mod_type = MOD_TYPES[modifier_selecton_index];
|
||||
if (mod_type == 1) {
|
||||
div = PPQN * modifier;
|
||||
} else {
|
||||
div = PPQN / modifier;
|
||||
}
|
||||
divideMode = mod_type;
|
||||
this->modifier = modifier;
|
||||
|
||||
if (this->modifier == 0) {
|
||||
turnOff();
|
||||
}
|
||||
|
||||
// TODO: check if this is actually needed
|
||||
setWidth(this->width);
|
||||
};
|
||||
|
||||
|
||||
void Gate::setWidth(uint16_t newWidth) {
|
||||
this->width = newWidth;
|
||||
|
||||
double ms_per_tick = (60000.0 / (double)BPM) / 96.0;
|
||||
|
||||
double ms_between_triggers = ms_per_tick * (double)this->tickInterval;
|
||||
|
||||
this->len = (uint32_t)(ms_between_triggers * (this->width / 100.0));
|
||||
|
||||
uint32_t max_allowed_len = (ms_between_triggers > 5) ? (uint32_t)ms_between_triggers - 2 : 1;
|
||||
|
||||
if (this->len > max_allowed_len) {
|
||||
this->len = max_allowed_len;
|
||||
width = newWidth;
|
||||
if (divideMode == 1) {
|
||||
len = (uint32_t)((double)(MINUTE_US / BPM) * (width / 100.0) / 1000.0);
|
||||
} else {
|
||||
len = (uint32_t)((double)(MINUTE_US / BPM / modifier) * (width / 100.0) / 1000.0);
|
||||
}
|
||||
};
|
||||
|
||||
if (this->len < 1) this->len = 1;
|
||||
void Gate::setP(uint16_t prob) {
|
||||
this->p = prob;
|
||||
}
|
||||
|
||||
|
||||
void Gate::turnOn() {
|
||||
if (!isEnabled || tickInterval == 0) return;
|
||||
|
||||
if (MASTER_TICK % tickInterval == 0) {
|
||||
if (MASTER_TICK != lastTriggerTick) {
|
||||
lastTriggerTick = MASTER_TICK;
|
||||
|
||||
cycle += 1;
|
||||
uint8_t pRes = 1;
|
||||
|
||||
if (cycle == div) {
|
||||
if (p < 100) {
|
||||
if ((rand() % 100) + 1 > p) pRes = 0;
|
||||
uint32_t r = (rand() % 100) + 1;
|
||||
if (r > p) {
|
||||
pRes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (pRes == 1) {
|
||||
state = 1;
|
||||
gpio_put(pin, 1);
|
||||
gpio_put(pin, state);
|
||||
dur = millis();
|
||||
}
|
||||
cycle = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
else {
|
||||
lastTriggerTick = 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Gate::turnOff() {
|
||||
if (state == 1) {
|
||||
if (millis() - dur >= len) {
|
||||
if (state == 1 && millis() - dur >= len) {
|
||||
state = 0;
|
||||
gpio_put(pin, 0);
|
||||
}
|
||||
}
|
||||
gpio_put(pin, state);
|
||||
dur = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
51
src/main.cpp
51
src/main.cpp
|
|
@ -11,16 +11,14 @@
|
|||
#include "DisplayHandler.h"
|
||||
#include "EncoderHandler.h"
|
||||
|
||||
|
||||
// Time based operations
|
||||
struct repeating_timer bpm_timer = {0};
|
||||
|
||||
volatile uint8_t BPM = 60;
|
||||
volatile uint8_t PLAY = 1;
|
||||
volatile uint32_t period_us = 0;
|
||||
volatile uint32_t MASTER_TICK;
|
||||
|
||||
volatile bool beatToggle = false;
|
||||
|
||||
// Initialize Outputs
|
||||
Gate out1(OUT_1_PIN);
|
||||
Gate out2(OUT_2_PIN);
|
||||
Gate out3(OUT_3_PIN);
|
||||
|
|
@ -32,15 +30,13 @@ Gate out8(OUT_8_PIN);
|
|||
|
||||
static Gate* outputs[] = {&out1, &out2, &out3, &out4, &out5, &out6, &out7, &out8};
|
||||
|
||||
|
||||
// Initialize Handlers
|
||||
static DisplayHandler display_handler(outputs);
|
||||
static EncoderHandler encoder_handler(&display_handler);
|
||||
|
||||
|
||||
|
||||
bool timer_callback(struct repeating_timer *t) {
|
||||
if (PLAY == 1) {
|
||||
beatToggle = true;
|
||||
MASTER_TICK += 1;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -90,32 +86,58 @@ void setup_outs() {
|
|||
}
|
||||
|
||||
// manual setup
|
||||
// out1.setDiv(5);
|
||||
// out1.setWidth(80);
|
||||
out1.setDiv(1);
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
void handle_outs() {
|
||||
if (beatToggle) {
|
||||
for (Gate* g: outputs) {
|
||||
if (g->modifier != 0) {
|
||||
g->turnOn();
|
||||
}
|
||||
}
|
||||
|
||||
beatToggle = false;
|
||||
}
|
||||
for (Gate* g: outputs) {
|
||||
if (g->modifier != 0) {
|
||||
g->turnOff();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
// initialize
|
||||
stdio_init_all();
|
||||
|
||||
// Seed random
|
||||
srand(rosc_hw->randombit);
|
||||
|
||||
// Initialize display and multicore
|
||||
display_handler.setup();
|
||||
multicore_launch_core1(core1_entry);
|
||||
multicore_fifo_push_blocking(1);
|
||||
|
||||
// Initialize Encoder
|
||||
encoder_handler.setup();
|
||||
|
||||
setup_outs();
|
||||
|
|
@ -123,7 +145,6 @@ int main() {
|
|||
update_period();
|
||||
|
||||
while (true) {
|
||||
encoder_handler.update();
|
||||
handle_outs();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue