完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
实验目的
本实验学习STM32的SPI接口的使用,利用SPI接口读写16M字节FLASH芯片W25Q128FVSSIG,将结果通过串口显示出来。SPI接口请参看数据手册第23章 实验简介 SPI协议(Serial Peripheral Interface),即串行外围设备接口,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议。 SPI接口包含4条总线,分别是SCK,MISO,MOSI,和CS,作用如下: SCK:时钟信号线,由主设备产生输出给从设备。 MISO: 主设备数据输入,从设备数据输出。 MOSI:主设备数据输出,从设备数据输入。 CS :从设备片选信号线,由主设备控制。 SPI根据时钟极性(CPOL)和时钟相位(CPHA)的不同分为4种模式,分别为CPOL=0 CPHA = 0,CPOL=0 CPHA = 1,CPOL = 1 CPHA = 0 和 CPOL = 1, CPOL = 1,这四种模式。时序图如下: 电路设计 星光STM32F103开发板采用的SPI FLASH为W25Q128FVSSIG芯片,容量16M字节,连接到STM32的SPI1,电路图如下所示: 由于和JTAG共用部分IO,程序设计时需要关闭JTAG功能,可通过串口下载其他程序,擦除芯片中的设置,恢复JTAG功能。 代码 main.c #include "MyIncludes.h" u16 sys_cnt = 0; //systick中断服务函数,1ms中断 void systick_isr(void) { if(sys_cnt < 1000) sys_cnt++; else { sys_cnt = 0; HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_4|GPIO_PIN_5); } } u32 w25_id; //25ID u8 writeBuffer1[256]; //写缓存区数组 u8 readBuffer1[256]; //读缓存区数组 u8 statusl; //貌似没用这个 int main() { u32 j; System_Init(); LED_Init(); SysTick_Init(systick_isr); USART1_Init(115200,NULL,NULL); W25_Flash_Init(); //Flash初始化 w25_id = W25_Flash_ReadJedecID(); //读取芯片JedecID if(w25_id != 0xEF4018) return 0; w25_id = W25_Flash_ReadID(); if(w25_id != 0xEF17) return 0; for(j=0; j < sizeof(writeBuffer1);j++) { writeBuffer1[j] = j; } //扇形测试 W25_FLASH_Erase(W25X_FLASH_ERASE_SECTOR,0); W25X_Flash_PageProgram(0,writeBuffer1,sizeof(writeBuffer1)); W25_Flash_Read(0,readBuffer1,sizeof(readBuffer1)); if(strncmp((char *)writeBuffer1,(char *)readBuffer1,256) != 0) { goto ERROR; } W25_FLASH_Erase(W25X_FLASH_ERASE_SECTOR,0); W25_Flash_Read(0,readBuffer1,sizeof(readBuffer1)); for(j=0; j if(readBuffer1[j] != 0xff) goto ERROR; } //32K块测试 W25_FLASH_Erase(W25X_FLASH_ERASE_BLOCK_32K,0); W25X_Flash_PageProgram(4*4096,writeBuffer1,sizeof(writeBuffer1)); W25_Flash_FastRead(4*4096,readBuffer1,sizeof(readBuffer1)); if(strncmp((char *)writeBuffer1,(char *)readBuffer1,256) !=0 ) { goto ERROR; } W25_FLASH_Erase(W25X_FLASH_ERASE_BLOCK_32K,0); W25_Flash_FastRead(4*4096,readBuffer1,sizeof(readBuffer1)); for(j=0;j if(readBuffer1[j] != 0xff) goto ERROR; } //64K块测试 W25_FLASH_Erase(W25X_FLASH_ERASE_BLOCK_64K,0); W25X_Flash_PageProgram(9*4096,writeBuffer1,sizeof(writeBuffer1)); W25_Flash_FastRead(9*4096,readBuffer1,sizeof(readBuffer1)); if(strncmp((char *)writeBuffer1,(char *)readBuffer1,256)!=0) { goto ERROR; } W25_FLASH_Erase(W25X_FLASH_ERASE_BLOCK_64K,0); W25_Flash_FastRead(9*4096,readBuffer1,sizeof(readBuffer1)); for(j=0; j if(readBuffer1[j] != 0xff) goto ERROR; } goto OK; ERROR: printf("W25Q128 SPI ReadWrite ERROR!!!rn"); goto EXIT; OK: printf("W25Q128 SPI ReadWrite OK!!!rn"); EXIT: while(1) { } } } w25xx_SPI.h #include "stm32f1xx.h" #include "stm32_types.h" #include "stm32f1xx_hal.h" #define W25_FLASH_SIZE (1024*1024*16) #define W25X10 0xEF10 #define W25Q20 0xEF11 #define W25Q40 0xEF12 #define W25Q80 0xEF13 #define W25Q16 0xEF14 #define W25Q32 0xEF15 #define W25Q64 0xEF16 #define W25Q128 0xEF17 extern u16 SPI_FLASH_TYPE; #define W25X_FLASH_ERASE_CHIP 0 #define W25X_FLASH_ERASE_SECTOR 1 #define W25X_FLASH_ERASE_BLOCK_32K 2 #define W25X_FLASH_ERASE_BLOCK_64K 3 #define W25X_FLASH_READ_STATUSREG1 0 #define W25X_FLASH_READ_STATUSREG2 1 #define W25X_FLASH_READ_STATUSREG3 2 #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_FastReadDua1 0x3B #define W25X_PageProgram 0x02 #define W25X_32K_BlockErase 0x52 #define W25X_64K_BlockErase 0xD8 #define W25X_SectorErase 0xD8 #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 W25XFLASH_REG_BIT_BUSY (1<<0) #define W25XFLASH_REG_BIT_WEL (1<<1) #define W25XFLASH_REG_BIT_BPX (0x7<<2) #define W25XFLASH_REG_BIT_TB (1<<5) #define W25XFLASH_REG_BIT_SRP (1<<7) void W25_Flash_Init(void); u8 W25_Flash_ReadSR(void); void W25_FLASH_Write_SR(u8 sr); u16 W25_Flash_ReadID(void); u32 W25_Flash_ReadJedecID(void); void W25_Flash_Read(u32 ReadAddr,u8* pBuffer,u32 NumByteToRead); void W25_Flash_FastRead(u32 ReadAddr, u8* pBuff,u32 NumByteToRead); void W25X_Flash_PageProgram(u32 WriteAddr,u8* pBuffer,u16 NumByteToWrite); void W25_FLASH_Erase(u8 Type,u32 EraseAddr); void W25_Flash_Write_NoCheck(u32 WriteAddr,u8* pBuffer,u16 NumByteToWrite); void W25_Flash_Write(u32 WriteAddr, u8* pBuffer,u16 NumByteToWrite); w25xx_SPI.c #include "w25xx_SPI.h" #include "spi.h" #include #include SPI_HandleTypeDef W25_Handle; //SPI句柄结构变量声明 void W25_Flash_Init() //Flash初始化 { SPIx_Init(&W25_Handle); //初始化SPI接口 } u8 W25_Flash_ReadWriteByte(u8 TxData) //Flash读写接口函数 (写入的数据) { return SPI_ReadWriteByte(&W25_Handle,TxData); //读写一个字节的数据 } u8 W25_Flash_ReadSR(void) { u8 byte = 0 ; W25_SPI_FLASH_CSL(); //使能 W25_Flash_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令(0x05) byte = W25_Flash_ReadWriteByte(0xFF); //读取一个字节 W25_SPI_FLASH_CSH(); //取消是能 return byte; } //没用到 void W25_FLASH_Write_SR(u8 sr) { W25_SPI_FLASH_CSL(); //使能器件 W25_Flash_ReadWriteByte(W25X_WriteStatusReg); //发送写取状态寄存器命令(0x01) W25_Flash_ReadWriteByte(sr); //写入一个字节 W25_SPI_FLASH_CSH(); //取消片选 } void W25_FLASH_Write_Enable(void) { W25_SPI_FLASH_CSL(); //使能器件 W25_Flash_ReadWriteByte(W25X_WriteEnable); //发送写取状态寄存器命令 W25_SPI_FLASH_CSH(); //取消选中的FLASH } //**没用到** void W25_FLASH_Write_Disable(void) { W25_SPI_FLASH_CSL(); //使能器件 W25_Flash_ReadWriteByte(W25X_WriteDisable); //发送写禁止指令(0x04) W25_SPI_FLASH_CSH(); //取消使能 } u16 W25_Flash_ReadID(void) { u16 Temp = 0; W25_SPI_FLASH_CSL(); //选中FLASH W25_Flash_ReadWriteByte(W25X_ManufactDeviceID); //发送读取ID命令 (0x90); W25_Flash_ReadWriteByte(0x00); W25_Flash_ReadWriteByte(0x00); W25_Flash_ReadWriteByte(0x00); //发送24位地址 Temp |= W25_Flash_ReadWriteByte(0xFF)<<8; Temp |= W25_Flash_ReadWriteByte(0xFF); //返回16位地址 W25_SPI_FLASH_CSH(); //取消选中FLash return Temp; } u32 W25_Flash_ReadJedecID(void) //读取芯片JedecID { u32 Temp = 0; W25_SPI_FLASH_CSL(); //选中FLASH W25_Flash_ReadWriteByte(W25X_JedecDeviceID);//发送读取ID命令 (0x9f) Temp |= W25_Flash_ReadWriteByte(0xFF)<<16; Temp |= W25_Flash_ReadWriteByte(0xFF)<<8; Temp |= W25_Flash_ReadWriteByte(0xFF); //读取24位数据(刚开始8位左移16或上 8位移动 //8 再或上 8位 ) //取反、bai左移、按位与、按位异或、du按位或) //优先级由zhi高到低的顺序排列为取反 > 左移 //> 按位与 > 按位异或dao > 按位或。 W25_SPI_FLASH_CSH(); //取消选中FLASH return Temp; } void W25_Flash_Read(u32 ReadAddr,u8* pBuffer,u32 NumByteToRead) { u16 i; W25_SPI_FLASH_CSL(); //使能器件 W25_Flash_ReadWriteByte(W25X_ReadData); //发送读命令 W25_Flash_ReadWriteByte((u8)((ReadAddr) >> 16)); W25_Flash_ReadWriteByte((u8)((ReadAddr)>>8)); W25_Flash_ReadWriteByte((u8)ReadAddr); //发送要读取的32位地址 for(i=0;i pBuffer = W25_Flash_ReadWriteByte(0xff); //循环读数 } W25_SPI_FLASH_CSH(); //禁止 while((W25_Flash_ReadSR()&W25XFLASH_REG_BIT_BUSY) ==0x01 ); //如果器件忙,等待 } void W25_Flash_FastRead(u32 ReadAddr,u8* pBuffer,u32 NumByteToRead) { u16 i; W25_SPI_FLASH_CSL(); //使能 W25_Flash_ReadWriteByte(W25X_FastReadData); //发送高速读命令0x0b,3字节地址 (0x0b) W25_Flash_ReadWriteByte(((ReadAddr)>>16)); W25_Flash_ReadWriteByte(((ReadAddr)>>8)); W25_Flash_ReadWriteByte(ReadAddr); //发送32bit地址 W25_Flash_ReadWriteByte(0xFF); //产生8个空闲时钟周期脉冲 for(i=0;i pBuffer = W25_Flash_ReadWriteByte(0xFF); //循环度数 } W25_SPI_FLASH_CSH(); while(W25_Flash_ReadSR()&W25XFLASH_REG_BIT_BUSY);//器件忙等待 } void W25X_Flash_PageProgram(u32 WriteAddr,u8* pBuffer,u16 NumByteToWrite) //页编程(写入的地址,写数据存储的缓存区,写入的值的长度) { u32 i; W25_FLASH_Write_Enable(); //发送写使能命令 W25_SPI_FLASH_CSL(); //发送低速读写命令0xAD,3字节地址,写入的字节 W25_Flash_ReadWriteByte(W25X_PageProgram); W25_Flash_ReadWriteByte(((WriteAddr)>>16)); W25_Flash_ReadWriteByte(((WriteAddr)>>8)); W25_Flash_ReadWriteByte(WriteAddr); //一共发送32bit地址 for(i=0; i { W25_Flash_ReadWriteByte(pBuffer); //编程 } W25_SPI_FLASH_CSH(); //取消 while(W25_Flash_ReadSR()&W25XFLASH_REG_BIT_BUSY);//如果器件忙,等待 } void W25_FLASH_Erase(u8 Type,u32 EraseAddr) //FLASH的擦除操作(擦除类型,擦除首地址) { W25_FLASH_Write_Enable(); //发送写使能命令 switch(Type) { case W25X_FLASH_ERASE_CHIP: //整片擦除 W25_SPI_FLASH_CSL(); //选中使能FLASH W25_Flash_ReadWriteByte(W25X_ChipErase); //发送擦除整片命令(0xc7) W25_SPI_FLASH_CSH(); //取消选中flash break; case W25X_FLASH_ERASE_SECTOR: //4K扇区擦除 W25_SPI_FLASH_CSL(); //使能 W25_Flash_ReadWriteByte(W25X_SectorErase); //发送4K扇区擦除命令(0xd8) W25_Flash_ReadWriteByte(((EraseAddr)>>16)); //擦除前16位 W25_Flash_ReadWriteByte(((EraseAddr)>>8)); //擦除前8位 W25_Flash_ReadWriteByte(EraseAddr); //擦除完 W25_SPI_FLASH_CSH(); //取消 break; case W25X_FLASH_ERASE_BLOCK_32K: //32K块擦除 W25_SPI_FLASH_CSL(); //使能 W25_Flash_ReadWriteByte(W25X_32K_BlockErase); //发送擦除命令(0x52) W25_Flash_ReadWriteByte(((EraseAddr)>>16)); W25_Flash_ReadWriteByte(((EraseAddr)>>8)); W25_Flash_ReadWriteByte(EraseAddr); W25_SPI_FLASH_CSH(); break; case W25X_FLASH_ERASE_BLOCK_64K: //64K块擦除 W25_SPI_FLASH_CSL(); W25_Flash_ReadWriteByte(W25X_64K_BlockErase); W25_Flash_ReadWriteByte(((EraseAddr)>>16)); W25_Flash_ReadWriteByte(((EraseAddr)>>8)); W25_Flash_ReadWriteByte(EraseAddr); W25_SPI_FLASH_CSH(); break; default:break; } while(W25_Flash_ReadSR()&W25XFLASH_REG_BIT_BUSY);//如果器件忙等待 } //未用到 void W25_Flash_Write_NoCheck(u32 WriteAddr, u8* pBuffer, u16 NumByteToWrite) //无检验写SPI 在指定的位置开始写入指定长度数据 { u16 pageremain; pageremain = 256-WriteAddr%256; //单页剩余数字 if(NumByteToWrite <= pageremain) pageremain = NumByteToWrite; //不大于256字节 while(1) { W25X_Flash_PageProgram (WriteAddr,pBuffer,pageremain); if(NumByteToWrite==pageremain) break; //写入结束 else//NumByteToWrite>pageremain { pBuffer+=pageremain; WriteAddr+=pageremain; NumByteToWrite-=pageremain; //减去已经写入了的字节数 if(NumByteToWrite>256) pageremain = 256; //一次可以写入256个字节 else pageremain=NumByteToWrite; //不够256个字节 } }; } //没用到 u8 W25SPI_FLASH_BUFFER[4096]; void W25_Flash_Write(u32 WriteAddr,u8* pBuffer,u16 NumByteToWrite) //写SPI FLASH,在指定地址开始写入指定长度的数据 { u32 secpos; //扇区地址 u16 secoff; //扇区内偏移 u16 secremain; //扇区剩余空间 u16 i; u8 * SPI_FLASH_BUF; SPI_FLASH_BUF = W25SPI_FLASH_BUFFER; //可以通过申请内存的方式实现 secpos = WriteAddr/4096; //扇区地址 secpos = WriteAddr%4096; //扇区内的偏移 secremain = 4096-secoff; //扇区内剩余空间的大小 if(NumByteToWrite <= secremain) secremain = NumByteToWrite; //不大于4096个字节 while(1) { W25_Flash_FastRead(secpos*4096,SPI_FLASH_BUF,4096);//读出整个扇区的内容 for(i=0;i { if(SPI_FLASH_BUF[secoff+i] != 0XFF) break; //需要擦除 } if(i W25_FLASH_Erase(W25X_FLASH_ERASE_SECTOR,secpos*4096);//擦除这个扇区 for(i=1;i { SPI_FLASH_BUF[i+secoff]=pBuffer; } W25_Flash_Write_NoCheck(secpos*4096,SPI_FLASH_BUF,4096);//写入整个扇区 } else { W25_Flash_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间 } if(NumByteToWrite == secremain) { break;//写入结束了 } else //写入未结束 { secpos++; //扇区地址增1 secoff=0; //偏移位置为0 pBuffer+=secremain; //指针偏移 WriteAddr+=secremain; //写地址偏移 NumByteToWrite-=secremain; //字节数递减 if(NumByteToWrite>4096) secremain = 4096; //下一个扇区还是写不完 else secremain=NumByteToWrite; //下一个扇区可以写完 } } } spi.h #include "stm32f1xx.h" #include "stm32_types.h" #include "stm32f1xx_hal.h" #define FLASH_SPIx SPI1 #define FLASH_SPIx_RCC_CLK_ENABLE() __HAL_RCC_SPI1_CLK_ENABLE() //SPI1时钟使能 #define FLASH_SPIx_RCC_CLK_DISABLE() __HAL_RCC_SPI1_CLK_DISABLE() //SPI时钟禁能 #define FLASH_SPI_GPIO_ClK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() //GPIO时钟使能 #define FLASH_SPI_GPIO_PORT GPIOB #define FLASH_SPI_SCK_PIN GPIO_PIN_3 //SCK引脚 PB3 #define FLASH_SPI_MISO_PIN GPIO_PIN_4 //MISO引脚 PB4 #define FLASH_SPI_MOSI_PIN GPIO_PIN_5 //MOSI引脚 PB5 #define SPI_CS_PIN GPIO_PIN_3 //cs引脚 #define SPI_CS_GPIO_PORT GPIOD #define SPI_CS_CLK_ENABLE() //CS引脚使能 __HAL_RCC_GPIOD_CLK_ENABLE() #define W25_SPI_FLASH_CSH() HAL_GPIO_WritePin(SPI_CS_GPIO_PORT,SPI_CS_PIN,GPIO_PIN_SET) //选中FLASH #define W25_SPI_FLASH_CSL() HAL_GPIO_WritePin(SPI_CS_GPIO_PORT,SPI_CS_PIN,GPIO_PIN_RESET) //取消选中FLASH void SPIx_Init(SPI_HandleTypeDef *spi_handle); //SPI接口初始化 void SPI_WriteReadData(SPI_HandleTypeDef *spi_handle,const uint8_t *DataIn,uint8_t *DataOut,uint16_t DataLegnth); //SPI读写一组字节数据(SPI句柄,待写入的数据,读到的数据,写入数据的长度) uint8_t SPI_ReadWriteByte(SPI_HandleTypeDef *spi_handle,uint8_t Value); //SPI读写一个字节数据(SPI句柄,待写入的值) 实验现象 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1537 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1490 浏览 1 评论
910 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
654 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1538 浏览 2 评论
1845浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
598浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
499浏览 3评论
499浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
483浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-5 21:25 , Processed in 0.566154 second(s), Total 45, Slave 38 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号