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 13:03] – [Pseudo code] willemen:pfw:usb_cdc_driver_for_rp2040 [2026-01-13 18:16] (current) – [The idea] willem
Line 18: Line 18:
 hex hex
 Function: DEVICE-DECRIPTOR \ 18 bytes Function: DEVICE-DECRIPTOR \ 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 ) +The USB CDC device descriptor
-10 c, 66 c, ( 6610 ) 00 c, 01 c, ( vsn 1.00 ) 00 c, 02 c, 00 c, 01 c,  align+
  
 Function: CONFIGURATION-DESCRIPTOR \ 9 or 75 bytes 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' x
 +
 +Function: PREPARE  ( a u ep -- )
 +Prepare data packet from address 'a' with the length 'u' to send tp 'ep' x
 +
 +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'
 +
 +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' x
 +
 +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 )
 +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)
 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
  
-hex +: START-USB     ( -- )
-Function: START-USB     ( -- )  \ Initialise the RP2040 USB hardware+
     1000000  4000C000           \ Bit-24 mask & Reset register     1000000  4000C000           \ Bit-24 mask & Reset register
     2dup **bis  2dup **bic      \ Restart USB     2dup **bis  2dup **bic      \ Restart USB
Line 57: Line 241:
     00010000  5011204C  ! ;     \ USB_SIE_CONTROL   Enable pull up     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 ;
 +
 +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.1766837011.txt.gz · Last modified: 2025-12-27 13:03 by willem