Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 03:45 09 Dec 2025 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 : 3rd UART via PIO

     Page 3 of 3    
Author Message
phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 2851
Posted: 06:27am 31 Jan 2025
Copy link to clipboard 
Print this post

Have been playing with both .09 and .10.
PIO STOP 1,0 at the start of Sub UART_TX(tx$) doesn't appear to make any difference now.
When sending from Com2 to PIO for the first time there is a "framing error" but the data is received anyway.

v0.10 on Pico2 VGA - b5 with Com2.
 'PIO 8n1 UART ' v0.10  based on RP2040 datasheet 3.6.3 and 3.6.4
 'requires PicoMite MMBasic 6.00.02 beta 0 to PIO assemble

 'version control
 ' v0.0        sending from individual fifo writes works
 ' v0.1        sending from DMA works.
 ' v0.2        faster TX conversion string to dma fifo (idea: Peter)
 ' v0.3        UART RX experiments 8 deep fifo (needs V6.00.02b0 or newer)
 ' v0.4        UART_RX using DMA works, even at 115200
 ' v0.5        cleanup of the code
 ' v0.6        RX ringbuffer
 ' v0.7        improve RX robustness v0.6=+1/-5% 0.7=+4/-4% (16*rx_bau_rate)
 ' v0.8        Communicate framing errors through IRQ register
 ' v0.9        start PIO sequence changed, PIO stop for TX DMA
 ' v0.10       Change PIO ASSEMBLE to PIO PROGRAM to compact more


 Option default integer

 'PIO program uses 2 state machines, one for RX and one for TX
 'baud_rate = 19200 '115200
 rx_buf_size = 128 '256 'characters
 tx_buf_size = 128 '256 'characters
 tx_baud_rate = 230400
 rx_baud_rate = tx_baud_rate '19200*0.96 to test robustness



 'memory buffer reservation

 'DMA buffers (packed) for 128 characters maximum
 Dim tx_pck%(tx_buf_size/2 - 1)            'packed TX array, 2 char per integer
 Const tx_pck_ad%=Peek(varaddr tx_pck%())  'memory address of array start

 Dim rx_pck%                               'packed RX array, 2 char per integer
 PIO make ring buffer rx_pck%,rx_buf_size*4    'as ring buffer, size in bytes
 Const rx_pck_ad%=Peek(varaddr rx_pck%())  'memory address of array start
 Dim old_rx% = rx_pck_ad%                  'old read pointer, default at start
 Const rx_pck_end%=rx_pck_ad%+rx_buf_size*4'last location of rx buffer in bytes

 Dim rx$ = "string for received characters"'string for received characters
 Const rx_ad%=Peek(varaddr rx$)            'memory address of string


 'make sure all fifo's and program memory are cleared, then program PIO
 PIO clear 1
 INIT_PIO


 '---------------------------- example code -------------------------------

 SetPin gp5,gp4, COM2 : Open "COM2:tx_baud_rate" As #5 'gp1 & gp5 joined, gp4 & gp2 joined
 'test text string
 Dim a$ = "Hello World, I admire your beautiful beaches" + Chr$(13)
 Dim dat$, b$ = "The quick brown fox jumps over the lazy dog. ", tmr!

 UART_TX("test PIO to Com2 - OK") : Pause 9: dat$ = Input$(128, #5) :Print dat$
 Print #5, "Test Com2 to PIO - OK" : Print : Pause 9
 If UART_RX%() Then
   Print rx$
  Else
   Print "Framing Error - but rx$ received anyway eg:- ";rx$
   Print "So do Framing Errors matter?" :Print
   Poke word pio_1_irq,255     'clear framing error
   Pause 3000
 End If

 Pause 1000

 'main loop: send string repeatedly
 Do
   tmr! = Timer + 2000
   '---------------------- send a string to Com2 -----------------

   dat$ = "" : UART_TX(a$) : Pause 19: dat$ = Input$(128, #5) :Print dat$

   Pause 50


   '--------------------- receive a string from Com2 ----------------

   Print #5, b$+b$ : Pause 19
   'print characters in the receive buffer if no framing error
   'UART_RX%() uses the global variable rx$ to assemble all data in.
   If UART_RX%() Then
     Print rx$                   'print the received string
   Else
     Print "framing error"
     Poke word pio_1_irq,255     'clear framing error
   End If

   Do While Timer < tmr! : Loop

 Loop




 '-------------------------- subs -------------------------------------------

 'sends string using DMA to PIO TX
Sub UART_TX(tx$)

'  PIO STOP 1,0  'does not appear to be needed

 'get start and size of string to be sent
 tx_l% = Len(tx$) : tx_ad% = Peek(varaddr tx$)

 'pack string for sending to UART
 Memory copy tx_ad%+1, tx_pck_ad%, tx_l%, 1, 4

 'start the DMA
 PIO init machine 1,0,f0,p0,e0,s0,0        'SM0 start through DMA at defined state
 PIO dma tx 1, 0, tx_l%, tx_pck%(),        'start SM0, send string through DMA

End Sub



Function UART_RX%()

 Local LOF%, LOF1%, LOF2%, new_rx%

 new_rx% = Pio(DMA RX POINTER)   'get current position in ring buffer

 Select Case new_rx%-old_rx%
   Case 0
     'no new data
     rx$=""

   Case >0
     'new data, no wrap around
     LOF% = (new_rx% - old_rx%) \ 4

     'unpack RX buffer to string
     Memory copy old_rx%,rx_ad%+1,LOF%,4,1           'fill string characters
     Poke byte rx_ad%,LOF%                           'fill len(string)

   Case <0
     'new data, with wrap around, needs split copying.
     LOF1% = (rx_pck_end% - old_rx%) \ 4             'begin text until end of buffer
     LOF2% = (new_rx% - rx_pck_ad%) \ 4              'start buffer to end of text
     LOF% = LOF1% + LOF2%                            'total size of text

     'unpack RX buffer to string
     Memory copy old_rx%,rx_ad%+1,LOF1%,4,1          'fill start string characters
     Memory copy rx_pck_ad%,rx_ad%+1+LOF1%,LOF2%,4,1 'fill end string characters
     Poke byte rx_ad%,LOF%                           'fill len(string)

 End Select

 old_rx% = new_rx%   'reposition read pointer

 UART_RX% = (Peek(word pio_1_irq)=0)     'UART status 1=okay, 0=framing error

End Function


 'program PIO 1 SM0 and 1, and start the UART
Sub INIT_PIO

 'use PIO IRQ register to transfer framing errors
 pio_1_irq% = &h30 + &h50300000  'register + PIO1 offset
 Poke word pio_1_irq,255         'erase all flag

 'define PIO output pin
 SetPin GP0,pio1

 'PIO program, for source code see PIO_UART_09.bas
 Dim prog%(7) = (&h6001F7279FA0E081,&h4001F42720200643,&h20A0C00000CD0E47,&h800040780005,0,0,0,0)
 PIO program 1,prog%()

 'configure pio0 StateMachine 0
 f0=tx_baud_rate*8
 p0=Pio(PINCTRL 2,1,1,,gp0,gp0,gp0)  'note:2=include side enable bit
 e0=Pio(execctrl gp1,1,4,0,1 )       '1=side set only when defined
 s0=Pio(shiftctrl 0,0,0,0,0,1,0,0)   'shift OUT direction LSB first
 PIO init machine 1,0,f0,p0,e0,s0,0  'SM0 start at address 0

 'configure pio0 StateMachine 1
 f1=rx_baud_rate*16
 p1=Pio(PINCTRL 0,,,gp1,,,)          'gp1 = IN pin
 e1=Pio(execctrl gp1,5,14)           'gp1 = JMP pin
 s1=Pio(shiftctrl 0,0,0,0,1,0,0,0)   'shift IN LSB first
 PIO init machine 1,1,f1,p1,e1,s1,5  'SM1 start at address 5

 ' start the PIO UART
 PIO dma rx 1,1,0,rx_pck%(),,32,rx_buf_size%  'start the RX PIO with DMA ringbuffer

End Sub

Edited 2025-01-31 16:30 by phil99
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5518
Posted: 08:32am 31 Jan 2025
Copy link to clipboard 
Print this post

Hi Phil,

There was a change in the assembler. Peter changed it, and 6.00.02b5 works correct now. The assembler improves constantly, in small steps. I think we are close to "the perfect assembler". It is on my list comming weeks to also look into the interrupt flags.
I am working on an improved frequency counter that should have a consistent 0.2ppm resolution accros the whole frequency range (~7 digits accurate) without external hardware. The old PIO counter had less resolution in the 100Hz..100kHz range (5 digits), but was very accurate at 1Hz and above 10MHz.

Volhout

EDIT: The framing error is caused by INIT_PIO (starts RX DMA) before SETPIN GP4,GP5,COM2:OPEN "COM2
There will be a "BREAK" character received, but that is not pushed into the FIFO. Since it is the first character, you do not see the difference. When this happens in the middle of a string, you will be missing 1 or more characters (until re-sync). That is when the framing error is important. That message is corrupt.
Edited 2025-01-31 18:53 by Volhout
PicomiteVGA PETSCII ROBOTS
 
phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 2851
Posted: 11:13am 31 Jan 2025
Copy link to clipboard 
Print this post

Thanks for the explanations.
When sending from Com2 to PIO UART_RX for the first time an initial dummy message solves the first framing error.
eg before the main loop.      Print #5, "" : Pause 9 'at the Tx end
    Poke word pio_1_irq,255     'clear framing error at the Rx end
 
homa

Guru

Joined: 05/11/2021
Location: Germany
Posts: 507
Posted: 11:16pm 04 Dec 2025
Copy link to clipboard 
Print this post

I'm too stupid. I'm trying to use a long string a%() instead of a string a$, and I'm failing to adjust the sender routine. Can anyone help me here?
Because my target long string is empty!?


Sub UART_TX(tx$)

'  PIO STOP 1,0  'does not appear to be needed

'get start and size of string to be sent
tx_l% = Len(tx$) : tx_ad% = Peek(varaddr tx$)

'pack string for sending to UART
Memory copy tx_ad%+1, tx_pck_ad%, tx_l%, 1, 4

'start the DMA
PIO init machine 1,0,f0,p0,e0,s0,0        'SM0 start through DMA at defined state
PIO dma tx 1, 0, tx_l%, tx_pck%(),        'start SM0, send string through DMA

End Sub


-->


Sub UART_TX(tx%())

'  PIO STOP 1,0  'does not appear to be needed

'get start and size of string to be sent
tx_l% = LLen(tx%()) : tx_ad% = Peek(varaddr tx%())

'pack string for sending to UART
Memory copy tx_ad%, tx_pck_ad%, tx_l%, 4, 4

'start the DMA
PIO init machine 1,0,f0,p0,e0,s0,0        'SM0 start through DMA at defined state
PIO dma tx 1, 0, tx_l%, tx_pck%(),        'start SM0, send string through DMA

End Sub


Matthias
 
phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 2851
Posted: 12:33am 05 Dec 2025
Copy link to clipboard 
Print this post

Perhaps memory is allocated differently for strings and long strings.

As a workaround cut the long string into a series of strings (maybe 128 characters each) and send one at a time. Then reassemble into a long string at the other end.

Edit.
original
'pack string for sending to UART
Memory copy tx_ad%+1, tx_pck_ad%, tx_l%, 1, 4
your code
'pack string for sending to UART
Memory copy tx_ad%, tx_pck_ad%, tx_l%, 4, 4
I think this is only copying every fourth byte from the source. Is there a reason for that?

So perhaps:-
'pack long string for sending to UART
Memory copy tx_ad()%, tx_pck_ad%, tx_l%, 1, 4

Edited 2025-12-05 14:06 by phil99
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5518
Posted: 07:58am 05 Dec 2025
Copy link to clipboard 
Print this post

  phil99 said  Perhaps memory is allocated differently for strings and long strings.


When I remember well, strings allocate a 256 byte block, with at position 0 the length, the remaining bytes (1...255) are string content. That is why the MEM COPY starts at position tx_ad%+1.
A long string is in essence an integer array, that starts data at position 0 (like all arrays). I do not have knowledge where the string length is stored, but I expect it in the header (the block of 64 bytes that has the array name and other size parameters in it).

Volhout
Edited 2025-12-05 18:19 by Volhout
PicomiteVGA PETSCII ROBOTS
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10685
Posted: 08:23am 05 Dec 2025
Copy link to clipboard 
Print this post

A longstring stores the length in the first integer in the array
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5518
Posted: 08:34am 05 Dec 2025
Copy link to clipboard 
Print this post

  matherp said  A longstring stores the length in the first integer in the array


Thank you Peter,

Is it correct then to get the length from byte position 0..7 as a 64 bit integer, and the string data at byte positions 8...32767 ?

Regards,

Volhout
Edited 2025-12-05 18:35 by Volhout
PicomiteVGA PETSCII ROBOTS
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10685
Posted: 08:35am 05 Dec 2025
Copy link to clipboard 
Print this post

Exactly
 
homa

Guru

Joined: 05/11/2021
Location: Germany
Posts: 507
Posted: 05:01pm 05 Dec 2025
Copy link to clipboard 
Print this post

thx!
 
homa

Guru

Joined: 05/11/2021
Location: Germany
Posts: 507
Posted: 04:33pm 07 Dec 2025
Copy link to clipboard 
Print this post

I have questions about the fifth option ‘completioninterrupt’
in the command: PIO dma tx
Could this be buggy? Because it disrupts the serial transmission when I want to use it. So the level then goes back to 0?
I actually wanted to know when the string had been sent so that I could send another one and not send the next one while the previous one was still being sent.


> option list
WebMite MMBasic RP2040 Edition V6.01.00RC21
OPTION SYSTEM SPI GP2,GP3,GP4
OPTION COLOURCODE ON
OPTION CPUSPEED (KHz) 200000
OPTION SDCARD GP5


AND

I can't get the command on page 200 of the manual to work. Where and when do I need to use this command?
This sub in parameter 4 is never called?

Similar for TX interrupt in which case MMBasic gets an interrupt when data is needed for the TX FIFO.
PIO INTERRUPT a,b,c,d
a/ PIO
(0 or 1)
b/ state machine
(0...3)
c/ Name of RX interrupt handler (i.e. "myRX_Interrupt" or 0 to disable)
d/ Name of TX interrupt handler (i.e. "myTX_Interrupt" or 0 to disable)
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10685
Posted: 04:45pm 07 Dec 2025
Copy link to clipboard 
Print this post

  Quote  Could this be buggy? Because it disrupts the serial transmission when I want to use it. So the level then goes back to 0?

This is a completion interrupt when the dma completes. It cannot know what the PIO is then doing. If you use this you need to calculate in your Basic code how long to wait until the PIO has completed processing the data in its FIFO and only then use the PIO again.

  Quote  I can't get the command on page 200 of the manual to work. Where and when do I need to use this command?
This sub in parameter 4 is never called?

From the manual entry
  Quote  PIO INTERRUPT pio, sm[,RXinterrupt] [,TXinterrupt]
Sets Basic interrupts for PIO activity.
Use the value 0 for RXinterrupt or TXinterrupt to disable an interrupt.
Omit values not needed.
The RX interrupt triggers whenever a word has been "pushed" by the PIO code
into the specified FIFO. The data MUST be read in the interrupt to clear it.
The TX interrupt triggers whenever the specified FIFO has been FULL and the
PIO code has now "pulled" it


Don't know if anyone has used this recently but nothing has changed in this area so it should still work but the use is very specific as above.
 
homa

Guru

Joined: 05/11/2021
Location: Germany
Posts: 507
Posted: 06:27pm 07 Dec 2025
Copy link to clipboard 
Print this post

Okay, thanks for clarifying that. So the PIO should continue working and send the rest. Unfortunately, that doesn't work as expected. Despite the additional pause.

When I take my long pause without this parameter and repeat the transmission periodically, everything works fine. **Scratching my head**

That's why I kept searching and wanted to use PIO INTERRUPT.
Your description reads differently than on page 200 of the manual. Does it only trigger when the FIFO is full?
Does that even happen when I send a long string with DMA?

So I dismissed that idea for the time being, continued searching, and tried this:
Then the general question: do I have to get the status from the flags myself? Are no variables set in mmbasic?

Because it looks like the information I'm looking for is in bit 16:

pio_0_registers% = &h50200000 + &h4 'PIO0 Offset + Register FSTAT
print BIN$(Peek(word pio_0_registers) , 32)

fifo:   00001111000000000000111100000000
fifo:   00001110000000010000111100000000


This helps here without errors before sending again periodically:

do while (peek(word pio_0_registers) AND 65536)<>0
loop
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10685
Posted: 06:32pm 07 Dec 2025
Copy link to clipboard 
Print this post

Have a look at the PIO function in the manual
 
homa

Guru

Joined: 05/11/2021
Location: Germany
Posts: 507
Posted: 08:28pm 07 Dec 2025
Copy link to clipboard 
Print this post

Command PIO, Page 139 ff
Appendix F, Page 196 ff

Am I overlooking something? What exactly do you mean? I find the description very brief in some places.
Unfortunately, the search for PIO also finds GPIO.  

I have buttons on my eyes.  

The optional parameter completioninterrupt is the name of a MMBasic
subroutine rthat will be called when the DMA completes and in the case of
DMA_OUT the FIFO has been emptied.
If the optional interrupt is not used then the status of the DMA can be checked
using the functions:
MM.INFO(PIO RX DMA)
MM.INFO(PIO TX DMA)

For testing purposes, I inserted a ‘Print MM.INFO(PIO TX DMA)’, whereby even here a 1 is sometimes reported when the FLAG is already 0 again via PEEK (see above)? Is that possible?

Oh dear, Peter, how do you keep track of all this complexity?
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 6384
Posted: 09:12pm 07 Dec 2025
Copy link to clipboard 
Print this post

  homa said  Command PIO, Page 139 ff
Appendix F, Page 196 ff

Am I overlooking something? What exactly do you mean? I find the description very brief in some places.
Unfortunately, the search for PIO also finds GPIO.  


You have found the PIO command.
Now you need to look for the POI FUNCTION.
Missing in the index but on page 179-180 in the V6.0.3 manual

Search for PIO(DMA RX POINTER)

Jim
VK7JH
MMedit
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5518
Posted: 08:27am 08 Dec 2025
Copy link to clipboard 
Print this post

My manual may be A4 size formatted. Then it is on pages 180/181.

Volhout
PicomiteVGA PETSCII ROBOTS
 
     Page 3 of 3    
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 2025