Compare commits
5 commits
e317595cdd
...
d4f8c94cd4
| Author | SHA1 | Date | |
|---|---|---|---|
| d4f8c94cd4 | |||
| 95d8951846 | |||
| 3f257adf49 | |||
| 20281ad17a | |||
| 90693e8c2a |
7 changed files with 253 additions and 75 deletions
34
CMakeLists.txt
Normal file
34
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
|
# 1. Generate the map for your Neovim clangd linter
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
# Include the SDK's CMake entry point
|
||||||
|
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
|
||||||
|
|
||||||
|
project(clock C CXX ASM)
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
pico_sdk_init()
|
||||||
|
|
||||||
|
include_directories(include)
|
||||||
|
|
||||||
|
add_executable(clock
|
||||||
|
src/main.cpp
|
||||||
|
src/Gate.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Enable USB output (useful for later printf debugging)
|
||||||
|
pico_enable_stdio_usb(clock 1)
|
||||||
|
pico_enable_stdio_uart(clock 0)
|
||||||
|
|
||||||
|
# Pull in standard library and hardware abstraction
|
||||||
|
target_link_libraries(clock
|
||||||
|
pico_stdlib
|
||||||
|
hardware_gpio
|
||||||
|
hardware_i2c
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create the .uf2 file
|
||||||
|
pico_add_extra_outputs(clock)
|
||||||
33
Out.h
33
Out.h
|
|
@ -1,33 +0,0 @@
|
||||||
// Out.h
|
|
||||||
#ifndef Out_h
|
|
||||||
#define Out_h
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
class Out {
|
|
||||||
private:
|
|
||||||
byte pin;
|
|
||||||
unsigned char state;
|
|
||||||
int cycle;
|
|
||||||
unsigned long dur;
|
|
||||||
unsigned long len;
|
|
||||||
byte width;
|
|
||||||
byte divideMode;
|
|
||||||
int div;
|
|
||||||
int modifier;
|
|
||||||
String divString;
|
|
||||||
int p;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Out(byte pin);
|
|
||||||
void turnOn();
|
|
||||||
void turnOff();
|
|
||||||
void setLen(unsigned long currentPeriod);
|
|
||||||
void setDiv(int newDiv, byte divide = 1);
|
|
||||||
void setWidth(int newWidth);
|
|
||||||
void setP(int prob);
|
|
||||||
int getState();
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
36
include/Gate.h
Normal file
36
include/Gate.h
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Gate.h
|
||||||
|
#ifndef Gate_h
|
||||||
|
#define Gate_h
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
class Gate {
|
||||||
|
private:
|
||||||
|
bool state;
|
||||||
|
int16_t cycle;
|
||||||
|
uint32_t dur;
|
||||||
|
uint32_t len;
|
||||||
|
uint8_t width;
|
||||||
|
uint8_t divideMode;
|
||||||
|
uint16_t div;
|
||||||
|
uint16_t modifier;
|
||||||
|
std::string divString;
|
||||||
|
uint8_t p;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Gate(uint8_t pin);
|
||||||
|
uint8_t pin;
|
||||||
|
|
||||||
|
void turnOn();
|
||||||
|
void turnOff();
|
||||||
|
void setLen(uint32_t currentPeriod);
|
||||||
|
void setDiv(uint16_t newDiv, uint8_t divide = 1);
|
||||||
|
void setWidth(uint16_t newWidth);
|
||||||
|
void setP(uint16_t prob);
|
||||||
|
|
||||||
|
bool getState();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,12 +1,8 @@
|
||||||
#ifndef GLOBALS_H
|
#ifndef GLOBALS_H
|
||||||
#define GLOBALS_H
|
#define GLOBALS_H
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <cstdint>
|
||||||
|
|
||||||
extern byte BPM;
|
|
||||||
extern unsigned long minute;
|
|
||||||
extern unsigned long period;
|
|
||||||
extern byte ppqn;
|
|
||||||
#endif // GLOBALS_H
|
#endif // GLOBALS_H
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -45,6 +45,10 @@ unsigned long screenLastUpdate = 0;
|
||||||
const unsigned long screenThrottle = 200;
|
const unsigned long screenThrottle = 200;
|
||||||
byte forceScreenUpdate = 1;
|
byte forceScreenUpdate = 1;
|
||||||
|
|
||||||
|
byte currentScreen = 0;
|
||||||
|
byte editMode = 0;
|
||||||
|
int mainScreenCursorPos = 0;
|
||||||
|
byte maxMainScreenPos = 8;
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(9600);
|
Serial.begin(9600);
|
||||||
|
|
@ -58,6 +62,7 @@ void setup() {
|
||||||
|
|
||||||
// screen setup
|
// screen setup
|
||||||
u8g2.begin();
|
u8g2.begin();
|
||||||
|
u8g2.setFontMode(1);
|
||||||
|
|
||||||
// display.clearDisplay();
|
// display.clearDisplay();
|
||||||
// display.setTextSize(2);
|
// display.setTextSize(2);
|
||||||
|
|
@ -150,16 +155,39 @@ void updateScreen() {
|
||||||
if (currentTime - screenLastUpdate >= screenThrottle && forceScreenUpdate == 1) {
|
if (currentTime - screenLastUpdate >= screenThrottle && forceScreenUpdate == 1) {
|
||||||
screenLastUpdate = currentTime;
|
screenLastUpdate = currentTime;
|
||||||
forceScreenUpdate = 0;
|
forceScreenUpdate = 0;
|
||||||
// The firstPage()/nextPage() loop manages where the drawing commands go
|
|
||||||
u8g2.firstPage();
|
u8g2.firstPage();
|
||||||
do {
|
do {
|
||||||
// --- Drawing Commands for the CURRENT Page ---
|
// 12px - nonbold
|
||||||
// All drawing commands must live INSIDE this do/while loop.
|
if (mainScreenCursorPos == 0) {
|
||||||
u8g2.setFont(u8g2_font_helvR08_tf);
|
u8g2.setFont(u8g2_font_7x14B_mf);
|
||||||
u8g2.setCursor(0, 10);
|
} else {
|
||||||
u8g2.print(F("BPM: "));
|
u8g2.setFont(u8g2_font_7x14_mf);
|
||||||
u8g2.print(BPM); // Example of dynamic data
|
}
|
||||||
|
|
||||||
|
u8g2.setCursor(0, 15);
|
||||||
|
u8g2.print(F("BPM: "));
|
||||||
|
u8g2.print(BPM);
|
||||||
|
|
||||||
|
u8g2.setFont(u8g2_font_7x14_mf);
|
||||||
|
u8g2.setCursor(0, 45);
|
||||||
|
|
||||||
|
char buffer[5];
|
||||||
|
for (byte i = 1; i < 9; i++) {
|
||||||
|
if (i == 5) {
|
||||||
|
u8g2.setCursor(0, 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mainScreenCursorPos == i) {
|
||||||
|
u8g2.setFont(u8g2_font_7x14B_mf);
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(buffer, "[%d] ", i);
|
||||||
|
u8g2.print(buffer);
|
||||||
|
|
||||||
|
if (mainScreenCursorPos == i) {
|
||||||
|
u8g2.setFont(u8g2_font_7x14_mf);
|
||||||
|
}
|
||||||
|
};
|
||||||
} while (u8g2.nextPage());
|
} while (u8g2.nextPage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -186,11 +214,22 @@ void checkRPot() {
|
||||||
if (currentPos != previousPos) {
|
if (currentPos != previousPos) {
|
||||||
Serial.print("Encoder Position: ");
|
Serial.print("Encoder Position: ");
|
||||||
Serial.println(currentPos);
|
Serial.println(currentPos);
|
||||||
|
|
||||||
if (currentPos > previousPos) {
|
// on home screen
|
||||||
BPM++;
|
if (currentScreen == 0) {
|
||||||
} else {
|
if (editMode == 0) {
|
||||||
BPM--;
|
if (currentPos > previousPos) {
|
||||||
|
mainScreenCursorPos++;
|
||||||
|
if (mainScreenCursorPos > maxMainScreenPos) {
|
||||||
|
mainScreenCursorPos = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mainScreenCursorPos--;
|
||||||
|
if (mainScreenCursorPos < 0) {
|
||||||
|
mainScreenCursorPos = maxMainScreenPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
forceScreenUpdate = 1;
|
forceScreenUpdate = 1;
|
||||||
|
|
@ -202,6 +241,17 @@ void checkRPot() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleBPMChange(byte increase) {
|
||||||
|
if (increase == 1) {
|
||||||
|
BPM++;
|
||||||
|
} else {
|
||||||
|
BPM--;
|
||||||
|
}
|
||||||
|
|
||||||
|
period = (minute / BPM) / ppqn;
|
||||||
|
Timer1.setPeriod(period);
|
||||||
|
}
|
||||||
|
|
||||||
void updateEncoder() {
|
void updateEncoder() {
|
||||||
// The ISR should be as short and fast as possible
|
// The ISR should be as short and fast as possible
|
||||||
// Check the state of the DT pin to determine direction
|
// Check the state of the DT pin to determine direction
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
// Out.cpp
|
// Gate.cpp
|
||||||
#include "Arduino.h"
|
#include "pico/stdlib.h"
|
||||||
#include "Out.h"
|
#include "Gate.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
#include <string>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
Out::Out(byte pin) {
|
Gate::Gate(uint8_t pin) {
|
||||||
this->pin = pin;
|
this->pin = pin;
|
||||||
state = LOW;
|
state = 0;
|
||||||
|
|
||||||
divideMode = 1; // 1 divison | 0 multiplication
|
divideMode = 1; // 1 divison | 0 multiplication
|
||||||
modifier = 1; // divide mode modifier (4x, /32, etc)
|
modifier = 1; // divide mode modifier (4x, /32, etc)
|
||||||
|
|
@ -18,58 +20,55 @@ Out::Out(byte pin) {
|
||||||
len = 0; // max len a pulse can be on, as determined by width
|
len = 0; // max len a pulse can be on, as determined by width
|
||||||
|
|
||||||
p = 100; // probability of a pulse
|
p = 100; // probability of a pulse
|
||||||
|
|
||||||
|
|
||||||
pinMode(pin, OUTPUT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Out::getState() {
|
bool Gate::getState() {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Out::setLen(unsigned long currentPeriod) {
|
void Gate::setLen(uint32_t currentPeriod) {
|
||||||
len = (unsigned long)((double)currentPeriod * (width / 100.0) / 1000.0);
|
len = (uint32_t)((double)currentPeriod * (width / 100.0) / 1000.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Out::setDiv(int modifier, byte divide = 1) {
|
void Gate::setDiv(uint16_t modifier, uint8_t divide) {
|
||||||
if (divide == 1) {
|
if (divide == 1) {
|
||||||
div = ppqn * modifier;
|
div = ppqn * modifier;
|
||||||
divString = "/" + modifier;
|
divString = "/" + std::to_string(modifier);
|
||||||
} else {
|
} else {
|
||||||
div = ppqn / modifier;
|
div = ppqn / modifier;
|
||||||
divString = "x" + modifier;
|
divString = "x" + std::to_string(modifier);
|
||||||
}
|
}
|
||||||
divideMode = divide;
|
divideMode = divide;
|
||||||
this->modifier = modifier;
|
this->modifier = modifier;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Out::setWidth(int newWidth) {
|
void Gate::setWidth(uint16_t newWidth) {
|
||||||
width = newWidth;
|
width = newWidth;
|
||||||
if (divideMode == 1) {
|
if (divideMode == 1) {
|
||||||
len = (unsigned long)((double)(minute / BPM) * (width / 100.0) / 1000.0);
|
len = (uint32_t)((double)(minute / BPM) * (width / 100.0) / 1000.0);
|
||||||
} else {
|
} else {
|
||||||
len = (unsigned long)((double)(minute / BPM / modifier) * (width / 100.0) / 1000.0);
|
len = (uint32_t)((double)(minute / BPM / modifier) * (width / 100.0) / 1000.0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Out::setP(int prob) {
|
void Gate::setP(uint16_t prob) {
|
||||||
this->p = prob;
|
this->p = prob;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Out::turnOn() {
|
void Gate::turnOn() {
|
||||||
cycle += 1;
|
cycle += 1;
|
||||||
byte pRes = 1;
|
uint8_t pRes = 1;
|
||||||
|
|
||||||
if (cycle == div) {
|
if (cycle == div) {
|
||||||
if (p < 100) {
|
if (p < 100) {
|
||||||
long r = random(1, 101);
|
uint32_t r = (rand() % 100) + 1;
|
||||||
if (r > p) {
|
if (r > p) {
|
||||||
pRes = 0;
|
pRes = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pRes == 1) {
|
if (pRes == 1) {
|
||||||
state = HIGH;
|
state = 1;
|
||||||
digitalWrite(pin, state);
|
digitalWrite(pin, state);
|
||||||
dur = millis();
|
dur = millis();
|
||||||
}
|
}
|
||||||
|
|
@ -77,9 +76,9 @@ void Out::turnOn() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Out::turnOff() {
|
void Gate::turnOff() {
|
||||||
if (state == HIGH && millis() - dur >= len) {
|
if (state == 1 && millis() - dur >= len) {
|
||||||
state = LOW;
|
state = 0;
|
||||||
digitalWrite(pin, state);
|
digitalWrite(pin, state);
|
||||||
dur = 0;
|
dur = 0;
|
||||||
};
|
};
|
||||||
96
src/main.cpp
Normal file
96
src/main.cpp
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "hardware/structs/rosc.h"
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "pico/time.h"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
Gate out2(OUT_2_PIN);
|
||||||
|
Gate out3(OUT_3_PIN);
|
||||||
|
Gate out4(OUT_4_PIN);
|
||||||
|
Gate out5(OUT_5_PIN);
|
||||||
|
Gate out6(OUT_6_PIN);
|
||||||
|
Gate out7(OUT_7_PIN);
|
||||||
|
Gate out8(OUT_8_PIN);
|
||||||
|
|
||||||
|
|
||||||
|
bool timer_callback(struct repeating_timer *t) {
|
||||||
|
if (PLAY == 1) {
|
||||||
|
beatToggle = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void init_timer(uint32_t period_us) {
|
||||||
|
cancel_repeating_timer(&bpm_timer);
|
||||||
|
add_repeating_timer_us(-(int64_t)period_us, timer_callback, NULL, &bpm_timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void update_period() {
|
||||||
|
period_us = (uint32_t)(MINUTE_US / (uint32_t)BPM / PPQN);
|
||||||
|
init_timer(period_us);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
update_period();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue