无法使用STM32F103C8

时间:2018-07-11 06:15:54

标签: stm32 spi hal eeprom stm32f1

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

我似乎无法做的是读回数据。对于读取操作,我在示波器上看不到任何波形。所需的时钟周期数是奇数,而不是8的倍数。我不知道如何发送奇数个时钟周期,因为所有数据都是8位,16位或32位。无论何时需要25或29个时钟周期,我似乎都在发送32,而所需的周期是9,我似乎在发送16。我真的希望避免像this thread中所建议的那样进行位冲击。

这是主要代码:

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;
}

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

启用

enable

擦除

erase

enter image description here

1 个答案:

答案 0 :(得分:0)

在没有详细浏览代码的情况下,我发现了一个可能的问题:为了完成SPI操作,通常需要先将片选(CS)线拉低,然后在每次操作后再次拉高。 / p>

因此,驱动程序代码中的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,这需要您手动设置芯片选择线。

BTW:无关,但也许有趣: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()中:字节数(大小)似乎不正确,应为2而不是1,以使HAL_SPI_Transmit()/ HAL_SPI_TransmitReceive()发送/接收正确的字节数。

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

由于这似乎是一项早期测试,因此我将尝试使其尽可能简单,并通过手动将相同的SPI引脚置位来使第一次启用/写入/读取操作进行(正常配置GPIO),这样就不会妨碍STM32的面向字节的SPI HAL函数的问题。然后努力开发一个不错的小驱动程序...也许STM32的SPI仍然可以以某种方式使用,现在很难告诉我...