aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cpu/cpu.ts3
-rw-r--r--src/peripherals/gpio.spec.ts4
-rw-r--r--src/peripherals/gpio.ts72
-rw-r--r--src/peripherals/timer.spec.ts121
-rw-r--r--src/peripherals/timer.ts66
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);
}
}
If you find this content useful please consider a small donation via bitcoin: bitcoin:1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2?amount=0.001&message=Donation Scraping git contents via cgit is very resource intensive. The continued hosting of these git repositories is entirely financed by your bitcoin contributions. For donations of a least 1 mBTC you include your IP address in the message of your bitcoin donation. This will remove this message in the future for requests from your IP, which will significantly reduce token usage. In any case, any contributions to bitcoin address 1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2 are very welcome. Thanks in advance for you contribution to a self-sustaining ecosystem.