1

我正在尝试从外部EEPROM写入和读取。有一个起始位 (SB),后跟一个操作码,然后是一个 6 位地址,然后是实际数据。我已将 SB 和操作码组合成一个字节,我可以将其作为开始条件发送。我能够启用、擦除然后写入 EEPROM。我假设这是有效的,因为 HAL 函数返回 HAL_OK 并且我可以在示波器上看到有效波形。

我似乎无法做的是读回数据。对于 READ 操作,我在示波器上看不到任何波形。所需的时钟周期数是奇数,而不是 8 的倍数。我不知道如何发送奇数时钟周期,因为所有数据都是 8、16 或 32 位。在需要 25 或 29 个时钟周期的地方,我似乎发送 32 个,而在所需周期为 9 个的地方,我似乎发送 16 个。我真的希望避免按照该线程中的建议进行位碰撞。

这是主要代码:

int main(void)
{
  HAL_Init();
  MX_GPIO_Init();
  MX_SPI1_Init();
  __HAL_SPI_ENABLE(&hspi1);

  // pull the CS pin high to select the EEPROM (active HIGH)
  HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET);
  HAL_Delay(10);

  // Enable the EEPROM
  enable_status = Enable_EEPROM(&EEPROM_SPI_PORT);
  HAL_Delay(10);

  // Erase the value at address 0x00
  erase_status = Erase_EEPROM(&EEPROM_SPI_PORT, addr);
  HAL_Delay(10);

  // Write data 0xABCD at addr 0x00
  write_status = Write_EEPROM(&EEPROM_SPI_PORT, addr, tx_data);
  HAL_Delay(10);

  // Disabling the EEPROM (with an EWDS) after a WRITE as described in the datasheet
  disable_status = Disable_EEPROM(&EEPROM_SPI_PORT);
  HAL_Delay(10);

  // Re-enabling it
  enable_status = Enable_EEPROM(&EEPROM_SPI_PORT);
  HAL_Delay(10);

  // Read from the EEPROM. This part isn't working.
  read_status = Read_EEPROM(&EEPROM_SPI_PORT, addr, rx_data);
  HAL_Delay(10);

  // Pull the CS pin low to deselect the chip again.
  HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET);


  while (1)
  {

  }
}

SPI 初始化为处理 16 位数据值

SPI_HandleTypeDef hspi1;

