Nuttx外扩SDRAM(IS42)

前言

Nuttx当中有很多系统的组件都会用到内存中堆(heap)的资源,在终端上通过键入“free”指令可以查看到当前内存中活动的用户堆的实时用量,当应用在需要大量的堆开销的情况下,本身芯片内部集成的小容量内存就显得捉襟见肘了(我使用过MCU目前最大的内存容量也就是128KB+16KB+368KB =512KB);估计有疑问说为什么是3块,再移植前,我们先来捋一捋一些基础知识。

板载内存

日常中接触到的cortex-M4及其一下系列内核的单片机,一般在内存上大都是一块内存,我接触到最多的ST的单片机一般规格多为“32KB、64KB、256KB、320KB”等等,这里的内存就是我们所说的sram。例如下图:
Nuttx外扩SDRAM(IS42)
但是在F7系列上确实这样内存变成了512KB+16KB:
Nuttx外扩SDRAM(IS42)
这个是因为在F7系列上sram还分为了ITCM和DTCM(TCM=Tightly Coupled Memory,是一种高速缓存,据说是被直接集成在CPU芯片中。DS有两种TCM,分别是ITCM(Instruction TCM)和DTCM(Data TCM)。由于是高速缓存,所以这两块内存区域被当做特殊的用途。比如某些对时间要求非常严格的代码,就可以被放到ITCM中执行。这可以有效地提高运行速度。某些需要频繁存取的数据,也可以放到DTCM中以节省存取时间。)
Nuttx外扩SDRAM(IS42)

外扩内存

既然板载的内存不够用,外扩的内存怎么加呢?这里就需要用到Flexible memory controller (FMC),来实现外扩内存。首先需要确定外扩的SDRAM的规格和接口类型;

- 内部规格

SDRAM内部包含的存储阵列,可以把它理解成一张表格,数据就填在这张表格上。和表格查找一样,指定一个行地址和列地址,就可以精确地找到目标单元格,这是SDRAM芯片寻址的基本原理。这样的每个单元格被称为存储单元,而这样的表则被称为存储阵列(Bank),目前设计的SDRAM芯片基本上内部都包含有4个这样的Bank,寻址时指定Bank号以及行地址,然后再指定列地址即可寻找到目标存储单元。所及行地址、列地址以及bank数基本就决定了我们SDRAM的大小了。
Nuttx外扩SDRAM(IS42)

- 硬件接口
大概了解了SDRAM的内部结构,接下来看看常见的SDRAM的硬件接口:

  1. CLK,同步时钟信号,所有输入信号都在CLK为上升沿的时候被采集;
  2. CKE,时钟使能信号,禁止时钟信号时SDRAM会启动自刷新操作;
  3. CS,片选信号,低电平有效;
  4. CAS#,列地址选通,为低电平时地址线表示的是列地址;
  5. RAS#,行地址选通,为低电平时地址线表示的是行地址;
  6. WE#,写入使能,低电平有效;
  7. DQM[0:1],数据输入/输出掩码信号,表示DQ信号线的有效部分;
  8. BA[0:1],Bank地址输入,选择要控制的Bank;
  9. A[0:11],地址输入;
  10. DQ[0:15],数据输入输出信号 ;(同种IC有16bits、32bits位宽等多种规格)
    具体的时序可以参考对应使用的芯片使用手册;

Nuttx下的外扩SDRAM接口

与一般的驱动不一样的是,Nuttx下外扩SDRAM直接就挂在地址总线上了,以本次移植为例,采用STM32F746系列的单片机;

- 外设驱动FMC
在配置的板子的src文件下比如我的板子是stm32f746g-disco,找到stm32_extmem.c文件<…/nuttx/configs/stm32f746g-disco/src>

/************************************************************************************
 * configs/stm32f746g-disco/src/stm32_extmem.c
 *
 *   Copyright (C) 2018 Marcin Wyrwas. All rights reserved.
 *   Author: Marcin Wyrwas <mvp1@wp.pl>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ************************************************************************************/

