From 2a3940d8a36433485a6ef489d5123cd491618b50 Mon Sep 17 00:00:00 2001 From: Duncan Wilkie Date: Mon, 7 Aug 2023 17:29:29 -0500 Subject: slave progress and WCET --- controller/Makefile | 14 +- controller/inc/base_midi.h | 15 +- controller/ld/TM4C123GH6PM.ld | 20 +- controller/libs/base_midi.c | 47 +- controller/libs/midi_ci.c | 536 ++++++++++++++++++++- controller/src/main.c | 104 +++- specs/AVR-Instruction-Set-Manual-DS40002198A.pdf | Bin 0 -> 1196344 bytes ...8_bit_Microcontroller_ATtiny25_ATti-1315542.pdf | Bin 0 -> 2644021 bytes 8 files changed, 688 insertions(+), 48 deletions(-) create mode 100644 specs/AVR-Instruction-Set-Manual-DS40002198A.pdf create mode 100644 specs/Atmel_2586_AVR_8_bit_Microcontroller_ATtiny25_ATti-1315542.pdf diff --git a/controller/Makefile b/controller/Makefile index 9be9eb3..aedbfe4 100644 --- a/controller/Makefile +++ b/controller/Makefile @@ -17,23 +17,23 @@ SRCS = $(wildcard src/*.c) \ OBJ = obj/ OBJS = $(addprefix $(OBJ),$(notdir $(SRCS:.c=.o))) LD_SCRIPT = ld/$(MCU).ld -IPATH = /home/dnw/Code/TivaC/libs/driverlib +IPATH = /home/dnw/Code/TivaC/libs # Flags. CFLAGS = -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -ffunction-sections \ - -fdata-sections -MD -std=c2x -Wall -Wextra -Werror -DPART_${MCU} -c -Os -Dgcc -ggdb + -fdata-sections -MD -std=c2x -Wall -Wextra -Werror -DPART_${MCU} -c -O0 -Dgcc -ggdb CFLAGS += ${patsubst %,-I%,${subst :, ,${IPATH}}} -iquote inc # Compiler/standard resource locations. LIBGCC := ${shell ${CC} ${CFLAGS} -print-libgcc-file-name} -LIBC := ${shell ${CC} ${CFLAGS} -print-file-name=libc.a} +LIBC := ${shell ${CC} ${CFLAGS} -print-file-name=libc_nano.a} LIBM := ${shell ${CC} ${CFLAGS} -print-file-name=libm.a} +LIBG := ${shell ${CC} ${CFLAGS} -print-file-name=libg_nano.a} +LIBSTUB := ${shell ${CC} ${CFLAGS} -print-file-name=libnosys.a} +LIBDRIVER := /home/dnw/Code/TivaC/libs/driverlib/gcc/libdriver.a # More flags. -LDFLAGS = -T $(LD_SCRIPT) -e Reset_Handler --gc-sections '${LIBGCC}' '${LIBC}' '${LIBM}' - - - +LDFLAGS = -T $(LD_SCRIPT) --gc-sections '${LIBM}' '${LIBGCC}' '${LIBDRIVER}' '${LIBC}' '${LIBSTUB}' # Targets. diff --git a/controller/inc/base_midi.h b/controller/inc/base_midi.h index 8320803..b6e5b19 100644 --- a/controller/inc/base_midi.h +++ b/controller/inc/base_midi.h @@ -8,7 +8,8 @@ // Configuration. #define DEBUG 0 // If defined, enable input safety checking (e.g. too large data bytes, bad ID numbers, etc). // These checks have some performance downside. -#define RUNNING_STATUS 0 // If defined, the MIDI instrument stores a transmit status and uses running statuses whenever possible. +/* #define RUNNING_STATUS 0 // If defined, the MIDI instrument stores a transmit status and uses running statuses whenever possible .*/ + #define KEY_ON_VELOCITY 0 // If defined, the MIDI instrument sends velocities with Note On messages. #undef RELEASE_VELOCITY // If defined, the MIDI instrument sends Note Off messages with velocities. // Otherwise, sends Note On velocity 0 or Note Off velocity 0 according to ACTUAL_OFF_MESSAGE. @@ -69,7 +70,17 @@ typedef struct { bool (*unimplemented_universal_sysex_collector)(uint8_t); // Same as above. Parsing ends with end_of_sysex_handler. } ConsumerBehavior; -extern ConsumerBehavior pfns; // Initialize this globally in your main. +typedef struct { + uint8_t status; + bool first_byte; + uint8_t byte0; + bool send_eox; + uint8_t *stack; + size_t top; + size_t size; +} ParserMemory; + +void midi_init(ConsumerBehavior new_pfns); // System exclusive sends. void sysex(uint16_t manufacturer_id, uint8_t* contents, size_t contents_len); diff --git a/controller/ld/TM4C123GH6PM.ld b/controller/ld/TM4C123GH6PM.ld index 29ff8d8..611ac9e 100644 --- a/controller/ld/TM4C123GH6PM.ld +++ b/controller/ld/TM4C123GH6PM.ld @@ -21,13 +21,19 @@ * Description: linker file for the TM4C Launchpad */ +ENTRY(Reset_Handler) + MEMORY { + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 256K /* FLASH size 256KB */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K/* RAM size 32KB */ } +_Min_Heap_Size = 0x3000; +_Min_Stack_Size = 0x3000; + SECTIONS { @@ -38,7 +44,17 @@ SECTIONS * those lesser than 0x20007FFF to 0x2000000,which is the origina address of RAM, * until it comes in contact with .bss or .data in which case a buffer overflow occurs */ - PROVIDE( _stack_ptr = ORIGIN(RAM) + LENGTH(RAM)); + PROVIDE( _stack_ptr = ORIGIN(RAM) + _Min_Stack_Size); + + ._user_heap_stack : + { + . = ALIGN(8); + . = . + _Min_Stack_Size; + . = . + _Min_Heap_Size; + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + . = ALIGN(8); + } > RAM /* constants and other code stored in FLASH */ .text : @@ -67,6 +83,4 @@ SECTIONS _ebss = .; /* end of .bss segment */ } > RAM - - } diff --git a/controller/libs/base_midi.c b/controller/libs/base_midi.c index 6efd2f3..0394406 100644 --- a/controller/libs/base_midi.c +++ b/controller/libs/base_midi.c @@ -1,14 +1,18 @@ - // This file is written to be read side-along with the MIDI 1.0 spec. +// This file is written to be read side-along with the MIDI 1.0 spec. // Note the tables at its very end, with /absolutely no direct allusion in the text/. #include "base_midi.h" #include #include -// TODO: startup function to initialize ParseMemory buffer. + #ifdef RUNNING_STATUS uint8_t last_status = 0x00; #endif +ConsumerBehavior pfns; +ParserMemory memory; + + // Helper functions #define MAX_DATA_BYTE 0b01111111 static inline void MIDI_bare_send(uint8_t status) { @@ -539,24 +543,21 @@ void single_note_tuning_change(uint8_t device_id, uint8_t program, uint8_t *note // Parsing. // Stream parsing. -typedef struct { - uint8_t status; - bool first_byte; - uint8_t byte0; - bool send_eox; - uint8_t *stack; - size_t top; - size_t size; -} ParserMemory; #define WAITING_FOR_STATUS 0x00 -ParserMemory memory = {.status = WAITING_FOR_STATUS, - .first_byte = false, - .byte0 = 0x00, - .send_eox = false, - .stack = NULL, // TODO: initialize from main. - .top = 0, - .size = 128}; + + +void midi_init(ConsumerBehavior new_pfns) { + memory.status = WAITING_FOR_STATUS; + memory.first_byte = false; + memory.send_eox = false; + memory.stack = malloc(128); + memory.top = 0; + memory.size = 128; + + pfns = new_pfns; +} + static inline void push_fn(uint8_t val) { if (memory.top == memory.size - 1) { @@ -571,14 +572,6 @@ static inline void push_fn(uint8_t val) { push_fn(byte); \ } while(0) -static inline uint8_t pop() { - if (memory.size - memory.top > 128) { - memory.stack = realloc(memory.stack, memory.size - 128); - } - - --memory.top; - return memory.stack[memory.top + 1]; -} static inline void reset() { memory.stack = realloc(memory.stack, 128); @@ -594,7 +587,7 @@ static inline void reset() { #define channel (memory.status & 0x0f) - +#define MIDI_CI_SUBID 0x0d // Memory should be large enough to handle the largest message that's expected to be processed automatically here. void parse_midi_stream(uint8_t byte) { diff --git a/controller/libs/midi_ci.c b/controller/libs/midi_ci.c index 7013d7a..cecef52 100644 --- a/controller/libs/midi_ci.c +++ b/controller/libs/midi_ci.c @@ -4,9 +4,13 @@ // - MIDI CI Specification, minimum requirements + Property Exchange chapter, // - Common Rules for MIDI CI Property Exchange. +#define DISABLE true +#ifndef DISABLE #include "base_midi.h" +#include "parson.h" uint32_t our_muid = rand(); // TODO: think. +uint8_t request_id = 0; #define MIDI_CI_SUBID 0x0d #define PROFILE_CONFIGURATION_MASK 0x20 @@ -16,7 +20,7 @@ uint32_t our_muid = rand(); // TODO: think. #define MIDI_CI_VERSION 0x02 #define TO_FUNCTION_BLOCK_ID 0x7f static void ci(uint8_t source_target, uint8_t message_type, uint32_t destination_miud, - uint8_t *contents, uint8_t contents_len) { + uint8_t *contents, size_t contents_len) { uint8_t *new = malloc(1 + 4 + 4 + contents_len); new[0] = MIDI_CI_VERSION; new[1] = our_muid & 0x00'00'00'ff; @@ -106,16 +110,14 @@ void discovery_reply(uint8_t initiator_muid, uint8_t initiator_output_path_id) { } -// TODO: Decide if Inquiry: Endpoint and Reply to Endpoint are necessary for standard conformace if we're over MIDI 1.0. - // TODO: should we set a new MUID inside this function? #define INVALIDATE_MUID_SUBID 0x7e -void invalidate_muid() { +void invalidate_muid(uint8_t muid) { uint8_t *new = malloc(4); - new[0] = our_muid & 0x00'00'00'ff; - new[1] = (our_muid >> 8) & 0x00'00'00'ff; - new[2] = (our_muid >> 16) & 0x00'00'00'ff; - new[3] = (our_muid >> 24) & 0x00'00'00'ff; + new[0] = muid & 0x00'00'00'ff; + new[1] = (muid >> 8) & 0x00'00'00'ff; + new[2] = (muid >> 16) & 0x00'00'00'ff; + new[3] = (muid >> 24) & 0x00'00'00'ff; ci(TO_FUNCTION_BLOCK_ID, BROADCAST_MUID, new, 4); @@ -123,6 +125,10 @@ void invalidate_muid() { } +static inline void invalidate_our_muid() { + invalidate_muid(our_muid); +} + // TODO: consider structifying? #define ACK_SUBID 0x7d void ci_ack(uint8_t acking_device_id, uint8_t acking_muid, uint8_t original_classification, uint8_t status_code, @@ -172,3 +178,517 @@ void ci_nack(uint8_t naking_device_id, uint8_t naking_muid, uint8_t original_cla free(new); } + +#define WHOLE_FUNCTION_BLOCK_ID 0x0f +#define PROPERTY_EXCHANGE_MAJOR_VERSION 0x00 +#define PROPERTY_EXCHANGE_MINOR_VERSION 0x00 +#define SIMULTANEOUS_REQUESTS 0x01 +#define INQUIRE_EXCHANGE_CAPABILITIES_SUBID 0x30 +void inquire_property_exchange_capabilities(uint8_t destination_muid) { + uint8_t *new = malloc(3); + new[0] = SIMULTANEOUS_REQUESTS; + new[1] = PROPERTY_EXCHANGE_MAJOR_VERSION; + new[2] = PROPERTY_EXCHANGE_MINOR_VERSION; + + ci(WHOLE_FUNCTION_BLOCK_ID, INQUIRE_EXCHANGE_CAPABILITIES_SUBID, destination_muid, new, 3); + + free(new); + +} + +#define REPLY_EXCHANGE_CAPABILITIES_SUBID 0x31 +void reply_proprty_exchange_capabilities (uint8_t initiator_muid) { + uint8_t *new = malloc(3); + new[0] = SIMULTANEOUS_REQUESTS; + new[1] = PROPERTY_EXCHANGE_MAJOR_VERSION; + new[2] = PROPERTY_EXCHANGE_MINOR_VERSION; + + ci(WHOLE_FUNCTION_BLOCK_ID, REPLY_EXCHANGE_CAPABILITIES_SUBID, initiator_muid, new, 3); + + free(new); + +} + +// TODO: complain; standard doesn't specify what goes into the "Max SysEx size." E.g. do I include the EOX? +#define CI_MESSAGE_NONVARIABLE_SIZE (1 + 1 + 1 + 1 + 1 + 1 + 4 + 4 + 1 + 2 + 2 + 2 + 2 + 1) +#define min(X, Y) ((X) < (Y) ? (X) : (Y)) +static void chunked_property_exchange_send(uint32_t receiver_max_sysex, uint8_t message_subid, uint32_t destination_muid, + uint8_t request_id, uint8_t *header_data, uint16_t header_len, + uint8_t *property_data, uint16_t property_len) { + uint32_t total_len = CI_MESSAGE_NONVARIABLE_SIZE + header_len + property_len; + uint32_t leftover_bytes = total_len % receiver_max_sysex; + uint32_t chunk_count = total_len / receiver_max_sysex + (leftover_bytes == 0 ? 0 : 1); + uint32_t leftover_property_bytes = leftover_bytes - CI_MESSAGE_NONVARIABLE_SIZE; + uint32_t max_per_message_property_bytes = receiver_max_sysex - CI_MESSAGE_NONVARIABLE_SIZE; + + uint8_t *new = malloc(3 + header_len + 6 + max_per_message_property_bytes); + new[0] = request_id; + new[1] = header_len & 0x00ff; + new[2] = header_len >> 8; + memcpy(new + 3, header_data, header_len); + + for (uint32_t chunk = 1; chunk < chunk_count, i++) { + new[3 + header_len] = chunk_count & 0x00ff; + new[3 + header_len + 1] = chunk_count >> 8; + new[3 + header_len + 2] = chunk & 0x00ff; + new[3 + header_len + 3] = chunk >> 8; + new[3 + header_len + 4] = max_per_message_property_bytes & 0x00ff; + new[3 + header_len + 5] = max_per_message_property_bytes >> 8; + + memcpy(3 + header_len + 6, property_data + (chunk - 1) * max_per_message_property_bytes, max_per_message_property_bytes); + + ci(WHOLE_FUNCTION_BLOCK_ID, message_subid, destination_muid, new, 3 + header_len + 6 + max_per_message_property_bytes); + + } + + if (leftover_property_bytes != 0) { + new[3 + header_len] = chunk_count & 0x00ff; + new[3 + header_len + 1] = chunk_count >> 8; + new[3 + header_len + 2] = chunk & 0x00ff; + new[3 + header_len + 3] = chunk >> 8; + new[3 + header_len + 4] = leftover_property_bytes & 0x00ff; + new[3 + header_len + 5] = leftover_property_bytes >> 8; + + memcpy(3 + header_len + 6, property_data + (chunk_count - 1) * max_per_message_property_bytes, leftover_property_bytes); + + ci(WHOLE_FUNCTION_BLOCK_ID, message_subid, destination_muid, new, 3 + header_len + 6 + leftover_property_bytes); + + } + + + free(new); + +} + +#define INQUIRE_GET_SUBID 0x34 +void inquiry_get_property(uint32_t destination_muid, uint8_t request_id, uint8_t *header_data, uint16_t header_len) { + uint8_t *new = malloc(3 + header_len + 3); + new[0] = request_id; + new[1] = header_len & 0x00ff; + new[2] = header_len >> 8; + + memcpy(new + 3, header_data, header_len); + + // Chunk count. + new[3 + header_len] = 0x01; + new[3 + header_len + 1] = 0x00; + // Current chunk number. + new[3 + header_len + 2] = 0x01; + new[3 + header_len + 3] = 0x00; + // Property length. + new[3 + header_len + 4] = 0x00; + new[3 + header_len + 5] = 0x00; + + ci(WHOLE_FUNCTION_BLOCK_ID, INQUIRE_GET_SUBID, destination_muid, new, 3 + header_len + 6); + + free(new); +} + +#define REPLY_GET_SUBID 0x35 +void reply_get_property(uint32_t receiver_max_sysex, uint32_t destination_muid, uint8_t request_id, uint8_t *header_data, + uint16_t header_len, uint8_t *property_data, uint16_t property_len) { + + + chunked_property_exchange_send(receiver_max_sysex, REPLY_GET_SUBID, destination_muid, request_id, header_data, + header_len, property_data, property_len); + +} + +#define INQURIE_SET_SUBID 0x36 +void inquiry_set_property(uint32_t receiver_max_sysex, uint32_t destination_muid, uint8_t request_id, uint8_t *header_data, + uint16_t header_len, uint8_t *property_data, uint16_t property_len) { + + chunked_property_exchange_send(receiver_max_sysex, INQUIRE_GET_SUBID, destination_muid, request_id, + header_data, header_len, property_data, property_len) + +} + +#define REPLY_SET_SUBID 0x37 +void reply_set_property(uint32_t receiver_max_sysex, uint32_t destination_muid, uint8_t request_id, uint8_t *header_data, + uint16_t header_len, uint8_t *property_data, uint16_t property_len) { + + + chunked_property_exchange_send(receiver_max_sysex, REPLY_SET_SUBID, destination_muid, request_id, + header_data, header_len, property_data, property_len); + +} + +#define SUBSCRIPTION_SUBID 0x38 +void subscription(uint32_t receiver_max_sysex, uint32_t destination_muid, uint8_t request_id, uint8_t *header_data, + uint16_t header_len, uint8_t *property_data, uint16_t property_len) { + + chunked_property_exchange_send(receiver_max_sysex, SUBSCRIPTION_SUBID, destination_muid, request_id, + header_data, header_len, property_data, property_len); + +} + +#define REPLY_SUBSCRIPTION_SUBID 0x39 +void reply_subscription(uint32_t receiver_max_sysex, uint32_t destination_muid, uint8_t request_id, uint8_t *header_data, + uint16_t header_len, uint8_t *property_data, uint16_t property_len) { + + chunked_property_exchange_send(receiver_max_sysex, REPLY_SUBSCRIPTION_SUBID, destination_muid, request_id, + header_data, header_len, property_data, property_len); + +} + +#define NOTIFY_SUBID 0x3f +void notify(uint32_t receiver_max_sysex, uint32_t destination_muid, uint8_t request_id, uint8_t *header_data, + uint16_t header_len, uint8_t *property_data, uint16_t property_len) { + + chunked_property_exchange_send(receiver_max_sysex, NOTIFY_SUBID, destination_muid, request_id, + header_data, header_len, property_data, property_len); + +} + + +// Parsing. + +typedef enum { + DEVICE_ID, + SUBID, + VERSION, + SOURCE, + DESTINATION, + // Discovery. + MANUFACTURER, + FAMILY, + MODEL, + REVISION, + CI_CATEGORY, + MAX_SYSEX, + INITIATOR_OUTPUT_PATH, + // Invalidate. + TARGET_MUID, + // Ack. + ACK_ORIG_SUBID, + ACK_CODE, + ACK_DATA, + ACK_DETAILS, + ACK_TEXT_LENGTH, + ACK_TEXT, + // Nak. + NAK_ORIG_SUBID, + NAK_CODE, + NAK_DATA, + NAK_DETAILS, + NAK_LENGTH, + NAK_TEXT, + // Property Exchange Capabilites. + MAX_SIMUL_EXCHANGE, + EXCHANGE_MAJOR_VER, + EXCHANGE_MINOR_VER, + // Property Exchange messages. + REQUEST_ID, + HEADER_LEN, + HEADER_DATA, + CHUNK_COUNT, + THIS_CHUNK, + PROPERTY_LEN, + PROPERTY_DATA + +} ParseState; + + +const JSON_String supported_resources[] = { + {"ResourceList"}, + {"DeviceInfo"}, + {"ChannelList"}, + {"X-KeyLayout"}, + /* {"X-"} */ +}; + +typedef struct { + uint8_t *stack; + size_t top; + size_t size; + size_t field_position; + ParseState state; +} CIParser; + +extern CIParser ci_memory; + +static inline void next_in_field() { + ++ci_memory.field_position; +} + +static inline void new_field(ParseState to) { + ci_memory.field_position = 0; + ci_memory.state = to; +} + +static inline void push_fn(uint8_t val) { + if (ci_memory.top == ci_memory.size - 1) { + ci_memory.stack = realloc(ci_memory.stack, ci_memory.size + 128); + } + + ci_memory.stack[ci_memory.top + 1] = val; + ++ci_memory.top; +} + +#define push() do { \ + push_fn(byte); \ + } while(0) + + +static inline void reset() { + ci_memory.stack = realloc(ci_memory.stack, 128); + ci_memory.size = 128; + ci_memory.top = 0; + ci_memory.status = WAITING_FOR_STATUS; +} + +static inline uint8_t device_id() { + return ci_memory.stack[0]; +} + +static inline uint8_t command_id() { + return ci_memory.stack[1]; +} + +static inline uint32_t lsb_32bit_from(size_t start_index) { + return (ci_memory.stack[start_index + 3] << 24) | (ci_memory.stack[start_index + 2] << 16) \ + | (ci_memory.stack[start_index + 1] << 8) | (ci_memory.stack[start_index]); +} +static inline uint32_t source_muid() { + return lsb_32bit_from(2); +} + +static inline uint32_t dest_muid() { + return lsb_32bit_from(6); +} + +#define STACK_FIRST_NONCOMMON_IDX 10 + +#define push_until(val, then_block) do { \ + switch(ci_memory.field_position) { \ + case 0 ... (val - 1): \ + push(); \ + next_in_field(); \ + return true; \ + case val: \ + then_block \ + } \ + } while(0) + +#define push_whole_field(val, new) do { \ + switch(ci_memory.field_position) { \ + case 0 ... (val - 1): \ + push(); \ + next_in_field(); \ + return true; \ + case val: \ + push(); \ + new_field(new); \ + return true; \ + } \ + } while(0) + + +typedef struct { + void (*ack_handler)(uint8_t, uint8_t, uint8_t, uint32_t, uint8_t, uint16_t, char*); + void (*nak_handler)(uint8_t, uint8_t, uint8_t, uint32_t, uint8_t, uint16_t, char*); +} CIConsumerBehavior; + +extern CIConsumerBehavior ci_pfns; + +// The user's `universal_system_exclusive_handler` should feed everything into this, device-ID down, +// once it recognizes a MIDI-CI message. +bool midi_ci_parse(uint8_t byte) { + switch (ci_memory.state) { + case DEVICE_ID: + push(); + new_field(SUBID); + return true; + case SUBID: + switch (ci_memory.field_position) { + case 0: +#ifdef DEBUG + if (byte != 0x0d) { + while(true); // Non-MIDI-CI Universal SysEx Sub ID 1. + } +#endif + next_in_field(); + return true; + case 1: + push() + new_field(VERSION); + return true; + } + case VERSION: +#ifdef DEBUGp + if (byte > 0x01) { + while(true); // Unsupported future MIDI-CI Message Version/Format. + } +#endif + new_field(SOURCE); + return true; + case SOURCE: + push_whole_field(4, DESTINATION); + case DESTINATION: + push_until(4, { + push(); + switch (command_id()) { + case DISCOVERY_SUBID: + new_field(MANUFACTURER); + return true; + case REPLY_TO_DISCOVERY_SUBID: + new_field(MANUFACTURER); + return true; + case INVALIDATE_MUID_SUBID: + new_field(TARGET_MUID); + return true; + case ACK_SUBID: + new_field(ACK_ORIG_SUBID); + return true; + case NAK_SUBID: + new_field(NAK_ORIG_SUBID); + return true; + case INQUIRE_EXCHANGE_CAPABILITIES_SUBID: + new_field(MAX_SIMUL_EXCHANGE); + return true; + case REPLY_EXCHANGE_CAPABILITIES_SUBID: + new_field(MAX_SIMUL_EXCHANGE); + return true; + case INQUIRE_GET_SUBID: + case REPLY_GET_SUBID: + case INQUIRE_SET_SUBID: + case REPLY_SET_SUBID: + case SUBSCRIPTION_SUBID: + case REPLY_SUBSCRIPTION_SUBID: + case NOTIFY_SUBID: + new_field(REQUEST_ID); + return true; + default: +#ifdef DEBUG + while(true); // Unsupported MIDI-CI command. +#endif + return false; + }}); + + // Discovery messages. + case MANUFACTURER: + push_whole_field(3, FAMILY); + case FAMILY: + push_whole_field(2, MODEL); + case REVISION: + push_whole_field(4, CI_SUPPORTED); + case CI_SUPPORTED: + push_whole_field(1, MAX_SYSEX); + case MAX_SYSEX: + push_whole_field(4, INITIATOR_OUTPUT_PATH); + case INITIATOR_OUTPUT_PATH: + switch (command_id()) { + case DISCOVERY_SUBID: + if (source_muid() == our_muid) { + invalidate_our_muid(); + our_muid = rand(); // TODO: user function for new MUID? + discovery(); + reset(); + return false; + } + discovery_reply(source_muid(), byte); + reset(); + return false; + case DISCOVERY_REPLY_SUBID: + if (already_seen(source_muid())) { // TODO: already_seen and associated logging. + invalidate_muid(source_muid()); + discovery(); + reset(); + return false; + } + default: +#ifdef DEBUG + while(true); // Unexpected non-Discovery command id. +#endif + reset(); + return false; + } + // Invalidate. + case TARGET_MUID: + push_until(4, { + uint32_t to_invalidate = lsb_32bit_from(STACK_FIRST_NONCOMMON_IDX); + if (to_invalidate == our_muid) { + our_muid = rand(); + discovery(); + } else if (already_seen(to_invalidate)) { + invalidate_stored_data(to_invalidate); // TODO: to_invalidate; see above. + + } + reset(); + return false; + }); + // ACK. + case ACK_ORIG_SUBID: + push_whole_field(1, ACK_CODE); + case ACK_CODE: + push_whole_field(1, ACK_DATA); + case ACK_DATA: + push_whole_field(1, ACK_DETAILS); + case ACK_DETAILS: + push_whole_field(5, ACK_TEXT_LENGTH); + case ACK_TEXT_LENGTH: + push_whole_field(2, ACK_TEXT); + case ACK_TEXT: + push_until((ci_memory.stack[ci_memory.top - 1] << 8) | ci_memory.stack[ci_memory.top], { + // TODO: + }); + reset(); + return false; + // NAK. + case NAK_ORIG_SUBID: + push_whole_field(1, ACK_CODE); + case NAK_CODE: + push_whole_field(1, ACK_DATA); + case NAK_DATA: + push_whole_field(1, ACK_DETAILS); + case NAK_DETAILS: + push_whole_field(5, ACK_TEXT_LENGTH); + case NAK_LENGTH: + push_whole_field(2, ACK_TEXT); + case NAK_TEXT: + push_until((ci_memory.stack[ci_memory.top - 1] << 8) | ci_memory.stack[ci_memory.top], { + // TODO: + }); + reset(); + return false; + + // TODO: Property Exchange Capabilites + // Property Exchange. + case REQUEST_ID: + push_whole_field(1, HEADER_LEN); + case HEADER_LEN: + push_whole_field(2, HEADER_DATA); + case HEADER_DATA: + push_until((ci_memory.stack[ci_memory.top - 1] << 8) | ci_memory.stack[ci_memory.top], { + // TODO: + }); + case CHUNK_COUNT: + push_whole_field(2, THIS_CHUNK); + case THIS_CHUNK: + push_whole_field(2, PROPERTY_LEN); + case PROPERTY_LEN: + push_whole_field(2, PROPERTY_DATA); + case PROPERTY_DATA: + push_until((ci_memory.stack[ci_memory.top - 1] << 8) | ci_memory.stack[ci_memory.top], { + JSON_Value *header = json_parse_string(ci_memory.stack + \ + (ci_memory.stack[ci_memory.top - 1] << 8) | ci_memory.stack[ci_memory.top]); + switch (command_id()) { + case INQUIRE_GET_SUBID: +#ifdef DEBUG + if (header->type != JSONObject) { + while(true); // Non-object JSON Value. + } +#endif + + // TODO: the spec kinda sucks, ngl. I'll get MIDI 1.0, and then return here. + case REPLY_GET_SUBID: + case INQUIRE_SET_SUBID: + case REPLY_SET_SUBID: + case SUBSCRIPTION_SUBID: + case REPLY_SUBSCRIPTION_SUBID: + case NOTIFY_SUBID: + } + }); + } +} +#endif diff --git a/controller/src/main.c b/controller/src/main.c index 1bfe8b0..a5f47c4 100644 --- a/controller/src/main.c +++ b/controller/src/main.c @@ -1,6 +1,108 @@ #include "base_midi.h" -#include "uart.h" + +#include "driverlib/uart.h" +#include "driverlib/sysctl.h" +#include "driverlib/gpio.h" +#include "driverlib/pin_map.h" +#include "inc/hw_uart.h" +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" + + +void UART1IntHandler(void) { + UARTIntClear(UART1_BASE, UART_INT_RX); + while (UARTCharsAvail(UART1_BASE)) { + parse_midi_stream(UARTCharGet(UART1_BASE)); + } +} + +void UARTWrite(uint8_t byte) { + UARTCharPut(UART1_BASE, byte); +} + +static void nothing0(void) {} +static void nothing1(uint8_t) {} +static void nothing2(uint8_t, uint8_t) {} +static void nothing3(uint8_t, uint8_t, uint8_t) {} +static bool quit_parsing(uint8_t) { // TODO: fix library parsers so this works. + return false; +} + + +bool note_on_flag = false; + +void button_handler(void) { + GPIOIntClear(GPIO_PORTF_BASE, GPIO_INT_PIN_4); + if (!note_on_flag) { + note_on(0x02, 0x40, 0x40); + note_on_flag = true; + } else { + note_off(0x02, 0x40); + note_on_flag = false; + } +} + + int main() { + SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC); + + // Initialize UART1. + SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1); + + + while(!SysCtlPeripheralReady(SYSCTL_PERIPH_UART1)); + + // Initialize the GPIO pins for UART1. + GPIOPinConfigure(GPIO_PC4_U1RX); + GPIOPinConfigure(GPIO_PC5_U1TX); + GPIOPinTypeUART(GPIO_PORTC_BASE, GPIO_PIN_4 | GPIO_PIN_5); + + // Configure the UART using the specifications found in the minipix_uart_interface. + // Cource code: word length - 8, 1 stop bit, no parity. + UARTConfigSetExpClk(UART1_BASE, SysCtlClockGet(), 31250, UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE); + UARTEnable(UART1_BASE); + UARTIntEnable(UART1_BASE, UART_INT_RX); + UARTIntRegister(UART1_BASE, UART1IntHandler); + + // Set up MIDI 1.0 library to ignore basically everything. + ConsumerBehavior new_pfns = {.uart_write = UARTWrite, + .note_on_handler = nothing3, + .note_off_handler = nothing3, + .poly_key_handler = nothing3, + .control_change_handler = nothing3, + .program_change_handler = nothing2, + .all_sound_off_handler = nothing1, + .local_control_handler = nothing2, + .all_notes_off_handler = nothing1, + .poly_on_handler = nothing1, + .mtc_quarter_frame_handler = nothing2, + .song_position_pointer_handler = nothing2, + .song_select_handler = nothing1, + .tune_request_handler = nothing0, + .timing_clock_handler = nothing0, + .start_handler = nothing0, + .continue_handler = nothing0, + .stop_handler = nothing0, + .active_sensing_handler = nothing0, + .system_reset_handler = nothing0, // TODO: change. + .sysex_collector = quit_parsing, + .end_of_sysex_handler = nothing0, + /* .bulk_tuning_dump_request_handler = nothing, // TODO: change. */ + /* .bulk_tuning_dump_handler = nothing, */ + /* .single_note_tuning_change_handler = nothing, */ + .unimplemented_universal_sysex_collector = quit_parsing}; + + midi_init(new_pfns); + + // Interrupt whenever GPIO pin PF4 changes, due to SW1 button press. + SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); + GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, GPIO_PIN_4); + GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_STRENGTH_4MA, GPIO_PIN_TYPE_STD_WPU); + GPIOIntTypeSet(GPIO_PORTF_BASE, GPIO_PIN_4, GPIO_RISING_EDGE); + GPIOIntEnable(GPIO_PORTF_BASE, GPIO_INT_PIN_4); + GPIOIntRegister(GPIO_PORTF_BASE, button_handler); + while(true); // Main application loop. + } diff --git a/specs/AVR-Instruction-Set-Manual-DS40002198A.pdf b/specs/AVR-Instruction-Set-Manual-DS40002198A.pdf new file mode 100644 index 0000000..0f2903c Binary files /dev/null and b/specs/AVR-Instruction-Set-Manual-DS40002198A.pdf differ diff --git a/specs/Atmel_2586_AVR_8_bit_Microcontroller_ATtiny25_ATti-1315542.pdf b/specs/Atmel_2586_AVR_8_bit_Microcontroller_ATtiny25_ATti-1315542.pdf new file mode 100644 index 0000000..e211238 Binary files /dev/null and b/specs/Atmel_2586_AVR_8_bit_Microcontroller_ATtiny25_ATti-1315542.pdf differ -- cgit v1.2.3