Subversion Repositories Projects

Rev

Rev 321 | Rev 325 | 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>
31
 
32
/* TODO:
33
 * - verifiy correctness of values
324 cascade 34
 * - clean up code :)
321 cascade 35
 */
36
 
37
/* ##########################################################################
38
 * Debugging and general purpose definitions
39
 * ##########################################################################*/
40
#define ALLCHARSDEBUG 0         // set to 1 and flash firmware to see all chars
324 cascade 41
#define WRITECHARS 224          // set to 2XX and flash firmware to write new char
321 cascade 42
                                                        // enables the allchars as well to see results
43
#define NTSC 0                          // set to 1 for NTSC mode + lifts the bottom line
44
#define ARTHORIZON 0            // set to 1 to enable roll&nick artificial horizon
324 cascade 45
#define NOOSD 0                         // set to 1 to disable OSD completely
46
#define NOOSD_BUT_WRN 0         // set to 1 to disable OSD completely but show 
47
                                                        // battery and receive signal warnings
321 cascade 48
#define UBAT_WRN 94                     // voltage for blinking warning, like FC settings
49
#define RCLVL_WRN 100           // make the RC level blink if below this number
50
 
51
// ### read datasheet before changing stuff below this line :)
52
#define BLINK   0b01001111      // attribute byte for blinking chars
53
 
54
/* ##########################################################################
324 cascade 55
 * FLAGS usable during runtime
56
 * ##########################################################################*/
57
#define COSD_FLAG_NTSC                  1
58
#define COSD_FLAG_ARTHORIZON    2
59
#define COSD_FLAG_NOOSD                 4
60
#define COSD_FLAG_NOOSD_BUT_WRN 8
61
 
62
/* ##########################################################################
321 cascade 63
 * Software SPI to communicate with MAX7456
64
 * ##########################################################################*/
65
#define MAX_CS_HIGH             PORTA |=  (1 << PA1);
66
#define MAX_CS_LOW              PORTA &= ~(1 << PA1);
67
#define MAX_SDIN_HIGH           PORTA |=  (1 << PA2);
68
#define MAX_SDIN_LOW            PORTA &= ~(1 << PA2);
69
#define MAX_SCLK_HIGH           PORTA |=  (1 << PA3);
70
#define MAX_SCLK_LOW            PORTA &= ~(1 << PA3);
71
#define MAX_RESET_HIGH          PORTA |=  (1 << PA5);
72
#define MAX_RESET_LOW           PORTA &= ~(1 << PA5);
73
 
74
/* ##########################################################################
75
 * LED controll
76
 * ##########################################################################*/
77
#define LED1_ON                 PORTC |=  (1 << PC0);
78
#define LED1_OFF                PORTC &= ~(1 << PC0);
79
#define LED2_ON                 PORTC |=  (1 << PC1);
80
#define LED2_OFF                PORTC &= ~(1 << PC1);
81
#define LED3_ON                 PORTC |=  (1 << PC2);
82
#define LED3_OFF                PORTC &= ~(1 << PC2);
83
#define LED4_ON                 PORTC |=  (1 << PC3);
84
#define LED4_OFF                PORTC &= ~(1 << PC3);
85
 
86
/* ##########################################################################
87
 * switch controll
88
 * ##########################################################################*/
324 cascade 89
#define S1_PRESSED              !(PINC & (1<<PC5))
90
#define S2_PRESSED              !(PINC & (1<<PC4))
321 cascade 91
 
92
/* ##########################################################################
93
 * gain some fake arm compat :)
94
 * ##########################################################################*/
95
#define u8 uint8_t
96
#define s8 int8_t
97
#define u16 uint16_t
98
#define s16 int16_t
99
#define u32 uint32_t
100
#define s32 int32_t
101
 
102
#if !(ALLCHARSDEBUG|WRITECHARS)
103
/* ##########################################################################
104
 * MK data strucs & flags
105
 * ##########################################################################*/
106
#define NC_FLAG_FREE                    1
107
#define NC_FLAG_PH                      2
108
#define NC_FLAG_CH                      4
109
#define NC_FLAG_RANGE_LIMIT             8
110
#define NC_SERIAL_LINK_OK               16
111
#define NC_FLAG_TARGET_REACHED          32
112
 
113
#define FLAG_MOTOR_RUN  1
114
#define FLAG_FLY        2
115
#define FLAG_CALIBRATE  4
116
#define FLAG_START      8
117
 
118
/*
119
 * FC Debug Struct
120
 * portions taken and adapted from
121
 * http://svn.mikrokopter.de/mikrowebsvn/filedetails.php?repname=FlightCtrl&path=%2Ftags%2FV0.72p%2Fuart.h
122
 */
123
typedef struct {
124
    uint8_t Digital[2];
125
    uint16_t Analog[32]; // Debugvalues
126
} __attribute__((packed)) DebugOut_t;
127
 
128
/*
129
 * NaviCtrl OSD Structs
130
 * portions taken and adapted from
131
 * http://svn.mikrokopter.de/mikrowebsvn/filedetails.php?repname=NaviCtrl&path=%2Ftags%2FV0.14e%2Fuart1.h
132
 */
133
typedef struct {
134
    s32 Longitude; // in 1E-7 deg
135
    s32 Latitude; // in 1E-7 deg
136
    s32 Altitude; // in mm
137
    u8 Status; // validity of data
138
} __attribute__((packed)) GPS_Pos_t;
139
 
140
typedef struct {
141
    s16 Distance; // distance to target in cm
142
    s16 Bearing; // course to target in deg
143
} __attribute__((packed)) GPS_PosDev_t;
144
 
145
typedef struct {
146
    GPS_Pos_t CurrentPosition; // see ubx.h for details
147
    GPS_Pos_t TargetPosition;
148
    GPS_PosDev_t TargetPositionDeviation;
149
    GPS_Pos_t HomePosition;
150
    GPS_PosDev_t HomePositionDeviation;
151
    u8 WaypointIndex; // index of current waypoints running from 0 to WaypointNumber-1
152
    u8 WaypointNumber; // number of stored waypoints
153
    u8 SatsInUse; // no of satellites used for position solution
154
    s16 Altimeter; // hight according to air pressure
155
    s16 Variometer; // climb(+) and sink(-) rate
156
    u16 FlyingTime; // in seconds
157
    u8 UBat; // Battery Voltage in 0.1 Volts
158
    u16 GroundSpeed; // speed over ground in cm/s (2D)
159
    s16 Heading; // current flight direction in deg as angle to north
160
    s16 CompassHeading; // current compass value
161
    s8 AngleNick; // current Nick angle in 1
162
    s8 AngleRoll; // current Rick angle in 1
163
    u8 RC_Quality; // RC_Quality
164
    u8 MKFlags; // Flags from FC
165
    u8 NCFlags; // Flags from NC
166
    u8 Errorcode; // 0 --> okay
167
    u8 OperatingRadius; // current operation radius around the Home Position in m
168
    u8 Reserve[7]; // for future use
169
} __attribute__((packed)) NaviData_t;
170
 
171
 
172
/* ##########################################################################
173
 * global definitions and global vars
174
 * ##########################################################################*/
175
#define baud 57600
176
 
177
#define RXD_BUFFER_LEN          150
178
#define TXD_BUFFER_LEN          150
179
 
180
volatile uint8_t rxd_buffer_locked = 0;
181
volatile uint8_t rxd_buffer[RXD_BUFFER_LEN];
182
volatile uint8_t txd_buffer[TXD_BUFFER_LEN];
183
volatile uint8_t ReceivedBytes = 0;
184
volatile uint8_t *pRxData = 0;
185
volatile uint8_t RxDataLen = 0;
186
volatile uint16_t setsReceived = 0;
187
 
188
volatile NaviData_t naviData;
189
volatile DebugOut_t debugData;
190
 
191
// cache old vars for blinking attribute, checkup is faster than full
192
// attribute write each time
193
volatile uint8_t last_UBat = 255;
194
volatile uint8_t last_RC_Quality = 255;
195
 
196
// 16bit should be enough, normal LiPos don't last that long
197
volatile uint16_t uptime = 0;
198
volatile uint16_t timer = 0;
199
 
