summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/arm/atsam/libraries/libchip/source/afec.c
blob: d81d739a9fc86b38c98a5d223fcc466e74e47065 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
/* ---------------------------------------------------------------------------- */
/*                  Atmel Microcontroller Software Support                      */
/*                       SAM Software Package License                           */
/* ---------------------------------------------------------------------------- */
/* Copyright (c) 2015, Atmel Corporation                                        */
/*                                                                              */
/* All rights reserved.                                                         */
/*                                                                              */
/* Redistribution and use in source and binary forms, with or without           */
/* modification, are permitted provided that the following condition is met:    */
/*                                                                              */
/* - Redistributions of source code must retain the above copyright notice,     */
/* this list of conditions and the disclaimer below.                            */
/*                                                                              */
/* Atmel's name may not be used to endorse or promote products derived from     */
/* this software without specific prior written permission.                     */
/*                                                                              */
/* DISCLAIMER:  THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR   */
/* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE   */
/* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,      */
/* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */
/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,  */
/* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    */
/* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING         */
/* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */
/* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                           */
/* ---------------------------------------------------------------------------- */

/** \addtogroup AFEC_module Working with AFE
 * \ingroup peripherals_module
 * The AFE driver provides the interface to configure and use the AFE peripheral.
 * \n
 *
 * It converts the analog input to digital format. The converted result could be
 * 12bit or 10bit. The AFE supports up to 16 analog lines.
 *
 * To Enable a AFE conversion,the user has to follow these few steps:
 * <ul>
 * <li> Select an appropriate reference voltage on ADVREF   </li>
 * <li> Configure the AFE according to its requirements and special needs,which
 * could be  broken down into several parts:
 * -#   Select the resolution by setting or clearing AFEC_MR_LOWRES bit in
 *      AFEC_MR (Mode Register)
 * -#   Set AFE clock by setting AFEC_MR_PRESCAL bits in AFEC_MR, the clock is
 *      calculated with AFEClock = MCK / ((PRESCAL+1) * 2)
 * -#   Set Startup Time,Tracking Clock cycles and Transfer Clock respectively
 *      in AFEC_MR.
 </li>
 * <li> Start conversion by setting AFEC_CR_START in AFEC_CR. </li>
 * </ul>
 *
 * For more accurate information, please look at the AFE section of the
 * Datasheet.
 *
 * Related files :\n
 * \ref afec.c\n
 * \ref afec.h\n
 * \ref afe_dma.c\n
 * \ref afe_dma.h\n
 */
/*@{*/
/*@}*/
/**
 * \file
 *
 * Implementation of Analog-to-Digital Converter (AFE).
 *
 */
/*----------------------------------------------------------------------------
 *        Headers
 *----------------------------------------------------------------------------*/

#include "chip.h"


/*----------------------------------------------------------------------------
 *        Local variables
 *----------------------------------------------------------------------------*/

/** Current working clock */
static uint32_t dwAFEClock = 0;

/*----------------------------------------------------------------------------
 *        Exported functions
 *----------------------------------------------------------------------------*/

/**
 * \brief Initialize the AFE controller
 *
 * \param pAFE Pointer to an AFE instance.
 * \param dwID AFE Index
 */
extern void AFEC_Initialize(Afec *pAFE, uint32_t dwID)
{
	/* Enable peripheral clock*/
	PMC_EnablePeripheral(dwID);

	/*  Reset the controller */
	pAFE->AFEC_CR = AFEC_CR_SWRST;

	/* Reset Mode Register */
	pAFE->AFEC_MR = 0;
}

/**
 * \brief Set AFE clock.
 *
 * \param pAFE Pointer to an AFE instance.
 * \param dwPres prescale value
 * \param dwMck Board MCK (Hz)
 *
 * \return AFE clock
 */

