sam7fc/src/at91_adc.c

239 lines
7.2 KiB
C
Raw Permalink Normal View History

2008-02-03 21:41:39 +01:00
/***************************************************************************
2008-06-23 15:33:07 +02:00
* sam7fc - ADC routines / calibration *
* *
2008-02-03 21:41:39 +01:00
* Copyright (C) 01/2008 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; version 2 of the License *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <stdint.h>
#include <stdio.h>
#include "AT91SAM7S256.h"
#include "board.h"
2008-03-10 21:37:56 +01:00
#include "at91_adc.h"
2008-02-03 21:41:39 +01:00
#include "at91_pitc.h"
#include "at91_twi.h"
2008-06-23 15:33:07 +02:00
#include "telemetrie.h"
2008-03-11 18:27:35 +01:00
2008-06-23 15:33:07 +02:00
static uint32_t adc_status;
2008-03-10 21:37:56 +01:00
static uint16_t adc_tmp[7];
static uint16_t adc_offset[6];
2008-06-23 15:33:07 +02:00
TDC_PTR(adc_offset0, &adc_offset[ADC_GYRO_NICK], "XADC_GYRO_NICK (offset)", int16_t, TDC_UNSIGNED);
TDC_PTR(adc_offset1, &adc_offset[ADC_GYRO_ROLL], "XADC_GYRO_ROLL (offset)", int16_t, TDC_UNSIGNED);
TDC_PTR(adc_offset2, &adc_offset[ADC_GYRO_GIER], "XADC_GYRO_GIER (offset)", int16_t, TDC_UNSIGNED);
TDC_PTR(adc_offset3, &adc_offset[ADC_ACC_NICK], "XADC_ACC_NICK (offset)", int16_t, TDC_UNSIGNED);
TDC_PTR(adc_offset4, &adc_offset[ADC_ACC_ROLL], "XADC_ACC_ROLL (offset)", int16_t, TDC_UNSIGNED);
TDC_PTR(adc_offset5, &adc_offset[ADC_ACC_GIER], "XADC_ACC_GIER (offset)", int16_t, TDC_UNSIGNED);
#define ADC_CAL_COUNT_MAX 1024
static uint32_t adc_cal_count;
static uint32_t adc_cal_data[3];
/* check eeprom parameter size (3x uint16_t) */
#if ((3 * 2) != EE_ACC_CAL_DATA_SIZE)
#error "invalid EE_ACC_CAL_DATA_SIZE"
#endif
2008-02-03 21:41:39 +01:00
static void at91_adc_isr(void)
{
AT91S_PDC *pdc = AT91C_BASE_PDC_ADC;
2008-03-10 21:37:56 +01:00
pdc->PDC_RPR = (uint32_t) &adc_tmp;
pdc->PDC_RCR = ARRAY_SIZE(adc_tmp);
2008-02-03 21:41:39 +01:00
pdc->PDC_PTCR = AT91C_PDC_RXTEN;
/* clear interrupts */
2008-06-23 15:33:07 +02:00
uint32_t dummy = *AT91C_ADC_SR;
dummy = dummy;
2008-02-03 21:41:39 +01:00
2008-06-23 15:33:07 +02:00
if (!(adc_status & (ADC_CAL_GYRO | ADC_CAL_ACC)))
return;
2008-03-10 21:37:56 +01:00
2008-06-23 15:33:07 +02:00
if (adc_status & ADC_CAL_GYRO) {
adc_cal_data[0] += adc_tmp[ADC_GYRO_NICK];
adc_cal_data[1] += adc_tmp[ADC_GYRO_ROLL];
adc_cal_data[2] += adc_tmp[ADC_GYRO_GIER];
2008-03-10 21:37:56 +01:00
} else {
2008-06-23 15:33:07 +02:00
adc_cal_data[0] += adc_tmp[ADC_ACC_NICK];
adc_cal_data[1] += adc_tmp[ADC_ACC_ROLL];
adc_cal_data[2] += adc_tmp[ADC_ACC_GIER];
2008-03-10 21:37:56 +01:00
}
2008-06-23 15:33:07 +02:00
adc_cal_count++;
}
2008-03-10 21:37:56 +01:00
2008-06-23 15:33:07 +02:00
static uint32_t adc_calibrate_cb(struct pitc_timer *timer)
{
if (adc_cal_count < ADC_CAL_COUNT_MAX) {
/* trigger next cycle */
*AT91C_ADC_CR = AT91C_ADC_START;
return PITC_RESTART_TIMER;
2008-03-11 22:02:06 +01:00
2008-06-23 15:33:07 +02:00
} else {
if (adc_status & ADC_CAL_GYRO) {
adc_offset[ADC_GYRO_NICK] = adc_cal_data[0] / ADC_CAL_COUNT_MAX;
adc_offset[ADC_GYRO_ROLL] = adc_cal_data[1] / ADC_CAL_COUNT_MAX;
adc_offset[ADC_GYRO_GIER] = adc_cal_data[2] / ADC_CAL_COUNT_MAX;
2008-03-11 22:02:06 +01:00
2008-06-23 15:33:07 +02:00
adc_calibrate(ADC_CAL_GYRO_COMPLETE);
2008-03-11 22:02:06 +01:00
} else {
2008-06-23 15:33:07 +02:00
adc_offset[ADC_ACC_NICK] = adc_cal_data[0] / ADC_CAL_COUNT_MAX;
adc_offset[ADC_ACC_ROLL] = adc_cal_data[1] / ADC_CAL_COUNT_MAX;
adc_offset[ADC_ACC_GIER] = adc_cal_data[2] / ADC_CAL_COUNT_MAX;
2008-03-10 21:37:56 +01:00
2008-06-23 15:33:07 +02:00
adc_calibrate(ADC_CAL_ACC_COMPLETE);
2008-03-11 22:02:06 +01:00
}
2008-03-10 21:37:56 +01:00
return PITC_REMOVE_TIMER;
}
2008-02-03 21:41:39 +01:00
}
2008-03-10 21:37:56 +01:00
static struct pitc_timer adc_cal_timer = {
.interval = 1,
.func = &adc_calibrate_cb,
2008-02-06 17:56:27 +01:00
};
2008-03-10 21:37:56 +01:00
void adc_trigger(void)
{
2008-06-23 15:33:07 +02:00
// TODO: err in retvalue?
if (!(adc_status & (ADC_CAL_GYRO | ADC_CAL_ACC)))
*AT91C_ADC_CR = AT91C_ADC_START;
}
void adc_get_results(int16_t *adc_result)
{
// TODO: err in retvalue?
if (!(adc_status & (ADC_CAL_GYRO | ADC_CAL_ACC))) {
uint32_t i;
for (i = ADC_GYRO_NICK; i <= ADC_GYRO_GIER; i++)
adc_result[i] = (int16_t)(adc_offset[i]) - (int16_t)(adc_tmp[i]);
for (i = ADC_ACC_NICK; i <= ADC_ACC_GIER; i++)
adc_result[i] = (int16_t)(adc_tmp[i]) - (int16_t)(adc_offset[i]);
2008-03-10 21:37:56 +01:00
2008-06-23 15:33:07 +02:00
/* (adc / 1024) * 3.3V * (11k / 1k) * 100 */
adc_result[ADC_VOLTAGE] = ((uint32_t)adc_tmp[ADC_VOLTAGE] * 3630) / 1024;
}
2008-03-10 21:37:56 +01:00
}
void adc_calibrate(uint32_t mode)
{
2008-06-23 15:33:07 +02:00
/* disable interrupt */
*AT91C_ADC_IDR = AT91C_ADC_ENDRX;
switch (mode) {
case ADC_CAL_GYRO_COMPLETE:
adc_status &= ~ADC_CAL_GYRO;
break;
case ADC_CAL_ACC_COMPLETE:
twi_write_eeprom(EE_ACC_CAL_DATA,
(uint8_t *)&(adc_offset[ADC_ACC_NICK]),
EE_ACC_CAL_DATA_SIZE);
adc_status &= ~ADC_CAL_ACC;
break;
case ADC_CAL_ACC_LOAD:
twi_read_eeprom(EE_ACC_CAL_DATA,
(uint8_t *)&(adc_offset[ADC_ACC_NICK]),
EE_ACC_CAL_DATA_SIZE);
break;
case ADC_CAL_GYRO:
case ADC_CAL_ACC:
/* calibration in progress.. */
if (adc_status & (ADC_CAL_GYRO | ADC_CAL_ACC))
break;
adc_status |= mode;
adc_cal_count = 0;
2008-03-10 21:37:56 +01:00
adc_cal_data[0] = 0;
adc_cal_data[1] = 0;
adc_cal_data[2] = 0;
pitc_schedule_timer(&adc_cal_timer);
2008-06-23 15:33:07 +02:00
/* trigger next cycle */
*AT91C_ADC_CR = AT91C_ADC_START;
break;
2008-03-10 21:37:56 +01:00
}
2008-06-23 15:33:07 +02:00
if (!(adc_status & (ADC_CAL_GYRO | ADC_CAL_ACC))) {
printf("ADC offsets: %d/%d/%d %d/%d/%d\n\r",
adc_offset[ADC_GYRO_NICK],
adc_offset[ADC_GYRO_ROLL],
adc_offset[ADC_GYRO_GIER],
adc_offset[ADC_ACC_NICK],
adc_offset[ADC_ACC_ROLL],
adc_offset[ADC_ACC_GIER]);
}
/* enable interrupt */
*AT91C_ADC_IER = AT91C_ADC_ENDRX;
}
void adc_drift_adjust(int16_t nick, int16_t roll, int16_t yaw)
{
adc_offset[ADC_GYRO_NICK] += nick;
adc_offset[ADC_GYRO_ROLL] += roll;
adc_offset[ADC_GYRO_GIER] += yaw;
2008-03-10 21:37:56 +01:00
}
void at91_adc_init(void)
2008-02-03 21:41:39 +01:00
{
/* enable ADC clock */
*AT91C_PMC_PCER = (1 << AT91C_ID_ADC);
/* ADC Software reset */
AT91S_ADC *adc = AT91C_BASE_ADC;
adc->ADC_CR = AT91C_ADC_SWRST;
2008-02-06 14:20:47 +01:00
/*
2008-02-03 21:41:39 +01:00
* ADC config: 10bit, no sleep
* 4.8MHz (48MHz / ((4 +1) * 2) = 4.8MHz)
2008-02-06 14:20:47 +01:00
* 96 cycles Startup ((11 +1) * 8 / 4.8MHz = 20us)
2008-02-03 21:41:39 +01:00
* 3 cycles SH ((2 +1) / 4.8MHz = 625ns)
* Conversion time per channel @5MHz ~2us
2008-02-06 14:20:47 +01:00
*/
2008-02-03 21:41:39 +01:00
adc->ADC_MR = AT91C_ADC_TRGEN_DIS |
AT91C_ADC_LOWRES_10_BIT |
AT91C_ADC_SLEEP_NORMAL_MODE |
(AT91C_ADC_PRESCAL & (4 << 8)) |
(AT91C_ADC_STARTUP & (11 << 16)) |
(AT91C_ADC_SHTIM & (2 << 24));
2008-02-06 14:20:47 +01:00
2008-02-03 21:41:39 +01:00
/* setup PDC */
AT91S_PDC *pdc = AT91C_BASE_PDC_ADC;
2008-03-10 21:37:56 +01:00
pdc->PDC_RPR = (uint32_t) &adc_tmp;
pdc->PDC_RCR = ARRAY_SIZE(adc_tmp);
2008-02-03 21:41:39 +01:00
pdc->PDC_PTCR = AT91C_PDC_RXTEN;
2008-03-10 21:37:56 +01:00
/* enable 7 channels (0-1-2-4-5-6-7), PDC Interrupt */
2008-02-29 19:50:08 +01:00
adc->ADC_CHER = 0xF7;
2008-02-03 21:41:39 +01:00
adc->ADC_IER = AT91C_ADC_ENDRX;
/* low priority, level triggered, own vector */
AT91S_AIC *aic = AT91C_BASE_AIC;
aic->AIC_SMR[AT91C_ID_ADC] = IRQPRIO_ADC | AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL;
aic->AIC_SVR[AT91C_ID_ADC] = (uint32_t)at91_adc_isr;
aic->AIC_IECR = (1<<AT91C_ID_ADC);
2008-06-23 15:33:07 +02:00
adc_calibrate(ADC_CAL_ACC_LOAD);
2008-03-11 23:26:56 +01:00
adc_calibrate(ADC_CAL_GYRO);
2008-02-03 21:41:39 +01:00
}