1. FLASH介绍
msp430的Flash是可字节(8bit)、字(16bit)、长字(32bit)寻址和编程的存储器。Flash储存模块由一个集成的控制器控制编程和擦除操作。该模块包含三个寄存器、一个时序发生器和一个提供编程和擦除电压的电压发生器组成。累积的高电压时间不能过长,在另一个擦出周期之前,32bit的字写入不能超过四次。
Flash有以下特点:
- 集成编程电压发生器
- 字节、字、长字编程
- 超低功耗操作
- 可进行段擦除、扇区擦除和全部擦出
- 边沿0和边沿1读模式
- 当程序不在待擦除的扇区执行时,扇区可以单独擦除
2. Flash段
Flash分为主存、信息部分和BSL存储器部分,可被字节(8bit)、字(16bit)、长字(32byte)、块(128byte)写入,但段是最小的擦除大小。数据段和代码段没有差别,然而段的大小根据不同区段有差异:
- 主存和BSL段是512bytes
- 信息储存段是128bytes
信息共有四段,从A到D,每一个段可单独擦除。BSL有四段,从A到D,每一个段可单独擦除。主存被分割成64KB的扇区,每个扇区又被分成512B的段。
2.1 Segment A
信息储存器的A段被LOCKA锁定,当LOCKA=1,段A不能读写,所有信息被保护;当LOCKA=0时,段A和其他段一样可被读写。
注意:当向LOCKA位写入1时,LOCKA位的状态将被切换。向LOCKA写入0不起作用。
FCTL4 = FWKEY;//清除LOCKINFO位
if(FCTL3 & (0X96+LOCKA))//如果LOCKA锁定
{
FCTL3 = FWKEY + LOCKA;//解锁
}
//A段已解锁
if(~(FCTL3 & (0X96+LOCKA)))//如果LOCKA没有锁定
{
FCTL3 = FWKEY + LOCKA;//锁定
}
//A段已锁定
3. Flash 操作
Flash默认读模式,就像ROM一样不能写入和擦除,Flash时序发生器和电压生成器关闭。当一个扇区被擦除时,Flash允许在另外的扇区中执行程序,也可以从没有在执行擦除的扇区读数据。
闪存操作 | 程序在Flash中执行 | 程序在RAM中执行 |
---|---|---|
扇区擦除 | 支持 执行的程序必须不在被擦除的段中 | 支持 |
段擦除 | 不支持 | 支持 |
写入 | 不支持 | 支持 |
Flash可以在不需要外部电压的情况下实现系统中编程。CPU能够对Flash进行编程。Flash通过BLKWRT,WRT, MERAS,ERASE位来选择写入/擦除模式:
- 字节、字、长字写入
- 块写入
- 段擦除
- 扇区擦除(针对主储存扇区)
- 主存擦除(所以主储存扇区)
- 当擦除扇区读(除了从当前的扇区读)
当Flash的扇区正在编程或擦除时,从该扇区读或者写是禁止的。对Flash的擦除或者编程开始于Flash或者RAM。
3.1 Flash的擦除
Flash擦除后的逻辑值为1,每一位都可以单独从1编程到0,但要从0编程到1需要擦除操作。通过配置ERASE和MERAS位选择三种擦除模式:
ERASE | MERAS | 擦除模式 |
---|---|---|
0 | 0 | 没有擦除操作 |
0 | 1 | 段擦除 |
1 | 0 | 由空写地址选择的扇区擦除(一个扇区) |
1 | 1 | 主存擦除(使用的储存扇区,不包括信息储存区A-D和BSL扇区的A-D) |
擦除周期
擦除周期开始于对要擦除段的地址范围的空写,也就是写0。BUSY位在空写之后立即置1,并在整个擦除周期中保持置1。BUSY、MERAS和ERASE在周期结束时自动清除。当清除控制位时,不应该进行额外的空写访问,否则,ACCVIFG将置1。全部擦除周期定时不依赖于设备上闪存的数量。所有设备的擦除周期是相等的。
擦除主内存
主存储器由一个或多个扇区组成。每个扇区可以单独被擦除(扇区擦除)。所有的主存都可以在大规模擦除模式下被擦除。
擦除信息段或BSL闪存段
信息存储区A到D段和BSLA到D段只能在段擦除模式下进行擦除。它们不会在扇区清除或大规模清除过程中被清除。擦除首先清除LOCKINFO位。
从Flash中擦除
擦除周期可以从闪存中启动。在扇区擦除过程中,代码可以从闪存或RAM中执行,但执行的代码不能放在要擦除的扇区中。
擦除程序流程图:
C语言示例:
void erase_from_flash(unsigned int *atr)
{
unsigned int *ptr = (unsigned int *)atr;
_DINT(); //写入过程不允许中断,否则结果无法预料
while(FCTL3&BUSY); //忙碌等待
FCTL3 = FWKEY; //清除LOCK
FCTL1 = FWKEY + ERASE; //单段擦除
*ptr = 0; //空写擦除
while(FCTL3 & BUSY); //忙碌等待
FCTL1 = FWKEY; //清除ERASE
FCTL3 = FWKEY + LOCK; //置位LOCK,保护数据
_EINI();
}
从RAM中Erase
擦除周期可以从RAM开始。在这种情况下,CPU没有被占用,可以继续执行来自RAM的代码。BUSY位用来确定擦除周期是否结束。当Flash繁忙时,启动一个擦除周期或一个编程周期会导致访问冲突,ACCIFG被设置为1,擦除操作的结果是不可预测的。
3.2 FLASH写入
BLKWRT | WRT | 写入模式 |
---|---|---|
0 | 1 | Byte or word写入 |
1 | 0 | Long-word写入 |
1 | 1 | Long-word block写入 |
写模式使用一系列单独的写指令。使用长字写模式的速度大约是字节或字模式的两倍,使用长字块写模式大约比字节或字模式快四倍,因为在完整的字块写时电压发生器保持开着,而且长字是并行写的。在字节或字写模式、长字写模式或块长字写模式下,任何修改目地址的指令都可以用在flash。
BUSY位在写操作处于活动状态时置1,并在操作完成时清除。如果写操作是从RAM开始的,CPU不能在BUSY置1时访问flash。否则,会发生访问冲突,ACCVIFG将会置1,导致flash写入不可预知。
字节或字写入
字节或字的写操作可以从Flash或RAM中开始。当从Flash中初始化时,直到写入完成CPU被挂起。写操作完成后,CPU继续执行写访问之后的指令。字节、字和长字的写定时如图所示。字节、字和长字的写时序相同。
当从RAM执行一个字节或字的写操作时,CPU继续从RAM执行代码。在CPU再次访问flash之前,BUSY位必须为零,否则会发生访问冲突,ACCVIFG置1,写结果不可预知。
在任何写模式下,内部产生的编程电压应用于完整的128字节块。对于任何块,都不能超过累积编程时间(tCPT)。每一个字节、字或长字的写都累积程序时间。如果达到或超过程序的最大累积时间,段数据将返回不可预测的结果,需要在进一步使用之前擦除。具体规格请参见设备专用数据表。
从Flash字节或字写入
//Byte or word write from flash.
//假定 0x0FF1E 已被擦除
//假定 ACCVIE = NMIIE = OFIE = 0.
void byte_write_flash(unsigned int adr,unsigned char date)
{
unsigned int *ptr = (unsigned int *)atr;
WDTCTL = WDTPW + WDTHOLD;//关闭看门狗
FCTL3 = FWKEY; //清除LOCK
FCTL1 = FWKEY + WRT; //字/字节写入
*ptr = date; //写入数据
FCTL1 = FWKEY; //清除写入模式
FCTL3 = FWKEY + LOCK; //上锁
}
从RAM字节或字写入
//Byte or word write from RAM.
//假定 0x0FF1E is already erased
//假定 ACCVIE = NMIIE = OFIE = 0.
void byte_write_ram(unsigned int adr,unsigned char date)
{
unsigned int *ptr = (unsigned int *)atr;
WDTCTL = WDTPW + WDTHOLD;//关闭看门狗
while(FCTL3&BUSY); //忙碌等待
FCTL3 = FWKEY; //清除LOCK
FCTL1 = FWKEY + WRT; //字/字节写入
*ptr = date; //写入数据
while(FCTL3&BUSY); //忙碌等待
FCTL1 = FWKEY; //清除写入模式
FCTL3 = FWKEY + LOCK; //上锁
}
Long-Word写入
长字写操作可以从Flash或RAM中开始。32位写入flash控制器后,并且开始编程周期,BUSY位被置1。当从闪存中初始化时,直到写完成时CPU被挂起。写操作完成后,CPU继续执行写访问之后的指令。长字由四个连续的字节组成,与32位的地址对齐(只有较低的两个地址位不同)。字节可以以任何顺序或字节和字的任何组合写入。如果一个字节或字被多次写入,那么最后一次写入的数据将被存储到flash存储器中。
如果在所有四个字节都可用之前,对32位地址之外的闪存地址进行写操作,则到目前为止所写的数据将被丢弃,最新写的字节或字将定义新的32位对齐的地址。
当32位可用时,执行写周期。当从RAM执行时,CPU继续执行代码。CPU再次访问flash之前,BUSY位必须为零,否则会发生访问冲突,ACCVIFG置1,写结果不可预知。
在长字写模式中,内部生成的编程电压应用于一个完整的128字节块。对于任何块,都不能超过累积编程时间(tCPT)。每次写入都增加一个段的累积程序时间。如果达到或超过了程序的最大累积时间,则必须删除该段。进一步的编程或使用数据将返回不可预测的结果。
从Flash长字写入
//Long-word write from flash.
//假定 0x0FF1C and 0x0FF1E已被擦除
//假定 ACCVIE = NMIIE = OFIE = 0.
void long_write_flash(unsigned int adr,long date)
{
unsigned int *ptr = (unsigned int *)atr;
WDTCTL = WDTPW + WDTHOLD;//关闭看门狗
FCTL3 = FWKEY; //清除LOCK
FCTL1 = FWKEY + BLKWRT; //长字写入
*ptr = date; //写入数据
FCTL1 = FWKEY; //清除写入模式
FCTL3 = FWKEY + LOCK; //上锁
}
从RAM长字写入
//Two 16-bit word writes from RAM.
//假定 0x0FF1C and 0x0FF1E已被擦除
//假定 ACCVIE = NMIIE = OFIE = 0.
void byte_write_ram(unsigned int adr,long date)
{
unsigned int *ptr = (unsigned int *)atr;
WDTCTL = WDTPW + WDTHOLD;//关闭看门狗
while(FCTL3&BUSY); //忙碌等待
FCTL3 = FWKEY; //清除LOCK
FCTL1 = FWKEY + BLKWRT; //长字写入
*ptr = date; //写入数据
while(FCTL3&BUSY); //忙碌等待
FCTL1 = FWKEY; //清除写入模式
FCTL3 = FWKEY + LOCK; //上锁
}
块写入
当需要编写许多顺序字节或字时,可以使用块写来加速闪存写进程。闪存编程电压保持在写128字节块的持续时间。在块写入期间,不能超过任何块的累积编程时间(tCPT)。使用块写模式,只能写长字。
块写入不能从闪存中开始,必须从RAM开始。在块写的整个过程中,BUSY位始终保持置1。在向块写入四个字节或两个字之间必须检查WAIT位。设置WAIT后,就可以写入该块的四个字节或两个16位字。当写连续块时,必须在当前块完成后清除BLKWRT位。在tEND给出的闪存恢复时间之后,可以设置BLKWRT来启动下一个块写。BUSY在每个块写合并后被清除。
//Write one block starting at 0F000h.
//必须从RAM启动, 假定Flash已被擦除
//假定 ACCVIE = NMIIE = OFIE = 0.
void block_write_ram(unsigned int adr,long *date,unsigned n)
{
unsined i = 0;
unsigned int *ptr = (unsigned int *)atr;
WDTCTL = WDTPW + WDTHOLD;//关闭看门狗
while(FCTL3&BUSY); //忙碌等待
FCTL3 = FWKEY; //清除LOCK
FCTL1 = FWKEY + BLKWRT + WRT; //块写入
for(i=0;i<=n;i++)
{
while(FCTL3&WAIR); //等待写入完成
*ptr = date[i]; //写入数据
}
while(FCTL3&BUSY); //忙碌等待
FCTL1 = FWKEY; //清除写入模式
FCTL3 = FWKEY + LOCK; //上锁
}