Files
RP2350_MIDI_Lighter/Firmware/Core1_Light_Controller.c
Chris 89c875e38f - First complete version of firmware. Currently being tested in the rehearsal room
- Added bunch of screens, fonts and images
 - Added script to read out frame buffer (function currently disabled in Firmware)
2025-10-26 20:57:58 +01:00

437 lines
15 KiB
C

/*
* Core1_Light_Controller.c
*
* Created: Sat Jan 21 2023 16:22:51
* Author Chris
*/
// ============================================================================================
// Includes
#include "Core1_Light_Controller.h"
#include "MIDI_Note_List.h"
#include "Core1_LED_Control.h"
#include "Command_Definition.h"
// ============================================================================================
// Defines
#define PAUSE_LIGHT_TIMEOUT_TICKS (_EEPROM_Content.Pause_Light_Configuration[ch].Timeout * (1000 / TIMER_INTERVALL_LED_UPDATE_ms))
#define NOTE_COLOR_RED _EEPROM_Content.Channel_MIDI_Configuration[ch].Note_Color_Red
#define NOTE_COLOR_GREEN _EEPROM_Content.Channel_MIDI_Configuration[ch].Note_Color_Green
#define NOTE_COLOR_BLUE _EEPROM_Content.Channel_MIDI_Configuration[ch].Note_Color_Blue
#define NOTE_COLOR_RED_ALT NOTE_COLOR_RED + 1
#define NOTE_COLOR_GREEN_ALT NOTE_COLOR_GREEN + 1
#define NOTE_COLOR_BLUE_ALT NOTE_COLOR_BLUE + 1
#define NOTE_COLOR_COUNT_RESET_THRESHOLD_TICKS (_EEPROM_Content.Channel_MIDI_Configuration[ch].Note_Reset_Timeout * (1000 / TIMER_INTERVALL_LED_UPDATE_ms))
// #define COUNT_APPLIED_NOTES
// ============================================================================================
// Datatypes
// ============================================================================================
// Variables
volatile bool _MIDI_To_Light_Enabled;
extern volatile EEPROM_Content_t _EEPROM_Content;
Info_Last_Received_Note_t _Info_Last_Received_Note[NUM_LED_CHANNELS];
Info_Last_Received_Note_t _Info_Last_Applied_Note[NUM_LED_CHANNELS];
volatile Pause_Light_Timer_s _Pause_Light_Timer[NUM_LED_CHANNELS];
volatile int _NoteOn_Color_Counter[NUM_LED_CHANNELS][NUM_LED_COLORS];
volatile int _NoteOn_Color_Reset_Counter;
// 1 LED Channel, 3 LED Colors, 2 Event Types (Note On & Off)
int32_t _Event_Received_Counter[NUM_LED_CHANNELS][NUM_LED_COLORS][2];
static const uint8_t _PWM_Lookup[NUM_LED_COLORS][128] = {
{ // Red
0x04, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0A, 0x0A, 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0B,
0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x10, 0x10, 0x11, 0x11,
0x12, 0x12, 0x13, 0x14, 0x14, 0x15, 0x15, 0x16, 0x17, 0x18, 0x19, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x25, 0x26, 0x28, 0x29, 0x2A, 0x2C, 0x2D, 0x2E, 0x30, 0x32,
0x33, 0x35, 0x37, 0x38, 0x3A, 0x3C, 0x3E, 0x40, 0x42, 0x45, 0x47, 0x49, 0x4B, 0x4E, 0x50, 0x53,
0x56, 0x58, 0x5B, 0x5E, 0x61, 0x64, 0x68, 0x6B, 0x6E, 0x72, 0x76, 0x79, 0x7E, 0x82, 0x86, 0x8B,
0x8F, 0x94, 0x99, 0x9E, 0xA6, 0xAC, 0xB2, 0xB9, 0xBF, 0xC6, 0xCD, 0xD5, 0xDD, 0xE5, 0xED, 0xFF
},
{ // Green
0x03, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08,
0x08, 0x09, 0x09, 0x09, 0x0A, 0x0A, 0x0B, 0x0B, 0x0C, 0x0C, 0x0C, 0x0D, 0x0E, 0x0E, 0x0F, 0x0F,
0x10, 0x11, 0x12, 0x12, 0x13, 0x14, 0x15, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
0x1E, 0x1F, 0x20, 0x21, 0x23, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2A, 0x2C, 0x2D, 0x2F, 0x30, 0x32,
0x33, 0x35, 0x37, 0x38, 0x3A, 0x3C, 0x3D, 0x3F, 0x41, 0x43, 0x45, 0x47, 0x49, 0x4B, 0x4D, 0x4F,
0x51, 0x53, 0x55, 0x57, 0x5A, 0x5C, 0x5E, 0x60, 0x63, 0x65, 0x68, 0x6B, 0x6F, 0x72, 0x75, 0x78,
0x7A, 0x7D, 0x81, 0x84, 0x87, 0x8A, 0x8D, 0x90, 0x94, 0x98, 0x9B, 0x9F, 0xA2, 0xA6, 0xAA, 0xAE,
0xB3, 0xB6, 0xBA, 0xBF, 0xC3, 0xC8, 0xCC, 0xD1, 0xD6, 0xDB, 0xE0, 0xE5, 0xE9, 0xEE, 0xF1, 0xFF
},
{ // Blue
0x01, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08,
0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x0A, 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0C, 0x0C, 0x0D, 0x0D,
0x0E, 0x0E, 0x0F, 0x0F, 0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x14, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x25, 0x26, 0x27, 0x29, 0x2A,
0x2B, 0x2D, 0x2E, 0x30, 0x31, 0x33, 0x35, 0x36, 0x38, 0x39, 0x3B, 0x3D, 0x3F, 0x40, 0x42, 0x44,
0x46, 0x49, 0x4B, 0x4E, 0x50, 0x52, 0x55, 0x57, 0x59, 0x5C, 0x5E, 0x61, 0x63, 0x66, 0x69, 0x6C,
0x6F, 0x72, 0x75, 0x77, 0x7B, 0x7E, 0x81, 0x85, 0x88, 0x8C, 0x90, 0x93, 0x97, 0x9B, 0x9F, 0xA3,
0xA8, 0xAC, 0xB0, 0xB5, 0xBA, 0xBE, 0xC4, 0xC9, 0xCE, 0xD4, 0xD9, 0xDF, 0xE4, 0xEA, 0xEF, 0xFF
}
};
// ============================================================================================
// Function Declarations
bool Core1_Light_Controller_Check_Channel_Match (enum LED_Channel channel, uint8_t midi_channel);
bool Core1_Light_Controller_Check_Octave_Match (enum LED_Channel channel, uint midi_note);
void Core1_Light_Controller_Pause_Light_Trigger (uint8_t midi_event, uint8_t midi_channel);
void Core1_Light_Controller_Reset_NoteOn_Counter();
/*******************************************************************
Interrupt Service Routines
*******************************************************************/
/*******************************************************************
Functions
*******************************************************************/
void Core1_Light_Controller_Init(void)
{
_MIDI_To_Light_Enabled = true;
for(uint32_t ch=0;ch<NUM_LED_CHANNELS;ch++)
{
_Info_Last_Received_Note[ch].Event = 0;
_Info_Last_Received_Note[ch].Note = NO_NOTE;
_Info_Last_Received_Note[ch].Note_In_Octave = NO_NOTE;
_Info_Last_Received_Note[ch].Velocity = 0;
_Info_Last_Received_Note[ch].Timestamp_ms = 0;
_Info_Last_Applied_Note[ch].Event = 0;
_Info_Last_Applied_Note[ch].Note = NO_NOTE;
_Info_Last_Applied_Note[ch].Note_In_Octave = NO_NOTE;
_Info_Last_Applied_Note[ch].Velocity = 0;
_Info_Last_Applied_Note[ch].Timestamp_ms = 0;
for(uint32_t col=0;col < NUM_LED_COLORS;col++) {
_Event_Received_Counter[ch][col][MIDI_EVENT_NOTE_OFF - MIDI_EVENT_NOTE_OFF] = 0;
_Event_Received_Counter[ch][col][MIDI_EVENT_NOTE_ON - MIDI_EVENT_NOTE_OFF] = 0;
}
}
Core1_Light_Controller_Reset_NoteOn_Counter();
_NoteOn_Color_Reset_Counter = 0;
}
void Core1_Light_Controller_Tick(void)
{
if(_MIDI_To_Light_Enabled != true) {
return;
}
if(Core1_LED_Control_Get_Timer_Fired() != true) {
return;
}
for(uint ch=0;ch<NUM_LED_CHANNELS;ch++)
{
if(_EEPROM_Content.Pause_Light_Configuration[ch].Enabled == 0) {
continue;
}
if(_Pause_Light_Timer[ch].Timer == PAUSE_LIGHT_TIMEOUT_TICKS && _Pause_Light_Timer[ch].Is_Active != true)
{
_Pause_Light_Timer[ch].Is_Active = true;
Core1_LED_Control_Set_Fade_Speed(ch, _EEPROM_Content.Pause_Light_Configuration[ch].Fade_Speed);
Core1_LED_Control_Set_DC_Target(ch, R, _EEPROM_Content.Pause_Light_Configuration[ch].Color.R);
Core1_LED_Control_Set_DC_Target(ch, G, _EEPROM_Content.Pause_Light_Configuration[ch].Color.G);
Core1_LED_Control_Set_DC_Target(ch, B, _EEPROM_Content.Pause_Light_Configuration[ch].Color.B);
}
if(_Pause_Light_Timer[ch].Timer < PAUSE_LIGHT_TIMEOUT_TICKS) {
_Pause_Light_Timer[ch].Timer++;
}
if(_EEPROM_Content.Channel_MIDI_Configuration[0].Note_Reset_Enabled > 0)
{
if(_NoteOn_Color_Reset_Counter < NOTE_COLOR_COUNT_RESET_THRESHOLD_TICKS) {
_NoteOn_Color_Reset_Counter++;
}
else {
Core1_Light_Controller_Reset_NoteOn_Counter();
}
}
}
}
void Core1_Light_Controller_MIDI_OnOff_Event_Received(uint8_t midi_command_shifted_right, uint8_t midi_channel)
{
if(_MIDI_To_Light_Enabled != true) {
return;
}
Core1_Light_Controller_Pause_Light_Trigger(midi_command_shifted_right, midi_channel);
}
void Core1_Light_Controller_MIDI_Other_Event_Received(uint8_t midi_data)
{
if(_MIDI_To_Light_Enabled != true) {
return;
}
if(IS_MIDI_COMMAND_WITH_CHANNEL(midi_data)) {
Core1_Light_Controller_Pause_Light_Trigger(MIDI_EVENT_FROM_COMMAND(midi_data), MIDI_CHANNEL_FROM_COMMAND(midi_data));
} else {
Core1_Light_Controller_Pause_Light_Trigger(midi_data, MIDI_CHANNEL_16 + 1);
}
}
void Core1_Light_Controller_MIDI_Full_Note_Received(uint8_t midi_event, uint8_t midi_channel, uint8_t midi_note, uint8_t value)
{
if(_MIDI_To_Light_Enabled != true) {
return;
}
Core1_Light_Controller_Pause_Light_Trigger(midi_event, midi_channel);
for(uint ch=0;ch<NUM_LED_CHANNELS;ch++)
{
if(Core1_Light_Controller_Check_Channel_Match(ch, midi_channel) != true) {
continue;
}
if(Core1_Light_Controller_Check_Octave_Match(ch, midi_note) != true) {
continue;
}
uint8_t midi_note_in_octave = midi_note % NOTES_PER_OCTAVE;
_Info_Last_Received_Note[ch].Event = midi_event;
_Info_Last_Received_Note[ch].Note = midi_note;
_Info_Last_Received_Note[ch].Note_In_Octave = midi_note_in_octave;
_Info_Last_Received_Note[ch].Velocity = value;
_Info_Last_Received_Note[ch].Timestamp_ms = to_ms_since_boot(get_absolute_time());
_NoteOn_Color_Reset_Counter = 0;
bool Note_Applied = true;
if(midi_event == MIDI_EVENT_NOTE_ON)
{
if(midi_note_in_octave == NOTE_COLOR_RED || midi_note_in_octave == NOTE_COLOR_RED_ALT)
{
uint8_t DutyCycle = value;
if(_EEPROM_Content.Device_Configuration.Use_Color_Correction > 0) { DutyCycle = _PWM_Lookup[R][value]; } else { DutyCycle = value << 1; }
Core1_LED_Control_Set_DC_Direct(ch, R, DutyCycle);
_NoteOn_Color_Counter[ch][R]++;
#ifdef COUNT_APPLIED_NOTES
_Event_Received_Counter[ch][R][MIDI_EVENT_NOTE_ON - MIDI_EVENT_NOTE_OFF]++;
#endif
}
else if(midi_note_in_octave == NOTE_COLOR_GREEN || midi_note_in_octave == NOTE_COLOR_GREEN_ALT)
{
uint8_t DutyCycle = value;
if(_EEPROM_Content.Device_Configuration.Use_Color_Correction > 0) { DutyCycle = _PWM_Lookup[G][value]; } else { DutyCycle = value << 1; }
Core1_LED_Control_Set_DC_Direct(ch, G, DutyCycle);
_NoteOn_Color_Counter[ch][G]++;
#ifdef COUNT_APPLIED_NOTES
_Event_Received_Counter[ch][G][MIDI_EVENT_NOTE_ON - MIDI_EVENT_NOTE_OFF]++;
#endif
}
else if(midi_note_in_octave == NOTE_COLOR_BLUE || midi_note_in_octave == NOTE_COLOR_BLUE_ALT)
{
uint8_t DutyCycle = value;
if(_EEPROM_Content.Device_Configuration.Use_Color_Correction > 0) { DutyCycle = _PWM_Lookup[B][value]; } else { DutyCycle = value << 1; }
Core1_LED_Control_Set_DC_Direct(ch, B, DutyCycle);
_NoteOn_Color_Counter[ch][B]++;
#ifdef COUNT_APPLIED_NOTES
_Event_Received_Counter[ch][B][MIDI_EVENT_NOTE_ON - MIDI_EVENT_NOTE_OFF]++;
#endif
}
else
{
Note_Applied = false;
}
}
else if((midi_event == MIDI_EVENT_NOTE_OFF && _EEPROM_Content.Channel_MIDI_Configuration[ch].Skip_Note_Off_Event == 0))
{
if(midi_note_in_octave == NOTE_COLOR_RED || midi_note_in_octave == NOTE_COLOR_RED_ALT)
{
_NoteOn_Color_Counter[ch][R]--;
if(_NoteOn_Color_Counter[ch][R] == 0) { Core1_LED_Control_Set_DC_Direct(ch, R, 0); }
#ifdef COUNT_APPLIED_NOTES
_Event_Received_Counter[ch][R][MIDI_EVENT_NOTE_OFF - MIDI_EVENT_NOTE_OFF]++;
#endif
}
else if(midi_note_in_octave == NOTE_COLOR_GREEN || midi_note_in_octave == NOTE_COLOR_GREEN_ALT)
{
_NoteOn_Color_Counter[ch][G]--;
if(_NoteOn_Color_Counter[ch][G] == 0) { Core1_LED_Control_Set_DC_Direct(ch, G, 0); }
#ifdef COUNT_APPLIED_NOTES
_Event_Received_Counter[ch][G][MIDI_EVENT_NOTE_OFF - MIDI_EVENT_NOTE_OFF]++;
#endif
}
else if(midi_note_in_octave == NOTE_COLOR_BLUE || midi_note_in_octave == NOTE_COLOR_BLUE_ALT)
{
_NoteOn_Color_Counter[ch][B]--;
if(_NoteOn_Color_Counter[ch][B] == 0) { Core1_LED_Control_Set_DC_Direct(ch, B, 0); }
#ifdef COUNT_APPLIED_NOTES
_Event_Received_Counter[ch][B][MIDI_EVENT_NOTE_OFF - MIDI_EVENT_NOTE_OFF]++;
#endif
}
else
{
Note_Applied = false;
}
}
else
{
Note_Applied = false;
}
if(Note_Applied == true) {
_Info_Last_Applied_Note[ch].Event = midi_event;
_Info_Last_Applied_Note[ch].Note = midi_note;
_Info_Last_Applied_Note[ch].Note_In_Octave = midi_note_in_octave;
_Info_Last_Applied_Note[ch].Velocity = value;
_Info_Last_Applied_Note[ch].Timestamp_ms = to_ms_since_boot(get_absolute_time());
}
}
}
void Core1_Light_Controller_Set_MIDI_To_Light_Enabled(bool enabled)
{
_MIDI_To_Light_Enabled = enabled;
for(uint ch=0;ch<NUM_LED_CHANNELS;ch++) {
_Pause_Light_Timer[ch].Timer = 0;
_Pause_Light_Timer[ch].Is_Active = false;
}
}
/*******************************************************************
Internal Functions
*******************************************************************/
bool Core1_Light_Controller_Check_Channel_Match(enum LED_Channel channel, uint8_t midi_channel)
{
if(channel >= NUM_LED_CHANNELS) {
return false;
}
return (midi_channel == _EEPROM_Content.Channel_MIDI_Configuration[channel].MIDI_Channel);
}
bool Core1_Light_Controller_Check_Octave_Match(enum LED_Channel channel, uint midi_note)
{
if(channel >= NUM_LED_CHANNELS) {
return false;
}
if(midi_note >= MIDI_NOTE_LIST_LENGTH) {
return false;
}
return (_MIDI_Note_List[midi_note].Octave == _EEPROM_Content.Channel_MIDI_Configuration[channel].Octave);
}
void Core1_Light_Controller_Pause_Light_Trigger(uint8_t midi_event, uint8_t midi_channel)
{
for(uint ch=0;ch<NUM_LED_CHANNELS;ch++)
{
bool Match_Success = false;
switch(_EEPROM_Content.Pause_Light_Configuration[ch].Reset_Condition)
{
case CHANNEL_MATCH:
if(midi_channel == _EEPROM_Content.Channel_MIDI_Configuration[ch].MIDI_Channel) {
Match_Success = true;
}
break;
case EVENT_MATCH:
if(midi_event == MIDI_EVENT_NOTE_ON || midi_event == MIDI_EVENT_NOTE_OFF) {
Match_Success = true;
}
break;
case CHANNEL_AND_EVENT_MATCH:
if( (midi_channel == _EEPROM_Content.Channel_MIDI_Configuration[ch].MIDI_Channel) &&
(midi_event == MIDI_EVENT_NOTE_ON || midi_event == MIDI_EVENT_NOTE_OFF))
{
Match_Success = true;
}
break;
case ANY_TRAFFIC:
default:
Match_Success = true;
break;
}
if(Match_Success) {
if(_Pause_Light_Timer[ch].Is_Active == true)
{
_Pause_Light_Timer[ch].Is_Active = false;
Core1_LED_Control_Set_DC_Direct(ch, R, 0);
Core1_LED_Control_Set_DC_Direct(ch, G, 0);
Core1_LED_Control_Set_DC_Direct(ch, B, 0);
Core1_LED_Control_Set_DC_Target(ch, R, 0);
Core1_LED_Control_Set_DC_Target(ch, G, 0);
Core1_LED_Control_Set_DC_Target(ch, B, 0);
}
_Pause_Light_Timer[ch].Timer = 0;
Core1_LED_Control_Set_Fade_Speed(ch, 0);
}
}
}
void Core1_Light_Controller_Reset_NoteOn_Counter()
{
for(uint ch=0;ch<NUM_LED_CHANNELS;ch++)
{
for(uint l=0;l<NUM_LED_COLORS;l++)
{
if(_NoteOn_Color_Counter[ch][l] > 0) {
Core1_LED_Control_Set_DC_Direct(ch, l, 0);
}
_NoteOn_Color_Counter[ch][l] = 0;
}
}
}
Note_t Core1_Light_Controller_Get_Octave_Note_For_Channel(enum LED_Channel channel)
{
if(channel >= NUM_LED_CHANNELS) {
return NO_NOTE;
}
return _Info_Last_Applied_Note[channel].Note_In_Octave;
}
Value_t Core1_Light_Controller_Get_Note_Value_For_Channel(enum LED_Channel channel)
{
if(channel >= NUM_LED_CHANNELS) {
return 0;
}
return _Info_Last_Applied_Note[channel].Velocity;
}