Monday, July 13, 2009

Atmel AVR Development in Linux

I recently acquired a AT90USB162 development board from SparkFun Electronics, a most excellent online electronics shop that caters primarily to hobbyists. For those not familiar, the AT90USB162 is one in a line of low cost 8-bit AVR micro controllers sold by Atmel that supports USB in addition to a host of other basic peripherals such as MMC/SD, audio, serial, and a joystick, to name a few. For those, like myself, that normally deal in the realm of more powerful embedded devices like the Gumstix or BeagleBoard it can take a little getting used to.

The main difference between development for the AT90USB and a more powerful chip is that the AT90USB doesn't run what most people would consider an operating system. This presents some challenges to those not familiar with this arena because things which you would normally rely on say Linux to do, for example, you now have to do yourself. For instance, the "schedular" on these chips is usually nothing more than an infinite loop with a small set of hard coded functions acting as the "tasks" that get called over and over again. Likewise, there are no drivers to abstract away hardware details. A far cry to be sure from the preemptive multi tasking operating systems to which most of us are a custom.

While we can't use Linux on these devices we can still enlist the help of GCC (targeted for AVR) along with a host of other open source software to speed up the whole process. Perhaps the most popular tool chain for working with AVR devices is a free development environment called "WinAVR". WinAVR is a GCC / Bin Utils / AVRlibC based distribution with an IDE thrown in that, as you might guess, runs on windows. Alternatively Atmel distributes their own environment, AVR Studio, which also runs on windows. While these seem to be the most popular judging from posts on internet message boards they aren't the only option. It is possible to do development directly under Linux without depending on Windows or any other non-free software. What follows are my notes on how I got my board up and running using only free software running on Linux.

The main thing I found is that while there are many good resources out there that provide hints no one site contained all the correct information in the same place and many omitted a few critical less than obvious details. Hence my motivation to document it here. Before you start you are going to want to gather recent versions of at least the following packages...
Since your system will already have native versions of GCC and Binutils installed we want to be careful not to clobber the native versions. We do this in two ways. First we keep out AVR tool chain separate from our systems by installing to a separate path and secondly all of the AVR versions of the tools will have the prefix "avr-" perpended to them.

To get started create a directory in your home directory where we will unpack all of the source code and do the compilation. I called my "avr-src". The first package we need to build is Binutils. Unpack, configure, build and install as follows.

tar -xzvf binutils-2.19.1.tar.gz -C ~/avr-scr/
cd ~/avr-src/binutils-1.19.1
./configure --target=avr --program-prefix=avr- --prefix=/opt/avr
make
sudo make install


Notice above when we invoke configure the "--program-prefix" and "--prefix" options. These are the parameters that keep our AVR tools from clobbering our system's tools. You can chose a different path than "/opt/avr" such as "/usr/local/avr" depending on your preference so long as you are consistent.

Next we'll install GCC. GCC doesn't support building within the source directory so we need to create a separate build directory to avoid errors.

tar -xjvf gcc-4.4.0.tar.bz2 -C ~/avr-src/
cd ~/avr-src/
mkdir avr-gcc-build
cd avr-gcc-build
../gcc-4.4.0/configure --target=avr --program-prefix=avr- --prefix=/opt/avr --enable-languages="c, c++" --disable-nls
make
sudo make install


A gotcha I encountered here is is not having the right libraries and headers installed. GCC requires the GNU MP and GNU MPFR libraries be installed on your host system. The easiest way to fix this is to use your Linux distribution's package system to find and install these packages. You will need both the libraries themselves as well as the "devel" versions of the package which install the header files. If your system doesn't provide packages or they are to far out of date you can google for the respective packages and build them from source. If you build from source be sure to run the ldconfig command as root so your system picks up the updated libraries before proceeding with building your AVR tools.

Two notes on the GCC configure options. --disable-nls disables features dealing with internationalization which you likely won't need on a micro controller target. --enable-languages tells configure what languages to build compilers for. In this case C and C++ will be supported on our AVR target.

Before going further we need to add out newly built tool chain to our path. If you use a Bash shell you can edit your .bashrc file and add the line "export PATH=$PATH:/opt/avr/bin" to it. Then source the .bashrc file or open a new shell so the path changes take effect.

Now we will build AVR Lib C. AVR Lib C is a stripped down standard C library targeted specifically for AVR's. It also includes a lot of AVR specific definitions for hardware registers and a handful of helpful functions to do things like manipulate watchdog timers and configure the interrupts. Build it as follows.