/************************************************************************************
 * Included Files
 ************************************************************************************/

#include <nuttx/config.h>

#include <stdint.h>
#include <assert.h>
#include <debug.h>

#include "chip.h"
#include "up_arch.h"

#include "stm32_fmc.h"
#include "stm32_gpio.h"
#include "stm32_rcc.h"
#include "stm32f746g-disco.h"

#include <arch/board/board.h>

/************************************************************************************
 * Pre-processor Definitions
 ************************************************************************************/

#ifndef CONFIG_STM32F7_FMC
#  warning "FMC is not enabled"
#endif

#if STM32F7_NGPIO < 7
#  error "Required GPIO ports not enabled"
#endif

#define STM32_FMC_NADDRCONFIGS 22
#define STM32_FMC_NDATACONFIGS 16

#define STM32_SDRAM_CLKEN     FMC_SDRAM_MODE_CMD_CLK_ENABLE | FMC_SDRAM_CMD_BANK_1
#define STM32_SDRAM_PALL      FMC_SDRAM_MODE_CMD_PALL | FMC_SDRAM_CMD_BANK_1
#define STM32_SDRAM_REFRESH   FMC_SDRAM_MODE_CMD_AUTO_REFRESH | FMC_SDRAM_CMD_BANK_1 |\
                                (7 << FMC_SDRAM_AUTO_REFRESH_SHIFT)
#define STM32_SDRAM_MODEREG   FMC_SDRAM_MODE_CMD_LOAD_MODE | FMC_SDRAM_CMD_BANK_1 |\
                                FMC_SDRAM_MODEREG_BURST_LENGTH_1 | \
                                FMC_SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |\
                                FMC_SDRAM_MODEREG_CAS_LATENCY_3 |\
                                FMC_SDRAM_MODEREG_WRITEBURST_MODE_SINGLE


/************************************************************************************
 * Public Data
 ************************************************************************************/

/* GPIO configurations common to most external memories */
**//注意1:这里需要根据硬件IO连接修改对应的IO口**
static const uint32_t g_addressconfig[STM32_FMC_NADDRCONFIGS] =
{
  GPIO_FMC_A0,  GPIO_FMC_A1,  GPIO_FMC_A2,  GPIO_FMC_A3,  GPIO_FMC_A4 , GPIO_FMC_A5,
  GPIO_FMC_A6,  GPIO_FMC_A7,  GPIO_FMC_A8,  GPIO_FMC_A9,  GPIO_FMC_A10, GPIO_FMC_A11,

  GPIO_FMC_SDCKE0_1, GPIO_FMC_SDNE0_3, GPIO_FMC_SDNWE_3, GPIO_FMC_NBL0,
  GPIO_FMC_SDNRAS, GPIO_FMC_NBL1,  GPIO_FMC_BA0,   GPIO_FMC_BA1,
  GPIO_FMC_SDCLK,  GPIO_FMC_SDNCAS
};

static const uint32_t g_dataconfig[STM32_FMC_NDATACONFIGS] =
{
  GPIO_FMC_D0,  GPIO_FMC_D1 , GPIO_FMC_D2,  GPIO_FMC_D3,  GPIO_FMC_D4 , GPIO_FMC_D5,
  GPIO_FMC_D6,  GPIO_FMC_D7,  GPIO_FMC_D8,  GPIO_FMC_D9,  GPIO_FMC_D10, GPIO_FMC_D11,
  GPIO_FMC_D12, GPIO_FMC_D13, GPIO_FMC_D14, GPIO_FMC_D15
};

/************************************************************************************
 * Private Data
 ************************************************************************************/

/************************************************************************************
 * Private Functions
 ************************************************************************************/

/************************************************************************************
 * Public Functions
 ************************************************************************************/

/************************************************************************************
 * Name: stm32_extmemgpios
 *
 * Description:
 *   Initialize GPIOs for external memory usage
 *
 ************************************************************************************/

