在HCS08 微控制器上 操作EEPROM 2
0赞
使用的 EEPROM 的大小。最大为 510 字节。但是当此值比较小时,程序占用的资源比
较经济,因为在进行模拟的 EEPROM 读、写和更新操作时,都占用了以该值为大小的栈空
间。因此也要仔细分配工程的栈空间大小,在工程的链接器参数 PRM 文件中指定适当的
STACKSIZE。
• HCS08_FLASH_START_ADDR
定义为控制器的片上 FLASH的起始地址。每个不同的微控制器该值可能不同。模拟的
EEPROM 的空间被分配在 FLASH空间的头两页中。在PRM 文件中可以找到这个地址。注
意,在 PRM 文件中,ROM 的分配空间也要做相应修改,使其起始地址为原来值+1024。
• HCS08_FLASH_PAGE_SIZE
FLASH页面大小。对于 HCS08 微控制器为512 字节。
• HCS08_BUS_FREQUENCY
微控制器运行的总线频率。此值用于设定正确的FCLK。
• HCS08_FLASH_PROTECTION
该值用于设置 FLASH保护的NVPROT。
HCS08_EE_Init
函数原型:
void HCS08_EE_Init(void);
功能描述:
该函数用于初始化FDIV。使用 API 时,必须在程序初始化时调用该函数。
参数:
无。
返回值:
无。
HCS08_EE_WriteRecord
函数原型:
void HCS08_EE_WriteRecord(unsigned char *src);
功能描述:
该函数用于保存一个 EEPROM 数据记录。所有的 EEPROM 数据都被写入到 FLASH存
储器当前页的下一个空记录地址空间单元。
参数:
unsigned char *src。指向要写入的数据源。
返回值:
无。
HCS08_EE_ModifyByte
函数原型:
void HCS08_EE_ModifyByte(unsigned int index, unsigned char data);
功能描述:
该函数用来更新 EEPROM 数据记录中的指定字节。在执行时,首先把纪录读出,更新
指定字节数据后,再写入。
参数:
unsigned int index,要更新的字节在记录中的索引;
unsigned char data,更新的数据。
返回值:
无。
HCS08_EE_ReadRecord
函数原型:
void HCS08_EE_ReadRecord(unsigned char *dest);
功能描述:
该函数找到当前有效的记录,并读取其数据到缓冲区。
参数:
unsigned char *dest,读取数据缓冲区。
返回值:
无。
HCS08_EE_ReadByte
函数原型:
unsigned char HCS08_EE_ReadByte(unsigned int index);
功能描述:
读取 EEPROM 数据记录中指定的一个字节。
参数:
unsigned int index,目标字节在记录中的索引。
返回值:
unsigned char,读取得数据。
总结
使用本文给出的 API,用户只要制定需要使用的 EEPROM 的长度,再做一些基本设置之
后,即可以使用 HCS08 的 FLASH模拟 EEPROM。由于 API 屏蔽了底层的操作,将接口函
数封装为类似于真实 EEPROM 的操作,使得 EEPROM 的模拟可以快速可靠地实现。但是本
API 使用的资源(FLASH和栈)较多,并且尚未加入冗余设计,在实际应用中可酌情使用。
附录:HCS08_EEPROM_API源代码
HCS08_EEPROM_API.h
/**
* Copyright (c) 2006, Freescale Semiconductor
* File name : HCS08_EEPROM_API.h
* CPU : MC9S08
* Compiler: CodeWarrior v5.1 08C Compiler
*
* Author : Jerry Shi (b01136@freescale.com)
*
* Description : This head file includes definition for subroutines that
* emulating EEPROM with FLASH memory on HCS08 MCU.
*
* History :
* 10/08/2006 : API works on MC9S08QG8CDTE
*/
#ifndef __HCS08_EEPROM_API_H
#define __HCS08_EEPROM_API_H
/**
** Below macros define some nesseccsry data for EEPROM emulation.
** Users MUST modify those macros according to their own application.
** PRM file also should be modified to update the ROM address and
** reserve EEPROM address (2 pages).
** STACKSIZE must be carefully reviewed because the APU functions
** uses many stack size. Do-on-stack code size is 43 bytes.
** Function HCS08_EE_ModifyByte() uses as much as EEPROM_DATA_LEN
** bytes of stack to hold the data buffer.
**/
/**
** EEPROM data is treated as fixed-size record.
** FLAG, DATA0, DATA1, DATA2, ..., DATAn
**
**/
#define EEPROM_DATA_LEN 0x20
/**
** Refer to the PRM file of your project and define HCS08_FLASH_START_ADDR
** accordingly. Also the PRM file should be modified to specify the new ROM
** address. 2 pages are used for EEPROM emulatio, thus ROM = ROM + 0x200.
**/
#define HCS08_FLASH_START_ADDR 0xE000
/**
** Page size of HCS08 is 0x200
**/
#define HCS08_FLASH_PAGE_SIZE 0x200
/**
** CPU bus frequncy, modify it according to your system configuration.
** Unit is kHz. For example, 4 MHz is defined as 4000 (kHz)
**/
#define HCS08_BUS_FREQUENCY 4000
/**
** FLASH block protection. Default is off.
** It is stongly to turn it on to protect the code space of CPU.
**/
#define HCS08_FLASH_PROTECTION 0xFF
/**
** Possible return values of operation result.
**/
#define HCS08_EE_ERROR_OK 0x00
#define HCS08_EE_ERROR_UNKOWN 0xFF
#define HCS08_EE_ERROR_OUT_OF_RANGE 0x01
/**
** Below macros are defined for API use only.
** Do NOT modify it.
**/
#define FLAG_CURENT_PAGE 0xAA
#define FLAG_RECORD_USED 0x55
#define FLASH_CMD_BYTEPROG 0x20
#define FLASH_CMD_PAGEERASE 0x40
#define EEPROM_START_ADDR HCS08_FLASH_START_ADDR
#define EEPROM_PAGE_SIZE HCS08_FLASH_PAGE_SIZE
/**
** how many times one page can be re-used
** (HCS08_FLASH_PAGE_SIZE - 1) / (EEPROM_DATA_LEN + 1)
**/
#define PAGE_REUSE_TIMES (HCS08_FLASH_PAGE_SIZE - 1) / (EEPROM_DATA_LEN + 1)
/**
** Functions declarations of API.
**/
void HCS08_EE_Init(void);
void HCS08_EE_WriteRecord(unsigned char *src);
void HCS08_EE_ReadRecord(unsigned char *dest);
void HCS08_EE_ModifyByte(unsigned int index, unsigned char data);
unsigned char HCS08_EE_ReadByte(unsigned int index);
#endif
HCS08_EEPROM_API.c
/**
* Copyright (c) 2006, Freescale Semiconductor
* File name : HCS08_EEPROM_API.c
* CPU : MC9S08
* Compiler: CodeWarrior v5.1 08C Compiler
*
* Author : Jerry Shi (b01136@freescale.com)
*
* Description : This file includes definition for subroutines that
* emulating EEPROM with FLASH memory on HCS08 MCU.
*
* History :
* 10/08/2006 : API works on MC9S08QG8CDTE
*/
#include <hidef.h> /* for EnableInterrupts macro */
#include <stdio.h> /* for memcpy() */
#include "derivative.h" /* include peripheral declarations */
#include "hcs08_eeprom_api.h"
#pragma MESSAGE DISABLE C4001 // condition always FALSE
static void flash_byte_prog(unsigned int _addr, unsigned char _data);
static void flash_page_erase(unsigned int _addr);
static unsigned char flash_byte_read(unsigned int _addr);
const unsigned char __NVPROT @ 0xFFBD = HCS08_FLASH_PROTECTION;
/*
** ===================================================================
** Method : HCS08_EE_Init
**
** Description :
** fFCLK initialization code for FLASH programming and erasing.
** fFCLK must be within the range from 150 kHz to 200 kHz.
** After this initialization methdo, fFCLK is always arround
** 175 kHz, which is within the range of specification.
** Patameters:
** no.
** Return Values:
** no
** ===================================================================
*/
void HCS08_EE_Init(void)
{
/**
** fFCLK setting
** FCDIV: 7 6 5 4 3 2 1 0
** | | |
** | | +DIV[5:0]: fFCLK = fBUS / (8exp(PRDIV8) * DIV +1)
** | +PRDIV8: Prescale by 8, 1: input to the FLASH clock divider is **
the bus rate clock divided by 8.
** +DIVLD: divisor load status, 1: has been written since reset.
*/
if (!FCDIV_DIVLD)
{
if (HCS08_BUS_FREQUENCY >= 12000)
{
FCDIV = (((HCS08_BUS_FREQUENCY / (8*175))) | 0x40) - 1;
}
else
{
FCDIV = (HCS08_BUS_FREQUENCY / 175) - 1;
}
}
}
/*
** ===================================================================
** Method : HCS08_EE_WriteRecord
**
** Description :
** this function writes the whold data record to the next
** available space block of FLASH, also the Record Has Been
** used flag before the data. If the current page will be full,
** it erases the next page.
** Patameters:
** unsigned char *. -- point to the source data
** Return Values:
** no
** ===================================================================
*/
void HCS08_EE_WriteRecord(unsigned char *src)
{
unsigned int i, j;
unsigned char flag_page_switch;
unsigned int ee_current_page, ee_next_page;
flag_page_switch = 0;
ee_current_page = EEPROM_START_ADDR;
ee_next_page = EEPROM_START_ADDR + EEPROM_PAGE_SIZE;
// find the current page
if (FLAG_CURENT_PAGE !=
flash_byte_read(ee_current_page + EEPROM_PAGE_SIZE - 1))
{
ee_current_page = EEPROM_START_ADDR + EEPROM_PAGE_SIZE;
ee_next_page = EEPROM_START_ADDR;
}
for (;;)
{
for (i = 0;
i < PAGE_REUSE_TIMES * EEPROM_DATA_LEN;
i = i + EEPROM_DATA_LEN + 1)
{
if (FLAG_RECORD_USED != flash_byte_read(ee_current_page + i))
{
// this record space is empty
// wite flag to mark this record is used
flash_byte_prog(ee_current_page + i, FLAG_RECORD_USED);
// then write the record data to the following addresses
for (j = 0; j < EEPROM_DATA_LEN; j++)
{
flash_byte_prog(ee_current_page + 1 + i + j, *(src + j));
}
// do we need to switch to next page?
if (flag_page_switch)
flash_page_erase(ee_next_page);
return;
}
}
// all the records are visited now, but does not find the current record
// page switch
j = ee_next_page;
ee_next_page = ee_current_page;
ee_current_page = j;
flag_page_switch = 1;
// mark the current page
flash_byte_prog((ee_current_page + EEPROM_PAGE_SIZE -1),
FLAG_CURENT_PAGE);
}
return;
}
/*
** ===================================================================
** Method : HCS08_EE_ModifyByte
**
** Description :
** this function find the current data record from the current
** page, and modifies the specified byte of the record.
** Patameters:
** unsigned int *. -- index of target byte to be modified in the record
** unsigned char *. -- source data
** Return Values:
** no
** ===================================================================
*/
void HCS08_EE_ModifyByte(unsigned int index, unsigned char data)
{
unsigned char buff[EEPROM_DATA_LEN];
HCS08_EE_ReadRecord(buff);
buff[index] = data;
HCS08_EE_WriteRecord(buff);
return;
}
/*
** ===================================================================
** Method : HCS08_EE_ReadRecord
**
** Description :
** this function finds the current record in the current page,
** then reads the all the data in the record to a buffer.
** Patameters:
** unsigned char *. -- point to the dest buffer
** Return Values:
** no
** ===================================================================
*/
void HCS08_EE_ReadRecord(unsigned char *dest)
{
unsigned int i, j;
unsigned int ee_current_page;
ee_current_page = EEPROM_START_ADDR;
// find the current page
if (FLAG_CURENT_PAGE != flash_byte_read(ee_current_page + EEPROM_PAGE_SIZE - 1))
ee_current_page = EEPROM_START_ADDR + EEPROM_PAGE_SIZE;
// visit all the records till find out the 'current' record
for (i = 0;
i < (PAGE_REUSE_TIMES - 1) * EEPROM_DATA_LEN;
i+= EEPROM_DATA_LEN + 1)
{
if (FLAG_RECORD_USED != flash_byte_read(ee_current_page + 1 + i +
EEPROM_DATA_LEN))
break;
}
// when the 'current' record is found, read data of it
for (j = 0; j < EEPROM_DATA_LEN; j++)
*(dest + j) = flash_byte_read(ee_current_page + 1 + i + j);
return;
}
/*
** ===================================================================
** Method : HCS08_EE_ReadByte
**
** Description :
** this function find the current data record from the current
** page, and reads the specified byte of the record.
** Patameters:
** unsigned int *. -- index of target byte to be read in the record
** Return Values:
** unsigned char *. -- data
** ===================================================================
*/
unsigned char HCS08_EE_ReadByte(unsigned int index)
{
unsigned int i;
unsigned int ee_current_page;
ee_current_page = EEPROM_START_ADDR;
// find the current page
if (FLAG_CURENT_PAGE != flash_byte_read(ee_current_page + EEPROM_PAGE_SIZE - 1))
ee_current_page = EEPROM_START_ADDR + EEPROM_PAGE_SIZE;
// visit all the records till find out the 'current' record
for (i = 0;
i < (PAGE_REUSE_TIMES - 1) * EEPROM_DATA_LEN;
i+= EEPROM_DATA_LEN + 1)
{
if (FLAG_RECORD_USED != flash_byte_read(ee_current_page + 1 + i +
EEPROM_DATA_LEN))
break;
}
// when break, the current record is found
return flash_byte_read(ee_current_page + i + 1 + index);
}
/* ===================================================================*/
// Array of opcode instructions of the Erase/Program function in
// the HCS08 family
// size = 40 bytes
const unsigned char ROM_PGM[] =
{
0x9E, 0xFE, 0x03, // LDHX 3,SP ; get the target address '_addr'
/* PC is pushed when calling, 2 bytes used (SP is decreased by 2)
** SP is now pointing to the next available unit
** thus '_addr' is at [SP+3]
*/
0xF7, // STA ,X ; store 'data' to 'addr'
0xA6, 0x20, // LDA #$20 ; load FCMD, CMD should be changed before
calling
0xC7, 0x18, 0x26, // STA $1826 ; FCMD
0x45, 0x18, 0x25, // LDHX #$1825 ; address of FSTAT
0xF6, // LDA ,X ; load FSTAT to A
0xAA, 0x80, // ORA #$80 ; set FCBEF to lunch the command
0xF7, // STA ,X ; store back to FSTAT
0x9D, // NOP ; wait for 4 cycles
0x9D, // NOP
0x9D, // NOP
0x9D, // NOP
0x45, 0x18, 0x25, // LDHX #$1825 ; check FACCERR and FPVIOL
0xF6, // LDA ,X
0xA5, 0x30, // BIT #$30
0x27, 0x04, // BEQ *+6 ; branch to label_1
0xA6, 0xFF, // LDA #$FF ; return 0xFF if flag error
0x20, 0x07, // BRA *+9 ; branch to label_2
//label_1:
0xC6, 0x18, 0x25, // LDA $1825 ; wait for command to be completed
0xA5, 0x40, // BIT #$40
0x27, 0xF9, // BEQ *-5 ; branch to label_1
//label_2:
0x81 // RTS ; return
};
/*
** ===================================================================
** Method : flash_byte_prog
**
** Description :
** flash_byte_prog programs one byte of data to the specified
** address of FLASH. this is an internal method only used by API.
** Patameters:
** unsigned int, -- target address to be programmed
** unsigned char. -- data to be programmed
** Return Values:
** no
** ===================================================================
*/
static
void flash_byte_prog(unsigned int _addr, unsigned char _data)
{
unsigned char _pgm[sizeof(ROM_PGM) + 2];
// save '_addr'
_pgm[0] = (unsigned char)((_addr & 0xff00) >> 8);
_pgm[1] = (unsigned char)(_addr & 0x00ff);
// copy the code to stack (local variable _pgm[])
(void)memcpy(_pgm + 2, ROM_PGM, sizeof(ROM_PGM));
_pgm[7] = FLASH_CMD_BYTEPROG; // change FCMD value
DisableInterrupts;
if (FSTAT&0x10){ // Check to see if FACCERR is set
FSTAT = FSTAT | 0x10; // write a 1 to FACCERR to clear
}
__asm
{
LDA _data // pass '_data' thru A
TSX // transfer SP to HX
JSR 2,X // first 2 bytes of _pgm[] are '_addr'
}
EnableInterrupts;
}
/*
** ===================================================================
** Method : flash_page_erase
**
** Description :
** flash_page_erase erases one page that contains the specified
** address of FLASH. this is an internal method only used by API.
** Patameters:
** unsigned int. -- target address to be erased
** Return Values:
** no
** ===================================================================
*/
static
void flash_page_erase(unsigned int _addr)
{
unsigned char _pgm[sizeof(ROM_PGM) + 2];
// save '_addr'
_pgm[0] = (unsigned char)((_addr & 0xff00) >> 8);
_pgm[1] = (unsigned char)(_addr & 0x00ff);
// copy codes to stack (local variable _pgm[])
(void)memcpy(_pgm + 2, ROM_PGM, sizeof(ROM_PGM));
_pgm[7] = FLASH_CMD_PAGEERASE;// change FCMD value
DisableInterrupts;
if (FSTAT&0x10){ // Check to see if FACCERR is set
FSTAT = FSTAT | 0x10; // write a 1 to FACCERR to clear
}
__asm
{
TSX // transfer SP to HX
JSR 2,X // first 2 bytes of _pgm[] are '_addr'
}
EnableInterrupts;
}
/*
** ===================================================================
** Method : flash_byte_read
**
** Description :
** flash_byte_read reads one byte data from the specified
** address of FLASH. this is an internal method only used by API.
** Patameters:
** unsigned int. -- target address to be erased
** Return Values:
** unsigned char. -- the data
** ===================================================================
*/
static
unsigned char flash_byte_read(unsigned int _addr)
{
return *(unsigned char *)_addr;
}
