AVR ATtiny USB Tutorial Part 3

This is the third part of my USB tutorial for ATtiny2313 and V-USB library. In the second part we got the breadboard setup more or less covered, and now is the time for actual code! This will most likely be the longest of the three parts, so let’s get started.

Adding V-USB as a part of your project

First, we will download the latest version V-USB library from OBdev. Head to the Downloads-section and get the latest .zip – I got vusb-20120109.zip.

Unzip the archive and copy the usbdrv subfolder to your project folder (the whole folder, not just contents). Go to the subfolder and make a copy of usbconfig-prototype.h with the name usbconfig.h. Locate the #define lines for IO port and port bits and clock rate, and update them as necessary to reflect our configuration where D+ is in PD2 and D- in PD3 and clock rate is 12 MHz:

#define USB_CFG_IOPORTNAME      D
#define USB_CFG_DMINUS_BIT      3
#define USB_CFG_DPLUS_BIT       2
#define USB_CFG_CLOCK_KHZ       12000


It’s also a good idea to ensure that V-USB tells the computer that it is powered via USB (i.e. not self powered) and takes maximum of 50 mA of power (defaults in my version is USB power, 100 mA max.):

#define USB_CFG_IS_SELF_POWERED         0
#define USB_CFG_MAX_BUS_POWER           50

We will be using OBdev’s licenced vendor and device IDs, so they don’t need to be changed (note that this also means my tutorial will fall under GPL, including the schematic in part 2). But we do want to customize the vendor name and device name (note the backward slash in vendor name string used to split the #define to two lines so it fits into this blog post):

#define  USB_CFG_VENDOR_ID       0xc0, 0x16 /* = 0x16c0 */
#define  USB_CFG_DEVICE_ID       0xdc, 0x05 /* = 0x05dc */

#define USB_CFG_DEVICE_VERSION  0x00, 0x01

#define USB_CFG_VENDOR_NAME     'c', 'o', 'd', 'e', 'a', 'n', 'd', 'l', \
                                'i', 'f', 'e', '.', 'c', 'o', 'm'
#define USB_CFG_VENDOR_NAME_LEN 15

#define USB_CFG_DEVICE_NAME     'U', 'S', 'B', 'e', 'x', 'a', 'm', 'p', 'l', 'e'
#define USB_CFG_DEVICE_NAME_LEN 10

The usbconfig.h header file is well documented so you may want to read the rest of the options to get an idea what the library can can do. Now the only thing missing is the actual C file to use the library. Here is the barebones version of main.c we’ll start with:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>

#include "usbdrv.h"

#define F_CPU 12000000L
#include <util/delay.h>

USB_PUBLIC uchar usbFunctionSetup(uchar data[8]) {
	return 0; // do nothing for now
}

int main() {
	uchar i;

    wdt_enable(WDTO_1S); // enable 1s watchdog timer

    usbInit();
	
    usbDeviceDisconnect(); // enforce re-enumeration
    for(i = 0; i<250; i++) { // wait 500 ms
        wdt_reset(); // keep the watchdog happy
        _delay_ms(2);
    }
    usbDeviceConnect();
	
    sei(); // Enable interrupts after re-enumeration
	
    while(1) {
        wdt_reset(); // keep the watchdog happy
        usbPoll();
    }
	
    return 0;
}

The code should be pretty straightforward to understand:

  • Include the usbdrv.h to access V-USB functions
  • Implement usbFunctionSetup() to handle USB requests (we’ll do that soon)
  • In the main function, set up a 1 second watchdog timer that resets the microcontroller if 1000 milliseconds pass without a call to wdt_reset()
  • Call usbInit() to initialize the V-USB library
  • Enforce USB device re-enumeration using usbDeviceDisconnect(), a 500 ms delay (while calling the watchdog reset every 2 ms) and usbDeviceConnect()
  • Enable interrupts
  • Loop forever while calling the watchdog reset and usbPoll()

The reason we are using the watchdog timer is, that our code might for some reason freeze (for example, corrupt data is read and a bug or eternal loop occurs) and the USB device would then stop responding. In this case, the wdt_reset() gets no longer called and after 1 second, the watchdog timer automatically resets our ATtiny, and the program execution starts again (like you’d just resetted the circuit yourself). While this is not absolutely necessary, it is a good practice and saves the user from unplugging and replugging the device if something strange happens.

Another thing you may wonder is why the disconnect/delay/connect -procedure is needed at all. This is because the host PC can remember the identifier assigned to a USB device even if our device resets and forgets that identifier. By enforcing re-enumeration we make sure that both the host PC and our device have the same ID used to communicate over the USB bus.

Now let’s see if we can get it all to compile. The easiest way is to use my ready-made Makefile which also contains the necessary statements for our PC side command-line client. Put the makefile to your project folder and run make main.hex too see if it works. This way, you also see the actual commands the Makefile is used to run – there is nothing special with V-USB, the main things you need to remember is to have -Iusbdrv in place for the compiler to find the .h files from usbdrv folder, and then just having the three additional object files (usbdrv.o, oddebug.o, usbdrvasm.o) put into the same .elf file along with main.o.

Responding to USB control messages

Before we head to PC side code, let’s make our device firmware respond to two basic commands sent via USB: Turning a LED on and off. USB communication is initiated from host side and so-called control messages are sent to make USB device return it’s name or any other desired function. The specification also has room for vendor-specific control messages. Let’s define two such messages:

#define USB_LED_OFF 0
#define USB_LED_ON  1

Now V-USB library automatically calls our usbFunctionSetup() method when it receives a vendor-specific control message. The parameter is a 8-byte buffer actually containing a structure called usbRequest_t that is defined in usbdrv.h. I suggest you to check that definition out. At this point we are interested in the bRequest property that will either be 0 (USB_LED_OFF) or 1 (USB_LED_ON) – we will shortly see how our command-line client on PC side will generate these control requests, so there is nothing magical happening here – we just send a request from PC side and interpret it on the other side. Here’s the modified function to turn the LED on or off:

// this gets called when custom control message is received
USB_PUBLIC uchar usbFunctionSetup(uchar data[8]) {
    usbRequest_t *rq = (void *)data; // cast data to correct type
	
    switch(rq->bRequest) { // custom command is in the bRequest field
    case USB_LED_ON:
        PORTB |= 1; // turn LED on
        return 0;
    case USB_LED_OFF: 
        PORTB &= ~1; // turn LED off
        return 0;
    }

    return 0; // should not get here
}

Of course for this to work we need to set PB0 as output in the beginning of main() (I suggest just after the “uchar i;” line):

    DDRB = 1; // PB0 as output

Now we’re ready to deploy our code to the chip. Just call make flash and the dependencies should take care of recompiling the main code file automatically.

Windows driver hell

Now if you are doing this project on a Linux box, congratulations, you likely can just get LibUSB and compile the command-line client to talk to your new device. On Windows, however, you will need a custom device driver. Especially nasty was the fact that older example projects on V-USB site don’t work with 64-bit Windows 7 at all. For this reason, I’ll outline the basic steps for creating and installing your own driver.

Now immediately after you have flashed the ATtiny you should hear the standard Windows fanfare di-dum sound that tells a new USB device has been plugged in. Windows will look for a driver and most likely fail. This is where libusb-win32 comes to the rescue:

  1. Go to the libusb-win32 home page (google if it the above link doesn’t work)
  2. Locate the heading “Device driver installation” and follow the instructions to download the latest version. I used libusb-win32-bin-1.2.6.0.zip. Unzip to a subfolder called “libusb” in the project folder.
  3. Run the INF-wizard using the instructions, put the installer somewhere you like (I recommend a new driver-subfolder in your project folder)
  4. Easiest way to install the driver is to do it as the final step of INF-wizard, there’s a button for it
  5. Replug your device and pray for the best

I could have a separate tutorial covering my trial-and-error methods of getting the USB device driver to install and work. I hope you have better luck. In case of problems, here are some helpful tips:

  • You can use Windows Device Manager to uninstall wrong drivers for your device, or if you get and “Unknown device”, update drivers for the device
  • Select “Browse my computer for driver software” and point Windows manually for correct location. Do not mind any “drivers not certified” -alerts, to go through them
  • Sometimes Windows manages to cache the device in a certain port and no matter how many times you unplug and replug it, it gets recognized wrong. Changing USB port or switching even to a different hub (e.g. from computer case front to back) helped me in some cases
  • Restarting the machine or googling for tools to reset USB cache may help, too :)