200
#endif // ends !(ALLCHARSDEBUG|WRITECHARS)
201
/* ##########################################################################
202
 * MAX7456 SPI & Display stuff
203
 * ##########################################################################*/
204
 
205
/**
206
 * Send a byte through SPI
207
 */
208
void spi_send(uint8_t byte) {
209
    for (int8_t i = 7; i >= 0; i--) {
210
        if (((byte >> i) & 1)) {
211
            MAX_SDIN_HIGH
212
        } else {
213
            MAX_SDIN_LOW
214
        }
215
        MAX_SCLK_HIGH
216
        MAX_SCLK_LOW
217
    }
218
}
219
 
220
/**
221
 *  Send <byte> to <address> of MAX7456
222
 */
223
void spi_send_byte(uint8_t address, uint8_t byte) {
224
    // start sending
225
    MAX_CS_LOW
226
 
227
    spi_send(address);
228
    spi_send(byte);
229
 
230
    // end sending
231
    MAX_CS_HIGH
232
}
233
 
234
/**
235
 *  write a <character> to <address> of MAX7456 display memory
236
 */
237
void write_char(uint16_t address, char character) {
238
    spi_send_byte(0x05, (address & 0xFF00) >> 8); // DMAH
239
    spi_send_byte(0x06, (address & 0x00FF)); // DMAL
240
    spi_send_byte(0x07, character); // DMDI
241
}
242
 
243
/**
244
 *  write a character <attribute> to <address> of MAX7456 display memory
245
 */
246
void write_char_att(uint16_t address, char attribute) {
247
    // the only important part is that the DMAH[1] is set
248
    // so we add 2 which binary is the 2nd lowest byte
249
    spi_send_byte(0x05, ((address & 0xFF00) >> 8) | 2); // DMAH
250
    spi_send_byte(0x06, (address & 0x00FF)); // DMAL
251
    spi_send_byte(0x07, attribute); // DMDI
252
}
253
 
254
/**
255
 *  write a <character> at <x>/<y> to MAX7456 display memory
256
 */
257
void write_char_xy(uint8_t x, uint8_t y, char character) {
258
    uint16_t address = y * 30 + x;
259
    write_char(address, character);
260
}
261
 
262
/**
263
 *  write a  character <attribute> at <x>/<y> to MAX7456 display memory
264
 */
265
void write_char_att_xy(uint8_t x, uint8_t y, char attribute) {
266
    uint16_t address = y * 30 + x;
267
    write_char_att(address, attribute);
268
}
269
 
270
/**
271
 *  clear display by writing blank characters all over it
272
 */
273
void clear(void) {
274
    uint16_t memory_address = 0;
275
    for (unsigned int a = 0; a < 480; a++) {
276
        write_char(memory_address++, 0);
277
    }
278
}
279
 
280
/**
281
 *  write an ascii <character> to <address> of MAX7456 display memory
282
 */
283
void write_ascii_char(uint16_t address, char c) {
284
    if (c == 32) c = 0; // remap space
285
    else if (c > 48 && c <= 57) c -= 48; // remap numbers
286
    else if (c == '0') c = 10; // remap zero
287
    else if (c >= 65 && c <= 90) c -= 54; // remap big letters
288
    else if (c >= 97 && c <= 122) c -= 60; // remap small letters
289
    else if (c == '(') c = 63; // remap
290
    else if (c == ')') c = 64; // remap
291
    else if (c == '.') c = 65; // remap
292
    else if (c == '?') c = 66; // remap
293
    else if (c == ';') c = 67; // remap
294
    else if (c == ':') c = 68; // remap
295
    else if (c == ',') c = 69; // remap
296
    else if (c == '\'') c = 70; // remap
297
    else if (c == '/') c = 71; // remap
298
    else if (c == '"') c = 72; // remap
299
    else if (c == '-') c = 73; // remap minus
300
    else if (c == '<') c = 74; // remap
301
    else if (c == '>') c = 75; // remap
302
    else if (c == '@') c = 76; // remap
303
    write_char(address, c);
304
}
305
 
306
/**
307
 *  write an ascii <string> at <x>/<y> to MAX7456 display memory
308
 */
309
void write_ascii_string(uint8_t x, uint8_t y, char *string) {
310
    while (*string) {
311
        write_ascii_char(((x++)+(y * 30)), *string);
312
        string++;
313
    }
314
}
315
 
316
/**
317
 *  Write only the last three digits of a <number> at <x>/<y> to MAX7456
318
 *  display memory. takes full 16bit numbers as well for stuff
319
 *  like compass only taking three characters (values <= 999)
320
 */
321
void write_3digit_number_u(uint8_t x, uint8_t y, uint16_t number) {
322
    uint16_t num = 100;
323
    uint8_t started = 0;
324
 
325
    while (num > 0) {
326
        uint8_t b = number / num;
327
        if (b > 0 || started || num == 1) {
328
            write_ascii_char((x++)+(y * 30), '0' + b);
329
            started = 1;
330
        } else {
331
            write_ascii_char((x++)+(y * 30), 0);
332
        }
333
        number -= b * num;
334
 
335
        num /= 10;
336
    }
337
}
338
 
339
/**
340
 *  Write only the last two digits of a number at <x>/<y> to MAX7456
341
 *  display memory. takes full 16bit numbers as well for stuff
342
 *  like seconds only taking two characters (values <= 99)
343
 *  Since this is used for seconds only and it looks better, there
344
 *  is a trading 0 attached
345
 */
346
void write_2digit_number_u(uint8_t x, uint8_t y, uint16_t number) {
347
    uint16_t num = 10;
348
    uint8_t started = 0;
349
 
350
    while (num > 0) {
351
        uint8_t b = number / num;
352
        if (b > 0 || started || num == 1) {
353
            write_ascii_char((x++)+(y * 30), '0' + b);
354
            started = 1;
355
        } else {
356
            write_ascii_char((x++)+(y * 30), '0');
357
        }
358
        number -= b * num;
359
 
360
        num /= 10;
361
    }
362
}
363
 
364
/**
365
 *  write a unsigned number as /10th at <x>/<y> to MAX7456 display memory
366
 */
367
void write_number_u_10th(uint8_t x, uint8_t y, uint16_t number) {
368
    uint16_t num = 10000;
369
    uint8_t started = 0;
370
 
371
    while (num > 0) {
372
        uint8_t b = number / num;
373
 
374
        if (b > 0 || started || num == 1) {
375
            if ((num / 10) == 0) write_char((x++)+(y * 30), 65);
376
            write_ascii_char((x++)+(y * 30), '0' + b);
377
            started = 1;
378
        } else {
379
            write_ascii_char((x++)+(y * 30), 0);
380
        }
381
        number -= b * num;
382
 
383
        num /= 10;
384
    }
385
}
386
 
387
/**
388
 *  write a unsigned number at <x>/<y> to MAX7456 display memory
389
 */
390
void write_number_u(uint8_t x, uint8_t y, uint16_t number) {
391
    uint16_t num = 10000;
392
    uint8_t started = 0;
393
 
394
    while (num > 0) {
395
        uint8_t b = number / num;
396
        if (b > 0 || started || num == 1) {
397
            write_ascii_char((x++)+(y * 30), '0' + b);
398
            started = 1;
399
        } else {
400
            write_ascii_char((x++)+(y * 30), 0);
401
        }
402
        number -= b * num;
403
 
404
        num /= 10;
405
    }
406
}
407
 
408
/**
409
 *  write a signed number at <x>/<y> to MAX7456 display memory
410
 */
411
void write_number_s(uint8_t x, uint8_t y, int16_t w) {
412
    if (((uint16_t) w) > 32767) {
413
        w = w - 65536;
414
 
415
        int16_t num = -10000;
416
        uint8_t started = 0;
417
 
418
        while (num < 0) {
419
            uint8_t b = w / num;
420
            if (b > 0 || started || num == 1) {
421
                if (!started) write_char((x - 1)+(y * 30), 0x49);
422
                write_ascii_char((x++)+(y * 30), '0' + b);
423
                started = 1;
424
            } else {
425
                write_ascii_char((x++)+(y * 30), 0);
426
            }
427
            w -= b * num;
428
 
429
            num /= 10;
430
        }
431
    } else {
432
        write_char((x)+(y * 30), 0);
433
        write_number_u(x, y, w);
434
    }
435
}
436
 