tar -xjvf avr-libc-1.6.7.tar.bz2 -C ~/avr-src/
cd ~/avr-src/avr-libc-1.6.7
./configure --build=`./config.guess` --host=avr --prefix=/opt/avr
make
su
make install
ldconfig
exit

Note those are back ticks surrounding "./config.guess" also if you use "sudo" or "su -" when installing you may experience errors to the effect of "can't find avr-ranlib". This has to do with the path being incorrect. Using plain su will preserve your path and avoids the error.

You now have all the tools you need to build software for the AVR target but you still need to be able to upload the software to the target device to run. Most AVR's in general require an In System Programmer (ISP) or similar special programming device. Luckily if you are using an AT90USBxxx based board it contains an on board boot loader that allows you to upload new images to it via USB using either Atmel's "FLIP" software or the open source DFU Programmer. (Note: dfu is the technical term for the standard the FLIP software implements.) So lets go ahead and install dfu-programmer.

tar -xzvf dfu-programmer-0.5.1.tar.gz -C ~/avr-src/
cd ~/avr-src/dfu-programmer-0.5.1
./configure --prefix=/opt/avr
make
sudo make install


Now we need to configure udev to allow access to your device when it is plugged in. This is controlled in /etc/udev/rules.d/ you need to create a new file in that directory called "99-dfu-programmer.rules" and add the following to it...

"""
SUBSYSTEM=="usb", ACTION=="add", SYSFS{idVendor}=="03eb", SYSFS{idProduct}=="2ffa", MODE="660", GROUP="uucp", SYMLINK+="at90usb-%k"
BUS=="usb", ACTION=="add", SYSFS{idVendor}=="03eb", SYSFS{idProduct}=="2ffa", MODE="660", GROUP="uucp"

SUBSYSTEM=="usb", ACTION=="add", SYSFS{idVendor}=="03eb", SYSFS{idProduct}=="2ffb", MODE="660", GROUP="uucp", SYMLINK+="at90usb-%k"
BUS=="usb", ACTION=="add", SYSFS{idVendor}=="03eb", SYSFS{idProduct}=="2ffb", MODE="660", GROUP="uucp"

SUBSYSTEM=="usb", ACTION=="add", SYSFS{idVendor}=="03eb", SYSFS{idProduct}=="2ff9", MODE="660", GROUP="uucp", SYMLINK+="at90usb-%k"
BUS=="usb", ACTION=="add", SYSFS{idVendor}=="03eb", SYSFS{idProduct}=="2ff9", MODE="660", GROUP="uucp"

SUBSYSTEM=="usb", ACTION=="add", SYSFS{idVendor}=="03eb", SYSFS{idProduct}=="2ff7", MODE="660", GROUP="uucp", SYMLINK+="at90usb-%k"
BUS=="usb", ACTION=="add", SYSFS{idVendor}=="03eb", SYSFS{idProduct}=="2ff7", MODE="660", GROUP="uucp"

SUBSYSTEM=="usb", ACTION=="add", SYSFS{idVendor}=="03eb", SYSFS{idProduct}=="2ff4", MODE="660", GROUP="uucp", SYMLINK+="at90usb-%k"
BUS=="usb", ACTION=="add", SYSFS{idVendor}=="03eb", SYSFS{idProduct}=="2ff4", MODE="660", GROUP="uucp"

SUBSYSTEM=="usb", ACTION=="add", SYSFS{idVendor}=="03eb", SYSFS{idProduct}=="2ff3", MODE="660", GROUP="uucp", SYMLINK+="at90usb-%k"
BUS=="usb", ACTION=="add", SYSFS{idVendor}=="03eb", SYSFS{idProduct}=="2ff3", MODE="660", GROUP="uucp"

"""

You may need to change the GROUP setting to be a group you are a part of instead of uucp. What this file does is tell udev how to present your device within sysfs and what permissions to assign to it. This file will cover a number of Atmel AT90USB boards based on their vendor and product id's. You can figure out the vendor and product id by using the lsusb command when your board is plugged in and in program mode. If your board isn't covered by the above just add another section with the correct vendor and product ids.

Once you've added the udev configuration file as root run the command "udevcontrol reload_rules" to pickup the changes.

To test that this worked plug your board in via USB and put it in programming mode. On my board that means holding down the "HWB" button and then pushing "RST" to reset the board. You will see a new device connected in the dmesg output and lsusb will also show the connected device. Now you should be ready to run dfu-programmer. We'll query the board for it's bootloader-version to test things out.

