1
0
Fork 0
mirror of https://github.com/opentx/opentx.git synced 2025-07-21 23:35:17 +03:00

Simu for V4. First draft of rotary encoders use in menus

This commit is contained in:
bsongis 2011-12-03 16:40:36 +00:00
parent 8faf304b14
commit 352b04302a
18 changed files with 904 additions and 178 deletions

View file

@ -48,6 +48,19 @@ HELI = NO
# Values = YES, NO
TEMPLATES = YES
# Enable navigation with Pots / Rotary encoders
# Values = NO
# POT1 (only POT1 available for fields modification),
# POT2 (menus scroll),
# POT3 (cursor down/up),
# POTS (POT1, POT2, POT3),
# RE1 (Rotary encoder 1, on V3/V4 boards)
ifeq ($(PCB), V4)
NAVIGATION = RE1
else
NAVIGATION = POTS
endif
# gruvin: BEEPER. Values = BUZZER, BUZZER_MOD or SPEAKER
# (without a mod, BUZZER can make PPM jack output switch from output to input at random)
# SPEAKER mode actually works on the stock radio, using the stock beeper. Sort of sound OK(ish).
@ -122,7 +135,7 @@ endif
# Disk IO support (PCB V2+ only)
ifneq ($(PCB), STD)
CPPSRC += time.cpp
CPPSRC += gtime.cpp
CPPSRC += rtc.cpp
CPPSRC += ff.cpp
CPPSRC += diskio.cpp
@ -178,6 +191,21 @@ CPPDEFS = -DF_CPU=$(F_CPU)UL
# NOTE: PCB version now overrides all the earlier individual settings
# These individual settings work only for PCB=STD
# If POT1/POTS/RE1 is used for fields modification
ifeq ($(NAVIGATION), POT1)
CPPDEFS += -DNAVIGATION_POT1
endif
ifeq ($(NAVIGATION), POT2)
CPPDEFS += -DNAVIGATION_POT2
endif
ifeq ($(NAVIGATION), POT3)
CPPDEFS += -DNAVIGATION_POT3
endif
ifeq ($(NAVIGATION), POTS)
CPPDEFS += -DNAVIGATION_POT1 -DNAVIGATION_POT2 -DNAVIGATION_POT3
endif
ifeq ($(PCB), STD)
# STD PCB, so ...
@ -232,6 +260,11 @@ ifeq ($(PCB), STD)
endif
else
ifeq ($(NAVIGATION), RE1)
CPPDEFS += -DNAVIGATION_RE1
endif
# not PCB=STD, so ...
CPPSRC += frsky.cpp
CPPDEFS += -DPCBV3 -DFRSKY -DFRSKY_HUB

View file

@ -26,11 +26,9 @@
/* are platform dependent. */
/*-----------------------------------------------------------------------*/
#include <avr/io.h>
#include "gruvin9x.h"
#include "diskio.h"
/* Definitions for MMC/SDC command */
#define CMD0 (0) /* GO_IDLE_STATE */
#define CMD1 (1) /* SEND_OP_COND (MMC) */
@ -59,8 +57,13 @@
/* Port Controls (Platform dependent) */
// GCC optimisation should result in a single CBI/SBI instructions here
#define CS_LOW() PORTB &= ~0x10 /* MMC CS = L */
#define CS_HIGH() PORTB |= 0x10 /* MMC CS = H */
#if defined (PCBV4)
# define CS_LOW() PORTB &= ~0x01 /* MMC CS = L */
# define CS_HIGH() PORTB |= 0x01 /* MMC CS = H */
#else
# define CS_LOW() PORTB &= ~0x10 /* MMC CS = L */
# define CS_HIGH() PORTB |= 0x10 /* MMC CS = H */
#endif
#define SOCKPORT PINB /* Socket contact port */
#define SOCKWP 0x00 // not implemented /* Write protect switch */
@ -175,7 +178,9 @@ static
void power_on (void)
{
// PORTE &= ~0x80; // Socket power on
#ifndef SIMU
for (Timer1 = 2; Timer1; ); // Wait for 20ms
#endif
//PORTB = 0b10110101; // Enable drivers
//DDRB = 0b11000111;

View file

@ -121,7 +121,6 @@ public:
uint8_t getDbl() { return m_dblcnt; }
};
Key keys[NUM_KEYS];
void Key::input(bool val, EnumKeys enuk)
{
@ -214,10 +213,6 @@ bool keyState(EnumKeys enuk)
case SW_Trainer: return PINB & (1<<INP_B_Trainer);
case SW_RE1: return (~PIND & 0b00100000);
case SW_RE2: return (~PIND & 0b00010000);
default:;
}
#else
@ -306,6 +301,10 @@ void per10ms()
*/
# if defined (PCBV4)
keys[BTN_RE1].input(~PIND & 0b00100000, BTN_RE1);
keys[BTN_RE2].input(~PIND & 0b00010000, BTN_RE2);
uint8_t tin = ~PINL;
uint8_t in;
in = (tin & 0x0f) << 3;
@ -343,6 +342,7 @@ void per10ms()
keys[enuk].input(in & (1<<i),(EnumKeys)enuk);
++enuk;
}
// End User buttons
// Trim switches ...

View file

@ -42,6 +42,7 @@
extern "C" {
#endif
#include "inttypes.h"
#include "integer.h" /* Basic integer types */
#include "ffconf.h" /* FatFs configuration options */
@ -473,7 +474,7 @@ TCHAR* f_gets (TCHAR*, int, FIL*); /* Get a string from the file */
/* RTC function */
#if !_FS_READONLY
extern DWORD get_fattime (void); /* Returns 32-bit packed date and time */
extern uint32_t get_fattime (void); /* Returns 32-bit packed date and time */
#endif
/* Unicode support functions */

View file

@ -439,14 +439,14 @@ bool RlcFile::copy(uint8_t i_fileDst, uint8_t i_fileSrc)
if (m_currBlk && (fri=EeFsGetLink(m_currBlk)))
EeFsSetLink(m_currBlk, 0);
if (fri) EeFsFree(fri); //chain in
eeFs.files[FILE_TMP].size = m_pos;
EFile::swap(m_fileId, FILE_TMP);
if (fri) EeFsFree(fri); //chain in
assert(!m_write_step);
s_sync_write = false;
// s_sync_write is set to false in swap();
return true;
}

View file

@ -488,7 +488,7 @@ void FrskyData::set(uint8_t value)
// TODO not here!
#if defined (PCBV3)
char g_logFilename[21]; // "/G9XLOGS/M00_000.TXT\0" max required length = 21
char g_logFilename[22]; // "/G9XLOGS/M00_000.TXT\0" max required length = 21
// These global so we can close any open file from anywhere
FATFS FATFS_Obj;
FIL g_oLogFile;

View file