437
/**
438
 *  write <seconds> as human readable time at <x>/<y> to MAX7456 display mem
439
 */
440
void write_time(uint8_t x, uint8_t y, uint16_t seconds) {
441
    uint16_t min = seconds / 60;
442
    seconds -= min * 60;
443
    write_3digit_number_u(x, y, min);
444
    write_char_xy(x + 3, y, 68);
445
    write_2digit_number_u(x + 4, y, seconds);
446
}
447
 
448
/**
449
 * for testing write all chars to screen
450
 */
451
void write_all_chars() {
452
    uint16_t x = 3, y = 2, t = 0;
453
    while (t < 256) {
454
        write_char_xy(x++, y, t++);
455
        if (x > 25) {
456
            y++;
457
            x = 3;
458
        }
459
    }
460
}
461
 
462
#if !(ALLCHARSDEBUG|WRITECHARS)
463
/* ##########################################################################
464
 * USART stuff
465
 * ##########################################################################*/
466
 
467
/**
468
 * init usart1
469
 */
470
void usart1_init() {
471
    UBRR1H = ((F_CPU / (16UL * baud)) - 1) >> 8;
472
    UBRR1L = (F_CPU / (16UL * baud)) - 1;
473
 
474
    // Enable receiver and transmitter; enable RX interrupt
475
    UCSR1B = (1 << RXEN1) | (1 << TXEN1) | (1 << RXCIE1);
476
 
477
    //asynchronous 8N1
478
    UCSR1C = (1 << URSEL1) | (3 << UCSZ10);
479
}
480
 
481
/**
482
 * send a single <character> through usart1
483
 */
484
void usart1_putc(unsigned char character) {
485
    // wait until UDR ready
486
    while (!(UCSR1A & (1 << UDRE1)));
487
    UDR1 = character;
488
}
489
 
490
/**
491
 * send a <string> throught usart1
492
 */
493
void usart1_puts(char *s) {
494
    while (*s) {
495
        usart1_putc(*s);
496
        s++;
497
    }
498
}
499
 
500
/**
501
 * receive data through usart1
502
 * portions taken and adapted from
503
 * http://svn.mikrokopter.de/mikrowebsvn/filedetails.php?repname=FlightCtrl&path=%2Fbranches%2FV0.72p+Code+Redesign+killagreg%2Fuart0.c
504
 */
505
ISR(SIG_USART1_RECV) {
506
    if (rxd_buffer_locked) return; // if rxd buffer is locked immediately return
507
    LED1_ON
508
            static uint16_t crc;
509
    static uint8_t ptr_rxd_buffer = 0;
510
    uint8_t crc1, crc2;
511
    uint8_t c;
512
 
513
    c = UDR1; // catch the received byte
514
 
515
    // the rxd buffer is unlocked
516
    if ((ptr_rxd_buffer == 0) && (c == '#')) // if rxd buffer is empty and syncronisation character is received
517
    {
518
                /*
519
                // skip other datasets
520
        if (ptr_rxd_buffer == 2 && rxd_buffer[ptr_rxd_buffer] != 'O') {
521
                        ptr_rxd_buffer = 0; // reset rxd buffer
522
                rxd_buffer_locked = 0; // unlock rxd buffer
523
                }*/
524
                rxd_buffer[ptr_rxd_buffer++] = c; // copy 1st byte to buffer
525
        crc = c; // init crc
526
    } else if (ptr_rxd_buffer < RXD_BUFFER_LEN) // collect incomming bytes
527
    {
528
        if (c != '\r') // no termination character
529
        {
530
            rxd_buffer[ptr_rxd_buffer++] = c; // copy byte to rxd buffer
531
            crc += c; // update crc
532
        } else // termination character was received
533
        {
534
            // the last 2 bytes are no subject for checksum calculation
535
            // they are the checksum itself
536
            crc -= rxd_buffer[ptr_rxd_buffer - 2];
537
            crc -= rxd_buffer[ptr_rxd_buffer - 1];
538
            // calculate checksum from transmitted data
539
            crc %= 4096;
540
            crc1 = '=' + crc / 64;
541
            crc2 = '=' + crc % 64;
542
            // compare checksum to transmitted checksum bytes
543
            if ((crc1 == rxd_buffer[ptr_rxd_buffer - 2]) && (crc2 == rxd_buffer[ptr_rxd_buffer - 1])) { // checksum valid
544
                rxd_buffer[ptr_rxd_buffer] = '\r'; // set termination character
545
                ReceivedBytes = ptr_rxd_buffer + 1; // store number of received bytes
546
                rxd_buffer_locked = 1; // lock the rxd buffer
547
            } else { // checksum invalid
548
                rxd_buffer_locked = 0; // unlock rxd buffer
549
            }
550
            ptr_rxd_buffer = 0; // reset rxd buffer pointer
551
        }
552
    } else // rxd buffer overrun
553
    {
554
        ptr_rxd_buffer = 0; // reset rxd buffer
555
        rxd_buffer_locked = 0; // unlock rxd buffer
556
    }
557
    LED1_OFF
558
}
559
 
560
/**
561
 * Decode the recevied Buffer
562
 * portions taken and adapted from
563
 * http://svn.mikrokopter.de/mikrowebsvn/filedetails.php?repname=FlightCtrl&path=%2Ftags%2FV0.72p%2Fuart.c
564
 */
565
void Decode64(void) {
566
    uint8_t a, b, c, d;
567
    uint8_t x, y, z;
568
    uint8_t ptrIn = 3;
569
    uint8_t ptrOut = 3;
570
    uint8_t len = ReceivedBytes - 6;
571
 
572
    while (len) {
573
        a = rxd_buffer[ptrIn++] - '=';
574
        b = rxd_buffer[ptrIn++] - '=';
575
        c = rxd_buffer[ptrIn++] - '=';
576
        d = rxd_buffer[ptrIn++] - '=';
577
 
578
        x = (a << 2) | (b >> 4);
579
        y = ((b & 0x0f) << 4) | (c >> 2);
580
        z = ((c & 0x03) << 6) | d;
581
 
582
        if (len--) rxd_buffer[ptrOut++] = x;
583
        else break;
584
        if (len--) rxd_buffer[ptrOut++] = y;
585
        else break;
586
        if (len--) rxd_buffer[ptrOut++] = z;
587
        else break;
588
    }
589
    pRxData = &rxd_buffer[3];
590
    RxDataLen = ptrOut - 3;
591
}
592
 
593
/**
594
 * request Data through USART in special MK format by adding checksum and
595
 * encode data in modified Base64
596
 * portions taken and adapted from
597
 * http://svn.mikrokopter.de/mikrowebsvn/filedetails.php?repname=FlightCtrl&path=%2Ftags%2FV0.72p%2Fuart.c
598
 */
599
void sendMKData(unsigned char cmd, unsigned char addr, unsigned char *snd, unsigned char len) {
600
    unsigned int pt = 0;
601
    unsigned char a, b, c;
602
    unsigned char ptr = 0;
603
 
604
    txd_buffer[pt++] = '#'; // Start-Byte
605
    txd_buffer[pt++] = 'a' + addr; // Adress
606
    txd_buffer[pt++] = cmd; // Command
607
    while (len) {
608
        if (len) {
609
            a = snd[ptr++];
610
            len--;
611
        } else a = 0;
612
        if (len) {
613
            b = snd[ptr++];
614
            len--;
615
        } else b = 0;
616
        if (len) {
617
            c = snd[ptr++];
618
            len--;
619
        } else c = 0;
620
        txd_buffer[pt++] = '=' + (a >> 2);
621
        txd_buffer[pt++] = '=' + (((a & 0x03) << 4) | ((b & 0xf0) >> 4));
622
        txd_buffer[pt++] = '=' + (((b & 0x0f) << 2) | ((c & 0xc0) >> 6));
623
        txd_buffer[pt++] = '=' + (c & 0x3f);
624
    }
625
 
626
    // add crc
627
    unsigned int tmpCRC = 0, i;
628
    for (i = 0; i < pt; i++) {
629
        tmpCRC += txd_buffer[i];
630
    }
631
    tmpCRC %= 4096;
632
    txd_buffer[i++] = '=' + tmpCRC / 64;
633
    txd_buffer[i++] = '=' + tmpCRC % 64;
634
    txd_buffer[i++] = '\r';
635
 
636
    usart1_puts((char*) txd_buffer);
637
}
638
 