dfu-programmer at90usb162 get bootloader-version

Replace "at90usb162" with what ever chip your board has. If you get a error message like "dfu:programmer: no device present" then your udev configuration is likely incorrect or you aren't part of the right group to access the device.

At this point you have everything you need to start developing. Good reference resources can be found at Atmel's website in the form of data sheets, and the AVR Lib C site. There is also a Freely available USB stack called LUFA that has a lot of good demo code. Atmel provides some example code but it's a bit of a mess.

Monday, September 8, 2008

N Queens Implmented With Bits...

Here is a pretty fast nQueens implementation I wrote about 5 years back. It uses bits to keep track of each queen's lanes of attack. This approach limits the size of the board you can use to n <= (b + 1)/2 where b is the number of bits in the data type used for the bit arrays. This particular implementation uses 3 separate bit arrays one each for the x, diagonal x and diagonal y axis. I've seen solutions that are even faster than this one which squeeze all of the bits into a single 64-bit type. The basic idea here is that each queen in addition to having an x and a y coordinate also has a dx and dy coordinate. For there to be no conflicts no two queens can share a coordinate in any of the 4 axises.
/*
* Fast N Queens solution using bits.
* Copyright (C) 2008 Pete M.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <sys/types.h>

#define MAXN 12
#define N (MAXN - 1)

#define MARK(a,bit) (a | (1 << bit))
#define TEST(a,bit) (a & (1 << bit))

static void placeQueen(u_int32_t y, u_int32_t x, u_int32_t dx, u_int32_t dy);
static void printSolution();

static int solution[MAXN];

int main() {
placeQueen(0, 0, 0, 0);
return 0;
}

void placeQueen(u_int32_t y, u_int32_t x, u_int32_t dx, u_int32_t dy) {
u_int32_t xpos;
u_int32_t dxpos;
u_int32_t dypos;

for(xpos = 0; xpos <= N; xpos++) {
dxpos = xpos + y;
dypos = N + xpos - y;
if(!(TEST(x, xpos) ||
TEST(dx, dxpos) ||
TEST(dy, dypos))) {
solution[y] = xpos;
if(y == N) {
printSolution();
}
else {
placeQueen(y+1, MARK(x, xpos),
MARK(dx, dxpos),
MARK(dy, dypos));
}
}
}
}

void printSolution() {
u_int32_t i;

for(i = 0; i <= N; i++) {
printf("(%d,%d)", i, solution[i]);
}

printf("\n");
}
Of course there are other solutions as well using arrays, pointers, etc...

Saturday, April 5, 2008

The What and Why...

This being my first entry I think it appropriate to briefly touch on some background, not because it is particularly interesting, but because it will give context for what follows.

I've wanted to start posting for the better part of the past year. My main motivation was and is to provide a purpose for my tinkering with technology. I'm a computer scientist by trade but my interests often lead me beyond the familiar realm of software. Surveying my desk I'm reminded of countless projects, started, but not entirely finished. The primary reason for this isn't so much a lack of time as it is an absence of purpose for further effort. My initial drive is usually to satisfy a curiosity which often happens before the point of "completion" as one might traditionally think of it. That is, to say, once I see something is possible I loose interest and move on. Which brings me back to my motivation for writing here. My hope is that by documenting my tinkering I'll provide a further purpose to finish what I start.

This perhaps raises the question, of what specifically I'll be writing about? After all, "technology" is an infinitely broad subject. To answer I would say, the question you should be asking, having read the above paragraph, is, "what's on your desk?" Given the greater descriptive value of pixels over words, here is a picture of my desk. I must admit this photo is slightly contrived, I do own shelves. My desk isn't usually this cluttered, though it is often short of a clear slab. None the less, you can see there are a lot of random things in this picture, embedded computers, a bar code scanner, RF id readers, GPS and wifi antennas, iPods, and computers running the Linux, Windows, and Solaris operating system to name a few. In fact, the desk itself is a mash-up of a chest of drawers from Ikea conjoined with a slab of wood and steel pipes from The Home Depot.

Besides the purpose of this Blog, this photo also serves to illustrate its' title, "The Random Bit". Which is to say, simply, I plan to write about a variety of different stuff. It seems silly, but lack of a suitable title was the main obstacle to me starting this sooner. Despite intending to begin a number of times before now, I could never get past those first words, I think these three capture the theme well.

If there is something you are particularly interested in learning more about leave a note and I might oblige by tackling that first. Otherwise, readers or more probably just the Google crawler will be at the mercy of my scattered interests.