历史上的今天
返回首页

历史上的今天

今天是:2025年07月15日(星期二)

正在发生

2021年07月15日 | 飞思卡尔MC9S12G系列单片机flash擦写

2021-07-15 来源:eefocus

最近在学习飞思卡尔MC9S12G系列单片机底层驱动,看了很多,目前理解还不深,处于入门菜鸟级别,仅在此记录下学习心得,后续会在实操中强化底层驱动开发能力,不能老是处于光说不练,搞些假把式。好了,言归正传。本文主要记录笔者基于MC9S12G系列单片机flash擦写,还望各路大神路过时,可指点一番。


Flash本身是非易失性存储,可以通过编程的方式擦写其中的内容,掉电后其内容不会丢失,一般是单片机的程序存储位置。单片机运行时先将Flash中下一条运行的程序读出,然后执行其内容,再读出下一条指令,再执行循环往复。通过擦写flash能够实现单片机应用程序自动更新。


G128系列单片机,查阅芯片手册,获得数据如下:

• 128Kbytes of P-Flash (Program Flash) memory

• 4 Kbytes of EEPROM memory

在这里插入图片描述

可以看出,G128系列单片机的Flash存储大小有128KB, 其全局地址范围为: 0x2_0000 - 0x3_FFFF。在这里可通过以下链接,熟悉并理解MC9S12G128内存映射。

MC9S12G128内存映射(本地地址,逻辑地址,全局地址)


第一张图:Flash模块功能框图

G128 Block Diagram

上图直接告诉我们,对flash操作其实不是直接对每个Flash存储区进行操作,而是通过控制Flash Interface进行的。具体就是操作Flash Interface的寄存器,再进而操作Flash存储区。那么寄存器有哪些?具体怎么操作?看第二张图。


第二张图:寄存器描述

在这里插入图片描述
在这里插入图片描述

Flash模块有20个控制和状态寄存器。上图是从芯片手册截取的一部分。但对于Flash模块而言关键寄存器就是图中圈住的几个。下面依次来介绍一下:


FCLKDIV(Flash时钟分配寄存器)

先上图(截取自芯片手册)

在这里插入图片描述
在这里插入图片描述

上图告诉我们:有三个寄存器需要处理(FDIVLD,FDIVLCK,FDIV):

FDIV寄存器:有六位,通过分频将总线时钟频率分频至1MHz以下从而使Flash控制器可以正常工作。FDIV的值与总线时钟频率有关,根据总线频率的大小确定FDIV的值(见上表),假如当前我的总线频率为16MHz,查表中16MHz在15.6与16.6之间,那么FDIV的值就是0x0F了。当确定好Flash控制器的频率后,需要将其写保护,以防误操作修改了分频寄存器,那么对FDIVLCK写1,当FDIVLCK写入1后,除非重启,否则FDIV的值不能被修改,重启后FDIVLCK的值将重新归零。


由此总结我们Flash控制器的时钟设置步骤如下:


根据总线频率设置分频FDIV

对分频进行保护,将FDIVLCK置为1

FCCOBIX(Flash CCOB索引寄存器)

先上图(截取自芯片手册)

在这里插入图片描述

上图告诉我们,这个寄存器是FCCOB的索引寄存器,CCOBIX[2:0] 只有三位,即8种状态。具体如何索引,看下图:

在这里插入图片描述
在这里插入图片描述

The CCOBIX bits are used to select which word of the FCCOB register

array is being read or written to.

FCCOBIX的两位决定当前读取或者写入FCCOB的是哪一个word(一般而言一个word在单片机中指两个字节),也就是说FCCOB是一个多word的寄存器,通过FCCOBIX来确定当前操作或者读取的是哪一位。具体如何操作,我们要来看看FCCOB寄存器。FCCOBIN和FCCOB这两个寄存器得结合起来看。


FCCOB(Flash指令寄存器)

在这里插入图片描述

上图告诉我们:FCCOB是一个六个word长度的数组,通过FCCOBIX寄存器来定位当前操作的是哪个word。一个word是16位的,前[7:0]位Low,后[15:8]为High。

由于FCCOB只有6个word长,所以FCCOBIX范围为0x00-0x05,也就是说当FCCOBIX = 0x00时,读写FCCOB寄存器操作第0个word的内容,当FCCOBIX = 0x01时,读写FCCOB寄存器操作第1个word的内容,以此类推。也就是说我们操作FCCOB实际内容是由FCCOBIX这个索引寄存器来控制的


