打印
[开发资料]

CW32L010用FLASH模拟EEPROM

[复制链接]
46|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
zhanan|  楼主 | 2024-11-19 11:18 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
CW32L010横空出世,定时器和ADC变化很大,FLASH基本和以前型号一样,但有一点改动,BUSY位从CR1寄存器改到ISR寄存器了。
把F003的程序改改就能用,太棒了,拿走不谢。

只有一个文件 eeprom.c,添加到项目中去,下面是和别的程序怎么配合:

#include "CW32L010.h"
#include "eeprom.h"

u16 FEE_data[5]={100,200,300,400,500}; // eeprom变量数组,通过函数读写。eeprom变量名称→枚举名→数组下标

int main(void)
{

  FEE_init(); // eeprom初始化
  while(1)
  {
    工作程序();

  }
}
/***/

void 工作程序(void)
{
    temp=FEE_rd(En1) +1; // 读  En1是eeprom变量的枚举名,在eeprom.h中定义

    FEE_wr(temp,En1);    // 写
}


使用特权

评论回复
沙发
zhanan|  楼主 | 2024-11-19 11:21 | 只看该作者
eeprom.h 内容
#include "CW32L010.h"

extern u16 FEE_data[5]; //变量表初始值,数组标号对应变量ID。变量表和菜单参数放在一起
typedef enum {En0,En1,En2,En3,En4} en_feedata_t; // 变量ID

extern void FEE_init(void); // eeprom初始化
extern u16 FEE_rd(en_feedata_t); // 读
extern void FEE_wr(u16,en_feedata_t); // 写


使用特权

评论回复
板凳
zhanan|  楼主 | 2024-11-19 11:25 | 只看该作者
eeprom.c 内容

/*
项目组织
1.将eeprom.c添加到项目中
2.在设置菜单所在c文件中定义数组u16 FEE_data[],并赋予初始值
3.修改eeprom.h中FEE_data数组大小及变量id枚举名
运用
4.上电初始化 FEE_init();
5.FEE_rd(en_feedata_t); // 读,数组标号用枚举名
   FEE_wr(u16,en_feedata_t); // 写
*/
#include "eeprom.h"

#define FEE_LOCKS  (512*8) /* 锁定区字节数,一个锁定位管8页4096字节,以下地址须按此对齐*/
#define FEE_P0ADDR  0x0000F800  /* P0首地址 */
#define FEE_PAGES   1  /* 每P页数只能1页 */
#define FEE_WORDS  (512/4 * FEE_PAGES)  /* 字数 = 页字节数/4 *页数 */
#define FEE_P1ADDR (FEE_P0ADDR + FEE_WORDS*4) /* P1首地址 */

static u32 FEEcounts; // 累计擦除次数(用作启用页页首标记)
static u32 FEEaddr; // 当前页首地址,指向当前启用页
static u16 FEEoffset; // 页指针,指向当前页内可写地址

