Performance: optimized 32-bit shifts (#1187)
* Add optimized 32-bit shifting * Tooth based time to angle coversion is only used by a few decoders. So move the functions into decoders.cpp * Better separation of deocders and crank maths. * Apply optimised shifts * Doxygen
This commit is contained in:
parent
478e16cc52
commit
0f13753ed3
380
Doxyfile
380
Doxyfile
|
@ -1,4 +1,4 @@
|
|||
# Doxyfile 1.9.4
|
||||
# Doxyfile 1.10.0
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
|
@ -19,7 +19,8 @@
|
|||
# configuration file:
|
||||
# doxygen -x [configFile]
|
||||
# Use doxygen to compare the used configuration file with the template
|
||||
# configuration file without replacing the environment variables:
|
||||
# configuration file without replacing the environment variables or CMake type
|
||||
# replacement variables:
|
||||
# doxygen -x_noenv [configFile]
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
@ -62,6 +63,12 @@ PROJECT_BRIEF =
|
|||
|
||||
PROJECT_LOGO = reference/speeduino_logo.png
|
||||
|
||||
# With the PROJECT_ICON tag one can specify an icon that is included in the tabs
|
||||
# when the HTML document is shown. Doxygen will copy the logo to the output
|
||||
# directory.
|
||||
|
||||
PROJECT_ICON =
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
|
||||
# into which the generated documentation will be written. If a relative path is
|
||||
# entered, it will be relative to the location where doxygen was started. If
|
||||
|
@ -85,7 +92,7 @@ CREATE_SUBDIRS = NO
|
|||
# level increment doubles the number of directories, resulting in 4096
|
||||
# directories at level 8 which is the default and also the maximum value. The
|
||||
# sub-directories are organized in 2 levels, the first level always has a fixed
|
||||
# numer of 16 directories.
|
||||
# number of 16 directories.
|
||||
# Minimum value: 0, maximum value: 8, default value: 8.
|
||||
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
|
||||
|
||||
|
@ -362,6 +369,17 @@ MARKDOWN_SUPPORT = YES
|
|||
|
||||
TOC_INCLUDE_HEADINGS = 0
|
||||
|
||||
# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to
|
||||
# generate identifiers for the Markdown headings. Note: Every identifier is
|
||||
# unique.
|
||||
# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a
|
||||
# sequence number starting at 0 and GITHUB use the lower case version of title
|
||||
# with any whitespace replaced by '-' and punctuation characters removed.
|
||||
# The default value is: DOXYGEN.
|
||||
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
|
||||
|
||||
MARKDOWN_ID_STYLE = DOXYGEN
|
||||
|
||||
# When enabled doxygen tries to link words that correspond to documented
|
||||
# classes, or namespaces to their corresponding documentation. Such a link can
|
||||
# be prevented in individual cases by putting a % sign in front of the word or
|
||||
|
@ -486,6 +504,14 @@ LOOKUP_CACHE_SIZE = 0
|
|||
|
||||
NUM_PROC_THREADS = 1
|
||||
|
||||
# If the TIMESTAMP tag is set different from NO then each generated page will
|
||||
# contain the date or date and time when the page was generated. Setting this to
|
||||
# NO can help when comparing the output of multiple runs.
|
||||
# Possible values are: YES, NO, DATETIME and DATE.
|
||||
# The default value is: NO.
|
||||
|
||||
TIMESTAMP = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Build related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
|
@ -567,7 +593,8 @@ HIDE_UNDOC_MEMBERS = NO
|
|||
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
|
||||
# undocumented classes that are normally visible in the class hierarchy. If set
|
||||
# to NO, these classes will be included in the various overviews. This option
|
||||
# has no effect if EXTRACT_ALL is enabled.
|
||||
# will also hide undocumented C++ concepts if enabled. This option has no effect
|
||||
# if EXTRACT_ALL is enabled.
|
||||
# The default value is: NO.
|
||||
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
|
@ -605,7 +632,8 @@ INTERNAL_DOCS = NO
|
|||
# Windows (including Cygwin) and MacOS, users should typically set this option
|
||||
# to NO, whereas on Linux or other Unix flavors it should typically be set to
|
||||
# YES.
|
||||
# The default value is: system dependent.
|
||||
# Possible values are: SYSTEM, NO and YES.
|
||||
# The default value is: SYSTEM.
|
||||
|
||||
CASE_SENSE_NAMES = NO
|
||||
|
||||
|
@ -857,11 +885,26 @@ WARN_IF_INCOMPLETE_DOC = YES
|
|||
|
||||
WARN_NO_PARAMDOC = NO
|
||||
|
||||
# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
|
||||
# undocumented enumeration values. If set to NO, doxygen will accept
|
||||
# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
|
||||
# will automatically be disabled.
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_IF_UNDOC_ENUM_VAL = NO
|
||||
|
||||
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
|
||||
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
|
||||
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
|
||||
# at the end of the doxygen process doxygen will return with a non-zero status.
|
||||
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
|
||||
# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves
|
||||
# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not
|
||||
# write the warning messages in between other messages but write them at the end
|
||||
# of a run, in case a WARN_LOGFILE is defined the warning messages will be
|
||||
# besides being in the defined file also be shown at the end of a run, unless
|
||||
# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case
|
||||
# the behavior will remain as with the setting FAIL_ON_WARNINGS.
|
||||
# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_AS_ERROR = NO
|
||||
|
@ -914,10 +957,21 @@ INPUT = speeduino/ \
|
|||
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
|
||||
# documentation (see:
|
||||
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
|
||||
# See also: INPUT_FILE_ENCODING
|
||||
# The default value is: UTF-8.
|
||||
|
||||
INPUT_ENCODING = UTF-8
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
|
||||
# character encoding on a per file pattern basis. Doxygen will compare the file
|
||||
# name with each pattern and apply the encoding instead of the default
|
||||
# INPUT_ENCODING) if there is a match. The character encodings are a list of the
|
||||
# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
|
||||
# "INPUT_ENCODING" for further information on supported encodings.
|
||||
|
||||
INPUT_FILE_ENCODING =
|
||||
|
||||
# If the value of the INPUT tag contains directories, you can use the
|
||||
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
|
||||
# *.h) to filter out the source-files in the directories.
|
||||
|
@ -929,12 +983,12 @@ INPUT_ENCODING = UTF-8
|
|||
# Note the list of default checked file patterns might differ from the list of
|
||||
# default file extension mappings.
|
||||
#
|
||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
|
||||
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
||||
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
|
||||
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
|
||||
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
|
||||
# *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,
|
||||
# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl,
|
||||
# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d,
|
||||
# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to
|
||||
# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
|
||||
# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
|
||||
FILE_PATTERNS = *.c \
|
||||
*.cc \
|
||||
|
@ -1020,9 +1074,6 @@ EXCLUDE_PATTERNS =
|
|||
# output. The symbol name can be a fully qualified name, a word, or if the
|
||||
# wildcard * is used, a substring. Examples: ANamespace, AClass,
|
||||
# ANamespace::AClass, ANamespace::*Test
|
||||
#
|
||||
# Note that the wildcards are matched against the file with absolute path, so to
|
||||
# exclude all test directories use the pattern */test/*
|
||||
|
||||
EXCLUDE_SYMBOLS = TO_TYPE_KEY \
|
||||
CTA_* \
|
||||
|
@ -1072,6 +1123,11 @@ IMAGE_PATH =
|
|||
# code is scanned, but not when the output code is generated. If lines are added
|
||||
# or removed, the anchors will not be placed correctly.
|
||||
#
|
||||
# Note that doxygen will use the data processed and written to standard output
|
||||
# for further processing, therefore nothing else, like debug statements or used
|
||||
# commands (so in case of a Windows batch file always use @echo OFF), should be
|
||||
# written to standard output.
|
||||
#
|
||||
# Note that for custom extensions or not directly supported extensions you also
|
||||
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
|
||||
# properly processed by doxygen.
|
||||
|
@ -1113,6 +1169,15 @@ FILTER_SOURCE_PATTERNS =
|
|||
|
||||
USE_MDFILE_AS_MAINPAGE =
|
||||
|
||||
# The Fortran standard specifies that for fixed formatted Fortran code all
|
||||
# characters from position 72 are to be considered as comment. A common
|
||||
# extension is to allow longer lines before the automatic comment starts. The
|
||||
# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
|
||||
# be processed before the automatic comment starts.
|
||||
# Minimum value: 7, maximum value: 10000, default value: 72.
|
||||
|
||||
FORTRAN_COMMENT_AFTER = 72
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to source browsing
|
||||
#---------------------------------------------------------------------------
|
||||
|
@ -1127,7 +1192,8 @@ USE_MDFILE_AS_MAINPAGE =
|
|||
SOURCE_BROWSER = NO
|
||||
|
||||
# Setting the INLINE_SOURCES tag to YES will include the body of functions,
|
||||
# classes and enums directly into the documentation.
|
||||
# multi-line macros, enums or list initialized variables directly into the
|
||||
# documentation.
|
||||
# The default value is: NO.
|
||||
|
||||
INLINE_SOURCES = NO
|
||||
|
@ -1250,10 +1316,11 @@ CLANG_DATABASE_PATH =
|
|||
|
||||
ALPHABETICAL_INDEX = YES
|
||||
|
||||
# In case all classes in a project start with a common prefix, all classes will
|
||||
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
|
||||
# can be used to specify a prefix (or a list of prefixes) that should be ignored
|
||||
# while generating the index headers.
|
||||
# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
|
||||
# that should be ignored while generating the index headers. The IGNORE_PREFIX
|
||||
# tag works for classes, function and member names. The entity will be placed in
|
||||
# the alphabetical list under the first letter of the entity name that remains
|
||||
# after removing the prefix.
|
||||
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
|
||||
|
||||
IGNORE_PREFIX =
|
||||
|
@ -1332,7 +1399,12 @@ HTML_STYLESHEET =
|
|||
# Doxygen will copy the style sheet files to the output directory.
|
||||
# Note: The order of the extra style sheet files is of importance (e.g. the last
|
||||
# style sheet in the list overrules the setting of the previous ones in the
|
||||
# list). For an example see the documentation.
|
||||
# list).
|
||||
# Note: Since the styling of scrollbars can currently not be overruled in
|
||||
# Webkit/Chromium, the styling will be left out of the default doxygen.css if
|
||||
# one or more extra stylesheets have been specified. So if scrollbar
|
||||
# customization is desired it has to be added explicitly. For an example see the
|
||||
# documentation.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_EXTRA_STYLESHEET =
|
||||
|
@ -1347,6 +1419,19 @@ HTML_EXTRA_STYLESHEET =
|
|||
|
||||
HTML_EXTRA_FILES =
|
||||
|
||||
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
|
||||
# should be rendered with a dark or light theme.
|
||||
# Possible values are: LIGHT always generate light mode output, DARK always
|
||||
# generate dark mode output, AUTO_LIGHT automatically set the mode according to
|
||||
# the user preference, use light mode if no preference is set (the default),
|
||||
# AUTO_DARK automatically set the mode according to the user preference, use
|
||||
# dark mode if no preference is set and TOGGLE allow to user to switch between
|
||||
# light and dark mode via a button.
|
||||
# The default value is: AUTO_LIGHT.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_COLORSTYLE = AUTO_LIGHT
|
||||
|
||||
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
||||
# will adjust the colors in the style sheet and background images according to
|
||||
# this color. Hue is specified as an angle on a color-wheel, see
|
||||
|
@ -1377,15 +1462,6 @@ HTML_COLORSTYLE_SAT = 100
|
|||
|
||||
HTML_COLORSTYLE_GAMMA = 80
|
||||
|
||||
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
|
||||
# page will contain the date and time when the page was generated. Setting this
|
||||
# to YES can help to show when doxygen was last run and thus if the
|
||||
# documentation is up to date.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_TIMESTAMP = YES
|
||||
|
||||
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
|
||||
# documentation will contain a main index with vertical navigation menus that
|
||||
# are dynamically created via JavaScript. If disabled, the navigation index will
|
||||
|
@ -1405,6 +1481,33 @@ HTML_DYNAMIC_MENUS = YES
|
|||
|
||||
HTML_DYNAMIC_SECTIONS = NO
|
||||
|
||||
# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be
|
||||
# dynamically folded and expanded in the generated HTML source code.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_CODE_FOLDING = YES
|
||||
|
||||
# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in
|
||||
# the top right corner of code and text fragments that allows the user to copy
|
||||
# its content to the clipboard. Note this only works if supported by the browser
|
||||
# and the web page is served via a secure context (see:
|
||||
# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file:
|
||||
# protocol.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_COPY_CLIPBOARD = YES
|
||||
|
||||
# Doxygen stores a couple of settings persistently in the browser (via e.g.
|
||||
# cookies). By default these settings apply to all HTML pages generated by
|
||||
# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store
|
||||
# the settings under a project specific key, such that the user preferences will
|
||||
# be stored separately.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_PROJECT_COOKIE =
|
||||
|
||||
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
|
||||
# shown in the various tree structured indices initially; the user can expand
|
||||
# and collapse entries dynamically later on. Doxygen will expand the tree to
|
||||
|
@ -1535,6 +1638,16 @@ BINARY_TOC = NO
|
|||
|
||||
TOC_EXPAND = NO
|
||||
|
||||
# The SITEMAP_URL tag is used to specify the full URL of the place where the
|
||||
# generated documentation will be placed on the server by the user during the
|
||||
# deployment of the documentation. The generated sitemap is called sitemap.xml
|
||||
# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL
|
||||
# is specified no sitemap is generated. For information about the sitemap
|
||||
# protocol see https://www.sitemaps.org
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
SITEMAP_URL =
|
||||
|
||||
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
|
||||
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
|
||||
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
|
||||
|
@ -1710,17 +1823,6 @@ HTML_FORMULA_FORMAT = png
|
|||
|
||||
FORMULA_FONTSIZE = 10
|
||||
|
||||
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
|
||||
# generated for formulas are transparent PNGs. Transparent PNGs are not
|
||||
# supported properly for IE 6.0, but are supported on all modern browsers.
|
||||
#
|
||||
# Note that when changing this option you need to delete any form_*.png files in
|
||||
# the HTML output directory before the changes have effect.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
FORMULA_TRANSPARENT = YES
|
||||
|
||||
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
|
||||
# to create new LaTeX commands to be used in formulas as building blocks. See
|
||||
# the section "Including formulas" for details.
|
||||
|
@ -2034,9 +2136,16 @@ PDF_HYPERLINKS = YES
|
|||
|
||||
USE_PDFLATEX = YES
|
||||
|
||||
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
|
||||
# command to the generated LaTeX files. This will instruct LaTeX to keep running
|
||||
# if errors occur, instead of asking the user for help.
|
||||
# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.
|
||||
# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch
|
||||
# mode nothing is printed on the terminal, errors are scrolled as if <return> is
|
||||
# hit at every error; missing files that TeX tries to input or request from
|
||||
# keyboard input (\read on a not open input stream) cause the job to abort,
|
||||
# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,
|
||||
# but there is no possibility of user interaction just like in batch mode,
|
||||
# SCROLL In scroll mode, TeX will stop only for missing files to input or if
|
||||
# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at
|
||||
# each error, asking for user intervention.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
|
@ -2057,14 +2166,6 @@ LATEX_HIDE_INDICES = NO
|
|||
|
||||
LATEX_BIB_STYLE = plain
|
||||
|
||||
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
|
||||
# page will contain the date and time when the page was generated. Setting this
|
||||
# to NO can help when comparing the output of multiple runs.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_TIMESTAMP = NO
|
||||
|
||||
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
|
||||
# path from which the emoji images will be read. If a relative path is entered,
|
||||
# it will be relative to the LATEX_OUTPUT directory. If left blank the
|
||||
|
@ -2230,7 +2331,7 @@ DOCBOOK_OUTPUT = docbook
|
|||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
|
||||
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
|
||||
# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures
|
||||
# the structure of the code including all documentation. Note that this feature
|
||||
# is still experimental and incomplete at the moment.
|
||||
# The default value is: NO.
|
||||
|
@ -2241,6 +2342,28 @@ GENERATE_AUTOGEN_DEF = NO
|
|||
# Configuration options related to Sqlite3 output
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3
|
||||
# database with symbols found by doxygen stored in tables.
|
||||
# The default value is: NO.
|
||||
|
||||
GENERATE_SQLITE3 = NO
|
||||
|
||||
# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
|
||||
# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
|
||||
# in front of it.
|
||||
# The default directory is: sqlite3.
|
||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
||||
|
||||
SQLITE3_OUTPUT = sqlite3
|
||||
|
||||
# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db
|
||||
# database file will be recreated with each doxygen run. If set to NO, doxygen
|
||||
# will warn if a database file is already found and not modify it.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
||||
|
||||
SQLITE3_RECREATE_DB = YES
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the Perl module output
|
||||
#---------------------------------------------------------------------------
|
||||
|
@ -2338,7 +2461,8 @@ INCLUDE_FILE_PATTERNS =
|
|||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
PREDEFINED = __attribute__((x))= \
|
||||
PROGMEM
|
||||
PROGMEM \
|
||||
USE_OPTIMIZED_SHIFTS=1
|
||||
|
||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
||||
# tag can be used to specify a list of macro names that should be expanded. The
|
||||
|
@ -2388,15 +2512,15 @@ TAGFILES =
|
|||
|
||||
GENERATE_TAGFILE =
|
||||
|
||||
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
|
||||
# the class index. If set to NO, only the inherited external classes will be
|
||||
# listed.
|
||||
# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces
|
||||
# will be listed in the class and namespace index. If set to NO, only the
|
||||
# inherited external classes will be listed.
|
||||
# The default value is: NO.
|
||||
|
||||
ALLEXTERNALS = NO
|
||||
|
||||
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
|
||||
# in the modules index. If set to NO, only the current project's groups will be
|
||||
# in the topic index. If set to NO, only the current project's groups will be
|
||||
# listed.
|
||||
# The default value is: YES.
|
||||
|
||||
|
@ -2410,16 +2534,9 @@ EXTERNAL_GROUPS = YES
|
|||
EXTERNAL_PAGES = YES
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the dot tool
|
||||
# Configuration options related to diagram generator tools
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
# If left empty dia is assumed to be found in the default search path.
|
||||
|
||||
DIA_PATH =
|
||||
|
||||
# If set to YES the inheritance and collaboration graphs will hide inheritance
|
||||
# and usage relations if the target is undocumented or is not a class.
|
||||
# The default value is: YES.
|
||||
|
@ -2428,7 +2545,7 @@ HIDE_UNDOC_RELATIONS = YES
|
|||
|
||||
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
|
||||
# available from the path. This tool is part of Graphviz (see:
|
||||
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
||||
# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
||||
# Bell Labs. The other options in this section have no effect if this option is
|
||||
# set to NO
|
||||
# The default value is: NO.
|
||||
|
@ -2445,37 +2562,55 @@ HAVE_DOT = YES
|
|||
|
||||
DOT_NUM_THREADS = 0
|
||||
|
||||
# When you want a differently looking font in the dot files that doxygen
|
||||
# generates you can specify the font name using DOT_FONTNAME. You need to make
|
||||
# sure dot is able to find the font, which can be done by putting it in a
|
||||
# standard location or by setting the DOTFONTPATH environment variable or by
|
||||
# setting DOT_FONTPATH to the directory containing the font.
|
||||
# The default value is: Helvetica.
|
||||
# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
|
||||
# subgraphs. When you want a differently looking font in the dot files that
|
||||
# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
|
||||
# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
|
||||
# Edge and Graph Attributes specification</a> You need to make sure dot is able
|
||||
# to find the font, which can be done by putting it in a standard location or by
|
||||
# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
|
||||
# directory containing the font. Default graphviz fontsize is 14.
|
||||
# The default value is: fontname=Helvetica,fontsize=10.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTNAME = Helvetica
|
||||
DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
|
||||
|
||||
# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
|
||||
# dot graphs.
|
||||
# Minimum value: 4, maximum value: 24, default value: 10.
|
||||
# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
|
||||
# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
|
||||
# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
|
||||
# arrows shapes.</a>
|
||||
# The default value is: labelfontname=Helvetica,labelfontsize=10.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTSIZE = 10
|
||||
DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10"
|
||||
|
||||
# By default doxygen will tell dot to use the default font as specified with
|
||||
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
|
||||
# the path where dot can find it using this tag.
|
||||
# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
|
||||
# around nodes set 'shape=plain' or 'shape=plaintext' <a
|
||||
# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
|
||||
# The default value is: shape=box,height=0.2,width=0.4.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
|
||||
|
||||
# You can set the path where dot can find font specified with fontname in
|
||||
# DOT_COMMON_ATTR and others dot attributes.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTPATH =
|
||||
|
||||
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
|
||||
# graph for each documented class showing the direct and indirect inheritance
|
||||
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
|
||||
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
|
||||
# to TEXT the direct and indirect inheritance relations will be shown as texts /
|
||||
# links.
|
||||
# Possible values are: NO, YES, TEXT and GRAPH.
|
||||
# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will
|
||||
# generate a graph for each documented class showing the direct and indirect
|
||||
# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and
|
||||
# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case
|
||||
# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the
|
||||
# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.
|
||||
# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance
|
||||
# relations will be shown as texts / links. Explicit enabling an inheritance
|
||||
# graph or choosing a different representation for an inheritance graph of a
|
||||
# specific class, can be accomplished by means of the command \inheritancegraph.
|
||||
# Disabling an inheritance graph can be accomplished by means of the command
|
||||
# \hideinheritancegraph.
|
||||
# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
|
||||
# The default value is: YES.
|
||||
|
||||
CLASS_GRAPH = YES
|
||||
|
@ -2483,15 +2618,21 @@ CLASS_GRAPH = YES
|
|||
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
|
||||
# graph for each documented class showing the direct and indirect implementation
|
||||
# dependencies (inheritance, containment, and class references variables) of the
|
||||
# class with other documented classes.
|
||||
# class with other documented classes. Explicit enabling a collaboration graph,
|
||||
# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the
|
||||
# command \collaborationgraph. Disabling a collaboration graph can be
|
||||
# accomplished by means of the command \hidecollaborationgraph.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
COLLABORATION_GRAPH = YES
|
||||
|
||||
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
|
||||
# groups, showing the direct groups dependencies. See also the chapter Grouping
|
||||
# in the manual.
|
||||
# groups, showing the direct groups dependencies. Explicit enabling a group
|
||||
# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means
|
||||
# of the command \groupgraph. Disabling a directory graph can be accomplished by
|
||||
# means of the command \hidegroupgraph. See also the chapter Grouping in the
|
||||
# manual.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
|
@ -2533,8 +2674,8 @@ DOT_UML_DETAILS = NO
|
|||
|
||||
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
|
||||
# to display on a single line. If the actual line length exceeds this threshold
|
||||
# significantly it will wrapped across multiple lines. Some heuristics are apply
|
||||
# to avoid ugly line breaks.
|
||||
# significantly it will be wrapped across multiple lines. Some heuristics are
|
||||
# applied to avoid ugly line breaks.
|
||||
# Minimum value: 0, maximum value: 1000, default value: 17.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
|
@ -2551,7 +2692,9 @@ TEMPLATE_RELATIONS = NO
|
|||
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
|
||||
# YES then doxygen will generate a graph for each documented file showing the
|
||||
# direct and indirect include dependencies of the file with other documented
|
||||
# files.
|
||||
# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,
|
||||
# can be accomplished by means of the command \includegraph. Disabling an
|
||||
# include graph can be accomplished by means of the command \hideincludegraph.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
|
@ -2560,7 +2703,10 @@ INCLUDE_GRAPH = NO
|
|||
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
|
||||
# set to YES then doxygen will generate a graph for each documented file showing
|
||||
# the direct and indirect include dependencies of the file with other documented
|
||||
# files.
|
||||
# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set
|
||||
# to NO, can be accomplished by means of the command \includedbygraph. Disabling
|
||||
# an included by graph can be accomplished by means of the command
|
||||
# \hideincludedbygraph.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
|
@ -2600,7 +2746,10 @@ GRAPHICAL_HIERARCHY = NO
|
|||
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
|
||||
# dependencies a directory has on other directories in a graphical way. The
|
||||
# dependency relations are determined by the #include relations between the
|
||||
# files in the directories.
|
||||
# files in the directories. Explicit enabling a directory graph, when
|
||||
# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command
|
||||
# \directorygraph. Disabling a directory graph can be accomplished by means of
|
||||
# the command \hidedirectorygraph.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
|
@ -2616,7 +2765,7 @@ DIR_GRAPH_MAX_DEPTH = 1
|
|||
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
|
||||
# generated by dot. For an explanation of the image formats see the section
|
||||
# output formats in the documentation of the dot tool (Graphviz (see:
|
||||
# http://www.graphviz.org/)).
|
||||
# https://www.graphviz.org/)).
|
||||
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
|
||||
# to make the SVG files visible in IE 9+ (other browsers do not have this
|
||||
# requirement).
|
||||
|
@ -2653,11 +2802,12 @@ DOT_PATH =
|
|||
|
||||
DOTFILE_DIRS =
|
||||
|
||||
# The MSCFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain msc files that are included in the documentation (see the \mscfile
|
||||
# command).
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
# If left empty dia is assumed to be found in the default search path.
|
||||
|
||||
MSCFILE_DIRS =
|
||||
DIA_PATH =
|
||||
|
||||
# The DIAFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain dia files that are included in the documentation (see the \diafile
|
||||
|
@ -2707,18 +2857,6 @@ DOT_GRAPH_MAX_NODES = 500
|
|||
|
||||
MAX_DOT_GRAPH_DEPTH = 0
|
||||
|
||||
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
|
||||
# background. This is disabled by default, because dot on Windows does not seem
|
||||
# to support this out of the box.
|
||||
#
|
||||
# Warning: Depending on the platform used, enabling this option may lead to
|
||||
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
|
||||
# read).
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_TRANSPARENT = YES
|
||||
|
||||
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
|
||||
# files in one run (i.e. multiple -o and -T options on the command line). This
|
||||
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
|
||||
|
@ -2746,3 +2884,19 @@ GENERATE_LEGEND = YES
|
|||
# The default value is: YES.
|
||||
|
||||
DOT_CLEANUP = YES
|
||||
|
||||
# You can define message sequence charts within doxygen comments using the \msc
|
||||
# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will
|
||||
# use a built-in version of mscgen tool to produce the charts. Alternatively,
|
||||
# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,
|
||||
# specifying prog as the value, doxygen will call the tool as prog -T
|
||||
# <outfile_format> -o <outputfile> <inputfile>. The external tool should support
|
||||
# output file formats "png", "eps", "svg", and "ismap".
|
||||
|
||||
MSCGEN_TOOL =
|
||||
|
||||
# The MSCFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain msc files that are included in the documentation (see the \mscfile
|
||||
# command).
|
||||
|
||||
MSCFILE_DIRS =
|
||||
|
|
|
@ -0,0 +1,687 @@
|
|||
#pragma once
|
||||
|
||||
/** @file
|
||||
* @brief Optimized multi-byte bit shifting *for AVR-GCC only*. See @ref group-opt-shift
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "globals.h" // Required for CPU/architecture preprocessor symbols
|
||||
|
||||
/// @defgroup group-opt-shift Optimised bitwise shifts
|
||||
///
|
||||
/// @brief As of AVR-GCC 13.2.0, the code produced for 32-bit shifts with a compile time shift distance is very poor.
|
||||
/// The templates here implement optimised shifting. Average 35% increase in shift performance on AtMega2560: see unit tests.
|
||||
///
|
||||
/// Usage:
|
||||
/// @code
|
||||
/// rpmDelta = lshift<10>(toothDeltaV) / (6 * toothDeltaT);
|
||||
/// @endcode
|
||||
///
|
||||
/// If there is no overload for a certain shift, that's because GCC produced decent ASM
|
||||
/// in that case.
|
||||
///
|
||||
/// @note Code is usable on all architectures, but the optimization only applies to AVR-GCC.
|
||||
/// Other compilers will see a standard bitwise shift.
|
||||
/// @{
|
||||
|
||||
// Flag if we should turn on optimized shifts
|
||||
#if !defined(USE_OPTIMIZED_SHIFTS)
|
||||
#if (defined(CORE_AVR) || defined(ARDUINO_ARCH_AVR)) && defined(__GNUC__)
|
||||
#define USE_OPTIMIZED_SHIFTS 1
|
||||
#else
|
||||
#define USE_OPTIMIZED_SHIFTS 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Bitwise left shift - generic, unoptimized, case
|
||||
*
|
||||
* @tparam b number of bits to shift by
|
||||
* @param a value to shift
|
||||
* @return uint32_t a<<b
|
||||
*/
|
||||
template <uint8_t b>
|
||||
static inline uint32_t lshift(uint32_t a) {
|
||||
#if USE_OPTIMIZED_SHIFTS==1
|
||||
// The shifts below have been validated to produce performant code in GCC.
|
||||
// Other shift amounts are either in a specialized template below (good) or are unvalidated (bad).
|
||||
static_assert(b==1 || b==2 || b==3 || b==8 || b==16 || b==24,
|
||||
"Unvalidated shift - confirm gcc produces performant code");
|
||||
#endif
|
||||
return a << b;
|
||||
}
|
||||
|
||||
#if USE_OPTIMIZED_SHIFTS==1
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
|
||||
/// @{
|
||||
/// @brief uint32_t bitwise left shift optimised for the specified shift distance
|
||||
///
|
||||
/// @note The assembler was generated using clang 17.0.1 cross compiling: -O3 --target=avr -mmcu=atmega2560. See https://godbolt.org/z/71cPnMYqs
|
||||
///
|
||||
/// @param a value to shift
|
||||
/// @return uint32_t a<<b
|
||||
template <>
|
||||
uint32_t lshift<4U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"swap %D0\n"
|
||||
"andi %D0, 240\n"
|
||||
"swap %C0\n"
|
||||
"eor %D0, %C0\n"
|
||||
"andi %C0, 240\n"
|
||||
"eor %D0, %C0\n"
|
||||
"swap %B0\n"
|
||||
"eor %C0, %B0\n"
|
||||
"andi %B0, 240\n"
|
||||
"eor %C0, %B0\n"
|
||||
"swap %A0\n"
|
||||
"eor %B0, %A0\n"
|
||||
"andi %A0, 240\n"
|
||||
"eor %B0, %A0\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
:
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t lshift<5U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"swap %D0\n"
|
||||
"andi %D0, 240\n"
|
||||
"swap %C0\n"
|
||||
"eor %D0, %C0\n"
|
||||
"andi %C0, 240\n"
|
||||
"eor %D0, %C0\n"
|
||||
"swap %B0\n"
|
||||
"eor %C0, %B0\n"
|
||||
"andi %B0, 240\n"
|
||||
"eor %C0, %B0\n"
|
||||
"swap %A0\n"
|
||||
"eor %B0, %A0\n"
|
||||
"andi %A0, 240\n"
|
||||
"eor %B0, %A0\n"
|
||||
"lsl %A0\n"
|
||||
"rol %B0\n"
|
||||
"rol %C0\n"
|
||||
"rol %D0\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
:
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t lshift<6U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"lsr %D0\n"
|
||||
"ror %C0\n"
|
||||
"ror %B0\n"
|
||||
"ror %A0\n"
|
||||
"mov r18, __zero_reg__\n"
|
||||
"ror r18\n"
|
||||
"lsr %D0\n"
|
||||
"ror %C0\n"
|
||||
"ror %B0\n"
|
||||
"ror %A0\n"
|
||||
"ror r18\n"
|
||||
"mov %D0, %C0\n"
|
||||
"mov %C0, %B0\n"
|
||||
"mov r19, %A0\n"
|
||||
"movw %A0, r18\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
: "r18", "r19"
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t lshift<7U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"lsr %D0\n"
|
||||
"ror %C0\n"
|
||||
"ror %B0\n"
|
||||
"ror %A0\n"
|
||||
"mov r18, __zero_reg__\n"
|
||||
"ror r18\n"
|
||||
"mov %D0, %C0\n"
|
||||
"mov %C0, %B0\n"
|
||||
"mov r19, %A0\n"
|
||||
"movw %A0, r18\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
: "r18", "r19"
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t lshift<9U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"lsl %A0\n"
|
||||
"rol %B0\n"
|
||||
"rol %C0\n"
|
||||
"mov %D0, %C0\n"
|
||||
"mov %C0, %B0\n"
|
||||
"mov %B0, %A0\n"
|
||||
"mov %A0, __zero_reg__\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
:
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t lshift<10U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"lsl %A0\n"
|
||||
"rol %B0\n"
|
||||
"rol %C0\n"
|
||||
"lsl %A0\n"
|
||||
"rol %B0\n"
|
||||
"rol %C0\n"
|
||||
"mov %D0, %C0\n"
|
||||
"mov %C0, %B0\n"
|
||||
"mov %B0, %A0\n"
|
||||
"mov %A0, __zero_reg__\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
:
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t lshift<11U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"lsl %A0\n"
|
||||
"rol %B0\n"
|
||||
"rol %C0\n"
|
||||
"lsl %A0\n"
|
||||
"rol %B0\n"
|
||||
"rol %C0\n"
|
||||
"lsl %A0\n"
|
||||
"rol %B0\n"
|
||||
"rol %C0\n"
|
||||
"mov %D0, %C0\n"
|
||||
"mov %C0, %B0\n"
|
||||
"mov %B0, %A0\n"
|
||||
"mov %A0, __zero_reg__\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
:
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t lshift<12U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"swap %C0\n"
|
||||
"andi %C0, 240\n"
|
||||
"swap %B0\n"
|
||||
"eor %C0, %B0\n"
|
||||
"andi %B0, 240\n"
|
||||
"eor %C0, %B0\n"
|
||||
"swap %A0\n"
|
||||
"eor %B0, %A0\n"
|
||||
"andi %A0, 240\n"
|
||||
"eor %B0, %A0\n"
|
||||
"mov %D0, %C0\n"
|
||||
"mov %C0, %B0\n"
|
||||
"mov %B0, %A0\n"
|
||||
"mov %A0, __zero_reg__\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
:
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t lshift<13U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"swap %C0\n"
|
||||
"andi %C0, 240\n"
|
||||
"swap %B0\n"
|
||||
"eor %C0, %B0\n"
|
||||
"andi %B0, 240\n"
|
||||
"eor %C0, %B0\n"
|
||||
"swap %A0\n"
|
||||
"eor %B0, %A0\n"
|
||||
"andi %A0, 240\n"
|
||||
"eor %B0, %A0\n"
|
||||
"lsl %A0\n"
|
||||
"rol %B0\n"
|
||||
"rol %C0\n"
|
||||
"mov %D0, %C0\n"
|
||||
"mov %C0, %B0\n"
|
||||
"mov %B0, %A0\n"
|
||||
"mov %A0, __zero_reg__\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
:
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t lshift<14U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"movw r18, %A0\n"
|
||||
"lsr %C0\n"
|
||||
"ror r19\n"
|
||||
"ror r18\n"
|
||||
"mov %B0, __zero_reg__\n"
|
||||
"ror %B0\n"
|
||||
"lsr %C0\n"
|
||||
"ror r19\n"
|
||||
"ror r18\n"
|
||||
"ror %B0\n"
|
||||
"mov %A0, __zero_reg__\n"
|
||||
"movw %C0, r18\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
: "r18", "r19"
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t lshift<15U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"movw r18, %A0\n"
|
||||
"lsr %C0\n"
|
||||
"ror r19\n"
|
||||
"ror r18\n"
|
||||
"mov %B0, __zero_reg__\n"
|
||||
"ror %B0\n"
|
||||
"mov %A0, __zero_reg__\n"
|
||||
"movw %C0, r18\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
: "r18", "r19"
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
///@}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Bitwise right shift - generic, unoptimized, case
|
||||
*
|
||||
* @tparam b number of bits to shift by
|
||||
* @param a value to shift
|
||||
* @return uint32_t
|
||||
*/
|
||||
template <uint8_t b>
|
||||
static inline uint32_t rshift(uint32_t a) {
|
||||
#if USE_OPTIMIZED_SHIFTS==1 // The shifts below have been validated to produce performant code in GCC.
|
||||
// Other shift amounts are either in a specialized template below (good) or are unvalidated (bad).
|
||||
static_assert(b==1 || b==2 || b==8 || b==16 || b==24,
|
||||
"Unvalidated shift - confirm gcc produces performant code");
|
||||
#endif
|
||||
return a >> b;
|
||||
}
|
||||
|
||||
#if USE_OPTIMIZED_SHIFTS==1
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
|
||||
/// @{
|
||||
/// @brief uint32_t bitwise right shift optimised for the specified shift distance
|
||||
///
|
||||
/// @note The assembler was generated using clang 17.0.1 cross compiling: -O3 --target=avr -mmcu=atmega2560. See https://godbolt.org/z/71cPnMYqs
|
||||
///
|
||||
/// @param a value to shift
|
||||
/// @return uint32_t a>>b
|
||||
template <>
|
||||
uint32_t rshift<3U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"lsr %D0\n"
|
||||
"ror %C0\n"
|
||||
"ror %B0\n"
|
||||
"ror %A0\n"
|
||||
"lsr %D0\n"
|
||||
"ror %C0\n"
|
||||
"ror %B0\n"
|
||||
"ror %A0\n"
|
||||
"lsr %D0\n"
|
||||
"ror %C0\n"
|
||||
"ror %B0\n"
|
||||
"ror %A0\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
:
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t rshift<4U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"swap %A0\n"
|
||||
"andi %A0, 15\n"
|
||||
"swap %B0\n"
|
||||
"eor %A0, %B0\n"
|
||||
"andi %B0, 15\n"
|
||||
"eor %A0, %B0\n"
|
||||
"swap %C0\n"
|
||||
"eor %B0, %C0\n"
|
||||
"andi %C0, 15\n"
|
||||
"eor %B0, %C0\n"
|
||||
"swap %D0\n"
|
||||
"eor %C0, %D0\n"
|
||||
"andi %D0, 15\n"
|
||||
"eor %C0, %D0\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
:
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t rshift<5U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"swap %A0\n"
|
||||
"andi %A0, 15\n"
|
||||
"swap %B0\n"
|
||||
"eor %A0, %B0\n"
|
||||
"andi %B0, 15\n"
|
||||
"eor %A0, %B0\n"
|
||||
"swap %C0\n"
|
||||
"eor %B0, %C0\n"
|
||||
"andi %C0, 15\n"
|
||||
"eor %B0, %C0\n"
|
||||
"swap %D0\n"
|
||||
"eor %C0, %D0\n"
|
||||
"andi %D0, 15\n"
|
||||
"eor %C0, %D0\n"
|
||||
"lsr %D0\n"
|
||||
"ror %C0\n"
|
||||
"ror %B0\n"
|
||||
"ror %A0\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
:
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t rshift<6U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"lsl %A0\n"
|
||||
"rol %B0\n"
|
||||
"rol %C0\n"
|
||||
"rol %D0\n"
|
||||
"mov r19, __zero_reg__\n"
|
||||
"rol r19\n"
|
||||
"lsl %A0\n"
|
||||
"rol %B0\n"
|
||||
"rol %C0\n"
|
||||
"rol %D0\n"
|
||||
"rol r19\n"
|
||||
"mov %A0, %B0\n"
|
||||
"mov %B0, %C0\n"
|
||||
"mov r18, %D0\n"
|
||||
"movw %C0, r18\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
: "r18", "r19"
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t rshift<7U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"lsl %A0\n"
|
||||
"rol %B0\n"
|
||||
"rol %C0\n"
|
||||
"rol %D0\n"
|
||||
"mov r19, __zero_reg__\n"
|
||||
"rol r19\n"
|
||||
"mov %A0, %B0\n"
|
||||
"mov %B0, %C0\n"
|
||||
"mov r18, %D0\n"
|
||||
"movw %C0, r18\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
: "r18", "r19"
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t rshift<9U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"lsr %D0\n"
|
||||
"ror %C0\n"
|
||||
"ror %B0\n"
|
||||
"mov %A0, %B0\n"
|
||||
"mov %B0, %C0\n"
|
||||
"mov %C0, %D0\n"
|
||||
"mov %D0, __zero_reg__\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
:
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t rshift<10U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"lsr %D0\n"
|
||||
"ror %C0\n"
|
||||
"ror %B0\n"
|
||||
"lsr %D0\n"
|
||||
"ror %C0\n"
|
||||
"ror %B0\n"
|
||||
"mov %A0, %B0\n"
|
||||
"mov %B0, %C0\n"
|
||||
"mov %C0, %D0\n"
|
||||
"mov %D0, __zero_reg__\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
:
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t rshift<11U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"lsr %D0\n"
|
||||
"ror %C0\n"
|
||||
"ror %B0\n"
|
||||
"lsr %D0\n"
|
||||
"ror %C0\n"
|
||||
"ror %B0\n"
|
||||
"lsr %D0\n"
|
||||
"ror %C0\n"
|
||||
"ror %B0\n"
|
||||
"mov %A0, %B0\n"
|
||||
"mov %B0, %C0\n"
|
||||
"mov %C0, %D0\n"
|
||||
"mov %D0, __zero_reg__\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
:
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t rshift<12U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"swap %B0\n"
|
||||
"andi %B0, 15\n"
|
||||
"swap %C0\n"
|
||||
"eor %B0, %C0\n"
|
||||
"andi %C0, 15\n"
|
||||
"eor %B0, %C0\n"
|
||||
"swap %D0\n"
|
||||
"eor %C0, %D0\n"
|
||||
"andi %D0, 15\n"
|
||||
"eor %C0, %D0\n"
|
||||
"mov %A0, %B0\n"
|
||||
"mov %B0, %C0\n"
|
||||
"mov %C0, %D0\n"
|
||||
"mov %D0, __zero_reg__\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
:
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t rshift<13U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"swap %B0\n"
|
||||
"andi %B0, 15\n"
|
||||
"swap %C0\n"
|
||||
"eor %B0, %C0\n"
|
||||
"andi %C0, 15\n"
|
||||
"eor %B0, %C0\n"
|
||||
"swap %D0\n"
|
||||
"eor %C0, %D0\n"
|
||||
"andi %D0, 15\n"
|
||||
"eor %C0, %D0\n"
|
||||
"lsr %D0\n"
|
||||
"ror %C0\n"
|
||||
"ror %B0\n"
|
||||
"mov %A0, %B0\n"
|
||||
"mov %B0, %C0\n"
|
||||
"mov %C0, %D0\n"
|
||||
"mov %D0, __zero_reg__\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
:
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t rshift<14U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"movw r18, %C0\n"
|
||||
"lsl %B0\n"
|
||||
"rol r18\n"
|
||||
"rol r19\n"
|
||||
"mov %C0, __zero_reg__\n"
|
||||
"rol %C0\n"
|
||||
"lsl %B0\n"
|
||||
"rol r18\n"
|
||||
"rol r19\n"
|
||||
"rol %C0\n"
|
||||
"mov %D0, __zero_reg__\n"
|
||||
"movw %A0, r18\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
: "r18", "r19"
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
template <>
|
||||
uint32_t rshift<15U>(uint32_t a)
|
||||
{
|
||||
asm(
|
||||
"movw r18, %C0\n"
|
||||
"lsl %B0\n"
|
||||
"rol r18\n"
|
||||
"rol r19\n"
|
||||
"mov %C0, __zero_reg__\n"
|
||||
"rol %C0\n"
|
||||
"mov %D0, __zero_reg__\n"
|
||||
"movw %A0, r18\n"
|
||||
: "=d" (a)
|
||||
: "0" (a)
|
||||
: "r18", "r19"
|
||||
);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Rounded arithmetic right shift
|
||||
*
|
||||
* Right shifting throws away bits. When use for fixed point division, this
|
||||
* effecitvely rounds down (towards zero). To round-to-the-nearest-integer
|
||||
* when right-shifting by S, just add in 2 power b−1 (which is the
|
||||
* fixed-point equivalent of 0.5) first
|
||||
*
|
||||
* @tparam b number of bits to shift by
|
||||
* @param a value to shift
|
||||
* @return uint32_t
|
||||
*/
|
||||
template <uint8_t b>
|
||||
static inline uint32_t rshift_round(uint32_t a) {
|
||||
return rshift<b>(a+(1UL<<(b-1UL)));
|
||||
}
|
||||
|
||||
///@}
|
|
@ -1,6 +1,6 @@
|
|||
#include "globals.h"
|
||||
#include "crankMaths.h"
|
||||
#include "decoders.h"
|
||||
#include "bit_shifts.h"
|
||||
|
||||
#define SECOND_DERIV_ENABLED 0
|
||||
|
||||
|
@ -10,51 +10,36 @@ byte deltaToothCount = 0; //The last tooth that was used with the deltaV calc
|
|||
int rpmDelta;
|
||||
#endif
|
||||
|
||||
uint32_t angleToTimeMicroSecPerDegree(uint16_t angle) {
|
||||
UQ24X8_t micros = (uint32_t)angle * (uint32_t)microsPerDegree;
|
||||
return RSHIFT_ROUND(micros, microsPerDegree_Shift);
|
||||
typedef uint32_t UQ24X8_t;
|
||||
static constexpr uint8_t UQ24X8_Shift = 8U;
|
||||
|
||||
/** @brief uS per degree at current RPM in UQ24.8 fixed point */
|
||||
static UQ24X8_t microsPerDegree;
|
||||
static constexpr uint8_t microsPerDegree_Shift = UQ24X8_Shift;
|
||||
|
||||
typedef uint16_t UQ1X15_t;
|
||||
static constexpr uint8_t UQ1X15_Shift = 15U;
|
||||
|
||||
/** @brief Degrees per uS in UQ1.15 fixed point.
|
||||
*
|
||||
* Ranges from 8 (0.000246) at MIN_RPM to 3542 (0.108) at MAX_RPM
|
||||
*/
|
||||
static UQ1X15_t degreesPerMicro;
|
||||
static constexpr uint8_t degreesPerMicro_Shift = UQ1X15_Shift;
|
||||
|
||||
void setAngleConverterRevolutionTime(uint32_t revolutionTime) {
|
||||
microsPerDegree = div360(lshift<microsPerDegree_Shift>(revolutionTime));
|
||||
degreesPerMicro = (uint16_t)UDIV_ROUND_CLOSEST(lshift<degreesPerMicro_Shift>(UINT32_C(360)), revolutionTime, uint32_t);
|
||||
}
|
||||
|
||||
uint32_t angleToTimeIntervalTooth(uint16_t angle) {
|
||||
noInterrupts();
|
||||
if(BIT_CHECK(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT))
|
||||
{
|
||||
unsigned long toothTime = (toothLastToothTime - toothLastMinusOneToothTime);
|
||||
uint16_t tempTriggerToothAngle = triggerToothAngle; // triggerToothAngle is set by interrupts
|
||||
interrupts();
|
||||
|
||||
return (toothTime * (uint32_t)angle) / tempTriggerToothAngle;
|
||||
}
|
||||
//Safety check. This can occur if the last tooth seen was outside the normal pattern etc
|
||||
else {
|
||||
interrupts();
|
||||
return angleToTimeMicroSecPerDegree(angle);
|
||||
}
|
||||
uint32_t angleToTimeMicroSecPerDegree(uint16_t angle) {
|
||||
UQ24X8_t micros = (uint32_t)angle * (uint32_t)microsPerDegree;
|
||||
return rshift_round<microsPerDegree_Shift>(micros);
|
||||
}
|
||||
|
||||
uint16_t timeToAngleDegPerMicroSec(uint32_t time) {
|
||||
uint32_t degFixed = time * (uint32_t)degreesPerMicro;
|
||||
return RSHIFT_ROUND(degFixed, degreesPerMicro_Shift);
|
||||
}
|
||||
|
||||
|
||||
uint16_t timeToAngleIntervalTooth(uint32_t time)
|
||||
{
|
||||
noInterrupts();
|
||||
//Still uses a last interval method (ie retrospective), but bases the interval on the gap between the 2 most recent teeth rather than the last full revolution
|
||||
if(BIT_CHECK(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT))
|
||||
{
|
||||
unsigned long toothTime = (toothLastToothTime - toothLastMinusOneToothTime);
|
||||
uint16_t tempTriggerToothAngle = triggerToothAngle; // triggerToothAngle is set by interrupts
|
||||
interrupts();
|
||||
|
||||
return (unsigned long)(time * (uint32_t)tempTriggerToothAngle) / toothTime;
|
||||
}
|
||||
else {
|
||||
interrupts();
|
||||
//Safety check. This can occur if the last tooth seen was outside the normal pattern etc
|
||||
return timeToAngleDegPerMicroSec(time);
|
||||
}
|
||||
return rshift_round<degreesPerMicro_Shift>(degFixed);
|
||||
}
|
||||
|
||||
#if SECOND_DERIV_ENABLED!=0
|
||||
|
@ -92,7 +77,7 @@ void doCrankSpeedCalcs(void)
|
|||
uint32_t toothDeltaT = toothHistory[toothHistoryIndex];
|
||||
//long timeToLastTooth = micros() - toothLastToothTime;
|
||||
|
||||
rpmDelta = (toothDeltaV << 10) / (6 * toothDeltaT);
|
||||
rpmDelta = lshift<10>(toothDeltaV) / (6 * toothDeltaT);
|
||||
}
|
||||
|
||||
timePerDegreex16 = ldiv( 2666656L, currentStatus.RPM + rpmDelta).quot; //This gives accuracy down to 0.1 of a degree and can provide noticeably better timing results on low resolution triggers
|
||||
|
|
|
@ -46,6 +46,13 @@ static inline int16_t injectorLimits(int16_t angle)
|
|||
*/
|
||||
#define MIN_RPM ((MICROS_PER_DEG_1_RPM/(UINT16_MAX/16UL))+1UL)
|
||||
|
||||
/**
|
||||
* @brief Set the revolution time, from which some of the degree<-->angle conversions are derived
|
||||
*
|
||||
* @param revolutionTime The crank revolution time.
|
||||
*/
|
||||
void setAngleConverterRevolutionTime(uint32_t revolutionTime);
|
||||
|
||||
/**
|
||||
* @name Converts angular degrees to the time interval that amount of rotation
|
||||
* will take at current RPM.
|
||||
|
@ -56,39 +63,17 @@ static inline int16_t injectorLimits(int16_t angle)
|
|||
* @param angle Angle in degrees
|
||||
* @return Time interval in uS
|
||||
*/
|
||||
///@{
|
||||
/** @brief Converts based on the time one degree of rotation takes
|
||||
*
|
||||
* Inverse of timeToAngleDegPerMicroSec
|
||||
*/
|
||||
uint32_t angleToTimeMicroSecPerDegree(uint16_t angle);
|
||||
|
||||
/** @brief Converts based on the time interval between the 2 most recently detected decoder teeth
|
||||
*
|
||||
* Inverse of timeToAngleIntervalTooth
|
||||
*/
|
||||
uint32_t angleToTimeIntervalTooth(uint16_t angle);
|
||||
///@}
|
||||
|
||||
/**
|
||||
* @name Converts a time interval in microsecods to the equivalent degrees of angular (crank)
|
||||
* rotation at current RPM.
|
||||
*
|
||||
* Inverse of angleToTimeMicroSecPerDegree
|
||||
*
|
||||
* @param time Time interval in uS
|
||||
* @return Angle in degrees
|
||||
*/
|
||||
///@{
|
||||
/** @brief Converts based on the the interval on time one degree of rotation takes
|
||||
*
|
||||
* Inverse of angleToTimeMicroSecPerDegree
|
||||
*/
|
||||
uint16_t timeToAngleDegPerMicroSec(uint32_t time);
|
||||
|
||||
/** @brief Converts based on the time interval between the 2 most recently detected decoder teeth
|
||||
*
|
||||
* Inverse of angleToTimeIntervalTooth
|
||||
*/
|
||||
uint16_t timeToAngleIntervalTooth(uint32_t time);
|
||||
///@}
|
||||
|
||||
#endif
|
|
@ -100,9 +100,6 @@ volatile unsigned long triggerThirdFilterTime; // The shortest time (in uS) that
|
|||
|
||||
volatile uint8_t decoderState = 0;
|
||||
|
||||
UQ24X8_t microsPerDegree;
|
||||
UQ1X15_t degreesPerMicro;
|
||||
|
||||
unsigned int triggerSecFilterTime_duration; // The shortest valid time (in uS) pulse DURATION
|
||||
volatile uint16_t triggerToothAngle; //The number of crank degrees that elapse per tooth
|
||||
byte checkSyncToothCount; //How many teeth must've been seen on this revolution before we try to confirm sync (Useful for missing tooth type decoders)
|
||||
|
@ -293,6 +290,47 @@ void loggerTertiaryISR(void)
|
|||
}
|
||||
}
|
||||
|
||||
#if false
|
||||
#if !defined(UNIT_TEST)
|
||||
static
|
||||
#endif
|
||||
uint32_t angleToTimeIntervalTooth(uint16_t angle) {
|
||||
noInterrupts();
|
||||
if(BIT_CHECK(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT))
|
||||
{
|
||||
unsigned long toothTime = (toothLastToothTime - toothLastMinusOneToothTime);
|
||||
uint16_t tempTriggerToothAngle = triggerToothAngle; // triggerToothAngle is set by interrupts
|
||||
interrupts();
|
||||
|
||||
return (toothTime * (uint32_t)angle) / tempTriggerToothAngle;
|
||||
}
|
||||
//Safety check. This can occur if the last tooth seen was outside the normal pattern etc
|
||||
else {
|
||||
interrupts();
|
||||
return angleToTimeMicroSecPerDegree(angle);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static uint16_t timeToAngleIntervalTooth(uint32_t time)
|
||||
{
|
||||
noInterrupts();
|
||||
//Still uses a last interval method (ie retrospective), but bases the interval on the gap between the 2 most recent teeth rather than the last full revolution
|
||||
if(BIT_CHECK(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT))
|
||||
{
|
||||
unsigned long toothTime = (toothLastToothTime - toothLastMinusOneToothTime);
|
||||
uint16_t tempTriggerToothAngle = triggerToothAngle; // triggerToothAngle is set by interrupts
|
||||
interrupts();
|
||||
|
||||
return (unsigned long)(time * (uint32_t)tempTriggerToothAngle) / toothTime;
|
||||
}
|
||||
else {
|
||||
interrupts();
|
||||
//Safety check. This can occur if the last tooth seen was outside the normal pattern etc
|
||||
return timeToAngleDegPerMicroSec(time);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool IsCranking(const statuses &status) {
|
||||
return (status.RPM < status.crankRPM) && (status.startRevolutions == 0U);
|
||||
}
|
||||
|
@ -305,8 +343,7 @@ static __attribute__((noinline)) bool SetRevolutionTime(uint32_t revTime)
|
|||
{
|
||||
if (revTime!=revolutionTime) {
|
||||
revolutionTime = revTime;
|
||||
microsPerDegree = div360(revolutionTime << microsPerDegree_Shift);
|
||||
degreesPerMicro = (uint16_t)UDIV_ROUND_CLOSEST((UINT32_C(360) << degreesPerMicro_Shift), revolutionTime, uint32_t);
|
||||
setAngleConverterRevolutionTime(revolutionTime);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -1470,7 +1507,7 @@ void triggerPri_4G63(void)
|
|||
if(configPage2.nCylinders == 4)
|
||||
{
|
||||
triggerToothAngle = 110;
|
||||
triggerFilterTime = (curGap * 3) >> 3; //Trigger filter is set to (110*3)/8=41.25=41 degrees (Next trigger is 70 degrees away).
|
||||
triggerFilterTime = rshift<3>(curGap * 3UL); //Trigger filter is set to (110*3)/8=41.25=41 degrees (Next trigger is 70 degrees away).
|
||||
}
|
||||
else if(configPage2.nCylinders == 6)
|
||||
{
|
||||
|
@ -1516,7 +1553,7 @@ void triggerPri_4G63(void)
|
|||
triggerToothAngle = 70;
|
||||
if(configPage2.nCylinders == 4)
|
||||
{
|
||||
triggerFilterTime = (curGap * 11) >> 3;//96.26 degrees with a target of 110
|
||||
triggerFilterTime = rshift<3>(curGap * 11UL);//96.26 degrees with a target of 110
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1528,7 +1565,7 @@ void triggerPri_4G63(void)
|
|||
if(configPage2.nCylinders == 4)
|
||||
{
|
||||
triggerToothAngle = 110;
|
||||
triggerFilterTime = (curGap * 9) >> 5; //61.87 degrees with a target of 70
|
||||
triggerFilterTime = rshift<5>(curGap * 9UL); //61.87 degrees with a target of 70
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2451,7 +2488,7 @@ void triggerPri_Miata9905(void)
|
|||
{
|
||||
//Lite filter
|
||||
if( (toothCurrentCount == 1) || (toothCurrentCount == 3) || (toothCurrentCount == 5) || (toothCurrentCount == 7) ) { triggerToothAngle = 70; triggerFilterTime = curGap; } //Trigger filter is set to whatever time it took to do 70 degrees (Next trigger is 110 degrees away)
|
||||
else { triggerToothAngle = 110; triggerFilterTime = (curGap * 3) >> 3; } //Trigger filter is set to (110*3)/8=41.25=41 degrees (Next trigger is 70 degrees away).
|
||||
else { triggerToothAngle = 110; triggerFilterTime = rshift<3>(curGap * 3UL); } //Trigger filter is set to (110*3)/8=41.25=41 degrees (Next trigger is 70 degrees away).
|
||||
}
|
||||
else if(configPage4.triggerFilter == 2)
|
||||
{
|
||||
|
@ -2462,8 +2499,8 @@ void triggerPri_Miata9905(void)
|
|||
else if (configPage4.triggerFilter == 3)
|
||||
{
|
||||
//Aggressive filter level
|
||||
if( (toothCurrentCount == 1) || (toothCurrentCount == 3) || (toothCurrentCount == 5) || (toothCurrentCount == 7) ) { triggerToothAngle = 70; triggerFilterTime = (curGap * 11) >> 3 ; } //96.26 degrees with a target of 110
|
||||
else { triggerToothAngle = 110; triggerFilterTime = (curGap * 9) >> 5; } //61.87 degrees with a target of 70
|
||||
if( (toothCurrentCount == 1) || (toothCurrentCount == 3) || (toothCurrentCount == 5) || (toothCurrentCount == 7) ) { triggerToothAngle = 70; triggerFilterTime = rshift<3>(curGap * 11UL) ; } //96.26 degrees with a target of 110
|
||||
else { triggerToothAngle = 110; triggerFilterTime = rshift<5>(curGap * 9UL); } //61.87 degrees with a target of 70
|
||||
}
|
||||
else if (configPage4.triggerFilter == 0)
|
||||
{
|
||||
|
@ -2695,7 +2732,7 @@ void triggerPri_MazdaAU(void)
|
|||
|
||||
//Whilst this is an uneven tooth pattern, if the specific angle between the last 2 teeth is specified, 1st deriv prediction can be used
|
||||
if( (toothCurrentCount == 1) || (toothCurrentCount == 3) ) { triggerToothAngle = 72; triggerFilterTime = curGap; } //Trigger filter is set to whatever time it took to do 72 degrees (Next trigger is 108 degrees away)
|
||||
else { triggerToothAngle = 108; triggerFilterTime = (curGap * 3) >> 3; } //Trigger filter is set to (108*3)/8=40 degrees (Next trigger is 70 degrees away).
|
||||
else { triggerToothAngle = 108; triggerFilterTime = rshift<3>(curGap * 3UL); } //Trigger filter is set to (108*3)/8=40 degrees (Next trigger is 70 degrees away).
|
||||
|
||||
toothLastMinusOneToothTime = toothLastToothTime;
|
||||
toothLastToothTime = curTime;
|
||||
|
@ -5709,13 +5746,13 @@ void triggerPri_SuzukiK6A(void)
|
|||
switch (configPage4.triggerFilter)
|
||||
{
|
||||
case 1: // 25 % 17 degrees
|
||||
triggerFilterTime = curGap>>3;
|
||||
triggerFilterTime = rshift<3>(curGap);
|
||||
break;
|
||||
case 2: // 50 % 35 degrees
|
||||
triggerFilterTime = (curGap>>3) + (curGap>>4);
|
||||
triggerFilterTime = rshift<3>(curGap) + rshift<4>(curGap);
|
||||
break;
|
||||
case 3: // 75 % 52 degrees
|
||||
triggerFilterTime = (curGap>>2) + (curGap>>4);
|
||||
triggerFilterTime = rshift<2>(curGap) + rshift<4>(curGap);
|
||||
break;
|
||||
default:
|
||||
triggerFilterTime = 0;
|
||||
|
@ -5728,13 +5765,13 @@ void triggerPri_SuzukiK6A(void)
|
|||
switch (configPage4.triggerFilter)
|
||||
{
|
||||
case 1: // 25 % 8 degrees
|
||||
triggerFilterTime = curGap>>3;
|
||||
triggerFilterTime = rshift<3>(curGap);
|
||||
break;
|
||||
case 2: // 50 % 17 degrees
|
||||
triggerFilterTime = curGap>>2;
|
||||
triggerFilterTime = rshift<2>(curGap);
|
||||
break;
|
||||
case 3: // 75 % 25 degrees
|
||||
triggerFilterTime = (curGap>>2) + (curGap>>3);
|
||||
triggerFilterTime = rshift<2>(curGap) + rshift<3>(curGap);
|
||||
break;
|
||||
default:
|
||||
triggerFilterTime = 0;
|
||||
|
@ -5766,13 +5803,13 @@ void triggerPri_SuzukiK6A(void)
|
|||
switch (configPage4.triggerFilter)
|
||||
{
|
||||
case 1: // 25 % 17 degrees
|
||||
triggerFilterTime = curGap>>3;
|
||||
triggerFilterTime = rshift<3>(curGap);
|
||||
break;
|
||||
case 2: // 50 % 35 degrees
|
||||
triggerFilterTime = curGap>>2;
|
||||
triggerFilterTime = rshift<2>(curGap);
|
||||
break;
|
||||
case 3: // 75 % 52 degrees
|
||||
triggerFilterTime = (curGap>>2) + (curGap>>3);
|
||||
triggerFilterTime = rshift<2>(curGap) + rshift<3>(curGap);
|
||||
break;
|
||||
default:
|
||||
triggerFilterTime = 0;
|
||||
|
@ -5786,13 +5823,13 @@ void triggerPri_SuzukiK6A(void)
|
|||
switch (configPage4.triggerFilter)
|
||||
{
|
||||
case 1: // 25 % 42 degrees
|
||||
triggerFilterTime = (curGap>>1) + (curGap>>3);
|
||||
triggerFilterTime = rshift<1>(curGap) + rshift<3>(curGap);
|
||||
break;
|
||||
case 2: // 50 % 85 degrees
|
||||
triggerFilterTime = curGap + (curGap>>2);
|
||||
triggerFilterTime = curGap + rshift<2>(curGap);
|
||||
break;
|
||||
case 3: // 75 % 127 degrees
|
||||
triggerFilterTime = curGap + (curGap>>1) + (curGap>>2);
|
||||
triggerFilterTime = curGap + rshift<1>(curGap) + rshift<2>(curGap);
|
||||
break;
|
||||
default:
|
||||
triggerFilterTime = 0;
|
||||
|
|
|
@ -303,23 +303,6 @@ extern unsigned long elapsedTime;
|
|||
extern unsigned long lastCrankAngleCalc;
|
||||
extern unsigned long lastVVTtime; //The time between the vvt reference pulse and the last crank pulse
|
||||
|
||||
typedef uint32_t UQ24X8_t;
|
||||
constexpr uint8_t UQ24X8_Shift = 8U;
|
||||
|
||||
/** @brief uS per degree at current RPM in UQ24.8 fixed point */
|
||||
extern UQ24X8_t microsPerDegree;
|
||||
constexpr uint8_t microsPerDegree_Shift = UQ24X8_Shift;
|
||||
|
||||
typedef uint16_t UQ1X15_t;
|
||||
constexpr uint8_t UQ1X15_Shift = 15U;
|
||||
|
||||
/** @brief Degrees per uS in UQ1.15 fixed point.
|
||||
*
|
||||
* Ranges from 8 (0.000246) at MIN_RPM to 3542 (0.108) at MAX_RPM
|
||||
*/
|
||||
extern UQ1X15_t degreesPerMicro;
|
||||
constexpr uint8_t degreesPerMicro_Shift = UQ1X15_Shift;
|
||||
|
||||
extern uint16_t ignition1EndTooth;
|
||||
extern uint16_t ignition2EndTooth;
|
||||
extern uint16_t ignition3EndTooth;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
#include "globals.h"
|
||||
#include "bit_shifts.h"
|
||||
|
||||
#ifdef USE_LIBDIVIDE
|
||||
// We use pre-computed constant parameters with libdivide where possible.
|
||||
|
@ -91,26 +92,19 @@ extern uint8_t random1to100(void);
|
|||
*/
|
||||
#define UDIV_ROUND_CLOSEST(n, d, t) ((t)((n) + DIV_ROUND_CORRECT(d, t))/(t)(d))
|
||||
|
||||
/**
|
||||
* @brief Rounded arithmetic right shift
|
||||
*
|
||||
* Right shifting throws away bits. When use for fixed point division, this
|
||||
* effectively rounds down (towards zero). To round-to-the-nearest-integer
|
||||
* when right-shifting by S, just add in 2 power S−1 (which is the
|
||||
* fixed-point equivalent of 0.5) first
|
||||
*
|
||||
* @param n The value to shift
|
||||
* @param s The shift distance in bits
|
||||
*/
|
||||
#define RSHIFT_ROUND(n, s) (((n)+(1UL<<(s-1UL)))>>(s))
|
||||
///@}
|
||||
|
||||
/** @brief Test whether the parameter is an integer or not. */
|
||||
#define IS_INTEGER(d) ((d) == (int32_t)(d))
|
||||
|
||||
/**
|
||||
* @defgroup group-div100 Optimised integer division by 100
|
||||
/**
|
||||
* @{
|
||||
* @brief Performance optimised integer division by 100. I.e. same as n/100
|
||||
*
|
||||
* Uses the rounding behaviour controlled by @ref DIV_ROUND_BEHAVIOR
|
||||
*
|
||||
* @param n Dividend to divide by 100
|
||||
* @return n/100, with rounding behavior applied
|
||||
*/
|
||||
static inline uint16_t div100(uint16_t n) {
|
||||
// As of avr-gcc 5.4.0, the compiler will optimize this to a multiply/shift
|
||||
|
@ -223,9 +217,9 @@ static inline int16_t nudge(int16_t min, int16_t max, int16_t value, int16_t nud
|
|||
|
||||
//This is a dedicated function that specifically handles the case of mapping 0-1023 values into a 0 to X range
|
||||
//This is a common case because it means converting from a standard 10-bit analog input to a byte or 10-bit analog into 0-511 (Eg the temperature readings)
|
||||
#define fastMap1023toX(x, out_max) ( ((uint32_t)(x) * (out_max)) >> 10)
|
||||
#define fastMap1023toX(x, out_max) ( rshift<10>((uint32_t)(x) * (out_max)) )
|
||||
//This is a new version that allows for out_min
|
||||
#define fastMap10Bit(x, out_min, out_max) ( ( ((uint32_t)(x) * ((out_max)-(out_min))) >> 10 ) + (out_min))
|
||||
#define fastMap10Bit(x, out_min, out_max) ( rshift<10>( (uint32_t)(x) * ((out_max)-(out_min)) ) + (out_min))
|
||||
|
||||
#if defined(CORE_AVR) || defined(ARDUINO_ARCH_AVR)
|
||||
|
||||
|
|
|
@ -1199,47 +1199,47 @@ uint16_t PW(int REQ_FUEL, byte VE, long MAP, uint16_t corrections, int injOpen)
|
|||
//Standard float version of the calculation
|
||||
//return (REQ_FUEL * (float)(VE/100.0) * (float)(MAP/100.0) * (float)(TPS/100.0) * (float)(corrections/100.0) + injOpen);
|
||||
//Note: The MAP and TPS portions are currently disabled, we use VE and corrections only
|
||||
uint16_t iVE, iCorrections;
|
||||
uint16_t iVE;
|
||||
uint16_t iMAP = 100;
|
||||
uint16_t iAFR = 147;
|
||||
|
||||
//100% float free version, does sacrifice a little bit of accuracy, but not much.
|
||||
|
||||
//If corrections are huge, use less bitshift to avoid overflow. Sacrifices a bit more accuracy (basically only during very cold temp cranking)
|
||||
byte bitShift = 7;
|
||||
if (corrections > 511 ) { bitShift = 6; }
|
||||
if (corrections > 1023) { bitShift = 5; }
|
||||
|
||||
|
||||
//iVE = ((unsigned int)VE << 7) / 100;
|
||||
iVE = div100(((uint16_t)VE << 7));
|
||||
iVE = div100(((uint16_t)VE << 7U));
|
||||
|
||||
//Check whether either of the multiply MAP modes is turned on
|
||||
//if ( configPage2.multiplyMAP == MULTIPLY_MAP_MODE_100) { iMAP = ((unsigned int)MAP << 7) / 100; }
|
||||
if ( configPage2.multiplyMAP == MULTIPLY_MAP_MODE_100) { iMAP = div100( ((uint16_t)MAP << 7U) ); }
|
||||
else if( configPage2.multiplyMAP == MULTIPLY_MAP_MODE_BARO) { iMAP = ((unsigned int)MAP << 7) / currentStatus.baro; }
|
||||
else if( configPage2.multiplyMAP == MULTIPLY_MAP_MODE_BARO) { iMAP = ((unsigned int)MAP << 7U) / currentStatus.baro; }
|
||||
|
||||
if ( (configPage2.includeAFR == true) && (configPage6.egoType == EGO_TYPE_WIDE) && (currentStatus.runSecs > configPage6.ego_sdelay) ) {
|
||||
iAFR = ((unsigned int)currentStatus.O2 << 7) / currentStatus.afrTarget; //Include AFR (vs target) if enabled
|
||||
iAFR = ((unsigned int)currentStatus.O2 << 7U) / currentStatus.afrTarget; //Include AFR (vs target) if enabled
|
||||
}
|
||||
if ( (configPage2.incorporateAFR == true) && (configPage2.includeAFR == false) ) {
|
||||
iAFR = ((unsigned int)configPage2.stoich << 7) / currentStatus.afrTarget; //Incorporate stoich vs target AFR, if enabled.
|
||||
iAFR = ((unsigned int)configPage2.stoich << 7U) / currentStatus.afrTarget; //Incorporate stoich vs target AFR, if enabled.
|
||||
}
|
||||
//iCorrections = (corrections << bitShift) / 100;
|
||||
iCorrections = div100((corrections << bitShift));
|
||||
|
||||
|
||||
uint32_t intermediate = ((uint32_t)REQ_FUEL * (uint32_t)iVE) >> 7; //Need to use an intermediate value to avoid overflowing the long
|
||||
if ( configPage2.multiplyMAP > 0 ) { intermediate = (intermediate * (uint32_t)iMAP) >> 7; }
|
||||
uint32_t intermediate = rshift<7U>((uint32_t)REQ_FUEL * (uint32_t)iVE); //Need to use an intermediate value to avoid overflowing the long
|
||||
if ( configPage2.multiplyMAP > 0 ) { intermediate = rshift<7U>(intermediate * (uint32_t)iMAP); }
|
||||
|
||||
if ( (configPage2.includeAFR == true) && (configPage6.egoType == EGO_TYPE_WIDE) && (currentStatus.runSecs > configPage6.ego_sdelay) ) {
|
||||
//EGO type must be set to wideband and the AFR warmup time must've elapsed for this to be used
|
||||
intermediate = (intermediate * (uint32_t)iAFR) >> 7;
|
||||
intermediate = rshift<7U>(intermediate * (uint32_t)iAFR);
|
||||
}
|
||||
if ( (configPage2.incorporateAFR == true) && (configPage2.includeAFR == false) ) {
|
||||
intermediate = (intermediate * (uint32_t)iAFR) >> 7;
|
||||
intermediate = rshift<7U>(intermediate * (uint32_t)iAFR);
|
||||
}
|
||||
|
||||
intermediate = (intermediate * (uint32_t)iCorrections) >> bitShift;
|
||||
|
||||
//If corrections are huge, use less bitshift to avoid overflow. Sacrifices a bit more accuracy (basically only during very cold temp cranking)
|
||||
if (corrections < 512 ) {
|
||||
intermediate = rshift<7U>(intermediate * div100(lshift<7U>(corrections)));
|
||||
} else if (corrections < 1024 ) {
|
||||
intermediate = rshift<6U>(intermediate * div100(lshift<6U>(corrections)));
|
||||
} else {
|
||||
intermediate = rshift<5U>(intermediate * div100(lshift<5U>(corrections)));
|
||||
}
|
||||
|
||||
if (intermediate != 0)
|
||||
{
|
||||
//If intermediate is not 0, we need to add the opening time (0 typically indicates that one of the full fuel cuts is active)
|
||||
|
|
|
@ -320,11 +320,10 @@ void oneMSInterval(void) //Most ARM chips can simply call a function
|
|||
currentStatus.ethanolPct = ADC_FILTER(tempEthPct, configPage4.FILTER_FLEX, currentStatus.ethanolPct);
|
||||
|
||||
//Continental flex sensor fuel temperature can be read with following formula: (Temperature = (41.25 * pulse width(ms)) - 81.25). 1000μs = -40C and 5000μs = 125C
|
||||
if(flexPulseWidth > 5000) { flexPulseWidth = 5000; }
|
||||
else if(flexPulseWidth < 1000) { flexPulseWidth = 1000; }
|
||||
currentStatus.fuelTemp = div100( (int16_t)(((4224 * (long)flexPulseWidth) >> 10) - 8125) );
|
||||
flexPulseWidth = constrain(flexPulseWidth, 1000UL, 5000UL);
|
||||
int32_t tempX100 = (int32_t)rshift<10>(4224UL * flexPulseWidth) - 8125L; //Split up for MISRA compliance
|
||||
currentStatus.fuelTemp = div100((int16_t)tempX100);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Turn off any of the pulsed testing outputs if they are active and have been running for long enough
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
extern void testPercent(void);
|
||||
extern void testDivision(void);
|
||||
extern void testBitShift(void);
|
||||
|
||||
#define UNITY_EXCLUDE_DETAILS
|
||||
|
||||
|
@ -20,6 +21,7 @@ void setup()
|
|||
testCrankMaths();
|
||||
testPercent();
|
||||
testDivision();
|
||||
testBitShift();
|
||||
|
||||
UNITY_END(); // stop unit testing
|
||||
}
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
#include <unity.h>
|
||||
#include <Arduino.h>
|
||||
#include "bit_shifts.h"
|
||||
#include "maths.h"
|
||||
#include "../timer.hpp"
|
||||
#include "../test_utils.h"
|
||||
|
||||
template <uint8_t shiftDistance>
|
||||
static void test_lshift(void) {
|
||||
uint32_t value = 33333;
|
||||
char szMsg[16];
|
||||
sprintf(szMsg, "%" PRIu8, shiftDistance);
|
||||
TEST_ASSERT_EQUAL_MESSAGE(value << shiftDistance, lshift<shiftDistance>(value), szMsg);
|
||||
}
|
||||
|
||||
static void test_LShift()
|
||||
{
|
||||
test_lshift<1U>();
|
||||
test_lshift<2U>();
|
||||
test_lshift<3U>();
|
||||
test_lshift<4U>();
|
||||
test_lshift<5U>();
|
||||
test_lshift<6U>();
|
||||
test_lshift<7U>();
|
||||
test_lshift<8U>();
|
||||
test_lshift<9U>();
|
||||
test_lshift<10U>();
|
||||
test_lshift<11U>();
|
||||
test_lshift<12U>();
|
||||
test_lshift<13U>();
|
||||
test_lshift<14U>();
|
||||
test_lshift<15U>();
|
||||
}
|
||||
|
||||
template <uint8_t shiftDistance>
|
||||
static void test_rshift(void) {
|
||||
uint32_t value = 33333;
|
||||
char szMsg[16];
|
||||
sprintf(szMsg, "%" PRIu8, shiftDistance);
|
||||
TEST_ASSERT_EQUAL_MESSAGE(value >> shiftDistance, rshift<shiftDistance>(value), szMsg);
|
||||
}
|
||||
|
||||
void test_RShift()
|
||||
{
|
||||
test_rshift<1U>();
|
||||
test_rshift<2U>();
|
||||
test_rshift<3U>();
|
||||
test_rshift<4U>();
|
||||
test_rshift<5U>();
|
||||
test_rshift<6U>();
|
||||
test_rshift<7U>();
|
||||
test_rshift<8U>();
|
||||
test_rshift<9U>();
|
||||
test_rshift<10U>();
|
||||
test_rshift<11U>();
|
||||
test_rshift<12U>();
|
||||
test_rshift<13U>();
|
||||
test_rshift<14U>();
|
||||
test_rshift<15U>();
|
||||
}
|
||||
|
||||
static uint32_t seedValue;
|
||||
|
||||
// Force no inline, or compiler will optimize shifts away
|
||||
// (which it won't do in normal operaton when the left shift operand is unknown at compile time.)
|
||||
static void __attribute__((noinline)) nativeTest(uint8_t index, uint32_t &checkSum) {
|
||||
if (index==1U) { checkSum = seedValue; }
|
||||
if (index==4U) { checkSum += checkSum >> 4U; }
|
||||
if (index==5U) { checkSum += checkSum >> 5U; }
|
||||
if (index==6U) { checkSum += checkSum >> 6U; }
|
||||
if (index==7U) { checkSum += checkSum >> 7U; }
|
||||
if (index==9U) { checkSum += checkSum >> 9U; }
|
||||
if (index==10U) { checkSum += checkSum >> 10U; }
|
||||
if (index==11U) { checkSum += checkSum >> 11U; }
|
||||
if (index==12U) { checkSum += checkSum >> 12U; }
|
||||
if (index==13U) { checkSum += checkSum >> 13U; }
|
||||
if (index==14U) { checkSum += checkSum >> 14U; }
|
||||
if (index==15U) { checkSum += checkSum >> 15U; }
|
||||
};
|
||||
static void __attribute__((noinline)) optimizedTest(uint8_t index, uint32_t &checkSum) {
|
||||
if (index==1U) { checkSum = seedValue; }
|
||||
if (index==4U) { checkSum += rshift<4U>(checkSum); }
|
||||
if (index==5U) { checkSum += rshift<5U>(checkSum); }
|
||||
if (index==6U) { checkSum += rshift<6U>(checkSum); }
|
||||
if (index==7U) { checkSum += rshift<7U>(checkSum); }
|
||||
if (index==9U) { checkSum += rshift<9U>(checkSum); }
|
||||
if (index==10U) { checkSum += rshift<10U>(checkSum); }
|
||||
if (index==11U) { checkSum += rshift<11U>(checkSum); }
|
||||
if (index==12U) { checkSum += rshift<12U>(checkSum); }
|
||||
if (index==13U) { checkSum += rshift<13U>(checkSum); }
|
||||
if (index==14U) { checkSum += rshift<14U>(checkSum); }
|
||||
if (index==15U) { checkSum += rshift<15U>(checkSum); }
|
||||
};
|
||||
|
||||
static void test_rshift_perf(void) {
|
||||
constexpr uint16_t iters = 128;
|
||||
constexpr uint8_t start_index = 1;
|
||||
constexpr uint8_t end_index = 16;
|
||||
constexpr uint8_t step = 1;
|
||||
|
||||
seedValue = rand();
|
||||
|
||||
TEST_MESSAGE("rshift ");
|
||||
auto comparison = compare_executiontime<uint8_t, uint32_t>(iters, start_index, end_index, step, nativeTest, optimizedTest);
|
||||
|
||||
// This must be here to force the compiler to run the loops above
|
||||
TEST_ASSERT_EQUAL(comparison.timeA.result, comparison.timeB.result);
|
||||
|
||||
TEST_ASSERT_LESS_THAN(comparison.timeA.durationMicros, comparison.timeB.durationMicros);
|
||||
}
|
||||
|
||||
void testBitShift() {
|
||||
SET_UNITY_FILENAME() {
|
||||
RUN_TEST(test_LShift);
|
||||
RUN_TEST(test_RShift);
|
||||
RUN_TEST(test_rshift_perf);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
#include <unity.h>
|
||||
// #include "globals.h"
|
||||
#include "crankMaths.h"
|
||||
#include "decoders.h"
|
||||
#include "../test_utils.h"
|
||||
|
||||
extern void SetRevolutionTime(uint32_t revTime);
|
||||
|
||||
|
@ -18,6 +18,7 @@ void test_crankmaths_angletotime_revolution_execute() {
|
|||
TEST_ASSERT_INT32_WITHIN(1, testdata->expected, angleToTimeMicroSecPerDegree(testdata->angle));
|
||||
}
|
||||
|
||||
#if false
|
||||
struct crankmaths_tooth_testdata {
|
||||
uint16_t rpm;
|
||||
uint16_t triggerToothAngle;
|
||||
|
@ -26,64 +27,70 @@ struct crankmaths_tooth_testdata {
|
|||
unsigned long expected;
|
||||
} *crankmaths_tooth_testdata_current;
|
||||
|
||||
extern uint32_t angleToTimeIntervalTooth(uint16_t angle);
|
||||
|
||||
void test_crankmaths_angletotime_tooth_execute() {
|
||||
crankmaths_tooth_testdata *testdata = crankmaths_tooth_testdata_current;
|
||||
triggerToothAngle = testdata->triggerToothAngle;
|
||||
toothLastToothTime = toothLastMinusOneToothTime + testdata->toothTime;
|
||||
TEST_ASSERT_EQUAL(testdata->expected, angleToTimeIntervalTooth(testdata->angle));
|
||||
}
|
||||
#endif
|
||||
|
||||
void testCrankMaths()
|
||||
{
|
||||
const byte testNameLength = 200;
|
||||
char testName[testNameLength];
|
||||
SET_UNITY_FILENAME() {
|
||||
constexpr byte testNameLength = 200;
|
||||
char testName[testNameLength];
|
||||
|
||||
const crankmaths_rev_testdata crankmaths_rev_testdatas[] = {
|
||||
{ .rpm = 50, .revolutionTime = 1200000, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 50, .revolutionTime = 1200000, .angle = 25, .expected = 83333 }, // 83333,3333
|
||||
{ .rpm = 50, .revolutionTime = 1200000, .angle = 720, .expected = 2400000 },
|
||||
{ .rpm = 2500, .revolutionTime = 24000, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 2500, .revolutionTime = 24000, .angle = 25, .expected = 1667 }, // 1666,6666
|
||||
{ .rpm = 2500, .revolutionTime = 24000, .angle = 720, .expected = 48000 },
|
||||
{ .rpm = 20000, .revolutionTime = 3000, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 20000, .revolutionTime = 3000, .angle = 25, .expected = 208 }, // 208,3333
|
||||
{ .rpm = 20000, .revolutionTime = 3000, .angle = 720, .expected = 6000 }
|
||||
};
|
||||
const crankmaths_rev_testdata crankmaths_rev_testdatas[] = {
|
||||
{ .rpm = 50, .revolutionTime = 1200000, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 50, .revolutionTime = 1200000, .angle = 25, .expected = 83333 }, // 83333,3333
|
||||
{ .rpm = 50, .revolutionTime = 1200000, .angle = 720, .expected = 2400000 },
|
||||
{ .rpm = 2500, .revolutionTime = 24000, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 2500, .revolutionTime = 24000, .angle = 25, .expected = 1667 }, // 1666,6666
|
||||
{ .rpm = 2500, .revolutionTime = 24000, .angle = 720, .expected = 48000 },
|
||||
{ .rpm = 20000, .revolutionTime = 3000, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 20000, .revolutionTime = 3000, .angle = 25, .expected = 208 }, // 208,3333
|
||||
{ .rpm = 20000, .revolutionTime = 3000, .angle = 720, .expected = 6000 }
|
||||
};
|
||||
|
||||
for (auto testdata : crankmaths_rev_testdatas) {
|
||||
crankmaths_rev_testdata_current = &testdata;
|
||||
snprintf(testName, testNameLength, "crankmaths/angletotime/revolution/%urpm/%uangle", testdata.rpm, testdata.angle);
|
||||
UnityDefaultTestRun(test_crankmaths_angletotime_revolution_execute, testName, __LINE__);
|
||||
for (auto testdata : crankmaths_rev_testdatas) {
|
||||
crankmaths_rev_testdata_current = &testdata;
|
||||
snprintf(testName, testNameLength, "crankmaths/angletotime/revolution/%urpm/%uangle", testdata.rpm, testdata.angle);
|
||||
UnityDefaultTestRun(test_crankmaths_angletotime_revolution_execute, testName, __LINE__);
|
||||
}
|
||||
|
||||
#if false
|
||||
const crankmaths_tooth_testdata crankmaths_tooth_testdatas[] = {
|
||||
{ .rpm = 50, .triggerToothAngle = 3, .toothTime = 10000, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 50, .triggerToothAngle = 3, .toothTime = 10000, .angle = 25, .expected = 83333 }, // 83333,3333
|
||||
{ .rpm = 50, .triggerToothAngle = 3, .toothTime = 10000, .angle = 720, .expected = 2400000 },
|
||||
{ .rpm = 2500, .triggerToothAngle = 3, .toothTime = 200, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 2500, .triggerToothAngle = 3, .toothTime = 200, .angle = 25, .expected = 1666 }, // 1666,6666
|
||||
{ .rpm = 2500, .triggerToothAngle = 3, .toothTime = 200, .angle = 720, .expected = 48000 },
|
||||
{ .rpm = 20000, .triggerToothAngle = 3, .toothTime = 25, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 20000, .triggerToothAngle = 3, .toothTime = 25, .angle = 25, .expected = 208 }, // 208,3333
|
||||
{ .rpm = 20000, .triggerToothAngle = 3, .toothTime = 25, .angle = 720, .expected = 6000 },
|
||||
{ .rpm = 50, .triggerToothAngle = 180, .toothTime = 600000, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 50, .triggerToothAngle = 180, .toothTime = 600000, .angle = 25, .expected = 83333 }, // 83333,3333
|
||||
{ .rpm = 50, .triggerToothAngle = 180, .toothTime = 600000, .angle = 720, .expected = 2400000 },
|
||||
{ .rpm = 2500, .triggerToothAngle = 180, .toothTime = 12000, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 2500, .triggerToothAngle = 180, .toothTime = 12000, .angle = 25, .expected = 1666 }, // 1666,6666
|
||||
{ .rpm = 2500, .triggerToothAngle = 180, .toothTime = 12000, .angle = 720, .expected = 48000 },
|
||||
{ .rpm = 20000, .triggerToothAngle = 180, .toothTime = 1500, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 20000, .triggerToothAngle = 180, .toothTime = 1500, .angle = 25, .expected = 208 }, // 208,3333
|
||||
{ .rpm = 20000, .triggerToothAngle = 180, .toothTime = 1500, .angle = 720, .expected = 6000 },
|
||||
};
|
||||
// The same for all tests
|
||||
BIT_SET(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT);
|
||||
toothLastMinusOneToothTime = 200000;
|
||||
|
||||
for (auto testdata : crankmaths_tooth_testdatas) {
|
||||
crankmaths_tooth_testdata_current = &testdata;
|
||||
snprintf(testName, testNameLength, "crankmaths/angletotime/tooth/%urpm/%uangle/%utoothangle", testdata.rpm, testdata.angle, testdata.triggerToothAngle);
|
||||
UnityDefaultTestRun(test_crankmaths_angletotime_tooth_execute, testName, __LINE__);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
const crankmaths_tooth_testdata crankmaths_tooth_testdatas[] = {
|
||||
{ .rpm = 50, .triggerToothAngle = 3, .toothTime = 10000, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 50, .triggerToothAngle = 3, .toothTime = 10000, .angle = 25, .expected = 83333 }, // 83333,3333
|
||||
{ .rpm = 50, .triggerToothAngle = 3, .toothTime = 10000, .angle = 720, .expected = 2400000 },
|
||||
{ .rpm = 2500, .triggerToothAngle = 3, .toothTime = 200, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 2500, .triggerToothAngle = 3, .toothTime = 200, .angle = 25, .expected = 1666 }, // 1666,6666
|
||||
{ .rpm = 2500, .triggerToothAngle = 3, .toothTime = 200, .angle = 720, .expected = 48000 },
|
||||
{ .rpm = 20000, .triggerToothAngle = 3, .toothTime = 25, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 20000, .triggerToothAngle = 3, .toothTime = 25, .angle = 25, .expected = 208 }, // 208,3333
|
||||
{ .rpm = 20000, .triggerToothAngle = 3, .toothTime = 25, .angle = 720, .expected = 6000 },
|
||||
{ .rpm = 50, .triggerToothAngle = 180, .toothTime = 600000, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 50, .triggerToothAngle = 180, .toothTime = 600000, .angle = 25, .expected = 83333 }, // 83333,3333
|
||||
{ .rpm = 50, .triggerToothAngle = 180, .toothTime = 600000, .angle = 720, .expected = 2400000 },
|
||||
{ .rpm = 2500, .triggerToothAngle = 180, .toothTime = 12000, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 2500, .triggerToothAngle = 180, .toothTime = 12000, .angle = 25, .expected = 1666 }, // 1666,6666
|
||||
{ .rpm = 2500, .triggerToothAngle = 180, .toothTime = 12000, .angle = 720, .expected = 48000 },
|
||||
{ .rpm = 20000, .triggerToothAngle = 180, .toothTime = 1500, .angle = 0, .expected = 0 },
|
||||
{ .rpm = 20000, .triggerToothAngle = 180, .toothTime = 1500, .angle = 25, .expected = 208 }, // 208,3333
|
||||
{ .rpm = 20000, .triggerToothAngle = 180, .toothTime = 1500, .angle = 720, .expected = 6000 },
|
||||
};
|
||||
// The same for all tests
|
||||
BIT_SET(decoderState, BIT_DECODER_TOOTH_ANG_CORRECT);
|
||||
toothLastMinusOneToothTime = 200000;
|
||||
|
||||
for (auto testdata : crankmaths_tooth_testdatas) {
|
||||
crankmaths_tooth_testdata_current = &testdata;
|
||||
snprintf(testName, testNameLength, "crankmaths/angletotime/tooth/%urpm/%uangle/%utoothangle", testdata.rpm, testdata.angle, testdata.triggerToothAngle);
|
||||
UnityDefaultTestRun(test_crankmaths_angletotime_tooth_execute, testName, __LINE__);
|
||||
}
|
||||
|
||||
}
|
|
@ -71,8 +71,6 @@ void test_calc_ign_timeout_360()
|
|||
setEngineSpeed(4000, 360);
|
||||
|
||||
TEST_ASSERT_EQUAL(15000, revolutionTime);
|
||||
TEST_ASSERT_EQUAL(786, degreesPerMicro);
|
||||
TEST_ASSERT_EQUAL(10667, microsPerDegree);
|
||||
TEST_ASSERT_EQUAL(96, dwellAngle);
|
||||
|
||||
// Expected test values were generated using floating point calculations (in Excel)
|
||||
|
|
Loading…
Reference in New Issue