FSTAT(Flash状态寄存器)

先上图(截取自芯片手册)

在这里插入图片描述
在这里插入图片描述

上图告诉我们:

CCIF:用来显示一个Flash指令是否已经执行完成

若CCIF = 0 则表示Flash控制器正在执行指令

若CCIF = 1则表示Flash控制器当前空闲,前一条指令已经执行完毕。

除此以外,CCIF的重要作用就是让Flash控制器开始执行指令,由于Flash控制器的指令内容需要在开始前先行准备到Flash指令寄存器中,当指令准备完成后,通过将CCIF位置1就可以使Flash控制器执行当前写入在指令寄存器中的指令。需要注意的是,当Flash需要执行下一条指令前,务必要检查前一条指令是否已经执行完毕,即CCIF是否为1,若CCIF = 1则再写入1方可执行指令寄存器中的内容。


第三张图:Flash擦写操作流

先上图(截取自芯片手册)

在这里插入图片描述

上图告诉我们:

1.FCLKDIV时钟分频寄存器初始化,确定分频因子

2.检查命令缓冲区(FSTAT_CCIF)是否为空

3.对读写错误(FSTAT_ACCER)标志位清零(写1清零)

4.对保护区编程错误(FSTAT_PVIOL)标志位清零(写1清零)

5.对索引寄存器(FCCOBIX)赋值0x00,写入FCCOB编程命令0x06

6.对索引寄存器(FCCOBIX)赋值0x01,写入FCCOB全局地址(写入的首地址)

7.将编程的数据赋值给要编程的Flash地址

8.检查是否还有新的数据需要写入Flash,如有转到第5步

9.等待,直到命令完成标志位(FSTAT_CCIF)置位


Flashde的擦除操作流类似,即将第5步改为如下,去掉第7、8步:

5.对索引寄存器(FCCOBIX)赋值0x00,写入FCCOB擦除命令0x0A


Flash的擦写注意事项

由于在Flash的擦除和写入中,Flash是不能读的,故擦除和写入Flash的程序要放到RAM中去,即:在Flash擦除或写入之前,要把擦除和写入的可执行代码复制到RAM中去,并让程序在RAM中执行。如果没有注意此事项,程序会跑飞。如何解决这个问题,查找资料获知如下:

使用#pragma关键字,配合codewarrior的.prm文件


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

//对flash进行操作的代码

#pragma CODE_SEG DEFAULT


关于codewarrior的.prm文件的结构可参考如下博主的文章,描述的很清晰:


飞思卡尔MC9S12(X)系列的内存资源分配和.prm文件的结构


编码实现

prm文件设置

NAMES END


SEGMENTS/* Register space  *//*    IO_SEG        = PAGED         0x0000 TO   0x03FF; intentionally not defined */

/* RAM */      

      RAM           = READ_WRITE    0x2000 TO   0x3BFF;      CODE_RAM      = READ_WRITE    0x3C00 TO   0x3FFF; /*1 kB for flash read and write*/

/* D-Flash */      

      DFLASH        = READ_ONLY   0x000400 TO 0x0013FF;

/* non-paged FLASHs */      

      ROM_1400      = READ_ONLY     0x1400 TO   0x1FFF;

      ROM_BOOT      = READ_ONLY     0x4000 TO   0x43FF;           

      ROM_FLASH     = READ_ONLY     0X4400 TO   0x47FF RELOCATE_TO 0x3C00;  // 1KB for necessary flash operation

      ROM_C000      = READ_ONLY     0xC000 TO   0xFEFF; 

 /*   VECTORS       = READ_ONLY     0xFF00 TO   0xFFFF; intentionally not defined: used for VECTOR commands below */   

   //OSVECTORS      = READ_ONLY     0xFF80 TO   0xFFFF;   /* OSEK interrupt vectors (use your vector.o) */

/* paged FLASH:                     0x8000 TO   0xBFFF; addressed through PPAGE */

      PAGE_08       = READ_ONLY   0x088000 TO 0x08BFFF;      

      PAGE_09       = READ_ONLY   0x098000 TO 0x09BFFF;      

      PAGE_0A       = READ_ONLY   0x0A8000 TO 0x0ABFFF;      

      PAGE_0B       = READ_ONLY   0x0B8000 TO 0x0BBFFF;      

      PAGE_0C       = READ_ONLY   0x0C8000 TO 0x0C93FF;      

      PAGE_0C_A000  = READ_ONLY   0x0CA000 TO 0x0CBFFF;      

      PAGE_0E       = READ_ONLY   0x0E8000 TO 0x0EBFFF;

