如何在 Arduino 中使用 PROGMEM 存储大型不可变数据?


PROGMEM 是一个关键字,当您希望将数据存储在程序内存(Flash)而不是 SRAM 中时使用。虽然您可以将 PROGMEM 用于单个变量,但这并没有多大意义。毕竟,SRAM 的空间足以容纳您的单个变量,并且访问存储在 SRAM 中的变量速度更快。

PROGMEM 主要用于大型数据块(主要是数组),这些数据块可能会占用 SRAM 的大量空间(SRAM 的大小通常远小于 Flash 内存,但访问速度更快)。将数据存储在 PROGMEM 中意味着它在运行时不能动态修改。因此,人们通常使用 PROGMEM 来存储大型不可变文本或数据。

如果您使用的是低于 1.0 版本的 Arduino IDE(为什么?),则需要在代码顶部包含

#include <avr/pgmspace.h>

才能使用 PROGMEM 关键字。对于 Arduino 1.0 及更高版本,您可以直接使用 PROGMEM 关键字,无需任何包含。

请注意,PROGMEM 仅适用于全局变量或使用 static 关键字定义的变量。

语法

语法如下:

const dataType arrayName[] PROGMEM = {data0, data1, data3…};

任何变量类型都允许用于 datatype,而 **arrayName** 是数组的名称。

为了访问使用 PROGMEM 存储在 Flash 内存中的数据,可以使用专用函数:

  • **strlen_P(arrayName)** - 此函数返回数组 **arrayName** 的长度。

  • **pgm_read_byte_near(address)** - 此函数返回位于 address 位置的一个字节的值。

  • **pgm_read_word_near(address)** - 此函数返回从 address 位置开始的一个字(在大多数微控制器上为 2 个字节)的值。

下面示例将阐明这些函数的用法。这些并不是唯一可与 PROGMEM 一起使用的函数。您可以在这里找到其他一些函数:这里

示例

让我们从示例开始。我们将使用 Arduino 文档中提供的示例。

我们将查看第一个示例。如您所见,在 PROGMEM 中定义了两个数组,一个是 16 位整数数组(16 位 = Arduino Uno 上的一个字),另一个是字符数组(每个字符为 8 位或 1 字节)。

// 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;
char myChar;

在 **setup** 中,我们初始化 **Serial**。然后,我们一次读取一个字,打印 charset 数组的整数。请注意,我们如何使用 **charSet + k** 作为我们要读取的地址。您可能还记得,数组的名称也是其第一个元素的指针。这里使用了相同的属性。

void setup() {
   Serial.begin(9600);
   while (!Serial); // wait for serial port to connect. Needed for native USB
   // put your setup code here, to run once:
   // read back a 2-byte int
   for (byte k = 0; k < 5; k++) {
      displayInt = pgm_read_word_near(charSet + k);
      Serial.println(displayInt);
   }
   Serial.println();

然后,我们从 **signMessage** 数组中一次读取一个字节并将其打印到串口监视器。请注意,我们使用 **strlen_P** 函数获取数组的长度,以确定 **for** 循环的终止条件。

   // read back a char
   for (byte k = 0; k < strlen_P(signMessage); k++) {
      myChar = pgm_read_byte_near(signMessage + k);
      Serial.print(myChar);
   }
   Serial.println();
}

循环内部没有任何操作。

更新于:2021年7月24日

7000+ 次查看

启动您的 职业生涯

完成课程获得认证

开始学习
广告