PH47代码框架软件二次开发极简教程
1. 教程说明
本教程适用于对飞控及Stm32程序设计比较熟悉的二次开发者快速掌握PH47框架的使用要点。本教程仅对PH47框架中最主要的二次开发特性进行简要说明,建议与框架中\DevStudio\Algorithms\Controller_Demo.cpp(.h)示例代码配合学习。关于二次开发特性中的详细或高级内容,请参见《PH47框架二次开发教程》。
对于飞控或是Stm32编程不是太熟练的开发者,建议先了解学习Stm32程序设计相关内容,以及《PH47框架二次开发教程》。
1.1 基本概念
- 时间相关、循环控制、调试功能大部分基本功能由框架层全局函数实现。
- 整个PH47框架内,数据读取、写入、交换等操作均依托框架层的数据总线系统(frm.bus),以frm.bus.Function()方式使用。
- 机载调参参数(控制参数)的读取、写入存储均通过框架层的参数系统(core.para)实现,以core.para.Function()方式使用。
- 用户代码一般位于应用层Application当中实现。应用层为用户提供了初始化、快速循环,普通循环、慢速循环函数,以及GCS上行指令响应函数、控制台指令响应函数等重要函数框架供用户扩充使用。
2. 基本全局功能函数使用
2.1 常用全局时间函数
函 数 | 功 能 描 述 |
HAL_Delay() | 用于ms 级延时,执行时独占系统资源 |
osDelay() | 用于ms 级延时。osDelay() 依赖于 FreeRTOS 启动完成,延时期间可以将系统资源轮换给其他线程使用 |
gDelay_us() | 用于 us 级别延时。延时时间不要超过 1000us |
gGetMills() | 获取调用时刻距离 mcu 加电启动时刻的ms时间间隔, 获取数据范围为 4294967296ms,即1193 hour |
gGetMills_f() | 获取精确到小数点后3位的ms时间, 即精确到us。 gGetMills_f()在运行时间超过4800s后会溢出重置为 0 后重新开始计时 |
gGetMicros() | 获取mcu加电以来的us时间。 注意: us计时器溢出时间为600s |
2.2 调试功能使用
函 数 | 功 能 描 述 |
TRACE() | 使用邮件队列方式显示格式化字符串。 具有线程安全性,且连续调用能够确保顺序正确。依赖于 PH47 框架的启动完成, 框架启动前不能正常工作。显示时刻会晚于函数调用时刻(滞后不高于 10ms 级别) 。 TRACE("\r\ndemo> 1. TRACE at %.3fms", gGetMills_f()); |
gPrintf() | 使用中断方式显示格式化字符串。 不依赖于框架的启动完成。连续密集调用时显示的先后顺序可能会发生改变 gPrintf("\r\ndemo> 2. gPrintf at %.3fms", gGetMills_f()); |
ASSERT(f) | 表达式f真值确认。 若表示式f为false,则核心板状态指示LED连续快速闪烁,当前线程执行停止在ASSERT()语句处,同时在调试串口输出ASSERT语句所在的文件名及代码行号。 |
bus.arDbg_1[10] bus.arDbg_2[10] | 调试专用总线数据项。只需将调试数据赋值给总线数据项即可。 PH47框架已经预置了调试数据的下行发送机制,用户无需自己实现。 调试数据默认下行发送频率为25hz,运行过程中可调整。 |
3 使用CFreqDiv类进行循环频率控制
- 1/3: 在.h文件中定义一个CFreqDiv类对象_FreqDemo_1Hz。
CFreqDiv _FreqDemo_1Hz;
- 2/3: 适当位置对_FreqDemo_1Hz对象进行初始化,如在.cpp文件初始化函数中,设定循环间隔。
_FreqDemo_1Hz.SetRunInv_ms(1000); // 循环控制间隔为 1000ms, 即 1Hz
- 3/3: 在需要进行循环频率控制的位置通过IsSetRunInv_ms()函数判断是否达到循环控制条件。此处注意:_FreqDemo_1Hz对象的IsSetRunInv_ms()函数调用在同一循环中只能在一个位置出现一次,不能在多个不同位置多次调用。
if(_FreqDemo_1Hz.IsSetRunInv_ms())
{// do something…
}
4. 数据总线基本操作
数据总线系统为整个PH47代码框架提供了设置、获取、交换数据的方法和途径。以及获取诸如设置获取数据时间戳,数据设置时间间隔,数据权限控制等功能。
数据总线系统由一系列总线数据项(Bus item)构成,总线数据项既可以是包含了一个数据简单类型(如 uint8_t,int32,float等)的数据项,也可以是多个具有相关性的数据构成的复杂类型数据项,如结构变量。
4.1 数据总线系统文件包含
用户要在自己的代码文件中使用数据总线功能,只需在自己的.cpp文件开始出加入如下文件包含:
#include"../../Frame/UseGlobalObject.h" //文件包含路径根据实际情况确定
数据总线中数据项的声明位于如下文件中:
\DevStudio\BoardConfig\Board_BBDB\DataBus_BBDB.h
数据总线中复杂类型数据项(框架内置)的结构声明位于如下文件中:
\DevStudio\Frame\Core\BusItemFormatDef.h
4.2 数据总线功能函数使用
函 数 | 功 能 描 述 |
Get() | 读取数据总线数值 Vector3f vAcc = bus.sImu.AccRaw.Get(); // 获取总线数据项数值 TRACE("\r\n> Acc %.2f %.2f %.2f", vAcc.x, vAcc.y, vAcc.z); |
Set() | 设置数据总线数值。 本函数在设置总线数值的同时,对当前总线数据的时间戳进行了设置。 // demo 1 bus.arDbg_2[0].Set(2.234); // demo 2 MICOLINK_PAYLOAD_RANGE_SENSOR_t payload; payload.distance = 10.5f; payload.strength = 1.0f; bus.OptiFlowTof_MTF01.Set(&payload); // 设置总线数据项数值 |
GetTimeStamp_ms() | 获取 AccRaw 以 ms 为单位时间戳的整数部分,示例见下。 |
GetTmStampDec_us() | 获取 AccRaw 以 ms 为单位时间戳的 3 位小数部分(即精确到 us),示例见下。 |
GetDt2Prev_us() | 获取总线数据项最近两次被设置(Set)之间的时间间隔(us) // 上述三个函数综合示例 TRACE("\r\n> AccRaw timeTag:%d.%dms dt:%dus", bus.sImu.AccRaw.GetTimeStamp_ms(), // 时间戳整数部分(ms) bus.sImu.AccRaw.GetTmStampDec_us(), // 时间戳小数部分(us) bus.sImu.AccRaw.GetDt2Prev_us()); // 最近两次设置时间间隔(us) |
4.3 全局变量
全局变量变量是一种特殊的总线数据,用于在整个PH47代码框架内表示系统状态,分为bool型和uint8_t型两种类型。整个框架共有48个bool型,8个uint8_t类型全局变量。
PH47代码框架以及CSS插件已经预置了全局变量的发送及解析机制,以5Hz频率下行发送更新。用户在PH47框架中只需对其进行设置或读取即可。
函 数 | 功 能 描 述 |
gGetStatus() GetStatus() | 读取全局变量数值。 gGetStatus()等同于GetStatus(),只是前者为全局函数形式,后者需要通过core对象进行引用。 gGetStatus(S_IMU_STILL); // bool 类型 core.GetStatus(S_RC_ALIVE); uint8_t uTmp = gGetStatus(S_SYSTEM_STATUS_TAG); // uint8_t 类型 |
gSetStatus() SetStatus() | 设置全局状态变量。 参数blForceUpdate为true表示无论当前全局状态变量数值如何都进行设置。 false表示若待设置数值与当前数值相同就不进行设置 gSetStatus(S_WATCH_DOG_MODE, true, true); |
4.4 自定义简单类型总线数据项
在\DevStudio\BoardConfig\Board_BBDB\DataBus_BBDB.h文件中注释为"用户自定义设置区域"内加入自有总线数据的定义(注意!不要对该区域以外的总线数据声明进行任何修改或删除!):
// <=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>
// 用户自定义参数设置区域, 不得对该区域之外的任何内容进行修改
CBusItem<float> fAspdDiffPress; // float类型的bus item
// <=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>
之后就可以使用总线相关函数实现各种功能了
4.5 自定义复杂类型总线数据项目
- 1/3:在需要使用总线的.cpp文件头部加入对数据总线的文件包含:
#include"../../Frame/UseGlobalObject.h"
- 2/3:在如下文件中加入对复杂类型的结构声明(用户自定义):
\DevStudio\BoardConfig\Board_BBDB\BusItemDef4User.h
struct BUS_ITEM_DEMO
{CBusItem<uint8_t> Item_A;CBusItem<float> Item_B;
};
- 3/3:在\DevStudio\BoardConfig\Board_BBDB\DataBus_BBDB.h文件的“用户自定义参数设置区域”加入总线数据的定义:
// <=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>
// 用户自定义参数设置区域, 不得对该区域之外的任何内容进行修改
BUS_ITEM_DEMO sBusItemDemo;
// <=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>
4.6 自定义全局状态变量
文件DevStudio\BoardConfig\Board_BBP\StateVarDef_BBP.h对全局状态变量的ID进行了定义,此文件中的enum STATUS_BOOL及enumSTATUS_UINT枚举分别定义了bool及uint8_t类型的全局状态变量ID。
与自定义总线数据类似,自定义全局状态变量必须位于标注“用户自定义参数设置区域”的区域内。二次开发者不能对该区域意外的任何内容进行删除或修改。
- 1/2:在DevStudio\BoardConfig\Board_BBDB\StateVarDef_BBDB.h文件的enum STATUS_BOOL声明的用户自定义部分加入S_STATUS_VAR_DEMO声明
// <=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>
// 用户自定义 全局状态变量 设置区域, 不得对该区域之外的任何内容进行修改
S_STATUS_VAR_DEMO, // 直接定义 id 即可
// <=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>
- 2/2:之后在程序中需要的地方就可以通过下述函数设置或是获取状态;
gSetStatus(S_STATUS_VAR_DEMO, true);
bool blStatus = gGetStatus(S_STATUS_VAR_DEMO);
5. 机载参数系统基本操作
机载控制参数系统为整个PH47代码框架提供了控制参数的读取、写入、空地间传输等功能的全套使用解决方案。
机载参数数据存储于控制板外置 flash 或 eeprom 存储器中。可通过 CSS控制站以单个或批量的方式下载及上传设定。
5.1 参数系统文件包含
用户要在自己的代码文件中使用参数系统功能,只需在自己的.cpp文件开始处加入如下文件包含:
#include"../../Frame/UseGlobalObject.h" //文件包含路径根据实际情况确定
参数系统的参数id定义,以及参数默认属性设置(参数字典),分别位于如下文件中:
\DevStudio\BoardConfig\Firmware_BBDB\ParamDict_BBDB.h
\DevStudio\BoardConfig\Firmware_BBDB\ParamDict_BBDB.cpp
5.2 参数系统功能使用
函 数 | 功 能 描 述 |
Get() | 读取记载参数数值。 Get() 函数获取的参数存储于RAM中,若存储器(flash/e2prom)中参数被CSS上传设定所更新,或被程序中其他参数设定函数所改变,则RAM中参数数值也自动更新,用户无需手动刷新。 // 获取当前设定的遥测通讯波特率 uint32_t uBaudRate = (uint32_t)(core.para.Get(P_BAUD_RATE)); // 获取加速度计x轴偏移量 float fVal = frm.para.Get(P_SENSOR_ACC_OFST_X); |
Set() | 设置机载调参参数数值。 blForcesave决定是否对存储介质(Flash或EEprom)存储数据进行强制更新。 为 ture时不能进行高速率(>50hz)密集调用,否则有可能造成存储器密集写入超时而导致数据存储失败 // 设置参数数值强制更新存储 core.para.Set(P_SENSOR_ACC_OFST_X, _cal_acc_offset.x, true); // 仅对RAM当中的参数数值进行更新,控制板重启后丢失 frm.para.Set(P_SENSOR_ACC_OFST_X, blSave) |
5.3 自定义参数的实现
- 1/3:在ParaDict_BBDB.h文件enum PARAID枚举定义的用户区域当中加入新参数P_TEST_USER_1的 id 的定义,无需为id指定特定数值,顺序排列即可。在 CSS 参数下载列表中的顺序以enum PARAID当中的顺序为准。
// <=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>// 用户自定义参数设置区域, 不得对该区域之外的任何内容进行修改P_TEST_USER_1, // 自定义参数 id// <=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>
- 2/3:在ParaDict_BBDB.cpp文件全局数组g_arParaDictMCU[]参数字典的用户区域中加入新参数的属性数据。enum PARAID中参数id的数值及顺序与g_arParaDictMCU[]各元素均无关系,可根据实际需要组织。
// <=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>// 用户自定义参数设置区域, 不得对该区域之外的任何内容进行修改// 自定义参数 P_TEST_USER_1 的默认属性进行设置//{"TestUser_1", P_TEST_USER_1, 0x0000, 256.0f, 0.0f, 500.0f}, // <=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>=<=>
- 3/3;修改参数字典并编译下载至控制板后,必须通过控制板的调试串口运行一次 "initfstrun;"命令以初始化存储器数据。
6. 遥测通讯系统使用
PH7 代码框架通过 mavlink plus 协议与地面控制站 GCS,或其他无人控制目标进行双向通讯。mavlink plus 协议在mavlink 1.0基础上增加修改了message 设置,并对 GCS 上行控制命令实施真伪性动态甄别。
6.1 遥测通讯系统文件包含
若在用户二次开发代码中使用mavlink通讯功能,那么就需要在自己的代码中加入如下文件包含:
#include "/DevStudio/Frame/Core/MavlinkInterface.h"#include "/DevStudio/Frame/Core/MavlinkWrap.h"
6.2 遥测通讯系统功能使用
在整个 PH7 代码框架内对 GCS 的数据通讯以core.mavlink.FunName()的形式来使用 mavlink 协议相关功能函数。
对其他控制目标的数据通讯以core.consolelink.FunName()的形式来使用 mavlink 通讯相关功能函数。
PH47代码框架已对Message的下行发送进行了完善的封装,为不同类型的Message设定了不同的发送速率,发送速率在运行过程中可重新设定调整。整个Message的下行发送机制为全自动执行,用户在仅需在发送message时以core.mavlink.AddMsg_xxx()形式调用对应函数,即可将待发送消息加入下行消息发送队列随即自动发送。二次开发用户常用消息发送函数如下:
函 数 | 功 能 描 述 |
AddMsg_Ctrl_Base() | 下行发送飞控控制器相关数据 |
AddMsg_Atms_Data() | 下行发送大气相关数据 |
AddMsg_GpsRawInt() | 下行发送GPS相关数据 |
AddMsg_Raw_Imu() | 下行发送IMU相关数据 |
AddMsg_Attitude() | 下行发送姿态相关数据 |
AddMsg_Debug_1() | 下行发送调试数据 1 |
AddMsg_Debug_2() | 下行发送调试数据 2 |
AddMsg_StatusText() | 下行发送状态信息 |
mavlink message 发送示例:
// demo 1: 下行发送心跳包
mavlink_heartbeat_t packet;
memset(&packet, 0x00, sizeof(packet));packet.base_mode = (uint8_t) 6;
packet.system_status = 0xA5;
packet.autopilot = BOARD_PH7;
packet.custom_mode = 0xEE;
packet.mavlink_version = MAVLINK_VERSION;uRet = core.mavlink.AddMsg_HeartBeat(&packet); // 加入发送队列,随后自动发送
demo 2: 下行发送状态信息
char szTmp[32] = {""};
gFormat(szTmp, "Log %d HIL sim start.", uFlightNum);
core.mavlink.AddMsg_StatusText(szTmp, MAV_SEVERITY_WARNING);// 更多Message发送使用方法可见文件:\DevStudio\Frame\Frame.cpp 当中的
// void MavMsgSnd2GCS_Slow() 函数
7. 应用层框架预制功能函数的使用
PH47框架在应用层\DevStudio\Application\App_BBP.cpp(.h)文件,即应用层类CAppBBDB中预置了多个供二次开发用户使用的功能函数,用户根据上述函数的功能特性,有针对性的在其中加入自己的代码即可方便的进行二次开发。
函数名称 CAppBBDB类成员函数 | 功 能 描 述 |
Init() | PH47框架完全启动后调用该初始化函数 在本函数中可安全使用 ASSERT, TRACE 等依赖于框架启动的函数 |
FastThread_1000Hz() | 快速线程函数,调用频率在400—500hz之间波动。 |
NormalThread_250Hz() | 普通线程函数,调用频率固定为250hz |
SlowThread_50Hz() | 慢速线程函数,调用频率固定为50hz |
HandleRevMavlinkMsg() | mavlink 用户自定义上行控制指令处理函数 |
HandleConsoleCmd() | 调试控制台用户自定义命令处理函数 |
7.1 程序示例:
普通线程函数示例
void CAppBBDB::NormalThread_250Hz()
{// 获取循环间隔时间float fDt = 0.0f;if(_NormalLoopDt.GetLocalDt_ms(fDt) == false)return;// Controller_Common 示例代码algo._pController_Common->Update(fDt);
}
接收 mavlink message 用户自定义上行指令处理示例
void CAppBBDB::HandleRevMavlinkMsg(mavlink_command_long_sa_t *pPacket)
{//解析及响应 GCS 上行控制命令if(pRevMsg ->msgid == MAVLINK_MSG_ID_COMMAND_LONG_SA ){mavlink_command_long_sa_t packet;mavlink_msg_command_long_sa_decode(pRevMsg, &packet);if(packet.command == MAV_CMD_USER_DEFINE){ if(packet.param1 == fValue){// User code ...}}}
}
用户自定义调试控制台命令处理函数示例
void CAppBBDB::HandleConsoleCmd(uint8_t *pBuff)
{// pBuff 为用户命令字符串,命令字符串最长不能超过 32 byteif(strcmp((char *) pBuff, "User CMD") == 0){// User code ...}
}
更多内容见CSDN博客专栏:无人机飞控https://blog.csdn.net/ss15/category_9690939.html?spm=1001.2014.3001.5482相关资源:PH47: PH47运动控制代码框架.https://gitee.com/ss15/ph47