ESP8266 цикл перезагрузки в функции "memcpy_P"

Я пытаюсь изменить код, который был изначально разработан для платы Trinket (ATTiny85). Он правильно компилируется для платы ESP8266-12E (NodeMCU1.0). Но устройство перезагружается при загрузке изображения в память.

Линии с:

memcpy_P(palette, imagePalette,  2 * 3);

Вызывающее устройство будет перезагружено.

Я буду очень рад, если кто-то посоветует мне, что нужно изменить, чтобы решить эту проблему? Код взят из Github

Код:

#include <Arduino.h>
#include <Adafruit_DotStar.h>
#include <SPI.h> // Enable this line on Pro Trinket
typedef uint16_t line_t; // Bigger images OK on other boards


// CONFIGURABLE STUFF ------------------------------------------------------

#include "fire.h" // Graphics data is contained in this header file.

#define LED_DATA_PIN  MOSI
#define LED_CLOCK_PIN SCK
#define SELECT_PIN 3

boolean autoCycle = false; // Set to true to cycle images by default
#define CYCLE_TIME 15      // Time, in seconds, between auto-cycle images

// -------------------------------------------------------------------------

#if defined(LED_DATA_PIN) && defined(LED_CLOCK_PIN)
// Older DotStar LEDs use GBR order.  If colors are wrong, edit here.
Adafruit_DotStar strip = Adafruit_DotStar(NUM_LEDS,
  LED_DATA_PIN, LED_CLOCK_PIN, DOTSTAR_BRG);
#else
Adafruit_DotStar strip = Adafruit_DotStar(NUM_LEDS, DOTSTAR_BRG);
#endif

void     imageInit(void);
uint16_t readVoltage(void);
#ifdef MOTION_PIN
void     sleep(void);
#endif

void setup() {
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000L)
  clock_prescale_set(clock_div_1);   // Enable 16 MHz on Trinket
#endif

#ifdef POWER_PIN
  pinMode(POWER_PIN, OUTPUT);
  digitalWrite(POWER_PIN, LOW); // Power-on LED strip
#endif
  strip.begin();                // Allocate DotStar buffer, init SPI
  strip.clear();                // Make sure strip is clear
  strip.show();                 // before measuring battery


 imageInit(); // Initialize pointers for default image

#ifdef SELECT_PIN
  pinMode(SELECT_PIN, INPUT_PULLUP);
#endif
}

// GLOBAL STATE STUFF ------------------------------------------------------

uint32_t lastImageTime = 0L; // Time of last image change
uint8_t  imageNumber   = 0,  // Current image being displayed
         imageType,          // Image type: PALETTE[1,4,8] or TRUECOLOR
        *imagePalette,       // -> palette data in PROGMEM
        *imagePixels,        // -> pixel data in PROGMEM
         palette[16][3];     // RAM-based color table for 1- or 4-bit images
line_t   imageLines,         // Number of lines in active image
         imageLine;          // Current line number in image
#ifdef SELECT_PIN
uint8_t  debounce      = 0;  // Debounce counter for image select pin
#endif

void imageInit() { // Initialize global image state for current imageNumber
  imageType    = pgm_read_byte(&images[imageNumber].type);
  imageLines   = pgm_read_word(&images[imageNumber].lines);
  imageLine    = 0;
  imagePalette = (uint8_t *)pgm_read_word(&images[imageNumber].palette);
  imagePixels  = (uint8_t *)pgm_read_word(&images[imageNumber].pixels);
  if(imageType == PALETTE1)      memcpy_P(palette, imagePalette,  2 * 3);
  else if(imageType == PALETTE4) memcpy_P(palette, imagePalette, 16 * 3);
  lastImageTime = millis(); // Save time of image init for next auto-cycle
}

void nextImage(void) {
  if(++imageNumber >= NUM_IMAGES) imageNumber = 0;
  imageInit();
}

// MAIN LOOP ---------------------------------------------------------------

void loop() {
  uint32_t t = millis();               // Current time, milliseconds

  if(autoCycle) {
    if((t - lastImageTime) >= (CYCLE_TIME * 1000L)) nextImage();
    // CPU clocks vary slightly; multiple poi won't stay in perfect sync.
    // Keep this in mind when using auto-cycle mode, you may want to cull
    // the image selection to avoid unintentional regrettable combinations.
  }
#ifdef SELECT_PIN
  if(digitalRead(SELECT_PIN)) {        // Image select?
    debounce = 0;                      // Not pressed -- reset counter
  } else {                             // Pressed...
    if(++debounce >= 25) {             // Debounce input
      nextImage();                     // Switch to next image
      while(!digitalRead(SELECT_PIN)); // Wait for release
      // If held 1+ sec, toggle auto-cycle mode on/off
      if((millis() - t) >= 1000L) autoCycle = !autoCycle;
      debounce = 0;
    }
  }
#endif

  switch(imageType) {

    case PALETTE1: { // 1-bit (2 color) palette-based image
      uint8_t  pixelNum = 0, byteNum, bitNum, pixels, idx,
              *ptr = (uint8_t *)&imagePixels[imageLine * NUM_LEDS / 8];
      for(byteNum = NUM_LEDS/8; byteNum--; ) { // Always padded to next byte
        pixels = pgm_read_byte(ptr++);  // 8 pixels of data (pixel 0 = LSB)
        for(bitNum = 8; bitNum--; pixels >>= 1) {
          idx = pixels & 1; // Color table index for pixel (0 or 1)
          strip.setPixelColor(pixelNum++,
            palette[idx][0], palette[idx][1], palette[idx][2]);
        }
      }
      break;
    }

    case PALETTE4: { // 4-bit (16 color) palette-based image
      uint8_t  pixelNum, p1, p2,
              *ptr = (uint8_t *)&imagePixels[imageLine * NUM_LEDS / 2];
      for(pixelNum = 0; pixelNum < NUM_LEDS; ) {
        p2  = pgm_read_byte(ptr++); // Data for two pixels...
        p1  = p2 >> 4;              // Shift down 4 bits for first pixel
        p2 &= 0x0F;                 // Mask out low 4 bits for second pixel
        strip.setPixelColor(pixelNum++,
          palette[p1][0], palette[p1][1], palette[p1][2]);
        strip.setPixelColor(pixelNum++,
          palette[p2][0], palette[p2][1], palette[p2][2]);
      }
      break;
    }

  }

  strip.show(); // Refresh LEDs

delayMicroseconds(900);
if(++imageLine >= imageLines) imageLine = 0; // Next scanline, wrap around

}

И файл fire.h:

// Don't edit this file!  It's software-generated.
// See convert.py script instead.

#define PALETTE1  0
#define PALETTE4  1
#define PALETTE8  2
#define TRUECOLOR 3

#define NUM_LEDS 16

// fire.gif ----------------------------------------------------------------

const uint8_t PROGMEM palette00[][3] = {
  {  88,  80,   3 },
  {  88,  88,  23 },
  {  88,  34,   0 },
  {  88,   4,   0 },
  {  88,  49,   0 },
  {  88,   9,   0 },
  {   0,   0,   0 },
  {  88,  88,  10 },
  {  88,  17,   0 },
  {  16,   0,   0 },
  {  88,   1,   0 },
  {  88,   0,   0 },
  {  88,  68,   1 },
  {  33,   0,   0 },
  {   1,   0,   0 },
  {   7,   0,   0 } };

const uint8_t PROGMEM pixels00[] = {
  0X66, 0X6E, 0X95, 0XC7, 0X05, 0XD9, 0XEB, 0XE6,
  0X66, 0X6E, 0X93, 0XCC, 0X4A, 0X9F, 0X9B, 0XE6,
  0X66, 0X66, 0X93, 0X4C, 0X8D, 0X99, 0XB9, 0XE6,
  0X66, 0X6E, 0XD3, 0X42, 0X39, 0X9B, 0XB9, 0XE6,
  0X66, 0X6F, 0XD5, 0X88, 0XD9, 0X9B, 0XB9, 0XE6,
  0X66, 0X6F, 0XB8, 0X23, 0XDF, 0X9B, 0XBD, 0XE6,
  0X66, 0XE9, 0XA8, 0X2A, 0X9E, 0X9B, 0XBD, 0XF6,
  0X66, 0XE9, 0X32, 0X8B, 0XFE, 0X9B, 0XAB, 0XDE,
  0X66, 0XED, 0X52, 0X5D, 0XE6, 0XFB, 0XA3, 0XB9,
  0X66, 0XFB, 0X22, 0X59, 0XE6, 0XED, 0XA3, 0XAD,
  0X66, 0X9A, 0X24, 0X39, 0X66, 0XEF, 0XA3, 0X3B,
  0X6E, 0XF3, 0X24, 0X39, 0XE6, 0X6F, 0XD3, 0X3B,
  0X66, 0X93, 0X44, 0X39, 0XE6, 0X6E, 0X93, 0X3A,
  0X6E, 0X93, 0X44, 0X59, 0X66, 0X6E, 0X93, 0X5A,
  0X6E, 0X93, 0X2C, 0X59, 0XE6, 0X6E, 0XD3, 0X5A,
  0X66, 0X9A, 0X44, 0X8D, 0XF6, 0XEF, 0XA8, 0X8A,
  0X66, 0XFA, 0X2C, 0X45, 0X9F, 0XFD, 0X52, 0X8B,
  0X66, 0XED, 0X8C, 0X04, 0X5D, 0XD8, 0X44, 0X3D,
  0X66, 0XE9, 0X3C, 0X77, 0XC4, 0X4C, 0XC4, 0XA9,
  0X66, 0X69, 0XAC, 0X71, 0X77, 0X77, 0XC5, 0XBF,
  0X66, 0XEF, 0XB2, 0X71, 0X11, 0X70, 0X2A, 0X9E,
  0X66, 0X6F, 0XD8, 0X01, 0X11, 0X74, 0XA9, 0XF6,
  0X66, 0X6E, 0X93, 0XC1, 0X17, 0XC3, 0XDE, 0X66,
  0X6E, 0X6E, 0XFA, 0X47, 0X17, 0X2D, 0XF6, 0X66,
  0X6E, 0XBF, 0XFD, 0X50, 0X7C, 0X89, 0XE6, 0X66,
  0X6E, 0XBF, 0XFD, 0XA4, 0XCC, 0X39, 0XE6, 0X66,
  0X6E, 0XFB, 0X9F, 0XD8, 0X44, 0X39, 0XE6, 0X66,
  0X6E, 0X9B, 0XBD, 0X93, 0X22, 0X39, 0XE6, 0X66,
  0X6E, 0XFB, 0XB9, 0X9D, 0X52, 0X5D, 0XE6, 0X66,
  0X6E, 0XDB, 0XB9, 0XF9, 0X32, 0X8A, 0XF6, 0X66,
  0X6F, 0XDB, 0XB9, 0XFF, 0XA2, 0X8A, 0X9E, 0X66,
  0XED, 0XBA, 0XB9, 0XE9, 0XB8, 0X23, 0X9E, 0X66,
  0X9B, 0XA3, 0XBF, 0XEE, 0XD5, 0X25, 0XDF, 0X66,
  0X9A, 0X3A, 0XDF, 0X6E, 0X93, 0X28, 0XBF, 0X66,
  0XB3, 0X5B, 0X96, 0X66, 0X93, 0X22, 0XAF, 0XE6,
  0XB3, 0X3D, 0XE6, 0X6E, 0X93, 0X24, 0X39, 0XE6,
  0XA3, 0X39, 0XE6, 0X66, 0X93, 0X42, 0X39, 0XE6,
  0XA5, 0X39, 0XE6, 0X6E, 0X93, 0X44, 0X39, 0XE6,
  0XA5, 0X3D, 0XE6, 0X66, 0XD5, 0X44, 0XA9, 0X66,
  0XA8, 0X8A, 0X96, 0X6F, 0XD2, 0X44, 0XAF, 0X66,
  0XB8, 0X25, 0XD9, 0XFD, 0X3C, 0XC8, 0XAF, 0XE6,
  0XD5, 0X44, 0X5D, 0XD3, 0XC0, 0X08, 0XDE, 0X66,
  0X9A, 0X20, 0XC4, 0X4C, 0X77, 0XC3, 0XDE, 0X66,
  0XFB, 0X8C, 0X77, 0X71, 0X71, 0X43, 0XFE, 0X66,
  0XE9, 0XA2, 0X01, 0X11, 0X17, 0X4B, 0XF6, 0X66,
  0X6E, 0XDA, 0X47, 0X11, 0X17, 0X8D, 0XF6, 0X66,
  0X66, 0XE9, 0X3C, 0X11, 0X1C, 0X39, 0XE6, 0X66,
  0X66, 0X6F, 0XD2, 0X71, 0X74, 0XAF, 0XEE, 0X66 };

