Melding freeRTOS with ChaN’s FatF & HD44780 LCD on Freetronics EtherMega

This post is a bit of a mixed bag, describing some software and hardware integration, together with some raving about a great tool I’ve been using. So, let’s get started.

Platform

Some time ago I got a Freetronics EtherMega, which is essentially an Arduino Mega2560 with an integrated Wiznet W5100 Ethernet interface, and a MicroSD card cage. I’ve introduced this product and the use of freeRTOS here.

EtherMega (Arduino Mega 2560) and freeRTOS

One great thing about the ATmega2560 used in the EtherMega, and the Arduino Mega2560, is the availability of an external memory bus. I’ve been using a Rugged Circuits QuadRAM, and now have ordered three more of their MegaRAM devices, and intend to make the ATmega2560 my standard platform. Why three? Well everyone knows that good things come in threes.

QuadRAM (512kByte) on Freetronics EtherMega (Arduino) ATmega2560 with freeRTOS

I’m actually preferring the Rugged Circuits MegaRAM which has only 128kBytes of RAM, so it won’t be as flexible for bank switching as its big brother. Also its chip select line is reversed (note to self to fix this in the driver). But, simply having 64kBytes of normal extended RAM plus another 56kBytes of special purpose (bank switched) RAM seems like it wil be sufficient for the duration. I’ve bought a couple to go on some Android ADK devices, that I’ll write about soon.

Recently, I also acquired a Freetronics 16×2 LCD-keypad-shield to use as a drop-on display for debugging and status, and anything really. It works really nicely and with its single pin switch analogue interface (which will be useful for navigation). Unfortunately there is a conflict between the SD Card device select pin on the EtherMega (Arduino pin D4) and one of the data pins on the 16×2 LCD.

My rectification of this pin usage conflict can be seen on the pictures below, where the yellow wire joins Pin D4 to Pin D2. What can’t be seen is that the leg of Pin D4 has been cut off, so it doesn’t insert into the EtherMega, so there is no elecrical connection between the D4 pin on the 16×2 LCD, and the D4 Pin used on the EtherMega.

P1010882P1010884P1010883P1010886

Tools

Recently I purchased a Saleae Logic to use in developing. I have got to say that this is probably the best $149 that I have spent on any tool, ever. Having the ability to capture long periods (minutes) of data, with 24MHz resolution, and zoom, shrink, drag, flick around in it, and also compare many windows of alternative samples is just so great. It saves so much time being able to simply “see” what the device is actually doing on the SPI, I2C and serial ports, simultaneously, is well great. But I already said that.

P1010781

Software – FatF File System

As usual, the code is on AVRfreeRTOS on Sourceforge.

My main work was to put the existing ChaN’s FatFs Generic FAT File System Module v0.9 into my existing ATmega2560 freeRTOS system, using my existing libraries, and generally being fully integrated into the system, as a plaform for some further work.

This was fairly time consuming (until I got my Saleae Logic), because the SPI bus transfers required to initiate and drive a SD card are complex, and depend on which version of SD card (MMC, SD, HDSD) is being used.

Now that everything is working, I’ve also done some SPI optimisation, to speed up multi-byte SPI bus transfers used for reading and writing to the SD card.

In testing with a Freetronics EtherMega driving an 4GByte HDSD card the system achieved the following results.

  • Byte transfer cycle time: MOSI 3.750us, MISO 3.6250us
  • Multibyte transfer cycle time: MOSI 1.3333us, MISO 1.3750us
  • Gross Performance increase: MOSI 2.8x, MISO 2.64x

Measured performance for a multi-MegaByte file copy is about 140kBytes/s which includes both read and write operations to the same SD card.

Software – HD44780 LCD

As usual, the code is on AVRfreeRTOS on Sourceforge.

Also, as I had purchased a 16×2 LCD display and I wanted to implement a flexible solution for display, I also ported the Control Module for HD44780 Character LCD into my system.

This was pretty straightforward, once I’d recognised the pin confict issue between the two Freetronics devices, and perhaps the most interesting things to say are:

  • Using a macro to control the pin assignment means that it is very easy to change the pins used for any display type. Simply renumber them in the macro and it is done.
  • Also, using the standard avr-libc stdio utility vsprintf formatting allows me to choose how much library I want to bring in. The standard library doesn’t support float formatting, but with a simple link switch either a simpler (smaller) or more fully featured (larger) standard library can be included. I also use the standard avr-libc tools for the serial port, so there is no additional overhead specifically for the LCD.

Wiznet W5100

Now I’ve finished the W5100 drivers from Wiznet, incorporating their new v1.6 changes (because they screwed up the silicon ARP state machine). And also, a fix for a subtle bug caused
by writing to the W5100 Tx buffer before it was finished with a previous transmission. This was fixed by checking the Tx read pointer and comparing it with the Tx write pointer. When the chip is idle, they are the same. That took me 3 weeks to isolate.

Now the fun part starts, which will be to re-learn the IP protocol suites, through re-implementing some of the standard network tools, like HTTP, FTP, NTP, DHCP, DNS, that we just take for granted. DHCP is done. Ping is done. HTTP is done.

