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:
www.avrfreaks.net/wiki - AVR-GCC
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:
www.arduino.cc/en/Tutorial/Blink
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 |
|
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:
MCU.PortB_Bits(0..7)
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[1]: 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[1]: 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[1]: 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[1]: 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
yours blinked!
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 |
d9 | PB1 | PWM | d6 | PD6 | PWM |
d10 | PB2 | PWM | d5 | PD5 | PWM |
d11 | PB3 | MOSI/PWM | d4 | PD4 | Digital 4 |
d12 | PB4 | MISO | d3 | PD3 | PWM |
d13 | PB5 | SCK (Boarduino LED) | d2 | PDs | Digital 2 |
gnd | gnd | d1 | PD1 | TXD | |
aref | Chip pin 21 | AREF | d0 | PD0 | RXD |
empty | |||||
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 |
[img src="attachment/Boarduino_Pinout.png"]
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:
[img src="attachment/USB_Port.png"]
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
COM5 first.
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.