/* includes ----------------------------------------------------------*/ #include "norflash.h" /* typedef -----------------------------------------------------------*/ /* define ------------------------------------------------------------*/ /* macro -------------------------------------------------------------*/ /* variables ---------------------------------------------------------*/ uint16_t g_norflash_type = NM25Q128; /* 默认时NM25Q128 */ /* function prototypes -----------------------------------------------*/ /** * @brief 初始化SPI NOR FLASH * @param NONE * @retval NONE */ void norflash_init(void) { uint8_t temp; // NORFLASH_CS_GPIO_CLK_ENABLE(); /* NORFLASH CS脚 时钟使能 */ // // GPIO_InitTypeDef gpio_init_struct; // gpio_init_struct.Pin = NORFLASH_CS_GPIO_PIN; // gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; // gpio_init_struct.Pull = GPIO_PULLUP; // gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; // HAL_GPIO_Init(NORFLASH_CS_GPIO_PORT, &gpio_init_struct); /* CS引脚模式设置(复用输出) */ NORFLASH_CS(1); /* 取消片选 */ // spi2_init(); /* 初始化SPI2 */ spi_set_speed(SPI_SPEED_2); /* SPI 切换到高速状态 18Mhz */ g_norflash_type = norflash_read_id(); /* 读取FLASH ID. */ if (g_norflash_type == W25Q256) /* SPI FLASH为W25Q256, 必须使能4字节地址模式 */ { temp = norflash_read_sr(3); /* 读取状态寄存器3,判断地址模式 */ if ((temp & 0X01) == 0) /* 如果不是4字节地址模式,则进入4字节地址模式 */ { norflash_write_enable(); /* 写使能 */ temp |= 1 << 1; /* ADP=1, 上电4位地址模式 */ norflash_write_sr(3, temp); /* 写SR3 */ NORFLASH_CS(0); spi_read_write_byte(FLASH_Enable4ByteAddr); /* 使能4字节地址指令 */ NORFLASH_CS(1); } } //printf("ID:%x\r\n", g_norflash_type); } /** * @brief 等待空闲 * @param NONE * @retval NONE */ static void norflash_wait_busy(void) { while ((norflash_read_sr(1) & 0x01) == 0x01); /* 等待BUSY位清空 */ } /** * @brief 25QXX写使能 * @note 将S1寄存器的WEL置位 * @param NONE * @retval NONE */ void norflash_write_enable(void) { NORFLASH_CS(0); spi_read_write_byte(FLASH_WriteEnable); /* 发送写使能 */ NORFLASH_CS(1); } /** * @brief 25QXX发送地址 * @note 根据芯片型号不同,发送24bit/32bit地址 * @param address : 要发送的地址 * @retval NONE */ static void norflash_send_address(uint32_t address) { if (g_norflash_type == W25Q256) /* 只有W25Q256支持4字节地址模式 */ { spi_read_write_byte((uint8_t)((address)>>24)); /* 发送 bit31 ~ bit24 地址 */ } spi_read_write_byte((uint8_t)((address)>>16)); /* 发送 bit23 ~ bit16 地址 */ spi_read_write_byte((uint8_t)((address)>>8)); /* 发送 bit15 ~ bit8 地址 */ spi_read_write_byte((uint8_t)address); /* 发送 bit7 ~ bit0 地址 */ } /** * @brief 读取25QXX的状态寄存器,25QXX一共有3个状态寄存器 * @note 状态寄存器1: * 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 * * 状态寄存器2: * BIT7 6 5 4 3 2 1 0 * SUS CMP LB3 LB2 LB1 (R) QE SRP1 * * 状态寄存器3: * BIT7 6 5 4 3 2 1 0 * HOLD/RST DRV1 DRV0 (R) (R) WPS ADP ADS * * @param regno: 状态寄存器号,范围:1~3 * @retval 状态寄存器值 */ uint8_t norflash_read_sr(uint8_t regno) { uint8_t byte = 0, command = 0; switch (regno) { case 1: command = FLASH_ReadStatusReg1; /* 读状态寄存器1指令 */ break; case 2: command = FLASH_ReadStatusReg2; /* 读状态寄存器2指令 */ break; case 3: command = FLASH_ReadStatusReg3; /* 读状态寄存器3指令 */ break; default: command = FLASH_ReadStatusReg1; break; } NORFLASH_CS(0); spi_read_write_byte(command); /* 发送读寄存器命令 */ byte = spi_read_write_byte(0Xff); /* 读取一个字节 */ NORFLASH_CS(1); return byte; } /** * @brief 写25QXX状态寄存器 * @note 寄存器说明见norflash_read_sr函数说明 * @param regno: 状态寄存器号,范围:1~3 * @param sr : 要写入状态寄存器的值 * @retval NONE */ void norflash_write_sr(uint8_t regno, uint8_t sr) { uint8_t command = 0; switch (regno) { case 1: command = FLASH_WriteStatusReg1; /* 写状态寄存器1指令 */ break; case 2: command = FLASH_WriteStatusReg2; /* 写状态寄存器2指令 */ break; case 3: command = FLASH_WriteStatusReg3; /* 写状态寄存器3指令 */ break; default: command = FLASH_WriteStatusReg1; break; } NORFLASH_CS(0); spi_read_write_byte(command); /* 发送读寄存器命令 */ spi_read_write_byte(sr); /* 写一个字节 */ NORFLASH_CS(1); } /** * @brief 读取芯片ID * @param NONE * @retval FLASH芯片ID * @note 芯片ID列表见: norflash.h, 芯片列表部分 */ uint16_t norflash_read_id(void) { uint16_t deviceid; NORFLASH_CS(0); spi_read_write_byte(FLASH_ManufactDeviceID); /* 发送读 ID 命令 */ spi_read_write_byte(0); /* 写入一个字节 */ spi_read_write_byte(0); spi_read_write_byte(0); deviceid = spi_read_write_byte(0xFF) << 8; /* 读取高8位字节 */ deviceid |= spi_read_write_byte(0xFF); /* 读取低8位字节 */ NORFLASH_CS(1); return deviceid; } /** * @brief 读取SPI FLASH * @note 在指定地址开始读取指定长度的数据 * @param pbuf : 数据存储区 * @param addr : 开始读取的地址(最大32bit) * @param datalen : 要读取的字节数(最大65535) * @retval NONE */ void norflash_read(uint8_t *pbuf, uint32_t addr, uint16_t datalen) { uint16_t i; // 进入临界区 挂起所有任务 vTaskSuspendAll(); NORFLASH_CS(0); spi_read_write_byte(FLASH_ReadData); /* 发送读取命令 */ norflash_send_address(addr); /* 发送地址 */ for(i=0;i pageremain */ { pbuf += pageremain; /* pbuf指针地址偏移,前面已经写了pageremain字节 */ addr += pageremain; /* 写地址偏移,前面已经写了pageremain字节 */ datalen -= pageremain; /* 写入总长度减去已经写入了的字节数 */ if (datalen > 256) /* 剩余数据还大于一页,可以一次写一页 */ { pageremain = 256; /* 一次可以写入256字节 */ } else /* 剩余数据大于一页,可以一次写完 */ { pageremain = datalen; /* 不够256个字节了 */ } } } } /** * @brief 写SPI FLASH * @note 在指定地址开始写入指定长度的数据,该函数带擦除操作 * SPI FLASH 一般是: 256个字节为一个Page, 4Kbytes为1个Sector, 16个扇区为1个Block * 擦除的最小单位为Sector. * * @param pbuf : 数据存储区 * @param addr : addr : 开始写入的地址(最大32bit) * @param datalen : datalen : 要写入的字节数(最大65535) * @retval NONE */ uint8_t g_norflash_buf[4096]; /* 扇区缓存 */ void norflash_write(uint8_t *pbuf, uint32_t addr, uint16_t datalen) { uint32_t secpos; uint16_t secoff; uint16_t secremain; uint16_t i; uint8_t *norflash_buf; norflash_buf = g_norflash_buf; secpos = addr / 4096; /* 扇区地址 */ secoff = addr % 4096; /* 在扇区内的偏移 */ secremain = 4096 - secoff; /* 扇区剩余空间大小 */ //printf("ad:%X,nb:%X\r\n", addr, datalen); /* 测试用 */ if (datalen <= secremain) { secremain = datalen; /* 不大于4096个字节 */ } // 进入临界区 挂起所有任务 vTaskSuspendAll(); while (1) { norflash_read(norflash_buf, secpos * 4096, 4096); /* 读出整个扇区的内容 */ for (i = 0; i < secremain; i++) /* 校验数据 */ { if (norflash_buf[secoff + i] != 0XFF) { break; /* 需要擦除,直接退出for循环 */ } } if (i < secremain) /* 需要擦除 */ { norflash_erase_sector(secpos); /* 擦除这个扇区 */ for (i = 0; i < secremain; i++) /* 复制 */ { norflash_buf[i + secoff] = pbuf[i]; } norflash_write_nocheck(norflash_buf, secpos * 4096, 4096); /* 写入整个扇区 */ } else /* 写已经擦除了的,直接写入扇区剩余区间 */ { norflash_write_nocheck(pbuf, addr, secremain); /* 直接写扇区 */ } if (datalen == secremain) { break; /* 写入结束了 */ } else /* 写入未结束 */ { secpos++; /* 扇区地址增1 */ secoff = 0; /* 偏移位置未0 */ pbuf += secremain; /* 指针偏移 */ addr += secremain; /* 写地址偏移 */ datalen -= secremain; /* 字节数递减 */ if (datalen > 4096) { secremain = 4096; /* 下一个扇区还是写不完 */ } else { secremain = datalen;/* 下一个扇区可以写完了 */ } } } // 退出临界区 恢复任务调度 xTaskResumeAll(); } /** * @brief 擦除整个芯片 * @note 等待时间超长... * @param NONE * @retval NONE */ void norflash_erase_chip(void) { norflash_write_enable(); /* 写使能 */ norflash_wait_busy(); /* 等待空闲 */ NORFLASH_CS(0); spi_read_write_byte(FLASH_ChipErase); /* 发送读寄存器命令 */ NORFLASH_CS(1); norflash_wait_busy(); /* 等待芯片擦除结束 */ } /** * @brief 擦除一个扇区 * @note 注意,这里是扇区地址,不是字节地址 * 擦除一个扇区的最少时间:150ms * * @param saddr : 扇区地址,根据实际容量设置 * @retval NONE */ void norflash_erase_sector(uint32_t saddr) { //printf("fe:%x\r\n", saddr); /* 监视flash擦除情况,测试用 */ saddr *= 4096; norflash_write_enable(); /* 写使能 */ norflash_wait_busy(); /* 等待空闲 */ NORFLASH_CS(0); spi_read_write_byte(FLASH_SectorErase); /* 发送写页指令 */ norflash_send_address(saddr); /* 发送地址 */ NORFLASH_CS(1); norflash_wait_busy(); /* 等待扇区擦除完成 */ }