本节继续研究Chibios,欢迎批评指正!!!
这里我使用的是ubuntu系统,编译代码用的是gcc-arm-none-eabi-5_4-2016q3,在进入代码分析之前我的疑惑
1.我看gcc-arm-none-eabi-5_4就是不顺眼,这是什么鬼?
答;这鬼是一种交叉编译工具,更详细的是下面内容
还要注意: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 ***
这部分还没有细致分析
#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) //头文件包含
定义启动代码时,需要的一些函数,这些函数在汇编中会跳转过来
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
}
/** @} */
上述文件需要注意的函数:
//内核初始化函数,会被在汇编使用
void __core_init(void)
{
#if CORTEX_MODEL == 7
SCB_EnableICache();//使能I-cache指令缓存
SCB_EnableDCache();//使能D-cache数据缓存。
#endif
}
/***********************************************************************************************************************
*函数原型: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);
}
/***********************************************************************************************************************
*函数原型: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
}
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
}
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
}
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
}
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%"
}
/**
* @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
}
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
/**
/**
* @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__) */
/** @} */
#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
........后面内容省略......
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();
}
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;
}
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
}
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:二进制信号量
那么我们的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线程讲解
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);
}
下面说明上面的执行过程:定时器进程首先运行,后面会加载注册了多少定时器线程,采用二进制信号量来保证每个定时器线程的正常运行
如果_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();
}
}
//设置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
}
#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); //释放二进制信号量
}
#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
/***********************************************************************************************************************
*函数原型: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
};
/***********************************************************************************************************************
*函数原型: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;
}
}
*到这里所有任务全速运行