历史上的今天
返回首页

历史上的今天

今天是:2024年10月25日(星期五)

正在发生

2021年10月25日 | stm32专题十八:stm32读写SPI FLASH

2021-10-25 来源:eefocus

直接上代码:


bsp_spi_flash.c


/**

  ******************************************************************************

  * @file    bsp_spi_flash.c

  * @author  STMicroelectronics

  * @version V1.0

  * @date    2019

  * @brief   SPI FLASH(W25Q64)应用函数bsp

  ******************************************************************************

  */ 

 

#include "bsp_spi_flash.h"

#include "./usart/bsp_usart.h"

 

 

static __IO uint32_t  SPITimeout = SPIT_LONG_TIMEOUT;   

 

 

static uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);

 

 

/**

  * @brief  SPI I/O配置

  * @param  无

  * @retval 无

  */

static void SPI_GPIO_Config(void)

{

  GPIO_InitTypeDef  GPIO_InitStructure; 

 

/* 使能与 SPI 有关的时钟 */

FLASH_SPI_APBxClock_FUN(FLASH_SPI_CLK, ENABLE);

FLASH_SPI_GPIO_APBxClock_FUN(FLASH_SPI_GPIO_CLK, ENABLE);

    

  /* MISO MOSI SCK */

  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        // SCK 推挽复用

  GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        // MOSI 推挽复用

  GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // MOSI 浮空输入

  GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure);

// 初始化CS引脚,使用软件控制,所以直接配置为推挽输出

GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;      // CS 推挽输出

  GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure);

// 拉高CS,使SPI处于空闲状态

FLASH_SPI_CS_HIGH;

}

 

 

/**

  * @brief  SPI 工作模式配置

  * @param  无

  * @retval 无

  */

static void SPI_Mode_Config(void)

{

  SPI_InitTypeDef SPI_InitStructure; 

 

  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

SPI_InitStructure.SPI_CRCPolynomial = 0; // 不使用CRC功能,数值随便写

  

SPI_Init(FLASH_SPIx, &SPI_InitStructure);

SPI_Cmd(FLASH_SPIx, ENABLE);

}

 

 

/**

  * @brief  I2C 外设(EEPROM)初始化

  * @param  无

  * @retval 无

  */

void SPI_FLASH_Init(void)

{

  SPI_GPIO_Config(); 

  SPI_Mode_Config();

}

 

// 发送并接收一个字节

uint8_t SPI_FLASH_Send_Byte(uint8_t data)

{

SPITimeout = SPIT_FLAG_TIMEOUT;

// 检查并等待至TX缓冲区为空

while (SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_TXE) == RESET)

{

if ((SPITimeout--) == 0)

{

return SPI_TIMEOUT_UserCallback(0);

}

}

// 程序执行到此处,TX缓冲区已空

SPI_I2S_SendData(FLASH_SPIx, data);

SPITimeout = SPIT_FLAG_TIMEOUT;

// 检查并等待至RX缓冲区非空

while (SPI_I2S_GetFlagStatus(FLASH_SPIx, SPI_I2S_FLAG_RXNE) == RESET)

{

if ((SPITimeout--) == 0)

{

return SPI_TIMEOUT_UserCallback(0);

}

}

// 程序执行到此处,说明数据发送完毕,并接收到一个字节

return SPI_I2S_ReceiveData(FLASH_SPIx);

}

 

 

// 接收一个字节

uint8_t SPI_FLASH_Read_Byte(void)

{

return SPI_FLASH_Send_Byte(DUMMY);

}

 

// 读取FLASH ID

uint32_t SPI_Read_ID(void)

{

uint32_t flash_id = 0;

// 片选使能

FLASH_SPI_CS_LOW;

 

SPI_FLASH_Send_Byte(JEDEC_ID); // 发送命令码

flash_id = SPI_FLASH_Read_Byte();

flash_id <<= 8;

flash_id |= SPI_FLASH_Read_Byte();

flash_id <<= 8;

flash_id |= SPI_FLASH_Read_Byte();

 

FLASH_SPI_CS_HIGH;

return flash_id;

}

 

// 写入

 

// Flash写入使能

void SPI_WriteEnable(void)

{

FLASH_SPI_CS_LOW;

 

SPI_FLASH_Send_Byte(WRITE_ENABLE); // 发送命令码

FLASH_SPI_CS_HIGH;

}

 

 

// 擦除Flash指定扇区

void SPI_Erase_Sector(uint32_t addr)

{

SPI_WriteEnable(); // 擦除之前先调用写使能

// 片选使能

FLASH_SPI_CS_LOW;

SPI_FLASH_Send_Byte(ERASE_SECTOR);

 

SPI_FLASH_Send_Byte((addr >> 16) & (0xFF)); // 发送命令码

SPI_FLASH_Send_Byte((addr >> 8) & (0xFF));

 

SPI_FLASH_Send_Byte((addr) & (0xFF));

FLASH_SPI_CS_HIGH;

// Flash的擦除需要时间,要通过读取状态寄存器,来判断是否擦除完成

SPI_WaitForWriteEnd();

}

 

// 读取Flash的内容

void SPI_Read_Data(uint32_t addr, uint8_t *read_buff, uint32_t numByteToRead)

{

// 片选使能

FLASH_SPI_CS_LOW;

SPI_FLASH_Send_Byte(READ_DATA);

 

SPI_FLASH_Send_Byte((addr >> 16) & (0xFF));

SPI_FLASH_Send_Byte((addr >> 8) & (0xFF));

 

SPI_FLASH_Send_Byte((addr) & (0xFF));

while (numByteToRead--)

{

*read_buff = SPI_FLASH_Read_Byte();

read_buff++;

}

FLASH_SPI_CS_HIGH;

}

 

 

// 向Flash中写入内容(一次最多写256字节)

void SPI_Write_Data(uint32_t addr, uint8_t *write_buff, uint32_t numByteToWrite)

{

SPI_WriteEnable(); // 写入之前先调用写使能

// 片选使能

FLASH_SPI_CS_LOW;

SPI_FLASH_Send_Byte(WRITE_DATA);

 

SPI_FLASH_Send_Byte((addr >> 16) & (0xFF));

SPI_FLASH_Send_Byte((addr >> 8) & (0xFF));

 

SPI_FLASH_Send_Byte((addr) & (0xFF));

while (numByteToWrite--)

{

SPI_FLASH_Send_Byte(*write_buff);

write_buff++;

}

FLASH_SPI_CS_HIGH;

SPI_WaitForWriteEnd();

}

 

 

 

// 等待Flash内部时序操作完成

void SPI_WaitForWriteEnd(void)

{

uint8_t status = 0;

// 片选使能

FLASH_SPI_CS_LOW;

 

SPI_FLASH_Send_Byte(STATUS); // 发送命令码

do

{

status = SPI_FLASH_Read_Byte();

}

while ((status & 0x01) == 1); // SPI 总线忙碌

FLASH_SPI_CS_HIGH;

}

 

 

/**

  * @brief  Basic management of the timeout situation.

  * @param  errorCode:错误代码,可以用来定位是哪个环节出错.

  * @retval 返回0,表示SPI读取失败.

  */

static  uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)

{

  /* Block communication and all processes */

  FLASH_ERROR("SPI 等待超时!errorCode = %d",errorCode);

  

  return 0;

}

 

/*********************************************END OF FILE**********************/

 

bsp_spi_flash.h


#ifndef __BSP_SPI_FLASH_H

#define __BSP_SPI_FLASH_H

 

 

#include "stm32f10x.h"

 

 

/**************************I2C参数定义,I2C1或I2C2********************************/

#define             FLASH_SPIx                             SPI1

#define             FLASH_SPI_APBxClock_FUN                   RCC_APB2PeriphClockCmd

#define             FLASH_SPI_CLK                             RCC_APB2Periph_SPI1

#define             FLASH_SPI_GPIO_APBxClock_FUN              RCC_APB2PeriphClockCmd

#define             FLASH_SPI_GPIO_CLK                        (RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC) 

 

#define             FLASH_SPI_SCK_PORT                        GPIOA   

#define             FLASH_SPI_SCK_PIN                         GPIO_Pin_5

 

#define             FLASH_SPI_MISO_PORT                       GPIOA

#define             FLASH_SPI_MISO_PIN                        GPIO_Pin_6

 

#define             FLASH_SPI_MOSI_PORT                       GPIOA 

#define             FLASH_SPI_MOSI_PIN                        GPIO_Pin_7

 

#define             FLASH_SPI_CS_PORT                        GPIOC 

#define             FLASH_SPI_CS_PIN                          GPIO_Pin_0

 

#define DUMMY (0X00)

#define JEDEC_ID (0X9F)

#define ERASE_SECTOR (0X20)

#define STATUS (0X05)

#define READ_DATA (0X03)

#define WRITE_ENABLE (0X06)

#define WRITE_DATA (0X02)

 

/*等待超时时间*/

#define SPIT_FLAG_TIMEOUT         ((uint32_t)0x1000)

#define SPIT_LONG_TIMEOUT         ((uint32_t)(10 * SPIT_FLAG_TIMEOUT))

 

/* CS引脚配置 */

#define FLASH_SPI_CS_HIGH do{GPIO_SetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN);}while(0)

#define FLASH_SPI_CS_LOW do{GPIO_ResetBits(FLASH_SPI_CS_PORT,FLASH_SPI_CS_PIN);}while(0)

 

/*信息输出*/

#define FLASH_DEBUG_ON          0

 

#define FLASH_INFO(fmt,arg...)            printf("<<-FLASH-INFO->> "fmt"n",##arg)

#define FLASH_ERROR(fmt,arg...)          printf("<<-FLASH-ERROR->> "fmt"n",##arg)

