从闪存访问结构中的数据

时间:2018-11-10 15:20:45

标签: c structure avr avr-gcc flash-memory

我正在使用Atmel AVR,并试图从存储在闪存(程序)存储器中的结构访问数据。

结构为:

typedef struct {
    uint8_t width;
    uint8_t height; // row number 0 to 5
    uint8_t images; // how many frames does this bitmap have
    uint8_t data[]; // the actual pixel data
} bitmap_t;

数据为:

    __flash   static const bitmap_t bmp_stereo2  = {14,1,1,{126,129,60,66,24,36,60,60,36,24,66,60,129,126}};

我正在尝试使用(显示部分代码)访问数据...

void lcd_bitmap2(const bitmap_t *bit, uint8_t id, uint8_t posx, uint8_t posy) {
    uint8_t x;
    uint8_t y;  

    const uint8_t bw  = pgm_read_byte(&bit->width);   // this works -- I can print out to serial
    const uint8_t bh  = pgm_read_byte(&bit->height);  //this also works -- I can print out to serial
    // this doesn't work
    const uint8_t  *data = pgm_read_word(&bit->data); // I get: - initialization makes pointer from integer without a cast [enabled by default] 

    const uint8_t  *data = (uint8_t *)pgm_read_word(&bit->data); // this also doen't work (no warning, but wrong data read out)

    //rest of function...

因此,我可以访问width,height和image变量,但不能访问结构的数据部分。 如果我不将其存储在闪存中,则所有方法都可以正常工作-这是我的检索工作,只是结构的数据数组部分有问题(宽度,高度和图像都可以读取)

2 个答案:

答案 0 :(得分:2)

TL; DR :您无法在标准C中安全地做到这一点。

给出此定义...

typedef struct {
    uint8_t width;
    uint8_t height; // row number 0 to 5
    uint8_t images; // how many frames does this bitmap have
    uint8_t data[]; // the actual pixel data
} bitmap_t;

... bitmap_t.data是“灵活数组成员”:

  

作为特殊情况,结构中的最后一个元素包含多个   命名成员的数组类型可能不完整;这叫做   灵活的数组成员。 在大多数情况下,灵活数组成员   被忽略 。特别是,结构的大小就像   柔性数组成员被省略,除了它可能有更多   比省略所暗示的尾随填充。但是,当.(或   ->)运算符具有一个左操作数,该操作数是一个指向具有灵活数组成员的结构的指针(指向该成员的右操作数),   它的行为就像该成员被最长的数组替换   (具有相同的元素类型)不会使结构变大   比被访问的对象[...]。

[C2011 6.7.2.1/18;强调]

标准中的示例阐明,正如您的代码尝试执行的那样,为灵活的数组成员提供初始化程序是无效的。此外,没有理由期望为具有静态或自动存储期限的对象(例如您的)的灵活数组成员的内容分配任何空间。灵活的数组成员仅在与动态分配的对象结合使用时才有用,除非在实现方面超出了标准要求的实现中除外。

因此,访问静态分配的对象的灵活数组成员会产生未定义的行为。这与存储的实际位置没有直接关系,尽管在闪存中UB的显示方式可能有所不同。 (毕竟,行为是 undefined 。)要以尝试使用的方式使用bitmap_t类型的对象,则需要修改该类型,以使其{{1} }成员具有完整的类型(即固定尺寸)。

答案 1 :(得分:1)

(独立于约翰·博林格回答)

您在此处错误地将pgm_read_word用作->data的{​​{1}}成员,即使使用bitmap_t定义了结构,代码也无法正常工作。

data[14]是闪存中的字节大小的对象。 您可以使用XXX阅读它 这样一来,人们可以通过以下方式读取位置pgm_read_byte(& XXX)pixel_number对象)处的像素:

data[pixel_number]

直接结构访问也得到相同的结果

// just address of first byte in data[]
const uint8_t  *data = bit->data;

uint8_t  pixel = pgm_read_byte(&data[pixel_number]);

最后,现代的avr-gcc编译器(关键字为uint8_t pixel = pgm_read_byte(&bit->data[pixel_number]); 的编译器)可以自动执行此操作。 只需使用__flash修饰符声明所有指针(__flash可以省略):

<avr/pgmspace.h>

p.s。根据我的经验,avr-gcc会以您期望的方式处理不完整的数组初始化(也在Flash中),并且无法访问。