简体   繁体   English

在 PROGMEM 中添加更多数据会破坏 Arduino Mega 2560 上的 SPI 传输

[英]Adding more data in PROGMEM breaks SPI transfer on Arduino Mega 2560

I'm working on a small project (or so I thought) involving an Arduino Mega (2560) and a Waveshare ePaper display.我正在做一个涉及 Arduino Mega (2560) 和 Waveshare ePaper 显示器的小项目(或者我认为)。

I've got it working alright with the library ( epd7in5 ) and I've added two images into PROGMEM .我已经让它与库( epd7in5 )一起正常工作,并且我已经将两个图像添加到PROGMEM But as soon as I add a third image (and thus a third entry into the PROGMEM ), somehow the ePaper screen doesn't initiate anymore.但是,一旦我添加了第三张图像(因此在PROGMEM添加了第三个条目),不知何故电子纸屏幕不再启动。 Adding some debugging in the library shows that the code gets stuck on a specific SPI.transfer() .在库中添加一些调试显示代码卡在特定的SPI.transfer()

EDIT: Theory Is it possible that SPI is not compatible when there's too much data in flash?编辑:理论当闪存中有太多数据时,SPI 是否可能不兼容? I've read up on it that 64kb is the max.我读过它,64kb 是最大值。 I'm slightly above that with two images, but significantly so with three.两张图片我略高于那个,但三张图片明显如此。 Could be that this breaks SPI?这可能会破坏SPI吗? And if so: can I fix it?如果是这样:我可以修复它吗?

I've added the code below and the specific part of the library where the SPI.transfer() fails.我添加了下面的代码以及SPI.transfer()失败的库的特定部分。

Main.cpp主程序

Removing the code related to dummy3 ensures that the dummy3 array doesn't compile.删除与 dummy3 相关的代码可确保 dummy3 数组不会编译。 Only using dummy1 and dummy2 everything works fine.仅使用 dummy1 和 dummy2 一切正常。 Adding dummy3 and the program gets stuck on epd.Init() .添加 dummy3 并且程序卡在epd.Init()

#include <SPI.h>
#include <epd7in5.h>
#include "imagedata.h"

Epd epd;

void debug(String);

void setup() {
  Serial.begin(9600);
  debug("Serial begin");

  if (epd.Init() != 0) {
    debug("INIT FAILED!");
    return;
  }

  debug("Changing image");
  epd.DisplayFrame(dummy1);  //DisplayFrame by default includes WaitUntilIdle.
  debug("dummy1 on ePaper");
  delay(1000);
  debug("Changing image");
  epd.DisplayFrame(dummy2);
  debug("dummy2 on ePaper");
  delay(1000);
  debug("Changing image");
  epd.DisplayFrame(dummy3);
  debug("dummy2 on ePaper");
  epd.SendCommand(POWER_OFF); 
  debug("POWER_OFF");
}

void loop() {
}

void debug(String message) {
  Serial.print(millis());
  Serial.print("\t");
  Serial.println(message);
}

imagedata.cpp图像数据文件

I've removed the actual image data as it's A LOT.我已经删除了实际的图像数据,因为它很多。 Two images result in a total flash of 67326 bytes (about 26% of total flash mem of 2560).两个图像的总闪存为 67326 字节(约占 2560 闪存总内存的 26%)。 Three images result in a total flash of 98052 bytes (about 38% of total flash mem of 2560).三个图像导致总闪存为 98052 字节(约占 2560 闪存总内存的 38%)。 Headerfile contains simply the declarations.头文件只包含声明。

#include "imagedata.h"
#include <avr/pgmspace.h>

const unsigned char dummy1[30726] PROGMEM = {...data...};
const unsigned char dummy2[30726] PROGMEM = {...data...};
const unsigned char dummy3[30726] PROGMEM = {...data...};

epd7in5.cpp epd7in5.cpp

I've added the debug function.我已经添加了调试功能。 SendData is also included and uses the debug as well. SendData 也包括在内并使用调试。

void Epd::debug(String message) {
  Serial.print(millis());
  Serial.print("\t");
  Serial.print("EPD");
  Serial.print("\t");
  Serial.println(message);
}

int Epd::Init(void) {
  if (IfInit() != 0) {
    return -1;
  }

  debug("Resetting");
  Reset();

  debug("SendCommand(POWER_SETTING);");
  SendCommand(POWER_SETTING);
  debug("SendData(0x37);");
  SendData(0x37);
  debug("SendData(0x00);");
  SendData(0x00);

  debug("SendCommand(PANEL_SETTING);");
  SendCommand(PANEL_SETTING);
  SendData(0xCF);
  SendData(0x08);

  SendCommand(BOOSTER_SOFT_START);
  SendData(0xc7);
  SendData(0xcc);
  SendData(0x28);

  SendCommand(POWER_ON);
  WaitUntilIdle();

  SendCommand(PLL_CONTROL);
  SendData(0x3c);

  SendCommand(TEMPERATURE_CALIBRATION);
  SendData(0x00);

  SendCommand(VCOM_AND_DATA_INTERVAL_SETTING);
  SendData(0x77);

  SendCommand(TCON_SETTING);
  SendData(0x22);

  SendCommand(TCON_RESOLUTION);
  SendData(0x02);  //source 640
  SendData(0x80);
  SendData(0x01);  //gate 384
  SendData(0x80);

  SendCommand(VCM_DC_SETTING);
  SendData(0x1E);  //decide by LUT file

  SendCommand(0xe5);  //FLASH MODE
  SendData(0x03);

  return 0;
}

