飞思卡尔MC9S12G系列单片机flash擦写

本文介绍了飞思卡尔MC9S12G系列单片机的Flash模块,包括Flash功能框图、关键寄存器如FCLKDIV、FCCOBIX、FCCOB和FSTAT的详细解释,以及Flash擦写操作流程。在实际操作中,需要注意Flash控制器的时钟设置,确保指令执行完毕后再进行新的操作。此外,由于Flash在擦写期间不能读取,因此擦写代码必须放在RAM中执行。文章还提供了prm文件的配置示例,用于将代码从Flash复制到RAM中执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


最近在学习飞思卡尔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控制器的时钟设置步骤如下:

  1. 根据总线频率设置分频FDIV
  2. 对分频进行保护,将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

Flash擦写头文件

在这里插入代码片

Flash擦写源文件

在这里插入代码片

ROM中程序复制到RAM中函数

在这里插入代码片
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值