GCC Code Coverage Report


Directory: lib/fprime-samd/fprime-samd/
File: Drv/AdcDriver/AdcDriver.cpp
Date: 2026-09-26 00:05:09
Exec Total Coverage
Lines: 0 69 0.0%
Functions: 0 7 0.0%
Branches: 0 54 0.0%

Line Branch Exec Source
1 // ======================================================================
2 // \title AdcDriver.cpp
3 // \author crsmith
4 // \brief cpp file for AdcDriver component implementation class
5 // ======================================================================
6
7 #include "fprime-samd/Drv/AdcDriver/AdcDriver.hpp"
8 #include "fprime-samd/Drv/AdcDriver/AdcDriverHardware.hpp"
9
10 namespace Samd21 {
11
12 // Singleton instance for ISR access
13 AdcDriver* AdcDriver::s_instance = nullptr;
14
15 // ----------------------------------------------------------------------
16 // Component construction and destruction
17 // ----------------------------------------------------------------------
18
19 ✗ AdcDriver::AdcDriver(const char* const compName)
20 : AdcDriverComponentBase(compName),
21 ✗ m_configured(false),
22 ✗ m_gain(0),
23 ✗ m_state(State::IDLE),
24 ✗ m_lastResult(0),
25 ✗ m_pendingPortNum(0) {
26 // Initialize channel arrays
27 ✗ for (FwIndexType i = 0; i < ADC_CHANNEL_COUNT; i++) {
28 ✗ m_channels[i] = AdcChannel::AIN0;
29 ✗ m_channelConfigured[i] = false;
30 }
31 // Note: s_instance is set in configure() to ensure only configured driver handles interrupts
32 ✗ }
33
34 ✗ AdcDriver::~AdcDriver() {
35 ✗ if (s_instance == this) {
36 ✗ s_instance = nullptr;
37 }
38 ✗ }
39
40 // ----------------------------------------------------------------------
41 // Configuration methods
42 // ----------------------------------------------------------------------
43 // Convenient conversion time calculator https://blog.thea.codes/getting-the-most-out-of-the-samd21-adc/
44 ✗ void AdcDriver::configure(VoltageReference ref, Resolution res, SampleCount samples, U8 samplingTime, Gain gain) {
45 // Enforce singleton: only one AdcDriver instance can be configured at a time
46 // to ensure ISR routing works correctly. A reconfigure() method could be added
47 // in the future to allow runtime reconfiguration of ADC parameters (resolution,
48 // reference voltage, etc.) without violating the singleton constraint.
49 ✗ FW_ASSERT(s_instance == nullptr);
50
51 // Store gain for use in readAdc
52 ✗ m_gain = static_cast<U8>(gain);
53
54 // Delegate all hardware configuration to the HAL
55 ✗ AdcHardware::AdcHal::configure(ref, res, samples, samplingTime, gain);
56
57 // Register singleton for ISR access
58 ✗ s_instance = this;
59
60 // Enable interrupts via HAL (HAL handles priority calculation)
61 ✗ AdcHardware::AdcHal::enableInterrupt();
62 ✗ AdcHardware::AdcHal::enableAdcInterrupts();
63
64 ✗ m_configured = true;
65 ✗ }
66
67 ✗ void AdcDriver::configureChannel(FwIndexType portNum, AdcChannel channel) {
68 ✗ FW_ASSERT(m_configured);
69 ✗ FW_ASSERT(portNum < ADC_CHANNEL_COUNT);
70 ✗ FW_ASSERT(!m_channelConfigured[portNum]);
71
72 // Note: Pin muxing should be configured in topology BEFORE calling this for external channels (AIN0-AIN19)
73 // Use Samd21::PinMux::configure(PINMUX_xxx) in instances.fpp
74 // Internal channels (TEMP, BANDGAP, SCALEDIOVCC) don't require pin configuration
75
76 // Enable internal resources if needed (only TEMP and BANDGAP require hardware setup)
77 ✗ if (channel == AdcChannel::TEMP || channel == AdcChannel::BANDGAP) {
78 ✗ AdcHardware::AdcHal::configureChannel(channel);
79 }
80
81 // Store channel mapping in component state
82 ✗ m_channels[portNum] = channel;
83 ✗ m_channelConfigured[portNum] = true;
84 ✗ }
85
86 // ----------------------------------------------------------------------
87 // Handler implementations for typed input ports
88 // ----------------------------------------------------------------------
89
90 ✗ Samd21::AdcStatus AdcDriver::readAdc_handler(FwIndexType portNum) {
91 // Validate configuration
92 ✗ if (!m_configured) {
93 ✗ return Samd21::AdcStatus::ADC_NOT_CONFIGURED;
94 }
95 ✗ if (!m_channelConfigured[portNum]) {
96 ✗ return Samd21::AdcStatus::ADC_INVALID_CHANNEL;
97 }
98
99 // Check if ADC is already busy
100 ✗ if (m_state != State::IDLE) {
101 ✗ return Samd21::AdcStatus::ADC_BUSY;
102 }
103
104 // Select input channel with configured gain via HAL
105 ✗ AdcHardware::AdcHal::selectChannel(m_channels[portNum], m_gain);
106
107 // Transition to CONVERTING state
108 ✗ m_state = State::CONVERTING;
109 ✗ m_pendingPortNum = portNum;
110
111 // Start conversion via HAL (interrupt will fire when complete)
112 ✗ AdcHardware::AdcHal::startConversion();
113
114 ✗ return Samd21::AdcStatus::ADC_OK;
115 }
116
117 ✗ bool AdcDriver::activeIn_handler(FwIndexType portNum, U32 context) {
118 (void)portNum;
119 (void)context;
120
121 // Check if conversion is complete and result ready to deliver
122 ✗ if (m_state == State::COMPLETE || m_state == State::COMPLETE_OVERRUN) {
123 // Determine status based on whether overrun occurred
124 ✗ Samd21::AdcStatus status =
125 ✗ (m_state == State::COMPLETE_OVERRUN) ? Samd21::AdcStatus::ADC_OVERRUN : Samd21::AdcStatus::ADC_OK;
126
127 ✗ const FwIndexType portNumToReport = m_pendingPortNum;
128 ✗ const U32 resultToReport = m_lastResult;
129
130 ✗ m_state = State::IDLE;
131
132 ✗ this->adcResult_out(portNumToReport, resultToReport, status);
133 ✗ } else {
134 ✗ return false;
135 }
136
137 ✗ return true;
138 }
139
140 // ----------------------------------------------------------------------
141 // Interrupt handler
142 // ----------------------------------------------------------------------
143
144 ✗ void AdcDriver::handleInterrupt() {
145 // Only process interrupt if we're actually waiting for a conversion
146 ✗ if (m_state != State::CONVERTING) {
147 // Spurious interrupt - clear flags but don't change state
148 ✗ if (AdcHardware::AdcHal::isOverrun()) {
149 ✗ AdcHardware::AdcHal::clearOverrun();
150 }
151 ✗ if (AdcHardware::AdcHal::isResultReady()) {
152 ✗ (void)AdcHardware::AdcHal::readResult(); // Clear RESRDY by reading
153 }
154 ✗ return;
155 }
156
157 ✗ bool overrun = false;
158
159 // Check for overrun error first via HAL
160 ✗ if (AdcHardware::AdcHal::isOverrun()) {
161 // Clear overrun flag via HAL
162 ✗ AdcHardware::AdcHal::clearOverrun();
163 ✗ overrun = true;
164 }
165
166 // Check if RESRDY flag is set via HAL (this happens even if OVERRUN occurred)
167 ✗ if (AdcHardware::AdcHal::isResultReady()) {
168 // Read result via HAL (reading RESULT automatically clears RESRDY flag per SAMD21 datasheet ยง33.6.5)
169 ✗ m_lastResult = AdcHardware::AdcHal::readResult();
170
171 // Transition to appropriate COMPLETE state
172 ✗ m_state = overrun ? State::COMPLETE_OVERRUN : State::COMPLETE;
173 }
174 }
175
176 } // namespace Samd21
177