VSCode下 搭建 ARM Cortex-M 开发环境 -- Part 1 编译环境搭建

  • Part 0 开发工具安装
  • Part 1 编译环境搭建
  • Part 2 调试环境搭建

VSCode下 搭建 ARM Cortex-M 开发环境 -- Part 1 编译环境搭建

前言

本章旨在记录如何在VSCode下搭建编译环境, 具体包含以下几个部分:

  1. Project架构搭建
  2. VSCode下创建同名Project
  3. 移植和编写程序
  4. VSCode下C环境配置
  5. Makefile编译
  6. VSCode下Terminal配置
  7. 编译程序
  8. 程序烧写和运行

Project架构搭建

  • 首先,我们需要在搭建项目的文件夹下创建项目文件夹
    例如下图中,在E:\STMicroelectronics\workspace\stm32f429\projects下创建一个名为HAL_demo的文件夹。后续用到的所有文件等都会放到HAL_demo文件夹下

    项目文件夹.png

  • 然后,在项目文件夹下创建后续所需用到的各个模块的文件夹
    例如下图中,在HAL_demo文件下创建APP、CMSIS、Kernel、lib、LinkerScripts、Startup、STM32F4xx_HAL_Driver、STM32F4xx_System、STM32F429I-Discovery、tools这些文件夹。

    各个模块文件夹.png

    .vscode文件夹是VSCode自动生成的保持settings的文件夹,无需创建
    out文件夹下放的是待会build出来的elf文件、bin文件等,在编译程序过程中会自动生产该文件夹,无需创建
    Makefile文件是整个Project的总Makefile文件,后续会详细介绍该文件,所以后续章节再创建即可

  • 简单介绍下各个模块文件夹的含义。
    APP: 与application相关的文件都会放到APP文件夹下,例如main.c等

    CMSIS: ARM Cortex™ 微控制器软件接口标准(CMSIS:Cortex Microcontroller Software Interface Standard) 是 Cortex-M 处理器系列的与供应商无关的硬件抽象层。所以,CMSIS文件夹下会放ARM Cortex-M 所需的硬件抽象层文件,例如core_cm4.h等

    Kernel: OS相关的文件都会放到Kernel文件夹下,例如FreeRTOS相关的文件

    lib: GNU toolchain中pre-build好的一些lib都会放到lib文件夹下,例如libc.a、libg.a、libgcc.a等

    LinkerScripts: arm-none-eabi-ld所需要的memory layout文件都会放到LinkerScripts文件夹下

    Startup: ARM chip所需的 startup文件会放到Startup文件夹下,例如startup_stm32f429_439xx.s

    STM32F4xx_HAL_Driver: 与STM32F429芯片相关的Hardware driver程序文件都会放在STM32F4xx_HAL_Driver文件夹下,例如stm32f4xx_hal_gpio.c等

    STM32F4xx_System: 与STM32F429芯片相关的system level程序文件都会放在STM32F4xx_System文件夹下,例如system_stm32f4xx.c等

    STM32F429I-Discovery: 与STM32F429I-Discovery板子相关的程序文件都会放在STM32F429I-Discovery文件夹下,例如stm32f429i_discovery_eeprom.c等

    tools: tool所需的文件或者所需的tool文件都放在tools文件夹下

  • 至此,整个Project的架构已经搭建完毕。接下来章节所需做的就是:具体实现各个模块文件夹中的内容

VSCode下创建同名Project

搭建完整个Project架构后,我们可以通过VSCode的Project+ 这个插件构建一个同名Project,方便以后在不同Projects之间来回切换。

  • 首先,在VSCode内按下F1快捷键显示所有命令。


    显示所有命名.png
  • 然后,在窗口中输入如下command,进入到project edit界面 。
>projects:Edit Configuration
project edit界面.png
  • 接着,创建一个name为STM32的group。然后在group的projects下创建一个name为HAL demo的project,并且把path设为刚才创建的HAL_demo这个文件夹的路径。


    创建Project.png
  • 完成上述步骤,即可在VSCode侧边栏中的PROJECTS下见到我们刚才创建的那个Project。以后需要切换到这个project只需右击该project选择open or open in New Window即可。
    Project.png

    右击切换Project.png

移植和编写程序

搭建完Project架构和在VSCode下创建好Project以后,接下来要做就是本文最重要的部分:移植和编写各个module文件夹下的程序了。

  • CMSIS 文件夹下内容实现

CMSIS文件夹下主要需要移植ARM Cortex-M 所需的硬件抽象层文件

  1. 把STM32Cube_FW_F4_V1.18.0\Drivers\CMSIS下所有内容 都复制到 CMSIS文件夹下
    STM32Cube_FW_F4_V1.18.0\Drivers\CMSIS下所有内容.png

    CMSIS下所有内容.png

    至此,CMSIS 文件夹下内容已经全部实现。我们后续所用到文件 其实都在CMSIS\Include下,例如cmsis_gcc.h中会包含__disable_irq 和 __enable_irq这些内联汇编。
    CMSIS文件夹下实际使用到的文件.png
  • Startup 文件夹下内容实现

Startup文件夹下主要需要移植一个startup_stm32f429_439xx.s文件
startup_stm32f429_439xx.s文件主要实现以下功能:

  1. 初始化SP和PC值;
  2. 设置Vector Table;
  3. 初始化.data 和 .bss section在memory上的data;
  4. 初始化C library所需环境,初始化system环境,jump到main函数等。
  1. 在Startup文件夹下创建一个gcc 文件夹Makefile 和 module.mk文件后续章节会介绍
    gcc文件夹.png
  2. 把之前安装好的STM32Cube中的startup_stm32f429.s(具体路径见下图,STM32Cube_FW_F4_V1.18.0\Drivers\CMSIS\Device\ST\STM32F4xx\Source\Templates\gcc)复制到 刚才创建的gcc文件夹下并且命名为startup_stm32f429_439xx.s(见下下图)
    STM32Cube中的startup_stm32f429文件.png

    复制的startup_stm32f429_439xx.s文件.png
  3. 对gcc文件夹中的startup_stm32f429_439xx.s文件作如下改动:把bl __libc_init_array 这行mask掉
    startup_stm32f429_439xx.s文件的改动.png

    至此,Startup文件夹下的内容已经完成。startup.s文件 从第一行code到最后一行code 主要就是为了实现如下的开机FLOW
    STM32 GCC 开机Flow.png
  • STM32F4xx_HAL_Driver 文件夹下内容实现

STM32F4xx_HAL_Driver文件夹下主要需要移植STM32Cube中提供的那些HAL driver文件,例如uart、i2c、spi、adc等硬件设备的驱动程序。

  1. 在STM32F4xx_HAL_Driver 文件夹下分别创建inc和src文件夹Makefile 和 module.mk文件后续章节会介绍
    创建inc和src文件夹.png
  2. STM32Cube_FW_F4_V1.18.0\Drivers\STM32F4xx_HAL_Driver\Inc下的所有头文件和子文件夹 复制 到STM32F4xx_HAL_Driver\inc下
    移植头文件.png
  3. STM32Cube_FW_F4_V1.18.0\Drivers\STM32F4xx_HAL_Driver\Src下的所有C文件 复制 到STM32F4xx_HAL_Driver\src下
    移植C文件.png
  4. STM32F4xx_HAL_Driver\inc下stm32_assert_template.h和stm32f4xx_hal_conf_template.h删除
  5. stm32f4xx_ll_rcc.h做如下修改:#define HSE_VALUE (25000000U) 改为 #define HSE_VALUE (8000000U)
    修改HSE_VALUE.png
  6. STM32F4xx_HAL_Driver\src下stm32f4xx_hal_msp_template.c、stm32f4xx_hal_timebase_rtc_alarm_template.c、stm32f4xx_hal_timebase_rtc_wakeup_template.c 和 stm32f4xx_hal_timebase_tim_template.c删除
  7. stm32f4xx_hal.c做如下修改:mask掉HAL_InitTick(TICK_INT_PRIORITY)这行
    因为FreeRTOS中会对systick进行配置,所以这边无需call HAL_InitTick去配置systick。如果是裸机开发,无需mask这行code。
    Mask HAL_InitTick.png

    至此,STM32F4xx_HAL_Driver文件夹下的内容已经完成。在应用程序中call STM32F4xx_HAL_Driver下各个文件中的API 即可实现对Hardware的控制,不再需要直接去control hardware registers了
  • STM32F4xx_System 文件夹下内容实现

STM32F4xx_System文件夹下主要需要移植一些STM32 System Level所依赖的文件,例如:

  1. system_stm32f4xx.c 中的functions实现System Clock setting,
  2. stm32f4xx_it.c 中的functions实现各个Interrupt handler,
  3. stm32f4xx_hal_msp.c 中的functions实现low level hardware initialization,例如Uart Port GPIO Pinmux Setting等。
  1. 在STM32F4xx_System文件夹下分别创建inc和src文件夹Makefile 和 module.mk文件后续章节会介绍
    STM32F4xx_System文件夹.png
  2. STM32Cube_FW_F4_V1.18.0\Drivers\CMSIS\Device\ST\STM32F4xx\Include下的stm32f4xx.h、stm32f429xx.h以及system_stm32f4xx.h 复制 到 STM32F4xx_System\inc下
    所需复制的stm32f4xx相关头文件.png

    复制到STM32F4xx_System\inc.png
  3. STM32Cube_FW_F4_V1.18.0\Drivers\CMSIS\Device\ST\STM32F4xx\Source\Templates下的system_stm32f4xx.c 复制到 STM32F4xx_System\src下
    所需复制的system_stm32f4xx C文件.png

    复制到STM32F4xx_System\src.png
  4. 按照如下代码,修改STM32F4xx_System\src 下的 system_stm32f4xx.c,主要是为了正确地设置system clock。
/**
  ******************************************************************************
  * @file    system_stm32f4xx.c
  * @author  MCD Application Team
  * @brief   CMSIS Cortex-M4 Device Peripheral Access Layer System Source File.
  *
  *   This file provides two functions and one global variable to be called from 
  *   user application:
  *      - SystemInit(): This function is called at startup just after reset and 
  *                      before branch to main program. This call is made inside
  *                      the "startup_stm32f4xx.s" file.
  *
  *      - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
  *                                  by the user application to setup the SysTick 
  *                                  timer or configure other parameters.
  *                                     
  *      - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
  *                                 be called whenever the core clock is changed
  *                                 during program execution.
  *
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2017 STMicroelectronics </center></h2>
  *
  * 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 of STMicroelectronics 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 HOLDER 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.
  *
  ******************************************************************************
  */

/** @addtogroup CMSIS
  * @{
  */

/** @addtogroup stm32f4xx_system
  * @{
  */  
  
/** @addtogroup STM32F4xx_System_Private_Includes
  * @{
  */

#include "stm32f4xx.h"

#if !defined  (HSE_VALUE) 
  #define HSE_VALUE    ((uint32_t)8000000) /*!< Default value of the External oscillator in Hz */
#endif /* HSE_VALUE */

#if !defined  (HSI_VALUE)
  #define HSI_VALUE    ((uint32_t)16000000) /*!< Value of the Internal oscillator in Hz*/
#endif /* HSI_VALUE */

/**
  * @}
  */

/** @addtogroup STM32F4xx_System_Private_TypesDefinitions
  * @{
  */

/**
  * @}
  */

/** @addtogroup STM32F4xx_System_Private_Defines
  * @{
  */

/************************* Miscellaneous Configuration ************************/
/*!< Uncomment the following line if you need to use external SDRAM mounted
     on DK as data memory  */
/* #define DATA_IN_ExtSDRAM */

/*!< Uncomment the following line if you need to relocate your vector Table in
     Internal SRAM. */
/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET  0x00 /*!< Vector Table base offset field. 
                                   This value must be a multiple of 0x200. */
/******************************************************************************/

/************************* PLL Parameters *************************************/
/* Select the PLL clock source */

// #define PLL_SOURCE_HSI        // HSI (~16 MHz) used to clock the PLL, and the PLL is used as system clock source
#define PLL_SOURCE_HSE        // HSE (8MHz) used to clock the PLL, and the PLL is used as system clock source
// #define PLL_SOURCE_HSE_BYPASS   // HSE bypassed with an external clock (8MHz, coming from ST-Link) used to clock
                                // the PLL, and the PLL is used as system clock source


/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
#if defined  (PLL_SOURCE_HSI)
#define PLL_M      16
#else
#define PLL_M      8
#endif
#define PLL_N      360

/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P      2

/* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLLQ */
#define PLL_Q      7

/******************************************************************************/



/**
  * @}
  */

/** @addtogroup STM32F4xx_System_Private_Macros
  * @{
  */

/**
  * @}
  */

/** @addtogroup STM32F4xx_System_Private_Variables
  * @{
  */
  /* This variable is updated in three ways:
      1) by calling CMSIS function SystemCoreClockUpdate()
      2) by calling HAL API function HAL_RCC_GetHCLKFreq()
      3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency 
         Note: If you use this function to configure the system clock; then there
               is no need to call the 2 first functions listed above, since SystemCoreClock
               variable is updated automatically.
  */
uint32_t SystemCoreClock = 180000000;
const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
const uint8_t APBPrescTable[8]  = {0, 0, 0, 0, 1, 2, 3, 4};
/**
  * @}
  */
  
/** @addtogroup STM32F4xx_System_Private_FunctionPrototypes
  * @{
  */

static void SetSysClock(void);
#if defined (DATA_IN_ExtSDRAM)
  static void SystemInit_ExtMemCtl(void); 
#endif /* DATA_IN_ExtSDRAM */

/** @addtogroup STM32F4xx_System_Private_FunctionPrototypes
  * @{
  */

/**
  * @}
  */

/** @addtogroup STM32F4xx_System_Private_Functions
  * @{
  */

/**
  * @brief  Setup the microcontroller system
  *         Initialize the FPU setting, vector table location and External memory 
  *         configuration.
  * @param  None
  * @retval None
  */
void SystemInit(void)
{
  /* FPU settings ------------------------------------------------------------*/
  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
     SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
  #endif
  /* Reset the RCC clock configuration to the default reset state ------------*/
  /* Set HSION bit */
  RCC->CR |= (uint32_t)0x00000001;

  /* Reset CFGR register */
  RCC->CFGR = 0x00000000;

  /* Reset HSEON, CSSON and PLLON bits */
  RCC->CR &= (uint32_t)0xFEF6FFFF;

  /* Reset PLLCFGR register */
  RCC->PLLCFGR = 0x24003010;

  /* Reset HSEBYP bit */
  RCC->CR &= (uint32_t)0xFFFBFFFF;

  /* Disable all interrupts */
  RCC->CIR = 0x00000000;
  
#if defined (DATA_IN_ExtSDRAM)
  SystemInit_ExtMemCtl(); 
#endif /* DATA_IN_ExtSDRAM */
         
  /* Configure the System clock source, PLL Multiplier and Divider factors, 
     AHB/APBx prescalers and Flash settings ----------------------------------*/
  SetSysClock();

  /* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}

/**
   * @brief  Update SystemCoreClock variable according to Clock Register Values.
  *         The SystemCoreClock variable contains the core clock (HCLK), it can
  *         be used by the user application to setup the SysTick timer or configure
  *         other parameters.
  *           
  * @note   Each time the core clock (HCLK) changes, this function must be called
  *         to update SystemCoreClock variable value. Otherwise, any configuration
  *         based on this variable will be incorrect.         
  *     
  * @note   - The system frequency computed by this function is not the real 
  *           frequency in the chip. It is calculated based on the predefined 
  *           constant and the selected clock source:
  *             
  *           - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
  *                                              
  *           - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
  *                          
  *           - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) 
  *             or HSI_VALUE(*) multiplied/divided by the PLL factors.
  *         
  *         (*) HSI_VALUE is a constant defined in stm32f4xx_hal_conf.h file (default value
  *             16 MHz) but the real value may vary depending on the variations
  *             in voltage and temperature.   
  *    
  *         (**) HSE_VALUE is a constant defined in stm32f4xx_hal_conf.h file (its value
  *              depends on the application requirements), user has to ensure that HSE_VALUE
  *              is same as the real frequency of the crystal used. Otherwise, this function
  *              may have wrong result.
  *                
  *         - The result of this function could be not correct when using fractional
  *           value for HSE crystal.
  *     
  * @param  None
  * @retval None
  */
void SystemCoreClockUpdate(void)
{
  uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2;
  
  /* Get SYSCLK source -------------------------------------------------------*/
  tmp = RCC->CFGR & RCC_CFGR_SWS;

  switch (tmp)
  {
    case 0x00:  /* HSI used as system clock source */
      SystemCoreClock = HSI_VALUE;
      break;
    case 0x04:  /* HSE used as system clock source */
      SystemCoreClock = HSE_VALUE;
      break;
    case 0x08:  /* PLL used as system clock source */

      /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
         SYSCLK = PLL_VCO / PLL_P
         */    
      pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22;
      pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM;
      
      if (pllsource != 0)
      {
        /* HSE used as PLL clock source */
        pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
      }
      else
      {
        /* HSI used as PLL clock source */
        pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6);
      }

      pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2;
      SystemCoreClock = pllvco/pllp;
      break;
    default:
      SystemCoreClock = HSI_VALUE;
      break;
  }
  /* Compute HCLK frequency --------------------------------------------------*/
  /* Get HCLK prescaler */
  tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
  /* HCLK frequency */
  SystemCoreClock >>= tmp;
}

/**
  * @brief  Configures the System clock source, PLL Multiplier and Divider factors, 
  *         AHB/APBx prescalers and Flash settings
  * @Note   This function should be called only once the RCC clock configuration  
  *         is reset to the default reset state (done in SystemInit() function).   
  * @param  None
  * @retval None
  */
static void SetSysClock(void)
{
/******************************************************************************/
/*            PLL (clocked by HSE) used as System clock source                */
/******************************************************************************/
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  

#ifdef PLL_SOURCE_HSI

      /* Configure the main PLL */
    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                   (RCC_PLLCFGR_PLLSRC_HSI) | (PLL_Q << 24);

#else /* PLL_SOURCE_HSE_BYPASS or PLL_SOURCE_HSE */

  /* Enable HSE */
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
#ifdef PLL_SOURCE_HSE_BYPASS
    /* Enable HSE */
  RCC->CR |= ((uint32_t)RCC_CR_HSEBYP);
#endif   /* PLL_SOURCE_HSE_BYPASS */
  /* Wait till HSE is ready and if Time out is reached exit */
  do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY;
    StartUpCounter++;
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

  if ((RCC->CR & RCC_CR_HSERDY) != RESET)
  {
    HSEStatus = (uint32_t)0x01;
  }
  else
  {
    HSEStatus = (uint32_t)0x00;
  }

  if (HSEStatus == (uint32_t)0x01)
  {
    /* Configure the main PLL */
    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                   (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);

  }
    else
  { /* If HSE fails to start-up, the application will have wrong clock
         configuration. User can add here some code to deal with this error */
  }
#endif /*PLL_SOURCE_HSI*/
  
      /* Select regulator voltage output Scale 1 mode, System frequency up to 180 MHz */
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
    PWR->CR |= PWR_CR_VOS;

    /* HCLK = SYSCLK / 1*/
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
      
    /* PCLK2 = HCLK / 2*/
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
    
    /* PCLK1 = HCLK / 4*/
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;

    /* Enable the main PLL */
    RCC->CR |= RCC_CR_PLLON;

    /* Wait till the main PLL is ready */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)
    {
    }

    /* Enable the Over-drive to extend the clock frequency to 180 Mhz */
    PWR->CR |= PWR_CR_ODEN;
    while((PWR->CSR & PWR_CSR_ODRDY) == 0)
    {
    }
    PWR->CR |= PWR_CR_ODSWEN;
    while((PWR->CSR & PWR_CSR_ODSWRDY) == 0)
    {
    } 
     
    /* Configure Flash prefetch, Instruction cache, Data cache and wait state */
    FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS;

    /* Select the main PLL as system clock source */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
    RCC->CFGR |= RCC_CFGR_SW_PLL;

    /* Wait till the main PLL is used as system clock source */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL)
    {
    }
}

#if defined (DATA_IN_ExtSDRAM)
/**
  * @brief  Setup the external memory controller.
  *         Called in startup_stm32f4xx.s before jump to main.
  *         This function configures the external memories (SDRAM)
  *         This SDRAM will be used as program data memory (including heap and stack).
  * @param  None
  * @retval None
  */
void SystemInit_ExtMemCtl(void)
{
  register uint32_t tmpreg = 0, timeout = 0xFFFF;
  register __IO uint32_t index;

  /* Enable GPIOB, GPIOC, GPIOD, GPIOE, GPIOF and GPIOG interface 
  clock */
  RCC->AHB1ENR |= 0x0000007E;

  /* Connect PBx pins to FMC Alternate function */
  GPIOB->AFR[0]  = 0x0CC00000;
  GPIOB->AFR[1]  = 0x00000000;
  /* Configure PBx pins in Alternate function mode */ 
  GPIOB->MODER   = 0x00002A80;
  /* Configure PBx pins speed to 100 MHz */
  GPIOB->OSPEEDR = 0x00003CC0;
  /* Configure PBx pins Output type to push-pull */
  GPIOB->OTYPER  = 0x00000000;
  /* No pull-up, pull-down for PBx pins */ 
  GPIOB->PUPDR   = 0x00000100;

  /* Connect PCx pins to FMC Alternate function */
  GPIOC->AFR[0]  = 0x0000000C;
  GPIOC->AFR[1]  = 0x00000000;
  /* Configure PCx pins in Alternate function mode */ 
  GPIOC->MODER   = 0x00000002;
  /* Configure PCx pins speed to 100 MHz */
  GPIOC->OSPEEDR = 0x00000003;
  /* Configure PCx pins Output type to push-pull */
  GPIOC->OTYPER  = 0x00000000;
  /* No pull-up, pull-down for PCx pins */ 
  GPIOC->PUPDR   = 0x00000000;

  /* Connect PDx pins to FMC Alternate function */
  GPIOD->AFR[0]  = 0x000000CC;
  GPIOD->AFR[1]  = 0xCC000CCC;
  /* Configure PDx pins in Alternate function mode */ 
  GPIOD->MODER   = 0xA02A000A;
  /* Configure PDx pins speed to 100 MHz */
  GPIOD->OSPEEDR = 0xF03F000F;
  /* Configure PDx pins Output type to push-pull */
  GPIOD->OTYPER  = 0x00000000;
  /* No pull-up, pull-down for PDx pins */ 
  GPIOD->PUPDR   = 0x00000000;

  /* Connect PEx pins to FMC Alternate function */
  GPIOE->AFR[0]  = 0xC00000CC;
  GPIOE->AFR[1]  = 0xCCCCCCCC;
  /* Configure PEx pins in Alternate function mode */ 
  GPIOE->MODER   = 0xAAAA800A;
  /* Configure PEx pins speed to 100 MHz */
  GPIOE->OSPEEDR = 0xFFFFC00F;
  /* Configure PEx pins Output type to push-pull */
  GPIOE->OTYPER  = 0x00000000;
  /* No pull-up, pull-down for PEx pins */ 
  GPIOE->PUPDR   = 0x00000000;
  
  /* Connect PFx pins to FMC Alternate function */
  GPIOF->AFR[0]  = 0x00CCCCCC;
  GPIOF->AFR[1]  = 0xCCCCC000;
  /* Configure PFx pins in Alternate function mode */ 
  GPIOF->MODER   = 0xAA800AAA;
  /* Configure PFx pins speed to 100 MHz */
  GPIOF->OSPEEDR = 0xFFC00FFF;
  /* Configure PFx pins Output type to push-pull */
  GPIOF->OTYPER  = 0x00000000;
  /* No pull-up, pull-down for PFx pins */ 
  GPIOF->PUPDR   = 0x00000000;
  
  /* Connect PGx pins to FMC Alternate function */
  GPIOG->AFR[0]  = 0x00CC00CC;
  GPIOG->AFR[1]  = 0xC000000C;
  /* Configure PGx pins in Alternate function mode */ 
  GPIOG->MODER   = 0x80020A0A;
  /* Configure PGx pins speed to 100 MHz */
  GPIOG->OSPEEDR = 0xC0030F0F;
  /* Configure PGx pins Output type to push-pull */
  GPIOG->OTYPER  = 0x00000000;
  /* No pull-up, pull-down for PGx pins */ 
  GPIOG->PUPDR   = 0x00000000;

  /* FMC Configuration */
  /* Enable the FMC interface clock */
  RCC->AHB3ENR |= 0x00000001;
  
  /* Configure and enable SDRAM bank2 */
  FMC_Bank5_6->SDCR[0] = 0x00002ED0;
  FMC_Bank5_6->SDCR[1] = 0x000001D4;
  FMC_Bank5_6->SDTR[0] = 0x0F1F6FFF;
  FMC_Bank5_6->SDTR[1] = 0x01010361;
  
  /* SDRAM initialization sequence */
  /* Clock enable command */
  FMC_Bank5_6->SDCMR = 0x00000009; 
  tmpreg = FMC_Bank5_6->SDSR & 0x00000020; 
  while((tmpreg != 0) && (timeout-- > 0))
  {
    tmpreg = FMC_Bank5_6->SDSR & 0x00000020; 
  }

  /* Delay */
  for (index = 0; index<1000; index++);
  
  /* PALL command */
  FMC_Bank5_6->SDCMR = 0x0000000A;     
  timeout = 0xFFFF;
  while((tmpreg != 0) && (timeout-- > 0))
  {
    tmpreg = FMC_Bank5_6->SDSR & 0x00000020; 
  }
  
  /* Auto refresh command */
  FMC_Bank5_6->SDCMR = 0x0000006B;
  timeout = 0xFFFF;
  while((tmpreg != 0) && (timeout-- > 0))
  {
    tmpreg = FMC_Bank5_6->SDSR & 0x00000020; 
  }
 
  /* MRD register program */
  FMC_Bank5_6->SDCMR = 0x0004620C;
  timeout = 0xFFFF;
  while((tmpreg != 0) && (timeout-- > 0))
  {
    tmpreg = FMC_Bank5_6->SDSR & 0x00000020; 
  } 
  
  /* Set refresh count */
  tmpreg = FMC_Bank5_6->SDRTR;
  FMC_Bank5_6->SDRTR = (tmpreg | (0x0000056A<<1));
  
  /* Disable write protection */
  tmpreg = FMC_Bank5_6->SDCR[1]; 
  FMC_Bank5_6->SDCR[1] = (tmpreg & 0xFFFFFDFF);
}
#endif /* DATA_IN_ExtSDRAM */

/**
  * @}
  */

/**
  * @}
  */
  
/**
  * @}
  */    
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
  1. STM32Cube_FW_F4_V1.18.0\Projects\STM32F429I-Discovery\Examples\UART\UART_TwoBoards_ComPolling\Inc下的stm32f4xx_it.h 复制到 STM32F4xx_System\inc下
    所需复制的stm32f4xx_it头文件.png

    复制到STM32F4xx_System\inc.png
  2. 修改STM32F4xx_System\inc下的stm32f4xx_it.h:把 #include "main.h" 替换为 #include "stm32f429xx.h"
    因为main.h会放到APP文件夹下,所以不希望这边直接include APP下的东西
    修改stm32f4xx_it.h.png
  3. STM32Cube_FW_F4_V1.18.0\Projects\STM32F429I-Discovery\Examples\UART\UART_TwoBoards_ComPolling\Src下的stm32f4xx_it.c 复制到 STM32F4xx_System\src下
    所需复制的stm32f4xx_it C文件.png

    复制到STM32F4xx_System\src.png
  4. 修改STM32F4xx_System\src下的stm32f4xx_it.c
    1) 把 #include "main.h" 这行删除
    删除main.h.png

    2) 对SVC_Handler, PendSV_Handler, SysTick_Handler做如下改动:
    修改exception handler.png

    这几个function都对应着ARM Cortex-M架构下的几种exception, 在startup_stm32f429_439xx.s中定义vector table的时候会用到这几个function的起始address,而FreeRTOS 下portable部分会另外实作这几个function。为了避免Linking阶段发生重定义错误,所以特别在stm32f4xx_it.c中的这几个function前面加上weak属性,这样待会产生可执行文件的时候会优先使用FreeRTOS中的实作,而不会使用stm32f4xx_it.c中的实作
  5. STM32Cube_FW_F4_V1.18.0\Projects\STM32F429I-Discovery\Examples\UART\UART_TwoBoards_ComPolling\Inc下的main.h 复制到 STM32F4xx_System\inc下并且 重名为 stm32f4xx_hal_msp.h
    所需复制的main头文件.png

    复制到STM32F4xx_System\inc并且重命名.png
  6. 按照如下代码,修改STM32F4xx_System\inc下的stm32f4xx_hal_msp.h
/**
  ******************************************************************************
  * @file    UART/UART_TwoBoards_ComPolling/Inc/main.h 
  * @author  MCD Application Team
  * @brief   Header for main.c module
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT(c) 2017 STMicroelectronics</center></h2>
  *
  * 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 of STMicroelectronics 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 HOLDER 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.
  *
  ******************************************************************************
  */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F4XX_HAL_MSP_H
#define __STM32F4XX_HAL_MSP_H

/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"

/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* User can use this section to tailor USARTx/UARTx instance used and associated 
   resources */
/* Definition for USARTx clock resources */
#define USARTx                           USART1
#define USARTx_CLK_ENABLE()              __HAL_RCC_USART1_CLK_ENABLE();
#define USARTx_RX_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOA_CLK_ENABLE()
#define USARTx_TX_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOA_CLK_ENABLE() 

#define USARTx_FORCE_RESET()             __HAL_RCC_USART2_FORCE_RESET()
#define USARTx_RELEASE_RESET()           __HAL_RCC_USART2_RELEASE_RESET()

/* Definition for USARTx Pins */
#define USARTx_TX_PIN                    GPIO_PIN_9
#define USARTx_TX_GPIO_PORT              GPIOA  
#define USARTx_TX_AF                     GPIO_AF7_USART1
#define USARTx_RX_PIN                    GPIO_PIN_10
#define USARTx_RX_GPIO_PORT              GPIOA 
#define USARTx_RX_AF                     GPIO_AF7_USART1

/* Exported functions ------------------------------------------------------- */

#endif /* __STM32F4XX_HAL_MSP_H */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

stm32f4xx_hal_msp.h 主要来自于main.h。在stm32f4xx_hal_msp.h中我们主要需要实现待会吐log所用的Uart Port的相关定义。

  1. STM32Cube_FW_F4_V1.18.0\Projects\STM32F429I-Discovery\Examples\UART\UART_TwoBoards_ComPolling\Src下的stm32f4xx_hal_msp.c 复制到 STM32F4xx_System\src下
    所需复制的stm32f4xx_hal_msp C文件.png

    复制到STM32F4xx_System\src.png
  2. 修改STM32F4xx_System\src下的stm32f4xx_hal_msp.c: 把#include "main.h" 改为 #include "stm32f4xx_hal_msp.h"
    修改include.png

    至此,STM32F4xx_System文件夹下的内容已经完成。
    如果需要配置Fault/Exception/Interrupt handler, 只需修改stm32f4xx_it.c;
    如果需要修改System Clock, 只需修改system_stm32f4xx.c;
    如果需要修改Log口Hardware配置,只需修改stm32f4xx_hal_msp.c
  • STM32F429I-Discovery 文件夹下内容实现

STM32F429I-Discovery文件夹下主要需要移植STM32F429I_DISCOVERY Board相关的device driver, 例如板子上的lcd、gyroscope device等设备的驱动程序

  1. 在STM32F429I-Discovery文件夹下分别创建inc和src文件夹其他文件和文件夹后续章节会介绍
    创建inc和src文件夹.png
  2. STM32Cube_FW_F4_V1.18.0\Drivers\BSP\STM32F429I-Discovery下 所有H文件 复制到 STM32F429I-Discovery\inc下
    STM32Cube_FW_F4_V1.18.0\Drivers\BSP\STM32F429I-Discovery下所有H文件.png

    复制所有H文件.png
  3. STM32Cube_FW_F4_V1.18.0\Drivers\BSP\STM32F429I-Discovery下 所有C文件 复制到 STM32F429I-Discovery\src下
    STM32Cube_FW_F4_V1.18.0\Drivers\BSP\STM32F429I-Discovery下所有C文件.png

    复制所有C文件.png
  4. STM32Cube_FW_F4_V1.18.0\Drivers\BSP下 Components文件夹 直接复制到 STM32F429I-Discovery文件夹下
    STM32Cube_FW_F4_V1.18.0\Drivers\BSP下Components文件夹.png

    STM32F429I-Discovery文件夹下Components文件夹.png
  5. STM32Cube_FW_F4_V1.18.0\Utilities下 Fonts文件夹 直接复制到 STM32F429I-Discovery文件夹下
    STM32Cube_FW_F4_V1.18.0\Utilities下Fonts文件夹.png

    STM32F429I-Discovery文件夹下Fonts文件夹.png
  6. 修改STM32F429I-Discovery\src\stm32f429i_discovery_lcd.c 和 STM32F429I-Discovery\src\stm32f429i_discovery_lcd.h 文件:把其中所有 "../../../Utilities/" 替换为 ".."
    H文件中的修改.png

    C文件中的修改.png

    修改这部分是因为我们Project中的Fonts文件夹和STM32Cube中的路径不一致,如果不按照上述方式修改会发生build error。
    至此,STM32F429I-Discovery 文件夹下内容已经全部实现,如果板子是用的是STM32F429I_DISCOVERY Board,可以使用该文件夹下的设备驱动,例如待会会使用BSP_LED_Init/BSP_LED_Toggle 这些API去控制板子上的LED灯
  • Kernel 文件夹下内容实现

Kernel文件夹下主要需要移植FreeRTOS操作系统

  1. 在Kernel文件夹下创建FreeRTOS文件夹其他文件后续章节会介绍
    创建FreeRTOS文件夹.png
  2. 进入下载好的FreeRTOS Source Code路径下的Source文件夹下,例如我这边的E:\FreeRTOS\Source_Code\FreeRTOSV8.2.3\FreeRTOS\Source
    Source文件夹.png
  3. Source文件夹下的所有文件和文件夹都 复制到 Kernel/FreeRTOS下
    图片.png
  4. 进入到Kernel/FreeRTOS/portable下,把除了 GCC文件夹、MemMang文件夹 和 readme 以外的所有的东西都删除掉
    portable文件夹.png
  5. 进入到Kernel/FreeRTOS/portable/GCC下,把除了 ARM_CM4F文件夹 以外的所有的东西都删除掉
    如果读者使用的CPU不是Cortex-M4 Core的话,那么就只需要在GCC下保留对应的CPU架构的文件夹,其他架构的文件夹都不需要。例如Cortex-M0的话,就留ARM_CM0就好了
    GCC文件夹.png

    至此,FreeRTOS Kernel已经移植好了,待会在APP文件夹下添加一个FreeRTOSConfig.h文件,整个Project即可使用FreeRTOS了。
  • lib 文件夹下内容实现

lib文件夹下主要存放pre-build好的一些lib,例如libc.a、libg.a、libgcc.a等

  1. 打开{ARM GNU ToolChain 文件夹}\share\doc\gcc-arm-none-eabi\readme.txt发现,CM4F所使用的lib是放在v7e-m/fpv4-sp/hard下的lib
    前提是使用Hard FP
    查看ReadMe.png

    Hard FP.png
  2. 进入到{ARM GNU ToolChain 文件夹}\lib\gcc下,把整个arm-none-eabi文件夹 复制到 lib 文件夹下
    lib\gcc下arm-none-eabi文件夹.png

    lib文件夹下arm-none-eabi文件夹.png
  3. 待会会用到lib\arm-none-eabi\6.3.1\thumb\v7e-m\fpv4-sp\hard下的libgcc.a
    libgcc.png
  4. 进入到{ARM GNU ToolChain 文件夹}\arm-none-eabi\lib\thumb下,把整个v7e-m文件夹 复制到 lib 文件夹下


    arm-none-eabi\lib\thumb下v7e-m文件夹.png

    lib文件夹下v7e-m文件夹.png
  5. 待会会用到lib\v7e-m\fpv4-sp\hard下的libc.a 和 libg.a
    libc.a 和 libg.a.png
  6. 进入到{ARM GNU ToolChain 文件夹}\arm-none-eabi文件夹下,把整个include文件夹 复制到 lib 文件夹下
    arm-none-eabi文件夹下include文件夹.png

    lib 文件夹下include文件夹.png

    至此,lib下内容已经完全实现。待会build code时候用到的lib, 例如libc.a、libg.a、libgcc.a等,都会在lib\arm-none-eabi\6.3.1\thumb\v7e-m/fpv4-sp/hard or lib\v7e-m/fpv4-sp/hard
  • APP 文件夹下内容实现

APP文件夹主要实现main.c 和 syscall.c(提供libc所需的system calls, 例如_exit等)

  1. 在APP文件夹下分别创建inc和src文件夹其他文件后续章节会介绍
    创建inc和src文件夹.png
  2. 在inc文件夹下 创建 main.h,具体内容如下(主要参考STM32Cube_FW_F4_V1.18.0\Projects\STM32F429I-Discovery\Examples\UART\UART_TwoBoards_ComPolling\Inc\main.h):
    main.h文件.png
/**
  ******************************************************************************
  * @file    UART/UART_TwoBoards_ComPolling/Inc/main.h 
  * @author  MCD Application Team
  * @brief   Header for main.c module
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT(c) 2017 STMicroelectronics</center></h2>
  *
  * 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 of STMicroelectronics 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 HOLDER 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.
  *
  ******************************************************************************
  */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H

/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "stm32f429i_discovery.h"
#include "stm32f4xx_hal_msp.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "croutine.h"
#include "event_groups.h"
#include "semphr.h"

/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* User can use this section to tailor USARTx/UARTx instance used and associated 
   resources */
/* Size of Transmission buffer */
#define TXBUFFERSIZE                     (COUNTOF(aTxBuffer) - 1)
/* Size of Reception buffer */
#define RXBUFFERSIZE                     TXBUFFERSIZE

/* Exported macro ------------------------------------------------------------*/
#define COUNTOF(__BUFFER__)   (sizeof(__BUFFER__) / sizeof(*(__BUFFER__)))

/* Exported functions ------------------------------------------------------- */

#endif /* __MAIN_H */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
  1. 在inc文件夹下 创建 FreeRTOSConfig.h,具体内容如下(主要参考FreeRTOSV8.2.3\FreeRTOS\Demo\CORTEX_M4F_STM32F407ZG-SK\FreeRTOSConfig.h):
    FreeRTOSConfig.h.png
