每天读一点儿APM(PIX)代码之四:minimosd

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

  每天读一点儿APM(PIX)代码之四:minimosd_第1张图片

该项目版本截止于12年的2.0。其硬件解决方案是采用atmega328芯片作为主控,配置相当于arduino nano,具有32kbflash2kbsram以及1kbeeprom;同时采用7456作为字符叠加芯片,7456可以存储256个字符,针对pal或者ntsc信号完成字符叠加。在软件方面,minimosd遵从mavlink协议,从串口获取封装为该协议的飞行数据,显示在视频之上,同时具有桌面配置程序,支持用户配置显示信息的内容与风格。

minimosd2.0版本的基础之上,全球范围内出现了若干分支版本,其中比较著名的是托管在github上的minimosd-extra 2.4https://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(PIX)代码之四:minimosd_第2张图片

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.inominimosd的程序入口点,是整个程序的骨架。

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

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

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

  每天读一点儿APM(PIX)代码之四:minimosd_第3张图片

 

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

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

[cpp]  view plain  copy
 
  1. Serial.begin(TELEMETRY_SPEED);// setup mavlink port  
  2. mavlink_comm_0_port = &Serial;  

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

[cpp]  view plain  copy
 
  1. MAV_DATA_STREAM_EXTENDED_STATUS,  
  2. MAV_DATA_STREAM_RC_CHANNELS,  
  3. MAV_DATA_STREAM_POSITION,  
  4. MAV_DATA_STREAM_EXTRA1,   
  5. MAV_DATA_STREAM_EXTRA2  

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

[cpp]  view plain  copy
 
  1. pinMode(MAX7456_SELECT,  OUTPUT); // OSD CS  
  2. digitalWrite(MAX7456_SELECT,  HIGH); // unplug OSD  

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

[cpp]  view plain  copy
 
  1. //****** First set of 8 Panels ******  
  2. uint16_t offset = OffsetBITpanel * panel;  
  3. setBit(panA_REG[panel], Cen_BIT, readEEPROM(panCenter_en_ADDR + offset));  
  4. panCenter_XY[0][panel]=readEEPROM(panCenter_x_ADDR + offset);  
  5. panCenter_XY[1][panel]=checkPAL(readEEPROM(panCenter_y_ADDR + offset));  

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

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

[cpp]  view plain  copy
 
  1. // Panel BIT registers  
  2. byte panA_REG[npanels] = {0b00000000};  
  3. byte panB_REG[npanels] = {0b00000000};  
  4. byte panC_REG[npanels] = {0b00000000};  
  5. byte panD_REG[npanels] = {0b00000000}  

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

[cpp]  view plain  copy
 
  1. // First 8 panels and their X,Y coordinate holders  
  2. byte panCenter_XY[2][npanels]; // = { 13,7,0 };  
  3. byte panPitch_XY[2][npanels]; // = { 11,1 };  
  4. byte panRoll_XY[2][npanels]; // = { 23,7 };  
  5. byte panBatt_A_XY[2][npanels]; // = { 23,1 };  
  6. byte panGPSats_XY[2][npanels]; // = { 2,12 };  
  7. byte panGPL_XY[2][npanels]; // = { 2,11 };  
  8. byte panGPS_XY[2][npanels]; // = { 2,13 };  
  9. byte panBatteryPercent_XY[2][npanels];  
  10. // ........  

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

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

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

[cpp]  view plain  copy
 
  1. if(mavlink_parse_char(MAVLINK_COMM_0, c, &msg, &status)) {  
  2.                 mavlink_active = 1;  
  3.                 //handle msg  
  4.                 switch(msg.msgid) {  
  5.                  case MAVLINK_MSG_ID_HEARTBEAT:  
  6.   
  7.                  case MAVLINK_MSG_ID_SYS_STATUS:  
  8.                         osd_vbat_A = (mavlink_msg_sys_status_get_voltage_battery(&msg) / 1000.0f); //Battery voltage, in millivolts (1 = 1 millivolt)  
  9.                         osd_curr_A = mavlink_msg_sys_status_get_current_battery(&msg); //Battery current, in 10*milliamperes (1 = 10 milliampere)           
  10.                         osd_battery_remaining_A = mavlink_msg_sys_status_get_battery_remaining(&msg); //Remaining battery energy: (0%: 0, 100%: 100)  
  11.                         ..  
  12.                  case MAVLINK_MSG_ID_GPS_RAW_INT:  

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

[cpp]  view plain  copy
 
  1. if(ISd(panel,Warn_BIT)) panWarn(panWarn_XY[0][panel], panWarn_XY[1][panel]); // this must be here so warnings are always checked  
  2. //Testing bits from 8 bit register A   
  3. if(ISa(panel,Cen_BIT)) panCenter(panCenter_XY[0][panel],     panCenter_XY[1][panel]);   //4x2  
  4. if(ISa(panel,Pit_BIT)) panPitch(panPitch_XY[0][panel],   panPitch_XY[1][panel]); //5x1  
  5. if(ISa(panel,Rol_BIT)) panRoll(panRoll_XY[0][panel],     panRoll_XY[1][panel]);  
  6. if(ISa(panel,BatA_BIT)) panBatt_A(panBatt_A_XY[0][panel], panBatt_A_XY[1][panel]); //7x1  
  7. //.........................  
  8. //.........................  

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

[cpp]  view plain  copy
 
  1. void panBatt_A(int first_col, int first_line){  
  2.     osd.setPanel(first_col, first_line);  
  3.     osd.openPanel();  
  4.     osd.printf("%c%5.2f%c", 0xE2, (double)osd_vbat_A, 0x8E);  
  5.     osd.closePanel();  
  6. }  

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

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

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

  每天读一点儿APM(PIX)代码之四:minimosd_第4张图片

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

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


apm、pix咨询、定制开发与服务,欢迎大家联系博主.

你可能感兴趣的:(多旋翼飞行器)