Loading...
Searching...
No Matches
adc.c
1/***********************************************************************************
2 * @file adc.c *
3 * @author Matt Ricci *
4 * @addtogroup ADC *
5 ***********************************************************************************/
6
7#include "adc.h"
8
9#include "stddef.h"
10
11static void _ADC_init(ADC_TypeDef *, ADC_Config *);
12
13/* =============================================================================== */
23ADC_t ADC_init(ADC_TypeDef *interface, ADC_Config *config) {
24 // Early return error struct if peripheral is NULL
25 if (interface == NULL)
26 return (ADC_t){.interface = NULL};
27
28 // Initialise ADC struct with interface
29 ADC_t adc = {.interface = interface};
30
31 // Update config and enable peripheral
32 ADC_updateConfig(&adc, config);
33
34 // Initialise methods
38
39 return adc;
40}
41
42// ALLOW FORMATTING
43#ifndef __DOXYGEN__
44
45/* =============================================================================== */
57static void _ADC_init(ADC_TypeDef *interface, ADC_Config *config) {
58
59 interface->CR2 &= ~ADC_CR2_ADON; // Power down ADC while configuring
60
61 // --- Configure CR1 ---
62 //
63 // clang-format off
64 interface->CR1 &= ~ADC_CR1_CONFIG_MASK;
65 interface->CR1 |= (
66 (config->RES << ADC_CR1_RES_Pos)
67 | (config->AWDEN << ADC_CR1_AWDEN_Pos)
68 | (config->JAWDEN << ADC_CR1_JAWDEN_Pos)
69 | (config->AWDSGL << ADC_CR1_AWDSGL_Pos)
70 | (config->SCAN << ADC_CR1_SCAN_Pos)
71 | (config->JEOCIE << ADC_CR1_JEOCIE_Pos)
72 | (config->AWDIE << ADC_CR1_AWDIE_Pos)
73 | (config->EOCIE << ADC_CR1_EOCIE_Pos)
74 | (config->AWDCH << ADC_CR1_AWDCH_Pos)
75 );
76 // clang-format on
77
78 // --- Configure CR2 ---
79 //
80 // clang-format off
81 interface->CR2 &= ~ADC_CR2_CONFIG_MASK;
82 interface->CR2 |= (
83 (config->ALIGN << ADC_CR2_ALIGN_Pos)
84 | (config->EOCS << ADC_CR2_EOCS_Pos)
85 | (config->DMA << ADC_CR2_DMA_Pos)
86 | (config->CONT << ADC_CR2_CONT_Pos)
87 );
88 // clang-format on
89
90 // --- Configure SMPR2 (Channels 10-18) and SMPR2 (Channels 0-9) ---
91 // SMPR2: For channels 0 to 9
92 interface->SMPR2 &= ~ADC_SMPR2_CONFIG_MASK;
93 for (uint8_t i = 0; i < 10; i++)
94 interface->SMPR2 |= (config->SMP[i] << (i * 3));
95
96 // SMPR1: For channels 10 to 18
97 interface->SMPR1 &= ~ADC_SMPR1_CONFIG_MASK;
98 for (uint8_t i = 0; i < 9; i++)
99 interface->SMPR1 |= (config->SMP[10 + i] << (i * 3));
100
101 // --- Configure Watchdog Thresholds ---
102 // HTR: Higher threshold. Only bits [11:0] are used.
103 interface->HTR &= ~ADC_HTR_HT;
104 interface->HTR |= config->HTR & ADC_HTR_HT_Msk;
105
106 // LTR: Lower threshold. Only bits [11:0] are used.
107 interface->LTR &= ~ADC_LTR_LT;
108 interface->LTR |= config->LTR & ADC_LTR_LT_Msk;
109
110 // --- Configure Regular Sequence Registers SQR1, SQR2, SQR3 ---
111 // Number of conversions in regular sequence (L field in SQR1)
112 // config->L is ADC_Lx which is 0 for 1 conversion, up to 15 for 16 conversions.
113 // SQR1_L field is (number of conversions - 1).
114 interface->SQR1 &= ~ADC_SQR1_CONFIG_MASK;
115 interface->SQR1 |= (config->L << ADC_SQR1_L_Pos); // L is 0-indexed (0 means 1 conversion)
116
117 // SQR3: SQ1-SQ6 (5 bits per SQx)
118 interface->SQR3 &= ~ADC_SQR3_CONFIG_MASK;
119 uint8_t conversions = config->L + 1; // Actual number of conversions
120 for (uint8_t i = 0; i < 6 && i < conversions; i++) {
121 interface->SQR3 |= (config->SQ[i] << (i * 5));
122 }
123
124 // SQR2: SQ7-SQ12
125 interface->SQR2 &= ~ADC_SQR2_CONFIG_MASK;
126 for (uint8_t i = 0; i < 6 && (i + 6) < conversions; i++) {
127 interface->SQR2 |= (config->SQ[i + 6] << (i * 5));
128 }
129
130 // SQR1: SQ13-SQ16
131 interface->SQR1;
132 for (uint8_t i = 0; i < 4 && (i + 12) < conversions; i++) {
133 interface->SQR1 |= (config->SQ[i + 12] << (i * 5));
134 }
135
136 // --- Configure Injected Sequence (JSQR) ---
137 interface->JSQR &= ~ADC_JSQR_CONFIG_MASK;
138 interface->JSQR |= (config->JL << ADC_JSQR_JL_Pos); // JL is 0 for 1 conv, up to 3 for 4 convs.
139 conversions = config->JL + 1;
140 for (uint8_t i = 0; i < 4 && i < conversions; i++) {
141 interface->JSQR |= (config->JSQ[i] << (i * 4));
142 }
143
144 // clang-format off
145 ADC123_COMMON->CCR &= ~(ADC_CCR_TSVREFE_Msk | ADC_CCR_VBATE_Msk | ADC_CCR_ADCPRE_Msk);
146 ADC123_COMMON->CCR |= (
147 ((config->TSVREFE & 1) << ADC_CCR_TSVREFE_Pos)
148 | ((config->VBATE & 1) << ADC_CCR_VBATE_Pos)
149 | ((config->ADCPRE & 3) << ADC_CCR_ADCPRE_Pos)
150 );
151 // clang-format on
152
153 interface->CR2 |= ADC_CR2_ADON; // Finish configuring and power on
154 for (volatile int i = 0; i < 500; ++i);
155}
156
157#endif
158
159/* =============================================================================== */
171 // Eearly exit if struct is invalid
172 if (adc == NULL || adc->interface == NULL)
173 return false;
174
175 // Ensure ADON is set
176 if ((adc->interface->CR2 & ADC_CR2_ADON) == 0) {
177 adc->interface->CR2 |= ADC_CR2_ADON;
178 }
179
180 // Set start bit and begin conversion
181 if (type == ADC_CONVERSION_REGULAR)
182 adc->interface->CR2 |= ADC_CR2_SWSTART;
183 else if (type == ADC_CONVERSION_INJECTED)
184 adc->interface->CR2 |= ADC_CR2_JSWSTART;
185 else
186 return false;
187
188 return true;
189}
190
191/* =============================================================================== */
201uint16_t ADC_readData(ADC_t *adc) {
202 // Early exit with 0 if struct is invalid
203 if (adc == NULL || adc->interface == NULL)
204 return 0;
205
206 // Wait for EOC (End Of Conversion) flag for regular channel
207 // This is a blocking wait.
208 while (!(adc->interface->SR & ADC_SR_EOC));
209
210 // Reading DR clears the EOC flag (if EOCS=0 in CR2, or if it's the last of sequence)
211 return (adc->interface->DR & 0xFFFF);
212}
213
214/* =============================================================================== */
225bool ADC_updateConfig(ADC_t *adc, ADC_Config *config) {
226 // Initialise config with default values if passed NULL.
227 if (config == NULL) {
228 config = &ADC_CONFIG_DEFAULT;
229 }
230
231 // TODO: Validate config before setting, early return false on error
232
233 // Update peripheral with new config
234 adc->config = *config;
235
236 // Initialise SPI registers and enable peripheral
237 _ADC_init(adc->interface, config);
238
239 return true;
240}
bool(* startConversion)(struct _ADC *adc, ADC_ConversionType type)
Function pointer to start an ADC conversion (regular or injected).
Definition adc.h:330
bool DMA
Direct Memory Access mode enable for regular channels (CR2.DMA).
Definition adc.h:308
ADC_Config config
Current configuration of the ADC peripheral.
Definition adc.h:328
ADC_Prescale ADCPRE
ADC prescaler for ADCCLK (ADC_CCR.ADCPRE).
Definition adc.h:319
bool AWDSGL
Analog Watchdog on single channel (true) or all (false) (CR1.AWDSGL).
Definition adc.h:303
uint16_t LTR
Analog watchdog lower threshold (LTR). Value should be 12-bit.
Definition adc.h:312
bool EOCIE
Interrupt Enable for End Of regular Conversion (CR1.EOCIE).
Definition adc.h:300
ADC_EocSelect EOCS
End Of Conversion Selection (CR2.EOCS).
Definition adc.h:307
bool VBATE
VBAT channel enable (ADC_CCR.VBATE).
Definition adc.h:318
ADC_Align ALIGN
Data alignment (CR2.ALIGN).
Definition adc.h:306
bool SCAN
Scan mode enable for regular channels (CR1.SCAN).
Definition adc.h:304
ADC_SequenceLength JL
Injected channel sequence length (1 to 4 conversions) (JSQR.JL).
Definition adc.h:315
uint16_t(* readData)(struct _ADC *adc)
Function pointer to read the ADC regular conversion result.
Definition adc.h:331
bool AWDEN
Analog Watchdog Enable on regular channels (CR1.AWDEN).
Definition adc.h:298
ADC_TypeDef * interface
Pointer to the STM32 ADC peripheral register map (e.g., ADC1).
Definition adc.h:327
bool AWDIE
Interrupt Enable for Analog Watchdog (CR1.AWDIE).
Definition adc.h:301
bool CONT
Continuous conversion mode for regular channels (CR2.CONT).
Definition adc.h:309
bool JAWDEN
Analog Watchdog Enable on injected channels (CR1.JAWDEN).
Definition adc.h:299
bool(* updateConfig)(struct _ADC *adc, ADC_Config *config)
Function pointer to update the ADC configuration.
Definition adc.h:329
ADC_Resolution RES
Resolution of the ADC (CR1.RES).
Definition adc.h:297
ADC_Channel SQ[16]
Regular channel sequence definition (SQ1 to SQ16) (SQR1, SQR2, SQR3).
Definition adc.h:314
ADC_SampleTime SMP[19]
Sampling time for each channel (0-18) (SMPR1, SMPR2).
Definition adc.h:310
bool JEOCIE
Interrupt Enable for End Of Injected Conversion (CR1.JEOCIE).
Definition adc.h:302
ADC_Channel JSQ[4]
Injected channel sequence definition (JSQ1 to JSQ4) (JSQR).
Definition adc.h:316
ADC_Channel AWDCH
Analog Watchdog Channel Select (if AWDSGL is true) (CR1.AWDCH).
Definition adc.h:305
bool TSVREFE
Temperature sensor and VREFINT enable (ADC_CCR.TSVREFE).
Definition adc.h:317
ADC_SequenceLength L
Regular channel sequence length (1 to 16 conversions) (SQR1.L).
Definition adc.h:313
uint16_t HTR
Analog watchdog higher threshold (HTR). Value should be 12-bit.
Definition adc.h:311
ADC_t ADC_init(ADC_TypeDef *interface, ADC_Config *config)
Definition adc.c:23
uint16_t ADC_readData(ADC_t *adc)
Reads the data from the last ADC regular conversion.
Definition adc.c:201
bool ADC_startConversion(ADC_t *adc, ADC_ConversionType type)
Starts a regular ADC conversion.
Definition adc.c:170
bool ADC_updateConfig(ADC_t *adc, ADC_Config *config)
Update ADC peripheral configuration.
Definition adc.c:225
ADC_ConversionType
ADC conversion type enumeration.
Definition adc.h:286
@ ADC_CONVERSION_REGULAR
Start a regular group conversion.
Definition adc.h:287
@ ADC_CONVERSION_INJECTED
Start an injected group conversion.
Definition adc.h:288
ADC configuration structure.
Definition adc.h:296
Struct definition for ADC interface. Provides the API handle for consumers to interact with an ADC pe...
Definition adc.h:326