aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gadc/AT91SAM7/gadc_lld.c
blob: 52b065392a6fd5b4f0f6e16e3379174f63c9ebdb (plain)
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
/*
 * This file is subject to the terms of the GFX License. If a copy of
 * the license was not distributed with this file, you can obtain one at:
 *
 *              http://ugfx.org/license.html
 */

/**
 * @file    drivers/gadc/AT91SAM7/gadc_lld.c
 * @brief   GADC - Periodic ADC driver source file for the AT91SAM7 cpu.
 */

#include "gfx.h"

#if GFX_USE_GADC

#include "src/gadc/driver.h"

static GDataBuffer	*pData;
static size_t		bytesperconversion;

static void ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n);
static void ISR_ErrorI(ADCDriver *adcp, adcerror_t err);


static ADCConversionGroup acg = {
		FALSE,					// circular
		1,						// num_channels
		ISR_CompleteI,			// end_cb
		ISR_ErrorI,				// error_cb
		0,						// channelselects
		0,						// trigger
		0,						// frequency
		};

static void ISR_CompleteI(ADCDriver *adcp, adcsample_t *buffer, size_t n) {
	(void)	adcp;
	(void)	buffer;

	if (pData) {
		// A set of timer base conversions is complete
		pData->len += n * bytesperconversion;

		// Are we finished yet?
		// In ChibiOS we (may) get a half-buffer complete. In this situation the conversions
		//	are really not complete and so we just wait for the next lot of data.
		if (pData->len + bytesperconversion > pData->size)
			gadcDataReadyI();

	} else {
		// A single non-timer conversion is complete
		gadcDataReadyI();
	}
}

static void ISR_ErrorI(ADCDriver *adcp, adcerror_t err) {
	(void)	adcp;
	(void)	err;

	gadcDataFailI();
}

void gadc_lld_init(void) {
	adcStart(&ADCD1, 0);
}

void gadc_lld_start_timer(GadcLldTimerData *pgtd) {
	int			phys;

	/* Calculate the bytes per conversion from physdev */
	/* The AT91SAM7 has AD0..7 - physdev is a bitmap of those channels */
	phys = pgtd->physdev;
	for(bytesperconversion = 0; phys; phys >>= 1)
		if (phys & 0x01)
			bytesperconversion++;
	bytesperconversion *= (gfxSampleFormatBits(GADC_SAMPLE_FORMAT)+7)/8;

	/**
	 * The AT91SAM7 ADC driver supports triggering the ADC using a timer without having to implement
	 * an interrupt handler for the timer. The driver also initialises the timer correctly for us.
	 * Because we aren't trapping the interrupt ourselves we can't increment GADC_Timer_Missed if an
	 * interrupt is missed.
	 */
	acg.frequency = pgtd->frequency;
}

void gadc_lld_stop_timer(GadcLldTimerData *pgtd) {
	(void) pgtd;
	if ((acg.trigger & ~ADC_TRIGGER_SOFTWARE) == ADC_TRIGGER_TIMER)
		adcStop(&ADCD1);
}

void gadc_lld_adc_timerI(GadcLldTimerData *pgtd) {
	/**
	 *  We don't need to calculate num_channels because the AT91SAM7 ADC does this for us.
	 */
	acg.channelselects = pgtd->physdev;
	acg.trigger = pgtd->now ? (ADC_TRIGGER_TIMER|ADC_TRIGGER_SOFTWARE) : ADC_TRIGGER_TIMER;

	pData = pgtd->pdata;
	adcStartConversionI(&ADCD1, &acg, (adcsample_t *)(pgtd->pdata+1), pData->size/bytesperconversion);

	/* Next time assume the same (still running) timer */
	acg.frequency = 0;
}

void gadc_lld_adc_nontimerI(GadcLldNonTimerData *pgntd) {
	/**
	 *  We don't need to calculate num_channels because the AT91SAM7 ADC does this for us.
	 */
	acg.channelselects = pgntd->physdev;
	acg.trigger = ADC_TRIGGER_SOFTWARE;

	pData = 0;
	adcStartConversionI(&ADCD1, &acg, pgntd->buffer, 1);
}

#endif /* GFX_USE_GADC */