从本科时代开始学习单片机,研究生两年半也一直在做有关单片机的事情,工作两年继续进行单片机开发,开始是ATmega8/16/128,后来以STM32F103为主,最近在学习MSP430。虽然8位/16位/32位的单片机都用过了,但一直是专注于外设的使用,关于FLASH、SRAM、EEPROM等存储和地址特性了解很少。现在学到了MSP430的FLASH存储控制器,正好趁这个机会搞搞清楚,也是因为MSP430的仿真器FET430UIF和它在Linux下的应用程序MSPDEBUG,实在是很好用,读写CPU寄存器和地址空间的命令都相当简单。
MSP430F2616有92KB+256B的Flash存储空间,以及4KB的SRAM;另外,ATmega8有8K的Flash、1K的SRAM和512字节的EEPROM;而STM32F103RBT6有128K的Flash,以及20K的SRAM。以MSP430F2616为例,执行烧写命令:
(mspdebug) prog maria.elf
Erasing...
Programming...
Writing 2770 bytes at 2100 [section: .text]...
Writing 42 bytes at 2bd2 [section: .rodata]...
Writing 12 bytes at 2bfc [section: .data]...
Writing 64 bytes at ffc0 [section: .vectors]...
Done, 2888 bytes total
MSP430F2616的存储空间地址分成三个部分:0~0x1FF是寄存器地址映射,包括特殊功能寄存器和外设寄存器;0x1100~0x20FF是SRAM地址;0x18FFF~0x02100是Flash地址,在此中的0x0FFFF~0x0FFC0存储了中断向量表。
现在,就可以将Flash存储空间和上面的prog信息一一对应起来,第一步,将2770个字节的代码区烧写到0x2100地址,第二步,将使用const修饰的全局变量的初始值烧写到0x2bd2地址,第三步,将初始化过的非零全局变量的初始值烧写到0x2bfc地址,第四步,将中断服务程序的函数地址烧写到中断向量表的对应位置。
一般来说,烧写到Flash中的值不能在程序运行中由程序更改,所以虽然全局变量的初始值存放在Flash中,它们的实际地址都在SRAM里面,局部变量存放在0x020FF~0x01900的Extended区,而全局变量存放在0x018FF~0x01100的Mirrored区。程序运行的时候,从Flash中的0x2100开始执行指令,根据指令跳转在新的指令去执行,或者操作局部变量和全局变量的值。
ATmega8的存储器空间比MSP430F2616稍微简单一些,STM32F103RBT6的存储空间比MSP430F2616稍微复杂一些,但是基本原理都是差不多的。
===================================
很多场合,用户需要一个很实用的功能,就是存储数据并掉电保存,比如,设置RS485的地址数据,又或者存储某个功能的标定参数……ATmega8可以使用内置的512字节EEPROM来实现,而MSP430F2616和STM32F103RBT6是使用内部的空闲FLASH存储区。MSP430F2616的Flash Memory Controler就是做这一用途,而STM32F103RBT6的Flash Control虽然没有出现在用户手册中,但官方网站提供了EEPROM emulation的技术支持手册。
EEPROM和Flash有很多不一样的地方,首先前者可按字节读写,后者也可按字节读写,但需要先按页擦除;另外,前者的速度和目前可实现的容量都逊于后者。
MSP430F2616的Flash Memory Controller使用起来非常方便,只是还是要注意两点,第一是Flash Control的时钟必须在257KHz~476KHz之间,所以使用不同的时钟源ACLK/SMCLK/MCLK的话,得设置不同的分频值;第二是不要两次写入同一个地址,因为Flash只能由1写入0,所以未擦除就再此写入可能会得到错误的值。
/**********************************************/
void wait_xt2clock(void)
{
BCSCTL1 &= ~XT2OFF;
BCSCTL3 |= XT2S_2;
do {
IFG1 &= ~OFIFG;
Delay100Us_DcoDef(1);
} while (IFG1 & OFIFG);
BCSCTL2 |= SELM_2;
}
int main (void)
{
uint16_t i = 0;
char *flash = 0x2ff0;
WDTCTL = WDTPW + WDTHOLD;
// 需要使用MCLK作为Flash Memory Controler的时钟,所以使能外部的8MHz晶振
// 当然也可以使用其他的时钟
wait_xt2clock();
// FCTL2写入识别KEY,选择MCLK作为时钟,9是对应的分频值
// FCTL3写入识别KEY,同时unlock
// FCTL1写入识别KEY,同时设置为Erase individual segment only
// 首先要写入一个无意义的值,告诉Flash Memory Controler要开始了
FCTL2 = FWKEY + FSSEL1 + 9;
FCTL3 = FWKEY;
FCTL1 = FWKEY + ERASE;
*flash = 0;
// FCTL1写入识别KEY,同时启动写入模式
FCTL1 = FWKEY + WRT;
for (i = 0; i < 0xff; i++)
*flash++ = i;
// 关掉写入模式,同时再次锁定
FCTL1 = FWKEY;
FCTL3 = FWKEY + LOCK;
while (1);
return 0;
}
/**********************************************/
飞言走笔 2015-1-23 08:57
用户377235 2013-10-27 11:17
赤心木 2013-6-24 21:46
用户377235 2013-6-23 12:50
用户403664 2013-6-6 11:17