STM32 HAL 库 SPI接口驱动FLASH(ZD25WQ16C)
本文主要介绍STM32 HAL 库 SPI接口驱动的相关接口函数,并使用STM32 Cube配置SPI接口生成代码,同时介绍了HAL库中几个核心的接口函数的功能,还介绍ZD25WQ16C Flash芯片的相关内容。并基于ST的HAL库函数实现其驱动。
目录
2.1 HAL_SPI_TransmitReceive 函数
概述
本文主要介绍STM32 HAL 库 SPI接口驱动的相关接口函数,并使用STM32 Cube配置SPI接口生成代码,同时介绍了HAL库中几个核心的接口函数的功能,还介绍ZD25WQ16C Flash芯片的相关内容。并基于ST的HAL库函数实现其驱动。
1 STM32 SPI驱动实现
1.1 STM32Cube配置参数
1) 使能SPI接口

2) 配置相关的参数

3) 确认IO接口相关配置

4) 完成上述配置后,生成项目代码,其框架结构如下:

1.2 实现和驱动相关的接口函数
STM32 的HAL库中已经实现和MCU芯片相关的接口函数,驱动具体的芯片其使用的接口函数需要根据芯片的特性编写。这里主要实现两个函数:
1) SPI_NSS_Set, 实现片选功能
2) spi_TransferByte 读写数据功能
具体源代码如下:
// SPI发送并接收一个字节
uint8_t spi_TransferByte(uint8_t data)
{
uint8_t RxData;
HAL_SPI_TransmitReceive(&hspi1, &data, &RxData, 1, 1000);
return RxData;
}
// 设置NSS引脚状态
void SPI_NSS_Set(uint8_t state)
{
if (state)
HAL_GPIO_WritePin(FLASH_CS_GPIO_Port, FLASH_CS_Pin, GPIO_PIN_SET);
else
HAL_GPIO_WritePin(FLASH_CS_GPIO_Port, FLASH_CS_Pin, GPIO_PIN_RESET);
}
2 STM32库SPI相关的函数介绍
2.1 HAL_SPI_TransmitReceive 函数
HAL_SPI_TransmitReceive 是 STM32 的 HAL 库中一个非常重要的 SPI 通信函数。它的核心功能是 在一次 SPI 通信过程中,同时完成数据的发送和接收。这是一种 全双工 的通信模式,在主机向从机发送数据的同时,也从从机读取数据。
1) 函数原型:
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi,
uint8_t *pTxData,
uint8_t *pRxData,
uint16_t Size,
uint32_t Timeout);
2) 参数详解
*hspi
类型:
SPI_HandleTypeDef指针含义: 指向一个 SPI 实例的句柄,这个句柄包含了该 SPI 外设的所有配置信息(如模式、波特率、数据大小等)。你需要在初始化时配置好这个句柄。
*pTxData
类型:
uint8_t指针(指向字节数组)含义: 指向发送数据缓冲区的指针。函数会从这个缓冲区读取数据,并通过 MOSI 线发送出去。
注意: 如果只想接收数据而不发送,这个参数不能为
NULL,你需要提供一个有效的缓冲区(里面的数据内容通常会被忽略,但某些从机需要时钟,所以必须发送数据来产生时钟信号)。
*pRxData
类型:
uint8_t指针(指向字节数组)含义: 指向接收数据缓冲区的指针。函数会将从 MISO 线接收到的数据存入这个缓冲区。
注意: 缓冲区必须足够大以容纳
Size个字节的数据。
Size
类型:
uint16_t含义: 期望发送和接收的数据量(以字节为单位)。发送和接收的字节数是相同的。
Timeout
类型:
uint32_t含义: 超时时间(以毫秒为单位)。函数会等待通信完成,但如果超过这个时间仍未完成,函数会退出并返回
HAL_TIMEOUT错误。可以使用HAL_MAX_DELAY来一直等待。
3) 返回值
返回值
HAL_OK: 操作成功。
HAL_ERROR: 参数错误。
HAL_BUSY: SPI 总线正忙。
HAL_TIMEOUT: 操作超时。
2.2 HAL_SPI_Transmit函数
HAL_SPI_Transmit 是 STM32 HAL 库中用于 同步发送数据 的 SPI 函数。它的核心功能是 将指定数据缓冲区中的数据通过 SPI 总线发送出去,而不关心接收到的数据。这是一种主发送的通信模式,主要用于向从设备写入数据、发送命令或配置寄存器。
1)函数原型
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi,
uint8_t *pData,
uint16_t Size,
uint32_t Timeout);
2) 参数介绍
*hspi
类型:
SPI_HandleTypeDef指针含义: 指向 SPI 实例的句柄,包含该 SPI 外设的所有配置信息。
*pData
类型:
uint8_t指针(指向字节数组)含义: 指向发送数据缓冲区的指针。函数会从这个缓冲区读取数据并通过 MOSI 线发送出去。
Size
类型:
uint16_t含义: 要发送的数据量(以字节为单位)。
Timeout
类型:
uint32_t含义: 超时时间(以毫秒为单位)。如果通信超过此时间未完成,函数返回
HAL_TIMEOUT。
3) 返回值
HAL_OK: 发送成功
HAL_ERROR: 参数错误
HAL_BUSY: SPI 总线正忙
HAL_TIMEOUT: 操作超时
2.3 HAL_SPI_Receive函数
HAL_SPI_Receive 是 STM32 HAL 库中用于 同步接收数据 的 SPI 函数。它的核心功能是 从 SPI 从设备读取指定数量的数据到接收缓冲区。虽然名为"接收"函数,但由于 SPI 是全双工协议,实际上它通过发送虚拟数据来产生时钟信号,从而接收从设备的返回数据。
1) 函数原型
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi,
uint8_t *pData,
uint16_t Size,
uint32_t Timeout);
2) 参数介绍
*hspi
类型:
SPI_HandleTypeDef指针含义: 指向 SPI 实例的句柄,包含该 SPI 外设的所有配置信息。
*pData
类型:
uint8_t指针(指向字节数组)含义: 指向接收数据缓冲区的指针。函数会将从 MISO 线接收到的数据存入这个缓冲区。
Size
类型:
uint16_t含义: 要接收的数据量(以字节为单位)。
Timeout
类型:
uint32_t含义: 超时时间(以毫秒为单位)。
3) 返回值
HAL_OK: 接收成功
HAL_ERROR: 参数错误
HAL_BUSY: SPI 总线正忙
HAL_TIMEOUT: 操作超时
2.4 函数功能总结
| 函数 | 功能 | 内部机制 | 适用场景 |
|---|---|---|---|
HAL_SPI_Receive |
只接收数据 | 发送虚拟数据产生时钟 | 读取传感器数据、存储器内容 |
HAL_SPI_Transmit |
只发送数据 | 忽略接收到的数据 | 配置寄存器、发送命令 |
HAL_SPI_TransmitReceive |
同时发送和接收 | 真正的全双工交换 | 发送命令并接收响应的复杂协议 |
HAL_SPI_Receive_IT |
中断方式接收 | 非阻塞、异步 | 需要同时处理其他任务 |
HAL_SPI_Receive_DMA |
DMA方式接收 | 高效、不占用CPU | 大数据量接收 |
3 ZD25WQ16C 芯片
ZD25WQ16C 是一款 16M-bit(即 2MB)的串行 Flash 存储器,支持标准的 SPI 接口,最高时钟频率可达 104MHz。它支持双线/四线 SPI 模式,具有高速读取、低功耗等特点。
关键规格
容量: 16M-bit = 2M-Byte = 2048KB
接口: 标准 SPI / Dual SPI / Quad SPI
电压: 2.7V - 3.6V
扇区结构: 统一 4KB 扇区
页大小: 256 字节
封装: SOIC-8, WSON-8 等
3.1 硬件结构
1) 框架结构

