通过单个 GPIO 引脚转储闪存
Posted
技术标签:
【中文标题】通过单个 GPIO 引脚转储闪存【英文标题】:Dump Flash Memory through a single GPIO pin 【发布时间】:2014-07-21 15:15:32 【问题描述】:我正在使用英飞凌的 XMC4500 放松套件,并尝试通过单个 GPIO 引脚提取固件。
我非常幼稚的想法是通过 GPIO 引脚一次转储一位,然后用逻辑分析仪以某种方式“嗅探”数据。
伪代码:
while(word by word memory copy hasn't finished)
...
register = value;
temp_value = value AND 0x1;
pin = temp_value;
value = value >> 1;
...
我在正确的轨道上吗?有没有人有更好/更好的想法如何存档?
### 编辑###
实际上,我的(shell)代码的要求是它需要非常小。我发现了this 关于如何 通过闪烁 LED 转储固件。
但是,我很难使用 Saleae Logic Analyzer 接收正确的值。
基本上我在做的是:
-
将 GPIO 引脚方向设置为输出
用时钟(SPI 串行时钟)闪烁 LED1(引脚 1.1)
使用数据位 (SPI MOSI) 闪烁 LED2(引脚 1.0)
用逻辑分析仪嗅探引脚
这是我的 C 代码:
#include "XMC4500.h"
#define DEL 1260
void init()
// P1.0 output, push pull
PORT1->IOCR0 = 0x80UL << 0;
// P1.1 output, push pull
PORT1->IOCR0 |= 0x80UL << 8;
void delay(int i)
while(--i)
asm("nop\n");
asm("nop\n");
// Sets a pin to high
// P1.0 = SPI MOSI
// P1.1 = SPI CLOCK
void output_high(int i)
// P1.0 high
if(i == 0)
PORT1->OUT |= 0x1UL;
// P1.1 high
if(i == 1)
PORT1->OUT |= 0x2UL;
// Sets a pin to low
// P1.0 = SPI MOSI
// P1.1 = SPI CLOCK
void output_low(int i)
// P1.0 low
if(i == 0)
PORT1->OUT &= (~0x1UL);
// P1.1 low
if(i == 1)
PORT1->OUT &= (~0x2UL);
// SPI bit banging
void spi_send_byte(unsigned char data)
int i;
// Send bits 7..0
for (i = 0; i < 8; i++)
// Sets P1.1 to low (serial clock)
output_low(1);
// Consider leftmost bit
// Set line high if bit is 1, low if bit is 0
if (data & 0x80)
// Sets P1.0 to high (MOSI)
output_high(0);
else
// Sets P1.0 to low (MOSI)
output_low(0);
delay(DEL);
// Sets P1.1 to high (Serial Clock)
output_high(1);
// Shift byte left so next bit will be leftmost
data <<= 1;
int main()
init();
while(1)
spi_send_byte('t');
spi_send_byte('e');
spi_send_byte('s');
spi_send_byte('t');
return 0;
###第二次编辑###
使用以下代码转储闪存可以正常工作:
#include "XMC4500.h"
// SPI bit banging
void spi_send_word(uint32_t data)
int i;
// LSB first, 32 bits per transfer
for (i = 0; i < 32; i++)
// set pin 1.1 to low (SPI clock)
PORT1->OUT &= (~0x2UL);
// set line high if bit is 1, low if bit is 0
if (data & 0x1)
// set pin 1.0 to high (SPI MOSI)
PORT1->OUT |= 0x1UL;
else
// set pin 1.0 to low (SPI MOSI)
PORT1->OUT &= (~0x1UL);
// set pin 1.1 to high (SPI clock)
PORT1->OUT |= 0x2UL;
data >>= 1;
int main()
// start dumping at memory address 0x08000000
unsigned int *p;
p = (uint32_t *)(0x08000000u);
// configure pin 1.0 and pin 1.1 as output (push-pull)
PORT1->IOCR0 = 0x8080UL;
while(1)
spi_send_word(*p);
p++;
【问题讨论】:
你能发布单个字符的可视化分析图吗?如果您要进行定制处理,您可能会发现 sigrok 比 salae 的前端更好。就个人而言,我和 Clifford 一起建议您发送普通异步串行数据,您可以使用串行端口或便宜的 USB 串行转换器捕获这些数据 - 您可能仅传输到仅期望 RS232 级别的数据中通过反转传输数据的感觉,就像线路驱动器一样,甚至只发送 3.3v 或 5v 信号。 最后我通过一个引脚成功地转储了内存。请参阅上面的工作代码。 【参考方案1】:您的解决方案的最大问题是恢复时间信息 - 知道一个单词从哪里开始,另一个单词在哪里结束。在 UART tx 引脚上输出数据会更简单 - UART 添加开始和停止位并为您管理时序,并且可以通过常规 PC 串行端口直接读取输出。
如果您不能使用 UART,则通过使用 UART 时序对 GPIO 进行 bit-banging 来模拟 UART 仍将允许使用传统串行端口直接接收数据。
软件 UART 实现示例可以在 here 找到。在您的情况下,您当然只需要传输功能。
【讨论】:
【参考方案2】:根据您的要求,这可能会很好地工作。需要考虑的是,在循环数据时是否存在任何时序变化,例如闪存读取时间变化、缓存内容等,您将遇到一个问题,以确定字节的开始和停止位置。您可能想看看 1-Wire 协议:
http://en.wikipedia.org/wiki/1-Wire
您不必将其实现为规范或任何东西,只需查看它的想法即可。如果你实现类似的东西,你的逻辑也很简单:
while(word by word memory copy hasn't finished)
...
register = value;
temp_value = value AND 0x1;
one_wire_send(temp_value);
value = value >> 1;
...
【讨论】:
以上是关于通过单个 GPIO 引脚转储闪存的主要内容,如果未能解决你的问题,请参考以下文章
ESP32/8266利用SPIFFS(闪存文件系统)创建 Web服务器实现引脚控制