Mega External Memory Interface Library

I have written a small library to use the External Memory Interface on the Mega1280/2560. I still get confused by some of the pointer stuff, so if there's a better way of doing things, please let me know so that I may learn more. I'm looking for BETA testors, constructive comments, tips etc. I have succesfully used this library on an Arduino Mega1280 using a texas instraments bq3287EA RTC. See the library comments for details. I was able to read/write the RTC's registers as locations in RAM, as well as read/write to the RAM located in the RTC. I added address range checking. If the address is out of range, read functions return NULL and write functions won't write anything. I had to post the .cpp file seperately because of the size limit.

Here's the .h file,

ExMem.h:

// ExMem.h created for project ExMem Library on 09/17/2012 05:49:48

#ifndef ExMem_h
#define ExMem_h

#include <Arduino.h>

class exmem
{
    public:
        exmem();
        void enable(uint8_t addressWidth = 16, uint16_t lowerLimit = 0x0000,
                          uint8_t upperSectorWaitState = 0x00, uint8_t lowerSectorWaitState = 0x00);
        void disable(void);
        uint8_t readLower(uint16_t address);
        uint8_t readUpper(uint16_t address);
        void writeLower(uint16_t address, uint8_t data);
        void writeUpper(uint16_t address, uint8_t data);
};

extern exmem ExMem; // Create an external instance named ExMem

#endif

I'd suggest editing the subject line to be useful - ie include the keywords "Mega external memory interface library", make it easier for people to notice and search for it - you'll get more testers that way :slight_smile:

@MarkT

Thanks. Done.

DJ

80 characters per line is very good idea.

Here's the .cpp file,

ExMem.cpp:

ExMem.cpp:
[code]

// ExMem.cpp created for project ExMem Library on 09/17/2012 05:49:06

#include "ExMem.h"

// Pointer to first External Memory address.
uint8_t volatile *pExtMemory = reinterpret_cast <uint8_t volatile *> (0x2200);

// Offset from start of ext. memory(*pExtMemory) to start of upper sector.
static uint16_t upperSectorOffset = 0x0000;

// Highest accessable address.
static uint16_t upperLimit = 0xFFFF;

// Sector size = Highest valid address + 1.
static uint16_t upperSize = 0xDE00;
static uint16_t lowerSize = 0xDE00;

exmem::exmem()
{
}

// Using enable() with no parameters selects the default parameters.
// Any invalid value results in the default value being set.

// Valid addressWidth values =
// 8, 10, 11, 12, 13, 14, 15, 16(default)

// Valid upperSectorBoundry values =
// NONE(default), 0x4000, 0x6000, 0x8000, 0xA000, 0xC000, 0xE000 
//     *note    NONE(default) = NO lower sector

// Valid wait-state values for Upper and Lower sectors = 0(default), 1, 2, 3
// 0 = no wait-states
// 1 = 1 wait-state for read/write
// 2 = 2 wait-states for read/write
// 3 = 2 wait-states for read/write plus 1 wait-state after address to bus

void exmem::enable(uint8_t addressWidth, uint16_t upperSectorBoundry,
                               uint8_t upperSectorWaitState, uint8_t lowerSectorWaitState)
{
    // Release unused address pins from Port C
    switch (addressWidth)
    {
        // Clear External Memory High Mask register bits 
        XMCRB &= 0xF8; 
        case 8: // Releases all Port C pins (Mega pins 30-37)
            // Set highest accessable memory address
            upperLimit = 0x00FF; 
            // Set External Memory High Mask register bits
            XMCRB = 0x07;         
            break;
        case 10: // Releases Port C7-C2 (Mega pins 30-35)
            upperLimit = 0x03FF;
            XMCRB = 0x06;
            break;
        case 11: // Releases Port C7-C3 (Mega pins 30-34)
            upperLimit = 0x07FF;
            XMCRB = 0x05;
            break;
        case 12: // Releases Port C7-C4 (Mega pins 30-33)
            upperLimit = 0x0FFF;
            XMCRB = 0x04;
            break;
        case 13: // Releases Port C7-C5 (Mega pins 30-32)
            upperLimit = 0x1FFF;
            XMCRB = 0x03;
            break;
        case 14: // Releases Port C7-C6 (Mega pins 30-31)
            upperLimit = 0x3FFF;
            XMCRB = 0x02;
            break;
        case 15: // Releases Port C7 (Mega pin 30)
            upperLimit = 0x7FFF;
            XMCRB = 0x01;
            break;
        default: // Use all Port C pins(default)
            upperLimit = 0xFFFF;
            break;
    }
    // Adjust upperSectorBoundry for addressWidth
    uint16_t boundry;
    if (addressWidth < 15)
    {
        boundry = 0x0000;
    }
    else if (addressWidth < 16)
    {
        boundry = constrain(upperSectorBoundry, 0x0000, 0x6000);
    }
    else
    {
        boundry = upperSectorBoundry;
    }
 
    // Clears External Memory Upper Sector Limit bits
    XMCRA &= 0x8F; 
    // Sets the address boundry between lower and upper sectors
    switch (boundry)
    {
        case 0x4000: // Sets upper sector at 0x4000
            // Sets upper sector offset
            upperSectorOffset = 0x1E00;
            // Sets External Memory Upper Sector Limit bits
            XMCRA |= 0x20; 
            break;
        case 0x6000: // Sets upper sector at 0x6000
            upperSectorOffset = 0x3E00;
            XMCRA |= 0x30;
            break;
        case 0x8000: // Sets upper sector at 0x8000
            upperSectorOffset = 0x5E00;
            XMCRA |= 0x40;
            break;
        case 0xA000: // Sets upper sector at 0xA000
            upperSectorOffset = 0x7E00;
            XMCRA |= 0x50;
            break;
        case 0xC000: // Sets upper sector at 0xC000
            upperSectorOffset = 0x9E00;
            XMCRA |= 0x60;
            break;
        case 0xE000: // Sets upper sector at 0xE000
            upperSectorOffset = 0xBE00;
            XMCRA |= 0x70;
            break;
        default: // Sets upper sector at 0x0000(default)
            upperSectorOffset = 0x0000;
            break;
    }
    // Set upper/lower sector sizes
    if (0 < upperSectorOffset)
    {
        lowerSize = upperSectorOffset;
        upperSize = ((1 + exp(2, addressWidth)) - (0x2200 + upperSectorOffset));
    }
    else
    {
        lowerSize = upperSize = ((1 + exp(2, addressWidth)) - 0x2200);
    }
    // Clears Upper Sector Wait State register bits
    XMCRA &= 0xF3; 
    // Sets the upper sector wait-state
    switch (upperSectorWaitState) 
    {
        case 1: // 1 wait-state for read/write
            XMCRA |= 0x04;
            break;
        case 2: // 2 wait-states for read/write
            XMCRA |= 0x08;
            break;
        case 3: // 2 wait-states for read/write and 1 wait-state after address
            XMCRA |= 0x0C;
            break;
        default: // No wait-states
            break;
    }
    // Clears the Lower Sector Wait State register bits
    XMCRA &= 0xFC; 
    // Sets the lower sector wait-state
    switch (lowerSectorWaitState) 
    {
        case 1: // 1 wait-state for read/write
            XMCRA |= 0x01;
            break;
        case 2: // 2 wait-states for read/write
            XMCRA |= 0x02;
            break;
        case 3: // 2 wait-states for read/write and 1 wait-state after address
            XMCRA |= 0x03;
            break;
        default: // No wait-states
            break;
    }
    // Enable External Memory Interface
    XMCRA |= 0x80; 
}

void exmem::disable(void)
{
    // Disable External Memory Interface
    XMCRA &= 0x7F; 
}

////////////////////////////////////////////////////////////////////////////////
// The following functions Read To or Write From the device(s) connected to the
// External Memory Interface. The functions read/write a byte value from/to an
// address in the upper or lower sector. Lower sector addresses start at 0x0000
// to upperSectorBoundry - 1. Upper sector addressess start at 0x0000 to the 
// highest accessable address - upperSectorBoundry. The highest address is
// selected with the enable() function's addressWidth parameter. If there is 
// only an upper sector then the lower sector's range will be the same as the
// upper sector's. Both upper and lower functions can be used if there is only
// an upper sector. They will have the same effect.
////////////////////////////////////////////////////////////////////////////////

uint8_t exmem::readLower(uint16_t address)
{
    // Create a variable to holds the data.
    uint8_t data; 
    // Check if address is out of range.
    if (address >= lowerSize) 
    {
        // Out of range returns NULL byte.
        data = 0x00; 
    }
    else // Address is within range.
    {
        // Create a pointer to the lower sector address being read.
        volatile uint8_t *pMemory = (pExtMemory + address); 
        // Load variable with data stored at the address.
        data = *pMemory; 
    }
        return data; // Return with data.
}

uint8_t exmem::readUpper(uint16_t address)
// Create a variable to holds the data.
uint8_t data;
// Check if address is out of range.
if (address >= upperSize) 
{
    // Out of range returns NULL byte.
    data = 0x00; 
}
else // Address is within range.
{
    // Create a pointer to the upper seotor address being read.
volatile uint8_t *pMemory = (pExtMemory + upperSectorOffset + address);
// Load variable with the data stored at the address.
data = *pMemory; 
}
return data; // Return with data;
}

void exmem::writeLower(uint16_t address, uint8_t data)
{
    // Only perform write if address is within range.
    if (address < lowerSize)
    {
        // Create a pointer
        volatile uint8_t *pMemory;
        // Assign the address being pointed to.
        pMemory = (volatile uint8_t *)(pExtMemory + address);
        // Put the data at the address being pointed to.
        *pMemory = (uint8_t)(data);
    }
}

void exmem::writeUpper(uint16_t address, uint8_t data)
// Only perform write if address is within range.
if (address < upperSize) 
{
    // Create a pointer
    volatile uint8_t *pMemory;
    // Assign the address being pointed to.
    pMemory = (volatile uint8_t *)(pExtMemory + upperSectorOffset + address);
    // Put the data at the address being pointed to.
    *pMemory = (uint8_t)(data);

exmem ExMem = exmem(); // Create an instance named ExMem

Thanks again for your time,
DigitalJohnson
[/code]

These comments were in the .cpp file.

////////////////////////////////////////////////////////////////////////////////
// This Library is written for the Arduino Mega 1280/2560. See the ATmega1280/
// 2560 data sheet on how to connect your hardware to the External Memory 
// Interface of the ATmega. I have successfully tested this library with a
// Mega1280 and the texas instramens bq3287EA Real-Time Clock. I used a 74ACH573
// transparent latch between the mega and the RTC as per the data sheet. I tied
// the AD7:0 pins from the mega(pins 22-29) to the AD7:0 pins of the RTC
// (pins 4-11). I also tied AD7 of the mega(pin 29) to D7 of the latch. Then I
// tied the EXTRAM pin of the RTC to the Q7 output of the latch to access the
// upper 128 bytes of ram on the RTC. Use the ExMem.enable() function to set up
// and turn on the External Memory Interface. I used enable(8, 0x0000, 1, 0)
// for my tests, (8 = 8 bit addressWidth, 0x0000 = only an upper sector,
// 1 = 1 wait-state(upper sector), 0 = 0 wait-state (lower sector)). If there's
// only an upper sector, you can omit the last parameter.
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////
// You can have two different wait-state settings if needed. (eg. Two
// hardware devices each having different timing specs). If only one wait
// state setting is needed use the default(0x0000). Otherwise entering a 
// valid value creates an upper and	a lower sector, each having it's own
// wait-state set with the enable() function. If the default is used there 
// is only an upper sector. However, both "Upper" and "Lower" functions can
// still be used, as they will have the same effect.
////////////////////////////////////////////////////////////////////////////