// flash.c
/* ###################################################################**
Filename : FlashRoutines.c**
Processor : MC9S08**
FileFormat: V1.0**
DataSheet :**
Compiler : CodeWarrior compiler 5.7.0**
Date/Time : January 30, 2008 : 8am**
Abstract : ** .****
(c) Copyright EDS Engineering Pty Ltd 2007**
http : www.edsengineering.com.au**
mail : carl@edsengineering.com.au****
Notes for implmenting this code:** //This needs to be setup once on boot up
#ifdef _MC9S08 //If using S08, setup bus
while(FSTAT_FACCERR)
FSTAT_FACCERR = 1; //Make sure error is cleared before setting up the divider
if(!FCDIV_DIVLD) FCDIV = initFCDIV; //set fFCLK = about 200kHz (if not already setup i.e. on warm boot)
FPROT_FPOPEN = 1; //No flash protection
FPROT_FPS = 1; //No flash protection
#endif
**** ###################################################################*/
#include "derivative.h"
#include "FlashRoutines.h" //Flash data table as required
//Need to add anything here to the entries section in the PRM file or you will not be able to use it
#pragma CONST_SEG
UserFlashDataconst far byte FB01 = 0xC1;
const far byte FB02 = 0xC4;
#pragma CONST_SEG DEFAULT
#ifdef _MC9S08 //Flash block protect register set
const byte xFLBPR @ 0xFF7E = 0xFF; // NOTHING IS PROTECTED!
//xFLCR (Flash Control Register) This is not setup properly in the the GP32 header file for addressing far bytes
byte xFLCR @ 0xFE08; //
#define PGM 1 // Mask bits for FLCR
#define ERASE 2
#define MASS 4
#define HVEN 8
#endif
/*************************************************************************//****
Example / Test code *****//*************************************************************************/
//TEST CODE FOR MAIN LOOP
//This main loop simply writes 0x01 to 0x40 into the address of UserFlashData (see PRM file)
//PLease note, you must have the PRM setup correctly for this code
/*
byte RAMData[64] = {0}; //RAM Data that will be written to Flash
//TEST CODE
byte FlashData[64] = {0}; //Area to write to (for testing only)
byte *pRAMData = &RAMData[0];
byte temp = 0x00;
char *pFlashData = (byte *)Start_User_Flash_Data; //Point to the segment
(ref to PRM)while(pRAMData != (RAMData+64)) *pRAMData++ = ++temp ; //Fill RAMData with 0x01 to 0x40p
RAMData = &RAMData[0];
//Reset pointerDisableInterrupts;
//Must disable interupts while writing to flash
CopyInRAM();
//Move required routines to RAM for running (cannot write to flash while PC is in flash!)
EraseRoutine(pFlashData);
//Erase data before writing
WriteFlashRow(pRAMData, pFlashData);
//write data to flash
EnableInterrupts;
for(;;) PTC ^= 1; //Stop here, just toggle the output
}
*//*************************************************************************
//**** Copy routines to RAM (must execute from RAM!) *****
//*************************************************************************/
// This routine copies all the flash routines to ram as defined in// the PRM file
// Add this to the last SEGMENTS entry in the .PRM file
// ROM_IMAGE = READ_ONLY 0xF000 TO 0xF2FF RELOCATE_TO 0x0200;
//Where 0x0200 is the area in RAM free
// Add this line to the last entry under PLACMENT
// ToCopyToRAM
INTO ROM_IMAGE;
// For more information see TN228 from freescale, or search on "SEGMENTS"
// or "RELOCATE_TO" in the code warrior help files.
/*************************************************************************/
void CopyInRAM(void)
{
char *srcPtr, *dstPtr; int count;
srcPtr = (byte *)Start_Copy_In_RAM;
dstPtr = (byte *)&EraseRoutine; //Must be address of first routines
for (count = 0; count < (int) Size_Copy_In_RAM; count++, dstPtr++, srcPtr++)
{
*dstPtr = *srcPtr;
}
}
//WARNING: These function MUST be loaded into RAM before calling using
// CopyInRAM();
//Call prior to calling function in 'ToCopyToRAM'#pragma CODE_SEG ToCopyToRAM
/*************************************************************************/
/**** Erase a Block of FLASH Memory Subroutine *****/
/*************************************************************************/
// This routine erases a block of FLASH // Make sure interupts are off, code is loaded into ram, simply point to any
// addres in the block you want to erase
// Initializations required:
// Pass Value:
// FlashRow = Any address in the block of flash to erase
// Values returned:
// none
//Note: S08 family is a 512 block!
/*************************************************************************/
void EraseRoutine (byte *FlashRow)
//WARNING: This name is part of CopyInRAM, do not add code above this{
//- - - - - - - - - - - - - - - - - - - - - - - - - - -
//Runs this code when running MC9S08 processors#ifdef _MC9S08 byte tmpa; //Just used for chewing cycles
if(FSTAT_FACCERR)
FSTAT_FACCERR = 1; //Step 1 - If there is an access error, clear it while(!FSTAT_FCBEF) //Step 2 - Wait until ready to accept the command
__RESET_WATCHDOG(); //feed the dog
*FlashRow = 0xFF; // Step 3 - Write to any FLASH
FCMD = mPageErase; // Step 4 - Setup the erase command
FSTAT_FCBEF = 1; // Step 5 - Carry out command
__asm ror tmpa; // Step 6 - Chew some cycles as required by the data sheet (atleast four cycles, this will do 5)
if(FSTAT_FPVIOL || FSTAT_FACCERR)
FSTAT_FACCERR = 1; //Step 7 - If there is an access error, clear it, work out error handling later
while(!FSTAT_FCCF) // Step 8 - Wait for the command complete flag __RESET_WATCHDOG(); //feed the dog
#endif
//- - - - - - - - - - - - - - - - - - - - - - - - - - -
//Runs this code when running HC908 processors
#ifdef _HC908 xFLCR |= ERASE; // Step 1 - Set erase and clear MASS erase
xFLCR &= (~MASS); // MASS and xFLCR is actually xFLCR GP32 header is WRONG
__asm lda xFLBPR; // Step 2 - read from Flash block protect register *FlashRow = 0xFF; // Step 3 - Write to any FLASH
Delay(t10uS); // Step 4 - Delay 10uS
xFLCR |= HVEN; // Step 5 - Set the HVEN bit
Delay(t1mS); // Step 6 - Wait 1mS
xFLCR &= (~ERASE); // Step 7 - Clear the ERASE bit
Delay(t5uS); // Step 8 - Wait min 5uS
xFLCR &= (~HVEN); // Step 9 - Clear the HVEN bit
#endif
//- - - - - - - - - - - - - - - - - - - - - - - - - - -}
/*************************************************************************
//**** Flash Write Row *****
//*************************************************************************/
//Pass a pointer to the data in RAM to write, called pRAMData
//Pass a pointer to the Flash row to write to
//Warning, this WILL write the hole row i.e. 64 bytes, you must point to the START of a row!
void WriteFlashRow(byte* pRAMData, byte* pFlashData)
{
//- - - - - - - - - - - - - - - - - - - - - - - - - - -
//Runs this code when running MC9S08 processors
#ifdef _MC9S08 byte tmpa; //Just used for chewing cycles
byte StillData = 0x40; //How many bytes to write (complete 64 bytes)
if(FSTAT_FACCERR)
FSTAT_FACCERR = 1; //Step 1 - If there is an access error, clear it
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//Loop to program all bytes
while(StillData--)
{
__RESET_WATCHDOG(); //feed the dog
while(!FSTAT_FCBEF) //Step 2 - Wait until ready to accept the command __RESET_WATCHDOG(); //feed the dog
*pFlashData++ = *pRAMData++; // Step 3 - Write RAM data to FLASH FCMD = mBurstProg; // Step 4 - Setup the burst program command FSTAT_FCBEF = 1; // Step 5 - Carry out command
__asm ror tmpa; // Step 6 - Chew some cycles as required by the data sheet (atleast four cycles, this will do 5)
if(FSTAT_FPVIOL || FSTAT_FACCERR)
//Step 7 - If there is an access error, clear it, and exit (work out error handling later)
{
FSTAT_FACCERR = 1;
return;
}
}
while(!FSTAT_FCCF) // Step 8 - Wait for the command complete flag __RESET_WATCHDOG(); //feed the dog
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#endif
//- - - - - - - - - - - - - - - - - - - - - - - - - - -
//Runs this code when running HC908 processors#ifdef _HC908
byte StillData = 0x40; //How many bytes to write (complete 64 bytes)
byte* pFlashFirstByte = pFlashData; //Need to save this so we know when the row is
programmed xFLCR |= PGM; //Step 1 - Set PGM bit
__asm lda xFLBPR; // Step 2 - read from Flash block protect register
*pFlashFirstByte = 0xFF; //Step 3 - Write to any address in the flash range we are about to write data to
Delay(t10uS); //Step 4 - Wait for time tNVS xFLCR |= HVEN; //Step 5 - Set the HVEN enable Delay(t5uS); //Step 6 - Wait for time
//Step 7-8-9 - Write data to flash, delay 30uS, for entire row 0x40 bytes
(routine takes 30.1uS while(StillData--)
{
*pFlashData++ = *pRAMData++;
Delay(tPrg); //Tprog MAX is 40uS, allowing for the delay in the loop, this works out to ~30.1uS
}
xFLCR &= (~PGM); //Step 10 - Clear PGM bit
Delay(t5uS); //Step 11 - Delay for tNVH
xFLCR &= (~HVEN); //Step 12 - Clear HVEN
#endif
//- - - - - - - - - - - - - - - - - - - - - - - - }
/*************************************************************************
//**** Delay Routine *****
//*************************************************************************/
//Routine for delays above, not efficient at all, takes up time, and code space
//Please feel free to re write this and email to carl@edsengineering.com.au to be re posted
//Initializations required:
//Pass in variable A = Delay Value
//Values returned:
//None
/*************************************************************************/
void Delay (byte A)
{
unsigned char count;
while (A!=0)
{
for (count=0x03;count!=0;count--);
A--;
}
return;
}
#pragma CODE_SEG DEFAULT;
//WARNING: Do not add code to be ran from RAM below here
// S08 PRM
/* This is a linker parameter file for the mc9s08gb32a */
NAMES END
/* CodeWarrior will pass all the needed files to the linker by command line. But here you may add your own files too. */
SEGMENTS
/* Here all RAM/ROM areas of the device are listed. Used in PLACEMENT below. */
Z_RAM = READ_WRITE 0x0080 TO 0x00FF;
RAM = READ_WRITE 0x0100 TO 0x087F;
ROMDATA = READ_ONLY 0x8000 TO 0x87FF;
//2K reserved for flash constants
ROM = READ_ONLY 0x8800 TO 0xEFFF;
//Did end at 0xFFAF, moved back to make room for flash ram CDN 30/01/08
F_RAM = READ_ONLY 0xF300 TO 0xFFAF;
//Added read write block for flash data storage CDN 30/01/08
ROM1 = READ_ONLY 0xFFC0 TO 0xFFCB;
ROM_IMAGE = READ_ONLY 0xF000 TO 0xF2FF RELOCATE_TO 0x0600;
//CDN 30/01/08 9pm (0x0600 needs to be adjusted, beware of stack and normal ram)
/* INTVECTS = READ_ONLY 0xFFCC TO 0xFFFF;
Reserved for Interrupt Vectors */
ENDPLACEMENT
/* Here all predefined and user segments are placed into the SEGMENTS defined above. */ DEFAULT_RAM /* non-zero page variables */
INTO RAM;
FlashData INTO ROMDATA;
_PRESTART, /* startup code */ STARTUP, /* startup data structures */ ROM_VAR, /* constant variables */ STRINGS, /* string literals */ VIRTUAL_TABLE_SEGMENT, /* C++ virtual table segment */ DEFAULT_ROM, COPY /* copy down information: how to initialize variables */ INTO ROM;
/* ,ROM1: To use "ROM1" as well, pass the option -OnB=b to the compiler */ _DATA_ZEROPAGE, /* zero page variables */ MY_ZEROPAGE INTO Z_RAM; UserFlashData INTO F_RAM; //CDN 30/01/08 9pm
ToCopyToRAM INTO ROM_IMAGE; //CDN 30/01/08 9pm
ENDSTACKSIZE 0x50VECTOR 0 _Startup /* Reset vector: this is the default entry point for an application. */
//Required for flash routines
ENTRIES FB01 FB02
END
//Just setup this define below to chose which processor you are using, include flash.h in your main.c
// flash.h
#define _MC9S08 //Use this to run for this family
// #define _HC908 //Use this to run for this family#include "derivative.h"
#pragma CONST_SEG UserFlashData extern const far byte FB01;
extern const far byte FB02;
#pragma CONST_SEG DEFAULT
/*************************************************************************
//***** Program-Specific Defines *****
//*************************************************************************/
#ifdef _MC9S08 //This must be setup based on the clock settings
#define initFCDIV 0b01001100 //FLASH clock divider (setup for 20Mhz Bus)
// ||||||||
// |||||||+-DIV0 \
// ||||||+--DIV1 |
// |||||+---DIV2 >-- divide by (12+1)
// ||||+----DIV3 | BUSCLK/(8*12)~=(192.3Khz)
// |||+-----DIV4 |
// ||+------DIV5 /
// |+-------PRDIV8 -- divide (prescale) by 8
// +--------DIVLD --- read-only status
#endif
extern char __SEG_START_ToCopyToRAM[]; //Used for copying code to RAMextern char
__SEG_SIZE_ToCopyToRAM[]; //Used for copying code to RAMextern byte __SEG_START_UserFlashData[]; //Used for copying code to RAMextern byte __SEG_SIZE_UserFlashData[]; //Used for copying code to RAMvoid EraseRoutine (byte*);void Delay (byte);
void CopyInRAM(void);
void WriteFlashRow(byte*, byte*); //Start_Copy_In_RAM refers to the begining of the segment
//ToCopyToRAM. This segment contains the functions after
//they have been copied to RAM.#define Start_Copy_In_RAM __SEG_START_ToCopyToRAM
#define Size_Copy_In_RAM __SEG_SIZE_ToCopyToRAM
//Start_In_ROM refers to the begining of the segment
//ToCopyToRAM. This segment contains the functions in ROM.
//refers to the beginining of the segment
#define Start_User_Flash_Data __SEG_START_UserFlashData
//UserFlashData start
#define User_Flash_Data __SEG_START_UserFlashData
//Size of UserFlashData as defined in PRM file
#define Size_User_Flash_Data __SEG_SIZE_UserFlashData
#ifdef _HC908
/*************************************************************************/
/***** FLASH Delay Equates *****/ /*************************************************************************/
// To setup these values use
// 30X + 31 cycles where X is the value u pass
// NOTE, when passing 0x00, it only takes 30 cycles, not 31.
// to take into account bus frequency
// (30X + 31) / Bus Frequency = Time
// GP32 Set at 4.9152uS //
// .XLS formula =((30*A2)+31) / $C$2
// Where A2 is a colum from 0 > 255, and Cell C2 = bus frequency in Hertz
// These results arnt correct, but they are close enough for the flash erase/write /*************************************************************************/
#define t5uS 0x00 // ~5uS (6.31uS)
#define t10uS 0x01 // ~10uS (12.4uS)
#define tPrg 0x02 // Used for delay in programming bytes
#define t30uS 0x03 // ~30uS (30.7uS)
#define t1mS 0xA3 // 1ms (1.00mS)
#endif
//908 prm file /* This is a linker parameter file for the GP32 */ NAMES END /* CodeWarrior will pass all the needed files to the linker by command line. But here you may add your own files too. */ SEGMENTS /* Here all RAM/ROM areas of the device are listed. Used in PLACEMENT below. */ ROM = READ_ONLY 0xA000 TO 0xFDFF; //Moved from 0x8000 to 0x9000 to make room for flash data storage
F_RAM = READ_ONLY 0x9000 TO 0x9FFF; //Added read write block for flash data storage
Z_RAM = READ_WRITE 0x0040 TO 0x00FF;
RAM = READ_WRITE 0x0100 TO 0x023F;
ROM_IMAGE = READ_ONLY 0xF000 TO 0xF2FF RELOCATE_TO 0x017A;
//CDN 02/07/07 7pm
ENDPLACEMENT
/* Here all predefined and user segments are placed into the SEGMENTS defined above. */ DEFAULT_RAM INTO RAM;
DEFAULT_ROM, ROM_VAR, STRINGS INTO ROM;
/* In case you want to use as well, be sure the option -OnB=b is passed to the compiler. */ _DATA_ZEROPAGE, MY_ZEROPAGE INTO Z_RAM;
UserFlashData INTO F_RAM; //CDN 02/07/07 10pm
ToCopyToRAM INTO ROM_IMAGE; //CDN 02/07/07 7pm
ENDSTACKSIZE 0x50VECTOR 0 _Startup
/* Reset vector: this is the default entry point for an application. */
ENTRIES FB01 FB02
END