历史上的今天
返回首页

历史上的今天

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

2021年11月03日 | STM32F103ZET6 — SPI

2021-11-03 来源:eefocus

简介

SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议。


SPI 具有信号线少,协议简单,数据率高等优点。数据传送速率达几MB/s


Pin 脚介绍

标准的 SPI 使用 4 Pin 进行数据传送:


(1)MOSI – 主器设备数据输出,从器件数据输入


(2)MISO – 主器设备件数据输入,从器件数据输出


(3)SCLK – 时钟信号,由主器件产生, 最大为fPCLK/2,从模式频率最大为fCPU/2


(4)NSS – 从器件使能信号,由主器件控制,有的 IC 会标注为 CS (Chip select)

数据时在 CLK 时钟的驱动下,在数据线上按照一个 bit 一个 bit 的进行传送,数据可以在时钟的上升沿或者下降沿改变(或者采样)。


SPI 传输的缺点是,没有数据完整性校验,也没有流控机制。


既然称 SPI 为总线,则 SPI 就可以支持多个设备相连接,通过 CS 片选信号来指定期望通讯的设备。(多机通讯)


SPI 模式

SPI 通讯有 4 中不同的通讯模式,通信双方需要配置成为一样的模式,才能够进行正常的数据传输,这里有两个概念:


CPOL:时钟极性


CPHA:时钟相位


CPOL:(时钟极性)控制在没有数据传输时,SPI 时钟的空闲状态电平。即,定义了总线空闲的工作状态(注意,和 UART 不同,SPI 是通过 CLK 的状态来表征当前的总线状态,即不发生任何数据交互的时候,时钟信号总是没有进行翻转的)


CPOL=0,表示当SCLK=0时处于空闲态


CPOL=1,表示当SCLK=1时处于空闲态


CPHA:(时钟相位)是用来配置数据采样是在第几个边沿。


CPHA=0,表示数据采样是在第1个边沿


CPHA=1,表示数据采样是在第2个边沿


所以 CPOL 和 CPHA 的不同组合,成为了 SPI 的四种传输模式:


SPI Mode

image.png?imageView2/2/w/550

四种传输模式,定义了不同时刻的总线启动,以及数据发送和采样时间:


CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第1个边沿,也就是 SCLK 由低电平到高电平的跳变,所以数据采样是在上升沿,数据发送是在下降沿。


CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第1个边沿,也就是 SCLK 由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。


CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是 SCLK 由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。


CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第1个边沿,也就是 SCLK 由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。


对应到波形上:

STM32 SPI 特性

STM32 上支持 3 路 SPI:


可以支持全双工的通信

支持硬件 CRC

可编程的数据顺序,MSB在前或LSB在前

主模式和从模式的快速通信

可编程的时钟极性和相位(CPOL,CPHA)

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

可触发中断的主模式故障、过载以及CRC错误标志

支持DMA功能的1字节发送和接收缓冲器:产生发送和接受请求


SPI 时钟

单板上使用 SPI2 进行 SPI FLASH 的操作,使用的是 APB1 的时钟,最大配置为 36MHz。


硬件连接

硬件上,通过单板的 SPI2 引脚,连接到外部的 SPI FLASH (W25Q64)

所以,在配置的时候,需要针对 SPI2 进行配置。


SPI Flash 简介

硬件单板上,连接的是 WinBond 的 W25Q64BV 的 SPI Flash,此款 Flash 的特性如下:


大小:64M-bit / 8M-byte

页 : 256B

支持 80MHz 的时钟

支持扇区擦除:Sector Erase (4K-bytes)

支持块擦除:Block Erase (32K and 64K-bytes)

支持页写入:0~256-bytes

软件/硬件写保护

由于暂时不需要硬件写保护和Hold功能,故,直接将 WP和HOLD引脚接到 VCC(3.3V)


(此款SPI FLASH 还支持双线和4线 QSPI 的读写,由于 STM32 不支持,所以不在多说)


根据 W25Q64BV 的 Datasheet 描述,在操作这块 FLASH 的时候,需要配置主机为:


SPI Mode 0 或者 Mode 3

MSB 先传输

故,在 SPI2 配置的时候,需要进行对应的配置,才能够继续正常数据通信。


W25Q64BV 存在两个寄存器可以被访问,为 Status Register-1 和 Status Register-2,其中描述的关于 Write-Protect 的部分,暂时不管。与读写相关的就是 BUSY 位了,因为对 SPI Flash 编程后,Flash 需要内部的 cylce 进行数据的写入,内部program的时候会将 BUSY 置成 1,写入完成后,会将 BUSY 位置 0,故,每次对 Flash 进行写(包括擦除)之前,均要进行 BUSY 位的判断。

好啦,现在就开始按照 DateSheet 进行配置我们的 STM32 了。


STM32 SPI2 配置