Here is a web server running on this platform.

EtherMega server

QuadRAM (512kByte) on Freetronics EtherMega (Arduino) ATmega2560 with freeRTOS

I’ve been spending some time integrating the Rugged Circuits QuadRAM extension for the Arduino Mega (or Freetronics EtherMega) into the freeRTOS environment. And, it is now my standard environment. Actually, the MegaRAM, a slightly cheaper 128kByte version is my standard, as I’ve not found an application yet that needs more than 64kBytes total RAM. But, that will happen.

QuadRAM is available from Rugged Circuits again, after a long intermission.

Without adding any complexity into the environment, I can address up to 56kBytes of heap variables, effectively leaving the entire 8kbyte of internal RAM for the stack. With no complexity or overhead.

In addition, with some simple commands within a task can implement bank switching to access up to 7 additional banks of RAM, each up to 56kBytes.

A further degree of integration into freeRTOS is to completely automate memory bank switching, to give each of either 7 or 15 tasks a bank of RAM for its exclusive use. But this is a goal for the next few months.

Here are some pictures.

P1010781P1010782P1010783

And here are the links to the products described.

Rugged Circuits QuadRAM

 

I’m very happy with my Saleae Logic too. Must write a review on this, one day. Great tool, more useful every day.

The described code is all available on Sourceforge, if you’re intending to try this at home.

The only finished example using the memory routines is the MegaSD project. Take a look at it on Sourceforge to see how to use the extra RAM.

HOW TO

What do we have to do to get this build working? Well it is pretty simple really, once everything is figured out. It is really only three steps.

  1. Initialise the RAM, and tell the AVR that it should enable its extended RAM bus.
  2. Tell the compiler that you’re moving the heap into the new extended RAM address space.
  3. Tell freeRTOS to move its heap to the new extended RAM address space.

Initialise the RAM

The RAM should be initialised prior to building the standard C environment that comes along for the ride. It can be done in the .init1 (using assembler) or in .init3 in C. I built both methods, but elected to just use the C method, as it is more maintainable (legible).

There are a number of references for this code. Some of the older ones refer incorrectly to a MCUCR register. That is not correct for the ATmega2560.

This example covers what Rugged Circuits suggest for their testing of the QuadRAM, but it doesn’t put the initialisation into .init3, which is needed to make the initialisation before heap is assigned. It makes the initialisation carefree.

// put this C code into .init3 (assembler could go into .init1)
void extRAMinit (void) __attribute__ ((used, naked, section (".init3")));
void extRAMinit (void)
{
// Bits PL7, PL6, PL5 select the bank
// We're assuming with this initialisation we want to have 8 banks of 56kByte (+ 8kByte inbuilt unbanked)
DDRL  |= 0xE0;
PORTL &= ~0xE0;  // Select bank 0
// PD7 is RAM chip enable, active low. Enable it now.
DDRD |= _BV(PD7);
PORTD &= ~_BV(PD7);
// Enable XMEM interface
XMCRA = _BV(SRE); // Set the SRE bit, to enable the interface
XMCRB = 0x00;

To ensure that this .init3 function, that I’ve put into lib_ext_ram, is included in your linked code, we need to call something from the lib_ext_ram library. If you’re planning to use the banks of RAM, then this is easy as you’ll naturally be calling the bank switching functions.

However, if you only want to use the extra 56kByte of RAM for simplicity (it is after all 7x more than you have available with just the internal RAM), then just call this function once from somewhere, possibly main(). I have added it to the freeRTOS stack initialisation function in port.c, so I don’t need to see it ever again.

extRAMcheck();

It returns the XMCRA value, that can be tested if you desire. But there’s no need as things will anyway have gone badly pear shaped if the RAM is not properly enabled. Calling this once is all that is needed to ensure that the .init3 code is properly inserted into the linked code.

Note: that the above code is specific to the QuadRAM device. The MegaRAM device has different IO in use, and the differences are noted in my code on Sourceforge.

Move the heap

The standard C heap has to be moved to the new location above the stack. There are other memory allocation options, but in my opinion this is the most sensible one and the only one I’m planning to implement.

The __heap_start and __heap_end symbols describe the addresses occupied by the extended RAM, and inform malloc() of the location of the heap. This is described in more detail here http://www.nongnu.org/avr-libc/user-manual/malloc.html. This is a great diagram showing the situation.

Malloc-x2
avr-gcc -Wl,-Map,MegaSDTest.map -Wl,--gc-sections -Wl,--section-start=.ext_ram_heap=0x802200  -Wl,--defsym=__heap_start=0x802200,--defsym=__heap_end=0x80ffff -mmcu=atmega2560 -o "MegaSDTest.elf" $(OBJS) $(USER_OBJS) $(LIBS)

Tell freeRTOS

Now freeRTOS has to be made aware of these changes to the heap location. There are three heap management options available for the AVR port. The two most memory economical options use a fixed array of memory defined in the .data section on initialisation. Clearly, this is not going to be useful. For the third option heap_3.c, which uses malloc(), we have nothing more to do.

However, getting heap_1.c and/or heap_2.c to work is not that complicated either. There are three parts to this. Firstly, creating a new section name, and locating it at the start of the desired heap space. We’ve already done that, above, with the –section-start command. The forth option heap_4.c has also been implemented.

Secondly, we have to make a small modification to both heap_1.c heap_2.c and heap_4.c to inform the compiler that the freeRTOS heap will be located at this .ext_ram_heap location. That is done in this manner (heap_2.c shown).

static union xRTOS_HEAP
{
//... edited ...
unsigned char ucHeap[ configTOTAL_HEAP_SIZE ];
//... edited ...
#if defined(portEXT_RAM)
} xHeap  __attribute__((section(".ext_ram_heap"))); // Added this section to get heap to go to the ext memory.
#else
} xHeap;
#endif