extern uint32_t AFEC_SetClock(Afec *pAFE, uint32_t dwClk, uint32_t dwMck)
{
	uint32_t dwPres, dwMr;
	/* Formula for PRESCAL is:
	   PRESCAL = peripheral clock/ fAFE Clock - 1 */

	dwPres = (dwMck) / (dwClk) - 1;
	dwMr = AFEC_MR_PRESCAL(dwPres);

	if (dwMr == 0) return 0;

	dwMr |= (pAFE->AFEC_MR & ~AFEC_MR_PRESCAL_Msk);
	pAFE->AFEC_MR = dwMr;
	dwAFEClock = dwMck / (dwPres + 1);
	return dwAFEClock;
}

/**
 * \brief Set AFE timing.
 *
 * \param pAFE Pointer to an AFE instance.
 * \param dwStartup startup value
 * \param dwTracking tracking value
 * \param dwSettling settling value
 */
extern void AFEC_SetTiming(Afec *pAFE, uint32_t dwStartup, uint32_t dwTracking,
							uint32_t dwSettling)
{
	uint32_t dwMr;

	dwMr = pAFE->AFEC_MR;
	dwMr &= (~AFEC_MR_STARTUP_Msk) & (~AFEC_MR_TRACKTIM_Msk) &
			(~AFEC_MR_SETTLING_Msk);

	/* Formula:
	 *     Startup  Time = startup value / AFEClock
	 *     Transfer Time = (TRANSFER * 2 + 3) / AFEClock
	 *     Tracking Time = (TRACKTIM + 1) / AFEClock
	 *     Settling Time = settling value / AFEClock
	 */
	dwMr |= dwStartup | dwTracking | dwSettling;
	pAFE->AFEC_MR |= dwMr;
}

/**
 * \brief Set AFE trigger.
 *
 * \param pAFE Pointer to an AFE instance.
 * \param dwTrgSel Trigger selection
 */
extern void AFEC_SetTrigger(Afec *pAFE, uint32_t dwTrgSel)
{
	uint32_t dwMr;

	dwMr = pAFE->AFEC_MR;
	dwMr &= ~AFEC_MR_TRGSEL_Msk;
	dwMr |= dwTrgSel;
	pAFE->AFEC_MR |= dwMr;
}


/**
 * \brief Enable/Disable sleep mode.
 *
 * \param pAFE Pointer to an AFE instance.
 * \param bEnDis Enable/Disable sleep mode.
 */
extern void AFEC_SetSleepMode(Afec *pAFE, uint8_t bEnDis)
{
	if (bEnDis)
		pAFE->AFEC_MR |=  AFEC_MR_SLEEP;
	else
		pAFE->AFEC_MR &= ~AFEC_MR_SLEEP;
}

/**
 * \brief Enable/Disable fast wake up.
 *
 * \param pAFE Pointer to an AFE instance.
 * \param bEnDis Enable/Disable fast wake up in sleep mode.
 */
extern void AFEC_SetFastWakeup(Afec *pAFE, uint8_t bEnDis)
{
	if (bEnDis)
		pAFE->AFEC_MR |=  AFEC_MR_FWUP;
	else
		pAFE->AFEC_MR &= ~AFEC_MR_FWUP;
}

/**
 * \brief Enable/Disable sequence mode.
 *
 * \param pAFE  Pointer to an AFE instance.
 * \param bEnDis Enable/Disable sequence mode.
 */
extern void AFEC_SetSequenceMode(Afec *pAFE, uint8_t bEnDis)
{
	if (bEnDis) {
		/* User Sequence Mode: The sequence respects what is defined in
		   AFEC_SEQR1 and AFEC_SEQR2 */
		pAFE->AFEC_MR |=  AFEC_MR_USEQ;
	} else {
		/* Normal Mode: The controller converts channels in a simple
		numeric order. */
		pAFE->AFEC_MR &= ~AFEC_MR_USEQ;
	}
}

/**
 * \brief Set channel sequence.
 *
 * \param pAFE   Pointer to an AFE instance.
 * \param dwSEQ1 Sequence 1 ~ 8  channel number.
 * \param dwSEQ2 Sequence 9 ~ 16 channel number.
 */
extern void AFEC_SetSequence(Afec *pAFE, uint32_t dwSEQ1, uint32_t dwSEQ2)
{
	pAFE->AFEC_SEQ1R = dwSEQ1;
	pAFE->AFEC_SEQ2R = dwSEQ2;
}

/**
 * \brief Set channel sequence by given channel list.
 *
 * \param pAFE    Pointer to an AFE instance.
 * \param ucChList Channel list.
 * \param ucNumCh  Number of channels in list.
 */
extern void AFEC_SetSequenceByList(Afec *pAFE, uint8_t ucChList[],
									uint8_t ucNumCh)
{
	uint8_t i;
	uint8_t ucShift;

	pAFE->AFEC_SEQ1R = 0;

	for (i = 0, ucShift = 0; i < 8; i ++, ucShift += 4) {
		if (i >= ucNumCh) return;

		pAFE->AFEC_SEQ1R |= ucChList[i] << ucShift;

	}

	pAFE->AFEC_SEQ2R = 0;

	for (ucShift = 0; i < 16; i ++, ucShift += 4) {
		if (i >= ucNumCh) return;

		pAFE->AFEC_SEQ2R |= ucChList[i] << ucShift;
	}
}

/**
 * \brief Set analog change.
 * IF enabled, it allows different analog settings for each channel,
 * otherwise, DIFF0, GAIN0 and OFF0 are used for all channels.
 *
 * \param pAFE   Pointer to an AFE instance.
 * \param bEnDis Enable/Disable.
 */
extern void AFEC_SetAnalogChange(Afec *pAFE, uint8_t bEnDis)
{
	if (bEnDis)
		pAFE->AFEC_MR |=  AFEC_MR_ONE;
	else
		pAFE->AFEC_MR &= ~AFEC_MR_ONE;
}

/**
 * \brief Set "TAG" mode, show channel number in last data or not.
 *
 * \param pAFE   Pointer to an AFE instance.
 * \param bEnDis Enable/Disable TAG value.
 */
extern void AFEC_SetTagEnable(Afec *pAFE, uint8_t bEnDis)
{
	if (bEnDis)
		pAFE->AFEC_EMR |=  AFEC_EMR_TAG;
	else
		pAFE->AFEC_EMR &= ~AFEC_EMR_TAG;
}

/**
 * \brief Set compare channel.
 *
 * \param pAFE Pointer to an AFE instance.
 * \param dwChannel channel number to be set,16 for all channels
 */
extern void AFEC_SetCompareChannel(Afec *pAFE, uint32_t dwChannel)
{
	assert(dwChannel <= 16);

	if (dwChannel < 16) {
		pAFE->AFEC_EMR &= ~(AFEC_EMR_CMPALL);
		pAFE->AFEC_EMR &= ~(AFEC_EMR_CMPSEL_Msk);
		pAFE->AFEC_EMR |= (dwChannel << AFEC_EMR_CMPSEL_Pos);
	} else
		pAFE->AFEC_EMR |= AFEC_EMR_CMPALL;
}

/**
 * \brief Set compare mode.
 *
 * \param pAFE Pointer to an AFE instance.
 * \param dwMode compare mode
 */
extern void AFEC_SetCompareMode(Afec *pAFE, uint32_t dwMode)
{
	pAFE->AFEC_EMR &= ~(AFEC_EMR_CMPMODE_Msk);
	pAFE->AFEC_EMR |= (dwMode & AFEC_EMR_CMPMODE_Msk);
}

/**
 * \brief Set comparison window.
 *
 * \param pAFE Pointer to an AFE instance.
 * \param dwHi_Lo Comparison Window
 */
extern void AFEC_SetComparisonWindow(Afec *pAFE, uint32_t dwHi_Lo)
{
	pAFE->AFEC_CWR = dwHi_Lo;
}

/**
 * \brief Return the Channel Converted Data
 *
 * \param pAFE Pointer to an AFE instance.
 * \param dwChannel channel to get converted value
 */
extern uint32_t AFEC_GetConvertedData(Afec *pAFE, uint32_t dwChannel)
{
	uint32_t dwData = 0;
	assert(dwChannel < 12);
	pAFE->AFEC_CSELR = dwChannel;
	dwData = pAFE->AFEC_CDR;

	return dwData;
}


/**
 * Sets the AFE startup time.
 * \param pAFE  Pointer to an AFE instance.
 * \param dwUs  Startup time in uS.
 */
void AFEC_SetStartupTime(Afec *pAFE, uint32_t dwUs)
{
	uint32_t dwStart;
	uint32_t dwMr;

	if (dwAFEClock == 0) return;

	/* Formula for STARTUP is:
	   STARTUP = (time x AFECLK) / (1000000) - 1
	   Division multiplied by 10 for higher precision */

	dwStart = (dwUs * dwAFEClock) / (100000);

	if (dwStart % 10) dwStart /= 10;
	else {
		dwStart /= 10;

		if (dwStart) dwStart --;
	}

	if      (dwStart >  896) dwMr = AFEC_MR_STARTUP_SUT960;
	else if (dwStart >  832) dwMr = AFEC_MR_STARTUP_SUT896;
	else if (dwStart >  768) dwMr = AFEC_MR_STARTUP_SUT832;
	else if (dwStart >  704) dwMr = AFEC_MR_STARTUP_SUT768;
	else if (dwStart >  640) dwMr = AFEC_MR_STARTUP_SUT704;
	else if (dwStart >  576) dwMr = AFEC_MR_STARTUP_SUT640;
	else if (dwStart >  512) dwMr = AFEC_MR_STARTUP_SUT576;
	else if (dwStart >  112) dwMr = AFEC_MR_STARTUP_SUT512;
	else if (dwStart >   96) dwMr = AFEC_MR_STARTUP_SUT112;
	else if (dwStart >   80) dwMr = AFEC_MR_STARTUP_SUT96;
	else if (dwStart >   64) dwMr = AFEC_MR_STARTUP_SUT80;
	else if (dwStart >   24) dwMr = AFEC_MR_STARTUP_SUT64;
	else if (dwStart >   16) dwMr = AFEC_MR_STARTUP_SUT24;
	else if (dwStart >    8) dwMr = AFEC_MR_STARTUP_SUT16;
	else if (dwStart >    0) dwMr = AFEC_MR_STARTUP_SUT8;
	else                     dwMr = AFEC_MR_STARTUP_SUT0;

	dwMr |= pAFE->AFEC_MR & ~AFEC_MR_STARTUP_Msk;
	pAFE->AFEC_MR = dwMr;
}


/**
 * Set AFE tracking time
 * \param pAFE  Pointer to an AFE instance.
 * \param dwNs  Tracking time in nS.
 */
void AFEC_SetTrackingTime(Afec *pAFE, uint32_t dwNs)
{
	uint32_t dwShtim;
	uint32_t dwMr;

	if (dwAFEClock == 0) return;

	/* Formula for SHTIM is:
	   SHTIM = (time x AFECLK) / (1000000000) - 1
	   Since 1 billion is close to the maximum value for an integer, we first
	   divide AFECLK by 1000 to avoid an overflow */
	dwShtim = (dwNs * (dwAFEClock / 1000)) / 100000;

	if (dwShtim % 10) dwShtim /= 10;
	else {
		dwShtim /= 10;

		if (dwShtim) dwShtim --;
	}

	dwMr  = AFEC_MR_TRACKTIM(dwShtim);
	dwMr |= pAFE->AFEC_MR & ~AFEC_MR_TRACKTIM_Msk;
	pAFE->AFEC_MR = dwMr;
}

/**
 * \brief Set analog offset to be used for channel CSEL.
 *
 * \param afec  Base address of the AFEC.
 * \param dwChannel AFEC channel number.
 * \param aoffset  Analog offset value.
 */
void AFEC_SetAnalogOffset(Afec *pAFE, uint32_t dwChannel, uint32_t aoffset)
{
	assert(dwChannel < 12);
	pAFE->AFEC_CSELR = dwChannel;
	pAFE->AFEC_COCR = (aoffset & AFEC_COCR_AOFF_Msk);;
}

/**
 * \brief Set analog offset to be used for channel CSEL.
 *
 * \param afec  Base address of the AFEC.
 * \param control  Analog control value.
 */
void AFEC_SetAnalogControl(Afec *pAFE, uint32_t control)
{
	pAFE->AFEC_ACR = control;
}