@ -256,8 +256,8 @@ void menuProcTime(uint8_t event)
int8_t sub = m_posVert - 1; // vertical position (1 = page counter, top/right)
uint8_t subSub = m_posHorz; // horizontal position
static struct tm t;
struct tm *at = &t;
static struct gtm t;
struct gtm *at = &t;
switch(event)
{
@ -266,7 +266,7 @@ void menuProcTime(uint8_t event)
killEvents(event);
break;
case EVT_KEY_FIRST(KEY_MENU):
if (sub >= 0 && !s_editMode) // set the date and time into RTC chip
if (sub >= 0 && s_editMode<=0) // set the date and time into RTC chip
{
g_ms100 = 0; // start of next second begins now
g_unixTime = mktime(&t); // update local timestamp and get wday calculated
@ -286,7 +286,7 @@ void menuProcTime(uint8_t event)
break;
}
if (!s_editMode) filltm(&g_unixTime, &t);
if (s_editMode<=0) filltm(&g_unixTime, &t);
lcd_putc(FW*10+2, FH*2, '-'); lcd_putc(FW*13, FH*2, '-');
lcd_putc(FW*10+1, FH*4, ':'); lcd_putc(FW*13-1, FH*4, ':');
@ -299,7 +299,7 @@ void menuProcTime(uint8_t event)
for(uint8_t j=0; j<3;j++) // 3 settings each for date and time (YMD and HMS)
{
uint8_t attr = (sub==i && subSub==j) ? (s_editMode ? BLINK : INVERS) : 0;
uint8_t attr = (sub==i && subSub==j) ? (s_editMode>0 ? BLINK : INVERS) : 0;
switch(i)
{
case 0: // DATE
@ -307,15 +307,15 @@ void menuProcTime(uint8_t event)
{
case 0:
lcd_outdezAtt(FW*10+2, y, at->tm_year+1900, attr);
if(attr && (s_editMode || p1valdiff)) at->tm_year = checkIncDec( event, at->tm_year, 110, 200, 0);
if(attr && (s_editMode>0 || p1valdiff)) at->tm_year = checkIncDec( event, at->tm_year, 110, 200, 0);
break;
case 1:
lcd_outdezNAtt(FW*13, y, at->tm_mon+1, attr|LEADING0, 2);
if(attr && (s_editMode || p1valdiff)) at->tm_mon = checkIncDec( event, at->tm_mon, 0, 11, 0);
if(attr && (s_editMode>0 || p1valdiff)) at->tm_mon = checkIncDec( event, at->tm_mon, 0, 11, 0);
break;
case 2:
lcd_outdezNAtt(FW*16-2, y, at->tm_mday, attr|LEADING0, 2);
if(attr && (s_editMode || p1valdiff)) at->tm_mday = checkIncDec( event, at->tm_mday, 1, 31, 0);
if(attr && (s_editMode>0 || p1valdiff)) at->tm_mday = checkIncDec( event, at->tm_mday, 1, 31, 0);
break;
}
break;
@ -325,15 +325,15 @@ void menuProcTime(uint8_t event)
{
case 0:
lcd_outdezNAtt(FW*10+1, y, at->tm_hour, attr|LEADING0, 2);
if(attr && (s_editMode || p1valdiff)) at->tm_hour = checkIncDec( event, at->tm_hour, 0, 23, 0);
if(attr && (s_editMode>0 || p1valdiff)) at->tm_hour = checkIncDec( event, at->tm_hour, 0, 23, 0);
break;
case 1:
lcd_outdezNAtt(FW*13-1, y, at->tm_min, attr|LEADING0, 2);
if(attr && (s_editMode || p1valdiff)) at->tm_min = checkIncDec( event, at->tm_min, 0, 59, 0);
if(attr && (s_editMode>0 || p1valdiff)) at->tm_min = checkIncDec( event, at->tm_min, 0, 59, 0);
break;
case 2:
lcd_outdezNAtt(FW*16-2, y, at->tm_sec, attr|LEADING0, 2);
if(attr && (s_editMode || p1valdiff)) at->tm_sec = checkIncDec( event, at->tm_sec, 0, 59, 0);
if(attr && (s_editMode>0 || p1valdiff)) at->tm_sec = checkIncDec( event, at->tm_sec, 0, 59, 0);
break;
}
break;
@ -363,7 +363,7 @@ void menuProcTrainer(uint8_t event)
sub--;
y = 2*FH;
blink = s_editMode ? BLINK : INVERS ;
blink = (s_editMode>0) ? BLINK : INVERS ;
for (uint8_t i=0; i<4; i++) {
uint8_t chan = pgm_read_byte(chout_ar+g_eeGeneral.templateSetup*4+i); // G: Issue 30.
@ -374,17 +374,17 @@ void menuProcTrainer(uint8_t event)
edit = (sub==i && subSub==0);
lcd_putsnAtt(4*FW, y, PSTR("off += :=")+3*td->mode, 3, edit ? blink : 0);
if (edit && s_editMode)
if (edit && s_editMode>0)
CHECK_INCDEC_GENVAR(event, td->mode, 0, 2);
edit = (sub==i && subSub==1);
lcd_outdezAtt(11*FW, y, td->studWeight, edit ? blink : 0);
if (edit && s_editMode)
if (edit && s_editMode>0)
CHECK_INCDEC_GENVAR(event, td->studWeight, -100, 100);
edit = (sub==i && subSub==2);
lcd_putsnAtt(12*FW, y, PSTR("ch1ch2ch3ch4")+3*td->srcChn, 3, edit ? blink : 0);
if (edit && s_editMode)
if (edit && s_editMode>0)
CHECK_INCDEC_GENVAR(event, td->srcChn, 0, 3);
edit = (sub==i && subSub==3);
@ -455,7 +455,7 @@ void menuProcDiagKeys(uint8_t event)
for(uint8_t i=0; i<2; i++) {
uint8_t y = i*FH + FH;
lcd_putsn_P(14*FW, y, PSTR("RE1RE2")+3*i, 3);
lcd_outdezNAtt(18*FW, y, g_rotenc[i], LEFT|(keyState((EnumKeys)(SW_RE1+i)) ? INVERS : 0));
lcd_outdezNAtt(18*FW, y, g_rotenc[i], LEFT|(keyState((EnumKeys)(BTN_RE1+i)) ? INVERS : 0));
}
#endif

View file

@ -25,11 +25,6 @@
// MM/SD card Disk IO Support
#if defined (PCBV3)
#include "integer.h"
#include "time.h"
#include "rtc.h"
#include "ff.h"
#include "diskio.h"
time_t g_unixTime; // Global date/time register, incremented each second in per10ms()
#endif
@ -816,7 +811,7 @@ void getADC_bandgap()
{
#if defined(PCBSTD)
ADMUX=0x1E|ADC_VREF_TYPE; // Switch MUX to internal 1.22V reference
_delay_us(5); // short delay to stabilise reference voltage
_delay_us(7); // short delay to stabilise reference voltage
ADCSRA|=0x40;
while ((ADCSRA & 0x10)==0);
ADCSRA|=0x10; // grab a sample
@ -864,7 +859,6 @@ uint16_t g_LightOffCounter;
uint8_t beepAgain = 0;
uint8_t beepAgainOrig = 0;
uint8_t beepOn = false;
int16_t p1val;
inline bool checkSlaveMode()
{
@ -1695,18 +1689,6 @@ void perMain()
else
BACKLIGHT_OFF;
////////////////
// G: TODO This shouldn't be in perMain(). It should be in the same place
// all the other ADC samples happen
static int16_t p1valprev;
p1valdiff = (p1val-calibratedStick[6])/32;
if(p1valdiff) {
p1valdiff = (p1valprev-calibratedStick[6])/2;
p1val = calibratedStick[6];
}
p1valprev = calibratedStick[6];
/////////////////
g_menuStack[g_menuStackPtr](evt);
refreshDisplay();
@ -2028,30 +2010,6 @@ ISR(TIMER3_CAPT_vect) // G: High frequency noise can cause stack overflo with IS
#endif
}
#if defined (PCBV3)
/*---------------------------------------------------------*/
/* User Provided Date/Time Function for FatFs module */
/*---------------------------------------------------------*/
/* This is a real time clock service to be called from */
/* FatFs module. Any valid time must be returned even if */
/* the system does not support a real time clock. */
/* This is not required in read-only configuration. */
uint32_t get_fattime(void)
{
struct tm t;
filltm(&g_unixTime, &t); // create a struct tm date/time structure from global unix time stamp
/* Pack date and time into a DWORD variable */
return ((DWORD)(t.tm_year - 80) << 25)
| ((uint32_t)(t.tm_mon+1) << 21)
| ((uint32_t)t.tm_mday << 16)
| ((uint32_t)t.tm_hour << 11)
| ((uint32_t)t.tm_min << 5)
| ((uint32_t)t.tm_sec >> 1);
}
#endif
extern uint16_t g_timeMain;
/*
@ -2089,6 +2047,29 @@ uint16_t DEBUG2 = 0;
#endif
#if defined (PCBV3)
/*---------------------------------------------------------*/
/* User Provided Date/Time Function for FatFs module */
/*---------------------------------------------------------*/
/* This is a real time clock service to be called from */
/* FatFs module. Any valid time must be returned even if */
/* the system does not support a real time clock. */
/* This is not required in read-only configuration. */
uint32_t get_fattime(void) // TODO why not in ff.cpp?
{
struct gtm t;
filltm(&g_unixTime, &t); // create a struct tm date/time structure from global unix time stamp
/* Pack date and time into a DWORD variable */
return ((DWORD)(t.tm_year - 80) << 25)
| ((uint32_t)(t.tm_mon+1) << 21)
| ((uint32_t)t.tm_mday << 16)
| ((uint32_t)t.tm_hour << 11)
| ((uint32_t)t.tm_min << 5)
| ((uint32_t)t.tm_sec >> 1);
}
#endif
void instantTrim()
{
@ -2145,6 +2126,11 @@ void moveTrimsToOffsets() // copy state of 3 primary to subtrim
#if defined (PCBV4)
// Rotary encoder interrupts
volatile uint8_t g_rotenc[2] = {0};
#endif
#ifndef SIMU
#if defined (PCBV4)
ISR(INT2_vect)
{
uint8_t input = PIND & 0b00001100;
@ -2168,8 +2154,6 @@ ISR(INT6_vect)
}
#endif
#ifndef SIMU
extern unsigned char __bss_end ;
uint16_t stack_free()
@ -2295,13 +2279,13 @@ int main(void)
}
}
sei(); // interrupts needed for eeReadAll function (soon).
g_menuStack[0] = menuMainView;
g_menuStack[1] = menuProcModelSelect;
lcdSetRefVolt(25);
sei(); // interrupts needed for FRSKY_Init and eeReadAll.
#if defined (FRSKY)
FRSKY_Init();
#endif
@ -2341,7 +2325,7 @@ int main(void)
#if defined (PCBV3)
// Initialise global unix timestamp with current time from RTC chip on SD card interface
RTC rtc;
struct tm utm;
struct gtm utm;
rtc_gettime(&rtc);
utm.tm_year = rtc.year - 1900;
utm.tm_mon = rtc.month - 1;

View file

@ -30,7 +30,7 @@
#if defined(PCBV3)
#include "ff.h"
#include "time.h"
#include "gtime.h"
#endif
#ifdef SIMU
@ -280,6 +280,10 @@ enum EnumKeys {
TRM_RV_UP ,
TRM_RH_DWN ,
TRM_RH_UP ,
#ifdef PCBV4
BTN_RE1,
BTN_RE2,
#endif
//SW_NC ,
//SW_ON ,
SW_ThrCt ,
@ -291,8 +295,8 @@ enum EnumKeys {
SW_AileDR ,
SW_Gear ,
SW_Trainer,
SW_RE1,
SW_RE2
NUM_KEYS = SW_ThrCt
};
#define CURVE_BASE 7
@ -376,7 +380,6 @@ enum EnumKeys {
#define THRCHK_DEADBAND 16
#define SPLASH_TIMEOUT (4*100) //400 msec - 4 seconds
#define NUM_KEYS TRM_RH_UP+1
#define TRM_BASE TRM_LH_DWN
//#define _MSK_KEY_FIRST (_MSK_KEY_REPT|0x20)
@ -732,7 +735,7 @@ extern char userDataDisplayBuf[TELEM_SCREEN_BUFFER_SIZE]; // text buffer for frs
#endif
#if defined (PCBV3)
extern char g_logFilename[21]; // pers.cpp::resetTelemetry()
extern char g_logFilename[22]; // pers.cpp::resetTelemetry()
extern FATFS FATFS_Obj; // pers.cpp::resetTelemetry()
extern FIL g_oLogFile; // pers.cpp::resetTelemetry()
#endif

485
src/gtime.cpp Normal file
View file

@ -0,0 +1,485 @@
/*
* Authors (alphabetical order)
* - Bertrand Songis <bsongis@gmail.com>
* - Bryan J. Rentoul (Gruvin) <gruvin@gmail.com>
* - Cameron Weeks <th9xer@gmail.com>
*
* gruvin9x is based on code named er9x by
* Author - Erez Raviv <erezraviv@gmail.com>, which is in turn
* was based on the original (and ongoing) project by Thomas Husterer,
* th9x -- http://code.google.com/p/th9x/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*
*/
#include "gtime.h"
#define LEAP_SECONDS_POSSIBLE 0
/* Shift A right by B bits portably, by dividing A by 2**B and
truncating towards minus infinity. A and B should be free of side
effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
INT_BITS is the number of useful bits in an int. GNU code can
assume that INT_BITS is at least 32.
ISO C99 says that A >> B is implementation-defined if A < 0. Some
implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
right in the usual way when A < 0, so SHR falls back on division if
ordinary A >> B doesn't seem to be the usual signed shift. */
#define SHR(a, b) \
(-1 >> 1 == -1 \
? (a) >> (b) \
: (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
/* The extra casts in the following macros work around compiler bugs,
e.g., in Cray C 5.0.3.0. */
/* True if the arithmetic type T is an integer type. bool counts as
an integer. */
#define TYPE_IS_INTEGER(t) ((t) 1.5 == 1)
/* True if negative values of the signed integer type T use two's
complement, ones' complement, or signed magnitude representation,
respectively. Much GNU code assumes two's complement, but some
people like to be portable to all possible C hosts. */
#define TYPE_TWOS_COMPLEMENT(t) ((t) ~ (t) 0 == (t) -1)
#define TYPE_ONES_COMPLEMENT(t) ((t) ~ (t) 0 == 0)
#define TYPE_SIGNED_MAGNITUDE(t) ((t) ~ (t) 0 < (t) -1)
/* True if the arithmetic type T is signed. */
#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
/* The maximum and minimum values for the integer type T. These
macros have undefined behavior if T is signed and has padding bits.
If this is a problem for you, please let us know how to fix it for
your host. */
#define TYPE_MINIMUM(t) \
((t) (! TYPE_SIGNED (t) \
? (t) 0 \
: TYPE_SIGNED_MAGNITUDE (t) \
? ~ (t) 0 \
: ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1)))
#define TYPE_MAXIMUM(t) \
((t) (! TYPE_SIGNED (t) \
? (t) -1 \
: ~ (~ (t) 0 << (sizeof (t) * CHAR_BIT - 1))))
#ifndef TIME_T_MIN
# define TIME_T_MIN TYPE_MINIMUM (time_t)
#endif
#ifndef TIME_T_MAX
# define TIME_T_MAX TYPE_MAXIMUM (time_t)
#endif
#define TIME_T_MIDPOINT (SHR (TIME_T_MIN + TIME_T_MAX, 1) + 1)
/* Verify a requirement at compile-time (unlike assert, which is runtime). */
#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; }
verify (time_t_is_integer, TYPE_IS_INTEGER (time_t));
verify (twos_complement_arithmetic, TYPE_TWOS_COMPLEMENT (int));
/* The code also assumes that signed integer overflow silently wraps
around, but this assumption can't be stated without causing a
diagnostic on some hosts. */
#define EPOCH_YEAR 1970
#define TM_YEAR_BASE 1900
verify (base_year_is_a_multiple_of_100, TM_YEAR_BASE % 100 == 0);
#define SECS_PER_HOUR 3600ul
#define SECS_PER_DAY 86400ul
#define EOVERFLOW 0
/* Return 1 if YEAR + TM_YEAR_BASE is a leap year. */
static inline int
leapyear (long int year)
{
/* Don't add YEAR to TM_YEAR_BASE, as that might overflow.
Also, work even if YEAR is negative. */
return
((year & 3) == 0
&& (year % 100 != 0
|| ((year / 100) & 3) == (- (TM_YEAR_BASE / 100) & 3)));
}
const unsigned short int __mon_yday[2][13] =
{
/* Normal years. */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
/* Leap years. */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};
/* Compute the `struct tm' representation of *T,
offset OFFSET seconds east of UTC,
and store year, yday, mon, mday, wday, hour, min, sec into *TP.
Return nonzero if successful. */
int
__offtime (
time_t *t,
long int offset,
struct gtm *tp)
{
long int days, rem, y;
const unsigned short int *ip;
days = *t / SECS_PER_DAY;
rem = *t % SECS_PER_DAY;
rem += offset;
while (rem < 0)
{
rem += SECS_PER_DAY;
--days;
}
while (rem >= SECS_PER_DAY)
{
rem -= SECS_PER_DAY;
++days;
}
tp->tm_hour = rem / SECS_PER_HOUR;
rem %= SECS_PER_HOUR;
tp->tm_min = rem / 60;
tp->tm_sec = rem % 60;
/* January 1, 1970 was a Thursday. */
tp->tm_wday = (4 + days) % 7;
if (tp->tm_wday < 0)
tp->tm_wday += 7;
y = 1970;
#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
#define LEAPS_THRU_END_OF(y) (DIV (y, 4) - DIV (y, 100) + DIV (y, 400))
while (days < 0 || days >= (leapyear (y) ? 366 : 365))
{
/* Guess a corrected year, assuming 365 days per year. */
long int yg = y + days / 365 - (days % 365 < 0);
/* Adjust DAYS and Y to match the guessed year. */
days -= ((yg - y) * 365
+ LEAPS_THRU_END_OF (yg - 1)
- LEAPS_THRU_END_OF (y - 1));
y = yg;
}
tp->tm_year = y - 1900;
if (tp->tm_year != y - 1900)
{
/* The year cannot be represented due to overflow. */
// __set_errno (EOVERFLOW);
return 0;
}
tp->tm_yday = days;
ip = __mon_yday[leapyear(y)];
for (y = 11; days < (long int) ip[y]; --y)
continue;
days -= ip[y];
tp->tm_mon = y;
tp->tm_mday = days + 1;
return 1;
}
/* time_r function implementations */
// G: No time zones in our implementation so just do the converion from time_t to struct tm
struct gtm *
__localtime_r (time_t * t, struct gtm * tp)
{
__offtime(t, 0, tp);
return tp;
}
/* Return an integer value measuring (YEAR1-YDAY1 HOUR1:MIN1:SEC1) -
(YEAR0-YDAY0 HOUR0:MIN0:SEC0) in seconds, assuming that the clocks
were not adjusted between the time stamps.
The YEAR values uses the same numbering as TP->tm_year. Values
need not be in the usual range. However, YEAR1 must not be less
than 2 * INT_MIN or greater than 2 * INT_MAX.
The result may overflow. It is the caller's responsibility to
detect overflow. */
static inline time_t
ydhms_diff (long int year1, long int yday1, int hour1, int min1, int sec1,
int year0, int yday0, int hour0, int min0, int sec0)
{
verify (C99_integer_division, -1 / 2 == 0);
verify (long_int_year_and_yday_are_wide_enough,
INT_MAX <= LONG_MAX / 2 || TIME_T_MAX <= UINT_MAX);
/* Compute intervening leap days correctly even if year is negative.
Take care to avoid integer overflow here. */
int a4 = SHR (year1, 2) + SHR (TM_YEAR_BASE, 2) - ! (year1 & 3);
int b4 = SHR (year0, 2) + SHR (TM_YEAR_BASE, 2) - ! (year0 & 3);
int a100 = a4 / 25 - (a4 % 25 < 0);
int b100 = b4 / 25 - (b4 % 25 < 0);
int a400 = SHR (a100, 2);
int b400 = SHR (b100, 2);
int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
/* Compute the desired time in time_t precision. Overflow might
occur here. */
time_t tyear1 = year1;
time_t years = tyear1 - year0;
time_t days = 365 * years + yday1 - yday0 + intervening_leap_days;
time_t hours = 24 * days + hour1 - hour0;
time_t minutes = 60 * hours + min1 - min0;
time_t seconds = 60 * minutes + sec1 - sec0;
return seconds;
}
/* Return a time_t value corresponding to (YEAR-YDAY HOUR:MIN:SEC),
assuming that *T corresponds to *TP and that no clock adjustments
occurred between *TP and the desired time.
If TP is null, return a value not equal to *T; this avoids false matches.
If overflow occurs, yield the minimal or maximal value, except do not
yield a value equal to *T. */
static time_t
guess_time_tm (long int year, long int yday, int hour, int min, int sec,
time_t *t, struct gtm *tp)
{
if (tp)
{
time_t d = ydhms_diff (year, yday, hour, min, sec,
tp->tm_year, tp->tm_yday,
tp->tm_hour, tp->tm_min, tp->tm_sec);
time_t t1 = *t + d;
if ((t1 < *t) == (TYPE_SIGNED (time_t) ? d < 0 : TIME_T_MAX / 2 < d))
return t1;
}
/* Overflow occurred one way or another. Return the nearest result
that is actually in range, except don't report a zero difference
if the actual difference is nonzero, as that would cause a false
match; and don't oscillate between two values, as that would
confuse the spring-forward gap detector. */
return (*t < TIME_T_MIDPOINT
? (*t <= TIME_T_MIN + 1 ? *t + 1 : TIME_T_MIN)
: (TIME_T_MAX - 1 <= *t ? *t - 1 : TIME_T_MAX));
}
/* Use CONVERT to convert *T to a broken down time in *TP.
If *T is out of range for conversion, adjust it so that
it is the nearest in-range value and then convert that. */
static struct gtm *
ranged_convert (struct gtm *(*convert) (time_t *, struct gtm *),
time_t *t, struct gtm *tp)
{
struct gtm *r = convert (t, tp);
if (!r && *t)
{
time_t bad = *t;
time_t ok = 0;
/* BAD is a known unconvertible time_t, and OK is a known good one.
Use binary search to narrow the range between BAD and OK until
they differ by 1. */
while (bad != ok + (bad < 0 ? -1 : 1))
{
time_t mid = *t = (bad < 0
? bad + ((ok - bad) >> 1)
: ok + ((bad - ok) >> 1));
r = convert (t, tp);
if (r)
ok = mid;
else
bad = mid;
}
if (!r && ok)
{
/* The last conversion attempt failed;
revert to the most recent successful attempt. */
*t = ok;
r = convert (t, tp);
}
}
return r;
}
/* Convert *TP to a time_t value, inverting
the monotonic and mostly-unit-linear conversion function CONVERT.
Use *OFFSET to keep track of a guess at the offset of the result,
compared to what the result would be for UTC without leap seconds.
If *OFFSET's guess is correct, only one CONVERT call is needed.
This function is external because it is used also by timegm.c. */
time_t
__mktime_internal (struct gtm *tp,
struct gtm *(*convert) (time_t *, struct gtm *),
time_t *offset)
{
time_t t, gt, t0, t1, t2;
struct gtm tm;
/* The maximum number of probes (calls to CONVERT) should be enough
to handle any combinations of time zone rule changes, solar time,
leap seconds, and oscillations around a spring-forward gap.
POSIX.1 prohibits leap seconds, but some hosts have them anyway. */
int remaining_probes = 6;
/* Time requested. Copy it in case CONVERT modifies *TP; this can
occur if TP is localtime's returned value and CONVERT is localtime. */
int sec = tp->tm_sec;
int min = tp->tm_min;
int hour = tp->tm_hour;
int mday = tp->tm_mday;
int mon = tp->tm_mon;
int year_requested = tp->tm_year;
/* Ensure that mon is in range, and set year accordingly. */
int mon_remainder = mon % 12;
int negative_mon_remainder = mon_remainder < 0;
int mon_years = mon / 12 - negative_mon_remainder;
long int lyear_requested = year_requested;
long int year = lyear_requested + mon_years;
/* The other values need not be in range:
the remaining code handles minor overflows correctly,
assuming int and time_t arithmetic wraps around.
Major overflows are caught at the end. */
/* Calculate day of year from year, month, and day of month.
The result need not be in range. */
int mon_yday = ((__mon_yday[leapyear (year)]
[mon_remainder + 12 * negative_mon_remainder])
- 1);
long int lmday = mday;
long int yday = mon_yday + lmday;
time_t guessed_offset = *offset;
int sec_requested = sec;
/*
if (LEAP_SECONDS_POSSIBLE)
{
// Handle out-of-range seconds specially,
// since ydhms_tm_diff assumes every minute has 60 seconds.
if (sec < 0)
sec = 0;
if (59 < sec)
sec = 59;
}
*/
/* Invert CONVERT by probing. First assume the same offset as last
time. */
t0 = ydhms_diff (year, yday, hour, min, sec,
EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, - guessed_offset);
if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
{
/* time_t isn't large enough to rule out overflows, so check
for major overflows. A gross check suffices, since if t0
has overflowed, it is off by a multiple of TIME_T_MAX -
TIME_T_MIN + 1. So ignore any component of the difference
that is bounded by a small value. */
/* Approximate log base 2 of the number of time units per
biennium. A biennium is 2 years; use this unit instead of
years to avoid integer overflow. For example, 2 average
Gregorian years are 2 * 365.2425 * 24 * 60 * 60 seconds,
which is 63113904 seconds, and rint (log2 (63113904)) is
26. */
int ALOG2_SECONDS_PER_BIENNIUM = 26;
int ALOG2_MINUTES_PER_BIENNIUM = 20;
int ALOG2_HOURS_PER_BIENNIUM = 14;
int ALOG2_DAYS_PER_BIENNIUM = 10;
int LOG2_YEARS_PER_BIENNIUM = 1;
int approx_requested_biennia =
(SHR (year_requested, LOG2_YEARS_PER_BIENNIUM)
- SHR (EPOCH_YEAR - TM_YEAR_BASE, LOG2_YEARS_PER_BIENNIUM)
+ SHR (mday, ALOG2_DAYS_PER_BIENNIUM)
+ SHR (hour, ALOG2_HOURS_PER_BIENNIUM)
+ SHR (min, ALOG2_MINUTES_PER_BIENNIUM)
+ (LEAP_SECONDS_POSSIBLE
? 0
: SHR (sec, ALOG2_SECONDS_PER_BIENNIUM)));
int approx_biennia = SHR (t0, ALOG2_SECONDS_PER_BIENNIUM);
int diff = approx_biennia - approx_requested_biennia;
int abs_diff = diff < 0 ? - diff : diff;
/* IRIX 4.0.5 cc miscalculates TIME_T_MIN / 3: it erroneously
gives a positive value of 715827882. Setting a variable
first then doing math on it seems to work.
(ghazi@caip.rutgers.edu) */
time_t time_t_max = TIME_T_MAX;
time_t time_t_min = TIME_T_MIN;
time_t overflow_threshold =
(time_t_max / 3 - time_t_min / 3) >> ALOG2_SECONDS_PER_BIENNIUM;
if (overflow_threshold < abs_diff)
{
/* Overflow occurred. Try repairing it; this might work if
the time zone offset is enough to undo the overflow. */
time_t repaired_t0 = -1 - t0;
approx_biennia = SHR (repaired_t0, ALOG2_SECONDS_PER_BIENNIUM);
diff = approx_biennia - approx_requested_biennia;
abs_diff = diff < 0 ? - diff : diff;
if (overflow_threshold < abs_diff)
return -1;
guessed_offset += repaired_t0 - t0;
t0 = repaired_t0;
}
}
/* Repeatedly use the error to improve the guess. */
for (t = t1 = t2 = t0;
(gt = guess_time_tm (year, yday, hour, min, sec, &t,
ranged_convert (convert, &t, &tm)),
t != gt);
t1 = t2, t2 = t, t = gt)
if (t == t1 && t != t2)
goto offset_found;
else if (--remaining_probes == 0)
return -1;
offset_found:
*offset = guessed_offset + t - t0;
if (LEAP_SECONDS_POSSIBLE && sec_requested != tm.tm_sec)
{
/* Adjust time to reflect the tm_sec requested, not the normalized value.
Also, repair any damage from a false match due to a leap second. */
int sec_adjustment = (sec == 0 && tm.tm_sec == 60) - sec;
t1 = t + sec_requested;
t2 = t1 + sec_adjustment;
if (((t1 < t) != (sec_requested < 0))
| ((t2 < t1) != (sec_adjustment < 0))
| ! convert (&t2, &tm))
return -1;
t = t2;
}
*tp = tm;
return t;
}
/* Convert *TP to a time_t value. */
time_t
mktime (struct gtm *tp)
{
// no time zone stuff. Just do the math ;)
static time_t localtime_offset;
return __mktime_internal (tp, __localtime_r, &localtime_offset);
}
/* Fill a (struct tm) TP* from a given time_t time stamp */
time_t
filltm(time_t *t, struct gtm *tp)
{
return __offtime(t, 0, tp);
}

55
src/gtime.h Normal file
View file

@ -0,0 +1,55 @@
/*
* Authors (alphabetical order)
* - Bertrand Songis <bsongis@gmail.com>
* - Bryan J. Rentoul (Gruvin) <gruvin@gmail.com>
*
* gruvin9x is based on code named er9x by
* Author - Erez Raviv <erezraviv@gmail.com>, which is in turn
* was based on the original (and ongoing) project by Thomas Husterer,
* th9x -- http://code.google.com/p/th9x/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*
*/
#ifndef g9x_time_h
#define g9x_time_h
#include <inttypes.h>
#define CHAR_BIT 8
#define INT_MAX 32767
#define INT_MIN -32767
#define LONG_MAX 0x7FFFFFFFL
#define LONG_MIN ((long) 0x80000000L)
#define UINT_MAX 0xFFFFU/0xFFFFFFFFUL
#define ULONG_MAX 0xFFFFFFFFUL
typedef long int time_t;
struct gtm
{
int8_t tm_sec; /* Seconds. [0-60] (1 leap second) */
int8_t tm_min; /* Minutes. [0-59] */
int8_t tm_hour; /* Hours. [0-23] */
int8_t tm_mday; /* Day. [1-31] */
int8_t tm_mon; /* Month. [0-11] */
int8_t tm_year; /* Year - 1900. Limited to the year 2115. Oh no! :P */
int8_t tm_wday; /* Day of week. [0-6] */
int16_t tm_yday; /* Day of year. [0-365] Needed internally for calculations */
};
extern const unsigned short int __mon_yday[2][13];
extern time_t mktime(struct gtm *tp);
extern time_t filltm(time_t *t, struct gtm *tp);
#endif

View file

@ -27,11 +27,11 @@
int16_t calibratedStick[NUM_STICKS+NUM_POTS];
int16_t ex_chans[NUM_CHNOUT]; // Outputs + intermidiates
uint8_t s_pgOfs;
uint8_t s_editMode;
int8_t s_editMode;
uint8_t s_noHi;
uint8_t s_noScroll;
int16_t g_chans512[NUM_CHNOUT];
int16_t g_chans512[NUM_CHNOUT]; // TODO not here!
void menu_lcd_onoff( uint8_t x,uint8_t y, uint8_t value, uint8_t mode )
{
@ -50,7 +50,10 @@ void DisplayScreenIndex(uint8_t index, uint8_t count, uint8_t attr)
lcd_outdezAtt(1+128-FW*(count>9 ? 3 : 2),0,index+1,attr);
}
#if defined(NAVIGATION_POT1) || defined(NAVIGATION_RE1)
int16_t p1valdiff;
#endif
int8_t checkIncDec_Ret;
int16_t checkIncDec(uint8_t event, int16_t val, int16_t i_min, int16_t i_max, uint8_t i_flags)
{
@ -63,7 +66,7 @@ int16_t checkIncDec(uint8_t event, int16_t val, int16_t i_min, int16_t i_max, ui
kmi=hlp;
event=EVT_KEY_FIRST(EVT_KEY_MASK & event);
}
if(event==EVT_KEY_FIRST(kpl) || event== EVT_KEY_REPT(kpl) || (s_editMode && (event==EVT_KEY_FIRST(KEY_UP) || event== EVT_KEY_REPT(KEY_UP))) ) {
if(event==EVT_KEY_FIRST(kpl) || event== EVT_KEY_REPT(kpl) || (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_UP) || event== EVT_KEY_REPT(KEY_UP))) ) {
newval++;
#if defined (BEEPSPKR)
beepKeySpkr(BEEP_KEY_UP_FREQ);
@ -71,7 +74,7 @@ int16_t checkIncDec(uint8_t event, int16_t val, int16_t i_min, int16_t i_max, ui
beepKey();
#endif
kother=kmi;
}else if(event==EVT_KEY_FIRST(kmi) || event== EVT_KEY_REPT(kmi) || (s_editMode && (event==EVT_KEY_FIRST(KEY_DOWN) || event== EVT_KEY_REPT(KEY_DOWN))) ) {
}else if(event==EVT_KEY_FIRST(kmi) || event== EVT_KEY_REPT(kmi) || (s_editMode>0 && (event==EVT_KEY_FIRST(KEY_DOWN) || event== EVT_KEY_REPT(KEY_DOWN))) ) {
newval--;
#if defined (BEEPSPKR)
beepKeySpkr(BEEP_KEY_DOWN_FREQ);
@ -86,13 +89,15 @@ int16_t checkIncDec(uint8_t event, int16_t val, int16_t i_min, int16_t i_max, ui
killEvents(kpl);
}
if(i_min==0 && i_max==1 && event==EVT_KEY_FIRST(KEY_MENU)) {
s_editMode = false;
s_editMode = 0;
newval=!val;
killEvents(event);
}
#if defined(NAVIGATION_POT1) || defined (NAVIGATION_RE1)
//change values based on P1
newval -= p1valdiff;
#endif
if(newval > i_max)
{
@ -155,29 +160,92 @@ bool check_submenu_simple(uint8_t event, uint8_t maxrow)
return check_simple(event, 0, 0, 0, maxrow);
}
#define SCROLL_TH 64
#define SCROLL_POT1_TH 32
#define MAXCOL(row) (horTab ? pgm_read_byte(horTab+min(row, horTabMax)) : (const uint8_t)0)
#define INC(val,max) if(val<max) {val++;} else {val=0;}
#define DEC(val,max) if(val>0 ) {val--;} else {val=max;}
bool check(uint8_t event, uint8_t curr, MenuFuncP *menuTab, uint8_t menuTabSize, prog_uint8_t *horTab, uint8_t horTabMax, uint8_t maxrow)
{
#ifdef NAVIGATION_RE1
// check rotary encoder 1 if changed -> cursor down/up
static int16_t re1valprev;
p1valdiff = 0;
int8_t scrollRE = re1valprev - g_rotenc[0];
if (scrollRE) {
re1valprev = g_rotenc[0];
if (s_editMode > 0) {
p1valdiff = scrollRE;
scrollRE = 0;
}
}
if (event == EVT_KEY_FIRST(BTN_RE1)) {
scrollRE = 0;
if (s_editMode++ > 0) s_editMode = 0;
if (s_editMode > 0 && m_posVert == 0) s_editMode = -1;
}
#else
#define scrollRE 0
#endif
#ifdef NAVIGATION_POT1
// check pot 1 - if changed -> scroll values
static int16_t p1valprev;
p1valdiff = (p1valprev-calibratedStick[6]) / SCROLL_POT1_TH;
if (p1valdiff) p1valprev = calibratedStick[6];
#endif
#ifdef NAVIGATION_POT2
// check pot 2 - if changed -> scroll menu
static int16_t p2valprev;
int8_t scrollLR = (p2valprev-calibratedStick[4]) / SCROLL_TH;
if (scrollLR) p2valprev = calibratedStick[4];
#else
#define scrollLR 0
#endif
#ifdef NAVIGATION_POT3
// check pot 3 if changed -> cursor down/up
static int16_t p3valprev;
int8_t scrollUD = (p3valprev-calibratedStick[5]) / SCROLL_TH;
if (scrollUD) p3valprev = calibratedStick[5];
#else
#define scrollUD 0
#endif
if(scrollLR || scrollUD || p1valdiff) g_LightOffCounter = g_eeGeneral.lightAutoOff*500; // on keypress turn the light on 5*100
if (menuTab) {
uint8_t attr = 0;
if (m_posVert==0 && !s_noScroll) {
attr = INVERS;
switch(event)
{
int8_t cc = curr;
if (scrollLR || (scrollRE && s_editMode < 0)) {
cc = limit((int8_t)0, (int8_t)(curr - scrollLR - scrollRE), (int8_t)(menuTabSize-1));
}
switch(event) {
case EVT_KEY_FIRST(KEY_LEFT):
if(curr>0)
chainMenu((MenuFuncP)pgm_read_adr(&menuTab[curr-1]));
if (curr > 0)
cc = curr - 1;
else
chainMenu((MenuFuncP)pgm_read_adr(&menuTab[menuTabSize-1]));
return false;
cc = menuTabSize-1;
break;
case EVT_KEY_FIRST(KEY_RIGHT):
if(curr < (menuTabSize-1))
chainMenu((MenuFuncP)pgm_read_adr(&menuTab[curr+1]));
if (curr < (menuTabSize-1))
cc = curr + 1;
else
chainMenu((MenuFuncP)pgm_read_adr(&menuTab[0]));
cc = 0;
break;
}
if (cc != curr) {
chainMenu((MenuFuncP)pgm_read_adr(&menuTab[cc]));
return false;
}
}
@ -188,23 +256,67 @@ bool check(uint8_t event, uint8_t curr, MenuFuncP *menuTab, uint8_t menuTabSize,
theFile.DisplayProgressBar(menuTab ? lcd_lastPos-2*FW-((curr+1)/10*FWNUM)-2 : 20*FW+1);
uint8_t maxcol = MAXCOL(m_posVert);
if (s_editMode<=0) {
if (scrollUD) {
m_posVert = limit((int8_t)0, (int8_t)(m_posVert - scrollUD), (int8_t)maxrow);
m_posHorz = min(m_posHorz, MAXCOL(m_posVert));
BLINK_SYNC;
}
if (scrollLR && m_posVert>0) {
m_posHorz = limit((int8_t)0, (int8_t)(m_posHorz - scrollLR), (int8_t)maxcol);
BLINK_SYNC;
}
#ifdef NAVIGATION_RE1
while (scrollRE) {
if (scrollRE > 0) {
--scrollRE;
if (++m_posHorz > MAXCOL(m_posVert)) {
m_posHorz = 0;
if (++m_posVert > maxrow) {
m_posVert = maxrow;
m_posHorz = MAXCOL(m_posVert);
scrollRE = 0;
}
}
}
else {
++scrollRE;
if (m_posHorz-- == 0) {
if (m_posVert-- == 0) {
m_posVert = 0;
m_posHorz = 0;
scrollRE = 0;
}
else {
m_posHorz = MAXCOL(m_posVert);
}
}
}
}
#endif
}
switch(event)
{
case EVT_ENTRY:
minit();
s_editMode = false;
s_editMode = -1;
break;
case EVT_KEY_FIRST(KEY_MENU):
if (maxcol > 0)
s_editMode = !s_editMode;
s_editMode = (s_editMode<=0);
break;
case EVT_KEY_LONG(KEY_EXIT):
s_editMode = false;
s_editMode = 0;
popMenu();
break;
case EVT_KEY_BREAK(KEY_EXIT):
if(s_editMode) {
s_editMode = false;
if(s_editMode>0) {
s_editMode = 0;
break;
}
if(m_posVert==0 || !menuTab) {
@ -219,7 +331,7 @@ bool check(uint8_t event, uint8_t curr, MenuFuncP *menuTab, uint8_t menuTabSize,
case EVT_KEY_REPT(KEY_RIGHT): //inc
if(m_posHorz==maxcol) break;
case EVT_KEY_FIRST(KEY_RIGHT)://inc
if(!horTab || s_editMode)break;
if(!horTab || s_editMode>0)break;
INC(m_posHorz,maxcol);
BLINK_SYNC;
break;
@ -227,7 +339,7 @@ bool check(uint8_t event, uint8_t curr, MenuFuncP *menuTab, uint8_t menuTabSize,
case EVT_KEY_REPT(KEY_LEFT): //dec
if(m_posHorz==0) break;
case EVT_KEY_FIRST(KEY_LEFT)://dec
if(!horTab || s_editMode)break;
if(!horTab || s_editMode>0)break;
DEC(m_posHorz,maxcol);
BLINK_SYNC;
break;
@ -235,7 +347,7 @@ bool check(uint8_t event, uint8_t curr, MenuFuncP *menuTab, uint8_t menuTabSize,
case EVT_KEY_REPT(KEY_DOWN): //inc
if(m_posVert==maxrow) break;
case EVT_KEY_FIRST(KEY_DOWN): //inc
if(s_editMode)break;
if(s_editMode>0)break;
do {
INC(m_posVert,maxrow);
} while(MAXCOL(m_posVert) == (uint8_t)-1);
@ -246,7 +358,7 @@ bool check(uint8_t event, uint8_t curr, MenuFuncP *menuTab, uint8_t menuTabSize,
case EVT_KEY_REPT(KEY_UP): //dec
if(m_posVert==0) break;
case EVT_KEY_FIRST(KEY_UP): //dec
if(s_editMode)break;
if(s_editMode>0)break;
do {
DEC(m_posVert,maxrow);
} while(MAXCOL(m_posVert) == (uint8_t)-1);

View file

@ -69,9 +69,14 @@ void menuProcModelSelect(uint8_t event);
void menuProcStatistic(uint8_t event);
void menuProcStatistic2(uint8_t event);
#if defined(NAVIGATION_POT1) || defined(NAVIGATION_RE1)
extern int16_t p1valdiff;
#else
#define p1valdiff 0
#endif
extern int8_t checkIncDec_Ret; // global helper vars
extern uint8_t s_editMode; // global editmode
extern int8_t s_editMode; // global editmode
int16_t checkIncDec(uint8_t event, int16_t i_pval, int16_t i_min, int16_t i_max, uint8_t i_flags);
int8_t checkIncDecModel(uint8_t event, int8_t i_val, int8_t i_min, int8_t i_max);

View file

@ -345,7 +345,7 @@ void menuProcModelSelect(uint8_t event)
void EditName(uint8_t x, uint8_t y, char *name, uint8_t size, uint8_t event, bool active, uint8_t & cur)
{
if (active) {
if (s_editMode) {
if (s_editMode>0) {
switch(event) {
case EVT_KEY_BREAK(KEY_LEFT):
if (cur>0) cur--;
@ -360,8 +360,8 @@ void EditName(uint8_t x, uint8_t y, char *name, uint8_t size, uint8_t event, boo
}
}
lcd_putsnAtt(x, y, name, size, ZCHAR | (active ? (s_editMode ? 0 : INVERS) : 0));
if (active && s_editMode) {
lcd_putsnAtt(x, y, name, size, ZCHAR | (active ? ((s_editMode>0) ? 0 : INVERS) : 0));
if (active && s_editMode>0) {
char c = name[cur];
char v = c;
if (p1valdiff || event==EVT_KEY_FIRST(KEY_DOWN) || event==EVT_KEY_FIRST(KEY_UP)
@ -402,12 +402,12 @@ void menuProcModel(uint8_t event)
for (uint8_t i=0; i<2; i++, timer=&g_model.timer2) {
if (s_pgOfs<subN) {
lcd_putsnAtt(0*FW, y, PSTR("Timer1Timer2")+6*i, 6, 0);
putsTmrMode(PARAM_OFS, y, timer->mode, sub==subN && m_posHorz==0 ? (s_editMode ? BLINK : INVERS) : 0);
putsTmrMode(PARAM_OFS, y, timer->mode, sub==subN && m_posHorz==0 ? ((s_editMode>0) ? BLINK : INVERS) : 0);
putsTime(14*FW-3, y, timer->val,
(sub==subN && m_posHorz==1 ? (s_editMode ? BLINK : INVERS):0),
(sub==subN && m_posHorz==2 ? (s_editMode ? BLINK : INVERS):0) );
lcd_putsnAtt(19*FW, y, PSTR("\x7e\x7f")+1-timer->dir,1,sub==subN && m_posHorz==3 ? (s_editMode ? BLINK : INVERS) : 0);
if (sub==subN && (s_editMode || p1valdiff)) {
(sub==subN && m_posHorz==1 ? ((s_editMode>0) ? BLINK : INVERS):0),
(sub==subN && m_posHorz==2 ? ((s_editMode>0) ? BLINK : INVERS):0) );
lcd_putsnAtt(19*FW, y, PSTR("\x7e\x7f")+1-timer->dir,1,sub==subN && m_posHorz==3 ? ((s_editMode>0) ? BLINK : INVERS) : 0);
if (sub==subN && (s_editMode>0 || p1valdiff)) {
switch (m_posHorz) {
case 0:
CHECK_INCDEC_MODELVAR(event, timer->mode, -(13+2*MAX_SWITCH),(13+2*MAX_SWITCH));
@ -461,7 +461,7 @@ void menuProcModel(uint8_t event)
lcd_puts_P( 0, y, PSTR("T-Trim"));
menu_lcd_onoff(PARAM_OFS, y, g_model.thrTrim, sub==subN && m_posHorz==0) ;
lcd_putsnAtt(PARAM_OFS+4*FW, y, PSTR("LinearExp ")+6*g_model.thrExpo,6,(sub==subN && m_posHorz==1) ? INVERS : 0);
if (sub==subN && (s_editMode || p1valdiff)) {
if (sub==subN && (s_editMode>0 || p1valdiff)) {
switch (m_posHorz) {
case 0:
CHECK_INCDEC_MODELVAR(event,g_model.thrTrim,0,1);
@ -480,7 +480,7 @@ void menuProcModel(uint8_t event)
if(sub==subN){
if((event==EVT_KEY_FIRST(KEY_MENU)) || p1valdiff) {
killEvents(event);
s_editMode = false;
s_editMode = 0;
g_model.beepANACenter ^= (1<<m_posHorz);
STORE_MODELVARS;
}
@ -491,16 +491,16 @@ void menuProcModel(uint8_t event)
if(s_pgOfs<subN) {
lcd_puts_P(0, y, PSTR("Proto"));
lcd_putsnAtt(PARAM_OFS, y, PSTR(PROT_STR)+PROT_STR_LEN*g_model.protocol,PROT_STR_LEN,
(sub==subN && m_posHorz==0 ? (s_editMode ? BLINK : INVERS):0));
(sub==subN && m_posHorz==0 ? (s_editMode>0 ? BLINK : INVERS):0));
if(!g_model.protocol) {
lcd_putsnAtt(PARAM_OFS+4*FW, y, PSTR("4CH 6CH 8CH 10CH12CH14CH16CH")+4*(g_model.ppmNCH+2),4,(sub==subN && m_posHorz==1 ? (s_editMode ? BLINK : INVERS):0));
lcd_putsnAtt(PARAM_OFS+4*FW, y, PSTR("4CH 6CH 8CH 10CH12CH14CH16CH")+4*(g_model.ppmNCH+2),4,(sub==subN && m_posHorz==1 ? ((s_editMode>0) ? BLINK : INVERS):0));
lcd_putsAtt(PARAM_OFS+11*FW, y, PSTR("u"),0);
lcd_outdezAtt(PARAM_OFS+11*FW, y, (g_model.ppmDelay*50)+300, (sub==subN && m_posHorz==2 ? (s_editMode ? BLINK : INVERS):0));
lcd_outdezAtt(PARAM_OFS+11*FW, y, (g_model.ppmDelay*50)+300, (sub==subN && m_posHorz==2 ? ((s_editMode>0) ? BLINK : INVERS):0));
}
else if (sub==subN) {
m_posHorz = 0;
}
if (sub==subN && (s_editMode || p1valdiff || !g_model.protocol)) {
if (sub==subN && (s_editMode>0 || p1valdiff || !g_model.protocol)) {
switch (m_posHorz) {
case 0:
CHECK_INCDEC_MODELVAR(event,g_model.protocol,0,PROT_MAX);
@ -521,8 +521,8 @@ void menuProcModel(uint8_t event)
lcd_puts_P(0, y, PSTR("PPM frame"));
lcd_puts_P(PARAM_OFS+3*FW, y, PSTR("ms"));
lcd_outdezAtt(PARAM_OFS, y, (int16_t)g_model.ppmFrameLength*5 + 225, ((sub==subN && m_posHorz==0) ? INVERS:0) | PREC1|LEFT);
lcd_putsnAtt(PARAM_OFS+6*FW, y, PSTR("POSNEG")+3*g_model.pulsePol,3,(sub==subN && m_posHorz==1 ? (s_editMode ? BLINK : INVERS):0));
if(sub==subN && (s_editMode || p1valdiff)) {
lcd_putsnAtt(PARAM_OFS+6*FW, y, PSTR("POSNEG")+3*g_model.pulsePol,3,(sub==subN && m_posHorz==1 ? ((s_editMode>0) ? BLINK : INVERS):0));
if(sub==subN && (s_editMode>0 || p1valdiff)) {
switch (m_posHorz) {
case 0:
CHECK_INCDEC_MODELVAR(event, g_model.ppmFrameLength, -20, 20);
@ -570,13 +570,13 @@ void menuProcPhaseOne(uint8_t event)
if (v > TRIM_EXTENDED_MAX) {
uint8_t p = v - TRIM_EXTENDED_MAX - 1;
if (p >= s_currIdx) p++;
lcd_putcAtt((10+t)*FW, y, '0'+p, (attr && m_posHorz==t) ? (s_editMode ? BLINK : INVERS) : 0);
lcd_putcAtt((10+t)*FW, y, '0'+p, (attr && m_posHorz==t) ? ((s_editMode>0) ? BLINK : INVERS) : 0);
}
else {
v = TRIM_EXTENDED_MAX;
putsChnLetter((10+t)*FW, y, t+1, (attr && m_posHorz==t) ? (s_editMode ? BLINK : INVERS) : 0);
putsChnLetter((10+t)*FW, y, t+1, (attr && m_posHorz==t) ? ((s_editMode>0) ? BLINK : INVERS) : 0);
}
if (attr && m_posHorz==t && (s_editMode || p1valdiff)) {
if (attr && m_posHorz==t && ((s_editMode>0) || p1valdiff)) {
v = checkIncDec(event, v, TRIM_EXTENDED_MAX, TRIM_EXTENDED_MAX+MAX_PHASES-1, EE_MODEL);
if (checkIncDec_Ret) {
if (v == TRIM_EXTENDED_MAX) v = 0;
@ -763,10 +763,10 @@ void menuProcCurveOne(uint8_t event)
autoThrStep = 0;
break;
case EVT_KEY_FIRST(KEY_MENU):
if (!s_editMode) {
if (s_editMode<=0) {
switch (m_posHorz) {
case 0:
s_editMode = true;
s_editMode = 1;
break;
case 1:
if (++dfltCrv > 4)
@ -787,9 +787,9 @@ void menuProcCurveOne(uint8_t event)
if (autoThrStep) {
autoThrStep = 0;
}
else if (s_editMode) {
else if (s_editMode>0) {
m_posHorz = 0;
s_editMode = false;
s_editMode = 0;
}
else {
popMenu();
@ -801,7 +801,7 @@ void menuProcCurveOne(uint8_t event)
break;
case EVT_KEY_REPT(KEY_RIGHT):
case EVT_KEY_FIRST(KEY_RIGHT):
if (!autoThrStep && m_posHorz<(s_editMode ? points-1 : ((g_menuStack[g_menuStackPtr-1] == menuProcExpoOne && IS_THROTTLE(expoaddress(s_currIdx)->chn)) ? 2 : 1))) m_posHorz++;
if (!autoThrStep && m_posHorz<((s_editMode>0) ? points-1 : ((g_menuStack[g_menuStackPtr-1] == menuProcExpoOne && IS_THROTTLE(expoaddress(s_currIdx)->chn)) ? 2 : 1))) m_posHorz++;
break;
}
@ -813,14 +813,14 @@ void menuProcCurveOne(uint8_t event)
else {
x = 4*FW; y = (i+1) * FH;
}
uint8_t attr = (s_editMode && m_posHorz == i) ? INVERS : 0;
uint8_t attr = (s_editMode>0 && m_posHorz==i) ? INVERS : 0;
lcd_outdezAtt(x, y, crv[i], attr);
}
lcd_puts_P(0*FW, 7*FH, PSTR("MODE"));
lcd_putsnAtt(5*FW-2, 7*FH, PSTR("EDIT ""PRSET""A.THR")+5*(!s_editMode)*m_posHorz, 5, s_editMode || autoThrStep ? 0 : INVERS);
lcd_putsnAtt(5*FW-2, 7*FH, PSTR("EDIT ""PRSET""A.THR")+5*(s_editMode<=0)*m_posHorz, 5, s_editMode>0 || autoThrStep ? 0 : INVERS);
if (s_editMode || autoThrStep) {
if (s_editMode>0 || autoThrStep) {
for (uint8_t i=0; i<points; i++) {
uint8_t xx = X0-1-WCHART+i*WCHART/(points/2);
uint8_t yy = (DISPLAY_H-1) - (100 + crv[i]) * (DISPLAY_H-1) / 200;
@ -1478,8 +1478,8 @@ void menuProcLimits(uint8_t event)
putsChn(0,y,k+1,0);
lcd_putcAtt(12*FW+FW/2, y, (swVal[k] ? 127 : 126),0); //'<' : '>'
for (uint8_t j=0; j<4; j++) {
uint8_t attr = ((sub==k && m_posHorz==j) ? (s_editMode ? BLINK : INVERS) : 0);
uint8_t active = (attr && (s_editMode || p1valdiff)) ;
uint8_t attr = ((sub==k && m_posHorz==j) ? ((s_editMode>0) ? BLINK : INVERS) : 0);
uint8_t active = (attr && (s_editMode>0 || p1valdiff)) ;
switch(j)
{
case 0:
@ -1490,7 +1490,7 @@ void menuProcLimits(uint8_t event)
else if (attr && event==EVT_KEY_LONG(KEY_MENU)) {
int16_t zero = g_chans512[k];
ld->offset = (ld->revert) ? -zero : zero;
s_editMode = false;
s_editMode = 0;
STORE_MODELVARS;
}
break;
@ -1583,7 +1583,7 @@ void menuProcCustomSwitches(uint8_t event)
y=(i+1)*FH;
k=i+s_pgOfs;
if(k==NUM_CSW) break;
uint8_t attr = (sub==k ? (s_editMode ? BLINK : INVERS) : 0);
uint8_t attr = (sub==k ? ((s_editMode>0) ? BLINK : INVERS) : 0);
CustomSwData &cs = g_model.customSw[k];
//write SW names here
@ -1614,7 +1614,7 @@ void menuProcCustomSwitches(uint8_t event)
putsChnRaw( 17*FW, y, cs.v2 ,m_posHorz==2 ? attr : 0);
}
if((s_editMode || p1valdiff) && attr)
if((s_editMode>0 || p1valdiff) && attr)
switch (m_posHorz) {
case 0:
CHECK_INCDEC_MODELVAR( event, cs.func, 0,CS_MAXF);
@ -1671,8 +1671,8 @@ void menuProcFunctionSwitches(uint8_t event)
if(k==NUM_CHNOUT) break;
FuncSwData *sd = &g_model.funcSw[k];
for (uint8_t j=0; j<2; j++) {
uint8_t attr = ((sub==k && m_posHorz==j) ? (s_editMode ? BLINK : INVERS) : 0);
uint8_t active = (attr && (s_editMode || p1valdiff));
uint8_t attr = ((sub==k && m_posHorz==j) ? ((s_editMode>0) ? BLINK : INVERS) : 0);
uint8_t active = (attr && (s_editMode>0 || p1valdiff));
switch (j) {
case 0:
putsSwitches(1*FW, y, sd->swtch, attr);
@ -1710,8 +1710,8 @@ void menuProcSafetySwitches(uint8_t event)
SafetySwData *sd = &g_model.safetySw[k];
putsChn(0,y,k+1,0);
for(uint8_t j=0; j<=2;j++){
uint8_t attr = ((m_posVert-1==k && m_posHorz==j) ? (s_editMode ? BLINK : INVERS) : 0);
uint8_t active = (attr && (s_editMode || p1valdiff));
uint8_t attr = ((m_posVert-1==k && m_posHorz==j) ? ((s_editMode>0) ? BLINK : INVERS) : 0);
uint8_t active = (attr && (s_editMode>0 || p1valdiff));
switch(j)
{
case 0:
@ -1745,11 +1745,11 @@ void menuProcTelemetry(uint8_t event)
case EVT_KEY_BREAK(KEY_UP):
case EVT_KEY_BREAK(KEY_LEFT):
case EVT_KEY_BREAK(KEY_RIGHT):
if(s_editMode) // only fr-sky alarm fields have an edit mode
if (s_editMode>0) // only fr-sky alarm fields have an edit mode
FRSKY_setModelAlarms(); // update Fr-Sky module when edit mode exited
}
blink = s_editMode ? BLINK : INVERS ;
blink = (s_editMode>0) ? BLINK : INVERS ;
uint8_t subN = 1;
uint8_t t;
int16_t val;
@ -1767,7 +1767,7 @@ void menuProcTelemetry(uint8_t event)
lcd_putsAtt(4, y, PSTR("Max"), 0);
putsTelemetry(8*FW, y, g_model.frsky.channels[i].ratio, g_model.frsky.channels[i].type, (sub==subN && m_posHorz==0 ? blink:0)|NO_UNIT|LEFT);
lcd_putsnAtt(lcd_lastPos, y, PSTR("v-")+g_model.frsky.channels[i].type, 1, (sub==subN && m_posHorz==1 ? blink:0));
if (sub==subN && (s_editMode || p1valdiff)) {
if (sub==subN && (s_editMode>0 || p1valdiff)) {
if (m_posHorz == 0)
g_model.frsky.channels[i].ratio = checkIncDec(event, g_model.frsky.channels[i].ratio, 0, 255, EE_MODEL);
else
@ -1792,8 +1792,8 @@ void menuProcTelemetry(uint8_t event)
putsTelemetry(8*FW, y, val, g_model.frsky.channels[i].type, (sub==subN && m_posHorz==0 ? blink:0)|LEFT);
val = ((int16_t)g_model.frsky.channels[i].barMax+g_model.frsky.channels[i].offset)*g_model.frsky.channels[i].ratio / 255;
putsTelemetry(13*FW, y, val, g_model.frsky.channels[i].type, (sub==subN && m_posHorz==1 ? blink:0)|LEFT);
if(sub==subN && m_posHorz==0 && (s_editMode || p1valdiff)) g_model.frsky.channels[i].barMin = checkIncDec(event, g_model.frsky.channels[i].barMin, 0, 255, EE_MODEL);
if(sub==subN && m_posHorz==1 && (s_editMode || p1valdiff)) g_model.frsky.channels[i].barMax = checkIncDec(event, g_model.frsky.channels[i].barMax, 0, 255, EE_MODEL);
if(sub==subN && m_posHorz==0 && (s_editMode>0 || p1valdiff)) g_model.frsky.channels[i].barMin = checkIncDec(event, g_model.frsky.channels[i].barMin, 0, 255, EE_MODEL);
if(sub==subN && m_posHorz==1 && (s_editMode>0 || p1valdiff)) g_model.frsky.channels[i].barMax = checkIncDec(event, g_model.frsky.channels[i].barMax, 0, 255, EE_MODEL);
}
subN++;
@ -1806,7 +1806,7 @@ void menuProcTelemetry(uint8_t event)
uint8_t alarmValue = ((uint16_t)g_model.frsky.channels[i].alarms_value[j] * g_model.frsky.channels[i].ratio) / 255;
putsTelemetry(14*FW, y, alarmValue, g_model.frsky.channels[i].type, (sub==subN && m_posHorz==2 ? blink:0) | LEFT);
if(sub==subN && (s_editMode || p1valdiff)) {
if(sub==subN && (s_editMode>0 || p1valdiff)) {
switch (m_posHorz) {
case 0:
t = ALARM_LEVEL(i, j);

View file

@ -22,19 +22,23 @@
/*--------------------------------------------------------------------------*/
/* RTC controls */
#include <avr/io.h>
#include <string.h>
#include "rtc.h"
#include "gruvin9x.h"
#if defined (PCBV4)
#define SCL_LOW() DDRD |= 0x01 /* SCL = LOW */
#define SCL_HIGH() DDRD &= ~0x01 /* SCL = High-Z */
#define SCL_VAL ((PIND & 0x01) ? 1 : 0) /* SCL input level */
#define SDA_LOW() DDRD |= 0x02 /* SDA = LOW */
#define SDA_HIGH() DDRD &= ~0x02 /* SDA = High-Z */
#define SDA_VAL ((PIND & 0x02) ? 1 : 0) /* SDA input level */
#else // PCBV3
#define SCL_LOW() DDRB |= 0x20 /* SCL = LOW */
#define SCL_HIGH() DDRB &= ~0x20 /* SCL = High-Z */
#define SCL_VAL ((PINB & 0x20) ? 1 : 0) /* SCL input level */
#define SDA_LOW() DDRB |= 0x40 /* SDA = LOW */
#define SDA_HIGH() DDRB &= ~0x40 /* SDA = High-Z */
#define SDA_VAL ((PINB & 0x40) ? 1 : 0) /* SDA input level */
#endif
/*-------------------------------------------------*/

View file

@ -27,7 +27,7 @@
#include "gruvin9x.h"
#include "menus.h"
volatile unsigned char pinb=0, pinc=0xff, pind, pine=0xff, ping=0xff;
volatile unsigned char pinb=0, pinc=0xff, pind, pine=0xff, ping=0xff, pinj=0xff, pinl=0;
unsigned char portb, dummyport;
const char *eepromFile = "eeprom.bin";

View file

@ -39,6 +39,9 @@ typedef const int8_t prog_int8_t;
extern sem_t eeprom_write_sem;
#define loop_until_bit_is_set( port, bitnum) \
while ( 0/*! ( (port) & (1 << (bitnum)) )*/ ) ;
#define PROGMEM
#define pgm_read_byte(address_short) (*(uint8_t*)(address_short))
#define pgm_read_word(address_short) (*(uint16_t*)(address_short))
@ -70,6 +73,8 @@ extern sem_t eeprom_write_sem;
#define PIND ~pind
#define PINE ~pine
#define PING ~ping
#define PINJ ~pinj
#define PINL ~pinl
#define EEMEM
#define UCSR0B dummyport
@ -79,8 +84,38 @@ extern sem_t eeprom_write_sem;
#define DDE0 dummyport
#define PORTE0 dummyport
#define RXCIE0 dummyport
#define OCR0A dummyport
extern volatile unsigned char pinb,pinc,pind,pine,ping;
#define SPDR dummyport
#define SPSR dummyport
#define SPIF dummyport
#define SPCR dummyport
#define OUT_B_LIGHT 7
#define INP_E_ElevDR 2
#define INP_E_Trainer 5
#define INP_E_Gear 4
#define INP_C_ThrCt 6
#define INP_C_AileDR 7
#define INP_E_ID2 6
#define INP_B_KEY_LFT 6
#define INP_B_KEY_RGT 5
#define INP_B_KEY_UP 4
#define INP_B_KEY_DWN 3
#define INP_B_KEY_EXT 2
#define INP_B_KEY_MEN 1
#define INP_P_SPARE6 7
#define INP_P_SPARE5 6
#define INP_P_KEY_EXT 5
#define INP_P_KEY_MEN 4
#define INP_P_KEY_LFT 3
#define INP_P_KEY_RGT 2
#define INP_P_KEY_UP 1
#define INP_P_KEY_DWN 0
extern volatile unsigned char pinb,pinc,pind,pine,ping,pinj,pinl;
extern unsigned char portb,dummyport;
void InitEepromThread();

View file

@ -269,21 +269,25 @@ void Gruvin9xSim::refreshDiplay()
if(hasFocus()) {
static FXuint keys1[]={
KEY_Return, INP_B_KEY_MEN,
KEY_Page_Up, INP_B_KEY_MEN,
KEY_KP_1, INP_B_KEY_MEN,
KEY_Page_Down, INP_B_KEY_EXT,
KEY_BackSpace, INP_B_KEY_EXT,
KEY_KP_0, INP_B_KEY_EXT,
KEY_Down, INP_B_KEY_DWN,
KEY_Up, INP_B_KEY_UP,
KEY_Right, INP_B_KEY_RGT,
KEY_Left, INP_B_KEY_LFT
KEY_Return, INP_B_KEY_MEN, INP_P_KEY_MEN,
KEY_Page_Up, INP_B_KEY_MEN, INP_P_KEY_MEN,
KEY_KP_1, INP_B_KEY_MEN, INP_P_KEY_MEN,
KEY_Page_Down, INP_B_KEY_EXT, INP_P_KEY_EXT,
KEY_BackSpace, INP_B_KEY_EXT, INP_P_KEY_EXT,
KEY_KP_0, INP_B_KEY_EXT, INP_P_KEY_EXT,
KEY_Down, INP_B_KEY_DWN, INP_P_KEY_DWN,
KEY_Up, INP_B_KEY_UP, INP_P_KEY_UP,
KEY_Right, INP_B_KEY_RGT, INP_P_KEY_RGT,
KEY_Left, INP_B_KEY_LFT, INP_P_KEY_LFT
};
pinb &= ~ 0x7e;
for(unsigned i=0; i<DIM(keys1);i+=2){
if(getApp()->getKeyState(keys1[i])) pinb |= (1<<keys1[i+1]);
pinl &= ~ 0x3f; // for v4
for(unsigned i=0; i<DIM(keys1);i+=3) {
if (getApp()->getKeyState(keys1[i])) {
pinb |= (1<<keys1[i+1]);
pinl |= (1<<keys1[i+2]);
}
}
#ifdef __APPLE__