Subversion Repositories Projects

Rev

Rev 346 | Rev 355 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
321 cascade 1
/****************************************************************************
2
 *   Copyright (C) 2009 by Claas Anders "CaScAdE" Rathje                    *
3
 *   admiralcascade@gmail.com                                               *
4
 *   Project-URL: http://www.mylifesucks.de/oss/c-osd/                      *
5
 *                                                                          *
6
 *   This program is free software; you can redistribute it and/or modify   *
7
 *   it under the terms of the GNU General Public License as published by   *
8
 *   the Free Software Foundation; either version 2 of the License.         *
9
 *                                                                          *
10
 *   This program is distributed in the hope that it will be useful,        *
11
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
12
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
13
 *   GNU General Public License for more details.                           *
14
 *                                                                          *
15
 *   You should have received a copy of the GNU General Public License      *
16
 *   along with this program; if not, write to the                          *
17
 *   Free Software Foundation, Inc.,                                        *
18
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.              *
19
 *                                                                          *
20
 *                                                                          *
21
 *   Credits to:                                                            *
22
 *   Holger Buss & Ingo Busker from mikrokopter.de for the MK project       *
23
 *   Gregor "killagreg" Stobrawa for making the MK code readable            *
24
 *   Klaus "akku" Buettner for the hardware                                 *
25
 *   Manuel "KeyOz" Schrape for explaining the MK protocol to me            *
26
 ****************************************************************************/
27
 
28
#include <avr/io.h>
29
#include <avr/interrupt.h>
30
#include <util/delay.h>
346 cascade 31
#include <avr/pgmspace.h> 
32
#include "main.h"
33
#include "max7456_software_spi.h"
331 cascade 34
#include "usart1.h"
321 cascade 35
 
36
/* TODO:
37
 * - verifiy correctness of values
324 cascade 38
 * - clean up code :)
321 cascade 39
 */
40
 
329 cascade 41
#if !(ALLCHARSDEBUG|(WRITECHARS != -1))
346 cascade 42
// data structs not needed for character flashin
331 cascade 43
#include "mk-data-structs.h"
321 cascade 44
 
45
/* ##########################################################################
46
 * global definitions and global vars
47
 * ##########################################################################*/
346 cascade 48
 
321 cascade 49
volatile uint16_t setsReceived = 0;
50
 
51
volatile NaviData_t naviData;
52
volatile DebugOut_t debugData;
53
 
54
// cache old vars for blinking attribute, checkup is faster than full
55
// attribute write each time
56
volatile uint8_t last_UBat = 255;
57
volatile uint8_t last_RC_Quality = 255;
58
 
59
// 16bit should be enough, normal LiPos don't last that long
60
volatile uint16_t uptime = 0;
61
volatile uint16_t timer = 0;
62
 
331 cascade 63
// remember last time data was received
64
volatile uint8_t seconds_since_last_data = 0;
65
 
346 cascade 66
// store stats description in progmem to save space
67
char stats_item_0[] PROGMEM = "max Altitude:";
68
char stats_item_1[] PROGMEM = "max Speed   :";
69
char stats_item_2[] PROGMEM = "max Distance:";
70
char stats_item_3[] PROGMEM = "min voltage :";
71
char stats_item_4[] PROGMEM = "max time    :";
72
char* stats_item_pointers[] PROGMEM = {stats_item_0, stats_item_1, stats_item_2, stats_item_3, stats_item_4};
73
 
74
// store more fixed strings in progmen
75
char ON[]  PROGMEM = "ON ";
76
char OFF[] PROGMEM = "OFF";
77
 
329 cascade 78
#endif // ends !(ALLCHARSDEBUG|(WRITECHARS != -1))
326 cascade 79
 
80
// general PAL|NTSC distingiusch stuff
81
uint8_t top_line = 1;
82
uint8_t bottom_line = 14;
83
 
84
// Flags
85
uint8_t COSD_FLAGS = 0;
86
 
321 cascade 87
/* ##########################################################################
326 cascade 88
 * debounce buttons
89
 * ##########################################################################*/
90
int s1_pressed() {
91
        if (S1_PRESSED) {
92
                _delay_ms(25);
93
                if (S1_PRESSED) return 1;
94
        }
95
        return 0;
96
}
97
 