This is what the INF-wizard (located in the bin-subfolder of libusb) looks like if you are lucky (USBexample device visible):

This is what you can also enter manually if you are not so lucky:

After clicking Next, you get to save your generated driver to a folder. It’s a good idea to use “Install now…” to avoid installing the drivers manually. My installation hanged 50 % of the time (needed to wait 5 minutes for the installer to exit), but the driver still seemed to install OK.

Command-line client

Congratulations! We are almost there! Now we only need to prepare the host-side software. For compiling it, I recommend the GNU C compiler gcc from MinGW project and the MSYS tools that you should be able to install along with it, but probably Visual C and others work just fine. MinGW installer is really hard to locate (big thumbs down for the wiki authors on usability), but currently trial and error should eventually get you here.

We already installed libusb-win32 in the previous section, and the needed library and header files are all there. Just copy (or rename) the oddly named lusb0_usb.h to usb.h in the libusb/include folder and we’re good to go.

First thing we’ll need to do is to have a little helper function to decipher USB descriptor strings used for vendor name and device name. For this and also later for communicating with our device, we will use usb_control_msg() function provided by libusb. You basically pass the device handle, direction of message (USB_ENDPOINT_IN for device to host and USB_ENDPOINT_OUT for host to device) and other information used to define the recipient and direction, and the control message request, index and value codes, as well as a buffer for input/output, buffer size and maximum timeout for the call.

For those who want to understand the control messages better, I warmly recommend the surprisingly user-friendly USB 2.0 specification. The part on control messages starts on page 248 and the first table concerning this should be Table 9-2. You can find the download link for the spec from part 1 of this tutorial. The specification is rather simple and defined constant values closely reflect the #defines in libusb so you should understand everything rather well just by comparing the spec and my example code.

If you wonder what the rest of our helper function does after calling usb_control_msg, it’s basically checking the return value and response length, and converting it from UTF16-LE to Latin1 (same as ASCII if there’s no special characters). The USB descriptor return format is strictly defined to remove any possibilities for interpretation. This is all explained in chapter 9.5 of the USB specification. So here’s the helper function (it’s basically a slightly revised version of the one used in V-USB PowerSwitch example):

/* Used to get descriptor strings for device identification */
static int usbGetDescriptorString(usb_dev_handle *dev, int index, int langid, 
                                  char *buf, int buflen) {
    char buffer[256];
    int rval, i;

    // make standard request GET_DESCRIPTOR, type string and given index 
    // (e.g. dev->iProduct)
    rval = usb_control_msg(dev, 
        USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_ENDPOINT_IN, 
        USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + index, langid, 
        buffer, sizeof(buffer), 1000);

    if(rval < 0) // error
        return rval;

    // rval should be bytes read, but buffer[0] contains the actual response size
    if((unsigned char)buffer[0] < rval)
        rval = (unsigned char)buffer[0]; // string is shorter than bytes read

    if(buffer[1] != USB_DT_STRING) // second byte is the data type
        return 0; // invalid return type

    // we're dealing with UTF-16LE here so actual chars is half of rval,
    // and index 0 doesn't count
    rval /= 2;

    /* lossy conversion to ISO Latin1 */
    for(i = 1; i < rval && i < buflen; i++) {
        if(buffer[2 * i + 1] == 0)
            buf[i-1] = buffer[2 * i];
        else
            buf[i-1] = '?'; /* outside of ISO Latin1 range */
    }
    buf[i-1] = 0;

    return i-1;
}

Now we can use this helper function when we iterate over USB devices to recognize our own device with vendor name “codeandlife.com” and device name “USBexample”. The libusb-win32 API documentation gives us a good starting point in the examples section. I won’t be hand-holding you too much on this function, the basic logic is just to loop through every USB bus and every USB device in them, open a device and ask it’s vendor and device name, return it if it matches or close and proceed to next one if it does not:

static usb_dev_handle * usbOpenDevice(int vendor, char *vendorName, 
                                      int product, char *productName) {
    struct usb_bus *bus;
    struct usb_device *dev;
    char devVendor[256], devProduct[256];

    usb_dev_handle * handle = NULL;

    usb_init();
    usb_find_busses();
    usb_find_devices();

    for(bus=usb_get_busses(); bus; bus=bus->next) {
        for(dev=bus->devices; dev; dev=dev->next) {			
            if(dev->descriptor.idVendor != vendor ||
               dev->descriptor.idProduct != product)
                continue;

            /* we need to open the device in order to query strings */
            if(!(handle = usb_open(dev))) {
                fprintf(stderr, "Warning: cannot open USB device: %sn",
                        usb_strerror());
                continue;
            }

            /* get vendor name */
            if(usbGetDescriptorString(handle, dev->descriptor.iManufacturer, 
                                      0x0409, devVendor, sizeof(devVendor)) < 0) {
                fprintf(stderr, 
                        "Warning: cannot query manufacturer for device: %sn", 
                        usb_strerror());
                usb_close(handle);
                continue;
            }

            /* get product name */
            if(usbGetDescriptorString(handle, dev->descriptor.iProduct, 
                                      0x0409, devProduct, sizeof(devVendor)) < 0) {
                fprintf(stderr, 
                        "Warning: cannot query product for device: %sn", 
                        usb_strerror());
                usb_close(handle);
                continue;
            }

            if(strcmp(devVendor, vendorName) == 0 && 
               strcmp(devProduct, productName) == 0)
                return handle;
            else
                usb_close(handle);
        }
    }

    return NULL;
}

Oh and the 0x0409 is the language code for English, this is also found from the USB specification. Note how the vendor and device names need to be fetched using our helper function – the standard device descriptor in dev only tells the parameter values we need to use for usbGetDescriptorString() to get them (fields iManufacturer and iProduct). This is of course understandable as device descriptor is constant length but the vendor and device names have varying length.

Now that we got past all the sillyness of helper functions to scan all USB devices and return their specially formatted descriptor messages in order to get one simple device handle, the rest of the code is very straightforward. Only thing making the USB communication different from standard structure of accessing a file are the calls to usb_control_msg(), otherwise the general structure of open / do stuff / close applies:

int main(int argc, char **argv) {
    usb_dev_handle *handle = NULL;
    int nBytes = 0;
    char buffer[256];

    if(argc < 2) {
        printf("Usage:\n");
        printf("usbtext.exe on\n");
        printf("usbtext.exe off\n");
        exit(1);
    }

    handle = usbOpenDevice(0x16C0, "codeandlife.com", 0x05DC, "USBexample");

    if(handle == NULL) {
        fprintf(stderr, "Could not find USB device!n");
        exit(1);
    }

    if(strcmp(argv[1], "on") == 0) {
        nBytes = usb_control_msg(handle, 
            USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, 
            USB_LED_ON, 0, 0, (char *)buffer, sizeof(buffer), 5000);
    } else if(strcmp(argv[1], "off") == 0) {
        nBytes = usb_control_msg(handle, 
            USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, 
            USB_LED_OFF, 0, 0, (char *)buffer, sizeof(buffer), 5000);
    }

    if(nBytes < 0)
        fprintf(stderr, "USB error: %sn", usb_strerror());

    usb_close(handle);

    return 0;
}