配置过程主要分为两步:GPIO 的配置,SPI2 的配置(如原理图所示,PB_12 的 GPIO 用于了 CS 片选,我们需要将其配置成为输出的 GPIO,拉低的时候,选中 Flash,拉高的时候释放 Flash 信号)


1. 开启 GPIO B 组的时钟


2. 开启 SPI2 的时钟


3. 按照 STM32 手册,配置 SCK 、MOSI 和 NSS 为复用推挽输出、MISO为浮空输入(有的代码将 MISO 配置成为的输出,虽然也可以运行,不过,您不觉得别扭么?还是遵循 Spec 的来吧)。同时将 GPIO_B _12配置为输出(CS信号)

4. 配置 SPI2 为全双工模式


5. SPI2 为 Master


6. SPI2 运行在 MODE3(按照 W25Q64BV 的 Timing 要求 )


7. SPI2 NSS 为软件模式(根本没用)


8. 预分频系数为 4 分频(APB1 为 36M,则 SPI2 的 SCK 为 9 MHz)


9. SPI2 传输 MSB(按照 W25Q64BV 的 Timing 要求)


10. 不启用 CRC


11. 开启 SPI 功能


此刻 SPI 的配置就基本完成了。接下来就是 按照 W25Q64BV 的 Timing 要求,写 FLASH 驱动咯.....


#define SK_SPI_FLASH_CS_HIGH()           GPIO_SetBits(GPIOB, GPIO_Pin_12)

#define SK_SPI_FLASH_CS_LOW()            GPIO_ResetBits(GPIOB, GPIO_Pin_12)

 

/*******************************************************************************

* Function Name  : SK_SPIPortInit

* Description    : Configure the I/O port for SPI2.

* Input          : None

* Output         : None

* Return         : None

*******************************************************************************/

static void _SK_SPI2PortInit(void)

{

    GPIO_InitTypeDef stGpioInit;

 

    /*!< Configure pins: SCK */

    stGpioInit.GPIO_Pin = GPIO_Pin_13;

    stGpioInit.GPIO_Speed = GPIO_Speed_50MHz;

    stGpioInit.GPIO_Mode = GPIO_Mode_AF_PP;

    GPIO_Init(GPIOB, &stGpioInit);

 

    /*!< Configure pins: MISO */

    stGpioInit.GPIO_Pin = GPIO_Pin_14;

    stGpioInit.GPIO_Mode = GPIO_Mode_IN_FLOATING;

    GPIO_Init(GPIOB, &stGpioInit);

 

    /*!< Configure pins: MOSI */

    stGpioInit.GPIO_Pin = GPIO_Pin_15;

    stGpioInit.GPIO_Speed = GPIO_Speed_50MHz;

    stGpioInit.GPIO_Mode = GPIO_Mode_AF_PP;

    GPIO_Init(GPIOB, &stGpioInit);

 

    /*!< Configure pins: CS */

    stGpioInit.GPIO_Pin = GPIO_Pin_12;

    stGpioInit.GPIO_Speed = GPIO_Speed_50MHz;

    stGpioInit.GPIO_Mode = GPIO_Mode_Out_PP;

    GPIO_Init(GPIOB, &stGpioInit);

}

 

/*******************************************************************************

* Function Name  : _SK_SPI2BusInit

* Description    : Configure the SPI2 Bus to adpte the W25Q64 Flash.

* Input          : None

* Output         : None

* Return         : None

*******************************************************************************/

static void _SK_SPI2BusInit(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_4; // PCLK = 36M, SPI2 CLK = PCLK/4 = 9M

    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

    SPI_InitStructure.SPI_CRCPolynomial = 7;

 

    SPI_Init(SPI2, &SPI_InitStructure);

    SPI_Cmd(SPI2, ENABLE);

}

 

/*******************************************************************************

* Function Name  : SK_SPIInit

* Description    : Initializes the peripherals used by the SPI FLASH driver.

* Input          : None

* Output         : None

* Return         : None

*******************************************************************************/

void SK_SPIFlashInit(void)

{

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);

 

    _SK_SPI2PortInit();

 

    SK_SPI_FLASH_CS_HIGH();

 

    _SK_SPI2BusInit();

}


W25Q64BV Flash 驱动

W25Q64BV 是 Flash 嘛,最主要的就是读和写。当然,除了这些,还有擦除功能,读 ID 等等。不着急,一步一步来,都是套路。


STM32 在进行 SPI 全双工数据传输的时候,在通过 MOSI 写出去数据后,可以立马进行 MISO 数据读取,通过轮询 TXE 标志来判断数据已经全部加载到移位寄存器,通过轮询 RXE 标志来得知本次 MISO 的数据已经全部到账。


针对  W25Q64BV Flash,Datasheet 中列出了支持的多种不同的命令:

