fix(avr/serial): queue RX bytes so Serial.readStringUntil sees the whole input
avr8js's usart.writeByte(value) rejects the call (returns false, drops the byte) whenever rxBusyValue is set — and rxBusyValue stays true for one full cyclesPerChar after each accepted call. The old serialWrite() fed every character in a synchronous for-loop, so only the first byte made it through and the sketch saw 'h' when the user typed 'hello\n'. Buffer pending bytes in serialRxQueue and pump them one at a time: - serialWrite() now just queues + kicks drainSerialRxQueue once - drainSerialRxQueue calls writeByte on the head of the queue and only shifts it off if writeByte returned true (avr8js accepted it) - usart.onRxComplete is wired to drainSerialRxQueue so the next byte ships as soon as the sketch's RX side actually consumed the previous one — matches the cyclesPerChar pacing the real chip enforces Same handler wired in both USART setup paths (the Uno/Nano branch and the post-loadHex Mega/ATtiny branch). TX path (onByteTransmit + emitUartTxFrame for the oscilloscope waveform) is unchanged.
This commit is contained in:
parent
c7594c1dac
commit
76f6cd9a37
|
|
@ -269,6 +269,15 @@ export class AVRSimulator {
|
|||
private cpu: CPU | null = null;
|
||||
/** Peripherals kept alive by reference so GC doesn't collect their CPU hooks */
|
||||
private peripherals: unknown[] = [];
|
||||
/**
|
||||
* Pending RX bytes waiting to be fed to the USART. avr8js's writeByte
|
||||
* rejects (returns false, drops the byte) whenever rxBusyValue is set
|
||||
* — and rxBusyValue stays set for `cyclesPerChar` after each call.
|
||||
* A naive `for c of text: usart.writeByte(c)` loop therefore only
|
||||
* delivers the first character. We buffer the rest here and drain
|
||||
* one byte at a time on each frame's tick.
|
||||
*/
|
||||
private serialRxQueue: number[] = [];
|
||||
private portB: AVRIOPort | null = null;
|
||||
private portC: AVRIOPort | null = null;
|
||||
private portD: AVRIOPort | null = null;
|
||||
|
|
@ -434,6 +443,7 @@ export class AVRSimulator {
|
|||
// waveform during Serial.print. See emitUartTxFrame() for details.
|
||||
this.emitUartTxFrame(value);
|
||||
};
|
||||
this.usart.onRxComplete = () => this.drainSerialRxQueue();
|
||||
this.usart.onConfigurationChange = () => {
|
||||
if (this.onBaudRateChange && this.usart) this.onBaudRateChange(this.usart.baudRate);
|
||||
// Seed idle HIGH on the TX pin the first time TXEN flips on.
|
||||
|
|
@ -845,6 +855,7 @@ export class AVRSimulator {
|
|||
if (this.onSerialData) this.onSerialData(String.fromCharCode(value));
|
||||
this.emitUartTxFrame(value);
|
||||
};
|
||||
this.usart.onRxComplete = () => this.drainSerialRxQueue();
|
||||
this.usart.onConfigurationChange = () => {
|
||||
if (this.onBaudRateChange && this.usart) this.onBaudRateChange(this.usart.baudRate);
|
||||
this.handleUartConfigChange();
|
||||
|
|
@ -937,11 +948,34 @@ export class AVRSimulator {
|
|||
|
||||
/**
|
||||
* Send a byte to the Arduino serial port (RX) — as if typed in the Serial Monitor.
|
||||
*
|
||||
* AVR has no hardware RX FIFO, so avr8js's writeByte() rejects every
|
||||
* call while rxBusyValue is set (one full cyclesPerChar after the
|
||||
* previous byte). A naive loop would only deliver the first character.
|
||||
* Queue the bytes here and drain one at a time from onRxComplete.
|
||||
*/
|
||||
serialWrite(text: string): void {
|
||||
if (!this.usart) return;
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
this.usart.writeByte(text.charCodeAt(i));
|
||||
this.serialRxQueue.push(text.charCodeAt(i));
|
||||
}
|
||||
this.drainSerialRxQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pump the next pending RX byte into the USART. Called once from
|
||||
* serialWrite() to kick the pipeline, then re-armed from
|
||||
* usart.onRxComplete after every byte the sketch actually receives.
|
||||
* The cyclesPerChar gap that avr8js enforces between writeByte calls
|
||||
* gives the sketch time to read UDR0 between bytes — same pacing the
|
||||
* real chip sees at the configured baud rate.
|
||||
*/
|
||||
private drainSerialRxQueue(): void {
|
||||
if (!this.usart) return;
|
||||
if (this.serialRxQueue.length === 0) return;
|
||||
const next = this.serialRxQueue[0];
|
||||
if (this.usart.writeByte(next)) {
|
||||
this.serialRxQueue.shift();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue