为ALIENTEK 阿波罗STM32F429开发板移植openvela

本指南(参考为STM32F407 开发板移植 openvela)详细介绍了如何为 ALIENTEK 阿波罗STM32F429开发板(后文简称“开发板”)移植openvela 操作系统。核心板的主控芯片为 STM32F429IGT6。由于 NuttX 官方已提供对 STM32F429I 系列芯片的支持,本指南将重点阐述板级支持包(Board Support Package, BSP)的适配过程。

准备工作

在开始之前,请确保您已完成以下准备工作:

  1. 获取源码:参考文档快速入门下载最新代码。

  2. 了解 openvela 架构:建议您预先阅读 openvela 架构以理解其分层设计。

  3. 查阅系统启动流程文档,获取更详细的启动时序和函数调用关系图。

  4. 准备好硬件及配套软件

一、移植原理与启动流程

openvela 的移植本质上是为操作系统框架提供一套与特定硬件交互的接口。这些接口构成了板级支持包(BSP),它位于 nuttx/boards/ 目录下。本指南将通过实现一个最小功能的 BSP,让openvela在 开发板上启动并运行。

关键启动函数

openvela 在启动过程中,会按照特定顺序调用一系列由 BSP 提供的函数来完成硬件初始化。要成功启动系统,您必须在 BSP 中实现以下关键函数:

函数 描述
void stm32_boardinitialize(void) 由系统启动代码 __start 调用。这是最早执行的板级初始化函数,用于配置最基础的硬件,如时钟和调试串口。
void board_late_initialize(void) 如果配置了 CONFIG_BOARD_LATE_INITIALIZE,此函数会在 OS 初始化后期被调用,通常用于初始化那些依赖 OS 服务的驱动。
int stm32_bringup(void) 如果配置 CONFIG_BOARD_LATE_INITIALIZE 选项,由 board_late_initialize() 调用; 如果没有配置 CONFIG_BOARD_LATE_INITIALIZE 选项,由 board_app_initialize(arg) 调用。 在 stm32_bringup 里将实现驱动的初始化。
int board_app_initialize(uintptr_t arg) 由 boardctl() 接口通过 BOARDIOC_INIT 命令触发,用于执行应用层或用户自定义的初始化。

此外,如果您的系统配置CONFIG_ARCH_LEDS用于显示系统状态(如启动、Panic),则还需要实现以下函数:

函数 描述
void board_autoled_initialize(void) 初始化用于系统状态指示的 LED 引脚,通常设置为 GPIO 输出模式。
void board_autoled_on(int led) 根据系统状态(如 LED_STARTED)点亮指定的 LED。
void board_autoled_off(int led) 根据系统状态(如 LED_PANIC 结束)熄灭指定的 LED。

硬件配置:时钟与引脚

除了实现函数接口,您还需要在板级头文件board.h中定义宏,以配置 STM32 的时钟树、外设引脚功能等。这些宏将被芯片级的驱动代码(如 stm32_rcc.c)使用。
以下是开发板时钟树的配置示例,它描述了如何将 25MHz 的外部高速晶振(HSE)通过 PLL 倍频至 168MHz 作为系统主时钟(SYSCLK)。

/* Clocking *****************************************************************/

/* The APOLLO Discovery board features a single 25MHz crystal.

 *  Space is provided for a 32kHz RTC backup crystal, but it is not stuffed.
    *
 *  This is the canonical configuration:
 *  System Clock source           : PLL (HSE)
 *  SYSCLK(Hz)                    : 168000000    Determined by PLL
 *  configuration
 *  HCLK(Hz)                      : 168000000    (STM32_RCC_CFGR_HPRE)
 *  AHB Prescaler                 : 1            (STM32_RCC_CFGR_HPRE)
 *  APB1 Prescaler                : 4            (STM32_RCC_CFGR_PPRE1)
 *  APB2 Prescaler                : 2            (STM32_RCC_CFGR_PPRE2)
 *  HSE Frequency(Hz)             : 25000000     (STM32_BOARD_XTAL)
 *  PLLM                          : 25           (STM32_PLLCFG_PLLM)
 *  PLLN                          : 336          (STM32_PLLCFG_PLLN)
 *  PLLP                          : 2            (STM32_PLLCFG_PLLP)
 *  PLLQ                          : 7            (STM32_PLLCFG_PLLQ)
 *  Main regulator output voltage : Scale1 mode  Needed for high speed
 *  SYSCLK
 *  Flash Latency(WS)             : 5
 *  Prefetch Buffer               : OFF
 *  Instruction cache             : ON
 *  Data cache                    : ON
 *  Require 48MHz for USB OTG FS, : Enabled
 *  SDIO and RNG clock
 */

二、代码实现步骤

本节将指导您完成本项目的文件创建和代码编写。

1.创建代码目录结构

首先,在nuttx/boards/arm/stm32/目录下,创建一个名为 apollo-disco的新目录,并建立如下的子目录和文件结构。
代码路径: nuttx/boards/arm/stm32/apollo-disco/

apollo-disco
├── CMakeLists.txt
├── configs                           # defconfig 配置路径
│   └── nsh
│       └── defconfig                 # NuttShell (NSH) 的默认配置
├── include
│   └── board.h                       # 板级硬件配置头文件
├── Kconfig                           # 板级 Kconfig 配置文件
├── scripts                           # 链接脚本
│   ├── ld.script                     # 链接器脚本
│   └── Make.defs                     # 板级 Make 定义
└── src
    ├── CMakeLists.txt
    ├── Make.defs
    ├── stm32_appinit.c               # 实现 board_app_initialize
    ├── stm32_autoleds.c              # 实现系统状态 LED 控制
    ├── stm32_boot.c                  # 实现 stm32_boardinitialize
    ├── stm32_bringup.c               # 实现驱动初始化
    ├── apollo-disco.h                # 板级私有头文件,定义 GPIO
    └── stm32_userleds.c              # 实现用户层 LED 驱动接口
    └── stm32_extmem.c                # 实现 stm32_sdram_initialize
    

2.实现核心初始化函数

src/ 目录下,您需要创建并填充以下 C 文件。

stm32_boot.c:早期硬件初始化

此文件负责实现 stm32_boardinitialize(),用于在系统启动的最初阶段配置必要的硬件。

#include <nuttx/config.h>
#include "apollo-disco.h" // 板级私有定义

void stm32_boardinitialize(void)
{
#ifdef CONFIG_ARCH_LEDS
  /* 如果启用了系统状态LED,则初始化它们 */  
  board_autoled_initialize();
#endif
    
#ifdef CONFIG_STM32_FMC
  stm32_sdram_initialize();
#endif
}


#ifdef CONFIG_BOARD_LATE_INITIALIZE
void board_late_initialize(void)
{
  /* 执行板级后期初始化 */
  stm32_bringup();
}
#endif
stm32_bringup.c:设备驱动初始化

此文件中的 stm32_bringup() 函数负责初始化并注册所有板载设备的驱动程序。在本例中,我们啥都不用干。

#include "stm32.h"

#ifdef CONFIG_USERLED
#  include <nuttx/leds/userled.h>
#endif

#include "apollo-disco.h"

int stm32_bringup(void)
{
  int ret = OK;
  /* 在此添加其他驱动的初始化,例如 I2C, SPI, SDIO 等 */
    
  UNUSED(ret);
  return OK;
}
stm32_appinit.c:应用初始化桥梁

主要实现 int board_app_initialize(uintptr_t arg) 函数:

  • 如果打开 CONFIG_BOARD_LATE_INITIALIZE stm32_bringup() 将由board_late_initialize()调用。
  • 如果没有打开 CONFIG_BOARD_LATE_INITIALIZEstm32_bringup() 将由 board_app_initialize(arg) 调用。
int board_app_initialize(uintptr_t arg)
{
#ifdef CONFIG_BOARD_LATE_INITIALIZE
  /* 如果定义了后期初始化,bringup 已被调用,此处无需操作 */
  return OK;
#else
  /* 否则,在此处调用 bringup 来初始化驱动 */
  return stm32_bringup();
#endif
}

3.实现 LED 驱动

openvela将 LED 分为两类:一类用于指示系统状态(autoleds),另一类供用户应用程序控制(userleds)。

stm32_autoleds.c:系统状态 LED

该文件实现 board_autoled_* 系列函数,来控制实现 LED 的亮灭,由系统内核在特定事件(如启动、断言失败)发生时自动调用。此部分实现参照nuttx/boards/arm/stm32/stm32f429i-disco/src/stm32_autoleds.c

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

#include <nuttx/config.h>

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

#include <nuttx/board.h>
#include <arch/board/board.h>

#include "chip.h"
#include "arm_internal.h"
#include "stm32.h"
#include "apollo-disco.h"

#ifdef CONFIG_ARCH_LEDS

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

/* The following definitions map the encoded LED setting to GPIO settings */

#define STM32F4_LED1      (1 << 0)
#define STM32F4_LED2      (1 << 1)

#define ON_SETBITS_SHIFT  (0)
#define ON_CLRBITS_SHIFT  (4)
#define OFF_SETBITS_SHIFT (8)
#define OFF_CLRBITS_SHIFT (12)

#define ON_BITS(v)        ((v) & 0xff)
#define OFF_BITS(v)       (((v) >> 8) & 0x0ff)
#define SETBITS(b)        ((b) & 0x0f)
#define CLRBITS(b)        (((b) >> 4) & 0x0f)

#define ON_SETBITS(v)     (SETBITS(ON_BITS(v))
#define ON_CLRBITS(v)     (CLRBITS(ON_BITS(v))
#define OFF_SETBITS(v)    (SETBITS(OFF_BITS(v))
#define OFF_CLRBITS(v)    (CLRBITS(OFF_BITS(v))

#define LED_STARTED_ON_SETBITS       ((STM32F4_LED1) << ON_SETBITS_SHIFT)
#define LED_STARTED_ON_CLRBITS       ((STM32F4_LED2) << ON_CLRBITS_SHIFT)
#define LED_STARTED_OFF_SETBITS      (0 << OFF_SETBITS_SHIFT)
#define LED_STARTED_OFF_CLRBITS      ((STM32F4_LED1|STM32F4_LED2) << OFF_CLRBITS_SHIFT)

#define LED_HEAPALLOCATE_ON_SETBITS  ((STM32F4_LED2) << ON_SETBITS_SHIFT)
#define LED_HEAPALLOCATE_ON_CLRBITS  ((STM32F4_LED1) << ON_CLRBITS_SHIFT)
#define LED_HEAPALLOCATE_OFF_SETBITS ((STM32F4_LED1) << OFF_SETBITS_SHIFT)
#define LED_HEAPALLOCATE_OFF_CLRBITS ((STM32F4_LED2) << OFF_CLRBITS_SHIFT)

#define LED_IRQSENABLED_ON_SETBITS   ((STM32F4_LED1|STM32F4_LED2) << ON_SETBITS_SHIFT)
#define LED_IRQSENABLED_ON_CLRBITS   ((STM32F4_LED2) << ON_CLRBITS_SHIFT)
#define LED_IRQSENABLED_OFF_SETBITS  ((STM32F4_LED2) << OFF_SETBITS_SHIFT)
#define LED_IRQSENABLED_OFF_CLRBITS  ((STM32F4_LED1) << OFF_CLRBITS_SHIFT)

#define LED_STACKCREATED_ON_SETBITS  ((STM32F4_LED1) << ON_SETBITS_SHIFT)
#define LED_STACKCREATED_ON_CLRBITS  ((STM32F4_LED1|STM32F4_LED2) << ON_CLRBITS_SHIFT)
#define LED_STACKCREATED_OFF_SETBITS ((STM32F4_LED1|STM32F4_LED2) << OFF_SETBITS_SHIFT)
#define LED_STACKCREATED_OFF_CLRBITS ((STM32F4_LED1|STM32F4_LED2) << OFF_CLRBITS_SHIFT)

#define LED_INIRQ_ON_SETBITS         ((STM32F4_LED2) << ON_SETBITS_SHIFT)
#define LED_INIRQ_ON_CLRBITS         ((0) << ON_CLRBITS_SHIFT)
#define LED_INIRQ_OFF_SETBITS        ((0) << OFF_SETBITS_SHIFT)
#define LED_INIRQ_OFF_CLRBITS        ((STM32F4_LED2) << OFF_CLRBITS_SHIFT)

#define LED_SIGNAL_ON_SETBITS        ((STM32F4_LED2) << ON_SETBITS_SHIFT)
#define LED_SIGNAL_ON_CLRBITS        ((0) << ON_CLRBITS_SHIFT)
#define LED_SIGNAL_OFF_SETBITS       ((0) << OFF_SETBITS_SHIFT)
#define LED_SIGNAL_OFF_CLRBITS       ((STM32F4_LED2) << OFF_CLRBITS_SHIFT)

#define LED_ASSERTION_ON_SETBITS     ((STM32F4_LED2) << ON_SETBITS_SHIFT)
#define LED_ASSERTION_ON_CLRBITS     ((0) << ON_CLRBITS_SHIFT)
#define LED_ASSERTION_OFF_SETBITS    ((0) << OFF_SETBITS_SHIFT)
#define LED_ASSERTION_OFF_CLRBITS    ((STM32F4_LED2) << OFF_CLRBITS_SHIFT)

#define LED_PANIC_ON_SETBITS         ((STM32F4_LED2) << ON_SETBITS_SHIFT)
#define LED_PANIC_ON_CLRBITS         ((0) << ON_CLRBITS_SHIFT)
#define LED_PANIC_OFF_SETBITS        ((0) << OFF_SETBITS_SHIFT)
#define LED_PANIC_OFF_CLRBITS        ((STM32F4_LED2) << OFF_CLRBITS_SHIFT)

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

static const uint16_t g_ledbits[8] =
{
  (LED_STARTED_ON_SETBITS       | LED_STARTED_ON_CLRBITS |
  LED_STARTED_OFF_SETBITS      | LED_STARTED_OFF_CLRBITS),

  (LED_HEAPALLOCATE_ON_SETBITS  | LED_HEAPALLOCATE_ON_CLRBITS |
  LED_HEAPALLOCATE_OFF_SETBITS | LED_HEAPALLOCATE_OFF_CLRBITS),

  (LED_IRQSENABLED_ON_SETBITS   | LED_IRQSENABLED_ON_CLRBITS |
  LED_IRQSENABLED_OFF_SETBITS  | LED_IRQSENABLED_OFF_CLRBITS),

  (LED_STACKCREATED_ON_SETBITS  | LED_STACKCREATED_ON_CLRBITS |
  LED_STACKCREATED_OFF_SETBITS | LED_STACKCREATED_OFF_CLRBITS),

  (LED_INIRQ_ON_SETBITS         | LED_INIRQ_ON_CLRBITS |
  LED_INIRQ_OFF_SETBITS        | LED_INIRQ_OFF_CLRBITS),

  (LED_SIGNAL_ON_SETBITS        | LED_SIGNAL_ON_CLRBITS |
  LED_SIGNAL_OFF_SETBITS       | LED_SIGNAL_OFF_CLRBITS),

  (LED_ASSERTION_ON_SETBITS     | LED_ASSERTION_ON_CLRBITS |
  LED_ASSERTION_OFF_SETBITS    | LED_ASSERTION_OFF_CLRBITS),

  (LED_PANIC_ON_SETBITS         | LED_PANIC_ON_CLRBITS |
  LED_PANIC_OFF_SETBITS        | LED_PANIC_OFF_CLRBITS)
};

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

static inline void led_clrbits(unsigned int clrbits)
{
  if ((clrbits & STM32F4_LED1) != 0)
    {
      stm32_gpiowrite(GPIO_LED_RED, false);
    }

  if ((clrbits & STM32F4_LED2) != 0)
    {
      stm32_gpiowrite(GPIO_LED_GREEN, false);
    }
}

static inline void led_setbits(unsigned int setbits)
{
  if ((setbits & STM32F4_LED1) != 0)
    {
      stm32_gpiowrite(GPIO_LED_RED, true);
    }

  if ((setbits & STM32F4_LED2) != 0)
    {
      stm32_gpiowrite(GPIO_LED_GREEN, true);
    }
}

static void led_setonoff(unsigned int bits)
{
  led_clrbits(CLRBITS(bits));
  led_setbits(SETBITS(bits));
}

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

/****************************************************************************
 * Name: board_autoled_initialize
 ****************************************************************************/

void board_autoled_initialize(void)
{
  /* Configure LED1-4 GPIOs for output */

  stm32_configgpio(GPIO_LED_GREEN);
  stm32_configgpio(GPIO_LED_RED);
}

/****************************************************************************
 * Name: board_autoled_on
 ****************************************************************************/

void board_autoled_on(int led)
{
  ledinfo("board_autoled_on(%d)\n", led);

  led_setonoff(ON_BITS(g_ledbits[led]));

}

/****************************************************************************
 * Name: board_autoled_off
 ****************************************************************************/

void board_autoled_off(int led)
{

  led_setonoff(OFF_BITS(g_ledbits[led]));
}

#endif /* CONFIG_ARCH_LEDS */

stm32_userleds.c:用户应用 LED

该文件为通用的 LED 驱动(位于 drivers/leds/userled_lower.c)提供底层的硬件操作接口。应用程序通过标准的 open(), write(), ioctl() 等VFS接口访问 /dev/userleds 来控制这些 LED。

我们需要提供下面的函数接口给 userled_lower.c 调用:

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

#include <nuttx/config.h>

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

#include <arch/board/board.h>
#include <nuttx/power/pm.h>

#include "chip.h"
#include "arm_internal.h"
#include "stm32.h"
#include "apollo-disco.h"

#ifndef CONFIG_ARCH_LEDS

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

/* This array maps an LED number to GPIO pin configuration */

static uint32_t g_ledcfg[BOARD_NLEDS] =
{
  GPIO_LED_GREEN, GPIO_LED_RED
};

/****************************************************************************
 * Private Function Protototypes
 ****************************************************************************/


/****************************************************************************
 * Name: board_userled_initialize
 ****************************************************************************/


uint32_t board_userled_initialize(void)
{
  int i;

  /* 定义板载用户 LED 对应的 GPIO 配置 */
  for (i = 0; i < BOARD_NLEDS; i++)
    {
      stm32_configgpio(g_ledcfg[i]);
    }
  return BOARD_NLEDS;

}   
void board_userled(int led, bool ledon)
{
  /* 初始化用户 LED 的 GPIO */
  if ((unsigned)led < BOARD_NLEDS)
    {
      stm32_gpiowrite(g_ledcfg[led], ledon);
    }
}
  
void board_userled_all(uint32_t ledset)
{
  int i;

  /* Configure LED GPIOs for output */

  for (i = 0; i < BOARD_NLEDS; i++)
    {
      stm32_gpiowrite(g_ledcfg[i], (ledset & (1 << i)) != 0);
    }
}
#endif /* !CONFIG_ARCH_LEDS */

最终,在 userled_upper.c 实现下面 LED 驱动接口:

static int     userled_open(FAR struct file *filep);
static int     userled_close(FAR struct file *filep);
static ssize_t userled_write(FAR struct file *filep, FAR const char *buffer,
                        size_t buflen);
static int     userled_ioctl(FAR struct file *filep, int cmd,
                         unsigned long arg);

4、实现外部 SDRAM驱动

我们将通过STM32F429的FMC接口,来驱动W9825G6KH这颗SDRAM芯片。

stm32_extmem.c:外部 SDRAM驱动

该文件实现 stm32_sdram_initialize 函数,供 stm32_bringup() 函数负责初始化并注册外部 SDRAM驱动。此部分实现参照nuttx/boards/arm/stm32/stm32f429i-disco/src/stm32_extmem.c

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

#include <nuttx/config.h>

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

#include <arch/board/board.h>

#include "chip.h"
#include "arm_internal.h"
#include "stm32.h"
#include "apollo-disco.h"

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

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

#if STM32_NGPIO_PORTS < 6
#error "Required GPIO ports not enabled"
#endif

#define STM32_SDRAM_CLKEN     FMC_SDCMR_CMD_CLK_ENABLE | FMC_SDCMR_BANK_1

#define STM32_SDRAM_PALL      FMC_SDCMR_CMD_PALL | FMC_SDCMR_BANK_1

#define STM32_SDRAM_REFRESH   FMC_SDCMR_CMD_AUTO_REFRESH | FMC_SDCMR_BANK_1 |\
                                FMC_SDCMR_NRFS(4)

#define STM32_SDRAM_MODEREG   FMC_SDCMR_CMD_LOAD_MODE | FMC_SDCMR_BANK_1 |\
                                FMC_SDCMR_MDR_BURST_LENGTH_1 | \
                                FMC_SDCMR_MDR_BURST_TYPE_SEQUENTIAL |\
                                FMC_SDCMR_MDR_CAS_LATENCY_3 |\
                                FMC_SDCMR_MDR_WBL_SINGLE

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

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

/* GPIO configurations common to most external memories */

static const uint32_t g_sdram_config[] =
{
  /* 16 data lines */

  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,

  /* 13 address lines */

  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_A12,

  /* control lines */

  GPIO_FMC_SDCKE0, GPIO_FMC_SDNE0, GPIO_FMC_SDNWE, GPIO_FMC_NBL0,
  GPIO_FMC_SDNRAS, GPIO_FMC_NBL1, GPIO_FMC_BA0, GPIO_FMC_BA1,
  GPIO_FMC_SDCLK, GPIO_FMC_SDNCAS,
};


#define NUM_SDRAM_GPIOS (sizeof(g_sdram_config) / sizeof(uint32_t))

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

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

/****************************************************************************
 * Name: stm32_sdram_initialize
 *
 * Description:
 *   Called from stm32_bringup to initialize external SDRAM access.
 *
 ****************************************************************************/

void stm32_sdram_initialize(void)
{
  uint32_t val;
  int i;
  volatile int count;

  /* Enable GPIOs as FMC / memory pins */

  for (i = 0; i < NUM_SDRAM_GPIOS; i++)
    {
      stm32_configgpio(g_sdram_config[i]);
    }

  /* Enable AHB clocking to the FMC */

  stm32_fmc_enable();

/* Configure and enable the SDRAM bank1
   *
   *   FMC clock = 168MHz/2 = 84MHz
   *   84MHz = 11,90 ns
   *   All timings from the datasheet for Speedgrade -7 (=7ns)
   */

  val = FMC_SDCR_RPIPE_1 |      /* rpipe = 1 hclk */
    FMC_SDCR_SDCLK_2X |         /* sdclk = 2 hclk */
    FMC_SDCR_CAS_LATENCY_3 |    /* cas latency = 3 cycles */
    FMC_SDCR_NBANKS_4 |         /* 4 internal banks */
    FMC_SDCR_WIDTH_16 |         /* width = 16 bits */
    FMC_SDCR_ROWS_13 |          /* numrows = 13 */
    FMC_SDCR_COLS_9;            /* numcols = 9 bits */

  stm32_fmc_sdram_set_control(1, val);

  val = FMC_SDTR_TRCD(2) |      /* tRCD min = 15ns */
    FMC_SDTR_TRP(2) |           /* tRP  min = 15ns */
    FMC_SDTR_TWR(2) |           /* tWR      = 2CLK */
    FMC_SDTR_TRC(6) |           /* tRC  min = 60ns */
    FMC_SDTR_TRAS(6) |          /* tRAS min = 60ns */
    FMC_SDTR_TXSR(7) |          /* tXSR min = 72ns */
    FMC_SDTR_TMRD(2);           /* tMRD     = 2CLK */
  
  stm32_fmc_sdram_set_timing(1, val);

  /* SDRAM Initialization sequence */

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

  /* Set refresh count
   *
   * FMC_CLK = 84MHz
   * Refresh_Rate = 7.81us
   * Counter = (FMC_CLK * Refresh_Rate) - 20
   */

  stm32_fmc_sdram_set_refresh_rate(636);

  /* Disable write protection */

  /* stm32_fmc_sdram_write_protect(2, false); */
}

说明:

此文件中相关配置与定义,需按照《STM32F429开发指南 V1.1 – HAL库版本 −ALIENTEK阿波罗STM32F429开发板教程 》

