Compare commits

..

11 commits

Author SHA1 Message Date
SpaceyKasey
928758c800 Merge branch 'main' of https://github.com/SpaceyKasey/trigor-mortse 2023-01-21 01:42:15 -05:00
SpaceyKasey
446d0f2d8d GFX fixes 2023-01-21 01:40:12 -05:00
KaraBun
9262ec1b17 Errors Mostly fixed, compiles, sorta runs 2023-01-21 01:39:21 -05:00
SpaceyKasey
dc7dc45820 Merge branch 'main' of https://github.com/SpaceyKasey/trigor-mortse 2023-01-21 00:50:46 -05:00
SpaceyKasey
00c301f799 fixes for gfx 2023-01-21 00:45:01 -05:00
KaraBun
07ae3f86be Fixed a bazillion errors 2023-01-21 00:44:47 -05:00
SpaceyKasey
1af412dd4b Begin gfx library 2023-01-21 00:24:51 -05:00
SpaceyKasey
3b9715e2d4 Merge branch 'main' of https://github.com/SpaceyKasey/trigor-mortse 2023-01-20 21:33:10 -05:00
SpaceyKasey
93d6407b0d Commit before merge 2023-01-20 21:32:18 -05:00
SpaceyKasey
e0e7d65bb4 Save before merging changes 2023-01-20 21:31:59 -05:00
KaraBun
66a3c0304a clock add 2023-01-20 21:29:41 -05:00
7 changed files with 367 additions and 14 deletions

6
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,6 @@
{
"files.associations": {
"ios": "cpp",
"cstddef": "cpp"
}
}

View file

@ -1 +1,2 @@
# Lanes # Trigor Mortse
Trigor Mortse is an eurorack sequencer module based on morse code.

View file

