From a795fc7c4ec8eb03feee7b1965ca224466e36dcd Mon Sep 17 00:00:00 2001 From: Chris Vig Date: Thu, 28 Aug 2025 21:50:29 -0500 Subject: [PATCH] Reorganize input module so it covers output as well. --- .vscode/c_cpp_properties.json | 31 +-- configuration.cmake | 84 ++++--- src/main/CMakeLists.txt | 4 +- src/main/application/config.c | 33 +-- src/main/application/config.h | 13 +- src/main/application/debug_port.c | 105 +++++---- src/main/application/input.c | 181 --------------- src/main/application/input.h | 141 ------------ src/main/application/io.c | 357 ++++++++++++++++++++++++++++++ src/main/application/io.h | 190 ++++++++++++++++ src/main/application/keyer.c | 77 +++---- src/main/application/keyer.h | 13 -- src/main/application/strings.c | 127 +++++++---- src/main/application/strings.h | 55 +++-- src/main/core/main.c | 17 +- src/main/core/sys.h | 2 +- src/main/drivers/gpio.c | 9 + src/main/drivers/gpio.h | 7 + src/main/utility/utility.h | 70 +++--- 19 files changed, 904 insertions(+), 612 deletions(-) delete mode 100644 src/main/application/input.c delete mode 100644 src/main/application/input.h create mode 100644 src/main/application/io.c create mode 100644 src/main/application/io.h diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index a08eecb..d0dca1b 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -18,21 +18,24 @@ "_CONFIG_DFLT_BUZZER_FREQUENCY=700", "_CONFIG_DFLT_LED_STATUS_ENABLED=true", "_CONFIG_DFLT_LED_KEY_ENABLED=true", - "_CONFIG_DFLT_INPUT_TYPE_TRS_0_TIP=INPUT_TYPE_STRAIGHT_KEY", - "_CONFIG_DFLT_INPUT_POLARITY_TRS_0_TIP=INPUT_POLARITY_ACTIVE_LOW", - "_CONFIG_DFLT_INPUT_TYPE_TRS_0_RING=INPUT_TYPE_NONE", - "_CONFIG_DFLT_INPUT_POLARITY_TRS_0_RING=INPUT_POLARITY_ACTIVE_LOW", - "_CONFIG_DFLT_INPUT_TYPE_TRS_1_TIP=INPUT_TYPE_PADDLE_LEFT", - "_CONFIG_DFLT_INPUT_POLARITY_TRS_1_TIP=INPUT_POLARITY_ACTIVE_LOW", - "_CONFIG_DFLT_INPUT_TYPE_TRS_1_RING=INPUT_TYPE_PADDLE_RIGHT", - "_CONFIG_DFLT_INPUT_POLARITY_TRS_1_RING=INPUT_POLARITY_ACTIVE_LOW", - "_CONFIG_DFLT_INPUT_TYPE_TRS_2_TIP=INPUT_TYPE_NONE", - "_CONFIG_DFLT_INPUT_POLARITY_TRS_2_TIP=INPUT_POLARITY_ACTIVE_LOW", - "_CONFIG_DFLT_INPUT_TYPE_TRS_2_RING=INPUT_TYPE_NONE", - "_CONFIG_DFLT_INPUT_POLARITY_TRS_2_RING=INPUT_POLARITY_ACTIVE_LOW", + "_CONFIG_DFLT_IO_TYPE_TRS_0_TIP=IO_TYPE_INPUT_STRAIGHT_KEY", + "_CONFIG_DFLT_IO_POLARITY_TRS_0_TIP=IO_POLARITY_ACTIVE_LOW", + "_CONFIG_DFLT_IO_TYPE_TRS_0_RING=IO_TYPE_NONE", + "_CONFIG_DFLT_IO_POLARITY_TRS_0_RING=IO_POLARITY_ACTIVE_LOW", + "_CONFIG_DFLT_IO_TYPE_TRS_1_TIP=IO_TYPE_INPUT_PADDLE_LEFT", + "_CONFIG_DFLT_IO_POLARITY_TRS_1_TIP=IO_POLARITY_ACTIVE_LOW", + "_CONFIG_DFLT_IO_TYPE_TRS_1_RING=IO_TYPE_INPUT_PADDLE_RIGHT", + "_CONFIG_DFLT_IO_POLARITY_TRS_1_RING=IO_POLARITY_ACTIVE_LOW", + "_CONFIG_DFLT_IO_TYPE_TRS_2_TIP=IO_TYPE_NONE", + "_CONFIG_DFLT_IO_POLARITY_TRS_2_TIP=IO_POLARITY_ACTIVE_LOW", + "_CONFIG_DFLT_IO_TYPE_TRS_2_RING=IO_TYPE_NONE", + "_CONFIG_DFLT_IO_POLARITY_TRS_2_RING=IO_POLARITY_ACTIVE_LOW", + "_CONFIG_DFLT_IO_TYPE_TRS_3_TIP=IO_TYPE_OUTPUT_KEYER", + "_CONFIG_DFLT_IO_POLARITY_TRS_3_TIP=IO_POLARITY_ACTIVE_LOW", + "_CONFIG_DFLT_IO_TYPE_TRS_3_RING=IO_TYPE_NONE", + "_CONFIG_DFLT_IO_POLARITY_TRS_3_RING=IO_POLARITY_ACTIVE_LOW", "_CONFIG_DFLT_KEYER_PADDLE_MODE=KEYER_PADDLE_MODE_IAMBIC", - "_CONFIG_DFLT_KEYER_PADDLE_INVERT=false", - "_CONFIG_DFLT_KEYER_OUTPUT_ACTIVE_LOW=true" + "_CONFIG_DFLT_KEYER_PADDLE_INVERT=false" ], "compilerPath": "/usr/bin/avr-gcc", "compilerArgs": [ diff --git a/configuration.cmake b/configuration.cmake index 0661a51..bc68f58 100644 --- a/configuration.cmake +++ b/configuration.cmake @@ -22,30 +22,38 @@ set(CONFIG_DFLT_LED_STATUS_ENABLED true CACHE STRING "Is the status LED enabled? (true / false)") set(CONFIG_DFLT_LED_KEY_ENABLED true CACHE STRING "Is the key LED enabled? (true / false)") -set(CONFIG_DFLT_INPUT_TYPE_TRS_0_TIP INPUT_TYPE_STRAIGHT_KEY - CACHE STRING "Input type for the tip of TRS 0. (input_type_t)") -set(CONFIG_DFLT_INPUT_POLARITY_TRS_0_TIP INPUT_POLARITY_ACTIVE_LOW - CACHE STRING "Input polarity for the tip of TRS 0. (input_polarity_t)") -set(CONFIG_DFLT_INPUT_TYPE_TRS_0_RING INPUT_TYPE_NONE - CACHE STRING "Input type for the ring of TRS 0. (input_type_t)") -set(CONFIG_DFLT_INPUT_POLARITY_TRS_0_RING INPUT_POLARITY_ACTIVE_LOW - CACHE STRING "Input polarity for the ring of TRS 0. (input_polarity_t)") -set(CONFIG_DFLT_INPUT_TYPE_TRS_1_TIP INPUT_TYPE_PADDLE_LEFT - CACHE STRING "Input type for the tip of TRS 0. (input_type_t)") -set(CONFIG_DFLT_INPUT_POLARITY_TRS_1_TIP INPUT_POLARITY_ACTIVE_LOW - CACHE STRING "Input polarity for the tip of TRS 0. (input_polarity_t)") -set(CONFIG_DFLT_INPUT_TYPE_TRS_1_RING INPUT_TYPE_PADDLE_RIGHT - CACHE STRING "Input type for the ring of TRS 0. (input_type_t)") -set(CONFIG_DFLT_INPUT_POLARITY_TRS_1_RING INPUT_POLARITY_ACTIVE_LOW - CACHE STRING "Input polarity for the ring of TRS 0. (input_polarity_t)") -set(CONFIG_DFLT_INPUT_TYPE_TRS_2_TIP INPUT_TYPE_NONE - CACHE STRING "Input type for the tip of TRS 0. (input_type_t)") -set(CONFIG_DFLT_INPUT_POLARITY_TRS_2_TIP INPUT_POLARITY_ACTIVE_LOW - CACHE STRING "Input polarity for the tip of TRS 0. (input_polarity_t)") -set(CONFIG_DFLT_INPUT_TYPE_TRS_2_RING INPUT_TYPE_NONE - CACHE STRING "Input type for the ring of TRS 0. (input_type_t)") -set(CONFIG_DFLT_INPUT_POLARITY_TRS_2_RING INPUT_POLARITY_ACTIVE_LOW - CACHE STRING "Input polarity for the ring of TRS 0. (input_polarity_t)") +set(CONFIG_DFLT_IO_TYPE_TRS_0_TIP IO_TYPE_INPUT_STRAIGHT_KEY + CACHE STRING "I/O type for the tip of TRS 0. (io_type_t)") +set(CONFIG_DFLT_IO_POLARITY_TRS_0_TIP IO_POLARITY_ACTIVE_LOW + CACHE STRING "I/O polarity for the tip of TRS 0. (io_polarity_t)") +set(CONFIG_DFLT_IO_TYPE_TRS_0_RING IO_TYPE_NONE + CACHE STRING "I/O type for the ring of TRS 0. (io_type_t)") +set(CONFIG_DFLT_IO_POLARITY_TRS_0_RING IO_POLARITY_ACTIVE_LOW + CACHE STRING "I/O polarity for the ring of TRS 0. (io_polarity_t)") +set(CONFIG_DFLT_IO_TYPE_TRS_1_TIP IO_TYPE_INPUT_PADDLE_LEFT + CACHE STRING "I/O type for the tip of TRS 1. (io_type_t)") +set(CONFIG_DFLT_IO_POLARITY_TRS_1_TIP IO_POLARITY_ACTIVE_LOW + CACHE STRING "I/O polarity for the tip of TRS 1. (io_polarity_t)") +set(CONFIG_DFLT_IO_TYPE_TRS_1_RING IO_TYPE_INPUT_PADDLE_RIGHT + CACHE STRING "I/O type for the ring of TRS 1. (io_type_t)") +set(CONFIG_DFLT_IO_POLARITY_TRS_1_RING IO_POLARITY_ACTIVE_LOW + CACHE STRING "I/O polarity for the ring of TRS 1. (io_polarity_t)") +set(CONFIG_DFLT_IO_TYPE_TRS_2_TIP IO_TYPE_NONE + CACHE STRING "I/O type for the tip of TRS 2. (io_type_t)") +set(CONFIG_DFLT_IO_POLARITY_TRS_2_TIP IO_POLARITY_ACTIVE_LOW + CACHE STRING "I/O polarity for the tip of TRS 2. (io_polarity_t)") +set(CONFIG_DFLT_IO_TYPE_TRS_2_RING IO_TYPE_NONE + CACHE STRING "I/O type for the ring of TRS 2. (io_type_t)") +set(CONFIG_DFLT_IO_POLARITY_TRS_2_RING IO_POLARITY_ACTIVE_LOW + CACHE STRING "I/O polarity for the ring of TRS 2. (io_polarity_t)") +set(CONFIG_DFLT_IO_TYPE_TRS_3_TIP IO_TYPE_OUTPUT_KEYER + CACHE STRING "I/O type for the tip of TRS 3. (io_type_t)") +set(CONFIG_DFLT_IO_POLARITY_TRS_3_TIP IO_POLARITY_ACTIVE_LOW + CACHE STRING "I/O polarity for the tip of TRS 3. (io_polarity_t)") +set(CONFIG_DFLT_IO_TYPE_TRS_3_RING IO_TYPE_NONE + CACHE STRING "I/O type for the ring of TRS 3. (io_type_t)") +set(CONFIG_DFLT_IO_POLARITY_TRS_3_RING IO_POLARITY_ACTIVE_LOW + CACHE STRING "I/O polarity for the ring of TRS 3. (io_polarity_t)") set(CONFIG_DFLT_KEYER_PADDLE_MODE KEYER_PADDLE_MODE_IAMBIC CACHE STRING "Keyer paddle mode. (keyer_paddle_mode_t)") set(CONFIG_DFLT_KEYER_PADDLE_INVERT false @@ -63,18 +71,22 @@ add_compile_definitions( _CONFIG_DFLT_LED_STATUS_ACTIVE_LO=${CONFIG_DFLT_LED_STATUS_ACTIVE_LO} _CONFIG_DFLT_LED_KEY_ENABLED=${CONFIG_DFLT_LED_KEY_ENABLED} _CONFIG_DFLT_LED_KEY_ACTIVE_LO=${CONFIG_DFLT_LED_KEY_ACTIVE_LO} - _CONFIG_DFLT_INPUT_TYPE_TRS_0_TIP=${CONFIG_DFLT_INPUT_TYPE_TRS_0_TIP} - _CONFIG_DFLT_INPUT_POLARITY_TRS_0_TIP=${CONFIG_DFLT_INPUT_POLARITY_TRS_0_TIP} - _CONFIG_DFLT_INPUT_TYPE_TRS_0_RING=${CONFIG_DFLT_INPUT_TYPE_TRS_0_RING} - _CONFIG_DFLT_INPUT_POLARITY_TRS_0_RING=${CONFIG_DFLT_INPUT_POLARITY_TRS_0_RING} - _CONFIG_DFLT_INPUT_TYPE_TRS_1_TIP=${CONFIG_DFLT_INPUT_TYPE_TRS_1_TIP} - _CONFIG_DFLT_INPUT_POLARITY_TRS_1_TIP=${CONFIG_DFLT_INPUT_POLARITY_TRS_1_TIP} - _CONFIG_DFLT_INPUT_TYPE_TRS_1_RING=${CONFIG_DFLT_INPUT_TYPE_TRS_1_RING} - _CONFIG_DFLT_INPUT_POLARITY_TRS_1_RING=${CONFIG_DFLT_INPUT_POLARITY_TRS_1_RING} - _CONFIG_DFLT_INPUT_TYPE_TRS_2_TIP=${CONFIG_DFLT_INPUT_TYPE_TRS_2_TIP} - _CONFIG_DFLT_INPUT_POLARITY_TRS_2_TIP=${CONFIG_DFLT_INPUT_POLARITY_TRS_2_TIP} - _CONFIG_DFLT_INPUT_TYPE_TRS_2_RING=${CONFIG_DFLT_INPUT_TYPE_TRS_2_RING} - _CONFIG_DFLT_INPUT_POLARITY_TRS_2_RING=${CONFIG_DFLT_INPUT_POLARITY_TRS_2_RING} + _CONFIG_DFLT_IO_TYPE_TRS_0_TIP=${CONFIG_DFLT_IO_TYPE_TRS_0_TIP} + _CONFIG_DFLT_IO_POLARITY_TRS_0_TIP=${CONFIG_DFLT_IO_POLARITY_TRS_0_TIP} + _CONFIG_DFLT_IO_TYPE_TRS_0_RING=${CONFIG_DFLT_IO_TYPE_TRS_0_RING} + _CONFIG_DFLT_IO_POLARITY_TRS_0_RING=${CONFIG_DFLT_IO_POLARITY_TRS_0_RING} + _CONFIG_DFLT_IO_TYPE_TRS_1_TIP=${CONFIG_DFLT_IO_TYPE_TRS_1_TIP} + _CONFIG_DFLT_IO_POLARITY_TRS_1_TIP=${CONFIG_DFLT_IO_POLARITY_TRS_1_TIP} + _CONFIG_DFLT_IO_TYPE_TRS_1_RING=${CONFIG_DFLT_IO_TYPE_TRS_1_RING} + _CONFIG_DFLT_IO_POLARITY_TRS_1_RING=${CONFIG_DFLT_IO_POLARITY_TRS_1_RING} + _CONFIG_DFLT_IO_TYPE_TRS_2_TIP=${CONFIG_DFLT_IO_TYPE_TRS_2_TIP} + _CONFIG_DFLT_IO_POLARITY_TRS_2_TIP=${CONFIG_DFLT_IO_POLARITY_TRS_2_TIP} + _CONFIG_DFLT_IO_TYPE_TRS_2_RING=${CONFIG_DFLT_IO_TYPE_TRS_2_RING} + _CONFIG_DFLT_IO_POLARITY_TRS_2_RING=${CONFIG_DFLT_IO_POLARITY_TRS_2_RING} + _CONFIG_DFLT_IO_TYPE_TRS_3_TIP=${CONFIG_DFLT_IO_TYPE_TRS_3_TIP} + _CONFIG_DFLT_IO_POLARITY_TRS_3_TIP=${CONFIG_DFLT_IO_POLARITY_TRS_3_TIP} + _CONFIG_DFLT_IO_TYPE_TRS_3_RING=${CONFIG_DFLT_IO_TYPE_TRS_3_RING} + _CONFIG_DFLT_IO_POLARITY_TRS_3_RING=${CONFIG_DFLT_IO_POLARITY_TRS_3_RING} _CONFIG_DFLT_KEYER_PADDLE_MODE=${CONFIG_DFLT_KEYER_PADDLE_MODE} _CONFIG_DFLT_KEYER_PADDLE_INVERT=${CONFIG_DFLT_KEYER_PADDLE_INVERT} _CONFIG_DFLT_KEYER_OUTPUT_ACTIVE_LOW=${CONFIG_DFLT_KEYER_OUTPUT_ACTIVE_LOW} diff --git a/src/main/CMakeLists.txt b/src/main/CMakeLists.txt index a8527f3..c8dea34 100644 --- a/src/main/CMakeLists.txt +++ b/src/main/CMakeLists.txt @@ -17,8 +17,8 @@ set(EXECUTABLE_SOURCE application/buzzer.c application/config.h application/debug_port.c application/debug_port.h - application/input.c - application/input.h + application/io.c + application/io.h application/keyer.c application/keyer.h application/led.c diff --git a/src/main/application/config.c b/src/main/application/config.c index 044503b..15723c4 100644 --- a/src/main/application/config.c +++ b/src/main/application/config.c @@ -14,7 +14,6 @@ #include #include "application/config.h" -#include "application/input.h" #include "application/keyer.h" #include "application/led.h" #include "application/storage.h" @@ -83,24 +82,28 @@ void config_default( config_t * config ) config->led_enabled[ LED_STATUS ] = _CONFIG_DFLT_LED_STATUS_ENABLED; config->led_enabled[ LED_KEY ] = _CONFIG_DFLT_LED_KEY_ENABLED; - // Input configuration - config->input_type[ INPUT_PIN_TRS_0_TIP ] = _CONFIG_DFLT_INPUT_TYPE_TRS_0_TIP; - config->input_polarity[ INPUT_PIN_TRS_0_TIP ] = _CONFIG_DFLT_INPUT_POLARITY_TRS_0_TIP; - config->input_type[ INPUT_PIN_TRS_0_RING ] = _CONFIG_DFLT_INPUT_TYPE_TRS_0_RING; - config->input_polarity[ INPUT_PIN_TRS_0_RING ] = _CONFIG_DFLT_INPUT_POLARITY_TRS_0_RING; - config->input_type[ INPUT_PIN_TRS_1_TIP ] = _CONFIG_DFLT_INPUT_TYPE_TRS_1_TIP; - config->input_polarity[ INPUT_PIN_TRS_1_TIP ] = _CONFIG_DFLT_INPUT_POLARITY_TRS_1_TIP; - config->input_type[ INPUT_PIN_TRS_1_RING ] = _CONFIG_DFLT_INPUT_TYPE_TRS_1_RING; - config->input_polarity[ INPUT_PIN_TRS_1_RING ] = _CONFIG_DFLT_INPUT_POLARITY_TRS_1_RING; - config->input_type[ INPUT_PIN_TRS_2_TIP ] = _CONFIG_DFLT_INPUT_TYPE_TRS_2_TIP; - config->input_polarity[ INPUT_PIN_TRS_2_TIP ] = _CONFIG_DFLT_INPUT_POLARITY_TRS_2_TIP; - config->input_type[ INPUT_PIN_TRS_2_RING ] = _CONFIG_DFLT_INPUT_TYPE_TRS_2_RING; - config->input_polarity[ INPUT_PIN_TRS_2_RING ] = _CONFIG_DFLT_INPUT_POLARITY_TRS_2_RING; + // I/O configuration + // TODO: Compile-time constants + config->io_type[ IO_PIN_TRS_0_TIP ] = _CONFIG_DFLT_IO_TYPE_TRS_0_TIP; + config->io_polarity[ IO_PIN_TRS_0_TIP ] = _CONFIG_DFLT_IO_POLARITY_TRS_0_TIP; + config->io_type[ IO_PIN_TRS_0_RING ] = _CONFIG_DFLT_IO_TYPE_TRS_0_RING; + config->io_polarity[ IO_PIN_TRS_0_RING ] = _CONFIG_DFLT_IO_POLARITY_TRS_0_RING; + config->io_type[ IO_PIN_TRS_1_TIP ] = _CONFIG_DFLT_IO_TYPE_TRS_1_TIP; + config->io_polarity[ IO_PIN_TRS_1_TIP ] = _CONFIG_DFLT_IO_POLARITY_TRS_1_TIP; + config->io_type[ IO_PIN_TRS_1_RING ] = _CONFIG_DFLT_IO_TYPE_TRS_1_RING; + config->io_polarity[ IO_PIN_TRS_1_RING ] = _CONFIG_DFLT_IO_POLARITY_TRS_1_RING; + config->io_type[ IO_PIN_TRS_2_TIP ] = _CONFIG_DFLT_IO_TYPE_TRS_2_TIP; + config->io_polarity[ IO_PIN_TRS_2_TIP ] = _CONFIG_DFLT_IO_POLARITY_TRS_2_TIP; + config->io_type[ IO_PIN_TRS_2_RING ] = _CONFIG_DFLT_IO_TYPE_TRS_2_RING; + config->io_polarity[ IO_PIN_TRS_2_RING ] = _CONFIG_DFLT_IO_POLARITY_TRS_2_RING; + config->io_type[ IO_PIN_TRS_3_TIP ] = _CONFIG_DFLT_IO_TYPE_TRS_3_TIP; + config->io_polarity[ IO_PIN_TRS_3_TIP ] = _CONFIG_DFLT_IO_POLARITY_TRS_3_TIP; + config->io_type[ IO_PIN_TRS_3_RING ] = _CONFIG_DFLT_IO_TYPE_TRS_3_RING; + config->io_polarity[ IO_PIN_TRS_3_RING ] = _CONFIG_DFLT_IO_POLARITY_TRS_3_RING; // Keyer configuration config->keyer_paddle_mode = _CONFIG_DFLT_KEYER_PADDLE_MODE; config->keyer_paddle_invert = _CONFIG_DFLT_KEYER_PADDLE_INVERT; - config->keyer_output_active_low = _CONFIG_DFLT_KEYER_OUTPUT_ACTIVE_LOW; // Ensure we generated a valid configuration assert_always( validate_config( config ) ); diff --git a/src/main/application/config.h b/src/main/application/config.h index b632c87..26c8ca0 100644 --- a/src/main/application/config.h +++ b/src/main/application/config.h @@ -16,7 +16,7 @@ #include #include "application/buzzer.h" -#include "application/input.h" +#include "application/io.h" #include "application/keyer.h" #include "application/led.h" #include "application/wpm.h" @@ -44,11 +44,11 @@ typedef struct /** If set to `false`, the LED will be disabled and will not illuminate. */ bool led_enabled[ LED_COUNT ]; - /** The configured input type for each input pin. */ - input_type_t input_type[ INPUT_PIN_COUNT ]; + /** The configured I/O type for each I/O pin. */ + io_type_t io_type[ IO_PIN_COUNT ]; - /** The configured input polarity for each input pin. */ - input_polarity_t input_polarity[ INPUT_PIN_COUNT ]; + /** The configured I/O polarity for each I/O pin. */ + io_polarity_t io_polarity[ IO_PIN_COUNT ]; /** The keyer's paddle mode. */ keyer_paddle_mode_t keyer_paddle_mode; @@ -56,9 +56,6 @@ typedef struct /** If set to `true`, the keyer will emit dashes from the left paddle and dots from the right paddle. */ bool keyer_paddle_invert; - /** If set to `true`, the keyer's output is active low. */ - bool keyer_output_active_low; - } config_t; /** diff --git a/src/main/application/debug_port.c b/src/main/application/debug_port.c index 4ed41ff..acee42a 100644 --- a/src/main/application/debug_port.c +++ b/src/main/application/debug_port.c @@ -79,10 +79,10 @@ #define CMD_STR_HELP "Help" /** - * @def CMD_STR_INPUT - * @brief The string for the `input` command. + * @def CMD_STR_IO + * @brief The string for the `io` command. */ -#define CMD_STR_INPUT "Input" +#define CMD_STR_IO "IO" /** * @def CMD_STR_KEYER @@ -199,10 +199,10 @@ static void exec_command_eeprom( char const * const command ); static void exec_command_help( char const * const command ); /** - * @fn exec_command_input( char const * const command ) - * @brief Executes the `input` command. + * @fn exec_command_io( char const * const command ) + * @brief Executes the `io` command. */ -static void exec_command_input( char const * const command ); +static void exec_command_io( char const * const command ); /** * @fn exec_command_keyer( char const * const command ) @@ -371,8 +371,8 @@ static void exec_command( char const * const command ) exec_command_eeprom( command ); else if( string_begins_with( command, CMD_STR_HELP ) ) exec_command_help( command ); - else if( string_begins_with( command, CMD_STR_INPUT ) ) - exec_command_input( command ); + else if( string_begins_with( command, CMD_STR_IO ) ) + exec_command_io( command ); else if( string_begins_with( command, CMD_STR_KEYER ) ) exec_command_keyer( command ); else if( string_begins_with( command, CMD_STR_LED ) ) @@ -407,7 +407,7 @@ static void exec_command_buzzer( char const * const command ) // Turn buzzer on buzzer_set_enabled( true ); } - else if( string_equals( command, CMD_STR_BUZZER " " DISABLED_STR " " stringize( false ) ) ) + else if( string_equals( command, CMD_STR_BUZZER " " ENABLED_STR " " stringize( false ) ) ) { // Turn buzzer oiff buzzer_set_enabled( false ); @@ -566,14 +566,19 @@ static void exec_command_help( char const * const command ) } /* exec_command_help() */ -static void exec_command_input( char const * const command ) +static void exec_command_io( char const * const command ) { char pin_str[ TOKEN_MAX_LEN ]; char subcommand_str[ TOKEN_MAX_LEN ]; int sscanf_count; - // Drop `input` prefix - char const * c = command + 6; + // Drop `io` prefix + if( ! string_begins_with( command, CMD_STR_IO " " ) ) + { + print_invalid_command( command ); + return; + } + char const * c = command + 3; // Get tokens int token_count = sscanf( c, TOKEN_FMT_STR " " TOKEN_FMT_STR " %n", pin_str, subcommand_str, & sscanf_count ); @@ -583,9 +588,9 @@ static void exec_command_input( char const * const command ) return; } - // Parse input pin - input_pin_t pin; - if( ! string_to_input_pin( pin_str, & pin ) ) + // Parse pin + io_pin_t pin; + if( ! string_to_io_pin( pin_str, & pin ) ) { print_invalid_command( command ); return; @@ -595,33 +600,40 @@ static void exec_command_input( char const * const command ) if( token_count == 2 ) { // Check subcommand - input_polarity_t polarity; - input_type_t type; - if( string_to_input_polarity( subcommand_str, & polarity ) ) + io_polarity_t polarity; + io_type_t type; + if( string_to_io_polarity( subcommand_str, & polarity ) ) { // Set polarity - input_set_polarity( pin, polarity ); + io_set_polarity( pin, polarity ); } - else if( string_to_input_type( subcommand_str, & type ) ) + else if( string_to_io_type( subcommand_str, & type ) ) { // Set type - input_set_type( pin, type ); + io_set_type( pin, type ); + } + else if( string_equals( subcommand_str, "disable" ) ) + { + // Set type to NONE + io_set_type( pin, IO_TYPE_NONE ); } else { + debug_port_printf( "%s\r\n", subcommand_str ); + debug_port_printf( "%s\r\n", string_from_io_type( IO_TYPE_NONE ) ); print_invalid_command( command ); return; } } // Print status info - debug_port_printf( CMD_STR_INPUT " %s: %s (%s - %s)" NEWLINE_STR, - string_from_input_pin( pin ), - input_get_on( pin ) ? ON_STR : OFF_STR, - string_from_input_type( input_get_type( pin ) ), - string_from_input_polarity( input_get_polarity( pin ) ) ); + debug_port_printf( CMD_STR_IO " %s: %s (%s - %s)" NEWLINE_STR, + string_from_io_pin( pin ), + string_from_io_state( io_get_state( pin ) ), + string_from_io_type( io_get_type( pin ) ), + string_from_io_polarity( io_get_polarity( pin ) ) ); -} /* exec_command_input() */ +} /* exec_command_io() */ static void exec_command_keyer( char const * const command ) @@ -654,16 +666,6 @@ static void exec_command_keyer( char const * const command ) debug_port_printf( CMD_STR_KEYER ": \"%s\" (%u chars queued)" NEWLINE_STR, command + 10, count ); return; } - else if( string_equals( command, CMD_STR_KEYER " output_active_low " ENABLE_STR ) ) - { - // Set output to active low - keyer_set_output_active_low( true ); - } - else if( string_equals( command, CMD_STR_KEYER " output_active_low " DISABLE_STR ) ) - { - // Set output to active high - keyer_set_output_active_low( false ); - } else if( string_equals( command, CMD_STR_KEYER " " stringize( KEYER_PADDLE_MODE_IAMBIC ) ) ) { // Set to iambic mode @@ -713,6 +715,11 @@ static void exec_command_led( char const * const command ) int sscanf_count; // Drop `led` prefix + if( ! string_begins_with( command, CMD_STR_LED " " ) ) + { + print_invalid_command( command ); + return; + } char const * c = command + 4; // Get tokens @@ -760,7 +767,12 @@ static void exec_command_led( char const * const command ) static void exec_command_panic( char const * const command ) { - ( void )command; + // No arguments are supported + if( ! string_equals( command, CMD_STR_PANIC ) ) + { + print_invalid_command( command ); + return; + } keyer_panic(); debug_port_print( "Stopped keyer." NEWLINE_STR ); @@ -770,7 +782,12 @@ static void exec_command_panic( char const * const command ) static void exec_command_tick( char const * const command ) { - ( void )command; + // No arguments are supported + if( ! string_equals( command, CMD_STR_TICK ) ) + { + print_invalid_command( command ); + return; + } debug_port_printf( CMD_STR_TICK ": %lu" NEWLINE_STR, sys_get_tick() ); @@ -779,7 +796,12 @@ static void exec_command_tick( char const * const command ) static void exec_command_version( char const * const command ) { - ( void )command; + // No arguments are supported + if( ! string_equals( command, CMD_STR_VERSION ) ) + { + print_invalid_command( command ); + return; + } version_t version; version_get( & version ); @@ -810,7 +832,8 @@ static void exec_command_wpm( char const * const command ) { // No subcommand - interpret as a status request. No action required. } - else if( sscanf( command + 4, "%f %n", & wpm, & sscanf_count ) == 1 && + else if( string_begins_with( command, CMD_STR_WPM " " ) && + sscanf( command + 4, "%f %n", & wpm, & sscanf_count ) == 1 && ( command + 4 )[ sscanf_count ] == NULL_CHAR ) { // Set WPM diff --git a/src/main/application/input.c b/src/main/application/input.c deleted file mode 100644 index 26621bc..0000000 --- a/src/main/application/input.c +++ /dev/null @@ -1,181 +0,0 @@ -/** - * @file src/main/application/input.c - * @brief Implementation for the key input module. - * - * @author Chris Vig (chris@invictus.so) - * @date 2025-08-17 - * @cpyrt © 2025 by Chris Vig. Licensed under the GNU General Public License v3 (GPLv3). - */ - -/* ---------------------------------------------------- INCLUDES ---------------------------------------------------- */ - -#include -#include - -#include "application/config.h" -#include "application/input.h" -#include "core/sys.h" -#include "drivers/gpio.h" -#include "utility/debug.h" -#include "utility/types.h" -#include "utility/utility.h" - -/* --------------------------------------------------- CONSTANTS ---------------------------------------------------- */ - -// Lookup table for GPIO pins -static gpio_pin_t const s_pin_tbl[] = -{ - GPIO_PIN_A0, /* INPUT_PIN_0_TIP */ - GPIO_PIN_A1, /* INPUT_PIN_0_RING */ - GPIO_PIN_A2, /* INPUT_PIN_1_TIP */ - GPIO_PIN_A3, /* INPUT_PIN_1_RING */ - GPIO_PIN_A4, /* INPUT_PIN_2_TIP */ - GPIO_PIN_A5, /* INPUT_PIN_2_RING */ -}; -_Static_assert( array_count( s_pin_tbl ) == INPUT_PIN_COUNT, "Invalid pin table!" ); - -/* ----------------------------------------------------- MACROS ----------------------------------------------------- */ - -/** - * @def validate_pin( _pin ) - * @brief Validates that the specified `input_pin_t` is valid. - */ -#define validate_pin( _pin ) \ - assert_always( ( _pin ) < INPUT_PIN_COUNT ) - -/** - * @def validate_polarity( _polarity ) - * @brief Validates that the specified `input_polarity_t` is valid. - */ -#define validate_polarity( _polarity ) \ - assert_always( ( _polarity ) < INPUT_POLARITY_COUNT ) - -/** - * @def validate_type( _type ) - * @brief Validates that the specified `input_type_t` is valid. - */ -#define validate_type( _input ) \ - assert_always( ( _input ) < INPUT_TYPE_COUNT ) - -/* --------------------------------------------------- PROCEDURES --------------------------------------------------- */ - -bool input_get_on( input_pin_t pin ) -{ - validate_pin( pin ); - - gpio_state_t state = gpio_get_state( s_pin_tbl[ pin ] ); - if( input_get_polarity( pin ) == INPUT_POLARITY_ACTIVE_LOW ) - return( state == GPIO_STATE_LOW ); - else - return( state == GPIO_STATE_HIGH ); - -} /* input_get() */ - - -input_polarity_t input_get_polarity( input_pin_t pin ) -{ - validate_pin( pin ); - - return( config()->input_polarity[ pin ] ); - -} /* input_get_polarity() */ - - -input_type_t input_get_type( input_pin_t pin ) -{ - validate_pin( pin ); - - return( config()->input_type[ pin ] ); - -} /* input_get_type() */ - - -void input_init( void ) -{ - // Configure GPIO, enabling interrupts - for( input_pin_t pin = 0; pin < INPUT_PIN_COUNT; pin++ ) - { - gpio_pin_t gpio_pin = s_pin_tbl[ pin ]; - gpio_set_dir( gpio_pin, GPIO_DIR_IN ); - gpio_set_pullup( gpio_pin, true ); - gpio_set_pcint_enabled_port( gpio_get_pin_port( gpio_pin ), true ); - gpio_set_pcint_enabled_pin( gpio_pin, true ); - } - -} /* input_init() */ - - -void input_set_polarity( input_pin_t pin, input_polarity_t polarity ) -{ - validate_pin( pin ); - validate_polarity( polarity ); - - config_t config; - config_get( & config ); - config.input_polarity[ pin ] = polarity; - config_set( & config ); - -} /* input_set_polarity() */ - - -void input_set_type( input_pin_t pin, input_type_t type ) -{ - validate_pin( pin ); - if( type != INPUT_TYPE_NONE ) - validate_type( type ); - - config_t config; - config_get( & config ); - config.input_type[ pin ] = type; - config_set( & config ); - -} /* input_set_type() */ - - -void input_tick( tick_t tick ) -{ - ( void )tick; - -} /* input_tick() */ - - -bool input_type_get_on( input_type_t type ) -{ - validate_type( type ); - - bool result = false; - for( input_pin_t pin = 0; pin < INPUT_PIN_COUNT; pin++ ) - { - if( input_get_type( pin ) == type && input_get_on( pin ) ) - { - result = true; - break; - } - } - - return( result ); - -} /* input_type_get_on() */ - - -input_type_field_t input_types_get_on( void ) -{ - input_type_field_t ret = 0; - for( input_pin_t pin = 0; pin < INPUT_PIN_COUNT; pin++ ) - { - input_type_t type = input_get_type( pin ); - bool on = input_get_on( pin ); - if( type != INPUT_TYPE_NONE && on ) - set_bit( ret, type ); - } - - return( ret ); - -} /* input_types_get_on() */ - - -ISR( PCINT0_vect ) -{ - sys_enqueue_event( EVENT_INPUT_STATE ); - -} /* ISR( PCINT0_vect ) */ diff --git a/src/main/application/input.h b/src/main/application/input.h deleted file mode 100644 index aa4cf7c..0000000 --- a/src/main/application/input.h +++ /dev/null @@ -1,141 +0,0 @@ -/** - * @file src/main/application/input.h - * @brief Header for the key input module. - * - * @author Chris Vig (chris@invictus.so) - * @date 2025-08-17 - * @cpyrt © 2025 by Chris Vig. Licensed under the GNU General Public License v3 (GPLv3). - */ - -#if !defined( APPLICATION_INPUT_H ) -#define APPLICATION_INPUT_H - -/* ---------------------------------------------------- INCLUDES ---------------------------------------------------- */ - -#include -#include - -#include "utility/types.h" -#include "utility/utility.h" - -/* ----------------------------------------------------- TYPES ------------------------------------------------------ */ - -/** - * @typedef input_pin_t - * @brief Enumeration of the supported input pins. - * @note The device has three TRS connectors for inputs. The sleeve of each connector is connected to ground, which - * means that we have two inputs for each connector, for a total of 6 pins. Hopefully this will be enough for - * most users. - */ -typedef uint8_t input_pin_t; -enum -{ - INPUT_PIN_TRS_0_TIP, /**< The tip on TRS connector 0. */ - INPUT_PIN_TRS_0_RING, /**< The ring on TRS connector 0. */ - INPUT_PIN_TRS_1_TIP, /**< The tip on TRS connector 1. */ - INPUT_PIN_TRS_1_RING, /**< The ring on TRS connector 1. */ - INPUT_PIN_TRS_2_TIP, /**< The tip on TRS connector 2. */ - INPUT_PIN_TRS_2_RING, /**< The ring on TRS connector 2. */ - - INPUT_PIN_COUNT, /**< Number of valid input pins. */ -}; - -/** - * @typedef input_polarity_t - * @brief Enumeration of the supported input polarities. - * @note This represents whether the input is considered "active" when it is grounded or when it is high. - */ -typedef uint8_t input_polarity_t; -enum -{ - INPUT_POLARITY_ACTIVE_LOW, /**< The input is active when grounded. */ - INPUT_POLARITY_ACTIVE_HIGH, /**< The input is active when at Vcc. */ - - INPUT_POLARITY_COUNT, /**< Number of valid input polarities. */ -}; - -/** - * @typedef input_type_t - * @brief Enumeration of the supported input types. - */ -typedef uint8_t input_type_t; -enum -{ - INPUT_TYPE_STRAIGHT_KEY, /**< Input is for a straight key. */ - INPUT_TYPE_PADDLE_LEFT, /**< Input is for the left key on paddles. */ - INPUT_TYPE_PADDLE_RIGHT, /**< Input is for the right key on paddles. */ - - INPUT_TYPE_COUNT, /**< Number of valid input types. */ - - INPUT_TYPE_NONE /**< Unused input type. */ - = INPUT_TYPE_COUNT -}; - -/** - * @typedef input_type_field_t - * @brief Bitfield of input types. - * @note Bit indices are specified by the `input_type_t` enumeration. - */ -typedef uint8_t input_type_field_t; - -_Static_assert( INPUT_TYPE_COUNT < sizeof_bits( input_type_field_t ), "Input type field is too small!" ); - -/* ---------------------------------------------- PROCEDURE PROTOTYPES ---------------------------------------------- */ - -/** - * @fn input_get_on( input_type_t ) - * @brief Returns the state of the specified input pin. - */ -bool input_get_on( input_pin_t pin ); - -/** - * @fn input_get_polarity( input_pin_t ) - * @brief Gets the input polarity for the specified input pin. - */ -input_polarity_t input_get_polarity( input_pin_t pin ); - -/** - * @fn input_get_type( input_pin_t ) - * @brief Gets the input type for the specified input pin. - */ -input_type_t input_get_type( input_pin_t pin ); - -/** - * @fn input_init( void ) - * @brief Initializes the key input module. - */ -void input_init( void ); - -/** - * @fn input_set_polarity( input_pin_t, input_polarity_t ) - * @brief Sets the input polarity for the specified input pin. - * @note This modifies the application configuration. - */ -void input_set_polarity( input_pin_t pin, input_polarity_t polarity ); - -/** - * @fn input_set_type( input_pin_t, input_type_t ) - * @brief Sets the input type for the specified input pin. - * @note This modifies the application configuration. - */ -void input_set_type( input_pin_t pin, input_type_t type ); - -/** - * @fn input_tick( tick_t ) - * @brief Performs periodic processing at the specified tick count. - */ -void input_tick( tick_t tick ); - -/** - * @fn input_type_get_on( input_type_t ) - * @brief Returns `true` if any input with the specified type is on. - */ -bool input_type_get_on( input_type_t type ); - -/** - * @fn input_types_get_on( void ) - * @brief Returns a bitfield of all input types which are currently on. - */ -input_type_field_t input_types_get_on( void ); - -#endif /* !defined( APPLICATION_INPUT_H ) */ diff --git a/src/main/application/io.c b/src/main/application/io.c new file mode 100644 index 0000000..240c466 --- /dev/null +++ b/src/main/application/io.c @@ -0,0 +1,357 @@ +/** + * @brief src/main/application/io.c + * @brief Implementation for the keyer input / output module. + * + * @author Chris Vig (chris@invictus.so) + * @date 2025-08-29 + * @cpyrt © 2025 by Chris Vig. Licensed under the GNU General Public License v3 (GPLv3). + */ + +/* ---------------------------------------------------- INCLUDES ---------------------------------------------------- */ + +#include +#include + +#include +#include + +#include "application/config.h" +#include "application/io.h" +#include "core/sys.h" +#include "drivers/gpio.h" +#include "utility/debug.h" +#include "utility/utility.h" + +/* --------------------------------------------------- CONSTANTS ---------------------------------------------------- */ + +static gpio_pin_t const s_pin_tbl[] = +{ + GPIO_PIN_A0, /* IO_PIN_TRS_0_TIP */ + GPIO_PIN_A1, /* IO_PIN_TRS_0_RING */ + GPIO_PIN_A2, /* IO_PIN_TRS_1_TIP */ + GPIO_PIN_A3, /* IO_PIN_TRS_1_RING */ + GPIO_PIN_A4, /* IO_PIN_TRS_2_TIP */ + GPIO_PIN_A5, /* IO_PIN_TRS_2_RING */ + GPIO_PIN_A6, /* IO_PIN_TRS_3_TIP */ + GPIO_PIN_A7, /* IO_PIN_TRS_3_RING */ +}; +_Static_assert( array_count( s_pin_tbl ) == IO_PIN_COUNT, "Invalid pin table!" ); + +/* --------------------------------------------------- VARIABLES ---------------------------------------------------- */ + +static io_state_t s_output_state[ IO_PIN_COUNT ]; + +/* ----------------------------------------------------- MACROS ----------------------------------------------------- */ + +// Validation macros +#define validate_pin( _pin ) \ + assert_always( ( _pin ) < IO_PIN_COUNT ) +#define validate_polarity( _polarity ) \ + assert_always( ( _polarity ) < IO_POLARITY_COUNT ) +#define validate_state( _state ) \ + assert_always( ( _state ) < IO_STATE_COUNT ) +#define validate_type( _type ) \ + assert_always( ( _type ) < IO_TYPE_COUNT ) +#define validate_type_or_none( _type ) \ + assert_always( ( _type ) < IO_TYPE_COUNT || ( _type ) == IO_TYPE_NONE ) + +// Get GPIO pin +#define gpio_pin( _pin ) \ + ( s_pin_tbl[ ( _pin ) ] ) + +/* ---------------------------------------------- PROCEDURE PROTOTYPES ---------------------------------------------- */ + +/** + * @fn update_config( void ) + * @brief Updates the configuration of all GPIO pins. + * @note If `force` is set to `true`, the configuration of all I/O pins will be forcibly updated. Otherwise, only the + * pins which appear to be in the wrong state will be updated. + */ +static void update_config( bool force ); + +/** + * @fn update_state( void ) + * @brief Updates the state of all GPIO pins. + */ +static void update_state( void ); + +/* --------------------------------------------------- PROCEDURES --------------------------------------------------- */ + +io_polarity_t io_get_polarity( io_pin_t pin ) +{ + validate_pin( pin ); + + return( config()->io_polarity[ pin ] ); + +} /* io_get_polarity() */ + + +io_state_t io_get_state( io_pin_t pin ) +{ + validate_pin( pin ); + + // Action depends on input type + if( io_pin_is_input( pin ) ) + { + // Evaluate based on GPIO state, ignore cache + gpio_state_t gpio_state = gpio_get_state( gpio_pin( pin ) ); + switch( io_get_polarity( pin ) ) + { + case IO_POLARITY_ACTIVE_LOW: + return( gpio_state == GPIO_STATE_LOW ); + case IO_POLARITY_ACTIVE_HIGH: + return( gpio_state == GPIO_STATE_HIGH ); + default: + return( IO_STATE_NONE ); + } + } + else if( io_pin_is_output( pin ) ) + // Return cached state + return( s_output_state[ pin ] ); + else + // Not configured + return( IO_STATE_NONE ); + +} /* io_get_state() */ + + +io_state_t io_get_state_type( io_type_t type ) +{ + validate_type( type ); + + // Search for any pin which is ON + io_state_t ret = IO_STATE_NONE; + for( io_pin_t pin = 0; pin < IO_PIN_COUNT; pin++ ) + { + if( io_get_type( pin ) != type ) + continue; + if( ret == IO_STATE_NONE ) + ret = IO_STATE_OFF; + if( io_get_state( pin ) == IO_STATE_ON ) + { + ret = IO_STATE_ON; + break; + } + } + + return( ret ); + +} /* io_get_state_type() */ + + +io_type_t io_get_type( io_pin_t pin ) +{ + validate_pin( pin ); + + return( config()->io_type[ pin ] ); + +} /* io_get_type() */ + + +void io_init( void ) +{ + // Initialize output state to all off + for( io_pin_t pin = 0; pin < IO_PIN_COUNT; pin++ ) + s_output_state[ pin ] = IO_STATE_OFF; + + // Initialize + update_config( true ); + update_state(); + +} /* io_init() */ + + +bool io_pin_is_input( io_pin_t pin ) +{ + return( io_type_is_input( io_get_type( pin ) ) ); + +} /* io_pin_is_input() */ + + +bool io_pin_is_output( io_pin_t pin ) +{ + return( io_type_is_output( io_get_type( pin ) ) ); + +} /* io_pin_is_output() */ + + +void io_set_output_state( io_pin_t pin, io_state_t state ) +{ + validate_pin( pin ); + validate_state( state ); + + // We can only set the state for outputs + if( ! io_pin_is_output( pin ) ) + return; + + s_output_state[ pin ] = state; + + update_state(); + +} /* io_set_output_state() */ + + +void io_set_output_state_type( io_type_t type, io_state_t state ) +{ + validate_type( type ); + validate_state( state ); + + // We can only set the state for outputs + if( ! io_type_is_output( type ) ) + return; + + for( io_pin_t pin = 0; pin < IO_PIN_COUNT; pin++ ) + if( io_get_type( pin ) == type ) + s_output_state[ pin ] = state; + + update_state(); + +} /* io_set_output_state_type() */ + + +void io_set_polarity( io_pin_t pin, io_polarity_t polarity ) +{ + validate_pin( pin ); + validate_polarity( polarity ); + + config_t config; + config_get( & config ); + config.io_polarity[ pin ] = polarity; + config_set( & config ); + + update_config( false ); + update_state(); + +} /* io_set_polarity() */ + + +void io_set_type( io_pin_t pin, io_type_t type ) +{ + validate_pin( pin ); + validate_type_or_none( type ); + + config_t config; + config_get( & config ); + config.io_type[ pin ] = type; + config_set( & config ); + + update_config( false ); + update_state(); + +} /* io_set_type() */ + + +void io_tick( tick_t tick ) +{ + ( void )tick; + + update_config( false ); + update_state(); + +} /* io_tick() */ + + +bool io_type_is_input( io_type_t type ) +{ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wtype-limits" + return( type >= IO_TYPE_INPUT_FIRST && type <= IO_TYPE_INPUT_LAST ); + #pragma GCC diagnostic pop + +} /* io_type_is_input() */ + + +bool io_type_is_output( io_type_t type ) +{ + return( type >= IO_TYPE_OUTPUT_FIRST && type <= IO_TYPE_OUTPUT_LAST ); + +} /* io_type_is_output() */ + + +static void update_config( bool force ) +{ + // Enable interrupt for entire port + gpio_set_pcint_enabled_port( GPIO_PORT_A, true ); + + // Configure each individual pin + for( io_pin_t pin = 0; pin < IO_PIN_COUNT; pin++ ) + { + // Get I/O type for this pin + io_type_t type = io_get_type( pin ); + bool type_is_output = io_type_is_output( type ); + bool type_is_input = io_type_is_input( type ); + + // Configure the pin if forced, or if the I/O direction is wrong + if( type_is_output && + ( force || gpio_get_dir( gpio_pin( pin ) ) != GPIO_DIR_OUT ) ) + { + // Pin is an output + gpio_set_dir( gpio_pin( pin ), GPIO_DIR_OUT ); + gpio_set_pullup( gpio_pin( pin ), false ); + gpio_set_pcint_enabled_pin( gpio_pin( pin ), false ); + } + else if( type_is_input && + ( force || + gpio_get_dir( gpio_pin( pin ) ) != GPIO_DIR_IN || + ( gpio_get_dir( gpio_pin( pin ) ) == GPIO_DIR_IN && + gpio_get_pullup( gpio_pin( pin ) ) == false ) ) ) + { + // Pin is an input + gpio_set_dir( gpio_pin( pin ), GPIO_DIR_IN ); + gpio_set_pullup( gpio_pin( pin ), true ); + gpio_set_pcint_enabled_pin( gpio_pin( pin ), true ); + } + else if( ! type_is_input && + ! type_is_output && + ( force || + gpio_get_dir( gpio_pin( pin ) ) != GPIO_DIR_IN || + ( gpio_get_dir( gpio_pin( pin ) ) == GPIO_DIR_IN && + gpio_get_pullup( gpio_pin( pin ) ) == true ) ) ) + { + // Pin is unused + gpio_set_dir( gpio_pin( pin ), GPIO_DIR_IN ); + gpio_set_pullup( gpio_pin( pin ), false ); + gpio_set_pcint_enabled_pin( gpio_pin( pin ), false ); + } + } + +} /* update_config() */ + + +static void update_state( void ) +{ + for( io_pin_t pin = 0; pin < IO_PIN_COUNT; pin++ ) + { + // Only touch pins which are configured as outputs + if( ! io_pin_is_output( pin ) ) + continue; + + // Get polarity and state + io_polarity_t polarity = io_get_polarity( pin ); + io_state_t state = s_output_state[ pin ]; + + // Take action only in definitive states + if( polarity == IO_POLARITY_ACTIVE_LOW ) + { + if( state == IO_STATE_OFF ) + gpio_set_state( gpio_pin( pin ), GPIO_STATE_HIGH ); + else if( state == IO_STATE_ON ) + gpio_set_state( gpio_pin( pin ), GPIO_STATE_LOW ); + } + else if( polarity == IO_POLARITY_ACTIVE_HIGH ) + { + if( state == IO_STATE_OFF ) + gpio_set_state( gpio_pin( pin ), GPIO_STATE_LOW ); + else if( state == IO_STATE_ON ) + gpio_set_state( gpio_pin( pin ), GPIO_STATE_HIGH ); + } + } + +} /* update_state() */ + + +ISR( PCINT0_vect ) +{ + sys_enqueue_event( EVENT_IO_STATE ); + +} /* ISR( PCINT0_vect ) */ diff --git a/src/main/application/io.h b/src/main/application/io.h new file mode 100644 index 0000000..de8a69e --- /dev/null +++ b/src/main/application/io.h @@ -0,0 +1,190 @@ +/** + * @brief src/main/application/io.h + * @brief Header for the keyer input / output module. + * + * @author Chris Vig (chris@invictus.so) + * @date 2025-08-29 + * @cpyrt © 2025 by Chris Vig. Licensed under the GNU General Public License v3 (GPLv3). + */ + +#if !defined( APPLICATION_IO_H ) +#define APPLICATION_IO_H + +/* ---------------------------------------------------- INCLUDES ---------------------------------------------------- */ + +#include +#include + +#include "utility/types.h" + +/* ----------------------------------------------------- TYPES ------------------------------------------------------ */ + +/** + * @typedef io_pin_t + * @brief Enumeration of the supported I/O pins. + */ +typedef uint8_t io_pin_t; +enum +{ + IO_PIN_TRS_0_TIP, /**< The tip pin for TRS connector 0. */ + IO_PIN_TRS_0_RING, /**< The ring pin for TRS connector 0. */ + IO_PIN_TRS_1_TIP, /**< The tip pin for TRS connector 1. */ + IO_PIN_TRS_1_RING, /**< The ring pin for TRS connector 1. */ + IO_PIN_TRS_2_TIP, /**< The tip pin for TRS connector 2. */ + IO_PIN_TRS_2_RING, /**< The ring pin for TRS connector 2. */ + IO_PIN_TRS_3_TIP, /**< The tip pin for TRS connector 3. */ + IO_PIN_TRS_3_RING, /**< The ring pin for TRS connector 3. */ + + IO_PIN_COUNT, /**< Number of valid I/O pins. */ +}; + +/** + * @typedef io_polarity_t + * @brief Enumeration of the supported I/O polarities. + */ +typedef uint8_t io_polarity_t; +enum +{ + IO_POLARITY_ACTIVE_LOW, /**< I/O is "on" when grounded. */ + IO_POLARITY_ACTIVE_HIGH, /**< I/O is "on" when at Vcc. */ + + IO_POLARITY_COUNT, /**< Number of valid I/O polarities. */ +}; + +/** + * @typedef io_state_t + * @brief Enumeration of the states that I/O pins may be in. + */ +typedef uint8_t io_state_t; +enum +{ + IO_STATE_OFF, /**< I/O is "off" (inactive). */ + IO_STATE_ON, /**< I/O is "on" (active). */ + + IO_STATE_COUNT, /**< Number of valid I/O states. */ + + IO_STATE_NONE /**< I/O has no state (not configured). */ + = IO_STATE_COUNT, +}; + +/** + * @typedef io_type_t + * @brief Enumeration of the supported I/O types. + */ +typedef uint8_t io_type_t; +enum +{ + IO_TYPE_INPUT_STRAIGHT_KEY, /**< I/O is a straight key input. */ + IO_TYPE_INPUT_PADDLE_LEFT, /**< I/O is a left paddle input. */ + IO_TYPE_INPUT_PADDLE_RIGHT, /**< I/O is a right paddle input. */ + + IO_TYPE_OUTPUT_KEYER, /**< I/O is the keyer output to the radio. */ + + IO_TYPE_COUNT, /**< Number of valid I/O types. */ + + IO_TYPE_NONE /**< Pin is not configured. */ + = IO_TYPE_COUNT, + + IO_TYPE_INPUT_FIRST /**< First input type. */ + = IO_TYPE_INPUT_STRAIGHT_KEY, + IO_TYPE_INPUT_LAST /**< Last input type. */ + = IO_TYPE_INPUT_PADDLE_RIGHT, + + IO_TYPE_OUTPUT_FIRST /**< First output type. */ + = IO_TYPE_OUTPUT_KEYER, + IO_TYPE_OUTPUT_LAST /**< Last output type. */ + = IO_TYPE_OUTPUT_KEYER, +}; + +/* ---------------------------------------------- PROCEDURE PROTOTYPES ---------------------------------------------- */ + +/** + * @fn io_get_polarity( io_pin_t ) + * @brief Returns the polarity of the specified I/O pin. + */ +io_polarity_t io_get_polarity( io_pin_t pin ); + +/** + * @fn io_get_state( io_pin_t ) + * @brief Returns the state of the specified I/O pin. + */ +io_state_t io_get_state( io_pin_t pin ); + +/** + * @fn io_get_state( io_type_t ) + * @brief Returns the state of the specified I/O type. + * @note This will return `IO_STATE_ON` if *any* pin with this input type is currently `IO_STATE_ON`. + */ +io_state_t io_get_state_type( io_type_t type ); + +/** + * @fn io_get_type( io_pin_t ) + * @brief Returns the I/O type of the specified I/O pin. + */ +io_type_t io_get_type( io_pin_t pin ); + +/** + * @fn io_init( void ) + * @brief Initializes the keyer input / output module. + */ +void io_init( void ); + +/** + * @fn io_pin_is_input( io_pin_t ) + * @brief Returns `true` if the specified `io_pin_t` is configured as an input. + */ +bool io_pin_is_input( io_pin_t pin ); + +/** + * @fn io_pin_is_output( io_pin_t ) + * @brief Returns `true` if the specified `io_pin_t` is configured as an output. + */ +bool io_pin_is_output( io_pin_t pin ); + +/** + * @fn io_set_output_state( io_pin_t, io_state_t ) + * @brief Sets the state of the specified I/O pin. + * @note Has no effect if the specified pin is not configured as an output. + */ +void io_set_output_state( io_pin_t pin, io_state_t state ); + +/** + * @fn io_set_output_state_type( io_type_t, io_state_t ) + * @brief Sets the state of the specified I/O type. + * @note Has no effect if the specified I/O type is not an output. + */ +void io_set_output_state_type( io_type_t type, io_state_t state ); + +/** + * @fn io_set_polarity( io_pin_t, io_polarity_t ) + * @brief Sets the polarity of the specified I/O pin. + * @note This modifies the application configuration. + */ +void io_set_polarity( io_pin_t pin, io_polarity_t polarity ); + +/** + * @fn io_set_type( io_pin_t, io_type_t ) + * @brief Sets the I/O type of the specified I/O pin. + * @note This modifies the application configuration. + */ +void io_set_type( io_pin_t pin, io_type_t type ); + +/** + * @fn io_tick( tick_t ) + * @brief Performs periodic processing at the specified tick. + */ +void io_tick( tick_t tick ); + +/** + * @fn io_type_is_input( io_type_t ) + * @brief Returns `true` if the specified `io_type_t` represents an input. + */ +bool io_type_is_input( io_type_t type ); + +/** + * @fn io_type_is_output( io_type_t ) + * @brief Returns `true` if the specified `io_type_t` reprsents an output. + */ +bool io_type_is_output( io_type_t type ); + +#endif /* !defined( APPLICATION_IO_H ) */ diff --git a/src/main/application/keyer.c b/src/main/application/keyer.c index 7eb419a..6003a48 100644 --- a/src/main/application/keyer.c +++ b/src/main/application/keyer.c @@ -15,7 +15,7 @@ #include "application/buzzer.h" #include "application/config.h" -#include "application/input.h" +#include "application/io.h" #include "application/keyer.h" #include "application/led.h" #include "application/wpm.h" @@ -62,12 +62,6 @@ enum */ #define AUTOKEY_BUF_SZ 4096 -/** - * @def KEYER_OUT_PIN - * @brief The GPIO pin which is used for the keyer output. - */ -#define KEYER_OUT_PIN GPIO_PIN_A6 - /* --------------------------------------------------- VARIABLES ---------------------------------------------------- */ static bool s_keyed = false; /**< Is the keyer hardware currently keyed? */ @@ -647,13 +641,6 @@ bool keyer_get_on( void ) } /* keyer_get_on() */ -bool keyer_get_output_active_low( void ) -{ - return( config()->keyer_output_active_low ); - -} /* keyer_get_output_active_low() */ - - bool keyer_get_paddle_invert( void ) { return( config()->keyer_paddle_invert ); @@ -684,9 +671,6 @@ void keyer_init( void ) s_ticks[ el ] = 0; s_ticks_tick = 0; - // Initialize GPIO - gpio_set_dir( KEYER_OUT_PIN, GPIO_DIR_OUT ); - // Default to key off set_keyed( false ); @@ -705,16 +689,6 @@ void keyer_panic( void ) } /* keyer_panic() */ -void keyer_set_output_active_low( bool active_low ) -{ - config_t config; - config_get( & config ); - config.keyer_output_active_low = active_low; - config_set( & config ); - -} /* keyer_set_output_active_low() */ - - void keyer_set_paddle_invert( bool invert ) { config_t config; @@ -984,10 +958,21 @@ static bool get_keyed( void ) static state_t get_next_state( void ) { - // Get all relevant inputs - static input_type_field_t inputs = 0; - input_type_field_t prev_inputs = inputs; - inputs = input_types_get_on(); + // Static variables to store values from previous invocations + static bool straight_key = false; + static bool paddle_left = false; + static bool paddle_right = false; + + // Store previous values for paddles + bool prev_paddle_left = paddle_left; + bool prev_paddle_right = paddle_right; + + // Get updated inputs + straight_key = ( io_get_state_type( IO_TYPE_INPUT_STRAIGHT_KEY ) == IO_STATE_ON ); + paddle_left = ( io_get_state_type( IO_TYPE_INPUT_PADDLE_LEFT ) == IO_STATE_ON ); + paddle_right = ( io_get_state_type( IO_TYPE_INPUT_PADDLE_RIGHT ) == IO_STATE_ON ); + + // Get other required settings bool paddle_invert = keyer_get_paddle_invert(); // Determine next state @@ -996,13 +981,12 @@ static state_t get_next_state( void ) // Autokey has highest priority return( STATE_AUTOKEY ); } - else if( is_bit_set( inputs, INPUT_TYPE_STRAIGHT_KEY ) ) + else if( straight_key ) { // Straight key supersedes all other inputs return( STATE_ON ); } - else if( is_bit_set( inputs, INPUT_TYPE_PADDLE_LEFT ) && - is_bit_set( inputs, INPUT_TYPE_PADDLE_RIGHT ) ) + else if( paddle_left && paddle_right ) { switch( keyer_get_paddle_mode() ) { @@ -1016,14 +1000,12 @@ static state_t get_next_state( void ) case KEYER_PADDLE_MODE_ULTIMATIC_ALTERNATE: // The most recently activated paddle wins - if( is_bit_set( inputs, INPUT_TYPE_PADDLE_LEFT ) && - is_bit_clear( prev_inputs, INPUT_TYPE_PADDLE_LEFT ) ) + if( paddle_left && ! prev_paddle_left ) { // Left paddle was more recently activated return( paddle_invert ? STATE_DASHES : STATE_DOTS ); } - else if( is_bit_set( inputs, INPUT_TYPE_PADDLE_RIGHT ) && - is_bit_clear( prev_inputs, INPUT_TYPE_PADDLE_RIGHT ) ) + else if( paddle_right && ! prev_paddle_right ) { // Right paddle was more recently activated return( paddle_invert ? STATE_DOTS : STATE_DASHES ); @@ -1039,14 +1021,14 @@ static state_t get_next_state( void ) fail(); } } - else if( ( ! paddle_invert && is_bit_set( inputs, INPUT_TYPE_PADDLE_LEFT ) ) || - ( paddle_invert && is_bit_set( inputs, INPUT_TYPE_PADDLE_RIGHT ) ) ) + else if( ( ! paddle_invert && paddle_left ) || + ( paddle_invert && paddle_right ) ) { // The left paddle traditionally emits dots return( STATE_DOTS ); } - else if( ( ! paddle_invert && is_bit_set( inputs, INPUT_TYPE_PADDLE_RIGHT ) ) || - ( paddle_invert && is_bit_set( inputs, INPUT_TYPE_PADDLE_LEFT ) ) ) + else if( ( ! paddle_invert && paddle_right ) || + ( paddle_invert && paddle_left ) ) { // The right paddle traditionally emits dashes return( STATE_DASHES ); @@ -1071,17 +1053,8 @@ static void set_keyed( bool keyed ) static void update_hardware( void ) { - // Update GPIO - bool active_lo = keyer_get_output_active_low(); - gpio_set_state( KEYER_OUT_PIN, - s_keyed ? - ( active_lo ? GPIO_STATE_LOW : GPIO_STATE_HIGH ) : - ( active_lo ? GPIO_STATE_HIGH : GPIO_STATE_LOW ) ); - - // Update LED + io_set_output_state_type( IO_TYPE_OUTPUT_KEYER, s_keyed ? IO_STATE_ON : IO_STATE_OFF ); led_set_on( LED_KEY, s_keyed ); - - // Update buzzer buzzer_set_on( s_keyed ); } /* update_hardware() */ diff --git a/src/main/application/keyer.h b/src/main/application/keyer.h index ec06a7e..c0809ee 100644 --- a/src/main/application/keyer.h +++ b/src/main/application/keyer.h @@ -73,12 +73,6 @@ size_t keyer_autokey_str( char const * str ); */ bool keyer_get_on( void ); -/** - * @fn keyer_get_output_active_low( void ) - * @brief Returns `true` if the keyer's output is active low. - */ -bool keyer_get_output_active_low( void ); - /** * @fn keyer_get_paddle_invert( void ) * @brief Returns `true` if the keyer is configured to invert the paddles. In this case, the right paddle will emit @@ -104,13 +98,6 @@ void keyer_init( void ); */ void keyer_panic( void ); -/** - * @fn keyer_set_output_active_low( bool ) - * @brief Sets whether the keyer's output is active low or not. - * @note This modifies the application configuration. - */ -void keyer_set_output_active_low( bool active_low ); - /** * @fn keyer_set_paddle_invert( bool ) * @brief Enables or disables the "invert paddles" setting. If set to `true`, the right paddle will emit dots and the diff --git a/src/main/application/strings.c b/src/main/application/strings.c index 3120a31..6600827 100644 --- a/src/main/application/strings.c +++ b/src/main/application/strings.c @@ -11,6 +11,7 @@ #include +#include "application/io.h" #include "application/strings.h" #include "utility/constants.h" #include "utility/utility.h" @@ -35,34 +36,47 @@ static char const * const s_element_tbl[] = }; _Static_assert( array_count( s_element_tbl ) == WPM_ELEMENT_COUNT, "Invalid string table!" ); -#define INPUT_PIN_PREFIX_LEN 10 -static char const * const s_input_pin_tbl[] = +#define IO_PIN_PREFIX_LEN 7 +static char const * const s_io_pin_tbl[] = { - stringize( INPUT_PIN_TRS_0_TIP ), - stringize( INPUT_PIN_TRS_0_RING ), - stringize( INPUT_PIN_TRS_1_TIP ), - stringize( INPUT_PIN_TRS_1_RING ), - stringize( INPUT_PIN_TRS_2_TIP ), - stringize( INPUT_PIN_TRS_2_RING ), + stringize( IO_PIN_TRS_0_TIP ), + stringize( IO_PIN_TRS_0_RING ), + stringize( IO_PIN_TRS_1_TIP ), + stringize( IO_PIN_TRS_1_RING ), + stringize( IO_PIN_TRS_2_TIP ), + stringize( IO_PIN_TRS_2_RING ), + stringize( IO_PIN_TRS_3_TIP ), + stringize( IO_PIN_TRS_3_RING ), }; -_Static_assert( array_count( s_input_pin_tbl ) == INPUT_PIN_COUNT, "Invalid string table!" ); +_Static_assert( array_count( s_io_pin_tbl ) == IO_PIN_COUNT, "Invalid string table!" ); -#define INPUT_POLARITY_PREFIX_LEN 15 -static char const * const s_input_polarity_tbl[] = +#define IO_POLARITY_PREFIX_LEN 12 +static char const * const s_io_polarity_tbl[] = { - stringize( INPUT_POLARITY_ACTIVE_LOW ), - stringize( INPUT_POLARITY_ACTIVE_HIGH ), + stringize( IO_POLARITY_ACTIVE_LOW ), + stringize( IO_POLARITY_ACTIVE_HIGH ), }; -_Static_assert( array_count( s_input_polarity_tbl ) == INPUT_POLARITY_COUNT, "Invalid string table!" ); +_Static_assert( array_count( s_io_polarity_tbl ) == IO_POLARITY_COUNT, "Invalid string table!" ); -#define INPUT_TYPE_PREFIX_LEN 11 -static char const * const s_input_type_tbl[] = +#define IO_STATE_PREFIX_LEN 9 +static char const * const s_io_state_tbl[] = { - stringize( INPUT_TYPE_STRAIGHT_KEY ), - stringize( INPUT_TYPE_PADDLE_LEFT ), - stringize( INPUT_TYPE_PADDLE_RIGHT ), + stringize( IO_STATE_OFF ), + stringize( IO_STATE_ON ), + stringize( IO_STATE_NONE ), }; -_Static_assert( array_count( s_input_type_tbl ) == INPUT_TYPE_COUNT, "Invalid string table!" ); +_Static_assert( array_count( s_io_state_tbl ) == IO_STATE_COUNT + 1 /* for NONE */, "Invalid string table!" ); + +#define IO_TYPE_PREFIX_LEN 8 +static char const * const s_io_type_tbl[] = +{ + stringize( IO_TYPE_INPUT_STRAIGHT_KEY ), + stringize( IO_TYPE_INPUT_PADDLE_LEFT ), + stringize( IO_TYPE_INPUT_PADDLE_RIGHT ), + stringize( IO_TYPE_OUTPUT_KEYER ), + stringize( IO_TYPE_NONE ), +}; +_Static_assert( array_count( s_io_type_tbl ) == IO_TYPE_COUNT + 1 /* for NONE */, "Invalid string table!" ); #define LED_PREFIX_LEN 4 static char const * const s_led_tbl[] = @@ -122,30 +136,37 @@ char const * string_from_element( wpm_element_t el ) } /* string_from_element() */ -char const * string_from_input_pin( input_pin_t pin ) +char const * string_from_io_pin( io_pin_t pin ) +{ + return( find_string( s_io_pin_tbl, array_count( s_io_pin_tbl ), IO_PIN_PREFIX_LEN, ( size_t )pin ) ); + +} /* string_from_io_pin() */ + + +char const * string_from_io_polarity( io_polarity_t polarity ) { - return( find_string( s_input_pin_tbl, INPUT_PIN_COUNT, INPUT_PIN_PREFIX_LEN, ( size_t )pin ) ); + return( find_string( s_io_polarity_tbl, array_count( s_io_polarity_tbl ), IO_POLARITY_PREFIX_LEN, ( size_t )polarity ) ); -} /* string_from_input_pin() */ +} /* string_from_io_polarity() */ -char const * string_from_input_polarity( input_polarity_t polarity ) +char const * string_from_io_state( io_state_t state ) { - return( find_string( s_input_polarity_tbl, INPUT_POLARITY_COUNT, INPUT_POLARITY_PREFIX_LEN, ( size_t )polarity ) ); + return( find_string( s_io_state_tbl, array_count( s_io_state_tbl ), IO_STATE_PREFIX_LEN, ( size_t )state ) ); -} /* string_from_input_polarity() */ +} /* string_from_io_state() */ -char const * string_from_input_type( input_type_t type ) +char const * string_from_io_type( io_type_t type ) { - return( find_string( s_input_type_tbl, INPUT_TYPE_COUNT, INPUT_TYPE_PREFIX_LEN, ( size_t )type ) ); + return( find_string( s_io_type_tbl, array_count( s_io_type_tbl ), IO_TYPE_PREFIX_LEN, ( size_t )type ) ); -} /* string_from_input_type() */ +} /* string_from_io_type() */ char const * string_from_led( led_t led ) { - return( find_string( s_led_tbl, LED_COUNT, LED_PREFIX_LEN, ( size_t )led ) ); + return( find_string( s_led_tbl, array_count( s_led_tbl ), LED_PREFIX_LEN, ( size_t )led ) ); } /* string_from_led() */ @@ -172,7 +193,7 @@ bool string_to_bool( char const * str, bool * b ) bool string_to_element( char const * str, wpm_element_t * el ) { size_t result = find_value( s_element_tbl, array_count( s_element_tbl ), ELEMENT_PREFIX_LEN, str ); - if( result == WPM_ELEMENT_COUNT ) + if( result == array_count( s_element_tbl ) ) return( false ); * el = ( wpm_element_t )result; @@ -181,46 +202,58 @@ bool string_to_element( char const * str, wpm_element_t * el ) } /* string_to_element() */ -bool string_to_input_pin( char const * str, input_pin_t * pin ) +bool string_to_io_pin( char const * str, io_pin_t * pin ) +{ + size_t result = find_value( s_io_pin_tbl, array_count( s_io_pin_tbl ), IO_PIN_PREFIX_LEN, str ); + if( result == array_count( s_io_pin_tbl ) ) + return( false ); + + * pin = ( io_pin_t )result; + return( true ); + +} /* string_to_io_pin() */ + + +bool string_to_io_polarity( char const * str, io_polarity_t * polarity ) { - size_t result = find_value( s_input_pin_tbl, array_count( s_input_pin_tbl ), INPUT_PIN_PREFIX_LEN, str ); - if( result == INPUT_PIN_COUNT ) + size_t result = find_value( s_io_polarity_tbl, array_count( s_io_polarity_tbl ), IO_POLARITY_PREFIX_LEN, str ); + if( result == array_count( s_io_polarity_tbl ) ) return( false ); - * pin = ( input_pin_t )result; + * polarity = ( io_polarity_t )result; return( true ); -} /* string_to_input_pin() */ +} /* string_to_io_polarity() */ -bool string_to_input_polarity( char const * str, input_polarity_t * polarity ) +bool string_to_io_state( char const * str, io_state_t * state ) { - size_t result = find_value( s_input_polarity_tbl, array_count( s_input_polarity_tbl ), INPUT_POLARITY_PREFIX_LEN, str ); - if( result == INPUT_POLARITY_COUNT ) + size_t result = find_value( s_io_state_tbl, array_count( s_io_state_tbl ), IO_STATE_PREFIX_LEN, str ); + if( result == array_count( s_io_state_tbl ) ) return( false ); - * polarity = ( input_polarity_t )result; + * state = ( io_state_t )result; return( true ); -} /* string_to_input_polarity() */ +} /* string_to_io_state() */ -bool string_to_input_type( char const * str, input_type_t * type ) +bool string_to_io_type( char const * str, io_type_t * type ) { - size_t result = find_value( s_input_type_tbl, array_count( s_input_type_tbl ), INPUT_TYPE_PREFIX_LEN, str ); - if( result == INPUT_TYPE_COUNT ) + size_t result = find_value( s_io_type_tbl, array_count( s_io_type_tbl ), IO_TYPE_PREFIX_LEN, str ); + if( result == array_count( s_io_type_tbl ) ) return( false ); - * type = ( input_type_t )result; + * type = ( io_type_t )result; return( true ); -} /* string_to_input_type() */ +} /* string_to_io_type() */ bool string_to_led( char const * str, led_t * led ) { size_t result = find_value( s_led_tbl, array_count( s_led_tbl ), LED_PREFIX_LEN, str ); - if( result == LED_COUNT ) + if( result == array_count( s_led_tbl ) ) return( false ); * led = ( led_t )result; diff --git a/src/main/application/strings.h b/src/main/application/strings.h index 484b3f9..3eea57d 100644 --- a/src/main/application/strings.h +++ b/src/main/application/strings.h @@ -14,7 +14,7 @@ #include -#include "application/input.h" +#include "application/io.h" #include "application/keyer.h" #include "application/led.h" #include "application/wpm.h" @@ -35,13 +35,13 @@ /** * @def OFF_STR - * @brief The token which indicates that an input or output is off. + * @brief The token which indicates that an io or output is off. */ #define OFF_STR "off" /** * @def ON_STR - * @brief The token which indicates that an input or output is on. + * @brief The token which indicates that an io or output is on. */ #define ON_STR "on" @@ -74,22 +74,28 @@ char const * string_from_bool( bool b ); char const * string_from_element( wpm_element_t el ); /** - * @fn string_from_input_pin( input_pin_t ) - * @brief Returns a string for the specified `input_pin_t`. + * @fn string_from_io_pin( io_pin_t ) + * @brief Returns a string for the specified `io_pin_t`. */ -char const * string_from_input_pin( input_pin_t pin ); +char const * string_from_io_pin( io_pin_t pin ); /** - * @fn string_from_input_polarity( input_polarity_t ) - * @brief Returns a string for the specified `input_polarity_t`. + * @fn string_from_io_polarity( io_polarity_t ) + * @brief Returns a string for the specified `io_polarity_t`. */ -char const * string_from_input_polarity( input_polarity_t polarity ); +char const * string_from_io_polarity( io_polarity_t polarity ); /** - * @fn string_from_input_type( input_type_t ) - * @brief Returns a string for the specified `input_type_t`. + * @fn string_from_io_state( io_state_t ) + * @brief Returns a string for the specified `io_state_t`. */ -char const * string_from_input_type( input_type_t type ); +char const * string_from_io_state( io_state_t state ); + +/** + * @fn string_from_io_type( io_type_t ) + * @brief Returns a string for the specified `io_type_t`. + */ +char const * string_from_io_type( io_type_t type ); /** * @fn string_from_led( led_t ) @@ -118,25 +124,32 @@ bool string_to_bool( char const * str, bool * b ); bool string_to_element( char const * str, wpm_element_t * el ); /** - * @fn string_to_input_pin( char const *, input_pin_t * ) - * @brief Converts a string to an `input_pin_t`, if possible. + * @fn string_to_io_pin( char const *, io_pin_t * ) + * @brief Converts a string to an `io_pin_t`, if possible. + * @returns `true` if the conversion succeeded. + */ +bool string_to_io_pin( char const * str, io_pin_t * pin ); + +/** + * @fn string_to_io_polarity( char const *, io_polarity_t * ) + * @brief Converts a string to an `io_polarity_t`, if possible. * @returns `true` if the conversion succeeded. */ -bool string_to_input_pin( char const * str, input_pin_t * pin ); +bool string_to_io_polarity( char const * str, io_polarity_t * polarity ); /** - * @fn string_to_input_polarity( char const *, input_polarity_t * ) - * @brief Converts a string to an `input_polarity_t`, if possible. + * @fn string_to_io_state( char const *, io_state_t ) + * @brief Converts a string to an `io_state_t`, if possible. * @returns `true` if the conversion succeeded. */ -bool string_to_input_polarity( char const * str, input_polarity_t * polarity ); +bool string_to_io_state( char const * str, io_state_t * state ); /** - * @fn string_to_input_type( char const *, input_type_t * ) - * @brief Converts a string to an `input_type_t`, if possible. + * @fn string_to_io_type( char const *, io_type_t * ) + * @brief Converts a string to an `io_type_t`, if possible. * @returns `true` if the conversion succeeded. */ -bool string_to_input_type( char const * str, input_type_t * type ); +bool string_to_io_type( char const * str, io_type_t * type ); /** * @fn string_to_led( char const *, led_t * ) diff --git a/src/main/core/main.c b/src/main/core/main.c index 25e0c85..7031c1e 100644 --- a/src/main/core/main.c +++ b/src/main/core/main.c @@ -19,7 +19,6 @@ #include "application/buzzer.h" #include "application/config.h" #include "application/debug_port.h" -#include "application/input.h" #include "application/keyer.h" #include "application/led.h" #include "application/storage.h" @@ -60,10 +59,10 @@ int main( void ); /** - * @fn handle_input_state( void ) - * @brief Handles the `EVENT_INPUT_STATE` event. + * @fn handle_io_state( void ) + * @brief Handles the `EVENT_IO_STATE` event. */ -static void handle_input_state( void ); +static void handle_io_state( void ); /** * @fn handle_tick( void ) @@ -136,7 +135,7 @@ int main( void ) } /* main() */ -static void handle_input_state( void ) +static void handle_io_state( void ) { // An input state changed - immediately update the keyer keyer_tick( sys_get_tick() ); @@ -206,7 +205,7 @@ static void init( void ) storage_init(); config_init(); led_init(); - input_init(); + io_init(); buzzer_init(); debug_port_init(); keyer_init(); @@ -225,8 +224,8 @@ static void main_loop( void ) event_field_t events = sys_wait(); if( is_bit_set( events, EVENT_TICK ) ) handle_tick(); - if( is_bit_set( events, EVENT_INPUT_STATE ) ) - handle_input_state(); + if( is_bit_set( events, EVENT_IO_STATE ) ) + handle_io_state(); if( is_bit_set( events, EVENT_USART_0_RX_COMPLETE ) ) handle_usart_rx_complete( USART_0 ); if( is_bit_set( events, EVENT_USART_0_TX_COMPLETE ) ) @@ -243,7 +242,6 @@ static void main_loop( void ) static void periodic_1ms( tick_t tick ) { // Periodic processing for modules with 1000 Hz tick rates - input_tick( tick ); keyer_tick( tick ); } /* periodic_1ms() */ @@ -253,6 +251,7 @@ static void periodic_50ms( tick_t tick ) { // Periodic processing for modules with 20 Hz tick rates buzzer_tick( tick ); + io_tick( tick ); led_tick( tick ); } /* periodic_50ms() */ diff --git a/src/main/core/sys.h b/src/main/core/sys.h index 44b64f4..15225a7 100644 --- a/src/main/core/sys.h +++ b/src/main/core/sys.h @@ -28,7 +28,7 @@ typedef uint8_t event_t; enum { EVENT_TICK, /**< 1 millisecond tick event. */ - EVENT_INPUT_STATE, /**< Key input state changed. */ + EVENT_IO_STATE, /**< Keyer input / output state changed. */ EVENT_USART_0_RX_COMPLETE, /**< USART 0 completed receiving data. */ EVENT_USART_0_TX_COMPLETE, /**< USART 0 completed transmitting data. */ EVENT_USART_1_RX_COMPLETE, /**< USART 1 completed receiving data. */ diff --git a/src/main/drivers/gpio.c b/src/main/drivers/gpio.c index 1aefa73..da11608 100644 --- a/src/main/drivers/gpio.c +++ b/src/main/drivers/gpio.c @@ -137,6 +137,15 @@ gpio_port_t gpio_get_pin_port( gpio_pin_t pin ) } /* gpio_get_pin_port() */ +bool gpio_get_pullup( gpio_pin_t pin ) +{ + validate_pin( pin ); + + return( is_bit_set( PIN_OUT( pin ), PIN_INDEX( pin ) ) ); + +} /* gpio_get_pullup() */ + + gpio_state_t gpio_get_state( gpio_pin_t pin ) { validate_pin( pin ); diff --git a/src/main/drivers/gpio.h b/src/main/drivers/gpio.h index c6c6329..92363ed 100644 --- a/src/main/drivers/gpio.h +++ b/src/main/drivers/gpio.h @@ -145,6 +145,13 @@ gpio_dir_t gpio_get_dir( gpio_pin_t pin ); */ gpio_port_t gpio_get_pin_port( gpio_pin_t pin ); +/** + * @fn gpio_get_pullup( gpio_pin_t ) + * @brief Returns the state of the pull-up resistor for the specified pin. + * @note This is only applicable to input pins. + */ +bool gpio_get_pullup( gpio_pin_t pin ); + /** * @fn gpio_get_state( gpio_pin_t ) * @brief Returns the current state of the specified pin. diff --git a/src/main/utility/utility.h b/src/main/utility/utility.h index 9942e61..3cc02c4 100644 --- a/src/main/utility/utility.h +++ b/src/main/utility/utility.h @@ -16,6 +16,8 @@ /* ----------------------------------------------------- MACROS ----------------------------------------------------- */ +/* -- COMPILER UTILITIES -- */ + /** * @def FUNC_MAY_BE_UNUSED * @brief May be placed after a function declaration to inform the compiler that a function may not be called, and @@ -31,6 +33,36 @@ #define FUNC_NEVER_RETURNS \ __attribute__((noreturn)) +/** + * @def array_count( _a ) + * @brief Returns the number of elements in the specified array. + */ +#define array_count( _a ) \ + ( sizeof( _a ) / sizeof( _a[ 0 ] ) ) + +/** + * @def sizeof_bits( _x ) + * @brief Returns the size of `_x`, in bits. + */ +#define sizeof_bits( _x ) \ + ( sizeof( _x ) * BITS_PER_BYTE ) + +/** + * @def stringize( _x ) + * @brief Returns a string representation of the argument. + */ +#define stringize( _x ) \ + # _x + +/** + * @def stringize_value( _x ) + * @brief Returns a string representation of the *value* of the argument. + */ +#define stringize_value( _x ) \ + stringize( _x ) + +/* -- SIMPLE CONSTANTS -- */ + /** * @def TRUE * @brief Define the `TRUE` macro as `1`. @@ -63,12 +95,14 @@ #define OFF \ ( 0 ) +/* -- UTILITY FUNCTIONS -- */ + /** - * @def array_count( _a ) - * @brief Returns the number of elements in the specified array. + * @def nop() + * @brief Do nothing, stylishly. */ -#define array_count( _a ) \ - ( sizeof( _a ) / sizeof( _a[ 0 ] ) ) +#define nop() \ + ( void )0 /** * @def clamp( _x, _min, _max ) @@ -92,33 +126,7 @@ } \ while( 0 ) -/** - * @def nop() - * @brief Do nothing, stylishly. - */ -#define nop() \ - ( void )0 - -/** - * @def sizeof_bits( _x ) - * @brief Returns the size of `_x`, in bits. - */ -#define sizeof_bits( _x ) \ - ( sizeof( _x ) * BITS_PER_BYTE ) - -/** - * @def stringize( _x ) - * @brief Returns a string representation of the argument. - */ -#define stringize( _x ) \ - # _x - -/** - * @def stringize_value( _x ) - * @brief Returns a string representation of the *value* of the argument. - */ -#define stringize_value( _x ) \ - stringize( _x ) +/* -- BITFIELD MANIPULATION -- */ /** * @def bitmask( _b )