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

时间:2015-06-28 14:14:53

标签: c arduino avr progmem

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

我在下面列出了Arduino的完整测试草图。它的大部分工作,但是当我循环遍历“alpha”字符的每个字节时,正如“alphabytes”所指出的那样,我只是把垃圾拿出来所以我显然没有访问正确的内存位置。问题是,我无法弄清楚如何访问该内存位置。

我已经看到了其他几个这样的例子,但没有一个在指针数组中有不同大小的数据数组。

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

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

2 个答案:

答案 0 :(得分:0)

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

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

对pgm_read_byte()的内部调用:

pgm_read_byte(&alphabytes[a])

返回一个值,该值是被评估字符的地址(注意前导“address-of”运算符“&amp;”)。

外部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”尝试并且它也有效)。

答案 1 :(得分:0)

请注意, 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++);
    ...
}