Hacker News new | past | comments | ask | show | jobs | submit login
Making USB devices – end to end guide to your first gadget (popovicu.com)
441 points by todsacerdoti 7 months ago | hide | past | favorite | 76 comments



A very nice writeup and intro into USB, although - it's very focused on using an ST microcontroller, which is a lot more steps and toolchains compared the the recent ESP32 ecosystem that offers a number of easy plug-and-play ways to make USB devices work (for example, several projects from Adafruit's learning pages provide basic use cases). Another issue is differential pairs - having designed several working USB boards myself, this has not a concern for beginners, mainly relevant for doing high-speed work. USB controller ICs (as used typically with arduio/esp) can handle a lot of the details for you, so doing the calculations seems overkill, especially for someone making their first gadget.


I recently built a small hand-wired macro pad using an Arduino Pro Micro equipped with ATmega32U4, it's apparently quite popular amongst hobbyists building custom keyboards. Quick and fun project for a beginner, the most tedious part of this project was to carve the wooden case.


Do you have a link to a PCB/kit, preferably with LEDs? Does yours run QMK? It sounds like a nice project if it costs a few bucks to get the materials, though I don't know what I'd do with it.


I didn't used a kit, but bought the components separately (microcontroller, key caps, switches, wires). It runs custom code as it was sufficient to fit my use case, though QMK may be possible? The keys are mapped from F13 to F22 and can be use in some software that allow custom keybinds.


This is great, thanks! I had researched extra F keys when I was making my keyboard (exactly to use as macros) but I hadn't discovered F13 to F22, thank you! That's exactly what I needed.


I think it is nice to have a basic understanding of differential pairs and impedance, I will admit I never really had to do the calculations since the ecad software I use has several tools for routing differential pairs and even analyzing signals for impedance. But as long as you keep your traces really short it normally doesn't matter.


> USB controller ICs (as used typically with arduio/esp) can handle a lot of the details for you

Can anyone recommend any good USB controller ICs? I normally just use a microcontroller with USB built in.


I have had a good experience using microchips USB2514 and USB2512


I just looked up the USB2514, and it would seem to not support PerPortPowerSwitching. (used by e.g. uhubctrl to toggle/switch power to individual USB ports). Am I not reading the specs correctly?


I was able to reset downstream USB devices using usbreset to the individual ports (or the entire hub) in my design, using usbreset. I did also have a hardware reset built in using the cfg_sel0 and cfg_sel1 pins 24 & 25 and RESET_N. I did not try to turn off power to those ports for any extended time so I am not sure if that works.


I used to test devices for USB compliance a long time ago. One of the things I often saw problems with was the inrush current test. Basically too much bypass caps on the 5v. I didn't see it mentioned in the article. It's really easy to get focused on the high speed digital design but for compliance it's sometimes the less sexy stuff that gets you. No idea how this stuff works with newer versions of the standard but it seems like there still is a test. Nice article though.

https://compliance.usb.org/index.asp?UpdateFile=Electrical#:....


What devices do people use to limit inrush current? You can make a current limiter with a few transistors, but I'm guessing there must be better integrated solutions (e.g. with temperature protection, etc.)


I only saw this topic recently. Sorry for replying 2 days after everyone else has left...

------------

I like the pMOS constant-slew rate trick myself.

A number of voltage-regulators have soft-start ramp-up already built in. But if your project doesn't have one, you can build it out of pMOS pretty easily. With just a resistor + capacitor on the pMOS (serving as negative feedback, to slow down the start), you can arbitrarily slow-down inrush current to whatever values you wish.

https://www.ti.com/lit/an/slva156/slva156.pdf?ts=17176224951...

Constant-slew rate voltage control vs a capacitive load effectively creates current-controlled startup functionality. So its "good enough" for most people's purposes (assuming capacitive loads are what's causing you to be worried).

So its a bit of an A-problem vs B-problem here. I'm giving you a slew-rate controlled soft start circuit when you asked for current control. But... its probably what you want?

> but I'm guessing there must be better integrated solutions (e.g. with temperature protection, etc.)

