Ardupilot chibios编译,启动,main函数学习(2)

目录

文章目录

  • 目录
  • 摘要
  • 1. Chibios编译过程
  • 2. Chibios启动代码
    • 1.startup_stm32f7xx.mk文件
      • 1.crt1.c
        • 1.内核初始化函数
        • 2.__early_init初始化函数
        • 3.__late_init函数
          • 1. halInit()函数
          • 2. chSysInit()函数
          • 3. malloc_init()函数
          • 4. setup_usb_strings()函数
        • 4.内存初始化函数
      • 2.汇编文件
        • 1.crt0_v7m.S核心启动文件
        • 2.vectors.S中断向量表
  • 3. Chibios run()函数学习
    • 1.HAL_ChibiOS的run()函数
    • 2.HAL_ChibiOS的main_loop()函数
    • 3.HAL_ChibiOS的init()函数
      • 1.信号量创建函数
      • 2.定时器进程
      • 3.遥控器输入进程
      • 4. io进程
      • 5. 存储进程
    • 4. g_callbacks->setup()函数
    • 5. g_callbacks->loop()函数
  • 4.后续分析要点:
    • 1.waf怎么编译
    • 2.STM32F1协处理器与STM32F7主处理器怎么建立联系?
    • 3.Bootloader怎么识别更新固件代码?
    • 4.固件代码的起始地址是多少?
    • 5.反汇编代码

摘要



本节继续研究Chibios,欢迎批评指正!!!



1. Chibios编译过程



这里我使用的是ubuntu系统,编译代码用的是gcc-arm-none-eabi-5_4-2016q3,在进入代码分析之前我的疑惑


1.我看gcc-arm-none-eabi-5_4就是不顺眼,这是什么鬼?
答;这鬼是一种交叉编译工具,更详细的是下面内容

Ardupilot chibios编译,启动,main函数学习(2)_第1张图片
Ardupilot chibios编译,启动,main函数学习(2)_第2张图片

还要注意:GCC(GNU Compiler Collection,GNU编译器套件),是由 GNU 开发的编程语言编译器。
我们采用的编辑器是gcc,编辑的对象类型满足arm-none-eabi

# ARM Cortex-Mx common makefile scripts and rules.
#ARM Cortex-Mx  通用的的文件文件脚本和规则。
##############################################################################
# Processing options coming from the upper Makefile.
# 处理选项来自上面的Makefile文件

# Compiler options
OPT    := $(USE_OPT)
COPT   := $(USE_COPT)
CPPOPT := $(USE_CPPOPT)

# Garbage collection
ifeq ($(USE_LINK_GC),yes)
  OPT   += -ffunction-sections -fdata-sections -fno-common
  LDOPT := ,--gc-sections
else
  LDOPT :=
endif

# Linker extra options
ifneq ($(USE_LDOPT),)
  LDOPT := $(LDOPT),$(USE_LDOPT)
endif

# Link time optimizations
ifeq ($(USE_LTO),yes)
  OPT += -flto
endif

# FPU options default (Cortex-M4 and Cortex-M7 single precision).
ifeq ($(USE_FPU_OPT),)
  USE_FPU_OPT = -mfloat-abi=$(USE_FPU) -mfpu=fpv4-sp-d16
endif

# FPU-related options
ifeq ($(USE_FPU),)
  USE_FPU = no
endif
ifneq ($(USE_FPU),no)
  OPT    += $(USE_FPU_OPT)
  DDEFS  += -DCORTEX_USE_FPU=TRUE
  DADEFS += -DCORTEX_USE_FPU=TRUE
else
  DDEFS  += -DCORTEX_USE_FPU=FALSE
  DADEFS += -DCORTEX_USE_FPU=FALSE
endif

# Process stack size
ifeq ($(USE_PROCESS_STACKSIZE),)
  LDOPT := $(LDOPT),--defsym=__process_stack_size__=0x400
else
  LDOPT := $(LDOPT),--defsym=__process_stack_size__=$(USE_PROCESS_STACKSIZE)
endif

# Exceptions stack size
ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
  LDOPT := $(LDOPT),--defsym=__main_stack_size__=0x400
else
  LDOPT := $(LDOPT),--defsym=__main_stack_size__=$(USE_EXCEPTIONS_STACKSIZE)
endif

# Output directory and files
ifeq ($(BUILDDIR),)
  BUILDDIR = build
endif
ifeq ($(BUILDDIR),.)
  BUILDDIR = build
endif

# Dependencies directory
ifeq ($(DEPDIR),)
  DEPDIR = .dep
endif
ifeq ($(DEPDIR),.)
  DEPDIR = .dep
endif

OUTFILES := $(BUILDDIR)/$(PROJECT).elf \
            $(BUILDDIR)/$(PROJECT).hex \
            $(BUILDDIR)/$(PROJECT).bin \
            $(BUILDDIR)/$(PROJECT).dmp \
            $(BUILDDIR)/$(PROJECT).list

ifdef SREC
  OUTFILES += $(BUILDDIR)/$(PROJECT).srec
endif

# Source files groups and paths
ifeq ($(USE_THUMB),yes)
  TCSRC   += $(CSRC)
  TCPPSRC += $(CPPSRC)
else
  ACSRC   += $(CSRC)
  ACPPSRC += $(CPPSRC)
endif
ASRC      := $(ACSRC) $(ACPPSRC)
TSRC      := $(TCSRC) $(TCPPSRC)
SRCPATHS  := $(sort $(dir $(ASMXSRC)) $(dir $(ASMSRC)) $(dir $(ASRC)) $(dir $(TSRC)))

# Various directories
OBJDIR    := $(BUILDDIR)/obj
LSTDIR    := $(BUILDDIR)/lst

# Object files groups
ACOBJS    := $(addprefix $(OBJDIR)/, $(notdir $(ACSRC:.c=.o)))
ACPPOBJS  := $(addprefix $(OBJDIR)/, $(notdir $(ACPPSRC:.cpp=.o)))
TCOBJS    := $(addprefix $(OBJDIR)/, $(notdir $(TCSRC:.c=.o)))
TCPPOBJS  := $(addprefix $(OBJDIR)/, $(notdir $(TCPPSRC:.cpp=.o)))
ASMOBJS   := $(addprefix $(OBJDIR)/, $(notdir $(ASMSRC:.s=.o)))
ASMXOBJS  := $(addprefix $(OBJDIR)/, $(notdir $(ASMXSRC:.S=.o)))
OBJS      := $(ASMXOBJS) $(ASMOBJS) $(ACOBJS) $(TCOBJS) $(ACPPOBJS) $(TCPPOBJS)

# Paths
IINCDIR   := $(patsubst %,-I%,$(INCDIR) $(DINCDIR) $(UINCDIR))
LLIBDIR   := $(patsubst %,-L%,$(DLIBDIR) $(ULIBDIR))
LLIBDIR   += -L$(dir $(LDSCRIPT))

# Macros
DEFS      := $(DDEFS) $(UDEFS)
ADEFS     := $(DADEFS) $(UADEFS)

# Libs
LIBS      := $(DLIBS) $(ULIBS)

# Various settings
MCFLAGS   := -mcpu=$(MCU)
ODFLAGS   = -x --syms
ASFLAGS   = $(MCFLAGS) $(OPT) -Wa,-amhls=$(LSTDIR)/$(notdir $(<:.s=.lst)) $(ADEFS)
ASXFLAGS  = $(MCFLAGS) $(OPT) -Wa,-amhls=$(LSTDIR)/$(notdir $(<:.S=.lst)) $(ADEFS)
CFLAGS    = $(MCFLAGS) $(OPT) $(COPT) $(CWARN) -Wa,-alms=$(LSTDIR)/$(notdir $(<:.c=.lst)) $(DEFS)
CPPFLAGS  = $(MCFLAGS) $(OPT) $(CPPOPT) $(CPPWARN) -Wa,-alms=$(LSTDIR)/$(notdir $(<:.cpp=.lst)) $(DEFS)
LDFLAGS   = $(MCFLAGS) $(OPT) -nostartfiles $(LLIBDIR) -Wl,-Map=$(BUILDDIR)/$(PROJECT).map,--cref,--no-warn-mismatch,--library-path=$(RULESPATH)/ld,--script=$(LDSCRIPT)$(LDOPT)

# Thumb interwork enabled only if needed because it kills performance.
ifneq ($(strip $(TSRC)),)
  CFLAGS   += -DTHUMB_PRESENT
  CPPFLAGS += -DTHUMB_PRESENT
  ASFLAGS  += -DTHUMB_PRESENT
  ASXFLAGS += -DTHUMB_PRESENT
  ifneq ($(strip $(ASRC)),)
    # Mixed ARM and THUMB mode.
    CFLAGS   += -mthumb-interwork
    CPPFLAGS += -mthumb-interwork
    ASFLAGS  += -mthumb-interwork
    ASXFLAGS += -mthumb-interwork
    LDFLAGS  += -mthumb-interwork
  else
    # Pure THUMB mode, THUMB C code cannot be called by ARM asm code directly.
    CFLAGS   += -mno-thumb-interwork -DTHUMB_NO_INTERWORKING
    CPPFLAGS += -mno-thumb-interwork -DTHUMB_NO_INTERWORKING
    ASFLAGS  += -mno-thumb-interwork -DTHUMB_NO_INTERWORKING -mthumb
    ASXFLAGS += -mno-thumb-interwork -DTHUMB_NO_INTERWORKING -mthumb
    LDFLAGS  += -mno-thumb-interwork -mthumb
  endif
else
  # Pure ARM mode
  CFLAGS   += -mno-thumb-interwork
  CPPFLAGS += -mno-thumb-interwork
  ASFLAGS  += -mno-thumb-interwork
  ASXFLAGS += -mno-thumb-interwork
  LDFLAGS  += -mno-thumb-interwork
endif

# Generate dependency information
ASFLAGS  += -MD -MP -MF $(DEPDIR)/$(@F).d
ASXFLAGS += -MD -MP -MF $(DEPDIR)/$(@F).d
CFLAGS   += -MD -MP -MF $(DEPDIR)/$(@F).d
CPPFLAGS += -MD -MP -MF $(DEPDIR)/$(@F).d

# Paths where to search for sources
VPATH     = $(SRCPATHS)

#
# Makefile rules
#

all: PRE_MAKE_ALL_RULE_HOOK $(OBJS) $(OUTFILES) POST_MAKE_ALL_RULE_HOOK

PRE_MAKE_ALL_RULE_HOOK:

POST_MAKE_ALL_RULE_HOOK:

$(OBJS): | $(BUILDDIR) $(OBJDIR) $(LSTDIR) $(DEPDIR)

$(BUILDDIR):
ifneq ($(USE_VERBOSE_COMPILE),yes)
	@echo Compiler Options
	@echo $(CC) -c $(CFLAGS) -I. $(IINCDIR) main.c -o main.o
	@echo
endif
	@mkdir -p $(BUILDDIR)

$(OBJDIR):
	@mkdir -p $(OBJDIR)

$(LSTDIR):
	@mkdir -p $(LSTDIR)

$(DEPDIR):
	@mkdir -p $(DEPDIR)

$(ACPPOBJS) : $(OBJDIR)/%.o : %.cpp $(MAKEFILE_LIST)
ifeq ($(USE_VERBOSE_COMPILE),yes)
	@echo
	$(CPPC) -c $(CPPFLAGS) $(AOPT) -I. $(IINCDIR) $< -o $@
else
	@echo Compiling $( $@
	$(SZ) $<
else
	@echo Creating $@
	@$(OD) $(ODFLAGS) $< > $@
	@echo
	@$(SZ) $<
endif

%.list: %.elf
ifeq ($(USE_VERBOSE_COMPILE),yes)
	$(OD) -S $< > $@
else
	@echo Creating $@
	@$(OD) -S $< > $@
	@echo
	@echo Done
endif

lib: $(OBJS) $(BUILDDIR)/lib$(PROJECT).a

$(BUILDDIR)/lib$(PROJECT).a: $(OBJS)
	@$(AR) -r $@ $^
	@echo
	@echo Done

clean: CLEAN_RULE_HOOK
	@echo Cleaning
	@echo - $(DEPDIR)
	@-rm -fR $(DEPDIR)/* $(BUILDDIR)/* 2>/dev/null
	@-if [ -d "$(DEPDIR)" ]; then rmdir -p --ignore-fail-on-non-empty $(subst ./,,$(DEPDIR)) 2>/dev/null; fi
	@echo - $(BUILDDIR)
	@-if [ -d "$(BUILDDIR)" ]; then rmdir -p --ignore-fail-on-non-empty $(subst ./,,$(BUILDDIR)) 2>/dev/null; fi
	@echo
	@echo Done

CLEAN_RULE_HOOK:

#
# Include the dependency files, should be the last of the makefile
#
-include $(wildcard $(DEPDIR)/*)

# *** EOF ***

这部分还没有细致分析



2. Chibios启动代码



Ardupilot chibios编译,启动,main函数学习(2)_第3张图片



1.startup_stm32f7xx.mk文件



#CHIbios通用STM32 F7XX启动和CMSIS(微控制器软件接口标准)文件列表 ---List of the ChibiOS generic STM32F7xx startup and CMSIS files.

#变量定义
STARTUPSRC = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/crt1.c
   #变量定义       
STARTUPASM = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/crt0_v7m.S \
             $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/vectors.S
#变量定义
STARTUPINC = $(CHIBIOS)/os/common/portability/GCC \
             $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC \
             $(CHIBIOS)/os/common/startup/ARMCMx/devices/STM32F7xx \
             $(CHIBIOS)/os/common/ext/ARM/CMSIS/Core/Include \
             $(CHIBIOS)/os/common/ext/ST/STM32F7xx
#变量定义
STARTUPLD  = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/ld

#共享变量---- Shared variables
ALLXASMSRC += $(STARTUPASM)  //汇编文件目录
ALLCSRC    += $(STARTUPSRC)      //源文件目录
ALLINC     += $(STARTUPINC)          //头文件包含

1.crt1.c

定义启动代码时,需要的一些函数,这些函数在汇编中会跳转过来

STARTUPSRC = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/crt1.c #定义一个源文件
/**
 * @file    ARMCMx/compilers/GCC/crt1.c
 * @brief   Startup stub functions.
 *
 * @addtogroup ARMCMx_GCC_STARTUP
 * @{
 */

#include 
#include 

#include "cmparams.h"

/*===========================================================================*/
/*模块局部定义---- Module local definitions.                                                 */
/*===========================================================================*/

#if !defined(CRT1_AREAS_NUMBER) || defined(__DOXYGEN__)
#define CRT1_AREAS_NUMBER                   8
#endif

#if (CRT1_AREAS_NUMBER < 0) || (CRT1_AREAS_NUMBER > 8)
#error "CRT1_AREAS_NUMBER must be within 0 and 8"
#endif

/*===========================================================================*/
/* 模块导出变量。----Module exported variables.                                                */
/*===========================================================================*/

/*===========================================================================*/
/* 模块本地类型。---Module local types.                                                       */
/*===========================================================================*/

/**
 * @brief   要初始化的区域的类型。----Type of an area to be initialized.
 */
typedef struct
{
  uint32_t              *init_text_area;
  uint32_t              *init_area;
  uint32_t              *clear_area;
  uint32_t              *no_init_area;
} ram_init_area_t;

/*===========================================================================*/
/*局部变量的模块。--- Module local variables.                                                   */
/*===========================================================================*/

#if (CRT1_AREAS_NUMBER > 0) || defined(__DOXYGEN__)
extern uint32_t __ram0_init_text__, __ram0_init__, __ram0_clear__, __ram0_noinit__;
#endif
#if (CRT1_AREAS_NUMBER > 1) || defined(__DOXYGEN__)
extern uint32_t __ram1_init_text__, __ram1_init__, __ram1_clear__, __ram1_noinit__;
#endif
#if (CRT1_AREAS_NUMBER > 2) || defined(__DOXYGEN__)
extern uint32_t __ram2_init_text__, __ram2_init__, __ram2_clear__, __ram2_noinit__;
#endif
#if (CRT1_AREAS_NUMBER > 3) || defined(__DOXYGEN__)
extern uint32_t __ram3_init_text__, __ram3_init__, __ram3_clear__, __ram3_noinit__;
#endif
#if (CRT1_AREAS_NUMBER > 4) || defined(__DOXYGEN__)
extern uint32_t __ram4_init_text__, __ram4_init__, __ram4_clear__, __ram4_noinit__;
#endif
#if (CRT1_AREAS_NUMBER > 5) || defined(__DOXYGEN__)
extern uint32_t __ram5_init_text__, __ram5_init__, __ram5_clear__, __ram5_noinit__;
#endif
#if (CRT1_AREAS_NUMBER > 6) || defined(__DOXYGEN__)
extern uint32_t __ram6_init_text__, __ram6_init__, __ram6_clear__, __ram6_noinit__;
#endif
#if (CRT1_AREAS_NUMBER > 7) || defined(__DOXYGEN__)
extern uint32_t __ram7_init_text__, __ram7_init__, __ram7_clear__, __ram7_noinit__;
#endif

/**
 * @brief  要初始化的区域的静态表。---- Static table of areas to be initialized.
 */
#if (CRT1_AREAS_NUMBER > 0) || defined(__DOXYGEN__)
static const ram_init_area_t ram_areas[CRT1_AREAS_NUMBER] = {
  {&__ram0_init_text__, &__ram0_init__, &__ram0_clear__, &__ram0_noinit__},
#if (CRT1_AREAS_NUMBER > 1) || defined(__DOXYGEN__)
  {&__ram1_init_text__, &__ram1_init__, &__ram1_clear__, &__ram1_noinit__},
#endif
#if (CRT1_AREAS_NUMBER > 2) || defined(__DOXYGEN__)
  {&__ram2_init_text__, &__ram2_init__, &__ram2_clear__, &__ram2_noinit__},
#endif
#if (CRT1_AREAS_NUMBER > 3) || defined(__DOXYGEN__)
  {&__ram3_init_text__, &__ram3_init__, &__ram3_clear__, &__ram3_noinit__},
#endif
#if (CRT1_AREAS_NUMBER > 4) || defined(__DOXYGEN__)
  {&__ram4_init_text__, &__ram4_init__, &__ram4_clear__, &__ram4_noinit__},
#endif
#if (CRT1_AREAS_NUMBER > 5) || defined(__DOXYGEN__)
  {&__ram5_init_text__, &__ram5_init__, &__ram5_clear__, &__ram5_noinit__},
#endif
#if (CRT1_AREAS_NUMBER > 6) || defined(__DOXYGEN__)
  {&__ram6_init_text__, &__ram6_init__, &__ram6_clear__, &__ram6_noinit__},
#endif
#if (CRT1_AREAS_NUMBER > 7) || defined(__DOXYGEN__)
  {&__ram7_init_text__, &__ram7_init__, &__ram7_clear__, &__ram7_noinit__},
#endif
};
#endif

/*===========================================================================*/
/* Module local functions.                                                   */
/*===========================================================================*/

/*===========================================================================*/
/* Module exported functions.                                                */
/*===========================================================================*/

/**
 * @brief   与体系结构相关的核心初始化。---Architecture-dependent core initialization.
 * @details 在堆栈初始化之后和在数据和BSS段初始化之前立即调用此钩子。This hook is invoked immediately after the stack initialization
 *          。---and before the DATA and BSS segments initialization.
 * @note    注意:这个函数是一个弱符号。----This function is a weak symbol.
 */
#if !defined(__DOXYGEN__)
__attribute__((weak))
#endif
/*lint -save -e9075 [8.4] All symbols are invoked from asm context.*/

//内核初始化函数,会被在汇编使用
void __core_init(void)
{

#if CORTEX_MODEL == 7
  SCB_EnableICache();//使能I-cache指令缓存
  SCB_EnableDCache();//使能D-cache数据缓存。
#endif
}

/**
 * @brief   早期初始化。-----Early initialization.
 * @details 在堆栈初始化之后和在数据和BSS段初始化之前立即调用此钩子
 *                                   This hook is invoked immediately after the stack and core
 *                                   initialization and before the DATA and BSS segments
 *                                   initialization.
 * @note    注意:这个函数是一个弱符号----This function is a weak symbol.
 */
#if !defined(__DOXYGEN__)
__attribute__((weak))
#endif
/*lint -save -e9075 [8.4] All symbols are invoked from asm context.*/
#ifndef _ARDUPILOT_
void __early_init(void) {} //该函数在板层初始化完成
#endif
/*lint -restore*/

/**
 * @brief   后期初始化函数Late initialization.
 * @details This hook is invoked after the DATA and BSS segments
 *          initialization and before any static constructor. The
 *          default behavior is to do nothing.
 * @note    This function is a weak symbol.
 */
#if !defined(__DOXYGEN__)
__attribute__((weak))
#endif
/*lint -save -e9075 [8.4] All symbols are invoked from asm context.*/
#ifndef _ARDUPILOT_
void __late_init(void) {}//该函数在板层初始化完成
#endif
/*lint -restore*/

/**
 * @brief   Default @p main() function exit handler.
 * @details This handler is invoked or the @p main() function exit. The
 *          default behavior is to enter an infinite loop.
 * @note    This function is a weak symbol.
 */
#if !defined(__DOXYGEN__)
__attribute__((noreturn, weak))
#endif
/*lint -save -e9075 [8.4] All symbols are invoked from asm context.*/
void __default_exit(void) 
{
/*lint -restore*/

  while (true) 
  {
  }
}

/**
 * @brief  执行各种RAM区域的初始化汇编中使用--- Performs the initialization of the various RAM areas.
 */
void __init_ram_areas(void) 
{
#if CRT1_AREAS_NUMBER > 0
  const ram_init_area_t *rap = ram_areas;

  do {
    uint32_t *tp = rap->init_text_area;
    uint32_t *p = rap->init_area;

    /* Copying initialization data.*/
    while (p < rap->clear_area) 
    {
      *p = *tp;
      p++;
      tp++;
    }

    /* Zeroing clear area.*/
    while (p < rap->no_init_area) 
    {
      *p = 0;
      p++;
    }
    rap++;
  }
  while (rap < &ram_areas[CRT1_AREAS_NUMBER]);
#endif
}

/** @} */



上述文件需要注意的函数:



1.内核初始化函数



//内核初始化函数,会被在汇编使用
void __core_init(void)
{

#if CORTEX_MODEL == 7
  SCB_EnableICache();//使能I-cache指令缓存
  SCB_EnableDCache();//使能D-cache数据缓存。
#endif
}


2.__early_init初始化函数



/***********************************************************************************************************************
*函数原型:void __early_init(void)
*函数功能:早期初始化
*修改日期:2018-10-30
*修改作者:cihang_uav
*备注信息:
*************************************************************************************************************************/
void __early_init(void)
{
#ifndef STM32F100_MCUCONF //看到这应该明白了,这是那个协处理器
  stm32_gpio_init();
#endif
  stm32_clock_init();
}

函数:stm32_gpio_init()

static void stm32_gpio_init(void) 
{

  /* Enabling GPIO-related clocks, the mask comes from the
     registry header file.*/
  rccResetAHB1(STM32_GPIO_EN_MASK);
  rccEnableAHB1(STM32_GPIO_EN_MASK, true);

  /* Initializing all the defined GPIO ports.*/
#if STM32_HAS_GPIOA
  gpio_init(GPIOA, &gpio_default_config.PAData);
#endif
#if STM32_HAS_GPIOB
  gpio_init(GPIOB, &gpio_default_config.PBData);
#endif
#if STM32_HAS_GPIOC
  gpio_init(GPIOC, &gpio_default_config.PCData);
#endif
#if STM32_HAS_GPIOD
  gpio_init(GPIOD, &gpio_default_config.PDData);
#endif
#if STM32_HAS_GPIOE
  gpio_init(GPIOE, &gpio_default_config.PEData);
#endif
#if STM32_HAS_GPIOF
  gpio_init(GPIOF, &gpio_default_config.PFData);
#endif
#if STM32_HAS_GPIOG
  gpio_init(GPIOG, &gpio_default_config.PGData);
#endif
#if STM32_HAS_GPIOH
  gpio_init(GPIOH, &gpio_default_config.PHData);
#endif
#if STM32_HAS_GPIOI
  gpio_init(GPIOI, &gpio_default_config.PIData);
#endif
#if STM32_HAS_GPIOJ
  gpio_init(GPIOJ, &gpio_default_config.PJData);
#endif
#if STM32_HAS_GPIOK
  gpio_init(GPIOK, &gpio_default_config.PKData);
#endif
}

函数:stm32_clock_init(),有STM32F1和STM32F7都会使用

F1芯片时钟

void stm32_clock_init(void) 
{

#if !STM32_NO_INIT
  /* HSI setup, it enforces the reset situation in order to handle possible
     problems with JTAG probes and re-initializations.*/
  RCC->CR |= RCC_CR_HSION;                  /* Make sure HSI is ON.         */
  while (!(RCC->CR & RCC_CR_HSIRDY))
    ;                                       /* Wait until HSI is stable.    */

  /* HSI is selected as new source without touching the other fields in
     CFGR. Clearing the register has to be postponed after HSI is the
     new source.*/
  RCC->CFGR &= ~RCC_CFGR_SW;                /* Reset SW, selecting HSI.     */
  while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI)
    ;                                       /* Wait until HSI is selected.  */

  /* Registers finally cleared to reset values.*/
  RCC->CR &= RCC_CR_HSITRIM | RCC_CR_HSION; /* CR Reset value.              */
  RCC->CFGR = 0;                            /* CFGR reset value.            */

#if STM32_HSE_ENABLED
#if defined(STM32_HSE_BYPASS)
  /* HSE Bypass.*/
  RCC->CR |= RCC_CR_HSEBYP;
#endif
  /* HSE activation.*/
  RCC->CR |= RCC_CR_HSEON;
  while (!(RCC->CR & RCC_CR_HSERDY))
    ;                                       /* Waits until HSE is stable.   */
#endif

#if STM32_LSI_ENABLED
  /* LSI activation.*/
  RCC->CSR |= RCC_CSR_LSION;
  while ((RCC->CSR & RCC_CSR_LSIRDY) == 0)
    ;                                       /* Waits until LSI is stable.   */
#endif

  /* Settings of various dividers and multipliers in CFGR2.*/
  RCC->CFGR2 = STM32_PLL3MUL | STM32_PLL2MUL | STM32_PREDIV2 |
               STM32_PREDIV1 | STM32_PREDIV1SRC;

  /* PLL2 setup, if activated.*/
#if STM32_ACTIVATE_PLL2
  RCC->CR |= RCC_CR_PLL2ON;
  while (!(RCC->CR & RCC_CR_PLL2RDY))
    ;                                        /* Waits until PLL2 is stable. */
#endif

  /* PLL3 setup, if activated.*/
#if STM32_ACTIVATE_PLL3
  RCC->CR |= RCC_CR_PLL3ON;
  while (!(RCC->CR & RCC_CR_PLL3RDY))
    ;                                        /* Waits until PLL3 is stable. */
#endif

  /* PLL1 setup, if activated.*/
#if STM32_ACTIVATE_PLL1
  RCC->CFGR |= STM32_PLLMUL | STM32_PLLSRC;
  RCC->CR   |= RCC_CR_PLLON;
  while (!(RCC->CR & RCC_CR_PLLRDY))
    ;                           /* Waits until PLL1 is stable.              */
#endif

  /* Clock settings.*/
#if STM32_HAS_OTG1
  RCC->CFGR = STM32_MCOSEL | STM32_OTGFSPRE | STM32_PLLMUL | STM32_PLLSRC |
              STM32_ADCPRE | STM32_PPRE2    | STM32_PPRE1  | STM32_HPRE;
#else
  RCC->CFGR = STM32_MCO    |                  STM32_PLLMUL | STM32_PLLSRC |
              STM32_ADCPRE | STM32_PPRE2    | STM32_PPRE1  | STM32_HPRE;
#endif

  /* Flash setup and final clock selection.   */
  FLASH->ACR = STM32_FLASHBITS; /* Flash wait states depending on clock.    */

  /* Switching to the configured clock source if it is different from HSI.*/
#if (STM32_SW != STM32_SW_HSI)
  RCC->CFGR |= STM32_SW;        /* Switches on the selected clock source.   */
  while ((RCC->CFGR & RCC_CFGR_SWS) != (STM32_SW << 2))
    ;
#endif

#if !STM32_HSI_ENABLED
  RCC->CR &= ~RCC_CR_HSION;
#endif
#endif /* !STM32_NO_INIT */
}