98
int s2_pressed() {
99
        if (S2_PRESSED) {
100
                _delay_ms(25);
101
                if (S2_PRESSED) return 1;
102
        }
103
        return 0;
104
}
105
 
321 cascade 106
 
329 cascade 107
#if !(ALLCHARSDEBUG|(WRITECHARS != -1))
321 cascade 108
 
109
/**
331 cascade 110
 * serial support
321 cascade 111
 */
346 cascade 112
//#include "usart1.c"
321 cascade 113
 
114
 
115
/* ##########################################################################
116
 * timer stuff
117
 * ##########################################################################*/
118
 
331 cascade 119
/**
120
 * timer kicks in every 1000uS ^= 1ms
321 cascade 121
 */
122
ISR(TIMER0_OVF_vect) {
123
    OCR0 = 6; // preload
124
    if (!timer--) {
125
        uptime++;
126
        timer = 999;
331 cascade 127
                seconds_since_last_data++;
321 cascade 128
    }
129
}
130
 
131
/* ##########################################################################
132
 * compass stuff
133
 * ##########################################################################*/
134
 
135
/**
136
 * convert the <heading> gotton from NC into an index
137
 */
138
uint8_t heading_conv(uint16_t heading) {
139
    if (heading > 23 && heading < 68) {
140
        //direction = "NE";
141
        return 0;
142
    } else if (heading > 67 && heading < 113) {
143
        //direction = "E ";
144
        return 1;
145
    } else if (heading > 112 && heading < 158) {
146
        //direction = "SE";
147
        return 2;
148
    } else if (heading > 157 && heading < 203) {
149
        //direction = "S ";
150
        return 3;
151
    } else if (heading > 202 && heading < 248) {
152
        //direction = "SW";
153
        return 4;
154
    } else if (heading > 247 && heading < 293) {
155
        //direction = "W ";
156
        return 5;
157
    } else if (heading > 292 && heading < 338) {
158
        //direction = "NW";
159
        return 6;
160
    }
161
    //direction = "N ";
162
    return 7;
163
}
164
 
165
/**
166
 * draw a compass rose at <x>/<y> for <heading>
167
 */
168
void draw_compass(uint8_t x, uint8_t y, uint16_t heading) {
169
    //char* rose = "---N---O---S---W---N---O---S---W---N---O---S---W";
170
    char rose[48] = {216, 215, 216, 211, 216, 215, 216, 213, 216, 215, 216, 212,
171
                    216, 215, 216, 214, 216, 215, 216, 211, 216, 215, 216, 213,
172
                    216, 215, 216, 212, 216, 215, 216, 214, 216, 215, 216, 211,
173
                    216, 215, 216, 213, 216, 215, 216, 212, 216, 215, 216, 214};
174
        // the center is char 19 (north), we add the current heading in 8th
175
        // which would be 22.5 degrees, but float would bloat up the code
176
        // and *10 / 225 would take ages... so we take the uncorrect way
177
    uint8_t front = 19 + (heading / 22);
178
    for (uint8_t i = 0; i < 9; i++) {
179
                write_char_xy(x++, y, rose[front - 4 + i]);
180
    }
181
}
182
 
183
/* ##########################################################################
184
 * artificial horizon
185
 * ##########################################################################*/
186
// remember last time displayed values
187
int8_t old_af_x = -1, old_af_y = -1;
188
 
189
/**
190
 * draw roll und nick indicators (could be enhanced to full artificial horizon)
191
 * from line <firstline> to <listlines> for given <nick> and <roll> values
192
 */