/* SPI1 init function */
void MX_SPI1_Init(void)
{

  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

这些是 EEPROM 功能

#define ERASE   0x07 // erase specific memory location. This is followed by the 8-bit address and then by the 16-bit data.
#define READ    0x06 // read the memory location.
#define WRITE   0x05 // write to the memory location

#define EEPROM_SPI_PORT hspi1
extern SPI_HandleTypeDef EEPROM_SPI_PORT;


//Enable the EEPROM
//Accepts: SPI handle
//Returns: Success or failure of the enable operation
uint8_t Enable_EEPROM (SPI_TypeDef *spi_handle) {
    uint16_t ewen = (0x04 << 8) | 0b00110000;
    if (HAL_SPI_Transmit(spi_handle, &ewen, 1, HAL_MAX_DELAY) == HAL_OK) return TRUE;
    else return FALSE;
}

//Disable the EEPROM
//Accepts: SPI handle
//Returns: Success or failure of the disable operation
uint8_t Disable_EEPROM (SPI_TypeDef *spi_handle) {
    uint16_t ewds = (0x04 << 8) | 0b00000000;
    if (HAL_SPI_Transmit(spi_handle, &ewds, 1, HAL_MAX_DELAY) == HAL_OK) return TRUE;
    else return FALSE;
}

//Read from the EEPROM
//Accepts: SPI handle, memory address and data buffer where the read value will be stored
//Returns: Success or failure of read operation
uint8_t Read_EEPROM (SPI_TypeDef *spi_handle, uint8_t addr, uint16_t data) {
    uint16_t write_package;
    write_package = (READ << 8 | addr);
//  if (HAL_SPI_Transmit(spi_handle, &write_package, 1, HAL_MAX_DELAY) == HAL_OK) {
//      HAL_Delay(10);
//      if (HAL_SPI_Receive(spi_handle, &data, 1, HAL_MAX_DELAY) == HAL_OK) return TRUE;
//      else return FALSE;
//  }
    if (HAL_SPI_TransmitReceive(spi_handle, &write_package, &data, 1, HAL_MAX_DELAY) == HAL_OK) return TRUE;
    else return FALSE;
}

//Write to the EEPROM
//Accepts: SPI handle, memory address and data to be written
//Returns: Success or failure of write operation
uint8_t Write_EEPROM (SPI_TypeDef *spi_handle, uint8_t addr, uint16_t data) {
    uint16_t write_package[2];
    write_package[0] = (WRITE << 8 | addr);
    write_package[1] = data;
    if (HAL_SPI_Transmit(spi_handle, write_package, 2, HAL_MAX_DELAY) == HAL_OK) return TRUE;
    else return FALSE;
}

//Erase a specific memory address from the EEPROM
//Accepts: SPI handle and the memory address to be erased
//Returns: Success or failure of erase operation
uint8_t Erase_EEPROM (SPI_TypeDef *spi_handle, uint8_t addr) {
    uint16_t write_package;
    write_package = (ERASE << 8 | addr);
    if (HAL_SPI_Transmit(spi_handle, &write_package, 1, HAL_MAX_DELAY) == HAL_OK) return TRUE;
    else return FALSE;
}

编辑:我在这里也附上了波形。

使能够

使能够

擦除

擦除

在此处输入图像描述

4

1 回答 1

0

没有仔细查看您的代码,我发现了一个可能的问题:为了完成 SPI 操作,通常需要先将片选 (CS) 线拉低,然后在每次操作后再次设置高。

因此,您的驱动程序代码中的 EEPROM 功能可能需要先将 CS 引脚设置为低电平,进行一些 SPI 操作,然后再将其设置为高电平。

为方便起见,我通常会在驱动源文件中添加一些简单的辅助函数:

static GPIO_TypeDef *_cs_port;
static uint16_t _cs_pin;

static void _chip_select(void)
{
    HAL_GPIO_WritePin(_cs_port, _cs_pin, GPIO_PIN_RESET);
}

static void _chip_deselect(void)
{
    HAL_GPIO_WritePin(_cs_port, _cs_pin, GPIO_PIN_SET);
}

在这种情况下,我通常会初始化驱动程序并跟踪外围实例和片选 GPIO,类似于:

static SPI_HandleTypeDef *_spi;
static uint8_t _init = 0;

int8_t eeprom_init(
    SPI_HandleTypeDef *spi,
    GPIO_TypeDef *gpio_cs_port,
    uint16_t gpio_cs_pin)
{
    if (_init)
        return -1;

    _spi = spi;
    _cs_port = gpio_cs_port;
    _cs_pin = gpio_cs_pin;

    /* do initialization here */

    _chip_deselect();

    _init = 1;

    return 0;
}

int8_t eeprom_clear(void)
{
    if (!_init)
        return -1;

    /* do de-initialization here */

    _spi = 0;
    _cs_port = 0;
    _cs_pin = 0;
    _init = 0;

    return 0;
}

int8_t eeprom_op_x(void)
{
    if (!_init)
        return -1;

    _chip_select();

    op_x(); /* todo */

    _chip_deselect();

    return 0;
}

我希望这有帮助 :) !您的硬件/软件中可能存在其他问题;这可能不是您问题的完整解决方案。

BTW:也有使用硬件片选(STM32 SPI外设)的方法,我没用过(参考手册中的SPI/NSS)。据我所知,您还在 SPI 配置中使用了 SPI_NSS_SOFT,这需要您手动设置片选线。

顺便说一句:不相关,但可能感兴趣:ST 提供简单的 HAL 函数来访问外部 I2C 闪存(HAL_I2C_Mem_*() 函数)。


编辑 0(通过浏览代码/数据表获得更多发现):

  • Read_EEPROM() 不会像这样工作,从总线读取的数据无法在函数范围之外访问(C 问题)。相反,可以将指向读取缓冲区的指针传递给函数(或者可以将读取的数据作为返回值返回)。例如像这样:uint8_t Read_EEPROM (SPI_TypeDef *spi_handle, uint8_t addr, uint8_t *data, uint8_t byte_count)

  • 在 Read_EEPROM() 中:当像这样使用时,HAL_SPI_TransmitReceive() 不会读取传入的字节。它同时接收和发送。因此,首先编写read / address 命令,然后开始读取传入的字节(就像在您的代码中已被注释掉的代码一样)是有意义的。

  • 在 Enable_/Disable_/Read_/Erase_EEPROM(): The number of bytes (size) 似乎是错误的,应该是 2 而不是 1,以使 HAL_SPI_Transmit() / HAL_SPI_TransmitReceive() 发送/接收正确的字节数.

  • 这个 IC 似乎不太适合与普通 SPI 一起使用,因为它需要一个非常特定的位序列,它不是字节对齐的(就像你说的那样)。进行交流可能是有意义 的(就像您提到的那样),并注意数据表中说明的每一点...

由于这似乎是一个早期测试,我会尽量保持它尽可能简单,并通过手动对相同的 SPI 引脚进行位旋转(重新配置为普通 GPIO)来进行第一次启用/写入/读取操作,这样 STM32 的面向字节的 SPI HAL 函数的问题就不会妨碍您了。然后努力做一个不错的小驱动程序...也许STM32的SPI仍然可以以某种方式使用,现在对我来说很难说...

于 2018-07-13T12:11:30.560 回答