Followers

Recent Posts

Monday, January 25, 2010

Byte by byte

Here are a few things which might aid you in your detective work.

First of all, I am still using avr-gcc and avrlibc to build the code. To achieve this, you first need to:

compile,
avr-gcc -mmcu=atmega128 -I. -g -Wall -Os -c *.c

link,
avr-gcc -o main.elf -mmcu=atmega128 *.o

and then pack
avr-objcopy -j .text -O ihex main.elf main.hex

Then download the resulting .hex file using the RBCUpgradeTool. Note that you can simply switch the RBC block off and on, rather than messing around with the reset pin.

This should help anyone who is trying to get to the bottom of the mystery I spoke about earlier. I also promised to talk a little about transferring data from the program space into working memory.

Unlike desktop computers, embedded systems are built according to the Harvard Architecture which keeps the program instructions separate from the working memory. They even have separate buses, which means that an opcode and an operand can be fetched on a single clock cycle. In normal operation, there is no need to exchange data between the two areas of memory, since self modifying code has been all but banished to the history books.

Sometimes, you find yourself running out of working memory especially if you are storing large tables or arrays which will eat up the free addresses very quickly. Also beware of memory leaks. Unlike a desktop with an operating system, your program won't segfault or blue-screen to let you know something is wrong - it will probably just start suddenly spewing garbage.

However, if you absolutley must use giant arrays or matricies in your program, you can keep your RAM free by storing them in the program memory and then copy it out only when you need to use it. This is done by using the LMP (LoadProgramMemory) instruction, which enables a special register which shifts the data from the program bus onto the data bus.

Unfortunatley, because the data bus is still only 8 bits wide, memory has to be loaded one byte at a time - so ints (which are 16 bits) need special routines to make sure that the high and low bytes are stored in adjacent locations. Also, reading a byte from flash memory (where the program is stored) takes more than a few clock cycles, during which execution needs to halt. Reading a large table could take up to a second.

Most of the details and heavy lifting are handled by special hardware and instructions on the microcontroller, and core data types like floats have library functions in avrlibc. It is still the responsibility of the programmer to know when to load a table, and particularly to make sure that any memory loaded goes out of scope or is free()d when the operation is complete.

The canned actions which are made using the robobuilder software suite can be exported as 16xN tables, where N is the number of poses (there are 16 servos for the basic robobuilder). For a complicated action, this could be many, many bytes - so the tables are necessarily stored in the flash memory. And of course, since only one action can be performed at a time, you can store dozens of actions in the 128kB of flash and simply swap them out when one needs to be performed.

No comments:

Post a Comment