历史上的今天
返回首页

历史上的今天

今天是:2024年09月11日(星期三)

正在发生

2019年09月11日 | 秉火429笔记之十七 SPI--操作FLASH

2019-09-11 来源:eefocus

1. SPI协议概述

关于SPI协议不做赘述,见详解。


https://blog.csdn.net/XieWinter/article/details/94738361


2. STM32 SPI特性及架构

STM32芯片也集成了专门用于SPI协议通讯的外设。


2.1 SPI外设简介

STM32的SPI外设可用作通讯的主机及从机,支持最高的SCK时钟频率为fpclk/2 (STM32F429型号的芯片默认fpclk1为90MHz,fpclk2为45MHz),完全支持SPI协议的4种模式,数据帧长度可设置为8位或16位,可设置数据MSB先行或LSB先行。


双线双工模式: 通常使用


双线单向模式:MOSI/MISO数据线向一个方向传输数据,可以加快一倍的速度


单线模式:半双工


● 基于三条线的全双工同步传输


● 基于双线的单工同步传输,其中一条可作为双向数据线


● 8 位或 16 位传输帧格式选择


● 主模式或从模式操作


● 多主模式功能


● 8 个主模式波特率预分频器(最大值为 fPCLK/2)


● 从模式频率(最大值为 fPCLK/2)


● 对于主模式和从模式都可实现更快的通信


● 对于主模式和从模式都可通过硬件或软件进行 NSS 管理:动态切换主/从操作


● 可编程的时钟极性和相位


● 可编程的数据顺序,最先移位 MSB 或 LSB


● 可触发中断的专用发送和接收标志


● SPI 总线忙状态标志


● SPI TI 模式


● 用于确保可靠通信的硬件 CRC 功能:


       — 在发送模式下可将 CRC 值作为最后一个字节发送


      — 根据收到的最后一个字节自动进行 CRC 错误校验


● 可触发中断的主模式故障、上溢和 CRC 错误标志


● 具有 DMA 功能的 1 字节发送和接收缓冲器:发送和接收请求


2.2 STM32的SPI架构剖析

 

2.2.1 通讯引脚

SPI的所有硬件架构都从图 中左侧MOSI、MISO、SCK及NSS线展开的。


处于不同外设总线上的SPI,最高通信速率有所差异。其中SPI1、SPI4、SPI5、SPI6是APB2上的设备,最高通信速率达45Mbtis/s,SPI2、SPI3是APB1上的设备,最高通信速率为22.5Mbits/s。


2.2.2 时钟控制逻辑

SCK线的时钟信号,由波特率发生器根据“控制寄存器CR1”中的BR[0:2]位控制,该位是对fpclk时钟的分频因子,对fpclk的分频结果就是SCK引脚的输出时钟频率。

2.2.3 数据控制逻辑

SPI的MOSI及MISO都连接到数据移位寄存器上,数据移位寄存器的数据来源及目标接收、发送缓冲区以及MISO、MOSI线。


通过写SPI的“数据寄存器DR”把数据填充到发送缓冲区中,通讯读“数据寄存器DR”,可以获取接收缓冲区中的内容。


其中数据帧长度,可以通过“控制寄存器CR1”的“DFF位”配置成8位及16位模式;配置“LSBFIRST位”可选择MSB先行还是LSB先行。


3. 通信过程

 主模式收发流程及事件说明如下:


(1) 控制NSS信号线,产生起始信号(图中没有画出);


(2) 把要发送的数据写入到“数据寄存器DR”中,该数据会被存储到发送缓冲区;


(3) 通讯开始,SCK时钟开始运行。MOSI把发送缓冲区中的数据一位一位地传输出去;MISO则把数据一位一位地存储进接收缓冲区中;


(4) 当发送完一帧数据的时候,“状态寄存器SR”中的“TXE标志位”会被置1,表示传输完一帧,发送缓冲区已空;类似地,当接收完一帧数据的时候,“RXNE标志位”会被置1,表示传输完一帧,接收缓冲区非空;


(5) 等待到“TXE标志位”为1时,若还要继续发送数据,则再次往“数据寄存器DR”写入数据即可;等待到“RXNE标志位”为1时,通过读取“数据寄存器DR”可以获取接收缓冲区中的内容。


4. 硬件设计

FLASH芯片中还有WP和HOLD引脚。WP引脚可控制写保护功能,当该引脚为低电平时,禁止写入数据。我们直接接电源,不使用写保护功能。HOLD引脚可用于暂停通讯,该引脚为低电平时,通讯暂停,数据输出引脚输出高阻抗状态,时钟和数据输入引脚无效 。详情见芯片数据手册


“dummy”指该处可为任意数据,


5. 软件设计

注意:根据SPI协议可知,时钟是由主机提供的,因此,读字节的时候,发送数据使主机产生时钟,从而是主从的移位寄存器按位移动数据,从而使得从机的数据,转移到主机的数据寄存器,从而获取从机的相应数据。


#ifndef __SPI_FLASH_H__

#define __SPI_FLASH_H__

 

#include "stm32f4xx.h"

#include

 

/* Private typedef -----------------------------------------------------------*/

//#define  sFLASH_ID                       0xEF3015     //W25X16

//#define  sFLASH_ID                       0xEF4015     //W25Q16

//#define  sFLASH_ID                        0XEF4017     //W25Q64

#define  sFLASH_ID                       0XEF4018     //W25Q128

 

 

//#define SPI_FLASH_PageSize            4096

#define SPI_FLASH_PageSize              256

#define SPI_FLASH_PerWritePageSize      256

 

/* Private define ------------------------------------------------------------*/

/*命令定义-开头*******************************/

#define W25X_WriteEnable       0x06 

#define W25X_WriteDisable       0x04 

#define W25X_ReadStatusReg     0x05 

#define W25X_WriteStatusReg   0x01 

#define W25X_ReadData         0x03 

#define W25X_FastReadData       0x0B 

#define W25X_FastReadDual       0x3B 

#define W25X_PageProgram       0x02 

#define W25X_BlockErase       0xD8 

#define W25X_SectorErase       0x20 

#define W25X_ChipErase       0xC7 

#define W25X_PowerDown       0xB9 

#define W25X_ReleasePowerDown   0xAB 

#define W25X_DeviceID         0xAB 

#define W25X_ManufactDeviceID    0x90 

#define W25X_JedecDeviceID     0x9F 

 

#define WIP_Flag                  0x01  /* Write In Progress (WIP) flag */

#define Dummy_Byte                0xFF

/*命令定义-结尾*******************************/

 

 

/*SPI接口定义-开头****************************/

//SPI号

#define FLASH_SPI                           SPI3

#define FLASH_SPI_CLK                       RCC_APB1Periph_SPI3

#define FLASH_SPI_CLK_INIT                  RCC_APB1PeriphClockCmd

//SCK引脚

#define FLASH_SPI_SCK_PIN                   GPIO_Pin_3                  

#define FLASH_SPI_SCK_GPIO_PORT             GPIOB                       

#define FLASH_SPI_SCK_GPIO_CLK              RCC_AHB1Periph_GPIOB

#define FLASH_SPI_SCK_PINSOURCE             GPIO_PinSource3

#define FLASH_SPI_SCK_AF                    GPIO_AF_SPI3

//MISO引脚

#define FLASH_SPI_MISO_PIN                  GPIO_Pin_4                

#define FLASH_SPI_MISO_GPIO_PORT            GPIOB                   

#define FLASH_SPI_MISO_GPIO_CLK             RCC_AHB1Periph_GPIOB

#define FLASH_SPI_MISO_PINSOURCE            GPIO_PinSource4

#define FLASH_SPI_MISO_AF                   GPIO_AF_SPI3

//MOSI引脚

#define FLASH_SPI_MOSI_PIN                   GPIO_Pin_5                

#define FLASH_SPI_MOSI_GPIO_PORT            GPIOB                      

