【问题标题】:accessing data from structure in flash从闪存中的结构访问数据
【发布时间】:2018-11-10 15:20:44
【问题描述】:

我正在使用 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...

所以我可以访问宽度、高度和图像变量,但不能访问结构的数据部分。 如果我不存储在闪存中,一切正常 - 这是我的检索,只是结构的数据数组部分存在问题(宽度、高度和图像读取正常)

【问题讨论】:

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


    【解决方案1】:

    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 在闪存中时可能会以不同的方式表现出来。 (毕竟,行为是未定义。)要以您尝试的方式使用bitmap_t 类型的对象,您需要修改该类型,使其data 成员具有完整类型(即固定尺寸)。

    【讨论】:

    • 非常感谢 - 对它进行了排序。不知道为什么 data 需要前面的 __flash -(__flash const uint8_t *data = bit->data;) 但其他不需要 - 这是数组处理的区别吗?非常感谢和所有其他回复的人
    • @Russell 如果某个变量放置在__flash 内存空间中,则指向该变量的指针必须限定为__flash。所以,__flash const uint8_t *ptr; 是数据空间中的指针(可能在 CPU 寄存器中),它指向 __flash const uint8_t 变量,即放在闪存中的变量。与uint8_t * __flash const ptrf; 相比——常量指针本身放置在__flash 空间中,它指向数据空间(RAM)中的变量。
    【解决方案2】:

    (独立于 John Bollinger 的回答)

    您在此处错误地使用了pgm_read_word->databitmap_t 成员,即使结构将使用data[14] 定义,代码也无法工作。

    XXX 是闪存中的字节大小对象。 你可以用pgm_read_byte(& XXX)阅读 因此,可以通过这种方式读取位置pixel_numberdata[pixel_number] 对象)处的像素:

    // just address of first byte in data[]
    const uint8_t  *data = bit->data;
    
    uint8_t  pixel = pgm_read_byte(&data[pixel_number]);
    

    直接结构访问得到同样的结果

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

    最后,现代 avr-gcc 编译器(带有关键字 __flash 的编译器)可以自动执行此操作。 只需用__flash 修饰符声明所有指针(<avr/pgmspace.h> 可以省略):

    void lcd_bitmap2(__flash const bitmap_t *bit, uint8_t id,
                     uint8_t posx, uint8_t posy)
    {
        uint8_t x;
        uint8_t y;  
    
        const uint8_t bw  = bit->width;
        const uint8_t bh  = bit->height;
        __flash const uint8_t *data = bit->data;
    
        uint8_t pixel_number; // make calculations with id, bw, bh and positions
    
        uint8_t pixel = data[pixel_number];
    
        // or ditectly pixel = bit->data[pixel_number];
    

    附言根据我的经验,avr-gcc 处理不完整的数组初始化(也在闪存中)并以您期望的方式访问。

    【讨论】:

      猜你喜欢
      • 2020-03-22
      • 1970-01-01
      • 1970-01-01
      • 2016-12-24
      • 1970-01-01
      • 1970-01-01
      • 2023-03-17
      • 1970-01-01
      相关资源
      最近更新 更多