Note that the second parameter for usb_control_msg now uses USB_TYPE_VENDOR to indicate we are sending a vendor-specific control message. You can also see that the request, value and index parameters (here USB_LED_ON/OFF, 0, 0) can be freely used to communicate with our code on device side.

You can grab the complete code for usbtest.c from project zip file with two #defines that match the ones in main.c and compile with make usbtest.exe (actually just “make” suffices, as usbtest.exe is one of the default targets). Now you should be able to turn the LED on and off using commands “usbtest on” and “usbtest off”. Note again that you need MinGW installed and “gcc” working in order to do this. The full compile command that “make” runs is:

gcc -I ./libusb/include -L ./libusb/lib/gcc -O -Wall usbtest.c -o usbtest.exe -lusb

Wow! That is all. I think this is enough for anyone to grasp in a single reading, so I will be splitting additional V-USB tricks into a fourth part of this tutorial that I will write later. Spending 5 hours in one setting to write this one is enough. :)

Proceed to the next part of this tutorial

195 Comments or trackbacks to AVR ATtiny USB Tutorial Part 3

Marek:
February 1, 2012 at 10:49

A very good tutorial! The led blinks :-) :-) I am just wondering how to pass an integer to the MCU ATtiny? I saw there is a variable wValue that is uint16, but somehow it doesn’t work… Can you help me?

Thanks,

Marek

reply

jokkebk:
February 1, 2012 at 11:08

Thanks! The two additional parameter values to usb_control_msg that are 0, 0 in the example should be able to transmit any two 16-bit integers to your ATtiny. For example, try

nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, USB_LED_ON, 123, 234, (char *)buffer, sizeof(buffer), 5000);

The 123 and 234 should be visible on ATtiny side also, in wValue and the other …Index fields. I’ll test it myself this evening. :)

reply

Marek:
February 1, 2012 at 12:28

@Marek
I am sorry, I figured it out using my library for character LCD module and displaying rq->wValue.word in binary using something like that.
for(unsigned char i=0;i<16;i++)
LCDdata(((data&(1<>(15-i))+48);

}
My problem is not in the transfer protocol but elsewhere…
Maybe somebody had the same problem and find some help in that comment, so again: Both V-USB and LIBUSB are working well!

reply

Marek:
February 1, 2012 at 12:30

@jokkebk
Thanks for such a speedy reply :-) I figured it out, the problem was in my other function using that values….

THANKS!

reply

Marek:
February 1, 2012 at 12:30

@Marek
BTW: I am using ATmega8 and woking well :-)

reply

Marek:
February 1, 2012 at 12:31

@Marek
*working

reply

Marek:
February 1, 2012 at 12:34

But anyway, I wonder why in the specification of USB2.0 you citated in the part 2 the bRequest is 16bit long and in V-USB it is 8bit long.. but i doesn’t matter for me, I’d like just to ask if there is a possibility to make bRequest be 16bit long in V-USB. Is it a modification of the protocol?
Thanks..

reply

jokkebk:
February 1, 2012 at 16:35

Good to hear that the tutorial code is working, also on ATmega8!!!

At least my USB 2.0 spec page 248 says bRequest is 1 byte, and wValue, wIndex are 2 bytes. The first byte at offset 0 seems to be bmRequestType. So V-USB seems to be correct. :)

reply

Marek:
February 2, 2012 at 13:01

@jokkebk
Good afternoon,
Please could you publish here how to make the transfer of data in the other direction using V-USB and libusb? (Attiny to PC) I’ve read some passages from the USB2.0 specifications but I don’t grasp the practical use of the stuff… :-(
If you are busy, please give me some good source and I will try my best.. :-)
Thanks!

Greetings from the Czech Republic
Marek

reply

jokkebk:
February 2, 2012 at 13:32

I’m planning of doing the 4th part of the tutorial this weekend with the exact content for buffered transfers from and to ATTiny.

If you’re impatient, basically you only need to change ATtiny side usbFunctionSetup and V-USB will do the rest, you should be able to read “Hi!” from PC side buffer and nBytes should be 4 after this:

static uchar message[4] = “Hi!”;
usbMsgPtr = message;
return 4;

The LED on/off messages are already of type device->host, the part 3 example code was just currently returning zero-sized response. Additional code example can be found from V-USB custom device example (firmware side main.c). Once you understand this you can probably figure how to return a bit longer or shorter responses or no response at all (return 0) in your function. There’s a limit on how long the response can be, I don’t remember it but you can find it in V-USB docs (and my upcoming part 4).

reply

Marek:
February 2, 2012 at 20:31

@jokkebk
Thank you very much for your advice, I’ll give it a try.

Looking forward to the 4th part. :-)
Marek

reply

Jobin:
February 2, 2012 at 21:07

Thank you Jokkebk very much for the tutorial… I will work with hardware on this weekend and will let you know the feedback.And firmware code is compiling in AVR Studio 4.

Also please let me know why we are using the below mentioned functions.
usbInit(); and usbPoll();
As of now i commented on the AVR Studio for the compilation.

Jobin Jose

reply

Jobin:
February 2, 2012 at 21:10

