V-USB: Outputting Data with usbFunctionRead()
Fri, Feb 24, 2012 in post Electronics V-USB tutorials usbFunctionRead V-USB
I promised to commenter Marek to post an example of using usbFunctionRead()
to return larger amounts of data. So building upon the ATtiny85 version we made in last part, let’s add one more command to usbtest.exe:
#define USB_DATA_LONGOUT 5
// [...] Change the buffer size in main():
char buffer[2048];
// [...] Add the following in the if-else structure in main():
} else if(strcmp(argv[1], "longout") == 0) {
nBytes = usb_control_msg(handle,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN,
USB_DATA_LONGOUT, 0, 0, (char *)buffer, sizeof(buffer), 5000);
printf("Received %d bytes: %s\n", nBytes, buffer);
}
Now let’s tell V-USB that we’re implementing usbFunctionRead()
and doing transfers of more than 254 bytes in usbconfig.h
:
#define USB_CFG_IMPLEMENT_FN_READ 1
#define USB_CFG_LONG_TRANSFERS 1
Funny thing is, if you don’t define long transfers, V-USB will use a byte-sized variable for transfer length, and transfers using usbFunctionRead will fail with PC-side buffers longer than 254 bytes. So you have two options:
- Define USB_CFG_LONG_TRANSFERS to 1
- Have the PC-side buffer of 254 bytes or less
For some strange reason, the standard V-USB method of using usbMsgPtr does not fail with the 256-byte buffer I had in the tutorials. Weird. Anyways, after we’ve configured long transfers the return value of usbFunctionSetup needs to be changed. V-USB automatically defines usbMsgLen_t to be the correct data type, so we’ll use that. Here are the changes to main.c:
// [...] Add the define and a data counter for USB_DATA_LONGOUT
#define USB_DATA_LONGOUT 5
static int dataSent;
// [...] Change the return type from uchar to usbMsgLen_t
USB_PUBLIC usbMsgLen_t usbFunctionSetup(uchar data[8]) {
// [...] Add handling of USB_DATA_LONGOUT
case USB_DATA_LONGOUT: // send data to PC
dataSent = 0;
return USB_NO_MSG;
// [...] Add new function usbFunctionRead
USB_PUBLIC uchar usbFunctionRead(uchar *data, uchar len) {
uchar i;
for(i = 0; dataSent < 1024 && i < len; i++, dataSent++)
data[i] = '0'+i;
// terminate the string if it's the last byte sent
if(i && dataSent == 1024)
data[i-1] = 0; // NULL
return i; // equals the amount of bytes written
}
We're using the dataSent
variable to keep track how many bytes we've returned and quit once 1024 bytes is sent. Once usbFunctionRead returns a value less than "len" parameter, the transfer is automatically terminated.
Voila! Note that using long transfers finally pushes the firmware size over 2048 bytes, so ATtiny2313 cannot be used anymore unless you trim something out or maybe optimize main.c for size. With ATtiny85 I'm using now it's not a problem. You can download the zip if you like. :)
29 comments
Marek:
Thank you very much! :-)
Marek
Marek:
Hello Jokkebk,
Just letting you know, that I will use V-USB, thanks to the knowledge I’ve got from you in a university project. When it’s finished, I’ll post some sources here with your permission. It is going to be an atmospheric data logger – the MCU (ATmega8) will be measuring temperature and pressure and storing it in an external EEPROM along with the time of measurement (there will be an RTC on board). The measuring time step and other config will be set using USB and stored in ATmega’s internal EEPROM. For the PC interface: It will be written as a standart console application and there will be a GUI written as a form application in Visual C++ 2008 ( Windows is a condition for the project… :-(, but it does not matter, as it will be only a GUI )
Thank you
Regards,
Marek
jokkebk:
Cool! Good luck with your uni project, and great if you publish the sources here! If you like, you could even be in touch with me beforehand via e-mail, we could even publish it as a separate post. :)
Kath:
I was searching for some vUSB explainations and came to your site. And, it’s great to see such work. What I was looking for is, I already have a project for my university in which the RS232 / UART is used to transmit a continuous stream of data with the following format:
The above pattern is transmitted at rate of 100 Hz from the micro-controller to the PC. Also, there is a control command from the PC side after which the stream of the data starts or stops.
Now, I want to convert it to a USB version. In addition, I did a little experiment with the vUSB LED example found on the vUSB site. Can you give me some proper direction to accomplish this ? Because seeing your works I am getting a feeling that it should be possible to transmit the above data via USB.
And finally,
thanks and keep up the good work !
Kath:
It seems that the format is gone somehow.
The format is
32 bytes of string data + linefeed + carriage return
jokkebk:
Yes, it should definitely be possible. In the USB protocol, the transfers are host-initiated, and if I have understood it correctly, the maximum throughput of USB 1.1 is achieved with the host polling for data every 1 ms. So 10 ms is definitely within the working parameters.
However, to set up this polling, you need to define an interrupt endpoint for V-USB and communicate to the host the polling frequency. I have not tried it so I cannot help you on how this is achieved.
However, I do suspect that the polling system _is_ the same one that is used with USB keyboard and mice, for which I do have examples, so you could find some help there on how to actually implement the firmware code, once you get the host to poll for data every 10 ms.
Good luck with your project!
Kath:
Thank you very much for your time. I will try to follow and let you know what happens.
Kath:
Not sure if it is a stupid idea, but can I use the control messages via USB_ENDPOINT_IN for this purpose like you have shown ? If I loop through the commands after every 10ms, will I get the expected ‘polling frequency’ or will it be unsafe to do so (i.e. jamming of the USB bus, or the libUSB driver) ?
Because the USB_ENDPOINT_IN have a inward buffer for 255 bytes ( I am using ATmega8, so should be able to use all of the space unlike the tiny ones, I guess!), my requirement of sending 32 bytes of data from AVR to PC is fulfilled. Only thing to know is does doing it this way causes any instability ? If not, then I guess this should be pretty straight forward according to your tutorial part 4.
jokkebk:
Yes, that is definitely possible, and I don’t think polling manually every 10ms is going to choke the USB bus in any way.
And it is a lot easier than figuring out how to do data transfers with interrupt endpoint. :) Plus, in any case, you can try that later once you get the control message method working!
Kath:
Thanks for the heads up :) Lets see if it works!
Marek:
Ok :-)
I am working on it right now :-) nearly a half of it is already working on a breadboard.. But I am quite busy this week, so i am not sure to make it work completely this week, i would be rather this moth.
I’ll keep in touch.
Marek
Marek:
Good evening :-)
I have quite advanced in my project, but i have still one problem, that i can’t solve… after some time of no communication (the device is only left connected in the usb port, but no transfers are being done) the device just disconnects, not in windows, but when i want to access it, it fails.. can you give me an advice?
Thank you,
Marek
jokkebk:
Hmm, unfortunately I don’t have any experience on long-running usb devices. You could try asking V-USB forums on the issue, it might be something related to the clocks falling out of sync, or maybe Windows sends some “sleep” command to the device that the library does not understand.
Two workarounds that spring to mind would be to poll the device every once in a while (I recall USB can be set to poll a device automatically with some endpoint configuration, so that could even be done automatically, but you’d need to research that yourself), or simply just have a “reset”-button that either resets the whole circuit, or is handled in your code which then resets just the USB (reset code is just before the main loop, you could just copy that).
Kath:
I did experiment with the manual looping for transferring 8 bytes (default payload data in endpoint 0). Also, I reduced the timeout parameter (given 5000 in the PowerSwitch example from vUSB) of usb_control_msg to 10 and also inserted a 5ms delay in the loop. So, I get total 15ms per iteration. That gives about 66.667 Hz data rate which is good at this moment. I am not sure what is the unit of the timeout parameter though! I guess it is in milli-seconds?) And, lowering it to 1 just causes libusb to give error in USB communication.
It works, but while looping, my Core 2 Duo CPU is working at about 60-70% load. I wonder what is the problem here. When I use the USBasp the processor meter hardly shows any load change. So, why does it happen when I use the looping method ?
And, I don’t know how the PC side client of USBasp communicates with the HW. Any idea ? FYI, from the firmware it is seen that it uses the endpoint 0, usbFunctionRead(…) and usbFunctionWrite(…). Can you tell me anything about the transfer process of the host?
jokkebk:
Nice to hear that you got it working that way! Which side did you add the 5ms delay to? PC side? AVR side should not need any, I think.
http://www.beyondlogic.org/usbnutshell/usb4.shtml contains information about different transfer modes. Based on this, only the control and interrupt endpoint transfers are available for low speed devices. You could take a look at my USB mouse example to see the main function loop that sends data over interrupt endpoint, but I’m not sure how to make the PC poll that on a non-HID device, so you’d need to find out that yourself.
I took a cursory look to USBasp firmware source code, and it seems to work with control transfers, the data sent/received is probably just longer than the 8 bytes at a time (it’s using usbFunctionRead and Write as you noted). So no additional techniques there.
Kath:
Yes, you are right. I did add the delay on the PC side’s loop. And without the minimum value of 10 for timeout parameter in the usb_control_msg libusb gives error.
Again, while looping, my Core 2 Duo CPU is working at about 60-70% load. I wonder what is the problem here. When I use the USBasp the processor meter hardly shows any load change. Now I am trying to write a DLL and a VB GUI to see if that performs better.
Can you please direct me to any link or documentation on how(source code perhaps?) the PC side client (e.g. extremeBurner AVR, AVR-DUDE etc… ) communicate with the USBasp hardware.
jokkebk:
You could download avrdude sources and take a look in there. From a quick peek, the file usbasp.c would be quite a safe bet. For example the …_transfer -function seems to use libusb in quite a normal way.
As a standard CPU utilization debugging method, you could try profiling your code (I think google will have useful info on turning on gcc’s profiling methods), or just printing out the time used by various parts of code. My guess would be that if you don’t have anything else than libusb function calls and a delay in your innerloop, the libusb control transfer function is the culprit, but doesn’t hurt to be sure. :)
Kath:
I have good news! I wrote a DLL file and also a GUI for my purpose. Now I can achieve 8 bytes data transfer at about 90 Hz. So about 90*8 = 720 bps transfer rate. And the best part is, CPU is barely used for 1~2% in this way. So, this is a good information for anyone who want to transfer data at this rate via v-USB through endpoint 0.
I think the Command Prompt did all the sluggishness or there is other issues of the console application. So, I will now make the program a bit efficient as far as I can, and also will try to manage the memory in a better way.
Again, thank you very much for the heads up. :) Can I add you on facebook for quicker communication ?
jokkebk:
Wow, quite an improvement! Nice to hear about your progress. Strange that the console application is so much heavier, it might have something to do with how GCC version handles USB communication or something.
Unfortunately I use FB solely for RL stuff, but you can e-mail me at jokkebk … codeandlife.com if you like :)
Marek:
Good morning!
I write just to tell you, that my project of temperature/pressure/voltage dataLogger is getting finished. I have already completed GUI which allows graphing and some stats, but i have only done a czech version so far… Nontheless the command-line app which controls the USB bus and which can be used to read the datalogger and export all the information into a .csv file is written internationally = in english. Currently i am working on a double sided PCB to make it look as a USB flash drive. I think i’ll make another version with THT components to make it simpler to assembly.. I’ll be back when i have finished completely and assembled… I’ll send you then some photo and some comments and source codes of the GUI, console app, and firmware and schematics if you wish to post it…
Yours,
Marek Novak
declan smith:
Here is GUI for testing V-USB using libUSB dll.
Any suggestion welcome.
Download:
http://www.ziddu.com/download/21008674/V-USBtester32.zip.html
jokkebk:
Wow, looks quite cool!
declan smith:
@jokkebk
I was able to made my V-USB device works because of your tutorial, so I tried to make a simple tester to be an easy host software (because I had usually confused by command line and firmware for checking errors :)) ), I hope it usefull and helps beginner.
Any suggestion for improvement welcome :)
george:
I just noticed that the schematic.png in the zip file is the old one without the 2.2 kOhm resistor. It may confuse someone.
jokkebk:
Good catch! Thanks, fixed that.
Karan:
Sir, When I am trying to receive data through usbfunctionread in my pc I can transfer only a maximum of 7 bytes. If the size is more, it fails.
Also I get this error “../USBTrial.c:20: error: conflicting types for ‘usbFunctionSetup'”
when I do USB_CFG_LONG_TRANSFERS to 1.
Sir please quickly help me out….
jokkebk:
The “conflicting types” error means that a header (.h) file defines the return type or parameters a bit differently than the actual function declaration in a source (.c) file, for example “void foo(int x)” in header but “int foo(int x)” or “void foo(unsigned char x)” in source.
I don’t have a quick fix for your problem, only idea that springs to my mind is that make sure you are not trying to send more than 8 (or maybe 7?) bytes at a time with usbFunctionRead, as the V-USB buffer is only 8 bytes if I recall correctly (I could also be wrong and it might be larger). For more data than that, you need to send it chunk by chunk. If you’re using my example code it should that automatically, though.
simon:
hi
first thanks for your great post on vusb .
how to Inputting Data with vusb in hid mode ?
how to get hid report buffer data ?
thanks
kom46:
Hi! I think your example can’t work.
1. In function usbFunctionRead uchar i, uchar len have maximum value 255, int datasent -32767. Therefore data[ i] will never have more than 255 elements in for (i=0;dataSent<1024 && i < len; i++,dataSent++) data [i] =’0’+i; No reason to write 1024 here. If (i && dataSent ==1024) data[i-1] =0; will not work.
2. I tried to use
char buffer[2048];
… usb_control_msg(…,…,0,0,buffer, sizeof(buffer)…);
It doesn’t work until I changed char buffer[250];