接上篇博客,测试蓝牙例程通过后,接下来实现利用CC2642控制板上的SPI接口NAND Flash和EEPROM。
软件:simplelink_cc26x2_sdk_2_30_00_34。
工具:Win10 64位PC,CCS8.2
一、TI SPI接口例程介绍
TI为CC2642开发提供了蓝牙协议栈,当然也包括外设的驱动,协议栈功能非常全面,涉及到了所有CC2642的资源。本文主要利用spimaster_CC26X2R1_LAUNCHXL_tirtos_ccs例程中的SPI接口来调试FLASH。TI为用户提供SPI.c和SPI.h两个文件用于控制SPI总线,在SPI.c中的函数会向下调用SPICC26X2DMA.c和SPICC26X2DMA.h,使能DMA实现和更底层的通信。总结一下调用关系如下图所示:对于用户,只需关注SPI.c和SPI.h即可,下层的具体寄存器操作可以放心的交给协议栈的层层调用来完成。
在SPI.c中,我们能用到的函数列表和SPICC26X2DMA.c对应关系如下表:那就是说,只要操作如下图中的几个函数(还有一个SPI_Params_init()),就可以实现对SPI接口的配置,并使用SPI总线了,是不是so easy!
SPI_init:初始化SPI驱动,在使用SPI时,需要优先调用该函数。
SPI_Params_init(&spiParams): 初始化SPI参数,关于SPI的工作模式,传输速率和帧形式等,都在spiParams中设置。具体内容如下图所示:
这里的transferMode用来定义SPI的两种模式,分别是Blocking阻塞模式和Callback模式,阻塞模式会阻塞task的执行,直到SPI传输完成。Callback模式需要另生成一个回调函数,不会阻塞任务的执行,当SPI传输完成进入回调函数中。frameFormat用来设置SPI的时间相位极性,这个需要参考slave的要求,像本文用到的flash只支持00和11模式。在SPI_init()函数调用后就需要调用该函数初始化SPI参数。
SPI_OPEN():用于开启SPI驱动,将上面的配置参数具体执行,执行该函数后,SPI就准备就绪,可以开始SPI操作了。
SPI_transfer():SPI数据接收和发送函数,调用该函数会使能UDMA,接收和发送数据。
SPI_close():关闭SPI,如果想再次使用SPI,需要重新OPEN。
SPI_control():发送控制命令配置SPI接口,举个例子,当使用多从机时,每个从机有独立的CS,可以使用该命令来切换当前CS。
SPI_transferCancel():取消spi transfer命令,只能用于CallBack模式下。
以上就是SPI.c中几个函数,通过操作这几个函数,就可以实现SPI通信了。至于SPI.c是如何调用DMA函数的呢?设置在CC26X2R1_LAUNCHXL.c函数中。截图如下:
上图中.fxnTablePtr中是函数指针列表,对SPI的操作就是通过这些函数实现;.hwAttrs中设置了SPI的硬件接口,如下图所示:包括引脚定义默认Txbuf数据等:
这里重点说一下CS引脚的设置,如果使用HW CS,那就在该出分配CS引脚,对于多从机,可以使用上文提到的SPI_control函数切换CS。如果使用SW CS,那么该处引脚定义成PIN_UNASSIGNED(表明没有该引脚)。然后在程序中自行上下拉CS引脚。**.minDmaTransferSize用来设置启动DMA传输的门限值,当传输数据小于该值,不会使能DMA传输,当传输值大于该值,才会调用DMA传输。**官方解释截图如下:
以上是SPI开发用到的函数介绍,具体例程会在第四节程序实现中介绍。
二、SPI NAND FLASH控制介绍
本文使用的SPI NAND FLASH型号为W25N01GV,是winbond的1Gbit nand flash.一个block有64个page,一个page 2Kbytes.单次操作最小擦除一个block,最大写入一个page,写入超过一个page,则会覆盖之前写入的数据,支持Fast读,可以一个读命令读取所有内存数据。内置buffer,读写的数据先缓存到buffer,然后再输入输出,所以读写指令都需要分为两步完成。接下来具体介绍关于NAND FLASH的操作:
(1)FLASH写保护:flash具有三个状态寄存器,其中寄存器1用来设置写保护,定义如下图:
该寄存器可以设置写保护的范围,以及是硬件写保护还是软件写保护,默认情况下所有内存写保护打开,所以使用的第一步是改写该寄存器,否则只能读写buffer,不能访问内存(我就被这点给坑了)。
(2)坏块管理:总所周知,NAND FLASH在出厂和使用过程中会产生坏块,受技术限制这是不可避免的。该芯片提供了额外20block空间用于出现坏块时的替换。坏块的检查方法是读block的page0的第一个字节,如果非0XFF,表明该块是坏块(在擦除后读)。在检测到出现坏块后,可以调用A1h指令来重新映射坏块,具体操作时序如下图:
(3)块擦除:FLASH的架构只能将1改为0,不能将0写为1,所以在写之前需要擦除块。该芯片支持块擦除一种方式,一次性擦除一block128KB的数据。块擦除操作执行前需要使能WEL位,操作指令为0x06.使能后可以在状态寄存器3中查看。
(4)写操作:FLASH的写操作分为两步,首先将数据发送到flash内部的buffer中,然后执行page写操作,将数据从buffer写入到flash内存。具体如下:执行02h或者84h指令(区别是02h在写入后会复位buffer)将数据写入buffer,数据长度小于等于一个page(2KB),写地址为column地址,16位地址的低12位有效。写入初始地址后,随后每写入一个字节,地址自增1,直到满page,如果还有数据,那么会重新从page的开始字节复写。时序如下:
接下来第二步是page写,将buffer中数据写入flash,指令是10h。写地址是page地址,由10位block地址和6位page地址组成。时序如下图所示:
(5)读操作:读数据采用Fast模式时可以读取整个flash的数据。同样读数据也分为两步:首先将flash中数据缓存到buffer,操作指令是13h,读地址是page地址,定义同上。时序如下:
接下来是将buffer中数据读入master。有两种读方式,普通读(03h)和fast读(0Bh),二者区别是Fast模式在读完一个page后可以继续读下一page,直到所以内存数据读完。时序图如下:
Fast模式与此相同,只是指令不同。这里读模式又分为buffer mode和continue mode,区别是continue mode不需要指定column地址,从page的第一个字节开始读。
(6)ECC校验:FLASH带有ECC校验功能,不使能ECC功能时,一个page2112字节,默认情况下使能,可以在状态寄存器2中设置不使能。
(7)写使能:在每次写操作、擦除操作、坏块管理操作前都需要设置写使能WEL位。操作指令是06h.
三、SPI EEPROM控制介绍
使用的EEPROM型号是M95512RDW6,512kbit EEPROM。SPI接口,一个page128字节,一次写最多1page,读可以多page读。相比flash,写操作前不需要擦除,没有坏块管理。读写操作指令和flash相同,在这里不多介绍。
四、程序实现
首先是SPI参数的设置,参数设置完成后,在函数中按照第一节介绍的函数调用顺序执行,截图如下:
首先执行SPI_init初始化函数,然后配置spiParams,设置相位极性00模式,SPI通信速率,执行SPI_open函数打开SPI接口。
接下来使能eeprom的WEL位,写指令06h给eeprom,这里重点看spi发送数据的实现。本文使用的SW CS,即软件实现CS拉高拉低,.count设置发送数据数量,.txbuf设置发送数据,.rxbuf设置接收数据buffer,本次只需要发,不需接收数据,可以将.rxbuf置0,如果接收buffer置0,接收到的数据将被抛弃。
上图是发送数据,跟使能时结构相同,区别是指令不同。调用SPI_transfer后,会自动使能DMA将数据发送出去,发送完成返回1,注意一点,这里只是将数据从master发送出去,至于slave是否已经写入内存却不一定。通过读状态寄存器3的最低位是否busy,可以知道操作是否完成。
读寄存器命令如上图所示:首先调用SPI_transfer发送命令,然后再次调用SPI_transfer接收数据。读数据与此相同,区别在于读数据量和指令。以上是关于eeprom的部分实现代码,对于需要完整代码和flash读写操作代码的朋友,可以留言邮箱给我。
五、注意要点
1、flash读写时序中的dummy clock,可以通过发送任意字节数据来实现。
2、eeprom和flash的状态寄存器3最低位是busy位,表明当前是否有写任务在进行,在flash的page read和块擦除操作中,需要读状态寄存器来判断当前任务是否执行完成。如果上一任务没有执行完,新输入的指令将被忽略。eeprom中写操作比较读状态寄存器来判断是否完成。
3、flash的状态寄存器1用于不使能写保护,每次上电都需要设置;
4、SPI调用DMA模块,一次最大传输1024 data frames(4-16bits),大于该值的数据将分为多次发送。