@ -1,6 +1,8 @@
#include <Encoder.h> #include <Encoder.h>
#include <Arduino.h> #include <Arduino.h>
#include "src/MortseUi.h"
const byte MorseTable[] = {0b00100000,0,0b00010010,0,0,0,0,0b00011110,0b10110110,0b00101101,0,0b10101010,0b00110011,0b00100001,0b00010101,0b10110010,0b10111111,0b10101111,0b10100111,0b10100011,0b10100001,0b10100000,0b10110000,0b10111000,0b10111100,0b10111111,0b00111000,0b00101010,0,0b10110001,0,0b00001100,0b00011010,0b01000001,0b10001000,0b10001010,0b01100100,0b00100000,0b10000010,0b01100110,0b10000000,0b01000000,0b10000111,0b01100101,0b10000100,0b01000011,0b01000010,0b01100111,0b10000110,0b10001101,0b01100010,0b01100000,0b00100001,0b01100001,0b10000001,0b01100011,0b10001001,0b10001011,0b10001100,0b10110110,0,0b00101101,0,0b00001101,0b00011110,0b01000001,0b10001000,0b10001010,0b01100100,0b00100000,0b10000010,0b01100110,0b10000000,0b01000000,0b10000111,0b01100101,0b10000100,0b01000011,0b01000010,0b01100111,0b10000110,0b10001101,0b01100010,0b01100000,0b00100001,0b01100001,0b10000001,0b01100011,0b10001001,0b10001011,0b10001100,0b10110110,0,0b00101101,0};
const int Channel1 = 9; //Output Channel 1 const int Channel1 = 9; //Output Channel 1
const int Channel2 = 10; //Output Channel 2 const int Channel2 = 10; //Output Channel 2
@ -15,20 +17,30 @@ const int ClockIn = 4; //Clock In
const int ClockDetect = 7; //Detect clock jack const int ClockDetect = 7; //Detect clock jack
const int DebounceTime = 10; //Debounce time in ms const int DebounceTime = 10; //Debounce time in ms
const int DisplaySpi = 0; //todo: find spi port ## from library
Encoder LeftEnc( Enc1P1, Enc1P2); Encoder LeftEnc( Enc1P1, Enc1P2);
Encoder RightEnc( Enc2P1, Enc2P2); Encoder RightEnc( Enc2P1, Enc2P2);
//MortseUi Ui(DisplaySpi);
String TestText = "Momento Mortse";
int E1 = 0; int E1 = 0;
int E2 = 0; int E2 = 0;
int Channel1Index = 0;
int MorseIndex = 0;
bool E1Btn = false; bool E1Btn = false;
bool E2Btn = false; bool E2Btn = false;
bool ClockState = false; bool ClockState = false;
bool E1Prev, E2Prev, CDPrev, E1Click, E2Click, CDIn = false; bool Beat = false;
unsigned int E1Bounce, E2Bounce, CDBounce = 0; unsigned int Channel1Trig = 0, Channel2Trig = 0, Channel3Trig = 0;
unsigned long ClockPrev = 0; bool E1Prev, E2Prev, CDPrev, E1Click, E2Click, CDIn = false, Channel1State = false, Channel2State = false, Channel3State = false;
unsigned int E1Bounce, E2Bounce, CDBounce = 0, BeatBounce = 0;
unsigned long ClockPrev = 0, ClockInPrev = 0, LastBeat = 0;
byte Input = 0; byte Input = 0;
int PpQN = 24; int PpQN = 1;
float Clock = 120; float Clock = 15;
float ClockTick = (1/((Clock * PpQN)/60)) * 1000; float ClockTick = (1/((Clock * PpQN)/60)) * 1000;
unsigned long ClockTime = 0; unsigned long ClockTime = 0;
unsigned long LastStepTime = 0; unsigned long LastStepTime = 0;
@ -38,13 +50,16 @@ void setup() {
//Open Serial for output prior to installing a screen //Open Serial for output prior to installing a screen
Serial.begin( 115200 ); Serial.begin( 115200 );
Serial.println("Env Gen"); Serial.println("Momento Mortse");
randomSeed(analogRead(A7)); randomSeed(analogRead(A7));
pinMode(Enc1Btn, INPUT_PULLUP); pinMode(Enc1Btn, INPUT_PULLUP);
pinMode(Enc2Btn, INPUT_PULLUP); pinMode(Enc2Btn, INPUT_PULLUP);
pinMode(ClockIn, INPUT); pinMode(ClockIn, INPUT);
pinMode(ClockDetect, INPUT_PULLUP); pinMode(ClockDetect, INPUT_PULLUP);
pinMode(Channel1, OUTPUT);
pinMode(Channel2, OUTPUT);
pinMode(Channel3, OUTPUT);
E1Btn = digitalRead(Enc1Btn); E1Btn = digitalRead(Enc1Btn);
E1Prev = E1Btn; E1Prev = E1Btn;
@ -52,6 +67,9 @@ void setup() {
E2Prev = E2Btn; E2Prev = E2Btn;
ClockState = digitalRead(ClockIn); ClockState = digitalRead(ClockIn);
CDPrev = digitalRead(ClockDetect); CDPrev = digitalRead(ClockDetect);
digitalWrite(Channel1, LOW);
digitalWrite(Channel2, LOW);
digitalWrite(Channel2, LOW);
// Timer0 is already used for millis() - we'll just interrupt somewhere // Timer0 is already used for millis() - we'll just interrupt somewhere
// in the middle and call the "Compare A" function below // in the middle and call the "Compare A" function below
@ -103,22 +121,30 @@ void loop() {
if (ClockState){ if (ClockState){
unsigned long tmpClock = micros(); unsigned long tmpClock = micros();
float clkInTick = tmpClock - ClockPrev; float clkInTick = tmpClock - ClockPrev;
float newBPM = ((1.0/(clkInTick/1000000.0)) * 60.0)/(float)PpQN;
ClockPrev = tmpClock; ClockPrev = tmpClock;
if (abs(Clock - newBPM) > 0.5){ if (abs(ClockInPrev - clkInTick) > 500){
Clock = newBPM; Clock =((1.0/(clkInTick/1000000.0)) * 60.0)/(float)PpQN;
String outputBPM = "New BPM: "; String outputBPM = "New BPM: ";
outputBPM.concat(newBPM); outputBPM.concat(Clock);
Serial.println(outputBPM); Serial.println(outputBPM);
outputBPM = "Clock Tick: "; outputBPM = "Clock Tick: ";
outputBPM.concat(clkInTick); outputBPM.concat(clkInTick);
Serial.println(outputBPM); Serial.println(outputBPM);
ClockTick = (1/((Clock * PpQN)/60)) * 1000; ClockTick = (1/((Clock * PpQN)/60)) * 1000;
} }
else{
Beat = true;
LastBeat = tmpClock;
}
} }
} }
if ((LastBeat + ClockTick) < micros()){
Beat = true;
LastBeat = micros();
}
if (newLEnc != EncLeft || newREnc != EncRight){ if (newLEnc != EncLeft || newREnc != EncRight){
String output = "Left Enc Pos: "; String output = "Left Enc Pos: ";
output.concat(newLEnc); output.concat(newLEnc);
@ -144,8 +170,55 @@ void loop() {
} }
unsigned long currentTime = millis(); if (Beat){
} Beat = false;
if (BeatBounce == 0){
BeatBounce = DebounceTime;
//Insert Beat Output here!
char outputChar = TestText[Channel1Index];
Serial.println(outputChar);
if (outputChar == '\0' ){
Channel1Index = 0;
outputChar = TestText[Channel1Index];
}
byte morseLength = MorseTable[((int)outputChar - 32)];
byte morsePattern = morseLength;
bool trigger = (morsePattern >> MorseIndex) & 1;
if (MorseIndex == 0){
Channel1Index++;
outputChar = TestText[Channel1Index];
MorseIndex = (((MorseTable[((int)outputChar - 32)]) >> 5)) & 7;
}
MorseIndex--;
if (trigger){
digitalWrite(Channel1, HIGH);
Channel1Trig = DebounceTime;
Channel1State = true;
}
}
}
if ((Channel1State) && (Channel1Trig == 0)){
digitalWrite(Channel1, LOW);
Channel1State = false;
}
if (Channel2State && !Channel2Trig){
digitalWrite(Channel2, LOW);
Channel2State = false;
}
if (Channel3State && !Channel3Trig){
digitalWrite(Channel3, LOW);
Channel3State = false;
}
//Ui.tick();
}
// Interrupt is called once a millisecond, // Interrupt is called once a millisecond,
SIGNAL(TIMER0_COMPA_vect) SIGNAL(TIMER0_COMPA_vect)
@ -153,4 +226,8 @@ SIGNAL(TIMER0_COMPA_vect)
E1Bounce = (E1Bounce - 1) & 0b10000000; E1Bounce = (E1Bounce - 1) & 0b10000000;
E2Bounce = (E2Bounce - 1) & 0b10000000; E2Bounce = (E2Bounce - 1) & 0b10000000;
CDBounce = (CDBounce - 1) & 0b10000000; CDBounce = (CDBounce - 1) & 0b10000000;
} BeatBounce = (BeatBounce - 1) & 0b01111111;
Channel1Trig = (Channel1Trig - 1) & 0b01111111;
Channel2Trig = (Channel2Trig - 1) & 0b10000000;
Channel3Trig = (Channel3Trig - 1) & 0b10000000;
}

