Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 04:22 11 Jul 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 2 of 3    
Author Message
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5054
Posted: 07:43pm 17 Jan 2025
Copy link to clipboard 
Print this post

Hi Thomas,

Please find attached MMBasic program demonstrating a UART running on PIO.
In this case it uses GP0 for TX and GP1 for RX. When you connect both together, you see the send string (a$) received correctly.

When there are questions, feel free to ask.

Technically this could also work for a 4'th UART, but MMBasic only supports a single DMA RX and DMA TX. The RX and TX buffers are layed out for 128 characters, but could be extended to 255 (string size).

Please understand you need MMBasic 6.00.02 beta 0 to run the PIO ASSEMBLER.

 'PIO 8n1 UART 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
 
 option default integer
 
 'PIO program uses 2 state machines, one for RX and one for TX
 baud_rate = 19200 '115200
 
 
 'memory buffer reservation
 
 'DMA buffers (packed) for 128 characters maximum
 dim tx_pck%(63)                           'packed TX array, 2 char per integer
 const tx_pck_ad%=peek(varaddr tx_pck%())  'memory address of array start
 dim rx_pck%(63)                           'packed RX array, 2 char per integer
 const rx_pck_ad%=peek(varaddr rx_pck%())  'memory address of array start
 dim rx$                                   '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 -------------------------------
 
 'test text string
 a$ = "Hello World, admire your beautiful beaches" + chr$(13)
 
 
 'main loop: send string repeatedly
 do
   
   '---------------------- send a string -----------------
   UART_TX (a$)
   
   
   pause 50
   
   '--------------------- receive a string ----------------
   
   'print characters in the receive buffer if any
   print UART_RX$()
   
   
 loop
 
 
 
 
 '-------------------------- subs -------------------------------------------
 
 'sends string using DMA to PIO TX
sub UART_TX (tx$)
 
 '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 dma tx 1,0,tx_l,tx_pck%(),
 
end sub
 
 
 'get data from DMA buffer and restart DMA
sub RdyInt
 
 'unpack RX buffer to string
 memory copy rx_pck_ad%,rx_ad%+1,LOF%,4,1    'fill string characters
 poke byte rx_ad%,LOF%                       'fill len(string)
 
 'restart the DMA
 pio dma rx 1,1,128,rx_pck%(),RdyInt,32    'start the PIO with DMA
 
end sub
 
 
function UART_RX$()
 
 UART_RX$=""
 
 'check for characters in the receive buffer
 LOF% = (Pio(DMA RX POINTER) - rx_pck_ad%) / 4   'in characters
 
 'when data in buffer, get it
 if LOF% > 0 then
   PIO DMA RX OFF      'stop the DMA, generates a RdyInt
   UART_RX$ = rx$      'continue after RdyInt, result is in rx$
 end if
 
end function