void Epd::SendData(unsigned char data) {
  debug("DigitalWrite(dc_pin, HIGH);");
  DigitalWrite(dc_pin, HIGH);
  debug("SpiTransfer(data);");
  SpiTransfer(data);
}

epdif.cpp文件

This is the SPI transfer part that doesn't continue.这是不继续的SPI传输部分。

void EpdIf::SpiTransfer(unsigned char data) {
    digitalWrite(CS_PIN, LOW);
    SPI.transfer(data);
    digitalWrite(CS_PIN, HIGH);
}

The serial print of the project is as follows...项目的连载打印如下...

0   Serial begin
0   EPD Resetting
400 EPD SendCommand(POWER_SETTING);
400 EPD SendData(0x37);
400 EPD DigitalWrite(dc_pin, HIGH);
435 EPD SpiTransfer(data);

So, as soon as it runs SpiTransfer the code simply stops working.因此,只要它运行SpiTransfer ,代码就会停止工作。 It seems it is in an infinite loop within SPI.transfer();似乎它在SPI.transfer();处于无限循环中SPI.transfer(); but I don't know exactly how that would happen.但我不知道这会如何发生。 I don't see how PROGMEM could interfere with the transfer and I have enough flash memory left...我看不出PROGMEM如何干扰传输,而且我还有足够的闪存...

What can be solution for this?什么可以解决这个问题? Is it a problem in SPI that I need to change?我需要更改 SPI 中的问题吗? Or do I need to store my data differently in PROGMEM ?还是我需要在PROGMEM以不同的方式存储我的数据? I am a bit at a loss.我有点不知所措。

Thanks in advance for your help, greatly appreciated.在此先感谢您的帮助,不胜感激。

Your problem is not with SPI (itself).您的问题不在于 SPI(本身)。

It is related to the amount of data you have in progmem and reading it.它与您在程序中拥有和阅读的数据量有关。 If you are using more than 64k of Progmem you run into 2 different sets of problems:如果您使用超过 64k 的 Progmem,您会遇到 2 组不同的问题:

  1. Reading the data in a reliable way以可靠的方式读取数据

Reading the progmem should use the pgm_read_xxx_far macros for any address after 65536. Epd::DisplayFrame uses pgm_read_byte(&frame_buffer[i]);读取程序应该对 65536 之后的任何地址使用pgm_read_xxx_farEpd::DisplayFrame使用pgm_read_byte(&frame_buffer[i]); so you have a problem on the library level.所以你在图书馆层面有问题。 I'm not familiar with this library, so I'm not sure if there's an alternative function that you can call, and provide the buffer yourself, after reading it from PROGMEM with pgm_read_byte_far .我不熟悉这个库,所以我不确定在从 PROGMEM 使用pgm_read_byte_far从 PROGMEM 读取它之后,是否有可以调用的替代函数,并自己提供缓冲区。 This is the easy part of the problem这是问题的简单部分

  1. Getting said data into PROGMEM in the first place首先将所述数据放入 PROGMEM

The Arduino core, and the avr compiler themselves assume that all pointers are only 16 bits. Arduino 核心和 avr 编译器本身假定所有指针都只有 16 位。 Putting data into PROGMEM makes it appear before the arduino core and executable code in the final executable that runs your program.将数据放入 PROGMEM 使其出现在运行程序的最终可执行文件中的 arduino 核心和可执行代码之前。

The reliable way to get this going correctly is with a custom linker script, that will get your data out of the way and place it after all executable code.正确执行此操作的可靠方法是使用自定义链接器脚本,这将使您的数据不受影响,并将其放置在所有可执行代码之后。 This would be hard, and I'm afraid I can't give any info on how to accomplish that.这将很难,而且恐怕我无法提供有关如何实现这一目标的任何信息。

Alternatively, you can try to use _attribute__((section(".fini2"))) to use the .fini2 section.或者,您可以尝试使用_attribute__((section(".fini2")))来使用.fini2部分。 Some people have done it before, and it has worked for them.有些人以前做过,而且对他们有用。

You would use it like that:你会这样使用它:

const unsigned char __attribute__((section(".fini2"))) dummy1[30726] = {...data...};

The last alternative would be to not use PROGMEM at all, and have some kind of external storage that you use for your data (eg some SDCard).最后一种选择是根本不使用 PROGMEM,并使用某种外部存储来存储数据(例如某些 SDCard)。 This would most probably be the easiest way to tackle that issue.这很可能是解决该问题的最简单方法。

Source : This excellent thread and the wonderfull insight by westfw on the arduino forums: https://forum.arduino.cc/index.php?topic=485507.0来源:这个优秀的帖子和westfw在 arduino 论坛上的精彩见解: https : //forum.arduino.cc/index.php ?topic= 485507.0

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

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