diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..59f49c1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.20) + +set(ENV{PICO_SDK_PATH} ${CMAKE_CURRENT_LIST_DIR}/pico-sdk) +set(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/FreeRTOS/FreeRTOS/Source) +set(PICO_BOARD adafruit_macropad_rp2040) + +include(pico_sdk_import.cmake) +include(pico_extras_import_optional.cmake) +include(FreeRTOS_Kernel_import.cmake) + +project(threeam C CXX ASM) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) + +add_executable(stream_pad) + +pico_sdk_init() + +pico_generate_pio_header(stream_pad ${CMAKE_CURRENT_LIST_DIR}/src/Neopixel.pio) + +target_sources(stream_pad PRIVATE + src/main.cpp + src/Page.cpp + src/Config.cpp + src/Neopixels.cpp + src/ReadKeys.cpp + src/Wheel.cpp + #this is definitely wrong + #pico-sdk/src/rp2_common/hardware_gpio/include/hardware/ + EmbeddedProto/src/Fields.cpp + EmbeddedProto/src/MessageInterface.cpp + EmbeddedProto/src/ReadBufferSection.cpp + ) + +target_include_directories(stream_pad PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/src/ + ${CMAKE_CURRENT_LIST_DIR}/src/protogen/ + ${CMAKE_CURRENT_LIST_DIR}/EmbeddedProto/src/ + ${CMAKE_CURRENT_LIST_DIR}/tinyusb/ + ) + +target_link_libraries(stream_pad + pico_stdlib + hardware_pio + pico_unique_id + #tinyusb_device + #tinyusb_board + ) + +pico_enable_stdio_usb(stream_pad 0) +pico_enable_stdio_uart(stream_pad 1) + +pico_add_extra_outputs(stream_pad) diff --git a/FreeRTOS_Kernel_import.cmake b/FreeRTOS_Kernel_import.cmake new file mode 100644 index 0000000..dc68ed0 --- /dev/null +++ b/FreeRTOS_Kernel_import.cmake @@ -0,0 +1,62 @@ +# This is a copy of /portable/ThirdParty/GCC/RP2040/FREERTOS_KERNEL_import.cmake + +# This can be dropped into an external project to help locate the FreeRTOS kernel +# It should be include()ed prior to project(). Alternatively this file may +# or the CMakeLists.txt in this directory may be included or added via add_subdirectory +# respectively. + +if (DEFINED ENV{FREERTOS_KERNEL_PATH} AND (NOT FREERTOS_KERNEL_PATH)) + set(FREERTOS_KERNEL_PATH $ENV{FREERTOS_KERNEL_PATH}) + message("Using FREERTOS_KERNEL_PATH from environment ('${FREERTOS_KERNEL_PATH}')") +endif () + +set(FREERTOS_KERNEL_RP2040_RELATIVE_PATH "portable/ThirdParty/GCC/RP2040") +# undo the above +set(FREERTOS_KERNEL_RP2040_BACK_PATH "../../../..") + +if (NOT FREERTOS_KERNEL_PATH) + # check if we are inside the FreeRTOS kernel tree (i.e. this file has been included directly) + get_filename_component(_ACTUAL_PATH ${CMAKE_CURRENT_LIST_DIR} REALPATH) + get_filename_component(_POSSIBLE_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} REALPATH) + if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH) + get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH) + endif() + if (_ACTUAL_PATH STREQUAL _POSSIBLE_PATH) + get_filename_component(FREERTOS_KERNEL_PATH ${CMAKE_CURRENT_LIST_DIR}/${FREERTOS_KERNEL_RP2040_BACK_PATH} REALPATH) + message("Setting FREERTOS_KERNEL_PATH to ${FREERTOS_KERNEL_PATH} based on location of FreeRTOS-Kernel-import.cmake") + elseif (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../FreeRTOS-Kernel") + set(FREERTOS_KERNEL_PATH ${PICO_SDK_PATH}/../FreeRTOS-Kernel) + message("Defaulting FREERTOS_KERNEL_PATH as sibling of PICO_SDK_PATH: ${FREERTOS_KERNEL_PATH}") + endif() +endif () + +if (NOT FREERTOS_KERNEL_PATH) + foreach(POSSIBLE_SUFFIX Source FreeRTOS-Kernel FreeRTOS/Source) + # check if FreeRTOS-Kernel exists under directory that included us + set(SEARCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR}}) + set(SEARCH_ROOT ../../../..) + get_filename_component(_POSSIBLE_PATH ${SEARCH_ROOT}/${POSSIBLE_SUFFIX} REALPATH) + if (EXISTS ${_POSSIBLE_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/CMakeLists.txt) + get_filename_component(FREERTOS_KERNEL_PATH ${_POSSIBLE_PATH} REALPATH) + message("Setting FREERTOS_KERNEL_PATH to '${FREERTOS_KERNEL_PATH}' found relative to enclosing project") + break() + endif() + endforeach() +endif() + +if (NOT FREERTOS_KERNEL_PATH) + message(FATAL_ERROR "FreeRTOS location was not specified. Please set FREERTOS_KERNEL_PATH.") +endif() + +set(FREERTOS_KERNEL_PATH "${FREERTOS_KERNEL_PATH}" CACHE PATH "Path to the FreeRTOS Kernel") + +get_filename_component(FREERTOS_KERNEL_PATH "${FREERTOS_KERNEL_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${FREERTOS_KERNEL_PATH}) + message(FATAL_ERROR "Directory '${FREERTOS_KERNEL_PATH}' not found") +endif() +if (NOT EXISTS ${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}/CMakeLists.txt) + message(FATAL_ERROR "Directory '${FREERTOS_KERNEL_PATH}' does not contain an RP2040 port here: ${FREERTOS_KERNEL_RP2040_RELATIVE_PATH}") +endif() +set(FREERTOS_KERNEL_PATH ${FREERTOS_KERNEL_PATH} CACHE PATH "Path to the FreeRTOS_KERNEL" FORCE) + +add_subdirectory(${FREERTOS_KERNEL_PATH}/${FREERTOS_KERNEL_RP2040_RELATIVE_PATH} FREERTOS_KERNEL) \ No newline at end of file diff --git a/src/Neopixels.cpp b/src/Neopixels.cpp new file mode 100644 index 0000000..65f5718 --- /dev/null +++ b/src/Neopixels.cpp @@ -0,0 +1,48 @@ +#include "Neopixels.hpp" +#include +#include "Neopixel.pio.h" +//#include "Pinmap.hpp" +#include "hardware/gpio.h" + +#define NEO_PIO pio0 + +namespace LED +{ + static int state_machine; + void init_neopixels(Pin::pin_t pin) + { + constexpr float NEOPIXEL_FREQ = 800000; + static bool initialized = false; + if (!initialized) + { + PIO pio = NEO_PIO; + auto offset = pio_add_program(pio, &ws2812_program); + state_machine = pio_claim_unused_sm(pio, true); + ws2812_program_init(pio, state_machine, offset, pin, NEOPIXEL_FREQ, false); + initialized = true; + } + } + void write_leds(Configuration::Pixel *buffer) + { + + } + void put_pixel(Configuration::Pixel pixel) + { + pio_sm_put_blocking(NEO_PIO, state_machine, pixel.value); + } + void init_notification_led() + { + gpio_init(Pin::LED); + //gpio_set_function(Pin::LED, GPIO_FUNC_SIO); + gpio_set_input_enabled(Pin::LED, false); + gpio_put(Pin::LED, false); + } + void set_notification_led(bool value) + { + gpio_put(Pin::LED, value); + } + bool get_notification_led() + { + return gpio_get(Pin::LED); + } +} diff --git a/src/Neopixels.hpp b/src/Neopixels.hpp new file mode 100644 index 0000000..6b5cccb --- /dev/null +++ b/src/Neopixels.hpp @@ -0,0 +1,18 @@ +#ifndef H_594B306D782340DC94F812F498931430 +#define H_594B306D782340DC94F812F498931430 + +#include "Pinmap.hpp" +#include "Pixel.hpp" + +namespace LED +{ + void init_neopixels(Pin::pin_t pin); + void write_leds(Configuration::Pixel *buffer); + void put_pixel(Configuration::Pixel pixel); + void init_notification_led(); + void set_notification_led(bool value); + bool get_notification_led(); +} + + +#endif //H_594B306D782340DC94F812F498931430 diff --git a/src/Pinmap.hpp b/src/Pinmap.hpp new file mode 100644 index 0000000..d6351d0 --- /dev/null +++ b/src/Pinmap.hpp @@ -0,0 +1,35 @@ +#ifndef H_FCF0432A49E34C2DA99E0085234840A5 +#define H_FCF0432A49E34C2DA99E0085234840A5 + +#include +#include + +namespace Pin +{ + using pin_t = unsigned int; + constexpr pin_t InvalidPin = std::numeric_limits::max(); + constexpr pin_t WheelButton = 0; + constexpr pin_t WheelA = 17; + constexpr pin_t WheelB = 18; + constexpr pin_t Key(uint32_t key_number) + { + if (1 <= key_number <= 12) + return static_cast(key_number); + return InvalidPin; + } + constexpr pin_t OLED_CS = 22; + constexpr pin_t OLED_RESET = 23; + constexpr pin_t OLED_DC = 24; + constexpr pin_t SCK = 26; + constexpr pin_t MOSI = 27; + constexpr pin_t MISO = 28; + + constexpr pin_t NEOPIXEL = 19; + + constexpr pin_t LED = 13; + constexpr pin_t SPEAKER_SHUTDOWN = 14; + constexpr pin_t SPEAKER = 16; + +} + +#endif //H_FCF0432A49E34C2DA99E0085234840A5 \ No newline at end of file diff --git a/src/ReadKeys.cpp b/src/ReadKeys.cpp new file mode 100644 index 0000000..b9cf992 --- /dev/null +++ b/src/ReadKeys.cpp @@ -0,0 +1,42 @@ +#include "ReadKeys.hpp" +#include "Pinmap.hpp" +#include "hardware/gpio.h" + +namespace Hardware +{ + bool KeyState::get_pin(int pinnum) const + { + return data & (1 << pinnum); + } + void KeyState::set_pin(int pinnum, bool value) + { + if (value) + data |= (1 << pinnum); + else + data &= ~(1 << pinnum); + } + void init_keys() + { + static bool initialized = false; + if (!initialized) + { + gpio_init_mask(key_pins()); + for (uint32_t i = 1; i <= 12; i++) + { + //gpio_set_function(Pin::Key(i), GPIO_FUNC_SIO); + gpio_set_input_enabled(Pin::Key(i), true); + gpio_pull_up(Pin::Key(i)); + } + initialized = true; + } + } + KeyState read_keys() + { + uint32_t raw = gpio_get_all(); + KeyState state; + for (int i = 1; i <= 12; i++) + if (raw & (1 << i)) + state.set_pin(i, true); + return state; + } +} \ No newline at end of file diff --git a/src/ReadKeys.hpp b/src/ReadKeys.hpp new file mode 100644 index 0000000..57fd0d2 --- /dev/null +++ b/src/ReadKeys.hpp @@ -0,0 +1,39 @@ +#ifndef H_010DEF16FF074C9DAAD8143DF189A536 +#define H_010DEF16FF074C9DAAD8143DF189A536 + +#include +#include "Pinmap.hpp" +#include + +namespace Hardware +{ + constexpr unsigned int key_pins() + { + uint32_t pins = 0; + for (int i = 1; i <= 12; i++) + pins |= Pin::Key(i); + return pins; + } + struct KeyState + { + //might implement an iterator eventually + // struct Iterator + // { + // using iterator_category = std::random_access_iterator_tag; + // using difference_type = int; + // using value_type = bool; + // using pointer = bool*; + // using reference = bool&; + + // private: + + // }; + uint32_t data; + bool get_pin(int pinnum) const; + void set_pin(int pinnum, bool value); + }; + void init_keys(); + KeyState read_keys(); +} + +#endif //H_010DEF16FF074C9DAAD8143DF189A536 diff --git a/src/Speaker.cpp b/src/Speaker.cpp new file mode 100644 index 0000000..2000dfb --- /dev/null +++ b/src/Speaker.cpp @@ -0,0 +1,17 @@ +#include "Speaker.hpp" + +namespace Hardware +{ + void init_speaker() + { + + } + void speaker_set_tone(float hz) + { + + } + void speaker_off() + { + + } +} \ No newline at end of file diff --git a/src/Speaker.hpp b/src/Speaker.hpp new file mode 100644 index 0000000..174a337 --- /dev/null +++ b/src/Speaker.hpp @@ -0,0 +1,11 @@ +#ifndef H_9C70317050DA422699F985336969FE03 +#define H_9C70317050DA422699F985336969FE03 + +namespace Hardware +{ + void init_speaker(); + void speaker_set_tone(float hz); + void speaker_off(); +} + +#endif //H_9C70317050DA422699F985336969FE03 diff --git a/src/Wheel.cpp b/src/Wheel.cpp new file mode 100644 index 0000000..c3429c7 --- /dev/null +++ b/src/Wheel.cpp @@ -0,0 +1,27 @@ +#include "Wheel.hpp" +#include "hardware/gpio.h" +#include "Pinmap.hpp" + +namespace Hardware +{ + void init_wheel() + { + gpio_init_mask( + (1 << Pin::WheelA) + | (1 << Pin::WheelB) + | (1 << Pin::WheelButton)); + gpio_set_input_enabled(Pin::WheelA, true); + gpio_set_input_enabled(Pin::WheelB, true); + gpio_set_input_enabled(Pin::WheelButton, true); + gpio_pull_up(Pin::WheelButton); + } + WheelState get_wheel_state() + { + WheelState state; + auto pins = gpio_get_all(); + state.a = (pins & (1 << Pin::WheelA)) != 0; + state.b = (pins & (1 << Pin::WheelB)) != 0; + state.button = (pins & (1 << Pin::WheelButton)) != 0; + return state; + } +} diff --git a/src/Wheel.hpp b/src/Wheel.hpp new file mode 100644 index 0000000..5610a5e --- /dev/null +++ b/src/Wheel.hpp @@ -0,0 +1,19 @@ +#ifndef H_A2D3745FDEDC4DAF9F72E30E5E782C5B +#define H_A2D3745FDEDC4DAF9F72E30E5E782C5B + +#include + +namespace Hardware +{ + struct WheelState + { + bool a; + bool b; + bool button; + int32_t count; + }; + void init_wheel(); + WheelState get_wheel_state(); +} + +#endif //H_A2D3745FDEDC4DAF9F72E30E5E782C5B diff --git a/src/main.cpp b/src/main.cpp index c5d8248..66faaa1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,9 +3,11 @@ #include "ProtoConfig.h" #include "ReadBufferInterface.h" #include "ReadBufferFixedSize.h" -//#include +#include #include "pico/stdlib.h" #include +#include "Neopixels.hpp" +#include "ReadKeys.hpp" constexpr uint32_t MAX_KEY_PRESSES = 5; constexpr uint32_t MAX_STR_LENGTH = 50; @@ -27,7 +29,27 @@ char config_buffer[1024]; extern "C" int main() { - stdio_init_all(); + //stdio_init_all(); + //sleep_ms(10 * 1000); + //puts("boot\n"); + //printf("boot\n"); + // LED::init_neopixels(Pin::NEOPIXEL); + // Configuration::Pixel pix; + // pix.red() = 100; + // pix.green() = 100; + // pix.blue() = 100; + // for (int i = 0; i < 12; i++) + // LED::put_pixel(pix); + // Hardware::init_keys(); + LED::init_notification_led(); + while (1) + { + auto keys = Hardware::read_keys(); + LED::set_notification_led(keys.data != 0); + printf("Keys: {}\n", keys.data); + sleep_ms(1000); + } + //block scope to keep the deserialized config around as little as possible { EmbeddedProto::ReadBufferFixedSize buffer; ConfigDeser config_message; diff --git a/src/tusb_config.h b/src/tusb_config.h new file mode 100644 index 0000000..868424e --- /dev/null +++ b/src/tusb_config.h @@ -0,0 +1,111 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by board.mk +#ifndef CFG_TUSB_MCU + #error CFG_TUSB_MCU must be defined +#endif + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_DEVICE_RHPORT_NUM + #define BOARD_DEVICE_RHPORT_NUM 0 +#endif + +// RHPort max operational speed can defined by board.mk +// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed +#ifndef BOARD_DEVICE_RHPORT_SPEED + #if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \ + CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56 || CFG_TUSB_MCU == OPT_MCU_SAMX7X) + #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED + #else + #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED + #endif +#endif + +// Device mode with rhport and speed defined by board.mk +#if BOARD_DEVICE_RHPORT_NUM == 0 + #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) +#elif BOARD_DEVICE_RHPORT_NUM == 1 + #define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) +#else + #error "Incorrect RHPort configuration" +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +// CFG_TUSB_DEBUG is defined by compiler in DEBUG build +// #define CFG_TUSB_DEBUG 0 + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_HID 1 +#define CFG_TUD_CDC 0 +#define CFG_TUD_MSC 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 + +// HID buffer size Should be sufficient to hold ID (if any) + Data +#define CFG_TUD_HID_EP_BUFSIZE 16 + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */