Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 05:57 23 Aug 2026 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : Bytes lost from the serial port to a file

Author Message
homa

Guru

Joined: 05/11/2021
Location: Germany
Posts: 672
Posted: 09:51pm 21 Aug 2026
Copy link to clipboard 
Print this post

Hi Peter,
I tried to write a data stream from the serial port to a file, but I always lose a few bytes in the process—even with PRINT MEMORY. Then I had the idea to write it not to the flash disk, but to the SD card, where it now works perfectly.

Is this because of this explanation I tried to find using AI?
I hope the translation is good enough...
I would really appreciate some confirmation or an explanation from you. Thanks in advance!

Matthias



The UART hardware (typically a PL011-type UART on the RP2350 with a very small hardware FIFO, often only 16–32 bytes) must be read. This hardware FIFO represents a bottleneck: 32 bytes × 320 µs = 10,240 µs ≈ 10.2 ms.

This means that if more than ~10 ms elapses between two reads of the hardware FIFO (performed by the underlying MMBasic firmware, not directly by your Midiin subroutine), the tiny hardware FIFO overflows before your 4,096-byte software buffer even notices it. This happens silently, without an error message.

That would explain why:

a 4096-byte software buffer (with a 1.3-second reserve) is insufficient, since the bytes are already lost at the hardware level beforehand.
The losses occur irregularly and are consistent with normal SD write latencies (a range from a few milliseconds to the low double-digit millisecond range is sufficient).
Some SD/SPI drivers temporarily disable global interrupts during critical transfer phases (which is common during bit-banging or DMA setup phases). If MIDI bytes arrive precisely during this window, even a “real” hardware interrupt cannot respond in time, and the 10-ms hardware FIFO buffer is quickly exhausted.

That’s a very good question—and yes, that could indeed be the crucial point. I’ve looked at the source code for the PicoMite/MMBasic firmware (the basis for RP2350 MMBasic devices like PicoCalc) as well as the underlying Raspberry Pi Pico SDK documentation on flash access.


The fundamental difference: A: (Flash) vs. B: (SD card)

A: Internal flash memory

The RP2350’s internal flash chip operates in XIP (Execute-In-Place) mode. This means that the processor executes its own program code directly from the flash while it is running. This has a serious hardware consequence, which is explicitly mentioned in the official Pico SDK documentation:

“Flash cannot be erased or written to when in XIP mode. […] It is therefore critical that no code or data is being read from flash while flash is being written or erased.”

And further, specifically regarding interrupts:

“They are unsafe if you have interrupt handlers or an interrupt vector table in flash, so you must disable interrupts before calling [flash_range_program/erase]”

This means: Every write or erase operation on the internal flash forces the SDK to completely disable all interrupts on the writing core. This is not optional but physically necessary, since the flash chip cannot be read simultaneously while it is being written to or erased (not even for interrupt vectors or handlers that are themselves stored in flash!). A flash sector erase typically takes 10–100+ ms, while a pure program (without erase) tends to take 1–3 ms. However, as soon as an erase cycle is due (which happens regularly with sequential data appending), the interrupts are completely disabled for the entire duration.

Interestingly, there is even a note to this effect in the README of the PicoMite source code: It describes that the GPIO interrupt handler had to be deliberately placed in RAM rather than in flash—an indication that interrupt/flash conflicts are a known and actively addressed issue in this codebase.

B: (SD Card)

The SD card, on the other hand, is connected to an external SPI/SDIO bus. It is a standard peripheral device through which data transfer occurs not via the internal flash address space, but via a separate hardware component. No global interrupts need to be disabled for this access. Your Midiin interrupt can continue to fire during an SD write operation and retrieve bytes from the UART hardware.

Why this matches your symptoms exactly: A 10 ms hardware FIFO buffer (from my previous calculation) is more than enough to handle normal SD card latencies (which occur even with SPI without an interrupt disable), but not a flash erase cycle of, say, 50 ms, during which literally nothing responds.
It is precisely in those moments when a flash write operation coincides with a dense burst of notes that bytes are lost.
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 11776
Posted: 10:06pm 21 Aug 2026
Copy link to clipboard 
Print this post

what s/w? what version? Options if applicable? Example code?
Not a mind reader
Edited 2026-08-22 08:07 by matherp
 
homa

Guru

Joined: 05/11/2021
Location: Germany
Posts: 672
Posted: 06:57pm 22 Aug 2026
Copy link to clipboard 
Print this post

  matherp said  ... Not a mind reader


Why not? That would be impressive!
I’ve been thinking about this problem and how to optimise it for the last two days, so I simply forgot all about it.

> option list
PicoMite MMBasic RP2350A V6.03.01
OPTION LCD SPI GP10,GP11,GP12
OPTION SYSTEM I2C GP6,GP7, SLOW
OPTION FLASH SIZE 4194304
OPTION LIBRARY_FLASH_SIZE  4B000
OPTION COLOURCODE ON
OPTION DEFAULT COLOURS GREEN, BLACK
OPTION KEYBOARD PICOCALC
OPTION HEARTBEAT OFF
OPTION CPUSPEED (KHz) 252000
OPTION LCDPANEL CONSOLE ,, FF00
OPTION DISPLAY 26, 40
OPTION LCDPANEL ST7365P, PORTRAIT,GP14,GP15,GP13
OPTION BACKLIGHT KBD 32
OPTION BACKLIGHT LCD 64
OPTION SDCARD GP17, GP18, GP19, GP16
OPTION AUDIO GP26,GP27', ON PWM CHANNEL 5
OPTION MODBUFF ENABLE  192
OPTION PLATFORM PicoCalc
>


' midiRec3v02.bas