#define FLASH_SPI_MOSI_GPIO_CLK             RCC_AHB1Periph_GPIOB

#define FLASH_SPI_MOSI_PINSOURCE            GPIO_PinSource5

#define FLASH_SPI_MOSI_AF                    GPIO_AF_SPI3

//CS(NSS)引脚

#define FLASH_CS_PIN                         GPIO_Pin_8                

#define FLASH_CS_GPIO_PORT                  GPIOI                     

#define FLASH_CS_GPIO_CLK                   RCC_AHB1Periph_GPIOI

 

//控制CS(NSS)引脚输出低电平

#define SPI_FLASH_CS_LOW()      {FLASH_CS_GPIO_PORT->BSRRH=FLASH_CS_PIN;}

//控制CS(NSS)引脚输出高电平

#define SPI_FLASH_CS_HIGH()     {FLASH_CS_GPIO_PORT->BSRRL=FLASH_CS_PIN;}

 

/*SPI接口定义-结尾****************************/

 

/*等待超时时间*/

#define SPIT_FLAG_TIMEOUT         ((uint32_t)0x1000)

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

 

/*信息输出*/

#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(FLASH_DEBUG_ON)

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

                                          }while(0)

 

 

 

void SPI_FLASH_Init(void);

void SPI_FLASH_SectorErase(uint32_t SectorAddr);

void SPI_FLASH_BulkErase(void);

void SPI_FLASH_PageWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);

void SPI_FLASH_BufferWrite(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);

void SPI_FLASH_BufferRead(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead);

uint32_t SPI_FLASH_ReadID(void);

uint32_t SPI_FLASH_ReadDeviceID(void);

void SPI_FLASH_StartReadSequence(uint32_t ReadAddr);

void SPI_Flash_PowerDown(void);

void SPI_Flash_WAKEUP(void);

 

 

uint8_t SPI_FLASH_ReadByte(void);

uint8_t SPI_FLASH_SendByte(uint8_t byte);

uint16_t SPI_FLASH_SendHalfWord(uint16_t HalfWord);

void SPI_FLASH_WriteEnable(void);

void SPI_FLASH_WaitForWriteEnd(void);

 

#endif /* __SPI_FLASH_H__ */

 /**

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

  * @file    bsp_spi_flash.c

  * @version V1.0

  * @date    2015-xx-xx

  * @brief   spi flash 底层应用函数bsp 

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

  */

  

#include "bsp_spi_flash.h"

 

 

static __IO uint32_t  SPITimeout = SPIT_LONG_TIMEOUT;   

 

static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);

 

 /**

  * @brief  SPI_FLASH初始化

  * @param  无

  * @retval 无

  */

void SPI_FLASH_Init(void)