'program PIO 1 SM0 and 1, and start the UART
sub INIT_PIO
 
 'State machine 0 is UART TX
 
 SetPin GP0,pio1
 
 ' note: side enable is implemented as the msb side_set bit since assembler
 ' instruction bits   12        11       10 9  8
 ' use              side_en   side_set     delay
 
 PIO ASSEMBLE 1,".program uart_tx"         'uart tx program from RP2040 datasheet
 PIO ASSEMBLE 1,".side_set 1 opt"          'side set 1 bit + opt=side enable
 pio assemble 1,".line 0"                  'start of PIO program memory
 PIO ASSEMBLE 1,"set pindirs, 1"           'gp0 is output
 PIO ASSEMBLE 1,".wrap target"             'return point for wrap
 PIO ASSEMBLE 1,"pull block side 1 [7]"    'get data from fifo
 PIO ASSEMBLE 1,"set x, 7  side 0 [7]"     'bit counter 7,6,5,....,0
 PIO ASSEMBLE 1,"bitloop:"                 'label
 PIO ASSEMBLE 1,"out pins, 1"              'shift 1 bit out
 PIO ASSEMBLE 1,"jmp x-- bitloop  [6]"     'check if 0, then decrement
 PIO ASSEMBLE 1,".wrap"                    'wrap to target
 PIO ASSEMBLE 1,".end program"' list"       'write program and list hex values
 
 'configure pio0 StateMachine 0
 f0=baud_rate*8
 '              a b c d  e   f   g                     'a,e=side set   c,g=OUT  b,f=set
 p0=Pio(PINCTRL 2,1,1, ,gp0,gp0,gp0)                   'note:2=include side enable bit
 e0=Pio(execctrl gp1,Pio(.wrap target),Pio(.wrap),0,1 )'1=side set only when defined
 s0=Pio(shiftctrl 0,0,0,0,0,1,0,0)                     'shift OUT direction LSB first
 
 'write the configuration
 PIO init machine 1,0,f0,p0,e0,s0,0        'SM0 start at address 0
 
 
 
 'Statemachine 1 = UART RX
 
 PIO ASSEMBLE 1,".program uart_rx"         'uart tx program from RP2040 datasheet
 pio assemble 1,".line 5"                  '5=start of PIO program memory RX
 PIO ASSEMBLE 1,"start:"                   'label
 PIO ASSEMBLE 1,".wrap target"             'outer loop return
 PIO ASSEMBLE 1,"wait 0 pin 0"             'wait for a start bit
 PIO ASSEMBLE 1,"set x,7 [10]"             'set counter to 7,wait 10 cycles (=mid lsb)
 PIO ASSEMBLE 1,"bitloop2:"                'inner loop
 PIO ASSEMBLE 1,"in pins,1"                'shif in 1 bit
 PIO ASSEMBLE 1,"jmp x-- bitloop2 [6]"     'if x>0 next bit, delay 6, then x--
 PIO ASSEMBLE 1,"jmp pin good_stop"         'if valid stop bit -> good_stop
 PIO ASSEMBLE 1,"irq 4 rel"                '?? flag for ARM ??
 PIO ASSEMBLE 1,"wait 1 pin 0"             'wait for a valid stop
 PIO ASSEMBLE 1,"jmp start"                'then goto wait for new start (loop)
 PIO ASSEMBLE 1,"good_stop:"                'label
 PIO ASSEMBLE 1,"in null,24"               'shift the data to LSB in the ISR
 PIO ASSEMBLE 1,"push"                     'shift data to fifo
 PIO ASSEMBLE 1,".wrap"                    'outer loop for new character
 PIO ASSEMBLE 1,".end program"' list"
 
 'configure pio0 StateMachine 1
 f1=baud_rate*8
 '              a b c  d   e   f   g                   'a,e=side set c,g=OUT b,f=set
 p1=Pio(PINCTRL 0, , ,gp1,  ,   ,    )                 'gp1 = IN pin
 e1=Pio(execctrl gp1,Pio(.wrap target),Pio(.wrap))     'gp1 = JMP pin
 s1=Pio(shiftctrl 0,0,0,0,1,0,0,0)                     'shift IN LSB first
 
 'write the configuration
 PIO init machine 1,1,f1,p1,e1,s1,5        'SM0 start at address 5
 
 
 ' start the PIO UART
 pio dma rx 1,1,128,rx_pck%(),RdyInt,32      'start the RX PIO with DMA
 pio start 1,0                               'start the TX UART
 
end sub


Volhout
PicomiteVGA PETSCII ROBOTS
 
karlelch

Senior Member

Joined: 30/10/2014
Location: Germany
Posts: 235
Posted: 07:13am 18 Jan 2025
Copy link to clipboard 
Print this post

Hi Volout,

  Volhout said  Hi Thomas,

Please find attached MMBasic program demonstrating a UART running on PIO.
In this case it uses GP0 for TX and GP1 for RX. When you connect both together, you see the send string (a$) received correctly.

When there are questions, feel free to ask.Volhout

Wow! Thanks a lot! I think this may be an important mean to get around the two-hardware-UART limitation even in the PR2350.
 

I will test it soon. For my specific problem, I got an I2C solution to work, but I kept my UART code in the program and can enable it with a single switch ...

Best
Thomas
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5054
Posted: 07:45am 18 Jan 2025
Copy link to clipboard 
Print this post

Hi Thomas,

This UART is usable in a question/answer application. It’s weak point is that when you read data from its RX buffer, the DMA is stopped momentarily, and restarted. Any character received during this action is lost.

I will investigate a solution using a ring buffer, not stopping the DMA, but that needs more time.

When I am closer to final, I will also release a version that communicates framing errors to mmbasic level, and runs on other PIO and older mmbasic versions. I appreciate feedback on this UART core to proceed.

Volhout
Edited 2025-01-18 17:49 by Volhout
PicomiteVGA PETSCII ROBOTS
 
phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 2606
Posted: 12:56pm 18 Jan 2025
Copy link to clipboard 
Print this post

Excellent! Even at this stage it is very usable.
> RUN
'''World, admire your beautiful beaches
Hello World, admire your beautiful beaches
Hello World, admire your beautiful beaches
Hello World, admire your beautiful beaches
Hello World, admire your beautiful beaches
>
> INIT_PIO
> a$= "The quick brown fox jumps over the lazy dog."
> UART_TX (a$) : pause 50: print UART_RX$()
The quick brown fox jumps over the lazy dog.
>
> a$= "This UART is usable in a question/answer application. Its weak point is that when you read data from its RX buffer,"
> UART_TX (a$) : pause 50: print UART_RX$()
This UART is usable in a question/answer application. Its weak point is that when you read data
>
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 1360
Posted: 01:27pm 18 Jan 2025
Copy link to clipboard 
Print this post

  phil99 said  Excellent! Even at this stage it is very usable.


Heck YEAH    
 
karlelch

Senior Member

Joined: 30/10/2014
Location: Germany
Posts: 235
Posted: 04:03pm 18 Jan 2025
Copy link to clipboard 
Print this post

Hi Volhout,

works like a charm!
  Quote  I will investigate a solution using a ring buffer, not stopping the DMA, but that needs more time.

That would be very useful - or at least some flag that indicates that data was lost? Or both?

  Quote  When I am closer to final, I will also release a version that communicates framing errors to mmbasic level, and runs on other PIO and older mmbasic versions. I appreciate feedback on this UART core to proceed.

For general use, the final code would do great in a library.

Thanks again and best
Thomas
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5054
Posted: 09:33pm 19 Jan 2025
Copy link to clipboard 
Print this post

Hi Thomas, phil99,

Thanks for testing. Below is the functional version for PIO1 on GP0 and GP1.
Also still needs V6.00.02b0 to assemble.

This version uses the ring buffer, so should be full duplex.
It has increased robustness against bitrate variations (increased RX PIO clock speed and tuned internal delays), and reports framing errors (communicated via the IRQ flag register).

Later this week I will compact the PIO assembler code into an array, so the MMBasic source becomes more compact. I didn't do that yet so I could still make changes easier. But now I am becomming more confident.

 'PIO 8n1 UART 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
 
 
 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 = 19200
 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
 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 -------------------------------
 
 'test text string
 a$ = "Hello World, I admire your beautiful beaches" + chr$(13)
 
 
 'main loop: send string repeatedly
 do
   
   '---------------------- send a string -----------------
   UART_TX (a$)
   
   
   pause 50
   
   
   '--------------------- receive a string ----------------
   
   '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
   
 loop
 
 
 
 
 '-------------------------- subs -------------------------------------------
 
 'sends string using DMA to PIO TX
sub UART_TX (tx$)
 
 '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 dma tx 1, 0, tx_l%, tx_pck%(),
 
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=0kay, 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

 
 'State machine 0 is UART TX
 
 SetPin GP0,pio1
 
 ' note: side enable is implemented as the msb side_set bit since assembler
 ' instruction bits   12        11       10 9  8
 ' use              side_en   side_set     delay
 
 PIO ASSEMBLE 1,".program uart_tx"         'uart tx program from RP2040 datasheet
 PIO ASSEMBLE 1,".side_set 1 opt"          'side set 1 bit + opt=side enable
 pio assemble 1,".line 0"                  'start of PIO program memory
 PIO ASSEMBLE 1,"set pindirs, 1"           'gp0 is output
 PIO ASSEMBLE 1,".wrap target"             'return point for wrap
 PIO ASSEMBLE 1,"pull block side 1 [7]"    'get data from fifo
 PIO ASSEMBLE 1,"set x, 7  side 0 [7]"     'bit counter 7,6,5,....,0
 PIO ASSEMBLE 1,"bitloop:"                 'label
 PIO ASSEMBLE 1,"out pins, 1"              'shift 1 bit out
 PIO ASSEMBLE 1,"jmp x-- bitloop  [6]"     'check if 0, then decrement
 PIO ASSEMBLE 1,".wrap"                    'wrap to target
 PIO ASSEMBLE 1,".end program"' list"      'write program and list hex values
 
 'configure pio0 StateMachine 0
 f0=tx_baud_rate*8
 '              a b c d  e   f   g                     'a,e=side set   c,g=OUT  b,f=set
 p0=Pio(PINCTRL 2,1,1, ,gp0,gp0,gp0)                   'note:2=include side enable bit
 e0=Pio(execctrl gp1,Pio(.wrap target),Pio(.wrap),0,1 )'1=side set only when defined
 s0=Pio(shiftctrl 0,0,0,0,0,1,0,0)                     'shift OUT direction LSB first
 
 'write the configuration
 PIO init machine 1,0,f0,p0,e0,s0,0        'SM0 start at address 0
 
 
 
 'Statemachine 1 = UART RX
 
 PIO ASSEMBLE 1,".program uart_rx"         'uart tx program from RP2040 datasheet
 pio assemble 1,".line 5"                  '5=start of PIO program memory RX
 PIO ASSEMBLE 1,"start:"                   'label
 PIO ASSEMBLE 1,".wrap target"             'outer loop return
 PIO ASSEMBLE 1,"wait 0 pin 0"             'wait for a start bit
 PIO ASSEMBLE 1,"set x,7 [20]"             'set counter to 7,wait 10 cycles (=mid lsb)
 PIO ASSEMBLE 1,"bitloop2:"                'inner loop
 PIO ASSEMBLE 1,"in pins,1"                'shif in 1 bit
 PIO ASSEMBLE 1,"jmp x-- bitloop2 [14]"    'if x>0 next bit, delay 6, then x--
 PIO ASSEMBLE 1,"jmp pin good_stop"        'if valid stop bit -> good_stop
 PIO ASSEMBLE 1,"irq set 0"                'was "irq 4 rel" set flag for ARM
 PIO ASSEMBLE 1,"wait 1 pin 0"             'wait for a valid stop
 PIO ASSEMBLE 1,"jmp start"                'then goto wait for new start (loop)
 PIO ASSEMBLE 1,"good_stop:"               'label
 PIO ASSEMBLE 1,"in null,24"               'shift the data to LSB in the ISR
 PIO ASSEMBLE 1,"push"                     'shift data to fifo
 PIO ASSEMBLE 1,".wrap"                    'outer loop for new character
 PIO ASSEMBLE 1,".end program"' list"
 
 'configure pio0 StateMachine 1
 f1=rx_baud_rate*16
 '              a b c  d   e   f   g                   'a,e=side set c,g=OUT b,f=set
 p1=Pio(PINCTRL 0, , ,gp1,  ,   ,    )                 'gp1 = IN pin
 e1=Pio(execctrl gp1,Pio(.wrap target),Pio(.wrap))     'gp1 = JMP pin
 s1=Pio(shiftctrl 0,0,0,0,1,0,0,0)                     'shift IN LSB first
 
 'write the configuration
 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
 pio start 1,0                                'start the TX UART
 
end sub


Volhout
PicomiteVGA PETSCII ROBOTS
 
phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 2606
Posted: 12:12am 20 Jan 2025
Copy link to clipboard 
Print this post

Version 0.8 communicating with COM2. Very nice.
'---------------------------- example code -------------------------------

'test text string
a$ = "Hello World, I admire your beautiful beaches" '+ Chr$(13)

SetPin gp5,gp4, COM2 : Open "COM2:19200" As #5 'gp1 & gp5 joined, gp4 & gp2 joined
Dim n%, dat$, b$= "The quick brown fox jumps over the lazy dog. "
'main loop: send string repeatedly
Do
  Inc n%

  '---------------------- send a string -----------------
  UART_TX(Str$(n%)+" "+a$) : Pause 50: dat$ = Input$(128, #5) :Print dat$
  Pause 50
  Inc n%

  '--------------------- receive a string ----------------
  Print #5, Str$(n%)+" Hello,         "; b$+b$ : Pause 60
  '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
  Pause 1000
Loop
> RUN
1 Hello World, I admire your beautiful beaches
�2 Hello,         The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.

3 Hello World, I admire your beautiful beaches
4 Hello,         The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.

5 Hello World, I admire your beautiful beaches
6 Hello,         The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.

7 Hello World, I admire your beautiful beaches
8 Hello,         The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.

>
Don't know where the non-printable character comes from. Always before line 2.

Edit. If the pauses are shorter than the transmission time funny things happen.
Edit 2. More playing. The non-printable character is chr$(252)
> RUN
1 Hello World, I admire your beautiful beaches  Rx Baud Rate 230400
framing error  UART_RX%() 0
3 Hello World, I admire your beautiful beaches  Rx Baud Rate 230400
4 Hello, 16:31:37 The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.

5 Hello World, I admire your beautiful beaches  Rx Baud Rate 230400
6 Hello, 16:31:39 The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.

>

The non-printable character is gone, along with the rest of line 2.
Edited 2025-01-20 16:01 by phil99
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5054
Posted: 06:12am 20 Jan 2025
Copy link to clipboard 
Print this post

Hi phill,

The weird character can be explained (line 2). You start the RX PIO with ring buffer, and then setpin of the COM 2. This will cause a transition, that will be seen by the runnin PIO

The last listing is causing me grey hairs. Someone reads data from an area where they should not.

Volhout
PicomiteVGA PETSCII ROBOTS
 
phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 2606
Posted: 06:39am 20 Jan 2025
Copy link to clipboard 
Print this post

Moving COM2 setup to the setup area fixed both issues.
'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 = 19200 '230400 '115200 '38400 '19200
rx_baud_rate = tx_baud_rate' * .96 'to test robustness
SetPin gp5,gp4, COM2 : Open "COM2:tx_baud_rate" As #5 'gp1 & gp5 joined, gp4 & gp2 joined


'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
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 -------------------------------

'test text string
a$ = "Hello World, I admire your beautiful beaches " '+ Chr$(13)

Dim n%=-1, dat$, b$= "The quick brown fox jumps over the lazy dog. "
'main loop: send string repeatedly
Do
  Inc n%

  '---------------------- send a string -----------------
  UART_TX(Str$(n%)+" "+a$+" Rx Baud Rate "+Str$(Rx_baud_rate)) :Pause 90 : dat$=Input$(128, #5) :Print dat$


  Pause 100
  Inc n%

  '--------------------- receive a string ----------------
  Print #5, Str$(n%)+" Hello, ";Time$;" "; b$+b$ : Pause 90
  '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$, Asc(rx$)                 'print the received string
  Else
    Print "framing error  UART_RX%()"; UART_RX%()
    Poke word pio_1_irq,255     'clear framing error
  End If
  Pause 1000
Loop

>  RUN
0 Hello World, I admire your beautiful beaches  Rx Baud Rate 19200
1 Hello, 17:38:23 The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.
        49
2 Hello World, I admire your beautiful beaches  Rx Baud Rate 19200
3 Hello, 17:38:24 The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.
        51
4 Hello World, I admire your beautiful beaches  Rx Baud Rate 19200
5 Hello, 17:38:26 The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.
        53
6 Hello World, I admire your beautiful beaches  Rx Baud Rate 19200
7 Hello, 17:38:27 The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.
        55
>
0 Hello World, I admire your beautiful beaches  Rx Baud Rate 230400
1 Hello, 17:40:02 The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.
        49
2 Hello World, I admire your beautiful beaches  Rx Baud Rate 230400
3 Hello, 17:40:03 The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.
        51
4 Hello World, I admire your beautiful beaches  Rx Baud Rate 230400
5 Hello, 17:40:04 The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.

Edited 2025-01-20 16:41 by phil99
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5054
Posted: 07:12am 20 Jan 2025
Copy link to clipboard 
Print this post

Hi Phill,

There may still be an issue when starting a new TX DMA when the old one is not yet finsihed. Needs investigation. Maybe a forced PIO TX DMA STOP before issuing a new one.
I will look into that later.

Thank you for helping me debug the UART. Working together is nice, and I remember you where interested in PIO as well (A+ score at the PIO training). For you this is not "black magic".

Again, thanks...

Volhout
Edited 2025-01-20 17:15 by Volhout
PicomiteVGA PETSCII ROBOTS
 
phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 2606
Posted: 07:43am 20 Jan 2025
Copy link to clipboard 
Print this post

Yes, when Stop is added to the start of Tx and Rx the text just gets truncated if the pause is too short.
'sends string using DMA to PIO TX
Sub UART_TX(tx$)
PIO stop 1,0
...

End Sub

Function UART_RX%()
PIO stop 1,0

Purging the residual text in the buffer would finish the job.
> RUN
0 Hello World, I admire your beautiful beaches  Rx Baud Rate 230400
1 Hello, 18:43:06 The quick brown fox jumps over the lazy dog. The quick brown fox jumps over th         49
2 Hello World, I admire your beautiful beaches  Rx Baud Rate 230400
e lazy dog.
3 Hello, 18:43:08 The quick brown fox jumps over the lazy dog. The quick brown fox jumps over t  101
4 Hello World, I admire your beautiful beaches  Rx Baud Rate 230400
he lazy dog.
5 Hello, 18:43:09 The quick brown fox jumps over the lazy dog. The quick brown fox jumps over t  104
6 Hello World, I admire your beautiful beaches  Rx Baud Rate 230400
he lazy dog.
7 Hello, 18:43:10 The quick brown fox jumps over the lazy dog. The quick brown fox jumps over t  104
>
That was with Pause 4 in each direction.
.
Edited 2025-01-20 17:52 by phil99
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5054
Posted: 08:06am 20 Jan 2025
Copy link to clipboard 
Print this post

Hi Phill,

Why do you stop PIO 1.0 (TX DMA) in UART_RX ?
I would not do that.
And do not stop PIO 1.1 (RX DMA) since keeping RX DMA running is the guarantee that you do not miss any characters.

@Thomas: Although you may put this (later, when finished) in a library, be alert that this UART requires some resources that become unavaiable for your main program. And this is not very clear when "hidden" in a library.

Volhout
Edited 2025-01-20 18:12 by Volhout
PicomiteVGA PETSCII ROBOTS
 
phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 2606
Posted: 09:49am 20 Jan 2025
Copy link to clipboard 
Print this post

  Quote  Why do you stop PIO 1.0 (TX DMA) in UART_RX ?
No logic was used at all, just trial and error. Too hot for thinking.
Yes, that was doing nothing.
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 7857
Posted: 10:51am 20 Jan 2025
Copy link to clipboard 
Print this post

Do you fancy a bit of weather equalization, Phil? 3°C and very overcast here, with a bit of rain forecast.  :)
Mick

Zilog Inside! nascom.info for Nascom & Gemini
Preliminary MMBasic docs & my PCB designs
 
phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 2606
Posted: 11:28am 20 Jan 2025
Copy link to clipboard 
Print this post

Ok I will stop grumbling. If it has to be one or the other this is better, up to a point.
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5054
Posted: 11:30am 20 Jan 2025
Copy link to clipboard 
Print this post

@Mick,

The text of the sunny beaches was mine. That is how much I long for to see them.
Phil added the neutral "fox jumps over the lazy dog".
So it is me to blaim, being under the same overcast and cold sky for weeks.

Volhout
PicomiteVGA PETSCII ROBOTS
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 7857
Posted: 01:58pm 20 Jan 2025
Copy link to clipboard 
Print this post

Phil:
  Quote  No logic was used at all, just trial and error. Too hot for thinking.


I can't imagine that it's too hot for you to think too, Volhout. :)

I'd go out and fly a little drone but it's drizzling now. Not a good combination, drones, drizzle and a remarkably muddy dog-walking field.
Mick

Zilog Inside! nascom.info for Nascom & Gemini
Preliminary MMBasic docs & my PCB designs
 
karlelch

Senior Member

Joined: 30/10/2014
Location: Germany
Posts: 235
Posted: 06:18pm 20 Jan 2025
Copy link to clipboard 
Print this post

Hi Volhout, hi Phill,

thanks for doing all the testing  
(I am currently very busy at work and probably won't find time for more testing before the next weekend.)

  Volhout said  @Thomas: Although you may put this (later, when finished) in a library, be alert that this UART requires some resources that become unavailable for your main program. And this is not very clear when "hidden" in a library.


True, but I am not thinking necessarily of a library that is "always" there. I think more of "drivers". For my robot, I have several "drivers" that I sequentially load as libraries and they follow a standard format (see below). These "drivers" could announce their hardware requirements on load (for example, see below).

Cheers
Thomas

' lib_bno055.bas v0.1.1
' ---------------------
' BNO055 MNU driver
' 2024-02-04 - v0.1.1 - Fewer constant declarations to save global
'                       variable slots
' Example:
'   BNO055.init
'   BNO055.getEuler v()
'   Print "h="+Str$(v(0),3,0)+" p="+Str$(v(1),3,0)+" r="+Str$(v(2),3,0)
'   BNO055.getSystemStatus
'   BNO055.close
' ---------------------------------------------------------------------------
Option Base 0
Option Explicit
Option Escape
Option Default Float

' Constants
Const BNO_ID              = &HA0 ' Device ID
Const BNO_ADDR            = &H28 ' I2C address
'Const BNO_CHIP_ID_ADDR   = &H00 ' Register addresses
'Const BNO_PAGE_ID_ADDR   = &H07
'Const BNO_OPR_MODE_ADDR  = &H3D
'Const BNO_PWR_MODE_ADDR  = &H3E
'Const BNO_SYS_TRIG_ADDR  = &H3F
'Const BNO_SYS_STAT_ADDR  = &H39
'Const BNO_STEST_RES_ADDR = &H36
'Const BNO_SYS_ERR_ADDR   = &H3A
'Const BNO_OPR_MODE_CONF  = &H00 ' Operation mode(s)
'Const BNO_OPR_MODE_NDOF  = &H0C
'Const BNO_PWR_MODE_NORM  = &H00 ' Power mode(s)

' Global variables
Dim integer BNO055.ready = 0, BNO055.mode = &H0C
Dim integer BNO055.sys_status, BNO055.test_res, BNO055.sys_err

print "BNO055 sensor driver in library"
print "| Hardware requirements: I2C2"

' ---------------------------------------------------------------------------
Sub BNO055.init sda, scl
 ' Initialize BNO055 sensor
 Local integer res
 Print "Initializing BNO055 orientation sensor ..."
 ' Open I2C2 for sensor
 SetPin sda, scl, I2C2
 I2C2 Open 400, 1000
 Pause 850
 res = BNO055._read1(&H00)
 Print "| Device "+Choice(res = BNO_ID, "", "not ") +"found."
 BNO055.ready = res = BNO_ID
 If BNO055.ready Then
   ' Reset and configure
   BNO055._write1 &H3D, &H00
   Print "| Resetting ";
   BNO055._write1 &H3F, &H20 : Pause 30
   Do While Not(BNO055._read1(&H00) = BNO_ID)
     Print ".";
     Pause 50
   Loop
   Pause 50
   Print
   BNO055._write1 &H3E, &H00 : Pause 10
   BNO055._write1 &H07, &H00
   BNO055._write1 &H3F, &H00 : Pause 10
   BNO055._write1 &H3D, BNO055.mode : Pause 20
   Print "| Ready."
 EndIf
End Sub

Sub BNO055.getEuler v()
 ' Get Euler vector -> `v(2)` heading, pitch, roll
 Static integer buf(5) = (0,0,0,0,0,0), w(2)
 Local string s$ length 2
 I2C2 Write BNO_ADDR, 0, 1, &H1A
 I2C2 Read BNO_ADDR, 0, 6, buf()
 w(0) = buf(0) Or (buf(1) << 8)
 s$ = Chr$(buf(2))+Chr$(buf(3))
 w(1) = Str2bin(int16, s$)
 s$ = Chr$(buf(4))+Chr$(buf(5))
 w(2) = Str2bin(int16, s$)
 Math Scale w(), 1/16, v()
End Sub

Sub BNO055.getSystemStatus
 ' Get system status
 BNO055._write1 &H07, &H00
 BNO055.sys_status = BNO055._read1(&H39)
 BNO055.test_res = BNO055._read1(&H36)
 BNO055.sys_err = BNO055._read1(&H3A)
 Print BNO055.sys_status, BNO055.test_res, BNO055.sys_err
End Sub

Sub BNO055.close
 ' Close connection to BNO055 sensor
 I2C2 Close
 BNO055.ready = 0
End Sub

Function BNO055._read1(reg%) As integer
 I2C2 Write BNO_ADDR, 0, 1, reg%
 I2C2 Read  BNO_ADDR, 0, 1, BNO055._read1
End Function

Sub BNO055._write1 reg%, val%
 I2C2 Write BNO_ADDR, 0, 2, reg%, val%
End Sub

Function _b2s$(a%(), n) As String
 Local integer i
 Local string s$ = ""
 For i=0 To n-1 : _b2s$ = _b2s$ +Hex$(a%(i),2) :  Next
End Function

' ---------------------------------------------------------------------------
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5054
Posted: 12:38pm 27 Jan 2025
Copy link to clipboard 
Print this post

I think I have included all fixes.

PIO_UART_09 is the version that live assembles the PIO code, and needs v6.00.02b0 or newer to run.
PIO_UART_10 the assembly is removed, just the binaries are included. This version should run on 6.00.01 and earlier (later than 5.07.04b27)

PIO_UART_09.zip

PIO_UART_010.zip

Additional comprimations can be done by replacing the PIO(SHIFTCTRL xxxx) and others with the corresponding number. But that would remove flexibility (what pins to use, what PIO to use). It is up to you. If you put this in your application / library, please also publish the code here. I would also like to learn...

Thank you,

Volhout
Edited 2025-01-27 22:40 by Volhout
PicomiteVGA PETSCII ROBOTS
 
     Page 2 of 3    
Print this page
The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025