From 237010901e70799f49dd13a389298ddee09cd5d6 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Thu, 17 Feb 2022 16:24:47 +0100 Subject: [PATCH 01/84] WIP --- src/event_sources/rmt_esp32.cc | 79 ++++++++++++++++++++++ src/event_sources/rmt_esp32.h | 40 +++++++++++ src/primitive.h | 7 ++ src/resources/gpio_esp32.cc | 5 +- src/resources/rmt_esp32.cc | 120 +++++++++++++++++++++++++++++++++ 5 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 src/event_sources/rmt_esp32.cc create mode 100644 src/event_sources/rmt_esp32.h create mode 100644 src/resources/rmt_esp32.cc diff --git a/src/event_sources/rmt_esp32.cc b/src/event_sources/rmt_esp32.cc new file mode 100644 index 000000000..157cc2d9e --- /dev/null +++ b/src/event_sources/rmt_esp32.cc @@ -0,0 +1,79 @@ +// Copyright (C) 2022 Toitware ApS. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 only. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// The license can be found in the file `LICENSE` in the top level +// directory of this repository. + +#include "../top.h" + +#ifdef TOIT_FREERTOS + +namespace toit { + +#include "rmt_esp32.h" +#include "driver/rmt.h" + +RMTEventSource* RMTEventSource::_instance = null; + +RMTEventSource::RMTEventSource() + : EventSource("RMT") + , Thread("RMT") { + SystemEventSource::instance()->run([&]() -> void { + // TODO + }); + + // Create OS thread to handle RMT events. + spawn(); + + ASSERT(_instance == null); + _instance = this; +} + +GPIOEventSource::~GPIOEventSource() { + join(); + + _instance = null; +} + +void GPIOEventSource::entry() { + HeapTagScope scope(ITERATE_CUSTOM_TAGS + EVENT_SOURCE_MALLOC_TAG); + while (true) { + word id; + if (xQueueReceive(_queue, &id, portMAX_DELAY) != pdTRUE) continue; + if (id == -1) break; + + // Read value as fast as possible, for accuracy. + uint32_t value = gpio_get_level(gpio_num_t(id)); + + // Take lock and check that the resource still exists. If not, discard the result. + Locker locker(mutex()); + IntResource* resource = find_resource_by_id(locker, id); + if (resource == null) continue; + dispatch(locker, resource, value); + // TODO(anders): Consider using this event (time) as entropy source. + } +} + +void GPIOEventSource::on_register_resource(Locker& locker, Resource* r) { + ASSERT(is_locked()); + // TODO +} + +void GPIOEventSource::on_unregister_resource(Locker& locker, Resource* r) { + ASSERT(is_locked()); + // TODO + }); +} + +} // namespace toit + +#endif // TOIT_FREERTOS diff --git a/src/event_sources/rmt_esp32.h b/src/event_sources/rmt_esp32.h new file mode 100644 index 000000000..950abbc2d --- /dev/null +++ b/src/event_sources/rmt_esp32.h @@ -0,0 +1,40 @@ +// Copyright (C) 2022 Toitware ApS. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 only. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// The license can be found in the file `LICENSE` in the top level +// directory of this repository. + +#pragma once + +#include "../resource.h" +#include "../os.h" + +namespace toit { + +class RMTEventSource : public EventSource, public Thread { + public: + static RMTEventSource* instance() { return _instance; } + + RMTEventSource(); + ~RMTEventSource(); + + private: + void entry(); + + void on_register_resource(Locker& locker, Resource* r) override; + void on_unregister_resource(Locker& locker, Resource* r) override; + + static RMTEventSource* _instance; + +}; + +} // namespace toit diff --git a/src/primitive.h b/src/primitive.h index b22e60bcd..cc183a2da 100644 --- a/src/primitive.h +++ b/src/primitive.h @@ -36,6 +36,7 @@ namespace toit { M(i2s, MODULE_I2S) \ M(spi, MODULE_SPI) \ M(uart, MODULE_UART) \ + M(rmt, MODULE_RMT) \ M(crypto, MODULE_CRYPTO) \ M(encoding,MODULE_ENCODING) \ M(font, MODULE_FONT) \ @@ -359,6 +360,12 @@ namespace toit { PRIMITIVE(write, 6) \ PRIMITIVE(read, 1) \ +#define MODULE_RMT(PRIMITIVE) \ + PRIMITIVE(init, 0) \ + PRIMITIVE(use, 2) \ + PRIMITIVE(unuse, 2) \ + PRIMITIVE(config, 6) \ + #define MODULE_CRYPTO(PRIMITIVE) \ PRIMITIVE(sha1_start, 1) \ PRIMITIVE(sha1_add, 4) \ diff --git a/src/resources/gpio_esp32.cc b/src/resources/gpio_esp32.cc index b01a0cf7f..a39f2f929 100644 --- a/src/resources/gpio_esp32.cc +++ b/src/resources/gpio_esp32.cc @@ -102,7 +102,10 @@ PRIMITIVE(use) { if (!gpio_pins.take(num)) ALREADY_IN_USE; IntResource* resource = resource_group->register_id(num); - if (!resource) MALLOC_FAILED; + if (!resource) { + gpio_pins.put(num) + MALLOC_FAILED; + } proxy->set_external_address(resource); return proxy; diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc new file mode 100644 index 000000000..23053689e --- /dev/null +++ b/src/resources/rmt_esp32.cc @@ -0,0 +1,120 @@ +// Copyright (C) 2022 Toitware ApS. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 only. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// The license can be found in the file `LICENSE` in the top level +// directory of this repository. + +#include "../top.h" +#ifdef TOIT_FREERTOS +#include "../primitive.h" +#include "../resource.h" +#include "../resource_pool.h" + +#include "driver/rmt.h" + +namespace toit { + + + +ResourcePool rmt_channels( + RMT_CHANNEL_0, RMT_CHANNEL_1, RMT_CHANNEL_2, RMT_CHANNEL_3, + RMT_CHANNEL_4, RMT_CHANNEL_5, RMT_CHANNEL_6, RMT_CHANNEL_7 +); + +class RMTResourceGRoup : public ResourceGroup { + public: + TAG(RMTResourceGroup); + explicit RMTResourceGroup(Process* process) + : ResourceGroup(process, GPIOEventSource::instance()) {} + + virtual void on_unregister_resource(Resource* r) { + rmt_channel_t channel = static_cast(static_castid()); + rmt_uninstall_driver(channel); + rmt_channels.put(channel); + // TODO uninstall driver + + } + + private: + virtual uint32_t on_event(Resource* resource, word data, uint32_t state) { + + } +} + +MODULE_IMPLEMENTATION(MODULE_RMT) + +PRIMITIVE(init) { + ByteArray* proxy = process->object_heap()->allocate_proxy(); + if (proxy == null) ALLOCATION_FAILED; + + RMTResourceGroup* rmt = _new RMTResourceGroup(process); + if (!rmt) MALLOC_FAILED; + + proxy->set_external_address(rmt); + return proxy; +} + +PRIMITIVE(use) { + ARGS(RMTResourceGroup, resource_group, int, channel_num) + + ByteArray* proxy = process->object_heap()->allocate_proxy(); + if (proxy == null) ALLOCATION_FAILED; + + if (!rmt_channels.take(channel_num)) ALREADY_IN_USE; + + // TODO install RMT driver for channel. + IntResource* resource = resource_group->register_id(channel_num); + if (!resource) { + rmt_channels.take(put) + MALLOC_FAILED; + } + proxy->set_external_address(resource); + + return proxy; +} + +PRIMITIVE(unuse) { + ARGS(RMTResourceGroup, resource_group, IntResource, resource) + + int channel = resource->id(); + resource_group->unregister_id(channel); + resource_proxy->clear_external_address(); + return process->program()->null_object(); +} + +PRIMITIVE(config) { + ARGS(int, pin_num, int, channel_num, bool, rx, bool, tx int, mem_block_num) + if (rx == tx || mem_block_num < 2) INVALID_ARGUMENT; + + rmt_config_t config = rx ? RMT_DEFAULT_CONFIG_RX(pin_num, channel_num) : RMT_DEFAULT_CONFIG_TX(pin_num, channel_num); + config.mem_block_num = mem_block_num; + + // TODO: Allow additional paramters + + esp_err_t err = rmt_config(&config); + if (ESP_OK != err) return Primitive::os_error(err, process); + + err = rmt_install_driver(channel_number, 0, 0); + if (ESP_OK != err) return Primitive::os_error(err, process); + + return process->program()->null_object(); +} // namespace toit + +PRIMITIVE(receive) { + +} + +PRIMITIVE(transmit) { + +} + +#endif // TOIT_FREERTOS From 6343c0194f83bac1627d1c5f7bf04e8b2ab30e1f Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 21 Feb 2022 15:43:30 +0100 Subject: [PATCH 02/84] WIP --- src/primitive.h | 1 + src/resources/rmt_esp32.cc | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/primitive.h b/src/primitive.h index cc183a2da..91b7c3257 100644 --- a/src/primitive.h +++ b/src/primitive.h @@ -766,6 +766,7 @@ namespace toit { #define _A_T_X509ResourceGroup(N, name) MAKE_UNPACKING_MACRO(X509ResourceGroup, N, name) #define _A_T_PWMResourceGroup(N, name) MAKE_UNPACKING_MACRO(PWMResourceGroup, N, name) #define _A_T_RpcResourceGroup(N, name) MAKE_UNPACKING_MACRO(RpcResourceGroup, N, name) +#define _A_T_RMTResourceGroup(N, name) MAKE_UNPACKING_MACRO(RMTResourceGroup, N, name) #define _A_T_Resource(N, name) MAKE_UNPACKING_MACRO(Resource, N, name) #define _A_T_Directory(N, name) MAKE_UNPACKING_MACRO(Directory, N, name) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 23053689e..2a9a73442 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -83,7 +83,7 @@ PRIMITIVE(use) { } PRIMITIVE(unuse) { - ARGS(RMTResourceGroup, resource_group, IntResource, resource) + ARGS(RMTResourceGroup, resource_group, IntResource, resource) int channel = resource->id(); resource_group->unregister_id(channel); @@ -114,7 +114,16 @@ PRIMITIVE(receive) { } PRIMITIVE(transmit) { + ARGS(int, channel_num, Blob, blob) + if (blob.length() % 4 != 0) INVALID_ARGUMENT; + + uint8* bytes = blob.address(); + rmt_item32_t* items = reinterpret_cast(bytes); + esp_err_t err = rmt_write_items(channel_num, items, blob.length() / 4, true); + if (ESP_OK != err) return Primitive::os_error(err, process); + + return process->program()->null_object(); } #endif // TOIT_FREERTOS From f17391f45c3d87f2560d05179397b9b3f2c9672f Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Wed, 23 Feb 2022 13:07:15 +0100 Subject: [PATCH 03/84] WIP --- src/primitive.h | 3 +++ src/resources/rmt_esp32.cc | 27 +++++++++++++++++---------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/primitive.h b/src/primitive.h index 91b7c3257..290c669aa 100644 --- a/src/primitive.h +++ b/src/primitive.h @@ -365,6 +365,9 @@ namespace toit { PRIMITIVE(use, 2) \ PRIMITIVE(unuse, 2) \ PRIMITIVE(config, 6) \ + PRIMITIVE(read, 1) \ + PRIMITIVE(transfer, 2) \ + PRIMITIVE(transfer_and_read, 3) \ #define MODULE_CRYPTO(PRIMITIVE) \ PRIMITIVE(sha1_start, 1) \ diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 2a9a73442..4b94af1be 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -24,7 +24,6 @@ namespace toit { - ResourcePool rmt_channels( RMT_CHANNEL_0, RMT_CHANNEL_1, RMT_CHANNEL_2, RMT_CHANNEL_3, RMT_CHANNEL_4, RMT_CHANNEL_5, RMT_CHANNEL_6, RMT_CHANNEL_7 @@ -107,23 +106,31 @@ PRIMITIVE(config) { if (ESP_OK != err) return Primitive::os_error(err, process); return process->program()->null_object(); -} // namespace toit +} -PRIMITIVE(receive) { +PRIMITIVE(read) { + ARGS(int, rx_num) } -PRIMITIVE(transmit) { - ARGS(int, channel_num, Blob, blob) +PRIMITIVE(transfer) { + ARGS(int, tx_num, Blob, blob) - if (blob.length() % 4 != 0) INVALID_ARGUMENT; + if (item_bytes.length() % 4 != 0) INVALID_ARGUMENT; - uint8* bytes = blob.address(); - rmt_item32_t* items = reinterpret_cast(bytes); - esp_err_t err = rmt_write_items(channel_num, items, blob.length() / 4, true); - if (ESP_OK != err) return Primitive::os_error(err, process); + rmt_item32_t* items = reinterpret_cast(items_bytes); + + esp_err_t err = rmt_write_items(tx_num, items, items_bytes.length() / 4, true); + + if ( err != ESP_OK) return Primitive::os_error(err, process); return process->program()->null_object(); } +PRIMITIVE(transfer_and_read) { + ARGS(int, tx_num, int, rx_num, Blob, items_bytes) + +} + +} // namespace toit #endif // TOIT_FREERTOS From 77800f187b5798b66ef58f00b9033d5b9f74c1b8 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Wed, 23 Feb 2022 13:50:31 +0100 Subject: [PATCH 04/84] WIP --- src/event_sources/rmt_esp32.cc | 79 ---------------------------------- src/event_sources/rmt_esp32.h | 40 ----------------- src/resources/gpio_esp32.cc | 2 +- src/resources/rmt_esp32.cc | 27 ++++++------ src/tags.h | 1 + 5 files changed, 15 insertions(+), 134 deletions(-) delete mode 100644 src/event_sources/rmt_esp32.cc delete mode 100644 src/event_sources/rmt_esp32.h diff --git a/src/event_sources/rmt_esp32.cc b/src/event_sources/rmt_esp32.cc deleted file mode 100644 index 157cc2d9e..000000000 --- a/src/event_sources/rmt_esp32.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (C) 2022 Toitware ApS. -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; version -// 2.1 only. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// The license can be found in the file `LICENSE` in the top level -// directory of this repository. - -#include "../top.h" - -#ifdef TOIT_FREERTOS - -namespace toit { - -#include "rmt_esp32.h" -#include "driver/rmt.h" - -RMTEventSource* RMTEventSource::_instance = null; - -RMTEventSource::RMTEventSource() - : EventSource("RMT") - , Thread("RMT") { - SystemEventSource::instance()->run([&]() -> void { - // TODO - }); - - // Create OS thread to handle RMT events. - spawn(); - - ASSERT(_instance == null); - _instance = this; -} - -GPIOEventSource::~GPIOEventSource() { - join(); - - _instance = null; -} - -void GPIOEventSource::entry() { - HeapTagScope scope(ITERATE_CUSTOM_TAGS + EVENT_SOURCE_MALLOC_TAG); - while (true) { - word id; - if (xQueueReceive(_queue, &id, portMAX_DELAY) != pdTRUE) continue; - if (id == -1) break; - - // Read value as fast as possible, for accuracy. - uint32_t value = gpio_get_level(gpio_num_t(id)); - - // Take lock and check that the resource still exists. If not, discard the result. - Locker locker(mutex()); - IntResource* resource = find_resource_by_id(locker, id); - if (resource == null) continue; - dispatch(locker, resource, value); - // TODO(anders): Consider using this event (time) as entropy source. - } -} - -void GPIOEventSource::on_register_resource(Locker& locker, Resource* r) { - ASSERT(is_locked()); - // TODO -} - -void GPIOEventSource::on_unregister_resource(Locker& locker, Resource* r) { - ASSERT(is_locked()); - // TODO - }); -} - -} // namespace toit - -#endif // TOIT_FREERTOS diff --git a/src/event_sources/rmt_esp32.h b/src/event_sources/rmt_esp32.h deleted file mode 100644 index 950abbc2d..000000000 --- a/src/event_sources/rmt_esp32.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2022 Toitware ApS. -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; version -// 2.1 only. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// The license can be found in the file `LICENSE` in the top level -// directory of this repository. - -#pragma once - -#include "../resource.h" -#include "../os.h" - -namespace toit { - -class RMTEventSource : public EventSource, public Thread { - public: - static RMTEventSource* instance() { return _instance; } - - RMTEventSource(); - ~RMTEventSource(); - - private: - void entry(); - - void on_register_resource(Locker& locker, Resource* r) override; - void on_unregister_resource(Locker& locker, Resource* r) override; - - static RMTEventSource* _instance; - -}; - -} // namespace toit diff --git a/src/resources/gpio_esp32.cc b/src/resources/gpio_esp32.cc index a39f2f929..6d158bbff 100644 --- a/src/resources/gpio_esp32.cc +++ b/src/resources/gpio_esp32.cc @@ -103,7 +103,7 @@ PRIMITIVE(use) { IntResource* resource = resource_group->register_id(num); if (!resource) { - gpio_pins.put(num) + gpio_pins.put(num); MALLOC_FAILED; } proxy->set_external_address(resource); diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 4b94af1be..d49cb3128 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -14,13 +14,15 @@ // directory of this repository. #include "../top.h" + #ifdef TOIT_FREERTOS + +#include "driver/rmt.h" + #include "../primitive.h" #include "../resource.h" #include "../resource_pool.h" -#include "driver/rmt.h" - namespace toit { @@ -29,27 +31,22 @@ ResourcePool rmt_channels( RMT_CHANNEL_4, RMT_CHANNEL_5, RMT_CHANNEL_6, RMT_CHANNEL_7 ); -class RMTResourceGRoup : public ResourceGroup { +class RMTResourceGroup : public ResourceGroup { public: TAG(RMTResourceGroup); - explicit RMTResourceGroup(Process* process) - : ResourceGroup(process, GPIOEventSource::instance()) {} + RMTResourceGroup(Process* process) + : ResourceGroup(process, null) { } virtual void on_unregister_resource(Resource* r) { - rmt_channel_t channel = static_cast(static_castid()); - rmt_uninstall_driver(channel); + rmt_channel_t channel = static_cast(static_cast(r)->id()); + rmt_driver_uninstall(channel); rmt_channels.put(channel); - // TODO uninstall driver - } - private: - virtual uint32_t on_event(Resource* resource, word data, uint32_t state) { +}; - } -} -MODULE_IMPLEMENTATION(MODULE_RMT) +MODULE_IMPLEMENTATION(rmt, MODULE_RMT) PRIMITIVE(init) { ByteArray* proxy = process->object_heap()->allocate_proxy(); @@ -111,6 +108,7 @@ PRIMITIVE(config) { PRIMITIVE(read) { ARGS(int, rx_num) + return process->program()->null_object(); } PRIMITIVE(transfer) { @@ -130,6 +128,7 @@ PRIMITIVE(transfer) { PRIMITIVE(transfer_and_read) { ARGS(int, tx_num, int, rx_num, Blob, items_bytes) + return process->program()->null_object(); } } // namespace toit diff --git a/src/tags.h b/src/tags.h index aaa6d30a0..018303964 100644 --- a/src/tags.h +++ b/src/tags.h @@ -59,6 +59,7 @@ namespace toit { fn(MbedTLSResourceGroup) \ fn(UDPResourceGroup) \ fn(UARTResourceGroup) \ + fn(RMTResourceGroup) \ fn(WifiResourceGroup) \ fn(EthernetResourceGroup) \ fn(BLEResourceGroup) \ From c46466588556d0741f39c572747948fa71424a56 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 25 Feb 2022 13:40:44 +0100 Subject: [PATCH 05/84] First stab at primtiives --- src/primitive.h | 1 - src/resources/rmt_esp32.cc | 86 +++++++++++++++++++++++++++++--------- 2 files changed, 67 insertions(+), 20 deletions(-) diff --git a/src/primitive.h b/src/primitive.h index 290c669aa..40e64bb83 100644 --- a/src/primitive.h +++ b/src/primitive.h @@ -365,7 +365,6 @@ namespace toit { PRIMITIVE(use, 2) \ PRIMITIVE(unuse, 2) \ PRIMITIVE(config, 6) \ - PRIMITIVE(read, 1) \ PRIMITIVE(transfer, 2) \ PRIMITIVE(transfer_and_read, 3) \ diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index d49cb3128..4bfd3fba9 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -19,7 +19,9 @@ #include "driver/rmt.h" +#include "../objects_inline.h" #include "../primitive.h" +#include "../process.h" #include "../resource.h" #include "../resource_pool.h" @@ -46,7 +48,7 @@ class RMTResourceGroup : public ResourceGroup { }; -MODULE_IMPLEMENTATION(rmt, MODULE_RMT) +MODULE_IMPLEMENTATION(rmt, MODULE_RMT); PRIMITIVE(init) { ByteArray* proxy = process->object_heap()->allocate_proxy(); @@ -67,10 +69,10 @@ PRIMITIVE(use) { if (!rmt_channels.take(channel_num)) ALREADY_IN_USE; - // TODO install RMT driver for channel. + // TODO install RMT driver for channel? IntResource* resource = resource_group->register_id(channel_num); if (!resource) { - rmt_channels.take(put) + rmt_channels.put(channel_num); MALLOC_FAILED; } proxy->set_external_address(resource); @@ -88,37 +90,53 @@ PRIMITIVE(unuse) { } PRIMITIVE(config) { - ARGS(int, pin_num, int, channel_num, bool, rx, bool, tx int, mem_block_num) - if (rx == tx || mem_block_num < 2) INVALID_ARGUMENT; + ARGS(int, pin_num, int, channel_num, bool, is_tx, int, mem_block_num) + if (mem_block_num < 2) INVALID_ARGUMENT; - rmt_config_t config = rx ? RMT_DEFAULT_CONFIG_RX(pin_num, channel_num) : RMT_DEFAULT_CONFIG_TX(pin_num, channel_num); + // TODO: is there a better way to initialize this? + rmt_config_t config = { }; config.mem_block_num = mem_block_num; - + config.channel = (rmt_channel_t) channel_num; + config.gpio_num = (gpio_num_t) pin_num; // TODO: Allow additional paramters + config.clk_div = 80; + config.flags = 0; + config.rmt_mode = is_tx ? RMT_MODE_TX : RMT_MODE_RX; + if (is_tx) { + rmt_tx_config_t tx_config = { 0 }; + tx_config.carrier_freq_hz = 38000; + tx_config.carrier_level = RMT_CARRIER_LEVEL_HIGH; + tx_config.idle_level = RMT_IDLE_LEVEL_LOW; + tx_config.carrier_duty_percent = 33; + tx_config.carrier_en = false; + tx_config.loop_en = false; + tx_config.idle_output_en = true; + config.tx_config = tx_config; + } else { + rmt_rx_config_t rx_config = { 0 }; + rx_config.idle_threshold = 12000; + rx_config.filter_ticks_thresh = 100; + rx_config.filter_en = true; + config.rx_config = rx_config; + } esp_err_t err = rmt_config(&config); if (ESP_OK != err) return Primitive::os_error(err, process); - err = rmt_install_driver(channel_number, 0, 0); + err = rmt_driver_install((rmt_channel_t) channel_num, 0, 0); if (ESP_OK != err) return Primitive::os_error(err, process); return process->program()->null_object(); } -PRIMITIVE(read) { - ARGS(int, rx_num) - - return process->program()->null_object(); -} - PRIMITIVE(transfer) { - ARGS(int, tx_num, Blob, blob) + ARGS(int, tx_num, Blob, items_bytes) - if (item_bytes.length() % 4 != 0) INVALID_ARGUMENT; + if (items_bytes.length() % 4 != 0) INVALID_ARGUMENT; - rmt_item32_t* items = reinterpret_cast(items_bytes); + rmt_item32_t* items = reinterpret_cast(const_cast(items_bytes.address())); - esp_err_t err = rmt_write_items(tx_num, items, items_bytes.length() / 4, true); + esp_err_t err = rmt_write_items((rmt_channel_t) tx_num, items, items_bytes.length() / 4, true); if ( err != ESP_OK) return Primitive::os_error(err, process); @@ -126,10 +144,40 @@ PRIMITIVE(transfer) { } PRIMITIVE(transfer_and_read) { - ARGS(int, tx_num, int, rx_num, Blob, items_bytes) + ARGS(int, tx_num, int, rx_num, Blob, items_bytes, int, max_output_len) + + if (items_bytes.length() % 4 != 0) INVALID_ARGUMENT; + + Error* error = null; + ByteArray* data = process->allocate_byte_array(max_output_len, &error, /*force_external*/ true); + if (data == null) return error; + + rmt_item32_t* items = reinterpret_cast(const_cast(items_bytes.address())); + rmt_channel_t rx_channel = (rmt_channel_t) rx_num; + + RingbufHandle_t rb = NULL; + esp_err_t err = rmt_get_ringbuf_handle(rx_channel, &rb); + if (err != ESP_OK) return Primitive::os_error(err, process); + + rmt_rx_start(rx_channel, true); + err = rmt_write_items((rmt_channel_t) tx_num, items, items_bytes.length() / 4, true); + if (err != ESP_OK) return Primitive::os_error(err, process); + + size_t length = 0; + // TODO how many ticks should we actually wait? + void* received_items = xRingbufferReceive(rb, &length, 400); + + // TODO check whether length corresponds to rmt_item32_t? + + ByteArray::Bytes bytes(data); + memcpy(bytes.address(), received_items, length); + vRingbufferReturnItem(rb, received_items); + rmt_rx_stop(rx_channel); + data->resize_external(process, length); return process->program()->null_object(); } + } // namespace toit #endif // TOIT_FREERTOS From e7173e88c0680ec22b3355d8d1496ab9e6a451aa Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 25 Feb 2022 13:41:45 +0100 Subject: [PATCH 06/84] Add lib file --- lib/rmt.toit | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 lib/rmt.toit diff --git a/lib/rmt.toit b/lib/rmt.toit new file mode 100644 index 000000000..3efbe65e7 --- /dev/null +++ b/lib/rmt.toit @@ -0,0 +1,3 @@ +// Copyright (C) 2022 Toitware ApS. All rights reserved. +// Use of this source code is governed by an MIT-style license that can be +// found in the lib/LICENSE file. From facddf1188f4e22a8b8c4288d0e2afce0777abb5 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 25 Feb 2022 13:46:57 +0100 Subject: [PATCH 07/84] Update primitive parameter amount --- src/primitive.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/primitive.h b/src/primitive.h index 40e64bb83..1fecb4fe1 100644 --- a/src/primitive.h +++ b/src/primitive.h @@ -364,9 +364,9 @@ namespace toit { PRIMITIVE(init, 0) \ PRIMITIVE(use, 2) \ PRIMITIVE(unuse, 2) \ - PRIMITIVE(config, 6) \ + PRIMITIVE(config, 4) \ PRIMITIVE(transfer, 2) \ - PRIMITIVE(transfer_and_read, 3) \ + PRIMITIVE(transfer_and_read, 4) \ #define MODULE_CRYPTO(PRIMITIVE) \ PRIMITIVE(sha1_start, 1) \ From b594a5f93c2113b629a9d4cd4e1361715ae3d2ef Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 25 Feb 2022 13:47:10 +0100 Subject: [PATCH 08/84] Add primitives --- lib/rmt.toit | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/rmt.toit b/lib/rmt.toit index 3efbe65e7..6a3c93618 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -1,3 +1,21 @@ // Copyright (C) 2022 Toitware ApS. All rights reserved. // Use of this source code is governed by an MIT-style license that can be // found in the lib/LICENSE file. + +rmt_init_: + #primitive.rmt.init + +rmt_use_ resource_group channel_num: + #primitive.rmt.use + +rmt_unuse_ resource_group resource: + #primitive.rmt.unuse + +rmt_config_ pin_num/int channel_num/int is_tx/bool mem_block_num/int: + #primitive.rmt.config + +rmt_transfer_ tx_num/int items_bytes/*/Blob*/: + #primitive.rmt.transfer + +rmt_transfer_and_read_ tx_num/int rx_num/int items_bytes max_output_len/int: + #primitive.rmt.transfer_and_read From bf0f0c689b145da8fb5d5d99c0aac1b2fcfafe9d Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 25 Feb 2022 14:03:35 +0100 Subject: [PATCH 09/84] Add resource group --- lib/rmt.toit | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/rmt.toit b/lib/rmt.toit index 6a3c93618..9bfae67cd 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -2,6 +2,9 @@ // Use of this source code is governed by an MIT-style license that can be // found in the lib/LICENSE file. + +resource_group_ ::= rmt_init_ + rmt_init_: #primitive.rmt.init From ddb7f997d282ce9d538aadb44dba347b9c835dc3 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 25 Feb 2022 16:05:31 +0100 Subject: [PATCH 10/84] WIP on toit level rmt lib --- lib/rmt.toit | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 9bfae67cd..1d876d395 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -2,6 +2,90 @@ // Use of this source code is governed by an MIT-style license that can be // found in the lib/LICENSE file. +import gpio + +class Item: + value/int + period/int + + constructor value period: + this.period = period & 0x7FFF + this.value = value & 0b1 + + constructor.from_bytes bytes/ByteArray index/int: + period = (bytes[index] << 8 | bytes[index + 1]) >> 1 + value = bytes[index + 1] & 0x01 + + first_byte -> int: + return period >> 7 + + second_byte -> int: + return ((period << 1) & 0xFF) | value + +class Controller: + rx_ch/int? + rx/gpio.Pin? + tx_ch/int? + tx/gpio.Pin? + + rmt_rx_ := null + rmt_tx_ := null + + constructor --.rx --.tx --.rx_ch --.tx_ch: + if (not rx) and (not tx): throw "INVALID_ARGUMENT" + if (rx and not rx_ch) or (not rx and rx_ch): throw "INVALID_ARGUMENT" + if (tx and not tx_ch) or (not tx and tx_ch): throw "INVALID_ARGUMENT" + if rx: rmt_rx_ = rmt_use_ resource_group_ rx_ch + if tx: rmt_tx_ = rmt_use_ resource_group_ tx_ch + + // TODO config? + + config: + throw "not implemented" + + transfer items/List/**/: + if not rmt_tx_: throw "not configured for transfer" + + rmt_transfer_ tx_ch + items_to_bytes_ items + + transfer_and_read items max_items_size: + throw "not implemented" + + bytes_to_items_ bytes/ByteArray -> List: + items_size := bytes.size / 2 + result := List items_size + items_size.repeat: + result.add + Item.from_bytes bytes it * 2 + return result + + items_to_bytes_ items/List/**/ -> ByteArray: + should_pad := items.size % 2 == 0 + // Ensure there is an even number of items. + bytes_size := should_pad ? items.size * 2 + 2 : items.size * 2 + bytes := ByteArray bytes_size + idx := 0 + + items.do: | item/Item | + bytes[idx] = item.first_byte + bytes[idx + 1] = item.second_byte + idx += 2 + + if should_pad: + bytes[idx] = 0 + bytes[idx + 1] = 0 + + return bytes + + close: + if rmt_rx_: + rmt_unuse_ resource_group_ rmt_rx_ + rmt_rx_ = null + if rmt_tx_: + rmt_unuse_ resource_group_ rmt_tx_ + rmt_tx_ = null + resource_group_ ::= rmt_init_ @@ -17,8 +101,8 @@ rmt_unuse_ resource_group resource: rmt_config_ pin_num/int channel_num/int is_tx/bool mem_block_num/int: #primitive.rmt.config -rmt_transfer_ tx_num/int items_bytes/*/Blob*/: +rmt_transfer_ tx_ch/int items_bytes/*/Blob*/: #primitive.rmt.transfer -rmt_transfer_and_read_ tx_num/int rx_num/int items_bytes max_output_len/int: +rmt_transfer_and_read_ tx_ch/int rx_ch/int items_bytes max_output_len/int: #primitive.rmt.transfer_and_read From 059f50fb0e7059ba4c1143f1bab5dd90c6b078d0 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Tue, 1 Mar 2022 11:28:29 +0100 Subject: [PATCH 11/84] Add config to controller --- lib/rmt.toit | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 1d876d395..46fa7323c 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -38,10 +38,11 @@ class Controller: if rx: rmt_rx_ = rmt_use_ resource_group_ rx_ch if tx: rmt_tx_ = rmt_use_ resource_group_ tx_ch - // TODO config? + config_ - config: - throw "not implemented" + config_: + if rx: rmt_config_ rx.num rx_ch false 500 + if tx: rmt_config_ tx.num tx_ch true 0 transfer items/List/**/: if not rmt_tx_: throw "not configured for transfer" From 57e2ab194c392adcf51860f44cf81234adcdb580 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Tue, 1 Mar 2022 11:28:37 +0100 Subject: [PATCH 12/84] Add transfer and read to controller --- lib/rmt.toit | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 46fa7323c..3e2cad8cd 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -51,14 +51,19 @@ class Controller: items_to_bytes_ items transfer_and_read items max_items_size: - throw "not implemented" + max_output_len := 4096 + result := rmt_transfer_and_read_ tx_ch rx_ch + items_to_bytes_ items + max_output_len + + return bytes_to_items_ items bytes_to_items_ bytes/ByteArray -> List: items_size := bytes.size / 2 result := List items_size items_size.repeat: result.add - Item.from_bytes bytes it * 2 + Item.from_bytes it * 2 bytes return result items_to_bytes_ items/List/**/ -> ByteArray: From 5d62fe010fc84fbb3cca51b21e29fcdcafd2e053 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Tue, 1 Mar 2022 11:28:46 +0100 Subject: [PATCH 13/84] Add == operator to item --- lib/rmt.toit | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/rmt.toit b/lib/rmt.toit index 3e2cad8cd..423b19370 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -22,6 +22,11 @@ class Item: second_byte -> int: return ((period << 1) & 0xFF) | value + operator == other/any: + if other is not Item: return false + + return value == other.value and period == other.period + class Controller: rx_ch/int? rx/gpio.Pin? From 8d94d13611d1e8381a1e491c4e30ff1047962f24 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Tue, 1 Mar 2022 11:28:53 +0100 Subject: [PATCH 14/84] SWap Item from bytes constructor argument --- lib/rmt.toit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 423b19370..d1f392de8 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -12,7 +12,7 @@ class Item: this.period = period & 0x7FFF this.value = value & 0b1 - constructor.from_bytes bytes/ByteArray index/int: + constructor.from_bytes index/int bytes/ByteArray: period = (bytes[index] << 8 | bytes[index + 1]) >> 1 value = bytes[index + 1] & 0x01 From 00c6d23d280d6ca4f13410e7bb66695a14e7b9e2 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Tue, 1 Mar 2022 11:29:02 +0100 Subject: [PATCH 15/84] Add tests --- tests/rmt_item_test.toit | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tests/rmt_item_test.toit diff --git a/tests/rmt_item_test.toit b/tests/rmt_item_test.toit new file mode 100644 index 000000000..4d69d5226 --- /dev/null +++ b/tests/rmt_item_test.toit @@ -0,0 +1,52 @@ +// Copyright (C) 2022 Toitware ApS. +// Use of this source code is governed by a Zero-Clause BSD license that can +// be found in the tests/LICENSE file. + +import expect show * +import rmt show Item + +main: + test_item_construction + test_item_construction_truncates_values + test_item_serialization + +test_item_construction: + item := Item 0 1024 + expect_equals 0 item.value + expect_equals 1024 item.period + + item = Item 1 0 + expect_equals 1 item.value + expect_equals 0 item.period + +test_item_construction_truncates_values: + item := Item 2 0xFFFF + expect_equals 0 item.value + expect_equals 0x7FFF item.period + +test_item_serialization: + item := Item 0 0 + expect_bytes_equal + #[0x00,0x00] + #[item.first_byte, item.second_byte] + + item = Item 1 0 + expect_bytes_equal + #[0x00,0x01] + #[item.first_byte, item.second_byte] + + item = Item 0 0x7FFF + expect_bytes_equal + #[0xFF,0xFE] + #[item.first_byte, item.second_byte] + + item = Item 0 0x700F + expect_bytes_equal + #[0xE0,0x1E] + #[item.first_byte, item.second_byte] + + item = Item 0 1024 + expect_equals + item + Item.from_bytes 0 + ByteArray 2: it == 0 ? item.first_byte : item.second_byte From 37815f72a69900e2f5d945797d55122dce8ae811 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Wed, 2 Mar 2022 15:44:35 +0100 Subject: [PATCH 16/84] Update config primitives --- lib/rmt.toit | 41 ++++++++++++++++--- src/primitive.h | 20 +++++++++- src/resources/rmt_esp32.cc | 82 ++++++++++++++++++++++++-------------- 3 files changed, 107 insertions(+), 36 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index d1f392de8..2136c3c58 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -42,12 +42,38 @@ class Controller: if (tx and not tx_ch) or (not tx and tx_ch): throw "INVALID_ARGUMENT" if rx: rmt_rx_ = rmt_use_ resource_group_ rx_ch if tx: rmt_tx_ = rmt_use_ resource_group_ tx_ch - config_ config_: - if rx: rmt_config_ rx.num rx_ch false 500 - if tx: rmt_config_ tx.num tx_ch true 0 + if rx: config_rx_ --pin_num=rx.num --channel_num=rx_ch --mem_block_num=1 + if tx: config_tx_ --pin_num=tx.num --channel_num=tx_ch + + config_rx_ + --pin_num/int + --channel_num/int + --mem_block_num/int=1 + --clk_div/int=80 + --flags/int=0 + --idle_threshold/int=12000 + --filter_en/bool=true + --filter_ticks_thresh/int=100: + rmt_config_rx_ rx.num rx_ch mem_block_num clk_div flags idle_threshold filter_en filter_ticks_thresh + + config_rx_ + --pin_num/int + --channel_num/int + --mem_block_num/int=1 + --clk_div/int=80 + --flags/int=0 + --carrier_en/bool=false + --carrier_freq_hz/int=38000 + --carrier_level/int=1 + --carrier_duty_percent/int=33 + --loop_en/bool=false + --idle_output_en/bool=true + --idle_level/int=0: + rmt_config_tx_ tx.num tx_ch mem_block_num clk_div flags carrier_en carrier_freq_hz carrier_level carrier_duty_percent loop_en idle_output_en idle_level + transfer items/List/**/: if not rmt_tx_: throw "not configured for transfer" @@ -109,8 +135,13 @@ rmt_use_ resource_group channel_num: rmt_unuse_ resource_group resource: #primitive.rmt.unuse -rmt_config_ pin_num/int channel_num/int is_tx/bool mem_block_num/int: - #primitive.rmt.config +rmt_config_rx_ pin_num/int channel_num/int mem_block_num/int clk_div/int flags/int idle_threshold/int filter_en/bool filter_ticks_thresh/int: + #primitive.rmt.config_rx + +rmt_config_tx_ pin_num/int channel_num/int mem_block_num/int clk_div/int flags/int + carrier_en/bool carrier_freq_hz/int carrier_level/int carrier_duty_percent/int + loop_en/bool idle_output_en/bool idle_level/int: + #primitive.rmt.config_tx rmt_transfer_ tx_ch/int items_bytes/*/Blob*/: #primitive.rmt.transfer diff --git a/src/primitive.h b/src/primitive.h index 1fecb4fe1..7152b99ff 100644 --- a/src/primitive.h +++ b/src/primitive.h @@ -364,7 +364,8 @@ namespace toit { PRIMITIVE(init, 0) \ PRIMITIVE(use, 2) \ PRIMITIVE(unuse, 2) \ - PRIMITIVE(config, 4) \ + PRIMITIVE(config_rx, 8) \ + PRIMITIVE(config_tx, 12) \ PRIMITIVE(transfer, 2) \ PRIMITIVE(transfer_and_read, 4) \ @@ -893,10 +894,25 @@ namespace toit { _A_T_##t10(9, n10); \ _A_T_##t11(10, n11); -#define _OVERRIDE(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, NAME, ...) NAME +#define _A_24(t1, n1, t2, n2, t3, n3, t4, n4, t5, n5, t6, n6, t7, n7, t8, n8, t9, n9, t10, n10, t11, n11, t12, n12) \ + _A_T_##t1(0, n1); \ + _A_T_##t2(1, n2); \ + _A_T_##t3(2, n3); \ + _A_T_##t4(3, n4); \ + _A_T_##t5(4, n5); \ + _A_T_##t6(5, n6); \ + _A_T_##t7(6, n7); \ + _A_T_##t8(7, n8); \ + _A_T_##t9(8, n9); \ + _A_T_##t10(9, n10); \ + _A_T_##t11(10, n11); \ + _A_T_##t12(11, n12); + +#define _OVERRIDE(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _34, NAME, ...) NAME #define ARGS(...) \ _OVERRIDE(__VA_ARGS__, \ + _A_24, _ODD, \ _A_22, _ODD, \ _A_20, _ODD, \ _A_18, _ODD, \ diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 4bfd3fba9..54e767de6 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -89,41 +89,65 @@ PRIMITIVE(unuse) { return process->program()->null_object(); } -PRIMITIVE(config) { - ARGS(int, pin_num, int, channel_num, bool, is_tx, int, mem_block_num) - if (mem_block_num < 2) INVALID_ARGUMENT; +esp_err_t configure(const rmt_config_t* config, rmt_channel_t channel_num, Process* process) { + esp_err_t err = rmt_config(config); + if (ESP_OK != err) return err; + + err = rmt_driver_install((rmt_channel_t) channel_num, 0, 0); + if (ESP_OK != err) return err; + return err; +} + +PRIMITIVE(config_tx) { + ARGS(int, pin_num, int, channel_num, int, mem_block_num, int, clk_div, int, flags, + bool, carrier_en, int, carrier_freq_hz, int, carrier_level, int, carrier_duty_percent, + bool, loop_en, bool, idle_output_en, int, idle_level) // TODO: is there a better way to initialize this? rmt_config_t config = { }; - config.mem_block_num = mem_block_num; - config.channel = (rmt_channel_t) channel_num; - config.gpio_num = (gpio_num_t) pin_num; - // TODO: Allow additional paramters - config.clk_div = 80; - config.flags = 0; - config.rmt_mode = is_tx ? RMT_MODE_TX : RMT_MODE_RX; - if (is_tx) { - rmt_tx_config_t tx_config = { 0 }; - tx_config.carrier_freq_hz = 38000; - tx_config.carrier_level = RMT_CARRIER_LEVEL_HIGH; - tx_config.idle_level = RMT_IDLE_LEVEL_LOW; - tx_config.carrier_duty_percent = 33; - tx_config.carrier_en = false; - tx_config.loop_en = false; - tx_config.idle_output_en = true; - config.tx_config = tx_config; - } else { - rmt_rx_config_t rx_config = { 0 }; - rx_config.idle_threshold = 12000; - rx_config.filter_ticks_thresh = 100; - rx_config.filter_en = true; - config.rx_config = rx_config; - } - esp_err_t err = rmt_config(&config); + config.gpio_num = (gpio_num_t) pin_num; + config.channel = (rmt_channel_t) channel_num; + config.mem_block_num = mem_block_num; + config.clk_div = clk_div; + config.flags = flags; + config.rmt_mode = RMT_MODE_TX; + rmt_tx_config_t tx_config = { 0 }; + tx_config.carrier_en = carrier_en; + tx_config.carrier_freq_hz = carrier_freq_hz; + tx_config.carrier_level = (rmt_carrier_level_t) carrier_level; + tx_config.carrier_duty_percent = carrier_duty_percent; + tx_config.loop_en = loop_en; + tx_config.idle_output_en = idle_output_en; + tx_config.idle_level = (rmt_idle_level_t) idle_level; + config.tx_config = tx_config; + + esp_err_t err = configure(&config, (rmt_channel_t) channel_num, process); if (ESP_OK != err) return Primitive::os_error(err, process); - err = rmt_driver_install((rmt_channel_t) channel_num, 0, 0); + return process->program()->null_object(); +} + +PRIMITIVE(config_rx) { + ARGS(int, pin_num, int, channel_num, int, mem_block_num, int, clk_div, int, flags, + int, idle_threshold, bool, filter_en, int, filter_ticks_thresh) + + // TODO: is there a better way to initialize this? + rmt_config_t config = { }; + + config.gpio_num = (gpio_num_t) pin_num; + config.channel = (rmt_channel_t) channel_num; + config.mem_block_num = mem_block_num; + config.clk_div = clk_div; + config.flags = flags; + config.rmt_mode = RMT_MODE_RX; + rmt_rx_config_t rx_config = { 0 }; + rx_config.idle_threshold = idle_threshold; + rx_config.filter_en = filter_en; + rx_config.filter_ticks_thresh = filter_ticks_thresh; + config.rx_config = rx_config; + + esp_err_t err = configure(&config,(rmt_channel_t) channel_num, process); if (ESP_OK != err) return Primitive::os_error(err, process); return process->program()->null_object(); From c6020e5ecb9f7392a2634709ecd890ffd6afaf63 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Thu, 3 Mar 2022 12:27:26 +0100 Subject: [PATCH 17/84] WIP --- lib/rmt.toit | 29 +++++++++++++++-------------- src/resources/rmt_esp32.cc | 23 +++++++++++++++++------ 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 2136c3c58..bc8bb3dc2 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -42,13 +42,9 @@ class Controller: if (tx and not tx_ch) or (not tx and tx_ch): throw "INVALID_ARGUMENT" if rx: rmt_rx_ = rmt_use_ resource_group_ rx_ch if tx: rmt_tx_ = rmt_use_ resource_group_ tx_ch - config_ - config_: - if rx: config_rx_ --pin_num=rx.num --channel_num=rx_ch --mem_block_num=1 - if tx: config_tx_ --pin_num=tx.num --channel_num=tx_ch - config_rx_ + config_rx --pin_num/int --channel_num/int --mem_block_num/int=1 @@ -59,7 +55,7 @@ class Controller: --filter_ticks_thresh/int=100: rmt_config_rx_ rx.num rx_ch mem_block_num clk_div flags idle_threshold filter_en filter_ticks_thresh - config_rx_ + config_tx --pin_num/int --channel_num/int --mem_block_num/int=1 @@ -81,24 +77,28 @@ class Controller: rmt_transfer_ tx_ch items_to_bytes_ items - transfer_and_read items max_items_size: + transfer_and_read items max_items_size -> List: max_output_len := 4096 + print "len: $((items_to_bytes_ items).size) " result := rmt_transfer_and_read_ tx_ch rx_ch items_to_bytes_ items max_output_len - - return bytes_to_items_ items + print "convert to items" + return bytes_to_items_ result bytes_to_items_ bytes/ByteArray -> List: items_size := bytes.size / 2 - result := List items_size + print "construct result list" + result := [] + print "convert bytes to items" items_size.repeat: result.add Item.from_bytes it * 2 bytes + print "return result" return result items_to_bytes_ items/List/**/ -> ByteArray: - should_pad := items.size % 2 == 0 + should_pad := items.size % 2 == 1 // Ensure there is an even number of items. bytes_size := should_pad ? items.size * 2 + 2 : items.size * 2 bytes := ByteArray bytes_size @@ -135,12 +135,13 @@ rmt_use_ resource_group channel_num: rmt_unuse_ resource_group resource: #primitive.rmt.unuse -rmt_config_rx_ pin_num/int channel_num/int mem_block_num/int clk_div/int flags/int idle_threshold/int filter_en/bool filter_ticks_thresh/int: +rmt_config_rx_ pin_num/int channel_num/int mem_block_num/int clk_div/int flags/int + idle_threshold/int filter_en/bool filter_ticks_thresh/int: #primitive.rmt.config_rx rmt_config_tx_ pin_num/int channel_num/int mem_block_num/int clk_div/int flags/int - carrier_en/bool carrier_freq_hz/int carrier_level/int carrier_duty_percent/int - loop_en/bool idle_output_en/bool idle_level/int: + carrier_en/bool carrier_freq_hz/int carrier_level/int carrier_duty_percent/int + loop_en/bool idle_output_en/bool idle_level/int: #primitive.rmt.config_tx rmt_transfer_ tx_ch/int items_bytes/*/Blob*/: diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 54e767de6..b3df46281 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -89,11 +89,13 @@ PRIMITIVE(unuse) { return process->program()->null_object(); } -esp_err_t configure(const rmt_config_t* config, rmt_channel_t channel_num, Process* process) { + +// HELPER +esp_err_t configure(const rmt_config_t* config, rmt_channel_t channel_num, size_t rx_buffer_size, Process* process) { esp_err_t err = rmt_config(config); if (ESP_OK != err) return err; - err = rmt_driver_install((rmt_channel_t) channel_num, 0, 0); + err = rmt_driver_install((rmt_channel_t) channel_num, rx_buffer_size, 0); if (ESP_OK != err) return err; return err; } @@ -122,7 +124,7 @@ PRIMITIVE(config_tx) { tx_config.idle_level = (rmt_idle_level_t) idle_level; config.tx_config = tx_config; - esp_err_t err = configure(&config, (rmt_channel_t) channel_num, process); + esp_err_t err = configure(&config, (rmt_channel_t) channel_num, 0, process); if (ESP_OK != err) return Primitive::os_error(err, process); return process->program()->null_object(); @@ -147,7 +149,7 @@ PRIMITIVE(config_rx) { rx_config.filter_ticks_thresh = filter_ticks_thresh; config.rx_config = rx_config; - esp_err_t err = configure(&config,(rmt_channel_t) channel_num, process); + esp_err_t err = configure(&config,(rmt_channel_t) channel_num, 1000, process); if (ESP_OK != err) return Primitive::os_error(err, process); return process->program()->null_object(); @@ -169,37 +171,46 @@ PRIMITIVE(transfer) { PRIMITIVE(transfer_and_read) { ARGS(int, tx_num, int, rx_num, Blob, items_bytes, int, max_output_len) - + printf("begin\n"); if (items_bytes.length() % 4 != 0) INVALID_ARGUMENT; + printf("allocate\n"); Error* error = null; ByteArray* data = process->allocate_byte_array(max_output_len, &error, /*force_external*/ true); if (data == null) return error; + printf("get them items\n"); rmt_item32_t* items = reinterpret_cast(const_cast(items_bytes.address())); rmt_channel_t rx_channel = (rmt_channel_t) rx_num; + printf("give me buffer\n"); RingbufHandle_t rb = NULL; esp_err_t err = rmt_get_ringbuf_handle(rx_channel, &rb); if (err != ESP_OK) return Primitive::os_error(err, process); + printf("start read\n"); rmt_rx_start(rx_channel, true); + printf("write\n"); err = rmt_write_items((rmt_channel_t) tx_num, items, items_bytes.length() / 4, true); if (err != ESP_OK) return Primitive::os_error(err, process); size_t length = 0; // TODO how many ticks should we actually wait? + printf("get items\n"); void* received_items = xRingbufferReceive(rb, &length, 400); // TODO check whether length corresponds to rmt_item32_t? + printf("prepare result\n"); ByteArray::Bytes bytes(data); memcpy(bytes.address(), received_items, length); + printf("return buffer\n"); vRingbufferReturnItem(rb, received_items); + printf("stop reading\n"); rmt_rx_stop(rx_channel); data->resize_external(process, length); - return process->program()->null_object(); + return data; } From 7ca4dc475968d3a78470c2029c8f4ea3c96402a9 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 4 Mar 2022 13:45:28 +0100 Subject: [PATCH 18/84] Fix RMT item serialization --- lib/rmt.toit | 8 ++++---- tests/rmt_item_test.toit | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index bc8bb3dc2..be27f076d 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -13,14 +13,14 @@ class Item: this.value = value & 0b1 constructor.from_bytes index/int bytes/ByteArray: - period = (bytes[index] << 8 | bytes[index + 1]) >> 1 - value = bytes[index + 1] & 0x01 + period = (bytes[index] | ((bytes[index + 1]) & 0xEF) << 8) + value = bytes[index + 1] & 0x80 first_byte -> int: - return period >> 7 + return period & 0xFF second_byte -> int: - return ((period << 1) & 0xFF) | value + return (period >> 8 ) | (value << 7) operator == other/any: if other is not Item: return false diff --git a/tests/rmt_item_test.toit b/tests/rmt_item_test.toit index 4d69d5226..6dea231fe 100644 --- a/tests/rmt_item_test.toit +++ b/tests/rmt_item_test.toit @@ -32,17 +32,27 @@ test_item_serialization: item = Item 1 0 expect_bytes_equal - #[0x00,0x01] + #[0x00,0x80] #[item.first_byte, item.second_byte] item = Item 0 0x7FFF expect_bytes_equal - #[0xFF,0xFE] + #[0xFF,0x7F] + #[item.first_byte, item.second_byte] + + item = Item 1 0x7FFF + expect_bytes_equal + #[0xFF,0xFF] #[item.first_byte, item.second_byte] item = Item 0 0x700F expect_bytes_equal - #[0xE0,0x1E] + #[0x0F,0x70] + #[item.first_byte, item.second_byte] + + item = Item 1 0x700F + expect_bytes_equal + #[0x0F,0xF0] #[item.first_byte, item.second_byte] item = Item 0 1024 From c03fd2c85cc42a53f3a109ac2e724b86acb56feb Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 4 Mar 2022 13:52:27 +0100 Subject: [PATCH 19/84] Add Item Toit doc --- lib/rmt.toit | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/rmt.toit b/lib/rmt.toit index be27f076d..3fa52a014 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -4,6 +4,17 @@ import gpio +/** +Each RMT item consists of a value/level and a period. + +# Advanced +When an RMT value is written, then the given value is sustained for the given + period. The period is specified in number of ticks, so the actual time the + value is sustained is determined by the RMT controller configuration. + +At the lower level, an item consits of 16 bits: 15 bits for the period and 1 + bit for the value/level. +*/ class Item: value/int period/int From f3ad0288d1fad179dcda31fdd3a124f353a74007 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 4 Mar 2022 13:52:49 +0100 Subject: [PATCH 20/84] Check for error when starting rx --- src/resources/rmt_esp32.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index b3df46281..f7ec2867a 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -189,7 +189,8 @@ PRIMITIVE(transfer_and_read) { if (err != ESP_OK) return Primitive::os_error(err, process); printf("start read\n"); - rmt_rx_start(rx_channel, true); + err = rmt_rx_start(rx_channel, true); + if (err != ESP_OK) return Primitive::os_error(err, process); printf("write\n"); err = rmt_write_items((rmt_channel_t) tx_num, items, items_bytes.length() / 4, true); if (err != ESP_OK) return Primitive::os_error(err, process); From aaaa4bfb0f2ff99ce5b9f78d26c141b1a4140297 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 4 Mar 2022 13:53:07 +0100 Subject: [PATCH 21/84] Add TODO --- src/resources/rmt_esp32.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index f7ec2867a..135126be8 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -208,6 +208,7 @@ PRIMITIVE(transfer_and_read) { printf("return buffer\n"); vRingbufferReturnItem(rb, received_items); printf("stop reading\n"); + // TODO check error? rmt_rx_stop(rx_channel); data->resize_external(process, length); From 56d7467c37f0571dda1b9881a11ff1527138183a Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 4 Mar 2022 15:20:42 +0100 Subject: [PATCH 22/84] Fix value initialization in Item construction from bytes --- lib/rmt.toit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 3fa52a014..65a4143b8 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -25,7 +25,7 @@ class Item: constructor.from_bytes index/int bytes/ByteArray: period = (bytes[index] | ((bytes[index + 1]) & 0xEF) << 8) - value = bytes[index + 1] & 0x80 + value = (bytes[index + 1] >> 7) & 0b1 first_byte -> int: return period & 0xFF From 2a34f2916f7d8e753e361793b356d1ff145c57f1 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 13:26:13 +0100 Subject: [PATCH 23/84] Add item serialization test --- tests/rmt_item_test.toit | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/rmt_item_test.toit b/tests/rmt_item_test.toit index 6dea231fe..9bb118585 100644 --- a/tests/rmt_item_test.toit +++ b/tests/rmt_item_test.toit @@ -60,3 +60,9 @@ test_item_serialization: item Item.from_bytes 0 ByteArray 2: it == 0 ? item.first_byte : item.second_byte + + item = Item 1 0 + expect_equals + item + Item.from_bytes 0 + ByteArray 2: it == 0 ? item.first_byte : item.second_byte From 4ee99592bdeac1aa8779384bd10c1b3f0c17cc6d Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 13:26:32 +0100 Subject: [PATCH 24/84] This seems to work --- lib/rmt.toit | 25 ++++++++++++++------- src/primitive.h | 1 + src/resources/rmt_esp32.cc | 45 +++++++++++++++++++++++++++++++------- 3 files changed, 55 insertions(+), 16 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 65a4143b8..562b0200c 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -19,13 +19,14 @@ class Item: value/int period/int + // TODO: swap params constructor value period: this.period = period & 0x7FFF this.value = value & 0b1 constructor.from_bytes index/int bytes/ByteArray: - period = (bytes[index] | ((bytes[index + 1]) & 0xEF) << 8) - value = (bytes[index + 1] >> 7) & 0b1 + period = bytes[index] | ((bytes[index + 1] & 0x7F) << 8) + value = bytes[index + 1] >> 7 first_byte -> int: return period & 0xFF @@ -38,14 +39,17 @@ class Item: return value == other.value and period == other.period + stringify -> string: + return "($period, $value)" + class Controller: rx_ch/int? rx/gpio.Pin? tx_ch/int? tx/gpio.Pin? - rmt_rx_ := null - rmt_tx_ := null + rmt_rx_/ByteArray? := null + rmt_tx_/ByteArray? := null constructor --.rx --.tx --.rx_ch --.tx_ch: if (not rx) and (not tx): throw "INVALID_ARGUMENT" @@ -90,19 +94,21 @@ class Controller: transfer_and_read items max_items_size -> List: max_output_len := 4096 - print "len: $((items_to_bytes_ items).size) " + bytes := items_to_bytes_ items + print "len: $(bytes.size), $bytes" result := rmt_transfer_and_read_ tx_ch rx_ch - items_to_bytes_ items + bytes max_output_len print "convert to items" return bytes_to_items_ result bytes_to_items_ bytes/ByteArray -> List: items_size := bytes.size / 2 - print "construct result list" + print "construct result list $items_size" result := [] print "convert bytes to items" items_size.repeat: + print "add item" result.add Item.from_bytes it * 2 bytes print "return result" @@ -158,5 +164,8 @@ rmt_config_tx_ pin_num/int channel_num/int mem_block_num/int clk_div/int flags/i rmt_transfer_ tx_ch/int items_bytes/*/Blob*/: #primitive.rmt.transfer -rmt_transfer_and_read_ tx_ch/int rx_ch/int items_bytes max_output_len/int: +rmt_transfer_and_read_ tx_ch/int rx_ch/int items_bytes/*/Blob*/ max_output_len/int: #primitive.rmt.transfer_and_read + +rmt_val_to_item dur0 lvl0 dur1 lvl1: + #primitive.rmt.val_to_item diff --git a/src/primitive.h b/src/primitive.h index 1215bef05..33691debb 100644 --- a/src/primitive.h +++ b/src/primitive.h @@ -374,6 +374,7 @@ namespace toit { PRIMITIVE(init, 0) \ PRIMITIVE(use, 2) \ PRIMITIVE(unuse, 2) \ + PRIMITIVE(val_to_item, 4)\ PRIMITIVE(config_rx, 8) \ PRIMITIVE(config_tx, 12) \ PRIMITIVE(transfer, 2) \ diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 135126be8..e1796eaa3 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -95,6 +95,9 @@ esp_err_t configure(const rmt_config_t* config, rmt_channel_t channel_num, size_ esp_err_t err = rmt_config(config); if (ESP_OK != err) return err; + err = rmt_set_source_clk(channel_num, RMT_BASECLK_APB); + if (ESP_OK != err) return err; + err = rmt_driver_install((rmt_channel_t) channel_num, rx_buffer_size, 0); if (ESP_OK != err) return err; return err; @@ -169,6 +172,20 @@ PRIMITIVE(transfer) { return process->program()->null_object(); } +// TODO delete this primitive before committing. +PRIMITIVE(val_to_item) { + ARGS(uint32, dur0, uint32, lvl0, uint32, dur1, uint32, lvl1) + Error* error = null; + ByteArray* data = process->allocate_byte_array(4, &error); + if (data == null) return error; + + rmt_item32_t item = {dur0, lvl0, dur1, lvl1}; + + ByteArray::Bytes bytes(data); + memcpy(bytes.address(), reinterpret_cast(&item), 4); + return data; +} + PRIMITIVE(transfer_and_read) { ARGS(int, tx_num, int, rx_num, Blob, items_bytes, int, max_output_len) printf("begin\n"); @@ -180,7 +197,8 @@ PRIMITIVE(transfer_and_read) { if (data == null) return error; printf("get them items\n"); - rmt_item32_t* items = reinterpret_cast(const_cast(items_bytes.address())); + const rmt_item32_t* items = reinterpret_cast(items_bytes.address()); + printf("dur0: %d val0: %d dur1: %d val1: %d\n", items[0].duration0, items[0].level0, items[0].duration1, items[0].level1); rmt_channel_t rx_channel = (rmt_channel_t) rx_num; printf("give me buffer\n"); @@ -191,22 +209,33 @@ PRIMITIVE(transfer_and_read) { printf("start read\n"); err = rmt_rx_start(rx_channel, true); if (err != ESP_OK) return Primitive::os_error(err, process); - printf("write\n"); + + printf("write (len %d)\n", items_bytes.length() / 4); err = rmt_write_items((rmt_channel_t) tx_num, items, items_bytes.length() / 4, true); if (err != ESP_OK) return Primitive::os_error(err, process); size_t length = 0; + // TODO how many ticks should we actually wait? printf("get items\n"); - void* received_items = xRingbufferReceive(rb, &length, 400); + void* received_bytes = xRingbufferReceive(rb, &length, 5000); + + printf("length: %d\n", length); + // TODO remove this before commit: + rmt_item32_t* received_items = reinterpret_cast(received_bytes); + if (length > 0) { + printf("received item... dur0: %d val0: %d dur1: %d val1: %d\n", received_items[0].duration0, received_items[0].level0, received_items[0].duration1, received_items[0].level1); + + printf("prepare result\n"); + ByteArray::Bytes bytes(data); + memcpy(bytes.address(), received_bytes, length); + printf("return buffer\n"); + vRingbufferReturnItem(rb, received_bytes); + + } // TODO check whether length corresponds to rmt_item32_t? - printf("prepare result\n"); - ByteArray::Bytes bytes(data); - memcpy(bytes.address(), received_items, length); - printf("return buffer\n"); - vRingbufferReturnItem(rb, received_items); printf("stop reading\n"); // TODO check error? rmt_rx_stop(rx_channel); From 72e16ae53311eaaf4338a99e3afbc049ef2a5cf0 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 13:30:45 +0100 Subject: [PATCH 25/84] Remove val to item test primitive --- lib/rmt.toit | 3 --- src/primitive.h | 1 - src/resources/rmt_esp32.cc | 14 -------------- 3 files changed, 18 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 562b0200c..de5285621 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -166,6 +166,3 @@ rmt_transfer_ tx_ch/int items_bytes/*/Blob*/: rmt_transfer_and_read_ tx_ch/int rx_ch/int items_bytes/*/Blob*/ max_output_len/int: #primitive.rmt.transfer_and_read - -rmt_val_to_item dur0 lvl0 dur1 lvl1: - #primitive.rmt.val_to_item diff --git a/src/primitive.h b/src/primitive.h index 33691debb..1215bef05 100644 --- a/src/primitive.h +++ b/src/primitive.h @@ -374,7 +374,6 @@ namespace toit { PRIMITIVE(init, 0) \ PRIMITIVE(use, 2) \ PRIMITIVE(unuse, 2) \ - PRIMITIVE(val_to_item, 4)\ PRIMITIVE(config_rx, 8) \ PRIMITIVE(config_tx, 12) \ PRIMITIVE(transfer, 2) \ diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index e1796eaa3..e7bd5916b 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -172,20 +172,6 @@ PRIMITIVE(transfer) { return process->program()->null_object(); } -// TODO delete this primitive before committing. -PRIMITIVE(val_to_item) { - ARGS(uint32, dur0, uint32, lvl0, uint32, dur1, uint32, lvl1) - Error* error = null; - ByteArray* data = process->allocate_byte_array(4, &error); - if (data == null) return error; - - rmt_item32_t item = {dur0, lvl0, dur1, lvl1}; - - ByteArray::Bytes bytes(data); - memcpy(bytes.address(), reinterpret_cast(&item), 4); - return data; -} - PRIMITIVE(transfer_and_read) { ARGS(int, tx_num, int, rx_num, Blob, items_bytes, int, max_output_len) printf("begin\n"); From a7e44911796132439f4a00c2c649818d0eacfcf3 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 13:31:04 +0100 Subject: [PATCH 26/84] Update comment with reason for external allocation --- src/resources/rmt_esp32.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index e7bd5916b..327e8c32d 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -179,7 +179,8 @@ PRIMITIVE(transfer_and_read) { printf("allocate\n"); Error* error = null; - ByteArray* data = process->allocate_byte_array(max_output_len, &error, /*force_external*/ true); + // Force external, so we can adjust the length after the read. + ByteArray* data = process->allocate_byte_array(max_output_len, &error, true); if (data == null) return error; printf("get them items\n"); From 6aea331d65bfe93a063ffce2ac622c419103a6b3 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 13:31:25 +0100 Subject: [PATCH 27/84] Remove debug printing --- src/resources/rmt_esp32.cc | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 327e8c32d..8b210f291 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -174,57 +174,35 @@ PRIMITIVE(transfer) { PRIMITIVE(transfer_and_read) { ARGS(int, tx_num, int, rx_num, Blob, items_bytes, int, max_output_len) - printf("begin\n"); if (items_bytes.length() % 4 != 0) INVALID_ARGUMENT; - printf("allocate\n"); Error* error = null; // Force external, so we can adjust the length after the read. ByteArray* data = process->allocate_byte_array(max_output_len, &error, true); if (data == null) return error; - printf("get them items\n"); const rmt_item32_t* items = reinterpret_cast(items_bytes.address()); - printf("dur0: %d val0: %d dur1: %d val1: %d\n", items[0].duration0, items[0].level0, items[0].duration1, items[0].level1); rmt_channel_t rx_channel = (rmt_channel_t) rx_num; - printf("give me buffer\n"); RingbufHandle_t rb = NULL; esp_err_t err = rmt_get_ringbuf_handle(rx_channel, &rb); if (err != ESP_OK) return Primitive::os_error(err, process); - printf("start read\n"); err = rmt_rx_start(rx_channel, true); if (err != ESP_OK) return Primitive::os_error(err, process); - printf("write (len %d)\n", items_bytes.length() / 4); err = rmt_write_items((rmt_channel_t) tx_num, items, items_bytes.length() / 4, true); if (err != ESP_OK) return Primitive::os_error(err, process); size_t length = 0; - - // TODO how many ticks should we actually wait? - printf("get items\n"); void* received_bytes = xRingbufferReceive(rb, &length, 5000); - - printf("length: %d\n", length); - // TODO remove this before commit: - rmt_item32_t* received_items = reinterpret_cast(received_bytes); if (length > 0) { - printf("received item... dur0: %d val0: %d dur1: %d val1: %d\n", received_items[0].duration0, received_items[0].level0, received_items[0].duration1, received_items[0].level1); - - printf("prepare result\n"); ByteArray::Bytes bytes(data); memcpy(bytes.address(), received_bytes, length); - printf("return buffer\n"); - vRingbufferReturnItem(rb, received_bytes); + vRingbufferReturnItem(rb, received_bytes); } - // TODO check whether length corresponds to rmt_item32_t? - - printf("stop reading\n"); - // TODO check error? rmt_rx_stop(rx_channel); data->resize_external(process, length); From 0465f9e701170429f444e6f3c56acfe660ba79bf Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 14:03:25 +0100 Subject: [PATCH 28/84] Reset RX memory --- src/resources/rmt_esp32.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 8b210f291..e61601fab 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -184,8 +184,11 @@ PRIMITIVE(transfer_and_read) { const rmt_item32_t* items = reinterpret_cast(items_bytes.address()); rmt_channel_t rx_channel = (rmt_channel_t) rx_num; + esp_err_t err = rmt_rx_memory_reset(rx_channel); + if (err != ESP_OK) return Primitive::os_error(err, process); + RingbufHandle_t rb = NULL; - esp_err_t err = rmt_get_ringbuf_handle(rx_channel, &rb); + err = rmt_get_ringbuf_handle(rx_channel, &rb); if (err != ESP_OK) return Primitive::os_error(err, process); err = rmt_rx_start(rx_channel, true); From 84af9c876406ec53f03ec6403249fd5a8456eea4 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 14:05:16 +0100 Subject: [PATCH 29/84] Add reset mem comment --- src/resources/rmt_esp32.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index e61601fab..8963092ec 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -184,6 +184,7 @@ PRIMITIVE(transfer_and_read) { const rmt_item32_t* items = reinterpret_cast(items_bytes.address()); rmt_channel_t rx_channel = (rmt_channel_t) rx_num; + // If we don't reset the memory, then the RX buffer will eventually be filled. esp_err_t err = rmt_rx_memory_reset(rx_channel); if (err != ESP_OK) return Primitive::os_error(err, process); From 317d30288ddd03f66770d179659e8765d0de4221 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 14:05:44 +0100 Subject: [PATCH 30/84] Remove debug printing --- lib/rmt.toit | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index de5285621..43880b2ea 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -95,23 +95,17 @@ class Controller: transfer_and_read items max_items_size -> List: max_output_len := 4096 bytes := items_to_bytes_ items - print "len: $(bytes.size), $bytes" result := rmt_transfer_and_read_ tx_ch rx_ch bytes max_output_len - print "convert to items" return bytes_to_items_ result bytes_to_items_ bytes/ByteArray -> List: items_size := bytes.size / 2 - print "construct result list $items_size" result := [] - print "convert bytes to items" items_size.repeat: - print "add item" result.add Item.from_bytes it * 2 bytes - print "return result" return result items_to_bytes_ items/List/**/ -> ByteArray: From 98f1c292ba6d65a9b0c7028773ecba7241e4bd13 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 14:06:24 +0100 Subject: [PATCH 31/84] Preallocate result list --- lib/rmt.toit | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 43880b2ea..10f8b122f 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -102,10 +102,9 @@ class Controller: bytes_to_items_ bytes/ByteArray -> List: items_size := bytes.size / 2 - result := [] + result := List items_size items_size.repeat: - result.add - Item.from_bytes it * 2 bytes + result[it] = Item.from_bytes it * 2 bytes return result items_to_bytes_ items/List/**/ -> ByteArray: From 12277f1e1acf8009414226b1e8ae6557f5d49fda Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 14:09:54 +0100 Subject: [PATCH 32/84] Swap Item constructor parameters --- lib/rmt.toit | 3 +-- tests/rmt_item_test.toit | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 10f8b122f..909d3318d 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -19,8 +19,7 @@ class Item: value/int period/int - // TODO: swap params - constructor value period: + constructor period value: this.period = period & 0x7FFF this.value = value & 0b1 diff --git a/tests/rmt_item_test.toit b/tests/rmt_item_test.toit index 9bb118585..7bce30133 100644 --- a/tests/rmt_item_test.toit +++ b/tests/rmt_item_test.toit @@ -11,16 +11,16 @@ main: test_item_serialization test_item_construction: - item := Item 0 1024 + item := Item 1024 0 expect_equals 0 item.value expect_equals 1024 item.period - item = Item 1 0 + item = Item 0 1 expect_equals 1 item.value expect_equals 0 item.period test_item_construction_truncates_values: - item := Item 2 0xFFFF + item := Item 0xFFFF 2 expect_equals 0 item.value expect_equals 0x7FFF item.period @@ -30,38 +30,38 @@ test_item_serialization: #[0x00,0x00] #[item.first_byte, item.second_byte] - item = Item 1 0 + item = Item 0 1 expect_bytes_equal #[0x00,0x80] #[item.first_byte, item.second_byte] - item = Item 0 0x7FFF + item = Item 0x7FFF 0 expect_bytes_equal #[0xFF,0x7F] #[item.first_byte, item.second_byte] - item = Item 1 0x7FFF + item = Item 0x7FFF 1 expect_bytes_equal #[0xFF,0xFF] #[item.first_byte, item.second_byte] - item = Item 0 0x700F + item = Item 0x700F 0 expect_bytes_equal #[0x0F,0x70] #[item.first_byte, item.second_byte] - item = Item 1 0x700F + item = Item 0x700F 1 expect_bytes_equal #[0x0F,0xF0] #[item.first_byte, item.second_byte] - item = Item 0 1024 + item = Item 1024 0 expect_equals item Item.from_bytes 0 ByteArray 2: it == 0 ? item.first_byte : item.second_byte - item = Item 1 0 + item = Item 0 1 expect_equals item Item.from_bytes 0 From ebad10bdda7089140f76ae2e6b866081b03ea171 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 14:17:12 +0100 Subject: [PATCH 33/84] Add ringbuffer flush --- src/resources/rmt_esp32.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 8963092ec..d33694c83 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -172,6 +172,14 @@ PRIMITIVE(transfer) { return process->program()->null_object(); } +void flush_buffer(RingbufHandle_t rb) { + void* bytes = null; + size_t length = 0; + while((bytes = xRingbufferReceive(rb, &length, 0))) { + vRingbufferReturnItem(rb, bytes); + } +} + PRIMITIVE(transfer_and_read) { ARGS(int, tx_num, int, rx_num, Blob, items_bytes, int, max_output_len) if (items_bytes.length() % 4 != 0) INVALID_ARGUMENT; @@ -192,6 +200,8 @@ PRIMITIVE(transfer_and_read) { err = rmt_get_ringbuf_handle(rx_channel, &rb); if (err != ESP_OK) return Primitive::os_error(err, process); + flush_buffer(rb); + err = rmt_rx_start(rx_channel, true); if (err != ESP_OK) return Primitive::os_error(err, process); From cd3f05a7aa73a16312ad9dec1fb27000a6049c19 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 14:49:38 +0100 Subject: [PATCH 34/84] Reduce receive timeout --- src/resources/rmt_esp32.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index d33694c83..e66d58742 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -209,7 +209,7 @@ PRIMITIVE(transfer_and_read) { if (err != ESP_OK) return Primitive::os_error(err, process); size_t length = 0; - void* received_bytes = xRingbufferReceive(rb, &length, 5000); + void* received_bytes = xRingbufferReceive(rb, &length, 50); if (length > 0) { ByteArray::Bytes bytes(data); memcpy(bytes.address(), received_bytes, length); From 591b9fbe68f16eac5f316475d4bbdda0197ddeeb Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 14:58:49 +0100 Subject: [PATCH 35/84] Restructure rmt toit lib --- lib/rmt.toit | 54 ++++++++++++++++++---------------------------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 909d3318d..ad1a61b47 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -41,22 +41,14 @@ class Item: stringify -> string: return "($period, $value)" -class Controller: - rx_ch/int? - rx/gpio.Pin? - tx_ch/int? - tx/gpio.Pin? - - rmt_rx_/ByteArray? := null - rmt_tx_/ByteArray? := null +class Channel: + num/int + pin/gpio.Pin - constructor --.rx --.tx --.rx_ch --.tx_ch: - if (not rx) and (not tx): throw "INVALID_ARGUMENT" - if (rx and not rx_ch) or (not rx and rx_ch): throw "INVALID_ARGUMENT" - if (tx and not tx_ch) or (not tx and tx_ch): throw "INVALID_ARGUMENT" - if rx: rmt_rx_ = rmt_use_ resource_group_ rx_ch - if tx: rmt_tx_ = rmt_use_ resource_group_ tx_ch + res_/ByteArray? := null + constructor .pin .num: + res_ = rmt_use_ resource_group_ num config_rx --pin_num/int @@ -67,7 +59,7 @@ class Controller: --idle_threshold/int=12000 --filter_en/bool=true --filter_ticks_thresh/int=100: - rmt_config_rx_ rx.num rx_ch mem_block_num clk_div flags idle_threshold filter_en filter_ticks_thresh + rmt_config_rx_ pin.num num mem_block_num clk_div flags idle_threshold filter_en filter_ticks_thresh config_tx --pin_num/int @@ -82,31 +74,32 @@ class Controller: --loop_en/bool=false --idle_output_en/bool=true --idle_level/int=0: - rmt_config_tx_ tx.num tx_ch mem_block_num clk_div flags carrier_en carrier_freq_hz carrier_level carrier_duty_percent loop_en idle_output_en idle_level + rmt_config_tx_ pin.num num mem_block_num clk_div flags carrier_en carrier_freq_hz carrier_level carrier_duty_percent loop_en idle_output_en idle_level + close: + if res_: + rmt_unuse_ resource_group_ res_ + res_ = null - transfer items/List/**/: - if not rmt_tx_: throw "not configured for transfer" - - rmt_transfer_ tx_ch +class Controller: + static transfer channel/Channel items/List/**/: + rmt_transfer_ channel.num items_to_bytes_ items - transfer_and_read items max_items_size -> List: + static transfer_and_read --rx/Channel --tx/Channel items/List/**/ max_items_size/int -> List: max_output_len := 4096 bytes := items_to_bytes_ items - result := rmt_transfer_and_read_ tx_ch rx_ch - bytes - max_output_len + result := rmt_transfer_and_read_ tx.num rx.num bytes max_output_len return bytes_to_items_ result - bytes_to_items_ bytes/ByteArray -> List: + static bytes_to_items_ bytes/ByteArray -> List: items_size := bytes.size / 2 result := List items_size items_size.repeat: result[it] = Item.from_bytes it * 2 bytes return result - items_to_bytes_ items/List/**/ -> ByteArray: + static items_to_bytes_ items/List/**/ -> ByteArray: should_pad := items.size % 2 == 1 // Ensure there is an even number of items. bytes_size := should_pad ? items.size * 2 + 2 : items.size * 2 @@ -124,15 +117,6 @@ class Controller: return bytes - close: - if rmt_rx_: - rmt_unuse_ resource_group_ rmt_rx_ - rmt_rx_ = null - if rmt_tx_: - rmt_unuse_ resource_group_ rmt_tx_ - rmt_tx_ = null - - resource_group_ ::= rmt_init_ rmt_init_: From d2d2f4c7c79f803a8a41c7c038c40d41f17b4610 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 15:03:30 +0100 Subject: [PATCH 36/84] Remove newlines --- src/resources/rmt_esp32.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index e66d58742..3ffef5251 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -160,13 +160,10 @@ PRIMITIVE(config_rx) { PRIMITIVE(transfer) { ARGS(int, tx_num, Blob, items_bytes) - if (items_bytes.length() % 4 != 0) INVALID_ARGUMENT; rmt_item32_t* items = reinterpret_cast(const_cast(items_bytes.address())); - esp_err_t err = rmt_write_items((rmt_channel_t) tx_num, items, items_bytes.length() / 4, true); - if ( err != ESP_OK) return Primitive::os_error(err, process); return process->program()->null_object(); From 6dd2f965c9a3bf600d303443698c9d4fec910e95 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 15:04:44 +0100 Subject: [PATCH 37/84] Remove more newlines --- src/resources/rmt_esp32.cc | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 3ffef5251..039ca3b5e 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -47,7 +47,6 @@ class RMTResourceGroup : public ResourceGroup { }; - MODULE_IMPLEMENTATION(rmt, MODULE_RMT); PRIMITIVE(init) { @@ -63,7 +62,6 @@ PRIMITIVE(init) { PRIMITIVE(use) { ARGS(RMTResourceGroup, resource_group, int, channel_num) - ByteArray* proxy = process->object_heap()->allocate_proxy(); if (proxy == null) ALLOCATION_FAILED; @@ -82,15 +80,12 @@ PRIMITIVE(use) { PRIMITIVE(unuse) { ARGS(RMTResourceGroup, resource_group, IntResource, resource) - int channel = resource->id(); resource_group->unregister_id(channel); resource_proxy->clear_external_address(); return process->program()->null_object(); } - -// HELPER esp_err_t configure(const rmt_config_t* config, rmt_channel_t channel_num, size_t rx_buffer_size, Process* process) { esp_err_t err = rmt_config(config); if (ESP_OK != err) return err; @@ -108,7 +103,6 @@ PRIMITIVE(config_tx) { bool, carrier_en, int, carrier_freq_hz, int, carrier_level, int, carrier_duty_percent, bool, loop_en, bool, idle_output_en, int, idle_level) - // TODO: is there a better way to initialize this? rmt_config_t config = { }; config.gpio_num = (gpio_num_t) pin_num; @@ -137,7 +131,6 @@ PRIMITIVE(config_rx) { ARGS(int, pin_num, int, channel_num, int, mem_block_num, int, clk_div, int, flags, int, idle_threshold, bool, filter_en, int, filter_ticks_thresh) - // TODO: is there a better way to initialize this? rmt_config_t config = { }; config.gpio_num = (gpio_num_t) pin_num; From bb97354f540106160897d7437bdd19a0151763b5 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 15:26:38 +0100 Subject: [PATCH 38/84] Rename value -> level --- lib/rmt.toit | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index ad1a61b47..f49fbac3c 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -5,41 +5,51 @@ import gpio /** -Each RMT item consists of a value/level and a period. +Support for the ESP32 Remote Control (RMT). + +The $Channel represents a channel in the controller. + +An $Item represents an item from the controller. +*/ + +/** +An Item to be transferred or received with RMT. + +Each RMT item consists of a level and a period. # Advanced -When an RMT value is written, then the given value is sustained for the given +When an RMT level is written, then the given level is sustained for the given period. The period is specified in number of ticks, so the actual time the - value is sustained is determined by the RMT controller configuration. + level is sustained is determined by the RMT controller configuration. At the lower level, an item consits of 16 bits: 15 bits for the period and 1 - bit for the value/level. + bit for the level. */ class Item: - value/int + level/int period/int - constructor period value: + constructor period level: this.period = period & 0x7FFF - this.value = value & 0b1 + this.level = level & 0b1 constructor.from_bytes index/int bytes/ByteArray: period = bytes[index] | ((bytes[index + 1] & 0x7F) << 8) - value = bytes[index + 1] >> 7 + level = bytes[index + 1] >> 7 first_byte -> int: return period & 0xFF second_byte -> int: - return (period >> 8 ) | (value << 7) + return (period >> 8 ) | (level << 7) operator == other/any: if other is not Item: return false - return value == other.value and period == other.period + return level == other.level and period == other.period stringify -> string: - return "($period, $value)" + return "($period, $level)" class Channel: num/int From b21af009df507aae7afd6d0edd6fabf3fa947a24 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 15:30:08 +0100 Subject: [PATCH 39/84] Rewrite Item Toit doc --- lib/rmt.toit | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index f49fbac3c..5016203f5 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -15,12 +15,12 @@ An $Item represents an item from the controller. /** An Item to be transferred or received with RMT. -Each RMT item consists of a level and a period. +An RMT item consists of a level (low or high) and a period (the amount of + ticks the level is sustained). # Advanced -When an RMT level is written, then the given level is sustained for the given - period. The period is specified in number of ticks, so the actual time the - level is sustained is determined by the RMT controller configuration. +The period is specified in number of ticks, so the actual time the level is + sustained is determined by the RMT controller configuration. At the lower level, an item consits of 16 bits: 15 bits for the period and 1 bit for the level. From 72bf74fb37029df721a2c4c714ee172c2ec6c3c1 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 15:30:25 +0100 Subject: [PATCH 40/84] Make Item's byte access primitive --- lib/rmt.toit | 8 ++++---- tests/rmt_item_test.toit | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 5016203f5..dc46dc829 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -37,10 +37,10 @@ class Item: period = bytes[index] | ((bytes[index + 1] & 0x7F) << 8) level = bytes[index + 1] >> 7 - first_byte -> int: + first_byte_ -> int: return period & 0xFF - second_byte -> int: + second_byte_ -> int: return (period >> 8 ) | (level << 7) operator == other/any: @@ -117,8 +117,8 @@ class Controller: idx := 0 items.do: | item/Item | - bytes[idx] = item.first_byte - bytes[idx + 1] = item.second_byte + bytes[idx] = item.first_byte_ + bytes[idx + 1] = item.second_byte_ idx += 2 if should_pad: diff --git a/tests/rmt_item_test.toit b/tests/rmt_item_test.toit index 7bce30133..d2515f1cf 100644 --- a/tests/rmt_item_test.toit +++ b/tests/rmt_item_test.toit @@ -28,41 +28,41 @@ test_item_serialization: item := Item 0 0 expect_bytes_equal #[0x00,0x00] - #[item.first_byte, item.second_byte] + #[item.first_bytes_, item.second_byte_] item = Item 0 1 expect_bytes_equal #[0x00,0x80] - #[item.first_byte, item.second_byte] + #[item.first_bytes_, item.second_byte_] item = Item 0x7FFF 0 expect_bytes_equal #[0xFF,0x7F] - #[item.first_byte, item.second_byte] + #[item.first_bytes_, item.second_byte_] item = Item 0x7FFF 1 expect_bytes_equal #[0xFF,0xFF] - #[item.first_byte, item.second_byte] + #[item.first_bytes_, item.second_byte_] item = Item 0x700F 0 expect_bytes_equal #[0x0F,0x70] - #[item.first_byte, item.second_byte] + #[item.first_bytes_, item.second_byte_] item = Item 0x700F 1 expect_bytes_equal #[0x0F,0xF0] - #[item.first_byte, item.second_byte] + #[item.first_bytes_, item.second_byte_] item = Item 1024 0 expect_equals item Item.from_bytes 0 - ByteArray 2: it == 0 ? item.first_byte : item.second_byte + ByteArray 2: it == 0 ? item.first_bytes_ : item.second_byte_ item = Item 0 1 expect_equals item Item.from_bytes 0 - ByteArray 2: it == 0 ? item.first_byte : item.second_byte + ByteArray 2: it == 0 ? item.first_bytes_ : item.second_byte_ From 0664a7f950f88ae3c0e7eaed322c4c2bc3aef2ba Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 15:35:00 +0100 Subject: [PATCH 41/84] Remove unused channel config pin number parameter --- lib/rmt.toit | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index dc46dc829..d486ff211 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -61,7 +61,6 @@ class Channel: res_ = rmt_use_ resource_group_ num config_rx - --pin_num/int --channel_num/int --mem_block_num/int=1 --clk_div/int=80 @@ -72,7 +71,6 @@ class Channel: rmt_config_rx_ pin.num num mem_block_num clk_div flags idle_threshold filter_en filter_ticks_thresh config_tx - --pin_num/int --channel_num/int --mem_block_num/int=1 --clk_div/int=80 From a4182386fe2be1e66506d3a7992078b67dfce1f0 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 15:35:51 +0100 Subject: [PATCH 42/84] Remove unused channel config channel number parameter --- lib/rmt.toit | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index d486ff211..0e11f5feb 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -61,7 +61,6 @@ class Channel: res_ = rmt_use_ resource_group_ num config_rx - --channel_num/int --mem_block_num/int=1 --clk_div/int=80 --flags/int=0 @@ -71,7 +70,6 @@ class Channel: rmt_config_rx_ pin.num num mem_block_num clk_div flags idle_threshold filter_en filter_ticks_thresh config_tx - --channel_num/int --mem_block_num/int=1 --clk_div/int=80 --flags/int=0 From d7961933e6415dfce8437dede69054510697877f Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 15:50:16 +0100 Subject: [PATCH 43/84] Add more docs --- lib/rmt.toit | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/lib/rmt.toit b/lib/rmt.toit index 0e11f5feb..3c4995312 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -29,10 +29,12 @@ class Item: level/int period/int + /** Constructs an item from the given $period and $level. */ constructor period level: this.period = period & 0x7FFF this.level = level & 0b1 + /** Deserialized an item from the given $bytes at the given $index. */ constructor.from_bytes index/int bytes/ByteArray: period = bytes[index] | ((bytes[index + 1] & 0x7F) << 8) level = bytes[index + 1] >> 7 @@ -43,23 +45,44 @@ class Item: second_byte_ -> int: return (period >> 8 ) | (level << 7) + /** See $super. */ operator == other/any: if other is not Item: return false return level == other.level and period == other.period + /** See $super. */ stringify -> string: return "($period, $level)" +/** +An RMT channel. + +The channel must be configured after construction. + + The channel can be configured for either RX or TX. +*/ class Channel: num/int pin/gpio.Pin res_/ByteArray? := null + /** Constructs a channel using the given $num using the given $pin. */ constructor .pin .num: res_ = rmt_use_ resource_group_ num + /** + Configure the channel for RX. + + - $mem_block_num is the number of memory blocks (512 bytes) used by this channel. + - $clk_div is the source clock divider. Must be in the range [0,255]. + - $flags is the configuration flags. See the ESP-IDF documentation for available flags. + - $idle_threshold is the amount of clock cycles the receiver will run without seeing an edge. + - $filter_en whether the filter is enabled. + - $filter_ticks_thresh pulses shorter than this value is filtered away. + Only works with $filter_en. The value must be in the range [0,255]. + */ config_rx --mem_block_num/int=1 --clk_div/int=80 @@ -69,6 +92,14 @@ class Channel: --filter_ticks_thresh/int=100: rmt_config_rx_ pin.num num mem_block_num clk_div flags idle_threshold filter_en filter_ticks_thresh + /** + Configure the channel for TX. + + - $mem_block_num is the number of memory blocks (512 bytes) used by this channel. + - $clk_div is the source clock divider. Must be in the range [0,255]. + - $flags is the configuration flags. See the ESP-IDF documentation for available flags. + - $carrier_en + */ config_tx --mem_block_num/int=1 --clk_div/int=80 From 51c0a8d3558f76e77529f5e3a2e22e80bc9880e8 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 15:50:52 +0100 Subject: [PATCH 44/84] Fix typo --- tests/rmt_item_test.toit | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/rmt_item_test.toit b/tests/rmt_item_test.toit index d2515f1cf..911f7aa0b 100644 --- a/tests/rmt_item_test.toit +++ b/tests/rmt_item_test.toit @@ -28,41 +28,41 @@ test_item_serialization: item := Item 0 0 expect_bytes_equal #[0x00,0x00] - #[item.first_bytes_, item.second_byte_] + #[item.first_byte_, item.second_byte_] item = Item 0 1 expect_bytes_equal #[0x00,0x80] - #[item.first_bytes_, item.second_byte_] + #[item.first_byte_, item.second_byte_] item = Item 0x7FFF 0 expect_bytes_equal #[0xFF,0x7F] - #[item.first_bytes_, item.second_byte_] + #[item.first_byte_, item.second_byte_] item = Item 0x7FFF 1 expect_bytes_equal #[0xFF,0xFF] - #[item.first_bytes_, item.second_byte_] + #[item.first_byte_, item.second_byte_] item = Item 0x700F 0 expect_bytes_equal #[0x0F,0x70] - #[item.first_bytes_, item.second_byte_] + #[item.first_byte_, item.second_byte_] item = Item 0x700F 1 expect_bytes_equal #[0x0F,0xF0] - #[item.first_bytes_, item.second_byte_] + #[item.first_byte_, item.second_byte_] item = Item 1024 0 expect_equals item Item.from_bytes 0 - ByteArray 2: it == 0 ? item.first_bytes_ : item.second_byte_ + ByteArray 2: it == 0 ? item.first_byte_ : item.second_byte_ item = Item 0 1 expect_equals item Item.from_bytes 0 - ByteArray 2: it == 0 ? item.first_bytes_ : item.second_byte_ + ByteArray 2: it == 0 ? item.first_byte_ : item.second_byte_ From 265fb290026524c7b25ccd4580e5dde16775e6cb Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 7 Mar 2022 15:51:43 +0100 Subject: [PATCH 45/84] value -> level --- tests/rmt_item_test.toit | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/rmt_item_test.toit b/tests/rmt_item_test.toit index 911f7aa0b..cc83f37b6 100644 --- a/tests/rmt_item_test.toit +++ b/tests/rmt_item_test.toit @@ -12,16 +12,16 @@ main: test_item_construction: item := Item 1024 0 - expect_equals 0 item.value + expect_equals 0 item.level expect_equals 1024 item.period item = Item 0 1 - expect_equals 1 item.value + expect_equals 1 item.level expect_equals 0 item.period test_item_construction_truncates_values: item := Item 0xFFFF 2 - expect_equals 0 item.value + expect_equals 0 item.level expect_equals 0x7FFF item.period test_item_serialization: From 481aed1040493728e5580c012e80bfa259f484aa Mon Sep 17 00:00:00 2001 From: Lau <5364453+lask@users.noreply.github.com> Date: Tue, 8 Mar 2022 08:43:55 +0100 Subject: [PATCH 46/84] Apply suggestions from code review Co-authored-by: Florian Loitsch --- lib/rmt.toit | 2 +- src/resources/rmt_esp32.cc | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 3c4995312..2596b3906 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -60,7 +60,7 @@ An RMT channel. The channel must be configured after construction. - The channel can be configured for either RX or TX. +The channel can be configured for either RX or TX. */ class Channel: num/int diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 039ca3b5e..3064ace0b 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -94,7 +94,6 @@ esp_err_t configure(const rmt_config_t* config, rmt_channel_t channel_num, size_ if (ESP_OK != err) return err; err = rmt_driver_install((rmt_channel_t) channel_num, rx_buffer_size, 0); - if (ESP_OK != err) return err; return err; } From ae825ff0b03558ab891b0a46b33f41e8ab503a75 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Tue, 8 Mar 2022 15:49:13 +0100 Subject: [PATCH 47/84] Remove unnecessary reset The reset is also done when we stop reading --- src/resources/rmt_esp32.cc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 039ca3b5e..6a2fa582d 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -182,12 +182,8 @@ PRIMITIVE(transfer_and_read) { const rmt_item32_t* items = reinterpret_cast(items_bytes.address()); rmt_channel_t rx_channel = (rmt_channel_t) rx_num; - // If we don't reset the memory, then the RX buffer will eventually be filled. - esp_err_t err = rmt_rx_memory_reset(rx_channel); - if (err != ESP_OK) return Primitive::os_error(err, process); - RingbufHandle_t rb = NULL; - err = rmt_get_ringbuf_handle(rx_channel, &rb); + esp_err_t err = rmt_get_ringbuf_handle(rx_channel, &rb); if (err != ESP_OK) return Primitive::os_error(err, process); flush_buffer(rb); From fe99d86c701505a3b57497cd734ff9747db99516 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Tue, 8 Mar 2022 15:50:01 +0100 Subject: [PATCH 48/84] Remember to stop reading if we fail to retrieve an item --- src/resources/rmt_esp32.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 6a2fa582d..56099a633 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -192,7 +192,10 @@ PRIMITIVE(transfer_and_read) { if (err != ESP_OK) return Primitive::os_error(err, process); err = rmt_write_items((rmt_channel_t) tx_num, items, items_bytes.length() / 4, true); - if (err != ESP_OK) return Primitive::os_error(err, process); + if (err != ESP_OK) { + rmt_rx_stop(rx_channel); + return Primitive::os_error(err, process); + } size_t length = 0; void* received_bytes = xRingbufferReceive(rb, &length, 50); From e49791d1b796650a6e09f872cacca221d4b45e16 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Tue, 8 Mar 2022 15:50:24 +0100 Subject: [PATCH 49/84] Check for null rather than length when checking element received from ringbuffer --- src/resources/rmt_esp32.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 56099a633..8af7e8efd 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -199,10 +199,9 @@ PRIMITIVE(transfer_and_read) { size_t length = 0; void* received_bytes = xRingbufferReceive(rb, &length, 50); - if (length > 0) { + if (received_bytes != NULL) { ByteArray::Bytes bytes(data); memcpy(bytes.address(), received_bytes, length); - vRingbufferReturnItem(rb, received_bytes); } From fd19cc872b6f514dae4e73e5f1c307cc77d486e3 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Tue, 8 Mar 2022 15:52:20 +0100 Subject: [PATCH 50/84] NULL -> null --- src/resources/rmt_esp32.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 8af7e8efd..e4e33c81a 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -182,7 +182,7 @@ PRIMITIVE(transfer_and_read) { const rmt_item32_t* items = reinterpret_cast(items_bytes.address()); rmt_channel_t rx_channel = (rmt_channel_t) rx_num; - RingbufHandle_t rb = NULL; + RingbufHandle_t rb = null; esp_err_t err = rmt_get_ringbuf_handle(rx_channel, &rb); if (err != ESP_OK) return Primitive::os_error(err, process); @@ -199,7 +199,7 @@ PRIMITIVE(transfer_and_read) { size_t length = 0; void* received_bytes = xRingbufferReceive(rb, &length, 50); - if (received_bytes != NULL) { + if (received_bytes != null) { ByteArray::Bytes bytes(data); memcpy(bytes.address(), received_bytes, length); vRingbufferReturnItem(rb, received_bytes); From 07d7bb23a30597d02309573dd28cebe86624c7d4 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Tue, 8 Mar 2022 15:53:19 +0100 Subject: [PATCH 51/84] Only uninstall driver when applicable --- src/resources/rmt_esp32.cc | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index e4e33c81a..c5f4ea42e 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -41,10 +41,11 @@ class RMTResourceGroup : public ResourceGroup { virtual void on_unregister_resource(Resource* r) { rmt_channel_t channel = static_cast(static_cast(r)->id()); - rmt_driver_uninstall(channel); + rmt_channel_status_result_t channel_status; + rmt_get_channel_status(&channel_status); + if (channel_status.status[channel] != RMT_CHANNEL_UNINIT) rmt_driver_uninstall(channel); rmt_channels.put(channel); } - }; MODULE_IMPLEMENTATION(rmt, MODULE_RMT); @@ -87,7 +88,16 @@ PRIMITIVE(unuse) { } esp_err_t configure(const rmt_config_t* config, rmt_channel_t channel_num, size_t rx_buffer_size, Process* process) { - esp_err_t err = rmt_config(config); + rmt_channel_status_result_t channel_status; + esp_err_t err = rmt_get_channel_status(&channel_status); + if (ESP_OK != err) return err; + + if (channel_status.status[channel_num] != RMT_CHANNEL_UNINIT) { + err = rmt_driver_uninstall(channel_num); + if (ESP_OK != err) return err; + } + + err = rmt_config(config); if (ESP_OK != err) return err; err = rmt_set_source_clk(channel_num, RMT_BASECLK_APB); From 94be81101d57b7da3f4389f05ad901a434227cfb Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Tue, 8 Mar 2022 15:53:43 +0100 Subject: [PATCH 52/84] Add a bit more Toit docs --- lib/rmt.toit | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 3c4995312..bdf7b22d1 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -79,7 +79,7 @@ class Channel: - $clk_div is the source clock divider. Must be in the range [0,255]. - $flags is the configuration flags. See the ESP-IDF documentation for available flags. - $idle_threshold is the amount of clock cycles the receiver will run without seeing an edge. - - $filter_en whether the filter is enabled. + - $filter_en is whether the filter is enabled. - $filter_ticks_thresh pulses shorter than this value is filtered away. Only works with $filter_en. The value must be in the range [0,255]. */ @@ -98,7 +98,14 @@ class Channel: - $mem_block_num is the number of memory blocks (512 bytes) used by this channel. - $clk_div is the source clock divider. Must be in the range [0,255]. - $flags is the configuration flags. See the ESP-IDF documentation for available flags. - - $carrier_en + - $carrier_en is whether a carrier wave is used. + - $carrier_freq_hz is the frequency of the carrier wave. + - $carrier_level is the way the carrier way is modulated. + Set to 1 to transmit on low output level and 0 to transmit on high output level. + - $carrier_duty_percent is the proportion of time the carrier wave is low. + - $loop_en is whether the transmitter continously writes the provided items in a loop. + - $idle_output_en is whether the transmitter outputs when idle. + - $idle_level is the level transmitted by the transmitter when idle. */ config_tx --mem_block_num/int=1 From 3d8aae35a87552a96cf8d9e2745c1c9ddb031933 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Wed, 9 Mar 2022 08:55:02 +0100 Subject: [PATCH 53/84] Make RX buffer size configurable --- lib/rmt.toit | 7 ++++--- src/primitive.h | 2 +- src/resources/rmt_esp32.cc | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 80363b3cc..77ff7c67f 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -89,8 +89,9 @@ class Channel: --flags/int=0 --idle_threshold/int=12000 --filter_en/bool=true - --filter_ticks_thresh/int=100: - rmt_config_rx_ pin.num num mem_block_num clk_div flags idle_threshold filter_en filter_ticks_thresh + --filter_ticks_thresh/int=100 + --rx_buffer_size=128: + rmt_config_rx_ pin.num num mem_block_num clk_div flags idle_threshold filter_en filter_ticks_thresh rx_buffer_size /** Configure the channel for TX. @@ -173,7 +174,7 @@ rmt_unuse_ resource_group resource: #primitive.rmt.unuse rmt_config_rx_ pin_num/int channel_num/int mem_block_num/int clk_div/int flags/int - idle_threshold/int filter_en/bool filter_ticks_thresh/int: + idle_threshold/int filter_en/bool filter_ticks_thresh/int rx_buffer_size/int: #primitive.rmt.config_rx rmt_config_tx_ pin_num/int channel_num/int mem_block_num/int clk_div/int flags/int diff --git a/src/primitive.h b/src/primitive.h index 1215bef05..8ebe03792 100644 --- a/src/primitive.h +++ b/src/primitive.h @@ -374,7 +374,7 @@ namespace toit { PRIMITIVE(init, 0) \ PRIMITIVE(use, 2) \ PRIMITIVE(unuse, 2) \ - PRIMITIVE(config_rx, 8) \ + PRIMITIVE(config_rx, 9) \ PRIMITIVE(config_tx, 12) \ PRIMITIVE(transfer, 2) \ PRIMITIVE(transfer_and_read, 4) \ diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 7eee20f0a..74f9c41c7 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -138,7 +138,7 @@ PRIMITIVE(config_tx) { PRIMITIVE(config_rx) { ARGS(int, pin_num, int, channel_num, int, mem_block_num, int, clk_div, int, flags, - int, idle_threshold, bool, filter_en, int, filter_ticks_thresh) + int, idle_threshold, bool, filter_en, int, filter_ticks_thresh, int, rx_buffer_size) rmt_config_t config = { }; @@ -154,7 +154,7 @@ PRIMITIVE(config_rx) { rx_config.filter_ticks_thresh = filter_ticks_thresh; config.rx_config = rx_config; - esp_err_t err = configure(&config,(rmt_channel_t) channel_num, 1000, process); + esp_err_t err = configure(&config,(rmt_channel_t) channel_num, rx_buffer_size, process); if (ESP_OK != err) return Primitive::os_error(err, process); return process->program()->null_object(); From 2f6038b11d2da07d13d153f29a586f332ebdfda2 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Wed, 9 Mar 2022 11:59:29 +0100 Subject: [PATCH 54/84] Update rtm API (WIP) --- lib/rmt.toit | 94 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 35 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 77ff7c67f..e6c7d48df 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -12,6 +12,59 @@ The $Channel represents a channel in the controller. An $Item represents an item from the controller. */ + +class Items: + size/int + bytes/ByteArray + + constructor .size: + bytes = construct_bytes_ size + + static construct_bytes_ size/int -> ByteArray: + should_pad := size % 2 == 1 + bytes_size := should_pad ? size * 2 + 2 : size * 2 + return ByteArray bytes_size + + // TODO what's a nice convenient constructor for populating Items with known values? + + constructor.from_bytes .bytes/ByteArray: + if bytes.size % 4 != 0: throw "INVALID_ARGUMENT" + + size = bytes.size / 2 + + item_level i -> int: + check_bounds_ i + return item_level_ i + + item_period i -> int: + check_bounds_ i + return item_period_ i + + set_item i period level -> none: + check_bounds_ i + period = period & 0x7FFF + level = level & 0b1 + bytes[i] = period & 0xFF + bytes[i + 1] = (period >> 8 ) | (level << 7) + + do [block]: + size.repeat: + i := it * 2 + block.call + item_period_ i + item_level_ i + + check_bounds_ i: + if not 0 <= i < size: throw "OUT_OF_BOUNDS" + + item_level_ i -> int: + return bytes[i + 1] >> 7 + + item_period_ i -> int: + return bytes[i] | ((bytes[i + 1] & 0x7F) << 8) + + + /** An Item to be transferred or received with RMT. @@ -126,41 +179,12 @@ class Channel: rmt_unuse_ resource_group_ res_ res_ = null -class Controller: - static transfer channel/Channel items/List/**/: - rmt_transfer_ channel.num - items_to_bytes_ items - - static transfer_and_read --rx/Channel --tx/Channel items/List/**/ max_items_size/int -> List: - max_output_len := 4096 - bytes := items_to_bytes_ items - result := rmt_transfer_and_read_ tx.num rx.num bytes max_output_len - return bytes_to_items_ result - - static bytes_to_items_ bytes/ByteArray -> List: - items_size := bytes.size / 2 - result := List items_size - items_size.repeat: - result[it] = Item.from_bytes it * 2 bytes - return result - - static items_to_bytes_ items/List/**/ -> ByteArray: - should_pad := items.size % 2 == 1 - // Ensure there is an even number of items. - bytes_size := should_pad ? items.size * 2 + 2 : items.size * 2 - bytes := ByteArray bytes_size - idx := 0 - - items.do: | item/Item | - bytes[idx] = item.first_byte_ - bytes[idx + 1] = item.second_byte_ - idx += 2 - - if should_pad: - bytes[idx] = 0 - bytes[idx + 1] = 0 - - return bytes +transfer channel/Channel items/Items: + rmt_transfer_ channel.num items.bytes + +transfer_and_read --rx/Channel --tx/Channel items/Items max_items_size/int -> Items: + result := rmt_transfer_and_read_ tx.num rx.num items.bytes max_items_size + return Items.from_bytes result resource_group_ ::= rmt_init_ From deebade907899b3f2025822b88b3d95158810a66 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Wed, 9 Mar 2022 11:59:44 +0100 Subject: [PATCH 55/84] Add rmt items tests (WIP) --- tests/rmt_items_test.toit | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/rmt_items_test.toit diff --git a/tests/rmt_items_test.toit b/tests/rmt_items_test.toit new file mode 100644 index 000000000..aa36ab0c5 --- /dev/null +++ b/tests/rmt_items_test.toit @@ -0,0 +1,38 @@ +// Copyright (C) 2022 Toitware ApS. +// Use of this source code is governed by a Zero-Clause BSD license that can +// be found in the tests/LICENSE file. + +import expect show * +import rmt show Items + +main: + test_items_construction + test_items_getters + test_items_setter + test_items_do + +test_items_construction: + items := Items 4 + expect_equals 4 items.size + expect_equals 8 items.bytes.size + + items = Items 5 + expect_equals 5 items.size + expect_equals 12 items.bytes.size + + bytes := #[0x11, 0x22, 0x33, 0x44] + items = Items.from_bytes bytes + expect_equals 2 items.size + expect_bytes_equal bytes items.bytes + + bytes = #[0x11, 0x22, 0x33, 0x44, 0x55] + expect_throw "INVALID_ARGUMENT": + Items.from_bytes bytes + +test_items_getters: + + +test_items_setter: + + +test_items_do: From badd7559a3812bb76234357e684611b9d4c0cf28 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Wed, 9 Mar 2022 15:06:53 +0100 Subject: [PATCH 56/84] Add types to RMT Items methods --- lib/rmt.toit | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index e6c7d48df..307fe6182 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -32,15 +32,15 @@ class Items: size = bytes.size / 2 - item_level i -> int: + item_level i/int -> int: check_bounds_ i return item_level_ i - item_period i -> int: + item_period i/int -> int: check_bounds_ i return item_period_ i - set_item i period level -> none: + set_item i/int period/int level/int -> none: check_bounds_ i period = period & 0x7FFF level = level & 0b1 From 2ac191161e8f5c94a93ae4314b8f92dd20ccf0a9 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Wed, 9 Mar 2022 15:07:07 +0100 Subject: [PATCH 57/84] Fix incorrect Items indexing into backing bytes --- lib/rmt.toit | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 307fe6182..fac1b181f 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -42,26 +42,27 @@ class Items: set_item i/int period/int level/int -> none: check_bounds_ i + idx := i * 2 period = period & 0x7FFF level = level & 0b1 - bytes[i] = period & 0xFF - bytes[i + 1] = (period >> 8 ) | (level << 7) + bytes[idx] = period & 0xFF + bytes[idx + 1] = (period >> 8 ) | (level << 7) do [block]: size.repeat: - i := it * 2 block.call - item_period_ i - item_level_ i + item_period_ it + item_level_ it check_bounds_ i: if not 0 <= i < size: throw "OUT_OF_BOUNDS" item_level_ i -> int: - return bytes[i + 1] >> 7 + return bytes[i * 2 + 1] >> 7 item_period_ i -> int: - return bytes[i] | ((bytes[i + 1] & 0x7F) << 8) + idx := i * 2 + return bytes[idx] | ((bytes[idx + 1] & 0x7F) << 8) From 7ac2355e27a56e6447614f673d8011e4add9481b Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Wed, 9 Mar 2022 15:08:15 +0100 Subject: [PATCH 58/84] Finish Items tests --- tests/rmt_items_test.toit | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/tests/rmt_items_test.toit b/tests/rmt_items_test.toit index aa36ab0c5..90da2b1dd 100644 --- a/tests/rmt_items_test.toit +++ b/tests/rmt_items_test.toit @@ -30,9 +30,70 @@ test_items_construction: Items.from_bytes bytes test_items_getters: + bytes := #[ + 0x00, 0x00, + 0xFF, 0xFF, + 0xFF, 0x7F, + 0x00, 0x80 + ] + items := Items.from_bytes bytes + expect_equals 0 (items.item_level 0) + expect_equals 0 (items.item_period 0) + + expect_equals 1 (items.item_level 1) + expect_equals 0x7FFF (items.item_period 1) + expect_equals 0 (items.item_level 2) + expect_equals 0x7FFF (items.item_period 2) + + expect_equals 1 (items.item_level 3) + expect_equals 0 (items.item_period 3) + + expect_throw "OUT_OF_BOUNDS": items.item_level -1 + expect_throw "OUT_OF_BOUNDS": items.item_period -1 + expect_throw "OUT_OF_BOUNDS": items.item_level 4 + expect_throw "OUT_OF_BOUNDS": items.item_period 4 test_items_setter: + items := Items 3 + items.do: | period level | + expect_equals 0 period + expect_equals 0 level + + items.set_item 0 8 1 + expect_equals 8 + items.item_period 0 + expect_equals 1 + items.item_level 0 + items.set_item 1 0x7FFF 0 + expect_equals 0x7FFF + items.item_period 1 + expect_equals 0 + items.item_level 1 + + items.set_item 2 0 1 + expect_equals 0 + items.item_period 2 + expect_equals 1 + items.item_level 0 test_items_do: + bytes := #[ + 0x00, 0x00, + 0x01, 0x00, + 0x02, 0x00, + 0x03, 0x00 + ] + items := Items.from_bytes bytes + item_count := 0 + items.do: | period level | + expect_equals item_count period + expect_equals 0 level + item_count++ + expect_equals 4 item_count + + items = Items 3 + item_count = 0 + items.do: item_count++ + expect_equals 3 item_count From a992dd2a85816f10a5a73c5b62d57a0e48c1921f Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Wed, 9 Mar 2022 15:09:56 +0100 Subject: [PATCH 59/84] Remove obsolete Item class from RMT library --- lib/rmt.toit | 57 +++++++-------------------------- tests/rmt_item_test.toit | 68 ---------------------------------------- 2 files changed, 12 insertions(+), 113 deletions(-) delete mode 100644 tests/rmt_item_test.toit diff --git a/lib/rmt.toit b/lib/rmt.toit index fac1b181f..560520bba 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -12,7 +12,19 @@ The $Channel represents a channel in the controller. An $Item represents an item from the controller. */ +/** +An Item to be transferred or received with RMT. + +An RMT item consists of a level (low or high) and a period (the amount of + ticks the level is sustained). + +# Advanced +The period is specified in number of ticks, so the actual time the level is + sustained is determined by the RMT controller configuration. +At the lower level, an item consits of 16 bits: 15 bits for the period and 1 + bit for the level. +*/ class Items: size/int bytes/ByteArray @@ -64,51 +76,6 @@ class Items: idx := i * 2 return bytes[idx] | ((bytes[idx + 1] & 0x7F) << 8) - - -/** -An Item to be transferred or received with RMT. - -An RMT item consists of a level (low or high) and a period (the amount of - ticks the level is sustained). - -# Advanced -The period is specified in number of ticks, so the actual time the level is - sustained is determined by the RMT controller configuration. - -At the lower level, an item consits of 16 bits: 15 bits for the period and 1 - bit for the level. -*/ -class Item: - level/int - period/int - - /** Constructs an item from the given $period and $level. */ - constructor period level: - this.period = period & 0x7FFF - this.level = level & 0b1 - - /** Deserialized an item from the given $bytes at the given $index. */ - constructor.from_bytes index/int bytes/ByteArray: - period = bytes[index] | ((bytes[index + 1] & 0x7F) << 8) - level = bytes[index + 1] >> 7 - - first_byte_ -> int: - return period & 0xFF - - second_byte_ -> int: - return (period >> 8 ) | (level << 7) - - /** See $super. */ - operator == other/any: - if other is not Item: return false - - return level == other.level and period == other.period - - /** See $super. */ - stringify -> string: - return "($period, $level)" - /** An RMT channel. diff --git a/tests/rmt_item_test.toit b/tests/rmt_item_test.toit deleted file mode 100644 index cc83f37b6..000000000 --- a/tests/rmt_item_test.toit +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2022 Toitware ApS. -// Use of this source code is governed by a Zero-Clause BSD license that can -// be found in the tests/LICENSE file. - -import expect show * -import rmt show Item - -main: - test_item_construction - test_item_construction_truncates_values - test_item_serialization - -test_item_construction: - item := Item 1024 0 - expect_equals 0 item.level - expect_equals 1024 item.period - - item = Item 0 1 - expect_equals 1 item.level - expect_equals 0 item.period - -test_item_construction_truncates_values: - item := Item 0xFFFF 2 - expect_equals 0 item.level - expect_equals 0x7FFF item.period - -test_item_serialization: - item := Item 0 0 - expect_bytes_equal - #[0x00,0x00] - #[item.first_byte_, item.second_byte_] - - item = Item 0 1 - expect_bytes_equal - #[0x00,0x80] - #[item.first_byte_, item.second_byte_] - - item = Item 0x7FFF 0 - expect_bytes_equal - #[0xFF,0x7F] - #[item.first_byte_, item.second_byte_] - - item = Item 0x7FFF 1 - expect_bytes_equal - #[0xFF,0xFF] - #[item.first_byte_, item.second_byte_] - - item = Item 0x700F 0 - expect_bytes_equal - #[0x0F,0x70] - #[item.first_byte_, item.second_byte_] - - item = Item 0x700F 1 - expect_bytes_equal - #[0x0F,0xF0] - #[item.first_byte_, item.second_byte_] - - item = Item 1024 0 - expect_equals - item - Item.from_bytes 0 - ByteArray 2: it == 0 ? item.first_byte_ : item.second_byte_ - - item = Item 0 1 - expect_equals - item - Item.from_bytes 0 - ByteArray 2: it == 0 ? item.first_byte_ : item.second_byte_ From 6ef9607fb0b9f73bb0896625c498b2002e2e544e Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Thu, 10 Mar 2022 09:42:29 +0100 Subject: [PATCH 60/84] Correct reference in docs --- lib/rmt.toit | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 560520bba..17b314fc4 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -7,9 +7,9 @@ import gpio /** Support for the ESP32 Remote Control (RMT). -The $Channel represents a channel in the controller. +A $Channel corresponds to a channel in the ESP32 RMT controller. -An $Item represents an item from the controller. +An $Items represents an item from the controller. */ /** From 501dceaaf8b8cf6c5fb9e5acc4a3e9513e0feb62 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Thu, 10 Mar 2022 10:13:53 +0100 Subject: [PATCH 61/84] Add more docs --- lib/rmt.toit | 77 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 17b314fc4..edf8b4814 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -9,7 +9,7 @@ Support for the ESP32 Remote Control (RMT). A $Channel corresponds to a channel in the ESP32 RMT controller. -An $Items represents an item from the controller. +$Items represent a collection of items to be sent by the RMT controller. */ /** @@ -26,11 +26,23 @@ At the lower level, an item consits of 16 bits: 15 bits for the period and 1 bit for the level. */ class Items: + /** The amount of items in the collection. */ size/int - bytes/ByteArray + bytes_/ByteArray + + /** + Constructs a collection of items of the given $size. + + All items are initialized to 0 period and 0 level. + + # Advanced + If the given $size is not divisible by 2, then the byte array allocted for + $bytes_ is patted with two bytes to make the $bytes_ usable by the RMT + primitives. + */ constructor .size: - bytes = construct_bytes_ size + bytes_ = construct_bytes_ size static construct_bytes_ size/int -> ByteArray: should_pad := size % 2 == 1 @@ -39,27 +51,54 @@ class Items: // TODO what's a nice convenient constructor for populating Items with known values? - constructor.from_bytes .bytes/ByteArray: + /** + Constructs a collection of items from the given $bytes. + + The $bytes size must be divisible by 4. + + # Advanced + The bytes must correspond to bytes produced by the RMT primitives. The + primitives convert the bytes to pairs of items (2 bytes per item) which + is the reason the $bytes size must be divisible by 4. + */ + constructor.from_bytes bytes/ByteArray: if bytes.size % 4 != 0: throw "INVALID_ARGUMENT" - size = bytes.size / 2 + bytes_ = bytes + size = bytes_.size / 2 + + /** + Gets the item period of the $i'th item. + + The given $i must be in the range [0,$size[. + */ + item_period i/int -> int: + check_bounds_ i + return item_period_ i + /** + Gets the item level of the $i'th item. + + The given $i must be in the range [0,$size[. + */ item_level i/int -> int: check_bounds_ i return item_level_ i - item_period i/int -> int: - check_bounds_ i - return item_period_ i + /** + Set the $i'th item to the given $period and $level. + The given $i must be in the range [0,$size[. + */ set_item i/int period/int level/int -> none: check_bounds_ i idx := i * 2 period = period & 0x7FFF level = level & 0b1 - bytes[idx] = period & 0xFF - bytes[idx + 1] = (period >> 8 ) | (level << 7) + bytes_[idx] = period & 0xFF + bytes_[idx + 1] = (period >> 8 ) | (level << 7) + /** Invokes the given $block on each item of this item collection. */ do [block]: size.repeat: block.call @@ -70,11 +109,11 @@ class Items: if not 0 <= i < size: throw "OUT_OF_BOUNDS" item_level_ i -> int: - return bytes[i * 2 + 1] >> 7 + return bytes_[i * 2 + 1] >> 7 item_period_ i -> int: idx := i * 2 - return bytes[idx] | ((bytes[idx + 1] & 0x7F) << 8) + return bytes_[idx] | ((bytes_[idx + 1] & 0x7F) << 8) /** An RMT channel. @@ -147,11 +186,19 @@ class Channel: rmt_unuse_ resource_group_ res_ res_ = null -transfer channel/Channel items/Items: - rmt_transfer_ channel.num items.bytes +/** Transfers the given $items over the given $channel.*/ +transfer channel/Channel items/Items -> none: + rmt_transfer_ channel.num items.bytes_ + +/** +Transfers the given $items while simoultaniously receiving. +The $items are transferred over the given $tx channel and items are received on the $rx channel. + +The given $max_items_size specifies the maximum byte size of the returned items. +*/ transfer_and_read --rx/Channel --tx/Channel items/Items max_items_size/int -> Items: - result := rmt_transfer_and_read_ tx.num rx.num items.bytes max_items_size + result := rmt_transfer_and_read_ tx.num rx.num items.bytes_ max_items_size return Items.from_bytes result resource_group_ ::= rmt_init_ From 94139b56cf146959383991f8a3eb76dc2dec4e41 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Thu, 10 Mar 2022 10:18:32 +0100 Subject: [PATCH 62/84] Check size of received data before copying --- src/resources/rmt_esp32.cc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 74f9c41c7..6fab7db49 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -209,9 +209,16 @@ PRIMITIVE(transfer_and_read) { size_t length = 0; void* received_bytes = xRingbufferReceive(rb, &length, 50); if (received_bytes != null) { - ByteArray::Bytes bytes(data); - memcpy(bytes.address(), received_bytes, length); - vRingbufferReturnItem(rb, received_bytes); + if (length <= max_output_len) { + ByteArray::Bytes bytes(data); + memcpy(bytes.address(), received_bytes, length); + vRingbufferReturnItem(rb, received_bytes); + } else { + vRingbufferReturnItem(rb, received_bytes); + rmt_rx_stop(rx_channel); + data->resize_external(process, 0); + OUT_OF_RANGE; + } } rmt_rx_stop(rx_channel); From 74332d4463c72bf4d8b7d38690109551f71f3274 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Thu, 10 Mar 2022 10:24:15 +0100 Subject: [PATCH 63/84] Fix renamed bytes -> bytes_ in test --- tests/rmt_items_test.toit | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/rmt_items_test.toit b/tests/rmt_items_test.toit index 90da2b1dd..aae77e0da 100644 --- a/tests/rmt_items_test.toit +++ b/tests/rmt_items_test.toit @@ -14,16 +14,16 @@ main: test_items_construction: items := Items 4 expect_equals 4 items.size - expect_equals 8 items.bytes.size + expect_equals 8 items.bytes_.size items = Items 5 expect_equals 5 items.size - expect_equals 12 items.bytes.size + expect_equals 12 items.bytes_.size bytes := #[0x11, 0x22, 0x33, 0x44] items = Items.from_bytes bytes expect_equals 2 items.size - expect_bytes_equal bytes items.bytes + expect_bytes_equal bytes items.bytes_ bytes = #[0x11, 0x22, 0x33, 0x44, 0x55] expect_throw "INVALID_ARGUMENT": From 6b566c1b59de345848feb456c61cf2f268997725 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Thu, 10 Mar 2022 15:15:36 +0100 Subject: [PATCH 64/84] read -> receive --- lib/rmt.toit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index edf8b4814..78b967b12 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -197,7 +197,7 @@ The $items are transferred over the given $tx channel and items are received on The given $max_items_size specifies the maximum byte size of the returned items. */ -transfer_and_read --rx/Channel --tx/Channel items/Items max_items_size/int -> Items: +transfer_and_receive --rx/Channel --tx/Channel items/Items max_items_size/int -> Items: result := rmt_transfer_and_read_ tx.num rx.num items.bytes_ max_items_size return Items.from_bytes result From 8678f398531eeaabac5a4d307efd03385c6cdf82 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Thu, 10 Mar 2022 15:52:21 +0100 Subject: [PATCH 65/84] Add limitations on the channel number to Toit doc --- lib/rmt.toit | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 78b967b12..ede6f9a91 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -128,7 +128,11 @@ class Channel: res_/ByteArray? := null - /** Constructs a channel using the given $num using the given $pin. */ + /** + Constructs a channel using the given $num using the given $pin. + + The givn $num must be in the range [0,7] and must not be in use. + */ constructor .pin .num: res_ = rmt_use_ resource_group_ num From 7f7238cab1d635ebc916c863e11627a9e43b0753 Mon Sep 17 00:00:00 2001 From: Lau <5364453+lask@users.noreply.github.com> Date: Fri, 11 Mar 2022 13:43:18 +0100 Subject: [PATCH 66/84] Update lib/rmt.toit Co-authored-by: Erik Corry --- lib/rmt.toit | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index ede6f9a91..da2b9b1f7 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -42,12 +42,8 @@ class Items: primitives. */ constructor .size: - bytes_ = construct_bytes_ size - - static construct_bytes_ size/int -> ByteArray: - should_pad := size % 2 == 1 - bytes_size := should_pad ? size * 2 + 2 : size * 2 - return ByteArray bytes_size + bytes_ = ByteArray + round_up (size * 2) 4 // TODO what's a nice convenient constructor for populating Items with known values? From 77a89de13fc4559200fefce265b89545d8cc21cc Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 11 Mar 2022 14:22:44 +0100 Subject: [PATCH 67/84] Rename Items -> Signals --- lib/rmt.toit | 76 ++++++++++++++-------------- tests/rmt_signals_test.toit | 99 +++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 38 deletions(-) create mode 100644 tests/rmt_signals_test.toit diff --git a/lib/rmt.toit b/lib/rmt.toit index da2b9b1f7..e97a37530 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -9,32 +9,32 @@ Support for the ESP32 Remote Control (RMT). A $Channel corresponds to a channel in the ESP32 RMT controller. -$Items represent a collection of items to be sent by the RMT controller. +$Signals represent a collection of signals to be sent by the RMT controller. */ /** -An Item to be transferred or received with RMT. +A collection of signals to be transferred or received with the RMT controller. -An RMT item consists of a level (low or high) and a period (the amount of +An RMT signal consists of a level (low or high) and a period (the amount of ticks the level is sustained). # Advanced The period is specified in number of ticks, so the actual time the level is sustained is determined by the RMT controller configuration. -At the lower level, an item consits of 16 bits: 15 bits for the period and 1 - bit for the level. +At the lower level, a signal consits of 16 bits: 15 bits for the period and 1 + bit for the level. Signals must be transfered as pairs also known as an item. */ -class Items: - /** The amount of items in the collection. */ +class Signals: + /** The amount of signals in the collection. */ size/int bytes_/ByteArray /** - Constructs a collection of items of the given $size. + Constructs a collection of signals of the given $size. - All items are initialized to 0 period and 0 level. + All signals are initialized to 0 period and 0 level. # Advanced If the given $size is not divisible by 2, then the byte array allocted for @@ -45,17 +45,17 @@ class Items: bytes_ = ByteArray round_up (size * 2) 4 - // TODO what's a nice convenient constructor for populating Items with known values? + // TODO what's a nice convenient constructor for populating Signals with known values? /** - Constructs a collection of items from the given $bytes. + Constructs a collection of signals from the given $bytes. The $bytes size must be divisible by 4. # Advanced The bytes must correspond to bytes produced by the RMT primitives. The - primitives convert the bytes to pairs of items (2 bytes per item) which - is the reason the $bytes size must be divisible by 4. + primitives operate with pairs of signals (called an item) which is the + reason the $bytes size must be divisible by 4. */ constructor.from_bytes bytes/ByteArray: if bytes.size % 4 != 0: throw "INVALID_ARGUMENT" @@ -64,29 +64,29 @@ class Items: size = bytes_.size / 2 /** - Gets the item period of the $i'th item. + Gets the signal period of the $i'th signal. The given $i must be in the range [0,$size[. */ - item_period i/int -> int: + signal_period i/int -> int: check_bounds_ i - return item_period_ i + return signal_period_ i /** - Gets the item level of the $i'th item. + Gets the signal level of the $i'th signal. The given $i must be in the range [0,$size[. */ - item_level i/int -> int: + signal_level i/int -> int: check_bounds_ i - return item_level_ i + return signal_level_ i /** - Set the $i'th item to the given $period and $level. + Set the $i'th signal to the given $period and $level. The given $i must be in the range [0,$size[. */ - set_item i/int period/int level/int -> none: + set_signal i/int period/int level/int -> none: check_bounds_ i idx := i * 2 period = period & 0x7FFF @@ -94,20 +94,20 @@ class Items: bytes_[idx] = period & 0xFF bytes_[idx + 1] = (period >> 8 ) | (level << 7) - /** Invokes the given $block on each item of this item collection. */ + /** Invokes the given $block on each signal of this signal collection. */ do [block]: size.repeat: block.call - item_period_ it - item_level_ it + signal_period_ it + signal_level_ it check_bounds_ i: if not 0 <= i < size: throw "OUT_OF_BOUNDS" - item_level_ i -> int: + signal_level_ i -> int: return bytes_[i * 2 + 1] >> 7 - item_period_ i -> int: + signal_period_ i -> int: idx := i * 2 return bytes_[idx] | ((bytes_[idx + 1] & 0x7F) << 8) @@ -164,7 +164,7 @@ class Channel: - $carrier_level is the way the carrier way is modulated. Set to 1 to transmit on low output level and 0 to transmit on high output level. - $carrier_duty_percent is the proportion of time the carrier wave is low. - - $loop_en is whether the transmitter continously writes the provided items in a loop. + - $loop_en is whether the transmitter continously writes the provided signals in a loop. - $idle_output_en is whether the transmitter outputs when idle. - $idle_level is the level transmitted by the transmitter when idle. */ @@ -186,20 +186,20 @@ class Channel: rmt_unuse_ resource_group_ res_ res_ = null -/** Transfers the given $items over the given $channel.*/ -transfer channel/Channel items/Items -> none: - rmt_transfer_ channel.num items.bytes_ +/** Transfers the given $signals over the given $channel.*/ +transfer channel/Channel signals/Signals -> none: + rmt_transfer_ channel.num signals.bytes_ /** -Transfers the given $items while simoultaniously receiving. +Transfers the given $signals while simoultaniously receiving. -The $items are transferred over the given $tx channel and items are received on the $rx channel. +The $signals are transferred over the given $tx channel and signals are received on the $rx channel. -The given $max_items_size specifies the maximum byte size of the returned items. +The given $max_items_size specifies the maximum byte size of the returned signals. */ -transfer_and_receive --rx/Channel --tx/Channel items/Items max_items_size/int -> Items: - result := rmt_transfer_and_read_ tx.num rx.num items.bytes_ max_items_size - return Items.from_bytes result +transfer_and_receive --rx/Channel --tx/Channel signals/Signals max_items_size/int -> Signals: + result := rmt_transfer_and_read_ tx.num rx.num signals.bytes_ max_items_size + return Signals.from_bytes result resource_group_ ::= rmt_init_ @@ -221,8 +221,8 @@ rmt_config_tx_ pin_num/int channel_num/int mem_block_num/int clk_div/int flags/i loop_en/bool idle_output_en/bool idle_level/int: #primitive.rmt.config_tx -rmt_transfer_ tx_ch/int items_bytes/*/Blob*/: +rmt_transfer_ tx_ch/int signals_bytes/*/Blob*/: #primitive.rmt.transfer -rmt_transfer_and_read_ tx_ch/int rx_ch/int items_bytes/*/Blob*/ max_output_len/int: +rmt_transfer_and_read_ tx_ch/int rx_ch/int signals_bytes/*/Blob*/ max_output_len/int: #primitive.rmt.transfer_and_read diff --git a/tests/rmt_signals_test.toit b/tests/rmt_signals_test.toit new file mode 100644 index 000000000..9ee154b97 --- /dev/null +++ b/tests/rmt_signals_test.toit @@ -0,0 +1,99 @@ +// Copyright (C) 2022 Toitware ApS. +// Use of this source code is governed by a Zero-Clause BSD license that can +// be found in the tests/LICENSE file. + +import expect show * +import rmt show Signals + +main: + test_signals_construction + test_signals_getters + test_signals_setter + test_signals_do + +test_signals_construction: + signals := Signals 4 + expect_equals 4 signals.size + expect_equals 8 signals.bytes_.size + + signals = Signals 5 + expect_equals 5 signals.size + expect_equals 12 signals.bytes_.size + + bytes := #[0x11, 0x22, 0x33, 0x44] + signals = Signals.from_bytes bytes + expect_equals 2 signals.size + expect_bytes_equal bytes signals.bytes_ + + bytes = #[0x11, 0x22, 0x33, 0x44, 0x55] + expect_throw "INVALID_ARGUMENT": + Signals.from_bytes bytes + +test_signals_getters: + bytes := #[ + 0x00, 0x00, + 0xFF, 0xFF, + 0xFF, 0x7F, + 0x00, 0x80 + ] + signals := Signals.from_bytes bytes + expect_equals 0 (signals.item_level 0) + expect_equals 0 (signals.item_period 0) + + expect_equals 1 (signals.item_level 1) + expect_equals 0x7FFF (signals.item_period 1) + + expect_equals 0 (signals.item_level 2) + expect_equals 0x7FFF (signals.item_period 2) + + expect_equals 1 (signals.item_level 3) + expect_equals 0 (signals.item_period 3) + + expect_throw "OUT_OF_BOUNDS": signals.item_level -1 + expect_throw "OUT_OF_BOUNDS": signals.item_period -1 + expect_throw "OUT_OF_BOUNDS": signals.item_level 4 + expect_throw "OUT_OF_BOUNDS": signals.item_period 4 + +test_signals_setter: + signals := Signals 3 + signals.do: | period level | + expect_equals 0 period + expect_equals 0 level + + signals.set_item 0 8 1 + expect_equals 8 + signals.item_period 0 + expect_equals 1 + signals.item_level 0 + + signals.set_item 1 0x7FFF 0 + expect_equals 0x7FFF + signals.item_period 1 + expect_equals 0 + signals.item_level 1 + + signals.set_item 2 0 1 + expect_equals 0 + signals.item_period 2 + expect_equals 1 + signals.item_level 0 + +test_signals_do: + bytes := #[ + 0x00, 0x00, + 0x01, 0x00, + 0x02, 0x00, + 0x03, 0x00 + ] + signals := Signals.from_bytes bytes + item_count := 0 + signals.do: | period level | + expect_equals item_count period + expect_equals 0 level + item_count++ + expect_equals 4 item_count + + signals = Signals 3 + item_count = 0 + signals.do: item_count++ + expect_equals 3 item_count From e1f4e37da4b7d098885defed1da2e9b08ae2f089 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 11 Mar 2022 14:51:49 +0100 Subject: [PATCH 68/84] amount -> number --- lib/rmt.toit | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index e97a37530..6dc36bdae 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -15,7 +15,7 @@ $Signals represent a collection of signals to be sent by the RMT controller. /** A collection of signals to be transferred or received with the RMT controller. -An RMT signal consists of a level (low or high) and a period (the amount of +An RMT signal consists of a level (low or high) and a period (the number of ticks the level is sustained). # Advanced @@ -26,7 +26,7 @@ At the lower level, a signal consits of 16 bits: 15 bits for the period and 1 bit for the level. Signals must be transfered as pairs also known as an item. */ class Signals: - /** The amount of signals in the collection. */ + /** The number of signals in the collection. */ size/int bytes_/ByteArray @@ -138,7 +138,7 @@ class Channel: - $mem_block_num is the number of memory blocks (512 bytes) used by this channel. - $clk_div is the source clock divider. Must be in the range [0,255]. - $flags is the configuration flags. See the ESP-IDF documentation for available flags. - - $idle_threshold is the amount of clock cycles the receiver will run without seeing an edge. + - $idle_threshold is the number of clock cycles the receiver will run without seeing an edge. - $filter_en is whether the filter is enabled. - $filter_ticks_thresh pulses shorter than this value is filtered away. Only works with $filter_en. The value must be in the range [0,255]. From 4a7d54c6e63ef3b6c297b77b746ab47c50acc589 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 11 Mar 2022 15:05:10 +0100 Subject: [PATCH 69/84] Add alternating constructors --- lib/rmt.toit | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 6dc36bdae..2b4926e6d 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -32,7 +32,7 @@ class Signals: bytes_/ByteArray /** - Constructs a collection of signals of the given $size. + Creates a collection of signals of the given $size. All signals are initialized to 0 period and 0 level. @@ -45,10 +45,42 @@ class Signals: bytes_ = ByteArray round_up (size * 2) 4 + /** + Creates signals that alternate between a level of 0 and 1 with the periods + given in the indexable collection $periods. + + The level of the first signal is $first_level. + */ + constructor.alternating --first_level/int periods: + if first_level != 0 and first_level != 1: throw "INVALID_ARGUMENT" + + return Signals.alternating periods.size --first_level=first_level: | idx | + periods[idx] + + /** + Creates items that alternate between a level of 0 and 1 with the periods + given by successive calls to the block. + + The $block is called with the signal index and the level it is created with. + + The level of the first signal is $first_level. + */ + constructor.alternating size/int --first_level [block]: + if first_level != 0 and first_level != 1: throw "INVALID_ARGUMENT" + + signals := Signals size + level := first_level + size.repeat: + signals.set_signal it level (block.call it level) + level = level ^ 1 + + return signals + + // TODO what's a nice convenient constructor for populating Signals with known values? /** - Constructs a collection of signals from the given $bytes. + Creates a collection of signals from the given $bytes. The $bytes size must be divisible by 4. From 00179b6e30cc8b83752124236b088fe89b972a8d Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 11 Mar 2022 15:10:35 +0100 Subject: [PATCH 70/84] Make set throw when level or period out of range --- lib/rmt.toit | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 2b4926e6d..2a37fc1f2 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -121,8 +121,9 @@ class Signals: set_signal i/int period/int level/int -> none: check_bounds_ i idx := i * 2 - period = period & 0x7FFF - level = level & 0b1 + if not 0 <= period <= 0x7FFF: throw "INVALID_ARGUMENT" + if level != 0 and level != 1: throw "INVALID_ARGUMENT" + bytes_[idx] = period & 0xFF bytes_[idx + 1] = (period >> 8 ) | (level << 7) From 75ed26fbcf895f11a80d5c624b7172ab73a0085f Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 11 Mar 2022 15:10:53 +0100 Subject: [PATCH 71/84] Expand docs --- lib/rmt.toit | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 2a37fc1f2..e66e7d2ed 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -117,6 +117,10 @@ class Signals: Set the $i'th signal to the given $period and $level. The given $i must be in the range [0,$size[. + + The given $period must be in the range [0,0x7FFF]. + + The given $level must be 0 or 1. */ set_signal i/int period/int level/int -> none: check_bounds_ i @@ -127,7 +131,11 @@ class Signals: bytes_[idx] = period & 0xFF bytes_[idx + 1] = (period >> 8 ) | (level << 7) - /** Invokes the given $block on each signal of this signal collection. */ + /** + Invokes the given $block on each signal of this signal collection. + + The block is invoked with the period and the level of each signal. + */ do [block]: size.repeat: block.call From 98ee3b85ef42321011ad6efa2a6b4b36741d6d37 Mon Sep 17 00:00:00 2001 From: Lau <5364453+lask@users.noreply.github.com> Date: Fri, 11 Mar 2022 15:11:58 +0100 Subject: [PATCH 72/84] Use LITTLE_ENDIAN to get period Co-authored-by: Erik Corry --- lib/rmt.toit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index 2b4926e6d..7746ddb24 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -141,7 +141,7 @@ class Signals: signal_period_ i -> int: idx := i * 2 - return bytes_[idx] | ((bytes_[idx + 1] & 0x7F) << 8) + return (LITTLE_ENDIAN.uint16 bytes_ idx) & 0x7fff /** An RMT channel. From 69d40fb9b03465d6ff0583a2b5c64cdd61a9dff3 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 11 Mar 2022 15:47:24 +0100 Subject: [PATCH 73/84] Update mem block docs --- lib/rmt.toit | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index e66e7d2ed..c4625b208 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -176,13 +176,19 @@ class Channel: /** Configure the channel for RX. - - $mem_block_num is the number of memory blocks (512 bytes) used by this channel. + - $mem_block_num is the number of memory blocks (256 bytes or 128 signals) + used by this channel. - $clk_div is the source clock divider. Must be in the range [0,255]. - $flags is the configuration flags. See the ESP-IDF documentation for available flags. - $idle_threshold is the number of clock cycles the receiver will run without seeing an edge. - $filter_en is whether the filter is enabled. - $filter_ticks_thresh pulses shorter than this value is filtered away. Only works with $filter_en. The value must be in the range [0,255]. + + # Advanced + If $mem_block_num is greater than 1, then it will take the memory of the + subsequent channels. For instance, if channel 2 is configured with a + $mem_block_num = 3, then channels 3 and 4 are unusable. */ config_rx --mem_block_num/int=1 @@ -197,7 +203,8 @@ class Channel: /** Configure the channel for TX. - - $mem_block_num is the number of memory blocks (512 bytes) used by this channel. + - $mem_block_num is the number of memory blocks (256 bytes or 128 signals) + used by this channel. - $clk_div is the source clock divider. Must be in the range [0,255]. - $flags is the configuration flags. See the ESP-IDF documentation for available flags. - $carrier_en is whether a carrier wave is used. @@ -208,6 +215,11 @@ class Channel: - $loop_en is whether the transmitter continously writes the provided signals in a loop. - $idle_output_en is whether the transmitter outputs when idle. - $idle_level is the level transmitted by the transmitter when idle. + + # Advanced + If $mem_block_num is greater than 1, then it will take the memory of the + subsequent channels. For instance, if channel 2 is configured with a + $mem_block_num = 3, then channels 3 and 4 are unusable. */ config_tx --mem_block_num/int=1 From 445b6aad7056fe5f92dd7ce139518ca08e8f28c1 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 11 Mar 2022 15:54:18 +0100 Subject: [PATCH 74/84] 24 comes after 23 --- src/primitive.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/primitive.h b/src/primitive.h index 8ebe03792..d9c665473 100644 --- a/src/primitive.h +++ b/src/primitive.h @@ -921,7 +921,7 @@ namespace toit { _A_T_##t11(10, n11); \ _A_T_##t12(11, n12); -#define _OVERRIDE(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _34, NAME, ...) NAME +#define _OVERRIDE(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, NAME, ...) NAME #define ARGS(...) \ _OVERRIDE(__VA_ARGS__, \ From eb4fb1c5960aafbdca367f8e7ea5bf65d0339baf Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 11 Mar 2022 16:01:47 +0100 Subject: [PATCH 75/84] Add missing import --- lib/rmt.toit | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/rmt.toit b/lib/rmt.toit index 37d0f8490..d71cc4bc4 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -3,6 +3,7 @@ // found in the lib/LICENSE file. import gpio +import binary show LITTLE_ENDIAN /** Support for the ESP32 Remote Control (RMT). From e8fc90e68fe82c9cbbef4019c395121a0574efdc Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 11 Mar 2022 16:01:55 +0100 Subject: [PATCH 76/84] merge ifs --- lib/rmt.toit | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index d71cc4bc4..25d5db023 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -126,8 +126,7 @@ class Signals: set_signal i/int period/int level/int -> none: check_bounds_ i idx := i * 2 - if not 0 <= period <= 0x7FFF: throw "INVALID_ARGUMENT" - if level != 0 and level != 1: throw "INVALID_ARGUMENT" + if not 0 <= period <= 0x7FFF or level != 0 and level != 1: throw "INVALID_ARGUMENT" bytes_[idx] = period & 0xFF bytes_[idx + 1] = (period >> 8 ) | (level << 7) From 2af2dc005f984887ad6c14115e917f1592e82ab0 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 11 Mar 2022 16:03:53 +0100 Subject: [PATCH 77/84] Use macro to initialize rmt configs --- src/resources/rmt_esp32.cc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 6fab7db49..362607da8 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -112,10 +112,8 @@ PRIMITIVE(config_tx) { bool, carrier_en, int, carrier_freq_hz, int, carrier_level, int, carrier_duty_percent, bool, loop_en, bool, idle_output_en, int, idle_level) - rmt_config_t config = { }; + rmt_config_t config = RMT_DEFAULT_CONFIG_TX((gpio_num_t) pin_num, (rmt_channel_t) channel_num); - config.gpio_num = (gpio_num_t) pin_num; - config.channel = (rmt_channel_t) channel_num; config.mem_block_num = mem_block_num; config.clk_div = clk_div; config.flags = flags; @@ -140,10 +138,8 @@ PRIMITIVE(config_rx) { ARGS(int, pin_num, int, channel_num, int, mem_block_num, int, clk_div, int, flags, int, idle_threshold, bool, filter_en, int, filter_ticks_thresh, int, rx_buffer_size) - rmt_config_t config = { }; + rmt_config_t config = RMT_DEFAULT_CONFIG_RX((gpio_num_t) pin_num, (rmt_channel_t) channel_num); - config.gpio_num = (gpio_num_t) pin_num; - config.channel = (rmt_channel_t) channel_num; config.mem_block_num = mem_block_num; config.clk_div = clk_div; config.flags = flags; From ce453697bfe30127ed6cec07f174ce85b193eb8a Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 11 Mar 2022 16:07:00 +0100 Subject: [PATCH 78/84] Remove unnecessary const cast --- src/resources/rmt_esp32.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 362607da8..8a9b7e588 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -160,7 +160,7 @@ PRIMITIVE(transfer) { ARGS(int, tx_num, Blob, items_bytes) if (items_bytes.length() % 4 != 0) INVALID_ARGUMENT; - rmt_item32_t* items = reinterpret_cast(const_cast(items_bytes.address())); + const rmt_item32_t* items = reinterpret_cast(items_bytes.address()); esp_err_t err = rmt_write_items((rmt_channel_t) tx_num, items, items_bytes.length() / 4, true); if ( err != ESP_OK) return Primitive::os_error(err, process); From 6ed704893c7f8d0c776427bb82c456218d93c3c9 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Fri, 11 Mar 2022 16:11:46 +0100 Subject: [PATCH 79/84] Change cast style --- src/resources/rmt_esp32.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index 8a9b7e588..a90aeb5c0 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -103,7 +103,7 @@ esp_err_t configure(const rmt_config_t* config, rmt_channel_t channel_num, size_ err = rmt_set_source_clk(channel_num, RMT_BASECLK_APB); if (ESP_OK != err) return err; - err = rmt_driver_install((rmt_channel_t) channel_num, rx_buffer_size, 0); + err = rmt_driver_install(channel_num, rx_buffer_size, 0); return err; } @@ -112,7 +112,7 @@ PRIMITIVE(config_tx) { bool, carrier_en, int, carrier_freq_hz, int, carrier_level, int, carrier_duty_percent, bool, loop_en, bool, idle_output_en, int, idle_level) - rmt_config_t config = RMT_DEFAULT_CONFIG_TX((gpio_num_t) pin_num, (rmt_channel_t) channel_num); + rmt_config_t config = RMT_DEFAULT_CONFIG_TX(static_cast(pin_num), static_cast(channel_num)); config.mem_block_num = mem_block_num; config.clk_div = clk_div; @@ -121,14 +121,14 @@ PRIMITIVE(config_tx) { rmt_tx_config_t tx_config = { 0 }; tx_config.carrier_en = carrier_en; tx_config.carrier_freq_hz = carrier_freq_hz; - tx_config.carrier_level = (rmt_carrier_level_t) carrier_level; + tx_config.carrier_level = static_cast(carrier_level); tx_config.carrier_duty_percent = carrier_duty_percent; tx_config.loop_en = loop_en; tx_config.idle_output_en = idle_output_en; - tx_config.idle_level = (rmt_idle_level_t) idle_level; + tx_config.idle_level = static_cast(idle_level); config.tx_config = tx_config; - esp_err_t err = configure(&config, (rmt_channel_t) channel_num, 0, process); + esp_err_t err = configure(&config, static_cast(channel_num), 0, process); if (ESP_OK != err) return Primitive::os_error(err, process); return process->program()->null_object(); @@ -138,7 +138,7 @@ PRIMITIVE(config_rx) { ARGS(int, pin_num, int, channel_num, int, mem_block_num, int, clk_div, int, flags, int, idle_threshold, bool, filter_en, int, filter_ticks_thresh, int, rx_buffer_size) - rmt_config_t config = RMT_DEFAULT_CONFIG_RX((gpio_num_t) pin_num, (rmt_channel_t) channel_num); + rmt_config_t config = RMT_DEFAULT_CONFIG_RX(static_cast(pin_num), static_cast(channel_num)); config.mem_block_num = mem_block_num; config.clk_div = clk_div; @@ -150,7 +150,7 @@ PRIMITIVE(config_rx) { rx_config.filter_ticks_thresh = filter_ticks_thresh; config.rx_config = rx_config; - esp_err_t err = configure(&config,(rmt_channel_t) channel_num, rx_buffer_size, process); + esp_err_t err = configure(&config,static_cast(channel_num), rx_buffer_size, process); if (ESP_OK != err) return Primitive::os_error(err, process); return process->program()->null_object(); @@ -161,7 +161,7 @@ PRIMITIVE(transfer) { if (items_bytes.length() % 4 != 0) INVALID_ARGUMENT; const rmt_item32_t* items = reinterpret_cast(items_bytes.address()); - esp_err_t err = rmt_write_items((rmt_channel_t) tx_num, items, items_bytes.length() / 4, true); + esp_err_t err = rmt_write_items(static_cast(tx_num), items, items_bytes.length() / 4, true); if ( err != ESP_OK) return Primitive::os_error(err, process); return process->program()->null_object(); @@ -196,7 +196,7 @@ PRIMITIVE(transfer_and_read) { err = rmt_rx_start(rx_channel, true); if (err != ESP_OK) return Primitive::os_error(err, process); - err = rmt_write_items((rmt_channel_t) tx_num, items, items_bytes.length() / 4, true); + err = rmt_write_items(static_cast(tx_num), items, items_bytes.length() / 4, true); if (err != ESP_OK) { rmt_rx_stop(rx_channel); return Primitive::os_error(err, process); From 7b08e25a9bbce92ed7770a166fc7ac717c8776f8 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 14 Mar 2022 15:15:11 +0100 Subject: [PATCH 80/84] Fix test --- lib/rmt.toit | 60 +++++++++++----------- tests/rmt_items_test.toit | 99 ------------------------------------- tests/rmt_signals_test.toit | 56 ++++++++++----------- 3 files changed, 55 insertions(+), 160 deletions(-) delete mode 100644 tests/rmt_items_test.toit diff --git a/lib/rmt.toit b/lib/rmt.toit index 25d5db023..f0e766499 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -36,10 +36,10 @@ class Signals: Creates a collection of signals of the given $size. All signals are initialized to 0 period and 0 level. - + # Advanced - If the given $size is not divisible by 2, then the byte array allocted for - $bytes_ is patted with two bytes to make the $bytes_ usable by the RMT + If the given $size is not divisible by 2, then the byte array allocted for + $bytes_ is patted with two bytes to make the $bytes_ usable by the RMT primitives. */ constructor .size: @@ -47,32 +47,32 @@ class Signals: round_up (size * 2) 4 /** - Creates signals that alternate between a level of 0 and 1 with the periods - given in the indexable collection $periods. + Creates signals that alternate between a level of 0 and 1 with the periods + given in the indexable collection $periods. The level of the first signal is $first_level. */ - constructor.alternating --first_level/int periods: + constructor.alternating --first_level/int periods: if first_level != 0 and first_level != 1: throw "INVALID_ARGUMENT" return Signals.alternating periods.size --first_level=first_level: | idx | periods[idx] /** - Creates items that alternate between a level of 0 and 1 with the periods - given by successive calls to the block. + Creates items that alternate between a level of 0 and 1 with the periods + given by successive calls to the block. The $block is called with the signal index and the level it is created with. The level of the first signal is $first_level. - */ + */ constructor.alternating size/int --first_level [block]: if first_level != 0 and first_level != 1: throw "INVALID_ARGUMENT" signals := Signals size level := first_level size.repeat: - signals.set_signal it level (block.call it level) + signals.set_signal it (block.call it level) level level = level ^ 1 return signals @@ -86,8 +86,8 @@ class Signals: The $bytes size must be divisible by 4. # Advanced - The bytes must correspond to bytes produced by the RMT primitives. The - primitives operate with pairs of signals (called an item) which is the + The bytes must correspond to bytes produced by the RMT primitives. The + primitives operate with pairs of signals (called an item) which is the reason the $bytes size must be divisible by 4. */ constructor.from_bytes bytes/ByteArray: @@ -96,18 +96,18 @@ class Signals: bytes_ = bytes size = bytes_.size / 2 - /** - Gets the signal period of the $i'th signal. - + /** + Gets the signal period of the $i'th signal. + The given $i must be in the range [0,$size[. */ signal_period i/int -> int: check_bounds_ i return signal_period_ i - /** - Gets the signal level of the $i'th signal. - + /** + Gets the signal level of the $i'th signal. + The given $i must be in the range [0,$size[. */ signal_level i/int -> int: @@ -118,7 +118,7 @@ class Signals: Set the $i'th signal to the given $period and $level. The given $i must be in the range [0,$size[. - + The given $period must be in the range [0,0x7FFF]. The given $level must be 0 or 1. @@ -131,9 +131,9 @@ class Signals: bytes_[idx] = period & 0xFF bytes_[idx + 1] = (period >> 8 ) | (level << 7) - /** - Invokes the given $block on each signal of this signal collection. - + /** + Invokes the given $block on each signal of this signal collection. + The block is invoked with the period and the level of each signal. */ do [block]: @@ -165,9 +165,9 @@ class Channel: res_/ByteArray? := null - /** - Constructs a channel using the given $num using the given $pin. - + /** + Constructs a channel using the given $num using the given $pin. + The givn $num must be in the range [0,7] and must not be in use. */ constructor .pin .num: @@ -176,7 +176,7 @@ class Channel: /** Configure the channel for RX. - - $mem_block_num is the number of memory blocks (256 bytes or 128 signals) + - $mem_block_num is the number of memory blocks (256 bytes or 128 signals) used by this channel. - $clk_div is the source clock divider. Must be in the range [0,255]. - $flags is the configuration flags. See the ESP-IDF documentation for available flags. @@ -184,9 +184,9 @@ class Channel: - $filter_en is whether the filter is enabled. - $filter_ticks_thresh pulses shorter than this value is filtered away. Only works with $filter_en. The value must be in the range [0,255]. - + # Advanced - If $mem_block_num is greater than 1, then it will take the memory of the + If $mem_block_num is greater than 1, then it will take the memory of the subsequent channels. For instance, if channel 2 is configured with a $mem_block_num = 3, then channels 3 and 4 are unusable. */ @@ -203,7 +203,7 @@ class Channel: /** Configure the channel for TX. - - $mem_block_num is the number of memory blocks (256 bytes or 128 signals) + - $mem_block_num is the number of memory blocks (256 bytes or 128 signals) used by this channel. - $clk_div is the source clock divider. Must be in the range [0,255]. - $flags is the configuration flags. See the ESP-IDF documentation for available flags. @@ -217,7 +217,7 @@ class Channel: - $idle_level is the level transmitted by the transmitter when idle. # Advanced - If $mem_block_num is greater than 1, then it will take the memory of the + If $mem_block_num is greater than 1, then it will take the memory of the subsequent channels. For instance, if channel 2 is configured with a $mem_block_num = 3, then channels 3 and 4 are unusable. */ diff --git a/tests/rmt_items_test.toit b/tests/rmt_items_test.toit deleted file mode 100644 index aae77e0da..000000000 --- a/tests/rmt_items_test.toit +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (C) 2022 Toitware ApS. -// Use of this source code is governed by a Zero-Clause BSD license that can -// be found in the tests/LICENSE file. - -import expect show * -import rmt show Items - -main: - test_items_construction - test_items_getters - test_items_setter - test_items_do - -test_items_construction: - items := Items 4 - expect_equals 4 items.size - expect_equals 8 items.bytes_.size - - items = Items 5 - expect_equals 5 items.size - expect_equals 12 items.bytes_.size - - bytes := #[0x11, 0x22, 0x33, 0x44] - items = Items.from_bytes bytes - expect_equals 2 items.size - expect_bytes_equal bytes items.bytes_ - - bytes = #[0x11, 0x22, 0x33, 0x44, 0x55] - expect_throw "INVALID_ARGUMENT": - Items.from_bytes bytes - -test_items_getters: - bytes := #[ - 0x00, 0x00, - 0xFF, 0xFF, - 0xFF, 0x7F, - 0x00, 0x80 - ] - items := Items.from_bytes bytes - expect_equals 0 (items.item_level 0) - expect_equals 0 (items.item_period 0) - - expect_equals 1 (items.item_level 1) - expect_equals 0x7FFF (items.item_period 1) - - expect_equals 0 (items.item_level 2) - expect_equals 0x7FFF (items.item_period 2) - - expect_equals 1 (items.item_level 3) - expect_equals 0 (items.item_period 3) - - expect_throw "OUT_OF_BOUNDS": items.item_level -1 - expect_throw "OUT_OF_BOUNDS": items.item_period -1 - expect_throw "OUT_OF_BOUNDS": items.item_level 4 - expect_throw "OUT_OF_BOUNDS": items.item_period 4 - -test_items_setter: - items := Items 3 - items.do: | period level | - expect_equals 0 period - expect_equals 0 level - - items.set_item 0 8 1 - expect_equals 8 - items.item_period 0 - expect_equals 1 - items.item_level 0 - - items.set_item 1 0x7FFF 0 - expect_equals 0x7FFF - items.item_period 1 - expect_equals 0 - items.item_level 1 - - items.set_item 2 0 1 - expect_equals 0 - items.item_period 2 - expect_equals 1 - items.item_level 0 - -test_items_do: - bytes := #[ - 0x00, 0x00, - 0x01, 0x00, - 0x02, 0x00, - 0x03, 0x00 - ] - items := Items.from_bytes bytes - item_count := 0 - items.do: | period level | - expect_equals item_count period - expect_equals 0 level - item_count++ - expect_equals 4 item_count - - items = Items 3 - item_count = 0 - items.do: item_count++ - expect_equals 3 item_count diff --git a/tests/rmt_signals_test.toit b/tests/rmt_signals_test.toit index 9ee154b97..06101f5e5 100644 --- a/tests/rmt_signals_test.toit +++ b/tests/rmt_signals_test.toit @@ -30,53 +30,47 @@ test_signals_construction: Signals.from_bytes bytes test_signals_getters: - bytes := #[ - 0x00, 0x00, - 0xFF, 0xFF, - 0xFF, 0x7F, - 0x00, 0x80 - ] - signals := Signals.from_bytes bytes - expect_equals 0 (signals.item_level 0) - expect_equals 0 (signals.item_period 0) - - expect_equals 1 (signals.item_level 1) - expect_equals 0x7FFF (signals.item_period 1) + signals := Signals.alternating --first_level=0 [0, 0x7fff, 0x7fff, 0] + expect_equals 0 (signals.signal_level 0) + expect_equals 0 (signals.signal_period 0) + + expect_equals 1 (signals.signal_level 1) + expect_equals 0x7FFF (signals.signal_period 1) - expect_equals 0 (signals.item_level 2) - expect_equals 0x7FFF (signals.item_period 2) + expect_equals 0 (signals.signal_level 2) + expect_equals 0x7FFF (signals.signal_period 2) - expect_equals 1 (signals.item_level 3) - expect_equals 0 (signals.item_period 3) + expect_equals 1 (signals.signal_level 3) + expect_equals 0 (signals.signal_period 3) - expect_throw "OUT_OF_BOUNDS": signals.item_level -1 - expect_throw "OUT_OF_BOUNDS": signals.item_period -1 - expect_throw "OUT_OF_BOUNDS": signals.item_level 4 - expect_throw "OUT_OF_BOUNDS": signals.item_period 4 + expect_throw "OUT_OF_BOUNDS": signals.signal_level -1 + expect_throw "OUT_OF_BOUNDS": signals.signal_period -1 + expect_throw "OUT_OF_BOUNDS": signals.signal_level 4 + expect_throw "OUT_OF_BOUNDS": signals.signal_period 4 test_signals_setter: signals := Signals 3 signals.do: | period level | expect_equals 0 period expect_equals 0 level - - signals.set_item 0 8 1 - expect_equals 8 - signals.item_period 0 + + signals.set_signal 0 8 1 + expect_equals 8 + signals.signal_period 0 expect_equals 1 - signals.item_level 0 + signals.signal_level 0 - signals.set_item 1 0x7FFF 0 + signals.set_signal 1 0x7FFF 0 expect_equals 0x7FFF - signals.item_period 1 + signals.signal_period 1 expect_equals 0 - signals.item_level 1 + signals.signal_level 1 - signals.set_item 2 0 1 + signals.set_signal 2 0 1 expect_equals 0 - signals.item_period 2 + signals.signal_period 2 expect_equals 1 - signals.item_level 0 + signals.signal_level 0 test_signals_do: bytes := #[ From b6fc277bb269addd61292908c48475220479f166 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 14 Mar 2022 15:24:15 +0100 Subject: [PATCH 81/84] Add test of alternating constructor --- tests/rmt_signals_test.toit | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/rmt_signals_test.toit b/tests/rmt_signals_test.toit index 06101f5e5..29aa9aca5 100644 --- a/tests/rmt_signals_test.toit +++ b/tests/rmt_signals_test.toit @@ -7,6 +7,8 @@ import rmt show Signals main: test_signals_construction + test_signals_from_bytes + test_signals_from_alternating test_signals_getters test_signals_setter test_signals_do @@ -20,8 +22,10 @@ test_signals_construction: expect_equals 5 signals.size expect_equals 12 signals.bytes_.size + +test_signals_from_bytes: bytes := #[0x11, 0x22, 0x33, 0x44] - signals = Signals.from_bytes bytes + signals := Signals.from_bytes bytes expect_equals 2 signals.size expect_bytes_equal bytes signals.bytes_ @@ -29,6 +33,29 @@ test_signals_construction: expect_throw "INVALID_ARGUMENT": Signals.from_bytes bytes +test_signals_from_alternating: + periods := [0,1,2,3,4] + signals := Signals.alternating --first_level=0 periods + + level := 0 + periods.size.repeat: + expect_equals level (signals.signal_level it) + level = level ^ 1 + expect_equals it (signals.signal_period it) + + signals = Signals.alternating --first_level=1 periods + level = 1 + periods.size.repeat: + expect_equals level (signals.signal_level it) + level = level ^ 1 + expect_equals it (signals.signal_period it) + + expect_throw "INVALID_ARGUMENT": + Signals.alternating --first_level=2 [] + + expect_throw "INVALID_ARGUMENT": + Signals.alternating --first_level=0 [0x8FFF] + test_signals_getters: signals := Signals.alternating --first_level=0 [0, 0x7fff, 0x7fff, 0] expect_equals 0 (signals.signal_level 0) From 2fe9ada4671d9e62e6a7befabd23a9dffd20c5c1 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 14 Mar 2022 15:27:59 +0100 Subject: [PATCH 82/84] Naming and spelling --- lib/rmt.toit | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index f0e766499..d1eb9bb2a 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -244,14 +244,14 @@ transfer channel/Channel signals/Signals -> none: rmt_transfer_ channel.num signals.bytes_ /** -Transfers the given $signals while simoultaniously receiving. +Transfers the given $signals while simultaneously receiving. The $signals are transferred over the given $tx channel and signals are received on the $rx channel. -The given $max_items_size specifies the maximum byte size of the returned signals. +The given $max_returned_bytes specifies the maximum byte size of the returned signals. */ -transfer_and_receive --rx/Channel --tx/Channel signals/Signals max_items_size/int -> Signals: - result := rmt_transfer_and_read_ tx.num rx.num signals.bytes_ max_items_size +transfer_and_receive --rx/Channel --tx/Channel signals/Signals max_returned_bytes/int -> Signals: + result := rmt_transfer_and_read_ tx.num rx.num signals.bytes_ max_returned_bytes return Signals.from_bytes result resource_group_ ::= rmt_init_ From 2ccd99c03f2b36f3d4e65a39944301ebbd3027c9 Mon Sep 17 00:00:00 2001 From: Lau Skorstengaard Date: Mon, 14 Mar 2022 15:41:20 +0100 Subject: [PATCH 83/84] Fix Erik's comment --- src/resources/rmt_esp32.cc | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/resources/rmt_esp32.cc b/src/resources/rmt_esp32.cc index a90aeb5c0..3ae793c9e 100644 --- a/src/resources/rmt_esp32.cc +++ b/src/resources/rmt_esp32.cc @@ -118,15 +118,13 @@ PRIMITIVE(config_tx) { config.clk_div = clk_div; config.flags = flags; config.rmt_mode = RMT_MODE_TX; - rmt_tx_config_t tx_config = { 0 }; - tx_config.carrier_en = carrier_en; - tx_config.carrier_freq_hz = carrier_freq_hz; - tx_config.carrier_level = static_cast(carrier_level); - tx_config.carrier_duty_percent = carrier_duty_percent; - tx_config.loop_en = loop_en; - tx_config.idle_output_en = idle_output_en; - tx_config.idle_level = static_cast(idle_level); - config.tx_config = tx_config; + config.tx_config.carrier_en = carrier_en; + config.tx_config.carrier_freq_hz = carrier_freq_hz; + config.tx_config.carrier_level = static_cast(carrier_level); + config.tx_config.carrier_duty_percent = carrier_duty_percent; + config.tx_config.loop_en = loop_en; + config.tx_config.idle_output_en = idle_output_en; + config.tx_config.idle_level = static_cast(idle_level); esp_err_t err = configure(&config, static_cast(channel_num), 0, process); if (ESP_OK != err) return Primitive::os_error(err, process); @@ -144,11 +142,9 @@ PRIMITIVE(config_rx) { config.clk_div = clk_div; config.flags = flags; config.rmt_mode = RMT_MODE_RX; - rmt_rx_config_t rx_config = { 0 }; - rx_config.idle_threshold = idle_threshold; - rx_config.filter_en = filter_en; - rx_config.filter_ticks_thresh = filter_ticks_thresh; - config.rx_config = rx_config; + config.rx_config.idle_threshold = idle_threshold; + config.rx_config.filter_en = filter_en; + config.rx_config.filter_ticks_thresh = filter_ticks_thresh; esp_err_t err = configure(&config,static_cast(channel_num), rx_buffer_size, process); if (ESP_OK != err) return Primitive::os_error(err, process); From 2d9a4bad73d4b1229ba217b4a1749aa75015215d Mon Sep 17 00:00:00 2001 From: Lau <5364453+lask@users.noreply.github.com> Date: Tue, 15 Mar 2022 08:59:55 +0100 Subject: [PATCH 84/84] Apply suggestions from code review Co-authored-by: Erik Corry --- lib/rmt.toit | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rmt.toit b/lib/rmt.toit index d1eb9bb2a..7f4b8ccd4 100644 --- a/lib/rmt.toit +++ b/lib/rmt.toit @@ -23,7 +23,7 @@ An RMT signal consists of a level (low or high) and a period (the number of The period is specified in number of ticks, so the actual time the level is sustained is determined by the RMT controller configuration. -At the lower level, a signal consits of 16 bits: 15 bits for the period and 1 +At the lower level, a signal consists of 16 bits: 15 bits for the period and 1 bit for the level. Signals must be transfered as pairs also known as an item. */ class Signals: @@ -39,7 +39,7 @@ class Signals: # Advanced If the given $size is not divisible by 2, then the byte array allocted for - $bytes_ is patted with two bytes to make the $bytes_ usable by the RMT + $bytes_ is padded with two bytes to make the $bytes_ usable by the RMT primitives. */ constructor .size: @@ -66,14 +66,14 @@ class Signals: The level of the first signal is $first_level. */ - constructor.alternating size/int --first_level [block]: + constructor.alternating size/int --first_level/int [block]: if first_level != 0 and first_level != 1: throw "INVALID_ARGUMENT" signals := Signals size level := first_level size.repeat: signals.set_signal it (block.call it level) level - level = level ^ 1 + level ^= 1 return signals