├── 3dprint ├── USB_Knob_case.fcstd ├── USB_Knob_case_bottom.stl ├── USB_Knob_case_ring.stl ├── USB_Knob_case_top.stl └── USB_Knob_knob.stl ├── LICENSE ├── README.md ├── documentation ├── USB_Knob_pic1.jpg ├── USB_Knob_pic2.jpg ├── USB_Knob_pic3.jpg ├── USB_Knob_pic4.jpg └── USB_Knob_wiring.png ├── hardware ├── USB_Knob_BOM.tsv ├── USB_Knob_gerber.zip └── USB_Knob_schematic.pdf └── software ├── custom_knob ├── bin │ ├── custom_knob.bin │ └── custom_knob.hex ├── config.h ├── ld │ └── ch32v003.ld ├── macros.h ├── makefile └── src │ ├── ch32v003.h │ ├── gpio.h │ ├── main.c │ ├── system.c │ ├── system.h │ ├── usb.h │ ├── usb_composite.c │ ├── usb_composite.h │ ├── usb_descr.h │ ├── usb_handler.S │ ├── usb_handler.c │ └── usb_handler.h ├── mousewheel_knob ├── bin │ ├── mousewheel_knob.bin │ └── mousewheel_knob.hex ├── config.h ├── ld │ └── ch32v003.ld ├── makefile └── src │ ├── ch32v003.h │ ├── gpio.h │ ├── main.c │ ├── system.c │ ├── system.h │ ├── usb.h │ ├── usb_descr.h │ ├── usb_handler.S │ ├── usb_handler.c │ ├── usb_handler.h │ ├── usb_mouse.c │ └── usb_mouse.h └── volume_knob ├── bin ├── volume_knob.bin └── volume_knob.hex ├── config.h ├── ld └── ch32v003.ld ├── makefile └── src ├── ch32v003.h ├── gpio.h ├── main.c ├── system.c ├── system.h ├── usb.h ├── usb_consumer.c ├── usb_consumer.h ├── usb_descr.h ├── usb_handler.S ├── usb_handler.c └── usb_handler.h /3dprint/USB_Knob_case.fcstd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/bd0be4a51de5bf36abb6228fba8c7d0bc2adba6d/3dprint/USB_Knob_case.fcstd -------------------------------------------------------------------------------- /3dprint/USB_Knob_case_bottom.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/bd0be4a51de5bf36abb6228fba8c7d0bc2adba6d/3dprint/USB_Knob_case_bottom.stl -------------------------------------------------------------------------------- /3dprint/USB_Knob_case_ring.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/bd0be4a51de5bf36abb6228fba8c7d0bc2adba6d/3dprint/USB_Knob_case_ring.stl -------------------------------------------------------------------------------- /3dprint/USB_Knob_case_top.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/bd0be4a51de5bf36abb6228fba8c7d0bc2adba6d/3dprint/USB_Knob_case_top.stl -------------------------------------------------------------------------------- /3dprint/USB_Knob_knob.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/bd0be4a51de5bf36abb6228fba8c7d0bc2adba6d/3dprint/USB_Knob_knob.stl -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. 2 | To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send 3 | a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CH32V003 USB Knob - Rotary Encoder with USB Interface 2 | The USB Knob is a versatile device that combines the functionality of a rotary encoder with the convenience of a USB interface. It is based on the cheap CH32V003 32-bit RISC-V microcontroller. One of the standout features of the USB Knob is that it requires no driver installation, making it incredibly easy to use. The device acts as a USB human interface device (HID), meaning that it can be recognized and utilized by most computers without the need for additional software. This makes the USB Knob an ideal choice for a wide range of applications, including volume control for PCs. 3 | 4 | ![USB_Knob_pic1.jpg](https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/main/documentation/USB_Knob_pic1.jpg) 5 | 6 | # Hardware 7 | ## Schematic 8 | ![USB_Knob_wiring.png](https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/main/documentation/USB_Knob_wiring.png) 9 | 10 | ## The CH32V003 Family of 32-bit RISC-V Microcontrollers 11 | The CH32V003 series is a collection of industrial-grade general-purpose microcontrollers that utilize the QingKe RISC-V2A core design supporting the RV32EC instruction set. These microcontrollers are equipped with various features such as a 48MHz system main frequency, 16KB flash, 2KB SRAM, 2.7V - 5.5V supply voltage support, a single-wire serial debug interface, low power consumption, and an ultra-small package. Additionally, the CH32V003 series includes a built-in set of components including a DMA controller, a 10-bit ADC, op-amp comparators, multiple timers, and standard communication interfaces such as USART, I2C, and SPI. 12 | 13 | ## Building Instructions 14 | 1. Take the Gerber files (the *zip* file inside the *hardware* folder) and upload them to a PCB (printed circuit board) manufacturer of your choice (e.g., [JLCPCB](https://jlcpcb.com/)). They will use these files to create the circuit board for your device and send it to you. 15 | 2. Once you have the PCB, you can start soldering the components onto it. Use the BOM (bill of materials) and schematic as a guide to make sure everything is connected correctly. You can find the corresponding files in the *hardware* folder. 16 | 3. Upload the firmware by following the instructions in the next section (see below). 17 | 4. To create the case for your device, use the *stl* files in the *3dprint* folder with your 3D printer. Make sure to use transparent filament for the ring. 18 | 5. After printing, secure the PCB to the bottom of the case using four self-tapping M2x5mm screws. 19 | 6. Next, glue the ring from the bottom into the circular recess in the top of the case. 20 | 7. Finally, assemble the case. Place the knob onto the rotary encoder. Your device is now ready to use! 21 | 22 | ![USB_Knob_pic2.jpg](https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/main/documentation/USB_Knob_pic2.jpg) 23 | 24 | # Software 25 | ## Software USB 26 | Since the CH32V003 lacks a hardware USB peripheral, USB functionality is handled in software. Charles Lohr's excellent implementation, [RV003USB](https://github.com/cnlohr/rv003usb), is used for this. It emulates a USB low-speed device using pin-change interrupts and bit-banging, with assembly code for the low-level protocol and some C code for higher-level functionality. 27 | 28 | ## Firmware Versions 29 | ### Volume Knob 30 | This firmware enables the USB Knob to control the volume of your computer. By turning the knob, you can increase or decrease the volume, while pressing the knob will mute or unmute the sound. The device is recognized as a USB HID consumer multimedia keyboard and should work immediately without the need for any driver installation. 31 | 32 | ### Mouse Wheel Knob 33 | This firmware is designed to transform the USB Knob into a mouse wheel. By rotating the knob, it emulates the movement of a mouse wheel, allowing users to scroll up and down web pages, among other things. Pressing the knob is equivalent to clicking on the mouse wheel or the middle mouse button. The device registers as a USB HID mouse on the computer and should work immediately without the need for driver installation. 34 | 35 | ### Custom Knob 36 | This firmware is perfect for customizing it according to your own preferences. It allows for the free mapping of USB HID actions to USB Knob events. The firmware is structured and commented in such a way that it should be possible to make adjustments even with basic programming skills. It is designed as a USB HID composite device that combines a keyboard, a mouse, and a game controller. 37 | 38 | ## Programming and Debugging Device 39 | To program the CH32V003 microcontroller, you will need a special programming device which utilizes the proprietary single-wire serial debug interface (SDI). The [WCH-LinkE](http://www.wch-ic.com/products/WCH-Link.html) (pay attention to the "E" in the name) is a suitable device for this purpose and can be purchased commercially for around $4. This debugging tool is not only compatible with the CH32V003 but also with other WCH RISC-V and ARM-based microcontrollers. 40 | 41 | ![CH32V003_wch-linke.jpg](https://raw.githubusercontent.com/wagiminator/Development-Boards/main/CH32V003F4P6_DevBoard/documentation/CH32V003_wch-linke.jpg) 42 | 43 | To use the WCH-LinkE on Linux, you need to grant access permissions beforehand by executing the following commands: 44 | ``` 45 | echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="8010", MODE="666"' | sudo tee /etc/udev/rules.d/99-WCH-LinkE.rules 46 | echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="8012", MODE="666"' | sudo tee -a /etc/udev/rules.d/99-WCH-LinkE.rules 47 | sudo udevadm control --reload-rules 48 | ``` 49 | 50 | On Windows, if you need to you can install the WinUSB driver over the WCH interface 1 using the [Zadig](https://zadig.akeo.ie/) tool. 51 | 52 | To upload the firmware, you need to ensure that the USB Knob is disconnected from USB. Then, you should make the following connections to the WCH-LinkE: 53 | 54 | ``` 55 | WCH-LinkE USB-Knob 56 | +-------+ +------+ 57 | | SWDIO| <--> |DIO | 58 | | GND| ---> |GND | 59 | | 3V3| ---> |VCC | 60 | +-------+ +------+ 61 | ``` 62 | 63 | If the blue LED on the WCH-LinkE remains illuminated once it is connected to the USB port, it means that the device is currently in ARM mode and must be switched to RISC-V mode initially. There are a few ways to accomplish this: 64 | - You can utilize the Python command-line tool [rvprog](https://pypi.org/project/rvprog/) (with *-v* option). 65 | - Alternatively, you can select "WCH-LinkRV" in the software provided by WCH, such as MounRiver Studio or WCH-LinkUtility. 66 | - Another option is to hold down the ModeS button on the device while plugging it into the USB port. 67 | 68 | More information can be found in the [WCH-Link User Manual](http://www.wch-ic.com/downloads/WCH-LinkUserManual_PDF.html). 69 | 70 | ## Compiling and Uploading Firmware using the Makefile 71 | ### Linux 72 | Install the toolchain (GCC compiler, Python3, and rvprog): 73 | ``` 74 | sudo apt install build-essential libnewlib-dev gcc-riscv64-unknown-elf 75 | sudo apt install python3 python3-pip 76 | pip install rvprog 77 | ``` 78 | 79 | Disconnect the USB cable from the USB Knob. Connect the USB-Knob via the 3-pin PROG header to the WCH-LinkE programming device. Open a terminal and navigate to the folder with the *makefile*. Run the following command to compile and upload: 80 | ``` 81 | make flash 82 | ``` 83 | 84 | ### Other Operating Systems 85 | Follow the instructions on [CNLohr's ch32v003fun page](https://github.com/cnlohr/ch32v003fun/wiki/Installation) to set up the toolchain on your respective operating system (for Windows, use WSL). Also, install [Python3](https://www.pythontutorial.net/getting-started/install-python/) and [rvprog](https://pypi.org/project/rvprog/). Compile and upload with "make flash". Note that I only have Debian-based Linux and have not tested it on other operating systems. 86 | 87 | ## Uploading pre-compiled Firmware Binary 88 | WCH offers the free but closed-source software [WCH-LinkUtility](https://www.wch.cn/downloads/WCH-LinkUtility_ZIP.html) to upload the precompiled hex-file with Windows. Select the "WCH-LinkRV" mode in the software, open the *.hex* file in the *bin* folder and upload it to the microcontroller. 89 | 90 | Alternatively, there is an open-source tool called [minichlink](https://github.com/cnlohr/ch32v003fun/tree/master/minichlink) developed by Charles Lohr (CNLohr). It can be used with Windows, Linux and Mac. 91 | 92 | If you have installed [Python3](https://www.pythontutorial.net/getting-started/install-python/) on your system, you can also use the platform-independent open-source command-line tool [rvprog](https://pypi.org/project/rvprog/) for uploading: 93 | ``` 94 | rvprog -f bin/.bin 95 | ``` 96 | 97 | # References, Links and Notes 98 | - [EasyEDA Design Files](https://oshwlab.com/wagiminator) 99 | - [CNLohr: rv003usb](https://github.com/cnlohr/rv003usb) 100 | - [MCU Templates](https://github.com/wagiminator/MCU-Templates) 101 | - [MCU Flash Tools](https://github.com/wagiminator/MCU-Flash-Tools) 102 | - [CH32V003 Datasheets](http://www.wch-ic.com/products/CH32V003.html) 103 | - [ATtiny85 TinyKnob](https://github.com/wagiminator/ATtiny85-TinyKnob) 104 | - [CH32X033 USB Knob](https://github.com/wagiminator/CH32X033-USB-Knob) 105 | - [CH552E USB Knob](https://github.com/wagiminator/CH552-USB-Knob) 106 | - [CH552E MacroPad mini](https://github.com/wagiminator/CH552-Macropad-mini) 107 | - [CH552G MacroPad plus](https://github.com/wagiminator/CH552-MacroPad-plus) 108 | 109 | ![USB_Knob_pic3.jpg](https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/main/documentation/USB_Knob_pic3.jpg) 110 | 111 | # License 112 | ![license.png](https://i.creativecommons.org/l/by-sa/3.0/88x31.png) 113 | 114 | This work is licensed under Creative Commons Attribution-ShareAlike 3.0 Unported License. 115 | (http://creativecommons.org/licenses/by-sa/3.0/) 116 | -------------------------------------------------------------------------------- /documentation/USB_Knob_pic1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/bd0be4a51de5bf36abb6228fba8c7d0bc2adba6d/documentation/USB_Knob_pic1.jpg -------------------------------------------------------------------------------- /documentation/USB_Knob_pic2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/bd0be4a51de5bf36abb6228fba8c7d0bc2adba6d/documentation/USB_Knob_pic2.jpg -------------------------------------------------------------------------------- /documentation/USB_Knob_pic3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/bd0be4a51de5bf36abb6228fba8c7d0bc2adba6d/documentation/USB_Knob_pic3.jpg -------------------------------------------------------------------------------- /documentation/USB_Knob_pic4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/bd0be4a51de5bf36abb6228fba8c7d0bc2adba6d/documentation/USB_Knob_pic4.jpg -------------------------------------------------------------------------------- /documentation/USB_Knob_wiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/bd0be4a51de5bf36abb6228fba8c7d0bc2adba6d/documentation/USB_Knob_wiring.png -------------------------------------------------------------------------------- /hardware/USB_Knob_BOM.tsv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/bd0be4a51de5bf36abb6228fba8c7d0bc2adba6d/hardware/USB_Knob_BOM.tsv -------------------------------------------------------------------------------- /hardware/USB_Knob_gerber.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/bd0be4a51de5bf36abb6228fba8c7d0bc2adba6d/hardware/USB_Knob_gerber.zip -------------------------------------------------------------------------------- /hardware/USB_Knob_schematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/bd0be4a51de5bf36abb6228fba8c7d0bc2adba6d/hardware/USB_Knob_schematic.pdf -------------------------------------------------------------------------------- /software/custom_knob/bin/custom_knob.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/bd0be4a51de5bf36abb6228fba8c7d0bc2adba6d/software/custom_knob/bin/custom_knob.bin -------------------------------------------------------------------------------- /software/custom_knob/bin/custom_knob.hex: -------------------------------------------------------------------------------- 1 | :100000006F00E01A00000000AC010000AC0100002D 2 | :1000100000000000000000000000000000000000E0 3 | :1000200000000000000000000000000000000000D0 4 | :10003000AC01000000000000AC0100000000000066 5 | :10004000AC010000AC010000AC010000AC010000FC 6 | :10005000B0030000AC010000AC010000AC010000E6 7 | :10006000AC010000AC010000AC010000AC010000DC 8 | :10007000AC010000AC010000AC010000AC010000CC 9 | :10008000AC010000AC010000AC010000AC010000BC 10 | :0C009000AC010000AC010000AC0100005D 11 | :10009C00B7F700E09C4737F700E03E951C47898F87 12 | :1000AC00E3CE07FE8280B71701409843511122C25C 13 | :1000BC001377F7F026C006C41367070898C30947DF 14 | :1000CC0098CB9443FD751387F50F0566F98E1307CE 15 | :1000DC000680D98E94C3114588CB94433707F1FF22 16 | :1000EC007D17F98E37070800D98E94C3414798CBFA 17 | :1000FC0023AA0182B7160240984EBD051306068846 18 | :10010C001367570098CE03A707800144B70400205B 19 | :10011C006D8F518F23A0E780B707014023A4070000 20 | :10012C0023A0A74023A6A74037071000B7E700E09D 21 | :10013C0023A0E710B71701409847098B21E3984794 22 | :10014C0093870400118B0DCB255737B50300A38083 23 | :10015C00E700130505982D3FB7070020938707008C 24 | :10016C00A3800700371701401C47898BF5DF3165E9 25 | :10017C00130505B8313F7DBF2957C1BF9C47C18BC3 26 | :10018C0009E8F5F7938704000957A380E7000544B5 27 | :10019C00F9BFF1DF93870400A38007000144C1BFBE 28 | :1001AC0001A09307200B970100209381E164138138 29 | :1001BC00010013058008731005308D457390458040 30 | :1001CC0017050000130545E34D8D731055307390E2 31 | :1001DC001734B70700200567938707001307C791EB 32 | :1001EC009386818163EBD704938781811387818701 33 | :1001FC0063EBE704B7270240054798C3B707000134 34 | :10020C0037170240938717081CC3B70600021C431C 35 | :10021C00F58FF5DF89475CC3A146B7170240D84379 36 | :10022C00318BE31ED7FE37F700E0954614C3984F89 37 | :10023C001367470398CF73002030104311079107C1 38 | :10024C0023AEC7FE45B7910723AE07FE55B79317EC 39 | :10025C005600B306F70010C38C529306300C99C1AC 40 | :10026C009306B004BA979C57D18FADCB85476312D8 41 | :10027C00F6081387118183470700FD1793F7F70FD3 42 | :10028C002300F70085EB8D472300F700511122C2A4 43 | :10029C0001461384C18095451385C18006C4C12EC7 44 | :1002AC0023010400A3010400A24023020400124411 45 | :1002BC00310182800947638CE7000D47E388E7FC36 46 | :1002CC006399C7020146A145138541804DA63705A8 47 | :1002DC00002001468D4513050500CDBF484F1C5F1E 48 | :1002EC004C570E05898D3E95A14763C6B7006345F3 49 | :1002FC00B0003685B5A6A1450146C9BF0843931782 50 | :10030C005500BA97D4536393C60893B61600D4D34A 51 | :10031C003DE51C47A5CF83D6450083D7250003D5E3 52 | :10032C006500C206DD8E83D70500232E0700232C23 53 | :10033C0007022324070223260702232407008583B0 54 | :10034C0013060034639AC70485679387878C93855B 55 | :10035C0047052A8390436310D602D04310DF03C6AF 56 | :10036C0087009312060193D20201637355001A861B 57 | :10037C004206418250D7B107E39EB7FC01A88547DE 58 | :10038C006316F50003C70500238CE1809306200D4E 59 | :10039C00094681450145E1AC13060028E398C7FEE8 60 | :1003AC0054C3EDB75D712AC03ECAB717014093879D 61 | :1003BC000780884719892EC232C436C63AC826CE61 62 | :1003CC0012D837320140130282C58C4799896309D0 63 | :1003DC00052288471989631BB50288471989631759 64 | :1003EC00B502884719896313B50288471989631FB9 65 | :1003FC00B50088471989631BB500884719896317AD 66 | :10040C00B500884719896313B50009A022CC0146B1 67 | :10041C0016D01AD2232002008847198963010518C7 68 | :10042C002D8DA98D09A009C909A0804719882D8C8B 69 | :10043C0011E009A0C5B701001ED406DAA144194485 70 | :10044C0001000100C166FD16296705079303F1033E 71 | :10045C00930200080100010023200200884719893B 72 | :10046C00630F05122D8DA98D01001335150006069D 73 | :10047C00498E7D15498C1D887D14FD14E1F493047F 74 | :10048C0000061375C60009C55147F9461300000054 75 | :10049C002380C3009383130023200200884719890B 76 | :1004AC002D8D0DC5A98D09A01944BDC91395F60153 77 | :1004BC007D858582798DA98E0582FD1413F57400D6 78 | :1004CC0061D9010001000100E1F8D1A87D1413F5F8 79 | :1004DC0016007D15798D8582A98E05821366060816 80 | :1004EC0011C8FD1413F5740045D50100010001007D 81 | :1004FC00C5F475A0FD1413F5740009E52380C30041 82 | :10050C0093831300232002008847198911C92D8D6C 83 | :10051C00A98D51C5194401000D457D157DFDADFC1E 84 | :10052C0013F574002DED9305F103882185059700D3 85 | :10053C00000093800007930755FB425217070020D9 86 | :10054C00130747ADC9C785EA92211375F6071D82BB 87 | :10055C003D8A09446375860401C540236311A404D4 88 | :10056C00938747FCC1CFC517639407006FF03FCE4C 89 | :10057C008917B5CB2DA02D640504818E8DE2B38631 90 | :10058C00B3408506938787F80146639407006FF0A4 91 | :10059C00FFD6C5170546639407006FF03FD662443B 92 | :1005AC00F24482521253A253D250F2444252224687 93 | :1005BC00B2464247924509A0B7070140938747418D 94 | :1005CC00114588C30245D247616173002030104346 95 | :1005DC0016063A967106484285452D8D48C208424A 96 | :1005EC00050508C26DBF10C385450CC716063A96A3 97 | :1005FC000CD281454CCE0CDE4CD255B710A345B76E 98 | :10060C0037F500E02105170700201307A7A00C4BB6 99 | :10061C00104110CB0D8E50CBB165938505B80D8E66 100 | :10062C008567938707FAE352F6F8FD7793870706F9 101 | :10063C00E34DF6F60C4FB2950CCFA5D93717024007 102 | :10064C000843135635007D8A930770F07D8DB305F2 103 | :10065C00B04013D6954041060E06518D08C3B1B774 104 | :10066C00AA86170500001305251389452E8641110E 105 | :10067C0022C026C2B7170140938707809843FD74A8 106 | :10068C00BD04658F93040022458FB70404008904D0 107 | :10069C0084CB98C337030600190312C49E0613E4D7 108 | :1006AC000604814209E6A962850241667D168E0523 109 | :1006BC00AE8337320140130282C51947BD4509A0EC 110 | :1006CC00A2860580858A7D1781E6B3C4640019472C 111 | :1006DC0084CBFD1591C58D46FD16FDFE0100CDB7F1 112 | :1006EC009E85ADC5FD156394020041667D16010023 113 | :1006FC0000210505A286858A81CE05809376160099 114 | :10070C00FD16B3F656000582358E7D1735CB29A81C 115 | :10071C0005809316F601FD86B3C4640084CB19479B 116 | :10072C000582B3F65600358E81C993F67500FD151A 117 | :10073C00E1D209A0C1B70100638A020093D2820002 118 | :10074C009D45E38902FA1344F6FF6DB78D46FD16FD 119 | :10075C00FDFEB704060084CBA146FD16FDFEB704D2 120 | :10076C000200910484CB84439306F0CCF58C930661 121 | :10077C000044D58C84C302449244224241018280BD 122 | :10078C009146FD16FDFEB3C464001947010001003B 123 | :10079C0084CB59BF0000000018034300480033000D 124 | :1007AC00320056003000300033004800490044004D 125 | :1007BC0000000000120355005300420020004B00C3 126 | :1007CC006E006F0062000000180377006100670084 127 | :1007DC0069006D0069006E00610074006F007200AA 128 | :1007EC000000000004030904090229000101008033 129 | :1007FC00190904000002030101040921100121015F 130 | :10080C002293000705810308000107050203080075 131 | :10081C000A0000001201100100000008091203C0B8 132 | :10082C00000101020301000005010906A101850177 133 | :10083C00050719E029E715002501750195088102C6 134 | :10084C00750895018103190029E7150026FF00752D 135 | :10085C000895058100050819012905150025017564 136 | :10086C000195059102750395019103C0050C0901D1 137 | :10087C00A101850219002A3C021500263C027510C4 138 | :10088C0095018100C005010902A1010901A10085A2 139 | :10089C00030509190129031500250175019503812B 140 | :1008AC0002750595018103050109300931093815D7 141 | :1008BC0081257F750895038106C0C00000010000EA 142 | :1008CC00200800001200000000020000F4070000E5 143 | :1008DC0029000000002200003408000093000000F2 144 | :1008EC0000030000F00700000400000001030904ED 145 | :1008FC00D40700001800000002030904C007000020 146 | :10090C001200000003030904A407000018000000F3 147 | :10091C0002000000010000000000000003000000C5 148 | :08092C000003000000000000C0 149 | :00000001FF 150 | -------------------------------------------------------------------------------- /software/custom_knob/config.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // User Configurations 3 | // =================================================================================== 4 | 5 | #pragma once 6 | 7 | // Rotary encoder in definitions 8 | #define PIN_ENC_A PC1 // pin connected to rotary encoder A 9 | #define PIN_ENC_B PC2 // pin connected to rotary encoder B 10 | #define PIN_ENC_SW PC4 // pin connected to rotary encoder switch 11 | 12 | // USB pin definitions 13 | #define USB_PORT A // [A,C,D] GPIO Port to use with D+ and D- 14 | #define USB_PIN_DP 1 // [0-4] GPIO Number for USB D+ Pin 15 | #define USB_PIN_DM 2 // [0-4] GPIO Number for USB D- Pin 16 | 17 | // USB configuration descriptor 18 | #define USB_MAX_POWER_mA 50 // max power in mA 19 | 20 | // USB device descriptor 21 | #define USB_VENDOR_ID 0x1209 // VID 22 | #define USB_PRODUCT_ID 0xc003 // PID 23 | #define USB_DEVICE_VERSION 0x0100 // v1.0 (BCD-format) 24 | #define USB_LANGUAGE 0x0409 // US English 25 | 26 | // USB descriptor strings 27 | #define MANUF_STR "wagiminator" 28 | #define PROD_STR "USB Knob" 29 | #define SERIAL_STR "CH32V003HID" 30 | -------------------------------------------------------------------------------- /software/custom_knob/ld/ch32v003.ld: -------------------------------------------------------------------------------- 1 | ENTRY( jump_reset ) 2 | 3 | MEMORY 4 | { 5 | FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 16K 6 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 2K 7 | } 8 | 9 | SECTIONS 10 | { 11 | .init : 12 | { 13 | _sinit = .; 14 | . = ALIGN(4); 15 | KEEP(*(SORT_NONE(.init.jump))) 16 | KEEP(*(SORT_NONE(.init.vectors))) 17 | . = ALIGN(4); 18 | _einit = .; 19 | } >FLASH AT>FLASH 20 | 21 | .text : 22 | { 23 | . = ALIGN(4); 24 | *(.text) 25 | *(.text.*) 26 | *(.rodata) 27 | *(.rodata*) 28 | *(.gnu.linkonce.t.*) 29 | . = ALIGN(4); 30 | } >FLASH AT>FLASH 31 | 32 | .fini : 33 | { 34 | KEEP(*(SORT_NONE(.fini))) 35 | . = ALIGN(4); 36 | } >FLASH AT>FLASH 37 | 38 | PROVIDE(_etext = .); 39 | PROVIDE(_eitcm = .); 40 | 41 | .preinit_array : 42 | { 43 | PROVIDE_HIDDEN(__preinit_array_start = .); 44 | KEEP(*(.preinit_array)) 45 | PROVIDE_HIDDEN(__preinit_array_end = .); 46 | } >FLASH AT>FLASH 47 | 48 | .init_array : 49 | { 50 | PROVIDE_HIDDEN(__init_array_start = .); 51 | KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)SORT_BY_INIT_PRIORITY(.ctors.*))) 52 | KEEP(*(.init_array EXCLUDE_FILE(*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o) .ctors)) 53 | PROVIDE_HIDDEN(__init_array_end = .); 54 | } >FLASH AT>FLASH 55 | 56 | .fini_array : 57 | { 58 | PROVIDE_HIDDEN(__fini_array_start = .); 59 | KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) 60 | KEEP(*(.fini_array EXCLUDE_FILE(*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o) .dtors)) 61 | PROVIDE_HIDDEN(__fini_array_end = .); 62 | } >FLASH AT>FLASH 63 | 64 | .ctors : 65 | { 66 | KEEP(*crtbegin.o(.ctors)) 67 | KEEP(*crtbegin?.o(.ctors)) 68 | KEEP(*(EXCLUDE_FILE(*crtend.o *crtend?.o) .ctors)) 69 | KEEP(*(SORT(.ctors.*))) 70 | KEEP(*(.ctors)) 71 | } >FLASH AT>FLASH 72 | 73 | .dtors : 74 | { 75 | KEEP(*crtbegin.o(.dtors)) 76 | KEEP(*crtbegin?.o(.dtors)) 77 | KEEP(*(EXCLUDE_FILE(*crtend.o *crtend?.o) .dtors)) 78 | KEEP(*(SORT(.dtors.*))) 79 | KEEP(*(.dtors)) 80 | } >FLASH AT>FLASH 81 | 82 | .dalign : 83 | { 84 | . = ALIGN(4); 85 | PROVIDE(_data_vma = .); 86 | } >RAM AT>FLASH 87 | 88 | .dlalign : 89 | { 90 | . = ALIGN(4); 91 | PROVIDE(_data_lma = .); 92 | } >FLASH AT>FLASH 93 | 94 | .data : 95 | { 96 | . = ALIGN(4); 97 | *(.gnu.linkonce.r.*) 98 | *(.data .data.*) 99 | *(.gnu.linkonce.d.*) 100 | . = ALIGN(8); 101 | PROVIDE(__global_pointer$ = . + 0x800); 102 | *(.sdata .sdata.*) 103 | *(.sdata2*) 104 | *(.gnu.linkonce.s.*) 105 | . = ALIGN(8); 106 | *(.srodata.cst16) 107 | *(.srodata.cst8) 108 | *(.srodata.cst4) 109 | *(.srodata.cst2) 110 | *(.srodata .srodata.*) 111 | . = ALIGN(4); 112 | PROVIDE(_edata = .); 113 | } >RAM AT>FLASH 114 | 115 | .bss : 116 | { 117 | . = ALIGN(4); 118 | PROVIDE(_sbss = .); 119 | *(.sbss*) 120 | *(.gnu.linkonce.sb.*) 121 | *(.bss*) 122 | *(.gnu.linkonce.b.*) 123 | *(COMMON*) 124 | . = ALIGN(4); 125 | PROVIDE(_ebss = .); 126 | } >RAM AT>FLASH 127 | 128 | PROVIDE(_end = _ebss); 129 | PROVIDE(end = . ); 130 | PROVIDE(_eusrstack = ORIGIN(RAM) + LENGTH(RAM)); 131 | } 132 | -------------------------------------------------------------------------------- /software/custom_knob/macros.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Macro Functions which associate Actions with Events (Customize your USB Knob here!) 3 | // =================================================================================== 4 | // 5 | // The list of available USB HID functions can be found in src/usb_composite.h 6 | 7 | // Rotary encoder example -> volume control knob 8 | // --------------------------------------------- 9 | 10 | // Define action(s) if encoder was rotated clockwise 11 | static inline void ENC_CW_ACTION() { 12 | CON_press(CON_VOL_UP); // press VOLUME UP key 13 | } 14 | 15 | // Define action(s) after encoder was rotated clockwise 16 | static inline void ENC_CW_RELEASED() { 17 | CON_release(); // release VOLUME UP KEY 18 | } 19 | 20 | // Define action(s) if encoder was rotated counter-clockwise 21 | static inline void ENC_CCW_ACTION() { 22 | CON_press(CON_VOL_DOWN); // press VOLUME DOWN key 23 | } 24 | 25 | // Define action(s) after encoder was rotated counter-clockwise 26 | static inline void ENC_CCW_RELEASED() { 27 | CON_release(); // release VOLUME DOWN KEY 28 | } 29 | 30 | // Define action(s) if encoder switch was pressed 31 | static inline void ENC_SW_PRESSED() { 32 | CON_press(CON_VOL_MUTE); // press VOLUME MUTE key 33 | } 34 | 35 | // Define action(s) if encoder switch was released 36 | static inline void ENC_SW_RELEASED() { 37 | CON_release(); // release VOLUME MUTE key 38 | } 39 | -------------------------------------------------------------------------------- /software/custom_knob/makefile: -------------------------------------------------------------------------------- 1 | # =================================================================================== 2 | # Project Makefile 3 | # =================================================================================== 4 | # Project: USB Rotary Encoder for CH32V003 - Custom Control 5 | # Author: Stefan Wagner 6 | # Year: 2024 7 | # URL: https://github.com/wagiminator 8 | # =================================================================================== 9 | # Install toolchain: 10 | # sudo apt install build-essential libnewlib-dev gcc-riscv64-unknown-elf 11 | # sudo apt install python3 python3-pip 12 | # pip install rvprog 13 | # 14 | # Provide access permission to WCH-LinkE programmer: 15 | # echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="8010", MODE="666"' | sudo tee /etc/udev/rules.d/99-WCH-LinkE.rules 16 | # echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="8012", MODE="666"' | sudo tee -a /etc/udev/rules.d/99-WCH-LinkE.rules 17 | # sudo udevadm control --reload-rules 18 | # 19 | # Connect WCH-LinkE programmer to your board. Type "make flash" in the command line. 20 | # =================================================================================== 21 | 22 | # Files and Folders 23 | TARGET = custom_knob 24 | INCLUDE = include 25 | SOURCE = src 26 | BIN = bin 27 | 28 | # Microcontroller Settings 29 | F_CPU = 48000000 30 | LDSCRIPT = ld/ch32v003.ld 31 | CPUARCH = -march=rv32ec -mabi=ilp32e 32 | 33 | # Toolchain 34 | PREFIX = riscv64-unknown-elf 35 | CC = $(PREFIX)-gcc 36 | OBJCOPY = $(PREFIX)-objcopy 37 | OBJDUMP = $(PREFIX)-objdump 38 | OBJSIZE = $(PREFIX)-size 39 | NEWLIB = /usr/include/newlib 40 | ISPTOOL = rvprog -f $(BIN)/$(TARGET).bin 41 | CLEAN = rm -f *.lst *.obj *.cof *.list *.map *.eep.hex *.o *.d 42 | 43 | # Compiler Flags 44 | CFLAGS = -g -Os -flto -ffunction-sections -fdata-sections -fno-builtin -nostdlib 45 | CFLAGS += $(CPUARCH) -DF_CPU=$(F_CPU) -I$(NEWLIB) -I$(INCLUDE) -I$(SOURCE) -I. -Wall 46 | LDFLAGS = -T$(LDSCRIPT) -lgcc -Wl,--gc-sections,--build-id=none 47 | CFILES = $(wildcard ./*.c) $(wildcard $(SOURCE)/*.c) $(wildcard $(SOURCE)/*.S) 48 | 49 | # Symbolic Targets 50 | help: 51 | @echo "Use the following commands:" 52 | @echo "make all compile and build $(TARGET).elf/.bin/.hex/.asm" 53 | @echo "make hex compile and build $(TARGET).hex" 54 | @echo "make asm compile and disassemble to $(TARGET).asm" 55 | @echo "make bin compile and build $(TARGET).bin" 56 | @echo "make flash compile and upload to MCU" 57 | @echo "make clean remove all build files" 58 | 59 | $(BIN)/$(TARGET).elf: $(CFILES) 60 | @echo "Building $(BIN)/$(TARGET).elf ..." 61 | @mkdir -p $(BIN) 62 | @$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) 63 | 64 | $(BIN)/$(TARGET).lst: $(BIN)/$(TARGET).elf 65 | @echo "Building $(BIN)/$(TARGET).lst ..." 66 | @$(OBJDUMP) -S $^ > $(BIN)/$(TARGET).lst 67 | 68 | $(BIN)/$(TARGET).map: $(BIN)/$(TARGET).elf 69 | @echo "Building $(BIN)/$(TARGET).map ..." 70 | @$(OBJDUMP) -t $^ > $(BIN)/$(TARGET).map 71 | 72 | $(BIN)/$(TARGET).bin: $(BIN)/$(TARGET).elf 73 | @echo "Building $(BIN)/$(TARGET).bin ..." 74 | @$(OBJCOPY) -O binary $< $(BIN)/$(TARGET).bin 75 | 76 | $(BIN)/$(TARGET).hex: $(BIN)/$(TARGET).elf 77 | @echo "Building $(BIN)/$(TARGET).hex ..." 78 | @$(OBJCOPY) -O ihex $< $(BIN)/$(TARGET).hex 79 | 80 | $(BIN)/$(TARGET).asm: $(BIN)/$(TARGET).elf 81 | @echo "Disassembling to $(BIN)/$(TARGET).asm ..." 82 | @$(OBJDUMP) -d $(BIN)/$(TARGET).elf > $(BIN)/$(TARGET).asm 83 | 84 | all: $(BIN)/$(TARGET).lst $(BIN)/$(TARGET).map $(BIN)/$(TARGET).bin $(BIN)/$(TARGET).hex $(BIN)/$(TARGET).asm size 85 | 86 | elf: $(BIN)/$(TARGET).elf removetemp size 87 | 88 | bin: $(BIN)/$(TARGET).bin removetemp size removeelf 89 | 90 | hex: $(BIN)/$(TARGET).hex removetemp size removeelf 91 | 92 | asm: $(BIN)/$(TARGET).asm removetemp size removeelf 93 | 94 | flash: $(BIN)/$(TARGET).bin size removeelf 95 | @echo "Uploading to MCU ..." 96 | @$(ISPTOOL) 97 | 98 | clean: 99 | @echo "Cleaning all up ..." 100 | @$(CLEAN) 101 | @rm -f $(BIN)/$(TARGET).elf $(BIN)/$(TARGET).lst $(BIN)/$(TARGET).map $(BIN)/$(TARGET).bin $(BIN)/$(TARGET).hex $(BIN)/$(TARGET).asm 102 | 103 | size: 104 | @echo "------------------" 105 | @echo "FLASH: $(shell $(OBJSIZE) -d $(BIN)/$(TARGET).elf | awk '/[0-9]/ {print $$1 + $$2}') bytes" 106 | @echo "SRAM: $(shell $(OBJSIZE) -d $(BIN)/$(TARGET).elf | awk '/[0-9]/ {print $$2 + $$3}') bytes" 107 | @echo "------------------" 108 | 109 | removetemp: 110 | @echo "Removing temporary files ..." 111 | @$(CLEAN) 112 | 113 | removeelf: 114 | @echo "Removing $(BIN)/$(TARGET).elf ..." 115 | @rm -f $(BIN)/$(TARGET).elf 116 | -------------------------------------------------------------------------------- /software/custom_knob/src/main.c: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Project: USB Rotary Encoder for CH32V003 - Custom Control 3 | // Version: v1.0 4 | // Year: 2024 5 | // Author: Stefan Wagner 6 | // Github: https://github.com/wagiminator 7 | // EasyEDA: https://easyeda.com/wagiminator 8 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 9 | // =================================================================================== 10 | // 11 | // Description: 12 | // ------------ 13 | // Customize your USB Rotary Encoder Knob. 14 | // 15 | // References: 16 | // ----------- 17 | // - CNLohr ch32v003fun: https://github.com/cnlohr/ch32v003fun 18 | // - CNLohr rv003usb: https://github.com/cnlohr/rv003usb 19 | // - WCH Nanjing Qinheng Microelectronics: http://wch.cn 20 | // 21 | // Compilation Instructions: 22 | // ------------------------- 23 | // - Make sure GCC toolchain (gcc-riscv64-unknown-elf, newlib) and Python3 with rvprog 24 | // are installed. In addition, Linux requires access rights to WCH-LinkE programmer. 25 | // - Connect the WCH-LinkE programmer to the MCU board. 26 | // - Run 'make flash'. 27 | // 28 | // Operating Instructions: 29 | // ----------------------- 30 | // - Costomize your rotary encoder knob in the macros.h file. 31 | // - Connect the board via USB to your PC. It should be detected as a HID composite 32 | // device with keyboard, mouse and game controller interface. 33 | // - Turn the rotary encoder or press the encoder switch. 34 | 35 | 36 | // =================================================================================== 37 | // Libraries, Definitions and Macros 38 | // =================================================================================== 39 | #include // user configurations 40 | #include // system functions 41 | #include // GPIO functions 42 | #include // USB HID Composite 43 | #include // user defined macros 44 | 45 | // =================================================================================== 46 | // Main Function 47 | // =================================================================================== 48 | int main(void) { 49 | // Variables 50 | uint8_t isSwitchPressed = 0; // state of rotary encoder switch 51 | 52 | // Setup 53 | PIN_input_PU(PIN_ENC_A); // set encoder pins to input pullup 54 | PIN_input_PU(PIN_ENC_B); 55 | PIN_input_PU(PIN_ENC_SW); 56 | HID_init(); // init USB HID composite device 57 | 58 | // Loop 59 | while(1) { 60 | if(!PIN_read(PIN_ENC_A)) { // encoder turned ? 61 | if(PIN_read(PIN_ENC_B)) { // clockwise? 62 | ENC_CW_ACTION(); // -> take proper action 63 | DLY_ms(5); // -> debounce 64 | ENC_CW_RELEASED(); // -> quit action 65 | } 66 | else { // counter-clockwise? 67 | ENC_CCW_ACTION(); // -> take proper action 68 | DLY_ms(5); // -> debounce 69 | ENC_CCW_RELEASED(); // -> quit action 70 | } 71 | while(!PIN_read(PIN_ENC_A)); // wait until next detent 72 | } 73 | else { 74 | if(!isSwitchPressed && !PIN_read(PIN_ENC_SW)) { // switch previously pressed? 75 | ENC_SW_PRESSED(); // -> take proper action 76 | isSwitchPressed = 1; // -> update switch state 77 | } 78 | else if(isSwitchPressed && PIN_read(PIN_ENC_SW)) { // switch previously released? 79 | ENC_SW_RELEASED(); // -> take proper action 80 | isSwitchPressed = 0; // -> update switch state 81 | } 82 | } 83 | DLY_ms(1); // slow down a little 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /software/custom_knob/src/system.c: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Basic System Functions for CH32V003 * v1.6 * 3 | // =================================================================================== 4 | // 5 | // This file must be included!!!! 6 | // 7 | // References: 8 | // ----------- 9 | // - CNLohr ch32v003fun: https://github.com/cnlohr/ch32v003fun 10 | // - WCH Nanjing Qinheng Microelectronics: http://wch.cn 11 | // 12 | // 2023 by Stefan Wagner: https://github.com/wagiminator 13 | 14 | #include "system.h" 15 | 16 | // =================================================================================== 17 | // Setup Microcontroller (this function is called automatically at startup) 18 | // =================================================================================== 19 | void SYS_init(void) { 20 | // Init system clock 21 | #if SYS_CLK_INIT > 0 22 | #if F_CPU > 24000000 23 | FLASH->ACTLR = FLASH_ACTLR_LATENCY_1; // 1 cycle latency 24 | #endif 25 | CLK_init(); // init system clock 26 | #endif 27 | 28 | // Init SYSTICK 29 | #if SYS_TICK_INIT > 0 30 | STK_init(); 31 | #endif 32 | 33 | // Enable GPIO 34 | #if SYS_GPIO_EN > 0 35 | RCC->APB2PCENR |= RCC_IOPAEN | RCC_IOPCEN | RCC_IOPDEN; 36 | #endif 37 | } 38 | 39 | // =================================================================================== 40 | // System Clock Functions 41 | // =================================================================================== 42 | 43 | // Init internal oscillator (non PLL) as system clock source 44 | void CLK_init_HSI(void) { 45 | RCC->CFGR0 = CLK_DIV; // set clock divider 46 | } 47 | 48 | // Init internal oscillator with PLL as system clock source 49 | void CLK_init_HSI_PLL(void) { 50 | RCC->CTLR = RCC_HSION | RCC_PLLON | ((HSITRIM) << 3); // enable PLL, keep HSI on 51 | while(!(RCC->CTLR & RCC_PLLRDY)); // wait till PLL is ready 52 | RCC->CFGR0 = CLK_DIV | RCC_SW_PLL; // select PLL as system clock source 53 | while((RCC->CFGR0 & RCC_SWS) != RCC_SWS_PLL); // wait till PLL is used as system clock source 54 | } 55 | 56 | // Init external crystal (non PLL) as system clock source 57 | void CLK_init_HSE(void) { 58 | RCC->APB2PCENR |= RCC_AFIOEN; // enable auxiliary clock module 59 | AFIO->PCFR1 |= AFIO_PCFR1_PA12_REMAP; // pins PA1-PA2 for external crystal 60 | RCC->CTLR = RCC_HSION | RCC_HSEON | ((HSITRIM) << 3); // enable HSE and keep HSI on 61 | while(!(RCC->CTLR & RCC_HSERDY)); // wait till HSE is ready 62 | RCC->CFGR0 = CLK_DIV | RCC_SW_HSE; // set clock divider, use HSE for system clock 63 | while((RCC->CFGR0 & RCC_SWS) != RCC_SWS_HSE); // wait till HSE is used as system clock source 64 | } 65 | 66 | // Init external crystal (PLL) as system clock source 67 | void CLK_init_HSE_PLL(void) { 68 | RCC->APB2PCENR |= RCC_AFIOEN; // enable auxiliary clock module 69 | AFIO->PCFR1 |= AFIO_PCFR1_PA12_REMAP; // pins PA1-PA2 for external crystal 70 | RCC->CTLR = RCC_HSION | RCC_HSEON | ((HSITRIM) << 3); // enable HSE and keep HSI on 71 | while(!(RCC->CTLR & RCC_HSERDY)); // wait till HSE is ready 72 | RCC->CFGR0 = RCC_PLLSRC | CLK_DIV; // set clock divider, use HSE as PLL source 73 | RCC->CTLR = RCC_PLLON | RCC_HSION | RCC_HSEON | ((HSITRIM) << 3); // enable PLL 74 | while(!(RCC->CTLR & RCC_PLLRDY)); // wait till PLL is ready 75 | RCC->CFGR0 = RCC_PLLSRC | CLK_DIV | RCC_SW_PLL; // select PLL as system clock source 76 | while((RCC->CFGR0 & RCC_SWS) != RCC_SWS_PLL); // wait till PLL is used as system clock source 77 | } 78 | 79 | // Reset system clock to default state 80 | void CLK_reset(void) { 81 | RCC->CTLR |= RCC_HSION; // enable HSI 82 | while(!(RCC->CTLR & RCC_HSIRDY)); // wait until HSI is ready 83 | RCC->CFGR0 = 0x00000000; // select HSI as system clock source 84 | while(RCC->CFGR0 & RCC_SWS); // wait until HSI is selected 85 | RCC->CTLR = RCC_HSION | ((HSITRIM) << 3); // use HSI only 86 | RCC->INTR = 0x009F0000; // disable interrupts and clear flags 87 | FLASH->ACTLR = FLASH_ACTLR_LATENCY_0; // no flash wait states 88 | } 89 | 90 | // Setup pin PC4 for MCO (output, push-pull, 50MHz, auxiliary) 91 | void MCO_init(void) { 92 | RCC->APB2PCENR |= RCC_AFIOEN | RCC_IOPCEN; 93 | GPIOC->CFGLR = (GPIOC->CFGLR & ~((uint32_t)0b1111<<(4<<2))) | ((uint32_t)0b1011<<(4<<2)); 94 | } 95 | 96 | // =================================================================================== 97 | // Delay Functions 98 | // =================================================================================== 99 | 100 | // Wait n counts of SysTick 101 | void DLY_ticks(uint32_t n) { 102 | uint32_t end = STK->CNT + n; 103 | while(((int32_t)(STK->CNT - end)) < 0); 104 | } 105 | 106 | // =================================================================================== 107 | // Bootloader (BOOT) Functions 108 | // =================================================================================== 109 | 110 | // Perform software reset and jump to bootloader 111 | void BOOT_now(void) { 112 | FLASH->KEYR = 0x45670123; 113 | FLASH->KEYR = 0xCDEF89AB; 114 | FLASH->BOOT_MODEKEYR = 0x45670123; 115 | FLASH->BOOT_MODEKEYR = 0xCDEF89AB; // unlock flash 116 | FLASH->STATR |= (uint16_t)1<<14; // start bootloader after software reset 117 | FLASH->CTLR |= FLASH_CTLR_LOCK; // lock flash 118 | RCC->RSTSCKR |= RCC_RMVF; // clear reset flags 119 | PFIC->CFGR = PFIC_RESETSYS | PFIC_KEY3; // perform software reset 120 | } 121 | 122 | // =================================================================================== 123 | // Independent Watchdog Timer (IWDG) Functions 124 | // =================================================================================== 125 | 126 | // Start independent watchdog timer (IWDG) with given time in milliseconds (max 8191). 127 | // Once the IWDG has been started, it cannot be disabled, only reloaded (feed). 128 | // It can be stopped by disabling the internal low-speed clock (LSI). 129 | void IWDG_start(uint16_t ms) { 130 | LSI_enable(); // enable internal low-speed clock (LSI) 131 | IWDG->CTLR = 0x5555; // allow register modification 132 | while(IWDG->STATR & IWDG_PVU); // wait for clock register to be ready 133 | IWDG->PSCR = 0b111; // set LSI clock prescaler 256 134 | while(IWDG->STATR & IWDG_RVU); // wait for reload register to be ready 135 | IWDG->RLDR = ms >> 1; // set watchdog counter reload value 136 | IWDG->CTLR = 0xAAAA; // load reload value into watchdog counter 137 | IWDG->CTLR = 0xCCCC; // enable IWDG 138 | } 139 | 140 | // Reload watchdog counter with n milliseconds, n<=8191 141 | void IWDG_reload(uint16_t ms) { 142 | IWDG->CTLR = 0x5555; // allow register modification 143 | while(IWDG->STATR & IWDG_RVU); // wait for reload register to be ready 144 | IWDG->RLDR = ms >> 1; // set watchdog counter reload value 145 | IWDG->CTLR = 0xAAAA; // load reload value into watchdog counter 146 | } 147 | 148 | // =================================================================================== 149 | // Automatic Wake-up Timer (AWU) Functions 150 | // =================================================================================== 151 | 152 | // Init automatic wake-up timer 153 | void AWU_init(void) { 154 | LSI_enable(); // enable internal low-speed clock (LSI) 155 | EXTI->EVENR |= ((uint32_t)1<<9); // enable AWU event 156 | EXTI->RTENR |= ((uint32_t)1<<9); // enable AWU rising edge triggering 157 | RCC->APB1PCENR |= RCC_PWREN; // enable power module 158 | PWR->AWUCSR = PWR_AWUCSR_AWUEN; // enable automatic wake-up timer 159 | } 160 | 161 | // Stop automatic wake-up timer 162 | void AWU_stop(void) { 163 | PWR->AWUCSR = 0x00; // disable automatic wake-up timer 164 | EXTI->EVENR &= ~((uint32_t)1<<9); // disable AWU event 165 | EXTI->RTENR &= ~((uint32_t)1<<9); // disable AWU rising edge triggering 166 | } 167 | 168 | // =================================================================================== 169 | // Sleep Functions 170 | // =================================================================================== 171 | 172 | // Put device into sleep, wake up by interrupt 173 | void SLEEP_WFI_now(void) { 174 | PFIC->SCTLR &= ~PFIC_SLEEPDEEP; // set power-down mode to sleep 175 | __WFI(); // wait for interrupt 176 | } 177 | 178 | // Put device into sleep, wake up by event 179 | void SLEEP_WFE_now(void) { 180 | PFIC->SCTLR &= ~PFIC_SLEEPDEEP; // set power-down mode to sleep 181 | __WFE(); // wait for event 182 | } 183 | 184 | // Put device into standby (deep sleep), wake up interrupt 185 | void STDBY_WFI_now(void) { 186 | RCC->APB1PCENR |= RCC_PWREN; // enable power module 187 | PWR->CTLR |= PWR_CTLR_PDDS; // set power-down mode to standby (deep sleep) 188 | PFIC->SCTLR |= PFIC_SLEEPDEEP; 189 | __WFI(); // wait for interrupt 190 | PWR->CTLR &= ~PWR_CTLR_PDDS; // disable PDDS again 191 | } 192 | 193 | // Put device into standby (deep sleep), wake up event 194 | void STDBY_WFE_now(void) { 195 | RCC->APB1PCENR |= RCC_PWREN; // enable power module 196 | PWR->CTLR |= PWR_CTLR_PDDS; // set power-down mode to standby (deep sleep) 197 | PFIC->SCTLR |= PFIC_SLEEPDEEP; 198 | __WFE(); // wait for event 199 | PWR->CTLR &= ~PWR_CTLR_PDDS; // disable PDDS again 200 | } 201 | 202 | // =================================================================================== 203 | // C++ Support 204 | // Based on CNLohr ch32v003fun: https://github.com/cnlohr/ch32v003fun 205 | // =================================================================================== 206 | #ifdef __cplusplus 207 | extern void __cxa_pure_virtual() { while (1); } 208 | extern void (*__preinit_array_start[]) (void) __attribute__((weak)); 209 | extern void (*__preinit_array_end[]) (void) __attribute__((weak)); 210 | extern void (*__init_array_start[]) (void) __attribute__((weak)); 211 | extern void (*__init_array_end[]) (void) __attribute__((weak)); 212 | 213 | void __libc_init_array(void) { 214 | uint32_t count, i; 215 | count = __preinit_array_end - __preinit_array_start; 216 | for(i = 0; i < count; i++) __preinit_array_start[i](); 217 | count = __init_array_end - __init_array_start; 218 | for(i = 0; i < count; i++) __init_array_start[i](); 219 | } 220 | #endif 221 | 222 | // =================================================================================== 223 | // C version of CH32V003 Startup .s file from WCH 224 | // Based on CNLohr ch32v003fun: https://github.com/cnlohr/ch32v003fun 225 | // =================================================================================== 226 | extern uint32_t _sbss; 227 | extern uint32_t _ebss; 228 | extern uint32_t _data_lma; 229 | extern uint32_t _data_vma; 230 | extern uint32_t _edata; 231 | 232 | // Prototypes 233 | int main(void) __attribute__((section(".text.main"), used)); 234 | void jump_reset(void) __attribute__((section(".init.jump"), naked, used)); 235 | void reset_handler(void) __attribute__((section(".text.reset_handler"), naked, used)); 236 | 237 | // FLASH starts with a jump to the reset handler 238 | void jump_reset(void) { asm volatile("j reset_handler"); } 239 | 240 | #if SYS_USE_VECTORS > 0 241 | // Unless a specific handler is overridden, it just spins forever 242 | void default_handler(void) __attribute__((section(".text.vector_handler"), naked, used)); 243 | void default_handler(void) { while(1); } 244 | 245 | // All interrupt handlers are aliased to default_handler unless overridden individually 246 | #define DUMMY_HANDLER __attribute__((section(".text.vector_handler"), weak, alias("default_handler"), used)) 247 | DUMMY_HANDLER void NMI_Handler(void); 248 | DUMMY_HANDLER void HardFault_Handler(void); 249 | DUMMY_HANDLER void SysTick_Handler(void); 250 | DUMMY_HANDLER void SW_Handler(void); 251 | DUMMY_HANDLER void WWDG_IRQHandler(void); 252 | DUMMY_HANDLER void PVD_IRQHandler(void); 253 | DUMMY_HANDLER void FLASH_IRQHandler(void); 254 | DUMMY_HANDLER void RCC_IRQHandler(void); 255 | DUMMY_HANDLER void EXTI7_0_IRQHandler(void); 256 | DUMMY_HANDLER void AWU_IRQHandler(void); 257 | DUMMY_HANDLER void DMA1_Channel1_IRQHandler(void); 258 | DUMMY_HANDLER void DMA1_Channel2_IRQHandler(void); 259 | DUMMY_HANDLER void DMA1_Channel3_IRQHandler(void); 260 | DUMMY_HANDLER void DMA1_Channel4_IRQHandler(void); 261 | DUMMY_HANDLER void DMA1_Channel5_IRQHandler(void); 262 | DUMMY_HANDLER void DMA1_Channel6_IRQHandler(void); 263 | DUMMY_HANDLER void DMA1_Channel7_IRQHandler(void); 264 | DUMMY_HANDLER void ADC1_IRQHandler(void); 265 | DUMMY_HANDLER void I2C1_EV_IRQHandler(void); 266 | DUMMY_HANDLER void I2C1_ER_IRQHandler(void); 267 | DUMMY_HANDLER void USART1_IRQHandler(void); 268 | DUMMY_HANDLER void SPI1_IRQHandler(void); 269 | DUMMY_HANDLER void TIM1_BRK_IRQHandler(void); 270 | DUMMY_HANDLER void TIM1_UP_IRQHandler(void); 271 | DUMMY_HANDLER void TIM1_TRG_COM_IRQHandler(void); 272 | DUMMY_HANDLER void TIM1_CC_IRQHandler(void); 273 | DUMMY_HANDLER void TIM2_IRQHandler(void); 274 | 275 | // Interrupt vector table 276 | void (*const vectors[])(void) __attribute__((section(".init.vectors"), used)); 277 | void (*const vectors[])(void) = { 278 | // RISC-V handlers 279 | 0, // 1 - Reserved 280 | NMI_Handler, // 2 - NMI Handler 281 | HardFault_Handler, // 3 - Hard Fault Handler 282 | 0, // 4 - Reserved 283 | 0, // 5 - Reserved 284 | 0, // 6 - Reserved 285 | 0, // 7 - Reserved 286 | 0, // 8 - Reserved 287 | 0, // 9 - Reserved 288 | 0, // 10 - Reserved 289 | 0, // 11 - Reserved 290 | SysTick_Handler, // 12 - SysTick Handler 291 | 0, // 13 - Reserved 292 | SW_Handler, // 14 - SW Handler 293 | 0, // 15 - Reserved 294 | 295 | // Peripheral handlers 296 | WWDG_IRQHandler, // 16 - Window Watchdog 297 | PVD_IRQHandler, // 17 - PVD through EXTI Line detect 298 | FLASH_IRQHandler, // 18 - Flash 299 | RCC_IRQHandler, // 19 - RCC 300 | EXTI7_0_IRQHandler, // 20 - EXTI Line 7..0 301 | AWU_IRQHandler, // 21 - AWU 302 | DMA1_Channel1_IRQHandler, // 22 - DMA1 Channel 1 303 | DMA1_Channel2_IRQHandler, // 23 - DMA1 Channel 2 304 | DMA1_Channel3_IRQHandler, // 24 - DMA1 Channel 3 305 | DMA1_Channel4_IRQHandler, // 25 - DMA1 Channel 4 306 | DMA1_Channel5_IRQHandler, // 26 - DMA1 Channel 5 307 | DMA1_Channel6_IRQHandler, // 27 - DMA1 Channel 6 308 | DMA1_Channel7_IRQHandler, // 28 - DMA1 Channel 7 309 | ADC1_IRQHandler, // 29 - ADC1 310 | I2C1_EV_IRQHandler, // 30 - I2C1 Event 311 | I2C1_ER_IRQHandler, // 31 - I2C1 Error 312 | USART1_IRQHandler, // 32 - USART1 313 | SPI1_IRQHandler, // 33 - SPI1 314 | TIM1_BRK_IRQHandler, // 34 - TIM1 Break 315 | TIM1_UP_IRQHandler, // 35 - TIM1 Update 316 | TIM1_TRG_COM_IRQHandler, // 36 - TIM1 Trigger and Commutation 317 | TIM1_CC_IRQHandler, // 37 - TIM1 Capture Compare 318 | TIM2_IRQHandler, // 38 - TIM2 319 | }; 320 | #endif // SYS_USE_VECTORS > 0 321 | 322 | // Reset handler 323 | void reset_handler(void) { 324 | uint32_t *src, *dst; 325 | 326 | // Set pointers, vectors, processor status, and interrupts 327 | asm volatile( 328 | " .option push \n\ 329 | .option norelax \n\ 330 | la gp, __global_pointer$ \n\ 331 | .option pop \n\ 332 | la sp, _eusrstack \n" 333 | #if __GNUC__ > 10 334 | ".option arch, +zicsr \n" 335 | #endif 336 | " li a0, 0x88 \n\ 337 | csrw mstatus, a0 \n\ 338 | li a1, 0x3 \n\ 339 | csrw 0x804, a1 \n\ 340 | la a0, jump_reset \n\ 341 | or a0, a0, a1 \n\ 342 | csrw mtvec, a0 \n\ 343 | csrw mepc, %[main] \n" 344 | : : [main] "r" (main) : "a0", "a1" , "memory" 345 | ); 346 | 347 | // Copy data from FLASH to RAM 348 | src = &_data_lma; 349 | dst = &_data_vma; 350 | while(dst < &_edata) *dst++ = *src++; 351 | 352 | // Clear uninitialized variables 353 | #if SYS_CLEAR_BSS > 0 354 | dst = &_sbss; 355 | while(dst < &_ebss) *dst++ = 0; 356 | #endif 357 | 358 | // C++ Support 359 | #ifdef __cplusplus 360 | __libc_init_array(); 361 | #endif 362 | 363 | // Init system 364 | SYS_init(); 365 | 366 | // Return 367 | asm volatile("mret"); 368 | } 369 | -------------------------------------------------------------------------------- /software/custom_knob/src/usb.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // USB Constant and Structure Defines 3 | // =================================================================================== 4 | 5 | #pragma once 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #include 12 | 13 | // USB PID 14 | #ifndef USB_PID_SETUP 15 | #define USB_PID_NULL 0x00 // reserved PID 16 | #define USB_PID_SOF 0x05 17 | #define USB_PID_SETUP 0x0D 18 | #define USB_PID_IN 0x09 19 | #define USB_PID_OUT 0x01 20 | #define USB_PID_ACK 0x02 21 | #define USB_PID_NAK 0x0A 22 | #define USB_PID_STALL 0x0E 23 | #define USB_PID_DATA0 0x03 24 | #define USB_PID_DATA1 0x0B 25 | #define USB_PID_PRE 0x0C 26 | #endif 27 | 28 | // USB standard device request code 29 | #ifndef USB_GET_DESCRIPTOR 30 | #define USB_GET_STATUS 0x00 31 | #define USB_CLEAR_FEATURE 0x01 32 | #define USB_SET_FEATURE 0x03 33 | #define USB_SET_ADDRESS 0x05 34 | #define USB_GET_DESCRIPTOR 0x06 35 | #define USB_SET_DESCRIPTOR 0x07 36 | #define USB_GET_CONFIGURATION 0x08 37 | #define USB_SET_CONFIGURATION 0x09 38 | #define USB_GET_INTERFACE 0x0A 39 | #define USB_SET_INTERFACE 0x0B 40 | #define USB_SYNCH_FRAME 0x0C 41 | #endif 42 | 43 | // USB hub class request code 44 | #ifndef HUB_GET_DESCRIPTOR 45 | #define HUB_GET_STATUS 0x00 46 | #define HUB_CLEAR_FEATURE 0x01 47 | #define HUB_GET_STATE 0x02 48 | #define HUB_SET_FEATURE 0x03 49 | #define HUB_GET_DESCRIPTOR 0x06 50 | #define HUB_SET_DESCRIPTOR 0x07 51 | #endif 52 | 53 | // USB HID class request code 54 | #ifndef HID_GET_REPORT 55 | #define HID_GET_REPORT 0x01 56 | #define HID_GET_IDLE 0x02 57 | #define HID_GET_PROTOCOL 0x03 58 | #define HID_SET_REPORT 0x09 59 | #define HID_SET_IDLE 0x0A 60 | #define HID_SET_PROTOCOL 0x0B 61 | #endif 62 | 63 | // Bit define for USB request type 64 | #ifndef USB_REQ_TYP_MASK 65 | #define USB_REQ_TYP_IN 0x80 // control IN, device to host 66 | #define USB_REQ_TYP_OUT 0x00 // control OUT, host to device 67 | #define USB_REQ_TYP_READ 0x80 // control read, device to host 68 | #define USB_REQ_TYP_WRITE 0x00 // control write, host to device 69 | #define USB_REQ_TYP_MASK 0x60 // bit mask of request type 70 | #define USB_REQ_TYP_STANDARD 0x00 71 | #define USB_REQ_TYP_CLASS 0x20 72 | #define USB_REQ_TYP_VENDOR 0x40 73 | #define USB_REQ_TYP_RESERVED 0x60 74 | #define USB_REQ_RECIP_MASK 0x1F // bit mask of request recipient 75 | #define USB_REQ_RECIP_DEVICE 0x00 76 | #define USB_REQ_RECIP_INTERF 0x01 77 | #define USB_REQ_RECIP_ENDP 0x02 78 | #define USB_REQ_RECIP_OTHER 0x03 79 | #endif 80 | 81 | // USB request type for hub class request 82 | #ifndef HUB_GET_HUB_DESCRIPTOR 83 | #define HUB_CLEAR_HUB_FEATURE 0x20 84 | #define HUB_CLEAR_PORT_FEATURE 0x23 85 | #define HUB_GET_BUS_STATE 0xA3 86 | #define HUB_GET_HUB_DESCRIPTOR 0xA0 87 | #define HUB_GET_HUB_STATUS 0xA0 88 | #define HUB_GET_PORT_STATUS 0xA3 89 | #define HUB_SET_HUB_DESCRIPTOR 0x20 90 | #define HUB_SET_HUB_FEATURE 0x20 91 | #define HUB_SET_PORT_FEATURE 0x23 92 | #endif 93 | 94 | // Hub class feature selectors 95 | #ifndef HUB_PORT_RESET 96 | #define HUB_C_HUB_LOCAL_POWER 0 97 | #define HUB_C_HUB_OVER_CURRENT 1 98 | #define HUB_PORT_CONNECTION 0 99 | #define HUB_PORT_ENABLE 1 100 | #define HUB_PORT_SUSPEND 2 101 | #define HUB_PORT_OVER_CURRENT 3 102 | #define HUB_PORT_RESET 4 103 | #define HUB_PORT_POWER 8 104 | #define HUB_PORT_LOW_SPEED 9 105 | #define HUB_C_PORT_CONNECTION 16 106 | #define HUB_C_PORT_ENABLE 17 107 | #define HUB_C_PORT_SUSPEND 18 108 | #define HUB_C_PORT_OVER_CURRENT 19 109 | #define HUB_C_PORT_RESET 20 110 | #endif 111 | 112 | // USB descriptor type 113 | #ifndef USB_DESCR_TYP_DEVICE 114 | #define USB_DESCR_TYP_DEVICE 0x01 115 | #define USB_DESCR_TYP_CONFIG 0x02 116 | #define USB_DESCR_TYP_STRING 0x03 117 | #define USB_DESCR_TYP_INTERF 0x04 118 | #define USB_DESCR_TYP_ENDP 0x05 119 | #define USB_DESCR_TYP_QUALIF 0x06 120 | #define USB_DESCR_TYP_SPEED 0x07 121 | #define USB_DESCR_TYP_OTG 0x09 122 | #define USB_DESCR_TYP_IAD 0x0B 123 | #define USB_DESCR_TYP_HID 0x21 124 | #define USB_DESCR_TYP_REPORT 0x22 125 | #define USB_DESCR_TYP_PHYSIC 0x23 126 | #define USB_DESCR_TYP_CS_INTF 0x24 127 | #define USB_DESCR_TYP_CS_ENDP 0x25 128 | #define USB_DESCR_TYP_HUB 0x29 129 | #endif 130 | 131 | // USB device class 132 | #ifndef USB_DEV_CLASS_HUB 133 | #define USB_DEV_CLASS_RESERVED 0x00 134 | #define USB_DEV_CLASS_AUDIO 0x01 135 | #define USB_DEV_CLASS_COMM 0x02 136 | #define USB_DEV_CLASS_HID 0x03 137 | #define USB_DEV_CLASS_MONITOR 0x04 138 | #define USB_DEV_CLASS_PHYSIC_IF 0x05 139 | #define USB_DEV_CLASS_POWER 0x06 140 | #define USB_DEV_CLASS_PRINTER 0x07 141 | #define USB_DEV_CLASS_STORAGE 0x08 142 | #define USB_DEV_CLASS_HUB 0x09 143 | #define USB_DEV_CLASS_DATA 0x0A 144 | #define USB_DEV_CLASS_MISC 0xEF 145 | #define USB_DEV_CLASS_VENDOR 0xFF 146 | #endif 147 | 148 | // USB endpoint type and attributes 149 | #ifndef USB_ENDP_TYPE_MASK 150 | #define USB_ENDP_DIR_MASK 0x80 151 | #define USB_ENDP_ADDR_MASK 0x0F 152 | #define USB_ENDP_TYPE_MASK 0x03 153 | #define USB_ENDP_TYPE_CTRL 0x00 154 | #define USB_ENDP_TYPE_ISOCH 0x01 155 | #define USB_ENDP_TYPE_BULK 0x02 156 | #define USB_ENDP_TYPE_INTER 0x03 157 | #define USB_ENDP_ADDR_EP1_OUT 0x01 158 | #define USB_ENDP_ADDR_EP1_IN 0x81 159 | #define USB_ENDP_ADDR_EP2_OUT 0x02 160 | #define USB_ENDP_ADDR_EP2_IN 0x82 161 | #define USB_ENDP_ADDR_EP3_OUT 0x03 162 | #define USB_ENDP_ADDR_EP3_IN 0x83 163 | #define USB_ENDP_ADDR_EP4_OUT 0x04 164 | #define USB_ENDP_ADDR_EP4_IN 0x84 165 | #endif 166 | 167 | #ifndef MAX_PACKET_SIZE 168 | #define MAX_PACKET_SIZE 64 // maximum packet size 169 | #endif 170 | 171 | // USB descriptor type defines 172 | typedef struct __attribute__((packed)) { 173 | uint8_t bRequestType; 174 | uint8_t bRequest; 175 | uint8_t wValueL; 176 | uint8_t wValueH; 177 | uint8_t wIndexL; 178 | uint8_t wIndexH; 179 | uint8_t wLengthL; 180 | uint8_t wLengthH; 181 | } USB_SETUP_REQ, *PUSB_SETUP_REQ; 182 | 183 | typedef struct __attribute__((packed)) { 184 | uint8_t bLength; 185 | uint8_t bDescriptorType; 186 | uint16_t bcdUSB; 187 | uint8_t bDeviceClass; 188 | uint8_t bDeviceSubClass; 189 | uint8_t bDeviceProtocol; 190 | uint8_t bMaxPacketSize0; 191 | uint16_t idVendor; 192 | uint16_t idProduct; 193 | uint16_t bcdDevice; 194 | uint8_t iManufacturer; 195 | uint8_t iProduct; 196 | uint8_t iSerialNumber; 197 | uint8_t bNumConfigurations; 198 | } USB_DEV_DESCR, *PUSB_DEV_DESCR; 199 | 200 | typedef struct __attribute__((packed)) { 201 | uint8_t bLength; 202 | uint8_t bDescriptorType; 203 | uint16_t wTotalLength; 204 | uint8_t bNumInterfaces; 205 | uint8_t bConfigurationValue; 206 | uint8_t iConfiguration; 207 | uint8_t bmAttributes; 208 | uint8_t MaxPower; 209 | } USB_CFG_DESCR, *PUSB_CFG_DESCR; 210 | 211 | typedef struct __attribute__((packed)) { 212 | uint8_t bLength; 213 | uint8_t bDescriptorType; 214 | uint8_t bInterfaceNumber; 215 | uint8_t bAlternateSetting; 216 | uint8_t bNumEndpoints; 217 | uint8_t bInterfaceClass; 218 | uint8_t bInterfaceSubClass; 219 | uint8_t bInterfaceProtocol; 220 | uint8_t iInterface; 221 | } USB_ITF_DESCR, *PUSB_ITF_DESCR; 222 | 223 | typedef struct __attribute__((packed)) { 224 | uint8_t bLength; 225 | uint8_t bDescriptorType; 226 | uint8_t bFirstInterface; 227 | uint8_t bInterfaceCount; 228 | uint8_t bFunctionClass; 229 | uint8_t bFunctionSubClass; 230 | uint8_t bFunctionProtocol; 231 | uint8_t iFunction; 232 | } USB_IAD_DESCR, *PUSB_IAD_DESCR; 233 | 234 | typedef struct __attribute__((packed)) { 235 | uint8_t bLength; 236 | uint8_t bDescriptorType; 237 | uint8_t bEndpointAddress; 238 | uint8_t bmAttributes; 239 | uint16_t wMaxPacketSize; 240 | uint8_t bInterval; 241 | } USB_ENDP_DESCR; 242 | 243 | typedef struct __attribute__((packed)) { 244 | USB_CFG_DESCR cfg_descr; 245 | USB_ITF_DESCR itf_descr; 246 | USB_ENDP_DESCR endp_descr[1]; 247 | } USB_CFG_DESCR_LONG, *PUSB_CFG_DESCR_LONG; 248 | 249 | typedef struct __attribute__((packed)) { 250 | uint8_t bLength; 251 | uint8_t bDescriptorType; 252 | uint16_t bString[]; 253 | } USB_STR_DESCR, *PUSB_STR_DESCR; 254 | 255 | typedef struct __attribute__((packed)) { 256 | uint8_t bDescLength; 257 | uint8_t bDescriptorType; 258 | uint8_t bNbrPorts; 259 | uint16_t wHubCharacteristics; 260 | uint8_t bPwrOn2PwrGood; 261 | uint8_t bHubContrCurrent; 262 | uint8_t DeviceRemovable; 263 | uint8_t PortPwrCtrlMask; 264 | } USB_HUB_DESCR, *PUSB_HUB_DESCR; 265 | 266 | typedef struct __attribute__((packed)) { 267 | uint8_t bLength; 268 | uint8_t bDescriptorType; 269 | uint16_t bcdHID; 270 | uint8_t bCountryCode; 271 | uint8_t bNumDescriptors; 272 | uint8_t bDescriptorTypeX; 273 | uint16_t wDescriptorLength; 274 | } USB_HID_DESCR, *PUSB_HID_DESCR; 275 | 276 | typedef struct __attribute__((packed)) { 277 | uint8_t mCBW_Sig0; 278 | uint8_t mCBW_Sig1; 279 | uint8_t mCBW_Sig2; 280 | uint8_t mCBW_Sig3; 281 | uint8_t mCBW_Tag0; 282 | uint8_t mCBW_Tag1; 283 | uint8_t mCBW_Tag2; 284 | uint8_t mCBW_Tag3; 285 | uint8_t mCBW_DataLen0; 286 | uint8_t mCBW_DataLen1; 287 | uint8_t mCBW_DataLen2; 288 | uint8_t mCBW_DataLen3; 289 | uint8_t mCBW_Flag; 290 | uint8_t mCBW_LUN; 291 | uint8_t mCBW_CB_Len; 292 | uint8_t mCBW_CB_Buf[16]; 293 | } UDISK_BOC_CBW, *PUDISK_BOC_CBW; 294 | 295 | typedef struct __attribute__((packed)) { 296 | uint8_t mCSW_Sig0; 297 | uint8_t mCSW_Sig1; 298 | uint8_t mCSW_Sig2; 299 | uint8_t mCSW_Sig3; 300 | uint8_t mCSW_Tag0; 301 | uint8_t mCSW_Tag1; 302 | uint8_t mCSW_Tag2; 303 | uint8_t mCSW_Tag3; 304 | uint8_t mCSW_Residue0; 305 | uint8_t mCSW_Residue1; 306 | uint8_t mCSW_Residue2; 307 | uint8_t mCSW_Residue3; 308 | uint8_t mCSW_Status; 309 | } UDISK_BOC_CSW, *PUDISK_BOC_CSW; 310 | 311 | #ifdef __cplusplus 312 | } 313 | #endif 314 | -------------------------------------------------------------------------------- /software/custom_knob/src/usb_composite.c: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // USB HID Composite Device Functions for CH32V003 * v1.0 * 3 | // =================================================================================== 4 | 5 | #include "usb_composite.h" 6 | 7 | // =================================================================================== 8 | // HID reports 9 | // =================================================================================== 10 | volatile uint8_t KBD_report[] = {1,0,0,0,0,0,0,0}; 11 | volatile uint8_t CON_report[] = {2,0,0}; 12 | volatile uint8_t MOUSE_report[] = {3,0,0,0,0}; 13 | volatile uint8_t KBD_state; 14 | 15 | // =================================================================================== 16 | // ASCII to keycode mapping table 17 | // =================================================================================== 18 | const uint8_t KBD_map[128] = { 19 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x2b, 0x28, 0x00, 0x00, 0x00, 20 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 21 | 0x00, 0x00, 0x00, 0x00, 0x2c, 0x9e, 0xb4, 0xa0, 0xa1, 0xa2, 0xa4, 0x34, 0xa6, 0xa7, 22 | 0xa5, 0xae, 0x36, 0x2d, 0x37, 0x38, 0x27, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 23 | 0x25, 0x26, 0xb3, 0x33, 0xb6, 0x2e, 0xb7, 0xb8, 0x9f, 0x84, 0x85, 0x86, 0x87, 0x88, 24 | 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 25 | 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x2f, 0x31, 0x30, 0xa3, 0xad, 0x35, 0x04, 26 | 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 27 | 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0xaf, 0xb1, 0xb0, 28 | 0xb5, 0x00 29 | }; 30 | 31 | // =================================================================================== 32 | // Standard Keyboard Functions 33 | // =================================================================================== 34 | 35 | // Press a key on keyboard 36 | void KBD_press(uint8_t key) { 37 | uint8_t i; 38 | 39 | // Convert key for HID report 40 | if(key >= 136) key -= 136; // non-printing key/not a modifier? 41 | else if(key >= 128) { // modifier key? 42 | KBD_report[1] |= (1<<(key-128)); // add modifier to report 43 | key = 0; 44 | } 45 | else { // printing key? 46 | key = KBD_map[key]; // convert ascii to keycode for report 47 | if(!key) return; // no valid key 48 | if(key & 0x80) { // capital letter/shift character? 49 | KBD_report[1] |= 0x02; // add left shift modifier 50 | key &= 0x7F; // remove shift from key itself 51 | } 52 | } 53 | 54 | // Check if key is already present in report 55 | for(i=3; i<8; i++) { 56 | if(KBD_report[i] == key) return; // return if already in report 57 | } 58 | 59 | // Find an empty slot, insert key and transmit report 60 | for(i=3; i<8; i++) { 61 | if(KBD_report[i] == 0) { // empty slot? 62 | KBD_report[i] = key; // insert key 63 | return; // and return 64 | } 65 | } 66 | } 67 | 68 | // Release a key on keyboard 69 | void KBD_release(uint8_t key) { 70 | uint8_t i; 71 | 72 | // Convert key for HID report 73 | if(key >= 136) key -= 136; // non-printing key/not a modifier? 74 | else if(key >= 128) { // modifier key? 75 | KBD_report[1] &= ~(1<<(key-128)); // delete modifier in report 76 | key = 0; 77 | } 78 | else { // printing key? 79 | key = KBD_map[key]; // convert ascii to keycode for report 80 | if(!key) return; // no valid key 81 | if(key & 0x80) { // capital letter/shift character? 82 | KBD_report[1] &= ~0x02; // remove shift modifier 83 | key &= 0x7F; // remove shift from key itself 84 | } 85 | } 86 | 87 | // Delete key in report 88 | for(i=3; i<8; i++) { 89 | if(KBD_report[i] == key) KBD_report[i] = 0; // delete key in report 90 | } 91 | } 92 | 93 | // Press and release a key on keyboard 94 | void KBD_type(uint8_t key) { 95 | KBD_press(key); 96 | DLY_ms(6); 97 | KBD_release(key); 98 | DLY_ms(6); 99 | } 100 | 101 | // Release all keys on keyboard 102 | void KBD_releaseAll(void) { 103 | uint8_t i; 104 | for(i=7; i; i--) KBD_report[i] = 0; // delete all keys in report 105 | } 106 | 107 | // Write text with keyboard 108 | void KBD_print(char* str) { 109 | while(*str) KBD_type(*str++); 110 | } 111 | 112 | // =================================================================================== 113 | // Consumer Multimedia Keyboard Functions 114 | // =================================================================================== 115 | 116 | // Press a consumer key on keyboard 117 | void CON_press(uint8_t key) { 118 | CON_report[1] = key; 119 | } 120 | 121 | // Release a consumer key on keyboard 122 | void CON_release(void) { 123 | CON_report[1] = 0; 124 | } 125 | 126 | // Press and release a consumer key on keyboard 127 | void CON_type(uint8_t key) { 128 | CON_press(key); 129 | DLY_ms(6); 130 | CON_release(); 131 | DLY_ms(6); 132 | } 133 | 134 | // =================================================================================== 135 | // Mouse Functions 136 | // =================================================================================== 137 | 138 | // Press mouse button(s) 139 | void MOUSE_press(uint8_t buttons) { 140 | MOUSE_report[1] |= buttons; // press button(s) 141 | } 142 | 143 | // Release mouse button(s) 144 | void MOUSE_release(uint8_t buttons) { 145 | MOUSE_report[1] &= ~buttons; // release button(s) 146 | } 147 | 148 | // Move mouse pointer 149 | void MOUSE_move(int8_t xrel, int8_t yrel) { 150 | INT_ATOMIC_BLOCK { 151 | MOUSE_report[2] += (uint8_t)xrel; // set relative x-movement 152 | MOUSE_report[3] += (uint8_t)yrel; // set relative y-movement 153 | } 154 | } 155 | 156 | // Move mouse wheel 157 | void MOUSE_wheel(int8_t rel) { 158 | INT_ATOMIC_BLOCK { 159 | MOUSE_report[4] += (uint8_t)rel; // set relative wheel movement 160 | } 161 | } 162 | 163 | // =================================================================================== 164 | // RV003USB Software USB User Handle Functions 165 | // =================================================================================== 166 | void usb_handle_user_in_request(struct usb_endpoint * e, uint8_t * scratchpad, int endp, uint32_t sendtok, struct rv003usb_internal * ist) { 167 | 168 | // Current device (alternating) 169 | static uint8_t dev = 3; 170 | 171 | // Mouse 172 | if(endp == 1) { 173 | if(!(--dev)) dev = 3; 174 | switch(dev) { 175 | case 1: usb_send_data((uint8_t*)&KBD_report, sizeof(KBD_report), 0, sendtok); return; 176 | case 2: usb_send_data((uint8_t*)&CON_report, sizeof(CON_report), 0, sendtok); return; 177 | case 3: usb_send_data((uint8_t*)&MOUSE_report, sizeof(MOUSE_report), 0, sendtok); 178 | MOUSE_report[2] = 0; MOUSE_report[3] = 0; MOUSE_report[4] = 0; return; 179 | } 180 | } 181 | 182 | // Control transfer 183 | usb_send_empty(sendtok); 184 | } 185 | 186 | void usb_handle_user_data(struct usb_endpoint * e, int current_endpoint, uint8_t * data, int len, struct rv003usb_internal * ist) { 187 | if(current_endpoint == 1) KBD_state = data[0]; 188 | } 189 | -------------------------------------------------------------------------------- /software/custom_knob/src/usb_composite.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // USB HID Composite Device Functions for CH32V003 * v1.0 * 3 | // =================================================================================== 4 | // 5 | // Functions available: 6 | // -------------------- 7 | // HID_init() init HID composite device 8 | // 9 | // KBD_press(k) press a key on keyboard (see below for control keys) 10 | // KBD_release(k) release a key on keyboard 11 | // KBD_type(k) press and release a key on keyboard 12 | // KBD_releaseAll() release all keys on keyboard 13 | // KBD_print(s) type some text on the keyboard (string) 14 | // KBD_getState(); get state of keyboard LEDs (see below) 15 | // 16 | // CON_press(k) press a consumer/multimedia key (see below) 17 | // CON_release() release consumer/multimedia key 18 | // CON_type(k) press and release a consumer/multimedia key 19 | // 20 | // MOUSE_press(b) press button(s) (see below) 21 | // MOUSE_release(b) release button(s) 22 | // MOUSE_move(x,y) move mouse pointer (relative) 23 | // MOUSE_wheel(w) move mouse wheel (relative) 24 | // MOUSE_wheel_up() move mouse wheel one step up 25 | // MOUSE_wheel_down() move mouse wheel one step down 26 | // 27 | // 2023 by Stefan Wagner: https://github.com/wagiminator 28 | 29 | #pragma once 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | #include 36 | #include "usb_handler.h" 37 | 38 | // Functions 39 | #define HID_init usb_setup // init HID composite device 40 | 41 | void KBD_press(uint8_t key); // press a key on keyboard 42 | void KBD_release(uint8_t key); // release a key on keyboard 43 | void KBD_type(uint8_t key); // press and release a key on keyboard 44 | void KBD_releaseAll(void); // release all keys on keyboard 45 | void KBD_print(char* str); // type some text on the keyboard 46 | 47 | void CON_press(uint8_t key); // press a consumer key on keyboard 48 | void CON_release(void); // release consumer key on keyboard 49 | void CON_type(uint8_t key); // press and release a consumer key 50 | 51 | void MOUSE_press(uint8_t buttons); // press button(s) 52 | void MOUSE_release(uint8_t buttons); // release button(s) 53 | void MOUSE_move(int8_t xrel, int8_t yrel); // move mouse pointer (relative) 54 | void MOUSE_wheel(int8_t rel); // move mouse wheel (relative) 55 | 56 | #define MOUSE_wheel_up() MOUSE_wheel( 1) // move mouse wheel one step up 57 | #define MOUSE_wheel_down() MOUSE_wheel(-1) // move mouse wheel one step down 58 | 59 | // Mouse buttons 60 | #define MOUSE_BUTTON_LEFT 0x01 // left mouse button 61 | #define MOUSE_BUTTON_RIGHT 0x02 // right mouse button 62 | #define MOUSE_BUTTON_MIDDLE 0x04 // middle mouse button 63 | 64 | // Keyboard LED states 65 | extern volatile uint8_t KBD_state; 66 | #define KBD_getState() (KBD_state) 67 | #define KBD_NUM_LOCK_state (KBD_getState() & 1) 68 | #define KBD_CAPS_LOCK_state ((KBD_getState() >> 1) & 1) 69 | #define KBD_SCROLL_LOCK_state ((KBD_getState() >> 2) & 1) 70 | #define KBD_COMPOSE_state ((KBD_getState() >> 3) & 1) 71 | #define KBD_KANA_state ((KBD_getState() >> 4) & 1) 72 | 73 | // Modifier keys 74 | #define KBD_KEY_LEFT_CTRL 0x80 75 | #define KBD_KEY_LEFT_SHIFT 0x81 76 | #define KBD_KEY_LEFT_ALT 0x82 77 | #define KBD_KEY_LEFT_GUI 0x83 78 | #define KBD_KEY_RIGHT_CTRL 0x84 79 | #define KBD_KEY_RIGHT_SHIFT 0x85 80 | #define KBD_KEY_RIGHT_ALT 0x86 81 | #define KBD_KEY_RIGHT_GUI 0x87 82 | 83 | // Special keys 84 | #define KBD_KEY_UP_ARROW 0xDA 85 | #define KBD_KEY_DOWN_ARROW 0xD9 86 | #define KBD_KEY_LEFT_ARROW 0xD8 87 | #define KBD_KEY_RIGHT_ARROW 0xD7 88 | #define KBD_KEY_BACKSPACE 0xB2 89 | #define KBD_KEY_TAB 0xB3 90 | #define KBD_KEY_RETURN 0xB0 91 | #define KBD_KEY_ESC 0xB1 92 | #define KBD_KEY_INSERT 0xD1 93 | #define KBD_KEY_DELETE 0xD4 94 | #define KBD_KEY_PAGE_UP 0xD3 95 | #define KBD_KEY_PAGE_DOWN 0xD6 96 | #define KBD_KEY_HOME 0xD2 97 | #define KBD_KEY_END 0xD5 98 | #define KBD_KEY_CAPS_LOCK 0xC1 99 | #define KBD_KEY_F1 0xC2 100 | #define KBD_KEY_F2 0xC3 101 | #define KBD_KEY_F3 0xC4 102 | #define KBD_KEY_F4 0xC5 103 | #define KBD_KEY_F5 0xC6 104 | #define KBD_KEY_F6 0xC7 105 | #define KBD_KEY_F7 0xC8 106 | #define KBD_KEY_F8 0xC9 107 | #define KBD_KEY_F9 0xCA 108 | #define KBD_KEY_F10 0xCB 109 | #define KBD_KEY_F11 0xCC 110 | #define KBD_KEY_F12 0xCD 111 | #define KBD_KEY_F13 0xF0 112 | #define KBD_KEY_F14 0xF1 113 | #define KBD_KEY_F15 0xF2 114 | #define KBD_KEY_F16 0xF3 115 | #define KBD_KEY_F17 0xF4 116 | #define KBD_KEY_F18 0xF5 117 | #define KBD_KEY_F19 0xF6 118 | #define KBD_KEY_F20 0xF7 119 | #define KBD_KEY_F21 0xF8 120 | #define KBD_KEY_F22 0xF9 121 | #define KBD_KEY_F23 0xFA 122 | #define KBD_KEY_F24 0xFB 123 | 124 | // Consumer Keyboard Keycodes 125 | #define CON_SYS_POWER 0x30 126 | #define CON_SYS_RESET 0x31 127 | #define CON_SYS_SLEEP 0x32 128 | 129 | #define CON_VOL_MUTE 0xE2 130 | #define CON_VOL_UP 0xE9 131 | #define CON_VOL_DOWN 0xEA 132 | 133 | #define CON_MEDIA_PLAY 0xB0 134 | #define CON_MEDIA_PAUSE 0xB1 135 | #define CON_MEDIA_RECORD 0xB2 136 | #define CON_MEDIA_FORWARD 0xB3 137 | #define CON_MEDIA_REWIND 0xB4 138 | #define CON_MEDIA_NEXT 0xB5 139 | #define CON_MEDIA_PREV 0xB6 140 | #define CON_MEDIA_STOP 0xB7 141 | #define CON_MEDIA_EJECT 0xB8 142 | #define CON_MEDIA_RANDOM 0xB9 143 | 144 | #define CON_MENU 0x40 145 | #define CON_MENU_PICK 0x41 146 | #define CON_MENU_UP 0x42 147 | #define CON_MENU_DOWN 0x43 148 | #define CON_MENU_LEFT 0x44 149 | #define CON_MENU_RIGHT 0x45 150 | #define CON_MENU_ESCAPE 0x46 151 | #define CON_MENU_INCR 0x47 152 | #define CON_MENU_DECR 0x48 153 | 154 | #ifdef __cplusplus 155 | } 156 | #endif 157 | -------------------------------------------------------------------------------- /software/custom_knob/src/usb_descr.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // USB Descriptors and Definitions 3 | // =================================================================================== 4 | // 5 | // Definition of USB descriptors and endpoints. 6 | // 7 | // The following must be defined in config.h: 8 | // USB_VENDOR_ID - Vendor ID (16-bit word) 9 | // USB_PRODUCT_ID - Product ID (16-bit word) 10 | // USB_DEVICE_VERSION - Device version (16-bit BCD) 11 | // USB_LANGUAGE - Language descriptor code 12 | // USB_MAX_POWER_mA - Device max power in mA 13 | // All string descriptors. 14 | 15 | #pragma once 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | #include 22 | 23 | // =================================================================================== 24 | // RV003USB Defines 25 | // =================================================================================== 26 | //Defines the number of endpoints for this device. (Always add one for EP0). For two EPs, this should be 3. 27 | #define ENDPOINTS 2 28 | 29 | #define RV003USB_OPTIMIZE_FLASH 1 30 | #define RV003USB_HANDLE_IN_REQUEST 1 31 | #define RV003USB_OTHER_CONTROL 0 32 | #define RV003USB_HANDLE_USER_DATA 1 33 | #define RV003USB_HID_FEATURES 0 34 | 35 | #ifndef __ASSEMBLER__ 36 | #include 37 | 38 | // =================================================================================== 39 | // HID Report Descriptor 40 | // =================================================================================== 41 | static const uint8_t ReportDescr[] = { 42 | // Standard keyboard 43 | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 44 | 0x09, 0x06, // USAGE (Keyboard) 45 | 0xa1, 0x01, // COLLECTION (Application) 46 | 0x85, 0x01, // REPORT_ID (1) 47 | 0x05, 0x07, // USAGE_PAGE (Keyboard) 48 | 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) 49 | 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) 50 | 0x15, 0x00, // LOGICAL_MINIMUM (0) 51 | 0x25, 0x01, // LOGICAL_MAXIMUM (1) 52 | 0x75, 0x01, // REPORT_SIZE (1) 53 | 0x95, 0x08, // REPORT_COUNT (8) 54 | 0x81, 0x02, // INPUT (Data,Var,Abs) 55 | 0x75, 0x08, // REPORT_SIZE (8) 56 | 0x95, 0x01, // REPORT_COUNT (1) 57 | 0x81, 0x03, // INPUT (Cnst,Var,Abs) 58 | 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) 59 | 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) 60 | 0x15, 0x00, // LOGICAL_MINIMUM (0) 61 | 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255) 62 | 0x75, 0x08, // REPORT_SIZE (8) 63 | 0x95, 0x05, // REPORT_COUNT (5) 64 | 0x81, 0x00, // INPUT (Data,Ary,Abs) 65 | 0x05, 0x08, // USAGE_PAGE (LEDs) 66 | 0x19, 0x01, // USAGE_MINIMUM (Num Lock) 67 | 0x29, 0x05, // USAGE_MAXIMUM (Kana) 68 | 0x15, 0x00, // LOGICAL_MINIMUM (0) 69 | 0x25, 0x01, // LOGICAL_MAXIMUM (1) 70 | 0x75, 0x01, // REPORT_SIZE (1) 71 | 0x95, 0x05, // REPORT_COUNT (5) 72 | 0x91, 0x02, // OUTPUT (Data,Var,Abs) 73 | 0x75, 0x03, // REPORT_SIZE (3) 74 | 0x95, 0x01, // REPORT_COUNT (1) 75 | 0x91, 0x03, // OUTPUT (Cnst,Var,Abs) 76 | 0xc0, // END_COLLECTION 77 | 78 | // Consumer multimedia keyboard 79 | 0x05, 0x0c, // USAGE_PAGE (Consumer Devices) 80 | 0x09, 0x01, // USAGE (Consumer Control) 81 | 0xa1, 0x01, // COLLECTION (Application) 82 | 0x85, 0x02, // REPORT_ID (2) 83 | 0x19, 0x00, // USAGE_MINIMUM (Unassigned) 84 | 0x2a, 0x3c, 0x02, // USAGE_MAXIMUM (AC Format) 85 | 0x15, 0x00, // LOGICAL_MINIMUM (0) 86 | 0x26, 0x3c, 0x02, // LOGICAL_MAXIMUM (572) 87 | 0x75, 0x10, // REPORT_SIZE (16) 88 | 0x95, 0x01, // REPORT_COUNT (1) 89 | 0x81, 0x00, // INPUT (Data,Var,Abs) 90 | 0xc0, // END_COLLECTION 91 | 92 | // Mouse with wheel and 3 buttons 93 | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 94 | 0x09, 0x02, // USAGE (Mouse) 95 | 0xa1, 0x01, // COLLECTION (Application) 96 | 0x09, 0x01, // USAGE (Pointer) 97 | 0xa1, 0x00, // COLLECTION (Physical) 98 | 0x85, 0x03, // REPORT_ID (3) 99 | 0x05, 0x09, // USAGE_PAGE (Button) 100 | 0x19, 0x01, // USAGE_MINIMUM (Button 1) 101 | 0x29, 0x03, // USAGE_MAXIMUM (Button 3) 102 | 0x15, 0x00, // LOGICAL_MINIMUM (0) 103 | 0x25, 0x01, // LOGICAL_MAXIMUM (1) 104 | 0x75, 0x01, // REPORT_SIZE (1) 105 | 0x95, 0x03, // REPORT_COUNT (3) 106 | 0x81, 0x02, // INPUT (Data,Var,Abs) 107 | 0x75, 0x05, // REPORT_SIZE (5) 108 | 0x95, 0x01, // REPORT_COUNT (1) 109 | 0x81, 0x03, // INPUT (Cnst,Var,Abs) 110 | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 111 | 0x09, 0x30, // USAGE (X) 112 | 0x09, 0x31, // USAGE (Y) 113 | 0x09, 0x38, // USAGE (Wheel) 114 | 0x15, 0x81, // LOGICAL_MINIMUM (-127) 115 | 0x25, 0x7f, // LOGICAL_MAXIMUM (127) 116 | 0x75, 0x08, // REPORT_SIZE (8) 117 | 0x95, 0x03, // REPORT_COUNT (3) 118 | 0x81, 0x06, // INPUT (Data,Var,Rel) 119 | 0xc0, // END_COLLECTION 120 | 0xc0 // END_COLLECTION 121 | }; 122 | 123 | // =================================================================================== 124 | // Device Descriptor 125 | // =================================================================================== 126 | static const USB_DEV_DESCR DevDescr = { 127 | .bLength = sizeof(DevDescr), // size of the descriptor in bytes: 18 128 | .bDescriptorType = USB_DESCR_TYP_DEVICE, // device descriptor: 0x01 129 | .bcdUSB = 0x0110, // USB specification: USB 1.1 130 | .bDeviceClass = 0, // interface will define class 131 | .bDeviceSubClass = 0, // unused 132 | .bDeviceProtocol = 0, // unused 133 | .bMaxPacketSize0 = 8, // maximum packet size for Endpoint 0 134 | .idVendor = USB_VENDOR_ID, // VID 135 | .idProduct = USB_PRODUCT_ID, // PID 136 | .bcdDevice = USB_DEVICE_VERSION, // device version 137 | .iManufacturer = 1, // index of Manufacturer String Descr 138 | .iProduct = 2, // index of Product String Descriptor 139 | .iSerialNumber = 3, // index of Serial Number String Descr 140 | .bNumConfigurations = 1 // number of possible configurations 141 | }; 142 | 143 | // =================================================================================== 144 | // Configuration Descriptor 145 | // =================================================================================== 146 | struct USB_CFG_DESCR_HID { 147 | USB_CFG_DESCR config; 148 | USB_ITF_DESCR interface0; 149 | USB_HID_DESCR hid0; 150 | USB_ENDP_DESCR ep1IN; 151 | USB_ENDP_DESCR ep2OUT; 152 | }; 153 | 154 | static const struct USB_CFG_DESCR_HID CfgDescr = { 155 | // Configuration Descriptor 156 | .config = { 157 | .bLength = sizeof(USB_CFG_DESCR), // size of the descriptor in bytes 158 | .bDescriptorType = USB_DESCR_TYP_CONFIG, // configuration descriptor: 0x02 159 | .wTotalLength = sizeof(CfgDescr), // total length in bytes 160 | .bNumInterfaces = 1, // number of interfaces: 1 161 | .bConfigurationValue= 1, // value to select this configuration 162 | .iConfiguration = 0, // no configuration string descriptor 163 | .bmAttributes = 0x80, // attributes = bus powered, no wakeup 164 | .MaxPower = USB_MAX_POWER_mA / 2 // in 2mA units 165 | }, 166 | 167 | // Interface Descriptor 168 | .interface0 = { 169 | .bLength = sizeof(USB_ITF_DESCR), // size of the descriptor in bytes: 9 170 | .bDescriptorType = USB_DESCR_TYP_INTERF, // interface descriptor: 0x04 171 | .bInterfaceNumber = 0, // number of this interface: 0 172 | .bAlternateSetting = 0, // value used to select alternative setting 173 | .bNumEndpoints = 2, // number of endpoints used: 2 174 | .bInterfaceClass = USB_DEV_CLASS_HID, // interface class: HID (0x03) 175 | .bInterfaceSubClass = 1, // boot interface 176 | .bInterfaceProtocol = 1, // keyboard 177 | .iInterface = 4 // interface string descriptor 178 | }, 179 | 180 | // HID Descriptor 181 | .hid0 = { 182 | .bLength = sizeof(USB_HID_DESCR), // size of the descriptor in bytes: 9 183 | .bDescriptorType = USB_DESCR_TYP_HID, // HID descriptor: 0x21 184 | .bcdHID = 0x0110, // HID class spec version (BCD: 1.1) 185 | .bCountryCode = 33, // country code: US 186 | .bNumDescriptors = 1, // number of report descriptors: 1 187 | .bDescriptorTypeX = USB_DESCR_TYP_REPORT, // descriptor type: report (0x22) 188 | .wDescriptorLength = sizeof(ReportDescr) // report descriptor length 189 | }, 190 | 191 | // Endpoint Descriptor: Endpoint 1 (IN, Interrupt) 192 | .ep1IN = { 193 | .bLength = sizeof(USB_ENDP_DESCR), // size of the descriptor in bytes: 7 194 | .bDescriptorType = USB_DESCR_TYP_ENDP, // endpoint descriptor: 0x05 195 | .bEndpointAddress = USB_ENDP_ADDR_EP1_IN, // endpoint: 1, direction: IN (0x81) 196 | .bmAttributes = USB_ENDP_TYPE_INTER, // transfer type: interrupt (0x03) 197 | .wMaxPacketSize = 8, // max packet size 198 | .bInterval = 1 // polling intervall in ms 199 | }, 200 | 201 | // Endpoint Descriptor: Endpoint 2 (OUT, Interrupt) 202 | .ep2OUT = { 203 | .bLength = sizeof(USB_ENDP_DESCR), // size of the descriptor in bytes: 7 204 | .bDescriptorType = USB_DESCR_TYP_ENDP, // endpoint descriptor: 0x05 205 | .bEndpointAddress = USB_ENDP_ADDR_EP2_OUT, // endpoint: 1, direction: OUT (0x02) 206 | .bmAttributes = USB_ENDP_TYPE_INTER, // transfer type: interrupt (0x03) 207 | .wMaxPacketSize = 8, // max packet size 208 | .bInterval = 10 // polling intervall in ms 209 | } 210 | }; 211 | 212 | // =================================================================================== 213 | // String Descriptors 214 | // =================================================================================== 215 | #define __code const __attribute__((section(".rodata"))) 216 | #define USB_CHAR_GLUE(s) u##s 217 | #define USB_CHAR_SIZE(s) sizeof(USB_CHAR_GLUE(s)) 218 | #define USB_CHAR_TO_STR_DESCR(s) { \ 219 | .bLength = sizeof(USB_CHAR_GLUE(s)), \ 220 | .bDescriptorType = USB_DESCR_TYP_STRING, \ 221 | .bString = USB_CHAR_GLUE(s) \ 222 | } 223 | 224 | // Language Descriptor (Index 0) 225 | static __code USB_STR_DESCR string0 = { 226 | .bLength = 4, 227 | .bDescriptorType = USB_DESCR_TYP_STRING, 228 | .bString = {USB_LANGUAGE} 229 | }; 230 | 231 | // Manufacturer String Descriptor (Index 1) 232 | static __code USB_STR_DESCR string1 = USB_CHAR_TO_STR_DESCR(MANUF_STR); 233 | 234 | // Product String Descriptor (Index 2) 235 | static __code USB_STR_DESCR string2 = USB_CHAR_TO_STR_DESCR(PROD_STR); 236 | 237 | // Serial String Descriptor (Index 3) 238 | static __code USB_STR_DESCR string3 = USB_CHAR_TO_STR_DESCR(SERIAL_STR); 239 | 240 | // =================================================================================== 241 | // Descriptor Table 242 | // =================================================================================== 243 | // This table defines which descriptor data is sent for each specific 244 | // request from the host (in wValue and wIndex). 245 | struct descriptor_list_struct { 246 | uint32_t lIndexValue; 247 | const uint8_t *addr; 248 | uint8_t length; 249 | }; 250 | 251 | const static struct descriptor_list_struct descriptor_list[] = { 252 | {0x00000100, (const uint8_t *)&DevDescr, sizeof(DevDescr)}, 253 | {0x00000200, (const uint8_t *)&CfgDescr, sizeof(CfgDescr)}, 254 | {0x00002200, ReportDescr, sizeof(ReportDescr)}, 255 | {0x00000300, (const uint8_t *)&string0, 4}, 256 | {0x04090301, (const uint8_t *)&string1, USB_CHAR_SIZE(MANUF_STR)}, 257 | {0x04090302, (const uint8_t *)&string2, USB_CHAR_SIZE(PROD_STR)}, 258 | {0x04090303, (const uint8_t *)&string3, USB_CHAR_SIZE(SERIAL_STR)} 259 | }; 260 | 261 | #define DESCRIPTOR_LIST_ENTRIES ((sizeof(descriptor_list))/(sizeof(struct descriptor_list_struct)) ) 262 | 263 | #endif // __ASSEMBLER__ 264 | 265 | #ifdef __cplusplus 266 | } 267 | #endif 268 | -------------------------------------------------------------------------------- /software/custom_knob/src/usb_handler.c: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Software USB Handler for CH32V003 * v1.0 * 3 | // =================================================================================== 4 | // 5 | // This file contains a copy of rv003usb.c (https://github.com/cnlohr/rv003usb), 6 | // copyright (c) 2023 CNLohr (MIT License), with some minor changes by Stefan Wagner. 7 | 8 | #include 9 | 10 | #define INSTANCE_DESCRIPTORS 1 11 | 12 | #include "usb_handler.h" 13 | 14 | #define ENDPOINT0_SIZE 8 //Fixed for USB 1.1, Low Speed. 15 | 16 | struct rv003usb_internal rv003usb_internal_data; 17 | 18 | #define LOCAL_CONCAT(A, B) A##B 19 | #define LOCAL_EXP(A, B) LOCAL_CONCAT(A,B) 20 | 21 | void usb_setup(void) { 22 | rv003usb_internal_data.se0_windup = 0; 23 | 24 | // Enable GPIOs, TIMERs 25 | RCC->APB2PCENR |= LOCAL_EXP(LOCAL_EXP(RCC_IOP, USB_PORT), EN) | RCC_AFIOEN; 26 | 27 | // GPIO Setup 28 | LOCAL_EXP( GPIO, USB_PORT )->CFGLR = 29 | ( LOCAL_EXP( GPIO, USB_PORT )->CFGLR & 30 | (~( ( ( 0xf << (USB_PIN_DP*4)) | ( 0xf << (USB_PIN_DM*4)) 31 | #ifdef USB_PIN_DPU 32 | | ( 0xf << (USB_PIN_DPU*4)) 33 | #endif 34 | ) )) ) 35 | | 36 | #ifdef USB_PIN_DPU 37 | (0b0011)<<(4*USB_PIN_DPU) | 38 | #endif 39 | (0b1000)<<(4*USB_PIN_DP) | 40 | (0b1000)<<(4*USB_PIN_DM); 41 | 42 | // Configure USB_PIN_DM (D-) as an interrupt on falling edge. 43 | AFIO->EXTICR = LOCAL_EXP(AFIO_EXTICR1_EXTI0_P,USB_PORT)<<(USB_PIN_DM*2); // Configure EXTI interrupt for USB_PIN_DM 44 | EXTI->INTENR = 1<FTENR = 1<BSHR = 1<current_endpoint = endp; 59 | struct usb_endpoint * e = &ist->eps[endp]; 60 | 61 | int tosend = 0; 62 | uint8_t * sendnow; 63 | int sendtok = e->toggle_in?0b01001011:0b11000011; 64 | 65 | #if RV003USB_HANDLE_IN_REQUEST 66 | if(e->custom || endp) { 67 | // Can re-use data-stack as scratchpad. 68 | sendnow = __builtin_assume_aligned(data, 4); 69 | usb_handle_user_in_request(e, sendnow, endp, sendtok, ist); 70 | return; 71 | } 72 | #endif 73 | 74 | tosend = 0; 75 | 76 | // Handle IN (sending data back to PC) 77 | // Do this down here. 78 | // We do this because we are required to have an in-endpoint. We don't 79 | // have to do anything with it, though. 80 | uint8_t * tsend = e->opaque; 81 | 82 | int offset = (e->count)<<3; 83 | tosend = (int)e->max_len - offset; 84 | if(tosend > ENDPOINT0_SIZE) tosend = ENDPOINT0_SIZE; 85 | sendnow = tsend + offset; 86 | if(tosend <= 0) usb_send_empty(sendtok); 87 | else usb_send_data(sendnow, tosend, 0, sendtok); 88 | } 89 | 90 | void usb_pid_handle_out(uint32_t addr, uint8_t * data, uint32_t endp, uint32_t unused, struct rv003usb_internal * ist) { 91 | ist->current_endpoint = endp; 92 | 93 | // We need to handle this here because we could have an interrupt in the middle of a control or big transfer. 94 | // This will correctly swap back the endpoint. 95 | } 96 | 97 | void usb_pid_handle_data(uint32_t this_token, uint8_t * data, uint32_t which_data, uint32_t length, struct rv003usb_internal * ist) { 98 | //Received data from host. 99 | int epno = ist->current_endpoint; 100 | struct usb_endpoint * e = &ist->eps[epno]; 101 | 102 | length -= 3; 103 | uint8_t * data_in = __builtin_assume_aligned( data, 4 ); 104 | 105 | // Alrady received this packet. 106 | if(e->toggle_out != which_data) goto just_ack; 107 | 108 | e->toggle_out = !e->toggle_out; 109 | 110 | #if RV003USB_HANDLE_USER_DATA 111 | if(epno || (!ist->setup_request && length > 3)) { 112 | usb_handle_user_data( e, epno, data_in, length, ist ); 113 | #if RV003USB_USER_DATA_HANDLES_TOKEN 114 | return; 115 | #endif 116 | } 117 | else 118 | #endif 119 | 120 | if(ist->setup_request) { 121 | // For receiving CONTROL-OUT messages into RAM. NOTE: MUST be ALIGNED to 4, and be allocated round_up( payload, 8 ) + 4 122 | // opaque points to [length received, but uninitialized before complete][data...] 123 | #if RV003USB_SUPPORT_CONTROL_OUT 124 | if(ist->setup_request == 2) { 125 | // This mode is where we record control-in data into a pointer and mark it as 126 | int offset = e->count << 3; 127 | uint32_t * base = __builtin_assume_aligned( e->opaque, 4 ); 128 | uint32_t * dout = __builtin_assume_aligned( ((uint8_t*)base) + offset + 4, 4 ); 129 | uint32_t * din = __builtin_assume_aligned( data_in, 4 ); 130 | if(offset < e->max_len) { 131 | dout[0] = din[0]; 132 | dout[1] = din[1]; 133 | e->count++; 134 | if(offset + 8 >= e->max_len) base[0] = e->max_len; 135 | } 136 | goto just_ack; 137 | } 138 | #endif 139 | 140 | struct usb_urb * s = __builtin_assume_aligned( (struct usb_urb *)(data_in), 4 ); 141 | 142 | uint32_t wvi = s->lValueLSBIndexMSB; 143 | uint32_t wLength = s->wLength; 144 | //Send just a data packet. 145 | e->count = 0; 146 | e->opaque = 0; 147 | e->custom = 0; 148 | e->max_len = 0; 149 | ist->setup_request = 0; 150 | 151 | //int bRequest = s->wRequestTypeLSBRequestMSB >> 8; 152 | 153 | // We shift down because we don't care if USB_RECIP_INTERFACE is set or not. 154 | // Otherwise we have to write extra code to handle each case if it's set or 155 | // not set, but in general, there's never a situation where we realy care. 156 | uint32_t reqShl = s->wRequestTypeLSBRequestMSB >> 1; 157 | 158 | //LogUEvent( 0, s->wRequestTypeLSBRequestMSB, wvi, s->wLength ); 159 | #if RV003USB_HID_FEATURES 160 | if(reqShl == (0x01a1>>1)) { 161 | // Class read request. 162 | // The host wants to read back from us. hid_get_feature_report 163 | usb_handle_hid_get_report_start( e, wLength, wvi ); 164 | } 165 | else if(reqShl == (0x0921>>1)) { 166 | // Class request (Will be writing) This is hid_send_feature_report 167 | usb_handle_hid_set_report_start( e, wLength, wvi ); 168 | } 169 | else 170 | #endif 171 | 172 | if(reqShl == (0x0680>>1)) { // GET_DESCRIPTOR = 6 (msb) 173 | int i; 174 | const struct descriptor_list_struct * dl; 175 | for(i = 0; i < DESCRIPTOR_LIST_ENTRIES; i++) { 176 | dl = &descriptor_list[i]; 177 | if(dl->lIndexValue == wvi) { 178 | e->opaque = (uint8_t*)dl->addr; 179 | uint16_t swLen = wLength; 180 | uint16_t elLen = dl->length; 181 | e->max_len = (swLen < elLen)?swLen:elLen; 182 | } 183 | } 184 | } 185 | else if(reqShl == (0x0500>>1)) { // SET_ADDRESS = 0x05 186 | ist->my_address = wvi; 187 | } 188 | 189 | // You could handle SET_CONFIGURATION == 0x0900 here if you wanted. 190 | // Can also handle GET_CONFIGURATION == 0x0880 to which we send back { 0x00 }, or the interface number. (But no one does this). 191 | // You could handle SET_INTERFACE == 0x1101 here if you wanted. 192 | // or 193 | // USB_REQ_GET_INTERFACE to which we would send 0x00, or the interface # 194 | else { 195 | #if RV003USB_OTHER_CONTROL 196 | usb_handle_other_control_message( e, s, ist ); 197 | #endif 198 | } 199 | } 200 | just_ack: 201 | { 202 | //Got the right data. Acknowledge. 203 | usb_send_data( 0, 0, 2, 0xD2 ); // Send ACK 204 | } 205 | return; 206 | } 207 | 208 | #if defined( RV003USB_OPTIMIZE_FLASH ) && RV003USB_OPTIMIZE_FLASH 209 | 210 | // Do not compile ACK commands. 211 | 212 | #else 213 | 214 | void usb_pid_handle_ack(uint32_t dummy, uint8_t * data, uint32_t dummy1, uint32_t dummy2, struct rv003usb_internal * ist) { 215 | struct usb_endpoint * e = &ist->eps[ist->current_endpoint]; 216 | e->toggle_in = !e->toggle_in; 217 | e->count++; 218 | } 219 | 220 | //Received a setup for a specific endpoint. 221 | void usb_pid_handle_setup(uint32_t addr, uint8_t * data, uint32_t endp, uint32_t unused, struct rv003usb_internal * ist) { 222 | struct usb_endpoint * e = &ist->eps[endp]; 223 | ist->current_endpoint = endp; 224 | ist->setup_request = 1; 225 | e->toggle_in = 1; 226 | e->toggle_out = 0; 227 | e->count = 0; 228 | e->opaque = 0; 229 | } 230 | 231 | #endif 232 | -------------------------------------------------------------------------------- /software/custom_knob/src/usb_handler.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Software USB Handler for CH32V003 * v1.0 * 3 | // =================================================================================== 4 | // 5 | // This file contains a copy of rv003usb.h (https://github.com/cnlohr/rv003usb), 6 | // copyright (c) 2023 CNLohr (MIT License), with some minor changes by Stefan Wagner. 7 | 8 | #pragma once 9 | 10 | #include "usb_descr.h" 11 | 12 | // USB handler checks 13 | #ifndef __ASSEMBLER__ 14 | #include "system.h" 15 | #if SYS_USE_VECTORS == 0 16 | #error Interrupt vector table must be enabled (SYS_USE_VECTORS in system.h)! 17 | #endif 18 | #if SYS_TICK_INIT == 0 19 | #error SysTick must be enabled (SYS_TICK_INIT in system.h)! 20 | #endif 21 | #endif 22 | 23 | #define LOCAL_CONCAT_BASE(A, B) A##B##_BASE 24 | #define LOCAL_EXP_BASE(A, B) LOCAL_CONCAT_BASE(A,B) 25 | 26 | #define USB_GPIO_BASE LOCAL_EXP_BASE( GPIO, USB_PORT ) 27 | 28 | // Public stuff: 29 | 30 | /* Here are your options: 31 | #define RV003USB_HANDLE_IN_REQUEST 0 32 | #define RV003USB_OTHER_CONTROL 0 33 | #define RV003USB_HANDLE_USER_DATA 0 34 | #define RV003USB_HID_FEATURES 0 35 | #define RV003USB_SUPPORT_CONTROL_OUT 0 36 | #define RV003USB_CUSTOM_C 0 37 | #define RV003USB_USER_DATA_HANDLES_TOKEN 0 38 | */ 39 | 40 | #ifndef __ASSEMBLER__ 41 | 42 | extern uint32_t * always0; 43 | 44 | struct usb_endpoint; 45 | struct rv003usb_internal; 46 | struct usb_urb; 47 | 48 | // usb_hande_interrupt_in is OBLIGATED to call usb_send_data or usb_send_empty. 49 | // Enable with RV003USB_HANDLE_IN_REQUEST=1 50 | void usb_handle_user_in_request( struct usb_endpoint * e, uint8_t * scratchpad, int endp, uint32_t sendtok, struct rv003usb_internal * ist ); 51 | 52 | // Enable with RV003USB_HID_FEATURES=1 53 | void usb_handle_hid_set_report_start( struct usb_endpoint * e, int reqLen, uint32_t lValueLSBIndexMSB ); 54 | void usb_handle_hid_get_report_start( struct usb_endpoint * e, int reqLen, uint32_t lValueLSBIndexMSB ); 55 | 56 | // Enable with RV003USB_OTHER_CONTROL=1 57 | void usb_handle_other_control_message( struct usb_endpoint * e, struct usb_urb * s, struct rv003usb_internal * ist ); 58 | 59 | // Received data from the host which is not an internal control message, i.e. 60 | // this could be going to an endpoint or be data coming in for an unidentified 61 | // control message. 62 | // Enable with RV003USB_HANDLE_USER_DATA=1 63 | void usb_handle_user_data( struct usb_endpoint * e, int current_endpoint, uint8_t * data, int len, struct rv003usb_internal * ist ); 64 | 65 | // If you want to use custom functions for the level 2 stack, then say 66 | // RV003USB_CUSTOM_C 67 | // This is mostly useful on things like bootloaders. 68 | 69 | // Note: This checks addr & endp to make sure they are valid. 70 | void usb_pid_handle_setup( uint32_t addr, uint8_t * data, uint32_t endp, uint32_t unused, struct rv003usb_internal * ist ); 71 | void usb_pid_handle_in( uint32_t addr, uint8_t * data, uint32_t endp, uint32_t unused, struct rv003usb_internal * ist ); 72 | void usb_pid_handle_out( uint32_t addr, uint8_t * data, uint32_t endp, uint32_t unused, struct rv003usb_internal * ist ); 73 | void usb_pid_handle_data( uint32_t this_token, uint8_t * data, uint32_t which_data, uint32_t length, struct rv003usb_internal * ist ); 74 | void usb_pid_handle_ack( uint32_t dummy, uint8_t * data, uint32_t dummy2, uint32_t dummy3, struct rv003usb_internal * ist ); 75 | 76 | //poly_function = 0 to include CRC. 77 | //poly_function = 2 to exclude CRC. 78 | //This function is provided in assembly 79 | void usb_send_data( const void * data, uint32_t length, uint32_t poly_function, uint32_t token ); 80 | void usb_send_empty( uint32_t token ); 81 | 82 | void usb_setup(); 83 | 84 | #define LogUEvent( a, b, c, d ) 85 | #define GetUEvent() 0 86 | 87 | #endif // __ASSEMBLER__ 88 | 89 | 90 | // Internal stuff. 91 | 92 | // Packet Type + 8 + CRC + Buffer 93 | #define USB_BUFFER_SIZE 12 94 | 95 | #define USB_DMASK ((1<<(USB_PIN_DP)) | 1<<(USB_PIN_DM)) 96 | 97 | #ifdef RV003USB_OPTIMIZE_FLASH 98 | #define MY_ADDRESS_OFFSET_BYTES 4 99 | #define LAST_SE0_OFFSET 16 100 | #define DELTA_SE0_OFFSET 20 101 | #define SE0_WINDUP_OFFSET 24 102 | #define ENDP_OFFSET 28 103 | #define SETUP_REQUEST_OFFSET 8 104 | 105 | #define EP_COUNT_OFFSET 0 106 | #define EP_TOGGLE_IN_OFFSET 4 107 | #define EP_TOGGLE_OUT_OFFSET 8 108 | #define EP_IS_CUSTOM_OFFSET 12 109 | #define EP_MAX_LEN_OFFSET 16 110 | #define EP_OPAQUE_OFFSET 28 111 | #else 112 | #define MY_ADDRESS_OFFSET_BYTES 1 113 | #define LAST_SE0_OFFSET 4 114 | #define DELTA_SE0_OFFSET 8 115 | #define SE0_WINDUP_OFFSET 12 116 | #endif 117 | 118 | #ifndef __ASSEMBLER__ 119 | 120 | #define EMPTY_SEND_BUFFER (uint8_t*)1 121 | 122 | // This can be undone once support for fast-c.lbu / c.sbu is made available. 123 | #ifdef RV003USB_OPTIMIZE_FLASH 124 | #define TURBO8TYPE uint32_t 125 | #define TURBO16TYPE uint32_t 126 | #else 127 | #define TURBO8TYPE uint8_t 128 | #define TURBO16TYPE uint16_t 129 | #endif 130 | 131 | struct usb_endpoint { 132 | TURBO8TYPE count; // ack count / in count 133 | TURBO8TYPE toggle_in; // DATA0 or DATA1? 134 | TURBO8TYPE toggle_out; // Out PC->US 135 | TURBO8TYPE custom; // Anything nonzero will incur the custom call. 136 | TURBO16TYPE max_len; 137 | TURBO16TYPE reserved1; 138 | uint32_t reserved2; 139 | uint8_t * opaque; // For user. 140 | }; // CAREFUL! sizeof pacekt 141 | 142 | // Make the size of this a power of 2, otherwise it will be slow to access. 143 | #ifdef RV003USB_OPTIMIZE_FLASH 144 | _Static_assert( (sizeof(struct usb_endpoint) == 32), "usb_endpoint must be pow2 sized" ); 145 | #else 146 | _Static_assert( (sizeof(struct usb_endpoint) == 16), "usb_endpoint must be pow2 sized" ); 147 | #endif 148 | 149 | struct rv003usb_internal { 150 | TURBO8TYPE current_endpoint; // Can this be combined with setup_request? 151 | TURBO8TYPE my_address; // Will be 0 until set up. 152 | TURBO8TYPE setup_request; // 0 for non-setup request, 1 after setup token, is allowed to be 2 for control-out if RV003USB_SUPPORT_CONTROL_OUT is set. 153 | TURBO8TYPE reserved; 154 | uint32_t last_se0_cyccount; 155 | int32_t delta_se0_cyccount; 156 | uint32_t se0_windup; 157 | // 5 bytes + 6 * ENDPOINTS 158 | 159 | struct usb_endpoint eps[ENDPOINTS]; 160 | }; 161 | 162 | //Detailed analysis of some useful stuff and performance tweaking: http://naberius.de/2015/05/14/esp8266-gpio-output-performance/ 163 | //Reverse engineerd boot room can be helpful, too: http://cholla.mmto.org/esp8266/bootrom/boot.txt 164 | //USB Protocol read from wikipedia: https://en.wikipedia.org/wiki/USB 165 | // Neat stuff: http://www.usbmadesimple.co.uk/ums_3.htm 166 | // Neat stuff: http://www.beyondlogic.org/usbnutshell/usb1.shtml 167 | 168 | struct usb_urb { 169 | uint16_t wRequestTypeLSBRequestMSB; 170 | uint32_t lValueLSBIndexMSB; 171 | uint16_t wLength; 172 | } __attribute__((packed)); 173 | 174 | 175 | extern struct rv003usb_internal rv003usb_internal_data; 176 | 177 | #endif // __ASSEMBLER__ 178 | -------------------------------------------------------------------------------- /software/mousewheel_knob/bin/mousewheel_knob.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/bd0be4a51de5bf36abb6228fba8c7d0bc2adba6d/software/mousewheel_knob/bin/mousewheel_knob.bin -------------------------------------------------------------------------------- /software/mousewheel_knob/bin/mousewheel_knob.hex: -------------------------------------------------------------------------------- 1 | :100000006F00A01E000000009C0000009C0000008B 2 | :1000100000000000000000000000000000000000E0 3 | :1000200000000000000000000000000000000000D0 4 | :100030009C000000000000009C0000000000000088 5 | :100040009C0000009C0000009C0000009C00000040 6 | :10005000A40300009C0000009C0000009C00000025 7 | :100060009C0000009C0000009C0000009C00000020 8 | :100070009C0000009C0000009C0000009C00000010 9 | :100080009C0000009C0000009C0000009C00000000 10 | :0C0090009C0000009C0000009C00000090 11 | :10009C0001A073270030937777F7739007301377AD 12 | :1000AC008708B70700209387070083C6370036956B 13 | :1000BC0062056185A381A700F3270030D98F739067 14 | :1000CC0007308280B71701409843511122C2137731 15 | :1000DC00F7F026C006C41367070898C3094798CBE6 16 | :1000EC009443FD751387F50F0566F98E130706808B 17 | :1000FC00D98E94C3114588CB94433707F1FF7D17F4 18 | :10010C00F98E37070800D98E94C3414798CB23AE9C 19 | :10011C000180B7160240984EBD051306068813677A 20 | :10012C00570098CE03A707808144370400206D8FB9 21 | :10013C00518F23A0E780B707014023A4070023A019 22 | :10014C00A74023A6A74037071000B7E700E023A07D 23 | :10015C00E710B717014098479C47098B39E7918BFB 24 | :10016C007D5591E305453537B7F700E09847B7570C 25 | :10017C000700938707303E97B7F600E09C46998FAF 26 | :10018C00E3CE07FE371701401C47898BF5DFB7F725 27 | :10019C0000E09847B167938707B83E97B7F600E041 28 | :1001AC009C46998FE3CE07FE6DB7C18B91ECE5F3BE 29 | :1001BC001307040083470700854493F7F70F93E771 30 | :1001CC0047002300F700E1B7F9D313070400834776 31 | :1001DC000700814493F7B70F2300F7004DBF930737 32 | :1001EC00000D9701002093812161138101001305FB 33 | :1001FC008008731005308D457390458017050000FD 34 | :10020C00130585DF4D8D7310553073901734B70778 35 | :10021C0000200567B7060020938707001307478B5C 36 | :10022C009386060063EDD704B70700209387070079 37 | :10023C001387018663EBE704B7270240054798C391 38 | :10024C00B707000137170240938717081CC3B7067E 39 | :10025C0000021C43F58FF5DF89475CC3A146B71735 40 | :10026C000240D843318BE31ED7FE37F700E09546AA 41 | :10027C0014C3984F1367470398CF73002030104373 42 | :10028C001107910723AEC7FE71BF910723AE07FE7E 43 | :10029C0055B793175600B306F70010C38C5293064C 44 | :1002AC00300C99C19306B004BA979C57D18F95CB5B 45 | :1002BC0085476313F60451113705002022C201460D 46 | :1002CC001304050091451305050006C4592EA3001F 47 | :1002DC00040023010400A240A301040012443101D4 48 | :1002EC008280484F1C5F4C570E05898D3E95A14767 49 | :1002FC0063C6B7006345B0003685A9AEA14501467B 50 | :10030C008DA61C439607BA97D453639AC60693B628 51 | :10031C001600D4D31447A5C683D6450003D62500B2 52 | :10032C0003D56500C206D18E03D6050023AE0700A7 53 | :10033C0023AC070223A4070223A6070223240700E9 54 | :10034C000582930500346312B6040567130707860C 55 | :10035C00930547052A8310436310D602504390DF60 56 | :10036C00034687009312060193D202016373550072 57 | :10037C001A8642064182D0D73107E31EB7FC93069A 58 | :10038C00200D094681450145E9AC93070028E31887 59 | :10039C00F6FE54C3EDB700005D712AC03ECAB71714 60 | :1003AC00014093870780884719892EC232C436C60C 61 | :1003BC003AC826CE12D837320140130282C58C4778 62 | :1003CC0099896309052288471989631BB5028847F7 63 | :1003DC0019896317B502884719896313B5028847D1 64 | :1003EC001989631FB50088471989631BB5008847B5 65 | :1003FC0019896317B500884719896313B50009A0DB 66 | :10040C0022CC014616D01AD2232002008847198923 67 | :10041C00630105182D8DA98D09A009C909A0804774 68 | :10042C0019882D8C11E009A0C5B701001ED406DA7D 69 | :10043C00A144194401000100C166FD162967050796 70 | :10044C009303F10393020008010001002320020032 71 | :10045C0088471989630F05122D8DA98D010013355D 72 | :10046C0015000606498E7D15498C1D887D14FD14DA 73 | :10047C00E1F4930400061375C60009C55147F9460B 74 | :10048C00130000002380C300938313002320020079 75 | :10049C00884719892D8D0DC5A98D09A01944BDC991 76 | :1004AC001395F6017D858582798DA98E0582FD14C3 77 | :1004BC0013F5740061D9010001000100E1F8D1A825 78 | :1004CC007D1413F516007D15798D8582A98E058214 79 | :1004DC001366060811C8FD1413F5740045D5010008 80 | :1004EC0001000100C5F475A0FD1413F5740009E5B5 81 | :1004FC002380C300938313002320020088471989AB 82 | :10050C0011C92D8DA98D51C5194401000D457D15BD 83 | :10051C007DFDADFC13F574002DED9305F1038821E1 84 | :10052C0085059700000093800007930755FB425206 85 | :10053C0017070020130787ACC9C785EA92211375EA 86 | :10054C00F6071D823D8A09446375860401C5402364 87 | :10055C006311A404938747FCC1CFC51763940700AC 88 | :10056C006FF03FD38917B5CB2DA02D640504818E78 89 | :10057C008DE2B386B3408506938787F80146639472 90 | :10058C0007006FF01FD8C5170546639407006FF07E 91 | :10059C005FD76244F24482521253A253D250F244B7 92 | :1005AC0042522246B2464247924509A0B707014043 93 | :1005BC0093874741114588C30245D2476161730057 94 | :1005CC002030104316063A967106484285452D8D0B 95 | :1005DC0048C20842050508C26DBF10C385450CC74B 96 | :1005EC0016063A960CD281454CCE0CDE4CD255B741 97 | :1005FC0010A345B737F500E02105170700201307B6 98 | :10060C00E79F0C4B104110CB0D8E50CBB1659385F1 99 | :10061C0005B80D8E8567938707FAE352F6F8FD77D8 100 | :10062C0093870706E34DF6F60C4FB2950CCFA5D980 101 | :10063C00371702400843135635007D8A930770F034 102 | :10064C007D8DB305B04013D6954041060E06518DF5 103 | :10065C0008C3B1B7AA8617050000130525138945F1 104 | :10066C002E86411122C026C2B717014093870780FE 105 | :10067C009843FD74BD04658F93040022458FB70425 106 | :10068C000400890484CB98C337030600190312C4F1 107 | :10069C009E0613E40604814209E6A96285024166BE 108 | :1006AC007D168E05AE8337320140130282C5194781 109 | :1006BC00BD4509A0A2860580858A7D1781E6B3C455 110 | :1006CC006400194784CBFD1591C58D46FD16FDFEC2 111 | :1006DC000100CDB79E85ADC5FD1563940200416642 112 | :1006EC007D16010000210505A286858A81CE058034 113 | :1006FC0093761600FD16B3F656000582358E7D17DF 114 | :10070C0035CB29A805809316F601FD86B3C4640089 115 | :10071C0084CB19470582B3F65600358E81C993F602 116 | :10072C007500FD15E1D209A0C1B70100638A020072 117 | :10073C0093D282009D45E38902FA1344F6FF6DB70C 118 | :10074C008D46FD16FDFEB704060084CBA146FD16B2 119 | :10075C00FDFEB7040200910484CB84439306F0CCD5 120 | :10076C00F58C93060044D58C84C3024492442242F7 121 | :10077C00410182809146FD16FDFEB3C46400194709 122 | :10078C000100010084CB59BF000000001803430096 123 | :10079C00480033003200560030003000330048006F 124 | :1007AC0049004400000000001E03550053004200A5 125 | :1007BC0020004D006F0075007300650020004B0099 126 | :1007CC006E006F0062000000180377006100670084 127 | :1007DC0069006D0069006E00610074006F007200AA 128 | :1007EC00000000000403090409022200010100803A 129 | :1007FC001909040000010301020009211101000183 130 | :10080C002234000705810304000A000012011001C4 131 | :10081C0000000008091203C00001010203010000DE 132 | :10082C0005010902A1010901A1000509190129030A 133 | :10083C00150025017501950381027505950181034C 134 | :10084C0005010930093109381581257F7508950393 135 | :10085C008106C0C000010000180800001200000052 136 | :10086C0000020000F407000022000000002200003B 137 | :10087C002C0800003400000000030000F00700000A 138 | :10088C000400000001030904D40700001800000054 139 | :10089C0002030904B40700001E000000030309044E 140 | :0808AC0098070000180000008D 141 | :00000001FF 142 | -------------------------------------------------------------------------------- /software/mousewheel_knob/config.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // User Configurations 3 | // =================================================================================== 4 | 5 | #pragma once 6 | 7 | // Rotary encoder in definitions 8 | #define PIN_ENC_A PC1 // pin connected to rotary encoder A 9 | #define PIN_ENC_B PC2 // pin connected to rotary encoder B 10 | #define PIN_ENC_SW PC4 // pin connected to rotary encoder switch 11 | 12 | // USB pin definitions 13 | #define USB_PORT A // [A,C,D] GPIO Port to use with D+ and D- 14 | #define USB_PIN_DP 1 // [0-4] GPIO Number for USB D+ Pin 15 | #define USB_PIN_DM 2 // [0-4] GPIO Number for USB D- Pin 16 | 17 | // USB configuration descriptor 18 | #define USB_MAX_POWER_mA 50 // max power in mA 19 | 20 | // USB device descriptor 21 | #define USB_VENDOR_ID 0x1209 // VID 22 | #define USB_PRODUCT_ID 0xc003 // PID 23 | #define USB_DEVICE_VERSION 0x0100 // v1.0 (BCD-format) 24 | #define USB_LANGUAGE 0x0409 // US English 25 | 26 | // USB descriptor strings 27 | #define MANUF_STR "wagiminator" 28 | #define PROD_STR "USB Mouse Knob" 29 | #define SERIAL_STR "CH32V003HID" 30 | 31 | -------------------------------------------------------------------------------- /software/mousewheel_knob/ld/ch32v003.ld: -------------------------------------------------------------------------------- 1 | ENTRY( jump_reset ) 2 | 3 | MEMORY 4 | { 5 | FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 16K 6 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 2K 7 | } 8 | 9 | SECTIONS 10 | { 11 | .init : 12 | { 13 | _sinit = .; 14 | . = ALIGN(4); 15 | KEEP(*(SORT_NONE(.init.jump))) 16 | KEEP(*(SORT_NONE(.init.vectors))) 17 | . = ALIGN(4); 18 | _einit = .; 19 | } >FLASH AT>FLASH 20 | 21 | .text : 22 | { 23 | . = ALIGN(4); 24 | *(.text) 25 | *(.text.*) 26 | *(.rodata) 27 | *(.rodata*) 28 | *(.gnu.linkonce.t.*) 29 | . = ALIGN(4); 30 | } >FLASH AT>FLASH 31 | 32 | .fini : 33 | { 34 | KEEP(*(SORT_NONE(.fini))) 35 | . = ALIGN(4); 36 | } >FLASH AT>FLASH 37 | 38 | PROVIDE(_etext = .); 39 | PROVIDE(_eitcm = .); 40 | 41 | .preinit_array : 42 | { 43 | PROVIDE_HIDDEN(__preinit_array_start = .); 44 | KEEP(*(.preinit_array)) 45 | PROVIDE_HIDDEN(__preinit_array_end = .); 46 | } >FLASH AT>FLASH 47 | 48 | .init_array : 49 | { 50 | PROVIDE_HIDDEN(__init_array_start = .); 51 | KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)SORT_BY_INIT_PRIORITY(.ctors.*))) 52 | KEEP(*(.init_array EXCLUDE_FILE(*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o) .ctors)) 53 | PROVIDE_HIDDEN(__init_array_end = .); 54 | } >FLASH AT>FLASH 55 | 56 | .fini_array : 57 | { 58 | PROVIDE_HIDDEN(__fini_array_start = .); 59 | KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) 60 | KEEP(*(.fini_array EXCLUDE_FILE(*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o) .dtors)) 61 | PROVIDE_HIDDEN(__fini_array_end = .); 62 | } >FLASH AT>FLASH 63 | 64 | .ctors : 65 | { 66 | KEEP(*crtbegin.o(.ctors)) 67 | KEEP(*crtbegin?.o(.ctors)) 68 | KEEP(*(EXCLUDE_FILE(*crtend.o *crtend?.o) .ctors)) 69 | KEEP(*(SORT(.ctors.*))) 70 | KEEP(*(.ctors)) 71 | } >FLASH AT>FLASH 72 | 73 | .dtors : 74 | { 75 | KEEP(*crtbegin.o(.dtors)) 76 | KEEP(*crtbegin?.o(.dtors)) 77 | KEEP(*(EXCLUDE_FILE(*crtend.o *crtend?.o) .dtors)) 78 | KEEP(*(SORT(.dtors.*))) 79 | KEEP(*(.dtors)) 80 | } >FLASH AT>FLASH 81 | 82 | .dalign : 83 | { 84 | . = ALIGN(4); 85 | PROVIDE(_data_vma = .); 86 | } >RAM AT>FLASH 87 | 88 | .dlalign : 89 | { 90 | . = ALIGN(4); 91 | PROVIDE(_data_lma = .); 92 | } >FLASH AT>FLASH 93 | 94 | .data : 95 | { 96 | . = ALIGN(4); 97 | *(.gnu.linkonce.r.*) 98 | *(.data .data.*) 99 | *(.gnu.linkonce.d.*) 100 | . = ALIGN(8); 101 | PROVIDE(__global_pointer$ = . + 0x800); 102 | *(.sdata .sdata.*) 103 | *(.sdata2*) 104 | *(.gnu.linkonce.s.*) 105 | . = ALIGN(8); 106 | *(.srodata.cst16) 107 | *(.srodata.cst8) 108 | *(.srodata.cst4) 109 | *(.srodata.cst2) 110 | *(.srodata .srodata.*) 111 | . = ALIGN(4); 112 | PROVIDE(_edata = .); 113 | } >RAM AT>FLASH 114 | 115 | .bss : 116 | { 117 | . = ALIGN(4); 118 | PROVIDE(_sbss = .); 119 | *(.sbss*) 120 | *(.gnu.linkonce.sb.*) 121 | *(.bss*) 122 | *(.gnu.linkonce.b.*) 123 | *(COMMON*) 124 | . = ALIGN(4); 125 | PROVIDE(_ebss = .); 126 | } >RAM AT>FLASH 127 | 128 | PROVIDE(_end = _ebss); 129 | PROVIDE(end = . ); 130 | PROVIDE(_eusrstack = ORIGIN(RAM) + LENGTH(RAM)); 131 | } 132 | -------------------------------------------------------------------------------- /software/mousewheel_knob/makefile: -------------------------------------------------------------------------------- 1 | # =================================================================================== 2 | # Project Makefile 3 | # =================================================================================== 4 | # Project: USB Rotary Encoder for CH32V003 - Mouse Wheel 5 | # Author: Stefan Wagner 6 | # Year: 2024 7 | # URL: https://github.com/wagiminator 8 | # =================================================================================== 9 | # Install toolchain: 10 | # sudo apt install build-essential libnewlib-dev gcc-riscv64-unknown-elf 11 | # sudo apt install python3 python3-pip 12 | # pip install rvprog 13 | # 14 | # Provide access permission to WCH-LinkE programmer: 15 | # echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="8010", MODE="666"' | sudo tee /etc/udev/rules.d/99-WCH-LinkE.rules 16 | # echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="8012", MODE="666"' | sudo tee -a /etc/udev/rules.d/99-WCH-LinkE.rules 17 | # sudo udevadm control --reload-rules 18 | # 19 | # Connect WCH-LinkE programmer to your board. Type "make flash" in the command line. 20 | # =================================================================================== 21 | 22 | # Files and Folders 23 | TARGET = mousewheel_knob 24 | INCLUDE = include 25 | SOURCE = src 26 | BIN = bin 27 | 28 | # Microcontroller Settings 29 | F_CPU = 48000000 30 | LDSCRIPT = ld/ch32v003.ld 31 | CPUARCH = -march=rv32ec -mabi=ilp32e 32 | 33 | # Toolchain 34 | PREFIX = riscv64-unknown-elf 35 | CC = $(PREFIX)-gcc 36 | OBJCOPY = $(PREFIX)-objcopy 37 | OBJDUMP = $(PREFIX)-objdump 38 | OBJSIZE = $(PREFIX)-size 39 | NEWLIB = /usr/include/newlib 40 | ISPTOOL = rvprog -f $(BIN)/$(TARGET).bin 41 | CLEAN = rm -f *.lst *.obj *.cof *.list *.map *.eep.hex *.o *.d 42 | 43 | # Compiler Flags 44 | CFLAGS = -g -Os -flto -ffunction-sections -fdata-sections -fno-builtin -nostdlib 45 | CFLAGS += $(CPUARCH) -DF_CPU=$(F_CPU) -I$(NEWLIB) -I$(INCLUDE) -I$(SOURCE) -I. -Wall 46 | LDFLAGS = -T$(LDSCRIPT) -lgcc -Wl,--gc-sections,--build-id=none 47 | CFILES = $(wildcard ./*.c) $(wildcard $(SOURCE)/*.c) $(wildcard $(SOURCE)/*.S) 48 | 49 | # Symbolic Targets 50 | help: 51 | @echo "Use the following commands:" 52 | @echo "make all compile and build $(TARGET).elf/.bin/.hex/.asm" 53 | @echo "make hex compile and build $(TARGET).hex" 54 | @echo "make asm compile and disassemble to $(TARGET).asm" 55 | @echo "make bin compile and build $(TARGET).bin" 56 | @echo "make flash compile and upload to MCU" 57 | @echo "make clean remove all build files" 58 | 59 | $(BIN)/$(TARGET).elf: $(CFILES) 60 | @echo "Building $(BIN)/$(TARGET).elf ..." 61 | @mkdir -p $(BIN) 62 | @$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) 63 | 64 | $(BIN)/$(TARGET).lst: $(BIN)/$(TARGET).elf 65 | @echo "Building $(BIN)/$(TARGET).lst ..." 66 | @$(OBJDUMP) -S $^ > $(BIN)/$(TARGET).lst 67 | 68 | $(BIN)/$(TARGET).map: $(BIN)/$(TARGET).elf 69 | @echo "Building $(BIN)/$(TARGET).map ..." 70 | @$(OBJDUMP) -t $^ > $(BIN)/$(TARGET).map 71 | 72 | $(BIN)/$(TARGET).bin: $(BIN)/$(TARGET).elf 73 | @echo "Building $(BIN)/$(TARGET).bin ..." 74 | @$(OBJCOPY) -O binary $< $(BIN)/$(TARGET).bin 75 | 76 | $(BIN)/$(TARGET).hex: $(BIN)/$(TARGET).elf 77 | @echo "Building $(BIN)/$(TARGET).hex ..." 78 | @$(OBJCOPY) -O ihex $< $(BIN)/$(TARGET).hex 79 | 80 | $(BIN)/$(TARGET).asm: $(BIN)/$(TARGET).elf 81 | @echo "Disassembling to $(BIN)/$(TARGET).asm ..." 82 | @$(OBJDUMP) -d $(BIN)/$(TARGET).elf > $(BIN)/$(TARGET).asm 83 | 84 | all: $(BIN)/$(TARGET).lst $(BIN)/$(TARGET).map $(BIN)/$(TARGET).bin $(BIN)/$(TARGET).hex $(BIN)/$(TARGET).asm size 85 | 86 | elf: $(BIN)/$(TARGET).elf removetemp size 87 | 88 | bin: $(BIN)/$(TARGET).bin removetemp size removeelf 89 | 90 | hex: $(BIN)/$(TARGET).hex removetemp size removeelf 91 | 92 | asm: $(BIN)/$(TARGET).asm removetemp size removeelf 93 | 94 | flash: $(BIN)/$(TARGET).bin size removeelf 95 | @echo "Uploading to MCU ..." 96 | @$(ISPTOOL) 97 | 98 | clean: 99 | @echo "Cleaning all up ..." 100 | @$(CLEAN) 101 | @rm -f $(BIN)/$(TARGET).elf $(BIN)/$(TARGET).lst $(BIN)/$(TARGET).map $(BIN)/$(TARGET).bin $(BIN)/$(TARGET).hex $(BIN)/$(TARGET).asm 102 | 103 | size: 104 | @echo "------------------" 105 | @echo "FLASH: $(shell $(OBJSIZE) -d $(BIN)/$(TARGET).elf | awk '/[0-9]/ {print $$1 + $$2}') bytes" 106 | @echo "SRAM: $(shell $(OBJSIZE) -d $(BIN)/$(TARGET).elf | awk '/[0-9]/ {print $$2 + $$3}') bytes" 107 | @echo "------------------" 108 | 109 | removetemp: 110 | @echo "Removing temporary files ..." 111 | @$(CLEAN) 112 | 113 | removeelf: 114 | @echo "Removing $(BIN)/$(TARGET).elf ..." 115 | @rm -f $(BIN)/$(TARGET).elf 116 | -------------------------------------------------------------------------------- /software/mousewheel_knob/src/main.c: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Project: USB Rotary Encoder for CH32V003 - Mouse Wheel 3 | // Version: v1.0 4 | // Year: 2024 5 | // Author: Stefan Wagner 6 | // Github: https://github.com/wagiminator 7 | // EasyEDA: https://easyeda.com/wagiminator 8 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 9 | // =================================================================================== 10 | // 11 | // Description: 12 | // ------------ 13 | // Turns the rotary encoder into a mouse wheel. 14 | // 15 | // References: 16 | // ----------- 17 | // - CNLohr ch32v003fun: https://github.com/cnlohr/ch32v003fun 18 | // - CNLohr rv003usb: https://github.com/cnlohr/rv003usb 19 | // - WCH Nanjing Qinheng Microelectronics: http://wch.cn 20 | // 21 | // Compilation Instructions: 22 | // ------------------------- 23 | // - Make sure GCC toolchain (gcc-riscv64-unknown-elf, newlib) and Python3 with rvprog 24 | // are installed. In addition, Linux requires access rights to WCH-LinkE programmer. 25 | // - Connect the WCH-LinkE programmer to the MCU board. 26 | // - Run 'make flash'. 27 | // 28 | // Operating Instructions: 29 | // ----------------------- 30 | // - Connect the board via USB to your PC. It should be detected as a HID device. 31 | // - Use the rotary encoder like the wheel on your mouse. 32 | 33 | 34 | // =================================================================================== 35 | // Libraries, Definitions and Macros 36 | // =================================================================================== 37 | #include "gpio.h" // GPIO functions 38 | #include "usb_mouse.h" // USB HID mouse functions 39 | 40 | // =================================================================================== 41 | // Main Function 42 | // =================================================================================== 43 | int main(void) { 44 | // Variables 45 | uint8_t isSwitchPressed = 0; // state of rotary encoder switch 46 | 47 | // Setup 48 | PIN_input_PU(PIN_ENC_A); // set encoder pins to input pullup 49 | PIN_input_PU(PIN_ENC_B); 50 | PIN_input_PU(PIN_ENC_SW); 51 | MOUSE_init(); // init USB HID mouse 52 | 53 | // Loop 54 | while(1) { 55 | if(!PIN_read(PIN_ENC_A)) { // encoder turned ? 56 | if(PIN_read(PIN_ENC_B)) { // clockwise? 57 | MOUSE_wheel(-1); // -> turn mousewheel down 58 | } 59 | else { // counter-clockwise? 60 | MOUSE_wheel(1); // -> turn mousewheel up 61 | } 62 | DLY_ms(10); // debounce 63 | while(!PIN_read(PIN_ENC_A)); // wait until next detent 64 | } 65 | else { 66 | if(!isSwitchPressed && !PIN_read(PIN_ENC_SW)) { // switch previously pressed? 67 | MOUSE_press(MOUSE_BUTTON_MIDDLE); // press mouse wheel button 68 | isSwitchPressed = 1; 69 | } 70 | else if(isSwitchPressed && PIN_read(PIN_ENC_SW)) { // switch previously released? 71 | MOUSE_release(MOUSE_BUTTON_MIDDLE); // release mouse wheel button 72 | isSwitchPressed = 0; // update switch state 73 | } 74 | } 75 | DLY_ms(1); // slow down a little 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /software/mousewheel_knob/src/usb.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // USB Constant and Structure Defines 3 | // =================================================================================== 4 | 5 | #pragma once 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #include 12 | 13 | // USB PID 14 | #ifndef USB_PID_SETUP 15 | #define USB_PID_NULL 0x00 // reserved PID 16 | #define USB_PID_SOF 0x05 17 | #define USB_PID_SETUP 0x0D 18 | #define USB_PID_IN 0x09 19 | #define USB_PID_OUT 0x01 20 | #define USB_PID_ACK 0x02 21 | #define USB_PID_NAK 0x0A 22 | #define USB_PID_STALL 0x0E 23 | #define USB_PID_DATA0 0x03 24 | #define USB_PID_DATA1 0x0B 25 | #define USB_PID_PRE 0x0C 26 | #endif 27 | 28 | // USB standard device request code 29 | #ifndef USB_GET_DESCRIPTOR 30 | #define USB_GET_STATUS 0x00 31 | #define USB_CLEAR_FEATURE 0x01 32 | #define USB_SET_FEATURE 0x03 33 | #define USB_SET_ADDRESS 0x05 34 | #define USB_GET_DESCRIPTOR 0x06 35 | #define USB_SET_DESCRIPTOR 0x07 36 | #define USB_GET_CONFIGURATION 0x08 37 | #define USB_SET_CONFIGURATION 0x09 38 | #define USB_GET_INTERFACE 0x0A 39 | #define USB_SET_INTERFACE 0x0B 40 | #define USB_SYNCH_FRAME 0x0C 41 | #endif 42 | 43 | // USB hub class request code 44 | #ifndef HUB_GET_DESCRIPTOR 45 | #define HUB_GET_STATUS 0x00 46 | #define HUB_CLEAR_FEATURE 0x01 47 | #define HUB_GET_STATE 0x02 48 | #define HUB_SET_FEATURE 0x03 49 | #define HUB_GET_DESCRIPTOR 0x06 50 | #define HUB_SET_DESCRIPTOR 0x07 51 | #endif 52 | 53 | // USB HID class request code 54 | #ifndef HID_GET_REPORT 55 | #define HID_GET_REPORT 0x01 56 | #define HID_GET_IDLE 0x02 57 | #define HID_GET_PROTOCOL 0x03 58 | #define HID_SET_REPORT 0x09 59 | #define HID_SET_IDLE 0x0A 60 | #define HID_SET_PROTOCOL 0x0B 61 | #endif 62 | 63 | // Bit define for USB request type 64 | #ifndef USB_REQ_TYP_MASK 65 | #define USB_REQ_TYP_IN 0x80 // control IN, device to host 66 | #define USB_REQ_TYP_OUT 0x00 // control OUT, host to device 67 | #define USB_REQ_TYP_READ 0x80 // control read, device to host 68 | #define USB_REQ_TYP_WRITE 0x00 // control write, host to device 69 | #define USB_REQ_TYP_MASK 0x60 // bit mask of request type 70 | #define USB_REQ_TYP_STANDARD 0x00 71 | #define USB_REQ_TYP_CLASS 0x20 72 | #define USB_REQ_TYP_VENDOR 0x40 73 | #define USB_REQ_TYP_RESERVED 0x60 74 | #define USB_REQ_RECIP_MASK 0x1F // bit mask of request recipient 75 | #define USB_REQ_RECIP_DEVICE 0x00 76 | #define USB_REQ_RECIP_INTERF 0x01 77 | #define USB_REQ_RECIP_ENDP 0x02 78 | #define USB_REQ_RECIP_OTHER 0x03 79 | #endif 80 | 81 | // USB request type for hub class request 82 | #ifndef HUB_GET_HUB_DESCRIPTOR 83 | #define HUB_CLEAR_HUB_FEATURE 0x20 84 | #define HUB_CLEAR_PORT_FEATURE 0x23 85 | #define HUB_GET_BUS_STATE 0xA3 86 | #define HUB_GET_HUB_DESCRIPTOR 0xA0 87 | #define HUB_GET_HUB_STATUS 0xA0 88 | #define HUB_GET_PORT_STATUS 0xA3 89 | #define HUB_SET_HUB_DESCRIPTOR 0x20 90 | #define HUB_SET_HUB_FEATURE 0x20 91 | #define HUB_SET_PORT_FEATURE 0x23 92 | #endif 93 | 94 | // Hub class feature selectors 95 | #ifndef HUB_PORT_RESET 96 | #define HUB_C_HUB_LOCAL_POWER 0 97 | #define HUB_C_HUB_OVER_CURRENT 1 98 | #define HUB_PORT_CONNECTION 0 99 | #define HUB_PORT_ENABLE 1 100 | #define HUB_PORT_SUSPEND 2 101 | #define HUB_PORT_OVER_CURRENT 3 102 | #define HUB_PORT_RESET 4 103 | #define HUB_PORT_POWER 8 104 | #define HUB_PORT_LOW_SPEED 9 105 | #define HUB_C_PORT_CONNECTION 16 106 | #define HUB_C_PORT_ENABLE 17 107 | #define HUB_C_PORT_SUSPEND 18 108 | #define HUB_C_PORT_OVER_CURRENT 19 109 | #define HUB_C_PORT_RESET 20 110 | #endif 111 | 112 | // USB descriptor type 113 | #ifndef USB_DESCR_TYP_DEVICE 114 | #define USB_DESCR_TYP_DEVICE 0x01 115 | #define USB_DESCR_TYP_CONFIG 0x02 116 | #define USB_DESCR_TYP_STRING 0x03 117 | #define USB_DESCR_TYP_INTERF 0x04 118 | #define USB_DESCR_TYP_ENDP 0x05 119 | #define USB_DESCR_TYP_QUALIF 0x06 120 | #define USB_DESCR_TYP_SPEED 0x07 121 | #define USB_DESCR_TYP_OTG 0x09 122 | #define USB_DESCR_TYP_IAD 0x0B 123 | #define USB_DESCR_TYP_HID 0x21 124 | #define USB_DESCR_TYP_REPORT 0x22 125 | #define USB_DESCR_TYP_PHYSIC 0x23 126 | #define USB_DESCR_TYP_CS_INTF 0x24 127 | #define USB_DESCR_TYP_CS_ENDP 0x25 128 | #define USB_DESCR_TYP_HUB 0x29 129 | #endif 130 | 131 | // USB device class 132 | #ifndef USB_DEV_CLASS_HUB 133 | #define USB_DEV_CLASS_RESERVED 0x00 134 | #define USB_DEV_CLASS_AUDIO 0x01 135 | #define USB_DEV_CLASS_COMM 0x02 136 | #define USB_DEV_CLASS_HID 0x03 137 | #define USB_DEV_CLASS_MONITOR 0x04 138 | #define USB_DEV_CLASS_PHYSIC_IF 0x05 139 | #define USB_DEV_CLASS_POWER 0x06 140 | #define USB_DEV_CLASS_PRINTER 0x07 141 | #define USB_DEV_CLASS_STORAGE 0x08 142 | #define USB_DEV_CLASS_HUB 0x09 143 | #define USB_DEV_CLASS_DATA 0x0A 144 | #define USB_DEV_CLASS_MISC 0xEF 145 | #define USB_DEV_CLASS_VENDOR 0xFF 146 | #endif 147 | 148 | // USB endpoint type and attributes 149 | #ifndef USB_ENDP_TYPE_MASK 150 | #define USB_ENDP_DIR_MASK 0x80 151 | #define USB_ENDP_ADDR_MASK 0x0F 152 | #define USB_ENDP_TYPE_MASK 0x03 153 | #define USB_ENDP_TYPE_CTRL 0x00 154 | #define USB_ENDP_TYPE_ISOCH 0x01 155 | #define USB_ENDP_TYPE_BULK 0x02 156 | #define USB_ENDP_TYPE_INTER 0x03 157 | #define USB_ENDP_ADDR_EP1_OUT 0x01 158 | #define USB_ENDP_ADDR_EP1_IN 0x81 159 | #define USB_ENDP_ADDR_EP2_OUT 0x02 160 | #define USB_ENDP_ADDR_EP2_IN 0x82 161 | #define USB_ENDP_ADDR_EP3_OUT 0x03 162 | #define USB_ENDP_ADDR_EP3_IN 0x83 163 | #define USB_ENDP_ADDR_EP4_OUT 0x04 164 | #define USB_ENDP_ADDR_EP4_IN 0x84 165 | #endif 166 | 167 | #ifndef MAX_PACKET_SIZE 168 | #define MAX_PACKET_SIZE 64 // maximum packet size 169 | #endif 170 | 171 | // USB descriptor type defines 172 | typedef struct __attribute__((packed)) { 173 | uint8_t bRequestType; 174 | uint8_t bRequest; 175 | uint8_t wValueL; 176 | uint8_t wValueH; 177 | uint8_t wIndexL; 178 | uint8_t wIndexH; 179 | uint8_t wLengthL; 180 | uint8_t wLengthH; 181 | } USB_SETUP_REQ, *PUSB_SETUP_REQ; 182 | 183 | typedef struct __attribute__((packed)) { 184 | uint8_t bLength; 185 | uint8_t bDescriptorType; 186 | uint16_t bcdUSB; 187 | uint8_t bDeviceClass; 188 | uint8_t bDeviceSubClass; 189 | uint8_t bDeviceProtocol; 190 | uint8_t bMaxPacketSize0; 191 | uint16_t idVendor; 192 | uint16_t idProduct; 193 | uint16_t bcdDevice; 194 | uint8_t iManufacturer; 195 | uint8_t iProduct; 196 | uint8_t iSerialNumber; 197 | uint8_t bNumConfigurations; 198 | } USB_DEV_DESCR, *PUSB_DEV_DESCR; 199 | 200 | typedef struct __attribute__((packed)) { 201 | uint8_t bLength; 202 | uint8_t bDescriptorType; 203 | uint16_t wTotalLength; 204 | uint8_t bNumInterfaces; 205 | uint8_t bConfigurationValue; 206 | uint8_t iConfiguration; 207 | uint8_t bmAttributes; 208 | uint8_t MaxPower; 209 | } USB_CFG_DESCR, *PUSB_CFG_DESCR; 210 | 211 | typedef struct __attribute__((packed)) { 212 | uint8_t bLength; 213 | uint8_t bDescriptorType; 214 | uint8_t bInterfaceNumber; 215 | uint8_t bAlternateSetting; 216 | uint8_t bNumEndpoints; 217 | uint8_t bInterfaceClass; 218 | uint8_t bInterfaceSubClass; 219 | uint8_t bInterfaceProtocol; 220 | uint8_t iInterface; 221 | } USB_ITF_DESCR, *PUSB_ITF_DESCR; 222 | 223 | typedef struct __attribute__((packed)) { 224 | uint8_t bLength; 225 | uint8_t bDescriptorType; 226 | uint8_t bFirstInterface; 227 | uint8_t bInterfaceCount; 228 | uint8_t bFunctionClass; 229 | uint8_t bFunctionSubClass; 230 | uint8_t bFunctionProtocol; 231 | uint8_t iFunction; 232 | } USB_IAD_DESCR, *PUSB_IAD_DESCR; 233 | 234 | typedef struct __attribute__((packed)) { 235 | uint8_t bLength; 236 | uint8_t bDescriptorType; 237 | uint8_t bEndpointAddress; 238 | uint8_t bmAttributes; 239 | uint16_t wMaxPacketSize; 240 | uint8_t bInterval; 241 | } USB_ENDP_DESCR; 242 | 243 | typedef struct __attribute__((packed)) { 244 | USB_CFG_DESCR cfg_descr; 245 | USB_ITF_DESCR itf_descr; 246 | USB_ENDP_DESCR endp_descr[1]; 247 | } USB_CFG_DESCR_LONG, *PUSB_CFG_DESCR_LONG; 248 | 249 | typedef struct __attribute__((packed)) { 250 | uint8_t bLength; 251 | uint8_t bDescriptorType; 252 | uint16_t bString[]; 253 | } USB_STR_DESCR, *PUSB_STR_DESCR; 254 | 255 | typedef struct __attribute__((packed)) { 256 | uint8_t bDescLength; 257 | uint8_t bDescriptorType; 258 | uint8_t bNbrPorts; 259 | uint16_t wHubCharacteristics; 260 | uint8_t bPwrOn2PwrGood; 261 | uint8_t bHubContrCurrent; 262 | uint8_t DeviceRemovable; 263 | uint8_t PortPwrCtrlMask; 264 | } USB_HUB_DESCR, *PUSB_HUB_DESCR; 265 | 266 | typedef struct __attribute__((packed)) { 267 | uint8_t bLength; 268 | uint8_t bDescriptorType; 269 | uint16_t bcdHID; 270 | uint8_t bCountryCode; 271 | uint8_t bNumDescriptors; 272 | uint8_t bDescriptorTypeX; 273 | uint16_t wDescriptorLength; 274 | } USB_HID_DESCR, *PUSB_HID_DESCR; 275 | 276 | typedef struct __attribute__((packed)) { 277 | uint8_t mCBW_Sig0; 278 | uint8_t mCBW_Sig1; 279 | uint8_t mCBW_Sig2; 280 | uint8_t mCBW_Sig3; 281 | uint8_t mCBW_Tag0; 282 | uint8_t mCBW_Tag1; 283 | uint8_t mCBW_Tag2; 284 | uint8_t mCBW_Tag3; 285 | uint8_t mCBW_DataLen0; 286 | uint8_t mCBW_DataLen1; 287 | uint8_t mCBW_DataLen2; 288 | uint8_t mCBW_DataLen3; 289 | uint8_t mCBW_Flag; 290 | uint8_t mCBW_LUN; 291 | uint8_t mCBW_CB_Len; 292 | uint8_t mCBW_CB_Buf[16]; 293 | } UDISK_BOC_CBW, *PUDISK_BOC_CBW; 294 | 295 | typedef struct __attribute__((packed)) { 296 | uint8_t mCSW_Sig0; 297 | uint8_t mCSW_Sig1; 298 | uint8_t mCSW_Sig2; 299 | uint8_t mCSW_Sig3; 300 | uint8_t mCSW_Tag0; 301 | uint8_t mCSW_Tag1; 302 | uint8_t mCSW_Tag2; 303 | uint8_t mCSW_Tag3; 304 | uint8_t mCSW_Residue0; 305 | uint8_t mCSW_Residue1; 306 | uint8_t mCSW_Residue2; 307 | uint8_t mCSW_Residue3; 308 | uint8_t mCSW_Status; 309 | } UDISK_BOC_CSW, *PUDISK_BOC_CSW; 310 | 311 | #ifdef __cplusplus 312 | } 313 | #endif 314 | -------------------------------------------------------------------------------- /software/mousewheel_knob/src/usb_descr.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // USB Descriptors and Definitions 3 | // =================================================================================== 4 | // 5 | // Definition of USB descriptors and endpoints. 6 | // 7 | // The following must be defined in config.h: 8 | // USB_VENDOR_ID - Vendor ID (16-bit word) 9 | // USB_PRODUCT_ID - Product ID (16-bit word) 10 | // USB_DEVICE_VERSION - Device version (16-bit BCD) 11 | // USB_LANGUAGE - Language descriptor code 12 | // USB_MAX_POWER_mA - Device max power in mA 13 | // All string descriptors. 14 | 15 | #pragma once 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | #include 22 | 23 | // =================================================================================== 24 | // RV003USB Defines 25 | // =================================================================================== 26 | //Defines the number of endpoints for this device. (Always add one for EP0). For two EPs, this should be 3. 27 | #define ENDPOINTS 2 28 | 29 | #define RV003USB_OPTIMIZE_FLASH 1 30 | #define RV003USB_HANDLE_IN_REQUEST 1 31 | #define RV003USB_OTHER_CONTROL 0 32 | #define RV003USB_HANDLE_USER_DATA 0 33 | #define RV003USB_HID_FEATURES 0 34 | 35 | #ifndef __ASSEMBLER__ 36 | #include 37 | 38 | // =================================================================================== 39 | // HID Report Descriptor 40 | // =================================================================================== 41 | static const uint8_t ReportDescr[] = { 42 | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 43 | 0x09, 0x02, // USAGE (Mouse) 44 | 0xa1, 0x01, // COLLECTION (Application) 45 | 0x09, 0x01, // USAGE (Pointer) 46 | 0xa1, 0x00, // COLLECTION (Physical) 47 | 0x05, 0x09, // USAGE_PAGE (Button) 48 | 0x19, 0x01, // USAGE_MINIMUM (Button 1) 49 | 0x29, 0x03, // USAGE_MAXIMUM (Button 3) 50 | 0x15, 0x00, // LOGICAL_MINIMUM (0) 51 | 0x25, 0x01, // LOGICAL_MAXIMUM (1) 52 | 0x75, 0x01, // REPORT_SIZE (1) 53 | 0x95, 0x03, // REPORT_COUNT (3) 54 | 0x81, 0x02, // INPUT (Data,Var,Abs) 55 | 0x75, 0x05, // REPORT_SIZE (5) 56 | 0x95, 0x01, // REPORT_COUNT (1) 57 | 0x81, 0x03, // INPUT (Cnst,Var,Abs) 58 | 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 59 | 0x09, 0x30, // USAGE (X) 60 | 0x09, 0x31, // USAGE (Y) 61 | 0x09, 0x38, // USAGE (Wheel) 62 | 0x15, 0x81, // LOGICAL_MINIMUM (-127) 63 | 0x25, 0x7f, // LOGICAL_MAXIMUM (127) 64 | 0x75, 0x08, // REPORT_SIZE (8) 65 | 0x95, 0x03, // REPORT_COUNT (3) 66 | 0x81, 0x06, // INPUT (Data,Var,Rel) 67 | 0xc0, // END_COLLECTION 68 | 0xc0 // END_COLLECTION 69 | }; 70 | 71 | // =================================================================================== 72 | // Device Descriptor 73 | // =================================================================================== 74 | static const USB_DEV_DESCR DevDescr = { 75 | .bLength = sizeof(DevDescr), // size of the descriptor in bytes: 18 76 | .bDescriptorType = USB_DESCR_TYP_DEVICE, // device descriptor: 0x01 77 | .bcdUSB = 0x0110, // USB specification: USB 1.1 78 | .bDeviceClass = 0, // interface will define class 79 | .bDeviceSubClass = 0, // unused 80 | .bDeviceProtocol = 0, // unused 81 | .bMaxPacketSize0 = 8, // maximum packet size for Endpoint 0 82 | .idVendor = USB_VENDOR_ID, // VID 83 | .idProduct = USB_PRODUCT_ID, // PID 84 | .bcdDevice = USB_DEVICE_VERSION, // device version 85 | .iManufacturer = 1, // index of Manufacturer String Descr 86 | .iProduct = 2, // index of Product String Descriptor 87 | .iSerialNumber = 3, // index of Serial Number String Descr 88 | .bNumConfigurations = 1 // number of possible configurations 89 | }; 90 | 91 | // =================================================================================== 92 | // Configuration Descriptor 93 | // =================================================================================== 94 | struct USB_CFG_DESCR_HID { 95 | USB_CFG_DESCR config; 96 | USB_ITF_DESCR interface0; 97 | USB_HID_DESCR hid0; 98 | USB_ENDP_DESCR ep1IN; 99 | }; 100 | 101 | static const struct USB_CFG_DESCR_HID CfgDescr = { 102 | // Configuration Descriptor 103 | .config = { 104 | .bLength = sizeof(USB_CFG_DESCR), // size of the descriptor in bytes 105 | .bDescriptorType = USB_DESCR_TYP_CONFIG, // configuration descriptor: 0x02 106 | .wTotalLength = sizeof(CfgDescr), // total length in bytes 107 | .bNumInterfaces = 1, // number of interfaces: 1 108 | .bConfigurationValue= 1, // value to select this configuration 109 | .iConfiguration = 0, // no configuration string descriptor 110 | .bmAttributes = 0x80, // attributes = bus powered, no wakeup 111 | .MaxPower = USB_MAX_POWER_mA / 2 // in 2mA units 112 | }, 113 | 114 | // Interface Descriptor 115 | .interface0 = { 116 | .bLength = sizeof(USB_ITF_DESCR), // size of the descriptor in bytes: 9 117 | .bDescriptorType = USB_DESCR_TYP_INTERF, // interface descriptor: 0x04 118 | .bInterfaceNumber = 0, // number of this interface: 0 119 | .bAlternateSetting = 0, // value used to select alternative setting 120 | .bNumEndpoints = 1, // number of endpoints used: 1 121 | .bInterfaceClass = USB_DEV_CLASS_HID, // interface class: HID (0x03) 122 | .bInterfaceSubClass = 1, // boot interface 123 | .bInterfaceProtocol = 2, // mouse 124 | .iInterface = 0 // interface string descriptor 125 | }, 126 | 127 | // HID Descriptor 128 | .hid0 = { 129 | .bLength = sizeof(USB_HID_DESCR), // size of the descriptor in bytes: 9 130 | .bDescriptorType = USB_DESCR_TYP_HID, // HID descriptor: 0x21 131 | .bcdHID = 0x0111, // HID class spec version (BCD: 1.11) 132 | .bCountryCode = 0, // not supported 133 | .bNumDescriptors = 1, // number of report descriptors: 1 134 | .bDescriptorTypeX = USB_DESCR_TYP_REPORT, // descriptor type: report (0x22) 135 | .wDescriptorLength = sizeof(ReportDescr) // report descriptor length 136 | }, 137 | 138 | // Endpoint Descriptor: Endpoint 1 (IN, Interrupt) 139 | .ep1IN = { 140 | .bLength = sizeof(USB_ENDP_DESCR), // size of the descriptor in bytes: 7 141 | .bDescriptorType = USB_DESCR_TYP_ENDP, // endpoint descriptor: 0x05 142 | .bEndpointAddress = USB_ENDP_ADDR_EP1_IN, // endpoint: 1, direction: IN (0x81) 143 | .bmAttributes = USB_ENDP_TYPE_INTER, // transfer type: interrupt (0x03) 144 | .wMaxPacketSize = 4, // max packet size 145 | .bInterval = 10 // polling intervall in ms 146 | } 147 | }; 148 | 149 | // =================================================================================== 150 | // String Descriptors 151 | // =================================================================================== 152 | #define __code const __attribute__((section(".rodata"))) 153 | #define USB_CHAR_GLUE(s) u##s 154 | #define USB_CHAR_SIZE(s) sizeof(USB_CHAR_GLUE(s)) 155 | #define USB_CHAR_TO_STR_DESCR(s) { \ 156 | .bLength = sizeof(USB_CHAR_GLUE(s)), \ 157 | .bDescriptorType = USB_DESCR_TYP_STRING, \ 158 | .bString = USB_CHAR_GLUE(s) \ 159 | } 160 | 161 | // Language Descriptor (Index 0) 162 | static __code USB_STR_DESCR string0 = { 163 | .bLength = 4, 164 | .bDescriptorType = USB_DESCR_TYP_STRING, 165 | .bString = {USB_LANGUAGE} 166 | }; 167 | 168 | // Manufacturer String Descriptor (Index 1) 169 | static __code USB_STR_DESCR string1 = USB_CHAR_TO_STR_DESCR(MANUF_STR); 170 | 171 | // Product String Descriptor (Index 2) 172 | static __code USB_STR_DESCR string2 = USB_CHAR_TO_STR_DESCR(PROD_STR); 173 | 174 | // Serial String Descriptor (Index 3) 175 | static __code USB_STR_DESCR string3 = USB_CHAR_TO_STR_DESCR(SERIAL_STR); 176 | 177 | // =================================================================================== 178 | // Descriptor Table 179 | // =================================================================================== 180 | // This table defines which descriptor data is sent for each specific 181 | // request from the host (in wValue and wIndex). 182 | struct descriptor_list_struct { 183 | uint32_t lIndexValue; 184 | const uint8_t *addr; 185 | uint8_t length; 186 | }; 187 | 188 | const static struct descriptor_list_struct descriptor_list[] = { 189 | {0x00000100, (const uint8_t *)&DevDescr, sizeof(DevDescr)}, 190 | {0x00000200, (const uint8_t *)&CfgDescr, sizeof(CfgDescr)}, 191 | {0x00002200, ReportDescr, sizeof(ReportDescr)}, 192 | {0x00000300, (const uint8_t *)&string0, 4}, 193 | {0x04090301, (const uint8_t *)&string1, USB_CHAR_SIZE(MANUF_STR)}, 194 | {0x04090302, (const uint8_t *)&string2, USB_CHAR_SIZE(PROD_STR)}, 195 | {0x04090303, (const uint8_t *)&string3, USB_CHAR_SIZE(SERIAL_STR)} 196 | }; 197 | 198 | #define DESCRIPTOR_LIST_ENTRIES ((sizeof(descriptor_list))/(sizeof(struct descriptor_list_struct)) ) 199 | 200 | #endif // __ASSEMBLER__ 201 | 202 | #ifdef __cplusplus 203 | } 204 | #endif 205 | -------------------------------------------------------------------------------- /software/mousewheel_knob/src/usb_handler.c: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Software USB Handler for CH32V003 * v1.0 * 3 | // =================================================================================== 4 | // 5 | // This file contains a copy of rv003usb.c (https://github.com/cnlohr/rv003usb), 6 | // copyright (c) 2023 CNLohr (MIT License), with some minor changes by Stefan Wagner. 7 | 8 | #include 9 | 10 | #define INSTANCE_DESCRIPTORS 1 11 | 12 | #include "usb_handler.h" 13 | 14 | #define ENDPOINT0_SIZE 8 //Fixed for USB 1.1, Low Speed. 15 | 16 | struct rv003usb_internal rv003usb_internal_data; 17 | 18 | #define LOCAL_CONCAT(A, B) A##B 19 | #define LOCAL_EXP(A, B) LOCAL_CONCAT(A,B) 20 | 21 | void usb_setup(void) { 22 | rv003usb_internal_data.se0_windup = 0; 23 | 24 | // Enable GPIOs, TIMERs 25 | RCC->APB2PCENR |= LOCAL_EXP(LOCAL_EXP(RCC_IOP, USB_PORT), EN) | RCC_AFIOEN; 26 | 27 | // GPIO Setup 28 | LOCAL_EXP( GPIO, USB_PORT )->CFGLR = 29 | ( LOCAL_EXP( GPIO, USB_PORT )->CFGLR & 30 | (~( ( ( 0xf << (USB_PIN_DP*4)) | ( 0xf << (USB_PIN_DM*4)) 31 | #ifdef USB_PIN_DPU 32 | | ( 0xf << (USB_PIN_DPU*4)) 33 | #endif 34 | ) )) ) 35 | | 36 | #ifdef USB_PIN_DPU 37 | (0b0011)<<(4*USB_PIN_DPU) | 38 | #endif 39 | (0b1000)<<(4*USB_PIN_DP) | 40 | (0b1000)<<(4*USB_PIN_DM); 41 | 42 | // Configure USB_PIN_DM (D-) as an interrupt on falling edge. 43 | AFIO->EXTICR = LOCAL_EXP(AFIO_EXTICR1_EXTI0_P,USB_PORT)<<(USB_PIN_DM*2); // Configure EXTI interrupt for USB_PIN_DM 44 | EXTI->INTENR = 1<FTENR = 1<BSHR = 1<current_endpoint = endp; 59 | struct usb_endpoint * e = &ist->eps[endp]; 60 | 61 | int tosend = 0; 62 | uint8_t * sendnow; 63 | int sendtok = e->toggle_in?0b01001011:0b11000011; 64 | 65 | #if RV003USB_HANDLE_IN_REQUEST 66 | if(e->custom || endp) { 67 | // Can re-use data-stack as scratchpad. 68 | sendnow = __builtin_assume_aligned(data, 4); 69 | usb_handle_user_in_request(e, sendnow, endp, sendtok, ist); 70 | return; 71 | } 72 | #endif 73 | 74 | tosend = 0; 75 | 76 | // Handle IN (sending data back to PC) 77 | // Do this down here. 78 | // We do this because we are required to have an in-endpoint. We don't 79 | // have to do anything with it, though. 80 | uint8_t * tsend = e->opaque; 81 | 82 | int offset = (e->count)<<3; 83 | tosend = (int)e->max_len - offset; 84 | if(tosend > ENDPOINT0_SIZE) tosend = ENDPOINT0_SIZE; 85 | sendnow = tsend + offset; 86 | if(tosend <= 0) usb_send_empty(sendtok); 87 | else usb_send_data(sendnow, tosend, 0, sendtok); 88 | } 89 | 90 | void usb_pid_handle_out(uint32_t addr, uint8_t * data, uint32_t endp, uint32_t unused, struct rv003usb_internal * ist) { 91 | ist->current_endpoint = endp; 92 | 93 | // We need to handle this here because we could have an interrupt in the middle of a control or big transfer. 94 | // This will correctly swap back the endpoint. 95 | } 96 | 97 | void usb_pid_handle_data(uint32_t this_token, uint8_t * data, uint32_t which_data, uint32_t length, struct rv003usb_internal * ist) { 98 | //Received data from host. 99 | int epno = ist->current_endpoint; 100 | struct usb_endpoint * e = &ist->eps[epno]; 101 | 102 | length -= 3; 103 | uint8_t * data_in = __builtin_assume_aligned( data, 4 ); 104 | 105 | // Alrady received this packet. 106 | if(e->toggle_out != which_data) goto just_ack; 107 | 108 | e->toggle_out = !e->toggle_out; 109 | 110 | #if RV003USB_HANDLE_USER_DATA 111 | if(epno || (!ist->setup_request && length > 3)) { 112 | usb_handle_user_data( e, epno, data_in, length, ist ); 113 | #if RV003USB_USER_DATA_HANDLES_TOKEN 114 | return; 115 | #endif 116 | } 117 | else 118 | #endif 119 | 120 | if(ist->setup_request) { 121 | // For receiving CONTROL-OUT messages into RAM. NOTE: MUST be ALIGNED to 4, and be allocated round_up( payload, 8 ) + 4 122 | // opaque points to [length received, but uninitialized before complete][data...] 123 | #if RV003USB_SUPPORT_CONTROL_OUT 124 | if(ist->setup_request == 2) { 125 | // This mode is where we record control-in data into a pointer and mark it as 126 | int offset = e->count << 3; 127 | uint32_t * base = __builtin_assume_aligned( e->opaque, 4 ); 128 | uint32_t * dout = __builtin_assume_aligned( ((uint8_t*)base) + offset + 4, 4 ); 129 | uint32_t * din = __builtin_assume_aligned( data_in, 4 ); 130 | if(offset < e->max_len) { 131 | dout[0] = din[0]; 132 | dout[1] = din[1]; 133 | e->count++; 134 | if(offset + 8 >= e->max_len) base[0] = e->max_len; 135 | } 136 | goto just_ack; 137 | } 138 | #endif 139 | 140 | struct usb_urb * s = __builtin_assume_aligned( (struct usb_urb *)(data_in), 4 ); 141 | 142 | uint32_t wvi = s->lValueLSBIndexMSB; 143 | uint32_t wLength = s->wLength; 144 | //Send just a data packet. 145 | e->count = 0; 146 | e->opaque = 0; 147 | e->custom = 0; 148 | e->max_len = 0; 149 | ist->setup_request = 0; 150 | 151 | //int bRequest = s->wRequestTypeLSBRequestMSB >> 8; 152 | 153 | // We shift down because we don't care if USB_RECIP_INTERFACE is set or not. 154 | // Otherwise we have to write extra code to handle each case if it's set or 155 | // not set, but in general, there's never a situation where we realy care. 156 | uint32_t reqShl = s->wRequestTypeLSBRequestMSB >> 1; 157 | 158 | //LogUEvent( 0, s->wRequestTypeLSBRequestMSB, wvi, s->wLength ); 159 | #if RV003USB_HID_FEATURES 160 | if(reqShl == (0x01a1>>1)) { 161 | // Class read request. 162 | // The host wants to read back from us. hid_get_feature_report 163 | usb_handle_hid_get_report_start( e, wLength, wvi ); 164 | } 165 | else if(reqShl == (0x0921>>1)) { 166 | // Class request (Will be writing) This is hid_send_feature_report 167 | usb_handle_hid_set_report_start( e, wLength, wvi ); 168 | } 169 | else 170 | #endif 171 | 172 | if(reqShl == (0x0680>>1)) { // GET_DESCRIPTOR = 6 (msb) 173 | int i; 174 | const struct descriptor_list_struct * dl; 175 | for(i = 0; i < DESCRIPTOR_LIST_ENTRIES; i++) { 176 | dl = &descriptor_list[i]; 177 | if(dl->lIndexValue == wvi) { 178 | e->opaque = (uint8_t*)dl->addr; 179 | uint16_t swLen = wLength; 180 | uint16_t elLen = dl->length; 181 | e->max_len = (swLen < elLen)?swLen:elLen; 182 | } 183 | } 184 | } 185 | else if(reqShl == (0x0500>>1)) { // SET_ADDRESS = 0x05 186 | ist->my_address = wvi; 187 | } 188 | 189 | // You could handle SET_CONFIGURATION == 0x0900 here if you wanted. 190 | // Can also handle GET_CONFIGURATION == 0x0880 to which we send back { 0x00 }, or the interface number. (But no one does this). 191 | // You could handle SET_INTERFACE == 0x1101 here if you wanted. 192 | // or 193 | // USB_REQ_GET_INTERFACE to which we would send 0x00, or the interface # 194 | else { 195 | #if RV003USB_OTHER_CONTROL 196 | usb_handle_other_control_message( e, s, ist ); 197 | #endif 198 | } 199 | } 200 | just_ack: 201 | { 202 | //Got the right data. Acknowledge. 203 | usb_send_data( 0, 0, 2, 0xD2 ); // Send ACK 204 | } 205 | return; 206 | } 207 | 208 | #if defined( RV003USB_OPTIMIZE_FLASH ) && RV003USB_OPTIMIZE_FLASH 209 | 210 | // Do not compile ACK commands. 211 | 212 | #else 213 | 214 | void usb_pid_handle_ack(uint32_t dummy, uint8_t * data, uint32_t dummy1, uint32_t dummy2, struct rv003usb_internal * ist) { 215 | struct usb_endpoint * e = &ist->eps[ist->current_endpoint]; 216 | e->toggle_in = !e->toggle_in; 217 | e->count++; 218 | } 219 | 220 | //Received a setup for a specific endpoint. 221 | void usb_pid_handle_setup(uint32_t addr, uint8_t * data, uint32_t endp, uint32_t unused, struct rv003usb_internal * ist) { 222 | struct usb_endpoint * e = &ist->eps[endp]; 223 | ist->current_endpoint = endp; 224 | ist->setup_request = 1; 225 | e->toggle_in = 1; 226 | e->toggle_out = 0; 227 | e->count = 0; 228 | e->opaque = 0; 229 | } 230 | 231 | #endif 232 | -------------------------------------------------------------------------------- /software/mousewheel_knob/src/usb_handler.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Software USB Handler for CH32V003 * v1.0 * 3 | // =================================================================================== 4 | // 5 | // This file contains a copy of rv003usb.h (https://github.com/cnlohr/rv003usb), 6 | // copyright (c) 2023 CNLohr (MIT License), with some minor changes by Stefan Wagner. 7 | 8 | #pragma once 9 | 10 | #include "usb_descr.h" 11 | 12 | // USB handler checks 13 | #ifndef __ASSEMBLER__ 14 | #include "system.h" 15 | #if SYS_USE_VECTORS == 0 16 | #error Interrupt vector table must be enabled (SYS_USE_VECTORS in system.h)! 17 | #endif 18 | #if SYS_TICK_INIT == 0 19 | #error SysTick must be enabled (SYS_TICK_INIT in system.h)! 20 | #endif 21 | #endif 22 | 23 | #define LOCAL_CONCAT_BASE(A, B) A##B##_BASE 24 | #define LOCAL_EXP_BASE(A, B) LOCAL_CONCAT_BASE(A,B) 25 | 26 | #define USB_GPIO_BASE LOCAL_EXP_BASE( GPIO, USB_PORT ) 27 | 28 | // Public stuff: 29 | 30 | /* Here are your options: 31 | #define RV003USB_HANDLE_IN_REQUEST 0 32 | #define RV003USB_OTHER_CONTROL 0 33 | #define RV003USB_HANDLE_USER_DATA 0 34 | #define RV003USB_HID_FEATURES 0 35 | #define RV003USB_SUPPORT_CONTROL_OUT 0 36 | #define RV003USB_CUSTOM_C 0 37 | #define RV003USB_USER_DATA_HANDLES_TOKEN 0 38 | */ 39 | 40 | #ifndef __ASSEMBLER__ 41 | 42 | extern uint32_t * always0; 43 | 44 | struct usb_endpoint; 45 | struct rv003usb_internal; 46 | struct usb_urb; 47 | 48 | // usb_hande_interrupt_in is OBLIGATED to call usb_send_data or usb_send_empty. 49 | // Enable with RV003USB_HANDLE_IN_REQUEST=1 50 | void usb_handle_user_in_request( struct usb_endpoint * e, uint8_t * scratchpad, int endp, uint32_t sendtok, struct rv003usb_internal * ist ); 51 | 52 | // Enable with RV003USB_HID_FEATURES=1 53 | void usb_handle_hid_set_report_start( struct usb_endpoint * e, int reqLen, uint32_t lValueLSBIndexMSB ); 54 | void usb_handle_hid_get_report_start( struct usb_endpoint * e, int reqLen, uint32_t lValueLSBIndexMSB ); 55 | 56 | // Enable with RV003USB_OTHER_CONTROL=1 57 | void usb_handle_other_control_message( struct usb_endpoint * e, struct usb_urb * s, struct rv003usb_internal * ist ); 58 | 59 | // Received data from the host which is not an internal control message, i.e. 60 | // this could be going to an endpoint or be data coming in for an unidentified 61 | // control message. 62 | // Enable with RV003USB_HANDLE_USER_DATA=1 63 | void usb_handle_user_data( struct usb_endpoint * e, int current_endpoint, uint8_t * data, int len, struct rv003usb_internal * ist ); 64 | 65 | // If you want to use custom functions for the level 2 stack, then say 66 | // RV003USB_CUSTOM_C 67 | // This is mostly useful on things like bootloaders. 68 | 69 | // Note: This checks addr & endp to make sure they are valid. 70 | void usb_pid_handle_setup( uint32_t addr, uint8_t * data, uint32_t endp, uint32_t unused, struct rv003usb_internal * ist ); 71 | void usb_pid_handle_in( uint32_t addr, uint8_t * data, uint32_t endp, uint32_t unused, struct rv003usb_internal * ist ); 72 | void usb_pid_handle_out( uint32_t addr, uint8_t * data, uint32_t endp, uint32_t unused, struct rv003usb_internal * ist ); 73 | void usb_pid_handle_data( uint32_t this_token, uint8_t * data, uint32_t which_data, uint32_t length, struct rv003usb_internal * ist ); 74 | void usb_pid_handle_ack( uint32_t dummy, uint8_t * data, uint32_t dummy2, uint32_t dummy3, struct rv003usb_internal * ist ); 75 | 76 | //poly_function = 0 to include CRC. 77 | //poly_function = 2 to exclude CRC. 78 | //This function is provided in assembly 79 | void usb_send_data( const void * data, uint32_t length, uint32_t poly_function, uint32_t token ); 80 | void usb_send_empty( uint32_t token ); 81 | 82 | void usb_setup(); 83 | 84 | #define LogUEvent( a, b, c, d ) 85 | #define GetUEvent() 0 86 | 87 | #endif // __ASSEMBLER__ 88 | 89 | 90 | // Internal stuff. 91 | 92 | // Packet Type + 8 + CRC + Buffer 93 | #define USB_BUFFER_SIZE 12 94 | 95 | #define USB_DMASK ((1<<(USB_PIN_DP)) | 1<<(USB_PIN_DM)) 96 | 97 | #ifdef RV003USB_OPTIMIZE_FLASH 98 | #define MY_ADDRESS_OFFSET_BYTES 4 99 | #define LAST_SE0_OFFSET 16 100 | #define DELTA_SE0_OFFSET 20 101 | #define SE0_WINDUP_OFFSET 24 102 | #define ENDP_OFFSET 28 103 | #define SETUP_REQUEST_OFFSET 8 104 | 105 | #define EP_COUNT_OFFSET 0 106 | #define EP_TOGGLE_IN_OFFSET 4 107 | #define EP_TOGGLE_OUT_OFFSET 8 108 | #define EP_IS_CUSTOM_OFFSET 12 109 | #define EP_MAX_LEN_OFFSET 16 110 | #define EP_OPAQUE_OFFSET 28 111 | #else 112 | #define MY_ADDRESS_OFFSET_BYTES 1 113 | #define LAST_SE0_OFFSET 4 114 | #define DELTA_SE0_OFFSET 8 115 | #define SE0_WINDUP_OFFSET 12 116 | #endif 117 | 118 | #ifndef __ASSEMBLER__ 119 | 120 | #define EMPTY_SEND_BUFFER (uint8_t*)1 121 | 122 | // This can be undone once support for fast-c.lbu / c.sbu is made available. 123 | #ifdef RV003USB_OPTIMIZE_FLASH 124 | #define TURBO8TYPE uint32_t 125 | #define TURBO16TYPE uint32_t 126 | #else 127 | #define TURBO8TYPE uint8_t 128 | #define TURBO16TYPE uint16_t 129 | #endif 130 | 131 | struct usb_endpoint { 132 | TURBO8TYPE count; // ack count / in count 133 | TURBO8TYPE toggle_in; // DATA0 or DATA1? 134 | TURBO8TYPE toggle_out; // Out PC->US 135 | TURBO8TYPE custom; // Anything nonzero will incur the custom call. 136 | TURBO16TYPE max_len; 137 | TURBO16TYPE reserved1; 138 | uint32_t reserved2; 139 | uint8_t * opaque; // For user. 140 | }; // CAREFUL! sizeof pacekt 141 | 142 | // Make the size of this a power of 2, otherwise it will be slow to access. 143 | #ifdef RV003USB_OPTIMIZE_FLASH 144 | _Static_assert( (sizeof(struct usb_endpoint) == 32), "usb_endpoint must be pow2 sized" ); 145 | #else 146 | _Static_assert( (sizeof(struct usb_endpoint) == 16), "usb_endpoint must be pow2 sized" ); 147 | #endif 148 | 149 | struct rv003usb_internal { 150 | TURBO8TYPE current_endpoint; // Can this be combined with setup_request? 151 | TURBO8TYPE my_address; // Will be 0 until set up. 152 | TURBO8TYPE setup_request; // 0 for non-setup request, 1 after setup token, is allowed to be 2 for control-out if RV003USB_SUPPORT_CONTROL_OUT is set. 153 | TURBO8TYPE reserved; 154 | uint32_t last_se0_cyccount; 155 | int32_t delta_se0_cyccount; 156 | uint32_t se0_windup; 157 | // 5 bytes + 6 * ENDPOINTS 158 | 159 | struct usb_endpoint eps[ENDPOINTS]; 160 | }; 161 | 162 | //Detailed analysis of some useful stuff and performance tweaking: http://naberius.de/2015/05/14/esp8266-gpio-output-performance/ 163 | //Reverse engineerd boot room can be helpful, too: http://cholla.mmto.org/esp8266/bootrom/boot.txt 164 | //USB Protocol read from wikipedia: https://en.wikipedia.org/wiki/USB 165 | // Neat stuff: http://www.usbmadesimple.co.uk/ums_3.htm 166 | // Neat stuff: http://www.beyondlogic.org/usbnutshell/usb1.shtml 167 | 168 | struct usb_urb { 169 | uint16_t wRequestTypeLSBRequestMSB; 170 | uint32_t lValueLSBIndexMSB; 171 | uint16_t wLength; 172 | } __attribute__((packed)); 173 | 174 | 175 | extern struct rv003usb_internal rv003usb_internal_data; 176 | 177 | #endif // __ASSEMBLER__ 178 | -------------------------------------------------------------------------------- /software/mousewheel_knob/src/usb_mouse.c: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // USB HID Standard Mouse Functions for CH32V003 * v1.0 * 3 | // =================================================================================== 4 | // 2023 by Stefan Wagner: https://github.com/wagiminator 5 | 6 | #include "usb_mouse.h" 7 | 8 | // =================================================================================== 9 | // Mouse HID Report 10 | // =================================================================================== 11 | 12 | // HID report typedef 13 | typedef struct { 14 | uint8_t buttons; // button states 15 | int8_t xmove; // relative movement on the x-axis 16 | int8_t ymove; // relative movement on the y-axis 17 | int8_t wmove; // relative movement of the wheel 18 | } HID_MOUSE_REPORT_TYPE; 19 | 20 | // Initialize HID report 21 | volatile HID_MOUSE_REPORT_TYPE MOUSE_report = { 22 | .buttons = 0, 23 | .xmove = 0, 24 | .ymove = 0, 25 | .wmove = 0 26 | }; 27 | 28 | // =================================================================================== 29 | // Mouse Functions 30 | // =================================================================================== 31 | 32 | // Press mouse button(s) 33 | void MOUSE_press(uint8_t buttons) { 34 | MOUSE_report.buttons |= buttons; // press button(s) 35 | } 36 | 37 | // Release mouse button(s) 38 | void MOUSE_release(uint8_t buttons) { 39 | MOUSE_report.buttons &= ~buttons; // release button(s) 40 | } 41 | 42 | // Move mouse pointer 43 | void MOUSE_move(int8_t xrel, int8_t yrel) { 44 | INT_ATOMIC_BLOCK { 45 | MOUSE_report.xmove += xrel; // set relative x-movement 46 | MOUSE_report.ymove += yrel; // set relative y-movement 47 | } 48 | } 49 | 50 | // Move mouse wheel 51 | void MOUSE_wheel(int8_t rel) { 52 | INT_ATOMIC_BLOCK { 53 | MOUSE_report.wmove += rel; // set relative wheel movement 54 | } 55 | } 56 | 57 | // =================================================================================== 58 | // RV003USB Software USB User Handle Functions 59 | // =================================================================================== 60 | void usb_handle_user_in_request(struct usb_endpoint * e, uint8_t * scratchpad, int endp, uint32_t sendtok, struct rv003usb_internal * ist) { 61 | 62 | // Mouse 63 | if(endp == 1) { 64 | usb_send_data((uint8_t*)&MOUSE_report, sizeof(MOUSE_report), 0, sendtok); 65 | MOUSE_report.xmove = 0; // reset movements 66 | MOUSE_report.ymove = 0; 67 | MOUSE_report.wmove = 0; 68 | } 69 | 70 | // Control transfer 71 | else usb_send_empty(sendtok); 72 | } 73 | -------------------------------------------------------------------------------- /software/mousewheel_knob/src/usb_mouse.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // USB HID Standard Mouse Functions for CH32V003 * v1.0 * 3 | // =================================================================================== 4 | // 5 | // Functions available: 6 | // -------------------- 7 | // MOUSE_init() init USB HID Mouse 8 | // MOUSE_press(b) press button(s) (see below) 9 | // MOUSE_release(b) release button(s) 10 | // MOUSE_move(x,y) move mouse pointer (relative) 11 | // MOUSE_wheel(w) move mouse wheel (relative) 12 | // MOUSE_wheel_up() move mouse wheel one step up 13 | // MOUSE_wheel_down() move mouse wheel one step down 14 | // 15 | // 2023 by Stefan Wagner: https://github.com/wagiminator 16 | 17 | #pragma once 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | #include 24 | #include "usb_handler.h" 25 | 26 | // Functions 27 | #define MOUSE_init usb_setup // init mouse 28 | void MOUSE_press(uint8_t buttons); // press button(s) 29 | void MOUSE_release(uint8_t buttons); // release button(s) 30 | void MOUSE_move(int8_t xrel, int8_t yrel); // move mouse pointer (relative) 31 | void MOUSE_wheel(int8_t rel); // move mouse wheel (relative) 32 | 33 | #define MOUSE_wheel_up() MOUSE_wheel( 1) // move mouse wheel one step up 34 | #define MOUSE_wheel_down() MOUSE_wheel(-1) // move mouse wheel one step down 35 | 36 | // Mouse buttons 37 | #define MOUSE_BUTTON_LEFT 0x01 // left mouse button 38 | #define MOUSE_BUTTON_RIGHT 0x02 // right mouse button 39 | #define MOUSE_BUTTON_MIDDLE 0x04 // middle mouse button 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif 44 | -------------------------------------------------------------------------------- /software/volume_knob/bin/volume_knob.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wagiminator/CH32V003-USB-Knob/bd0be4a51de5bf36abb6228fba8c7d0bc2adba6d/software/volume_knob/bin/volume_knob.bin -------------------------------------------------------------------------------- /software/volume_knob/bin/volume_knob.hex: -------------------------------------------------------------------------------- 1 | :100000006F00A01B00000000B8010000B801000054 2 | :1000100000000000000000000000000000000000E0 3 | :1000200000000000000000000000000000000000D0 4 | :10003000B801000000000000B8010000000000004E 5 | :10004000B8010000B8010000B8010000B8010000CC 6 | :100050004C030000B8010000B8010000B801000026 7 | :10006000B8010000B8010000B8010000B8010000AC 8 | :10007000B8010000B8010000B8010000B80100009C 9 | :10008000B8010000B8010000B8010000B80100008C 10 | :0C009000B8010000B8010000B801000039 11 | :10009C00B7F700E09C4737F700E03E951C47898F87 12 | :1000AC00E3CE07FE8280511122C2371401401C405E 13 | :1000BC0026C006C493F7F7F093E707081CC08947DE 14 | :1000CC001CC81840FD769387F60F85647D8F938747 15 | :1000DC0004805D8F18C0114610C81840B707F1FF97 16 | :1000EC00FD177D8FB70708005D8F18C0C1473165BC 17 | :1000FC001CC8130505B8693F23A0018237170240BD 18 | :10010C001C4FFD76BD0693E757001CCF8327048058 19 | :10011C00938404881146F58FC58F2320F480B7078C 20 | :10012C00014023A4070023A0C74023A6C7403707DC 21 | :10013C001000B7E700E023A0E7100144B70400204B 22 | :10014C00B71701409847098B21E398479387040020 23 | :10015C00118B0DCB255737550700A380E7001305EE 24 | :10016C0005303D37B707002093870700A3800700B1 25 | :10017C00371701401C47898BF5DF3165130505B82E 26 | :10018C00013F7DBF2957C1BF9C47C18B09E8F5F7DB 27 | :10019C00938704000957A380E7000544F9BFF1DFFA 28 | :1001AC0093870400A38007000144C1BF01A09307FB 29 | :1001BC00200B97010020938121641381010013050A 30 | :1001CC008008731005308D4573904580170500002D 31 | :1001DC00130585E24D8D7310553073901734B707A6 32 | :1001EC000020056793870700130787849386818017 33 | :1001FC0063EBD704938781801387418663EBE70415 34 | :10020C00B7270240054798C3B707000137170240CC 35 | :10021C00938717081CC3B70600021C43F58FF5DF44 36 | :10022C0089475CC3A146B7170240D843318BE31E04 37 | :10023C00D7FE37F700E0954614C3984F1367470372 38 | :10024C0098CF7300203010431107910723AEC7FEDF 39 | :10025C0045B7910723AE07FE55B793175600B30663 40 | :10026C00F70010C38C529306B00499E19306300C3E 41 | :10027C00BA979C57D18F99CB85476314F6023705F3 42 | :10028C00002001468D451305050041A6484F1C5F13 43 | :10029C004C570E05898D3E95A14763C6B700634543 44 | :1002AC00B0003685A1AEA1450146C5B71C439607E3 45 | :1002BC00BA97D453639AC60693B61600D4D3144790 46 | :1002CC00A5C683D6450003D6250003D56500C20616 47 | :1002DC00D18E03D6050023AE070023AC070223A45E 48 | :1002EC00070223A607022324070005829305003486 49 | :1002FC006312B60401471307407F930547052A8311 50 | :10030C0010436310D602504390DF034687009312CC 51 | :10031C00060193D20201637355001A86420641828C 52 | :10032C00D0D73107E31EB7FC9306200D0946814553 53 | :10033C000145E1AC93070028E318F6FE54C3EDB772 54 | :10034C005D712AC03ECAB717014093870780884762 55 | :10035C0019892EC232C436C63AC826CE12D83732C4 56 | :10036C000140130282C58C4799896309052288478D 57 | :10037C001989631BB502884719896317B502884729 58 | :10038C0019896313B50288471989631FB50088471B 59 | :10039C001989631BB500884719896317B50088470D 60 | :1003AC0019896313B50009A022CC014616D01AD2C4 61 | :1003BC002320020088471989630105182D8DA98D0A 62 | :1003CC0009A009C909A0804719882D8C11E009A042 63 | :1003DC00C5B701001ED406DAA1441944010001007E 64 | :1003EC00C166FD16296705079303F1039302000804 65 | :1003FC00010001002320020088471989630F0512B0 66 | :10040C002D8DA98D0100133515000606498E7D151D 67 | :10041C00498C1D887D14FD14E1F4930400061375BA 68 | :10042C00C60009C55147F946130000002380C300DC 69 | :10043C009383130023200200884719892D8D0DC545 70 | :10044C00A98D09A01944BDC91395F6017D85858236 71 | :10045C00798DA98E0582FD1413F5740061D9010004 72 | :10046C0001000100E1F8D1A87D1413F516007D15EB 73 | :10047C00798D8582A98E05821366060811C8FD1434 74 | :10048C0013F5740045D5010001000100C5F475A0F9 75 | :10049C00FD1413F5740009E52380C3009383130046 76 | :1004AC00232002008847198911C92D8DA98D51C5AA 77 | :1004BC00194401000D457D157DFDADFC13F574004F 78 | :1004CC002DED9305F103882185059700000093809D 79 | :1004DC000007930755FB425217070020130747B23A 80 | :1004EC00C9C785EA92211375F6071D823D8A094416 81 | :1004FC006375860401C540236311A404938747FCEC 82 | :10050C00C1CFC517639407006FF03FD58917B5CBE2 83 | :10051C002DA02D640504818E8DE2B386B340850633 84 | :10052C00938787F80146639407006FF03FD8C5178F 85 | :10053C000546639407006FF07FD76244F244825201 86 | :10054C001253A253D250F24442522246B246424770 87 | :10055C00924509A0B707014093874741114588C3CD 88 | :10056C000245D247616173002030104316063A965B 89 | :10057C007106484285452D8D48C20842050508C2C2 90 | :10058C006DBF10C385450CC716063A960CD2814533 91 | :10059C004CCE0CDE4CD255B710A345B737F500E066 92 | :1005AC002105170700201307A7A50C4B104110CBF2 93 | :1005BC000D8E50CBB165938505B80D8E85679387ED 94 | :1005CC0007FAE352F6F8FD7793870706E34DF6F644 95 | :1005DC000C4FB2950CCFA5D93717024008431356D0 96 | :1005EC0035007D8A930770F07D8DB305B04013D62E 97 | :1005FC00954041060E06518D08C3B1B7AA86170562 98 | :10060C0000001305251389452E86411122C026C2F0 99 | :10061C00B7170140938707809843FD74BD04658F1D 100 | :10062C0093040022458FB7040400890484CB98C33B 101 | :10063C0037030600190312C49E0613E40604814214 102 | :10064C0009E6A962850241667D168E05AE833732B6 103 | :10065C000140130282C51947BD4509A0A286058039 104 | :10066C00858A7D1781E6B3C46400194784CBFD15D8 105 | :10067C0091C58D46FD16FDFE0100CDB79E85ADC51D 106 | :10068C00FD156394020041667D16010000210505ED 107 | :10069C00A286858A81CE058093761600FD16B3F668 108 | :1006AC0056000582358E7D1735CB29A8058093160B 109 | :1006BC00F601FD86B3C4640084CB19470582B3F6FA 110 | :1006CC005600358E81C993F67500FD15E1D209A04F 111 | :1006DC00C1B70100638A020093D282009D45E38971 112 | :1006EC0002FA1344F6FF6DB78D46FD16FDFEB704F6 113 | :1006FC00060084CBA146FD16FDFEB7040200910452 114 | :10070C0084CB84439306F0CCF58C93060044D58CB3 115 | :10071C0084C3024492442242410182809146FD16D8 116 | :10072C00FDFEB3C4640019470100010084CB59BF1E 117 | :10073C00000000001803430048003300320056004C 118 | :10074C003000300033004800490044000000000035 119 | :10075C002003550053004200200056006F006C002F 120 | :10076C0075006D00650020004B006E006F0062008C 121 | :10077C0000000000180377006100670069006D003D 122 | :10078C0069006E00610074006F00720000000000D0 123 | :10079C000403090409022200010100801909040064 124 | :1007AC000001030101000921100121012219000798 125 | :1007BC000581030400010000120110010000000873 126 | :1007CC00091203C00001010203010000050C09011C 127 | :1007DC00A101850119002A3C021500263C02950155 128 | :1007EC0075108100C000000000010000C40700006B 129 | :1007FC001200000000020000A00700002200000010 130 | :10080C0000220000D80700001900000000030000BF 131 | :10081C009C0700000400000001030904800700008D 132 | :10082C0018000000020309045C070000200000000F 133 | :0C083C000303090440070000180000003E 134 | :080848000100000000000000A7 135 | :00000001FF 136 | -------------------------------------------------------------------------------- /software/volume_knob/config.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // User Configurations 3 | // =================================================================================== 4 | 5 | #pragma once 6 | 7 | // Rotary encoder in definitions 8 | #define PIN_ENC_A PC1 // pin connected to rotary encoder A 9 | #define PIN_ENC_B PC2 // pin connected to rotary encoder B 10 | #define PIN_ENC_SW PC4 // pin connected to rotary encoder switch 11 | 12 | // USB pin definitions 13 | #define USB_PORT A // [A,C,D] GPIO Port to use with D+ and D- 14 | #define USB_PIN_DP 1 // [0-4] GPIO Number for USB D+ Pin 15 | #define USB_PIN_DM 2 // [0-4] GPIO Number for USB D- Pin 16 | 17 | // USB configuration descriptor 18 | #define USB_MAX_POWER_mA 50 // max power in mA 19 | 20 | // USB device descriptor 21 | #define USB_VENDOR_ID 0x1209 // VID 22 | #define USB_PRODUCT_ID 0xc003 // PID 23 | #define USB_DEVICE_VERSION 0x0100 // v1.0 (BCD-format) 24 | #define USB_LANGUAGE 0x0409 // US English 25 | 26 | // USB descriptor strings 27 | #define MANUF_STR "wagiminator" 28 | #define PROD_STR "USB Volume Knob" 29 | #define SERIAL_STR "CH32V003HID" 30 | -------------------------------------------------------------------------------- /software/volume_knob/ld/ch32v003.ld: -------------------------------------------------------------------------------- 1 | ENTRY( jump_reset ) 2 | 3 | MEMORY 4 | { 5 | FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 16K 6 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 2K 7 | } 8 | 9 | SECTIONS 10 | { 11 | .init : 12 | { 13 | _sinit = .; 14 | . = ALIGN(4); 15 | KEEP(*(SORT_NONE(.init.jump))) 16 | KEEP(*(SORT_NONE(.init.vectors))) 17 | . = ALIGN(4); 18 | _einit = .; 19 | } >FLASH AT>FLASH 20 | 21 | .text : 22 | { 23 | . = ALIGN(4); 24 | *(.text) 25 | *(.text.*) 26 | *(.rodata) 27 | *(.rodata*) 28 | *(.gnu.linkonce.t.*) 29 | . = ALIGN(4); 30 | } >FLASH AT>FLASH 31 | 32 | .fini : 33 | { 34 | KEEP(*(SORT_NONE(.fini))) 35 | . = ALIGN(4); 36 | } >FLASH AT>FLASH 37 | 38 | PROVIDE(_etext = .); 39 | PROVIDE(_eitcm = .); 40 | 41 | .preinit_array : 42 | { 43 | PROVIDE_HIDDEN(__preinit_array_start = .); 44 | KEEP(*(.preinit_array)) 45 | PROVIDE_HIDDEN(__preinit_array_end = .); 46 | } >FLASH AT>FLASH 47 | 48 | .init_array : 49 | { 50 | PROVIDE_HIDDEN(__init_array_start = .); 51 | KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*)SORT_BY_INIT_PRIORITY(.ctors.*))) 52 | KEEP(*(.init_array EXCLUDE_FILE(*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o) .ctors)) 53 | PROVIDE_HIDDEN(__init_array_end = .); 54 | } >FLASH AT>FLASH 55 | 56 | .fini_array : 57 | { 58 | PROVIDE_HIDDEN(__fini_array_start = .); 59 | KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) 60 | KEEP(*(.fini_array EXCLUDE_FILE(*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o) .dtors)) 61 | PROVIDE_HIDDEN(__fini_array_end = .); 62 | } >FLASH AT>FLASH 63 | 64 | .ctors : 65 | { 66 | KEEP(*crtbegin.o(.ctors)) 67 | KEEP(*crtbegin?.o(.ctors)) 68 | KEEP(*(EXCLUDE_FILE(*crtend.o *crtend?.o) .ctors)) 69 | KEEP(*(SORT(.ctors.*))) 70 | KEEP(*(.ctors)) 71 | } >FLASH AT>FLASH 72 | 73 | .dtors : 74 | { 75 | KEEP(*crtbegin.o(.dtors)) 76 | KEEP(*crtbegin?.o(.dtors)) 77 | KEEP(*(EXCLUDE_FILE(*crtend.o *crtend?.o) .dtors)) 78 | KEEP(*(SORT(.dtors.*))) 79 | KEEP(*(.dtors)) 80 | } >FLASH AT>FLASH 81 | 82 | .dalign : 83 | { 84 | . = ALIGN(4); 85 | PROVIDE(_data_vma = .); 86 | } >RAM AT>FLASH 87 | 88 | .dlalign : 89 | { 90 | . = ALIGN(4); 91 | PROVIDE(_data_lma = .); 92 | } >FLASH AT>FLASH 93 | 94 | .data : 95 | { 96 | . = ALIGN(4); 97 | *(.gnu.linkonce.r.*) 98 | *(.data .data.*) 99 | *(.gnu.linkonce.d.*) 100 | . = ALIGN(8); 101 | PROVIDE(__global_pointer$ = . + 0x800); 102 | *(.sdata .sdata.*) 103 | *(.sdata2*) 104 | *(.gnu.linkonce.s.*) 105 | . = ALIGN(8); 106 | *(.srodata.cst16) 107 | *(.srodata.cst8) 108 | *(.srodata.cst4) 109 | *(.srodata.cst2) 110 | *(.srodata .srodata.*) 111 | . = ALIGN(4); 112 | PROVIDE(_edata = .); 113 | } >RAM AT>FLASH 114 | 115 | .bss : 116 | { 117 | . = ALIGN(4); 118 | PROVIDE(_sbss = .); 119 | *(.sbss*) 120 | *(.gnu.linkonce.sb.*) 121 | *(.bss*) 122 | *(.gnu.linkonce.b.*) 123 | *(COMMON*) 124 | . = ALIGN(4); 125 | PROVIDE(_ebss = .); 126 | } >RAM AT>FLASH 127 | 128 | PROVIDE(_end = _ebss); 129 | PROVIDE(end = . ); 130 | PROVIDE(_eusrstack = ORIGIN(RAM) + LENGTH(RAM)); 131 | } 132 | -------------------------------------------------------------------------------- /software/volume_knob/makefile: -------------------------------------------------------------------------------- 1 | # =================================================================================== 2 | # Project Makefile 3 | # =================================================================================== 4 | # Project: USB Rotary Encoder for CH32V003 - Volume Control 5 | # Author: Stefan Wagner 6 | # Year: 2024 7 | # URL: https://github.com/wagiminator 8 | # =================================================================================== 9 | # Install toolchain: 10 | # sudo apt install build-essential libnewlib-dev gcc-riscv64-unknown-elf 11 | # sudo apt install python3 python3-pip 12 | # pip install rvprog 13 | # 14 | # Provide access permission to WCH-LinkE programmer: 15 | # echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="8010", MODE="666"' | sudo tee /etc/udev/rules.d/99-WCH-LinkE.rules 16 | # echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="8012", MODE="666"' | sudo tee -a /etc/udev/rules.d/99-WCH-LinkE.rules 17 | # sudo udevadm control --reload-rules 18 | # 19 | # Connect WCH-LinkE programmer to your board. Type "make flash" in the command line. 20 | # =================================================================================== 21 | 22 | # Files and Folders 23 | TARGET = volume_knob 24 | INCLUDE = include 25 | SOURCE = src 26 | BIN = bin 27 | 28 | # Microcontroller Settings 29 | F_CPU = 48000000 30 | LDSCRIPT = ld/ch32v003.ld 31 | CPUARCH = -march=rv32ec -mabi=ilp32e 32 | 33 | # Toolchain 34 | PREFIX = riscv64-unknown-elf 35 | CC = $(PREFIX)-gcc 36 | OBJCOPY = $(PREFIX)-objcopy 37 | OBJDUMP = $(PREFIX)-objdump 38 | OBJSIZE = $(PREFIX)-size 39 | NEWLIB = /usr/include/newlib 40 | ISPTOOL = rvprog -f $(BIN)/$(TARGET).bin 41 | CLEAN = rm -f *.lst *.obj *.cof *.list *.map *.eep.hex *.o *.d 42 | 43 | # Compiler Flags 44 | CFLAGS = -g -Os -flto -ffunction-sections -fdata-sections -fno-builtin -nostdlib 45 | CFLAGS += $(CPUARCH) -DF_CPU=$(F_CPU) -I$(NEWLIB) -I$(INCLUDE) -I$(SOURCE) -I. -Wall 46 | LDFLAGS = -T$(LDSCRIPT) -lgcc -Wl,--gc-sections,--build-id=none 47 | CFILES = $(wildcard ./*.c) $(wildcard $(SOURCE)/*.c) $(wildcard $(SOURCE)/*.S) 48 | 49 | # Symbolic Targets 50 | help: 51 | @echo "Use the following commands:" 52 | @echo "make all compile and build $(TARGET).elf/.bin/.hex/.asm" 53 | @echo "make hex compile and build $(TARGET).hex" 54 | @echo "make asm compile and disassemble to $(TARGET).asm" 55 | @echo "make bin compile and build $(TARGET).bin" 56 | @echo "make flash compile and upload to MCU" 57 | @echo "make clean remove all build files" 58 | 59 | $(BIN)/$(TARGET).elf: $(CFILES) 60 | @echo "Building $(BIN)/$(TARGET).elf ..." 61 | @mkdir -p $(BIN) 62 | @$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) 63 | 64 | $(BIN)/$(TARGET).lst: $(BIN)/$(TARGET).elf 65 | @echo "Building $(BIN)/$(TARGET).lst ..." 66 | @$(OBJDUMP) -S $^ > $(BIN)/$(TARGET).lst 67 | 68 | $(BIN)/$(TARGET).map: $(BIN)/$(TARGET).elf 69 | @echo "Building $(BIN)/$(TARGET).map ..." 70 | @$(OBJDUMP) -t $^ > $(BIN)/$(TARGET).map 71 | 72 | $(BIN)/$(TARGET).bin: $(BIN)/$(TARGET).elf 73 | @echo "Building $(BIN)/$(TARGET).bin ..." 74 | @$(OBJCOPY) -O binary $< $(BIN)/$(TARGET).bin 75 | 76 | $(BIN)/$(TARGET).hex: $(BIN)/$(TARGET).elf 77 | @echo "Building $(BIN)/$(TARGET).hex ..." 78 | @$(OBJCOPY) -O ihex $< $(BIN)/$(TARGET).hex 79 | 80 | $(BIN)/$(TARGET).asm: $(BIN)/$(TARGET).elf 81 | @echo "Disassembling to $(BIN)/$(TARGET).asm ..." 82 | @$(OBJDUMP) -d $(BIN)/$(TARGET).elf > $(BIN)/$(TARGET).asm 83 | 84 | all: $(BIN)/$(TARGET).lst $(BIN)/$(TARGET).map $(BIN)/$(TARGET).bin $(BIN)/$(TARGET).hex $(BIN)/$(TARGET).asm size 85 | 86 | elf: $(BIN)/$(TARGET).elf removetemp size 87 | 88 | bin: $(BIN)/$(TARGET).bin removetemp size removeelf 89 | 90 | hex: $(BIN)/$(TARGET).hex removetemp size removeelf 91 | 92 | asm: $(BIN)/$(TARGET).asm removetemp size removeelf 93 | 94 | flash: $(BIN)/$(TARGET).bin size removeelf 95 | @echo "Uploading to MCU ..." 96 | @$(ISPTOOL) 97 | 98 | clean: 99 | @echo "Cleaning all up ..." 100 | @$(CLEAN) 101 | @rm -f $(BIN)/$(TARGET).elf $(BIN)/$(TARGET).lst $(BIN)/$(TARGET).map $(BIN)/$(TARGET).bin $(BIN)/$(TARGET).hex $(BIN)/$(TARGET).asm 102 | 103 | size: 104 | @echo "------------------" 105 | @echo "FLASH: $(shell $(OBJSIZE) -d $(BIN)/$(TARGET).elf | awk '/[0-9]/ {print $$1 + $$2}') bytes" 106 | @echo "SRAM: $(shell $(OBJSIZE) -d $(BIN)/$(TARGET).elf | awk '/[0-9]/ {print $$2 + $$3}') bytes" 107 | @echo "------------------" 108 | 109 | removetemp: 110 | @echo "Removing temporary files ..." 111 | @$(CLEAN) 112 | 113 | removeelf: 114 | @echo "Removing $(BIN)/$(TARGET).elf ..." 115 | @rm -f $(BIN)/$(TARGET).elf 116 | -------------------------------------------------------------------------------- /software/volume_knob/src/main.c: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Project: USB Rotary Encoder for CH32V003 - Volume Control 3 | // Version: v1.0 4 | // Year: 2024 5 | // Author: Stefan Wagner 6 | // Github: https://github.com/wagiminator 7 | // EasyEDA: https://easyeda.com/wagiminator 8 | // License: http://creativecommons.org/licenses/by-sa/3.0/ 9 | // =================================================================================== 10 | // 11 | // Description: 12 | // ------------ 13 | // Controls PC volume via rotary encoder. 14 | // 15 | // References: 16 | // ----------- 17 | // - CNLohr ch32v003fun: https://github.com/cnlohr/ch32v003fun 18 | // - CNLohr rv003usb: https://github.com/cnlohr/rv003usb 19 | // - WCH Nanjing Qinheng Microelectronics: http://wch.cn 20 | // 21 | // Compilation Instructions: 22 | // ------------------------- 23 | // - Make sure GCC toolchain (gcc-riscv64-unknown-elf, newlib) and Python3 with rvprog 24 | // are installed. In addition, Linux requires access rights to WCH-LinkE programmer. 25 | // - Connect the WCH-LinkE programmer to the MCU board. 26 | // - Run 'make flash'. 27 | // 28 | // Operating Instructions: 29 | // ----------------------- 30 | // - Connect a rotary encoder to the MCU pins defined in config.h (active low). 31 | // - Connect the board via USB to your PC. It should be detected as a HID device. 32 | // - Turn the rotary encoder to increase/decrease volume. 33 | // - Press rotary encoder button to mute/unmute. 34 | 35 | 36 | // =================================================================================== 37 | // Libraries, Definitions and Macros 38 | // =================================================================================== 39 | #include // user configurations 40 | #include // system functions 41 | #include // GPIO functions 42 | #include // USB consumer multimedia keyboard 43 | 44 | // =================================================================================== 45 | // Main Function 46 | // =================================================================================== 47 | int main(void) { 48 | // Variables 49 | uint8_t isSwitchPressed = 0; // state of rotary encoder switch 50 | 51 | // Setup 52 | PIN_input_PU(PIN_ENC_A); // set encoder pins to input pullup 53 | PIN_input_PU(PIN_ENC_B); 54 | PIN_input_PU(PIN_ENC_SW); 55 | 56 | // Init USB keyboard 57 | DLY_ms(1); // wait a bit for USB 58 | CON_init(); // init USB HID consumer keyboard 59 | 60 | // Loop 61 | while(1) { 62 | if(!PIN_read(PIN_ENC_A)) { // encoder turned ? 63 | if(PIN_read(PIN_ENC_B)) { // clockwise? 64 | CON_press(CON_VOL_UP); // -> press volume up 65 | } 66 | else { // counter-clockwise? 67 | CON_press(CON_VOL_DOWN); // -> press volume down 68 | } 69 | DLY_ms(10); // debounce/USP poll wait 70 | CON_release(); // release key 71 | while(!PIN_read(PIN_ENC_A)); // wait until next detent 72 | } 73 | else { 74 | if(!isSwitchPressed && !PIN_read(PIN_ENC_SW)) { // switch previously pressed? 75 | CON_press(CON_VOL_MUTE); // press volume mute key 76 | isSwitchPressed = 1; // update switch state 77 | } 78 | else if(isSwitchPressed && PIN_read(PIN_ENC_SW)) { // switch previously released? 79 | CON_release(); // release volume mute key 80 | isSwitchPressed = 0; // update switch state 81 | } 82 | } 83 | DLY_ms(1); // debounce/USP poll wait 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /software/volume_knob/src/usb.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // USB Constant and Structure Defines 3 | // =================================================================================== 4 | 5 | #pragma once 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #include 12 | 13 | // USB PID 14 | #ifndef USB_PID_SETUP 15 | #define USB_PID_NULL 0x00 // reserved PID 16 | #define USB_PID_SOF 0x05 17 | #define USB_PID_SETUP 0x0D 18 | #define USB_PID_IN 0x09 19 | #define USB_PID_OUT 0x01 20 | #define USB_PID_ACK 0x02 21 | #define USB_PID_NAK 0x0A 22 | #define USB_PID_STALL 0x0E 23 | #define USB_PID_DATA0 0x03 24 | #define USB_PID_DATA1 0x0B 25 | #define USB_PID_PRE 0x0C 26 | #endif 27 | 28 | // USB standard device request code 29 | #ifndef USB_GET_DESCRIPTOR 30 | #define USB_GET_STATUS 0x00 31 | #define USB_CLEAR_FEATURE 0x01 32 | #define USB_SET_FEATURE 0x03 33 | #define USB_SET_ADDRESS 0x05 34 | #define USB_GET_DESCRIPTOR 0x06 35 | #define USB_SET_DESCRIPTOR 0x07 36 | #define USB_GET_CONFIGURATION 0x08 37 | #define USB_SET_CONFIGURATION 0x09 38 | #define USB_GET_INTERFACE 0x0A 39 | #define USB_SET_INTERFACE 0x0B 40 | #define USB_SYNCH_FRAME 0x0C 41 | #endif 42 | 43 | // USB hub class request code 44 | #ifndef HUB_GET_DESCRIPTOR 45 | #define HUB_GET_STATUS 0x00 46 | #define HUB_CLEAR_FEATURE 0x01 47 | #define HUB_GET_STATE 0x02 48 | #define HUB_SET_FEATURE 0x03 49 | #define HUB_GET_DESCRIPTOR 0x06 50 | #define HUB_SET_DESCRIPTOR 0x07 51 | #endif 52 | 53 | // USB HID class request code 54 | #ifndef HID_GET_REPORT 55 | #define HID_GET_REPORT 0x01 56 | #define HID_GET_IDLE 0x02 57 | #define HID_GET_PROTOCOL 0x03 58 | #define HID_SET_REPORT 0x09 59 | #define HID_SET_IDLE 0x0A 60 | #define HID_SET_PROTOCOL 0x0B 61 | #endif 62 | 63 | // Bit define for USB request type 64 | #ifndef USB_REQ_TYP_MASK 65 | #define USB_REQ_TYP_IN 0x80 // control IN, device to host 66 | #define USB_REQ_TYP_OUT 0x00 // control OUT, host to device 67 | #define USB_REQ_TYP_READ 0x80 // control read, device to host 68 | #define USB_REQ_TYP_WRITE 0x00 // control write, host to device 69 | #define USB_REQ_TYP_MASK 0x60 // bit mask of request type 70 | #define USB_REQ_TYP_STANDARD 0x00 71 | #define USB_REQ_TYP_CLASS 0x20 72 | #define USB_REQ_TYP_VENDOR 0x40 73 | #define USB_REQ_TYP_RESERVED 0x60 74 | #define USB_REQ_RECIP_MASK 0x1F // bit mask of request recipient 75 | #define USB_REQ_RECIP_DEVICE 0x00 76 | #define USB_REQ_RECIP_INTERF 0x01 77 | #define USB_REQ_RECIP_ENDP 0x02 78 | #define USB_REQ_RECIP_OTHER 0x03 79 | #endif 80 | 81 | // USB request type for hub class request 82 | #ifndef HUB_GET_HUB_DESCRIPTOR 83 | #define HUB_CLEAR_HUB_FEATURE 0x20 84 | #define HUB_CLEAR_PORT_FEATURE 0x23 85 | #define HUB_GET_BUS_STATE 0xA3 86 | #define HUB_GET_HUB_DESCRIPTOR 0xA0 87 | #define HUB_GET_HUB_STATUS 0xA0 88 | #define HUB_GET_PORT_STATUS 0xA3 89 | #define HUB_SET_HUB_DESCRIPTOR 0x20 90 | #define HUB_SET_HUB_FEATURE 0x20 91 | #define HUB_SET_PORT_FEATURE 0x23 92 | #endif 93 | 94 | // Hub class feature selectors 95 | #ifndef HUB_PORT_RESET 96 | #define HUB_C_HUB_LOCAL_POWER 0 97 | #define HUB_C_HUB_OVER_CURRENT 1 98 | #define HUB_PORT_CONNECTION 0 99 | #define HUB_PORT_ENABLE 1 100 | #define HUB_PORT_SUSPEND 2 101 | #define HUB_PORT_OVER_CURRENT 3 102 | #define HUB_PORT_RESET 4 103 | #define HUB_PORT_POWER 8 104 | #define HUB_PORT_LOW_SPEED 9 105 | #define HUB_C_PORT_CONNECTION 16 106 | #define HUB_C_PORT_ENABLE 17 107 | #define HUB_C_PORT_SUSPEND 18 108 | #define HUB_C_PORT_OVER_CURRENT 19 109 | #define HUB_C_PORT_RESET 20 110 | #endif 111 | 112 | // USB descriptor type 113 | #ifndef USB_DESCR_TYP_DEVICE 114 | #define USB_DESCR_TYP_DEVICE 0x01 115 | #define USB_DESCR_TYP_CONFIG 0x02 116 | #define USB_DESCR_TYP_STRING 0x03 117 | #define USB_DESCR_TYP_INTERF 0x04 118 | #define USB_DESCR_TYP_ENDP 0x05 119 | #define USB_DESCR_TYP_QUALIF 0x06 120 | #define USB_DESCR_TYP_SPEED 0x07 121 | #define USB_DESCR_TYP_OTG 0x09 122 | #define USB_DESCR_TYP_IAD 0x0B 123 | #define USB_DESCR_TYP_HID 0x21 124 | #define USB_DESCR_TYP_REPORT 0x22 125 | #define USB_DESCR_TYP_PHYSIC 0x23 126 | #define USB_DESCR_TYP_CS_INTF 0x24 127 | #define USB_DESCR_TYP_CS_ENDP 0x25 128 | #define USB_DESCR_TYP_HUB 0x29 129 | #endif 130 | 131 | // USB device class 132 | #ifndef USB_DEV_CLASS_HUB 133 | #define USB_DEV_CLASS_RESERVED 0x00 134 | #define USB_DEV_CLASS_AUDIO 0x01 135 | #define USB_DEV_CLASS_COMM 0x02 136 | #define USB_DEV_CLASS_HID 0x03 137 | #define USB_DEV_CLASS_MONITOR 0x04 138 | #define USB_DEV_CLASS_PHYSIC_IF 0x05 139 | #define USB_DEV_CLASS_POWER 0x06 140 | #define USB_DEV_CLASS_PRINTER 0x07 141 | #define USB_DEV_CLASS_STORAGE 0x08 142 | #define USB_DEV_CLASS_HUB 0x09 143 | #define USB_DEV_CLASS_DATA 0x0A 144 | #define USB_DEV_CLASS_MISC 0xEF 145 | #define USB_DEV_CLASS_VENDOR 0xFF 146 | #endif 147 | 148 | // USB endpoint type and attributes 149 | #ifndef USB_ENDP_TYPE_MASK 150 | #define USB_ENDP_DIR_MASK 0x80 151 | #define USB_ENDP_ADDR_MASK 0x0F 152 | #define USB_ENDP_TYPE_MASK 0x03 153 | #define USB_ENDP_TYPE_CTRL 0x00 154 | #define USB_ENDP_TYPE_ISOCH 0x01 155 | #define USB_ENDP_TYPE_BULK 0x02 156 | #define USB_ENDP_TYPE_INTER 0x03 157 | #define USB_ENDP_ADDR_EP1_OUT 0x01 158 | #define USB_ENDP_ADDR_EP1_IN 0x81 159 | #define USB_ENDP_ADDR_EP2_OUT 0x02 160 | #define USB_ENDP_ADDR_EP2_IN 0x82 161 | #define USB_ENDP_ADDR_EP3_OUT 0x03 162 | #define USB_ENDP_ADDR_EP3_IN 0x83 163 | #define USB_ENDP_ADDR_EP4_OUT 0x04 164 | #define USB_ENDP_ADDR_EP4_IN 0x84 165 | #endif 166 | 167 | #ifndef MAX_PACKET_SIZE 168 | #define MAX_PACKET_SIZE 64 // maximum packet size 169 | #endif 170 | 171 | // USB descriptor type defines 172 | typedef struct __attribute__((packed)) { 173 | uint8_t bRequestType; 174 | uint8_t bRequest; 175 | uint8_t wValueL; 176 | uint8_t wValueH; 177 | uint8_t wIndexL; 178 | uint8_t wIndexH; 179 | uint8_t wLengthL; 180 | uint8_t wLengthH; 181 | } USB_SETUP_REQ, *PUSB_SETUP_REQ; 182 | 183 | typedef struct __attribute__((packed)) { 184 | uint8_t bLength; 185 | uint8_t bDescriptorType; 186 | uint16_t bcdUSB; 187 | uint8_t bDeviceClass; 188 | uint8_t bDeviceSubClass; 189 | uint8_t bDeviceProtocol; 190 | uint8_t bMaxPacketSize0; 191 | uint16_t idVendor; 192 | uint16_t idProduct; 193 | uint16_t bcdDevice; 194 | uint8_t iManufacturer; 195 | uint8_t iProduct; 196 | uint8_t iSerialNumber; 197 | uint8_t bNumConfigurations; 198 | } USB_DEV_DESCR, *PUSB_DEV_DESCR; 199 | 200 | typedef struct __attribute__((packed)) { 201 | uint8_t bLength; 202 | uint8_t bDescriptorType; 203 | uint16_t wTotalLength; 204 | uint8_t bNumInterfaces; 205 | uint8_t bConfigurationValue; 206 | uint8_t iConfiguration; 207 | uint8_t bmAttributes; 208 | uint8_t MaxPower; 209 | } USB_CFG_DESCR, *PUSB_CFG_DESCR; 210 | 211 | typedef struct __attribute__((packed)) { 212 | uint8_t bLength; 213 | uint8_t bDescriptorType; 214 | uint8_t bInterfaceNumber; 215 | uint8_t bAlternateSetting; 216 | uint8_t bNumEndpoints; 217 | uint8_t bInterfaceClass; 218 | uint8_t bInterfaceSubClass; 219 | uint8_t bInterfaceProtocol; 220 | uint8_t iInterface; 221 | } USB_ITF_DESCR, *PUSB_ITF_DESCR; 222 | 223 | typedef struct __attribute__((packed)) { 224 | uint8_t bLength; 225 | uint8_t bDescriptorType; 226 | uint8_t bFirstInterface; 227 | uint8_t bInterfaceCount; 228 | uint8_t bFunctionClass; 229 | uint8_t bFunctionSubClass; 230 | uint8_t bFunctionProtocol; 231 | uint8_t iFunction; 232 | } USB_IAD_DESCR, *PUSB_IAD_DESCR; 233 | 234 | typedef struct __attribute__((packed)) { 235 | uint8_t bLength; 236 | uint8_t bDescriptorType; 237 | uint8_t bEndpointAddress; 238 | uint8_t bmAttributes; 239 | uint16_t wMaxPacketSize; 240 | uint8_t bInterval; 241 | } USB_ENDP_DESCR; 242 | 243 | typedef struct __attribute__((packed)) { 244 | USB_CFG_DESCR cfg_descr; 245 | USB_ITF_DESCR itf_descr; 246 | USB_ENDP_DESCR endp_descr[1]; 247 | } USB_CFG_DESCR_LONG, *PUSB_CFG_DESCR_LONG; 248 | 249 | typedef struct __attribute__((packed)) { 250 | uint8_t bLength; 251 | uint8_t bDescriptorType; 252 | uint16_t bString[]; 253 | } USB_STR_DESCR, *PUSB_STR_DESCR; 254 | 255 | typedef struct __attribute__((packed)) { 256 | uint8_t bDescLength; 257 | uint8_t bDescriptorType; 258 | uint8_t bNbrPorts; 259 | uint16_t wHubCharacteristics; 260 | uint8_t bPwrOn2PwrGood; 261 | uint8_t bHubContrCurrent; 262 | uint8_t DeviceRemovable; 263 | uint8_t PortPwrCtrlMask; 264 | } USB_HUB_DESCR, *PUSB_HUB_DESCR; 265 | 266 | typedef struct __attribute__((packed)) { 267 | uint8_t bLength; 268 | uint8_t bDescriptorType; 269 | uint16_t bcdHID; 270 | uint8_t bCountryCode; 271 | uint8_t bNumDescriptors; 272 | uint8_t bDescriptorTypeX; 273 | uint16_t wDescriptorLength; 274 | } USB_HID_DESCR, *PUSB_HID_DESCR; 275 | 276 | typedef struct __attribute__((packed)) { 277 | uint8_t mCBW_Sig0; 278 | uint8_t mCBW_Sig1; 279 | uint8_t mCBW_Sig2; 280 | uint8_t mCBW_Sig3; 281 | uint8_t mCBW_Tag0; 282 | uint8_t mCBW_Tag1; 283 | uint8_t mCBW_Tag2; 284 | uint8_t mCBW_Tag3; 285 | uint8_t mCBW_DataLen0; 286 | uint8_t mCBW_DataLen1; 287 | uint8_t mCBW_DataLen2; 288 | uint8_t mCBW_DataLen3; 289 | uint8_t mCBW_Flag; 290 | uint8_t mCBW_LUN; 291 | uint8_t mCBW_CB_Len; 292 | uint8_t mCBW_CB_Buf[16]; 293 | } UDISK_BOC_CBW, *PUDISK_BOC_CBW; 294 | 295 | typedef struct __attribute__((packed)) { 296 | uint8_t mCSW_Sig0; 297 | uint8_t mCSW_Sig1; 298 | uint8_t mCSW_Sig2; 299 | uint8_t mCSW_Sig3; 300 | uint8_t mCSW_Tag0; 301 | uint8_t mCSW_Tag1; 302 | uint8_t mCSW_Tag2; 303 | uint8_t mCSW_Tag3; 304 | uint8_t mCSW_Residue0; 305 | uint8_t mCSW_Residue1; 306 | uint8_t mCSW_Residue2; 307 | uint8_t mCSW_Residue3; 308 | uint8_t mCSW_Status; 309 | } UDISK_BOC_CSW, *PUDISK_BOC_CSW; 310 | 311 | #ifdef __cplusplus 312 | } 313 | #endif 314 | -------------------------------------------------------------------------------- /software/volume_knob/src/usb_consumer.c: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // USB HID Consumer Media Keyboard Device Functions for CH32V003 * v1.0 * 3 | // =================================================================================== 4 | 5 | #include "usb_consumer.h" 6 | 7 | // =================================================================================== 8 | // HID reports 9 | // =================================================================================== 10 | volatile uint8_t CON_report[3] = {1, 0, 0}; 11 | 12 | // =================================================================================== 13 | // Consumer Multimedia Keyboard Functions 14 | // =================================================================================== 15 | 16 | // Press a consumer key on keyboard 17 | void CON_press(uint8_t key) { 18 | CON_report[1] = key; 19 | } 20 | 21 | // Release a consumer key on keyboard 22 | void CON_release(void) { 23 | CON_report[1] = 0; 24 | } 25 | 26 | // Press and release a consumer key on keyboard 27 | void CON_type(uint8_t key) { 28 | CON_press(key); 29 | DLY_ms(2); 30 | CON_release(); 31 | DLY_ms(2); 32 | } 33 | 34 | // =================================================================================== 35 | // RV003USB Software USB User Handle Functions 36 | // =================================================================================== 37 | void usb_handle_user_in_request(struct usb_endpoint * e, uint8_t * scratchpad, int endp, uint32_t sendtok, struct rv003usb_internal * ist) { 38 | 39 | // Mouse 40 | if(endp == 1) { 41 | usb_send_data((uint8_t*)&CON_report, sizeof(CON_report), 0, sendtok); 42 | } 43 | 44 | // Control transfer 45 | else usb_send_empty(sendtok); 46 | } 47 | -------------------------------------------------------------------------------- /software/volume_knob/src/usb_consumer.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // USB HID Consumer Media Keyboard Device Functions for CH32V003 * v1.0 * 3 | // =================================================================================== 4 | // 5 | // Functions available: 6 | // -------------------- 7 | // HID_init() init HID consumer device 8 | // CON_press(k) press a consumer/multimedia key (see below) 9 | // CON_release() release consumer/multimedia key 10 | // CON_type(k) press and release a consumer/multimedia key 11 | // 12 | // 2023 by Stefan Wagner: https://github.com/wagiminator 13 | 14 | #pragma once 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | #include 21 | #include "usb_handler.h" 22 | 23 | // Functions 24 | #define CON_init usb_setup // init HID consumer device 25 | void CON_press(uint8_t key); // press a consumer key on keyboard 26 | void CON_release(void); // release consumer key on keyboard 27 | void CON_type(uint8_t key); // press and release a consumer key 28 | 29 | // Consumer Keyboard Keycodes 30 | #define CON_SYS_POWER 0x30 31 | #define CON_SYS_RESET 0x31 32 | #define CON_SYS_SLEEP 0x32 33 | 34 | #define CON_VOL_MUTE 0xE2 35 | #define CON_VOL_UP 0xE9 36 | #define CON_VOL_DOWN 0xEA 37 | 38 | #define CON_MEDIA_PLAY 0xB0 39 | #define CON_MEDIA_PAUSE 0xB1 40 | #define CON_MEDIA_RECORD 0xB2 41 | #define CON_MEDIA_FORWARD 0xB3 42 | #define CON_MEDIA_REWIND 0xB4 43 | #define CON_MEDIA_NEXT 0xB5 44 | #define CON_MEDIA_PREV 0xB6 45 | #define CON_MEDIA_STOP 0xB7 46 | #define CON_MEDIA_EJECT 0xB8 47 | #define CON_MEDIA_RANDOM 0xB9 48 | 49 | #define CON_MENU 0x40 50 | #define CON_MENU_PICK 0x41 51 | #define CON_MENU_UP 0x42 52 | #define CON_MENU_DOWN 0x43 53 | #define CON_MENU_LEFT 0x44 54 | #define CON_MENU_RIGHT 0x45 55 | #define CON_MENU_ESCAPE 0x46 56 | #define CON_MENU_INCR 0x47 57 | #define CON_MENU_DECR 0x48 58 | 59 | #ifdef __cplusplus 60 | } 61 | #endif 62 | -------------------------------------------------------------------------------- /software/volume_knob/src/usb_descr.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // USB Descriptors and Definitions 3 | // =================================================================================== 4 | // 5 | // Definition of USB descriptors and endpoints. 6 | // 7 | // The following must be defined in config.h: 8 | // USB_VENDOR_ID - Vendor ID (16-bit word) 9 | // USB_PRODUCT_ID - Product ID (16-bit word) 10 | // USB_DEVICE_VERSION - Device version (16-bit BCD) 11 | // USB_LANGUAGE - Language descriptor code 12 | // USB_MAX_POWER_mA - Device max power in mA 13 | // All string descriptors. 14 | 15 | #pragma once 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | #include 22 | 23 | // =================================================================================== 24 | // RV003USB Defines 25 | // =================================================================================== 26 | //Defines the number of endpoints for this device. (Always add one for EP0). For two EPs, this should be 3. 27 | #define ENDPOINTS 2 28 | 29 | #define RV003USB_OPTIMIZE_FLASH 1 30 | #define RV003USB_HANDLE_IN_REQUEST 1 31 | #define RV003USB_OTHER_CONTROL 0 32 | #define RV003USB_HANDLE_USER_DATA 0 33 | #define RV003USB_HID_FEATURES 0 34 | 35 | #ifndef __ASSEMBLER__ 36 | #include 37 | 38 | // =================================================================================== 39 | // HID Report Descriptor 40 | // =================================================================================== 41 | static const uint8_t ReportDescr[] = { 42 | 0x05, 0x0c, // USAGE_PAGE (Consumer Devices) 43 | 0x09, 0x01, // USAGE (Consumer Control) 44 | 0xa1, 0x01, // COLLECTION (Application) 45 | 0x85, 0x01, // REPORT_ID (1) 46 | 0x19, 0x00, // USAGE_MINIMUM (Unassigned) 47 | 0x2a, 0x3c, 0x02, // USAGE_MAXIMUM (AC Format) 48 | 0x15, 0x00, // LOGICAL_MINIMUM (0) 49 | 0x26, 0x3c, 0x02, // LOGICAL_MAXIMUM (572) 50 | 0x95, 0x01, // REPORT_COUNT (1) 51 | 0x75, 0x10, // REPORT_SIZE (16) 52 | 0x81, 0x00, // INPUT (Data,Var,Abs) 53 | 0xc0 // END_COLLECTION 54 | }; 55 | 56 | // =================================================================================== 57 | // Device Descriptor 58 | // =================================================================================== 59 | static const USB_DEV_DESCR DevDescr = { 60 | .bLength = sizeof(DevDescr), // size of the descriptor in bytes: 18 61 | .bDescriptorType = USB_DESCR_TYP_DEVICE, // device descriptor: 0x01 62 | .bcdUSB = 0x0110, // USB specification: USB 1.1 63 | .bDeviceClass = 0, // interface will define class 64 | .bDeviceSubClass = 0, // unused 65 | .bDeviceProtocol = 0, // unused 66 | .bMaxPacketSize0 = 8, // maximum packet size for Endpoint 0 67 | .idVendor = USB_VENDOR_ID, // VID 68 | .idProduct = USB_PRODUCT_ID, // PID 69 | .bcdDevice = USB_DEVICE_VERSION, // device version 70 | .iManufacturer = 1, // index of Manufacturer String Descr 71 | .iProduct = 2, // index of Product String Descriptor 72 | .iSerialNumber = 3, // index of Serial Number String Descr 73 | .bNumConfigurations = 1 // number of possible configurations 74 | }; 75 | 76 | // =================================================================================== 77 | // Configuration Descriptor 78 | // =================================================================================== 79 | struct USB_CFG_DESCR_HID { 80 | USB_CFG_DESCR config; 81 | USB_ITF_DESCR interface0; 82 | USB_HID_DESCR hid0; 83 | USB_ENDP_DESCR ep1IN; 84 | }; 85 | 86 | static const struct USB_CFG_DESCR_HID CfgDescr = { 87 | // Configuration Descriptor 88 | .config = { 89 | .bLength = sizeof(USB_CFG_DESCR), // size of the descriptor in bytes 90 | .bDescriptorType = USB_DESCR_TYP_CONFIG, // configuration descriptor: 0x02 91 | .wTotalLength = sizeof(CfgDescr), // total length in bytes 92 | .bNumInterfaces = 1, // number of interfaces: 1 93 | .bConfigurationValue= 1, // value to select this configuration 94 | .iConfiguration = 0, // no configuration string descriptor 95 | .bmAttributes = 0x80, // attributes = bus powered, no wakeup 96 | .MaxPower = USB_MAX_POWER_mA / 2 // in 2mA units 97 | }, 98 | 99 | // Interface Descriptor 100 | .interface0 = { 101 | .bLength = sizeof(USB_ITF_DESCR), // size of the descriptor in bytes: 9 102 | .bDescriptorType = USB_DESCR_TYP_INTERF, // interface descriptor: 0x04 103 | .bInterfaceNumber = 0, // number of this interface: 0 104 | .bAlternateSetting = 0, // value used to select alternative setting 105 | .bNumEndpoints = 1, // number of endpoints used: 1 106 | .bInterfaceClass = USB_DEV_CLASS_HID, // interface class: HID (0x03) 107 | .bInterfaceSubClass = 1, // boot interface 108 | .bInterfaceProtocol = 1, // keyboard 109 | .iInterface = 0 // interface string descriptor 110 | }, 111 | 112 | // HID Descriptor 113 | .hid0 = { 114 | .bLength = sizeof(USB_HID_DESCR), // size of the descriptor in bytes: 9 115 | .bDescriptorType = USB_DESCR_TYP_HID, // HID descriptor: 0x21 116 | .bcdHID = 0x0110, // HID class spec version (BCD: 1.1) 117 | .bCountryCode = 33, // country code: US 118 | .bNumDescriptors = 1, // number of report descriptors: 1 119 | .bDescriptorTypeX = USB_DESCR_TYP_REPORT, // descriptor type: report (0x22) 120 | .wDescriptorLength = sizeof(ReportDescr) // report descriptor length 121 | }, 122 | 123 | // Endpoint Descriptor: Endpoint 1 (IN, Interrupt) 124 | .ep1IN = { 125 | .bLength = sizeof(USB_ENDP_DESCR), // size of the descriptor in bytes: 7 126 | .bDescriptorType = USB_DESCR_TYP_ENDP, // endpoint descriptor: 0x05 127 | .bEndpointAddress = USB_ENDP_ADDR_EP1_IN, // endpoint: 1, direction: IN (0x01) 128 | .bmAttributes = USB_ENDP_TYPE_INTER, // transfer type: interrupt (0x03) 129 | .wMaxPacketSize = 4, // max packet size 130 | .bInterval = 1 // polling intervall in ms 131 | } 132 | }; 133 | 134 | // =================================================================================== 135 | // String Descriptors 136 | // =================================================================================== 137 | #define __code const __attribute__((section(".rodata"))) 138 | #define USB_CHAR_GLUE(s) u##s 139 | #define USB_CHAR_SIZE(s) sizeof(USB_CHAR_GLUE(s)) 140 | #define USB_CHAR_TO_STR_DESCR(s) { \ 141 | .bLength = sizeof(USB_CHAR_GLUE(s)), \ 142 | .bDescriptorType = USB_DESCR_TYP_STRING, \ 143 | .bString = USB_CHAR_GLUE(s) \ 144 | } 145 | 146 | // Language Descriptor (Index 0) 147 | static __code USB_STR_DESCR string0 = { 148 | .bLength = 4, 149 | .bDescriptorType = USB_DESCR_TYP_STRING, 150 | .bString = {USB_LANGUAGE} 151 | }; 152 | 153 | // Manufacturer String Descriptor (Index 1) 154 | static __code USB_STR_DESCR string1 = USB_CHAR_TO_STR_DESCR(MANUF_STR); 155 | 156 | // Product String Descriptor (Index 2) 157 | static __code USB_STR_DESCR string2 = USB_CHAR_TO_STR_DESCR(PROD_STR); 158 | 159 | // Serial String Descriptor (Index 3) 160 | static __code USB_STR_DESCR string3 = USB_CHAR_TO_STR_DESCR(SERIAL_STR); 161 | 162 | // =================================================================================== 163 | // Descriptor Table 164 | // =================================================================================== 165 | // This table defines which descriptor data is sent for each specific 166 | // request from the host (in wValue and wIndex). 167 | struct descriptor_list_struct { 168 | uint32_t lIndexValue; 169 | const uint8_t *addr; 170 | uint8_t length; 171 | }; 172 | 173 | const static struct descriptor_list_struct descriptor_list[] = { 174 | {0x00000100, (const uint8_t *)&DevDescr, sizeof(DevDescr)}, 175 | {0x00000200, (const uint8_t *)&CfgDescr, sizeof(CfgDescr)}, 176 | {0x00002200, ReportDescr, sizeof(ReportDescr)}, 177 | {0x00000300, (const uint8_t *)&string0, 4}, 178 | {0x04090301, (const uint8_t *)&string1, USB_CHAR_SIZE(MANUF_STR)}, 179 | {0x04090302, (const uint8_t *)&string2, USB_CHAR_SIZE(PROD_STR)}, 180 | {0x04090303, (const uint8_t *)&string3, USB_CHAR_SIZE(SERIAL_STR)} 181 | }; 182 | 183 | #define DESCRIPTOR_LIST_ENTRIES ((sizeof(descriptor_list))/(sizeof(struct descriptor_list_struct)) ) 184 | 185 | #endif // __ASSEMBLER__ 186 | 187 | #ifdef __cplusplus 188 | } 189 | #endif 190 | -------------------------------------------------------------------------------- /software/volume_knob/src/usb_handler.c: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Software USB Handler for CH32V003 * v1.0 * 3 | // =================================================================================== 4 | // 5 | // This file contains a copy of rv003usb.c (https://github.com/cnlohr/rv003usb), 6 | // copyright (c) 2023 CNLohr (MIT License), with some minor changes by Stefan Wagner. 7 | 8 | #include 9 | 10 | #define INSTANCE_DESCRIPTORS 1 11 | 12 | #include "usb_handler.h" 13 | 14 | #define ENDPOINT0_SIZE 8 //Fixed for USB 1.1, Low Speed. 15 | 16 | struct rv003usb_internal rv003usb_internal_data; 17 | 18 | #define LOCAL_CONCAT(A, B) A##B 19 | #define LOCAL_EXP(A, B) LOCAL_CONCAT(A,B) 20 | 21 | void usb_setup(void) { 22 | rv003usb_internal_data.se0_windup = 0; 23 | 24 | // Enable GPIOs, TIMERs 25 | RCC->APB2PCENR |= LOCAL_EXP(LOCAL_EXP(RCC_IOP, USB_PORT), EN) | RCC_AFIOEN; 26 | 27 | // GPIO Setup 28 | LOCAL_EXP( GPIO, USB_PORT )->CFGLR = 29 | ( LOCAL_EXP( GPIO, USB_PORT )->CFGLR & 30 | (~( ( ( 0xf << (USB_PIN_DP*4)) | ( 0xf << (USB_PIN_DM*4)) 31 | #ifdef USB_PIN_DPU 32 | | ( 0xf << (USB_PIN_DPU*4)) 33 | #endif 34 | ) )) ) 35 | | 36 | #ifdef USB_PIN_DPU 37 | (0b0011)<<(4*USB_PIN_DPU) | 38 | #endif 39 | (0b1000)<<(4*USB_PIN_DP) | 40 | (0b1000)<<(4*USB_PIN_DM); 41 | 42 | // Configure USB_PIN_DM (D-) as an interrupt on falling edge. 43 | AFIO->EXTICR = LOCAL_EXP(AFIO_EXTICR1_EXTI0_P,USB_PORT)<<(USB_PIN_DM*2); // Configure EXTI interrupt for USB_PIN_DM 44 | EXTI->INTENR = 1<FTENR = 1<BSHR = 1<current_endpoint = endp; 59 | struct usb_endpoint * e = &ist->eps[endp]; 60 | 61 | int tosend = 0; 62 | uint8_t * sendnow; 63 | int sendtok = e->toggle_in?0b01001011:0b11000011; 64 | 65 | #if RV003USB_HANDLE_IN_REQUEST 66 | if(e->custom || endp) { 67 | // Can re-use data-stack as scratchpad. 68 | sendnow = __builtin_assume_aligned(data, 4); 69 | usb_handle_user_in_request(e, sendnow, endp, sendtok, ist); 70 | return; 71 | } 72 | #endif 73 | 74 | tosend = 0; 75 | 76 | // Handle IN (sending data back to PC) 77 | // Do this down here. 78 | // We do this because we are required to have an in-endpoint. We don't 79 | // have to do anything with it, though. 80 | uint8_t * tsend = e->opaque; 81 | 82 | int offset = (e->count)<<3; 83 | tosend = (int)e->max_len - offset; 84 | if(tosend > ENDPOINT0_SIZE) tosend = ENDPOINT0_SIZE; 85 | sendnow = tsend + offset; 86 | if(tosend <= 0) usb_send_empty(sendtok); 87 | else usb_send_data(sendnow, tosend, 0, sendtok); 88 | } 89 | 90 | void usb_pid_handle_out(uint32_t addr, uint8_t * data, uint32_t endp, uint32_t unused, struct rv003usb_internal * ist) { 91 | ist->current_endpoint = endp; 92 | 93 | // We need to handle this here because we could have an interrupt in the middle of a control or big transfer. 94 | // This will correctly swap back the endpoint. 95 | } 96 | 97 | void usb_pid_handle_data(uint32_t this_token, uint8_t * data, uint32_t which_data, uint32_t length, struct rv003usb_internal * ist) { 98 | //Received data from host. 99 | int epno = ist->current_endpoint; 100 | struct usb_endpoint * e = &ist->eps[epno]; 101 | 102 | length -= 3; 103 | uint8_t * data_in = __builtin_assume_aligned( data, 4 ); 104 | 105 | // Alrady received this packet. 106 | if(e->toggle_out != which_data) goto just_ack; 107 | 108 | e->toggle_out = !e->toggle_out; 109 | 110 | #if RV003USB_HANDLE_USER_DATA 111 | if(epno || (!ist->setup_request && length > 3)) { 112 | usb_handle_user_data( e, epno, data_in, length, ist ); 113 | #if RV003USB_USER_DATA_HANDLES_TOKEN 114 | return; 115 | #endif 116 | } 117 | else 118 | #endif 119 | 120 | if(ist->setup_request) { 121 | // For receiving CONTROL-OUT messages into RAM. NOTE: MUST be ALIGNED to 4, and be allocated round_up( payload, 8 ) + 4 122 | // opaque points to [length received, but uninitialized before complete][data...] 123 | #if RV003USB_SUPPORT_CONTROL_OUT 124 | if(ist->setup_request == 2) { 125 | // This mode is where we record control-in data into a pointer and mark it as 126 | int offset = e->count << 3; 127 | uint32_t * base = __builtin_assume_aligned( e->opaque, 4 ); 128 | uint32_t * dout = __builtin_assume_aligned( ((uint8_t*)base) + offset + 4, 4 ); 129 | uint32_t * din = __builtin_assume_aligned( data_in, 4 ); 130 | if(offset < e->max_len) { 131 | dout[0] = din[0]; 132 | dout[1] = din[1]; 133 | e->count++; 134 | if(offset + 8 >= e->max_len) base[0] = e->max_len; 135 | } 136 | goto just_ack; 137 | } 138 | #endif 139 | 140 | struct usb_urb * s = __builtin_assume_aligned( (struct usb_urb *)(data_in), 4 ); 141 | 142 | uint32_t wvi = s->lValueLSBIndexMSB; 143 | uint32_t wLength = s->wLength; 144 | //Send just a data packet. 145 | e->count = 0; 146 | e->opaque = 0; 147 | e->custom = 0; 148 | e->max_len = 0; 149 | ist->setup_request = 0; 150 | 151 | //int bRequest = s->wRequestTypeLSBRequestMSB >> 8; 152 | 153 | // We shift down because we don't care if USB_RECIP_INTERFACE is set or not. 154 | // Otherwise we have to write extra code to handle each case if it's set or 155 | // not set, but in general, there's never a situation where we realy care. 156 | uint32_t reqShl = s->wRequestTypeLSBRequestMSB >> 1; 157 | 158 | //LogUEvent( 0, s->wRequestTypeLSBRequestMSB, wvi, s->wLength ); 159 | #if RV003USB_HID_FEATURES 160 | if(reqShl == (0x01a1>>1)) { 161 | // Class read request. 162 | // The host wants to read back from us. hid_get_feature_report 163 | usb_handle_hid_get_report_start( e, wLength, wvi ); 164 | } 165 | else if(reqShl == (0x0921>>1)) { 166 | // Class request (Will be writing) This is hid_send_feature_report 167 | usb_handle_hid_set_report_start( e, wLength, wvi ); 168 | } 169 | else 170 | #endif 171 | 172 | if(reqShl == (0x0680>>1)) { // GET_DESCRIPTOR = 6 (msb) 173 | int i; 174 | const struct descriptor_list_struct * dl; 175 | for(i = 0; i < DESCRIPTOR_LIST_ENTRIES; i++) { 176 | dl = &descriptor_list[i]; 177 | if(dl->lIndexValue == wvi) { 178 | e->opaque = (uint8_t*)dl->addr; 179 | uint16_t swLen = wLength; 180 | uint16_t elLen = dl->length; 181 | e->max_len = (swLen < elLen)?swLen:elLen; 182 | } 183 | } 184 | } 185 | else if(reqShl == (0x0500>>1)) { // SET_ADDRESS = 0x05 186 | ist->my_address = wvi; 187 | } 188 | 189 | // You could handle SET_CONFIGURATION == 0x0900 here if you wanted. 190 | // Can also handle GET_CONFIGURATION == 0x0880 to which we send back { 0x00 }, or the interface number. (But no one does this). 191 | // You could handle SET_INTERFACE == 0x1101 here if you wanted. 192 | // or 193 | // USB_REQ_GET_INTERFACE to which we would send 0x00, or the interface # 194 | else { 195 | #if RV003USB_OTHER_CONTROL 196 | usb_handle_other_control_message( e, s, ist ); 197 | #endif 198 | } 199 | } 200 | just_ack: 201 | { 202 | //Got the right data. Acknowledge. 203 | usb_send_data( 0, 0, 2, 0xD2 ); // Send ACK 204 | } 205 | return; 206 | } 207 | 208 | #if defined( RV003USB_OPTIMIZE_FLASH ) && RV003USB_OPTIMIZE_FLASH 209 | 210 | // Do not compile ACK commands. 211 | 212 | #else 213 | 214 | void usb_pid_handle_ack(uint32_t dummy, uint8_t * data, uint32_t dummy1, uint32_t dummy2, struct rv003usb_internal * ist) { 215 | struct usb_endpoint * e = &ist->eps[ist->current_endpoint]; 216 | e->toggle_in = !e->toggle_in; 217 | e->count++; 218 | } 219 | 220 | //Received a setup for a specific endpoint. 221 | void usb_pid_handle_setup(uint32_t addr, uint8_t * data, uint32_t endp, uint32_t unused, struct rv003usb_internal * ist) { 222 | struct usb_endpoint * e = &ist->eps[endp]; 223 | ist->current_endpoint = endp; 224 | ist->setup_request = 1; 225 | e->toggle_in = 1; 226 | e->toggle_out = 0; 227 | e->count = 0; 228 | e->opaque = 0; 229 | } 230 | 231 | #endif 232 | -------------------------------------------------------------------------------- /software/volume_knob/src/usb_handler.h: -------------------------------------------------------------------------------- 1 | // =================================================================================== 2 | // Software USB Handler for CH32V003 * v1.0 * 3 | // =================================================================================== 4 | // 5 | // This file contains a copy of rv003usb.h (https://github.com/cnlohr/rv003usb), 6 | // copyright (c) 2023 CNLohr (MIT License), with some minor changes by Stefan Wagner. 7 | 8 | #pragma once 9 | 10 | #include "usb_descr.h" 11 | 12 | // USB handler checks 13 | #ifndef __ASSEMBLER__ 14 | #include "system.h" 15 | #if SYS_USE_VECTORS == 0 16 | #error Interrupt vector table must be enabled (SYS_USE_VECTORS in system.h)! 17 | #endif 18 | #if SYS_TICK_INIT == 0 19 | #error SysTick must be enabled (SYS_TICK_INIT in system.h)! 20 | #endif 21 | #endif 22 | 23 | #define LOCAL_CONCAT_BASE(A, B) A##B##_BASE 24 | #define LOCAL_EXP_BASE(A, B) LOCAL_CONCAT_BASE(A,B) 25 | 26 | #define USB_GPIO_BASE LOCAL_EXP_BASE( GPIO, USB_PORT ) 27 | 28 | // Public stuff: 29 | 30 | /* Here are your options: 31 | #define RV003USB_HANDLE_IN_REQUEST 0 32 | #define RV003USB_OTHER_CONTROL 0 33 | #define RV003USB_HANDLE_USER_DATA 0 34 | #define RV003USB_HID_FEATURES 0 35 | #define RV003USB_SUPPORT_CONTROL_OUT 0 36 | #define RV003USB_CUSTOM_C 0 37 | #define RV003USB_USER_DATA_HANDLES_TOKEN 0 38 | */ 39 | 40 | #ifndef __ASSEMBLER__ 41 | 42 | extern uint32_t * always0; 43 | 44 | struct usb_endpoint; 45 | struct rv003usb_internal; 46 | struct usb_urb; 47 | 48 | // usb_hande_interrupt_in is OBLIGATED to call usb_send_data or usb_send_empty. 49 | // Enable with RV003USB_HANDLE_IN_REQUEST=1 50 | void usb_handle_user_in_request( struct usb_endpoint * e, uint8_t * scratchpad, int endp, uint32_t sendtok, struct rv003usb_internal * ist ); 51 | 52 | // Enable with RV003USB_HID_FEATURES=1 53 | void usb_handle_hid_set_report_start( struct usb_endpoint * e, int reqLen, uint32_t lValueLSBIndexMSB ); 54 | void usb_handle_hid_get_report_start( struct usb_endpoint * e, int reqLen, uint32_t lValueLSBIndexMSB ); 55 | 56 | // Enable with RV003USB_OTHER_CONTROL=1 57 | void usb_handle_other_control_message( struct usb_endpoint * e, struct usb_urb * s, struct rv003usb_internal * ist ); 58 | 59 | // Received data from the host which is not an internal control message, i.e. 60 | // this could be going to an endpoint or be data coming in for an unidentified 61 | // control message. 62 | // Enable with RV003USB_HANDLE_USER_DATA=1 63 | void usb_handle_user_data( struct usb_endpoint * e, int current_endpoint, uint8_t * data, int len, struct rv003usb_internal * ist ); 64 | 65 | // If you want to use custom functions for the level 2 stack, then say 66 | // RV003USB_CUSTOM_C 67 | // This is mostly useful on things like bootloaders. 68 | 69 | // Note: This checks addr & endp to make sure they are valid. 70 | void usb_pid_handle_setup( uint32_t addr, uint8_t * data, uint32_t endp, uint32_t unused, struct rv003usb_internal * ist ); 71 | void usb_pid_handle_in( uint32_t addr, uint8_t * data, uint32_t endp, uint32_t unused, struct rv003usb_internal * ist ); 72 | void usb_pid_handle_out( uint32_t addr, uint8_t * data, uint32_t endp, uint32_t unused, struct rv003usb_internal * ist ); 73 | void usb_pid_handle_data( uint32_t this_token, uint8_t * data, uint32_t which_data, uint32_t length, struct rv003usb_internal * ist ); 74 | void usb_pid_handle_ack( uint32_t dummy, uint8_t * data, uint32_t dummy2, uint32_t dummy3, struct rv003usb_internal * ist ); 75 | 76 | //poly_function = 0 to include CRC. 77 | //poly_function = 2 to exclude CRC. 78 | //This function is provided in assembly 79 | void usb_send_data( const void * data, uint32_t length, uint32_t poly_function, uint32_t token ); 80 | void usb_send_empty( uint32_t token ); 81 | 82 | void usb_setup(); 83 | 84 | #define LogUEvent( a, b, c, d ) 85 | #define GetUEvent() 0 86 | 87 | #endif // __ASSEMBLER__ 88 | 89 | 90 | // Internal stuff. 91 | 92 | // Packet Type + 8 + CRC + Buffer 93 | #define USB_BUFFER_SIZE 12 94 | 95 | #define USB_DMASK ((1<<(USB_PIN_DP)) | 1<<(USB_PIN_DM)) 96 | 97 | #ifdef RV003USB_OPTIMIZE_FLASH 98 | #define MY_ADDRESS_OFFSET_BYTES 4 99 | #define LAST_SE0_OFFSET 16 100 | #define DELTA_SE0_OFFSET 20 101 | #define SE0_WINDUP_OFFSET 24 102 | #define ENDP_OFFSET 28 103 | #define SETUP_REQUEST_OFFSET 8 104 | 105 | #define EP_COUNT_OFFSET 0 106 | #define EP_TOGGLE_IN_OFFSET 4 107 | #define EP_TOGGLE_OUT_OFFSET 8 108 | #define EP_IS_CUSTOM_OFFSET 12 109 | #define EP_MAX_LEN_OFFSET 16 110 | #define EP_OPAQUE_OFFSET 28 111 | #else 112 | #define MY_ADDRESS_OFFSET_BYTES 1 113 | #define LAST_SE0_OFFSET 4 114 | #define DELTA_SE0_OFFSET 8 115 | #define SE0_WINDUP_OFFSET 12 116 | #endif 117 | 118 | #ifndef __ASSEMBLER__ 119 | 120 | #define EMPTY_SEND_BUFFER (uint8_t*)1 121 | 122 | // This can be undone once support for fast-c.lbu / c.sbu is made available. 123 | #ifdef RV003USB_OPTIMIZE_FLASH 124 | #define TURBO8TYPE uint32_t 125 | #define TURBO16TYPE uint32_t 126 | #else 127 | #define TURBO8TYPE uint8_t 128 | #define TURBO16TYPE uint16_t 129 | #endif 130 | 131 | struct usb_endpoint { 132 | TURBO8TYPE count; // ack count / in count 133 | TURBO8TYPE toggle_in; // DATA0 or DATA1? 134 | TURBO8TYPE toggle_out; // Out PC->US 135 | TURBO8TYPE custom; // Anything nonzero will incur the custom call. 136 | TURBO16TYPE max_len; 137 | TURBO16TYPE reserved1; 138 | uint32_t reserved2; 139 | uint8_t * opaque; // For user. 140 | }; // CAREFUL! sizeof pacekt 141 | 142 | // Make the size of this a power of 2, otherwise it will be slow to access. 143 | #ifdef RV003USB_OPTIMIZE_FLASH 144 | _Static_assert( (sizeof(struct usb_endpoint) == 32), "usb_endpoint must be pow2 sized" ); 145 | #else 146 | _Static_assert( (sizeof(struct usb_endpoint) == 16), "usb_endpoint must be pow2 sized" ); 147 | #endif 148 | 149 | struct rv003usb_internal { 150 | TURBO8TYPE current_endpoint; // Can this be combined with setup_request? 151 | TURBO8TYPE my_address; // Will be 0 until set up. 152 | TURBO8TYPE setup_request; // 0 for non-setup request, 1 after setup token, is allowed to be 2 for control-out if RV003USB_SUPPORT_CONTROL_OUT is set. 153 | TURBO8TYPE reserved; 154 | uint32_t last_se0_cyccount; 155 | int32_t delta_se0_cyccount; 156 | uint32_t se0_windup; 157 | // 5 bytes + 6 * ENDPOINTS 158 | 159 | struct usb_endpoint eps[ENDPOINTS]; 160 | }; 161 | 162 | //Detailed analysis of some useful stuff and performance tweaking: http://naberius.de/2015/05/14/esp8266-gpio-output-performance/ 163 | //Reverse engineerd boot room can be helpful, too: http://cholla.mmto.org/esp8266/bootrom/boot.txt 164 | //USB Protocol read from wikipedia: https://en.wikipedia.org/wiki/USB 165 | // Neat stuff: http://www.usbmadesimple.co.uk/ums_3.htm 166 | // Neat stuff: http://www.beyondlogic.org/usbnutshell/usb1.shtml 167 | 168 | struct usb_urb { 169 | uint16_t wRequestTypeLSBRequestMSB; 170 | uint32_t lValueLSBIndexMSB; 171 | uint16_t wLength; 172 | } __attribute__((packed)); 173 | 174 | 175 | extern struct rv003usb_internal rv003usb_internal_data; 176 | 177 | #endif // __ASSEMBLER__ 178 | --------------------------------------------------------------------------------