/*
    FreeRTOS V8.2.3 - Copyright (C) 2015 Real Time Engineers Ltd.
    All rights reserved

    VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.

    This file is part of the FreeRTOS distribution.

    FreeRTOS is free software; you can redistribute it and/or modify it under
    the terms of the GNU General Public License (version 2) as published by the
    Free Software Foundation >>>> AND MODIFIED BY <<<< the FreeRTOS exception.

    ***************************************************************************
    >>!   NOTE: The modification to the GPL is included to allow you to     !<<
    >>!   distribute a combined work that includes FreeRTOS without being   !<<
    >>!   obliged to provide the source code for proprietary components     !<<
    >>!   outside of the FreeRTOS kernel.                                   !<<
    ***************************************************************************

    FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    FOR A PARTICULAR PURPOSE.  Full license text is available on the following
    link: http://www.freertos.org/a00114.html

    ***************************************************************************
     *                                                                       *
     *    FreeRTOS provides completely free yet professionally developed,    *
     *    robust, strictly quality controlled, supported, and cross          *
     *    platform software that is more than just the market leader, it     *
     *    is the industry's de facto standard.                               *
     *                                                                       *
     *    Help yourself get started quickly while simultaneously helping     *
     *    to support the FreeRTOS project by purchasing a FreeRTOS           *
     *    tutorial book, reference manual, or both:                          *
     *    http://www.FreeRTOS.org/Documentation                              *
     *                                                                       *
    ***************************************************************************

    http://www.FreeRTOS.org/FAQHelp.html - Having a problem?  Start by reading
    the FAQ page "My application does not run, what could be wrong?".  Have you
    defined configASSERT()?

    http://www.FreeRTOS.org/support - In return for receiving this top quality
    embedded software for free we request you assist our global community by
    participating in the support forum.

    http://www.FreeRTOS.org/training - Investing in training allows your team to
    be as productive as possible as early as possible.  Now you can receive
    FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
    Ltd, and the world's leading authority on the world's leading RTOS.

    http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
    including FreeRTOS+Trace - an indispensable productivity tool, a DOS
    compatible FAT file system, and our tiny thread aware UDP/IP stack.

    http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
    Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.

    http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
    Integrity Systems ltd. to sell under the OpenRTOS brand.  Low cost OpenRTOS
    licenses offer ticketed support, indemnification and commercial middleware.

    http://www.SafeRTOS.com - High Integrity Systems also provide a safety
    engineered and independently SIL3 certified version for use in safety and
    mission critical applications that require provable dependability.

    1 tab == 4 spaces!
*/


#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

/*-----------------------------------------------------------
 * Application specific definitions.
 *
 * These definitions should be adjusted for your particular hardware and
 * application requirements.
 *
 * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
 * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
 *
 * See http://www.freertos.org/a00110.html.
 *----------------------------------------------------------*/

/* Ensure stdint is only used by the compiler, and not the assembler. */
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
    #include <stdint.h>
    extern uint32_t SystemCoreClock;
#endif

#define configUSE_PREEMPTION            1
#define configUSE_IDLE_HOOK             0
#define configUSE_TICK_HOOK             1
#define configCPU_CLOCK_HZ              ( SystemCoreClock )
#define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES            ( 5 )
#define configMINIMAL_STACK_SIZE        ( ( unsigned short ) 128 )
#define configTOTAL_HEAP_SIZE           ( ( size_t ) ( 30 * 1024 ) )
#define configMAX_TASK_NAME_LEN         ( 16 )
#define configUSE_TRACE_FACILITY        1
#define configUSE_16_BIT_TICKS          0
#define configIDLE_SHOULD_YIELD         0

#define configUSE_MUTEXES               1
#define configQUEUE_REGISTRY_SIZE       8
//#define configCHECK_FOR_STACK_OVERFLOW    2
#define configUSE_RECURSIVE_MUTEXES     1
#define configUSE_MALLOC_FAILED_HOOK    0
#define configUSE_APPLICATION_TASK_TAG  0
#define configUSE_COUNTING_SEMAPHORES   1

#define configUSE_STATS_FORMATTING_FUNCTIONS    1
//#define configSUPPORT_DYNAMIC_ALLOCATION 1


/* Co-routine definitions. */
#define configUSE_CO_ROUTINES       0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

/* Software timer definitions. */
#define configUSE_TIMERS                1
#define configTIMER_TASK_PRIORITY       ( 2 )
#define configTIMER_QUEUE_LENGTH        10
#define configTIMER_TASK_STACK_DEPTH    ( configMINIMAL_STACK_SIZE * 2 )

/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
#define INCLUDE_vTaskPrioritySet        1
#define INCLUDE_uxTaskPriorityGet       1
#define INCLUDE_vTaskDelete             1
#define INCLUDE_vTaskCleanUpResources   0
#define INCLUDE_vTaskSuspend            1
#define INCLUDE_vTaskDelayUntil         1
#define INCLUDE_vTaskDelay              1
#define INCLUDE_pcTaskGetTaskName       1


/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
    /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
    #define configPRIO_BITS             __NVIC_PRIO_BITS
#else
    #define configPRIO_BITS             4        /* 15 priority levels */
#endif

/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY         0xf

/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY    0x01

/* Interrupt priorities used by the kernel port layer itself.  These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY         ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY    ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
    
/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); } 
    
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler

#endif /* FREERTOS_CONFIG_H */
  1. STM32Cube_FW_F4_V1.18.0\Projects\STM32F429I-Discovery\Examples\UART\UART_TwoBoards_ComPolling\Inc下的stm32f4xx_hal_conf.h文件 复制到 APP\inc
    原stm32f4xx_hal_conf.h.png

    APP\inc下stm32f4xx_hal_conf.h.png
  2. 对APP\inc下的stm32f4xx_hal_conf.h文件做如下修改
    stm32f4xx_hal_conf.h中的修改.png
  3. 在APP\src 下创建 syscalls.c 文件,用于完成C lib所需的system calls。具体内容如下:
    图片.png
#include <errno.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stm32f4xx.h>

#undef errno
extern int errno;

// extern int __io_putchar(int ch) __attribute__((weak));
// extern int __io_getchar(void) __attribute__((weak));

int __io_putchar(int ch)
{
    // return(ITM_SendChar(ch));
    extern UART_HandleTypeDef UartHandle;
    extern HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);

    HAL_UART_Transmit(&UartHandle, (uint8_t*)&ch, 1, 10);
    return 0;
}

int __io_getchar(void)
{
    // return(ITM_ReceiveChar());
    return 0;
}

int _close(int file)
{
    return -1;
}

int _stat(char *file, struct stat *st)
{
    st->st_mode = S_IFCHR;
    return 0;
}

int _fstat(int file, struct stat *st)
{
    st->st_mode = S_IFCHR;
    return 0;
}

int _isatty(int file)
{
    return 1;
}

int _lseek(int file, int ptr, int dir)
{
    return 0;
}

int _open(const char *name, int flags, int mode)
{
    return -1;
}

int _read(int file, char *ptr, int len)
{
    // int DataIdx;

    // for (DataIdx = 0; DataIdx < len; DataIdx++) {
    //     __io_putchar('+');
    //     *ptr++ = __io_getchar();
    //     __io_putchar('-');
    // }

    // return len;
    return 0;
}

int _write(int file, char *ptr, int len)
{
    // int DataIdx;

    // for (DataIdx = 0; DataIdx < len; DataIdx++) {
    //     __io_putchar(*ptr++);
    // }
    extern UART_HandleTypeDef UartHandle;
    extern HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
    HAL_UART_Transmit(&UartHandle, (uint8_t*)ptr, len, 10);
    return len;
}

caddr_t _sbrk_r(struct _reent *r, size_t nbytes)
{
    extern char end;       /* Defined by linker */
    static char *heap_end; /* Previous end of heap or 0 if none */
    char        *prev_heap_end;
    char        *stack;

    __asm volatile("MRS %0, msp\n" : "=r"(stack));

    if (0 == heap_end) {
        heap_end = &end; /* Initialize first time round */
    }

    prev_heap_end = heap_end;

    if (stack < (prev_heap_end + nbytes)) {
     /* heap would overlap the current stack depth.
        * Future:  use sbrk() system call to make simulator grow memory beyond
        * the stack and allocate that
        */
        //errno = ENOMEM;
        return (char *) - 1;
    }
    heap_end += nbytes;

    return (caddr_t) prev_heap_end;
}

int _execve(char *name, char **argv, char **env)
{
    errno = ENOTSUP;
    return -1;
}

int _fork(void)
{
    errno = ENOTSUP;
    return -1;
}
/*
 * * getpid -- only one process, so just return 1.
 * */
#define __MYPID 1
int _getpid()
{
    return __MYPID;
}


/*
 * * kill -- go out via exit...
 * */
int _kill(int pid, int sig)
{
    errno = ENOTSUP;
    return -1;
}

int _wait(int *status)
{
    errno = ENOTSUP;
    return -1;
}

void _exit(int __status)
{
    errno = ENOTSUP;
    // return -1;
}

int _link(char *old, char *new)
{
    errno = EMLINK;
    return -1;
}

int _unlink(char *name)
{
    errno = EMLINK;
    return -1;
}

  1. 在syscall.c中特别有去把_write function 接到 HAL UART API上。这样,待会程序跑起来以后,所有call printf 吐出的log 都会通过UART口吐出来。所以,待会程序测试的时候可以用串口工具软件查看LOG方便调试。
    把printf接到UART上.png
  2. 在APP\src 下创建 main.c 文件,用于完成整个Project的主要功能。具体内容如下:
    main.c文件.png
/**
  ******************************************************************************
  * @file    UART/UART_TwoBoards_ComPolling/Src/main.c
  * @author  MCD Application Team
  * @brief   This sample code shows how to use STM32F4xx UART HAL API to transmit
  *          and receive a data buffer with a communication process based on
  *          polling transfer.
  *          The communication is done using 2 Boards.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT(c) 2017 STMicroelectronics</center></h2>
  *
  * 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 of STMicroelectronics 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 HOLDER 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.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "main.h"


/** @addtogroup STM32F4xx_HAL_Examples
  * @{
  */

/** @addtogroup UART_TwoBoards_ComPolling
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define TRANSMITTER_BOARD

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* UART handler declaration */
UART_HandleTypeDef UartHandle;

/* Buffer used for transmission */
uint8_t aTxBuffer[] = " **** UART_TwoBoards_ComPolling ****  **** UART_TwoBoards_ComPolling ****  **** UART_TwoBoards_ComPolling **** \r\n";

/* Buffer used for reception */
uint8_t aRxBuffer[RXBUFFERSIZE];