F7芯片时钟

void stm32_clock_init(void) 
{

#if !STM32_NO_INIT
  /* PWR clock enabled.*/
#if defined(HAL_USE_RTC) && defined(RCC_APB1ENR_RTCEN)
  RCC->APB1ENR = RCC_APB1ENR_PWREN | RCC_APB1ENR_RTCEN;
#else
  RCC->APB1ENR = RCC_APB1ENR_PWREN;
#endif

  /* PWR initialization.*/
  PWR->CR1 = STM32_VOS;

  /* HSI setup, it enforces the reset situation in order to handle possible
     problems with JTAG probes and re-initializations.*/
  RCC->CR |= RCC_CR_HSION;                  /* Make sure HSI is ON.         */
  while (!(RCC->CR & RCC_CR_HSIRDY))
    ;                                       /* Wait until HSI is stable.    */

  /* HSI is selected as new source without touching the other fields in
     CFGR. Clearing the register has to be postponed after HSI is the
     new source.*/
  RCC->CFGR &= ~RCC_CFGR_SW;                /* Reset SW, selecting HSI.     */
  while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI)
    ;                                       /* Wait until HSI is selected.  */

  /* Registers finally cleared to reset values.*/
  RCC->CR &= RCC_CR_HSITRIM | RCC_CR_HSION; /* CR Reset value.              */
  RCC->CFGR = 0;                            /* CFGR reset value.            */

#if STM32_HSE_ENABLED
  /* HSE activation.*/
#if defined(STM32_HSE_BYPASS)
  /* HSE Bypass.*/
  RCC->CR |= RCC_CR_HSEON | RCC_CR_HSEBYP;
#else
  /* No HSE Bypass.*/
  RCC->CR |= RCC_CR_HSEON;
#endif
  while ((RCC->CR & RCC_CR_HSERDY) == 0)
    ;                           /* Waits until HSE is stable.               */
#endif

#if STM32_LSI_ENABLED
  /* LSI activation.*/
  RCC->CSR |= RCC_CSR_LSION;
  while ((RCC->CSR & RCC_CSR_LSIRDY) == 0)
    ;                           /* Waits until LSI is stable.               */
#endif

#if STM32_ACTIVATE_PLL
  /* PLL activation.*/
  RCC->PLLCFGR = STM32_PLLQ | STM32_PLLSRC | STM32_PLLP | STM32_PLLN |
                 STM32_PLLM;
  RCC->CR |= RCC_CR_PLLON;

  /* Synchronization with voltage regulator stabilization.*/
  while ((PWR->CSR1 & PWR_CSR1_VOSRDY) == 0)
    ;                           /* Waits until power regulator is stable.   */

#if STM32_OVERDRIVE_REQUIRED
  /* Overdrive activation performed after activating the PLL in order to save
     time as recommended in RM in "Entering Over-drive mode" paragraph.*/
  PWR->CR1 |= PWR_CR1_ODEN;
  while (!(PWR->CSR1 & PWR_CSR1_ODRDY))
      ;
  PWR->CR1 |= PWR_CR1_ODSWEN;
  while (!(PWR->CSR1 & PWR_CSR1_ODSWRDY))
      ;
#endif /* STM32_OVERDRIVE_REQUIRED */

  /* Waiting for PLL lock.*/
  while (!(RCC->CR & RCC_CR_PLLRDY))
    ;
#endif /* STM32_OVERDRIVE_REQUIRED */

#if STM32_ACTIVATE_PLLI2S
  /* PLLI2S activation.*/
  RCC->PLLI2SCFGR = STM32_PLLI2SR | STM32_PLLI2SN;
  RCC->CR |= RCC_CR_PLLI2SON;

  /* Waiting for PLL lock.*/
  while (!(RCC->CR & RCC_CR_PLLI2SRDY))
    ;
#endif

#if STM32_ACTIVATE_PLLSAI
  /* PLLSAI activation.*/
  RCC->PLLSAICFGR = STM32_PLLSAIR | STM32_PLLSAIQ | STM32_PLLSAIP |
                    STM32_PLLSAIN;
  RCC->CR |= RCC_CR_PLLSAION;

  /* Waiting for PLL lock.*/
  while (!(RCC->CR & RCC_CR_PLLSAIRDY))
    ;
#endif

  /* Other clock-related settings (dividers, MCO etc).*/
  RCC->CFGR = STM32_MCO2SEL | STM32_MCO2PRE | STM32_MCO1PRE | STM32_I2SSRC |
              STM32_MCO1SEL | STM32_RTCPRE  | STM32_PPRE2   | STM32_PPRE1  |
              STM32_HPRE;

  /* DCKCFGR1 register initialization, note, must take care of the _OFF
     pseudo settings.*/
  {
    uint32_t dckcfgr1 = STM32_PLLI2SDIVQ | STM32_PLLSAIDIVQ | STM32_PLLSAIDIVR;
#if STM32_SAI2SEL != STM32_SAI2SEL_OFF
    dckcfgr1 |= STM32_SAI2SEL;
#endif
#if STM32_SAI1SEL != STM32_SAI1SEL_OFF
    dckcfgr1 |= STM32_SAI1SEL;
#endif
    RCC->DCKCFGR1 = dckcfgr1;
  }

  /* Peripheral clock sources.*/
  RCC->DCKCFGR2 = STM32_SDMMC2SEL | STM32_SDMMC1SEL | STM32_CK48MSEL  |
                  STM32_CECSEL    | STM32_LPTIM1SEL | STM32_I2C4SEL   |
                  STM32_I2C3SEL   | STM32_I2C2SEL   | STM32_I2C1SEL   |
                  STM32_UART8SEL  | STM32_UART7SEL  | STM32_USART6SEL |
                  STM32_UART5SEL  | STM32_UART4SEL  | STM32_USART3SEL |
                  STM32_USART2SEL | STM32_USART1SEL;

  /* Flash setup.*/
  FLASH->ACR = FLASH_ACR_ARTEN | FLASH_ACR_PRFTEN | STM32_FLASHBITS;

  /* Switching to the configured clock source if it is different from HSI.*/
#if (STM32_SW != STM32_SW_HSI)
  RCC->CFGR |= STM32_SW;        /* Switches on the selected clock source.   */
  while ((RCC->CFGR & RCC_CFGR_SWS) != (STM32_SW << 2))
    ;
#endif
#endif /* STM32_NO_INIT */

  /* SYSCFG clock enabled here because it is a multi-functional unit shared
     among multiple drivers.*/
  rccEnableAPB2(RCC_APB2ENR_SYSCFGEN, true);
}


3.__late_init函数



/***********************************************************************************************************************
*函数原型:void __late_init(void)
*函数功能:后期初始化
*修改日期:2018-10-30
*修改作者:cihang_uav
*备注信息:
*************************************************************************************************************************/
void __late_init(void)
{
  halInit(); //硬件抽象层初始化
  chSysInit();//chibios系统初始化
#if CH_CFG_USE_HEAP == TRUE
  malloc_init(); //内存处理初始化
#endif
#ifdef HAL_USB_PRODUCT_ID
  setup_usb_strings(); //初始化usb字符串
#endif
}



1. halInit()函数


void halInit(void)
{

  /* 初始化OS的硬件抽象层Initializes the OS Abstraction Layer.*/
  osalInit();

  /*平台低层初始化---- Platform low level initializations.*/
  hal_lld_init();

#if (HAL_USE_PAL == TRUE) || defined(__DOXYGEN__)
#if defined(PAL_NEW_INIT)
  palInit();
#else
  palInit(&pal_default_config);
#endif
#endif
#if (HAL_USE_ADC == TRUE) || defined(__DOXYGEN__)
  adcInit();
#endif
#if (HAL_USE_CAN == TRUE) || defined(__DOXYGEN__)
  canInit();
#endif
#if (HAL_USE_CRY == TRUE) || defined(__DOXYGEN__)
  cryInit();
#endif
#if (HAL_USE_DAC == TRUE) || defined(__DOXYGEN__)
  dacInit();
#endif
#if (HAL_USE_EXT == TRUE) || defined(__DOXYGEN__)
  extInit();
#endif
#if (HAL_USE_GPT == TRUE) || defined(__DOXYGEN__)
  gptInit();
#endif
#if (HAL_USE_I2C == TRUE) || defined(__DOXYGEN__)
  i2cInit();
#endif
#if (HAL_USE_I2S == TRUE) || defined(__DOXYGEN__)
  i2sInit();
#endif
#if (HAL_USE_ICU == TRUE) || defined(__DOXYGEN__)
  icuInit();
#endif
#if (HAL_USE_EICU == TRUE) || defined(__DOXYGEN__)
  eicuInit();
#endif
#if (HAL_USE_MAC == TRUE) || defined(__DOXYGEN__)
  macInit();
#endif
#if (HAL_USE_PWM == TRUE) || defined(__DOXYGEN__)
  pwmInit();
#endif
#if (HAL_USE_QSPI == TRUE) || defined(__DOXYGEN__)
  qspiInit();
#endif
#if (HAL_USE_SERIAL == TRUE) || defined(__DOXYGEN__)
  sdInit();
#endif
#if (HAL_USE_SDC == TRUE) || defined(__DOXYGEN__)
  sdcInit();
#endif
#if (HAL_USE_SPI == TRUE) || defined(__DOXYGEN__)
  spiInit();
#endif
#if (HAL_USE_UART == TRUE) || defined(__DOXYGEN__)
  uartInit();
#endif
#if (HAL_USE_USB == TRUE) || defined(__DOXYGEN__)
  usbInit();
#endif
#if (HAL_USE_MMC_SPI == TRUE) || defined(__DOXYGEN__)
  mmcInit();
#endif
#if (HAL_USE_SERIAL_USB == TRUE) || defined(__DOXYGEN__)
  sduInit();
#endif
#if (HAL_USE_RTC == TRUE) || defined(__DOXYGEN__)
  rtcInit();
#endif
#if (HAL_USE_WDG == TRUE) || defined(__DOXYGEN__)
  wdgInit();
#endif

  /* Community driver overlay initialization.*/
#if defined(HAL_USE_COMMUNITY) || defined(__DOXYGEN__)
#if (HAL_USE_COMMUNITY == TRUE) || defined(__DOXYGEN__)
  halCommunityInit();
#endif
#endif

  /*板专用初始化----- Board specific initialization.*/
  boardInit();

/*
 *  The ST driver is a special case, it is only initialized if the OSAL is
 *  configured to require it.
 *  ST驱动器是一种特殊情况,只有在OSAR是初始化时才初始化。
 *  配置为需要它。
 */
#if OSAL_ST_MODE != OSAL_ST_MODE_NONE
  stInit();
#endif
}


2. chSysInit()函数


void chSysInit(void)
{

  _scheduler_init();
  _vt_init();
  _trace_init();

#if CH_DBG_SYSTEM_STATE_CHECK == TRUE
  ch.dbg.isr_cnt  = (cnt_t)0;
  ch.dbg.lock_cnt = (cnt_t)0;
#endif
#if CH_CFG_USE_TM == TRUE
  _tm_init();
#endif
#if CH_CFG_USE_MEMCORE == TRUE
  _core_init();
#endif
#if CH_CFG_USE_HEAP == TRUE
  _heap_init();
#endif
#if CH_CFG_USE_FACTORY == TRUE
  _factory_init();
#endif
#if CH_DBG_STATISTICS == TRUE
  _stats_init();
#endif

#if CH_CFG_NO_IDLE_THREAD == FALSE
  /* Now this instructions flow becomes the main thread.*/
#if CH_CFG_USE_REGISTRY == TRUE
  currp = _thread_init(&ch.mainthread, (const char *)&ch_debug, NORMALPRIO);
#else
  currp = _thread_init(&ch.mainthread, "main", NORMALPRIO);
#endif
#else
  /* Now this instructions flow becomes the idle thread.*/
  currp = _thread_init(&ch.mainthread, "idle", IDLEPRIO);
#endif

#if CH_DBG_ENABLE_STACK_CHECK == TRUE
  {
    /* Setting up the base address of the static main thread stack, the
       symbol must be provided externally.*/
    extern stkalign_t __main_thread_stack_base__;
    currp->wabase = &__main_thread_stack_base__;
  }
#elif CH_CFG_USE_DYNAMIC == TRUE
  currp->wabase = NULL;
#endif

  /* Setting up the caller as current thread.*/
  currp->state = CH_STATE_CURRENT;

  /* Port layer initialization last because it depend on some of the
     initializations performed before.*/
  port_init();

#if CH_DBG_STATISTICS == TRUE
  /* Starting measurement for this thread.*/
  chTMStartMeasurementX(&currp->stats);
#endif

  /* Initialization hook.*/
  CH_CFG_SYSTEM_INIT_HOOK();

  /* It is alive now.*/
  chSysEnable();

#if CH_CFG_NO_IDLE_THREAD == FALSE
  {
    static const thread_descriptor_t idle_descriptor = {
      "idle",
      THD_WORKING_AREA_BASE(ch_idle_thread_wa),
      THD_WORKING_AREA_END(ch_idle_thread_wa),
      IDLEPRIO,
      _idle_thread,
      NULL
    };

    /* This thread has the lowest priority in the system, its role is just to
       serve interrupts in its context while keeping the lowest energy saving
       mode compatible with the system status.*/
    (void) chThdCreate(&idle_descriptor);
  }
#endif
}


3. malloc_init()函数



void malloc_init(void)
{
#if defined(CCM_RAM_SIZE_KB)
    chHeapObjectInit(&ccm_heap, (void *)CCM_BASE_ADDRESS, CCM_RAM_SIZE_KB*1024);
#endif

#if defined(DTCM_RAM_SIZE_KB)
    chHeapObjectInit(&dtcm_heap, (void *)DTCM_BASE_ADDRESS, DTCM_RAM_SIZE_KB*1024);
#endif

#if DMA_RESERVE_SIZE != 0
    /*
      create a DMA reserve heap, to ensure we keep some memory for DMA
      safe memory allocations
     */
    void *dma_reserve = malloc_dtcm(DMA_RESERVE_SIZE);
    if (!dma_reserve) {
        dma_reserve = chHeapAllocAligned(NULL, DMA_RESERVE_SIZE, MIN_ALIGNMENT);
    }
    chHeapObjectInit(&dma_reserve_heap, dma_reserve, DMA_RESERVE_SIZE);
#endif //#if DMA_RESERVE_SIZE != 0
}


4. setup_usb_strings()函数


void setup_usb_strings(void)
{
    setup_usb_string(&vcom_strings[1], HAL_USB_STRING_MANUFACTURER, vcom_buffers[0]); //"ArduPilot"
    setup_usb_string(&vcom_strings[2], HAL_USB_STRING_PRODUCT, vcom_buffers[1]);      //"%BOARD%"
    setup_usb_string(&vcom_strings[3], HAL_USB_STRING_SERIAL, vcom_buffers[2]);       //"%SERIAL%"
}

4.内存初始化函数

/**
 * @brief  执行各种RAM区域的初始化汇编中使用--- Performs the initialization of the various RAM areas.
 */
void __init_ram_areas(void)
{
#if CRT1_AREAS_NUMBER > 0
  const ram_init_area_t *rap = ram_areas;

  do {
    uint32_t *tp = rap->init_text_area;
    uint32_t *p = rap->init_area;

    /* Copying initialization data.*/
    while (p < rap->clear_area)
    {
      *p = *tp;
      p++;
      tp++;
    }

    /* Zeroing clear area.*/
    while (p < rap->no_init_area)
    {
      *p = 0;
      p++;
    }
    rap++;
  }
  while (rap < &ram_areas[CRT1_AREAS_NUMBER]);
#endif
}


2.汇编文件



STARTUPASM = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/crt0_v7m.S \
             $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/vectors.S


包含两个汇编文件crt0_v7m.S和vectors.S



1.crt0_v7m.S核心启动文件


/**
/**
 * @file    crt0_v7m.S
 * @brief   Generic ARMv7-M (Cortex-M3/M4/M7) startup file for ChibiOS.
 *
 * @addtogroup ARMCMx_GCC_STARTUP_V7M
 * @{
 */

/*===========================================================================*/
/* Module constants.                                                         */
/*===========================================================================*/

#if !defined(FALSE) || defined(__DOXYGEN__)
#define FALSE                               0
#endif

#if !defined(TRUE) || defined(__DOXYGEN__)
#define TRUE                                1
#endif

#define CONTROL_MODE_PRIVILEGED             0
#define CONTROL_MODE_UNPRIVILEGED           1
#define CONTROL_USE_MSP                     0
#define CONTROL_USE_PSP                     2
#define CONTROL_FPCA                        4

#define FPCCR_ASPEN                         (1 << 31)
#define FPCCR_LSPEN                         (1 << 30)

#define SCB_VTOR                            0xE000ED08
#define SCB_CPACR                           0xE000ED88
#define SCB_FPCCR                           0xE000EF34
#define SCB_FPDSCR                          0xE000EF3C

/*===========================================================================*/
/*模块预编译设置------Module pre-compile time settings.                                         */
/*===========================================================================*/

/**
 * @brief强制初始化MSP-----------Enforces initialization of MSP.
 * @note    This is required if the boot process is not reliable for whatever
 *          reason (bad ROMs, bad bootloaders, bad debuggers=.
 * 这是必需的,如果引导过程是不可靠的,无论什么原因(坏ROM,坏引导程序,坏调试器=)。
 */
#if !defined(CRT0_FORCE_MSP_INIT) || defined(__DOXYGEN__)
#define CRT0_FORCE_MSP_INIT                 TRUE
#endif

/**
 * @briefVTor特殊寄存器初始化--------------VTOR special register initialization.
 * @detailsVTor初始化为指向向量表-----------VTOR is initialized to point to the vectors table.
 */
#if !defined(CRT0_VTOR_INIT) || defined(__DOXYGEN__)
#define CRT0_VTOR_INIT                      TRUE
#endif

/**
 * @brief      浮点运算单元初始化开关-----   FPU initialization switch.
 */
#if !defined(CRT0_INIT_FPU) || defined(__DOXYGEN__)
#if defined(CORTEX_USE_FPU) || defined(__DOXYGEN__)
#define CRT0_INIT_FPU                       CORTEX_USE_FPU
#else
#define CRT0_INIT_FPU                       FALSE
#endif
#endif

/**
 * @brief 控制特殊寄存器初始化值。-------  Control special register initialization value.
 * @details The system is setup to run in privileged mode using the PSP
 *          stack (dual stack mode).
 *该系统设置为使用PSP栈(双栈模式)以特权模式运行。
 */
#if !defined(CRT0_CONTROL_INIT) || defined(__DOXYGEN__)
#define CRT0_CONTROL_INIT                   (CONTROL_USE_PSP |              \
                                             CONTROL_MODE_PRIVILEGED)
#endif

/**
 * @brief 核心初始化开关。----------Core initialization switch.
 */
#if !defined(CRT0_INIT_CORE) || defined(__DOXYGEN__)
#define CRT0_INIT_CORE                      TRUE
#endif

/**
 * @brief  堆栈段初始化开关---------Stack segments initialization switch.
 */
#if !defined(CRT0_STACKS_FILL_PATTERN) || defined(__DOXYGEN__)
#define CRT0_STACKS_FILL_PATTERN            0x55555555
#endif

/**
 * @brief  堆栈段初始化开关。--------Stack segments initialization switch.
 */
#if !defined(CRT0_INIT_STACKS) || defined(__DOXYGEN__)
#define CRT0_INIT_STACKS                    TRUE
#endif

/**
 * @brief  数据段初始化开关。-------- DATA segment initialization switch.
 */
#if !defined(CRT0_INIT_DATA) || defined(__DOXYGEN__)
#define CRT0_INIT_DATA                      TRUE
#endif

/**
 * @briefBSS(BSS段通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域。)段初始化开关--------BSS segment initialization switch.
 */
#if !defined(CRT0_INIT_BSS) || defined(__DOXYGEN__)
#define CRT0_INIT_BSS                       TRUE
#endif

/**
 * @brief RAM区域初始化开关--------------  RAM areas initialization switch.
 */
#if !defined(CRT0_INIT_RAM_AREAS) || defined(__DOXYGEN__)
#define CRT0_INIT_RAM_AREAS                 TRUE
#endif

/**
 * @brief   构造函数调用开关。--------------Constructors invocation switch.
 */
#if !defined(CRT0_CALL_CONSTRUCTORS) || defined(__DOXYGEN__)
#define CRT0_CALL_CONSTRUCTORS              TRUE
#endif

/**
 * @brief   析构函数调用开关。--------------Destructors invocation switch.
 */
#if !defined(CRT0_CALL_DESTRUCTORS) || defined(__DOXYGEN__)
#define CRT0_CALL_DESTRUCTORS               TRUE
#endif

/**
 * @brief  FPU(浮点运算) FPCCR寄存器初始化值-----FPU FPCCR register initialization value.
 * @note    Only used if @p CRT0_INIT_FPU is equal to @p TRUE.
 */
#if !defined(CRT0_FPCCR_INIT) || defined(__DOXYGEN__)
#define CRT0_FPCCR_INIT                     (FPCCR_ASPEN | FPCCR_LSPEN)
#endif

/**
 * @brief   寄存器初始化值CPACR register initialization value.
 * @note    Only used if @p CRT0_INIT_FPU is equal to @p TRUE.
 */
#if !defined(CRT0_CPACR_INIT) || defined(__DOXYGEN__)
#define CRT0_CPACR_INIT                     0x00F00000
#endif

/*=======================================================================================================================================*/
/*代码段---- Code section.
*Doxygen是一种开源跨平台的,以类似JavaDoc风格描述的文档系统,完全支持C、C++、Java、Objective-C和IDL语言,部分支持PHP、C#。
*注释的语法与Qt-Doc、KDoc和JavaDoc兼容。Doxygen可以从一套归档源文件开始,生成HTML格式的在线类浏览器,或离线的LATEX、RTF参考手册。
*MRS 指令:  对状态寄存器CPSR和SPSR进行读操作。通过读CPSR可以获得当前处理器的工作状态。读SPSR寄存器可以获得进入异常前的处理器状态(因为只有异常模式下有SPSR寄存器)。

*MSR指令:    对状态寄存器CPSR和SPSR进行写操作。与MRS配合使用,可以实现对CPSR或SPSR寄存器的读-修改-写操作,可以切换处理器模式、或者允许/禁止IRQ/FIQ中断等。                                                        */
/*=======================================================================================================================================*/

#if !defined(__DOXYGEN__)

                .syntax unified     //是一个指示,说明下面的指令是ARM和THUMB通用格式的
                .cpu    cortex-m3   //CPU内核
#if CRT0_INIT_FPU == TRUE
                .fpu    fpv4-sp-d16 //浮点运算
#else
                .fpu    softvfp
#endif

                .thumb             //thumb指令格式
                .text              //代码段

/*
 * CRT0进入点------CRT0 entry point.
 */
                .align  2         //4字节对齐
                .thumb_func
                .global _crt0_entry
_crt0_entry:
                /*中断是全局屏蔽的---- Interrupts are globally masked initially.*/
                cpsid   i

#if CRT0_FORCE_MSP_INIT == TRUE
                /*MSP堆栈指针初始化--- MSP stack pointers initialization.*/
                ldr     r0, =__main_stack_end__
                msr     MSP, r0
#endif

                /*PSP堆栈指针初始化----- PSP stack pointers initialization.*/
                ldr     r0, =__process_stack_end__
                msr     PSP, r0

#if CRT0_VTOR_INIT == TRUE
                ldr     r0, =_vectors
                movw    r1, #SCB_VTOR & 0xFFFF
                movt    r1, #SCB_VTOR >> 16
                str     r0, [r1]
#endif

#if CRT0_INIT_FPU == TRUE
                /*FPU FPCCR寄存器初始化------- FPU FPCCR initialization.*/
                movw    r0, #CRT0_FPCCR_INIT & 0xFFFF
                movt    r0, #CRT0_FPCCR_INIT >> 16
                movw    r1, #SCB_FPCCR & 0xFFFF
                movt    r1, #SCB_FPCCR >> 16
                str     r0, [r1]
                dsb
                isb

                /*CPACR初始化---------- CPACR initialization.*/
                movw    r0, #CRT0_CPACR_INIT & 0xFFFF
                movt    r0, #CRT0_CPACR_INIT >> 16
                movw    r1, #SCB_CPACR & 0xFFFF
                movt    r1, #SCB_CPACR >> 16
                str     r0, [r1]
                dsb
                isb

                /*FPU FPSCR初始化清除----- FPU FPSCR initially cleared.*/
                mov     r0, #0
                vmsr    FPSCR, r0

                /*FPU FPDSCR初始化清除---- FPU FPDSCR initially cleared.*/
                movw    r1, #SCB_FPDSCR & 0xFFFF
                movt    r1, #SCB_FPDSCR >> 16
                str     r0, [r1]

                /*在控制寄存器中强制执行FPCA位--- Enforcing FPCA bit in the CONTROL register.*/
                movs    r0, #CRT0_CONTROL_INIT | CONTROL_FPCA

#else
                movs    r0, #CRT0_CONTROL_INIT
#endif

                /*配置寄存器控制初始化---- CONTROL register initialization as configured.*/
                msr     CONTROL, r0
                isb

#if CRT0_INIT_CORE == TRUE
                /*跳转到内核初始化,该函数在crt1.c中实现-----Core initialization.*/
                bl      __core_init  //BL Label ;跳转到Label对应的地址,并且把跳转前的下条指令地址保存到LR
#endif

                /*跳转到早期初始化------Early initialization.*/
                bl      __early_init

#if CRT0_INIT_STACKS == TRUE
                ldr     r0, =CRT0_STACKS_FILL_PATTERN
                /* Main Stack initialization. Note, it assumes that the
                   stack size is a multiple of 4 so the linker file must
                   ensure this.*/
                 //主栈初始化。注意,它假定堆栈大小是4的倍数,因此链接器文件必须确保这一点。
                ldr     r1, =__main_stack_base__
                ldr     r2, =__main_stack_end__
msloop:
                cmp     r1, r2  //比较指令
                itt     lo       //接下来的两条指令条件执行
                strlo   r0, [r1], #4
                blo     msloop   //小于(无符号数)跳转

                /* Process Stack initialization. Note, it assumes that the
                   stack size is a multiple of 4 so the linker file must
                   ensure this.*/
                //进程堆栈初始化。注意,它假定堆栈大小是4的倍数,因此链接器文件必须确保这一点。
                ldr     r1, =__process_stack_base__
                ldr     r2, =__process_stack_end__
psloop:
                cmp     r1, r2
                itt     lo
                strlo   r0, [r1], #4
                blo     psloop
#endif

#if CRT0_INIT_DATA == TRUE
                /*Data initialization. Note, it assumes that the DATA size
                  is a multiple of 4 so the linker file must ensure this.*/
                //数据初始化。注意,它假定数据大小是4的倍数,因此链接器文件必须确保这一点。
                ldr     r1, =_textdata_start
                ldr     r2, =_data_start
                ldr     r3, =_data_end
dloop:
                cmp     r2, r3
                ittt    lo
                ldrlo   r0, [r1], #4
                strlo   r0, [r2], #4
                blo     dloop
#endif

#if CRT0_INIT_BSS == TRUE
                /* BSS initialization. Note, it assumes that the DATA size
                  is a multiple of 4 so the linker file must ensure this.*/
                //BSS初始化。注意,它假定数据大小是4的倍数,因此链接器文件必须确保这一点
                //BSS存放的是未初始化的全局变量和静态变量,数据段存放的是初始化后的全局变量和静态变量。
                //BSS(Block Started by Symbol)通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域
                movs    r0, #0
                ldr     r1, =_bss_start
                ldr     r2, =_bss_end
bloop:
                cmp     r1, r2
                itt     lo
                strlo   r0, [r1], #4
                blo     bloop
#endif

#if CRT0_INIT_RAM_AREAS == TRUE
                /*内存区域初始化------RAM areas initialization.*/
                bl      __init_ram_areas
#endif

                /*后期初始化,该函数在板层board.c中实现----- Late initialization..*/
                bl      __late_init

#if CRT0_CALL_CONSTRUCTORS == TRUE
                /*构造函数的调用---- Constructors invocation.*/
                ldr     r4, =__init_array_start
                ldr     r5, =__init_array_end
initloop:
                cmp     r4, r5
                bge     endinitloop  //大于或等于才跳
                ldr     r1, [r4], #4
                blx     r1
                b       initloop    //跳转,不返回
endinitloop:
#endif

                /*主程序调用,包含返回值R0。---- Main program invocation, r0 contains the returned value.*/
                bl      main   //该函数将会跳转到标准的C++,应用main函数,然后调用arducopter中的main()函数

#if CRT0_CALL_DESTRUCTORS == TRUE
                /*析构函数调用---- Destructors invocation.*/
                ldr     r4, =__fini_array_start
                ldr     r5, =__fini_array_end
finiloop:
                cmp     r4, r5
                bge     endfiniloop
                ldr     r1, [r4], #4
                blx     r1
                b       finiloop
endfiniloop:
#endif

                /*分支到定义的退出处理程序---- Branching to the defined exit handler.*/
                b       __default_exit

#endif /* !defined(__DOXYGEN__) */

/** @} */




2.vectors.S中断向量表


#if !defined(__DOXYGEN__)

        .syntax unified
        .cpu    cortex-m0
        .thumb

        .section    .vectors, "ax"
        .align      4
        .globl      _vectors
_vectors:
        .long       __main_stack_end__
        .long       Reset_Handler
        .long       NMI_Handler
        .long       HardFault_Handler
        .long       MemManage_Handler
        .long       BusFault_Handler
        .long       UsageFault_Handler
        .long       Vector1C
        .long       Vector20
        .long       Vector24
        .long       Vector28
        .long       SVC_Handler
        .long       DebugMon_Handler
        .long       Vector34
        .long       PendSV_Handler
        .long       SysTick_Handler
        .long       Vector40,   Vector44,   Vector48,   Vector4C
#if CORTEX_NUM_VECTORS > 4
        .long       Vector50,   Vector54,   Vector58,   Vector5C
#endif
#if CORTEX_NUM_VECTORS > 8
        .long       Vector60,   Vector64,   Vector68,   Vector6C
#endif
#if CORTEX_NUM_VECTORS > 12
        .long       Vector70,   Vector74,   Vector78,   Vector7C
#endif
#if CORTEX_NUM_VECTORS > 16
        .long       Vector80,   Vector84,   Vector88,   Vector8C
#endif
#if CORTEX_NUM_VECTORS > 20
        .long       Vector90,   Vector94,   Vector98,   Vector9C
#endif
#if CORTEX_NUM_VECTORS > 24
        .long       VectorA0,   VectorA4,   VectorA8,   VectorAC
#endif
#if CORTEX_NUM_VECTORS > 28
        .long       VectorB0,   VectorB4,   VectorB8,   VectorBC
#endif
........后面内容省略......


3. Chibios run()函数学习



1.HAL_ChibiOS的run()函数




void HAL_ChibiOS::run(int argc, char * const argv[], Callbacks* callbacks) const
{
    /*
     * -系统初始化-------------------------------------System initializations.
     * -Chibios 硬件抽象层初始化,也就是进行驱动配置--------ChibiOS HAL initialization, this also initializes the configured device drivers
     * -和执行特殊的板层配置-----------------------------and performs the board-specific initializations.
     * -内核初始化、主函数()成为一个线程,然后激活RTOS------ Kernel initialization, the main() function becomes a thread and the
     *   RTOS is active.
     */

#ifdef HAL_USB_PRODUCT_ID
  setup_usb_strings(); //动态分配USB描述符字符串,建议先不要去研究这个
#endif
    
#ifdef HAL_STDOUT_SERIAL
    //标准输出初始化--------STDOUT Initialistion
    SerialConfig stdoutcfg =
    {
      HAL_STDOUT_BAUDRATE,
      0,
      USART_CR2_STOP1_BITS,
      0
    };
    sdStart((SerialDriver*)&HAL_STDOUT_SERIAL, &stdoutcfg);
#endif

    assert(callbacks);       //用来让程序测试条件,如果条件正确继续执行,如果条件错误,报错。
    g_callbacks = callbacks; //函数定义传递

    //接管执行main------------Takeover main
    main_loop();
}


2.HAL_ChibiOS的main_loop()函数



static void main_loop()
{
    daemon_task = chThdGetSelfX(); //返回当前线程

    /*
      把main loop的优先级切换到高优先级-----switch to high priority for main loop
     */
    chThdSetPriority(APM_MAIN_PRIORITY); //180

#ifdef HAL_I2C_CLEAR_BUS
    //- Clear all I2C Buses. This can be needed on some boards which
    // can get a stuck I2C peripheral on boot
    //清除所有的I2C总线。这可能需要在一些板上,可以得到一个卡住的I2C外围设备启动。
    ChibiOS::I2CBus::clear_all();
#endif

#if STM32_DMA_ADVANCED
    ChibiOS::Shared_DMA::init(); //不使能DMA
#endif
    peripheral_power_enable();   //启用外围电源
        
    hal.uartA->begin(115200);   //初始化USB的波特率

#ifdef HAL_SPI_CHECK_CLOCK_FREQ
    //SPI时钟频率的可选测试---- optional test of SPI clock frequencies
    ChibiOS::SPIDevice::test_clock_freq();
#endif 

    //初始化SD卡和文件系统-----Setup SD Card and Initialise FATFS bindings
    sdcard_init();

    hal.uartB->begin(38400);  //GPS波特率38400
    hal.uartC->begin(57600);  //串口波特率设置
    hal.analogin->init();     //模拟输入初始化,主要测试ADC功能,检查电源电压


    hal.scheduler->init();   //初始化任务init线程

    /*
        run setup() at low priority to ensure CLI doesn't hang the system, and to allow initial sensor read loops to run
     */

    //以低优先级运行SETUP()以确保CLI(命令行界面)不挂起系统,并允许初始传感器读取循环运行。
    hal_chibios_set_priority(APM_STARTUP_PRIORITY); //APM_STARTUP_PRIORITY=10

    schedulerInstance.hal_initialized(); //_hal_initialized = true

    g_callbacks->setup();                 //调用应用层的setup()函数
    hal.scheduler->system_initialized(); //系统初始化

    thread_running = true;
    chRegSetThreadName(SKETCHNAME);
    
    /*
      main loop切换到低优先级-------switch to high priority for main loop
     */
    chThdSetPriority(APM_MAIN_PRIORITY);

    hal.uartG->printf("UARTG\r\n"); //自己添加打印函数


    while (true)
    {
        g_callbacks->loop();  //调用APP的loop线程

        /*
          give up 250 microseconds of time if the INS loop hasn't
          called delay_microseconds_boost(), to ensure low priority
          drivers get a chance to run. Calling
          delay_microseconds_boost() means we have already given up
          time from the main loop, so we don't need to do it again
          here
          如果INS回路回调Delay-MySudiSsBooSth()函数没有响应,则放弃250微秒的时间。
         以确保低优先级。有机会运行。回调延迟函数delay_microseconds_boost意味着我们已经放弃了主回路循环,所以我们不需要再做一次。
         */
        hal.uartG->printf("MMM\r\n"); //自己添加打印函数
        if (!schedulerInstance.check_called_boost())
        {
        	hal.uartG->printf("NNN\r\n"); //自己添加打印函数
            hal.scheduler->delay_microseconds(250);
        }
    }
    thread_running = false;
}




3.HAL_ChibiOS的init()函数



  • hal.scheduler->init(); //初始化任务init线程


Ardupilot chibios编译,启动,main函数学习(2)_第4张图片

void Scheduler::init()
{
    chBSemObjectInit(&_timer_semaphore, false); //信号量
    chBSemObjectInit(&_io_semaphore, false);     //信号量
    //设置定时器进程-这将调用任务在1kHz---- setup the timer thread - this will call tasks at 1kHz
    _timer_thread_ctx = chThdCreateStatic(_timer_thread_wa,
                     sizeof(_timer_thread_wa),
                     APM_TIMER_PRIORITY,        /* Initial priority.    */
                     _timer_thread,             /* Thread function.     */
                     this);                     /* Thread parameter.    */

    //设置RCIN进程-这将调用任务在1kHz---- setup the RCIN thread - this will call tasks at 1kHz
    _rcin_thread_ctx = chThdCreateStatic(_rcin_thread_wa,
                     sizeof(_rcin_thread_wa),
                     APM_RCIN_PRIORITY,        /* Initial priority.    */
                     _rcin_thread,             /* Thread function.     */
                     this);                     /* Thread parameter.    */
#ifndef HAL_USE_EMPTY_IO
    //IO进程以较低优先级运行--------- the IO thread runs at lower priority
    _io_thread_ctx = chThdCreateStatic(_io_thread_wa,
                     sizeof(_io_thread_wa),
                     APM_IO_PRIORITY,        /* Initial priority.      */
                     _io_thread,             /* Thread function.       */
                     this);                  /* Thread parameter.      */
#endif

#ifndef HAL_USE_EMPTY_STORAGE
    //存储进程在IO优先级之上运行。-------- the storage thread runs at just above IO priority
    _storage_thread_ctx = chThdCreateStatic(_storage_thread_wa,
                     sizeof(_storage_thread_wa),
                     APM_STORAGE_PRIORITY,        /* Initial priority.      */
                     _storage_thread,             /* Thread function.       */
                     this);                  /* Thread parameter.      */
#endif

}


1.信号量创建函数



    chBSemObjectInit(&_timer_semaphore, false);
    chBSemObjectInit(&_io_semaphore, false);

二进制信号量定义

#if CH_CFG_USE_SEMAPHORES == TRUE
    binary_semaphore_t _timer_semaphore;
    binary_semaphore_t _io_semaphore;
#endif
typedef struct ch_binary_semaphore 
{
  semaphore_t           sem; //结构体
} binary_semaphore_t;

信号量结构体

typedef struct ch_semaphore {
  threads_queue_t       queue;      /**< @brief Queue of the threads sleeping
                                                on this semaphore.          */
  cnt_t                 cnt;        /**< @brief The semaphore counter.      */
} semaphore_t;
/**
 * @brief   Initializes a binary semaphore.

 * @param[out] bsp      pointer to a @p binary_semaphore_t structure
 * @param[in] taken     initial state of the binary semaphore:
 *                      - @a false, the initial state is not taken.
 *                      - @a true, the initial state is taken.

 */
static inline void chBSemObjectInit(binary_semaphore_t *bsp, bool taken) 
{

  chSemObjectInit(&bsp->sem, taken ? (cnt_t)0 : (cnt_t)1);
}

这里一定要理解taken这个值得含义,否则后面的代码会理解错误,如果taken=0,表示钥匙没有被使用,可以使用,taken=1,表示钥匙被取走,不能使用,要等待,如果理解反了,后面无法分析


ps:二进制信号量

Ardupilot chibios编译,启动,main函数学习(2)_第5张图片



那么我们的Chibios的系统怎么实现使用信号量的呢?



答案:
某些资源在同一时刻只可以被一个任务操作,实时操作系统的任务抢占特性会导致这些资源可能被多个任务同时操作,从而产生错误。我们这里的Chibios创建的信号量就是根据二进制信号量的原理,利用二进制信号量保护这些资源,使多个任务只能串行的操作这些资源记住是串行操作,因为chibios里面的io线程很多,所以不能相互影响。



为什么要这样做,原因:



有时候我们可以设计一块共享内存,用来在多个任务间传递数据,比如使用任务1向共享内存中写入数据,使用任务2从这片内存中读取数据,这样就可以实现任务1向任务2传递数据的功能,但这样做有一个问题,如果任务1正在向共享内存中写数据的过程中发生了任务切换,切换到了任务2,那么任务2所读取的共享内存中的数据就不完全是最新写入的有效数据,这样任务2就会读取到错误的数据。

为了防止这个问题发生,最简单的办法就是使用一个全局变量来指示共享内存的访问权限,当全局变量为1时,共享内存可以被访问,当全局变量为0时共享内存不可被访问。当一个任务操作共享内存时,首先判断全局变量,如果为0,说明共享内存正在被其它任务操作,此时无法被访问,如果为1的话说明共享内存可以被访问,那么该任务则将全局变量置为0,表明共享内存已经被访问,其它任务此时不可访问共享内存。当任务操作完共享内存后将全局变量置为1,释放对共享内存的访问权限,此后共享内存又可以被再次访问。
二进制信号量就是基于上述原理实现的,简单来说,二进制信号量就是一个全局变量,用来实现各种资源的互斥,但使用**全局变量作为资源互斥的开关存在一个缺点:**当任务获取不到访问权限时,它可能需要等待该权限,需要暂时放弃CPU资源,让给其它任务去运行,这就需要发生任务调度,但直接触发任务调度的软中断调度函数被封装到了操作系统内部,用户不可见,因此获取不到权限的任务也就无法主动发生任务调度切换到其它任务。


**
下面举例子说明:假设有一对母子,需要在一个房间,坐在同一个凳子,拿着同一把水果刀削苹果,削完苹果必须把苹果放在盘子里,然后离开房间,才算完成任务。
那么我们假设母亲和儿子都在房间外,开始去执行这个任务。那么任意一时刻完成削一个苹果,只能一个人完成,削苹果完成后,然后离开房间,下一个人才能执行,在上一个人没有完成时,下一个人只能等待。**
削苹果 semwait(plate)
放苹果 semsignal(apple)
母亲进程:
削苹果 semwait(plate)
放苹果 semsignal(apple)
儿子进程
取苹果 semwait(apple)
吃苹果semsignal(apple)



信号的使用在后面定时器线程和io线程讲解


2.定时器进程



1.定时器进程创建


    //设置定时器线程-这将调用任务在1kHz---- setup the timer thread - this will call tasks at 1kHz
    _timer_thread_ctx = chThdCreateStatic(_timer_thread_wa,
                     sizeof(_timer_thread_wa),
                     APM_TIMER_PRIORITY,        /* Initial priority.181    */
                     _timer_thread,             /* Thread function.     */
                     this);                     /* Thread parameter.    */

2.定时器进程函数实现


void Scheduler::_timer_thread(void *arg)
{
    Scheduler *sched = (Scheduler *)arg;
    chRegSetThreadName("apm_timer");

    while (!sched->_hal_initialized)     //没有完成初始化
    {
        sched->delay_microseconds(1000); //延迟1ms
    }
    while (true)
    {
        sched->delay_microseconds(1000);

        //运行注册计时器------run registered timers
        sched->_run_timers();

        //处理任何即将来的遥控器RC输出请求---- process any pending RC output requests
        hal.rcout->timer_tick();
    }
}


函数:sched->_run_timers();


void Scheduler::_run_timers()
{
//	hal.uartG->printf("CHIBIOS\r\n");
//	hal.uartG->printf("_in_timer_proc=%d\r\n",_in_timer_proc);
    if (_in_timer_proc) //在定时器进程中?,第一次不会进入if
    {
        return;
    }
    _in_timer_proc = true;

    int num_procs = 0;
    chBSemWait(&_timer_semaphore);  //等待信号量,这里可以直接进去,不用再等待了,相当于共享资源保护,为什么?看上面对信号量的初始值的讲解
    num_procs = _num_timer_procs;   //运行保存这个值,只有该任务执行完毕,才能更改
  //  hal.uartG->printf("num_procs=%d\r\n",num_procs);
    chBSemSignal(&_timer_semaphore);
    //现在调用基于计时器的驱动程序----- now call the timer based drivers
    for (int i = 0; i < num_procs; i++) //这里主要判断有多少定时器驱动程序
    {
        if (_timer_proc[i])
        {
            _timer_proc[i](); //这个函数在哪,看下面的函数
        }
    }

    //故障安全,如果一个初始化---- and the failsafe, if one is setup
    if (_failsafe != nullptr)
    {
        _failsafe();
    }

#if HAL_USE_ADC == TRUE
    //处理模拟输入线程-----process analog input
    ((AnalogIn *)hal.analogin)->_timer_tick();
#endif

    _in_timer_proc = false;
}

上面这个函数跟哪里有联系呢,看下函数?

void Scheduler::register_timer_process(AP_HAL::MemberProc proc)
{
    chBSemWait(&_timer_semaphore);
    for (uint8_t i = 0; i < _num_timer_procs; i++)
    {
        if (_timer_proc[i] == proc)
        {
            chBSemSignal(&_timer_semaphore);
            return;
        }
    }

    if (_num_timer_procs < CHIBIOS_SCHEDULER_MAX_TIMER_PROCS) //CHIBIOS_SCHEDULER_MAX_TIMER_PROCS=8
    {
        _timer_proc[_num_timer_procs] = proc;
        _num_timer_procs++;
    } else
    {
        hal.console->printf("Out of timer processes\n");
    }
    chBSemSignal(&_timer_semaphore);
}

Ardupilot chibios编译,启动,main函数学习(2)_第6张图片



下面说明上面的执行过程:定时器进程首先运行,后面会加载注册了多少定时器线程,采用二进制信号量来保证每个定时器线程的正常运行
如果_timer_thread()进程,没有注册一个定时器线程,那么这个进程里面还有其他的两个线程

#if HAL_USE_ADC == TRUE
    //处理模拟输入线程-----process analog input
    ((AnalogIn *)hal.analogin)->_timer_tick();
#endif
 //处理任何未决的RC输出请求---- process any pending RC output requests
        hal.rcout->timer_tick();

来分析下没有定时器线程的过程:
第一步:

void Scheduler::_timer_thread(void *arg)
{
    Scheduler *sched = (Scheduler *)arg;
    chRegSetThreadName("apm_timer");

    while (!sched->_hal_initialized)     //完成初始化
    {
        sched->delay_microseconds(1000); //延迟1ms
    }
    while (true)
    {
        sched->delay_microseconds(1000);

        //运行注册计时器------run registered timers
        sched->_run_timers();

        //处理任何未决的RC输出请求---- process any pending RC output requests
        hal.rcout->timer_tick();
    }
}

我们要分析sched->_run_timers();其中//的都不会运行

void Scheduler::_run_timers()
{
//	hal.uartG->printf("CHIBIOS\r\n");
//	hal.uartG->printf("_in_timer_proc=%d\r\n",_in_timer_proc);
    if (_in_timer_proc) //在定时器进程中?,第一次不会进入if
    {
        return;
    }
    _in_timer_proc = true;

    int num_procs = 0;
    chBSemWait(&_timer_semaphore);  //等待信号量,这里可以直接进去,不用再等待了,相当于共享资源保护,为什么?看上面对信号量的初始值的讲解
    num_procs = _num_timer_procs;   //运行保存这个值,只有该任务执行完毕,才能更改
  //  hal.uartG->printf("num_procs=%d\r\n",num_procs);
    chBSemSignal(&_timer_semaphore);
    //现在调用基于计时器的驱动程序----- now call the timer based drivers
//    for (int i = 0; i < num_procs; i++) //这里主要判断有多少定时器驱动程序
//    {
//        if (_timer_proc[i])
//        {
//            _timer_proc[i](); //这个函数在哪,看下面的函数
//        }
//    }
//
//    //故障安全,如果一个初始化---- and the failsafe, if one is setup
//    if (_failsafe != nullptr)
//    {
//        _failsafe();
//    }

#if HAL_USE_ADC == TRUE
    //处理模拟输入线程-----process analog input
    ((AnalogIn *)hal.analogin)->_timer_tick();
#endif

    _in_timer_proc = false;
}

*1.函数最重要的就是执行 ((AnalogIn )hal.analogin)->_timer_tick();


/************************************************************************************************************************************
*函数原型:vvoid AnalogIn::_timer_tick(void)
*函数功能:进程初始化
*修改日期:2018-10-29
*备   注:called at 1kHz
*************************************************************************************************************************************/

void AnalogIn::_timer_tick(void)
{
    //读取adc在100hz-----read adc at 100Hz
    uint32_t now = AP_HAL::micros();
    uint32_t delta_t = now - _last_run;
    if (delta_t < 10000)  //10ms,否则返回
    {
        return;
    }
    _last_run = now;

    uint32_t buf_adc[ADC_GRP1_NUM_CHANNELS];

    /* 读取所有通道的值------read all channels available */
    read_adc(buf_adc);

    //更新电压状态-----update power status flags
    update_power_flags();
    
    //将传入的信道与当前活动的引脚匹配---- match the incoming channels to the currently active pins
    for (uint8_t i=0; i < ADC_GRP1_NUM_CHANNELS; i++) 
    {
#ifdef ANALOG_VCC_5V_PIN
        if (pin_config[i].channel == ANALOG_VCC_5V_PIN) 
        {
            //记录VCC值以供以后使用---- record the Vcc value for later use in
            // voltage_average_ratiometric()
            _board_voltage = buf_adc[i] * pin_config[i].scaling;
        }
#endif
    }

#if HAL_WITH_IO_MCU
    //处理IOMCU的特殊输入----- now handle special inputs from IOMCU
    _servorail_voltage = iomcu.get_vservo();
    _rssi_voltage = iomcu.get_vrssi();
#endif
    
    for (uint8_t i=0; i_pin) 
                {
                    // add a value
                    c->_add_value(buf_adc[i], _board_voltage);
                } else if (c->_pin == ANALOG_SERVO_VRSSI_PIN) 
                {
                    c->_add_value(_rssi_voltage / VOLTAGE_SCALING, 0);
                }
            }
        }
    }

#if CHIBIOS_ADC_MAVLINK_DEBUG
    static uint8_t count;
    if (AP_HAL::millis() > 5000 && count++ == 10) 
    {
        count = 0;
        uint16_t adc[6] {};
        uint8_t n = ADC_GRP1_NUM_CHANNELS;
        if (n > 6) 
        {
            n = 6;
        }
        for (uint8_t i=0; i < n; i++) 
        {
            adc[i] = buf_adc[i];
        }
        mavlink_msg_ap_adc_send(MAVLINK_COMM_0, adc[0], adc[1], adc[2], adc[3], adc[4], adc[5]); //通过mavlink发送出去到地面站
    }
#endif
}

上面这个任务是读取电源电压的,先不做分析,后续用到继续讲解


2.分析函数:
处理任何请求的的遥控器RC输出请求---- process any pending RC output requests
hal.rcout->timer_tick();



/************************************************************************************************************************************
*函数原型:void RCOutput::timer_tick(void)
*函数功能:周期定时器----这是用于单机模式和dshop模式(DSHOT是无人机里边,一种新型的驱动电调(ESC,无刷直流电机电子调速器)的方式),加上安全开关更新。
*修改日期:2018-10-29
*备   注: periodic timer. This is used for oneshot and dshot modes, plus for safety switch update
*************************************************************************************************************************************/

void RCOutput::timer_tick(void)
{
    safety_update(); //更新安全状态
    
    uint64_t now = AP_HAL::micros64();
    for (uint8_t i = 0; i < NUM_GROUPS; i++ ) 
    {
        pwm_group &group = pwm_group_list[i];
        if (!serial_group &&
            group.current_mode >= MODE_PWM_DSHOT150 &&
            group.current_mode <= MODE_PWM_DSHOT1200 &&
            now - group.last_dshot_send_us > 400) {
            // do a blocking send now, to guarantee DShot sends at
            // above 1000 Hz. This makes the protocol more reliable on
            // long cables, and also keeps some ESCs happy that don't
            // like low rates
        	//现在做一个阻塞发送,保证DShot发送在1000赫兹以上。这使得协议在长电缆上更加可靠,同时也保持一些不喜欢低利率的ESC高兴。
            dshot_send(group, true);
        }
    }
    if (min_pulse_trigger_us == 0 ||
        serial_group != nullptr) 
    {
        return;
    }
    if (now > min_pulse_trigger_us &&
        now - min_pulse_trigger_us > 4000) 
    {
        //最小250Hz触发器--- trigger at a minimum of 250Hz
        trigger_groups();
    }
}

总结
Ardupilot chibios编译,启动,main函数学习(2)_第7张图片



3.遥控器输入进程



  //设置RCIN线程-这将调用任务在1kHz---- setup the RCIN thread - this will call tasks at 1kHz
    _rcin_thread_ctx = chThdCreateStatic(_rcin_thread_wa,
                     sizeof(_rcin_thread_wa),
                     APM_RCIN_PRIORITY,        /* Initial priority. 177   */
                     _rcin_thread,             /* Thread function.     */
                     this);                     /* Thread parameter.    */


/************************************************************************************************************************************
*函数原型:void Scheduler::_rcin_thread(void *arg)
*函数功能:进程初始化
*修改日期:2018-10-29
*备   注:
*************************************************************************************************************************************/
void Scheduler::_rcin_thread(void *arg)
{
    Scheduler *sched = (Scheduler *)arg;
    chRegSetThreadName("apm_rcin"); //进程名字
    while (!sched->_hal_initialized)
    {
        sched->delay_microseconds(20000);
    }
    while (true)
    {
        sched->delay_microseconds(2500);
        ((RCInput *)hal.rcin)->_timer_tick(); //2.5ms运行一次
    }
}


void RCInput::_timer_tick(void)
{
    if (!_init) 
    {
        return;
    }
#if HAL_USE_ICU == TRUE || HAL_USE_EICU == TRUE
    uint32_t width_s0, width_s1;

    while(sig_reader.read(width_s0, width_s1)) 
    {
        rcin_prot.process_pulse(width_s0, width_s1);
    }

    if (rcin_prot.new_input()) 
    {
        rcin_mutex.take(HAL_SEMAPHORE_BLOCK_FOREVER);
        _rcin_timestamp_last_signal = AP_HAL::micros();
        _num_channels = rcin_prot.num_channels();
        for (uint8_t i=0; i<_num_channels; i++) 
        {
            _rc_values[i] = rcin_prot.read(i);
        }
        rcin_mutex.give();
    }
#endif

#if HAL_RCINPUT_WITH_AP_RADIO
    if (radio && radio->last_recv_us() != last_radio_us) 
    {
        last_radio_us = radio->last_recv_us();
        rcin_mutex.take(HAL_SEMAPHORE_BLOCK_FOREVER);
        _rcin_timestamp_last_signal = last_radio_us;
        _num_channels = radio->num_channels();
        for (uint8_t i=0; i<_num_channels; i++) {
            _rc_values[i] = radio->read(i);
        }
        rcin_mutex.give();
    }
#endif

#if HAL_WITH_IO_MCU
    rcin_mutex.take(HAL_SEMAPHORE_BLOCK_FOREVER);
    if (AP_BoardConfig::io_enabled() &&
        iomcu.check_rcinput(last_iomcu_us, _num_channels, _rc_values, RC_INPUT_MAX_CHANNELS)) {
        _rcin_timestamp_last_signal = last_iomcu_us;
    }
    rcin_mutex.give();
#endif
    //注意,我们依赖于模型代码检查有没有新数据输入,以及最后一个有效输入以处理故障安全的超时
    // note, we rely on the vehicle code checking new_input()
    // and a timeout for the last valid input to handle failsafe
}


4. io进程



#ifndef HAL_USE_EMPTY_IO
    //IO线程以较低优先级运行--------- the IO thread runs at lower priority
    _io_thread_ctx = chThdCreateStatic(_io_thread_wa,
                     sizeof(_io_thread_wa),
                     APM_IO_PRIORITY,        /* Initial priority.  58    */
                     _io_thread,             /* Thread function.       */
                     this);                  /* Thread parameter.      */
#endif


IO进程与定时器进程有点相似,都采用二进制信号量,保证每个线程的正常运行。



void Scheduler::_io_thread(void* arg)
{
	hal.uartG->printf("GGG\r\n");
    Scheduler *sched = (Scheduler *)arg;
    chRegSetThreadName("apm_io");   //设置进程名称
    while (!sched->_hal_initialized) //没有初始化就等待1ms
    {
        sched->delay_microseconds(1000);
    }
    while (true)
    {
        sched->delay_microseconds(1000); //io线程的周期是1ms

        //运行注册IO进程--------------run registered IO processes
        sched->_run_io();
    }
}
void Scheduler::_run_io(void)
{

    if (_in_io_proc)  //_in_io_proc=0,第一次刚开始时
    {
        return;
    }
    //由于第一次_in_io_proc=0,运行到这里被设置1后,如果进入二进制信号量等待的话,一直处于返回状态,因为_in_io_proc没有被设置成0,说明一次run_io还没有运行完
    _in_io_proc = true;

    int num_procs = 0;            //进程计数
    chBSemWait(&_io_semaphore);   //等待信号量的到来
    hal.uartG->printf("GG2\r\n");
    num_procs = _num_io_procs;
    chBSemSignal(&_io_semaphore); //释放二进制信号量
    //现在调用基于IO的驱动程序----- -now call the IO based drivers
    for (int i = 0; i < num_procs; i++)
    {
        if (_io_proc[i])
        {
            _io_proc[i]();//IO_TIMER()
        }
    }

    _in_io_proc = false;
}

注意这个函数:

*************************************************************************************************************************/
void Scheduler::register_io_process(AP_HAL::MemberProc proc)
{

    chBSemWait(&_io_semaphore);  //等待二进制信号量到来,如果来了,往下执行

//    hal.uartG->printf("GG1\r\n");
    for (uint8_t i = 0; i < _num_io_procs; i++)
    {
        if (_io_proc[i] == proc)
        {
            chBSemSignal(&_io_semaphore); //释放二进制信号量
            return;
        }
    }
    
    if (_num_io_procs < CHIBIOS_SCHEDULER_MAX_TIMER_PROCS) //最多8个
    {
        _io_proc[_num_io_procs] = proc;
        _num_io_procs++;
    } else
    {
        hal.console->printf("Out of IO processes\n");
    }
    chBSemSignal(&_io_semaphore); //释放二进制信号量
}

Ardupilot chibios编译,启动,main函数学习(2)_第8张图片



5. 存储进程



#ifndef HAL_USE_EMPTY_STORAGE
    //存储线程在IO优先级之上运行。-------- the storage thread runs at just above IO priority
    _storage_thread_ctx = chThdCreateStatic(_storage_thread_wa,
                     sizeof(_storage_thread_wa),
                     APM_STORAGE_PRIORITY,        /* Initial priority.      */
                     _storage_thread,             /* Thread function.       */
                     this);                  /* Thread parameter.      */
#endif

存储线程

void Scheduler::_storage_thread(void* arg)
{
    Scheduler *sched = (Scheduler *)arg;
    chRegSetThreadName("apm_storage");
    while (!sched->_hal_initialized) 
    {
        sched->delay_microseconds(10000);
    }
    while (true) 
    {
        sched->delay_microseconds(10000); //10ms

        // process any pending storage writes
        hal.storage->_timer_tick();
    }
}

/************************************************************************************************************************************
*函数原型:void Storage::_timer_tick(void)
*函数功能:
*修改日期:2018-10-29
*备   注:
*************************************************************************************************************************************/
void Storage::_timer_tick(void)
{
    if (!_initialised) 
    {
        return;
    }
    if (_dirty_mask.empty()) 
    {
        _last_empty_ms = AP_HAL::millis();
        return;
    }

    // write out the first dirty line. We don't write more
    // than one to keep the latency of this call to a minimum
    uint16_t i;
    for (i=0; i


4. g_callbacks->setup()函数




/***********************************************************************************************************************
*函数原型:void Copter::setup()
*函数功能:初始化
*修改日期:2018-9-13
*修改作者:
*备注信息:
*************************************************************************************************************************/
void Copter::setup()
{
	 //从参数表中加载默认参数----------Load the default values of variables listed in var_info[]s
	    AP_Param::setup_sketch_defaults();

	 //初始化储存的多旋翼布局-----------setup storage layout for copter
	    StorageManager::set_layout_copter();
	 //传感器初始化,注册
	   init_ardupilot();

	 //初始化整个主loop任务调度-------initialise the main loop scheduler
	   scheduler.init(&scheduler_tasks[0], ARRAY_SIZE(scheduler_tasks), MASK_LOG_PM);

}

注册所有的驱动

/***********************************************************************************************************************
*函数原型:void Copter::init_ardupilot()
*函数功能:初始化
*修改日期:2018-10-26
*修改作者:cihang_uav
*备注信息:
*************************************************************************************************************************/
void Copter::init_ardupilot()
{
    //初始化usb,设置波特率115200------initialise serial port
    serial_manager.init_console();

    //初始化设备整体功能------------init vehicle capabilties
    init_capabilities();

    //打印初始化
    hal.console->printf("\n\nInit %s"
                        "\n\nFree RAM: %u\n",
                        AP::fwversion().fw_string,
                        (unsigned)hal.util->available_memory());

    //在控制台上报告固件版本代码(实际的EEPROM格式版本检查在Load参数参数函数中完成)
    // Report firmware version code expect on console (check of actual EEPROM format version is done in load_parameters function)
    //
    report_version();

    //从EEPROM加载参数--------------------load parameters from EEPROM
    load_parameters();

    // 循环时间。主循环更新的时间速率---------time per loop - this gets updated in the main loop() based on actual loop rate
    G_Dt = 1.0 / scheduler.get_loop_rate_hz();

#if STATS_ENABLED == ENABLED
    //初始化状态数据模块,主要是g2参数--------initialise stats module
    g2.stats.init();
#endif
    //设置DataFlash,记录log
    gcs().set_dataflash(&DataFlash);

    //正确识别地面站---------------------- identify ourselves correctly with the ground station
    mavlink_system.sysid = g.sysid_this_mav;
    
    //初始化所有串口---------------------- initialise serial ports
    serial_manager.init();

    //设置第一端口(usb)以允许BoardConfig报告错误--- setup first port early to allow BoardConfig to report errors
    gcs().chan(0).setup_uart(serial_manager, AP_SerialManager::SerialProtocol_MAVLink, 0);


    // Register mavlink_delay_cb, which will run anytime you have
    // more than 5ms remaining in your call to hal.scheduler->delay
    //注册mavlink_delay_cb()回调函数,将允许任何时候允许,超过5ms时间将调用延迟
    hal.scheduler->register_delay_callback(mavlink_delay_cb_static, 5);
    
    //板层初始化,包含gpio,rc,pwm,sbus等
    BoardConfig.init();
#if HAL_WITH_UAVCAN
    BoardConfig_CAN.init(); //can
#endif

    //夹子初始化-----init cargo gripper
#if GRIPPER_ENABLED == ENABLED
    g2.gripper.init();
#endif

    //绞车和轮编码器---- init winch and wheel encoder
    winch_init();

    //初始化通知系统,主要led,beep------ initialise notify system
    notify.init();
    notify_flight_mode();

    //初始化电池监控器----- initialise battery monitor
    battery.init();

    //初始化RSSI---- Init RSSI
    rssi.init();
    
    barometer.init();

    //设置TELM插槽为串行端口-----setup telem slots with serial ports
    gcs().setup_uarts(serial_manager);

#if FRSKY_TELEM_ENABLED == ENABLED
    // setup frsky, and pass a number of parameters to the library
    frsky_telemetry.init(serial_manager,
                         get_frame_mav_type(),
                         &ap.value);
    frsky_telemetry.set_frame_string(get_frame_string());
#endif

#if DEVO_TELEM_ENABLED == ENABLED
    // setup devo
    devo_telemetry.init(serial_manager);
#endif

#if OSD_ENABLED == ENABLED
    osd.init();
#endif

#if LOGGING_ENABLED == ENABLED
    log_init();
#endif

    // update motor interlock state
    update_using_interlock();

#if FRAME_CONFIG == HELI_FRAME
    // trad heli specific initialisation
    heli_init();
#endif
#if FRAME_CONFIG == HELI_FRAME
    input_manager.set_loop_rate(scheduler.get_loop_rate_hz());
#endif
    //初始化遥控器输入
    init_rc_in();               // sets up rc channels from radio

    //如果可能的话,默认框架类与固件匹配----- default frame class to match firmware if possible
    set_default_frame_class();

    //配置电机类,分配电机----- allocate the motors class
    allocate_motors();

    //设置电机并输出到ESCS---- sets up motors and output to escs
    init_rc_out();

    //电机初始化,所以可以发送参数------ motors initialised so parameters can be sent
    ap.initialised_params = true;

    relay.init();

    /*
     *  setup the 'main loop is dead' check. Note that this relies on
     *  the RC library being initialised.
     */
    //设置“主循环是死的”检查。请注意,这依赖于RC库正在初始化。
    hal.scheduler->register_timer_failsafe(failsafe_check_static, 1000);

#if BEACON_ENABLED == ENABLED
    // give AHRS the range beacon sensor
    ahrs.set_beacon(&g2.beacon);
#endif

    //GPS初始化----Do GPS init
    gps.set_log_gps_bit(MASK_LOG_GPS);
    gps.init(serial_manager);
    //罗盘使能
    init_compass();

    //光流使能
#if OPTFLOW == ENABLED
    // make optflow available to AHRS
    ahrs.set_optflow(&optflow);
#endif

    //初始化位置类----init Location class
#if AP_TERRAIN_AVAILABLE && AC_TERRAIN
    Location_Class::set_terrain(&terrain);
    wp_nav->set_terrain(&terrain);
#endif

#if AC_AVOID_ENABLED == ENABLED
    wp_nav->set_avoidance(&avoid);
    loiter_nav->set_avoidance(&avoid);
#endif

    attitude_control->parameter_sanity_check();
    //设置位置控制时间
    pos_control->set_dt(scheduler.get_loop_period_s());

    //初始化光流------init the optical flow sensor
    init_optflow();

#if MOUNT == ENABLED
    //初始化摄像机挂载---- initialise camera mount
    camera_mount.init(serial_manager);
#endif

#if PRECISION_LANDING == ENABLED
    //初始化精确着陆---- initialise precision landing
    init_precland();
#endif

    //起落架位置初始化----- initialise landing gear position
    landinggear.init();

#ifdef USERHOOK_INIT
    USERHOOK_INIT
#endif

#if HIL_MODE != HIL_MODE_DISABLED
    while (barometer.get_last_update() == 0) {
        // the barometer begins updating when we get the first
        // HIL_STATE message
        gcs().send_text(MAV_SEVERITY_WARNING, "Waiting for first HIL_STATE message");
        delay(1000);
    }

    // set INS to HIL mode
    ins.set_hil_mode();
#endif

    //在地面读出气压--- read Baro pressure at ground
    //-----------------------------
    barometer.set_log_baro_bit(MASK_LOG_IMU);
    barometer.calibrate(); //气压计校准

    //初始化测距仪-----------initialise rangefinder
    init_rangefinder();

    //初始化近距离传感器-------init proximity sensor
    init_proximity();

#if BEACON_ENABLED == ENABLED
    //初始化非GPS位置估计----- init beacons used for non-gps position estimation
    g2.beacon.init();
#endif

    //初始化视觉测距法------- init visual odometry
    init_visual_odom();

#if RPM_ENABLED == ENABLED
    //初始化AP_RPM库------ initialise AP_RPM library
    rpm_sensor.init();
#endif

#if MODE_AUTO_ENABLED == ENABLED
    //初始化任务库---- initialise mission library
    mission.init();
#endif

#if MODE_SMARTRTL_ENABLED == ENABLED
    //初始化智能返航----------initialize SmartRTL
    g2.smart_rtl.init();
#endif

    //初始化Dataflash库------initialise DataFlash library
#if MODE_AUTO_ENABLED == ENABLED
    DataFlash.set_mission(&mission);
#endif

    //初始化设备log
    DataFlash.setVehicle_Startup_Log_Writer(FUNCTOR_BIND(&copter, &Copter::Log_Write_Vehicle_Startup_Messages, void));

    //初始化rc通道,包含设置模式-----initialise rc channels including setting mode
    rc().init();

    //初始化惯性传感器
    startup_INS_ground();

    //设置着陆--------标志set landed flags
    set_land_complete(true);
    set_land_complete_maybe(true);

    // we don't want writes to the serial port to cause us to pause
    // mid-flight, so set the serial ports non-blocking once we are
    // ready to fly
    //我们不希望写入串行端口使我们中途暂停,所以设置串行端口非阻塞,一旦我们准备飞行
    serial_manager.set_blocking_writes_all(false);

    //启用CPU故障安全----- enable CPU failsafe
    failsafe_enable();

    ins.set_log_raw_bit(MASK_LOG_IMU_RAW);

    //启用电机输出------------ enable output to motors
    if (arming.rc_calibration_checks(true))
    {
        enable_motor_output();
    }

    //如果请求不使能安全开关------ disable safety if requested
    BoardConfig.init_safety();

    //终端打印可以飞行
    hal.console->printf("\nReady to FLY ");

    //初始化已完成的标志------- flag that initialisation has completed
    ap.initialised = true;
}


加载任务列表:

 scheduler.init(&scheduler_tasks[0], ARRAY_SIZE(scheduler_tasks), MASK_LOG_PM);
const AP_Scheduler::Task Copter::scheduler_tasks[] =
{
    SCHED_TASK(rc_loop,              100,    130), //遥控器1-4通道,和5通道模式
    SCHED_TASK(throttle_loop,         50,     75),
    SCHED_TASK(update_GPS,            50,    200),
#if OPTFLOW == ENABLED
    SCHED_TASK(update_optical_flow,  200,    160),
#endif
    SCHED_TASK(update_batt_compass,   10,    120),
    SCHED_TASK(read_aux_all,          10,     50),
    SCHED_TASK(arm_motors_check,      10,     50), //解锁,上锁实现代码
#if TOY_MODE_ENABLED == ENABLED
    SCHED_TASK_CLASS(ToyMode,              &copter.g2.toy_mode,         update,          10,  50),
#endif
    SCHED_TASK(auto_disarm_check,     10,     50),
    SCHED_TASK(auto_trim,             10,     75),
#if RANGEFINDER_ENABLED == ENABLED
    SCHED_TASK(read_rangefinder,      20,    100),
#endif
#if PROXIMITY_ENABLED == ENABLED
    SCHED_TASK_CLASS(AP_Proximity,         &copter.g2.proximity,        update,         100,  50),
#endif
#if BEACON_ENABLED == ENABLED
    SCHED_TASK_CLASS(AP_Beacon,            &copter.g2.beacon,           update,         400,  50),
#endif
#if VISUAL_ODOMETRY_ENABLED == ENABLED
    SCHED_TASK(update_visual_odom,   400,     50),
#endif
    SCHED_TASK(update_altitude,       10,    100),
    SCHED_TASK(run_nav_updates,       50,    100),
    SCHED_TASK(update_throttle_hover,100,     90),
#if MODE_SMARTRTL_ENABLED == ENABLED
    SCHED_TASK_CLASS(Copter::ModeSmartRTL, &copter.mode_smartrtl,       save_position,    3, 100),
#endif
#if SPRAYER_ENABLED == ENABLED
    SCHED_TASK_CLASS(AC_Sprayer,           &copter.sprayer,             update,           3,  90),
#endif
    SCHED_TASK(three_hz_loop,          3,     75),
    SCHED_TASK_CLASS(AP_ServoRelayEvents,  &copter.ServoRelayEvents,      update_events, 50,     75),
    SCHED_TASK_CLASS(AP_Baro,              &copter.barometer,           accumulate,      50,  90),

//	SCHED_TASK(update_ukf,           200,     200), //ukf运算

#if PRECISION_LANDING == ENABLED
    SCHED_TASK(update_precland,      400,     50),
#endif
#if FRAME_CONFIG == HELI_FRAME
    SCHED_TASK(check_dynamic_flight,  50,     75),
#endif
#if LOGGING_ENABLED == ENABLED
    SCHED_TASK(fourhundred_hz_logging,400,    50),
#endif
    SCHED_TASK_CLASS(AP_Notify,            &copter.notify,              update,          50,  90),
    SCHED_TASK(one_hz_loop,            1,    100),
    SCHED_TASK(ekf_check,             10,     75),
    SCHED_TASK(gpsglitch_check,       10,     50),
    SCHED_TASK(landinggear_update,    10,     75),
    SCHED_TASK(lost_vehicle_check,    10,     50),

    SCHED_TASK(gcs_update,           400,    180), //更新数据---2.5ms
    SCHED_TASK(gcs_send_heartbeat,     1,    110), //发送心跳包---1s
    SCHED_TASK(gcs_send_deferred,     50,    550), //发送输入---20ms
    SCHED_TASK(gcs_data_stream_send,  50,    550), //发送数据流--20ms

#if MOUNT == ENABLED
    SCHED_TASK_CLASS(AP_Mount,             &copter.camera_mount,        update,          50,  75),
#endif
#if CAMERA == ENABLED
    SCHED_TASK_CLASS(AP_Camera,            &copter.camera,              update_trigger,  50,  75),
#endif
#if LOGGING_ENABLED == ENABLED
    SCHED_TASK(ten_hz_logging_loop,   10,    350),
    SCHED_TASK(twentyfive_hz_logging, 25,    110),
    SCHED_TASK_CLASS(DataFlash_Class,      &copter.DataFlash,           periodic_tasks, 400, 300),
#endif
    SCHED_TASK_CLASS(AP_InertialSensor,    &copter.ins,                 periodic,       400,  50),
    SCHED_TASK_CLASS(AP_Scheduler,         &copter.scheduler,           update_logging, 0.1,  75),
#if RPM_ENABLED == ENABLED
    SCHED_TASK(rpm_update,            10,    200),
#endif
    SCHED_TASK(compass_cal_update,   100,    100),
    SCHED_TASK(accel_cal_update,      10,    100),
    SCHED_TASK_CLASS(AP_TempCalibration,   &copter.g2.temp_calibration, update,          10, 100),
#if ADSB_ENABLED == ENABLED
    SCHED_TASK(avoidance_adsb_update, 10,    100),
#endif
#if ADVANCED_FAILSAFE == ENABLED
    SCHED_TASK(afs_fs_check,          10,    100),
#endif
#if AC_TERRAIN == ENABLED
    SCHED_TASK(terrain_update,        10,    100),
#endif
#if GRIPPER_ENABLED == ENABLED
    SCHED_TASK_CLASS(AP_Gripper,           &copter.g2.gripper,          update,          10,  75),
#endif
#if WINCH_ENABLED == ENABLED
    SCHED_TASK(winch_update,          10,     50),
#endif
#ifdef USERHOOK_FASTLOOP
    SCHED_TASK(userhook_FastLoop,    100,     75),
#endif
#ifdef USERHOOK_50HZLOOP
    SCHED_TASK(userhook_50Hz,         50,     75),
#endif
#ifdef USERHOOK_MEDIUMLOOP
    SCHED_TASK(userhook_MediumLoop,   10,     75),
#endif
#ifdef USERHOOK_SLOWLOOP
    SCHED_TASK(userhook_SlowLoop,     3.3,    75),
#endif
#ifdef USERHOOK_SUPERSLOWLOOP
    SCHED_TASK(userhook_SuperSlowLoop, 1,   75),
#endif
    SCHED_TASK_CLASS(AP_Button,            &copter.g2.button,           update,           5, 100),
#if STATS_ENABLED == ENABLED
    SCHED_TASK_CLASS(AP_Stats,             &copter.g2.stats,            update,           1, 100),
#endif
#if OSD_ENABLED == ENABLED
    SCHED_TASK(publish_osd_info, 1, 10),
#endif
};



5. g_callbacks->loop()函数



/***********************************************************************************************************************
*函数原型:void Copter::loop()
*函数功能:核心循环函数
*修改日期:2018-9-12
*修改作者:cihang_uav
*备注信息:
*************************************************************************************************************************/
void Copter::loop()
{
    scheduler.loop();
    G_Dt = scheduler.get_last_loop_time_s();
}

void AP_Scheduler::loop()
{
	//等待INS数据采集完毕-------------------------wait for an INS sample
	    AP::ins().wait_for_sample();

	    //获取系统采样时间
	    const uint32_t sample_time_us = AP_HAL::micros();

	    //开始时间等于0?
	    if (_loop_timer_start_us == 0)
	    {
	        _loop_timer_start_us = sample_time_us;
	        _last_loop_time_s = get_loop_period_s();
	    } else
	    {
	        _last_loop_time_s = (sample_time_us - _loop_timer_start_us) * 1.0e-6; //得到执行整个loop函数的时间
	    }

	    //执行快速循环函数----Execute the fast loop
	    // ---------------------
	    if (_fastloop_fn)
	    {
	    	// hal.uartG->printf("_fastloop_fn=%d\r\n",_fastloop_fn); //loop_us=2500,这个单位是us
	    	// hal.uartG->printf("MMM\r\n"); //loop_us=2500,这个单位是us
	        _fastloop_fn(); //该函数会通过指针函数知识,调用fast_loop()函数
	       // hal.uartG->printf("NNN\r\n"); //loop_us=2500,这个单位是us
	    }

	    //高速度调度器,一个任务节拍已经执行------tell the scheduler one tick has passed
	    tick();
	    //运行所有要运行的任务。注意我们只是必须按每个循环调用一次,因为任务是按计划进行的,并且每个任务是主循环的倍数。
	    //所以如果在第一次运行主循环时,任务表中的任务有不运行的任务.将等待下一次任务调度运行到来,才有可能会被运行。
	    //这里我举个例子:主任务时间2.5ms,其中一个数组表任务时间是10ms,那么需要运行四次loop才可能运行数组表中的任务,前三次就是上面说的情况
	    // run all the tasks that are due to run. Note that we only
	    // have to call this once per loop, as the tasks are scheduled
	    // in multiples of the main loop tick. So if they don't run on
	    // the first call to the scheduler they won't run on a later
	    // call until scheduler.tick() is called again
	    const uint32_t loop_us = get_loop_period_us();  //这个是主循环时间,400HZ,
	    //这里增加串口打印函数,
	 //   hal.uartG->printf("loop_us=%d\r\n",loop_us); //loop_us=2500,这个单位是us

	    //获取执行完fast_loop()函数后,剩余多少时间给数组表任务使用=开始+2.5ms-运行完上面fast_loop()到这的时间,获得剩余时间,主要给数组表任务使用
	    const uint32_t time_available = (sample_time_us + loop_us) - AP_HAL::micros();
	    //打印出剩余时间
	  //  hal.uartG->printf("time_available=%d\r\n",time_available); //time_available=2212..


	    //运行run()函数,运行数组表任务
	    run(time_available > loop_us ? 0u : time_available); //运行函数,上面计算剩余的时间都留给任务表中的任务去用。

	#if CONFIG_HAL_BOARD == HAL_BOARD_SITL
	    // move result of AP_HAL::micros() forward:
	    hal.scheduler->delay_microseconds(1);
	#endif

	    //检查loop时间-----check loop time
	    perf_info.check_loop_time(sample_time_us - _loop_timer_start_us);

	    //重新赋值loop起始时间,下次计算loop运行时间使用
	    _loop_timer_start_us = sample_time_us;

}

运行fast_loop(),运行run()函数,运行数组表任务,第一个例程已经讲解

1.运行fast_loop()函数

/***********************************************************************************************************************
*函数原型:void Copter::fast_loop()
*函数功能:快速函数
*修改日期:2018-9-10
*修改作者:cihang_uav
*备注信息:Main loop - 400hz,运行周期是2.5ms
*************************************************************************************************************************/

void Copter::fast_loop()
{
	//hal.uartG->printf("NNN0\r\n"); //loop_us=2500,这个单位是us
    // update INS immediately to get current gyro data populated
    ins.update();

    // run low level rate controllers that only require IMU data
    attitude_control->rate_controller_run();

    // send outputs to the motors library immediately
    motors_output();

    // run EKF state estimator (expensive)
    // --------------------
    read_AHRS();

#if FRAME_CONFIG == HELI_FRAME
    update_heli_control_dynamics();
#endif //HELI_FRAME

    // Inertial Nav
    // --------------------
    read_inertia();

    // check if ekf has reset target heading or position
    check_ekf_reset();

    //运行姿态控制器------run the attitude controllers
    update_flight_mode();

    // update home from EKF if necessary
    update_home_from_EKF();

    // check if we've landed or crashed
    update_land_and_crash_detectors();

#if MOUNT == ENABLED
    // camera mount's fast update
    camera_mount.update_fast();
#endif

    // log sensor health
    if (should_log(MASK_LOG_ANY))
    {
        Log_Sensor_Health();
    }
   // hal.uartG->printf("NNN1\r\n"); //loop_us=2500,这个单位是us
}

2.运行run()函数

void AP_Scheduler::run(uint32_t time_available)
{
//	hal.uartG->printf("AP_Scheduler Run\r\n");
    uint32_t run_started_usec = AP_HAL::micros();   //运行开始时间
    uint32_t now = run_started_usec;                //记录这个值,开始运行时间
   //调试
    if (_debug > 1 && _perf_counters == nullptr)
    {
        _perf_counters = new AP_HAL::Util::perf_counter_t[_num_tasks];
        if (_perf_counters != nullptr)
        {
            for (uint8_t i=0; i<_num_tasks; i++)
            {
                _perf_counters[i] = hal.util->perf_alloc(AP_HAL::Util::PC_ELAPSED, _tasks[i].name); //性能计数
            }
        }
    }
    //通过for循环,在允许的时间范围内,尽可能的多执行任务表中的任务,这里举个例子,假如我们还剩1.5ms的时间给剩余的任务表使用;
    for (uint8_t i=0; i<_num_tasks; i++)
    {

    	hal.uartG->printf("i=%d\r\n",i); //

        uint16_t dt = _tick_counter - _last_run[i]; //_last_run[i]:执行一个loop,记录的任务表中每个任务被被执行的次数,因此dt表示上次运行的那个任务到现在这里的圈数
     //   hal.uartG->printf("_tick_counter=%d\r\n",_tick_counter); //time_available=2212..
      //  hal.uartG->printf("_last_run[i]=%d\r\n",_last_run[i]); //time_available=2212..


        //_loop_rate_hz 是循环的频率,这里是400Hz,_tasks[i].rate_hz是任务表中的某个任务的频率,假如是20Hz,那么interval_ticks =20,
        //那就应该是运行了20圈后,执行该任务。
        uint16_t interval_ticks = _loop_rate_hz / _tasks[i].rate_hz; //这个值表示执行某个任务需要的圈数
        if (interval_ticks < 1) //如果这个值小于1,就默认是1,一般小于很少
        {
            interval_ticks = 1;
        }
        if (dt < interval_ticks) //如果dt= interval_ticks*2)
        {
            // we've slipped a whole run of this task!
            debug(2, "Scheduler slip task[%u-%s] (%u/%u/%u)\n",
                  (unsigned)i,
                  _tasks[i].name,
                  (unsigned)dt,
                  (unsigned)interval_ticks,
                  (unsigned)_task_time_allowed);
        }
        //这个时间太大是不行,不会运行
        if (_task_time_allowed > time_available)
        {
            // not enough time to run this task.  Continue loop -
            // maybe another task will fit into time remaining
            continue;
        }

        // run it
        _task_time_started = now;
        current_task = i;
        if (_debug > 1 && _perf_counters && _perf_counters[i])
        {
            hal.util->perf_begin(_perf_counters[i]);
        }
      //  hal.uartG->printf("BBB\r\n"); //time_available=2212..
        //运行任务函数
        _tasks[i].function();
      //  hal.uartG->printf("BBB12\r\n"); //time_available=2212..
        if (_debug > 1 && _perf_counters && _perf_counters[i])
        {
            hal.util->perf_end(_perf_counters[i]);
        }
        current_task = -1;

        // record the tick counter when we ran. This drives
        // when we next run the event
        _last_run[i] = _tick_counter;

        // work out how long the event actually took
        now = AP_HAL::micros();
        uint32_t time_taken = now - _task_time_started;

        if (time_taken > _task_time_allowed)
        {
            // the event overran!
            debug(3, "Scheduler overrun task[%u-%s] (%u/%u)\n",
                  (unsigned)i,
                  _tasks[i].name,
                  (unsigned)time_taken,
                  (unsigned)_task_time_allowed);
        }
        if (time_taken >= time_available)
        {
            time_available = 0;
            break;
        }
        time_available -= time_taken;
    }

    // update number of spare microseconds
    _spare_micros += time_available;

    _spare_ticks++;
    if (_spare_ticks == 32)
    {
        _spare_ticks /= 2;
        _spare_micros /= 2;
    }
}

*到这里所有任务全速运行



4.后续分析要点:



1.waf怎么编译

2.STM32F1协处理器与STM32F7主处理器怎么建立联系?

3.Bootloader怎么识别更新固件代码?

4.固件代码的起始地址是多少?

5.反汇编代码

你可能感兴趣的:(ardupilot学习)