I believe the "proper" solutions are called a "load switch", of which there are a huge variety of integrated chips and MOSFETs with load-switch (constant slew rate + temperature control) solutions available.

EDIT: Something like this: https://www.nxp.com/products/power-management/smart-switches...

Things get rather complicated as you edge into "proper" solutions. I'm just a hobbyist though, so I don't have much experience with these "proper" designs.

EDIT2: Maybe that NXP Load switch is a bit "too full featured" for most projects. This TI one I found seems to be more mainstream and simple. TPS22950CQDDCRQ1 (https://www.ti.com/product/TPS22950-Q1)


You can use inductors. Some USB cables have a choke on them. But basically the problem is when a large amount of circuitry all powers up at one when you plug in. So everything connected directly to USB 5v with a lot of large capacitors to filter the power. If you really need that then you can self power and avoid the problem.


If the answer to excessive in-rush current (due to capacitance) is to add series inductance, then: Isn't the more-efficient answer simply less capacitance?


I think the correct answer really depends upon what you are trying to do. Sometimes where board space and cost are not an issue people use a combination of larger and smaller bypass caps to reduce switching noise. I don't think there is a one size fits all solution because people power many different types of things from USB. It's more just a caution about USB power in general. Most people know the 500mA current limit, but in-rush compliance is something you don't really see in functional testing because typically you can get away with a violation and the board will still work.


I'm looking more for a solution that acts as a constant current source, but where the voltage never exceeds the input voltage.


AMC7135 might work: it's a linear LDO fixed current regulator designed for driving LEDs, which internally is basically an N-channel MOSFET which holds it's gate voltage in the linear region and actively adjusts it to provide constant current.


Thank you for the suggestion, but it seems that the device has no over-temperature protection.


A note on USB-C, to augment the article's note: Wire the CC pins to appropriate resistors, or it probably won't work!

And a note on the differential routing and impedance: For USB 2.0, it's not a big deal. Keep the traces of similar length, and reasonably direct. Probably next to each other. You probably don't need to worry about fine-tuning the length and trace widths, impedance control, RF best practices etc. Just connect the nets.


480MBit/s is fast enough that you do need to worry about best practices and impedance matching within 10% or so. Many MCUs will also need series resistors between the USB PHY pins and the connector.

Will you need to respin a board because you needed to use a 20 mil trace instead of 24 mil? Probably not. All things considered, laying out a USB 2.0 differential pair is pretty low stakes. But you should still try and do it right - it's good practice.


Very cool guide.

But if you are worried about soldering more fiddly stuff like those ARM processors: It doesn't have to be that big, STM32 is nice if you need the power, but for smaller stuff smaller controllers can be preferrable.

E.g. one may also consider using VUSB, which is a library that bit-bangs USB on small Atmel microcontrollers: https://www.obdev.at/products/vusb/index.html Example board schematic we've used to teach students Linux kernel module programming: https://gitlab.cs.fau.de/i4/passt/passtboard-v2 with firmware http://www.poempelfox.de/ds1820tousb/ and https://gitlab.cs.fau.de/i4/passt/ds1820tousb

Also very easy, if you are inclined towards Arduino style programming there are tons of boards you can just use as USB devices with the included libraries in very few lines of code, for example https://www.az-delivery.de/en/products/digispark-board


> if you are worried about soldering more fiddly stuff like those ARM processors

If soldering's a worry, you can get quite reasonably priced dev boards with the microcontroller USB port already fitted and working - such as the NUCLEO-F429ZI https://www.st.com/en/evaluation-tools/nucleo-f429zi.html

Very similar to the NUCLEO-F103RB board the post author used, but as well as the USB connector at the top of the board for the built in programmer/debugger, it also has one at the bottom of the board, wired straight to the microcontroller.

You can also download the board's schematics, if you want to copy their choice of ESD protection and suchlike.


There's also things like the CH32V203 [1] which is a TSSOP-20 with hardware USB and costs around $0.81 in singles. The software side might be a little more ... challenging, though.

https://www.wch-ic.com/products/CH32V203.html?


Software USB can also be done on the 10 cents CH32V003:

https://github.com/cnlohr/rv003usb


They offer some fairly usable example reference code for a lot of the on-chip functionality-- including USB peripherals. I was able to turn their CH32v3xx examples into a pretty capable custom-keyboard firmware.


CH32F series is more or less the same and actually has CoreSight SWD and clone STM peripherals


Ive written bare-metal USB code on an MCU before, and I found it to be quite a shock compared to simple digital protocols like SPI or I2C. The physical and data link layers aren't much more complicated than, say, CAN, but beyond that you immediately run into a brick wall of descriptors, endpoints, and driver configuration. USB was designed from the ground up to be a plug-and-play ecosystem for PCs, and it really shows. Using the vendor-provided software as much as possible is definitely the way to go if you can.

Some half-remembered hints: You want bulk transfers for high throughput (don't even look at isochronous). USB is a master/slave protocol so if you're not getting peak throughput it's usually due to something on the host (PC) side. If the license (LGPL) is compatible with your needs, libusb is pretty easy to use. If you're not using the vendor driver, a hardware USB protocol analyzer is really helpful. USB in a NutShell[1] is a decent web reference for understanding the protocol.

[1] https://www.beyondlogic.org/usbnutshell/usb1.shtml


OK, dumb question, but since we're talking USB on STM32: Does anyone know how to support receiving more than 64 bytes in a frame? I've been looping over 64 byte frames in software, but I know it's possible to go higher. (Up to 1Mbyte I think). The problem is, the Reference Manual lists settings for this that are not normal registers. They are a pseudo-register of some sort. Wondering if there is an easy workaround for this! (The non-OTG USB peripheral)


USB full speed bulk transfer is limited to 64 bytes. (Dunno which stm32 you're talking about, but stm32g4 is only full speed.)


A single BULK USB endpoint can only support 64 bytes per transfer. You could make use of multiple endpoints (8 are available in total) or switch to isochronous endpoints which can support up to 1023 bytes per frame.

Most hardware support for isochronous transfers requires DMA on the MCU side, so it tends to be a pain unless your vendor has a library that handles it for you.

You can in general send up to 19 bulk transfers in a single frame (even on a single endpoint), but again, vendor libraries differ wildly in their support for this.


Note that isochronous transfers require kernel-mode drivers on the host side, so you won't be able to use libusb in that case. Bulk transfers are the way to go if you want high throughput.


Isn't the restriction just on the reception side? So, if you have a MCU talking to a PC or another MCU, you can send however many bytes you want, but not receive? I say this because A: The Reference Manual only indicates this limit for reception, and B: I only experienced this on reception: PCs seem capable of sending and receiving messages larger than 64-bytes, and STM32s seem capable of sending messages larger than 64-bytes, but not receiving (without isosynchronous, or anything special)


The limits on packet size are based on the transfer type (control, interrupt, isochronous, or bulk) and whether the connection is low speed (1.5 Mbps), full speed (12 Mbps) or high speed (480 Mbps). The USB module on the MCU will be designed for the largest possible packet, which IIRC is a full-speed 1023-byte isochronous packet. (MCUs usually aren't fast enough to reach high speed.)

Data larger than one packet can be sent as a multi-packet "transfer". This is where bulk transfers get their throughput -- at full speed, the largest bulk packet is only 64 bytes, but you can send 19 bulk packets per 1-millisecond frame, which gives 1216 bytes/millisecond, more than the 1023 bytes/millisecond possible with isochronous.

You might be able to force the hardware to send nonstandard packets, but then it's not really USB any more.


I appreciate the insight! That makes sense in context.


Yes, "bulk" was the fourth word of my comment.


G4 indeed! I think something in the RM implied it could go higher, but I had trouble interpreting it. Thanks for the explanation.


I use ESP32 mostly but I have a cheap hack that fits a lot of projects...

For super quick easy custom controllers, also consider pulling the control board from discarded USB keyboards. Use conductive glue instead of solder to attach wires to the contacts, and a helping of hot glue to keep them secured. I've made cheap but very robust 1-button game controllers with an arcade button that sends a space bar click. You get all the debounce etc, no code.


Are there any development boards with USB 3 support? I'm trying to prototype a USB C monitor sink but having trouble finding a board that has the power necessary to receive DisplayPort over USB.


There is not much power needed to receive DisplayPort over USB (assuming you already can receive displayport signals or just route them to an external monitor).

You just need to implement an USB billboard device (optional to make it work, but required by the spec IIRC) and signal the correct alternate function. Then DisplayPort signalling will be present on the USB-C plug. Then just connect the right AUX wires to the DisplayPort connector.


There is this: https://octopart.com/cyusb3kit-003-cypress+semiconductor-494... and the pricing seems reasonable. When I looked for something like that two years ago the prices were order of magnitude higher.


This device is not really suited, it is a high speed USB3.0 peripheral. You might be able to use it with the right software, but then you are just using the CPU in it.


The way how the Cypress FX series works (and for that matter majority of non-trivial USB-to-something chips do) is that there is a bunch of DMA-capable peripherals inside and the in fact ridiculously underpowered CPU core inside only sets up the required configuration for the DMA transactions to happen.


Most FPGAs with transceivers can do it.


Do you have an example? Looking for a product recommendation others have good experience with


https://github.com/enjoy-digital/usb3_pipe

I don't know that there are any fully-baked solutions for it though, but it's definitely doable in-FPGA with some effort.


I've been using https://github.com/xairy/raw-gadget to create a virtual USB device on my raspberry pi I've got plugged into my PC. I'm currently using It to emulate a MTP camera so I can spoof some proprietary software.


You gotta give us more details than that! Sounds like fun!


Most of the details are here: https://github.com/petabyt/vcam With the raw gadget code in src/otg.c

It hasn't only just been fun, it's also proven to be a big help with an Android app I've been working on :)


That is so very cool, thank you!


fwiw; I've prototyped some USB gadgets using Raspberry Pi Zeroes and Composite USB in the Linux Kernel. At least storage and serial devices were pretty easy to get going.

You'll need something like a shell script on the Raspberry to initialize the composite kernel module and you'll find the boildplate in the kernel docs.


I would love to play with pis and make virtual usb devices, like a webcam.

pikvm is an interesting project.

It will hook to a PC, and the USB connection can not only pretend to be a keyboard and mouse, but it can be a USB drive that you can boot the system with. Pretty interesting for installs.


Friendly reminder that USB is not free.

You must pay a one-time fee of $6,000 in exchange Vendor ID [0].

[0] https://www.usb.org/getting-vendor-id


For hobbyist use you can just pick a random value and it'll usually work. Open-source hardware can get a product ID for free from https://pid.codes. Small-run commercial hardware can usually get a free product ID from the maker of their USB-capable chip. And once you're big enough that you're designing your own chip, $6,000 isn't such a big deal anymore.

So yes, you are technically right, but it really doesn't matter all that much in practice. USB is by far one of the most accessible major hardware standards out there.


Many chip vendors will give you a PID under their VID if you're making a product. For example this blog post uses and ST chip, and they'll provide a PID if you request it [1] - free of charge, but subject to some conditions.

(Of course if your USB device needs Windows drivers, you'll still have to deal with things like code signing...)

[1] https://community.st.com/t5/stm32-mcus-embedded-software/dea...


The part that makes me feel most uncomfortable about usb VID/PIDs is that they are only 16bits each. While I don't think 4billion unique commercial USB devices is limit we will reach anytime soon, efficiently managing the available address space is a different question. And unlike IPv4 addresses a range of USB VID/PIDs can't be as easily reused without making the VID/PID meaningless.


By that time they'll just add an extra field to the protocol and assign a special "see other field" VID/PID pair.

For a lot of products the VID/PID combo isn't that important on a protocol level, and there's technically no reason why two completely unrelated products couldn't be using the same pair. In practice you're mainly going to use them for software to target a specific device, for example the Linux HID driver uses it to work around hardware/firmware bugs. But HID devices are self-describing, and that's the main mechanism used by the driver to figure out what has been attached and how to behave.

If modern OSes can do the same to distinguish hardware with the extra field, sharing the same value for backwards compatibility with legacy machines really isn't going to be a big deal. It's not like MAC or IPv4 addresses where a clash will essentially kill all communication.


The problem isn't the combined limit, it's the hard limit on Vendor IDs at 65,535.

Since very few companies have 65,535 products, almost all Product IDs in any given Vendor ID namespace are unused.

That is the maximum number of organizations on Earth that can be issued a USB Vendor ID until the spec is modified. They'll likely introduce a special Vendor ID that indicates to clients they should look elsewhere for the real (and longer) one.

While the rules were more lax many years ago, and some vendors are grandfathered in and can therefore share their Product ID allocation, these behaviors are explicitly prohibited by the USB-IF for newly issued Vendor IDs.


FWIW, for my own personal projects I resort to vendor id 6666 (Prototype).


Yes, for a single-use hobby product it doesn't matter much and you can just risk the collision. And yes, some vendors will generously let you have a Product ID under their Vendor ID, but then your product looks as if it was made by them when your customers plug it in.

If you plan to deploy more than a few devices you'll need a Vendor ID and people should know that there is a relatively large tax in front of that need.

Hard disagree on $6,000 not mattering. We build a small number of devices for severely disabled people and sell a few hundred a year. The USB IF was unwilling to budge at all on their prices and this was a large cost that we did not anticipate, and by the way, is technically shameful.

How much does a fucking number cost?

Well, it depends on how scarce you decide to make it, and of course, what the market will bear.


See also https://pid.codes/

For the DirtyJTAG project we use one of those:

https://pid.codes/1209/C0CA/


NXP runs a USB VID/PID Program [0] for small production designs (<10,000 units) that use their MCUs. They’ll give you 3 PIDs for free under one of their VIDs. I use this in my side projects (~200 units) and works pretty well!

[0] https://community.nxp.com/t5/Kinetis-Microcontrollers/NXP-US...


Easy way around that is to license your PID(s) from MCS Electronics. They bought a VID before the USB-IF got around to prohibiting resale of PIDs.


I just use 0xF055 as VID. What is the USB-IF gonna do?


I’ve commonly seen OpenMoko’s ID, 1d50, used for free projects. See the list at <http://www.linux-usb.org/usb.ids>.


.. or use someone else's. I believe there's a hobbyist VID under which you can reserve PIDs, or in many cases you can just copy an existing product if your device is generic enough and you're not planning to sell a big production run.


There's pid.codes which uses a vid of a defunct organization, but before that there was also the F055 vid. I still use that vid because no company will accept that vid, since it's commonly used by foss projects.


Wow, first time I hear about it, this is an eye opening experience.

I will think twice now before saying “why couldn’t they just use USB for this?”


> I will think twice now before saying “why couldn’t they just use USB for this?”

I dunno, I think either you're doing a small run and you can just bake in a Teensy or whatever, or you're doing a big run and $6k is a drop in the bucket.


This is just plain wrong. Not every business is a megacorp or VC-backed unicorn.

Many, many products start life on a shoestring and small batch production runs.

There are other products that intentionally serve small markets, like people with disabilities.

For these organizations, $6,000 is not a trivial amount of money.


Even crazier, it's just a 16 bit value.

What are they gonna do when they run out?


Yeah, I was going to ask about this. I think ST Micro will sublicense you a PID under their VID, but with restrictions, and companies attempting to buy a VID and sell PIDs as a service have been sued?


This is a very well written and informative article for working with USB, wish I had it when I first started USB signals.


what about converting existing devices from USB-A to USB-C? I have a bunch of powerbanks lying around that I'd love to keep using but most of my devices now use a USB-C charging cable, would it be a simple case of replacing the ports and re-soldering or is there any extra component I need to add?


I would use an adapter for that use case.

The wiring of the ports on a new design is straightforward; you are using the same pins if it's USB 2.0 (As the article says), plus two extra connections that go to a resistors. If you can find ports that use the same footprint, feasible, but probably not worth it. Desoldering a port is a pain because you have to get all the pins to melting temperature concurrently, and you'd have to figure out how to wire those CC pins.


I recently used CH340N (soic 8 virtual com port) and it was really easy. Easy soldering, no extra parts just directly connect to USB, works out of the box on Linux.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: