Files
RP2350_MIDI_Lighter/Firmware/Core1_Light_Controller.c
Chris 128d42c586 - Fixed drawing of round objects (Circles, Rounded Rects) using a lookup table
- Added function to read out the display_buffer via USB-Serial
 - Added basic structure and files for later complete firmware (still in progress)
 - Added Doc folder with schematic in it
 - Added Python script and batch file to read out the display buffer and open the image in gimp
2025-09-07 08:55:39 +02:00

400 lines
14 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 100 // 100 * 10 ms -> 1s
// ============================================================================================
// Datatypes
// ============================================================================================
// Variables
volatile bool _MIDI_To_Light_Enabled;
extern volatile EEPROM_Content_t _EEPROM_Content;
volatile Info_Last_Received_Note_t _Info_Last_Received_Note[NUM_LED_CHANNELS];
volatile 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;
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(uint 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;
}
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(_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]++;
}
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]++;
}
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]++;
}
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); }
}
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); }
}
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); }
}
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;
}