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