typedef struct {
  uint8_t        type;    // PALETTE[1,4,8] or TRUECOLOR
  line_t         lines;   // Length of image (in scanlines)
  const uint8_t *palette; // -> PROGMEM color table (NULL if truecolor)
  const uint8_t *pixels;  // -> Pixel data in PROGMEM
} image;

const image PROGMEM images[] = {
  { PALETTE4 ,   48, (const uint8_t *)palette00, pixels00 }
};

#define NUM_IMAGES (sizeof(images) / sizeof(images[0]))

Код ошибки: (я добавил информацию Serial.Print перед каждой строкой кода, так что я знаю, что что-то есть с memcpy_P)

Loading pgm_read_byte... OK ! 
Loading pgm_read_word... OK ! 
Loading PALETTE1 memcpy_P... 
Exception (28):
epc1=0x40202fda epc2=0x00000000 epc3=0x00000000 excvaddr=0x000078ec depc=0x00000000

ctx: cont 
sp: 3ffef830 end: 3ffefa40 offset: 01a0

>>>stack>>>
3ffef9d0:  3ffe8910 3ffee7d0 00000001 402022d6  
3ffef9e0:  00000000 00000001 3ffee9ec 3ffe8910  
3ffef9f0:  3ffee7c4 3ffe88ec 3ffee9ec 40201ff9  
3ffefa00:  0001c200 0000001c 3ffee7d0 3ffeea10  
3ffefa10:  3fffdad0 00000000 3ffee7d0 40202051  
3ffefa20:  feefeffe feefeffe 3ffeea08 40202d60  
3ffefa30:  feefeffe feefeffe 3ffeea20 4010070c  
<<<stack<<<

Декодированная ошибка с AVR декодером:

Exception 28: LoadProhibited: A load referenced a page mapped with an attribute that does not permit loads
Decoding 6 results
0x40202fda: pgm_read_byte_inlined at C:\Users\Rafal\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.4.0-rc2\cores\esp8266/pgmspace.h line 104
0x402022d6: Adafruit_DotStar::sw_spi_out(unsigned char) at D:\Dropbox\ArduinoLib\libraries\Adafruit_DotStar/Adafruit_DotStar.cpp line 40
0x40201ff9: imageInit() at D:\Dropbox\ArduinoLib\sketch_nov16b/sketch_nov16b.ino line 167
0x40202051: setup at D:\Dropbox\ArduinoLib\sketch_nov16b/sketch_nov16b.ino line 125
0x40202d60: loop_wrapper at C:\Users\Rafal\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.4.0-rc2\cores\esp8266/core_esp8266_main.cpp line 57
0x4010070c: cont_norm at C:\Users\Rafal\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.4.0-rc2\cores\esp8266/cont.S line 109

КСТАТИ. Я пробовал несколько версий плат 2.3.0, 2.4.0 RC1 и RC2.

1 ответ

Мне удалось получить код Adafruit, который вы опубликовали выше, для работы с ESP8266, удалив все экземпляры PROGMEM из скетча. Просто используйте функцию найти и заменить в Arduino и удалить все программы и заменить ничем. Затем замените imageinit на приведенный ниже из эскиза сверхновой. Он работает, но сохраняет все шаблоны в рабочей памяти, а не во флэш-памяти. Сейчас я пытаюсь сохранить шаблоны во флэш-памяти, чтобы можно было сохранить больше шаблонов.

void imageInit() { // Initialize global image state for current imageNumber
  imageType    = images[imageNumber].type;
  imageLines   = images[imageNumber].lines;
  imageLine    = 0;
  imagePalette = (uint8_t *)images[imageNumber].palette;
  imagePixels  = (uint8_t *)images[imageNumber].pixels;
  // 1- and 4-bit images have their color palette loaded into RAM both for
  // faster access and to allow dynamic color changing.  Not done w/8-bit
  // because that would require inordinate RAM (328P could handle it, but
  // I'd rather keep the RAM free for other features in the future).
  if(imageType == PALETTE1)      memcpy_P(palette, imagePalette,  2 * 3);
  else if(imageType == PALETTE4) memcpy_P(palette, imagePalette, 16 * 3);
  lastImageTime = millis(); // Save time of image init for next auto-cycle
}
Другие вопросы по тегам