/* ********************************************************************************************************* * * 模块名称 : NOR Flash驱动模块 * 文件名称 : bsp_nor_flash.c * 版 本 : V1.0 * 说 明 : 安富莱STM32-V5开发板标配的NOR Flash 为 S29GL128P10TFI01 容量16M字节,16Bit,100ns速度 * 物理地址 : 0x6400 0000 * 修改记录 : * 版本号 日期 作者 说明 * V1.0 2013-02-01 armfly 正式发布 * * Copyright (C), 2013-2014, 安富莱电子 www.armfly.com * ********************************************************************************************************* */ /* S29GL128P 内存组织结构: 每个扇区128K字节,一共128个扇区。总容量为 16M字节。按16Bit访问。 挂在STM32上,对应的物理地址范围为 : 0x6400 0000 - 0x64FF FFFF. 只能按16Bit模式访问。 */ #include "nor_flash.h" #include "interface.h" #include #define ADDR_SHIFT(A) (NOR_FLASH_ADDR + (2 * (A))) #define NOR_WRITE(Address, Data) (*(__IO uint16_t *)(Address) = (Data)) /* 判忙时的执行语句循环次数 */ #define BlockErase_Timeout ((uint32_t)0x00A00000) #define ChipErase_Timeout ((uint32_t)0x30000000) #define Program_Timeout ((uint32_t)0x00001400) /* PD6 是NOR Flash输出到STM32的忙信号, 通过GPIO查询方式判忙 */ #define NOR_IS_BUSY() (GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_6) == RESET) static void NOR_QuitToReadStatus(void); static uint8_t NOR_GetStatus(uint32_t Timeout); /* ********************************************************************************************************* * 函 数 名: bsp_InitNorFlash * 功能说明: 配置连接外部NOR Flash的GPIO和FSMC * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ void nor_flash_init(void) { FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure; FSMC_NORSRAMTimingInitTypeDef p; /* NOR Flash 的 GPIO : PD0/FSMC_D2 PD1/FSMC_D3 PD4/FSMC_NOE PD5/FSMC_NWE PD6/FSMC_NWAIT - 忙信号,配置为GPIO,输入模式,通过软件查询方式判忙 PD8/FSMC_D13 PD9/FSMC_D14 PD10/FSMC_D15 PD11/FSMC_CLE/FSMC_A16 PD12/FSMC_ALE/FSMC_A17 PD13/FSMC_A18 PD14/FSMC_D0 PD15/FSMC_D1 PE3/FSMC_A19 PE4/FSMC_A20 PE5/FSMC_A21 PE6/FSMC_A22 PE7/FSMC_D4 PE8/FSMC_D5 PE9/FSMC_D6 PE10/FSMC_D7 PE11/FSMC_D8 PE12/FSMC_D9 PE13/FSMC_D10 PE14/FSMC_D11 PE15/FSMC_D12 PF0/FSMC_A0 PF1/FSMC_A1 PF2/FSMC_A2 PF3/FSMC_A3 PF4/FSMC_A4 PF5/FSMC_A5 PF12/FSMC_A6 PF13/FSMC_A7 PF14/FSMC_A8 PF15/FSMC_A9 PG0/FSMC_A10 PG1/FSMC_A11 PG2/FSMC_A12 PG3/FSMC_A13 PG4/FSMC_A14 PG5/FSMC_A15 PG9/FSMC_NE2 - 片选信号 */ /*-- FSMC Configuration ------------------------------------------------------*/ p.FSMC_AddressSetupTime = 0x06; /* 0x05正常, 0x04 出错 */ p.FSMC_AddressHoldTime = 0x01; p.FSMC_DataSetupTime = 0x0C; /* 0x0B正常, 0x0A 出错 */ p.FSMC_BusTurnAroundDuration = 0x00; p.FSMC_CLKDivision = 0x00; p.FSMC_DataLatency = 0x00; p.FSMC_AccessMode = FSMC_AccessMode_B; FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM2; FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_NOR; FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b; FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable; FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable; FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low; FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable; FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState; FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable; FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable; FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable; FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable; FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p; FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p; FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure); /*!< Enable FSMC Bank1_SRAM2 Bank */ FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM2, ENABLE); } /* ********************************************************************************************************* * 函 数 名: NOR_ReadID * 功能说明: 读取NOR Flash的器件ID * 形 参: 无 * 返 回 值: 器件ID,32Bit, 高8bit 是Manufacturer_Code, 低24bit是器件ID ********************************************************************************************************* */ uint32_t NOR_ReadID(void) { uint32_t uiID; uint8_t id1, id2, id3, id4; NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA); NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055); NOR_WRITE(ADDR_SHIFT(0x0555), 0x0090); id1 = *(__IO uint16_t *)ADDR_SHIFT(0x0000); id2 = *(__IO uint16_t *)ADDR_SHIFT(0x0001); id3 = *(__IO uint16_t *)ADDR_SHIFT(0x000E); id4 = *(__IO uint16_t *)ADDR_SHIFT(0x000F); uiID = ((uint32_t)id1 << 24) | ((uint32_t)id2 << 16) | ((uint32_t)id3 << 8) | id4; NOR_WRITE(NOR_FLASH_ADDR, 0x00F0); /* 退出ID模式 */ return uiID; } /* ********************************************************************************************************* * 函 数 名: NOR_QuitToReadStatus * 功能说明: 复位NOR,退到读状态 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void NOR_QuitToReadStatus(void) { NOR_WRITE(ADDR_SHIFT(0x00555), 0x00AA); NOR_WRITE(ADDR_SHIFT(0x002AA), 0x0055); NOR_WRITE(NOR_FLASH_ADDR, 0x00F0); } /* ********************************************************************************************************* * 函 数 名: NOR_GetStatus * 功能说明: 读取NOR的操作状态 * 形 参: 无 * 返 回 值: 0表示成功. NOR_SUCCESS, NOR_ERROR, NOR_TIMEOUT ********************************************************************************************************* */ static uint8_t NOR_GetStatus(uint32_t Timeout) { uint16_t val1 = 0x00; uint16_t val2 = 0x00; uint8_t status = NOR_ONGOING; uint32_t timeout = Timeout; /* 等待NOR输出忙信号,高电平时等待。避免NOR的忙信号还未反映过来导致CPU提前认为不忙了 */ while ((!NOR_IS_BUSY()) && (timeout > 0)) { timeout--; } /* 等待NOR忙信号结束,低电平时等待 */ timeout = Timeout; while (NOR_IS_BUSY() && (timeout > 0)) { timeout--; } /* - DQ 6 编程时跳变 - DQ 6 和 DQ 2 在擦除时跳变 - DQ 2 在擦除挂起时跳变 - DQ 1 在编程错误时置1 - DQ 5 在超时时置1 */ /* 通过读取DQ6, DQ5 的数据位是否存在翻转现象判断NOR 内部操作是否完成。如果正忙,则第2次读和第1次读的数据不同 */ while ((Timeout != 0x00) && (status != NOR_SUCCESS)) { Timeout--; /* Read DQ6 */ val1 = *(__IO uint16_t *)(NOR_FLASH_ADDR); val2 = *(__IO uint16_t *)(NOR_FLASH_ADDR); /* If DQ6 did not toggle between the two reads then return NOR_Success */ if ((val1 & 0x0040) == (val2 & 0x0040)) { return NOR_SUCCESS; } /* Read DQ2 */ if ((val1 & 0x0020) != 0x0020) { status = NOR_ONGOING; } val1 = *(__IO uint16_t *)(NOR_FLASH_ADDR); val2 = *(__IO uint16_t *)(NOR_FLASH_ADDR); if ((val1 & 0x0040) == (val2 & 0x0040)) { return NOR_SUCCESS; } else if ((val1 & 0x0020) == 0x0020) { status = NOR_ERROR; NOR_QuitToReadStatus(); } } if (Timeout == 0x00) { status = NOR_TIMEOUT; NOR_QuitToReadStatus(); } /* 返回操作状态 */ return (status); } /* ********************************************************************************************************* * 函 数 名: NOR_EraseChip * 功能说明: 擦除NOR Flash整个芯片 * 形 参: 无 * 返 回 值: 0表示成功 ********************************************************************************************************* */ uint8_t NOR_EraseChip(void) { NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA); NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055); NOR_WRITE(ADDR_SHIFT(0x0555), 0x0080); NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA); NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055); NOR_WRITE(ADDR_SHIFT(0x0555), 0x0010); return (NOR_GetStatus(ChipErase_Timeout)); } /* ********************************************************************************************************* * 函 数 名: NOR_StartEraseChip * 功能说明: 开始擦除NOR Flash整个芯片, 不等待结束 * 形 参: 无 * 返 回 值: 0表示成功 ********************************************************************************************************* */ void NOR_StartEraseChip(void) { NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA); NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055); NOR_WRITE(ADDR_SHIFT(0x0555), 0x0080); NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA); NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055); NOR_WRITE(ADDR_SHIFT(0x0555), 0x0010); NOR_GetStatus(1000); } /* ********************************************************************************************************* * 函 数 名: NOR_CheckComplete * 功能说明: 检测擦除是否完成 * 形 参: 无 * 返 回 值: 0表示成功 NOR_SUCCESS, NOR_ERROR, NOR_TIMEOUT ********************************************************************************************************* */ uint8_t NOR_CheckStatus(void) { uint16_t val1 = 0x00; uint16_t val2 = 0x00; uint8_t status = NOR_ONGOING; uint32_t timeout = 10; /* - DQ 6 编程时跳变 - DQ 6 和 DQ 2 在擦除时跳变 - DQ 2 在擦除挂起时跳变 - DQ 1 在编程错误时置1 - DQ 5 在超时时置1 */ /* 通过读取DQ6, DQ5 的数据位是否存在翻转现象判断NOR 内部操作是否完成。如果正忙,则第2次读和第1次读的数据不同 */ while ((timeout != 0x00) && (status != NOR_SUCCESS)) { timeout--; /* Read DQ6 */ val1 = *(__IO uint16_t *)(NOR_FLASH_ADDR); val2 = *(__IO uint16_t *)(NOR_FLASH_ADDR); /* If DQ6 did not toggle between the two reads then return NOR_Success */ if ((val1 & 0x0040) == (val2 & 0x0040)) { return NOR_SUCCESS; } /* Read DQ2 */ if ((val1 & 0x0020) != 0x0020) { status = NOR_ONGOING; } val1 = *(__IO uint16_t *)(NOR_FLASH_ADDR); val2 = *(__IO uint16_t *)(NOR_FLASH_ADDR); if ((val1 & 0x0040) == (val2 & 0x0040)) { return NOR_SUCCESS; } else if ((val1 & 0x0020) == 0x0020) { status = NOR_ERROR; NOR_QuitToReadStatus(); } } if (timeout == 0x00) { status = NOR_TIMEOUT; // NOR_QuitToReadStatus(); } /* 返回操作状态 */ return (status); } /* ********************************************************************************************************* * 函 数 名: NOR_EraseSector * 功能说明: 擦除NOR Flash指定的扇区 * 形 参: 扇区地址 * 返 回 值: NOR_SUCCESS, NOR_ERROR, NOR_TIMEOUT ********************************************************************************************************* */ uint8_t NOR_EraseSector(uint32_t _uiBlockAddr) { NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA); NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055); NOR_WRITE(ADDR_SHIFT(0x0555), 0x0080); NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA); NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055); NOR_WRITE((NOR_FLASH_ADDR + _uiBlockAddr), 0x30); return (NOR_GetStatus(BlockErase_Timeout)); } /* ********************************************************************************************************* * 函 数 名: NOR_ReadByte * 功能说明: 读取单字节数据 * 形 参: _uiWriteAddr : 偏移地址[0, 16*1024*1024 - 2]; 编程地址可以为偶数,也可以为奇数。 * 返 回 值: 读取到的数据 ********************************************************************************************************* */ uint8_t NOR_ReadByte(uint32_t _uiWriteAddr) { uint16_t usHalfWord; if (_uiWriteAddr % 2) /* 奇数地址 */ { usHalfWord = *(uint16_t *)(NOR_FLASH_ADDR + _uiWriteAddr - 1); return (usHalfWord >> 8); /* 取高8Bit */ } else /* 偶数地址 */ { usHalfWord = *(uint16_t *)(NOR_FLASH_ADDR + _uiWriteAddr); return usHalfWord; /* 取低8Bit */ } } /* ********************************************************************************************************* * 函 数 名: NOR_ReadBuffer * 功能说明: 连续读取NOR Flash * 形 参: _pBuf : 字节型数据缓冲区,用于存放读出的数据 * _uiWriteAddr : 偏移地址[0, 16*1024*1024 - 2]; 编程地址可以为偶数,也可以为奇数。 * _uiBytes : 字节大小 * 返 回 值: 读取到的数据 ********************************************************************************************************* */ void NOR_ReadBuffer(uint8_t *_pBuf, uint32_t _uiWriteAddr, uint32_t _uiBytes) { uint16_t usHalfWord; uint16_t *pNor16; uint32_t i; uint32_t uiNum; uiNum = _uiBytes; /* 处理首字节 */ if (_uiWriteAddr % 2) /* 奇数地址 */ { usHalfWord = *(uint16_t *)(NOR_FLASH_ADDR + _uiWriteAddr - 1); *_pBuf++ = (usHalfWord >> 8); /* 取高8Bit */ uiNum--; _uiWriteAddr++; /* 变为偶数 */ } /* 按照双字节模式连续读取NOR数据至缓冲区_pBuf */ pNor16 = (uint16_t *)(NOR_FLASH_ADDR + _uiWriteAddr); for (i = 0; i < uiNum / 2; i++) { usHalfWord = *pNor16++; *_pBuf++ = usHalfWord; *_pBuf++ = usHalfWord >> 8; uiNum -= 2; } /* 处理最后1个字节 */ if (uiNum == 1) { *_pBuf++ = *pNor16; } } /* ********************************************************************************************************* * 函 数 名: NOR_WriteHalfWord * 功能说明: 半字编程. 编程前执行解锁命令序列。编程完毕后,自动退到读取模式。半字编程可以是随机地址。 * 编程前需要保证存储单元是全0xFF状态。可以重复编程相同的数据。 * 形 参: _uiWriteAddr : 偏移地址[0, 16*1024*1024 - 2]; 编程地址必须为偶数 * _usData : 数据 16Bit * * 返 回 值: NOR_SUCCESS, NOR_ERROR, NOR_TIMEOUT ********************************************************************************************************* */ uint8_t NOR_WriteHalfWord(uint32_t _uiWriteAddr, uint16_t _usData) { NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA); NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055); NOR_WRITE(ADDR_SHIFT(0x0555), 0x00A0); NOR_WRITE(NOR_FLASH_ADDR + _uiWriteAddr, _usData); return (NOR_GetStatus(Program_Timeout)); } /* ********************************************************************************************************* * 函 数 名: NOR_WriteByte * 功能说明: 字节编程. 编程前需要保证存储单元是全0xFF状态。可以重复编程相同的数据。 * 形 参: _uiWriteAddr : 偏移地址[0, 16*1024*1024 - 1]; 编程地址可以为奇数也可以为偶数 * _usData : 数据 16Bit * * 返 回 值: NOR_SUCCESS, NOR_ERROR, NOR_TIMEOUT ********************************************************************************************************* */ uint8_t NOR_WriteByte(uint32_t _uiWriteAddr, uint8_t _ucByte) { uint16_t usHalfWord; if (_uiWriteAddr % 2) /* 奇数地址 */ { /* 读出2字节数据,然后改写高字节,维持以前的低字节数据不变 */ usHalfWord = *(uint16_t *)(NOR_FLASH_ADDR + _uiWriteAddr - 1); usHalfWord &= 0x00FF; usHalfWord |= (_ucByte << 8); } else { /* 读取NOR原始数据,保留高字节 */ usHalfWord = *(uint16_t *)(NOR_FLASH_ADDR + _uiWriteAddr); usHalfWord &= 0xFF00; usHalfWord |= _ucByte; } return NOR_WriteHalfWord(_uiWriteAddr, usHalfWord); } /* ********************************************************************************************************* * 函 数 名: NOR_WriteInPage. * 功能说明: 页面内编程(64字节一个页面). 编程前需要保证存储单元是全0xFF状态。可以重复编程相同的数据。 * 形 参: pBuffer : 数据存放在此缓冲区 * _uiWriteAddr : 偏移地址, 必须是偶数开始 * _usNumHalfword : 数据格式,双字节为1个单位. 值域: 1-32 * * 返 回 值: NOR_SUCCESS, NOR_ERROR, NOR_TIMEOUT ********************************************************************************************************* */ uint8_t NOR_WriteInPage(uint16_t *pBuffer, uint32_t _uiWriteAddr, uint16_t _usNumHalfword) { uint32_t lastloadedaddress; uint32_t currentaddress; uint32_t endaddress; /* pdf 表7.7 写入缓冲器编程 写入缓冲器编程允许系统在一个编程操作中写入最多32 个字。与标准的“ 字” 编程算法相比,这可以有效地 加快字编程速度。 */ if (_usNumHalfword > 32) { return NOR_ERROR; } if ((_uiWriteAddr % 2) != 0) { return NOR_ERROR; } _uiWriteAddr = _uiWriteAddr / 2; currentaddress = _uiWriteAddr; endaddress = _uiWriteAddr + _usNumHalfword - 1; lastloadedaddress = _uiWriteAddr; /* 解锁命令序列 */ NOR_WRITE(ADDR_SHIFT(0x00555), 0x00AA); NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055); /* Write Write Buffer Load Command */ NOR_WRITE(ADDR_SHIFT(_uiWriteAddr), 0x0025); NOR_WRITE(ADDR_SHIFT(_uiWriteAddr), (_usNumHalfword - 1)); /* Load Data into NOR Buffer */ while (currentaddress <= endaddress) { /* Store last loaded address & data value (for polling) */ lastloadedaddress = currentaddress; NOR_WRITE(ADDR_SHIFT(currentaddress), *pBuffer++); currentaddress += 1; } NOR_WRITE(ADDR_SHIFT(lastloadedaddress), 0x29); return (NOR_GetStatus(Program_Timeout)); } /* ********************************************************************************************************* * 函 数 名: NOR_WriteBuffer * 功能说明: 连续编程操作。采取半字编程模式。 * S29GL 支持64字节页面大小的连续编程。本函数暂时不支持页面编程。 * 形 参: _pBuf : 8位数据缓冲区 * _uiWriteAddr : 写入的存储单元首地址, 必须为偶数 * _uiBytes : 字节个数 * 返 回 值: NOR_SUCCESS, NOR_ERROR, NOR_TIMEOUT ********************************************************************************************************* */ uint8_t NOR_WriteBuffer(uint8_t *_pBuf, uint32_t _uiWriteAddr, uint32_t _uiBytes) { uint16_t usHalfWord; uint32_t i; uint32_t uiNum; uint8_t ucStatus; uiNum = _uiBytes; /* 处理首字节 */ if (_uiWriteAddr % 2) /* 奇数地址 */ { /* 读出2字节数据,然后改写高字节,维持以前的低字节数据不变 */ usHalfWord = *(uint16_t *)(NOR_FLASH_ADDR + _uiWriteAddr - 1); usHalfWord &= 0x00FF; usHalfWord |= ((*_pBuf++) << 8); ucStatus = NOR_WriteHalfWord(_uiWriteAddr - 1, usHalfWord); if (ucStatus != NOR_SUCCESS) { goto err_quit; } uiNum--; _uiWriteAddr++; /* 变为偶数 */ } /* 按照双字节模式连续编程NOR数据 */ for (i = 0; i < uiNum / 2; i++) { usHalfWord = *_pBuf++; usHalfWord |= ((*_pBuf++) << 8); ucStatus = NOR_WriteHalfWord(_uiWriteAddr, usHalfWord); if (ucStatus != NOR_SUCCESS) { goto err_quit; } _uiWriteAddr += 2; } /* 处理最后1个字节 */ if (uiNum % 2) { /* 读取NOR原始数据,保留高字节 */ usHalfWord = *(uint16_t *)(NOR_FLASH_ADDR + _uiWriteAddr); usHalfWord &= 0xFF00; usHalfWord |= (*_pBuf++); ucStatus = NOR_WriteHalfWord(_uiWriteAddr, usHalfWord); if (ucStatus != NOR_SUCCESS) { goto err_quit; } } ucStatus = NOR_SUCCESS; err_quit: return ucStatus; } /***************************** 安富莱电子 www.armfly.com (END OF FILE) *********************************/