在 Arduino/AVR 上访问 PROGMEM 中的单个字节

Accessing individual bytes in PROGMEM on Arduino/AVR

我已经阅读了几天关于访问 PROGMEM 的内容,并梳理了其他几个问题,但我的代码仍然无法正常工作。任何帮助将不胜感激。

我在下面包含了 Arduino 的完整测试草图。它的大部分工作正常,但是当我循环遍历 "alpha" 字符的每个字节时,正如 "alphabytes" 所指出的那样,我只是得到了垃圾,所以我显然没有访问正确的内存位置.问题是,我不知道如何访问该内存位置。

我见过其他几个这样工作的例子,但是 none 在指针数组中有不同大小的数据数组。

请查看以“>>>> 问题是...”开头的行

// Include PROGMEM library
#include <avr/pgmspace.h>

// Variable to hold an alphabet character row
char column_byte;

// Used to hold LED pixel value during display
char led_val;

// Used to hold the screen buffer for drawing the screen
char matrix_screen[64];

/*
  Define Alphabet characters. This should allow for characters of varying byte lengths.
*/
const char alpha_A[] PROGMEM = {0x06, 0x38, 0x48, 0x38, 0x06};
const char alpha_B[] PROGMEM = {0x7E, 0x52, 0x52, 0x2C};
const char alpha_C[] PROGMEM = {0x3C, 0x42, 0x42, 0x24};

/*
  The "alphabytes" contains an array of references (pointers) to each character array.
  Read right-to-left, alphabytes is a 3-element constant array of pointers,
  which points to constant characters

*/
const char* const alphabytes[3] PROGMEM = {
  alpha_A, alpha_B, alpha_C
};

/*
  This array is necessary to list the number of pixel-columns used by each character.
  The "sizeof" function cannot be used on the inner dimension of alphabytes directly
  because it will always return the value "2". The "size_t" data
  type is used because is a type suitable for representing the amount of memory
  a data object requires, expressed in units of 'char'.
*/
const char alphabytes_sizes[3] PROGMEM = {
  sizeof(alpha_A), sizeof(alpha_B), sizeof(alpha_C)
};

/**
 * Code Setup. This runs once at the start of operation. Mandatory Arduino function
 **/
void setup(){

  // Include serial for debugging
  Serial.begin(9600);
}

/**
 * Code Loop. This runs continually after setup. Mandatory Arduino function
 **/
void loop(){

  // Loop through all alphabet characters
  for( int a = 0; a < 3; a++) {

    // Reset screen
    for (int r = 0; r < 64; r++) {
      matrix_screen[r] = 0;
    }

    // This line works to read the length of the selected "alphabyte"
    int num_char_bytes = pgm_read_byte(alphabytes_sizes + a);

    for (int b = 0; b < num_char_bytes; b++){

      // Based on alphabytes definition,
      // Examples of desired value for column_byte would be:
      //
      // When a=0, b=0 -> column_byte = 0x06
      // When a=0, b=1 -> column_byte = 0x38
      // When a=0, b=2 -> column_byte = 0x48
      // When a=0, b=3 -> column_byte = 0x38
      // When a=0, b=4 -> column_byte = 0x06
      // When a=1, b=0 -> column_byte = 0x7E
      // When a=1, b=1 -> column_byte = 0x52
      // When a=1, b=2 -> column_byte = 0x52
      // When a=1, b=3 -> column_byte = 0x2C
      // When a=2, b=0 -> column_byte = 0x3C
      // When a=2, b=1 -> column_byte = 0x42
      // When a=2, b=2 -> column_byte = 0x42
      // When a=2, b=3 -> column_byte = 0x24

      // >>>>> Question is... how to I get that? <<<<<<<
      // column_byte = pgm_read_byte(&(alphabytes[a][b])); // This doesn't work

      // Thought: calculate offset each time
      // int offset = 0;
      // for(int c = 0; c < a; c++){
      //   offset += pgm_read_byte(alphabytes_sizes + c);
      // }
      // column_byte = pgm_read_byte(&(alphabytes[offset])); // This doesn't work

      // column_byte = (char*)pgm_read_word(&alphabytes[a][b]); // Doesn't compile
      column_byte = pgm_read_word(&alphabytes[a][b]); // Doesn't work

      // Read each bit of column byte and save to screen buffer
      for (int j = 0; j < 8; j++) {
        led_val = bitRead(column_byte, 7 - j);
        matrix_screen[b * 8 + j] = led_val;
      }

    }

    // Render buffer to screen
    draw_screen();

    // Delay between frames
    delay(5000);

  }

}

/**
 * Draw the screen. This doesn't have the correct orientation, but
 * that's fine for the purposes of this test.
 **/
void draw_screen(){
  for (int a = 0; a < 8; a++) {
    for (int b = 0; b < 8; b++) {
      Serial.print((int) matrix_screen[a * 8 + b]);
      Serial.print(" ");
    }
    Serial.println();
  }
  Serial.println();
}

经过大量研究(坦率地说,经过大量试验和错误),我找到了一个有效的解决方案。我不知道这是最正确还是最优雅的解决方案,但它确实有效。

column_byte = pgm_read_byte(pgm_read_byte(&alphabytes[a]) + b);

对 pgm_read_byte() 的内部调用:

pgm_read_byte(&alphabytes[a])

returns 一个值,它是正在评估的字符的地址(注意前导 "address-of" 运算符“&”)。

外部 pgm_read_byte 在偏移量 "b" 处读取该内存。

这个解决方案也可以分为两部分:

int memory_loc = pgm_read_byte(&alphabytes[a]);
column_byte = pgm_read_byte(memory_loc + b);

我的 C 技能不足以真正解释 "memory_loc" 是否需要是一个 int(我试过它作为 "char" 并且它也有效)。

注意alphabytes它是数组,每个元素包含一个REFERENCE(即地址),其中存储了相应的字符。因此,您应该分两步访问它。

第一步是了解所需项目在程序中的地址。地址为 16 位宽(除非您使用 128+k 设备)。

PGM_VOID_P ptr = (PGM_VOID_P) pgm_read_word(&alphabytes[a]); 

而且,如果你想逐字节访问它,你可以只使用这个指针读取,然后递增它:

for (int b = 0; b < num_char_bytes; b++) {
    uint8_t column_byte = pgm_read_byte(ptr++);
    ...
}