Programming the eDemo AVR

The eSPOT demo board has an Atmel AVR microcontroller (MCU) for LEDs, pushbuttons and general IO. This MCU comes preprogrammed at the factory  and can be reprogrammed in the field with an ant upgrade command. A SPOT owner might wish to change the firmware to either add functionality or do something the eDemo board currently doesn't provide.

The latest Red release of the SPOT SDK will program the eDemo AVR firmware directly and without a bootloader during the upgrade process. The same code that is used to upgrade the eDemo board firmware can be used to load custom firmware into the eDemo AVR. This blog walks you through the steps of downloading a tool chain for Windows and programming a simple example on the eDemo board. It does use an existing java class that is in the Red release of the SPOT SDK and this SDK must be installed on the desktop and the target SPOT plugged into USB for this to work.

The firmware for the AVR MCU is written in C and compiled using the open source avr-gcc,  Gnu Compiler Collection . Warning: modifying the firmware can render the SPOT inoperable or bricked. Most RMA Bricked SPOTs eventually end up in my office and while we recover most of them, it is a slow process. You've been warned.

As a prerequisite, you should have working knowledge of the C programming language, be able to use gcc tools especially make and know your way around a microcontroller. If you want to learn C, I'd start with the Kernighan and Ritchie book, "The C Programming Language" and use the abundant material online. For learning about the AVR MCU, a place to start is with AVRFreaks for forums, downloads, tutorials, etc. The datasheet for the Atmega88 is needed for code development and can be downloaded here. The current eDemo board (Rev 5.x and before) uses the Atmega88 or Atmega88V. Atmel is replacing this part with the Atmega88PA but we are not using that part yet.

The Atmega MCU has 8Kbytes of Flash memory, 1Kbyte of SRAM, and 512 bytes of EEPROM.  It has built in peripherals such as general purpose IO, analog to digital converters, serial interfaces (UART, I2C/TWI, SPI), and timer/counters. The peripherals share pins in common and interact with the processor through memory mapped registers. The registers are set to default values on reset. The default values and register/bit field naming are all in the Atmega88 datasheet.

The tool chain
Tool chains come in a lot of flavors and for different operating systems. For example, the IAR Workbench is a commercial and complete integrated development environment (IDE). It has source code editors, build automation tools, compilers, libraries and debugging built in.  Net Beans, Eclipse, AVR Studio and Programmers Notepad (included with WinAVR) are open source IDEs with front end extensions to run compilers, debuggers and build automation programs. There is a good write-up at Linux Journal  on putting the tool chain in Linux for the AVR. WinAVR is one of the best AVR tool chain packages for Windows. There is a plugin for Eclipse and WinAVR.

On Windows, you can run the tools from command line using Cygwin. Cygwin is an open sourced Linux-like environment that runs on Microsoft Windows. It uses Linux syntax but there are differences between the two which can cause problems from to time such as spaces in filenames, the use of '\\' or '/' as path names and the drive letter. The advantage of Cygwin is there are a large number of additional tools and utilities that can be downloaded with it.