27
src/MortseGfx.cpp Normal file
View file

@ -0,0 +1,27 @@
#include "MortseGfx.h"
MortseGfx::MortseGfx(){
cursor_x = 0;
cursor_y = 0;
cursor_state = hidden;
}
void MortseGfx::MoveCursor(byte row, byte column){
}
void MortseGfx::SetChar(byte row, byte column, char value){
}
void MortseGfx::ClearChar(byte row, byte column){
}
void MortseGfx::SetCursorState(cursorState state){
}

58
src/MortseGfx.h Normal file
View file

@ -0,0 +1,58 @@
#ifndef MORTSE_GFX_H
#define MORTSE_GFX_H
#include <Arduino.h>
//#include <SPI.h>
//#include <Wire.h>
//#include <Adafruit_GFX.h>
//#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO: A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO: 2(SDA), 3(SCL), ...
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
//Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define NUMFLAKES 10 // Number of snowflakes in the animation example
#define LOGO_HEIGHT 16
#define LOGO_WIDTH 16
//static const unsigned char PROGMEM
enum cursorState {
hidden,
below
};
class MortseGfx
{
private:
byte cursor_x;
byte cursor_y;
enum cursorState cursor_state;
public:
MortseGfx();
void MoveCursor(byte row, byte column);
void SetChar(byte row, byte column, char value);
void ClearChar(byte row, byte column);
void SetCursorState(cursorState state);
};
#endif

109
src/MortseUi.cpp Normal file
View file

