一、概要
STM8系列一般拥有如下几种三种数据区
用户启动区域(UBC)包含有复位和中断向量表,它可用于存储IAP及通讯程序;
数据EEPROM(DATA)区域可用于存储用户具体项目所需的数据;
主程序区是指在FLASH程序存储器中用于存储应用代码的区域;
选项字节用于配置硬件特性和存储器保护状态。
作为应用而言,一般主要使用EEPROM(DATA),存放各种参数、或者离线数据、状态数据等等。
下面以以STM8L052R8为例,简单说明对其的访问方法。
根据STM8L052R8的手册,其有Memory信息如下:
■ Memories
– 64 KB Flash program memory and 256 bytes data EEPROM with ECC, RWW
– Flexible write and read protection modes
– 4 KB of RAM
可知其具有256字节的EEPROM。并带有ECC校验,和RWW(读同时写)功能。
RWW特性允许户在执行程序和读程序存储器时对DATA EEPROM区域进行写操作,
因此执行的时间被优化了。相反的操作是不允许的:即不允许在写程序寄存器是对其进行读操作。
RWW特性是一直有效的而且可以在任意时刻使用
对EEPROM编程也有如下几种方式,顾名思义,很容易理解其含义。
字节编程方式最易于理解,也最简单。
二、更深入的细节
STM8系列有存储器存取安全系统(MASS),在复位后,主程序和DATA区域都被自动保护以防止无意的写操作。
在修改其内容前必须对其解锁,而解锁的机制由存储器存取安全系统(MASS)来管理。(UBC始终为写保护)
因此写操作时需要先解除写保护,并在完成写入后恢复写保护(视应用而定)。
Unlock的具体操作是,向FLASH_DUKR寄存器连续写入两个被叫作MASS密钥的值:
如果解锁成功,FLASH_IAPSR中的DUL位被置为1,表示成功。
应用必须检测这个标志才可进行后续操作。
(编程区与之类似,但写入的是PUKR,且2个密钥顺序相反)
对EEPROM的读写其实非常简单,就是直接对地址按字节进行赋值和取值。
但是在操作后,需要等待其操作完成。判断方法是:
另外,试图向被保护页进行写操作时,会发生错误,此时FLASH_IAPSR得WR_PG_DIS标志位会置1。
所以,最终的判断方法是:
HVOFF或者WR_PG_DIS被置为1,前者为正常介绍,后者表示出错
三、示例代码
地址范围定义(读写范围为0~127字节)
#define DATA_MEMORY_START_ADDR (FLASH_DATA_EEPROM_START_PHYSICAL_ADDRESS)
#define DATA_MEMORY_STOP_ADDR (FLASH_DATA_EEPROM_START_PHYSICAL_ADDRESS + 128)
初始化函数
void flash_init(void)
{
// 设置编程时间,指定标准编程时间即可
FLASH_SetProgrammingTime(FLASH_ProgramTime_Standard);
// 解锁EEPROM区域(注意type是Data)
FLASH_Unlock(FLASH_MemType_Data);
// 等待解锁成功
// 本质是判断FLASH->IAPSR寄存器的DUL标志位是否变为1。1表示写保护消除,0为保护中
// 任何时候都可以通过变更此标志位为0来恢复写保护状态
while (FLASH_GetFlagStatus(FLASH_FLAG_DUL) == RESET);
}
读函数
uint8_t flash_read(uint32_t FlashAddr, uint8_t *dest, uint8_t nbyte)
{
uint8_t i = 0;
// 越界判断
if((FlashAddr < DATA_MEMORY_START_ADDR)||(FlashAddr+ nbyte > DATA_MEMORY_STOP_ADDR)) {
return FALSE;
}
// 按字节读
for(i=0; i// 等待操作完成,此处未处理错误
FLASH_WaitForLastOperation(FLASH_MemType_Data);
}
return nbyte;
}
写函数
uint8_t flash_write(uint32_t FlashAddr, uint8_t *source, uint8_t nbyte)
{
uint8_t i = 0;
// 越界判断
if((FlashAddr < DATA_MEMORY_START_ADDR)||(FlashAddr+ nbyte > DATA_MEMORY_STOP_ADDR)) {
return FALSE;
}
// 按字节写
for(i=0;i// 等待操作完成,此处未处理错误
FLASH_WaitForLastOperation(FLASH_MemType_Data);
}
return nbyte;
}
四、库函数实现解析
FLASH_Unlock函数
void FLASH_Unlock(FLASH_MemType_TypeDef FLASH_MemType)
{
/* Unlock program memory */
if(FLASH_MemType == FLASH_MemType_Program)
{
FLASH->PUKR = FLASH_RASS_KEY1;
FLASH->PUKR = FLASH_RASS_KEY2;
}
/* Unlock data memory */
// 连续两次赋值密钥(固定值)
if(FLASH_MemType == FLASH_MemType_Data)
{
FLASH->DUKR = FLASH_RASS_KEY2; /* Warning: keys are reversed on data memory !!! */ /* 0xAE */
FLASH->DUKR = FLASH_RASS_KEY1; /* 0x56 */
}
}
FLASH_ReadByte、FLASH_ProgramByte、FLASH_EraseByte
由下可知,读写擦出均为直接操作地址。
uint8_t FLASH_ReadByte(uint32_t Address)
{
/* Read byte */
return(*(PointerAttr uint8_t *) (MemoryAddressCast)Address);
}
void FLASH_ProgramByte(uint32_t Address, uint8_t Data)
{
*(PointerAttr uint8_t*) (MemoryAddressCast)Address = Data;
}
void FLASH_EraseByte(uint32_t Address)
{
*(PointerAttr uint8_t*) (MemoryAddressCast)Address = FLASH_CLEAR_BYTE; /* Erase byte */
}
FLASH_WaitForLastOperation 操作等待
FLASH_Status_TypeDef FLASH_WaitForLastOperation(FLASH_MemType_TypeDef FLASH_MemType))
{
uint16_t timeout = OPERATION_TIMEOUT;
uint8_t flagstatus = 0x00;
/* Wait until operation completion or write protected page occurred */
// 程序区等待IAPSR的EOP或者WR_PG_DIS标识位被置为1
if(FLASH_MemType == FLASH_MemType_Program)
{
while((flagstatus == 0x00) && (timeout != 0x00))
{
flagstatus = (uint8_t)(FLASH->IAPSR & (uint8_t)(FLASH_IAPSR_EOP |
FLASH_IAPSR_WR_PG_DIS));
timeout--; // 贴心的超时处理
}
}
else
{
// 数据区的话,等待IAPSR的HVOFF或者WR_PG_DIS标识位被置为1
while((flagstatus == 0x00) && (timeout != 0x00))
{
flagstatus = (uint8_t)(FLASH->IAPSR & (uint8_t)(FLASH_IAPSR_HVOFF |
FLASH_IAPSR_WR_PG_DIS));
timeout--; // 贴心的超时处理
}
}
if(timeout == 0x00)
{
// 超时
flagstatus = FLASH_Status_TimeOut;
}
return((FLASH_Status_TypeDef)flagstatus);
}
以上,相比直接操作寄存器,用库做STM开发还是比较有效率的。