193
void draw_artificial_horizon(uint8_t firstline, uint8_t lastline, int16_t nick, int16_t roll) {
194
        char noodle[5] = {225, 225, 226, 227, 227};
195
        uint8_t center_x = 15;
196
        uint8_t center_y = lastline - firstline;
197
        center_y = 7;
198
        write_char_xy(center_x,center_y,228);
199
        uint8_t cpos, nicky, rollx;
200
 
201
        // which line
202
        int8_t ypos =  nick / 20;
203
        // which character from the array?
204
        if (nick < 0) {
205
                cpos = -1*((nick - (ypos * 20))/4);
206
                ypos--;
207
        } else cpos = 4-((nick - (ypos * 20))/4);
208
        if (cpos > 4) cpos = 4;
209
 
210
        nicky = center_y - ypos;
211
        if (nicky > lastline) nicky = lastline;
212
        else if (nicky < firstline) nicky = firstline;
213
 
214
        // ensure roll-borders
215
        rollx = (roll / 8)+15;
216
        if (rollx < 2) rollx = 2;
217
        else if (rollx > 28) rollx = 28;
218
 
219
 
220
        // clear roll
221
        if (old_af_x != rollx && old_af_x >= 0) {
222
                write_char_xy(old_af_x,13,0);
223
        }
224
 
225
        // clear nick
226
        if (old_af_y != nicky && old_af_y >= 0) {
227
                write_char_xy(center_x-1,old_af_y,0);
228
                write_char_xy(center_x+1,old_af_y,0);
229
        }
230
 
231
 
232
        // draw nick
233
        write_char_xy(center_x-1,nicky,noodle[cpos]);
234
        write_char_xy(center_x+1,nicky,noodle[cpos]);
235
 
236
        // draw roll
237
        write_char_xy(rollx,lastline,229);
238
 
239
        // update old vars
240
        old_af_x = rollx;
241
        old_af_y = nicky;
242
 
243
        // debug numbers
244
        //write_3digit_number_u(20,6,cpos);
245
        //write_number_s(20,7,ypos);    
246
        //write_number_s(0,7,nick);             
247
        //write_number_s(18,11,roll);   
248
}
249
 
326 cascade 250
/* ##########################################################################
251
 * A simple config menu for the flags
252
 * ##########################################################################*/
339 cascade 253
 
254
/**
255
 * helper function for menu updating
256
 */
257
void config_menu_drawings(uint8_t chosen) {
258
    // clear prevoius _cursor_
346 cascade 259
    write_ascii_string(3, (chosen + 6) % 7, " ");
339 cascade 260
    // draw current _cursor_
261
    write_ascii_string(3, chosen + 6, ">");
262
    if (COSD_FLAGS & COSD_FLAG_HUD) {
346 cascade 263
        write_ascii_string_pgm(23, 6, ON);
339 cascade 264
    } else {
346 cascade 265
                write_ascii_string_pgm(23, 6, OFF);
339 cascade 266
    }
267
    if (COSD_FLAGS & COSD_FLAG_ARTHORIZON) {
346 cascade 268
        write_ascii_string_pgm(23, 7, ON);
339 cascade 269
    } else {
346 cascade 270
        write_ascii_string_pgm(23, 7, OFF);
339 cascade 271
    }
272
        if (COSD_FLAGS & COSD_FLAG_STATS) {
346 cascade 273
        write_ascii_string_pgm(23, 8, ON);
339 cascade 274
    } else {
346 cascade 275
        write_ascii_string_pgm(23, 8, OFF);
339 cascade 276
    }
277
    if (COSD_FLAGS & COSD_FLAG_WARNINGS) {
346 cascade 278
        write_ascii_string_pgm(23, 9, ON);
339 cascade 279
    } else {
346 cascade 280
        write_ascii_string_pgm(23, 9, OFF);
339 cascade 281
    }
282
}
283
 
284
/**
346 cascade 285
 * some sort of clicking response in the menu
286
 */
287
void config_menu_doclick(uint8_t chosen, char** menu) {
288
        write_ascii_string(4, chosen + 6, "DONE              ");
289
        _delay_ms(1000);
290
        write_ascii_string(4, chosen + 6, menu[chosen]);
291
}
292
 
293
/**
339 cascade 294
 * a simple config menu tryout
295
 */
