GCC AVR(Atmel Studio+ AVR Studio)如何在程序存储器(flash)空间存放字符串常量和使用字符串常量

目录

一、program memory和data memory

二、如何将字符串变量定义到program memory(flash)

三、如何读取字符串变量

四、PSTR()

五、使用strcpy()函数将字符串常量拷贝字符串缓冲区会占用大量的data memory区空间;使用strcpy_P()函数将字符串常量拷贝到字符串缓冲区仅仅占用program memory区(flash)空间,不会占用data memory区空间。

 六、使用sprintf()函数将字符串常量打印到字符串缓冲区会占用大量的data memory区空间;使用sprintf_P()函数将字符串常量打印到字符串缓冲区仅仅占用program memory区(flash)空间,不会占用data memory区空间。

七、总结


一、program memory和data memory

        GCC AVR(Atmel Studio+ AVR Studio)默认将变量(包含普通变量、数组和字符串)定义在data memory,这个需要特别注意。

        如果程序中定义有大量的常量,在定义时,一定要在头部写上PROGMEM,还要添加头文件#include <avr/pgmspace.h>,这样,定义的常量就保存在program memory(flash)中了。否则,系统就会将变量定义到data memory区,编译时由于data memory仅有4k而会出现内存溢出的现象。
        当data memory区的变量读取program memory区的数据时,需要用库函数pgm_read_byte()进行读取。

