Timer8 now has a variable amount of output compare units.
[avr-sim.git] / src / ADC.cpp
blob1abac3a3884124002117c64407d65026c515f496
1 /*
2 * avr-sim: An atmel AVR simulator
3 * Copyright (C) 2008 Tom Haber
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "ADC.h"
20 #include "Bus.h"
21 #include "Registers.h"
23 #define INIT_CYCLES ((int)(13.5*2))
24 #define RUN_CYCLES ((int)(1.5*2))
25 #define CONV_CYCLES ((int)(13*2) - RUN_CYCLES)
26 #define INT_CYCLES CONV_CYCLES
28 enum {
29 MUX0 = 1<<0,
30 MUX1 = 1<<1,
31 MUX2 = 1<<2,
32 MUX3 = 1<<3,
33 MUX4 = 1<<4,
34 ADLAR = 1<<5,
35 REFS0 = 1<<6,
36 REFS1 = 1<<7
39 enum {
40 ADPS0 = 1<<0, /* Clock Rate Select 0 */
41 ADPS1 = 1<<1, /* Clock Rate Select 1 */
42 ADPS2 = 1<<2, /* Clock Rate Select 1 */
43 ADIE = 1<<3, /* ADC Interrupt Enable */
44 ADIF = 1<<4, /* ADC Interrupt Flag */
45 ADFR = 1<<5, /* Free Run Select */
46 ADSC = 1<<6, /* ADC Start Conversion */
47 ADEN = 1<<7, /* ADC Enable */
50 namespace avr {
52 ADC::ADC(Bus & bus, unsigned int ccVec) : Hardware(bus), ccVec(ccVec) {
53 bus.claimInterrupt(ccVec, this);
56 ADC::~ADC() {
59 void ADC::reset() {
60 adcsr_old = *adcsr;
61 prescaler = 0;
62 clk = 0;
63 sample = 0.0f;
66 bool ADC::attachReg(const char *name, IORegister *reg) {
67 if( strcmp(name, "admux") == 0 )
68 admux = reg;
69 else if( strcmp(name, "adcsr") == 0 ) {
70 adcsr = reg;
71 } else if( strcmp(name, "adch") == 0 )
72 adch = reg;
73 else if( strcmp(name, "adcl") == 0 )
74 adcl = reg;
75 else
76 return false;
78 reg->registerHW(this);
79 return true;
82 bool ADC::finishBuild() {
83 return ((admux != 0) && (adcsr != 0) && (adch != 0) && (adcl != 0) );
86 void ADC::setAdcsr(unsigned char val) {
87 unsigned char old = adcsr_old & (ADIF|ADSC);
88 if( (val & ADIF) != 0 ) // Clear interrupt flag if written.
89 old &= ~ADIF;
91 val = old | (val & ~ADIF);
93 // Check interrupt
94 if( (val & (ADIE|ADIF)) == (ADIE|ADIF) )
95 bus.raiseInterrupt(ccVec);
96 else
97 bus.clearInterrupt(ccVec);
99 // We trigger the adc at twice the speed to allow
100 // starting with sampling at half a cycle.
101 switch ( val & ( ADPS2|ADPS1|ADPS0)) {
102 case 0:
103 case ADPS0:
104 prescaler = 1;
105 break;
107 case ADPS1:
108 prescaler = 2;
109 break;
111 case (ADPS1|ADPS0):
112 prescaler = 4;
113 break;
115 case (ADPS2):
116 prescaler = 8;
117 break;
119 case (ADPS2|ADPS0):
120 prescaler = 16;
121 break;
123 case (ADPS2|ADPS1):
124 prescaler = 32;
125 break;
127 case (ADPS2|ADPS1|ADPS0):
128 prescaler = 64;
129 break;
132 if( ((val & ADSC) != 0) && ((adcsr_old & ADSC) == 0) ) {
133 bus.setBreakDelta( INIT_CYCLES * prescaler, this );
134 clk = 0;
135 sample = 0.0f;
138 adcsr->set( val );
139 adcsr_old = val;
142 float ADC::refVoltage() const {
143 switch( *admux & (REFS0|REFS1) ) {
144 case 0:
145 return 5.0f; // aref voltage
147 case REFS0:
148 return 5.0f; // avcc
150 case REFS1|REFS0:
151 return 2.56f;
153 default:
154 return 0.0f;
158 void ADC::regChanged( IORegister *reg ) {
159 if( reg == admux )
161 else if( reg == adcsr )
162 setAdcsr( *adcsr );
165 void ADC::step() {
166 if( clk++ == CONV_CYCLES ) {
167 // TODO gain, differential
168 unsigned int adc = (unsigned int)((sample / INT_CYCLES) * (1<<10));
169 // TODO adlar register locking
170 adch->set( adc >> 8 );
171 adcl->set( adc & 0xff );
173 adcsr_old |= ADIF;
175 if( (adcsr_old & (ADIE|ADIF)) == (ADIE|ADIF) )
176 bus.raiseInterrupt(ccVec);
178 if( (adcsr_old & ADFR) != 0 ) {
179 // Start again if free running mode
180 clk = 0;
181 sample = 0.0f;
182 bus.setBreakDelta( RUN_CYCLES*prescaler, this );
183 } else {
184 // Clear start conversion bit
185 adcsr_old &= ~ADSC;
188 adcsr->set( adcsr_old );
189 } else {
190 int chan = *admux & (MUX2|MUX1|MUX0);
191 float V = 0.0f; //adpin[chan].voltage();
192 float Vref = refVoltage();
194 if( V > Vref ) V = Vref;
195 sample += (V / Vref);
197 bus.setBreakDelta( prescaler, this );
201 void ADC::beforeInvokeInterrupt(unsigned int vector) {
202 if( vector == ccVec )
203 adcsr->set( *adcsr & ~ADIF );