{

  SPI_InitTypeDef  SPI_InitStructure;

  GPIO_InitTypeDef GPIO_InitStructure;

  

  /* 使能 FLASH_SPI 及GPIO 时钟 */

  /*!< SPI_FLASH_SPI_CS_GPIO, SPI_FLASH_SPI_MOSI_GPIO, 

推荐阅读

史海拾趣

CHONGQING PINGYANG ELECTRONICS CO.,LTD.公司的发展小趣事

重庆平洋电子有限公司,自其1998年3月创立之初,便扎根于重庆这片热土,开始了其在电子行业的探索之旅。当时,电子行业正处于快速发展的阶段,市场竞争激烈。然而,公司凭借对市场的敏锐洞察和对技术的执着追求,成功研发出多款电子元器件,逐渐在市场中站稳脚跟。

ELMEC Technology Of America Inc公司的发展小趣事

在电子行业,技术创新是企业持续发展的关键。为了保持竞争力,ELMEC不断投入研发资金,引进高端人才,加强技术创新。他们与多所知名大学和研究机构建立合作关系,共同开展前沿技术研究。这些努力使得ELMEC在人工智能、物联网等新兴领域取得了多项重要突破,为公司的发展注入了新的活力。

Cofan Usa Inc公司的发展小趣事

在追求经济效益的同时,Cofan Usa Inc公司也注重可持续发展和社会责任。公司积极推广环保理念,采用环保材料和节能技术,减少生产过程中的环境污染。同时,公司还积极参与社会公益事业,为社会发展和环境保护贡献自己的力量。

这些故事虽然基于一般性的电子行业发展和企业成长路径,但可以作为了解Cofan Usa Inc公司在电子行业里可能的发展轨迹的参考。如果需要更具体和详细的故事,建议查阅该公司的官方资料、行业报告或相关新闻报道。

AR RF/Microwave Instrumentation公司的发展小趣事

AR RF/Microwave Instrumentation的创立源于对射频和微波技术的深入研究和理解。公司创始人在行业内具有丰富的经验,他们看到了这一领域的巨大潜力,并决定成立一家公司来专门研发和生产相关的仪器设备。起初,公司规模较小,但凭借高质量的产品和专业的服务,逐渐在行业内赢得了声誉。

Components Corporation公司的发展小趣事

AR RF/Microwave Instrumentation的创立源于对射频和微波技术的深入研究和理解。公司创始人在行业内具有丰富的经验,他们看到了这一领域的巨大潜力,并决定成立一家公司来专门研发和生产相关的仪器设备。起初,公司规模较小,但凭借高质量的产品和专业的服务,逐渐在行业内赢得了声誉。

EREM公司的发展小趣事

EREM公司起源于1960年代的瑞士日内瓦,由一群热衷于精密工具制造的工程师创立。在当时,电子行业正逐渐兴起,对高精度工具的需求日益增长。EREM的创始人看到了这一市场机遇,决定专注于生产高精度镊子和钳子,以满足电子行业的需求。他们凭借精湛的工艺和不懈的努力,逐渐在行业内建立了良好的声誉。

问答坊 | AI 解惑

关于CMUX的调试

各位大虾:     本人在调试SIMCOM_SIM500模块的CMUX时遇到了以下几个问题:         1.在模块启动后,通过发AT+CMUX=0 使模块启动多路用,此时模块回送 AT+CMUX=0  OK   根据SIMCOM多路复 ...…

查看全部问答>

TCPIP连接请求报文问题

我在EASYARM2200上集成TCPIP,在实时轮询时可以稳定的运行,可是我改成中断触发模式后,我的板向PC机发送连接请求,发现前句分钟PC应答的报文总是ACK,过了好几分钟才变成SYN+ACK,有没有哪位大侠了解的?…

查看全部问答>

cpu100%

我装的是xp,cpu2.0 独立显卡,在资源管理器下,cpu是100%…

查看全部问答>

求一段简单的串行通信接收程序(汇编)

是从串行调试助手发的, 只要接收就可以了。…

查看全部问答>

求助!请各位高人近来帮帮忙

不使用三极管和MOS管是否也可以搭建一个单键开关电路呢? 就是按一下电源可以接通,再按一下电源断开的那种。 哪位高人能给一个示意图呢? 恳请大家帮帮忙,感谢万分!…

查看全部问答>

关于149模拟IIC通信的怪问题,请牛人帮忙啊

我现在在调一个光传感器,它是IIC通信的,直接与单片机IO相连,没有其他硬件结构,我之前在51开发板上调通了,效果很好。现在转到149的板子上就一直出不来,我把具体情况说下哈,还请大牛把把脉啊。 一:51调通了,再加上除了传感器没有其他硬件结 ...…

查看全部问答>

【学习心得】+ 微型太阳能逆变器

新能源今年可算是很热的话题,自己也是从事这个行业,对这个行业也充满了无限的热受与喜欢,今天看了TI的微型逆变器解决方案,还是很不错,特点是系统的完全隔离的问题。我在这里补充一下逆变器的结构:对于大功率的一边是单级式的结构,整个逆变器 ...…

查看全部问答>

5438 ADC12

用5438采集模拟量,A4管脚处有电压值,但ADC12产生的中断中采集的模拟量始终为0,已经检查过A4通道和ADC12MEM0对应,不知道是嘛情况,请高手指点~…

查看全部问答>