Download js/gpio.js from HelloSun/esp32emu: direct link, hf CLI and curl.
- Browser
- Download file 6.53 kB
-
https://huggingface.co/spaces/HelloSun/esp32emu/resolve/main/js/gpio.js
- Command line
-
hf download hf://spaces/HelloSun/esp32emu/js/gpio.js
-
curl -L -o gpio.js https://huggingface.co/spaces/HelloSun/esp32emu/resolve/main/js/gpio.js
6.53 kB
| // GPIO matrix, IO_MUX and pad model | |
| export class GPIO { | |
| constructor(soc) { this.soc = soc; this.iomux = new IoMux(soc); this.listeners = []; this.reset(); } | |
| reset() { | |
| this.r = new Int32Array(0x400); | |
| this.out = new Uint32Array(2); this.en = new Uint32Array(2); | |
| this.pin = new Int32Array(40); | |
| this.inSel = new Int32Array(256); this.outSel = new Int32Array(40); | |
| for (let i = 0; i < 40; i++) this.outSel[i] = 256; | |
| this.status = new Uint32Array(2); | |
| this.extLow = new Uint32Array(2); // external devices pulling pad low | |
| this.extHigh = new Uint32Array(2); // external devices driving pad high | |
| this.pullup = new Uint32Array(2); | |
| this.strap = 0x12; | |
| this.lastLevel = new Uint32Array(2); | |
| this.sigOut = new Map(); // signal -> value (peripheral outputs) | |
| this.pad = new Uint8Array(40); | |
| this.matrixVersion = (this.matrixVersion || 0) + 1; | |
| } | |
| // pad electrical level (0/1) | |
| level(n) { | |
| const w = n >> 5, b = 1 << (n & 31); | |
| const drv = this.driving(n); | |
| if (drv === 0) return 0; if (drv === 1) { return (this.extLow[w] & b) ? 0 : 1; } | |
| // not driven by chip | |
| if (this.extLow[w] & b) return 0; | |
| if (this.extHigh[w] & b) return 1; | |
| if (this.pullup[w] & b) return 1; | |
| return (this.floating ? this.floating[n] : 0) | 0; | |
| } | |
| // returns 0, 1 or -1 (high-z) | |
| driving(n) { | |
| const rio = this.soc.rtcio; | |
| if (rio && rio.drives(n)) return rio.outLevel(n); | |
| const sel = this.outSel[n]; | |
| const sig = sel & 0x1FF; | |
| let en, val; | |
| if (sig === 256 || sig === 0x100) { en = (this.en[n >> 5] >>> (n & 31)) & 1; val = (this.out[n >> 5] >>> (n & 31)) & 1; } | |
| else { | |
| const oenSel = (sel >> 10) & 1; | |
| en = oenSel ? ((this.en[n >> 5] >>> (n & 31)) & 1) : 1; | |
| if (((sel >> 11) & 1)) en ^= 1; | |
| val = this.sigOut.get(sig) | 0; if ((sel >> 9) & 1) val ^= 1; | |
| } | |
| if (!en) return -1; | |
| if ((this.pin[n] >> 2) & 1) return val ? -1 : 0; // open drain | |
| return val; | |
| } | |
| inputLevel(n) { return this.level(n); } | |
| reg32Read(off) { | |
| switch (off) { | |
| case 0x04: return this.out[0]; case 0x10: return this.out[1]; | |
| case 0x20: return this.en[0]; case 0x2c: return this.en[1]; | |
| case 0x38: return this.strap; | |
| case 0x3c: return this.inReg(0); case 0x40: return this.inReg(1); | |
| case 0x44: return this.status[0]; case 0x50: return this.status[1]; | |
| case 0x60: return this.intFor(1, 0); case 0x68: return this.intFor(0, 0); | |
| case 0x74: return this.intFor(1, 1); case 0x7c: return this.intFor(0, 1); | |
| } | |
| if (off >= 0x88 && off < 0x88 + 40 * 4) return this.pin[(off - 0x88) >> 2]; | |
| if (off >= 0x130 && off < 0x530) return this.inSel[(off - 0x130) >> 2]; | |
| if (off >= 0x530 && off < 0x530 + 40 * 4) return this.outSel[(off - 0x530) >> 2]; | |
| return this.r[off >> 2]; | |
| } | |
| inReg(w) { let v = 0; for (let i = 0; i < 32; i++) { const n = w * 32 + i; if (n < 40 && this.level(n)) v |= (1 << i); } return v | 0; } | |
| intFor(cpu, w) { | |
| // cpu 0 = PRO (int_ena bit 2, nmi bit3), cpu 1 = APP (bit 0, nmi bit1) | |
| let v = 0; | |
| const st = this.status[w]; | |
| for (let i = 0; i < 32; i++) { | |
| const n = w * 32 + i; if (n >= 40) break; | |
| if (!((st >>> i) & 1)) continue; | |
| const ena = (this.pin[n] >> 13) & 31; | |
| if (cpu === 0 ? (ena & 4) : (ena & 1)) v |= (1 << i); | |
| } | |
| return v | 0; | |
| } | |
| read(off) { return this.reg32Read(off); } | |
| write(off, v) { | |
| v |= 0; | |
| const old = this.snapshot(); | |
| switch (off) { | |
| case 0x04: this.out[0] = v; break; case 0x08: this.out[0] |= v; break; case 0x0c: this.out[0] &= ~v; break; | |
| case 0x10: this.out[1] = v & 0xFF; break; case 0x14: this.out[1] |= v & 0xFF; break; case 0x18: this.out[1] &= ~v; break; | |
| case 0x20: this.en[0] = v; break; case 0x24: this.en[0] |= v; break; case 0x28: this.en[0] &= ~v; break; | |
| case 0x2c: this.en[1] = v & 0xFF; break; case 0x30: this.en[1] |= v & 0xFF; break; case 0x34: this.en[1] &= ~v; break; | |
| case 0x48: this.status[0] |= v; break; case 0x4c: this.status[0] &= ~v; break; | |
| case 0x54: this.status[1] |= v; break; case 0x58: this.status[1] &= ~v; break; | |
| default: | |
| if (off >= 0x88 && off < 0x88 + 40 * 4) this.pin[(off - 0x88) >> 2] = v; | |
| else if (off >= 0x130 && off < 0x530) this.inSel[(off - 0x130) >> 2] = v; | |
| else if (off >= 0x530 && off < 0x530 + 40 * 4) { this.outSel[(off - 0x530) >> 2] = v; this.matrixVersion++; } | |
| else this.r[off >> 2] = v; | |
| } | |
| this.padsChanged(old); | |
| this.updateInt(); | |
| } | |
| snapshot() { const a = new Uint8Array(40); for (let i = 0; i < 40; i++) a[i] = this.level(i); return a; } | |
| // called whenever pad levels might have changed (chip drive or external) | |
| padsChanged(old) { | |
| old = old || this.lastSnap; | |
| const now = this.snapshot(); | |
| this.lastSnap = now; | |
| if (!old) return; | |
| for (let n = 0; n < 40; n++) { | |
| if (old[n] === now[n]) continue; | |
| const w = n >> 5, b = 1 << (n & 31); | |
| const type = (this.pin[n] >> 7) & 7; | |
| if ((type === 1 && now[n]) || (type === 2 && !now[n]) || type === 3) this.status[w] |= b; | |
| for (const l of this.listeners) l(n, now[n]); | |
| } | |
| for (const t of this.levelWatch || []) t(); | |
| } | |
| updateInt() { | |
| // level interrupts | |
| for (let n = 0; n < 40; n++) { | |
| const type = (this.pin[n] >> 7) & 7; const w = n >> 5, b = 1 << (n & 31); | |
| if (type === 4 && !this.level(n)) this.status[w] |= b; | |
| else if (type === 5 && this.level(n)) this.status[w] |= b; | |
| } | |
| this.soc.setIrqCpu(0, 22, this.intFor(0, 0) | this.intFor(0, 1)); | |
| this.soc.setIrqCpu(1, 22, this.intFor(1, 0) | this.intFor(1, 1)); | |
| } | |
| // external drive from devices | |
| setExt(n, low) { const w = n >> 5, b = 1 << (n & 31); const old = this.snapshot(); if (low) this.extLow[w] |= b; else this.extLow[w] &= ~b; this.padsChanged(old); this.updateInt(); } | |
| setPullup(n, on) { const w = n >> 5, b = 1 << (n & 31); if (on) this.pullup[w] |= b; else this.pullup[w] &= ~b; } | |
| setSignalOut(sig, v) { this.sigOut.set(sig, v); } | |
| // peripheral input: value for input signal (e.g. UART RX) = pad level via inSel | |
| signalIn(sig) { | |
| const s = this.inSel[sig]; const pad = s & 0x3F; | |
| if (s & 0x40) return 1; // constant high | |
| if (pad === 0x30) return 0; // constant low | |
| if (pad === 0x38 || (s & 0x40)) return 1; | |
| return this.level(pad); | |
| } | |
| } | |
| class IoMux { | |
| constructor(soc) { this.soc = soc; this.r = new Int32Array(0x400); } | |
| read(off) { return this.r[off >> 2]; } | |
| write(off, v) { this.r[off >> 2] = v; } | |
| } | |