User Tools

Site Tools


en:pfw:usb_cdc_driver_for_rp2040

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
en:pfw:usb_cdc_driver_for_rp2040 [2025-12-27 12:54] – [Pseudo code] willemen:pfw:usb_cdc_driver_for_rp2040 [2026-01-13 18:16] (current) – [The idea] willem
Line 17: Line 17:
 <code> <code>
 hex hex
-Function DEVICE-DECRIPTOR \ 18 bytes+FunctionDEVICE-DECRIPTOR \ 18 bytes 
 +The USB CDC device descriptor 
 + 
 +Function: CONFIGURATION-DESCRIPTOR \ 9 or 75 bytes 
 +The complex USB CDC device descriptor 
 + 
 +Function: USB-STATE    ( -- a ) 
 +Hold current USB state in first half word: 3 = ready 
 +In second half word are memory for the 900/880 requests 
 +The English/US language ID string 
 +CDC Line data: 115k2, Stop bits, Parity, Data bits 
 + 
 +Function: START-USB     ( -- ) 
 +Initialise the RP2040 USB hardware, restart the USB hardware, 
 +erase the USB DPRAM, enable USB controller, set USB address to zero. 
 +Setup used endpoints and used interrupts & enable full speed USB. 
 + 
 +Function: PAD           ( -- a ) 
 +Reserve 64 bytes RAM as scratchpad area 
 + 
 +Function: EP0   ( -- a ) 
 +Function: EP1   ( -- a ) 
 +Function: EP2   ( -- a ) 
 +Function: EP3   ( -- a ) 
 +Seven cells data structures to control endpoint-0, 1, 2 & 3 
 +The first four cells a variable data 
 +The three cells thereafter contain USB pointers & register addresses 
 + 
 +Function: @VAL   ( -- +n ) 
 +Read the wValue from any eight bytes setup packet 
 +Function: @LEN   ( -- +n ) 
 +Read the wLength from any eight bytes setup packet 
 + 
 +Function: >UNI   ( a1 u -- a2 ) 
 +Store the ASCII string to a unicode string tailored for USB 
 + 
 +Function: >CNT    \ Offset 00 
 +Function: >ORG    \ Offset 04 
 +Function: >PKT    \ Offset 08 
 +Function: >PID    \ Offset 12 
 +Function: >ICTRL  \ Offset 16 
 +Function: >OCTRL  \ Offset 20 
 +Function: >BUF    \ Offset 24 
 +Seven functions that calculate the address of each field in the  
 +endpoint data structures 
 + 
 +Function: EP-IN    ( ep -- org buf pkt ) 
 +Calculate data to send for 'ep'
 + 
 +Function: PREPARE  ( a u ep -- ) 
 +Prepare data packet from address 'a' with the length 'u' to send tp 'ep'
 + 
 +Function: >NEXT    ( ep -- pkt ) 
 +Get next packet size for 'ep' x, when 'pkt' is zero there is no more  
 +data to send 
 + 
 +Function: !PKT     ( pkt ep -- ictrl ) 
 +Store next packet to send for 'ep' x  
 + 
 +Function: PREP-RCV ( ep -- ) 
 +Enable endpoint 'ep' x  to receive a data packet 
 + 
 +Function: GONE?    ( ep -- f ) 
 +Leave true when the data in 'ep' x was sent 
 + 
 +Function: USB?     ( -- +n ) 
 +Leave three when the host & device are connected 
 + 
 +Function: USB-SEND ( ep -- ) 
 +Transmit the prepared data packet to 'ep'
 + 
 +Function: USB-RCV  ( pid ep -- ) 
 +Enable the receiving of a data packet on 'ep' x starting with the 'pid'  
 +from the stack 
 + 
 +Function: BUS=RESET ( -- ) 
 +Handle a USB bus reset, clearing the device address to zero, the USB  
 +state to zero, reinitialise the receiving & transmitting endpoints 
 + 
 +Function: SETUP>   ( a u -- ) 
 +Handle the answer data  packet 'a' 'u' for all setup packages 
 +When done wait for a ZLP from the host to signal a correct answer 
 + 
 +Function: XTABLE 
 +    Define an execution table entry for '+n' request actions 
 +    Define:  ( +n "name" -- ) 
 +        Create a table with "name" for '+n' actions 
 +    Action:  ( request -- ) 
 +        Execute the action for 'request' or generate a stall 
 +        when the 'request' is not available 
 +         
 +Function: ZLP>     ( -- ) 
 +Send a zero length package through endpoint zero. This is used  
 +to signal the handling of a request that sends no data back 
 + 
 +Define four action in front that leave their execution tokens on the stack 
 +0302 action xt 
 +0300 action xt 
 +0200 action xt 
 +0100 action xt 
 +Function: HANDLE-SETUP ( req0 .. req3 4 -- ) 
 + 
 +2221 action xt 
 +21A1 action xt 
 +2021 action xt 
 +0900 action xt 
 +0880 action xt 
 +0680 action xt 
 +0500 action xt 
 +Function: HANDLE-REQ)  ( req0 .. req6 7 -- ) 
 + 
 +Function: HANDLE-REQ   ( -- ) 
 +Reset setup request interrupt, read bmrequest type and do HANDLE-REQ) 
 + 
 +Function: #L           ( -- +n ) 
 +Ring buffer size, must be a power of two 
 +Function: #R           ( -- +n ) 
 +this is equal to #L - 1 
 + 
 +Function: #RX          ( -- +n ) 
 +Leave number of characters in receive buffer 
 +Function: #TX          ( -- +n ) 
 +Leave number of characters in transmit buffer 
 + 
 +Function: >RX          ( c -- ) 
 +Store character 'c' in RX ring buffer 
 +Function: >TX          ( c -- ) 
 +Store character 'c' in TX ring buffer 
 + 
 +Function: RX>          ( -- c ) 
 +Read character 'c' from RX ring buffer 
 +Function: TX>          ( -- c ) 
 +Read character 'c' from TX ring buffer 
 + 
 +Function: IFLAG    
 +Reserve one cell space for receive interrupt flag 
 + 
 +Function: ENDPOINTS    ( -- ) 
 +Handle EP1 & EP3 endpoints, save received data interrupt flag in IFLAG 
 +Release all interrupt flags 
 + 
 +Function: REQUESTS     ( -- ) 
 +Handle all USB interrupts, that are the USB bus reset, setup requests & endpoints 
 + 
 +Function: USB-HANDLER  ( -- ) 
 +Handle the receiving of the RX data using iFLAG and use the low level USB 
 +ACK/NAK handshake to move data from an endpoint to the RX ring buffer in a controlled way. 
 +Handle the transmitting of TX data, but only when a connection is made. Before filling 
 +the TX endpoint check if the previous data packet was sent too. 
 + 
 +Function: USB-KEY?     ( -- flag ) 
 +Leave true flag when there is data in the RX ring buffer, otherwise false 
 +Call the USB-HANDLER once too 
 + 
 +Function: USB-KEY      ( -- c ) 
 +Leave character 'c' when there is data in the RX ring buffer 
 +Call the USB-HANDLER waiting for space in the RX ring buffer 
 + 
 +Function: USB-EMIT     ( c -- ) 
 +Store character 'c' when there is space in the TX ring buffer 
 +Call the USB-HANDLER waiting for space in the TX ring buffer 
 + 
 +Function: USB-ON 
 +Initialise the USB DPRAM, call START-USB, clear USB-STATE & IFLAG 
 +finally initialse the KEY?, KEY and EMIT vectors of the used system. 
 + 
 +</code> 
 + 
 +===== Generic Forth implementation example ===== 
 + 
 +<code forth> 
 +\ This program assumes 32-bit cells. 
 +\ Non standard words used are: 
 +\ : C@+   ( a1 -- a2 b )  count ; 
 +\ : H@    ( a -- h )      count swap  c@ 8 lshift  or ; 
 +\ : H!    ( h a -- )      >r  dup r@ c!  8 rshift  r> 1+ c! ; 
 +\ : @+    ( a1 -- a2 x )  >r r@ cell+  r> @ ; 
 +\ : **BIS ( msk a -- )    >r r@ @ or  r> ! ; 
 +\ : **BIC ( msk a -- )    >r invert r@ @ and  r> ! ; 
 +\ : **BIX ( msk a -- )    >r r@ @ xor  r> ! ; 
 +\ : BIT** ( msk a -- b )  @ and ; 
 + 
 +hex  \ USB device data structures 
 +create DEV-DESCR    \ Device descriptor (18 bytes)
 12 c, 01 c, 10 c, 01 c, ( 1.10 ) EF c, 02 c, 01 c, 40 c, 66 c, 66 c, ( 6666 ) 12 c, 01 c, 10 c, 01 c, ( 1.10 ) EF c, 02 c, 01 c, 40 c, 66 c, 66 c, ( 6666 )
 10 c, 66 c, ( 6610 ) 00 c, 01 c, ( vsn 1.00 ) 00 c, 02 c, 00 c, 01 c,  align 10 c, 66 c, ( 6610 ) 00 c, 01 c, ( vsn 1.00 ) 00 c, 02 c, 00 c, 01 c,  align
