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.
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
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]
////////////////////////////////////////////////////////////////////////////////
// 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.
////////////////////////////////////////////////////////////////////////////