Jobin :
Thank you Jokkebk for the tutorial… I will work with hardware on this weekend and will let you know the feedback.And firmware code is compiling in AVR Studio 4.
Also please let me know why you are using the below mentioned functions.
usbInit(); and usbPoll();
As of now i commented on the AVR Studio for the compilation.
Jobin Jose

reply

Marek:
February 2, 2012 at 22:53

@jokkebk
IT WORKS!
Thanks :-)

reply

jokkebk:
February 4, 2012 at 15:14

@Jobin
The usbInit() function intializes the V-USB library and is needed for it to function. usbPoll() is also part of V-USB and it does all the heavy work for us – checks if there is data to be received over USB and calls our usbFunctionSetup with all the data prepared. usbPoll needs to be called quite frequently (V-USB documentation had the exact requirements), otherwise USB functionality just won’t work.

reply

Jobin:
February 5, 2012 at 10:17

Thanks Jokkebk … So i will add those two from V- USB. Thanks lot .. will post you the updates. For PC to communicate i want to use Lab View. Will share once it is up …

reply

Stan:
February 9, 2012 at 19:43

V-USB is very intuitive to use, highly recommeded. There is bit of learning curve, once passed possibilities are endless.

I did an example project a while back that includes SHT-11 sensor, if anyone interested:
https://github.com/baracudaz/usb-sht

reply

jokkebk:
February 10, 2012 at 10:31

@Stan
Your project looks nice! Although I don’t know what that sensor does. :)

reply

Phillipp Stengel:
February 14, 2012 at 0:31

Hey,
hope anybody can help me. I’ve got the newest version of the CrossPack for Mac installed. And I thaught this would be a good base for compiling the usbtool of VUSB. But I’m just getting errors and warning this way:
gcc -o usbtool opendevice.o usbtool.o
Undefined symbols for architecture x86_64:
“_usb_get_string_simple”, referenced from:
_usbGetStringAscii in opendevice.o
“_usb_control_msg”, referenced from:
_usbGetStringAscii in opendevice.o
_main in usbtool.o
“_usb_find_busses”, referenced from:
_usbOpenDevice in opendevice.o
“_usb_find_devices”, referenced from:
_usbOpenDevice in opendevice.o

I tried to set the target system to 32bit but even if I do this the error is there and I’m told symbolic links for the chosen system are missing.

reply

jokkebk says:
February 17, 2012 at 13:01

@Phillipp Stengel

It sounds like you’re missing the USB library file containing the said functions. Also, if CrossPack for Mac is the AVR development environment, you naturally cannot compile any PC/Mac side software, you need the normal gcc environment for Mac for that (and something like libusb for mac). I don’t have a Mac so cannot help you further than that, sorry. :) It seems the “standard” libusb does support Mac so compiling with it should be straightforward: http://www.libusb.org/

reply

Ragy:
February 17, 2012 at 12:56

Thank you for this very good tutorial :) :) :)
Thank you very much

reply

Lloyd:
February 18, 2012 at 1:55

Hello,
After i managed to get the rest workin, i have a problem with the host software. When i start the programm compiled with your make and usbtest.c the consol dissapeares directly. But with a bit testing i found out that argc < 2 when i start the Programm, and beacause of the exit(1); it dissapeares. Any idea where i go wrong?

But thanks for this great tutorial!

reply

jokkebk:
February 18, 2012 at 11:32

@Lloyd
It’s a command-line tool so you need to pass it at least 1 argument – just double-clicking the icon will very briefly run the program (which will display usage, but too fast to see :). So fire up the command-line prompt and try “usbtest on” and “usbtest off” to see if LED lights.

If you have passed command-line arguments and it still exits, there’s a problem with the compiler…

reply

Lloyd:
February 18, 2012 at 16:46

@jokkebk

Big thanks now it works ;D

reply

Nahian:
February 24, 2012 at 13:08

thanks for the great tutorial.. I am getting the error in avr studio 4

make: *** No rule to make target `usbtest.c’, needed by `usbtest.exe’. Stop.
Build failed with 1 errors and 0 warnings…

please suggest

reply

jokkebk:
February 24, 2012 at 14:08

@Nahian
You should have these lines in your Makefile:

CMDLINE = usbtest.exe

$(CMDLINE): usbtest.c
gcc -I ./libusb/include -L ./libusb/lib/gcc -O -Wall usbtest.c -o usbtest.exe -lusb

Note that the gcc used here is actual PC compiler, I’m using MinGW myself. AVR Studio may not understand that and therefore fail. You can also use the above gcc command to compile it manually. Also make sure you unzipped the usbtest.c to your project folder!

Also, the project zip contains a precompiled usbtest.exe so you could just use that and maybe remove the above files from AVR Studio’s Makefile if they offend it. :)

reply

Manash Pratim Das:
March 5, 2012 at 19:40

IMPORTANT…

Sir please can you give the pc side communication code for using in Visual C# or Visual C++.. please your immediate help is required.

Thanks in advance.

reply

erko says:
July 20, 2012 at 16:26

@Manash (26.)

Compiling with Visual C++ 20xx Express Edition

