0,0 → 1,431 |
/**************************************************************************** |
* Copyright (C) 2009-2010 by Claas Anders "CaScAdE" Rathje * |
* admiralcascade@gmail.com * |
* Project-URL: http://www.mylifesucks.de/oss/c-osd/ * |
* MOD By Eduardo R Lavratti - aKKa AGRESSiVA * |
* |
* This program is free software; you can redistribute it and/or modify * |
* it under the terms of the GNU General Public License as published by * |
* the Free Software Foundation; either version 2 of the License. * |
* * |
* This program is distributed in the hope that it will be useful, * |
* but WITHOUT ANY WARRANTY; without even the implied warranty of * |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
* GNU General Public License for more details. * |
* * |
* You should have received a copy of the GNU General Public License * |
* along with this program; if not, write to the * |
* Free Software Foundation, Inc., * |
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * |
* * |
* * |
* Credits to: * |
* Holger Buss & Ingo Busker from mikrokopter.de for the MK project + SVN * |
* Gregor "killagreg" Stobrawa for making the MK code readable * |
* Klaus "akku" Buettner for the hardware * |
* Manuel "KeyOz" Schrape for explaining the MK protocol to me * |
****************************************************************************/ |
|
#include <avr/io.h> |
#include <avr/interrupt.h> |
#include <util/delay.h> |
#include <avr/pgmspace.h> |
#include "main.h" |
#include "max7456_software_spi.h" |
#include "usart1.h" |
#include "osd_helpers.h" |
#include "config.h" |
#include "spi.h" |
#include "buttons.h" |
#include "ppm.h" |
#include "osd_ncmode_default.h" |
#include "osd_ncmode_minimal.h" |
#include "osd_fcmode_default.h" |
#include "osd_fcmode_jopl.h" |
|
#if WRITECHARS != -1 |
#include "characters.h" |
#endif |
|
/* TODO: |
* - verifiy correctness of values |
* - clean up code :) |
*/ |
|
|
/* ########################################################################## |
* global definitions and global vars |
* ##########################################################################*/ |
|
volatile NaviData_t naviData; |
volatile DebugOut_t debugData; |
|
// cache old vars for blinking attribute, checkup is faster than full |
// attribute write each time |
volatile uint8_t last_UBat = 255; |
volatile uint8_t last_RC_Quality = 255; |
|
// 16bit should be enough, normal LiPos don't last that long |
volatile uint16_t uptime = 0; |
volatile uint16_t timer = 0; |
volatile uint16_t flytime_fc = 0; |
|
// remember last time data was received |
volatile uint8_t seconds_since_last_data = 0; |
|
// general PAL|NTSC distingiusch stuff |
uint8_t top_line = 1; |
uint8_t bottom_line = 14; |
|
// battery voltages |
uint8_t min_voltage = 0; |
uint8_t max_voltage = 0; |
|
// Flags |
uint8_t COSD_FLAGS_MODES = 0, COSD_FLAGS_CONFIG = 0, COSD_FLAGS_RUNTIME = 0, COSD_DISPLAYMODE = 0; |
|
#if !(ALLCHARSDEBUG|(WRITECHARS != -1)) |
|
// stats for after flight |
int16_t max_Altimeter = 0; |
uint8_t min_UBat = 255; |
uint16_t max_GroundSpeed = 0; |
int16_t max_Distance = 0; |
uint16_t max_FlyingTime = 0; |
|
// flags from last round to check for changes |
uint8_t old_MKFlags = 0; |
|
// store stats description in progmem to save space |
const char stats_item_0[] PROGMEM = "max Altitude:"; |
const char stats_item_1[] PROGMEM = "max Speed :"; |
const char stats_item_2[] PROGMEM = "max Distance:"; |
const char stats_item_3[] PROGMEM = "min Voltage :"; |
const char stats_item_4[] PROGMEM = "max Time :"; |
const char stats_item_5[] PROGMEM = "longitude :"; |
const char stats_item_6[] PROGMEM = "latitude :"; |
const char stats_item_7[] PROGMEM = "max current :"; |
const char *stats_item_pointers[8] PROGMEM = {stats_item_0, stats_item_1, stats_item_2, |
stats_item_3, stats_item_4, stats_item_5, stats_item_6, stats_item_7}; |
|
//char* directions[8] = {"NE", "E ", "SE", "S ", "SW", "W ", "NW", "N "}; |
//char arrowdir[8] = {218, 217, 224, 223, 222, 221, 220, 219}; |
const char str_NE[] PROGMEM = "NE"; |
const char str_E[] PROGMEM = "E "; |
const char str_SE[] PROGMEM = "SE"; |
const char str_S[] PROGMEM = "S "; |
const char str_SW[] PROGMEM = "SW"; |
const char str_W[] PROGMEM = "W "; |
const char str_NW[] PROGMEM = "NW"; |
const char str_N[] PROGMEM = "N "; |
const char *directions[8] PROGMEM = { |
str_NE, |
str_E, |
str_SE, |
str_S, |
str_SW, |
str_W, |
str_NW, |
str_N}; |
|
/* ########################################################################## |
* Different display mode function pointers |
* ##########################################################################*/ |
const char str_1[] PROGMEM = "default"; |
const char str_2[] PROGMEM = "minimal"; |
const char str_3[] PROGMEM = " jopl"; |
|
const displaymode_t ncdisplaymodes[] PROGMEM = { |
{ osd_ncmode_default, (char *)str_1 }, |
{ osd_ncmode_minimal, (char *)str_2 } |
}; |
|
const displaymode_t fcdisplaymodes[] PROGMEM = { |
{ osd_fcmode_default, (char *)str_1 }, |
{ osd_fcmode_jopl, (char *)str_3 } |
}; |
|
int (*osd_ncmode)(void) = (int(*)(void)) &osd_ncmode_default; |
int (*osd_fcmode)(void) = (int(*)(void)) &osd_fcmode_default; |
#endif |
|
/* ########################################################################## |
* Interrupt handler |
* ##########################################################################*/ |
|
/** |
* handler for undefined Interrupts |
* if not defined AVR will reset in case any unhandled interrupts occur |
*/ |
ISR(__vector_default) { |
asm("nop"); |
} |
|
#if !(ALLCHARSDEBUG|(WRITECHARS != -1)) |
/* ########################################################################## |
* timer stuff |
* ##########################################################################*/ |
|
static uint8_t delay_spi = 0; |
|
/** |
* timer kicks in every 1000uS ^= 1ms |
*/ |
ISR(TIMER0_COMP_vect) { |
if (!timer--) { |
uptime++; |
|
#if FCONLY |
if (debugData.Analog[12]>10) { |
flytime_fc++; |
} |
#endif |
|
timer = 999; |
seconds_since_last_data++; |
} |
// in case there is still some spi data to send do it now |
// delay to give the slave some time to compute values |
if (spi_ready && icnt) { |
if (!delay_spi--) { |
delay_spi = 8; |
spi_send_next(); |
} |
} |
} |
#endif // ends !(ALLCHARSDEBUG|(WRITECHARS != -1))#endif // ends !(ALLCHARSDEBUG|(WRITECHARS != -1)) |
|
/* ########################################################################## |
* MAIN |
* ##########################################################################*/ |
int main(void) { |
// set up FLAGS |
COSD_FLAGS_MODES = 0, COSD_FLAGS_CONFIG = 0, COSD_FLAGS_RUNTIME = 0; |
#if NTSC |
COSD_FLAGS_CONFIG |= COSD_FLAG_NTSC; |
#endif |
#if HUD |
COSD_FLAGS_MODES |= COSD_FLAG_HUD; |
#endif |
#if ARTHORIZON |
COSD_FLAGS_MODES |= COSD_FLAG_ARTHORIZON; |
#endif |
#if BIGVARIO |
COSD_FLAGS_MODES |= COSD_FLAG_BIGARIO; |
#endif |
#if STATS |
COSD_FLAGS_MODES |= COSD_FLAG_STATS; |
#endif |
#if WARNINGS |
COSD_FLAGS_MODES |= COSD_FLAG_WARNINGS; |
#endif |
#if FCONLY |
COSD_FLAGS_CONFIG |= COSD_FLAG_FCMODE; |
#endif |
|
|
// set up Atmega162 Ports |
DDRA |= (1 << PA1); // PA1 output (/CS) |
MAX_CS_HIGH |
DDRA |= (1 << PA2); // PA2 output (SDIN) |
MAX_SDIN_LOW |
DDRA |= (1 << PA3); // PA3 output (SCLK) |
MAX_SCLK_LOW |
DDRA |= (1 << PA5); // PA5 output (RESET) |
MAX_RESET_HIGH |
|
DDRC |= (1 << PC0); // PC0 output (LED1 gn) |
LED1_OFF |
DDRC |= (1 << PC1); // PC1 output (LED2 rt) |
LED2_OFF |
DDRC |= (1 << PC2); // PC2 output (LED3 gn) |
LED3_OFF |
DDRC |= (1 << PC3); // PC3 output (LED4 rt) |
LED4_OFF |
|
DDRC &= ~(1 << PC4); // PC4 input (MODE) |
PORTC |= (1 << PC4); // pullup |
DDRC &= ~(1 << PC5); // PC5 input (SET) |
PORTC |= (1 << PC5); // pullup |
|
// reset the MAX7456 to be sure any undefined states do no harm |
MAX_RESET_LOW |
MAX_RESET_HIGH |
|
// give the FC/NC and the maxim time to come up |
LED4_ON |
_delay_ms(1000); |
LED4_OFF |
|
//Pushing NEW chars to the MAX7456 |
#if (WRITECHARS != -1) |
// DISABLE display (VM0) |
spi_send_byte(0x00, 0b00000000); |
learn_all_chars_pgm(); |
#else |
// read out config for NTSC/PAL distinguishing |
get_eeprom(0); |
#endif |
|
// Setup Video Mode |
if (COSD_FLAGS_CONFIG & COSD_FLAG_NTSC) { |
// NTSC + enable display immediately (VM0) |
spi_send_byte(0x00, 0b00001000); |
|
bottom_line = 12; |
} else { |
// PAL + enable display immediately (VM0) |
spi_send_byte(0x00, 0b01001000); |
|
bottom_line = 14; |
} |
|
/*// clear all display-mem (DMM) |
spi_send_byte(0x04, 0b00000100); |
|
// clearing takes 12uS according to maxim so lets wait longer |
_delay_us(120); |
|
// 8bit mode |
spi_send_byte(0x04, 0b01000000);*/ |
|
// clear display memory and set to 8bit mode |
clear(); |
|
#if !(ALLCHARSDEBUG|(WRITECHARS != -1)) |
// init usart |
usart1_init(); |
|
// keep serial port clean |
usart1_DisableTXD(); |
|
// set up timer |
// CTC, Prescaler /64 |
TCCR0 = (1 << WGM01) | (0 << WGM00) | (0 << CS02) | (1 << CS01) | (1 << CS00); |
|
TCNT0 = 0; |
OCR0 = 250; |
|
// enable timer output compare interrupt |
TIMSK &= ~(1 << TOIE0); |
TIMSK |= (1 << OCIE0); |
|
// SPI setup |
DDRD |= (1 << PD2); // PD2 output (INT0) |
SpiMasterInit(); |
|
// PPM detection setup |
ppm_init(); |
|
// enable interrupts |
sei(); |
#endif |
|
write_ascii_string(2, 5, " AGRESSiVA MOD"); |
write_ascii_string(2, 7, " EPi OSD Firmware"); |
write_ascii_string(2, 9, " WAiTiNG for Controller"); |
// we are ready |
LED3_ON |
|
#if ALLCHARSDEBUG | (WRITECHARS != -1) |
clear(); |
write_all_chars(); |
LED1_ON |
LED2_ON |
LED3_ON |
LED4_ON |
#else |
|
// clear serial screen |
//usart1_puts("\x1B[2J\x1B[H"); |
//usart1_puts("hello world!123\r\n"); |
|
while (1) { |
// in case SPI is ready and there is nothing to send right now |
if (!icnt && spi_ready) { |
// correct transfer ends with d (done) |
if (SPI_buffer.buffer.chk == 'd') { |
ampere = SPI_buffer.data.ampere; |
ampere_wasted = SPI_buffer.data.mah; |
s_volt = SPI_buffer.data.volt; |
|
// if this is the first receival we should print the small A |
if (!(COSD_FLAGS_RUNTIME & COSD_FLAG_STROMREC)) { |
clear(); |
COSD_FLAGS_RUNTIME &= ~COSD_ICONS_WRITTEN; |
// update this flag |
COSD_FLAGS_RUNTIME |= COSD_FLAG_STROMREC; |
} |
} else { |
// update flags |
COSD_FLAGS_RUNTIME &= ~COSD_FLAG_STROMREC; |
} |
StartTransfer(9); |
} |
if (rxd_buffer_locked) { |
#if FCONLY |
if (rxd_buffer[2] == 'D') { // FC Data |
Decode64(); |
debugData = *((DebugOut_t*) pRxData); |
|
// init on first data retrival, distinguished by last battery :) |
if (last_UBat == 255) { |
if (debugData.Analog[9] > 40) { |
// fix for min_UBat |
min_UBat = debugData.Analog[9]; |
last_UBat = debugData.Analog[9]; |
init_cosd(last_UBat); |
} |
} else { |
osd_fcmode(); |
} |
seconds_since_last_data = 0; |
} |
#else |
if (rxd_buffer[2] == 'O') { // NC OSD Data |
Decode64(); |
naviData = *((NaviData_t*) pRxData); |
|
// init on first data retrival, distinguished by last battery :) |
if (last_UBat == 255) { |
if (naviData.UBat > 40) { |
// fix for min_UBat |
min_UBat = naviData.UBat; |
last_UBat = naviData.UBat; |
init_cosd(last_UBat); |
} |
} else { |
osd_ncmode(); |
} |
//seconds_since_last_data = 0; |
} |
#endif |
rxd_buffer_locked = 0; |
} |
// handle keypress |
if (s1_pressed()) { |
config_menu(); |
} |
if (seconds_since_last_data > 0) { |
usart1_EnableTXD(); |
//usart1_puts_pgm(PSTR("zu alt\r\n")); |
#if FCONLY |
// request data ever 100ms from FC; |
//usart1_request_mk_data(0, 'd', 100); |
usart1_puts_pgm(PSTR(REQUEST_DBG_DATA)); |
#else |
// request OSD Data from NC every 100ms |
//usart1_request_mk_data(1, 'o', 100); |
usart1_puts_pgm(PSTR(REQUEST_OSD_DATA)); |
|
// and disable debug... |
//usart1_request_mk_data(0, 'd', 0); |
#endif |
// reset last time counter |
seconds_since_last_data = 0; |
usart1_DisableTXD(); |
} |
} |
#endif |
return 0; |
} |