MEAM.Design - ATmega32 Programming - USB Communications


For either debugging or data communications, it is possible to send and receive data between the ATmega32 and a computer via the same USB port that you use for programming.


To setup the m2 to communicate over USB, you will need to download the following mUSB-specific support files:

t_usb.h Use this instead of m_usb.h if you are using a Teensy instead of an M2.
t_usb.c Use this instead of m_usb.c if you are using a Teensy instead of an M2.

Be sure to include the C file in your project:

  • For Windows OS users, if you're using Option 1, you will then need to right-click on the Source Files folder, and select Add Existing Source File(s)..., then select the m_usb.c file, and if you're using Option 2, place m_usb.c in src/.
  • For Mac and Linux users, if you're using Option 1, edit your Makefile to add "m_usb.o" after "main.o" on the OBJECTS line, and if you're using Option 2, place m_usb.c in src/.

Also, place the H file next to your main file for Option 1 or place the H file in inc/ for Option 2, and include m_usb.h in your main routine.

Teensy users are using option 2 if you have been following the guide up to this point.


NOTE - It is assumed that you are running the system clock at 16MHz, though it has been tested to work slower.

As described in the header file, this will give you access to a number of public functions (note - datatype is void unless specified):

          m_usb_init();           Initialize the USB subsystem
char m_usb_isconnected(); Confirm that you have established communication with the PC.
unsigned char m_usb_rx_available(); Returns how many bytes are in the receive FIFO buffer (up to 255).
char m_usb_rx_char(); Returns the bottom byte from the receive FIFO buffer or -1 if timeout.
m_usb_rx_flush(); Discard all data in the receive buffer.
char m_usb_tx_char(unsigned char c); Add a single 8-bit unsigned char to the transmit buffer. Returns 0 if all is well, -1 if there is an error.
m_usb_tx_hex(unsigned int i); Add a 16-bit unsigned int to the transmit buffer and send as hex-value characters (0000 to FFFF) .
m_usb_tx_uint(unsigned int i); Add a 16-bit unsigned int to the transmit buffer and send as decimal-value characters (0 to 65535).
m_usb_tx_int(int i); Add a 16-bit signed int to the transmit buffer and send as decimal-value characters (-32768 to 32767).
m_usb_tx_ulong(unsigned long l); Add a 32-bit unsigned long to the transmit buffer and send as decimal-value characters (0 to 4294967295).
m_usb_tx_long(long l); Add a 32-bit signed long to the transmit buffer and send as decimal-value characters (-2147483648 to 2147483647).
m_usb_tx_string(char* s); Add a string to the transmit buffer.
m_usb_tx_push(); Immediately transmit all buffered output.

For example, the following code will initialize the USB subsystem, wait for a connection, then wait for a packet to be received and then echo it back to the computer as a decimal number

#include "m_general.h"
#include "m_usb.h"
int main(void)
unsigned int value;
while(!m_usb_isconnected()); // wait for a connection
value = m_usb_rx_char();

Computer-side connection options

On a PC, open a serial terminal and connect to the proper COM port. Options include:

Terminal - this may even be able to run on the GM lab computers as it's an executable.
When you press rescan you should see a COM port in the COM dialog box, which should correspond to the location of your device. Press connect and you should be receiving transmitted data immediately. Note that on some machines it might help to run Terminal as an Administrator.
USB Serial Driver - must be run as administrator
Note - if you are working outside of the GM lab and have a Windows computer, you may need to install this driver to help the OS recognize the ATmega32U4

In Mac OS X, you can use terminal to send/receive packets. First, find the serial object ("ls /dev/tty.*"), then start the session ("screen /dev/tty.usbmodem###"). If you don't see any /dev/tty.usbmodem devices, even with the usb cable attached to your board, it maybe because the board uses HID. When you download a program that uses the USB serial device the Mac will then recognize it as a serial device and establish the /dev/tty.usbmodem###. To end the session, press Ctrl-A then Ctrl-\. In Linux, it's the same except for the name of the serial object ("ls /dev/ttyACM*");

You can stream data directly in/out of Matlab, like this:

handle = serial(port,'Baudrate', 9600); where port is either 'COM#' in Windows or '/dev/tty.usbmodem#' in OS X.
fprintf(handle, message); where message is a string
fwrite(handle, variable); where variable is a value

Other thoughts on USB communications:

AVR CDC Demo Code Hacking
AVR provides some demo code for communication device class (CDC) operation on the ATmega32u4.

This code will compile with AVR-studio, or with a modified Makefile on OS X (Change "avr-gcc.exe" to "avr-gcc". When building, flashing, and running, this causes the ATmega32 to appear as a COM port on a connected host machine. The code is intended to be used with a specific development board that has various other widgets on it (temperature sensor, joystick, etc), but can be modified to send whatever data one wants it to. It gives access to the user by overloading the standard printf() function.

Unfortunately (for the sake of simplicity), the asynchronous digital communication seems to require the use of a process scheduler. The schedule mediates between the usb driver on the MCU and the user-supplied code that actually outputs the data. This means that the user defines two functions:

cdc_task_init() Initialization code
cdc_task() Task code

The task code function will be called repeatedly in a loop by the scheduler, so it needs to return quickly, otherwise the USB communication will be blocked.

The code to build is at the path EVK527-series4-cdc-2_0_2-doc/demo/EVK527-series4-cdc while the rest is support code that we can go through and cut some fat from.