|
Forum Index : Microcontroller and PC projects : 3rd UART via PIO
| Author | Message | ||||
| phil99 Guru Joined: 11/02/2018 Location: AustraliaPosts: 2851 |
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: NetherlandsPosts: 5518 |
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: AustraliaPosts: 2851 |
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: GermanyPosts: 507 |
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: AustraliaPosts: 2851 |
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 your codeMemory copy tx_ad%+1, tx_pck_ad%, tx_l%, 1, 4 'pack string for sending to UART I think this is only copying every fourth byte from the source. Is there a reason for that?Memory copy tx_ad%, tx_pck_ad%, tx_l%, 4, 4 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: NetherlandsPosts: 5518 |
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 KingdomPosts: 10685 |
A longstring stores the length in the first integer in the array |
||||
| Volhout Guru Joined: 05/03/2018 Location: NetherlandsPosts: 5518 |
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 KingdomPosts: 10685 |
Exactly |
||||
| homa Guru Joined: 05/11/2021 Location: GermanyPosts: 507 |
thx! |
||||
| homa Guru Joined: 05/11/2021 Location: GermanyPosts: 507 |
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 KingdomPosts: 10685 |
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. From the manual entry 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: GermanyPosts: 507 |
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 KingdomPosts: 10685 |
Have a look at the PIO function in the manual |
||||
| homa Guru Joined: 05/11/2021 Location: GermanyPosts: 507 |
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: AustraliaPosts: 6384 |
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: NetherlandsPosts: 5518 |
My manual may be A4 size formatted. Then it is on pages 180/181. Volhout PicomiteVGA PETSCII ROBOTS |
||||
| The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |