Written by Warren W. Gay VE3WWG
This tutorial was written to save other Arduino folks a lot of trouble getting started with AVR-Ada. While the Arduino IDE is a great place to start, advanced projects may require a make file and the use of Ada. I started out using the Arduino IDE myself, but soon after I also returned to using Ada in other projects. Naturally I pined to use Ada on the USB Boarduino I that had invested in.
If you're like me, you might be short on time and in a hurry. Haste eventually made things take longer for me. So I'll pass on some lessons learned. Consider this the "fast track" if you are hoping to use your Arduino with AVR-Ada.
The hardware in the example used is the USB Boarduino, with the ATmega168 part in the socket.
I'm going to assume you have Cygwin installed. Alternatively, you may be using a DOS or MSYS shell instead.
If you're using Linux, you'll need the AVR tool chain installed in place of WinAVR. Linux users may want to use the following documentation as a start:
To install AVR-Ada currently (under Windows), you need to download and install:
When installing software newer than this, note that the limiting factor will be AVR-Ada. Look for it first and then determine what version of WinAVR it will cooperate with.
Beyond installing them both into the same directory (I used C:WinAVR-20100110), this part is painless.
Looking at the literature and various software packages, you might conclude that the ATmega328p part designation covers all of it's family members including the ATmega168p, which I was using. While the code may be the same, the architecture resources are different. Most notably the available amount of RAM!
What I discovered later, is that function calls failed to return. This was due to the compiler generating code for the ATmega328p, which has more RAM (2048 bytes vs 1024 for the ATmega168p). So if you're not sure which part you are using then get the magnifying glass out now and specify it correctly. Details count in WinAVR!
Once I configured my project to compile with -XMCU=atmega168p, it failed because there was no “built” library support for it. You can easily correct that problem, by doing the following:
C:> CD C:WinAVR-20100110libgnat C:> MKDIR avr_libatmega168plib C:> avr-gnatmake -XMCU=atmega168p -P avr.gpr
Once project avr.gpr has been "made" with the part number of your choosing, you should be able to compile your project, as per usual.
The “blink LED” project is the “Hello World” program in the embedded systems world. The Arduino project describes their flavor of this here:
Assuming you are using an Arduino board, this project is the first thing you should test This will make sure your PC and hardware communicate and work ok together:
If the LED blinks on and off, you have proven that:
If this doesn't work, then try the various Arduino resources and troubleshoot that first. That will be the easiest path to AVR-Ada happiness. In this tutorial, I'm going to assume that the Arduino blink project worked.
Up until this point, I had never needed to use GNAT's “project files”. But avoiding them in AVR-Ada is definitely a bad idea.
Using GNAT's project files will drastically reduce the complexity of your AVR-Ada build process. So it is important to get the essentials of this correct for your first AVR-Ada project. Further, you can use this simple example as a starting point for new projects.
Within my Cygin environment, I created a subdirectory ~/avr for my own avr projects. Under that, I then created subdirectory “blinky”, and changed to that directory (~/avr/blinky). This is what we'll assume for this tutorial.
Within your project directory, you want to create your GNAT project file. Call it blinky.gpr. The following simple one can be used to get started:
with "avr.gpr"; project Blinky is package Compiler renames AVR.Compiler; package Builder renames AVR.Builder; package Binder renames AVR.Binder; package Linker renames AVR.Linker; for Exec_Dir use "."; for Source_Files use ("blinky.adb"); for Main use ("blinky.adb"); end Blinky;
If you don't follow this general formula and try to specify all the options in the make file (as I did), you will waste several hours. After you give up, you'll then come back to using the above. Save yourself the grief.
If you don't use Cygwin, then skip to the next section. The avr script below is designed to allow the native windows avr tools to be temporarily searched first on the PATH. This avoids mixing with Cygwin tools.
Note that I mount /cygdrive/c as /c in my own cygwin environment. If you don't, then substitute /cygdrive/c where you see /c in the example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#!/bin/bash # Put the C:WinAVR tools in the path and execute cmd: # AVR="/c/WinAVR-20100110/bin:/c/WinAVR-20100110/utils/bin" PATH="$AVR:$PATH” cmd="$1" shift 1 case "$1" in make ) cmd=/usr/bin/make;; * ) ;; esac exec $cmd $@ # End
You will need to edit the line AVR=... to match where you've installed your WinAVR. The case statement in this script makes certain that the Cywin version of the make command is used. Otherwise, we expect the avr tool chain to be used.
To build this project, we just need a bare bones make file. The first example is for Cygwin, which makes use of the avr script. Non-Cygwin users can review the second Makefile example.
MCU=atmega168p AVRDUDE_CONF=C:/WinAVR-20100110/bin/avrdude.conf all: exec avr make avr_all clean: rm -f b~*.ad[sb] *.o *.ali *.hex *.elf burn: exec avr make avr_burn dump: avr avr-objdump -mavr:5 -d blinky.elf avr_all: avr-gnatmake -g -XMCU=$(MCU) -Pblinky.gpr avr-objcopy -O ihex blinky.elf blinky.hex avr_burn: avrdude -C $(AVRDUDE_CONF) -v -v -p m168 \ -c arduino -P COM5 -b 19200 \ -F -U flash:w:blinky.hex # End
IMPORTANT: Change the MCU= line if you're using a different AVR chip (this is critical).
You may also need to change the AVRDUDE_CONF= line as well, if your WinAVR was installed differently (theoretically, avrdude should be able to find its own config file, but I don't like leaving things to chance here).
If you're using Linux instead, your make file can be simpler:
MCU=atmega168p AVRDUDE_CONF=/usr/local/etc/avrdude.conf all: avr-gnatmake -g -XMCU=$(MCU) -Pblinky.gpr avr-objcopy -O ihex blinky.elf blinky.hex clean: rm -f b~*.ad[sb] *.o *.ali *.hex *.elf burn: avrdude -C $(AVRDUDE_CONF) -v -v -p m168 \ -c arduino -P COM5 -b 19200 \ -F -U flash:w:blinky.hex dump: avr avr-objdump -mavr:5 -d blinky.elf # End
The make "targets" that we will use are:
Target "all" is the default, if you just enter "make". The Cygwin Makefile translates a target "all" (for example), into "avr_all". This is how you can conveniently access the WinAVR tool chain from a Cygwin session.
This invokes the avr script to temporarily modify the environment PATH for the avr tool chain.
One of the challenges I faced, was figuring which ATmega port and bit to use in order to turn on and off the “standard” LED on the USB Boarduino. In the Arduino realm, all you care about is that the LED is controlled by “digital pin 13”. The real port and bit is mapped for you in the Arduino library code. Finding out how this mapped to the hardware seemed to be a well kept secret. Perhaps this had more to do with the USB Boarduino's schematic. There was no LED drawn on it (apart from power), even though it is present on the PCB.
The USB Boarduino's schematic labels only “digital pins" 2 through 10. By extension and I should have realized that the “digital pins 11, 12 and 13” must be the ones labeled MOSI, MISO and SCK respectively. Additionally, "digital pins" 0 and 1 are labeled RxD and TxD on the schematic.
After some investigation I was able to conclude that the "standard" LED is controlled by ATmega168 port B, bit 5. This is the one labeled “SCK” on the schematic.
Often an LED is “lit up” when you output a “low” signal. In this case a resistor tied to +5V, with the LED's anode connected to the resistor (+) and the cathode tied to the "output pin". In this circuit, bringing the pin low completes the circuit and lights the LED.
However, for the "standard" LED on the Arduino, it is lit when the output goes “high” instead. This means you must set the bit “true” (1 or high) to light it, and otherwise set it "false" (0 or low) to turn it off.
When things are not working in the beginning, this kind of trivial information is important to know about.
There is no mapping of "digital pins" in AVR-Ada like there is in the Arduino. So knowing the port and bit that controls the LED is important to the Ada version of the Blink program (we'll call it “Blinky”).
Create the Ada source file named blinky.adb:
with AVR.MCU; with AVR.Wait; use AVR; procedure Blinky is procedure Delay_MS(MS : Natural) is begin for X in 1..MS loop AVR.Wait.Wait_4_Cycles(8000); end loop; end; LED : Boolean renames MCU.PortB_Bits(5); begin MCU.DDRB_Bits := (others => DD_Output); loop LED := True; Delay_MS(600); LED := False; Delay_MS(200); end loop; end Blinky;
The inner procedure “Delay_MS” is a very crude approximation to milliseconds. It serves our purposes here, however. The argument MS indicates how many milliseconds to waste time for.
The variable named “LED” is a renamed bit from a boolean array:
Setting the correct bit True turns on the LED and setting it False darkens it. In the loop coded, the LED stays on longer than it stays off. This helps to prove which sense of the LED activates it.
From the shell, you should be able to “make” (or “make all”) to build blinky. If you had failed compiles beforehand, it is best to do a “make clean” first. The removal of old files forces a complete rebuild of everything.
$ make exec avr make avr_all make: Entering directory `/home/Warren/avr/blinky' avr-gnatmake -g -XMCU=atmega168p -Pblinky.gpr avr-gcc -c --RTS=rts/avr5 -gnatec=C:WinAVR-20100110libgnatgnat.adc -gdwarf-2 -gnatwp -gnatwu -gnatn -gnatp -gnatVn -Os -gnatef -fverbose-asm -frename-registers -mmcu=atmega168p -gnateDMCU=atmega168p -fdata-sections -ffunction-sections -g -I- -gnatA F:cygwinhomeWarrenavrblinkyblinky.adb avr-gnatbind --RTS=rts/avr5 -freestanding -I- -x F:cygwinhomeWarrenavrblinkyblinky.ali avr-gnatlink F:cygwinhomeWarrenavrblinkyblinky.ali -Wl,--gc-sections -gdwarf-2 -Wl,--relax "--GCC=avr-gcc -Os -mmcu=atmega168p --RTS=rts/avr5 -fdata-sections -ffunction-sections" -g -LC:WinAVR-20100110libgnatavr_libatmega168plib -lavrada -o F:cygwinhomeWarrenavrblinkyblinky.elf avr-objcopy -O ihex blinky.elf blinky.hex make: Leaving directory `/home/Warren/avr/blinky' $
When all is successful, you should see a new hex file:
$ ls -ltr ... -rwxr-xr-x 1 Warren None 635 2010-07-31 23:41 blinky.hex
It should look something like this:
$ cat blinky.hex :1000000033C000004CC000004AC0000048C00000DF :1000100046C0000044C0000042C0000040C00000D4 :100020003EC000003CC000003AC0000038C00000E4 :1000300036C0000034C0000032C0000030C00000F4 :100040002EC000002CC000002AC0000028C0000004 :1000500026C0000024C0000022C0000020C0000014 :100060001EC000001CC0000011241FBECFEFD8E04E :10007000DEBFCDBF11E0A0E0B1E0ECEDF0E002C0EA :1000800005900D92A030B107D9F711E0A0E0B1E0E2 :1000900001C01D92A030B107E1F702D01DC0B0CF62 :1000A00000D08FEF84B920E43FE12D9A81E090E009 :1000B000F9013197F1F742E08835940711F0019684 :1000C000F7CF2D9881E090E0D9011197F1F7883CA6 :0C00D000910559F30196F8CFF894FFCF8A :00000001FF $
This is the moment you've been waiting for. To upload the hex file
to your ATmega chip (the USB Boarduino in my case), you do:
$ make burn exec avr make avr_burn make: Entering directory `/home/Warren/avr/blinky' avrdude -C C:/WinAVR-20100110/bin/avrdude.conf -v -v -p m168 -c arduino -P COM5 -b 19200 -F -U flash:w:blinky.hex avrdude.exe: Version 5.10, compiled on Jan 19 2010 at 10:45:23 Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com Copyright (c) 2007-2009 Joerg Wunsch System wide configuration file is "C:/WinAVR-20100110/bin/avrdude.conf" Using Port : COM5 ... avrdude.exe: verifying ... avrdude.exe: 220 bytes of flash verified avrdude.exe done. Thank you. make: Leaving directory `/home/Warren/avr/blinky' $
My USB Boarduino executes the program immediately after the "upload".
However, some Arduinos require a reset and a ten second delay. Hopefully
I spent a lot of time sorting out the parameters used by Arduino for avrdude. The Arduino IDE tends to hide all of this good info from the end user. Here are some things to watch out for and to know about:
The above information is about the USB Boarduino, using a chip with an Arduino bootloader in it. If you have a stock chip, or using a different bootloader, then you may need to revisit the avrdude settings.
Once you have all of the answers (for Arduino), using AVR-Ada is rather straight forward to use. Here are some key points to keep in mind:
The following chart is a mapping between the USB Boarduino's outer pins
and the AVR port and pin numbers.
For example, the d5 pin (Arduino's digital 5)
is mapped to AVR port PD5 (port D pin 5).
|Left pin||AVR Port||Notes||Right Pin||AVR Port||Notes|
|d8||PB0||Digital 8||d7||PD7||Digital 7|
|d13||PB5||SCK (Boarduino LED)||d2||PDs||Digital 2|
|aref||Chip pin 21||AREF||d0||PD0||RXD|
|a0||PC0||analog in 0|
|a1||PC1||analog in 1|
|a2||PC2||analog in 2||5V||VCC||5 volts|
|a3||PC3||analog in 3||gnd||gnd||Ground|
|a4||PC4||analog in 4||3V||3 volts|
|a5||PC5||analog in 5||reset||PC6||Reset|
Using your "Device Manager" (Windows) you should be able to narrow it
down. Since you're not likely to have other USB serial ports, there might
only be one showing:
In my case, the port is configured for COM3. But I believe the default
install puts it at COM5. If it shows up in the list, I would try the
I would do a read test first. This will tell you if the programmer is
able to see your Boarduino or not. If not, it may be because the port
was specified incorrectly.
avrdude -C C:\...\avrdude.conf -c avrisp -P COM3 -b 19200 -v -p m168 -U flash:r:"read.bin":r
If it is successful, you should see something like:
avrdude.exe: Version 5.10, compiled on Jan 19 2010 at 10:45:23 Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/ Copyright (c) 2007-2009 Joerg Wunsch System wide configuration file is "C:CYGWINusrlocaletcavrdude.conf" Using Port : COM3 Using Programmer : avrisp Overriding Baud Rate : 19200 AVR Part : ATMEGA168 Chip Erase delay : 9000 us PAGEL : PD7 BS2 : PC2 RESET disposition : dedicated RETRY pulse : SCK serial program mode : yes parallel program mode : yes Timeout : 200 StabDelay : 100 CmdexeDelay : 25 SyncLoops : 32 ByteDelay : 0 PollIndex : 3 PollValue : 0x53 Memory Detail : Block Poll Page Polled Memory Type Mode Delay Size Indx Paged Size Size #Pages MinW MaxW ReadBack ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- --------- eeprom 65 5 4 0 no 512 4 0 3600 3600 0xff 0xff flash 65 6 128 0 yes 16384 128 128 4500 4500 0xff 0xff lfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 hfuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 efuse 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 lock 0 0 0 0 no 1 0 0 4500 4500 0x00 0x00 calibration 0 0 0 0 no 1 0 0 0 0 0x00 0x00 signature 0 0 0 0 no 3 0 0 0 0 0x00 0x00 Programmer Type : STK500 Description : Atmel AVR ISP Hardware Version: 2 Firmware Version: 1.16 Vtarget : 0.0 V Varef : 0.0 V Oscillator : Off SCK period : 0.1 us avrdude.exe: AVR device initialized and ready to accept instructions Reading | ################################################## | 100% 0.05s avrdude.exe: Device signature = 0x000000 avrdude.exe: Yikes! Invalid device signature. Double check connections and try again, or use -F to override this check. avrdude.exe done. Thank you.
There is a device signature mismatch shown in the results
but the -F option overrides this and I just ignore it.