639
/* ##########################################################################
640
 * timer stuff
641
 * ##########################################################################*/
642
 
643
/*
644
 * timer kicks in every 1000uS
645
 */
646
ISR(TIMER0_OVF_vect) {
647
    OCR0 = 6; // preload
648
    if (!timer--) {
649
        uptime++;
650
        timer = 999;
651
    }
652
}
653
 
654
/* ##########################################################################
655
 * compass stuff
656
 * ##########################################################################*/
657
 
658
/**
659
 * convert the <heading> gotton from NC into an index
660
 */
661
uint8_t heading_conv(uint16_t heading) {
662
    if (heading > 23 && heading < 68) {
663
        //direction = "NE";
664
        return 0;
665
    } else if (heading > 67 && heading < 113) {
666
        //direction = "E ";
667
        return 1;
668
    } else if (heading > 112 && heading < 158) {
669
        //direction = "SE";
670
        return 2;
671
    } else if (heading > 157 && heading < 203) {
672
        //direction = "S ";
673
        return 3;
674
    } else if (heading > 202 && heading < 248) {
675
        //direction = "SW";
676
        return 4;
677
    } else if (heading > 247 && heading < 293) {
678
        //direction = "W ";
679
        return 5;
680
    } else if (heading > 292 && heading < 338) {
681
        //direction = "NW";
682
        return 6;
683
    }
684
    //direction = "N ";
685
    return 7;
686
}
687
 
688
/**
689
 * draw a compass rose at <x>/<y> for <heading>
690
 */
691
void draw_compass(uint8_t x, uint8_t y, uint16_t heading) {
692
    //char* rose = "---N---O---S---W---N---O---S---W---N---O---S---W";
693
    char rose[48] = {216, 215, 216, 211, 216, 215, 216, 213, 216, 215, 216, 212,
694
                    216, 215, 216, 214, 216, 215, 216, 211, 216, 215, 216, 213,
695
                    216, 215, 216, 212, 216, 215, 216, 214, 216, 215, 216, 211,
696
                    216, 215, 216, 213, 216, 215, 216, 212, 216, 215, 216, 214};
697
        // the center is char 19 (north), we add the current heading in 8th
698
        // which would be 22.5 degrees, but float would bloat up the code
699
        // and *10 / 225 would take ages... so we take the uncorrect way
700
    uint8_t front = 19 + (heading / 22);
701
    for (uint8_t i = 0; i < 9; i++) {
702
                write_char_xy(x++, y, rose[front - 4 + i]);
703
    }
704
}
705
 
706
/* ##########################################################################
707
 * artificial horizon
708
 * ##########################################################################*/
709
// remember last time displayed values
710
int8_t old_af_x = -1, old_af_y = -1;
711
 
712
/**
713
 * draw roll und nick indicators (could be enhanced to full artificial horizon)
714
 * from line <firstline> to <listlines> for given <nick> and <roll> values
715
 */
716
void draw_artificial_horizon(uint8_t firstline, uint8_t lastline, int16_t nick, int16_t roll) {
717
        char noodle[5] = {225, 225, 226, 227, 227};
718
        uint8_t center_x = 15;
719
        uint8_t center_y = lastline - firstline;
720
        center_y = 7;
721
        write_char_xy(center_x,center_y,228);
722
        uint8_t cpos, nicky, rollx;
723
 
724
        // which line
725
        int8_t ypos =  nick / 20;
726
        // which character from the array?
727
        if (nick < 0) {
728
                cpos = -1*((nick - (ypos * 20))/4);
729
                ypos--;
730
        } else cpos = 4-((nick - (ypos * 20))/4);
731
        if (cpos > 4) cpos = 4;
732
 
733
        nicky = center_y - ypos;
734
        if (nicky > lastline) nicky = lastline;
735
        else if (nicky < firstline) nicky = firstline;
736
 
737
        // ensure roll-borders
738
        rollx = (roll / 8)+15;
739
        if (rollx < 2) rollx = 2;
740
        else if (rollx > 28) rollx = 28;
741
 
742
 
743
        // clear roll
744
        if (old_af_x != rollx && old_af_x >= 0) {
745
                write_char_xy(old_af_x,13,0);
746
        }
747
 
748
        // clear nick
749
        if (old_af_y != nicky && old_af_y >= 0) {
750
                write_char_xy(center_x-1,old_af_y,0);
751
                write_char_xy(center_x+1,old_af_y,0);
752
        }
753
 
754
 
755
        // draw nick
756
        write_char_xy(center_x-1,nicky,noodle[cpos]);
757
        write_char_xy(center_x+1,nicky,noodle[cpos]);
758
 
759
        // draw roll
760
        write_char_xy(rollx,lastline,229);
761
 
762
        // update old vars
763
        old_af_x = rollx;
764
        old_af_y = nicky;
765
 
766
        // debug numbers
767
        //write_3digit_number_u(20,6,cpos);
768
        //write_number_s(20,7,ypos);    
769
        //write_number_s(0,7,nick);             
770
        //write_number_s(18,11,roll);   
771
}
772
 
773
#endif
774
 
775
/* ##########################################################################
776
 * MAIN
777
 * ##########################################################################*/
778
int main(void) {
324 cascade 779
        // set up FLAGS, compiler should flatten this one
780
        uint8_t COSD_FLAGS = (NTSC << (COSD_FLAG_NTSC - 1));
781
        COSD_FLAGS |= (ARTHORIZON << (COSD_FLAG_ARTHORIZON - 1));
782
        COSD_FLAGS |= (NOOSD << (COSD_FLAG_NOOSD - 1));
783
        COSD_FLAGS |= (NOOSD_BUT_WRN << (COSD_FLAG_NOOSD_BUT_WRN - 1));
784
 
785
        // set up Atmega162 Ports
321 cascade 786
    DDRA |= (1 << PA1); // PA1 output (/CS)
787
    MAX_CS_HIGH
788
    DDRA |= (1 << PA2); // PA2 output (SDIN)
789
    MAX_SDIN_LOW
790
    DDRA |= (1 << PA3); // PA3 output (SCLK)
791
    MAX_SCLK_LOW
792
    DDRA |= (1 << PA5); // PA5 output (RESET)
793
    MAX_RESET_HIGH
794
 
795
    DDRC |= (1 << PC0); // PC0 output (LED1 gn)
796
    LED1_OFF
797
    DDRC |= (1 << PC1); // PC1 output (LED2 rt)
798
    LED2_OFF
799
    DDRC |= (1 << PC2); // PC2 output (LED3 gn)
800
    LED3_OFF
801
    DDRC |= (1 << PC3); // PC3 output (LED4 rt)
802
    LED4_OFF
803
 
804
    DDRC &= ~(1 << PC4); // PC4 input  (MODE)
805
    PORTC |= (1 << PC4); // pullup
806
    DDRC &= ~(1 << PC5); // PC5 input  (SET)
807
    PORTC |= (1 << PC5); // pullup
808
 
809
    MAX_RESET_LOW
810
    MAX_RESET_HIGH
811
 
324 cascade 812
        // check for keypress at startup
813
        if (S2_PRESSED) { // togle COSD_FLAG_ARTHORIZON
814
                        COSD_FLAGS ^= (1 << (COSD_FLAG_ARTHORIZON - 1));
815
                        _delay_ms(100);
816
    }
817
 
321 cascade 818
    // give the FC/NC and the maxim time to come up
819
    LED4_ON
820
    _delay_ms(2000);
821
 
822
    LED4_OFF
823
 
824
    /* ##########################################################################
825
     * Pushing NEW chars to the MAX7456
826
     * ##########################################################################*/
827
#if WRITECHARS
828
        void learn_char(uint8_t number, unsigned char* data) {
829
        // select character to write (CMAH)
830
        spi_send_byte(0x09, number);
831
 
832
        for (uint8_t i = 0; i < 54; i++) {
833
            // select 4pixel byte of char (CMAL)
834
            spi_send_byte(0x0A, i);
835
 
836
            // write 4pixel byte of char (CMDI)
837
            spi_send_byte(0x0B, data[i]);
838
        }
839
 
840
        // write to the NVM array from the shadow RAM (CMM)
841
        spi_send_byte(0x08, 0b10100000);
842
 
843
        // according to maxim writing to nvram takes about 12ms, lets wait longer
844
        _delay_ms(120);
845
    }
846
 
847
    // DISABLE display (VM0)
848
    spi_send_byte(0x00, 0b00000000);
849
 
850
    /**
851
     * easy char creation:
852
     * http://cascade.dyndns.org/~cascade/scripts/max7456/
853
     */
854
    // GPS
855
    unsigned char cc8[54] = {0x55, 0x50, 0x55, 0x55, 0x4a, 0x15, 0x55, 0x2a,
856
        0x85, 0x55, 0x2a, 0xa1, 0x55, 0x4a, 0xa8, 0x55,
857
        0x52, 0xa8, 0x55, 0x54, 0xaa, 0x55, 0x55, 0x09,
858
        0x55, 0x55, 0x52, 0x55, 0x55, 0x1a, 0x55, 0x51,
859
        0x96, 0x55, 0x18, 0x85, 0x54, 0x88, 0x28, 0x54,
860
        0x82, 0x05, 0x55, 0x20, 0xa1, 0x55, 0x48, 0x15,
861
        0x55, 0x52, 0x85, 0x55, 0x54, 0x15};
862
 
863
    unsigned char cc9[54] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
864
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x45,
865
        0x55, 0x55, 0x21, 0x55, 0x55, 0xa8, 0x55, 0x55,
866
        0xa1, 0x55, 0x55, 0x98, 0x15, 0x55, 0x2a, 0x85,
867
        0x55, 0x4a, 0xa1, 0x55, 0x4a, 0xa8, 0x55, 0x52,
868
        0xaa, 0x15, 0x54, 0xaa, 0x15, 0x55, 0x28, 0x55,
869
        0x55, 0x41, 0x55, 0x55, 0x55, 0x55};