@ -0,0 +1,109 @@
///
#include "MortseUi.h"
//Public Methods
/// @brief Constructor for Mortse UI
MortseUI::MortseUI(byte port)
{
_nextCursorFlash = 0;
_gfx = new MortseGfx();
_cursorStyle = below;
_cursor_visible = true;
_cursorLine = 0;
_cursorPosition = 0;
_cursorLine_buff = 0;
_cursorPosition_buff = 0;
_updateCursor = false;
_line1[LINE_LENGTH];
_line2[LINE_LENGTH];
_line1_buff[LINE_LENGTH];
_line2_buff[LINE_LENGTH];
_updateLine1 = false;
_updateLine2 = false;
this->_incrementCursor();
//Initialize lines to be blank
for(int ix = 0; ix < LINE_LENGTH; ix++){
_line1[ix] = _line2[ix] = _line1_buff[ix] = _line2_buff[ix] = ' ';
}
}
/// @brief Called every frame
void MortseUi::Tick(){
if(millis() >= nextCursorFlash){
this->_toggleCursor();
this->_incrementCursor();
}
}
/// @brief Returns a pointer to the first line on the screen
/// @param line1 character pointer array which is the length of LINE_LENGTH
void MortseUi::GetLine1(char *line1){
line1 = &this->_line1_buff;
}
/// @brief Returns a pointer to the second line on the screen
/// @param line2 character pointer array which is the length of LINE_LENGTH
void MortseUi::GetLine2(char *line2){
line2 = &this->_line2_buff;
}
/// @brief Lets the graphics library know that the line1 array should be updated on the screen
void MortseUi::UpdateLine1(){
this->_updateLine(*_line1, *_line1_buff, 1);
}
/// @brief Lets the graphics library know that the line2 array should be updated on the screen
void MortseUi::UpdateLine2(){
this->_updateLine(*_line2, *_line2_buff, 2;
}
/// @brief Returns pointers to the line number and
/// @param line
/// @param position
void GetCursor(byte *line, byte* position){
line = &this->_curs
}
/// @brief Lets the graphics library know that the cursor position should be updated on the screen
void UpdateCursor(){
if(cursor)
}
/// Private Methods
void MortseUi::void _incrementCursor(){
nextCursorFlash = millis() + CURSOR_DELAY_MS;
}
void MortseUi::void _toggleCursor(){
if(this->_cursor_visible = !_cursor_visible){
this->_gfx.SetCursorState(this->_cursorStyle);
}
else{
this->_gfx.SetCursorState(hidden);
}
}
void MortseUi::_updateLine(byte *lineRef, byte *lineBuff, byte lineNo){
for(int ix = 0; ix > LINE_LENGTH; ix++){
if(lineRef[ix] != lineBuff[ix]){
lineRef[ix] = lineBuff[ix];
_gfx->SetChar(lineNo, ix, lineRef[ix]);
}
}
}

75
src/MortseUi.h Normal file
View file

@ -0,0 +1,75 @@
#ifndef MORTSE_UI_H
#define MORTSE_UI_H
#define CURSOR_DELAY_MS 500
#define LINE_LENGTH 16
#include <Arduino.h>
#include "MortseGfx.h"
class MortseUI{
private:
//Graphics Library
MortseGfx *_gfx();
//Cursor State
enum cursorState _cursorStyle;
bool _cursor_visible;
unsigned long _nextCursorFlash;
byte _cursorLine_buff;
byte _cursorLine;
byte _cursorPosition_buff;
byte _cursorPosition;
bool _updateCursor;
char _line1[LINE_LENGTH];
char _line2[LINE_LENGTH];
char _line1_buff[LINE_LENGTH];
char _line2_buff[LINE_LENGTH];
bool _updateLine1;
bool _updateLine2;
//Line State
//Cursor State Updates
void _incrementCursor();
void _toggleCursor();
void _updateLine(byte &lineRef, byte &lineBuff, byte lineNo);
public:
MortseUI(byte port);
/// @brief Called Every Frame
void Tick();
/// @brief Returns a pointer to the first line on the screen
/// @param line1 character pointer array which is the length of LINE_LENGTH
void GetLine1(char *line1);
/// @brief Returns a pointer to the second line on the screen
/// @param line2 character pointer array which is the length of LINE_LENGTH
void GetLine2(char *line2);
/// @brief Lets the graphics library know that the line1 array should be updated on the screen
void UpdateLine1();
/// @brief Lets the graphics library know that the line2 array should be updated on the screen
void UpdateLine2();
/// @brief Returns pointers to the line number and
/// @param line
/// @param position
void GetCursor(byte *line, byte* position);
/// @brief Lets the graphics library know that the cursor position should be updated on the screen
void UpdateCursor();
};
#endif