From a2a8e988d2830573d284d31d59f701348457f7e7 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Sun, 31 Oct 2021 15:55:38 +0000 Subject: [PATCH 1/4] MicroPython SDL: Experimental MicroPython unix/SDL port --- libraries/hardware-unix.cpp | 294 ++++++++++++++++++ libraries/picosystem-unix.cpp | 54 ++++ libraries/picosystem.cpp | 1 - libraries/picosystem.hpp | 1 - libraries/utility.cpp | 4 +- micropython/modules/micropython-unix.cmake | 6 + micropython/modules/picosystem/buffer.cpp | 4 - micropython/modules/picosystem/hardware.cpp | 4 - .../modules/picosystem/micropython-unix.cmake | 72 +++++ .../modules/picosystem/picosystem-unix.cpp | 169 ++++++++++ micropython/modules/picosystem/primitives.cpp | 4 - micropython/modules/picosystem/state.cpp | 4 - micropython/modules/picosystem/stats.cpp | 4 - micropython/modules/picosystem/utility.cpp | 4 - micropython/modules/picosystem/voice.cpp | 4 - 15 files changed, 597 insertions(+), 32 deletions(-) create mode 100644 libraries/hardware-unix.cpp create mode 100644 libraries/picosystem-unix.cpp create mode 100644 micropython/modules/micropython-unix.cmake create mode 100644 micropython/modules/picosystem/micropython-unix.cmake create mode 100644 micropython/modules/picosystem/picosystem-unix.cpp diff --git a/libraries/hardware-unix.cpp b/libraries/hardware-unix.cpp new file mode 100644 index 0000000..c1c14c4 --- /dev/null +++ b/libraries/hardware-unix.cpp @@ -0,0 +1,294 @@ +#include +#include +#include +#include +#include +#include +#include "SDL.h" + +#include "picosystem.hpp" + +SDL_Window* window = nullptr; +SDL_Thread *t_event_pump = nullptr; +SDL_Renderer *renderer = nullptr; +SDL_Texture *texture = nullptr; +SDL_mutex *m_flip = nullptr; +static bool running = true; +std::chrono::time_point t_start; +std::chrono::time_point t_last_flip; + +const uint32_t FPS = 50; + +namespace picosystem { + uint32_t _sdl_input = 0; + + std::map keys = { + // arrow keys + {SDLK_DOWN, button::DOWN}, + {SDLK_UP, button::UP}, + {SDLK_LEFT, button::LEFT}, + {SDLK_RIGHT, button::RIGHT}, + + // wasd + {SDLK_w, button::UP}, + {SDLK_a, button::LEFT}, + {SDLK_s, button::DOWN}, + {SDLK_d, button::RIGHT}, + + // action buttons + {SDLK_z, button::A}, + {SDLK_x, button::B}, + {SDLK_c, button::X}, + {SDLK_v, button::Y}, + + {SDLK_u, button::A}, + {SDLK_i, button::B}, + {SDLK_o, button::X}, + {SDLK_p, button::Y}, + }; + + std::map buttons = { + // dpad + {SDL_CONTROLLER_BUTTON_DPAD_DOWN, button::DOWN}, + {SDL_CONTROLLER_BUTTON_DPAD_UP, button::UP}, + {SDL_CONTROLLER_BUTTON_DPAD_LEFT, button::LEFT}, + {SDL_CONTROLLER_BUTTON_DPAD_RIGHT, button::RIGHT}, + + // action buttons + {SDL_CONTROLLER_BUTTON_A, button::A}, + {SDL_CONTROLLER_BUTTON_B, button::B}, + {SDL_CONTROLLER_BUTTON_X, button::X}, + {SDL_CONTROLLER_BUTTON_Y, button::Y}, + }; + + enum pin { + RED = 14, GREEN = 13, BLUE = 15, // user rgb led + CS = 5, SCK = 6, MOSI = 7, // spi + VSYNC = 8, DC = 9, LCD_RESET = 4, BACKLIGHT = 12, // screen + AUDIO = 11, // audio + CHARGE_LED = 2, CHARGING = 24, BATTERY_LEVEL = 26 // battery / charging + }; + + int _sdl_find_key(int key) { + auto iter = keys.find(key); + if (iter == keys.end()) return 0; + else return iter->second; + } + + int _sdl_find_button(int button) { + auto iter = buttons.find(button); + if (iter == buttons.end()) return 0; + else return iter->second; + } + + void _sdl_resize() { + SDL_LockMutex(m_flip); + SDL_DestroyTexture(texture); + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGR565, SDL_TEXTUREACCESS_STREAMING, SCREEN->w, SCREEN->h); + SDL_UnlockMutex(m_flip); + } + + void _sdl_handle_keyboard(int key, bool state) { + if (int button = _sdl_find_key(key)) { + _sdl_input &= ~(1U << button); + _sdl_input |= state << button; + } + } + + void _sdl_handle_controller_button(int btn, bool state) { + if (int button = _sdl_find_button(btn)) { + _sdl_input &= ~(1U << button); + _sdl_input |= state << button; + } + } + + void _sdl_handle_event(SDL_Event &event) { + switch (event.type) { + case SDL_QUIT: + running = false; + break; + + case SDL_WINDOWEVENT: + if (event.window.event == SDL_WINDOWEVENT_RESIZED) { + _sdl_resize(); + } + break; + + case SDL_KEYDOWN: // fall-though + case SDL_KEYUP: + _sdl_handle_keyboard(event.key.keysym.sym, event.type == SDL_KEYDOWN); + break; + + case SDL_CONTROLLERBUTTONDOWN: + case SDL_CONTROLLERBUTTONUP: + _sdl_handle_controller_button(event.cbutton.button, event.type == SDL_CONTROLLERBUTTONDOWN); + break; + + case SDL_CONTROLLERDEVICEADDED: + SDL_GameControllerOpen(event.cdevice.which); + break; + + case SDL_CONTROLLERDEVICEREMOVED: + SDL_GameControllerClose(SDL_GameControllerFromInstanceID(event.cdevice.which)); + break; + + case SDL_RENDER_TARGETS_RESET: + std::cout << "Targets reset" << std::endl; + + case SDL_RENDER_DEVICE_RESET: + std::cout << "Device reset" << std::endl; + break; + + default: + break; + } + } + + static int _sdl_event_pump(void *ptr) { + SDL_Event event; + + while (running && SDL_WaitEvent(&event)) { + _sdl_handle_event(event); + } + + exit(0); + } + + bool pressed(uint32_t b) { + return !(_io & (1U << b)) && (_lio & (1U << b)); + } + + bool button(uint32_t b) { + return !(_io & (1U << b)); + } + + void _reset_to_dfu() { + } + + float _battery_voltage() { + return 3.3f; + } + + uint32_t time() { + auto elapsed = std::chrono::high_resolution_clock::now() - t_start; + return std::chrono::duration_cast(elapsed).count(); + } + + uint32_t time_us() { + auto elapsed = std::chrono::high_resolution_clock::now() - t_start; + return std::chrono::duration_cast(elapsed).count(); + } + + void sleep(uint32_t d) { + std::this_thread::sleep_for(std::chrono::milliseconds(d)); + } + + void sleep_us(uint32_t d) { + std::this_thread::sleep_for(std::chrono::microseconds(d)); + } + + uint32_t battery() { + // convert to 0..1 range for battery between 2.8v and 4.1v + float c = (_battery_voltage() - 2.8f) / 1.3f; + return std::max(0.0f, std::min(1.0f, c)) * 100; + } + + void _wait_vsync() {} + + bool _in_flip = false; + bool _is_flipping() { + return _in_flip; + } + + void _flip() { + SDL_LockMutex(m_flip); + SDL_Rect dest; + dest.x = 0; + dest.y = 0; + dest.w = SCREEN->w; + dest.h = SCREEN->h; + + SDL_UpdateTexture(texture, nullptr, SCREEN->data, SCREEN->w * 2); + + SDL_SetRenderTarget(renderer, nullptr); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, nullptr, (const SDL_Rect *)&dest); + + SDL_RenderPresent(renderer); + SDL_UnlockMutex(m_flip); + + t_last_flip = std::chrono::high_resolution_clock::now(); + } + + uint16_t _gamma_correct(uint8_t v) { + float gamma = 2.8; + return (uint16_t)(pow((float)(v) / 100.0f, gamma) * 65535.0f + 0.5f); + } + + void backlight(uint8_t b) { + (void)b; + } + + void _play_note(uint32_t f, uint32_t v) { + (void)f; + (void)v; + } + + void led(uint8_t r, uint8_t g, uint8_t b) { + (void)r; + (void)g; + (void)b; + } + + uint32_t _gpio_get() { + return _sdl_input; + } + + void _sdl_set_mode() { + SDL_RenderSetLogicalSize(renderer, SCREEN->w, SCREEN->h); + SDL_RenderSetIntegerScale(renderer, SDL_TRUE); + } + + void _init_hardware() { + t_start = std::chrono::high_resolution_clock::now(); + m_flip = SDL_CreateMutex(); + + std::cout << "Powered by PicoSystem SDL2 debugger runtime" << std::endl << std::endl; + if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_GAMECONTROLLER|SDL_INIT_AUDIO) < 0) { + std::cerr << "could not initialize SDL2: " << SDL_GetError() << std::endl; + return; + } + + std::cout << "Screen " << SCREEN->w << "x" << SCREEN->h << std::endl << std::endl; + + window = SDL_CreateWindow( + "SDL Debug", + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + SCREEN->w*2, SCREEN->h*2, + SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE + ); + + if (window == nullptr) { + std::cerr << "could not create window: " << SDL_GetError() << std::endl; + return; + } + SDL_SetWindowMinimumSize(window, SCREEN->w, SCREEN->h); + + renderer = SDL_CreateRenderer(window, -1, 0); + if (renderer == nullptr) { + std::cerr << "could not create renderer: " << SDL_GetError() << std::endl; + } + + _sdl_set_mode(); + + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + SDL_RenderPresent(renderer); + + _sdl_resize(); + + t_event_pump = SDL_CreateThread(_sdl_event_pump, "Events", nullptr); + } + +} \ No newline at end of file diff --git a/libraries/picosystem-unix.cpp b/libraries/picosystem-unix.cpp new file mode 100644 index 0000000..32cb1b0 --- /dev/null +++ b/libraries/picosystem-unix.cpp @@ -0,0 +1,54 @@ +#include +#include + +#include + +#include "picosystem.hpp" + + +namespace picosystem { + + stat_t stats; + + color_t _pen; + uint8_t _a = 15; + + int32_t _tx = 0, _ty = 0; + int32_t _tlh = 8, _tls = 1; + int32_t _tlw = -1; + + int32_t _camx = 0, _camy = 0; + uint32_t _io = 0, _lio = 0; + blend_func_t _bf = ALPHA; + + #ifndef DYNAMIC_BUFFER + #ifdef PIXEL_DOUBLE + color_t _fb[120 * 120] __attribute__ ((aligned (4))) = { }; + buffer_t *SCREEN = buffer(120, 120, _fb); + int32_t _cx = 0, _cy = 0, _cw = 120, _ch = 120; + #else + color_t _fb[240 * 240] __attribute__ ((aligned (4))) = { }; + buffer_t *SCREEN = buffer(240, 240, _fb); + int32_t _cx = 0, _cy = 0, _cw = 240, _ch = 240; + #endif + #else + buffer_t *SCREEN = nullptr; + int32_t _cx = 0, _cy = 0, _cw = 120, _ch = 120; + #endif + + buffer_t *_dt = SCREEN; + + #ifdef NO_SPRITESHEET + buffer_t *_ss = nullptr; + #else + buffer_t *SPRITESHEET = buffer(128, 128, (void *)_default_sprite_sheet); + buffer_t *_ss = SPRITESHEET; + #endif + + #ifdef NO_FONT + uint8_t *_font = nullptr; + #else + uint8_t *_font = (uint8_t *)&_default_font[0][0]; + #endif + +} \ No newline at end of file diff --git a/libraries/picosystem.cpp b/libraries/picosystem.cpp index 3f1d645..1e9b124 100644 --- a/libraries/picosystem.cpp +++ b/libraries/picosystem.cpp @@ -109,7 +109,6 @@ int main() { init(); uint32_t tick = 0; - uint32_t last_frame_ms = 0; uint32_t start_flip = 0; _io = _gpio_get(); diff --git a/libraries/picosystem.hpp b/libraries/picosystem.hpp index d26aaa1..0a32ae0 100644 --- a/libraries/picosystem.hpp +++ b/libraries/picosystem.hpp @@ -8,7 +8,6 @@ #include #include -#include "pico/stdlib.h" void init(); void update(uint32_t tick); diff --git a/libraries/utility.cpp b/libraries/utility.cpp index 7093140..a60da41 100644 --- a/libraries/utility.cpp +++ b/libraries/utility.cpp @@ -15,7 +15,7 @@ namespace picosystem { std::string str(int32_t v) { static char b[32]; - snprintf(b, 32, "%ld", v); + snprintf(b, 32, "%d", v); return b; } @@ -25,7 +25,7 @@ namespace picosystem { std::string str(uint32_t v) { static char b[32]; - snprintf(b, 32, "%lu", v); + snprintf(b, 32, "%u", v); return b; } diff --git a/micropython/modules/micropython-unix.cmake b/micropython/modules/micropython-unix.cmake new file mode 100644 index 0000000..d48e660 --- /dev/null +++ b/micropython/modules/micropython-unix.cmake @@ -0,0 +1,6 @@ +include_directories(${CMAKE_CURRENT_LIST_DIR}/../../) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../") + +include(picosystem/micropython-unix) diff --git a/micropython/modules/picosystem/buffer.cpp b/micropython/modules/picosystem/buffer.cpp index 00c582c..2cd2123 100644 --- a/micropython/modules/picosystem/buffer.cpp +++ b/micropython/modules/picosystem/buffer.cpp @@ -1,7 +1,3 @@ -#include "hardware/spi.h" -#include "hardware/sync.h" -#include "pico/binary_info.h" - #include "libraries/picosystem.hpp" #define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o)) diff --git a/micropython/modules/picosystem/hardware.cpp b/micropython/modules/picosystem/hardware.cpp index b9e1c33..da1e2a8 100644 --- a/micropython/modules/picosystem/hardware.cpp +++ b/micropython/modules/picosystem/hardware.cpp @@ -1,7 +1,3 @@ -#include "hardware/spi.h" -#include "hardware/sync.h" -#include "pico/binary_info.h" - #include "libraries/picosystem.hpp" #define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o)) diff --git a/micropython/modules/picosystem/micropython-unix.cmake b/micropython/modules/picosystem/micropython-unix.cmake new file mode 100644 index 0000000..d80e8a2 --- /dev/null +++ b/micropython/modules/picosystem/micropython-unix.cmake @@ -0,0 +1,72 @@ +set(MOD_NAME picosystem) +string(TOUPPER ${MOD_NAME} MOD_NAME_UPPER) +add_library(usermod_${MOD_NAME} INTERFACE) + +target_sources(usermod_${MOD_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/picosystem.c + ${CMAKE_CURRENT_LIST_DIR}/picosystem-unix.cpp + ${CMAKE_CURRENT_LIST_DIR}/voice.cpp + ${CMAKE_CURRENT_LIST_DIR}/buffer.cpp + ${CMAKE_CURRENT_LIST_DIR}/state.cpp + ${CMAKE_CURRENT_LIST_DIR}/primitives.cpp + ${CMAKE_CURRENT_LIST_DIR}/text.cpp + ${CMAKE_CURRENT_LIST_DIR}/utility.cpp + ${CMAKE_CURRENT_LIST_DIR}/hardware.cpp + ${CMAKE_CURRENT_LIST_DIR}/stats.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/picosystem-unix.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/audio.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/blend.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/state.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/primitives.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/hardware-unix.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/text.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/utility.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/assets.cpp +) + +target_include_directories(usermod_${MOD_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR} +) + +target_compile_definitions(usermod_${MOD_NAME} INTERFACE + MODULE_${MOD_NAME_UPPER}_ENABLED=1 + PIXEL_DOUBLE=1 +) + +target_link_libraries(usermod INTERFACE usermod_${MOD_NAME}) + +set_source_files_properties( + ${CMAKE_CURRENT_LIST_DIR}/picosystem.c + PROPERTIES COMPILE_FLAGS + "-Wno-discarded-qualifiers -Wno-implicit-int" +) + +set_source_files_properties( + ${CMAKE_CURRENT_LIST_DIR}/picosystem.cpp + PROPERTIES COMPILE_FLAGS + "-DPIXEL_DOUBLE=1" +) + +set_source_files_properties( + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/blend.cpp + PROPERTIES COMPILE_FLAGS + "-fpermissive -Wno-error" +) + +set_source_files_properties( + ${CMAKE_CURRENT_LIST_DIR}/state.cpp + PROPERTIES COMPILE_FLAGS + "-Wno-error=format=" +) + +# TODO fix sign compare issues in codebase +set_source_files_properties( + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/primitives.cpp + PROPERTIES COMPILE_FLAGS + "-Wno-error=sign-compare" +) +set_source_files_properties( + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/text.cpp + PROPERTIES COMPILE_FLAGS + "-Wno-error=sign-compare" +) \ No newline at end of file diff --git a/micropython/modules/picosystem/picosystem-unix.cpp b/micropython/modules/picosystem/picosystem-unix.cpp new file mode 100644 index 0000000..fe64bab --- /dev/null +++ b/micropython/modules/picosystem/picosystem-unix.cpp @@ -0,0 +1,169 @@ +#include "libraries/picosystem.hpp" + +#define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o)) + +using namespace picosystem; + +extern "C" { +#include "picosystem.h" +#include "math.h" +#include "cstring" +#include "py/gc.h" + + +bool running = true; +uint32_t tick = 0; + +mp_obj_t update_callback_obj = mp_const_none; +mp_obj_t draw_callback_obj = mp_const_none; + +typedef struct _PicosystemBuffer_obj_t { + mp_obj_base_t base; + buffer_t *buffer; +} _PicosystemBuffer_obj_t; + +typedef struct _PicosystemVoice_obj_t { + mp_obj_base_t base; + voice_t *voice; +} _PicosystemVoice_obj_t; + +mp_obj_t pimoroni_mp_load_global(qstr qst) { + mp_map_elem_t *elem = mp_map_lookup(&mp_globals_get()->map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP); + if (elem == NULL) { + return mp_const_none; + } + return elem->value; +} + +static inline void mp_hal_delay_us(mp_uint_t us) { + usleep(us); +} + +mp_obj_t picosystem_init() { + + _init_hardware(); + + target(SCREEN); + + update_callback_obj = mp_const_none; + draw_callback_obj = mp_const_none; + + // setup lut for fast sin/cos functions + for(uint32_t i = 0; i < 256; i++) { + _fsin_lut[i] = sin((_PI * 2.0f) * (float(i) / 256.0f)); + } + + return mp_const_none; +} + +mp_obj_t picosystem_start() { + if(update_callback_obj == mp_const_none) { + update_callback_obj = pimoroni_mp_load_global(qstr_from_str("update")); + if(update_callback_obj == mp_const_none) { + //TODO switch out this URL for the final one + mp_raise_msg(&mp_type_NameError, "a function named 'update(ticks)' is not defined. Check out https://github.com/pimoroni/picosystem/blob/main/micropython/README.md for instructions"); + } + } + + if(draw_callback_obj == mp_const_none) { + draw_callback_obj = mp_load_global(qstr_from_str("draw")); + if(draw_callback_obj == mp_const_none) { + //TODO switch out this URL for the final one + mp_raise_msg(&mp_type_NameError, "a function named 'draw()' is not defined. Check out https://github.com/pimoroni/picosystem/blob/main/micropython/README.md for instructions"); + } + } + + _io = 0; + running = true; + + while(running) { + uint32_t start_tick_us = time_us(); + + // store previous io state and get new io state + _lio = _io; + _io = 0; + + // call users update() function + uint32_t start_update_us = time_us(); + mp_call_function_1(update_callback_obj, mp_obj_new_int(tick++)); + stats.update_us = time_us() - start_update_us; + + // if we're currently transferring the the framebuffer to the screen then + // wait until that is complete before allowing the user to do their drawing + uint32_t wait_us = 0; + uint32_t start_wait_flip_us = time_us(); + wait_us += time_us() - start_wait_flip_us; + + // call user render function to draw world + uint32_t start_draw_us = time_us(); + mp_call_function_1(draw_callback_obj, mp_obj_new_int(tick)); + stats.draw_us = time_us() - start_draw_us; + + // wait for the screen to vsync before triggering flip + // to ensure no tearing + uint32_t start_wait_vsync_us = time_us(); + wait_us += time_us() - start_wait_vsync_us; + + // flip the framebuffer to the screen + _flip(); + + // force a per-frame gc.collect() to avoid stutter + // cost on the order of microseconds. + gc_collect(); + + stats.tick_us = time_us() - start_tick_us; + + // calculate fps and round to nearest value (instead of truncating/floor) + stats.fps = (1000000 - 1) / stats.tick_us + 1; + + if(stats.fps > 40) { + // if fps is high enough then we definitely didn't miss vsync + stats.idle = (wait_us * 100) / stats.tick_us; + }else{ + // if we missed vsync then we overran the frame time and hence had + // no idle time + stats.idle = 0; + } + + MICROPY_EVENT_POLL_HOOK + + } + + update_callback_obj = mp_const_none; + draw_callback_obj = mp_const_none; + tick = 0; + + return mp_const_none; +} + +/* quit() - Break out of the main loop started by start() */ +mp_obj_t picosystem_quit(){ + running = false; + return mp_const_none; +} + +/* flip() - Flip the buffer, use this from the repl! (or, your own main loop?) */ +mp_obj_t picosystem_flip() { + _flip(); + return mp_const_none; +} + +/* _logo() - Render the 1-bit logo to the center of the screen in the pen colour */ +mp_obj_t picosystem_logo() { + const uint8_t *s = _picosystem_logo; + + for(int y = 35; y < 85; y++) { + for(int x = 19; x < 101; x+=8) { + for(int bit = 0; bit < 8; bit++) { + if(*s & (0b10000000 >> bit)) { + pixel(x + bit, y); + } + } + s++; + } + } + + return mp_const_none; +} + +} diff --git a/micropython/modules/picosystem/primitives.cpp b/micropython/modules/picosystem/primitives.cpp index efd0304..ad97b25 100644 --- a/micropython/modules/picosystem/primitives.cpp +++ b/micropython/modules/picosystem/primitives.cpp @@ -1,7 +1,3 @@ -#include "hardware/spi.h" -#include "hardware/sync.h" -#include "pico/binary_info.h" - #include "libraries/picosystem.hpp" #define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o)) diff --git a/micropython/modules/picosystem/state.cpp b/micropython/modules/picosystem/state.cpp index 338d2bf..f5ea43f 100644 --- a/micropython/modules/picosystem/state.cpp +++ b/micropython/modules/picosystem/state.cpp @@ -1,7 +1,3 @@ -#include "hardware/spi.h" -#include "hardware/sync.h" -#include "pico/binary_info.h" - #include "libraries/picosystem.hpp" #define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o)) diff --git a/micropython/modules/picosystem/stats.cpp b/micropython/modules/picosystem/stats.cpp index 97e3e45..b4e5628 100644 --- a/micropython/modules/picosystem/stats.cpp +++ b/micropython/modules/picosystem/stats.cpp @@ -1,7 +1,3 @@ -#include "hardware/spi.h" -#include "hardware/sync.h" -#include "pico/binary_info.h" - #include "libraries/picosystem.hpp" #define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o)) diff --git a/micropython/modules/picosystem/utility.cpp b/micropython/modules/picosystem/utility.cpp index f199d0f..783c90a 100644 --- a/micropython/modules/picosystem/utility.cpp +++ b/micropython/modules/picosystem/utility.cpp @@ -1,7 +1,3 @@ -#include "hardware/spi.h" -#include "hardware/sync.h" -#include "pico/binary_info.h" - #include "libraries/picosystem.hpp" #define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o)) diff --git a/micropython/modules/picosystem/voice.cpp b/micropython/modules/picosystem/voice.cpp index 57c2ba2..337c3d0 100644 --- a/micropython/modules/picosystem/voice.cpp +++ b/micropython/modules/picosystem/voice.cpp @@ -1,7 +1,3 @@ -#include "hardware/spi.h" -#include "hardware/sync.h" -#include "pico/binary_info.h" - #include "libraries/picosystem.hpp" #define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o)) From d211a297bd63b173417871f0e0baa1775fd0949d Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Sun, 31 Oct 2021 21:28:55 +0000 Subject: [PATCH 2/4] MicroPython SDL: Fix rendering and IO, init when start'ed --- libraries/hardware-unix.cpp | 15 +++++++++--- libraries/picosystem.hpp | 1 + .../modules/picosystem/picosystem-unix.cpp | 24 +++++++++---------- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/libraries/hardware-unix.cpp b/libraries/hardware-unix.cpp index c1c14c4..63a1b83 100644 --- a/libraries/hardware-unix.cpp +++ b/libraries/hardware-unix.cpp @@ -14,6 +14,7 @@ SDL_Renderer *renderer = nullptr; SDL_Texture *texture = nullptr; SDL_mutex *m_flip = nullptr; static bool running = true; +static bool _done_init = false; std::chrono::time_point t_start; std::chrono::time_point t_last_flip; @@ -84,7 +85,7 @@ namespace picosystem { void _sdl_resize() { SDL_LockMutex(m_flip); SDL_DestroyTexture(texture); - texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGR565, SDL_TEXTUREACCESS_STREAMING, SCREEN->w, SCREEN->h); + texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB4444, SDL_TEXTUREACCESS_STREAMING, SCREEN->w, SCREEN->h); SDL_UnlockMutex(m_flip); } @@ -207,8 +208,13 @@ namespace picosystem { dest.y = 0; dest.w = SCREEN->w; dest.h = SCREEN->h; + + color_t data[SCREEN->w * SCREEN->h]; + for (auto i = 0; i < SCREEN->w * SCREEN->h; i++) { + data[i] = (SCREEN->data[i] >> 8) | ((SCREEN->data[i] & 0xff) << 8); + } - SDL_UpdateTexture(texture, nullptr, SCREEN->data, SCREEN->w * 2); + SDL_UpdateTexture(texture, nullptr, data, SCREEN->w * 2); SDL_SetRenderTarget(renderer, nullptr); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); @@ -242,7 +248,7 @@ namespace picosystem { } uint32_t _gpio_get() { - return _sdl_input; + return ~_sdl_input; } void _sdl_set_mode() { @@ -251,6 +257,9 @@ namespace picosystem { } void _init_hardware() { + if(_done_init) return; + _done_init = true; + t_start = std::chrono::high_resolution_clock::now(); m_flip = SDL_CreateMutex(); diff --git a/libraries/picosystem.hpp b/libraries/picosystem.hpp index 0a32ae0..b8369c1 100644 --- a/libraries/picosystem.hpp +++ b/libraries/picosystem.hpp @@ -192,6 +192,7 @@ namespace picosystem { uint32_t time(); uint32_t time_us(); void sleep(uint32_t d); + void sleep_us(uint32_t d); bool intersects( int32_t x, int32_t y, int32_t w, int32_t h, int32_t cx, int32_t cy, int32_t cw, int32_t ch); diff --git a/micropython/modules/picosystem/picosystem-unix.cpp b/micropython/modules/picosystem/picosystem-unix.cpp index fe64bab..4fcd1fc 100644 --- a/micropython/modules/picosystem/picosystem-unix.cpp +++ b/micropython/modules/picosystem/picosystem-unix.cpp @@ -57,23 +57,21 @@ mp_obj_t picosystem_init() { } mp_obj_t picosystem_start() { + (void)picosystem_init(); + + update_callback_obj = pimoroni_mp_load_global(qstr_from_str("update")); if(update_callback_obj == mp_const_none) { - update_callback_obj = pimoroni_mp_load_global(qstr_from_str("update")); - if(update_callback_obj == mp_const_none) { - //TODO switch out this URL for the final one - mp_raise_msg(&mp_type_NameError, "a function named 'update(ticks)' is not defined. Check out https://github.com/pimoroni/picosystem/blob/main/micropython/README.md for instructions"); - } + //TODO switch out this URL for the final one + mp_raise_msg(&mp_type_NameError, "a function named 'update(ticks)' is not defined. Check out https://github.com/pimoroni/picosystem/blob/main/micropython/README.md for instructions"); } + draw_callback_obj = mp_load_global(qstr_from_str("draw")); if(draw_callback_obj == mp_const_none) { - draw_callback_obj = mp_load_global(qstr_from_str("draw")); - if(draw_callback_obj == mp_const_none) { - //TODO switch out this URL for the final one - mp_raise_msg(&mp_type_NameError, "a function named 'draw()' is not defined. Check out https://github.com/pimoroni/picosystem/blob/main/micropython/README.md for instructions"); - } + //TODO switch out this URL for the final one + mp_raise_msg(&mp_type_NameError, "a function named 'draw()' is not defined. Check out https://github.com/pimoroni/picosystem/blob/main/micropython/README.md for instructions"); } - _io = 0; + _io = _gpio_get(); running = true; while(running) { @@ -81,7 +79,7 @@ mp_obj_t picosystem_start() { // store previous io state and get new io state _lio = _io; - _io = 0; + _io = _gpio_get(); // call users update() function uint32_t start_update_us = time_us(); @@ -125,6 +123,8 @@ mp_obj_t picosystem_start() { stats.idle = 0; } + sleep_us(25000 - stats.tick_us); + MICROPY_EVENT_POLL_HOOK } From ec91bb998554d74659733cdd3e4e40dd051d4cef Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Sun, 31 Oct 2021 21:44:33 +0000 Subject: [PATCH 3/4] MicroPython SDL: Attempt CI build. --- .github/workflows/micropython-unix.yml | 93 ++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 .github/workflows/micropython-unix.yml diff --git a/.github/workflows/micropython-unix.yml b/.github/workflows/micropython-unix.yml new file mode 100644 index 0000000..7a246c7 --- /dev/null +++ b/.github/workflows/micropython-unix.yml @@ -0,0 +1,93 @@ +name: MicroPython SDL + +on: + push: + pull_request: + release: + types: [created] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + MICROPYTHON_VERSION: v1.17 + BUILD_TYPE: Release + MICROPY_VARIANT: standard + +jobs: + build: + name: ${{matrix.name}} + strategy: + matrix: + include: + - os: ubuntu-20.04 + name: MicroPython Build (Unix, SDL) (Linux) + cache-key: linux + apt-packages: libsdl2-dev libreadline-dev + + runs-on: ${{matrix.os}} + + steps: + # Check out MicroPython + - name: Checkout MicroPython + uses: actions/checkout@v2 + with: + repository: pimoroni/micropython + ref: experimental/picosystem-sdl-unix #${{env.MICROPYTHON_VERSION}} + submodules: false # MicroPython submodules are hideously broken + path: micropython + + - uses: actions/checkout@v2 + with: + submodules: true + path: picosystem-${{ github.sha }} + + # Linux deps + - name: Install deps + if: runner.os == 'Linux' + run: | + sudo apt update && sudo apt install ${{matrix.apt-packages}} + + - name: Fetch base MicroPython submodules + shell: bash + working-directory: micropython + run: git submodule update --init + + - name: Build mpy-cross + shell: bash + working-directory: micropython/mpy-cross + run: make + + - name: Make MicroPython Build Dir + shell: bash + working-directory: micropython/ports/unix + run: mkdir build + + - name: Build MicroPython + shell: bash + working-directory: micropython/ports/unix/build + run: | + cmake .. -DUSER_C_MODULES=../../../picosystem-${GITHUB_SHA}/micropython/modules/micropython-unix.cmake -DSDL2_DIR=/usr/lib/x86_64-linux-gnu/cmake/SDL2/ + make -j2 + + - name: Rename .uf2 for artifact & release + shell: bash + working-directory: micropython/ports/unix/build + run: | + cp micropython ${{github.event.repository.name}}-${{github.sha}}-micropython-unix-sdl-${{env.MICROPYTHON_VERSION}} + cp micropython ${{github.event.repository.name}}-${{github.event.release.tag_name}}-micropython-unix-sdl-${{env.MICROPYTHON_VERSION}} + + - name: Store .uf2 as artifact + uses: actions/upload-artifact@v2 + with: + name: ${{github.event.repository.name}}-${{github.sha}}-micropython-unix-sdl-${{env.MICROPYTHON_VERSION}} + path: micropython/ports/unix/build/${{github.event.repository.name}}-${{github.sha}}-micropython-unix-sdl-${{env.MICROPYTHON_VERSION}} + + - name: Upload .uf2 + if: github.event_name == 'release' + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + with: + asset_path: micropython/ports/unix/build/micropython + upload_url: ${{github.event.release.upload_url}} + asset_name: ${{github.event.repository.name}}-${{github.event.release.tag_name}}-micropython-unix-sdl-${{env.MICROPYTHON_VERSION}} + asset_content_type: application/octet-stream From 8a1080905b5aa468fee256f6ab09e5998fe413a7 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 1 Nov 2021 09:03:37 +0000 Subject: [PATCH 4/4] MicroPython: Squash additional Werrors. --- micropython/modules/picosystem/micropython.cmake | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/micropython/modules/picosystem/micropython.cmake b/micropython/modules/picosystem/micropython.cmake index 8431a8d..b801f25 100644 --- a/micropython/modules/picosystem/micropython.cmake +++ b/micropython/modules/picosystem/micropython.cmake @@ -50,7 +50,9 @@ set_source_files_properties( "-DPIXEL_DOUBLE=1" ) -# TODO fix sign compare issues in codebase +# Squash warnings in codebase. +# MicroPython compiles with -Werror +# TODO fix these! set_source_files_properties( ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/primitives.cpp PROPERTIES COMPILE_FLAGS @@ -60,4 +62,9 @@ set_source_files_properties( ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/text.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=sign-compare" +) +set_source_files_properties( + ${CMAKE_CURRENT_LIST_DIR}/../../../libraries/utility.cpp + PROPERTIES COMPILE_FLAGS + "-Wno-error=format=" ) \ No newline at end of file