XMEGA USB bulk transfer, without ASF (or LUFA)

Go To Last Post
27 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've posted an example of USB bulk transfer for the XMEGA to the projects section.

 

A few weeks ago I posted a USB bulk transfer example project for the AT32UC3B series. My aim was to develop a simple example that didn't use the ASF (or LUFA). At the time, the 32 bit Atmel parts were unfamiliar to me, and the datasheet was obscure in spots, but there was enough information there to figure out what was needed. The example I posted is pretty simple-minded and primitive, but it gets the job done.

 

For a related project, I wanted to do the same for the ATXmega16C4. This turned out to be a bit more difficult.

 

USB on the xmega series works differently than it does for the other Atmel parts I've seen (e.g. AT32, ATmega8u2, SAM3S). There is no dedicated memory either for endpoint registers or for endpoint data buffers. Additionally, the handshaking between firmware and hardware to acknowledge and transfer packets is different.

 

The datasheet pretty clearly explains how the registers and buffers should be set up, so that wasn't much of a problem. What the datasheet doesn't do is describe the details of the enumeration process and how setup packets are acknowledged and replied to, and how to go about setting the address once it's received from the host. This is done with fairly complete text and timing diagrams for the AT32UC3B; there's neither of these for the xmega.

 

So I spent about three weeks of banging my head against the wall, doing trial-and-error and making very slow and sporadic progress. The sticking point – how to set the address – was finally solved when I found this guy's USB stack:

 

https://github.com/nonolith/USB-...

 

Among all the USB stacks for the xmega I've seen online, this one was the least impenetrable, at least so far as finding the one piece of information I needed – how to set the address.

 

My point is that what with better documentation would have taken a few hours instead took me three weeks. I asked Atmel support for help on this, and the reply was that everything happened automatically, and all I had to do was to write the address to the proper register on receiving it, and everything would be fine. Not true, needless to say.

 

Some points:

 

1. In the xmega LUFA stacks I've seen, a cobbled-together assembly routine, LACR16(), is used to clear bits in the endpoint registers. This is no longer necessary:

 

ep0_out_p->STATUS &= ~0x02;

 

for instance, works just fine.

 

2. On the other hand, clearing the transaction complete register cannot be accomplished by

 

USB_INTFLAGSBCLR = 0x02;

 

as you would think; instead, you have to do:

 

USB_FIFOWP = 0;

 

3. Unlike with the AT32UC3B, if the hardware receives an IN token it will immediately grab the data from the IN buffer without waiting for the firmware to write to it. To prevent this, the endpoint IN BUSNACK0 can be set preemptively, and then, after data is written to the IN buffer, cleared to send the data.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, this is very interesting. I'll take a look, I've been thinking abut ways to do a USB bootloader that will fit into 4k.

 

The LACR16 routine dates back to a time when GCC didn't support the new 16 bit access op-codes on the XMEGA devices. Now it does so there is no need to use it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wow, a little over 2k with the serial output removed. Nice work! I'm wondering if I should try to make it work as a bulk endpoint with WinUSB, since I can sign the WinUSB driver now, or if I should work on making it a HID device. The latter sounds like a royal pain.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks - I was going to look at how big it was without the serial stuff, but I've been too lazy so far.

 

DId you compile the host side software under Windows?  If so, how much had to be changed?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also, there's certainly scope to shrink it further.  If you pre-concatenate the configuration descriptor block, you can get rid of the arraycat function and stdarg.h.  Similarly, by putting the string descriptors into arrays beforehand, you can get rid of the section of code that creates them from the strings in descriptors.h and also get rid of string.h.  I think this gets rid of all the #includes and #defines except for io.h and interrupt.h. No idea how much all that saves, though.

 

 You can also save RAM by using  endpoint buffers shorter than the 64 bytes I've set up.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have not tried to compile the host side at all yet. The big problem on Windows is drivers. I have an open source developer signing certificate from unizeto.pl, but it isn't tied to the Microsoft root cert so it can only be used to sign .inf "drivers" that use a Microsoft signed binary. So I can use usbser.sys for CDC and stuff like that, or WinUSB. Well, there is HID too of course. WinUSB seems like the best bet, even if it has some limitations. I'll try to get that working at some point.

 

Thanks for the hints about size. It should be okay for now, I only need to get some simple NVM code in there. A quick look suggests that all the parts with USB have at least 4k bootloaders.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

By the way, what licence is the code under?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I abandoned Windows years ago, with the death of W98, so I don't really know what I'm talking about, but libusb is user-mode, so I wouldn't think the signing thing should come into it at all.

 

As for license - I haven't attached one.  You're welcome to use it without attribution.  I disclaim all responsibility if your house catches fire and your pet ocelot dies, though.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks :-) I'll credit you of course, and the bootloader will be GPL.

 

Unfortunately Windows isn't like Linux with regard to USB drivers. There needs to be a kernel mode driver part and it has to be cryptographically signed. It's a reasonable security measure but can be a bit of a pain for open source people. Even with the Windows port of libusb you will need a signed driver for anything other than a HID device on Windows 8 or later.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hmm.  I should have a look at this issue then.  I just yesterday started (in a general way) to investigate  using Java and  the usb4java library (which uses libusb) to get non-platform-dependent access to usb.  If it isn't going to work without jumping through hoops, I may have second thoughts.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you can't do it as a HID device then you can do it as a WinUSB device, and then just use the libusb driver to talk to it in a platform independent way. I'm not sure of the specifics but there is some chatter about it on the libusb mailing list.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, I see there's a GPL Windows app called Zadig which installs the necessary USB driver(s) and which is signed.  I think I'll wait until I'm around a Windows machine to try to understand the process better.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I also implemented a USB stack which was based on the nonolith, but I added my own optimizations.