static void stm32_extmemgpios(const uint32_t *gpios, int ngpios)
{
  int i;

  /* Configure GPIOs */

  for (i = 0; i < ngpios; i++)
    {
      stm32_configgpio(gpios[i]);
    }
}

/************************************************************************************
 * Name: stm32_sdramcommand
 *
 * Description:
 *   Initialize data line GPIOs for external memory access
 *
 ************************************************************************************/

static void stm32_sdramcommand(uint32_t command)
{
  uint32_t  regval;
  volatile  uint32_t timeout = 0xFFFF;

  regval = getreg32( STM32_FMC_SDSR ) & 0x00000020;
  while ((regval != 0) && timeout-- > 0)
    {
      regval = getreg32( STM32_FMC_SDSR ) & 0x00000020;
    }
  putreg32(command, STM32_FMC_SDCMR);
  timeout = 0xFFFF;
  regval = getreg32( STM32_FMC_SDSR ) & 0x00000020;
  while ((regval != 0) && timeout-- > 0)
    {
      regval = getreg32( STM32_FMC_SDSR ) & 0x00000020;
    }
}

/************************************************************************************
 * Name: stm32_enablefmc
 *
 * Description:
 *  enable clocking to the FMC module
 *
 ************************************************************************************/

void stm32_enablefmc(void)
{
  uint32_t regval;
  volatile int count;

  /* Enable GPIOs as FMC / memory pins */

  stm32_extmemgpios(g_addressconfig, STM32_FMC_NADDRCONFIGS);
  stm32_extmemgpios(g_dataconfig, STM32_FMC_NDATACONFIGS);

  /* Enable AHB clocking to the FMC */

  regval  = getreg32( STM32_RCC_AHB3ENR);
  regval |= RCC_AHB3ENR_FMCEN;
  putreg32(regval, STM32_RCC_AHB3ENR);

  /* Configure and enable the SDRAM bank1
   *
   *   FMC clock = 216MHz/2 = 108MHz
   *   108MHz = 9,26 ns
   *   All timings from the datasheet for Speedgrade -6A (=6ns)
   */

  putreg32(FMC_SDRAM_CR_RPIPE_0 |
           FMC_SDRAM_CR_BURST_READ |
           FMC_SDRAM_CR_SDCLK_2X |
           FMC_SDRAM_CR_CASLAT_3 |
           FMC_SDRAM_CR_BANKS_4 |			//根据所使用的IC驱动BANK数量一般为2或4
           FMC_SDRAM_CR_WIDTH_16 |			//设置数据位宽,硬件连了16根数据线就16,连了32根就是32
           FMC_SDRAM_CR_ROWBITS_12 | 		//行地址位宽(参看IC手册确定)
           FMC_SDRAM_CR_COLBITS_8,			//列地址位宽(参看IC手册确定)
      STM32_FMC_SDCR1);

  putreg32((1 << FMC_SDRAM_TR_TRCD_SHIFT) |  /* tRCD min = 18ns */
           (1 << FMC_SDRAM_TR_TRP_SHIFT) |   /* tRP  min = 18ns */
           (1 << FMC_SDRAM_TR_TWR_SHIFT) |   /* tWR      = 2CLK */
           (6 << FMC_SDRAM_TR_TRC_SHIFT) |   /* tRC  min = 64ns */
           (4 << FMC_SDRAM_TR_TRAS_SHIFT) |  /* tRAS min = 46ns */
           (7 << FMC_SDRAM_TR_TXSR_SHIFT) |  /* tXSR min = 74ns */
           (1 << FMC_SDRAM_TR_TMRD_SHIFT),   /* tMRD     = 2CLK */
      STM32_FMC_SDTR1);

  /* SDRAM Initialization sequence */

  stm32_sdramcommand(STM32_SDRAM_CLKEN);      /* Clock enable command */
  for (count = 0; count < 10000; count++) ;    /* Delay */
  stm32_sdramcommand(STM32_SDRAM_PALL);       /* Precharge ALL command */
  stm32_sdramcommand(STM32_SDRAM_REFRESH);    /* Auto refresh command */
  stm32_sdramcommand(STM32_SDRAM_MODEREG);    /* Mode Register program */

  /* Set refresh count
   *
   * FMC_CLK = 108MHz
   * Refresh_Rate = 64ms / 4096 rows = 15.63us
   * Counter = (FMC_CLK * Refresh_Rate) - 20
   */

  putreg32(1668 << 1, STM32_FMC_SDRTR);
}

