// clock module concept
// aka "Not Pockets"
#include <LiquidCrystal.h>
#include "not_pockets.h"
LiquidCrystal lcd(12, 11, 10, 9, 8, 7); // might want to redo these to avoid PWM pins?
#define NUM_CHANNELS 6 // 6 output & global menu
#define NUM_PAGES 3
// #define NUM_GLOBAL_PAGES
// Master clock declarations
#define CHANNEL1_OUT 5 // might update later
#define CHANNEL2_OUT 6
#define CHANNEL3_OUT 16
#define CHANNEL4_OUT 17
#define CHANNEL5_OUT 18
#define CHANNEL6_OUT 19
uint32_t master_clock = 0; // 65536 beats at 300 bpm is over 3 hours, so there shouldn't be any danger from clock rollover
// remember to reset the clock to 0 on restart
uint16_t master_clock_16msb; // upper 16 bits to track number of beats
uint16_t master_clock_16lsb; // lower 16 bits to track fraction of beat
uint32_t clock1 = 0;
uint16_t clock1_16lsb;
uint16_t phase1 = 0x7FFF; // gotta fix
bool prev_clk1 = 0;
unsigned int bpm = 60;
bool prev_clk = 0;
// Rotary encoder declarations
#define ENCODER_CLK 2
#define ENCODER_DT 3
#define ENCODER_SW 4
bool encLastClk = 1;
bool encNewClk;
bool encDtVal;
uint8_t incomingByte;
bool encLastSw = 1; // think it comes up high as default
bool encNewSw;
uint8_t channel = 0;
uint8_t page = 0;
bool mode = 0; // 0 = select / 1 = edit
ISR(TIMER1_COMPA_vect){
master_clock += bpm;
}
void setup() {
cli();
TCCR1A = 0; // zero this out, not sure what the default is, but it wasn't working as expected
TCCR1B = 0;
// set timer 1 register to zero, I don't know if this is strictly necessary
TCNT1 = 0;
// freq (Hz) = bpm / 60
// number of increments per beat = increment rate / freq
// increment value = 65536 / number of increments (for 16 bit uint)
// so the increment value = 65536 * bpm / (increment rate * 60)
// with an increment rate of 65536/60, increment value = bpm, which corresponds to 1092.26Hz
// 2MHz / 1092.26 Hz = 1831
// Set Compare Match to 1831 - 1
OCR1A = 1830;
// Set CTC mode and /8 prescale
TCCR1B |= (1<<WGM12);
TCCR1B |= (1<<CS11);
// Output Compare Match A Interrupt Enable
TIMSK1 |= (1 << OCIE1A);
sei();
Serial.begin(115200);
pinMode(LED_BUILTIN,OUTPUT);
pinMode(CHANNEL1_OUT,OUTPUT);
pinMode(CHANNEL2_OUT,OUTPUT);
pinMode(CHANNEL3_OUT,OUTPUT);
pinMode(CHANNEL4_OUT,OUTPUT);
pinMode(CHANNEL5_OUT,OUTPUT);
pinMode(CHANNEL6_OUT,OUTPUT);
Serial.println("Hello!");
// clean this up
lcd.createChar(0, zero_dark); // not sure I need these
lcd.createChar(1, one_dark);
lcd.createChar(2, two_dark);
lcd.createChar(7, colon_dark);
lcd.begin(8, 1);
processLCD(channel, page, mode);
}
void loop() {
// Master clock processing
master_clock_16lsb = master_clock & 0xFFFF;
master_clock_16msb = master_clock >> 16; // do we actually need this for anything?
if (prev_clk ^ master_clock_16lsb>>15){
// Serial.print(master_clock_16msb);
// Serial.print(" ");
// Serial.println(master_clock_16lsb);
prev_clk = master_clock_16lsb>>15;
digitalWrite(LED_BUILTIN, !prev_clk);
Serial.print(!prev_clk ? "ON " : "OFF ");
Serial.println(master_clock, HEX);
}
// process individual outputs
// digitalWrite(CHANNEL1_OUT, master_clock_16msb & 1);
clock1 = master_clock * 2; // scaled by an ammount
clock1_16lsb = (clock1 & 0xFFFF) - phase1;
if (prev_clk1 ^ clock1_16lsb>>15){
prev_clk1 = clock1_16lsb>>15;
digitalWrite(CHANNEL1_OUT, !prev_clk1);
Serial.print(!prev_clk1 ? "ON " : "OFF ");
Serial.println(clock1_16lsb, HEX);
}
// progress! just gotta make phase and duty and scale and everything else user editable
// also need to better handle channel parameters
// Fake rotary encoder - what do we do if the button is down and rotation occurs?
if (Serial.available() > 0){
incomingByte = Serial.read();
Serial.print("Byte received: ");
Serial.println(incomingByte);
}
if (incomingByte == 114 || incomingByte == 108){ // right "r"/114 or left "l"/108 rotation
if (!mode) { // mode = select
if (page == 0){ // Page 0: bpm/mod
if (incomingByte == 114){ // right rotation, increment channel
channel = (channel == NUM_CHANNELS) ? 0 : channel + 1;
} else { // else left rotation, decrement channel
channel = (channel == 0) ? NUM_CHANNELS : channel - 1;
}
} // end if page 0
if (channel != 0 && page != 0){ // do something with the secret evil menu
if (incomingByte == 114){
page = (page == NUM_PAGES) ? 1 : page + 1;
} else {
page = (page == 1) ? NUM_PAGES : page - 1;
}
}
} else { // else mode = edit
if (page == 0 && channel == 0){ // Page 0: bpm/mod
if (incomingByte == 114){ // right rotation, increment channel
if (bpm < 300){ bpm ++;}
} else {
if (bpm > 30 ){ bpm --;}
}
}
}
processLCD(channel, page, mode);
} // end if rotation
if (incomingByte == 112){ // press "p"/112
if (!mode){ // doing it like this becasue I don't know how save/load is gunna work yet
mode = 1;
} else if (mode){
mode = 0;
}
Serial.print("Mode:");
Serial.println( mode );
processLCD(channel, page, mode);
}
// long press here
if (incomingByte == 111 && !mode){ // using "o" for long because I already used "l" for left
// also don't have this do anything whilst in edit mode
if (channel != 0 && page == 0){
page = 1;
} else if (page != 0){
page = 0;
} else {
// secret evil menu for global settings
}
processLCD(channel,page,mode);
}
}
void processLCD (uint8_t channel, uint8_t page, bool mode){ // gotta do some lookup on index or whatever
lcd.clear();
switch (channel){
case 0:
switch (page){
case 0:
lcd.print(" BPM");
lcd.print(mode ? "\x07" : ":");
if (bpm < 100){
lcd.print(" "); // right align 2 digit numbers
}
lcd.print(bpm);
break;
// Other cases - I don't know yet
} // end switch page
break; // end channel 0
default: // output channels
switch (page){
case 0:
lcd.print(channel);
lcd.print("MOD"); // lookup for this?
lcd.print(mode ? "\x07" : ":");
// need some kind of lookup for this
break;
case 1:
lcd.print(channel);
lcd.print("DUT");
lcd.print(mode ? "\x07" : ":");
break;
case 2:
lcd.print(channel);
lcd.print("PHS");
lcd.print(mode ? "\x07" : ":");
break;
case 3:
lcd.print(channel);
lcd.print("DLY");
lcd.print(mode ? "\x07" : ":");
break;
// other cases
}
}
return 0;
}