From 52be489d811819e76930ea881542eb968786e810 Mon Sep 17 00:00:00 2001 From: Xander Date: Tue, 17 Dec 2024 11:29:50 -0800 Subject: [PATCH 01/12] :sparkles: stm32f1 ADC driver --- CMakeLists.txt | 2 + conanfile.py | 3 +- include/libhal-arm-mcu/stm32f1/adc.hpp | 89 +++++++++++ include/libhal-stm32f1/adc.hpp | 17 +++ src/stm32f1/adc.cpp | 177 ++++++++++++++++++++++ src/stm32f1/adc_reg.hpp | 200 +++++++++++++++++++++++++ tests/stm32f1/adc.test.cpp | 15 ++ 7 files changed, 502 insertions(+), 1 deletion(-) create mode 100644 include/libhal-arm-mcu/stm32f1/adc.hpp create mode 100644 include/libhal-stm32f1/adc.hpp create mode 100644 src/stm32f1/adc.cpp create mode 100644 src/stm32f1/adc_reg.hpp create mode 100644 tests/stm32f1/adc.test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6bc5199..60ed2dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,6 +64,7 @@ libhal_test_and_make_library( src/stm32f1/uart_reg.hpp src/stm32f1/uart.cpp src/stm32f1/spi.cpp + src/stm32f1/adc.cpp # stm32f411 src/stm32f411/input_pin.cpp @@ -100,6 +101,7 @@ libhal_test_and_make_library( tests/stm32f1/output_pin.test.cpp tests/stm32f1/uart.test.cpp tests/stm32f1/spi.test.cpp + tests/stm32f1/adc.test.cpp # stm32f411 tests/stm32f411/output_pin.test.cpp diff --git a/conanfile.py b/conanfile.py index 5e85029..39390d9 100644 --- a/conanfile.py +++ b/conanfile.py @@ -54,7 +54,8 @@ class libhal_arm_mcu_conan(ConanFile): def requirements(self): bootstrap = self.python_requires["libhal-bootstrap"] bootstrap.module.add_library_requirements( - self, override_libhal_util_version="5.3.0") + self, override_libhal_util_version="5.3.0", + override_libhal_version="4.7.0") self.requires("ring-span-lite/[^0.7.0]", transitive_headers=True) self.requires("scope-lite/0.2.0") diff --git a/include/libhal-arm-mcu/stm32f1/adc.hpp b/include/libhal-arm-mcu/stm32f1/adc.hpp new file mode 100644 index 0000000..2f7277b --- /dev/null +++ b/include/libhal-arm-mcu/stm32f1/adc.hpp @@ -0,0 +1,89 @@ +// Copyright 2024 Khalil Estell +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LIC`ENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include + +#include +#include +#include + +namespace hal::stm32f1 { +/** + * @brief Analog to digital converter + * + */ +class adc final : public hal::adc +{ +public: + /// Channel specific information + struct channel + { + /// ADC port + std::uint8_t port; + /// ADC pin + std::uint8_t pin; + /// Channel data index + uint8_t index; + }; + + /** + * @brief Get a predefined adc channel + * + * - ADC channel 0 is PA0 + * - ADC channel 1 is PA1 + * - ADC channel 2 is PA2 + * - ADC channel 3 is PA3 + * - ADC channel 4 is PA4 + * - ADC channel 5 is PA5 + * - ADC channel 6 is PA6 + * - ADC channel 7 is PA7 + * - ADC channel 8 is PB0 + * - ADC channel 9 is PB1 + * - ADC channel 10 is PC0 + * - ADC channel 11 is PC1 + * - ADC channel 12 is PC2 + * - ADC channel 13 is PC3 + * - ADC channel 14 is PC4 + * - ADC channel 15 is PC5 + * + * @param p_channel - which adc channel to use + */ + adc(hal::channel_param auto p_channel) + : adc(get_predefined_channel_info(static_cast(p_channel()))) + { + static_assert(0 <= p_channel() && p_channel() <= 15, + "Available ADC channels are from 0 to 15"); + } + + /** + * @brief Construct a custom adc object based on the passed in channel + * information. + * + * @param p_channel - Which adc channel to use + */ + adc(channel const& p_channel); + + adc(adc const& p_other) = delete; + adc& operator=(adc const& p_other) = delete; + adc(adc&& p_other) noexcept = delete; + adc& operator=(adc&& p_other) noexcept = delete; + virtual ~adc() = default; + +private: + channel get_predefined_channel_info(std::uint8_t p_channel); + float driver_read() override; +}; +} // namespace hal::stm32f1 diff --git a/include/libhal-stm32f1/adc.hpp b/include/libhal-stm32f1/adc.hpp new file mode 100644 index 0000000..c8e4b16 --- /dev/null +++ b/include/libhal-stm32f1/adc.hpp @@ -0,0 +1,17 @@ +// Copyright 2024 Khalil Estell +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include diff --git a/src/stm32f1/adc.cpp b/src/stm32f1/adc.cpp new file mode 100644 index 0000000..13511e8 --- /dev/null +++ b/src/stm32f1/adc.cpp @@ -0,0 +1,177 @@ +// Copyright 2024 Khalil Estell +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include "adc_reg.hpp" +#include "pin.hpp" +#include "power.hpp" + +namespace hal::stm32f1 { + +namespace { +void setup(adc::channel const& p_channel) +{ + auto adc_frequency = frequency(hal::stm32f1::peripheral::adc1); + if (adc_frequency > 14.0_MHz) { + throw std::errc::invalid_argument; + } + + if (p_channel.index >= adc_reg_t::regular_channel_length) { + throw std::errc::invalid_argument; + } + + // Power on adc clock + power_on(peripheral::adc1); + + // Set specified channel to analog input mode + configure_pin({ .port = p_channel.port, .pin = p_channel.pin }, input_analog); + + // Power on adc + hal::bit_modify(adc_reg->control_2) + .set(); + + // Set the specified channel to be sampled + hal::bit_modify(adc_reg->regular_sequence_3) + .insert(p_channel.index); + + // Start adc calibration + hal::bit_modify(adc_reg->control_2) + .set(); + + // Wait for calibration to complete + while (bit_extract( + adc_reg->control_2) == 1) { + } +} +} // namespace + +adc::adc(channel const& p_channel) +{ + setup(p_channel); +} + +adc::channel adc::get_predefined_channel_info(std::uint8_t p_channel) +{ + constexpr std::array channels{ + adc::channel{ + .port = 'A', + .pin = 0, + .index = 0, + }, + adc::channel{ + .port = 'A', + .pin = 1, + .index = 1, + }, + adc::channel{ + .port = 'A', + .pin = 2, + .index = 2, + }, + adc::channel{ + .port = 'A', + .pin = 3, + .index = 3, + }, + adc::channel{ + .port = 'A', + .pin = 4, + .index = 4, + }, + adc::channel{ + .port = 'A', + .pin = 5, + .index = 5, + }, + adc::channel{ + .port = 'A', + .pin = 6, + .index = 6, + }, + adc::channel{ + .port = 'A', + .pin = 7, + .index = 7, + }, + adc::channel{ + .port = 'B', + .pin = 0, + .index = 8, + }, + adc::channel{ + .port = 'B', + .pin = 1, + .index = 9, + }, + adc::channel{ + .port = 'C', + .pin = 0, + .index = 10, + }, + adc::channel{ + .port = 'C', + .pin = 1, + .index = 11, + }, + adc::channel{ + .port = 'C', + .pin = 2, + .index = 12, + }, + adc::channel{ + .port = 'C', + .pin = 3, + .index = 13, + }, + adc::channel{ + .port = 'C', + .pin = 4, + .index = 14, + }, + adc::channel{ + .port = 'C', + .pin = 5, + .index = 15, + }, + }; + return channels[p_channel]; +} + +float adc::driver_read() +{ + // Start adc conversion + hal::bit_modify(adc_reg->control_2) + .set(); + + // Wait for conversion to complete + while (bit_extract(adc_reg->status) == + 0) { + } + + constexpr auto full_scale_max = bit_limits<12, size_t>::max(); + constexpr auto full_scale_float = static_cast(full_scale_max); + // Read sample from peripheral memory + auto sample_integer = + hal::bit_extract( + adc_reg->regular_data); + auto sample = static_cast(sample_integer); + return sample / full_scale_float; +} + +} // namespace hal::stm32f1 diff --git a/src/stm32f1/adc_reg.hpp b/src/stm32f1/adc_reg.hpp new file mode 100644 index 0000000..ee1374e --- /dev/null +++ b/src/stm32f1/adc_reg.hpp @@ -0,0 +1,200 @@ +// Copyright 2024 Khalil Estell +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include + +#include + +namespace hal::stm32f1 { +/// adc register map +struct adc_reg_t +{ + /// Number of regular channels + static constexpr size_t regular_channel_length = 16; + /// Number of injected channels + static constexpr size_t injected_channel_length = 4; + /// Offset: 0x00 A/D Status Register (RC/W0) + std::uint32_t volatile status; + /// Offset: 0x04 A/D Control Register 1 (R/W) + std::uint32_t volatile control_1; + /// Offset: 0x08 A/D Control Register 2 (R/W) + std::uint32_t volatile control_2; + /// Offset: 0x0C A/D Sample Time Register 1 (R/W) + std::uint32_t volatile sample_time_1; + /// Offset: 0x10 A/D Sample Time Register 2 (R/W) + std::uint32_t volatile sample_time_2; + /// Offset: 0x14-0x20 A/D Injected Channel 0..3 Data Offset Register (R/W) + std::array + injected_channel_data_offset; + /// Offset: 0x24 A/D Watchdog High Treshold Register (R/W) + std::uint32_t volatile watchdog_high_threshold; + /// Offset: 0x28 A/D Watchdog Low Treshold Register (R/W) + std::uint32_t volatile watchdog_low_threshold; + /// Offset: 0x2C A/D Regular Sequence Register 1 (R/W) + std::uint32_t volatile regular_sequence_1; + /// Offset: 0x30 A/D Regular Sequence Register 2 (R/W) + std::uint32_t volatile regular_sequence_2; + /// Offset: 0x34 A/D Regular Sequence Register 3 (R/W) + std::uint32_t volatile regular_sequence_3; + /// Offset: 0x38 A/D Injected Sequence Register (R/W) + std::uint32_t volatile injected_sequence; + /// Offset: 0x3C-0x48 A/D Injected Data Register 0..3 (R/ ) + std::array injected_data; + /// Offset: 0x4C A/D Regular Data Register (R/ ) + std::uint32_t volatile regular_data; +}; + +/// Namespace containing the bit_mask objects that are use to manipulate the +/// stm32f1 ADC Status register +namespace adc_status_register { +/// This bit is set by hardware when the converted voltage crosses the values +/// programmed in the ADC_LTR and ADC_HTR registers. It is cleared by software. +static constexpr auto analog_watchdog_flag = hal::bit_mask::from<0>(); + +/// This bit is set by hardware at the end of a group channel conversion +/// (regular or injected). It is cleared by software or by reading the ADC_DR. +static constexpr auto end_of_conversion = hal::bit_mask::from<1>(); + +/// This bit is set by hardware at the end of all injected group channel +/// conversion. It is cleared by software. +static constexpr auto injected_channel_end_of_conversion = + hal::bit_mask::from<2>(); + +/// This bit is set by hardware when injected channel conversion starts. It is +/// cleared by software. +static constexpr auto injected_channel_start_flag = hal::bit_mask::from<3>(); + +/// This bit is set by hardware when regular channel conversion starts. It is +/// cleared by software. +static constexpr auto regular_channel_start_flag = hal::bit_mask::from<4>(); +}; // namespace adc_status_register + +/// Namespace containing the bit_mask objects that are use to manipulate the +/// stm32f1 ADC Control register 2 +namespace adc_control_register_2 { +/// This bit is set and cleared by software. If this bit holds a value of zero +/// and a 1 is written to it then it wakes up the ADC from Power Down state. +/// Conversion starts when this bit holds a value of 1 and a 1 is written to it. +/// The application should allow a delay of tSTAB between power up and start of +/// conversion. Refer to Figure 23. +/// 0: Disable ADC conversion/calibration and go to power down mode. +/// 1: Enable ADC and to start conversion +/// Note: If any other bit in this register apart from ADON is changed at the +/// same time, then conversion is not triggered. This is to prevent triggering +/// an erroneous conversion. +static constexpr auto ad_converter_on = hal::bit_mask::from<0>(); + +/// This bit is set and cleared by software. If set conversion takes place +/// continuously till this bit is reset. +static constexpr auto continuous_conversion = hal::bit_mask::from<1>(); + +/// This bit is set by software to start the calibration. It is reset by +/// hardware after calibration is complete. +static constexpr auto ad_calibration = hal::bit_mask::from<2>(); + +/// This bit is set by software and cleared by hardware, and is used to reset +/// the ADC calibration. It is cleared after the calibration registers are +/// initialized. +static constexpr auto reset_calibration = hal::bit_mask::from<3>(); + +/// This bit is set and cleared by software to enable or disable DMA mode. If +/// its 0 then its disabled, and if its 1 then its enabled. +static constexpr auto direct_memory_access_mode = hal::bit_mask::from<8>(); + +/// This bit is set and cleared by software to determine which data alignment to +/// use. If its 0 then its right-aligned, if its 1 then its left-aligned. +static constexpr auto data_alignment = hal::bit_mask::from<11>(); + +/// These bits select the external event used to trigger the start of conversion +/// of an injected group +static constexpr auto external_event_select_injected_group = + hal::bit_mask::from<12, 14>(); + +/// This bit is set and cleared by software to enable/disable the external +/// trigger used to start conversion of an injected channel group. +static constexpr auto external_trigger_conversion_mode_injected_channels = + hal::bit_mask::from<15>(); + +/// These bits select the external event used to trigger the start of conversion +/// of a regular group. +static constexpr auto external_event_select_regular_group = + hal::bit_mask::from<17, 19>(); + +/// This bit is set and cleared by software to enable/disable the external +/// trigger used to start conversion of a regular channel group. +static constexpr auto external_trigger_conversion_mode_regular_channel = + hal::bit_mask::from<20>(); + +/// This bit is set by software and cleared by software or by hardware as soon +/// as the conversion starts. It starts a conversion of a group of injected +/// channels (if JSWSTART is selected as trigger event by the JEXTSEL[2:0] bits. +static constexpr auto start_conversion_injected_channels = + hal::bit_mask::from<21>(); + +/// This bit is set by software to start conversion and cleared by hardware as +/// soon as conversion starts. It starts a conversion of a group of regular +/// channels if SWSTART is selected as trigger event by the EXTSEL[2:0] bits. +static constexpr auto start_conversion_regular_channels = + hal::bit_mask::from<22>(); + +/// This bit is set and cleared by software to enable/disable the temperature +/// sensor and VREFINT channel. In devices with dual ADCs this bit is present +/// only in ADC1. +static constexpr auto temperature_sensor_and_reference_voltage_enable = + hal::bit_mask::from<23>(); +}; // namespace adc_control_register_2 + +/// Namespace containing the bit_mask objects that are use to manipulate the +/// stm32f1 ADC Regular Sequence register 3 +namespace adc_regular_sequence_register_3 { +/// First channel conversion in regular sequence +static constexpr auto first_conversion = hal::bit_mask::from<0, 4>(); + +/// Second channel conversion in regular sequence +static constexpr auto second_conversion = hal::bit_mask::from<5, 9>(); + +/// Third channel conversion in regular sequence +static constexpr auto third_conversion = hal::bit_mask::from<10, 14>(); + +/// Fourth channel conversion in regular sequence +static constexpr auto fourth_conversion = hal::bit_mask::from<15, 19>(); + +/// Fifth channel conversion in regular sequence +static constexpr auto fifth_conversion = hal::bit_mask::from<20, 24>(); + +/// Sixth channel conversion in regular sequence +static constexpr auto sixth_conversion = hal::bit_mask::from<25, 29>(); +}; // namespace adc_regular_sequence_register_3 + +/// Namespace containing the bit_mask objects that are use to manipulate the +/// stm32f1 ADC Regular Data register +namespace adc_regular_data_register { +/// These bits are read only. They contain the conversion result from the +/// regular channels. The data is left or right-aligned depending on bit 11 in +/// ADC_CR2. +static constexpr auto regular_data = hal::bit_mask::from<0, 15>(); + +/// In ADC1: In dual mode, these bits contain the regular data of ADC2. Refer to +/// Section 11.9: Dual ADC mode. +/// In ADC2 and ADC3: these bits are not used. +static constexpr auto dual_mode_data = hal::bit_mask::from<16, 31>(); +}; // namespace adc_regular_data_register + +constexpr std::uintptr_t stm_apb2_base = 0x40000000UL; +constexpr std::uintptr_t stm_adc_addr = stm_apb2_base + 0x12400; +inline auto* adc_reg = reinterpret_cast(stm_adc_addr); +} // namespace hal::stm32f1 diff --git a/tests/stm32f1/adc.test.cpp b/tests/stm32f1/adc.test.cpp new file mode 100644 index 0000000..104c325 --- /dev/null +++ b/tests/stm32f1/adc.test.cpp @@ -0,0 +1,15 @@ +// Copyright 2024 Khalil Estell +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include From b9581e390682ec22a602bd86d39c526bc895e7ec Mon Sep 17 00:00:00 2001 From: Xander Date: Mon, 23 Dec 2024 12:54:03 -0800 Subject: [PATCH 02/12] :art: Updated to reflect PR suggestions --- include/libhal-arm-mcu/stm32f1/adc.hpp | 70 ++--- include/libhal-stm32f1/adc.hpp | 17 -- src/stm32f1/adc.cpp | 355 +++++++++++++++++-------- src/stm32f1/adc_reg.hpp | 200 -------------- tests/stm32f1/adc.test.cpp | 2 +- 5 files changed, 267 insertions(+), 377 deletions(-) delete mode 100644 include/libhal-stm32f1/adc.hpp delete mode 100644 src/stm32f1/adc_reg.hpp diff --git a/include/libhal-arm-mcu/stm32f1/adc.hpp b/include/libhal-arm-mcu/stm32f1/adc.hpp index 2f7277b..c03b2bc 100644 --- a/include/libhal-arm-mcu/stm32f1/adc.hpp +++ b/include/libhal-arm-mcu/stm32f1/adc.hpp @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LIC`ENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -28,53 +28,38 @@ namespace hal::stm32f1 { class adc final : public hal::adc { public: - /// Channel specific information - struct channel - { - /// ADC port - std::uint8_t port; - /// ADC pin - std::uint8_t pin; - /// Channel data index - uint8_t index; - }; - /** - * @brief Get a predefined adc channel - * - * - ADC channel 0 is PA0 - * - ADC channel 1 is PA1 - * - ADC channel 2 is PA2 - * - ADC channel 3 is PA3 - * - ADC channel 4 is PA4 - * - ADC channel 5 is PA5 - * - ADC channel 6 is PA6 - * - ADC channel 7 is PA7 - * - ADC channel 8 is PB0 - * - ADC channel 9 is PB1 - * - ADC channel 10 is PC0 - * - ADC channel 11 is PC1 - * - ADC channel 12 is PC2 - * - ADC channel 13 is PC3 - * - ADC channel 14 is PC4 - * - ADC channel 15 is PC5 - * - * @param p_channel - which adc channel to use + * @brief Defines the pins which can be used for analog input for the adc */ - adc(hal::channel_param auto p_channel) - : adc(get_predefined_channel_info(static_cast(p_channel()))) + enum class pins : hal::u8 { - static_assert(0 <= p_channel() && p_channel() <= 15, - "Available ADC channels are from 0 to 15"); - } + pa0 = 0, + pa1 = 1, + pa2 = 2, + pa3 = 3, + pa4 = 4, + pa5 = 5, + pa6 = 6, + pa7 = 7, + pb0 = 8, + pb1 = 9, + pc0 = 10, + pc1 = 11, + pc2 = 12, + pc3 = 13, + pc4 = 14, + pc5 = 15, + }; /** - * @brief Construct a custom adc object based on the passed in channel - * information. + * @brief Construct an adc object based on the passed in pin. Note: Each adc + * object is tied to one pin, so to add more pins you need to create more + * objects. * - * @param p_channel - Which adc channel to use + * @param p_pin - Which pin to use. Note: The enum members in "pins" are the + * only pins capable of analog input for the ADC. */ - adc(channel const& p_channel); + adc(pins const& p_pin); adc(adc const& p_other) = delete; adc& operator=(adc const& p_other) = delete; @@ -83,7 +68,8 @@ class adc final : public hal::adc virtual ~adc() = default; private: - channel get_predefined_channel_info(std::uint8_t p_channel); + pins const m_pin; + float driver_read() override; }; } // namespace hal::stm32f1 diff --git a/include/libhal-stm32f1/adc.hpp b/include/libhal-stm32f1/adc.hpp deleted file mode 100644 index c8e4b16..0000000 --- a/include/libhal-stm32f1/adc.hpp +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2024 Khalil Estell -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include diff --git a/src/stm32f1/adc.cpp b/src/stm32f1/adc.cpp index 13511e8..4ca7f18 100644 --- a/src/stm32f1/adc.cpp +++ b/src/stm32f1/adc.cpp @@ -12,149 +12,270 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include -#include -#include +#include +#include + +#include +#include +#include #include #include +#include +#include -#include "adc_reg.hpp" #include "pin.hpp" #include "power.hpp" namespace hal::stm32f1 { namespace { -void setup(adc::channel const& p_channel) + +/// adc register map +struct adc_reg_t { - auto adc_frequency = frequency(hal::stm32f1::peripheral::adc1); - if (adc_frequency > 14.0_MHz) { - throw std::errc::invalid_argument; - } + /// Number of injected channels + static constexpr size_t injected_channel_length = 4; + /// Offset: 0x00 A/D Status Register (RC/W0) + hal::u32 volatile status; + /// Offset: 0x04 A/D Control Register 1 (R/W) + hal::u32 volatile control_1; + /// Offset: 0x08 A/D Control Register 2 (R/W) + hal::u32 volatile control_2; + /// Offset: 0x0C A/D Sample Time Register 1 (R/W) + hal::u32 volatile sample_time_1; + /// Offset: 0x10 A/D Sample Time Register 2 (R/W) + hal::u32 volatile sample_time_2; + /// Offset: 0x14-0x20 A/D Injected Channel 0..3 Data Offset Register (R/W) + std::array + injected_channel_data_offset; + /// Offset: 0x24 A/D Watchdog High Treshold Register (R/W) + hal::u32 volatile watchdog_high_threshold; + /// Offset: 0x28 A/D Watchdog Low Treshold Register (R/W) + hal::u32 volatile watchdog_low_threshold; + /// Offset: 0x2C A/D Regular Sequence Register 1 (R/W) + hal::u32 volatile regular_sequence_1; + /// Offset: 0x30 A/D Regular Sequence Register 2 (R/W) + hal::u32 volatile regular_sequence_2; + /// Offset: 0x34 A/D Regular Sequence Register 3 (R/W) + hal::u32 volatile regular_sequence_3; + /// Offset: 0x38 A/D Injected Sequence Register (R/W) + hal::u32 volatile injected_sequence; + /// Offset: 0x3C-0x48 A/D Injected Data Register 0..3 (R/ ) + std::array injected_data; + /// Offset: 0x4C A/D Regular Data Register (R/ ) + hal::u32 volatile regular_data; +}; + +/// Namespace containing the bit_mask objects that are use to manipulate the +/// stm32f1 ADC Status register +namespace adc_status_register { +/// This bit is set by hardware when the converted voltage crosses the values +/// programmed in the ADC_LTR and ADC_HTR registers. It is cleared by software. +[[maybe_unused]] static constexpr auto analog_watchdog_flag = + hal::bit_mask::from(0); + +/// This bit is set by hardware at the end of a group channel conversion +/// (regular or injected). It is cleared by software or by reading the ADC_DR. +static constexpr auto end_of_conversion = hal::bit_mask::from(1); + +/// This bit is set by hardware at the end of all injected group channel +/// conversion. It is cleared by software. +[[maybe_unused]] static constexpr auto injected_channel_end_of_conversion = + hal::bit_mask::from(2); + +/// This bit is set by hardware when injected channel conversion starts. It is +/// cleared by software. +[[maybe_unused]] static constexpr auto injected_channel_start_flag = + hal::bit_mask::from(3); + +/// This bit is set by hardware when regular channel conversion starts. It is +/// cleared by software. +[[maybe_unused]] static constexpr auto regular_channel_start_flag = + hal::bit_mask::from(4); +}; // namespace adc_status_register + +/// Namespace containing the bit_mask objects that are use to manipulate the +/// stm32f1 ADC Control register 2 +namespace adc_control_register_2 { +/// This bit is set and cleared by software. If this bit holds a value of zero +/// and a 1 is written to it then it wakes up the ADC from Power Down state. +/// Conversion starts when this bit holds a value of 1 and a 1 is written to it. +/// The application should allow a delay of tSTAB between power up and start of +/// conversion. Refer to Figure 23. +/// 0: Disable ADC conversion/calibration and go to power down mode. +/// 1: Enable ADC and to start conversion +/// Note: If any other bit in this register apart from ADON is changed at the +/// same time, then conversion is not triggered. This is to prevent triggering +/// an erroneous conversion. +static constexpr auto ad_converter_on = hal::bit_mask::from(0); + +/// This bit is set and cleared by software. If set conversion takes place +/// continuously till this bit is reset. +[[maybe_unused]] static constexpr auto continuous_conversion = + hal::bit_mask::from(1); + +/// This bit is set by software to start the calibration. It is reset by +/// hardware after calibration is complete. +static constexpr auto ad_calibration = hal::bit_mask::from(2); + +/// This bit is set by software and cleared by hardware, and is used to reset +/// the ADC calibration. It is cleared after the calibration registers are +/// initialized. +[[maybe_unused]] static constexpr auto reset_calibration = + hal::bit_mask::from(3); + +/// This bit is set and cleared by software to enable or disable DMA mode. If +/// its 0 then its disabled, and if its 1 then its enabled. +[[maybe_unused]] static constexpr auto direct_memory_access_mode = + hal::bit_mask::from(8); + +/// This bit is set and cleared by software to determine which data alignment to +/// use. If its 0 then its right-aligned, if its 1 then its left-aligned. +[[maybe_unused]] static constexpr auto data_alignment = hal::bit_mask::from(11); + +/// These bits select the external event used to trigger the start of conversion +/// of an injected group +[[maybe_unused]] static constexpr auto external_event_select_injected_group = + hal::bit_mask::from(12, 14); + +/// This bit is set and cleared by software to enable/disable the external +/// trigger used to start conversion of an injected channel group. +[[maybe_unused]] static constexpr auto + external_trigger_conversion_mode_injected_channels = hal::bit_mask::from(15); + +/// These bits select the external event used to trigger the start of conversion +/// of a regular group. +[[maybe_unused]] static constexpr auto external_event_select_regular_group = + hal::bit_mask::from(17, 19); + +/// This bit is set and cleared by software to enable/disable the external +/// trigger used to start conversion of a regular channel group. +[[maybe_unused]] static constexpr auto + external_trigger_conversion_mode_regular_channel = hal::bit_mask::from(20); + +/// This bit is set by software and cleared by software or by hardware as soon +/// as the conversion starts. It starts a conversion of a group of injected +/// channels (if JSWSTART is selected as trigger event by the JEXTSEL[2:0] bits. +[[maybe_unused]] static constexpr auto start_conversion_injected_channels = + hal::bit_mask::from(21); + +/// This bit is set by software to start conversion and cleared by hardware as +/// soon as conversion starts. It starts a conversion of a group of regular +/// channels if SWSTART is selected as trigger event by the EXTSEL[2:0] bits. +[[maybe_unused]] static constexpr auto start_conversion_regular_channels = + hal::bit_mask::from(22); + +/// This bit is set and cleared by software to enable/disable the temperature +/// sensor and VREFINT channel. In devices with dual ADCs this bit is present +/// only in ADC1. +[[maybe_unused]] static constexpr auto + temperature_sensor_and_reference_voltage_enable = hal::bit_mask::from(23); +}; // namespace adc_control_register_2 + +/// Namespace containing the bit_mask objects that are use to manipulate the +/// stm32f1 ADC Regular Sequence register 3 +namespace adc_regular_sequence_register_3 { +/// First channel conversion in regular sequence +static constexpr auto first_conversion = hal::bit_mask::from(0, 4); - if (p_channel.index >= adc_reg_t::regular_channel_length) { - throw std::errc::invalid_argument; +/// Second channel conversion in regular sequence +[[maybe_unused]] static constexpr auto second_conversion = + hal::bit_mask::from(5, 9); + +/// Third channel conversion in regular sequence +[[maybe_unused]] static constexpr auto third_conversion = + hal::bit_mask::from(10, 14); + +/// Fourth channel conversion in regular sequence +[[maybe_unused]] static constexpr auto fourth_conversion = + hal::bit_mask::from(15, 19); + +/// Fifth channel conversion in regular sequence +[[maybe_unused]] static constexpr auto fifth_conversion = + hal::bit_mask::from(20, 24); + +/// Sixth channel conversion in regular sequence +[[maybe_unused]] static constexpr auto sixth_conversion = + hal::bit_mask::from(25, 29); +}; // namespace adc_regular_sequence_register_3 + +/// Namespace containing the bit_mask objects that are use to manipulate the +/// stm32f1 ADC Regular Data register +namespace adc_regular_data_register { +/// These bits are read only. They contain the conversion result from the +/// regular channels. The data is left or right-aligned depending on bit 11 in +/// ADC_CR2. +static constexpr auto regular_data = hal::bit_mask::from(0, 15); + +/// In ADC1: In dual mode, these bits contain the regular data of ADC2. Refer to +/// Section 11.9: Dual ADC mode. +/// In ADC2 and ADC3: these bits are not used. +[[maybe_unused]] static constexpr auto dual_mode_data = + hal::bit_mask::from(16, 31); +}; // namespace adc_regular_data_register + +constexpr std::uintptr_t stm_apb2_base = 0x40000000UL; +constexpr std::uintptr_t stm_adc_addr = stm_apb2_base + 0x12400; +inline auto* adc_reg = reinterpret_cast(stm_adc_addr); + +void setup(adc::pins const& p_pin) +{ + auto const adc_frequency = frequency(hal::stm32f1::peripheral::adc1); + if (adc_frequency > 14.0_MHz) { + hal::safe_throw(hal::operation_not_supported(&adc_frequency)); } // Power on adc clock power_on(peripheral::adc1); - // Set specified channel to analog input mode - configure_pin({ .port = p_channel.port, .pin = p_channel.pin }, input_analog); + // Derive port and pin from enum + hal::u8 port, pin; + if (hal::value(p_pin) <= 7) { + port = 'A'; + pin = hal::value(p_pin); + } else if (hal::value(p_pin) <= 9) { + port = 'B'; + pin = hal::value(p_pin) - 8; + } else { + port = 'C'; + pin = hal::value(p_pin) - 10; + } - // Power on adc - hal::bit_modify(adc_reg->control_2) - .set(); + // Set specified pin to analog input mode + configure_pin({ .port = port, .pin = pin }, input_analog); - // Set the specified channel to be sampled - hal::bit_modify(adc_reg->regular_sequence_3) - .insert(p_channel.index); + // Turns on and calibrates ADC only if its first time power-up + if (bit_extract( + adc_reg->control_2) == 0) { + // Power on adc + hal::bit_modify(adc_reg->control_2) + .set(); - // Start adc calibration - hal::bit_modify(adc_reg->control_2) - .set(); + // Start adc calibration + hal::bit_modify(adc_reg->control_2) + .set(); - // Wait for calibration to complete - while (bit_extract( - adc_reg->control_2) == 1) { + // Wait for calibration to complete + while (bit_extract( + adc_reg->control_2) == 1) { + } } } } // namespace -adc::adc(channel const& p_channel) +adc::adc(pins const& p_pin) + : m_pin(p_pin) { - setup(p_channel); -} - -adc::channel adc::get_predefined_channel_info(std::uint8_t p_channel) -{ - constexpr std::array channels{ - adc::channel{ - .port = 'A', - .pin = 0, - .index = 0, - }, - adc::channel{ - .port = 'A', - .pin = 1, - .index = 1, - }, - adc::channel{ - .port = 'A', - .pin = 2, - .index = 2, - }, - adc::channel{ - .port = 'A', - .pin = 3, - .index = 3, - }, - adc::channel{ - .port = 'A', - .pin = 4, - .index = 4, - }, - adc::channel{ - .port = 'A', - .pin = 5, - .index = 5, - }, - adc::channel{ - .port = 'A', - .pin = 6, - .index = 6, - }, - adc::channel{ - .port = 'A', - .pin = 7, - .index = 7, - }, - adc::channel{ - .port = 'B', - .pin = 0, - .index = 8, - }, - adc::channel{ - .port = 'B', - .pin = 1, - .index = 9, - }, - adc::channel{ - .port = 'C', - .pin = 0, - .index = 10, - }, - adc::channel{ - .port = 'C', - .pin = 1, - .index = 11, - }, - adc::channel{ - .port = 'C', - .pin = 2, - .index = 12, - }, - adc::channel{ - .port = 'C', - .pin = 3, - .index = 13, - }, - adc::channel{ - .port = 'C', - .pin = 4, - .index = 14, - }, - adc::channel{ - .port = 'C', - .pin = 5, - .index = 15, - }, - }; - return channels[p_channel]; + setup(m_pin); } float adc::driver_read() { + // Set the specified channel to be sampled + hal::bit_modify(adc_reg->regular_sequence_3) + .insert( + hal::value(m_pin)); + // Start adc conversion hal::bit_modify(adc_reg->control_2) .set(); @@ -164,10 +285,10 @@ float adc::driver_read() 0) { } - constexpr auto full_scale_max = bit_limits<12, size_t>::max(); - constexpr auto full_scale_float = static_cast(full_scale_max); + auto const full_scale_max = bit_limits<12, size_t>::max(); + auto const full_scale_float = static_cast(full_scale_max); // Read sample from peripheral memory - auto sample_integer = + auto const sample_integer = hal::bit_extract( adc_reg->regular_data); auto sample = static_cast(sample_integer); diff --git a/src/stm32f1/adc_reg.hpp b/src/stm32f1/adc_reg.hpp deleted file mode 100644 index ee1374e..0000000 --- a/src/stm32f1/adc_reg.hpp +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2024 Khalil Estell -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include - -#include - -namespace hal::stm32f1 { -/// adc register map -struct adc_reg_t -{ - /// Number of regular channels - static constexpr size_t regular_channel_length = 16; - /// Number of injected channels - static constexpr size_t injected_channel_length = 4; - /// Offset: 0x00 A/D Status Register (RC/W0) - std::uint32_t volatile status; - /// Offset: 0x04 A/D Control Register 1 (R/W) - std::uint32_t volatile control_1; - /// Offset: 0x08 A/D Control Register 2 (R/W) - std::uint32_t volatile control_2; - /// Offset: 0x0C A/D Sample Time Register 1 (R/W) - std::uint32_t volatile sample_time_1; - /// Offset: 0x10 A/D Sample Time Register 2 (R/W) - std::uint32_t volatile sample_time_2; - /// Offset: 0x14-0x20 A/D Injected Channel 0..3 Data Offset Register (R/W) - std::array - injected_channel_data_offset; - /// Offset: 0x24 A/D Watchdog High Treshold Register (R/W) - std::uint32_t volatile watchdog_high_threshold; - /// Offset: 0x28 A/D Watchdog Low Treshold Register (R/W) - std::uint32_t volatile watchdog_low_threshold; - /// Offset: 0x2C A/D Regular Sequence Register 1 (R/W) - std::uint32_t volatile regular_sequence_1; - /// Offset: 0x30 A/D Regular Sequence Register 2 (R/W) - std::uint32_t volatile regular_sequence_2; - /// Offset: 0x34 A/D Regular Sequence Register 3 (R/W) - std::uint32_t volatile regular_sequence_3; - /// Offset: 0x38 A/D Injected Sequence Register (R/W) - std::uint32_t volatile injected_sequence; - /// Offset: 0x3C-0x48 A/D Injected Data Register 0..3 (R/ ) - std::array injected_data; - /// Offset: 0x4C A/D Regular Data Register (R/ ) - std::uint32_t volatile regular_data; -}; - -/// Namespace containing the bit_mask objects that are use to manipulate the -/// stm32f1 ADC Status register -namespace adc_status_register { -/// This bit is set by hardware when the converted voltage crosses the values -/// programmed in the ADC_LTR and ADC_HTR registers. It is cleared by software. -static constexpr auto analog_watchdog_flag = hal::bit_mask::from<0>(); - -/// This bit is set by hardware at the end of a group channel conversion -/// (regular or injected). It is cleared by software or by reading the ADC_DR. -static constexpr auto end_of_conversion = hal::bit_mask::from<1>(); - -/// This bit is set by hardware at the end of all injected group channel -/// conversion. It is cleared by software. -static constexpr auto injected_channel_end_of_conversion = - hal::bit_mask::from<2>(); - -/// This bit is set by hardware when injected channel conversion starts. It is -/// cleared by software. -static constexpr auto injected_channel_start_flag = hal::bit_mask::from<3>(); - -/// This bit is set by hardware when regular channel conversion starts. It is -/// cleared by software. -static constexpr auto regular_channel_start_flag = hal::bit_mask::from<4>(); -}; // namespace adc_status_register - -/// Namespace containing the bit_mask objects that are use to manipulate the -/// stm32f1 ADC Control register 2 -namespace adc_control_register_2 { -/// This bit is set and cleared by software. If this bit holds a value of zero -/// and a 1 is written to it then it wakes up the ADC from Power Down state. -/// Conversion starts when this bit holds a value of 1 and a 1 is written to it. -/// The application should allow a delay of tSTAB between power up and start of -/// conversion. Refer to Figure 23. -/// 0: Disable ADC conversion/calibration and go to power down mode. -/// 1: Enable ADC and to start conversion -/// Note: If any other bit in this register apart from ADON is changed at the -/// same time, then conversion is not triggered. This is to prevent triggering -/// an erroneous conversion. -static constexpr auto ad_converter_on = hal::bit_mask::from<0>(); - -/// This bit is set and cleared by software. If set conversion takes place -/// continuously till this bit is reset. -static constexpr auto continuous_conversion = hal::bit_mask::from<1>(); - -/// This bit is set by software to start the calibration. It is reset by -/// hardware after calibration is complete. -static constexpr auto ad_calibration = hal::bit_mask::from<2>(); - -/// This bit is set by software and cleared by hardware, and is used to reset -/// the ADC calibration. It is cleared after the calibration registers are -/// initialized. -static constexpr auto reset_calibration = hal::bit_mask::from<3>(); - -/// This bit is set and cleared by software to enable or disable DMA mode. If -/// its 0 then its disabled, and if its 1 then its enabled. -static constexpr auto direct_memory_access_mode = hal::bit_mask::from<8>(); - -/// This bit is set and cleared by software to determine which data alignment to -/// use. If its 0 then its right-aligned, if its 1 then its left-aligned. -static constexpr auto data_alignment = hal::bit_mask::from<11>(); - -/// These bits select the external event used to trigger the start of conversion -/// of an injected group -static constexpr auto external_event_select_injected_group = - hal::bit_mask::from<12, 14>(); - -/// This bit is set and cleared by software to enable/disable the external -/// trigger used to start conversion of an injected channel group. -static constexpr auto external_trigger_conversion_mode_injected_channels = - hal::bit_mask::from<15>(); - -/// These bits select the external event used to trigger the start of conversion -/// of a regular group. -static constexpr auto external_event_select_regular_group = - hal::bit_mask::from<17, 19>(); - -/// This bit is set and cleared by software to enable/disable the external -/// trigger used to start conversion of a regular channel group. -static constexpr auto external_trigger_conversion_mode_regular_channel = - hal::bit_mask::from<20>(); - -/// This bit is set by software and cleared by software or by hardware as soon -/// as the conversion starts. It starts a conversion of a group of injected -/// channels (if JSWSTART is selected as trigger event by the JEXTSEL[2:0] bits. -static constexpr auto start_conversion_injected_channels = - hal::bit_mask::from<21>(); - -/// This bit is set by software to start conversion and cleared by hardware as -/// soon as conversion starts. It starts a conversion of a group of regular -/// channels if SWSTART is selected as trigger event by the EXTSEL[2:0] bits. -static constexpr auto start_conversion_regular_channels = - hal::bit_mask::from<22>(); - -/// This bit is set and cleared by software to enable/disable the temperature -/// sensor and VREFINT channel. In devices with dual ADCs this bit is present -/// only in ADC1. -static constexpr auto temperature_sensor_and_reference_voltage_enable = - hal::bit_mask::from<23>(); -}; // namespace adc_control_register_2 - -/// Namespace containing the bit_mask objects that are use to manipulate the -/// stm32f1 ADC Regular Sequence register 3 -namespace adc_regular_sequence_register_3 { -/// First channel conversion in regular sequence -static constexpr auto first_conversion = hal::bit_mask::from<0, 4>(); - -/// Second channel conversion in regular sequence -static constexpr auto second_conversion = hal::bit_mask::from<5, 9>(); - -/// Third channel conversion in regular sequence -static constexpr auto third_conversion = hal::bit_mask::from<10, 14>(); - -/// Fourth channel conversion in regular sequence -static constexpr auto fourth_conversion = hal::bit_mask::from<15, 19>(); - -/// Fifth channel conversion in regular sequence -static constexpr auto fifth_conversion = hal::bit_mask::from<20, 24>(); - -/// Sixth channel conversion in regular sequence -static constexpr auto sixth_conversion = hal::bit_mask::from<25, 29>(); -}; // namespace adc_regular_sequence_register_3 - -/// Namespace containing the bit_mask objects that are use to manipulate the -/// stm32f1 ADC Regular Data register -namespace adc_regular_data_register { -/// These bits are read only. They contain the conversion result from the -/// regular channels. The data is left or right-aligned depending on bit 11 in -/// ADC_CR2. -static constexpr auto regular_data = hal::bit_mask::from<0, 15>(); - -/// In ADC1: In dual mode, these bits contain the regular data of ADC2. Refer to -/// Section 11.9: Dual ADC mode. -/// In ADC2 and ADC3: these bits are not used. -static constexpr auto dual_mode_data = hal::bit_mask::from<16, 31>(); -}; // namespace adc_regular_data_register - -constexpr std::uintptr_t stm_apb2_base = 0x40000000UL; -constexpr std::uintptr_t stm_adc_addr = stm_apb2_base + 0x12400; -inline auto* adc_reg = reinterpret_cast(stm_adc_addr); -} // namespace hal::stm32f1 diff --git a/tests/stm32f1/adc.test.cpp b/tests/stm32f1/adc.test.cpp index 104c325..d4bbb61 100644 --- a/tests/stm32f1/adc.test.cpp +++ b/tests/stm32f1/adc.test.cpp @@ -12,4 +12,4 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include +#include From a06ef028ce9f5e73d62e710d714fc3b56eb3ddfa Mon Sep 17 00:00:00 2001 From: Xander Date: Mon, 23 Dec 2024 13:47:01 -0800 Subject: [PATCH 03/12] :art: Update to fix PR suggestions --- include/libhal-arm-mcu/stm32f1/adc.hpp | 6 ++---- src/stm32f1/adc.cpp | 9 +++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/include/libhal-arm-mcu/stm32f1/adc.hpp b/include/libhal-arm-mcu/stm32f1/adc.hpp index c03b2bc..4536d8e 100644 --- a/include/libhal-arm-mcu/stm32f1/adc.hpp +++ b/include/libhal-arm-mcu/stm32f1/adc.hpp @@ -14,8 +14,6 @@ #pragma once -#include - #include #include #include @@ -68,8 +66,8 @@ class adc final : public hal::adc virtual ~adc() = default; private: - pins const m_pin; - float driver_read() override; + + pins m_pin; }; } // namespace hal::stm32f1 diff --git a/src/stm32f1/adc.cpp b/src/stm32f1/adc.cpp index 4ca7f18..2c18970 100644 --- a/src/stm32f1/adc.cpp +++ b/src/stm32f1/adc.cpp @@ -222,7 +222,7 @@ void setup(adc::pins const& p_pin) { auto const adc_frequency = frequency(hal::stm32f1::peripheral::adc1); if (adc_frequency > 14.0_MHz) { - hal::safe_throw(hal::operation_not_supported(&adc_frequency)); + hal::safe_throw(hal::operation_not_supported(nullptr)); } // Power on adc clock @@ -251,7 +251,8 @@ void setup(adc::pins const& p_pin) hal::bit_modify(adc_reg->control_2) .set(); - // Start adc calibration + // Start adc calibration. ADC must have been in power-on state for a minimum + // of 2 clock cycles before starting calibration. hal::bit_modify(adc_reg->control_2) .set(); @@ -285,8 +286,8 @@ float adc::driver_read() 0) { } - auto const full_scale_max = bit_limits<12, size_t>::max(); - auto const full_scale_float = static_cast(full_scale_max); + auto constexpr full_scale_max = bit_limits<12, size_t>::max(); + auto constexpr full_scale_float = static_cast(full_scale_max); // Read sample from peripheral memory auto const sample_integer = hal::bit_extract( From 552c98962763992b56242b6c63335d9e321e2d06 Mon Sep 17 00:00:00 2001 From: Xander Date: Tue, 21 Jan 2025 16:12:30 -0800 Subject: [PATCH 04/12] :sparkles: stm32f1 adc peripheral manager --- include/libhal-arm-mcu/stm32f1/adc.hpp | 100 ++++++++++++--- src/stm32f1/adc.cpp | 165 ++++++++++++++++--------- 2 files changed, 188 insertions(+), 77 deletions(-) diff --git a/include/libhal-arm-mcu/stm32f1/adc.hpp b/include/libhal-arm-mcu/stm32f1/adc.hpp index 4536d8e..42d8f8b 100644 --- a/include/libhal-arm-mcu/stm32f1/adc.hpp +++ b/include/libhal-arm-mcu/stm32f1/adc.hpp @@ -14,20 +14,27 @@ #pragma once +#include #include -#include +#include #include namespace hal::stm32f1 { + /** - * @brief Analog to digital converter + * @brief Manager for the stm32f1 series' onboard adc peripheral. Used to + * construct and manage channel objects that are tied to an adc. This also + * applies to the MCU variants that have more than one adc available. * */ -class adc final : public hal::adc +class adc_peripheral_manager final { public: + // Forward declaration. + class channel; /** - * @brief Defines the pins which can be used for analog input for the adc + * @brief Defines the pins which can be used for analog input for adc1 and + * adc2. */ enum class pins : hal::u8 { @@ -50,24 +57,85 @@ class adc final : public hal::adc }; /** - * @brief Construct an adc object based on the passed in pin. Note: Each adc - * object is tied to one pin, so to add more pins you need to create more - * objects. + * @brief Defines the available adc peripherals that CAN be onboard the MCU. + * Note that the XL-density stm32f1 MCU's only have adc1. + * + */ + enum class adc_selection : hal::u8 + { + adc1 = 0, + adc2 = 1, + }; + + /** + * @brief Construct a new adc peripheral manager object. + * + * @param p_adc_selection - The specified adc peripheral to use. + * @param p_lock - An externally declared lock to use for thread safety when + * trying to read from the adc's. This can be a basic_lock or any type that + * derives from it. + */ + adc_peripheral_manager(adc_selection p_adc_selection, + hal::basic_lock& p_lock); + + /** + * @brief Creates and configures an adc channel under the calling adc + * peripheral manager. + * + * @param p_pin - The pin to be used for the channels analog input. + * @return channel - object that can be read for analog input . + */ + channel acquire_channel(pins p_pin); + +private: + /** + * @brief Takes an analog input reading. * - * @param p_pin - Which pin to use. Note: The enum members in "pins" are the - * only pins capable of analog input for the ADC. + * @param p_pin - The pin to read from. + * @return float - The sampled adc value. */ - adc(pins const& p_pin); + float read_channel(pins p_pin); - adc(adc const& p_other) = delete; - adc& operator=(adc const& p_other) = delete; - adc(adc&& p_other) noexcept = delete; - adc& operator=(adc&& p_other) noexcept = delete; - virtual ~adc() = default; + /// The lock to be used for thread safety with adc reads. + hal::basic_lock* m_lock; + /// A pointer to track the location of the registers for the specified adc + /// peripheral. + void* adc_reg_location; +}; +/** + * @brief Creates channels to be used by the adc peripheral manager to read + * certain pins' analog input. + * + */ +class adc_peripheral_manager::channel : public hal::adc +{ private: + /// Gives adc_peripheral_manager access to channel's private members. + friend class adc_peripheral_manager; + + // Constructor for channel. Needs to configure the given pin + /** + * @brief Constructs a channel object. + * + * @param p_manager - The adc peripheral manager that will be managing this + * channel resource. + * @param p_pin - The pin that will be used for this channels analog input. + */ + channel(adc_peripheral_manager& p_manager, + adc_peripheral_manager::pins p_pin); + + /** + * @brief Takes an analog input reading. + * + * @return float - The sampled adc value. + */ float driver_read() override; - pins m_pin; + /// The adc peripheral manager that manages this channel. + adc_peripheral_manager* m_manager; + /// The pin that is used for this channel. + adc_peripheral_manager::pins m_pin; }; + } // namespace hal::stm32f1 diff --git a/src/stm32f1/adc.cpp b/src/stm32f1/adc.cpp index 2c18970..b098679 100644 --- a/src/stm32f1/adc.cpp +++ b/src/stm32f1/adc.cpp @@ -1,19 +1,7 @@ -// Copyright 2024 Khalil Estell -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +#include #include -#include +#include #include #include @@ -22,6 +10,8 @@ #include #include #include +#include +#include #include "pin.hpp" #include "power.hpp" @@ -67,7 +57,7 @@ struct adc_reg_t }; /// Namespace containing the bit_mask objects that are use to manipulate the -/// stm32f1 ADC Status register +/// stm32f1 ADC Status register. namespace adc_status_register { /// This bit is set by hardware when the converted voltage crosses the values /// programmed in the ADC_LTR and ADC_HTR registers. It is cleared by software. @@ -95,7 +85,7 @@ static constexpr auto end_of_conversion = hal::bit_mask::from(1); }; // namespace adc_status_register /// Namespace containing the bit_mask objects that are use to manipulate the -/// stm32f1 ADC Control register 2 +/// stm32f1 ADC Control register 2. namespace adc_control_register_2 { /// This bit is set and cleared by software. If this bit holds a value of zero /// and a 1 is written to it then it wakes up the ADC from Power Down state. @@ -134,7 +124,7 @@ static constexpr auto ad_calibration = hal::bit_mask::from(2); [[maybe_unused]] static constexpr auto data_alignment = hal::bit_mask::from(11); /// These bits select the external event used to trigger the start of conversion -/// of an injected group +/// of an injected group. [[maybe_unused]] static constexpr auto external_event_select_injected_group = hal::bit_mask::from(12, 14); @@ -173,34 +163,34 @@ static constexpr auto ad_calibration = hal::bit_mask::from(2); }; // namespace adc_control_register_2 /// Namespace containing the bit_mask objects that are use to manipulate the -/// stm32f1 ADC Regular Sequence register 3 +/// stm32f1 ADC Regular Sequence register 3. namespace adc_regular_sequence_register_3 { -/// First channel conversion in regular sequence +/// First channel conversion in regular sequence. static constexpr auto first_conversion = hal::bit_mask::from(0, 4); -/// Second channel conversion in regular sequence +/// Second channel conversion in regular sequence. [[maybe_unused]] static constexpr auto second_conversion = hal::bit_mask::from(5, 9); -/// Third channel conversion in regular sequence +/// Third channel conversion in regular sequence. [[maybe_unused]] static constexpr auto third_conversion = hal::bit_mask::from(10, 14); -/// Fourth channel conversion in regular sequence +/// Fourth channel conversion in regular sequence. [[maybe_unused]] static constexpr auto fourth_conversion = hal::bit_mask::from(15, 19); -/// Fifth channel conversion in regular sequence +/// Fifth channel conversion in regular sequence. [[maybe_unused]] static constexpr auto fifth_conversion = hal::bit_mask::from(20, 24); -/// Sixth channel conversion in regular sequence +/// Sixth channel conversion in regular sequence. [[maybe_unused]] static constexpr auto sixth_conversion = hal::bit_mask::from(25, 29); }; // namespace adc_regular_sequence_register_3 /// Namespace containing the bit_mask objects that are use to manipulate the -/// stm32f1 ADC Regular Data register +/// stm32f1 ADC Regular Data register. namespace adc_regular_data_register { /// These bits are read only. They contain the conversion result from the /// regular channels. The data is left or right-aligned depending on bit 11 in @@ -214,21 +204,44 @@ static constexpr auto regular_data = hal::bit_mask::from(0, 15); hal::bit_mask::from(16, 31); }; // namespace adc_regular_data_register -constexpr std::uintptr_t stm_apb2_base = 0x40000000UL; -constexpr std::uintptr_t stm_adc_addr = stm_apb2_base + 0x12400; -inline auto* adc_reg = reinterpret_cast(stm_adc_addr); - -void setup(adc::pins const& p_pin) +// Function to setup and initialize the specified adc. +void setup_adc(hal::stm32f1::peripheral const& p_adc_peripheral, + adc_reg_t* p_adc_reg) { - auto const adc_frequency = frequency(hal::stm32f1::peripheral::adc1); + // Verify adc's clock is not higher than the maximum frequency. + auto const adc_frequency = frequency(p_adc_peripheral); if (adc_frequency > 14.0_MHz) { hal::safe_throw(hal::operation_not_supported(nullptr)); } - // Power on adc clock - power_on(peripheral::adc1); + // Power on adc clock. + power_on(p_adc_peripheral); + + // Turns on and calibrates the adc only if its the first time power-on. This + // is to prevent accidentally toggling the start of a new conversion as it + // uses the same bit. + if (bit_extract( + p_adc_reg->control_2) == 0) { + // Power on the adc. + hal::bit_modify(p_adc_reg->control_2) + .set(); + + // Start adc calibration. ADC must have been in power-on state for a minimum + // of 2 clock cycles before starting calibration. + hal::bit_modify(p_adc_reg->control_2) + .set(); - // Derive port and pin from enum + // Wait for calibration to complete. + while (bit_extract( + p_adc_reg->control_2) == 1) { + } + } +} + +// Function to setup and initialize the specified pin to analog input mode. +void setup_pin(adc_peripheral_manager::pins const& p_pin) +{ + // Derive port and pin from the enum. hal::u8 port, pin; if (hal::value(p_pin) <= 7) { port = 'A'; @@ -241,54 +254,71 @@ void setup(adc::pins const& p_pin) pin = hal::value(p_pin) - 10; } - // Set specified pin to analog input mode + // Set specified pin to analog input mode. configure_pin({ .port = port, .pin = pin }, input_analog); +} +} // namespace - // Turns on and calibrates ADC only if its first time power-up - if (bit_extract( - adc_reg->control_2) == 0) { - // Power on adc - hal::bit_modify(adc_reg->control_2) - .set(); - - // Start adc calibration. ADC must have been in power-on state for a minimum - // of 2 clock cycles before starting calibration. - hal::bit_modify(adc_reg->control_2) - .set(); - - // Wait for calibration to complete - while (bit_extract( - adc_reg->control_2) == 1) { - } +adc_peripheral_manager::adc_peripheral_manager(adc_selection p_adc_selection, + hal::basic_lock& p_lock) + : m_lock(&p_lock) +{ + // Base address for apb2 bus. + constexpr std::uintptr_t stm_apb2_base = 0x40000000UL; + // Determines the appropriate adc peripheral to use and its memory offset. + int adc_offset; + hal::stm32f1::peripheral adc_peripheral; + switch (p_adc_selection) { + case adc_selection::adc1: + adc_offset = 12400; + adc_peripheral = peripheral::adc1; + break; + case adc_selection::adc2: + adc_offset = 12800; + adc_peripheral = peripheral::adc2; + break; + default: + adc_offset = 12400; + adc_peripheral = peripheral::adc1; + break; } + // Stores address of specified adc peripheral's config registers to the + // manager object. NOLINTNEXTLINE(performance-no-int-to-ptr) + adc_reg_location = reinterpret_cast(stm_apb2_base + adc_offset); + + // NOLINTNEXTLINE(performance-no-int-to-ptr) + setup_adc(adc_peripheral, reinterpret_cast(adc_reg_location)); } -} // namespace -adc::adc(pins const& p_pin) - : m_pin(p_pin) +adc_peripheral_manager::channel adc_peripheral_manager::acquire_channel( + pins p_pin) { - setup(m_pin); + return { *this, p_pin }; } -float adc::driver_read() +float adc_peripheral_manager::read_channel(pins p_pin) { - // Set the specified channel to be sampled + // Lock the lock. + std::lock_guard acquire_lock(*m_lock); + + auto adc_reg = reinterpret_cast(adc_reg_location); + // Set the specified channel to be sampled. hal::bit_modify(adc_reg->regular_sequence_3) .insert( - hal::value(m_pin)); + hal::value(p_pin)); - // Start adc conversion + // Start adc conversion. hal::bit_modify(adc_reg->control_2) .set(); - // Wait for conversion to complete + // Wait for conversion to complete. while (bit_extract(adc_reg->status) == 0) { } auto constexpr full_scale_max = bit_limits<12, size_t>::max(); auto constexpr full_scale_float = static_cast(full_scale_max); - // Read sample from peripheral memory + // Read sample from peripheral's memory. auto const sample_integer = hal::bit_extract( adc_reg->regular_data); @@ -296,4 +326,17 @@ float adc::driver_read() return sample / full_scale_float; } +adc_peripheral_manager::channel::channel(adc_peripheral_manager& p_manager, + adc_peripheral_manager::pins p_pin) + : m_manager(&p_manager) + , m_pin(p_pin) +{ + setup_pin(m_pin); +} + +float adc_peripheral_manager::channel::driver_read() +{ + return m_manager->read_channel(m_pin); +} + } // namespace hal::stm32f1 From 249dc3f5b1f747b43829b7c42d1ca2d3c01383b7 Mon Sep 17 00:00:00 2001 From: Xander Date: Tue, 17 Dec 2024 11:29:50 -0800 Subject: [PATCH 05/12] :sparkles: stm32f1 ADC driver --- conanfile.py | 7 +- include/libhal-stm32f1/adc.hpp | 17 +++ src/stm32f1/adc_reg.hpp | 200 +++++++++++++++++++++++++++++++++ 3 files changed, 222 insertions(+), 2 deletions(-) create mode 100644 include/libhal-stm32f1/adc.hpp create mode 100644 src/stm32f1/adc_reg.hpp diff --git a/conanfile.py b/conanfile.py index 074fd99..be9a657 100644 --- a/conanfile.py +++ b/conanfile.py @@ -52,8 +52,11 @@ class libhal_arm_mcu_conan(ConanFile): } def requirements(self): - self.requires("libhal/[^4.9.0]", transitive_headers=True) - self.requires("libhal-util/[^5.3.0]", transitive_headers=True) + bootstrap = self.python_requires["libhal-bootstrap"] + bootstrap.module.add_library_requirements( + self, override_libhal_util_version="5.3.0", + override_libhal_version="4.7.0") + self.requires("ring-span-lite/[^0.7.0]", transitive_headers=True) self.requires("scope-lite/0.2.0") diff --git a/include/libhal-stm32f1/adc.hpp b/include/libhal-stm32f1/adc.hpp new file mode 100644 index 0000000..c8e4b16 --- /dev/null +++ b/include/libhal-stm32f1/adc.hpp @@ -0,0 +1,17 @@ +// Copyright 2024 Khalil Estell +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include diff --git a/src/stm32f1/adc_reg.hpp b/src/stm32f1/adc_reg.hpp new file mode 100644 index 0000000..ee1374e --- /dev/null +++ b/src/stm32f1/adc_reg.hpp @@ -0,0 +1,200 @@ +// Copyright 2024 Khalil Estell +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include + +#include + +namespace hal::stm32f1 { +/// adc register map +struct adc_reg_t +{ + /// Number of regular channels + static constexpr size_t regular_channel_length = 16; + /// Number of injected channels + static constexpr size_t injected_channel_length = 4; + /// Offset: 0x00 A/D Status Register (RC/W0) + std::uint32_t volatile status; + /// Offset: 0x04 A/D Control Register 1 (R/W) + std::uint32_t volatile control_1; + /// Offset: 0x08 A/D Control Register 2 (R/W) + std::uint32_t volatile control_2; + /// Offset: 0x0C A/D Sample Time Register 1 (R/W) + std::uint32_t volatile sample_time_1; + /// Offset: 0x10 A/D Sample Time Register 2 (R/W) + std::uint32_t volatile sample_time_2; + /// Offset: 0x14-0x20 A/D Injected Channel 0..3 Data Offset Register (R/W) + std::array + injected_channel_data_offset; + /// Offset: 0x24 A/D Watchdog High Treshold Register (R/W) + std::uint32_t volatile watchdog_high_threshold; + /// Offset: 0x28 A/D Watchdog Low Treshold Register (R/W) + std::uint32_t volatile watchdog_low_threshold; + /// Offset: 0x2C A/D Regular Sequence Register 1 (R/W) + std::uint32_t volatile regular_sequence_1; + /// Offset: 0x30 A/D Regular Sequence Register 2 (R/W) + std::uint32_t volatile regular_sequence_2; + /// Offset: 0x34 A/D Regular Sequence Register 3 (R/W) + std::uint32_t volatile regular_sequence_3; + /// Offset: 0x38 A/D Injected Sequence Register (R/W) + std::uint32_t volatile injected_sequence; + /// Offset: 0x3C-0x48 A/D Injected Data Register 0..3 (R/ ) + std::array injected_data; + /// Offset: 0x4C A/D Regular Data Register (R/ ) + std::uint32_t volatile regular_data; +}; + +/// Namespace containing the bit_mask objects that are use to manipulate the +/// stm32f1 ADC Status register +namespace adc_status_register { +/// This bit is set by hardware when the converted voltage crosses the values +/// programmed in the ADC_LTR and ADC_HTR registers. It is cleared by software. +static constexpr auto analog_watchdog_flag = hal::bit_mask::from<0>(); + +/// This bit is set by hardware at the end of a group channel conversion +/// (regular or injected). It is cleared by software or by reading the ADC_DR. +static constexpr auto end_of_conversion = hal::bit_mask::from<1>(); + +/// This bit is set by hardware at the end of all injected group channel +/// conversion. It is cleared by software. +static constexpr auto injected_channel_end_of_conversion = + hal::bit_mask::from<2>(); + +/// This bit is set by hardware when injected channel conversion starts. It is +/// cleared by software. +static constexpr auto injected_channel_start_flag = hal::bit_mask::from<3>(); + +/// This bit is set by hardware when regular channel conversion starts. It is +/// cleared by software. +static constexpr auto regular_channel_start_flag = hal::bit_mask::from<4>(); +}; // namespace adc_status_register + +/// Namespace containing the bit_mask objects that are use to manipulate the +/// stm32f1 ADC Control register 2 +namespace adc_control_register_2 { +/// This bit is set and cleared by software. If this bit holds a value of zero +/// and a 1 is written to it then it wakes up the ADC from Power Down state. +/// Conversion starts when this bit holds a value of 1 and a 1 is written to it. +/// The application should allow a delay of tSTAB between power up and start of +/// conversion. Refer to Figure 23. +/// 0: Disable ADC conversion/calibration and go to power down mode. +/// 1: Enable ADC and to start conversion +/// Note: If any other bit in this register apart from ADON is changed at the +/// same time, then conversion is not triggered. This is to prevent triggering +/// an erroneous conversion. +static constexpr auto ad_converter_on = hal::bit_mask::from<0>(); + +/// This bit is set and cleared by software. If set conversion takes place +/// continuously till this bit is reset. +static constexpr auto continuous_conversion = hal::bit_mask::from<1>(); + +/// This bit is set by software to start the calibration. It is reset by +/// hardware after calibration is complete. +static constexpr auto ad_calibration = hal::bit_mask::from<2>(); + +/// This bit is set by software and cleared by hardware, and is used to reset +/// the ADC calibration. It is cleared after the calibration registers are +/// initialized. +static constexpr auto reset_calibration = hal::bit_mask::from<3>(); + +/// This bit is set and cleared by software to enable or disable DMA mode. If +/// its 0 then its disabled, and if its 1 then its enabled. +static constexpr auto direct_memory_access_mode = hal::bit_mask::from<8>(); + +/// This bit is set and cleared by software to determine which data alignment to +/// use. If its 0 then its right-aligned, if its 1 then its left-aligned. +static constexpr auto data_alignment = hal::bit_mask::from<11>(); + +/// These bits select the external event used to trigger the start of conversion +/// of an injected group +static constexpr auto external_event_select_injected_group = + hal::bit_mask::from<12, 14>(); + +/// This bit is set and cleared by software to enable/disable the external +/// trigger used to start conversion of an injected channel group. +static constexpr auto external_trigger_conversion_mode_injected_channels = + hal::bit_mask::from<15>(); + +/// These bits select the external event used to trigger the start of conversion +/// of a regular group. +static constexpr auto external_event_select_regular_group = + hal::bit_mask::from<17, 19>(); + +/// This bit is set and cleared by software to enable/disable the external +/// trigger used to start conversion of a regular channel group. +static constexpr auto external_trigger_conversion_mode_regular_channel = + hal::bit_mask::from<20>(); + +/// This bit is set by software and cleared by software or by hardware as soon +/// as the conversion starts. It starts a conversion of a group of injected +/// channels (if JSWSTART is selected as trigger event by the JEXTSEL[2:0] bits. +static constexpr auto start_conversion_injected_channels = + hal::bit_mask::from<21>(); + +/// This bit is set by software to start conversion and cleared by hardware as +/// soon as conversion starts. It starts a conversion of a group of regular +/// channels if SWSTART is selected as trigger event by the EXTSEL[2:0] bits. +static constexpr auto start_conversion_regular_channels = + hal::bit_mask::from<22>(); + +/// This bit is set and cleared by software to enable/disable the temperature +/// sensor and VREFINT channel. In devices with dual ADCs this bit is present +/// only in ADC1. +static constexpr auto temperature_sensor_and_reference_voltage_enable = + hal::bit_mask::from<23>(); +}; // namespace adc_control_register_2 + +/// Namespace containing the bit_mask objects that are use to manipulate the +/// stm32f1 ADC Regular Sequence register 3 +namespace adc_regular_sequence_register_3 { +/// First channel conversion in regular sequence +static constexpr auto first_conversion = hal::bit_mask::from<0, 4>(); + +/// Second channel conversion in regular sequence +static constexpr auto second_conversion = hal::bit_mask::from<5, 9>(); + +/// Third channel conversion in regular sequence +static constexpr auto third_conversion = hal::bit_mask::from<10, 14>(); + +/// Fourth channel conversion in regular sequence +static constexpr auto fourth_conversion = hal::bit_mask::from<15, 19>(); + +/// Fifth channel conversion in regular sequence +static constexpr auto fifth_conversion = hal::bit_mask::from<20, 24>(); + +/// Sixth channel conversion in regular sequence +static constexpr auto sixth_conversion = hal::bit_mask::from<25, 29>(); +}; // namespace adc_regular_sequence_register_3 + +/// Namespace containing the bit_mask objects that are use to manipulate the +/// stm32f1 ADC Regular Data register +namespace adc_regular_data_register { +/// These bits are read only. They contain the conversion result from the +/// regular channels. The data is left or right-aligned depending on bit 11 in +/// ADC_CR2. +static constexpr auto regular_data = hal::bit_mask::from<0, 15>(); + +/// In ADC1: In dual mode, these bits contain the regular data of ADC2. Refer to +/// Section 11.9: Dual ADC mode. +/// In ADC2 and ADC3: these bits are not used. +static constexpr auto dual_mode_data = hal::bit_mask::from<16, 31>(); +}; // namespace adc_regular_data_register + +constexpr std::uintptr_t stm_apb2_base = 0x40000000UL; +constexpr std::uintptr_t stm_adc_addr = stm_apb2_base + 0x12400; +inline auto* adc_reg = reinterpret_cast(stm_adc_addr); +} // namespace hal::stm32f1 From 8df56a98399fca7776facd5efbb332f8ad7e21b5 Mon Sep 17 00:00:00 2001 From: Xander Date: Fri, 24 Jan 2025 15:28:04 -0800 Subject: [PATCH 06/12] :sparkles: stm32f1 adc peripheral manager - Updated to fix register pointer error. - Verified and tested with modified demo using both adc1 and adc2, as well as using adc1 with 2 separate channels. --- include/libhal-arm-mcu/stm32f1/adc.hpp | 2 +- src/stm32f1/adc.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/libhal-arm-mcu/stm32f1/adc.hpp b/include/libhal-arm-mcu/stm32f1/adc.hpp index 42d8f8b..efe932b 100644 --- a/include/libhal-arm-mcu/stm32f1/adc.hpp +++ b/include/libhal-arm-mcu/stm32f1/adc.hpp @@ -100,7 +100,7 @@ class adc_peripheral_manager final hal::basic_lock* m_lock; /// A pointer to track the location of the registers for the specified adc /// peripheral. - void* adc_reg_location; + std::uintptr_t adc_reg_location; }; /** diff --git a/src/stm32f1/adc.cpp b/src/stm32f1/adc.cpp index b098679..0f29bc4 100644 --- a/src/stm32f1/adc.cpp +++ b/src/stm32f1/adc.cpp @@ -270,21 +270,21 @@ adc_peripheral_manager::adc_peripheral_manager(adc_selection p_adc_selection, hal::stm32f1::peripheral adc_peripheral; switch (p_adc_selection) { case adc_selection::adc1: - adc_offset = 12400; + adc_offset = 0x12400; adc_peripheral = peripheral::adc1; break; case adc_selection::adc2: - adc_offset = 12800; + adc_offset = 0x12800; adc_peripheral = peripheral::adc2; break; default: - adc_offset = 12400; + adc_offset = 0x12400; adc_peripheral = peripheral::adc1; break; } // Stores address of specified adc peripheral's config registers to the // manager object. NOLINTNEXTLINE(performance-no-int-to-ptr) - adc_reg_location = reinterpret_cast(stm_apb2_base + adc_offset); + adc_reg_location = stm_apb2_base + adc_offset; // NOLINTNEXTLINE(performance-no-int-to-ptr) setup_adc(adc_peripheral, reinterpret_cast(adc_reg_location)); From 46a26dcac4efc6e425dba802a9db2e07f5e64b3b Mon Sep 17 00:00:00 2001 From: Xander Date: Fri, 24 Jan 2025 15:28:04 -0800 Subject: [PATCH 07/12] :sparkles: stm32f1 adc peripheral manager - Updated to fix register pointer error. - Verified and tested with modified demo using both adc1 and adc2, as well as using adc1 with 2 separate channels. --- src/stm32f1/adc_reg.hpp | 200 ---------------------------------------- 1 file changed, 200 deletions(-) delete mode 100644 src/stm32f1/adc_reg.hpp diff --git a/src/stm32f1/adc_reg.hpp b/src/stm32f1/adc_reg.hpp deleted file mode 100644 index ee1374e..0000000 --- a/src/stm32f1/adc_reg.hpp +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2024 Khalil Estell -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include - -#include - -namespace hal::stm32f1 { -/// adc register map -struct adc_reg_t -{ - /// Number of regular channels - static constexpr size_t regular_channel_length = 16; - /// Number of injected channels - static constexpr size_t injected_channel_length = 4; - /// Offset: 0x00 A/D Status Register (RC/W0) - std::uint32_t volatile status; - /// Offset: 0x04 A/D Control Register 1 (R/W) - std::uint32_t volatile control_1; - /// Offset: 0x08 A/D Control Register 2 (R/W) - std::uint32_t volatile control_2; - /// Offset: 0x0C A/D Sample Time Register 1 (R/W) - std::uint32_t volatile sample_time_1; - /// Offset: 0x10 A/D Sample Time Register 2 (R/W) - std::uint32_t volatile sample_time_2; - /// Offset: 0x14-0x20 A/D Injected Channel 0..3 Data Offset Register (R/W) - std::array - injected_channel_data_offset; - /// Offset: 0x24 A/D Watchdog High Treshold Register (R/W) - std::uint32_t volatile watchdog_high_threshold; - /// Offset: 0x28 A/D Watchdog Low Treshold Register (R/W) - std::uint32_t volatile watchdog_low_threshold; - /// Offset: 0x2C A/D Regular Sequence Register 1 (R/W) - std::uint32_t volatile regular_sequence_1; - /// Offset: 0x30 A/D Regular Sequence Register 2 (R/W) - std::uint32_t volatile regular_sequence_2; - /// Offset: 0x34 A/D Regular Sequence Register 3 (R/W) - std::uint32_t volatile regular_sequence_3; - /// Offset: 0x38 A/D Injected Sequence Register (R/W) - std::uint32_t volatile injected_sequence; - /// Offset: 0x3C-0x48 A/D Injected Data Register 0..3 (R/ ) - std::array injected_data; - /// Offset: 0x4C A/D Regular Data Register (R/ ) - std::uint32_t volatile regular_data; -}; - -/// Namespace containing the bit_mask objects that are use to manipulate the -/// stm32f1 ADC Status register -namespace adc_status_register { -/// This bit is set by hardware when the converted voltage crosses the values -/// programmed in the ADC_LTR and ADC_HTR registers. It is cleared by software. -static constexpr auto analog_watchdog_flag = hal::bit_mask::from<0>(); - -/// This bit is set by hardware at the end of a group channel conversion -/// (regular or injected). It is cleared by software or by reading the ADC_DR. -static constexpr auto end_of_conversion = hal::bit_mask::from<1>(); - -/// This bit is set by hardware at the end of all injected group channel -/// conversion. It is cleared by software. -static constexpr auto injected_channel_end_of_conversion = - hal::bit_mask::from<2>(); - -/// This bit is set by hardware when injected channel conversion starts. It is -/// cleared by software. -static constexpr auto injected_channel_start_flag = hal::bit_mask::from<3>(); - -/// This bit is set by hardware when regular channel conversion starts. It is -/// cleared by software. -static constexpr auto regular_channel_start_flag = hal::bit_mask::from<4>(); -}; // namespace adc_status_register - -/// Namespace containing the bit_mask objects that are use to manipulate the -/// stm32f1 ADC Control register 2 -namespace adc_control_register_2 { -/// This bit is set and cleared by software. If this bit holds a value of zero -/// and a 1 is written to it then it wakes up the ADC from Power Down state. -/// Conversion starts when this bit holds a value of 1 and a 1 is written to it. -/// The application should allow a delay of tSTAB between power up and start of -/// conversion. Refer to Figure 23. -/// 0: Disable ADC conversion/calibration and go to power down mode. -/// 1: Enable ADC and to start conversion -/// Note: If any other bit in this register apart from ADON is changed at the -/// same time, then conversion is not triggered. This is to prevent triggering -/// an erroneous conversion. -static constexpr auto ad_converter_on = hal::bit_mask::from<0>(); - -/// This bit is set and cleared by software. If set conversion takes place -/// continuously till this bit is reset. -static constexpr auto continuous_conversion = hal::bit_mask::from<1>(); - -/// This bit is set by software to start the calibration. It is reset by -/// hardware after calibration is complete. -static constexpr auto ad_calibration = hal::bit_mask::from<2>(); - -/// This bit is set by software and cleared by hardware, and is used to reset -/// the ADC calibration. It is cleared after the calibration registers are -/// initialized. -static constexpr auto reset_calibration = hal::bit_mask::from<3>(); - -/// This bit is set and cleared by software to enable or disable DMA mode. If -/// its 0 then its disabled, and if its 1 then its enabled. -static constexpr auto direct_memory_access_mode = hal::bit_mask::from<8>(); - -/// This bit is set and cleared by software to determine which data alignment to -/// use. If its 0 then its right-aligned, if its 1 then its left-aligned. -static constexpr auto data_alignment = hal::bit_mask::from<11>(); - -/// These bits select the external event used to trigger the start of conversion -/// of an injected group -static constexpr auto external_event_select_injected_group = - hal::bit_mask::from<12, 14>(); - -/// This bit is set and cleared by software to enable/disable the external -/// trigger used to start conversion of an injected channel group. -static constexpr auto external_trigger_conversion_mode_injected_channels = - hal::bit_mask::from<15>(); - -/// These bits select the external event used to trigger the start of conversion -/// of a regular group. -static constexpr auto external_event_select_regular_group = - hal::bit_mask::from<17, 19>(); - -/// This bit is set and cleared by software to enable/disable the external -/// trigger used to start conversion of a regular channel group. -static constexpr auto external_trigger_conversion_mode_regular_channel = - hal::bit_mask::from<20>(); - -/// This bit is set by software and cleared by software or by hardware as soon -/// as the conversion starts. It starts a conversion of a group of injected -/// channels (if JSWSTART is selected as trigger event by the JEXTSEL[2:0] bits. -static constexpr auto start_conversion_injected_channels = - hal::bit_mask::from<21>(); - -/// This bit is set by software to start conversion and cleared by hardware as -/// soon as conversion starts. It starts a conversion of a group of regular -/// channels if SWSTART is selected as trigger event by the EXTSEL[2:0] bits. -static constexpr auto start_conversion_regular_channels = - hal::bit_mask::from<22>(); - -/// This bit is set and cleared by software to enable/disable the temperature -/// sensor and VREFINT channel. In devices with dual ADCs this bit is present -/// only in ADC1. -static constexpr auto temperature_sensor_and_reference_voltage_enable = - hal::bit_mask::from<23>(); -}; // namespace adc_control_register_2 - -/// Namespace containing the bit_mask objects that are use to manipulate the -/// stm32f1 ADC Regular Sequence register 3 -namespace adc_regular_sequence_register_3 { -/// First channel conversion in regular sequence -static constexpr auto first_conversion = hal::bit_mask::from<0, 4>(); - -/// Second channel conversion in regular sequence -static constexpr auto second_conversion = hal::bit_mask::from<5, 9>(); - -/// Third channel conversion in regular sequence -static constexpr auto third_conversion = hal::bit_mask::from<10, 14>(); - -/// Fourth channel conversion in regular sequence -static constexpr auto fourth_conversion = hal::bit_mask::from<15, 19>(); - -/// Fifth channel conversion in regular sequence -static constexpr auto fifth_conversion = hal::bit_mask::from<20, 24>(); - -/// Sixth channel conversion in regular sequence -static constexpr auto sixth_conversion = hal::bit_mask::from<25, 29>(); -}; // namespace adc_regular_sequence_register_3 - -/// Namespace containing the bit_mask objects that are use to manipulate the -/// stm32f1 ADC Regular Data register -namespace adc_regular_data_register { -/// These bits are read only. They contain the conversion result from the -/// regular channels. The data is left or right-aligned depending on bit 11 in -/// ADC_CR2. -static constexpr auto regular_data = hal::bit_mask::from<0, 15>(); - -/// In ADC1: In dual mode, these bits contain the regular data of ADC2. Refer to -/// Section 11.9: Dual ADC mode. -/// In ADC2 and ADC3: these bits are not used. -static constexpr auto dual_mode_data = hal::bit_mask::from<16, 31>(); -}; // namespace adc_regular_data_register - -constexpr std::uintptr_t stm_apb2_base = 0x40000000UL; -constexpr std::uintptr_t stm_adc_addr = stm_apb2_base + 0x12400; -inline auto* adc_reg = reinterpret_cast(stm_adc_addr); -} // namespace hal::stm32f1 From 3234dd2ca6902b2528bb9c1f3bc84a76debd8494 Mon Sep 17 00:00:00 2001 From: Xander Date: Fri, 24 Jan 2025 15:28:04 -0800 Subject: [PATCH 08/12] :sparkles: stm32f1 adc peripheral manager - Updated to fix register pointer error. - Verified and tested with modified demo using both adc1 and adc2, as well as using adc1 with 2 separate channels. --- src/stm32f1/adc.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stm32f1/adc.cpp b/src/stm32f1/adc.cpp index 0f29bc4..77a6d7d 100644 --- a/src/stm32f1/adc.cpp +++ b/src/stm32f1/adc.cpp @@ -301,6 +301,7 @@ float adc_peripheral_manager::read_channel(pins p_pin) // Lock the lock. std::lock_guard acquire_lock(*m_lock); + // NOLINTNEXTLINE(performance-no-int-to-ptr) auto adc_reg = reinterpret_cast(adc_reg_location); // Set the specified channel to be sampled. hal::bit_modify(adc_reg->regular_sequence_3) From 0b0bd79f31abd6458ceb3536f5a11e104d4a8cd1 Mon Sep 17 00:00:00 2001 From: Xander Date: Mon, 27 Jan 2025 12:44:17 -0800 Subject: [PATCH 09/12] :art: Updated to reflect PR changes - Changed from adc_reg_t * to void * for adc_reg_location - Included function to cast for usage in implementation file - Updated to make classes non-copyable or movable - Removal/addition of some const's - Fixed comments --- conanfile.py | 9 ++-- include/libhal-arm-mcu/stm32f1/adc.hpp | 28 ++++++++++--- src/stm32f1/adc.cpp | 57 +++++++++++++++++--------- 3 files changed, 62 insertions(+), 32 deletions(-) diff --git a/conanfile.py b/conanfile.py index be9a657..12218e6 100644 --- a/conanfile.py +++ b/conanfile.py @@ -52,11 +52,8 @@ class libhal_arm_mcu_conan(ConanFile): } def requirements(self): - bootstrap = self.python_requires["libhal-bootstrap"] - bootstrap.module.add_library_requirements( - self, override_libhal_util_version="5.3.0", - override_libhal_version="4.7.0") - + self.requires("libhal/[^4.9.0]", transitive_headers=True) + self.requires("libhal-util/[^5.3.0]", transitive_headers=True) self.requires("ring-span-lite/[^0.7.0]", transitive_headers=True) self.requires("scope-lite/0.2.0") @@ -131,4 +128,4 @@ def append_linker_using_platform(self, platform: str): self.cpp_info.exelinkflags.append( "-T" + linker_script_name + ".ld") return - # Add additional script searching queries here + # Add additional script searching queries here \ No newline at end of file diff --git a/include/libhal-arm-mcu/stm32f1/adc.hpp b/include/libhal-arm-mcu/stm32f1/adc.hpp index efe932b..ed169b1 100644 --- a/include/libhal-arm-mcu/stm32f1/adc.hpp +++ b/include/libhal-arm-mcu/stm32f1/adc.hpp @@ -58,7 +58,9 @@ class adc_peripheral_manager final /** * @brief Defines the available adc peripherals that CAN be onboard the MCU. - * Note that the XL-density stm32f1 MCU's only have adc1. + * Having two adc's provides the capability to do dual-channel conversions, as + * well as do simultaneous sampling through dual-mode (not currently + * supported). Note that the XL-density stm32f1 MCU's only have adc1. * */ enum class adc_selection : hal::u8 @@ -83,10 +85,18 @@ class adc_peripheral_manager final * peripheral manager. * * @param p_pin - The pin to be used for the channels analog input. - * @return channel - object that can be read for analog input . + * @return channel - is an implementation of the `hal::adc` interface that + * represents a channel on the ADC peripheral. */ channel acquire_channel(pins p_pin); + adc_peripheral_manager(adc_peripheral_manager const& p_other) = delete; + adc_peripheral_manager& operator=(adc_peripheral_manager const& p_other) = + delete; + adc_peripheral_manager(adc_peripheral_manager&& p_other) noexcept = delete; + adc_peripheral_manager& operator=(adc_peripheral_manager&& p_other) noexcept = + delete; + private: /** * @brief Takes an analog input reading. @@ -100,18 +110,24 @@ class adc_peripheral_manager final hal::basic_lock* m_lock; /// A pointer to track the location of the registers for the specified adc /// peripheral. - std::uintptr_t adc_reg_location; + void* adc_reg_location; }; /** - * @brief Creates channels to be used by the adc peripheral manager to read - * certain pins' analog input. + * @brief This class implements the `hal::adc` abstract base class. It creates + * channels to be used by the adc peripheral manager to read certain pins' + * analog input. * */ class adc_peripheral_manager::channel : public hal::adc { +public: + channel(channel const& p_other) = delete; + channel& operator=(channel const& p_other) = delete; + channel(channel&& p_other) noexcept = delete; + channel& operator=(channel&& p_other) noexcept = delete; + private: - /// Gives adc_peripheral_manager access to channel's private members. friend class adc_peripheral_manager; // Constructor for channel. Needs to configure the given pin diff --git a/src/stm32f1/adc.cpp b/src/stm32f1/adc.cpp index 77a6d7d..c64ee38 100644 --- a/src/stm32f1/adc.cpp +++ b/src/stm32f1/adc.cpp @@ -1,3 +1,17 @@ +// Copyright 2024 Khalil Estell +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include #include @@ -204,10 +218,15 @@ static constexpr auto regular_data = hal::bit_mask::from(0, 15); hal::bit_mask::from(16, 31); }; // namespace adc_regular_data_register -// Function to setup and initialize the specified adc. -void setup_adc(hal::stm32f1::peripheral const& p_adc_peripheral, - adc_reg_t* p_adc_reg) +adc_reg_t& to_reg(void* p_address) { + return *reinterpret_cast(p_address); +} + +void setup_adc(hal::stm32f1::peripheral p_adc_peripheral, void* p_address) +{ + auto& adc_reg = to_reg(p_address); + // Verify adc's clock is not higher than the maximum frequency. auto const adc_frequency = frequency(p_adc_peripheral); if (adc_frequency > 14.0_MHz) { @@ -221,24 +240,23 @@ void setup_adc(hal::stm32f1::peripheral const& p_adc_peripheral, // is to prevent accidentally toggling the start of a new conversion as it // uses the same bit. if (bit_extract( - p_adc_reg->control_2) == 0) { + adc_reg.control_2) == 0) { // Power on the adc. - hal::bit_modify(p_adc_reg->control_2) + hal::bit_modify(adc_reg.control_2) .set(); // Start adc calibration. ADC must have been in power-on state for a minimum // of 2 clock cycles before starting calibration. - hal::bit_modify(p_adc_reg->control_2) + hal::bit_modify(adc_reg.control_2) .set(); // Wait for calibration to complete. while (bit_extract( - p_adc_reg->control_2) == 1) { + adc_reg.control_2) == 1) { } } } -// Function to setup and initialize the specified pin to analog input mode. void setup_pin(adc_peripheral_manager::pins const& p_pin) { // Derive port and pin from the enum. @@ -262,11 +280,12 @@ void setup_pin(adc_peripheral_manager::pins const& p_pin) adc_peripheral_manager::adc_peripheral_manager(adc_selection p_adc_selection, hal::basic_lock& p_lock) : m_lock(&p_lock) + , adc_reg_location(nullptr) { // Base address for apb2 bus. constexpr std::uintptr_t stm_apb2_base = 0x40000000UL; // Determines the appropriate adc peripheral to use and its memory offset. - int adc_offset; + std::uintptr_t adc_offset; hal::stm32f1::peripheral adc_peripheral; switch (p_adc_selection) { case adc_selection::adc1: @@ -283,11 +302,10 @@ adc_peripheral_manager::adc_peripheral_manager(adc_selection p_adc_selection, break; } // Stores address of specified adc peripheral's config registers to the - // manager object. NOLINTNEXTLINE(performance-no-int-to-ptr) - adc_reg_location = stm_apb2_base + adc_offset; + // manager object. + adc_reg_location = reinterpret_cast(stm_apb2_base + adc_offset); - // NOLINTNEXTLINE(performance-no-int-to-ptr) - setup_adc(adc_peripheral, reinterpret_cast(adc_reg_location)); + setup_adc(adc_peripheral, adc_reg_location); } adc_peripheral_manager::channel adc_peripheral_manager::acquire_channel( @@ -301,19 +319,18 @@ float adc_peripheral_manager::read_channel(pins p_pin) // Lock the lock. std::lock_guard acquire_lock(*m_lock); - // NOLINTNEXTLINE(performance-no-int-to-ptr) - auto adc_reg = reinterpret_cast(adc_reg_location); + auto& adc_reg = to_reg(adc_reg_location); // Set the specified channel to be sampled. - hal::bit_modify(adc_reg->regular_sequence_3) + hal::bit_modify(adc_reg.regular_sequence_3) .insert( hal::value(p_pin)); // Start adc conversion. - hal::bit_modify(adc_reg->control_2) + hal::bit_modify(adc_reg.control_2) .set(); // Wait for conversion to complete. - while (bit_extract(adc_reg->status) == + while (bit_extract(adc_reg.status) == 0) { } @@ -322,8 +339,8 @@ float adc_peripheral_manager::read_channel(pins p_pin) // Read sample from peripheral's memory. auto const sample_integer = hal::bit_extract( - adc_reg->regular_data); - auto sample = static_cast(sample_integer); + adc_reg.regular_data); + auto const sample = static_cast(sample_integer); return sample / full_scale_float; } From 3922377fa368b7dc4ce92e2128d9ce4f2341c76e Mon Sep 17 00:00:00 2001 From: Xander Date: Mon, 27 Jan 2025 13:04:20 -0800 Subject: [PATCH 10/12] :art: Fixed formatting --- src/stm32f1/adc.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/stm32f1/adc.cpp b/src/stm32f1/adc.cpp index c64ee38..846f9a1 100644 --- a/src/stm32f1/adc.cpp +++ b/src/stm32f1/adc.cpp @@ -239,8 +239,8 @@ void setup_adc(hal::stm32f1::peripheral p_adc_peripheral, void* p_address) // Turns on and calibrates the adc only if its the first time power-on. This // is to prevent accidentally toggling the start of a new conversion as it // uses the same bit. - if (bit_extract( - adc_reg.control_2) == 0) { + if (bit_extract(adc_reg.control_2) == + 0) { // Power on the adc. hal::bit_modify(adc_reg.control_2) .set(); @@ -303,6 +303,7 @@ adc_peripheral_manager::adc_peripheral_manager(adc_selection p_adc_selection, } // Stores address of specified adc peripheral's config registers to the // manager object. + // NOLINTNEXTLINE(performance-no-int-to-ptr) adc_reg_location = reinterpret_cast(stm_apb2_base + adc_offset); setup_adc(adc_peripheral, adc_reg_location); From 463d7f06aed7dc549b24bcb141eca40c5a6f8d2b Mon Sep 17 00:00:00 2001 From: Xander Date: Tue, 28 Jan 2025 11:42:50 -0800 Subject: [PATCH 11/12] :sparkles: stm32f1 adc driver --- src/stm32f1/adc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stm32f1/adc.cpp b/src/stm32f1/adc.cpp index 846f9a1..a8824f3 100644 --- a/src/stm32f1/adc.cpp +++ b/src/stm32f1/adc.cpp @@ -331,8 +331,8 @@ float adc_peripheral_manager::read_channel(pins p_pin) .set(); // Wait for conversion to complete. - while (bit_extract(adc_reg.status) == - 0) { + while ( + not bit_extract(adc_reg.status)) { } auto constexpr full_scale_max = bit_limits<12, size_t>::max(); From 346c7784ed67e4447c9c8b2ec1d183252baa6615 Mon Sep 17 00:00:00 2001 From: Xander Date: Tue, 28 Jan 2025 11:42:50 -0800 Subject: [PATCH 12/12] :sparkles: stm32f1 adc driver --- include/libhal-arm-mcu/stm32f1/adc.hpp | 15 +-------------- include/libhal-stm32f1/adc.hpp | 17 ----------------- src/stm32f1/adc.cpp | 1 + 3 files changed, 2 insertions(+), 31 deletions(-) delete mode 100644 include/libhal-stm32f1/adc.hpp diff --git a/include/libhal-arm-mcu/stm32f1/adc.hpp b/include/libhal-arm-mcu/stm32f1/adc.hpp index ed169b1..51e07cb 100644 --- a/include/libhal-arm-mcu/stm32f1/adc.hpp +++ b/include/libhal-arm-mcu/stm32f1/adc.hpp @@ -98,12 +98,6 @@ class adc_peripheral_manager final delete; private: - /** - * @brief Takes an analog input reading. - * - * @param p_pin - The pin to read from. - * @return float - The sampled adc value. - */ float read_channel(pins p_pin); /// The lock to be used for thread safety with adc reads. @@ -130,22 +124,15 @@ class adc_peripheral_manager::channel : public hal::adc private: friend class adc_peripheral_manager; - // Constructor for channel. Needs to configure the given pin /** * @brief Constructs a channel object. * - * @param p_manager - The adc peripheral manager that will be managing this - * channel resource. + * @param p_manager - The adc peripheral manager that this channel belongs to. * @param p_pin - The pin that will be used for this channels analog input. */ channel(adc_peripheral_manager& p_manager, adc_peripheral_manager::pins p_pin); - /** - * @brief Takes an analog input reading. - * - * @return float - The sampled adc value. - */ float driver_read() override; /// The adc peripheral manager that manages this channel. diff --git a/include/libhal-stm32f1/adc.hpp b/include/libhal-stm32f1/adc.hpp deleted file mode 100644 index c8e4b16..0000000 --- a/include/libhal-stm32f1/adc.hpp +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2024 Khalil Estell -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include diff --git a/src/stm32f1/adc.cpp b/src/stm32f1/adc.cpp index a8824f3..692c467 100644 --- a/src/stm32f1/adc.cpp +++ b/src/stm32f1/adc.cpp @@ -333,6 +333,7 @@ float adc_peripheral_manager::read_channel(pins p_pin) // Wait for conversion to complete. while ( not bit_extract(adc_reg.status)) { + continue; } auto constexpr full_scale_max = bit_limits<12, size_t>::max();