- +create CONFIG-DESCR Configuration descriptor (9 or 75 bytes)
-Function CONFIGURATION-DESCRIPTOR \ 9 or 75 bytes+
 9 c,  2 c,  4B c,  0 c,  2 c,  1 c,  0 c,  80 c,  FA c, \ Maximum power = 500mA 9 c,  2 c,  4B c,  0 c,  2 c,  1 c,  0 c,  80 c,  FA c, \ Maximum power = 500mA
 8 c,  0B c,  0 c,  2 c,  2 c,  2 c,  1 c,  0 c,         \ Interface Association Descriptor - CDC 0 8 c,  0B c,  0 c,  2 c,  2 c,  2 c,  1 c,  0 c,         \ Interface Association Descriptor - CDC 0
Line 33: Line 215:
 7 c,  5 c,  82 c,  2 c,  40 c, 0 c,  0 c,               \ Endpoint 2 IN descriptor 7 c,  5 c,  82 c,  2 c,  40 c, 0 c,  0 c,               \ Endpoint 2 IN descriptor
 7 c,  5 c,  3 c,  2 c,  40 c, 0 c,  0 c,  align         \ Endpoint 3 OUT descriptor 7 c,  5 c,  3 c,  2 c,  40 c, 0 c,  0 c,  align         \ Endpoint 3 OUT descriptor
 +
 +create PAD  ( -- a )    40 allot    \ Scratchpad memory
  
 decimal decimal