https://github.com/ganzziani/XScopes-Firmware

 

I am currently working on a USB bootloader as well.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ganzziani, perhaps we could combine our efforts. What are your goals for the bootloader?

 

I'm still trying to decide if 4k support is worth it. tominsf code seems to work very well, but it is cut down and doesn't implement a full stack so I'm a little bit worried about compatibility in the future. There is also the driver issue. If I only support 8k I can use HID and the ASF and avoid all these problems.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That would be great!

I already have the USB stack working on the bootloader flash section, and I can enter the bootloader while I am running from the application flash, but I don't have any bootloader specific functions implemented. My next step is to transfer the bootloader stuff from any other bootloader project.

How about we do this? I can put the project on GitHub later today, and we can collaborate from there:

https://github.com/ganzziani

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I already started on Github with my code: https://github.com/mojo-chan/hid...

 

What is your goal? Are you trying to support 4k devices? Will it be a custom USB device or HID standard, and if it's custom what do you plan to do about drivers for Windows?

 

The more I think about it the more I tend to think that HID is the way to go. It's slower and needs an 8k bootloader, but the benefit of not needing a driver and of being able to use the ASF code outweighs all that. Tom's code is actually pretty nice, but it's a bit minimal and the lack of support for things like bus events worries me a bit. I could add that stuff in, it isn't that complex, but then I'd have to maintain and debug it and probably start to run up against the 4k limit again anyway. The ASF is fairly mature now and Atmel will doubtless keep it updated if issues are found in the future.

 

The bootloader functionality is fairly trivial really... I have come up with a simple protocol and will a little C app that should be portable. Have a look at the C code, it's far from complete but the bit that reads in .hex files is there already. That bit is worth stealing forking. Or just implement the same protocol and use it as is.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, I need to support 4k devices. My goal is to use the same USB driver as the application code. I am using bulk transfers and on the PC I use WinUSB. I'll check out your code tonight.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Interesting, I'll looking forward to seeing your code too.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I had to do some clean up on the code before posting it. It still needs some work though.

https://github.com/ganzziani/XMEGA-USB_Bootloader

 

Now I have to add custom commands for sending and receiving data, as well as doing the actual writes to flash.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ganzziani, I'm going to upload some more code to Github in the next few days, but could I suggest we standardize on an embedded firmware info block? I propose the following:

 

#define VERSION_MAJOR	1
#define VERSION_MINOR	0


typedef struct {
	char		magic_string[8];
	uint8_t		version_major;
	uint8_t		version_minor;
	uint8_t		mcu_signature[3];
	uint32_t	flash_size_b;
	uint16_t	page_size_b;
	uint32_t	eeprom_size_b;
	uint16_t	eeprom_page_size_b;
} FW_INFO_t;


// data embedded in firmware image so that the bootloader program can read it
volatile const __flash FW_INFO_t firmware_info =	{	{0x59, 0x61, 0x6d, 0x61, 0x4e, 0x65, 0x6b, 0x6f},		// "YamaNeko" magic identifier
														VERSION_MAJOR,
														VERSION_MINOR,
														{ SIGNATURE_0, SIGNATURE_1, SIGNATURE_2 },
														APP_SECTION_SIZE,
														APP_SECTION_PAGE_SIZE,
														EEPROM_SIZE,
														EEPROM_PAGE_SIZE
													};


int main(void)
{
	firmware_info.magic_string[0];	// prevent firmware_info being optimized away

	for(;;);
}

 

The PC side bootloader app can then scan through the firmware image for the "YamaNeko" string and read the data that follows it. The app can then display the version of firmware it is loading, and check that the target MCU matches it etc. If you can think of any other useful information to add to the struct let me know. If we both use the same format then our bootloaders will be somewhat compatible.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've sent you a private message. We can communicate directly thru email.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I just posted a working, somewhat tested update to Github. It now loads the firmware image and calculates the correct CRC.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Spam removed

Ross McKenzie ValuSoft Melbourne Australia

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, Thanks for the work to dig through the "documentation". I was playing with the code and the configuration descriptor is numbered 0  ("0x00,            // 1 ID of this configuration") . It did not work till I made it 0x01. I thought configuration descriptors need to start at 1, 0 is "not configured". am I missing something?

Thanks again,

 

Eric

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

enav wrote:

 I was playing with the code and the configuration descriptor is numbered 0  ("0x00,            // 1 ID of this configuration") . It did not work till I made it 0x01. I thought configuration descriptors need to start at 1, 0 is "not configured". am I missing something?

Configuration number 0 works for me.  The device is "configured" when the host has all the configuration descriptors, usually just one.

 

I'm currently using winusb, not CDC.  I could try CDC if it matters.
 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am on a Linux system so the system may treat things differently. I was using the "vendor class" (class 0xff) not CDC. I do not think that should matter. I looked up the choice of valid config numbers in Jan Axelson's USB book and it confirmed that zero is not valid. Maybe windows is not so strict.

Eric

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah, USB remains a mystery.  The only thing I can find about the configuration number is that it is the number the host uses for the SetConfiguration setup pkt. 

 

I was using 1 also but just tried zero to see if it would work, and it did.  I may have chosen 1 originally because I saw it in someone else's code.

 

The class is also a mystery.  I was going to use vendor class (0xff), but switched to 0x00 for winusb.  I think I saw the zero class in Dean Camera's LUFA.