aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorUri Shaked2020-05-25 16:20:24 +0300
committerGitHub2020-05-25 16:20:24 +0300
commit00cea56ab3d5f211aa738195de66139bd1a74f56 (patch)
tree6ef5b22ca31863341b9d70c81234e56a7a87dbb9 /src
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')
-rw-r--r--src/cpu/cpu.ts4
-rw-r--r--src/peripherals/gpio.ts64
-rw-r--r--src/peripherals/timer.spec.ts158
-rw-r--r--src/peripherals/timer.ts173
4 files changed, 342 insertions, 57 deletions
diff --git a/src/cpu/cpu.ts b/src/cpu/cpu.ts
index 5663a1c..ed88e71 100644
--- a/src/cpu/cpu.ts
+++ b/src/cpu/cpu.ts
@@ -44,6 +44,7 @@ export type CPUMemoryReadHook = (addr: u16) => u8;
export interface CPUMemoryReadHooks {
[key: number]: CPUMemoryReadHook;
}
+
export class CPU implements ICPU {
readonly data: Uint8Array = new Uint8Array(this.sramBytes + registerSpace);
readonly data16 = new Uint16Array(this.data.buffer);
@@ -53,6 +54,9 @@ export class CPU implements ICPU {
readonly writeHooks: CPUMemoryHooks = [];
readonly pc22Bits = this.progBytes.length > 0x20000;
+ // This lets the Timer Compare output override GPIO pins:
+ readonly gpioTimerHooks: CPUMemoryHooks = [];
+
pc = 0;
cycles = 0;
diff --git a/src/peripherals/gpio.ts b/src/peripherals/gpio.ts
index e3c8eaa..cb21669 100644
--- a/src/peripherals/gpio.ts
+++ b/src/peripherals/gpio.ts
@@ -90,23 +90,34 @@ export enum PinState {
InputPullUp,
}
+/* This mechanism allows timers to override specific GPIO pins */
+export enum PinOverrideMode {
+ None,
+ Enable,
+ Set,
+ Clear,
+ Toggle,
+}
+
export class AVRIOPort {
private listeners: GPIOListener[] = [];
private pinValue: u8 = 0;
+ private overrideMask: u8 = 0xff;
+ private overrideValue: u8;
+ private lastValue: u8 = 0;
+ private lastDdr: u8 = 0;
constructor(private cpu: CPU, private portConfig: AVRPortConfig) {
- cpu.writeHooks[portConfig.DDR] = (value, oldValue) => {
+ cpu.writeHooks[portConfig.DDR] = (value: u8) => {
const portValue = cpu.data[portConfig.PORT];
this.updatePinRegister(portValue, value);
- this.writeGpio(value & portValue, oldValue & oldValue);
+ this.writeGpio(portValue, value);
};
- cpu.writeHooks[portConfig.PORT] = (value: u8, oldValue: u8) => {
+ cpu.writeHooks[portConfig.PORT] = (value: u8) => {
const ddrMask = cpu.data[portConfig.DDR];
cpu.data[portConfig.PORT] = value;
- value &= ddrMask;
- cpu.data[portConfig.PIN] = (cpu.data[portConfig.PIN] & ~ddrMask) | value;
this.updatePinRegister(value, ddrMask);
- this.writeGpio(value, oldValue & ddrMask);
+ this.writeGpio(value, ddrMask);
return true;
};
cpu.writeHooks[portConfig.PIN] = (value: u8) => {
@@ -116,9 +127,34 @@ export class AVRIOPort {
const portValue = oldPortValue ^ value;
cpu.data[portConfig.PORT] = portValue;
cpu.data[portConfig.PIN] = (cpu.data[portConfig.PIN] & ~ddrMask) | (portValue & ddrMask);
- this.writeGpio(portValue & ddrMask, oldPortValue & ddrMask);
+ this.writeGpio(portValue, 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;
+ } 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;
+ }
+ }
+ this.writeGpio(cpu.data[portConfig.PORT], cpu.data[portConfig.DDR]);
+ };
}
addListener(listener: GPIOListener) {
@@ -142,7 +178,7 @@ export class AVRIOPort {
const port = this.cpu.data[this.portConfig.PORT];
const bitMask = 1 << index;
if (ddr & bitMask) {
- return port & bitMask ? PinState.High : PinState.Low;
+ return this.lastValue & bitMask ? PinState.High : PinState.Low;
} else {
return port & bitMask ? PinState.InputPullUp : PinState.Input;
}
@@ -165,9 +201,15 @@ export class AVRIOPort {
this.cpu.data[this.portConfig.PIN] = (this.pinValue & ~ddr) | (port & ddr);
}
- private writeGpio(value: u8, oldValue: u8) {
- for (const listener of this.listeners) {
- listener(value, oldValue);
+ private writeGpio(value: u8, ddr: u8) {
+ const newValue = ((value & this.overrideMask) | this.overrideValue) & ddr;
+ const prevValue = this.lastValue;
+ if (newValue !== prevValue || ddr !== this.lastDdr) {
+ this.lastValue = newValue;
+ this.lastDdr = ddr;
+ for (const listener of this.listeners) {
+ listener(newValue, prevValue);
+ }
}
}
}
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);
+ });
});
});
diff --git a/src/peripherals/timer.ts b/src/peripherals/timer.ts
index 4e75fb1..4293a49 100644
--- a/src/peripherals/timer.ts
+++ b/src/peripherals/timer.ts
@@ -8,6 +8,7 @@
import { CPU } from '../cpu/cpu';
import { avrInterrupt } from '../cpu/interrupt';
+import { portBConfig, portDConfig, PinOverrideMode } from './gpio';
const timer01Dividers = {
0: 0,
@@ -61,6 +62,12 @@ interface AVRTimerConfig {
TIMSK: u8;
dividers: TimerDividers;
+
+ // Output compare pins
+ compPortA: u16;
+ compPinA: u8;
+ compPortB: u16;
+ compPinB: u8;
}
export const timer0Config: AVRTimerConfig = {
@@ -79,6 +86,10 @@ export const timer0Config: AVRTimerConfig = {
TCCRC: 0, // not available
TIMSK: 0x6e,
dividers: timer01Dividers,
+ compPortA: portDConfig.PORT,
+ compPinA: 6,
+ compPortB: portDConfig.PORT,
+ compPinB: 5,
};
export const timer1Config: AVRTimerConfig = {
@@ -97,6 +108,10 @@ export const timer1Config: AVRTimerConfig = {
TCCRC: 0x82,
TIMSK: 0x6f,
dividers: timer01Dividers,
+ compPortA: portBConfig.PORT,
+ compPinA: 1,
+ compPortB: portBConfig.PORT,
+ compPinB: 2,
};
export const timer2Config: AVRTimerConfig = {
@@ -124,6 +139,10 @@ export const timer2Config: AVRTimerConfig = {
6: 256,
7: 1024,
},
+ compPortA: portBConfig.PORT,
+ compPinA: 3,
+ compPortB: portDConfig.PORT,
+ compPinB: 3,
};
/* All the following types and constants are related to WGM (Waveform Generation Mode) bits: */
@@ -185,14 +204,36 @@ const wgmModes16Bit: WGMConfig[] = [
/*15*/ [TimerMode.FastPWM, TopOCRA, OCRUpdateMode.Bottom, TOVUpdateMode.Top],
];
+type CompBitsValue = 0 | 1 | 2 | 3;
+
+function compToOverride(comp: CompBitsValue) {
+ switch (comp) {
+ case 1:
+ return PinOverrideMode.Toggle;
+ case 2:
+ return PinOverrideMode.Clear;
+ case 3:
+ return PinOverrideMode.Set;
+ default:
+ return PinOverrideMode.Enable;
+ }
+}
+
export class AVRTimer {
private lastCycle = 0;
private ocrA: u16 = 0;
private ocrB: u16 = 0;
+ private icr: u16 = 0; // only for 16-bit timers
private timerMode: TimerMode;
private topValue: TimerTopValue;
private tcnt: u16 = 0;
+ private compA: CompBitsValue;
+ private compB: CompBitsValue;
private tcntUpdated = false;
+ private countingUp = true;
+
+ // This is the temporary register used to access 16-bit registers (section 16.3 of the datasheet)
+ private highByteTemp: u8 = 0;
constructor(private cpu: CPU, private config: AVRTimerConfig) {
this.updateWGMConfig();
@@ -205,20 +246,36 @@ export class AVRTimer {
};
this.cpu.writeHooks[config.TCNT] = (value: u8) => {
- const highByte = this.config.bits === 16 ? this.cpu.data[config.TCNT + 1] : 0;
- this.tcnt = (highByte << 8) | value;
+ this.tcnt = (this.highByteTemp << 8) | value;
this.tcntUpdated = true;
this.timerUpdated();
};
- this.registerHook(config.OCRA, (value: u16) => {
+ this.cpu.writeHooks[config.OCRA] = (value: u8) => {
// TODO implement buffering when timer running in PWM mode
- this.ocrA = value;
- });
- this.registerHook(config.OCRB, (value: u16) => {
- this.ocrB = value;
- });
+ this.ocrA = (this.highByteTemp << 8) | value;
+ };
+ this.cpu.writeHooks[config.OCRB] = (value: u8) => {
+ // TODO implement buffering when timer running in PWM mode
+ this.ocrB = (this.highByteTemp << 8) | value;
+ };
+ this.cpu.writeHooks[config.ICR] = (value: u8) => {
+ this.icr = (this.highByteTemp << 8) | value;
+ };
+ if (this.config.bits === 16) {
+ const updateTempRegister = (value: u8) => {
+ this.highByteTemp = value;
+ };
+ this.cpu.writeHooks[config.TCNT + 1] = updateTempRegister;
+ this.cpu.writeHooks[config.OCRA + 1] = updateTempRegister;
+ this.cpu.writeHooks[config.OCRB + 1] = updateTempRegister;
+ this.cpu.writeHooks[config.ICR + 1] = updateTempRegister;
+ }
cpu.writeHooks[config.TCCRA] = (value) => {
this.cpu.data[config.TCCRA] = value;
+ this.compA = ((value >> 6) & 0x3) as CompBitsValue;
+ this.updateCompA(this.compA ? PinOverrideMode.Enable : PinOverrideMode.None);
+ this.compB = ((value >> 4) & 0x3) as CompBitsValue;
+ this.updateCompB(this.compB ? PinOverrideMode.Enable : PinOverrideMode.None);
this.updateWGMConfig();
return true;
};
@@ -255,11 +312,6 @@ export class AVRTimer {
return this.cpu.data[this.config.TIMSK];
}
- get ICR() {
- // Only available for 16-bit timers
- return (this.cpu.data[this.config.ICR + 1] << 8) | this.cpu.data[this.config.ICR];
- }
-
get CS() {
return (this.TCCRB & 0x7) as 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;
}
@@ -274,21 +326,12 @@ export class AVRTimer {
case TopOCRA:
return this.ocrA;
case TopICR:
- return this.ICR;
+ return this.icr;
default:
return this.topValue;
}
}
- private registerHook(address: number, hook: (value: u16) => void) {
- if (this.config.bits === 16) {
- this.cpu.writeHooks[address] = (value: u8) => hook((this.cpu.data[address + 1] << 8) | value);
- this.cpu.writeHooks[address + 1] = (value: u8) => hook((value << 8) | this.cpu.data[address]);
- } else {
- this.cpu.writeHooks[address] = hook;
- }
- }
-
private updateWGMConfig() {
const wgmModes = this.config.bits === 16 ? wgmModes16Bit : wgmModes8Bit;
const [timerMode, topValue] = wgmModes[this.WGM];
@@ -303,20 +346,18 @@ export class AVRTimer {
const counterDelta = Math.floor(delta / divider);
this.lastCycle += counterDelta * divider;
const val = this.tcnt;
- const newVal = (val + counterDelta) % (this.TOP + 1);
+ const { timerMode } = this;
+ const phasePwm =
+ timerMode === TimerMode.PWMPhaseCorrect || timerMode === TimerMode.PWMPhaseFrequencyCorrect;
+ const newVal = phasePwm
+ ? this.phasePwmCount(val, counterDelta)
+ : (val + counterDelta) % (this.TOP + 1);
// A CPU write overrides (has priority over) all counter clear or count operations.
if (!this.tcntUpdated) {
this.tcnt = newVal;
this.timerUpdated();
}
- const { timerMode } = this;
- if (
- (timerMode === TimerMode.Normal ||
- timerMode === TimerMode.PWMPhaseCorrect ||
- timerMode === TimerMode.PWMPhaseFrequencyCorrect ||
- timerMode === TimerMode.FastPWM) &&
- val > newVal
- ) {
+ if ((timerMode === TimerMode.Normal || timerMode === TimerMode.FastPWM) && val > newVal) {
this.TIFR |= TOV;
}
}
@@ -337,8 +378,28 @@ export class AVRTimer {
}
}
+ private phasePwmCount(value: u16, delta: u8) {
+ while (delta > 0) {
+ if (this.countingUp) {
+ value++;
+ if (value === this.TOP && !this.tcntUpdated) {
+ this.countingUp = false;
+ }
+ } else {
+ value--;
+ if (!value && !this.tcntUpdated) {
+ this.countingUp = true;
+ this.TIFR |= TOV;
+ }
+ }
+ delta--;
+ }
+ return value;
+ }
+
private timerUpdated() {
const value = this.tcnt;
+
if (this.ocrA && value === this.ocrA) {
this.TIFR |= OCFA;
if (this.timerMode === TimerMode.CTC) {
@@ -346,9 +407,57 @@ export class AVRTimer {
this.tcnt = 0;
this.TIFR |= TOV;
}
+ if (this.compA) {
+ this.updateCompPin(this.compA, 'A');
+ }
}
if (this.ocrB && value === this.ocrB) {
this.TIFR |= OCFB;
+ if (this.compB) {
+ this.updateCompPin(this.compB, 'B');
+ }
+ }
+ }
+
+ private updateCompPin(compValue: CompBitsValue, pinName: 'A' | 'B') {
+ let newValue: PinOverrideMode = PinOverrideMode.None;
+ const inverted = compValue === 3;
+ const isSet = this.countingUp === inverted;
+ switch (this.timerMode) {
+ case TimerMode.Normal:
+ case TimerMode.CTC:
+ case TimerMode.FastPWM:
+ newValue = compToOverride(compValue);
+ break;
+
+ case TimerMode.PWMPhaseCorrect:
+ case TimerMode.PWMPhaseFrequencyCorrect:
+ newValue = isSet ? PinOverrideMode.Set : PinOverrideMode.Clear;
+ break;
+ }
+
+ if (newValue !== PinOverrideMode.None) {
+ if (pinName === 'A') {
+ this.updateCompA(newValue);
+ } else {
+ this.updateCompB(newValue);
+ }
+ }
+ }
+
+ private updateCompA(value: PinOverrideMode) {
+ const { compPortA, compPinA } = this.config;
+ const hook = this.cpu.gpioTimerHooks[compPortA];
+ if (hook) {
+ hook(compPinA, value, compPortA);
+ }
+ }
+
+ private updateCompB(value: PinOverrideMode) {
+ const { compPortB, compPinB } = this.config;
+ const hook = this.cpu.gpioTimerHooks[compPortB];
+ if (hook) {
+ hook(compPinB, value, compPortB);
}
}
}