' Ring buffer setup (4096 entries for bytes and timestamps)
Dim rb_byte%(4095)
Dim rb_time%(4095)
Dim rb_head% = 0
Dim rb_tail% = 0

' Open the serial interface using a 1-byte interrupt trigger!
SetPin GP4, GP5, COM2
Open "COM2:31250, 4096, midiin, 1" As #2
Open "temp.bin" For RANDOM As #3  

' Flash-Init dummy start
Dim dummy%(7)
For i% = 0 To 7: dummy%(i%) = 0: Next i%
Memory PRINT #3, 8, dummy%()

' OPTIMISATION: Micro-burst buffer (only 32 events = 256 bytes)
Dim buffer%(31)
Dim idx% = 0

Dim st% = 0, rs% = 0
Dim dat1% = 0, exp% = 0
Dim t_evt% = 0
Dim run_flag% = 0

' Variables for loop reception
Dim b% = 0
Dim t_incoming% = 0

Print Chr$(27)+"[2J"+Chr$(27)+"[H"
CLS
Print "midiRec3v02 Ready (Micro-Burst Mode)..."
Timer = 0

Do
 If LCASE$(Inkey$) = "s" Then Exit Do

' Check whether the ring buffer contains any new data'
 If rb_head% = rb_tail% Then Continue Do

' Retrieve a byte and a precise timestamp from the ring buffer
 b% = rb_byte%(rb_tail%)
 t_incoming% = rb_time%(rb_tail%)
 rb_tail% = (rb_tail% + 1) Mod 4096

 If b% >= &HF8 Then Continue Do

 If b% >= &HF0 And b% <= &HF7 Then
   rs% = 0
   If b% = &HF0 Then st% = 3
   If b% = &HF7 Then st% = 0
   Continue Do
 EndIf
 If st% = 3 And b% < &H80 Then Continue Do

 ' Status-Byte
 If b% >= &H80 Then
   rs% = b%
   st% = 1
   run_flag% = 0
   t_evt% = t_incoming%
   
   Select Case (b% And &HF0)
     Case &HC0, &HD0: exp% = 1
     Case Else :      exp% = 2
   End Select
   Continue Do
 EndIf

 If rs% = 0 Then Continue Do

 If st% = 1 Then
   If run_flag% = 1 Then
     t_evt% = t_incoming%
     run_flag% = 0
   EndIf
   
   dat1% = b%
   
   If exp% = 1 Then
     buffer%(idx%) = t_evt% Or (rs% << 32) Or (dat1% << 40)
     idx% = idx% + 1
     st% = 1
     run_flag% = 1
   Else
     st% = 2
   EndIf

 ElseIf st% = 2 Then
   buffer%(idx%) = t_evt% Or (rs% << 32) Or (dat1% << 40) Or (b% << 48)
   idx% = idx% + 1
   st% = 1
   run_flag% = 1
 EndIf

' MICRO-BURST WRITE: Write as few as 32 events (256 bytes)!
 If idx% = 32 Then
   Memory PRINT #3, 256, buffer%()
   idx% = 0
 EndIf
Loop

Close #2

' Run the remaining data in the ring buffer through the state machine in its entirety'
Do While rb_head% <> rb_tail%
 b% = rb_byte%(rb_tail%)
 t_incoming% = rb_time%(rb_tail%)
 rb_tail% = (rb_tail% + 1) Mod 4096
 
 If b% >= &HF8 Then Continue Do
 If b% >= &HF0 And b% <= &HF7 Then
   rs% = 0
   If b% = &HF0 Then st% = 3
   If b% = &HF7 Then st% = 0
   Continue Do
 EndIf
 If st% = 3 And b% < &H80 Then Continue Do

 If b% >= &H80 Then
   rs% = b%
   st% = 1
   run_flag% = 0
   t_evt% = t_incoming%
   Select Case (b% And &HF0)
     Case &HC0, &HD0: exp% = 1
     Case Else :      exp% = 2
   End Select
   Continue Do
 EndIf

 If rs% = 0 Then Continue Do

 If st% = 1 Then
   If run_flag% = 1 Then
     t_evt% = t_incoming%
     run_flag% = 0
   EndIf
   dat1% = b%
   If exp% = 1 Then
     buffer%(idx%) = t_evt% Or (rs% << 32) Or (dat1% << 40)
     idx% = idx% + 1
     st% = 1
     run_flag% = 1
   Else
     st% = 2
   EndIf
 ElseIf st% = 2 Then
   buffer%(idx%) = t_evt% Or (rs% << 32) Or (dat1% << 40) Or (b% << 48)
   idx% = idx% + 1
   st% = 1
   run_flag% = 1
 EndIf
 
 If idx% = 32 Then
   Memory PRINT #3, 256, buffer%()
   idx% = 0
 EndIf
Loop

' Write the remaining events to the event buffer
If idx% > 0 Then
 Memory PRINT #3, idx% * 8, buffer%()
EndIf

Close #3
Print "Saved temp.bin"
End

SUB midiin
 Local t%
 Local b_in%
 Local next_head%
 
 Do While Loc(#2) > 0
   t% = Fix(Timer) And &HFFFFFFFF
   b_in% = Asc(Input$(1, #2))
   next_head% = (rb_head% + 1) Mod 4096
   
   If next_head% <> rb_tail% Then
     rb_byte%(rb_head%) = b_in%
     rb_time%(rb_head%) = t%
     rb_head% = next_head%
   EndIf
 Loop
END SUB


Comparison between the transmitted data stream and the recording:




As soon as I switch the working directory from the flash drive (drive a:) to the SD card (b:), I no longer have any missing data!

Hence my question to the AI and my attempt to verify this explanation with you.

Matthias
 
Print this page


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2026