-Function  USB-STATE  0 ,           \ Hold current USB state in first half word: 3 = ready +create USB-STATE  0 ,           \ Hold current USB state: 3 = ready 
-                                   In second half word are memory for the 900/880 requests +                                Second half word for 900/880 requests 
-          4 c, 3 c, 9 c, 4 c,      \ English/US = language ID +    4 c, 3 c, 9 c, 4 c,         \ English/US = language ID 
-          115200 ,  0 c,  0 c,  8 c,  align  \ Line data: 115k2, Stop bits, Parity, Data bits+    115200 ,  0 c,  0 c,  8 c,  \ Line data: 115k2, Stop bits, Parity, Data bits 
 +align  hex 
 + 
 +: START-USB     ( -- ) 
 +    1000000  4000C000           \ Bit-24 mask & Reset register 
 +    2dup **bis  2dup **bic      \ Restart USB 
 +    begin  2dup 8 + bit** until \ Wait until USB is ready 
 +    2drop 50100000 1000 false fill \ Erase USB ram 
 +    00000009  50110074  !       \ USB_USB_MUXING    Softcon, to PHY 
 +    0000000C  50110078  !       \ USB_USB_PWR       VBUS overide & detect enable 
 +    00000001  50110040  !       \ USB_MAIN_CTRl     Enable controller 
 +    20000000  5011004C  !       \ USB_SIE_CONTROL   Enable End Point 0 interrupt 
 +    false     50110000  !       \ Respond to address 0 on initial setup 
 +    00011010  50110090  !       \ USB_INTE          Enable 3 interrupts 
 +    AC000180  50100008  !       \ init COMM endpoint in buffer 1 
 +    A8000200  50100010  !       \ init SEND endpoint in buffer 2 
 +    A8000280  5010001C  !       \ init RECV endpoint out buffer 3 
 +    00010000  5011204C  ! ;     \ USB_SIE_CONTROL   Enable pull up 
 + 
 +\ Endpoint data structures, 4 variable cells & 3 constant cells 
 +\ Name:    CNT  ORG  PKT  PID   INBUF-CTRL  OUTBUF-CTRL   EPxBUF 
 +\ Offsets: 00   04   08   0C       10            14         18 
 +create EP0   4 cells allot   50100080  dup ,   4 + ,    50100100 , 
 +create EP1   4 cells allot   50100088  dup ,   4 + ,    50100180 , 
 +create EP2   4 cells allot   50100090  dup ,   4 + ,    50100200 , 
 +create EP3   4 cells allot   50100098  dup ,   4 + ,    50100280 , 
 + 
 +: @VAL      ( -- +n )       50100002 h@ ; \ wValue 
 +: @LEN      ( -- +n )       50100006 h@ ; \ wLength 
 + 
 +: >UNI      ( a1 u -- a2 )      \ Convert string to unicode format for USB 
 +    dup  2* 302 + pad h!  0      \ String length & notifier 
 +    ?do  c@+  pad i 2* + 2 + h!  loop  drop  pad ; 
 + 
 +\ Addresssing tools for the endpoint data structures 
 +: >CNT      ; immediate     \ 00 - ep0 >cnt !   - Bytes left te send 
 +: >ORG      cell+ ;         \ 04 - ep1 >org @   - Address of next packet 
 +: >PKT      2 cells + ;     \ 08                - Packet size 
 +: >PID      3 cells + ;     \ 0C                - Actual PID 
 +: >ICTRL    4 cells + ;     \ 10                - USB in control register 
 +: >OCTRL    5 cells + ;     \ 14                - USB out control register 
 +: >BUF      6 cells + ;     \ 18                - EPx buffer address 
 + 
 +\ Primitive endpoint functionality 
 +: EP-IN     ( ep -- org buf pkt )     \ Calculate data to send for EPx 
 +    >r  r@ >org @   r@ >buf @   r> >pkt @ ; 
 +     
 +: PREPARE   ( a u ep -- )             \ Prepare data to send for EPx 
 +    >r  dup r@ >cnt !  40 min r@ >pkt !  r> >org ! ; 
 +     
 +: >NEXT     ( ep -- pkt )             \ Get next packet size for EPx 
 +    >r  r@ >pkt @   r@ >org @  +  r@ >org ! 
 +    r@ >pkt @  r@ @  over -  dup r@ !  40 min r> >pkt ! ; 
 +     
 +: !PKT      ( pkt ep -- ictrl )       \ pid + pkt + mask - Store next packet to send for EPx 
 +     > r@ >pid @ or  8000 or  r> >ictrl @  tuck ! ; 
 +      
 +: PREP-RCV  ( ep -- )                 \ Prepare to receive a packet on EPx 
 +    >r r@ >octrl @   r@ >pid @  40 or  over ! 
 +    2000 r> >pid **bix  400 swap **bis ; 
 +     
 +: GONE?     ( ep -- f )     >ictrl @ @ 400 and 0= ; \ Leave true when Epx packet was sent 
 +: USB?      ( -- +n )       usb-state h@ 3 = ;      \ True when host & device are connected 
 + 
 +\ Basic receive & transmit packet handlers 
 +: USB-SEND   ( ep -- )      >r  r@ >next  r@ !pkt  2000 r> >pid **bix  400 swap **bix ; 
 +: USB-RCV    ( pid ep -- )  tuck >pid !  prep-rcv ; 
 + 
 +: BUS-RESET     ( -- ) 
 +    80000  50113050  !                     \ SIE_STATUS - BUS_RESET (Clear bit) 
 +    false  50110000  !  false usb-state !  \ USB-address=0 & USB-state=0, u-config = 0 
 +    false ep3 usb-rcv   false ep2 >pid ! ; \ Allow receiving EP3 & init. transmit EP2 
 + 
 +: SETUP>        ( a u -- )      \ Handle the answer for all setup packages 
 +    2000 ep0 >pid !  ep0 prepare           \ Start with DATA1, setup packet data 
 +    begin 
 +        ep0 ep-in move  ep0 usb-send       \ Store & send packet 
 +        1 50110058                         \ Packet gone? 
 +        begin  2dup bit** until  **bis 
 +    ep0 @ 0= until  2000 ep0 usb-rcv ;     \ Handle ZLP 
 + 
 +: XTABLE    ( +n "name" -- )    \ Define custom execution table 
 +    create 
 +    does> @+    ( req -- ) 
 +    >r r@ for  2dup @ = if              \ Token found? 
 +            nip  rdrop  r> cells +      \ Yes, calc. cell with XT 
 +            @ execute  exit             \ Fetch & execute XT 
 +        then 
 +        cell+ 
 +    next  rdrop  2drop                  \ No token found 
 +    1 5011,0068 !  800 5010,0080 ! ;    \ Send EP0 stall 
 + 
 +: ZLP>          ( -- )      here false setup> ;             \ Send EP0 ZLP 
 + 
 +:noname     me count 1D min >uni  dup c@ @len min setup> ;  \ 0302 Device name string 
 +:noname     usb-state cell+ 4 setup> ;                      \ 0300 Lang-ID string 
 +:noname     config-descr  @len 4B min setup> ;              \ 0200 Config descriptor 
 +:noname     dev-descr  @len 12 min setup> ;                 \ 0100 Device descriptor 
 +4 xtable HANDLE-SETUP   ( req -- ) 
 +    0100 ,     0200 ,      0300 ,     0302 , 
 +    ( dev ) ,  ( conf ) ,  ( 300 ) ,  ( 302 ) , 
 + 
 +:noname     @val usb-state c!  zlp> ;        \ 2221 Set control line state 
 +:noname     usb-state cell+ cell+ 7 setup> ; \ 21A1 Get line coding 
 +:noname     2000 ep0 usb-rcv  zlp> ;         \ 2021 Set line coding 
 +:noname     zlp>  @val   usb-state 2 +  h! ; \ 0900 Set USB configuration 
 +:noname     usb-state 2 + 2 setup> ;         \ 0880 Get USB configuration 
 +:noname     @val handle-setup ;              \ 0680 Basic usb SETUP handler 
 +:noname     zlp>  @val 5011,0000 ! ;         \ 0500 Set device address 
 +7 xtable HANDLE-REQ)    ( -- ) 
 +    0500 ,  0680 ,  0880 ,  0900 ,  2021 ,  21A1 ,  2221 , 
 +    ,       ,       ,       ,       ,       ,       , 
 + 
 +: HANDLE-REQ    ( -- )  20000 5011,3050 !  5010,0000 h@  handle-req) ; 
 + 
 +\ Ring buffer structure for serial I/O 
 +
 +\ 0   = Number of used storage units (CNT) 
 +\ 1   = In pointer (IN) 
 +\ 2   = Out pointer (OUT) 
 +\ 3   = Start of ring buffer 
 +100     constant #L     \ Buffer size 
 +#L 1-   constant #R     \ Buffer mask 
 + 
 +50100300   constant BUFFER1             \ Usess 2 * #L + 24 bytes  = 536 bytes 
 +buffer1 #l + 3 cells + constant BUFFER2 \ Start of out buffer 
 +: #RX       ( -- +n )   buffer1 @ ; \ +n characters in receive buffer 
 +: #TX       ( -- +n )   buffer2 @ ; \ +n characters in send buffer 
 + 
 +: >RX       ( ch -- ) 
 +    buffer1 cell+ >r 
 +    r@ @  r@ 2 cells + +  c!        \ Yes, add char to buffer 
 +    r@ @ 1+ #r and  r@ !            \ Increase & protect write pointer 
 +    1  r> cell-  +! ;               \ Increase used space 
 +: RX>       ( -- ch ) 
 +    buffer1 2 cells + >r 
 +    r@ @  r@ cell+ + c@             \ Read char. from buffer 
 +    r@ @ 1+ #r and r@ !             \ Increase & protect read pointer 
 +    true  r> 2 cells -  +! ;        \ Decrease used space 
 + 
 +: >TX       ( ch -- ) 
 +    buffer2 cell+ >r 
 +    r@ @  r@ 2 cells + +  c!        \ Yes, add char to buffer 
 +    r@ @ 1+ #r and  r@ !            \ Increase & protect write pointer 
 +    1  r> cell-  +! ;               \ Increase used space 
 +: TX>       ( -- ch ) 
 +    buffer2 2 cells + >r 
 +    r@ @  r@ cell+ + c@             \ Read char. from buffer 
 +    r@ @ 1+ #r and  r@ !            \ Increase & protect read pointer 
 +    true  r> 2 cells -  +! ;        \ Decrease used space 
 + 
 +0 value IFLAG 
 +: ENDPOINTS ( -- )   \ Handle used endpoints 
 +    50110058 @ >r 
 +    r@ 04 and if  zlp>      then    \ EP1 active? 
 +    iflag  r@ 80 and or to iflag    \ EP3 active, remember 
 +    r> 50110058 ! ; 
 + 
 +: REQUESTS  ( -- )                  \ Handle all USB requests 
 +    50110098 @ >r ( ints ) 
 +    r@    10 and if  endpoints   then      10 = #04 bitmask 
 +    r@  1000 and if  bus-reset   then    1000 = #12 bitmask 
 +    r> 10000 and if  handle-req  then ; \ 10000 = #16 bitmask 
 + 
 +: USB-HANDLER       ( -- )  \ Handle USB setup & character I/O 
 +    requests 
 +    iflag if                    \ Next RX packet wanted? 
 +        #L #tx - 40 >           \ Yes, enough space in TX buffer? 
 +        #L #rx - 40 > and if    \ And next RX packet fits too? 
 +            ep3 >buf @  5010009C c@ 
 +            0 ?do c@+ >rx loop  \ Yes, fill RX buffer 
 +            drop false to iflag \ Done 
 +            ep3 prep-rcv        \ And allow next RX packet 
 +        then 
 +    then 
 +    usb? if                     \ Still connected? 
 +        #tx if                  \ EP2 Any chars to send? 
 +            ep2 gone? if        \ Yes, previous packet gone? 
 +                ep2 >buf @  #tx  40 umin    \ Packet place & size 
 +                2dup ep2 prepare  bounds    \ Init. transmit pointers 
 +                ?do  tx> i c!  loop         \ Place data in EP2-buffer 
 +                ep2 usb-send                \ Send to host 
 +            then 
 +        then 
 +    then ; 
 + 
 +: USB-KEY?  ( -- f )    usb-handler  #rx ;    \ KEY & EMIT with ringbuffer 
 +: USB-KEY   ( -- c )    begin  usb-handler  #rx until  rx> ; 
 +: USB-EMIT  ( c -- )    begin  usb-handler  #L #tx - until  >tx ; 
 + 
 +: USB-ON            ( -- ) 
 +    50100300  [ #L 2* 6 cells + ] literal  false fill \ Init. buffer space to zero 
 +    start-usb  false usb-state !  false to iflag 
 +    ['] usb-key? to 'key?           \ Install USB CDC char. I/O vectors (noForth specific!) 
 +    ['] usb-key  to 'key 
 +    ['] usb-emit to 'emit ;
  
-hex+usb-on
 </code> </code>
  
-===== Implementation example =====+===== Implementations =====
  
 +[[https://github.com/WillemOuwerkerk/noForth-T-hardware-examples-RP2040/tree/main/USB-CDC|Different noForth implementations for the USB-CDC driver]]
en/pfw/usb_cdc_driver_for_rp2040.1766836471.txt.gz · Last modified: 2025-12-27 12:54 by willem