summaryrefslogtreecommitdiff
path: root/firmware/target/sh/adc-sh.c
blob: 2e6d6407b8d5aa340c3adb12e9a32e7e625c93bc (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
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2002 by Linus Nielsen Feltzing
 *
 * 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; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include "config.h"
#include "cpu.h"
#include "system.h"
#include "kernel.h"
#include "thread.h"
#include "string.h"
#include "adc.h"

/**************************************************************************
 ** The A/D conversion is done every tick, in three steps:
 **
 ** 1) On the tick interrupt, the conversion of channels 0-3 is started, and
 **    the A/D interrupt is enabled.
 **
 ** 2) After the conversion is done (approx. 256*4 cycles later), an interrupt
 **    is generated at level 1, which is the same level as the tick interrupt
 **    itself. This interrupt will be pending until the tick interrupt is
 **    finished.
 **    When the A/D interrupt is finally served, it will read the results
 **    from the first conversion and start the conversion of channels 4-7.
 **
 ** 3) When the conversion of channels 4-7 is finished, the interrupt is
 **    triggered again, and the results are read. This time, no new
 **    conversion is started, it will be done in the next tick interrupt.
 **
 ** Thus, each channel will be updated HZ times per second.
 **
 *************************************************************************/

static int current_channel;
static unsigned short adcdata[NUM_ADC_CHANNELS];

static void adc_tick(void)
{
    /* Start a conversion of channel group 0. This will trigger an interrupt,
       and the interrupt handler will take care of group 1. */

    current_channel = 0;
    ADCSR = ADCSR_ADST | ADCSR_ADIE | ADCSR_SCAN | 3;
}

void ADITI(void) __attribute__((interrupt_handler));
void ADITI(void)
{
    if(ADCSR & ADCSR_ADF)
    {
        ADCSR = 0;

        if(current_channel == 0)
        {
            adcdata[0] = ADDRA >> 6;
            adcdata[1] = ADDRB >> 6;
            adcdata[2] = ADDRC >> 6;
            adcdata[3] = ADDRD >> 6;
            current_channel = 4;
            
            /* Convert the next group */
            ADCSR = ADCSR_ADST | ADCSR_ADIE | ADCSR_SCAN | 7;
        }
        else
        {
            adcdata[4] = ADDRA >> 6;
            adcdata[5] = ADDRB >> 6;
            adcdata[6] = ADDRC >> 6;
            adcdata[7] = ADDRD >> 6;
        }
    }
}

unsigned short adc_read(int channel)
{
    return adcdata[channel];
}

void adc_init(void)
{
    ADCR  = 0x7f; /* No external trigger; other bits should be 1 according
                     to the manual... */

    ADCSR = 0;
    
    current_channel = 0;

    /* Enable the A/D IRQ on level 1 */
    IPRE = (IPRE & 0xf0ff) | 0x0100;
    
    tick_add_task(adc_tick);
    
    sleep(2);    /* Ensure valid readings when adc_init returns */
}