(Thanks to http://www.sajidmc.net/2010/08/compiling-v-usb-avr-usb-example-program.html)
Copy \libusb-win32-bin-1.2.4.0\lib\MSVC\libusb.lib
to C:\Program Files\Microsoft Visual Studio 10.0\VC\lib\libusb.lib
Copy \libusb-win32-bin-1.2.4.0\include\usb.h
to C:\Program Files\Microsoft Visual Studio 10.0\VC\include\usb.h
Select New -> Project -> Win32 Console Application
Name: set-led
Location: \My Documents\Visual Studio 20xx\Projects\vusb-20100715\examples\custom-class\commandline
Solution name: set-led
Next>
Console application, NO precompiled header, Finish
Select View -> solution explorer
Right click Header Files -> Add -> Existing Item -> opendevice.h
Right click Source Files -> Add -> Existing Item -> opendevice.c & set-led.c
Right click Source Files -> Add -> Existing Item -> set-led.c
Double click set-led.c
Find & Replace (Ctrl H) strcasecmp -> strcmp (4 times)
Select set-led Properties (Alt+F7):
Configuration: Active(Debug)
Platform: Active(Win32)
Configuration Properties -> Linker -> Input ->
-> Additional Dependencies, click …, add: libusb.lib -> ok -> ok
Select Build Solution (F7)

reply

jokkebk:
March 5, 2012 at 20:45

@Manash

I would assume that usbtest.c would compile just fine with Visual C# / C++ as long as you add libusb-win32 to your project – based on a quick Google search people have successfully used libusb in Visual C and C#. However, I cannot help you on the details, as I don’t have either installed and I’ve last used them several years ago.

Update: Oh and also note that the next part of this tutorial contains full source code zip, although you probably need the full libusb-win32 package to get Visual C++ -compatible library dll files.

reply

Chris:
March 6, 2012 at 22:39

First…great tutorial; very well written and executed–thank you.

I have another V-USB (Attiny 85) project connected to the a PC using the same vendor/device ID successfully running the lib-usb driver. I’m unable to get this project device beyond the ‘unknown device’ error on a 64-bit Win7 machine. I have tried all your suggestions above to no avail–including disconnecting the attiny 85 device.

Have you been able to connect multiple v-usb based projects (same vendor/device ID) on the same machine? Would the same vendor/device ID cause a conflict?

Thank you.
Chris

reply

jokkebk:
March 6, 2012 at 23:04

Hi Chris,
I think Windows chooses the driver based on vendor & device ID, so if you have multiple drivers for the same combo, that will cause problems (it did with me). If you’re tinkering at home you could probably just choose any device ID for the second project to avoid conflicts…

reply

Chris:
March 7, 2012 at 16:23

@jokkek,

Thank you for the response. I tried changing the vendor & device ID, but had the same issue. I suspect I have a hardware issue. I’m running the 2313 at 16Mhz (only one I had on hand) and made the adjustments to the v-usb config file accordingly. I might have to match a different value capacitor (I used 15pf) on the oscillator. Wish I had a good oscilloscope or DSP to track this problem down :)

reply

jokkebk:
March 7, 2012 at 16:33

@Chris: I had a similar problem and it turned out V-USB driver files did not recompile after changing usbconfig.h so it had old settings for pins and frequency. :) Only thing that springs to my mind would be to try to find a “virgin” computer to plug your device into, to see if Windows has messed up your drivers somehow in the dev computer (there are tools and commands to purge windows usb driver cache if that’s the case).

Hope you get it fixed. And if you’re looking for an oscilloscope, check out my Picoscope review – or buy a BitScope and tell me if it’s worth it. ;)

reply

Chris:
March 7, 2012 at 17:28

@jokkebk

In fact I have read your well-written review and have a Picoscope on my wishlist! I’m frozen in inaction for fear of committing $200-$300 and then regretting the purchase. A few more problems like this and I’m sure I’ll go over the edge and make the purchase. :)

I also read your problems with the changing usbconfig.h file, so I forced a clean re-compile as well as trying a different machine. That is why I suspect a hardware issue. One issue I noticed you address in the Attiny85 project is long leads…I’ll correct my wiring tonight and try again.

I’m eager to correct the problem; I have finished a LCD library for the 2313 and the usb piece is the only thing remaining. Thanks again for responding.
Chris

reply

Chris:
March 8, 2012 at 2:07

Success…shorter leads on the D- and D+ lines made the difference.

reply

Dominik:
March 11, 2012 at 16:46

Hi,
thanks for the good tutorial. Just to let you know: I’ve a Mac running Mac OS X 10.7 and MacPorts. I needed to install the old libusb port called libusb-legacy (the new libusb-API 1.0 does not work with the sample code). I just had to change the build target for the command line tool in the Makefile and all compiled fine:

# One-liner to compile the command-line client from usbtest.c
$(CMDLINE): usbtest.c
gcc -I /opt/local/include/libusb-legacy -L /opt/local/lib/libusb-legacy -O -Wall usbtest.c -o usbtest -lusb-legacy

Best regards,
Dominik

reply

jokkebk says:
March 12, 2012 at 9:28

Dominik: Thank you for the info! The new libusb interface would probably need some code changes to make it work.

reply

asad:
April 2, 2012 at 9:08

what are the functions included in the header file # include

reply

jokkebk says:
April 2, 2012 at 17:09