#define FLASH_DEBUG(fmt,arg...)          do{

                                          if(EEPROM_DEBUG_ON)

                                          printf("<<-FLASH-DEBUG->> [%d]"fmt"n",__LINE__, ##arg);

推荐阅读

史海拾趣

Design Gateway公司的发展小趣事

1985年,泰德·维特(Ted Waitt)和Mike Hammond两位年轻人决定辍学创业。他们利用维特祖母的15,000美元存款作为担保,从银行获得了10,000美元贷款,租用了一台计算机,并准备了一份三页的经营计划。在维特父亲位于爱荷华州苏城的牧场里,他们创办了TIPC Network公司(Gateway的前身)。公司起步时,主要向得克萨斯电脑器械公司销售零配件,并开展计算机邮购业务。得益于直销商业模式,Gateway在短短四个月内就实现了10万美元的销售额。

旌芯半导体(GN)公司的发展小趣事

1985年,泰德·维特(Ted Waitt)和Mike Hammond两位年轻人决定辍学创业。他们利用维特祖母的15,000美元存款作为担保,从银行获得了10,000美元贷款,租用了一台计算机,并准备了一份三页的经营计划。在维特父亲位于爱荷华州苏城的牧场里,他们创办了TIPC Network公司(Gateway的前身)。公司起步时,主要向得克萨斯电脑器械公司销售零配件,并开展计算机邮购业务。得益于直销商业模式,Gateway在短短四个月内就实现了10万美元的销售额。

高博(GBG)公司的发展小趣事

谷峰电子有限公司的故事始于1995年,当时公司在香港成立,标志着其半导体元器件研发与销售的起点。初期,面对激烈的市场竞争和技术挑战,谷峰团队凭借对半导体技术的深刻理解和对市场需求的敏锐洞察,逐步确立了以功率MOSFET为核心产品的战略方向。2000年,谷峰在深圳设立分公司,进一步扩大了其研发和销售网络,为公司的快速发展奠定了坚实基础。

Electronic公司的发展小趣事

特斯拉,虽然是一家电动汽车公司,但其在电子行业的发展中也展现出了强大的跨界创新能力。特斯拉通过自主研发和生产电池、电机等核心零部件,成功将电动汽车的性能和品质提升到了新的高度。同时,特斯拉还积极探索自动驾驶、智能交通等前沿技术,不断推动电动汽车行业的变革和发展。特斯拉的跨界创新不仅颠覆了传统汽车行业的格局,也为电子行业带来了新的机遇和挑战。

请注意,以上故事框架仅供参考,具体细节和数据可能需要根据实际情况进行调整和补充。同时,由于篇幅限制,每个故事的字数可能无法满足500字以上的要求。

ZTE高新兴(Gosuncn)公司的发展小趣事

特斯拉,虽然是一家电动汽车公司,但其在电子行业的发展中也展现出了强大的跨界创新能力。特斯拉通过自主研发和生产电池、电机等核心零部件,成功将电动汽车的性能和品质提升到了新的高度。同时,特斯拉还积极探索自动驾驶、智能交通等前沿技术,不断推动电动汽车行业的变革和发展。特斯拉的跨界创新不仅颠覆了传统汽车行业的格局,也为电子行业带来了新的机遇和挑战。

请注意,以上故事框架仅供参考,具体细节和数据可能需要根据实际情况进行调整和补充。同时,由于篇幅限制,每个故事的字数可能无法满足500字以上的要求。

FEMA Electronics Corporation公司的发展小趣事

FEMA的创始人李明(化名)是一位资深的电子工程师,他在一次与客户的交流中,发现了市场对高质量电子元件的迫切需求。于是,他毅然决定创办FEMA,专注于研发和生产高性能的电子元器件。创业初期,公司面临着资金短缺、技术瓶颈等重重挑战。然而,李明凭借坚定的信念和不懈的努力,成功攻克了技术难关,并与多家知名企业建立了合作关系,为公司的发展奠定了坚实的基础。

问答坊 | AI 解惑

请教大家我应该用什么方法设计这个波形发生和控制电路

大家好!我想设计一个电路,可以产生两路同步的波形,还在波形产生到某个点的时候触发一个采集卡采 集数据,请问我是用cpld,单片机,arm,fpga,dss,plc中的哪种?因为我要学会了才弄,希望大家给 点意见。…

查看全部问答>

LCD黑屏问题调试[PXA310 bootloader]

LCD黑屏问题调试[PXA310 bootloader] 1. PXA310+DA9034+NXP5209架构的手机 2.Bug:LCD黑屏问题调试 3.任务级别:bootloader XXX:为保密公司做的项目 附件功能用不了,我用latex写的报告, 1.复制粘贴至另一个文件后,保存为一个文件,文 ...…

查看全部问答>

无线传感器分级休眠模型的研究

摘要:为了提高无线传感器的有效工作时间, 在无线传感器处于等待状态时, 令其休眠是重要的一种降低功耗策略。分析已 有的两种无线传感器分级休眠能耗模型的特点, 指出这两种模型是单部件无线传感器分级休眠模型, 它们不适用于多部件 组成的无线传 ...…

查看全部问答>

2440实验板,不通过JTAG如何烧写引导程序

大家好!2440的板子上不用JTAG,可以向flash里面烧写引导程序吗?通过USB、串口或者网口可以吗?…

查看全部问答>

请问Build菜单中Open Build Release Directory是连接哪个文件的。我想知道如何写一个.bat文件,让其自动编译驱动。

请问Build菜单中Open Build Release Directory是连接哪个文件的。我想知道如何写一个.bat文件,让其自动编译驱动。然后加载到nk.bin文件中。分别使用命令build、makeimg。 下面是我写的,我觉得是“call wince.bat ARMV4I CEBASE smdk2440”错误。 ...…

查看全部问答>

求助,望各位大侠赐教

现在在搞个磁卡打印机的程序,用的COM口,发出读指令后,有返回值,请问如何记录返回值?谢谢!…

查看全部问答>

请香主和大侠看下RTC的问题(用官方库)已解决

先贴上中断部分的程序:void RTC_IRQHandler(void){  vu32 Time_temp;  if (RTC_GetITStatus(RTC_IT_SEC) != RESET)  {    /* Clear the RTC&nbs ...…

查看全部问答>

用Flash保存设置的数据,如何知道未用Flash

小弟用STM32做一光源控制器,其中一些相关的设置,希望能保存到Flash中,在下次开机的时候能够直接读取出来,省去重新设置的麻烦。 在保存数据的时候,我如何确定要保存到Flash的哪个地址?哪些地址是没有被代码使用到的? 代码是如何被存放 ...…

查看全部问答>

怎么向外部存储器烧写程序

我想把程序烧写到外部存储器中 用的是quartus ii , 外部存储器不是专用的EPCS那种 是winbond的 该怎么操作呢 在网上看了好多 但还是很迷茫 看altera 的资料讲的都是针对EPCS的。按他的方法先把.sof文件转成.jin的格式 然后再用slf方式下载到存储器 ...…

查看全部问答>