STM32F4:通过SPI管理EEPROM 25LC256

STM32F4 : EEPROM 25LC256 management through SPI

我正在尝试用 STM32F469I-DISCO 驱动 EEPROM Chip 25LC256 但无法实现。 我曾尝试使用 HAL API 基创建我自己的函数,但显然出了点问题:我不知道我是否在芯片上写入数据,因为我无法读取它。让我再解释一下。

所以我的芯片是DIP 25LC256(DS在上面是你想要的)。 EEPROM 的 PIN HOLD 和 WP 连接到 VCC (3.3V)。 PIN CS 连接到 PH6(板载 ARD_D10)并由软件管理。 PIN SI 和 PIN SO 分别连接到具有正确复用功能(GPIO_AF5_SPI2)的 PB15(ARD_D11)和 PB14(ARD_D12)。 PIN SCK 也连接到 PD3 (ADR_D13)。

这是我的 SPI 配置代码:

EEPROM_StatusTypeDef ConfigurationSPI2(SPI_HandleTypeDef *spi2Handle){

  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();

  GPIO_InitTypeDef  gpioInit;

  ////  SCK [PD3]
  gpioInit.Pin =    GPIO_PIN_3;
  gpioInit.Mode =   GPIO_MODE_AF_PP;
  gpioInit.Pull =   GPIO_PULLDOWN;
  gpioInit.Speed =  GPIO_SPEED_FREQ_HIGH;
  gpioInit.Alternate = GPIO_AF5_SPI2;

  HAL_GPIO_Init(GPIOD, &gpioInit);

  //// MOSI [PB15]
  gpioInit.Pin =    GPIO_PIN_15;
  gpioInit.Pull =   GPIO_PULLUP;
  HAL_GPIO_Init(GPIOB, &gpioInit);

  //// MISO [PB14]
  gpioInit.Pin =    GPIO_PIN_14;
  gpioInit.Pull =   GPIO_NOPULL;
  HAL_GPIO_Init(GPIOB, &gpioInit);

  //// CS [PH6]
  gpioInit.Pin =    GPIO_PIN_6;
  gpioInit.Mode =   GPIO_MODE_OUTPUT_PP;
  gpioInit.Speed =  GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOH, &gpioInit);
  HAL_GPIO_WritePin(GPIOH, GPIO_PIN_6, GPIO_PIN_SET);

  //// SPI2

  __HAL_RCC_SPI2_CLK_ENABLE();

  spi2Handle->Instance = SPI2;

  spi2Handle->Init.Mode =               SPI_MODE_MASTER;
  spi2Handle->Init.Direction =          SPI_DIRECTION_2LINES;
  spi2Handle->Init.DataSize =           SPI_DATASIZE_8BIT;
  spi2Handle->Init.CLKPolarity =        SPI_POLARITY_LOW;
  spi2Handle->Init.CLKPhase =           SPI_PHASE_1EDGE;
  spi2Handle->Init.NSS =                SPI_NSS_SOFT;
  spi2Handle->Init.BaudRatePrescaler =  SPI_BAUDRATEPRESCALER_16;
  spi2Handle->Init.FirstBit =           SPI_FIRSTBIT_MSB;
  spi2Handle->Init.TIMode =             SPI_TIMODE_DISABLE;
  spi2Handle->Init.CRCCalculation =     SPI_CRCCALCULATION_DISABLE ;
  spi2Handle->Init.CRCPolynomial =      7;

  if(HAL_SPI_Init(spi2Handle) != HAL_OK){
      return EEPROM_ERROR;
  }

  return EEPROM_OK;
}

还有两个函数分别允许(理论上)写入和读取芯片:

写入函数:

EEPROM_StatusTypeDef WriteEEPROM(SPI_HandleTypeDef *spi2Handle, uint8_t *txBuffer, uint16_t size, uint16_t addr){

    uint8_t addrLow = addr & 0xFF;
    uint8_t addrHigh = (addr >> 8);

    uint8_t wrenInstruction = WREN_EEPROM; // Value : 0x06

    uint8_t buffer[32] = {WRITE_EEPROM, addrHigh, addrLow}; //Value : 0x02

    for(uint i = 0 ; i < size ; i++){
        buffer[3+i] = txBuffer[i];
    }

    HAL_GPIO_WritePin(GPIOH, GPIO_PIN_6, RESET);
    if(HAL_SPI_Transmit(spi2Handle, &wrenInstruction, 1, TIMEOUT_EEPROM) != HAL_OK){
        return EEPROM_ERROR;;
    }
    HAL_GPIO_WritePin(GPIOH, GPIO_PIN_6, SET);

    HAL_GPIO_WritePin(GPIOH, GPIO_PIN_6, RESET);
    if(HAL_SPI_Transmit(spi2Handle, buffer, (size + 3), TIMEOUT_EEPROM) != HAL_OK){
        return EEPROM_ERROR;
    }
    HAL_GPIO_WritePin(GPIOH, GPIO_PIN_6, SET);

    return EEPROM_OK;
}

