目录
概述
1 硬件
1.1 MCU和S29GL128P10TFI01控制电路
1.1.1 S29GL128P10TFI01
1.1.2 MCU与NOR Flash接口
1.2 STM32F4的FSMC接口
1.2.1 时序信号
1.2.2 外部存储器接口信号
2 Flash leader功能实现
2.1 框架结构介绍
2.2 S29GL128P10TFI01的Flash leader框架
2.3 NOR 驱动程序
2.3 Dev_Inf的参数
3 Keil中的配置
3.1 配置参数
3.2 编译项目
4 测试
4.1 ST-link连接板卡
4.2 下载文件测试
源代码下载地址:
M29W128GL-STM32F4-ALY源代码资源-CSDN文库
概述
本文主要介绍基于STM32F407芯片,NOR Flash芯片为S29GL128P10TFI01。使用其设计一个Flashloader 程序,并且在STM32CubeProgrammer工具中使用该文件,实现NOR Flash擦除数据,编程数据,读取数据的功能。
1 硬件
1.1 MCU和S29GL128P10TFI01控制电路
1.1.1 S29GL128P10TFI01
芯片S29GL128P10TFI01是一款闪存芯片,由Spansion公司生产。它具有128Mbit(16MB)的容量,工作电压为2.7V至3.6V,采用主要的块擦除技术和512字节的页面编程。该芯片采用NOR闪存架构,可用于嵌入式系统、网络设备、消费电子产品等领域。它具有较快的读取和编程速度,适用于需要大容量存储和快速数据访问的应用场景。
1.1.2 MCU与NOR Flash接口
NOR Flash与MCU通过FSMC接口进行连接,其接口框图图形如下,NOR Flash和SRAM共用一个FSMC接口,可通过配置下表寄存器的值,选择相应的区域,以确定Flash对应的接口。
STM32407与 S29GL128P10TFI01芯片的硬件电路图,该Flash在MCU上的操作起始地址为0x6400 0000。
1.2 STM32F4的FSMC接口
1.2.1 时序信号
FSMC 会生成适当的信号时序,以驱动以下类型的存储器:
● 异步 SRAM 和 ROM
— 8 位
— 16 位
— 32 位
● PSRAM( Cellular RAM)
— 异步模式
— 突发模式
— 复用或非复用
● NOR Flash
— 异步模式或突发模式
— 复用或非复用
FSMC 会为每个存储区域输出唯一的片选信号 NE[4:1]。所有其它信号(地址、数据和控制)均为共享信号。对于同步访问, FSMC 只有在读/写事务期间才会向所选的外部器件发出时钟 (CLK)。 HCLK时钟频率是该时钟的整数倍。每个存储区域的大小固定,均为 64 MB。每个存储区域都通过专用的寄存器进行配置。存储器的可编程参数包括访问时序和对等待管理的支持(用于在突发模式下访问 NOR Flash和 PSRAM)。
1.2.2 外部存储器接口信号
1)非复用 I/O NOR Flash
NOR Flash 存储器采用 16 位字寻址。最大容量为 512 Mb( 26 个地址线)。
2)复用 I/O NOR Flash
NOR-Flash 存储器采用 16 位字寻址。最大容量为 512 Mb( 26 个地址线)。
1.3 软硬件信息
软硬件信息 | 版本信息 |
---|---|
STM32 MCU | STM32F407IGTx |
NOR Flash | S29GL128P10TFI01 |
Keil | MDK ARM 5.38 |
调试工具:st-link | ST-LINK/V2-1 |
STM32CubeProgrammer | v2.16.0 |
2 Flash leader功能实现
2.1 框架结构介绍
安装STM32CubeProgrammer软件之后,ST提供了许多Demo可供参考,在\STM32CubeProgrammer\bin\ExternalLoader目录下有一个基于S29GL128P10TFI01的外部Flash loader。
打开该代码之后可以看见如下代码结构,该项目的代码分为两个部分
leader文件目录: Flash leader驱动相关接口
STM32xxx目录: 和MCU相关的驱动目录
2.2 S29GL128P10TFI01的Flash leader框架
参考M29W128GL_STM3210E-EVAL设计S29GL128P10TFI01_STM32F4的Flash leader功能程序。复制一份M29W128GL_STM3210E-EVAL的代码,命名如下:
打开项目文件,得到如下目录结构,让后将STM32F1xx的库文件,换成STM32F4XX的库文件
2.3 NOR 驱动程序
创建stm32f4_fsmc_nor.c文件,编写如下代码:
#include "stm32f4_fsmc_nor.h"
#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 bsp_InitNorFlash(void)
{
FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
FSMC_NORSRAMTimingInitTypeDef p;
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能GPIO时钟 */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOF |
RCC_AHB1Periph_GPIOG, ENABLE);
/* 使能 FSMC 时钟 */
RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC, ENABLE);
/* 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 - 片选信号
*/
/* GPIOD configuration */
GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource4, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource10, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource11, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_FSMC);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5 |
GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 |
GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* GPIOE configuration */
GPIO_PinAFConfig(GPIOE, GPIO_PinSource3 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource4 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource5 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource6 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource7 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource8 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource9 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource10 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource11 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource12 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource13 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource14 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOE, GPIO_PinSource15 , GPIO_AF_FSMC);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 |
GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 |
GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11|
GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_Init(GPIOE, &GPIO_InitStructure);
/* GPIOF configuration */
GPIO_PinAFConfig(GPIOF, GPIO_PinSource0 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource1 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource2 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource3 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource4 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource5 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource12 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource13 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource14 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource15 , GPIO_AF_FSMC);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 |
GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_12 | GPIO_Pin_13 |
GPIO_Pin_14 | GPIO_Pin_15;
GPIO_Init(GPIOF, &GPIO_InitStructure);
/* GPIOG configuration */
GPIO_PinAFConfig(GPIOG, GPIO_PinSource0 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG, GPIO_PinSource1 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG, GPIO_PinSource2 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG, GPIO_PinSource3 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG, GPIO_PinSource4 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG, GPIO_PinSource5 , GPIO_AF_FSMC);
GPIO_PinAFConfig(GPIOG, GPIO_PinSource9 , GPIO_AF_FSMC);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 |
GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_9;
GPIO_Init(GPIOG, &GPIO_InitStructure);
/* PD6 作为忙信号, 配置为GPIO输入模式,软件查询 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; /* 输入模式 */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/*-- 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_ReadBuffer8Bit(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_WriteBuffer8Bit(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;
}
NOR_STATUS NOR_WriteBuffer(uint16_t* pBuffer, uint32_t WriteAddr, uint32_t NumHalfwordToWrite)
{
NOR_STATUS status = NOR_ONGOING;
do
{
/*!< Transfer data to the memory */
status = NOR_WriteHalfWord(WriteAddr, *pBuffer++);
WriteAddr = WriteAddr + 2;
NumHalfwordToWrite--;
}
while((status == NOR_SUCCESS) && (NumHalfwordToWrite != 0));
return (status);
}
void NOR_ReadBuffer(uint16_t* pBuffer, uint32_t ReadAddr, uint32_t NumHalfwordToRead)
{
NOR_WRITE(ADDR_SHIFT(0x0555), 0x00AA);
NOR_WRITE(ADDR_SHIFT(0x02AA), 0x0055);
NOR_WRITE((NOR_FLASH_ADDR + ReadAddr), 0x00F0);
for(; NumHalfwordToRead != 0x00; NumHalfwordToRead--) /*!< while there is data to read */
{
/*!< Read a Halfword from the NOR */
*pBuffer++ = *(__IO uint16_t *)((NOR_FLASH_ADDR + ReadAddr));
ReadAddr = ReadAddr + 2;
}
}
创建stm32f4_fsmc_nor.h文件,编写如下代码:
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F4_FSMC_NOR_H
#define __STM32F4_FSMC_NOR_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx.h"
#define NOR_FLASH_ADDR ((uint32_t)0x64000000)
#define NOR_SECTOR_SIZE (128 * 1024) /* 扇区大小 */
#define NOR_SECTOR_COUNT 128 /* 扇区个数 */
#define NOR_FLASH_SIZE (NOR_SECTOR_SIZE * NOR_SECTOR_COUNT)
/*
制造商ID:Spansion 0x01
S29GL01GP 01 7E 28 01 1 Gigabit 128M字节
S29GL512P 01 7E 23 01 512 Megabit 64M字节
S29GL256P 01 7E 22 01 256 Megabit 32M字节
S29GL128P 01 7E 21 01 128 Megabit 16M字节
*/
typedef enum
{
S29GL128P = 0x017E2101,
S29GL256P = 0x017E2201,
S29GL512P = 0x017E2301
}NOR_CHIP_ID;
/* NOR Status */
typedef enum
{
NOR_SUCCESS = 0,
NOR_ONGOING = 1,
NOR_ERROR = 2,
NOR_TIMEOUT = 3
}NOR_STATUS;
void bsp_InitNorFlash(void);
uint32_t NOR_ReadID(void);
uint8_t NOR_EraseChip(void);
uint8_t NOR_EraseSector(uint32_t _uiBlockAddr);
uint8_t NOR_ReadByte(uint32_t _uiWriteAddr);
void NOR_ReadBuffer(uint16_t *_pBuf, uint32_t _uiWriteAddr, uint32_t _uiBytes);
uint8_t NOR_WriteHalfWord(uint32_t _uiWriteAddr, uint16_t _usData);
uint8_t NOR_WriteByte(uint32_t _uiWriteAddr, uint8_t _ucByte);
uint8_t NOR_WriteInPage(uint16_t *pBuffer, uint32_t _uiWriteAddr, uint16_t _usNumHalfword);
NOR_STATUS NOR_WriteBuffer(uint16_t *_pBuf, uint32_t _uiWriteAddr, uint32_t _uiBytes);
void NOR_StartEraseChip(void);
uint8_t NOR_CheckStatus(void);
#ifdef __cplusplus
}
#endif
#endif /* __STM32F4_FSMC_NOR_H */
2.3 Dev_Inf的参数
在Dev_Inf.c文件中实现如下代码(注意在该文件中不要添加任何头文件或者函数),修改对应的参数,使其和板卡上的芯片参数一致
源代码如下:
#include "Dev_Inf.h"
/* This structure containes information used by ST-LINK Utility to program and erase the device */
struct StorageInfo const StorageInfo = {
"M29W128GL_STM32F407ALY", // Device Name + version number
NOR_FLASH, // Device Type
0x64000000, // Device Start Address
0x01000000, // Device Size in Bytes (16MBytes/128Mbits)
0x00000010, // Programming Page Size 16Bytes
0xFF, // Initial Content of Erased Memory
// Specify Size and Address of Sectors (view example below)
0x00000080, 0x00020000, // Sector Num : 128 ,Sector Size: 64KBytes
0x00000000, 0x00000000,
};
/*
Sector coding example
A device with succives 16 Sectors of 1KBytes, 128 Sectors of 16 KBytes,
8 Sectors of 2KBytes and 16384 Sectors of 8KBytes
0x00000010, 0x00000400, // 16 Sectors of 1KBytes
0x00000080, 0x00004000, // 128 Sectors of 16 KBytes
0x00000008, 0x00000800, // 8 Sectors of 2KBytes
0x00004000, 0x00002000, // 16384 Sectors of 8KBytes
0x00000000, 0x00000000, // end
*/
/* End of this file */
3 Keil中的配置
3.1 配置参数
1) 选择MCU的类型
2)定义Output文件的文件名称
3)复制生成的.stldr文件到固定的目录
cmd.exe /C copy "!L" "..\..\..\@L.stldr"
4)使能Library, 生成代码配置为图中选项
5)配置汇编选项中的代码特征
6)配置链接文件
3.2 编译项目
配置完成参数后,就可以编译项目,编译结果如下:
同时,在如下目录中看见生成的文件名称
4 测试
4.1 ST-link连接板卡
打开STM32CubeProgrammer工具,使用ST-link和板卡连接起来,选中Flash leader 源文件
如果连接正常,可以看见,ST-Link从NOR Flash中读取到了数据
4.2 下载文件测试
1)写Flash文件到Nor Flash之前,首先需要擦除芯片内的信息
擦除完成后,可以看见Flash中的数据全部为0xff,说明擦除Flash数据完成
2)下载数据至Flash中
3)读取数据,读取数据的地址位0x6400 0000, 比较读写的数据文件,二者完全相同