查看: 19010|回复: 17

Arduino进阶教程——使用PROGMEM在flash中存储数据

[复制链接]
  • TA的每日心情
    郁闷
    2018-12-6 22:21
  • 签到天数: 48 天

    [LV.5]常住居民I

    发表于 2014-11-14 00:14 | 显示全部楼层 |阅读模式
    本方法只适用于使用AVR作为核心的Arduino!
    本文为机器翻译,原文地址:https://www.arduino.cc/en/Reference/PROGMEM

    闪存(程序)内存而不是SRAM存储数据。有各种类型的可用内存的描述上的Arduino板。

    该PROGMEM关键字是一个可变调节剂,但只应在pgmspace.h定义的数据类型使用。它告诉编译器“把这个信息到闪存”,而不是到SRAM,它通常会去。

    PROGMEM是pgmspace.h库仅在AVR架构可用的一部分。所以,你首先需要包含库顶部草图,就像这样:


    使用语法:

    kittenblock中小学创客名师推荐的图形化编程软件

    const dataType variableName[] PROGMEM = {data0, data1, data3...};


    dataType   任何变量类型
    variableName   数组名称

    请注意,因为PROGMEM是一个可变的调节剂,也没有严格的,它应该走的快规律,Arduino的编译器接受以下所有的定义,这也是代名词。然而实验已经表明,在Arduino的各种版本(具有用GCC版本做),PROGMEM可以在一个位置,而不是工作在另一。“字符串表”下面的例子已经过测试,Arduino的合作13. IDE的早期版本可能更好地工作,如果PROGMEM的变量名后包括在内。

    kittenblock中小学创客名师推荐的图形化编程软件

    const dataType variableName[] PROGMEM = {};   // use this form
    const PROGMEM  dataType  variableName[] = {}; // or this form
    const dataType PROGMEM variableName[] = {};   // not this one


    虽然PROGMEM可以在单个变量使用,真的是唯一值得大惊小怪的,如果你有更大的数据块需要存储,这通常是最容易在数组中(或其他C数据结构超出了我们现在的讨论)。

    使用PROGMEM也是一个两步过程。获取数据到闪存后,它需要特殊的方法(函数),在pgmspace.h库还规定,从程序存储器中的数据读回SRAM,所以我们可以做一些有用的事情吧。



    示例程序:
    下面的代码片段说明了如何读取和写入字符(字节)和int(2字节)PROGMEM。

    kittenblock中小学创客名师推荐的图形化编程软件

    #include <avr/pgmspace.h>
    
    
    // save some unsigned ints
    const PROGMEM  uint16_t charSet[]  = { 65000, 32796, 16843, 10, 11234};
    
    // save some chars
    const char signMessage[] PROGMEM  = {"I AM PREDATOR,  UNSEEN COMBATANT. CREATED BY THE UNITED STATES DEPART"};
    
    unsigned int displayInt;
    int k;    // counter variable
    char myChar;
    
    
    void setup() {
      Serial.begin(9600);
      while (!Serial);
    
      // put your setup code here, to run once:
      // read back a 2-byte int
      for (k = 0; k < 5; k++)
      {
        displayInt = pgm_read_word_near(charSet + k);
        Serial.println(displayInt);
      }
      Serial.println();
    
      // read back a char
      int len = strlen_P(signMessage);
      for (k = 0; k < len; k++)
      {
        myChar =  pgm_read_byte_near(signMessage + k);
        Serial.print(myChar);
      }
    
      Serial.println();
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
    
    }



    字符串数组

    用大量的文字,如配有液晶显示器的一个项目工作时,设置一个字符串数组它往往是方便。因为字符串本身是数组,这是在实际的二维阵列的实例。

    这些往往是这样把它们放入程序存储器经常需要大的结构。下面的代码说明了这个想法。



    kittenblock中小学创客名师推荐的图形化编程软件

    /*
     PROGMEM string demo
     How to store a table of strings in program memory (flash),
     and retrieve them.
    
     Information summarized from:
     http://www.nongnu.org/avr-libc/user-manual/pgmspace.html
    
     Setting up a table (array) of strings in program memory is slightly complicated, but
     here is a good template to follow.
    
     Setting up the strings is a two-step process. First define the strings.
    */
    
    #include <avr/pgmspace.h>
    const char string_0[] PROGMEM = "String 0";   // "String 0" etc are strings to store - change to suit.
    const char string_1[] PROGMEM = "String 1";
    const char string_2[] PROGMEM = "String 2";
    const char string_3[] PROGMEM = "String 3";
    const char string_4[] PROGMEM = "String 4";
    const char string_5[] PROGMEM = "String 5";
    
    
    // Then set up a table to refer to your strings.
    
    const char* const string_table[] PROGMEM = {string_0, string_1, string_2, string_3, string_4, string_5};
    
    char buffer[30];    // make sure this is large enough for the largest string it must hold
    
    void setup()
    {
      Serial.begin(9600);
      while(!Serial);
      Serial.println("OK");
    }
    
    
    void loop()
    {
      /* Using the string table in program memory requires the use of special functions to retrieve the data.
         The strcpy_P function copies a string from program space to a string in RAM ("buffer").
         Make sure your receiving string in RAM  is large enough to hold whatever
         you are retrieving from program space. */
    
    
      for (int i = 0; i < 6; i++)
      {
        strcpy_P(buffer, (char*)pgm_read_word(&(string_table))); // Necessary casts and dereferencing, just copy.
        Serial.println(buffer);
        delay( 500 );
      }
    }


    注意事项

    请注意,必须使用全局变量定义flash存储的数据,或者用static关键字定义,这样才能使PROGMEM正常工作。
    如果在一个函数中使用如下方式定义,PROGMEM将无法正常工作:

    kittenblock中小学创客名师推荐的图形化编程软件

    const char long_str[] PROGMEM = "Hi, I would like to tell you a bit about myself.\n";

    要作为局部变量定义,必须添加static 关键字,如:

    kittenblock中小学创客名师推荐的图形化编程软件

    const static char long_str[] PROGMEM = "Hi, I would like to tell you a bit about myself.\n"


    F()宏
    通常我们都使用如下语句,进行串口输出:

    kittenblock中小学创客名师推荐的图形化编程软件

    Serial.print("Write something on  the Serial Monitor");

    但这样使用,每次调用时,都会先将数据保存在RAM中。当我们要输出长的字符串时,就会占用很多的RAM空间。使用F()就可以很好的解决这个问题:

    kittenblock中小学创客名师推荐的图形化编程软件

    Serial.print(F("Write something on the Serial Monitor that is stored in FLASH"));

    如果以上内容对你有帮助,你可以通过打赏支持作者
  • TA的每日心情
    奋斗
    2016-11-4 10:39
  • 签到天数: 1 天

    [LV.1]初来乍到

    发表于 2016-11-3 17:22 | 显示全部楼层
    大神你好,arduino101 的flash存进去的数据,怎么调用出来呢?

    该用户从未签到

    发表于 2017-3-7 10:17 | 显示全部楼层
    请问怎么读取float型数组?
  • TA的每日心情
    开心
    2018-11-18 00:25
  • 签到天数: 238 天

    [LV.7]常住居民III

    发表于 2017-6-19 14:47 | 显示全部楼层
    原来还可以这样呀。

    该用户从未签到

    发表于 2017-7-24 20:01 | 显示全部楼层
    请问这存进FLASH的数组还可以进行写入操作吗,比如说定义一个空的数组(全部是0)放在FLASH,后期单片机收集的数据进行写入

    点评

    不明白你是什么意思,“存进”即是“写入”  详情 回复 发表于 2017-8-20 00:36
  • TA的每日心情
    开心
    2017-8-26 08:03
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    发表于 2017-8-19 21:50 | 显示全部楼层
    还看英文的比较容易看懂
  • TA的每日心情
    郁闷
    2018-12-6 22:21
  • 签到天数: 48 天

    [LV.5]常住居民I

     楼主| 发表于 2017-8-20 00:36 | 显示全部楼层
    759717075 发表于 2017-7-24 20:01
    请问这存进FLASH的数组还可以进行写入操作吗,比如说定义一个空的数组(全部是0)放在FLASH,后期单片机收 ...

    不明白你是什么意思,“存进”即是“写入”
    如果以上内容对你有帮助,你可以通过打赏支持作者
  • TA的每日心情

    2018-5-15 11:29
  • 签到天数: 5 天

    [LV.2]偶尔看看I

    发表于 2018-4-21 13:02 | 显示全部楼层
    马住以后来学习~~·
  • TA的每日心情
    开心
    2019-4-2 21:37
  • 签到天数: 10 天

    [LV.3]偶尔看看II

    发表于 2018-4-22 11:19 | 显示全部楼层
    很有用的知识,学习了
  • TA的每日心情
    无聊
    2019-5-15 17:11
  • 签到天数: 26 天

    [LV.4]偶尔看看III

    发表于 2019-3-12 17:33 | 显示全部楼层
    本帖最后由 MrBattery 于 2019-3-12 18:09 编辑

    请问下如果我想读取整个数组该怎么写,我本来的数组是unsigned int直接定义的,因为动态内存满了就换成了这个语句,但是我原来的调用语句无法调用我设定的数组了
    期待大佬回复


    #include <avr/pgmspace.h>
    #include <IRremote.h>//红外发射模块接3引脚


    IRsend irsend;
    const unsigned int geli_on_30Hhigh[254] PROGMEM  = {8900,4450,700,600,650,650, ........(省略).......};
    const unsigned int geli_on_16Chigh[254] PROGMEM = {9000,4350,750,1600,650,650,
    ........(省略).......
    };
    const unsigned int geli_off[254] PROGMEM = {9000,4350,750,600,700,600,700,1650,
    ........(省略).......
    };

    void setup(){}

    void loop()
    {
      irsend.sendRaw(geli_on_30Hhigh,254,38);   //这里是发射语句,本来是可以发射的,但是现在无法发射红外信号
      delay(500);
      irsend.sendRaw(geli_on_16Chigh,254,38);
      delay(500);
      irsend.sendRaw(geli_off,254,38);
      delay(500);
    }


    您需要登录后才可以回帖 登录 | 立即注册  

    本版积分规则

    热门推荐

    有对屏幕了解的大神吗?
    有对屏幕了解的大神吗?
    这种屏幕可以用arduino板或者树莓派做出来吗?小白对这方面一无所知,求指点
    编写了个贪食蛇小程序
    编写了个贪食蛇小程序
    8*8双色3mm红蓝雾状发光二极管 用595驱动的
    Adruino小型自平衡机器人EVA(+硬件+源代码+3D文件)
    Adruino小型自平衡机器人E
    [*]2个月前做的小机器人,在CSDN个人博客上发了,从没接触过PID、3D打印、电机等,就
    超萌机器人  pando
    超萌机器人 pando
    ## 准备工作准备好如下材料: · DFRobot Romeo BLE mini V2.0 控制器 × 1·
    红外模块在接收时码值变动
    红外模块在接收时码值变动
    在使用arduino连接一个红外接收后,下载的红外模块的库IRremote,在使用时发现,遥控
    Copyright   ©2015-2016  Arduino中文社区  Powered by©Discuz!   ( 蜀ICP备14017632号-3 )
    快速回复 返回顶部 返回列表