diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h index 005563aec..51d621dc6 100644 --- a/cores/arduino/Arduino.h +++ b/cores/arduino/Arduino.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #if DT_PROP_LEN(DT_PATH(zephyr_user), digital_pin_gpios) > 0 @@ -103,6 +104,26 @@ 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 { +#if DT_PROP_LEN_OR(DT_PATH(zephyr_user), dac_channels, 0) > 0 + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), dac_channels, DAC_ENUMS) +#endif + NUM_OF_DACS +}; + #endif void interrupts(void); @@ -110,6 +131,15 @@ 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) + +#if defined(CONFIG_PWM) || defined(CONFIG_DAC) +void analogWriteResolution(int bits); +#endif + #include #if !defined(LED_BUILTIN) && defined(ZARD_LED_BUILTIN) @@ -118,4 +148,12 @@ int digitalPinToInterrupt(pin_size_t pin); #ifdef __cplusplus #include +#include +#include +#include +#include + +// Allow namespace-less operations if Arduino.h is included +using namespace arduino; + #endif // __cplusplus diff --git a/cores/arduino/overloads.h b/cores/arduino/overloads.h new file mode 100644 index 000000000..d659836ab --- /dev/null +++ b/cores/arduino/overloads.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) Arduino s.r.l. and/or its affiliated companies + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#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/time_macros.h b/cores/arduino/time_macros.h new file mode 100644 index 000000000..4be801b5e --- /dev/null +++ b/cores/arduino/time_macros.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) Arduino s.r.l. and/or its affiliated companies + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#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/zephyrCommon.cpp b/cores/arduino/zephyrCommon.cpp index dd670fe54..ed2bad52b 100644 --- a/cores/arduino/zephyrCommon.cpp +++ b/cores/arduino/zephyrCommon.cpp @@ -154,14 +154,14 @@ size_t pwm_pin_index(pin_size_t pinNumber) { DT_PHA_BY_IDX(DT_PATH(zephyr_user), p, i, pin)), #define ADC_CH_CFG(n, p, i) arduino_adc[i].channel_cfg, -const struct adc_dt_spec arduino_adc[] = { +static const struct adc_dt_spec arduino_adc[] = { DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, ADC_DT_SPEC)}; /* io-channel-pins node provides a mapping digital pin numbers to adc channels */ const pin_size_t arduino_analog_pins[] = { DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), adc_pin_gpios, ADC_PINS)}; -struct adc_channel_cfg channel_cfg[ARRAY_SIZE(arduino_analog_pins)] = { +struct adc_channel_cfg channel_cfg[] = { DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, ADC_CH_CFG)}; size_t analog_pin_index(pin_size_t pinNumber) { @@ -175,6 +175,32 @@ size_t analog_pin_index(pin_size_t pinNumber) { #endif // CONFIG_ADC +#ifdef CONFIG_DAC + +#if (DT_NODE_HAS_PROP(DT_PATH(zephyr_user), dac)) + +#define DAC_NODE DT_PHANDLE(DT_PATH(zephyr_user), dac) +#define DAC_RESOLUTION DT_PROP(DT_PATH(zephyr_user), dac_resolution) +static const struct device *const dac_dev = DEVICE_DT_GET(DAC_NODE); + +#define DAC_CHANNEL_DEFINE(n, p, i) \ + { \ + .channel_id = DT_PROP_BY_IDX(n, p, i), \ + .resolution = DAC_RESOLUTION, \ + .buffered = true, \ + }, + +#if DT_PROP_LEN_OR(DT_PATH(zephyr_user), dac_channels, 0) > 0 +static const struct dac_channel_cfg dac_ch_cfg[] = { + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), dac_channels, DAC_CHANNEL_DEFINE)}; + +static bool dac_channel_initialized[NUM_OF_DACS]; +#endif + +#endif + +#endif // CONFIG_DAC + static unsigned int irq_key; static bool interrupts_disabled = false; } // namespace @@ -354,9 +380,27 @@ unsigned long millis(void) { 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 +static uint32_t map64(uint32_t x, uint32_t in_min, uint32_t in_max, uint32_t out_min, + uint32_t out_max) { + return ((uint64_t)(x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min); +} + void analogWrite(pin_size_t pinNumber, int value) { + const int maxInput = BIT(_analog_write_resolution) - 1U; size_t idx = pwm_pin_index(pinNumber); if (idx >= ARRAY_SIZE(arduino_pwm)) { @@ -367,24 +411,57 @@ void analogWrite(pin_size_t pinNumber, int value) { return; } - if (((uint32_t)value) > arduino_pwm[idx].period) { - value = arduino_pwm[idx].period; - } else if (value < 0) { - value = 0; - } + value = CLAMP(value, 0, maxInput); + + const uint32_t pulse = map64(value, 0, maxInput, 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); + (void)pwm_set_pulse_dt(&arduino_pwm[idx], pulse); } #endif +#ifdef CONFIG_DAC +void analogWrite(enum dacPins dacName, int value) { +#if DT_PROP_LEN_OR(DT_PATH(zephyr_user), dac_channels, 0) > 0 + const int maxInput = BIT(_analog_write_resolution) - 1U; + int ret = 0; + + if (dacName >= NUM_OF_DACS) { + return; + } + + if (!dac_channel_initialized[dacName]) { + if (!device_is_ready(dac_dev)) { + return; + } + + ret = dac_channel_setup(dac_dev, &dac_ch_cfg[dacName]); + if (ret != 0) { + return; + } + dac_channel_initialized[dacName] = true; + } + + value = CLAMP(value, 0, maxInput); + + const int max_dac_value = BIT(dac_ch_cfg[dacName].resolution) - 1; + const uint32_t output = map(value, 0, maxInput, 0, max_dac_value); + + (void)dac_write_value(dac_dev, dac_ch_cfg[dacName].channel_id, output); +#else + ARG_UNUSED(dacName); + ARG_UNUSED(value); +#endif +} +#endif + #ifdef CONFIG_ADC -void analogReference(uint8_t mode) { +void __attribute__((weak)) analogReference(uint8_t mode) { /* * The Arduino API not clearly defined what means of * the mode argument of analogReference(). @@ -395,9 +472,20 @@ void analogReference(uint8_t 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; - int16_t buf; + uint16_t buf; struct adc_sequence seq = {.buffer = &buf, .buffer_size = sizeof(buf)}; size_t idx = analog_pin_index(pinNumber); @@ -427,7 +515,17 @@ int analogRead(pin_size_t pinNumber) { return err; } - return buf; + /* + * 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