stm32f4-emu 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +273 -0
- package/index.mjs +39 -0
- package/mcp/server.mjs +235 -0
- package/package.json +83 -0
- package/site/app.js +654 -0
- package/site/components.js +146 -0
- package/site/emulator.js +885 -0
- package/site/firmware.js +37 -0
- package/site/index.html +385 -0
- package/site/loaders.js +133 -0
- package/site/netsim.js +212 -0
- package/site/vendor/package.json +15 -0
- package/site/vendor/stm32_periph_wasm.d.ts +400 -0
- package/site/vendor/stm32_periph_wasm.js +944 -0
- package/site/vendor/stm32_periph_wasm_bg.wasm +0 -0
- package/site/vendor/stm32_periph_wasm_bg.wasm.d.ts +68 -0
- package/site/vendor/stm32f407.svd +41374 -0
- package/site/vendor/unicorn_arm.cjs +0 -0
- package/site/vendor/unicorn_arm.js +0 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// Minimal starter virtual-component library, built on the public
|
|
2
|
+
// pin/register-access API from emulator.js (`emu.pin()`, `emu.watchPin()`,
|
|
3
|
+
// `emu.read32()`, `emu.i2cRegfile()`). No imports: works in Node and the
|
|
4
|
+
// browser, same "import-free" convention as emulator.js.
|
|
5
|
+
//
|
|
6
|
+
// These are templates, not a full component catalog — attach your own
|
|
7
|
+
// devices the same way (see docs/components.md).
|
|
8
|
+
|
|
9
|
+
// LED wired to a GPIO pin the guest drives as output.
|
|
10
|
+
export class LED {
|
|
11
|
+
constructor(emu, port, num, { activeLow = false } = {}) {
|
|
12
|
+
this.emu = emu;
|
|
13
|
+
this.port = port;
|
|
14
|
+
this.num = num;
|
|
15
|
+
this.activeLow = activeLow;
|
|
16
|
+
this._pin = emu.pin(port, num);
|
|
17
|
+
this._unwatch = null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get value() {
|
|
21
|
+
const raw = this._pin.read();
|
|
22
|
+
return this.activeLow ? !raw : raw;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
watch(callback) {
|
|
26
|
+
this.unwatch();
|
|
27
|
+
this._unwatch = this.emu.watchPin(this.port, this.num, (raw) => {
|
|
28
|
+
callback(this.activeLow ? !raw : raw);
|
|
29
|
+
});
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
unwatch() {
|
|
34
|
+
if (this._unwatch) { this._unwatch(); this._unwatch = null; }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Button that drives a GPIO pin as input into the guest (press/release).
|
|
39
|
+
// activeLow=true (default) models a pull-up button: idle=high, pressed=low.
|
|
40
|
+
export class Button {
|
|
41
|
+
constructor(emu, port, num, { activeLow = true } = {}) {
|
|
42
|
+
this.emu = emu;
|
|
43
|
+
this.activeLow = activeLow;
|
|
44
|
+
this._pin = emu.pin(port, num);
|
|
45
|
+
this._pin.write(activeLow); // idle level
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
press() { this._pin.write(!this.activeLow); }
|
|
49
|
+
release() { this._pin.write(this.activeLow); }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// STM32F407 general-purpose/advanced timer base addresses (RM0090).
|
|
53
|
+
const TIM_BASE = {
|
|
54
|
+
TIM1: 0x40010000, TIM2: 0x40000000, TIM3: 0x40000400, TIM4: 0x40000800,
|
|
55
|
+
TIM5: 0x40000C00, TIM6: 0x40001000, TIM7: 0x40001400, TIM8: 0x40010400,
|
|
56
|
+
TIM9: 0x40014000, TIM10: 0x40014400, TIM11: 0x40014800,
|
|
57
|
+
TIM12: 0x40001800, TIM13: 0x40001C00, TIM14: 0x40002000,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Default timer clock at the standard 168 MHz F407 configuration: APB2
|
|
61
|
+
// timers (TIM1/8/9/10/11) tick at 168 MHz, APB1 timers at 84 MHz. Pass
|
|
62
|
+
// `clockHz` explicitly if your firmware clocks the buses differently.
|
|
63
|
+
const TIM_APB2 = new Set(['TIM1', 'TIM8', 'TIM9', 'TIM10', 'TIM11']);
|
|
64
|
+
const timDefaultClock = (timer) => (TIM_APB2.has(timer) ? 168e6 : 84e6);
|
|
65
|
+
|
|
66
|
+
// Read-only PWM observer for one output-compare channel (1-4) of a timer:
|
|
67
|
+
// decodes CR1/CCER/PSC/ARR/CCRn via emu.read32, the same approach
|
|
68
|
+
// emulator.js's built-in buzzer device uses internally, generalized to any
|
|
69
|
+
// timer/channel. Base for a servo-angle or PWM-dimmed-LED component — map
|
|
70
|
+
// `.duty` (0-1) to whatever range your virtual device needs.
|
|
71
|
+
export class Pwm {
|
|
72
|
+
constructor(emu, timer, channel = 1, { clockHz } = {}) {
|
|
73
|
+
const base = TIM_BASE[timer];
|
|
74
|
+
if (base === undefined) throw new Error(`Pwm: unknown timer '${timer}'`);
|
|
75
|
+
if (!Number.isInteger(channel) || channel < 1 || channel > 4) {
|
|
76
|
+
throw new Error(`Pwm: channel must be 1-4, got ${channel}`);
|
|
77
|
+
}
|
|
78
|
+
this.emu = emu;
|
|
79
|
+
this.timer = timer;
|
|
80
|
+
this.base = base;
|
|
81
|
+
this.channel = channel;
|
|
82
|
+
this.clockHz = clockHz ?? timDefaultClock(timer);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
_regs() {
|
|
86
|
+
const r = (off) => this.emu.read32(this.base + off);
|
|
87
|
+
return {
|
|
88
|
+
cr1: r(0x00), ccer: r(0x20), psc: r(0x28), arr: r(0x2C),
|
|
89
|
+
ccr: r(0x34 + (this.channel - 1) * 4),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
get freq() {
|
|
94
|
+
const { cr1, psc, arr } = this._regs();
|
|
95
|
+
if (!(cr1 & 1) || arr === 0 || arr >= 0xFFFFFF) return 0;
|
|
96
|
+
const div = (psc + 1) * (arr + 1);
|
|
97
|
+
return div > 0 ? this.clockHz / div : 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
get duty() {
|
|
101
|
+
const { cr1, ccer, arr, ccr } = this._regs();
|
|
102
|
+
const ccEnabled = (ccer >> ((this.channel - 1) * 4)) & 1;
|
|
103
|
+
if (!(cr1 & 1) || !ccEnabled || arr === 0) return 0;
|
|
104
|
+
return ccr / (arr + 1);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Wraps an I2C register-file device already registered via the
|
|
109
|
+
// `ext_devices.regfile` construction option (i2c_register_regfile must run
|
|
110
|
+
// before init() — see docs/components.md). `peripheral` must match the
|
|
111
|
+
// config's `peripheral` field. The DS3231 RTC in emulator.js is a built-in
|
|
112
|
+
// example of this same pattern; this is the generic, embedder-usable form.
|
|
113
|
+
export class I2cRegisterDevice {
|
|
114
|
+
constructor(emu, peripheral) {
|
|
115
|
+
this._regs = emu.i2cRegfile(peripheral);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
get(offset) { return this._regs.get(offset); }
|
|
119
|
+
set(offset, value) { this._regs.set(offset, value); }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Drives an ADC channel's value (0-4095, or a {min,max}-mapped range) via
|
|
123
|
+
// emu.setAdcChannel/clearAdcChannel — live, any time, unlike the SPI/I2C
|
|
124
|
+
// devices above (no "before init()" constraint; see docs/components.md).
|
|
125
|
+
// Without an override the channel falls back to the emulator's synthetic
|
|
126
|
+
// temp/vref/vbat/random defaults.
|
|
127
|
+
export class Potentiometer {
|
|
128
|
+
constructor(emu, peripheral, channel, { min = 0, max = 4095 } = {}) {
|
|
129
|
+
this.emu = emu;
|
|
130
|
+
this.peripheral = peripheral;
|
|
131
|
+
this.channel = channel;
|
|
132
|
+
this.min = min;
|
|
133
|
+
this.max = max;
|
|
134
|
+
this._value = min;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
get value() { return this._value; }
|
|
138
|
+
|
|
139
|
+
set value(v) {
|
|
140
|
+
this._value = Math.min(this.max, Math.max(this.min, v));
|
|
141
|
+
const raw = Math.round(((this._value - this.min) / (this.max - this.min || 1)) * 4095);
|
|
142
|
+
this.emu.setAdcChannel(this.peripheral, this.channel, raw);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
release() { this.emu.clearAdcChannel(this.peripheral, this.channel); }
|
|
146
|
+
}
|