326 cascade 296
void config_menu(void) {
297
        // disable interrupts (makes the menu more smoothely)
298
        cli();
321 cascade 299
 
326 cascade 300
        // clear screen
301
        clear();
302
 
346 cascade 303
        char* menu[8] = {"Full HUD",
339 cascade 304
                                         "Art.Horizon in HUD",
305
                                         "Statistics",
306
                                         "Warnings",    // TODO: do it!
346 cascade 307
                                         "Reset uptime",
308
                                         "Request OSD-data",
309
                                         "Disable Debug-data",
339 cascade 310
                                         "EXIT"};
326 cascade 311
 
312
        uint8_t inmenu = 1;
313
        uint8_t chosen = 0;
339 cascade 314
        write_ascii_string(6,  2, "C-OSD Config Menu");
326 cascade 315
 
316
        // wait a bit before doing stuff so user has chance to release button
317
        _delay_ms(250);
318
 
339 cascade 319
        write_ascii_string(4,  6, menu[0]);
320
        write_ascii_string(4,  7, menu[1]);
321
        write_ascii_string(4,  8, menu[2]);
322
        write_ascii_string(4,  9, menu[3]);
323
        write_ascii_string(4, 10, menu[4]);
346 cascade 324
        write_ascii_string(4, 11, menu[5]);
325
        write_ascii_string(4, 12, menu[6]);
326
        write_ascii_string(4, 13, menu[7]);
339 cascade 327
 
328
        config_menu_drawings(chosen);
329
 
326 cascade 330
        while (inmenu) {
331
                        if (s2_pressed()) {
339 cascade 332
                                write_ascii_string(3,  chosen+6, " ");
346 cascade 333
                                chosen = (chosen + 1) % 8;
339 cascade 334
                                write_ascii_string(3,  chosen+6, ">");
326 cascade 335
                                _delay_ms(500);
336
                        } else if (s1_pressed()) {
337
                                switch (chosen) {
339 cascade 338
                                        case 0:         // full HUD
339
                                                COSD_FLAGS ^= COSD_FLAG_HUD;
340
                                                config_menu_drawings(chosen);
326 cascade 341
                                                break;
339 cascade 342
                                        case 1:         // art horizon
343
                                                COSD_FLAGS ^= COSD_FLAG_ARTHORIZON;
344
                                                config_menu_drawings(chosen);
326 cascade 345
                                                break;
339 cascade 346
                                        case 2:         // statistics
347
                                                COSD_FLAGS ^= COSD_FLAG_STATS;
348
                                                config_menu_drawings(chosen);
326 cascade 349
                                                break;
339 cascade 350
                                        case 3:         // warnings
351
                                                COSD_FLAGS ^= COSD_FLAG_WARNINGS;
352
                                                config_menu_drawings(chosen);
353
                                                break;
346 cascade 354
                                        case 4:         // reset uptime
355
                                                uptime = 0;
356
                                                config_menu_doclick(chosen, menu);
357
                                                break;
358
                                        case 5:         // re-request OSD data
359
                                                // request OSD Data from NC every 100ms
360
                                                usart1_request_mk_data(1, 'o', 100);
361
                                                config_menu_doclick(chosen, menu);
362
                                                break;
363
                                        case 6:         // disable debug data
364
                                                // disable sending of debug data
365
                                                // may result in smoother ddata display
366
                                                usart1_request_mk_data(0, 'd', 0);
367
                                                config_menu_doclick(chosen, menu);
368
                                                break;
369
                                        case 7:         // exit
339 cascade 370
                                                inmenu = 0;
371
                                                break;
326 cascade 372
                                }
339 cascade 373
                                _delay_ms(250);
326 cascade 374
                        }
375
        }
376
 
377
        // clear screen up again
378
        clear();
379
 
380
        // update flags to paint display again if needed
381
        COSD_FLAGS &= ~COSD_ICONS_WRITTEN;
382
 
383
        // enable interrupts again
384
        sei();
385
}
386
 
329 cascade 387
#endif // ends !(ALLCHARSDEBUG|(WRITECHARS != -1))
321 cascade 388
/* ##########################################################################
389
 * MAIN
390
 * ##########################################################################*/