第十八章 SDRAM实验 18.3 软件设计 中的相应内容完成。例如:

  1. 使能FMC时钟

    void stm32_sdram_initialize(void)
    {
        ......
        stm32_fmc_enable();
        ......
    }
    
  2. 配置FMC相关的IO

    参考 图18.1.2.5 W9825G6KH原理图1.2.3 阿波罗 IO引脚分配

    /* GPIO configurations common to most external memories */
    
    static const uint32_t g_sdram_config[] =
    {
      /* 16 data lines */
    
      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,
    
      /* 13 address lines */
    
      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_A12,
    
      /* control lines */
    
      GPIO_FMC_SDCKE0, GPIO_FMC_SDNE0, GPIO_FMC_SDNWE, GPIO_FMC_NBL0,
      GPIO_FMC_SDNRAS, GPIO_FMC_NBL1, GPIO_FMC_BA0, GPIO_FMC_BA1,
      GPIO_FMC_SDCLK, GPIO_FMC_SDNCAS,
    };
    
    #define NUM_SDRAM_GPIOS (sizeof(g_sdram_config) / sizeof(uint32_t))
    
    void stm32_sdram_initialize(void)
    {
        ......
    
      /* Enable GPIOs as FMC / memory pins */
    
      for (i = 0; i < NUM_SDRAM_GPIOS; i++)
        {
          stm32_configgpio(g_sdram_config[i]);
        }
        ......
    }
    
    
  3. 初始化SDRAM控制参数和时间参数

    参考 图18.1.2.5 W9825G6KH原理图 可知,阿波罗STM32F429核心板板载的W9825G6KH芯片挂在FMC SDRAM的控制器1上面(SDNE0),因此需设置寄存器FMC_SDCR1FMC_SDTR1以实现初始化SDRAM控制参数和时间参数。

    • 寄存器FMC_SDCR1用来设置SDRAM的相关控制参数,比如地址线宽度、CAS延迟、SDRAM时钟等。对于W9825G6KH,相关配置信息如下:

      • 内部有4个BANK:FMC_SDCR_NBANKS_4
      • 数据位宽为16位:FMC_SDCR_WIDTH_16
      • 13位行地址:FMC_SDCR_ROWS_13
      • 9位列地址:FMC_SDCR_COLS_9
      • SDRAM的时钟周期为2倍HCLK时钟周期:FMC_SDCR_SDCLK_2X
      • RPIPE延迟为1个HCLK时钟周期:FMC_SDCR_RPIPE_1
      • CAS延迟为3个存储器时钟周期:FMC_SDCR_CAS_LATENCY_3

      因此:

    
    void stm32_sdram_initialize(void)
    {
        uint32_t val;
        ......
        val = FMC_SDCR_RPIPE_1 |      /* rpipe = 1 hclk */
        	FMC_SDCR_SDCLK_2X |         /* sdclk = 2 hclk */
        	FMC_SDCR_CAS_LATENCY_3 |    /* cas latency = 3 cycles */
        	FMC_SDCR_NBANKS_4 |         /* 4 internal banks */
        	FMC_SDCR_WIDTH_16 |         /* width = 16 bits */
        	FMC_SDCR_ROWS_13 |          /* numrows = 13 */
        	FMC_SDCR_COLS_9;            /* numcols = 9 bits */
        
        stm32_fmc_sdram_set_control(1, val);
        ......
    }
    
    
    • 寄存器FMC_SDTR1用来设置SDRAM时间相关参数,比如自刷新时间、恢复延迟、预充电延迟等。对于W9825G6KH,相关配置信息如下:

      • 加载模式到激活时间的延迟为2个存储器时钟周期:FMC_SDTR_TMRD(2)
      • 退出自刷新延迟为7个存储器时钟周期:FMC_SDTR_TXSR(7)
      • 自刷新时间为6个存储器时钟周期:FMC_SDTR_TRAS(6)
      • 行循环延迟为6个存储器存储器时钟周期:FMC_SDTR_TRC(6)
      • 恢复延迟为2个存储器时钟周期:FMC_SDTR_TWR(2)
      • 行预充电延迟为2个存储器时钟周期:FMC_SDTR_TRP(2)
      • 行到列延迟为2个存储器时钟周期:FMC_SDTR_TMRD(2)

      因此

      void stm32_sdram_initialize(void)
      {
          uint32_t val;
          ......
          val = FMC_SDTR_TRCD(2) |      /* tRCD min = 15ns */
          FMC_SDTR_TRP(2) |           /* tRP  min = 15ns */
          FMC_SDTR_TWR(2) |           /* tWR      = 2CLK */
          FMC_SDTR_TRC(6) |           /* tRC  min = 60ns */
          FMC_SDTR_TRAS(6) |          /* tRAS min = 60ns */
          FMC_SDTR_TXSR(7) |          /* tXSR min = 72ns */
          FMC_SDTR_TMRD(2);           /* tMRD     = 2CLK */
        
          stm32_fmc_sdram_set_timing(1, val);
          ......
      }
      
  4. 发送SDRAM初始化序列

    SDRAM初始化步骤为:

    • 首先使能时钟配置,
    • 然后等待至少200us,
    • 对所有BANK进行预充电,
    • 执行自刷新命令等,
    • 最后配置模式寄存器。
    #define STM32_SDRAM_CLKEN     FMC_SDCMR_CMD_CLK_ENABLE | FMC_SDCMR_BANK_1
    
    #define STM32_SDRAM_PALL      FMC_SDCMR_CMD_PALL | FMC_SDCMR_BANK_1
    
    #define STM32_SDRAM_REFRESH   FMC_SDCMR_CMD_AUTO_REFRESH | FMC_SDCMR_BANK_1 |\
                                    FMC_SDCMR_NRFS(8)
    
    #define STM32_SDRAM_MODEREG   FMC_SDCMR_CMD_LOAD_MODE | FMC_SDCMR_BANK_1 |\
                                    FMC_SDCMR_MDR_BURST_LENGTH_1 | \
                                    FMC_SDCMR_MDR_BURST_TYPE_SEQUENTIAL |\
                                    FMC_SDCMR_MDR_CAS_LATENCY_3 |\
                                    FMC_SDCMR_MDR_WBL_SINGLE
    
    void stm32_sdram_initialize(void)
    {
        ......
     /* SDRAM Initialization sequence */
    
      stm32_fmc_sdram_command(STM32_SDRAM_CLKEN);   /* Clock enable command */
      for (count = 0; count < 10000; count++);      /* Delay */
      stm32_fmc_sdram_command(STM32_SDRAM_PALL);    /* Precharge ALL command */
      stm32_fmc_sdram_command(STM32_SDRAM_REFRESH); /* Auto refresh command */
      stm32_fmc_sdram_command(STM32_SDRAM_MODEREG); /* Mode Register program */
        ......
    }
    
  5. 设置刷新频率

    W9825G6KH的刷新周期为64ms,行数为8192行,所以刷新速率为:
    刷新速率 = 64 m s / 8192 = 7.81 u s 刷新速率=64ms/8192=7.81us 刷新速率=64ms/8192=7.81us

    而SDRAM时钟频率=168Mhz/2=84Mhz(降频12M),所以COUNT的值为:
    C O U N T = 7.81 u s / 11.9 n s = 656 COUNT=7.81us/11.9ns=656 COUNT=7.81us/11.9ns=656

    而如果SDRAM在接受读请求后,出现内部刷新请求,则必须将刷新速率增加20个SDRAM时钟周期,以获得充足的余量,所以,实际设计的COUNT值应该是:COUNT-20=636。所以,我们设置FMC_SDRTR的COUNT=636。

    static const uint32_t g_sdram_config[] =
    {
      ......
    
     /* Set refresh count
       *
       * FMC_CLK = 84MHz
       * Refresh_Rate = 7.81us
       * Counter = (FMC_CLK * Refresh_Rate) - 20
       */
    
      stm32_fmc_sdram_set_refresh_rate(636);
        ......
    };
    

5、定义硬件宏

src/apollo-disco.hinclude/board.h 中定义与硬件相关的宏。

apollo-disco.h:板级私有定义

此文件定义了板载外设的 GPIO 引脚和其他私有配置。参考boards/arm/stm32/stm32f429i-disco/src/stm32f429i-disco.h

/* Configuration ************************************************************/

/* LED. 
 *       User LED1: the green LED is a user LED connected to board LED DS1
 * corresponding to MCU I/O PB0.
 *       User LED2: the red LED is a user LED connected to board LED DS0
 * corresponding to MCU I/O PB1.
 *
 * - When the I/O is HIGH value, the LED is on.
 * - When the I/O is LOW, the LED is off.
 */

#define GPIO_LED_RED       (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
                            GPIO_OUTPUT_SET|GPIO_PORTB|GPIO_PIN1)
#define GPIO_LED_GREEN       (GPIO_OUTPUT|GPIO_PUSHPULL|GPIO_SPEED_50MHz|\
                            GPIO_OUTPUT_SET|GPIO_PORTB|GPIO_PIN0)

/* BUTTONS -- NOTE that all have EXTI interrupts configured */
/*
 *        KEY0 : the KEY0 button is connected to the I/O PH3 of the STM32, PULLUP
 *        KEY1 : the KEY1 button is connected to the I/O PH2 of the STM32, PULLUP
 *        KEY2 : the KEY2 button is connected to the I/O PC13 of the STM32, PULLUP
 *        KEY3 : the KEY3 button is connected to the I/O PA0 of the STM32, PULLDOWM
 *
 */

#define MIN_IRQBUTTON   BUTTON_KEY0
#define MAX_IRQBUTTON   BUTTON_KEY3
#define NUM_IRQBUTTONS  4

#define GPIO_BTN_KEY0   (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|GPIO_EXTI|GPIO_PORTH|GPIO_PIN3)
#define GPIO_BTN_KEY1   (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|GPIO_EXTI|GPIO_PORTH|GPIO_PIN2)
#define GPIO_BTN_KEY2   (GPIO_INPUT|GPIO_PULLUP|GPIO_SPEED_50MHz|GPIO_EXTI|GPIO_PORTC|GPIO_PIN13)
#define GPIO_BTN_KEY3   (GPIO_INPUT|GPIO_PULLDOWN|GPIO_SPEED_50MHz|GPIO_EXTI|GPIO_PORTA|GPIO_PIN0)

#define NUM_BUTTONS        4

#define BUTTON_KEY0_BIT    (1 << BUTTON_KEY0)
#define BUTTON_KEY1_BIT    (1 << BUTTON_KEY1)
#define BUTTON_KEY2_BIT    (1 << BUTTON_KEY2)
#define BUTTON_KEY3_BIT    (1 << BUTTON_KEY3)
include/board.h:公共板级定义

此文件包含被 NuttX 内核和应用共享的板级定义,如时钟配置。参考boards/arm/stm32/stm32f429i-disco/include/board.h

#ifndef __BOARDS_ARM_STM32_APOLLO_DISCO_INCLUDE_BOARD_H
#define __BOARDS_ARM_STM32_APOLLO_DISCO_INCLUDE_BOARD_H

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

#include <nuttx/config.h>

#ifndef __ASSEMBLY__
#  include <stdint.h>
#endif

/* DO NOT include STM32 internal header files here */

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

/* Clocking *****************************************************************/

/* The APOLLO Discovery board features a single 25MHz crystal.
 *  Space is provided for a 32kHz RTC backup crystal, but it is not stuffed.
 *
 * This is the canonical configuration:
 *   System Clock source           : PLL (HSE)
 *   SYSCLK(Hz)                    : 168000000    Determined by PLL
 *                                                configuration
 *   HCLK(Hz)                      : 168000000    (STM32_RCC_CFGR_HPRE)
 *   AHB Prescaler                 : 1            (STM32_RCC_CFGR_HPRE)
 *   APB1 Prescaler                : 4            (STM32_RCC_CFGR_PPRE1)
 *   APB2 Prescaler                : 2            (STM32_RCC_CFGR_PPRE2)
 *   HSE Frequency(Hz)             : 25000000      (STM32_BOARD_XTAL)
 *   PLLM                          : 25            (STM32_PLLCFG_PLLM)
 *   PLLN                          : 336          (STM32_PLLCFG_PLLN)
 *   PLLP                          : 2            (STM32_PLLCFG_PLLP)
 *   PLLQ                          : 7            (STM32_PLLCFG_PLLQ)
 *   Main regulator output voltage : Scale1 mode  Needed for high speed
 *                                                SYSCLK
 *   Flash Latency(WS)             : 5
 *   Prefetch Buffer               : OFF
 *   Instruction cache             : ON
 *   Data cache                    : ON
 *   Require 48MHz for USB OTG FS, : Enabled
 *   SDIO and RNG clock
 */

/* HSI - 16 MHz RC factory-trimmed
 * LSI - 32 KHz RC
 * HSE - On-board crystal frequency is 25MHz
 * LSE - 32.768 kHz
 */

#define STM32_BOARD_XTAL        25000000ul

#define STM32_HSI_FREQUENCY     16000000ul
#define STM32_LSI_FREQUENCY     32000
#define STM32_HSE_FREQUENCY     STM32_BOARD_XTAL
#define STM32_LSE_FREQUENCY     32768

/* Main PLL Configuration.
 *
 *
 * Formulae:
 *
 *   VCO input freq        = PLL input clock freq/PLLM 2 <= PLLM <= 63
 *   VCO output freq       = VCO input freq × PLLN,    192 <= PLLN <= 432
 *   PLL output clock freq = VCO freq / PLLP,          PLLP = 2,4,6 or 8
 *   USB OTG FS clock freq = VCO freq / PLLQ,          2 <= PLLQ <= 15
 *
 * There is no config for 180 MHz and 48 MHz for usb,
 * so we would like to have SYSYCLK=168 MHz and we must have the USB
 * clock = 48MHz
 *
 * PLLQ = 7 PLLP = 2 PLLN=336 PLLM=25
 *
 * PLL source is HSE
 * PLL_VCO = (STM32_HSE_FREQUENCY / PLLM) * PLLN
 *         = (25,000,000 / 25) * 336
 *         = 336,000,000
 * SYSCLK  = PLL_VCO / PLLP
 *         = 336,000,000 / 2 = 168,000,000
 * USB OTG FS, SDIO and RNG Clock
 *         =  PLL_VCO / PLLQ
 *         = 48,000,000
 */

#define STM32_PLLCFG_PLLM       RCC_PLLCFG_PLLM(25)
#define STM32_PLLCFG_PLLN       RCC_PLLCFG_PLLN(336)
#define STM32_PLLCFG_PLLP       RCC_PLLCFG_PLLP_2
#define STM32_PLLCFG_PLLQ       RCC_PLLCFG_PLLQ(7)

#define STM32_SYSCLK_FREQUENCY  168000000ul

/* AHB clock (HCLK) is SYSCLK (168MHz) */

#define STM32_RCC_CFGR_HPRE     RCC_CFGR_HPRE_SYSCLK  /* HCLK  = SYSCLK / 1 */
#define STM32_HCLK_FREQUENCY    STM32_SYSCLK_FREQUENCY

/* APB1 clock (PCLK1) is HCLK/4 (42MHz) */

#define STM32_RCC_CFGR_PPRE1    RCC_CFGR_PPRE1_HCLKd4     /* PCLK1 = HCLK / 4 */
#define STM32_PCLK1_FREQUENCY   (STM32_HCLK_FREQUENCY/4)

/* Timers driven from APB1 will be twice PCLK1 */

#define STM32_APB1_TIM2_CLKIN   (2*STM32_PCLK1_FREQUENCY)
#define STM32_APB1_TIM3_CLKIN   (2*STM32_PCLK1_FREQUENCY)
#define STM32_APB1_TIM4_CLKIN   (2*STM32_PCLK1_FREQUENCY)
#define STM32_APB1_TIM5_CLKIN   (2*STM32_PCLK1_FREQUENCY)
#define STM32_APB1_TIM6_CLKIN   (2*STM32_PCLK1_FREQUENCY)
#define STM32_APB1_TIM7_CLKIN   (2*STM32_PCLK1_FREQUENCY)
#define STM32_APB1_TIM12_CLKIN  (2*STM32_PCLK1_FREQUENCY)
#define STM32_APB1_TIM13_CLKIN  (2*STM32_PCLK1_FREQUENCY)
#define STM32_APB1_TIM14_CLKIN  (2*STM32_PCLK1_FREQUENCY)

/* APB2 clock (PCLK2) is HCLK/2 (84MHz) */

#define STM32_RCC_CFGR_PPRE2    RCC_CFGR_PPRE2_HCLKd2     /* PCLK2 = HCLK / 2 */
#define STM32_PCLK2_FREQUENCY   (STM32_HCLK_FREQUENCY/2)

/* Timers driven from APB2 will be twice PCLK2 */

#define STM32_APB2_TIM1_CLKIN   (2*STM32_PCLK2_FREQUENCY)
#define STM32_APB2_TIM8_CLKIN   (2*STM32_PCLK2_FREQUENCY)
#define STM32_APB2_TIM9_CLKIN   (2*STM32_PCLK2_FREQUENCY)
#define STM32_APB2_TIM10_CLKIN  (2*STM32_PCLK2_FREQUENCY)
#define STM32_APB2_TIM11_CLKIN  (2*STM32_PCLK2_FREQUENCY)

/* Timer Frequencies, if APBx is set to 1, frequency is same to APBx
 * otherwise frequency is 2xAPBx.
 * Note: TIM1,8 are on APB2, others on APB1
 */

#define BOARD_TIM1_FREQUENCY    STM32_HCLK_FREQUENCY
#define BOARD_TIM2_FREQUENCY    (STM32_HCLK_FREQUENCY/2)
#define BOARD_TIM3_FREQUENCY    (STM32_HCLK_FREQUENCY/2)
#define BOARD_TIM4_FREQUENCY    (STM32_HCLK_FREQUENCY/2)
#define BOARD_TIM5_FREQUENCY    (STM32_HCLK_FREQUENCY/2)
#define BOARD_TIM6_FREQUENCY    (STM32_HCLK_FREQUENCY/2)
#define BOARD_TIM7_FREQUENCY    (STM32_HCLK_FREQUENCY/2)
#define BOARD_TIM8_FREQUENCY    STM32_HCLK_FREQUENCY

/* LED definitions **********************************************************/

 /*
 * If CONFIG_ARCH_LEDS is not defined, then the user can control the LEDs in
 * any way.  The following definitions are used to access individual LEDs.
 */

/* LED index values for use with board_userled() */

#define BOARD_LED1        0
#define BOARD_LED2        1
#define BOARD_NLEDS       2

#define BOARD_LED_GREEN   BOARD_LED1
#define BOARD_LED_ORANGE  BOARD_LED2

/* LED bits for use with board_userled_all() */

#define BOARD_LED1_BIT    (1 << BOARD_LED1)
#define BOARD_LED2_BIT    (1 << BOARD_LED2)

/* If CONFIG_ARCH_LEDs is defined, then NuttX will control the 2 LEDs on
 * board the apollo-disco.
 * The following definitions describe how NuttX controls the LEDs:
 */

#define LED_STARTED       0  /* LED1 */
#define LED_HEAPALLOCATE  1  /* LED2 */
#define LED_IRQSENABLED   2  /* LED1 + LED2 */
#define LED_STACKCREATED  3  /* LED3 */
#define LED_INIRQ         4  /* LED1 + LED3 */
#define LED_SIGNAL        5  /* LED2 + LED3 */
#define LED_ASSERTION     6  /* LED1 + LED2 + LED3 */
#define LED_PANIC         7  /* N/C  + N/C  + N/C + LED4 */

/* Button definitions *******************************************************/

/* The APOLLO Discovery supports 4 button: */

#define BUTTON_KEY0        0 /* KEY0 */
#define BUTTON_KEY1        1 /* KEY1 */
#define BUTTON_KEY2        2 /* KEY2 */
#define BUTTON_KEY3        3 /* KEY_UP*/

#define NUM_BUTTONS        4

#define BUTTON_KEY0_BIT    (1 << BUTTON_KEY0)
#define BUTTON_KEY1_BIT    (1 << BUTTON_KEY1)
#define BUTTON_KEY2_BIT    (1 << BUTTON_KEY2)
#define BUTTON_KEY3_BIT    (1 << BUTTON_KEY3)

/* Alternate function pin selections ****************************************/

/* USART1:
 *
 * The Apollo Discovery has no on-board serial devices, but the console is
 * brought out to PA9 (TX) and PA10 (RX) for connection to an external serial
 * device. (See the README.txt file for other options)
 */

#define GPIO_USART1_RX GPIO_USART1_RX_1
#define GPIO_USART1_TX GPIO_USART1_TX_1


#endif /* __BOARDS_ARM_STM32_APOLLO_DISCO_INCLUDE_BOARD_H */

6、编写项目配置文件

编写src/Make.defs

参考boards/arm/stm32/stm32f429i-disco/src/Make.defs

include $(TOPDIR)/Make.defs

CSRCS = stm32_boot.c stm32_bringup.c

ifeq ($(CONFIG_ARCH_LEDS),y)
CSRCS += stm32_autoleds.c
else
CSRCS += stm32_userleds.c
endif

ifeq ($(CONFIG_BOARDCTL),y)
CSRCS += stm32_appinit.c
endif

ifeq ($(CONFIG_STM32_FMC),y)
CSRCS += stm32_extmem.c
endif

DEPPATH += --dep-path board
VPATH += :board
CFLAGS += ${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)$(CONFIG_ARCH)$(DELIM)src$(DELIM)board$(DELIM)board

编写src/CMakeLists.txt

参考boards/arm/stm32/stm32f429i-disco/src/CMakeLists.txt)

set(SRCS stm32_boot.c stm32_bringup.c)

if(CONFIG_ARCH_LEDS)
  list(APPEND SRCS stm32_autoleds.c)
else()
  list(APPEND SRCS stm32_userleds.c)
endif()

if(CONFIG_BOARDCTL)
  list(APPEND SRCS stm32_appinit.c)
endif()

if(CONFIG_STM32_FMC)
  list(APPEND SRCS stm32_extmem.c)
endif()

target_sources(board PRIVATE ${SRCS})

set_property(GLOBAL PROPERTY LD_SCRIPT "${NUTTX_BOARD_DIR}/scripts/ld.script")

编写CMakeLists.txt

参考boards/arm/stm32/stm32f429i-disco/CMakeLists.txt

add_subdirectory(src)

7、链接脚本

scripts/Make.defs

参考boards/arm/stm32/stm32f429i-disco/scripts/Make.defs

include $(TOPDIR)/.config
include $(TOPDIR)/tools/Config.mk
include $(TOPDIR)/arch/arm/src/armv7-m/Toolchain.defs

ifeq ($(CONFIG_SYSTEM_OFLOADER),y)
LDSCRIPT = ofloader.ld
else
LDSCRIPT = ld.script
endif

ARCHSCRIPT += $(BOARD_DIR)$(DELIM)scripts$(DELIM)$(LDSCRIPT)

ARCHPICFLAGS = -fpic -msingle-pic-base -mpic-register=r10

CFLAGS := $(ARCHCFLAGS) $(ARCHOPTIMIZATION) $(ARCHCPUFLAGS) $(ARCHINCLUDES) $(ARCHDEFINES) $(EXTRAFLAGS)
CPICFLAGS = $(ARCHPICFLAGS) $(CFLAGS)
CXXFLAGS := $(ARCHCXXFLAGS) $(ARCHOPTIMIZATION) $(ARCHCPUFLAGS) $(ARCHXXINCLUDES) $(ARCHDEFINES) $(EXTRAFLAGS)
CXXPICFLAGS = $(ARCHPICFLAGS) $(CXXFLAGS)
CPPFLAGS := $(ARCHINCLUDES) $(ARCHDEFINES) $(EXTRAFLAGS)
AFLAGS := $(CFLAGS) -D__ASSEMBLY__

NXFLATLDFLAGS1 = -r -d -warn-common
NXFLATLDFLAGS2 = $(NXFLATLDFLAGS1) -T$(TOPDIR)/binfmt/libnxflat/gnu-nxflat-pcrel.ld -no-check-sections
LDNXFLATFLAGS = -e main -s 2048

scripts/ld.script

参考boards/arm/stm32/stm32f429i-disco/scripts/ld.script


/* The STM32F429IGT6 has 1024Kb of FLASH beginning at address 0x0800:0000 and
 * 256Kb of SRAM. SRAM is split up into four blocks:
 *
 * 1) 112Kb of SRAM beginning at address 0x2000:0000
 * 2)  16Kb of SRAM beginning at address 0x2001:c000
 * 3)  64Kb of SRAM beginning at address 0x2002:0000
 * 4)  64Kb of CCM SRAM beginning at address 0x1000:0000
 *
 * When booting from FLASH, FLASH memory is aliased to address 0x0000:0000
 * where the code expects to begin execution by jumping to the entry point in
 * the 0x0800:0000 address
 * range.
 */