static TaskHandle_t xHandleTaskLED3 = NULL;
static TaskHandle_t xHandleTaskLED4 = NULL;

/* Private function prototypes -----------------------------------------------*/
static void SystemClock_Config(void);
static void Error_Handler(void);
static uint16_t Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength);
static void vTaskLED3(void *pvParameters);
static void vTaskLED4(void *pvParameters);
/* Private functions ---------------------------------------------------------*/
void vApplicationTickHook(void)
{
    uint32_t i = 0;
    for(i=0; i < portTICK_PERIOD_MS; i++)
    {
        HAL_IncTick();
    }
}


/**
  * @brief  Main program
  * @param  None
  * @retval None
  */
int main(void)
{

    /* STM32F4xx HAL library initialization:
        - Configure the Flash prefetch, instruction and Data caches
        - Configure the Systick to generate an interrupt each 1 msec
        - Set NVIC Group Priority to 4
        - Global MSP (MCU Support Package) initialization
        */
    HAL_Init();

    printf("hello,world\r\n");

    /* Configure LED3 and LED4 */
    BSP_LED_Init(LED3);
    BSP_LED_Init(LED4);

    /* Configure the system clock to 180 MHz */
    //SystemClock_Config();

    /*##-1- Configure the UART peripheral ######################################*/
    /* Put the USART peripheral in the Asynchronous mode (UART Mode) */
    /* UART1 configured as follow:
        - Word Length = 8 Bits
        - Stop Bit = One Stop bit
        - Parity = None
        - BaudRate = 9600 baud
        - Hardware flow control disabled (RTS and CTS signals) */
    UartHandle.Instance          = USARTx;

    UartHandle.Init.BaudRate     = 115200;
    UartHandle.Init.WordLength   = UART_WORDLENGTH_8B;
    UartHandle.Init.StopBits     = UART_STOPBITS_1;
    UartHandle.Init.Parity       = UART_PARITY_NONE;
    UartHandle.Init.HwFlowCtl    = UART_HWCONTROL_NONE;
    UartHandle.Init.Mode         = UART_MODE_TX_RX;
    UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;

    if(HAL_UART_Init(&UartHandle) != HAL_OK)
    {
        Error_Handler();
    }

    xTaskCreate(vTaskLED3, "vTaskLED3", 512, NULL, 2, &xHandleTaskLED3 );

    xTaskCreate(vTaskLED4, "vTaskLED4", 512, NULL, 2, &xHandleTaskLED4 );

    vTaskStartScheduler();

    /* Infinite loop */
    while (1)
    {
    }


#if 0
#ifdef TRANSMITTER_BOARD

  /* Configure USER Button */
  BSP_PB_Init(BUTTON_KEY, BUTTON_MODE_GPIO);

    while(1)
    {
        while (BSP_PB_GetState(BUTTON_KEY) == RESET)
        {
        }

        /* The board sends the message and expects to receive it back */

        /*##-2- Start the transmission process #####################################*/
        /* While the UART in reception process, user can transmit data through
        "aTxBuffer" buffer */
        if(HAL_UART_Transmit(&UartHandle, (uint8_t*)aTxBuffer, TXBUFFERSIZE, 5000)!= HAL_OK)
        {
          Error_Handler();
        }

        /* Turn LED3 on: Transfer in transmission process is correct */
        /* then Off for next transmission */
        BSP_LED_On(LED3);
        HAL_Delay(200);
        BSP_LED_Off(LED3);

        printf("hello,world\r\n");
    }

  /* Wait for USER Button press before starting the Communication */
  while (BSP_PB_GetState(BUTTON_KEY) == RESET)
  {
  }

  /* The board sends the message and expects to receive it back */

  /*##-2- Start the transmission process #####################################*/
  /* While the UART in reception process, user can transmit data through
     "aTxBuffer" buffer */
  if(HAL_UART_Transmit(&UartHandle, (uint8_t*)aTxBuffer, TXBUFFERSIZE, 5000)!= HAL_OK)
  {
    Error_Handler();
  }

  /* Turn LED3 on: Transfer in transmission process is correct */
  /* then Off for next transmission */
  BSP_LED_On(LED3);
  HAL_Delay(200);
  BSP_LED_Off(LED3);

  /*##-3- Put UART peripheral in reception process ###########################*/
  if(HAL_UART_Receive(&UartHandle, (uint8_t *)aRxBuffer, RXBUFFERSIZE, 5000) != HAL_OK)
  {
    Error_Handler();
  }

  /* Turn LED3 on: Transfer in transmission process is correct */
  /* then Off for next transmission */
  BSP_LED_On(LED3);
  HAL_Delay(200);
  BSP_LED_Off(LED3);

#else

  /* The board receives the message and sends it back */

  /*##-2- Put UART peripheral in reception process ###########################*/
  if(HAL_UART_Receive(&UartHandle, (uint8_t *)aRxBuffer, RXBUFFERSIZE, 5000) != HAL_OK)
  {
    Error_Handler();
  }

  /* Turn LED3 on: Transfer in transmission process is correct */
  /* then Off for next transmission */
  BSP_LED_On(LED3);
  HAL_Delay(200);
  BSP_LED_Off(LED3);

  /*##-3- Start the transmission process #####################################*/
  /* While the UART in reception process, user can transmit data through
     "aTxBuffer" buffer */
  if(HAL_UART_Transmit(&UartHandle, (uint8_t*)aTxBuffer, TXBUFFERSIZE, 5000)!= HAL_OK)
  {
    Error_Handler();
  }

  /* Turn LED3 on: Transfer in transmission process is correct */
  /* then Off for next transmission */
  BSP_LED_On(LED3);
  HAL_Delay(200);
  BSP_LED_Off(LED3);

#endif /* TRANSMITTER_BOARD */

  /*##-4- Compare the sent and received buffers ##############################*/
  if(Buffercmp((uint8_t*)aTxBuffer,(uint8_t*)aRxBuffer,RXBUFFERSIZE))
  {
    Error_Handler();
  }

  /* Infinite loop */
  while (1)
  {
    /* Toggle LED3 */
    BSP_LED_Toggle(LED3);

    /* Wait for 40ms */
    HAL_Delay(40);
  }
#endif
}

static void vTaskLED3(void *pvParameters)
{
    while(1)
    {
        BSP_LED_Toggle(LED3);
        printf("I am in Task LED3\r\n");
        vTaskDelay(250/portTICK_PERIOD_MS);
    }
}

static void vTaskLED4(void *pvParameters)
{
    while(1)
    {
        BSP_LED_Toggle(LED4);
        printf("I am in Task LED4\r\n");
        vTaskDelay(100/portTICK_PERIOD_MS);
    }
}

/**
  * @brief  System Clock Configuration
  *         The system Clock is configured as follow :
  *            System Clock source            = PLL (HSE)
  *            SYSCLK(Hz)                     = 180000000
  *            HCLK(Hz)                       = 180000000
  *            AHB Prescaler                  = 1
  *            APB1 Prescaler                 = 4
  *            APB2 Prescaler                 = 2
  *            HSE Frequency(Hz)              = 8000000
  *            PLL_M                          = 8
  *            PLL_N                          = 360
  *            PLL_P                          = 2
  *            PLL_Q                          = 7
  *            VDD(V)                         = 3.3
  *            Main regulator output voltage  = Scale1 mode
  *            Flash Latency(WS)              = 5
  * @param  None
  * @retval None
  */
static void SystemClock_Config(void)
{
    RCC_ClkInitTypeDef RCC_ClkInitStruct;
    RCC_OscInitTypeDef RCC_OscInitStruct;

    /* Enable Power Control clock */
    __HAL_RCC_PWR_CLK_ENABLE();

    /* The voltage scaling allows optimizing the power consumption when the device is
        clocked below the maximum system frequency, to update the voltage scaling value
        regarding system frequency refer to product datasheet.  */
    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

    /* Enable HSE Oscillator and activate PLL with HSE as source */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = 8;
    RCC_OscInitStruct.PLL.PLLN = 360;
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
    RCC_OscInitStruct.PLL.PLLQ = 7;
    if(HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        Error_Handler();
    }

    /* Activate the Over-Drive mode */
    HAL_PWREx_EnableOverDrive();

    /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
        clocks dividers */
    RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
    if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
    {
        Error_Handler();
    }
}

/**
  * @brief  UART error callbacks
  * @param  UartHandle: UART handle
  * @note   This example shows a simple way to report transfer error, and you can
  *         add your own implementation.
  * @retval None
  */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *UartHandle)
{
  /* Turn LED4 on: Transfer error in reception/transmission process */
  BSP_LED_On(LED4);
}

/**
  * @brief  Compares two buffers.
  * @param  pBuffer1, pBuffer2: buffers to be compared.
  * @param  BufferLength: buffer's length
  * @retval 0  : pBuffer1 identical to pBuffer2
  *         >0 : pBuffer1 differs from pBuffer2
  */
static uint16_t Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength)
{
  while (BufferLength--)
  {
    if ((*pBuffer1) != *pBuffer2)
    {
      return BufferLength;
    }
    pBuffer1++;
    pBuffer2++;
  }

  return 0;
}

/**
  * @brief  This function is executed in case of error occurrence.
  * @param  None
  * @retval None
  */
static void Error_Handler(void)
{
  /* Turn LED4 on */
  BSP_LED_On(LED4);
  while(1)
  {
  }
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif

/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
  1. main.c 文件主要创建了两个不同frequency的task。在这两个task中会吐不同的log和control不同的LED灯。


    两个不同task.png
  2. 同时,在FreeRTOSConfig.h中有特意打开systick hook, 而在main.c中有特别实作这个Tick Hook。这是因为:STM32 HAL API是要基于Tick Count去计算Timeout Error的。为了保证HAL API正常使用,特意在systick hook中call HAL_IncTick去增加STM32 HAL API所依赖的Tick Count值
    enable systick hook.png

    Tick Hook.png

    至此,APP文件下的内容已经实现。待会程序编译好后,可以看到开发板上两个LED灯以不同频率闪动,并且COM Port上可以正确收到不同LOG。
  • LinkerScripts文件夹下内容实现

LinkerScripts文件夹主要存放memory layout文件

  1. STM32Cube_FW_F4_V1.18.0\Projects\STM32F429I-Discovery\Examples\UART\UART_TwoBoards_ComPolling\SW4STM32\STM32F429I-Discovery下的 STM32F429ZITx_FLASH.ld 复制到 LinkerScripts文件夹下
    原STM32F429ZITx_FLASH.ld.png

    LinkerScripts文件夹下STM32F429ZITx_FLASH.ld.png

    至此,LinkerScripts文件夹下内容已经实现。待会编译程序的时候,Linker会根据该文件夹下的STM32F429ZITx_FLASH.ld文件去 链接 其他各个module文件build出来的目标文件和库文件 构成 可执行文件

VSCode下C环境配置

  • 完成上述整个Project程序的移植和编写以后,会发现VSCode提示很多如下的Error:


    Errors.png
  • 无需担心,点击下“小灯泡”,选择Edit "IncludePath" settings,进入到c_cpp_properties.json(见下图)配置下include path即可。


    c_cpp_properties.json.png
  • 按照下图修改c_cpp_properties.json后就不会出现上述Error了。


    图片.png

Makefile编写

Warning: 由于笔者对Makefile这块不熟悉,对CMake这些Makefile管理工具也不熟悉,所以本章所用到的Makefile文件都是笔者自己写的,所以质量比较挫,大家将就用吧!!!
整个Project的Code都准备好了以后,接下来就得写Makefile去编译整个Project了。

  • APP模块文件夹下分别创建Makefile和module.mk文件
    创建Makefile和module.mk文件.png

Makefile文件用于:把这个模块文件下的源码(.c or .s)编译出目标文件(.o)

CC=arm-none-eabi-gcc
AR=arm-none-eabi-ar

CUR_DIR = $(shell pwd)
SRCDIR  = ./src
INCDIR  = ./inc
OBJDIR  = ../out/objects
###########################################
# vpath %.c src  
CFLAGS  = -g -O0 -Wall
CFLAGS += -mthumb -mcpu=cortex-m4
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
CFLAGS += -mlittle-endian  -mthumb-interwork

CFLAGS += -I../APP/inc
CFLAGS += -I../CMSIS/Include
CFLAGS += -I../STM32F4xx_HAL_Driver/inc -I../STM32F4xx_System/inc -I../STM32F429I-Discovery/inc
CFLAGS += -I../Kernel/FreeRTOS/include -I../Kernel/FreeRTOS/portable/GCC/ARM_CM4F
CFLAGS += -I$(INCDIR)

CFLAGS += -DSTM32F429xx -DUSE_HAL_DRIVER

SRCS = $(notdir $(wildcard ${SRCDIR}/*.c))
OBJS = $(addprefix $(OBJDIR)/, $(SRCS:.c=.o))

.PHONY: all

all: $(OBJS)
$(OBJDIR)/%.o : $(SRCDIR)/%.c
    $(CC) $^ $(CFLAGS) -c -o $@

clean: 
    rm -f $(OBJS)

module.mk文件用于:告诉顶层Makefile文件在这个模块文件夹下哪些目标文件需要被链接

APP_SRCDIR = $(PWD)/APP/src
C_FILES += $(notdir $(wildcard ${APP_SRCDIR}/*.c))
  • Kernel模块文件夹下分别创建Makefile和module.mk文件
    Makefile和module.mk文件.png

    Makefile文件用于:把这个模块文件下的源码(.c or .s)编译出目标文件(.o)

CC=arm-none-eabi-gcc
AR=arm-none-eabi-ar

CUR_DIR = $(shell pwd)
SRCDIR  = ./FreeRTOS
INCDIR  = ./FreeRTOS/include
OBJDIR  = ../out/objects
###########################################
# vpath %.c src  
CFLAGS  = -g -O1 -Wall
CFLAGS += -mthumb -mcpu=cortex-m4
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
CFLAGS += -mlittle-endian  -mthumb-interwork

CFLAGS += -I../APP/inc
CFLAGS += -I../CMSIS/Include
CFLAGS += -I../STM32F4xx_HAL_Driver/inc -I../STM32F4xx_System/inc -I../STM32F429I-Discovery/inc
CFLAGS += -I./FreeRTOS/portable/GCC/ARM_CM4F
CFLAGS += -I$(INCDIR)



SRCS = $(notdir $(wildcard ${SRCDIR}/*.c))
SRCS += $(notdir $(wildcard ${SRCDIR}/portable/GCC/ARM_CM4F/*.c))
SRCS += $(notdir $(wildcard ${SRCDIR}/portable/MemMang/heap_4.c))
OBJS = $(addprefix $(OBJDIR)/, $(SRCS:.c=.o))

.PHONY: all

all: $(OBJS)
$(OBJDIR)/%.o : $(SRCDIR)/%.c
    $(CC) $^ $(CFLAGS) -c -o $@

$(OBJDIR)/%.o : $(SRCDIR)/portable/GCC/ARM_CM4F/%.c
    $(CC) $^ $(CFLAGS) -c -o $@

$(OBJDIR)/heap_4.o : $(SRCDIR)/portable/MemMang/heap_4.c
    $(CC) $^ $(CFLAGS) -c -o $@    

clean: 
    rm -f $(OBJS)

module.mk文件用于:告诉顶层Makefile文件在这个模块文件夹下哪些目标文件需要被链接

C_FILES += event_groups.c
C_FILES += list.c
C_FILES += queue.c
C_FILES += tasks.c
C_FILES += timers.c
C_FILES += port.c
C_FILES += heap_4.c
  • Startup模块文件夹下分别创建Makefile和module.mk文件
    Makefile和module.mk文件.png

    Makefile文件用于:把这个模块文件下的源码(.c or .s)编译出目标文件(.o)

CC=arm-none-eabi-gcc
AR=arm-none-eabi-ar
AS=arm-none-eabi-as

CUR_DIR = $(shell pwd)
SRCDIR  = ./gcc
INCDIR  = ./
OBJDIR  = ../out/objects
###########################################
# vpath %.c src  
CFLAGS  = -g -O1 -Wall
CFLAGS += -mthumb -mcpu=cortex-m4
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
CFLAGS += -mlittle-endian  -mthumb-interwork
# CFLAGS += -ffreestanding -nostdlib
# CFLAGS += -DSTM32F429_439xx -DUSE_STDPERIPH_DRIVER
# CFLAGS += -I../STM32F4xx_StdPeriph_Driver/inc -I../STM32F4xx/ -I../CMSIS/Include -I../STM32F429I-Discovery
# CFLAGS += -I../Include
# CFLAGS += -I../FreeRTOS/include -I../FreeRTOS/portable/GCC/ARM_CM4F
ASFLAGS = 

SRCS = $(notdir $(wildcard ${SRCDIR}/*.s))
OBJS = $(addprefix $(OBJDIR)/, $(SRCS:.s=.o))

.PHONY: all

all: $(OBJS)
$(OBJDIR)/%.o : $(SRCDIR)/%.s
    $(AS) $^ $(ASFLAGS) -o $@

clean: 
    rm -f $(OBJS)

module.mk文件用于:告诉顶层Makefile文件在这个模块文件夹下哪些目标文件需要被链接

# S_FILES += startup_stm32f429_439xx.s
STARTUP_SRCDIR = $(PWD)/Startup/gcc
S_FILES += $(notdir $(wildcard $(STARTUP_SRCDIR)/*.s))
  • STM32F4xx_HAL_Driver模块文件夹下分别创建Makefile和module.mk文件
    Makefile和module.mk文件.png

    Makefile文件用于:把这个模块文件下的源码(.c or .s)编译出目标文件(.o)

CC=arm-none-eabi-gcc
AR=arm-none-eabi-ar

CUR_DIR = $(shell pwd)
SRCDIR  = ./src
INCDIR  = ./inc
OBJDIR  = ../out/objects
###########################################
# vpath %.c src  
CFLAGS  = -g -O0 -Wall
CFLAGS += -mthumb -mcpu=cortex-m4
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
CFLAGS += -mlittle-endian  -mthumb-interwork

CFLAGS += -I../CMSIS/Include
CFLAGS += -I../STM32F4xx_HAL_Driver/inc -I../STM32F4xx_System/inc -I../STM32F429I-Discovery/inc
CFLAGS += -I$(INCDIR) -I$(INCDIR)/Legacy
CFLAGS += -I../APP/inc

CFLAGS += -DSTM32F429xx -DUSE_HAL_DRIVER

SRCS = $(notdir $(wildcard ${SRCDIR}/*.c))
OBJS = $(addprefix $(OBJDIR)/, $(SRCS:.c=.o))

.PHONY: all

all: $(OBJS)
$(OBJDIR)/%.o : $(SRCDIR)/%.c
    $(CC) $^ $(CFLAGS) -c -o $@

clean: 
    rm -f $(OBJS)

module.mk文件用于:告诉顶层Makefile文件在这个模块文件夹下哪些目标文件需要被链接

STM32F4XX_HAL_SRCDIR = $(PWD)/STM32F4xx_HAL_Driver/src
C_FILES += $(notdir $(wildcard ${STM32F4XX_HAL_SRCDIR}/*.c))
  • STM32F4xx_System模块文件夹下分别创建Makefile和module.mk文件
    创建Makefile和module.mk文件.png

    Makefile文件用于:把这个模块文件下的源码(.c or .s)编译出目标文件(.o)

CC=arm-none-eabi-gcc
AR=arm-none-eabi-ar

CUR_DIR = $(shell pwd)
SRCDIR  = ./src
INCDIR  = ./inc
OBJDIR  = ../out/objects
###########################################
# vpath %.c src  
CFLAGS  = -g -O0 -Wall
CFLAGS += -mthumb -mcpu=cortex-m4
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
CFLAGS += -mlittle-endian  -mthumb-interwork

CFLAGS += -I../CMSIS/Include
CFLAGS += -I../STM32F4xx_HAL_Driver/inc -I../STM32F4xx_System/inc -I../STM32F429I-Discovery/inc
CFLAGS += -I$(INCDIR)
CFLAGS += -I../APP/inc

CFLAGS += -DSTM32F429xx -DUSE_HAL_DRIVER

SRCS = $(notdir $(wildcard ${SRCDIR}/*.c))
OBJS = $(addprefix $(OBJDIR)/, $(SRCS:.c=.o))

.PHONY: all

all: $(OBJS)
$(OBJDIR)/%.o : $(SRCDIR)/%.c
    $(CC) $^ $(CFLAGS) -c -o $@

clean: 
    rm -f $(OBJS)

module.mk文件用于:告诉顶层Makefile文件在这个模块文件夹下哪些目标文件需要被链接

STM32F4XX_SYSTEM_SRCDIR = $(PWD)/STM32F4xx_System/src
C_FILES += $(notdir $(wildcard ${STM32F4XX_SYSTEM_SRCDIR}/*.c))
  • STM32F429I-Discovery模块文件夹下分别创建Makefile和module.mk文件
    Makefile和module.mk文件.png

    Makefile文件用于:把这个模块文件下的源码(.c or .s)编译出目标文件(.o)

CC=arm-none-eabi-gcc
AR=arm-none-eabi-ar

CUR_DIR = $(shell pwd)
SRCDIR  = ./src
INCDIR  = ./inc
OBJDIR  = ../out/objects
###########################################
# vpath %.c src  
CFLAGS  = -g -O0 -Wall
CFLAGS += -mthumb -mcpu=cortex-m4
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
CFLAGS += -mlittle-endian  -mthumb-interwork

CFLAGS += -I../CMSIS/Include
CFLAGS += -I../STM32F4xx_HAL_Driver/inc -I../STM32F4xx_System/inc -I../STM32F429I-Discovery/inc
CFLAGS += -I$(INCDIR)
CFLAGS += -I../APP/inc

CFLAGS += -DSTM32F429xx -DUSE_HAL_DRIVER

SRCS = $(notdir $(wildcard ${SRCDIR}/*.c))
OBJS = $(addprefix $(OBJDIR)/, $(SRCS:.c=.o))

.PHONY: all

all: $(OBJS)
$(OBJDIR)/%.o : $(SRCDIR)/%.c
    $(CC) $^ $(CFLAGS) -c -o $@

clean: 
    rm -f $(OBJS)

module.mk文件用于:告诉顶层Makefile文件在这个模块文件夹下哪些目标文件需要被链接

STM32F4XX_DISCOVERY_SRCDIR = $(PWD)/STM32F429I-Discovery/src
C_FILES += $(notdir $(wildcard ${STM32F4XX_DISCOVERY_SRCDIR}/*.c))
  • 在Project rootpath下创建一个顶层Makefile文件(代码见下)
    顶层Makefile文件.png

    顶层Makefile文件用于:1)进入到各个模块文件夹下编译出目标文件 2)链接所有需要使用的目标文件和库文件生产可执行文件 3)把可执行文件转换成dis文件、map文件、bin文件和hex文件

PWD= $(shell pwd)
PROJ_NAME  = HAL_demo
OUTPATH = out
OBJPATH = objects
LOGPATH = logs
LIBPATH := ./lib

CC=arm-none-eabi-gcc
AS=arm-none-eabi-as
LD=arm-none-eabi-ld
AR=arm-none-eabi-ar
OBJCOPY=arm-none-eabi-objcopy
OBJDUMP=arm-none-eabi-objdump

CFLAGS  = -g -O2 -Wall
CFLAGS += -mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16
CFLAGS += -mlittle-endian  -mthumb-interwork
CFLAGS += --specs=nosys.specs

CFLAGS += -DSTM32F429xx -DUSE_HAL_DRIVER

CFLAGS += -I../CMSIS/Include
CFLAGS += -I../STM32F4xx_HAL_Driver/inc -I../STM32F4xx_System/inc -I../STM32F429I-Discovery/inc
CFLAGS += -I../APP/inc

# LDFLAGS = --specs=nano.specs -lnosys -nostartfiles
# LDFLAGS += -Wl,-wrap=malloc -Wl,-wrap=calloc -Wl,-wrap=realloc -Wl,-wrap=free
# LDFLAGS += -Wl,-T./LinkerScripts/STM32F429ZI_FLASH.ld -Wl,--gc-sections
LDFLAGS += -nostartfiles --gc-sections
# LDFLAGS += --gc-sections

LDFLAGS += -L$(LIBPATH)/v7e-m/fpv4-sp/hard -lc
LDFLAGS += -L$(LIBPATH)/v7e-m/fpv4-sp/hard -lg
# LDFLAGS += -L$(LIBPATH)/arm-none-eabi/6.3.1/hard -lgcc
LDFLAGS += -L$(LIBPATH)/arm-none-eabi/6.3.1/thumb/v7e-m/fpv4-sp/hard -lgcc
LDFLAGS += -T./LinkerScripts/STM32F429ZI_FLASH.ld
LDFLAGS += -Map=$(OUTPATH)/$(PROJ_NAME).map

C_FILES :=
S_FILES :=

include ./APP/module.mk
include ./Kernel/module.mk
include ./Startup/module.mk
include ./STM32F4xx_HAL_Driver/module.mk
include ./STM32F4xx_System/module.mk
include ./STM32F429I-Discovery/module.mk

C_OBJS := $(addprefix $(OUTPATH)/$(OBJPATH)/, $(C_FILES:.c=.o))
S_OBJS += $(addprefix $(OUTPATH)/$(OBJPATH)/, $(S_FILES:.s=.o))
LIB_OBJS := libc.a

subdirs :=
subdirs += ./Startup ./STM32F4xx_System ./STM32F4xx_HAL_Driver ./STM32F429I-Discovery ./Kernel ./APP

all:
    @mkdir $(OUTPATH)
    @mkdir $(OUTPATH)/$(OBJPATH)
    @mkdir $(OUTPATH)/$(LOGPATH)
    @echo building $(S_OBJS) $(C_OBJS) $(LIB_OBJS)
    for d in $(subdirs); do make -C $$d || exit 1; done
    # for d in $(subdirs); do make -C $$d || exit 1; done > $(OUTPATH)/$(LOGPATH)/build.log
    @echo building $(OUTPATH)/$(PROJ_NAME).elf
    $(LD) $(C_OBJS) $(S_OBJS) $(LDFLAGS) -o $(OUTPATH)/$(PROJ_NAME).elf
    @$(OBJDUMP) -D $(OUTPATH)/$(PROJ_NAME).elf > $(OUTPATH)/$(PROJ_NAME).dis
    @$(OBJCOPY) -O ihex $(OUTPATH)/$(PROJ_NAME).elf $(OUTPATH)/$(PROJ_NAME).hex
    @$(OBJCOPY) -O binary $(OUTPATH)/$(PROJ_NAME).elf $(OUTPATH)/$(PROJ_NAME).bin
    @echo Done 

clean:
    @echo clean $(S_OBJS) $(C_OBJS) $(LIB_OBJS)
    for d in $(subdirs); do make -C $$d $@; done
    @echo cleaning $(OUTPATH)/$(PROJ_NAME).elf
    @rm -rf $(OUTPATH)

至此,整个Makefile系统已经搭建完毕。在Terminal(Cygwin)下输入make指令就可以build code了。

VSCode下Terminal配置

VSCode是带有Terminal交换面板的。在Windows下,VSCode默认使用的terminal是cmd或者powershell。由于Cygwin更好使用,所有得把VSCode默认的terminal改成Cygwin
具体修改方式如下:

  • 编辑系统环境变量,把{Cygwin安装路径}\bin 加到PATH里面
    编辑系统环境变量.png
  • 进入到用户设置界面,然后把terminal.integrated.shell.windows 设置为 按照好的Cygwin的bash.exe
    修改Terminal.png
  • 修改好Terminal后,按下 “Ctrl + ` ” 快捷键即可发现默认的终端已经成功切换为Cygwin了


    Cygwin Terminal.png

编译程序

  • gcc下编译过程如下图所示:

.c文件 经过 arm-none-eabi-gcc 编译成 .o文件
.s文件 经过 arm-none-eabi-as 编译成 .o文件
.o文件 和 .a文件 经过 arm-none-eabi-ld 链接成 .elf文件
.elf文件 经过 arm-none-eabi-objcopy 和 arm-none-eabi-objdump 转换成 hex文件/dis文件
arm-none-eabi-gdb 使用 .elf文件 进行debug

gcc.png
  • 在Terminal下输入make指令,即会开始build程序

    make指令.png

    编译过程.png

  • 编译完成后,生成的所有文件都会放在out文件夹下

    out文件夹.png

  • 如果改了source code,需要重新编译程序,那就得先执行make clean指令去删除之前build好的所有东西。然后重新执行make指令即可

程序烧写和运行

完成上面所有步骤,可以得到hex文件。我们只需要把hex文件download到STM32F429内部的FLASH上,就可以执行程序查看效果了。

  • 程序烧写
  1. 把板子连接上电脑


    ST-Link Debug.png
  2. 打开STM32 ST-LINK Utility,连上ST-Link


    连上ST-Link.png
  3. 擦除FLASH上所有数据


    擦除FLASH上所有数据.png
  4. 选择out文件夹下的hex文件,download到FLASH里面


    download.png

    至此,程序已经成功download到FLASH上了。

  • 程序运行
  1. 查看LED等闪烁


    IMG_20180222_215111.jpg
  2. 查看串口LOG


    LOG.png

本章总结

本章的主要目的:在VSCode下搭建编译环境

  • 架构Project搭建
  • 移植和编写各个模块文件夹下的程序
  • Makefile编译
  • 编译程序
  • 程序烧写和运行

在下一章中,会向大家介绍如何在VSCode下搭建调试环境

推荐阅读更多精彩内容