I’m sorry, it seems WordPress thought your <library.h> was a tag, could you please repeat?

reply

fernando:
April 5, 2012 at 3:21

buen tutorial pero tengo un problema amigo al compilar en avrstudio me sale este error

avr-gcc: usbdrv.o: No such file or directory
make: *** [led.elf] Error 1
Build failed with 1 errors and 0 warnings…

y porcierto estoy usando un atmega8 que debo hacer

reply

jokkebk says:
April 5, 2012 at 9:11

Parece que el usbdrv.c no se ha compilado. Es necesario añadir usbdrv / usbdrv.c, usbdrv / usbdrvasm.S y posiblemente usbdrv / oddebug.c en su proyecto.

Otra alternativa es utilizar WinAVR para compilar el proyecto, sólo tiene que utilizar “make” en el directorio del proyecto y que se compilará y parpadeará para usted. Fusibles todavía tienen que ser cambiado manualmente.

Lo siento por mi español, estoy usando Google Translate. ;)

reply

fernando:
April 5, 2012 at 11:24

gracias, por el aporte !FUNCIONA PARA WINDOWS 7 64 BITS!
ya inclui los archivos que me dijiste pero ahora me salio otro error al compilarlo
que es este:

../usbdrvasm.S:391:9: error: #error “USB_CFG_CLOCK_KHZ is not one of the supported non-crc-rates!”
make: *** [usbdrvasm.o] Error 1
Build failed with 1 errors and 2 warnings…

ya que quiero realizar tu proyecto para poder transmitir datos de la pc a la USART del micro y recibirlos de la misma, a y no se programar en lenguaje en C solo se programar en lenguaje ensamblador
espero de antemano que me puedas ayudar, por favor.

reply

jokkebk says:
April 5, 2012 at 12:05

Suena como USB_CFG_CLOCK_KHZ no se ha definido en usbconfig.h a uno de los valores admitidos. Usted debe definir F_CPU de velocidad de reloj en Hz o establecer USB_CFG_CLOCK_KHZ a 12000, 15000, 16000 o 20000, dependiendo de su velocidad de reloj.

Alternativamente, puede ser que usbconfig.h tiene que ser en el mismo directorio como archivos usbdrv – o bien copiarlo a la carpeta usbdrv o mover los archivos de usbdrv al mismo directorio que main.c.

That’s about as far as I can help you – in case you need additional instructions, I’d suggest posting your question to a suitable electronics forum!

reply

Ketan Kothari:
April 10, 2012 at 11:31

Awesome Tutorial for USB+Atmega………

reply

maithanh80:
April 14, 2012 at 8:29

thanhks for your tutorial.
but when i do the same i get this error in avrstudio:
G:\project dien tu\rf switch\AVR STUDIO\tiny2313_usb\default/../tiny2313_usb.c:18: undefined reference to `usbInit’
G:\project dien tu\rf switch\AVR STUDIO\tiny2313_usb\default/../tiny2313_usb.c:28: undefined reference to `usbPoll’
can you help me.

reply

maithanh80:
April 14, 2012 at 8:32

#include
#include
#include “usbdrv.h”
#define F_CPU 12000000L
#include
#include “oddebug.h”
USB_PUBLIC unsigned char usbFunctionSetup(unsigned char data[8]){
return 0;
}

int main()
{
unsigned char i;
wdt_enable(WDTO_1S);
usbInit();
usbDeviceDisconnect();
for(i=0; i<250;i++){
wdt_reset();
_delay_ms(2);
}
usbDeviceConnect();
sei();
while(1){
wdt_reset();
usbPoll();
}
return 0;

}