870
 
871
    // RC
872
    unsigned char cca[54] = {0x54, 0xaa, 0x85, 0x52, 0x00, 0x21, 0x48, 0x2a,
873
        0x08, 0x60, 0x80, 0x82, 0x62, 0x08, 0x22, 0x62,
874
        0x2a, 0x22, 0x62, 0x08, 0x22, 0x60, 0x88, 0x82,
875
        0x48, 0x08, 0x08, 0x52, 0x08, 0x21, 0x54, 0x48,
876
        0x45, 0x55, 0x48, 0x55, 0x55, 0x48, 0x55, 0x55,
877
        0x48, 0x55, 0x55, 0x48, 0x55, 0x55, 0x48, 0x55,
878
        0x55, 0x2a, 0x15, 0x54, 0xaa, 0x85};
879
 
880
    // km/h
881
    unsigned char ccb[54] = {0x55, 0x55, 0x55, 0x01, 0x55, 0x55, 0x21, 0x55,
882
        0x55, 0x20, 0x15, 0x55, 0x22, 0x15, 0x55, 0x28,
883
        0x15, 0x55, 0x22, 0x15, 0x55, 0x00, 0x00, 0x15,
884
        0x52, 0xaa, 0x15, 0x52, 0x22, 0x15, 0x52, 0x22,
885
        0x15, 0x50, 0x00, 0x05, 0x55, 0x54, 0x85, 0x55,
886
        0x54, 0x80, 0x55, 0x54, 0xa8, 0x55, 0x54, 0x88,
887
        0x55, 0x54, 0x88, 0x55, 0x54, 0x00};
888
 
889
 
890
    // small meters m
891
    unsigned char ccc[54] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
892
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
893
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
894
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
895
        0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 0x15, 0x22,
896
        0x8a, 0x15, 0x28, 0xa2, 0x15, 0x20, 0x82, 0x15,
897
        0x20, 0x82, 0x15, 0x00, 0x00, 0x15};
898
 
899
    // vario down
900
    unsigned char ccd[54] = {0x55, 0x00, 0x55, 0x55, 0x28, 0x55, 0x55, 0x28,
901
        0x55, 0x55, 0x28, 0x55, 0x55, 0x28, 0x55, 0x55,
902
        0x28, 0x55, 0x55, 0x28, 0x55, 0x55, 0x28, 0x55,
903
        0x55, 0x28, 0x55, 0x55, 0x28, 0x55, 0x55, 0x28,
904
        0x55, 0x00, 0x28, 0x00, 0x2a, 0xaa, 0xa8, 0x0a,
905
        0xaa, 0xa0, 0x42, 0xaa, 0x81, 0x50, 0xaa, 0x05,
906
        0x54, 0x28, 0x15, 0x55, 0x00, 0x55};
907
 
908
    // vario hold
909
    unsigned char cce[54] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
910
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
911
        0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 0x00,
912
        0x2a, 0xaa, 0xa8, 0x2a, 0xaa, 0xa8, 0x00, 0x00,
913
        0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
914
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
915
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
916
 
917
    // vario up
918
    unsigned char ccf[54] = {0x55, 0x00, 0x55, 0x54, 0x28, 0x15, 0x50, 0xaa,
919
        0x05, 0x42, 0xaa, 0x81, 0x0a, 0xaa, 0xa0, 0x2a,
920
        0xaa, 0xa8, 0x00, 0x28, 0x00, 0x55, 0x28, 0x55,
921
        0x55, 0x28, 0x55, 0x55, 0x28, 0x55, 0x55, 0x28,
922
        0x55, 0x55, 0x28, 0x55, 0x55, 0x28, 0x55, 0x55,
923
        0x28, 0x55, 0x55, 0x28, 0x55, 0x55, 0x28, 0x55,
924
        0x55, 0x28, 0x55, 0x55, 0x00, 0x55};
925
 
926
    // degree symbol
927
    unsigned char cd0[54] = {0x55, 0x55, 0x55, 0x54, 0x01, 0x55, 0x52, 0xa8,
928
        0x55, 0x48, 0x02, 0x15, 0x48, 0x52, 0x15, 0x48,
929
        0x52, 0x15, 0x48, 0x02, 0x15, 0x52, 0xa8, 0x55,
930
        0x54, 0x01, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
931
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
932
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
933
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
934
 
935
    // clock on symbol
936
    unsigned char cd1[54] = {0x54, 0x14, 0x51, 0x52, 0x82, 0x08, 0x48, 0x22,
937
        0x88, 0x48, 0x22, 0x28, 0x48, 0x22, 0x28, 0x52,
938
        0x82, 0x08, 0x54, 0x14, 0x51, 0x55, 0x40, 0x55,
939
        0x55, 0x2a, 0x15, 0x54, 0x88, 0x85, 0x52, 0x08,
940
        0x21, 0x48, 0x48, 0x08, 0x48, 0x4a, 0x88, 0x48,
941
        0x50, 0x08, 0x52, 0x15, 0x21, 0x54, 0x80, 0x85,
942
        0x55, 0x2a, 0x15, 0x55, 0x40, 0x55};
943
 
944
    // clock fly symbol
945
    unsigned char cd2[54] = {0x40, 0x45, 0x11, 0x2a, 0x20, 0x88, 0x20, 0x20,
946
        0x88, 0x28, 0x21, 0x21, 0x21, 0x20, 0x21, 0x21,
947
        0x2a, 0x21, 0x45, 0x40, 0x45, 0x55, 0x40, 0x55,
948
        0x55, 0x2a, 0x15, 0x54, 0x88, 0x85, 0x52, 0x08,
949
        0x21, 0x48, 0x48, 0x08, 0x48, 0x4a, 0x88, 0x48,
950
        0x50, 0x08, 0x52, 0x15, 0x21, 0x54, 0x80, 0x85,
951
        0x55, 0x2a, 0x15, 0x55, 0x40, 0x55};
952
 
953
    // compass north
954
    unsigned char cd3[54] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
955
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x54,
956
        0x54, 0x55, 0x52, 0x12, 0x15, 0x52, 0x82, 0x15,
957
        0x02, 0x82, 0x00, 0xa2, 0x22, 0x2a, 0x02, 0x0a,
958
        0x00, 0x52, 0x0a, 0x15, 0x52, 0x12, 0x15, 0x54,
959
        0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
960
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
961
 
962
    // compass south
963
    unsigned char cd4[54] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
964
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
965
        0x01, 0x55, 0x54, 0xa8, 0x55, 0x52, 0x02, 0x15,
966
        0x04, 0x84, 0x40, 0xa1, 0x21, 0x2a, 0x04, 0x48,
967
        0x40, 0x52, 0x02, 0x15, 0x54, 0xa8, 0x55, 0x55,
968
        0x01, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
969
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
970
 
971
    // compass east
972
    unsigned char cd5[54] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
973
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x54,
974
        0x00, 0x55, 0x52, 0xaa, 0x15, 0x52, 0x00, 0x55,
975
        0x02, 0x05, 0x40, 0xa2, 0xa1, 0x2a, 0x02, 0x05,
976
        0x40, 0x52, 0x00, 0x55, 0x52, 0xaa, 0x15, 0x54,
977
        0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
978
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
979
 
980
    // compass west
981
    unsigned char cd6[54] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
982
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x54,
983
        0x54, 0x55, 0x52, 0x12, 0x15, 0x52, 0x12, 0x15,
984
        0x02, 0x02, 0x00, 0xa2, 0x22, 0x2a, 0x02, 0x8a,
985
        0x00, 0x52, 0x8a, 0x15, 0x52, 0x12, 0x15, 0x54,
986
        0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
987
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
988
 
989
    // compass between
990
    unsigned char cd7[54] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
991
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
992
        0x55, 0x55, 0x55, 0x45, 0x55, 0x55, 0x21, 0x55,
993
        0x01, 0x21, 0x00, 0xa8, 0x20, 0xaa, 0x01, 0x21,
994
        0x00, 0x55, 0x21, 0x55, 0x55, 0x45, 0x55, 0x55,
995
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
996
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
997
 
998
    // compass line
999
    unsigned char cd8[54] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1000
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1001
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1002
        0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00,
1003
        0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1004
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1005
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
1006
 
1007
    // arrow right
1008
    unsigned char cd9[54] ={0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1009
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1010
        0x55, 0x15, 0x55, 0x54, 0x85, 0x40, 0x00, 0xa1,
1011
        0x2a, 0xaa, 0xa8, 0x2a, 0xaa, 0xa8, 0x40, 0x00,
1012
        0xa1, 0x55, 0x54, 0x85, 0x55, 0x55, 0x15, 0x55,
1013
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1014
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
1015
 
1016
    // arrow right-up
1017
    unsigned char cda[54] ={0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1018
        0x55, 0x55, 0x40, 0x01, 0x55, 0x2a, 0xa8, 0x55,
1019
        0x4a, 0xa8, 0x55, 0x52, 0xa8, 0x55, 0x4a, 0xa8,
1020
        0x55, 0x2a, 0x28, 0x54, 0xa8, 0x48, 0x52, 0xa1,
1021
        0x51, 0x4a, 0x85, 0x55, 0x52, 0x15, 0x55, 0x54,
1022
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1023
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
1024
 
1025
    // arrow up
1026
    unsigned char cdb[54] ={0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1027
        0x55, 0x55, 0x41, 0x55, 0x55, 0x28, 0x55, 0x54,
1028
        0xaa, 0x15, 0x52, 0xaa, 0x85, 0x54, 0x28, 0x15,
1029
        0x55, 0x28, 0x55, 0x55, 0x28, 0x55, 0x55, 0x28,
1030
        0x55, 0x55, 0x28, 0x55, 0x55, 0x28, 0x55, 0x55,
1031
        0x28, 0x55, 0x55, 0x41, 0x55, 0x55, 0x55, 0x55,
1032
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
1033
 
1034
    // arrow left-up
1035
    unsigned char cdc[54] ={0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1036
        0x55, 0x40, 0x01, 0x55, 0x2a, 0xa8, 0x55, 0x2a,
1037
        0xa1, 0x55, 0x2a, 0x85, 0x55, 0x2a, 0xa1, 0x55,
1038
        0x28, 0xa8, 0x55, 0x21, 0x2a, 0x15, 0x45, 0x4a,
1039
        0x85, 0x55, 0x52, 0xa1, 0x55, 0x54, 0x85, 0x55,
1040
        0x55, 0x15, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1041
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
1042
 
1043
    // arrow left
1044
    unsigned char cdd[54] ={0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1045
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x54,
1046
        0x55, 0x55, 0x52, 0x15, 0x55, 0x4a, 0x00, 0x01,
1047
        0x2a, 0xaa, 0xa8, 0x2a, 0xaa, 0xa8, 0x4a, 0x00,
1048
        0x01, 0x52, 0x15, 0x55, 0x54, 0x55, 0x55, 0x55,
1049
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1050
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
1051
 
1052
    // arrow left-down
1053
    unsigned char cde[54] ={0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1054
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x15, 0x55,
1055
        0x54, 0x85, 0x55, 0x52, 0xa1, 0x45, 0x4a, 0x85,
1056
        0x21, 0x2a, 0x15, 0x28, 0xa8, 0x55, 0x2a, 0xa1,
1057
        0x55, 0x2a, 0x85, 0x55, 0x2a, 0xa1, 0x55, 0x2a,
1058
        0xa8, 0x55, 0x40, 0x01, 0x55, 0x55, 0x55, 0x55,
1059
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
1060
 
1061
    // arrow down
1062
    unsigned char cdf[54] ={0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1063
        0x55, 0x55, 0x41, 0x55, 0x55, 0x28, 0x55, 0x55,
1064
        0x28, 0x55, 0x55, 0x28, 0x55, 0x55, 0x28, 0x55,
1065
        0x55, 0x28, 0x55, 0x55, 0x28, 0x55, 0x54, 0x28,
1066
        0x15, 0x52, 0xaa, 0x85, 0x54, 0xaa, 0x15, 0x55,
1067
        0x28, 0x55, 0x55, 0x41, 0x55, 0x55, 0x55, 0x55,
1068
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
1069
 
1070
    // arrow right-down
1071
    unsigned char ce0[54] ={0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1072
        0x55, 0x55, 0x55, 0x55, 0x54, 0x55, 0x55, 0x52,
1073
        0x15, 0x55, 0x4a, 0x85, 0x55, 0x52, 0xa1, 0x51,
1074
        0x54, 0xa8, 0x48, 0x55, 0x2a, 0x28, 0x55, 0x4a,
1075
        0xa8, 0x55, 0x52, 0xa8, 0x55, 0x4a, 0xa8, 0x55,
1076
        0x2a, 0xa8, 0x55, 0x40, 0x01, 0x55, 0x55, 0x55,
1077
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
1078
 
1079
        // horizon up
1080
    unsigned char ce1[54] ={0x55, 0x55, 0x55, 0x00, 0x00, 0x00, 0xaa, 0xaa,
1081
        0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x55,
1082
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1083
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1084
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1085
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1086
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
1087
 
1088
        // horizon middle
1089
    unsigned char ce2[54] ={0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1090
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1091
        0x55, 0x55, 0x55, 0x55, 0x55, 0x00, 0x00, 0x00,
1092
        0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00,
1093
        0x00, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1094
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1095
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
1096
 
1097
        // horizon down
1098
    unsigned char ce3[54] ={0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1099
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1100
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1101
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1102
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00,
1103
        0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
1104
        0x00, 0x00, 0x00, 0x55, 0x55, 0x55};
1105
 
1106
        // horizon center
1107
    unsigned char ce4[54] ={0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1108
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1109
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1110
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1111
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x00,
1112
        0x14, 0x00, 0xaa, 0x14, 0xaa, 0xaa, 0x82, 0xaa,
1113
        0x00, 0xaa, 0x00, 0x54, 0x00, 0x15};
1114
 
1115
        // horizon roll
1116
    unsigned char ce5[54] ={0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1117
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1118
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1119
        0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
1120
        0x55, 0x00, 0x55, 0x00, 0x2a, 0x14, 0xa8, 0x4a,
1121
        0x82, 0xa1, 0x52, 0xaa, 0x85, 0x54, 0xaa, 0x15,
1122
        0x55, 0x28, 0x55, 0x55, 0x41, 0x55};
1123
 
1124
        // gps PH
1125
    unsigned char ce6[54] ={0x55, 0x05, 0x11, 0x54, 0xa0, 0x88, 0x54, 0x88,
1126
        0x88, 0x54, 0xa0, 0xa8, 0x54, 0x84, 0x88, 0x44,
1127
        0x84, 0x88, 0x21, 0x15, 0x11, 0xa8, 0x55, 0x55,
1128
        0xa1, 0x55, 0x55, 0x98, 0x15, 0x55, 0x2a, 0x85,
1129
        0x55, 0x4a, 0xa1, 0x55, 0x4a, 0xa8, 0x55, 0x52,
1130
        0xaa, 0x15, 0x54, 0xaa, 0x15, 0x55, 0x28, 0x55,
1131
        0x55, 0x41, 0x55, 0x55, 0x55, 0x55};
1132
 
1133
        // gps CH
1134
    unsigned char ce7[54] ={0x55, 0x55, 0x41, 0x55, 0x55, 0x28, 0x55, 0x54,
1135
        0x81, 0x55, 0x54, 0x85, 0x55, 0x54, 0x81, 0x45,
1136
        0x55, 0x28, 0x21, 0x55, 0x01, 0xa8, 0x54, 0x88,
1137
        0xa1, 0x54, 0x88, 0x98, 0x14, 0xa8, 0x2a, 0x84,
1138
        0x88, 0x4a, 0xa0, 0x88, 0x4a, 0xa8, 0x11, 0x52,
1139
        0xaa, 0x15, 0x54, 0xaa, 0x15, 0x55, 0x28, 0x55,
1140
        0x55, 0x41, 0x55, 0x55, 0x55, 0x55};
1141
 
1142
 
324 cascade 1143
        // flashing more than 8 chars per time is not proven to be safe
321 cascade 1144
        // so take care
324 cascade 1145
#if WRITECHARS == 200
1146
    learn_char(200, cc8);
321 cascade 1147
    learn_char(201, cc9);
1148
    learn_char(202, cca);
1149
    learn_char(203, ccb);
1150
    learn_char(204, ccc);
1151
    learn_char(205, ccd);
1152
    learn_char(206, cce);
324 cascade 1153
    learn_char(207, ccf);
1154
#endif
1155
#if WRITECHARS == 208
1156
    learn_char(208, cd0);
321 cascade 1157
    learn_char(209, cd1);
1158
    learn_char(210, cd2);
1159
    learn_char(211, cd3);
1160
    learn_char(212, cd4);
1161
    learn_char(213, cd5);
1162
    learn_char(214, cd6);
324 cascade 1163
    learn_char(215, cd7);
1164
#endif
1165
#if WRITECHARS == 216
1166
    learn_char(216, cd8);
321 cascade 1167
        learn_char(217, cd9);
1168
    learn_char(218, cda);
1169
    learn_char(219, cdb);
1170
    learn_char(220, cdc);
1171
    learn_char(221, cdd);
1172
    learn_char(222, cde);
324 cascade 1173
    learn_char(223, cdf);
1174
#endif
1175
#if WRITECHARS == 224
1176
    learn_char(224, ce0);
321 cascade 1177
        learn_char(225, ce1);
1178
        learn_char(226, ce2);
1179
        learn_char(227, ce3);
1180
        learn_char(228, ce4);
1181
        learn_char(229, ce5);
1182
        learn_char(230, ce6);
324 cascade 1183
        learn_char(231, ce7);
321 cascade 1184
#endif
324 cascade 1185
#endif // write char general
321 cascade 1186
    /* ##########################################################################
1187
     * continue normal main
1188
     * ##########################################################################*/
324 cascade 1189
 
1190
        // Setup Video Mode
1191
        uint8_t top_line = 1;
1192
        uint8_t bottom_line = 0;
1193
        if (COSD_FLAGS & COSD_FLAG_NTSC) {
1194
        // NTSC + enable display immediately (VM0)
1195
        spi_send_byte(0x00, 0b00001000);
1196
                bottom_line = 12;
1197
        } else {
1198
        // PAL + enable display immediately (VM0)
1199
        spi_send_byte(0x00, 0b01001000);
1200
                bottom_line = 14;
1201
        }
321 cascade 1202
 
1203
    // clear all display-mem (DMM)
1204
    spi_send_byte(0x04, 0b00000100);
1205
 
1206
    // clearing takes 12uS according to maxim so lets wait longer
1207
    _delay_us(120);
1208
 
1209
    // 8bit mode
1210
    spi_send_byte(0x04, 0b01000000);
1211
 
1212
    // write blank chars to whole screen
1213
    clear();
1214
 
1215
#if !(ALLCHARSDEBUG|WRITECHARS)
1216
    // init usart
1217
    usart1_init();
1218
 
1219
    // set up timer
1220
    TCCR0 |= (1 << CS00) | (1 << CS01); // timer0 prescaler 64
1221
    OCR0 = 6; // preload
1222
    TIMSK |= (1 << TOIE0); // enable overflow timer0
1223
 
1224
    // unmask interrupts
1225
    sei();
1226
#endif
1227
 
1228
    //write_ascii_string(2,  7, "         CaScAdE          ");
1229
    //write_ascii_string(2,  8, "is TESTING his open source");
1230
    //write_ascii_string(2,  9, "    EPi OSD Firmware");
1231
    //write_ascii_string(2, 10, "Scheiss Kompass");
1232
 
1233
 
1234
 
1235
 
1236
    // custom char preview
1237
    /*write_char_xy( 2, 7, 200);
1238
    write_char_xy( 3, 7, 201);
1239
    write_char_xy( 4, 7, 202);
1240
    write_char_xy( 5, 7, 203);
1241
    write_char_xy( 6, 7, 204);
1242
    write_char_xy( 7, 7, 205);
1243
    write_char_xy( 8, 7, 206);
1244
    write_char_xy( 9, 7, 207);
1245
    write_char_xy(10, 7, 208);
1246
    write_char_xy(11, 7, 209);
1247
    write_char_xy(12, 7, 210);
1248
    write_char_xy(13, 7, 211);
1249
    write_char_xy(14, 7, 212);
1250
    write_char_xy(15, 7, 213);
1251
    write_char_xy(16, 7, 214);
1252
    write_char_xy(17, 7, 215);*/
1253
 
1254
    // we are ready
1255
    LED3_ON
1256
 
1257
 
1258
 
1259
#if ALLCHARSDEBUG | WRITECHARS
1260
        clear();
1261
    write_all_chars();
1262
#else
1263
    // clear serial screen
1264
    //usart1_puts("\x1B[2J\x1B[H");
1265
    //usart1_puts("hello world!\r\n");
1266
 
1267
 
1268
    // request data ever 100ms from FC
1269
        //unsigned char ms = 10;
1270
        //sendMKData('d', 0, &ms, 1);
1271
 
1272
    // request OSD Data from NC every 100ms
1273
        unsigned char ms = 10;
1274
    sendMKData('o', 1, &ms, 1);
1275
        // and disable debug...
1276
        //ms = 0;
1277
        //sendMKData('d', 0, &ms, 1);
1278
 
1279
        // stats for after flight
1280
        int16_t max_Altimeter = 0;
1281
        uint16_t max_GroundSpeed = 0;
1282
        int16_t max_Distance = 0;
324 cascade 1283
        uint8_t min_UBat = 255;
1284
        uint16_t max_FlyingTime = 0;
321 cascade 1285
 
1286
        // flags from last round to check for changes
1287
        uint8_t old_MKFlags = 0;
1288
 
1289
    // write fix characters, only update the data dependend
1290
    write_char_xy(5, top_line, 203); // km/h
1291
    write_char_xy(10, top_line, 202); // RC-transmitter
1292
    write_char_xy(16, top_line, 208); // degree symbol
1293
    write_char_xy(27, top_line, 204); // small meters m
1294
    write_ascii_string(6, bottom_line, "V"); // voltage
1295
    write_char_xy(14, bottom_line, 209); // on clock
1296
    write_char_xy(22, bottom_line, 210); // fly clock
1297
    write_char_xy(26, bottom_line, 200); // sat1
1298
    write_char_xy(27, bottom_line, 201); // sat2
1299
 
1300
    char* directions[8] = {"NE", "E ", "SE", "S ", "SW", "W ", "NW", "N "};
1301
        char arrowdir[8] =   { 218,  217,  224,  223,  222,  221,  220, 219};
1302
 
1303
    while (1) {
1304
        if (rxd_buffer_locked) {
1305
            if (rxd_buffer[2] == 'D') { // FC Data
1306
                /*Decode64();
1307
                debugData = *((DebugOut_t*) pRxData);
1308
                write_number_s(12, 2, RxDataLen);
1309
                write_number_s(20, 2, setsReceived++);
1310
                write_number_s(12, 3, debugData.Analog[0]);
1311
                write_number_s(12, 4, debugData.Analog[2]);
1312
                write_number_s(12, 5, debugData.Analog[1]);
1313
                write_number_s(12, 6, debugData.Analog[3]);
1314
                write_number_s(12, 7, debugData.Analog[9]);
1315
                write_number_s(12, 8, debugData.Analog[10]);
1316
                                write_number_s(12, 4, debugData.Analog[12]);
1317
                                write_number_s(12, 5, debugData.Analog[13]);
1318
                                write_number_s(12, 6, debugData.Analog[14]);
1319
                                write_number_s(12, 7, debugData.Analog[15]);*/
1320
            } else if (rxd_buffer[2] == 'O') { // NC OSD Data
1321
                Decode64();
1322
                naviData = *((NaviData_t*) pRxData);
1323
 
1324
                                // first line
1325
                                write_3digit_number_u(2, top_line, (uint16_t)(((uint32_t)naviData.GroundSpeed*36)/1000));
1326
 
1327
                write_3digit_number_u(7, top_line, naviData.RC_Quality);
1328
                if (naviData.RC_Quality <= RCLVL_WRN && last_RC_Quality > RCLVL_WRN) {
1329
                    for (uint8_t x = 0; x < 4; x++)
1330
                        write_char_att_xy(7 + x, top_line, BLINK);
1331
                } else if (naviData.RC_Quality > RCLVL_WRN && last_RC_Quality <= RCLVL_WRN) {
1332
                    for (uint8_t x = 0; x < 4; x++)
1333
                        write_char_att_xy(7 + x, top_line, 0);
1334
                }
1335
                last_RC_Quality = naviData.RC_Quality;
1336
 
1337
                write_3digit_number_u(13, top_line, naviData.CompassHeading);
1338
 
1339
                write_ascii_string(17, top_line, directions[heading_conv(naviData.CompassHeading)]);
1340
 
1341
                if (naviData.Variometer == 0) {
1342
                    write_char_xy(20, top_line, 206); // plain line
1343
                } else if (naviData.Variometer > 0) {
1344
                    write_char_xy(20, top_line, 207); // arrow up
1345
                } else {
1346
                    write_char_xy(20, top_line, 205); // arrow down
1347
                }
1348
 
1349
                                // TODO: is this really dm?
1350
                write_number_s(22, top_line, naviData.Altimeter/10);
1351
 
1352
                                // seccond line
1353
                draw_compass(11, top_line + 1, naviData.CompassHeading);
1354
 
1355
                                // TODO: verify correctness
1356
                                uint16_t heading_home = (naviData.HomePositionDeviation.Bearing + 360 - naviData.CompassHeading) % 360;
1357
                                write_char_xy(27, top_line + 1, arrowdir[heading_conv(heading_home)]);
1358
 
1359
 
1360
                                write_number_s(22, top_line + 1, naviData.HomePositionDeviation.Distance);
1361
 
1362
                                // center
1363
                                if (naviData.MKFlags & FLAG_MOTOR_RUN) { // should be engines running
1364
                                        if (!(old_MKFlags & FLAG_MOTOR_RUN)) { // motors just started, clear middle
1365
                                                for (uint8_t x = 0; x < 30; x++) {
1366
                                                        write_char_xy(x, 5, 0);
1367
                                                        write_char_xy(x, 7, 0);
1368
                                                        write_char_xy(x, 9, 0);
1369
                                                }
1370
                                        }
324 cascade 1371
                                if (COSD_FLAGS & COSD_FLAG_ARTHORIZON) {
1372
                                        draw_artificial_horizon(top_line + 2, bottom_line - 1, naviData.AngleNick, naviData.AngleRoll);
1373
                                }
321 cascade 1374
                                } else {
1375
                                        // stats
1376
                                        write_ascii_string(2, 5, "max Altitude:");
1377
                                        write_number_s(17, 5, max_Altimeter/10);
1378
                                        write_char_xy(22, 5, 204); // small meters m
324 cascade 1379
                                        write_ascii_string(2, 6, "max Speed   :");
1380
                                        write_3digit_number_u(19, 6, (uint16_t)(((uint32_t)max_GroundSpeed*36)/1000));
1381
                                        write_char_xy(22, 6, 203); // km/h
1382
                                        write_ascii_string(2, 7, "max Distance:");
1383
                                        write_number_s(17, 7, max_Distance/100);
1384
                                        write_char_xy(22, 7, 204); // small meters m
1385
                                        write_ascii_string(2, 8, "min voltage :");
1386
                                        //write_number_s(17, 8, min_UBat/10);
1387
                                        write_number_u_10th(16, 8, min_UBat);
1388
                                        write_ascii_string(22, 8, "V"); // voltage
1389
                                        write_ascii_string(2, 9, "max time    :");
1390
                                        write_number_s(17, 9, max_FlyingTime);
1391
                                        write_char_xy(22, 9, 210); // fly clock
321 cascade 1392
                                }
1393
 
1394
                                // bottom line
1395
                write_number_u_10th(0, bottom_line, naviData.UBat);
1396
                if (naviData.UBat <= UBAT_WRN && last_UBat > UBAT_WRN) {
1397
                    for (uint8_t x = 0; x < 7; x++)
1398
                        write_char_att_xy(x, bottom_line, BLINK);
1399
                } else {
1400
                    for (uint8_t x = 0; x < 7; x++)
1401
                        write_char_att_xy(x, bottom_line, 0);
1402
                }
1403
 
1404
                write_time(8, bottom_line, uptime);
1405
                write_time(16, bottom_line, naviData.FlyingTime);
1406
 
1407
                write_3digit_number_u(23, bottom_line, naviData.SatsInUse);
1408
 
1409
                                if (naviData.NCFlags & NC_FLAG_CH) {
1410
                                        write_char_xy(27, bottom_line, 231);    // gps ch
1411
                                } else if (naviData.NCFlags & NC_FLAG_PH) {
1412
                                        write_char_xy(27, bottom_line, 230);    // gps ph
1413
                                } else { // (naviData.NCFlags & NC_FLAG_FREE)
1414
                                        write_char_xy(27, bottom_line, 201);    // sat2 (free)
1415
                                }
1416
 
1417
                //write_number_s(8, 5, RxDataLen);
1418
                //write_number_s(16, 5, setsReceived++);
1419
 
1420
                                // remember statistics
1421
                                if (naviData.Altimeter > max_Altimeter) max_Altimeter = naviData.Altimeter;
1422
                                if (naviData.GroundSpeed > max_GroundSpeed) max_GroundSpeed = naviData.GroundSpeed;
1423
                                if (naviData.HomePositionDeviation.Distance > max_Distance) {
1424
                                        max_Distance = naviData.HomePositionDeviation.Distance;
1425
                                }
324 cascade 1426
                                if (naviData.UBat < min_UBat) min_UBat = naviData.UBat;
1427
                                if (naviData.FlyingTime > max_FlyingTime) max_FlyingTime = naviData.FlyingTime;
1428
 
321 cascade 1429
                                old_MKFlags = naviData.MKFlags;
1430
            }
1431
            rxd_buffer_locked = 0;
1432
        }
1433
        // handle keypress
1434
        if (S1_PRESSED) {
1435
            uptime = 0;
1436
            _delay_ms(100);
1437
        }
1438
        if (S2_PRESSED) {
1439
                        //sendMKData('d', 1, (unsigned char*) 0, 1);
1440
            // request OSD Data from NC every 100ms
1441
                        unsigned char ms = 10;
1442
            sendMKData('o', 1, &ms, 1);
1443
            _delay_ms(500);
1444
        }
1445
    }
1446
#endif
1447
    return 0;
1448
}