首页 理论教育 STM32固件库编程:编写SPI

STM32固件库编程:编写SPI

时间:2023-10-21 理论教育 版权反馈
【摘要】:初始化函数按照步骤:使能时钟、初始化结构体、初始化函数,代码如下:SPI_FLASH_Init()完成了SPI接口的初始化工作,但这只是接口上的初始化,完成接口的设置而已,因此我们还需继续编写W25Q128读与写的操作函数。写双字节函数SPI_FLASH_SendHalfWord()。写允许函数SPI_FLASH_WriteEnable()。等待写结束函数SPI_FLASH_WaitForWriteEnd()。页写函数SPI_FLASH_PageWrite()。缓冲写入函数SPI_FLASH_BufferWrite()。

STM32固件库编程:编写SPI_W25Q128.c

1.编写SPI的基本操作函数

在SPI_W25Q128.c中编写SPI的驱动函数。

(1)SPI初始化函数SPI_FLASH_Init()。

初始化函数按照步骤:使能时钟、初始化结构体、初始化函数,代码如下:

SPI_FLASH_Init()完成了SPI接口的初始化工作,但这只是接口上的初始化,完成接口的设置而已,因此我们还需继续编写W25Q128读与写的操作函数。

(2)写单字节函数SPI_FLASH_SendByte()。

同样地,为了更方便地实现写单个字节,我们需要编写一个写单字节函数。由SPI的通信原理知,主设备向从设备发送数据移位的同时,从设备也向主设备发送数据移位,因此主设备发送1字节数据后,应该接收了从设备发送过来的1字节数据。该函数带返回值。函数编写如下:

该函数有1个形参,有返回值,形参是需要发送的无符号单字节数据,返回值的类型也是无符号单字节。

(3)读单字节函数SPI_FLASH_ReadByte()。

为了更方便地实现Flash的单字节读操作,单字节的读取函数定义如下:

因为发送与接收是同时进行的,读接收数据的8位,意味着需要向从设备发送8位数据,因此函数发送一个字节的Dummy(随机数据),返回接收值。该函数无形参,有返回值,返回值的类型为无符号单字节。

(4)写双字节函数SPI_FLASH_SendHalfWord()。

编写函数如下:

该函数用于发送16位数(适用于帧格式16位模式),与写字节函数类似,只有一个形参,带返回值,返回值也是从设备返回的16位数。

(5)写允许函数SPI_FLASH_WriteEnable()。

编写函数如下:

该函数作用很简单,在写数据之前,通过SendByte函数发送写使能指令。

(6)等待写结束函数SPI_FLASH_WaitForWriteEnd()。

编写函数如下:

等待写结束函数无形参与返回值,只有写结束了,才能进行下一步操作。

(7)扇区删除函数SPI_FLASH_SectorErase()。

编写函数如下:

函数的形参是扇区首地址。函数执行后,首地址之后的一个扇区(4KB)全部被删除。扇区删除指令的格式如表9-5指示。函数首先进行写使能,然后输出片选低电平(低电平有效),再根据指令的数据格式,开始以8个位为扇区删除指令,后面8个位为高地址,再后面8个位为中地址,最后8个位为低地址,传送完数据后,输出片选高电平,等待写结束。

表9-5 Sector Erase(4KB)指令格式

FLASH在写数据之前需要对其进行删除(擦除),这是由FLASH工作原理决定的,为了快速地进行操作,总是将一大片区域的单元一起擦除。为了更好地管理大容量存储器,存储器都会将单元地址组织成块(Block)、扇区(Sector)和页(Page)。块与扇区的关系如图9-18所示。

图9-18 W25Q128结构图

一块W25Q128芯片的16M字节被分为了256个块,每一个块容量为64K字节,256个块的地址编号从高2位开始,比如:Block0的首地址是000000H,块内单元地址范围是000000H~00FFFFH;Block1的首地址是010000H,块内单元地址范围是010000H~01FFFFH。以此类推。每一个块分为16个扇区,每个扇区为4K字节。若Sector0的首地址是xx0000H(xx代表不同块),Sector1的首地址是xx1000H,以此可类推出16个扇区地址首地址。我们主要关心的是每个扇区、每个块的首地址,比如Block1的Sector1的首地址是011000H。为什么只关心首地址呢?因为使用了SectorErase(4KB)指令后,它会把指令后的24位地址开始的连续4KB的单元全部擦除。假设输入的擦除地址不是每个扇区的首地址,会导致一种情况,即函数擦除了两个扇区的数据,但这两个扇区没有完全被擦除。假设输入的擦除地址是011800H,此时的地址落在Sector1~Sector2之间,而扇区擦除是擦除连续4KB的单元,因此Sector1的后半部分与Sector2的前半部分会被擦除,如图9-19所示,因此在使用该函数时,应该输入的地址是扇区的首地址,即4K对齐。

