- /**
- * [url=home.php?mod=space&uid=247401]@brief[/url] SMC NOR/SRAM Config structure
- */
- typedef struct
- {
- SMC_BANK1_NORSRAM_T bank; /*!< NORSRAM bank selection */
- SMC_DATA_ADDRESS_MUX_T dataAddressMux; /*!< Data address bus multiplexing selection */
- SMC_MEMORY_TYPE_T memoryType; /*!< Memory type selection */
- SMC_MEMORY_DATA_WIDTH_T memoryDataWidth; /*!< Data width selection */
- SMC_BURST_ACCESS_MODE_T burstAcceesMode; /*!< Set burst access mode */
- SMC_ASYNCHRONOUS_WAIT_T asynchronousWait; /*!< Set asynchronous wait */
- SMC_WAIT_SIGNAL_POLARITY_T waitSignalPolarity; /*!< Set wait signal polarity */
- SMC_WRAP_MODE_T wrapMode; /*!< Set wrapped burst mode */
- SMC_WAIT_SIGNAL_ACTIVE_T waitSignalActive; /*!< Set wait timing */
- SMC_WRITE_OPERATION_T writeOperation; /*!< Set write operation */
- SMC_WAITE_SIGNAL_T waiteSignal; /*!< Set wait signal */
- SMC_EXTENDEN_MODE_T extendedMode; /*!< Set extended mode */
- SMC_WRITE_BURST_T writeBurst; /*!< Set write burst */
- SMC_NORSRAMTimingConfig_T* readWriteTimingStruct; /*!< Read and write timing */
- SMC_NORSRAMTimingConfig_T* writeTimingStruct; /*!< Write timing */
- } SMC_NORSRAMConfig_T;
该结构体主要是在配置 SMC 初始化时所需要的参数,最底层的代码其实就是配置 SMC 的片选控制、片选时序、写时序这 3 个寄存器,下面简单介绍下这些结构体成员:
(1) bank:选择 SRAM 类型的哪个存储区域,该参数需要根据片选引脚的连接不同进行选择,前面也有介绍不同的片选信号对应的是哪个存储区域。
(2) dataAddressMux :用于设置地址线与数据线是否进行复用。对于 NorFlash 存储器类型,地址线与数据线是可以分时复用的,这样可以减少 GPIO 引脚的使用,该参数仅对 NorFlash 有效。
(3) memoryType :配置存储器类型,可以配置的存储器类型有:SRAM、PSRAM、NorFlash。
(4) memoryDataWidth :配置控制存储器的数据位宽,可恶意配置为 8 位或者 16 位。
(5) burstAcceesMode :本成员用于配置是否使用突发访问模式。突发访问模式是指发送一个地址后连续访问多个数据,非突发模式下每访问一个数据都需要输入一个地址。该成员只对同步模式下才有效。
(6) asynchronousWait :用于配置是否使能同步等待信号,对于同步类型的 NorFlash 或者 PSRAM 存储器,可以使用 SMC_NWAIT 引脚插入等待状态。
(7) waitSignalPolarity :该成员用于配置等待信号极性,可设置要求等待的信号是高电平还是低电平。
(8) wrapMode :配置是否使能非对齐突发模式,仅在突发模式下才有效。
(9) waitSignalActive :用于配置等待信号是在等待之前有效还是等待期间有效,只在突发模式下有效。
(10) writeOperation :用于配置是否开启写使能,如果禁止写使能,那么 SMC 外设对存储器只能进行读操作,写操作此时是禁止的。
(11) waiteSignal :该成员用于设置是否使能 NWAIT 信号插入等待状态,只对突发模式下有效。
(12) extendedMode :该成员用于设置是否使能扩展模式,在扩展模式下,读写时序可以分别配置不同的时序寄存器,即读写时序可以不一样;而在费扩展模式下,读写时序的配置都是一样的。
(13) writeBurst :用于配置是否使能写突发操作,只在突发模式下有效。
(14) readWriteTimingStruct :读写时序结构体。在不使用扩展模式时,该成员可同时配置读写时序;在扩展模式下,只配置读时序。
(15) writeTimingStruct:写时序结构体,在扩展模式时,可以配置写时序;在非扩展模式下,配置无效。
3.3 APM32F4xx SMC 控制 SRAM 时序计算APM32F4xx SMC 要控制外部 SRAM 存储器,必须要正确配置时序结构体中的两个时序参数:地址建立时间和数据建立时间,其他的时序参数没有用到,设置为 0 即可。
下面以 SRAM 芯片型号 IS62WV12816 为例,介绍 APM32F4xx SMC 控制 SRAM 芯片的时序计算。根据 IS62WV12816 芯片手册,其关键的时序参数如下:
写时序参数要求:
读时序参数要求:
APM32F4xx SMC 控制外部 SRAM 的读写时序有多种不同的模式,下面展示的是模式 A 的读写时序,如下图所示:
SMC 写 SRAM 时序:
SMC 读 SRAM 时序:
根据 IS62WV12816 芯片的时间参数要求,以及 SMC 读写 SRAM 的时序图,可以得到 IS62WV12816 芯片的时间参数计算公式如下:
tWC = ADDSET + DATASET + 1 >= 55ns
tPWE = DATASET >= 40ns
tRC = ADDSET + DATASET >= 55ns
对时序结构体进行赋值的 ADDSET 和 DATASET 参数,它的单位都是 1 个 HCLK 周期,而 APM32F4xx 的 HCLK 时钟频率是 168MHz ,那么 1 个 HCLK 周期就是 1/168 us = 6ns .
根据前面的 IS62WV12816 关键的时间参数表格可知,地址建立时间是 0 ,所以时序结构体的 ADDSET 参数赋值为 0 即可;然后根据上面的三条表达式,为了同时满足读写时序的要求,可得出 DATASET 参数赋值为 10 ,这样就可以满足 SRAM 读写的时间要求了。
4. APM32F4xx SMC 读写外部SRAM例程
4.1 硬件设计外部 SRAM 芯片使用的是 IS62WV12816 型号,SRAM 芯片和 APM32F4xx 连接的原理图如下:
可以看到,SRAM 芯片有很多的信号线需要与 APM32F4xx 的相连,其中 SRAM 芯片的大部分信号线都是与 MCU 连接固定的引脚的,只有片选引脚可以让我们选择连接 SMC_NE1 - NE4,而片选引脚的连接不同,会映射到 MCU 不同的地址空间。
上面的原理图片选引脚连接的是 SMC_NE1 ,那么 SRAM 的存储空间会被映射到 MCU 内部的 0x60000000 ~ 0x63FFFFFF 这一片地址空间上。对于 IS62WV12816 型号,其内存大小是 256KB ,那么当 MCU 访问 0x60000000 开始的 256KB 的内存空间时,SMC 外设会根据初始化的配置,产生相应的访问时序,从而实现外部 SRAM 的读写操作。
4.2 软件设计要通过 APM32F4xx SMC 实现外部 SRAM 存储器的访问,只要配置使用到的 SMC 外设相关的 GPIO 引脚以及配置好 SMC 的时序结构体和初始化结构体即可。代码实现流程图如下:
4.2.1 SMC GPIO引脚配置对 SMC 外设所使用到的 GPIO 引脚,全部都配置为复用功能输出模式,具体代码如下:
- static void SMC_SRAM_GPIOConfig(void)
- {
- GPIO_Config_T gpioConfig;
- /**
- +--------------------+--------------------+
- | PD0 <-> SMC_D2 | PE0 <-> SMC_NBL1 |
- | PD1 <-> SMC_D3 | PE1 <-> SMC_NBL0 |
- | PD4 <-> SMC_NOE | PE7 <-> SMC_D4 |
- | PD5 <-> SMC_NWE | PE8 <-> SMC_D5 |
- | PD7 <-> SMC_NE1 | PE9 <-> SMC_D6 |
- | PD8 <-> SMC_D13 | PE10 <-> SMC_D7 |
- | PD9 <-> SMC_D14 | PE11 <-> SMC_D8 |
- | PD10 <-> SMC_D15 | PE12 <-> SMC_D9 |
- | PD11 <-> SMC_A16 | PE13 <-> SMC_D10 |
- | PD12 <-> SMC_A17 | PE14 <-> SMC_D11 |
- | PD13 <-> SMC_A18 | PE15 <-> SMC_D12 |
- | PD14 <-> SMC_D0 | |
- | PD15 <-> SMC_D1 | |
- +--------------------+--------------------+
- | PF0 <-> SMC_A0 | PG0 <-> SMC_A10 |
- | PF1 <-> SMC_A1 | PG1 <-> SMC_A11 |
- | PF2 <-> SMC_A2 | PG2 <-> SMC_A12 |
- | PF3 <-> SMC_A3 | PG3 <-> SMC_A13 |
- | PF4 <-> SMC_A4 | PG4 <-> SMC_A14 |
- | PF5 <-> SMC_A5 | PG5 <-> SMC_A15 |
- | PF12 <-> SMC_A6 | |
- | PF13 <-> SMC_A7 | |
- | PF14 <-> SMC_A8 | |
- | PF15 <-> SMC_A9 | |
- +--------------------+--------------------+
- */
-
- /* Enable GPIO Clock */
- RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOD | RCM_AHB1_PERIPH_GPIOE | \
- RCM_AHB1_PERIPH_GPIOF | RCM_AHB1_PERIPH_GPIOG);
- /* SMC SRAM GPIO Config */
- gpioConfig.speed = GPIO_SPEED_100MHz;
- gpioConfig.mode = GPIO_MODE_AF;
- gpioConfig.otype = GPIO_OTYPE_PP;
- gpioConfig.pupd = GPIO_PUPD_UP;
-
- gpioConfig.pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5 | 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_Config(GPIOD, &gpioConfig);
-
- GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_0, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_1, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_4, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_5, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_7, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_8, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_9, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_10, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_11, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_12, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_13, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_14, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOD, GPIO_PIN_SOURCE_15, GPIO_AF_FSMC);
-
- gpioConfig.pin = GPIO_PIN_0 | GPIO_PIN_1 | 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_Config(GPIOE, &gpioConfig);
-
- GPIO_ConfigPinAF(GPIOE, GPIO_PIN_SOURCE_0, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOE, GPIO_PIN_SOURCE_1, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOE, GPIO_PIN_SOURCE_7, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOE, GPIO_PIN_SOURCE_8, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOE, GPIO_PIN_SOURCE_9, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOE, GPIO_PIN_SOURCE_10, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOE, GPIO_PIN_SOURCE_11, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOE, GPIO_PIN_SOURCE_12, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOE, GPIO_PIN_SOURCE_13, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOE, GPIO_PIN_SOURCE_14, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOE, GPIO_PIN_SOURCE_15, GPIO_AF_FSMC);
-
- gpioConfig.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_Config(GPIOF, &gpioConfig);
-
- GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_0, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_1, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_2, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_3, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_4, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_5, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_12, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_13, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_14, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOF, GPIO_PIN_SOURCE_15, GPIO_AF_FSMC);
-
- gpioConfig.pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | \
- GPIO_PIN_5;
- GPIO_Config(GPIOG, &gpioConfig);
-
- GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_0, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_1, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_2, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_3, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_4, GPIO_AF_FSMC);
- GPIO_ConfigPinAF(GPIOG, GPIO_PIN_SOURCE_5, GPIO_AF_FSMC);
- }
4.2.2 SMC 初始化配置APM32F4xx 固件库已经对 SMC 外设对时序控制以及初始化配置相关的参数封装在时序结构体和初始化配置结构体了,SMC 外设的初始化配置,主要就是对这两个结构体成员变量进行赋值,最后调用固件库函数提供的初始化配置函数实现 SMC 外设的初始化配置,具体代码如下:
- void SMC_SRAM_Init(void)
- {
- SMC_NORSRAMConfig_T SMC_NORSRAM_ConfigStruct;
- SMC_NORSRAMTimingConfig_T readWriteTimingStruct;
-
- /* SMC SRAM GPIO Config */
- SMC_SRAM_GPIOConfig();
-
- /* Enable SMC Clock */
- RCM_EnableAHB3PeriphClock(RCM_AHB3_PERIPH_EMMC);
-
- /* SMC SRAM Timing Config */
- readWriteTimingStruct.addressSetupTime = 0x00;
- readWriteTimingStruct.addressHodeTime = 0x00;
- readWriteTimingStruct.dataSetupTime = 0x0A;
- readWriteTimingStruct.busTurnaroundTime = 0x00;
- readWriteTimingStruct.clockDivision = 0x00;
- readWriteTimingStruct.dataLatency = 0x00;
- readWriteTimingStruct.accessMode = SMC_ACCESS_MODE_A;
-
- /* SMC SRAM Init Config */
- SMC_NORSRAM_ConfigStruct.bank = SMC_BANK1_NORSRAM_1;
- SMC_NORSRAM_ConfigStruct.dataAddressMux = SMC_DATA_ADDRESS_MUX_DISABLE;
- SMC_NORSRAM_ConfigStruct.memoryType = SMC_MEMORY_TYPE_SRAM;
- SMC_NORSRAM_ConfigStruct.memoryDataWidth = SMC_MEMORY_DATA_WIDTH_16BIT;
- SMC_NORSRAM_ConfigStruct.burstAcceesMode = SMC_BURST_ACCESS_MODE_DISABLE;
- SMC_NORSRAM_ConfigStruct.asynchronousWait = SMC_ASYNCHRONOUS_WAIT_DISABLE;
- SMC_NORSRAM_ConfigStruct.waitSignalPolarity = SMC_WAIT_SIGNAL_POLARITY_LOW;
- SMC_NORSRAM_ConfigStruct.wrapMode = SMC_WRAP_MODE_DISABLE;
- SMC_NORSRAM_ConfigStruct.waitSignalActive = SMC_WAIT_SIGNAL_ACTIVE_BEFORE_WAIT_STATE;
- SMC_NORSRAM_ConfigStruct.writeOperation = SMC_WRITE_OPERATION_ENABLE;
- SMC_NORSRAM_ConfigStruct.waiteSignal = SMC_WAITE_SIGNAL_DISABLE;
- SMC_NORSRAM_ConfigStruct.extendedMode = SMC_EXTENDEN_MODE_DISABLE;
- SMC_NORSRAM_ConfigStruct.writeBurst = SMC_WRITE_BURST_DISABLE;
- SMC_NORSRAM_ConfigStruct.readWriteTimingStruct = &readWriteTimingStruct;
- SMC_NORSRAM_ConfigStruct.writeTimingStruct = &readWriteTimingStruct;
-
- SMC_ConfigNORSRAM(&SMC_NORSRAM_ConfigStruct);
- }
对于时序结构体 SMC_NORSRAMTimingConfig_T 相关的参数配置,需要根据 SRAM 的芯片手册提供的时间参数要求,计算出对应结构体成员的值,从而对各成员参数进行合理配置,本例程所使用的 SRAM 芯片型号是 IS62WV12816 。
4.2.3 SMC 访问外部 SRAM 存储器通过原理图可以确定,外部 SRAM 芯片的片选引脚连接到的是 SMC_NE1 引脚,那么外部 SRAM 的存储空间会映射到 MCU 内部的 0x60000000 ~ 0x63FFFFFF 范围内的地址空间。对于本例程所使用的 IS62WV12816 型号,其存储空间大小是 256KB ,所以我们访问外部 SRAM 的起始地址是 0x60000000 ,结束地址是 0x60040000 。
只要初始化了 SMC 外设之后,对外部 SRAM 的读写访问就是对内存的读写操作。而对于 C 语言的话,通过指针就可以实现对特定内存地址进行读写操作,例如:
对 SRAM 的 0x6000000 地址,读取 1 个字节数据:
- uint8_t data = *(uint8_t *)0x6000000;
对 SRAM 的 0x6000000 地址,写入 1 个字节数据:
- *(uint8_t *)0x6000000 = (uint8_t)0x55;
如果我们想要定义变量在指定的内存地址上,可以使用编译器提供的扩展关键字来修饰变量,比如对于 ARM-CC 编译器变量定义如下:
- uint8_t data[256 * 1024] __attribute__((at(0x60000000)));
下面是对外部 SRAM 读写操作的函数封装:
- void SMC_SRAM_WriteBufferWord(uint32_t address, const uint32_t *pdata, uint32_t len)
- {
- uint32_t i = 0;
-
- for (i = 0; i < len / 4; i++)
- {
- *(__IO uint32_t *)(SRAM_START_ADDR + address) = pdata[i];
- address += 4;
- }
- }
- void SMC_SRAM_ReadBufferWord(uint32_t address, uint32_t *pdata, uint32_t len)
- {
- uint32_t i = 0;
-
- for (i = 0; i < len / 4; i++)
- {
- pdata[i] = *(__IO uint32_t *)(SRAM_START_ADDR + address);
- address += 4;
- }
- }
5. 基于外部SRAM存储器的应用案例后续计划补充基于SRAM存储器的应用案例,待定。
上述关于APM32F4 SMC SRAM的代码附件也上传了,以供大家参考。