And finally, now we’ve (probably, just because we can) allocated a large (up to 32kByte maximum) freeRTOS heap, we need to ensure that the loader omits this section from its preparations for writing the .hex file to flash (in a similar manner to the way the .eeprom section is removed).

avr-objcopy --remove-section=.eeprom --remove-section=.ext_ram_heap -O ihex MegaSDTest.elf  "MegaSDTest.hex"

Something to watch for is that none of your other code is calling malloc(), because if it does its memory allocations will collide with the freeRTOS heap. Either check that malloc() is not being linked in or, for the paranoid, just assign the heap_1.c heap_2.c or heap_4.c heap to a region separate to your new malloc() heap addresses.

And that’s all there is to getting an easy 512kByte of fast no-wait-state RAM on your Freetronics EtherMega or Arduino Mega2560. Enjoy!

EtherMega (Arduino Mega 2560) and freeRTOS

This is an unboxing and porting review of the Freetronics EtherMega.

Ethermega-production-sample-2_large

It is an Arduino Mega 2560 compatible product, with the added goodness of a Wiznet W5100 Ethernet module and a MicroSD card cage on board.

http://www.freetronics.com/products/ethermega-arduino-mega-2560-compatible-with-onboard-ethernet

Obviously, my first job is to port the freeRTOS code to run on the EtherMega ATmega2560 microcontroller. There are some things about the ATmega2560 that are different from other ATmega devices, making it necessary to modify the standard freeRTOS code for it to work correctly. But the main difference is that because of the size of its flash memory, it has a 3 byte program counter (rather than 2 byte in every other ATmega device).

There are only a few references to making the ATmega2560 work. This reference is the most compelling guide.

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=70387

Stu_San has proposed a port, and I have used it as an accurate guide to getting things running. There are some changes. freeRTOS has changed significantly with regard to how the port.c file is built. Also, I’ve already implemented options for Timer selection. So the resultant code on the Sourceforge AVRfreeRTOS Project is slightly different to what Stu_San proposed.

https://sourceforge.net/projects/avrfreertos/

The avr-linker script needs to have a small addition to support Stu_San’s suggestions. For the purposes of simplicity (only change a little as possible, to get things working), I’ve only included the .task tag into the avr6.x file, that will replace the normal avr-binutils file in /usr/lib/ldscripts

UPDATE: as of avrlibc 1.8.0 there is a .lowtext section in avr6.x included at the bottom of flash, which is exactly what we need for this outcome. Hence, in the port for freeRTOS800 I’ve converted from .task to use .lowtext, which means there is no need to change or modify the avr6.x file. It will just work automagically.

At this stage the EtherMega is nicely flashing its LED at me, which means that the scheduler and IO addressing using my digitalAnalogue tools are working correctly. But, I’m sure there are many things still to improve over the coming days and weeks.

Update – 25 January 2012

Corrected an error calculating the serial port baud rate. The (all) AVR data sheet has an error in it, that only exhibits at certain CPU frequencies. Unfortunately, I’ve normally only used Arduino and Freetronics kit over-clocked to 22.1184MHz, which provides perfect baud rates with no error. This CPU frequency does not exercise the faulty data sheet calculation, to generate errors.

WormFood’s AVR Baud Rate Calculator

The standard Arduino CPU frequency of 16MHz causes the serial port to run too slow by 3.7% at 115200 baud (well outside the recommended range of 2%), and more importantly the data sheet calculation chooses the wrong set up value for the UBRR value driving the baud clock, causing the serial port to become unworkable. The calculation method has been improved in the AVR <util/setbaud.h> file, and that calculation method is now used in my serial library. New code uploaded.

Update – 8 March 2012

Completed the ChaN FatF library port to freeRTOS for driving the microSD card. Supports both SD and HCSD. Code in usual place.

Update – 5 April 2012

Completed the Ethernet library port to freeRTOS for driving the Wiznet W5100. Supports TCP, UDP and Raw IP modes. Code in usual place.

The standard Arduino Mega stk500v2 bootloader used by default in the Freetronics EtherMega does not work when loading more than 64kB into flash. I’ve found a modified version by msproul that works, or at least does now that I’ve changed it a little more, to be found here.