2) memory 相关参数

3) 主要操作指令


3.2 芯片相关的参数
其设备ID如下:

工作参数:



3.3 封装和IO接口
D25Q16E(16M 位)串行闪存支持标准的串行外设接口(SPI),以及双/四SPI:串行时钟、芯片选择、串行数据输入/输出 0(SI)、输入/输出 1(SO)、输入/输出 2(WP#)、输入/输出 3(HOLD#)。双输入数据的传输速度为 266Mbit/s,四输入数据的传输速度为 532Mbit/s。

引脚定义如下:
4 驱动实现
4.1 代码实现
编写驱动程序,其代码如下:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : spi_flash.c
* Description : flash driver library
******************************************************************************
* @attention
*
* COPYRIGHT: Copyright (c) 2025 mingfei_tang
* DATE: MAR 28th, 2025
*
*
******************************************************************************
*/
/* USER CODE END Header */
#include "spi_flash.h"
#include "spi.h"
#if 0
#define zCMDxx_log printf
#else
#define zCMDxx_log(...)
#endif
Flash_Info flash_Info_Obj;
static bool flash_check_address( u32 addr);
static void spi_init( void )
{
}
u8 spi_transfer_byte( u8 data )
{
return spi_TransferByte(data);
}
/**
*
* @brief select the flash chip
*
* @details en = true, select the chip
* en = false, release the chip
*
* @ingroup spi flash driver
*/
static void spi_cs_ctrl( u8 en )
{
SPI_NSS_Set(en);
}
/**
*
* @brief read the chip id
*
* @details
*
*
* @ingroup spi flash driver
*/
static u16 flash_read_id(void)
{
u32 ID = 0;
u8 Temp0 = 0, Temp1 = 0, Temp2;
spi_cs_ctrl(0);
spi_transfer_byte( CMD_READ_JEDEC_ID );
spi_transfer_byte(0x00);
spi_transfer_byte(0x00);
spi_transfer_byte(0x00);
Temp0 = spi_transfer_byte(0xff);
Temp1 = spi_transfer_byte(0xff);
Temp2 = spi_transfer_byte(0xff);
spi_cs_ctrl(1);
ID = (Temp1 << 8) | Temp2;
return ID;
}
/**
*
* @brief read data from flash
*
* @details addr:start address
* buff: data buff
* len: data length that will be read
*
* @ingroup spi flash driver
*/
static void spi_flash_read_bytes( u32 addr, u8* buff, u16 len )
{
if( !flash_check_address( addr + len ))
return;
spi_cs_ctrl(0);
spi_transfer_byte( CMD_READ_DATA );
spi_transfer_byte( (addr & 0xFF0000) >>16 );
spi_transfer_byte( (addr & 0xFF00) >>8 );
spi_transfer_byte( addr & 0xff );
while (len--)
{
*buff++ = spi_transfer_byte(0xff);
}
spi_cs_ctrl(1);
}
/**
*
* @brief enable to write the flash
*
* @details
*
*
* @ingroup spi flash driver
*/
static void flash_wr_enable(void)
{
spi_cs_ctrl(0);
spi_transfer_byte( CMD_WRITE_ENABLE );
spi_cs_ctrl(1);
}
/**
*
* @brief wait for writing data ready
*
* @details
*
*
* @ingroup spi flash driver
*/
static void flash_wait_for_ready( void )
{
spi_cs_ctrl(0);
spi_transfer_byte( CMD_READ_STATUS1 );
while(( spi_transfer_byte(0xff) & 0x01 ) == 0x01);
spi_cs_ctrl(1);
}
/**
*
* @brief write one page
*
* @details
*
*
* @ingroup spi flash driver
*/
void flash_write_bytes_page(u32 addr, u8* buff, u16 len )
{
u16 i;
if( !flash_check_address( addr + len ))
return;
flash_wr_enable();
spi_cs_ctrl(0);
spi_transfer_byte( CMD_PAGE_PROGRAM );
spi_transfer_byte((u8)((addr)>>16));
spi_transfer_byte((u8)((addr)>>8));
spi_transfer_byte((u8)addr);
for( i=0; i < len; i++ )
{
spi_transfer_byte(buff[i]);
}
spi_cs_ctrl(1);
flash_wait_for_ready();
}
/**
*
* @brief erase the sector
*
* @details
*
*
* @ingroup spi flash driver
*/
void ext_flash_erase_page(u32 pagenum)
{
u32 addr;
addr = SPI_FLASH_PAGE_SIZE*pagenum;
if( !flash_check_address( addr ))
return;
flash_wr_enable();
flash_wait_for_ready();
spi_cs_ctrl(1);
spi_transfer_byte( CMD_PAGE_ERASE );
// write the address
spi_transfer_byte( (u8)((addr)>>16) );
spi_transfer_byte( (u8)((addr)>>8) );
spi_transfer_byte( (u8)addr );
spi_cs_ctrl(0);
flash_wait_for_ready();
}
/**
*
* @brief erase the sector
*
* @details
*
*
* @ingroup spi flash driver
*/
void flash_erase_sector(u32 sectornum)
{
u32 addr;
addr = SPI_FLASH_SECTOR_SIZE*sectornum;
if( !flash_check_address( addr ))
return;
flash_wr_enable();
flash_wait_for_ready();
spi_cs_ctrl(1);
spi_transfer_byte( CMD_SECTOR_ERASE );
// write the address
spi_transfer_byte( (u8)((addr)>>16) );
spi_transfer_byte( (u8)((addr)>>8) );
spi_transfer_byte( (u8)addr );
spi_cs_ctrl(0);
flash_wait_for_ready();
}
/**
*
* @brief erase the chip
*
* @details
*
*
* @ingroup spi flash driver
*/
void flash_erase_chip(void)
{
flash_wr_enable();
flash_wait_for_ready();
spi_cs_ctrl(1);
spi_transfer_byte( CMD_CHIP_ERASE );
spi_cs_ctrl(0);
flash_wait_for_ready();
}
/**
*
* @brief write block data
*
* @details
*
*
* @ingroup spi flash driver
*/
void ext_flash_wr_no_check(u32 addr, u8* buff, u16 len)
{
u16 pageremain;
pageremain = SPI_FLASH_PAGE_SIZE - addr%SPI_FLASH_PAGE_SIZE;
if(len <= pageremain )
pageremain=len;
while(1)
{
flash_write_bytes_page(addr,buff, pageremain);
if( len == pageremain )
break;
else
{
buff += pageremain;
addr += pageremain;
len -= pageremain;
if(len > SPI_FLASH_PAGE_SIZE)
pageremain = SPI_FLASH_PAGE_SIZE;
else
pageremain = len;
}
}
}
/**
*
* @brief write block data
*
* @details
*
*
* @ingroup spi flash driver
*/
u8 tempbuff[SPI_FLASH_PAGE_SIZE];
void ext_spi_flash_write_block( u32 addr, u8 *buff, u16 len)
{
u32 secpos;
u16 secoff;
u16 secremain;
u16 i;
u8 * pbuff;
if( !flash_check_address( addr + len ))
return;
pbuff = tempbuff;
secpos = addr/SPI_FLASH_PAGE_SIZE;
secoff = addr%SPI_FLASH_PAGE_SIZE;
secremain = SPI_FLASH_PAGE_SIZE-secoff;
if(len <= secremain)
secremain = len;
while(1)
{
spi_flash_read_bytes(secpos*SPI_FLASH_PAGE_SIZE, pbuff, SPI_FLASH_PAGE_SIZE);
for(i=0;i<secremain;i++)
{
if(pbuff[secoff+i] != 0XFF)
break;
}
if(i<secremain)
{
ext_flash_erase_page(secpos);
for(i=0;i<secremain;i++)
{
pbuff[i+secoff] = buff[i];
}
ext_flash_wr_no_check(secpos*SPI_FLASH_PAGE_SIZE, pbuff, SPI_FLASH_PAGE_SIZE);
}
else
{
ext_flash_wr_no_check(addr, buff, secremain);
}
if( len == secremain)
break;
else
{
secpos++;
secoff=0;
buff += secremain;
addr += secremain;
len -= secremain;
if( len > SPI_FLASH_PAGE_SIZE )
secremain = SPI_FLASH_PAGE_SIZE;
else
secremain = len;
}
}
}
/**
*
* @brief init flash driver
*
* @details
*
*
* @ingroup spi flash driver
*/
void flash_Init( void )
{
u16 ID;
spi_init();
// read flash ID
ID = flash_read_id();
flash_Info_Obj.ID = ID;
flash_Info_Obj.enable = false;
if( ID == 0X6015 || ID == 0X4015 ) //W25Q16
{
flash_Info_Obj.pageSize = SPI_FLASH_PAGE_SIZE;
flash_Info_Obj.pageTotal = 8192;
flash_Info_Obj.memSize = SPI_FLASH_PAGE_SIZE * 8192;
flash_Info_Obj.enable = true;
printf("Flash ID: 0x%04x, size: %d\r\n", ID, flash_Info_Obj.memSize);
}
else
{
flash_Info_Obj.pageSize = 0;
flash_Info_Obj.pageTotal = 0;
flash_Info_Obj.memSize = 0;
}
}
static bool flash_check_address( u32 addr)
{
if( !flash_Info_Obj.enable )
{
zCMDxx_log("zCMDxx: initial fail \r\n");
return false;
}
if( addr > flash_Info_Obj.memSize )
{
zCMDxx_log("zCMDxx: address out of range, addr = %x, memsize = %x \r\n", addr, flash_Info_Obj.memSize);
return false;
}
return true;
}
Flash_Info flash_chip_Infor( void )
{
return flash_Info_Obj;
}
/**
*
* @brief control the spi flash power
* en = 1: enable the spi
* en = 0: disable the spi and flash
* @details
*
*
* @ingroup spi flash driver
*/
/**
*
* @brief used to test the driver
*
* @details
*
*
* @ingroup spi flash driver
*/
#if 1
#define TEST_LEN 1024
u8 rd_buff[TEST_LEN];
u8 wd_buff[TEST_LEN];
bool spi_flash_test( void )
{
u32 addr;
spi_init();
flash_read_id();
for ( int i =0; i< TEST_LEN; i++ )
{
wd_buff[i] = 0x50;
}
addr = 6*256;
ext_spi_flash_write_block( addr, wd_buff, TEST_LEN);
spi_flash_read_bytes( addr, rd_buff, TEST_LEN);
for ( int i =0; i< TEST_LEN; i++ )
{
if(wd_buff[i]!=rd_buff[i])
{
zCMDxx_log("Flash: match data fail \r\n");
return false;
}
}
zCMDxx_log("Flash: match data pass \r\n");
return true;
}
#else
bool spi_flash_test( void )
{
//flash_erase_chip();
return true;
}
#endif
/******************************************************************************
* SPI Flash CS PIN
******************************************************************************/
/** End of this file */
/* End of this file */
4.2 验证
读取Flash的ID, 确认其和芯片定义一致

使用J-Link 调试确认:

更多推荐



所有评论(0)