cnlohr / rv003usb

CH32V003 RISC-V Pure Software USB Controller
MIT License
436 stars 43 forks source link

Best way to implement keyboard? #56

Closed doragasu closed 4 months ago

doragasu commented 4 months ago

I have a project that uses rv003usb to implement a HID keyboard. When I press a button on the board, the CH32V003 sends keys CTRL+SHIFT+1/2 (cycles between 1 and 2 each keypress). So far it works, but there's something bugging me, and it's that the keyboard implementation is sending keyboard status (mostly "nothing pressed") each 8 ms.

I have been monitoring how keyboards work using Wireshark (with usbmon module) and when the host requests the keyboard state with an interrupt IN request, the request is replied when user presses a key (that can be many seconds after the request is issued). But it looks like in rv003usb, the IN request must be replied immediately inside usb_handle_user_in_request() function. I have been doing experiments, and if I put delays when in usb_handle_user_in_request() before the usb_send_xxx(), the device is disconnected by the host.

So the question is, is there a way to only send "meaningful keyboard responses"? (That is, only send the response to the IN request when the keyboard changes state, even if that is many seconds after the keyboard state was requested).

doragasu commented 4 months ago

According to this, when the IN transfer arrives, if device has no data to send (no interrupt), it has to send a NAK. Now, how can I send the NAK (if possible at all) with rv003usb? I have tried without success usb_send_empty(0b01011010); and usb_send_empty(0b10100101); (NAK PID is 0xA, and I am not sure how the bit ordering and the negation is used to construct the PID).

cnlohr commented 4 months ago

Please see the PID section here: https://www.beyondlogic.org/usbnutshell/usb3.shtml

From the code,

        usb_send_data( 0, 0, 2, 0xD2 ); // Send ACK

Which would be 0b11010010 ACKs are 2...

So, NAK would be

0b01011010, as you mentioned above.

I think it's OK to do what you've described.

doragasu commented 4 months ago

Wow, usb_send_empty(0b01011010); was not working, but usb_send_data(0, 0, 2, 0b01011010); did the trick, now the Wireshark capture looks as expected, the IN request is only completed when I press the key and actual keyboard data has to be sent. I have not investigated the difference, maybe usb_send_empty() uses CRC and that is screwing the transfer?

Thanks a lot for help! Also if you want a pure HID keyboard example let me know and I will try preparing a PR!