1 /**
2 * \file
3 *
4 * \brief SAM Analog Comparator Driver
5 *
6 * Copyright (C) 2014-2016 Atmel Corporation. All rights reserved.
7 *
8 * \asf_license_start
9 *
10 * \page License
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions are met:
14 *
15 * 1. Redistributions of source code must retain the above copyright notice,
16 * this list of conditions and the following disclaimer.
17 *
18 * 2. Redistributions in binary form must reproduce the above copyright notice,
19 * this list of conditions and the following disclaimer in the documentation
20 * and/or other materials provided with the distribution.
21 *
22 * 3. The name of Atmel may not be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 *
25 * 4. This software may only be redistributed and used in connection with an
26 * Atmel microcontroller product.
27 *
28 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
29 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
30 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
31 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
32 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
37 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 * POSSIBILITY OF SUCH DAMAGE.
39 *
40 * \asf_license_stop
41 *
42 */
43 /*
44 * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
45 */
46 #include "ac.h"
47
_ac_set_config(struct ac_module * const module_inst,struct ac_config * const config)48 static enum status_code _ac_set_config(
49 struct ac_module *const module_inst,
50 struct ac_config *const config)
51 {
52 /* Sanity check arguments */
53 Assert(module_inst);
54 Assert(module_inst->hw);
55 Assert(config);
56
57 UNUSED(module_inst);
58
59 /* Set up GCLK */
60 struct system_gclk_chan_config gclk_chan_conf;
61 system_gclk_chan_get_config_defaults(&gclk_chan_conf);
62 gclk_chan_conf.source_generator = config->source_generator;
63 #if (SAMC21) || (SAMC20)
64 /* The Analog Comparators and ADC1 use the same generic clock configuration.
65 * GCLK_ADC1 must be used to configure the clock for AC as GCLK_AC is not
66 * functional. Errata reference: 13404
67 */
68 system_gclk_chan_set_config(34, &gclk_chan_conf);
69 system_gclk_chan_enable(34);
70 #else
71 system_gclk_chan_set_config(AC_GCLK_ID, &gclk_chan_conf);
72 system_gclk_chan_enable(AC_GCLK_ID);
73 #endif
74
75 return STATUS_OK;
76 }
77
78 /** \brief Resets and disables the Analog Comparator driver.
79 *
80 * Resets and disables the Analog Comparator driver, resets the internal
81 * states and registers of the hardware module to their power-on defaults.
82 *
83 * \param[out] module_inst Pointer to the AC software instance struct
84 */
ac_reset(struct ac_module * const module_inst)85 enum status_code ac_reset(
86 struct ac_module *const module_inst)
87 {
88 /* Sanity check arguments */
89 Assert(module_inst);
90 Assert(module_inst->hw);
91
92 Ac *const ac_module = module_inst->hw;
93
94 /* Disable the hardware module */
95 ac_disable(module_inst);
96
97 while (ac_is_syncing(module_inst)) {
98 /* Wait until synchronization is complete */
99 }
100
101 /* Software reset the module */
102 ac_module->CTRLA.reg |= AC_CTRLA_SWRST;
103
104 return STATUS_OK;
105 }
106
107 /** \brief Initializes and configures the Analog Comparator driver.
108 *
109 * Initializes the Analog Comparator driver, configuring it to the user
110 * supplied configuration parameters, ready for use. This function should be
111 * called before enabling the Analog Comparator.
112 *
113 * \note Once called the Analog Comparator will not be running; to start the
114 * Analog Comparator call \ref ac_enable() after configuring the module.
115 *
116 * \param[out] module_inst Pointer to the AC software instance struct
117 * \param[in] hw Pointer to the AC module instance
118 * \param[in] config Pointer to the config struct, created by the user
119 * application
120 */
ac_init(struct ac_module * const module_inst,Ac * const hw,struct ac_config * const config)121 enum status_code ac_init(
122 struct ac_module *const module_inst,
123 Ac *const hw,
124 struct ac_config *const config)
125 {
126 /* Sanity check arguments */
127 Assert(module_inst);
128 Assert(hw);
129 Assert(config);
130
131 /* Initialize device instance */
132 module_inst->hw = hw;
133
134 #if (SAML21) || (SAMR30)
135 /* Turn on the digital interface clock */
136 system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBD, MCLK_APBDMASK_AC);
137 #else
138 /* Turn on the digital interface clock */
139 system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, MCLK_APBCMASK_AC);
140 #endif
141
142 #if AC_CALLBACK_MODE == true
143 /* Initialize parameters */
144 for (uint8_t i = 0; i < AC_CALLBACK_N; i++) {
145 module_inst->callback[i] = NULL;
146 }
147
148 /* Initialize software flags*/
149 module_inst->register_callback_mask = 0x00;
150 module_inst->enable_callback_mask = 0x00;
151
152 # if (AC_INST_NUM == 1)
153 _ac_instance[0] = module_inst;
154 # else
155 /* Register this instance for callbacks*/
156 _ac_instance[_ac_get_inst_index(hw)] = module_inst;
157 # endif
158 #endif
159
160 /* Write configuration to module */
161 return _ac_set_config(module_inst, config);
162 }
163
164 /** \brief Writes an Analog Comparator channel configuration to the hardware module.
165 *
166 * Writes a given Analog Comparator channel configuration to the hardware
167 * module.
168 *
169 * \param[in] module_inst Software instance for the Analog Comparator peripheral
170 * \param[in] channel Analog Comparator channel to configure
171 * \param[in] config Pointer to the channel configuration struct
172 */
ac_chan_set_config(struct ac_module * const module_inst,const enum ac_chan_channel channel,struct ac_chan_config * const config)173 enum status_code ac_chan_set_config(
174 struct ac_module *const module_inst,
175 const enum ac_chan_channel channel,
176 struct ac_chan_config *const config)
177 {
178 /* Sanity check arguments */
179 Assert(module_inst);
180 Assert(module_inst->hw);
181 Assert(config);
182
183 Ac *const ac_module = module_inst->hw;
184
185 /* Use a temporary variable to compute the comparator configuration */
186 uint32_t compctrl_temp = 0;
187
188 /* Enable output filter mode */
189 compctrl_temp |= config->filter;
190
191 /* Comparators should be enabled during sleep */
192 if (config->run_in_standby == true) {
193 compctrl_temp |= AC_COMPCTRL_RUNSTDBY;
194 }
195
196 /* Set output signal routing mode */
197 compctrl_temp |= config->output_mode;
198
199 /* Configure comparator positive and negative pin MUX configurations */
200 compctrl_temp |=
201 (uint32_t)config->positive_input |
202 (uint32_t)config->negative_input;
203
204 /* Set sampling mode (single shot or continuous) */
205 compctrl_temp |= config->sample_mode;
206
207 /* Set channel interrupt selection */
208 compctrl_temp |= config->interrupt_selection;
209
210 while (ac_is_syncing(module_inst)) {
211 /* Wait until synchronization is complete */
212 }
213
214 /* Write the final configuration to the module's control register */
215 ac_module->COMPCTRL[(uint8_t)channel].reg = compctrl_temp;
216
217 /* Configure VCC voltage scaling for the comparator */
218 ac_module->SCALER[(uint8_t)channel].reg = config->vcc_scale_factor - 1;
219
220 return STATUS_OK;
221 }
222
223 /**
224 * \brief Function used to setup interrupt selection of a window.
225 *
226 * This function is used to setup when an interrupt should occur
227 * for a given window.
228 *
229 * \note This must be done before enabling the channel.
230 *
231 * \param[in] module_inst Pointer to software instance struct
232 * \param[in] win_channel Window channel to setup
233 * \param[in] config Configuration for the given window channel
234 *
235 * \retval STATUS_OK Function exited successful
236 * \retval STATUS_ERR_INVALID_ARG win_channel argument incorrect
237 */
ac_win_set_config(struct ac_module * const module_inst,enum ac_win_channel const win_channel,struct ac_win_config * const config)238 enum status_code ac_win_set_config(
239 struct ac_module *const module_inst,
240 enum ac_win_channel const win_channel,
241 struct ac_win_config *const config)
242 {
243 Assert(module_inst);
244 Assert(module_inst->hw);
245 Assert(config);
246
247 uint8_t winctrl_mask;
248
249 winctrl_mask = module_inst->hw->WINCTRL.reg;
250
251 if (win_channel == AC_WIN_CHANNEL_0) {
252 winctrl_mask &= ~AC_WINCTRL_WINTSEL0_Msk;
253 winctrl_mask |= config->interrupt_selection;
254 }
255 #if (AC_PAIRS > 1)
256 else if (win_channel == AC_WIN_CHANNEL_1) {
257 winctrl_mask &= ~AC_WINCTRL_WINTSEL1_Msk;
258 winctrl_mask = (config->interrupt_selection << (AC_WINCTRL_WINTSEL1_Pos -
259 AC_WINCTRL_WINTSEL0_Pos));
260 }
261 #endif /* (AC_PAIRS > 1) */
262 else {
263 return STATUS_ERR_INVALID_ARG ;
264 }
265
266 module_inst->hw->WINCTRL.reg = winctrl_mask;
267
268 return STATUS_OK;
269 }
270
271 /** \brief Enables an Analog Comparator window channel that was previously configured.
272 *
273 * Enables and starts an Analog Comparator window channel.
274 *
275 * \note The comparator channels used by the window channel must be configured
276 * and enabled before calling this function. The two comparator channels
277 * forming each window comparator pair must have identical configurations
278 * other than the negative pin multiplexer setting.
279 *
280 * \param[in] module_inst Software instance for the Analog Comparator peripheral
281 * \param[in] win_channel Comparator window channel to enable
282 *
283 * \return Status of the window enable procedure.
284 *
285 * \retval STATUS_OK The window comparator was enabled
286 * \retval STATUS_ERR_IO One or both comparators in the window
287 * comparator pair is disabled
288 * \retval STATUS_ERR_BAD_FORMAT The comparator channels in the window pair
289 * were not configured correctly
290 */
ac_win_enable(struct ac_module * const module_inst,const enum ac_win_channel win_channel)291 enum status_code ac_win_enable(
292 struct ac_module *const module_inst,
293 const enum ac_win_channel win_channel)
294 {
295 /* Sanity check arguments */
296 Assert(module_inst);
297 Assert(module_inst->hw);
298
299 Ac *const ac_module = module_inst->hw;
300
301 /* Load the configurations of the two comparators used in the window */
302 uint32_t win_pair_comp0_conf = ac_module->COMPCTRL[win_channel * 2].reg;
303 uint32_t win_pair_comp1_conf = ac_module->COMPCTRL[win_channel * 2 + 1].reg;
304
305 /* Make sure both comparators in the window comparator pair are enabled */
306 if (!(win_pair_comp0_conf & AC_COMPCTRL_ENABLE) ||
307 !(win_pair_comp1_conf & AC_COMPCTRL_ENABLE)) {
308 return STATUS_ERR_IO;
309 }
310
311 /* Make sure the comparators are configured in the same way, other than the
312 * negative pin multiplexers */
313 if ((win_pair_comp0_conf & ~AC_COMPCTRL_MUXNEG_Msk) !=
314 (win_pair_comp1_conf & ~AC_COMPCTRL_MUXNEG_Msk)) {
315 return STATUS_ERR_BAD_FORMAT;
316 }
317
318 while (ac_is_syncing(module_inst)) {
319 /* Wait until synchronization is complete */
320 }
321
322 /* Enable the requested window comparator */
323 switch (win_channel)
324 {
325 case AC_WIN_CHANNEL_0:
326 ac_module->WINCTRL.reg |= AC_WINCTRL_WEN0;
327 break;
328
329 #if (AC_PAIRS > 1)
330 case AC_WIN_CHANNEL_1:
331 ac_module->WINCTRL.reg |= AC_WINCTRL_WEN1;
332 break;
333 #endif
334 }
335
336 return STATUS_OK;
337 }
338
339 /** \brief Disables an Analog Comparator window channel that was previously enabled.
340 *
341 * Stops an Analog Comparator window channel that was previously started via a
342 * call to \ref ac_win_enable().
343 *
344 * \param[in] module_inst Software instance for the Analog Comparator peripheral
345 * \param[in] win_channel Comparator window channel to disable
346 */
ac_win_disable(struct ac_module * const module_inst,const enum ac_win_channel win_channel)347 void ac_win_disable(
348 struct ac_module *const module_inst,
349 const enum ac_win_channel win_channel)
350 {
351 /* Sanity check arguments */
352 Assert(module_inst);
353 Assert(module_inst->hw);
354
355 Ac *const ac_module = module_inst->hw;
356
357 while (ac_is_syncing(module_inst)) {
358 /* Wait until synchronization is complete */
359 }
360
361 /* Disable the requested window comparator */
362 switch (win_channel)
363 {
364 case AC_WIN_CHANNEL_0:
365 ac_module->WINCTRL.reg &= ~AC_WINCTRL_WEN0;
366 break;
367
368 #if (AC_PAIRS > 1)
369 case AC_WIN_CHANNEL_1:
370 ac_module->WINCTRL.reg &= ~AC_WINCTRL_WEN1;
371 break;
372 #endif
373 }
374 }
375
376 /** \brief Determines the state of a specified Window Comparator.
377 *
378 * Retrieves the current window detection state, indicating what the input
379 * signal is currently comparing to relative to the window boundaries.
380 *
381 * \param[in] module_inst Software instance for the Analog Comparator peripheral
382 * \param[in] win_channel Comparator Window channel to test
383 *
384 * \return Bit mask of Analog Comparator window channel status flags.
385 */
ac_win_get_status(struct ac_module * const module_inst,const enum ac_win_channel win_channel)386 uint8_t ac_win_get_status(
387 struct ac_module *const module_inst,
388 const enum ac_win_channel win_channel)
389 {
390 /* Sanity check arguments */
391 Assert(module_inst);
392 Assert(module_inst->hw);
393
394 Ac *const ac_module = module_inst->hw;
395
396 uint32_t win_status = 0;
397
398 /* Check if interrupt flag is set */
399 if (ac_module->INTFLAG.reg & (AC_INTFLAG_WIN0 << win_channel)) {
400 win_status |= AC_WIN_STATUS_INTERRUPT_SET;
401 }
402
403 /* If one or both window comparators not ready, return unknown result */
404 if (ac_win_is_ready(module_inst, win_channel) == false) {
405 win_status |= AC_WIN_STATUS_UNKNOWN;
406 return win_status;
407 }
408
409 uint8_t statusa_tmp = ac_module->STATUSA.reg;
410
411 /* Map hardware comparison states to logical window states */
412 if (statusa_tmp & (AC_STATUSA_WSTATE0_BELOW << win_channel)) {
413 return win_status | AC_WIN_STATUS_BELOW;
414 } else if (statusa_tmp & (AC_STATUSA_WSTATE0_INSIDE << win_channel)) {
415 return win_status | AC_WIN_STATUS_INSIDE;
416 } else {
417 return win_status | AC_WIN_STATUS_ABOVE;
418 }
419
420 }
421