Linux-Powered Amateur Rocket Goes USB
The more interesting project is to get the LPC2148 to communicate over USB. The LPC2148 supports four different USB transfer types: control, bulk, interrupt and isochronous. A USB device can have several data pipes, or “endpoints”, that implement one of the transfer types. Each endpoint can either send data to the host (an IN endpoint) or send data from the host (an OUT endpoint). Control endpoints are bidirectional.
All USB devices must have one control endpoint over which to send their device descriptors. PSAS needed one other IN endpoint to send over periodically sampled sensor data, so we wanted either an interrupt or an isochronous IN endpoint. We always want to receive the latest data, so we chose the isochronous IN endpoint, because the host controller software will never attempt to retry a dropped isochronous transfer. Isochronous endpoints also could be used to turn the LPC2148 into a USB camera.
Dave and Kay recently added isochronous transfer and DMA support to the LPCUSB library. To try it out, you need to check out the latest code from the LPCUSB SVN repository:
$ svn co https://lpcusb.svn.sourceforge.net/svnroot/lpcusb lpcusb
I checked out version 177 into my $HOME/svn/ directory. Throughout these examples, I assume you use the same directories.
There should be an isochronous example in lpcusb/trunk/target/examples/ called isoc_io_dma_sample.c. This is a simple program for the LPC2148 that creates two isochronous endpoints. The IN isochronous endpoint sends a counter value into the host and then increments the counter. The OUT endpoint allows the host to control whether LED1 on the board is on or off.
To build the isoc example, change directories to lpcusb/trunk/target and type make. You now should have a file called isoc_io_dma_sample.hex in the examples directory.
Now you need to flash the .hex file to the LPC2148 board. You need to use the OpenOCD config file from the lpc-kit, and modify the OpenOCD script to download the correct .hex file.
First, copy the OpenOCD template script from lpc-kit:
$ cd ~/svn/lpcusb/trunk/target/examples/ $ cp ~/git/lpc-kit/Dev/2148/lpc-template/src/ ↪oocd_flash_lpc2148.script .
Also, copy the OpenOCD config file into the LPCUSB examples directory:
$ cp ~/git/lpc-kit/Config/2148/openocd_lpc2148_v1257.cfg .
Now, modify the script to tell OpenOCD to send the isoc_io_dma_sample.hex file to the LPC2148. Change this line:
flash write_image template.hex 0x0 ihex
flash write_image isoc_io_dma_sample.hex 0x0 ihex
Next, start the OpenOCD dæmon:
$ sudo ~/git/lpc-kit/LPC/2148/OCD/bin/openocd \ -f openocd_lpc2148_v1257.cfg
From another terminal, Telnet into the OpenOCD port, and then tell OpenOCD to run the modified script:
$ cd ~/svn/lpcusb/trunk/target/examples/ $ telnet localhost 4444 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Open On-Chip Debugger > script oocd_flash_lpc2148.script
If you've followed the instructions, LED2 on the Olimex board will start to blink incessantly, and you should see an OpenOCD message similar to the following:
wrote 9454 byte from file isoc_io_dma_sample.hex in 0.994377s (9.284629 kb/s)
Close the connection by pressing Ctrl-] and then Ctrl-D. Kill the OpenOCD dæmon in the other terminal by typing Ctrl-C. Remove the JTAG connector, press the LPC2148 reset button, and connect a USB cable from the Olimex board to your computer's USB port. Make sure to plug in to a root port, not through a USB hub. Some hubs have issues with isochronous transfers, so a direct connection is best. You can power the LPC2148 solely off USB bus power, but I left the 9V wall wart plugged in.
If you have CONFIG_USB_DEBUG turned on in your Linux kernel config, you will be able to watch the USB subsystem connect to the USB device as you plug it in:
$ sudo tail -f /var/log/kern.log ... usb 2-2: New USB device found, idVendor=ffff, idProduct=0005 ... usb 2-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3 ... usb 2-2: Product: USBSerial ... usb 2-2: Manufacturer: LPCUSB ... usb 2-2: SerialNumber: DEADC0DE
Type sudo lsusb to see which USB devices are connected to your system. You should see a device with an ID of ffff:0005. For me, it showed up as device 15:
$ sudo lsusb Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 002 Device 015: ID ffff:0005 Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 001 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
You can use the -v flag to examine the full device descriptors. This outputs all descriptors for all devices, so it's best to limit the output to the LPC2148 device with the -d <ID> option:
$ sudo lsusb -v -d ffff:0005
You should see two endpoint descriptors, one for an isochronous OUT endpoint and one for an isochronous IN endpoint.
Congratulations! The Linux kernel can initialize the LPC2148 USB device successfully. Unfortunately, there is no standard Linux USB kernel driver for this device. Instead, you need to compile and run a user-space program that uses the Linux kernel USB interface (usbfs) to talk to the device directly.
First, you need to have the libusb-dev package installed to get the usb.h header file for usbfs:
$ sudo aptitude install libusb-dev
Now, change directories into the lpcusb host-side code examples:
$ cd ~/svn/lpcusb/trunk/host/linux_isoc_sample/
Type make. This creates src/linux_usbfs_isoc_io_test, a binary that needs to run as root. Type sudo src/linux_usbfs_isoc_io_test to talk to the USB device. You will see lots of messages scroll by, similar to the following:
Bytes/second 1226 Input Length 4 number sent from device 0x3116D4 ret 0 status 0 flag 2 error_count 0 number_of_packets 1 actual_length 0 start_frame 614 usercontext -1077961592 iso_frame_desc.actual_length 0 iso_frame_desc.length 128 iso_frame_desc.status 0 Bytes/second 1228 Input Length 4 number sent from device 0x3116D5 ret 0 status 0 flag 2 error_count 0 number_of_packets 1 actual_length 0 start_frame 615 usercontext -1077961592 iso_frame_desc.actual_length 0 iso_frame_desc.length 128 iso_frame_desc.status 0
The start_frame is the USB bus “frame number” in which the transfer started. A frame represents a one millisecond time period. As long as you see steadily incrementing start_frame numbers, you know the system isn't dropping isochronous packets. The hexidecimal “number sent from device” is the counter on the LPC2148 that is incremented when the interrupt handler is run and there's an isochronous IN transfer to send to the host.
The isochronous IN endpoint is working correctly if the start_frame and device counter are incremented at the same rate. They may be out of sync for the last couple transfers when you kill the program by pressing Ctrl-C. You also can tell whether the isochronous OUT endpoint is working if the LED1 on the board turns on and off every second.
Practical books for the most technical people on the planet. Newly available books include:
- Agile Product Development by Ted Schmidt
- Improve Business Processes with an Enterprise Job Scheduler by Mike Diehl
- Finding Your Way: Mapping Your Network to Improve Manageability by Bill Childers
- DIY Commerce Site by Reven Lerner
Plus many more.
- Handheld Emulation: Achievement Unlocked!
- Building a Multisourced Infrastructure Using OpenVPN
- Unikernels, Docker, and Why You Should Care
- Happy GPL Birthday VLC!
- Download "Linux Management with Red Hat Satellite: Measuring Business Impact and ROI"
- New Products
- Controversy at the Linux Foundation
- February 2016 Issue of Linux Journal
- Non-Linux FOSS: Snk
- Giving Silos Their Due