83 lines
2.1 KiB
TypeScript
83 lines
2.1 KiB
TypeScript
/**
|
|
* SPIBus — virtual SPI bus for custom chips.
|
|
*
|
|
* Each chip with vx_spi_attach() registers a SPIDevice. The active slave is
|
|
* the one that has a pending transfer (set up via vx_spi_start()) — typically
|
|
* gated by the chip's own CS pin watch.
|
|
*
|
|
* In the MVP, this bus is *not* connected to the AVR's SPI master peripheral
|
|
* — that integration requires hooking avr8js AVRSPI which behaves differently
|
|
* from AVRTWI. For now, tests/chip-to-chip flows can drive the bus via
|
|
* `transferByte`. AVR-driven SPI is a phase-2 follow-up.
|
|
*/
|
|
|
|
export class SPIDevice {
|
|
private _pending: {
|
|
buffer: Uint8Array;
|
|
count: number;
|
|
position: number;
|
|
onDone: (buffer: Uint8Array, count: number) => void;
|
|
} | null = null;
|
|
|
|
startTransfer(
|
|
buffer: Uint8Array,
|
|
count: number,
|
|
onDone: (buffer: Uint8Array, count: number) => void,
|
|
): void {
|
|
this._pending = { buffer, count, position: 0, onDone };
|
|
}
|
|
|
|
stopTransfer(): void {
|
|
const t = this._pending;
|
|
if (!t) return;
|
|
this._pending = null;
|
|
t.onDone(t.buffer, t.position);
|
|
}
|
|
|
|
hasPendingTransfer(): boolean {
|
|
return this._pending !== null;
|
|
}
|
|
|
|
transfer(masterByte: number): number {
|
|
const t = this._pending;
|
|
if (!t) return 0xff;
|
|
const slaveByte = t.buffer[t.position];
|
|
t.buffer[t.position] = masterByte & 0xff;
|
|
t.position++;
|
|
if (t.position >= t.count) {
|
|
const buf = t.buffer;
|
|
const cnt = t.count;
|
|
const cb = t.onDone;
|
|
this._pending = null;
|
|
cb(buf, cnt);
|
|
}
|
|
return slaveByte;
|
|
}
|
|
}
|
|
|
|
export class SPIBus {
|
|
private devices = new Set<SPIDevice>();
|
|
|
|
addDevice(dev: SPIDevice): void {
|
|
this.devices.add(dev);
|
|
}
|
|
|
|
removeDevice(dev: SPIDevice): void {
|
|
this.devices.delete(dev);
|
|
}
|
|
|
|
/** Transfer one byte. Returns the active slave's response on MISO. */
|
|
transferByte(masterByte: number): number {
|
|
for (const d of this.devices) {
|
|
if (d.hasPendingTransfer()) return d.transfer(masterByte);
|
|
}
|
|
return 0xff;
|
|
}
|
|
|
|
transferBytes(bytes: number[]): number[] {
|
|
const out: number[] = [];
|
|
for (const b of bytes) out.push(this.transferByte(b));
|
|
return out;
|
|
}
|
|
}
|