/*    PAGE_0D       = READ_ONLY   0x0D8000 TO 0x0DBFFF; not used: equivalent to ROM_4000 */

/*    PAGE_0F       = READ_ONLY   0x0F8000 TO 0x0FBEFF; not used: equivalent to ROM_C000 */

END


PLACEMENT /* here all predefined and user segments are placed into the SEGMENTS defined above. */      

      _PRESTART,              /* Used in HIWARE format: jump to _Startup at the code start */      

      STARTUP,                /* startup data structures */      

      ROM_VAR,                /* constant variables */      

      STRINGS,                /* string literals */      

      VIRTUAL_TABLE_SEGMENT,  /* C++ virtual table segment */    

      //.ostext,                /* OSEK */


      NON_BANKED,             /* runtime routines which must not be banked */

      COPY                    /* copy down information: how to initialize variables */                                      

                              /* in case you want to use ROM_4000 here as well, make sure     

                              that all files (incl. library files) are compiled with the                                 

                              option: -OnB=b */                               

                        INTO  ROM_C000/*, ROM_1400, ROM_4000*/;

      

      BOOTLOADER        INTO  ROM_BOOT;      

      FLASH_CODE        INTO  ROM_FLASH;


      USER_APP          INTO  PAGE_08;      

      TEST_AREA         INTO  PAGE_09;  /* physical address from 0x2_4000 */

      DEFAULT_ROM             INTO  PAGE_0A, PAGE_0B, PAGE_0C, PAGE_0C_A000, PAGE_0E;                

      //.stackstart,            /* eventually used for OSEK kernel awareness: Main Stack Start */      

      SSTACK,                 /* allocate stack first to avoid overwriting variables on overflow */    

      //.stackend,              /* eventually used for OSEK kernel awareness: Main-Stack End */    

     

     DEFAULT_RAM         INTO  RAM;

      

     //.vectors            INTO  OSVECTORS; /* OSEK */

END


ENTRIES /* keep the following unreferenced variables */   

 /* OSEK: always allocate the vector table and all dependent objects */  

 //_vectab OsBuildNumber _OsOrtiStackStart _OsOrtiStart

END


STACKSIZE 0x100


VECTOR 0 _Startup /* reset vector: this is the default entry point for a C/C++ application. */

//VECTOR 0 Entry  /* reset vector: this is the default entry point for an Assembly application. */

//INIT Entry      /* for assembly applications: that this is as well the initialization entry point */


VECTOR ADDRESS 0xFFD6 SCI0_INT_receive

推荐阅读

史海拾趣

Anvo-Systems公司的发展小趣事

Anvo-Systems公司自成立以来,一直致力于电子技术的研发与创新。某年,公司成功研发出一款具有高效能源利用率的智能家居控制系统,该产品迅速在市场上获得了广泛关注。通过不断优化产品性能和用户体验,Anvo-Systems逐步扩大了市场份额,并与多家知名家电品牌建立了合作关系。这一技术突破不仅提升了公司的知名度,也为公司的持续发展奠定了坚实基础。

Cristek Interconnects Inc公司的发展小趣事

Cristek Interconnects Inc公司自成立以来,一直致力于电子连接器技术的研发与创新。在早期的发展阶段,公司凭借其出色的研发团队,成功开发出一种新型的高性能连接器,这种连接器具有更高的导电性和更低的信号衰减,极大地提高了电子设备的性能。这一技术突破为公司赢得了市场的广泛认可,也奠定了Cristek在电子行业中的重要地位。

CANDD公司的发展小趣事

随着全球环保意识的提高,CANDD公司积极响应号召,开始践行绿色环保理念。公司投入大量资金研发环保型电子产品,并采用了更加环保的生产工艺和材料。同时,公司还加强了废弃电子产品的回收和处理工作,努力减少对环境的污染。这些举措不仅提升了公司的社会形象,还为公司赢得了更多消费者的支持。

Herrmann Kg公司的发展小趣事

