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 功能到内存中要求的那样。其实也是有必要的。
感谢您的帮助!
我正在尝试用 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 功能到内存中要求的那样。其实也是有必要的。
感谢您的帮助!