MEMORY
{
  flash (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
  sram (rwx) : ORIGIN = 0x20000000, LENGTH = 112K
}

OUTPUT_ARCH(arm)
EXTERN(_vectors)
ENTRY(_stext)
SECTIONS
{
    .text : {
        _stext = ABSOLUTE(.);
        *(.vectors)
        *(.text .text.*)
        *(.fixup)
        *(.gnu.warning)
        *(.rodata .rodata.*)
        *(.gnu.linkonce.t.*)
        *(.glue_7)
        *(.glue_7t)
        *(.got)
        *(.gcc_except_table)
        *(.gnu.linkonce.r.*)
        _etext = ABSOLUTE(.);
    } > flash

    .init_section : ALIGN(4) {
        _sinit = ABSOLUTE(.);
        KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
        KEEP(*(.init_array EXCLUDE_FILE(*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o) .ctors))
        _einit = ABSOLUTE(.);
    } > flash

    .ARM.extab : ALIGN(4) {
        *(.ARM.extab*)
    } > flash

    .ARM.exidx : ALIGN(4) {
        __exidx_start = ABSOLUTE(.);
        *(.ARM.exidx*)
        __exidx_end = ABSOLUTE(.);
    } > flash

    .tdata : {
        _stdata = ABSOLUTE(.);
        *(.tdata .tdata.* .gnu.linkonce.td.*);
        _etdata = ABSOLUTE(.);
    } > flash

    .tbss : {
        _stbss = ABSOLUTE(.);
        *(.tbss .tbss.* .gnu.linkonce.tb.* .tcommon);
        _etbss = ABSOLUTE(.);
    } > flash

    _eronly = ABSOLUTE(.);

    /* The RAM vector table (if present) should lie at the beginning of SRAM */

    .ram_vectors : {
        *(.ram_vectors)
    } > sram

    .data : ALIGN(4) {
        _sdata = ABSOLUTE(.);
        *(.data .data.*)
        *(.gnu.linkonce.d.*)
        CONSTRUCTORS
        . = ALIGN(4);
        _edata = ABSOLUTE(.);
    } > sram AT > flash

    .bss : ALIGN(4) {
        _sbss = ABSOLUTE(.);
        *(.bss .bss.*)
        *(.gnu.linkonce.b.*)
        *(COMMON)
        . = ALIGN(4);
        _ebss = ABSOLUTE(.);
    } > sram

    /* Stabs debugging sections. */

    .stab 0 : { *(.stab) }
    .stabstr 0 : { *(.stabstr) }
    .stab.excl 0 : { *(.stab.excl) }
    .stab.exclstr 0 : { *(.stab.exclstr) }
    .stab.index 0 : { *(.stab.index) }
    .stab.indexstr 0 : { *(.stab.indexstr) }
    .comment 0 : { *(.comment) }
    .debug_abbrev 0 : { *(.debug_abbrev) }
    .debug_info 0 : { *(.debug_info) }
    .debug_line 0 : { *(.debug_line) }
    .debug_pubnames 0 : { *(.debug_pubnames) }
    .debug_aranges 0 : { *(.debug_aranges) }
}

scripts/ofloader.script

参考boards/arm/stm32/stm32f429i-disco/scripts/ofloader.ld


/* The STM32F429IGT6 has 1024Kb of sram beginning at address 0x0800:0000 and
 * 256Kb of SRAM. SRAM is split up into four blocks:
 *
 * 1) 112Kb of SRAM beginning at address 0x2000:0000
 * 2)  16Kb of SRAM beginning at address 0x2001:c000
 * 3)  64Kb of SRAM beginning at address 0x2002:0000
 * 4)  64Kb of CCM SRAM beginning at address 0x1000:0000
 *
 * When booting from sram, FLASH memory is aliased to address 0x0000:0000
 * where the code expects to begin execution by jumping to the entry point in
 * the 0x0800:0000 address
 * range.
 */

MEMORY
{
  sram (rwx) : ORIGIN = 0x20000000, LENGTH = 112K
}

OUTPUT_ARCH(arm)
EXTERN(_vectors)
ENTRY(_stext)
SECTIONS
{
      PrgCode : ALIGN(4) {
        KEEP(*(PrgCode PrgCode.*));
        . = ALIGN(4);
      } > sram

    .text : {
        _stext = ABSOLUTE(.);
        *(.vectors)
        *(.text .text.*)
        *(.fixup)
        *(.gnu.warning)
        *(.rodata .rodata.*)
        *(.gnu.linkonce.t.*)
        *(.glue_7)
        *(.glue_7t)
        *(.got)
        *(.gcc_except_table)
        *(.gnu.linkonce.r.*)
        _etext = ABSOLUTE(.);
    } > sram

    .init_section : ALIGN(4) {
        _sinit = ABSOLUTE(.);
        KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
        KEEP(*(.init_array EXCLUDE_FILE(*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o) .ctors))
        _einit = ABSOLUTE(.);
    } > sram

    .ARM.extab : ALIGN(4) {
        *(.ARM.extab*)
    } > sram

    .ARM.exidx : ALIGN(4) {
        __exidx_start = ABSOLUTE(.);
        *(.ARM.exidx*)
        __exidx_end = ABSOLUTE(.);
    } > sram

    .tdata : {
        _stdata = ABSOLUTE(.);
        *(.tdata .tdata.* .gnu.linkonce.td.*);
        _etdata = ABSOLUTE(.);
    } > sram

    .tbss : {
        _stbss = ABSOLUTE(.);
        *(.tbss .tbss.* .gnu.linkonce.tb.* .tcommon);
        _etbss = ABSOLUTE(.);
    } > sram

    PrgData : ALIGN(4) {
        KEEP(*(PrgData PrgData.*))
        . = ALIGN(4);
    } > sram

    _eronly = ABSOLUTE(.);

    /* The RAM vector table (if present) should lie at the beginning of SRAM */

    .ram_vectors : {
        *(.ram_vectors)
    } > sram

    .data : ALIGN(4) {
        _sdata = ABSOLUTE(.);
        *(.data .data.*)
        *(.gnu.linkonce.d.*)
        CONSTRUCTORS
        . = ALIGN(4);
        _edata = ABSOLUTE(.);
    } > sram

    DevDscr : ALIGN(4) {
        KEEP(*(DevDscr DevDscr.*));
        KEEP(*(DevStack DevStack.*))
        . = ALIGN(4);
        /* StackBuff are the stack used to run opeflashloder,
         * and the location of the transmitted buffer.
         */
    } > sram

    .bss : ALIGN(4) {
        _sbss = ABSOLUTE(.);
        *(.bss .bss.*)
        *(.gnu.linkonce.b.*)
        *(COMMON)
        . = ALIGN(4);
        _ebss = ABSOLUTE(.);
    } > sram


    /* Stabs debugging sections. */

    .stab 0 : { *(.stab) }
    .stabstr 0 : { *(.stabstr) }
    .stab.excl 0 : { *(.stab.excl) }
    .stab.exclstr 0 : { *(.stab.exclstr) }
    .stab.index 0 : { *(.stab.index) }
    .stab.indexstr 0 : { *(.stab.indexstr) }
    .comment 0 : { *(.comment) }
    .debug_abbrev 0 : { *(.debug_abbrev) }
    .debug_info 0 : { *(.debug_info) }
    .debug_line 0 : { *(.debug_line) }
    .debug_pubnames 0 : { *(.debug_pubnames) }
    .debug_aranges 0 : { *(.debug_aranges) }
}

三、集成到构建系统

完成代码编写后,需要修改构建系统配置,使 openvela 能够识别和编译您的新 BSP。

1、Kconfig 集成

Kconfig 用于管理内核和应用的功能配置。您需要执行以下三步来集成新板。

1.在 nuttx/boards/Kconfig 中定义板级选项:

添加一个新的 config 条目,用于在配置菜单中显示您的开发板。

config ARCH_BOARD_APOLLO_DISCO
	bool "ATK APOLLO-Discovery board"
	depends on ARCH_CHIP_STM32F429I
	select ARCH_HAVE_LEDS
	select ARCH_HAVE_BUTTONS
	select ARCH_HAVE_IRQBUTTONS
	---help---
		ATK Apollo-Discovery board based on the STMicro STM32F429IGT6 MCU.

上述可参考条目config ARCH_BOARD_STM32F429I_DISCO
在这里插入图片描述

2.在 nuttx/boards/Kconfig 中设置默认板名:

当选中您的板型时,让 ARCH_BOARD 变量自动设置为您的 BSP 目录名。

config ARCH_BOARD
    string
    # ... 其他板的 default 设置 ...
    default "apollo-disco"      if ARCH_BOARD_APOLLO_DISCO

上述可参考条目default "stm32f429i-disco"
在这里插入图片描述

3.在 nuttx/boards/Kconfig 中加载板级 Kconfig 文件:

确保选中您的板型时,会加载 BSP 目录下的 Kconfig 文件。

if ARCH_BOARD_APOLLO_DISCO
source "boards/arm/stm32/apollo-disco/Kconfig"
endif

上述可参考条目if ARCH_BOARD_STM32F429I_DISCO"
在这里插入图片描述

4.在 apollo-disco/Kconfig 文件中可以添加此板特有的配置选项。

可参照boards/arm/stm32/stm32f429i-disco/Kconfig

#
# For a description of the syntax of this configuration file,
# see the file kconfig-language.txt in the NuttX tools repository.
#

if ARCH_BOARD_APOLLO_DISCO

config APOLLO_DISCO_FLASH
	bool "MTD driver for external 32Mbyte W25Q256 FLASH on SPI5"
	default n
	select MTD
	select MTD_W25QXXXJV
	select MTD_SMART
	select FS_SMARTFS
	select STM32_SPI5
	select MTD_BYTE_WRITE
	---help---
		Configures an MTD device for use with the onboard flash

config APOLLO_DISCO_FLASH_MINOR
	int "Minor number for the FLASH /dev/smart entry"
	default 0
	depends on APOLLO_DISCO_FLASH
	---help---
		Sets the minor number for the FLASH MTD /dev entry

config APOLLO_DISCO_FLASH_PART
	bool "Enable partition support on FLASH"
	default n
	depends on APOLLO_DISCO_FLASH
	---help---
		Enables creation of partitions on the FLASH

config APOLLO_DISCO_FLASH_CONFIG_PART
	bool "Create application config data partition on FLASH"
	default y
	depends on APOLLO_DISCO_FLASH_PART
	depends on PLATFORM_CONFIGDATA
	---help---
		Enables creation of a /dev/config partition on the FLASH

config APOLLO_DISCO_FLASH_CONFIG_PART_NUMBER
	int "Index number of config partition (in list below)"
	default 0
	depends on APOLLO_DISCO_FLASH_CONFIG_PART
	---help---
		Specifies the index number of the config data partition
		from the partition list.

config APOLLO_DISCO_FLASH_PART_LIST
	string "Flash partition size list"
	default "4,8188"
	depends on APOLLO_DISCO_FLASH_PART
	---help---
		Comma separated list of partition sizes in KB.

config APOLLO_DISCO_FLASH_PART_NAMES
	string "Flash partition name list"
	default "config,rfs"
	depends on APOLLO_DISCO_FLASH_PART
	depends on MTD_PARTITION_NAMES
	---help---
		Comma separated list of partition names.

config APOLLO_DISCO_RAMMTD
	bool "MTD driver for SMARTFS RAM disk"
	default n
	select MTD
	select RAMMTD
	---help---
		Configures an MTD based RAM device for use with SMARTFS.

config APOLLO_DISCO_RAMMTD_MINOR
	int "Minor number for RAM /dev/smart entry"
	default 1
	depends on APOLLO_DISCO_RAMMTD
	---help---
		Sets the minor number for the RAM MTD /dev entry

config APOLLO_DISCO_RAMMTD_SIZE
	int "Size in KB of the RAM device to create"
	default 32
	depends on APOLLO_DISCO_RAMMTD
	---help---
		Sets the size of static RAM allocation for the SMART RAM device

config APOLLODISCO_USBHOST_STACKSIZE
	int "USB host waiter stack size"
	default 1024
	depends on USBHOST

config APOLLODISCO_USBHOST_PRIO
	int "USB host waiter task priority"
	default 100
	depends on USBHOST

config PM_BUTTONS
	bool "PM Button support"
	default n
	depends on PM && ARCH_IRQBUTTONS
	---help---
		Enable PM button EXTI interrupts to support PM testing

config PM_BUTTON_ACTIVITY
	int "Button PM activity weight"
	default 10
	depends on PM_BUTTONS
	---help---
		The activity weight to report to the power management subsystem when a button is pressed.

config PM_ALARM_SEC
	int "PM_STANDBY delay (seconds)"
	default 15
	depends on PM && RTC_ALARM
	---help---
		Number of seconds to wait in PM_STANDBY before going to PM_STANDBY mode.

config PM_ALARM_NSEC
	int "PM_STANDBY delay (nanoseconds)"
	default 0
	depends on PM && RTC_ALARM
	---help---
		Number of additional nanoseconds to wait in PM_STANDBY before going to PM_STANDBY mode.

config PM_SLEEP_WAKEUP
	bool "PM_SLEEP wake-up alarm"
	default n
	depends on PM && RTC_ALARM
	---help---
		Wake-up of PM_SLEEP mode after a delay and resume normal operation.

config PM_SLEEP_WAKEUP_SEC
	int "PM_SLEEP delay (seconds)"
	default 10
	depends on PM && RTC_ALARM
	---help---
		Number of seconds to wait in PM_SLEEP before going to PM_STANDBY mode.

config PM_SLEEP_WAKEUP_NSEC
	int "PM_SLEEP delay (nanoseconds)"
	default 0
	depends on PM && RTC_ALARM
	---help---
		Number of additional nanoseconds to wait in PM_SLEEP before going to PM_STANDBY mode.


config APOLLO_DISCO_HIGHPRI
	bool "High priority interrupt test"
	default n

endif # ARCH_BOARD_APOLLO_DISCO

说明

  1. 参考《STM32F429开发指南 V1.1 – HAL库版本 −ALIENTEK阿波罗STM32F429开发板教程 》1.1.2 STM32F429核心板资源第三十二章 SPI实验 可知,阿波罗开发板的上的SPI FLASH W25Q256是直接连在STM32F429的SPI5上的,因此apollo-disco/Kconfig文件中的相关配置项描述需符合硬件配置。
  2. 本阶段不考虑接入LCD屏,因此不包含相关配置项。
    在这里插入图片描述

2、创建默认配置 (defconfig)

defconfig 文件是一个最小化的系统配置集合,为用户提供了一个开箱即用的配置起点。您应该为每个核心功能(如 NSH、特定示例)提供一个 defconfig。

参照nuttx/boards/arm/stm32/stm32f429i-disco/configs/nsh/deconfig,创建 nuttx/boards/arm/stm32/apollo-disco/configs/nsh/deconfig 如下:

#
# This file is autogenerated: PLEASE DO NOT EDIT IT.
#
# You can use "make menuconfig" to make any modifications to the installed .config file.
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
# modifications.
#
# CONFIG_ARCH_FPU is not set
# CONFIG_STM32_FLASH_PREFETCH is not set
CONFIG_ARCH="arm"
CONFIG_ARCH_BOARD="apollo-disco"
CONFIG_ARCH_BOARD_APOLLO_DISCO=y
CONFIG_ARCH_BUTTONS=y
CONFIG_ARCH_CHIP="stm32"
CONFIG_ARCH_CHIP_STM32=y
CONFIG_ARCH_CHIP_STM32F429I=y
CONFIG_ARCH_STACKDUMP=y
CONFIG_BOARD_LOOPSPERMSEC=16717
CONFIG_BUILTIN=y
CONFIG_DEBUG_SYMBOLS=y
CONFIG_FS_PROCFS=y
CONFIG_HAVE_CXX=y
CONFIG_HAVE_CXXINITIALIZE=y
CONFIG_HEAP2_BASE=0xC0000000
CONFIG_HEAP2_SIZE=33554432
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INTELHEX_BINARY=y
CONFIG_MM_REGIONS=3
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_FILEIOSIZE=512
CONFIG_NSH_LINELEN=64
CONFIG_NSH_READLINE=y
CONFIG_PREALLOC_TIMERS=4
CONFIG_RAM_SIZE=114688
CONFIG_RAM_START=0x20000000
CONFIG_RAW_BINARY=y
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_WAITPID=y
CONFIG_SPI=y
CONFIG_START_DAY=6
CONFIG_START_MONTH=12
CONFIG_START_YEAR=2011
CONFIG_STM32_DISABLE_IDLE_SLEEP_DURING_DEBUG=y
CONFIG_STM32_EXTERNAL_RAM=y
CONFIG_STM32_FMC=y
CONFIG_STM32_JTAG_SW_ENABLE=y
CONFIG_STM32_PWR=y
CONFIG_STM32_USART1=y
CONFIG_SYSTEM_NSH=y
CONFIG_TASK_NAME_SIZE=0
CONFIG_USART1_SERIAL_CONSOLE=y

说明:

  1. 参考《STM32F429开发指南 V1.1 – HAL库版本 −ALIENTEK阿波罗STM32F429开发板教程 》1.1.2 STM32F429核心板资源第十八章 SDRAM实验图17.1.2.2 FMC存储块地址映像 可知,阿波罗STM32F429开发板板载了一颗32M字节容量的SDRAM芯片:W9825G6KH,挂载于SDRAM Bank1,因此有CONFIG_HEAP2_BASE=0xC0000000,CONFIG_HEAP2_SIZE=33554432(1024*1024*32)。
    在这里插入图片描述

四、运行Demo

1、编译代码

在完成上面的步骤后,按以下流程为 阿波罗STM32F429 开发板生成所需二进制文件:

# 进入 openvela 根目录  

# 编译  
./build.sh apollo-disco:nsh -j8

编译完成后,生成的文件位于 nuttx 目录下,包括:

  • nuttx.bin
  • nuttx.hex

2、烧录固件

参考在 STM32F411 上使用 openvela 点亮 LED: : 四、运行Demo::5、烧录固件

3、连接串口

参考在 STM32F411 上使用 openvela 点亮 LED: : 四、运行Demo::6、连接串口

4、交互测试

重新打开 minicom,连接成功后,在 Minicom 终端中按回车,您会看到 nsh> 提示符。

输入一些常用命令(如lshelp)若反馈正常,则证明本文代码移植实验成功。
在这里插入图片描述

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