Herrmann Kg凭借其卓越的产品质量和专业的服务,赢得了全球众多领先客户的信赖。例如,CUSTOMCELLS®作为一家全球领先的客户专用锂电池开发商,在生产过程中采用了Herrmann Kg的超声波焊接系统,有效降低了废品率,提高了生产效率。这种深度合作不仅展示了Herrmann Kg在电池制造领域的专业能力,也进一步巩固了其在全球市场中的地位。通过不断满足客户的特定需求,Herrmann Kg在电子及其他相关行业中树立了良好的口碑。

台湾肯尼威(CANNYWELL)公司的发展小趣事

台湾肯尼威公司成立于1986年,其创始人李明(化名)是一位对电子技术充满热情的工程师。在当时的台湾,电子制造业正处于蓬勃发展的阶段。李明看到电源滤波器在电子设备中的重要性,于是决定专注于这一领域的研发和生产。他带领一支小团队,经过无数次的试验和改进,终于成功研发出CW系列单相电源滤波器,为公司的发展奠定了坚实的技术基础。

EnerSys公司的发展小趣事

EnerSys一直致力于技术创新和研发投入。公司拥有一支专业的研发团队,不断推出具有竞争力的新产品和解决方案。例如,EnerSys在锂电池领域取得了重要突破,成功开发出高能量密度、长寿命的锂电池产品。这些创新产品不仅满足了客户的多样化需求,还推动了公司在电子行业中的持续发展。

问答坊 | AI 解惑

为什么卖IC比不如卖杂货?

2000年,沃尔玛全年销售1650亿美元,全球IC产业销售2050亿美元,比沃尔玛高出20%以上!2008年,沃尔玛全年销售3745亿美元,全球IC销售2500亿美元,比沃尔玛低近50%!更惨的是,预计09年沃尔玛销售将增长1~3%,而全球IC销售则将下降16.3%!跌倒21 ...…

查看全部问答>

SPARC V8结构嵌入式微处理器开发环境的设计实现之二

2、SPARC V8结构嵌入式微处理器的软件集成开发环境设计 2.1 软件集成开发环境总体设计 一个好的设计应该是在参考已有优秀系统的前提下,根据自身的特点和需求来定制的,这种设计思路在设计中已经被广泛使用。在设计软件集成开发环境时也是采用了 ...…

查看全部问答>

周立功写给学单片机的年轻人 -----有必要看看

作为过来人思前想后,我感到完全有责任将发自心底的感受传递给年轻一代,“一个企业家心灵深处渴望优秀人才的卓越追求和深层次的叹息、痛苦和感受”。您们千万不要等到毕业求职时才觉得自己能力太差,世界上从来就没有后悔药。当然,如果您现在看了 ...…

查看全部问答>

GPRS MODEM 电路原理图.

GPRS MODEM 电路原理图.…

查看全部问答>

linux 嵌入式技术爱好者交流群

linux 嵌入式技术爱好者交流群68158867 欢迎各位爱好者能加入!!!!…

查看全部问答>

testbench 如何编写

1.激励的设置 相应于被测试模块的输入激励设置为reg型,输出相应设置为wire类型,双向端口inout在测试中需要进行处理。 方法1:为双向端口设置中间变量inout_reg作为该inout的输出寄存,inout口在testbench中要定义为wire型变量,然后用输出使 ...…

查看全部问答>

招聘单片机嵌入式开发工程师

招聘要求: 1:2年以上嵌人式系统软件开发相关工作经验。 2:熟悉计算机组成原理及操作系统,有较强的嵌人式系统软件,硬件设计技能.。 3:至少熟悉一种业界常用的嵌人式微处理器(ARM优先考虑),熟悉各种通用接口(如Ethernet,USB,SPI,串口, 485 ...…

查看全部问答>

ip

DHCP AUTOIP 静态IP 在使用DHCP获取失败后,系统会用AUTOIP来配置IP。我想让他DHCP失败后就用STATIC 静态IP问题1:我想在DHCP失败后就用STATIC 静态IP,该怎么做? 问题2:AUTOIP不是很明白,能帮我解释一下吗? 这些问题困扰了我很久, &nbs ...…

查看全部问答>

ZJM12864BSBD

在电路图上,看到好多的有关ZJM12864BSBD的液晶,在网上查了一下,找不到这个型号啊,也没有卖的,到底怎么回事啊?我设计电路的时候想用这个型号,怎么找不到这个型号的相关资料,也没有卖的?…

查看全部问答>