当前位置:   article > 正文

(五)Betaflight 移植到keil——BF_OS运行_betaflight二次开发环境 keil

betaflight二次开发环境 keil

目录

5.6.4.1 __attribute__ 工具

5.6.4.2 scheduler();函数

(1) micros() 函数

1. register关键字

2. SCB(系统控制块)ICSR(中断控制与状态寄存器)的VECTACTiVE(Active vector)

3. 关于引用#include "platform.h"的__get_BASEPRI()

4. microsISR()函数

(2) inline内联函数关键字

(3) Systick用来计算任务运行时间?

(4)schedulerExecuteTask 执行任务函数

(5) 扫描任务队列


BF_OS的通过在While循环中运行run();函数来实现任务调试,run();原型为

  1. void FAST_CODE run(void)
  2. {
  3.     while (true) {
  4. //        scheduler();
  5. #ifdef SIMULATOR_BUILD
  6.         delayMicroseconds_real(50); // max rate 20kHz
  7. #endif
  8.     }
  9. }

注:可能由于编译环境不同,BF源码中的一些.h文件中缺乏某些宏定义,需要自己在其中用#include引用相应头文件。初步猜测,是因为Linux下的gun在编译时将某些宏定义赋予了“全局”属性。

5.6.4.1 __attribute__ 工具

  1. void FAST_CODE run(void) 的定义中有一个FAST_CODE宏定义,该宏定义为:
  2. #define FAST_CODE                   __attribute__((section(".tcm_code"))) __attribute__((optimize(ITCM_RAM_OPTIMISATION)))

 __attribute__,嗯猜测应该算是一个宏定义,可以在C语言中向编译器指定一些操作,如用于控制变量和函数的属性,以及指定变量的地址和段地址。BF源码是在Linux下用GUN编译器中编译,而Keil MDK中使用ARMCC,两者都有这个工具,但具体功能有所不同。比如ARMCC似乎是没有对“optimize”的定义,这个是用来设置程序优化程度为-O2的,Keil是在“魔术棒”(目标选项)有定义。这里把该宏定义更改为:

#define FAST_CODE                   __attribute__((section(".tcm_code")))

__attribute__使用参考:

在 Keil MDK 中控制变量和函数属性,以及指定变量地址和段地址_WellAndy的博客-CSDN博客

Keil 优化等级说明参考:

记录一下KEIL编译器的优化等级及说明_keil优化等级设置多少合适-CSDN博客

这里将Keil程序优化等级设置为了level -O1,在后期将尝试改为level -O2

5.6.4.2 scheduler();函数

函数原型有点长:

  1. FAST_CODE void scheduler(void)
  2. {
  3.     static uint32_t checkCycles = 0;
  4.     static uint32_t scheduleCount = 0;
  5. #if !defined(UNIT_TEST)
  6.     const timeUs_t schedulerStartTimeUs = micros();
  7. #endif
  8.     timeUs_t currentTimeUs;
  9.     uint32_t nowCycles;
  10.     timeUs_t taskExecutionTimeUs = 0;
  11.     task_t *selectedTask = NULL;
  12.     uint16_t selectedTaskDynamicPriority = 0;
  13.     uint32_t nextTargetCycles = 0;
  14.     int32_t schedLoopRemainingCycles;
  15. ……
  16. }

(1) micros() 函数

在“\src\main\drivers\system.c”中定义,原型为:

  1. uint32_t micros(void)
  2. {
  3.     register uint32_t ms, cycle_cnt; //将变量的值保存在 内核通用寄存器中
  4.     // Call microsISR() in interrupt and elevated (non-zero) BASEPRI context
  5. //SCB(系统控制块)的中断控制与状态寄存器的 异常序号
  6.     if ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) || (__get_BASEPRI())) {
  7.         return microsISR();
  8.     }
  9.     do {
  10.         ms = sysTickUptime;
  11.         cycle_cnt = SysTick->VAL;
  12.     } while (ms != sysTickUptime || cycle_cnt > sysTickValStamp);
  13.     return (ms * 1000) + (usTicks * 1000 - cycle_cnt) / usTicks;
  14. }

关于micros(void)的声明又是一个坑,uint32_t micros(void)函数并未在自己的“system.h”头文件声明,而是在“\src\main\drivers\time.h”中有声明:

  1. timeUs_t micros(void);
  2. timeUs_t microsISR(void);
  3. timeMs_t millis(void);

timeUs_t是一个typedef类型定义,其实就是uint32_t类型。所以,如果你想用这个micros函数,要使用#include "drivers/time.h"而不是#include "drivers/system.h"

1. register关键字

一般程序中定义的变量的值在处理器启动与程序运行后都会依次调用到内存中。但使用register定义的变量在调用时却是被存储在处理内核通用寄存器(即R0~R13)中的,意味着该变量会作为一个寄存器变量,让该变量的访问速度达到最快。

参考:

C语言——register_c语言register-CSDN博客

2. SCB(系统控制块)ICSR(中断控制与状态寄存器)的VECTACTiVE(Active vector)

这个寄存器的位储存着当前活动的“异常号”。“异常号”是处理器中各类定义的“异常”的序号,包括复位、Systick、外设中断(IRQ)等。通常32学习中所遇到的中断都属于外设中断(IRQ),而IRQ只是作为Cotex-M4内核中的一种异常,具体实现是由芯片厂家设计的。当然“异常”都有各自的中断,但两者还是有区别的,两者是因果关系发生异常不一定引起中断,中断与异常的编号也是两个系统。

Cotex-M4为每个异常都分配了一个编号,

具体参考“2、参考资料\STM32F3与F4系列Cortex M4内核编程手册.pdf”中“2.3.2 Exception types”与“4.4.3 Interrupt control and state register (ICSR)”章节。

3. 关于引用#include "platform.h"的__get_BASEPRI()

"platform.h"文件即“平台”头文件,其中包含了对AT32VMT7库头文件的调用,而AT32库中又引用了一些core内核库头文件与cmsis(标准接口)库头文件。

综上所述,__get_BASEPRI()就是一个cmsis头文件中的宏定义,位于“\lib\main\AT32F43x\cmsis\cm4\core_support\cmsis_armcc.h”中,可以通过汇编语言得内核寄存器“BASEPRI”的值。

BASEPRI保存的值猜测也为之前据说的“异常号”。首先这个BASEPRI值所对应的“异常”的中断是有优先级的,如n,那么处理器会屏避优先级大于等于n的中断。当BASEPRI为0时则该寄存器不起作用。

BASEPRI默认为16,即IRQ的异常号?

4. microsISR()函数

函数原型为:

  1. uint32_t microsISR(void)
  2. {
  3.     register uint32_t ms, pending, cycle_cnt;
  4.     ATOMIC_BLOCK(NVIC_PRIO_MAX) {
  5.         cycle_cnt = SysTick->VAL;
  6.         if (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) {
  7.             // Update pending.
  8.             // Record it for multiple calls within the same rollover period
  9.             // (Will be cleared when serviced).
  10.             // Note that multiple rollovers are not considered.
  11.             sysTickPending = 1;
  12.             // Read VAL again to ensure the value is read after the rollover.
  13.             cycle_cnt = SysTick->VAL;
  14.         }
  15.         ms = sysTickUptime;
  16.         pending = sysTickPending;
  17.     }
  18.     return ((ms + pending) * 1000) + (usTicks * 1000 - cycle_cnt) / usTicks;
  19. }

ATOMIC_BLOCK是一个宏定义,猜测是用来原子操作、

初步猜测其功能是得到当前us级Systick计数转值,其中有调用SCB、BASEPRI等内核寄存器,目前还没搞清楚具体作用。

注:“us级”这里指将对应时钟计数转化为以us为单位。

(2) inline内联函数关键字

437行有程序

schedLoopRemainingCycles = cmpTimeCycles(nextTargetCycles, nowCycles); //返回next - now

调用了cmpTimeCycles函数,这是一个定义在“\src\main\common\time.h”中的一个函数。原型为:

static inline int32_t cmpTimeCycles(uint32_t a, uint32_t b) { return (int32_t)(a - b); }

其中使用了inline 内联函数关键字,这个关键字定义的函数与宏定义有着极其相似的功能,在编译时会将定义函数的内容复制到调用处,然后编译。

例如:

c = cmpTimeCycles(a, b );

实际编译为:

c = a - b;

这样就避免了频繁调用函数对栈内存重复开辟所带来的消耗。但内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。

参考:

内联函数 —— C 中关键字 inline 用法解析(转载)_c inline-CSDN博客

(3) Systick用来计算任务运行时间?

(4)schedulerExecuteTask 执行任务函数

参数:

task_t *selectedTask 任务控制块指针

timeUs_t currentTimeUs 当前us级OS计数值

功能是执行输入任务控制块对应的任务函数,该函数原型为:

(5) 扫描任务队列

在程序612行,进行任务队列扫描。

  1.  for (task_t *task = queueFirst(); task != NULL; task = queueNext()) {
  2.     ……
  3. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/495988
推荐阅读
相关标签
  

闽ICP备14008679号