Files
homeclaw/web-ui/vendor/rp2040js/utils/timer32.js
T
kim 209f3a3aef v3.0.6: RP2040 에뮬레이터 개선 — USB CDC, vendor화, 부팅 감지
- USB CDC 경로 복원: #define Serial Serial1 우회 제거
- rp2040js/avr8js/codemirror/xterm/monaco-editor vendor화
- /vendor 정적 라우팅 추가 (Express 5 catch-all 우회)
- 부팅 완료 타임아웃 감지 (Serial 출력 없어도 8초 후 RAF 전환)
- 리셋 시 Simulator 재생성으로 주변 장치 상태 초기화
- 시리얼 패널의 내부 메시지를 console.log로 이동
2026-06-19 15:49:36 +09:00

202 lines
5.9 KiB
JavaScript

export var TimerMode;
(function (TimerMode) {
TimerMode[TimerMode["Increment"] = 0] = "Increment";
TimerMode[TimerMode["Decrement"] = 1] = "Decrement";
TimerMode[TimerMode["ZigZag"] = 2] = "ZigZag";
})(TimerMode || (TimerMode = {}));
export class Timer32 {
constructor(clock, baseFreq) {
this.clock = clock;
this.baseFreq = baseFreq;
this.baseValue = 0;
this.baseNanos = 0;
this.topValue = 0xffffffff;
this.prescalerValue = 1;
this.timerMode = TimerMode.Increment;
this.enabled = true;
this.listeners = [];
}
reset() {
this.baseNanos = this.clock.nanos;
this.baseValue = 0;
this.updated();
}
set(value, zigZagDown = false) {
this.baseValue = zigZagDown ? this.topValue * 2 - value : value;
this.baseNanos = this.clock.nanos;
this.updated();
}
/**
* Advances the counter by the given amount. Note that this will
* decrease the counter if the timer is running in Decrement mode.
*
* @param delta The value to add to the counter. Can be negative.
*/
advance(delta) {
this.baseValue += delta;
}
get rawCounter() {
const { baseFreq, prescalerValue, baseNanos, baseValue, enabled, timerMode } = this;
if (!baseFreq || !prescalerValue || !enabled) {
return this.baseValue;
}
const zigzag = timerMode == TimerMode.ZigZag;
const ticks = ((this.clock.nanos - baseNanos) / 1e9) * (baseFreq / prescalerValue);
const topModulo = zigzag ? this.topValue * 2 : this.topValue + 1;
const delta = timerMode == TimerMode.Decrement ? topModulo - (ticks % topModulo) : ticks;
let currentValue = Math.round(baseValue + delta);
if (this.topValue != 0xffffffff) {
currentValue %= topModulo;
}
return currentValue;
}
get counter() {
let currentValue = this.rawCounter;
if (this.timerMode == TimerMode.ZigZag && currentValue > this.topValue) {
currentValue = this.topValue * 2 - currentValue;
}
return currentValue >>> 0;
}
get top() {
return this.topValue;
}
set top(value) {
const { counter } = this;
this.topValue = value;
this.set(counter <= this.topValue ? counter : 0);
}
get frequency() {
return this.baseFreq;
}
set frequency(value) {
this.baseValue = this.counter;
this.baseNanos = this.clock.nanos;
this.baseFreq = value;
this.updated();
}
get prescaler() {
return this.prescalerValue;
}
set prescaler(value) {
this.baseValue = this.counter;
this.baseNanos = this.clock.nanos;
this.enabled = this.prescalerValue !== 0;
this.prescalerValue = value;
this.updated();
}
toNanos(cycles) {
const { baseFreq, prescalerValue } = this;
return (cycles * 1e9) / (baseFreq / prescalerValue);
}
get enable() {
return this.enabled;
}
set enable(value) {
if (value !== this.enabled) {
if (value) {
this.baseNanos = this.clock.nanos;
}
else {
this.baseValue = this.counter;
}
this.enabled = value;
this.updated();
}
}
get mode() {
return this.timerMode;
}
set mode(value) {
if (this.timerMode !== value) {
const { counter } = this;
this.timerMode = value;
this.set(counter);
}
}
updated() {
for (const listener of this.listeners) {
listener();
}
}
}
export class Timer32PeriodicAlarm {
constructor(timer, callback) {
this.timer = timer;
this.callback = callback;
this.targetValue = 0;
this.enabled = false;
this.handleAlarm = () => {
this.callback();
if (this.enabled && this.timer.enable) {
this.schedule();
}
};
this.update = () => {
this.cancel();
if (this.enabled && this.timer.enable) {
this.schedule();
}
};
this.clockAlarm = this.timer.clock.createAlarm(this.handleAlarm);
timer.listeners.push(this.update);
}
get enable() {
return this.enabled;
}
set enable(value) {
if (value !== this.enabled) {
this.enabled = value;
if (value && this.timer.enable) {
this.schedule();
}
else {
this.cancel();
}
}
}
get target() {
return this.targetValue;
}
set target(value) {
if (value === this.targetValue) {
return;
}
this.targetValue = value;
if (this.enabled && this.timer.enable) {
this.cancel();
this.schedule();
}
}
schedule() {
const { timer, targetValue } = this;
const { top, mode, rawCounter } = timer;
let cycleDelta = targetValue - rawCounter;
if (mode === TimerMode.ZigZag && cycleDelta < 0) {
if (cycleDelta < -top) {
cycleDelta += 2 * top;
}
else {
cycleDelta = top * 2 - targetValue - rawCounter;
}
}
if (top != 0xffffffff) {
if (cycleDelta <= 0) {
cycleDelta += top + 1;
}
if (targetValue > top) {
// Skip alarm
return;
}
}
if (mode === TimerMode.Decrement) {
cycleDelta = top + 1 - cycleDelta;
}
const cyclesToAlarm = cycleDelta >>> 0;
const nanosToAlarm = timer.toNanos(cyclesToAlarm);
this.clockAlarm.schedule(nanosToAlarm);
}
cancel() {
this.clockAlarm.cancel();
}
}