Merge pull request #2 from nathantsoi/sirinfpv-max-osd-development

Sirinfpv max osd development
This commit is contained in:
Evgeny 2016-06-17 00:44:29 -07:00 committed by GitHub
commit 7a4d996318
21 changed files with 830 additions and 496 deletions

View File

@ -8,6 +8,7 @@ targets=("PUBLISHMETA=True" \
"TARGET=SPRACINGF3" \
"TARGET=SPRACINGF3EVO" \
"TARGET=SPRACINGF3MINI" \
"TARGET=OMNIBUS" \
"TARGET=NAZE" \
"TARGET=AFROMINI" \
"TARGET=RMDO" \

View File

@ -38,6 +38,7 @@
#include "drivers/serial.h"
#include "drivers/gyro_sync.h"
#include "drivers/pwm_output.h"
#include "drivers/max7456.h"
#include "sensors/sensors.h"
#include "sensors/gyro.h"
@ -420,17 +421,7 @@ static void resetConf(void)
#endif
#ifdef OSD
featureSet(FEATURE_OSD);
masterConfig.osdProfile.system = 0;
masterConfig.osdProfile.item_pos[OSD_MAIN_BATT_VOLTAGE] = -29;
masterConfig.osdProfile.item_pos[OSD_RSSI_VALUE] = -59;
masterConfig.osdProfile.item_pos[OSD_TIMER] = -39;
masterConfig.osdProfile.item_pos[OSD_THROTTLE_POS] = -9;
masterConfig.osdProfile.item_pos[OSD_CPU_LOAD] = 26;
masterConfig.osdProfile.item_pos[OSD_VTX_CHANNEL] = 1;
masterConfig.osdProfile.item_pos[OSD_VOLTAGE_WARNING] = -80;
masterConfig.osdProfile.item_pos[OSD_ARMED] = -107;
masterConfig.osdProfile.item_pos[OSD_DISARMED] = -109;
resetOsdConfig();
#endif
#ifdef USE_RTC6705

View File

@ -27,22 +27,27 @@
#ifdef USE_MAX7456
#include "drivers/bus_spi.h"
#include "drivers/light_led.h"
#include "drivers/system.h"
#include "max7456.h"
#include "max7456_symbols.h"
#define DISABLE_MAX7456 GPIO_SetBits(MAX7456_CS_GPIO, MAX7456_CS_PIN)
#define ENABLE_MAX7456 GPIO_ResetBits(MAX7456_CS_GPIO, MAX7456_CS_PIN)
#define DISABLE_MAX7456 IOHi(max7456CsPin)
#define ENABLE_MAX7456 IOLo(max7456CsPin)
/** PAL or NTSC, value is number of chars total */
#define VIDEO_BUFFER_CHARS_NTSC 390
#define VIDEO_BUFFER_CHARS_PAL 480
static IO_t max7456CsPin = IO_NONE;
/** Artificial Horizon limits **/
#define AHIPITCHMAX 200 // Specify maximum AHI pitch value displayed. Default 200 = 20.0 degrees
#define AHIROLLMAX 400 // Specify maximum AHI roll value displayed. Default 400 = 40.0 degrees
#define AHISIDEBARWIDTHPOSITION 7
#define AHISIDEBARHEIGHTPOSITION 3
uint16_t max_screen_size;
uint8_t video_signal_type = 0;
uint8_t max7456_lock = 0;
char screen[VIDEO_BUFFER_CHARS_PAL];
char max7456_screen[VIDEO_BUFFER_CHARS_PAL];
uint8_t max7456_send(uint8_t add, uint8_t data) {
@ -51,11 +56,17 @@ uint8_t max7456_send(uint8_t add, uint8_t data) {
}
void max7456_init(uint8_t system) {
void max7456_init(uint8_t video_system) {
uint8_t max_screen_rows;
uint8_t srdata = 0;
uint16_t x;
char buf[30];
char buf[LINE];
#ifdef MAX7456_SPI_CS_PIN
max7456CsPin = IOGetByTag(IO_TAG(MAX7456_SPI_CS_PIN));
#endif
IOInit(max7456CsPin, OWNER_SYSTEM, RESOURCE_SPI);
IOConfigGPIO(max7456CsPin, SPI_IO_CS_CFG);
//Minimum spi clock period for max7456 is 100ns (10Mhz)
spiSetDivisor(MAX7456_SPI_INSTANCE, SPI_9MHZ_CLOCK_DIVIDER);
@ -75,21 +86,21 @@ void max7456_init(uint8_t system) {
}
// Override detected type: 0-AUTO, 1-PAL, 2-NTSC
switch(system) {
case 1:
switch(video_system) {
case PAL:
video_signal_type = VIDEO_MODE_PAL;
break;
case 2:
case NTSC:
video_signal_type = VIDEO_MODE_NTSC;
break;
}
if (video_signal_type) { //PAL
max_screen_size = VIDEO_BUFFER_CHARS_PAL;
max_screen_rows = 16;
max_screen_rows = VIDEO_LINES_PAL;
} else { // NTSC
max_screen_size = VIDEO_BUFFER_CHARS_NTSC;
max_screen_rows = 13;
max_screen_rows = VIDEO_LINES_NTSC;
}
// set all rows to same charactor black/white level
@ -106,13 +117,13 @@ void max7456_init(uint8_t system) {
x = 160;
for (int i = 1; i < 5; i++) {
for (int j = 3; j < 27; j++)
screen[i * 30 + j] = (char)x++;
max7456_screen[i * LINE + j] = (char)x++;
}
tfp_sprintf(buf, "BF VERSION: %s", FC_VERSION_STRING);
max7456_write_string(buf, 5*30+5);
max7456_write_string("MENU: THRT MID", 6*30+7);
max7456_write_string("YAW RIGHT", 7*30+13);
max7456_write_string("PITCH UP", 8*30+13);
max7456_write_string(buf, LINE06+5);
max7456_write_string("MENU: THRT MID", LINE07+7);
max7456_write_string("YAW RIGHT", LINE08+13);
max7456_write_string("PITCH UP", LINE09+13);
max7456_draw_screen();
}
@ -121,14 +132,52 @@ void max7456_write_string(const char *string, int16_t address) {
char *dest;
if (address >= 0)
dest = screen + address;
dest = max7456_screen + address;
else
dest = screen + (max_screen_size + address);
dest = max7456_screen + (max_screen_size + address);
while(*string && dest < (screen + max_screen_size))
while(*string && dest < (max7456_screen + max_screen_size))
*dest++ = *string++;
}
// Write the artifical horizon to the screen buffer
void max7456_artificial_horizon(int rollAngle, int pitchAngle, uint8_t show_sidebars) {
uint16_t position = 194;
if(pitchAngle>AHIPITCHMAX) pitchAngle=AHIPITCHMAX;
if(pitchAngle<-AHIPITCHMAX) pitchAngle=-AHIPITCHMAX;
if(rollAngle>AHIROLLMAX) rollAngle=AHIROLLMAX;
if(rollAngle<-AHIROLLMAX) rollAngle=-AHIROLLMAX;
for(uint8_t X=0; X<=8; X++) {
if (X==4) X=5;
int Y = (rollAngle * (4-X)) / 64;
Y -= pitchAngle / 8;
Y += 41;
if(Y >= 0 && Y <= 81) {
uint16_t pos = position -7 + LINE*(Y/9) + 3 - 4*LINE + X;
max7456_screen[pos] = SYM_AH_BAR9_0+(Y%9);
}
}
max7456_screen[position-1] = SYM_AH_CENTER_LINE;
max7456_screen[position+1] = SYM_AH_CENTER_LINE_RIGHT;
max7456_screen[position] = SYM_AH_CENTER;
if (show_sidebars) {
// Draw AH sides
int8_t hudwidth = AHISIDEBARWIDTHPOSITION;
int8_t hudheight = AHISIDEBARHEIGHTPOSITION;
for(int8_t X=-hudheight; X<=hudheight; X++) {
max7456_screen[position-hudwidth+(X*LINE)] = SYM_AH_DECORATION;
max7456_screen[position+hudwidth+(X*LINE)] = SYM_AH_DECORATION;
}
// AH level indicators
max7456_screen[position-hudwidth+1] = SYM_AH_LEFT;
max7456_screen[position+hudwidth-1] = SYM_AH_RIGHT;
}
}
void max7456_draw_screen(void) {
uint16_t xx;
if (!max7456_lock) {
@ -136,8 +185,8 @@ void max7456_draw_screen(void) {
for (xx = 0; xx < max_screen_size; ++xx) {
max7456_send(MAX7456ADD_DMAH, xx>>8);
max7456_send(MAX7456ADD_DMAL, xx);
max7456_send(MAX7456ADD_DMDI, screen[xx]);
screen[xx] = ' ';
max7456_send(MAX7456ADD_DMDI, max7456_screen[xx]);
max7456_screen[xx] = ' ';
}
DISABLE_MAX7456;
}
@ -151,8 +200,8 @@ void max7456_draw_screen_fast(void) {
max7456_send(MAX7456ADD_DMAL, 0);
max7456_send(MAX7456ADD_DMM, 1);
for (xx = 0; xx < max_screen_size; ++xx) {
max7456_send(MAX7456ADD_DMDI, screen[xx]);
screen[xx] = ' ';
max7456_send(MAX7456ADD_DMDI, max7456_screen[xx]);
max7456_screen[xx] = ' ';
}
max7456_send(MAX7456ADD_DMDI, 0xFF);
max7456_send(MAX7456ADD_DMM, 0);
@ -175,6 +224,11 @@ void max7456_write_nvm(uint8_t char_address, uint8_t *font_data) {
for(x = 0; x < 54; x++) {
max7456_send(MAX7456ADD_CMAL, x); //set start address low
max7456_send(MAX7456ADD_CMDI, font_data[x]);
#ifdef LED0_TOGGLE
LED0_TOGGLE;
#else
LED1_TOGGLE;
#endif
}
// transfer 54 bytes from shadow ram to NVM

View File

@ -114,12 +114,41 @@
#define WRITE_NVR 0xA0
#define STATUS_REG_NVR_BUSY 0x20
/** Line multiples, for convenience & one less op at runtime **/
#define LINE 30
#define LINE01 0
#define LINE02 30
#define LINE03 60
#define LINE04 90
#define LINE05 120
#define LINE06 150
#define LINE07 180
#define LINE08 210
#define LINE09 240
#define LINE10 270
#define LINE11 300
#define LINE12 330
#define LINE13 360
#define LINE14 390
#define LINE15 420
#define LINE16 450
/** PAL or NTSC, value is number of chars total */
#define VIDEO_BUFFER_CHARS_NTSC 390
#define VIDEO_BUFFER_CHARS_PAL 480
#define VIDEO_LINES_NTSC 13
#define VIDEO_LINES_PAL 16
enum VIDEO_TYPES { AUTO = 0, PAL, NTSC };
extern uint16_t max_screen_size;
char screen[480];
char max7456_screen[VIDEO_BUFFER_CHARS_PAL];
void max7456_init(uint8_t system);
void max7456_draw_screen(void);
void max7456_draw_screen_fast(void);
void max7456_artificial_horizon(int rollAngle, int pitchAngle, uint8_t show_sidebars);
void max7456_write_string(const char *string, int16_t address);
void max7456_write_nvm(uint8_t char_address, uint8_t *font_data);

View File

@ -0,0 +1,233 @@
/* @file max7456_symbols.h
* @brief max7456 symbols for the mwosd font set
*
* @author Nathan Tsoi nathan@vertile.com
*
* Copyright (C) 2016 Nathan Tsoi
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#pragma once
#ifdef USE_MAX7456
// Character Symbols
#define SYM_BLANK 0x20
// Satellite Graphics
#define SYM_SAT_L 0x1E
#define SYM_SAT_R 0x1F
//#define SYM_SAT 0x0F // Not used
// Degrees Icon for HEADING/DIRECTION HOME
#define SYM_DEGREES 0xBD
// Direction arrows
#define SYM_ARROW_SOUTH 0x60
#define SYM_ARROW_2 0x61
#define SYM_ARROW_3 0x62
#define SYM_ARROW_4 0x63
#define SYM_ARROW_EAST 0x64
#define SYM_ARROW_6 0x65
#define SYM_ARROW_7 0x66
#define SYM_ARROW_8 0x67
#define SYM_ARROW_NORTH 0x68
#define SYM_ARROW_10 0x69
#define SYM_ARROW_11 0x6A
#define SYM_ARROW_12 0x6B
#define SYM_ARROW_WEST 0x6C
#define SYM_ARROW_14 0x6D
#define SYM_ARROW_15 0x6E
#define SYM_ARROW_16 0x6F
// Heading Graphics
#define SYM_HEADING_N 0x18
#define SYM_HEADING_S 0x19
#define SYM_HEADING_E 0x1A
#define SYM_HEADING_W 0x1B
#define SYM_HEADING_DIVIDED_LINE 0x1C
#define SYM_HEADING_LINE 0x1D
// FRSKY HUB
#define SYM_CELL0 0xF0
#define SYM_CELL1 0xF1
#define SYM_CELL2 0xF2
#define SYM_CELL3 0xF3
#define SYM_CELL4 0xF4
#define SYM_CELL5 0xF5
#define SYM_CELL6 0xF6
#define SYM_CELL7 0xF7
#define SYM_CELL8 0xF8
#define SYM_CELL9 0xF9
#define SYM_CELLA 0xFA
#define SYM_CELLB 0xFB
#define SYM_CELLC 0xFC
#define SYM_CELLD 0xFD
#define SYM_CELLE 0xFE
#define SYM_CELLF 0xC3
// Map mode
#define SYM_HOME 0x04
#define SYM_AIRCRAFT 0x05
#define SYM_RANGE_100 0x21
#define SYM_RANGE_500 0x22
#define SYM_RANGE_2500 0x23
#define SYM_RANGE_MAX 0x24
#define SYM_DIRECTION 0x72
// GPS Coordinates and Altitude
#define SYM_LAT 0xCA
#define SYM_LON 0xCB
#define SYM_ALT 0xCC
// GPS Mode and Autopilot
#define SYM_3DFIX 0xDF
#define SYM_HOLD 0xEF
#define SYM_G_HOME 0xFF
#define SYM_GHOME 0x9D
#define SYM_GHOME1 0x9E
#define SYM_GHOLD 0xCD
#define SYM_GHOLD1 0xCE
#define SYM_GMISSION 0xB5
#define SYM_GMISSION1 0xB6
#define SYM_GLAND 0xB7
#define SYM_GLAND1 0xB8
// Gimbal active Mode
#define SYM_GIMBAL 0x16
#define SYM_GIMBAL1 0x17
// Sensor´s Presence
#define SYM_ACC 0xA0
#define SYM_MAG 0xA1
#define SYM_BAR 0xA2
#define SYM_GPS 0xA3
#define SYM_MAN 0xC0
#define SYM_MAN1 0xC1
#define SYM_MAN2 0xC2
#define SYM_CHECK 0xBE
#define SYM_BARO10 0xB7
#define SYM_BARO11 0xB8
#define SYM_MAG10 0xB5
#define SYM_MAG11 0xB6
// AH Center screen Graphics
//#define SYM_AH_CENTER 0x01
#ifdef ALT_CENTER
#define SYM_AH_CENTER_LINE 0xB0
#define SYM_AH_CENTER 0xB1
#define SYM_AH_CENTER_LINE_RIGHT 0xB2
#else
#define SYM_AH_CENTER_LINE 0x26
#define SYM_AH_CENTER 0x7E
#define SYM_AH_CENTER_LINE_RIGHT 0xBC
#endif
#define SYM_AH_RIGHT 0x02
#define SYM_AH_LEFT 0x03
#define SYM_AH_DECORATION_UP 0xC9
#define SYM_AH_DECORATION_DOWN 0xCF
// AH Bars
#define SYM_AH_BAR9_0 0x80
// Temperature
#define SYM_TEMP_F 0x0D
#define SYM_TEMP_C 0x0E
// Batt evolution
#define SYM_BATT_FULL 0x90
#define SYM_BATT_5 0x91
#define SYM_BATT_4 0x92
#define SYM_BATT_3 0x93
#define SYM_BATT_2 0x94
#define SYM_BATT_1 0x95
#define SYM_BATT_EMPTY 0x96
// Vario
#define SYM_VARIO 0x7F
// Glidescope
#define SYM_GLIDESCOPE 0xE0
// Batt Icon´s
#define SYM_MAIN_BATT 0x97
#define SYM_VID_BAT 0xBF
// Unit Icon´s (Metric)
#define SYM_MS 0x9F
#define SYM_KMH 0xA5
#define SYM_ALTM 0xA7
#define SYM_DISTHOME_M 0xBB
#define SYM_M 0x0C
// Unit Icon´s (Imperial)
#define SYM_FTS 0x99
#define SYM_MPH 0xA6
#define SYM_ALTFT 0xA8
#define SYM_DISTHOME_FT 0xB9
#define SYM_FT 0x0F
// Voltage and amperage
#define SYM_VOLT 0xA9
#define SYM_AMP 0x9A
#define SYM_MAH 0xA4
#define SYM_WATT 0x57
// Flying Mode
#define SYM_ACRO 0xAE
#define SYM_ACROGY 0x98
#define SYM_ACRO1 0xAF
#define SYM_STABLE 0xAC
#define SYM_STABLE1 0xAD
#define SYM_HORIZON 0xC4
#define SYM_HORIZON1 0xC5
#define SYM_PASS 0xAA
#define SYM_PASS1 0xAB
#define SYM_AIR 0xEA
#define SYM_AIR1 0xEB
#define SYM_PLUS 0x89
// Note, these change with scrolling enabled (scrolling is TODO)
//#define SYM_AH_DECORATION_LEFT 0x13
//#define SYM_AH_DECORATION_RIGHT 0x13
#define SYM_AH_DECORATION 0x13
// Time
#define SYM_ON_M 0x9B
#define SYM_FLY_M 0x9C
#define SYM_ON_H 0x70
#define SYM_FLY_H 0x71
// Throttle Position (%)
#define SYM_THR 0xC8
#define SYM_THR1 0xC9
// RSSI
#define SYM_RSSI 0xBA
// Menu cursor
#define SYM_CURSOR SYM_AH_LEFT
//Misc
#define SYM_COLON 0x2D
//sport
#define SYM_MIN 0xB3
#define SYM_AVG 0xB4
#endif

View File

@ -237,7 +237,7 @@ pwmOutputConfiguration_t *pwmInit(drv_pwm_config_t *init)
type = MAP_TO_SERVO_OUTPUT;
#endif
#if defined(SPRACINGF3MINI)
#if defined(SPRACINGF3MINI) || defined(OMNIBUS)
// remap PWM6+7 as servos
if ((timerIndex == PWM6 || timerIndex == PWM7) && timerHardwarePtr->tim == TIM15)
type = MAP_TO_SERVO_OUTPUT;

View File

@ -22,7 +22,8 @@ typedef enum {
OWNER_SDCARD,
OWNER_FLASH,
OWNER_USB,
OWNER_BEEPER
OWNER_BEEPER,
OWNER_OSD
} resourceOwner_t;
// Currently TIMER should be shared resource (softserial dualtimer and timerqueue needs to allocate timer channel, but pin can be used for other function)

View File

@ -58,6 +58,7 @@
#include "io/gimbal.h"
#include "io/serial.h"
#include "io/gps.h"
#include "io/osd.h"
#include "io/vtx.h"
#include "flight/failsafe.h"

View File

@ -105,6 +105,7 @@
#ifdef OSD
#include "drivers/max7456.h"
#include "drivers/max7456_symbols.h"
#ifdef USE_RTC6705
#include "drivers/vtx_soft_spi_rtc6705.h"
@ -668,30 +669,39 @@ void updateOsd(void)
}
if (masterConfig.osdProfile.item_pos[OSD_MAIN_BATT_VOLTAGE] != -1) {
sprintf(line, "\x01%d.%1d", vbat / 10, vbat % 10);
line[0] = SYM_VOLT;
sprintf(line+1, "%d.%1d", vbat / 10, vbat % 10);
max7456_write_string(line, masterConfig.osdProfile.item_pos[OSD_MAIN_BATT_VOLTAGE]);
}
if (masterConfig.osdProfile.item_pos[OSD_RSSI_VALUE] != -1) {
sprintf(line, "\x02%d", rssi / 10);
line[0] = SYM_RSSI;
sprintf(line+1, "%d", rssi / 10);
max7456_write_string(line, masterConfig.osdProfile.item_pos[OSD_RSSI_VALUE]);
}
if (masterConfig.osdProfile.item_pos[OSD_THROTTLE_POS] != -1) {
sprintf(line, "\x03%3d", (constrain(rcData[THROTTLE], PWM_RANGE_MIN, PWM_RANGE_MAX) - PWM_RANGE_MIN) * 100 / (PWM_RANGE_MAX - PWM_RANGE_MIN));
line[0] = SYM_THR;
line[1] = SYM_THR1;
sprintf(line+2, "%3d", (constrain(rcData[THROTTLE], PWM_RANGE_MIN, PWM_RANGE_MAX) - PWM_RANGE_MIN) * 100 / (PWM_RANGE_MAX - PWM_RANGE_MIN));
max7456_write_string(line, masterConfig.osdProfile.item_pos[OSD_THROTTLE_POS]);
}
if (masterConfig.osdProfile.item_pos[OSD_TIMER] != -1) {
if (armed) {
seconds = armed_seconds + ((now-armed_at) / 1000000);
sprintf(line, "\x04 %02d:%02d", seconds / 60, seconds % 60);
line[0] = SYM_FLY_M;
sprintf(line+1, " %02d:%02d", seconds / 60, seconds % 60);
} else {
line[0] = SYM_ON_M;
seconds = now / 1000000;
sprintf(line, "\x05 %02d:%02d", seconds / 60, seconds % 60);
sprintf(line+1, " %02d:%02d", seconds / 60, seconds % 60);
}
max7456_write_string(line, masterConfig.osdProfile.item_pos[OSD_TIMER]);
}
if (masterConfig.osdProfile.item_pos[OSD_CPU_LOAD] != -1) {
print_average_system_load(masterConfig.osdProfile.item_pos[OSD_CPU_LOAD], 0);
}
if (masterConfig.osdProfile.item_pos[OSD_ARTIFICIAL_HORIZON] != -1) {
max7456_artificial_horizon(attitude.values.roll, attitude.values.pitch, masterConfig.osdProfile.item_pos[OSD_HORIZON_SIDEBARS] != -1);
}
}
} else {
max7456_draw_screen_fast();
@ -707,8 +717,25 @@ void osdInit(void)
rtc6705_soft_spi_set_channel(vtx_freq[current_vtx_channel]);
rtc6705_soft_spi_set_rf_power(masterConfig.vtx_power);
#endif
max7456_init(masterConfig.osdProfile.system);
max7456_init(masterConfig.osdProfile.video_system);
}
void resetOsdConfig(void)
{
featureSet(FEATURE_OSD);
masterConfig.osdProfile.video_system = AUTO;
masterConfig.osdProfile.item_pos[OSD_MAIN_BATT_VOLTAGE] = -29;
masterConfig.osdProfile.item_pos[OSD_RSSI_VALUE] = -59;
masterConfig.osdProfile.item_pos[OSD_TIMER] = -39;
masterConfig.osdProfile.item_pos[OSD_THROTTLE_POS] = -9;
masterConfig.osdProfile.item_pos[OSD_CPU_LOAD] = 26;
masterConfig.osdProfile.item_pos[OSD_VTX_CHANNEL] = 1;
masterConfig.osdProfile.item_pos[OSD_VOLTAGE_WARNING] = -80;
masterConfig.osdProfile.item_pos[OSD_ARMED] = -107;
masterConfig.osdProfile.item_pos[OSD_DISARMED] = -109;
masterConfig.osdProfile.item_pos[OSD_ARTIFICIAL_HORIZON] = -1;
masterConfig.osdProfile.item_pos[OSD_HORIZON_SIDEBARS] = -1;
}
#endif

View File

@ -48,14 +48,18 @@ typedef enum {
OSD_VOLTAGE_WARNING,
OSD_ARMED,
OSD_DISARMED,
OSD_ARTIFICIAL_HORIZON,
OSD_HORIZON_SIDEBARS,
OSD_MAX_ITEMS, // MUST BE LAST
} osd_items_t;
typedef struct {
uint8_t system;
// AUTO / PAL / NTSC in VIDEO_TYPES enum
uint8_t video_system;
int16_t item_pos[OSD_MAX_ITEMS];
} osd_profile;
void updateOsd(void);
void osdInit(void);
void resetOsdConfig(void);

View File

@ -204,7 +204,7 @@ static const char * const featureNames[] = {
"SONAR", "TELEMETRY", "CURRENT_METER", "3D", "RX_PARALLEL_PWM",
"RX_MSP", "RSSI_ADC", "LED_STRIP", "DISPLAY", "ONESHOT125",
"BLACKBOX", "CHANNEL_FORWARDING", "TRANSPONDER", "AIRMODE", "SUPEREXPO_RATES",
NULL
"OSD", NULL
};
// sync this with rxFailsafeChannelMode_e
@ -809,7 +809,7 @@ const clivalue_t valueTable[] = {
{ "vtx_power", VAR_UINT8 | MASTER_VALUE, &masterConfig.vtx_power, .config.minmax = { 0, 1 } },
#endif
#ifdef OSD
{ "osd_system", VAR_UINT8 | MASTER_VALUE, &masterConfig.osdProfile.system, .config.minmax = { 0, 2 } },
{ "osd_video_system", VAR_UINT8 | MASTER_VALUE, &masterConfig.osdProfile.video_system, .config.minmax = { 0, 2 } },
{ "osd_main_voltage_pos", VAR_INT16 | MASTER_VALUE, &masterConfig.osdProfile.item_pos[OSD_MAIN_BATT_VOLTAGE], .config.minmax = { -480, 480 } },
{ "osd_rssi_pos", VAR_INT16 | MASTER_VALUE, &masterConfig.osdProfile.item_pos[OSD_RSSI_VALUE], .config.minmax = { -480, 480 } },
{ "osd_timer_pos", VAR_INT16 | MASTER_VALUE, &masterConfig.osdProfile.item_pos[OSD_TIMER], .config.minmax = { -480, 480 } },
@ -819,6 +819,8 @@ const clivalue_t valueTable[] = {
{ "osd_voltage_warning_pos", VAR_INT16 | MASTER_VALUE, &masterConfig.osdProfile.item_pos[OSD_VOLTAGE_WARNING], .config.minmax = { -480, 480 } },
{ "osd_armed_pos", VAR_INT16 | MASTER_VALUE, &masterConfig.osdProfile.item_pos[OSD_ARMED], .config.minmax = { -480, 480 } },
{ "osd_disarmed_pos", VAR_INT16 | MASTER_VALUE, &masterConfig.osdProfile.item_pos[OSD_DISARMED], .config.minmax = { -480, 480 } },
{ "osd_artificial_horizon", VAR_INT16 | MASTER_VALUE, &masterConfig.osdProfile.item_pos[OSD_ARTIFICIAL_HORIZON], .config.minmax = { -1, 0 } },
{ "osd_horizon_sidebars", VAR_INT16 | MASTER_VALUE, &masterConfig.osdProfile.item_pos[OSD_HORIZON_SIDEBARS], .config.minmax = { -1, 0 } },
#endif
};

View File

@ -1214,6 +1214,21 @@ static bool processOutCommand(uint8_t cmdMSP)
#endif
break;
case MSP_OSD_CONFIG:
#ifdef OSD
headSerialReply(2 + (OSD_MAX_ITEMS * 2));
serialize8(1); // OSD supported
// send video system (AUTO/PAL/NTSC)
serialize8(masterConfig.osdProfile.video_system);
for (i = 0; i < OSD_MAX_ITEMS; i++) {
serialize16(masterConfig.osdProfile.item_pos[i]);
}
#else
headSerialReply(1);
serialize8(0); // OSD not supported
#endif
break;
case MSP_BF_BUILD_INFO:
headSerialReply(11 + 4 + 4);
for (i = 0; i < 11; i++)
@ -1524,9 +1539,15 @@ static bool processInCommand(void)
#endif
#ifdef OSD
case MSP_SET_OSD_CONFIG:
masterConfig.osdProfile.system = read8();
for (i = 0; i < OSD_MAX_ITEMS; i++)
masterConfig.osdProfile.item_pos[i] = read16();
addr = read8();
// set all the other settings
if ((int8_t)addr == -1) {
masterConfig.osdProfile.video_system = read8();
}
// set a position setting
else {
masterConfig.osdProfile.item_pos[addr] = read16();
}
break;
case MSP_OSD_CHAR_WRITE:
addr = read8();

View File

@ -59,7 +59,7 @@
#define MSP_PROTOCOL_VERSION 0
#define API_VERSION_MAJOR 1 // increment when major changes are made
#define API_VERSION_MINOR 16 // increment when any change is made, reset to zero when major changes are released after changing API_VERSION_MAJOR
#define API_VERSION_MINOR 17 // increment when any change is made, reset to zero when major changes are released after changing API_VERSION_MAJOR
#define API_VERSION_LENGTH 2

View File

@ -197,7 +197,7 @@ void init(void)
EXTIInit();
#endif
#ifdef SPRACINGF3MINI
#if defined(SPRACINGF3MINI) || defined(OMNIBUS)
gpio_config_t buttonAGpioConfig = {
BUTTON_A_PIN,
Mode_IPU,
@ -416,11 +416,13 @@ void init(void)
}
#endif
#if defined(SPRACINGF3MINI) && defined(SONAR) && defined(USE_SOFTSERIAL1)
#if defined(SPRACINGF3MINI) || defined(OMNIBUS)
#if defined(SONAR) && defined(USE_SOFTSERIAL1)
if (feature(FEATURE_SONAR) && feature(FEATURE_SOFTSERIAL)) {
serialRemovePort(SERIAL_PORT_SOFTSERIAL1);
}
#endif
#endif
#ifdef USE_I2C
#if defined(NAZE)

View File

@ -0,0 +1,89 @@
#include <stdbool.h>
#include <stdint.h>
#include <platform.h>
#include "drivers/pwm_mapping.h"
const uint16_t multiPPM[] = {
PWM1 | (MAP_TO_PPM_INPUT << 8), // PPM input
PWM2 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM3 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM4 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM5 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM6 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM7 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM8 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM9 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM10 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM11 | (MAP_TO_MOTOR_OUTPUT << 8),
0xFFFF
};
const uint16_t multiPWM[] = {
PWM2 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM3 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM4 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM5 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM6 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM7 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM8 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM9 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM10 | (MAP_TO_MOTOR_OUTPUT << 8),
PWM11 | (MAP_TO_MOTOR_OUTPUT << 8),
0xFFFF
};
const uint16_t airPPM[] = {
PWM1 | (MAP_TO_PPM_INPUT << 8), // PPM input
PWM2 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #1
PWM3 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #2
PWM4 | (MAP_TO_SERVO_OUTPUT << 8), // servo #1
PWM5 | (MAP_TO_SERVO_OUTPUT << 8),
PWM6 | (MAP_TO_SERVO_OUTPUT << 8),
PWM7 | (MAP_TO_SERVO_OUTPUT << 8),
PWM8 | (MAP_TO_SERVO_OUTPUT << 8),
PWM9 | (MAP_TO_SERVO_OUTPUT << 8),
PWM10 | (MAP_TO_SERVO_OUTPUT << 8),
PWM11 | (MAP_TO_SERVO_OUTPUT << 8), // servo #8
0xFFFF
};
const uint16_t airPWM[] = {
PWM2 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #1
PWM3 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #2
PWM4 | (MAP_TO_SERVO_OUTPUT << 8), // servo #1
PWM5 | (MAP_TO_SERVO_OUTPUT << 8),
PWM6 | (MAP_TO_SERVO_OUTPUT << 8),
PWM7 | (MAP_TO_SERVO_OUTPUT << 8),
PWM8 | (MAP_TO_SERVO_OUTPUT << 8),
PWM9 | (MAP_TO_SERVO_OUTPUT << 8),
PWM10 | (MAP_TO_SERVO_OUTPUT << 8),
PWM11 | (MAP_TO_SERVO_OUTPUT << 8), // servo #8
0xFFFF
};
const timerHardware_t timerHardware[USABLE_TIMER_CHANNEL_COUNT] = {
// PPM Pad
{ TIM3, IO_TAG(PB4), TIM_Channel_1, TIM3_IRQn, 0, IOCFG_AF_PP, GPIO_AF_2, 0}, // PPM - PB4
// PB5 / TIM3 CH2 is connected to USBPresent
// Used by SPI1, MAX7456
//{ TIM16, IO_TAG(PA6), TIM_Channel_1, TIM1_UP_TIM16_IRQn, 1, IOCFG_AF_PP, GPIO_AF_1, 0}, // PWM1 - PA6
//{ TIM17, IO_TAG(PA7), TIM_Channel_1, TIM1_TRG_COM_TIM17_IRQn, 1, IOCFG_AF_PP, GPIO_AF_1, 0}, // PWM2 - PA7
{ TIM4, IO_TAG(PB8), TIM_Channel_3, TIM4_IRQn, 1, IOCFG_AF_PP, GPIO_AF_2, 0}, // PWM3 - PB8
{ TIM4, IO_TAG(PB9), TIM_Channel_4, TIM4_IRQn, 1, IOCFG_AF_PP, GPIO_AF_2, 0}, // PWM4 - PB9
{ TIM15, IO_TAG(PA2), TIM_Channel_1, TIM1_BRK_TIM15_IRQn, 1, IOCFG_AF_PP, GPIO_AF_9, 0}, // PWM5 - PA2
{ TIM15, IO_TAG(PA3), TIM_Channel_2, TIM1_BRK_TIM15_IRQn, 1, IOCFG_AF_PP, GPIO_AF_9, 0}, // PWM6 - PA3
{ TIM2, IO_TAG(PA0), TIM_Channel_1, TIM2_IRQn, 1, IOCFG_AF_PP, GPIO_AF_1, 0}, // PWM7 - PA0
{ TIM2, IO_TAG(PA1), TIM_Channel_2, TIM2_IRQn, 1, IOCFG_AF_PP, GPIO_AF_1, 0}, // PWM8 - PA1
// UART3 RX/TX
{ TIM2, IO_TAG(PB10), TIM_Channel_3, TIM2_IRQn, 1, IOCFG_AF_PP, GPIO_AF_1, 0}, // PWM9 - PB10 - TIM2_CH3 / USART3_TX (AF7)
{ TIM2, IO_TAG(PB11), TIM_Channel_4, TIM2_IRQn, 1, IOCFG_AF_PP, GPIO_AF_1, 0}, // PWM10 - PB11 - TIM2_CH4 / USART3_RX (AF7)
// LED Strip Pad
{ TIM1, IO_TAG(PA8), TIM_Channel_1, TIM1_CC_IRQn, 1, IOCFG_AF_PP, GPIO_AF_6, 0}, // GPIO_TIMER / LED_STRIP
};

View File

@ -0,0 +1,238 @@
/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define TARGET_BOARD_IDENTIFIER "OMNI" // https://en.wikipedia.org/wiki/Omnibus
#define CONFIG_FASTLOOP_PREFERRED_ACC ACC_NONE
#define LED0 PB3
#define BEEPER PC15
#define BEEPER_INVERTED
#define USABLE_TIMER_CHANNEL_COUNT 10 // 6 Outputs; PPM; LED Strip; 2 additional PWM pins also on UART3 RX/TX pins.
#define EXTI15_10_CALLBACK_HANDLER_COUNT 2 // MPU_INT, SDCardDetect
#define USE_EXTI
#define MPU_INT_EXTI PC13
#define USE_MPU_DATA_READY_SIGNAL
#define ENSURE_MPU_DATA_READY_IS_LOW
#define USE_MAG_DATA_READY_SIGNAL
#define ENSURE_MAG_DATA_READY_IS_HIGH
#define GYRO
//#define USE_FAKE_GYRO
#define USE_GYRO_MPU6500
#define ACC
//#define USE_FAKE_ACC
#define USE_ACC_MPU6500
#define ACC_MPU6500_ALIGN CW180_DEG
#define GYRO_MPU6500_ALIGN CW180_DEG
#define BARO
#define USE_BARO_BMP280
#define MAG
#define USE_MPU9250_MAG // Enables bypass configuration
#define USE_MAG_AK8975
#define USE_MAG_HMC5883 // External
#define MAG_AK8975_ALIGN CW90_DEG_FLIP
#define SONAR
#define SONAR_ECHO_PIN PB1
#define SONAR_TRIGGER_PIN PB0
#define USB_IO
#define USB_CABLE_DETECTION
#define USB_DETECT_PIN PB5
#define USE_VCP
#define USE_USART1
#define USE_USART2
#define USE_USART3
#define USE_SOFTSERIAL1
#define SERIAL_PORT_COUNT 5
#ifndef UART1_GPIO
#define UART1_TX_PIN GPIO_Pin_9 // PA9
#define UART1_RX_PIN GPIO_Pin_10 // PA10
#define UART1_GPIO GPIOA
#define UART1_GPIO_AF GPIO_AF_7
#define UART1_TX_PINSOURCE GPIO_PinSource9
#define UART1_RX_PINSOURCE GPIO_PinSource10
#endif
#define UART2_TX_PIN GPIO_Pin_14 // PA14 / SWCLK
#define UART2_RX_PIN GPIO_Pin_15 // PA15
#define UART2_GPIO GPIOA
#define UART2_GPIO_AF GPIO_AF_7
#define UART2_TX_PINSOURCE GPIO_PinSource14
#define UART2_RX_PINSOURCE GPIO_PinSource15
#ifndef UART3_GPIO
#define UART3_TX_PIN GPIO_Pin_10 // PB10 (AF7)
#define UART3_RX_PIN GPIO_Pin_11 // PB11 (AF7)
#define UART3_GPIO_AF GPIO_AF_7
#define UART3_GPIO GPIOB
#define UART3_TX_PINSOURCE GPIO_PinSource10
#define UART3_RX_PINSOURCE GPIO_PinSource11
#endif
#define SOFTSERIAL_1_TIMER TIM2
#define SOFTSERIAL_1_TIMER_RX_HARDWARE 9 // PA0 / PAD3
#define SOFTSERIAL_1_TIMER_TX_HARDWARE 10 // PA1 / PAD4
#define USE_I2C
#define I2C_DEVICE (I2CDEV_1) // PB6/SCL, PB7/SDA
#define USE_SPI
#define USE_SPI_DEVICE_1
#define SPI1_NSS_PIN PA4
#define SPI1_SCK_PIN PA5
#define SPI1_MISO_PIN PA6
#define SPI1_MOSI_PIN PA7
// OSD define info:
// feature name (includes source) -> MAX_OSD, used in target.mk
// include the osd code
#define OSD
// include the max7456 driver
#define USE_MAX7456
#define MAX7456_SPI_INSTANCE SPI1
#define MAX7456_SPI_CS_PIN SPI1_NSS_PIN
#define USE_SPI
#define USE_SPI_DEVICE_2 // PB12,13,14,15 on AF5
#define I2C_DEVICE (I2CDEV_1) // PB6/SCL, PB7/SDA
#define SPI2_NSS_PIN PB12
#define SPI2_SCK_PIN PB13
#define SPI2_MISO_PIN PB14
#define SPI2_MOSI_PIN PB15
#define USE_SDCARD
#define USE_SDCARD_SPI2
#define SDCARD_DETECT_INVERTED
#define SDCARD_DETECT_PIN PC14
#define SDCARD_SPI_INSTANCE SPI2
#define SDCARD_SPI_CS_PIN SPI2_NSS_PIN
// SPI2 is on the APB1 bus whose clock runs at 36MHz. Divide to under 400kHz for init:
#define SDCARD_SPI_INITIALIZATION_CLOCK_DIVIDER 128
// Divide to under 25MHz for normal operation:
#define SDCARD_SPI_FULL_SPEED_CLOCK_DIVIDER 2
// Note, this is the same DMA channel as USART1_RX. Luckily we don't use DMA for USART Rx.
#define SDCARD_DMA_CHANNEL_TX DMA1_Channel5
#define SDCARD_DMA_CHANNEL_TX_COMPLETE_FLAG DMA1_FLAG_TC5
// Performance logging for SD card operations:
// #define AFATFS_USE_INTROSPECTIVE_LOGGING
#define USE_ADC
#define BOARD_HAS_VOLTAGE_DIVIDER
#define ADC_INSTANCE ADC2
#define ADC_DMA_CHANNEL DMA2_Channel1
#define ADC_AHB_PERIPHERAL RCC_AHBPeriph_DMA2
#define VBAT_ADC_GPIO GPIOA
#define VBAT_ADC_GPIO_PIN GPIO_Pin_4
#define VBAT_ADC_CHANNEL ADC_Channel_1
#define CURRENT_METER_ADC_GPIO GPIOA
#define CURRENT_METER_ADC_GPIO_PIN GPIO_Pin_5
#define CURRENT_METER_ADC_CHANNEL ADC_Channel_2
#define RSSI_ADC_GPIO GPIOB
#define RSSI_ADC_GPIO_PIN GPIO_Pin_2
#define RSSI_ADC_CHANNEL ADC_Channel_12
#define LED_STRIP
#define LED_STRIP_TIMER TIM1
#define WS2811_GPIO GPIOA
#define WS2811_GPIO_AHB_PERIPHERAL RCC_AHBPeriph_GPIOA
#define WS2811_GPIO_AF GPIO_AF_6
#define WS2811_PIN GPIO_Pin_8
#define WS2811_PIN_SOURCE GPIO_PinSource8
#define WS2811_TIMER TIM1
#define WS2811_TIMER_APB2_PERIPHERAL RCC_APB2Periph_TIM1
#define WS2811_DMA_CHANNEL DMA1_Channel2
#define WS2811_IRQ DMA1_Channel2_IRQn
#define WS2811_DMA_TC_FLAG DMA1_FLAG_TC2
#define WS2811_DMA_HANDLER_IDENTIFER DMA1_CH2_HANDLER
//#define TRANSPONDER
//#define TRANSPONDER_GPIO GPIOA
//#define TRANSPONDER_GPIO_AHB_PERIPHERAL RCC_AHBPeriph_GPIOA
//#define TRANSPONDER_GPIO_AF GPIO_AF_6
//#define TRANSPONDER_PIN GPIO_Pin_8
//#define TRANSPONDER_PIN_SOURCE GPIO_PinSource8
//#define TRANSPONDER_TIMER TIM1
//#define TRANSPONDER_TIMER_APB2_PERIPHERAL RCC_APB2Periph_TIM1
//#define TRANSPONDER_DMA_CHANNEL DMA1_Channel2
//#define TRANSPONDER_IRQ DMA1_Channel2_IRQn
//#define TRANSPONDER_DMA_TC_FLAG DMA1_FLAG_TC2
//#define TRANSPONDER_DMA_HANDLER_IDENTIFER DMA1_CH2_HANDLER
//#define REDUCE_TRANSPONDER_CURRENT_DRAW_WHEN_USB_CABLE_PRESENT
#define ENABLE_BLACKBOX_LOGGING_ON_SDCARD_BY_DEFAULT
#define DEFAULT_RX_FEATURE FEATURE_RX_PPM
#define BUTTONS
#define BUTTON_A_PORT GPIOB
#define BUTTON_A_PIN Pin_1
#define BUTTON_B_PORT GPIOB
#define BUTTON_B_PIN Pin_0
#define SPEKTRUM_BIND
// USART3,
#define BIND_PIN PB11
#define HARDWARE_BIND_PLUG
#define BINDPLUG_PIN PB0
#define USE_SERIAL_4WAY_BLHELI_INTERFACE
#define TARGET_IO_PORTA 0xffff
#define TARGET_IO_PORTB 0xffff
#define TARGET_IO_PORTC (BIT(13)|BIT(14)|BIT(15))
#define TARGET_IO_PORTF (BIT(0)|BIT(1)|BIT(4))
#define USED_TIMERS (TIM_N(1) | TIM_N(2) | TIM_N(3) | TIM_N(4) | TIM_N(15))
#define TIMER_APB1_PERIPHERALS (RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3 | RCC_APB1Periph_TIM4)
#define TIMER_APB2_PERIPHERALS (RCC_APB2Periph_TIM1 | RCC_APB2Periph_TIM15)
#define TIMER_AHB_PERIPHERALS (RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB)

View File

@ -0,0 +1,16 @@
F3_TARGETS += $(TARGET)
FEATURES = VCP SDCARD MAX_OSD
TARGET_SRC = \
drivers/accgyro_mpu.c \
drivers/accgyro_mpu6500.c \
drivers/barometer_bmp280.c \
drivers/compass_ak8975.c \
drivers/compass_hmc5883l.c \
drivers/flash_m25p16.c \
drivers/light_ws2811strip.c \
drivers/light_ws2811strip_stm32f30x.c \
drivers/serial_softserial.c \
drivers/serial_usb_vcp.c \
drivers/sonar_hcsr04.c

View File

@ -109,9 +109,8 @@
#define SPI3_MOSI_PIN PB5
#define USE_MAX7456
#define MAX7456_CS_GPIO GPIOA
#define MAX7456_CS_PIN GPIO_Pin_15
#define MAX7456_SPI_INSTANCE SPI3
#define MAX7456_SPI_INSTANCE SPI3
#define MAX7456_SPI_CS_PIN PA15
#define USE_RTC6705
#define RTC6705_SPIDATA_GPIO GPIOC
@ -155,11 +154,7 @@
#define VBAT_ADC_CHANNEL ADC_Channel_1
//#define USE_QUAD_MIXER_ONLY
#define BLACKBOX
#define ENABLE_BLACKBOX_LOGGING_ON_SDCARD_BY_DEFAULT
#define TELEMETRY
#define SERIAL_RX
#define USE_CLI
#define OSD
#define CONFIG_FASTLOOP_PREFERRED_ACC ACC_DEFAULT

View File

@ -1,4 +1,4 @@
F3_TARGETS += $(TARGET)
F3_TARGETS += $(TARGET)
FEATURES = VCP SDCARD MAX_OSD
TARGET_SRC = \

View File

@ -1,18 +1,28 @@
/*
* This file is part of Cleanflight.
* Supports the GY-91 MPU9250 and BMP280 development board via SPI1
*
* Cleanflight is free software: you can redistribute it and/or modify
* Put the MAX7456 on SPI2 instead of an SDCARD
* MAX7456 CS -> PB12 (default)
* Uses the default pins for SPI2:
* #define SPI2_NSS_PIN PB12
* #define SPI2_SCK_PIN PB13
* #define SPI2_MISO_PIN PB14
* #define SPI2_MOSI_PIN PB15
*
* @author Nathan Tsoi
*
* This software is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
* along with this software. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
@ -38,11 +48,11 @@
#define SPI2_MISO_PIN PB14
#define SPI2_MOSI_PIN PB15
#define USE_SD_CARD
#define SD_DETECT_PIN PC14
#define SD_CS_PIN PB12
#define SD_SPI_INSTANCE SPI2
//#define USE_SD_CARD
//
//#define SD_DETECT_PIN PC14
//#define SD_CS_PIN PB12
//#define SD_SPI_INSTANCE SPI2
//#define USE_FLASHFS
//#define USE_FLASH_M25P16
@ -64,31 +74,50 @@
#define GYRO
#define USE_GYRO_L3GD20
#define L3GD20_SPI SPI1
#define L3GD20_CS_PIN PE3
#define GYRO_L3GD20_ALIGN CW270_DEG
#define USE_SDCARD
#define USE_SDCARD_SPI2
#define SDCARD_SPI_INSTANCE SPI2
#define SDCARD_SPI_CS_PIN PB12
// SPI2 is on the APB1 bus whose clock runs at 36MHz. Divide to under 400kHz for init:
#define SDCARD_SPI_INITIALIZATION_CLOCK_DIVIDER 128
// Divide to under 25MHz for normal operation:
#define SDCARD_SPI_FULL_SPEED_CLOCK_DIVIDER 2
// Note, this is the same DMA channel as USART1_RX. Luckily we don't use DMA for USART Rx.
#define SDCARD_DMA_CHANNEL_TX DMA1_Channel5
#define SDCARD_DMA_CHANNEL_TX_COMPLETE_FLAG DMA1_FLAG_TC5
// Performance logging for SD card operations:
// #define AFATFS_USE_INTROSPECTIVE_LOGGING
// Support the GY-91 MPU9250 dev board
#define USE_GYRO_MPU6500
#define USE_GYRO_SPI_MPU6500
#define MPU6500_CS_PIN PC14
#define MPU6500_SPI_INSTANCE SPI2
#define GYRO_MPU6500_ALIGN CW270_DEG_FLIP
#define ACC
#define USE_ACC_LSM303DLHC
#define USE_ACC_MPU6500
#define USE_ACC_SPI_MPU6500
#define ACC_MPU6500_ALIGN CW270_DEG_FLIP
//#define BARO
//#define BMP280_CS_PIN PB12
//#define BMP280_SPI_INSTANCE SPI2
//#define USE_BARO_BMP280
//#define USE_BARO_SPI_BMP280
#define OSD
#define USE_MAX7456
#define MAX7456_SPI_INSTANCE SPI2
#define MAX7456_SPI_CS_PIN SPI2_NSS_PIN
//#define USE_SDCARD
//#define USE_SDCARD_SPI2
//
//#define SDCARD_SPI_INSTANCE SPI2
//#define SDCARD_SPI_CS_PIN PB12
//// SPI2 is on the APB1 bus whose clock runs at 36MHz. Divide to under 400kHz for init:
//#define SDCARD_SPI_INITIALIZATION_CLOCK_DIVIDER 128
//// Divide to under 25MHz for normal operation:
//#define SDCARD_SPI_FULL_SPEED_CLOCK_DIVIDER 2
//
//// Note, this is the same DMA channel as USART1_RX. Luckily we don't use DMA for USART Rx.
//#define SDCARD_DMA_CHANNEL_TX DMA1_Channel5
//#define SDCARD_DMA_CHANNEL_TX_COMPLETE_FLAG DMA1_FLAG_TC5
// Performance logging for SD card operations:
// #define AFATFS_USE_INTROSPECTIVE_LOGGING
#define MAG
#define USE_MAG_HMC5883
@ -98,6 +127,14 @@
#define USE_USART2
#define SERIAL_PORT_COUNT 3
// uart2 gpio for shared serial rx/ppm
//#define UART2_TX_PIN GPIO_Pin_5 // PD5
//#define UART2_RX_PIN GPIO_Pin_6 // PD6
//#define UART2_GPIO GPIOD
//#define UART2_GPIO_AF GPIO_AF_7
//#define UART2_TX_PINSOURCE GPIO_PinSource5
//#define UART2_RX_PINSOURCE GPIO_PinSource6
#define USE_I2C
#define I2C_DEVICE (I2CDEV_1)

View File

@ -1,407 +0,0 @@
/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
extern "C" {
#include "platform.h"
#include "scheduler/scheduler.h"
}
#include "unittest_macros.h"
#include "gtest/gtest.h"
enum {
systemTime = 10,
pidLoopCheckerTime = 650,
updateAccelerometerTime = 192,
handleSerialTime = 30,
updateBeeperTime = 1,
updateBatteryTime = 1,
updateRxCheckTime = 34,
updateRxMainTime = 10,
processGPSTime = 10,
updateCompassTime = 195,
updateBaroTime = 201,
updateSonarTime = 10,
calculateAltitudeTime = 154,
updateDisplayTime = 10,
telemetryTime = 10,
ledStripTime = 10,
transponderTime = 10
};
extern "C" {
cfTask_t * unittest_scheduler_selectedTask;
uint8_t unittest_scheduler_selectedTaskDynPrio;
uint16_t unittest_scheduler_waitingTasks;
uint32_t unittest_scheduler_timeToNextRealtimeTask;
bool unittest_outsideRealtimeGuardInterval;
// set up micros() to simulate time
uint32_t simulatedTime = 0;
uint32_t micros(void) {return simulatedTime;}
// set up tasks to take a simulated representative time to execute
void taskMainPidLoopChecker(void) {simulatedTime+=pidLoopCheckerTime;}
void taskUpdateAccelerometer(void) {simulatedTime+=updateAccelerometerTime;}
void taskHandleSerial(void) {simulatedTime+=handleSerialTime;}
void taskUpdateBeeper(void) {simulatedTime+=updateBeeperTime;}
void taskUpdateBattery(void) {simulatedTime+=updateBatteryTime;}
bool taskUpdateRxCheck(uint32_t currentDeltaTime) {UNUSED(currentDeltaTime);simulatedTime+=updateRxCheckTime;return false;}
void taskUpdateRxMain(void) {simulatedTime+=updateRxMainTime;}
void taskProcessGPS(void) {simulatedTime+=processGPSTime;}
void taskUpdateCompass(void) {simulatedTime+=updateCompassTime;}
void taskUpdateBaro(void) {simulatedTime+=updateBaroTime;}
void taskUpdateSonar(void) {simulatedTime+=updateSonarTime;}
void taskCalculateAltitude(void) {simulatedTime+=calculateAltitudeTime;}
void taskUpdateDisplay(void) {simulatedTime+=updateDisplayTime;}
void taskTelemetry(void) {simulatedTime+=telemetryTime;}
void taskLedStrip(void) {simulatedTime+=ledStripTime;}
void taskTransponder(void) {simulatedTime+=transponderTime;}
extern cfTask_t* taskQueueArray[];
extern void queueClear(void);
extern int queueSize();
extern bool queueContains(cfTask_t *task);
extern bool queueAdd(cfTask_t *task);
extern bool queueRemove(cfTask_t *task);
extern cfTask_t *queueFirst(void);
extern cfTask_t *queueNext(void);
}
TEST(SchedulerUnittest, TestPriorites)
{
EXPECT_EQ(14, TASK_COUNT);
// if any of these fail then task priorities have changed and ordering in TestQueue needs to be re-checked
EXPECT_EQ(TASK_PRIORITY_HIGH, cfTasks[TASK_SYSTEM].staticPriority);
EXPECT_EQ(TASK_PRIORITY_REALTIME, cfTasks[TASK_GYROPID].staticPriority);
EXPECT_EQ(TASK_PRIORITY_MEDIUM, cfTasks[TASK_ACCEL].staticPriority);
EXPECT_EQ(TASK_PRIORITY_LOW, cfTasks[TASK_SERIAL].staticPriority);
EXPECT_EQ(TASK_PRIORITY_MEDIUM, cfTasks[TASK_BATTERY].staticPriority);
}
TEST(SchedulerUnittest, TestQueueInit)
{
queueClear();
EXPECT_EQ(0, queueSize());
EXPECT_EQ(0, queueFirst());
EXPECT_EQ(0, queueNext());
for (int ii = 0; ii <= TASK_COUNT; ++ii) {
EXPECT_EQ(0, taskQueueArray[ii]);
}
}
cfTask_t *deadBeefPtr = reinterpret_cast<cfTask_t*>(0xDEADBEEF);
TEST(SchedulerUnittest, TestQueue)
{
queueClear();
taskQueueArray[TASK_COUNT + 1] = deadBeefPtr;
queueAdd(&cfTasks[TASK_SYSTEM]); // TASK_PRIORITY_HIGH
EXPECT_EQ(1, queueSize());
EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueFirst());
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
queueAdd(&cfTasks[TASK_GYROPID]); // TASK_PRIORITY_REALTIME
EXPECT_EQ(2, queueSize());
EXPECT_EQ(&cfTasks[TASK_GYROPID], queueFirst());
EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueNext());
EXPECT_EQ(NULL, queueNext());
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
queueAdd(&cfTasks[TASK_SERIAL]); // TASK_PRIORITY_LOW
EXPECT_EQ(3, queueSize());
EXPECT_EQ(&cfTasks[TASK_GYROPID], queueFirst());
EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueNext());
EXPECT_EQ(&cfTasks[TASK_SERIAL], queueNext());
EXPECT_EQ(NULL, queueNext());
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
queueAdd(&cfTasks[TASK_BATTERY]); // TASK_PRIORITY_MEDIUM
EXPECT_EQ(4, queueSize());
EXPECT_EQ(&cfTasks[TASK_GYROPID], queueFirst());
EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueNext());
EXPECT_EQ(&cfTasks[TASK_BATTERY], queueNext());
EXPECT_EQ(&cfTasks[TASK_SERIAL], queueNext());
EXPECT_EQ(NULL, queueNext());
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
queueAdd(&cfTasks[TASK_RX]); // TASK_PRIORITY_HIGH
EXPECT_EQ(5, queueSize());
EXPECT_EQ(&cfTasks[TASK_GYROPID], queueFirst());
EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueNext());
EXPECT_EQ(&cfTasks[TASK_RX], queueNext());
EXPECT_EQ(&cfTasks[TASK_BATTERY], queueNext());
EXPECT_EQ(&cfTasks[TASK_SERIAL], queueNext());
EXPECT_EQ(NULL, queueNext());
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
queueRemove(&cfTasks[TASK_SYSTEM]); // TASK_PRIORITY_HIGH
EXPECT_EQ(4, queueSize());
EXPECT_EQ(&cfTasks[TASK_GYROPID], queueFirst());
EXPECT_EQ(&cfTasks[TASK_RX], queueNext());
EXPECT_EQ(&cfTasks[TASK_BATTERY], queueNext());
EXPECT_EQ(&cfTasks[TASK_SERIAL], queueNext());
EXPECT_EQ(NULL, queueNext());
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
}
TEST(SchedulerUnittest, TestQueueAddAndRemove)
{
queueClear();
taskQueueArray[TASK_COUNT + 1] = deadBeefPtr;
// fill up the queue
for (int taskId = 0; taskId < TASK_COUNT; ++taskId) {
const bool added = queueAdd(&cfTasks[taskId]);
EXPECT_EQ(true, added);
EXPECT_EQ(taskId + 1, queueSize());
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
}
// double check end of queue
EXPECT_EQ(TASK_COUNT, queueSize());
EXPECT_NE(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT - 1]); // last item was indeed added to queue
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT]); // null pointer at end of queue is preserved
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]); // there hasn't been an out by one error
// and empty it again
for (int taskId = 0; taskId < TASK_COUNT; ++taskId) {
const bool removed = queueRemove(&cfTasks[taskId]);
EXPECT_EQ(true, removed);
EXPECT_EQ(TASK_COUNT - taskId - 1, queueSize());
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - taskId]);
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
}
// double check size and end of queue
EXPECT_EQ(0, queueSize()); // queue is indeed empty
EXPECT_EQ(NULL, taskQueueArray[0]); // there is a null pointer at the end of the queueu
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]); // no accidental overwrites past end of queue
}
TEST(SchedulerUnittest, TestQueueArray)
{
// test there are no "out by one" errors or buffer overruns when items are added and removed
queueClear();
taskQueueArray[TASK_COUNT + 1] = deadBeefPtr; // note, must set deadBeefPtr after queueClear
for (int taskId = 0; taskId < TASK_COUNT - 1; ++taskId) {
setTaskEnabled(static_cast<cfTaskId_e>(taskId), true);
EXPECT_EQ(taskId + 1, queueSize());
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
}
EXPECT_EQ(TASK_COUNT - 1, queueSize());
EXPECT_NE(static_cast<cfTask_t*>(0), taskQueueArray[TASK_COUNT - 2]);
const cfTask_t *lastTaskPrev = taskQueueArray[TASK_COUNT - 2];
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 1]);
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT]);
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
setTaskEnabled(TASK_SYSTEM, false);
EXPECT_EQ(TASK_COUNT - 2, queueSize());
EXPECT_EQ(lastTaskPrev, taskQueueArray[TASK_COUNT - 3]);
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 2]); // NULL at end of queue
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 1]);
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT]);
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
taskQueueArray[TASK_COUNT - 2] = 0;
setTaskEnabled(TASK_SYSTEM, true);
EXPECT_EQ(TASK_COUNT - 1, queueSize());
EXPECT_EQ(lastTaskPrev, taskQueueArray[TASK_COUNT - 2]);
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 1]);
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT]);
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
cfTaskInfo_t taskInfo;
getTaskInfo(static_cast<cfTaskId_e>(TASK_COUNT - 1), &taskInfo);
EXPECT_EQ(false, taskInfo.isEnabled);
setTaskEnabled(static_cast<cfTaskId_e>(TASK_COUNT - 1), true);
EXPECT_EQ(TASK_COUNT, queueSize());
EXPECT_EQ(lastTaskPrev, taskQueueArray[TASK_COUNT - 1]);
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT]); // check no buffer overrun
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
setTaskEnabled(TASK_SYSTEM, false);
EXPECT_EQ(TASK_COUNT - 1, queueSize());
//EXPECT_EQ(lastTaskPrev, taskQueueArray[TASK_COUNT - 3]);
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 1]);
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT]);
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
setTaskEnabled(TASK_ACCEL, false);
EXPECT_EQ(TASK_COUNT - 2, queueSize());
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 2]);
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 1]);
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT]);
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
setTaskEnabled(TASK_BATTERY, false);
EXPECT_EQ(TASK_COUNT - 3, queueSize());
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 3]);
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 2]);
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT - 1]);
EXPECT_EQ(NULL, taskQueueArray[TASK_COUNT]);
EXPECT_EQ(deadBeefPtr, taskQueueArray[TASK_COUNT + 1]);
}
TEST(SchedulerUnittest, TestSchedulerInit)
{
schedulerInit();
EXPECT_EQ(1, queueSize());
EXPECT_EQ(&cfTasks[TASK_SYSTEM], queueFirst());
}
TEST(SchedulerUnittest, TestScheduleEmptyQueue)
{
queueClear();
simulatedTime = 4000;
// run the with an empty queue
scheduler();
EXPECT_EQ(NULL, unittest_scheduler_selectedTask);
}
TEST(SchedulerUnittest, TestSingleTask)
{
schedulerInit();
// disable all tasks except TASK_GYROPID
for (int taskId=0; taskId < TASK_COUNT; ++taskId) {
setTaskEnabled(static_cast<cfTaskId_e>(taskId), false);
}
setTaskEnabled(TASK_GYROPID, true);
cfTasks[TASK_GYROPID].lastExecutedAt = 1000;
simulatedTime = 4000;
// run the scheduler and check the task has executed
scheduler();
EXPECT_NE(static_cast<cfTask_t*>(0), unittest_scheduler_selectedTask);
EXPECT_EQ(&cfTasks[TASK_GYROPID], unittest_scheduler_selectedTask);
EXPECT_EQ(3000, cfTasks[TASK_GYROPID].taskLatestDeltaTime);
EXPECT_EQ(4000, cfTasks[TASK_GYROPID].lastExecutedAt);
EXPECT_EQ(pidLoopCheckerTime, cfTasks[TASK_GYROPID].totalExecutionTime);
// task has run, so its dynamic priority should have been set to zero
EXPECT_EQ(0, cfTasks[TASK_GYROPID].dynamicPriority);
}
TEST(SchedulerUnittest, TestTwoTasks)
{
// disable all tasks except TASK_GYROPID and TASK_ACCEL
for (int taskId=0; taskId < TASK_COUNT; ++taskId) {
setTaskEnabled(static_cast<cfTaskId_e>(taskId), false);
}
setTaskEnabled(TASK_ACCEL, true);
setTaskEnabled(TASK_GYROPID, true);
// set it up so that TASK_ACCEL ran just before TASK_GYROPID
static const uint32_t startTime = 4000;
simulatedTime = startTime;
cfTasks[TASK_GYROPID].lastExecutedAt = simulatedTime;
cfTasks[TASK_ACCEL].lastExecutedAt = cfTasks[TASK_GYROPID].lastExecutedAt - updateAccelerometerTime;
EXPECT_EQ(0, cfTasks[TASK_ACCEL].taskAgeCycles);
// run the scheduler
scheduler();
// no tasks should have run, since neither task's desired time has elapsed
EXPECT_EQ(static_cast<cfTask_t*>(0), unittest_scheduler_selectedTask);
// NOTE:
// TASK_GYROPID desiredPeriod is 1000 microseconds
// TASK_ACCEL desiredPeriod is 10000 microseconds
// 500 microseconds later
simulatedTime += 500;
// no tasks should run, since neither task's desired time has elapsed
scheduler();
EXPECT_EQ(static_cast<cfTask_t*>(0), unittest_scheduler_selectedTask);
EXPECT_EQ(0, unittest_scheduler_waitingTasks);
// 500 microseconds later, TASK_GYROPID desiredPeriod has elapsed
simulatedTime += 500;
// TASK_GYROPID should now run
scheduler();
EXPECT_EQ(&cfTasks[TASK_GYROPID], unittest_scheduler_selectedTask);
EXPECT_EQ(1, unittest_scheduler_waitingTasks);
EXPECT_EQ(5000 + pidLoopCheckerTime, simulatedTime);
simulatedTime += 1000 - pidLoopCheckerTime;
scheduler();
// TASK_GYROPID should run again
EXPECT_EQ(&cfTasks[TASK_GYROPID], unittest_scheduler_selectedTask);
scheduler();
EXPECT_EQ(static_cast<cfTask_t*>(0), unittest_scheduler_selectedTask);
EXPECT_EQ(0, unittest_scheduler_waitingTasks);
simulatedTime = startTime + 10500; // TASK_GYROPID and TASK_ACCEL desiredPeriods have elapsed
// of the two TASK_GYROPID should run first
scheduler();
EXPECT_EQ(&cfTasks[TASK_GYROPID], unittest_scheduler_selectedTask);
// and finally TASK_ACCEL should now run
scheduler();
EXPECT_EQ(&cfTasks[TASK_ACCEL], unittest_scheduler_selectedTask);
}
TEST(SchedulerUnittest, TestRealTimeGuardInNoTaskRun)
{
// disable all tasks except TASK_GYROPID and TASK_SYSTEM
for (int taskId=0; taskId < TASK_COUNT; ++taskId) {
setTaskEnabled(static_cast<cfTaskId_e>(taskId), false);
}
setTaskEnabled(TASK_GYROPID, true);
cfTasks[TASK_GYROPID].lastExecutedAt = 200000;
simulatedTime = 200700;
setTaskEnabled(TASK_SYSTEM, true);
cfTasks[TASK_SYSTEM].lastExecutedAt = 100000;
scheduler();
EXPECT_EQ(false, unittest_outsideRealtimeGuardInterval);
EXPECT_EQ(300, unittest_scheduler_timeToNextRealtimeTask);
// Nothing should be scheduled in guard period
EXPECT_EQ(NULL, unittest_scheduler_selectedTask);
EXPECT_EQ(100000, cfTasks[TASK_SYSTEM].lastExecutedAt);
EXPECT_EQ(200000, cfTasks[TASK_GYROPID].lastExecutedAt);
}
TEST(SchedulerUnittest, TestRealTimeGuardOutTaskRun)
{
// disable all tasks except TASK_GYROPID and TASK_SYSTEM
for (int taskId=0; taskId < TASK_COUNT; ++taskId) {
setTaskEnabled(static_cast<cfTaskId_e>(taskId), false);
}
setTaskEnabled(TASK_GYROPID, true);
cfTasks[TASK_GYROPID].lastExecutedAt = 200000;
simulatedTime = 200699;
setTaskEnabled(TASK_SYSTEM, true);
cfTasks[TASK_SYSTEM].lastExecutedAt = 100000;
scheduler();
EXPECT_EQ(true, unittest_outsideRealtimeGuardInterval);
EXPECT_EQ(301, unittest_scheduler_timeToNextRealtimeTask);
// System should be scheduled as not in guard period
EXPECT_EQ(&cfTasks[TASK_SYSTEM], unittest_scheduler_selectedTask);
EXPECT_EQ(200699, cfTasks[TASK_SYSTEM].lastExecutedAt);
EXPECT_EQ(200000, cfTasks[TASK_GYROPID].lastExecutedAt);
}
// STUBS
extern "C" {
}