From 18f544c888d96945b32a69332d4b156054996733 Mon Sep 17 00:00:00 2001 From: Paul Guyot Date: Sat, 1 Jul 2023 21:35:40 +0200 Subject: [PATCH] Update for atomvm master --- .github/workflows/build.yml | 3 +- .github/workflows/doc.yaml | 38 +++ .gitignore | 2 + CMakeLists.txt | 8 + Kconfig | 36 +++ README.md | 11 + nifs/atomvm_m5.cc | 247 ++++-------------- nifs/atomvm_m5_display.cc | 449 +++++++++++++++++++++++++++++++++ nifs/atomvm_m5_i2c.cc | 100 ++++++++ nifs/atomvm_m5_power.cc | 179 +++++++++++++ nifs/atomvm_m5_power_axp192.cc | 96 +++++++ nifs/atomvm_m5_rtc.cc | 193 ++++++++++++++ nifs/atomvm_m5_speaker.cc | 84 ++++++ nifs/include/atomvm_m5.h | 3 - src/m5_speaker.erl | 20 ++ 15 files changed, 1273 insertions(+), 196 deletions(-) create mode 100644 .github/workflows/doc.yaml create mode 100644 nifs/atomvm_m5_display.cc create mode 100644 nifs/atomvm_m5_i2c.cc create mode 100644 nifs/atomvm_m5_power.cc create mode 100644 nifs/atomvm_m5_power_axp192.cc create mode 100644 nifs/atomvm_m5_rtc.cc create mode 100644 nifs/atomvm_m5_speaker.cc create mode 100644 src/m5_speaker.erl diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d3b1ef3..7c06824 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,8 +13,7 @@ jobs: - name: Checkout AtomVM uses: actions/checkout@v3 with: - repository: pguyot/AtomVM - ref: w41/smp-support + repository: atomvm/AtomVM path: AtomVM - name: Checkout M5Unified uses: actions/checkout@v3 diff --git a/.github/workflows/doc.yaml b/.github/workflows/doc.yaml new file mode 100644 index 0000000..ac8e3ee --- /dev/null +++ b/.github/workflows/doc.yaml @@ -0,0 +1,38 @@ +name: Build and deploy documentation + +on: + push: + branches: ["main"] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + doc: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + container: erlang:26 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup Pages + uses: actions/configure-pages@v3 + - name: Build edoc + run: | + rebar3 edoc + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + path: doc + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v2 diff --git a/.gitignore b/.gitignore index c6127b3..297fd37 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,5 @@ modules.order Module.symvers Mkfile.old dkms.conf + +.DS_Store diff --git a/CMakeLists.txt b/CMakeLists.txt index 96e45c6..b2078e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,12 @@ set(ATOMVM_M5_COMPONENT_SRCS "nifs/atomvm_m5.cc" + "nifs/atomvm_m5_display.cc" + "nifs/atomvm_m5_i2c.cc" + "nifs/atomvm_m5_power.cc" + "nifs/atomvm_m5_power_axp192.cc" + "nifs/atomvm_m5_rtc.cc" + "nifs/atomvm_m5_speaker.cc" ) idf_component_register( @@ -10,6 +16,8 @@ idf_component_register( PRIV_REQUIRES "libatomvm" "avm_sys" "main" "M5Unified" ) +target_compile_features(${COMPONENT_LIB} INTERFACE cxx_std_14) + idf_build_set_property( LINK_OPTIONS "-Wl,--whole-archive ${CMAKE_CURRENT_BINARY_DIR}/lib${COMPONENT_NAME}.a -Wl,--no-whole-archive" APPEND diff --git a/Kconfig b/Kconfig index a21b923..2f4d78e 100644 --- a/Kconfig +++ b/Kconfig @@ -6,4 +6,40 @@ config AVM_M5_ENABLE help Use this parameter to enable or disable the AtomVM M5 driver. +config AVM_M5_DISPLAY_ENABLE + bool "Enable AtomVM M5 driver display API" + default y + help + Use this parameter to enable or disable the AtomVM M5 driver display API. + +config AVM_M5_POWER_ENABLE + bool "Enable AtomVM M5 driver power API" + default y + help + Use this parameter to enable or disable the AtomVM M5 driver power API. + +config AVM_M5_POWER_AXP192_ENABLE + bool "Enable AtomVM M5 driver power AXP192 API" + default y + help + Use this parameter to enable or disable the AtomVM M5 driver power AXP192 API. + +config AVM_M5_I2C_ENABLE + bool "Enable AtomVM M5 driver I2C API" + default y + help + Use this parameter to enable or disable the AtomVM M5 driver I2C API. + +config AVM_M5_RTC_ENABLE + bool "Enable AtomVM M5 driver RTC API" + default y + help + Use this parameter to enable or disable the AtomVM M5 driver RTC API. + +config AVM_M5_SPEAKER_ENABLE + bool "Enable AtomVM M5 driver speaker API" + default y + help + Use this parameter to enable or disable the AtomVM M5 driver speaker API. + endmenu diff --git a/README.md b/README.md index fa63423..1e3d22e 100644 --- a/README.md +++ b/README.md @@ -13,3 +13,14 @@ This project is a port of M5Unified for the AtomVM platform. ## Usage +The following modules are currently defined: + +``` +m5 +m5_display +m5_i2c +m5_power +m5_power_axp192 +m5_rtc +m5_speaker +``` diff --git a/nifs/atomvm_m5.cc b/nifs/atomvm_m5.cc index 4681283..5d9daa4 100644 --- a/nifs/atomvm_m5.cc +++ b/nifs/atomvm_m5.cc @@ -5,45 +5,54 @@ #include -#include +#include "atomvm_m5.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + #include #include +#include + +#include + +#pragma GCC diagnostic pop + #include #include #include #include #include #include -#include - -#include "esp32_sys.h" - +#include //#define ENABLE_TRACE -#include "trace.h" +#include -#include +#define MODULE_PREFIX "m5:" + +#define MAKE_ATOM(ctx, len, str) globalcontext_make_atom(ctx->global, ATOM_STR(len, str)) +//#define MAKE_ATOM(ctx, len, str) context_make_atom(ctx, ATOM_STR(len, str)) static term nif_begin(Context *ctx, int argc, term argv[]) { UNUSED(argc); + VALIDATE_VALUE(argv[0], term_is_list); auto cfg = M5.config(); + term opts = argv[0]; #if defined ( ARDUINO ) - cfg.serial_baudrate = 115200; // default=115200. if "Serial" is not needed, set it to 0. + cfg.serial_baudrate = term_to_int32(interop_proplist_get_value_default(opts, MAKE_ATOM(ctx, "\xF", "serial_baudrate"), term_from_int(115200))); #endif - cfg.clear_display = true; // default=true. clear the screen when begin. - cfg.output_power = true; // default=true. use external port 5V output. - cfg.internal_imu = true; // default=true. use internal IMU. - cfg.internal_rtc = true; // default=true. use internal RTC. - cfg.internal_spk = true; // default=true. use internal speaker. - cfg.internal_mic = true; // default=true. use internal microphone. - cfg.external_imu = true; // default=false. use Unit Accel & Gyro. - cfg.external_rtc = true; // default=false. use Unit RTC. - cfg.external_spk = false; // default=false. use SPK_HAT / ATOMIC_SPK - //cfg.external_spk_detail.omit_atomic_spk = true; // omit ATOMIC SPK - //cfg.external_spk_detail.omit_spk_hat = true; // omit SPK HAT - cfg.led_brightness = 64; // default= 0. system LED brightness (0=off / 255=max) (※ not NeoPixel) - + cfg.clear_display = interop_proplist_get_value_default(opts, MAKE_ATOM(ctx, "\xD", "clear_display"), TRUE_ATOM) == TRUE_ATOM; + cfg.output_power = interop_proplist_get_value_default(opts, MAKE_ATOM(ctx, "\xC", "output_power"), TRUE_ATOM) == TRUE_ATOM; + cfg.internal_imu = interop_proplist_get_value_default(opts, MAKE_ATOM(ctx, "\xC", "internal_imu"), TRUE_ATOM) == TRUE_ATOM; + cfg.internal_rtc = interop_proplist_get_value_default(opts, MAKE_ATOM(ctx, "\xC", "internal_rtc"), TRUE_ATOM) == TRUE_ATOM; + cfg.internal_spk = interop_proplist_get_value_default(opts, MAKE_ATOM(ctx, "\xC", "internal_spk"), TRUE_ATOM) == TRUE_ATOM; + cfg.internal_mic = interop_proplist_get_value_default(opts, MAKE_ATOM(ctx, "\xC", "internal_mic"), TRUE_ATOM) == TRUE_ATOM; + cfg.external_imu = interop_proplist_get_value_default(opts, MAKE_ATOM(ctx, "\xC", "external_imu"), FALSE_ATOM) == TRUE_ATOM; + cfg.external_rtc = interop_proplist_get_value_default(opts, MAKE_ATOM(ctx, "\xC", "external_rtc"), FALSE_ATOM) == TRUE_ATOM; + cfg.external_spk = interop_proplist_get_value_default(opts, MAKE_ATOM(ctx, "\xC", "external_spk"), FALSE_ATOM) == TRUE_ATOM; + cfg.led_brightness = term_to_int32(interop_proplist_get_value_default(opts, MAKE_ATOM(ctx, "\xF", "led_brightness"), term_from_int(64))); M5.begin(cfg); @@ -58,36 +67,36 @@ static term nif_get_board(Context *ctx, int argc, term argv[]) { #if defined (CONFIG_IDF_TARGET_ESP32C3) case m5::board_t::board_M5StampC3: - return globalcontext_make_atom(ctx, ATOM_STR("\x8", "stamp_c3")); + return MAKE_ATOM(ctx, "\x8", "stamp_c3"); case m5::board_t::board_M5StampC3U: - return globalcontext_make_atom(ctx->global, ATOM_STR("\x9", "stamp_c3u")); + return MAKE_ATOM(ctx, "\x9", "stamp_c3u"); #else case m5::board_t::board_M5Stack: - return globalcontext_make_atom(ctx->global, ATOM_STR("\x5", "stack")); + return MAKE_ATOM(ctx, "\x5", "stack"); case m5::board_t::board_M5StackCore2: - return globalcontext_make_atom(ctx->global, ATOM_STR("\xB", "stack_core2")); + return MAKE_ATOM(ctx, "\xB", "stack_core2"); case m5::board_t::board_M5StickC: - return globalcontext_make_atom(ctx->global, ATOM_STR("\x7", "stick_c")); + return MAKE_ATOM(ctx, "\x7", "stick_c"); case m5::board_t::board_M5StickCPlus: - return globalcontext_make_atom(ctx->global, ATOM_STR("\xB", "stick_cplus")); + return MAKE_ATOM(ctx, "\xB", "stick_cplus"); case m5::board_t::board_M5StackCoreInk: - return globalcontext_make_atom(ctx->global, ATOM_STR("\x8", "core_ink")); + return MAKE_ATOM(ctx, "\x8", "core_ink"); case m5::board_t::board_M5Paper: - return globalcontext_make_atom(ctx->global, ATOM_STR("\x5", "paper")); + return MAKE_ATOM(ctx, "\x5", "paper"); case m5::board_t::board_M5Tough: - return globalcontext_make_atom(ctx->global, ATOM_STR("\x5", "tough")); + return MAKE_ATOM(ctx, "\x5", "tough"); case m5::board_t::board_M5Station: - return globalcontext_make_atom(ctx->global, ATOM_STR("\x7", "station")); + return MAKE_ATOM(ctx, "\x7", "station"); case m5::board_t::board_M5Atom: - return globalcontext_make_atom(ctx->global, ATOM_STR("\x4", "atom")); + return MAKE_ATOM(ctx, "\x4", "atom"); case m5::board_t::board_M5AtomPsram: - return globalcontext_make_atom(ctx->global, ATOM_STR("\xA", "atom_psram")); + return MAKE_ATOM(ctx, "\xA", "atom_psram"); case m5::board_t::board_M5AtomU: - return globalcontext_make_atom(ctx->global, ATOM_STR("\x6", "atom_u")); + return MAKE_ATOM(ctx, "\x6", "atom_u"); case m5::board_t::board_M5TimerCam: - return globalcontext_make_atom(ctx->global, ATOM_STR("\xC", "timer_camera")); + return MAKE_ATOM(ctx, "\xC", "timer_camera"); case m5::board_t::board_M5StampPico: - return globalcontext_make_atom(ctx->global, ATOM_STR("\xA", "stamp_pico")); + return MAKE_ATOM(ctx, "\xA", "stamp_pico"); #endif default: return UNDEFINED_ATOM; @@ -103,174 +112,30 @@ static term nif_update(Context *ctx, int argc, term argv[]) return OK_ATOM; } -static term nif_display_set_epd_mode(Context *ctx, int argc, term argv[]) -{ - UNUSED(argc); - - M5.Display.setEpdMode(epd_mode_t::epd_fastest); // fastest but very-low quality. - return OK_ATOM; -} - -static term nif_display_height(Context *ctx, int argc, term argv[]) -{ - UNUSED(argc); - - return term_from_int32(M5.Display.height()); -} - -static term nif_display_width(Context *ctx, int argc, term argv[]) -{ - UNUSED(argc); - - return term_from_int32(M5.Display.width()); -} - -static term nif_display_get_rotation(Context *ctx, int argc, term argv[]) -{ - UNUSED(argc); - - return term_from_int32(M5.Display.getRotation()); -} - -static term nif_display_set_rotation(Context *ctx, int argc, term argv[]) -{ - UNUSED(argc); - VALIDATE_VALUE(argv[0], term_is_integer); - - M5.Display.setRotation(term_to_int32(argv[0])); - return OK_ATOM; -} - -static term nif_display_set_text_size(Context *ctx, int argc, term argv[]) -{ - UNUSED(argc); - VALIDATE_VALUE(argv[0], term_is_integer); - - M5.Display.setTextSize(term_to_int32(argv[0])); - return OK_ATOM; -} - -static term nif_display_start_write(Context *ctx, int argc, term argv[]) -{ - UNUSED(argc); - UNUSED(argv); - - M5.Display.startWrite(); - - return OK_ATOM; -} - -static term nif_display_end_write(Context *ctx, int argc, term argv[]) -{ - UNUSED(argc); - UNUSED(argv); - - M5.Display.endWrite(); - - return OK_ATOM; -} - -static term nif_display_fill_rect(Context *ctx, int argc, term argv[]) -{ - UNUSED(argc); - VALIDATE_VALUE(argv[0], term_is_integer); - VALIDATE_VALUE(argv[1], term_is_integer); - VALIDATE_VALUE(argv[2], term_is_integer); - VALIDATE_VALUE(argv[3], term_is_integer); - VALIDATE_VALUE(argv[4], term_is_integer); - - M5.Display.fillRect( - term_to_int32(argv[0]), - term_to_int32(argv[1]), - term_to_int32(argv[2]), - term_to_int32(argv[3]), - term_to_int32(argv[4]) - ); - - return OK_ATOM; -} - -static term nif_display_print(Context *ctx, int argc, term argv[]) -{ - UNUSED(argc); - VALIDATE_VALUE(argv[0], term_is_binary); - unsigned long binLen = term_binary_size(argv[0]); - const char* bin = term_binary_data(argv[0]); - size_t r = M5.Display.write(bin, binLen); - return term_from_int(r); -} - -static term nif_display_println(Context *ctx, int argc, term argv[]) -{ - size_t r = 0; - if (argc == 1) { - VALIDATE_VALUE(argv[0], term_is_binary); - unsigned long binLen = term_binary_size(argv[0]); - const char* bin = term_binary_data(argv[0]); - r = M5.Display.write(bin, binLen); - } - r += M5.Display.println(); - return term_from_int(r); -} - -static term nif_power_timer_sleep(Context *ctx, int argc, term argv[]) -{ - UNUSED(argc); - VALIDATE_VALUE(argv[0], term_is_integer); - M5.Power.timerSleep(term_to_int32(argv[0])); - return OK_ATOM; -} - -static term nif_get_battery_level(Context *ctx, int argc, term argv[]) -{ - UNUSED(argc); - UNUSED(argv); - int32_t level = M5.Power.getBatteryLevel(); - return term_from_int(level); -} - -constexpr std::array, 17> M5_NIFS = {{ - {"m5:begin_/1", { { NIFFunctionType }, nif_begin }}, - {"m5:get_board/0", { { NIFFunctionType }, nif_get_board }}, - {"m5:update/0", { { NIFFunctionType }, nif_update }}, - - {"m5_display:set_epd_mode/1", { { NIFFunctionType }, nif_display_set_epd_mode }}, - {"m5_display:height/0", { { NIFFunctionType }, nif_display_height }}, - {"m5_display:width/0", { { NIFFunctionType }, nif_display_width }}, - {"m5_display:set_rotation/1", { { NIFFunctionType }, nif_display_set_rotation }}, - {"m5_display:get_rotation/0", { { NIFFunctionType }, nif_display_get_rotation }}, - {"m5_display:set_text_size/1", { { NIFFunctionType }, nif_display_set_text_size }}, - {"m5_display:start_write/0", { { NIFFunctionType }, nif_display_start_write }}, - {"m5_display:end_write/0", { { NIFFunctionType }, nif_display_end_write }}, - {"m5_display:fill_rect/5", { { NIFFunctionType }, nif_display_fill_rect }}, - {"m5_display:print/1", { { NIFFunctionType }, nif_display_print }}, - {"m5_display:println/1", { { NIFFunctionType }, nif_display_println }}, - {"m5_display:println/0", { { NIFFunctionType }, nif_display_println }}, - - {"m5_power:timer_sleep/1", { { NIFFunctionType }, nif_power_timer_sleep }}, - {"m5_power:get_battery_level/0", { { NIFFunctionType }, nif_get_battery_level }}, +static constexpr std::array, 3> NIFS = {{ + {"begin_/1", { { NIFFunctionType }, nif_begin }}, + {"get_board/0", { { NIFFunctionType }, nif_get_board }}, + {"update/0", { { NIFFunctionType }, nif_update }}, }}; // // Component Nif Entrypoints // -void m5_init(GlobalContext *global) +static const struct Nif *get_nif(const char *nifname) { - // no-op -} - -const struct Nif *m5_get_nif(const char *nifname) -{ - for (const auto& nif : M5_NIFS) + if (memcmp(nifname, MODULE_PREFIX, strlen(MODULE_PREFIX))) { + return NULL; + } + for (const auto& nif : NIFS) { - if (strcmp(nif.first, nifname) == 0) { + if (strcmp(nif.first, nifname + strlen(MODULE_PREFIX)) == 0) { return &nif.second; } } return NULL; } -REGISTER_NIF_COLLECTION(m5, m5_init, m5_get_nif) +REGISTER_NIF_COLLECTION(m5, NULL, NULL, get_nif) #endif diff --git a/nifs/atomvm_m5_display.cc b/nifs/atomvm_m5_display.cc new file mode 100644 index 0000000..cb902a6 --- /dev/null +++ b/nifs/atomvm_m5_display.cc @@ -0,0 +1,449 @@ +/* SPDX-License-Identifier: MIT */ +#include + +#ifdef CONFIG_AVM_M5_DISPLAY_ENABLE + +#include + +#include "atomvm_m5.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + +#include +#include +#include + +#include + +#pragma GCC diagnostic pop + +#include +#include +#include +#include +#include +#include +#include +//#define ENABLE_TRACE +#include + +#define MODULE_PREFIX "m5_display:" + +#define M5_DISPLAY_NIF_INT32_v(name, api_func) \ +static term name(Context *ctx, int argc, term argv[]) \ +{ \ + UNUSED(argc); \ + UNUSED(argv); \ + M5.Display. api_func (); \ + return OK_ATOM; \ +} + +#define M5_DISPLAY_NIF_INT32_i1(name, api_func) \ +static term name(Context *ctx, int argc, term argv[]) \ +{ \ + UNUSED(argc); \ + VALIDATE_VALUE(argv[0], term_is_integer); \ + M5.Display. api_func ( \ + term_to_int32(argv[0]) \ + ); \ + return OK_ATOM; \ +} + +#define M5_DISPLAY_NIF_INT32_i2(name, api_func) \ +static term name(Context *ctx, int argc, term argv[]) \ +{ \ + UNUSED(argc); \ + VALIDATE_VALUE(argv[0], term_is_integer); \ + VALIDATE_VALUE(argv[1], term_is_integer); \ + M5.Display. api_func ( \ + term_to_int32(argv[0]), \ + term_to_int32(argv[1]) \ + ); \ + return OK_ATOM; \ +} + +#define M5_DISPLAY_NIF_INT32_i3(name, api_func) \ +static term name(Context *ctx, int argc, term argv[]) \ +{ \ + UNUSED(argc); \ + VALIDATE_VALUE(argv[0], term_is_integer); \ + VALIDATE_VALUE(argv[1], term_is_integer); \ + VALIDATE_VALUE(argv[2], term_is_integer); \ + M5.Display. api_func ( \ + term_to_int32(argv[0]), \ + term_to_int32(argv[1]), \ + term_to_int32(argv[2]) \ + ); \ + return OK_ATOM; \ +} + +#define M5_DISPLAY_NIF_INT32_i2_i3(name, api_func) \ +static term name(Context *ctx, int argc, term argv[]) \ +{ \ + for (int i = 0; i < argc; i++) { \ + VALIDATE_VALUE(argv[i], term_is_integer); \ + } \ + if (argc == 2) { \ + M5.Display. api_func ( \ + term_to_int32(argv[0]), \ + term_to_int32(argv[1]) \ + ); \ + } else { \ + M5.Display. api_func ( \ + term_to_int32(argv[0]), \ + term_to_int32(argv[1]), \ + term_to_int32(argv[2]) \ + ); \ + } \ + return OK_ATOM; \ +} +#define M5_DISPLAY_NIF_INT32_i3_i4(name, api_func) \ +static term name(Context *ctx, int argc, term argv[]) \ +{ \ + for (int i = 0; i < argc; i++) { \ + VALIDATE_VALUE(argv[i], term_is_integer); \ + } \ + if (argc == 3) { \ + M5.Display. api_func ( \ + term_to_int32(argv[0]), \ + term_to_int32(argv[1]), \ + term_to_int32(argv[2]) \ + ); \ + } else { \ + M5.Display. api_func ( \ + term_to_int32(argv[0]), \ + term_to_int32(argv[1]), \ + term_to_int32(argv[2]), \ + term_to_int32(argv[3]) \ + ); \ + } \ + return OK_ATOM; \ +} + +#define M5_DISPLAY_NIF_INT32_i4_i5(name, api_func) \ +static term name(Context *ctx, int argc, term argv[]) \ +{ \ + for (int i = 0; i < argc; i++) { \ + VALIDATE_VALUE(argv[i], term_is_integer); \ + } \ + if (argc == 4) { \ + M5.Display. api_func ( \ + term_to_int32(argv[0]), \ + term_to_int32(argv[1]), \ + term_to_int32(argv[2]), \ + term_to_int32(argv[3]) \ + ); \ + } else { \ + M5.Display. api_func ( \ + term_to_int32(argv[0]), \ + term_to_int32(argv[1]), \ + term_to_int32(argv[2]), \ + term_to_int32(argv[3]), \ + term_to_int32(argv[4]) \ + ); \ + } \ + return OK_ATOM; \ +} + +#define M5_DISPLAY_NIF_INT32_i5_i6(name, api_func) \ +static term name(Context *ctx, int argc, term argv[]) \ +{ \ + for (int i = 0; i < argc; i++) { \ + VALIDATE_VALUE(argv[i], term_is_integer); \ + } \ + if (argc == 5) { \ + M5.Display. api_func ( \ + term_to_int32(argv[0]), \ + term_to_int32(argv[1]), \ + term_to_int32(argv[2]), \ + term_to_int32(argv[3]), \ + term_to_int32(argv[4]) \ + ); \ + } else { \ + M5.Display. api_func ( \ + term_to_int32(argv[0]), \ + term_to_int32(argv[1]), \ + term_to_int32(argv[2]), \ + term_to_int32(argv[3]), \ + term_to_int32(argv[4]), \ + term_to_int32(argv[5]) \ + ); \ + } \ + return OK_ATOM; \ +} + +#define M5_DISPLAY_NIF_INT32_i6_i7(name, api_func) \ +static term name(Context *ctx, int argc, term argv[]) \ +{ \ + for (int i = 0; i < argc; i++) { \ + VALIDATE_VALUE(argv[i], term_is_integer); \ + } \ + if (argc == 6) { \ + M5.Display. api_func ( \ + term_to_int32(argv[0]), \ + term_to_int32(argv[1]), \ + term_to_int32(argv[2]), \ + term_to_int32(argv[3]), \ + term_to_int32(argv[4]), \ + term_to_int32(argv[5]) \ + ); \ + } else { \ + M5.Display. api_func ( \ + term_to_int32(argv[0]), \ + term_to_int32(argv[1]), \ + term_to_int32(argv[2]), \ + term_to_int32(argv[3]), \ + term_to_int32(argv[4]), \ + term_to_int32(argv[5]), \ + term_to_int32(argv[6]) \ + ); \ + } \ + return OK_ATOM; \ +} + +static term nif_display_set_epd_mode(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + + M5.Display.setEpdMode(epd_mode_t::epd_fastest); // fastest but very-low quality. + return OK_ATOM; +} + +M5_DISPLAY_NIF_INT32_v(nif_display_sleep, sleep) +M5_DISPLAY_NIF_INT32_v(nif_display_start_write, startWrite) +M5_DISPLAY_NIF_INT32_v(nif_display_end_write, endWrite) + +M5_DISPLAY_NIF_INT32_i2_i3(nif_display_write_pixel, writePixel) +M5_DISPLAY_NIF_INT32_i3_i4(nif_display_write_fast_vline, writeFastVLine) +M5_DISPLAY_NIF_INT32_i3_i4(nif_display_write_fast_hline, writeFastHLine) +M5_DISPLAY_NIF_INT32_i4_i5(nif_display_write_fill_rect, writeFillRect) +M5_DISPLAY_NIF_INT32_i4_i5(nif_display_write_fill_rect_preclipped, writeFillRectPreclipped) +M5_DISPLAY_NIF_INT32_i2(nif_display_write_color, writeColor) +M5_DISPLAY_NIF_INT32_i2(nif_display_push_block, pushBlock) +M5_DISPLAY_NIF_INT32_i2_i3(nif_display_draw_pixel, drawPixel) +M5_DISPLAY_NIF_INT32_i3_i4(nif_display_draw_fast_vline, drawFastVLine) +M5_DISPLAY_NIF_INT32_i3_i4(nif_display_draw_fast_hline, drawFastHLine) +M5_DISPLAY_NIF_INT32_i4_i5(nif_display_fill_rect, fillRect) +M5_DISPLAY_NIF_INT32_i4_i5(nif_display_draw_rect, drawRect) +M5_DISPLAY_NIF_INT32_i5_i6(nif_display_draw_round_rect, drawRoundRect) +M5_DISPLAY_NIF_INT32_i5_i6(nif_display_fill_round_rect, fillRoundRect) +M5_DISPLAY_NIF_INT32_i3_i4(nif_display_draw_circle, drawCircle) +M5_DISPLAY_NIF_INT32_i3_i4(nif_display_fill_circle, fillCircle) +M5_DISPLAY_NIF_INT32_i4_i5(nif_display_draw_ellipse, drawEllipse) +M5_DISPLAY_NIF_INT32_i4_i5(nif_display_fill_ellipse, fillEllipse) +M5_DISPLAY_NIF_INT32_i4_i5(nif_display_draw_line, drawLine) +M5_DISPLAY_NIF_INT32_i6_i7(nif_display_draw_triangle, drawTriangle) +M5_DISPLAY_NIF_INT32_i6_i7(nif_display_fill_triangle, fillTriangle) + +static term nif_display_draw_bezier(Context *ctx, int argc, term argv[]) +{ + for (int i = 0; i < argc; i++) { + VALIDATE_VALUE(argv[i], term_is_integer); + } + switch (argc) { + case 6: + M5.Display.drawBezier( + term_to_int32(argv[0]), + term_to_int32(argv[1]), + term_to_int32(argv[2]), + term_to_int32(argv[3]), + term_to_int32(argv[4]), + term_to_int32(argv[5]) + ); + break; + case 7: + M5.Display.drawBezier( + term_to_int32(argv[0]), + term_to_int32(argv[1]), + term_to_int32(argv[2]), + term_to_int32(argv[3]), + term_to_int32(argv[4]), + term_to_int32(argv[5]), + term_to_int32(argv[6]) + ); + break; + case 8: + M5.Display.drawBezier( + term_to_int32(argv[0]), + term_to_int32(argv[1]), + term_to_int32(argv[2]), + term_to_int32(argv[3]), + term_to_int32(argv[4]), + term_to_int32(argv[5]), + term_to_int32(argv[6]), + term_to_int32(argv[7]) + ); + break; + case 9: + M5.Display.drawBezier( + term_to_int32(argv[0]), + term_to_int32(argv[1]), + term_to_int32(argv[2]), + term_to_int32(argv[3]), + term_to_int32(argv[4]), + term_to_int32(argv[5]), + term_to_int32(argv[6]), + term_to_int32(argv[7]), + term_to_int32(argv[8]) + ); + break; + } + return OK_ATOM; +} + +static term nif_display_width(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + + return term_from_int32(M5.Display.width()); +} + +static term nif_display_height(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + + return term_from_int32(M5.Display.height()); +} + +M5_DISPLAY_NIF_INT32_v(nif_display_wait_display, waitDisplay) + +static term nif_display_get_rotation(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + + return term_from_int32(M5.Display.getRotation()); +} + +M5_DISPLAY_NIF_INT32_i1(nif_display_set_rotation, setRotation) + +static term nif_display_get_cursor(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + UNUSED(argv); + if (UNLIKELY(memory_ensure_free(ctx, 3) != MEMORY_GC_OK)) { + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + term result = term_alloc_tuple(2, &ctx->heap); + term_put_tuple_element(result, 0, term_from_int32(M5.Display.getCursorX())); + term_put_tuple_element(result, 1, term_from_int32(M5.Display.getCursorY())); + + return result; +} + +M5_DISPLAY_NIF_INT32_i2(nif_display_set_cursor, setCursor) +M5_DISPLAY_NIF_INT32_i1(nif_display_set_text_size, setTextSize) + +static term nif_display_print(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + VALIDATE_VALUE(argv[0], term_is_binary); + unsigned long binLen = term_binary_size(argv[0]); + const char* bin = term_binary_data(argv[0]); + size_t r = M5.Display.write(bin, binLen); + return term_from_int(r); +} + +static term nif_display_println(Context *ctx, int argc, term argv[]) +{ + size_t r = 0; + if (argc == 1) { + VALIDATE_VALUE(argv[0], term_is_binary); + unsigned long binLen = term_binary_size(argv[0]); + const char* bin = term_binary_data(argv[0]); + r = M5.Display.write(bin, binLen); + } + r += M5.Display.println(); + return term_from_int(r); +} + +static constexpr std::array, 59> NIFS = {{ + {"set_epd_mode/1", { { NIFFunctionType }, nif_display_set_epd_mode }}, + {"sleep/0", { { NIFFunctionType }, nif_display_sleep }}, + + {"start_write/0", { { NIFFunctionType }, nif_display_start_write }}, + {"end_write/0", { { NIFFunctionType }, nif_display_end_write }}, + + {"write_pixel/2", { { NIFFunctionType }, nif_display_write_pixel }}, + {"write_pixel/3", { { NIFFunctionType }, nif_display_write_pixel }}, + {"write_fast_vline/3", { { NIFFunctionType }, nif_display_write_fast_vline }}, + {"write_fast_vline/4", { { NIFFunctionType }, nif_display_write_fast_vline }}, + {"write_fast_hline/3", { { NIFFunctionType }, nif_display_write_fast_hline }}, + {"write_fast_hline/4", { { NIFFunctionType }, nif_display_write_fast_hline }}, + {"write_fill_rect/4", { { NIFFunctionType }, nif_display_write_fill_rect }}, + {"write_fill_rect/5", { { NIFFunctionType }, nif_display_write_fill_rect }}, + {"write_fill_rect_preclipped/4", { { NIFFunctionType }, nif_display_write_fill_rect_preclipped }}, + {"write_fill_rect_preclipped/5", { { NIFFunctionType }, nif_display_write_fill_rect_preclipped }}, + {"write_color/2", { { NIFFunctionType }, nif_display_write_color }}, + {"push_block/2", { { NIFFunctionType }, nif_display_push_block }}, + {"draw_pixel/2", { { NIFFunctionType }, nif_display_draw_pixel }}, + {"draw_pixel/3", { { NIFFunctionType }, nif_display_draw_pixel }}, + {"draw_fast_vline/3", { { NIFFunctionType }, nif_display_draw_fast_vline }}, + {"draw_fast_vline/4", { { NIFFunctionType }, nif_display_draw_fast_vline }}, + {"draw_fast_hline/3", { { NIFFunctionType }, nif_display_draw_fast_hline }}, + {"draw_fast_hline/4", { { NIFFunctionType }, nif_display_draw_fast_hline }}, + {"fill_rect/4", { { NIFFunctionType }, nif_display_fill_rect }}, + {"fill_rect/5", { { NIFFunctionType }, nif_display_fill_rect }}, + {"draw_rect/4", { { NIFFunctionType }, nif_display_draw_rect }}, + {"draw_rect/5", { { NIFFunctionType }, nif_display_draw_rect }}, + {"draw_round_rect/5", { { NIFFunctionType }, nif_display_draw_round_rect }}, + {"draw_round_rect/6", { { NIFFunctionType }, nif_display_draw_round_rect }}, + {"fill_round_rect/5", { { NIFFunctionType }, nif_display_fill_round_rect }}, + {"fill_round_rect/6", { { NIFFunctionType }, nif_display_fill_round_rect }}, + {"draw_circle/3", { { NIFFunctionType }, nif_display_draw_circle }}, + {"draw_circle/4", { { NIFFunctionType }, nif_display_draw_circle }}, + {"fill_circle/3", { { NIFFunctionType }, nif_display_fill_circle }}, + {"fill_circle/4", { { NIFFunctionType }, nif_display_fill_circle }}, + {"draw_ellipse/4", { { NIFFunctionType }, nif_display_draw_ellipse }}, + {"draw_ellipse/5", { { NIFFunctionType }, nif_display_draw_ellipse }}, + {"fill_ellipse/4", { { NIFFunctionType }, nif_display_fill_ellipse }}, + {"fill_ellipse/5", { { NIFFunctionType }, nif_display_fill_ellipse }}, + {"draw_line/4", { { NIFFunctionType }, nif_display_draw_line }}, + {"draw_line/5", { { NIFFunctionType }, nif_display_draw_line }}, + {"draw_triangle/6", { { NIFFunctionType }, nif_display_draw_triangle }}, + {"draw_triangle/7", { { NIFFunctionType }, nif_display_draw_triangle }}, + {"fill_triangle/6", { { NIFFunctionType }, nif_display_fill_triangle }}, + {"fill_triangle/7", { { NIFFunctionType }, nif_display_fill_triangle }}, + {"draw_bezier/6", { { NIFFunctionType }, nif_display_draw_bezier }}, + {"draw_bezier/7", { { NIFFunctionType }, nif_display_draw_bezier }}, + {"draw_bezier/8", { { NIFFunctionType }, nif_display_draw_bezier }}, + {"draw_bezier/9", { { NIFFunctionType }, nif_display_draw_bezier }}, + + {"width/0", { { NIFFunctionType }, nif_display_width }}, + {"height/0", { { NIFFunctionType }, nif_display_height }}, + + {"wait_display/0", { { NIFFunctionType }, nif_display_wait_display }}, + + {"get_rotation/0", { { NIFFunctionType }, nif_display_get_rotation }}, + {"set_rotation/1", { { NIFFunctionType }, nif_display_set_rotation }}, + + {"get_cursor/0", { { NIFFunctionType }, nif_display_get_cursor }}, + {"set_cursor/2", { { NIFFunctionType }, nif_display_set_cursor }}, + {"set_text_size/1", { { NIFFunctionType }, nif_display_set_text_size }}, + + {"print/1", { { NIFFunctionType }, nif_display_print }}, + {"println/1", { { NIFFunctionType }, nif_display_println }}, + {"println/0", { { NIFFunctionType }, nif_display_println }}, +}}; + +// +// Component Nif Entrypoints +// + +static const struct Nif *get_nif(const char *nifname) +{ + if (memcmp(nifname, MODULE_PREFIX, strlen(MODULE_PREFIX))) { + return NULL; + } + for (const auto& nif : NIFS) + { + if (strcmp(nif.first, nifname + strlen(MODULE_PREFIX)) == 0) { + return &nif.second; + } + } + return NULL; +} + +REGISTER_NIF_COLLECTION(m5_display, NULL, NULL, get_nif) + +#endif diff --git a/nifs/atomvm_m5_i2c.cc b/nifs/atomvm_m5_i2c.cc new file mode 100644 index 0000000..2ac99db --- /dev/null +++ b/nifs/atomvm_m5_i2c.cc @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: MIT */ +#include + +#ifdef CONFIG_AVM_M5_I2C_ENABLE + +#include + +#include "atomvm_m5.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + +#include +#include +#include + +#include + +#pragma GCC diagnostic pop + +#include +#include +#include +#include +#include +#include +#include +//#define ENABLE_TRACE +#include + +#define MODULE_IN_PREFIX "m5_in_i2c:" +#define MODULE_EX_PREFIX "m5_ex_i2c:" + +#define MAKE_ATOM(ctx, len, str) globalcontext_make_atom(ctx->global, ATOM_STR(len, str)) + +static term nif_in_i2c_set_port(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + M5.In_I2C.setPort(term_to_int32(argv[0]), term_to_int32(argv[1]), term_to_int32(argv[2])); + return OK_ATOM; +} + +static term nif_ex_i2c_set_port(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + M5.Ex_I2C.setPort(term_to_int32(argv[0]), term_to_int32(argv[1]), term_to_int32(argv[2])); + return OK_ATOM; +} + +static term nif_in_i2c_begin(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + bool result = M5.In_I2C.begin(term_to_int32(argv[0]), term_to_int32(argv[1]), term_to_int32(argv[2])); + return result ? TRUE_ATOM : FALSE_ATOM; +} + +static term nif_ex_i2c_begin(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + bool result = M5.Ex_I2C.begin(term_to_int32(argv[0]), term_to_int32(argv[1]), term_to_int32(argv[2])); + return result ? TRUE_ATOM : FALSE_ATOM; +} + +static constexpr std::array, 2> IN_NIFS = {{ + {"set_port/3", { { NIFFunctionType }, nif_in_i2c_set_port }}, + {"begin_/3", { { NIFFunctionType }, nif_in_i2c_begin }}, +}}; + +static constexpr std::array, 2> EX_NIFS = {{ + {"set_port/3", { { NIFFunctionType }, nif_ex_i2c_set_port }}, + {"begin_/3", { { NIFFunctionType }, nif_ex_i2c_begin }}, +}}; + +// +// Component Nif Entrypoints +// + +static const struct Nif *get_nif(const char *nifname) +{ + if (memcmp(nifname, MODULE_IN_PREFIX, strlen(MODULE_IN_PREFIX)) == 0) { + for (const auto& nif : IN_NIFS) + { + if (strcmp(nif.first, nifname + strlen(MODULE_IN_PREFIX)) == 0) { + return &nif.second; + } + } + } else if (memcmp(nifname, MODULE_EX_PREFIX, strlen(MODULE_EX_PREFIX)) == 0) { + for (const auto& nif : EX_NIFS) + { + if (strcmp(nif.first, nifname + strlen(MODULE_EX_PREFIX)) == 0) { + return &nif.second; + } + } + } + return NULL; +} + +REGISTER_NIF_COLLECTION(m5_i2c, NULL, NULL, get_nif) + +#endif diff --git a/nifs/atomvm_m5_power.cc b/nifs/atomvm_m5_power.cc new file mode 100644 index 0000000..0dd692f --- /dev/null +++ b/nifs/atomvm_m5_power.cc @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: MIT */ +#include + +#ifdef CONFIG_AVM_M5_POWER_ENABLE + +#include + +#include "atomvm_m5.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + +#include +#include +#include + +#include + +#pragma GCC diagnostic pop + +#include +#include +#include +#include +#include +#include +#include +//#define ENABLE_TRACE +#include + +#define MODULE_PREFIX "m5_power:" + +#define MAKE_ATOM(ctx, len, str) globalcontext_make_atom(ctx->global, ATOM_STR(len, str)) +//#define MAKE_ATOM(ctx, len, str) context_make_atom(ctx, ATOM_STR(len, str)) + +static term nif_power_deep_sleep(Context *ctx, int argc, term argv[]) +{ + printf("argc = %d\n", argc); + if (!term_is_any_integer(argv[0])) { + avm_int64_t t = term_maybe_unbox_int64(argv[0]); + printf("t = %llu, not an integer (?)\n", t); + } + avm_int64_t usec = 0; + bool touch_wakeup = true; + if (argc > 0) { + VALIDATE_VALUE(argv[0], term_is_any_integer); + usec = term_maybe_unbox_int64(argv[0]); + if (argc > 1) { + VALIDATE_VALUE(argv[1], term_is_atom); + touch_wakeup = argv[1] != FALSE_ATOM; + } + } + M5.Power.deepSleep(usec, touch_wakeup); + return OK_ATOM; +} + +static term nif_power_timer_sleep(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + VALIDATE_VALUE(argv[0], term_is_any_integer); + M5.Power.timerSleep(term_to_int32(argv[0])); + return OK_ATOM; +} + +static term nif_power_get_battery_level(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + UNUSED(argv); + return term_from_int(M5.Power.getBatteryLevel()); +} + +static term nif_power_set_battery_charge(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + VALIDATE_VALUE(argv[0], term_is_atom); + M5.Power.setBatteryCharge(argv[0] == TRUE_ATOM); + return OK_ATOM; +} + +static term nif_power_set_charge_current(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + VALIDATE_VALUE(argv[0], term_is_number); + M5.Power.setChargeCurrent(term_conv_to_float(argv[0])); + return OK_ATOM; +} + +static term nif_power_set_charge_voltage(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + VALIDATE_VALUE(argv[0], term_is_number); + M5.Power.setChargeVoltage(term_conv_to_float(argv[0])); + return OK_ATOM; +} + +static term nif_power_is_charging(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + UNUSED(argv); + + term result = term_nil(); + + switch (M5.Power.isCharging()) { + case m5::Power_Class::is_discharging: + result = FALSE_ATOM; + break; + case m5::Power_Class::is_charging: + result = TRUE_ATOM; + break; + case m5::Power_Class::charge_unknown: + result = UNDEFINED_ATOM; + break; + } + + return result; +} + +static term nif_power_get_type(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + UNUSED(argv); + + term result = term_nil(); + + switch (M5.Power.getType()) { + case m5::Power_Class::pmic_unknown: + result = MAKE_ATOM(ctx, "\x7", "unknown"); + break; + case m5::Power_Class::pmic_adc: + result = MAKE_ATOM(ctx, "\x3", "adc"); + break; + case m5::Power_Class::pmic_axp192: + result = MAKE_ATOM(ctx, "\x6", "axp192"); + break; + case m5::Power_Class::pmic_axp2101: + result = MAKE_ATOM(ctx, "\x7", "axp2101"); + break; + case m5::Power_Class::pmic_ip5306: + result = MAKE_ATOM(ctx, "\x6", "ip5306"); + break; + } + + return result; +} + +static constexpr std::array, 10> NIFS = {{ + {"deep_sleep/0", { { NIFFunctionType }, nif_power_deep_sleep }}, + {"deep_sleep/1", { { NIFFunctionType }, nif_power_deep_sleep }}, + {"deep_sleep/2", { { NIFFunctionType }, nif_power_deep_sleep }}, + {"timer_sleep/1", { { NIFFunctionType }, nif_power_timer_sleep }}, + {"get_battery_level/0", { { NIFFunctionType }, nif_power_get_battery_level }}, + {"set_battery_charge/1", { { NIFFunctionType }, nif_power_set_battery_charge }}, + {"set_charge_current/1", { { NIFFunctionType }, nif_power_set_charge_current }}, + {"set_charge_voltage/1", { { NIFFunctionType }, nif_power_set_charge_voltage }}, + {"is_charging/0", { { NIFFunctionType }, nif_power_is_charging }}, + {"get_type/0", { { NIFFunctionType }, nif_power_get_type }}, +}}; + +// +// Component Nif Entrypoints +// + +static const struct Nif *get_nif(const char *nifname) +{ + if (memcmp(nifname, MODULE_PREFIX, strlen(MODULE_PREFIX))) { + return NULL; + } + for (const auto& nif : NIFS) + { + if (strcmp(nif.first, nifname + strlen(MODULE_PREFIX)) == 0) { + return &nif.second; + } + } + return NULL; +} + +REGISTER_NIF_COLLECTION(m5_power, NULL, NULL, get_nif) + +#endif diff --git a/nifs/atomvm_m5_power_axp192.cc b/nifs/atomvm_m5_power_axp192.cc new file mode 100644 index 0000000..0ca02aa --- /dev/null +++ b/nifs/atomvm_m5_power_axp192.cc @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: MIT */ +#include + +#ifdef CONFIG_AVM_M5_POWER_AXP192_ENABLE + +#include + +#include "atomvm_m5.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + +#include +#include +#include + +#include + +#pragma GCC diagnostic pop + +#include +#include +#include +#include +#include +#include +#include +//#define ENABLE_TRACE +#include + +#define MODULE_PREFIX "m5_power_axp192:" + +#define MAKE_ATOM(ctx, len, str) globalcontext_make_atom(ctx->global, ATOM_STR(len, str)) + +#define NIF_IMPL_GETTER_INT(name, expr) \ + static term nif_power_axp192_ ## name (Context *ctx, int argc, term argv[]) \ + { \ + UNUSED(argc); \ + UNUSED(argv); \ + return term_from_int(M5.Power.Axp192.expr); \ + } +#define NIF_IMPL_GETTER_FLOAT(name, expr) \ + static term nif_power_axp192_ ## name (Context *ctx, int argc, term argv[]) \ + { \ + UNUSED(argc); \ + UNUSED(argv); \ + return term_from_float(M5.Power.Axp192.expr, &ctx->heap); \ + } + +NIF_IMPL_GETTER_INT(get_battery_level, getBatteryLevel()) +NIF_IMPL_GETTER_FLOAT(get_battery_voltage, getBatteryVoltage()) +NIF_IMPL_GETTER_FLOAT(get_battery_discharge_current, getBatteryDischargeCurrent()) +NIF_IMPL_GETTER_FLOAT(get_battery_charge_current, getBatteryChargeCurrent()) +NIF_IMPL_GETTER_FLOAT(get_battery_power, getBatteryPower()) +NIF_IMPL_GETTER_FLOAT(get_acin_voltage, getACINVoltage()) +NIF_IMPL_GETTER_FLOAT(get_acin_current, getACINCurrent()) +NIF_IMPL_GETTER_FLOAT(get_vbus_voltage, getVBUSVoltage()) +NIF_IMPL_GETTER_FLOAT(get_vbus_current, getVBUSCurrent()) +NIF_IMPL_GETTER_FLOAT(get_aps_voltage, getAPSVoltage()) +NIF_IMPL_GETTER_FLOAT(get_internal_temperature, getInternalTemperature()) + +static constexpr std::array, 11> NIFS = {{ + {"get_battery_level/0", { { NIFFunctionType }, nif_power_axp192_get_battery_level }}, + {"get_battery_voltage/0", { { NIFFunctionType }, nif_power_axp192_get_battery_voltage }}, + {"get_battery_discharge_current/0", { { NIFFunctionType }, nif_power_axp192_get_battery_discharge_current }}, + {"get_battery_charge_current/0", { { NIFFunctionType }, nif_power_axp192_get_battery_charge_current }}, + {"get_battery_power/0", { { NIFFunctionType }, nif_power_axp192_get_battery_power }}, + {"get_acin_voltage/0", { { NIFFunctionType }, nif_power_axp192_get_acin_voltage }}, + {"get_acin_current/0", { { NIFFunctionType }, nif_power_axp192_get_acin_current }}, + {"get_vbus_voltage/0", { { NIFFunctionType }, nif_power_axp192_get_vbus_voltage }}, + {"get_vbus_current/0", { { NIFFunctionType }, nif_power_axp192_get_vbus_current }}, + {"get_aps_voltage/0", { { NIFFunctionType }, nif_power_axp192_get_aps_voltage }}, + {"get_internal_temperature/0", { { NIFFunctionType }, nif_power_axp192_get_internal_temperature }}, +}}; + +// +// Component Nif Entrypoints +// + +static const struct Nif *get_nif(const char *nifname) +{ + if (memcmp(nifname, MODULE_PREFIX, strlen(MODULE_PREFIX))) { + return NULL; + } + for (const auto& nif : NIFS) + { + if (strcmp(nif.first, nifname + strlen(MODULE_PREFIX)) == 0) { + return &nif.second; + } + } + return NULL; +} + +REGISTER_NIF_COLLECTION(m5_power_axp192, NULL, NULL, get_nif) + +#endif diff --git a/nifs/atomvm_m5_rtc.cc b/nifs/atomvm_m5_rtc.cc new file mode 100644 index 0000000..77836f3 --- /dev/null +++ b/nifs/atomvm_m5_rtc.cc @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: MIT */ +#include + +#ifdef CONFIG_AVM_M5_RTC_ENABLE + +#include + +#include "atomvm_m5.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + +#include +#include +#include + +#include + +#pragma GCC diagnostic pop + +#include +#include +#include +#include +#include +#include +#include +//#define ENABLE_TRACE +#include + +#define MODULE_PREFIX "m5_rtc:" + +#define MAKE_ATOM(ctx, len, str) globalcontext_make_atom(ctx->global, ATOM_STR(len, str)) + +static term nif_rtc_get_time(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + UNUSED(argv); + + m5::rtc_time_t time; + M5.Rtc.getTime(&time); + if (memory_ensure_free_opt(ctx, TUPLE_SIZE(3), MEMORY_CAN_SHRINK) != MEMORY_GC_OK) { + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + term result = term_alloc_tuple(3, &ctx->heap); + term_put_tuple_element(result, 0, term_from_int(time.hours)); + term_put_tuple_element(result, 1, term_from_int(time.minutes)); + term_put_tuple_element(result, 2, term_from_int(time.seconds)); + + return result; +} + +static term nif_rtc_get_date(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + UNUSED(argv); + + m5::rtc_date_t date; + M5.Rtc.getDate(&date); + if (memory_ensure_free_opt(ctx, TUPLE_SIZE(3), MEMORY_CAN_SHRINK) != MEMORY_GC_OK) { + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + term result = term_alloc_tuple(3, &ctx->heap); + term_put_tuple_element(result, 0, term_from_int(date.year)); + term_put_tuple_element(result, 1, term_from_int(date.month)); + term_put_tuple_element(result, 2, term_from_int(date.date)); + + return result; +} + +static term nif_rtc_get_datetime(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + UNUSED(argv); + + m5::rtc_datetime_t datetime; + M5.Rtc.getDateTime(&datetime); + if (memory_ensure_free_opt(ctx, TUPLE_SIZE(2) + 2 * TUPLE_SIZE(3), MEMORY_CAN_SHRINK) != MEMORY_GC_OK) { + RAISE_ERROR(OUT_OF_MEMORY_ATOM); + } + term result = term_alloc_tuple(2, &ctx->heap); + term date_tuple = term_alloc_tuple(3, &ctx->heap); + term_put_tuple_element(date_tuple, 0, term_from_int(datetime.date.year)); + term_put_tuple_element(date_tuple, 1, term_from_int(datetime.date.month)); + term_put_tuple_element(date_tuple, 2, term_from_int(datetime.date.date)); + term_put_tuple_element(result, 0, date_tuple); + term time_tuple = term_alloc_tuple(3, &ctx->heap); + term_put_tuple_element(time_tuple, 0, term_from_int(datetime.time.hours)); + term_put_tuple_element(time_tuple, 1, term_from_int(datetime.time.minutes)); + term_put_tuple_element(time_tuple, 2, term_from_int(datetime.time.seconds)); + term_put_tuple_element(result, 1, time_tuple); + + return result; +} + +static term nif_rtc_set_time(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + + m5::rtc_time_t time; + term time_tuple = argv[0]; + VALIDATE_VALUE(time_tuple, term_is_tuple); + if (term_get_tuple_arity(time_tuple) != 3) { + RAISE_ERROR(BADARG_ATOM); + } + time.hours = term_to_int(term_get_tuple_element(time_tuple, 0)); + time.minutes = term_to_int(term_get_tuple_element(time_tuple, 1)); + time.seconds = term_to_int(term_get_tuple_element(time_tuple, 2)); + M5.Rtc.setTime(time); + + return OK_ATOM; +} + +static term nif_rtc_set_date(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + + m5::rtc_date_t date; + term date_tuple = argv[0]; + VALIDATE_VALUE(date_tuple, term_is_tuple); + if (term_get_tuple_arity(date_tuple) != 3) { + RAISE_ERROR(BADARG_ATOM); + } + date.year = term_to_int(term_get_tuple_element(date_tuple, 0)); + date.month = term_to_int(term_get_tuple_element(date_tuple, 1)); + date.date = term_to_int(term_get_tuple_element(date_tuple, 2)); + date.weekDay = -1; + M5.Rtc.setDate(date); + + return OK_ATOM; +} + +static term nif_rtc_set_datetime(Context *ctx, int argc, term argv[]) +{ + UNUSED(argc); + + m5::rtc_datetime_t datetime; + term datetime_tuple = argv[0]; + VALIDATE_VALUE(datetime_tuple, term_is_tuple); + if (term_get_tuple_arity(datetime_tuple) != 2) { + RAISE_ERROR(BADARG_ATOM); + } + term date_tuple = term_get_tuple_element(datetime_tuple, 0); + VALIDATE_VALUE(date_tuple, term_is_tuple); + if (term_get_tuple_arity(date_tuple) != 3) { + RAISE_ERROR(BADARG_ATOM); + } + term time_tuple = term_get_tuple_element(datetime_tuple, 1); + VALIDATE_VALUE(time_tuple, term_is_tuple); + if (term_get_tuple_arity(time_tuple) != 3) { + RAISE_ERROR(BADARG_ATOM); + } + datetime.date.year = term_to_int(term_get_tuple_element(date_tuple, 0)); + datetime.date.month = term_to_int(term_get_tuple_element(date_tuple, 1)); + datetime.date.date = term_to_int(term_get_tuple_element(date_tuple, 2)); + datetime.date.weekDay = -1; + datetime.time.hours = term_to_int(term_get_tuple_element(time_tuple, 0)); + datetime.time.minutes = term_to_int(term_get_tuple_element(time_tuple, 1)); + datetime.time.seconds = term_to_int(term_get_tuple_element(time_tuple, 2)); + M5.Rtc.setDateTime(datetime); + + return OK_ATOM; +} + +static constexpr std::array, 6> NIFS = {{ + {"get_time/0", { { NIFFunctionType }, nif_rtc_get_time }}, + {"get_date/0", { { NIFFunctionType }, nif_rtc_get_date }}, + {"get_datetime/0", { { NIFFunctionType }, nif_rtc_get_datetime }}, + {"set_time/1", { { NIFFunctionType }, nif_rtc_set_time }}, + {"set_date/1", { { NIFFunctionType }, nif_rtc_set_date }}, + {"set_datetime/1", { { NIFFunctionType }, nif_rtc_set_datetime }} +}}; + +// +// Component Nif Entrypoints +// + +static const struct Nif *get_nif(const char *nifname) +{ + if (memcmp(nifname, MODULE_PREFIX, strlen(MODULE_PREFIX)) == 0) { + for (const auto& nif : NIFS) + { + if (strcmp(nif.first, nifname + strlen(MODULE_PREFIX)) == 0) { + return &nif.second; + } + } + } + return NULL; +} + +REGISTER_NIF_COLLECTION(m5_rtc, NULL, NULL, get_nif) + +#endif diff --git a/nifs/atomvm_m5_speaker.cc b/nifs/atomvm_m5_speaker.cc new file mode 100644 index 0000000..07b897c --- /dev/null +++ b/nifs/atomvm_m5_speaker.cc @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: MIT */ +#include + +#ifdef CONFIG_AVM_M5_SPEAKER_ENABLE + +#include + +#include "atomvm_m5.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + +#include +#include +#include + +#include + +#pragma GCC diagnostic pop + +#include +#include +#include +#include +#include +#include +#include +//#define ENABLE_TRACE +#include + +#define MODULE_PREFIX "m5_speaker:" + +static term nif_speaker_tone(Context *ctx, int argc, term argv[]) +{ + VALIDATE_VALUE(argv[0], term_is_number); + VALIDATE_VALUE(argv[1], term_is_integer); + int channel = -1; + if (argc > 2) { + VALIDATE_VALUE(argv[2], term_is_number); + channel = term_to_int32(argv[2]); + } + bool stop_current_sound = true; + if (argc > 3) { + if (argv[3] != TRUE_ATOM && argv[3] != FALSE_ATOM) { + argv[0] = ERROR_ATOM; + argv[1] = BADARG_ATOM; + return term_invalid_term(); + } + stop_current_sound = argv[3] == TRUE_ATOM; + } + avm_float_t frequency = term_conv_to_float(argv[0]); + uint32_t duration = term_to_int32(argv[1]); + bool r = M5.Speaker.tone(frequency, duration, channel, stop_current_sound); + return r ? TRUE_ATOM : FALSE_ATOM; +} + +static constexpr std::array, 3> NIFS = {{ + {"tone/2", { { NIFFunctionType }, nif_speaker_tone }}, + {"tone/3", { { NIFFunctionType }, nif_speaker_tone }}, + {"tone/4", { { NIFFunctionType }, nif_speaker_tone }}, + +}}; + +// +// Component Nif Entrypoints +// + +static const struct Nif *get_nif(const char *nifname) +{ + if (memcmp(nifname, MODULE_PREFIX, strlen(MODULE_PREFIX))) { + return NULL; + } + for (const auto& nif : NIFS) + { + if (strcmp(nif.first, nifname + strlen(MODULE_PREFIX)) == 0) { + return &nif.second; + } + } + return NULL; +} + +REGISTER_NIF_COLLECTION(m5_speaker, NULL, NULL, get_nif) + +#endif diff --git a/nifs/include/atomvm_m5.h b/nifs/include/atomvm_m5.h index 6070ced..844e9ed 100644 --- a/nifs/include/atomvm_m5.h +++ b/nifs/include/atomvm_m5.h @@ -5,7 +5,4 @@ #include #include -void atomvm_m5_init(GlobalContext *global); -const struct Nif *atomvm_m5_get_nif(const char *nifname); - #endif diff --git a/src/m5_speaker.erl b/src/m5_speaker.erl new file mode 100644 index 0000000..a15214e --- /dev/null +++ b/src/m5_speaker.erl @@ -0,0 +1,20 @@ +% SPDX-License-Identifier: MIT +-module(m5_speaker). + +-export([ + tone/2, + tone/3, + tone/4 +]). + +-spec tone(Freq :: number(), Duration :: number()) -> boolean(). +tone(_Freq, _Duration) -> + throw(nif_error). + +-spec tone(Freq :: number(), Duration :: pos_integer(), Channel :: integer()) -> boolean(). +tone(_Freq, _Duration, _Channel) -> + throw(nif_error). + +-spec tone(Freq :: number(), Duration :: pos_integer(), Channel :: integer(), StopPrevious :: boolean()) -> boolean(). +tone(_Freq, _Duration, _Channel, _StopPrevious) -> + throw(nif_error).