-
Notifications
You must be signed in to change notification settings - Fork 68
Improve analogRead() and add DAC support #175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
Changes from all commits
1fafd6c
f402b2e
5bd7455
30f567a
dc63ec6
698fb2e
ce9649a
88525eb
bd1fd92
bebe022
b1ceda7
a28be06
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -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(); | ||||||||||||||||||||||||
|
Comment on lines
+13
to
+15
|
||||||||||||||||||||||||
| // In c++ mode, we also provide analogReadResolution and analogWriteResolution getters | |
| int analogReadResolution(); | |
| int analogWriteResolution(); | |
| // In c++ mode, we also provide analogReadResolution and analogWriteResolution getters | |
| #ifdef CONFIG_ADC | |
| int analogReadResolution(); | |
| #endif | |
| #if defined(CONFIG_PWM) || defined(CONFIG_DAC) | |
| int analogWriteResolution(); | |
| #endif |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,13 @@ | ||||||||||||||
| /* | ||||||||||||||
| * Copyright (c) Arduino s.r.l. and/or its affiliated companies | ||||||||||||||
| * | ||||||||||||||
| * SPDX-License-Identifier: Apache-2.0 | ||||||||||||||
| */ | ||||||||||||||
|
|
||||||||||||||
| #pragma once | ||||||||||||||
|
|
||||||||||||||
| #include <zephyr/sys/time_units.h> | ||||||||||||||
|
|
||||||||||||||
| #define clockCyclesPerMicrosecond() (1000000 / k_cyc_to_ns_near64(1000)) | ||||||||||||||
| #define clockCyclesToMicroseconds(a) (a / clockCyclesPerMicrosecond()) | ||||||||||||||
| #define microsecondsToClockCycles(a) (a * clockCyclesPerMicrosecond()) | ||||||||||||||
|
Comment on lines
+11
to
+13
|
||||||||||||||
| #define clockCyclesPerMicrosecond() (1000000 / k_cyc_to_ns_near64(1000)) | |
| #define clockCyclesToMicroseconds(a) (a / clockCyclesPerMicrosecond()) | |
| #define microsecondsToClockCycles(a) (a * clockCyclesPerMicrosecond()) | |
| #define clockCyclesPerMicrosecond() ((1000000ULL + k_cyc_to_ns_near64(1000) - 1) / k_cyc_to_ns_near64(1000)) | |
| #define clockCyclesToMicroseconds(a) ((a) / clockCyclesPerMicrosecond()) | |
| #define microsecondsToClockCycles(a) ((a) * clockCyclesPerMicrosecond()) |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _analog_write_resolution = bits; | |
| _analog_write_resolution = CLAMP(bits, 1, 31); |
Copilot
AI
Apr 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding analogWrite(enum dacPins, int) introduces an overload that can make existing calls like analogWrite(3, value) ambiguous on C++ (an int can convert to both pin_size_t and an unscoped enum). Consider avoiding the overload (e.g., use pin_size_t for DAC pins too, or provide a differently named API / a scoped enum class with explicit conversion).
Copilot
AI
Apr 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
analogReference() updates the separate channel_cfg[] array, but analogRead() still calls adc_channel_setup() with &arduino_adc[idx].channel_cfg. That means changes made by analogReference() are never applied to actual reads. Use channel_cfg[idx] in adc_channel_setup() (and ensure the indexing matches), or update arduino_adc[idx].channel_cfg instead of maintaining a separate array.
Copilot
AI
Apr 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
analogReadResolution(int bits) stores bits directly, but analogRead() later shifts by (seq.resolution - read_resolution) / (read_resolution - seq.resolution). If bits is negative or very large, this can trigger undefined shifts and/or signed overflow in the return expression. Validate/clamp bits to a sensible range (e.g., 1..16 or 1..31) and do the scaling using an unsigned wider type before converting to int.
Copilot
AI
Apr 17, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scaling logic in analogRead() does shifts based on user-controlled read_resolution. Without clamping, buf >> (seq.resolution - read_resolution) / buf << (read_resolution - seq.resolution) can shift by a negative or excessively large amount, and the left-shift is done on an int after promotion which can overflow (UB). Clamp read_resolution to a safe range and perform the shift on a wider unsigned type before returning.
| * 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); | |
| * number of bits the user has asked for. | |
| * | |
| * Clamp the user-controlled resolution to a range that is safe for | |
| * shifting a 32-bit temporary and that still fits in the positive | |
| * range of the int return type. | |
| */ | |
| int effective_read_resolution = read_resolution; | |
| if (effective_read_resolution < 0) { | |
| effective_read_resolution = 0; | |
| } else if (effective_read_resolution > 31) { | |
| effective_read_resolution = 31; | |
| } | |
| if (effective_read_resolution == seq.resolution) { | |
| return buf; | |
| } | |
| if (effective_read_resolution < seq.resolution) { | |
| return static_cast<int>(static_cast<uint32_t>(buf) >> | |
| (seq.resolution - effective_read_resolution)); | |
| } | |
| return static_cast<int>(static_cast<uint32_t>(buf) | |
| << (effective_read_resolution - seq.resolution)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overloads.his a header but has no include guard /#pragma once. This can lead to repeated processing and makes it inconsistent with the rest of the core headers (which all use#pragma once). Add an include guard or#pragma onceat the top.