A typical avr-gcc tool chain flow starts with the source code in C (.c file extension), assembler (.s) and the header or include files (.h).  The header files contain the function prototypes, typedefs, macros (#define) and other preprocessor directives. As a rule of thumb, if it directly generates code or data, it probably shouldn't be in the include file. After preprocess, the source is compiled into an object file (.o), a relocatable compiled code module with external references unresolved. The next phase, GCC takes on the linker role and combines the object files into a single executable link format (.elf).

avr-objcopy tool then converts the .elf file into a formatted binary file (.hex intel format) that can be programmed into the flash memory of the MCU. The tool chain process can be automated using the 'make' utility. make reads the rules of how a project is made from  makefile script (Makefile) and determine, from the modify date of the files, which tools need to be run to bring everything up to date.

Installing the command line tool chain on Windows
Install cygwin on your system and launch it. The first time cygwin window opens, it creates a C:\\cygwin\\home\\<user> directory with the default startup script .bashrc. Edit this script and add at the end:

#----------------
export WEBBROWSER=`sed -e 's/" \*-.\*//' -e 's/\\\\\\\\/\\//g' -e 's/"//'
'/proc/registry/HKEY_CLASSES_ROOT/http/shell/open/command/@'`
export SPOTHOME=`sed -n "/\^sunspot.home/{s/sunspot.home=//;s/ /\\ /g;p}" "$USERPROFILE/.sunspot.properties"`
export JAVADOCS=`cygpath -wa $SPOTHOME/doc/javadoc/index.html`
alias ports='devcon status =ports'
alias resetspot='devcon restart USB\\\\VID_0430\\&PID_1000\\\*'
alias spot='cd "$SPOTHOME"'
alias spotdoc='"$WEBBROWSER" "$JAVADOCS"&'
alias spotfinder='"$SPOTHOME/bin/spotfinder.exe"'
#----------------

Save this file and restart the cygwin window.

This adds SPOT relate bash commands:
spot -  changes the current working directory to be the currently installed SPOT SDK directory
spotdoc - opens current SPOT SDK javadoc with the default browser
spotfinder - opens the current SPOT SDK's spotfinder command.
ports - lists all ports in use
resetspot - reset the SPOT USB port

You must have the Red SPOT SDK installed (Click here for installer). If not, SPOTHOME will not be defined and spot, spotdoc and spotfinder will not work. If you activate or install a different release of the SPOT SDK, you will have to restart Cygwin. For ports and resetspot, you need devcon. Devcon can be downloaded from Microsoft here. Move devcon to C:\\cygwin\\usr\\local\\bin folder. Devcon is Microsoft's command line device manager for Windows and handy when dealing with USB devices.

I have had problems with cygwin make in the past and use the make included with WinAVR instead. I renamed Cygwin make to get it out of the way with:
mv /bin/make /bin/cygmake

Download and install the WinAVR tool chain from here.  This package contains the avr-gcc compiler, avrdude programmer, avr-gdb (debugger), programmers notepad, bin-utils, and avr-lib. Look inside the WinAVR-<release data> folder and read the WinAVR-user-manual.html. Inside the avr/include folder are all of the system include files for specific microcontrollers, standard C libraries, etc.

AVR C programs will include <avr/io.h> for all of the register definitions, bit fields, interrupt vectors, memory sizes, etc. The register names and bitfield names are the same as Atmel's datasheet. <avr/io.h> reads the MCU type from the Makefile to figure what device specific files are included. For the Atmega88, these are iom88.h and iomx8.h.

To program the AVR from the SPOT, we need to instruct make on what to do. Download the file SPOT.make and put it into your project directory. Add the line

-include SPOT.make

towards the end of your makefile. This adds two new rules:

make deploy - program the AVR with the generated .hex file

make restore - restore eDemo firmware to last upgrade

make deploy copies the current demosensorboardfirmware.jar file to your project directory and creates a small build.xml for it.  If you change SPOT SDK, delete the .jar file in your project. It will make a copy of your project binary and name it edemo.hex moving that into the jar file. This will not work unless you have SPOTHOME set. Mine is set through the .bashrc script and Cygwin. Once the file is installed, make runs ant jar-deploy to install and run the java program which in turn programs the AVR.

An example

 The following is a small program which can be put in the eDemo AVR that randomly flashes the eight tricolor LEDs. It has two functions: init() for port initialization and setLED() for bit shifting the color information into the LED shift registers. The main function gets a random number and sets the red, green and blue variables accordingly. We call a millisecond delay defined in util/delay.h, turn the LEDs off, delay some more and rerun.

Start by creating a project folder 'eblinky' and create a makefile using WinAVR's mfile program. mfile is an menu item in the WinAVR start menu. When it starts, click the Makefile tab and select:
Main file name: eblinky
MCU type: atmega88
Output format: ihex

Edit the make file and before the line: "# Target: clean project." add:
-include -SPOT.make
Save the file as Makefile in your project folder. Download SPOT.make and save in your project folder.

This Makefile will respond to the follow rules:
make all - compile all source and generate hex binary
make clean - delete intermediate files from the project
make program - program an AVR with a programming module (like AVRISP)
make deploy - program the intel hex binary into the SPOT eDEMO AVR.
make restore - restore the SPOT eDEMO firmware

Now copy between the dotted lines and paste into a text file or download from here. Save the file in our project folder as eblinky.c.

//-------------------
#include <avr/io.h>
#include <stdint.h>
#include <stdlib.h>
#include <util/delay.h>

// taken from edemo.h
#define LED_SCLK PD4
#define LED_SI PD5
#define LED_EN PD6
#define ACCEL_PD PD7

#define ACCEL_ST PB6
#define ACCEL_FS PB7

#define CALM 900
#define BUSY 200
#define BERZERK 90
#define RED_BULL 20

#define LED_SPEED RED_BULL

void init(void) {
    // set the frequency to 8MHz
    CLKPR = _BV(CLKPCE);
    CLKPR = 0;
    // Power down unused atmega modules
    PRR = _BV(PRUSART0) | _BV(PRTWI) | _BV(PRADC) | _BV(PRTIM0) | _BV(PRTIM2);
    // set the accelerometer control, rest are inputs
    PORTB = _BV(ACCEL_ST);
    DDRB = _BV(ACCEL_ST)|_BV(ACCEL_FS);
    // LED_EN is active low, starts high
    // ACCEL_PD starts low reseting the LED shift register
    PORTD = _BV(LED_EN);
    // LED serial chain are outputs
    DDRD = _BV(LED_SCLK)|_BV(LED_SI)|_BV(LED_EN)|_BV(ACCEL_PD);
    // PC0-3 are output, rest inputs default
    PORTC = 0;
    DDRC = 0xF;
    //  Accel Self test is off
    PORTB &= ~_BV(ACCEL_ST);
    // Power up and remove reset from LED shift register
    PORTD |= _BV(ACCEL_PD);
}

// simple LED program
// parameter red, green and blue are bit fields which turn on the respective color LED
// red bit 0 turns on red led1 (left LED), blue bit 7 turns on blue led8 (right LED)

void setLED(unsigned char red, unsigned char green, unsigned char blue) {
    unsigned char i;
    unsigned char led;
    enum {BLUE, GREEN, RED} color;

    PORTD &= ~_BV(LED_EN); // drop enable low
    for(color = BLUE; color <= RED; color++) {
        if (color == BLUE) led = blue;
        else if (color == GREEN) led = green;
        else led = red;
        for(i = 0; i < 8; i++) {
        // if our led bit is set, output '0' to turn on the LED
            if((led & 0x80) == 0) {
                PORTD &= ~_BV(LED_SI);
            } else {
                PORTD |= _BV(LED_SI);
            }
            PORTD |= _BV(LED_SCLK);
            PORTD &= ~_BV(LED_SCLK);
            led <<= 1;
        }
    }
    // enable high, load shift register to output register
    PORTD |= _BV(LED_EN);
}

int main(void) {
    int n;
    unsigned char r, g, b;
    init();
    while (1) {
        n = rand();
        r = g = b = 0;
        if (n & 0x100) r = (unsigned char) n;
        if (n & 0x200) g = (unsigned char) n;
        if (n & 0x400) b = (unsigned char) n;
        setLED(r, g, b); // turn LEDs on to some random pattern and color
        _delay_ms(LED_SPEED);
        setLED(0, 0, 0); // turn LEDs off
        _delay_ms(LED_SPEED);
    }
}
//-------------------

Once these files are in your project, open up a cygwin window and change directory to your project folder. (cd)
You should be able to type:
make all

You should see something like this:

$ make all

-------- begin --------
avr-gcc (WinAVR 20090313) 4.3.2
Copyright (C) 2008 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


Compiling C: eblinky.c
avr-gcc -c -mmcu=atmega88 -I. -gdwarf-2 -DF_CPU=8000000UL -Os -funsigned-char -f
unsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-ad
hlns=./eblinky.lst  -std=gnu99 -MMD -MP -MF .dep/eblinky.o.d eblinky.c -o eblink
y.o

Linking: eblinky.elf
avr-gcc -mmcu=atmega88 -I. -gdwarf-2 -DF_CPU=8000000UL -Os -funsigned-char -funs
igned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhln
s=eblinky.o  -std=gnu99 -MMD -MP -MF .dep/eblinky.elf.d eblinky.o --output eblin
ky.elf -Wl,-Map=eblinky.map,--cref     -lm

Creating load file for Flash: eblinky.hex
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock eblinky.elf eblinky.hex

Creating load file for EEPROM: eblinky.eep
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" \\
        --change-section-lma .eeprom=0 --no-change-warnings -O ihex eblinky.elf
eblinky.eep || exit 0

Creating Extended Listing: eblinky.lss
avr-objdump -h -S -z eblinky.elf > eblinky.lss

Creating Symbol Table: eblinky.sym
avr-nm -n eblinky.elf > eblinky.sym

Size after:
AVR Memory Usage
----------------
Device: atmega88

Program:     756 bytes (9.2% Full)
(.text + .data + .bootloader)

Data:          4 bytes (0.4% Full)
(.data + .bss + .noinit)

-------- end --------

After doing the make all, plug in a Spot and try make deploy. It may ask to reset the Spot when doing the ant deploy portion.

You can restore the SPOT back to the original eDemo firmware with make restore.

A number of files will be generated by make. Open the eblinky.lss file and you will see C source interspersed with the generated
assembly code from the compiler. Here is a fragment:

           PORTD |= _BV(LED_SCLK);
  a6: 5c 9a        sbi 0x0b, 4 ; 11
            PORTD &= ~_BV(LED_SCLK);
  a8: 5c 98        cbi 0x0b, 4 ; 11


On the Atmega88, there are four GPIO peripherals A, B, C and D. Each GPIO peripheral has a set of three 8-bit registers: output (PORTn), input (PINn) and data direction register (DDRn). Each bit corresponds to a pin on the package of the microcontroller and a bit in each register. The data direction register (DDRn) defines whether the corresponding bit is an output ('1') or an input ('0'). If the Data direction register has a bit set as an output and the output register has that bit written as a '1', the output pin will be high. If the output bit is written as a '0', the pin will go low.  The input register reads the state of it's corresponding pins. Likewise, if the pin is low, the bit in the input register will read '0', else if the pin is high, it will read '1'. Now if the output register is read, it will read back the state that was last written not the state of the pins. So PORTD is defined as the memory mapped output register for port D in the avr/io.h chain.

_BV() or bit vector, is a macro defined in sfr_defs.h (part of io.h chain) as _BV(x) = 1 << (x). Register bit fields in the io.h include are defined by bit position not actual bit mask. PD4 is defined as 4 and _BV(PD4) = 1 << (4) = 0b00010000 or 0x10. PORTD |= _BV(LED_SCLK) would be the same as PORTD = PORTD | _BV(LED_SCLK). To clear a bit, we use bit and operation ('&=') and invert the bit mask ( '~'). The compiler is smart enough to recognize these are bit set and clear operations on an IO register and generates the appropriate instructions. The compiler will evaluate expressions with constants early and not generate additional code as is the case with 1 << (LED_SCLK).

Sometimes if the compiler does not see a variable change inside of a loop, it may optimize the variable away. The variable may be changing from an interupt routine or it is an memory mapped IO device. In these circumstances you need to force the compiler to keep the reference by declaring the variable 'volatile'.

Constant strings, arrays etc. do not necessarily reside in flash such as const char str[] = "Hello World\\n";  avr-gcc will copy the arrays from flash into SRAM on initialization and eat our precious RAM quickly. The alternative is to use the avr/pgmspace.h and define the strings as const prog_char str[] = "Hello World\\n"; . There are functions equivalent to those used in string.h for accessing the strings in pgmspace.h. I'd suggest reading the faq for the tools for other subtle differences between C on a desktop and AVR C.

A few SPOT specific caveats. The AVR can wedge the ARM causing general havoc for the SPOT. Unless you are using SPI and it is in Slave mode, do not enable Port B bit 4 - MISO (PB4) as an output. This will disrupt any SPI communication and the SPOT will not function well.  If you are not generating an interrupt to the, do not enable Port D bit 3 - IRQ0 (PD3) as an output. If you do enable it, the output is normally low and a high will cause an interrupt.

If you want to modify the existing eDemo firmware, the source code is on java.net in a subversion repository.  I use tortoiseSVN client for subversion on Windows and has a good UI.  You need an account on Java.net to get the sources and signing up is free.
The source to the demosensorboardfirmware.jar can be found here.

The AVR fuses are also programmed from the java. The fuses set the clock source, start location on reset, brownout detect levels, etc. The fuses are different than prior releases and is coded to reset to 0x0000 (no bootloader).

Known bugs:  If running more than one SPOT, it will ask for which SPOT to use. It may come back and look like it's hanging but it's waiting for input. I had to enter the SPOT number four times before it responded. I filed a bug report.

There was a problem of the system checking integrity of eDemo before deploying SPIFirmwareUpdater. I've corrected this in 7/21/2009 SPOT.make

Comments:

I've corrupted the bootloader on my sunSPOT while updating it to the latest SDK (pushed the 'reset' accidentely while it was writing). The activity LED is now continously red when i turn it on, and the operating system doesnt report any USB devices when i plug it in(whether on Mac OS or Windows). Is there any possible way how i can force restoring the bootloader at home whithout RMA process ? Its somehow a deadlock, the operating system doesnt see an USB, so how should it write to a device which is invisible.
I would appreciate any hint.

Best Regards,
Alexei Simakov

Posted by Alexei Simakov on September 13, 2009 at 09:24 PM PDT #

Alexei,
A better venue for this is the sunspotworld forums. First, we get rss feeds from the forums and you'll likely get a quicker answer.

This is the ARM bootloader that you are having a problem with. During RMA, we remove the eSPOT main board put it into a test fixture and reprogram the flash memory through jtag. We use pogo pins that come in contact to the jtag pads on the bottom of the board. I use the binaries in the arm directory of the SDK (bootloader-spot.bin) and write them to flash at 0x10000000 using a jtag controller.

I have been working on an openocd blog on talking to Spots over jtag but that it will be a while. It doesn't quite work yet. I've used both Macraigor usb2demon and the Segger jlink jtag controllers.

Bob

Posted by Bob Alkire on September 19, 2009 at 04:41 AM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

user12611170

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today