Build started 15.4.2011 at 09:58:54
avr-gcc -mmcu=attiny2313 -Wall -gdwarf-2 -Os -std=gnu99 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT tiny2313_usb.o -MF dep/tiny2313_usb.o.d -c ../tiny2313_usb.c
avr-gcc -mmcu=attiny2313 -Wl,-Map=tiny2313_usb.map tiny2313_usb.o -o tiny2313_usb.elf
tiny2313_usb.o: In function `main':
G:\project dien tu\rf switch\AVR STUDIO\tiny2313_usb\default/../tiny2313_usb.c:18: undefined reference to `usbInit'
G:\project dien tu\rf switch\AVR STUDIO\tiny2313_usb\default/../tiny2313_usb.c:28: undefined reference to `usbPoll'
make: *** [tiny2313_usb.elf] Error 1
Build failed with 2 errors and 0 warnings…

reply

jobin says:
May 4, 2012 at 19:18

add usbdrvasm.asm and link it .. and also add usbdrvasm.S to the source file to link it succefully the assembly level codes are contained

reply

aztk:
April 15, 2012 at 11:14

Thanks bro!

It’s work very well!!!!!!!!!!!!!!!!! :D

reply

aztk:
April 15, 2012 at 11:17

Testing in C-AVR Eclipse + gcc in Ubuntu 10.04

reply

maithanh80:
April 16, 2012 at 13:18

may you help me
my english is very poor
so when i do this project is ok for all above, but i dont know where the testusb.c can run. visual c++ or what.?

reply

jokkebk says:
April 16, 2012 at 13:57

Easiest way is to install MinGW, it comes with GNU C Compiler (“gcc”) and GNU Make (“make”) commands which you can use to compile testusb.c. Visual C++ might also work, but the MinGW way is a lot easier.

http://www.mingw.org/wiki/Getting_Started

reply

ankush:
May 7, 2012 at 21:50

i am using WinAvr 4
i have tried a lot. but the problem,
undefined reference to “usbinit” …….stays
if i add the file #include “usbdrv.c”, then the problem change to linking the file usbdrvasm.
can anybody please tell me how to link usbdrvasm.S in WinAvr.

reply

jokkebk says:
May 10, 2012 at 22:59

You should never #include any .c file, only header files. The C files are supposed to be compiled into separate .o files and linked together after that. The same goes with .S assembler files – first compiled into .o file and then linked.

In AVR Studio, find the “Solution explorer” from right hand side panels, right-click your project and select “Add.. Existing item” to add those two files. You may also need to add usbdrv directory as include directory for both C compiler and Assembler. And of course the project needs to be a GNU C project.

I also added F_CPU=16500000 to the defines (-D options) for C and Assembler in the project settings, and got it to compile. Hope you’ll have success!

reply

Secret says:
March 5, 2014 at 18:01

“And of course the project needs to be a GNU C project.”
My project was set as C++, I’ve been looking so long for this solution, thank you so much, man!

reply

Mauro:
May 12, 2012 at 18:11

thanks for the great tutorial.
Im using avr studio 5.1 and atmega16. I do your tutorial but dont work. I dont have error messages. But when i run the INF-wizard my device is not visible who is visible is unknown device.
Can you help me?
Thanks

reply

jokkebk says:
May 13, 2012 at 12:18

Sorry, I cannot help you much; only suggestions I have are to double-check connections and that USB lines are connected to the correct pins, and maybe make sure that your ATmega is definitely running at the MHz number you have specified in usbconfig.h (e.g. 12 MHz if you have 12000 kHz in V-USB config).

As an additional tip, if electrical connections are correct, Windows should play the “bell sound” when device is connected, even if the firmware has some issues. If you don’t get that, it’s likely some connector / pullup / etc. issue.

reply

costel:
May 17, 2012 at 12:47

thanks for this great tutorial!!!
why not?
switch(rq->bRequest) { // custom command is in the bRequest field
case USB_LED_ON:
PORTB |= 1; // turn LED on
return 0;
case USB_LED_OFF:
PORTB &= ~1; // turn LED off
return 0;
}

with

in basic
PORTB.1=NOT PORTB.1

FOR LED BLINK!

reply

jokkebk says:
May 17, 2012 at 23:03

:) Surely one could also do “PORTB ^= 1;” to just toggle the LED. I thought that the ON/OFF commands would be a bit clearer so I went with those.

reply

mrl:
May 23, 2012 at 12:27

This is a very nice Tutorial!

However, my host test program (Win7, VS C++ Express 2010, linking msvc\libusb.lib) does not find any USB devices although I have at least 2 devices connected: in function usbOpenDevice the call to usb_get_busses() returns only one bus with bus->dirname = “bus-0″ and bus->devices being empty.

Any idea what I can do?

Many thanks in advance!

reply

jokkebk says:
May 23, 2012 at 12:50

Weird. I cannot help you much. It is possible that USB devices that are keyboards, mice and mass storage would not show in the iteration. You might try to compile with MinGW’s gcc to see if that would produce a different result, or check if there’s a newer version of libusb available or something.

reply

MRL says:
May 23, 2012 at 23:39

Stupid me. I first tried without my test board connected to the USB slot. When connected my ATtiny-USB device does indeed show up even though other devices seem not to show up (mouse, webcam, and so on). But for what I want everything works fine. Thanks for the quick reply!

reply

fernando:
June 6, 2012 at 6:13

Very very good tutorial,

My device had recognized by windows but I couldnt make the usbtes.c make it run, what should it promp??…when I could run it, shows:
Usage:
usbtext.exe on
usbtext.exe off
usbtext.exe out
usbtext.exe write
usbtext.exe in
and then says press enter to exit..something like that
Hope you can help me.

Many thanks

reply

jokkebk says:
June 9, 2012 at 11:29

Slightly belated reply, sorry for that. :) The program doesn’t prompt anything when you run it, you need to provide the on/off/etc as part of command invocation. If you just double-click the .exe, it runs the command without any parameters, which then just shows the different command-line options available.

So, you need to go to command line (Run->cmd.exe) and navigate to the directory with usbtest.exe, and type the commands “usbtest on”, “usbtest off” etc. there, or alternatively make shortcuts where you can specify the command-line options (from shortcut properties…).

Also, my tutorial part 4 has a link to a GUI version of the tool, if you prefer that. :)

reply

eka:
June 23, 2012 at 4:19

hello sir
very nice easy to understand tutorial regarding VUSB. I hope you don’t mind using your tutorial as my student teaching material on microcontroller.
thank you

reply

jokkebk says:
June 23, 2012 at 11:20

Thanks! And no, I definitely don’t mind, great if the tutorial gets new readers. :)

reply

Vinod S:
July 13, 2012 at 23:25

This is really a great tutorial….

reply
Newer Comments arrow

Leave a Reply

Your e-mail address will not be published.


+ seven = 8

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>