Hey there. I've been slaving away for a while, trying to understand the compilation and execution order of QMK, in order to get USB enumerated before matrix_setup() is run.
First, the motivation for why:
I have made a version of the lets_split keyboard, that uses kailh sockets, and has everything soldered onto the board. I couldn't find a method of ensuring that VUSB only had power on the plugged in half, that also allowed high current for LEDs, so VUSB is powered on both halves, and I, therefore, can't check that to determine which is plugged in or not.
On to what I did that works, ish:
I found my way to lufa.c, and saw that
int main(void)
{
#ifdef MIDI_ENABLE
setup_midi();
#endif
setup_mcu();
keyboard_setup();
setup_usb();
sei();
...
keyboard_setup() (so matrix_setup()) is run before setup_usb()
I have tried swapping that in my own fork here: https://github.com/Duckle29/qmk_firmware/commit/333af2be6167057b713ae48f7a719c874550ff21
Specifically the changes in lufa.c split_util.c
It works, but I have a feeling (and have now been told on gitter) that it working because of the delay is a coincidence and not reliable.
I have also been made aware that some special hardware won't work, if not initialized before usb_setup()
My question is then, how can I achieve this "is it plugged in" check? I'd love if USB could be initialized before checking "has USB" or even better, if a has_usb() function was built into QMK, that didn't rely on VUSB pad.
Thanks for your time :)
Hi,
I've found a clever solution in keyplus to determine if the USB bus is present without any extra pins before the USB host is initialized. You can see the code here for the approach I used.
Since the USB host has pull down resistors on both the D+/D- data lines, it is possible to determine the presence of the USB host by checking if these pins are pulled down or floating.
To determine if the pins are floating, we can use some low-level electrical effects. Every electrical signal in your circuit takes some time to switch due to parasitic capcitance, for the USB lines this value will probably be in the low picofarad range. However, in the presence of a pull-down resistor, this parasitic capacitance can be discharged more quickly, as the pull-down resistor will drain the charge from the imaginary capcitor. See RC time constant for more info.
So, if we send a quick pulse on the D+/D- data lines, it is possible to determine if the host pull-down resistors are present by observing the response of the data lines. If the host is present, the data lines will be quickly pulled low again by the pull-down resistors. If the host is absent, the data lines will float at the high logic level for long enough for us to assume the pull-down resistors are absent.
The only gotcha you need to watch out for is that the D+/D- pins on the USB plug are shorter than the 5V/GND pins, so your mcu will receiver power before the D+/D- are even connected, and you need to wait for the plug to be fulled mated before making this check.
Man that's brilliant. Thanks for the tip!
Edit: Ah of course that would've been too perfect. the ATMega32U4 doesn't allow direct pin access to DP and DM :/
I'm still digging a bit to see if I can ping the lines, but doesn't look promising
The next easiest option is to detect the start of frame interrupt. In the USB protocol, the host will send SOF (start-of-frame) packets every 1ms. The gist of this strategy:
UDCON register on the ATmega32u4)SOFII'm not sure how much it would help but the split_utli.c of the lets split does look for the USB attached But it would take some editing, but maybe that would be a good start.
https://github.com/qmk/qmk_firmware/blob/master/keyboards/lets_split/split_util.c#L54-L58
bool has_usb(void) {
USBCON |= (1 << OTGPADE); //enables VBUS pad
_delay_us(5);
return (USBSTA & (1<<VBUS)); //checks state of VBUS
}
Thanks for the help. I feel like I have adequately solved this, as I have now gotten it to work :) You can see my implementation here: https://github.com/qmk/qmk_firmware/pull/2559/commits/762451d81784dd6abfe3d31c12810ae93da3dd80
Basically, I can't check the vbus pad, as I don't have a diode on there. This means that VBUS will have power on either half, regardless.
Instead I check if the keyboard has enumerated. If it has enumerated, I set it up as a master.
As soon as the slave is first contacted my the master, it will also set a variable, ensuring it remains a slave in the future.
This seems to work great :)
Most helpful comment
Hi,
I've found a clever solution in keyplus to determine if the USB bus is present without any extra pins before the USB host is initialized. You can see the code here for the approach I used.
Since the USB host has pull down resistors on both the D+/D- data lines, it is possible to determine the presence of the USB host by checking if these pins are pulled down or floating.
To determine if the pins are floating, we can use some low-level electrical effects. Every electrical signal in your circuit takes some time to switch due to parasitic capcitance, for the USB lines this value will probably be in the low picofarad range. However, in the presence of a pull-down resistor, this parasitic capacitance can be discharged more quickly, as the pull-down resistor will drain the charge from the imaginary capcitor. See RC time constant for more info.
So, if we send a quick pulse on the D+/D- data lines, it is possible to determine if the host pull-down resistors are present by observing the response of the data lines. If the host is present, the data lines will be quickly pulled low again by the pull-down resistors. If the host is absent, the data lines will float at the high logic level for long enough for us to assume the pull-down resistors are absent.
The only gotcha you need to watch out for is that the D+/D- pins on the USB plug are shorter than the 5V/GND pins, so your mcu will receiver power before the D+/D- are even connected, and you need to wait for the plug to be fulled mated before making this check.