stm32f767(nand flash sdram FMC)学习总结

最近项目需要用到nand flash,把nand flash和sdram学习记录总结了一下

NAND FLASH

NAND FLASH 存储分为块,每个数块又分为多个页
块为NAND FLASH擦除的最小单位,页为NAND FLASH写入的最小单位

NAND flash写入之前必须执行擦除,这是因为写入NAND flash时只能将存储单元从“1”变为“0”而不能从“0”变为“1”,写入之前擦除操作就是将所有的存储单元都置1。
NAND flash只能按页读、写。最初NAND flash数据读、写容易出错,所以为了保证数据的正确行,必须要有对应的检测和纠错机制
在这里插入图片描述

NAND FLASH 存储结构
Die(芯片)​​ → ​​Plane(平面)​​ → ​​Block(块)​​ → ​​Page(页)→ ​​sector(扇区)
以W29N01GVSIAA为例
扇区:512B
页:2KB
块:128KB
平面(Plane): 128*1024KB = 128MB

在这里插入图片描述

在这里插入图片描述

页读命令

当设备开机时,“00h”命令被锁定到命令寄存器。因此,程序中只需发送地址然后发送“30h”命令进行设备的初始读取。当然也可以按照完整的“页”读流程,首先发送“00h”命令,之后发送地址,最后发送“30h”命令。发送“30h”命令之后,在tR时间内数据从NAND flash存储区复制到了NAND flash数据缓冲区,之后就可以向单片机输出数据了
在这里插入图片描述

ID读取命令

要进入ID 模式首先要向nannd-flash 发送“90h”命令。进入ID 模式之后根据输地址不同后面的
读ID 命令将会有所不同
在这里插入图片描述

块擦除命令

nand_flash 只能整块擦除
向nand_flash 写入擦除命令“60h”如图29‑6 标号① 所示,然后写入要擦除的地址,如② 所示。
写入要擦除的地址后nand_flash 并不会立即执行擦除工作,在#WE 的上升沿写入“D0h”命令之
后擦除操作才能开始。在擦除过程中RY/#BY 为低电平,并且开启擦除之后可以通过发送“70h”
命令查看当前擦除状态,如果状态寄存器Bit6 为1 表示擦除完成
在这里插入图片描述

坏块标记和ECC校验

坏块标记: 由于 NAND FLASH 存储器存在擦写次数限制,当某个块的擦写次数达到一定阈值后,就会出现坏块。因此,需要在初始化阶段进行坏块识别和标记。一种常见的方法是使用块擦除前后的校验方式,如果块在擦除后的状态与预期不符,则将其标记为坏块,并记录在特定的坏块表中。FTL(Flash Translation Layer)层可以负责管理坏块信息,并在进行数据存储时避开这些坏块

ECC校验: ECC(Error Correction Code)校验用于检测和纠正数据在读取过程中出现的错误。在写入数据时,需要使用特定的算法计算出数据的 ECC 值,并将其与数据一同存储在 NAND FLASH 中的备用区域。在读取数据时,将数据和相应的 ECC 值一同读出,并使用相同的算法重新计算 ECC 值。然后,将重新计算的 ECC 值与存储的 ECC 值进行比较。如果两者相符,表示数据未发生错误;如果不符,则表示数据发生了错误。根据错误的严重程度,可以选择进行错误纠正或者丢弃数据

逻辑地址-物理地址转换表(LUT表): 将逻辑地址映射到物理地址,确保不指向坏块的物理地址

保留区: 替代坏块 ;实现磨损均衡

ECC校验: 常用ECC算法:汉明码,RS码,BCH码

NAND句柄结构体

typedef struct
{
FMC_NAND_TypeDef             *Instance;  /*!< Register base address                        */

FMC_NAND_InitTypeDef         Init;       /*!< NAND device control configuration parameters */

HAL_LockTypeDef              Lock;       /*!< NAND locking object                          */

__IO HAL_NAND_StateTypeDef   State;      /*!< NAND device access state                     */

NAND_InfoTypeDef             Info;       /*!< NAND characteristic information structure    */
}NAND_HandleTypeDef;

NAND FLASH驱动步骤
1.配置FMC
2.设置NANDFLASH相关参数,调用HAL_NAND_Init()
3.使能存储区域的操作,__FMC_NAND_ENABLE()
4.访问NAND FLASH:根据操作规范,向其发送命令,地址,数据