图9-19 首地址与地址范围分布图

而在每一个块与扇区内,都有一个最小区域单位:页(Page),每一页的容量是256B,W25Q128有64K个页。写FLASH时为了提高写入效率,不可能一个字节一个字节地写,在写数据时一次最少写入一页,即一次最少写256B,每页有对应的首地址。

(8)卷删除函数SPI_FLASH_BulkErase()。

编写函数如下:

使用该函数将会整卷删除数据,应慎用。

(9)页写函数SPI_FLASH_PageWrite()。

编写函数如下:

记住,页写入之前,必须对该地址进行删除。页写入函数的形参有三个:第一个形参是需要写入数据的数组指针(需要写入的数据为8位数,以一维数组形式),第二个形参是需要写入的起始地址(32位数),第三个形参是需要写入的数据的容量(多少个8位数)。页写入不像块删除指令那样需要对齐,写入地址可以是非页首地址。写入数据之前,要先擦除这些存储单元

(10)缓冲写入函数SPI_FLASH_BufferWrite()。(www.xing528.com)

页写入函数相对来说比较简单,但不够严谨方便。若要将一个数组的数据写入指定地址,可以编写如下写函数:

上述这段代码是最常用的写函数,也是最长、最复杂的函数。读者通过该函数可以掌握FLASH这一类存储芯片的数据管理方式。该函数有三个形参:第一个形参是需要写入数据数组的指针,第二个形参是写入地址,第三个形参是写入数据长度。程序的基本流程图如图9-20所示。

图9-20 缓冲写入函数流程图

图9-20中的流程主要在于对数据长度的判断与地址的判断,如果写入地址正好页对齐,就比较容易处理,直接在其地址(也是某页的首地址)写数据直到结束。如果不对齐,那么这些数据将跨越某个页,即一部分数据在某页首地址前,一部分数据在某页首地址后,要分开处理这两部分数据。在首地址前的数据在该首地址的前一个页地址写入后,首地址后的数据才开始整页写入。比如要在000155H地址开始写入500个字节的数据,这个地址并没有与页的首地址对齐,而写数据又是按页写入的,因此它涉及3个页,如图9-21所示,500个字节中有171个字节在Page1中,256个字节在Page2中,73个字节在Page3中,程序通过公式计算出前171字节先在Page1页写入,中间256个字节在000200H(Page2的首地址)的地址写入,剩下的73字节在000300H(Page3的首地址)的地址写入。

图9-21 当地址不对齐且大于页容量的地址分布

(11)缓冲读函数SPI_FLASH_BufferRead()。

编写函数如下:

FLASH的读就比较简单了,函数有三个形参:第一个形参是存放读取数据的数组的指针地址,第二个形参是需要读取的地址,第三个形参是读取的数量。使用时要注意形参的数据格式。

(12)芯片ID读函数SPI_FLASH_ReadID()。

编写函数如下:

该函数没有形参,只对W25Q128的ID进行读取,数据返回类型为32位无符号数。

(13)制造商ID和设备ID读函数SPI_FLASH_ReadDeviceID()。

编写函数如下:

其使用方法与SPI_FLASH_ReadDeviceID()函数类似。

(14)开始顺序读函数SPI_FLASH_StartReadSequence()。

编写函数如下:

该函数相当于开始读取的信号,只有一个形参,形参为读取数据的目的地址。

(15)W25Q128断电函数SPI_Flash_PowerDown()。

编写函数如下:

该函数用于为W25Q128断电,可以节约能耗。

(16)W25Q128唤醒函数SPI_Flash_WAKEUP()。

编写函数如下:

该函数用于唤醒被断电了的W25Q128。

(17)超时函数。

编写函数如下:

该函数用于检测总线是否有超时,有一个形参,也有返回值。形参是错误码,若出现超时,函数输出超时错误信息码,返回0。

上述17个函数构成了W25Q128的基本操作函数,这些函数可以作为通用W25Q128/64/32等芯片的函数使用。

2.编写SPI-W25Q128.c

SPI_W25Q128.c函数如下:

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