前言:
为了方便查看博客,特意申请了一个公众号,附上二维码,有兴趣的朋友可以关注,和我一起讨论学习,一起享受技术,一起成长。

github:my github
注:博客所涉及的关于 stm32 的代码,均在仓库【stm32f013_study】下,包括底层驱动和应用测试代码。
本文设计的文件包含:
(1)hardware_spi.c:硬件 SPI 驱动实现
(2)drvsfspi.c:软件模拟 SPI 实现代码
(3)drvexflash.c:SPI FLASH 操作部分代码
(4)hal_spi.c:SPI 软件、硬件方式封装统一接口
(5)头文件:
hardware_spi.h :硬件 SPI 相关
drvsfspi.h :软件模拟 SPI 相关
drvexflash.hSPI FLASH 相关
hal_spi.h:软件、硬件 SPI 接口封装
1. 硬件连接
W25Q64 将 8M 的容量分为 128 个块(Block),每个块大小为 64K 字节,每个块又分为 16个扇区(Sector),每个扇区 4K 个字节。
W25Q64 的**最少擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。**操作需要给 W25Q64 开辟一个至少 4K 的缓存区,对 SRAM 要求比较高,要求芯片必须有 4K 以上 SRAM 才能很好的操作。

W25Q64 的擦写周期多达 10W 次,具有 20 年的数据保存期限,支持电压为 2.7~3.6V,W25Q64 支持标准的 SPI,还支持双输出/四输出的 SPI,最大 SPI 时钟可以到 80Mhz(双输出时相当于 160Mhz,四输出时相当于 320M)。
1.1 硬件连接
与 STM32 的引脚连接如下:这里是使用SPI1配置。

STM32引脚 |
对应SPI功能 |
---|
PA2 |
片选CS |
PA5 |
时钟SCK |
PA6 |
MISO |
PA7 |
MOSI |
STM32 的 SPI 功能很强大, SPI 时钟最多可以到 18Mhz,支持 DMA,可以配置为 SPI 协议或者 I2S 协议(仅大容量型号支持)。
1.2 SPI 通讯的通讯时序
SPI 协议定义了通讯的起始和停止信号、数据有效性、时钟同步等环节。
我们以读取 FLASH 的状态寄存器的时序图分析一下,时序图也是书写软件模拟时序的依据。

如上图,我们知道书写 FLASH (来自华邦 W25X 手册)支持的是模式 0 (CPOL = 0 && CPHA == 0) 和 模式 3(CPOL = 1 && CPHA == 1)
CS、SCK、MOSI 信号都由主机控制产生,而 MISO 的信号由从机产生,主机通过该信号线读取从机的数据。MOSI 与 MISO 的信号只在 CS 为低电平的时候才有效,在 SCK 的每个时钟周期 MOSI 和 MISO 传输一位数据。
1.2.1. 通讯的起始和停止信号
在上图,CS 信号线由高变低,为 SPI 通讯的起始信号。CS 是每个从机各自独占的信号线,当从机在自己的 CS 线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。当 CS 信号由低变高,为 SPI 通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。
1.2.2. 数据有效性
SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。

MOSI 及 MISO 数据线在 SCK 的每个时钟周期传输一位数据。数据传输时,MSB 先行或 LSB 先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同样的协定,一般都会采用图中的 MSB 先行模式。
观察上图,可知模式 0 和 3 都是在上升沿读取数据。
示例: FLASH 读取 JEDEC_ID (0x9F),SPI 模式 0,,频率 f = 1MHz。

读取 JEDEC_ID 时,FLASH 回复的一个字节:0xC8。

1.2.3 STM32 SPI外设
STM32 的 SPI 外设可用作通讯的主机及从机,支持最高的 SCK 时钟频率为 f pclk / 2 (STM32F103 型号的芯片默认 f pclk1 为 72MHz,f pclk2 为 36MHz),完全支持 SPI 协议的 4 种模式,数据帧长度可设置为 8 位或 16 位,可设置数据 MSB 先行或 LSB 先行。它还支持双线全双工、双线单向以及单线模式。
SPI架构:

