通过单个 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服务器实现引脚控制

通过应用程序跳转到 STM32 中的引导加载程序,即在引导模式下从用户闪存使用引导 0 和引导 1 引脚

W25Q128 闪存芯片SPI详解

arduino 闪存阵列

Nand闪存芯片; x8 和 x16 位的 I/O 宽度?

ESP8266通过程序向闪存文件系统文件添加信息