对应上述表格,有不同的 Timing 进行描述,比如:Opcode 为 0x9F 的时候,是读取一个叫 JEDEC ID 的东西:

可以看到,主机首先将 CS 拉低,然后主机处在 Mode 0 或者 Mode 3 的时候,在 MOSI 信号上输出 0x9F 的数据(命令),然后接着写入 Dummy Data (随便写点东西),然后再 MISO 信号上就能够收到 manufacture ID 的信息(为 0xEF),在继续写入 Dummy Data,继续接收,继续写入,继续接收,这样便可以得到期望的数据了。


获取 Flash ID 信息

根据上述方式,便可以获取 ID 信息:


/********************** W25Q64 Flash Command Defination ***********************/

#define SPI_FLASH_PerWritePageSize      256

 

#define W25X_WriteEnable                0x06 

#define W25X_WriteDisable               0x04 

#define W25X_ReadStatusReg_1            0x05

#define W25X_ReadStatusReg_2            0x35 

#define W25X_WriteStatusReg             0x01 

#define W25X_ReadData                   0x03 

#define W25X_FastReadData               0x0B 

#define W25X_FastReadDual               0x3B 

#define W25X_PageProgram                0x02 

#define W25X_64K_BlockErase             0xD8 

#define W25X_32K_BlockErase             0x52 

#define W25X_4K_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 Busy_Flag                       0x01  /* Write In Progress (WIP) flag */

#define Dummy_Byte                      0xFF  /* Dummy Data */

 

typedef struct {

    uint8_t manufacturer_id;

    uint8_t memory_type_id;

    uint8_t capacity_id;

    uint8_t device_id;

} W25Q64_ID_t;

 

 

/*******************************************************************************

* Function Name  : SPI_FLASH_SendByte

* Description    : Sends a byte through the SPI interface and return the byte

*                  received from the SPI bus.

* Input          : byte : byte to send.

* Output         : None

* Return         : The value of the received byte.

*******************************************************************************/

static uint8_t SPI_FLASH_SendByte(uint8_t byte)

{

  /* Loop while DR register in not emplty */

  while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);

 

  /* Send byte through the SPI2 peripheral */

推荐阅读

史海拾趣

飞翼科技(FEIYI)公司的发展小趣事

在电子行业中,艾迪沃德公司(Beijing IDworld Science & Technology Development Co., Ltd.)的发展历程充满了技术创新与市场拓展的亮点。以下是五个关于艾迪沃德公司发展起来的相关故事,每个故事均基于事实描述,旨在展现其成长轨迹。

1. 创立与技术创新起点

艾迪沃德公司成立于2004年6月,自创立之初便确立了以研究、开发国际先进指纹识别技术为基本战略的发展方向。在那个指纹识别技术刚刚兴起的时代,艾迪沃德凭借其前瞻性的视野,迅速投入到这一领域的探索中。公司自主研发的指纹识别技术,经过不断迭代与优化,逐渐成为了业界公认的优秀指纹识别核心算法之一。这一技术突破不仅为公司赢得了市场的初步认可,更为后续的产品开发奠定了坚实的基础。

2. 产品多元化与市场拓展

随着技术的不断成熟,艾迪沃德开始将指纹识别技术应用于更多领域,推出了包括指纹考勤机、指纹门禁系统、指纹保险柜在内的多元化产品系列。这些产品凭借其高安全性、便捷性和稳定性,迅速在市场中占据了一席之地。特别是在安防、金融、教育等行业,艾迪沃德的产品得到了广泛应用,进一步巩固了其在指纹识别领域的市场地位。

3. OEM与ODM业务的发展

为了满足不同客户的定制化需求,艾迪沃德积极拓展OEM(原始设备制造商)和ODM(原始设计制造商)业务。公司凭借其强大的研发能力和生产能力,为众多合作伙伴提供从产品设计、生产到售后的全方位服务。这一业务模式不仅为公司带来了稳定的收入来源,还进一步提升了艾迪沃德在电子行业中的知名度和影响力。

4. 技术支持与解决方案提供

艾迪沃德深知技术支持对于客户的重要性,因此公司组建了一支专业的技术支持团队,为客户提供包括技术咨询、方案设计、系统集成在内的全方位服务。无论是大型项目还是小型应用,艾迪沃德都能根据客户的具体需求,提供量身定制的解决方案。这种以客户为中心的服务理念,赢得了客户的广泛赞誉和信赖。

5. 国际合作与品牌建设

在国际化战略的推动下,艾迪沃德积极参与国际交流与合作,与多家国际知名企业建立了良好的合作关系。通过引进国外先进技术和管理经验,艾迪沃德不断提升自身的竞争力和创新能力。同时,公司还加大了品牌建设的力度,通过参加国际展会、发布新品等方式,提升品牌知名度和美誉度。这些努力不仅为公司带来了更多的国际合作机会,也为艾迪沃德在全球电子行业中树立了良好的品牌形象。

