简体   繁体   English

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

[英]Accessing individual bytes in PROGMEM on Arduino/AVR

I've read up on accessing PROGMEM for days now, and combed through several other questions, but I still can't get my code working. 我已经阅读了几天的访问PROGMEM的内容,并梳理了其他几个问题,但是我仍然无法使我的代码正常工作。 Any help would be appreciated. 任何帮助,将不胜感激。

I've included a full test sketch for Arduino below. 我在下面提供了针对Arduino的完整测试草图。 The majority of it works, but when I loop through each byte of an "alpha" character, as pointed to by "alphabytes", I'm just getting garbage out so I'm obviously not accessing the correct memory location. 它大部分都能工作,但是当我遍历“ alpha”字符所指向的“ alpha”字符的每个字节时,我只是在被清除掉,因此显然我无法访问正确的内存位置。 The problem is, I can't figure out how to access that memory location. 问题是,我不知道如何访问该内存位置。

I've seen several other examples of this working, but none that have different sizes of data arrays in the pointer array. 我已经看到了其他一些工作示例,但没有一个在指针数组中具有不同大小的数据数组。

Please see line beginning with ">>>> Question is..." 请查看以“ >>>>问题是...”开头的行。

// 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();
}

After much research (and quite frankly a lot of trial and error), I have come across a solution which is working. 经过大量研究(坦率地说,是反复试验),我遇到了一个可行的解决方案。 I don't know if this the the most correct or most elegant solution, but it works. 我不知道这是否是最正确或最优雅的解决方案,但是它可以工作。

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

The inner call to pgm_read_byte(): 对pgm_read_byte()的内部调用:

pgm_read_byte(&alphabytes[a])

returns a value, which is the address of the character being evaluated (notice the leading "address-of" operator "&"). 返回一个值,该值是要评估的字符的地址(请注意,前导“ address-of”运算符“&”)。

The outer pgm_read_byte reads that memory at an offset of "b". 外部pgm_read_byte以“ b”的偏移量读取该内存。

This solution can also be broken down into two parts: 此解决方案也可以分为两部分:

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

My C skills are not good enough to really explain if "memory_loc" needs to be an int (I tried it as a "char" and it also worked). 我的C语言技能不足以真正解释“ memory_loc”是否需要为int(我尝试将其作为“ char”使用,并且它也可以工作)。

Note that alphabytes it is array, which each element contains a REFERENCE (ie address) where corresponding characters are stored. 请注意,它是数组的Alphabyte ,每个元素包含一个REFERENCE(即地址),其中存储了相应的字符。 So, you should access it in two steps. 因此,您应该分两步访问它。

First step is to know address in the progmem of the required item. 第一步是在所需项目的程序中了解地址。 Addresses are 16bits wide (unless you are using 128+k device). 地址为16位宽(除非您使用的是128 + k设备)。

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

and, if you want to access it byte by byte, you can just read using this pointer, and then increment it: 并且,如果要逐字节访问它,则可以使用此指针进行读取,然后对其进行递增:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM