SPI是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。SPI接口主要应用在EEPROM,FLASH,实时时
钟, AD 转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管
脚上只占用四根线。
SPI接口一般使用4条线通信:
MISO主设备数据输入,从设备数据输出。
MOSI主设备数据输出,从设备数据输入。
SCLK时钟信号,由主设备产生。
CS从设备片选信号,由主设备控制。
主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。寄存器通过MOSI信号线将字
节传送给从机,从机也将自己的移位寄存器中的内容通过 MISO 信号线返回给主机。这样,两个移位寄存器中的内容就被交换。外
设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必
须发送一个空字节来引发从机的传输。
SPI主要特点有: 可以同时发出和接收串行数据; 可以当作主机或从机工作; 提供频率可编程时钟; 发送结束中断标志; 写冲突
保护; 总线竞争保护等。
W25Q128是外部flash芯片,具体芯片资料:以下列出一些关键的文件描述及时序图
1. 在对该flash进行写操作时必须先有擦出操作,不然会写入出错,还需写使能,写完必须写失能(很关键)
2. 擦出有扇区擦除,半块擦除,块擦除,全部擦除,全部擦除需要耗时超久
3. 可以对扇区,块上解锁
4. 有3个状态寄存器,可以查看芯片及内存的各种状态
5. 内存地址为24为地址
看图说话
1.flash内存图
概述:内存物理地址,分为块,扇区等等
2.读操作
概述:拉低片选,再发送读命令,再发送地址,随后才是数据
3.擦除
概述:类似读,知识命令不一样,命令详情可于作者的资料里下载
4.写操作
概述:类似读,写之前进行内存擦除,写使能,写一位数据后将clk拉高一表示将其stable(稳定)
6.操作流程图
更加具体的SPI操作flash的细节在代码中体现,代码中不为增加鲁棒性而增加代码的复杂度,极度最求简介易懂。
extern u16 W25QXX_TYPE; //¶¨ÒåW25QXXоƬÐͺÅ
#define W25QXX_CS PBout(12) //W25QXXµÄƬѡÐźÅ
////////////////////////////////////////////////////////////////////////////
//Ö¸Áî±í
#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 W25QXX_Init(void);
u16 W25QXX_ReadID(void); //¶ÁÈ¡FLASH ID
u8 W25QXX_ReadSR(void); //¶Áȡ״̬¼Ä´æÆ÷
void W25QXX_Write_SR(u8 sr); //д״̬¼Ä´æÆ÷
void W25QXX_Write_Enable(void); //дʹÄÜ
void W25QXX_Write_Disable(void); //д±£»¤
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead); //¶ÁÈ¡flash
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//дÈëflash
void W25QXX_Erase_Chip(void); //ÕûƬ²Á³ý
void W25QXX_Erase_Sector(u32 Dst_Addr); //ÉÈÇø²Á³ý
void W25QXX_Wait_Busy(void); //µÈ´ý¿ÕÏÐ
void W25QXX_PowerDown(void); //½øÈëµôµçģʽ
void W25QXX_WAKEUP(void);
void SPI2_Init(void);
void SPI2_SetSpeed(u8 SpeedSet);
u8 SPI2_ReadWriteByte(u8 TxData);
u16 W25QXX_TYPE=W25Q128;
//芯片内部硬件SPI初始化
void SPI2_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );//PORTBʱÖÓʹÄÜ
RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );//SPI2ʱÖÓʹÄÜ
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15¸´ÓÃÍÆÍìÊä³ö
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//³õʼ»¯GPIOB
GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //PB13/14/15ÉÏÀ
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //ÉèÖÃSPIµ¥Ïò»òÕßË«ÏòµÄÊý¾Ýģʽ:SPIÉèÖÃΪ˫ÏßË«ÏòÈ«Ë«¹¤
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //ÉèÖÃSPI¹¤×÷ģʽ:ÉèÖÃΪÖ÷SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //ÉèÖÃSPIµÄÊý¾Ý´óС:SPI·¢ËͽÓÊÕ8λ֡½á¹¹
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //´®ÐÐͬ²½Ê±ÖӵĿÕÏÐ״̬Ϊ¸ßµçƽ
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //´®ÐÐͬ²½Ê±Öӵĵڶþ¸öÌø±äÑØ£¨ÉÏÉý»òϽµ£©Êý¾Ý±»²ÉÑù
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSSÐźÅÓÉÓ²¼þ£¨NSS¹Ü½Å£©»¹ÊÇÈí¼þ£¨Ê¹ÓÃSSI룩¹ÜÀí:ÄÚ²¿NSSÐźÅÓÐSSIλ¿ØÖÆ
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //¶¨Ò岨ÌØÂÊÔ¤·ÖƵµÄÖµ:²¨ÌØÂÊÔ¤·ÖƵֵΪ256
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //Ö¸¶¨Êý¾Ý´«Êä´ÓMSBλ»¹ÊÇLSBλ¿ªÊ¼:Êý¾Ý´«Êä´ÓMSBλ¿ªÊ¼
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRCÖµ¼ÆËãµÄ¶àÏîʽ
SPI_Init(SPI2, &SPI_InitStructure); //¸ù¾ÝSPI_InitStructÖÐÖ¸¶¨µÄ²ÎÊý³õʼ»¯ÍâÉèSPIx¼Ä´æÆ÷
SPI_Cmd(SPI2, ENABLE);
SPI2_ReadWriteByte(0xff);
}
//SpeedSet:
//SPI_BaudRatePrescaler_2 2分频
//SPI_BaudRatePrescaler_8 8分频
//SPI_BaudRatePrescaler_16 16分频
//SPI_BaudRatePrescaler_256 256分频
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{
assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
SPI2->CR1&=0XFFC7;
SPI2->CR1|=SPI_BaudRatePrescaler;
SPI_Cmd(SPI2,ENABLE);
}
//内部使用的是库函数,可以自行查看源码备注
u8 SPI2_ReadWriteByte(u8 TxData)
{
u8 retry=0;
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET)
retry++;
if(retry>200)return 0;
}
SPI_I2S_SendData(SPI2, TxData);
retry=0;
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)
retry++;
if(retry>200)return 0;
}
return SPI_I2S_ReceiveData(SPI2);
}
//flash片选引脚初始化
void W25QXX_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
W25QXX_CS=1;
SPI2_Init();
SPI2_SetSpeed(SPI_BaudRatePrescaler_2);
W25QXX_TYPE=W25QXX_ReadID();//读取flash产品ID
}
//读寄存器
u8 W25QXX_ReadSR(void)
{
u8 byte=0;
W25QXX_CS=0;
SPI2_ReadWriteByte(W25X_ReadStatusReg);
byte=SPI2_ReadWriteByte(0Xff);
W25QXX_CS=1;
return byte;
}
//写寄存器
//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写
void W25QXX_Write_SR(u8 sr)
{
W25QXX_CS=0;
SPI2_ReadWriteByte(W25X_WriteStatusReg);
SPI2_ReadWriteByte(sr);
W25QXX_CS=1;
}
//写使能
void W25QXX_Write_Enable(void)
{
W25QXX_CS=0;
SPI2_ReadWriteByte(W25X_WriteEnable);
W25QXX_CS=1;
}
//写失能
void W25QXX_Write_Disable(void)
{
W25QXX_CS=0;
SPI2_ReadWriteByte(W25X_WriteDisable);
W25QXX_CS=1;
}
u16 W25QXX_ReadID(void)
{
u16 Temp = 0;
W25QXX_CS=0;
SPI2_ReadWriteByte(0x90);
SPI2_ReadWriteByte(0x00);
SPI2_ReadWriteByte(0x00);
SPI2_ReadWriteByte(0x00);
Temp|=SPI2_ReadWriteByte(0xFF)<<8;
Temp|=SPI2_ReadWriteByte(0xFF);
W25QXX_CS=1;
return Temp;
}
//pBuffer:数据存储区
//ReadAddr:地址
//NumByteToRead:需要读取的长度
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{
u16 i;
W25QXX_CS=0;
SPI2_ReadWriteByte(W25X_ReadData);
SPI2_ReadWriteByte((u8)((ReadAddr)>>16));
SPI2_ReadWriteByte((u8)((ReadAddr)>>8));
SPI2_ReadWriteByte((u8)ReadAddr);
for(i=0;i pBuffer[i]=SPI2_ReadWriteByte(0XFF);
}
W25QXX_CS=1;
}
//页写入,一次最多只能写入256个byte
void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 i;
W25QXX_Write_Enable();
W25QXX_CS=0;
SPI2_ReadWriteByte(W25X_PageProgram);
SPI2_ReadWriteByte((u8)((WriteAddr)>>16));
SPI2_ReadWriteByte((u8)((WriteAddr)>>8));
SPI2_ReadWriteByte((u8)WriteAddr);
for(i=0;i
void W25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 pageremain;
pageremain=256-WriteAddr%256;
if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;
while(1)
{
W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);
if(NumByteToWrite==pageremain)break;
else //NumByteToWrite>pageremain
{
pBuffer+=pageremain;
WriteAddr+=pageremain;
NumByteToWrite-=pageremain;
if(NumByteToWrite>256)pageremain=256;
else pageremain=NumByteToWrite;
}
};
}
u8 W25QXX_BUFFER[4096];
void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u32 secpos;
u16 secoff;
u16 secremain;
u16 i;
u8 * W25QXX_BUF;
W25QXX_BUF=W25QXX_BUFFER;
secpos=WriteAddr/4096;
secoff=WriteAddr%4096;
secremain=4096-secoff;
if(NumByteToWrite<=secremain)secremain=NumByteToWrite;
while(1)
{ //检查需要擦除的部分,非0xff为不干净的,需要擦除
W25QXX_Read(W25QXX_BUF,secpos*4096,4096);
for(i=0;i
W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);
if(NumByteToWrite==secremain)
break;
else//根据flash的内存特性,写入是做相应的地址偏移
{
secpos++;
secoff=0;
pBuffer+=secremain;
WriteAddr+=secremain;
NumByteToWrite-=secremain;
if(NumByteToWrite>4096)secremain=4096;
else secremain=NumByteToWrite;
}
};
}
//清除全部内存
void W25QXX_Erase_Chip(void)
{
W25QXX_Write_Enable();
W25QXX_Wait_Busy();
W25QXX_CS=0;
SPI2_ReadWriteByte(W25X_ChipErase);
W25QXX_CS=1;
W25QXX_Wait_Busy();
}
//扇区擦除
void W25QXX_Erase_Sector(u32 Dst_Addr)
{
printf("fe:%x\r\n",Dst_Addr);
Dst_Addr*=4096;
W25QXX_Write_Enable();
W25QXX_Wait_Busy();
W25QXX_CS=0;
SPI2_ReadWriteByte(W25X_SectorErase);
SPI2_ReadWriteByte((u8)((Dst_Addr)>>16));
SPI2_ReadWriteByte((u8)((Dst_Addr)>>8));
SPI2_ReadWriteByte((u8)Dst_Addr);
W25QXX_CS=1;
W25QXX_Wait_Busy();
}
//等待非busy
void W25QXX_Wait_Busy(void)
{
while((W25QXX_ReadSR()&0x01)==0x01); // µÈ´ýBUSYλÇå¿Õ
}
void W25QXX_PowerDown(void)
{
W25QXX_CS=0; //ʹÄÜÆ÷¼þ
SPI2_ReadWriteByte(W25X_PowerDown); //·¢Ë͵ôµçÃüÁî
W25QXX_CS=1; //È¡ÏûƬѡ
delay_us(3); //µÈ´ýTPD
}
void W25QXX_WAKEUP(void)
{
W25QXX_CS=0; //ʹÄÜÆ÷¼þ
SPI2_ReadWriteByte(W25X_ReleasePowerDown); // send W25X_PowerDown command 0xAB
W25QXX_CS=1; //È¡ÏûƬѡ
delay_us(3); //µÈ´ýTRES1
}
const u8 TEXT_Buffer[]={"WarShipSTM32 SPI TEST"};
#define SIZE sizeof(TEXT_Buffer)
//
int main(void)
{
u8 key;
u16 i=0;
u8 datatemp[SIZE];
u32 FLASH_SIZE;
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
SPI2_init();
W25QXX_Init();
while(W25QXX_ReadID()!=W25Q128)
{
printf("check fail!");
}
FLASH_SIZE=128*1024*1024;
while(1)
{
W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);
W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE);
printf("the datatemp : %s\n", datatemp);
}
}
}