diff options
| author | Uri Shaked | 2021-08-15 20:27:41 +0300 |
|---|---|---|
| committer | Uri Shaked | 2021-08-15 20:27:41 +0300 |
| commit | eff7111c3b08e8bc9030edc284074dfa2e9b7171 (patch) | |
| tree | 4909fdec5c6602c1da192c0ddd77a801890e941a | |
| parent | chore(deps): prettier 2.3.2 (diff) | |
| download | avr8js-eff7111c3b08e8bc9030edc284074dfa2e9b7171.tar.gz avr8js-eff7111c3b08e8bc9030edc284074dfa2e9b7171.tar.bz2 avr8js-eff7111c3b08e8bc9030edc284074dfa2e9b7171.zip | |
feat(timer): external timer support #97
also refactor timer/GPIO interaction to be more generic.
close #97
| -rw-r--r-- | src/cpu/cpu.ts | 3 | ||||
| -rw-r--r-- | src/peripherals/gpio.spec.ts | 4 | ||||
| -rw-r--r-- | src/peripherals/gpio.ts | 72 | ||||
| -rw-r--r-- | src/peripherals/timer.spec.ts | 121 | ||||
| -rw-r--r-- | src/peripherals/timer.ts | 66 |
5 files changed, 171 insertions, 95 deletions
diff --git a/src/cpu/cpu.ts b/src/cpu/cpu.ts index 3cb4846..096d9eb 100644 --- a/src/cpu/cpu.ts +++ b/src/cpu/cpu.ts @@ -77,9 +77,8 @@ export class CPU implements ICPU { private readonly clockEventPool: AVRClockEventEntry[] = []; // helps avoid garbage collection readonly pc22Bits = this.progBytes.length > 0x20000; - // This lets the Timer Compare output override GPIO pins: - readonly gpioTimerHooks: CPUMemoryHooks = []; readonly gpioPorts = new Set<AVRIOPort>(); + readonly gpioByPort: AVRIOPort[] = []; pc: u32 = 0; cycles: u32 = 0; diff --git a/src/peripherals/gpio.spec.ts b/src/peripherals/gpio.spec.ts index 31963a9..ed44264 100644 --- a/src/peripherals/gpio.spec.ts +++ b/src/peripherals/gpio.spec.ts @@ -80,10 +80,10 @@ describe('GPIO', () => { const cpu = new CPU(new Uint16Array(1024)); const port = new AVRIOPort(cpu, portBConfig); cpu.writeData(DDRB, 1 << 1); - cpu.gpioTimerHooks[PORTB](1, PinOverrideMode.Set, PORTB); + port.timerOverridePin(1, PinOverrideMode.Set); expect(port.pinState(1)).toBe(PinState.High); expect(cpu.data[PINB]).toBe(1 << 1); - cpu.gpioTimerHooks[PORTB](1, PinOverrideMode.Clear, PORTB); + port.timerOverridePin(1, PinOverrideMode.Clear); expect(port.pinState(1)).toBe(PinState.Low); expect(cpu.data[PINB]).toBe(0); }); diff --git a/src/peripherals/gpio.ts b/src/peripherals/gpio.ts index bf5e53c..6d1dae8 100644 --- a/src/peripherals/gpio.ts +++ b/src/peripherals/gpio.ts @@ -87,6 +87,7 @@ export const PCINT2 = { }; export type GPIOListener = (value: u8, oldValue: u8) => void; +export type ExternalClockListener = (pinValue: boolean) => void; export const portAConfig: AVRPortConfig = { PIN: 0x20, @@ -198,6 +199,8 @@ enum InterruptMode { } export class AVRIOPort { + readonly externalClockListeners: (ExternalClockListener | null)[] = []; + private readonly externalInts: (AVRInterruptConfig | null)[]; private readonly PCINT: AVRInterruptConfig | null; private listeners: GPIOListener[] = []; @@ -208,8 +211,10 @@ export class AVRIOPort { private lastDdr: u8 = 0; private lastPin: u8 = 0; - constructor(private cpu: CPU, private portConfig: AVRPortConfig) { + constructor(private cpu: CPU, readonly portConfig: Readonly<AVRPortConfig>) { cpu.gpioPorts.add(this); + cpu.gpioByPort[portConfig.PORT] = this; + cpu.writeHooks[portConfig.DDR] = (value: u8) => { const portValue = cpu.data[portConfig.PORT]; cpu.data[portConfig.DDR] = value; @@ -234,34 +239,6 @@ export class AVRIOPort { this.updatePinRegister(ddrMask); return true; }; - // The following hook is used by the timer compare output to override GPIO pins: - cpu.gpioTimerHooks[portConfig.PORT] = (pin: u8, mode: PinOverrideMode) => { - const pinMask = 1 << pin; - if (mode === PinOverrideMode.None) { - this.overrideMask |= pinMask; - this.overrideValue &= ~pinMask; - } else { - this.overrideMask &= ~pinMask; - switch (mode) { - case PinOverrideMode.Enable: - this.overrideValue &= ~pinMask; - this.overrideValue |= cpu.data[portConfig.PORT] & pinMask; - break; - case PinOverrideMode.Set: - this.overrideValue |= pinMask; - break; - case PinOverrideMode.Clear: - this.overrideValue &= ~pinMask; - break; - case PinOverrideMode.Toggle: - this.overrideValue ^= pinMask; - break; - } - } - const ddrMask = cpu.data[portConfig.DDR]; - this.writeGpio(cpu.data[portConfig.PORT], ddrMask); - this.updatePinRegister(ddrMask); - }; // External interrupts const { externalInterrupts } = portConfig; @@ -360,13 +337,48 @@ export class AVRIOPort { this.updatePinRegister(this.cpu.data[this.portConfig.DDR]); } + /** + * Internal method - do not call this directly! + * Used by the timer compare output units to override GPIO pins. + */ + timerOverridePin(pin: u8, mode: PinOverrideMode) { + const { cpu, portConfig } = this; + const pinMask = 1 << pin; + if (mode === PinOverrideMode.None) { + this.overrideMask |= pinMask; + this.overrideValue &= ~pinMask; + } else { + this.overrideMask &= ~pinMask; + switch (mode) { + case PinOverrideMode.Enable: + this.overrideValue &= ~pinMask; + this.overrideValue |= cpu.data[portConfig.PORT] & pinMask; + break; + case PinOverrideMode.Set: + this.overrideValue |= pinMask; + break; + case PinOverrideMode.Clear: + this.overrideValue &= ~pinMask; + break; + case PinOverrideMode.Toggle: + this.overrideValue ^= pinMask; + break; + } + } + const ddrMask = cpu.data[portConfig.DDR]; + this.writeGpio(cpu.data[portConfig.PORT], ddrMask); + this.updatePinRegister(ddrMask); + } + private updatePinRegister(ddr: u8) { const newPin = (this.pinValue & ~ddr) | (this.lastValue & ddr); this.cpu.data[this.portConfig.PIN] = newPin; if (this.lastPin !== newPin) { for (let index = 0; index < 8; index++) { if ((newPin & (1 << index)) !== (this.lastPin & (1 << index))) { - this.toggleInterrupt(index, !!(newPin & (1 << index))); + const value = !!(newPin & (1 << index)); + this.toggleInterrupt(index, value); + this.externalClockListeners[index]?.(value); } } this.lastPin = newPin; diff --git a/src/peripherals/timer.spec.ts b/src/peripherals/timer.spec.ts index a15672c..d89fba2 100644 --- a/src/peripherals/timer.spec.ts +++ b/src/peripherals/timer.spec.ts @@ -1,6 +1,6 @@ import { CPU } from '../cpu/cpu'; import { asmProgram, TestProgramRunner } from '../utils/test-utils'; -import { PinOverrideMode } from './gpio'; +import { AVRIOPort, PinOverrideMode, portBConfig, portDConfig } from './gpio'; import { AVRTimer, timer0Config, timer1Config, timer2Config } from './timer'; // CPU registers @@ -13,10 +13,6 @@ const R21 = 21; const R22 = 22; const SREG = 95; -// Port Registers -const PORTB = 0x25; -const PORTD = 0x2b; - // Timer 0 Registers const TIFR0 = 0x35; const TCCR0A = 0x44; @@ -61,10 +57,13 @@ const WGM12 = 8; const WGM13 = 16; const CS00 = 1; const CS01 = 2; +const CS02 = 4; const CS10 = 1; const CS21 = 2; const CS22 = 4; +const T0 = 4; // PD4 on ATmega328p + // opcodes const nopOpCode = '0000'; @@ -558,21 +557,21 @@ describe('timer', () => { new AVRTimer(cpu, timer0Config); // Listen to Port D's internal callback - const gpioCallback = jest.fn(); - cpu.gpioTimerHooks[PORTD] = gpioCallback; + const portD = new AVRIOPort(cpu, portDConfig); + const gpioCallback = jest.spyOn(portD, 'timerOverridePin'); const runner = new TestProgramRunner(cpu); runner.runToAddress(labels.beforeMatch); expect(cpu.readData(TCNT0)).toEqual(0xfd); expect(gpioCallback).toHaveBeenCalledTimes(1); - expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Enable, 0x2b); // OC0A: Enable + expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Enable); // OC0A: Enable gpioCallback.mockClear(); runner.runToAddress(labels.afterMatch); expect(cpu.readData(TCNT0)).toEqual(0xfe); expect(gpioCallback).toHaveBeenCalledTimes(1); - expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Set, 0x2b); // OC0A: Set + expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Set); // OC0A: Set gpioCallback.mockClear(); runner.runToAddress(labels.beforeBottom); @@ -583,7 +582,7 @@ describe('timer', () => { runner.runToAddress(labels.afterBottom); expect(cpu.readData(TCNT0)).toEqual(0x0); expect(gpioCallback).toHaveBeenCalledTimes(1); - expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Clear, 0x2b); // OC0A: Clear + expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Clear); // OC0A: Clear }); it('should toggle OC0A on Compare Match when COM0An = 1 (issue #78)', () => { @@ -611,21 +610,21 @@ describe('timer', () => { new AVRTimer(cpu, timer0Config); // Listen to Port D's internal callback - const gpioCallback = jest.fn(); - cpu.gpioTimerHooks[PORTD] = gpioCallback; + const portD = new AVRIOPort(cpu, portDConfig); + const gpioCallback = jest.spyOn(portD, 'timerOverridePin'); const runner = new TestProgramRunner(cpu); runner.runToAddress(labels.beforeMatch); expect(cpu.readData(TCNT0)).toEqual(0xfd); expect(gpioCallback).toHaveBeenCalledTimes(1); - expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Enable, 0x2b); // OC0A: Enable + expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Enable); // OC0A: Enable gpioCallback.mockClear(); runner.runToAddress(labels.afterMatch); expect(cpu.readData(TCNT0)).toEqual(0xfe); expect(gpioCallback).toHaveBeenCalledTimes(1); - expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Toggle, 0x2b); // OC0A: Toggle + expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Toggle); // OC0A: Toggle gpioCallback.mockClear(); runner.runToAddress(labels.afterOverflow); @@ -657,21 +656,21 @@ describe('timer', () => { new AVRTimer(cpu, timer0Config); // Listen to Port D's internal callback - const gpioCallback = jest.fn(); - cpu.gpioTimerHooks[PORTD] = gpioCallback; + const portD = new AVRIOPort(cpu, portDConfig); + const gpioCallback = jest.spyOn(portD, 'timerOverridePin'); const runner = new TestProgramRunner(cpu); // First, run with the bit set and assert that the Pin Override was enabled (OC0A connected) runner.runToAddress(labels.beforeClearWGM02); expect(gpioCallback).toHaveBeenCalledTimes(1); - expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Enable, 0x2b); + expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Enable); gpioCallback.mockClear(); // Now clear WGM02 and observe that Pin Override was disabled (OC0A disconnected) runner.runToAddress(labels.afterClearWGM02); expect(gpioCallback).toHaveBeenCalledTimes(1); - expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.None, 0x2b); + expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.None); gpioCallback.mockClear(); }); }); @@ -731,20 +730,20 @@ describe('timer', () => { new AVRTimer(cpu, timer0Config); // Listen to Port D's internal callback - const gpioCallback = jest.fn(); - cpu.gpioTimerHooks[PORTD] = gpioCallback; + const portD = new AVRIOPort(cpu, portDConfig); + const gpioCallback = jest.spyOn(portD, 'timerOverridePin'); const nopCount = lines.filter((line) => line.bytes == nopOpCode).length; const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount - nopCount); expect(cpu.readData(TCNT0)).toEqual(0xfd); - expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Enable, 0x2b); + expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Enable); gpioCallback.mockClear(); runner.runInstructions(1); expect(cpu.readData(TCNT0)).toEqual(0xfe); - expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Clear, 0x2b); + expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Clear); gpioCallback.mockClear(); runner.runInstructions(1); @@ -753,7 +752,7 @@ describe('timer', () => { runner.runInstructions(1); expect(cpu.readData(TCNT0)).toEqual(0xfe); - expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Set, 0x2b); + expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Set); }); it('should toggle OC0A when TCNT0=OCR0A and COM0An=1 (issue #78)', () => { @@ -778,19 +777,19 @@ describe('timer', () => { new AVRTimer(cpu, timer0Config); // Listen to Port D's internal callback - const gpioCallback = jest.fn(); - cpu.gpioTimerHooks[PORTD] = gpioCallback; + const portD = new AVRIOPort(cpu, portDConfig); + const gpioCallback = jest.spyOn(portD, 'timerOverridePin'); const runner = new TestProgramRunner(cpu); runner.runToAddress(labels.beforeMatch); expect(cpu.readData(TCNT0)).toEqual(0xfd); - expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Enable, 0x2b); + expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Enable); gpioCallback.mockClear(); runner.runToAddress(labels.afterMatch); expect(cpu.readData(TCNT0)).toEqual(0xfe); - expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Toggle, 0x2b); + expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Toggle); gpioCallback.mockClear(); }); @@ -811,8 +810,8 @@ describe('timer', () => { new AVRTimer(cpu, timer0Config); // Listen to Port D's internal callback - const gpioCallback = jest.fn(); - cpu.gpioTimerHooks[PORTD] = gpioCallback; + const portD = new AVRIOPort(cpu, portDConfig); + const gpioCallback = jest.spyOn(portD, 'timerOverridePin'); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); @@ -839,17 +838,17 @@ describe('timer', () => { new AVRTimer(cpu, timer0Config); // Listen to Port D's internal callback - const gpioCallback = jest.fn(); - cpu.gpioTimerHooks[PORTD] = gpioCallback; + const portD = new AVRIOPort(cpu, portDConfig); + const gpioCallback = jest.spyOn(portD, 'timerOverridePin'); const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); expect(cpu.readData(TCNT0)).toEqual(0x11); - expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Enable, 0x2b); + expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Enable); // Verify that Compare Match has occured and set the OC0A pin (PD6 on ATmega328p) - expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Set, 0x2b); + expect(gpioCallback).toHaveBeenCalledWith(6, PinOverrideMode.Set); }); it('should only update OCR0A when TCNT0=TOP in PWM Phase Correct mode (issue #76)', () => { @@ -881,10 +880,6 @@ describe('timer', () => { const cpu = new CPU(program); new AVRTimer(cpu, timer0Config); - // Listen to Port D's internal callback - const gpioCallback = jest.fn(); - cpu.gpioTimerHooks[PORTD] = gpioCallback; - const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); @@ -1023,20 +1018,20 @@ describe('timer', () => { new AVRTimer(cpu, timer1Config); // Listen to Port B's internal callback - const gpioCallback = jest.fn(); - cpu.gpioTimerHooks[PORTB] = gpioCallback; + const portB = new AVRIOPort(cpu, portBConfig); + const gpioCallback = jest.spyOn(portB, 'timerOverridePin'); const nopCount = lines.filter((line) => line.bytes == nopOpCode).length; const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount - nopCount); expect(cpu.readData(TCNT1)).toEqual(0x49); - expect(gpioCallback).toHaveBeenCalledWith(2, PinOverrideMode.Enable, 0x25); + expect(gpioCallback).toHaveBeenCalledWith(2, PinOverrideMode.Enable); gpioCallback.mockClear(); runner.runInstructions(1); expect(cpu.readData(TCNT1)).toEqual(0x4a); - expect(gpioCallback).toHaveBeenCalledWith(2, PinOverrideMode.Toggle, 0x25); + expect(gpioCallback).toHaveBeenCalledWith(2, PinOverrideMode.Toggle); }); it('should only update OCR1A when TCNT1=BOTTOM in PWM Phase/Frequency Correct mode (issue #76)', () => { @@ -1073,10 +1068,6 @@ describe('timer', () => { const cpu = new CPU(program); new AVRTimer(cpu, timer1Config); - // Listen to Port D's internal callback - const gpioCallback = jest.fn(); - cpu.gpioTimerHooks[PORTD] = gpioCallback; - const runner = new TestProgramRunner(cpu); runner.runInstructions(instructionCount); @@ -1086,4 +1077,44 @@ describe('timer', () => { expect(cpu.readData(R20)).toEqual(0x7); }); }); + + describe('External clock', () => { + it('should count on the falling edge of T0 when CS=110', () => { + const cpu = new CPU(new Uint16Array(0x1000)); + const port = new AVRIOPort(cpu, portDConfig); + new AVRTimer(cpu, timer0Config); + cpu.writeData(TCCR0B, CS02 | CS01); // Count on falling edge + cpu.cycles = 1; + cpu.tick(); + + port.setPin(T0, true); // Rising edge + cpu.cycles = 2; + cpu.tick(); + expect(cpu.readData(TCNT0)).toEqual(0); + + port.setPin(T0, false); // Falling edge + cpu.cycles = 3; + cpu.tick(); + expect(cpu.readData(TCNT0)).toEqual(1); + }); + + it('should count on the rising edge of T0 when CS=111', () => { + const cpu = new CPU(new Uint16Array(0x1000)); + const port = new AVRIOPort(cpu, portDConfig); + new AVRTimer(cpu, timer0Config); + cpu.writeData(TCCR0B, CS02 | CS01 | CS00); // Count on rising edge + cpu.cycles = 1; + cpu.tick(); + + port.setPin(T0, true); // Rising edge + cpu.cycles = 2; + cpu.tick(); + expect(cpu.readData(TCNT0)).toEqual(1); + + port.setPin(T0, false); // Falling edge + cpu.cycles = 3; + cpu.tick(); + expect(cpu.readData(TCNT0)).toEqual(1); + }); + }); }); diff --git a/src/peripherals/timer.ts b/src/peripherals/timer.ts index cd55f12..7b54d3f 100644 --- a/src/peripherals/timer.ts +++ b/src/peripherals/timer.ts @@ -3,11 +3,11 @@ * Part of AVR8js * Reference: http://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf * - * Copyright (C) 2019, 2020, Uri Shaked + * Copyright (C) 2019, 2020, 2021 Uri Shaked */ import { AVRInterruptConfig, CPU } from '../cpu/cpu'; -import { PinOverrideMode, portBConfig, portDConfig } from './gpio'; +import { AVRIOPort, PinOverrideMode, portBConfig, portDConfig } from './gpio'; const timer01Dividers = { 0: 0, @@ -16,10 +16,15 @@ const timer01Dividers = { 3: 64, 4: 256, 5: 1024, - 6: 0, // TODO: External clock source on T0 pin. Clock on falling edge. - 7: 0, // TODO: External clock source on T0 pin. Clock on rising edge. + 6: 0, // External clock - see ExternalClockMode + 7: 0, // Ditto }; +enum ExternalClockMode { + FallingEdge = 6, + RisingEdge = 7, +} + type u8 = number; type u16 = number; @@ -70,6 +75,10 @@ export interface AVRTimerConfig { compPinA: u8; compPortB: u16; compPinB: u8; + + // External clock pin (optional, 0 = unused) + externalClockPort: u16; + externalClockPin: u8; } /** These are differnet for some devices (e.g. ATtiny85) */ @@ -105,6 +114,8 @@ export const timer0Config: AVRTimerConfig = { compPinA: 6, compPortB: portDConfig.PORT, compPinB: 5, + externalClockPort: portDConfig.PORT, + externalClockPin: 4, ...defaultTimerBits, }; @@ -128,6 +139,8 @@ export const timer1Config: AVRTimerConfig = { compPinA: 1, compPortB: portBConfig.PORT, compPinB: 2, + externalClockPort: portDConfig.PORT, + externalClockPin: 5, ...defaultTimerBits, }; @@ -160,6 +173,8 @@ export const timer2Config: AVRTimerConfig = { compPinA: 3, compPortB: portDConfig.PORT, compPinB: 3, + externalClockPort: 0, // Not available + externalClockPin: 0, ...defaultTimerBits, }; @@ -262,6 +277,8 @@ export class AVRTimer { private updateDivider = false; private countingUp = true; private divider = 0; + private externalClockPort?: AVRIOPort; + private externalClockRisingEdge = false; // This is the temporary register used to access 16-bit registers (section 16.3 of the datasheet) private highByteTemp: u8 = 0; @@ -440,12 +457,12 @@ export class AVRTimer { } } - count = (reschedule = true) => { + count = (reschedule = true, external = false) => { const { divider, lastCycle, cpu } = this; const { cycles } = cpu; const delta = cycles - lastCycle; - if (divider && delta >= divider) { - const counterDelta = Math.floor(delta / divider); + if ((divider && delta >= divider) || external) { + const counterDelta = external ? 1 : Math.floor(delta / divider); this.lastCycle += counterDelta * divider; const val = this.tcnt; const { timerMode, TOP } = this; @@ -491,12 +508,27 @@ export class AVRTimer { this.tcntUpdated = false; } if (this.updateDivider) { - const newDivider = this.config.dividers[this.CS]; + const { CS } = this; + const { externalClockPin } = this.config; + const newDivider = this.config.dividers[CS]; this.lastCycle = newDivider ? this.cpu.cycles : 0; this.updateDivider = false; this.divider = newDivider; + if (this.config.externalClockPort && !this.externalClockPort) { + this.externalClockPort = this.cpu.gpioByPort[this.config.externalClockPort]; + } + if (this.externalClockPort) { + this.externalClockPort.externalClockListeners[externalClockPin] = null; + } if (newDivider) { cpu.addClockEvent(this.count, this.lastCycle + newDivider - cpu.cycles); + } else if ( + this.externalClockPort && + (CS === ExternalClockMode.FallingEdge || CS === ExternalClockMode.RisingEdge) + ) { + this.externalClockPort.externalClockListeners[externalClockPin] = + this.externalClockCallback; + this.externalClockRisingEdge = CS === ExternalClockMode.RisingEdge; } return; } @@ -505,6 +537,12 @@ export class AVRTimer { } }; + private externalClockCallback = (value: boolean) => { + if (value === this.externalClockRisingEdge) { + this.count(false, true); + } + }; + private phasePwmCount(value: u16, delta: u8) { const { ocrA, ocrB, TOP, tcntUpdated } = this; while (delta > 0) { @@ -601,17 +639,13 @@ export class AVRTimer { private updateCompA(value: PinOverrideMode) { const { compPortA, compPinA } = this.config; - const hook = this.cpu.gpioTimerHooks[compPortA]; - if (hook) { - hook(compPinA, value, compPortA); - } + const port = this.cpu.gpioByPort[compPortA]; + port?.timerOverridePin(compPinA, value); } private updateCompB(value: PinOverrideMode) { const { compPortB, compPinB } = this.config; - const hook = this.cpu.gpioTimerHooks[compPortB]; - if (hook) { - hook(compPinB, value, compPortB); - } + const port = this.cpu.gpioByPort[compPortB]; + port?.timerOverridePin(compPinB, value); } } |
