aboutsummaryrefslogtreecommitdiff
path: root/src/peripherals/adc.spec.ts
blob: c67b615853b13e5a9ca640f804e6198d83e4cf6d (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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import { describe, expect, it, vi } from 'vitest';
import { CPU } from '../cpu/cpu';
import { asmProgram, TestProgramRunner } from '../utils/test-utils';
import { adcConfig, ADCMuxInputType, AVRADC } from './adc';

const R16 = 16;
const R17 = 17;

const ADMUX = 0x7c;
const REFS0 = 1 << 6;

const ADCSRA = 0x7a;
const ADEN = 1 << 7;
const ADSC = 1 << 6;
const ADPS0 = 1 << 0;
const ADPS1 = 1 << 1;
const ADPS2 = 1 << 2;

const ADCH = 0x79;
const ADCL = 0x78;

describe('ADC', () => {
  it('should successfuly perform an ADC conversion', () => {
    const { program } = asmProgram(`
    ; register addresses
    _REPLACE ADMUX, ${ADMUX}
    _REPLACE ADCSRA, ${ADCSRA}
    _REPLACE ADCH, ${ADCH}
    _REPLACE ADCL, ${ADCL}

    ; Configure mux - channel 0, reference: AVCC with external capacitor at AREF pin
    ldi r24, ${REFS0}
    sts ADMUX, r24

    ; Start conversion with 128 prescaler
    ldi r24, ${ADEN | ADSC | ADPS0 | ADPS1 | ADPS2}
    sts ADCSRA, r24

    ; Wait until conversion is complete
  waitComplete:
    lds r24, ${ADCSRA}
    andi r24, ${ADSC}
    brne waitComplete

    ; Read the result
    lds r16, ${ADCL}
    lds r17, ${ADCH}

    break
  `);
    const cpu = new CPU(program);
    const adc = new AVRADC(cpu, adcConfig);
    const runner = new TestProgramRunner(cpu);

    const adcReadSpy = vi.spyOn(adc, 'onADCRead');
    adc.channelValues[0] = 2.56; // should result in 2.56/5*1024 = 524

    // Setup
    runner.runInstructions(4);
    expect(adcReadSpy).toHaveBeenCalledWith({ channel: 0, type: ADCMuxInputType.SingleEnded });

    // Run the "waitComplete" loop for a few cycles
    runner.runInstructions(12);

    cpu.cycles += 128 * 25; // skip to the end of the conversion
    cpu.tick();

    // Now read the result
    runner.runInstructions(5);

    const low = cpu.data[R16];
    const high = cpu.data[R17];
    expect((high << 8) | low).toEqual(524); // 2.56 volts - see above
  });

  it('should read 0 when the ADC peripheral is not enabled', () => {
    // This behavior was verified on real hardware, using the following test program:
    // https://wokwi.com/arduino/projects/309156042450666050
    // Thanks Oscar Oomens for spotting this!

    const { program } = asmProgram(`
    ; register addresses
    _REPLACE ADMUX, ${ADMUX}
    _REPLACE ADCSRA, ${ADCSRA}
    _REPLACE ADCH, ${ADCH}
    _REPLACE ADCL, ${ADCL}

    ; Load some initial value into r16/r17 to make sure we actually read 0 later
    ldi r16, 0xff
    ldi r17, 0xff

    ; Configure mux - channel 0, reference: AVCC with external capacitor at AREF pin
    ldi r24, ${REFS0}
    sts ADMUX, r24

    ; Start conversion with 128 prescaler, but without enabling the ADC
    ldi r24, ${ADSC | ADPS0 | ADPS1 | ADPS2}
    sts ADCSRA, r24

    ; Wait until conversion is complete
  waitComplete:
    lds r24, ${ADCSRA}
    andi r24, ${ADSC}
    brne waitComplete

    ; Read the result
    lds r16, ${ADCL}
    lds r17, ${ADCH}

    break
  `);
    const cpu = new CPU(program);
    const adc = new AVRADC(cpu, adcConfig);
    const runner = new TestProgramRunner(cpu, () => {
      /* do nothing on break */
    });

    const adcReadSpy = vi.spyOn(adc, 'onADCRead');
    adc.channelValues[0] = 2.56; // should result in 2.56/5*1024 = 524

    // Setup
    runner.runInstructions(6);
    expect(adcReadSpy).not.toHaveBeenCalled();

    // Run the "waitComplete" loop for a few cycles
    runner.runInstructions(12);

    cpu.cycles += 128 * 25; // skip to the end of the conversion
    cpu.tick();

    // Now read the result
    runner.runToBreak();

    const low = cpu.data[R16];
    const high = cpu.data[R17];
    expect((high << 8) | low).toEqual(0); // We should read 0 since the ADC hasn't been enabled
  });
});