~~NOCACHE~~
====== Yet Another Firmware Based SOFT USB Device ======
* This project has been uploaded to [[https://github.com/GenieKits/Yet-Another-Firmware-Based-SOFT-USB-Device|Github]] and
* You can get the source package and references from my [[:downloads|downloads page]] as well.
----
Can we implement an USB device on a simple MCU without any USB port? In this project I will give you a positive answer. In fact years ago there was a wondrous project which was called [[https://www.obdev.at/products/vusb/powerswitch.html|PowerSwitch]] published on the Internet. The author implemented a low speed USB device on an [[https://www.microchip.com/design-centers/8-bit/avr-mcus/device-selection|AVR]] microcontroller. Two GPIO pins were used to capture the signals of USB host then decoded them with firmware running inside the chip. These days I created a similar project to implement another SOFT USB device on another MCU. The chosen MCU is [[https://www.microchip.com/design-centers/16-bit|PIC24/dsPIC33]] which is produced by [[https://www.microchip.com|Microchip Co.]] and a custom [[wp>Human_interface_device|HID]] device is implemented on it. Schematic and source codes are included in this project.
----
===== Background =====
It was about 2006. I found a wondrous project on the Internet. It was called [[https://www.obdev.at/products/vusb/powerswitch.html|PowerSwitch]], which provides 8 channels of parallel output that intends to be switches. These switches could be controlled through an USB host but the MCU ([[wp>Atmel|ATMEL]] [[http://ww1.microchip.com/downloads/en/DeviceDoc/DOC0839.PDF|90S2313]]) doesn't have any USB peripheral. Two GPIO pins are used to capture the signals of USB D+/D- and all signals are decoded by the firmware in realtime. Of course, it is a low speed USB device.
The high speed of AT90S2313 is the key point of this project. The MCU runs at 12MIPS when it is driven by a 12MHz crystal. Every eight instructions corresponds to one bit in the USB low speed (1.5Mbps) bit stream. The level of D+/D- could be sampled accurately and decoded before the next bit coming. The firmware just does NRZI decoding and BIT UNSTUFF because 12MIPS is not fast enough to do the CRC verification. When the bit stream is sent to the host, the CRC16 code will be calculated and attached to the bit stream. A handshake NAK is sent to the host repeatedly until the CRC16 code was ready.
It is a really smart design. There is an application note (AVR309) published by ATMEL Co. in which the technique details are written down. The kernel of this project is coded with assembly language. Unfortunately, I couldn't analyze the complete code of PowerSwitch because I wasn't an expert to AT90xx chips at that time. So far PowerSwitch has been evolved into an independent project which is called [[https://www.obdev.at/products/vusb/index.html|V-USB]] and ported to [[wp>AVR_microcontrollers|ATMEGA]] chips. There are some practical product derived from this project, for example, [[https://www.fischl.de/usbasp/|USBAsp]]. However, it has not been implemented on other chips except ATMEL's.((Recently I found a [[https://github.com/ads830e/stm32f030-vusb|similar project]] which is based on [[https://www.st.com/en/microcontrollers-microprocessors/stm32f0x0-value-line.html|STM32F030]]. ))
Years ago I designed a few USB devices. Because the chips I used have USB port, I have never concerned about the low level protocol of USB. In this project I am planning to implement a new firmware based SOFT USB device which is similar as "V-USB". It will bring me better comprehension to the low level protocol of USB. I chose PIC24F/dsPIC33 series chip which is produced by Microchip and coded with C and Assembly language. If you are not an expert to Microchip's MCU, this project might bring great confusion to you. In another words, this project will bring you a great chance to study Microchip’s MCU in depth.
----
===== The Circuit =====
{{:ya-vusb:ya-vusb-sch.png}}
The major controller IC1 is [[https://www.microchip.com/wwwproducts/en/dsPIC33FJ12MC201|dsPIC33FJ12MC201]]. This chip provides small SSOP package with 20 pins and 40MIPS maximum running speed. I use an 8MHz crystal and generate 30MHz (Fpllout = 30MHz) clock via PLL, driving the CPU running at 15MIPS. Every 10 machine cycles corresponds to one bit in the USB low speed (1.5Mbps) bit stream. I use two pins (RA0/RA1) of GPIO-A to capture the level of USB D+/D-. Because the "Change Notification" interrupt can be triggered by RA0 or RA1 directly, we don't have to use the INT0 pin. We MUST connect USB D+/D- to two pins within one GPIO group. It's not allowed to connect D+ to RAx and D- to RBx. The pull-up resistor R7 on USB D- is NOT connected to 3.3v directly. I use RB4 to control this pull-up resistor. There is a LED connected to RB15. I will send some data through USB port to control this LED. MCLR/PGC1/PGD1 are used as an [[wp>In-system_programming|ICSP]] port. We can use some burner, such as [[https://www.microchip.com/Developmenttools/ProductDetails/PG164140|PICKit3]], to program the chip through it. RB7 is connected to the PIN6 of ICSP connector J2. It is used to send some debugging messages when we need to. The PIN6 of ICSP port has been used as the LVP signal of PICKit3. Because I have never used PICKit3, I don't know if this connection (RB7 to PIN6 of ICSP) will make PICKit3 fail or not. There is a NPN transistor T1 which is used as a 3.3V power regulator. On my experimental board it is replaced by an [[http://www.advanced-monolithic.com/products/voltreg.html|AMS1117-33]]. I think it will work if you try to connect a red LED at VBUS in series instead of transistor T1, reducing the +5V to 3.2V. The resistor R4 could be replaced by a solder bridge. If you want to use [[https://www.microchip.com/wwwproducts/en/PIC24F16KA101|PIC24F16KA101]] to instead dsPIC33FJ12MC201, you can just omit R4 and C3 and replace X1 with a 30MHz crystal.
Fuse definitions are not included in the source code. You have to set "FCKSM1/POSCMD1/FWDTEN/JTAGEN" into "Programmed (0)". All other fuses are "Unprogrammed (1)". If you use PIC24F16KA101 to instead of dsPIC33, another four bits "FNOSC2/FNOSC0/POSCMD0/FWDTEN" need to be set to "Programmed (0)".
{{:ya-vusb:fuses.png|left:dsPIC33 right:PIC24F}}
----
===== Coding Environment =====
This project is implemented and tested on WINDOWS platform. I use the XC16 compiler suit version 1.25 which is developed by Microchip. Unless the compiler I don't use any integrated development environment (MPLAB IDE) and debug probe ([[https://www.microchip.com/DevelopmentTools/ProductDetails/DV164045|ICD 4]] or PICKit). I haven’t bought any commercial license as well. Without commercial license the GCC compiler only supports -O1 level optimization. It's enough for this project. You can click the icon listed below to download XC16 compiler suit. It will work if you want to try another version higher than 1.25.
[[https://www.microchip.com/development-tools/pic-and-dspic-downloads-archive|{{ :ya-vusb:mplab-xc-compiler.png }}]]
I use a BAT file (usb.bat) to compile the source codes:
xc16-gcc -mcpu=33FJ12MC201 -O1 main.c hid.c usb.c sie.s dbg.s -o main.elf -T p33FJ12MC201.gld -Wl,--defsym,__has_user_init=1,-Map=main.map
xc16-bin2hex main.elf
xc16-objdump -D main.elf >main.txt
pause
As you can see I pass a parameter to the linker, -Wl,--defsym,__has_user_init=1, then the function named "__user_init" in the source file "sie.s" will be invoked by the C startup code. In the installation directory of XC16 you can find a source-code cab which is named "libpic30.zip". In this cab there is a source file named "crt0_standard.s" to be controlled by the symbol "__has_user_init".
The program running on the host for testing are coded and compiled with Microsoft [[wp>Microsoft_Visual_Studio|Visual Studio]] 2008. I use a self-made firmware burner to instead of PICKit3 or other debug probe. I'll create some documents for it in the future.
----
===== Structure of the source files =====
{{:ya-vusb:file-structure.png}}
There are five source files in this project. Two of them are assembly source codes (sie.s/dbg.s). "dbg.s" is used for debugging, sending some messages through the UART port or showing states through the LED. It is NOT a necessary module in this project. Others are C source files. "usb.c" is used to handle some standard requests of USB specification, such as "GET DESCRIPTOR", etc. "hid.c" is used to handle requests (SET/GET REPORT) of HID specification. Initialization code and main task loop are included in "main.c". The "main entry" of C language is defined in "sie.s". There only are two functions in "main.c", setup() and loop(), just like the source code style of [[wp>Arduino|arduino]].
"sie.s" is the kernel code of this project. The "Change Notification" interrupt service routine and USB low level protocol processing code are inside this file. There are also some global variables and buffers in this file. A group of API functions are defined in this module for "usb.c". It is recommended and easy to invoke these API functions instead of accessing the global variables.
----
===== Details of source codes =====
I don't like to put the whole text in this page because it is too long. Please [[:downloads|download the source files]] of this project. In the package there is a PDF file in which all key points about the source codes has been written down.