├── misra ├── suppression_notes.txt ├── misra.json ├── suppressions.txt └── all_rules.txt ├── test ├── test_decoders │ ├── NGC │ │ └── test_ngc.h │ ├── renix │ │ └── renix.h │ ├── FordST170 │ │ └── FordST170.h │ ├── Nissan360 │ │ └── Nissan360.h │ ├── dual_wheel │ │ └── dual_wheel.h │ ├── missing_tooth │ │ └── missing_tooth.h │ ├── SuzukiK6A │ │ ├── SuzukiK6A.h │ │ └── test_getCrankAngle.cpp │ ├── general.cpp │ └── test_decoders.cpp ├── test_fuel │ ├── test_corrections.h │ ├── test_staging.h │ ├── test_PW.h │ └── test_fuel.cpp ├── test_math │ ├── tests_crankmaths.h │ ├── test_fp_support.h │ ├── test_fp_support.cpp │ ├── main.cpp │ ├── test_low_pass_filter.cpp │ └── test_shifts.cpp ├── test_tables │ ├── test_table2d.h │ ├── tests_tables.h │ └── main.cpp ├── test_schedule_calcs │ ├── test_calcs_common.h │ ├── test_schedule_calcs.cpp │ └── test_adjust_crank_angle.cpp ├── test_table3d_native │ └── test_main.cpp ├── test_schedules │ ├── test_schedules.h │ ├── test_schedules.cpp │ └── test_status_initial_off.cpp ├── test_ign │ └── main.cpp ├── test_sensors │ ├── main.cpp │ └── test_fastMap10Bit.cpp ├── test_secondary │ └── test_main.cpp ├── test_init │ └── main.cpp └── timer.hpp ├── .github ├── FUNDING.yml ├── workflows │ ├── codespell-ignored-words.txt │ ├── validate-ini.yml │ ├── sim-unit-tests.yml │ ├── pr-memory-deltas-report.yaml │ ├── upload-ini.yml │ ├── codespell.yml │ ├── doxygen.yml │ ├── unit-tests.yml │ ├── misra.yml │ ├── create-release.yml │ ├── build-firmware.yml │ └── pr-memory-deltas-generate.yml ├── CODEOWNERS └── dependabot.yml ├── reference └── speeduino_logo.png ├── speeduino ├── page_crc.h ├── engineProtection.h ├── src │ ├── FastCRC │ │ ├── library.properties │ │ ├── examples │ │ │ ├── FastCRC_CRC32 │ │ │ │ └── FastCRC_CRC32.ino │ │ │ ├── FastCRC_CRC8 │ │ │ │ └── FastCRC_CRC8.ino │ │ │ ├── FastCRC_CRC16 │ │ │ │ └── FastCRC_CRC16.ino │ │ │ ├── FastCRC_cont │ │ │ │ └── FastCRC_cont.ino │ │ │ └── FastCRC_validate │ │ │ │ └── FastCRC_validate.ino │ │ ├── LICENCE.md │ │ ├── keywords.txt │ │ ├── README.md │ │ └── FastCRC_cpu.h │ ├── PID_v1 │ │ ├── Examples │ │ │ ├── PID_Basic │ │ │ │ └── PID_Basic.ino │ │ │ ├── PID_AdaptiveTunings │ │ │ │ └── PID_AdaptiveTunings.ino │ │ │ └── PID_RelayOutput │ │ │ │ └── PID_RelayOutput.ino │ │ └── keywords.txt │ ├── BackupSram │ │ ├── BackupSramAsEEPROM.h │ │ └── BackupSramAsEEPROM.cpp │ ├── SPIAsEEPROM │ │ ├── SPIMemory.cpp │ │ ├── DMASAM.h │ │ ├── diagnostics.h │ │ └── winbondflash.h │ ├── FlashStorage │ │ ├── FlashAsEEPROM.cpp │ │ ├── FlashStorage.h │ │ ├── FlashAsEEPROM.h │ │ └── FlashStorage.cpp │ ├── libdivide │ │ ├── constant_fast_div.hpp │ │ └── constant_fast_div.h │ └── HardwareTimers │ │ └── HardwareTimer.h_test ├── rtc_common.h ├── secondaryTables.h ├── init.h ├── bit_manip.h ├── updates.h ├── comms_secondary.h ├── table3d_axis_io.cpp ├── maths.cpp ├── unit_testing.h ├── board_template.cpp ├── board_same51.cpp ├── comms_sd.h ├── load_source.h ├── logger.h ├── comms.h ├── table3d_typedefs.h ├── table3d_axis_io.h ├── idle.h ├── table3d_interpolate.h ├── utilities.h ├── speeduino.h ├── table2d.h ├── sensors_map_structs.h ├── comms_CAN.h ├── timers.h ├── sensors.h ├── table3d.cpp ├── corrections.h ├── TS_CommandButtonHandler.h ├── schedule_calcs.cpp ├── crankMaths.h ├── acc_mc33810.cpp ├── rtc_common.cpp ├── page_crc.cpp ├── schedule_calcs.h ├── table3d_axes.h ├── pages.h ├── SD_logger.h ├── comms_legacy.h ├── crankMaths.cpp └── schedule_calcs.hpp ├── .gitattributes ├── .gitignore ├── lib └── readme.txt ├── post_extra_script.py ├── contributing.md └── README.md /misra/suppression_notes.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/test_decoders/NGC/test_ngc.h: -------------------------------------------------------------------------------- 1 | void testNGC(); -------------------------------------------------------------------------------- /test/test_decoders/renix/renix.h: -------------------------------------------------------------------------------- 1 | void testRenix(); 2 | -------------------------------------------------------------------------------- /test/test_fuel/test_corrections.h: -------------------------------------------------------------------------------- 1 | void testCorrections(); -------------------------------------------------------------------------------- /test/test_math/tests_crankmaths.h: -------------------------------------------------------------------------------- 1 | void testCrankMaths(); -------------------------------------------------------------------------------- /test/test_decoders/FordST170/FordST170.h: -------------------------------------------------------------------------------- 1 | void testFordST170(); -------------------------------------------------------------------------------- /test/test_decoders/Nissan360/Nissan360.h: -------------------------------------------------------------------------------- 1 | void testNissan360(); -------------------------------------------------------------------------------- /test/test_decoders/dual_wheel/dual_wheel.h: -------------------------------------------------------------------------------- 1 | void testDualWheel(); 2 | -------------------------------------------------------------------------------- /test/test_decoders/missing_tooth/missing_tooth.h: -------------------------------------------------------------------------------- 1 | void testMissingTooth(); 2 | -------------------------------------------------------------------------------- /test/test_tables/test_table2d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | extern void testTable2d(); -------------------------------------------------------------------------------- /misra/misra.json: -------------------------------------------------------------------------------- 1 | {"script": "misra.py","args": ["--rule-texts=./misra_2012_text.txt"]} -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: noisymime 4 | -------------------------------------------------------------------------------- /.github/workflows/codespell-ignored-words.txt: -------------------------------------------------------------------------------- 1 | fram 2 | iterm 3 | ntegral 4 | numer 5 | numers 6 | wel 7 | -------------------------------------------------------------------------------- /reference/speeduino_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Autohome2/speeduino/HEAD/reference/speeduino_logo.png -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Global owner 2 | * @noisymime 3 | 4 | #stm32 maintainer 5 | /speeduino/board_stm32* @VitorBoss 6 | -------------------------------------------------------------------------------- /test/test_decoders/SuzukiK6A/SuzukiK6A.h: -------------------------------------------------------------------------------- 1 | void testSuzukiK6A_setEndTeeth(void); 2 | void testSuzukiK6A_getCrankAngle(void); 3 | -------------------------------------------------------------------------------- /test/test_math/test_fp_support.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | void assert_rounded_div(int32_t a, int32_t b, int32_t actual); -------------------------------------------------------------------------------- /test/test_schedule_calcs/test_calcs_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void setEngineSpeed(uint16_t rpm, int16_t max_ign); -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | -------------------------------------------------------------------------------- /speeduino/page_crc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | /* 5 | * Calculates and returns the CRC32 value of a given page of memory 6 | */ 7 | uint32_t calculatePageCRC32(byte pageNum /**< [in] The page number to compute CRC for. */); -------------------------------------------------------------------------------- /speeduino/engineProtection.h: -------------------------------------------------------------------------------- 1 | 2 | #define HARD_REV_FIXED 1 3 | #define HARD_REV_COOLANT 2 4 | 5 | byte checkEngineProtect(void); 6 | byte checkRevLimit(void); 7 | byte checkBoostLimit(void); 8 | byte checkOilPressureLimit(void); 9 | byte checkAFRLimit(void); -------------------------------------------------------------------------------- /speeduino/src/FastCRC/library.properties: -------------------------------------------------------------------------------- 1 | name=FastCRC 2 | version=1.2 3 | author=Frank Bösing 4 | maintainer=Frank Boesing 5 | sentence=Fast CRC routines 6 | paragraph= 7 | category=Data Processing 8 | url=https://github.com/FrankBoesing/FastCRC 9 | architectures=* 10 | -------------------------------------------------------------------------------- /test/test_fuel/test_staging.h: -------------------------------------------------------------------------------- 1 | void testStaging(); 2 | void test_Staging_Off(void); 3 | void test_Staging_4cyl_Auto_Inactive(void); 4 | void test_Staging_4cyl_Table_Inactive(void); 5 | void test_Staging_4cyl_Auto_50pct(void); 6 | void test_Staging_4cyl_Auto_33pct(void); 7 | void test_Staging_4cyl_Table_50pct(void); 8 | -------------------------------------------------------------------------------- /speeduino/rtc_common.h: -------------------------------------------------------------------------------- 1 | #ifndef RTC_H 2 | #define RTC_H 3 | 4 | void initRTC(); 5 | uint8_t rtc_getSecond(); 6 | uint8_t rtc_getMinute(); 7 | uint8_t rtc_getHour(); 8 | uint8_t rtc_getDay(); 9 | uint8_t rtc_getDOW(); 10 | uint8_t rtc_getMonth(); 11 | uint16_t rtc_getYear(); 12 | void rtc_setTime(byte, byte, byte, byte, byte, uint16_t); 13 | 14 | 15 | 16 | #endif -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | 4 | *.ino text eol=lf 5 | *.h text eol=lf 6 | *.c text eol=lf 7 | *.ini text eol=lf 8 | *.msq text eol=lf 9 | 10 | # Denote all files that are truly binary and should not be modified. 11 | *.png binary 12 | *.jpg binary 13 | *.dll binary 14 | *.fzz binary 15 | *.xls binary 16 | 17 | 18 | *.pdf -text 19 | -------------------------------------------------------------------------------- /speeduino/secondaryTables.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "statuses.h" 5 | #include "config_pages.h" 6 | #include "table3d.h" 7 | 8 | void calculateSecondaryFuel(const config10 &page10, const table3d16RpmLoad &veLookupTable, statuses ¤t); 9 | void calculateSecondarySpark(const config2 &page2, const config10 &page10, const table3d16RpmLoad &sparkLookupTable, statuses ¤t); 10 | -------------------------------------------------------------------------------- /test/test_fuel/test_PW.h: -------------------------------------------------------------------------------- 1 | void testPW(); 2 | void test_PW_No_Multiply(); 3 | void test_PW_MAP_Multiply(void); 4 | void test_PW_AFR_Multiply(void); 5 | void test_PW_MAP_Multiply_Compatibility(void); 6 | void test_PW_ALL_Multiply(void); 7 | void test_PW_Large_Correction(); 8 | void test_PW_Very_Large_Correction(); 9 | void test_PW_4Cyl_PW0(void); 10 | void test_PW_Limit_90pct(void); 11 | void test_PW_Limit_Long_Revolution(void); -------------------------------------------------------------------------------- /.github/workflows/validate-ini.yml: -------------------------------------------------------------------------------- 1 | name: Validate INI 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | validate: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v4 12 | - name: Validate INI 13 | uses: hyper-tuner/ini-validate-action@v1 14 | with: 15 | github-token: "${{ secrets.GITHUB_TOKEN }}" 16 | filename: reference/speeduino.ini 17 | -------------------------------------------------------------------------------- /test/test_table3d_native/test_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "table3d_interpolate.cpp" 3 | typedef uint8_t byte; 4 | #include "table2d.ino" 5 | #include "..\test_misc\tests_tables.cpp" 6 | #include "..\test_misc\test_table2d.cpp" 7 | 8 | int main(int argc, char **argv) { 9 | UNITY_BEGIN(); 10 | 11 | testTables(); 12 | RUN_TEST(test_all_incrementing); 13 | 14 | testTable2d(); 15 | 16 | UNITY_END(); 17 | 18 | return 0; 19 | } -------------------------------------------------------------------------------- /test/test_tables/tests_tables.h: -------------------------------------------------------------------------------- 1 | extern void testTables(); 2 | void setup_FuelTable(void); 3 | void test_tableLookup_50pct(void); 4 | void test_tableLookup_exact1Axis(void); 5 | void test_tableLookup_exact2Axis(void); 6 | void test_tableLookup_overMaxX(void); 7 | void test_tableLookup_overMaxY(void); 8 | void test_tableLookup_underMinX(void); 9 | void test_tableLookup_underMinY(void); 10 | void test_tableLookup_roundUp(void); 11 | void test_all_incrementing(void); 12 | -------------------------------------------------------------------------------- /test/test_schedules/test_schedules.h: -------------------------------------------------------------------------------- 1 | #if !defined(__TEST_SCHEDULE_H__) 2 | #define __TEST_SCHEDULE_H__ 3 | 4 | void testSchedules(); 5 | void test_status_initial_off(void); 6 | void test_status_off_to_pending(void); 7 | void test_status_pending_to_running(void); 8 | void test_status_running_to_off(void); 9 | void test_status_running_to_pending(void); 10 | void test_accuracy_timeout(void); 11 | void test_accuracy_duration(void); 12 | 13 | void test_accuracy_timeout(void); 14 | 15 | #endif // __TEST_SCHEDULE_H__ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | float.py 3 | 4 | table.py 5 | 6 | .DS_Store 7 | 8 | reference/hardware/v0.2/~$schematic v0.2_bom.xlsx 9 | 10 | reference/hardware/v0.4/gerbers/Archive.zip 11 | 12 | .pioenvs 13 | .pio 14 | .piolibdeps 15 | .clang_complete 16 | .gcc-flags.json 17 | .project 18 | .vscode 19 | .build 20 | .kicad_pcb-bak 21 | .vscode/c_cpp_properties.json 22 | .vscode/launch.json 23 | .vscode/.browse.c_cpp.db* 24 | speeduino/board_samd21* 25 | reference/doxygen 26 | speeduino.ino.cpp 27 | test/output_export.cpp 28 | misra/.results 29 | ~$* 30 | local.ini 31 | -------------------------------------------------------------------------------- /speeduino/init.h: -------------------------------------------------------------------------------- 1 | #ifndef INIT_H 2 | #define INIT_H 3 | 4 | void initialiseAll(void); 5 | void initialiseTriggers(void); 6 | void setPinMapping(byte boardID); 7 | void changeHalfToFullSync(void); 8 | void changeFullToHalfSync(void); 9 | 10 | #define VSS_USES_RPM2() ((configPage2.vssMode > 1U) && (pinVSS == pinTrigger2) && !BIT_CHECK(decoderState, BIT_DECODER_HAS_SECONDARY)) // VSS is on the same pin as RPM2 and RPM2 is not used as part of the decoder 11 | #define FLEX_USES_RPM2() ((configPage2.flexEnabled > 0U) && (pinFlex == pinTrigger2) && !BIT_CHECK(decoderState, BIT_DECODER_HAS_SECONDARY)) // Same as above, but for Flex sensor 12 | 13 | #endif -------------------------------------------------------------------------------- /speeduino/src/FastCRC/examples/FastCRC_CRC32/FastCRC_CRC32.ino: -------------------------------------------------------------------------------- 1 | /* 2 | FastCRC-Example 3 | 4 | (c) Frank Boesing 2014 5 | */ 6 | 7 | #include 8 | 9 | FastCRC32 CRC32; 10 | 11 | uint8_t buf[9] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'}; 12 | 13 | void setup() { 14 | 15 | delay(1500); 16 | Serial.begin(115200); 17 | 18 | Serial.println("CRC Example"); 19 | Serial.println(); 20 | 21 | Serial.print("CRC32 of \""); 22 | 23 | for (unsigned int i = 0; i < sizeof(buf); i++) { 24 | Serial.print((char) buf[i]); 25 | } 26 | 27 | Serial.print("\" is: 0x"); 28 | Serial.println( CRC32.crc32(buf, sizeof(buf)), HEX ); 29 | 30 | } 31 | 32 | 33 | void loop() { 34 | } 35 | 36 | 37 | -------------------------------------------------------------------------------- /speeduino/src/FastCRC/examples/FastCRC_CRC8/FastCRC_CRC8.ino: -------------------------------------------------------------------------------- 1 | /* 2 | FastCRC-Example 3 | 4 | (c) Frank Boesing 2014 5 | */ 6 | 7 | #include 8 | 9 | FastCRC8 CRC8; 10 | 11 | uint8_t buf[9] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'}; 12 | 13 | void setup() { 14 | 15 | delay(1500); 16 | Serial.begin(115200); 17 | 18 | Serial.println("CRC Example"); 19 | Serial.println(); 20 | 21 | Serial.print("SMBUS-CRC of \""); 22 | 23 | for (unsigned int i = 0; i < sizeof(buf); i++) { 24 | Serial.print((char) buf[i]); 25 | } 26 | 27 | Serial.print("\" is: 0x"); 28 | Serial.println( CRC8.smbus(buf, sizeof(buf)), HEX ); 29 | 30 | } 31 | 32 | 33 | void loop() { 34 | } 35 | 36 | 37 | -------------------------------------------------------------------------------- /speeduino/src/FastCRC/examples/FastCRC_CRC16/FastCRC_CRC16.ino: -------------------------------------------------------------------------------- 1 | /* 2 | FastCRC-Example 3 | 4 | (c) Frank Boesing 2014 5 | */ 6 | 7 | #include 8 | 9 | FastCRC16 CRC16; 10 | 11 | uint8_t buf[9] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'}; 12 | 13 | void setup() { 14 | 15 | delay(1500); 16 | Serial.begin(115200); 17 | 18 | Serial.println("CRC Example"); 19 | Serial.println(); 20 | 21 | Serial.print("CCITT-CRC of \""); 22 | 23 | for (unsigned int i = 0; i < sizeof(buf); i++) { 24 | Serial.print((char) buf[i]); 25 | } 26 | 27 | Serial.print("\" is: 0x"); 28 | Serial.println( CRC16.ccitt(buf, sizeof(buf)), HEX ); 29 | 30 | } 31 | 32 | 33 | void loop() { 34 | } 35 | 36 | 37 | -------------------------------------------------------------------------------- /speeduino/bit_manip.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * @file 5 | * @brief Bit twiddling macros 6 | */ 7 | 8 | /** @brief Set bit b (0-7) in byte a */ 9 | #define BIT_SET(var,pos) ((var) |= (1U<<(pos))) 10 | 11 | /** @brief Clear bit b (0-7) in byte a */ 12 | #define BIT_CLEAR(var,pos) ((var) &= ~(1U<<(pos))) 13 | 14 | /** @brief Is bit pos (0-7) in byte var set? */ 15 | #define BIT_CHECK(var,pos) !!((var) & (1U<<(pos))) 16 | 17 | /** @brief Toggle the value of bit pos (0-7) in byte var */ 18 | #define BIT_TOGGLE(var,pos) ((var)^= 1UL << (pos)) 19 | 20 | /** @brief Set the value ([0,1], [true, false]) of bit pos (0-7) in byte var */ 21 | #define BIT_WRITE(var, pos, bitvalue) ((bitvalue) ? BIT_SET((var), (pos)) : BIT_CLEAR((var), (pos))) -------------------------------------------------------------------------------- /speeduino/src/PID_v1/Examples/PID_Basic/PID_Basic.ino: -------------------------------------------------------------------------------- 1 | /******************************************************** 2 | * PID Basic Example 3 | * Reading analog input 0 to control analog PWM output 3 4 | ********************************************************/ 5 | 6 | #include 7 | 8 | //Define Variables we'll be connecting to 9 | double Setpoint, Input, Output; 10 | 11 | //Specify the links and initial tuning parameters 12 | PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT); 13 | 14 | void setup() 15 | { 16 | //initialize the variables we're linked to 17 | Input = analogRead(0); 18 | Setpoint = 100; 19 | 20 | //turn the PID on 21 | myPID.SetMode(AUTOMATIC); 22 | } 23 | 24 | void loop() 25 | { 26 | Input = analogRead(0); 27 | myPID.Compute(); 28 | analogWrite(3,Output); 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /speeduino/updates.h: -------------------------------------------------------------------------------- 1 | #ifndef UPDATES_H 2 | #define UPDATES_H 3 | 4 | #include "table3d.h" 5 | 6 | void doUpdates(void); 7 | void multiplyTableLoad(void *pTable, table_type_t key, uint8_t multiplier); //Added 202201 - to update the table Y axis as TPS now works at 0.5% increments. Multiplies the load axis values by 4 (most tables) or by 2 (VVT table) 8 | void divideTableLoad(void *pTable, table_type_t key, uint8_t divisor); //Added 202201 - to update the table Y axis as TPS now works at 0.5% increments. This should only be needed by the VVT tables when using MAP as load. 9 | void multiplyTableValue(uint8_t pageNum, uint8_t multiplier); //Added to update the table values. Multiplies the value by the multiplier 10 | void divideTableValue(uint8_t pageNum, uint8_t divisor); //Added to update the table values. Divide the value by divisor 11 | 12 | #endif -------------------------------------------------------------------------------- /speeduino/comms_secondary.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMS_SECONDARY_H 2 | #define COMMS_SECONDARY_H 3 | 4 | #define NEW_CAN_PACKET_SIZE 123 5 | #define CAN_PACKET_SIZE 75 6 | 7 | #define SECONDARY_SERIAL_PROTO_GENERIC_FIXED 0 8 | #define SECONDARY_SERIAL_PROTO_GENERIC_INI 1 9 | #define SECONDARY_SERIAL_PROTO_CAN 2 10 | #define SECONDARY_SERIAL_PROTO_MSDROID 3 11 | #define SECONDARY_SERIAL_PROTO_REALDASH 4 12 | #define SECONDARY_SERIAL_PROTO_TUNERSTUDIO 5 13 | 14 | extern SECONDARY_SERIAL_T *pSecondarySerial; 15 | #define secondarySerial (*pSecondarySerial) 16 | 17 | void secondserial_Command(void);//This is the heart of the Command Line Interpreter. All that needed to be done was to make it human readable. 18 | void sendCancommand(uint8_t cmdtype , uint16_t canadddress, uint8_t candata1, uint8_t candata2, uint16_t sourcecanAddress); 19 | 20 | #endif // COMMS_SECONDARY_H 21 | -------------------------------------------------------------------------------- /test/test_ign/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void testIgnCorrections(void); 6 | 7 | #define UNITY_EXCLUDE_DETAILS 8 | 9 | void setup() 10 | { 11 | pinMode(LED_BUILTIN, OUTPUT); 12 | 13 | // NOTE!!! Wait for >2 secs 14 | // if board doesn't support software reset via Serial.DTR/RTS 15 | #if !defined(SIMULATOR) 16 | delay(2000); 17 | #endif 18 | 19 | UNITY_BEGIN(); // IMPORTANT LINE! 20 | 21 | testIgnCorrections(); 22 | 23 | UNITY_END(); // stop unit testing 24 | 25 | #if defined(SIMULATOR) // Tell SimAVR we are done 26 | cli(); 27 | sleep_enable(); 28 | sleep_cpu(); 29 | #endif 30 | } 31 | 32 | void loop() 33 | { 34 | // Blink to indicate end of test 35 | digitalWrite(LED_BUILTIN, HIGH); 36 | delay(250); 37 | digitalWrite(LED_BUILTIN, LOW); 38 | delay(250); 39 | } -------------------------------------------------------------------------------- /speeduino/table3d_axis_io.cpp: -------------------------------------------------------------------------------- 1 | #include "table3d_axis_io.h" 2 | #include "maths.h" 3 | 4 | static byte to_byte_100(int16_t value) { return (byte)div100(value); } 5 | static int16_t from_byte_100(byte in) { return (int16_t)in * 100; } 6 | 7 | static byte to_byte_1(int16_t value) { return (byte)value; } 8 | static int16_t from_byte_1(byte in) { return (int16_t)in; } 9 | 10 | static byte to_byte_2(int16_t value) { return (byte)(value/2); } //cppcheck-suppress misra-c2012-10.8 11 | static int16_t from_byte_2(byte in) { return (int16_t)in*2; } 12 | 13 | table3d_axis_io_converter get_table3d_axis_converter(axis_domain domain) { 14 | return domain==axis_domain_Rpm ? table3d_axis_io_converter { &to_byte_100, &from_byte_100 } : 15 | domain==axis_domain_Load ? table3d_axis_io_converter { &to_byte_2, &from_byte_2 } : 16 | table3d_axis_io_converter { &to_byte_1, &from_byte_1 }; 17 | } -------------------------------------------------------------------------------- /test/test_schedule_calcs/test_schedule_calcs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | extern void test_calc_ign_timeout(); 7 | extern void test_calc_inj_timeout(); 8 | extern void test_adjust_crank_angle(); 9 | 10 | void setup() 11 | { 12 | pinMode(LED_BUILTIN, OUTPUT); 13 | #if !defined(SIMULATOR) 14 | delay(2000); 15 | #endif 16 | 17 | UNITY_BEGIN(); // start unit testing 18 | 19 | test_calc_ign_timeout(); 20 | test_calc_inj_timeout(); 21 | test_adjust_crank_angle(); 22 | 23 | UNITY_END(); // stop unit testing 24 | 25 | #if defined(SIMULATOR) // Tell SimAVR we are done 26 | cli(); 27 | sleep_enable(); 28 | sleep_cpu(); 29 | #endif 30 | } 31 | 32 | void loop() 33 | { 34 | digitalWrite(LED_BUILTIN, HIGH); 35 | delay(250); 36 | digitalWrite(LED_BUILTIN, LOW); 37 | delay(250); 38 | } -------------------------------------------------------------------------------- /misra/suppressions.txt: -------------------------------------------------------------------------------- 1 | #All Advisory rules are suppressed: 2 | misra-c2012-1.2 3 | misra-c2012-2.3 4 | misra-c2012-2.4 5 | misra-c2012-2.5 6 | misra-c2012-2.6 7 | misra-c2012-2.7 8 | misra-c2012-4.2 9 | misra-c2012-5.9 10 | misra-c2012-8.7 11 | misra-c2012-8.9 12 | misra-c2012-8.13 13 | misra-c2012-10.5 14 | misra-c2012-11.4 15 | misra-c2012-11.5 16 | misra-c2012-12.1 17 | misra-c2012-12.3 18 | misra-c2012-12.4 19 | misra-c2012-13.3 20 | misra-c2012-13.4 21 | misra-c2012-15.1 22 | misra-c2012-15.4 23 | misra-c2012-15.5 24 | misra-c2012-17.5 25 | misra-c2012-17.8 26 | misra-c2012-18.4 27 | misra-c2012-18.5 28 | misra-c2012-19.2 29 | misra-c2012-20.1 30 | misra-c2012-20.5 31 | misra-c2012-20.10 32 | misra-c2012-21.12 33 | # As of cppcheck 2.9, these checks produce too many false positives 34 | # They do not account for class scope. 35 | # E.g. class1::method1==class2::method1 36 | misra-c2012-5.8 37 | misra-c2012-8.6 38 | -------------------------------------------------------------------------------- /speeduino/src/PID_v1/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For PID Library 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | PID KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | SetMode KEYWORD2 16 | Compute KEYWORD2 17 | SetOutputLimits KEYWORD2 18 | SetTunings KEYWORD2 19 | SetControllerDirection KEYWORD2 20 | SetSampleTime KEYWORD2 21 | GetKp KEYWORD2 22 | GetKi KEYWORD2 23 | GetKd KEYWORD2 24 | GetMode KEYWORD2 25 | GetDirection KEYWORD2 26 | 27 | ####################################### 28 | # Constants (LITERAL1) 29 | ####################################### 30 | 31 | AUTOMATIC LITERAL1 32 | MANUAL LITERAL1 33 | DIRECT LITERAL1 34 | REVERSE LITERAL1 -------------------------------------------------------------------------------- /speeduino/maths.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "maths.h" 3 | 4 | //Generates a random number from 1 to 100 (inclusive). 5 | //The initial seed used is always based on micros(), though this is unlikely to cause an issue as the first run is nearly random itself 6 | //Function requires 4 bytes to store state and seed, but operates very quickly (around 4uS per call) 7 | static uint8_t a, x, y, z; 8 | uint8_t random1to100() 9 | { 10 | //Check if this is the first time being run. If so, seed the random number generator with micros() 11 | if( (a == 0U) && (x == 0U) && (y == 0U) && (z == 0U) ) 12 | { 13 | x = micros() >> 24U; 14 | y = micros() >> 16U; 15 | z = micros() >> 8U; 16 | a = micros(); 17 | } 18 | 19 | do 20 | { 21 | unsigned char t = x ^ (x << 4); 22 | x=y; 23 | y=z; 24 | z=a; 25 | a = z ^ t ^ ( z >> 1) ^ (t << 1); 26 | } 27 | while(a >= 100U); 28 | return (a+1U); 29 | } 30 | -------------------------------------------------------------------------------- /test/test_tables/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "tests_tables.h" 6 | #include "test_table2d.h" 7 | 8 | #define UNITY_EXCLUDE_DETAILS 9 | 10 | void setup() 11 | { 12 | pinMode(LED_BUILTIN, OUTPUT); 13 | 14 | // NOTE!!! Wait for >2 secs 15 | // if board doesn't support software reset via Serial.DTR/RTS 16 | #if !defined(SIMULATOR) 17 | delay(2000); 18 | #endif 19 | 20 | UNITY_BEGIN(); // IMPORTANT LINE! 21 | 22 | testTables(); 23 | testTable2d(); 24 | 25 | UNITY_END(); // stop unit testing 26 | 27 | #if defined(SIMULATOR) // Tell SimAVR we are done 28 | cli(); 29 | sleep_enable(); 30 | sleep_cpu(); 31 | #endif 32 | } 33 | 34 | void loop() 35 | { 36 | // Blink to indicate end of test 37 | digitalWrite(LED_BUILTIN, HIGH); 38 | delay(250); 39 | digitalWrite(LED_BUILTIN, LOW); 40 | delay(250); 41 | } -------------------------------------------------------------------------------- /.github/workflows/sim-unit-tests.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | 3 | on: 4 | # Trigger the workflow on pull request 5 | pull_request: 6 | # Or push to non-master branch 7 | push: 8 | branches-ignore: [ master ] 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | jobs: 13 | Simulator-Unit-Test: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | 19 | - name: Set up Python 20 | uses: actions/setup-python@v5 21 | with: 22 | python-version: '3.10' 23 | 24 | - name: Install PlatformIO 25 | run: | 26 | python -m pip install --upgrade pip 27 | pip install --upgrade platformio 28 | pip install wheel 29 | platformio upgrade 30 | platformio pkg update 31 | 32 | - name: Run Simulator Unit Tests 33 | run: | 34 | platformio test -v -e megaatmega2560_sim_unittest -------------------------------------------------------------------------------- /.github/workflows/pr-memory-deltas-report.yaml: -------------------------------------------------------------------------------- 1 | name: Report Size Deltas 2 | 3 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | paths: 7 | - ".github/workflows/pr-memory-deltas-report.yaml" 8 | schedule: 9 | # Run at the minimum interval allowed by GitHub Actions. 10 | # Note: GitHub Actions periodically has outages which result in workflow failures. 11 | # In this event, the workflows will start passing again once the service recovers. 12 | - cron: "*/5 * * * *" 13 | workflow_dispatch: 14 | repository_dispatch: 15 | 16 | jobs: 17 | report: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Comment size deltas reports to PRs 21 | uses: arduino/report-size-deltas@v1 22 | with: 23 | # The name of the workflow artifact created by the sketch compilation workflow 24 | sketches-reports-source: ^sketches-report-.+ -------------------------------------------------------------------------------- /speeduino/src/FastCRC/examples/FastCRC_cont/FastCRC_cont.ino: -------------------------------------------------------------------------------- 1 | /* 2 | FastCRC-Example 3 | 4 | (c) Frank Boesing 2014 5 | 6 | This example shows how to use the update functions. 7 | */ 8 | 9 | #include 10 | 11 | FastCRC16 CRC16; 12 | 13 | uint8_t buf[9] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'}; 14 | 15 | void setup() { 16 | uint16_t crc; 17 | 18 | delay(1500); 19 | Serial.begin(115200); 20 | 21 | Serial.println("CRC Example"); 22 | Serial.println(); 23 | 24 | Serial.print("CCITT-CRC of \""); 25 | 26 | for (unsigned int i = 0; i < sizeof(buf); i++) { 27 | Serial.print((char) buf[i]); 28 | } 29 | 30 | Serial.print("\" is: 0x"); 31 | 32 | 33 | //Calculate first half of buffer: 34 | crc = CRC16.ccitt(&buf[0], 4); 35 | 36 | //Calculate seconde half of buffer: 37 | crc = CRC16.ccitt_upd(&buf[4],5); 38 | 39 | Serial.println(crc, HEX ); 40 | 41 | } 42 | 43 | 44 | void loop() { 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /test/test_sensors/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define UNITY_EXCLUDE_DETAILS 6 | 7 | extern void test_fastMap10Bit(void); 8 | extern void test_map_sampling(void); 9 | 10 | void setup() 11 | { 12 | pinMode(LED_BUILTIN, OUTPUT); 13 | 14 | // NOTE!!! Wait for >2 secs 15 | // if board doesn't support software reset via Serial.DTR/RTS 16 | #if !defined(SIMULATOR) 17 | delay(2000); 18 | #endif 19 | 20 | UNITY_BEGIN(); // IMPORTANT LINE! 21 | 22 | test_fastMap10Bit(); 23 | test_map_sampling(); 24 | 25 | UNITY_END(); // stop unit testing 26 | 27 | #if defined(SIMULATOR) // Tell SimAVR we are done 28 | cli(); 29 | sleep_enable(); 30 | sleep_cpu(); 31 | #endif 32 | } 33 | 34 | void loop() 35 | { 36 | // Blink to indicate end of test 37 | digitalWrite(LED_BUILTIN, HIGH); 38 | delay(250); 39 | digitalWrite(LED_BUILTIN, LOW); 40 | delay(250); 41 | } -------------------------------------------------------------------------------- /speeduino/unit_testing.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * @file 5 | * @brief Unit testability support 6 | * 7 | */ 8 | 9 | #if !defined(UNIT_TEST) 10 | /** 11 | * @brief Mark an entity as having static (internal) linkage, unless a unit 12 | * test is in progress - then the entity is given external linkage so the test can access it. 13 | * 14 | * Most useful for translation unit scoped variables. I.e. static within a CPP file 15 | * 16 | */ 17 | #define TESTABLE_STATIC static 18 | #else 19 | #define TESTABLE_STATIC 20 | #endif 21 | 22 | #if !defined(UNIT_TEST) 23 | /** 24 | * @brief Mark an entity as having static (internal) linkage & inlined, unless a unit 25 | * test is in progress - then the entity is given external linkage so the test can access it. 26 | * 27 | * Most useful for translation unit scoped functions. I.e. "static inline" within a CPP file 28 | * 29 | */ 30 | #define TESTABLE_INLINE_STATIC static inline 31 | #else 32 | #define TESTABLE_INLINE_STATIC 33 | #endif -------------------------------------------------------------------------------- /test/test_sensors/test_fastMap10Bit.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../test_utils.h" 3 | 4 | extern int16_t fastMap10Bit(uint16_t value, int16_t rangeMin, int16_t rangeMax); 5 | 6 | static void test_fastMap10Bit_negative_range(void) { 7 | TEST_ASSERT_EQUAL_INT(-1500, fastMap10Bit(0, -1500, -500)); 8 | TEST_ASSERT_EQUAL_INT(-501, fastMap10Bit(1023, -1500, -500)); 9 | // Greater than 10-bit input 10 | TEST_ASSERT_NOT_EQUAL_INT(-501, fastMap10Bit(1023*2, -1500, -500)); 11 | } 12 | 13 | static void test_fastMap10Bit_positive_range(void) { 14 | TEST_ASSERT_EQUAL_INT(500, fastMap10Bit(0, 500, 1500)); 15 | TEST_ASSERT_EQUAL_INT(1499, fastMap10Bit(1023, 500, 1500)); 16 | // Greater than 10-bit input 17 | TEST_ASSERT_NOT_EQUAL_INT(1499, fastMap10Bit(1023*2, 500, 1500)); 18 | } 19 | 20 | void test_fastMap10Bit(void) { 21 | SET_UNITY_FILENAME() { 22 | RUN_TEST(test_fastMap10Bit_negative_range); 23 | RUN_TEST(test_fastMap10Bit_positive_range); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/test_schedules/test_schedules.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "test_schedules.h" 7 | 8 | void setup() 9 | { 10 | pinMode(LED_BUILTIN, OUTPUT); 11 | #if !defined(SIMULATOR) 12 | delay(2000); 13 | #endif 14 | 15 | UNITY_BEGIN(); // start unit testing 16 | 17 | initialiseAll(); //Run the main initialise function 18 | test_status_initial_off(); 19 | //test_status_off_to_pending(); 20 | //test_status_pending_to_running(); 21 | //test_status_running_to_pending(); 22 | //test_status_running_to_off(); 23 | test_accuracy_timeout(); 24 | test_accuracy_duration(); 25 | 26 | UNITY_END(); // stop unit testing 27 | 28 | #if defined(SIMULATOR) // Tell SimAVR we are done 29 | cli(); 30 | sleep_enable(); 31 | sleep_cpu(); 32 | #endif 33 | } 34 | 35 | void loop() 36 | { 37 | digitalWrite(LED_BUILTIN, HIGH); 38 | delay(250); 39 | digitalWrite(LED_BUILTIN, LOW); 40 | delay(250); 41 | } -------------------------------------------------------------------------------- /test/test_secondary/test_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define UNITY_EXCLUDE_DETAILS 6 | 7 | extern void test_calculateSecondaryFuel(void); 8 | extern void test_calculateSecondarySpark(void); 9 | 10 | 11 | void setup() 12 | { 13 | pinMode(LED_BUILTIN, OUTPUT); 14 | 15 | // NOTE!!! Wait for >2 secs 16 | // if board doesn't support software reset via Serial.DTR/RTS 17 | #if !defined(SIMULATOR) 18 | delay(2000); 19 | #endif 20 | 21 | UNITY_BEGIN(); // IMPORTANT LINE! 22 | 23 | test_calculateSecondaryFuel(); 24 | test_calculateSecondarySpark(); 25 | 26 | UNITY_END(); // stop unit testing 27 | 28 | #if defined(SIMULATOR) // Tell SimAVR we are done 29 | cli(); 30 | sleep_enable(); 31 | sleep_cpu(); 32 | #endif 33 | } 34 | 35 | void loop() 36 | { 37 | // Blink to indicate end of test 38 | digitalWrite(LED_BUILTIN, HIGH); 39 | delay(250); 40 | digitalWrite(LED_BUILTIN, LOW); 41 | delay(250); 42 | } -------------------------------------------------------------------------------- /lib/readme.txt: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for the project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link to executable file. 4 | 5 | The source code of each library should be placed in separate directory, like 6 | "lib/private_lib/[here are source files]". 7 | 8 | For example, see how can be organized `Foo` and `Bar` libraries: 9 | 10 | |--lib 11 | | |--Bar 12 | | | |--docs 13 | | | |--examples 14 | | | |--src 15 | | | |- Bar.c 16 | | | |- Bar.h 17 | | |--Foo 18 | | | |- Foo.c 19 | | | |- Foo.h 20 | | |- readme.txt --> THIS FILE 21 | |- platformio.ini 22 | |--src 23 | |- main.c 24 | 25 | Then in `src/main.c` you should use: 26 | 27 | #include 28 | #include 29 | 30 | // rest H/C/CPP code 31 | 32 | PlatformIO will find your libraries automatically, configure preprocessor's 33 | include paths and build them. 34 | 35 | More information about PlatformIO Library Dependency Finder 36 | - http://docs.platformio.org/page/librarymanager/ldf.html 37 | -------------------------------------------------------------------------------- /test/test_fuel/test_fuel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "test_corrections.h" 8 | #include "test_PW.h" 9 | #include "test_staging.h" 10 | 11 | #define UNITY_EXCLUDE_DETAILS 12 | 13 | void setup() 14 | { 15 | pinMode(LED_BUILTIN, OUTPUT); 16 | 17 | // NOTE!!! Wait for >2 secs 18 | // if board doesn't support software reset via Serial.DTR/RTS 19 | #if !defined(SIMULATOR) 20 | delay(2000); 21 | #endif 22 | 23 | UNITY_BEGIN(); // IMPORTANT LINE! 24 | 25 | testCorrections(); 26 | testPW(); 27 | testStaging(); 28 | 29 | UNITY_END(); // stop unit testing 30 | 31 | #if defined(SIMULATOR) // Tell SimAVR we are done 32 | cli(); 33 | sleep_enable(); 34 | sleep_cpu(); 35 | #endif 36 | } 37 | 38 | void loop() 39 | { 40 | // Blink to indicate end of test 41 | digitalWrite(LED_BUILTIN, HIGH); 42 | delay(250); 43 | digitalWrite(LED_BUILTIN, LOW); 44 | delay(250); 45 | } -------------------------------------------------------------------------------- /speeduino/board_template.cpp: -------------------------------------------------------------------------------- 1 | #include "globals.h" 2 | #if defined(CORE_TEMPLATE) 3 | 4 | 5 | void initBoard() 6 | { 7 | /* 8 | *********************************************************************************************************** 9 | * General 10 | */ 11 | 12 | /* 13 | *********************************************************************************************************** 14 | * Timers 15 | */ 16 | 17 | /* 18 | *********************************************************************************************************** 19 | * Auxiliaries 20 | */ 21 | 22 | /* 23 | *********************************************************************************************************** 24 | * Idle 25 | */ 26 | 27 | /* 28 | *********************************************************************************************************** 29 | * Schedules 30 | */ 31 | } 32 | 33 | uint16_t freeRam() 34 | { 35 | 36 | } 37 | 38 | void doSystemReset() { return; } 39 | void jumpToBootloader() { return; } 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /test/test_math/test_fp_support.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "test_fp_support.h" 4 | 5 | #if defined(ARDUINO_ARCH_AVR) 6 | #include 7 | using test_float_t = float64_t; 8 | #else 9 | using test_float_t = double; 10 | #endif 11 | 12 | test_float_t floatDivision(int32_t a, int32_t b) { 13 | #if defined(ARDUINO_ARCH_AVR) 14 | return fp64_div(fp64_int32_to_float64(a), fp64_int32_to_float64(b)); 15 | #else 16 | return (double)a/(double)b; 17 | #endif 18 | } 19 | 20 | int32_t round_float(test_float_t f) { 21 | #if defined(ARDUINO_ARCH_AVR) 22 | return fp64_lround(f); 23 | #else 24 | return round(f); 25 | #endif 26 | } 27 | 28 | 29 | void assert_rounded_div(int32_t a, int32_t b, int32_t actual) { 30 | test_float_t fExpected = floatDivision(a, b); 31 | int32_t expected = round_float(fExpected); 32 | 33 | // char msg[64]; 34 | // sprintf(msg, "a: %" PRIi32 ", b: %" PRIi32 " fExpected: %s", a, b, fp64_to_string(fExpected, 17, 15)); 35 | // TEST_ASSERT_EQUAL_MESSAGE(expected, actual, msg); 36 | TEST_ASSERT_EQUAL(expected, actual); 37 | } -------------------------------------------------------------------------------- /speeduino/board_same51.cpp: -------------------------------------------------------------------------------- 1 | #if defined(CORE_SAME51) 2 | #include "globals.h" 3 | #include "auxiliaries.h" 4 | 5 | void initBoard() 6 | { 7 | /* 8 | *********************************************************************************************************** 9 | * General 10 | */ 11 | 12 | /* 13 | *********************************************************************************************************** 14 | * Timers 15 | */ 16 | 17 | /* 18 | *********************************************************************************************************** 19 | * Auxiliaries 20 | */ 21 | 22 | /* 23 | *********************************************************************************************************** 24 | * Idle 25 | */ 26 | 27 | /* 28 | *********************************************************************************************************** 29 | * Schedules 30 | */ 31 | } 32 | 33 | uint16_t freeRam() 34 | { 35 | return 0; 36 | } 37 | 38 | void doSystemReset() { return; } 39 | void jumpToBootloader() { return; } 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /.github/workflows/upload-ini.yml: -------------------------------------------------------------------------------- 1 | name: Upload INI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | paths: 7 | - 'reference/speeduino.ini' 8 | 9 | jobs: 10 | validate: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | - name: Validate INI 17 | uses: hyper-tuner/ini-validate-action@v1 18 | with: 19 | github-token: "${{ secrets.GITHUB_TOKEN }}" 20 | filename: reference/speeduino.ini 21 | 22 | upload: 23 | runs-on: ubuntu-latest 24 | if: github.repository_owner == 'speeduino' 25 | needs: validate 26 | 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v4 30 | - name: Upload INI 31 | uses: hyper-tuner/ini-upload-action@v1 32 | with: 33 | api-url: "${{ secrets.HYPER_TUNER_INI_UPLOAD_URL }}" 34 | username: "${{ secrets.HYPER_TUNER_INI_UPLOAD_USERNAME }}" 35 | password: "${{ secrets.HYPER_TUNER_INI_UPLOAD_PASSWORD }}" 36 | path: reference/speeduino.ini 37 | ecosystem: speeduino 38 | -------------------------------------------------------------------------------- /test/test_math/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "tests_crankmaths.h" 5 | 6 | extern void testPercent(void); 7 | extern void testDivision(void); 8 | extern void testBitShift(void); 9 | extern void test_LOW_PASS_FILTER(void); 10 | 11 | #define UNITY_EXCLUDE_DETAILS 12 | 13 | void setup() 14 | { 15 | pinMode(LED_BUILTIN, OUTPUT); 16 | 17 | // NOTE!!! Wait for >2 secs 18 | // if board doesn't support software reset via Serial.DTR/RTS 19 | #if !defined(SIMULATOR) 20 | delay(2000); 21 | #endif 22 | 23 | UNITY_BEGIN(); // IMPORTANT LINE! 24 | 25 | testCrankMaths(); 26 | testPercent(); 27 | testDivision(); 28 | testBitShift(); 29 | test_LOW_PASS_FILTER(); 30 | 31 | UNITY_END(); // stop unit testing 32 | 33 | #if defined(SIMULATOR) // Tell SimAVR we are done 34 | cli(); 35 | sleep_enable(); 36 | sleep_cpu(); 37 | #endif 38 | } 39 | 40 | void loop() 41 | { 42 | // Blink to indicate end of test 43 | digitalWrite(LED_BUILTIN, HIGH); 44 | delay(250); 45 | digitalWrite(LED_BUILTIN, LOW); 46 | delay(250); 47 | } -------------------------------------------------------------------------------- /speeduino/src/FastCRC/LICENCE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Frank 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/test_decoders/general.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../test_utils.h" 3 | #include "decoders.h" 4 | 5 | static void test_engineIsRunning(void) 6 | { 7 | extern volatile unsigned long toothLastToothTime; 8 | extern unsigned long MAX_STALL_TIME; 9 | 10 | MAX_STALL_TIME = 1000; 11 | toothLastToothTime = 0; 12 | TEST_ASSERT_TRUE(engineIsRunning(toothLastToothTime+MAX_STALL_TIME-1UL)); 13 | TEST_ASSERT_FALSE(engineIsRunning(toothLastToothTime+MAX_STALL_TIME)); 14 | TEST_ASSERT_FALSE(engineIsRunning(toothLastToothTime+MAX_STALL_TIME+1UL)); 15 | 16 | // Simulate an interrupt for a pulse being triggered between a call 17 | // to micros() (1000) and the call to engineIsRunning() 18 | toothLastToothTime = 2500; 19 | TEST_ASSERT_TRUE(engineIsRunning(1000UL)); 20 | 21 | TEST_ASSERT_TRUE(engineIsRunning(2499UL)); 22 | TEST_ASSERT_TRUE(engineIsRunning(2500UL)); 23 | TEST_ASSERT_TRUE(engineIsRunning(2501UL)); 24 | 25 | TEST_ASSERT_FALSE(engineIsRunning(toothLastToothTime+MAX_STALL_TIME)); 26 | } 27 | 28 | void testDecoder_General() 29 | { 30 | SET_UNITY_FILENAME() { 31 | RUN_TEST(test_engineIsRunning); 32 | } 33 | } -------------------------------------------------------------------------------- /post_extra_script.py: -------------------------------------------------------------------------------- 1 | Import("env") 2 | 3 | # see https://github.com/platformio/platformio-core/issues/3742#issuecomment-1003454439 4 | def wait_for_monitor_port(source, target, env): 5 | # "pio test" has no delay between upload & monitoring. Unfortuneatly, the teensy 6 | # is rebooting at that point and the port isn't available. This rasies an exception. 7 | port = env.GetProjectOption("monitor_port") 8 | if port is None: 9 | from platformio.builder.tools.pioupload import AutodetectUploadPort 10 | AutodetectUploadPort(env) 11 | port = env.subst("$UPLOAD_PORT") 12 | if port: 13 | # We have a port specified, wait for it to 14 | # activate 15 | print(f"Waiting for port {port}...") 16 | import serial 17 | while True: 18 | try: 19 | serial.Serial(port) 20 | print("...done!") 21 | return 22 | except: 23 | pass 24 | 25 | # No port specified, try a generic delay 26 | print("Delay while uploading...") 27 | import time 28 | time.sleep(2) 29 | print("...done!") 30 | 31 | env.AddPostAction("upload", wait_for_monitor_port) -------------------------------------------------------------------------------- /speeduino/src/FastCRC/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For FastCRC 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | FastCRC7 KEYWORD1 10 | FastCRC8 KEYWORD1 11 | FastCRC16 KEYWORD1 12 | FastCRC32 KEYWORD1 13 | CRC7 KEYWORD1 14 | CRC8 KEYWORD1 15 | CRC16 KEYWORD1 16 | CRC32 KEYWORD1 17 | 18 | ####################################### 19 | # Methods and Functions (KEYWORD2) 20 | ####################################### 21 | crc7 KEYWORD2 22 | crc7_upd KEYWORD2 23 | ccitt KEYWORD2 24 | ccitt_upd KEYWORD2 25 | kermit KEYWORD2 26 | kermit_upd KEYWORD2 27 | mcrf4xx KEYWORD2 28 | mcrf4xx_upd KEYWORD2 29 | modbus KEYWORD2 30 | modbus_upd KEYWORD2 31 | xmodem KEYWORD2 32 | xmodem_upd KEYWORD2 33 | x25 KEYWORD2 34 | x25_upd KEYWORD2 35 | update KEYWORD2 36 | update_upd KEYWORD2 37 | generic KEYWORD2 38 | crc32 KEYWORD2 39 | crc32_upd KEYWORD2 40 | cksum KEYWORD2 41 | cksum_upd KEYWORD2 42 | 43 | ####################################### 44 | # Constants (LITERAL1) 45 | ####################################### 46 | 47 | CRC_FLAG_NOREFLECT LITERAL1 48 | CRC_FLAG_REFLECT LITERAL1 49 | CRC_FLAG_XOR LITERAL1 50 | CRC_FLAG_NOREFLECT_8 LITERAL1 51 | CRC_FLAG_REFLECT_SWAP LITERAL1 52 | 53 | -------------------------------------------------------------------------------- /speeduino/comms_sd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | //Hardcoded TunerStudio addresses/commands for various SD/RTC commands 5 | #define SD_READWRITE_PAGE 0x11 6 | #define SD_READFILE_PAGE 0x14 7 | #define SD_RTC_PAGE 0x07 8 | 9 | #define SD_READ_STAT_ARG1 0x0000 10 | #define SD_READ_STAT_ARG2 0x0010 11 | #define SD_READ_DIR_ARG1 0x0000 12 | #define SD_READ_DIR_ARG2 0x0202 13 | #define SD_READ_SEC_ARG1 0x0002 14 | #define SD_READ_SEC_ARG2 0x0004 15 | #define SD_READ_STRM_ARG1 0x0004 16 | #define SD_READ_STRM_ARG2 0x0001 17 | #define SD_READ_COMP_ARG1 0x0000 //Not used for anything 18 | #define SD_READ_COMP_ARG2 0x0800 19 | #define SD_RTC_READ_ARG1 0x024D 20 | #define SD_RTC_READ_ARG2 0x0008 21 | 22 | #define SD_WRITE_DO_ARG1 0x0000 23 | #define SD_WRITE_DO_ARG2 0x0001 24 | #define SD_WRITE_DIR_ARG1 0x0001 25 | #define SD_WRITE_DIR_ARG2 0x0002 26 | #define SD_WRITE_READ_SEC_ARG1 0x0002 27 | #define SD_WRITE_READ_SEC_ARG2 0x0004 28 | #define SD_WRITE_WRITE_SEC_ARG1 0x0003 29 | #define SD_WRITE_WRITE_SEC_ARG2 0x0204 30 | #define SD_WRITE_COMP_ARG1 0x0005 31 | #define SD_WRITE_COMP_ARG2 0x0008 32 | #define SD_ERASEFILE_ARG1 0x0006 33 | #define SD_ERASEFILE_ARG2 0x0006 34 | #define SD_SPD_TEST_ARG1 0x0007 35 | #define SD_SPD_TEST_ARG2 0x0004 36 | #define SD_RTC_WRITE_ARG1 0x027E 37 | #define SD_RTC_WRITE_ARG2 0x0009 -------------------------------------------------------------------------------- /test/test_decoders/test_decoders.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "missing_tooth/missing_tooth.h" 8 | #include "dual_wheel/dual_wheel.h" 9 | #include "renix/renix.h" 10 | #include "Nissan360/Nissan360.h" 11 | #include "FordST170/FordST170.h" 12 | #include "NGC/test_ngc.h" 13 | #include "SuzukiK6A/SuzukiK6A.h" 14 | 15 | extern void testDecoder_General(void); 16 | 17 | void setup() 18 | { 19 | pinMode(LED_BUILTIN, OUTPUT); 20 | 21 | // NOTE!!! Wait for >2 secs 22 | // if board doesn't support software reset via Serial.DTR/RTS 23 | #if !defined(SIMULATOR) 24 | delay(2000); 25 | #endif 26 | 27 | UNITY_BEGIN(); // IMPORTANT LINE! 28 | 29 | testMissingTooth(); 30 | testDualWheel(); 31 | testRenix(); 32 | testNissan360(); 33 | testFordST170(); 34 | testNGC(); 35 | testSuzukiK6A_setEndTeeth(); 36 | testSuzukiK6A_getCrankAngle(); 37 | testDecoder_General(); 38 | 39 | UNITY_END(); // stop unit testing 40 | 41 | #if defined(SIMULATOR) // Tell SimAVR we are done 42 | cli(); 43 | sleep_enable(); 44 | sleep_cpu(); 45 | #endif 46 | } 47 | 48 | void loop() 49 | { 50 | // Blink to indicate end of test 51 | digitalWrite(LED_BUILTIN, HIGH); 52 | delay(250); 53 | digitalWrite(LED_BUILTIN, LOW); 54 | delay(250); 55 | } -------------------------------------------------------------------------------- /speeduino/load_source.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "statuses.h" 4 | 5 | /** \enum LoadSource 6 | * @brief The load source for various tables 7 | * */ 8 | enum LoadSource { 9 | /** Manifold Absolute Pressure (MAP). Aka Intake MAP (IMAP). 10 | * I.e. a pressure sensor that reads the pressure (positive or negative) 11 | * in the intake manifold */ 12 | LOAD_SOURCE_MAP, 13 | /** Throttle Position Sensor (TPS)*/ 14 | LOAD_SOURCE_TPS, 15 | /** Ratio of intake pressure to exhaust pressure. 16 | * A variation of the standard MAP that uses the ratio of inlet manifold pressure to exhaust manifold pressure, 17 | * something that is particularly useful on turbo engines. */ 18 | LOAD_SOURCE_IMAPEMAP 19 | }; 20 | 21 | /** 22 | * @brief Get the load value, based the supplied algorithm 23 | * 24 | * @param algorithm The load algorithm 25 | * @param current The current system state, from which the load is computed. 26 | * @return The load. 27 | */ 28 | static inline int16_t getLoad(LoadSource algorithm, const statuses ¤t) { 29 | if (algorithm == LOAD_SOURCE_TPS) 30 | { 31 | //Alpha-N 32 | return current.TPS * 2U; 33 | } 34 | else if (algorithm == LOAD_SOURCE_IMAPEMAP) 35 | { 36 | //IMAP / EMAP 37 | return ((int16_t)current.MAP * 100) / current.EMAP; 38 | } else { 39 | // LOAD_SOURCE_MAP (the default). Aka Speed Density 40 | return current.MAP; 41 | } 42 | } -------------------------------------------------------------------------------- /speeduino/logger.h: -------------------------------------------------------------------------------- 1 | /** \file logger.h 2 | * @brief File for generating log files and meta data 3 | * @author Josh Stewart 4 | * 5 | * This file contains functions for creating a log file for use with by TunerStudio directly or to be written to an SD card 6 | * 7 | */ 8 | 9 | #ifndef LOGGER_H 10 | #define LOGGER_H 11 | 12 | #include "globals.h" // Needed for FPU_MAX_SIZE 13 | 14 | #ifndef UNIT_TEST // Scope guard for unit testing 15 | #define LOG_ENTRY_SIZE 130 /**< The size of the live data packet. This MUST match ochBlockSize setting in the ini file */ 16 | #else 17 | #define LOG_ENTRY_SIZE 1 /**< The size of the live data packet. This MUST match ochBlockSize setting in the ini file */ 18 | #endif 19 | 20 | byte getTSLogEntry(uint16_t byteNum); 21 | int16_t getReadableLogEntry(uint16_t logIndex); 22 | #if defined(FPU_MAX_SIZE) && FPU_MAX_SIZE >= 32 //cppcheck-suppress misra-c2012-20.9 23 | float getReadableFloatLogEntry(uint16_t logIndex); 24 | #endif 25 | uint8_t getLegacySecondarySerialLogEntry(uint16_t byteNum); 26 | bool is2ByteEntry(uint8_t key); 27 | 28 | void startToothLogger(void); 29 | void stopToothLogger(void); 30 | 31 | void startCompositeLogger(void); 32 | void stopCompositeLogger(void); 33 | 34 | void startCompositeLoggerTertiary(void); 35 | void stopCompositeLoggerTertiary(void); 36 | 37 | void startCompositeLoggerCams(void); 38 | void stopCompositeLoggerCams(void); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /.github/workflows/codespell.yml: -------------------------------------------------------------------------------- 1 | name: Run codespell 2 | 3 | on: 4 | # Triggers the workflow on push or pull request events but only for the master branch 5 | push: 6 | branches: [ master ] 7 | pull_request: 8 | branches: [ master ] 9 | 10 | # Allows you to run this workflow manually from the Actions tab 11 | workflow_dispatch: 12 | 13 | jobs: 14 | codespell: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | 20 | # codespell is not installed in the runner by default 21 | - name: install codespell 22 | run: | 23 | sudo apt update 24 | sudo apt install codespell 25 | 26 | - name: identify codespell 27 | run: codespell --version 28 | 29 | # use latest available dictionaries 30 | - name: update dictionary 31 | run: | 32 | curl --output-dir /tmp --remote-name https://raw.githubusercontent.com/codespell-project/codespell/master/codespell_lib/data/dictionary.txt 33 | curl --output-dir /tmp --remote-name https://raw.githubusercontent.com/codespell-project/codespell/master/codespell_lib/data/dictionary_rare.txt 34 | 35 | # If count >0 will trigger a workflow failure, else success 36 | - name: run codespell 37 | run: codespell --dictionary=/tmp/dictionary.txt --dictionary=/tmp/dictionary_rare.txt --ignore-words=./.github/workflows/codespell-ignored-words.txt --count ./speeduino/ 38 | -------------------------------------------------------------------------------- /speeduino/src/BackupSram/BackupSramAsEEPROM.h: -------------------------------------------------------------------------------- 1 | //Backup sram stores data in the battery backuped sram portion. 2 | //The backup battery is available on the ebay stm32F407VET6 black boards. 3 | #ifndef BACKUPSRAMASEEPROM_H 4 | #define BACKUPSRAMASEEPROM_H 5 | #if defined(STM32F407xx) 6 | #include 7 | #include "stm32f407xx.h" 8 | 9 | class BackupSramAsEEPROM { 10 | private: 11 | const uint16_t backup_size = 4096; //maximum of 4kb backuped sram available. 12 | int8_t write_byte( uint8_t *data, uint16_t bytes, uint16_t offset ); 13 | int8_t read_byte( uint8_t *data, uint16_t bytes, uint16_t offset ); 14 | 15 | public: 16 | BackupSramAsEEPROM(); 17 | uint8_t read(uint16_t address); 18 | int8_t write(uint16_t address, uint8_t val); 19 | int8_t update(uint16_t address, uint8_t val); 20 | uint16_t length(); 21 | template< typename T > T &get( int idx, T &t ){ 22 | uint16_t e = idx; 23 | uint8_t *ptr = (uint8_t*) &t; 24 | for( int count = sizeof(T) ; count ; --count, ++e ) *ptr++ = read(e); 25 | return t; 26 | } 27 | template< typename T > const T &put( int idx, const T &t ){ 28 | const uint8_t *ptr = (const uint8_t*) &t; 29 | uint16_t e = idx; 30 | for( int count = sizeof(T) ; count ; --count, ++e ) write(e, *ptr++); 31 | return t; 32 | } 33 | }; 34 | 35 | extern BackupSramAsEEPROM EEPROM; 36 | 37 | #endif 38 | #endif -------------------------------------------------------------------------------- /speeduino/comms.h: -------------------------------------------------------------------------------- 1 | /** \file comms.h 2 | * @brief File for handling all serial requests 3 | * @author Josh Stewart 4 | * 5 | * This file contains all the functions associated with serial comms. 6 | * This includes sending of live data, sending/receiving current page data, sending CRC values of pages, receiving sensor calibration data etc 7 | * 8 | */ 9 | 10 | #ifndef COMMS_H 11 | #define COMMS_H 12 | 13 | #if defined(CORE_TEENSY) 14 | #define BLOCKING_FACTOR 251 15 | #define TABLE_BLOCKING_FACTOR 256 16 | #elif defined(CORE_STM32) 17 | #define BLOCKING_FACTOR 121 18 | #define TABLE_BLOCKING_FACTOR 64 19 | #elif defined(CORE_AVR) 20 | #define BLOCKING_FACTOR 121 21 | #define TABLE_BLOCKING_FACTOR 64 22 | #endif 23 | 24 | extern Stream *pPrimarySerial; 25 | #define primarySerial (*pPrimarySerial) 26 | 27 | extern uint32_t serialReceiveStartTime; //!< The time in milliseconds at which the serial receive started. Used for calculating whether a timeout has occurred 28 | 29 | /** 30 | * @brief The serial receive pump. Should be called whenever the serial port 31 | * has data available to read. 32 | */ 33 | void serialReceive(void); 34 | 35 | /** @brief The serial transmit pump. Should be called when ::serialStatusFlag indicates a transmit 36 | * operation is in progress */ 37 | void serialTransmit(void); 38 | 39 | /** @brief Checks whether the current serial command should be timed out 40 | * 41 | * @return true if the serial command has been waiting too long 42 | */ 43 | bool isRxTimeout(void); 44 | 45 | #endif // COMMS_H 46 | -------------------------------------------------------------------------------- /speeduino/src/SPIAsEEPROM/SPIMemory.cpp: -------------------------------------------------------------------------------- 1 | /* Arduino SPIMemory Library v.3.2.0 2 | * Copyright (C) 2017 by Prajwal Bhattaram 3 | * Created by Prajwal Bhattaram - 19/05/2015 4 | * Modified by @boseji - 02/03/2017 5 | * Modified by Prajwal Bhattaram - 24/02/2018 6 | * 7 | * This file is part of the Arduino SPIMemory Library. This library is for 8 | * Winbond NOR flash memory modules. In its current form it enables reading 9 | * and writing individual data variables, structs and arrays from and to various locations; 10 | * reading and writing pages; continuous read functions; sector, block and chip erase; 11 | * suspending and resuming programming/erase and powering down for low power operation. 12 | * 13 | * This Library is free software: you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation, either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * This Library is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU General Public License v3.0 24 | * along with the Arduino SPIMemory Library. If not, see 25 | * . 26 | */ 27 | 28 | #include "SPIMemory.h" 29 | 30 | SPIMemory SPIMemory; // default instantiation of SPIMemory object 31 | -------------------------------------------------------------------------------- /speeduino/table3d_typedefs.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @addtogroup table_3d 3 | * @{ 4 | */ 5 | 6 | 7 | /** \file 8 | * @brief Typedefs for primitive 3D table elements 9 | * 10 | * These used are for consistency across functions that work on 3D table data. 11 | * For example:
12 | * table3d_value_t foo(table3d_axis_t input);
13 | * instead of:
14 | * uint8_t foo(int16_t input); 15 | */ 16 | 17 | #pragma once 18 | 19 | #include 20 | 21 | /** @brief Encodes the \b length of the axes */ 22 | using table3d_dim_t = uint8_t; 23 | 24 | /** @brief The type of each table value */ 25 | using table3d_value_t = uint8_t; 26 | 27 | /** @brief The type of each axis value */ 28 | using table3d_axis_t = int16_t; 29 | 30 | /** @brief Core 3d table generation macro 31 | * 32 | * We have a fixed number of table types: they are defined by this macro. 33 | * GENERATOR is expected to be another macros that takes at least 3 arguments: 34 | * axis length, x-axis domain, y-axis domain 35 | */ 36 | #define TABLE3D_GENERATOR(GENERATOR, ...) \ 37 | GENERATOR(6, Rpm, Load, ##__VA_ARGS__) \ 38 | GENERATOR(4, Rpm, Load, ##__VA_ARGS__) \ 39 | GENERATOR(8, Rpm, Load, ##__VA_ARGS__) \ 40 | GENERATOR(8, Rpm, Tps, ##__VA_ARGS__) \ 41 | GENERATOR(16, Rpm, Load, ##__VA_ARGS__) 42 | 43 | // Each 3d table is given a distinct type based on size & axis domains 44 | // This encapsulates the generation of the type name 45 | #define TABLE3D_TYPENAME_BASE(size, xDom, yDom) table3d ## size ## xDom ## yDom 46 | 47 | #define CAT_HELPER(a, b) a ## b 48 | #define CONCAT(A, B) CAT_HELPER(A, B) 49 | 50 | /** @} */ -------------------------------------------------------------------------------- /speeduino/table3d_axis_io.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @addtogroup table_3d 3 | * @{ 4 | */ 5 | 6 | /** \file 7 | * @brief 3D table axis I/O support 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "table3d_axes.h" 13 | 14 | /** @brief Byte type. This is not defined in any C or C++ standard header. */ 15 | typedef uint8_t byte; 16 | 17 | /** @brief Models int16->byte conversion 18 | * @see table3d_axis_io_converter 19 | */ 20 | typedef byte(*pToByteConverter)(int16_t value); 21 | 22 | /** @brief Models byte->int16 conversion 23 | * @see table3d_axis_io_converter 24 | */ 25 | typedef int16_t(*pFromByteConverter)(byte in); 26 | 27 | /** @brief Convert a 16-bit value to/from a byte. Useful for I/O. 28 | * 29 | * Often we need to deal internally with values that fit in 16-bits but do 30 | * not require much accuracy. E.g. table axes in RPM. For these values we can 31 | * save storage space (EEPROM) by scaling to/from 8-bits. 32 | */ 33 | struct table3d_axis_io_converter { 34 | pToByteConverter to_byte; 35 | pFromByteConverter from_byte; 36 | }; 37 | 38 | /** 39 | * @brief Obtain a converter instance for a given axis domain. 40 | * 41 | * Often we need to deal internally with values that fit in 16-bits but do 42 | * not require much accuracy. E.g. RPM. For these values we can 43 | * save storage space (EEPROM) by scaling to/from 8-bits. 44 | * 45 | * The converter is dependent on the domain. I.e all axes with the same domain use 46 | * the same converter 47 | * 48 | * Conversion during I/O is orthogonal to other axis concerns, so is separated and 49 | * encapsulated here. 50 | */ 51 | table3d_axis_io_converter get_table3d_axis_converter(axis_domain domain); 52 | 53 | /** @} */ -------------------------------------------------------------------------------- /speeduino/idle.h: -------------------------------------------------------------------------------- 1 | #ifndef IDLE_H 2 | #define IDLE_H 3 | 4 | #include "globals.h" 5 | #include "table2d.h" 6 | #include BOARD_H //Note that this is not a real file, it is defined in globals.h. 7 | 8 | #define IAC_ALGORITHM_NONE 0U 9 | #define IAC_ALGORITHM_ONOFF 1U 10 | #define IAC_ALGORITHM_PWM_OL 2U 11 | #define IAC_ALGORITHM_PWM_CL 3U 12 | #define IAC_ALGORITHM_STEP_OL 4U 13 | #define IAC_ALGORITHM_STEP_CL 5U 14 | #define IAC_ALGORITHM_PWM_OLCL 6U //Openloop plus closedloop IAC control 15 | #define IAC_ALGORITHM_STEP_OLCL 7U //Openloop plus closedloop IAC control 16 | 17 | #define IDLE_PIN_LOW() *idle_pin_port &= ~(idle_pin_mask) 18 | #define IDLE_PIN_HIGH() *idle_pin_port |= (idle_pin_mask) 19 | #define IDLE2_PIN_LOW() *idle2_pin_port &= ~(idle2_pin_mask) 20 | #define IDLE2_PIN_HIGH() *idle2_pin_port |= (idle2_pin_mask) 21 | 22 | #define STEPPER_FORWARD 0 23 | #define STEPPER_BACKWARD 1 24 | #define STEPPER_POWER_WHEN_ACTIVE 0 25 | #define IDLE_TABLE_SIZE 10 26 | 27 | enum StepperStatus {SOFF, STEPPING, COOLING}; //The 2 statuses that a stepper can have. STEPPING means that a high pulse is currently being sent and will need to be turned off at some point. 28 | 29 | struct StepperIdle 30 | { 31 | int curIdleStep; //Tracks the current location of the stepper 32 | int targetIdleStep; //What the targeted step is 33 | volatile StepperStatus stepperStatus; 34 | volatile unsigned long stepStartTime; 35 | }; 36 | 37 | extern uint16_t idle_pwm_max_count; //Used for variable PWM frequency 38 | extern long FeedForwardTerm; 39 | 40 | void initialiseIdle(bool forcehoming); 41 | void idleControl(void); 42 | void initialiseIdleUpOutput(void); 43 | void disableIdle(void); 44 | void idleInterrupt(void); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /speeduino/src/FastCRC/examples/FastCRC_validate/FastCRC_validate.ino: -------------------------------------------------------------------------------- 1 | //FastCRC 2 | //Validate computed CRCs 3 | // 4 | //(c) Frank Boesing 2014 5 | 6 | #include 7 | #include 8 | 9 | FastCRC7 CRC7; 10 | FastCRC8 CRC8; 11 | FastCRC16 CRC16; 12 | FastCRC32 CRC32; 13 | 14 | 15 | uint8_t buf[9] = {'1','2','3','4','5','6','7','8','9'}; 16 | 17 | 18 | void printVals(const char * name, uint32_t check, uint32_t val){ 19 | Serial.print(name); 20 | if (check == val) 21 | Serial.print(" is ok"); 22 | else 23 | Serial.print(" is NOT ok"); 24 | Serial.println(); 25 | } 26 | 27 | void setup() { 28 | uint32_t crc; 29 | 30 | delay(1500); 31 | Serial.begin(115200); 32 | 33 | Serial.println("CRC Validation"); 34 | 35 | crc = CRC7.crc7(buf, sizeof(buf)); 36 | printVals("CRC7", 0x75, crc); 37 | 38 | crc = CRC8.smbus(buf, sizeof(buf)); 39 | printVals("SMBUS", 0xf4, crc); 40 | 41 | crc = CRC8.maxim(buf, sizeof(buf)); 42 | printVals("Maxim", 0xa1, crc); 43 | 44 | crc = CRC16.ccitt(buf, sizeof(buf)); 45 | printVals("CCITT", 0x29b1, crc); 46 | 47 | crc = CRC16.mcrf4xx(buf, sizeof(buf)); 48 | printVals("MCRF4XX", 0x6f91, crc); 49 | 50 | crc = CRC16.modbus(buf, sizeof(buf)); 51 | printVals("MODBUS", 0x4b37, crc); 52 | 53 | crc = CRC16.kermit(buf, sizeof(buf)); 54 | printVals("KERMIT", 0x2189, crc); 55 | 56 | crc = CRC16.xmodem(buf, sizeof(buf)); 57 | printVals("XMODEM", 0x31c3, crc); 58 | 59 | crc = CRC16.x25(buf, sizeof(buf)); 60 | printVals("X.25", 0x906e, crc); 61 | 62 | crc = CRC32.crc32(buf, sizeof(buf)); 63 | printVals("CRC32", 0xcbf43926, crc); 64 | 65 | crc = CRC32.cksum(buf, sizeof(buf)); 66 | printVals("CKSUM", 0x765e7680, crc); 67 | } 68 | 69 | 70 | void loop() { 71 | } 72 | -------------------------------------------------------------------------------- /speeduino/src/PID_v1/Examples/PID_AdaptiveTunings/PID_AdaptiveTunings.ino: -------------------------------------------------------------------------------- 1 | /******************************************************** 2 | * PID Adaptive Tuning Example 3 | * One of the benefits of the PID library is that you can 4 | * change the tuning parameters at any time. this can be 5 | * helpful if we want the controller to be aggressive at some 6 | * times, and conservative at others. in the example below 7 | * we set the controller to use Conservative Tuning Parameters 8 | * when we're near setpoint and more aggressive Tuning 9 | * Parameters when we're farther away. 10 | ********************************************************/ 11 | 12 | #include 13 | 14 | //Define Variables we'll be connecting to 15 | double Setpoint, Input, Output; 16 | 17 | //Define the aggressive and conservative Tuning Parameters 18 | double aggKp=4, aggKi=0.2, aggKd=1; 19 | double consKp=1, consKi=0.05, consKd=0.25; 20 | 21 | //Specify the links and initial tuning parameters 22 | PID myPID(&Input, &Output, &Setpoint, consKp, consKi, consKd, DIRECT); 23 | 24 | void setup() 25 | { 26 | //initialize the variables we're linked to 27 | Input = analogRead(0); 28 | Setpoint = 100; 29 | 30 | //turn the PID on 31 | myPID.SetMode(AUTOMATIC); 32 | } 33 | 34 | void loop() 35 | { 36 | Input = analogRead(0); 37 | 38 | double gap = abs(Setpoint-Input); //distance away from setpoint 39 | if(gap<10) 40 | { //we're close to setpoint, use conservative tuning parameters 41 | myPID.SetTunings(consKp, consKi, consKd); 42 | } 43 | else 44 | { 45 | //we're far from setpoint, use aggressive tuning parameters 46 | myPID.SetTunings(aggKp, aggKi, aggKd); 47 | } 48 | 49 | myPID.Compute(); 50 | analogWrite(3,Output); 51 | } 52 | 53 | 54 | -------------------------------------------------------------------------------- /speeduino/src/FastCRC/README.md: -------------------------------------------------------------------------------- 1 | FastCRC 2 | ======= 3 | 4 | Fast CRC Arduino library 5 | Up to 30 times faster than crc16.h (_avr_libc) 6 | 7 | - uses the on-chip hardware for Teensy 3.0 / 3.1 / 3.2 / 3.5 / 3.6 8 | - uses fast table-algorithms for other chips 9 | 10 | List of supported CRC calculations: 11 | - 12 | 7 BIT: 13 | 14 | CRC7 15 | (poly=0x09 init=0x00 refin=false refout=false xorout=0x00 check=0x75) 16 | MultiMediaCard interface 17 | 18 | 19 | 8 BIT: 20 | 21 | SMBUS 22 | (poly=0x07 init=0x00 refin=false refout=false xorout=0x00 check=0xf4) 23 | 24 | MAXIM 25 | (poly=0x31 init=0x00 refin=true refout=true xorout=0x00 check=0xa1) 26 | 27 | 28 | 16 BIT: 29 | 30 | KERMIT (Alias CRC-16/CCITT, CRC-16/CCITT-TRUE, CRC-CCITT) 31 | (poly=0x1021 init=0x0000 refin=true refout=true xorout=0x0000 check=0x2189 32 | Attention: sometimes you'll find byteswapped presentation of result in other implementations) 33 | 34 | CCITT-FALSE 35 | (poly=0x1021 init=0xffff refin=false refout=false xorout=0x0000 check=0x29b1) 36 | 37 | MCRF4XX 38 | (poly=0x1021 init=0xffff refin=true refout=true xorout=0x0000 check=0x6f91) 39 | 40 | MODBUS 41 | (poly=0x8005 init=0xffff refin=true refout=true xorout=0x0000 check=0x4b37) 42 | 43 | XMODEM (Alias ZMODEM, CRC-16/ACORN) 44 | (poly=0x1021 init=0x0000 refin=false refout=false xorout=0x0000 check=0x31c3) 45 | 46 | X25 (Alias CRC-16/IBM-SDLC, CRC-16/ISO-HDLC, CRC-B) 47 | (poly=0x1021 init=0xffff refin=true refout=true xorout=0xffff check=0x906e) 48 | 49 | 50 | 32 BIT: 51 | 52 | CRC32, CRC-32/ADCCP, PKZIP, ETHERNET, 802.3 53 | (poly=0x04c11db7 init=0xffffffff refin=true refout=true xorout=0xffffffff check=0xcbf43926) 54 | 55 | CKSUM, CRC-32/POSIX 56 | (poly=0x04c11db7 init=0x00000000 refin=false refout=false xorout=0xffffffff check=0x765e7680) 57 | -------------------------------------------------------------------------------- /test/test_init/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "storage.h" 5 | #include "globals.h" 6 | 7 | // Since it's almost impossible for the tests to clean up 8 | // after themselves, we need to reset the global context 9 | // prior to each test running. 10 | // 11 | // Since each test is (usually) testing the results of 12 | // initialiseAll(), the flow is: 13 | // 1. prepareForInitialise() 14 | // 2. Set any config page values. 15 | // 3. initialiseAll() 16 | // 4. ASSERT on the results. 17 | void prepareForInitialiseAll(uint8_t boardId) { 18 | resetConfigPages(); 19 | // This is required to prevent initialiseAll() also 20 | // calling resetConfigPages & thus blatting any 21 | // configuration made in step 2. 22 | configPage2.pinMapping = boardId; 23 | currentStatus.initialisationComplete = false; 24 | } 25 | 26 | 27 | void testInitialisation(void); 28 | void testFuelScheduleInit(void); 29 | void testIgnitionScheduleInit(void); 30 | 31 | #define UNITY_EXCLUDE_DETAILS 32 | 33 | void setup() 34 | { 35 | pinMode(LED_BUILTIN, OUTPUT); 36 | 37 | // NOTE!!! Wait for >2 secs 38 | // if board doesn't support software reset via Serial.DTR/RTS 39 | #if !defined(SIMULATOR) 40 | delay(2000); 41 | #endif 42 | 43 | UNITY_BEGIN(); // IMPORTANT LINE! 44 | 45 | testFuelScheduleInit(); 46 | testIgnitionScheduleInit(); 47 | testInitialisation(); 48 | 49 | UNITY_END(); // stop unit testing 50 | 51 | #if defined(SIMULATOR) // Tell SimAVR we are done 52 | cli(); 53 | sleep_enable(); 54 | sleep_cpu(); 55 | #endif 56 | } 57 | 58 | void loop() 59 | { 60 | // Blink to indicate end of test 61 | digitalWrite(LED_BUILTIN, HIGH); 62 | delay(250); 63 | digitalWrite(LED_BUILTIN, LOW); 64 | delay(250); 65 | } -------------------------------------------------------------------------------- /.github/workflows/doxygen.yml: -------------------------------------------------------------------------------- 1 | name: Build and update Doxygen documentation 2 | 3 | # Controls when the action will run. Triggers the workflow on push or pull request 4 | # events but only for the master branch 5 | on: 6 | push: 7 | branches: [ master ] 8 | 9 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 10 | jobs: 11 | # This workflow contains a single job called "build" 12 | build: 13 | # The type of runner that the job will run on 14 | runs-on: ubuntu-latest 15 | 16 | # Steps represent a sequence of tasks that will be executed as part of the job 17 | steps: 18 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 19 | - uses: actions/checkout@v4 20 | 21 | - name: Build Doxygen documentation 22 | uses: mattnotmitt/doxygen-action@v1.9 23 | with: 24 | doxyfile-path: "./Doxyfile" # default is ./Doxyfile 25 | working-directory: "." # default is . 26 | 27 | - name: Setup git config 28 | if: github.repository_owner == 'speeduino' 29 | run: | 30 | git config --global user.name "GitHub Actions Bot" 31 | git config --global user.email "<>" 32 | 33 | - name: Commit updated Doxygen documentation 34 | if: github.event_name != 'pull_request' && github.repository_owner == 'speeduino' 35 | env: 36 | GH_DOXYGEN: ${{ secrets.GH_DOXYGEN }} 37 | run: | 38 | cd .. 39 | git clone https://github.com/speeduino/speeduino-doxygen 40 | cp -r speeduino/reference/doxygen/html/* speeduino-doxygen 41 | cd speeduino-doxygen 42 | git add --all 43 | git commit --allow-empty -m "Deploy code docs to GitHub repo. Commit ${GITHUB_SHA}" 44 | git push --force "https://${GH_DOXYGEN}@github.com/speeduino/speeduino-doxygen" > /dev/null 2>&1 45 | -------------------------------------------------------------------------------- /speeduino/table3d_interpolate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "table3d_typedefs.h" 4 | 5 | // A table location. 6 | struct coord2d 7 | { 8 | table3d_axis_t x; 9 | table3d_axis_t y; 10 | }; 11 | 12 | 13 | struct table3DGetValueCache { 14 | // Store the upper *index* of the X and Y axis bins that were last hit. 15 | // This is used to make the next check faster since very likely the x & y values have 16 | // only changed by a small amount & are in the same bin (or an adjacent bin). 17 | // 18 | // It's implicit that the other bin index is max bin index - 1 (a single axis 19 | // value can't span 2 axis bins). This saves 1 byte. 20 | // 21 | // E.g. 6 element x-axis contents: 22 | // [ 8| 9|12|15|18|21] 23 | // indices: 24 | // 0, 1, 2, 3, 4, 5 25 | // If lastXBinMax==3, the min index must be 2. I.e. the last X value looked 26 | // up was between 12last_lookup.x = INT16_MAX; 39 | } 40 | 41 | /* 42 | 3D Tables have an origin (0,0) in the top left hand corner. Vertical axis is expressed first. 43 | Eg: 2x2 table 44 | ----- 45 | |2 7| 46 | |1 4| 47 | ----- 48 | 49 | (0,1) = 7 50 | (0,0) = 2 51 | (1,0) = 1 52 | 53 | */ 54 | table3d_value_t get3DTableValue(struct table3DGetValueCache *pValueCache, 55 | table3d_dim_t axisSize, 56 | const table3d_value_t *pValues, 57 | const table3d_axis_t *pXAxis, 58 | const table3d_axis_t *pYAxis, 59 | table3d_axis_t y, table3d_axis_t x); -------------------------------------------------------------------------------- /.github/workflows/unit-tests.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | 3 | # Controls when the workflow will run 4 | on: 5 | # Triggers the workflow on push or pull request events but only for the master branch 6 | push: 7 | branches: [ master ] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | jobs: 13 | build: 14 | # Only try to run hardware unit tests on the upstream repository 15 | if: github.repository_owner == 'speeduino' 16 | 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - name: Set up GCC 23 | uses: egor-tensin/setup-gcc@v1 24 | with: 25 | version: latest 26 | platform: x32 27 | 28 | - name: Set up Python 29 | uses: actions/setup-python@v5 30 | with: 31 | python-version: '3.10' 32 | 33 | - name: Install PlatformIO 34 | run: | 35 | python -m pip install --upgrade pip 36 | pip install --upgrade platformio 37 | pip install wheel 38 | platformio upgrade 39 | platformio pkg update 40 | 41 | - name: Run Unit Tests 42 | env: 43 | PLATFORMIO_AUTH_TOKEN: ${{ secrets.PLATFORMIO_AUTH_TOKEN }} 44 | run: | 45 | platformio run -e megaatmega2560 46 | platformio remote test --force-remote --environment megaatmega2560 47 | 48 | - name: Discord Notification (Unit Tests Failed) 49 | uses: rjstone/discord-webhook-notify@v1 50 | if: failure() 51 | with: 52 | severity: error 53 | avatarUrl: https://avatars.githubusercontent.com/u/9919?v=4&size=48 54 | username: GitHub 55 | description: 'Unit Tests FAILED :warning:' 56 | details: "**Author:** ${{ github.event.pusher.name }} \n 57 | **Commit URL:** ${{ github.event.head_commit.url }} \n 58 | **Commit Message:** ${{ github.event.head_commit.message }}" 59 | webhookUrl: ${{ secrets.DISCORD_WEBHOOK }} -------------------------------------------------------------------------------- /speeduino/utilities.h: -------------------------------------------------------------------------------- 1 | /* 2 | These are some utility functions and variables used through the main code 3 | */ 4 | #ifndef UTILS_H 5 | #define UTILS_H 6 | 7 | #include 8 | 9 | #define CONCATS(s1, s2) (s1" " s2) //needed for some reason. not defined correctly because of utils.h file of speeduino (same name as one in arduino core) 10 | 11 | #define COMPARATOR_EQUAL 0 12 | #define COMPARATOR_NOT_EQUAL 1 13 | #define COMPARATOR_GREATER 2 14 | #define COMPARATOR_GREATER_EQUAL 3 15 | #define COMPARATOR_LESS 4 16 | #define COMPARATOR_LESS_EQUAL 5 17 | #define COMPARATOR_AND 6 18 | #define COMPARATOR_XOR 7 19 | 20 | #define BITWISE_DISABLED 0 21 | #define BITWISE_AND 1 22 | #define BITWISE_OR 2 23 | #define BITWISE_XOR 3 24 | 25 | #define REUSE_RULES 240 26 | 27 | extern uint8_t ioOutDelay[sizeof(configPage13.outputPin)]; 28 | extern uint8_t ioDelay[sizeof(configPage13.outputPin)]; 29 | extern uint8_t pinIsValid; 30 | extern uint8_t currentRuleStatus; 31 | //uint8_t outputPin[sizeof(configPage13.outputPin)]; 32 | 33 | void setResetControlPinState(void); 34 | byte pinTranslate(byte rawPin); 35 | byte pinTranslateAnalog(byte rawPin); 36 | void initialiseProgrammableIO(void); 37 | void checkProgrammableIO(void); 38 | int16_t ProgrammableIOGetData(uint16_t index); 39 | 40 | #if !defined(UNUSED) 41 | #define UNUSED(x) (void)(x) 42 | #endif 43 | 44 | #define _countof(x) (sizeof(x) / sizeof (x[0])) 45 | #define _end_range_address(array) (array + _countof(array)) 46 | #define _end_range_byte_address(array) (((byte*)array) + sizeof(array)) 47 | 48 | // Pre-processor arithmetic increment (pulled from Boost.Preprocessor) 49 | #define PP_INC(x) PP_INC_I(x) 50 | #define PP_INC_I(x) PP_INC_ ## x 51 | 52 | #define PP_INC_0 1 53 | #define PP_INC_1 2 54 | #define PP_INC_2 3 55 | #define PP_INC_3 4 56 | #define PP_INC_4 5 57 | #define PP_INC_5 6 58 | #define PP_INC_6 7 59 | #define PP_INC_7 8 60 | #define PP_INC_8 9 61 | #define PP_INC_9 10 62 | #define PP_INC_10 11 63 | #define PP_INC_11 12 64 | #define PP_INC_12 13 65 | 66 | #endif // UTILS_H 67 | -------------------------------------------------------------------------------- /speeduino/speeduino.h: -------------------------------------------------------------------------------- 1 | /** \file speeduino.h 2 | * @brief Speeduino main file containing initial setup and system loop functions 3 | * @author Josh Stewart 4 | * 5 | * This file contains the main system loop of the Speeduino core and thus much of the logic of the fuel and ignition algorithms is contained within this 6 | * It is where calls to all the auxiliary control systems, sensor reads, comms etc are made 7 | * 8 | * It also contains the setup() function that is called by the bootloader on system startup 9 | * 10 | */ 11 | 12 | #ifndef SPEEDUINO_H 13 | #define SPEEDUINO_H 14 | //#include "globals.h" 15 | 16 | #define CRANK_RUN_HYSTER 15 17 | 18 | void setup(void); 19 | void loop(void); 20 | uint16_t PW(int REQ_FUEL, byte VE, long MAP, uint16_t corrections, int injOpen); 21 | uint8_t getVE1(void); 22 | int8_t getAdvance1(void); 23 | uint16_t calculatePWLimit(); 24 | void calculateStaging(uint32_t); 25 | void calculateIgnitionAngles(uint16_t dwellAngle); 26 | void checkLaunchAndFlatShift(); 27 | 28 | extern uint16_t req_fuel_uS; /**< The required fuel variable (As calculated by TunerStudio) in uS */ 29 | extern uint16_t inj_opentime_uS; /**< The injector opening time. This is set within Tuner Studio, but stored here in uS rather than mS */ 30 | 31 | /** @name Staging 32 | * These values are a percentage of the total (Combined) req_fuel value that would be required for each injector channel to deliver that much fuel. 33 | * 34 | * Eg: 35 | * - Pri injectors are 250cc 36 | * - Sec injectors are 500cc 37 | * - Total injector capacity = 750cc 38 | * 39 | * - staged_req_fuel_mult_pri = 300% (The primary injectors would have to run 3x the overall PW in order to be the equivalent of the full 750cc capacity 40 | * - staged_req_fuel_mult_sec = 150% (The secondary injectors would have to run 1.5x the overall PW in order to be the equivalent of the full 750cc capacity 41 | */ 42 | ///@{ 43 | extern uint16_t staged_req_fuel_mult_pri; 44 | extern uint16_t staged_req_fuel_mult_sec; 45 | ///@} 46 | 47 | 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /speeduino/table2d.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file is used for everything related to maps/tables including their definition, functions etc 3 | */ 4 | #ifndef TABLE_H 5 | #define TABLE_H 6 | 7 | #include "globals.h" 8 | 9 | #define SIZE_SIGNED_BYTE 4 10 | #define SIZE_BYTE 8 11 | #define SIZE_INT 16 12 | 13 | /* 14 | The 2D table can contain either 8-bit (byte) or 16-bit (int) values 15 | The valueSize variable should be set to either 8 or 16 to indicate this BEFORE the table is used 16 | */ 17 | struct table2D { 18 | //Used 5414 RAM with original version 19 | byte valueSize; 20 | byte axisSize; 21 | byte xSize; 22 | 23 | void *values; 24 | void *axisX; 25 | 26 | //int16_t *values16; 27 | //int16_t *axisX16; 28 | 29 | //Store the last X and Y coordinates in the table. This is used to make the next check faster 30 | int16_t lastXMax; 31 | int16_t lastXMin; 32 | 33 | //Store the last input and output for caching 34 | int16_t lastInput; 35 | int16_t lastOutput; 36 | byte cacheTime; //Tracks when the last cache value was set so it can expire after x seconds. A timeout is required to pickup when a tuning value is changed, otherwise the old cached value will continue to be returned as the X value isn't changing. 37 | }; 38 | 39 | void construct2dTable(table2D &table, uint8_t length, uint8_t *values, uint8_t *bins); 40 | void construct2dTable(table2D &table, uint8_t length, uint8_t *values, int8_t *bins); 41 | void construct2dTable(table2D &table, uint8_t length, uint16_t *values, uint16_t *bins); 42 | void construct2dTable(table2D &table, uint8_t length, uint8_t *values, uint16_t *bins); 43 | void construct2dTable(table2D &table, uint8_t length, uint16_t *values, uint8_t *bins); 44 | void construct2dTable(table2D &table, uint8_t length, int16_t *values, uint8_t *bins); 45 | 46 | int16_t table2D_getAxisValue(struct table2D *fromTable, byte X_in); 47 | int16_t table2D_getRawValue(struct table2D *fromTable, byte X_index); 48 | 49 | int table2D_getValue(struct table2D *fromTable, int X_in); 50 | 51 | #endif // TABLE_H 52 | -------------------------------------------------------------------------------- /speeduino/src/PID_v1/Examples/PID_RelayOutput/PID_RelayOutput.ino: -------------------------------------------------------------------------------- 1 | /******************************************************** 2 | * PID RelayOutput Example 3 | * Same as basic example, except that this time, the output 4 | * is going to a digital pin which (we presume) is controlling 5 | * a relay. the pid is designed to Output an analog value, 6 | * but the relay can only be On/Off. 7 | * 8 | * to connect them together we use "time proportioning 9 | * control" it's essentially a really slow version of PWM. 10 | * first we decide on a window size (5000mS say.) we then 11 | * set the pid to adjust its output between 0 and that window 12 | * size. lastly, we add some logic that translates the PID 13 | * output into "Relay On Time" with the remainder of the 14 | * window being "Relay Off Time" 15 | ********************************************************/ 16 | 17 | #include 18 | #define RelayPin 6 19 | 20 | //Define Variables we'll be connecting to 21 | double Setpoint, Input, Output; 22 | 23 | //Specify the links and initial tuning parameters 24 | PID myPID(&Input, &Output, &Setpoint,2,5,1, DIRECT); 25 | 26 | int WindowSize = 5000; 27 | unsigned long windowStartTime; 28 | void setup() 29 | { 30 | windowStartTime = millis(); 31 | 32 | //initialize the variables we're linked to 33 | Setpoint = 100; 34 | 35 | //tell the PID to range between 0 and the full window size 36 | myPID.SetOutputLimits(0, WindowSize); 37 | 38 | //turn the PID on 39 | myPID.SetMode(AUTOMATIC); 40 | } 41 | 42 | void loop() 43 | { 44 | Input = analogRead(0); 45 | myPID.Compute(); 46 | 47 | /************************************************ 48 | * turn the output pin on/off based on pid output 49 | ************************************************/ 50 | if(millis() - windowStartTime>WindowSize) 51 | { //time to shift the Relay Window 52 | windowStartTime += WindowSize; 53 | } 54 | if(Output < millis() - windowStartTime) digitalWrite(RelayPin,HIGH); 55 | else digitalWrite(RelayPin,LOW); 56 | 57 | } 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /test/test_math/test_low_pass_filter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "maths.h" 3 | #include "../test_utils.h" 4 | 5 | static void test_U16_min(void) { 6 | // Passing zero for the filter value should return the input value 7 | TEST_ASSERT_EQUAL_UINT16(0, LOW_PASS_FILTER(0U, 0U, 0U)); 8 | TEST_ASSERT_EQUAL_UINT16(1234U, LOW_PASS_FILTER(1234U, 0U, 0U)); 9 | TEST_ASSERT_EQUAL_UINT16(1234U, LOW_PASS_FILTER(1234U, 0U, 9999U)); 10 | } 11 | 12 | static void test_U16_max(void) { 13 | // Passing UINT8_MAX for the filter value should make the input close to the previous value 14 | TEST_ASSERT_EQUAL_UINT16(0, LOW_PASS_FILTER(0U, UINT8_MAX, 0U)); 15 | TEST_ASSERT_EQUAL_UINT16(4U, LOW_PASS_FILTER(1234U, UINT8_MAX, 0U)); 16 | TEST_ASSERT_EQUAL_UINT16(9964U, LOW_PASS_FILTER(1234U, UINT8_MAX, 9999U)); 17 | } 18 | 19 | static void test_S16_min(void) { 20 | // Passing zero for the filter value should return the input value 21 | TEST_ASSERT_EQUAL_INT16(0, LOW_PASS_FILTER(0, 0U, 0)); 22 | TEST_ASSERT_EQUAL_INT16(1234, LOW_PASS_FILTER(1234, 0U, 0)); 23 | TEST_ASSERT_EQUAL_INT16(-1234, LOW_PASS_FILTER(-1234, 0U, 0)); 24 | TEST_ASSERT_EQUAL_INT16(1234, LOW_PASS_FILTER(1234, 0U, 9999)); 25 | TEST_ASSERT_EQUAL_INT16(-1234, LOW_PASS_FILTER(-1234, 0U, 9999)); 26 | } 27 | 28 | static void test_S16_max(void) { 29 | // Passing UINT8_MAX for the filter value should make the input close to the previous value 30 | TEST_ASSERT_EQUAL_INT16(0, LOW_PASS_FILTER(0, UINT8_MAX, 0)); 31 | TEST_ASSERT_EQUAL_INT16(4, LOW_PASS_FILTER(1234, UINT8_MAX, 0)); 32 | TEST_ASSERT_EQUAL_INT16(-4, LOW_PASS_FILTER(-1234, UINT8_MAX, 0)); 33 | TEST_ASSERT_EQUAL_INT16(9964, LOW_PASS_FILTER(1234, UINT8_MAX, 9999)); 34 | TEST_ASSERT_EQUAL_INT16(-9964, LOW_PASS_FILTER(-1234, UINT8_MAX, -9999)); 35 | } 36 | 37 | void test_LOW_PASS_FILTER(void) { 38 | SET_UNITY_FILENAME() { 39 | RUN_TEST(test_U16_min); 40 | RUN_TEST(test_U16_max); 41 | RUN_TEST(test_S16_min); 42 | RUN_TEST(test_S16_max); 43 | } 44 | } -------------------------------------------------------------------------------- /speeduino/sensors_map_structs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /// @cond 4 | #include 5 | 6 | // These are private to the MAP sampling algorithms but are broken out here 7 | // to support unit testing of the algorithm implementations 8 | 9 | struct map_last_read_t { 10 | uint16_t lastMAPValue; // kPA 11 | uint32_t currentReadingTime; // µS 12 | #if defined(__UINT24_MAX__) 13 | // Maximum time between readings is ~3s (at min RPM). 24 bits is enough for that 14 | __uint24 timeDeltaReadings; 15 | #else 16 | uint32_t timeDeltaReadings; 17 | #endif 18 | }; 19 | 20 | // A pair of ADC sensor readings 21 | struct map_adc_readings_t { 22 | uint16_t mapADC; 23 | uint16_t emapADC; 24 | }; 25 | 26 | // Working state for the cycle average sampling algorithm 27 | struct map_cycle_average_t { 28 | uint8_t cycleStartIndex; 29 | #if defined(__UINT24_MAX__) 30 | // Maximum revolution time is ~1.5s (at min RPM). At a 1KHz sampling rate & 2 revolutions, 31 | // we'll store 3000 readings each at maximum of 1023 (~3000000 max). So a 24-bit value 32 | // should be plenty. 33 | __uint24 mapAdcRunningTotal; 34 | __uint24 emapAdcRunningTotal; 35 | #else 36 | uint32_t mapAdcRunningTotal; 37 | uint32_t emapAdcRunningTotal; 38 | #endif 39 | uint16_t sampleCount; 40 | }; 41 | 42 | // Working state for the cycle minimum sampling algorithm 43 | struct map_cycle_min_t { 44 | uint8_t cycleStartIndex; 45 | uint16_t mapMinimum; 46 | }; 47 | 48 | // Working state for the event average sampling algorithm 49 | struct map_event_average_t { 50 | #if defined(__UINT24_MAX__) 51 | __uint24 mapAdcRunningTotal; 52 | #else 53 | uint32_t mapAdcRunningTotal; 54 | #endif 55 | uint16_t sampleCount; 56 | uint8_t eventStartIndex; 57 | }; 58 | 59 | // The overall MAP sampling system working state 60 | struct map_algorithm_t { 61 | map_last_read_t lastReading; 62 | map_adc_readings_t sensorReadings; 63 | 64 | union { 65 | map_cycle_average_t cycle_average; 66 | map_cycle_min_t cycle_min; 67 | map_event_average_t event_average; 68 | }; 69 | }; 70 | 71 | /// @endcond -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | The Speeduino project is always looking for new contributors so thanks for your interest in this! 4 | 5 | If you haven't already, jump onto our Discord as that is where most of the development discussion takes place: https://speeduino.com/home/community/discord 6 | 7 | Here are some important resources: 8 | 9 | * [Coding style guide](https://wiki.speeduino.com/dev/Style_code) 10 | * [Project forum](https://speeduino.com/forum) 11 | * [Discord](https://speeduino.com/home/community/discord) 12 | 13 | ## Testing 14 | 15 | All contributions should be in a working, compilable state. If your change does not compile cleanly it will be rejected immediately. 16 | 17 | ## Submitting changes 18 | 19 | Please send a [GitHub Pull Request to Speeduino](https://github.com/noisymime/speeduino/pull/new/master) with a clear list of what you've done (read more about [pull requests](http://help.github.com/pull-requests/)). 20 | 21 | Please try to make each pull request atomic, that is, there should be a single defined intent or feature for the pull request. If you are adding multiple features or fixing different bugs, please split these up and submit multiple pull requests. 22 | 23 | Always write a clear log message for your commits. One-line messages are fine for small changes, but bigger changes should look like this: 24 | 25 | $ git commit -m "A brief summary of the commit 26 | > 27 | > A paragraph describing what changed and its impact." 28 | 29 | ## Contributor Licensing 30 | 31 | Note that all contributions to the project are made under the Github Contributor Lincensing Agreement: https://github.com/noisymime/speeduino/wiki/Contributor-License-Agreement 32 | This ensures that all contributions made to the project are licensed in the same way as the existing work. Such an agreement is not intended to deprive contributors of their copyright, but to protect the project from claims by malicious contributors. 33 | 34 | ## Coding conventions 35 | 36 | The coding style guide can be found on the project wiki: https://wiki.speeduino.com/dev/Style_code 37 | -------------------------------------------------------------------------------- /speeduino/comms_CAN.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMS_CAN_H 2 | #define COMMS_CAN_H 3 | 4 | //For BMW e46/e39/e38, rover and mini other CAN instrument clusters 5 | #define CAN_BMW_ASC1 0x153 //Rx message from ACS unit that includes speed 6 | #define CAN_BMW_DME1 0x316 //Tx message that includes RPM 7 | #define CAN_BMW_DME2 0x329 //Tx message that includes CLT and TPS 8 | #define CAN_BMW_DME4 0x545 //Tx message that includes CLT and TPS 9 | #define CAN_BMW_ICL2 0x613 10 | #define CAN_BMW_ICL3 0x615 11 | 12 | //For VAG CAN instrument clusters 13 | #define CAN_VAG_RPM 0x280 14 | #define CAN_VAG_VSS 0x5A0 15 | 16 | //For Haltech IC-7 and IC-10 digital dashes 17 | #define CAN_HALTECH_DATA1 0x360 //RPM, MAP, TPS, Coolant Pressure. 50Hz 18 | #define CAN_HALTECH_DATA2 0x361 //Fuel Pressure, Oil Pressure, Load, Wastegate Pressure. 50Hz 19 | #define CAN_HALTECH_DATA3 0x362 //Advance, INJ Stage 1/2 duty cycles. 50Hz 20 | #define CAN_HALTECH_PW 0x364 //Pulsewidth 1-4. 50Hz 21 | #define CAN_HALTECH_LAMBDA 0x368 //Lambda 1-4. 20Hz 22 | #define CAN_HALTECH_TRIGGER 0x369 //Trigger Counter, sync level, sync error count. 20Hz 23 | #define CAN_HALTECH_VSS 0x370 //VSS, current gear and inlet cam angles. 20Hz 24 | #define CAN_HALTECH_DATA4 0x372 //Baro, BatteryV, Target boost. 10Hz 25 | #define CAN_HALTECH_DATA5 0x3E0 //IAT, CLT, Fuel Temp, Oil Temp. 10Hz 26 | 27 | #define CAN_BROADCAST_PROTOCOL_OFF 0 28 | #define CAN_BROADCAST_PROTOCOL_BMW 1 29 | #define CAN_BROADCAST_PROTOCOL_VAG 2 30 | #define CAN_BROADCAST_PROTOCOL_HALTECH 3 31 | 32 | 33 | #define CAN_WBO_RUSEFI 1 34 | #define CAN_WBO_AEM 2 35 | 36 | #define TS_CAN_OFFSET 0x100 37 | 38 | #if defined(NATIVE_CAN_AVAILABLE) 39 | 40 | void initCAN(); 41 | int CAN_read(); 42 | void CAN_write(); 43 | void sendCANBroadcast(uint8_t); 44 | void receiveCANwbo(); 45 | void DashMessages(uint16_t DashMessageID); 46 | void can_Command(void); 47 | void obd_response(uint8_t therequestedPID , uint8_t therequestedPIDlow, uint8_t therequestedPIDhigh); 48 | void readAuxCanBus(); 49 | 50 | extern CAN_message_t outMsg; 51 | extern CAN_message_t inMsg; 52 | 53 | #endif 54 | #endif // COMMS_CAN_H 55 | -------------------------------------------------------------------------------- /.github/workflows/misra.yml: -------------------------------------------------------------------------------- 1 | name: Perform MISRA scan 2 | 3 | # Controls when the action will run. Triggers the workflow on push or pull request 4 | # events but only for the master branch 5 | on: 6 | push: 7 | branches: 8 | - master 9 | - GH_misra 10 | 11 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 12 | jobs: 13 | # This workflow contains a single job called "build" 14 | build: 15 | # The type of runner that the job will run on 16 | runs-on: ubuntu-22.04 17 | 18 | # Steps represent a sequence of tasks that will be executed as part of the job 19 | steps: 20 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 21 | - uses: actions/checkout@v4 22 | 23 | - name: Install cppcheck, dos2unix 24 | run: | 25 | sudo apt-get install cppcheck 26 | sudo apt-get install dos2unix 27 | 28 | - name: Run MISRA scan 29 | continue-on-error: true 30 | run: | 31 | cd $GITHUB_WORKSPACE/misra/ 32 | mkdir .results 33 | chmod +x check_misra.sh 34 | dos2unix check_misra.sh 35 | ./check_misra.sh -c /usr/bin -o ./.results -q 36 | echo "Scan complete" 37 | NumViolations=`cat .results/error_count.txt` 38 | echo $NumViolations 39 | echo "VIOLATIONS=$NumViolations" >> $GITHUB_ENV 40 | ls $GITHUB_WORKSPACE/misra/.results/ 41 | 42 | - name: Save output to Gist 43 | if: github.event_name != 'pull_request' && github.repository_owner == 'speeduino' 44 | env: 45 | MISRA_GIST: ${{ secrets.MISRA_GIST }} 46 | VIOLATIONS: ${{ env.VIOLATIONS }} 47 | uses: schneegans/dynamic-badges-action@v1.7.0 48 | with: 49 | auth: ${{ secrets.MISRA_GIST }} 50 | gistID: d8a449a3f6d3307dab457431512502f9 51 | filename: misra_results.json 52 | label: MISRA Violations 53 | color: red 54 | message: ${{ env.VIOLATIONS }} 55 | 56 | - name: Archive MISRA scan artifacts 57 | uses: actions/upload-artifact@v4 58 | with: 59 | name: MISRA results 60 | path: | 61 | misra/.results/results.txt 62 | misra/.results/error_count.txt 63 | -------------------------------------------------------------------------------- /speeduino/src/FlashStorage/FlashAsEEPROM.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | EEPROM like API that uses Arduino Zero's flash memory. 3 | Written by A. Christian 4 | 5 | Copyright (c) 2015-2016 Arduino LLC. All right reserved. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 15 | See the GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | #if defined(CORE_SAM) && !defined(USE_SPI_EEPROM) 22 | #include "FlashAsEEPROM.h" 23 | 24 | FlashStorage(eeprom_storage, EEPROM_EMULATION); 25 | 26 | EEPROMClass::EEPROMClass(void) : _initialized(false), _dirty(false) { 27 | // Empty 28 | } 29 | 30 | uint8_t EEPROMClass::read(int address) 31 | { 32 | if (!_initialized) init(); 33 | return _eeprom.data[address]; 34 | } 35 | 36 | void EEPROMClass::update(int address, uint8_t value) 37 | { 38 | if (!_initialized) init(); 39 | if (_eeprom.data[address] != value) { 40 | _dirty = true; 41 | _eeprom.data[address] = value; 42 | } 43 | } 44 | 45 | void EEPROMClass::write(int address, uint8_t value) 46 | { 47 | update(address, value); 48 | } 49 | 50 | void EEPROMClass::init() 51 | { 52 | _eeprom = eeprom_storage.read(); 53 | if (!_eeprom.valid) { 54 | memset(_eeprom.data, 0xFF, EEPROM_EMULATION_SIZE); 55 | } 56 | _initialized = true; 57 | } 58 | 59 | bool EEPROMClass::isValid() 60 | { 61 | if (!_initialized) init(); 62 | return _eeprom.valid; 63 | } 64 | 65 | void EEPROMClass::commit() 66 | { 67 | if (!_initialized) init(); 68 | if (_dirty) { 69 | _eeprom.valid = true; 70 | eeprom_storage.write(_eeprom); 71 | } 72 | } 73 | 74 | EEPROMClass EEPROM; 75 | 76 | #endif -------------------------------------------------------------------------------- /speeduino/timers.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | /* 4 | NOTE - This file and it's associated functions need a CLEARER NAME 5 | 6 | //Purpose 7 | We're implementing a lower frequency interrupt loop to perform calculations that are needed less often, some of which depend on time having passed (delta/time) to be meaningful. 8 | 9 | 10 | //Technical 11 | Timer2 is only 8bit so we are setting the prescaler to 128 to get the most out of it. This means that the counter increments every 0.008ms and the overflow at 256 will be after 2.048ms 12 | Max Period = (Prescale)*(1/Frequency)*(2^8) 13 | (See arduinomega.blogspot.com.au/2011/05/timer2-and-overflow-interrupt-lets-get.html) 14 | 15 | We're after a 1ms interval so we'll need 131 intervals to reach this ( 1ms / 0.008ms per tick = 125). 16 | Hence we will preload the timer with 131 cycles to leave 125 until overflow (1ms). 17 | 18 | */ 19 | #ifndef TIMERS_H 20 | #define TIMERS_H 21 | 22 | #include "globals.h" 23 | 24 | #define SET_COMPARE(compare, value) compare = (COMPARE_TYPE)(value) // It is important that we cast this to the actual overflow limit of the timer. The compare variables type can be bigger than the timer overflow. 25 | 26 | #if(defined(CORE_TEENSY) || defined(CORE_STM32)) 27 | #define TACHO_PULSE_LOW() (digitalWrite(pinTachOut, LOW)) 28 | #define TACHO_PULSE_HIGH() (digitalWrite(pinTachOut, HIGH)) 29 | #else 30 | #define TACHO_PULSE_HIGH() (*tach_pin_port |= (tach_pin_mask)) 31 | #define TACHO_PULSE_LOW() (*tach_pin_port &= ~(tach_pin_mask)) 32 | #endif 33 | enum TachoOutputStatus {TACHO_INACTIVE, READY, ACTIVE}; //The 3 statuses that the tacho output pulse can have. NOTE: Cannot just use 'INACTIVE' as this is already defined within the Teensy Libs 34 | 35 | extern volatile TachoOutputStatus tachoOutputFlag; 36 | extern volatile bool tachoSweepEnabled; 37 | extern volatile uint16_t tachoSweepIncr; 38 | 39 | #define TACHO_SWEEP_TIME_MS 1500 40 | #define TACHO_SWEEP_RAMP_MS (TACHO_SWEEP_TIME_MS * 2 / 3) 41 | #define MS_PER_SEC 1000 42 | 43 | extern volatile unsigned int dwellLimit_uS; 44 | 45 | #if defined (CORE_TEENSY) 46 | extern IntervalTimer lowResTimer; 47 | void oneMSInterval(void); 48 | #elif defined (ARDUINO_ARCH_STM32) 49 | void oneMSInterval(void); 50 | #endif 51 | void initialiseTimers(void); 52 | 53 | #endif // TIMERS_H 54 | -------------------------------------------------------------------------------- /speeduino/sensors.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSORS_H 2 | #define SENSORS_H 3 | 4 | #include "globals.h" 5 | 6 | // The following are alpha values for the ADC filters. 7 | // Their values are from 0 to 240, with 0 being no filtering and 240 being maximum 8 | #define ADCFILTER_TPS_DEFAULT 50U 9 | #define ADCFILTER_CLT_DEFAULT 180U 10 | #define ADCFILTER_IAT_DEFAULT 180U 11 | #define ADCFILTER_O2_DEFAULT 128U 12 | #define ADCFILTER_BAT_DEFAULT 128U 13 | #define ADCFILTER_MAP_DEFAULT 20U //This is only used on Instantaneous MAP readings and is intentionally very weak to allow for faster response 14 | #define ADCFILTER_BARO_DEFAULT 64U 15 | 16 | #define ADCFILTER_PSI_DEFAULT 150U //not currently configurable at runtime, used for misc pressure sensors, oil, fuel, etc. 17 | 18 | #define FILTER_FLEX_DEFAULT 75U 19 | 20 | #define VSS_GEAR_HYSTERESIS 10U 21 | #define VSS_SAMPLES 4U //Must be a power of 2 and smaller than 255 22 | 23 | #define TPS_READ_FREQUENCY 30 //ONLY VALID VALUES ARE 15 or 30!!! 24 | 25 | extern volatile byte flexCounter; 26 | extern volatile uint32_t flexPulseWidth; 27 | 28 | #if defined(CORE_AVR) 29 | #define READ_FLEX() ((*flex_pin_port & flex_pin_mask) ? true : false) 30 | #else 31 | #define READ_FLEX() digitalRead(pinFlex) 32 | #endif 33 | 34 | extern bool auxIsEnabled; 35 | 36 | void initialiseADC(void); 37 | void readTPS(bool useFilter=true); //Allows the option to override the use of the filter 38 | void readO2_2(void); 39 | void flexPulse(void); 40 | void knockPulse(void); 41 | uint32_t vssGetPulseGap(byte toothHistoryIndex); 42 | void vssPulse(void); 43 | uint16_t getSpeed(void); 44 | byte getGear(void); 45 | byte getFuelPressure(void); 46 | byte getOilPressure(void); 47 | uint16_t readAuxanalog(uint8_t analogPin); 48 | uint16_t readAuxdigital(uint8_t digitalPin); 49 | void readCLT(bool useFilter=true); //Allows the option to override the use of the filter 50 | void readIAT(void); 51 | void readO2(void); 52 | void readBat(void); 53 | void readBaro(void); 54 | 55 | /** @brief Initialize the MAP calculation & Baro values */ 56 | void initialiseMAPBaro(void); 57 | void resetMAPcycleAndEvent(void); 58 | 59 | void readMAP(void); 60 | 61 | uint8_t getAnalogKnock(void); 62 | 63 | /** @brief Get the MAP change between the last 2 readings */ 64 | int16_t getMAPDelta(void); 65 | 66 | /** @brief Get the time in µS between the last 2 MAP readings */ 67 | uint32_t getMAPDeltaTime(void); 68 | 69 | #endif // SENSORS_H 70 | -------------------------------------------------------------------------------- /speeduino/src/FastCRC/FastCRC_cpu.h: -------------------------------------------------------------------------------- 1 | /* FastCRC library code is placed under the MIT license 2 | * Copyright (c) 2014,2015,2016 Frank Bösing 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | 26 | // CPU-specific implementations of helper functions 27 | 28 | #if !defined(KINETISK) 29 | #if !defined(FastCRC_cpu) 30 | #define FastCRC_cpu 31 | 32 | //Reverse byte order (16 bit) 33 | #if defined(__thumb__) 34 | static inline __attribute__((always_inline)) 35 | uint32_t REV16( uint32_t value) //ARM-THUMB 36 | { 37 | asm ("rev16 %0, %1" : "=r" (value) : "r" (value) ); 38 | return(value); 39 | } 40 | #else 41 | static inline __attribute__((always_inline)) 42 | uint32_t REV16( uint32_t value) //generic 43 | { 44 | return (value >> 8) | ((value & 0xff) << 8); 45 | } 46 | #endif 47 | 48 | 49 | 50 | 51 | 52 | //Reverse byte order (32 bit) 53 | #if defined(__thumb__) 54 | static inline __attribute__((always_inline)) 55 | uint32_t REV32( uint32_t value) //ARM-THUMB 56 | { 57 | asm ("rev %0, %1" : "=r" (value) : "r" (value) ); 58 | return(value); 59 | } 60 | #else 61 | static inline __attribute__((always_inline)) 62 | uint32_t REV32( uint32_t value) //generic 63 | { 64 | value = (value >> 16) | ((value & 0xffff) << 16); 65 | return ((value >> 8) & 0xff00ff) | ((value & 0xff00ff) << 8); 66 | } 67 | #endif 68 | 69 | 70 | #endif 71 | #endif 72 | -------------------------------------------------------------------------------- /speeduino/table3d.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "table3d.h" 3 | 4 | // =============================== Iterators ========================= 5 | 6 | table_value_iterator rows_begin(void *pTable, table_type_t key) 7 | { 8 | #define CTA_GET_ROW_ITERATOR(size, xDomain, yDomain, pTable) \ 9 | return ((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain)*)pTable)->values.begin(); 10 | #define CTA_GET_ROW_ITERATOR_DEFAULT ({ return table_value_iterator(NULL, 0U); }) 11 | CONCRETE_TABLE_ACTION(key, CTA_GET_ROW_ITERATOR, CTA_GET_ROW_ITERATOR_DEFAULT, pTable); 12 | } 13 | 14 | 15 | /** 16 | * Convert page iterator to table x axis iterator. 17 | */ 18 | table_axis_iterator x_begin(void *pTable, table_type_t key) 19 | { 20 | #define CTA_GET_X_ITERATOR(size, xDomain, yDomain, pTable) \ 21 | return ((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain)*)pTable)->axisX.begin(); 22 | #define CTA_GET_X_ITERATOR_DEFAULT ({ return table_axis_iterator(NULL, NULL, axis_domain_Tps); }) 23 | CONCRETE_TABLE_ACTION(key, CTA_GET_X_ITERATOR, CTA_GET_X_ITERATOR_DEFAULT, pTable); 24 | } 25 | 26 | table_axis_iterator x_rbegin(void *pTable, table_type_t key) 27 | { 28 | #define CTA_GET_X_RITERATOR(size, xDomain, yDomain, pTable) \ 29 | return ((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain)*)pTable)->axisX.rbegin(); 30 | #define CTA_GET_X_ITERATOR_DEFAULT ({ return table_axis_iterator(NULL, NULL, axis_domain_Tps); }) 31 | CONCRETE_TABLE_ACTION(key, CTA_GET_X_RITERATOR, CTA_GET_X_ITERATOR_DEFAULT, pTable); 32 | } 33 | 34 | /** 35 | * Convert page iterator to table y axis iterator. 36 | */ 37 | table_axis_iterator y_begin(void *pTable, table_type_t key) 38 | { 39 | #define CTA_GET_Y_ITERATOR(size, xDomain, yDomain, pTable) \ 40 | return ((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain)*)pTable)->axisY.begin(); 41 | #define CTA_GET_Y_ITERATOR_DEFAULT ({ return table_axis_iterator(NULL, NULL, axis_domain_Tps); }) 42 | CONCRETE_TABLE_ACTION(key, CTA_GET_Y_ITERATOR, CTA_GET_Y_ITERATOR_DEFAULT, pTable); 43 | } 44 | 45 | table_axis_iterator y_rbegin(void *pTable, table_type_t key) 46 | { 47 | #define CTA_GET_Y_RITERATOR(size, xDomain, yDomain, pTable) \ 48 | return ((TABLE3D_TYPENAME_BASE(size, xDomain, yDomain)*)pTable)->axisY.rbegin(); 49 | #define CTA_GET_Y_ITERATOR_DEFAULT ({ return table_axis_iterator(NULL, NULL, axis_domain_Tps); }) 50 | CONCRETE_TABLE_ACTION(key, CTA_GET_Y_RITERATOR, CTA_GET_Y_ITERATOR_DEFAULT, pTable); 51 | } -------------------------------------------------------------------------------- /test/test_decoders/SuzukiK6A/test_getCrankAngle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../../test_utils.h" 3 | #include "decoders.h" 4 | #include "init.h" 5 | 6 | static void test_k6a_getCrankAngle_tooth(uint8_t toothNum, uint16_t expectedCrankAngle, uint16_t expectedToothAngle) { 7 | triggerSetup_SuzukiK6A(); 8 | configPage4.triggerAngle = 0U; 9 | 10 | extern volatile unsigned long toothLastToothTime; 11 | toothLastToothTime = micros() - 150U; 12 | toothCurrentCount = toothNum; 13 | // Allow some variance since the algorithm relies on calling micros(); 14 | TEST_ASSERT_INT16_WITHIN_MESSAGE(2, expectedCrankAngle, getCrankAngle_SuzukiK6A(), "Crank Angle"); 15 | TEST_ASSERT_EQUAL_MESSAGE(expectedToothAngle, triggerToothAngle, "Tooth Angle"); 16 | } 17 | 18 | static void test_k6a_getCrankAngle_tooth0(void) { 19 | // Zero isn't a valid tooth, but just in case.... 20 | test_k6a_getCrankAngle_tooth(0, 650, 90); 21 | } 22 | 23 | static void test_k6a_getCrankAngle_tooth1(void) { 24 | test_k6a_getCrankAngle_tooth(1, 0, 70); 25 | } 26 | 27 | static void test_k6a_getCrankAngle_tooth2(void) { 28 | test_k6a_getCrankAngle_tooth(2, 170, 170); 29 | } 30 | 31 | static void test_k6a_getCrankAngle_tooth3(void) { 32 | test_k6a_getCrankAngle_tooth(3, 240, 70); 33 | } 34 | 35 | static void test_k6a_getCrankAngle_tooth4(void) { 36 | test_k6a_getCrankAngle_tooth(4, 410, 170); 37 | } 38 | 39 | 40 | static void test_k6a_getCrankAngle_tooth5(void) { 41 | test_k6a_getCrankAngle_tooth(5, 480, 70); 42 | } 43 | 44 | static void test_k6a_getCrankAngle_tooth6(void) { 45 | test_k6a_getCrankAngle_tooth(6, 515, 35); 46 | } 47 | 48 | static void test_k6a_getCrankAngle_tooth7(void) { 49 | test_k6a_getCrankAngle_tooth(7, 650, 135); 50 | } 51 | 52 | static void test_k6a_getCrankAngle_tooth8(void) { 53 | // 8 isn't a valid tooth, but just in case.... 54 | test_k6a_getCrankAngle_tooth(8, 0, 70); 55 | } 56 | 57 | void testSuzukiK6A_getCrankAngle() 58 | { 59 | SET_UNITY_FILENAME() { 60 | 61 | RUN_TEST(test_k6a_getCrankAngle_tooth0); 62 | RUN_TEST(test_k6a_getCrankAngle_tooth1); 63 | RUN_TEST(test_k6a_getCrankAngle_tooth2); 64 | RUN_TEST(test_k6a_getCrankAngle_tooth3); 65 | RUN_TEST(test_k6a_getCrankAngle_tooth4); 66 | RUN_TEST(test_k6a_getCrankAngle_tooth5); 67 | RUN_TEST(test_k6a_getCrankAngle_tooth6); 68 | RUN_TEST(test_k6a_getCrankAngle_tooth7); 69 | RUN_TEST(test_k6a_getCrankAngle_tooth8); 70 | 71 | } 72 | } -------------------------------------------------------------------------------- /speeduino/corrections.h: -------------------------------------------------------------------------------- 1 | /* 2 | All functions in the gamma file return 3 | 4 | */ 5 | #ifndef CORRECTIONS_H 6 | #define CORRECTIONS_H 7 | 8 | #define IGN_IDLE_THRESHOLD 200 //RPM threshold (below CL idle target) for when ign based idle control will engage 9 | 10 | void initialiseCorrections(void); 11 | uint16_t correctionsFuel(void); 12 | uint8_t calculateAfrTarget(table3d16RpmLoad &afrLookUpTable, const statuses ¤t, const config2 &page2, const config6 &page6); 13 | byte correctionWUE(void); //Warmup enrichment 14 | uint16_t correctionCranking(void); //Cranking enrichment 15 | byte correctionASE(void); //After Start Enrichment 16 | uint16_t correctionAccel(void); //Acceleration Enrichment 17 | byte correctionFloodClear(void); //Check for flood clear on cranking 18 | byte correctionAFRClosedLoop(void); //Closed loop AFR adjustment 19 | byte correctionFlex(void); //Flex fuel adjustment 20 | byte correctionFuelTemp(void); //Fuel temp correction 21 | byte correctionBatVoltage(void); //Battery voltage correction 22 | byte correctionIATDensity(void); //Inlet temp density correction 23 | byte correctionBaro(void); //Barometric pressure correction 24 | byte correctionLaunch(void); //Launch control correction 25 | byte correctionDFCOfuel(void); //DFCO taper correction 26 | bool correctionDFCO(void); //Decelleration fuel cutoff 27 | 28 | int8_t correctionsIgn(int8_t advance); 29 | int8_t correctionFixedTiming(int8_t advance); 30 | int8_t correctionCrankingFixedTiming(int8_t advance); 31 | int8_t correctionFlexTiming(int8_t advance); 32 | int8_t correctionWMITiming(int8_t advance); 33 | int8_t correctionKnockTiming(int8_t advance); 34 | int8_t correctionIATretard(int8_t advance); 35 | int8_t correctionCLTadvance(int8_t advance); 36 | int8_t correctionIdleAdvance(int8_t advance); 37 | int8_t correctionSoftRevLimit(int8_t advance); 38 | int8_t correctionNitrous(int8_t advance); 39 | int8_t correctionSoftLaunch(int8_t advance); 40 | int8_t correctionSoftFlatShift(int8_t advance); 41 | int8_t correctionKnock(int8_t advance); 42 | int8_t correctionDFCOignition(int8_t advance); 43 | 44 | uint16_t correctionsDwell(uint16_t dwell); 45 | 46 | extern byte activateMAPDOT; //The mapDOT value seen when the MAE was activated. 47 | extern byte activateTPSDOT; //The tpsDOT value seen when the MAE was activated. 48 | 49 | extern uint16_t AFRnextCycle; 50 | extern uint8_t aseTaper; 51 | extern uint8_t dfcoDelay; 52 | extern uint8_t idleAdvTaper; 53 | extern uint8_t crankingEnrichTaper; 54 | extern uint8_t dfcoTaper; 55 | 56 | #endif // CORRECTIONS_H 57 | -------------------------------------------------------------------------------- /speeduino/TS_CommandButtonHandler.h: -------------------------------------------------------------------------------- 1 | 2 | /** \file 3 | * Header file for the TunerStudio command handler 4 | * The command handler manages all the inputs FROM TS which are issued when a command button is clicked by the user 5 | */ 6 | 7 | #define TS_CMD_TEST_DSBL 256 8 | #define TS_CMD_TEST_ENBL 257 9 | 10 | #define TS_CMD_INJ1_ON 513 11 | #define TS_CMD_INJ1_OFF 514 12 | #define TS_CMD_INJ1_PULSED 515 13 | #define TS_CMD_INJ2_ON 516 14 | #define TS_CMD_INJ2_OFF 517 15 | #define TS_CMD_INJ2_PULSED 518 16 | #define TS_CMD_INJ3_ON 519 17 | #define TS_CMD_INJ3_OFF 520 18 | #define TS_CMD_INJ3_PULSED 521 19 | #define TS_CMD_INJ4_ON 522 20 | #define TS_CMD_INJ4_OFF 523 21 | #define TS_CMD_INJ4_PULSED 524 22 | #define TS_CMD_INJ5_ON 525 23 | #define TS_CMD_INJ5_OFF 526 24 | #define TS_CMD_INJ5_PULSED 527 25 | #define TS_CMD_INJ6_ON 528 26 | #define TS_CMD_INJ6_OFF 529 27 | #define TS_CMD_INJ6_PULSED 530 28 | #define TS_CMD_INJ7_ON 531 29 | #define TS_CMD_INJ7_OFF 532 30 | #define TS_CMD_INJ7_PULSED 533 31 | #define TS_CMD_INJ8_ON 534 32 | #define TS_CMD_INJ8_OFF 535 33 | #define TS_CMD_INJ8_PULSED 536 34 | 35 | #define TS_CMD_IGN1_ON 769 36 | #define TS_CMD_IGN1_OFF 770 37 | #define TS_CMD_IGN1_PULSED 771 38 | #define TS_CMD_IGN2_ON 772 39 | #define TS_CMD_IGN2_OFF 773 40 | #define TS_CMD_IGN2_PULSED 774 41 | #define TS_CMD_IGN3_ON 775 42 | #define TS_CMD_IGN3_OFF 776 43 | #define TS_CMD_IGN3_PULSED 777 44 | #define TS_CMD_IGN4_ON 778 45 | #define TS_CMD_IGN4_OFF 779 46 | #define TS_CMD_IGN4_PULSED 780 47 | #define TS_CMD_IGN5_ON 781 48 | #define TS_CMD_IGN5_OFF 782 49 | #define TS_CMD_IGN5_PULSED 783 50 | #define TS_CMD_IGN6_ON 784 51 | #define TS_CMD_IGN6_OFF 785 52 | #define TS_CMD_IGN6_PULSED 786 53 | #define TS_CMD_IGN7_ON 787 54 | #define TS_CMD_IGN7_OFF 788 55 | #define TS_CMD_IGN7_PULSED 789 56 | #define TS_CMD_IGN8_ON 790 57 | #define TS_CMD_IGN8_OFF 791 58 | #define TS_CMD_IGN8_PULSED 792 59 | 60 | #define TS_CMD_STM32_REBOOT 12800 61 | #define TS_CMD_STM32_BOOTLOADER 12801 62 | 63 | #define TS_CMD_SD_FORMAT 13057 64 | 65 | #define TS_CMD_VSS_60KMH 39168 //0x99x00 66 | #define TS_CMD_VSS_RATIO1 39169 67 | #define TS_CMD_VSS_RATIO2 39170 68 | #define TS_CMD_VSS_RATIO3 39171 69 | #define TS_CMD_VSS_RATIO4 39172 70 | #define TS_CMD_VSS_RATIO5 39173 71 | #define TS_CMD_VSS_RATIO6 39174 72 | 73 | /* the maximum id number is 65,535 */ 74 | bool TS_CommandButtonsHandler(uint16_t buttonCommand); 75 | -------------------------------------------------------------------------------- /speeduino/schedule_calcs.cpp: -------------------------------------------------------------------------------- 1 | #include "schedule_calcs.h" 2 | 3 | int ignition1StartAngle; 4 | int ignition1EndAngle; 5 | int channel1IgnDegrees; /**< The number of crank degrees until cylinder 1 is at TDC (This is obviously 0 for virtually ALL engines, but there's some weird ones) */ 6 | 7 | int ignition2StartAngle; 8 | int ignition2EndAngle; 9 | int channel2IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */ 10 | 11 | int ignition3StartAngle; 12 | int ignition3EndAngle; 13 | int channel3IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */ 14 | 15 | int ignition4StartAngle; 16 | int ignition4EndAngle; 17 | int channel4IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */ 18 | 19 | #if (IGN_CHANNELS >= 5) 20 | int ignition5StartAngle; 21 | int ignition5EndAngle; 22 | int channel5IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */ 23 | #endif 24 | #if (IGN_CHANNELS >= 6) 25 | int ignition6StartAngle; 26 | int ignition6EndAngle; 27 | int channel6IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */ 28 | #endif 29 | #if (IGN_CHANNELS >= 7) 30 | int ignition7StartAngle; 31 | int ignition7EndAngle; 32 | int channel7IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */ 33 | #endif 34 | #if (IGN_CHANNELS >= 8) 35 | int ignition8StartAngle; 36 | int ignition8EndAngle; 37 | int channel8IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */ 38 | #endif 39 | 40 | int channel1InjDegrees; /**< The number of crank degrees until cylinder 1 is at TDC (This is obviously 0 for virtually ALL engines, but there's some weird ones) */ 41 | int channel2InjDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */ 42 | int channel3InjDegrees; /**< The number of crank degrees until cylinder 3 (and 5/6/7/8) is at TDC */ 43 | int channel4InjDegrees; /**< The number of crank degrees until cylinder 4 (and 5/6/7/8) is at TDC */ 44 | #if (INJ_CHANNELS >= 5) 45 | int channel5InjDegrees; /**< The number of crank degrees until cylinder 5 is at TDC */ 46 | #endif 47 | #if (INJ_CHANNELS >= 6) 48 | int channel6InjDegrees; /**< The number of crank degrees until cylinder 6 is at TDC */ 49 | #endif 50 | #if (INJ_CHANNELS >= 7) 51 | int channel7InjDegrees; /**< The number of crank degrees until cylinder 7 is at TDC */ 52 | #endif 53 | #if (INJ_CHANNELS >= 8) 54 | int channel8InjDegrees; /**< The number of crank degrees until cylinder 8 is at TDC */ 55 | #endif 56 | 57 | 58 | -------------------------------------------------------------------------------- /speeduino/src/SPIAsEEPROM/DMASAM.h: -------------------------------------------------------------------------------- 1 | /* Arduino SPIMemory Library v.3.2.0 2 | * Copyright (C) 2017 by Prajwal Bhattaram 3 | * Created by Prajwal Bhattaram - 19/04/2018 4 | * Modified by Prajwal Bhattaram - 20/04/2018 5 | * 6 | * This file is part of the Arduino SPIMemory Library. This library is for 7 | * Winbond NOR flash memory modules. In its current form it enables reading 8 | * and writing individual data variables, structs and arrays from and to various locations; 9 | * reading and writing pages; continuous read functions; sector, block and chip erase; 10 | * suspending and resuming programming/erase and powering down for low power operation. 11 | * 12 | * This Library is free software: you can redistribute it and/or modify 13 | * it under the terms of the GNU General Public License as published by 14 | * the Free Software Foundation, either version 3 of the License, or 15 | * (at your option) any later version. 16 | * 17 | * This Library is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | * GNU General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU General Public License v3.0 23 | * along with the Arduino SPIMemory Library. If not, see 24 | * . 25 | */ 26 | 27 | #ifndef SAM_DMASPI_H 28 | #define SAM_DMASPI_H 29 | #include "SPIMemory.h" 30 | class DMASAM { 31 | public: 32 | DMASAM(void){}; 33 | ~DMASAM(void){}; 34 | void SPIDmaRX(uint8_t* dst, uint16_t count); 35 | void SPIDmaRX(char* dst, uint16_t count); 36 | void SPIDmaTX(const uint8_t* src, uint16_t count); 37 | void SPIDmaCharTX(const char* src, uint16_t count); 38 | void SPIBegin(void); 39 | void SPIInit(uint8_t dueSckDivisor); 40 | uint8_t SPITransfer(uint8_t b); 41 | uint8_t SPIRecByte(void); 42 | uint8_t SPIRecByte(uint8_t* buf, size_t len); 43 | int8_t SPIRecChar(void); 44 | int8_t SPIRecChar(char* buf, size_t len); 45 | void SPISendByte(uint8_t b); 46 | void SPISendByte(const uint8_t* buf, size_t len); 47 | void SPISendChar(char b); 48 | void SPISendChar(const char* buf, size_t len); 49 | private: 50 | void _dmac_disable(void); 51 | void _dmac_enable(void); 52 | void _dmac_channel_disable(uint32_t ul_num); 53 | void _dmac_channel_enable(uint32_t ul_num); 54 | bool _dmac_channel_transfer_done(uint32_t ul_num); 55 | }; 56 | 57 | extern DMASAM due; ///< default DMASAM instance 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /speeduino/crankMaths.h: -------------------------------------------------------------------------------- 1 | #ifndef CRANKMATHS_H 2 | #define CRANKMATHS_H 3 | 4 | #include "maths.h" 5 | #include "globals.h" 6 | 7 | /** 8 | * @brief Makes one pass at nudging the angle to within [0,CRANK_ANGLE_MAX_IGN] 9 | * 10 | * @param angle A crank angle in degrees 11 | * @return int16_t 12 | */ 13 | static inline int16_t ignitionLimits(int16_t angle) 14 | { 15 | return nudge(0, CRANK_ANGLE_MAX_IGN, angle, CRANK_ANGLE_MAX_IGN); 16 | } 17 | 18 | /** 19 | * @brief Makes one pass at nudging the angle to within [0,CRANK_ANGLE_MAX_INJ] 20 | * 21 | * @param angle A crank angle in degrees 22 | * @return int16_t 23 | */ 24 | static inline int16_t injectorLimits(int16_t angle) 25 | { 26 | int16_t tempAngle = angle; 27 | if(tempAngle < 0) { tempAngle = tempAngle + CRANK_ANGLE_MAX_INJ; } 28 | while(tempAngle > CRANK_ANGLE_MAX_INJ ) { tempAngle -= CRANK_ANGLE_MAX_INJ; } 29 | return tempAngle; 30 | } 31 | 32 | /** @brief At 1 RPM, each degree of angular rotation takes this many microseconds */ 33 | #define MICROS_PER_DEG_1_RPM INT32_C(166667) 34 | 35 | /** @brief The maximum rpm that the ECU will attempt to run at. 36 | * 37 | * It is NOT related to the rev limiter, but is instead dictates how fast certain operations will be 38 | * allowed to run. Lower number gives better performance 39 | **/ 40 | #define MAX_RPM INT16_C(18000) 41 | 42 | /** @brief Absolute minimum RPM that the crank math (& therefore all of Speeduino) can be used with 43 | * 44 | * This is dictated by the use of uint16_t as the base type for storing 45 | * angle<->time conversion factor (degreesPerMicro) 46 | */ 47 | #define MIN_RPM ((MICROS_PER_DEG_1_RPM/(UINT16_MAX/16UL))+1UL) 48 | 49 | /** 50 | * @brief Set the revolution time, from which some of the degree<-->angle conversions are derived 51 | * 52 | * @param revolutionTime The crank revolution time. 53 | */ 54 | void setAngleConverterRevolutionTime(uint32_t revolutionTime); 55 | 56 | /** 57 | * @name Converts angular degrees to the time interval that amount of rotation 58 | * will take at current RPM. 59 | * 60 | * Based on angle of [0,720] and min/max RPM, result ranges from 61 | * 9 (MAX_RPM, 1 deg) to 2926828 (MIN_RPM, 720 deg) 62 | * 63 | * @param angle Angle in degrees 64 | * @return Time interval in uS 65 | */ 66 | uint32_t angleToTimeMicroSecPerDegree(uint16_t angle); 67 | 68 | /** 69 | * @name Converts a time interval in microsecods to the equivalent degrees of angular (crank) 70 | * rotation at current RPM. 71 | * 72 | * Inverse of angleToTimeMicroSecPerDegree 73 | * 74 | * @param time Time interval in uS 75 | * @return Angle in degrees 76 | */ 77 | uint16_t timeToAngleDegPerMicroSec(uint32_t time); 78 | 79 | #endif -------------------------------------------------------------------------------- /speeduino/acc_mc33810.cpp: -------------------------------------------------------------------------------- 1 | #include "acc_mc33810.h" 2 | #include "globals.h" 3 | #include 4 | 5 | uint8_t MC33810_BIT_INJ1 = 1; 6 | uint8_t MC33810_BIT_INJ2 = 2; 7 | uint8_t MC33810_BIT_INJ3 = 3; 8 | uint8_t MC33810_BIT_INJ4 = 4; 9 | uint8_t MC33810_BIT_INJ5 = 5; 10 | uint8_t MC33810_BIT_INJ6 = 6; 11 | uint8_t MC33810_BIT_INJ7 = 7; 12 | uint8_t MC33810_BIT_INJ8 = 8; 13 | 14 | uint8_t MC33810_BIT_IGN1 = 1; 15 | uint8_t MC33810_BIT_IGN2 = 2; 16 | uint8_t MC33810_BIT_IGN3 = 3; 17 | uint8_t MC33810_BIT_IGN4 = 4; 18 | uint8_t MC33810_BIT_IGN5 = 5; 19 | uint8_t MC33810_BIT_IGN6 = 6; 20 | uint8_t MC33810_BIT_IGN7 = 7; 21 | uint8_t MC33810_BIT_IGN8 = 8; 22 | 23 | volatile PORT_TYPE *mc33810_1_pin_port; 24 | volatile PINMASK_TYPE mc33810_1_pin_mask; 25 | volatile PORT_TYPE *mc33810_2_pin_port; 26 | volatile PINMASK_TYPE mc33810_2_pin_mask; 27 | 28 | void initMC33810(void) 29 | { 30 | //Set pin port/masks 31 | mc33810_1_pin_port = portOutputRegister(digitalPinToPort(pinMC33810_1_CS)); 32 | mc33810_1_pin_mask = digitalPinToBitMask(pinMC33810_1_CS); 33 | mc33810_2_pin_port = portOutputRegister(digitalPinToPort(pinMC33810_2_CS)); 34 | mc33810_2_pin_mask = digitalPinToBitMask(pinMC33810_2_CS); 35 | 36 | //Set the output states of both ICs to be off to fuel and ignition 37 | mc33810_1_requestedState = 0; 38 | mc33810_2_requestedState = 0; 39 | mc33810_1_returnState = 0; 40 | mc33810_2_returnState = 0; 41 | 42 | pinMode(pinMC33810_1_CS, OUTPUT); 43 | pinMode(pinMC33810_2_CS, OUTPUT); 44 | 45 | SPI.begin(); 46 | //These are the SPI settings per the datasheet 47 | SPI.beginTransaction(SPISettings(6000000, MSBFIRST, SPI_MODE0)); 48 | 49 | //Set the ignition outputs to GPGD mode 50 | /* 51 | 0001 = Mode select command 52 | 1111 = Set all 1 GD[0...3] outputs to use GPGD mode 53 | 00000000 = All remaining values are unused (For us) 54 | */ 55 | //uint16_t cmd = 0b000111110000; 56 | uint16_t cmd = 0b0001111100000000; 57 | //IC1 58 | MC33810_1_ACTIVE(); 59 | SPI.transfer16(cmd); 60 | MC33810_1_INACTIVE(); 61 | //IC2 62 | MC33810_2_ACTIVE(); 63 | SPI.transfer16(cmd); 64 | MC33810_2_INACTIVE(); 65 | 66 | //Disable the Open Load pull-down current sync (See page 31 of MC33810 DS) 67 | /* 68 | 0010 = LSD Fault Command 69 | 1000 = LSD Fault operation is Shutdown (Default) 70 | 1111 = Open load detection fault when active (Default) 71 | 0000 = Disable open load detection when off (Changed from 1111 to 0000) 72 | */ 73 | cmd = 0b0010100011110000; 74 | //IC1 75 | MC33810_1_ACTIVE(); 76 | SPI.transfer16(cmd); 77 | MC33810_1_INACTIVE(); 78 | //IC2 79 | MC33810_2_ACTIVE(); 80 | SPI.transfer16(cmd); 81 | MC33810_2_INACTIVE(); 82 | 83 | } -------------------------------------------------------------------------------- /speeduino/src/FlashStorage/FlashStorage.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015 Arduino LLC. All right reserved. 3 | Written by Cristian Maglie 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13 | See the GNU Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | // Concatenate after macro expansion 25 | #define PPCAT_NX(A, B) A ## B 26 | #define PPCAT(A, B) PPCAT_NX(A, B) 27 | 28 | #define Flash(name, size) \ 29 | __attribute__((__aligned__(256))) \ 30 | static const uint8_t PPCAT(_data,name)[(size+255)/256*256] = { }; \ 31 | FlashClass name(PPCAT(_data,name), size); 32 | 33 | #define FlashStorage(name, T) \ 34 | __attribute__((__aligned__(256))) \ 35 | static const uint8_t PPCAT(_data,name)[(sizeof(T)+255)/256*256] = { }; \ 36 | FlashStorageClass name(PPCAT(_data,name)); 37 | 38 | class FlashClass { 39 | public: 40 | FlashClass(const void *flash_addr = NULL, uint32_t size = 0); 41 | 42 | void write(const void *data) { write(flash_address, data, flash_size); } 43 | void erase() { erase(flash_address, flash_size); } 44 | void read(void *data) { read(flash_address, data, flash_size); } 45 | 46 | void write(const volatile void *flash_ptr, const void *data, uint32_t size); 47 | void erase(const volatile void *flash_ptr, uint32_t size); 48 | void read(const volatile void *flash_ptr, void *data, uint32_t size); 49 | 50 | private: 51 | void erase(const volatile void *flash_ptr); 52 | 53 | const uint32_t PAGE_SIZE, PAGES, MAX_FLASH, ROW_SIZE; 54 | const volatile void *flash_address; 55 | const uint32_t flash_size; 56 | }; 57 | 58 | template 59 | class FlashStorageClass { 60 | public: 61 | FlashStorageClass(const void *flash_addr) : flash(flash_addr, sizeof(T)) { }; 62 | 63 | // Write data into flash memory. 64 | // Compiler is able to optimize parameter copy. 65 | inline void write(T data) { flash.erase(); flash.write(&data); } 66 | 67 | // Read data from flash into variable. 68 | inline void read(T *data) { flash.read(data); } 69 | 70 | // Overloaded version of read. 71 | // Compiler is able to optimize copy-on-return. 72 | inline T read() { T data; read(&data); return data; } 73 | 74 | private: 75 | FlashClass flash; 76 | }; 77 | 78 | -------------------------------------------------------------------------------- /speeduino/src/libdivide/constant_fast_div.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * When dividing by a known compile time constant, the division can be replaced 3 | * by a multiply+shift operation. GCC will do this automatically, 4 | * *BUT ONLY FOR DIVISION OF REGISTER-WIDTH OR NARROWER*. 5 | * 6 | * So on an 8-bit system, 16-bit divides will *NOT* be optimised. 7 | * 8 | * The templates here manually apply the multiply+shift operation for 16-bit numbers. 9 | */ 10 | 11 | #pragma once 12 | #include "libdivide.h" 13 | #include "u16_ldparams.h" 14 | #include "s16_ldparams.h" 15 | 16 | #ifdef __cplusplus 17 | namespace libdivide { 18 | 19 | // Implementation details 20 | namespace detail { 21 | 22 | // Specialized templates containing precomputed libdivide constants 23 | // Primary template for pre-generated libdivide constants 24 | template struct libdivide_constants {}; 25 | #include "u16_ldparams.hpp" 26 | #include "s16_ldparams.hpp" 27 | 28 | // Primary template - divide as normal. Performant for divisors that are a power of 2 29 | template 30 | struct fast_divide_t { 31 | static LIBDIVIDE_INLINE T divide(T n) { return n/divisor; } 32 | }; 33 | 34 | // Divide by 1 - no-op 35 | template 36 | struct fast_divide_t { 37 | static LIBDIVIDE_INLINE uint16_t divide(uint16_t n) { return n; } 38 | }; 39 | template 40 | struct fast_divide_t { 41 | static LIBDIVIDE_INLINE int16_t divide(int16_t n) { return n; } 42 | }; 43 | 44 | // Specialzed template for non-power of 2 uint16_t divisors 45 | template 46 | struct fast_divide_t { 47 | static LIBDIVIDE_INLINE uint16_t divide(uint16_t n) { 48 | return libdivide_u16_do_raw(n, libdivide_constants::libdivide.magic, 49 | libdivide_constants::libdivide.more); 50 | } 51 | }; 52 | 53 | // Specialzed template for non-power of 2 int16_t divisors 54 | template 55 | struct fast_divide_t { 56 | static LIBDIVIDE_INLINE int16_t divide(int16_t n) { 57 | return libdivide_s16_do_raw(n, libdivide_constants::libdivide.magic, 58 | libdivide_constants::libdivide.more); 59 | } 60 | }; 61 | 62 | // Power of 2 test 63 | template 64 | struct is_power_of_two { 65 | static constexpr bool val = N!=0 && (N & (N - 1))==0; 66 | }; 67 | } 68 | 69 | // Public API. 70 | template 71 | LIBDIVIDE_INLINE T fast_divide(T n) { 72 | return detail::fast_divide_t::val>::divide(n); 73 | } 74 | 75 | } 76 | #endif -------------------------------------------------------------------------------- /test/test_schedule_calcs/test_adjust_crank_angle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "schedule_calcs.h" 4 | #include "../test_utils.h" 5 | 6 | static void nullIgnCallback(void) {}; 7 | 8 | void test_adjust_crank_angle_pending_below_minrevolutions() 9 | { 10 | auto counter = decltype(+IGN4_COUNTER){0}; 11 | auto compare = decltype(+IGN4_COMPARE){0}; 12 | IgnitionSchedule schedule(counter, compare, nullIgnCallback, nullIgnCallback); 13 | 14 | schedule.Status = PENDING; 15 | currentStatus.startRevolutions = 0; 16 | 17 | schedule.compare = 101; 18 | schedule.counter = 100; 19 | 20 | // Should do nothing. 21 | adjustCrankAngle(schedule, 359, 180); 22 | 23 | TEST_ASSERT_EQUAL(101, schedule.compare); 24 | TEST_ASSERT_EQUAL(100, schedule.counter); 25 | TEST_ASSERT_FALSE(schedule.endScheduleSetByDecoder); 26 | } 27 | 28 | 29 | void test_adjust_crank_angle_pending_above_minrevolutions() 30 | { 31 | auto counter = decltype(+IGN4_COUNTER){0}; 32 | auto compare = decltype(+IGN4_COMPARE){0}; 33 | IgnitionSchedule schedule(counter, compare, nullIgnCallback, nullIgnCallback); 34 | 35 | schedule.Status = PENDING; 36 | currentStatus.startRevolutions = 2000; 37 | // timePerDegreex16 = 666; 38 | 39 | schedule.compare = 101; 40 | schedule.counter = 100; 41 | schedule.endCompare = 100; 42 | constexpr uint16_t newCrankAngle = 180; 43 | constexpr uint16_t chargeAngle = 359; 44 | 45 | adjustCrankAngle(schedule, chargeAngle, newCrankAngle); 46 | 47 | TEST_ASSERT_EQUAL(101, schedule.compare); 48 | TEST_ASSERT_EQUAL(100, schedule.counter); 49 | TEST_ASSERT_EQUAL(schedule.counter+uS_TO_TIMER_COMPARE(angleToTimeMicroSecPerDegree(chargeAngle-newCrankAngle)), schedule.endCompare); 50 | TEST_ASSERT_TRUE(schedule.endScheduleSetByDecoder); 51 | } 52 | 53 | void test_adjust_crank_angle_running() 54 | { 55 | auto counter = decltype(+IGN4_COUNTER){0}; 56 | auto compare = decltype(+IGN4_COMPARE){0}; 57 | IgnitionSchedule schedule(counter, compare, nullIgnCallback, nullIgnCallback); 58 | 59 | schedule.Status = RUNNING; 60 | currentStatus.startRevolutions = 2000; 61 | // timePerDegreex16 = 666; 62 | 63 | schedule.compare = 101; 64 | schedule.counter = 100; 65 | schedule.endCompare = 100; 66 | constexpr uint16_t newCrankAngle = 180; 67 | constexpr uint16_t chargeAngle = 359; 68 | 69 | adjustCrankAngle(schedule, chargeAngle, newCrankAngle); 70 | 71 | TEST_ASSERT_EQUAL(schedule.counter+uS_TO_TIMER_COMPARE(angleToTimeMicroSecPerDegree(chargeAngle-newCrankAngle)), schedule.compare); 72 | TEST_ASSERT_EQUAL(100, schedule.counter); 73 | TEST_ASSERT_EQUAL(100, schedule.endCompare); 74 | TEST_ASSERT_FALSE(schedule.endScheduleSetByDecoder); 75 | } 76 | 77 | void test_adjust_crank_angle() 78 | { 79 | SET_UNITY_FILENAME() { 80 | 81 | RUN_TEST(test_adjust_crank_angle_pending_below_minrevolutions); 82 | RUN_TEST(test_adjust_crank_angle_pending_above_minrevolutions); 83 | RUN_TEST(test_adjust_crank_angle_running); 84 | } 85 | } -------------------------------------------------------------------------------- /test/timer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #if defined(__AVR__) 6 | #include 7 | #else 8 | #include 9 | #endif 10 | 11 | #if !defined(MILLIS_PER_SEC) 12 | #define MILLIS_PER_SEC 1000ULL 13 | #endif 14 | #if !defined(MICROS_PER_SEC) 15 | #define MICROS_PER_SEC (MILLIS_PER_SEC*1000) 16 | #endif 17 | #if !defined(NANOS_PER_SEC) 18 | #define NANOS_PER_SEC (MICROS_PER_SEC*1000) 19 | #endif 20 | 21 | class timer { 22 | private: 23 | #if defined(__AVR__) 24 | uint32_t start_time; 25 | uint32_t end_time; 26 | #else 27 | struct timeval start_time; 28 | struct timeval end_time; 29 | #endif 30 | 31 | public: 32 | 33 | timer() { 34 | } 35 | 36 | void start() { 37 | #if defined(__AVR__) 38 | start_time = micros(); 39 | #else 40 | gettimeofday(&start_time, NULL); 41 | #endif 42 | } 43 | 44 | void stop() { 45 | #if defined(__AVR__) 46 | end_time = micros(); 47 | #else 48 | gettimeofday(&end_time, NULL); 49 | #endif 50 | } 51 | 52 | uint32_t duration_micros() { 53 | #if defined(__AVR__) 54 | return end_time-start_time; 55 | #else 56 | return (uint32_t)(((end_time.tv_sec - start_time.tv_sec) * MICROS_PER_SEC) + (end_time.tv_usec - start_time.tv_usec)); 57 | #endif 58 | } 59 | }; 60 | 61 | template 62 | void measure_executiontime(uint16_t iterations, TLoop from, TLoop to, TLoop step, timer &measure, TParam param, void (*pTestFun)(TLoop, TParam)) { 63 | measure.start(); 64 | for (uint16_t loop=0; loop 75 | struct execution_time { 76 | TParam result; 77 | uint32_t durationMicros; 78 | }; 79 | template 80 | struct comparative_execution_times { 81 | execution_time timeA; 82 | execution_time timeB; 83 | }; 84 | template 85 | comparative_execution_times compare_executiontime(uint16_t iterations, TLoop from, TLoop to, TLoop step, void (*pTestFunA)(TLoop, TParam&), void (*pTestFunB)(TLoop, TParam&)) { 86 | 87 | timer timerA; 88 | TParam paramA = 0; 89 | measure_executiontime(iterations, from, to, step, timerA, paramA, pTestFunA); 90 | 91 | timer timerB; 92 | TParam paramB = 0; 93 | measure_executiontime(iterations, from, to, step, timerB, paramB, pTestFunB); 94 | 95 | char buffer[128]; 96 | sprintf(buffer, "Timing: %" PRIu32 ", %" PRIu32, timerA.duration_micros(), timerB.duration_micros()); 97 | TEST_MESSAGE(buffer); 98 | 99 | return comparative_execution_times { 100 | .timeA = execution_time { .result = paramA, .durationMicros = timerA.duration_micros()}, 101 | .timeB = execution_time { .result = paramB, .durationMicros = timerB.duration_micros()} 102 | }; 103 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | Speeduino 4 | 5 | [![Release](https://img.shields.io/github/release/noisymime/speeduino.svg)](https://github.com/noisymime/speeduino/releases/latest) 6 | [![License](https://img.shields.io/badge/license-GPLv3-blue.svg)](https://github.com/noisymime/speeduino/blob/master/LICENSE) 7 | [![Build Status](https://img.shields.io/github/actions/workflow/status/noisymime/speeduino/build-firmware.yml?label=Build%20Status&branch=master)](https://github.com/noisymime/speeduino/actions/workflows/build-firmware.yml) 8 | [![Unit Tests](https://img.shields.io/github/actions/workflow/status/noisymime/speeduino/unit-tests.yml?label=Unit%20Tests&branch=master)](https://github.com/noisymime/speeduino/actions/workflows/unit-tests.yml) 9 | ![badge](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/noisymime/d8a449a3f6d3307dab457431512502f9/raw/misra_results.json) 10 | [![Sponsors](https://img.shields.io/github/sponsors/noisymime)](https://github.com/sponsors/noisymime) 11 | [![GitHub commits](https://img.shields.io/github/commits-since/noisymime/speeduino/202310.svg)](https://github.com/noisymime/speeduino/compare/202310...master) 12 | [![https://img.shields.io/discord/879495735912071269 ](https://img.shields.io/discord/879495735912071269?label=Discord&logo=Discord)](https://discord.gg/YWCEexaNDe) 13 | 14 | ##### A low cost, DIY friendly Engine Management System (ECU) based on the Arduino framework 15 |
16 | 17 | 18 | ## Speeduino 19 | The Speeduino project is a flexible, fully featured Engine Management Systems (EMS aka ECU) based on the low cost and open source Arduino platform. It provides the hardware, firmware and software components that make up an engine management system, all provided under open licenses. With over 1000 installations, Speeduino has matured into a product that meets the needs of the hobbyist and enthusiast community without driving prices to the levels of traditional aftermarket ECUs. 20 | 21 | ## Documentation 22 | The Speeduino online manual can be found at: https://wiki.speeduino.com 23 | 24 | ## Where to Buy 25 | [Pre-made Speeduino units/boards are available from a number of official resellers](https://speeduino.com/home/where-to-buy) 26 | 27 | These resellers all contribute a portion of sales back to the project to allow for continued development to take place and we encourage sales through them whenever possible. 28 | 29 | Of course, being open source, you are free to use the design files provided here to create your own hardware! 30 | 31 | ## Support 32 | In addition the manual referenced above, Speeduino has a large and very vibrant community of people to help out with your setup or any questions you might add. 33 | 34 | * [Discord](https://discord.gg/YWCEexaNDe) 35 | * [Speeduino Forum](https://speeduino.com/forum) 36 | * [Facebook](https://www.facebook.com/groups/191918764521976/) 37 | 38 | ## Contributors 39 | 40 | This project exists thanks to all the people who contribute, both in terms of code and testing provided. If you'd like to get involved, please have a read through [Contributing](contributing.md) and then jump on Discord to discuss things further 41 | -------------------------------------------------------------------------------- /speeduino/rtc_common.cpp: -------------------------------------------------------------------------------- 1 | #include "globals.h" 2 | #include RTC_LIB_H //Defined in each boards .h file 3 | #ifdef RTC_ENABLED 4 | #include "rtc_common.h" 5 | 6 | 7 | void initRTC() 8 | { 9 | 10 | #if defined(CORE_TEENSY35) || defined(CORE_TEENSY36)|| defined(CORE_TEENSY41) 11 | setSyncProvider(getTeensy3Time); 12 | #elif defined(CORE_STM32) 13 | 14 | #endif 15 | 16 | } 17 | 18 | uint8_t rtc_getSecond() 19 | { 20 | uint8_t tempSecond = 0; 21 | #ifdef RTC_ENABLED 22 | #if defined(CORE_TEENSY) 23 | tempSecond = second(); 24 | #elif defined(CORE_STM32) 25 | tempSecond = rtc.getSeconds(); 26 | #endif 27 | #endif 28 | return tempSecond; 29 | } 30 | 31 | uint8_t rtc_getMinute() 32 | { 33 | uint8_t tempMinute = 0; 34 | #ifdef RTC_ENABLED 35 | #if defined(CORE_TEENSY) 36 | tempMinute = minute(); 37 | #elif defined(CORE_STM32) 38 | tempMinute = rtc.getMinutes(); 39 | #endif 40 | #endif 41 | return tempMinute; 42 | } 43 | 44 | uint8_t rtc_getHour() 45 | { 46 | uint8_t tempHour = 0; 47 | #ifdef RTC_ENABLED 48 | #if defined(CORE_TEENSY) 49 | tempHour = hour(); 50 | #elif defined(CORE_STM32) 51 | tempHour = rtc.getHours(); 52 | #endif 53 | #endif 54 | return tempHour; 55 | } 56 | 57 | uint8_t rtc_getDay() 58 | { 59 | uint8_t tempDay = 0; 60 | #ifdef RTC_ENABLED 61 | #if defined(CORE_TEENSY) 62 | tempDay = day(); 63 | #elif defined(CORE_STM32) 64 | tempDay = rtc.getDay(); 65 | #endif 66 | #endif 67 | return tempDay; 68 | } 69 | 70 | uint8_t rtc_getDOW() 71 | { 72 | uint8_t dow = 0; 73 | #ifdef RTC_ENABLED 74 | #if defined(CORE_TEENSY) 75 | dow = weekday(); 76 | #elif defined(CORE_STM32) 77 | dow = rtc.getWeekDay(); 78 | #endif 79 | #endif 80 | return dow; 81 | } 82 | 83 | uint8_t rtc_getMonth() 84 | { 85 | uint8_t tempMonth = 0; 86 | #ifdef RTC_ENABLED 87 | #if defined(CORE_TEENSY) 88 | tempMonth = month(); 89 | #elif defined(CORE_STM32) 90 | tempMonth = rtc.getMonth(); 91 | #endif 92 | #endif 93 | return tempMonth; 94 | } 95 | 96 | uint16_t rtc_getYear() 97 | { 98 | uint16_t tempYear = 0; 99 | #ifdef RTC_ENABLED 100 | #if defined(CORE_TEENSY) 101 | tempYear = year(); 102 | #elif defined(CORE_STM32) 103 | //year in stm32 rtc is a byte. So add year 2000 to make it correct 104 | tempYear = (2000+rtc.getYear()); 105 | #endif 106 | #endif 107 | return tempYear; 108 | } 109 | 110 | void rtc_setTime(byte second, byte minute, byte hour, byte day, byte month, uint16_t year) 111 | { 112 | #ifdef RTC_ENABLED 113 | #if defined(CORE_TEENSY) 114 | setTime(hour, minute, second, day, month, year); 115 | Teensy3Clock.set(now()); 116 | #elif defined(CORE_STM32) 117 | //If RTC time has not been set earlier (no battery etc.) we need to stop the RTC and restart it with LSE_CLOCK to have accurate RTC. 118 | if (!rtc.isTimeSet()) { 119 | rtc.end(); 120 | rtc.setClockSource(STM32RTC::LSE_CLOCK); 121 | rtc.begin(); 122 | } 123 | rtc.setTime(hour, minute, second); 124 | //year in stm32 rtc is a byte. so subtract year 2000 to fit 125 | rtc.setDate(day, month, (year-2000)); 126 | #endif 127 | #endif 128 | } 129 | #endif 130 | -------------------------------------------------------------------------------- /speeduino/src/BackupSram/BackupSramAsEEPROM.cpp: -------------------------------------------------------------------------------- 1 | #if defined(STM32F407xx) 2 | #include "BackupSramAsEEPROM.h" 3 | 4 | 5 | BackupSramAsEEPROM::BackupSramAsEEPROM(){ 6 | //Enable the power interface clock 7 | RCC->APB1ENR |= RCC_APB1ENR_PWREN; 8 | 9 | //Enable the backup SRAM clock by setting BKPSRAMEN bit i 10 | RCC->AHB1ENR |= RCC_AHB1ENR_BKPSRAMEN; 11 | 12 | /** If the HSE divided by 2, 3, ..31 is used as the RTC clock, the 13 | * Backup Domain Access should be kept enabled. */ 14 | 15 | // Enable access to Backup domain 16 | PWR->CR |= PWR_CR_DBP; 17 | 18 | /** enable the backup regulator (used to maintain the backup SRAM content in 19 | * standby and Vbat modes). NOTE : this bit is not reset when the device 20 | * wakes up from standby, system reset or power reset. You can check that 21 | * the backup regulator is ready on PWR->CSR.brr, see rm p144 */ 22 | 23 | //Enable the backup power regulator. This makes the sram backup possible. bit is not reset by software! 24 | PWR->CSR |= PWR_CSR_BRE; 25 | 26 | //Wait until the backup power regulator is ready 27 | while ((PWR->CSR & PWR_CSR_BRR) == 0); 28 | } 29 | uint16_t BackupSramAsEEPROM::length(){ return 4096; } 30 | int8_t BackupSramAsEEPROM::write_byte( uint8_t *data, uint16_t bytes, uint16_t offset ) { 31 | uint8_t* base_addr = (uint8_t *) BKPSRAM_BASE; 32 | uint16_t i; 33 | if( bytes + offset >= backup_size ) { 34 | /* ERROR : the last byte is outside the backup SRAM region */ 35 | return -1; 36 | } 37 | 38 | /* disable backup domain write protection */ 39 | //Set the Disable Backup Domain write protection (DBP) bit in PWR power control register 40 | //PWR->CR |= PWR_CR_DBP; 41 | 42 | for( i = 0; i < bytes; i++ ) { 43 | *(base_addr + offset + i) = *(data + i); 44 | } 45 | //Enable write protection backup sram when finished 46 | //PWR->CR &= ~PWR_CR_DBP; 47 | return 0; 48 | } 49 | 50 | int8_t BackupSramAsEEPROM::read_byte( uint8_t *data, uint16_t bytes, uint16_t offset ) { 51 | uint8_t* base_addr = (uint8_t *) BKPSRAM_BASE; 52 | uint16_t i; 53 | if( bytes + offset >= backup_size ) { 54 | /* ERROR : the last byte is outside the backup SRAM region */ 55 | return -1; 56 | } 57 | 58 | for( i = 0; i < bytes; i++ ) { 59 | *(data + i) = *(base_addr + offset + i); 60 | } 61 | return 0; 62 | } 63 | 64 | uint8_t BackupSramAsEEPROM::read(uint16_t address) { 65 | uint8_t val = 0; 66 | read_byte(&val, 1, address); 67 | 68 | return val; 69 | } 70 | 71 | int8_t BackupSramAsEEPROM::write(uint16_t address, uint8_t val) { 72 | write_byte(&val, 1, address); 73 | return 0; 74 | } 75 | 76 | int8_t BackupSramAsEEPROM::update(uint16_t address, uint8_t val) { 77 | write_byte(&val, 1, address); 78 | return 0; 79 | } 80 | 81 | 82 | 83 | 84 | #endif 85 | 86 | -------------------------------------------------------------------------------- /.github/workflows/create-release.yml: -------------------------------------------------------------------------------- 1 | name: Create release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Cache pip 17 | uses: actions/cache@v4 18 | with: 19 | path: ~/.cache/pip 20 | key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} 21 | restore-keys: | 22 | ${{ runner.os }}-pip- 23 | 24 | - name: Cache PlatformIO 25 | uses: actions/cache@v4 26 | with: 27 | path: ~/.platformio 28 | key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} 29 | 30 | - name: Set up Python 31 | uses: actions/setup-python@v5 32 | 33 | - name: Install PlatformIO 34 | run: | 35 | python -m pip install --upgrade pip 36 | pip install --upgrade platformio 37 | 38 | - name: Build Firmware 39 | run: platformio run -e megaatmega2560 -e teensy35 -e teensy36 -e teensy41 -e black_F407VE 40 | 41 | - name: Upload to Speeduino server 42 | if: startsWith(github.ref, 'refs/tags/') 43 | env: 44 | WEB_PWD: ${{ secrets.WEB_PWD }} 45 | TAG_NAME: ${{ github.ref_name }} 46 | run: | 47 | curl https://speeduino.com:2078 || true 48 | curl --tlsv1.2 --ipv4 --user "speeduino_firmware@speeduino.com:$WEB_PWD" --basic -T "./.pio/build/megaatmega2560/firmware.hex" "https://speeduino.com:2078/bin/$TAG_NAME.hex" 49 | curl --tlsv1.2 --ipv4 --user "speeduino_firmware@speeduino.com:$WEB_PWD" --basic -T "./.pio/build/teensy35/firmware.hex" "https://speeduino.com:2078/teensy35/$TAG_NAME-teensy35.hex" 50 | curl --tlsv1.2 --ipv4 --user "speeduino_firmware@speeduino.com:$WEB_PWD" --basic -T "./.pio/build/teensy36/firmware.hex" "https://speeduino.com:2078/teensy36/$TAG_NAME-teensy36.hex" 51 | curl --tlsv1.2 --ipv4 --user "speeduino_firmware@speeduino.com:$WEB_PWD" --basic -T "./.pio/build/teensy41/firmware.hex" "https://speeduino.com:2078/teensy41/$TAG_NAME-teensy41.hex" 52 | curl --tlsv1.2 --ipv4 --user "speeduino_firmware@speeduino.com:$WEB_PWD" --basic -T "./.pio/build/black_F407VE/firmware.bin" "https://speeduino.com:2078/stm32f407/$TAG_NAME-stm32f407.bin" 53 | curl --tlsv1.2 --ipv4 --user "speeduino_firmware@speeduino.com:$WEB_PWD" --basic -T "./reference/speeduino.ini" "https://speeduino.com:2078/$TAG_NAME.ini" 54 | 55 | wget https://speeduino.com/fw/versions 56 | sed -i "1s/^/$TAG_NAME\n/" versions 57 | curl --tlsv1.2 --ipv4 --user "speeduino_firmware@speeduino.com:$WEB_PWD" --basic -T "./versions" "https://speeduino.com:2078/versions" 58 | 59 | 60 | - name: Build Changelog 61 | id: github_release 62 | uses: mikepenz/release-changelog-builder-action@v5 63 | env: 64 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 65 | with: 66 | mode: "COMMIT" 67 | fromTag: "202501" 68 | 69 | - name: Create Release 70 | uses: softprops/action-gh-release@v2 71 | if: startsWith(github.ref, 'refs/tags/') 72 | with: 73 | body: ${{steps.github_release.outputs.changelog}} 74 | files: | 75 | ./reference/speeduino.ini 76 | -------------------------------------------------------------------------------- /speeduino/src/SPIAsEEPROM/diagnostics.h: -------------------------------------------------------------------------------- 1 | /* Arduino SPIMemory Library v.3.1.0 2 | * Copyright (C) 2017 by Prajwal Bhattaram 3 | * Created by Prajwal Bhattaram - 19/05/2015 4 | * Modified by @boseji - 02/03/2017 5 | * Modified by Prajwal Bhattaram - 24/02/2018 6 | * 7 | * This file is part of the Arduino SPIMemory Library. This library is for 8 | * Winbond NOR flash memory modules. In its current form it enables reading 9 | * and writing individual data variables, structs and arrays from and to various locations; 10 | * reading and writing pages; continuous read functions; sector, block and chip erase; 11 | * suspending and resuming programming/erase and powering down for low power operation. 12 | * 13 | * This Library is free software: you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation, either version 3 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * This Library is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU General Public License v3.0 24 | * along with the Arduino SPIMemory Library. If not, see 25 | * . 26 | */ 27 | 28 | #ifndef DIAGNOSTICS_H 29 | #define DIAGNOSTICS_H 30 | 31 | #include "SPIMemory.h" 32 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// 33 | // List of Error codes // 34 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// 35 | 36 | #define SUCCESS 0x00 37 | #define CALLBEGIN 0x01 38 | #define UNKNOWNCHIP 0x02 39 | #define UNKNOWNCAP 0x03 40 | #define CHIPBUSY 0x04 41 | #define OUTOFBOUNDS 0x05 42 | #define CANTENWRITE 0x06 43 | #define PREVWRITTEN 0x07 44 | #define LOWRAM 0x08 45 | #define SYSSUSPEND 0x09 46 | #define ERRORCHKFAIL 0x0A 47 | #define NORESPONSE 0x0B 48 | #define UNSUPPORTEDFUNC 0x0C 49 | #define UNABLETO4BYTE 0x0D 50 | #define UNABLETO3BYTE 0x0E 51 | #define CHIPISPOWEREDDOWN 0x0F 52 | #define NOSFDP 0x10 53 | #define NOSFDPERASEPARAM 0x11 54 | #define NOSFDPERASETIME 0x12 55 | #define NOSFDPPROGRAMTIMEPARAM 0x13 56 | #define UNKNOWNERROR 0xFE 57 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// 58 | 59 | class Diagnostics { 60 | public: 61 | //------------------------------------ Constructor ------------------------------------// 62 | 63 | Diagnostics(void){}; 64 | ~Diagnostics(void){}; 65 | //------------------------------- Public functions -----------------------------------// 66 | void troubleshoot(uint8_t _code, bool printoverride = false); 67 | 68 | uint8_t errorcode; 69 | private: 70 | void _printErrorCode(void); 71 | void _printSupportLink(void); 72 | 73 | }; 74 | 75 | extern Diagnostics diagnostics; ///< default Diagnostics instance 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /speeduino/src/SPIAsEEPROM/winbondflash.h: -------------------------------------------------------------------------------- 1 | /* 2 | Winbond spi flash memory chip operating library for Arduino 3 | by WarMonkey (luoshumymail@gmail.com) 4 | for more information, please visit bbs.kechuang.org 5 | latest version available on http://code.google.com/p/winbondflash 6 | */ 7 | 8 | #ifndef _WINBONDFLASH_H__ 9 | #define _WINBONDFLASH_H__ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | //W25Q64 = 256_bytes_per_page * 16_pages_per_sector * 16_sectors_per_block * 128_blocks_per_chip 17 | //= 256b*16*16*128 = 8Mbyte = 64MBits 18 | 19 | //W25Q16 = 256_bytes_per_page * 16_pages_per_sector * 16_sectors_per_block * 32_blocks_per_chip 20 | //= 256b*16*16*32 = 2Mbyte = 16MBits 21 | 22 | #define _W25Q80 winbondFlashClass::W25Q80 23 | #define _W25Q16 winbondFlashClass::W25Q16 24 | #define _W25Q32 winbondFlashClass::W25Q32 25 | #define _W25Q64 winbondFlashClass::W25Q64 26 | #define _W25Q128 winbondFlashClass::W25Q128 27 | 28 | class winbondFlashClass { 29 | public: 30 | enum partNumber { 31 | custom = -1, 32 | autoDetect = 0, 33 | W25Q80 = 1, 34 | W25Q16 = 2, 35 | W25Q32 = 4, 36 | W25Q64 = 8, 37 | W25Q128 = 16 38 | }; 39 | 40 | bool begin(partNumber _partno = autoDetect); 41 | void end(); 42 | 43 | long bytes(); 44 | uint16_t pages(); 45 | uint16_t sectors(); 46 | uint16_t blocks(); 47 | 48 | uint16_t read(uint32_t addr,uint8_t *buf,uint16_t n=256); 49 | 50 | void setWriteEnable(bool cmd = true); 51 | inline void WE(bool cmd = true) {setWriteEnable(cmd);} 52 | 53 | //WE() every time before write or erase 54 | void writePage(uint32_t addr_start,uint8_t *buf, uint16_t n);//addr is 8bit-aligned, 0x00ffff00 55 | //write a page, sizeof(buf) is 256 bytes 56 | void eraseSector(uint32_t addr);//addr is 12bit-aligned, 0x00fff000 57 | //erase a sector ( 4096bytes ), return false if error 58 | void erase32kBlock(uint32_t addr);//addr is 15bit-aligned, 0x00ff8000 59 | //erase a 32k block ( 32768b ) 60 | void erase64kBlock(uint32_t addr);//addr is 16bit-aligned, 0x00ff0000 61 | //erase a 64k block ( 65536b ) 62 | void eraseAll(); 63 | //chip erase, return true if successfully started, busy()==false -> erase complete 64 | 65 | void eraseSuspend(); 66 | void eraseResume(); 67 | 68 | bool busy(); 69 | 70 | uint8_t readManufacturer(); 71 | uint16_t readPartID(); 72 | uint64_t readUniqueID(); 73 | uint16_t readSR(); 74 | 75 | private: 76 | partNumber partno; 77 | bool checkPartNo(partNumber _partno); 78 | 79 | protected: 80 | virtual void select() = 0; 81 | virtual uint8_t transfer(uint8_t x) = 0; 82 | virtual void deselect() = 0; 83 | 84 | }; 85 | 86 | class winbondFlashSPI: public winbondFlashClass { 87 | private: 88 | uint8_t nss; 89 | SPIClass *spi_port; 90 | inline void select() { 91 | digitalWrite(nss,LOW); 92 | } 93 | 94 | inline uint8_t transfer(uint8_t x) { 95 | byte y = spi_port->transfer(x); 96 | return y; 97 | } 98 | 99 | inline void deselect() { 100 | digitalWrite(nss,HIGH); 101 | } 102 | 103 | public: 104 | bool begin(partNumber _partno = autoDetect,SPIClass &_spi = SPI,uint8_t _nss = SS); 105 | void end(); 106 | }; 107 | 108 | #endif 109 | 110 | -------------------------------------------------------------------------------- /speeduino/page_crc.cpp: -------------------------------------------------------------------------------- 1 | #include "globals.h" 2 | #include "page_crc.h" 3 | #include "pages.h" 4 | #include "table3d_axis_io.h" 5 | #include "src/FastCRC/FastCRC.h" 6 | 7 | using pCrcCalc = uint32_t (FastCRC32::*)(const uint8_t *, const uint16_t, bool); 8 | 9 | static inline uint32_t compute_raw_crc(const page_iterator_t &entity, pCrcCalc calcFunc, FastCRC32 &crcCalc) 10 | { 11 | return (crcCalc.*calcFunc)((uint8_t*)entity.pData, entity.size, false); 12 | } 13 | 14 | static inline uint32_t compute_row_crc(const table_row_iterator &row, pCrcCalc calcFunc, FastCRC32 &crcCalc) 15 | { 16 | return (crcCalc.*calcFunc)(&*row, row.size(), false); 17 | } 18 | 19 | static inline uint32_t compute_tablevalues_crc(table_value_iterator it, pCrcCalc calcFunc, FastCRC32 &crcCalc) 20 | { 21 | uint32_t crc = compute_row_crc(*it, calcFunc, crcCalc); 22 | ++it; 23 | 24 | while (!it.at_end()) 25 | { 26 | crc = compute_row_crc(*it, &FastCRC32::crc32_upd, crcCalc); 27 | ++it; 28 | } 29 | return crc; 30 | } 31 | 32 | static inline uint32_t compute_tableaxis_crc(table_axis_iterator it, uint32_t crc, FastCRC32 &crcCalc) 33 | { 34 | const table3d_axis_io_converter converter = get_table3d_axis_converter(it.get_domain()); 35 | 36 | byte values[32]; // Fingers crossed we don't have a table bigger than 32x32 37 | byte *pValue = values; 38 | while (!it.at_end()) 39 | { 40 | *pValue++ = converter.to_byte(*it); 41 | ++it; 42 | } 43 | return pValue-values==0 ? crc : crcCalc.crc32_upd(values, pValue-values, false); 44 | } 45 | 46 | static inline uint32_t compute_table_crc(const page_iterator_t &entity, pCrcCalc calcFunc, FastCRC32 &crcCalc) 47 | { 48 | return compute_tableaxis_crc(y_begin(entity), 49 | compute_tableaxis_crc(x_begin(entity), 50 | compute_tablevalues_crc(rows_begin(entity), calcFunc, crcCalc), 51 | crcCalc), 52 | crcCalc); 53 | } 54 | 55 | static inline uint32_t pad_crc(uint16_t padding, uint32_t crc, FastCRC32 &crcCalc) 56 | { 57 | const uint8_t raw_value = 0u; 58 | while (padding>0) 59 | { 60 | crc = crcCalc.crc32_upd(&raw_value, 1, false); 61 | --padding; 62 | } 63 | return crc; 64 | } 65 | 66 | static inline uint32_t compute_crc(const page_iterator_t &entity, pCrcCalc calcFunc, FastCRC32 &crcCalc) 67 | { 68 | switch (entity.type) 69 | { 70 | case Raw: 71 | return compute_raw_crc(entity, calcFunc, crcCalc); 72 | break; 73 | 74 | case Table: 75 | return compute_table_crc(entity, calcFunc, crcCalc); 76 | break; 77 | 78 | case NoEntity: 79 | return pad_crc(entity.size, 0U, crcCalc); 80 | break; 81 | 82 | default: 83 | abort(); 84 | break; 85 | } 86 | } 87 | 88 | uint32_t calculatePageCRC32(byte pageNum) 89 | { 90 | FastCRC32 crcCalc; 91 | page_iterator_t entity = page_begin(pageNum); 92 | // Initial CRC calc 93 | uint32_t crc = compute_crc(entity, &FastCRC32::crc32, crcCalc); 94 | 95 | entity = advance(entity); 96 | while (entity.type!=End) 97 | { 98 | crc = compute_crc(entity, &FastCRC32::crc32_upd /* Note that we are *updating* */, crcCalc); 99 | entity = advance(entity); 100 | } 101 | return ~pad_crc(getPageSize(pageNum) - entity.size, crc, crcCalc); 102 | } -------------------------------------------------------------------------------- /.github/workflows/build-firmware.yml: -------------------------------------------------------------------------------- 1 | name: Build Firmware 2 | 3 | on: 4 | push: 5 | branches: [ master, '*-fixes' ] 6 | pull_request: 7 | branches: [ master ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - name: Cache pip 19 | uses: actions/cache@v4 20 | with: 21 | path: ~/.cache/pip 22 | key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} 23 | restore-keys: | 24 | ${{ runner.os }}-pip- 25 | 26 | - name: Cache PlatformIO 27 | uses: actions/cache@v4 28 | with: 29 | path: ~/.platformio 30 | key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} 31 | 32 | - name: Set up Python 33 | uses: actions/setup-python@v5 34 | 35 | - name: Install PlatformIO 36 | run: | 37 | python -m pip install --upgrade pip 38 | pip install --upgrade platformio 39 | 40 | - name: Build test atmel 41 | run: platformio run -e megaatmega2560 -e megaatmega2560-6-3 -e megaatmega2560-8-1 -e megaatmega2561 42 | 43 | - name: Build test teensy 44 | run: platformio run -e teensy35 -e teensy36 -e teensy41 45 | 46 | - name: Build test STM32 47 | run: platformio run -e black_F407VE -e BlackPill_F401CC -e BlackPill_F411CE_USB 48 | 49 | - name: Upload to Speeduino server 50 | if: github.event_name != 'pull_request' && github.repository_owner == 'speeduino' && github.ref_name == 'master' 51 | env: 52 | WEB_PWD: ${{ secrets.WEB_PWD }} 53 | run: | 54 | curl -v https://speeduino.com:2078 || true 55 | curl -v --tlsv1.2 --ipv4 --user "speeduino_firmware@speeduino.com:$WEB_PWD" --basic -T "./.pio/build/megaatmega2560/firmware.hex" "https://speeduino.com:2078/bin/master.hex" 56 | curl -v --tlsv1.2 --ipv4 --user "speeduino_firmware@speeduino.com:$WEB_PWD" --basic -T "./.pio/build/teensy35/firmware.hex" "https://speeduino.com:2078/teensy35/master-teensy35.hex" 57 | curl -v --tlsv1.2 --ipv4 --user "speeduino_firmware@speeduino.com:$WEB_PWD" --basic -T "./.pio/build/teensy36/firmware.hex" "https://speeduino.com:2078/teensy36/master-teensy36.hex" 58 | curl -v --tlsv1.2 --ipv4 --user "speeduino_firmware@speeduino.com:$WEB_PWD" --basic -T "./.pio/build/teensy41/firmware.hex" "https://speeduino.com:2078/teensy41/master-teensy41.hex" 59 | curl -v --tlsv1.2 --ipv4 --user "speeduino_firmware@speeduino.com:$WEB_PWD" --basic -T "./.pio/build/black_F407VE/firmware.bin" "https://speeduino.com:2078/stm32f407/master-stm32f407.bin" 60 | curl -v --tlsv1.2 --ipv4 --user "speeduino_firmware@speeduino.com:$WEB_PWD" --basic -T "./reference/speeduino.ini" "https://speeduino.com:2078/master.ini" 61 | 62 | - name: Discord Notification (Build Failed) 63 | uses: rjstone/discord-webhook-notify@v1 64 | if: failure() && github.event_name != 'pull_request' && github.repository_owner == 'speeduino' 65 | with: 66 | severity: error 67 | avatarUrl: https://avatars.githubusercontent.com/u/9919?v=4&size=48 68 | username: GitHub 69 | description: 'Firmware build FAILED :warning:' 70 | details: "**Author:** ${{ github.event.pusher.name }} \n 71 | **Commit URL:** ${{ github.event.head_commit.url }} \n 72 | **Commit Message:** ${{ github.event.head_commit.message }}" 73 | webhookUrl: ${{ secrets.DISCORD_WEBHOOK }} -------------------------------------------------------------------------------- /speeduino/src/FlashStorage/FlashAsEEPROM.h: -------------------------------------------------------------------------------- 1 | /* 2 | EEPROM like API that uses Arduino Zero's flash memory. 3 | Written by A. Christian 4 | 5 | Copyright (c) 2015-2016 Arduino LLC. All right reserved. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 15 | See the GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | 22 | #ifndef FLASH_AS_EEPROM_h 23 | #define FLASH_AS_EEPROM_h 24 | 25 | #include "FlashStorage.h" 26 | 27 | #ifndef EEPROM_EMULATION_SIZE 28 | #define EEPROM_EMULATION_SIZE 1024 29 | #endif 30 | 31 | typedef struct { 32 | byte data[EEPROM_EMULATION_SIZE]; 33 | boolean valid; 34 | } EEPROM_EMULATION; 35 | 36 | 37 | class EEPROMClass { 38 | 39 | public: 40 | EEPROMClass(void); 41 | 42 | /** 43 | * Read an eeprom cell 44 | * @param index 45 | * @return value 46 | */ 47 | uint8_t read(int); 48 | 49 | /** 50 | * Write value to an eeprom cell 51 | * @param index 52 | * @param value 53 | */ 54 | void write(int, uint8_t); 55 | 56 | /** 57 | * Update a eeprom cell 58 | * @param index 59 | * @param value 60 | */ 61 | void update(int, uint8_t); 62 | 63 | /** 64 | * Read AnyTypeOfData from eeprom 65 | * @param address 66 | * @return AnyTypeOfData 67 | */ 68 | template< typename T > T &get( int idx, T &t ){ 69 | uint16_t e = idx; 70 | uint8_t *ptr = (uint8_t*) &t; 71 | for( int count = sizeof(T) ; count ; --count, ++e ) *ptr++ = read(e); 72 | return t; 73 | } 74 | 75 | /** 76 | * Write AnyTypeOfData to eeprom 77 | * @param address 78 | * @param AnyTypeOfData 79 | * @return number of bytes written to flash 80 | */ 81 | template< typename T > const T &put( int idx, const T &t ){ 82 | const uint8_t *ptr = (const uint8_t*) &t; 83 | uint16_t e = idx; 84 | for( int count = sizeof(T) ; count ; --count, ++e ) update(e, *ptr++); 85 | return t; 86 | } 87 | 88 | /** 89 | * Check whether the eeprom data is valid 90 | * @return true, if eeprom data is valid (has been written at least once), false if not 91 | */ 92 | bool isValid(); 93 | 94 | /** 95 | * Write previously made eeprom changes to the underlying flash storage 96 | * Use this with care: Each and every commit will harm the flash and reduce it's lifetime (like with every flash memory) 97 | */ 98 | void commit(); 99 | 100 | uint16_t length() { return EEPROM_EMULATION_SIZE; } 101 | 102 | private: 103 | void init(); 104 | 105 | bool _initialized; 106 | EEPROM_EMULATION _eeprom; 107 | bool _dirty; 108 | }; 109 | 110 | extern EEPROMClass EEPROM; 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /misra/all_rules.txt: -------------------------------------------------------------------------------- 1 | misra-c2012-1.1 2 | misra-c2012-1.2 3 | misra-c2012-1.3 4 | misra-c2012-2.1 5 | misra-c2012-2.2 6 | misra-c2012-2.3 7 | misra-c2012-2.4 8 | misra-c2012-2.5 9 | misra-c2012-2.6 10 | misra-c2012-2.7 11 | misra-c2012-3.1 12 | misra-c2012-3.2 13 | misra-c2012-3.3 14 | misra-c2012-4.1 15 | misra-c2012-4.2 16 | misra-c2012-5.1 17 | misra-c2012-5.2 18 | misra-c2012-5.3 19 | misra-c2012-5.4 20 | misra-c2012-5.5 21 | misra-c2012-5.6 22 | misra-c2012-5.7 23 | misra-c2012-5.8 24 | misra-c2012-5.9 25 | misra-c2012-6.1 26 | misra-c2012-6.2 27 | misra-c2012-7.1 28 | misra-c2012-7.2 29 | misra-c2012-7.3 30 | misra-c2012-7.4 31 | misra-c2012-8.1 32 | misra-c2012-8.2 33 | misra-c2012-8.3 34 | misra-c2012-8.4 35 | misra-c2012-8.5 36 | misra-c2012-8.6 37 | misra-c2012-8.7 38 | misra-c2012-8.8 39 | misra-c2012-8.9 40 | misra-c2012-8.10 41 | misra-c2012-8.11 42 | misra-c2012-8.12 43 | misra-c2012-8.13 44 | misra-c2012-8.14 45 | misra-c2012-9.1 46 | misra-c2012-9.2 47 | misra-c2012-9.3 48 | misra-c2012-9.4 49 | misra-c2012-9.5 50 | misra-c2012-10.1 51 | misra-c2012-10.2 52 | misra-c2012-10.3 53 | misra-c2012-10.4 54 | misra-c2012-10.5 55 | misra-c2012-10.6 56 | misra-c2012-10.7 57 | misra-c2012-10.8 58 | misra-c2012-11.1 59 | misra-c2012-11.2 60 | misra-c2012-11.3 61 | misra-c2012-11.4 62 | misra-c2012-11.5 63 | misra-c2012-11.6 64 | misra-c2012-11.7 65 | misra-c2012-11.8 66 | misra-c2012-11.9 67 | misra-c2012-12.1 68 | misra-c2012-12.2 69 | misra-c2012-12.3 70 | misra-c2012-12.4 71 | misra-c2012-13.1 72 | misra-c2012-13.2 73 | misra-c2012-13.3 74 | misra-c2012-13.4 75 | misra-c2012-13.5 76 | misra-c2012-13.6 77 | misra-c2012-14.1 78 | misra-c2012-14.2 79 | misra-c2012-14.3 80 | misra-c2012-14.4 81 | misra-c2012-15.1 82 | misra-c2012-15.2 83 | misra-c2012-15.3 84 | misra-c2012-15.4 85 | misra-c2012-15.5 86 | misra-c2012-15.6 87 | misra-c2012-15.7 88 | misra-c2012-16.1 89 | misra-c2012-16.2 90 | misra-c2012-16.3 91 | misra-c2012-16.4 92 | misra-c2012-16.5 93 | misra-c2012-16.6 94 | misra-c2012-16.7 95 | misra-c2012-17.1 96 | misra-c2012-17.2 97 | misra-c2012-17.3 98 | misra-c2012-17.4 99 | misra-c2012-17.5 100 | misra-c2012-17.6 101 | misra-c2012-17.7 102 | misra-c2012-17.8 103 | misra-c2012-18.1 104 | misra-c2012-18.2 105 | misra-c2012-18.3 106 | misra-c2012-18.4 107 | misra-c2012-18.5 108 | misra-c2012-18.6 109 | misra-c2012-18.7 110 | misra-c2012-18.8 111 | misra-c2012-19.1 112 | misra-c2012-19.2 113 | misra-c2012-20.1 114 | misra-c2012-20.2 115 | misra-c2012-20.3 116 | misra-c2012-20.4 117 | misra-c2012-20.5 118 | misra-c2012-20.6 119 | misra-c2012-20.7 120 | misra-c2012-20.8 121 | misra-c2012-20.9 122 | misra-c2012-20.10 123 | misra-c2012-20.11 124 | misra-c2012-20.12 125 | misra-c2012-20.13 126 | misra-c2012-20.14 127 | misra-c2012-21.1 128 | misra-c2012-21.2 129 | misra-c2012-21.3 130 | misra-c2012-21.4 131 | misra-c2012-21.5 132 | misra-c2012-21.6 133 | misra-c2012-21.7 134 | misra-c2012-21.8 135 | misra-c2012-21.9 136 | misra-c2012-21.10 137 | misra-c2012-21.11 138 | misra-c2012-21.12 139 | misra-c2012-21.13 140 | misra-c2012-21.14 141 | misra-c2012-21.15 142 | misra-c2012-21.16 143 | misra-c2012-21.17 144 | misra-c2012-21.18 145 | misra-c2012-21.19 146 | misra-c2012-21.20 147 | misra-c2012-21.21 148 | misra-c2012-22.1 149 | misra-c2012-22.2 150 | misra-c2012-22.3 151 | misra-c2012-22.4 152 | misra-c2012-22.5 153 | misra-c2012-22.6 154 | misra-c2012-22.7 155 | misra-c2012-22.8 156 | misra-c2012-22.9 157 | misra-c2012-22.10 158 | -------------------------------------------------------------------------------- /speeduino/src/FlashStorage/FlashStorage.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015 Arduino LLC. All right reserved. 3 | Written by Cristian Maglie 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13 | See the GNU Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | */ 19 | #if defined(CORE_SAM) && !defined(USE_SPI_EEPROM) 20 | #include "FlashStorage.h" 21 | 22 | 23 | 24 | static const uint32_t pageSizes[] = { 8, 16, 32, 64, 128, 256, 512, 1024 }; 25 | 26 | FlashClass::FlashClass(const void *flash_addr, uint32_t size) : 27 | PAGE_SIZE(pageSizes[NVMCTRL->PARAM.bit.PSZ]), 28 | PAGES(NVMCTRL->PARAM.bit.NVMP), 29 | MAX_FLASH(PAGE_SIZE * PAGES), 30 | ROW_SIZE(PAGE_SIZE * 4), 31 | flash_address((volatile void *)flash_addr), 32 | flash_size(size) 33 | { 34 | } 35 | 36 | static inline uint32_t read_unaligned_uint32(const void *data) 37 | { 38 | union { 39 | uint32_t u32; 40 | uint8_t u8[4]; 41 | } res; 42 | const uint8_t *d = (const uint8_t *)data; 43 | res.u8[0] = d[0]; 44 | res.u8[1] = d[1]; 45 | res.u8[2] = d[2]; 46 | res.u8[3] = d[3]; 47 | return res.u32; 48 | } 49 | 50 | void FlashClass::write(const volatile void *flash_ptr, const void *data, uint32_t size) 51 | { 52 | // Calculate data boundaries 53 | size = (size + 3) / 4; 54 | volatile uint32_t *dst_addr = (volatile uint32_t *)flash_ptr; 55 | const uint8_t *src_addr = (uint8_t *)data; 56 | 57 | // Disable automatic page write 58 | NVMCTRL->CTRLB.bit.MANW = 1; 59 | 60 | // Do writes in pages 61 | while (size) { 62 | // Execute "PBC" Page Buffer Clear 63 | NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC; 64 | while (NVMCTRL->INTFLAG.bit.READY == 0) { } 65 | 66 | // Fill page buffer 67 | uint32_t i; 68 | for (i=0; i<(PAGE_SIZE/4) && size; i++) { 69 | *dst_addr = read_unaligned_uint32(src_addr); 70 | src_addr += 4; 71 | dst_addr++; 72 | size--; 73 | } 74 | 75 | // Execute "WP" Write Page 76 | NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_WP; 77 | while (NVMCTRL->INTFLAG.bit.READY == 0) { } 78 | } 79 | } 80 | 81 | void FlashClass::erase(const volatile void *flash_ptr, uint32_t size) 82 | { 83 | const uint8_t *ptr = (const uint8_t *)flash_ptr; 84 | while (size > ROW_SIZE) { 85 | erase(ptr); 86 | ptr += ROW_SIZE; 87 | size -= ROW_SIZE; 88 | } 89 | erase(ptr); 90 | } 91 | 92 | void FlashClass::erase(const volatile void *flash_ptr) 93 | { 94 | NVMCTRL->ADDR.reg = ((uint32_t)flash_ptr) / 2; 95 | NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER; 96 | while (!NVMCTRL->INTFLAG.bit.READY) { } 97 | } 98 | 99 | void FlashClass::read(const volatile void *flash_ptr, void *data, uint32_t size) 100 | { 101 | memcpy(data, (const void *)flash_ptr, size); 102 | } 103 | 104 | #endif -------------------------------------------------------------------------------- /speeduino/schedule_calcs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "scheduler.h" 5 | 6 | extern int ignition1StartAngle; 7 | extern int ignition1EndAngle; 8 | extern int channel1IgnDegrees; /**< The number of crank degrees until cylinder 1 is at TDC (This is obviously 0 for virtually ALL engines, but there's some weird ones) */ 9 | 10 | extern int ignition2StartAngle; 11 | extern int ignition2EndAngle; 12 | extern int channel2IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */ 13 | 14 | extern int ignition3StartAngle; 15 | extern int ignition3EndAngle; 16 | extern int channel3IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */ 17 | 18 | extern int ignition4StartAngle; 19 | extern int ignition4EndAngle; 20 | extern int channel4IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */ 21 | 22 | #if (IGN_CHANNELS >= 5) 23 | extern int ignition5StartAngle; 24 | extern int ignition5EndAngle; 25 | extern int channel5IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */ 26 | #endif 27 | #if (IGN_CHANNELS >= 6) 28 | extern int ignition6StartAngle; 29 | extern int ignition6EndAngle; 30 | extern int channel6IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */ 31 | #endif 32 | #if (IGN_CHANNELS >= 7) 33 | extern int ignition7StartAngle; 34 | extern int ignition7EndAngle; 35 | extern int channel7IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */ 36 | #endif 37 | #if (IGN_CHANNELS >= 8) 38 | extern int ignition8StartAngle; 39 | extern int ignition8EndAngle; 40 | extern int channel8IgnDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */ 41 | #endif 42 | 43 | extern int channel1InjDegrees; /**< The number of crank degrees until cylinder 1 is at TDC (This is obviously 0 for virtually ALL engines, but there's some weird ones) */ 44 | extern int channel2InjDegrees; /**< The number of crank degrees until cylinder 2 (and 5/6/7/8) is at TDC */ 45 | extern int channel3InjDegrees; /**< The number of crank degrees until cylinder 3 (and 5/6/7/8) is at TDC */ 46 | extern int channel4InjDegrees; /**< The number of crank degrees until cylinder 4 (and 5/6/7/8) is at TDC */ 47 | #if (INJ_CHANNELS >= 5) 48 | extern int channel5InjDegrees; /**< The number of crank degrees until cylinder 5 is at TDC */ 49 | #endif 50 | #if (INJ_CHANNELS >= 6) 51 | extern int channel6InjDegrees; /**< The number of crank degrees until cylinder 6 is at TDC */ 52 | #endif 53 | #if (INJ_CHANNELS >= 7) 54 | extern int channel7InjDegrees; /**< The number of crank degrees until cylinder 7 is at TDC */ 55 | #endif 56 | #if (INJ_CHANNELS >= 8) 57 | extern int channel8InjDegrees; /**< The number of crank degrees until cylinder 8 is at TDC */ 58 | #endif 59 | 60 | static inline uint16_t __attribute__((always_inline)) calculateInjectorStartAngle(uint16_t PWdivTimerPerDegree, int16_t injChannelDegrees, uint16_t injAngle); 61 | 62 | static inline uint32_t __attribute__((always_inline)) calculateInjectorTimeout(const FuelSchedule &schedule, int injectorStartAngle, int crankAngle); 63 | 64 | static inline void __attribute__((always_inline)) calculateIgnitionAngle(const uint16_t dwellAngle, const uint16_t channelIgnDegrees, int8_t advance, int *pEndAngle, int *pStartAngle); 65 | 66 | // Ignition for rotary. 67 | static inline void __attribute__((always_inline)) calculateIgnitionTrailingRotary(uint16_t dwellAngle, int rotarySplitDegrees, int leadIgnitionAngle, int *pEndAngle, int *pStartAngle); 68 | 69 | static inline uint32_t __attribute__((always_inline)) calculateIgnitionTimeout(const IgnitionSchedule &schedule, int startAngle, int channelIgnDegrees, int crankAngle); 70 | 71 | #include "schedule_calcs.hpp" 72 | -------------------------------------------------------------------------------- /speeduino/table3d_axes.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @addtogroup table_3d 3 | * @{ 4 | */ 5 | 6 | /** \file 7 | * @brief 3D table axis types and iterators 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "table3d_typedefs.h" 13 | 14 | /**\enum axis_domain 15 | * @brief Encodes the real world measurement that a table axis captures 16 | * */ 17 | enum axis_domain { 18 | /** RPM (engine speed) */ 19 | axis_domain_Rpm, 20 | /** Load */ 21 | axis_domain_Load, 22 | /** Throttle position */ 23 | axis_domain_Tps 24 | }; 25 | 26 | /** @brief Iterate over table axis elements */ 27 | class table_axis_iterator 28 | { 29 | public: 30 | 31 | /** @brief Construct */ 32 | table_axis_iterator(table3d_axis_t *pStart, const table3d_axis_t *pEnd, axis_domain domain) 33 | : _stride(pEnd>pStart ? stride_inc : stride_dec) 34 | , _pStart(pStart) 35 | , _pEnd(pEnd + _stride) 36 | , _domain(domain) //cppcheck-suppress misra-c2012-10.4 37 | { 38 | } 39 | 40 | axis_domain get_domain(void) const { return _domain; } 41 | 42 | /** @brief Advance the iterator 43 | * @param steps The number of elements to move the iterator 44 | */ 45 | table_axis_iterator& advance(int8_t steps) 46 | { 47 | _pStart = _pStart + ((int16_t)_stride * steps); 48 | return *this; 49 | } 50 | 51 | /** @brief Increment the iterator by one element*/ 52 | table_axis_iterator& operator++(void) 53 | { 54 | return advance(1); 55 | } 56 | 57 | /** @brief Test for end of iteration */ 58 | bool at_end(void) const 59 | { 60 | return _pStart == _pEnd; 61 | } 62 | 63 | /** @brief Dereference the iterator */ 64 | table3d_axis_t& operator*(void) 65 | { 66 | return *_pStart; 67 | } 68 | /** @copydoc table_axis_iterator::operator*() */ 69 | const table3d_axis_t& operator*(void) const 70 | { 71 | return *_pStart; 72 | } 73 | 74 | private: 75 | 76 | static constexpr int8_t stride_inc = 1; 77 | static constexpr int8_t stride_dec = -1; 78 | int8_t _stride; 79 | table3d_axis_t *_pStart; 80 | const table3d_axis_t *_pEnd; 81 | const axis_domain _domain; 82 | }; 83 | 84 | #define TABLE3D_TYPENAME_AXIS(size, domain) table3d ## size ## domain ## _axis 85 | 86 | #define TABLE3D_GEN_AXIS(size, dom) \ 87 | /** @brief The dxis for a 3D table with size x size dimensions and domain 'domain' */ \ 88 | struct TABLE3D_TYPENAME_AXIS(size, dom) { \ 89 | /** @brief The length of the axis in elements */ \ 90 | static constexpr table3d_dim_t length = (size); \ 91 | /** @brief The domain the axis represents */ \ 92 | static constexpr axis_domain domain = axis_domain_ ## dom; \ 93 | /** 94 | @brief The axis elements\ 95 | */ \ 96 | table3d_axis_t axis[(size)]; \ 97 | \ 98 | /** @brief Iterate over the axis elements */ \ 99 | table_axis_iterator begin(void) \ 100 | { \ 101 | return table_axis_iterator(axis+(size)-1, axis, domain); \ 102 | } \ 103 | /** @brief Iterate over the axis elements, from largest to smallest */ \ 104 | table_axis_iterator rbegin(void) \ 105 | { \ 106 | return table_axis_iterator(axis, axis+(size)-1, domain); \ 107 | } \ 108 | }; 109 | 110 | // This generates the axis types for the following sizes & domains: 111 | TABLE3D_GEN_AXIS(6, Rpm) 112 | TABLE3D_GEN_AXIS(6, Load) 113 | TABLE3D_GEN_AXIS(4, Rpm) 114 | TABLE3D_GEN_AXIS(4, Load) 115 | TABLE3D_GEN_AXIS(8, Rpm) 116 | TABLE3D_GEN_AXIS(8, Load) 117 | TABLE3D_GEN_AXIS(8, Tps) 118 | TABLE3D_GEN_AXIS(16, Rpm) 119 | TABLE3D_GEN_AXIS(16, Load) 120 | 121 | /** @} */ -------------------------------------------------------------------------------- /speeduino/pages.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "table3d.h" 4 | 5 | /** 6 | * Page count, as defined in the INI file 7 | */ 8 | uint8_t getPageCount(void); 9 | 10 | /** 11 | * Page size in bytes 12 | */ 13 | uint16_t getPageSize(byte pageNum /**< [in] The page number */ ); 14 | 15 | // These are the page numbers that the Tuner Studio serial protocol uses to transverse the different map and config pages. 16 | #define veMapPage 2 17 | #define veSetPage 1 //Note that this and the veMapPage were swapped in Feb 2019 as the 'algorithm' field must be declared in the ini before it's used in the fuel table 18 | #define ignMapPage 3 19 | #define ignSetPage 4//Config Page 2 20 | #define afrMapPage 5 21 | #define afrSetPage 6//Config Page 3 22 | #define boostvvtPage 7 23 | #define seqFuelPage 8 24 | #define canbusPage 9//Config Page 9 25 | #define warmupPage 10 //Config Page 10 26 | #define fuelMap2Page 11 27 | #define wmiMapPage 12 28 | #define progOutsPage 13 29 | #define ignMap2Page 14 30 | #define boostvvtPage2 15 31 | 32 | // ============================== Per-byte page access ========================== 33 | 34 | /** 35 | * Gets a single value from a page, with data aligned as per the ini file 36 | */ 37 | byte getPageValue( byte pageNum, /**< [in] The page number to retrieve data from. */ 38 | uint16_t offset /**< [in] The address in the page that should be returned. This is as per the page definition in the ini. */ 39 | ); 40 | 41 | /** 42 | * Sets a single value from a page, with data aligned as per the ini file 43 | */ 44 | void setPageValue( byte pageNum, /**< [in] The page number to retrieve data from. */ 45 | uint16_t offset, /**< [in] The address in the page that should be returned. This is as per the page definition in the ini. */ 46 | byte value /**< [in] The new value */ 47 | ); 48 | 49 | // ============================== Page Iteration ========================== 50 | 51 | // A logical TS page is actually multiple in memory entities. Allow iteration 52 | // over those entities. 53 | 54 | // Type of entity 55 | enum entity_type { 56 | Raw, // A block of memory 57 | Table, // A 3D table 58 | NoEntity, // No entity, but a valid offset 59 | End // The offset was past any known entity for the page 60 | }; 61 | 62 | // A entity on a logical page. 63 | struct page_iterator_t { 64 | void *pData; 65 | table_type_t table_key; 66 | uint8_t page; // The page the entity belongs to 67 | uint16_t start; // The start position of the entity, in bytes, from the start of the page 68 | uint16_t size; // Size of the entity in bytes 69 | entity_type type; 70 | }; 71 | 72 | /** 73 | * Initiates iteration over a pages entities. 74 | * Test `entity.type==End` to determine the end of the page. 75 | */ 76 | page_iterator_t page_begin(byte pageNum /**< [in] The page number to iterate over. */); 77 | 78 | /** 79 | * Moves the iterator to the next sub-entity on the page 80 | */ 81 | page_iterator_t advance(const page_iterator_t &it /**< [in] The current iterator */); 82 | 83 | /** 84 | * Convert page iterator to table value iterator. 85 | */ 86 | table_value_iterator rows_begin(const page_iterator_t &it); 87 | 88 | /** 89 | * Convert page iterator to table x axis iterator. 90 | */ 91 | table_axis_iterator x_begin(const page_iterator_t &it); 92 | 93 | /** 94 | * Convert page iterator to table x axis iterator. 95 | */ 96 | table_axis_iterator x_rbegin(const page_iterator_t &it); 97 | 98 | /** 99 | * Convert page iterator to table y axis iterator. 100 | */ 101 | table_axis_iterator y_begin(const page_iterator_t &it); 102 | -------------------------------------------------------------------------------- /.github/workflows/pr-memory-deltas-generate.yml: -------------------------------------------------------------------------------- 1 | name: Calculate memory deltas 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | env: 8 | # It's convenient to set variables for values used multiple times in the workflow. 9 | SKETCHES_REPORTS_PATH: sketches-reports 10 | 11 | jobs: 12 | compile: 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | board: 19 | # Each element in the sequence produces a matrix job: 20 | - fqbn: arduino:avr:mega 21 | # This suffix will be used to define a unique name for the sketches report artifact. 22 | artifact-name-suffix: arduino-avr-mega 23 | family-name: arduino:avr 24 | platform: | 25 | - name: arduino:avr 26 | compile-flags: | 27 | - --build-property 28 | - build.extra_flags=-DPLATFORMIO -DUSE_LIBDIVIDE -O3 -ffast-math -fshort-enums -funroll-loops -Wall -Wextra -std=c99 29 | lib_deps: | 30 | - name: Time 31 | - name: SimplyAtomic 32 | - fqbn: teensy:avr:teensy35 33 | artifact-name-suffix: teensy-avr-teensy35 34 | platform: | 35 | - source-url: https://www.pjrc.com/teensy/package_teensy_index.json 36 | name: teensy:avr 37 | compile-flags: | 38 | - --build-property 39 | - build..extra_flags="-Wall" 40 | lib_deps: | 41 | - name: SDfat 42 | - name: SimplyAtomic 43 | - fqbn: teensy:avr:teensy41 44 | artifact-name-suffix: teensy-avr-teensy41 45 | platform: | 46 | - source-url: https://www.pjrc.com/teensy/package_teensy_index.json 47 | name: teensy:avr 48 | compile-flags: | 49 | - --build-property 50 | - build..extra_flags="-Wall" 51 | lib_deps: | 52 | - name: SDfat 53 | - name: SimplyAtomic 54 | - fqbn: STMicroelectronics:stm32:GenF4 55 | artifact-name-suffix: stm32-avr-stm32f4 56 | platform: | 57 | - source-url: https://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.json 58 | name: STMicroelectronics:stm32 59 | version: 2.7.1 60 | compile-flags: | 61 | - --build-property 62 | - build.extra_flags=-DUSE_LIBDIVIDE -DUSBCON -DUSBD_USE_CDC -DHAL_PCD_MODULE_ENABLED -DHAL_CAN_MODULE_ENABLED -DSERIAL_TX_BUFFER_SIZE=128 -DSERIAL_RX_BUFFER_SIZE=128 -std=gnu++11 -UBOARD_MAX_IO_PINS 63 | lib_deps: | 64 | - name: "STM32duino RTC" 65 | version: 1.2.0 66 | - name: SdFat 67 | - name: SimplyAtomic 68 | 69 | steps: 70 | - name: Checkout repository 71 | uses: actions/checkout@v4 72 | 73 | - name: Compile platforms 74 | uses: arduino/compile-sketches@v1.1.2 75 | with: 76 | fqbn: ${{ matrix.board.fqbn }} 77 | enable-deltas-report: true 78 | sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} 79 | github-token: ${{ secrets.GITHUB_TOKEN }} 80 | sketch-paths: | 81 | - speeduino/speeduino.ino 82 | platforms: | 83 | ${{ matrix.board.platform }} 84 | cli-compile-flags: | 85 | ${{ matrix.board.compile-flags }} 86 | libraries: | 87 | ${{ matrix.board.lib_deps }} 88 | 89 | # This step is needed to pass the size data to the report job. 90 | - name: Upload sketches report to workflow artifact 91 | uses: actions/upload-artifact@v4 92 | with: 93 | name: sketches-report-${{ matrix.board.artifact-name-suffix }} 94 | path: ${{ env.SKETCHES_REPORTS_PATH }} -------------------------------------------------------------------------------- /speeduino/SD_logger.h: -------------------------------------------------------------------------------- 1 | #ifndef SD_LOGGER_H 2 | #define SD_LOGGER_H 3 | 4 | #ifdef SD_LOGGING 5 | 6 | #ifdef __SD_H__ 7 | #include 8 | #else 9 | #include "SdFat.h" 10 | #endif 11 | #include "RingBuf.h" 12 | 13 | 14 | #define SD_STATUS_OFF 0 /**< SD system is inactive. FS and file remain closed */ 15 | #define SD_STATUS_READY 1 /**< Card is present and ready, but a log session has not commenced */ 16 | #define SD_STATUS_ACTIVE 2 /**< Log session commenced */ 17 | #define SD_STATUS_ERROR_NO_CARD 3 /**< No SD card found when attempting to open file */ 18 | #define SD_STATUS_ERROR_NO_FS 4 /**< No filesystem found when attempting to open file */ 19 | #define SD_STATUS_ERROR_NO_WRITE 5 /**< Card and filesystem found, however file creation failed due to no write access */ 20 | #define SD_STATUS_ERROR_NO_SPACE 6 /**< File could not be preallocated as there is not enough space on card */ 21 | #define SD_STATUS_ERROR_WRITE_FAIL 7 /**< Log file created and opened, but a sector write failed during logging */ 22 | #define SD_STATUS_ERROR_FORMAT_FAIL 8 /**< Attempted formatting of SD card failed */ 23 | 24 | #define SD_STATUS_CARD_PRESENT 0 //0=no card, 1=card present 25 | #define SD_STATUS_CARD_TYPE 1 //0=SD, 1=SDHC 26 | #define SD_STATUS_CARD_READY 2 //0=not ready, 1=ready 27 | #define SD_STATUS_CARD_LOGGING 3 //0=not logging, 1=logging 28 | #define SD_STATUS_CARD_ERROR 4 //0=no error, 1=error 29 | #define SD_STATUS_CARD_VERSION 5 //0=1.x, 1=2.x 30 | #define SD_STATUS_CARD_FS 6 //0=no FAT16, 1=FAT32 31 | #define SD_STATUS_CARD_UNUSED 7 //0=normal, 1=unused 32 | 33 | 34 | #define SD_SECTOR_SIZE 512 // Standard SD sector size 35 | 36 | #if defined CORE_TEENSY 37 | #define SD_CS_PIN BUILTIN_SDCARD 38 | #elif defined CORE_STM32 39 | #define SD_CS_PIN PD2 //CS pin can be pretty much anything, but PD2 is one of the ones left unused from SDIO pins. 40 | #else 41 | #define SD_CS_PIN 10 //This is a made up value for now 42 | #endif 43 | 44 | #define SD_LOG_NUM_FIELDS 91 /**< The number of fields that are in the log. This is always smaller than the entry size due to some fields being 2 bytes */ 45 | #ifndef UNIT_TEST // Scope guard for unit testing 46 | #define SD_LOG_ENTRY_SIZE 127 /**< The size of the live data packet used by the SD card.*/ 47 | #else 48 | #define SD_LOG_ENTRY_SIZE 1 /**< The size of the live data packet used by the SD card.*/ 49 | #endif 50 | 51 | #define SD_LOG_FILE_SIZE 10000000 //Default 10mb file size 52 | #define MAX_LOG_FILES 9999 53 | #define LOG_FILE_PREFIX "SPD_" 54 | #define LOG_FILE_EXTENSION "csv" 55 | #define SD_LOG_ENTRY_TOTAL_BYTES (SD_LOG_ENTRY_SIZE + SD_LOG_NUM_FIELDS + 1) //The total size of each SD log entry in bytes. This is the size of the data packet + 1 comma for each field + 1 for the newline character 56 | #define RING_BUF_CAPACITY (SD_LOG_ENTRY_TOTAL_BYTES * 10) //Allow for 10 entries in the ringbuffer. Will need tuning 57 | 58 | /* 59 | Standard FAT16/32 60 | SdFs sd; 61 | FsFile logFile; 62 | RingBuf rb; 63 | */ 64 | //ExFat 65 | extern SdExFat sd; 66 | extern ExFile logFile; 67 | extern RingBuf rb; 68 | 69 | extern uint8_t SD_status; 70 | extern uint16_t currentLogFileNumber; 71 | extern bool manualLogActive; 72 | 73 | void initSD(); 74 | void writeSDLogEntry(); 75 | void writetSDLogHeader(); 76 | void beginSDLogging(); 77 | void endSDLogging(); 78 | void syncSDLog(); 79 | void setTS_SD_status(); 80 | void formatExFat(); 81 | void deleteLogFile(char, char, char, char); 82 | bool createLogFile(); 83 | void dateTime(uint16_t*, uint16_t*, uint8_t*); //Used for timestamping with RTC 84 | uint16_t getNextSDLogFileNumber(); 85 | bool getSDLogFileDetails(uint8_t* , uint16_t); 86 | void readSDSectors(uint8_t*, uint32_t, uint16_t); 87 | uint32_t sectorCount(); 88 | 89 | 90 | 91 | #endif //SD_LOGGING 92 | #endif //SD_LOGGER_H 93 | -------------------------------------------------------------------------------- /speeduino/comms_legacy.h: -------------------------------------------------------------------------------- 1 | /** \file comms.h 2 | * @brief File for handling all serial requests 3 | * @author Josh Stewart 4 | * 5 | * This file contains all the functions associated with serial comms. 6 | * This includes sending of live data, sending/receiving current page data, sending CRC values of pages, receiving sensor calibration data etc 7 | * 8 | */ 9 | 10 | #ifndef COMMS_LEGACY_H 11 | #define COMMS_LEGACY_H 12 | 13 | /** \enum SerialStatus 14 | * @brief The current state of serial communication 15 | * */ 16 | enum SerialStatus { 17 | /** No serial comms is in progress */ 18 | SERIAL_INACTIVE, 19 | /** A partial write is in progress. */ 20 | SERIAL_TRANSMIT_INPROGRESS, 21 | /** A partial write is in progress (legacy send). */ 22 | SERIAL_TRANSMIT_INPROGRESS_LEGACY, 23 | /** We are part way through transmitting the tooth log */ 24 | SERIAL_TRANSMIT_TOOTH_INPROGRESS, 25 | /** We are part way through transmitting the tooth log (legacy send) */ 26 | SERIAL_TRANSMIT_TOOTH_INPROGRESS_LEGACY, 27 | /** We are part way through transmitting the composite log */ 28 | SERIAL_TRANSMIT_COMPOSITE_INPROGRESS, 29 | /** We are part way through transmitting the composite log (legacy send) */ 30 | SERIAL_TRANSMIT_COMPOSITE_INPROGRESS_LEGACY, 31 | /** Whether or not a serial request has only been partially received. 32 | * This occurs when a the length has been received in the serial buffer, 33 | * but not all of the payload or CRC has yet been received. 34 | * 35 | * Expectation is that ::serialReceive is called until the status reverts 36 | * to SERIAL_INACTIVE 37 | */ 38 | SERIAL_RECEIVE_INPROGRESS, 39 | /** We are part way through processing a legacy serial commang: call ::serialReceive */ 40 | SERIAL_COMMAND_INPROGRESS_LEGACY, 41 | }; 42 | /** @brief Current status of serial comms. */ 43 | extern SerialStatus serialStatusFlag; 44 | extern SerialStatus serialSecondaryStatusFlag; 45 | 46 | /** 47 | * @brief Is a serial write in progress? 48 | * 49 | * Expectation is that ::serialTransmit is called until this 50 | * returns false 51 | */ 52 | inline bool serialTransmitInProgress(void) { 53 | return serialStatusFlag==SERIAL_TRANSMIT_INPROGRESS 54 | || serialStatusFlag==SERIAL_TRANSMIT_INPROGRESS_LEGACY 55 | || serialStatusFlag==SERIAL_TRANSMIT_TOOTH_INPROGRESS 56 | || serialStatusFlag==SERIAL_TRANSMIT_TOOTH_INPROGRESS_LEGACY 57 | || serialStatusFlag==SERIAL_TRANSMIT_COMPOSITE_INPROGRESS 58 | || serialStatusFlag==SERIAL_TRANSMIT_COMPOSITE_INPROGRESS_LEGACY; 59 | } 60 | 61 | /** 62 | * @brief Is a non-blocking serial receive operation in progress? 63 | * 64 | * Expectation is the ::serialReceive is called until this 65 | * returns false. 66 | */ 67 | inline bool serialRecieveInProgress(void) { 68 | return serialStatusFlag==SERIAL_RECEIVE_INPROGRESS 69 | || serialStatusFlag==SERIAL_COMMAND_INPROGRESS_LEGACY; 70 | } 71 | 72 | extern bool firstCommsRequest; /**< The number of times the A command has been issued. This is used to track whether a reset has recently been performed on the controller */ 73 | extern byte logItemsTransmitted; 74 | extern byte inProgressLength; 75 | 76 | void legacySerialCommand(void);//This is the heart of the Command Line Interpreter. All that needed to be done was to make it human readable. 77 | void legacySerialHandler(byte cmd, Stream &targetPort, SerialStatus &targetStatusFlag); 78 | void sendValues(uint16_t offset, uint16_t packetLength, byte cmd, Stream &targetPort, SerialStatus &targetStatusFlag); 79 | void sendValues(uint16_t offset, uint16_t packetLength, byte cmd, Stream &targetPort, SerialStatus &targetStatusFlag, uint8_t (*logFunction)(uint16_t)); 80 | void sendValuesLegacy(void); 81 | void sendPage(void); 82 | void sendPageASCII(void); 83 | void receiveCalibration(byte tableID); 84 | void testComm(void); 85 | void sendToothLog_legacy(byte startOffset); 86 | void sendCompositeLog_legacy(byte startOffset); 87 | 88 | #endif // COMMS_LEGACY_H 89 | -------------------------------------------------------------------------------- /speeduino/src/HardwareTimers/HardwareTimer.h_test: -------------------------------------------------------------------------------- 1 | #ifndef HARDWARETIMER_H_ 2 | #define HARDWARETIMER_H_ 3 | 4 | #include "Arduino.h" 5 | #include "timer.h" 6 | //#include "stm32_gpio_af.h" 7 | 8 | typedef enum { 9 | //libmaple: // HAL compatible 10 | TIMER_DISABLED, 11 | TIMER_PWM, // == TIM_OCMODE_PWM1 12 | TIMER_OUTPUT_COMPARE, // == TIM_OCMODE_TIMING no output, useful for only-interrupt 13 | 14 | //other: 15 | TIMER_OUTPUT_COMPARE_ACTIVE, // == TIM_OCMODE_ACTIVE pin is set high when counter == channel compare 16 | TIMER_OUTPUT_COMPARE_INACTIVE, // == TIM_OCMODE_INACTIVE pin is set low when counter == channel compare 17 | TIMER_OUTPUT_COMPARE_TOGGLE, // == TIM_OCMODE_TOGGLE pin toggles when counter == channel compare 18 | TIMER_OUTPUT_COMPARE_PWM1, // == TIM_OCMODE_PWM1 pin high when counter < channel compare, low otherwise 19 | TIMER_OUTPUT_COMPARE_PWM2, // == TIM_OCMODE_PWM2 pin low when counter < channel compare, high otherwise 20 | TIMER_OUTPUT_COMPARE_FORCED_ACTIVE, // == TIM_OCMODE_FORCED_ACTIVE pin always high 21 | TIMER_OUTPUT_COMPARE_FORCED_INACTIVE, // == TIM_OCMODE_FORCED_INACTIVE pin always low 22 | 23 | //Input capture 24 | TIMER_INPUT_CAPTURE_RISING, // == TIM_INPUTCHANNELPOLARITY_RISING 25 | TIMER_INPUT_CAPTURE_FALLING, // == TIM_INPUTCHANNELPOLARITY_FALLING 26 | 27 | //PWM input capture on channel 1 + channel 2 28 | //TIMER_INPUT_CAPTURE_PWM, // == TIM_INPUTCHANNELPOLARITY_RISING (channel 1) + TIM_INPUTCHANNELPOLARITY_FALLING (channel 2) 29 | 30 | //Encoder mode 31 | //TIMER_ENCODER // == TIM_ENCODERMODE_TI1 32 | } TIMER_MODES; 33 | 34 | #define TIMER_DEFAULT_PIN 0xFF 35 | 36 | class HardwareTimer { 37 | public: 38 | HardwareTimer(TIM_TypeDef* timer); 39 | 40 | void pause(void); 41 | 42 | void resume(void); 43 | 44 | uint32_t getPrescaleFactor(); 45 | 46 | void setPrescaleFactor(uint32_t factor); 47 | 48 | uint32_t getOverflow(); 49 | 50 | void setOverflow(uint32_t val); 51 | 52 | uint32_t getCount(void); 53 | 54 | void setCount(uint32_t val); 55 | 56 | uint32_t setPeriod(uint32_t microseconds); 57 | 58 | void setMode(int channel, TIMER_MODES mode, uint8_t pin = TIMER_DEFAULT_PIN); 59 | 60 | uint32_t getCompare(int channel); 61 | 62 | void setCompare(int channel, uint32_t compare); 63 | 64 | //Add interrupt to period update 65 | void attachInterrupt(void (*handler)(void)); 66 | 67 | void detachInterrupt(); 68 | 69 | //Add interrupt to channel 70 | void attachInterrupt(int channel, void (*handler)(void)); 71 | 72 | void detachInterrupt(int channel); 73 | 74 | void refresh(void); 75 | 76 | uint32_t getBaseFrequency(); 77 | 78 | TIM_HandleTypeDef handle = {0}; 79 | 80 | TIM_OC_InitTypeDef channelOC[4]; 81 | 82 | TIM_IC_InitTypeDef channelIC[4]; 83 | 84 | //Callbacks: 0 for update, 1-4 for channels 85 | void (*callbacks[5])(void); 86 | 87 | stimer_t _timer; 88 | 89 | // //const stm32_tim_pin_list_type *tim_pin_list; 90 | 91 | // int tim_pin_list_size; 92 | 93 | private: 94 | void resumeChannel(int channel, int timChannel); 95 | 96 | }; 97 | 98 | #ifdef TIM1 99 | extern HardwareTimer Timer1; 100 | #endif 101 | 102 | #ifdef TIM2 103 | extern HardwareTimer Timer2; 104 | #endif 105 | 106 | #ifdef TIM3 107 | extern HardwareTimer Timer3; 108 | #endif 109 | 110 | #ifdef TIM4 111 | extern HardwareTimer Timer4; 112 | #endif 113 | 114 | #ifdef TIM5 115 | extern HardwareTimer Timer5; 116 | #endif 117 | 118 | #ifdef TIM8 119 | extern HardwareTimer Timer8; 120 | #endif 121 | 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /speeduino/src/libdivide/constant_fast_div.h: -------------------------------------------------------------------------------- 1 | /* 2 | * When dividing by a known compile time constant, the division can be replaced 3 | * by a multiply+shift operation. GCC will do this automatically, 4 | * *BUT ONLY FOR DIVISION OF REGISTER-WIDTH OR NARROWER*. 5 | * 6 | * So on an 8-bit system, 16-bit divides will *NOT* be optimised. 7 | * 8 | * The macros here manually apply the multiply+shift operation for 16-bit numbers. 9 | * 10 | * Testing on an AtMega2560, -O3 optimizations: 11 | * Performance improvement of 85% to 90%+ speed up (division by non-powers of 2) 12 | * Zero increase in RAM usage 13 | * Average of 25 bytes Flash used per call site 14 | * Be careful calling this in a loop with aggressive loop unrolling! 15 | * 16 | * Note: testing of the multiply+shift technique on 8-bit division showed a 17 | * slight slow down over native code on AtMega2560. So the 8 bit equivalent 18 | * macros have not been included 19 | */ 20 | 21 | #pragma once 22 | #include "libdivide.h" 23 | #include "u16_ldparams.h" 24 | #include "s16_ldparams.h" 25 | 26 | #define CAT_HELPER(a, b) a ## b 27 | #define CONCAT(A, B) CAT_HELPER(A, B) 28 | 29 | // GCC will optimise division by a power of 2 30 | // So allow that. 31 | #define S16_ISPOW2_NEG(denom) \ 32 | (denom==-2 || \ 33 | denom==-4 || \ 34 | denom==-8 || \ 35 | denom==-16 || \ 36 | denom==-32 || \ 37 | denom==-64 || \ 38 | denom==-128 || \ 39 | denom==-256 || \ 40 | denom==-512 || \ 41 | denom==-1024 || \ 42 | denom==-2048 || \ 43 | denom==-4096 || \ 44 | denom==-8192 || \ 45 | denom==-16384) 46 | #define S16_ISPOW2_POS(denom) \ 47 | (denom==2 || \ 48 | denom==4 || \ 49 | denom==8 || \ 50 | denom==16 || \ 51 | denom==32 || \ 52 | denom==64 || \ 53 | denom==128 || \ 54 | denom==256 || \ 55 | denom==512 || \ 56 | denom==1024 || \ 57 | denom==2048 || \ 58 | denom==4096 || \ 59 | denom==8192 || \ 60 | denom==16384) 61 | #define U16_ISPOW2(denom) (S16_ISPOW2_POS(denom) || denom==32768) 62 | #define S16_ISPOW2(denom) (S16_ISPOW2_POS(denom) || S16_ISPOW2_NEG(denom)) 63 | 64 | // Apply the libdivide namespace if necessary 65 | #ifdef __cplusplus 66 | #define LIB_DIV_NAMESPACE libdivide:: 67 | #else 68 | #define LIB_DIV_NAMESPACE 69 | #endif 70 | 71 | /* 72 | * Wrapper for *unsigned* 16-bit DIVISION. The divisor must be a compile time 73 | * constant. 74 | * E.g. FAST_DIV16U(value, 100) 75 | */ 76 | #define U16_MAGIC(d) CONCAT(CONCAT(U16LD_DENOM_, d), _MAGIC) 77 | #define U16_MORE(d) CONCAT(CONCAT(U16LD_DENOM_, d), _MORE) 78 | #define FAST_DIV16U(a, d) (U16_ISPOW2(d) ? a/d : LIB_DIV_NAMESPACE libdivide_u16_do_raw(a, U16_MAGIC(d), U16_MORE(d))) 79 | 80 | /* 81 | * Wrapper for *signed* 16-bit DIVISION by a *POSITIVE* compile time constant. 82 | * E.g. FAST_DIV16(-value, 777) 83 | * 84 | * This only works for positive parameters :-( 85 | * A negative number results in a hyphen in the macro name, which is not allowed 86 | */ 87 | #define S16_MAGIC(d) CONCAT(CONCAT(S16LD_DENOM_, d), _MAGIC) 88 | #define S16_MORE(d) CONCAT(CONCAT(S16LD_DENOM_, d), _MORE) 89 | #define FAST_DIV16(a, d) (S16_ISPOW2(d) ? a/d : LIB_DIV_NAMESPACE libdivide_s16_do_raw(a, S16_MAGIC(d), S16_MORE(d))) 90 | 91 | /* 92 | * Wrapper for *signed* 16-bit DIVISION by a *NEGATIVE* compile time constant. 93 | * E.g. FAST_DIV16_NEG(-value, 777) // <-- It's converted to negative. Really. 94 | * 95 | * This only works for positive parameters :-( 96 | * A negative number results in a hyphen in the macro name, which is not allowed 97 | */ 98 | #define S16_MAGIC_NEG(d) CONCAT(CONCAT(S16LD_DENOM_MINUS_, d), _MAGIC) 99 | #define S16_MORE_NEG(d) CONCAT(CONCAT(S16LD_DENOM_MINUS_, d), _MORE) 100 | #define FAST_DIV16_NEG(a, d) (S16_ISPOW2(d) ? a/-d : LIB_DIV_NAMESPACE libdivide_s16_do_raw(a, S16_MAGIC_NEG(d), S16_MORE_NEG(d))) 101 | 102 | /* 103 | * Wrapper for *unsigned* 16-bit MODULUS. The divisor must be a compile time 104 | * constant. 105 | * E.g. FAST_MOD16U(value, 6) 106 | */ 107 | #define FAST_MOD16U(a, d) (a - (FAST_DIV16U(a, d) * d)) 108 | -------------------------------------------------------------------------------- /test/test_schedules/test_status_initial_off.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../test_utils.h" 4 | #include "scheduler.h" 5 | 6 | void test_status_initial_off_inj1(void) 7 | { 8 | initialiseSchedulers(); 9 | TEST_ASSERT_EQUAL(OFF, fuelSchedule1.Status); 10 | } 11 | 12 | void test_status_initial_off_inj2(void) 13 | { 14 | initialiseSchedulers(); 15 | TEST_ASSERT_EQUAL(OFF, fuelSchedule2.Status); 16 | } 17 | 18 | void test_status_initial_off_inj3(void) 19 | { 20 | initialiseSchedulers(); 21 | TEST_ASSERT_EQUAL(OFF, fuelSchedule3.Status); 22 | } 23 | 24 | void test_status_initial_off_inj4(void) 25 | { 26 | initialiseSchedulers(); 27 | TEST_ASSERT_EQUAL(OFF, fuelSchedule4.Status); 28 | } 29 | 30 | #if INJ_CHANNELS >= 5 31 | void test_status_initial_off_inj5(void) 32 | { 33 | initialiseSchedulers(); 34 | TEST_ASSERT_EQUAL(OFF, fuelSchedule5.Status); 35 | } 36 | #endif 37 | 38 | #if INJ_CHANNELS >= 6 39 | void test_status_initial_off_inj6(void) 40 | { 41 | initialiseSchedulers(); 42 | TEST_ASSERT_EQUAL(OFF, fuelSchedule6.Status); 43 | } 44 | #endif 45 | 46 | #if INJ_CHANNELS >= 7 47 | void test_status_initial_off_inj7(void) 48 | { 49 | initialiseSchedulers(); 50 | TEST_ASSERT_EQUAL(OFF, fuelSchedule7.Status); 51 | } 52 | #endif 53 | 54 | #if INJ_CHANNELS >= 8 55 | void test_status_initial_off_inj8(void) 56 | { 57 | initialiseSchedulers(); 58 | TEST_ASSERT_EQUAL(OFF, fuelSchedule8.Status); 59 | } 60 | #endif 61 | 62 | 63 | void test_status_initial_off_ign1(void) 64 | { 65 | initialiseSchedulers(); 66 | TEST_ASSERT_EQUAL(OFF, ignitionSchedule1.Status); 67 | } 68 | 69 | void test_status_initial_off_ign2(void) 70 | { 71 | initialiseSchedulers(); 72 | TEST_ASSERT_EQUAL(OFF, ignitionSchedule2.Status); 73 | } 74 | 75 | void test_status_initial_off_ign3(void) 76 | { 77 | initialiseSchedulers(); 78 | TEST_ASSERT_EQUAL(OFF, ignitionSchedule3.Status); 79 | } 80 | 81 | void test_status_initial_off_ign4(void) 82 | { 83 | initialiseSchedulers(); 84 | TEST_ASSERT_EQUAL(OFF, ignitionSchedule4.Status); 85 | } 86 | 87 | #if IGN_CHANNELS >= 5 88 | void test_status_initial_off_ign5(void) 89 | { 90 | initialiseSchedulers(); 91 | TEST_ASSERT_EQUAL(OFF, ignitionSchedule5.Status); 92 | } 93 | #endif 94 | 95 | #if IGN_CHANNELS >= 6 96 | void test_status_initial_off_ign6(void) 97 | { 98 | initialiseSchedulers(); 99 | TEST_ASSERT_EQUAL(OFF, ignitionSchedule6.Status); 100 | } 101 | #endif 102 | 103 | #if IGN_CHANNELS >= 7 104 | void test_status_initial_off_ign7(void) 105 | { 106 | initialiseSchedulers(); 107 | TEST_ASSERT_EQUAL(OFF, ignitionSchedule7.Status); 108 | } 109 | #endif 110 | 111 | #if IGN_CHANNELS >= 8 112 | void test_status_initial_off_ign8(void) 113 | { 114 | initialiseSchedulers(); 115 | TEST_ASSERT_EQUAL(OFF, ignitionSchedule8.Status); 116 | } 117 | #endif 118 | 119 | void test_status_initial_off(void) 120 | { 121 | SET_UNITY_FILENAME() { 122 | 123 | RUN_TEST(test_status_initial_off_inj1); 124 | RUN_TEST(test_status_initial_off_inj2); 125 | RUN_TEST(test_status_initial_off_inj3); 126 | RUN_TEST(test_status_initial_off_inj4); 127 | #if INJ_CHANNELS >= 5 128 | RUN_TEST(test_status_initial_off_inj5); 129 | #endif 130 | #if INJ_CHANNELS >= 6 131 | RUN_TEST(test_status_initial_off_inj6); 132 | #endif 133 | #if INJ_CHANNELS >= 7 134 | RUN_TEST(test_status_initial_off_inj7); 135 | #endif 136 | #if INJ_CHANNELS >= 8 137 | RUN_TEST(test_status_initial_off_inj8); 138 | #endif 139 | 140 | RUN_TEST(test_status_initial_off_ign1); 141 | RUN_TEST(test_status_initial_off_ign2); 142 | RUN_TEST(test_status_initial_off_ign3); 143 | RUN_TEST(test_status_initial_off_ign4); 144 | #if IGN_CHANNELS >= 5 145 | RUN_TEST(test_status_initial_off_ign5); 146 | #endif 147 | #if IGN_CHANNELS >= 6 148 | RUN_TEST(test_status_initial_off_ign6); 149 | #endif 150 | #if IGN_CHANNELS >= 7 151 | RUN_TEST(test_status_initial_off_ign7); 152 | #endif 153 | #if IGN_CHANNELS >= 8 154 | RUN_TEST(test_status_initial_off_ign8); 155 | #endif 156 | } 157 | } -------------------------------------------------------------------------------- /speeduino/crankMaths.cpp: -------------------------------------------------------------------------------- 1 | #include "globals.h" 2 | #include "crankMaths.h" 3 | #include "bit_shifts.h" 4 | 5 | #define SECOND_DERIV_ENABLED 0 6 | 7 | //These are only part of the experimental 2nd deriv calcs 8 | #if SECOND_DERIV_ENABLED!=0 9 | byte deltaToothCount = 0; //The last tooth that was used with the deltaV calc 10 | int rpmDelta; 11 | #endif 12 | 13 | typedef uint32_t UQ24X8_t; 14 | static constexpr uint8_t UQ24X8_Shift = 8U; 15 | 16 | /** @brief uS per degree at current RPM in UQ24.8 fixed point */ 17 | static UQ24X8_t microsPerDegree; 18 | static constexpr uint8_t microsPerDegree_Shift = UQ24X8_Shift; 19 | 20 | typedef uint16_t UQ1X15_t; 21 | static constexpr uint8_t UQ1X15_Shift = 15U; 22 | 23 | /** @brief Degrees per uS in UQ1.15 fixed point. 24 | * 25 | * Ranges from 8 (0.000246) at MIN_RPM to 3542 (0.108) at MAX_RPM 26 | */ 27 | static UQ1X15_t degreesPerMicro; 28 | static constexpr uint8_t degreesPerMicro_Shift = UQ1X15_Shift; 29 | 30 | void setAngleConverterRevolutionTime(uint32_t revolutionTime) { 31 | microsPerDegree = div360(lshift(revolutionTime)); 32 | degreesPerMicro = (uint16_t)UDIV_ROUND_CLOSEST(lshift(UINT32_C(360)), revolutionTime, uint32_t); 33 | } 34 | 35 | uint32_t angleToTimeMicroSecPerDegree(uint16_t angle) { 36 | UQ24X8_t micros = (uint32_t)angle * (uint32_t)microsPerDegree; 37 | return rshift_round(micros); 38 | } 39 | 40 | uint16_t timeToAngleDegPerMicroSec(uint32_t time) { 41 | uint32_t degFixed = time * (uint32_t)degreesPerMicro; 42 | return rshift_round(degFixed); 43 | } 44 | 45 | #if SECOND_DERIV_ENABLED!=0 46 | void doCrankSpeedCalcs(void) 47 | { 48 | //******************************************************** 49 | //How fast are we going? Need to know how long (uS) it will take to get from one tooth to the next. We then use that to estimate how far we are between the last tooth and the next one 50 | //We use a 1st Deriv acceleration prediction, but only when there is an even spacing between primary sensor teeth 51 | //Any decoder that has uneven spacing has its triggerToothAngle set to 0 52 | //THIS IS CURRENTLY DISABLED FOR ALL DECODERS! It needs more work. 53 | if( (BIT_CHECK(decoderState, BIT_DECODER_2ND_DERIV)) && (toothHistoryIndex >= 3) && (currentStatus.RPM < 2000) ) //toothHistoryIndex must be greater than or equal to 3 as we need the last 3 entries. Currently this mode only runs below 3000 rpm 54 | { 55 | //Only recalculate deltaV if the tooth has changed since last time (DeltaV stays the same until the next tooth) 56 | //if (deltaToothCount != toothCurrentCount) 57 | { 58 | deltaToothCount = toothCurrentCount; 59 | int angle1, angle2; //These represent the crank angles that are travelled for the last 2 pulses 60 | if(configPage4.TrigPattern == 4) 61 | { 62 | //Special case for 70/110 pattern on 4g63 63 | angle2 = triggerToothAngle; //Angle 2 is the most recent 64 | if (angle2 == 70) { angle1 = 110; } 65 | else { angle1 = 70; } 66 | } 67 | else if(configPage4.TrigPattern == 0) 68 | { 69 | //Special case for missing tooth decoder where the missing tooth was one of the last 2 seen 70 | if(toothCurrentCount == 1) { angle2 = 2*triggerToothAngle; angle1 = triggerToothAngle; } 71 | else if(toothCurrentCount == 2) { angle1 = 2*triggerToothAngle; angle2 = triggerToothAngle; } 72 | else { angle1 = triggerToothAngle; angle2 = triggerToothAngle; } 73 | } 74 | else { angle1 = triggerToothAngle; angle2 = triggerToothAngle; } 75 | 76 | uint32_t toothDeltaV = (MICROS_PER_SEC * angle2 / toothHistory[toothHistoryIndex]) - (MICROS_PER_SEC * angle1 / toothHistory[toothHistoryIndex-1]); 77 | uint32_t toothDeltaT = toothHistory[toothHistoryIndex]; 78 | //long timeToLastTooth = micros() - toothLastToothTime; 79 | 80 | rpmDelta = lshift<10>(toothDeltaV) / (6 * toothDeltaT); 81 | } 82 | 83 | 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 84 | } 85 | } 86 | #endif -------------------------------------------------------------------------------- /test/test_math/test_shifts.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "bit_shifts.h" 4 | #include "maths.h" 5 | #include "../timer.hpp" 6 | #include "../test_utils.h" 7 | 8 | template 9 | static void test_lshift(void) { 10 | uint32_t value = 33333; 11 | char szMsg[16]; 12 | sprintf(szMsg, "%" PRIu8, shiftDistance); 13 | TEST_ASSERT_EQUAL_MESSAGE(value << shiftDistance, lshift(value), szMsg); 14 | } 15 | 16 | static void test_LShift() 17 | { 18 | test_lshift<1U>(); 19 | test_lshift<2U>(); 20 | test_lshift<3U>(); 21 | test_lshift<4U>(); 22 | test_lshift<5U>(); 23 | test_lshift<6U>(); 24 | test_lshift<7U>(); 25 | test_lshift<8U>(); 26 | test_lshift<9U>(); 27 | test_lshift<10U>(); 28 | test_lshift<11U>(); 29 | test_lshift<12U>(); 30 | test_lshift<13U>(); 31 | test_lshift<14U>(); 32 | test_lshift<15U>(); 33 | } 34 | 35 | template 36 | static void test_rshift(void) { 37 | uint32_t value = 33333; 38 | char szMsg[16]; 39 | sprintf(szMsg, "%" PRIu8, shiftDistance); 40 | TEST_ASSERT_EQUAL_MESSAGE(value >> shiftDistance, rshift(value), szMsg); 41 | } 42 | 43 | void test_RShift() 44 | { 45 | test_rshift<1U>(); 46 | test_rshift<2U>(); 47 | test_rshift<3U>(); 48 | test_rshift<4U>(); 49 | test_rshift<5U>(); 50 | test_rshift<6U>(); 51 | test_rshift<7U>(); 52 | test_rshift<8U>(); 53 | test_rshift<9U>(); 54 | test_rshift<10U>(); 55 | test_rshift<11U>(); 56 | test_rshift<12U>(); 57 | test_rshift<13U>(); 58 | test_rshift<14U>(); 59 | test_rshift<15U>(); 60 | } 61 | 62 | static uint32_t seedValue; 63 | 64 | // Force no inline, or compiler will optimize shifts away 65 | // (which it won't do in normal operaton when the left shift operand is unknown at compile time.) 66 | static void __attribute__((noinline)) nativeTest(uint8_t index, uint32_t &checkSum) { 67 | if (index==1U) { checkSum = seedValue; } 68 | if (index==4U) { checkSum += checkSum >> 4U; } 69 | if (index==5U) { checkSum += checkSum >> 5U; } 70 | if (index==6U) { checkSum += checkSum >> 6U; } 71 | if (index==7U) { checkSum += checkSum >> 7U; } 72 | if (index==9U) { checkSum += checkSum >> 9U; } 73 | if (index==10U) { checkSum += checkSum >> 10U; } 74 | if (index==11U) { checkSum += checkSum >> 11U; } 75 | if (index==12U) { checkSum += checkSum >> 12U; } 76 | if (index==13U) { checkSum += checkSum >> 13U; } 77 | if (index==14U) { checkSum += checkSum >> 14U; } 78 | if (index==15U) { checkSum += checkSum >> 15U; } 79 | }; 80 | static void __attribute__((noinline)) optimizedTest(uint8_t index, uint32_t &checkSum) { 81 | if (index==1U) { checkSum = seedValue; } 82 | if (index==4U) { checkSum += rshift<4U>(checkSum); } 83 | if (index==5U) { checkSum += rshift<5U>(checkSum); } 84 | if (index==6U) { checkSum += rshift<6U>(checkSum); } 85 | if (index==7U) { checkSum += rshift<7U>(checkSum); } 86 | if (index==9U) { checkSum += rshift<9U>(checkSum); } 87 | if (index==10U) { checkSum += rshift<10U>(checkSum); } 88 | if (index==11U) { checkSum += rshift<11U>(checkSum); } 89 | if (index==12U) { checkSum += rshift<12U>(checkSum); } 90 | if (index==13U) { checkSum += rshift<13U>(checkSum); } 91 | if (index==14U) { checkSum += rshift<14U>(checkSum); } 92 | if (index==15U) { checkSum += rshift<15U>(checkSum); } 93 | }; 94 | 95 | static void test_rshift_perf(void) { 96 | #if USE_OPTIMIZED_SHIFTS==1 97 | constexpr uint16_t iters = 128; 98 | constexpr uint8_t start_index = 1; 99 | constexpr uint8_t end_index = 16; 100 | constexpr uint8_t step = 1; 101 | 102 | seedValue = rand(); 103 | 104 | TEST_MESSAGE("rshift "); 105 | auto comparison = compare_executiontime(iters, start_index, end_index, step, nativeTest, optimizedTest); 106 | 107 | // This must be here to force the compiler to run the loops above 108 | TEST_ASSERT_EQUAL(comparison.timeA.result, comparison.timeB.result); 109 | 110 | TEST_ASSERT_LESS_THAN(comparison.timeA.durationMicros, comparison.timeB.durationMicros); 111 | #endif 112 | } 113 | 114 | void testBitShift() { 115 | SET_UNITY_FILENAME() { 116 | RUN_TEST(test_LShift); 117 | RUN_TEST(test_RShift); 118 | RUN_TEST(test_rshift_perf); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /speeduino/schedule_calcs.hpp: -------------------------------------------------------------------------------- 1 | // Note that all functions with an underscore prefix are NOT part 2 | // of the public API. They are only here so we can inline them. 3 | 4 | #include "scheduler.h" 5 | #include "crankMaths.h" 6 | #include "maths.h" 7 | #include "timers.h" 8 | 9 | static inline uint16_t calculateInjectorStartAngle(uint16_t pwDegrees, int16_t injChannelDegrees, uint16_t injAngle) 10 | { 11 | // 0<=injAngle<=720° 12 | // 0<=injChannelDegrees<=720° 13 | // 0=0. 21 | while (startAngle>(uint16_t)CRANK_ANGLE_MAX_INJ) { startAngle = startAngle - (uint16_t)CRANK_ANGLE_MAX_INJ; } // Clamp to 0<=startAngle<=CRANK_ANGLE_MAX_INJ 22 | 23 | return startAngle; 24 | } 25 | 26 | static inline uint32_t calculateInjectorTimeout(const FuelSchedule &schedule, int openAngle, int crankAngle) 27 | { 28 | uint32_t tempTimeout = 0; 29 | int16_t delta = openAngle - crankAngle; 30 | 31 | if(delta > 0) { tempTimeout = angleToTimeMicroSecPerDegree((uint16_t)delta); } 32 | else if ( (schedule.Status == RUNNING) || (schedule.Status == OFF)) 33 | { 34 | while(delta < 0) { delta += CRANK_ANGLE_MAX_INJ; } 35 | tempTimeout = angleToTimeMicroSecPerDegree((uint16_t)delta); 36 | } 37 | 38 | return tempTimeout; 39 | } 40 | 41 | static inline void calculateIgnitionAngle(const uint16_t dwellAngle, const uint16_t channelIgnDegrees, int8_t advance, int *pEndAngle, int *pStartAngle) 42 | { 43 | *pEndAngle = (int16_t)(channelIgnDegrees==0U ? (uint16_t)CRANK_ANGLE_MAX_IGN : channelIgnDegrees) - (int16_t)advance; 44 | if(*pEndAngle > CRANK_ANGLE_MAX_IGN) {*pEndAngle -= CRANK_ANGLE_MAX_IGN;} 45 | *pStartAngle = *pEndAngle - dwellAngle; 46 | if(*pStartAngle < 0) {*pStartAngle += CRANK_ANGLE_MAX_IGN;} 47 | } 48 | 49 | static inline void calculateIgnitionTrailingRotary(uint16_t dwellAngle, int rotarySplitDegrees, int leadIgnitionAngle, int *pEndAngle, int *pStartAngle) 50 | { 51 | *pEndAngle = leadIgnitionAngle + rotarySplitDegrees; 52 | *pStartAngle = *pEndAngle - dwellAngle; 53 | if(*pStartAngle > CRANK_ANGLE_MAX_IGN) {*pStartAngle -= CRANK_ANGLE_MAX_IGN;} 54 | if(*pStartAngle < 0) {*pStartAngle += CRANK_ANGLE_MAX_IGN;} 55 | } 56 | 57 | static inline uint32_t _calculateIgnitionTimeout(const IgnitionSchedule &schedule, int16_t startAngle, int16_t crankAngle) 58 | { 59 | int16_t delta = startAngle - crankAngle; 60 | if (delta < 0) 61 | { 62 | if ((schedule.Status == RUNNING) && (delta>-CRANK_ANGLE_MAX_IGN)) 63 | { 64 | // Msut be >0 65 | delta = delta + CRANK_ANGLE_MAX_IGN; 66 | } 67 | else 68 | { 69 | return 0U; 70 | } 71 | } 72 | 73 | return angleToTimeMicroSecPerDegree(delta); 74 | } 75 | 76 | static inline uint16_t _adjustToIgnChannel(int angle, int channelInjDegrees) 77 | { 78 | angle = angle - channelInjDegrees; 79 | if( angle < 0) { return angle + CRANK_ANGLE_MAX_IGN; } 80 | return angle; 81 | } 82 | 83 | static inline uint32_t calculateIgnitionTimeout(const IgnitionSchedule &schedule, int startAngle, int channelIgnDegrees, int crankAngle) 84 | { 85 | if (channelIgnDegrees == 0) 86 | { 87 | return _calculateIgnitionTimeout(schedule, startAngle, crankAngle); 88 | } 89 | return _calculateIgnitionTimeout(schedule, _adjustToIgnChannel(startAngle, channelIgnDegrees), _adjustToIgnChannel(crankAngle, channelIgnDegrees)); 90 | } 91 | 92 | #define MIN_CYCLES_FOR_ENDCOMPARE 6 93 | 94 | inline void adjustCrankAngle(IgnitionSchedule &schedule, int endAngle, int crankAngle) 95 | { 96 | if( (schedule.Status == RUNNING) ) { 97 | SET_COMPARE(schedule.compare, schedule.counter + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (endAngle - crankAngle) ) ) ) ); 98 | } 99 | else if(currentStatus.startRevolutions > MIN_CYCLES_FOR_ENDCOMPARE) { 100 | schedule.endCompare = schedule.counter + uS_TO_TIMER_COMPARE( angleToTimeMicroSecPerDegree( ignitionLimits( (endAngle - crankAngle) ) ) ); 101 | schedule.endScheduleSetByDecoder = true; 102 | } 103 | } --------------------------------------------------------------------------------