391
int main(void) {
339 cascade 392
    // set up FLAGS, compiler should flatten this one
393
    COSD_FLAGS = (NTSC << 0);
394
    COSD_FLAGS |= (HUD << 1);
395
    COSD_FLAGS |= (ARTHORIZON << 2);
396
        COSD_FLAGS |= (STATS << 3);
397
    COSD_FLAGS |= (WARNINGS << 4);
324 cascade 398
 
339 cascade 399
    // set up Atmega162 Ports
321 cascade 400
    DDRA |= (1 << PA1); // PA1 output (/CS)
401
    MAX_CS_HIGH
402
    DDRA |= (1 << PA2); // PA2 output (SDIN)
403
    MAX_SDIN_LOW
404
    DDRA |= (1 << PA3); // PA3 output (SCLK)
405
    MAX_SCLK_LOW
406
    DDRA |= (1 << PA5); // PA5 output (RESET)
407
    MAX_RESET_HIGH
408
 
409
    DDRC |= (1 << PC0); // PC0 output (LED1 gn)
410
    LED1_OFF
411
    DDRC |= (1 << PC1); // PC1 output (LED2 rt)
412
    LED2_OFF
413
    DDRC |= (1 << PC2); // PC2 output (LED3 gn)
414
    LED3_OFF
415
    DDRC |= (1 << PC3); // PC3 output (LED4 rt)
416
    LED4_OFF
417
 
418
    DDRC &= ~(1 << PC4); // PC4 input  (MODE)
419
    PORTC |= (1 << PC4); // pullup
420
    DDRC &= ~(1 << PC5); // PC5 input  (SET)
421
    PORTC |= (1 << PC5); // pullup
422
 
339 cascade 423
    // set up top and bottom lines
424
    if (COSD_FLAGS & COSD_FLAG_NTSC) {
425
        bottom_line = 12;
426
    } else {
427
        bottom_line = 14;
428
    }
326 cascade 429
 
339 cascade 430
    // reset the MAX7456 to be sure any undefined states do no harm
321 cascade 431
    MAX_RESET_LOW
432
    MAX_RESET_HIGH
433
 
434
    // give the FC/NC and the maxim time to come up
435
    LED4_ON
436
    _delay_ms(2000);
437
 
438
    LED4_OFF
439
 
331 cascade 440
 
339 cascade 441
    //Pushing NEW chars to the MAX7456
329 cascade 442
#if (WRITECHARS != -1)
339 cascade 443
    // DISABLE display (VM0)
321 cascade 444
    spi_send_byte(0x00, 0b00000000);
339 cascade 445
#include "characters.c"
321 cascade 446
 
331 cascade 447
#endif 
321 cascade 448
 
339 cascade 449
    // Setup Video Mode
450
    if (COSD_FLAGS & COSD_FLAG_NTSC) {
451
        // NTSC + enable display immediately (VM0)
452
        spi_send_byte(0x00, 0b00001000);
453
    } else {
454
        // PAL + enable display immediately (VM0)
455
        spi_send_byte(0x00, 0b01001000);
456
    }
321 cascade 457
 
458
    // clear all display-mem (DMM)
459
    spi_send_byte(0x04, 0b00000100);
460
 
461
    // clearing takes 12uS according to maxim so lets wait longer
462
    _delay_us(120);
463
 
464
    // 8bit mode
465
    spi_send_byte(0x04, 0b01000000);
466
 
467
    // write blank chars to whole screen
468
    clear();
469
 
329 cascade 470
#if !(ALLCHARSDEBUG|(WRITECHARS != -1))
321 cascade 471
    // init usart
472
    usart1_init();
473
 
474
    // set up timer
475
    TCCR0 |= (1 << CS00) | (1 << CS01); // timer0 prescaler 64
476
    OCR0 = 6; // preload
477
    TIMSK |= (1 << TOIE0); // enable overflow timer0
478
 
326 cascade 479
    // enable interrupts
321 cascade 480
    sei();
481
#endif
482
 
483
    //write_ascii_string(2,  7, "         CaScAdE          ");
484
    //write_ascii_string(2,  8, "is TESTING his open source");
485
    //write_ascii_string(2,  9, "    EPi OSD Firmware");
486
 
487
    // custom char preview
488
    /*write_char_xy( 2, 7, 200);
489
    write_char_xy( 3, 7, 201);
490
    write_char_xy( 4, 7, 202);
491
    write_char_xy( 5, 7, 203);
492
    write_char_xy( 6, 7, 204);
493
    write_char_xy( 7, 7, 205);
494
    write_char_xy( 8, 7, 206);
495
    write_char_xy( 9, 7, 207);
496
    write_char_xy(10, 7, 208);
497
    write_char_xy(11, 7, 209);
498
    write_char_xy(12, 7, 210);
499
    write_char_xy(13, 7, 211);
500
    write_char_xy(14, 7, 212);
501
    write_char_xy(15, 7, 213);
502
    write_char_xy(16, 7, 214);
503
    write_char_xy(17, 7, 215);*/
504
 
505
    // we are ready
506
    LED3_ON
507
 
508
 
509
 
329 cascade 510
#if ALLCHARSDEBUG | (WRITECHARS != -1)
321 cascade 511
        clear();
512
    write_all_chars();
513
#else
339 cascade 514
        // clear serial screen
515
        //usart1_puts("\x1B[2J\x1B[H");
516
        //usart1_puts("hello world!\r\n");
321 cascade 517
 
339 cascade 518
 
346 cascade 519
        // request data ever 100ms from FC;
520
        //usart1_request_mk_data(0, 'd', 100);
339 cascade 521
 
522
        // request OSD Data from NC every 100ms
346 cascade 523
        usart1_request_mk_data(1, 'o', 100);
524
 
339 cascade 525
    // and disable debug...
346 cascade 526
        //usart1_request_mk_data(0, 'd', 0);
321 cascade 527
 
339 cascade 528
    // disable TXD-pin
529
    usart1_DisableTXD();
331 cascade 530
 
339 cascade 531
    // stats for after flight
532
    int16_t max_Altimeter = 0;
533
    uint16_t max_GroundSpeed = 0;
534
    int16_t max_Distance = 0;
535
    uint8_t min_UBat = 255;
536
    uint16_t max_FlyingTime = 0;
321 cascade 537
 
339 cascade 538
    // flags from last round to check for changes
539
    uint8_t old_MKFlags = 0;
540
 
321 cascade 541
    char* directions[8] = {"NE", "E ", "SE", "S ", "SW", "W ", "NW", "N "};
339 cascade 542
    char arrowdir[8] = {218, 217, 224, 223, 222, 221, 220, 219};
321 cascade 543
 
349 cascade 544
        /*write_ndigit_number_s_10th(5, 10, 1, 100, 0);
545
        write_ndigit_number_s_10th(5, 11, -1, 100, 0);
546
        write_ndigit_number_s_10th(5, 12, -11, 100, 0);*/
547
 
321 cascade 548
    while (1) {
339 cascade 549
        // write icons at init or after menu/mode-switch
550
        if (!(COSD_FLAGS & COSD_ICONS_WRITTEN) && (COSD_FLAGS & COSD_FLAG_HUD)) {
551
            write_char_xy(5, top_line, 203); // km/h
552
            write_char_xy(10, top_line, 202); // RC-transmitter
553
            write_char_xy(16, top_line, 208); // degree symbol
554
            write_char_xy(27, top_line, 204); // small meters m
555
            write_ascii_string(6, bottom_line, "V"); // voltage
556
            write_char_xy(14, bottom_line, 209); // on clock
557
            write_char_xy(22, bottom_line, 210); // fly clock
558
            write_char_xy(26, bottom_line, 200); // sat1
559
            write_char_xy(27, bottom_line, 201); // sat2
560
            COSD_FLAGS |= COSD_ICONS_WRITTEN;
561
        }
321 cascade 562
        if (rxd_buffer_locked) {
339 cascade 563
            if (COSD_FLAGS & COSD_FLAG_HUD) {
564
                if (rxd_buffer[2] == 'D') { // FC Data
565
                    /*Decode64();
566
                    debugData = *((DebugOut_t*) pRxData);
567
                    write_number_s(12, 2, RxDataLen);
568
                    write_number_s(20, 2, setsReceived++);
569
                    write_number_s(12, 3, debugData.Analog[0]); // AngleNick
570
                    write_number_s(12, 4, debugData.Analog[1]); // AngleRoll
336 cascade 571
                                        write_number_s(12, 5, debugData.Analog[5]); // Height
339 cascade 572
                    write_number_s(12, 6, debugData.Analog[9]); // Voltage
573
                    write_number_s(12, 7, debugData.Analog[10]);// RC Signal
336 cascade 574
                                        write_number_s(12, 8, debugData.Analog[11]);// Gyro compass*/
339 cascade 575
                } else if (rxd_buffer[2] == 'O') { // NC OSD Data
576
                    Decode64();
577
                    naviData = *((NaviData_t*) pRxData);
321 cascade 578
 
339 cascade 579
                    // first line
349 cascade 580
                    write_ndigit_number_u(2, top_line, (uint16_t) (((uint32_t) naviData.GroundSpeed * 36) / 1000), 100, 0);
321 cascade 581
 
349 cascade 582
                    write_ndigit_number_u(7, top_line, naviData.RC_Quality, 100, 0);
339 cascade 583
                    if (naviData.RC_Quality <= RCLVL_WRN && last_RC_Quality > RCLVL_WRN) {
584
                        for (uint8_t x = 0; x < 4; x++)
585
                            write_char_att_xy(7 + x, top_line, BLINK);
586
                    } else if (naviData.RC_Quality > RCLVL_WRN && last_RC_Quality <= RCLVL_WRN) {
587
                        for (uint8_t x = 0; x < 4; x++)
588
                            write_char_att_xy(7 + x, top_line, 0);
589
                    }
590
                    last_RC_Quality = naviData.RC_Quality;
321 cascade 591
 
349 cascade 592
                    write_ndigit_number_u(13, top_line, naviData.CompassHeading, 100, 0);
321 cascade 593
 
339 cascade 594
                    write_ascii_string(17, top_line, directions[heading_conv(naviData.CompassHeading)]);
321 cascade 595
 
339 cascade 596
                    if (naviData.Variometer == 0) {
597
                        write_char_xy(20, top_line, 206); // plain line
598
                    } else if (naviData.Variometer > 0 && naviData.Variometer <= 10) {
599
                        write_char_xy(20, top_line, 234); // small arrow up
600
                    } else if (naviData.Variometer > 10) {
601
                        write_char_xy(20, top_line, 235); // big arrow up
602
                    } else if (naviData.Variometer < 0 && naviData.Variometer >= -10) {
603
                        write_char_xy(20, top_line, 232); // small arrow down
604
                    } else {
605
                        write_char_xy(20, top_line, 233); //big arrow down
606
                    }
321 cascade 607
 
339 cascade 608
                    //note:lephisto:according to several sources it's /30
349 cascade 609
                    if (naviData.Altimeter > 300 || naviData.Altimeter < -300) {
339 cascade 610
                        // above 10m only write full meters
349 cascade 611
                        write_ndigit_number_s(23, top_line, naviData.Altimeter / 30, 1000, 0);
339 cascade 612
                    } else {
613
                        // up to 10m write meters.dm
349 cascade 614
                        //write_number_u_10th(21, top_line, naviData.Altimeter / 3);
615
                                                write_ndigit_number_s_10th(23, top_line, naviData.Altimeter / 3, 100, 0);
339 cascade 616
                    }
321 cascade 617
 
339 cascade 618
                    // seccond line
619
                    draw_compass(11, top_line + 1, naviData.CompassHeading);
321 cascade 620
 
339 cascade 621
                    // TODO: verify correctness
622
                    uint16_t heading_home = (naviData.HomePositionDeviation.Bearing + 360 - naviData.CompassHeading) % 360;
623
                    write_char_xy(27, top_line + 1, arrowdir[heading_conv(heading_home)]);
321 cascade 624
 
349 cascade 625
                    write_ndigit_number_u(24, top_line + 1, naviData.HomePositionDeviation.Distance / 100, 100, 0);
321 cascade 626
 
339 cascade 627
                    // center
628
                    if (naviData.MKFlags & FLAG_MOTOR_RUN) { // should be engines running
629
                        if (!(old_MKFlags & FLAG_MOTOR_RUN)) { // motors just started, clear middle
630
                            clear();
631
                            // update flags to paint display again if needed
632
                            COSD_FLAGS &= ~COSD_ICONS_WRITTEN;
633
                        }
634
                        if (COSD_FLAGS & COSD_FLAG_ARTHORIZON) {
635
                            draw_artificial_horizon(top_line + 2, bottom_line - 1, naviData.AngleNick, naviData.AngleRoll);
636
                        }
637
                    } else {
336 cascade 638
                                                // stats
339 cascade 639
                                                if (COSD_FLAGS & COSD_FLAG_STATS) {
346 cascade 640
                                                        write_ascii_string_pgm(2, 5, stats_item_pointers[0]); // max Altitude
349 cascade 641
                                write_ndigit_number_s(18, 5, max_Altimeter / 30, 1000, 0);
339 cascade 642
                                write_char_xy(22, 5, 204); // small meters m
346 cascade 643
                                                        write_ascii_string_pgm(2, 6, stats_item_pointers[1]); // max Speed
349 cascade 644
                                write_ndigit_number_u(19, 6, (uint16_t) (((uint32_t) max_GroundSpeed * 36) / 1000), 100, 0);
339 cascade 645
                                write_char_xy(22, 6, 203); // km/h
346 cascade 646
                                                        write_ascii_string_pgm(2, 7, stats_item_pointers[2]); // max Distance
349 cascade 647
                                write_ndigit_number_u(19, 7, max_Distance / 100, 100, 0);
339 cascade 648
                                write_char_xy(22, 7, 204); // small meters m
346 cascade 649
                                                        write_ascii_string_pgm(2, 8, stats_item_pointers[3]); // min voltage
349 cascade 650
                                                        write_ndigit_number_u_10th(18, 8, min_UBat, 100, 0);
339 cascade 651
                                write_ascii_string(22, 8, "V"); // voltage
346 cascade 652
                                                        write_ascii_string_pgm(2, 9, stats_item_pointers[4]); // max time
339 cascade 653
                                write_time(16, 9, max_FlyingTime);
654
                                write_char_xy(22, 9, 210); // fly clock
655
                                                } else if (COSD_FLAGS & COSD_FLAG_ARTHORIZON) { // if no stats there is space horizon
656
                            draw_artificial_horizon(top_line + 2, bottom_line - 1, naviData.AngleNick, naviData.AngleRoll);
657
                        }
658
                    }
321 cascade 659
 
339 cascade 660
                    // bottom line
349 cascade 661
                                        write_ndigit_number_u_10th(2, bottom_line, naviData.UBat, 100, 0);
339 cascade 662
                    if (naviData.UBat <= UBAT_WRN && last_UBat > UBAT_WRN) {
663
                        for (uint8_t x = 0; x < 7; x++)
664
                            write_char_att_xy(x, bottom_line, BLINK);
665
                    } else {
666
                        for (uint8_t x = 0; x < 7; x++)
667
                            write_char_att_xy(x, bottom_line, 0);
668
                    }
321 cascade 669
 
339 cascade 670
                    write_time(8, bottom_line, uptime);
671
                    write_time(16, bottom_line, naviData.FlyingTime);
321 cascade 672
 
349 cascade 673
                    write_ndigit_number_u(24, bottom_line, naviData.SatsInUse, 10, 0);
321 cascade 674
 
339 cascade 675
                    if (naviData.NCFlags & NC_FLAG_CH) {
676
                        write_char_xy(27, bottom_line, 231); // gps ch
677
                    } else if (naviData.NCFlags & NC_FLAG_PH) {
678
                        write_char_xy(27, bottom_line, 230); // gps ph
679
                    } else { // (naviData.NCFlags & NC_FLAG_FREE)
680
                        write_char_xy(27, bottom_line, 201); // sat2 (free)
681
                    }
321 cascade 682
 
339 cascade 683
                    //write_number_s(8, 5, RxDataLen);
684
                    //write_number_s(16, 5, setsReceived++);
321 cascade 685
 
339 cascade 686
                    // remember statistics
687
                    if (naviData.Altimeter > max_Altimeter) max_Altimeter = naviData.Altimeter;
688
                    if (naviData.GroundSpeed > max_GroundSpeed) max_GroundSpeed = naviData.GroundSpeed;
689
                    if (naviData.HomePositionDeviation.Distance > max_Distance) {
690
                        max_Distance = naviData.HomePositionDeviation.Distance;
691
                    }
692
                    if (naviData.UBat < min_UBat) min_UBat = naviData.UBat;
693
                    if (naviData.FlyingTime > max_FlyingTime) max_FlyingTime = naviData.FlyingTime;
694
 
695
                    old_MKFlags = naviData.MKFlags;
696
                }
697
            }
698
            seconds_since_last_data = 0;
321 cascade 699
            rxd_buffer_locked = 0;
700
        }
701
        // handle keypress
326 cascade 702
        if (s1_pressed()) {
339 cascade 703
            config_menu();
321 cascade 704
        }
339 cascade 705
        if (seconds_since_last_data > 2) {
346 cascade 706
                        // request OSD Data from NC every 100ms
707
                        usart1_request_mk_data(1, 'o', 100);
708
                        seconds_since_last_data = 0;
339 cascade 709
        }
321 cascade 710
    }
711
#endif
712
    return 0;
713
}