读取函数:

EEPROM_StatusTypeDef ReadEEPROM(SPI_HandleTypeDef *spi2Handle, uint8_t *rxBuffer, uint16_t size, uint16_t addr){

    uint8_t addrLow = addr & 0xFF;
    uint8_t addrHigh = (addr >> 8);
    uint8_t txBuffer[3] = {READ_EEPROM, addrHigh, addrLow};

    HAL_GPIO_WritePin(GPIOH, GPIO_PIN_6, RESET);

    HAL_SPI_Transmit(spi2Handle, txBuffer, 3, TIMEOUT_EEPROM);

    HAL_SPI_Receive(spi2Handle, rxBuffer, size, TIMEOUT_EEPROM);    

    HAL_GPIO_WritePin(GPIOH, GPIO_PIN_6, SET);

    return EEPROM_OK;
}

我知道我的功能不是很好 "beautiful" 但这是第一次尝试。总的来说,我首先尝试将 0x01 地址处的数据“0x05”写入芯片,然后再读回该数据:

uint8_t bufferEEPROM[1] = {5};
uint8_t bufferEEPROM2[1] = {1};

WriteEEPROM(&spi2Handle, bufferEEPROM, 1, 0x01);
ReadEEPROM(&spi2Handle, bufferEEPROM2, 1, 0x01);

我有一个示波器,所以因为它不工作(用 STM Studio 监控),所以我可视化了 CLK 和 SI PIN,然后是 CLK 和 SO PIN(只能同时看到两个通道):

如您所见,第一张图片以蓝色显示 CLK(黄色)和 SI(或 MOSI),我得到了所有预期的数据:WRite ENable 指令然后是 WRITE 指令。在 ADDRESS 之后,然后是 DATA

之后,读取功能开始。首先是 READ 指令和我要获取数据的 ADDRESS 指令。最后 8 位应该是存储在地址(在本例中为 0x01)的数据。 SI PIN 上发生了一些事情,但我猜这是因为 HAL_SPI_Receive() 函数实际上用我的数组 bufferEEPROM2[= 调用了 HAL_SPI_TransmitReceive() 58=] 作为参数(这就是我们可以使用 0b00000001 的原因)。所以这是因为我的 SPI 配置参数(全双工)。

无论如何,理论上我应该在 SO PIN 上看到 0b00000101,但正如您在第二张图片中看到的那样……什么都没有。

我曾尝试更改 gpioInit.Pull 上拉和下拉的 SO PIN,但没有任何改变。 NOPULL 是因为那是我最后尝试过的东西。

问题是我不知道从哪里开始。我的传输似乎有效(但实际上是这样吗?)。我的初始化有什么问题吗?实际上我的主要问题是:为什么我没有从我的 EEPROM 收到任何数据?

非常感谢!

写入操作需要一些时间才能完成(您的数据表在第 4 页上显示为 5 毫秒),在此期间,除了读取状态外,其他操作都是不可能的。尝试使用 RDSR (0x05) 操作码轮询状态寄存器以了解它何时准备就绪(位 0)。您还可以在发出 WREN 之前和之后检查状态(位 1)以查看它是否成功。

那么问题就解决了。以下是改进:

实际上有两个问题。正如 berendi 所说,第一个当然也是最重要的是时间问题。在我的 WRITE 函数中,我没有让 EEPROM 完成其写入周期的时间(数据表上为 5 毫秒)。我在所有 WRITE 函数的末尾添加了以下代码行:

HAL_Delay(10); //10 ms wait for the EEPROM to complete the write cycle

delay 值可能会更小我认为如果时间很短(理论上是 5 毫秒)。不过我没有测试低于 10 毫秒。另一件事。通过示波器,我还看到我的 芯片 Select 曾经在最后一个时钟边沿的中间达到 HIGH。我不能说这是否也意味着一些问题,因为这是我首先通过在 HAl_Delay(10) 之前添加代码行解决的问题。我所有的 SPI 传输 功能现在都以这种方式完成:

while(HAL_GPIO_ReadPin(CLK_PORT, CLK_PIN) == GPIO_PIN_SET){
}
HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET);
HAL_Delay(10);

这样我就有了正确的模式,我可以写入 EEPROM 并读回我写的内容。

注意:最后一件事让我对事件的误解更深了:由于我的写入功能不起作用,我专注于 STATUS REGISTER 写入和读取功能(为了逐步解决这个问题) .写入函数也不起作用,实际上是因为 WREN 位未设置。我虽然(错误的)写入状态寄存器的事实并没有要求也设置 WREN 就像 WRITE 功能到内存中要求的那样。其实也是有必要的。

感谢您的帮助!