/************************************************************************************
 * Name: stm32_disablefmc
 *
 * Description:
 *  enable clocking to the FMC module
 *
 ************************************************************************************/

void stm32_disablefmc(void)
{
  uint32_t regval;

  /* Disable AHB clocking to the FMC */

  regval  = getreg32(STM32_RCC_AHB3ENR);
  regval &= ~(uint32_t)RCC_AHB3ENR_FMCEN;
  putreg32(regval, STM32_RCC_AHB3ENR);
}

改好驱动了以后,我们需要把文件添加到makefile中,然后呢找到stm32_boot.c文件<…/nuttx/configs/stm32f746g-disco/src>
在如下位置调用初始化接口:

void stm32_boardinitialize(void)
{
#if defined(CONFIG_STM32F7_SPI1) || defined(CONFIG_STM32F7_SPI2) || \
    defined(CONFIG_STM32F7_SPI3) || defined(CONFIG_STM32F7_SPI4) || \
    defined(CONFIG_STM32F7_SPI5)
  /* Configure SPI chip selects if 1) SPI is not disabled, and 2) the weak function
   * stm32_spidev_initialize() has been brought into the link.
   */

  if (stm32_spidev_initialize)
    {
      stm32_spidev_initialize();
    }
#endif

#ifdef CONFIG_SPORADIC_INSTRUMENTATION
  /* This configuration has been used for evaluating the NuttX sporadic scheduler.
   * The following caqll initializes the sporadic scheduler monitor.
   */
  arch_sporadic_initialize();
#endif

#ifdef CONFIG_ARCH_LEDS
  /* Configure on-board LEDs if LED support has been selected. */
  board_autoled_initialize();
#endif

**//在这里添加我们的硬件驱动初始化代码,注意记得在stm32f746g-disco.h头文件里面去声明函数,不然编译会有警告。**
#ifdef CONFIG_STM32F7_FMC
  /* Initialize sdram via fmc.*/
  stm32_enablefmc();
#endif
}
  • .config配置

外设驱动好了以后,接下来就是配置编译选项:

  1. 通过make menuconfig指令打开配置页面

System Type > STM32 Peripheral Support
[] FMC
Memory Management
[
] Second user heap
(0xC0000000) Start address of second user heap region // 这个地址要看单片机划分的区域来定,哪段能用
(16777216) Size of the second user heap region //实际是多大内存就填写多大内存
保存

  1. 编写flash.ld
    文件的路径<…/nuttx/configs/stm32f746g-disco/scripts>,添加最后一行代码,确保地址和大小一致;
MEMORY
{
	itcm  (rwx) : ORIGIN = 0x00200000, LENGTH = 2048K
	flash (rx)  : ORIGIN = 0x08000000, LENGTH = 2048K
	dtcm  (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
	sram1 (rwx) : ORIGIN = 0x20020000, LENGTH = 368K
	sram2 (rwx) : ORIGIN = 0x2007c000, LENGTH = 16K
	sdram (rwx) : ORIGIN = 0xC0000000, LENGTH = 16384K
}
  1. 保存编译
    一切就绪,编译内核代码并烧录到目标板中,然后在终端中键入free:
    未加SDRAM:
nsh> free                                                                                       
             total       used       free    largest
Umem:       358592      51216     307376     208944

加入SDRAM后:

nsh> free
             total       used       free    largest
Umem:     17004736      75248   16929488   16777200

上一篇:JS实现鼠标移入DIV随机变换颜色


下一篇:20201103~Config文件读取