APM代码:minimosd与Mavlink协议

今天来聊一聊minimosd无人机协议。Osd的全称是on-screen display,指在屏幕视频之上叠加数据,相信每一个使用apm或者pix做飞行控制器的人都多少与osd打过交道。目前市面所有的飞控osd模块都是起源于google上的minimosd项目。

APM代码:minimosd与Mavlink协议

该项目版本截止于12年的2.0。其硬件解决方案是采用atmega328芯片作为主控,配置相当于arduino nano,具有32kb的flash,2kb的sram以及1kb的eeprom;同时采用7456作为字符叠加芯片,7456可以存储256个字符,针对pal或者ntsc信号完成字符叠加。在软件方面,遵从mavlink协议,从串口获取封装为该协议的飞行数据,显示在视频之上,同时具有桌面配置程序,支持用户配置显示信息的内容与风格。在minimosd2.0版本的基础之上,全球范围内出现了若干分支版本,其中比较著名的是托管在github上的minimosd-extra 2.4(https://github.com/diydrones/MinimOSD-Extra/wiki), 该版本在原版基础上扩展对mavlink传递信息的支持,可以显示更多的飞行数据,并且具备了简单的飞行统计功能,还改进了osd显示方案的切换方式。

在此之外,也流传着一批针对国内用户的中文版osd固件,基本上都是基于minimosd-extra进行了简单改造。而杭州的playuav则大胆的对osd体系结构进行了改进,彻底改变了osd硬件架构,做出了一些很有新意的osd尝试(https://github.com/PlayUAV/)。

作为一个源码解析类的专题,下面我们以google上的项目minimosd-2.0源码为基础,来进一步了解minimosd的程序语言。

Minimosd源码分为三个部分,分别是osd功能固件ArduCam_Osd;配套库libraries以及osd的桌面配置工具OSD_Config。我们主要研读的代码是ArduCam_OSD,是烧录到osd片子上的主要功能代码。

APM代码:minimosd与Mavlink协议

ArduCam工程的主要功能文件包括两个头文件:OSD_Vars.h,OSD_Config.h以及三个源码定义文件ArduCAM_osd.ion,OSD_Panels.ino,MAVLink.ino。

OSD_Vars.h中定义的变量储存了从mavlink协议获取的飞行数据,这些飞行数据被解析后,保存在运行时内存sram中。

OSD_Config.h则负责与eeprom打交道,定义了配置信息在eeprom中的保存位置,便于minimosd启动后,从eeprom读取用户配置信息。

ArduCAM_osd.ino是minimosd的程序入口点,是整个程序的骨架。

OSD_Panels.ino是具体的绘制功能,每一帧视频,都有这些功能,将飞行器信息绘制到视频之上。

MavLink.ino则是用于对mavlink协议的解析,将收到的数据包按照mavlink协议解析,解析成功后将数据储存在ODS_Vars.h中定义的变量中。

整个minimosd的程序流程非常简单,如下图所示

APM代码:minimosd与Mavlink协议

在minimosd通电启动后,首先调用到Setup入口函数,实现如下流程。

1.初始化mavlink,设置了与飞控连接的串口波特率以及端口号

Serial.begin(TELEMETRY_SPEED);// setup mavlink port

mavlink_comm_0_port = &Serial;

然后向飞控发送数据请求,osd需要如下飞行数据

MAV_DATA_STREAM_EXTENDED_STATUS,

MAV_DATA_STREAM_RC_CHANNELS,

MAV_DATA_STREAM_POSITION,

MAV_DATA_STREAM_EXTRA1,

</span>MAV_DATA_STREAM_EXTRA2

2.初始化7456,设置7456通信的spi端口,并且激活7456

pinMode(MAX7456_SELECT, OUTPUT); // OSD CS

digitalWrite(MAX7456_SELECT, HIGH); // unplug OSD

3.读取eeprom设置,OSD_Config.h中记录了不同的设置信息在eeprom中的存储位置,在这里逐次读取,如:

//****** First set of 8 Panels ******

uint16_t offset = OffsetBITpanel * panel;

setBit(panA_REG, Cen_BIT, readEEPROM(panCenter_en_ADDR + offset));

panCenter_XY[0]=readEEPROM(panCenter_x_ADDR + offset);

panCenter_XY[1]=checkPAL(readEEPROM(panCenter_y_ADDR + offset));

这里要说一下minimosd中的panel结构,minimosd中设计了4组,每组8个,共32个数据槽位,每个槽位分别对应于需要显示的一种飞行数据。针对每个槽位,可以开启或者关闭显示,亦可以设置该槽位在屏幕上的显示位置(x,y坐标)。对于上述的32组槽位设置,minimosd支持两种方案,所以放到一个长度为2的数组中进行管理 。

下述代码是每个槽位是否显示的变量结构,每组8个槽位的1或者0正好对应于一个byte数据类型,因为有两套显示方案,所以每组都是一个长度为2的byte数组

// Panel BIT registers

byte panA_REG[npanels] = {0b00000000};

byte panB_REG[npanels] = {0b00000000};

byte panC_REG[npanels] = {0b00000000};

byte panD_REG[npanels] = {0b00000000}

下述代码是槽位显示位置的变量结构,x和y坐标分别用byte来存储。同样,都可以应对两套显示方案。

// First 8 panels and their X,Y coordinate holders

byte panCenter_XY[2][npanels]; // = { 13,7,0 };

byte panPitch_XY[2][npanels]; // = { 11,1 };

byte panRoll_XY[2][npanels]; // = { 23,7 };

byte panBatt_A_XY[2][npanels]; // = { 23,1 };

byte panGPSats_XY[2][npanels]; // = { 2,12 };

byte panGPL_XY[2][npanels]; // = { 2,11 };

byte panGPS_XY[2][npanels]; // = { 2,13 };

byte panBatteryPercent_XY[2][npanels];

// ........

4.启动loop循环,无更多细节。

通电后,minimosd完成Setup中的所有功能,便进入了一个周而复始的循环工作过程,不断的调用Loop函数,而每个loop循环,则执行如下函数:

1.获取mavlink数据.根据mavlink的msgid,获知数据类型,后分别调用mavlink库中的函数对数据包进行解析,存储到变量中。

if(mavlink_parse_char(MAVLINK_COMM_0, c, &msg, &status)) {

mavlink_active = 1;

//handle msg

switch(msg.msgid) {

case MAVLINK_MSG_ID_HEARTBEAT:

case MAVLINK_MSG_ID_SYS_STATUS:

osd_vbat_A = (mavlink_msg_sys_status_get_voltage_battery(&msg) / 1000.0f); //Battery voltage, in millivolts (1 = 1 millivolt)

osd_curr_A = mavlink_msg_sys_status_get_current_battery(&msg); //Battery current, in 10*milliamperes (1 = 10 milliampere)

osd_battery_remaining_A = mavlink_msg_sys_status_get_battery_remaining(&msg); //Remaining battery energy: (0%: 0, 100%: 100)

..

case MAVLINK_MSG_ID_GPS_RAW_INT:

2.字符绘制,这是osd的核心功能,针对允许显示的飞行数据,调用绘制该数据的函数

if(ISd(panel,Warn_BIT)) panWarn(panWarn_XY[0], panWarn_XY[1]); // this must be here so warnings are always checked

//Testing bits from 8 bit register A

if(ISa(panel,Cen_BIT)) panCenter(panCenter_XY[0], panCenter_XY[1]); //4x2

if(ISa(panel,Pit_BIT)) panPitch(panPitch_XY[0], panPitch_XY[1]); //5x1

if(ISa(panel,Rol_BIT)) panRoll(panRoll_XY[0], panRoll_XY[1]);

if(ISa(panel,BatA_BIT)) panBatt_A(panBatt_A_XY[0], panBatt_A_XY[1]); //7x1

//.........................

//.........................

我们以绘制电池电量panBatt_A为例,该函数代码如下:

void panBatt_A(int first_col, int first_line){

osd.setPanel(first_col, first_line);

osd.openPanel;

osd.printf("%c%5.2f%c", 0xE2, (double)osd_vbat_A, 0x8E);

osd.closePanel;

}

其中,osd.setPanel函数是7456提供的开发接口,设置当前要绘制字符的位置。

Osd.printf函数类似于标准c++的printf,是向7456当前的位置输出字符。在上述代码中,在eeprom读出的屏幕位置,绘制了从mavlink协议获取的osd_vbat_A变量的值。

这里需要注意到的是,7456字符叠加器内部存储了256种不同的字符图标,正好对应了char类型的取值范围,可以通过MAX7456Charwizard.jar编辑相应的图标,大家使用的中文版osd,也就是这样制作出来的,仅仅将用到的中文画成了图标,而并不是支持所有的中文字库。

APM代码:minimosd与Mavlink协议

上述是osd的功能性代码的简单介绍,希望可以对大家有所帮助。

最后给大家留一个思考题。我们可以看到minimosd的功能非常简单,仅仅解析了mavlink4到5类数据包,存储其中32种飞行数据,但是,大家可以尝试看看代码中对于显示槽位相关的储存结构,十分的晦涩难懂。那么,请大家思考一下,为什么minimosd代码功能如此简单,却动用了天书一般的数据结构来实现?

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至2161241530@qq.com 举报,一经查实,本站将立刻删除。如若转载,请注明出处:https://www.woiwrj.com/wurenjibaike/djiwurenzhishi/8338/

发表评论

登录后才能评论