exp:写入数据封装函数:

/**
* @brief       在NAND一页中写入指定个字节的数据(main区和spare区都可以使用此函数)
* @param       PageNum         : 要写入的页地址,范围:0~(block_pagenum*block_totalnum-1)
* @param       ColNum          : 要写入的列开始地址(也就是页内地址),范围:0~(page_totalsize-1)
* @param       pBbuffer         : 指向数据存储区
* @param       NumByteToWrite: 要写入的字节数,该值不能超过该页剩余字节数!!!
* @retval      0,成功; 其他,错误代码
*/
uint8_t NAND_WritePage(uint32_t PageNum,uint16_t ColNum,uint8_t *pBuffer,uint16_t NumByteToWrite)
{
__IO uint16_t i=0;  
    uint8_t res=0;
    uint8_t eccnum=0;		//需要计算的ECC个数,每NAND_ECC_SECTOR_SIZE字节计算一个ecc
    uint8_t eccstart=0;		//第一个ECC值所属的地址范围
    
    *(__IO uint8_t*)(NAND_ADDRESS|NAND_CMD)=NAND_WRITE0;
    //发送地址
    *(__IO uint8_t*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)ColNum;
    *(__IO uint8_t*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(ColNum>>8);
    *(__IO uint8_t*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)PageNum;
    *(__IO uint8_t*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(PageNum>>8);

    NAND_Delay(30);//等待tADL 
    if(NumByteToWrite%NAND_ECC_SECTOR_SIZE)//不是NAND_ECC_SECTOR_SIZE的整数倍,不进行ECC校验
    {  
        for(i=0;i<NumByteToWrite;i++)		//写入数据
        {
            *(__IO uint8_t*)NAND_ADDRESS=*(__IO uint8_t*)pBuffer++;
        }
    }else
    {
        eccnum=NumByteToWrite/NAND_ECC_SECTOR_SIZE;			//得到ecc计算次数
        eccstart=ColNum/NAND_ECC_SECTOR_SIZE; 
        for(res=0;res<eccnum;res++)
        {
            FMC_Bank3->PCR|=1<<6;						//使能ECC校验 
            for(i=0;i<NAND_ECC_SECTOR_SIZE;i++)				//写入NAND_ECC_SECTOR_SIZE个数据
            {
                *(__IO uint8_t*)NAND_ADDRESS=*(__IO uint8_t*)pBuffer++;
            }		
            while(!(FMC_Bank3->SR&(1<<6)));				//等待FIFO空	
            nand_dev.ecc_hdbuf[res+eccstart]=FMC_Bank3->ECCR;	//读取硬件计算后的ECC值
            FMC_Bank3->PCR&=~(1<<6);						//禁止ECC校验
        }  
        i=nand_dev.page_mainsize+0X10+eccstart*4;			//计算写入ECC的spare区地址
        NAND_Delay(30);//等待 
        *(__IO uint8_t*)(NAND_ADDRESS|NAND_CMD)=0X85;				//随机写指令
        //发送地址
        *(__IO uint8_t*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)i;
        *(__IO uint8_t*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(i>>8);
        NAND_Delay(30);//等待tADL 
        pBuffer=(uint8_t*)&nand_dev.ecc_hdbuf[eccstart];
        for(i=0;i<eccnum;i++)					//写入ECC
        { 
            for(res=0;res<4;res++)				 
            {
                *(__IO uint8_t*)NAND_ADDRESS=*(__IO uint8_t*)pBuffer++;
            }
        } 		
    }

    *(__IO uint8_t*)(NAND_ADDRESS|NAND_CMD)=NAND_WRITE_TURE1; 

    if(NAND_WaitForReady()!=NSTA_READY)
    {
    return NSTA_ERROR;//失败
    }
    
    return 0;//成功   
}


SDRAM

SDRAM控制引脚说明
在这里插入图片描述

SDRAM内部包含的存储阵列(Bank)可理解为一张表格
在这里插入图片描述

SDRAM 芯片向外部提供有独立的BA类地址线用于Bank寻址,而行与列则共用A类地址线

在这里插入图片描述

SDRAM的命令表

1.命令禁止: 用于禁止SDRAM执行新的命令,但它不能停止当前正在执行的命令
2.空操作: 用于选中SDRAM,以便接下来发送命令
3.行有效: 进行存储单元寻址时,需要先选中要访问的Bank 和行,使它处于激活状态
4.列读写: 行地址通过“行有效”命令确定后,就要对列地址进行寻址了。“读命令”(READ) 和“写命令”(WRITE) 的时序很相似,见读取命令时序,通过共用的地址线A 发送列地址,同时使用WE引脚表示读/写方向,WE 为低电平时表示写,高电平时表示读。数据读写时,使用DQM 线表示有效的DQ数据线
5.预充电: SDRAM的寻址具有独占性,所以在进行完读写操作后,如果要对同一个Bank 的另一行进行寻址,就要将原来有效(ACTIVE)的行关闭,重新发送行/列地址。Bank关闭当前工作行,准备打开新行的操作就是预充电(Precharge)
6.刷新: SDRAM 要不断进行刷新(Refresh) 才能保留住数据,因此它是DRAM 最重要的操作。刷新有固定的周期,依次对所有行进行操作,以保证那些久久没被访问的存储单元数据正确

SDRAM的初始化流程
(1) 给SDRAM 上电,并提供稳定的时钟,至少100us;
(2) 发送“空操作”(NOP)
(3) 发送“预充电”(PRECHARGE) 命令,控制所有Bank 进行预充电
(4) 发送至少2 个“自动刷新”(AUTO REFRESH) 命令
(5) 发送“加载模式寄存器”(LOAD MODE REGISTER) 命令,配置SDRAM 的工作参数,并等
待tMRD 时间,tMRD 表示加载模式寄存器命令与行有行或刷新命令之间的延迟;
(6) 初始化流程完毕,可以开始读写数据。

SDRAM 的读写流程
(1) 发送“行有效”(ACTIVE) 命令,发送命令的同时包含行地址和Bank 地址
(2) 发送“读/写”(READ/WRITE) 命令,在发送命令的同时发送列地址,完成寻址的地址输入
(3) 发送第二次“行有效”(ACTIVE) 命令准备读写下一个数据

FMC:可变存储控制器用于驱动包括SRAM、SDRAM、NOR FLASH 以及NAND FLSAH 类型

NOR/PSRAM/SRAM 设备使用相同的控制器,NAND/PC 卡设备使用相同的控制器,而SDRAM 存储器使用独立的控制器。不同的控制器有专用的寄存器用于配置其工作模式

SDRAM控制器
FMC_SDCR 控制寄存器可配置SDCLK 的同步时钟频率、突发读使能、写保护、CAS 延迟、行列地址位数以及数据总线宽度等。
FMC_SDTR 时序寄存器用于配置SDRAM 访问时的各种时间延迟,如TRP 行预充电延迟、TMRD
加载模式寄存器激活延迟等。
FMC_SDCMR 命令模式寄存器用于存储要发送到SDRAM 模式寄存器的配置,以及要向SDRAM
芯片发送的命令。
FMC_SDRTR 用于配置SDRAM 的自动刷新周期

FMC地址映射

FMC 连接好外部的存储器并初始化后,就可以直接通过访问地址来读写数据,这种地址访问与I2C EEPROM、SPI FLASH 的不一样,后两种方式都需要控制I2C 或SPI 总线给存储器发送地址,然后获取数据;在程序里,这个地址和数据都需要分开使用不同的变量存储,并且访问时还需要使用代码控制发送读写命令
而使用FMC 外接存储器时,其存储单元是映射到STM32 的内部寻址空间的;在程序里,定义一个指向这些地址的指针,然后就可以通过指针直接修改该存储单元的内容,FMC 外设会自动完成数据访问过程,读写命令之类的操作不需要程序控制

SDRAM时序结构体

/* @brief 控制 SDRAM 的时序参数,这些参数的单位都是“周期”
* 各个参数的值可设置为 1-16 个周期。 */
typedef struct
{
uint32_t LoadToActiveDelay; /*TMRD: 加载模式寄存器命令后的延迟 */
uint32_t ExitSelfRefreshDelay; /*TXSR: 自刷新命令后的延迟 */
uint32_t SelfRefreshTime; /*TRAS: 自刷新时间 */
uint32_t RowCycleDelay; /*TRC: 行循环延迟 */
uint32_t WriteRecoveryTime; /*TWR: 恢复延迟 */
uint32_t RPDelay; /*TRP: 行预充电延迟 */
uint32_t RCDDelay; /*TRCD: 行到列延迟 */
} FMC_SDRAM_TimingTypeDef

SDRAM初始化结构体

/* @brief FMC SDRAM 初始化结构体类型定义 */
typedef struct
{
uint32_t Bank; /* 选择 FMC 的 SDRAM 存储区域 */
uint32_t ColumnBitsNumber; /* 定义 SDRAM 的列地址宽度 */
uint32_t RowBitsNumber; /* 定义 SDRAM 的行地址宽度 */
uint32_t MemoryDataWidth; /* 定义 SDRAM 的数据宽度 */
uint32_t InternalBankNumber; /* 定义 SDRAM 内部的 Bank 数目 */
uint32_t CASLatency; /* 定义 CASLatency 的时钟个数 */
uint32_t WriteProtection; /* 定义是否使能写保护模式 */
uint32_t SDClockPeriod; /* 配置同步时钟 SDCLK 的参数 */
uint32_t ReadBurst; /* 是否使能突发读模式 */
uint32_t ReadPipeDelay; /* 定义在 CAS 个延迟后再等待多少个 HCLK 时钟才读取数据 */
} FMC_SDRAM_InitTypeDef;

(1) Bank
本成员用于选择 FMC 映射的 SDRAM 存储区域,可选择存储区域 1 或 2
(FMC_SDRAM_BANK1/FMC_SDRAM_BANK2)。
(2) ColumnBitsNumber
本 成 员 用 于 设 置 要 控 制 的 SDRAM 的 列 地 址 宽 度, 可 选 择 8-11 位
(FMC_SDRAM_COLUMN_BITS_NUM_8/9/10/11b)。
(3) RowBitsNumber
本成员用于设置要控制的 SDRAM 的行地址宽度,可选择设置成 11-13 位
(FMC_SDRAM_ROW_BITS_NUM_11/12/13b)。
(4) MemoryDataWidth
本成员用于设置要控制的 SDRAM 的数据宽度,可选择设置成 8、16 或 32 位
(FMC_SDRAM_MEM_BUS_WIDTH_8/16/32b)。
(5) InternalBankNumber
本成员用于设置要控制的 SDRAM 的内部 Bank 数目,可选择设置成 2 或 4 个 Bank 数目(FMC_SDRAM_INTERN_BANKS_NUM_2/4),请注意区分这个结构体成员与Bank 的区别。
(6) CASLatency
本成员用于设置 CASLatency 即 CL 的时钟数目,可选择设置为 1、2 或 3 个时钟周期
(FMC_SDRAM_CAS_LATENCY_1/2/3)。
(7) WriteProtection
本成员用于设置是否使能写保护模式,如果使能了写保护则不能向SDRAM 写入数据,正
常使用都是禁止写保护的。
(8) ClockPeriod
本成员用于设置 FMC 与外部 SDRAM 通讯时的同步时钟参数,可以设置成 STM32
的 HCLK 时钟频率的 1/2、1/3 或禁止输出时钟 (FMC_SDRAM_CLOCK_PERIOD_2/3 或
FMC_SDRAM_CLOCK_DISABLE)。
(9) ReadBurst
本成员用于设置是否使能突发读取模式,禁止时等效于BL=1,使能时BL 的值等于模式寄
存器中的配置。
(10) ReadPipeDelay
本成员用于配置在CASLatency 个时钟周期后,再等待多少个HCLK 时钟周期才进行
数据采样,在确保正确的前提下,这个值设置为越短越好,可选择设置的参数值为0、
1 或2 个HCLK 时钟周期(FMC_SDRAM_RPIPE_DELAY_0/1/2)。
配置完SDRAM 初始化结构体后,调用FMC_SDRAMInit 函数把这些配置写入到FMC 的SDRAM
控制寄存器及时序寄存器,实现FMC 的初始化。

SDRAM命令结构体

typedef struct
{
uint32_t CommandMode; /* 要发送的命令 */
uint32_t CommandTarget; /* 目标存储器区域 */
uint32_t AutoRefreshNumber; /* 若发送的是自动刷新命令,
此处为发送的刷新次数,其它命令时无效 */
uint32_t ModeRegisterDefinition; /* 若发送的是加载模式寄存器命令,此处为要写入 SDRAM 模式寄存器的参数 */
} FMC_SDRAM_CommandTypeDef;

(1) CommandMode
本成员用于配置将要发送的命令,它可以被赋值为表26‑4 中的宏,这些宏代表了不同命令;
表26‑4 FMC 可输出的SDRAM 控制命令
宏 命令说明
FMC_SDRAM_CMD_NORMAL_MODE 正常模式命令
FMC_SDRAM_CMD_CLK_ENABLE 使能CLK 命令
FMC_SDRAM_CMD_PALL 对所有Bank 预充电命令
FMC_SDRAM_CMD_AUTOREFRESH_MODE 自动刷新命令
FMC_SDRAM_CMD_LOAD_MODE 加载模式寄存器命令
FMC_SDRAM_CMD_SELFREFRESH_MODE 自我刷新命令
FMC_SDRAM_CMD_POWERDOWN_MODE 掉电命令
(2) CommandTarget
本成员用于选择要控制的 FMC 存储区域,可选择存储区域 1 或 2 或 1 和
2(FMC_SDRAM_CMD_TARGET_BANK1/2,FMC_SDRAM_CMD_TARGET_BANK1_2);
(3) AutoRefreshNumber
有时需要连续发送多个“自动刷新”(Auto Refresh) 命令时,配置本成员即可控制它发送多
少次,可输入参数值为1-16,若发送的是其它命令,本参数值无效。如CommandMode 成
员被配置为宏FMC_SDRAM_CMD_AUTOREFRESH_MODE,而AutoRefreshNumber 被设置
为2 时,FMC 就会控制发送2 次自动刷新命令。
(4) ModeRegisterDefinition
当向 SDRAM 发送加载模式寄存器命令时,这个结构体成员的值将通过地址线发送到
SDRAM 的模式寄存器中,这个成员值长度为 13 位,各个位一一对应 SDRAM 的模式寄
存器。
配置完这些结构体成员,调用库函数 HAL_SDRAM_SendCommand 即可把这些参数写入到
FMC_SDCMR 寄存器中,然后FMC 外设就会发送相应的命令了

初始化SDRAM

void SDRAM_Init(void)
{
//FMC_SDRAMInitTypeDef  FMC_SDRAMInitStructure;
//  FMC_SDRAMTimingInitTypeDef  FMC_SDRAMTimingInitStructure; 

/* 配置FMC接口相关的 GPIO*/
SDRAM_GPIO_Config();

/* 使能 FMC 时钟 */
    __HAL_RCC_FMC_CLK_ENABLE();
//  RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FMC, ENABLE);

//  /* 配置 FMC 相关参数 ---------------------------------------------------------*/
//  /* SDCLK: 90 Mhz (HCLK/2 :180Mhz/2) */
//  /* TMRD: 2 Clock cycles */
//  FMC_SDRAMTimingInitStructure.FMC_LoadToActiveDelay    = 2;      
//  /* TXSR: min=70ns (7x11.11ns) */
//  FMC_SDRAMTimingInitStructure.FMC_ExitSelfRefreshDelay = 7;
//  /* TRAS: min=42ns (4x11.11ns) max=120k (ns) */
//  FMC_SDRAMTimingInitStructure.FMC_SelfRefreshTime      = 4;
//  /* TRC:  min=70 (7x11.11ns) */        
//  FMC_SDRAMTimingInitStructure.FMC_RowCycleDelay        = 7;         
//  /* TWR:  min=1+ 7ns (1+1x11.11ns) */
//  FMC_SDRAMTimingInitStructure.FMC_WriteRecoveryTime    = 2;      
//  /* TRP:  20ns => 2x11.11ns */
//  FMC_SDRAMTimingInitStructure.FMC_RPDelay              = 2;                
//  /* TRCD: 20ns => 2x11.11ns */
//  FMC_SDRAMTimingInitStructure.FMC_RCDDelay             = 2;

///* FMC SDRAM 控制配置 */
//  FMC_SDRAMInitStructure.FMC_Bank = FMC_BANK_SDRAM;
//  /* 行地址线宽度: [7:0] */
//  FMC_SDRAMInitStructure.FMC_ColumnBitsNumber = FMC_ColumnBits_Number_8b;
//  /* 列地址线宽度: [11:0] */
//  FMC_SDRAMInitStructure.FMC_RowBitsNumber = FMC_RowBits_Number_12b;
//  /* 数据线宽度 */
//  FMC_SDRAMInitStructure.FMC_SDMemoryDataWidth = SDRAM_MEMORY_WIDTH;
//  /* SDRAM内部bank数量*/
//  FMC_SDRAMInitStructure.FMC_InternalBankNumber = FMC_InternalBank_Number_4;
//  /* CAS潜伏期 */
//  FMC_SDRAMInitStructure.FMC_CASLatency = SDRAM_CAS_LATENCY; 
//  /* 禁止写保护*/
//  FMC_SDRAMInitStructure.FMC_WriteProtection = FMC_Write_Protection_Disable;
//  /* SDCLK时钟分频因子,SDCLK = HCLK/SDCLOCK_PERIOD*/
//  FMC_SDRAMInitStructure.FMC_SDClockPeriod = SDCLOCK_PERIOD; 
//  /* 突发读模式设置*/  
//  FMC_SDRAMInitStructure.FMC_ReadBurst = SDRAM_READBURST;
//  /* 读延迟配置 */
//  FMC_SDRAMInitStructure.FMC_ReadPipeDelay = FMC_ReadPipe_Delay_1;
//  /* SDRAM时序参数 */
//  FMC_SDRAMInitStructure.FMC_SDRAMTimingStruct = &FMC_SDRAMTimingInitStructure;
//  
//  /* FMC SDRAM bank initialization */
//  FMC_SDRAMInit(&FMC_SDRAMInitStructure); 
FMC_SDRAM_TimingTypeDef SdramTiming;

/** Perform the SDRAM1 memory initialization sequence
*/
hsdram1.Instance = FMC_SDRAM_DEVICE;
/* hsdram1.Init */
hsdram1.Init.SDBank = FMC_SDRAM_BANK2;
hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;
hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;
hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;
hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_DISABLE;
hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_1;
/* SdramTiming */
SdramTiming.LoadToActiveDelay = 2;
SdramTiming.ExitSelfRefreshDelay = 7;
SdramTiming.SelfRefreshTime = 4;
SdramTiming.RowCycleDelay = 7;
SdramTiming.WriteRecoveryTime = 2;
SdramTiming.RPDelay = 2;
SdramTiming.RCDDelay = 2;

HAL_SDRAM_Init(&hsdram1, &SdramTiming);  
/* FMC SDRAM device initialization sequence */
SDRAM_InitSequence(); 

}

使用指针的方式访问SDRAM 存储器

exp

/*SDRAM 起始地址 存储空间 2 的起始地址 */
#define SDRAM_BANK_ADDR ((uint32_t)0xD0000000)
/*SDRAM 大小,8M 字节 */
#define W9825G6KH_SIZE 0x2000000  //2000000*16bits = 0x2000000  ,32M字节
uint32_t temp;
/* 向 SDRAM 写入 8 位数据 */
*( uint8_t*) (SDRAM_BANK_ADDR ) = (uint8_t)0xAA;
/* 从 SDRAM 读取数据 */
temp = *( uint8_t*) (SDRAM_BANK_ADDR );

/* 写/读 16 位数据 */
*( uint16_t*) (SDRAM_BANK_ADDR+10 ) = (uint16_t)0xBBBB;
temp = *( uint16_t*) (SDRAM_BANK_ADDR+10 );
/* 写/读 32 位数据 */
*( uint32_t*) (SDRAM_BANK_ADDR+20 ) = (uint32_t)0xCCCCCCCC;
temp = *( uint32_t*) (SDRAM_BANK_ADDR+20 );

直接指定变量存储到SDRAM 空间

exp

/*SDRAM 起始地址 存储空间 2 的起始地址 */
#define SDRAM_BANK_ADDR ((uint32_t)0xD0000000)
/* 绝对定位方式访问 SDRAM, 这种方式必须定义成全局变量 */
uint8_t testValue __attribute__((at(SDRAM_BANK_ADDR)));
testValue = 0xDD;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值