static void FEEeof(void); // 存满处理
static void FEEput(u32 addr); // 转存处理
static void FEEextr(u32 addr); // 提取数据
static void FEEerase(u32 addr); // 擦除
static void FEEerase_put(u32 addr); // 擦存处理
/***/
u16 FEE_rd(en_feedata_t id) // eeprom读
{
  return FEE_data[id]; // 直接读取数组成员
}
/***/
void FEE_wr(u16 data, en_feedata_t id) // eeprom写
{
  if(data!=FEE_data[id])
  {
    u32 addr;
    FEE_data[id]=data; // 更新数组成员

    addr=FEEaddr+FEEoffset*4; // 写FLASH地址
    FLASH->PAGELOCK = 0x5A5A0000|(1<<(addr/FEE_LOCKS)); // 解锁地址所属锁定区
    while (FLASH->ISR & 0x20); // 等待空闲
    FLASH->CR1 = 0x5A5A0001; // 页编程模式
    *((u32 *)addr) = ((u32)id)<<16|data; //id在高半字,值在低半字
    while (FLASH->ISR & 0x20); // 等待完成
    FLASH->CR1 = 0x5A5A0000; // 改为只读
    FLASH->PAGELOCK = 0x5A5A0000; // 锁定页面
    FEEoffset++; // 指向下一个字
    FEEeof(); // 存满处理
  }
}
/***/
static void FEEput(u32 addr) // 转存
{
  FLASH->PAGELOCK = 0x5A5A0000|(1<<(addr/FEE_LOCKS)); // 解锁地址所属锁定区,转存用到的地址必须都在同一个锁定区
  while (FLASH->ISR & 0x20); // 等待空闲
  FLASH->CR1 = 0x5A5A0001; // 页编程模式
  for(FEEoffset=1; FEEoffset<=(sizeof(FEE_data)/sizeof(FEE_data[0])); FEEoffset++) // 数组各成员
  {
    *((u32 *)(addr+FEEoffset*4)) = ((FEEoffset-1)<<16)|FEE_data[FEEoffset-1]; // id在高半字,值在低半字
    while (FLASH->ISR & 0x20); // 等待完成
  }
  *((u32 *)addr) = ++FEEcounts; // 页首写累计擦除次数作为启用标记
  while (FLASH->ISR & 0x20); // 等待完成
  FLASH->CR1 = 0x5A5A0000; // 改为只读
  FLASH->PAGELOCK = 0x5A5A0000; // 重新锁定
  FEEaddr=addr; // 作为当前页
}
/***/
static void FEEerase(u32 addr) // 擦除指定页面(只擦一页,每P有多页要改)
{
  FLASH->PAGELOCK = 0x5A5A0000|(1<<(addr/FEE_LOCKS)); // 解锁地址所属锁定区
  while (FLASH->ISR & 0x20); // 等待空闲
  FLASH->CR1 = 0x5A5A0002; // 页擦除模式
  *((u32 *)addr) = 0; // 页面上写启动擦除
  while (FLASH->ISR & 0x20); // 等待完成
  FLASH->CR1 = 0x5A5A0000; // 改为只读
  FLASH->PAGELOCK = 0x5A5A0000; // 重新锁定
}
/***/
static void FEEerase_put(u32 addr) // 擦存
{       
  FEEerase(addr); //先擦除
  FEEput(addr); //再转存
}
/***/
static void FEEextr(u32 addr) // 提取数据
{
  u32 data;
  FEEaddr=addr; //作为当前页
  for(FEEoffset=1; FEEoffset<FEE_WORDS; FEEoffset++) // 页首字是启用标记,从下一个字开始
  {
    data=*(u32 *)(addr+FEEoffset*4); // 字内容
    if(~data) // 非空,有数据
    {
      FEE_data[data>>16] = data & 0xFFFF;// 替换数组成员值
    }
    else // 空,数据区结束
    {
      break;
    }
  }               
}
/***/
static void FEEeof(void) // 存满处理
{
  if(FEEoffset>=FEE_WORDS) //指针到达字数
  {
    u32 temp=FEEaddr; // 当前页
    FEEput((temp==FEE_P0ADDR)?(FEE_P1ADDR):(FEE_P0ADDR)); // 转存到另一页
    FEEerase(temp); //擦除旧页               
  }
}
/***/
void FEE_init(void) // 上电初始化
{
  u32 temp,temp0,temp1;

  SYSCTRL->AHBEN |= 0x5A5A0002; //开FLASH时钟

  temp=0;
  temp0 = *(u32 *)FEE_P0ADDR; //P0首字
  if(~temp0) temp|=1; // 非空(有0)
  if(~(*(u32 *)(FEE_P0ADDR + 4))) temp|=2; //P0数据非空
  temp1 = *(u32 *)(FEE_P1ADDR); // P1首字
  if(~temp1) temp|=4; // 非空
  if(~(*(u32 *)(FEE_P1ADDR + 4))) temp|=8; //P1数据非空
  switch (temp) /* 检查两个页面状态 */
  {
    case 0: case 2: // 0000:P0 P1全空 或 0010:P0初始化失败
      FEEcounts=1;
      FEEerase_put(FEE_P0ADDR); //擦存P0
      break;
    case 3: // 0011:P0启用中
      FEEcounts=temp0; // 累计擦除次数
      FEEextr(FEE_P0ADDR); //提取P0数据
      break;
    case 8: // 1011:P0>P1转存失败
      FEEcounts=temp0;
      FEEextr(FEE_P0ADDR); //提取P0数据
      FEEerase_put(FEE_P1ADDR); //擦存P1
      FEEerase(FEE_P0ADDR); //擦除P0
      break;
    case 12: // 1100:P1启用中
      FEEcounts=temp1;
      FEEextr(FEE_P1ADDR); //提取P1数据
      break;
    case 14: // 1110:P1>P0转存失败
      FEEcounts=temp1;
      FEEextr(FEE_P1ADDR); //提取P1数据
      FEEerase_put(FEE_P0ADDR); //擦存P0
      FEEerase(FEE_P1ADDR); //擦除P1
      break;
    case 15: // 1111:擦除失败
      if(temp0>temp1) // 标记数大的作为启用页
      {
        FEEcounts=temp0;
        FEEextr(FEE_P0ADDR); //提取P0数据
        FEEerase(FEE_P1ADDR); //擦除P1
      }
      else
      {
        FEEcounts=temp1;
        FEEextr(FEE_P1ADDR); //提取P1数据
        FEEerase(FEE_P0ADDR); //擦除P0
      }
      break;
  } /* 检查页面状态end */
  FEEeof(); // 满页处理
}
/***/
/*******************/


使用特权

评论回复
地板
zhanan|  楼主 | 2024-11-19 11:29 | 只看该作者
本帖最后由 zhanan 于 2024-11-19 11:32 编辑

原理参考这个链接:https://bbs.21ic.com/icview-3282436-2-1.html

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

10

主题

183

帖子

0

粉丝