我正在尝试从外部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;
}
编辑:我也在这里附加了波形。
启用
擦除
写
答案 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仍然可以以某种方式使用,现在很难告诉我...