在 PROGMEM 中添加更多数据会中断 Arduino Mega 2560 上的 SPI 传输
Adding more data in PROGMEM breaks SPI transfer on Arduino Mega 2560
我正在从事一个涉及 Arduino Mega (2560) 和 Waveshare ePaper 显示器的小项目(或者我认为如此)。
我已经让它与库 (epd7in5) 一起正常工作,并且我已经在 PROGMEM
中添加了两个图像。但是一旦我添加了第三张图片(因此 PROGMEM
中的第三个条目),电子纸屏幕就不会再启动了。在库中添加一些调试显示代码卡在特定的 SPI.transfer()
.
编辑:理论
有没有可能是flash数据太多的时候SPI不兼容?我已经读过 64kb 是最大值。我有两张图片略高于此,但三张图片明显如此。这可能会破坏 SPI 吗?如果是这样:我可以修复它吗?
我添加了下面的代码以及 SPI.transfer()
失败的库的特定部分。
Main.cpp
删除与 dummy3 相关的代码可确保 dummy3 数组无法编译。仅使用 dummy1 和 dummy2 一切正常。添加 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
我删除了实际的图像数据,因为它太多了。
两个图像导致总闪存为 67326 字节(约占总闪存 2560 的 26%)。
三个图像导致总闪存为 98052 字节(约占总闪存 2560 的 38%)。
头文件仅包含声明。
#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
我添加了调试功能。 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
这是没有继续的SPI传输部分。
void EpdIf::SpiTransfer(unsigned char data) {
digitalWrite(CS_PIN, LOW);
SPI.transfer(data);
digitalWrite(CS_PIN, HIGH);
}
项目的串口打印如下...
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);
因此,一旦它运行 SpiTransfer
,代码就会停止工作。它似乎在 SPI.transfer();
中处于无限循环中,但我不知道那会如何发生。我看不出 PROGMEM
会如何干扰传输,而且我还有足够的闪存...
有什么办法可以解决这个问题?我需要更改的是 SPI 中的问题吗?或者我是否需要在 PROGMEM
中以不同方式存储我的数据?我有点不知所措。
在此先感谢您的帮助,非常感谢。
您的问题不在于 SPI(本身)。
这与你在progmem中的数据量和阅读量有关。如果您使用超过 64k 的 Progmem,您 运行 会遇到两组不同的问题:
- 以可靠的方式读取数据
阅读程序应该使用 pgm_read_xxx_far
宏
65536 之后的地址。Epd::DisplayFrame
使用
pgm_read_byte(&frame_buffer[i]);
所以你在
图书馆级别。我不熟悉这个图书馆,所以我不确定
如果有一个替代函数可以调用,并提供
缓冲区自己,从 PROGMEM 中读取后
pgm_read_byte_far
。这是问题的简单部分
- 首先将上述数据导入 PROGMEM
Arduino 内核和 avr 编译器本身假设所有指针都只有 16 位。将数据放入 PROGMEM 使其出现在 运行 程序的最终可执行文件中的 arduino 核心和可执行代码之前。
正确执行此操作的可靠方法是使用自定义链接描述文件,这将使您的数据不受影响,并将其放在所有可执行代码之后。这会很难,恐怕我无法提供任何有关如何完成它的信息。
或者,您可以尝试使用 _attribute__((section(".fini2")))
来使用 .fini2
部分。有些人以前做过,并且对他们有用。
你会这样使用它:
const unsigned char __attribute__((section(".fini2"))) dummy1[30726] = {...data...};
最后的选择是根本不使用 PROGMEM,并使用某种外部存储来存储数据(例如一些 SDCard)。这很可能是解决该问题的最简单方法。
Source:Arduino 论坛上 westfw 的精彩帖子和精彩见解:
https://forum.arduino.cc/index.php?topic=485507.0
我正在从事一个涉及 Arduino Mega (2560) 和 Waveshare ePaper 显示器的小项目(或者我认为如此)。
我已经让它与库 (epd7in5) 一起正常工作,并且我已经在 PROGMEM
中添加了两个图像。但是一旦我添加了第三张图片(因此 PROGMEM
中的第三个条目),电子纸屏幕就不会再启动了。在库中添加一些调试显示代码卡在特定的 SPI.transfer()
.
编辑:理论 有没有可能是flash数据太多的时候SPI不兼容?我已经读过 64kb 是最大值。我有两张图片略高于此,但三张图片明显如此。这可能会破坏 SPI 吗?如果是这样:我可以修复它吗?
我添加了下面的代码以及 SPI.transfer()
失败的库的特定部分。
Main.cpp
删除与 dummy3 相关的代码可确保 dummy3 数组无法编译。仅使用 dummy1 和 dummy2 一切正常。添加 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
我删除了实际的图像数据,因为它太多了。 两个图像导致总闪存为 67326 字节(约占总闪存 2560 的 26%)。 三个图像导致总闪存为 98052 字节(约占总闪存 2560 的 38%)。 头文件仅包含声明。
#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
我添加了调试功能。 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
这是没有继续的SPI传输部分。
void EpdIf::SpiTransfer(unsigned char data) {
digitalWrite(CS_PIN, LOW);
SPI.transfer(data);
digitalWrite(CS_PIN, HIGH);
}
项目的串口打印如下...
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);
因此,一旦它运行 SpiTransfer
,代码就会停止工作。它似乎在 SPI.transfer();
中处于无限循环中,但我不知道那会如何发生。我看不出 PROGMEM
会如何干扰传输,而且我还有足够的闪存...
有什么办法可以解决这个问题?我需要更改的是 SPI 中的问题吗?或者我是否需要在 PROGMEM
中以不同方式存储我的数据?我有点不知所措。
在此先感谢您的帮助,非常感谢。
您的问题不在于 SPI(本身)。
这与你在progmem中的数据量和阅读量有关。如果您使用超过 64k 的 Progmem,您 运行 会遇到两组不同的问题:
- 以可靠的方式读取数据
阅读程序应该使用 pgm_read_xxx_far
宏
65536 之后的地址。Epd::DisplayFrame
使用
pgm_read_byte(&frame_buffer[i]);
所以你在
图书馆级别。我不熟悉这个图书馆,所以我不确定
如果有一个替代函数可以调用,并提供
缓冲区自己,从 PROGMEM 中读取后
pgm_read_byte_far
。这是问题的简单部分
- 首先将上述数据导入 PROGMEM
Arduino 内核和 avr 编译器本身假设所有指针都只有 16 位。将数据放入 PROGMEM 使其出现在 运行 程序的最终可执行文件中的 arduino 核心和可执行代码之前。
正确执行此操作的可靠方法是使用自定义链接描述文件,这将使您的数据不受影响,并将其放在所有可执行代码之后。这会很难,恐怕我无法提供任何有关如何完成它的信息。
或者,您可以尝试使用 _attribute__((section(".fini2")))
来使用 .fini2
部分。有些人以前做过,并且对他们有用。
你会这样使用它:
const unsigned char __attribute__((section(".fini2"))) dummy1[30726] = {...data...};
最后的选择是根本不使用 PROGMEM,并使用某种外部存储来存储数据(例如一些 SDCard)。这很可能是解决该问题的最简单方法。
Source:Arduino 论坛上 westfw 的精彩帖子和精彩见解: https://forum.arduino.cc/index.php?topic=485507.0