STM32学习笔记之硬件SPI读写与极性设置

发布者:温柔心情最新更新时间:2017-09-09 来源: eefocus关键字:STM32  SPI读写  极性设置 手机看文章 扫描二维码
随时随地手机看文章

【软件中如何设置SPI的极性和相位】 SPI分主设备和从设备,两者通过SPI协议通讯。 而设置SPI的模式,是从设备的模式,决定了主设备的模式。 所以要先去搞懂从设备的SPI是何种模式,然后再将主设备的SPI的模式,设置和从设备相同的模式,即可正常通讯。 对于从设备的SPI是什么模式,有两种: (1)固定的,有SPI从设备硬件决定的 SPI从设备,具体是什么模式,相关的datasheet中会有描述,需要自己去datasheet中找到相关的描述,即: 关于SPI从设备,在空闲的时候,是高电平还是低电平,即决定了CPOL是0还是1; 然后再找到关于设备是在上升沿还是下降沿去采样数据,这样就是,在定了CPOL的值的前提下,对应着可以推算出CPHA是0还是1了。举例1: CC2500 - Low-Cost Low-Power 2.4 GHz RF Transceiver的datasheet中SPI的时序图是: 
从图中可以看到,最开始的SCLK和结束时候的SCLK,即空闲时刻的SCLK,是低电平,推导出CPOL=0,然后可以看到数据采样的时候,即数据最中间的那一点,对应的是SCLK的第一个边沿,所以CPHA=0(此时对应的是上升沿)。  举例2: SSD1289 - 240 RGB x 320 TFT LCD Controller Driver的datasheet中提到: “SDI is shifted into 8-bit shift register on every rising edge of SCK in the order of data bit 7, data bit 6 …… data bit 0.” 意思是,数据是在上升沿采样,所以可以断定是CPOL=0,CPHA=0,或者CPOL=1,CPHA=1的模式,但是至于是哪种模式。 按理来说,接下来应该再去确定SCLK空闲时候是高电平还是低电平,用以确定CPOL是0还是1,但是datasheet中没有提到这点。 所以,此处,目前不太确定,是两种模式都支持,还是需要额外找证据却确定CPOL是0还是1. (2)可配置的,由软件自己设定 从设备也是一个SPI控制器,4种模式都支持,此时只要自己设置为某种模式即可。 然后知道了从设备的模式后,再去将SPI主设备的模式,设置为和从设备模式一样,即可。  对于如何配置SPI的CPOL和CPHA的话,不多细说,多数都是直接去写对应的SPI控制器中对应寄存器中的CPOL和CPHA那两位,写0或写1即可。 举例: C8051F347中的SPI就是一个SPI的controller控制器,即支持软件配置CPOL和CPHA的值,四种模式都支持,此处C8051F347作为SPI从设备,设置了CPOL=1,CPHA=0的模式,因此,此处对应主芯片中的SPI控制器,作为Master主设备,其SPI的模式也要设置为CPOL=1,CPHA=0,即可。






【SPI的读写程序设计】 文中标红的是特别注意看的地方主要是熟悉flash芯片的指令集,以及存储芯片扇区和块的理解,最重要的是擦除都是以扇区擦除的方式。


本节将利用SPI来实现对外部FLASH(W25X16)的读写,并将结果显示在TFTLCD模块上。本节分为如下几个部分:

3.17.1 SPI 简介

3.17.2 硬件设计

3.17.3 软件设计

3.17.4 下载与测试


  1 SPI 简介

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

SPI接口一般使用4条线:

MISO 主设备数据输入,从设备数据输出。

MOSI 主设备数据输出,从设备数据输入。

SCLK时钟信号,由主设备产生。

CS从设备片选信号,由主设备控制。

SPI主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等。

SPI总线四种工作方式 SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果CPOL=0,串行同步时钟的空闲状态为低电平;如果CPOL=1,串行同步时钟的空闲状态为高电平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样。SPI主模块和与之通信的外设备时钟相位和极性应该一致。


不同时钟相位下的总线数据传输时序见下图:



图3.17.1.1不同时钟相位下的总线传输时序(CPHA=0/1)


STM32的SPI功能很强大,SPI时钟最多可以到18Mhz,支持DMA,可以配置为SPI协议或者I2S协议。


本节,我们将利用STM32的SPI来读取外部SPIFLASH芯片(W25X16),实现类似上节的功能。这里对SPI我们只简单介绍一下SPI的使用,STM32的SPI详细介绍请参考《STM32参考手册》第422页,22节。然后我们再介绍下SPIFLASH芯片。

这节,我们使用STM32的SPI1的主模式,下面就来看看SPI1部分的设置步骤吧,STM32的主模式配置步骤如下:

1)配置相关引脚的复用功能,使能SPI1时钟。

我们要用SPI1,第一步就要是能SPI1的时钟,SPI1的时钟通过APB2ENR的第12位来设置。其次要设置SPI1的相关引脚为复用输出,这样才会连接到SPI1上否则这些IO口还是默认的状态,也就是标准输入输出口。这里我们使用的是PA5、6、7这3个(SCK.、MISO、MOSI,CS使用软件管理方式),所以设置这三个为复用IO。

2)设置SPI1工作模式。

这一步全部是通过SPI1_CR1来设置,我们设置SPI1为主机模式,设置数据格式为8位,然后通过CPOL和CPHA位来设置SCK时钟极性及采样方式。并设置SPI1的时钟频率(最大18Mhz),以及数据的格式(MSB在前还是LSB在前)。

3)使能SPI1。

这一步通过SPI1_CR1的bit6来设置,以启动SPI1,在启动之后,我们就可以开始SPI通讯了。

SPI1的使用就介绍到这里,接下来介绍一下W25X16。W25X16是华邦公司推出的继W25X10/20/40/80(从1Mb~8Mb)后容量更大的FLASH产品,W25X16的容量为16Mb,还有容量更大的W25X32/64,ALIENTEK所选择的W25X16容量为16Mb,也就是2M字节,同AT45DB161是一样大小的。

W25X16将2M的容量分为32个块(Block),每个块大小为64K字节,每个块又分为16个扇区(Sector),每个扇区4K个字节。W25X16的最少擦除单位为一个扇区,也就是每次必须擦除4K个字节。这样我们需要给W25X16开辟一个至少4K的缓存区,这样对SRAM要求比较高(相对于AT45DB161来说),但是它有价格及供货上的优势。

W25X16的差些周期为10000次,具有20年的数据保存期限,支持电压为2.7~3.6V,W25X16支持标准的SPI,还支持双输出的SPI,最大SPI时钟可以到75Mhz(双输出时相当于150Mhz),更多的W25X16的介绍,请参考W25X16的DATASHEET。


2 硬件设计

本节实验功能简介:开机的时候先检测W25X16是否存在,然后在主循环里面用1个按键用来执行写入W25X16的操作,另外一个按键用来执行读出操作,在TFTLCD模块上显示相关信息。同时用DS0提示程序正在运行。

所要用到的硬件资源如下:

1)STM32F103RBT6。

2)DS0(外部LED0)。

3)KEY0和KEY2。

4)TFTLCD液晶模块。

5)W25X16。

前面4部分的资源,我们前面已经介绍了,请大家参考相关章节。这里只介绍W25X16与STM32的连接,板上的W25X16是直接连在STM32F103RBT6上的,连接关系如下图:



图3.17.2.1STM32F103RBT6与W25X16连接电路图

 


3 软件设计

打开上一节的工程,首先在HARDWARE文件夹下新建一个FLASH的文件夹和SPI的文件夹。然后新建一个flash.c和flash.h的文件保存在FLASH文件夹下,新建spi.c和spi.h的文件,保存在SPI文件夹下,并将这两个文件夹加入头文件包含路径。

打开spi.c文件,输入如下代码:

#include"spi.h"                                  

//SPI口初始化

//这里针是对SPI1的初始化

voidSPIx_Init(void)

{  

     RCC->APB2ENR|=1<<2;       //PORTA时钟使能             

     RCC->APB2ENR|=1<<12;      //SPI1时钟使能

                   

     //这里只针对SPI口初始化

     GPIOA->CRL&=0X000FFFFF;

     GPIOA->CRL|=0XBBB00000;//PA5.6.7复用           

     GPIOA->ODR|=0X7<<5;    //PA5.6.7上拉      

     SPI1->CR1|=0<<10;//全双工模式          

     SPI1->CR1|=1<<9; //软件nss管理

     SPI1->CR1|=1<<8; 

     SPI1->CR1|=1<<2; //SPI主机

     SPI1->CR1|=0<<11;//8bit数据格式       

     SPI1->CR1|=1<<1; //空闲模式下SCK为1 CPOL=1

     SPI1->CR1|=1<<0; //数据采样从第二个时间边沿开始,CPHA=1 

     SPI1->CR1|=7<<3; //Fsck=Fcpu/256

     SPI1->CR1|=0<<7; //MSBfirst  

     SPI1->CR1|=1<<6; //SPI设备使能

     SPIx_ReadWriteByte(0xff);//启动传输               

}  


