#include "includes.h" #include "w25qxx.h" //w25Q128 static __IO uint32_t SPITimeout = SPIT_LONG_TIMEOUT; /* 容量 16页/扇区 128页/半块(32kB) 256页/块(64kB) w25q16 16M-bit 2MByte 8192编程页/256B 512扇区 64半块 32块 */ static uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode); /** * @brief SPII/O配置 * @param 无 * @retval 无 */ static void SPI_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /* 使能与SPI 有关的时钟 */ FLASH_SPI_APBxClock_FUN ( FLASH_SPI_CLK, ENABLE ); FLASH_SPI_GPIO_APBxClock_FUN ( FLASH_SPI_GPIO_CLK, ENABLE ); /* MISO MOSI SCK*/ GPIO_InitStructure.GPIO_Pin = FLASH_SPI_SCK_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(FLASH_SPI_SCK_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MOSI_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(FLASH_SPI_MOSI_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = FLASH_SPI_MISO_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(FLASH_SPI_MISO_PORT, &GPIO_InitStructure); //初始化CS引脚,使用软件控制,所以直接设置成推挽输出 GPIO_InitStructure.GPIO_Pin = FLASH_SPI_CS_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(FLASH_SPI_CS_PORT, &GPIO_InitStructure); FLASH_SPI_CS_HIGH; } /** * @brief SPI 工作模式配置 * @param 无 * @retval 无 */ static void SPI_Mode_Config(void) { SPI_InitTypeDef SPI_InitStructure; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2 ; //SPI 使用模式3 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge ; SPI_InitStructure.SPI_CPOL = SPI_CPOL_High ; SPI_InitStructure.SPI_CRCPolynomial = 0;//不使用CRC功能,数值随便写 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex ;//双线全双工 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB ; SPI_InitStructure.SPI_Mode = SPI_Mode_Master ; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft ; SPI_Init(FLASH_SPIx,&SPI_InitStructure); //写入配置到寄存器 SPI_Cmd(FLASH_SPIx,ENABLE);//使能SPI } /** * @brief SPI 初始化 * @param 无 * @retval 无 */ void SPI_USER_Init(void) { SPI_GPIO_Config(); SPI_Mode_Config(); } //发送并接收一个字节 uint8_t SPI_USER_Send_Byte(uint8_t data) { SPITimeout = SPIT_FLAG_TIMEOUT; //检查并等待至TX缓冲区为空 while(SPI_I2S_GetFlagStatus(FLASH_SPIx,SPI_I2S_FLAG_TXE) == RESET) { if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0); } //程序执行到此处,TX缓冲区已空 SPI_I2S_SendData (FLASH_SPIx,data); SPITimeout = SPIT_FLAG_TIMEOUT; //检查并等待至RX缓冲区为非空 while(SPI_I2S_GetFlagStatus(FLASH_SPIx,SPI_I2S_FLAG_RXNE) == RESET) { if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0); } //程序执行到此处,说明数据发送完毕,并接收到一字字节 return SPI_I2S_ReceiveData(FLASH_SPIx); } uint8_t SPI_USER_Read_Byte(void) { return SPI_USER_Send_Byte(DUMMY); } //读取ID号 uint32_t SPI_FLASH_Read_ID(void) { uint32_t flash_id; //片选使能 FLASH_SPI_CS_LOW; SPI_USER_Send_Byte(READ_JEDEC_ID); flash_id = SPI_USER_Send_Byte(DUMMY); flash_id <<= 8; flash_id |= SPI_USER_Send_Byte(DUMMY); flash_id <<= 8; flash_id |= SPI_USER_Send_Byte(DUMMY); FLASH_SPI_CS_HIGH; return flash_id; } //FLASH写入使能 void SPI_FLASH_Write_Enable(void) { //片选使能 FLASH_SPI_CS_LOW; SPI_USER_Send_Byte(WRITE_ENABLE); FLASH_SPI_CS_HIGH; } //擦除FLASH指定扇区 void SPI_FLASH_Erase_Sector(uint32_t addr) { SPI_FLASH_Write_Enable(); //片选使能 FLASH_SPI_CS_LOW; SPI_USER_Send_Byte(ERASE_SECTOR); SPI_USER_Send_Byte((addr>>16)&0xff); SPI_USER_Send_Byte((addr>>8)&0xff); SPI_USER_Send_Byte(addr&0xff); FLASH_SPI_CS_HIGH; SPI_FLASH_WaitForWriteEnd(); } //读取FLASH的内容 void SPI_FLASH_Read_Data(uint32_t addr,uint8_t *readBuff,uint32_t numByteToRead) { //片选使能 FLASH_SPI_CS_LOW; SPI_USER_Send_Byte(READ_DATA); SPI_USER_Send_Byte((addr>>16)&0xff); SPI_USER_Send_Byte((addr>>8)&0xff); SPI_USER_Send_Byte(addr&0xff); while(numByteToRead--) { *readBuff = SPI_USER_Send_Byte(DUMMY); readBuff++; } FLASH_SPI_CS_HIGH; } //向FLASH写入内容 void SPI_FLASH_Write_Data(uint32_t addr,uint8_t *writeBuff,uint32_t numByteToWrite) { SPI_FLASH_Write_Enable(); //片选使能 FLASH_SPI_CS_LOW; SPI_USER_Send_Byte(WRITE_DATA); SPI_USER_Send_Byte((addr>>16)&0xff); SPI_USER_Send_Byte((addr>>8)&0xff); SPI_USER_Send_Byte(addr&0xff); while(numByteToWrite--) { SPI_USER_Send_Byte(*writeBuff); writeBuff++; } FLASH_SPI_CS_HIGH; SPI_FLASH_WaitForWriteEnd(); } //连续写入多字节:不用考虑是否达到边界的问题,写入字节数也没有限制(当然要小于FLASH容量) void SPI_FLASH_Write_Datas(uint32_t addr,uint8_t *writeBuff,uint32_t numByteToWrite) { uint32_t page_offset = 0; //距离下一个页地址边界的偏移(距离) uint32_t write_len = 0; //每次要写入的字节数量 int page_size = 256; //页大小 SPI_FLASH_Write_Enable(); //片选使能 FLASH_SPI_CS_LOW; while(numByteToWrite > 0) { page_offset = page_size - (numByteToWrite % page_size); //计算页偏移 write_len = numByteToWrite>page_offset ? page_offset : numByteToWrite; //选择较小的作为本次写入的数据字节长度 SPI_FLASH_Write_Data( addr,writeBuff,write_len); //if(numByteToWrite) //改变相应参数 numByteToWrite = numByteToWrite - write_len; //减少写入数量 writeBuff = writeBuff + write_len; //增加写入缓冲地址 addr = addr+write_len; //增加FLASH写入的地址 } FLASH_SPI_CS_HIGH; } //等待FLASH内部时序操作完成 void SPI_FLASH_WaitForWriteEnd(void) { uint8_t status_reg = 0; //片选使能 FLASH_SPI_CS_LOW; SPI_USER_Send_Byte(READ_STATUS); do { status_reg = SPI_USER_Send_Byte(DUMMY); } while((status_reg & 0x01) == 1); FLASH_SPI_CS_HIGH; } /** * @brief Basic management of the timeout situation. * @param errorCode:错误代码,可以用来定位是哪个环节出错. * @retval 返回0,表示SPI读取失败. */ static uint32_t SPI_TIMEOUT_UserCallback(uint8_t errorCode) { /* Block communication and all processes */ FLASH_ERROR("SPI 等待超时!errorCode = %d",errorCode); return 0; } static u8 Spi_Buf_Write[256], Spi_Buf_Read[256]; uint8_t SPI_FLASH_Test(void) { u16 i; u32 id; SPI_USER_Init(); id = SPI_FLASH_Read_ID(); memset(Spi_Buf_Write, 0, 256); for ( i=0; i<=255; i++ ) {//填充缓冲 Spi_Buf_Write[i] = i+50; } data_dump("Flash Write", Spi_Buf_Write, sizeof(Spi_Buf_Write)); SPI_FLASH_Erase_Sector(0); //字节写入方式 SPI_FLASH_Write_Data(0, Spi_Buf_Write, 128); //将 EEPROM 读出数据顺序保持到 I2c_Buf_Read 中 memset(Spi_Buf_Read, 0, 256); SPI_FLASH_Read_Data(0, Spi_Buf_Read, 128); //将 I2c_Buf_Read 中的数据通过串口打印 data_dump("Flash Read", Spi_Buf_Read, sizeof(Spi_Buf_Read)); printf("\r\nSPI(W25qxx, id = %d)rw success\r\n", id); return 1; }