二、如何将字符串变量定义到program memory(flash

        第一步:必须包含头文件:#include <avr/pgmspace.h>

#include <avr/pgmspace.h>

        第二步:用关键字PROGMEM定义字符串常量

PROGMEM const char flash_string[]= "Hello World.";

三、如何读取字符串变量

        当data memory区的变量去读取program memory区的数据时,需要用库函数pgm_read_byte()进行读取,下面我们编写一个入口参数为字符串常量的函数,将入口的字符串常量发送到串口1,需要用到系统提供的函数pgm_read_byte()。

#include <avr/pgmspace.h>

PROGMEM const char flash_string[]= "Hello World.";


void HMISendString_const(const char *string)
{
	uint8_t ch;
    	
	
	while (pgm_read_byte(&(*string)) != '\0')
    {
		ch = pgm_read_byte(&(*string));
        UDR1 = ch;
        string++;
    }    
}


int main(void)
{
    HMISendString_const(flash_string); 
}

四、PSTR()

        AVR提供了一个宏 PSTR(),它在头文件pgmspace.h中,它可以用于将字符串定义到program memory区(flash)。宏 PSTR()的具体定义参见下图。

         上面的程序代码可以不定义PROGMEM const char flash_string[]= "Hello World.";  而是可以直接用宏 PSTR()将字符串定义到program memory区(flash),改造后的程序如下:

#include <avr/pgmspace.h>

void HMISendString_const(const char *string)
{
	uint8_t ch;
    	
	
	while (pgm_read_byte(&(*string)) != '\0')
    {
		ch = pgm_read_byte(&(*string));
        UDR1 = ch;
        string++;
    }    
}


int main(void)
{
    HMISendString_const(PSTR("Hello World.")); 
}

五、使用strcpy()函数将字符串常量拷贝字符串缓冲区会占用大量的data memory区空间;使用strcpy_P()函数将字符串常量拷贝到字符串缓冲区仅仅占用program memory区(flash)空间,不会占用data memory区空间。

1、使用strcpy()函数拷贝字符串前,先在data memory空间定义了一个字符串缓冲区char HMI_string[255],编译程序后发现data memory空间占用了2362个字节。

 2、现在我们用库函数strcpy()编写一段程序,将字符串常量 "I am C++"拷贝到字符串缓冲区HMI_string,观察是否会占用data memory区空间。

        程序代码参见下图,编译程序后发现data memory空间目前占用了2370个字节(之前为2362个字节)。

        怎么只是在程序中调用了strcpy(HMI_string , "I am C++") ,data memory空间就多占用了8个字节呢?     

  3、现在我们用库函数strcpy()再编写一段程序,将字符串常量 "I am C#"拷贝到字符串缓冲区HMI_string,观察是否会继续占用data memory区空间。

        程序代码参见下图,编译程序后发现data memory空间目前占用了2378个字节,又比之前多占用了8个字节(之前占用了2370个字节)。

        我们发现一个规律:(1)、只要调用strcpy(HMI_string , "I am C++"),编译器发现第二个入口参数是普通的字符串"I am C++",就会在data memory空间申请8个字节的空间。(2)、只要调用strcpy(HMI_string , "I am C#"),编译器发现第二个入口参数是普通的字符串"I am C#",就会在data memory空间申请8个字节的空间。       

 4、如何将一串字符串常量拷贝到字符串缓冲区,而且还可以确保第二个入口参数不占用data memory空间呢?

      strcpy()函数是GCC AVR的头文件string.h中的函数,要想将一串字符串常量拷贝到字符串缓冲区,而且字符串拷贝函数的入口参数还不占用data memory空间,就必须要使用头文件pgmspace.h中的库函数strcpy_P,库函数strcpy_P的原型如下:

                  char *strcpy_P(char *, const char *);                 

        头文件pgmspace.h中的库函数与头文件string.h中的库函数的区别是:

        (1)、pgmspace.h中的库函数比头文件string.h中的库函数多了一个后缀  _P。

        (2)、char *strcpy_P(char *, const char *)   第二个参数是程序存储器program memory区(flash)。

        (3)、主程序调用strcpy_P(HMI_string , PSTR("I am C++"));时,第二个参数不会占用data memory空间。

        因此,要想将一串字符串常量拷贝到字符串缓冲区,而且还要让入口参数的字符串常量不占用data memory空间,就必须要使用头文件pgmspace.h中的函数char *strcpy_P(char *, const char *)   

        警告:strcpy_P()的第二个入口参数的类型是 const,因此需要在字符串常量前面添加宏PSTR(),将其定义到program memory区

                  例如:

                             strcpy_P(HMI_string , PSTR("I am C++"));

5、验证

        (1)、程序中添加 strcpy_P(HMI_string , PSTR("I am C++")); ,程序编译程序后data memory空间仍然是2362字节,可见使用头文件pgmspace.h中的库函数 strcpy_P(), 第二个参数PSTR("I am C++")不会增加data memory空间。   

         (2)、程序中再添加 strcpy_P(HMI_string , PSTR("I am C#")); ,程序编译程序后data memory空间仍然是2362字节,可见使用头文件pgmspace.h中的库函数 strcpy_P(), 第二个参数PSTR("I am C#")不会增加data memory空间。   

 六、使用sprintf()函数将字符串常量打印到字符串缓冲区会占用大量的data memory区空间;使用sprintf_P()函数将字符串常量打印到字符串缓冲区仅仅占用program memory区(flash)空间,不会占用data memory区空间。

1、使用sprintf()函数打印字符串前,编译程序后发现data memory空间占用了2362个字节。

 2、现在我们用库函数sprintf()编写一段程序,将字符串常量 "I am C++"和数值1275混合打印到字符串缓冲区HMI_string,观察是否会占用data memory区空间。

        程序代码参见下图,编译程序后发现data memory空间目前占用了2372个字节(之前为2362个字节)。

        怎么只是在程序中调用了sprintf(HMI_string , "I am C++%d" , 1278) ,data memory空间就多占用了10个字节呢?     

        我们发现一个规律:(1)、只要调用sprintf(HMI_string , "I am C++%d" , 1278) 编译器发现第二个入口参数是普通的字符串"I am C++",就会在data memory空间申请10个字节的空间。

 3、如何将一串字符串常量和数值混合打印到字符串缓冲区,而且还可以确保第二个入口参数不占用data memory空间呢?

      sprintf()函数是GCC AVR的头文件string.h中的函数,要想将一串字符串常量和数值混合打印到字符串缓冲区,而且字符串打印函数的入口参数还不占用data memory空间,就必须要使用头文件pgmspace.h中的库函数sprintf_P,库函数sprintf_P的原型如下:

                      int    sprintf_P(char *__s, const char *__fmt, ...);

    头文件pgmspace.h中的库函数与头文件string.h中的库函数的区别是:

        (1)、pgmspace.h中的库函数比头文件string.h中的库函数多了一个后缀  _P。

        (2)、 int    sprintf_P(char *__s, const char *__fmt, ...); 第二个参数是程序存储器program memory区(flash)。

        (3)、主程序调用sprintf(HMI_string , "I am C++%d" , 1278)时,第二个参数不会占用data memory空间。

          因此,要想将一串字符串常量和数值混合打印到字符串缓冲区,而且还要让入口参数的字符串常量不占用data memory空间,就必须要使用头文件pgmspace.h中的函数 sprintf_P(char *__s, const char *__fmt, ...) 

        警告:sprintf_P()的第二个入口参数的类型是 const,因此需要在字符串常量前面添加宏PSTR(),将其定义到program memory区

                  例如:

                             sprintf_P(HMI_string , PSTR("I am C++%d") , 1278);

4、验证

        (1)、程序中添加 sprintf_P(HMI_string , PSTR("I am C++%d") , 1278); ,程序编译程序后data memory空间仍然是2362字节,可见使用头文件pgmspace.h中的库函数 sprintf_P(), 第二个参数PSTR("I am C++%d")不会增加data memory空间。   

 

七、总结

1、要想定义字符串常量到program memory区,首先要添加头文件#include <avr/pgmspace.h>,

2、然后用关键字PROGMEM定义字符串常量,例如:PROGMEM const char flash_string[]= "Hello World.";

3、也可以宏定义PSTR()直接在用户函数的入口参数上进行限制,例如:HMISendString_const(PSTR("Hello World."));

4、要想将字符串常量拷贝字符串缓冲区而不占用data memory区空间,必须要使用头文件pgmspace.h中的strcpy_P(),而不能使用头文件string.h中的strcpy()。而且strcpy_P()的第二个入口参数必须要用宏PSTR()进行限定,例如:strcpy_P(HMI_string , PSTR("I am C++"));

5、要想将字符串常量和数值混合打印到字符串缓冲区而不占用data memory区空间,必须要使用头文件pgmspace.h中的sprintf_P(),而不能使用头文件string.h中的sprintf()。而且sprintf_P()的第二个入口参数必须要用宏PSTR()进行限定,例如:sprintf_P(HMI_string , PSTR("I am C++%d") , 1278);

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值