//SPI 速度设置函数

//SpeedSet:

//SPI_SPEED_2   2分频   (SPI 36M@sys 72M)

//SPI_SPEED_8   8分频   (SPI 9M@sys 72M)

//SPI_SPEED_16  16分频  (SPI 4.5M@sys 72M)

//SPI_SPEED_256256分频 (SPI281.25K@sys 72M)


voidSPIx_SetSpeed(u8 SpeedSet)

{

     SPI1->CR1&=0XFFC7;//Fsck=Fcpu/256

     if(SpeedSet==SPI_SPEED_2)//二分频

     {

                 SPI1->CR1|=0<<3;//Fsck=Fpclk/2=36Mhz          

     }else if(SpeedSet==SPI_SPEED_8)//八分频

     {

                 SPI1->CR1|=2<<3;//Fsck=Fpclk/8=9Mhz

     }else if(SpeedSet==SPI_SPEED_16)//十六分频

     {

                 SPI1->CR1|=3<<3;//Fsck=Fpclk/16=4.5Mhz

     }else                                         //256分频

     {

                 SPI1->CR1|=7<<3;//Fsck=Fpclk/256=281.25Khz 低速模式

     }

     SPI1->CR1|=1<<6; //SPI设备使能          

}

//SPIx读写一个字节

//TxData:要写入的字节

//返回值:读取到的字节

u8SPIx_ReadWriteByte(u8 TxData)

{     u8 retry=0;                                           

     while((SPI1->SR&1<<1)==0)//等待发送区空     

     {  retry++;

                 if(retry>200)return 0;

     }                                   

     SPI1->DR=TxData;                     //发送一个byte

     retry=0;

     while((SPI1->SR&1<<0)==0) //等待接收完一个byte 

     {      retry++;

                if(retry>200)return 0;

     }                                                                                      

     return SPI1->DR;          //返回收到的数据                                                  

}


此部分代码主要初始化SPI,这里我们选择的是SPI1,所以在SPIx_Init函数里面,其相关的操作都是针对SPI1的,其初始化步骤和我们上面介绍的一样。在初始化之后,我们就可以开始使用SPI1了,在SPIx_Init函数里面,把SPI1的波特率设置成了最低(281.25Khz)。在外部函数里面,我们通过SPIx_SetSpeed来设置SPI1的速度,而我们的数据发送和接收则是通过SPIx_ReadWriteByte函数来实现的。、

保存spi.c,并把该文件加入RDWARE组下面,然后我们打开spi.h在里面输入如下代码:

#ifndef __SPI_H

#define __SPI_H

#include "sys.h"

// SPI总线速度设置

#define SPI_SPEED_2   0

#define SPI_SPEED_8   1

#define SPI_SPEED_16  2

#define SPI_SPEED_256 3 

void SPIx_Init(void);                                  //初始化SPI口

void SPIx_SetSpeed(u8 SpeedSet); //设置SPI速度  

u8 SPIx_ReadWriteByte(u8 TxData);//SPI总线读写一个字节

#endif

     此部分代码我们就不多介绍了,保存spi.h,然后我们打开flash.c,在里面输入如下代码:

#include "flash.h"

#include "spi.h"

#include "delay.h" 

//4Kbytes为一个Sector

//16个扇区为1个Block

//W25X16

//容量为2M字节,共有32个Block,512个Sector

//初始化SPI FLASH的IO


void SPI_Flash_Init(void)

{

     RCC->APB2ENR|=1<<2;       //PORTA时钟使能                 

     //这里

     GPIOA->CRL&=0XFFF000FF;

     GPIOA->CRL|=0X00033300;//PA2.3.4推挽           

     GPIOA->ODR|=0X7<<2;    //PA2.3.4上拉

     SPIx_Init();                      //初始化SPI

 

//读取SPI_FLASH的状态寄存器

//BIT7 6   5   4  3   2   1   0

//SPR  RV  TB BP2 BP1 BP0 WEL BUSY

//SPR:默认0,状态寄存器保护位,配合WP使用

//TB,BP2,BP1,BP0:FLASH区域写保护设置

//WEL:写使能锁定

//BUSY:忙标记位(1,忙;0,空闲)

//默认:0x00

u8 SPI_Flash_ReadSR(void)  

     u8byte=0;  

     SPI_FLASH_CS=0;                            //使能器件  

     SPIx_ReadWriteByte(W25X_ReadStatusReg);    //发送读取状态寄存器命令   

     byte=SPIx_ReadWriteByte(0Xff);             //读取一个字节 

     SPI_FLASH_CS=1;                            //取消片选    

     returnbyte;  

}


