Wednesday, June 1, 2016

Arduino's guts

After a long period of FreeRTOS posts, it's time to go back to Arduino to check some peculiarities. One of the great things that comes with an Arduino, besides the hardware ready to use out-of-the-box, is the Bootloader already burned in it. This means there is no need for the user to buy an expensive AVR programmer to work with it, you just have to plug it in an USB port and Download your application.

This bootloader uses a communication protocol originally developed by Atmel for the STK500 starterkit [http://www.atmel.com/Images/doc2525.pdf], which is why the protocol has this name. I believe there is no need to understand exactly how the protocol works, but a few remarks should be done. The bootloader will occupy some space in memory and that space mustn't be erased/overwritten, otherwise the bootloader is lost and we'll have to burn it again [https://www.arduino.cc/en/Hacking/Bootloader?from=Tutorial.Bootloader]. This can be prevented by correctly configuring the linker to only use addresses far from the bootloader and to place the main() function on the correct address that will be called.

So, let's check those information. First of all, we need to find the version of the bootloader that runs in our Arduino, so there goes a nice tip: send the string "!!!" to the Arduino just after it resets (you can use a serial terminal application, such as RealTerm [http://realterm.sourceforge.net/]). This will send us to the "Arduino Explorer" (a kind of terminal with a few commands we can send to the bootloader). So, press "H" to show the available commands: one of them should be "?=CPU stats" that will show which version is used. Step by step:

  1. Connect Arduino Board to your computer
  2. Open RealTerm and select the COM Port and Speed (115200), connect to COM Port
  3. Preset the string to be sent: "!!!" (3 exclamation points)
  4. Quickly, press the reset button on Arduino and send the String (this should be done within 1 or 2 seconds)
  5. Send "H" to check the available commands
  6. Send "?" to check the CPU Stats

In my case, the bootloader is the STK500v2 compiled in September 9th 2010 running on a ATmega2560. If you already installed the Arduino IDE, it's possible to analyze the code, under C:\Arduino\hardware\arduino\avr\bootloaders\stk500v2 (you can open the Programmer's Notepad project file STK500V2.pnproj).

Let's check some of the information we need to configure the linker in our port. First open the makefile of the bootloader and locate the part where the config variables for your board are. In my case, it's a mega2560, thus around line 337. One of the configurations is the BOOTLOADER_ADDRESS, set to 3E000 (hexadecimal), which means that the bootloader uses the last 8KB of read only memory (atmega2560 has 256KB of ROM, 0x3E000 == 248KB, 256 - 248 = 8KB).

Ok. From the paragraphs, please only remember the protocol your board uses (STK500v2) and that the maximum size of your program will have to be 248KB, which is a lot. I was writing and studying the subjects at the same time and just realized we won't have to configure much on the linker. If we were using an on board programmer, such as an ISP or JTAG, that doesn't communicate with the bootloader, than yes, we would have to take good care of the where the program would be programmed, but we're not. We'll actually use the bootloader through a serial connection and the bootloader will store our program in the ROM.

Enough with the bootloader, now: where is Arduino's main function? When you're using Arduino IDE, you have the setup() function, for configuring what you'll use, such as an analog output, a serial communication, etc, and the loop() function, which is basically what is in a while(1) loop. But what calls those functions? This is the job of the core library, which can be found under C:\Arduino\hardware\arduino\avr\cores\arduino. In the file main.cpp is defined the main() function, which initializes the microcontroller (function init()). Another few interesting functions are in wiring.c, such as the timer0 overflow interrupt, responsible for counting mili and microseconds (remember the delay() function?), and the init() itself. The wiring_analog.c has the analogRead() and analogWrite() definitions. pinMode(), digitalRead() and digitalWrite() are in wiring_digital.c.
int main(void)
{
    init();
    initVariant();

    #if defined(USBCON)
    USBDevice.attach();
    #endif

    setup();

    for (;;) {
        loop();
        if (serialEventRun) serialEventRun();
    }

    return 0;
}

FreeRTOS won't use any of this, as my intention is to have the RTOS running on the board as close to any other implementation as possible. This means that the main function will be the one defined in FreeRTOS' code and we won't have any loop() or setup() or digitalWrite() funcions. We're basically using the ArduinoMEGA as a development board that already comes with an easy to use bootloader and nothing more. The whole Arduino SDK will be nothing more than an application that we know it works and we can check for some configuration to use in our FreeRTOS port.

Next step will be to get Eclipse up and running with everything we need to upload our FreeRTOS.