aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorUri Shaked2021-10-07 00:05:23 +0300
committerUri Shaked2021-10-07 00:05:23 +0300
commitaa9838813f2541eb9fc972c24ac60afa022eb07a (patch)
tree4340b6f518ebe5f0481d77a65fe922d26d61e69c /src
parentchore(deps): bump tmpl from 1.0.4 to 1.0.5 (diff)
downloadavr8js-aa9838813f2541eb9fc972c24ac60afa022eb07a.tar.gz
avr8js-aa9838813f2541eb9fc972c24ac60afa022eb07a.tar.bz2
avr8js-aa9838813f2541eb9fc972c24ac60afa022eb07a.zip
feat(timer): Force Output Compare (FOC) bits
Diffstat (limited to '')
-rw-r--r--src/peripherals/timer.spec.ts69
-rw-r--r--src/peripherals/timer.ts34
2 files changed, 98 insertions, 5 deletions
diff --git a/src/peripherals/timer.spec.ts b/src/peripherals/timer.spec.ts
index c1bf1e1..1afca4c 100644
--- a/src/peripherals/timer.spec.ts
+++ b/src/peripherals/timer.spec.ts
@@ -27,6 +27,7 @@ const TIMSK1 = 0x6f;
const TIFR1 = 0x36;
const TCCR1A = 0x80;
const TCCR1B = 0x81;
+const TCCR1C = 0x82;
const TCNT1 = 0x84;
const TCNT1H = 0x85;
const ICR1 = 0x86;
@@ -34,6 +35,8 @@ const ICR1H = 0x87;
const OCR1A = 0x88;
const OCR1AH = 0x89;
const OCR1B = 0x8a;
+const OCR1C = 0x8c;
+const OCR1CH = 0x8d;
// Timer 2 Registers
const TCCR2B = 0xb1;
@@ -47,8 +50,9 @@ const OCIE0B = 4;
const TOIE0 = 1;
const OCF0A = 2;
const OCF0B = 4;
-const OCF1A = 2;
-const OCF1B = 4;
+const OCF1A = 1 << 1;
+const OCF1B = 1 << 2;
+const OCF1C = 1 << 3;
const WGM00 = 1;
const WGM10 = 1;
const WGM01 = 2;
@@ -61,6 +65,10 @@ const CS02 = 4;
const CS10 = 1;
const CS21 = 2;
const CS22 = 4;
+const COM0B1 = 1 << 5;
+const COM1C0 = 1 << 2;
+const FOC0B = 1 << 6;
+const FOC1C = 1 << 5;
const T0 = 4; // PD4 on ATmega328p
@@ -529,6 +537,20 @@ describe('timer', () => {
expect(cpu.readData(R17)).toEqual(2);
});
+ it('should clear OC0B pin when writing 1 to FOC0B', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
+ new AVRTimer(cpu, timer0Config);
+ cpu.writeData(TCCR0A, COM0B1);
+
+ // Listen to Port B's internal callback
+ const portD = new AVRIOPort(cpu, portDConfig);
+ const gpioCallback = jest.spyOn(portD, 'timerOverridePin');
+
+ cpu.writeData(TCCR0B, FOC0B);
+
+ expect(gpioCallback).toHaveBeenCalledWith(5, PinOverrideMode.Clear);
+ });
+
describe('Fast PWM mode', () => {
it('should set OC0A on Compare Match, clear on Bottom (issue #78)', () => {
const { program, labels } = asmProgram(`
@@ -1060,9 +1082,6 @@ describe('timer', () => {
});
it('should toggle OC1C on Compare Match', () => {
- const OCR1C = 0x8c;
- const OCR1CH = 0x8d;
- const OCF1C = 1 << 3;
const { program, lines, instructionCount } = asmProgram(`
; Set waveform generation mode (WGM) to Normal, top 0xFFFF
LDI r16, 0x04 ; TCCR1A = (1 << COM1C0);
@@ -1108,6 +1127,46 @@ describe('timer', () => {
expect(gpioCallback).toHaveBeenCalledWith(3, PinOverrideMode.Toggle);
});
+ it('should toggle OC1C on when writing 1 to FOC1C', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
+ new AVRTimer(cpu, {
+ ...timer1Config,
+ OCRC: OCR1C,
+ OCFC: OCF1C,
+ compPortC: portBConfig.PORT,
+ compPinC: 3,
+ });
+ cpu.writeData(TCCR1A, COM1C0);
+
+ // Listen to Port B's internal callback
+ const portB = new AVRIOPort(cpu, portBConfig);
+ const gpioCallback = jest.spyOn(portB, 'timerOverridePin');
+
+ cpu.writeData(TCCR1C, FOC1C);
+
+ expect(gpioCallback).toHaveBeenCalledWith(3, PinOverrideMode.Toggle);
+ });
+
+ it('should not toggle OC1C on when writing 1 to FOC1C in PWM mode', () => {
+ const cpu = new CPU(new Uint16Array(0x1000));
+ new AVRTimer(cpu, {
+ ...timer1Config,
+ OCRC: OCR1C,
+ OCFC: OCF1C,
+ compPortC: portBConfig.PORT,
+ compPinC: 3,
+ });
+ cpu.writeData(TCCR1A, COM1C0 | WGM11);
+
+ // Listen to Port B's internal callback
+ const portB = new AVRIOPort(cpu, portBConfig);
+ const gpioCallback = jest.spyOn(portB, 'timerOverridePin');
+
+ cpu.writeData(TCCR1C, FOC1C);
+
+ expect(gpioCallback).not.toHaveBeenCalled();
+ });
+
it('should only update OCR1A when TCNT1=BOTTOM in PWM Phase/Frequency Correct mode (issue #76)', () => {
const { program, instructionCount } = asmProgram(`
LDI r16, 0x0 ; OCR1AH = 0x0;
diff --git a/src/peripherals/timer.ts b/src/peripherals/timer.ts
index 35e8efd..b597d07 100644
--- a/src/peripherals/timer.ts
+++ b/src/peripherals/timer.ts
@@ -277,6 +277,11 @@ function compToOverride(comp: CompBitsValue) {
}
}
+// Force Output Compare (FOC) bits
+const FOCA = 1 << 7;
+const FOCB = 1 << 6;
+const FOCC = 1 << 5;
+
export class AVRTimer {
private readonly MAX = this.config.bits === 16 ? 0xffff : 0xff;
private lastCycle = 0;
@@ -397,6 +402,10 @@ export class AVRTimer {
return true;
};
cpu.writeHooks[config.TCCRB] = (value) => {
+ if (!config.TCCRC) {
+ this.checkForceCompare(value);
+ value &= ~(FOCA | FOCB);
+ }
this.cpu.data[config.TCCRB] = value;
this.updateDivider = true;
this.cpu.clearClockEvent(this.count);
@@ -404,6 +413,11 @@ export class AVRTimer {
this.updateWGMConfig();
return true;
};
+ if (config.TCCRC) {
+ cpu.writeHooks[config.TCCRC] = (value) => {
+ this.checkForceCompare(value);
+ };
+ }
cpu.writeHooks[config.TIFR] = (value) => {
this.cpu.data[config.TIFR] = value;
this.cpu.clearInterruptByFlag(this.OVF, value);
@@ -675,6 +689,26 @@ export class AVRTimer {
}
}
+ private checkForceCompare(value: number) {
+ if (
+ this.timerMode == TimerMode.FastPWM ||
+ this.timerMode == TimerMode.PWMPhaseCorrect ||
+ this.timerMode == TimerMode.PWMPhaseFrequencyCorrect
+ ) {
+ // The FOCnA/FOCnB/FOCnC bits are only active when the WGMn3:0 bits specifies a non-PWM mode
+ return;
+ }
+ if (value & FOCA) {
+ this.updateCompPin(this.compA, 'A');
+ }
+ if (value & FOCB) {
+ this.updateCompPin(this.compB, 'B');
+ }
+ if (this.config.compPortC && value & FOCC) {
+ this.updateCompPin(this.compC, 'C');
+ }
+ }
+
private updateCompPin(compValue: CompBitsValue, pinName: 'A' | 'B' | 'C', bottom = false) {
let newValue: PinOverrideMode = PinOverrideMode.None;
const invertingMode = compValue === 3;