//写SPI_FLASH状态寄存器

//只有SPR,TB,BP2,BP1,BP0(bit7,5,4,3,2)可以写!!!

void SPI_FLASH_Write_SR(u8 sr)  

{   SPI_FLASH_CS=0;                            //使能器件  

     SPIx_ReadWriteByte(W25X_WriteStatusReg);   //发送写取状态寄存器命令   

     SPIx_ReadWriteByte(sr);               //写入一个字节 

     SPI_FLASH_CS=1;                            //取消片选                 

}  


//SPI_FLASH写使能         

//将WEL置位  

void SPI_FLASH_Write_Enable(void)  

{

     SPI_FLASH_CS=0;                            //使能器件  

   SPIx_ReadWriteByte(W25X_WriteEnable);      //发送写使能 

     SPI_FLASH_CS=1;                            //取消片选                 

}


//SPI_FLASH写禁止         

//将WEL清零 

void SPI_FLASH_Write_Disable(void)  

{ SPI_FLASH_CS=0;                            //使能器件  

   SPIx_ReadWriteByte(W25X_WriteDisable);     //发送写禁止指令   

     SPI_FLASH_CS=1;                            //取消片选                 

}                               

//读取芯片ID W25X16的ID:0XEF14

u16 SPI_Flash_ReadID(void)

{u16Temp = 0;    

     SPI_FLASH_CS=0;                                              

     SPIx_ReadWriteByte(0x90);//发送读取ID命令      

     SPIx_ReadWriteByte(0x00);           

     SPIx_ReadWriteByte(0x00);           

     SPIx_ReadWriteByte(0x00);                                              

     Temp|=SPIx_ReadWriteByte(0xFF)<<8; 

     Temp|=SPIx_ReadWriteByte(0xFF);        

     SPI_FLASH_CS=1;                                              

     returnTemp;

}                              

//读取SPI FLASH 

//在指定地址开始读取指定长度的数据

//pBuffer:数据存储区

//ReadAddr:开始读取的地址(24bit)

//NumByteToRead:要读取的字节数(最大65535)

void SPI_Flash_Read(u8* pBuffer,u32ReadAddr,u16 NumByteToRead)  

