加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

【图文解说】基于飞思卡尔MC9S12XS的Flash擦除和写入操作

发布时间:2020-12-15 19:49:53 所属栏目:百科 来源:网络整理
导读:? ? ? ? ?关于Flash的擦除和写入,真的是让我最费力的一部分,网上的相关资料很少,好不容易找到了一点相关代码,却发现程序不能正常的运行,而且更令人无解的是程序本身怎么检查都检查不出错误。好啦,一点一点的说说我的辛酸史。 ???????? 至于擦除和写入

? ? ? ? ?关于Flash的擦除和写入,真的是让我最费力的一部分,网上的相关资料很少,好不容易找到了一点相关代码,却发现程序不能正常的运行,而且更令人无解的是程序本身怎么检查都检查不出错误。好啦,一点一点的说说我的辛酸史。

???????? 至于擦除和写入的原理是什么,这个不是我们关心的,我也不去赘述,我主要说明一下相关的具体操作。

???????? 我们主要是对飞思卡尔Flash中的PFlash即存放程序段的Flash进行操作。如图:


步骤大概分为:

1、设置Flash分频寄存器:

???????? Flash的操作对频率有一定的要求,过低擦除不成功,过高会损毁Flash,如图为:


MC9S12XS128的FCLKDIV寄存器,所有位都是可读的但是只有7位可写入。至于应该如何设置分频数,有一张表格可以参考:


如表格说明,分频数是根据外部时钟来定的,MC9S12XS128的外部时钟是16MHz,所以我选择了0x10作为分频数。

给出一一些具体的代码:

[cpp]? view plain ?copy
  1. /***********************************************************??
  2. **?名??????称:void?PFlash_Init(void)??
  3. **?功??????能:PFlash初始化???
  4. **?入口参数:无??
  5. **?出口参数:无??
  6. **?使用说明:无??
  7. ************************************************************/???
  8. void?PFlash_Init(void)??
  9. {??
  10. ??while(FSTAT_CCIF==0);??????????//等待正在处理的Flash操作,即CCIF=1时,操作完成??
  11. ??FCLKDIV=0x10;????????????????//外部晶振为16MHz,Flash时钟设置为1MHz??
  12. ??FCNFG=0x00;?????????????????//禁止中断??
  13. while(FCLKDIV_FDIVLD==0);?????//等待时钟设置成功,即FDIVLD为1时设置成功??
  14. }??
这里补充一下对 FCLKDIV_FDIVLD 的说明

说明很简答,配合着程序注释一看就懂。

2FSTAT状态寄存器

在上面的代码中出现了一行“while(FSTAT_CCIF==0);”,这就涉及到一个很重要的寄存器,就是Flash操作的状态寄存器,就像是Flash操作步骤中的“红绿灯”一样,告诉程序什么时候可以执行什么操作,什么时候必须要等待一下。如图:


其中我们比较关心的是CCIF位、ACCERR位和FPVIOL位,这三位也都是可读可写的。

CCIF:指令完成标志位,当CCIF位为1时,表示上一个Flash操作完成了。注意图中的高亮部分,如果想要使当前操作的指令完成,需要手动将CCIF清零(标志位置位就是令标志位为0,但具体操作是令标志位等于1),才能触发指令,令指令完成,也就是前面流程图中说明的,才能launch指令。

ACCERR和FPVIOL: 这两位主要标志是否有错误操作发生,当为1时,说明检测到错误。那检测到作物怎么办呢?解决方法也比较奇葩,将两个标志位清零(清零操作如上)就行,,,,,至于什么原理,我也不太明白。



数据手册如图,强烈建议还是自己读一下原文,很有好处。

???????? 再给出一些具体的代码:

CCIF的操作代码:

FSTAT_CCIF=1;???????????//启动执行命令,即launch指令??

  • while(FSTAT_CCIF==0);????//等待执行完成??
  • ACCERR和FPVIOL的操作代码:

    if(FSTAT_ACCERR)???????????//判断并清除标志位;????

  • ??????FSTAT_ACCERR=1;????//将标志位清零??
  • ?if(FSTAT_FPVIOL)?????????????????FSTAT_FPVIOL=1;??????//将标志位清零??

  • 注意点:说实话,当时我自己写程序的时候,就对标志位清零操作感到非常的疑惑,说好的清零操作呢,为什么是令标志位为等于1?后来才想明白,对于标志位,写0等效于没有操作,写1代表清零。原因主要有两点:

    1)??????标志位为1一般表示有什么事情发生啦,对于标志位什么时候为1,应该是单片机根据具体的情况作出自己的判断,是根据实际情况来置1的,而不应该是人为的置1。

    2) ??如果某次操作我们只想对状态寄存器中的某一位或某几位进行操作,那么对其余位就必须没有任何的影响,如果规定写0时等效于无操作。

    3、执行具体的指令

    ???????? 将分频和状态寄存器弄好后,就可以开开心心的执行具体的指令啦。指令具体的指令主要涉及到FCCOBIX寄存器FCCOB寄存器



    FCCOBIX寄存器和FCCOB寄存器必须要配合使用。FCCOBIX进行选择,然后往FCCOB中写入具体的指令、地址和数据。Flash操作命令表如图:


    这里我们只关心P-Flash的擦除和写入操作。

    P-Flash的擦除操作:如图,;令CCOBIX为000,往FCCOB中写入0x0A和高地址位,对照指令表,0X0A表示擦除操作,高地址位就是Global address模式下地址的高八位。然后令CCOBIX为001,往FCCOB写入Global address模式下地址的第八位,就可以擦除你想要的擦除的地址片区。


    **?名??????称:void?PFlash_Erase(word?ADDR16)??
  • **?功??????能:擦除P-FLASH的一个分区??
  • void?PFlash_Erase(word?ADDR16)??
  • while(FSTAT_CCIF==0);????
  • ??????FSTAT_ACCERR=1;????
  • ??????FSTAT_FPVIOL=1;??
  • ?????
  • ??FCCOBIX_CCOBIX=0x00;????
  • ??FCCOB=0x0A7E;??????????//写入擦除命令和高位地址,0A是指令,7E是高地址位??
  • ??FCCOBIX_CCOBIX=0x01;???????
  • ??FCCOB=ADDR16;???????????//写入低16位的地址????
  • ??FSTAT_CCIF=1;?????????????//启动执行命令????
  • while(FSTAT_CCIF==0);??????//等待执行完成????
  • }??
  • P-Flash的写入操作:写入操作和擦除操作是差不多的:


    模式是不是和擦除操作很像。CCOBIX为000和001时的含义和擦除操作是完全一样的。主要讲一下CCOBIX为010~101时的含义。因为MC9S12XS128规定Flash写入时,必须一次性写入8个字节(应该是4个字节吧,原文章这里写错吧),,,是的就是这么蛋疼。不多说啦,直接来一些具体的代码,就很好理解啦。

    **?名??????称:void?PFlash_Write(uint16?ADDR16)??

  • **?功??????能:向PFLASH写入数据???
  • ************************************************************/??
  • void?PFlash_Write(word?ADDR16)???
  • ??byte?i,j;???????//i为Buffer的下标,j为string的下标??
  • for?(i=0,j=0;i<4;i++,j++)??
  • ??{??
  • ????Buffer[i]=0x0000;??
  • ????Buffer[i]=Buffer[i]|(string[j]<<8);??
  • ????j++;??
  • ????Buffer[i]=Buffer[i]|string[j];??
  • ??}??
  • ????
  • while(FSTAT_CCIF==0);?????
  • ????//判断并清除标志位;????
  • ????????FSTAT_ACCERR=1;????
  • ????????FSTAT_FPVIOL=1;????
  • ??????
  • ????FCCOBIX_CCOBIX=0x00;?????
  • ????FCCOB=0x067E;?????????//写入命令和高位地址(06是对P-Flash进行固化的指令)??????????
  • ????FCCOBIX_CCOBIX=0x01;??//地址后16位????
  • ????FCCOB=ADDR16;?????????//写入低16位地址????
  • ????FCCOBIX_CCOBIX=0x02;??//写入第一个数据????
  • ????FCCOB=Buffer[0];????
  • ????FCCOBIX_CCOBIX=0x03;??//写入第二个数据????
  • ????FCCOB=Buffer[1];????
  • ????FCCOBIX_CCOBIX=0x04;??//写入第三个数据????
  • ????FCCOB=Buffer[2];????
  • ????FCCOBIX_CCOBIX=0x05;??//写入第四个数据????
  • ????FCCOB=Buffer[3];???????????
  • ????FSTAT_CCIF=1;?????????//写入执行命令????
  • while(FSTAT_CCIF==0);??//等待执行完毕????
  • 这段程序理解起来应该没有问题吧。

    4、最后一步:

    好啦,看到这里是不是想要自己写一个Flash操作代码尝试一下,然后你单步调制的时候会发现太好啦,终于擦除和写入啦,然后开始全速运行,咦,发现程序跑飞啦,不放弃,接着尝试,然后怎么检查都检查不出错误来,单步运行就是可以,全速运行就是不可以,反复多次,你会感到抓狂,会感到。

    ???????? 好啦好啦,不瞎扯啦,其实这个问题也困扰了我很久,因为我没有系统的学习过单片机,终于,在TI的论坛上,没错,你没有看错,一个飞思卡尔的问题最终在TI论坛上找到了解决方法(吐槽一下,飞思卡尔真是奇葩,资料超少,软件也超贵,相关的论坛也超冷淡。相比来说,TI做的就很好,资料很多,软件免费而且好用,论坛也很火)。

    ???????? 其实原因是,Flash不能对本身就行操作,也就是说在Flash中的代码不能对Flash进行操作,必须转移到RAM中才可以。怎么解决这个问题呢,我自己用的是#pragma关键字,再配合上codewarrior的.prm文件,就可以啦,全速运行下没有问题。

    #pragma? CODE_SEGFLASH_RAM????//.prm文件中将FLASH_RAM定义在RAM区中

    //?flash进行操作的代码

    #pragma CODE_SEG DEFAULF

    关于#pragma和.prm怎么用,又是另一个主题啦,在这里就不赘述。

    ???????? 除此之外还有一个方法,就是利用RELOCATE_TO,至于RELOCATE_TO怎么用,也是另外一个话题,在这里给出一些资料,要用到的同学可以自己去看看。

    ???????? 关于RELOCATE_TO的关键定义:

    ???????? Inthis example,references to functions in CODE_RELOC use addresses from 0x1000to 0x1FFF area,but the code is programmed from 0x8000 to 0x8FFF.

    来自http://blog.sina.com.cn/s/blog_83b3bb460100tesw.html

    ?

    ???????? 至此,flash的操作就讲完啦,其他操作也都是换汤不换药。

    //-------------------------

    擦除操作可以从Flash中启动或是从RAM中启动。当操作是从Flash中启动的时候,Flash控制器控制了操作时序,CPU运行被暂停直到擦除结束。擦除周期结束后,CPU继续执行,从空写入之后的指令开始运行。当从Flash中启动擦除操作时,可以擦除即将运行的程序所在的段,如果擦除了即将运行的程序所在的Flash段时,擦除结束后,CPU的运行不可预料。


    是指执行擦除操作的代码,可以驻留在?Flash?中,也可以驻留在?RAM?中。CPU?执行此操作时,从中取指运行。

    有些芯片是不支持从?Flash?中发起对?Flash?的擦写的,会引起总线冲突。当然,即便是支持从?Flash?发起擦除的芯片,也不能让它擦除掉擦除代码所在的空间,这样会跑飞。这要由编程者来控制。

    单片机如何控制是从flash中启动还是RAM中启动呢?

    从?Flash?启动就是常规的函数调用。RAM?启动的话,要将函数代码复制到?RAM?中,并将其起始地址声明为一个函数指针,调用此指针即可。

    实际上,无论哪一种情况,函数调用都是将当前代码地址压栈,将函数起始地址写入?CPU?取指地址寄存器。只是写入的地址不同而已。函数返回时,会从堆栈中重新弹出调用者的地址。当然,这些会是编译器来做的,你只需编写代码即可。

    FLASH读操作是整块读的,擦除程序如果在FLASH里运行的话,从flash去指令,cpu一下读很多条指令,这样cpu指令寄存器能放下吗?

    单片机内部一般是?NOR?FLASH,不需要整块读。

    “RAM?启动的话,要将函数代码复制到?RAM?中”这是怎么实现的啊?
    基本上有两类方法:

    1?隐式方式?
    利用编译器(需要一些编译信息指令文件的配合)将相关的代码段链接到?RAM?中,同时将代码内容存放在?Flash?中;另外,在代码运行前,用程序指令将代码内容复制到相应的?RAM?空间(例如利用?Startup.c?启动代码来复制它)。
    2?显式方式
    声明一个处于?RAM?地址函数指针,并将?Flash?中的一段代码复制过去。操作时,调用该函数指针。

    (编辑:李大同)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    • 推荐文章
        热点阅读