通讯引脚 :
SPI 的所有硬件架构都从上图中左 MOSI、MISO、SCK及 NSS 线展开的。
STM32 芯片有多个 SPI 外设,它们的 SPI 通讯信号引出到不同的 GPIO 引脚上,使用时必须配置到这些指定的引脚。
2. 软件配置
这里使用 STM32 的 SPI1 的主模式,SPI 相关的库函数和定义分布在文件 stm32f10x_spi.c 以及头文件 stm32f10x_spi.h 中。
2.1 配置相关引脚的复用功能
第一步就要使能 SPI1 的时钟, SPI1 的时钟通过 APB2ENR 的第 12 位来设置。其次要设置 SPI1 的相关引脚为复用输出,这样才会连接到 SPI1 上否则这些 IO 口还是默认的状态,也就是标准输入输出口。这里我们使用的是 PA5、 PA6、 PA7 这 3 个(SCK、 MISO、 MOSI、CS 使用软件管理方式),所以设置这三个为复用 IO。
宏定义:
#define SPIM1_GPIO_PORT GPIOA
#define SPIM1_CLK_IO (GPIO_Pin_5)
#define SPIM1_MISO_IO (GPIO_Pin_6)
#define SPIM1_MOSI_IO (GPIO_Pin_7)
#define FLASH_CS_IO (GPIO_Pin_2)
#define FLASH_CS_0() (GPIO_ResetBits(SPIM1_GPIO_PORT, FLASH_CS_IO))
#define FLASH_CS_1() (GPIO_SetBits(SPIM1_GPIO_PORT, FLASH_CS_IO))
#define RCC_PCLK_SPIM1_GPIO RCC_APB2Periph_GPIOA
#define RCC_PCLK_SPIM1_HD RCC_APB2Periph_SPI1
IO 配置:
void spi_gpio_init(uint8_t spi_chl)
{
GPIO_InitTypeDef gpio_config_init;
if (spi_chl == 1)
{
RCC_APB2PeriphClockCmd(RCC_PCLK_SPIM1_GPIO, ENABLE);
gpio_config_init.GPIO_Pin = SPIM1_CLK_IO | SPIM1_MOSI_IO;
gpio_config_init.GPIO_Mode = GPIO_Mode_AF_PP;
gpio_config_init.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SPIM1_GPIO_PORT, &gpio_config_init);
gpio_config_init.GPIO_Pin = SPIM1_MISO_IO;
gpio_config_init.GPIO_Mode = GPIO_Mode_IN_FLOATING;
gpio_config_init.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SPIM1_GPIO_PORT, &gpio_config_init);
GPIO_SetBits(SPIM1_GPIO_PORT, SPIM1_CLK_IO | SPIM1_MISO_IO | SPIM1_MOSI_IO);
}
}
2.2 初始化 SPI1,设置 SPI1 工作模式
接下来初始化 SPI1,设置 SPI1 为主机模式,设置数据格式为 8 位,然设置 SCK 时钟极性及采样方式。并设置 SPI1 的时钟频率(最大 18Mhz),以及数据的格式(MSB 在前还是 LSB 在前)。这在库函数中是通过 SPI_Init 函数来实现。
函数原型:
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
第一个参数是 SPI 标号,第二个参数结构体类型 SPI_InitTypeDef 为相关属性设置。
SPI_InitTypeDef 的定义如下:
typedef struct
{
uint16_t SPI_Direction;
uint16_t SPI_Mode;
uint16_t SPI_DataSize;
uint16_t SPI_CPOL;
uint16_t SPI_CPHA;
uint16_t SPI_NSS;
uint16_t SPI_BaudRatePrescaler;
uint16_t SPI_FirstBit;
uint16_t SPI_CRCPolynomial;
}SPI_InitTypeDef;
参数 |
解释 |
---|
SPI_Direction |
设置 SPI 的通信方式,可以选择为半双工,全双工,以及串行发和串行收方式 |
SPI_Mode |
设置 SPI 的主从模式,主机模式 (SPI_Mode_Master),从机模式 (PI_Mode_Slave)。 |
SPI_DataSiz |
数据为 8 位还是 16 位帧格式选择项。SPI_DataSize_8b(8 位),SPI_DataSize_16b (16位) |
SPI_CPOL |
设置时钟极性 |
SPI_CPHA |
设置时钟相位,也就是选择在串行同步时钟的第几个跳变沿(上升或下降)数据被采样,可以为第一个或者第二个条边沿采集 |
SPI_NSS |
设置 NSS 信号由硬件(NSS 管脚)还是软件控制 |
SPI_BaudRatePrescaler |
设置 SPI 波特率预分频值也就是决定 SPI 的时钟的参数 ,从不分频道 256 分频 8 个可选值 ,选择 256 分频值SPI_BaudRatePrescaler_256, 传输速度为 36M/256=140.625KHz。 |
SPI_FirstBit |
设置数据传输顺序是 MSB 位在前还是 LSB 位在前。SPI_FirstBit_MSB (高位在前) |
SPI_CRCPolynomial |
设置 CRC 校验多项式,提高通信可靠性,大于 1 即可 |
初始化的范例格式为:
void spi_master_init(uint8_t spi_chl)
{
SPI_InitTypeDef spi_config_init;
#if 1
if(spi_chl == 1)
{
spi_flash_gpio_init();
spi_gpio_init(1);
RCC_APB2PeriphClockCmd(RCC_PCLK_SPIM1_HD, ENABLE);
spi_config_init.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
spi_config_init.SPI_Mode = SPI_Mode_Master;
spi_config_init.SPI_DataSize = SPI_DataSize_8b;
spi_config_init.SPI_CPOL = SPI_CPOL_Low;
spi_config_init.SPI_CPHA = SPI_CPHA_1Edge;
spi_config_init.SPI_NSS = SPI_NSS_Soft;
spi_config_init.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
spi_config_init.SPI_FirstBit = SPI_FirstBit_MSB;
spi_config_init.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &spi_config_init);
SPI_Cmd(SPI1, ENABLE);
}
#endif
}
2.3 SPI 传输数据
通信接口需要有发送数据和接受数据的函数,固件库提供的发送数据函数原型为:
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
往 SPIx 数据寄存器写入数据 Data,从而实现发送。
固件库提供的接受数据函数原型为:
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;
这从 SPIx 数据寄存器读出接收到的数据。
收发单个字节数据:
uint8_t spi_master_send_recv_byte(uint8_t spi_chl, uint8_t spi_byte)
{
uint8_t time = 0;
if (spi_chl == 1)
{
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET)
{
time++;
if(time>200)
{
return false;
}
}
SPI_I2S_SendData(SPI1, spi_byte);
time = 0;
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)
{
time++;
if(time>200)
{
return false;
}
}
return SPI_I2S_ReceiveData(SPI1);
}
else
{
return false;
}
}
收发多个字节数据:
void spi_master_send_some_bytes(uint8_t spi_chl, uint8_t *pbdata, uint16_t send_length)
{
uint16_t i = 0;
for (i = 0; i < send_length; i++)
{
spi_master_send_recv_byte(spi_chl, pbdata[i]);
}
}
void spi_master_recv_some_bytes(uint8_t spi_chl, uint8_t *pbdata, uint16_t recv_length)
{
uint8_t *temp_data = pbdata;
while (recv_length--)
{
*temp_data++ = spi_master_send_recv_byte(spi_chl, 0xFF);
}
}
2.4 查看 SPI 传输状态
在 SPI 传输过程中,要判断数据是否传输完成,发送区是否为空等等状态,
通过函数 SPI_I2S_GetFlagStatus 实现的,判断发送是否完成的方法是:
SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE);
3. SPI FLASH 操作
3.1 宏定义部分
#define FLASH_WRITE_ENABLE_CMD 0x06
#define FLASH_WRITE_DISABLE_CMD 0x04
#define FLASH_READ_SR_CMD 0x05
#define FLASH_WRITE_SR_CMD 0x01
#define FLASH_READ_DATA 0x03
#define FLASH_FASTREAD_DATA 0x0b
#define FLASH_WRITE_PAGE 0x02
#define FLASH_ERASE_PAGE 0x81
#define FLASH_ERASE_SECTOR 0x20
#define FLASH_ERASE_BLOCK 0xd8
#define FLASH_ERASE_CHIP 0xc7
#define FLASH_POWER_DOWN 0xb9
#define FLASH_RELEASE_POWER_DOWN 0xab
#define FLASH_READ_DEVICE_ID 0x90
#define FLASH_READ_JEDEC_ID 0x9f
#define FLASH_SIZE (1*1024*1024)
#define PAGE_SIZE 8192
#define SECTOR_SIZE 512
#define BLOCK_SIZE 32
#define PAGE_LEN 255
3.2 中间层函数封装
注明: 此部分函数的封装是为了统一硬件 SPI 和软件模拟 SPI 接口。
uint8_t hal_spi_send_bytes(uint8_t mode, uint8_t *pbdata, uint16_t send_length)
{
if (mode == 0)
{
for (uint16_t i = 0; i < send_length; i++)
{
Spi_WriteByte(pbdata[i]);
}
return true;
}
else if (mode == 1)
{
spi_master_send_some_bytes(1, pbdata, send_length);
return true;
}
else
{
return false;
}
}
uint8_t hal_spi_recv_bytes(uint8_t mode, uint8_t *pbdata, uint16_t recv_length)
{
if (mode == 0)
{
for (uint16_t i = 0; i < recv_length; i++)
{
*pbdata++ = Spi_ReadByte();
}
return true;
}
else if (mode == 1)
{
spi_master_recv_some_bytes(1, pbdata, recv_length);
return true;
}
else
{
return false;
}
}
关于软件模拟 SPI 部分代码,参看:软件模拟SPI代码 。此处不再贴出。
3.3 FLASH 部分
__align(4) uint8_t g_DataTmpBuffer[0x1000] = {0};
#define SectorBuf g_DataTmpBuffer
void Flash_WriteEnable(void)
{
uint8_t command = FLASH_WRITE_ENABLE_CMD;
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
FLASH_CS_HIGH;
}
void Flash_WriteDisable(void)
{
uint8_t command = FLASH_WRITE_DISABLE_CMD;
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
FLASH_CS_HIGH;
}
uint8_t Flash_ReadSR(void)
{
uint8_t ucTmpVal = 0;
uint8_t command = FLASH_READ_SR_CMD;
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
hal_spi_recv_bytes(SPI_COMM_MODE, &ucTmpVal, 1);
FLASH_CS_HIGH;
return ucTmpVal;
}
void Flash_WriteSR(uint8_t _ucByte)
{
uint8_t command = FLASH_WRITE_SR_CMD;
Flash_WriteEnable();
Flash_WaitNobusy();
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
hal_spi_send_bytes(SPI_COMM_MODE, &_ucByte, 1);
FLASH_CS_HIGH;
}
void Flash_WaitNobusy(void)
{
while(((Flash_ReadSR()) & 0x01)==0x01);
}
void Flash_ReadSomeBytes(uint8_t *ucpBuffer, uint32_t _ulReadAddr, uint16_t _usNByte)
{
uint8_t command = FLASH_READ_DATA;
uint8_t temp_buff[3] = {0};
temp_buff[0] = (uint8_t)(_ulReadAddr >> 16);
temp_buff[1] = (uint8_t)(_ulReadAddr >> 8);
temp_buff[2] = (uint8_t)(_ulReadAddr >> 0);
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);
hal_spi_recv_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte);
FLASH_CS_HIGH;
}
void Flash_FastReadByte(uint8_t *ucpBuffer, uint32_t _ulReadAddr, uint16_t _usNByte)
{
uint8_t command = FLASH_FASTREAD_DATA;
uint8_t temp_buff[3] = {0};
temp_buff[0] = (uint8_t)(_ulReadAddr >> 16);
temp_buff[1] = (uint8_t)(_ulReadAddr >> 8);
temp_buff[2] = (uint8_t)(_ulReadAddr >> 0);
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);
hal_spi_recv_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte);
FLASH_CS_HIGH;
}
void Flash_WritePage(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte)
{
uint8_t command = FLASH_WRITE_PAGE;
uint8_t temp_buff[3] = {0};
temp_buff[0] = (uint8_t)(_ulWriteAddr >> 16);
temp_buff[1] = (uint8_t)(_ulWriteAddr >> 8);
temp_buff[2] = (uint8_t)(_ulWriteAddr >> 0);
Flash_WriteEnable();
Flash_WaitNobusy();
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);
hal_spi_send_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte);
FLASH_CS_HIGH;
Flash_WaitNobusy();
}
void Flash_WriteNoCheck(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte)
{
uint16_t PageByte = 256 - _ulWriteAddr % 256;
if(_usNByte <= PageByte)
{
PageByte = _usNByte;
}
while(1)
{
Flash_WritePage(ucpBuffer, _ulWriteAddr, PageByte);
if(_usNByte == PageByte)
break;
else
{
ucpBuffer += PageByte;
_ulWriteAddr += PageByte;
_usNByte -= PageByte;
if(_usNByte > 256)
{
PageByte = 256;
}
else
{
PageByte = _usNByte;
}
}
}
}
void Flash_WriteSomeBytes(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte)
{
uint32_t ulSecPos = 0;
uint16_t usSecOff = 0;
uint16_t usSecRemain = 0;
uint32_t i = 0;
ulSecPos = _ulWriteAddr / 4096;
usSecOff = _ulWriteAddr % 4096;
usSecRemain = 4096 - usSecOff;
if(_usNByte <= usSecRemain)
{
usSecRemain = _usNByte;
}
while(1)
{
Flash_ReadSomeBytes(SectorBuf, ulSecPos*4096, 4096);
for (i = 0; i < usSecRemain; i++)
{
if (SectorBuf[usSecOff + i] != 0xFF)
break;
}
if(i < usSecRemain)
{
Flash_EraseSector(ulSecPos);
for(i = 0; i < usSecRemain; i++)
{
SectorBuf[usSecOff + i] = ucpBuffer[i];
}
Flash_WriteNoCheck(SectorBuf, ulSecPos*4096, 4096);
}
else
{
Flash_WriteNoCheck(ucpBuffer, _ulWriteAddr, usSecRemain);
}
if(_usNByte == usSecRemain)
{
Flash_WriteDisable();
break;
}
else
{
ulSecPos++;
usSecOff = 0;
ucpBuffer += usSecRemain;
_ulWriteAddr += usSecRemain;
_usNByte -= usSecRemain;
if(_usNByte > 4096)
{
usSecRemain = 4096;
}
else
{
usSecRemain = _usNByte;
}
}
}
}
void Flash_ErasePage(uint32_t _ulPageAddr)
{
_ulPageAddr *= 256;
Flash_WriteEnable();
Flash_WaitNobusy();
FLASH_CS_LOW;
Spi_WriteByte(FLASH_ERASE_PAGE);
Spi_WriteByte((uint8_t)(_ulPageAddr>>16));
Spi_WriteByte((uint8_t)(_ulPageAddr>>8));
Spi_WriteByte((uint8_t)(_ulPageAddr>>0));
FLASH_CS_HIGH;
Flash_WaitNobusy();
}
void Flash_EraseSector(uint32_t _ulSectorAddr)
{
uint8_t command = FLASH_ERASE_SECTOR;
uint8_t temp_buff[3] = {0};
temp_buff[0] = (uint8_t)(_ulSectorAddr >> 16);
temp_buff[1] = (uint8_t)(_ulSectorAddr >> 8);
temp_buff[2] = (uint8_t)(_ulSectorAddr >> 0);
_ulSectorAddr *= 4096;
Flash_WriteEnable();
Flash_WaitNobusy();
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);
hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);
FLASH_CS_HIGH;
Flash_WaitNobusy();
}
void Flash_EraseBlock(uint32_t _ulBlockAddr)
{
uint8_t command = FLASH_ERASE_BLOCK;
_ulBlockAddr *= 65536;
Flash_WriteEnable();
Flash_WaitNobusy();
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr>>16), 1);
hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr>>8), 1);
hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr>>0), 1);
FLASH_CS_HIGH;
Flash_WaitNobusy();
}
void Flash_EraseChip(void)
{
uint8_t command = FLASH_ERASE_CHIP;
Flash_WriteEnable();
Flash_WaitNobusy();
FLASH_CS_LOW;
hal_spi_recv_bytes(SPI_COMM_MODE, &command, 1);
FLASH_CS_HIGH;
Flash_WaitNobusy();
}
void Flash_PowerDown(void)
{
uint8_t command = FLASH_POWER_DOWN;
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
FLASH_CS_HIGH;
Sys_delay_us(3);
}
void Flash_WakeUp(void)
{
uint8_t command = FLASH_RELEASE_POWER_DOWN;
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
FLASH_CS_HIGH;
Sys_delay_us(3);
}
uint16_t Flash_ReadDeviceID(void)
{
uint8_t command = FLASH_READ_DEVICE_ID;
uint16_t usFlashId = 0;
uint8_t temp_buff[3] = {0};
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
hal_spi_send_bytes(SPI_COMM_MODE, temp_buff, 3);
hal_spi_recv_bytes(SPI_COMM_MODE, temp_buff, 2);
FLASH_CS_HIGH;
usFlashId = (uint16_t)(temp_buff[0] << 8) | (temp_buff[1] << 0);
return usFlashId;
}
uint32_t Flash_ReadJEDECID(void)
{
uint8_t command = FLASH_READ_JEDEC_ID;
uint32_t flash_jed_id = 0;
uint8_t recv_buff[3] = {0};
FLASH_CS_LOW;
hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
hal_spi_recv_bytes(SPI_COMM_MODE, recv_buff, 3);
FLASH_CS_HIGH;
flash_jed_id = (recv_buff[0] << 16) | (recv_buff[1] << 8) | (recv_buff[2] << 0);
return flash_jed_id;
}
参考:
1.原子库函数手册
2.SPI—读写串行 FLASH