aboutsummaryrefslogtreecommitdiff
path: root/src/timer.ts
diff options
context:
space:
mode:
authorUri Shaked2019-11-30 19:55:01 +0200
committerUri Shaked2019-11-30 19:55:01 +0200
commitae5caeb0b044a93bebcb641e1dd4ff9a390da26f (patch)
treed83060286b949704d01f2479f7b167840c5d0798 /src/timer.ts
parentchore: release 0.3.2 (diff)
downloadavr8js-ae5caeb0b044a93bebcb641e1dd4ff9a390da26f.tar.gz
avr8js-ae5caeb0b044a93bebcb641e1dd4ff9a390da26f.tar.bz2
avr8js-ae5caeb0b044a93bebcb641e1dd4ff9a390da26f.zip
feat: Output Compare for Timers
close #4
Diffstat (limited to 'src/timer.ts')
-rw-r--r--src/timer.ts84
1 files changed, 78 insertions, 6 deletions
diff --git a/src/timer.ts b/src/timer.ts
index 7203574..1406eda 100644
--- a/src/timer.ts
+++ b/src/timer.ts
@@ -20,6 +20,19 @@ const dividers = {
7: 0 // TODO: External clock source on T0 pin. Clock on rising edge.
};
+const WGM_NORMAL = 0;
+const WGM_PWM_PHASE_CORRECT = 1;
+const WGM_CTC = 2;
+const WGM_FASTPWM = 3;
+
+const TOV = 1;
+const OCFA = 2;
+const OCFB = 4;
+
+const TOIE = 1;
+const OCIEA = 2;
+const OCIEB = 4;
+
type u8 = number;
interface AVRTimerConfig {
@@ -95,8 +108,29 @@ export const timer2Config: AVRTimerConfig = {
export class AVRTimer {
private mask = (1 << this.config.bits) - 1;
private lastCycle = 0;
+ private ocrA: u8 = 0;
+ private ocrB: u8 = 0;
+
+ constructor(private cpu: CPU, private config: AVRTimerConfig) {
+ cpu.writeHooks[config.TCNT] = (value: u8) => {
+ this.TCNT = value;
+ this.timerUpdated(value);
+ return true;
+ };
+ cpu.writeHooks[config.OCRA] = (value: u8) => {
+ // TODO implement buffering when timer running in PWM mode
+ this.ocrA = value;
+ };
+ cpu.writeHooks[config.OCRB] = (value: u8) => {
+ this.ocrB = value;
+ };
+ }
- constructor(private cpu: CPU, private config: AVRTimerConfig) {}
+ reset() {
+ this.lastCycle = 0;
+ this.ocrA = 0;
+ this.ocrB = 0;
+ }
get TIFR() {
return this.cpu.data[this.config.TIFR];
@@ -114,6 +148,10 @@ export class AVRTimer {
this.cpu.data[this.config.TCNT] = value;
}
+ get TCCRA() {
+ return this.cpu.data[this.config.TCCRA];
+ }
+
get TCCRB() {
return this.cpu.data[this.config.TCCRB];
}
@@ -126,6 +164,10 @@ export class AVRTimer {
return (this.TCCRB & 0x7) as 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;
}
+ get WGM() {
+ return ((this.TCCRB & 0x8) >> 1) | (this.TCCRA & 0x3);
+ }
+
tick() {
const divider = dividers[this.CS];
const delta = this.cpu.cycles - this.lastCycle;
@@ -135,13 +177,43 @@ export class AVRTimer {
const val = this.TCNT;
const newVal = (val + counterDelta) & this.mask;
this.TCNT = newVal;
- if (val > newVal) {
- this.TIFR |= 1; // TOV
+ this.timerUpdated(newVal);
+ if (
+ (this.WGM === WGM_NORMAL ||
+ this.WGM === WGM_PWM_PHASE_CORRECT ||
+ this.WGM === WGM_FASTPWM) &&
+ val > newVal
+ ) {
+ this.TIFR |= TOV;
+ }
+ }
+ if (this.cpu.interruptsEnabled) {
+ if (this.TIFR & TOV && this.TIMSK & TOIE) {
+ avrInterrupt(this.cpu, this.config.ovfInterrupt);
+ this.TIFR &= ~TOV;
+ }
+ if (this.TIFR & OCFA && this.TIMSK & OCIEA) {
+ avrInterrupt(this.cpu, this.config.compAInterrupt);
+ this.TIFR &= ~OCFA;
+ }
+ if (this.TIFR & OCFB && this.TIMSK & OCIEB) {
+ avrInterrupt(this.cpu, this.config.compBInterrupt);
+ this.TIFR &= ~OCFB;
+ }
+ }
+ }
+
+ private timerUpdated(value: u8) {
+ if (this.ocrA && value === this.ocrA) {
+ this.TIFR |= OCFA;
+ if (this.WGM === WGM_CTC) {
+ // Clear Timer on Compare Match (CTC) Mode
+ this.TCNT = 0;
+ this.TIFR |= TOV;
}
}
- if (this.TIFR & 0x1 && this.TIMSK & 0x1 && this.cpu.interruptsEnabled) {
- avrInterrupt(this.cpu, this.config.ovfInterrupt);
- this.TIFR &= ~0x1;
+ if (this.ocrB && value === this.ocrB) {
+ this.TIFR |= OCFB;
}
}
}