This can be done using some USB to serial converters, but I didn't have one at the time and I had serious driver issues in the past.
Also, this could be achieved by connecting the circuit directly to USB but that also has some drawbacks. First, you're limited to 12Mhz clock speed for USB to work. Also, the USB is very picky about the timing. If you take too much time with the interrupts disabled, you can get issues with the communication. The system may even disconnect the device if it doesn't respond within a certain period of time. Third, if you have bugs in your USB device which makes it continously send bytes or not respond for long periods of time, the Linux kernel can't cope with it. The associated process becomes hung up in an unrecoverable state and the only way to kill it is by removing power from the PC. So, doing development on a board that is directly connected to the USB is a bad idea, unless the primary function of the device you're making includes USB connection.
The firmware on the USB device is called usbterm. It uses the V-USB system to handle the USB stuff. This firmware contains some I2C slave code as a reusable module. This code is tested on Atmega8, and that's all you need because it's not the end product. You don't need to change it unless you lack the part.
The firmware library for target boards is completely home grown. Although it contains the necessary functions to communicate with the USB device, it still needs some work to make it even easier to use. This is also written for Atmega8, but I should test it on other atmegas to make it portable. I'd like to use some atmega328s for a change!
You can also give commands to the terminal program or the USB device thru this program. If a line starts with the character '#', then the next character is a command code. The following commands are recognized:
termas> more /etc/udev/rules.d/usbasp.rules SUBSYSTEM=="usb", ATTR{idVendor}=="16c0", ATTR{idProduct}=="05dc", OWNER="dodo" SUBSYSTEM=="usb", ATTR{idVendor}=="6666", ATTR{idProduct}=="1291", OWNER="dodo"The first line enables the USBASP programmer. The second line allows access to the USBTERM board. The vendor and product IDs are defined in two places in the source code. Once in the host program code and once in the USBTERM firmware.
In the termas/ directory, there is a module called "usbtermi.c". This file contains the definitions for the following functions. You can use them to communicate with the USB board from your own programs.
void usbterm_init(int debuglevel); // debuglevel is used for libusb 0 is minimum 3 is maximum void usbterm_finish(); // closes the device and finalizes libusb int usbterm_read(uint8_t *buffer, int len); // reads from device-to-pc buffer int usbterm_writecapacity(); // returns the number of bytes you can write to the // device without overflowing its buffers int usbterm_write(uint8_t *buffer, int len); // writes to the pc-to-device buffer int usbterm_test(uint8_t *buffer); // sends a test command and returns the result in buffer, // which must be at least 8 bytes big void usbterm_stats(int *wsize, int *chead, int *ctail, int *dhead, int *dtail); void usbterm_enable_avail_signal(); void usbterm_disable_avail_signal(); void usbterm_reset_buffers(); // these perform the same functions as explained in the // termas program commands void msleep(uint32_t milliseconds); // utility function
The device is also able to signal whether there is some data to be read. It does this by setting its pin PC3 high if there is data in the PC-to-device buffer. This option is by default turned off but can be controlled thru '#A' and '#a' termas commands or the usbtermi functions usbterm_enable_avail_signal() and usbterm_disable_avail_signal().
The file echo.c in lcdser/ contains a basic echo routine to test out the functionality of the system. It's a good idea to echo sent commands back in general.
The code in echo.c shows the proper way to use the system. When writing to the USB device, always ask beforehand how many bytes you can write. The same applies to reading. I2C has no way of signalling an end of file, I2C masters are supposed to ask for only valid addresses. There is a way of terminating communication in case of a bad address, but that results in garbage data in the I2C buffer, which isn't distinguishable from normal data.
Also keep in mind that unlike USB, I2C has no builtin message integrity checks. If a bit gets inverted somewhere, it's not possible to detect it thru hardware. If you intend to use the system as a communication protocol between your PC and the target board, make sure that you put some integrity checks.
It takes quite a bit of code to buffer things and then send them over I2C. I should instead make something which can send a single byte. For example, in the echo code, the data to be output already resides in the output buffer. There is no need to copy it around. I could simply give the i2c code a pointer and then work with that.
Alternatively, I could improve the i2c code to use interrupts and make it call some user defined function to provide more data.