AVR在IAR中将常量仅定义在FLASH上的细节

来源:互联网 发布:资海网络集团简介 编辑:程序博客网 时间:2024/04/29 06:03
1)看一个普通的定义

不用说,这种情况是肯定是test是在RAM中了

char test[] = "Put String In Flash";
如果是ARM平台的话,加个const就放到FLASH中了
const char test[] = "Put String In Flash";
但是在AVR中是行不通的,你会发现定义了这一行之后FLASH和RAM同时增加了(如果没有被编译器丢弃的话,IAR下加__root扩展字可以让编译器强制保留,方便测试)

const仅是做语法的修饰,不会影响变量最终放到什么位置,要控制变量存放的位置,在IAR下需要用到存储器属性扩展字__flash 或者__farflash修饰,下面的介绍以__flash为例,后面会讲到与__farflash的区别。为了简化讨论,所有的语句都没有再加const。


2) __flash位置的影响

char __flash test[] = "Put String In Flash";
__flash char test[] = "Put String In Flash";

这者效果是一样的,都只会占用FLASH,建议使用前一种方法,后面会涉及到指针的写法匹配的问题。

一旦这样修饰之后,如果想去修改这个常量,编译器会报错,所以基本不需要const了。

test[0] = 'A';   // ERROR! 

3) 如果将这个常量字符串传递到一个函数中,接受形参的写法

void fun_to_test_parameter(char __flash *p);
注意,不能将__flash扩展字写在char的前面,const这样写是没有区别的,但是这里真的不行,所以为什么上面定义变量时建议将__flash扩展字写在char后就是这样原因。

void fun_to_test_parameter(__flash char *p)        // ERROR!
上面等效于 char * __flash p,由于p是在栈上,不能强制改变其位置,编译器会报错


4) 这样解决了一些问题,但是当直接传递字符串立即数到函数时,编译器还是会把字符串本身拷贝到RAM中,这时候就会出现参数不兼容的错误,如下:

void fun_to_test_parameter(char __flash *p); fun_to_test_pararmeter("Swordman");    // ERROR!

这里有两种方法解决,

A,在函数上面定义一个static的常量,再传递进去

static char __flash string[] = "Swordman";fun_to_test_parameter(string);
B,在编译器里增加一个全局的东东,C/C++ Compiler -> Extra Options中勾选Use command line options,增加 --string_literals_in_flash一行

这样一来所有的字符串立即数都会被自动的放到__flash或者__farflash中(根据所选芯片FLASH大小决定),这时候接受的函数形参的类型需要与之匹配。


6) 对于库函数,如果向普通的如printf, strlen, memcpy这些函数传递FLASH指针会造成类型不兼容的错误,必须增加头文件<pgmspace.h>,使用*_P或者*_G的扩展版本才可以,如printf_P, memcpy_P,_G为通用版本可以接受SRAM和FLASH的指针,_P仅能接受FLASH指针


7) 使用__flash定义的指针变量占用2个字节,而使用__farflash的指针变量占3个字节,只有选择64K FLASH以上的AVR型号才可以使用__farflash,并且如果使用--string_lieterals_in_flash扩展时,在选择64K FLASH以上的AVR型号时,编译器会将字符串常量定义在__farflash,只有这种指向这种类型的指针才可以访问。


总结:

AVR是哈佛结构不要紧,最重要的是他的FLASH空间和SRAM空间是重叠的,所以在汇编级别的语句上访问的指令是不一样的,因此必须要通过特定的关键字来指定变量存放的位置,否则机器只会默认的将常量拷贝到内部RAM中,然后统一使用访问SRAM的汇编。