aboutsummaryrefslogtreecommitdiff
path: root/src/peripherals/timer.spec.ts
diff options
context:
space:
mode:
authorUri Shaked2020-05-25 16:20:24 +0300
committerGitHub2020-05-25 16:20:24 +0300
commit00cea56ab3d5f211aa738195de66139bd1a74f56 (patch)
tree6ef5b22ca31863341b9d70c81234e56a7a87dbb9 /src/peripherals/timer.spec.ts
parentdocs: walkthrough video link (diff)
downloadavr8js-00cea56ab3d5f211aa738195de66139bd1a74f56.tar.gz
avr8js-00cea56ab3d5f211aa738195de66139bd1a74f56.tar.bz2
avr8js-00cea56ab3d5f211aa738195de66139bd1a74f56.zip
feat(timer): Compare Match Output (#45)
The Compare Match Output bits are used to generate hardware PWM signals on selected MCU pins. This is also the mechanism used by Arduino's analogWrite() method. See #32 for more details
Diffstat (limited to 'src/peripherals/timer.spec.ts')
-rw-r--r--src/peripherals/timer.spec.ts158
1 files changed, 144 insertions, 14 deletions
diff --git a/src/peripherals/timer.spec.ts b/src/peripherals/timer.spec.ts
index 8733f73..f4b0c69 100644
--- a/src/peripherals/timer.spec.ts
+++ b/src/peripherals/timer.spec.ts
@@ -2,6 +2,7 @@ import { CPU } from '../cpu/cpu';
import { avrInstruction } from '../cpu/instruction';
import { assemble } from '../utils/assembler';
import { AVRTimer, timer0Config, timer1Config, timer2Config } from './timer';
+import { PinOverrideMode } from './gpio';
describe('timer', () => {
let cpu: CPU;
@@ -57,20 +58,6 @@ describe('timer', () => {
expect(cpu.data[0x35]).toEqual(1); // TOV bit in TIFR
});
- it('should set TOV if timer overflows in PWM Phase Correct mode', () => {
- const timer = new AVRTimer(cpu, timer0Config);
- cpu.writeData(0x46, 0xff); // TCNT0 <- 0xff
- timer.tick();
- cpu.writeData(0x47, 0x7f); // OCRA <- 0x7f
- cpu.writeData(0x44, 0x1); // WGM0 <- 1 (PWM, Phase Correct)
- cpu.data[0x45] = 0x1; // TCCR0B.CS <- 1
- cpu.cycles = 1;
- timer.tick();
- const tcnt = cpu.readData(0x46);
- expect(tcnt).toEqual(0); // TCNT should be 0
- expect(cpu.data[0x35]).toEqual(1); // TOV bit in TIFR
- });
-
it('should set TOV if timer overflows in FAST PWM mode', () => {
const timer = new AVRTimer(cpu, timer0Config);
cpu.writeData(0x46, 0xff); // TCNT0 <- 0xff
@@ -273,6 +260,108 @@ describe('timer', () => {
expect(cpu.data[1]).toEqual(2); // r1 should equal 2
});
+ describe('Phase-correct PWM mode', () => {
+ it('should count up to TOP, down to 0, and then set TOV flag', () => {
+ const program = [
+ // Set waveform generation mode (WGM) to PWM, Phase Correct, top OCR0A
+ 'LDI r16, 0x1', // TCCR0A = 1 << WGM00;
+ 'OUT 0x24, r16',
+ 'LDI r16, 0x9', // TCCR0B = (1 << WGM02) | (1 << CS00);
+ 'OUT 0x25, r16',
+ 'LDI r16, 0x3', // OCR0A = 0x3;
+ 'OUT 0x27, r16',
+ 'LDI r16, 0x2', // TCNT0 = 0x2;
+ 'OUT 0x26, r16',
+ ];
+ const nops = [
+ 'NOP', // TCNT0 will be 3
+ 'NOP', // TCNT0 will be 2
+ 'NOP', // TCNT0 will be 1
+ 'NOP', // TCNT0 will be 0
+ 'NOP', // TCNT0 will be 1 (end of test)
+ ];
+ loadProgram(...program, ...nops);
+ const timer = new AVRTimer(cpu, timer0Config);
+
+ for (let i = 0; i < program.length; i++) {
+ avrInstruction(cpu);
+ timer.tick();
+ }
+ expect(cpu.readData(0x46)).toEqual(2); // TCNT should be 2
+
+ avrInstruction(cpu);
+ timer.tick();
+ expect(cpu.readData(0x46)).toEqual(3); // TCNT should be 3
+
+ avrInstruction(cpu);
+ timer.tick();
+ expect(cpu.readData(0x46)).toEqual(2); // TCNT should be 2
+
+ avrInstruction(cpu);
+ timer.tick();
+ expect(cpu.readData(0x46)).toEqual(1); // TCNT should be 1
+ expect(cpu.data[0x35] & 0x1).toEqual(0); // TIFR should have TOV bit clear
+
+ avrInstruction(cpu);
+ timer.tick();
+ expect(cpu.readData(0x46)).toEqual(0); // TCNT should be 0
+ expect(cpu.data[0x35] & 0x1).toEqual(1); // TIFR should have TOV bit set
+
+ avrInstruction(cpu);
+ timer.tick();
+ expect(cpu.readData(0x46)).toEqual(1); // TCNT should be 1
+ });
+
+ it('should clear OC0A when TCNT0=OCR0A and counting up', () => {
+ const program = [
+ // Set waveform generation mode (WGM) to PWM, Phase Correct
+ 'LDI r16, 0x81', // TCCR0A = (1 << COM0A1) || (1 << WGM01);
+ 'OUT 0x24, r16',
+ 'LDI r16, 0x1', // TCCR0B = (1 << CS00);
+ 'OUT 0x25, r16',
+ 'LDI r16, 0xfe', // OCR0A = 0xfe;
+ 'OUT 0x27, r16',
+ 'LDI r16, 0xfd', // TCNT0 = 0xfd;
+ 'OUT 0x26, r16',
+ ];
+ const nops = [
+ 'NOP', // TCNT0 will be 0xfe
+ 'NOP', // TCNT0 will be 0xff
+ 'NOP', // TCNT0 will be 0xfe again (end of test)
+ ];
+ loadProgram(...program, ...nops);
+ const timer = new AVRTimer(cpu, timer0Config);
+
+ // Listen to Port D's internal callback
+ const gpioCallback = jest.fn();
+ cpu.gpioTimerHooks[0x2b] = gpioCallback;
+
+ for (let i = 0; i < program.length; i++) {
+ avrInstruction(cpu);
+ timer.tick();
+ }
+ expect(cpu.readData(0x46)).toEqual(0xfd); // TCNT0 should be 0xfd
+ expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Enable, 0x2b);
+ gpioCallback.mockClear();
+
+ avrInstruction(cpu);
+ timer.tick();
+ expect(cpu.readData(0x46)).toEqual(0xfe); // TCNT should be 0xfe
+ expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Clear, 0x2b);
+ gpioCallback.mockClear();
+
+ avrInstruction(cpu);
+ timer.tick();
+ expect(cpu.readData(0x46)).toEqual(0xff); // TCNT should be 0xff
+ expect(gpioCallback).not.toHaveBeenCalled();
+
+ avrInstruction(cpu);
+ timer.tick();
+ expect(cpu.readData(0x46)).toEqual(0xfe); // TCNT should be 0xfe
+ expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Set, 0x2b);
+ });
+ });
+
describe('16 bit timers', () => {
it('should increment 16-bit TCNT by 1', () => {
const timer = new AVRTimer(cpu, timer1Config);
@@ -365,5 +454,46 @@ describe('timer', () => {
const timerLow = cpu.readData(0x84);
expect((timerHigh << 8) | timerLow).toEqual(0xff00);
});
+
+ it('should toggle OC1B on Compare Match', () => {
+ const program = [
+ // Set waveform generation mode (WGM) to Normal, top 0xFFFF
+ 'LDI r16, 0x10', // TCCR1A = (1 << COM1B0);
+ 'STS 0x80, r16',
+ 'LDI r16, 0x1', // TCCR1B = (1 << CS00);
+ 'STS 0x81, r16',
+ 'LDI r16, 0x0', // OCR1BH = 0x0;
+ 'STS 0x8B, r16',
+ 'LDI r16, 0x4a', // OCR1BL = 0x4a;
+ 'STS 0x8A, r16',
+ 'LDI r16, 0x0', // TCNT1H = 0x0;
+ 'STS 0x85, r16',
+ 'LDI r16, 0x49', // TCNT1L = 0x49;
+ 'STS 0x84, r16',
+ ];
+ const nops = [
+ 'NOP', // TCNT1 will be 0x49
+ 'NOP', // TCNT1 will be 0x4a
+ ];
+ loadProgram(...program, ...nops);
+ const timer = new AVRTimer(cpu, timer1Config);
+
+ // Listen to Port B's internal callback
+ const gpioCallback = jest.fn();
+ cpu.gpioTimerHooks[0x25] = gpioCallback;
+
+ for (let i = 0; i < program.length; i++) {
+ avrInstruction(cpu);
+ timer.tick();
+ }
+ expect(cpu.readData(0x84)).toEqual(0x49); // TCNT1 should be 0x49
+ expect(gpioCallback).toHaveBeenCalledWith(2, PinOverrideMode.Enable, 0x25);
+ gpioCallback.mockClear();
+
+ avrInstruction(cpu);
+ timer.tick();
+ expect(cpu.readData(0x84)).toEqual(0x4a); // TCNT1 should be 0x4a
+ expect(gpioCallback).toHaveBeenCalledWith(2, PinOverrideMode.Toggle, 0x25);
+ });
});
});