diff --git a/Kconfig b/Kconfig index 20f88b8d2..256f97a9c 100644 --- a/Kconfig +++ b/Kconfig @@ -35,3 +35,23 @@ config ARDUINO_ENTRY default y endif + +if USB_DEVICE_STACK_NEXT + +config USB_DEVICE_PRODUCT + string "USB Device Product" + default "Arduino Generic board" + +config USB_DEVICE_MANUFACTURER + string "USB Device Manufacturer" + default "Arduino" + +config USB_DEVICE_VID + hex "USB Device Vendor ID" + default 0x2341 + +config USB_DEVICE_PID + hex "USB Device Product ID" + default 0x0001 + +endif diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h index 9657a7125..358b52c1e 100644 --- a/cores/arduino/Arduino.h +++ b/cores/arduino/Arduino.h @@ -12,19 +12,22 @@ #include #include #include +#include #include +#include +#if DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios) > 0 +/* Note: DT_REG_ADDR needs an expanded argument or it will not work properly */ +#define DIGITAL_PIN_MATCHES(dev_pha, pin, dev, num) \ + (((dev == DT_REG_ADDR(dev_pha)) && (num == pin)) ? 1 : 0) #define DIGITAL_PIN_EXISTS(n, p, i, dev, num) \ - (((dev == DT_REG_ADDR(DT_PHANDLE_BY_IDX(n, p, i))) && \ - (num == DT_PHA_BY_IDX(n, p, i, pin))) \ - ? 1 \ - : 0) + DIGITAL_PIN_MATCHES(DT_PHANDLE_BY_IDX(n, p, i), DT_PHA_BY_IDX(n, p, i, pin), dev, num) /* Check all pins are defined only once */ #define DIGITAL_PIN_CHECK_UNIQUE(i, _) \ - ((DT_FOREACH_PROP_ELEM_SEP_VARGS( \ - DT_PATH(zephyr_user), digital_pin_gpios, DIGITAL_PIN_EXISTS, (+), \ - DT_REG_ADDR(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), digital_pin_gpios, i)), \ + ((DT_FOREACH_PROP_ELEM_SEP_VARGS( \ + DT_PATH(zephyr_user), digital_pin_gpios, DIGITAL_PIN_EXISTS, (+), \ + DT_REG_ADDR(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), digital_pin_gpios, i)), \ DT_PHA_BY_IDX(DT_PATH(zephyr_user), digital_pin_gpios, i, pin))) == 1) #if !LISTIFY(DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios), DIGITAL_PIN_CHECK_UNIQUE, (&&)) @@ -32,23 +35,29 @@ #endif #undef DIGITAL_PIN_CHECK_UNIQUE +#endif + +// Helper macro to get Arduino pin number from device tree alias +#define DIGITAL_PIN_GPIOS_FIND_NODE(node) \ + DIGITAL_PIN_GPIOS_FIND_PIN(DT_REG_ADDR(DT_PHANDLE_BY_IDX(node, gpios, 0)), \ + DT_PHA_BY_IDX(node, gpios, 0, pin)) /* Return the index of it if matched, oterwise return 0 */ #define LED_BUILTIN_INDEX_BY_REG_AND_PINNUM(n, p, i, dev, num) \ (DIGITAL_PIN_EXISTS(n, p, i, dev, num) ? i : 0) /* Only matched pin returns non-zero value, so the sum is matched pin's index */ -#define DIGITAL_PIN_GPIOS_FIND_PIN(dev, pin) \ - DT_FOREACH_PROP_ELEM_SEP_VARGS(DT_PATH(zephyr_user), digital_pin_gpios, \ - LED_BUILTIN_INDEX_BY_REG_AND_PINNUM, (+), dev, pin) +#define DIGITAL_PIN_GPIOS_FIND_PIN(dev, pin) \ + DT_FOREACH_PROP_ELEM_SEP_VARGS(DT_PATH(zephyr_user), digital_pin_gpios, \ + LED_BUILTIN_INDEX_BY_REG_AND_PINNUM, (+), dev, pin) #if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), builtin_led_gpios) && \ (DT_PROP_LEN(DT_PATH(zephyr_user), builtin_led_gpios) > 0) -#if !(DT_FOREACH_PROP_ELEM_SEP_VARGS( \ - DT_PATH(zephyr_user), digital_pin_gpios, DIGITAL_PIN_EXISTS, (+), \ - DT_REG_ADDR(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), builtin_led_gpios, 0)), \ - DT_PHA_BY_IDX(DT_PATH(zephyr_user), builtin_led_gpios, 0, pin)) > 0) +#if !(DT_FOREACH_PROP_ELEM_SEP_VARGS( \ + DT_PATH(zephyr_user), digital_pin_gpios, DIGITAL_PIN_EXISTS, (+), \ + DT_REG_ADDR(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), builtin_led_gpios, 0)), \ + DT_PHA_BY_IDX(DT_PATH(zephyr_user), builtin_led_gpios, 0, pin)) > 0) #warning "pin not found in digital_pin_gpios" #else #define ZARD_LED_BUILTIN \ @@ -60,9 +69,10 @@ /* If digital-pin-gpios is not defined, tries to use the led0 alias */ #elif DT_NODE_EXISTS(DT_ALIAS(led0)) -#if !(DT_FOREACH_PROP_ELEM_SEP_VARGS(DT_PATH(zephyr_user), digital_pin_gpios, DIGITAL_PIN_EXISTS, \ - (+), DT_REG_ADDR(DT_PHANDLE_BY_IDX(DT_ALIAS(led0), gpios, 0)), \ - DT_PHA_BY_IDX(DT_ALIAS(led0), gpios, 0, pin)) > 0) +#if !(DT_FOREACH_PROP_ELEM_SEP_VARGS(DT_PATH(zephyr_user), digital_pin_gpios, DIGITAL_PIN_EXISTS, \ + (+), \ + DT_REG_ADDR(DT_PHANDLE_BY_IDX(DT_ALIAS(led0), gpios, 0)), \ + DT_PHA_BY_IDX(DT_ALIAS(led0), gpios, 0, pin)) > 0) #warning "pin not found in digital_pin_gpios" #else #define ZARD_LED_BUILTIN \ @@ -79,17 +89,38 @@ * enum digitalPins { D0, D1, ... LED... NUM_OF_DIGITAL_PINS }; */ enum digitalPins { +#if DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios) > 0 DT_FOREACH_PROP_ELEM_SEP(DT_PATH(zephyr_user), digital_pin_gpios, DN_ENUMS, (, )), +#endif NUM_OF_DIGITAL_PINS }; #ifdef CONFIG_ADC -#define AN_ENUMS(n, p, i) A ## i = DIGITAL_PIN_GPIOS_FIND_PIN( \ - DT_REG_ADDR(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), p, i)), \ - DT_PHA_BY_IDX(DT_PATH(zephyr_user), p, i, pin)), -enum analogPins { DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), - adc_pin_gpios, AN_ENUMS) }; +#define AN_ENUMS(n, p, i) \ + A##i = DIGITAL_PIN_GPIOS_FIND_PIN(DT_REG_ADDR(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), p, i)), \ + DT_PHA_BY_IDX(DT_PATH(zephyr_user), p, i, pin)), + +enum analogPins { + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), adc_pin_gpios, AN_ENUMS) +}; + +// We provide analogReadResolution APIs +void analogReadResolution(int bits); + +#endif + +#ifdef CONFIG_DAC + +#undef DAC0 +#undef DAC1 +#undef DAC2 +#undef DAC3 +#define DAC_ENUMS(n, p, i) DAC##i = i, + +enum dacPins { + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), dac_channels, DAC_ENUMS) NUM_OF_DACS +}; #endif @@ -98,6 +129,14 @@ void noInterrupts(void); int digitalPinToInterrupt(pin_size_t pin); +#define digitalPinToPort(x) (x) +#define digitalPinToBitMask(x) (x) +#define portOutputRegister(x) (x) +#define portInputRegister(x) (x) + +void analogReadResolution(int bits); +void analogWriteResolution(int bits); + #include #if !defined(LED_BUILTIN) && defined(ZARD_LED_BUILTIN) @@ -105,5 +144,14 @@ int digitalPinToInterrupt(pin_size_t pin); #endif // LED_BUILTIN #ifdef __cplusplus +#include #include +#include +#include +#include +#include + +// Allow namespace-less operations if Arduino.h is included +using namespace arduino; + #endif // __cplusplus diff --git a/cores/arduino/CMakeLists.txt b/cores/arduino/CMakeLists.txt index 36e337b90..d625dd827 100644 --- a/cores/arduino/CMakeLists.txt +++ b/cores/arduino/CMakeLists.txt @@ -13,6 +13,8 @@ if(NOT DEFINED ARDUINO_BUILD_PATH) zephyr_sources(zephyrSerial.cpp) zephyr_sources(zephyrCommon.cpp) +zephyr_sources(USB.cpp) +zephyr_sources(itoa.cpp) if(CONFIG_USE_ARDUINO_API_RUST_IMPLEMENTATION) zephyr_sources(zephyrPrint.cpp) @@ -21,6 +23,7 @@ endif() if(DEFINED CONFIG_ARDUINO_ENTRY) zephyr_sources(main.cpp) +zephyr_sources(threads.cpp) endif() endif() diff --git a/cores/arduino/SerialUSB.h b/cores/arduino/SerialUSB.h new file mode 100644 index 000000000..ba2ac9c6c --- /dev/null +++ b/cores/arduino/SerialUSB.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#if defined(CONFIG_USB_DEVICE_STACK_NEXT) +#include +extern "C" struct usbd_context *usbd_init_device(usbd_msg_cb_t msg_cb); +#endif + +namespace arduino { + +class SerialUSB_ : public ZephyrSerial { + +public: + SerialUSB_(const struct device *dev) : ZephyrSerial(dev) { + } + + void begin(unsigned long baudrate, uint16_t config); + + void begin(unsigned long baudrate) { + begin(baudrate, SERIAL_8N1); + } + + operator bool() override; + size_t write(const uint8_t *buffer, size_t size) override; + + size_t write(const uint8_t data) override { + return write(&data, 1); + } + + void flush() override; + +protected: + uint32_t dtr = 0; + uint32_t baudrate; + static void baudChangeHandler(const struct device *dev, uint32_t rate); + +private: + bool started = false; + +#if defined(CONFIG_USB_DEVICE_STACK_NEXT) + struct usbd_context *_usbd; + int enable_usb_device_next(); + static void usbd_next_cb(struct usbd_context *const ctx, const struct usbd_msg *msg); + static int usb_disable(); +#endif +}; +} // namespace arduino + +#if (DT_NODE_HAS_PROP(DT_PATH(zephyr_user), cdc_acm) && \ + (CONFIG_USB_CDC_ACM || CONFIG_USBD_CDC_ACM_CLASS)) +extern arduino::SerialUSB_ Serial; +#endif diff --git a/cores/arduino/USB.cpp b/cores/arduino/USB.cpp new file mode 100644 index 000000000..fe3291887 --- /dev/null +++ b/cores/arduino/USB.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2024 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#if ((DT_NODE_HAS_PROP(DT_PATH(zephyr_user), cdc_acm)) && \ + (CONFIG_USB_CDC_ACM || CONFIG_USBD_CDC_ACM_CLASS)) +const struct device *const usb_dev = + DEVICE_DT_GET(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), cdc_acm, 0)); + +void __attribute__((weak)) _on_1200_bps() { + NVIC_SystemReset(); +} + +void arduino::SerialUSB_::baudChangeHandler(const struct device *dev, uint32_t rate) { + (void)dev; // unused + if (rate == 1200) { + usb_disable(); + _on_1200_bps(); + } +} + +#if defined(CONFIG_USB_DEVICE_STACK_NEXT) +int arduino::SerialUSB_::usb_disable() { + // To avoid Cannot perform port reset: 1200-bps touch: setting DTR to OFF: protocol error + k_sleep(K_MSEC(100)); + return usbd_disable(Serial._usbd); +} + +void arduino::SerialUSB_::usbd_next_cb(struct usbd_context *const ctx, const struct usbd_msg *msg) { + if (usbd_can_detect_vbus(ctx)) { + if (msg->type == USBD_MSG_VBUS_READY) { + usbd_enable(ctx); + } + + if (msg->type == USBD_MSG_VBUS_REMOVED) { + usbd_disable(ctx); + } + } + + if (msg->type == USBD_MSG_CDC_ACM_LINE_CODING) { + uint32_t baudrate; + uart_line_ctrl_get(Serial.uart, UART_LINE_CTRL_BAUD_RATE, &baudrate); + Serial.baudChangeHandler(nullptr, baudrate); + } +} + +int arduino::SerialUSB_::enable_usb_device_next(void) { + int err; + + _usbd = usbd_init_device(arduino::SerialUSB_::usbd_next_cb); + if (_usbd == NULL) { + return -ENODEV; + } + + if (!usbd_can_detect_vbus(_usbd)) { + err = usbd_enable(_usbd); + if (err) { + return err; + } + } + return 0; +} +#endif /* defined(CONFIG_USB_DEVICE_STACK_NEXT) */ + +void arduino::SerialUSB_::begin(unsigned long baudrate, uint16_t config) { + if (!started) { +#ifndef CONFIG_USB_DEVICE_STACK_NEXT + usb_enable(NULL); +#ifndef CONFIG_CDC_ACM_DTE_RATE_CALLBACK_SUPPORT +#warning "Can't read CDC baud change, please enable CONFIG_CDC_ACM_DTE_RATE_CALLBACK_SUPPORT" +#else + cdc_acm_dte_rate_callback_set(usb_dev, SerialUSB_::baudChangeHandler); +#endif +#else + enable_usb_device_next(); +#endif + ZephyrSerial::begin(baudrate, config); + started = true; + } +} + +arduino::SerialUSB_::operator bool() { + uart_line_ctrl_get(uart, UART_LINE_CTRL_DTR, &dtr); + return dtr; +} + +size_t arduino::SerialUSB_::write(const uint8_t *buffer, size_t size) { + if (!Serial) { + return 0; + } + return arduino::ZephyrSerial::write(buffer, size); +} + +void arduino::SerialUSB_::flush() { + if (!Serial) { + return; + } + arduino::ZephyrSerial::flush(); +} + +arduino::SerialUSB_ Serial(usb_dev); +#endif diff --git a/cores/arduino/abi.cpp b/cores/arduino/abi.cpp new file mode 100644 index 000000000..872c8d083 --- /dev/null +++ b/cores/arduino/abi.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +namespace std { +void __throw_length_error(const char *__s __attribute__((unused))) { +} + +void __throw_bad_alloc() { +} + +void __throw_bad_function_call() { +} +}; // namespace std + +extern "C" { + +void *__dso_handle = (void *)&__dso_handle; + +void __cxa_pure_virtual(void) { +} + +void __cxa_deleted_virtual(void) { +} + +int __cxa_atexit(void (*func)(void *), void *arg, void *dso_handle) { + (void)func; + (void)arg; + (void)dso_handle; // unused + return 0; +} + +int atexit(void (*func)(void)) { + (void)func; + return 0; +} + +int strcmp(const char *s1, const char *s2) { + while (*s1 && (*s1 == *s2)) { + s1++; + s2++; + } + return *(const unsigned char *)s1 - *(const unsigned char *)s2; +} + +} /* extern "C" */ diff --git a/cores/arduino/apiCommon.cpp b/cores/arduino/apiCommon.cpp index cfb6d919f..36e808d8e 100644 --- a/cores/arduino/apiCommon.cpp +++ b/cores/arduino/apiCommon.cpp @@ -8,19 +8,19 @@ #include "zephyrInternal.h" extern "C" { - int32_t map_i32(int32_t x, int32_t in_min, int32_t in_max, int32_t out_min, int32_t out_max); - uint16_t makeWord_w(uint16_t w); - uint16_t makeWord_hl(byte h, byte l); +int32_t map_i32(int32_t x, int32_t in_min, int32_t in_max, int32_t out_min, int32_t out_max); +uint16_t makeWord_w(uint16_t w); +uint16_t makeWord_hl(byte h, byte l); } -long map(long x, long in_min, long in_max, long out_min, long out_max) -{ - return map_i32(x, in_min, in_max, out_min, out_max); +long map(long x, long in_min, long in_max, long out_min, long out_max) { + return map_i32(x, in_min, in_max, out_min, out_max); } uint16_t makeWord(uint16_t w) { - return makeWord_w(w); + return makeWord_w(w); } + uint16_t makeWord(byte h, byte l) { - return makeWord_hl(h, l); + return makeWord_hl(h, l); } diff --git a/cores/arduino/itoa.cpp b/cores/arduino/itoa.cpp new file mode 100644 index 000000000..193cd6ab9 --- /dev/null +++ b/cores/arduino/itoa.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2024 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +extern char *itoa(int value, char *string, int radix) { + return ltoa(value, string, radix); +} + +extern char *ltoa(long value, char *string, int radix) { + char tmp[33]; + char *tp = tmp; + long i; + unsigned long v; + int sign; + char *sp; + + if (string == NULL) { + return 0; + } + + if (radix > 36 || radix <= 1) { + return 0; + } + + sign = (radix == 10 && value < 0); + if (sign) { + v = -value; + } else { + v = (unsigned long)value; + } + + while (v || tp == tmp) { + i = v % radix; + v = v / radix; + if (i < 10) { + *tp++ = i + '0'; + } else { + *tp++ = i + 'a' - 10; + } + } + + sp = string; + + if (sign) { + *sp++ = '-'; + } + while (tp > tmp) { + *sp++ = *--tp; + } + *sp = 0; + + return string; +} + +extern char *utoa(unsigned int value, char *string, int radix) { + return ultoa(value, string, radix); +} + +extern char *ultoa(unsigned long value, char *string, int radix) { + char tmp[33]; + char *tp = tmp; + long i; + unsigned long v = value; + char *sp; + + if (string == NULL) { + return 0; + } + + if (radix > 36 || radix <= 1) { + return 0; + } + + while (v || tp == tmp) { + i = v % radix; + v = v / radix; + if (i < 10) { + *tp++ = i + '0'; + } else { + *tp++ = i + 'a' - 10; + } + } + + sp = string; + + while (tp > tmp) { + *sp++ = *--tp; + } + *sp = 0; + + return string; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/cores/arduino/main.cpp b/cores/arduino/main.cpp index 97afd420d..bab3b4100 100644 --- a/cores/arduino/main.cpp +++ b/cores/arduino/main.cpp @@ -5,14 +5,101 @@ */ #include "Arduino.h" +#include "zephyr/kernel.h" +#include +#ifdef CONFIG_LLEXT +#include +#endif + +#ifdef CONFIG_MULTITHREADING +void start_static_threads(); +#endif + +// This function will be overwriten by most variants. +void __attribute__((weak)) initVariant(void) { +} + +// This function can be overwriten by one library. +void __attribute__((weak)) __loopHook(void) { +} int main(void) { - setup(); +#if (DT_NODE_HAS_PROP(DT_PATH(zephyr_user), cdc_acm) && \ + (CONFIG_USB_CDC_ACM || CONFIG_USBD_CDC_ACM_CLASS)) + Serial.begin(115200); +#endif - for (;;) { - loop(); + initVariant(); + +#ifdef CONFIG_MULTITHREADING + start_static_threads(); +#endif + + setup(); + + for (;;) { + loop(); +#if 0 //(DT_NODE_HAS_PROP(DT_PATH(zephyr_user), cdc_acm) && CONFIG_USB_CDC_ACM) || + // DT_NODE_HAS_PROP(DT_PATH(zephyr_user), serials) if (arduino::serialEventRun) arduino::serialEventRun(); - } +#endif + __loopHook(); + } + + return 0; +} + +#ifdef CONFIG_LLEXT +LL_EXTENSION_SYMBOL(main); +#endif + +/* These magic symbols are provided by the linker. */ +extern void (*__preinit_array_start[])(void) __attribute__((weak)); +extern void (*__preinit_array_end[])(void) __attribute__((weak)); +extern void (*__init_array_start[])(void) __attribute__((weak)); +extern void (*__init_array_end[])(void) __attribute__((weak)); + +static void __libc_init_array(void) { + size_t count; + size_t i; + + count = __preinit_array_end - __preinit_array_start; + for (i = 0; i < count; i++) { + __preinit_array_start[i](); + } + + count = __init_array_end - __init_array_start; + for (i = 0; i < count; i++) { + __init_array_start[i](); + } +} + +extern "C" __attribute__((section(".entry_point"), used)) void entry_point(struct k_heap *stack, + size_t stack_size) { + (void)stack; + (void)stack_size; // unused + + // copy .data in the right place + // .bss should already be in the right place + // call constructors + extern uintptr_t _sidata; + extern uintptr_t _sdata; + extern uintptr_t _edata; + extern uintptr_t _sbss; + extern uintptr_t _ebss; + extern uintptr_t __heap_start; + extern uintptr_t __heap_end; + extern uintptr_t kheap_llext_heap; + extern uintptr_t kheap_llext_heap_size; + + //__asm volatile ("cpsie i"); + + printk("System Heap end: %p\n", &__heap_end); + printk("System Heap start: %p\n", &__heap_start); + printk("Sketch Heap start: %p, size %p\n", &kheap_llext_heap, &kheap_llext_heap_size); - return 0; + memcpy(&_sdata, &_sidata, (&_edata - &_sdata) * sizeof(uint32_t)); + memset(&_sbss, 0, (&_ebss - &_sbss) * sizeof(uint32_t)); + __libc_init_array(); + main(); } diff --git a/cores/arduino/new b/cores/arduino/new new file mode 100644 index 000000000..b97bc949f --- /dev/null +++ b/cores/arduino/new @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef NEW_H +#define NEW_H + +#include +//#pragma long_calls + +namespace std { + struct nothrow_t {}; + extern const nothrow_t nothrow; + + // These are not actually implemented, to prevent overhead and + // complexity. They are still declared to allow implementing + // them in user code if needed. + typedef void (*new_handler)(); + new_handler set_new_handler(new_handler new_p) noexcept; + new_handler get_new_handler() noexcept; + + // This is normally declared in various headers that we do not have + // available, so just define it here. We could also use ::size_t + // below, but then anyone including can no longer assume + // std::size_t is available. + using size_t = ::size_t; +} // namespace std + +void * operator new(std::size_t size); +void * operator new[](std::size_t size); + +#if __cplusplus >= 201703L +void * operator new(std::size_t count, std::align_val_t al); +void * operator new[](std::size_t count, std::align_val_t al); + +void* operator new(std::size_t count, std::align_val_t al, + const std::nothrow_t& tag) noexcept; +void* operator new[](std::size_t count, std::align_val_t al, + const std::nothrow_t& tag) noexcept; +#endif + +void * operator new(std::size_t size, const std::nothrow_t tag) noexcept; +void * operator new[](std::size_t size, const std::nothrow_t& tag) noexcept; + +void * operator new(std::size_t size, void *place) noexcept; +void * operator new[](std::size_t size, void *place) noexcept; + + +void operator delete(void * ptr) noexcept; +void operator delete[](void * ptr) noexcept; + +#if __cplusplus >= 201402L +void operator delete(void* ptr, std::size_t size) noexcept; +void operator delete[](void * ptr, std::size_t size) noexcept; +#endif // __cplusplus >= 201402L + +void operator delete(void* ptr, const std::nothrow_t& tag) noexcept; +void operator delete[](void* ptr, const std::nothrow_t& tag) noexcept; + +void operator delete(void* ptr, void* place) noexcept; +void operator delete[](void* ptr, void* place) noexcept; + +#endif + diff --git a/cores/arduino/new.cpp b/cores/arduino/new.cpp new file mode 100644 index 000000000..7b2a63a90 --- /dev/null +++ b/cores/arduino/new.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2024 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "new.h" + +// The C++ spec dictates that allocation failure should cause the +// (non-nothrow version of the) operator new to throw an exception. +// Since we expect to have exceptions disabled, it would be more +// appropriate (and probably standards-compliant) to terminate instead. +// Historically failure causes null to be returned, but this define +// allows switching to more robust terminating behaviour (that might +// become the default at some point in the future). Note that any code +// that wants null to be returned can (and should) use the nothrow +// versions of the new statement anyway and is unaffected by this. +// #define NEW_TERMINATES_ON_FAILURE + +namespace std { +// Defined in abi.cpp +void terminate(); + +const nothrow_t nothrow; +} // namespace std + +static void *new_helper(std::size_t size) { + // Even zero-sized allocations should return a unique pointer, but + // malloc does not guarantee this + if (size == 0) { + size = 1; + } + return malloc(size); +} + +void *operator new(std::size_t size) { + void *res = new_helper(size); +#if defined(NEW_TERMINATES_ON_FAILURE) + if (!res) { + std::terminate(); + } +#endif + return res; +} + +void *operator new[](std::size_t size) { + return operator new(size); +} + +#if __cplusplus >= 201703L +void *operator new(std::size_t count, std::align_val_t al) { + (void)al; // unused + return operator new(count); +} + +void *operator new[](std::size_t count, std::align_val_t al) { + (void)al; // unused + return operator new(count); +} + +void *operator new(std::size_t size, std::align_val_t al, const std::nothrow_t tag) noexcept { + (void)al; + (void)tag; // unused +#if defined(NEW_TERMINATES_ON_FAILURE) + // Cannot call throwing operator new as standard suggests, so call + // new_helper directly then + return new_helper(size); +#else + return operator new(size); +#endif +} + +void *operator new[](std::size_t size, std::align_val_t al, const std::nothrow_t &tag) noexcept { + (void)al; + (void)tag; // unused +#if defined(NEW_TERMINATES_ON_FAILURE) + // Cannot call throwing operator new[] as standard suggests, so call + // malloc directly then + return new_helper(size); +#else + return operator new[](size); +#endif +} +#endif + +void *operator new(std::size_t size, const std::nothrow_t tag) noexcept { + (void)tag; // unused +#if defined(NEW_TERMINATES_ON_FAILURE) + // Cannot call throwing operator new as standard suggests, so call + // new_helper directly then + return new_helper(size); +#else + return operator new(size); +#endif +} + +void *operator new[](std::size_t size, const std::nothrow_t &tag) noexcept { + (void)tag; // unused +#if defined(NEW_TERMINATES_ON_FAILURE) + // Cannot call throwing operator new[] as standard suggests, so call + // malloc directly then + return new_helper(size); +#else + return operator new[](size); +#endif +} + +void *operator new(std::size_t size, void *place) noexcept { + // Nothing to do + (void)size; // unused + return place; +} + +void *operator new[](std::size_t size, void *place) noexcept { + return operator new(size, place); +} + +void operator delete(void *ptr) noexcept { + free(ptr); +} + +void operator delete[](void *ptr) noexcept { + operator delete(ptr); +} + +#if __cplusplus >= 201402L +void operator delete(void *ptr, std::size_t size) noexcept { + (void)size; // unused + operator delete(ptr); +} + +void operator delete[](void *ptr, std::size_t size) noexcept { + (void)size; // unused + operator delete[](ptr); +} +#endif // __cplusplus >= 201402L + +void operator delete(void *ptr, const std::nothrow_t &tag) noexcept { + (void)tag; // unused + operator delete(ptr); +} + +void operator delete[](void *ptr, const std::nothrow_t &tag) noexcept { + (void)tag; // unused + operator delete[](ptr); +} + +void operator delete(void *ptr, void *place) noexcept { + (void)ptr; + (void)place; // unused + // Nothing to do +} + +void operator delete[](void *ptr, void *place) noexcept { + (void)ptr; + (void)place; // unused + // Nothing to do +} diff --git a/cores/arduino/new.h b/cores/arduino/new.h new file mode 100644 index 000000000..d52985368 --- /dev/null +++ b/cores/arduino/new.h @@ -0,0 +1,3 @@ +// This file originally used a non-standard name for this Arduino core +// only, so still expose the old new.h name for compatibility. +#include "new" diff --git a/cores/arduino/overloads.h b/cores/arduino/overloads.h new file mode 100644 index 000000000..a96653d7f --- /dev/null +++ b/cores/arduino/overloads.h @@ -0,0 +1,9 @@ +#ifdef CONFIG_DAC + +void analogWrite(enum dacPins pinNumber, int value); + +#endif + +// In c++ mode, we also provide analogReadResolution and analogWriteResolution getters +int analogReadResolution(); +int analogWriteResolution(); diff --git a/cores/arduino/threads.cpp b/cores/arduino/threads.cpp new file mode 100644 index 000000000..cf0d73767 --- /dev/null +++ b/cores/arduino/threads.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Arduino.h" + +#ifdef CONFIG_MULTITHREADING +void start_static_threads() { +#define _FOREACH_STATIC_THREAD(thread_data) \ + STRUCT_SECTION_FOREACH (_static_thread_data, thread_data) + + _FOREACH_STATIC_THREAD(thread_data) { +#ifdef CONFIG_TIMER_READS_ITS_FREQUENCY_AT_RUNTIME + k_timeout_t init_delay = K_MSEC(thread_data->init_delay_ms); +#else + k_timeout_t init_delay = thread_data->init_delay; +#endif + + k_thread_create(thread_data->init_thread, thread_data->init_stack, + thread_data->init_stack_size, thread_data->init_entry, thread_data->init_p1, + thread_data->init_p2, thread_data->init_p3, thread_data->init_prio, + thread_data->init_options, init_delay); + + k_thread_name_set(thread_data->init_thread, thread_data->init_name); + thread_data->init_thread->init_data = thread_data; + } +} +#endif diff --git a/cores/arduino/time_macros.h b/cores/arduino/time_macros.h new file mode 100644 index 000000000..6c9290f6c --- /dev/null +++ b/cores/arduino/time_macros.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#define clockCyclesPerMicrosecond() (1000000 / k_cyc_to_ns_near64(1000)) +#define clockCyclesToMicroseconds(a) (a / clockCyclesPerMicrosecond()) +#define microsecondsToClockCycles(a) (a * clockCyclesPerMicrosecond()) diff --git a/cores/arduino/usb_device_descriptor.c b/cores/arduino/usb_device_descriptor.c new file mode 100644 index 000000000..396379370 --- /dev/null +++ b/cores/arduino/usb_device_descriptor.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2025 Arduino SA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include + +#include + +#ifdef CONFIG_USB_DEVICE_STACK_NEXT + +/* By default, do not register the USB DFU class DFU mode instance. */ +static const char *const blocklist[] = { + "dfu_dfu", + NULL, +}; + +/* doc device instantiation start */ +USBD_DEVICE_DEFINE(usbd, DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)), CONFIG_USB_DEVICE_VID, + CONFIG_USB_DEVICE_PID); +/* doc device instantiation end */ + +/* doc string instantiation start */ +USBD_DESC_LANG_DEFINE(sample_lang); +USBD_DESC_MANUFACTURER_DEFINE(sample_mfr, CONFIG_USB_DEVICE_MANUFACTURER); +USBD_DESC_PRODUCT_DEFINE(sample_product, CONFIG_USB_DEVICE_PRODUCT); +USBD_DESC_SERIAL_NUMBER_DEFINE(sample_sn); +/* doc string instantiation end */ + +USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "FS Configuration"); +USBD_DESC_CONFIG_DEFINE(hs_cfg_desc, "HS Configuration"); + +/* doc configuration instantiation start */ +static const uint8_t attributes = 0; + +/* Full speed configuration */ +USBD_CONFIGURATION_DEFINE(sample_fs_config, attributes, 250, &fs_cfg_desc); + +/* High speed configuration */ +USBD_CONFIGURATION_DEFINE(sample_hs_config, attributes, 250, &hs_cfg_desc); +/* doc configuration instantiation end */ + +/* + * This does not yet provide valuable information, but rather serves as an + * example, and will be improved in the future. + */ +static const struct usb_bos_capability_lpm bos_cap_lpm = { + .bLength = sizeof(struct usb_bos_capability_lpm), + .bDescriptorType = USB_DESC_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_BOS_CAPABILITY_EXTENSION, + .bmAttributes = 0UL, +}; + +USBD_DESC_BOS_DEFINE(sample_usbext, sizeof(bos_cap_lpm), &bos_cap_lpm); + +static void sample_fix_code_triple(struct usbd_context *uds_ctx, const enum usbd_speed speed) { + /* Always use class code information from Interface Descriptors */ + if (IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS) || IS_ENABLED(CONFIG_USBD_CDC_ECM_CLASS) || + IS_ENABLED(CONFIG_USBD_CDC_NCM_CLASS) || IS_ENABLED(CONFIG_USBD_AUDIO2_CLASS)) { + /* + * Class with multiple interfaces have an Interface + * Association Descriptor available, use an appropriate triple + * to indicate it. + */ + usbd_device_set_code_triple(uds_ctx, speed, USB_BCC_MISCELLANEOUS, 0x02, 0x01); + } else { + usbd_device_set_code_triple(uds_ctx, speed, 0, 0, 0); + } +} + +struct usbd_context *usbd_setup_device(usbd_msg_cb_t msg_cb) { + int err; + + /* doc add string descriptor start */ + err = usbd_add_descriptor(&usbd, &sample_lang); + if (err) { + return NULL; + } + + err = usbd_add_descriptor(&usbd, &sample_mfr); + if (err) { + return NULL; + } + + err = usbd_add_descriptor(&usbd, &sample_product); + if (err) { + return NULL; + } + + err = usbd_add_descriptor(&usbd, &sample_sn); + if (err) { + return NULL; + } + /* doc add string descriptor end */ + + if (usbd_caps_speed(&usbd) == USBD_SPEED_HS) { + err = usbd_add_configuration(&usbd, USBD_SPEED_HS, &sample_hs_config); + if (err) { + return NULL; + } + + err = usbd_register_all_classes(&usbd, USBD_SPEED_HS, 1, blocklist); + if (err) { + return NULL; + } + + sample_fix_code_triple(&usbd, USBD_SPEED_HS); + } + + /* doc configuration register start */ + err = usbd_add_configuration(&usbd, USBD_SPEED_FS, &sample_fs_config); + if (err) { + return NULL; + } + /* doc configuration register end */ + + /* doc functions register start */ + err = usbd_register_all_classes(&usbd, USBD_SPEED_FS, 1, blocklist); + if (err) { + return NULL; + } + /* doc functions register end */ + + sample_fix_code_triple(&usbd, USBD_SPEED_FS); + + if (msg_cb != NULL) { + /* doc device init-and-msg start */ + err = usbd_msg_register_cb(&usbd, msg_cb); + if (err) { + return NULL; + } + /* doc device init-and-msg end */ + } + + if (0) { + (void)usbd_device_set_bcd_usb(&usbd, USBD_SPEED_FS, 0x0201); + (void)usbd_device_set_bcd_usb(&usbd, USBD_SPEED_HS, 0x0201); + + err = usbd_add_descriptor(&usbd, &sample_usbext); + if (err) { + return NULL; + } + } + + return &usbd; +} + +struct usbd_context *usbd_init_device(usbd_msg_cb_t msg_cb) { + int err; + + if (usbd_setup_device(msg_cb) == NULL) { + return NULL; + } + + /* doc device init start */ + err = usbd_init(&usbd); + if (err) { + return NULL; + } + /* doc device init end */ + + return &usbd; +} + +#endif diff --git a/cores/arduino/zephyrCommon.cpp b/cores/arduino/zephyrCommon.cpp index 92d99ff02..7a746e0ee 100644 --- a/cores/arduino/zephyrCommon.cpp +++ b/cores/arduino/zephyrCommon.cpp @@ -7,180 +7,200 @@ #include #include "zephyrInternal.h" -static const struct gpio_dt_spec arduino_pins[] = {DT_FOREACH_PROP_ELEM_SEP( +static const struct gpio_dt_spec arduino_pins[] = { + DT_FOREACH_PROP_ELEM_SEP( DT_PATH(zephyr_user), digital_pin_gpios, GPIO_DT_SPEC_GET_BY_IDX, (, ))}; namespace { +#if DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios) > 0 + /* * Calculate GPIO ports/pins number statically from devicetree configuration */ -template constexpr const N sum_of_list(const N sum, const Head &head) -{ - return sum + head; +template constexpr N sum_of_list(const N sum, const Head &head) { + return sum + head; } template -constexpr const N sum_of_list(const N sum, const Head &head, const Tail &...tail) -{ - return sum_of_list(sum + head, tail...); +constexpr N sum_of_list(const N sum, const Head &head, const Tail &...tail) { + return sum_of_list(sum + head, tail...); } -template constexpr const N max_in_list(const N max, const Head &head) -{ - return (max >= head) ? max : head; +template constexpr N max_in_list(const N max, const Head &head) { + return (max >= head) ? max : head; } template -constexpr const N max_in_list(const N max, const Head &head, const Tail &...tail) -{ - return max_in_list((max >= head) ? max : head, tail...); +constexpr N max_in_list(const N max, const Head &head, const Tail &...tail) { + return max_in_list((max >= head) ? max : head, tail...); } template -constexpr const size_t is_first_appearance(const size_t &idx, const size_t &at, const size_t &found, - const Query &query, const Head &head) -{ - return ((found == ((size_t)-1)) && (query == head) && (idx == at)) ? 1 : 0; +constexpr size_t is_first_appearance(const size_t &idx, const size_t &at, const size_t &found, + const Query &query, const Head &head) { + return ((found == ((size_t)-1)) && (query == head) && (idx == at)) ? 1 : 0; } template -constexpr const size_t is_first_appearance(const size_t &idx, const size_t &at, const size_t &found, - const Query &query, const Head &head, - const Tail &...tail) -{ - return ((found == ((size_t)-1)) && (query == head) && (idx == at)) - ? 1 - : is_first_appearance(idx + 1, at, (query == head ? idx : found), query, - tail...); +constexpr size_t is_first_appearance(const size_t &idx, const size_t &at, const size_t &found, + const Query &query, const Head &head, const Tail &...tail) { + return ((found == ((size_t)-1)) && (query == head) && (idx == at)) ? + 1 : + is_first_appearance(idx + 1, at, (query == head ? idx : found), query, tail...); } #define GET_DEVICE_VARGS(n, p, i, _) DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(n, p, i)) #define FIRST_APPEARANCE(n, p, i) \ - is_first_appearance(0, i, ((size_t)-1), DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(n, p, i)), \ - DT_FOREACH_PROP_ELEM_SEP_VARGS(n, p, GET_DEVICE_VARGS, (, ), 0)) -const int port_num = - sum_of_list(0, DT_FOREACH_PROP_ELEM_SEP(DT_PATH(zephyr_user), digital_pin_gpios, + is_first_appearance(0, i, ((size_t)-1), DEVICE_DT_GET(DT_GPIO_CTLR_BY_IDX(n, p, i)), \ + DT_FOREACH_PROP_ELEM_SEP_VARGS(n, p, GET_DEVICE_VARGS, (, ), 0)) +const int port_num = sum_of_list( + 0, DT_FOREACH_PROP_ELEM_SEP(DT_PATH(zephyr_user), digital_pin_gpios, FIRST_APPEARANCE, (, ))); #define GPIO_NGPIOS(n, p, i) DT_PROP(DT_GPIO_CTLR_BY_IDX(n, p, i), ngpios) const int max_ngpios = max_in_list( - 0, DT_FOREACH_PROP_ELEM_SEP(DT_PATH(zephyr_user), digital_pin_gpios, GPIO_NGPIOS, (, ))); + 0, DT_FOREACH_PROP_ELEM_SEP(DT_PATH(zephyr_user), digital_pin_gpios, GPIO_NGPIOS, (, ))); + +#else + +const int port_num = 1; +const int max_ngpios = 0; + +#endif /* * GPIO callback implementation */ struct arduino_callback { - voidFuncPtr handler; - bool enabled; + voidFuncPtr handler; + bool enabled; }; struct gpio_port_callback { - struct gpio_callback callback; - struct arduino_callback handlers[max_ngpios]; - gpio_port_pins_t pins; - const struct device *dev; + struct gpio_callback callback; + struct arduino_callback handlers[max_ngpios]; + gpio_port_pins_t pins; + const struct device *dev; } port_callback[port_num] = {0}; -struct gpio_port_callback *find_gpio_port_callback(const struct device *dev) -{ - for (size_t i = 0; i < ARRAY_SIZE(port_callback); i++) { - if (port_callback[i].dev == dev) { - return &port_callback[i]; - } - if (port_callback[i].dev == nullptr) { - port_callback[i].dev = dev; - return &port_callback[i]; - } - } +struct gpio_port_callback *find_gpio_port_callback(const struct device *dev) { + for (size_t i = 0; i < ARRAY_SIZE(port_callback); i++) { + if (port_callback[i].dev == dev) { + return &port_callback[i]; + } + if (port_callback[i].dev == nullptr) { + port_callback[i].dev = dev; + return &port_callback[i]; + } + } - return nullptr; + return nullptr; } -void setInterruptHandler(pin_size_t pinNumber, voidFuncPtr func) -{ - struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); +void setInterruptHandler(pin_size_t pinNumber, voidFuncPtr func) { + struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); - if (pcb) { - pcb->handlers[arduino_pins[pinNumber].pin].handler = func; - } + if (pcb) { + pcb->handlers[arduino_pins[pinNumber].pin].handler = func; + } } -void handleGpioCallback(const struct device *port, struct gpio_callback *cb, uint32_t pins) -{ - struct gpio_port_callback *pcb = (struct gpio_port_callback *)cb; +void handleGpioCallback(const struct device *port, struct gpio_callback *cb, uint32_t pins) { + (void)port; // unused + struct gpio_port_callback *pcb = (struct gpio_port_callback *)cb; - for (uint32_t i = 0; i < max_ngpios; i++) { - if (pins & BIT(i) && pcb->handlers[i].enabled) { - pcb->handlers[i].handler(); - } - } + for (uint32_t i = 0; i < max_ngpios; i++) { + if (pins & BIT(i) && pcb->handlers[i].enabled) { + pcb->handlers[i].handler(); + } + } } #ifdef CONFIG_PWM -#define PWM_DT_SPEC(n,p,i) PWM_DT_SPEC_GET_BY_IDX(n, i), -#define PWM_PINS(n, p, i) \ - DIGITAL_PIN_GPIOS_FIND_PIN( \ - DT_REG_ADDR(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), p, i)), \ - DT_PHA_BY_IDX(DT_PATH(zephyr_user), p, i, pin)), +#define PWM_DT_SPEC(n, p, i) PWM_DT_SPEC_GET_BY_IDX(n, i), +#define PWM_PINS(n, p, i) \ + DIGITAL_PIN_GPIOS_FIND_PIN(DT_REG_ADDR(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), p, i)), \ + DT_PHA_BY_IDX(DT_PATH(zephyr_user), p, i, pin)), -const struct pwm_dt_spec arduino_pwm[] = - { DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), pwms, PWM_DT_SPEC) }; +const struct pwm_dt_spec arduino_pwm[] = { + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), pwms, PWM_DT_SPEC)}; /* pwm-pins node provides a mapping digital pin numbers to pwm channels */ -const pin_size_t arduino_pwm_pins[] = - { DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), pwm_pin_gpios, PWM_PINS) }; +const pin_size_t arduino_pwm_pins[] = { + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), pwm_pin_gpios, PWM_PINS)}; size_t pwm_pin_index(pin_size_t pinNumber) { - for(size_t i=0; ipin]; - - if (pt->count == 0) { - k_timer_stop(timer); - gpio_pin_set_dt(spec, 0); - } else { - gpio_pin_toggle_dt(spec); - if (!pt->infinity) { - pt->count--; - } - } -} - -void tone(pin_size_t pinNumber, unsigned int frequency, - unsigned long duration) { - const struct gpio_dt_spec *spec = &arduino_pins[pinNumber]; - struct k_timer *timer; - k_timeout_t timeout; - - if (pinNumber >= MAX_TONE_PINS) { - return; - } - - timer = &arduino_pin_timers[pinNumber].timer; - - pinMode(pinNumber, OUTPUT); - k_timer_stop(&arduino_pin_timers[pinNumber].timer); - - if (frequency == 0) { - gpio_pin_set_dt(spec, 0); - return; - } - - timeout = K_NSEC(NSEC_PER_SEC / (TOGGLES_PER_CYCLE * frequency)); - if (timeout.ticks == 0) { - timeout.ticks = 1; - } - - arduino_pin_timers[pinNumber].infinity = (duration == 0); - arduino_pin_timers[pinNumber].count = (uint64_t)duration * frequency * + struct pin_timer *pt = CONTAINER_OF(timer, struct pin_timer, timer); + const struct gpio_dt_spec *spec = &arduino_pins[pt->pin]; + + if (pt->count == 0) { + k_timer_stop(timer); + gpio_pin_set_dt(spec, 0); + } else { + gpio_pin_toggle_dt(spec); + if (!pt->infinity) { + pt->count--; + } + } +} + +void tone(pin_size_t pinNumber, unsigned int frequency, unsigned long duration) { + const struct gpio_dt_spec *spec = &arduino_pins[pinNumber]; + struct k_timer *timer; + k_timeout_t timeout; + + if (pinNumber >= MAX_TONE_PINS) { + return; + } + + timer = &arduino_pin_timers[pinNumber].timer; + + pinMode(pinNumber, OUTPUT); + k_timer_stop(&arduino_pin_timers[pinNumber].timer); + + if (frequency == 0) { + gpio_pin_set_dt(spec, 0); + return; + } + + timeout = K_NSEC(NSEC_PER_SEC / (TOGGLES_PER_CYCLE * frequency)); + if (timeout.ticks == 0) { + timeout.ticks = 1; + } + + arduino_pin_timers[pinNumber].infinity = (duration == 0); + arduino_pin_timers[pinNumber].count = (uint64_t)duration * frequency * (MSEC_PER_SEC / TOGGLES_PER_CYCLE); - arduino_pin_timers[pinNumber].pin = pinNumber; - k_timer_init(timer, tone_expiry_cb, NULL); + arduino_pin_timers[pinNumber].pin = pinNumber; + k_timer_init(timer, tone_expiry_cb, NULL); - gpio_pin_set_dt(spec, 0); - k_timer_start(timer, timeout, timeout); + gpio_pin_set_dt(spec, 0); + k_timer_start(timer, timeout, timeout); } void noTone(pin_size_t pinNumber) { - const struct gpio_dt_spec *spec = &arduino_pins[pinNumber]; + const struct gpio_dt_spec *spec = &arduino_pins[pinNumber]; - k_timer_stop(&arduino_pin_timers[pinNumber].timer); - gpio_pin_set_dt(spec, 0); + k_timer_stop(&arduino_pin_timers[pinNumber].timer); + gpio_pin_set_dt(spec, 0); } void delay(unsigned long ms) { - k_sleep(K_MSEC(ms)); + k_sleep(K_MSEC(ms)); } void delayMicroseconds(unsigned int us) { - k_busy_wait(us); + k_busy_wait(us); } unsigned long micros(void) { - return k_cyc_to_us_floor32(k_cycle_get_32()); +#ifdef CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER + return k_cyc_to_us_floor32(k_cycle_get_64()); +#else + return k_cyc_to_us_floor32(k_cycle_get_32()); +#endif } unsigned long millis(void) { - return k_uptime_get_32(); + return k_uptime_get_32(); +} + +#if defined(CONFIG_DAC) || defined(CONFIG_PWM) +static int _analog_write_resolution = 8; + +void analogWriteResolution(int bits) { + _analog_write_resolution = bits; +} + +int analogWriteResolution() { + return _analog_write_resolution; } +#endif #ifdef CONFIG_PWM -void analogWrite(pin_size_t pinNumber, int value) -{ - size_t idx = pwm_pin_index(pinNumber); +void analogWrite(pin_size_t pinNumber, int value) { + size_t idx = pwm_pin_index(pinNumber); - if (idx >= ARRAY_SIZE(arduino_pwm)) { - return; - } + if (idx >= ARRAY_SIZE(arduino_pwm)) { + return; + } - if (!pwm_is_ready_dt(&arduino_pwm[idx])) { - return; - } + if (!pwm_is_ready_dt(&arduino_pwm[idx])) { + return; + } - if (((uint32_t)value) > arduino_pwm[idx].period) { - value = arduino_pwm[idx].period; - } else if (value < 0) { - value = 0; - } + value = map(value, 0, 1 << _analog_write_resolution, 0, arduino_pwm[idx].period); - /* - * A duty ratio determines by the period value defined in dts - * and the value arguments. So usually the period value sets as 255. - */ - (void)pwm_set_pulse_dt(&arduino_pwm[idx], value); + if (((uint32_t)value) > arduino_pwm[idx].period) { + value = arduino_pwm[idx].period; + } else if (value < 0) { + value = 0; + } + + /* + * A duty ratio determines by the period value defined in dts + * and the value arguments. So usually the period value sets as 255. + */ + (void)pwm_set_pulse_dt(&arduino_pwm[idx], value); } #endif +#ifdef CONFIG_DAC +void analogWrite(enum dacPins dacName, int value) { + if (dacName >= NUM_OF_DACS) { + return; + } + + dac_channel_setup(dac_dev, &dac_ch_cfg[dacName]); + + const int max_dac_value = 1U << dac_ch_cfg[dacName].resolution; + dac_write_value(dac_dev, dac_ch_cfg[dacName].channel_id, + map(value, 0, 1 << _analog_write_resolution, 0, max_dac_value)); +} +#endif + #ifdef CONFIG_ADC -void analogReference(uint8_t mode) -{ - /* - * The Arduino API not clearly defined what means of - * the mode argument of analogReference(). - * Treat the value as equivalent to zephyr's adc_reference. - */ - for (size_t i=0; i(mode); - } -} - -int analogRead(pin_size_t pinNumber) -{ - int err; - int16_t buf; - struct adc_sequence seq = { .buffer = &buf, .buffer_size = sizeof(buf) }; - size_t idx = analog_pin_index(pinNumber); - - if (idx >= ARRAY_SIZE(arduino_adc) ) { - return -EINVAL; - } - - /* - * ADC that is on MCU supported by Zephyr exists - * only 16bit resolution, currently. - */ - if (arduino_adc[idx].resolution > 16) { - return -ENOTSUP; - } - - err = adc_channel_setup(arduino_adc[idx].dev, &arduino_adc[idx].channel_cfg); - if (err < 0) { - return err; - } - - seq.channels = BIT(arduino_adc[idx].channel_id); - seq.resolution = arduino_adc[idx].resolution; - seq.oversampling = arduino_adc[idx].oversampling; - - err = adc_read(arduino_adc[idx].dev, &seq); - if (err < 0) { - return err; - } - - return buf; +void __attribute__((weak)) analogReference(uint8_t mode) { + /* + * The Arduino API not clearly defined what means of + * the mode argument of analogReference(). + * Treat the value as equivalent to zephyr's adc_reference. + */ + for (size_t i = 0; i < ARRAY_SIZE(channel_cfg); i++) { + channel_cfg[i].reference = static_cast(mode); + } +} + +// Note: We can not update the arduino_adc structure as it is read only... +static int read_resolution = 10; + +void analogReadResolution(int bits) { + read_resolution = bits; +} + +int analogReadResolution() { + return read_resolution; +} + +int analogRead(pin_size_t pinNumber) { + int err; + uint16_t buf; + struct adc_sequence seq = {.buffer = &buf, .buffer_size = sizeof(buf)}; + size_t idx = analog_pin_index(pinNumber); + + if (idx >= ARRAY_SIZE(arduino_adc)) { + return -EINVAL; + } + + /* + * ADC that is on MCU supported by Zephyr exists + * only 16bit resolution, currently. + */ + if (arduino_adc[idx].resolution > 16) { + return -ENOTSUP; + } + + err = adc_channel_setup(arduino_adc[idx].dev, &arduino_adc[idx].channel_cfg); + if (err < 0) { + return err; + } + + seq.channels = BIT(arduino_adc[idx].channel_id); + seq.resolution = arduino_adc[idx].resolution; + seq.oversampling = arduino_adc[idx].oversampling; + + err = adc_read(arduino_adc[idx].dev, &seq); + if (err < 0) { + return err; + } + + /* + * If necessary map the return value to the + * number of bits the user has asked for + */ + if (read_resolution == seq.resolution) { + return buf; + } + if (read_resolution < seq.resolution) { + return buf >> (seq.resolution - read_resolution); + } + return buf << (read_resolution - seq.resolution); } #endif -void attachInterrupt(pin_size_t pinNumber, voidFuncPtr callback, PinStatus pinStatus) -{ - struct gpio_port_callback *pcb; - gpio_flags_t intmode = 0; +void attachInterrupt(pin_size_t pinNumber, voidFuncPtr callback, PinStatus pinStatus) { + struct gpio_port_callback *pcb; + gpio_flags_t intmode = 0; - if (!callback) { - return; - } + if (!callback) { + return; + } - if (pinStatus == LOW) { - intmode |= GPIO_INT_LEVEL_LOW; - } else if (pinStatus == HIGH) { - intmode |= GPIO_INT_LEVEL_HIGH; - } else if (pinStatus == CHANGE) { - intmode |= GPIO_INT_EDGE_BOTH; - } else if (pinStatus == FALLING) { - intmode |= GPIO_INT_EDGE_FALLING; - } else if (pinStatus == RISING) { - intmode |= GPIO_INT_EDGE_RISING; - } else { - return; - } + if (pinStatus == LOW) { + intmode |= GPIO_INT_LEVEL_LOW; + } else if (pinStatus == HIGH) { + intmode |= GPIO_INT_LEVEL_HIGH; + } else if (pinStatus == CHANGE) { + intmode |= GPIO_INT_EDGE_BOTH; + } else if (pinStatus == FALLING) { + intmode |= GPIO_INT_EDGE_FALLING; + } else if (pinStatus == RISING) { + intmode |= GPIO_INT_EDGE_RISING; + } else { + return; + } - pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); - __ASSERT(pcb != nullptr, "gpio_port_callback not found"); + pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); + __ASSERT(pcb != nullptr, "gpio_port_callback not found"); - pcb->pins |= BIT(arduino_pins[pinNumber].pin); - setInterruptHandler(pinNumber, callback); - enableInterrupt(pinNumber); + pcb->pins |= BIT(arduino_pins[pinNumber].pin); + setInterruptHandler(pinNumber, callback); + enableInterrupt(pinNumber); - gpio_pin_interrupt_configure(arduino_pins[pinNumber].port, arduino_pins[pinNumber].pin, intmode); - gpio_init_callback(&pcb->callback, handleGpioCallback, pcb->pins); - gpio_add_callback(arduino_pins[pinNumber].port, &pcb->callback); + gpio_pin_interrupt_configure(arduino_pins[pinNumber].port, arduino_pins[pinNumber].pin, + intmode); + gpio_init_callback(&pcb->callback, handleGpioCallback, pcb->pins); + gpio_add_callback(arduino_pins[pinNumber].port, &pcb->callback); } -void detachInterrupt(pin_size_t pinNumber) -{ - setInterruptHandler(pinNumber, nullptr); - disableInterrupt(pinNumber); +void detachInterrupt(pin_size_t pinNumber) { + setInterruptHandler(pinNumber, nullptr); + disableInterrupt(pinNumber); } #ifndef CONFIG_MINIMAL_LIBC_RAND @@ -439,74 +505,76 @@ long random(long max) { #endif unsigned long pulseIn(pin_size_t pinNumber, uint8_t state, unsigned long timeout) { - struct k_timer timer; - int64_t start, end, delta = 0; - const struct gpio_dt_spec *spec = &arduino_pins[pinNumber]; - - if (!gpio_is_ready_dt(spec)) { - return 0; - } - - k_timer_init(&timer, NULL, NULL); - k_timer_start(&timer, K_MSEC(timeout), K_NO_WAIT); - - while(gpio_pin_get_dt(spec) == state && k_timer_status_get(&timer) == 0); - if (k_timer_status_get(&timer) > 0) { - goto cleanup; - } - - while(gpio_pin_get_dt(spec) != state && k_timer_status_get(&timer) == 0); - if (k_timer_status_get(&timer) > 0) { - goto cleanup; - } - - start = k_uptime_ticks(); - while(gpio_pin_get_dt(spec) == state && k_timer_status_get(&timer) == 0); - if (k_timer_status_get(&timer) > 0) { - goto cleanup; - } - end = k_uptime_ticks(); - - delta = k_ticks_to_us_floor64(end - start); + struct k_timer timer; + int64_t start, end, delta = 0; + const struct gpio_dt_spec *spec = &arduino_pins[pinNumber]; + + if (!gpio_is_ready_dt(spec)) { + return 0; + } + + k_timer_init(&timer, NULL, NULL); + k_timer_start(&timer, K_MSEC(timeout), K_NO_WAIT); + + while (gpio_pin_get_dt(spec) == state && k_timer_status_get(&timer) == 0) + ; + if (k_timer_status_get(&timer) > 0) { + goto cleanup; + } + + while (gpio_pin_get_dt(spec) != state && k_timer_status_get(&timer) == 0) + ; + if (k_timer_status_get(&timer) > 0) { + goto cleanup; + } + + start = k_uptime_ticks(); + while (gpio_pin_get_dt(spec) == state && k_timer_status_get(&timer) == 0) + ; + if (k_timer_status_get(&timer) > 0) { + goto cleanup; + } + end = k_uptime_ticks(); + + delta = k_ticks_to_us_floor64(end - start); cleanup: - k_timer_stop(&timer); - return (unsigned long)delta; + k_timer_stop(&timer); + return (unsigned long)delta; } void enableInterrupt(pin_size_t pinNumber) { - struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); + struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); - if (pcb) { - pcb->handlers[arduino_pins[pinNumber].pin].enabled = true; - } + if (pcb) { + pcb->handlers[arduino_pins[pinNumber].pin].enabled = true; + } } void disableInterrupt(pin_size_t pinNumber) { - struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); + struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pinNumber].port); - if (pcb) { - pcb->handlers[arduino_pins[pinNumber].pin].enabled = false; - } + if (pcb) { + pcb->handlers[arduino_pins[pinNumber].pin].enabled = false; + } } void interrupts(void) { - if (interrupts_disabled) { - irq_unlock(irq_key); - interrupts_disabled = false; - } + if (interrupts_disabled) { + irq_unlock(irq_key); + interrupts_disabled = false; + } } void noInterrupts(void) { - if (!interrupts_disabled) { - irq_key = irq_lock(); - interrupts_disabled = true; - } + if (!interrupts_disabled) { + irq_key = irq_lock(); + interrupts_disabled = true; + } } int digitalPinToInterrupt(pin_size_t pin) { - struct gpio_port_callback *pcb = - find_gpio_port_callback(arduino_pins[pin].port); + struct gpio_port_callback *pcb = find_gpio_port_callback(arduino_pins[pin].port); - return (pcb) ? pin : -1; + return (pcb) ? pin : -1; } diff --git a/cores/arduino/zephyrPrint.cpp b/cores/arduino/zephyrPrint.cpp index 2e07c2329..d192328e2 100644 --- a/cores/arduino/zephyrPrint.cpp +++ b/cores/arduino/zephyrPrint.cpp @@ -9,18 +9,14 @@ #include #include -namespace arduino -{ -namespace zephyr -{ +namespace arduino { +namespace zephyr { -int cbprintf_callback(int c, void *ctx) -{ +int cbprintf_callback(int c, void *ctx) { return reinterpret_cast(ctx)->write((unsigned char)c); } -size_t wrap_cbprintf(void *ctx, const char *format, ...) -{ +size_t wrap_cbprintf(void *ctx, const char *format, ...) { va_list ap; int rc; @@ -31,8 +27,7 @@ size_t wrap_cbprintf(void *ctx, const char *format, ...) return static_cast(rc > 0 ? rc : 0); } -size_t print_number_base_any(void *ctx, unsigned long long ull, int base) -{ +size_t print_number_base_any(void *ctx, unsigned long long ull, int base) { arduino::Print &print = *reinterpret_cast(ctx); char string[sizeof(unsigned long long) * 8] = {0}; size_t digit = 0; @@ -47,7 +42,7 @@ size_t print_number_base_any(void *ctx, unsigned long long ull, int base) if (value < 10) { string[sizeof(string) - digit] = '0' + value; } else { - string[sizeof(string) - digit] = 'A' + (value- 10); + string[sizeof(string) - digit] = 'A' + (value - 10); } digit++; @@ -57,8 +52,7 @@ size_t print_number_base_any(void *ctx, unsigned long long ull, int base) return print.write(string + (sizeof(string) - digit), digit + 1); } -size_t print_number_base_pow2(void *ctx, unsigned long long ull, unsigned bits) -{ +size_t print_number_base_pow2(void *ctx, unsigned long long ull, unsigned bits) { arduino::Print &print = *reinterpret_cast(ctx); const unsigned long long mask = (1 << bits) - 1; int digit = (((sizeof(unsigned long long) * 8) + bits) / bits); @@ -75,7 +69,7 @@ size_t print_number_base_pow2(void *ctx, unsigned long long ull, unsigned bits) if (value < 10) { print.write('0' + value); } else { - print.write('A' + (value- 10)); + print.write('A' + (value - 10)); } output_count++; } @@ -92,11 +86,10 @@ size_t print_number_base_pow2(void *ctx, unsigned long long ull, unsigned bits) * This is the default implementation. * It will be overridden by subclassese. */ -size_t arduino::Print::write(const uint8_t *buffer, size_t size) -{ - size_t i; - for (i=0; i #include -namespace arduino -{ -namespace zephyr -{ +namespace arduino { +namespace zephyr { int cbprintf_callback(int c, void *ctx); size_t wrap_cbprintf(void *ctx, const char *format, ...); size_t print_number_base_any(void *ctx, unsigned long long ull, int base); size_t print_number_base_pow2(void *ctx, unsigned long long ull, unsigned bits); -template size_t print_number(void *ctx, Number n, const int base, const char *decfmt) -{ +template +size_t print_number(void *ctx, Number n, const int base, const char *decfmt) { if (base == 0) { return reinterpret_cast(ctx)->write((char)n); } else if (base == 2) { @@ -46,63 +44,51 @@ template size_t print_number(void *ctx, Number n, const int base, } // namespace arduino -inline size_t arduino::Print::print(const __FlashStringHelper *fsh) -{ +inline size_t arduino::Print::print(const __FlashStringHelper *fsh) { return write(reinterpret_cast(fsh)); } -inline size_t arduino::Print::print(const String &s) -{ +inline size_t arduino::Print::print(const String &s) { return write(s.c_str(), s.length()); } -inline size_t arduino::Print::print(const char str[]) -{ +inline size_t arduino::Print::print(const char str[]) { return write(str); } -inline size_t arduino::Print::print(char c) -{ +inline size_t arduino::Print::print(char c) { return write(c); } -inline size_t arduino::Print::print(unsigned char n, int base) -{ +inline size_t arduino::Print::print(unsigned char n, int base) { return arduino::zephyr::print_number(this, n, base, "%hhu"); } -inline size_t arduino::Print::print(int n, int base) -{ +inline size_t arduino::Print::print(int n, int base) { return arduino::zephyr::print_number(this, n, base, "%d"); } -inline size_t arduino::Print::print(unsigned int n, int base) -{ +inline size_t arduino::Print::print(unsigned int n, int base) { return arduino::zephyr::print_number(this, n, base, "%u"); } -inline size_t arduino::Print::print(long n, int base) -{ +inline size_t arduino::Print::print(long n, int base) { return arduino::zephyr::print_number(this, n, base, "%ld"); } -inline size_t arduino::Print::print(unsigned long n, int base) -{ +inline size_t arduino::Print::print(unsigned long n, int base) { return arduino::zephyr::print_number(this, n, base, "%lu"); } -inline size_t arduino::Print::print(long long n, int base) -{ +inline size_t arduino::Print::print(long long n, int base) { return arduino::zephyr::print_number(this, n, base, "%lld"); } -inline size_t arduino::Print::print(unsigned long long n, int base) -{ +inline size_t arduino::Print::print(unsigned long long n, int base) { return arduino::zephyr::print_number(this, n, base, "%llu"); } -inline size_t arduino::Print::print(double n, int perception) -{ +inline size_t arduino::Print::print(double n, int perception) { if (perception < 10) { const char ch_perception = static_cast('0' + perception); const char format[] = {'%', '.', ch_perception, 'f', '\0'}; @@ -114,77 +100,62 @@ inline size_t arduino::Print::print(double n, int perception) } } -inline size_t arduino::Print::print(const Printable &printable) -{ +inline size_t arduino::Print::print(const Printable &printable) { return printable.printTo(*this); } -inline size_t arduino::Print::println(const __FlashStringHelper *fsh) -{ +inline size_t arduino::Print::println(const __FlashStringHelper *fsh) { return print(fsh) + println(); } -inline size_t arduino::Print::println(const String &s) -{ +inline size_t arduino::Print::println(const String &s) { return print(s) + println(); } -inline size_t arduino::Print::println(const char str[]) -{ +inline size_t arduino::Print::println(const char str[]) { return print(str) + println(); } -inline size_t arduino::Print::println(char c) -{ +inline size_t arduino::Print::println(char c) { return print(c) + println(); } -inline size_t arduino::Print::println(unsigned char uc, int base) -{ +inline size_t arduino::Print::println(unsigned char uc, int base) { return print(uc, base) + println(); } -inline size_t arduino::Print::println(int i, int base) -{ +inline size_t arduino::Print::println(int i, int base) { return print(i, base) + println(); } -inline size_t arduino::Print::println(unsigned int ui, int base) -{ +inline size_t arduino::Print::println(unsigned int ui, int base) { return print(ui, base) + println(); } -inline size_t arduino::Print::println(long l, int base) -{ +inline size_t arduino::Print::println(long l, int base) { return print(l, base) + println(); } -inline size_t arduino::Print::println(unsigned long ul, int base) -{ +inline size_t arduino::Print::println(unsigned long ul, int base) { return print(ul, base) + println(); } -inline size_t arduino::Print::println(long long ll, int base) -{ +inline size_t arduino::Print::println(long long ll, int base) { return print(ll, base) + println(); } -inline size_t arduino::Print::println(unsigned long long ull, int base) -{ +inline size_t arduino::Print::println(unsigned long long ull, int base) { return print(ull, base) + println(); } -inline size_t arduino::Print::println(double d, int perception) -{ +inline size_t arduino::Print::println(double d, int perception) { return print(d, perception) + println(); } -inline size_t arduino::Print::println(const Printable &printable) -{ +inline size_t arduino::Print::println(const Printable &printable) { return print(printable) + println(); } -inline size_t arduino::Print::println(void) -{ +inline size_t arduino::Print::println(void) { return write("\r\n", 2); } diff --git a/cores/arduino/zephyrSerial.cpp b/cores/arduino/zephyrSerial.cpp index b3047ac1c..f493047fa 100644 --- a/cores/arduino/zephyrSerial.cpp +++ b/cores/arduino/zephyrSerial.cpp @@ -9,12 +9,11 @@ #include #include +#include -namespace -{ +namespace { -enum uart_config_parity conf_parity(uint16_t conf) -{ +enum uart_config_parity conf_parity(uint16_t conf) { switch (conf & SERIAL_PARITY_MASK) { case SERIAL_PARITY_EVEN: return UART_CFG_PARITY_EVEN; @@ -25,8 +24,7 @@ enum uart_config_parity conf_parity(uint16_t conf) } } -enum uart_config_stop_bits conf_stop_bits(uint16_t conf) -{ +enum uart_config_stop_bits conf_stop_bits(uint16_t conf) { switch (conf & SERIAL_STOP_BIT_MASK) { case SERIAL_STOP_BIT_1_5: return UART_CFG_STOP_BITS_1_5; @@ -37,8 +35,7 @@ enum uart_config_stop_bits conf_stop_bits(uint16_t conf) } } -enum uart_config_data_bits conf_data_bits(uint16_t conf) -{ +enum uart_config_data_bits conf_data_bits(uint16_t conf) { switch (conf & SERIAL_DATA_MASK) { case SERIAL_DATA_5: return UART_CFG_DATA_BITS_5; @@ -53,8 +50,7 @@ enum uart_config_data_bits conf_data_bits(uint16_t conf) } // anonymous namespace -void arduino::ZephyrSerial::begin(unsigned long baud, uint16_t conf) -{ +void arduino::ZephyrSerial::begin(unsigned long baud, uint16_t conf) { struct uart_config config = { .baudrate = static_cast(baud), .parity = conf_parity(conf), @@ -65,11 +61,14 @@ void arduino::ZephyrSerial::begin(unsigned long baud, uint16_t conf) uart_configure(uart, &config); uart_irq_callback_user_data_set(uart, arduino::ZephyrSerial::IrqDispatch, this); + k_sem_take(&rx.sem, K_FOREVER); + ring_buf_reset(&rx.ringbuf); + k_sem_give(&rx.sem); + uart_irq_rx_enable(uart); } -void arduino::ZephyrSerial::IrqHandler() -{ +void arduino::ZephyrSerial::IrqHandler() { uint8_t buf[8]; int length; int ret = 0; @@ -78,10 +77,6 @@ void arduino::ZephyrSerial::IrqHandler() return; } - if (ring_buf_size_get(&tx.ringbuf) == 0) { - uart_irq_tx_disable(uart); - } - k_sem_take(&rx.sem, K_NO_WAIT); while (uart_irq_rx_ready(uart) && ((length = uart_fifo_read(uart, buf, sizeof(buf))) > 0)) { length = min(sizeof(buf), static_cast(length)); @@ -94,6 +89,11 @@ void arduino::ZephyrSerial::IrqHandler() k_sem_give(&rx.sem); k_sem_take(&tx.sem, K_NO_WAIT); + + if (ring_buf_size_get(&tx.ringbuf) == 0) { + uart_irq_tx_disable(uart); + } + while (uart_irq_tx_ready(uart) && ((length = ring_buf_size_get(&tx.ringbuf)) > 0)) { length = min(sizeof(buf), static_cast(length)); ring_buf_peek(&tx.ringbuf, &buf[0], length); @@ -108,13 +108,12 @@ void arduino::ZephyrSerial::IrqHandler() k_sem_give(&tx.sem); } -void arduino::ZephyrSerial::IrqDispatch(const struct device *dev, void *data) -{ +void arduino::ZephyrSerial::IrqDispatch(const struct device *dev, void *data) { + (void)dev; // unused reinterpret_cast(data)->IrqHandler(); } -int arduino::ZephyrSerial::available() -{ +int arduino::ZephyrSerial::available() { int ret; k_sem_take(&rx.sem, K_FOREVER); @@ -124,47 +123,83 @@ int arduino::ZephyrSerial::available() return ret; } -int arduino::ZephyrSerial::peek() -{ +int arduino::ZephyrSerial::availableForWrite() { + int ret; + + k_sem_take(&tx.sem, K_FOREVER); + ret = ring_buf_space_get(&tx.ringbuf); + k_sem_give(&tx.sem); + + return ret; +} + +int arduino::ZephyrSerial::peek() { uint8_t data; k_sem_take(&rx.sem, K_FOREVER); - ring_buf_peek(&rx.ringbuf, &data, 1); + uint32_t cb_ret = ring_buf_peek(&rx.ringbuf, &data, 1); k_sem_give(&rx.sem); - return data; + return cb_ret ? data : -1; } -int arduino::ZephyrSerial::read() -{ +int arduino::ZephyrSerial::read() { uint8_t data; k_sem_take(&rx.sem, K_FOREVER); - ring_buf_get(&rx.ringbuf, &data, 1); + uint32_t cb_ret = ring_buf_get(&rx.ringbuf, &data, 1); k_sem_give(&rx.sem); - return data; + return cb_ret ? data : -1; } -size_t arduino::ZephyrSerial::write(const uint8_t *buffer, size_t size) -{ - int ret; - - k_sem_take(&tx.sem, K_FOREVER); - ret = ring_buf_put(&tx.ringbuf, buffer, size); - k_sem_give(&tx.sem); +size_t arduino::ZephyrSerial::write(const uint8_t *buffer, size_t size) { + size_t idx = 0; - if (ret < 0) { - return 0; + while (1) { + k_sem_take(&tx.sem, K_FOREVER); + auto ret = ring_buf_put(&tx.ringbuf, &buffer[idx], size - idx); + k_sem_give(&tx.sem); + if (ret < 0) { + return 0; + } + idx += ret; + if (ret == 0) { + uart_irq_tx_enable(uart); + yield(); + } + if (idx == size) { + break; + } } uart_irq_tx_enable(uart); - return ret; + return size; } +void arduino::ZephyrSerial::flush() { + while (ring_buf_size_get(&tx.ringbuf) > 0) { + k_yield(); + } + while (uart_irq_tx_complete(uart) == 0) { + k_yield(); + } +} + +#if (DT_NODE_HAS_PROP(DT_PATH(zephyr_user), cdc_acm)) +#define FIRST_UART_INDEX 1 +#else +#define FIRST_UART_INDEX 0 +#endif + #if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), serials) -arduino::ZephyrSerial Serial(DEVICE_DT_GET(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), serials, 0))); +#if !(DT_NODE_HAS_PROP(DT_PATH(zephyr_user), cdc_acm) && \ + (CONFIG_USB_CDC_ACM || CONFIG_USBD_CDC_ACM_CLASS)) +// If CDC USB, use that object as Serial (and SerialUSB) +arduino::ZephyrSerial + Serial(DEVICE_DT_GET(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), serials, FIRST_UART_INDEX))); +#endif #if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1) #define ARDUINO_SERIAL_DEFINED_0 1 @@ -175,13 +210,17 @@ arduino::ZephyrSerial Serial(DEVICE_DT_GET(DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user COND_CODE_1(ARDUINO_SERIAL_DEFINED_##i, (DECL_SERIAL_0(n, p, i)), (DECL_SERIAL_N(n, p, i))) #define CALL_EVENT_0(n, p, i) -#define CALL_EVENT_N(n, p, i) if (_CONCAT(Serial, i).available()) _CONCAT(_CONCAT(serial, i), Event)(); -#define CALL_SERIALEVENT_N(n, p, i) \ +#define CALL_EVENT_N(n, p, i) \ + if (_CONCAT(Serial, i).available()) \ + _CONCAT(_CONCAT(serial, i), Event)(); +#define CALL_SERIALEVENT_N(n, p, i) \ COND_CODE_1(ARDUINO_SERIAL_DEFINED_##i, (CALL_EVENT_0(n, p, i)), (CALL_EVENT_N(n, p, i))); #define DECL_EVENT_0(n, p, i) -#define DECL_EVENT_N(n, p, i) __attribute__((weak)) void serial##i##Event() { } -#define DECLARE_SERIALEVENT_N(n, p, i) \ +#define DECL_EVENT_N(n, p, i) \ + __attribute__((weak)) void serial##i##Event() { \ + } +#define DECLARE_SERIALEVENT_N(n, p, i) \ COND_CODE_1(ARDUINO_SERIAL_DEFINED_##i, (DECL_EVENT_0(n, p, i)), (DECL_EVENT_N(n, p, i))); DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, DECLARE_SERIAL_N) @@ -193,15 +232,16 @@ arduino::ZephyrSerial Serial(DEVICE_DT_GET(DT_NODELABEL(arduino_serial))); arduino::ZephyrSerialStub Serial; #endif - -__attribute__((weak)) void serialEvent() { } +__attribute__((weak)) void serialEvent() { +} #if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1) DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, DECLARE_SERIALEVENT_N) #endif -void arduino::serialEventRun(void) -{ - if (Serial.available()) serialEvent(); +void arduino::serialEventRun(void) { + if (Serial.available()) { + serialEvent(); + } #if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1) DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), serials, CALL_SERIALEVENT_N) #endif diff --git a/cores/arduino/zephyrSerial.h b/cores/arduino/zephyrSerial.h index b62e54f64..5e1d15fe7 100644 --- a/cores/arduino/zephyrSerial.h +++ b/cores/arduino/zephyrSerial.h @@ -7,65 +7,94 @@ #pragma once #include - -#include +#include #include namespace arduino { -class ZephyrSerialStub : public HardwareSerial -{ +class ZephyrSerialStub : public HardwareSerial { public: - void begin(unsigned long baudRate) { } - void begin(unsigned long baudrate, uint16_t config) { } - void end() { } - int available() { return 0; } - int peek() { return 0; } - int read() { return 0; } - void flush() { } - size_t write(const uint8_t data) - { + void begin(__attribute__((unused)) unsigned long baudRate) { + } + + void begin(__attribute__((unused)) unsigned long baudrate, + __attribute__((unused)) uint16_t config) { + } + + void end() { + } + + int available() { + return 0; + } + + int peek() { + return 0; + } + + int read() { + return 0; + } + + void flush() { + } + + size_t write(const uint8_t data) { printk("%c", static_cast(data)); return 1; } - operator bool() { return true; } + operator bool() { + return true; + } }; -class ZephyrSerial : public HardwareSerial -{ +class ZephyrSerial : public HardwareSerial { public: - template - class ZephyrSerialBuffer - { - friend arduino::ZephyrSerial; + template class ZephyrSerialBuffer { + friend arduino::ZephyrSerial; struct ring_buf ringbuf; uint8_t buffer[SZ]; struct k_sem sem; - ZephyrSerialBuffer() - { + ZephyrSerialBuffer() { k_sem_init(&sem, 1, 1); ring_buf_init(&ringbuf, sizeof(buffer), buffer); } }; - ZephyrSerial(const struct device *dev) : uart(dev) { } + ZephyrSerial(const struct device *dev) : uart(dev) { + } + void begin(unsigned long baudrate, uint16_t config); - void begin(unsigned long baudrate) { begin(baudrate, SERIAL_8N1); } - void flush() { } - void end() { } + + void begin(unsigned long baudrate) { + begin(baudrate, SERIAL_8N1); + } + + void flush(); + + void end() { + } + size_t write(const uint8_t *buffer, size_t size); - size_t write(const uint8_t data) { return write(&data, 1); } + + size_t write(const uint8_t data) { + return write(&data, 1); + } + + using Print::write; // pull in write(str) and write(buf, size) from Print int available(); + int availableForWrite(); int peek(); int read(); - operator bool() - { + operator bool() { return true; } + friend class SerialUSB_; + protected: void IrqHandler(); static void IrqDispatch(const struct device *dev, void *data); @@ -75,14 +104,17 @@ class ZephyrSerial : public HardwareSerial ZephyrSerialBuffer rx; }; - -} // namespace arduino +} // namespace arduino #if DT_NODE_HAS_PROP(DT_PATH(zephyr_user), serials) +#if !(DT_NODE_HAS_PROP(DT_PATH(zephyr_user), cdc_acm) && \ + (CONFIG_USB_CDC_ACM || CONFIG_USBD_CDC_ACM_CLASS)) +// If CDC USB, use that object as Serial (and SerialUSB) extern arduino::ZephyrSerial Serial; +#endif #if (DT_PROP_LEN(DT_PATH(zephyr_user), serials) > 1) -#define SERIAL_DEFINED_0 1 -#define EXTERN_SERIAL_N(i) extern arduino::ZephyrSerial Serial##i; +#define SERIAL_DEFINED_0 1 +#define EXTERN_SERIAL_N(i) extern arduino::ZephyrSerial Serial##i; #define DECLARE_EXTERN_SERIAL_N(n, p, i) COND_CODE_1(SERIAL_DEFINED_##i, (), (EXTERN_SERIAL_N(i))) /* Declare Serial1, Serial2, ... */