Open probonopd opened 8 years ago
This should be able to fit fairly well, but there's a lot I don't know. Only thing I'm kind worried about is the way my reflasher works is it uses a scratchpad, verifies, and then flashes itself. If something goes wrong or you break the USB interrupt, you would have to go back to serial flashing to get the thing recovered.
Another big recommendation is I know people would probably want to turn this into a CDC device, but I really believe using control messages is the best way to stage data back and forth. It sidesteps a lot of problems with endpoints and keeps things really organized.
P.S. Right now my focus is on two other aspects of the project. (1) Demoizing the project and (2) Trying to get USB 1.1 Full Speed working. I still don't know if #2 is possible, but I am going to keep my focus there.
I also can continuously work to shrink my code size.
Who all knows much about how to set up bootloaders, etc?
Just realized igrr (https://github.com/igrr) the main contributor of esp8266/Arduino is sitting at @Espressif. He probably can help you with your Full Speed ambitions (interrupt/dma issues and what not)
I just made a post on their forums (though it looks like posts have to be approved) How would I go about requesting this from him? Should I be requesting it of him directly? I know RichardS said he's already on trying to get that info, too.
Hmm, I don't know how to get the info you need the best. It was just an observation.. Maybe it could be a start to formulate exactly what you need in a separate ticket so we could start to gather what you need. I could take a look the existing code from other projects..
Hmm, right now things are a bit stalled before we can COMPLETELY rule out full-speed... at least on my end, but I'm not sure what needs to happen arduino-end to start support of this project.
Is asking @igrr for information still a bottleneck? In my experience, he is very approachable, helpful and friendly and is online on https://gitter.im/esp8266/Arduino most of the time.
The main thing which needs to happen on Arduino end is making decisions :) For example, do we want USB stack running only in second stage bootloader (eboot) or in the application as well? If it runs from application code, we probably need to use same USB API which is used by other Arduino boards. We can probably reuse their code, but need to decide what subset we want to support. If it runs in bootloader, do we use some fixed delay there? Or maybe we can detect if USB cable is plugged in and avoid delaying if this isn't the case? Also, do we want espusb code to be updatable over usb? Do we want the update to be fail safe, or can we expect the user to fall back to serial upload if things go wrong? Answers to these questions will determine implementation on our end, which in case of bootloader-only espusb support should be quite straightforward.
Regarding the espusb code itself, I think it would be nice to package all usb-related code into a self-contained module (static library) with a well-defined API. I can understand that some hacking and exploration needs to happen first, but once they are finished some cleanup and refactoring would be much welcome. That would make it easier for others to use this project within other applications/frameworks as is, for example by adding it as a git submodule.
Regarding additional info about hardware features of the 8266, i think Angus has already given a series of replies on the 8266 community forum. If there's anything else you want to know, please ask.
Thanks @igrr for chiming in. I am by no means an expert so in the end it is @cnlohr and @igrr making the decisions, but since I started this thread, I would like to offer my perspective from an user's point of view.
Why do people love Arduino? Because it gets us producive extremely fast, without having to worry too much about the low-level infrastructure. Hence, I see 2 main use cases for espusb in esp8266/Arduino:
.ino
sketches with this functionality (like with Arduino Leonardo, where I don't have to change the sketch specifically to make USB flashing work)..ino
sketches (possibly using functions that look like https://github.com/gloob/vusb-for-arduino).Whereas it would be elegant to have the code for 1 and 2 combined in just one place, I wouldn't worry to much about having 2 separate copies of the USB code (one in the core/bootloader, one included into the sketch), since with 4 MB of flash, who cares.
@igrr I wish I had an opinion on the subject. I really don't know what's best, either, regarding where the bootloader should be, and the timeout, etc. I would be concerned to let it boot too quickly, since it may take a bit for the device to enumerate and a PC task to catch it if you want it to stick around in the bootloader.
I tried finding some spec for usb bootloading and didn't find one. I still much prefer use of control messages over just about anything else.
I am going to continue refining the main espusb project, now that I am putting the full-speed stuff on hold, indefinitely (unless espressif comes back with something). I have some updates just committed in 90e8a22. I will be splitting the code apart so the per-device stuff is separate from usb.c. Right now, endpoints are handled externally, but descriptors and custom control messages are in there. Most of this can be done in parallel - both work on Arduino and my work. Especially if it results in specific requests for changes to my code.
P.S. It's currently ~8.1 seconds to flash to the scratchpad via usb and another 2 seconds to copy it over and reboot.
All that said... I am not a fan of CDC devices. And, I don't even know if they're supported on all OSes in low-speed mode.
One other thing that is convenient. I do have code now that causes the bus to re-enumerate.
I am getting very close to what I want for the "library"
Right now, usb.h, usb.c, usb_asm_1bit.S and usb_table_1bit.h are all librariezed. All of the descriptors, custom behavior, etc. is located elsewhere. Current usage:
Total SRAM: 232 bytes + Descriptors, 317 bytes (Could be stored elsewhere) Total Flash/IRAM (Only iram, tables and usb_init can be stuck in slow flash): 1422 bytes
If only a bare minimum USB device is needed, could be smaller yet, but not by too much.
Ping @igrr your input is appreciated.
For the bootloader mode, this amount of RAM is not an issue, while for the application the amount of iram required may be a problem. We don't have many options for stuff to move from IRAM to flash to free up some space.
Next step would be to integrate espusb files into bootloader.
Would we do it where I have some code that just all lives in IRAM - then somehow, when it decides to boot, it loads the program off a different place on FLASH into IRAM and boots it?
Right now, I do use library calls for a few things like memcpy, and setting up the inputs to be GPIO instead of special functs.
I can add esp-usb to our eboot bootloader. It would be nice if there was a way to tell whether USB isn't connected at all (no cable) — and avoid loading espusb from flash in this case.
Memcpy is in rom, gpio_xxx functions are also in rom if I'm not mistaken. If there are any dependencies, I'm sure we will figure this out.
We could avoid use of interrupts and just poll for USB and/or serial? Do you use interrupts in eboot?
You can determine if you're connected by looking at D-. D+ will only change when the host decides to talk to you, which does seem to happen very quickly.
One of the biggest questions would be how long to wait if you are connected to a host. May take a while to know if the user wants their code to run and pretend to be a mouse vs they just haven't gotten their flasher running yet.
This is how it is done in the Arduino Leonardo board:
Rather than requiring a physical press of the reset button before an upload, the Leonardo is designed in a way that allows it to be reset by software running on a connected computer. The reset is triggered when the Leonardo's virtual (CDC) serial / COM port is opened at 1200 baud and then closed. When this happens, the processor will reset, breaking the USB connection to the computer (meaning that the virtual serial / COM port will disappear). After the processor resets, the bootloader starts, remaining active for about 8 seconds. The bootloader can also be initiated by pressing the reset button on the Leonardo. Note that when the board first powers up, it will jump straight to the user sketch, if present, rather than initiating the bootloader.
Is it possible to use something similar to this existing Arduino behavior as a starting point? That is, by default boot into the sketch directly without a delay unless the reason for the reboot is known to have been triggered by the Arduino flashing procedure.
Here is the source code of Caterina, the bootloader used in Arduino Leonardo: https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/bootloaders/caterina/Caterina.c
As mentioned above, because of IRAM usage it is likely that we integrate esp-idf into bootloader only, but not in the app. In this case we can assume that if the device is plugged in, it should wait for upload. If it isn't plugged in, it skips esp-idf completely and starts up as usual. What should we check for at D- line? Iff the device is not connected to the hub, it will be in some particular state? Re Leonardo approach: probably not relevant because low-speed CDC device isn't really a thing which is supported on all OSes.
https://github.com/cnlohr/espusb/blob/master/user/usb.c#L224
I think this may be helpful!
So google can find this: This is a picture of a powered-off ESP8266 being plugged into USB. It takes a moment, but the host does talk to it and enumerates
Re: CDC, etc. Even a bulk endpoint, I'm OK with, but the problem I want to avoid is I just hate it when things are COM ports, so many driver issues and the like!
Is there still some hope that this might happen some day?
I keep kicking the idea around, but, there's just SOOO much. I don't even know how to do the remapping of the IRAM stuff, so I'm pretty stuck at this point. Given a framework, I can totally write the USB code.
Thanks @cnlohr, what exactly do you mean with a "framework"?
I can get messages to/from the host via USB, and detect when it's plugged in or not, but I don't know how writing to flash works realistically, where different things would live in flash, and how to actually boot into flash.
@igrr can you help on the above? I think this would be a tremendous benefit for the whole platform.
FWIW, I'd love to see support for the Arduino Serial, Mouse & Keyboard libraries on ESP8266 with this also. Not sure if that's harder or easier to implement.
No way to do serial, but things like mouse/keyboard/joystick shouldn't be hard. Really, though, I think a bootloader is the critical point!
I agree, the bootloader and firmware updates are more important, but having USB support for Arduino applications would be very useful as well.
(I do realize that the native ESP8266 serial console wouldn't be possible but an application-level serial port should be, no?)
BTW: THANK YOU for this awesome work.
If you're using application-level stuff, you really really really should be using control messages rather than treating something like serial. Control messages allow you to say "Function 5, length 50, here's my payload" which is much better than handling framing, etc. yourself.
Back to discussion at hand: @igrr any ideas on how we could make this happen? Should I just make a super minimal make-usb-link thing that lives in IRAM? Or, is IRAM a bad thing when dealing with bootloaders?
From bootloader point of view, the following functions are needed:
is_usb_plugged_in()
should check if cable is connectedusb_enumerate(void (*cb)(size_t size, const uint8_t* data))
should return when enumeration is successful. Callback function cb
should be called whenever host performs a transfer.usb_transfer(size_t size, const uint8_t* data)
should perform transfer to the host.
I think this should be enough to implement firmware update.All functions should live in IRAM, as we have plenty of it in bootloader, and we don't use flash cache there at the moment.
I'll work on this shortly. I am gonna see if I can do all this with as little of the SDK as possible.
I am thinking the transfer thing might be a little easier to use via global messages, since, data going in and out has to be done asynchronously. I.e. "I got this data last transaction" and "when you get around to it, please send this back to the host" unless I can just have loop continuously on my side...
Hmm, I've got most of my stuff off the ground here, but, it's like the processor is waking up at 52 MHz instead of 80. If I call ets_update_cpu_frequency( 52 );
Everything works as-expected, but I think I should be developing for 80 MHz, no?
When bootloader runs, PLL is not enabled yet, this is done in one of closed source libraries. I would have expected to see 26 MHz frequency, but maybe there is an option to use 2*XTAL... I will check.
I really appreciate that! Will this be an issue potentially for loaded programs? Should I just make my USB stack work at 52 MHz?
For loaded programs — not really, as SDK startup code resets frequency to 80MHz. For the USB stack I can imagine switching from 80 to 52 can be a lot of work... I will see if I can pull some bits from closed source libs to enable 80MHz at boot. Because this is not necessary for normal (non-USB) boot, I want to check if cable is connected first. If it is, I will load the rest of USB code and libphy into RAM, set frequency, and then initialize USB.
Not interested in loading any libs or anything. I was just hoping it would be <10 lines of code. I couldn't find any references to the PLL in any of the registers anywhere (other than all the references to the RF PLLs). Would it be feasable to use rom_rfpll_set_freq
- or was that written for a previous silicon revision? Even if switched with it, I don't know how to actually change the main processor mux to that PLL if that's what's doing it.
Just a thumbs up for seeing the two geniuses of you working together on this.
That part of the 8266 is not publicly documented, so I can't give you any pointers.
I'm on vacation now, back to office next week; will look up those 10 lines of code with are needed to switch frequency. Then I'll make a branch of Arduino with 80Mhz enabled in bootloader, so that you can try running esp-usb there.
Figured I'd leave it here for anyone who needed it, I don't know why it took 4+hours to find, but...
if(rom_i2c_readReg(103,4,1) != 136) { // 8: 40MHz, 136: 26MHz
//if(get_sys_const(sys_const_crystal_26m_en) == 1) { // soc_param0: 0: 40MHz, 1: 26MHz, 2: 24MHz
// set 80MHz PLL CPU
rom_i2c_writeReg(103,4,1,136);
rom_i2c_writeReg(103,4,2,145);
//}
}
Thanks, @pvvx!
That should work :)
Ok, it's a little bigger than I was initially hoping, at 2.5kB, but, it seems to work pretty solid. It's much better than when you have the wifis and everything else going.
https://github.com/cnlohr/espusb/tree/master/bootloader
Right now, it just accepts control messages of length of ~2090 bytes (2048 plus a little bit) back and forth. Check out the "main" function for more info about how it works. You also get to keep the bRequest, wIndex, and other details so you don't need to weigh down the code with a protocol ontop of the control requests. I've tried sending/receiving as quickly as I could and I seem to get around 30kB/sec down at the same time as 30kB/sec up, or 60kB/sec in one direction.
Detection that there is a computer seems to happen in .1 to .3 seconds, so that's good.
I am still trying to figure out a good way to merge the interfaces... Take a look at main and see if you want something a little different?
I think I have a misunderstanding of what bootloaders do. Would the given program have to be compiled for specific operation with /this/ bootloader? I was hoping I would be able to rewrite the IRAM from within the bootloader to make the program "think" it was operating at the base address.
@me-no-dev can you help here? This would be really cool...
By the way, I have tried this code in 'bootloader' directory a few weeks back, and it compiled nicely with eboot bootloader. I couldn't figure out what hardware connections I need between the ESP8266 and USB (which pins are used, are there any passives or diodes needed, and so on).
@igrr There's now a small wiki on this to get started for ppl. It should ease up on development a lot! :D https://github.com/cnlohr/espusb/wiki/Getting-Started-Guide#hardware-schematics-diagram
edit: btw it just needs one pull up resistor (value more than 1k ohm) to 3.3v for it to work.
@igrr I would also be up for sending you a board with USB connections, regulator, etc. already on it.
I looked a few times through the code, but I guess I'm used to different style USB programming (let's say v-usb, libusb and such) and could not at all understand where to hook and how to listen for packets or send any (same for bulk). I have libs and definitions for many devices and have written lots of USB code, but I guess it was not enough :(
@me-no-dev : The fastest way to talk to this thing is via libusb, using specifically control packets. I will admit it is a little weird, but I have a demo that runs on the USB side as well as topside to test data transfer speeds.
@cnlohr I know what to do on the host side :) I want to be able to create different devices on the ESP, maybe even selectable which to start. For example I have code for driverless HID devices, CDC devices, all standard input devices and so on, but have no idea how to convert it to this library and run it on the ESP. I was expecting to have a callback of some sort to give the configuration blocks, callback for when a control packet is received, or bulk data (and bulk endpoint can be more than one) and also to be able to queue packets and bulk data for the host.
@cnlohr maybe we can chat on gitter or something, where I can share some code with you and better explain :)
@me-no-dev let's separate the discussion between the bootloader (i.e., flash the ESP over emulated USB) vs. "userland" (e.g., create different devices on the ESP from sketches, I have openend https://github.com/cnlohr/espusb/issues/24 for that).
@probonopd it's the same exact thing ;)
Great project - I hope it can be nicely integrated with https://github.com/esp8266/Arduino (e.g., to do the uploading and "serial" monitor using just the ESP and no serial adapter).
I also cross-posted this at https://github.com/esp8266/Arduino/issues/2375