CSB公司的发展小趣事

在电子行业的初期,CSB公司以其独特的技术创新能力脱颖而出。公司研发团队在某一关键领域取得了重大突破,开发出了一款具有市场竞争力的新产品。这款产品不仅满足了消费者对性能和品质的高要求,还凭借其独特的设计和功能,吸引了大量忠实用户。随着产品的热销,CSB公司的知名度逐渐提升,市场份额也稳步增长。

ENOCEAN公司的发展小趣事

为了进一步推动其技术在建筑行业的应用,EnOcean发起并建立了EnOcean联盟。该联盟由来自建筑行业的400多家公司组成,致力于推广基于EnOcean无线标准的免维护无线解决方案。通过与联盟成员的合作,EnOcean不断拓展其市场份额,并为智慧楼宇及能源管理提供更稳定的硬件解决方案。

Andigilog公司的发展小趣事

为了进一步推动其技术在建筑行业的应用,EnOcean发起并建立了EnOcean联盟。该联盟由来自建筑行业的400多家公司组成,致力于推广基于EnOcean无线标准的免维护无线解决方案。通过与联盟成员的合作,EnOcean不断拓展其市场份额,并为智慧楼宇及能源管理提供更稳定的硬件解决方案。

Electro-Optical Systems Inc公司的发展小趣事

为了保持技术领先地位和市场竞争力,EOS不断加大研发投入,积极推动产品创新。他们与多所知名高校和研究机构建立产学研合作关系,共同开展前沿技术研究。同时,EOS也积极拓展国际市场,产品出口到欧美、东南亚等多个国家和地区。通过持续创新和国际拓展,EOS在红外探测器领域的地位日益稳固。

Frolyt Condensers & Elements GmbH公司的发展小趣事

随着业务规模的扩大,Frolyt Condensers & Elements GmbH意识到全球化布局的重要性。2010年,公司制定了全球化战略,首先在亚洲设立了生产基地,以利用当地丰富的资源和低廉的劳动力成本。随后几年间,Frolyt还通过一系列并购活动,收购了多家在特定市场具有影响力的电容器制造商,进一步巩固了其在全球市场的地位。这些并购不仅扩大了Frolyt的产品线,还增强了其技术研发能力和市场服务能力。

问答坊 | AI 解惑

DSP学习进阶

学习TI的各种DSP,本着循序渐进的原则,可以分为多个层次。根据我多年开发DSP的经验,在这里总结一下各个层次的进阶: 1、DSP2000(除了2812): 进阶:标准C -> C和汇编混合编程 说明:把DSP2000当作单片机来玩就可以了,非常简单。 2、DS ...…

查看全部问答>

历届电赛元器件清单

本帖最后由 paulhyde 于 2014-9-15 09:12 编辑 历届带赛元器件清单  …

查看全部问答>

USB2LPT 1.4版 制作说明

电子元件 You need:     * CY7C68013A-56LFXC (»FX2LP«)     * Quartz crystal 24000 kHz [fundamental]     * LD1117S33 (Voltage regulator)     * USB receptacle (USB BG )   ...…

查看全部问答>

高手帮忙(有关工控嵌入式的问题)!!!

最近得到一块嵌入式的板子,搞不懂,朋友说像你这种菜鸟就别搞了,没戏。郁闷ING。 请教各位高手,给点建议,此板如何应用?还需要什么配件?如需系统的话,安装什么系统? 此帖非广告贴,板子应该是02年产的,已经停产了,下面为网上找到的具体参 ...…

查看全部问答>

单片机 数码管显示

刚开始学单片机,一步一步的对着视频学,并写些简单的程序等,学到了单片机控制数码管的亮灭,可是我写的程序就是不能 控制,请朋友们帮我看看是怎么回事。 这是我写的程序 #include void delay(unsigned int cnt) { while(--cnt); } voi ...…

查看全部问答>

如何将0~10v电压输入转换为10~0v电压输出?

再通过电位器让输入电压与输出电压之间有个线性比例关系。请大家帮忙看看,谢谢。…

查看全部问答>

输入捕获模式只能测量一个周期吗?

                                 在输入捕获模式下,如果想测量输入信号的频率,只能测量相邻两个边沿的时间间隔码?那样岂不是很容易被干扰信号所 ...…

查看全部问答>

敢问一句,论坛里玩LaunchPad的童鞋,win7下能驱动不,为嘛我的驱动安装不了啊?

IAR自带的驱动试过了,不行从别的地方下的驱动也驱动不了各位有什么建议吗?…

查看全部问答>

【项目外包】做一个mos开关并测试

做一个mos开关并测试 项目预算:¥ 5,000~10,000 开发周期:20天 项目分类: 嵌入式 竞标要求: 项目标签: mos ...…

查看全部问答>