1
0
Fork 0
mirror of https://github.com/opentx/opentx.git synced 2025-07-24 08:45:24 +03:00

Backup before extended trims function merge

This commit is contained in:
bsongis 2011-09-13 18:11:01 +00:00
parent 4788ea632e
commit f0fca34b7e
53 changed files with 18727 additions and 0 deletions

850
src/Makefile Normal file
View file

@ -0,0 +1,850 @@
# !!!! BETA Makefile !!!!
# !!!! Use at own risk !!!!
#----------------------------------------------------------------------------
# On command line:
#
# make all = Make software.
#
# make clean = Clean out built project files.
#
# make coff = Convert ELF to AVR COFF.
#
# make extcoff = Convert ELF to AVR Extended COFF.
#
# make program = Download the hex file to the device, using avrdude.
# Please customize the avrdude settings below first!
#
# make debug = Start either simulavr or avarice as specified for debugging,
# with avr-gdb or avr-insight as the front end for debugging.
#
# make filename.s = Just compile filename.c into the assembler code only.
#
# make filename.i = Create a preprocessed source file for use in submitting
# bug reports to the GCC project.
#
# To rebuild project do "make clean" then "make all".
#----------------------------------------------------------------------------
#----------- BUILD OPTIONS ---------------------------
#gruvin: PCB version -- OVERRIDES the following settings if not STD
# Values: STD, V3, V4
PCB = V3
# Following options for PCB=STD only (ignored otherwise) ...
# Enable JETI-Telemetry or FrSky Telemetry reception on UART0
# For this option you need to modify your hardware!
# More information at [insertURLhere]
# Values = STD, JETI, FRSKY
EXT = FRSKY
# Enable heli menu
# Values = YES, NO
HELI = NO
# Enable templates menu
# Values = YES, NO
TEMPLATES = YES
# gruvin: BEEPER. Values = BUZZER, BUZZER_MOD or SPEAKER
# (without a mod, BUZZER can make PPM jack output switch from output to input at random)
BEEPER = SPEAKER
# gruvin: Legacy support freeing of USART1 TX/RX pins [DEPRECATED]
# OPTIONS STD or FREED
USART1 = FREED
# gruvin: PCM-in circuit mod for JR/Spektrum (and others) compatability
# Values = STD, MOD1
PPMIN = STD
BATT voltage algorithm. Values = BANDGAP, UNSTABLE_BANDGAP
BATT = BANDGAP
# Decimals display in the main view (PPM calibration,
# Values = YES, NO
DECIMALS = NO
# TRANSLATIONS from previous EEPROM formats
# Values = YES, NO
TRANSLATIONS = YES
#------- END BUILD OPTIONS ---------------------------
# MCU name
ifeq ($(PCB), STD)
MCU = atmega64
endif
ifeq ($(PCB), V3)
MCU = atmega2561
endif
ifeq ($(PCB), V4)
MCU = atmega2560
endif
# Processor frequency.
F_CPU = 16000000
# Output format. (can be srec, ihex, binary)
FORMAT = ihex
# Target file name (without extension).
TARGET = gruvin9x
# Object files directory
OBJDIR = obj
# List C++ source files here. (C dependencies are automatically generated.)
CPPSRC = gruvin9x.cpp stamp.cpp menus.cpp model_menus.cpp general_menus.cpp main_views.cpp statistics_views.cpp pers.cpp file.cpp lcd.cpp drivers.cpp templates.cpp
ifeq ($(EXT), JETI)
CPPSRC += jeti.cpp
endif
ifeq ($(EXT), FRSKY)
CPPSRC += frsky.cpp
endif
# Disk IO support (PCB V2+ only)
ifneq ($(PCB), STD)
CPPSRC += time.cpp
CPPSRC += rtc.cpp
CPPSRC += ff.cpp
CPPSRC += diskio.cpp
endif
# List Assembler source files here.
# Make them always end in a capital .S. Files ending in a lowercase .s
# will not be considered source files but generated files (assembler
# output from the compiler), and will be deleted upon "make clean"!
# Even though the DOS/Win* filesystem matches both .s and .S the same,
# it will preserve the spelling of the filenames, and gcc itself does
# care about how the name is spelled on its command-line.
ASRC =
# Optimization level, can be [0, 1, 2, 3, s].
# 0 = turn off optimization. s = optimize for size.
# (Note: 3 is not always the best optimization level. See avr-libc FAQ.)
OPT = s
# Debugging format.
# Native formats for AVR-GCC's -g are dwarf-2 [default] or stabs.
# AVR Studio 4.10 requires dwarf-2.
# AVR [Extended] COFF format requires stabs, plus an avr-objcopy run.
DEBUG = dwarf-2
# List any extra directories to look for include files here.
# Each directory must be seperated by a space.
# Use forward slashes for directory separators.
# For a directory that has spaces, enclose it in quotes.
EXTRAINCDIRS =
# Compiler flag to set the C Standard level.
# c89 = "ANSI" C
# gnu89 = c89 plus GCC extensions
# c99 = ISO C99 standard (not yet fully implemented)
# gnu99 = c99 plus GCC extensions
CSTANDARD = -std=gnu99
# Place -D or -U options here for C sources
CDEFS = -DF_CPU=$(F_CPU)UL
# Place -D or -U options here for C++ sources
CPPDEFS = -DF_CPU=$(F_CPU)UL
#CPPDEFS += -D__STDC_LIMIT_MACROS
#CPPDEFS += -D__STDC_CONSTANT_MACROS
# NOTE: PCB version now overrides all the earlier individual settings
# These individual settings work only for PCB=STD
ifeq ($(PCB), STD)
# STD PCB, so ...
CPPDEFS += -DPCBSTD
# If JETI-Support is enabled
ifeq ($(EXT), JETI)
CPPDEFS += -DJETI
endif
# If FRSKY-Support is enabled
ifeq ($(EXT), FRSKY)
CPPDEFS += -DFRSKY
endif
# gruvin: If buzzer speaker replacment supported
ifeq ($(BEEPER), SPEAKER)
CPPDEFS += -DBEEPSPKR
endif
# If buzzer modified (no interference with PPM jack)
ifeq ($(BEEPER), BUZZER_MOD)
CPPDEFS += -DBUZZER_MOD
endif
# If BandGap is not rock solid
ifeq ($(BATT), UNSTABLE_BANDGAP)
CPPDEFS += -DBATT_UNSTABLE_BANDGAP
endif
# gruvin: Legacy support for hardware mod freeing USART1 [DEPRECATED]
ifeq ($(USART1), FREED)
CPPDEFS += -DUSART1FREED
endif
# gruvin: PCM-in circuit mod for JR/Spektrum (and others) compatability
ifeq ($(PPMIN), MOD1)
CPPDEFS += -DPPMIN_MOD1
endif
else
# not PCB=STD, so ...
ifeq ($(PCB), V3)
CPPDEFS += -DPCBV3 -DFRSKY -DBEEPSPKR
endif
ifeq ($(PCB), V4)
CPPDEFS += -DPCBV4 -DFRSKY -DBEEPSPKR
endif
endif
### Global Build-Option Directives ###
ifeq ($(DECIMALS), YES)
CPPDEFS += -DDECIMALS_DISPLAYED
endif
ifeq ($(TRANSLATIONS), YES)
CPPDEFS += -DTRANSLATIONS
endif
ifeq ($(HELI), YES)
CPPDEFS += -DHELI
endif
ifeq ($(TEMPLATES), YES)
CPPDEFS += -DTEMPLATES
endif
#---------------- Compiler Options C ----------------
# -g*: generate debugging information
# -O*: optimization level
# -f...: tuning, see GCC manual and avr-libc documentation
# -Wall...: warning level
# -Wa,...: tell GCC to pass this to the assembler.
# -adhlns...: create assembler listing
CFLAGS = -g$(DEBUG)
CFLAGS += $(CDEFS)
CFLAGS += -O$(OPT)
#CFLAGS += -mint8
#CFLAGS += -mshort-calls
CFLAGS += -funsigned-char
CFLAGS += -funsigned-bitfields
CFLAGS += -fpack-struct
CFLAGS += -fshort-enums
#CFLAGS += -fno-unit-at-a-time
CFLAGS += -Wall
CFLAGS += -Wstrict-prototypes
CFLAGS += -Wundef
#CFLAGS += -Wunreachable-code
#CFLAGS += -Wsign-compare
CFLAGS += -Wa,-adhlns=$(<:%.c=$(OBJDIR)/%.lst)
CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
CFLAGS += $(CSTANDARD)
CFLAGS+= --combine -fwhole-program
#---------------- Compiler Options C++ ----------------
# -g*: generate debugging information
# -O*: optimization level
# -f...: tuning, see GCC manual and avr-libc documentation
# -Wall...: warning level
# -Wa,...: tell GCC to pass this to the assembler.
# -adhlns...: create assembler listing
CPPFLAGS = -g$(DEBUG)
CPPFLAGS += $(CPPDEFS)
CPPFLAGS += -O$(OPT)
#CPPFLAGS += -mint8
#CPPFLAGS += -mshort-calls
#CPPFLAGS += -funsigned-char
#CPPFLAGS += -funsigned-bitfields
#CPPFLAGS += -fpack-struct
#CPPFLAGS += -fshort-enums
#CPPFLAGS += -fno-exceptions
#CPPFLAGS += -fno-unit-at-a-time
#CPPFLAGS += -fno-inline-small-functions
CPPFLAGS += -Wall
CPPFLAGS += -Wno-strict-aliasing
#CPPFLAGS += -Wstrict-prototypes
#CFLAGS += -Wundef
#CPPFLAGS += -Wunreachable-code
#CPPFLAGS += -Wsign-compare
#CPPFLAGS += -Wa,-adhlns=$(<:%.cpp=$(OBJDIR)/%.lst)
CPPFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
#CPPFLAGS += $(CSTANDARD)
#---------------- Assembler Options ----------------
# -Wa,...: tell GCC to pass this to the assembler.
# -ahlms: create listing
# -gstabs: have the assembler create line number information; note that
# for use in COFF files, additional information about filenames
# and function names needs to be present in the assembler source
# files -- see avr-libc docs [FIXME: not yet described there]
ASFLAGS = -Wa,-adhlns=$(<:%.S=$(OBJDIR)/%.lst),-gstabs
#---------------- Library Options ----------------
# Minimalistic printf version
PRINTF_LIB_MIN = -Wl,-u,vfprintf -lprintf_min
# Floating point printf version (requires MATH_LIB = -lm below)
PRINTF_LIB_FLOAT = -Wl,-u,vfprintf -lprintf_flt
# If this is left blank, then it will use the Standard printf version.
PRINTF_LIB =
#PRINTF_LIB = $(PRINTF_LIB_MIN)
#PRINTF_LIB = $(PRINTF_LIB_FLOAT)
# Minimalistic scanf version
SCANF_LIB_MIN = -Wl,-u,vfscanf -lscanf_min
# Floating point + %[ scanf version (requires MATH_LIB = -lm below)
SCANF_LIB_FLOAT = -Wl,-u,vfscanf -lscanf_flt
# If this is left blank, then it will use the Standard scanf version.
SCANF_LIB =
#SCANF_LIB = $(SCANF_LIB_MIN)
#SCANF_LIB = $(SCANF_LIB_FLOAT)
MATH_LIB = -lm
#---------------- External Memory Options ----------------
# 64 KB of external RAM, starting after internal RAM (ATmega128!),
# used for variables (.data/.bss) and heap (malloc()).
#EXTMEMOPTS = -Wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff
# 64 KB of external RAM, starting after internal RAM (ATmega128!),
# only used for heap (malloc()).
#EXTMEMOPTS = -Wl,--defsym=__heap_start=0x801100,--defsym=__heap_end=0x80ffff
EXTMEMOPTS =
#---------------- Linker Options ----------------
# -Wl,...: tell GCC to pass this to linker.
# -Map: create map file
# --cref: add cross reference to map file
LDFLAGS = -Wl,-Map=$(TARGET).map,--cref
LDFLAGS += $(EXTMEMOPTS)
LDFLAGS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB)
#LDFLAGS += -T linker_script.x
#---------------- Programming Options (avrdude) ----------------
# Programming hardware: alf avr910 avrisp bascom bsd
# dt006 pavr picoweb pony-stk200 sp12 stk200 stk500
#
# Type: avrdude -c ?
# to get a full listing.
#
AVRDUDE_PROGRAMMER = usbtiny
# com1 = serial port. Use lpt1 to connect to parallel port.
AVRDUDE_PORT = /dev/ttyUSB01
AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex:a
AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).bin:a
AVRDUDE_READ_FLASH = -U flash:r:$(TARGET).hex:r
AVRDUDE_READ_EEPROM = -U eeprom:r:$(TARGET).bin:r
# Uncomment the following if you want avrdude's erase cycle counter.
# Note that this counter needs to be initialized first using -Yn,
# see avrdude manual.
#AVRDUDE_ERASE_COUNTER = -y
# Uncomment the following if you do /not/ wish a verification to be
# performed after programming the device.
AVRDUDE_NO_VERIFY = -V
# Increase verbosity level. Please use this when submitting bug
# reports about avrdude. See <http://savannah.nongnu.org/projects/avrdude>
# to submit bug reports.
#AVRDUDE_VERBOSE = -v -v
#AVRDUDE_FLAGS = -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER)
AVRDUDE_FLAGS = -B 1 -p $(MCU) -c $(AVRDUDE_PROGRAMMER)
AVRDUDE_FLAGS += $(AVRDUDE_NO_VERIFY)
AVRDUDE_FLAGS += $(AVRDUDE_VERBOSE)
AVRDUDE_FLAGS += $(AVRDUDE_ERASE_COUNTER)
#---------------- Debugging Options ----------------
# For simulavr only - target MCU frequency.
DEBUG_MFREQ = $(F_CPU)
# Set the DEBUG_UI to either gdb or insight.
DEBUG_UI = gdb
# DEBUG_UI = insight
# Set the debugging back-end to either avarice, simulavr.
#DEBUG_BACKEND = avarice
DEBUG_BACKEND = simulavr
# GDB Init Filename.
GDBINIT_FILE = __avr_gdbinit
# When using avarice settings for the JTAG
JTAG_DEV = /dev/com1
# Debugging port used to communicate between GDB / avarice / simulavr.
DEBUG_PORT = 4242
# Debugging host used to communicate between GDB / avarice / simulavr, normally
# just set to localhost unless doing some sort of crazy debugging when
# avarice is running on a different computer.
DEBUG_HOST = localhost
#============================================================================
# Define programs and commands.
SHELL = sh
CC = avr-gcc
OBJCOPY = avr-objcopy
OBJDUMP = avr-objdump
SIZE = avr-size
NM = avr-nm
AVRDUDE = avrdude
REMOVE = rm -f
REMOVEDIR = rm -rf
COPY = cp
WINSHELL = cmd
# th9x/gruvin9x-specific
XBM2LBM = ruby ../util/xbm2lbm.rb
AREV = $(shell sh -c "cat .svn/entries | sed -n '4p'")
REV = $(shell echo $$(( $(AREV) + 1 )))
# Define Messages
# English
MSG_ERRORS_NONE = Errors: none
MSG_BEGIN = -------- begin --------
MSG_END = -------- end --------
MSG_SIZE_BEFORE = Size before:
MSG_SIZE_AFTER = Size after:
MSG_COFF = Converting to AVR COFF:
MSG_EXTENDED_COFF = Converting to AVR Extended COFF:
MSG_FLASH = Creating load file for Flash:
MSG_EEPROM = Creating load file for EEPROM:
MSG_EXTENDED_LISTING = Creating Extended Listing:
MSG_SYMBOL_TABLE = Creating Symbol Table:
MSG_LINKING = Linking:
MSG_COMPILING = Compiling C:
MSG_COMPILING_CPP = Compiling C++:
MSG_ASSEMBLING = Assembling:
MSG_CLEANING = Cleaning project:
MSG_CREATING_LIBRARY = Creating library:
# Define all object files.
OBJ = $(SRC:%.c=$(OBJDIR)/%.o) $(CPPSRC:%.cpp=$(OBJDIR)/%.o) $(ASRC:%.S=$(OBJDIR)/%.o)
# Define all listing files.
LST = $(SRC:%.c=$(OBJDIR)/%.lst) $(CPPSRC:%.cpp=$(OBJDIR)/%.lst) $(ASRC:%.S=$(OBJDIR)/%.lst)
# Compiler flags to generate dependency files.
GENDEPFLAGS = -MD -MP -MF .dep/$(@F).d
# Combine all necessary flags and optional flags.
# Add target processor to flags.
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) $(GENDEPFLAGS)
ALL_CPPFLAGS = -mmcu=$(MCU) -I. -x c++ $(CPPFLAGS) $(GENDEPFLAGS)
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS)
SUB_VER = ${shell sh -c "grep \"SUB_VERS\" gruvin9x.h | cut -d\ -f3 | egrep -o \"[[:digit:]]\""}
ABUILD_NUM = ${shell sh -c "grep \"BUILD_NUM\" stamp-gruvin9x.h | egrep -o \"[[:digit:]]+\""}
BUILD_NUM = $(shell echo $$(( $(ABUILD_NUM) + 1 )))
BUILD_DIR = $(shell pwd | awk -F'/' '{print $$((NF-1))}')
ifeq "$(USER)" "bryan"
THEUSER=gruvin
else
THEUSER=$(USER)
endif
# Default target.
all: begin gccversion sizebefore build sizeafter end
# Change the build target to build a HEX file or a library.
build: stamp font.lbm font_dblsize.lbm sticks.lbm s9xsplash.lbm elf hex eep lss sym
#build: lib
elf: $(TARGET).elf
hex: $(TARGET).hex
eep: $(TARGET).eep
lss: $(TARGET).lss
sym: $(TARGET).sym
LIBNAME=lib$(TARGET).a
lib: $(LIBNAME)
# Build stamp-file
stamp:
@echo
@echo "Generate Version-stamp:"
@echo "//Automatically generated file (Makefile) - do not edit" > stamp-gruvin9x.h
@echo "#define DATE_STR \"`date +%Y-%m-%d`\"" >> stamp-gruvin9x.h
@echo "#define TIME_STR \"`date +%H:%I:%S`\"" >> stamp-gruvin9x.h
@echo "#define TAG_VERS $(SUB_VER)-$(THEUSER)" >> stamp-gruvin9x.h
@echo "#define SVN_VERS \"$(BUILD_DIR)-r$(REV)\"" >> stamp-gruvin9x.h
@echo "#define BUILD_NUM $(BUILD_NUM)" >> stamp-gruvin9x.h
@cat stamp-gruvin9x.h
font.lbm: font_6x1.xbm
@echo
@echo "Convert font from xbm to lbm:"
$(XBM2LBM) $<
font_dblsize.lbm: font_dblsize.xbm
@echo
@echo "Convert font from xbm to lbm:"
$(XBM2LBM) $<
sticks.lbm: sticks_4x1.xbm
@echo
@echo "Convert font from xbm to lbm:"
$(XBM2LBM) $<
s9xsplash.lbm: s9xsplash.xbm
@echo
@echo "Convert font from xbm to lbm:"
$(XBM2LBM) $<
# Eye candy.
# AVR Studio 3.x does not check make's exit code but relies on
# the following magic strings to be generated by the compile job.
begin:
@echo
@echo $(MSG_BEGIN)
end:
@echo $(MSG_END)
@echo
# Display size of file.
HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex
ELFSIZE = $(SIZE) --mcu=$(MCU) --format=avr $(TARGET).elf
AVRMEM = avr-mem.sh $(TARGET).elf $(MCU)
sizebefore:
@if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_BEFORE); $(ELFSIZE); \
$(AVRMEM) 2>/dev/null; echo; fi
sizeafter:
@if test -f $(TARGET).elf; then echo; echo $(MSG_SIZE_AFTER); $(ELFSIZE); \
$(AVRMEM) 2>/dev/null; echo; fi
# Display compiler version information.
gccversion :
@$(CC) --version
# Program the device.
wflash: $(TARGET).hex
$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH)
weeprom: $(TARGET).bin
$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_EEPROM)
# Write flash and eeprom
wfe: $(TARGET).hex
$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH)
$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_EEPROM)
rflash: $(TARGET).hex
$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_READ_FLASH)
reeprom: $(TARGET).bin
$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_READ_EEPROM)
# Generate avr-gdb config/init file which does the following:
# define the reset signal, load the target file, connect to target, and set
# a breakpoint at main().
gdb-config:
@$(REMOVE) $(GDBINIT_FILE)
@echo define reset >> $(GDBINIT_FILE)
@echo SIGNAL SIGHUP >> $(GDBINIT_FILE)
@echo end >> $(GDBINIT_FILE)
@echo file $(TARGET).elf >> $(GDBINIT_FILE)
@echo target remote $(DEBUG_HOST):$(DEBUG_PORT) >> $(GDBINIT_FILE)
ifeq ($(DEBUG_BACKEND),simulavr)
@echo load >> $(GDBINIT_FILE)
endif
@echo break main >> $(GDBINIT_FILE)
# gruvin: added extra include and lib paths to get simu working on my Mac
FOXINC=-I/usr/local/include/fox-1.6 -I/usr/include/fox-1.6 \
-I$(FOXPATH)/include \
-I/opt/local/include/fox-1.6
FOXLIB=-L/usr/local/lib \
-L$(FOXPATH)/src/.libs \
-L/opt/local/lib \
-lFOX-1.6 \
-Wl,-rpath,$(FOXPATH)/src/.libs
LBITS := $(shell getconf LONG_BIT)
ifeq ($(LBITS),64)
ARCH=-arch x86_64
else
ARCH=
endif
simu: $(CPPSRC) Makefile simu.cpp $(CPPSRC) simpgmspace.cpp *.h *.lbm eeprom.bin
g++ simu.cpp $(CPPFLAGS) $(CPPSRC) simpgmspace.cpp $(ARCH) -o simu $(FOXINC) $(FOXLIB) -MD -DSIMU
eeprom.bin:
dd if=/dev/zero of=$@ bs=1 count=2048
debug: gdb-config $(TARGET).elf
ifeq ($(DEBUG_BACKEND),avarice)
@echo Starting AVaRICE - Press enter when "waiting to connect" message displays.
@$(WINSHELL) /c start avarice --jtag $(JTAG_DEV) --erase --program --file \
$(TARGET).elf $(DEBUG_HOST):$(DEBUG_PORT)
@$(WINSHELL) /c pause
else
@$(WINSHELL) /c start simulavr --gdbserver --device $(MCU) --clock-freq \
$(DEBUG_MFREQ) --port $(DEBUG_PORT)
endif
@$(WINSHELL) /c start avr-$(DEBUG_UI) --command=$(GDBINIT_FILE)
# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB.
COFFCONVERT = $(OBJCOPY) --debugging
COFFCONVERT += --change-section-address .data-0x800000
COFFCONVERT += --change-section-address .bss-0x800000
COFFCONVERT += --change-section-address .noinit-0x800000
COFFCONVERT += --change-section-address .eeprom-0x810000
coff: $(TARGET).elf
@echo
@echo $(MSG_COFF) $(TARGET).cof
$(COFFCONVERT) -O coff-avr $< $(TARGET).cof
extcoff: $(TARGET).elf
@echo
@echo $(MSG_EXTENDED_COFF) $(TARGET).cof
$(COFFCONVERT) -O coff-ext-avr $< $(TARGET).cof
# Create final output files (.hex, .eep) from ELF output file.
%.hex: %.elf
@echo
@echo $(MSG_FLASH) $@
$(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@
%.eep: %.elf
@echo
@echo $(MSG_EEPROM) $@
-$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
--change-section-lma .eeprom=0 -O $(FORMAT) $< $@
# Create extended listing file from ELF output file.
%.lss: %.elf
@echo
@echo $(MSG_EXTENDED_LISTING) $@
$(OBJDUMP) -h -S $< > $@
# Create a symbol table from ELF output file.
%.sym: %.elf
@echo
@echo $(MSG_SYMBOL_TABLE) $@
$(NM) -n $< > $@
# Create library from object files.
.SECONDARY : $(TARGET).a
.PRECIOUS : $(OBJ)
%.a: $(OBJ)
@echo
@echo $(MSG_CREATING_LIBRARY) $@
$(AR) $@ $(OBJ)
# Link: create ELF output file from object files.
.SECONDARY : $(TARGET).elf
.PRECIOUS : $(OBJ)
%.elf: $(OBJ)
@echo
@echo $(MSG_LINKING) $@
$(CC) $(ALL_CFLAGS) $^ --output $@ $(LDFLAGS)
# Compile: create object files from C source files.
$(OBJDIR)/%.o : %.c
@echo
@echo $(MSG_COMPILING) $<
$(CC) -c $(ALL_CFLAGS) $< -o $@
# Compile: create object files from C++ source files.
$(OBJDIR)/%.o : %.cpp
@echo
@echo $(MSG_COMPILING_CPP) $<
$(CC) -c $(ALL_CPPFLAGS) $< -o $@
# Compile: create assembler files from C source files.
%.s : %.c
$(CC) -S $(ALL_CFLAGS) $< -o $@
# Compile: create assembler files from C++ source files.
%.s : %.cpp
$(CC) -S $(ALL_CPPFLAGS) $< -o $@
# Assemble: create object files from assembler source files.
$(OBJDIR)/%.o : %.S
@echo
@echo $(MSG_ASSEMBLING) $<
$(CC) -c $(ALL_ASFLAGS) $< -o $@
# Create preprocessed source for use in sending a bug report.
%.i : %.c
$(CC) -E -mmcu=$(MCU) -I. $(CFLAGS) $< -o $@
# Target: clean project.
clean: begin clean_list end
clean_list :
@echo
@echo $(MSG_CLEANING)
$(REMOVE) simu
$(REMOVE) gtests
$(REMOVE) gtest.a
$(REMOVE) gtest_main.a
$(REMOVE) $(TARGET).hex
$(REMOVE) $(TARGET).eep
$(REMOVE) $(TARGET).cof
$(REMOVE) $(TARGET).elf
$(REMOVE) $(TARGET).map
$(REMOVE) $(TARGET).sym
$(REMOVE) $(TARGET).lss
$(REMOVEDIR) $(OBJDIR)
$(REMOVE) $(SRC:.c=.s)
$(REMOVE) *.o
$(REMOVE) *.d
$(REMOVEDIR) .dep
# Create object files directory
$(shell mkdir $(OBJDIR) 2>/dev/null)
# Include the dependency files.
-include $(shell mkdir .dep 2>/dev/null) $(wildcard .dep/*)
# Listing of phony targets.
.PHONY : all begin finish end sizebefore sizeafter gccversion \
build elf hex eep lss sym coff extcoff \
clean clean_list program debug gdb-config stamp
#### GOOGLE TESTS
GTEST_DIR = ../gtest-1.6.0/
# Where to find user code.
USER_DIR = ./
# Flags passed to the preprocessor.
CPPFLAGS += -I$(GTEST_DIR)/include
# Flags passed to the C++ compiler.
CXXFLAGS += -g -Wall -Wextra
# All Google Test headers. Usually you shouldn't change this
# definition.
GTEST_HEADERS = $(GTEST_DIR)/include/gtest/*.h \
$(GTEST_DIR)/include/gtest/internal/*.h
# House-keeping build targets.
# Builds gtest.a and gtest_main.a.
# Usually you shouldn't tweak such internal variables, indicated by a
# trailing _.
GTEST_SRCS_ = $(GTEST_DIR)/src/*.cc $(GTEST_DIR)/src/*.h $(GTEST_HEADERS)
# For simplicity and to avoid depending on Google Test's
# implementation details, the dependencies specified below are
# conservative and not optimized. This is fine as Google Test
# compiles fast and for ordinary users its source rarely changes.
gtest-all.o : $(GTEST_SRCS_)
$(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c \
$(GTEST_DIR)/src/gtest-all.cc
gtest_main.o : $(GTEST_SRCS_)
$(CXX) $(CPPFLAGS) -I$(GTEST_DIR) $(CXXFLAGS) -c \
$(GTEST_DIR)/src/gtest_main.cc
gtest.a : gtest-all.o
$(AR) $(ARFLAGS) $@ $^
gtest_main.a : gtest-all.o gtest_main.o
$(AR) $(ARFLAGS) $@ $^
# Builds a sample test. A test should link with either gtest.a or
# gtest_main.a, depending on whether it defines its own main()
# function.
gtests: $(CPPSRC) gtests.cpp simpgmspace.cpp gtest_main.a
g++ $(CPPSRC) gtests.cpp simpgmspace.cpp $(CPPFLAGS) -I$(GTEST_DIR) $(ARCH) -o gtests -lpthread -MD -DSIMU gtest_main.a

62
src/diskio-test.cpp Normal file
View file

@ -0,0 +1,62 @@
/*
I put this in place of the code in the FRSKY first menu page, just as a quick test
*/
// DISK_IO DEBUG -- XXX DELETE ME
static uint8_t onceonly = 0;
static FRESULT f_err_code;
static FATFS FATFS_Obj;
static uint8_t result = 0;
static TCHAR sBuffer[100] = {0};
static TCHAR *myStr = sBuffer;
if (!onceonly)
{
// First, let's try to set the RTC date/time
RTC rtc;
rtc.year = 2011;
rtc.month = 6;
rtc.mday = 23;
rtc.wday = 5;
rtc.hour = 21;
rtc.min = 53;
rtc.sec = 0;
// rtc_settime(&rtc);
// IT WORKED! And battery back-up for the RTC chip is working also.
//////////////////////
f_err_code = f_mount(0, &FATFS_Obj);
FIL fil_obj;
// atempt creating and writing to a new file
result = f_open(&fil_obj, "/foo.txt", FA_CREATE_ALWAYS | FA_WRITE);
f_printf(&fil_obj, "gruvin9x created this file! Yay!\n");
f_close(&fil_obj);
// That worked! Now test reading a line from another file.
result = f_open(&fil_obj, "/foo.txt", FA_READ);
myStr = f_gets(sBuffer, 100, &fil_obj);
f_close(&fil_obj);
onceonly = 1;
}
lcd_outdezAtt(5*FW, 2*FH, f_err_code, 0);
lcd_outdezAtt(5*FW, 3*FH, result, 0);
lcd_outdezAtt(5*FW, 4*FH, strlen(myStr), 0);
lcd_outdezAtt(5*FW, 5*FH, myStr[0], 0);
// can't use lcd_puts... becasue it specifies prog_char mem space, not SRAM
uint8_t x=0;
uint8_t j=6;
for (uint8_t i=0; i<strlen(myStr); i++)
{
if (myStr[i]==0) break;
if (myStr[i]!='\n') lcd_putc(x, j*FH, myStr[i]);
x+=FW;
}
return;

631
src/diskio.cpp Normal file
View file

@ -0,0 +1,631 @@
/*
* 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.
*
*/
/*-----------------------------------------------------------------------*/
/* MMCv3/SDv1/SDv2 (in SPI mode) control module (C)ChaN, 2010 */
/*-----------------------------------------------------------------------*/
/* Only rcvr_spi(), xmit_spi(), disk_timerproc() and some macros */
/* are platform dependent. */
/*-----------------------------------------------------------------------*/
#include <avr/io.h>
#include "diskio.h"
/* Definitions for MMC/SDC command */
#define CMD0 (0) /* GO_IDLE_STATE */
#define CMD1 (1) /* SEND_OP_COND (MMC) */
#define ACMD41 (0x80+41) /* SEND_OP_COND (SDC) */
#define CMD8 (8) /* SEND_IF_COND */
#define CMD9 (9) /* SEND_CSD */
#define CMD10 (10) /* SEND_CID */
#define CMD12 (12) /* STOP_TRANSMISSION */
#define ACMD13 (0x80+13) /* SD_STATUS (SDC) */
#define CMD16 (16) /* SET_BLOCKLEN */
#define CMD17 (17) /* READ_SINGLE_BLOCK */
#define CMD18 (18) /* READ_MULTIPLE_BLOCK */
#define CMD23 (23) /* SET_BLOCK_COUNT (MMC) */
#define ACMD23 (0x80+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
#define CMD24 (24) /* WRITE_BLOCK */
#define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */
#define CMD55 (55) /* APP_CMD */
#define CMD58 (58) /* READ_OCR */
/* Card type flags (CardType) */
#define CT_MMC 0x01 /* MMC ver 3 */
#define CT_SD1 0x02 /* SD ver 1 */
#define CT_SD2 0x04 /* SD ver 2 */
#define CT_SDC (CT_SD1|CT_SD2) /* SD */
#define CT_BLOCK 0x08 /* Block addressing */
/* 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 */
#define SOCKPORT PINB /* Socket contact port */
#define SOCKWP 0x00 // not implemented /* Write protect switch */
#define SOCKINS 0x00 // not implemented /* Card detect switch */
#define FCLK_SLOW() SPCR = 0x52 /* Set slow clock (100k-400k) */
#define FCLK_FAST() SPCR = 0x50 /* Set fast clock (depends on the CSD) */
/*--------------------------------------------------------------------------
Module Private Functions
---------------------------------------------------------------------------*/
static volatile
DSTATUS Stat = STA_NOINIT; /* Disk status */
volatile BYTE Timer1, Timer2; /* 100Hz decrement timer */
static
BYTE CardType; /* Card type flags */
/*-----------------------------------------------------------------------*/
/* Transmit a byte to MMC via SPI (Platform dependent) */
/*-----------------------------------------------------------------------*/
#define xmit_spi(dat) SPDR=(dat); loop_until_bit_is_set(SPSR,SPIF)
/*-----------------------------------------------------------------------*/
/* Receive a byte from MMC via SPI (Platform dependent) */
/*-----------------------------------------------------------------------*/
static
BYTE rcvr_spi (void)
{
SPDR = 0xFF;
loop_until_bit_is_set(SPSR, SPIF);
return SPDR;
}
/* Alternative macro to receive data fast */
#define rcvr_spi_m(dst) SPDR=0xFF; loop_until_bit_is_set(SPSR,SPIF); *(dst)=SPDR
/*-----------------------------------------------------------------------*/
/* Wait for card ready */
/*-----------------------------------------------------------------------*/
static
int wait_ready (void) /* 1:OK, 0:Timeout */
{
Timer2 = 50; /* Wait for ready in timeout of 500ms (G: now 50x16ms) */
rcvr_spi();
do
if (rcvr_spi() == 0xFF) return 1;
while (Timer2);
return 0;
}
/*-----------------------------------------------------------------------*/
/* Deselect the card and release SPI bus */
/*-----------------------------------------------------------------------*/
static
void deselect (void)
{
CS_HIGH();
rcvr_spi();
}
/*-----------------------------------------------------------------------*/
/* Select the card and wait for ready */
/*-----------------------------------------------------------------------*/
static
int select (void) /* 1:Successful, 0:Timeout */
{
CS_LOW();
if (!wait_ready()) {
deselect();
return 0;
}
return 1;
}
/*-----------------------------------------------------------------------*/
/* Power Control (Platform dependent) */
/*-----------------------------------------------------------------------*/
/* When the target system does not support socket power control, there */
/* is nothing to do in these functions and chk_power always returns 1. */
static
int power_status(void) /* Socket power state: 0=off, 1=on */
{
return (PORTE & 0x80) ? 0 : 1;
}
static
void power_on (void)
{
// PORTE &= ~0x80; // Socket power on
for (Timer1 = 2; Timer1; ); // Wait for 20ms
//PORTB = 0b10110101; // Enable drivers
//DDRB = 0b11000111;
SPCR = 0x52; // Enable SPI function in mode 0
SPSR = 0x00; // G: was 0x01; // SPI 2x mode
}
static
void power_off (void)
{
SPCR = 0; /* Disable SPI function */
// DDRB = 0b11000000; /* Disable drivers */
// PORTB = 0b10110000;
// PORTE |= 0x80; /* Socket power off */
Stat |= STA_NOINIT;
}
/*-----------------------------------------------------------------------*/
/* Receive a data packet from MMC */
/*-----------------------------------------------------------------------*/
static
int rcvr_datablock (
BYTE *buff, /* Data buffer to store received data */
UINT btr /* Byte count (must be multiple of 4) */
)
{
BYTE token;
Timer1 = 20;
do { /* Wait for data packet in timeout of 200ms */
token = rcvr_spi();
} while ((token == 0xFF) && Timer1);
if(token != 0xFE) return 0; /* If not valid data token, retutn with error */
do { /* Receive the data block into buffer */
rcvr_spi_m(buff++);
rcvr_spi_m(buff++);
rcvr_spi_m(buff++);
rcvr_spi_m(buff++);
} while (btr -= 4);
rcvr_spi(); /* Discard CRC */
rcvr_spi();
return 1; /* Return with success */
}
/*-----------------------------------------------------------------------*/
/* Send a data packet to MMC */
/*-----------------------------------------------------------------------*/
static
int xmit_datablock (
const BYTE *buff, /* 512 byte data block to be transmitted */
BYTE token /* Data/Stop token */
)
{
BYTE resp, wc;
if (!wait_ready()) return 0;
xmit_spi(token); /* Xmit data token */
if (token != 0xFD) { /* Is data token */
wc = 0;
do { /* Xmit the 512 byte data block to MMC */
xmit_spi(*buff++);
xmit_spi(*buff++);
} while (--wc);
xmit_spi(0xFF); /* CRC (Dummy) */
xmit_spi(0xFF);
resp = rcvr_spi(); /* Reveive data response */
if ((resp & 0x1F) != 0x05) /* If not accepted, return with error */
return 0;
}
return 1;
}
/*-----------------------------------------------------------------------*/
/* Send a command packet to MMC */
/*-----------------------------------------------------------------------*/
static
BYTE send_cmd ( /* Returns R1 resp (bit7==1:Send failed) */
BYTE cmd, /* Command index */
DWORD arg /* Argument */
)
{
BYTE n, res;
if (cmd & 0x80) { /* ACMD<n> is the command sequense of CMD55-CMD<n> */
cmd &= 0x7F;
res = send_cmd(CMD55, 0);
if (res > 1) return res;
}
/* Select the card and wait for ready */
deselect();
if (!select()) return 0xFF;
/* Send command packet */
xmit_spi(0x40 | cmd); /* Start + Command index */
xmit_spi((BYTE)(arg >> 24)); /* Argument[31..24] */
xmit_spi((BYTE)(arg >> 16)); /* Argument[23..16] */
xmit_spi((BYTE)(arg >> 8)); /* Argument[15..8] */
xmit_spi((BYTE)arg); /* Argument[7..0] */
n = 0x01; /* Dummy CRC + Stop */
if (cmd == CMD0) n = 0x95; /* Valid CRC for CMD0(0) */
if (cmd == CMD8) n = 0x87; /* Valid CRC for CMD8(0x1AA) */
xmit_spi(n);
/* Receive command response */
if (cmd == CMD12) rcvr_spi(); /* Skip a stuff byte when stop reading */
n = 10; /* Wait for a valid response in timeout of 10 attempts */
do
res = rcvr_spi();
while ((res & 0x80) && --n);
return res; /* Return with the response value */
}
/*--------------------------------------------------------------------------
Public Functions
---------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*/
/* Initialize Disk Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE drv /* Physical drive nmuber (0) */
)
{
BYTE n, cmd, ty, ocr[4];
if (drv) return STA_NOINIT; /* Supports only single drive */
if (Stat & STA_NODISK) return Stat; /* No card in the socket */
power_on(); /* Force socket power on */
FCLK_SLOW();
for (n = 10; n; n--) rcvr_spi(); /* 80 dummy clocks */
ty = 0;
if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */
Timer1 = 100; /* Initialization timeout of 1000 msec */
if (send_cmd(CMD8, 0x1AA) == 1) { /* SDv2? */
for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); /* Get trailing return value of R7 resp */
if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */
while (Timer1 && send_cmd(ACMD41, 1UL << 30)); /* Wait for leaving idle state (ACMD41 with HCS bit) */
if (Timer1 && send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */
for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* SDv2 */
}
}
} else { /* SDv1 or MMCv3 */
if (send_cmd(ACMD41, 0) <= 1) {
ty = CT_SD1; cmd = ACMD41; /* SDv1 */
} else {
ty = CT_MMC; cmd = CMD1; /* MMCv3 */
}
while (Timer1 && send_cmd(cmd, 0)); /* Wait for leaving idle state */
if (!Timer1 || send_cmd(CMD16, 512) != 0) /* Set R/W block length to 512 */
ty = 0;
}
}
CardType = ty;
deselect();
if (ty) { /* Initialization succeded */
Stat &= ~STA_NOINIT; /* Clear STA_NOINIT */
FCLK_FAST();
} else { /* Initialization failed */
power_off();
}
return Stat;
}
/*-----------------------------------------------------------------------*/
/* Get Disk Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE drv /* Physical drive nmuber (0) */
)
{
if (drv) return STA_NOINIT; /* Supports only single drive */
return Stat;
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE drv, /* Physical drive nmuber (0) */
BYTE *buff, /* Pointer to the data buffer to store read data */
DWORD sector, /* Start sector number (LBA) */
BYTE count /* Sector count (1..255) */
)
{
if (drv || !count) return RES_PARERR;
if (Stat & STA_NOINIT) return RES_NOTRDY;
if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert to byte address if needed */
if (count == 1) { /* Single block read */
if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */
&& rcvr_datablock(buff, 512))
count = 0;
}
else { /* Multiple block read */
if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */
do {
if (!rcvr_datablock(buff, 512)) break;
buff += 512;
} while (--count);
send_cmd(CMD12, 0); /* STOP_TRANSMISSION */
}
}
deselect();
return count ? RES_ERROR : RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_write (
BYTE drv, /* Physical drive nmuber (0) */
const BYTE *buff, /* Pointer to the data to be written */
DWORD sector, /* Start sector number (LBA) */
BYTE count /* Sector count (1..255) */
)
{
if (drv || !count) return RES_PARERR;
if (Stat & STA_NOINIT) return RES_NOTRDY;
if (Stat & STA_PROTECT) return RES_WRPRT;
if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert to byte address if needed */
if (count == 1) { /* Single block write */
if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */
&& xmit_datablock(buff, 0xFE))
count = 0;
}
else { /* Multiple block write */
if (CardType & CT_SDC) send_cmd(ACMD23, count);
if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */
do {
if (!xmit_datablock(buff, 0xFC)) break;
buff += 512;
} while (--count);
if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */
count = 1;
}
}
deselect();
return count ? RES_ERROR : RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
BYTE drv, /* Physical drive nmuber (0) */
BYTE ctrl, /* Control code */
BYTE *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
BYTE n, csd[16], *ptr = buff;
WORD csize;
if (drv) return RES_PARERR;
res = RES_ERROR;
if (ctrl == CTRL_POWER) {
switch (ptr[0]) {
case 0: /* Sub control code (POWER_OFF) */
power_off(); /* Power off */
res = RES_OK;
break;
case 1: /* Sub control code (POWER_GET) */
ptr[1] = (BYTE)power_status();
res = RES_OK;
break;
default :
res = RES_PARERR;
}
}
else {
if (Stat & STA_NOINIT) return RES_NOTRDY;
switch (ctrl) {
case CTRL_SYNC : /* Make sure that no pending write process. Do not remove this or written sector might not left updated. */
if (select()) {
deselect();
res = RES_OK;
}
break;
case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */
if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */
csize = csd[9] + ((WORD)csd[8] << 8) + 1;
*(DWORD*)buff = (DWORD)csize << 10;
} else { /* SDC ver 1.XX or MMC*/
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
*(DWORD*)buff = (DWORD)csize << (n - 9);
}
res = RES_OK;
}
break;
case GET_SECTOR_SIZE : /* Get R/W sector size (WORD) */
*(WORD*)buff = 512;
res = RES_OK;
break;
case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */
if (CardType & CT_SD2) { /* SDv2? */
if (send_cmd(ACMD13, 0) == 0) { /* Read SD status */
rcvr_spi();
if (rcvr_datablock(csd, 16)) { /* Read partial block */
for (n = 64 - 16; n; n--) rcvr_spi(); /* Purge trailing data */
*(DWORD*)buff = 16UL << (csd[10] >> 4);
res = RES_OK;
}
}
} else { /* SDv1 or MMCv3 */
if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { /* Read CSD */
if (CardType & CT_SD1) { /* SDv1 */
*(DWORD*)buff = (((csd[10] & 63) << 1) + ((WORD)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1);
} else { /* MMCv3 */
*(DWORD*)buff = ((WORD)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1);
}
res = RES_OK;
}
}
break;
case MMC_GET_TYPE : /* Get card type flags (1 byte) */
*ptr = CardType;
res = RES_OK;
break;
case MMC_GET_CSD : /* Receive CSD as a data block (16 bytes) */
if (send_cmd(CMD9, 0) == 0 /* READ_CSD */
&& rcvr_datablock(ptr, 16))
res = RES_OK;
break;
case MMC_GET_CID : /* Receive CID as a data block (16 bytes) */
if (send_cmd(CMD10, 0) == 0 /* READ_CID */
&& rcvr_datablock(ptr, 16))
res = RES_OK;
break;
case MMC_GET_OCR : /* Receive OCR as an R3 resp (4 bytes) */
if (send_cmd(CMD58, 0) == 0) { /* READ_OCR */
for (n = 4; n; n--) *ptr++ = rcvr_spi();
res = RES_OK;
}
break;
case MMC_GET_SDSTAT : /* Receive SD statsu as a data block (64 bytes) */
if (send_cmd(ACMD13, 0) == 0) { /* SD_STATUS */
rcvr_spi();
if (rcvr_datablock(ptr, 64))
res = RES_OK;
}
break;
default:
res = RES_PARERR;
}
deselect();
}
return res;
}
/*-----------------------------------------------------------------------*/
/* Device Timer Interrupt Procedure */
/*-----------------------------------------------------------------------*/
/* This function must be called in period of 10ms */
void disk_timerproc (void)
{
BYTE s;
/*
n = Timer1; // 100Hz decrement timer
if (n) Timer1 = --n;
n = Timer2;
if (n) Timer2 = --n;
*/
if (Timer1) Timer1--;
if (Timer2) Timer2--;
s = Stat;
/* G: Not implemented
if (SOCKWP) // Write protected
s |= STA_PROTECT;
else // Write enabled
s &= ~STA_PROTECT;
if (SOCKINS) // Card inserted
s &= ~STA_NODISK;
else // Socket empty
s |= (STA_NODISK | STA_NOINIT);
*/
s &= ~STA_NODISK;
s &= ~STA_PROTECT;
Stat = s;
}

103
src/diskio.h Normal file
View file

@ -0,0 +1,103 @@
/*
* 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.
*
*/
/*-----------------------------------------------------------------------
/ Low level disk interface modlue include file (C)ChaN, 2010
/-----------------------------------------------------------------------*/
#ifndef DEF_DISKIO
#define DEF_DISKIO
#include "integer.h"
#define DN_MCI 0 /* Physical drive number for MCI */
#define DN_NAND 1 /* Physical drive number for NAND flash */
/* These functions are defined in asmfunc.S */
void Copy_al2un (BYTE *dst, const DWORD *src, int count); /* Copy aligned to unaligned. */
void Copy_un2al (DWORD *dst, const BYTE *src, int count); /* Copy unaligned to aligned. */
/* Status of Disk Functions */
typedef BYTE DSTATUS;
/* Results of Disk Functions */
typedef enum {
RES_OK = 0, /* 0: Successful */
RES_ERROR, /* 1: R/W Error */
RES_WRPRT, /* 2: Write Protected */
RES_NOTRDY, /* 3: Not Ready */
RES_PARERR /* 4: Invalid Parameter */
} DRESULT;
/*---------------------------------------*/
/* Prototypes for disk control functions */
DSTATUS disk_initialize (BYTE);
DSTATUS disk_status (BYTE);
DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE);
DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE);
DRESULT disk_ioctl (BYTE, BYTE, BYTE*);
void disk_timerproc (void);
/* Disk Status Bits (DSTATUS) */
#define STA_NOINIT 0x01 /* Drive not initialized */
#define STA_NODISK 0x02 /* No medium in the drive */
#define STA_PROTECT 0x04 /* Write protected */
/* Command code for disk_ioctrl fucntion */
/* Generic ioctl command (defined for FatFs) */
#define CTRL_SYNC 0 /* Flush disk cache (for write functions) */
#define GET_SECTOR_COUNT 1 /* Get media size (for only f_mkfs()) */
#define GET_SECTOR_SIZE 2 /* Get sector size (for multiple sector size (_MAX_SS >= 1024)) */
#define GET_BLOCK_SIZE 3 /* Get erase block size (for only f_mkfs()) */
#define CTRL_ERASE_SECTOR 4 /* Force erased a block of sectors (for only _USE_ERASE) */
/* Generic ioctl command */
#define CTRL_POWER 5 /* Get/Set power status */
#define CTRL_LOCK 6 /* Lock/Unlock media removal */
#define CTRL_EJECT 7 /* Eject media */
/* MMC/SDC specific ioctl command */
#define MMC_GET_TYPE 10 /* Get card type */
#define MMC_GET_CSD 11 /* Get CSD */
#define MMC_GET_CID 12 /* Get CID */
#define MMC_GET_OCR 13 /* Get OCR */
#define MMC_GET_SDSTAT 14 /* Get SD status */
/* ATA/CF specific ioctl command */
#define ATA_GET_REV 20 /* Get F/W revision */
#define ATA_GET_MODEL 21 /* Get model name */
#define ATA_GET_SN 22 /* Get serial number */
/* NAND specific ioctl command */
#define NAND_FORMAT 30 /* Create physical format */
volatile extern BYTE Timer1, Timer2; /* 100Hz decrement timer */
#endif

445
src/drivers.cpp Normal file
View file

@ -0,0 +1,445 @@
/*
* 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.
*
*/
#include "gruvin9x.h"
#ifndef SIMU
#ifdef ASYNC_WRITE
uint16_t eeprom_pointer;
const char* eeprom_buffer_data;
volatile size_t eeprom_buffer_size = 0;
inline void eeprom_write_byte()
{
EEAR = eeprom_pointer;
EEDR = *eeprom_buffer_data;
#if defined (PCBV3)
EECR |= 1<<EEMPE;
EECR |= 1<<EEPE;
#else
EECR |= 1<<EEMWE;
EECR |= 1<<EEWE;
#endif
eeprom_pointer++;
eeprom_buffer_data++;
}
ISR(EE_READY_vect)
{
if (--eeprom_buffer_size > 0) {
eeprom_write_byte();
}
else {
#if defined (PCBV3)
EECR &= ~(1<<EERIE);
#else
EECR &= ~(1<<EERIE);
#endif
}
}
void eeWriteBlockCmp(const void *i_pointer_ram, void *i_pointer_eeprom, size_t size)
{
assert(!eeprom_buffer_size);
eeprom_pointer = (uint16_t)i_pointer_eeprom;
eeprom_buffer_data = (const char*)i_pointer_ram;
eeprom_buffer_size = size;
#if defined (PCBV3)
EECR |= (1<<EERIE);
#else
EECR |= (1<<EERIE);
#endif
while (s_sync_write && eeprom_buffer_size > 0);
}
#else
///opt/cross/avr/include/avr/eeprom.h
static inline void __attribute__ ((always_inline))
eeprom_write_byte_cmp (uint8_t dat, uint16_t pointer_eeprom)
{
//see /home/thus/work/avr/avrsdk4/avr-libc-1.4.4/libc/misc/eeprom.S:98 143
#if defined (PCBV3)
while(EECR & (1<<EEPE))
#else
while(EECR & (1<<EEWE))
#endif
;
EEAR = pointer_eeprom;
EECR |= 1<<EERE;
if(dat == EEDR) return;
EEDR = dat;
uint8_t flags=SREG;
cli();
#if defined (PCBV3)
EECR |= 1<<EEMPE;
EECR |= 1<<EEPE;
#else
EECR |= 1<<EEMWE;
EECR |= 1<<EEWE;
#endif
SREG = flags;
sei();
}
void eeWriteBlockCmp(const void *i_pointer_ram, void *i_pointer_eeprom, size_t size)
{
const char* pointer_ram = (const char*)i_pointer_ram;
uint16_t pointer_eeprom = (uint16_t)i_pointer_eeprom;
while(size){
eeprom_write_byte_cmp(*pointer_ram++,pointer_eeprom++);
size--;
}
}
#endif
#endif
static uint8_t s_evt;
void putEvent(uint8_t evt)
{
s_evt = evt;
}
uint8_t getEvent()
{
uint8_t evt = s_evt;
s_evt=0;
return evt;
}
class Key
{
#define FILTERBITS 4
#ifdef SIMU
#define FFVAL 1
#else
#define FFVAL ((1<<FILTERBITS)-1)
#endif
#define KSTATE_OFF 0
#define KSTATE_RPTDELAY 95 // gruvin: delay state before key repeating starts
//#define KSTATE_SHORT 96
#define KSTATE_START 97
#define KSTATE_PAUSE 98
#define KSTATE_KILLED 99
uint8_t m_vals:FILTERBITS; // key debounce? 4 = 40ms
uint8_t m_dblcnt:2;
uint8_t m_cnt;
uint8_t m_state;
public:
void input(bool val, EnumKeys enuk);
bool state() { return m_vals==FFVAL; }
void pauseEvents() { m_state = KSTATE_PAUSE; m_cnt = 0;}
void killEvents() { m_state = KSTATE_KILLED; m_dblcnt=0; }
uint8_t getDbl() { return m_dblcnt; }
};
Key keys[NUM_KEYS];
void Key::input(bool val, EnumKeys enuk)
{
// uint8_t old=m_vals;
m_vals <<= 1; if(val) m_vals |= 1; //portbit einschieben
m_cnt++;
if(m_state && m_vals==0){ //gerade eben sprung auf 0
if(m_state!=KSTATE_KILLED) {
putEvent(EVT_KEY_BREAK(enuk));
if(!( m_state == 16 && m_cnt<16)){
m_dblcnt=0;
}
// }
}
m_cnt = 0;
m_state = KSTATE_OFF;
}
switch(m_state){
case KSTATE_OFF:
if(m_vals==FFVAL){ //gerade eben sprung auf ff
m_state = KSTATE_START;
if(m_cnt>16) m_dblcnt=0; //pause zu lang fuer double
m_cnt = 0;
}
break;
//fallthrough
case KSTATE_START:
putEvent(EVT_KEY_FIRST(enuk));
m_dblcnt++;
m_state = KSTATE_RPTDELAY;
m_cnt = 0;
break;
case KSTATE_RPTDELAY: // gruvin: delay state before first key repeat
if(m_cnt == 24) putEvent(EVT_KEY_LONG(enuk));
if (m_cnt == 40) {
m_state = 16;
m_cnt = 0;
}
break;
case 16:
case 8:
case 4:
case 2:
if(m_cnt >= 48) { //3 6 12 24 48 pulses in every 480ms
m_state >>= 1;
m_cnt = 0;
}
//fallthrough
case 1:
if( (m_cnt & (m_state-1)) == 0) putEvent(EVT_KEY_REPT(enuk));
break;
case KSTATE_PAUSE: //pause
if(m_cnt >= 64) {
m_state = 8;
m_cnt = 0;
}
break;
case KSTATE_KILLED: //killed
break;
}
}
bool keyState(EnumKeys enuk)
{
if(enuk < (int)DIM(keys)) return keys[enuk].state() ? 1 : 0;
switch(enuk){
case SW_ElevDR : return PINE & (1<<INP_E_ElevDR);
#if defined(JETI) || defined(FRSKY)
case SW_AileDR : return PINC & (1<<INP_C_AileDR); //shad974: rerouted inputs to free up UART0
#else
case SW_AileDR : return PINE & (1<<INP_E_AileDR);
#endif
case SW_RuddDR : return PING & (1<<INP_G_RuddDR);
// INP_G_ID1 INP_E_ID2
// id0 0 1
// id1 1 1
// id2 1 0
case SW_ID0 : return !(PING & (1<<INP_G_ID1));
case SW_ID1 : return (PING & (1<<INP_G_ID1))&& (PINE & (1<<INP_E_ID2));
case SW_ID2 : return !(PINE & (1<<INP_E_ID2));
case SW_Gear : return PINE & (1<<INP_E_Gear);
//case SW_ThrCt : return PINE & (1<<INP_E_ThrCt);
#if defined(JETI) || defined(FRSKY)
case SW_ThrCt : return PINC & (1<<INP_C_ThrCt); //shad974: rerouted inputs to free up UART0
#else
case SW_ThrCt : return PINE & (1<<INP_E_ThrCt);
#endif
case SW_Trainer: return PINE & (1<<INP_E_Trainer);
default:;
}
return 0;
}
void pauseEvents(uint8_t event)
{
event=event & EVT_KEY_MASK;
if(event < (int)DIM(keys)) keys[event].pauseEvents();
}
void killEvents(uint8_t event)
{
event=event & EVT_KEY_MASK;
if(event < (int)DIM(keys)) keys[event].killEvents();
}
uint8_t getEventDbl(uint8_t event)
{
event=event & EVT_KEY_MASK;
if(event < (int)DIM(keys)) return keys[event].getDbl();
return 0;
}
//uint16_t g_anaIns[8];
volatile uint16_t g_tmr10ms;
volatile uint8_t g_blinkTmr10ms;
#if defined (PCBV3)
uint8_t g_ms100 = 0; // global to allow time set function to reset to zero
#endif
void per10ms()
{
g_tmr10ms++;
g_blinkTmr10ms++;
#if defined (PCBV3)
/* Update gloabal Date/Time every 100 per10ms cycles */
if (++g_ms100 == 100)
{
g_unixTime++; // inc global unix timestamp one second
g_ms100 = 0;
}
#endif
/**** BEGIN KEY STATE READ ****/
uint8_t enuk = KEY_MENU;
#if defined (PCBV3)
/* Original keys were connected to PORTB as follows:
Bit Key
7 other use
6 LEFT
5 RIGHT
4 UP
3 DOWN
2 EXIT
1 MENU
0 other use
*/
#define KEY_Y0 1 // EXIT / MENU
#define KEY_Y1 2 // LEFT / RIGHT / UP / DOWN
#define KEY_Y2 4 // LV_Trim_Up / Down / LH_Trim_Up / Down
#define KEY_Y3 8 // RV_Trim_Up / Down / RH_Trim_Up / Down
#define TRIM_M_LH_DWN 0 // KEY_X0
#define TRIM_M_LH_UP 1 // KEY_X1
#define TRIM_M_LV_DWN 2 // KEY_X2
#define TRIM_M_LV_UP 3 // KEY_X3
#define TRIM_M_RV_DWN 4 // KEY_X0
#define TRIM_M_RV_UP 5 // KEY_X1
#define TRIM_M_RH_DWN 6 // KEY_X2
#define TRIM_M_RH_UP 7 // KEY_3X
uint8_t in, tin;
in = keyDown(); // in gruvin9x.cpp
#else
uint8_t in = ~PINB;
#endif
for(int i=1; i<7; i++)
{
//INP_B_KEY_MEN 1 .. INP_B_KEY_LFT 6
keys[enuk].input(in & (1<<i),(EnumKeys)enuk);
++enuk;
}
#if defined (PCBV3)
static prog_uchar APM crossTrim[]={
1<<TRIM_M_RV_DWN,
1<<TRIM_M_RV_UP,
1<<TRIM_M_RH_DWN,
1<<TRIM_M_RH_UP,
1<<TRIM_M_LH_DWN,
1<<TRIM_M_LH_UP,
1<<TRIM_M_LV_DWN,
1<<TRIM_M_LV_UP
};
#else
static prog_uchar APM crossTrim[]={
1<<INP_D_TRM_LH_DWN, // bit 7
1<<INP_D_TRM_LH_UP,
1<<INP_D_TRM_LV_DWN,
1<<INP_D_TRM_LV_UP,
1<<INP_D_TRM_RV_DWN,
1<<INP_D_TRM_RV_UP,
1<<INP_D_TRM_RH_DWN,
1<<INP_D_TRM_RH_UP // bit 0
};
#endif
#if defined (PCBV3)
PORTD = ~KEY_Y2; // select Y2 row. (Bits 3:0 LVD / LVU / LHU / LHD)
_delay_us(1);
in = ~PIND & 0xf0; // mask out outputs
PORTD = ~KEY_Y3; // select Y3 row. (Bits 3:0 RHU / RHD / RVD / RVU)
_delay_us(1);
in |= ((~PIND & 0xf0) >> 4);
PORTD = 0xFF;
#else
in = ~PIND;
// Legacy support for USART1 free hardware mod [DEPRECATED]
#if defined(USART1FREED)
// mask out original INP_D_TRM_LV_UP and INP_D_TRM_LV_DWN bits
in &= ~((1<<INP_D_TRM_LV_UP) | (1<<INP_D_TRM_LV_DWN));
// merge in the two new switch port values
in |= (~PINC & (1<<INP_C_TRM_LV_UP)) ? (1<<INP_D_TRM_LV_UP) : 0;
in |= (~PING & (1<<INP_G_TRM_LV_DWN)) ? (1<<INP_D_TRM_LV_DWN) : 0;
#endif
#endif
for(int i=0; i<8; i++)
{
// INP_D_TRM_RH_UP 0 .. INP_D_TRM_LH_UP 7
keys[enuk].input(in & pgm_read_byte(crossTrim+i),(EnumKeys)enuk);
++enuk;
}
/**** END KEY STATE READ ****/
#if defined (FRSKY)
// Used to detect presence of valid FrSky telemetry packets inside the
// last <FRSKY_TIMEOUT10ms> 10ms intervals
if ( FrskyAlarmSendState )
FRSKY10mspoll() ;
if (frskyStreaming > 0)
frskyStreaming--;
else if (g_eeGeneral.enableTelemetryAlarm && (g_model.frsky.channels[0].ratio || g_model.frsky.channels[1].ratio)) {
#if defined (BEEPSPKR)
if (!(g_tmr10ms % 30)) beepWarn2Spkr((g_tmr10ms % 60) ? 25 : 20);
#else
if (!(g_tmr10ms % 30))
{
warble = !(g_tmr10ms % 60);
beepWarn2();
}
#endif
}
#endif
// These moved here from perOut() [gruvin9x.cpp] to improve beep trigger reliability.
#if defined (BEEPSPKR)
if(mixWarning & 1) if(((g_tmr10ms&0xFF)== 0)) beepWarn1Spkr(BEEP_DEFAULT_FREQ+7);
if(mixWarning & 2) if(((g_tmr10ms&0xFF)== 64)
|| ((g_tmr10ms&0xFF)== 72)) beepWarn1Spkr(BEEP_DEFAULT_FREQ+9);
if(mixWarning & 4) if(((g_tmr10ms&0xFF)==128) || ((g_tmr10ms&0xFF)==136)
|| ((g_tmr10ms&0xFF)==144)) beepWarn1Spkr(BEEP_DEFAULT_FREQ+11);
#else
if(mixWarning & 1) if(((g_tmr10ms&0xFF)== 0)) beepWarn1();
if(mixWarning & 2) if(((g_tmr10ms&0xFF)== 64) || ((g_tmr10ms&0xFF)== 72)) beepWarn1();
if(mixWarning & 4) if(((g_tmr10ms&0xFF)==128) || ((g_tmr10ms&0xFF)==136) || ((g_tmr10ms&0xFF)==144)) beepWarn1();
#endif
}

135
src/eeprom_v3.h Normal file
View file

@ -0,0 +1,135 @@
#ifndef eepromv3_h
#define eepromv3_h
#include <inttypes.h>
namespace EEPROM_V3 {
typedef struct t_TrainerMix {
uint8_t srcChn:3; //0-7 = ch1-8
int8_t swtch:5;
int8_t studWeight:6;
uint8_t mode:2; //off,add-mode,subst-mode
} __attribute__((packed)) TrainerMix; //
typedef struct t_TrainerData {
int16_t calib[4];
TrainerMix mix[4];
} __attribute__((packed)) TrainerData;
typedef struct t_EEGeneral {
uint8_t myVers;
int16_t calibMid[7];
int16_t calibSpanNeg[7];
int16_t calibSpanPos[7];
uint16_t chkSum;
uint8_t currModel; //0..15
uint8_t contrast;
uint8_t vBatWarn;
int8_t vBatCalib;
int8_t lightSw;
TrainerData trainer;
uint8_t view; //index of subview in main scrren
// uint8_t warnOpts; //bitset for several warnings
uint8_t disableThrottleWarning:1;
uint8_t disableSwitchWarning:1;
uint8_t disableMemoryWarning:1;
uint8_t beeperVal:3;
uint8_t reserveWarning:1;
uint8_t disableAlarmWarning:1;
uint8_t stickMode;
uint8_t inactivityTimer;
uint8_t throttleReversed:1;
uint8_t minuteBeep:1;
uint8_t preBeep:1;
uint8_t flashBeep:1;
uint8_t disableSplashScreen:1;
uint8_t res1:3;
uint8_t filterInput;
uint8_t lightAutoOff;
uint8_t templateSetup; //RETA order according to chout_ar array
int8_t PPM_Multiplier;
uint8_t res[1];
} __attribute__((packed)) EEGeneral;
//eeprom modelspec
//expo[3][2][2] //[Norm/Dr][expo/weight][R/L]
typedef struct t_ExpoData {
int8_t expo[3][2][2];
int8_t drSw1;
int8_t drSw2;
} __attribute__((packed)) ExpoData;
typedef struct t_LimitData {
int8_t min;
int8_t max;
bool revert;
int16_t offset;
} __attribute__((packed)) LimitData;
typedef struct t_MixData {
uint8_t destCh; // 1..NUM_CHNOUT
uint8_t srcRaw; //
int8_t weight;
int8_t swtch;
uint8_t curve; //0=symmetrisch 1=no neg 2=no pos
uint8_t delayUp:4;
uint8_t delayDown:4;
uint8_t speedUp:4; // Servogeschwindigkeit aus Tabelle (10ms Cycle)
uint8_t speedDown:4; // 0 nichts
uint8_t carryTrim:1;
uint8_t mltpx:3; // multiplex method 0=+ 1=* 2=replace
uint8_t mixWarn:4; // mixer warning
int8_t sOffset;
int8_t res;
} __attribute__((packed)) MixData;
typedef struct t_CSwData { // Custom Switches data
int8_t v1; //input
int8_t v2; //offset
uint8_t func;
} __attribute__((packed)) CSwData;
typedef struct t_SwashRingData { // Swash Ring data
uint8_t lim; // 0 mean off 100 full deflection
uint8_t chX; // 2 channels to limit
uint8_t chY; // 2 channels to limit
} __attribute__((packed)) SwashRingData;
typedef struct t_ModelData {
char name[10]; // 10 must be first for eeLoadModelName
uint8_t mdVers;
int8_t tmrMode; // timer trigger source -> off, abs, stk, stk%, sw/!sw, !m_sw/!m_sw
uint8_t tmrDir; // 0=>Count Down, 1=>Count Up
uint16_t tmrVal;
uint8_t protocol;
int8_t ppmNCH;
int8_t thrTrim:4; // Enable Throttle Trim
int8_t thrExpo:4; // Enable Throttle Expo
int8_t trimInc; // Trim Increments
int8_t ppmDelay;
int8_t trimSw;
uint8_t beepANACenter; // 1<<0->A1.. 1<<6->A7
uint8_t pulsePol;
char res[3];
MixData mixData[32];
LimitData limitData[16];
ExpoData expoData[4];
int8_t trim[4];
int8_t curves5[8][5];
int8_t curves9[8][9];
CSwData customSw[6];
SwashRingData swashR;
} __attribute__((packed)) ModelData;
}
#endif

158
src/eeprom_v4.h Normal file
View file

@ -0,0 +1,158 @@
#ifndef eepromv4_h
#define eepromv4_h
#include <inttypes.h>
namespace EEPROM_V4 {
typedef struct t_TrainerMix {
uint8_t srcChn:3; //0-7 = ch1-8
int8_t swtch:5;
int8_t studWeight:6;
uint8_t mode:2; //off,add-mode,subst-mode
} __attribute__((packed)) TrainerMix; //
typedef struct t_TrainerData {
int16_t calib[4];
TrainerMix mix[4];
} __attribute__((packed)) TrainerData;
typedef struct t_EEGeneral {
uint8_t myVers;
int16_t calibMid[7];
int16_t calibSpanNeg[7];
int16_t calibSpanPos[7];
uint16_t chkSum;
uint8_t currModel; //0..15
uint8_t contrast;
uint8_t vBatWarn;
int8_t vBatCalib;
int8_t lightSw;
TrainerData trainer;
uint8_t view; //index of subview in main scrren
uint8_t disableThrottleWarning:1;
uint8_t disableSwitchWarning:1;
uint8_t disableMemoryWarning:1;
uint8_t beeperVal:3;
uint8_t reserveWarning:1;
uint8_t disableAlarmWarning:1;
uint8_t stickMode;
uint8_t inactivityTimer;
uint8_t throttleReversed:1;
uint8_t minuteBeep:1;
uint8_t preBeep:1;
uint8_t flashBeep:1;
uint8_t disableSplashScreen:1;
uint8_t res1:3;
uint8_t filterInput;
uint8_t lightAutoOff;
uint8_t templateSetup; //RETA order according to chout_ar array
int8_t PPM_Multiplier;
uint8_t res[1];
} __attribute__((packed)) EEGeneral;
//eeprom modelspec
typedef struct t_ExpoData {
int8_t expo[3][2][2]; //[Norm/Dr][expo/weight][R/L]
int8_t drSw1;
int8_t drSw2;
} __attribute__((packed)) ExpoData;
typedef struct t_LimitData {
int8_t min;
int8_t max;
bool revert;
int16_t offset;
} __attribute__((packed)) LimitData;
typedef struct t_MixData {
uint8_t destCh; // 1..NUM_CHNOUT
uint8_t srcRaw; //
int8_t weight;
int8_t swtch;
uint8_t curve; //0=symmetrisch 1=no neg 2=no pos
uint8_t delayUp:4;
uint8_t delayDown:4;
uint8_t speedUp:4; // Servogeschwindigkeit aus Tabelle (10ms Cycle)
uint8_t speedDown:4; // 0 nichts
uint8_t carryTrim:1;
uint8_t mltpx:3; // multiplex method 0=+ 1=* 2=replace
uint8_t mixWarn:4; // mixer warning
int8_t sOffset;
int8_t res;
} __attribute__((packed)) MixData;
typedef struct t_CSwData { // Custom Switches data
int8_t v1; //input
int8_t v2; //offset
uint8_t func;
} __attribute__((packed)) CSwData;
typedef struct t_SwashRingData { // Swash Ring data
uint8_t lim; // 0 mean off 100 full deflection
uint8_t chX; // 2 channels to limit
uint8_t chY; // 2 channels to limit
} __attribute__((packed)) SwashRingData;
typedef struct t_SafetySwData { // Custom Switches data
int8_t swtch;
int8_t val;
} __attribute__((packed)) SafetySwData;
typedef struct t_FrSkyChannelData {
uint8_t ratio; // 0.0 means not used, 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc.
uint8_t alarms_value[2]; // 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc.
uint8_t alarms_level:4;
uint8_t alarms_greater:2; // 0=LT(<), 1=GT(>)
uint8_t type:2; // future use: 0=volts, ...
} __attribute__((packed)) FrSkyChannelData;
typedef struct t_FrSkyData {
FrSkyChannelData channels[2];
} __attribute__((packed)) FrSkyData;
typedef struct t_ModelData {
char name[10]; // 10 must be first for eeLoadModelName
uint8_t mdVers;
int8_t tmrMode; // timer trigger source -> off, abs, stk, stk%, sw/!sw, !m_sw/!m_sw
uint8_t tmrDir:1; //0=>Count Down, 1=>Count Up
uint8_t traineron:1; // 0 disable trainer, 1 allow trainer
uint8_t spare:6;
uint16_t tmrVal;
uint8_t protocol;
int8_t ppmNCH;
int8_t thrTrim:4; // Enable Throttle Trim
int8_t thrExpo:4; // Enable Throttle Expo
int8_t trimInc; // Trim Increments
int8_t ppmDelay;
int8_t trimSw;
uint8_t beepANACenter; // 1<<0->A1.. 1<<6->A7
uint8_t pulsePol:1;
uint8_t extendedLimits:1;
uint8_t swashInvertELE:1;
uint8_t swashInvertAIL:1;
uint8_t swashInvertCOL:1;
uint8_t swashType:3;
uint8_t swashCollectiveSource;
uint8_t swashRingValue;
char res1;
MixData mixData[32];
LimitData limitData[16];
ExpoData expoData[4];
int8_t trim[4];
int8_t curves5[8][5];
int8_t curves9[8][9];
CSwData customSw[12/*er9x=12, gruvin9x=6*/];
/* er9x only */
uint8_t a1voltRatio; //FrSky
uint8_t a2voltRatio; //FrSky
uint8_t res3;
SafetySwData safetySw[16];
FrSkyData frsky;
} __attribute__((packed)) ModelData;
}
#endif

3736
src/ff.cpp Normal file

File diff suppressed because it is too large Load diff

541
src/ff.h Normal file
View file

@ -0,0 +1,541 @@
/*---------------------------------------------------------------------------/
/ FatFs - FAT file system module include file R0.08a (C)ChaN, 2010
/----------------------------------------------------------------------------/
/ FatFs module is a generic FAT file system module for small embedded systems.
/ This is a free software that opened for education, research and commercial
/ developments under license policy of following trems.
/
/ Copyright (C) 2010, ChaN, all right reserved.
/
/ * The FatFs module is a free software and there is NO WARRANTY.
/ * No restriction on use. You can use, modify and redistribute it for
/ personal, non-profit or commercial product UNDER YOUR RESPONSIBILITY.
/ * Redistributions of source code must retain the above copyright notice.
/
/----------------------------------------------------------------------------*/
#ifndef _FATFS
#define _FATFS 8255 /* Revision ID */
#ifdef __cplusplus
extern "C" {
#endif
#include "integer.h" /* Basic integer types */
#include "ffconf.h" /* FatFs configuration options */
#if _FATFS != _FFCONF
#error Wrong configuration file (ffconf.h).
#endif
/* DBCS code ranges and SBCS extend char conversion table */
#if _CODE_PAGE == 932 /* Japanese Shift-JIS */
#define _DF1S 0x81 /* DBC 1st byte range 1 start */
#define _DF1E 0x9F /* DBC 1st byte range 1 end */
#define _DF2S 0xE0 /* DBC 1st byte range 2 start */
#define _DF2E 0xFC /* DBC 1st byte range 2 end */
#define _DS1S 0x40 /* DBC 2nd byte range 1 start */
#define _DS1E 0x7E /* DBC 2nd byte range 1 end */
#define _DS2S 0x80 /* DBC 2nd byte range 2 start */
#define _DS2E 0xFC /* DBC 2nd byte range 2 end */
#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */
#define _DF1S 0x81
#define _DF1E 0xFE
#define _DS1S 0x40
#define _DS1E 0x7E
#define _DS2S 0x80
#define _DS2E 0xFE
#elif _CODE_PAGE == 949 /* Korean */
#define _DF1S 0x81
#define _DF1E 0xFE
#define _DS1S 0x41
#define _DS1E 0x5A
#define _DS2S 0x61
#define _DS2E 0x7A
#define _DS3S 0x81
#define _DS3E 0xFE
#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */
#define _DF1S 0x81
#define _DF1E 0xFE
#define _DS1S 0x40
#define _DS1E 0x7E
#define _DS2S 0xA1
#define _DS2E 0xFE
#elif _CODE_PAGE == 437 /* U.S. (OEM) */
#define _DF1S 0
#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
#elif _CODE_PAGE == 720 /* Arabic (OEM) */
#define _DF1S 0
#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
#elif _CODE_PAGE == 737 /* Greek (OEM) */
#define _DF1S 0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
#elif _CODE_PAGE == 775 /* Baltic (OEM) */
#define _DF1S 0
#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
#elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */
#define _DF1S 0
#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
#elif _CODE_PAGE == 852 /* Latin 2 (OEM) */
#define _DF1S 0
#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \
0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}
#elif _CODE_PAGE == 855 /* Cyrillic (OEM) */
#define _DF1S 0
#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \
0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}
#elif _CODE_PAGE == 857 /* Turkish (OEM) */
#define _DF1S 0
#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
#elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */
#define _DF1S 0
#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
#elif _CODE_PAGE == 862 /* Hebrew (OEM) */
#define _DF1S 0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
#elif _CODE_PAGE == 866 /* Russian (OEM) */
#define _DF1S 0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
#elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */
#define _DF1S 0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */
#define _DF1S 0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}
#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */
#define _DF1S 0
#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF}
#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */
#define _DF1S 0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \
0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}
#elif _CODE_PAGE == 1253 /* Greek (Windows) */
#define _DF1S 0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \
0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF}
#elif _CODE_PAGE == 1254 /* Turkish (Windows) */
#define _DF1S 0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \
0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}
#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */
#define _DF1S 0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
#elif _CODE_PAGE == 1256 /* Arabic (Windows) */
#define _DF1S 0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \
0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF}
#elif _CODE_PAGE == 1257 /* Baltic (Windows) */
#define _DF1S 0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}
#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */
#define _DF1S 0
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \
0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F}
#elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */
#define _DF1S 0
#else
#error Unknown code page
#endif
/* Definitions of volume management */
#if _MULTI_PARTITION /* Multiple partition configuration */
#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive# */
#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition# */
typedef struct {
BYTE pd; /* Physical drive# */
BYTE pt; /* Partition # (0-3) */
} PARTITION;
extern const PARTITION VolToPart[]; /* Volume - Physical location resolution table */
#else /* Single partition configuration */
#define LD2PD(vol) (vol) /* Logical drive# is bound to the same physical drive# */
#define LD2PT(vol) 0 /* Always mounts the 1st partition */
#endif
/* Type of path name strings on FatFs API */
#if _LFN_UNICODE /* Unicode string */
#if !_USE_LFN
#error _LFN_UNICODE must be 0 in non-LFN cfg.
#endif
#ifndef _INC_TCHAR
typedef WCHAR TCHAR;
#define _T(x) L ## x
#define _TEXT(x) L ## x
#endif
#else /* ANSI/OEM string */
#ifndef _INC_TCHAR
typedef char TCHAR;
#define _T(x) x
#define _TEXT(x) x
#endif
#endif
/* File system object structure (FATFS) */
typedef struct {
BYTE fs_type; /* FAT sub-type (0:Not mounted) */
BYTE drv; /* Physical drive number */
BYTE csize; /* Sectors per cluster (1,2,4...128) */
BYTE n_fats; /* Number of FAT copies (1,2) */
BYTE wflag; /* win[] dirty flag (1:must be written back) */
BYTE fsi_flag; /* fsinfo dirty flag (1:must be written back) */
WORD id; /* File system mount ID */
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
#if _MAX_SS != 512
WORD ssize; /* Bytes per sector (512,1024,2048,4096) */
#endif
#if _FS_REENTRANT
_SYNC_t sobj; /* Identifier of sync object */
#endif
#if !_FS_READONLY
DWORD last_clust; /* Last allocated cluster */
DWORD free_clust; /* Number of free clusters */
DWORD fsi_sector; /* fsinfo sector (FAT32) */
#endif
#if _FS_RPATH
DWORD cdir; /* Current directory start cluster (0:root) */
#endif
DWORD n_fatent; /* Number of FAT entries (= number of clusters + 2) */
DWORD fsize; /* Sectors per FAT */
DWORD fatbase; /* FAT start sector */
DWORD dirbase; /* Root directory start sector (FAT32:Cluster#) */
DWORD database; /* Data start sector */
DWORD winsect; /* Current sector appearing in the win[] */
BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and Data on tiny cfg) */
} FATFS;
/* File object structure (FIL) */
typedef struct {
FATFS* fs; /* Pointer to the owner file system object */
WORD id; /* Owner file system mount ID */
BYTE flag; /* File status flags */
BYTE pad1;
DWORD fptr; /* File read/write pointer (0 on file open) */
DWORD fsize; /* File size */
DWORD org_clust; /* File start cluster (0 when fsize==0) */
DWORD curr_clust; /* Current cluster */
DWORD dsect; /* Current data sector */
#if !_FS_READONLY
DWORD dir_sect; /* Sector containing the directory entry */
BYTE* dir_ptr; /* Ponter to the directory entry in the window */
#endif
#if _USE_FASTSEEK
DWORD* cltbl; /* Pointer to the cluster link map table (null on file open) */
#endif
#if _FS_SHARE
UINT lockid; /* File lock ID (index of file semaphore table) */
#endif
#if !_FS_TINY
BYTE buf[_MAX_SS]; /* File data read/write buffer */
#endif
} FIL;
/* Directory object structure (DIR) */
typedef struct {
FATFS* fs; /* Pointer to the owner file system object */
WORD id; /* Owner file system mount ID */
WORD index; /* Current read/write index number */
DWORD sclust; /* Table start cluster (0:Root dir) */
DWORD clust; /* Current cluster */
DWORD sect; /* Current sector */
BYTE* dir; /* Pointer to the current SFN entry in the win[] */
BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
#if _USE_LFN
WCHAR* lfn; /* Pointer to the LFN working buffer */
WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */
#endif
} DIR;
/* File status structure (FILINFO) */
typedef struct {
DWORD fsize; /* File size */
WORD fdate; /* Last modified date */
WORD ftime; /* Last modified time */
BYTE fattrib; /* Attribute */
TCHAR fname[13]; /* Short file name (8.3 format) */
#if _USE_LFN
TCHAR* lfname; /* Pointer to the LFN buffer */
UINT lfsize; /* Size of LFN buffer in TCHAR */
#endif
} FILINFO;
/* File function return code (FRESULT) */
typedef enum {
FR_OK = 0, // (0) Succeeded
FR_DISK_ERR, // (1) A hard error occured in the low level disk I/O layer
FR_INT_ERR, // (2) Assertion failed
FR_NOT_READY, // (3) The physical drive cannot work
FR_NO_FILE, // (4) Could not find the file
FR_NO_PATH, // (5) Could not find the path
FR_INVALID_NAME, // (6) The path name format is invalid
FR_DENIED, // (7) Acces denied due to prohibited access or directory full
FR_EXIST, // (8) Acces denied due to prohibited access
FR_INVALID_OBJECT, // (9) The file/directory object is invalid
FR_WRITE_PROTECTED, // (10) The physical drive is write protected
FR_INVALID_DRIVE, // (11) The logical drive number is invalid
FR_NOT_ENABLED, // (12) The volume has no work area
FR_NO_FILESYSTEM, // (13) There is no valid FAT volume on the physical drive
FR_MKFS_ABORTED, // (14) The f_mkfs() aborted due to any parameter error
FR_TIMEOUT, // (15) Could not get a grant to access the volume within defined period
FR_LOCKED, // (16) The operation is rejected according to the file shareing policy
FR_NOT_ENOUGH_CORE, // (17) LFN working buffer could not be allocated
FR_TOO_MANY_OPEN_FILES // (18) Number of open files > _FS_SHARE
} FRESULT;
/*--------------------------------------------------------------*/
/* FatFs module application interface */
FRESULT f_mount (BYTE, FATFS*); /* Mount/Unmount a logical drive */
FRESULT f_open (FIL*, const TCHAR*, BYTE); /* Open or create a file */
FRESULT f_read (FIL*, void*, UINT, UINT*); /* Read data from a file */
FRESULT f_lseek (FIL*, DWORD); /* Move file pointer of a file object */
FRESULT f_close (FIL*); /* Close an open file object */
FRESULT f_opendir (DIR*, const TCHAR*); /* Open an existing directory */
FRESULT f_readdir (DIR*, FILINFO*); /* Read a directory item */
FRESULT f_stat (const TCHAR*, FILINFO*); /* Get file status */
#if !_FS_READONLY
FRESULT f_write (FIL*, const void*, UINT, UINT*); /* Write data to a file */
FRESULT f_getfree (const TCHAR*, DWORD*, FATFS**); /* Get number of free clusters on the drive */
FRESULT f_truncate (FIL*); /* Truncate file */
FRESULT f_sync (FIL*); /* Flush cached data of a writing file */
FRESULT f_unlink (const TCHAR*); /* Delete an existing file or directory */
FRESULT f_mkdir (const TCHAR*); /* Create a new directory */
FRESULT f_chmod (const TCHAR*, BYTE, BYTE); /* Change attriburte of the file/dir */
FRESULT f_utime (const TCHAR*, const FILINFO*); /* Change timestamp of the file/dir */
FRESULT f_rename (const TCHAR*, const TCHAR*); /* Rename/Move a file or directory */
#endif
#if _USE_FORWARD
FRESULT f_forward (FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*); /* Forward data to the stream */
#endif
#if _USE_MKFS
FRESULT f_mkfs (BYTE, BYTE, UINT); /* Create a file system on the drive */
#endif
#if _FS_RPATH
FRESULT f_chdrive (BYTE); /* Change current drive */
FRESULT f_chdir (const TCHAR*); /* Change current directory */
FRESULT f_getcwd (TCHAR*, UINT); /* Get current directory */
#endif
#if _USE_STRFUNC
int f_putc (TCHAR, FIL*); /* Put a character to the file */
int f_puts (const TCHAR*, FIL*); /* Put a string to the file */
int f_printf (FIL*, const TCHAR*, ...); /* Put a formatted string to the file */
TCHAR* f_gets (TCHAR*, int, FIL*); /* Get a string from the file */
#ifndef EOF
#define EOF (-1)
#endif
#endif
#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0)
#define f_error(fp) (((fp)->flag & FA__ERROR) ? 1 : 0)
#define f_tell(fp) ((fp)->fptr)
#define f_size(fp) ((fp)->fsize)
/*--------------------------------------------------------------*/
/* Additional user defined functions */
/* RTC function */
#if !_FS_READONLY
extern DWORD get_fattime (void); /* Returns 32-bit packed date and time */
#endif
/* Unicode support functions */
#if _USE_LFN /* Unicode - OEM code conversion */
WCHAR ff_convert (WCHAR, UINT); /* OEM-Unicode bidirectional conversion */
WCHAR ff_wtoupper (WCHAR); /* Unicode upper-case conversion */
#if _USE_LFN == 3 /* Memory functions */
void* ff_memalloc (UINT); /* Allocate memory block */
void ff_memfree (void*); /* Free memory block */
#endif
#endif
/* Sync functions */
#if _FS_REENTRANT
int ff_cre_syncobj (BYTE, _SYNC_t*);/* Create a sync object */
int ff_del_syncobj (_SYNC_t); /* Delete a sync object */
int ff_req_grant (_SYNC_t); /* Lock sync object */
void ff_rel_grant (_SYNC_t); /* Unlock sync object */
#endif
/*--------------------------------------------------------------*/
/* Flags and offset address */
/* File access control and file status flags (FIL.flag) */
#define FA_READ 0x01
#define FA_OPEN_EXISTING 0x00
#define FA__ERROR 0x80
#if !_FS_READONLY
#define FA_WRITE 0x02
#define FA_CREATE_NEW 0x04
#define FA_CREATE_ALWAYS 0x08
#define FA_OPEN_ALWAYS 0x10
#define FA__WRITTEN 0x20
#define FA__DIRTY 0x40
#endif
/* FAT sub type (FATFS.fs_type) */
#define FS_FAT12 1
#define FS_FAT16 2
#define FS_FAT32 3
/* File attribute bits for directory entry */
#define AM_RDO 0x01 /* Read only */
#define AM_HID 0x02 /* Hidden */
#define AM_SYS 0x04 /* System */
#define AM_VOL 0x08 /* Volume label */
#define AM_LFN 0x0F /* LFN entry */
#define AM_DIR 0x10 /* Directory */
#define AM_ARC 0x20 /* Archive */
#define AM_MASK 0x3F /* Mask of defined bits */
/* Fast seek function */
#define CREATE_LINKMAP 0xFFFFFFFF
/*--------------------------------*/
/* Multi-byte word access macros */
#if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */
#define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr))
#define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr))
#define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val)
#define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val)
#else /* Use byte-by-byte access to the FAT structure */
#define LD_WORD(ptr) (WORD)(((WORD)*(BYTE*)((ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))
#define LD_DWORD(ptr) (DWORD)(((DWORD)*(BYTE*)((ptr)+3)<<24)|((DWORD)*(BYTE*)((ptr)+2)<<16)|((WORD)*(BYTE*)((ptr)+1)<<8)|*(BYTE*)(ptr))
#define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8)
#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8); *(BYTE*)((ptr)+2)=(BYTE)((DWORD)(val)>>16); *(BYTE*)((ptr)+3)=(BYTE)((DWORD)(val)>>24)
#endif
#ifdef __cplusplus
}
#endif
#endif /* _FATFS */

188
src/ffconf.h Normal file
View file

@ -0,0 +1,188 @@
/*---------------------------------------------------------------------------/
/ FatFs - FAT file system module configuration file R0.08a (C)ChaN, 2010
/----------------------------------------------------------------------------/
/
/ CAUTION! Do not forget to make clean the project after any changes to
/ the configuration options.
/
/----------------------------------------------------------------------------*/
#ifndef _FFCONF
#define _FFCONF 8255 /* Revision ID */
/*---------------------------------------------------------------------------/
/ Function and Buffer Configurations
/----------------------------------------------------------------------------*/
#define _FS_TINY 1 /* 0:Normal or 1:Tiny */
/* When _FS_TINY is set to 1, FatFs uses the sector buffer in the file system
/ object instead of the sector buffer in the individual file object for file
/ data transfer. This reduces memory consumption 512 bytes each file object. */
#define _FS_READONLY 0 /* 0:Read/Write or 1:Read only */
/* Setting _FS_READONLY to 1 defines read only configuration. This removes
/ writing functions, f_write, f_sync, f_unlink, f_mkdir, f_chmod, f_rename,
/ f_truncate and useless f_getfree. */
#define _FS_MINIMIZE 0 /* 0 to 3 */
/* The _FS_MINIMIZE option defines minimization level to remove some functions.
/
/ 0: Full function.
/ 1: f_stat, f_getfree, f_unlink, f_mkdir, f_chmod, f_truncate and f_rename
/ are removed.
/ 2: f_opendir and f_readdir are removed in addition to 1.
/ 3: f_lseek is removed in addition to 2. */
#define _USE_STRFUNC 1 /* 0:Disable or 1/2:Enable */
/* To enable string functions, set _USE_STRFUNC to 1 or 2. */
#define _USE_MKFS 1 /* 0:Disable or 1:Enable */
/* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */
#define _USE_FORWARD 0 /* 0:Disable or 1:Enable */
/* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */
#define _USE_FASTSEEK 0 /* 0:Disable or 1:Enable */
/* To enable fast seek feature, set _USE_FASTSEEK to 1. */
/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/----------------------------------------------------------------------------*/
#define _CODE_PAGE 932
/* The _CODE_PAGE specifies the OEM code page to be used on the target system.
/ Incorrect setting of the code page can cause a file open failure.
/
/ 932 - Japanese Shift-JIS (DBCS, OEM, Windows)
/ 936 - Simplified Chinese GBK (DBCS, OEM, Windows)
/ 949 - Korean (DBCS, OEM, Windows)
/ 950 - Traditional Chinese Big5 (DBCS, OEM, Windows)
/ 1250 - Central Europe (Windows)
/ 1251 - Cyrillic (Windows)
/ 1252 - Latin 1 (Windows)
/ 1253 - Greek (Windows)
/ 1254 - Turkish (Windows)
/ 1255 - Hebrew (Windows)
/ 1256 - Arabic (Windows)
/ 1257 - Baltic (Windows)
/ 1258 - Vietnam (OEM, Windows)
/ 437 - U.S. (OEM)
/ 720 - Arabic (OEM)
/ 737 - Greek (OEM)
/ 775 - Baltic (OEM)
/ 850 - Multilingual Latin 1 (OEM)
/ 858 - Multilingual Latin 1 + Euro (OEM)
/ 852 - Latin 2 (OEM)
/ 855 - Cyrillic (OEM)
/ 866 - Russian (OEM)
/ 857 - Turkish (OEM)
/ 862 - Hebrew (OEM)
/ 874 - Thai (OEM, Windows)
/ 1 - ASCII only (Valid for non LFN cfg.)
*/
#define _USE_LFN 0 /* 0 to 3 */
#define _MAX_LFN 255 /* Maximum LFN length to handle (12 to 255) */
/* The _USE_LFN option switches the LFN support.
/
/ 0: Disable LFN feature. _MAX_LFN and _LFN_UNICODE have no effect.
/ 1: Enable LFN with static working buffer on the BSS. Always NOT reentrant.
/ 2: Enable LFN with dynamic working buffer on the STACK.
/ 3: Enable LFN with dynamic working buffer on the HEAP.
/
/ The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. To enable LFN,
/ Unicode handling functions ff_convert() and ff_wtoupper() must be added
/ to the project. When enable to use heap, memory control functions
/ ff_memalloc() and ff_memfree() must be added to the project. */
#define _LFN_UNICODE 0 /* 0:ANSI/OEM or 1:Unicode */
/* To switch the character code set on FatFs API to Unicode,
/ enable LFN feature and set _LFN_UNICODE to 1. */
#define _FS_RPATH 2 /* 0 to 2 */
/* The _FS_RPATH option configures relative path feature.
/
/ 0: Disable relative path feature and remove related functions.
/ 1: Enable relative path. f_chdrive() and f_chdir() are available.
/ 2: f_getcwd() is available in addition to 1.
/
/ Note that output of the f_readdir fnction is affected by this option. */
/*---------------------------------------------------------------------------/
/ Physical Drive Configurations
/----------------------------------------------------------------------------*/
#define _VOLUMES 1
/* Number of volumes (logical drives) to be used. */
#define _MAX_SS 512 /* 512, 1024, 2048 or 4096 */
/* Maximum sector size to be handled.
/ Always set 512 for memory card and hard disk but a larger value may be
/ required for floppy disk (512/1024) and optical disk (512/2048).
/ When _MAX_SS is larger than 512, GET_SECTOR_SIZE command must be implememted
/ to the disk_ioctl function. */
#define _MULTI_PARTITION 0 /* 0:Single partition or 1:Multiple partition */
/* When set to 0, each volume is bound to the same physical drive number and
/ it can mount only first primaly partition. When it is set to 1, each volume
/ is tied to the partitions listed in VolToPart[]. */
#define _USE_ERASE 0 /* 0:Disable or 1:Enable */
/* To enable sector erase feature, set _USE_ERASE to 1. */
/*---------------------------------------------------------------------------/
/ System Configurations
/----------------------------------------------------------------------------*/
#define _WORD_ACCESS 0 /* 0 or 1 */
/* Set 0 first and it is always compatible with all platforms. The _WORD_ACCESS
/ option defines which access method is used to the word data on the FAT volume.
/
/ 0: Byte-by-byte access.
/ 1: Word access. Do not choose this unless following condition is met.
/
/ When the byte order on the memory is big-endian or address miss-aligned word
/ access results incorrect behavior, the _WORD_ACCESS must be set to 0.
/ If it is not the case, the value can also be set to 1 to improve the
/ performance and code size. */
/* Include a header file here to define sync object types on the O/S */
/* #include <windows.h>, <ucos_ii.h.h>, <semphr.h> or ohters. */
#define _FS_REENTRANT 0 /* 0:Disable or 1:Enable */
#define _FS_TIMEOUT 1000 /* Timeout period in unit of time ticks */
#define _SYNC_t HANDLE /* O/S dependent type of sync object. e.g. HANDLE, OS_EVENT*, ID and etc.. */
/* The _FS_REENTRANT option switches the reentrancy of the FatFs module.
/
/ 0: Disable reentrancy. _SYNC_t and _FS_TIMEOUT have no effect.
/ 1: Enable reentrancy. Also user provided synchronization handlers,
/ ff_req_grant, ff_rel_grant, ff_del_syncobj and ff_cre_syncobj
/ function must be added to the project. */
#define _FS_SHARE 0 /* 0:Disable or >=1:Enable */
/* To enable file shareing feature, set _FS_SHARE to 1 or greater. The value
defines how many files can be opened simultaneously. */
#endif /* _FFCONFIG */

682
src/file.cpp Normal file
View file

@ -0,0 +1,682 @@
/*
*
* gruvin9x Author 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
* based on 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 "gruvin9x.h"
#include "stdio.h"
#include "inttypes.h"
#include "string.h"
uint8_t s_write_err = 0; // error reasons
#ifdef ASYNC_WRITE
uint8_t s_sync_write = false;
#endif
#define RESV 64 //reserv for eeprom header with directory (eeFs)
#define FIRSTBLK (RESV/BS)
#define BLOCKS (EESIZE/BS)
#define EEFS_VERS 4
struct DirEnt{
uint8_t startBlk;
uint16_t size:12;
uint16_t typ:4;
}__attribute__((packed));
#define MAXFILES (1+MAX_MODELS+3)
struct EeFs{
uint8_t version;
uint8_t mySize;
uint8_t freeList;
uint8_t bs;
DirEnt files[MAXFILES];
}__attribute__((packed)) eeFs;
static uint8_t EeFsRead(uint8_t blk,uint8_t ofs){
uint8_t ret;
eeprom_read_block(&ret,(const void*)(blk*BS+ofs),1);
return ret;
}
static void EeFsWrite(uint8_t blk,uint8_t ofs,uint8_t val){
eeWriteBlockCmp(&val, (void*)(blk*BS+ofs), 1);
}
static uint8_t EeFsGetLink(uint8_t blk){
return EeFsRead( blk,0);
}
static void EeFsSetLink(uint8_t blk,uint8_t val){
EeFsWrite( blk,0,val);
}
static uint8_t EeFsGetDat(uint8_t blk,uint8_t ofs){
return EeFsRead( blk,ofs+1);
}
static void EeFsSetDat(uint8_t blk,uint8_t ofs,uint8_t*buf,uint8_t len){
//EeFsWrite( blk,ofs+1,val);
eeWriteBlockCmp(buf, (void*)(blk*BS+ofs+1), len);
}
static void EeFsFlushFreelist()
{
eeWriteBlockCmp(&eeFs.freeList,&((EeFs*)0)->freeList ,sizeof(eeFs.freeList));
}
static void EeFsFlush()
{
eeWriteBlockCmp(&eeFs, 0,sizeof(eeFs));
}
uint16_t EeFsGetFree()
{
uint16_t ret = 0;
uint8_t i = eeFs.freeList;
while( i ){
ret += BS-1;
i = EeFsGetLink(i);
}
return ret;
}
// #ifndef ASYNC_WRITE TODO because duplicate code
static void EeFsFree(uint8_t blk){///free one or more blocks
uint8_t i = blk;
while( EeFsGetLink(i)) i = EeFsGetLink(i);
EeFsSetLink(i,eeFs.freeList);
eeFs.freeList = blk; //chain in front
EeFsFlushFreelist();
}
// #endif
#ifndef ASYNC_WRITE
static uint8_t EeFsAlloc(){ ///alloc one block from freelist
uint8_t ret=eeFs.freeList;
if(ret){
eeFs.freeList = EeFsGetLink(ret);
EeFsFlushFreelist();
EeFsSetLink(ret,0);
}
return ret;
}
#endif
int8_t EeFsck()
{
#ifdef ASYNC_WRITE
s_sync_write = true;
#endif
uint8_t *bufp;
static uint8_t buffer[BLOCKS];
bufp = buffer;
memset(bufp,0,BLOCKS);
uint8_t blk ;
int8_t ret=0;
for(uint8_t i = 0; i <= MAXFILES; i++){
uint8_t *startP = i==MAXFILES ? &eeFs.freeList : &eeFs.files[i].startBlk;
uint8_t lastBlk = 0;
blk = *startP;
while(blk){
if( ( blk < FIRSTBLK ) //goto err_1; //bad blk index
|| (blk >= BLOCKS ) //goto err_2; //bad blk index
|| (bufp[blk] ))//goto err_3; //blk double usage
{
if(lastBlk){
EeFsSetLink(lastBlk,0);
}else{
*startP = 0; //interrupt chain at startpos
EeFsFlush();
}
blk=0; //abort
}else{
bufp[blk] = i+1;
lastBlk = blk;
blk = EeFsGetLink(blk);
}
}
}
for(blk = FIRSTBLK; blk < BLOCKS; blk++){
if(bufp[blk]==0) { //goto err_4; //unused block
EeFsSetLink(blk,eeFs.freeList);
eeFs.freeList = blk; //chain in front
EeFsFlushFreelist();
}
}
#ifdef ASYNC_WRITE
s_sync_write = false;
#endif
return ret;
}
void EeFsFormat()
{
#ifdef ASYNC_WRITE
s_sync_write = true;
#endif
if(sizeof(eeFs) != RESV){
extern void eeprom_RESV_mismatch();
eeprom_RESV_mismatch();
}
memset(&eeFs,0, sizeof(eeFs));
eeFs.version = EEFS_VERS;
eeFs.mySize = sizeof(eeFs);
eeFs.freeList = 0;
eeFs.bs = BS;
for(uint8_t i = FIRSTBLK; i < BLOCKS; i++) EeFsSetLink(i,i+1);
EeFsSetLink(BLOCKS-1, 0);
eeFs.freeList = FIRSTBLK;
EeFsFlush();
#ifdef ASYNC_WRITE
s_sync_write = false;
#endif
}
bool EeFsOpen()
{
eeprom_read_block(&eeFs,0,sizeof(eeFs));
#ifdef SIMU
if(eeFs.version != EEFS_VERS) printf("bad eeFs.version\n");
if(eeFs.mySize != sizeof(eeFs)) printf("bad eeFs.mySize\n");
#endif
return eeFs.version == EEFS_VERS && eeFs.mySize == sizeof(eeFs);
}
bool EFile::exists(uint8_t i_fileId)
{
return eeFs.files[i_fileId].startBlk;
}
void EFile::swap(uint8_t i_fileId1, uint8_t i_fileId2)
{
DirEnt tmp = eeFs.files[i_fileId1];
eeFs.files[i_fileId1] = eeFs.files[i_fileId2];
eeFs.files[i_fileId2] = tmp;
EeFsFlush();
}
void EFile::rm(uint8_t i_fileId)
{
#ifdef ASYNC_WRITE
s_sync_write = true;
#endif
uint8_t i = eeFs.files[i_fileId].startBlk;
memset(&eeFs.files[i_fileId], 0, sizeof(eeFs.files[i_fileId]));
EeFsFlush(); //chained out
if(i) EeFsFree( i ); //chain in
#ifdef ASYNC_WRITE
s_sync_write = false;
#endif
}
uint16_t EFile::size(){
return eeFs.files[m_fileId].size;
}
// G: Open file ID for reading. Return the file's type
void EFile::openRd(uint8_t i_fileId){
m_fileId = i_fileId;
m_pos = 0;
m_currBlk = eeFs.files[m_fileId].startBlk;
m_ofs = 0;
s_write_err = ERR_NONE; // error reasons */
}
void RlcFile::openRlc(uint8_t i_fileId)
{
EFile::openRd(i_fileId);
m_zeroes = 0;
m_bRlc = 0;
}
uint8_t EFile::read(uint8_t*buf,uint16_t i_len){
uint16_t len = eeFs.files[m_fileId].size - m_pos;
if(len < i_len) i_len = len;
len = i_len;
while(len)
{
if(!m_currBlk) break;
*buf++ = EeFsGetDat(m_currBlk, m_ofs++);
if(m_ofs>=(BS-1)){
m_ofs=0;
m_currBlk=EeFsGetLink(m_currBlk);
}
len--;
}
m_pos += i_len - len;
return i_len - len;
}
// G: Read runlength (RLE) compressed bytes into buf.
#ifdef TRANSLATIONS
uint16_t RlcFile::readRlc12(uint8_t*buf,uint16_t i_len, bool rlc2)
#else
uint16_t RlcFile::readRlc(uint8_t*buf,uint16_t i_len)
#endif
{
uint16_t i=0;
for( ; 1; ){
uint8_t l=min<uint16_t>(m_zeroes,i_len-i);
memset(&buf[i],0,l);
i += l;
m_zeroes -= l;
if(m_zeroes) break;
l=min<uint16_t>(m_bRlc,i_len-i);
uint8_t lr = read(&buf[i],l);
i += lr ;
m_bRlc -= lr;
if(m_bRlc) break;
if(read(&m_bRlc,1)!=1) break; //read how many bytes to read
assert(m_bRlc & 0x7f);
#ifdef TRANSLATIONS
if (rlc2) {
#endif
if(m_bRlc&0x80){ // if contains high byte
m_zeroes =(m_bRlc>>4) & 0x7;
m_bRlc = m_bRlc & 0x0f;
}
else if(m_bRlc&0x40){
m_zeroes = m_bRlc & 0x3f;
m_bRlc = 0;
}
//else m_bRlc
#ifdef TRANSLATIONS
}
else {
if(m_bRlc&0x80){ // if contains high byte
m_zeroes = m_bRlc & 0x7f;
m_bRlc = 0;
}
}
#endif
}
return i;
}
#ifdef ASYNC_WRITE
void RlcFile::write1(uint8_t b)
{
m_write1_byte = b;
write(&m_write1_byte, 1);
}
void RlcFile::write(uint8_t *buf, uint8_t i_len)
{
m_write_len = i_len;
m_write_buf = buf;
do {
nextWriteStep();
} while (s_sync_write && m_write_len && !s_write_err);
}
void RlcFile::nextWriteStep()
{
if(!m_currBlk && m_pos==0)
{
eeFs.files[FILE_TMP].startBlk = m_currBlk = eeFs.freeList;
if (m_currBlk) {
eeFs.freeList = EeFsGetLink(m_currBlk);
m_write_step = WRITE_START_STEP + WRITE_FIRST_LINK;
EeFsFlushFreelist();
return;
}
}
if (m_write_step == WRITE_START_STEP + WRITE_FIRST_LINK) {
m_write_step = WRITE_START_STEP;
EeFsSetLink(m_currBlk, 0);
return;
}
while (m_write_len) {
if (!m_currBlk) {
s_write_err = ERR_FULL;
break;
}
if (m_ofs >= (BS-1)) {
m_ofs = 0;
if (!EeFsGetLink(m_currBlk)) {
if (!eeFs.freeList) {
s_write_err = ERR_FULL;
break;
}
m_write_step += WRITE_NEXT_LINK_1; // TODO review all these names
EeFsSetLink(m_currBlk, eeFs.freeList);
return;
}
m_currBlk = EeFsGetLink(m_currBlk);
}
switch (m_write_step & 0x0f) {
case WRITE_NEXT_LINK_1:
m_currBlk = eeFs.freeList;
eeFs.freeList = EeFsGetLink(eeFs.freeList);
m_write_step += 1;
EeFsFlushFreelist();
return;
case WRITE_NEXT_LINK_2:
m_write_step -= WRITE_NEXT_LINK_2;
EeFsSetLink(m_currBlk, 0); // TODO needed?
return;
}
if (!m_currBlk) { // TODO needed?
s_write_err = ERR_FULL;
break;
}
uint8_t tmp = BS-1-m_ofs; if(tmp>m_write_len) tmp=m_write_len;
m_write_buf += tmp;
m_write_len -= tmp;
m_ofs += tmp;
m_pos += tmp;
EeFsSetDat(m_currBlk, m_ofs-tmp, m_write_buf-tmp, tmp);
return;
}
if (s_write_err == ERR_FULL) {
alert(PSTR("EEPROM overflow"));
m_write_step = 0;
m_write_len = 0;
}
if (!s_sync_write)
nextRlcWriteStep();
}
#else
uint8_t RlcFile::write1(uint8_t b)
{
return write(&b,1);
}
uint8_t RlcFile::write(uint8_t*buf, uint8_t i_len)
{
uint8_t len=i_len;
if(!m_currBlk && m_pos==0)
{
eeFs.files[m_fileId].startBlk = m_currBlk = EeFsAlloc();
}
while(len)
{
#ifndef SIMU
if( (int16_t)(m_stopTime10ms - get_tmr10ms()) < 0)
{
s_write_err = ERR_TMO;
break;
}
#endif
if(!m_currBlk) {
s_write_err = ERR_FULL;
break;
}
if(m_ofs>=(BS-1)){
m_ofs=0;
if( ! EeFsGetLink(m_currBlk) ){
EeFsSetLink(m_currBlk, EeFsAlloc());
}
m_currBlk = EeFsGetLink(m_currBlk);
}
if(!m_currBlk) {
s_write_err = ERR_FULL;
break;
}
uint8_t l = BS-1-m_ofs; if(l>len) l=len;
EeFsSetDat(m_currBlk, m_ofs, buf, l);
buf +=l;
m_ofs +=l;
len -=l;
}
m_pos += i_len - len;
return i_len - len;
}
#endif
#ifdef ASYNC_WRITE
void RlcFile::create(uint8_t i_fileId, uint8_t typ, uint8_t sync_write)
{
// all write operations will be executed on FILE_TMP
openRlc(FILE_TMP); // internal use
eeFs.files[FILE_TMP].typ = typ;
eeFs.files[FILE_TMP].size = 0;
m_fileId = i_fileId;
s_sync_write = sync_write;
}
void RlcFile::writeRlc(uint8_t i_fileId, uint8_t typ, uint8_t*buf, uint16_t i_len, uint8_t sync_write)
{
create(i_fileId, typ, sync_write);
m_write_step = WRITE_START_STEP;
m_rlc_buf = buf;
m_rlc_len = i_len;
m_cur_rlc_len = 0;
do {
nextRlcWriteStep();
} while (s_sync_write && m_write_step && !s_write_err);
}
void RlcFile::nextRlcWriteStep()
{
uint8_t cnt = 1;
uint8_t cnt0 = 0;
uint16_t i = 0;
if (m_cur_rlc_len) {
uint8_t tmp1 = m_cur_rlc_len;
uint8_t *tmp2 = m_rlc_buf;
m_rlc_buf += m_cur_rlc_len;
m_cur_rlc_len = 0;
write(tmp2, tmp1);
return;
}
bool run0 = m_rlc_buf[0] == 0;
if(m_rlc_len==0) goto close;
for (i=1; 1; i++) // !! laeuft ein byte zu weit !!
{
bool cur0 = m_rlc_buf[i] == 0;
if (cur0 != run0 || cnt==0x3f || (cnt0 && cnt==0xf)|| i==m_rlc_len){
if (run0) {
assert(cnt0==0);
if (cnt<8 && i!=m_rlc_len)
cnt0 = cnt; //aufbew fuer spaeter
else {
m_rlc_buf+=cnt;
m_rlc_len-=cnt;
write1(cnt|0x40);
return;
}
}
else{
m_rlc_buf+=cnt0;
m_rlc_len-=cnt0+cnt;
m_cur_rlc_len=cnt;
if(cnt0){
write1(0x80 | (cnt0<<4) | cnt);
}
else{
write1(cnt);
}
return;
}
cnt=0;
if (i==m_rlc_len) break;
run0 = cur0;
}
cnt++;
}
close:
switch(m_write_step) {
case WRITE_START_STEP:
{
uint8_t fri=0;
eeFs.files[FILE_TMP].size = m_pos;
if (m_currBlk && ( fri = EeFsGetLink(m_currBlk))) {
uint8_t prev_freeList = eeFs.freeList;
eeFs.freeList = fri;
while( EeFsGetLink(fri)) fri = EeFsGetLink(fri);
m_write_step = WRITE_FREE_UNUSED_BLOCKS_STEP1;
EeFsSetLink(fri, prev_freeList);
return;
}
}
case WRITE_FLUSH_STEP: // TODO could be avoided
m_write_step = WRITE_SWAP_STEP;
EeFsFlush(); //chained out
return;
case WRITE_FREE_UNUSED_BLOCKS_STEP1:
m_write_step = WRITE_FREE_UNUSED_BLOCKS_STEP2;
EeFsSetLink(m_currBlk, 0);
return;
case WRITE_FREE_UNUSED_BLOCKS_STEP2:
m_write_step = WRITE_FLUSH_STEP;
EeFsFlushFreelist();
return;
case WRITE_SWAP_STEP:
m_write_step = 0;
EFile::swap(m_fileId, FILE_TMP);
}
}
void RlcFile::flush()
{
while (eeprom_buffer_size > 0);
s_sync_write = true;
while (theFile.m_write_len && !s_write_err)
theFile.nextWriteStep();
while (theFile.isWriting() && !s_write_err)
theFile.nextRlcWriteStep();
s_sync_write = false;
}
#else
void RlcFile::create(uint8_t i_fileId, uint8_t typ, uint16_t maxTme10ms)
{
openRlc(i_fileId); //internal use
eeFs.files[i_fileId].typ = typ;
eeFs.files[i_fileId].size = 0;
m_stopTime10ms = get_tmr10ms() + maxTme10ms;
}
// G: Write runlength (RLE) compressed bytes
uint16_t RlcFile::writeRlc(uint8_t i_fileId, uint8_t typ,uint8_t*buf,uint16_t i_len, uint8_t maxTme10ms){
create(i_fileId,typ,maxTme10ms);
bool run0 = buf[0] == 0;
uint8_t cnt = 1;
uint8_t cnt0 = 0;
uint16_t i = 0;
if(i_len==0) goto close;
//RLE compression:
//rb = read byte
//if (rb | 0x80) write rb & 0x7F zeros
//else write rb bytes
for( i=1; 1 ; i++) // !! laeuft ein byte zu weit !!
{
bool cur0 = buf[i] == 0;
if(cur0 != run0 || cnt==0x3f || (cnt0 && cnt==0xf)|| i==i_len){
if(run0){
assert(cnt0==0);
if(cnt<8 && i!=i_len)
cnt0 = cnt; //aufbew fuer spaeter
else {
if( write1(cnt|0x40)!=1) goto error;//-cnt&0x3f
}
}else{
if(cnt0){
if( write1(0x80 | (cnt0<<4) | cnt)!=1) goto error;//-cnt0xx-cnt
cnt0 = 0;
}else{
if( write1(cnt) !=1) goto error;//-cnt
}
uint8_t ret=write(&buf[i-cnt],cnt);
if( ret !=cnt) { cnt-=ret; goto error;}//-cnt
}
cnt=0;
if(i==i_len) break;
run0 = cur0;
}
cnt++;
}
if(0){
error:
i-=cnt+cnt0;
#ifdef SIMU
switch(s_write_err){
default:
case ERR_NONE:
assert(!"missing errno");
break;
case ERR_FULL:
printf("ERROR filesystem overflow! written: %d missing: %d\n",i,i_len-i);
break;
case ERR_TMO:
printf("ERROR filesystem write timeout %d 0ms\n",(int16_t)(m_stopTime10ms - g_tmr10ms));
break;
}
#endif
}
close:
close();
return i;
}
#endif
// G: Close file and truncate at this blk. Add any remaining blocks to freeList chain
void RlcFile::close()
{
uint8_t fri=0;
if(m_currBlk && ( fri = EeFsGetLink(m_currBlk))) EeFsSetLink(m_currBlk, 0);
#ifdef ASYNC_WRITE
eeFs.files[FILE_TMP].size = m_pos;
EFile::swap(m_fileId, FILE_TMP);
#else
eeFs.files[m_fileId].size = m_pos;
EeFsFlush(); //chained out
#endif
if(fri) EeFsFree( fri ); //chain in
#ifdef ASYNC_WRITE
m_write_step = 0;
s_sync_write = false;
#endif
}

174
src/file.h Normal file
View file

@ -0,0 +1,174 @@
#ifndef file_h
/*
* 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.
*
*/
#define file_h
#include <inttypes.h>
//
// bs=16 128 blocks verlust link:128 16files:16*8 128 sum 256
// bs=32 64 blocks verlust link: 64 16files:16*16 256 sum 320
//
#if defined(PCBV3)
#define EESIZE 4096
#define BS 32
#else
#define EESIZE 2048
#define BS 16
#endif
/// fileId of general file
#define FILE_GENERAL 0
/// convert model number 0..MAX_MODELS-1 int fileId
#define FILE_MODEL(n) (1+n)
#define FILE_TMP (1+16)
bool EeFsOpen();
int8_t EeFsck();
void EeFsFormat();
uint16_t EeFsGetFree();
#ifdef ASYNC_WRITE
extern volatile size_t eeprom_buffer_size;
#endif
class EFile
{
public:
///remove contents of given file
static void rm(uint8_t i_fileId);
///swap contents of file1 with them of file2
static void swap(uint8_t i_fileId1,uint8_t i_fileId2);
///return true if the file with given fileid exists
static bool exists(uint8_t i_fileId);
///return size of compressed file without block overhead
uint16_t size(); // TODO static ?
///open file for reading, no close necessary
void openRd(uint8_t i_fileId);
uint8_t read(uint8_t*buf, uint16_t i_len);
protected:
uint8_t m_fileId; //index of file in directory = filename
uint16_t m_pos; //over all filepos
uint8_t m_currBlk; //current block.id
uint8_t m_ofs; //offset inside of the current block
};
#define ERR_NONE 0
#define ERR_FULL 1
#ifndef ASYNC_WRITE
#define ERR_TMO 2
#endif
extern uint8_t s_write_err; // error reasons
extern uint8_t s_sync_write;
///deliver current errno, this is reset in open
inline uint8_t errno() { return s_write_err; }
class RlcFile: public EFile
{
uint8_t m_bRlc; // control byte for run length decoder
uint8_t m_zeroes;
#ifdef ASYNC_WRITE
uint8_t m_flags;
#define WRITE_FIRST_LINK 0x01
#define WRITE_NEXT_LINK_1 0x02
#define WRITE_NEXT_LINK_2 0x03
#define WRITE_START_STEP 0x10
#define WRITE_FREE_UNUSED_BLOCKS_STEP1 0x20
#define WRITE_FREE_UNUSED_BLOCKS_STEP2 0x30
#define WRITE_FLUSH_STEP 0x40
#define WRITE_SWAP_STEP 0x50
uint8_t m_write_step;
uint16_t m_rlc_len;
uint8_t * m_rlc_buf;
uint8_t m_cur_rlc_len;
uint8_t m_write1_byte;
uint8_t m_write_len;
uint8_t * m_write_buf;
#else
uint16_t m_stopTime10ms; // maximum point of time for writing
#endif
public:
void openRlc(uint8_t i_fileId);
#ifdef ASYNC_WRITE
void create(uint8_t i_fileId, uint8_t typ, uint8_t sync_write);
inline bool isWriting() { return m_write_step != 0; }
void write(uint8_t*buf, uint8_t i_len);
void write1(uint8_t b);
void nextWriteStep();
void nextRlcWriteStep();
void writeRlc(uint8_t i_fileId, uint8_t typ, uint8_t *buf, uint16_t i_len, uint8_t sync_write);
void flush();
#else
/// create a new file with given fileId,
/// !!! if this file already exists, then all blocks are reused
/// and all contents will be overwritten.
/// after writing close has to be called
void create(uint8_t i_fileId, uint8_t typ, uint16_t maxTme10ms);
uint8_t write(uint8_t*buf, uint8_t i_len);
uint8_t write1(uint8_t b);
///open file, write to file and close it.
///If file existed before, then contents is overwritten.
///If file was larger before, then unused blocks are freed
uint16_t writeRlc(uint8_t i_fileId, uint8_t typ,uint8_t*buf,uint16_t i_len, uint8_t maxTme10ms);
#endif
/*
* close file and truncate the blockchain if to long.
*/
void close();
///read from opened file and decode rlc-coded data
#ifdef TRANSLATIONS
uint16_t readRlc12(uint8_t*buf,uint16_t i_len,bool rlc2);
inline uint16_t readRlc1(uint8_t*buf,uint16_t i_len)
{
return readRlc12(buf,i_len,false);
}
inline uint16_t readRlc(uint8_t*buf, uint16_t i_len)
{
return readRlc12(buf,i_len,true);
}
#else
uint16_t readRlc(uint8_t*buf, uint16_t i_len); // TODO should be like writeRlc?
#endif
};
#endif
/*eof*/

9
src/font.lbm Normal file
View file

@ -0,0 +1,9 @@
prog_uchar APM font[] = {
80,8,80,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6f,0x00,0x00,0x07,0x00,0x07,0x00,0x14,0x7f,0x14,0x7f,0x14,0x24,0x2a,0x7f,0x2a,0x12,0x23,0x13,0x08,0x64,0x62,0x36,0x49,0x55,0x22,0x50,0x00,0x05,0x03,0x00,0x00,0x00,0x1c,0x22,0x41,0x00,0x00,0x41,0x22,0x1c,0x00,0x14,0x08,0x3e,0x08,0x14,0x08,0x08,0x3e,0x08,0x08,0x00,0x50,0x30,0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x00,0x60,0x60,0x00,0x00,0x20,0x10,0x08,0x04,0x02,
0x00,0x3e,0x41,0x41,0x3e,0x00,0x00,0x42,0x7f,0x40,0x00,0x62,0x51,0x49,0x46,0x00,0x41,0x49,0x49,0x36,0x00,0x18,0x14,0x12,0x7f,0x00,0x27,0x45,0x45,0x39,0x00,0x3e,0x49,0x49,0x32,0x00,0x01,0x79,0x05,0x03,0x00,0x36,0x49,0x49,0x36,0x00,0x06,0x49,0x29,0x1e,0x00,0x00,0x36,0x36,0x00,0x00,0x00,0x56,0x36,0x00,0x00,0x08,0x14,0x22,0x41,0x14,0x14,0x14,0x14,0x14,0x41,0x22,0x14,0x08,0x00,0x02,0x01,0x51,0x09,0x06,
0x06,0x09,0x09,0x06,0x00,0x7e,0x09,0x09,0x09,0x7e,0x7f,0x49,0x49,0x49,0x36,0x3e,0x41,0x41,0x41,0x22,0x7f,0x41,0x41,0x22,0x1c,0x7f,0x49,0x49,0x49,0x41,0x7f,0x09,0x09,0x09,0x01,0x3e,0x41,0x49,0x49,0x7a,0x7f,0x08,0x08,0x08,0x7f,0x00,0x41,0x7f,0x41,0x00,0x20,0x40,0x41,0x3f,0x01,0x7f,0x08,0x14,0x22,0x41,0x7f,0x40,0x40,0x40,0x40,0x7f,0x02,0x0c,0x02,0x7f,0x7f,0x04,0x08,0x10,0x7f,0x3e,0x41,0x41,0x41,0x3e,
0x7f,0x09,0x09,0x09,0x06,0x3e,0x41,0x51,0x21,0x5e,0x7f,0x09,0x19,0x29,0x46,0x26,0x49,0x49,0x49,0x32,0x01,0x01,0x7f,0x01,0x01,0x3f,0x40,0x40,0x40,0x3f,0x1f,0x20,0x40,0x20,0x1f,0x3f,0x40,0x30,0x40,0x3f,0x63,0x14,0x08,0x14,0x63,0x07,0x08,0x70,0x08,0x07,0x61,0x51,0x49,0x45,0x43,0x00,0x7f,0x41,0x41,0x00,0x02,0x04,0x08,0x10,0x20,0x00,0x41,0x41,0x7f,0x00,0x04,0x02,0x01,0x02,0x04,0x40,0x40,0x40,0x40,0x40,
0x00,0x01,0x02,0x04,0x00,0x20,0x54,0x54,0x54,0x78,0x7f,0x48,0x44,0x44,0x38,0x38,0x44,0x44,0x44,0x20,0x38,0x44,0x44,0x48,0x7f,0x38,0x54,0x54,0x54,0x18,0x08,0x7e,0x09,0x01,0x02,0x0c,0x52,0x52,0x52,0x3e,0x7f,0x08,0x04,0x04,0x78,0x00,0x44,0x7d,0x40,0x00,0x20,0x40,0x44,0x3d,0x00,0x7f,0x10,0x28,0x44,0x00,0x00,0x41,0x7f,0x40,0x00,0x7c,0x04,0x18,0x04,0x78,0x7c,0x08,0x04,0x04,0x78,0x38,0x44,0x44,0x44,0x38,
0x7c,0x14,0x14,0x14,0x08,0x08,0x14,0x14,0x18,0x7c,0x7c,0x08,0x04,0x04,0x08,0x48,0x54,0x54,0x54,0x20,0x04,0x3f,0x44,0x40,0x20,0x3c,0x40,0x40,0x20,0x7c,0x1c,0x20,0x40,0x20,0x1c,0x3c,0x40,0x20,0x40,0x3c,0x44,0x28,0x10,0x28,0x44,0x0c,0x50,0x50,0x50,0x3c,0x44,0x64,0x54,0x4c,0x44,0x00,0x08,0x36,0x41,0x00,0x00,0x00,0x7f,0x00,0x00,0x00,0x41,0x36,0x08,0x00,0x08,0x08,0x22,0x1c,0x08,0x08,0x1c,0x22,0x08,0x08,
};

BIN
src/font_6x1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

44
src/font_6x1.xbm Normal file
View file

@ -0,0 +1,44 @@
#define font_6x1_width 80
#define font_6x1_height 48
static unsigned char font_6x1_bits[] = {
0x00,0x29,0x45,0x86,0x31,0x48,0x00,0x00,0x00,0x00,0x00,0x29,
0xe5,0x67,0x22,0x84,0x10,0x02,0x00,0x80,0x00,0xa9,0x5f,0x50,
0x11,0x02,0x55,0x02,0x00,0x40,0x00,0x01,0xe5,0x88,0x00,0x02,
0xb9,0x0f,0x3e,0x20,0x00,0x80,0x4f,0x45,0x05,0x02,0x55,0x62,
0x00,0x10,0x00,0x01,0xf5,0x72,0x02,0x84,0x10,0x42,0x80,0x09,
0x00,0x01,0x45,0xb0,0x05,0x48,0x00,0x20,0x80,0x01,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x31,0x07,0x3d,
0xf3,0x8c,0x01,0x00,0x41,0x70,0x92,0x49,0x88,0x85,0x84,0x52,
0x32,0x86,0x80,0x88,0x12,0x41,0x48,0x9d,0x40,0x52,0x32,0x46,
0x3e,0x81,0x12,0x21,0x26,0xa1,0x23,0x8c,0x03,0x20,0x00,0x42,
0x12,0x11,0xe8,0xa1,0x24,0x12,0x32,0x46,0x3e,0x21,0x12,0x09,
0x08,0xa5,0x24,0x12,0x31,0x84,0x80,0x00,0x8c,0x7b,0x07,0x19,
0x23,0x8c,0x00,0x02,0x41,0x20,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xc6,0x3d,0x77,0xfe,0x77,0xd1,0xf1,0x18,
0x62,0x74,0x29,0xc6,0x98,0x42,0x88,0x91,0xa0,0x14,0x76,0x8c,
0x29,0xc6,0x10,0x43,0x08,0x91,0xa0,0x12,0xea,0x8c,0xe6,0xbf,
0x10,0xdf,0xeb,0x9f,0xa0,0x11,0x6a,0x8d,0x20,0xc6,0x10,0x43,
0x88,0x91,0xa0,0x12,0x62,0x8e,0x20,0xc6,0x98,0x42,0x88,0x91,
0xa4,0x14,0x62,0x8c,0x20,0x3e,0x77,0x7e,0xf0,0xd1,0x99,0xf8,
0x63,0x74,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xcf,0x3d,0xf7,0x63,0x8c,0x31,0x7e,0x07,0x1c,0x01,0x31,0xc6,
0x48,0x62,0x8c,0x31,0x42,0x11,0x90,0x02,0x31,0xc6,0x40,0x62,
0x8c,0x2a,0x22,0x21,0x50,0x04,0x2f,0x3e,0x47,0x62,0x8c,0x44,
0x11,0x41,0x10,0x00,0xa1,0x16,0x48,0x62,0xac,0x8a,0x08,0x81,
0x10,0x00,0x21,0xa5,0x48,0xa2,0xaa,0x91,0x04,0x01,0x11,0x00,
0xc1,0x46,0x47,0x1c,0x51,0x91,0x7c,0x07,0x1c,0xf8,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x04,0x00,0x01,
0x03,0x81,0xa0,0x60,0x00,0x00,0x04,0x04,0x00,0x81,0xf4,0x01,
0x80,0x40,0x00,0x00,0xc8,0x35,0x67,0x9d,0x88,0xcd,0xb0,0x44,
0x56,0x73,0x00,0xce,0x90,0xe3,0x89,0x93,0xa0,0x42,0xea,0x8c,
0xc0,0xc7,0x10,0xbf,0xf0,0x91,0xa0,0x41,0x6a,0x8c,0x20,0xc6,
0x18,0x83,0x80,0x91,0xa4,0x42,0x62,0x8c,0xc0,0x3f,0xe7,0x9d,
0x70,0xd1,0x99,0xe4,0x62,0x74,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x44,
0x04,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x42,0x08,0x21,
0xcf,0x36,0x77,0x62,0x8c,0x31,0x7e,0x42,0x08,0x12,0x31,0xcf,
0x20,0x62,0x8c,0x2a,0x22,0x41,0xd0,0xde,0xcf,0x07,0x27,0x62,
0x8c,0xc4,0x13,0x42,0x08,0x12,0x01,0x06,0x28,0xb3,0xaa,0x0a,
0x0a,0x42,0x08,0x21,0x01,0x86,0xc7,0x2c,0x51,0xd1,0x7d,0x44,
0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00};

15
src/font_dblsize.lbm Normal file
View file

@ -0,0 +1,15 @@
prog_uchar APM font_dblsize[] = {
160,96,1920,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x7e,0x00,0x00,0x7e,0x3e,0x00,0x00,0x60,0x60,0xfe,0xfe,0x60,0x60,0xfe,0xfe,0x60,0x60,0x60,0xf0,0x98,0x98,0xfe,0xfe,0x98,0x98,0x18,0x18,0x0c,0x1e,0x1e,0x0c,0x80,0xc0,0xe0,0x70,0x38,0x18,0x78,0xfc,0x8e,0xc6,0xe6,0x76,0x3c,0x18,0x00,0x00,0x00,0x00,0x66,0x76,0x3e,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0xf0,0x38,0x1c,0x0e,0x06,0x00,0x00,0x00,0x00,0x06,0x0e,0x1c,0x38,0xf0,0xe0,0x00,0x00,0x60,0x60,0xc0,0x80,0xf8,0xf8,0x80,0xc0,0x60,0x60,0x80,0x80,0x80,0x80,0xf8,0xf8,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xc0,0xe0,0x70,0x38,0x18,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x71,0x71,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0x7f,0x7f,0x06,0x06,0x7f,0x7f,0x06,0x06,0x18,0x18,0x19,0x19,0x7f,0x7f,0x19,0x19,0x0f,0x06,0x18,0x1c,0x0e,0x07,0x03,0x01,0x30,0x78,0x78,0x30,0x1e,0x3f,0x71,0x63,0x67,0x66,0x3c,0x3c,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x0f,0x1c,0x38,0x70,0x60,0x00,0x00,0x00,0x00,0x60,0x70,0x38,0x1c,0x0f,0x07,0x00,0x00,0x06,0x06,0x03,0x01,0x1f,0x1f,0x01,0x03,0x06,0x06,0x01,0x01,0x01,0x01,0x1f,0x1f,0x01,0x01,0x01,0x01,0x00,0x00,0x66,0x76,0x3e,0x1e,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x30,0x78,0x78,0x30,0x00,0x00,0x00,0x00,0x18,0x1c,0x0e,0x07,0x03,0x01,0x00,0x00,0x00,0x00,
0x00,0x00,0xf8,0xfc,0x0e,0x06,0x06,0x0e,0xfc,0xf8,0x00,0x00,0x18,0x1c,0xfe,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x1c,0x0e,0x06,0x86,0xce,0xfc,0x78,0x00,0x00,0x06,0x06,0x86,0x86,0x86,0xce,0xfc,0x78,0x00,0x00,0x80,0xc0,0xe0,0x70,0x38,0x1c,0xfe,0xfe,0x00,0x00,0x7e,0x7e,0x66,0x66,0x66,0xe6,0xc6,0x86,0x00,0x00,0xf8,0xfc,0x8e,0x86,0x86,0x8e,0x1c,0x18,0x00,0x00,0x06,0x06,0x86,0xc6,0xe6,0x76,0x3e,0x1e,0x00,0x00,0x78,0xfc,0xce,0x86,0x86,0xce,0xfc,0x78,0x00,0x00,0x78,0xfc,0xce,0x86,0x86,0xce,0xfc,0xf8,0x00,0x00,0x30,0x78,0x78,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x78,0x78,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xc0,0xe0,0x70,0x38,0x1c,0x0e,0x06,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x06,0x0e,0x1c,0x38,0x70,0xe0,0xc0,0x80,0x00,0x00,0x18,0x1c,0x0e,0x06,0x06,0x06,0x86,0xce,0xfc,0x78,
0x00,0x00,0x1f,0x3f,0x70,0x60,0x60,0x70,0x3f,0x1f,0x00,0x00,0x60,0x60,0x7f,0x7f,0x60,0x60,0x00,0x00,0x00,0x00,0x78,0x7c,0x6e,0x67,0x63,0x61,0x60,0x60,0x00,0x00,0x60,0x60,0x61,0x61,0x61,0x73,0x3f,0x1e,0x00,0x00,0x07,0x07,0x06,0x06,0x06,0x06,0x7f,0x7f,0x00,0x00,0x18,0x38,0x70,0x60,0x60,0x70,0x3f,0x1f,0x00,0x00,0x1f,0x3f,0x71,0x61,0x61,0x73,0x3f,0x1e,0x00,0x00,0x00,0x00,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x1e,0x3f,0x73,0x61,0x61,0x73,0x3f,0x1e,0x00,0x00,0x00,0x00,0x61,0x71,0x39,0x1d,0x0f,0x07,0x00,0x00,0x0c,0x1e,0x1e,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x76,0x3e,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x07,0x0e,0x1c,0x38,0x70,0x60,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x60,0x70,0x38,0x1c,0x0e,0x07,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x66,0x67,0x03,0x01,0x00,0x00,
0x78,0xfc,0xce,0x86,0x86,0xce,0xfc,0x78,0x00,0x00,0xe0,0xf0,0xb8,0x9c,0x8e,0x8e,0x9c,0xb8,0xf0,0xe0,0xfe,0xfe,0x86,0x86,0x86,0x86,0x86,0xce,0xfc,0x78,0xf8,0xfc,0x0e,0x06,0x06,0x06,0x06,0x0e,0x1c,0x18,0xfe,0xfe,0x06,0x06,0x06,0x0e,0x1c,0x38,0xf0,0xe0,0xfe,0xfe,0x86,0x86,0x86,0x86,0x86,0x86,0x06,0x06,0xfe,0xfe,0x86,0x86,0x86,0x86,0x86,0x86,0x06,0x06,0xf8,0xfc,0x0e,0x06,0x86,0x86,0x86,0x8e,0x9c,0x98,0xfe,0xfe,0x80,0x80,0x80,0x80,0x80,0x80,0xfe,0xfe,0x00,0x00,0x06,0x06,0xfe,0xfe,0x06,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0xfe,0xfe,0x06,0x06,0xfe,0xfe,0x80,0xc0,0xe0,0x70,0x38,0x1c,0x0e,0x06,0xfe,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xfe,0x1c,0x38,0xf0,0xf0,0x38,0x1c,0xfe,0xfe,0xfe,0xfe,0x70,0xe0,0xc0,0x80,0x00,0x00,0xfe,0xfe,0xf8,0xfc,0x0e,0x06,0x06,0x06,0x06,0x0e,0xfc,0xf8,
0x00,0x00,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x7f,0x7f,0x01,0x01,0x01,0x01,0x01,0x01,0x7f,0x7f,0x7f,0x7f,0x61,0x61,0x61,0x61,0x61,0x73,0x3f,0x1e,0x1f,0x3f,0x70,0x60,0x60,0x60,0x60,0x70,0x38,0x18,0x7f,0x7f,0x60,0x60,0x60,0x70,0x38,0x1c,0x0f,0x07,0x7f,0x7f,0x61,0x61,0x61,0x61,0x61,0x61,0x60,0x60,0x7f,0x7f,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x1f,0x3f,0x70,0x60,0x61,0x61,0x61,0x71,0x7f,0x3f,0x7f,0x7f,0x01,0x01,0x01,0x01,0x01,0x01,0x7f,0x7f,0x00,0x00,0x60,0x60,0x7f,0x7f,0x60,0x60,0x00,0x00,0x18,0x38,0x70,0x60,0x60,0x70,0x3f,0x1f,0x00,0x00,0x7f,0x7f,0x01,0x03,0x07,0x0e,0x1c,0x38,0x70,0x60,0x7f,0x7f,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x7f,0x7f,0x00,0x00,0x01,0x01,0x00,0x00,0x7f,0x7f,0x7f,0x7f,0x00,0x00,0x01,0x03,0x07,0x0e,0x7f,0x7f,0x1f,0x3f,0x70,0x60,0x60,0x60,0x60,0x70,0x3f,0x1f,
0xfe,0xfe,0x86,0x86,0x86,0x86,0x86,0xce,0xfc,0x78,0xf8,0xfc,0x0e,0x06,0x06,0x06,0x06,0x0e,0xfc,0xf8,0xfe,0xfe,0x86,0x86,0x86,0x86,0x86,0xce,0xfc,0x78,0x78,0xfc,0xce,0x86,0x86,0x86,0x86,0x8e,0x1c,0x18,0x06,0x06,0x06,0x06,0xfe,0xfe,0x06,0x06,0x06,0x06,0xfe,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xfe,0xfe,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xfe,0xfe,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xfe,0x1e,0x3e,0x70,0xe0,0xc0,0xc0,0xe0,0x70,0x3e,0x1e,0x7e,0xfe,0xc0,0x80,0x00,0x00,0x80,0xc0,0xfe,0x7e,0x06,0x06,0x06,0x06,0x86,0xc6,0xe6,0x76,0x3e,0x1e,0x00,0x00,0xfe,0xfe,0x06,0x06,0x06,0x06,0x00,0x00,0x18,0x38,0x70,0xe0,0xc0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0xfe,0xfe,0x00,0x00,0x60,0x70,0x38,0x1c,0x0e,0x0e,0x1c,0x38,0x70,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x7f,0x7f,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x1f,0x3f,0x70,0x60,0x66,0x6e,0x3c,0x38,0x7f,0x67,0x7f,0x7f,0x01,0x01,0x07,0x0f,0x1d,0x39,0x70,0x60,0x18,0x38,0x71,0x61,0x61,0x61,0x61,0x73,0x3f,0x1e,0x00,0x00,0x00,0x00,0x7f,0x7f,0x00,0x00,0x00,0x00,0x1f,0x3f,0x70,0x60,0x60,0x60,0x60,0x70,0x3f,0x1f,0x07,0x0f,0x1c,0x38,0x70,0x70,0x38,0x1c,0x0f,0x07,0x1f,0x3f,0x70,0x70,0x3e,0x3e,0x70,0x70,0x3f,0x1f,0x78,0x7c,0x0e,0x07,0x03,0x03,0x07,0x0e,0x7c,0x78,0x00,0x00,0x01,0x03,0x7f,0x7f,0x03,0x01,0x00,0x00,0x78,0x7c,0x6e,0x67,0x63,0x61,0x60,0x60,0x60,0x60,0x00,0x00,0x7f,0x7f,0x60,0x60,0x60,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x07,0x0e,0x1c,0x18,0x00,0x00,0x60,0x60,0x60,0x60,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,
0x00,0x00,0x06,0x0e,0x1c,0x38,0x70,0x60,0x00,0x00,0x00,0x00,0x60,0x60,0x60,0x60,0x60,0xe0,0xc0,0x80,0xfe,0xfe,0x80,0xc0,0xe0,0x60,0x60,0xe0,0xc0,0x80,0x80,0xc0,0xe0,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x80,0xc0,0xe0,0x60,0x60,0xe0,0xc0,0x80,0xfe,0xfe,0x80,0xc0,0xe0,0x60,0x60,0x60,0x60,0xe0,0xc0,0x80,0x80,0x80,0xf8,0xfc,0x8e,0x86,0x06,0x0e,0x1c,0x18,0xe0,0xf0,0x38,0x18,0x18,0x18,0x18,0x18,0xf8,0xf8,0xfe,0xfe,0x80,0xc0,0xe0,0x60,0x60,0xe0,0xc0,0x80,0x00,0x00,0x60,0x60,0xe6,0xe6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0xe6,0xe6,0x00,0x00,0xfe,0xfe,0x00,0x00,0x80,0xc0,0xe0,0x60,0x00,0x00,0x00,0x00,0x06,0x06,0xfe,0xfe,0x00,0x00,0x00,0x00,0xe0,0xe0,0x60,0xe0,0xc0,0xc0,0xe0,0xe0,0xc0,0x80,0xe0,0xe0,0x80,0xc0,0xe0,0x60,0x60,0xe0,0xc0,0x80,0x80,0xc0,0xe0,0x60,0x60,0x60,0x60,0xe0,0xc0,0x80,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x3c,0x7e,0x66,0x66,0x66,0x66,0x66,0x7f,0x7f,0x7f,0x7f,0x61,0x61,0x60,0x60,0x60,0x70,0x3f,0x1f,0x1f,0x3f,0x70,0x60,0x60,0x60,0x60,0x70,0x38,0x18,0x1f,0x3f,0x70,0x60,0x60,0x60,0x61,0x61,0x7f,0x7f,0x1f,0x3f,0x76,0x66,0x66,0x66,0x66,0x66,0x07,0x07,0x01,0x01,0x7f,0x7f,0x01,0x01,0x00,0x00,0x00,0x00,0x01,0x03,0x67,0x66,0x66,0x66,0x66,0x76,0x3f,0x1f,0x7f,0x7f,0x01,0x01,0x00,0x00,0x00,0x00,0x7f,0x7f,0x00,0x00,0x60,0x60,0x7f,0x7f,0x60,0x60,0x00,0x00,0x18,0x38,0x70,0x60,0x60,0x70,0x3f,0x1f,0x00,0x00,0x7f,0x7f,0x06,0x0f,0x1f,0x39,0x70,0x60,0x00,0x00,0x00,0x00,0x60,0x60,0x7f,0x7f,0x60,0x60,0x00,0x00,0x7f,0x7f,0x00,0x00,0x07,0x07,0x00,0x00,0x7f,0x7f,0x7f,0x7f,0x01,0x01,0x00,0x00,0x00,0x00,0x7f,0x7f,0x1f,0x3f,0x70,0x60,0x60,0x60,0x60,0x70,0x3f,0x1f,
0xe0,0xe0,0x60,0x60,0x60,0x60,0x60,0xe0,0xc0,0x80,0x80,0xc0,0xe0,0x60,0x60,0x60,0xc0,0x80,0xe0,0xe0,0xe0,0xe0,0x80,0xc0,0xe0,0x60,0x60,0xe0,0xc0,0x80,0x80,0xc0,0xe0,0x60,0x60,0x60,0x60,0x60,0x00,0x00,0x60,0x60,0xfe,0xfe,0x60,0x60,0x00,0x00,0x00,0x00,0xe0,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0xe0,0xe0,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0xe0,0xe0,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0xe0,0x60,0xe0,0xc0,0x80,0x00,0x00,0x80,0xc0,0xe0,0x60,0xe0,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0xe0,0x60,0x60,0x60,0x60,0x60,0x60,0xe0,0xe0,0xe0,0x60,0x00,0x00,0x80,0xc0,0x78,0x7c,0x0e,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x0e,0x7c,0x78,0xc0,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x18,0x38,0xf0,0xe0,0xc0,0x80,0x80,0xc0,0xe0,0xf0,0x38,0x18,0x80,0x80,0x80,0x80,
0x7f,0x7f,0x06,0x06,0x06,0x06,0x06,0x07,0x03,0x01,0x01,0x03,0x07,0x06,0x06,0x06,0x07,0x07,0x7f,0x7f,0x7f,0x7f,0x01,0x01,0x00,0x00,0x00,0x00,0x01,0x01,0x61,0x63,0x67,0x66,0x66,0x66,0x66,0x7e,0x3c,0x18,0x00,0x00,0x1f,0x3f,0x70,0x60,0x60,0x70,0x38,0x18,0x1f,0x3f,0x70,0x60,0x60,0x70,0x38,0x18,0x7f,0x7f,0x07,0x0f,0x1c,0x38,0x70,0x70,0x38,0x1c,0x0f,0x07,0x1f,0x3f,0x70,0x70,0x38,0x38,0x70,0x70,0x3f,0x1f,0x60,0x70,0x39,0x1f,0x0f,0x0f,0x1f,0x39,0x70,0x60,0x01,0x03,0x67,0x66,0x66,0x66,0x66,0x76,0x3f,0x1f,0x60,0x70,0x78,0x7c,0x6e,0x67,0x63,0x61,0x60,0x60,0x00,0x00,0x01,0x03,0x1e,0x3e,0x70,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x70,0x3e,0x1e,0x03,0x01,0x00,0x00,0x01,0x01,0x01,0x01,0x18,0x1c,0x0f,0x07,0x03,0x01,0x01,0x03,0x07,0x0f,0x1c,0x18,0x01,0x01,0x01,0x01,
};

BIN
src/font_dblsize.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

164
src/font_dblsize.xbm Normal file
View file

@ -0,0 +1,164 @@
#define font_dblsize_width 160
#define font_dblsize_height 96
static unsigned char font_dblsize_bits[]={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0x0c,
0x33,0x30,0x18,0xc0,0x03,0x0f,0xc0,0x30,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0x0c,0x33,0x30,0x3c,0xe0,
0x07,0x0f,0xe0,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0xc0,0xc0,0x0c,0x33,0xfc,0x3f,0x7c,0x0c,0x0c,0x70,0xe0,
0x00,0x03,0x0c,0x00,0x00,0x00,0x00,0xc0,0x00,0xc0,0xc0,0x0c,
0x33,0xfe,0x1b,0x3e,0x0e,0x0e,0x38,0xc0,0x01,0x03,0x0c,0x00,
0x00,0x00,0x00,0xe0,0x00,0xc0,0xc0,0xcc,0xff,0x33,0x00,0x37,
0x07,0x07,0x1c,0x80,0x33,0x33,0x0c,0x00,0x00,0x00,0x00,0x70,
0x00,0xc0,0x80,0xc4,0xff,0x33,0x80,0xb3,0x03,0x03,0x0c,0x00,
0x73,0x3b,0x0c,0x00,0x00,0x00,0x00,0x38,0x00,0xc0,0x00,0x00,
0x33,0xfe,0xc0,0xe1,0x01,0x00,0x0c,0x00,0xc3,0xcf,0xff,0x00,
0xfc,0x0f,0x00,0x1c,0x00,0xc0,0x00,0x00,0x33,0xfc,0xe1,0xe0,
0x01,0x00,0x0c,0x00,0xc3,0xcf,0xff,0x00,0xfc,0x0f,0x00,0x0e,
0x00,0x00,0x00,0xc0,0xff,0x30,0x73,0xb0,0x33,0x00,0x0c,0x00,
0x73,0x3b,0x0c,0x3c,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0xc0,
0xff,0x30,0x3b,0x30,0x3f,0x00,0x1c,0x80,0x33,0x33,0x0c,0x3c,
0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x00,0x33,0xff,0x1d,0x36,
0x0c,0x00,0x38,0xc0,0x01,0x03,0x0c,0x30,0x00,0x80,0xc1,0x01,
0x00,0xc0,0x00,0x00,0x33,0xff,0x0c,0x7f,0x0c,0x00,0x70,0xe0,
0x00,0x03,0x0c,0x38,0x00,0xc0,0xc3,0x00,0x00,0xc0,0x00,0x00,
0x33,0x30,0x00,0xef,0x3f,0x00,0xe0,0x70,0x00,0x00,0x00,0x1c,
0x00,0xc0,0x03,0x00,0x00,0xc0,0x00,0x00,0x33,0x30,0x00,0xc6,
0x33,0x00,0xc0,0x30,0x00,0x00,0x00,0x0c,0x00,0x80,0x01,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xf0,0xc0,0x00,0x0f,0x3f,0x00,0xf3,0x0f,
0x0f,0xff,0xf0,0xc0,0x03,0x00,0x00,0x00,0x03,0x30,0x00,0x3f,
0xf8,0xe1,0x80,0x1f,0x7f,0x80,0xf3,0x8f,0x1f,0xff,0xf8,0xe1,
0x07,0x00,0x00,0x80,0x03,0x70,0x80,0x7f,0x9c,0xf3,0xc0,0x39,
0xe0,0xc0,0x33,0xc0,0x39,0xc0,0x9c,0x73,0x8e,0x01,0x06,0xc0,
0x01,0xe0,0xc0,0xe1,0x0c,0xf3,0xc0,0x30,0xc0,0xe0,0x33,0xc0,
0x30,0xe0,0x0c,0x33,0xcc,0x03,0x0f,0xe0,0x00,0xc0,0xc1,0xc0,
0x0c,0xc3,0x00,0x30,0xc0,0x70,0xf3,0xc3,0x00,0x70,0x0c,0x33,
0xcc,0x03,0x0f,0x70,0xfc,0x8f,0x03,0xc0,0x0c,0xc3,0x00,0x38,
0xe0,0x38,0xf3,0xc7,0x00,0x38,0x9c,0x73,0x8e,0x01,0x06,0x38,
0xfc,0x0f,0x07,0xe0,0x0c,0xc3,0x00,0x1c,0x7c,0x1c,0x03,0xce,
0x0f,0x1c,0xf8,0xe1,0x0f,0x00,0x00,0x1c,0x00,0x00,0x0e,0x70,
0x0c,0xc3,0x00,0x0e,0x7c,0x0c,0x03,0xcc,0x1f,0x0c,0xf8,0xc1,
0x0f,0x00,0x00,0x1c,0x00,0x00,0x0e,0x38,0x0c,0xc3,0x00,0x07,
0xe0,0xfc,0x03,0xcc,0x38,0x0c,0x9c,0x03,0x8c,0x01,0x0f,0x38,
0xfc,0x0f,0x07,0x1c,0x0c,0xc3,0x80,0x03,0xc0,0xfc,0x03,0xcc,
0x30,0x0c,0x0c,0x03,0xce,0x03,0x0f,0x70,0xfc,0x8f,0x03,0x0c,
0x0c,0xc3,0xc0,0x01,0xc0,0x00,0x33,0xcc,0x30,0x0c,0x0c,0x03,
0xc7,0x03,0x0c,0xe0,0x00,0xc0,0x01,0x00,0x9c,0xc3,0xc0,0x00,
0xe0,0x00,0x73,0xce,0x39,0x0c,0x9c,0x83,0x83,0x01,0x0e,0xc0,
0x01,0xe0,0x00,0x00,0xf8,0xf1,0xc3,0x3f,0x7f,0x00,0xe3,0x87,
0x1f,0x0c,0xf8,0xc1,0x01,0x00,0x07,0x80,0x03,0x70,0x00,0x0c,
0xf0,0xf0,0xc3,0x3f,0x3f,0x00,0xc3,0x03,0x0f,0x0c,0xf0,0xc0,
0x00,0x00,0x03,0x00,0x03,0x30,0x00,0x0c,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x3c,0xc0,0xf0,0x0f,0x3f,0x3f,0xfc,0xff,0x3f,0x3f,0x03,0xf3,
0x03,0xff,0xc0,0x03,0x0c,0x3c,0x30,0x3f,0x7e,0xe0,0xf1,0x9f,
0x7f,0x7f,0xfc,0xff,0xbf,0x7f,0x03,0xf3,0x03,0xff,0xe0,0x03,
0x1c,0x3e,0xb0,0x7f,0xe7,0xf0,0x33,0xf8,0xe1,0xe3,0x0c,0x30,
0xc0,0xe1,0x03,0xc3,0x00,0xcc,0x70,0x03,0x3c,0x3f,0xf0,0xe1,
0xc3,0x38,0x37,0xf0,0xc0,0xc3,0x0d,0x30,0xc0,0xc0,0x03,0xc3,
0x00,0xcc,0x38,0x03,0xfc,0x7f,0xf0,0xc0,0xc3,0x1c,0x3e,0xf0,
0x00,0x83,0x0f,0x30,0xc0,0x00,0x03,0xc3,0x00,0xcc,0x1c,0x03,
0xec,0xfd,0xf0,0xc0,0xe7,0x0c,0x3c,0xf8,0x00,0x03,0x0f,0x30,
0xc0,0x00,0x03,0xc3,0x00,0xcc,0x0e,0x03,0xcc,0xfc,0xf1,0xc0,
0x7e,0xfc,0xff,0xdf,0x00,0x03,0xff,0xf3,0xcf,0xfc,0xff,0xc3,
0x00,0xcc,0x07,0x03,0xcc,0xbc,0xf3,0xc0,0x3c,0xfc,0xff,0xdf,
0x00,0x03,0xff,0xf3,0xcf,0xfc,0xff,0xc3,0x00,0xcc,0x07,0x03,
0xcc,0x3c,0xf7,0xc0,0x00,0x0c,0x3c,0xf8,0x00,0x03,0x0f,0x30,
0xc0,0xc0,0x03,0xc3,0x00,0xcc,0x0e,0x03,0x0c,0x3c,0xfe,0xc0,
0x00,0x0c,0x3c,0xf0,0x00,0x83,0x0f,0x30,0xc0,0xc0,0x03,0xc3,
0x00,0xcc,0x1c,0x03,0x0c,0x3c,0xfc,0xc0,0x00,0x0c,0x3c,0xf0,
0xc0,0xc3,0x0d,0x30,0xc0,0xc0,0x03,0xc3,0x30,0xcc,0x38,0x03,
0x0c,0x3c,0xf8,0xc0,0x00,0x0c,0x3c,0xf8,0xe1,0xe3,0x0c,0x30,
0xc0,0xe1,0x03,0xc3,0x70,0xce,0x70,0x03,0x0c,0x3c,0xf0,0xe1,
0x00,0x0c,0xfc,0x9f,0x7f,0x7f,0xfc,0x3f,0x80,0xff,0x03,0xf3,
0xe3,0xc7,0xe0,0xff,0x0f,0x3c,0xb0,0x7f,0x00,0x0c,0xfc,0x0f,
0x3f,0x3f,0xfc,0x3f,0x00,0x7f,0x03,0xf3,0xc3,0xc3,0xc0,0xff,
0x0f,0x3c,0x30,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xf0,0xf3,0x0f,
0x3f,0xff,0x0f,0x3c,0xf0,0xc0,0x03,0x0f,0xfc,0x3f,0x3f,0x00,
0xf0,0x03,0x03,0x00,0xff,0xf9,0xf7,0x9f,0x7f,0xff,0x0f,0x3c,
0xf0,0xc0,0x03,0x0f,0xfc,0x3f,0x3f,0x00,0xf0,0x83,0x07,0x00,
0x83,0x1f,0x3e,0xf8,0xe1,0x30,0x0c,0x3c,0xf0,0xc0,0x03,0x0f,
0x0c,0x30,0x03,0x03,0x00,0xc3,0x0f,0x00,0x03,0x0f,0x3c,0xf0,
0xc0,0x30,0x0c,0x3c,0xf0,0xc0,0x87,0x0f,0x0c,0x38,0x03,0x07,
0x00,0xe3,0x1c,0x00,0x03,0x0f,0x3c,0xf0,0x00,0x30,0x0c,0x3c,
0xf0,0xc0,0xce,0x0d,0x0c,0x1c,0x03,0x0e,0x00,0x73,0x38,0x00,
0x83,0x0f,0x3c,0xf8,0x01,0x30,0x0c,0x3c,0xf0,0xc0,0xfc,0x1c,
0x0e,0x0e,0x03,0x1c,0x00,0x33,0x30,0x00,0xff,0x0d,0xfc,0x9f,
0x3f,0x30,0x0c,0x3c,0xf0,0xc0,0x78,0x38,0x07,0x07,0x03,0x38,
0x00,0x03,0x00,0x00,0xff,0x0c,0xfc,0x0f,0x7f,0x30,0x0c,0x3c,
0xf0,0xc0,0x78,0xf0,0x83,0x03,0x03,0x70,0x00,0x03,0x00,0x00,
0x03,0xcc,0x3c,0x03,0xe0,0x30,0x0c,0x3c,0xf0,0xcc,0xfc,0xe0,
0xc1,0x01,0x03,0xe0,0x00,0x03,0x00,0x00,0x03,0xcc,0x3d,0x07,
0xc0,0x30,0x0c,0x7c,0xf8,0xcc,0xce,0xc1,0xe0,0x00,0x03,0xc0,
0x01,0x03,0x00,0x00,0x03,0x8c,0x37,0xce,0xc0,0x30,0x0c,0xec,
0xdc,0xcc,0x87,0xc3,0x70,0x00,0x03,0x80,0x03,0x03,0x00,0x00,
0x03,0x1c,0x37,0xdc,0xe1,0x30,0x1c,0xce,0xcf,0xff,0x03,0xc3,
0x30,0x00,0x03,0x00,0x03,0x03,0x00,0x00,0x03,0xf8,0x3f,0xb8,
0x7f,0x30,0xf8,0x87,0x87,0x7f,0x03,0xc3,0xf0,0x3f,0x3f,0x00,
0xf0,0x03,0xc0,0xff,0x03,0xf0,0x3c,0x30,0x3f,0x30,0xf0,0x03,
0x03,0x33,0x03,0xc3,0xf0,0x3f,0x3f,0x00,0xf0,0x03,0xc0,0xff,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x0c,0x00,0x30,0x00,0x00,0x00,0x03,0x00,
0x0f,0x00,0x03,0xc0,0x00,0xcc,0x00,0x3c,0x00,0x00,0x00,0x00,
0x1c,0x00,0x30,0x00,0x00,0x00,0x03,0x80,0x1f,0x00,0x03,0xc0,
0x00,0xcc,0x00,0x3c,0x00,0x00,0x00,0x00,0x38,0x00,0x30,0x00,
0x00,0x00,0x03,0xc0,0x39,0xff,0x03,0x00,0x00,0xc0,0x00,0x30,
0x00,0x00,0x00,0x00,0x70,0x00,0x30,0x00,0x00,0x00,0x03,0xc0,
0xb0,0xff,0x03,0x00,0x00,0xc0,0x00,0x30,0x00,0x00,0x00,0x00,
0xe0,0xf0,0x33,0x0f,0x3f,0x3c,0xf3,0xc3,0xc0,0xc1,0xf3,0xf0,
0x00,0xcf,0x30,0x30,0x3c,0x33,0x0f,0x3f,0xc0,0xf0,0xb7,0x9f,
0x3f,0x7e,0xfb,0xc7,0xc0,0xc0,0xfb,0xf1,0x00,0xcf,0x38,0x30,
0xfc,0xb7,0x9f,0x7f,0x00,0x00,0xfe,0xf9,0x01,0xe7,0x1f,0xfe,
0xc3,0xc0,0x9f,0xc3,0x00,0xcc,0x1c,0x30,0xec,0xff,0xf9,0xe1,
0x00,0x00,0xfc,0xf0,0x00,0xc3,0x0f,0xfc,0xc3,0xc1,0x0f,0xc3,
0x00,0xcc,0x0e,0x30,0xcc,0xfc,0xf0,0xc0,0x00,0xf0,0x3f,0xf0,
0x00,0x03,0xff,0xcf,0x80,0xff,0x03,0xc3,0x00,0xcc,0x07,0x30,
0xcc,0x3c,0xf0,0xc0,0x00,0xf8,0x3f,0xf0,0x00,0x03,0xff,0xcf,
0x00,0xff,0x03,0xc3,0x00,0xcc,0x07,0x30,0xcc,0x3c,0xf0,0xc0,
0x00,0x1c,0x3c,0xf0,0xc0,0x03,0x0f,0xc0,0x00,0xc0,0x03,0xc3,
0x30,0xcc,0x0e,0x30,0x0c,0x3c,0xf0,0xc0,0x00,0x1c,0x3c,0xf8,
0xe1,0x07,0x1f,0xc0,0x00,0xe0,0x03,0xc3,0x70,0xce,0x1c,0x30,
0x0c,0x3c,0xf0,0xe1,0x00,0xf8,0xff,0x9f,0x7f,0xfe,0xfb,0xc3,
0x00,0x7f,0x03,0xf3,0xe3,0xc7,0x38,0xfc,0x0c,0x3c,0xb0,0x7f,
0x00,0xf0,0xff,0x0f,0x3f,0xfc,0xf3,0xc3,0x00,0x3f,0x03,0xf3,
0xc3,0xc3,0x30,0xfc,0x0c,0x3c,0x30,0x3f,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x30,0x30,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x30,
0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x1c,0x30,0xe0,0x00,0x03,0x0c,
0x00,0x00,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x0c,0x30,0xc0,0x00,0x07,0x0e,0xff,0xf0,0x3c,0x0f,
0x3f,0x3f,0x0c,0x3c,0xf0,0xc0,0x03,0x0f,0xfc,0x3f,0x0c,0x30,
0xc0,0x00,0x0e,0x07,0xff,0xf9,0xbd,0x9f,0x3f,0x3f,0x0c,0x3c,
0xf0,0xc0,0x87,0x0f,0xfc,0x3f,0x0e,0x30,0xc0,0x01,0x9c,0x03,
0x83,0x1f,0xff,0xf9,0x01,0x0c,0x0c,0x3c,0xf0,0xc0,0xce,0x0d,
0x0c,0x1c,0x03,0x30,0x00,0xf3,0xfc,0xf3,0x83,0x1f,0xff,0xf0,
0x01,0x0c,0x0c,0x3c,0xf0,0xc0,0xfc,0x1c,0x0c,0x0e,0x03,0x30,
0x00,0xf3,0xfc,0xf3,0xff,0xf9,0x3f,0x80,0x3f,0x0c,0x0c,0x3c,
0xf0,0xc0,0x78,0xf8,0x0f,0x07,0x0e,0x30,0xc0,0x01,0x9c,0x03,
0xff,0xf0,0x3f,0x00,0x7f,0x0c,0x0c,0x7c,0xf8,0xc0,0x78,0xf0,
0x8f,0x03,0x0c,0x30,0xc0,0x00,0x0e,0x07,0x03,0x00,0x3c,0x00,
0xe0,0x0c,0x0f,0xef,0xdc,0xcc,0xfc,0x00,0xcc,0x01,0x0c,0x30,
0xc0,0x00,0x07,0x0e,0x03,0x00,0x3c,0x00,0xe0,0x9c,0x9f,0xcf,
0xcf,0xff,0xce,0x01,0xee,0x00,0x1c,0x30,0xe0,0x00,0x03,0x0c,
0x03,0x00,0x3c,0xc0,0x7f,0xf8,0xf9,0x8d,0x87,0x7f,0x87,0xf3,
0xf7,0x3f,0x38,0x30,0x70,0x00,0x00,0x00,0x03,0x00,0x3c,0xc0,
0x3f,0xf0,0xf0,0x0c,0x03,0x33,0x03,0xf3,0xf3,0x3f,0x30,0x30,
0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00};

460
src/frsky.cpp Normal file
View file

@ -0,0 +1,460 @@
/*
* Authors (alpahbetical order)
* - Bertrand Songis <bsongis@gmail.com>
* - Bryan J. Rentoul (Gruvin) <gruvin@gmail.com>
*
* Original contributors
* - Philip Moss Adapted first frsky functions from jeti.cpp code by
* - Karl Szmutny <shadow@privy.de>
* 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 "gruvin9x.h"
#include "frsky.h"
// Enumerate FrSky packet codes
#define LINKPKT 0xfe
#define USRPKT 0xfd
#define A11PKT 0xfc
#define A12PKT 0xfb
#define A21PKT 0xfa
#define A22PKT 0xf9
#define ALRM_REQUEST 0xf8
#define RSSI1PKT 0xf7
#define RSSI2PKT 0xf6
#define START_STOP 0x7e
#define BYTESTUFF 0x7d
#define STUFF_MASK 0x20
uint8_t frskyRxBuffer[19]; // Receive buffer. 9 bytes (full packet), worst case 18 bytes with byte-stuffing (+1)
uint8_t frskyTxBuffer[19]; // Ditto for transmit buffer
uint8_t frskyTxBufferCount = 0;
uint8_t FrskyRxBufferReady = 0;
uint8_t frskyStreaming = 0;
uint8_t frskyTxISRIndex = 0;
FrskyData frskyTelemetry[2];
FrskyData frskyRSSI[2];
struct FrskyAlarm {
uint8_t level; // The alarm's 'urgency' level. 0=disabled, 1=yellow, 2=orange, 3=red
uint8_t greater; // 1 = 'if greater than'. 0 = 'if less than'
uint8_t value; // The threshold above or below which the alarm will sound
};
struct FrskyAlarm frskyAlarms[4];
void frskyPushValue(uint8_t & i, uint8_t value);
/*
Called from somewhere in the main loop or a low prioirty interrupt
routine perhaps. This funtcion processes Fr-Sky telemetry data packets
assembled byt he USART0_RX_vect) ISR function (below) and stores
extracted data in global variables for use by other parts of the program.
Packets can be any of the following:
- A1/A2/RSSI telemtry data
- Alarm level/mode/threshold settings for Ch1A, Ch1B, Ch2A, Ch2B
- User Data packets
User Data packets are not yet implementedi (they are simply ignored),
but will likely one day contain the likes of GPS long/lat/alt/speed,
AoA, airspeed, etc.
*/
void processFrskyPacket(uint8_t *packet)
{
// What type of packet?
switch (packet[0])
{
case A22PKT:
case A21PKT:
case A12PKT:
case A11PKT:
{
struct FrskyAlarm *alarmptr ;
alarmptr = &frskyAlarms[(packet[0]-A22PKT)] ;
alarmptr->value = packet[1];
alarmptr->greater = packet[2] & 0x01;
alarmptr->level = packet[3] & 0x03;
}
break;
case LINKPKT: // A1/A2/RSSI values
frskyTelemetry[0].set(packet[1]);
frskyTelemetry[1].set(packet[2]);
frskyRSSI[0].set(packet[3]);
frskyRSSI[1].set(packet[4] / 2);
break;
case USRPKT: // User Data packet -- not yet implemented
break;
}
FrskyRxBufferReady = 0;
frskyStreaming = FRSKY_TIMEOUT10ms; // reset counter only if valid frsky packets are being detected
}
// Receive buffer state machine state defs
#define frskyDataIdle 0
#define frskyDataStart 1
#define frskyDataInFrame 2
#define frskyDataXOR 3
/*
Receive serial (RS-232) characters, detecting and storing each Fr-Sky
0x7e-framed packet as it arrives. When a complete packet has been
received, process its data into storage variables. NOTE: This is an
interrupt routine and should not get too lengthy. I originally had
the buffer being checked in the perMain function (because per10ms
isn't quite often enough for data streaming at 9600baud) but alas
that scheme lost packets also. So each packet is parsed as it arrives,
directly at the ISR function (through a call to frskyProcessPacket).
If this proves a problem in the future, then I'll just have to implement
a second buffer to receive data while one buffer is being processed (slowly).
*/
#ifndef SIMU
ISR(USART0_RX_vect)
{
uint8_t stat;
uint8_t data;
static uint8_t numPktBytes = 0;
static uint8_t dataState = frskyDataIdle;
UCSR0B &= ~(1 << RXCIE0); // disable Interrupt
sei() ;
stat = UCSR0A; // USART control and Status Register 0 A
/*
bit 7 6 5 4 3 2 1 0
RxC0 TxC0 UDRE0 FE0 DOR0 UPE0 U2X0 MPCM0
RxC0: Receive complete
TXC0: Transmit Complete
UDRE0: USART Data Register Empty
FE0: Frame Error
DOR0: Data OverRun
UPE0: USART Parity Error
U2X0: Double Tx Speed
PCM0: MultiProcessor Comms Mode
*/
// rh = UCSR0B; //USART control and Status Register 0 B
/*
bit 7 6 5 4 3 2 1 0
RXCIE0 TxCIE0 UDRIE0 RXEN0 TXEN0 UCSZ02 RXB80 TXB80
RxCIE0: Receive Complete int enable
TXCIE0: Transmit Complete int enable
UDRIE0: USART Data Register Empty int enable
RXEN0: Rx Enable
TXEN0: Tx Enable
UCSZ02: Character Size bit 2
RXB80: Rx data bit 8
TXB80: Tx data bit 8
*/
data = UDR0; // USART data register 0
if (stat & ((1 << FE0) | (1 << DOR0) | (1 << UPE0)))
{ // discard buffer and start fresh on any comms error
FrskyRxBufferReady = 0;
numPktBytes = 0;
}
else
{
if (FrskyRxBufferReady == 0) // can't get more data if the buffer hasn't been cleared
{
switch (dataState)
{
case frskyDataStart:
if (data == START_STOP) break; // Remain in userDataStart if possible 0x7e,0x7e doublet found.
if (numPktBytes < 19)
frskyRxBuffer[numPktBytes++] = data;
dataState = frskyDataInFrame;
break;
case frskyDataInFrame:
if (data == BYTESTUFF)
{
dataState = frskyDataXOR; // XOR next byte
break;
}
if (data == START_STOP) // end of frame detected
{
processFrskyPacket(frskyRxBuffer); // FrskyRxBufferReady = 1;
dataState = frskyDataIdle;
break;
}
frskyRxBuffer[numPktBytes++] = data;
break;
case frskyDataXOR:
if (numPktBytes < 19)
frskyRxBuffer[numPktBytes++] = data ^ STUFF_MASK;
dataState = frskyDataInFrame;
break;
case frskyDataIdle:
if (data == START_STOP)
{
numPktBytes = 0;
dataState = frskyDataStart;
}
break;
} // switch
} // if (FrskyRxBufferReady == 0)
}
cli() ;
UCSR0B |= (1 << RXCIE0); // enable Interrupt
}
/*
USART0 (transmit) Data Register Emtpy ISR
Usef to transmit FrSky data packets, which are buffered in frskyTXBuffer.
*/
ISR(USART0_UDRE_vect)
{
if (frskyTxBufferCount > 0) {
UDR0 = frskyTxBuffer[frskyTxISRIndex++];
frskyTxBufferCount--;
}
else {
UCSR0B &= ~(1 << UDRIE0); // disable UDRE0 interrupt
}
}
#endif
/******************************************/
void frskyTransmitBuffer()
{
frskyTxISRIndex = 0;
UCSR0B |= (1 << UDRIE0); // enable UDRE0 interrupt
}
uint8_t FrskyAlarmSendState = 0 ;
uint8_t FrskyDelay = 0 ;
void FRSKY10mspoll(void)
{
if (FrskyDelay)
{
FrskyDelay -= 1 ;
return ;
}
if (frskyTxBufferCount)
{
return; // we only have one buffer. If it's in use, then we can't send yet.
}
// Now send a packet
{
FrskyAlarmSendState -= 1 ;
uint8_t channel = 1 - (FrskyAlarmSendState / 2);
uint8_t alarm = 1 - (FrskyAlarmSendState % 2);
uint8_t i = 0;
frskyTxBuffer[i++] = START_STOP; // Start of packet
frskyTxBuffer[i++] = (A22PKT + FrskyAlarmSendState); // fc - fb - fa - f9
frskyPushValue(i, g_model.frsky.channels[channel].alarms_value[alarm]);
{
uint8_t *ptr ;
ptr = &frskyTxBuffer[i] ;
*ptr++ = ALARM_GREATER(channel, alarm);
*ptr++ = ALARM_LEVEL(channel, alarm);
*ptr++ = 0x00 ;
*ptr++ = 0x00 ;
*ptr++ = 0x00 ;
*ptr++ = 0x00 ;
*ptr++ = 0x00 ;
*ptr++ = START_STOP; // End of packet
i += 8 ;
}
FrskyDelay = 5 ; // 50mS
frskyTxBufferCount = i;
frskyTransmitBuffer();
}
}
// Send packet requesting all alarm settings be sent back to us
void FRSKY_setRSSIAlarms(void)
{
if (frskyTxBufferCount) return; // we only have one buffer. If it's in use, then we can't send. Sorry.
uint8_t i = 0;
for (int alarm=0; alarm<2; alarm++) {
frskyTxBuffer[i++] = START_STOP; // Start of packet
frskyTxBuffer[i++] = (RSSI1PKT-alarm); // f7 - f6
frskyPushValue(i, g_eeGeneral.frskyRssiAlarms[alarm].value+50-(10*i));
{
uint8_t *ptr ;
ptr = &frskyTxBuffer[i] ;
*ptr++ = 0x00 ;
*ptr++ = g_eeGeneral.frskyRssiAlarms[alarm].level;
*ptr++ = 0x00 ;
*ptr++ = 0x00 ;
*ptr++ = 0x00 ;
*ptr++ = 0x00 ;
*ptr++ = 0x00 ;
*ptr++ = START_STOP; // End of packet
i += 8 ;
}
}
frskyTxBufferCount = i;
frskyTransmitBuffer();
}
bool FRSKY_alarmRaised(uint8_t idx)
{
for (int i=0; i<2; i++) {
if (ALARM_LEVEL(idx, i) != alarm_off) {
if (ALARM_GREATER(idx, i)) {
if (frskyTelemetry[idx].value > g_model.frsky.channels[idx].alarms_value[i])
return true;
}
else {
if (frskyTelemetry[idx].value < g_model.frsky.channels[idx].alarms_value[i])
return true;
}
}
}
return false;
}
inline void FRSKY_EnableTXD(void)
{
frskyTxBufferCount = 0;
UCSR0B |= (1 << TXEN0) | (1 << UDRIE0); // enable TX and TX interrupt
}
inline void FRSKY_EnableRXD(void)
{
UCSR0B |= (1 << RXEN0); // enable RX
UCSR0B |= (1 << RXCIE0); // enable Interrupt
}
#if 0
void FRSKY_DisableTXD(void)
{
UCSR0B &= ~((1 << TXEN0) | (1 << UDRIE0)); // disable TX pin and interrupt
}
void FRSKY_DisableRXD(void)
{
UCSR0B &= ~(1 << RXEN0); // disable RX
UCSR0B &= ~(1 << RXCIE0); // disable Interrupt
}
#endif
void FRSKY_Init(void)
{
// clear frsky variables
memset(frskyAlarms, 0, sizeof(frskyAlarms));
resetTelemetry();
DDRE &= ~(1 << DDE0); // set RXD0 pin as input
PORTE &= ~(1 << PORTE0); // disable pullup on RXD0 pin
#undef BAUD
#define BAUD 9600
#ifndef SIMU
#include <util/setbaud.h>
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
UCSR0A &= ~(1 << U2X0); // disable double speed operation.
// set 8 N1
UCSR0B = 0 | (0 << RXCIE0) | (0 << TXCIE0) | (0 << UDRIE0) | (0 << RXEN0) | (0 << TXEN0) | (0 << UCSZ02);
UCSR0C = 0 | (1 << UCSZ01) | (1 << UCSZ00);
while (UCSR0A & (1 << RXC0)) UDR0; // flush receive buffer
#endif
// These should be running right from power up on a FrSky enabled '9X.
FRSKY_EnableTXD(); // enable FrSky-Telemetry reception
FRSKY_EnableRXD(); // enable FrSky-Telemetry reception
}
#if 0
// Send packet requesting all alarm settings be sent back to us
void frskyAlarmsRefresh()
{
if (frskyTxBufferCount) return; // we only have one buffer. If it's in use, then we can't send. Sorry.
{
uint8_t *ptr ;
ptr = &frskyTxBuffer[0] ;
*ptr++ = START_STOP; // Start of packet
*ptr++ = ALRM_REQUEST;
*ptr++ = 0x00 ;
*ptr++ = 0x00 ;
*ptr++ = 0x00 ;
*ptr++ = 0x00 ;
*ptr++ = 0x00 ;
*ptr++ = 0x00 ;
*ptr++ = 0x00 ;
*ptr++ = 0x00 ;
*ptr++ = START_STOP; // End of packet
}
frskyTxBufferCount = 11;
frskyTransmitBuffer();
}
#endif
void frskyPushValue(uint8_t & i, uint8_t value)
{
// byte stuff the only byte than might need it
if (value == START_STOP) {
frskyTxBuffer[i++] = BYTESTUFF;
frskyTxBuffer[i++] = 0x5e;
}
else if (value == BYTESTUFF) {
frskyTxBuffer[i++] = BYTESTUFF;
frskyTxBuffer[i++] = 0x5d;
}
else {
frskyTxBuffer[i++] = value;
}
}
void FrskyData::set(uint8_t value)
{
this->value = value;
if (!max || max < value)
max = value;
if (!min || min > value)
min = value;
}
void resetTelemetry()
{
memset(frskyTelemetry, 0, sizeof(frskyTelemetry));
memset(frskyRSSI, 0, sizeof(frskyRSSI));
}

62
src/frsky.h Normal file
View file

@ -0,0 +1,62 @@
/*
* Author - Bertrand Songis <bsongis@gmail.com>
*
* frsky.cpp original authors - Bryan J.Rentoul (Gruvin) <gruvin@gmail.com> and Philip Moss Adapted from jeti.cpp code by Karl
* Szmutny <shadow@privy.de>*
*
* 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 FRSKY_H
#define FRSKY_H
#include <inttypes.h>
// .20 seconds
#define FRSKY_TIMEOUT10ms 20
enum AlarmLevel {
alarm_off = 0,
alarm_yellow = 1,
alarm_orange = 2,
alarm_red = 3
};
#define ALARM_GREATER(channel, alarm) ((g_model.frsky.channels[channel].alarms_greater >> alarm) & 1)
#define ALARM_LEVEL(channel, alarm) ((g_model.frsky.channels[channel].alarms_level >> (2*alarm)) & 3)
struct FrskyData {
uint8_t value;
uint8_t min;
uint8_t max;
void set(uint8_t value);
};
// Global Fr-Sky telemetry data variables
extern uint8_t frskyStreaming; // >0 (true) == data is streaming in. 0 = nodata detected for some time
extern uint8_t FrskyAlarmSendState;
extern FrskyData frskyTelemetry[2];
extern FrskyData frskyRSSI[2];
void FRSKY_Init(void);
void FRSKY10mspoll(void);
inline void FRSKY_setModelAlarms(void)
{
FrskyAlarmSendState = 4 ;
}
bool FRSKY_alarmRaised(uint8_t idx);
void resetTelemetry();
#endif

36
src/fuses_2561.txt Normal file
View file

@ -0,0 +1,36 @@
The ATmega2561 uses quite different bit assignment for its option fuses
Fuse defaults for ATmega64A
Default values for ATmega64 are:
hfuse = 0b10011001 (0x99)
lfuse = 0b11100001 (0xE1)
efuse = 0b11111101 (0xFD)
Preferred values for ATmega64A are:
hfuse = 0x11
lfuse = 0x1F
efuse = 0xff
... which includes EESAVE feature, so that the EEPROM model data does NOT get
erased during chip erase prior to re-programming ... respectively.
- - - - - - - - - - - - - - - - - - - - - -
Fuse defaults for ATmega2560/1 are:
hfuse = 0b10011001 (0x99)
lfuse = 0b01100010 (0x62)
efuse = 0b11111111 (0xFF)
The bit assignments ae quite different from the AT'64A in all cases.
Preferred vale es for ATmega2560/1 are:
hfuse = 0b10011001 (0x11) [OCD enabled, JTAG enabled, WDT disabled :(,
EESAVE enabled, BOOT at 0x0000]
lfuse = 0b01101110 (0xd7) [CKDIV8 disabled, CKOUT disabled,
Full Swing Xtal Osc, 16K CK + 0ms reset,
BOD enabled]
efuse = 0b11111111 (0xfc) [ Brown out detector (BOD) = 4.3V ]

552
src/general_menus.cpp Normal file
View file

@ -0,0 +1,552 @@
/*
* 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.
*
*/
#include "menus.h"
#include "sticks.lbm"
enum EnumTabDiag {
e_Setup,
#if defined(PCBV3)
e_FrskyTime,
#endif
e_Trainer,
e_Vers,
e_Keys,
e_Ana,
e_Calib
};
void menuProcSetup(uint8_t event);
#if defined(PCBV3)
void menuProcTime(uint8_t event);
#endif
void menuProcTrainer(uint8_t event);
void menuProcDiagVers(uint8_t event);
void menuProcDiagKeys(uint8_t event);
void menuProcDiagAna(uint8_t event);
void menuProcDiagCalib(uint8_t event);
MenuFuncP_PROGMEM APM menuTabDiag[] = {
menuProcSetup,
#if defined(PCBV3)
menuProcTime,
#endif
menuProcTrainer,
menuProcDiagVers,
menuProcDiagKeys,
menuProcDiagAna,
menuProcDiagCalib
};
void menuProcSetup(uint8_t event)
{
#define COUNT_ITEMS 19
#define PARAM_OFS 17*FW
SIMPLE_MENU("RADIO SETUP", menuTabDiag, e_Setup, COUNT_ITEMS+1);
int8_t sub = m_posVert;
// last 2 lines (radio mode) are non break-able
if(s_pgOfs==COUNT_ITEMS-7) s_pgOfs= sub<(COUNT_ITEMS-4) ? COUNT_ITEMS-8 : COUNT_ITEMS-6;
uint8_t y = 1*FH;
uint8_t subN = 1;
if(s_pgOfs<subN) {
lcd_puts_P(0, y,PSTR("Beeper"));
lcd_putsnAtt(PARAM_OFS - FW, y, PSTR("Quiet""NoKey""Norm ""Long ""xLong")+5*g_eeGeneral.beeperVal,5,(sub==subN ? INVERS:0));
if(sub==subN) CHECK_INCDEC_GENVAR(event, g_eeGeneral.beeperVal, 0, 4);
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
lcd_puts_P(0, y,PSTR("Contrast"));
lcd_outdezAtt(PARAM_OFS,y,g_eeGeneral.contrast,(sub==subN ? INVERS : 0)|LEFT);
if(sub==subN) {
CHECK_INCDEC_GENVAR(event, g_eeGeneral.contrast, 10, 45);
lcdSetRefVolt(g_eeGeneral.contrast);
}
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
lcd_puts_P(0, y,PSTR("Battery warning"));
putsVolts(PARAM_OFS, y, g_eeGeneral.vBatWarn, (sub==subN ? INVERS : 0)|LEFT);
if(sub==subN) CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatWarn, 40, 120); //4-12V
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
lcd_puts_P(0, y,PSTR("Inactivity alarm"));
lcd_outdezAtt(PARAM_OFS, y, g_eeGeneral.inactivityTimer, (sub==subN ? INVERS : 0)|LEFT);
lcd_putc(lcd_lastPos, y, 'm');
if(sub==subN) g_eeGeneral.inactivityTimer = checkIncDec(event, g_eeGeneral.inactivityTimer, 0, 250, EE_GENERAL); //0..250minutes
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
lcd_puts_P(0, y,PSTR("Filter ADC"));
lcd_putsnAtt(PARAM_OFS, y, PSTR("SINGOSMPFILT")+4*g_eeGeneral.filterInput,4,(sub==subN ? INVERS:0));
if(sub==subN) CHECK_INCDEC_GENVAR(event, g_eeGeneral.filterInput, 0, 2);
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
lcd_puts_P(0, y,PSTR("Throttle reverse"));
menu_lcd_onoff( PARAM_OFS, y, g_eeGeneral.throttleReversed, sub==subN ) ;
if(sub==subN) CHECK_INCDEC_GENVAR(event, g_eeGeneral.throttleReversed, 0, 1);
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
lcd_puts_P(0, y,PSTR("Minute beep"));
menu_lcd_onoff( PARAM_OFS, y, g_eeGeneral.minuteBeep, sub==subN ) ;
if(sub==subN) CHECK_INCDEC_GENVAR(event, g_eeGeneral.minuteBeep, 0, 1);
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
lcd_puts_P(0, y,PSTR("Beep countdown"));
menu_lcd_onoff( PARAM_OFS, y, g_eeGeneral.preBeep, sub==subN ) ;
if(sub==subN) CHECK_INCDEC_GENVAR(event, g_eeGeneral.preBeep, 0, 1);
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
lcd_puts_P(0, y,PSTR("Flash on beep"));
menu_lcd_onoff( PARAM_OFS, y, g_eeGeneral.flashBeep, sub==subN ) ;
if(sub==subN) CHECK_INCDEC_GENVAR(event, g_eeGeneral.flashBeep, 0, 1);
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
lcd_puts_P(0, y,PSTR("Light switch"));
putsSwitches(PARAM_OFS,y,g_eeGeneral.lightSw,sub==subN ? INVERS : 0);
if(sub==subN) CHECK_INCDEC_GENVAR(event, g_eeGeneral.lightSw, -MAX_SWITCH, MAX_SWITCH);
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
lcd_puts_P(0, y,PSTR("Light off after"));
if(g_eeGeneral.lightAutoOff) {
lcd_outdezAtt(PARAM_OFS, y, g_eeGeneral.lightAutoOff*5,LEFT|(sub==subN ? INVERS : 0));
lcd_putc(lcd_lastPos, y, 's');
}
else
lcd_putsnAtt(PARAM_OFS, y, PSTR("OFF"),3,(sub==subN ? INVERS:0));
if(sub==subN) CHECK_INCDEC_GENVAR(event, g_eeGeneral.lightAutoOff, 0, 600/5);
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
uint8_t b = 1-g_eeGeneral.disableSplashScreen;
lcd_puts_P(0, y,PSTR("Splash screen"));
menu_lcd_onoff( PARAM_OFS, y, b, sub==subN ) ;
if(sub==subN)
{
CHECK_INCDEC_GENVAR(event, b, 0, 1);
g_eeGeneral.disableSplashScreen = 1-b;
}
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
uint8_t b = 1-g_eeGeneral.disableThrottleWarning;
lcd_puts_P(0, y,PSTR("Throttle Warning"));
menu_lcd_onoff( PARAM_OFS, y, b, sub==subN ) ;
if(sub==subN)
{
CHECK_INCDEC_GENVAR(event, b, 0, 1);
g_eeGeneral.disableThrottleWarning = 1-b;
}
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
lcd_puts_P(0, y,PSTR("Switch Warning"));
lcd_putsnAtt(PARAM_OFS, y, PSTR("Down""OFF ""Up ")+4*(1+g_eeGeneral.switchWarning),4,(sub==subN ? INVERS:0));
if(sub==subN)
CHECK_INCDEC_GENVAR(event, g_eeGeneral.switchWarning, -1, 1);
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
uint8_t b = 1-g_eeGeneral.disableMemoryWarning;
lcd_puts_P(0, y,PSTR("Memory Warning"));
menu_lcd_onoff( PARAM_OFS, y, b, sub==subN ) ;
if(sub==subN)
{
CHECK_INCDEC_GENVAR(event, b, 0, 1);
g_eeGeneral.disableMemoryWarning = 1-b;
}
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
uint8_t b = 1-g_eeGeneral.disableAlarmWarning;
lcd_puts_P(0, y,PSTR("Alarm Warning"));
menu_lcd_onoff( PARAM_OFS, y, b, sub==subN ) ;
if(sub==subN)
{
CHECK_INCDEC_GENVAR(event, b, 0, 1);
g_eeGeneral.disableAlarmWarning = 1-b;
}
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
uint8_t b = g_eeGeneral.enableTelemetryAlarm;
lcd_puts_P(0, y,PSTR("NO DATA Alarm"));
menu_lcd_onoff( PARAM_OFS, y, b, sub==subN ) ;
if(sub==subN)
{
CHECK_INCDEC_GENVAR(event, b, 0, 1);
g_eeGeneral.enableTelemetryAlarm = b;
}
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
uint8_t attr = sub==subN?INVERS:0;
lcd_puts_P(0, y,PSTR("Rx Channel Ord"));// RAET->AETR
for (uint8_t i=1; i<=4; i++)
putsChnLetter((16+i)*FW, y, i, attr);
if(attr) CHECK_INCDEC_GENVAR(event, g_eeGeneral.templateSetup, 0, 23);
if((y+=FH)>7*FH) return;
}subN++;
if(s_pgOfs<subN) {
lcd_puts_P( 1*FW, y, PSTR("Mode"));//sub==3?INVERS:0);
if(y<7*FH) {for(uint8_t i=0; i<4; i++) lcd_img((6+4*i)*FW, y, sticks,i,0); }
if((y+=FH)>7*FH) return;
lcd_putcAtt( 3*FW, y, '1'+g_eeGeneral.stickMode,sub==subN?INVERS:0);
for(uint8_t i=0; i<4; i++) putsChnRaw( (6+4*i)*FW, y,i+1,0);//sub==3?INVERS:0);
if(sub==subN) CHECK_INCDEC_GENVAR(event,g_eeGeneral.stickMode,0,3);
if((y+=FH)>7*FH) return;
}
}
#if defined(PCBV3)
// SD card interface contains Real-Time-Clock chip
void menuProcTime(uint8_t event)
{
MENU("DATE AND TIME", menuTabDiag, e_FrskyTime, 3, {0, 2/*, 2*/});
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;
switch(event)
{
case EVT_KEY_LONG(KEY_MENU):
// get data time from RTC chip (may not implement)
killEvents(event);
break;
case EVT_KEY_FIRST(KEY_MENU):
if (sub >= 0 && !s_editMode) // 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
RTC rtc;
rtc.year = t.tm_year + 1900;
rtc.month = t.tm_mon + 1;
rtc.mday = t.tm_mday;
rtc.hour = t.tm_hour;
rtc.min = t.tm_min;
rtc.sec = t.tm_sec;
rtc.wday = t.tm_wday + 1;
rtc_settime(&rtc);
}
break;
}
if (!s_editMode) 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, ':');
for(uint8_t i=0; i<2; i++) // 2 rows, date then time
{
uint8_t y=(i*2+2)*FH;
lcd_putsnAtt(0, y, PSTR("DATE:""TIME:")+i*5, 5, 0);
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;
switch(i)
{
case 0: // DATE
switch(j)
{
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);
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);
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);
break;
}
break;
case 1:
switch (j)
{
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);
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);
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);
break;
}
break;
}
}
}
}
#endif
void menuProcTrainer(uint8_t event)
{
MENU("TRAINER", menuTabDiag, e_Trainer, 7, {0, 2, 2, 2, 2/*, 0, 0*/});
int8_t sub = m_posVert;
uint8_t subSub = m_posHorz;
uint8_t y;
bool edit;
uint8_t blink ;
if (SLAVE_MODE) { // i am the slave
lcd_puts_P(7*FW, 3*FH, PSTR("Slave"));
return;
}
lcd_puts_P(3*FW, 1*FH, PSTR("mode % src"));
sub--;
y = 2*FH;
blink = s_editMode ? BLINK : INVERS ;
for (uint8_t i=0; i<4; i++) {
uint8_t chan = chout_ar[g_eeGeneral.templateSetup][i]; // G: Issue 30.
volatile TrainerMix *td = &g_eeGeneral.trainer.mix[chan-1];
putsChnRaw(0, y, chan, 0);
edit = (sub==i && subSub==0);
lcd_putsnAtt(4*FW, y, PSTR("off += :=")+3*td->mode, 3, edit ? blink : 0);
if (edit && s_editMode)
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)
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)
CHECK_INCDEC_GENVAR(event, td->srcChn, 0, 3);
edit = (sub==i && subSub==3);
y += FH;
}
lcd_puts_P(0*FW, y, PSTR("Multiplier"));
lcd_outdezAtt(13*FW, y, g_eeGeneral.PPM_Multiplier+10, (sub==4 ? INVERS : 0)|PREC1);
if(sub==4) CHECK_INCDEC_GENVAR(event, g_eeGeneral.PPM_Multiplier, -10, 40);
y += FH;
edit = (sub==5);
lcd_putsAtt(0*FW, y, PSTR("Cal"), edit ? INVERS : 0);
for (uint8_t i=0; i<4; i++) {
uint8_t x = (i*8+16)*FW/2;
#if defined (DECIMALS_DISPLAYED)
lcd_outdezAtt(x , y, (g_ppmIns[i]-g_eeGeneral.trainer.calib[i])*2, PREC1);
#else
lcd_outdezAtt(x , y, (g_ppmIns[i]-g_eeGeneral.trainer.calib[i])/5, 0);
#endif
}
if (edit) {
if (event==EVT_KEY_FIRST(KEY_MENU)){
memcpy(g_eeGeneral.trainer.calib, g_ppmIns, sizeof(g_eeGeneral.trainer.calib));
eeDirty(EE_GENERAL);
beepKey();
}
}
}
void menuProcDiagVers(uint8_t event)
{
SIMPLE_MENU("VERSION", menuTabDiag, e_Vers, 1);
lcd_puts_P(0, 2*FH,stamp4 );
lcd_puts_P(0, 3*FH,stamp1 );
lcd_puts_P(0, 4*FH,stamp5 );
lcd_puts_P(0, 5*FH,stamp2 );
lcd_puts_P(0, 6*FH,stamp3 );
lcd_puts_P(0, 7*FH,PSTR("EEPROM v"));
lcd_outdezAtt(8*FW, 7*FH, g_eeGeneral.myVers, LEFT);
}
void menuProcDiagKeys(uint8_t event)
{
SIMPLE_MENU("DIAG", menuTabDiag, e_Keys, 1);
for(uint8_t i=0; i<9; i++)
{
uint8_t y=i*FH; //+FH;
if(i>(SW_ID0-SW_BASE_DIAG)) y-=FH; //overwrite ID0
bool t=keyState((EnumKeys)(SW_BASE_DIAG+i));
putsSwitches(8*FW, y, i+1, 0); //ohne off,on
lcd_putcAtt(11*FW+2, y, t+'0', t ? INVERS : 0);
}
for(uint8_t i=0; i<6; i++)
{
uint8_t y=(5-i)*FH+2*FH;
bool t=keyState((EnumKeys)(KEY_MENU+i));
lcd_putsn_P(0, y, PSTR(" Menu Exit Down UpRight Left")+5*i, 5);
lcd_putcAtt(5*FW+2, y, t+'0', t);
}
lcd_putsn_P(14*FW, 3*FH, PSTR("Trim- +"), 7);
for(uint8_t i=0; i<4; i++)
{
uint8_t y=i*FH+FH*4;
lcd_img(14*FW, y, sticks,i,0);
bool tm=keyState((EnumKeys)(TRM_BASE+2*i));
bool tp=keyState((EnumKeys)(TRM_BASE+2*i+1));
lcd_putcAtt(18*FW, y, tm+'0',tm ? INVERS : 0);
lcd_putcAtt(20*FW, y, tp+'0',tp ? INVERS : 0);
}
}
void menuProcDiagAna(uint8_t event)
{
SIMPLE_MENU("ANA", menuTabDiag, e_Ana, 2);
int8_t sub = m_posVert ;
for(uint8_t i=0; i<8; i++)
{
uint8_t y=i*FH;
lcd_putsn_P( 4*FW, y,PSTR("A1A2A3A4A5A6A7A8")+2*i,2);
lcd_outhex4( 8*FW, y,anaIn(i));
if(i<7) lcd_outdez8(17*FW, y, (int32_t)calibratedStick[i]*100/1024);
if(i==7) putsVolts(17*FW, y, g_vbat100mV, (sub==1 ? INVERS : 0));
}
// lcd_outdezAtt( 21*FW, 3*FH, g_eeGeneral.vBatCalib, 0) ;
// lcd_outdezAtt( 21*FW, 4*FH, abRunningAvg, 0) ;
// Display raw BandGap result (debug)
lcd_putsn_P( 19*FW, 5*FH,PSTR("BG"),2) ;
lcd_outdezAtt(21*FW, 6*FH, BandGap, 0);
lcd_outdezAtt(21*FW, 7*FH, anaIn(7)*35/512, PREC1);
if(sub==1) CHECK_INCDEC_GENVAR(event, g_eeGeneral.vBatCalib, -127, 127);
}
void menuProcDiagCalib(uint8_t event)
{
SIMPLE_MENU("CALIBRATION", menuTabDiag, e_Calib, 4);
int8_t sub = m_posVert ;
static int16_t midVals[7];
static int16_t loVals[7];
static int16_t hiVals[7];
for(uint8_t i=0; i<7; i++) { //get low and high vals for sticks and trims
int16_t vt = anaIn(i);
loVals[i] = min(vt,loVals[i]);
hiVals[i] = max(vt,hiVals[i]);
//if(i>=4) midVals[i] = (loVals[i] + hiVals[i])/2;
}
switch(event)
{
case EVT_ENTRY:
for(uint8_t i=0; i<7; i++) loVals[i] = 15000;
break;
case EVT_KEY_BREAK(KEY_DOWN): // !! achtung sub schon umgesetzt
switch(sub)
{
case 2: //get mid
for(uint8_t i=0; i<7; i++)midVals[i] = anaIn(i);
beepKey();
break;
case 3:
for(uint8_t i=0; i<7; i++)
if(abs(loVals[i]-hiVals[i])>50) {
g_eeGeneral.calibMid[i] = midVals[i];
int16_t v = midVals[i] - loVals[i];
g_eeGeneral.calibSpanNeg[i] = v - v/64;
v = hiVals[i] - midVals[i];
g_eeGeneral.calibSpanPos[i] = v - v/64;
}
int16_t sum=0;
for(uint8_t i=0; i<12;i++) sum+=g_eeGeneral.calibMid[i];
g_eeGeneral.chkSum = sum;
eeDirty(EE_GENERAL); //eeWriteGeneral();
beepKey();
break;
}
break;
}
for(uint8_t i=1; i<4; i++)
{
uint8_t y=i*FH+FH;
lcd_putsnAtt( 0, y,PSTR("SetMid SetSpanDone ")+7*(i-1),7,
sub==i ? INVERS : 0);
}
for(uint8_t i=0; i<7; i++)
{
uint8_t y=i*FH;
lcd_puts_P( 11*FW, y+1*FH, PSTR("< >"));
lcd_outhex4( 8*FW-3, y+1*FH, sub==2 ? loVals[i] : g_eeGeneral.calibSpanNeg[i]);
lcd_outhex4(12*FW, y+1*FH, sub==1 ? anaIn(i) : (sub==2 ? midVals[i] : g_eeGeneral.calibMid[i]));
lcd_outhex4(17*FW, y+1*FH, sub==2 ? hiVals[i] : g_eeGeneral.calibSpanPos[i]);
}
}

2243
src/gruvin9x.cpp Normal file

File diff suppressed because it is too large Load diff

678
src/gruvin9x.h Normal file
View file

@ -0,0 +1,678 @@
/*
* 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 gruvin9x_h
#define gruvin9x_h
#define VERS 1
#define SUB_VERS 2
// #define ASYNC_WRITE
#include <inttypes.h>
#include <string.h>
#if defined(PCBV3)
#include "time.h"
#endif
#ifdef SIMU
#include "simpgmspace.h"
#define APM
#include "stdio.h"
#else
///opt/cross/avr/include/avr/pgmspace.h
#include <stddef.h>
#include <avr/io.h>
#define assert(x)
//disable whole pgmspace functionality for all avr-gcc because
//avr-gcc > 4.2.1 does not work anyway
//http://www.mail-archive.com/gcc-bugs@gcc.gnu.org/msg239240.html
//http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734
//
//Workarounds:
//
//PSTR is fixed below
//all prog_xx definitions must use APM explicitely
//#define __ATTR_PROGMEM__
#include <avr/pgmspace.h>
#ifdef __cplusplus
#define APM __attribute__(( section(".progmem.data") ))
#undef PSTR
#define PSTR(s) (__extension__({static prog_char APM __c[] = (s);&__c[0];}))
#endif
#include <avr/eeprom.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#define F_CPU 16000000UL // 16 MHz
#include <util/delay.h>
#define pgm_read_adr(address_short) pgm_read_word(address_short)
#include <avr/wdt.h>
#endif
#include "file.h"
extern RlcFile theFile; //used for any file operation
//
// elev thr
// LV RV
// 2 ^ 4 ^
// 1 2
// | rudd | aile
// <----X--3-> LH <----X--0-> RH
// 6 | 7 1 | 0
// | |
// 3 v 5 v
//
//PORTA 7 6 5 4 3 2 1 0
// O O O O O O O O
// ------------------------ LCD_DAT -----------------------
//
//PORTB 7 6 5 4 3 2 1 0
// O i i i i i i O
// light KEY_LFT KEY_RGT KEY_UP KEY_DWN KEY_EXT KEY_MEN PPM
//
//PORTC 7 6 5 4 3 2 1 0
// - - O O O O O -
// NC NC LCD_E LCD_RNW LCD_A0 LCD_RES LCD_CS1 NC
//
//PORTD 7 6 5 4 3 2 1 0
// i i i i i i i i
// TRM_D_DWN _UP TRM_C_DWN _UP TRM_B_DWN _UP TRM_A_DWN _UP
//
//PORTE 7 6 5 4 3 2 1 0
// i i i i O i i i
// PPM_IN ID2 Trainer Gear Buzzer ElevDR AileDR THRCT
//
//PORTF 7 6 5 4 3 2 1 0
// ai ai ai ai ai ai ai ai
// ANA_ BAT PITT_TRM HOV_THR HOV_PIT STCK_LH STCK_RV STCK_LV STCK_RH
// rud thro elev aile
//PORTG 7 6 5 4 3 2 1 0
// - - - O i i i
// SIM_CTL ID1 NC RF_POW RuddDR
#define PORTA_LCD_DAT PORTA
#if defined (PCBV3)
#define OUT_C_LIGHT 0
#else
#define OUT_B_LIGHT 7
#endif
#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 OUT_B_PPM 0
#define PORTC_LCD_CTRL PORTC
#define OUT_C_LCD_E 5
#define OUT_C_LCD_RnW 4
#define OUT_C_LCD_A0 3
#define OUT_C_LCD_RES 2
#define OUT_C_LCD_CS1 1
#define INP_D_TRM_LH_UP 7
#define INP_D_TRM_LH_DWN 6
#define INP_D_TRM_RV_DWN 5
#define INP_D_TRM_RV_UP 4
#define INP_D_TRM_LV_DWN 3
#define INP_D_TRM_LV_UP 2
#define INP_D_TRM_RH_DWN 1
#define INP_D_TRM_RH_UP 0
// Legacy support for USART1 hardware mod [DEPRECATED]
#if defined(USART1FREED)
// do not undef the original INP_D_TRM_LV_DWN/UP as we need them later
#define INP_C_TRM_LV_UP 0
#define INP_G_TRM_LV_DWN 2
#endif
#define INP_E_PPM_IN 7
#define INP_E_ID2 6
#define INP_E_Trainer 5
#define INP_E_Gear 4
#define OUT_E_BUZZER 3
#define INP_E_ElevDR 2
#define INP_E_AileDR 1
#define INP_E_ThrCt 0
#if defined(JETI) || defined(FRSKY)
#undef INP_E_ThrCt
#undef INP_E_AileDR
#define INP_C_ThrCt 6
#define INP_C_AileDR 7
#endif
#if defined (BEEPSPKR)
#define BEEP_KEY_TIME 5
#define BEEP_DEFAULT_FREQ 50
#define BEEP_KEY_UP_FREQ 55
#define BEEP_KEY_DOWN_FREQ 45
#endif
#define OUT_G_SIM_CTL 4 //1 : phone-jack=ppm_in
#define INP_G_ID1 3
#define INP_G_RF_POW 1
#define INP_G_RuddDR 0
#define SLAVE_MODE (PING & (1<<INP_G_RF_POW))
const uint8_t modn12x3[4][4]= {
{1, 2, 3, 4},
{1, 3, 2, 4},
{4, 2, 3, 1},
{4, 3, 2, 1} };
//R=1
//E=2
//T=3
//A=4
const uint8_t chout_ar[24][4] = { //First number is 0..23 -> template setup, Second is relevant channel out
{1,2,3,4},{1,2,4,3},{1,3,2,4},{1,3,4,2},{1,4,2,3},{1,4,3,2},
{2,1,3,4},{2,1,4,3},{2,3,1,4},{2,3,4,1},{2,4,1,3},{2,4,3,1},
{3,1,2,4},{3,1,4,2},{3,2,1,4},{3,2,4,1},{3,4,1,2},{3,4,2,1},
{4,1,2,3},{4,1,3,2},{4,2,1,3},{4,2,3,1},{4,3,1,2},{4,3,2,1} };
//convert from mode 1 to mode g_eeGeneral.stickMode
//NOTICE! => 1..4 -> 1..4
#define CONVERT_MODE(x) (((x)<=4) ? modn12x3[g_eeGeneral.stickMode][((x)-1)] : (x))
#define CHANNEL_ORDER(x) (chout_ar[g_eeGeneral.templateSetup][(x)-1])
#define THR_STICK (2-(g_eeGeneral.stickMode&1))
#define ELE_STICK (1+(g_eeGeneral.stickMode&1))
#define AIL_STICK ((g_eeGeneral.stickMode&2) ? 0 : 3)
#define RUD_STICK ((g_eeGeneral.stickMode&2) ? 3 : 0)
enum EnumKeys {
KEY_MENU ,
KEY_EXIT ,
KEY_DOWN ,
KEY_UP ,
KEY_RIGHT ,
KEY_LEFT ,
TRM_LH_DWN ,
TRM_LH_UP ,
TRM_LV_DWN ,
TRM_LV_UP ,
TRM_RV_DWN ,
TRM_RV_UP ,
TRM_RH_DWN ,
TRM_RH_UP ,
//SW_NC ,
//SW_ON ,
SW_ThrCt ,
SW_RuddDR ,
SW_ElevDR ,
SW_ID0 ,
SW_ID1 ,
SW_ID2 ,
SW_AileDR ,
SW_Gear ,
SW_Trainer
};
#define SWITCHES_STR "THR""RUD""ELE""ID0""ID1""ID2""AIL""GEA""TRN""SW1""SW2""SW3""SW4""SW5""SW6""SW7""SW8""SW9""SWA""SWB""SWC"
#define CURV_STR "---x>0x<0|x|f>0f<0|f|c1 c2 c3 c4 c5 c6 c7 c8 c9 c10c11c12c13c14c15c16"
#define CURVE_BASE 7
#define CSWITCH_STR "---- v>ofs v<ofs |v|>ofs|v|<ofsAND OR XOR ""v1==v2 ""v1!=v2 ""v1>v2 ""v1<v2 ""v1>=v2 ""v1<=v2 "
#define CSW_LEN_FUNC 7
#define FSWITCH_STR "---- ""Trainer ""Instant Trim ""Trims2Offsets ""Telemetry View"
#define FSW_LEN_FUNC 14
#define SWASH_TYPE_STR "--- ""120 ""120X ""140 ""90 "
#define SWASH_TYPE_120 1
#define SWASH_TYPE_120X 2
#define SWASH_TYPE_140 3
#define SWASH_TYPE_90 4
#define SWASH_TYPE_NUM 4
#define CS_OFF 0
#define CS_VPOS 1 //v>offset
#define CS_VNEG 2 //v<offset
#define CS_APOS 3 //|v|>offset
#define CS_ANEG 4 //|v|<offset
#define CS_AND 5
#define CS_OR 6
#define CS_XOR 7
#define CS_EQUAL 8
#define CS_NEQUAL 9
#define CS_GREATER 10
#define CS_LESS 11
#define CS_EGREATER 12
#define CS_ELESS 13
#define CS_MAXF 13 //max function
#define CS_VOFS 0
#define CS_VBOOL 1
#define CS_VCOMP 2
#define CS_STATE(x) ((x)<CS_AND ? CS_VOFS : ((x)<CS_EQUAL ? CS_VBOOL : CS_VCOMP))
//#define SW_BASE SW_NC
#define SW_BASE SW_ThrCt
#define SW_BASE_DIAG SW_ThrCt
//#define SWITCHES_STR " NC ON THR RUD ELE ID0 ID1 ID2 AILGEARTRNR"
#define MAX_PSWITCH (SW_Trainer-SW_ThrCt+1) // 9 physical switches
#define MAX_SWITCH (1+MAX_PSWITCH+NUM_CSW) // 22(1+9+12) !switches + 0 + 22 switches: 6 bits needed
#define MAX_DRSWITCH (MAX_PSWITCH+NUM_CSW/2) // 15(9+6) !switches + 0 + 15 switches: 5 bits needed
#define NUM_STICKS 4
#define NUM_POTS 3
#define PPM_BASE (MIX_FULL+3/*CYC1-CYC3*/) // because srcRaw is shifted +1!
#define NUM_CAL_PPM 4
#define NUM_PPM 8
#define CHOUT_BASE (PPM_BASE+NUM_PPM)
#ifdef FRSKY
#define NUM_TELEMETRY 2
#define TELEMETRY_CHANNELS "AD1 AD2 "
#else
#define NUM_TELEMETRY 0
#define TELEMETRY_CHANNELS ""
#endif
#define DSW_THR 1
#define DSW_RUD 2
#define DSW_ELE 3
#define DSW_ID0 4
#define DSW_ID1 5
#define DSW_ID2 6
#define DSW_AIL 7
#define DSW_GEA 8
#define DSW_TRN 9
#define DSW_SW1 10
#define DSW_SW2 11
#define DSW_SW3 12
#define DSW_SW4 13
#define DSW_SW5 14
#define DSW_SW6 15
#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)
//#define EVT_KEY_GEN_BREAK(key) ((key)|0x20)
#define _MSK_KEY_REPT 0x40
#define _MSK_KEY_DBL 0x10
#define IS_KEY_BREAK(key) (((key)&0xf0) == 0x20)
#define EVT_KEY_BREAK(key) ((key)| 0x20)
#define EVT_KEY_FIRST(key) ((key)| _MSK_KEY_REPT|0x20)
#define EVT_KEY_REPT(key) ((key)| _MSK_KEY_REPT )
#define EVT_KEY_LONG(key) ((key)|0x80)
#define EVT_KEY_DBL(key) ((key)|_MSK_KEY_DBL)
//#define EVT_KEY_DBL(key) ((key)|0x10)
#define EVT_ENTRY (0xff - _MSK_KEY_REPT)
#define EVT_ENTRY_UP (0xfe - _MSK_KEY_REPT)
#define EVT_KEY_MASK 0x0f
#define TMRMODE_NONE 0
#define TMRMODE_ABS 1
#define TMRMODE_THR 2
#define TMRMODE_THR_REL 3
#define MAX_ALERT_TIME 60
#define PROTO_PPM 0
#define PROTO_SILV_A 1
#define PROTO_SILV_B 2
#define PROTO_SILV_C 3
#define PROTO_TRACER_CTP1009 4
#define PROT_MAX 4
#define PROT_STR "PPM SILV_ASILV_BSILV_CTRAC09"
#define PROT_STR_LEN 6
typedef void (*getADCp)();
#define ZCHAR_MAX 40
#ifdef TRANSLATIONS
extern int8_t char2idx(char c);
#endif
extern char idx2char(int8_t idx);
/// stoppt alle events von dieser taste bis eine kurze Zeit abgelaufen ist
void pauseEvents(uint8_t enuk);
/// liefert die Zahl der schnellen Wiederholungen dieser Taste
uint8_t getEventDbl(uint8_t event);
/// stoppt alle events von dieser taste bis diese wieder losgelassen wird
void killEvents(uint8_t enuk);
/// liefert den Wert einer beliebigen Taste KEY_MENU..SW_Trainer
bool keyState(EnumKeys enuk);
/// Liefert das naechste Tasten-Event, auch trim-Tasten.
/// Das Ergebnis hat die Form:
/// EVT_KEY_BREAK(key), EVT_KEY_FIRST(key), EVT_KEY_REPT(key) oder EVT_KEY_LONG(key)
uint8_t getEvent();
void putEvent(uint8_t evt);
#if defined (PCBV3)
extern uint8_t keyDown();
#endif
/// Gibt Alarm Maske auf lcd aus.
/// Die Maske wird so lange angezeigt bis eine beliebige Taste gedrueckt wird.
void alert(const prog_char * s, bool defaults=false);
void message(const prog_char * s);
/// periodisches Hauptprogramm
void perMain();
/// Bearbeitet alle zeitkritischen Jobs.
/// wie z.B. einlesen aller Eingaenge, Entprellung, Key-Repeat..
void per10ms();
/// Erzeugt periodisch alle Outputs ausser Bildschirmausgaben.
void zeroVariables();
#define NO_TRAINER 0x01
#define NO_INPUT 0x02
extern void perOut(int16_t *chanOut, uint8_t att);
/// Liefert den Zustand des Switches 'swtch'. Die Numerierung erfolgt ab 1
/// (1=SW_ON, 2=SW_ThrCt, 10=SW_Trainer). 0 Bedeutet not conected.
/// Negative Werte erzeugen invertierte Ergebnisse.
/// Die Funktion putsSwitches(..) erzeugt den passenden Ausdruck.
///
/// \param swtch
/// 0 : not connected. Liefert den Wert 'nc'
/// 1.. MAX_SWITCH : SW_ON .. SW_Trainer
/// -1..-MAX_SWITCH : negierte Werte
/// \param nc Wert, der bei swtch==0 geliefert wird.
bool getSwitch(int8_t swtch, bool nc, uint8_t level=0);
/// Zeigt den Namen des Switches 'swtch' im display an
/// \param x x-koordinate 0..127
/// \param y y-koordinate 0..63 (nur durch 8 teilbar)
/// \param swtch -MAX_SWITCH .. MAX_SWITCH
/// \param att NO_INV,INVERS,BLINK
///
uint8_t getFlightPhase();
uint8_t getTrimFlightPhase(uint8_t idx, int8_t phase=-1);
extern uint16_t s_timeCumTot;
extern uint16_t s_timeCumAbs; //laufzeit in 1/16 sec
extern uint16_t s_timeCumSw; //laufzeit in 1/16 sec
extern uint16_t s_timeCumThr; //gewichtete laufzeit in 1/16 sec
extern uint16_t s_timeCum16ThrP; //gewichtete laufzeit in 1/16 sec
extern uint8_t s_timerState;
extern int16_t s_timerVal[2];
extern uint8_t trimsCheckTimer;
#define TMR_OFF 0
#define TMR_RUNNING 1
#define TMR_BEEPING 2
#define TMR_STOPPED 3
void resetTimer1();
extern uint8_t Timer2_running ;
extern uint16_t timer2 ;
void resetTimer2() ;
extern uint16_t g_tmr1Latency_max;
extern uint16_t g_tmr1Latency_min;
extern uint16_t g_timeMain;
extern uint16_t g_time_per10;
#define MAXTRACE 120
extern uint8_t s_traceBuf[MAXTRACE];
extern uint16_t s_traceWr;
extern uint16_t s_traceCnt;
const prog_char *get_switches_string() ;
uint16_t getTmr16KHz();
unsigned int stack_free();
void checkMem();
void checkTHR();
void checkSwitches();
#define GETADC_SING = 0
#define GETADC_OSMP = 1
#define GETADC_FILT = 2
void getADC_single();
void getADC_osmp();
void getADC_filt();
// checkIncDec flags
#define EE_GENERAL 0x01
#define EE_MODEL 0x02
extern bool warble;
extern uint8_t s_eeDirtyMsk;
#define STORE_MODELVARS eeDirty(EE_MODEL)
#define STORE_GENERALVARS eeDirty(EE_GENERAL)
#if defined (PCBV3)
#define BACKLIGHT_ON PORTC |= (1<<OUT_C_LIGHT)
#define BACKLIGHT_OFF PORTC &= ~(1<<OUT_C_LIGHT)
#else
#define BACKLIGHT_ON PORTB |= (1<<OUT_B_LIGHT)
#define BACKLIGHT_OFF PORTB &= ~(1<<OUT_B_LIGHT)
#endif
#define BITMASK(bit) (1<<(bit))
/// liefert Dimension eines Arrays
#define DIM(arr) (sizeof((arr))/sizeof((arr)[0]))
/// liefert Betrag des Arguments
template<class t> inline t abs(t a){ return a>0?a:-a; }
/// liefert das Minimum der Argumente
template<class t> inline t min(t a, t b){ return a<b?a:b; }
/// liefert das Maximum der Argumente
template<class t> inline t max(t a, t b){ return a>b?a:b; }
template<class t> inline int8_t sgn(t a){ return a>0 ? 1 : (a < 0 ? -1 : 0); }
/// Markiert einen EEPROM-Bereich als dirty. der Bereich wird dann in
/// eeCheck ins EEPROM zurueckgeschrieben.
void eeWriteBlockCmp(const void *i_pointer_ram, void *i_pointer_eeprom, size_t size);
void eeDirty(uint8_t msk);
void eeCheck(bool immediately=false);
//void eeWriteGeneral();
void eeReadAll();
bool eeModelExists(uint8_t id);
uint16_t eeLoadModelName(uint8_t id, char *name);
void eeLoadModel(uint8_t id);
//void eeSaveModel(uint8_t id);
bool eeDuplicateModel(uint8_t id);
///number of real input channels (1-9) plus virtual input channels X1-X4
#define NUM_XCHNRAW (NUM_STICKS+NUM_POTS+2/*MAX/FULL*/+3/*CYC1-CYC3*/+NUM_PPM+NUM_CHNOUT+NUM_TELEMETRY)
///number of real output channels (CH1-CH8) plus virtual output channels X1-X4
#define NUM_XCHNOUT (NUM_CHNOUT) //(NUM_CHNOUT)//+NUM_VIRT)
extern inline int16_t calc100toRESX(int8_t x)
{
// return (int16_t)x*10 + x/4 - x/64;
return ((x*41)>>2) - x/64;
}
extern inline int16_t calc1000toRESX(int16_t x)
{
// return x + x/32 - x/128 + x/512;
int16_t y = x>>5;
x+=y;
y=y>>2;
x-=y;
return x+(y>>2);
}
extern volatile uint16_t g_tmr10ms;
extern inline uint16_t get_tmr10ms()
{
uint16_t time ;
cli();
time = g_tmr10ms ;
sei();
return time ;
}
#define TMR_VAROFS 16
#define SUB_MODE_V 1
#define SUB_MODE_H 2
#define SUB_MODE_H_DBL 3
void setupPulses();
void setupPulsesPPM();
void setupPulsesSilver();
void setupPulsesTracerCtp1009();
void initTemplates();
#include "lcd.h"
extern const char stamp1[];
extern const char stamp2[];
extern const char stamp3[];
extern const char stamp4[];
extern const char stamp5[];
#include "myeeprom.h"
#ifdef JETI
// Jeti-DUPLEX Telemetry
extern uint16_t jeti_keys;
#include "jeti.h"
#endif
#if defined (FRSKY)
// FrSky Telemetry
#include "frsky.h"
#endif
#ifndef BATT_UNSTABLE_BANDGAP
extern uint16_t abRunningAvg;
extern uint8_t g_vbat100mV;
#else
extern uint16_t g_vbat100mV;
#endif
extern volatile uint16_t g_tmr10ms;
extern volatile uint8_t g_blinkTmr10ms;
extern uint8_t g_beepCnt;
extern uint8_t g_beepVal[5];
extern const PROGMEM char modi12x3[];
extern uint16_t pulses2MHz[120];
extern int16_t g_ppmIns[8];
extern int16_t g_chans512[NUM_CHNOUT];
extern volatile uint8_t tick10ms;
extern uint16_t BandGap;
extern uint16_t expou(uint16_t x, uint16_t k);
extern int16_t expo(int16_t x, int16_t k);
extern int16_t intpol(int16_t, uint8_t);
extern int16_t applyCurve(int16_t, uint8_t, uint8_t srcRaw);
extern void applyExpos(int16_t *anas);
extern uint16_t anaIn(uint8_t chan);
extern int16_t calibratedStick[7];
extern int16_t ex_chans[NUM_CHNOUT];
#define FLASH_DURATION 50
extern uint8_t beepAgain;
extern uint16_t g_LightOffCounter;
extern uint8_t mixWarning;
/// Erzeugt einen beep der laenge b
inline void _beep(uint8_t b) {
g_beepCnt=b;
}
extern uint8_t toneFreq;
#if defined (PCBV3)
inline void _beepSpkr(uint8_t d, uint8_t f)
{
g_beepCnt=d;
OCR0A = (5000 / f); // sticking with old values approx 20(abs. min) to 90, 60 being the default tone(?).
}
#elif defined (BEEPSPKR)
inline void _beepSpkr(uint8_t d, uint8_t f)
{
g_beepCnt=d;
toneFreq=f;
}
#endif
#if defined (BEEPSPKR)
#define beepKeySpkr(freq) _beepSpkr(g_beepVal[0],freq)
#define beepTrimSpkr(freq) _beepSpkr(g_beepVal[0],freq)
#define beepWarn1Spkr(freq) _beepSpkr(g_beepVal[1],freq)
#define beepWarn2Spkr(freq) _beepSpkr(g_beepVal[2],freq)
#define beepKey() _beepSpkr(g_beepVal[0],BEEP_DEFAULT_FREQ)
#define beepWarn() _beepSpkr(g_beepVal[3],BEEP_DEFAULT_FREQ)
#define beepWarn1() _beepSpkr(g_beepVal[1],BEEP_DEFAULT_FREQ)
#define beepWarn2() _beepSpkr(g_beepVal[2],BEEP_DEFAULT_FREQ)
#define beepErr() _beepSpkr(g_beepVal[4],BEEP_DEFAULT_FREQ)
#else
/// Erzeugt einen kurzen beep
#define beepKey() _beep(g_beepVal[0])
#define beepWarn() _beep(g_beepVal[3])
#define beepWarn1() _beep(g_beepVal[1])
#define beepWarn2() _beep(g_beepVal[2])
#define beepErr() _beep(g_beepVal[4])
#endif
// MM/SD card Disk IO Support
#if defined (PCBV3)
#include "rtc.h"
extern void disk_timerproc(void);
extern time_t g_unixTime; // global unix timestamp -- hold current time in seconds since 1970-01-01 00:00:00
extern uint8_t g_ms100; // defined in drivers.cpp
#endif
extern PhaseData *phaseaddress(uint8_t idx);
extern ExpoData *expoaddress(uint8_t idx);
extern MixData *mixaddress(uint8_t idx);
extern LimitData *limitaddress(uint8_t idx);
extern void incSubtrim(uint8_t idx, int16_t inc);
extern void instantTrim();
extern void moveTrimsToOffsets(); // move state of 3 primary trims to offsets
extern uint16_t active_functions;
inline bool isFunctionActive(uint8_t func)
{
return active_functions & (1 << (func-1));
}
#endif // gruvin9x_h
/*eof*/

218
src/gtests.cpp Normal file
View file

@ -0,0 +1,218 @@
/*
* 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.
*
*/
#include <gtest/gtest.h>
#include "gruvin9x.h"
uint16_t anaIn(uint8_t chan)
{
return 0;
}
TEST(incSubtrim, test1) {
memset(&g_model, 0, sizeof(g_model));
incSubtrim(0, 10);
for (int p=0; p<MAX_PHASES; p++) {
PhaseData *phase = phaseaddress(p);
EXPECT_EQ(phase->trim[0], -10) << "Should be equal to -10";
}
EXPECT_EQ(g_model.subtrim[0], 10) << "Should be equal to 10";
}
TEST(incSubtrim, test2) {
memset(&g_model, 0, sizeof(g_model));
phaseaddress(0)->trim[0] = -120;
phaseaddress(1)->trim[0] = +125;
incSubtrim(0, +10); // FP1 is trimmed by +10
EXPECT_EQ(phaseaddress(0)->trim[0], TRIM_MIN);
EXPECT_EQ(phaseaddress(1)->trim[0], +115); // and set to 125 just after
EXPECT_EQ(g_model.subtrim[0], 10);
}
TEST(trims, greaterTrimLink)
{
memset(&g_model, 0, sizeof(g_model));
phaseaddress(1)->trim[0] = -127; // link to FP3 trim
phaseaddress(3)->trim[0] = 32;
EXPECT_EQ(phaseaddress(getTrimFlightPhase(0, 1))->trim[0], 32);
}
TEST(trims, chainedTrims)
{
memset(&g_model, 0, sizeof(g_model));
phaseaddress(0)->trim[0] = 32;
phaseaddress(1)->trim[0] = +127; // link to FP0 trim
phaseaddress(2)->trim[0] = -128; // link to FP1 trim
EXPECT_EQ(phaseaddress(getTrimFlightPhase(0, 2))->trim[0], 32);
}
TEST(trims, infiniteChainedTrims)
{
memset(&g_model, 0, sizeof(g_model));
phaseaddress(0)->trim[0] = 32;
phaseaddress(1)->trim[0] = -127; // link to FP3 trim
phaseaddress(2)->trim[0] = -128; // link to FP1 trim
phaseaddress(3)->trim[0] = -127; // link to FP2 trim
EXPECT_EQ(phaseaddress(getTrimFlightPhase(0, 2))->trim[0], 32);
}
TEST(outdezNAtt, test_unsigned) {
uint16_t altitude = 65530;
uint8_t refBuf[sizeof(displayBuf)];
memset(displayBuf, 0, sizeof(displayBuf));
lcd_putc(0*FWNUM, 0, '6');
lcd_putc(1*FWNUM, 0, '5');
lcd_putc(2*FWNUM, 0, '5');
lcd_putc(3*FWNUM, 0, '3');
lcd_putc(4*FWNUM, 0, '0');
memcpy(refBuf, displayBuf, sizeof(displayBuf));
memset(displayBuf, 0, sizeof(displayBuf));
lcd_outdezNAtt(1, 0, altitude, LEFT|UNSIGN);
EXPECT_EQ(memcmp(refBuf, displayBuf, sizeof(displayBuf)), 0) << "Unsigned numbers will be bad displayed";
}
TEST(EEPROM, test1) {
eepromFile = NULL; // in memory
RlcFile f;
uint8_t buf[1000];
uint8_t buf2[1000];
EeFsFormat();
for(int i=0; i<10000; i++) {
int size = rand()%800;
for(int j=0; j<size; j++) {
buf[j] = rand() < (RAND_MAX/10000*i) ? 0 : (j&0xff);
}
f.writeRlc(5, 5, buf, size, 100);
// printf("size=%4d red=%4d\n\n\n", size, f.size());
f.openRd(5);
uint16_t n = f.readRlc(buf2,size+1);
EXPECT_EQ(n, size);
EXPECT_EQ(memcmp(buf, buf2, size), 0);
}
}
TEST(EEPROM, test2) {
eepromFile = NULL; // in memory
RlcFile f;
uint8_t buf[1000];
EeFsFormat();
for(int i=0; i<1000; i++) buf[i]='6'+i%4;
f.writeRlc(6, 6, buf, 300, 100);
f.openRd(6);
uint16_t sz=0;
for(int i=0; i<500; i++){
uint8_t b;
uint16_t n=f.readRlc(&b,1);
if(n) EXPECT_EQ(b, ('6'+sz%4));
sz+=n;
}
EXPECT_EQ(sz, 300);
}
TEST(EEPROM, eeCheckImmediately) {
eepromFile = NULL; // in memory
// RlcFile f;
uint8_t buf[1000];
EeFsFormat();
for(int i=0; i<1000; i++) buf[i]='6'+i%4;
theFile.writeRlc(6, 6, buf, 300, false);
eeCheck(true);
theFile.openRd(6);
uint16_t sz=0;
for(int i=0; i<500; i++){
uint8_t b;
uint16_t n=theFile.readRlc(&b,1);
if(n) EXPECT_EQ(b, ('6'+sz%4));
sz+=n;
}
EXPECT_EQ(sz, 300);
}
TEST(EEPROM, eeDuplicateModel) {
eepromFile = NULL; // in memory
uint8_t buf[1000];
EeFsFormat();
for(int i=0; i<1000; i++) buf[i]='6'+i%4;
theFile.writeRlc(5, 6, buf, 300, true);
eeDuplicateModel(4);
theFile.openRd(6);
uint16_t sz=0;
for(int i=0; i<500; i++){
uint8_t b;
uint16_t n=theFile.readRlc(&b,1);
if(n) EXPECT_EQ(b, ('6'+sz%4));
sz+=n;
}
EXPECT_EQ(sz, 300);
}
TEST(EEPROM, rm) {
eepromFile = NULL; // in memory
uint8_t buf[1000];
EeFsFormat();
for(int i=0; i<1000; i++) buf[i]='6'+i%4;
theFile.writeRlc(5, 6, buf, 300, true);
EXPECT_EQ(EFile::exists(5), true);
EFile::rm(5);
EXPECT_EQ(EFile::exists(5), false);
theFile.openRd(5);
uint16_t sz=0;
for(int i=0; i<500; i++){
uint8_t b;
uint16_t n=theFile.readRlc(&b,1);
if(n) EXPECT_EQ(b, ('6'+sz%4));
sz+=n;
}
EXPECT_EQ(sz, 0);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

37
src/integer.h Normal file
View file

@ -0,0 +1,37 @@
/*-------------------------------------------*/
/* Integer type definitions for FatFs module */
/*-------------------------------------------*/
#ifndef _INTEGER
#define _INTEGER
#ifdef _WIN32 /* FatFs development platform */
#include <windows.h>
#include <tchar.h>
#else /* Embedded platform */
/* These types must be 16-bit, 32-bit or larger integer */
typedef int INT;
typedef unsigned int UINT;
/* These types must be 8-bit integer */
typedef char CHAR;
typedef unsigned char UCHAR;
typedef unsigned char BYTE;
/* These types must be 16-bit integer */
typedef short SHORT;
typedef unsigned short USHORT;
typedef unsigned short WORD;
typedef unsigned short WCHAR;
/* These types must be 32-bit integer */
typedef long LONG;
typedef unsigned long ULONG;
typedef unsigned long DWORD;
#endif
#endif

200
src/jeti.cpp Normal file
View file

@ -0,0 +1,200 @@
/*
* gruvin9x Author Bryan J.Rentoul (Gruvin) <gruvin@gmail.com>
*
* jeti.cpp original author - Karl Szmutny <shadow@privy.de>
*
* Based on the Code from Peter "woggle" Mack, mac@denich.net
* -> http://svn.mikrokopter.de/filedetails.php?repname=Projects&path=/Transportables_Koptertool/trunk/jeti.c
*
* 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 "jeti.h"
#include "gruvin9x.h"
uint16_t jeti_keys = JETI_KEY_NOCHANGE;
uint8_t JetiBuffer[32]; // 32 characters
uint8_t JetiBufferReady;
ISR (USART0_RX_vect)
{
uint8_t stat;
uint8_t rh;
uint8_t rl;
static uint8_t jbp;
stat = UCSR0A;
rh = UCSR0B;
rl = UDR0;
if (stat & ((1 << FE0) | (1 << DOR0) | (1 << UPE0)))
{ // discard buffer and start new on any error
JetiBufferReady = 0;
jbp = 0;
}
else if ((rh & (1 << RXB80)) == 0)
{ // control
if (rl == 0xfe)
{ // start condition
JetiBufferReady = 0;
jbp = 0;
}
else if (rl == 0xff)
{ // stop condition
JetiBufferReady = 1;
}
}
else
{ // data
if (jbp < 32)
{
JetiBuffer[jbp++] = rl;
}
}
}
void JETI_Init (void)
{
jeti_keys = JETI_KEY_NOCHANGE;
DDRE &= ~(1 << DDE0); // set RXD0 pin as input
PORTE &= ~(1 << PORTE0); // disable pullup on RXD0 pin
#undef BAUD
#define BAUD 9600
#include <util/setbaud.h>
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
UCSR0A &= ~(1 << U2X0); // disable double speed operation
// set 9O1
UCSR0C = (1 << UPM01) | (1 << UPM00) | (1 << UCSZ01) | (1 << UCSZ00);
UCSR0B = (1 << UCSZ02);
// flush receive buffer
while ( UCSR0A & (1 << RXC0) ) UDR0;
}
void JETI_DisableTXD (void)
{
UCSR0B &= ~(1 << TXEN0); // disable TX
}
void JETI_EnableTXD (void)
{
UCSR0B |= (1 << TXEN0); // enable TX
}
void JETI_DisableRXD (void)
{
UCSR0B &= ~(1 << RXEN0); // disable RX
UCSR0B &= ~(1 << RXCIE0); // disable Interrupt
}
void JETI_EnableRXD (void)
{
UCSR0B |= (1 << RXEN0); // enable RX
UCSR0B |= (1 << RXCIE0); // enable Interrupt
}
void JETI_putw (uint16_t c)
{
loop_until_bit_is_set(UCSR0A, UDRE0);
UCSR0B &= ~(1 << TXB80);
if (c & 0x0100)
{
UCSR0B |= (1 << TXB80);
}
UDR0 = c;
}
void JETI_putc (uint8_t c)
{
loop_until_bit_is_set(UCSR0A, UDRE0);
// UCSRB &= ~(1 << TXB8);
UCSR0B |= (1 << TXB80);
UDR0 = c;
}
void JETI_puts (char *s)
{
while (*s)
{
JETI_putc (*s);
s++;
}
}
void JETI_put_start (void)
{
loop_until_bit_is_set(UCSR0A, UDRE0);
UCSR0B &= ~(1 << TXB80);
UDR0 = 0xFE;
}
void JETI_put_stop (void)
{
loop_until_bit_is_set(UCSR0A, UDRE0);
UCSR0B &= ~(1 << TXB80);
UDR0 = 0xFF;
}
#include "menus.h"
void menuProcJeti(uint8_t event)
{
TITLE("JETI");
switch(event)
{
//case EVT_KEY_FIRST(KEY_MENU):
// break;
case EVT_KEY_FIRST(KEY_EXIT):
JETI_DisableRXD();
chainMenu(menuProc0);
break;
}
for (uint8_t i = 0; i < 16; i++)
{
lcd_putcAtt((i+2)*FW, 3*FH, JetiBuffer[i], BSS);
lcd_putcAtt((i+2)*FW, 4*FH, JetiBuffer[i+16], BSS);
}
if (JetiBufferReady)
{
JETI_EnableTXD();
if (keyState((EnumKeys)(KEY_UP))) jeti_keys &= JETI_KEY_UP;
if (keyState((EnumKeys)(KEY_DOWN))) jeti_keys &= JETI_KEY_DOWN;
if (keyState((EnumKeys)(KEY_LEFT))) jeti_keys &= JETI_KEY_LEFT;
if (keyState((EnumKeys)(KEY_RIGHT))) jeti_keys &= JETI_KEY_RIGHT;
JetiBufferReady = 0; // invalidate buffer
JETI_putw((uint16_t) jeti_keys);
_delay_ms (1);
JETI_DisableTXD();
jeti_keys = JETI_KEY_NOCHANGE;
}
}

49
src/jeti.h Normal file
View file

@ -0,0 +1,49 @@
/*
* Author - Karl Szmutny <shadow@privy.de>
*
* Based on the Code from Peter "woggle" Mack, mac@denich.net
* -> http://svn.mikrokopter.de/filedetails.php?repname=Projects&path=/Transportables_Koptertool/trunk/jeti.c
*
* 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 jeti_h
#define jeti_h
#include "gruvin9x.h"
#define JETI_KEY_LEFT 0x70
#define JETI_KEY_RIGHT 0xe0
#define JETI_KEY_UP 0xd0
#define JETI_KEY_DOWN 0xb0
#define JETI_KEY_NOCHANGE 0xf0
extern uint16_t jeti_keys;
extern uint8_t JetiBuffer[32]; // 32 characters
extern uint8_t JetiBufferReady;
void JETI_Init(void);
void JETI_DisableTXD (void);
void JETI_EnableTXD (void);
void JETI_DisableRXD (void);
void JETI_EnableRXD (void);
void JETI_putw (uint16_t c);
void JETI_putc (uint8_t c);
void JETI_puts (char *s);
void JETI_put_start (void);
void JETI_put_stop (void);
void menuProcJeti(uint8_t event);
#endif

670
src/lcd.cpp Normal file
View file

@ -0,0 +1,670 @@
/*
* 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.
*
*/
#include "lcd.h"
uint8_t displayBuf[DISPLAY_W*DISPLAY_H/8];
#define DISPLAY_END (displayBuf+sizeof(displayBuf))
#include "font.lbm"
#define font_5x8_x20_x7f (font+3)
#include "font_dblsize.lbm"
#define font_10x16_x20_x7f (font_dblsize+3)
void lcd_clear()
{
memset(displayBuf, 0, sizeof(displayBuf));
}
void lcd_img(uint8_t i_x,uint8_t i_y,const prog_uchar * imgdat,uint8_t idx,uint8_t mode)
{
const prog_uchar *q = imgdat;
uint8_t w = pgm_read_byte(q++);
uint8_t hb = (pgm_read_byte(q++)+7)/8;
uint8_t sze1 = pgm_read_byte(q++);
q += idx*sze1;
bool inv = (mode & INVERS) ? true : (mode & BLINK ? BLINK_ON_PHASE : false);
for(uint8_t yb = 0; yb < hb; yb++){
uint8_t *p = &displayBuf[ (i_y / 8 + yb) * DISPLAY_W + i_x ];
for(uint8_t x=0; x < w; x++){
uint8_t b = pgm_read_byte(q++);
*p++ = inv ? ~b : b;
}
}
}
uint8_t lcd_lastPos;
void lcd_putcAtt(uint8_t x, uint8_t y, const char c, uint8_t mode)
{
uint8_t *p = &displayBuf[ y / 8 * DISPLAY_W + x ];
prog_uchar *q = &font_5x8_x20_x7f[ + (c-0x20)*5];
bool inv = (mode & INVERS) ? true : (mode & BLINK ? BLINK_ON_PHASE : false);
if(mode & DBLSIZE)
{
/* each letter consists of ten top bytes followed by
* by ten bottom bytes (20 bytes per * char) */
q = &font_10x16_x20_x7f[(c-0x20)*10 + ((c-0x20)/16)*160];
for(char i=5; i>=0; i--) {
if (mode & CONDENSED && i==0) break;
/*top byte*/
uint8_t b1 = i>0 ? pgm_read_byte(q) : 0;
/*bottom byte*/
uint8_t b3 = i>0 ? pgm_read_byte(160+q) : 0;
/*top byte*/
uint8_t b2 = i>0 ? pgm_read_byte(++q) : 0;
/*bottom byte*/
uint8_t b4 = i>0 ? pgm_read_byte(160+q) : 0;
if(inv) {
b1=~b1;
b2=~b2;
b3=~b3;
b4=~b4;
}
if(&p[DISPLAY_W+1] < DISPLAY_END){
p[0]=b1;
p[1]=b2;
p[DISPLAY_W] = b3;
p[DISPLAY_W+1] = b4;
p+=2;
}
q++;
}
#if OUTDEZ_SPEED != 0
lcd_lastPos = x + 2*FW;
#endif
}
else {
uint8_t condense=0;
if (mode & CONDENSED) {
*p++ = inv ? ~0 : 0;
condense=1;
}
for (char i=5; i!=0; i--) {
uint8_t b = pgm_read_byte(q++);
if (condense && i==4) {
/*condense the letter by skipping column 4 */
continue;
}
if(p<DISPLAY_END) *p++ = inv ? ~b : b;
}
if(p<DISPLAY_END) *p++ = inv ? ~0 : 0;
#if OUTDEZ_SPEED != 0
lcd_lastPos = x + FW;
#endif
}
}
void lcd_putc(uint8_t x,uint8_t y,const char c)
{
lcd_putcAtt(x,y,c,0);
}
void lcd_putsnAtt(uint8_t x,uint8_t y,const prog_char * s,uint8_t len,uint8_t mode)
{
while(len!=0) {
char c;
switch (mode & (BSS+ZCHAR)) {
case BSS:
c = *s;
break;
case ZCHAR:
c = idx2char(*s);
break;
default:
c = pgm_read_byte(s);
break;
}
lcd_putcAtt(x,y,c,mode);
x+=FW;
if (mode&DBLSIZE) x+=FW-1;
s++;
len--;
}
}
void lcd_putsn_P(uint8_t x,uint8_t y,const prog_char * s,uint8_t len)
{
lcd_putsnAtt( x,y,s,len,0);
}
void lcd_putsAtt(uint8_t x,uint8_t y,const prog_char * s,uint8_t mode)
{
while(1) {
char c = (mode & BSS) ? *s++ : pgm_read_byte(s++);
if(!c) break;
lcd_putcAtt(x,y,c,mode);
x+=FW;
if(mode&DBLSIZE) x+=FW;
}
#if OUTDEZ_SPEED == 0
lcd_lastPos = x;
#endif
}
void lcd_puts_P(uint8_t x,uint8_t y,const prog_char * s)
{
lcd_putsAtt( x, y, s, 0);
}
void lcd_outhex4(uint8_t x,uint8_t y,uint16_t val)
{
x+=FWNUM*4;
for(int i=0; i<4; i++)
{
x-=FWNUM;
char c = val & 0xf;
c = c>9 ? c+'A'-10 : c+'0';
lcd_putcAtt(x,y,c,c>='A'?CONDENSED:0);
val>>=4;
}
}
void lcd_outdez8(uint8_t x, uint8_t y, int8_t val)
{
lcd_outdezAtt(x, y, val);
}
void lcd_outdezAtt(uint8_t x, uint8_t y, int16_t val, uint8_t mode)
{
lcd_outdezNAtt(x, y, val, mode);
}
// TODO use doxygen style comments here
/*
USAGE:
lcd_outdezNAtt(x-coord, y-coord, (un)signed-value{0..65535|0..+/-32768},
mode_flags, length)
Available mode_flas: PREC{1..3}, UNSIGN (for programmer selected signed numbers
to allow for unsigned values up to the ful 65535 16-bit limt))
LEADING0 means pad 0 to the left of sig. digits up to 'len' total characters
*/
#if OUTDEZ_SPEED != 0
void lcd_outdezNAtt(uint8_t x, uint8_t y, int16_t val, uint8_t flags, uint8_t len)
{
assert(len <= 5);
char digits[5]; // sig. digits buffered in reverse order
int8_t lastDigit = 4;
int8_t mode = MODE(flags);
bool neg = false;
if (flags & UNSIGN) { flags -= UNSIGN; }
else if (val < 0) { neg=true; val=-val; }
bool dblsize = (flags & DBLSIZE);
// Buffer characters and determine the significant digit count
for (int8_t i=4; i>=0; i--)
{
if (val) lastDigit = i;
digits[i] = ((uint16_t)val % 10) + '0';
val = (uint16_t)val / 10;
}
switch(mode)
{
case MODE(LEADING0):
lastDigit = 5-len;
break;
default:
if (4-lastDigit < mode)
lastDigit = 4 - mode;
break;
}
lcd_lastPos = x;
if (~flags & LEFT) // determine correct x-coord starting point for decimal aligned
{
// Starting point for regular unsigned, non-decimal number
lcd_lastPos -= (5-lastDigit) * (dblsize ? 2*FWNUM : FWNUM) + (dblsize ? 1 : 0);
if (mode>0 && !dblsize) lcd_lastPos -= dblsize ? 3 : 2;
if (neg) lcd_lastPos -= dblsize ? 2*FW : FW;
}
if (neg) lcd_putcAtt(lcd_lastPos, y, '-', flags); // apply sign when required
uint8_t xn = 0;
uint8_t ln = 2;
for (int8_t i=lastDigit; i<5; i++)
{
lcd_putcAtt(lcd_lastPos-1, y, digits[i], flags);
if (dblsize) {
lcd_lastPos--;
}
// Draw decimal point
// Use direct screen writes to save flash, function calls, stack, cpu load
#if OUTDEZ_SPEED == 2
bool inv = (flags & INVERS) ? true : (flags & BLINK ? BLINK_ON_PHASE : false);
#endif
if (mode>0 && 4-i==mode) // .. then draw a d'point
{
if (dblsize)
{
xn = lcd_lastPos-1;
if (digits[i+1]=='2' || digits[i+1]=='3' || digits[i+1]=='1') ln++;
if (digits[i]=='2' || digits[i]=='4') {
if (digits[i+1]=='4') xn++;
else { xn--; ln++; }
}
}
else
{
#if OUTDEZ_SPEED == 2
displayBuf[ y * (DISPLAY_W/8) + lcd_lastPos ] = (inv ? 0x40 ^ 0xff : 0x40);
#else
lcd_plot(lcd_lastPos, y+6);
if (flags & INVERS || (flags & BLINK && BLINK_ON_PHASE))
lcd_vline(lcd_lastPos, y, 8);
#endif
lcd_lastPos += 2;
}
}
}
if (xn) {
lcd_hline(xn, y+2*FH-3, ln);
lcd_hline(xn, y+2*FH-2, ln);
}
}
#else
void lcd_outdezNAtt(uint8_t x, uint8_t y, int16_t val, uint8_t flags, uint8_t len)
{
uint8_t fw = FWNUM;
int8_t mode = MODE(flags);
bool neg = false;
if (flags & UNSIGN) { flags -= UNSIGN; }
else if (val < 0) { neg=true; val=-val; }
uint8_t xn = 0;
uint8_t ln = 2;
char c;
if (mode != MODE(LEADING0)) {
len = 1;
uint16_t tmp = ((uint16_t)val) / 10;
while (tmp) {
len++;
tmp /= 10;
}
if (len <= mode)
len = mode + 1;
}
if (flags & DBLSIZE) {
fw += FWNUM;
}
else {
if (flags & LEFT) {
if (mode > 0)
x += 2;
}
}
if (flags & LEFT) {
x += len * fw;
if (neg)
x += FWNUM;
}
lcd_lastPos = x;
x -= fw + 1;
for (uint8_t i=1; i<=len; i++) {
c = ((uint16_t)val % 10) + '0';
if (c=='1' && flags&DBLSIZE && i==len) { x+=2; flags|=CONDENSED; }
lcd_putcAtt(x, y, c, flags);
if (mode==i) {
flags &= ~PREC2; // TODO not needed but removes 64bytes, could be improved for sure, check asm
if (flags & DBLSIZE) {
xn = x;
if(c=='2' || c=='3' || c=='1') ln++;
uint8_t tn = ((uint16_t)val/10) % 10;
if (tn==2 || tn==4) {
if (c=='4') { xn++; }
else { xn--; ln++; }
}
}
else {
x -= 2;
lcd_plot(x+1, y+6);
if (flags & INVERS || (flags & BLINK && BLINK_ON_PHASE))
lcd_vline(x+1, y, 8);
}
}
val = ((uint16_t)val) / 10;
x-=fw;
}
if (xn) {
lcd_hline(xn, y+2*FH-3, ln);
lcd_hline(xn, y+2*FH-2, ln);
}
// TODO we could change the '-' to have one pixel removed at its left
if (neg) { lcd_putcAtt(x, y, '-', flags); lcd_plot(x, y+3); }
}
#endif
void lcd_mask(uint8_t *p, uint8_t mask, uint8_t att)
{
assert(p < DISPLAY_END);
if (att & BLACK)
*p |= mask;
else if (att & WHITE)
*p &= ~mask;
else
*p ^= mask;
}
void lcd_plot(uint8_t x,uint8_t y, uint8_t att)
{
uint8_t *p = &displayBuf[ y / 8 * DISPLAY_W + x ];
if (p<DISPLAY_END)
lcd_mask(p, BITMASK(y%8), att);
}
void lcd_hlineStip(int8_t x, uint8_t y, uint8_t w, uint8_t pat, uint8_t att)
{
if (y >= DISPLAY_H) return;
if (x<0) { w+=x; x=0; }
if (x+w > DISPLAY_W) { w = DISPLAY_W - x; }
uint8_t *p = &displayBuf[ y / 8 * DISPLAY_W + x ];
uint8_t msk = BITMASK(y%8);
while(w) {
if(pat&1) {
lcd_mask(p, msk, att);
pat = (pat >> 1) | 0x80;
}
else {
pat = pat >> 1;
}
w--;
p++;
}
}
void lcd_hline(uint8_t x,uint8_t y, uint8_t w, uint8_t att)
{
lcd_hlineStip(x, y, w, 0xff, att);
}
void lcd_vlineStip(uint8_t x, int8_t y, int8_t h, uint8_t pat)
{
if (y<0) { h+=y; y=0; }
if (y+h > DISPLAY_H) { h = DISPLAY_H - y; }
uint8_t *p = &displayBuf[ y / 8 * DISPLAY_W + x ];
y = y % 8;
if (y) {
assert(p < DISPLAY_END);
*p ^= ~(BITMASK(y)-1) & pat;
p += DISPLAY_W;
h -= 8-y;
}
while (h>0) {
assert(p < DISPLAY_END);
*p ^= pat;
p += DISPLAY_W;
h -= 8;
}
h = (h+8) % 8;
if (h) {
p -= DISPLAY_W;
assert(p < DISPLAY_END);
*p ^= ~(BITMASK(h)-1) & pat;
}
}
void lcd_vline(uint8_t x, int8_t y, int8_t h)
{
lcd_vlineStip(x, y, h, 0xff);
}
void lcd_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t pat, uint8_t att)
{
if (!((att & BLINK) && BLINK_ON_PHASE)) {
lcd_vlineStip(x, y, h, pat);
lcd_hlineStip(x, y+h-1, w, pat);
lcd_vlineStip(x+w-1, y, h, pat);
lcd_hlineStip(x, y, w, pat);
}
}
void lcd_filled_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t att)
{
for (uint8_t i=y; i<y+h; i++)
lcd_hline(x, i, w, att);
}
void putsTime(uint8_t x,uint8_t y,int16_t tme,uint8_t att,uint8_t att2)
{
if (tme<0) {
lcd_putcAtt(x - ((att & DBLSIZE) ? FW+1 : FWNUM), y, '-', att);
tme = -tme;
}
lcd_outdezNAtt(x, y, tme/60, att|LEADING0|LEFT, 2);
lcd_putcAtt(lcd_lastPos-((att & DBLSIZE) ? 1 : 0), y, ':', att&att2);
#if OUTDEZ_SPEED != 0
lcd_outdezNAtt(lcd_lastPos-((att & DBLSIZE) ? 5 : 0), y, tme%60, att2|LEADING0|LEFT, 2);
#else
lcd_outdezNAtt(lcd_lastPos+FW, y, tme%60, att2|LEADING0|LEFT, 2);
#endif
}
void putsVolts(uint8_t x, uint8_t y, uint16_t volts, uint8_t att)
{
// 215.94us vs 257.31us
lcd_outdezAtt(x, y, (int16_t)volts, att|PREC1|UNSIGN);
if (~att & NO_UNIT) lcd_putcAtt(lcd_lastPos, y, 'v', att);
}
void putsVBat(uint8_t x, uint8_t y, uint8_t att)
{
putsVolts(x, y, g_vbat100mV, att);
}
void putsChnRaw(uint8_t x, uint8_t y, uint8_t idx, uint8_t att)
{
if (idx==0)
lcd_putsnAtt(x,y,PSTR("----"),4,att);
else if(idx<=4)
lcd_putsnAtt(x,y,modi12x3+g_eeGeneral.stickMode*16+4*(idx-1),4,att);
else if(idx<=NUM_XCHNRAW)
lcd_putsnAtt(x,y,PSTR("P1 P2 P3 MAX FULLCYC1CYC2CYC3PPM1PPM2PPM3PPM4PPM5PPM6PPM7PPM8CH1 CH2 CH3 CH4 CH5 CH6 CH7 CH8 CH9 CH10CH11CH12CH13CH14CH15CH16"TELEMETRY_CHANNELS)+4*(idx-5),4,att);
}
void putsChn(uint8_t x, uint8_t y, uint8_t idx, uint8_t att)
{
if (idx > 0 && idx <= NUM_CHNOUT)
putsChnRaw(x, y, idx+20, att);
}
void putsChnLetter(uint8_t x, uint8_t y, uint8_t idx, uint8_t attr)
{
lcd_putsnAtt(x, y, PSTR("RETA")+CHANNEL_ORDER(idx)-1, 1, attr);
}
void putsModelName(uint8_t x, uint8_t y, char *name, uint8_t id, uint8_t att)
{
uint8_t len = sizeof(g_model.name);
while (len>0 && !name[len-1]) --len;
if (len==0) {
lcd_putsAtt(x, y, PSTR("MODEL"/*MODEL*/), att);
lcd_outdezNAtt(lcd_lastPos, y, id+1, att|LEADING0|LEFT, 2);
}
else {
lcd_putsnAtt(x, y, name, sizeof(g_model.name), ZCHAR|att);
}
}
void putsSwitches(uint8_t x,uint8_t y,int8_t idx,uint8_t att)
{
switch(idx){
case 0: lcd_putsAtt(x,y,PSTR("---"),att);return;
case MAX_SWITCH: lcd_putsAtt(x,y,PSTR("ON "),att);return;
case -MAX_SWITCH: lcd_putsAtt(x,y,PSTR("OFF"),att);return;
}
if (idx<0) lcd_putcAtt(x-FW, y, '!', att);
lcd_putsnAtt(x,y,get_switches_string()+3*(abs(idx)-1),3,att);
}
void putsFlightPhase(uint8_t x, uint8_t y, int8_t idx, uint8_t att)
{
if (idx==0) { lcd_putsAtt(x,y,PSTR("---"),att); return; }
if (idx < 0) { lcd_putcAtt(x-FW, y, '!', att); idx = -idx; }
lcd_putsAtt(x, y, PSTR("FP"), att);
lcd_putcAtt(x+2*FW, y, '0'+idx-1, att);
}
void putsTmrMode(uint8_t x, uint8_t y, int8_t mode, uint8_t att)
{
if (mode < 0) {
mode = -mode;
lcd_putcAtt(x-1*FW, y, '!', att);
}
if (mode < TMR_VAROFS) {
lcd_putsnAtt(x, y, PSTR("OFFABSRUsRU%ELsEL%THsTH%ALsAL%P1 P1%P2 P2%P3 P3%")+3*mode, 3, att);
return;
}
if (mode < TMR_VAROFS+MAX_SWITCH-1) { // normal on-off
putsSwitches(x, y, mode-(TMR_VAROFS-1), att);
return;
}
putsSwitches(x, y, mode-(TMR_VAROFS+MAX_SWITCH-1-1), att); // momentary on-off
if (~att & SHRT_TM_MODE) lcd_putcAtt(x+3*FW, y, 'm', att);
}
#ifdef FRSKY
// TODO move this into frsky.cpp
void putsTelemetry(uint8_t x, uint8_t y, uint8_t val, uint8_t unit, uint8_t att)
{
if (unit == 0/*v*/) {
putsVolts(x, y, val, att);
}
else /* raw or reserved unit */ {
lcd_outdezAtt(x, y, val, att);
}
}
#endif
void lcdSendCtl(uint8_t val)
{
PORTC_LCD_CTRL &= ~(1<<OUT_C_LCD_CS1);
#ifdef LCD_MULTIPLEX
DDRA = 0xFF; // set LCD_DAT pins to output
#endif
PORTC_LCD_CTRL &= ~(1<<OUT_C_LCD_A0);
PORTC_LCD_CTRL &= ~(1<<OUT_C_LCD_RnW);
PORTA_LCD_DAT = val;
PORTC_LCD_CTRL |= (1<<OUT_C_LCD_E);
PORTC_LCD_CTRL &= ~(1<<OUT_C_LCD_E);
PORTC_LCD_CTRL |= (1<<OUT_C_LCD_A0);
#ifdef LCD_MULTIPLEX
DDRA = 0x00; // set LCD_DAT pins to input
#endif
PORTC_LCD_CTRL |= (1<<OUT_C_LCD_CS1);
}
#define delay_1us() _delay_us(1)
void delay_1_5us(int ms)
{
for(int i=0; i<ms; i++) delay_1us();
}
void lcd_init()
{
// /home/thus/txt/datasheets/lcd/KS0713.pdf
// ~/txt/flieger/ST7565RV17.pdf from http://www.glyn.de/content.asp?wdid=132&sid=
PORTC_LCD_CTRL &= ~(1<<OUT_C_LCD_RES); //LCD_RES
delay_1us();
delay_1us();// f520 call 0xf4ce delay_1us() ; 0x0xf4ce
PORTC_LCD_CTRL |= (1<<OUT_C_LCD_RES); // f524 sbi 0x15, 2 IOADR-PORTC_LCD_CTRL; 21 1
delay_1_5us(1500);
lcdSendCtl(0xe2); //Initialize the internal functions
lcdSendCtl(0xae); //DON = 0: display OFF
lcdSendCtl(0xa1); //ADC = 1: reverse direction(SEG132->SEG1)
lcdSendCtl(0xA6); //REV = 0: non-reverse display
lcdSendCtl(0xA4); //EON = 0: normal display. non-entire
lcdSendCtl(0xA2); // Select LCD bias=0
lcdSendCtl(0xC0); //SHL = 0: normal direction (COM1->COM64)
lcdSendCtl(0x2F); //Control power circuit operation VC=VR=VF=1
lcdSendCtl(0x25); //Select int resistance ratio R2 R1 R0 =5
lcdSendCtl(0x81); //Set reference voltage Mode
lcdSendCtl(0x22); // 24 SV5 SV4 SV3 SV2 SV1 SV0 = 0x18
lcdSendCtl(0xAF); //DON = 1: display ON
g_eeGeneral.contrast = 0x22;
}
void lcdSetRefVolt(uint8_t val)
{
lcdSendCtl(0x81);
lcdSendCtl(val);
}
void refreshDiplay()
{
uint8_t *p=displayBuf;
for(uint8_t y=0; y < 8; y++) {
lcdSendCtl(0x04);
lcdSendCtl(0x10); //column addr 0
lcdSendCtl( y | 0xB0); //page addr y
PORTC_LCD_CTRL &= ~(1<<OUT_C_LCD_CS1);
#ifdef LCD_MULTIPLEX
DDRA = 0xFF; // set LCD_DAT pins to output
#endif
PORTC_LCD_CTRL |= (1<<OUT_C_LCD_A0);
PORTC_LCD_CTRL &= ~(1<<OUT_C_LCD_RnW);
for(uint8_t x=128; x>0; --x) {
PORTA_LCD_DAT = *p++;
PORTC_LCD_CTRL |= (1<<OUT_C_LCD_E);
PORTC_LCD_CTRL &= ~(1<<OUT_C_LCD_E);
}
PORTC_LCD_CTRL |= (1<<OUT_C_LCD_A0);
PORTC_LCD_CTRL |= (1<<OUT_C_LCD_CS1);
}
}

139
src/lcd.h Normal file
View file

@ -0,0 +1,139 @@
/*
* 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 lcd_h
#define lcd_h
#include "gruvin9x.h"
#define DISPLAY_W 128
#define DISPLAY_H 64
#define FW 6
#define FWNUM 5
#define FH 8
#define OUTDEZ_SPEED 0
/* lcd common flags */
#define BLINK 0x01
/* lcd text flags */
#define INVERS 0x02
#define DBLSIZE 0x04
/* lcd putc flags */
#define CONDENSED 0x08
/* lcd puts flags */
#define BSS 0x10
#define ZCHAR 0x20
/* lcd outdez flags */
#define UNSIGN 0x08
#define LEADING0 0x10
#define SPARE1 0x20
#define SPARE2 0x30
#define SPARE3 0x40
#define PREC1 0x50
#define PREC2 0x60
#define PREC3 0x70
#define MODE(flags) (-4 + ((int8_t)(flags & 0x70) >> 4))
#define LEFT 0x80 /* align left */
/* line, rect, square flags */
#define BLACK 0x02
#define WHITE 0x04
/* other flags */
#define NO_UNIT UNSIGN
#define SHRT_TM_MODE 0x10
extern uint8_t displayBuf[DISPLAY_W*DISPLAY_H/8];
extern uint8_t lcd_lastPos;
extern void lcd_putc(unsigned char x,unsigned char y,const char c);
extern void lcd_putcAtt(unsigned char x,unsigned char y,const char c,uint8_t mode);
extern void lcd_putsAtt(unsigned char x,unsigned char y,const prog_char * s,uint8_t mode);
extern void lcd_putsnAtt(unsigned char x,unsigned char y,const prog_char * s,unsigned char len,uint8_t mode);
extern void lcd_puts_P(unsigned char x,unsigned char y,const prog_char * s);
extern void lcd_putsn_P(unsigned char x,unsigned char y,const prog_char * s,unsigned char len);
extern void lcd_outhex4(unsigned char x,unsigned char y,uint16_t val);
extern void lcd_outdezAtt(uint8_t x, uint8_t y, int16_t val, uint8_t mode=0);
extern void lcd_outdezNAtt(uint8_t x, uint8_t y, int16_t val, uint8_t mode=0, uint8_t len=0);
extern void lcd_outdez8(uint8_t x, uint8_t y, int8_t val);
extern void putsModelName(uint8_t x, uint8_t y, char *name, uint8_t id, uint8_t att);
extern void putsSwitches(uint8_t x, uint8_t y, int8_t swtch, uint8_t att);
extern void putsFlightPhase(uint8_t x, uint8_t y, int8_t idx, uint8_t att);
extern void putsTmrMode(uint8_t x, uint8_t y, int8_t mode, uint8_t att);
extern void putsChnRaw(uint8_t x,uint8_t y,uint8_t idx1,uint8_t att);
extern void putsChn(uint8_t x,uint8_t y,uint8_t idx1,uint8_t att);
extern void putsChnLetter(uint8_t x, uint8_t y, uint8_t idx, uint8_t attr);
extern void putsVolts(uint8_t x, uint8_t y, uint16_t volts, uint8_t att);
extern void putsVBat(uint8_t x, uint8_t y, uint8_t att);
extern void putsTime(uint8_t x,uint8_t y, int16_t tme, uint8_t att, uint8_t att2);
#ifdef FRSKY
// TODO move this into frsky.h
extern void putsTelemetry(uint8_t x, uint8_t y, uint8_t val, uint8_t unit, uint8_t att);
#endif
extern void lcd_plot(unsigned char x, unsigned char y, uint8_t att=0);
extern void lcd_hline(unsigned char x,unsigned char y, uint8_t w, uint8_t att=0);
extern void lcd_hlineStip(int8_t x, uint8_t y, uint8_t w, uint8_t pat, uint8_t att=0);
extern void lcd_vline(uint8_t x, int8_t y, int8_t h);
extern void lcd_vlineStip(uint8_t x, int8_t y, int8_t h, uint8_t pat);
extern void lcd_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t pat=0xff, uint8_t att=0);
extern void lcd_filled_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t att=0);
inline void lcd_square(uint8_t x, uint8_t y, uint8_t w, uint8_t att=0) { lcd_rect(x, y, w, w, 0xff, att); }
#define DO_CROSS(xx,yy,ww) \
lcd_vline(xx,yy-ww/2,ww); \
lcd_hline(xx-ww/2,yy,ww);
#define V_BAR(xx,yy,ll) \
lcd_vline(xx-1,yy-ll,ll); \
lcd_vline(xx ,yy-ll,ll); \
lcd_vline(xx+1,yy-ll,ll);
extern void lcd_img_f(unsigned char x,unsigned char y);
extern void lcd_img(uint8_t i_x,uint8_t i_y,const prog_uchar * imgdat,uint8_t idx,uint8_t mode);
extern void lcdSetRefVolt(unsigned char val);
extern void lcd_init();
extern void lcd_clear();
extern void refreshDiplay();
#define BLINK_ON_PHASE (g_blinkTmr10ms & (1<<6))
#define BLINK_SYNC g_blinkTmr10ms = (3<<5)
#endif
/*eof*/

416
src/main_views.cpp Normal file
View file

@ -0,0 +1,416 @@
/*
* 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.
*
*/
#include "menus.h"
#define ALTERNATE 0x10
enum MainViews {
e_outputValues,
e_outputBars,
e_inputs,
e_timer2,
#ifdef FRSKY
e_telemetry,
#endif
MAX_VIEWS
};
uint8_t tabViews[] = {
1, /*e_outputValues*/
1, /*e_outputBars*/
3, /*e_inputs*/
1, /*e_timer2*/
#ifdef FRSKY
2, /*e_telemetry*/
#endif
};
void menuMainView(uint8_t event)
{
static uint8_t switchView = 255;
static bool instantTrimSwLock;
static bool trim2OfsSwLock;
uint8_t view = (switchView == 255 ? g_eeGeneral.view : switchView);
bool telemViewSw = isFunctionActive(FUNC_VIEW_TELEMETRY);
if (switchView == 255 && telemViewSw) { view = switchView = e_telemetry + ALTERNATE; }
if (switchView != 255 && !telemViewSw) { view = g_eeGeneral.view; switchView = 255; }
uint8_t view_base = view & 0x0f;
switch(event)
{
case EVT_KEY_BREAK(KEY_MENU):
if (view_base == e_timer2) {
Timer2_running = !Timer2_running;
beepKey();
}
break;
case EVT_KEY_LONG(KEY_MENU):// go to last menu
pushMenu(lastPopMenu());
killEvents(event);
break;
case EVT_KEY_BREAK(KEY_RIGHT):
case EVT_KEY_BREAK(KEY_LEFT):
if (switchView != 255) break;
g_eeGeneral.view = (view + (event == EVT_KEY_BREAK(KEY_RIGHT) ? ALTERNATE : tabViews[view_base]*ALTERNATE-ALTERNATE)) % (tabViews[view_base]*ALTERNATE);
eeDirty(EE_GENERAL);
beepKey();
break;
case EVT_KEY_LONG(KEY_RIGHT):
pushMenu(menuProcModelSelect);
killEvents(event);
break;
case EVT_KEY_LONG(KEY_LEFT):
pushMenu(menuProcSetup);
killEvents(event);
break;
case EVT_KEY_BREAK(KEY_UP):
if (switchView != 255) break;
g_eeGeneral.view = view+1;
if(g_eeGeneral.view>=MAX_VIEWS) g_eeGeneral.view=0;
eeDirty(EE_GENERAL);
beepKey();
break;
case EVT_KEY_BREAK(KEY_DOWN):
if (switchView != 255) break;
if(view>0)
g_eeGeneral.view = view - 1;
else
g_eeGeneral.view = MAX_VIEWS-1;
eeDirty(EE_GENERAL);
beepKey();
break;
case EVT_KEY_LONG(KEY_UP):
chainMenu(menuProcStatistic);
killEvents(event);
break;
case EVT_KEY_LONG(KEY_DOWN):
#if defined(JETI)
JETI_EnableRXD(); // enable JETI-Telemetry reception
chainMenu(menuProcJeti);
#else
chainMenu(menuProcStatistic2);
#endif
killEvents(event);
break;
case EVT_KEY_FIRST(KEY_EXIT):
if(s_timerState==TMR_BEEPING) {
s_timerState = TMR_STOPPED;
beepKey();
}
else if (view == e_timer2) {
resetTimer2();
beepKey();
}
#ifdef FRSKY
else if (view == e_telemetry) {
resetTelemetry();
beepKey();
}
#endif
else {
resetTimer1();
}
break;
case EVT_KEY_LONG(KEY_EXIT):
resetTimer1();
resetTimer2();
#ifdef FRSKY
resetTelemetry();
#endif
beepKey();
break;
case EVT_ENTRY:
killEvents(KEY_EXIT);
killEvents(KEY_UP);
killEvents(KEY_DOWN);
instantTrimSwLock = true;
trim2OfsSwLock = true;
break;
}
bool trimSw = isFunctionActive(FUNC_INSTANT_TRIM);
if (!instantTrimSwLock && trimSw) instantTrim();
instantTrimSwLock = trimSw;
trimSw = isFunctionActive(FUNC_TRIMS_2_OFS);
if (!trim2OfsSwLock && trimSw) moveTrimsToOffsets();
trim2OfsSwLock = trimSw;
if (view == e_telemetry+ALTERNATE) {
putsModelName(0, 0, g_model.name, g_eeGeneral.currModel, 0);
uint8_t att = (g_vbat100mV < g_eeGeneral.vBatWarn ? BLINK : 0);
putsVBat(14*FW,0,att);
if(s_timerState != TMR_OFF){
att = (s_timerState==TMR_BEEPING ? BLINK : 0);
putsTime(17*FW, 0, s_timerVal[0], att, att);
}
lcd_filled_rect(0, 0, DISPLAY_W, 8);
}
else {
uint8_t phase = getFlightPhase();
lcd_putsnAtt(6*FW+2, 2*FH, g_model.phaseData[phase].name, sizeof(g_model.phaseData[phase].name), ZCHAR);
uint8_t att = (g_vbat100mV < g_eeGeneral.vBatWarn ? BLINK : 0) | DBLSIZE;
putsModelName(2*FW-2, 0*FH, g_model.name, g_eeGeneral.currModel, DBLSIZE);
putsVBat(6*FW+1, 2*FH, att|NO_UNIT);
lcd_putc(6*FW+2, 3*FH, 'V');
if (s_timerState != TMR_OFF) {
uint8_t att = DBLSIZE | (s_timerState==TMR_BEEPING ? BLINK : 0);
putsTime(12*FW+3, FH*2, s_timerVal[0], att,att);
putsTmrMode(s_timerVal[0] >= 0 ? 9*FW-FW/2+5 : 9*FW-FW/2-2, FH*3, g_model.timer1.mode, SHRT_TM_MODE);
}
// trim sliders
for(uint8_t i=0; i<4; i++)
{
#define TL 27
// LH LV RV RH
static uint8_t x[4] = {128*1/4+2, 4, 128-4, 128*3/4-2};
static uint8_t vert[4] = {0,1,1,0};
uint8_t xm, ym;
xm = x[i];
uint8_t att = 0;
int16_t val = g_model.subtrim[i] + phaseaddress(getTrimFlightPhase(i, phase))->trim[i];
if (val < -125 || val > 125)
att = BLINK;
if (val < -(TL+1)*4)
val = -(TL+1);
else if (val > (TL+1)*4)
val = TL+1;
else
val /= 4;
if (vert[i]) {
ym = 31;
lcd_vline(xm, ym-TL, TL*2);
if(((g_eeGeneral.stickMode&1) != (i&1)) || !(g_model.thrTrim)){
lcd_vline(xm-1, ym-1, 3);
lcd_vline(xm+1, ym-1, 3);
}
ym -= val;
}
else {
ym = 60;
lcd_hline(xm-TL, ym, TL*2);
lcd_hline(xm-1, ym-1, 3);
lcd_hline(xm-1, ym+1, 3);
xm += val;
}
lcd_square(xm-3, ym-3, 7, att);
}
}
if(view_base<e_inputs) {
for(uint8_t i=0; i<8; i++)
{
uint8_t x0,y0;
int16_t val = g_chans512[i];
//val += g_model.limitData[i].revert ? g_model.limitData[i].offset : -g_model.limitData[i].offset;
switch(view_base)
{
case e_outputValues:
x0 = (i%4*9+3)*FW/2;
y0 = i/4*FH+40;
// *1000/1024 = x - x/32 + x/128
#define GPERC(x) (x - x/32 + x/128)
#if defined (DECIMALS_DISPLAYED)
lcd_outdezAtt( x0+4*FW , y0, GPERC(val), PREC1);
#else
lcd_outdezAtt( x0+4*FW , y0, GPERC(val)/10, 0); // G: Don't like the decimal part*
#endif
break;
case e_outputBars:
#define WBAR2 (50/2)
x0 = i<4 ? 128/4+2 : 128*3/4-2;
y0 = 38+(i%4)*5;
int8_t l = (abs(val) * WBAR2 + 512) / 1024;
if(l>WBAR2) l = WBAR2; // prevent bars from going over the end - comment for debugging
lcd_hlineStip(x0-WBAR2,y0,WBAR2*2+1,0x55);
lcd_vline(x0,y0-2,5);
if(val>0){
x0+=1;
}else{
x0-=l;
}
lcd_hline(x0,y0+1,l);
lcd_hline(x0,y0-1,l);
break;
}
}
}
#ifdef FRSKY
else if(view_base == e_telemetry) {
static uint8_t displayCount = 0;
static uint8_t staticTelemetry[2];
static uint8_t staticRSSI[2];
static bool alarmRaised[2];
if (frskyStreaming) {
uint8_t y0, x0, val, blink;
if (!displayCount) {
for (int i=0; i<2; i++) {
staticTelemetry[i] = frskyTelemetry[i].value;
staticRSSI[i] = frskyRSSI[i].value;
alarmRaised[i] = FRSKY_alarmRaised(i);
}
}
displayCount = (displayCount+1) % 50;
if (view & ALTERNATE) {
if (g_model.frsky.channels[0].ratio || g_model.frsky.channels[1].ratio) {
x0 = 0;
for (int i=0; i<2; i++) {
if (g_model.frsky.channels[i].ratio) {
blink = (alarmRaised[i] ? INVERS : 0);
lcd_puts_P(x0, 3*FH, PSTR("A ="));
lcd_putc(x0+FW, 3*FH, '1'+i);
x0 += 3*FW;
val = ((uint16_t)staticTelemetry[i]+g_model.frsky.channels[i].offset)*g_model.frsky.channels[i].ratio / 255;
putsTelemetry(x0, 2*FH, val, g_model.frsky.channels[i].type, blink|DBLSIZE|LEFT);
val = ((int16_t)frskyTelemetry[i].min+g_model.frsky.channels[i].offset)*g_model.frsky.channels[i].ratio / 255;
putsTelemetry(x0+FW, 4*FH, val, g_model.frsky.channels[i].type, 0);
val = ((int16_t)frskyTelemetry[i].max+g_model.frsky.channels[i].offset)*g_model.frsky.channels[i].ratio / 255;
putsTelemetry(x0+3*FW, 4*FH, val, g_model.frsky.channels[i].type, LEFT);
x0 = 11*FW-2;
}
}
}
#if 0
// Display RX Batt Volts only if a valid channel (A1/A2) has been selected
if (g_eeFrsky.rxVoltsChannel >0)
{
y+=FH; lcd_puts_P(2*FW, y, PSTR("Rx Batt:"));
// Rx batt voltage bar frame
// Minimum voltage
lcd_vline(3, 58, 6); // marker
y = 6*FH;
putsVolts(1, y, g_eeFrsky.rxVoltsBarMin, LEFT);
uint8_t middleVolts = g_eeFrsky.rxVoltsBarMin+(g_eeFrsky.rxVoltsBarMax - g_eeFrsky.rxVoltsBarMin)/2;
putsVolts(64-FW, y, middleVolts, LEFT);
lcd_vline(64, 58, 6); // marker
putsVolts(128-FW, y, g_eeFrsky.rxVoltsBarMax, 0);
lcd_vline(125, 58, 6); // marker
// Rx Batt: volts (255 == g_eefrsky.rxVoltsMax)
uint16_t centaVolts = (voltsVal > 0) ? (10 * (uint16_t)g_eeFrsky.rxVoltsMax * (uint32_t)(voltsVal) / 255) + g_eeFrsky.rxVoltsOfs : 0;
lcd_outdezAtt(13*FW, 4*FH, centaVolts, 0|PREC2);
lcd_putc(13*FW, 4*FH, 'v');
// draw the actual voltage bar
uint16_t centaVoltsMin = 10 * g_eeFrsky.rxVoltsBarMin;
if (centaVolts >= centaVoltsMin)
{
uint8_t vbarLen = (centaVolts - (10 * (uint16_t)g_eeFrsky.rxVoltsBarMin)) * 12
/ (g_eeFrsky.rxVoltsBarMax - g_eeFrsky.rxVoltsBarMin);
for (uint8_t i = 59; i < 63; i++) // Bar 4 pixels thick (high)
lcd_hline(4, i, (vbarLen > 120) ? 120 : vbarLen);
}
}
#endif
lcd_puts_P(0, 6*FH, PSTR("Rx="));
lcd_outdezAtt(3 * FW, 5*FH+2, staticRSSI[0], DBLSIZE|LEFT);
lcd_outdezAtt(4 * FW, 7*FH, frskyRSSI[0].min, 0);
lcd_outdezAtt(6 * FW, 7*FH, frskyRSSI[0].max, LEFT);
lcd_puts_P(11 * FW - 2, 6*FH, PSTR("Tx="));
lcd_outdezAtt(14 * FW - 2, 5*FH+2, staticRSSI[1], DBLSIZE|LEFT);
lcd_outdezAtt(15 * FW - 2, 7*FH, frskyRSSI[1].min, 0);
lcd_outdezAtt(17 * FW - 2, 7*FH, frskyRSSI[1].max, LEFT);
}
else {
y0 = 5*FH;
//lcd_puts_P(2*FW-3, y0, PSTR("Tele:"));
x0 = 4*FW-3;
for (int i=0; i<2; i++) {
if (g_model.frsky.channels[i].ratio) {
blink = (alarmRaised[i] ? INVERS+BLINK : 0)|LEFT;
lcd_puts_P(x0, y0, PSTR("A ="));
lcd_putc(x0+FW, y0, '1'+i);
val = ((int16_t)staticTelemetry[i]+g_model.frsky.channels[i].offset)*g_model.frsky.channels[i].ratio / 255;
putsTelemetry(x0+3*FW, y0, val, g_model.frsky.channels[i].type, blink);
x0 = 13*FW-3;
}
}
y0+=FH;
//lcd_puts_P(2*FW-3, y0, PSTR("RSSI:"));
lcd_puts_P(4*FW-3, y0, PSTR("Rx="));
lcd_outdezAtt(7*FW-3, y0, staticRSSI[0], LEFT);
lcd_puts_P(13*FW-3, y0, PSTR("Tx="));
lcd_outdezAtt(16*FW-3, y0, staticRSSI[1], LEFT);
}
}
else {
lcd_putsAtt(22, 40, PSTR("NO DATA"), DBLSIZE);
}
}
#endif
else if (view_base<e_timer2) {
#define BOX_WIDTH 23
#define BAR_HEIGHT (BOX_WIDTH-1l)
#define MARKER_WIDTH 5
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define BOX_LIMIT (BOX_WIDTH-MARKER_WIDTH)
#define LBOX_CENTERX ( SCREEN_WIDTH/4 + 10)
#define LBOX_CENTERY (SCREEN_HEIGHT-9-BOX_WIDTH/2)
#define RBOX_CENTERX (3*SCREEN_WIDTH/4 - 10)
#define RBOX_CENTERY (SCREEN_HEIGHT-9-BOX_WIDTH/2)
lcd_square(LBOX_CENTERX-BOX_WIDTH/2, LBOX_CENTERY-BOX_WIDTH/2, BOX_WIDTH);
lcd_square(RBOX_CENTERX-BOX_WIDTH/2, RBOX_CENTERY-BOX_WIDTH/2, BOX_WIDTH);
DO_CROSS(LBOX_CENTERX,LBOX_CENTERY,3)
DO_CROSS(RBOX_CENTERX,RBOX_CENTERY,3)
lcd_square(LBOX_CENTERX+(calibratedStick[0]*BOX_LIMIT/(2*RESX))-MARKER_WIDTH/2, LBOX_CENTERY-(calibratedStick[1]*BOX_LIMIT/(2*RESX))-MARKER_WIDTH/2, MARKER_WIDTH);
lcd_square(RBOX_CENTERX+(calibratedStick[3]*BOX_LIMIT/(2*RESX))-MARKER_WIDTH/2, RBOX_CENTERY-(calibratedStick[2]*BOX_LIMIT/(2*RESX))-MARKER_WIDTH/2, MARKER_WIDTH);
// Optimization by Mike Blandford
{
uint8_t x, y, len ; // declare temporary variables
for( x = -5, y = 4 ; y < 7 ; x += 5, y += 1 )
{
len = ((calibratedStick[y]+RESX)*BAR_HEIGHT/(RESX*2))+1l ; // calculate once per loop
V_BAR(SCREEN_WIDTH/2+x,SCREEN_HEIGHT-10, len )
}
}
int8_t a = (view == e_inputs) ? 0 : 3+(view/ALTERNATE)*6;
int8_t b = (view == e_inputs) ? 6 : 6+(view/ALTERNATE)*6;
for(int8_t i=a; i<(a+3); i++) lcd_putsnAtt(2*FW-2 ,(i-a)*FH+4*FH,get_switches_string()+3*i,3,getSwitch(i+1, 0) ? INVERS : 0);
for(int8_t i=b; i<(b+3); i++) lcd_putsnAtt(17*FW-1,(i-b)*FH+4*FH,get_switches_string()+3*i,3,getSwitch(i+1, 0) ? INVERS : 0);
}
else // timer2
{
putsTime(33+FW+2, FH*5, timer2, DBLSIZE, DBLSIZE);
}
}

514
src/menus.cpp Normal file
View file

@ -0,0 +1,514 @@
/*
* 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.
*
*/
#include "gruvin9x.h"
#include "templates.h"
#include "menus.h"
int16_t calibratedStick[NUM_STICKS+NUM_POTS];
int16_t ex_chans[NUM_CHNOUT]; // Outputs + intermidiates
uint8_t s_pgOfs;
uint8_t s_editMode;
uint8_t s_noHi;
int16_t g_chans512[NUM_CHNOUT];
void menu_lcd_onoff( uint8_t x,uint8_t y, uint8_t value, uint8_t mode )
{
lcd_putsnAtt( x, y, PSTR("OFFON ")+3*value,3,mode ? INVERS:0) ;
}
void menu_lcd_HYPHINV( uint8_t x,uint8_t y, uint8_t value, uint8_t mode )
{
lcd_putsnAtt( x, y, PSTR("---INV")+3*value,3,mode ? INVERS:0) ;
}
void DisplayScreenIndex(uint8_t index, uint8_t count, uint8_t attr)
{
lcd_outdezAtt(128,0,count,attr);
lcd_putcAtt(1+128-FW*(count>9 ? 3 : 2),0,'/',attr);
lcd_outdezAtt(1+128-FW*(count>9 ? 3 : 2),0,index+1,attr);
}
int16_t p1valdiff;
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)
{
int16_t newval = val;
uint8_t kpl=KEY_RIGHT, kmi=KEY_LEFT, kother = -1;
if(event & _MSK_KEY_DBL){
uint8_t hlp=kpl;
kpl=kmi;
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))) ) {
newval++;
#if defined (BEEPSPKR)
beepKeySpkr(BEEP_KEY_UP_FREQ);
#else
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))) ) {
newval--;
#if defined (BEEPSPKR)
beepKeySpkr(BEEP_KEY_DOWN_FREQ);
#else
beepKey();
#endif
kother=kpl;
}
if((kother != (uint8_t)-1) && keyState((EnumKeys)kother)){
newval=-val;
killEvents(kmi);
killEvents(kpl);
}
if(i_min==0 && i_max==1 && event==EVT_KEY_FIRST(KEY_MENU)) {
s_editMode = false;
newval=!val;
killEvents(event);
}
//change values based on P1
newval -= p1valdiff;
if(newval > i_max)
{
newval = i_max;
killEvents(event);
#if defined (BEEPSPKR)
beepWarn2Spkr(BEEP_KEY_UP_FREQ);
#else
beepWarn2();
#endif
}
if(newval < i_min)
{
newval = i_min;
killEvents(event);
#if defined (BEEPSPKR)
beepWarn2Spkr(BEEP_KEY_DOWN_FREQ);
#else
beepWarn2();
#endif
}
if(newval != val){
if(newval==0) {
pauseEvents(event); // delay before auto-repeat continues
#if defined (BEEPSPKR)
if (newval>val)
beepWarn2Spkr(BEEP_KEY_UP_FREQ);
else
beepWarn2Spkr(BEEP_KEY_DOWN_FREQ);
#else
beepKey();
#endif
}
eeDirty(i_flags & (EE_GENERAL|EE_MODEL));
checkIncDec_Ret = (newval > val ? 1 : -1);
}
else {
checkIncDec_Ret = 0;
}
return newval;
}
int8_t checkIncDecModel(uint8_t event, int8_t i_val, int8_t i_min, int8_t i_max)
{
return checkIncDec(event,i_val,i_min,i_max,EE_MODEL);
}
int8_t checkIncDecGen(uint8_t event, int8_t i_val, int8_t i_min, int8_t i_max)
{
return checkIncDec(event,i_val,i_min,i_max,EE_GENERAL);
}
bool check_simple(uint8_t event, uint8_t curr, MenuFuncP *menuTab, uint8_t menuTabSize, uint8_t maxrow)
{
return check(event, curr, menuTab, menuTabSize, 0, 0, maxrow);
}
bool check_submenu_simple(uint8_t event, uint8_t maxrow)
{
return check_simple(event, 0, 0, 0, maxrow);
}
#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)
{
if (menuTab) {
uint8_t attr = m_posVert==0 ? INVERS : 0;
if (m_posVert==0) {
switch(event)
{
case EVT_KEY_FIRST(KEY_LEFT):
if(curr>0)
chainMenu((MenuFuncP)pgm_read_adr(&menuTab[curr-1]));
else
chainMenu((MenuFuncP)pgm_read_adr(&menuTab[menuTabSize-1]));
return false;
case EVT_KEY_FIRST(KEY_RIGHT):
if(curr < (menuTabSize-1))
chainMenu((MenuFuncP)pgm_read_adr(&menuTab[curr+1]));
else
chainMenu((MenuFuncP)pgm_read_adr(&menuTab[0]));
return false;
}
}
DisplayScreenIndex(curr, menuTabSize, attr);
}
uint8_t maxcol = MAXCOL(m_posVert);
switch(event)
{
case EVT_ENTRY:
minit();
s_editMode = false;
break;
case EVT_KEY_FIRST(KEY_MENU):
if (maxcol > 0)
s_editMode = !s_editMode;
break;
case EVT_KEY_LONG(KEY_EXIT):
s_editMode = false;
popMenu(false);
break;
case EVT_KEY_BREAK(KEY_EXIT):
if(s_editMode) {
s_editMode = false;
break;
}
if(m_posVert==0 || !menuTab) {
popMenu(); //beeps itself
}
else {
beepKey();
minit();BLINK_SYNC;
}
break;
case EVT_KEY_REPT(KEY_RIGHT): //inc
if(m_posHorz==maxcol) break;
case EVT_KEY_FIRST(KEY_RIGHT)://inc
if(!horTab || s_editMode)break;
INC(m_posHorz,maxcol);
BLINK_SYNC;
break;
case EVT_KEY_REPT(KEY_LEFT): //dec
if(m_posHorz==0) break;
case EVT_KEY_FIRST(KEY_LEFT)://dec
if(!horTab || s_editMode)break;
DEC(m_posHorz,maxcol);
BLINK_SYNC;
break;
case EVT_KEY_REPT(KEY_DOWN): //inc
if(m_posVert==maxrow) break;
case EVT_KEY_FIRST(KEY_DOWN): //inc
if(s_editMode)break;
do {
INC(m_posVert,maxrow);
} while(MAXCOL(m_posVert) == (uint8_t)-1);
m_posHorz = min(m_posHorz, MAXCOL(m_posVert));
BLINK_SYNC;
break;
case EVT_KEY_REPT(KEY_UP): //dec
if(m_posVert==0) break;
case EVT_KEY_FIRST(KEY_UP): //dec
if(s_editMode)break;
do {
DEC(m_posVert,maxrow);
} while(MAXCOL(m_posVert) == (uint8_t)-1);
m_posHorz = min(m_posHorz, MAXCOL(m_posVert));
BLINK_SYNC;
break;
}
uint8_t max = menuTab ? 7 : 6;
if(m_posVert<1) s_pgOfs=0;
else if(m_posVert-s_pgOfs>max) s_pgOfs = m_posVert-max;
else if(m_posVert-s_pgOfs<1) s_pgOfs = m_posVert-1;
return true;
}
MenuFuncP g_menuStack[5];
uint8_t g_menuPos[4];
uint8_t g_menuStackPtr = 0;
uint8_t m_posVert;
uint8_t m_posHorz;
void popMenu(bool uppermost)
{
if (g_menuStackPtr>0) {
g_menuStackPtr = uppermost ? 0 : g_menuStackPtr-1;
beepKey();
m_posHorz = g_menuPos[g_menuStackPtr] & 0x0F;
m_posVert = g_menuPos[g_menuStackPtr] >> 4;
(*g_menuStack[g_menuStackPtr])(EVT_ENTRY_UP);
}
else {
alert(PSTR("menuStack underflow"));
}
}
void chainMenu(MenuFuncP newMenu)
{
g_menuStack[g_menuStackPtr] = newMenu;
(*newMenu)(EVT_ENTRY);
beepKey();
}
void pushMenu(MenuFuncP newMenu)
{
g_menuPos[g_menuStackPtr] = (m_posVert << 4) + m_posHorz;
g_menuStackPtr++;
if(g_menuStackPtr >= DIM(g_menuStack))
{
g_menuStackPtr--;
alert(PSTR("menuStack overflow"));
return;
}
beepKey();
g_menuStack[g_menuStackPtr] = newMenu;
(*newMenu)(EVT_ENTRY);
}
/******************************************************************************
the functions below are from int-level
the functions below are from int-level
the functions below are from int-level
******************************************************************************/
void setupPulses()
{
switch(g_model.protocol)
{
case PROTO_PPM:
setupPulsesPPM();
break;
case PROTO_SILV_A:
case PROTO_SILV_B:
case PROTO_SILV_C:
setupPulsesSilver();
break;
case PROTO_TRACER_CTP1009:
setupPulsesTracerCtp1009();
break;
}
}
//inline int16_t reduceRange(int16_t x) // for in case we want to have room for subtrims
//{
// return x-(x/4); //512+128 =? 640, 640 - 640/4 == 640 * 3/4 => 480 (just below 500msec - it can still reach 500 with offset)
//}
void setupPulsesPPM() // changed 10/05/2010 by dino Issue 128
{
#define PPM_CENTER 1200*2
int16_t PPM_range = g_model.extendedLimits ? 640*2 : 512*2; //range of 0.7..1.7msec
//Total frame length = 22.5msec
//each pulse is 0.7..1.7ms long with a 0.3ms stop tail
//The pulse ISR is 2mhz so everything is multiplied by 2
// G: Found the following reference at th9x. The below code does not seem
// to produce quite exactly this, to my eye. *shrug*
// http://www.aerodesign.de/peter/2000/PCM/frame_ppm.gif
uint8_t j=0;
uint8_t p=8+g_model.ppmNCH*2; //Channels *2
uint16_t q=(g_model.ppmDelay*50+300)*2; //Stoplen *2
uint16_t rest=22500u*2-q; //Minimum Framelen=22.5 ms
if(p>9) rest=p*(1720u*2 + q) + 4000u*2; //for more than 9 channels, frame must be longer
for(uint8_t i=0;i<p;i++){ //NUM_CHNOUT
int16_t v = max(min(g_chans512[i],(int16_t)PPM_range),(int16_t)-PPM_range) + (int16_t)PPM_CENTER;
rest-=(v+q);
pulses2MHz[j++] = q;
pulses2MHz[j++] = v - q + 600; /* as Pat MacKenzie suggests */
}
pulses2MHz[j++]=q;
pulses2MHz[j++]=rest;
pulses2MHz[j++]=0;
}
uint16_t *pulses2MHzPtr;
#define BITLEN (600u*2)
void _send_hilo(uint16_t hi,uint16_t lo)
{
*pulses2MHzPtr++=hi; *pulses2MHzPtr++=lo;
}
#define send_hilo_silv( hi, lo) _send_hilo( (hi)*BITLEN,(lo)*BITLEN )
void sendBitSilv(uint8_t val)
{
send_hilo_silv((val)?2:1,(val)?2:1);
}
void send2BitsSilv(uint8_t val)
{
sendBitSilv(val&2);sendBitSilv(val&1);
}
// _ oder - je 0.6ms (gemessen 0.7ms)
//
//____-----_-_-_--_--_ -_--__ -_-_-_-_ -_-_-_-_ --__--__-_______
// trailer chan m1 m2
//
//see /home/thus/txt/silverlit/thus.txt
//m1, m2 most significant bit first |m1-m2| <= 9
//chan: 01=C 10=B
//chk = 0 - chan -m1>>2 -m1 -m2>>2 -m2
//<= 500us Probleme
//>= 650us Probleme
//periode orig: 450ms
void setupPulsesSilver()
{
int8_t chan=1; //chan 1=C 2=B 0=A?
switch(g_model.protocol)
{
case PROTO_SILV_A: chan=0; break;
case PROTO_SILV_B: chan=2; break;
case PROTO_SILV_C: chan=1; break;
}
int8_t m1 = (uint16_t)(g_chans512[0]+1024)*2 / 256;
int8_t m2 = (uint16_t)(g_chans512[1]+1024)*2 / 256;
if (m1 < 0) m1=0;
if (m2 < 0) m2=0;
if (m1 > 15) m1=15;
if (m2 > 15) m2=15;
if (m2 > m1+9) m1=m2-9;
if (m1 > m2+9) m2=m1-9;
//uint8_t i=0;
pulses2MHzPtr=pulses2MHz;
send_hilo_silv(5,1); //idx 0 erzeugt pegel=0 am Ausgang, wird als high gesendet
send2BitsSilv(0);
send_hilo_silv(2,1);
send_hilo_silv(2,1);
send2BitsSilv(chan); //chan 1=C 2=B 0=A?
uint8_t sum = 0 - chan;
send2BitsSilv(m1>>2); //m1
sum-=m1>>2;
send2BitsSilv(m1);
sum-=m1;
send2BitsSilv(m2>>2); //m2
sum-=m2>>2;
send2BitsSilv(m2);
sum-=m2;
send2BitsSilv(sum); //chk
sendBitSilv(0);
pulses2MHzPtr--;
send_hilo_silv(50,0); //low-impuls (pegel=1) ueberschreiben
}
/*
TRACE CTP-1009
- = send 45MHz
_ = send nix
start1 0 1 start2
-------__ --_ -__ -----__
7ms 2 .8 .4 .4 .8 5 2
frame:
start1 24Bits_1 start2 24_Bits2
24Bits_1:
7 x Bits Throttle lsb first
1 x 0
6 x Bits rotate lsb first
1 x Bit 1=rechts
1 x 0
4 x Bits chk5 = nib2 ^ nib4
4 x Bits chk6 = nib1 ^ nib3
24Bits_2:
7 x Bits Vorwaets lsb first 0x3f = mid
1 x 1
7 x Bits 0x0e lsb first
1 x 1
4 x Bits chk5 = nib2 ^ nib4
4 x Bits chk6 = nib1 ^ nib3
*/
#define BIT_TRA (400u*2)
void sendBitTra(uint8_t val)
{
if(val) _send_hilo( BIT_TRA*1 , BIT_TRA*2 );
else _send_hilo( BIT_TRA*2 , BIT_TRA*1 );
}
void sendByteTra(uint8_t val)
{
for(uint8_t i=0; i<8; i++, val>>=1) sendBitTra(val&1);
}
void setupPulsesTracerCtp1009()
{
pulses2MHzPtr=pulses2MHz;
static bool phase;
if( (phase=!phase) ){
uint8_t thr = min(127u,(uint16_t)(g_chans512[0]+1024+8) / 16u);
uint8_t rot;
if (g_chans512[1] >= 0)
{
rot = min(63u,(uint16_t)( g_chans512[1]+16) / 32u) | 0x40;
}else{
rot = min(63u,(uint16_t)(-g_chans512[1]+16) / 32u);
}
sendByteTra(thr);
sendByteTra(rot);
uint8_t chk=thr^rot;
sendByteTra( (chk>>4) | (chk<<4) );
_send_hilo( 5000*2, 2000*2 );
}else{
uint8_t fwd = min(127u,(uint16_t)(g_chans512[2]+1024) / 16u) | 0x80;
sendByteTra(fwd);
sendByteTra(0x8e);
uint8_t chk=fwd^0x8e;
sendByteTra( (chk>>4) | (chk<<4) );
_send_hilo( 7000*2, 2000*2 );
}
*pulses2MHzPtr++=0;
if((pulses2MHzPtr-pulses2MHz) >= (signed)DIM(pulses2MHz)) alert(PSTR("pulse tab overflow"));
}

129
src/menus.h Normal file
View file

@ -0,0 +1,129 @@
/*
* 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.
*
*/
/*
Insert obligatories here
*/
#ifndef menus_h
#define menus_h
#include <inttypes.h>
#include "gruvin9x.h"
#define IS_THROTTLE(x) (((2-(g_eeGeneral.stickMode&1)) == x) && (x<4))
#define NO_HI_LEN 25
#define RESX 1024
#define RESXu 1024u
#define RESXul 1024ul
#define RESXl 1024l
#define RESKul 100ul
#define RESX_PLUS_TRIM (RESX+128)
typedef void (*MenuFuncP)(uint8_t event);
void DisplayScreenIndex(uint8_t index, uint8_t count, uint8_t attr);
extern uint8_t s_pgOfs;
extern uint8_t s_noHi;
// extern int16_t expo(int16_t x, int16_t k);
void menu_lcd_onoff(uint8_t x, uint8_t y, uint8_t value, uint8_t mode);
void menu_lcd_HYPHINV(uint8_t x, uint8_t y, uint8_t value, uint8_t mode);
extern MenuFuncP g_menuStack[5];
extern uint8_t g_menuStackPtr;
/// goto given Menu, but substitute current menu in menuStack
void chainMenu(MenuFuncP newMenu);
/// goto given Menu, store current menu in menuStack
void pushMenu(MenuFuncP newMenu);
///deliver address of last menu which was popped from
inline MenuFuncP lastPopMenu()
{
return g_menuStack[g_menuStackPtr+1];
}
/// return to last menu in menustack
/// if uppermost is set true, thenmenu return to uppermost menu in menustack
void popMenu(bool uppermost=false);
void menuMainView(uint8_t event);
void menuProcSetup(uint8_t event);
void menuProcModelSelect(uint8_t event);
void menuProcStatistic(uint8_t event);
void menuProcStatistic2(uint8_t event);
extern int16_t p1valdiff;
extern int8_t checkIncDec_Ret; // global helper vars
extern uint8_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);
int8_t checkIncDecGen(uint8_t event, int8_t i_val, int8_t i_min, int8_t i_max);
#define CHECK_INCDEC_MODELVAR( event, var, min, max) \
var = checkIncDecModel(event,var,min,max)
#define CHECK_INCDEC_GENVAR( event, var, min, max) \
var = checkIncDecGen(event,var,min,max)
// Menus related stuff ...
extern uint8_t m_posVert;
extern uint8_t m_posHorz;
inline void minit(){m_posVert=m_posHorz=0;}
bool check(uint8_t event, uint8_t curr, MenuFuncP *menuTab, uint8_t menuTabSize, prog_uint8_t *subTab, uint8_t subTabMax, uint8_t maxrow);
bool check_simple(uint8_t event, uint8_t curr, MenuFuncP *menuTab, uint8_t menuTabSize, uint8_t maxrow);
bool check_submenu_simple(uint8_t event, uint8_t maxrow);
typedef PROGMEM void (*MenuFuncP_PROGMEM)(uint8_t event);
#define TITLEP(pstr) lcd_putsAtt(0,0,pstr,INVERS)
#define TITLE(str) TITLEP(PSTR(str))
#define MENU(title, tab, menu, lines_count, lines...) \
TITLE(title); \
static prog_uint8_t APM mstate_tab[] = lines; \
if (!check(event,menu,tab,DIM(tab),mstate_tab,DIM(mstate_tab)-1,lines_count-1)) return;
#define SIMPLE_MENU_NOTITLE(tab, menu, lines_count) \
if (!check_simple(event,menu,tab,DIM(tab),lines_count-1)) return;
#define SIMPLE_MENU(title, tab, menu, lines_count) \
TITLE(title); \
SIMPLE_MENU_NOTITLE(tab, menu, lines_count)
#define SUBMENU(title, lines_count, lines...) \
TITLE(title); \
static prog_uint8_t APM mstate_tab[] = lines; \
if (!check(event,0,NULL,0,mstate_tab,DIM(mstate_tab)-1,lines_count-1)) return;
#define SIMPLE_SUBMENU_NOTITLE(lines_count) \
if (!check_submenu_simple(event,lines_count-1)) return;
#define SIMPLE_SUBMENU(title, lines_count) \
TITLE(title); \
SIMPLE_SUBMENU_NOTITLE(lines_count)
#endif

1773
src/model_menus.cpp Normal file

File diff suppressed because it is too large Load diff

256
src/myeeprom.h Normal file
View file

@ -0,0 +1,256 @@
/*
* Author - Erez Raviv <erezraviv@gmail.com>
*
* Based on 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 eeprom_h
#define eeprom_h
#include <inttypes.h>
#define WARN_THR_BIT 0x01
#define WARN_BEP_BIT 0x80
#define WARN_SW_BIT 0x02
#define WARN_MEM_BIT 0x04
#define WARN_BVAL_BIT 0x38
#define WARN_THR (!(g_eeGeneral.warnOpts & WARN_THR_BIT))
#define WARN_BEP (!(g_eeGeneral.warnOpts & WARN_BEP_BIT))
#define WARN_SW (!(g_eeGeneral.warnOpts & WARN_SW_BIT))
#define WARN_MEM (!(g_eeGeneral.warnOpts & WARN_MEM_BIT))
#define BEEP_VAL ( (g_eeGeneral.warnOpts & WARN_BVAL_BIT) >>3 )
#define EEPROM_ER9X_VER 4
#define EEPROM_ER9X_r751 9
#define EEPROM_VER_r584 3
#define EEPROM_VER_r751 5
#define EEPROM_VER 105
typedef struct t_TrainerMix {
uint8_t srcChn:6; // 0-7 = ch1-8
uint8_t mode:2; // off,add-mode,subst-mode
int8_t studWeight;
} __attribute__((packed)) TrainerMix; //
typedef struct t_TrainerData {
int16_t calib[4];
TrainerMix mix[4];
} __attribute__((packed)) TrainerData;
typedef struct t_FrSkyRSSIAlarm {
uint8_t level:2;
int8_t value:6;
} __attribute__((packed)) FrSkyRSSIAlarm;
typedef struct t_EEGeneral {
uint8_t myVers;
int16_t calibMid[7];
int16_t calibSpanNeg[7];
int16_t calibSpanPos[7];
uint16_t chkSum;
uint8_t currModel; //0..15
uint8_t contrast;
uint8_t vBatWarn;
int8_t vBatCalib;
int8_t lightSw;
TrainerData trainer;
uint8_t view; //index of subview in main scrren
uint8_t disableThrottleWarning:1;
int8_t switchWarning:2; // -1=down, 0=off, 1=up
uint8_t beeperVal:3;
uint8_t disableMemoryWarning:1;
uint8_t disableAlarmWarning:1;
uint8_t stickMode;
uint8_t inactivityTimer;
uint8_t throttleReversed:1;
uint8_t minuteBeep:1;
uint8_t preBeep:1;
uint8_t flashBeep:1;
uint8_t disableSplashScreen:1;
uint8_t enableTelemetryAlarm:1; // 0=no, 1=yes (Sound alarm when there's no telem. data coming in)
uint8_t spare:2;
uint8_t filterInput;
uint8_t lightAutoOff;
uint8_t templateSetup; //RETA order according to chout_ar array
int8_t PPM_Multiplier;
FrSkyRSSIAlarm frskyRssiAlarms[2];
} __attribute__((packed)) EEGeneral;
// eeprom modelspec
typedef struct t_ExpoData {
uint8_t mode:2; // 0=end, 1=pos, 2=neg, 3=both
uint8_t chn:2;
uint8_t curve:4; // 0=no curve, 1-6=std curves, 7-10=CV1-CV4, 11-15=CV9-CV13
int8_t swtch:5;
uint8_t phase:3; // if negPhase is 0: 0=normal, 5=FP4 if negPhase is 1: 5=!FP4
uint8_t negPhase:1;
uint8_t weight:7;
int8_t expo;
} __attribute__((packed)) ExpoData;
typedef struct t_LimitData {
int8_t min;
int8_t max;
bool revert;
int16_t offset;
} __attribute__((packed)) LimitData;
typedef struct t_MixData {
uint8_t destCh:5; // 0, 1..NUM_CHNOUT
uint8_t mixWarn:3; // mixer warning
#define MIX_P1 5
#define MIX_P2 6
#define MIX_P3 7
#define MIX_MAX 8
#define MIX_FULL 9
#define MIX_CYC1 10
#define MIX_CYC2 11
#define MIX_CYC3 12
uint8_t srcRaw; //
int8_t weight;
int8_t swtch;
uint8_t curve; // 0=symmetrisch, 1=no neg, 2=no pos
uint8_t delayUp:4;
uint8_t delayDown:4;
uint8_t speedUp:4; // Servogeschwindigkeit aus Tabelle (10ms Cycle)
uint8_t speedDown:4; // 0 nichts
uint8_t carryTrim:1;
#define MLTPX_ADD 0
#define MLTPX_MUL 1
#define MLTPX_REP 2
uint8_t mltpx:3; // multiplex method 0=+ 1=* 2=replace
int8_t phase:4; // -5=!FP4, 0=normal, 5=FP4
int8_t sOffset;
} __attribute__((packed)) MixData;
typedef struct t_CustomSwData { // Custom Switches data
int8_t v1; //input
int8_t v2; //offset
uint8_t func;
} __attribute__((packed)) CustomSwData;
typedef struct t_SafetySwData { // Safety Switches data
int8_t swtch;
int8_t val;
} __attribute__((packed)) SafetySwData;
#define FUNC_TRAINER 1
#define FUNC_INSTANT_TRIM 2
#define FUNC_TRIMS_2_OFS 3
#define FUNC_VIEW_TELEMETRY 4
#define FUNC_LAST 4
typedef struct t_FuncSwData { // Function Switches data
int8_t swtch; //input
uint8_t func;
} __attribute__((packed)) FuncSwData;
typedef struct t_FrSkyChannelData {
uint8_t ratio; // 0.0 means not used, 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc.
uint8_t type:4; // channel unit (0=volts, ...)
int8_t offset:4; // calibration offset. Signed 0.1V steps. EG. -4 to substract 0.4V
uint8_t alarms_value[2]; // 0.1V steps EG. 6.6 Volts = 66. 25.1V = 251, etc.
uint8_t alarms_level:4;
uint8_t alarms_greater:2; // 0=LT(<), 1=GT(>)
uint8_t spare:2;
int8_t barMin; // minimum for bar display
uint8_t barMax; // ditto for max display (would usually = ratio)
} __attribute__((packed)) FrSkyChannelData;
typedef struct t_FrSkyData {
FrSkyChannelData channels[2];
} __attribute__((packed)) FrSkyData;
typedef struct t_SwashRingData { // Swash Ring data
uint8_t invertELE:1;
uint8_t invertAIL:1;
uint8_t invertCOL:1;
uint8_t type:5;
uint8_t collectiveSource;
uint8_t value;
/* TODO BSS everything is in comments in menus.cpp, how should it be used?
uint8_t lim; // 0 mean off 100 full deflection
uint8_t chX; // 2 channels to limit
uint8_t chY; // 2 channels to limit */
} __attribute__((packed)) SwashRingData;
typedef struct t_PhaseData {
#define TRIM_MAX 125
#define TRIM_MIN (-TRIM_MAX)
int8_t trim[4]; // -125..125 => trim value, 127 => use trim of phase 0, -128, -127, -126 => use trim of phases 1|2|3|4 instead
int8_t swtch; // swtch of phase[0] is not used
char name[6];
uint8_t fadeIn:4;
uint8_t fadeOut:4;
} __attribute__((packed)) PhaseData;
#define MAX_MODELS 16
#define MAX_PHASES 5
#define MAX_MIXERS 32
#define MAX_EXPOS 14
#define MAX_CURVE5 8
#define MAX_CURVE9 8
#define NUM_CHNOUT 16 // number of real output channels CH1-CH16
#define NUM_CSW 12 // number of custom switches
#define NUM_FSW 12 // number of functions assigned to switches
typedef struct t_TimerData {
int8_t mode; // timer trigger source -> off, abs, stk, stk%, sw/!sw, !m_sw/!m_sw
uint16_t val:14;
uint8_t persistent:1;
uint8_t dir:1; // 0=>Count Down, 1=>Count Up
} __attribute__((packed)) TimerData;
typedef struct t_ModelData {
char name[10]; // 10 must be first for eeLoadModelName
TimerData timer1; // TODO timers array
uint8_t protocol:3;
int8_t ppmNCH:3;
uint8_t thrTrim:1; // Enable Throttle Trim
uint8_t thrExpo:1; // Enable Throttle Expo
uint8_t trimInc:3; // Trim Increments
uint8_t spare1:1;
uint8_t pulsePol:1;
uint8_t extendedLimits:1;
uint8_t extendedTrims:1;
uint8_t spare2:1;
int8_t ppmDelay;
uint8_t beepANACenter; // 1<<0->A1.. 1<<6->A7
TimerData timer2;
MixData mixData[MAX_MIXERS];
LimitData limitData[NUM_CHNOUT];
ExpoData expoData[MAX_EXPOS];
int8_t curves5[MAX_CURVE5][5];
int8_t curves9[MAX_CURVE9][9];
CustomSwData customSw[NUM_CSW];
SafetySwData safetySw[NUM_CHNOUT];
FuncSwData funcSw[NUM_FSW];
SwashRingData swashR;
PhaseData phaseData[MAX_PHASES];
#define SUBTRIM_MAX (1024-TRIM_MAX)
#define SUBTRIM_MIN (-SUBTRIM_MAX)
int16_t subtrim[NUM_STICKS];
FrSkyData frsky;
} __attribute__((packed)) ModelData;
extern EEGeneral g_eeGeneral;
extern ModelData g_model;
#define TOTAL_EEPROM_USAGE (sizeof(ModelData)*MAX_MODELS + sizeof(EEGeneral))
#endif
/*eof*/

411
src/pers.cpp Normal file
View file

@ -0,0 +1,411 @@
/*
* 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.
*
*/
#ifdef TRANSLATIONS
#include "eeprom_v4.h"
#include "eeprom_v3.h"
#endif
#include "gruvin9x.h"
#include "templates.h"
RlcFile theFile; //used for any file operation
#define FILE_TYP_GENERAL 1
#define FILE_TYP_MODEL 2
void generalDefault()
{
memset(&g_eeGeneral,0,sizeof(g_eeGeneral));
g_eeGeneral.myVers = EEPROM_VER;
g_eeGeneral.currModel= 0;
g_eeGeneral.contrast = 25;
g_eeGeneral.vBatWarn = 90;
#ifdef DEFAULTMODE1
g_eeGeneral.stickMode= 0; // default to mode 1
#else
g_eeGeneral.stickMode= 2; // default to mode 2
#endif
for (int i = 0; i < 7; ++i) {
g_eeGeneral.calibMid[i] = 0x200;
g_eeGeneral.calibSpanNeg[i] = 0x180;
g_eeGeneral.calibSpanPos[i] = 0x180;
}
int16_t sum=0;
for(int i=0; i<12;i++) sum+=g_eeGeneral.calibMid[i];
g_eeGeneral.chkSum = sum;
}
#ifdef TRANSLATIONS
uint8_t Translate()
{
if (g_eeGeneral.myVers == EEPROM_VER_r584 || g_eeGeneral.myVers == EEPROM_ER9X_VER) {
alert(g_eeGeneral.myVers == EEPROM_VER_r584 ? PSTR("EEprom Data v3") : PSTR("EEprom Data Er9x v4"), true);
message(PSTR("EEPROM Converting"));
theFile.readRlc1((uint8_t*)&g_eeGeneral, sizeof(g_eeGeneral));
memset(&g_eeGeneral.frskyRssiAlarms, 0 , sizeof(g_eeGeneral.frskyRssiAlarms));
if (g_eeGeneral.myVers == EEPROM_VER_r584) {
// previous version had only 6 custom switches, OFF and ON values have to be shifted 6
if (g_eeGeneral.lightSw == MAX_SWITCH-6)
g_eeGeneral.lightSw += 6;
if (g_eeGeneral.lightSw == -MAX_SWITCH+6)
g_eeGeneral.lightSw -= 6;
}
g_eeGeneral.view = 0; // will not translate the view index
EEPROM_V3::EEGeneral *old = (EEPROM_V3::EEGeneral *)&g_eeGeneral;
g_eeGeneral.disableMemoryWarning = old->disableMemoryWarning;
g_eeGeneral.switchWarning = old->disableSwitchWarning ? 0 : -1;
for (uint8_t i=0; i<4; i++) {
g_eeGeneral.trainer.mix[i].srcChn = old->trainer.mix[i].srcChn;
g_eeGeneral.trainer.mix[i].mode = old->trainer.mix[i].mode;
g_eeGeneral.trainer.mix[i].studWeight = old->trainer.mix[i].studWeight * 13 / 4;
}
for (uint8_t id=0; id<MAX_MODELS; id++) {
theFile.openRlc(FILE_MODEL(id));
uint16_t sz = theFile.readRlc1((uint8_t*)&g_model, sizeof(EEPROM_V4::ModelData));
if(sz > 0) {
EEPROM_V4::ModelData *v4 = (EEPROM_V4::ModelData *)&g_model;
EEPROM_V3::ModelData *v3 = (EEPROM_V3::ModelData *)&g_model;
SwashRingData swashR;
swashR.invertELE = v4->swashInvertELE;
swashR.invertAIL = v4->swashInvertAIL;
swashR.invertCOL = v4->swashInvertCOL;
swashR.type = v4->swashType;
swashR.collectiveSource = v4->swashCollectiveSource;
swashR.value = v4->swashRingValue;
int8_t trims[4];
memcpy(&trims[0], &v3->trim[0], 4);
int8_t trimSw = v3->trimSw;
for (uint8_t i=0; i<10; i++)
g_model.name[i] = char2idx(g_model.name[i]);
g_model.timer1.mode = v3->tmrMode;
g_model.timer1.val = v3->tmrVal;
g_model.timer1.persistent = 0;
g_model.timer1.dir = v3->tmrDir;
g_model.protocol = v3->protocol;
g_model.ppmNCH = v3->ppmNCH;
g_model.thrTrim = v3->thrTrim;
g_model.thrExpo = v3->thrExpo;
g_model.trimInc = v3->trimInc;
g_model.spare1 = 0;
g_model.pulsePol = v3->pulsePol;
if (g_eeGeneral.myVers == EEPROM_ER9X_VER) {
g_model.extendedLimits = v4->extendedLimits;
}
else {
g_model.extendedLimits = 0;
}
g_model.extendedTrims = 0;
g_model.spare2 = 0;
g_model.ppmDelay = v3->ppmDelay;
g_model.beepANACenter = v3->beepANACenter;
g_model.timer2.mode = 0;
g_model.timer2.val = 0;
g_model.timer2.persistent = 0;
g_model.timer2.dir = 0;
for (uint8_t i=0; i<MAX_MIXERS; i++) {
memmove(&g_model.mixData[i], &v3->mixData[i], sizeof(MixData)); // MixData size changed!
g_model.mixData[i].mixWarn = g_model.mixData[i].phase;
g_model.mixData[i].phase = 0;
}
assert((char *)&g_model.limitData[0] < (char *)&v3->limitData[0]);
memmove(&g_model.limitData[0], &v3->limitData[0], sizeof(LimitData)*NUM_CHNOUT);
assert((char *)&g_model.expoData[0] < (char *)v3->expoData);
EEPROM_V4::ExpoData expo4[4];
memcpy(&expo4[0], &v4->expoData[0], sizeof(expo4));
memset(&g_model.expoData[0], 0, sizeof(expo4));
uint8_t e = 0;
for (uint8_t ch=0; ch<4 && e<MAX_EXPOS; ch++) {
for (int8_t dr=2; dr>=0 && e<MAX_EXPOS; dr--) {
if ((dr==2 && !expo4[ch].drSw1) ||
(dr==1 && !expo4[ch].drSw2) ||
(dr==0 && !expo4[ch].expo[0][0][0] && !expo4[ch].expo[0][0][1] && !expo4[ch].expo[0][1][0] && !expo4[ch].expo[2][1][1])) continue;
g_model.expoData[e].swtch = (dr == 0 ? expo4[ch].drSw1 : (dr == 1 ? expo4[ch].drSw2 : 0));
g_model.expoData[e].chn = ch;
g_model.expoData[e].expo = expo4[ch].expo[dr][0][0];
g_model.expoData[e].weight = 100 + expo4[ch].expo[dr][1][0];
if (expo4[ch].expo[dr][0][0] == expo4[ch].expo[dr][0][1] && expo4[ch].expo[dr][1][0] == expo4[ch].expo[dr][1][1]) {
g_model.expoData[e++].mode = 3;
}
else {
g_model.expoData[e].mode = 1;
if (e < MAX_EXPOS-1) {
g_model.expoData[e+1].swtch = g_model.expoData[e].swtch;
g_model.expoData[++e].chn = ch;
g_model.expoData[e].mode = 2;
g_model.expoData[e].expo = expo4[ch].expo[dr][0][1];
g_model.expoData[e++].weight = 100 + expo4[ch].expo[dr][1][1];
}
}
}
}
assert((char *)&g_model.curves5[0][0] < (char *)&v3->curves5[0][0]);
memmove(&g_model.curves5[0][0], &v3->curves5[0][0], 5*MAX_CURVE5);
assert((char *)&g_model.curves9[0][0] < (char *)&v3->curves9[0][0]);
memmove(&g_model.curves9[0][0], &v3->curves9[0][0], 9*MAX_CURVE9);
if (g_eeGeneral.myVers == EEPROM_VER_r584) {
memmove(&g_model.customSw[0], &v3->customSw[0], sizeof(CustomSwData)*6);
memset(&g_model.customSw[6], 0, sizeof(CustomSwData)*6);
memset(&g_model.safetySw[0], 0, sizeof(SafetySwData)*NUM_CHNOUT + sizeof(SwashRingData) + sizeof(FrSkyData));
}
else {
assert((char *)&g_model.customSw[0] < (char *)&v4->customSw[0]);
memmove(&g_model.customSw[0], &v4->customSw[0], sizeof(CustomSwData)*12);
assert((char *)&g_model.safetySw[0] < (char *)&v4->safetySw[0]);
memmove(&g_model.safetySw[0], &v4->safetySw[0], sizeof(SafetySwData)*NUM_CHNOUT);
memcpy(&g_model.swashR, &swashR, sizeof(SwashRingData));
for (uint8_t i=0; i<2; i++) {
// TODO this conversion is bad
// assert(&g_model.frsky.channels[i].ratio < &v4->frsky.channels[i].ratio);
g_model.frsky.channels[i].ratio = v4->frsky.channels[i].ratio;
g_model.frsky.channels[i].type = v4->frsky.channels[i].type;
g_model.frsky.channels[i].offset = 0;
g_model.frsky.channels[i].alarms_value[0] = v4->frsky.channels[i].alarms_value[0];
g_model.frsky.channels[i].alarms_value[1] = v4->frsky.channels[i].alarms_value[1];
g_model.frsky.channels[i].alarms_level = v4->frsky.channels[i].alarms_level;
g_model.frsky.channels[i].alarms_greater = v4->frsky.channels[i].alarms_greater;
g_model.frsky.channels[i].barMin = 0;
g_model.frsky.channels[i].barMax = 0;
}
}
memset(&g_model.phaseData[0], 0, sizeof(g_model.phaseData) + sizeof(g_model.subtrim));
memset(&g_model.funcSw[0], 0, sizeof(g_model.funcSw));
if (trimSw) {
g_model.funcSw[0].swtch = trimSw;
g_model.funcSw[0].func = FUNC_INSTANT_TRIM;
g_model.funcSw[1].swtch = trimSw;
g_model.funcSw[1].func = FUNC_TRIMS_2_OFS;
}
memcpy(&g_model.phaseData[0].trim[0], &trims[0], 4);
theFile.writeRlc(FILE_MODEL(id), FILE_TYP_MODEL, (uint8_t*)&g_model, sizeof(g_model), true);
}
}
g_eeGeneral.myVers = EEPROM_VER;
theFile.writeRlc(FILE_GENERAL, FILE_TYP_GENERAL, (uint8_t*)&g_eeGeneral, sizeof(EEGeneral), true);
return sizeof(EEGeneral);
}
return 0;
}
#endif
bool eeLoadGeneral()
{
theFile.openRlc(FILE_GENERAL);
uint8_t sz = 0;
if (theFile.readRlc((uint8_t*)&g_eeGeneral, 1) == 1) {
theFile.openRlc(FILE_GENERAL);
if (g_eeGeneral.myVers == EEPROM_VER) {
sz = theFile.readRlc((uint8_t*)&g_eeGeneral, sizeof(g_eeGeneral));
}
#ifdef TRANSLATIONS
else {
sz = Translate();
}
#endif
}
if (sz == sizeof(EEGeneral)) {
uint16_t sum=0;
for(int i=0; i<12;i++) sum+=g_eeGeneral.calibMid[i];
return g_eeGeneral.chkSum == sum;
}
return false;
}
void modelDefault(uint8_t id)
{
memset(&g_model, 0, sizeof(g_model));
applyTemplate(0); //default 4 channel template
}
uint16_t eeLoadModelName(uint8_t id, char *name)
{
memset(name, 0, sizeof(g_model.name));
if (id<MAX_MODELS) {
theFile.openRlc(FILE_MODEL(id));
if (theFile.readRlc((uint8_t*)name, sizeof(g_model.name)) == sizeof(g_model.name)) {
return theFile.size();
}
}
return 0;
}
bool eeModelExists(uint8_t id)
{
return EFile::exists(FILE_MODEL(id));
}
void eeLoadModel(uint8_t id)
{
if(id<MAX_MODELS)
{
theFile.openRlc(FILE_MODEL(id));
uint16_t sz = theFile.readRlc((uint8_t*)&g_model, sizeof(g_model));
if (sz != sizeof(ModelData)) {
// alert("Error Loading Model");
modelDefault(id);
}
resetTimer1();
resetTimer2();
#ifdef FRSKY
resetTelemetry();
FRSKY_setModelAlarms();
#endif
}
}
bool eeDuplicateModel(uint8_t id)
{
uint8_t i;
for( i=id+1; i<MAX_MODELS; i++)
{
if(! EFile::exists(FILE_MODEL(i))) break;
}
if(i==MAX_MODELS) return false; // no free space in directory left
EFile theFile2;
theFile2.openRd(FILE_MODEL(id));
#ifdef ASYNC_WRITE
theFile.create(FILE_MODEL(i), FILE_TYP_MODEL, true);
#else
theFile.create(FILE_MODEL(i), FILE_TYP_MODEL, 600);
#endif
uint8_t buf[15];
uint8_t len;
while((len=theFile2.read(buf, 15)))
{
theFile.write(buf, len);
wdt_reset(); // TODO I don't know what it is
if (errno() != 0) {
return false;
}
}
theFile.close();
return true;
}
void eeReadAll()
{
if(!EeFsOpen() ||
EeFsck() < 0 ||
!eeLoadGeneral()
)
{
alert(PSTR("Bad EEprom Data"), true);
message(PSTR("EEPROM Formatting"));
EeFsFormat();
//alert(PSTR("format ok"));
generalDefault();
//alert(PSTR("default ok"));
#ifdef ASYNC_WRITE
theFile.writeRlc(FILE_GENERAL, FILE_TYP_GENERAL,(uint8_t*)&g_eeGeneral,sizeof(EEGeneral), true);
#else
uint16_t sz = theFile.writeRlc(FILE_GENERAL,FILE_TYP_GENERAL,(uint8_t*)&g_eeGeneral,sizeof(EEGeneral), 200);
if(sz!=sizeof(EEGeneral)) alert(PSTR("genwrite error"));
#endif
modelDefault(0);
//alert(PSTR("modef ok"));
#ifdef ASYNC_WRITE
theFile.writeRlc(FILE_MODEL(0), FILE_TYP_MODEL, (uint8_t*)&g_model, sizeof(g_model), true);
#else
theFile.writeRlc(FILE_MODEL(0), FILE_TYP_MODEL, (uint8_t*)&g_model, sizeof(g_model),200);
#endif
//alert(PSTR("modwrite ok"));
}
eeLoadModel(g_eeGeneral.currModel);
}
uint8_t s_eeDirtyMsk;
#ifndef ASYNC_WRITE
static uint16_t s_eeDirtyTime10ms;
#define WRITE_DELAY_10MS 100
#endif
void eeDirty(uint8_t msk)
{
s_eeDirtyMsk |= msk;
#ifndef ASYNC_WRITE
if (msk)
s_eeDirtyTime10ms = get_tmr10ms();
#endif
}
void eeCheck(bool immediately)
{
#ifdef ASYNC_WRITE
if (immediately) {
theFile.flush();
}
if (s_eeDirtyMsk & EE_GENERAL) {
s_eeDirtyMsk -= EE_GENERAL;
theFile.writeRlc(FILE_GENERAL, FILE_TYP_GENERAL, (uint8_t*)&g_eeGeneral, sizeof(EEGeneral), immediately);
if (!immediately) return;
}
if (s_eeDirtyMsk & EE_MODEL) {
s_eeDirtyMsk = 0;
theFile.writeRlc(FILE_MODEL(g_eeGeneral.currModel), FILE_TYP_MODEL, (uint8_t*)&g_model, sizeof(g_model), immediately);
}
#else
uint8_t msk = s_eeDirtyMsk;
if (!msk) return;
if( !immediately && ((get_tmr10ms() - s_eeDirtyTime10ms) < WRITE_DELAY_10MS)) return;
s_eeDirtyMsk = 0;
if(msk & EE_GENERAL){
if(theFile.writeRlc(FILE_TMP, FILE_TYP_GENERAL, (uint8_t*)&g_eeGeneral,
sizeof(EEGeneral),20) == sizeof(EEGeneral))
{
EFile::swap(FILE_GENERAL,FILE_TMP);
}else{
if(errno()==ERR_TMO){
s_eeDirtyMsk |= EE_GENERAL; //try again
s_eeDirtyTime10ms = get_tmr10ms() - WRITE_DELAY_10MS;
}else{
alert(PSTR("EEPROM overflow"));
}
}
//first finish GENERAL, then MODEL !!avoid Toggle effect
}
if(msk & EE_MODEL){
if(theFile.writeRlc(FILE_TMP, FILE_TYP_MODEL, (uint8_t*)&g_model,
sizeof(g_model),20) == sizeof(g_model))
{
EFile::swap(FILE_MODEL(g_eeGeneral.currModel),FILE_TMP);
}else{
if(errno()==ERR_TMO){
s_eeDirtyMsk |= EE_MODEL; //try again
s_eeDirtyTime10ms = get_tmr10ms() - WRITE_DELAY_10MS;
}else{
alert(PSTR("EEPROM overflow"));
}
}
}
//beepWarn1();
#endif
}

259
src/rtc.cpp Normal file
View file

@ -0,0 +1,259 @@
/*--------------------------------------------------------------------------*/
/* RTC controls */
#include <avr/io.h>
#include <string.h>
#include "rtc.h"
#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 */
/*-------------------------------------------------*/
/* I2C bus protocol */
static
void iic_delay (void)
{
int n;
for (n = 4; n; n--) PINB;
}
/* Generate start condition on the IIC bus */
static
void iic_start (void)
{
SDA_HIGH();
iic_delay();
SCL_HIGH();
iic_delay();
SDA_LOW();
iic_delay();
SCL_LOW();
iic_delay();
}
/* Generate stop condition on the IIC bus */
static
void iic_stop (void)
{
SDA_LOW();
iic_delay();
SCL_HIGH();
iic_delay();
SDA_HIGH();
iic_delay();
}
/* Send a byte to the IIC bus */
static
int iic_send (BYTE dat)
{
BYTE b = 0x80;
int ack;
do {
if (dat & b) { /* SDA = Z/L */
SDA_HIGH();
} else {
SDA_LOW();
}
iic_delay();
SCL_HIGH();
iic_delay();
SCL_LOW();
iic_delay();
} while (b >>= 1);
SDA_HIGH();
iic_delay();
SCL_HIGH();
ack = SDA_VAL ? 0 : 1; /* Sample ACK */
iic_delay();
SCL_LOW();
iic_delay();
return ack;
}
/* Receive a byte from the IIC bus */
static
BYTE iic_rcvr (int ack)
{
UINT d = 1;
do {
d <<= 1;
SCL_HIGH();
if (SDA_VAL) d++;
iic_delay();
SCL_LOW();
iic_delay();
} while (d < 0x100);
if (ack) { /* SDA = ACK */
SDA_LOW();
} else {
SDA_HIGH();
}
iic_delay();
SCL_HIGH();
iic_delay();
SCL_LOW();
SDA_HIGH();
iic_delay();
return (BYTE)d;
}
/*-------------------------------------------------*/
/* I2C block read/write controls */
int iic_read (
BYTE dev, /* Device address */
UINT adr, /* Read start address */
UINT cnt, /* Read byte count */
BYTE *buff /* Read data buffer */
)
{
BYTE *rbuff = buff;
int n;
if (!cnt) return 0;
n = 10;
do { /* Select device */
iic_start();
} while (!iic_send(dev) && --n);
if (n) {
if (iic_send((BYTE)adr)) { /* Set start address */
iic_start(); /* Reselect device in read mode */
if (iic_send(dev | 1)) {
do { /* Receive data */
cnt--;
*rbuff++ = iic_rcvr(cnt ? 1 : 0);
} while (cnt);
}
}
}
iic_stop(); /* Deselect device */
return cnt ? 0 : 1;
}
int iic_write (
BYTE dev, /* Device address */
UINT adr, /* Write start address */
UINT cnt, /* Write byte count */
const BYTE *buff /* Data to be written */
)
{
const BYTE *wbuff = buff;
int n;
if (!cnt) return 0;
n = 10;
do { /* Select device */
iic_start();
} while (!iic_send(dev) && --n);
if (n) {
if (iic_send((BYTE)adr)) { /* Set start address */
do { /* Send data */
if (!iic_send(*wbuff++)) break;
} while (--cnt);
}
}
iic_stop(); /* Deselect device */
return cnt ? 0 : 1;
}
/*-------------------------------------------------*/
/* RTC functions */
int rtc_gettime (RTC *rtc)
{
BYTE buf[8];
if (!iic_read(0xD0, 0, 7, buf)) return 0;
rtc->sec = (buf[0] & 0x0F) + ((buf[0] >> 4) & 7) * 10;
rtc->min = (buf[1] & 0x0F) + (buf[1] >> 4) * 10;
rtc->hour = (buf[2] & 0x0F) + ((buf[2] >> 4) & 3) * 10;
rtc->wday = (buf[2] & 0x07);
rtc->mday = (buf[4] & 0x0F) + ((buf[4] >> 4) & 3) * 10;
rtc->month = (buf[5] & 0x0F) + ((buf[5] >> 4) & 1) * 10;
rtc->year = 2000 + (buf[6] & 0x0F) + (buf[6] >> 4) * 10;
return 1;
}
int rtc_settime (const RTC *rtc)
{
BYTE buf[8];
buf[0] = rtc->sec / 10 * 16 + rtc->sec % 10;
buf[1] = rtc->min / 10 * 16 + rtc->min % 10;
buf[2] = rtc->hour / 10 * 16 + rtc->hour % 10;
buf[3] = rtc->wday & 7;
buf[4] = rtc->mday / 10 * 16 + rtc->mday % 10;
buf[5] = rtc->month / 10 * 16 + rtc->month % 10;
buf[6] = (rtc->year - 2000) / 10 * 16 + (rtc->year - 2000) % 10;
return iic_write(0xD0, 0, 7, buf);
}
int rtc_init (void)
{
BYTE buf[8]; /* RTC R/W buffer */
UINT adr;
/* Read RTC registers */
if (!iic_read(0xD0, 0, 8, buf)) return 0; /* IIC error */
if (buf[7] & 0x20) { /* When data has been volatiled, set default time */
/* Clear nv-ram. Reg[8..63] */
memset(buf, 0, 8);
for (adr = 8; adr < 64; adr += 8)
iic_write(0x0D, adr, 8, buf);
/* Reset time to Jan 1, '08. Reg[0..7] */
buf[4] = 1; buf[5] = 1; buf[6] = 8;
iic_write(0x0D, 0, 8, buf);
}
return 1;
}

23
src/rtc.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef RTC_H
#define RTC_H
#include "integer.h"
typedef struct {
WORD year; /* 2000..2099 */
BYTE month; /* 1..12 */
BYTE mday; /* 1.. 31 */
BYTE wday; /* 1..7 */
BYTE hour; /* 0..23 */
BYTE min; /* 0..59 */
BYTE sec; /* 0..59 */
} RTC;
int iic_write (BYTE, UINT, UINT, const void*); /* Write to IIC device */
int iic_read (BYTE, UINT, UINT, void*); /* Read from IIC device */
int rtc_init (void); /* Initialize RTC */
int rtc_gettime (RTC*); /* Get time */
int rtc_settime (const RTC*); /* Set time */
#endif

11
src/s9xsplash.lbm Normal file
View file

@ -0,0 +1,11 @@
prog_uchar APM s9xsplash[] = {
128,64,1024,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x08,0x04,0x04,0x84,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0xc4,0x04,0x04,0x88,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xbf,0x80,0x80,0x80,0xbf,0x80,0x80,0x80,0x80,0x00,0x00,0x30,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x88,0x09,0x09,0x08,0xf0,0x00,0x00,0x00,0x01,0x01,0x01,0x7f,0x01,0x01,0x01,0x01,0x00,0x00,0x7f,0x49,0x49,0x49,0x49,0x41,0x41,0x40,0x00,0x7f,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x7f,0x49,0x49,0x49,0x49,0x41,0x41,0x40,0x00,0x7f,0x01,0x06,0x0c,0x10,0x10,0x0c,0x06,0x01,0x7f,0x00,0x00,0x7f,0x49,0x49,0x49,0x49,0x41,0x41,0x40,0x01,0x01,0x01,0x7f,0x01,0x01,0x01,0x01,0x00,0x00,0x7f,0x09,0x09,0x09,0x09,0x19,0x29,0x4e,0x00,0x01,0x03,0x06,0x0c,0x78,0x0c,0x06,0x03,0x01,0x00,0x00,
0x00,0x00,0xf8,0xfe,0xff,0xff,0xff,0x0f,0x07,0x07,0x07,0x07,0x07,0x07,0x37,0x47,0x87,0x87,0x87,0x97,0x97,0x07,0x5f,0xdf,0xdf,0xdf,0xde,0xd8,0x80,0x10,0x10,0x10,0x10,0x10,0x0f,0x00,0x80,0x40,0x3f,0x80,0xc0,0xe0,0xe0,0xe0,0x60,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xd0,0x90,0x90,0x90,0x90,0x90,0x00,0x00,0x00,0xf0,0x90,0x90,0x90,0x90,0x10,0x10,0x00,0x00,0xf0,0x90,0x90,0x90,0x90,0x90,0x90,0xe0,0x00,0x00,0xf0,0x00,0x00,0x00,0xf0,0x90,0x90,0x90,0x90,0x10,0x10,0x00,0x60,0xd0,0x90,0x90,0x90,0x90,0x90,0x00,0x00,0x00,0x00,
0x00,0x00,0x01,0x07,0x07,0x0f,0x0f,0x0f,0x0e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x0e,0x0e,0x0e,0x0e,0x0e,0x8e,0xce,0xee,0xfc,0xf9,0x73,0x27,0x0f,0x1f,0x3f,0x7e,0xfc,0xf9,0xf1,0xf1,0xf9,0xfc,0x3e,0x1f,0x0f,0x07,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x06,0x04,0x04,0x04,0x04,0x04,0x03,0x00,0x00,0x07,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00,0x07,0x00,0x00,0x00,0x00,0x01,0x02,0x04,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x02,0x06,0x04,0x04,0x04,0x04,0x04,0x03,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x0c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1e,0x1e,0x1e,0x0e,0x0e,0x0f,0x07,0x07,0x03,0x03,0x01,0x80,0xc0,0xe0,0xf0,0xf8,0x7c,0x3e,0x1f,0x0f,0x07,0x07,0x0f,0x1f,0x3f,0x7e,0xfc,0xf8,0xf0,0xe0,0xc0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x4a,0x4a,0x4a,0x4a,0x4a,0x7c,0x00,0x02,0x06,0x0c,0x70,0x08,0x04,0x02,0x02,0x00,0x00,0x00,0x00,0x1c,0x22,0x41,0x41,0x49,0x49,0x49,0x3a,0x18,0x00,0x00,0x7e,0x12,0x12,0x12,0x12,0x32,0x4c,0x00,0x00,0x3e,0x40,0x40,0x40,0x40,0x40,0x3e,0x00,0x02,0x0c,0x18,0x60,0x60,0x30,0x0c,0x02,0x00,0x7e,0x00,0x00,0x7e,0x02,0x0c,0x18,0x30,0x40,0x7e,0x00,0x00,0x00,0x00,
0x00,0x00,0xe0,0x10,0x10,0x10,0xe0,0x00,0x00,0xc0,0x80,0x40,0x80,0x00,0x00,0x80,0x40,0x40,0x80,0x00,0x00,0xcc,0x8e,0x4f,0x8f,0x0f,0x07,0x03,0x01,0x00,0x00,0x00,0x60,0x90,0x90,0x10,0x00,0x00,0x00,0x80,0x41,0x43,0x87,0x0f,0x0f,0xcf,0x0e,0x0c,0xc0,0x00,0x00,0xc0,0x80,0x40,0xc0,0x00,0x00,0x80,0x40,0x40,0x40,0x00,0x00,0x80,0x40,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x07,0x08,0x08,0x08,0x07,0x00,0x00,0x3f,0x04,0x08,0x07,0x00,0x00,0x07,0x09,0xc9,0x49,0x40,0x40,0x8f,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x08,0x09,0x06,0x00,0x00,0x07,0x08,0x08,0x07,0x00,0x00,0x07,0x08,0x04,0x0f,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x40,0x07,0x08,0x08,0x08,0x00,0x00,0x07,0x09,0x09,0x09,0x00,0x00,0x00,0xc8,0x00,0x00,0x08,0xc0,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x3f,0x04,0x04,0x04,0x03,0x00,0x00,0x1e,0x21,0x21,0x1e,0x00,0x07,0x38,0x0e,0x0e,0x38,0x07,0x00,0x1e,0x25,0x25,0x26,0x00,0x00,0x3f,0x02,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x3f,0x00,0x00,0x3f,0x02,0x01,0x3e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x3c,0x03,0x00,0x00,0x00,0x1e,0x21,0x21,0x1e,0x00,0x00,0x1f,0x20,0x10,0x3f,0x00,0x3f,0x02,0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x02,0x02,0x02,0x3f,0x00,0x00,0x19,0x25,0x15,0x1e,0x20,0x00,0x3f,0x02,0x01,0x3e,0x00,0x1e,0x21,0x12,0x3f,0x00,0x00,0x22,0x25,0x29,0x11,0x00
};

89
src/s9xsplash.xbm Normal file
View file

@ -0,0 +1,89 @@
#define s9xsplash_width 128
#define s9xsplash_height 64
static unsigned char s9xsplash_bits[] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,
0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x80,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,
0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x40,0xf8,0xff,0x47,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x40,0x04,0x00,0x24,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x04,0x00,
0x18,0xfc,0xf3,0x27,0xe0,0xcf,0xc0,0xfc,0xfd,0xf3,0x67,0x30,
0x00,0x40,0x04,0x00,0x00,0x20,0x10,0x20,0x20,0x40,0xa1,0x04,
0x20,0x10,0xc8,0x18,0x00,0x40,0x04,0x00,0x00,0x20,0x10,0x20,
0x20,0x40,0xb3,0x04,0x20,0x10,0x88,0x0d,0x00,0x40,0x04,0xfc,
0x3f,0x20,0xf0,0x21,0xe0,0x43,0x92,0x7c,0x20,0xf0,0x0f,0x07,
0x00,0x40,0x04,0x02,0x40,0x20,0x10,0x20,0x20,0x40,0x8c,0x04,
0x20,0x10,0x02,0x02,0x00,0x40,0x04,0x02,0x40,0x20,0x10,0x20,
0x20,0x40,0x80,0x04,0x20,0x10,0x04,0x02,0x00,0x00,0x00,0xfc,
0x43,0x20,0xf0,0xef,0xef,0x5f,0x80,0xfc,0x23,0x10,0x08,0x02,
0x80,0xff,0x7f,0x00,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xf0,0xff,0xff,0x03,0x44,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0xff,0xff,0x07,
0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xf8,0xff,0xff,0x07,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xfc,0x00,0xc0,0x0f,0x44,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7c,0x40,0xd8,0xef,
0x43,0x00,0x00,0x00,0x00,0xe0,0xc7,0x9f,0x3f,0xe2,0xcf,0x0f,
0x7c,0x40,0x00,0x00,0x40,0x3e,0x00,0x00,0x00,0x10,0x40,0x80,
0x40,0x22,0x20,0x00,0x7c,0x80,0xc0,0x0f,0x20,0x1f,0x00,0x00,
0x00,0x30,0x40,0x80,0x40,0x22,0x60,0x00,0x7c,0x00,0x9f,0x1f,
0x90,0x0f,0x00,0x00,0x00,0xe0,0xc7,0x87,0x7f,0xe2,0xc3,0x0f,
0xfc,0x00,0x00,0x3f,0xcf,0x07,0x00,0x00,0x00,0x00,0x48,0x80,
0x10,0x22,0x00,0x10,0xf8,0xff,0x7f,0x7e,0xe0,0x03,0x00,0x00,
0x00,0x30,0x48,0x80,0x20,0x22,0x60,0x10,0xf8,0xff,0xff,0xfc,
0xf0,0x01,0x00,0x00,0x00,0xe0,0xc7,0xbf,0x40,0xe2,0xdf,0x0f,
0xe0,0xff,0xff,0xf9,0xf9,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x7e,0x80,0xf3,0x7f,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xe7,
0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xe0,0xc3,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x81,0x1f,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfc,0x80,
0x3f,0x00,0x00,0x00,0x00,0x80,0x0f,0x00,0x00,0x00,0x00,0x00,
0x00,0xe0,0x7f,0xc0,0x7f,0x00,0x00,0x7e,0x86,0x41,0x10,0x3f,
0x82,0x02,0x65,0x08,0xc0,0xff,0x1f,0xe0,0xff,0x00,0x00,0x82,
0x4c,0x20,0x00,0x41,0x82,0x84,0xa4,0x08,0x80,0xff,0x07,0xf0,
0xf9,0x01,0x00,0xfe,0x28,0x20,0x3e,0x41,0x82,0x8c,0xa4,0x09,
0x00,0xff,0x00,0xf8,0xf0,0x03,0x00,0x82,0x10,0x20,0x30,0x3f,
0x82,0x48,0x24,0x0b,0x00,0x00,0x00,0x7c,0xe0,0x07,0x00,0x82,
0x10,0x40,0x10,0x21,0x82,0x70,0x24,0x0a,0x00,0x00,0x00,0x3e,
0xc0,0x0f,0x00,0xfe,0x10,0x80,0x0f,0x41,0x7c,0x30,0x24,0x0c,
0x00,0x00,0x00,0x1f,0x80,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x1f,0x00,0x3f,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x0f,
0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xe0,0x07,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x03,0x00,0xf8,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x00,
0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x44,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x44,0x0a,0xa3,0x00,0x01,0x23,0x69,0x1c,
0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x96,0x64,0x01,
0x86,0x24,0x59,0x82,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x44,0x92,0x27,0x01,0x88,0x24,0x09,0x82,0x07,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x44,0x92,0x20,0x01,0x90,0x24,0x09,0x82,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x44,0x96,0x20,0x01,
0x90,0xa4,0x09,0x82,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x38,0x0a,0x27,0x01,0x0f,0x43,0x09,0x1c,0x47,0x12,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x01,0x40,0x04,0x00,0x00,
0x88,0x00,0x00,0x01,0x00,0x00,0x22,0x00,0x00,0x00,0x00,0x00,
0x40,0x04,0x00,0x00,0x88,0x00,0x00,0x01,0x00,0x00,0x22,0x26,
0x64,0x34,0xc0,0x29,0x80,0xc2,0x48,0x0d,0x88,0x1c,0x45,0x71,
0x00,0x00,0x22,0xa9,0x95,0x2c,0x00,0x59,0x80,0x22,0x49,0x0b,
0xf8,0x20,0xab,0x09,0x00,0x00,0x1e,0xa9,0xf5,0x04,0x00,0x49,
0x00,0x21,0x49,0x01,0x88,0x38,0x29,0x11,0x00,0x00,0x02,0xc9,
0x13,0x04,0x00,0x49,0x00,0x21,0x49,0x01,0x88,0x24,0x29,0x21,
0x00,0x00,0x02,0x49,0x12,0x04,0x00,0x49,0x00,0x21,0x69,0x01,
0x88,0x34,0xa9,0x41,0x80,0x24,0x02,0x46,0xe2,0x04,0x00,0x49,
0x00,0xc1,0x50,0x01,0x88,0x48,0x49,0x39,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00};

BIN
src/s9xsplash.xcf Normal file

Binary file not shown.

104
src/simpgmspace.cpp Normal file
View file

@ -0,0 +1,104 @@
/*
* 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.
*
*/
#include <unistd.h>
#include <time.h>
#include <ctype.h>
#include "simpgmspace.h"
#include "lcd.h"
#include "gruvin9x.h"
#include "menus.h"
volatile unsigned char pinb=0, pinc=0xff, pind, pine=0xff, ping=0xff;
unsigned char portb, dummyport;
const char *eepromFile = "eeprom.bin";
volatile size_t eeprom_buffer_size = 0;
uint8_t eeprom[EESIZE];
void eeWriteBlockCmp(const void *i_pointer_ram, void *pointer_eeprom, size_t size)
{
#if 0
printf(" eeWriteBlockCmp(%d %d)", size, (int)pointer_eeprom);
for(uint8_t i=0; i<size; i++)
printf(" %02X", ((const char*)i_pointer_ram)[i]);
printf("\n");fflush(stdout);
#endif
if (eepromFile) {
FILE *fp = fopen(eepromFile, "r+");
long ofs = (long) pointer_eeprom;
const char* pointer_ram= (const char*)i_pointer_ram;
//printf("eeWr p=%10p blk%3d ofs=%2d l=%d",pointer_ram,
// (int)pointer_eeprom/16,
// (int)pointer_eeprom%16,
// (int)size);
while(size) {
if(fseek(fp, ofs , SEEK_SET)==-1) perror("error in seek");
char buf[1];
if (fread(buf, 1, 1, fp) != 1) perror("error in read");
if (buf[0] != pointer_ram[0]){
//printf("X");
g_tmr10ms++;
if(fseek(fp, ofs , SEEK_SET)==-1) perror("error in seek");
fwrite(pointer_ram, 1, 1,fp);
}
else{
//printf(".");
}
size--;
ofs++;
(const char*)pointer_ram++;
}
fclose(fp);
}
else {
memcpy(&eeprom[(int64_t)pointer_eeprom], i_pointer_ram, size);
}
}
void eeprom_read_block (void *pointer_ram,
const void *pointer_eeprom,
size_t size)
{
if (eepromFile) {
FILE *fp=fopen(eepromFile, "r");
if(fseek(fp, (long) pointer_eeprom, SEEK_SET)==-1) perror("error in seek");
if (fread(pointer_ram, size, 1, fp) <= 0) perror("error in read");
fclose(fp);
}
else {
memcpy(pointer_ram, &eeprom[(int)pointer_eeprom], size);
}
}
#if 0
static void EeFsDump(){
for(int i=0; i<EESIZE; i++)
{
printf("%02x ",eeprom[i]);
if(i%16 == 15) puts("");
}
puts("");
}
#endif

85
src/simpgmspace.h Normal file
View file

@ -0,0 +1,85 @@
/*
* 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.
*
*/
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
typedef unsigned char prog_uchar;
typedef const char prog_char;
typedef const uint16_t prog_uint16_t;
typedef const uint8_t prog_uint8_t;
typedef const int16_t prog_int16_t;
typedef const int8_t prog_int8_t;
#define PROGMEM
#define pgm_read_byte(address_short) (*(uint8_t*)(address_short))
#define pgm_read_word(address_short) (*(uint16_t*)(address_short))
#define pgm_read_adr(address_short) *address_short
#define pgm_read_stringP(adr) ((adr))
#define PSTR(adr) adr
#define _delay_us(a)
#define cli()
#define sei()
#define strcpy_P strcpy
#define memcpy_P memcpy
#define PORTA dummyport
#define PORTB portb
#define PORTC dummyport
#define PORTD dummyport
#define PORTE dummyport
#define PORTF dummyport
#define PORTG dummyport
#define DDRA dummyport
#define DDRB dummyport
#define DDRC dummyport
#define DDRD dummyport
#define DDRE dummyport
#define DDRF dummyport
#define DDRG dummyport
#define PINB ~pinb
#define PINC ~pinc
#define PIND ~pind
#define PINE ~pine
#define PING ~ping
#define EEMEM
#define UCSR0B dummyport
#define UDRIE0 dummyport
#define TXEN0 dummyport
#define RXEN0 dummyport
#define DDE0 dummyport
#define PORTE0 dummyport
#define RXCIE0 dummyport
extern volatile unsigned char pinb,pinc,pind,pine,ping;
extern unsigned char portb,dummyport;
extern const char *eepromFile;
void eeprom_read_block (void *pointer_ram,
const void *pointer_eeprom,
size_t size);
#define offsetof(st, m) ((size_t) ( (char *)&((st *)(0))->m - (char *)0 ))
#define wdt_reset()

442
src/simu.cpp Normal file
View file

@ -0,0 +1,442 @@
/*
* 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.
*
*/
#include "fx.h"
#include "FXExpression.h"
#include "FXPNGImage.h"
#include <unistd.h>
#include "simpgmspace.h"
#include "lcd.h"
#include "fxkeys.h"
#include "gruvin9x.h"
#include "menus.h"
#include <time.h>
#include <ctype.h>
#define W DISPLAY_W
#define H DISPLAY_H
#define W2 W*2
#define H2 H*2
int g_snapshot_idx = 0;
class Gruvin9xSim: public FXMainWindow
{
FXDECLARE(Gruvin9xSim)
public:
Gruvin9xSim(){};
Gruvin9xSim(FXApp* a);
long onKeypress(FXObject*,FXSelector,void*);
long onArrowPress(FXObject*,FXSelector,void*);
long onChore(FXObject*,FXSelector,void*);
long onTimeout(FXObject*,FXSelector,void*);
void makeSnapshot(const FXDrawable* drawable);
void doEvents();
void refreshDiplay();
private:
FX::FXuchar buf2[W2*H2/8];
FXBitmap *bmp;
FXBitmapFrame *bmf;
bool firstTime;
public:
FXSlider *sliders[8];
FXKnob *knobs[8];
FXKnob *knobsppm[8];
FXArrowButton *arrow[3];
FXArrowButton *arrow2[3];
FXToggleButton *togButPpm;
};
// Message Map
FXDEFMAP(Gruvin9xSim) Gruvin9xSimMap[]={
//________Message_Type_________ID_____________________Message_Handler_______
FXMAPFUNC(SEL_CHORE, 1, Gruvin9xSim::onChore),
FXMAPFUNC(SEL_TIMEOUT, 2, Gruvin9xSim::onTimeout),
FXMAPFUNC(SEL_COMMAND, 1000, Gruvin9xSim::onArrowPress),
FXMAPFUNC(SEL_KEYPRESS, 0, Gruvin9xSim::onKeypress),
};
FXIMPLEMENT(Gruvin9xSim,FXMainWindow,Gruvin9xSimMap,ARRAYNUMBER(Gruvin9xSimMap))
Gruvin9xSim::Gruvin9xSim(FXApp* a)
:FXMainWindow(a,"Gruvin9xSim",NULL,NULL,DECOR_ALL,20,90,0,0)
{
firstTime=true;
for(int i=0; i<(W*H/8); i++) displayBuf[i]=0;//rand();
for(int i=0; i<(W2*H2/8); i++) buf2[i]=0;//rand();
bmp = new FXBitmap(a,&buf2,BITMAP_KEEP,W2,H2);
FXHorizontalFrame *hf00=new FXHorizontalFrame(this,LAYOUT_CENTER_X);
FXHorizontalFrame *hf01=new FXHorizontalFrame(this,LAYOUT_CENTER_X);
FXHorizontalFrame *hf02=new FXHorizontalFrame(this,LAYOUT_CENTER_X);
//FXHorizontalFrame *hf2=new FXHorizontalFrame(this,LAYOUT_FILL_X);
FXHorizontalFrame *hf10=new FXHorizontalFrame(this,LAYOUT_CENTER_X);
FXHorizontalFrame *hf11=new FXHorizontalFrame(this,LAYOUT_CENTER_X);
FXHorizontalFrame *hf1=new FXHorizontalFrame(this,LAYOUT_FILL_X);
//rh lv rv lh
for(int i=0; i<4; i++){
switch(i)
{
#define L LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT|LAYOUT_FIX_X|LAYOUT_FIX_Y
#undef X0
#define X0 10
#define Y0 20
case 0:
sliders[i]=new FXSlider(hf1,NULL,0,L|SLIDER_HORIZONTAL,X0+0,Y0+120,100,20);
break;
case 1:
sliders[i]=new FXSlider(hf1,NULL,0,L|SLIDER_VERTICAL,X0+100,Y0+20,20,100);
break;
case 2:
sliders[i]=new FXSlider(hf1,NULL,0,L|SLIDER_VERTICAL,X0+120,Y0+20,20,100);
break;
case 3:
sliders[i]=new FXSlider(hf1,NULL,0,L|SLIDER_HORIZONTAL,X0+140,Y0+120,100,20);
break;
default:;
}
sliders[i]->setRange(0+i*50,1023);
sliders[i]->setTickDelta(7);
sliders[i]->setValue(i==1 ? 200 : 512+i*25);
}
arrow[0]= new FXArrowButton(hf10,this,1000,ARROW_LEFT);
arrow[1]= new FXArrowButton(hf10,this,1000,ARROW_UP);
arrow[2]= new FXArrowButton(hf10,this,1000,ARROW_RIGHT);
for(int i=4; i<8; i++){
knobs[i]= new FXKnob(hf11,NULL,0,KNOB_TICKS|LAYOUT_LEFT);
knobs[i]->setRange(0,1023);
knobs[i]->setValue(512);
}
arrow2[0]= new FXArrowButton(hf00,this,1000,ARROW_LEFT);
arrow2[1]= new FXArrowButton(hf00,this,1000,ARROW_UP);
arrow2[2]= new FXArrowButton(hf00,this,1000,ARROW_RIGHT);
togButPpm = new FXToggleButton(hf00,"on", "off", NULL, NULL, NULL, 0, TOGGLEBUTTON_NORMAL);
for(int i=0; i<8; i++){
knobsppm[i]= new FXKnob(i<4?hf01:hf02,NULL,0,KNOB_TICKS|LAYOUT_LEFT);
knobsppm[i]->setRange(1000,2000);
knobsppm[i]->setValue(1500+i*20);
}
bmf = new FXBitmapFrame(this,bmp,0,0,0,0,0,0,0,0,0);
bmf->setOnColor(FXRGB(0,0,0));
//getApp()->addChore(this,1);
getApp()->addTimeout(this,2,100);
}
void Gruvin9xSim::makeSnapshot(const FXDrawable* drawable)
{
// Construct and create an FXImage object
FXPNGImage snapshot(getApp(), NULL, 0, drawable->getWidth(), drawable->getHeight());
snapshot.create();
// Create a window device context and lock it onto the image
FXDCWindow dc(&snapshot);
// Draw from the widget to this
dc.drawArea(drawable, 0, 0, drawable->getWidth(), drawable->getHeight(), 0, 0);
// Release lock
dc.end();
// Grab pixels from server side back to client side
snapshot.restore();
// Save recovered pixels to a file
FXFileStream stream;
char buf[100];
sprintf(buf,"snapshot-%02d.png", ++g_snapshot_idx);
for(unsigned i=4; i<strlen(buf); i++)
{
if(!isalnum(buf[i]) && buf[i]!='.' ) buf[i]='_';
}
if (stream.open(buf, FXStreamSave))
//if (stream.open("snapshot.png", FXStreamSave))
{
snapshot.savePixels(stream);
stream.close();
printf("Snapshot written: %s\n",buf);
}
else {
printf("Cannot create snapshot %s\n", buf);
}
}
void Gruvin9xSim::doEvents()
{
//getApp()->addChore(this,1);
getApp()->runOneEvent(false);
}
long Gruvin9xSim::onArrowPress(FXObject*sender,FXSelector sel,void*v)
{
int which,val;
if(sender==arrow[0]) { which=1; val=0;}
if(sender==arrow[1]) { which=1; val=512;}
if(sender==arrow[2]) { which=1; val=1023;}
if(sender==arrow2[0]){ which=2; val=1000;}
if(sender==arrow2[1]){ which=2; val=1500;}
if(sender==arrow2[2]){ which=2; val=2000;}
if(which == 1){
for(int i=0; i<4; i++) sliders[i]->setValue(val);
for(int i=4; i<7; i++) knobs[i]->setValue(val);
}
if(which == 2){
for(int i=0; i<8; i++) knobsppm[i]->setValue(val);
}
return 0;
}
long Gruvin9xSim::onKeypress(FXObject*,FXSelector,void*v)
{
FXEvent *evt=(FXEvent*)v;
// printf("keypress %x\n", evt->code);
if (evt->code=='s'){
makeSnapshot(bmf);
}
return 0;
}
extern uint16_t s_trainerLast10ms;
long Gruvin9xSim::onTimeout(FXObject*,FXSelector,void*)
{
if(togButPpm->getState()){
for(int i=0; i<8; i++){
g_ppmIns[i]=knobsppm[i]->getValue()-1500;
if(g_ppmIns[i]<-400){
// TODO BSS g_trainerSlaveActiveChns = i;
// TODO BSS s_trainerLast10ms = g_tmr10ms;
break;
}
}
}
per10ms();
getApp()->addChore(this,1);
getApp()->addTimeout(this,2,10);
return 0;
}
void Gruvin9xSim::refreshDiplay()
{
if(portb & 1<<OUT_B_LIGHT) bmf->setOffColor(FXRGB(150,200,152));
else bmf->setOffColor(FXRGB(200,200,200));
for(int x=0;x<W;x++){
for(int y=0;y<H;y++)
{
int o2 = x/4 + y*W*2*2/8;
if( displayBuf[x+(y/8)*W] & (1<<(y%8))) {
buf2[o2] |= 3<<(x%4*2);
buf2[o2+W2/8] |= 3<<(x%4*2);
}
else {
buf2[o2] &= ~(3<<(x%4*2));
buf2[o2+W2/8] &= ~(3<<(x%4*2));
//buf2[x2/8+y2*W2/8] &= ~(3<<(x%8));
}
}
}
bmp->setData (buf2,0);
bmp->render();
bmf->setBitmap( bmp );
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
};
pinb &= ~ 0x7e;
for(unsigned i=0; i<DIM(keys1);i+=2){
if(getApp()->getKeyState(keys1[i])) pinb |= (1<<keys1[i+1]);
}
#ifdef __APPLE__
// gruvin: Can't use Function keys on the Mac -- too many other app conflicts.
// The ordering of these keys, Q/W,E/R,T/Y,U/I matches the on screen
// order of trim sliders
static FXuint keys2[]={KEY_Y, KEY_T, KEY_W, KEY_Q, KEY_I, KEY_U, KEY_E, KEY_R };
#else
static FXuint keys2[]={KEY_F8, KEY_F7, KEY_F4, KEY_F3, KEY_F6, KEY_F5, KEY_F1, KEY_F2 };
#endif
pind = 0;
for(unsigned i=0; i<DIM(keys2);i++){
if(getApp()->getKeyState(keys2[i])) pind |= (1<<i);
}
struct SwitchKey {
FXuint key;
volatile unsigned char& pin;
unsigned char shift;
unsigned char value;
};
static SwitchKey keys3[] = {
#if defined(JETI) || defined(FRSKY)
{ KEY_1, pinc, INP_C_ThrCt, 0 },
{ KEY_6, pinc, INP_C_AileDR, 0 },
#else
{ KEY_1, pine, INP_E_ThrCt, 0 },
{ KEY_6, pine, INP_E_AileDR, 0 },
#endif
{ KEY_2, ping, INP_G_RuddDR, 0 },
{ KEY_3, pine, INP_E_ElevDR, 0 },
//KEY_4, ping, INP_G_ID1, 0,
//KEY_5, pine, INP_E_ID2, 0,
{ KEY_7, pine, INP_E_Gear, 0 },
{ KEY_8, pine, INP_E_Trainer, 0 } };
for(unsigned i=0; i<DIM(keys3); i++){
bool ks = getApp()->getKeyState(keys3[i].key);
if (ks != keys3[i].value) {
if (ks) keys3[i].pin ^= (1<<keys3[i].shift);
keys3[i].value = ks;
}
}
// INP_G_ID1 INP_E_ID2
// id0 0 1
// id1 1 1
// id2 1 0
static FXuint id=0,k4st=0,k5st=0;
bool ks=getApp()->getKeyState(KEY_4);
if(ks != k4st){
if(ks && id>0) id--;
k4st = ks;
}
ks=getApp()->getKeyState(KEY_5);
if(ks != k5st){
if(ks && id<2) id++;
k5st = ks;
}
switch(id){
case 0: ping |= (1<<INP_G_ID1); pine &= ~(1<<INP_E_ID2); break;
case 1: ping &= ~(1<<INP_G_ID1); pine &= ~(1<<INP_E_ID2); break;
case 2: ping &= ~(1<<INP_G_ID1); pine |= (1<<INP_E_ID2); break;
}
}
}
int state = 0;
void *init_function(void *) {
g_menuStack[0] = menuMainView;
g_menuStack[1] = menuProcModelSelect;
eeReadAll(); //load general setup and selected model
checkMem(); //enough eeprom free?
checkTHR();
checkSwitches(); //must be last
state = 2;
return 0;
}
#include <pthread.h>
long Gruvin9xSim::onChore(FXObject*,FXSelector,void*)
{
pthread_t pid;
refreshDiplay();
switch (state) {
case 0:
state = 1;
pthread_create(&pid, NULL, &init_function, NULL);
break;
case 2:
perMain();
break;
}
return 0;
}
Gruvin9xSim *th9xSim;
void doFxEvents()
{
//puts("doFxEvents");
th9xSim->getApp()->runOneEvent(false);
th9xSim->refreshDiplay();
}
int main(int argc,char **argv)
{
if(argc>=2){
eepromFile = argv[1];
}
printf("eeprom = %s\n",eepromFile);
// Each FOX GUI program needs one, and only one, application object.
// The application objects coordinates some common stuff shared between
// all the widgets; for example, it dispatches events, keeps track of
// all the windows, and so on.
// We pass the "name" of the application, and its "vendor", the name
// and vendor are used to search the registry database (which stores
// persistent information e.g. fonts and colors).
FXApp application("Gruvin9xSim", "thus");
// Here we initialize the application. We pass the command line arguments
// because FOX may sometimes need to filter out some of the arguments.
// This opens up the display as well, and reads the registry database
// so that persistent settings are now available.
application.init(argc,argv);
// This creates the main window. We pass in the title to be displayed
// above the window, and possibly some icons for when its iconified.
// The decorations determine stuff like the borders, close buttons,
// drag handles, and so on the Window Manager is supposed to give this
// window.
//FXMainWindow *main=new FXMainWindow(&application,"Hello",NULL,NULL,DECOR_ALL);
th9xSim = new Gruvin9xSim(&application);
application.create();
// Pretty self-explanatory:- this shows the window, and places it in the
// middle of the screen.
#ifndef __APPLE__
th9xSim->show(PLACEMENT_SCREEN);
#else
th9xSim->show(); // Otherwise the main window gets centred across my two monitors, split down the middle.
#endif
return application.run();
}
uint16_t anaIn(uint8_t chan)
{
if(chan<4) return th9xSim->sliders[chan]->getValue();
return th9xSim->knobs[chan]->getValue();
//return 512 - 512*10*chan/100;
//return (rand() & 0x1f) + 0x2f8;
}

6
src/stamp-gruvin9x.h Normal file
View file

@ -0,0 +1,6 @@
//Automatically generated file (Makefile) - do not edit
#define DATE_STR "2011-09-13"
#define TIME_STR "19:07:15"
#define TAG_VERS 2-bsongis
#define SVN_VERS "gruvin9x-dev-r819"
#define BUILD_NUM 827

13
src/stamp.cpp Normal file
View file

@ -0,0 +1,13 @@
#include "gruvin9x.h"
#include "stamp-gruvin9x.h"
#define STR2(s) #s
#define DEFNUMSTR(s) STR2(s)
const char APM stamp1[] = "VERS: V" DEFNUMSTR(VERS) "." DEFNUMSTR(TAG_VERS);
const char APM stamp2[] = "DATE: " DATE_STR;
const char APM stamp3[] = "TIME: " TIME_STR;
const char APM stamp4[] = " SVN: " SVN_VERS;
const char APM stamp5[] = " BLD: " DEFNUMSTR(BUILD_NUM);

105
src/statistics_views.cpp Normal file
View file

@ -0,0 +1,105 @@
/*
* 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.
*
*/
#include "menus.h"
void menuProcStatistic(uint8_t event)
{
TITLE("STAT");
switch(event)
{
case EVT_KEY_FIRST(KEY_UP):
chainMenu(menuProcStatistic2);
break;
case EVT_KEY_FIRST(KEY_DOWN):
case EVT_KEY_FIRST(KEY_EXIT):
chainMenu(menuMainView);
break;
}
lcd_puts_P( 1*FW, FH*1, PSTR("TME"));
putsTime( 5*FW, FH*1, s_timeCumAbs, 0, 0);
lcd_puts_P( 17*FW, FH*1, PSTR("TSW"));
putsTime( 11*FW, FH*1, s_timeCumSw, 0, 0);
lcd_puts_P( 1*FW, FH*2, PSTR("STK"));
putsTime( 5*FW, FH*2, s_timeCumThr, 0, 0);
lcd_puts_P( 17*FW, FH*2, PSTR("ST%"));
putsTime( 11*FW, FH*2, s_timeCum16ThrP/16, 0, 0);
lcd_puts_P( 17*FW, FH*0, PSTR("TOT"));
putsTime( 11*FW, FH*0, s_timeCumTot, 0, 0);
uint16_t traceRd = s_traceCnt>MAXTRACE ? s_traceWr : 0;
uint8_t x=5;
uint8_t y=60;
lcd_hline(x-3,y,120+3+3);
lcd_vline(x,y-32,32+3);
for(uint8_t i=0; i<120; i+=6)
{
lcd_vline(x+i+6,y-1,3);
}
for(uint8_t i=1; i<=120; i++)
{
lcd_vline(x+i,y-s_traceBuf[traceRd],s_traceBuf[traceRd]);
traceRd++;
if(traceRd>=MAXTRACE) traceRd=0;
if(traceRd==s_traceWr) break;
}
}
void menuProcStatistic2(uint8_t event)
{
TITLE("STAT2");
switch(event)
{
case EVT_KEY_FIRST(KEY_MENU):
g_tmr1Latency_min = 0x7ff;
g_tmr1Latency_max = 0;
g_timeMain = 0;
// g_time_per10 = 0;
beepKey();
break;
case EVT_KEY_FIRST(KEY_DOWN):
chainMenu(menuProcStatistic);
break;
case EVT_KEY_FIRST(KEY_UP):
case EVT_KEY_FIRST(KEY_EXIT):
chainMenu(menuMainView);
break;
}
lcd_puts_P( 0*FW, 1*FH, PSTR("tmr1Lat max us"));
lcd_outdez8(15*FW , 1*FH, g_tmr1Latency_max/2 );
lcd_puts_P( 0*FW, 2*FH, PSTR("tmr1Lat min us"));
lcd_outdez8(15*FW , 2*FH, g_tmr1Latency_min/2 );
lcd_puts_P( 0*FW, 3*FH, PSTR("tmr1 Jitter us"));
lcd_outdez8(15*FW , 3*FH, (g_tmr1Latency_max - g_tmr1Latency_min) /2 );
lcd_puts_P( 0*FW, 4*FH, PSTR("tmain max ms"));
lcd_outdezAtt(15*FW, 4*FH, (g_timeMain*100)/16, PREC2);
lcd_puts_P( 0*FW, 5*FH, PSTR("t10ms us"));
lcd_outdez8(15*FW , 5*FH, g_time_per10/2 );
#ifndef SIMU
lcd_puts_P( 0*FW, 6*FH, PSTR("Free Stack min b"));
lcd_outdezAtt(18*FW-1, 6*FH, stack_free() ) ;
#endif
lcd_puts_P( 3*FW, 7*FH, PSTR("[MENU] to reset"));
}

7
src/sticks.lbm Normal file
View file

@ -0,0 +1,7 @@
prog_uchar APM sticks[] = {
18,8,18,
0x00,0x08,0x1c,0x08,0x08,0x08,0x1c,0x08,0x00,0x00,0x08,0x22,0x00,0x49,0x00,0x22,0x08,0x00,
0x00,0x00,0x00,0x00,0x22,0x7f,0x22,0x00,0x00,0x00,0x08,0x22,0x00,0x49,0x00,0x22,0x08,0x00,
0x00,0x08,0x22,0x00,0x49,0x00,0x22,0x08,0x00,0x00,0x00,0x22,0x7f,0x22,0x00,0x00,0x00,0x00,
0x00,0x08,0x22,0x00,0x49,0x00,0x22,0x08,0x00,0x00,0x08,0x1c,0x08,0x08,0x08,0x1c,0x08,0x00,
};

11
src/sticks_4x1.xbm Normal file
View file

@ -0,0 +1,11 @@
#define sticks_width 18
#define sticks_height 32
static unsigned char sticks_bits[] = {
0x00, 0x20, 0x00, 0x00, 0x88, 0x00, 0x44, 0x00, 0x00, 0xfe, 0x24, 0x01,
0x44, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
0x20, 0x20, 0x00, 0x70, 0x88, 0x00, 0x20, 0x00, 0x00, 0x20, 0x24, 0x01,
0x20, 0x00, 0x00, 0x70, 0x88, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00,
0x10, 0x10, 0x00, 0x44, 0x38, 0x00, 0x00, 0x10, 0x00, 0x92, 0x10, 0x00,
0x00, 0x10, 0x00, 0x44, 0x38, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
0x10, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x88, 0x00, 0x92, 0xfc, 0x01,
0x00, 0x88, 0x00, 0x44, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 };

207
src/templates.cpp Normal file
View file

@ -0,0 +1,207 @@
/*
* gruvin9x Author Bryan J.Rentoul (Gruvin) <gruvin@gmail.com>
*
* templates.cpp original author - Erez Raviv <erezraviv@gmail.com>
*
* 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.
*
* ============================================================
* Templates file
*
* eccpm
* crow
* throttle cut
* flaperon
* elevon
* v-tail
* throttle hold
* Aileron Differential
* Spoilers
* Snap Roll
* ELE->Flap
* Flap->ELE
*
*
*
* =============================================================
* Assumptions:
* All primary channels are per modi12x3
* Each template added to the end of each channel
*
*
*
*/
#include "gruvin9x.h"
#include "templates.h"
MixData* setDest(uint8_t dch)
{
uint8_t i = 0;
while ((g_model.mixData[i].destCh<=dch) && (g_model.mixData[i].destCh) && (i<MAX_MIXERS)) i++;
if(i==MAX_MIXERS) return &g_model.mixData[0];
memmove(&g_model.mixData[i+1],&g_model.mixData[i],
(MAX_MIXERS-(i+1))*sizeof(MixData) );
memset(&g_model.mixData[i],0,sizeof(MixData));
g_model.mixData[i].destCh = dch;
return &g_model.mixData[i];
}
void clearMixes()
{
memset(g_model.mixData,0,sizeof(g_model.mixData)); //clear all mixes
}
void clearCurves()
{
memset(g_model.curves5,0,sizeof(g_model.curves5)); //clear all curves
memset(g_model.curves9,0,sizeof(g_model.curves9)); //clear all curves
}
void setCurve(uint8_t c, int8_t ar[])
{
if(c<MAX_CURVE5) //5 pt curve
for(uint8_t i=0; i<5; i++) g_model.curves5[c][i] = ar[i];
else //9 pt curve
for(uint8_t i=0; i<9; i++) g_model.curves9[c-MAX_CURVE5][i] = ar[i];
}
void setSwitch(uint8_t idx, uint8_t func, int8_t v1, int8_t v2)
{
g_model.customSw[idx-1].func = func;
g_model.customSw[idx-1].v1 = v1;
g_model.customSw[idx-1].v2 = v2;
}
void applyTemplate(uint8_t idx)
{
int8_t heli_ar1[] = {-100, 20, 50, 70, 90};
int8_t heli_ar2[] = {90, 70, 50, 70, 90};
int8_t heli_ar3[] = {-20, -20, 0, 60, 100};
int8_t heli_ar4[] = {-100, -60, 0, 60, 100};
int8_t heli_ar5[] = {-100, 0, 0, 0, 100};
MixData *md = &g_model.mixData[0];
//CC(STK) -> vSTK
//ICC(vSTK) -> STK
#define ICC(x) icc[(x)-1]
uint8_t icc[4] = {0};
for(uint8_t i=1; i<=4; i++) //generate inverse array
for(uint8_t j=1; j<=4; j++) if(CC(i)==j) icc[j-1]=i;
switch (idx){
//Simple 4-Ch
case (0):
md=setDest(ICC(STK_RUD)); md->srcRaw=CM(STK_RUD); md->weight=100;
md=setDest(ICC(STK_ELE)); md->srcRaw=CM(STK_ELE); md->weight=100;
md=setDest(ICC(STK_THR)); md->srcRaw=CM(STK_THR); md->weight=100;
md=setDest(ICC(STK_AIL)); md->srcRaw=CM(STK_AIL); md->weight=100;
break;
//T-Cut
case (1):
md=setDest(ICC(STK_THR)); md->srcRaw=MIX_MAX; md->weight=-100; md->swtch=DSW_THR; md->mltpx=MLTPX_REP;
break;
//V-Tail
case (2):
md=setDest(ICC(STK_RUD)); md->srcRaw=CM(STK_RUD); md->weight= 100;
md=setDest(ICC(STK_RUD)); md->srcRaw=CM(STK_ELE); md->weight=-100;
md=setDest(ICC(STK_ELE)); md->srcRaw=CM(STK_RUD); md->weight= 100;
md=setDest(ICC(STK_ELE)); md->srcRaw=CM(STK_ELE); md->weight= 100;
break;
//Elevon\\Delta
case (3):
md=setDest(ICC(STK_ELE)); md->srcRaw=CM(STK_ELE); md->weight= 100;
md=setDest(ICC(STK_ELE)); md->srcRaw=CM(STK_AIL); md->weight= 100;
md=setDest(ICC(STK_AIL)); md->srcRaw=CM(STK_ELE); md->weight= 100;
md=setDest(ICC(STK_AIL)); md->srcRaw=CM(STK_AIL); md->weight=-100;
break;
//eCCPM
case (4):
md=setDest(ICC(STK_ELE)); md->srcRaw=CM(STK_ELE); md->weight= 72;
md=setDest(ICC(STK_ELE)); md->srcRaw=CM(STK_THR); md->weight= 55;
md=setDest(ICC(STK_AIL)); md->srcRaw=CM(STK_ELE); md->weight=-36;
md=setDest(ICC(STK_AIL)); md->srcRaw=CM(STK_AIL); md->weight= 62;
md=setDest(ICC(STK_AIL)); md->srcRaw=CM(STK_THR); md->weight= 55;
md=setDest(6); md->srcRaw=CM(STK_ELE); md->weight=-36;
md=setDest(6); md->srcRaw=CM(STK_AIL); md->weight=-62;
md=setDest(6); md->srcRaw=CM(STK_THR); md->weight= 55;
break;
//Heli Setup
case (5):
clearMixes(); //This time we want a clean slate
clearCurves();
//Set up Mixes
md=setDest(ICC(STK_AIL)); md->srcRaw=CH(9); md->weight= 50;
md=setDest(ICC(STK_AIL)); md->srcRaw=CH(10); md->weight=-100;
md=setDest(ICC(STK_AIL)); md->srcRaw=CH(11); md->weight= 100; md->carryTrim=TRIM_OFF;
md=setDest(ICC(STK_ELE)); md->srcRaw=CH(9); md->weight=-100;
md=setDest(ICC(STK_ELE)); md->srcRaw=CH(11); md->weight= 100; md->carryTrim=TRIM_OFF;
md=setDest(ICC(STK_THR)); md->srcRaw=CM(STK_THR); md->weight= 100; md->swtch=DSW_ID0; md->curve=CV(1); md->carryTrim=TRIM_OFF;
md=setDest(ICC(STK_THR)); md->srcRaw=CM(STK_THR); md->weight= 100; md->swtch=DSW_ID1; md->curve=CV(2); md->carryTrim=TRIM_OFF;
md=setDest(ICC(STK_THR)); md->srcRaw=CM(STK_THR); md->weight= 110; md->swtch=DSW_ID2; md->curve=CV(2); md->carryTrim=TRIM_OFF;
md=setDest(ICC(STK_THR)); md->srcRaw=MIX_MAX; md->weight=-125; md->swtch=DSW_THR; md->mltpx=MLTPX_REP; md->carryTrim=TRIM_OFF;
md=setDest(ICC(STK_RUD)); md->srcRaw=CM(STK_RUD); md->weight=100;
md=setDest(5); md->srcRaw=MIX_MAX; md->weight= 50; md->swtch=-DSW_GEA; md->carryTrim=TRIM_OFF;
md=setDest(5); md->srcRaw=MIX_MAX; md->weight=-50; md->swtch= DSW_GEA; md->carryTrim=TRIM_OFF;
md=setDest(5); md->srcRaw=STK_P3; md->weight= 40; md->carryTrim=TRIM_OFF;
md=setDest(6); md->srcRaw=CH(9); md->weight= -50;
md=setDest(6); md->srcRaw=CH(10); md->weight=-100;
md=setDest(6); md->srcRaw=CH(11); md->weight=-100; md->carryTrim=TRIM_OFF;
md=setDest(9); md->srcRaw=CM(STK_ELE); md->weight= 60;
md=setDest(10); md->srcRaw=CM(STK_AIL); md->weight=-52;
md=setDest(11); md->srcRaw=CM(STK_THR); md->weight= 70; md->swtch=DSW_ID0; md->curve=CV(3); md->carryTrim=TRIM_OFF;
md=setDest(11); md->srcRaw=CM(STK_THR); md->weight= 70; md->swtch=DSW_ID1; md->curve=CV(4); md->carryTrim=TRIM_OFF;
md=setDest(11); md->srcRaw=CM(STK_THR); md->weight= 70; md->swtch=DSW_ID2; md->curve=CV(4); md->carryTrim=TRIM_OFF;
md=setDest(11); md->srcRaw=CM(STK_THR); md->weight=100; md->swtch=DSW_THR; md->curve=CV(5); md->carryTrim=TRIM_OFF; md->mltpx=MLTPX_REP;
//Set up Curves
setCurve(CURVE5(1),heli_ar1);
setCurve(CURVE5(2),heli_ar2);
setCurve(CURVE5(3),heli_ar3);
setCurve(CURVE5(4),heli_ar4);
setCurve(CURVE5(5),heli_ar5);
break;
//Servo Test
case (6):
md=setDest(15); md->srcRaw=CH(16); md->weight= 100; md->speedUp = 8; md->speedDown = 8;
md=setDest(16); md->srcRaw=MIX_FULL; md->weight= 110; md->swtch=DSW_SW1;
md=setDest(16); md->srcRaw=MIX_MAX; md->weight=-110; md->swtch=DSW_SW2; md->mltpx=MLTPX_REP;
md=setDest(16); md->srcRaw=MIX_MAX; md->weight= 110; md->swtch=DSW_SW3; md->mltpx=MLTPX_REP;
setSwitch(1,CS_LESS,CH(15),CH(16));
setSwitch(2,CS_VPOS,CH(15), 105);
setSwitch(3,CS_VNEG,CH(15), -105);
break;
default:
break;
}
STORE_MODELVARS;
}

88
src/templates.h Normal file
View file

@ -0,0 +1,88 @@
/*
* Author - Erez Raviv <erezraviv@gmail.com>
*
* 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.
*
* ============================================================
* Templates file
*
* eccpm
* crow
* throttle cut
* flaperon
* elevon
* v-tail
* throttle hold
* Aileron Differential
* Spoilers
* Snap Roll
* ELE->Flap
* Flap->ELE
*
*
*
* =============================================================
* Assumptions:
* All primary channels are per modi12x3
* Each template added to the end of each channel
*
*
*
*/
#ifndef TEMPLATES_H
#define TEMPLATES_H
#include <inttypes.h>
#define STK_RUD 1
#define STK_ELE 2
#define STK_THR 3
#define STK_AIL 4
#define STK_P1 5
#define STK_P2 6
#define STK_P3 7
#define NUM_TEMPLATES DIM(n_Templates)
#define NUM_TEMPLATE_MIX 8
#define TEMPLATE_NLEN 15
#define TRIM_ON 0
#define TRIM_OFF 1
#define CM(x) (CONVERT_MODE(x)) //good for SRC
#define CH(x) (CHOUT_BASE+(x))
#define CV(x) (CURVE_BASE+(x)-1)
#define CC(x) (CHANNEL_ORDER(x)) //need to invert this to work with dest
#define CURVE5(x) ((x)-1)
#define CURVE9(x) (MAX_CURVE5+(x)-1)
const char n_Templates[][TEMPLATE_NLEN] = {
"Simple 4-CH",
"T-Cut",
"V-Tail",
"Elevon\\Delta",
"eCCPM",
"Heli Setup",
"Servo Test"
};
void clearMixes();
void clearCurves();
void applyTemplate(uint8_t idx);
#endif //TEMPLATES_H

463
src/time.cpp Normal file
View file

@ -0,0 +1,463 @@
#include "time.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 tm *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 tm *
__localtime_r (time_t * t, struct tm * 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 tm *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 tm *
ranged_convert (struct tm *(*convert) (time_t *, struct tm *),
time_t *t, struct tm *tp)
{
struct tm *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 tm *tp,
struct tm *(*convert) (time_t *, struct tm *),
time_t *offset)
{
time_t t, gt, t0, t1, t2;
struct tm 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 tm *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 tm *tp)
{
return __offtime(t, 0, tp);
}

34
src/time.h Normal file
View file

@ -0,0 +1,34 @@
#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 tm
{
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 tm *tp);
extern time_t filltm(time_t *t, struct tm *tp);
#endif