{  u16 i;                                                                                                                                                   

     SPI_FLASH_CS=0;                            //使能器件  

   SPIx_ReadWriteByte(W25X_ReadData);         //发送读取命令  

   SPIx_ReadWriteByte((u8)((ReadAddr)>>16));  //发送24bit地址   

   SPIx_ReadWriteByte((u8)((ReadAddr)>>8));  

   SPIx_ReadWriteByte((u8)ReadAddr);  

   for(i=0;i

     {

       pBuffer=SPIx_ReadWriteByte(0XFF);  //循环读数 

   }

     SPI_FLASH_CS=1;                            //取消片选                 



//SPI在一页(0~65535)内写入少于256个字节的数据

//在指定地址开始写入最大256字节的数据

//pBuffer:数据存储区

//WriteAddr:开始写入的地址(24bit)

//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!           

void SPI_Flash_Write_Page(u8* pBuffer,u32WriteAddr,u16 NumByteToWrite)

{

     u16 i; 

   SPI_FLASH_Write_Enable();                  //SET WEL

     SPI_FLASH_CS=0;                            //使能器件  

   SPIx_ReadWriteByte(W25X_PageProgram);      //发送写页命令  

   SPIx_ReadWriteByte((u8)((WriteAddr)>>16)); //发送24bit地址   

   SPIx_ReadWriteByte((u8)((WriteAddr)>>8));  

   SPIx_ReadWriteByte((u8)WriteAddr);  

   for(i=0;i

     SPI_FLASH_CS=1;                            //取消片选

     SPI_Flash_Wait_Busy();                                                    //等待写入结束

}



//无检验写SPI FLASH

//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!

//具有自动换页功能

//在指定地址开始写入指定长度的数据,但是要确保地址不越界!

//pBuffer:数据存储区

//WriteAddr:开始写入的地址(24bit)

//NumByteToWrite:要写入的字节数(最大65535)

//CHECK OK

void SPI_Flash_Write_NoCheck(u8*pBuffer,u32 WriteAddr,u16 NumByteToWrite)  

{                                                    

     u16pageremain;   

     pageremain=256-WriteAddr%256;//单页剩余的字节数                               

     if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256个字节

     while(1)

     {            

                 SPI_Flash_Write_Page(pBuffer,WriteAddr,pageremain);

                 if(NumByteToWrite==pageremain)break;//写入结束了

                 else//NumByteToWrite>pageremain

                 {

                             pBuffer+=pageremain;

                             WriteAddr+=pageremain;           

 

                             NumByteToWrite-=pageremain;                             //减去已经写入了的字节数

                             if(NumByteToWrite>256)pageremain=256;//一次可以写入256个字节

                             elsepageremain=NumByteToWrite;            //不够256个字节了

                 }

     };            

}



//写SPI FLASH 

//在指定地址开始写入指定长度的数据

//该函数带擦除操作!

//pBuffer:数据存储区

//WriteAddr:开始写入的地址(24bit)

//NumByteToWrite:要写入的字节数(最大65535)                     

u8 SPI_FLASH_BUF[4096];

void SPI_Flash_Write(u8* pBuffer,u32WriteAddr,u16 NumByteToWrite)  

{

     u32secpos;

     u16secoff;

     u16secremain;     

     u16 i;   

     secpos=WriteAddr/4096;//扇区地址0~511 for w25x16

     secoff=WriteAddr%4096;//在扇区内的偏移

     secremain=4096-secoff;//扇区剩余空间大小  

     if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096个字节

     while(1)

     {         

                 SPI_Flash_Read(SPI_FLASH_BUF,secpos*4096,4096);//读出整个扇区的内容

                 for(i=0;i

                 {

                             if(SPI_FLASH_BUF[secoff+i]!=0XFF)break;//需要擦除    

                 }

                 if(i

                 {

                             SPI_Flash_Erase_Sector(secpos);//擦除这个扇区

                             for(i=0;i

                             {

                                         SPI_FLASH_BUF[i+secoff]=pBuffer;   

                             }

                             SPI_Flash_Write_NoCheck(SPI_FLASH_BUF,secpos*4096,4096);//写入整个扇区 

 

                 }elseSPI_Flash_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.                                                   

                 if(NumByteToWrite==secremain)break;//写入结束了

                 else//写入未结束

                 {

                             secpos++;//扇区地址增1

                             secoff=0;//偏移位置为0             

 

                             pBuffer+=secremain;  //指针偏移

                             WriteAddr+=secremain;//写地址偏移         

                             NumByteToWrite-=secremain;                                         //字节数递减

                             if(NumByteToWrite>4096)secremain=4096;          //下一个扇区还是写不完

                             elsesecremain=NumByteToWrite;                                    //下一个扇区可以写完了

                 }         

     };                     

}



//擦除整个芯片

//整片擦除时间:

//W25X16:25s

//W25X32:40s

//W25X64:40s

//等待时间超长...

void SPI_Flash_Erase_Chip(void)  

{                                             

   SPI_FLASH_Write_Enable();                  //SET WEL

   SPI_Flash_Wait_Busy();  

     SPI_FLASH_CS=0;                            //使能器件  

   SPIx_ReadWriteByte(W25X_ChipErase);        //发送片擦除命令 

     SPI_FLASH_CS=1;                            //取消片选                 

     SPI_Flash_Wait_Busy();                                                    //等待芯片擦除结束

}  

//擦除一个扇区

//Dst_Addr:扇区地址0~511 for w25x16

//擦除一个山区的最少时间:150ms

void SPI_Flash_Erase_Sector(u32Dst_Addr)  

{  

     Dst_Addr*=4096;

   SPI_FLASH_Write_Enable();                 //SET WEL         

   SPI_Flash_Wait_Busy();  

     SPI_FLASH_CS=0;                            //使能器件  

   SPIx_ReadWriteByte(W25X_SectorErase);      //发送扇区擦除指令

   SPIx_ReadWriteByte((u8)((Dst_Addr)>>16));  //发送24bit地址   

   SPIx_ReadWriteByte((u8)((Dst_Addr)>>8));  

   SPIx_ReadWriteByte((u8)Dst_Addr); 

     SPI_FLASH_CS=1;                            //取消片选                 

   SPI_Flash_Wait_Busy();                                                     //等待擦除完成

//等待空闲

void SPI_Flash_Wait_Busy(void)  

{  

     while((SPI_Flash_ReadSR()&0x01)==0x01);  // 等待BUSY位清空

//进入掉电模式

void SPI_Flash_PowerDown(void)  

{

     SPI_FLASH_CS=0;                            //使能器件  

   SPIx_ReadWriteByte(W25X_PowerDown);        //发送掉电命令 

     SPI_FLASH_CS=1;                            //取消片选                 

   delay_us(3);                               //等待TPD 

}  

//唤醒

void SPI_Flash_WAKEUP(void)  

     SPI_FLASH_CS=0;                            //使能器件  

   SPIx_ReadWriteByte(W25X_ReleasePowerDown);   // send W25X_PowerDown command 0xAB   

     SPI_FLASH_CS=1;                            //取消片选                 

   delay_us(3);                               //等待TRES1

}


此部分代码里面一个最关键的函数就是voidSPI_Flash_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite),该函数可以在W25X16的任意地址开始写入任意长度(必须不超过W25X16的容量)的数据。我们这里简单介绍一下思路:先获得首地址(WriteAddr)所在的扇区,并计算在扇区内的偏移,然后判断要写入的数据长度是否超过本扇区所剩下的长度,如果不超过,再先看看是否要删除,如果不要,则直接写入数据即可,如果要则读出整个扇区,在偏移处开始写入指定长度的数据,然后擦除这个扇区,再一次性写入。当所需要写入的数据长度超过一个扇区的长度的时候,我们先按照前面的步骤把扇区剩余部分写完,再在新扇区内执行同样的操作,如此循环,直到写入结束。


其他的代码就比较简单了,我们这里不介绍了。保存falsh.c,然后加入到HARDWARE组下面,再打开flahs.h,在该文件里面输入如下代码:

#ifndef __FLASH_H

#define __FLASH_H                                      

#include "sys.h"

//Mini STM32开发板

//W25X16 驱动函数

//正点原子@ALIENTEK

//2010/6/13

//V1.0

#define       SPI_FLASH_CSPAout(2)  //选中FLASH                                                          

////////////////////////////////////////////////////////////////////////////

//W25X16读写

#define FLASH_ID 0XEF14

//指令表

#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

 

void SPI_Flash_Init(void);

u16 SPI_Flash_ReadID(void);             //读取FLASH ID

u8  SPI_Flash_ReadSR(void);        //读取状态寄存器

void SPI_FLASH_Write_SR(u8 sr);              //写状态寄存器

void SPI_FLASH_Write_Enable(void);  //写使能

void SPI_FLASH_Write_Disable(void);        //写保护

void SPI_Flash_Read(u8* pBuffer,u32ReadAddr,u16 NumByteToRead);   //读取flash

void SPI_Flash_Write(u8* pBuffer,u32WriteAddr,u16 NumByteToWrite);//写入flash

void SPI_Flash_Erase_Chip(void);                  //整片擦除

void SPI_Flash_Erase_Sector(u32Dst_Addr);//扇区擦除

void SPI_Flash_Wait_Busy(void);           //等待空闲

void SPI_Flash_PowerDown(void);           //进入掉电模式

void SPI_Flash_WAKEUP(void);                               //唤醒

#endif

这里面就定义了一些与W25X16操作相关的命令,这些命令在W25X16的数据手册上都有详细的介绍,感兴趣的大家可以参考该数据手册,其他的就没啥好说的了。保存此部分代码。

最后,我们在test.c里面,修改main函数如下:

//要写入到W25X16的字符串数组

const u8 TEXT_Buffer[]={"MiniSTM32SPI TEST"};

#define SIZE sizeof(TEXT_Buffer)  

int main(void)

{              

     u8key;

     u16i=0;

     u8datatemp[SIZE];

                               

     Stm32_Clock_Init(9);//系统时钟设置

     delay_init(72);               //延时初始化

     uart_init(72,9600);//串口1初始化  

     LED_Init();                               //LED初始化

     KEY_Init();                               //按键初始化

     LCD_Init();                   //TFTLCD液晶初始化

     SPI_Flash_Init();   //SPI FLASH 初始化

 

 

     POINT_COLOR=RED;//设置字体为蓝色              

     LCD_ShowString(60,50,"MiniSTM32");

     LCD_ShowString(60,70,"SPITEST");    

     LCD_ShowString(60,90,"ATOM@ALIENTEK");

     LCD_ShowString(60,110,"2010/6/11");   

                              

     while(SPI_Flash_ReadID()!=FLASH_ID)//检测不到W25X16

     {            

                 i=SPI_Flash_ReadID();

                 printf("ID:%d",i);

                 LCD_ShowString(60,130,"W25X16Check Failed!");

                 delay_ms(500);

                 LCD_ShowString(60,130,"   Please Check!    ");

                 delay_ms(500);

                 LED0=!LED0;//DS0闪烁

     }

     LCD_ShowString(60,130,"W25X16Ready!");

     //显示提示信息

     LCD_ShowString(60,150,"KEY0:WriteKEY2:Read");

 

     POINT_COLOR=BLUE;//设置字体为蓝色           

     while(1)

     {

                 key=KEY_Scan();

                 if(key==1)//KEY0按下,写入SPIFLASH

                 {

                             LCD_Fill(0,170,239,319,WHITE);//清除半屏   

                             LCD_ShowString(60,170,"StartWrite W25X16....");

                             SPI_Flash_Write((u8*)TEXT_Buffer,1000,SIZE);//从1000字节处开始,写入SIZE长度的数据

                             LCD_ShowString(60,170,"W25X16Write Finished!");//提示传送完成

                 }

                 if(key==3)//KEY1按下,读取写入的字符传字符串并显示

                 {

                             LCD_ShowString(60,170,"StartRead W25X16.... ");

                             SPI_Flash_Read(datatemp,1000,SIZE);//从1000地址处开始,读出SIZE个字节

                             LCD_ShowString(60,170,"TheData Readed Is:  ");//提示传送完成

                             LCD_ShowString(60,190,datatemp);//显示读到的字符串

                 }

                 i++;

                 delay_ms(1);

                 if(i==200)

                 {

                             LED0=!LED0;//提示系统正在运行        

                             i=0;

                 }                        

     }

}


关键字:STM32  SPI读写  极性设置 引用地址:STM32学习笔记之硬件SPI读写与极性设置

上一篇:STM32学习笔记之堆栈空间
下一篇:STM32学习笔记之Keil工程Lib库文件的制作和运用

推荐阅读最新更新时间:2024-03-16 15:36

STM32之can 实例+代码解析
#include sysdef.h #define MAX_MAIL_NUM 3 //CAN总线调试:0=运行 1=自环调试 #define CAN_DEBUG 0 //CAN总线波特率:0=250kbps,1=500kbps,2=1Mbps #define CAN1_BPS 0 unsigned char can1_addr = 0; unsigned short Can1_Tx_Count =0; unsigned short Can1_Rx_Count =0; unsigned short Can1_Send_Delay =0; unsigned char Can1_Send_Buf ={0xe
[单片机]
STM32中电源各引脚说明
一、数字电路中,电源符号VCC:C=circuit,表示电路的意思,即接入电路的电压;VDD:D=Device,表示器件的意思,即器件的工作电压;VSS:S=Series,表示公共连接的意思,通常指电路公共接地端电压。总结来说,VCC接电路的电源(±),VSS接电路的地,VDD接器件的电源引脚(±)。 二、STM32中电源各引脚说明VDDA:A=Analog,表示模拟的意思,所以就是表示模拟器件的工作电压;VSSA:表示模拟器件的公共端地。VBAT:给后备区域供电,维持包括RTC/BKP寄存器等在内的一些数据的保存。100引脚的封装中:VREF-:A/D的参考,当需要使用时,必须绑定到VSSA(使得所有模拟器件的参考都相对于V
[单片机]
STM32单片机的八种IO口模式解析
STM32八种IO口模式区别 (1)GPIO_Mode_AIN模拟输入 (2)GPIO_Mode_IN_FLOATING浮空输入 (3)GPIO_Mode_IPD下拉输入 (4)GPIO_Mode_IPU上拉输入 (5)GPIO_Mode_Out_OD开漏输出 (6)GPIO_Mode_Out_PP推挽输出 (7)GPIO_Mode_AF_OD复用开漏输出 (8)GPIO_Mode_AF_PP复用推挽输出 以下是详细讲解 (1)GPIO_Mode_AIN模拟输入 即关闭施密特触发器,将电压信号传送到片上外设模块(不接上、下拉电阻) (2)GPIO_Mode_IN_FLOATING浮空输入 浮空输入状态下,IO的电平状态是不确定
[单片机]
stm32串口通信流程图
1.写在前面 首先,你要知道STM32启动文件中启动流程,你就需要掌握一点汇编基础知识。 汇编语言属于机器语言,或者说低级语言,C语言属于高级语言,所以,汇编和C语言在语法上差异很大。 如果你学底层开发,汇编的一些基础知识需要掌握。不需要精通,但需要看懂常见的汇编代码。 2.说明 STM32的启动文件与编译器有关,不同编译器,它的启动文件不同。 虽然启动文件(汇编)代码各有不同,但它们原理类似,都属于汇编程序。 我们拿基于MDK-ARM的启动文件来举例,说一下要点内容。 3.分配堆栈 在基于MDK的启动文件开始,有一段汇编代码是分配堆栈大小的。 这里重点知道堆栈数值大小就行。还有一段AREA(区域),表示分配一段堆栈数
[单片机]
<font color='red'>stm32</font>串口通信流程图
STM32复习笔记(十)LCD的介绍和使用方法
一、TFTLCD驱动原理-TFTLCD简介: (1)介绍TFTLCD: TFTLCD即薄膜晶体管液晶显示器。它与无源TN-LCD、STN-LCD的简单矩阵不同,它在液晶显示屏的每一个象素上都设置有一个薄膜晶体管(TFT),可有效地克服非选通时的串扰,使显示液晶屏的静态特性与扫描线数无关,因此大大提高了图像质量。 TFTLCD具有:亮度好、对比度高、层次感强、颜色鲜艳等特点。是目前最主流的LCD显示器。广泛应用于电视、手机、电脑、平板等各种电子产品。 (2)2.8寸 TFTLCD接口说明(16位80并口): (3)TFTLCD 16位80并口驱动简介: (4)ILI9341 驱动时序: 重点时序: 读ID低电平
[单片机]
<font color='red'>STM32</font>复习笔记(十)LCD的介绍和使用方法
stm32的可编程电压检测PVD
配置PVD的顺序如: 注意一般使能PVD在系统初始化完毕开启。 /** * @brief Configures EXTI Lines. * @param None * @retval None */ static void EXTI_Configuration(void) { EXTI_InitTypeDef EXTI_InitStructure; /* Configure EXTI Line16(PVD Output) to generate an interrupt on rising and falling edges */ EXTI_ClearITPendingBit(EXTI_Line16
[单片机]
秉火429笔记之一初识STM32
1. STM32概述 STM32,ST为意法半导体,M是Microelectronics的缩写,32表示32位,简而言之,STM32即为ST公司开发的32位微控制器。 2. STM32分类 STM32包含多个系列,从内核分为Cortex-M0、M3、M4、M7,每个内核有大概分为主流、高性能和低功耗。 3. STM32命名规则
[单片机]
秉火429笔记之一初识<font color='red'>STM32</font>
STM32的瞬态运动参数存储测试系统设计
引言 存储测试技术是在特殊环境下记录运动物体参数行之有效的方法,先将测试数据存入存储器,待装置回收后通过特定接口与上位机进行通信,还原数据信息。在诸多领域的测试中,对数据采集存储系统的实时性和功耗提出了更高的要求,随着半导体技术的发展,各种技术的进步使得高速度、低功耗的存储测试系统能够实现。 本系统选择ST公司超低功耗的基于ARM Cortex M3四核的处理器STM32F103C8T6作为核心控制元件,采取内部A/D转换器与铁电存储器结合的方法,实现压阻式加速度传感器测试数据的采集、存储,并利用LabView开发平台设计上位机应用软件实现测试数据的USB回读及处理。 1 系统原理 存储测试系统由电源管理模块、调理模块、外部晶
[单片机]
<font color='red'>STM32</font>的瞬态运动参数存储测试系统设计
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
  • 学习ARM开发(16)
    ARM有很多东西要学习,那么中断,就肯定是需要学习的东西。自从CPU引入中断以来,才真正地进入多任务系统工作,并且大大提高了工作效率。采 ...
  • 学习ARM开发(17)
    因为嵌入式系统里全部要使用中断的,那么我的S3C44B0怎么样中断流程呢?那我就需要了解整个流程了。要深入了解,最好的方法,就是去写程序 ...
  • 学习ARM开发(18)
    上一次已经了解ARM的中断处理过程,并且可以设置中断函数,那么它这样就可以工作了吗?答案是否定的。因为S3C44B0还有好几个寄存器是控制中 ...
  • 嵌入式系统调试仿真工具
    嵌入式硬件系统设计出来后就要进行调试,不管是硬件调试还是软件调试或者程序固化,都需要用到调试仿真工具。 随着处理器新品种、新 ...
  • 最近困扰在心中的一个小疑问终于解惑了~~
    最近在驱动方面一直在概念上不能很好的理解 有时候结合别人写的一点usb的例子能有点感觉,但是因为arm体系里面没有像单片机那样直接讲解引脚 ...
  • 学习ARM开发(1)
  • 学习ARM开发(2)
  • 学习ARM开发(4)
  • 学习ARM开发(6)
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved