软件框架讲解(5.0及以前)
飞控源代码部分,都是属于一砖一瓦敲出来的。没有使用实时操作系统(RTOS),我们称之为裸机代码,托管在Github上,名字为crazepony-firmware-none,尾缀none表示未使用操作系统裸跑的意思。
那么,现在就结合裸机代码,来说说Crazepony的软件框架。
本文档以Crazepony 5.0版本为基础。Crazepony 5.0版本及以前的代码主要由马骏(CamelGo)完成。贡献者黄永祥在5.1版本中对飞控代码进行了重构,将Crazepony的稳定性推向了一个新的高度。贡献者Nieyong在5.2版本中对代码进行了整理。
软件流程图
总体的流程图,就是这么简单。使用定时器4产生中断来置标志位,弄外使用定时器3来产生中断串口输出里面的任务,是整个飞机的核心。下面具体介绍实现细节。
初始化
学过51单片机的都知道,任何一个处理器要正常运行后面的代码,首先必须得有一大段设备初始化的代码先运行,这些代码用于初始化处理器的内部时钟、中断优先级、I/O口的输入输出方向等等,也就是为后续代码正常运行,做了一个环境配置准备。
Crazepony的主控是Crotex-M3内核,是ARM架构发展到一定阶段的产物。Crotex-M3是什么呢?还是ARM架构。于是,对ARM的初始化,首先必须要做的就是系统时钟初始化,中断向量表初始化,中断优先级初始化,I/O方向初始化,如下:
STM32内部模拟EEPROM初始化->LED初始化->延时函数初始化->蓝牙电源使能初始化->电机PWM输出初始化->电池电压AD初始化->IIC总线初始化->传感器初始化->PID参数初始化->无线收发模块初始化为接受模式->开蓝牙->开定时器3->开定时器4。
初始化看起来很繁杂,很多,也没啥好说的。
接下来 ,程序运行到死循环while(1)
,程序会一直停在这里,等待数据中断的到来,而不是死机死在这里,这是有区别的,学过51的人都知道,我不再多说。
在初始化代码段,我们说到初始化了两个定时器,一个定时器3,一个定时器4,这两个定时器都可以打断死循环while(1)。定时器3用于广播机身姿态信息,定时器4的任务要繁重得多,用于更新遥控数据+机身姿态融合+PID计算输出+PWM输出。可以看到,定时器4里面任务的优先级明显要比定时器3实时性要求更高,所以。中断优先级的顺序是:定时器4 > 串口中断 > 定时器3。姿态更新频率为1000Hz,广播信息更新频率为1Hz。
综上,有点乱,但是我们缕一缕。很简单,只有3个中断。定时器4是核心中断,所有的算法都是在这里实现的,机身的稳定也是靠这个中断来实现的
定时器4
可以看到定时器4的中断服务函数TIM4_IRQHandler()
中,有个一Controler()
。
而Controler()
内部,DMP姿态输出->接收遥控器数据->接收串口数据->PID计算+PWM输出,这些任务构成了Controler()
函数。
定时器3
Crazepony在飞行过程中,会向上位机发送姿态数据。于是,我们用了一个定时器来处理串口发送数据的问题。
在Crazepony上,ISP下载是通过UART1来实现的,有线串口打印用的UART1,2.1蓝牙透传也是接的UART1。所以,为了避免蓝牙透传和有线串口之间的数据冲突,我将蓝牙的供电设计成了软件使能方式启动蓝牙电源。这样一来,就可以程控切换数据通道,保证数据正常。
在回到定时器3的功能上来,先看具体程序段:
从定时器3的中断服务子程序可以看到,每进一次中断,向串口打印一次logo以及相关的姿态信息数据。此时,如果连接的是mircousb线,那么用串口助手可以看到如图所示的姿态信息反馈。
说到这里,有必要说一个事情就是,中断优先级的问题。
由于姿态数据对实时性要求是最高的,所以,处理姿态的代码应该是优先执行的,所以,定时器4的优先级要高于串口打印的优先级,即定时器4 > 串口中断 > 定时器3。