diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..9b2614c3 --- /dev/null +++ b/.clang-format @@ -0,0 +1,13 @@ +LineEnding: LF +UseTab: Never +IndentWidth: 4 +ColumnLimit: 120 + +IndentCaseLabels: false +BreakBeforeBraces: Allman +AllowShortIfStatementsOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: true + AcrossComments: false diff --git a/.editorconfig b/.editorconfig index 524cf926..af1c2ff3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,12 +1,12 @@ root = true [*] -end_of_line = crlf +end_of_line = lf insert_final_newline = true charset = utf-8 -indent_style = tab +indent_style = space indent_size = 4 trim_trailing_whitespace = true [*.md] -trim_trailing_whitespace = false \ No newline at end of file +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..f74c3dd1 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Disable git line ending conversion +* -text diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..54eb66e1 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,16 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + args: [--markdown-linebreak-ext=md] + - id: check-added-large-files + - id: mixed-line-ending + args: [--fix=lf] + exclude: '^drawings/' +- repo: https://github.com/pre-commit/mirrors-clang-format + rev: v18.1.8 + hooks: + - id: clang-format diff --git a/README.md b/README.md index b26fa807..47f29255 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ If you find this project useful, consider sending a small [donation](https://www * ⚠️ Unstable on BBS02 controllers! ## Highlights -* ✅ A bit more power without hardware modifications! (max 33A). +* ✅ A bit more power without hardware modifications! (max 33A). * ✅ No upper voltage limit in software, can by default run up to 63V (maximum rating of components). * ✅ Support lower voltage cutoff for use with e.g. 36V battery. * ✅ Smooth Throttle/PAS override. @@ -67,11 +67,11 @@ BBS02A - No idea, not tested, not recommended to try unless you have an already ### TSDZ2 Compatible with TSDZ2A/B using the STM microcontroller (which is nearly all off them). -### Displays and Controller +### Displays and Controller -Only displays with the Bafang display protocol can work. +Only displays with the Bafang display protocol can work. -Also the controllers need to be those, that are officially designed by Bafang, respectively Tongshen. +Also the controllers need to be those, that are officially designed by Bafang, respectively Tongshen. Some shops sell kits with their own controller. diff --git a/code/firmware/.gitignore b/code/firmware/.gitignore new file mode 100644 index 00000000..00a9017c --- /dev/null +++ b/code/firmware/.gitignore @@ -0,0 +1,3 @@ +.vs +.vscode +build diff --git a/code/firmware/CMakeLists.txt b/code/firmware/CMakeLists.txt new file mode 100644 index 00000000..19d55679 --- /dev/null +++ b/code/firmware/CMakeLists.txt @@ -0,0 +1,106 @@ +cmake_minimum_required(VERSION 3.15) + +project(bbs-fw C) + +set(TARGET_CONTROLLER "BBSHD" CACHE STRING "Target controller: BBSHD,BBS02,TSDZ2,TSDZ8") + +if (${TARGET_CONTROLLER} STREQUAL "BBSHD") + add_link_options(--xram-size 3840) +endif() + +if (${TARGET_CONTROLLER} STREQUAL "BBS02") + add_link_options(--xram-size 1792) +endif() + +if (${TARGET_CONTROLLER} STREQUAL "TSDZ2") + add_subdirectory(lib/stm8s) +endif() + +set(SOURCE_FILES + "src/adc.h" + "src/app.c" + "src/app.h" + "src/battery.c" + "src/battery.h" + "src/cfgstore.c" + "src/cfgstore.h" + "src/eeprom.h" + "src/eventlog.c" + "src/eventlog.h" + "src/extcom.c" + "src/extcom.h" + "src/fwconfig.h" + "src/interrupt.h" + "src/lights.h" + "src/main.c" + "src/motor.h" + "src/sensors.h" + "src/system.h" + "src/throttle.c" + "src/throttle.h" + "src/timers.h" + "src/uart.h" + "src/util.h" + "src/version.h" + "src/watchdog.h" +) + +if (${TARGET_CONTROLLER} STREQUAL "BBSHD" OR ${TARGET_CONTROLLER} STREQUAL "BBS02") + list(APPEND SOURCE_FILES + "src/bbsx/adc.c" + "src/bbsx/cpu.h" + "src/bbsx/eeprom.c" + "src/bbsx/interrupt.h" + "src/bbsx/lights.c" + "src/bbsx/motor.c" + "src/bbsx/pins.h" + "src/bbsx/sensors.c" + "src/bbsx/stc15.h" + "src/bbsx/system.c" + "src/bbsx/timers.c" + "src/bbsx/timers.h" + "src/bbsx/uart_motor.h" + "src/bbsx/uart.c" + "src/bbsx/watchdog.c" + ) +endif() + +if (${TARGET_CONTROLLER} STREQUAL "TSDZ2") + list(APPEND SOURCE_FILES + "src/tsdz2/adc.c" + "src/tsdz2/cpu.h" + "src/tsdz2/eeprom.c" + "src/tsdz2/interrupt.h" + "src/tsdz2/lights.c" + "src/tsdz2/motor.c" + "src/tsdz2/pins.h" + "src/tsdz2/sensors.c" + "src/tsdz2/stm8.h" + "src/tsdz2/system.c" + "src/tsdz2/timers.c" + "src/tsdz2/timers.h" + "src/tsdz2/torquesensor.c" + "src/tsdz2/uart.c" + "src/tsdz2/watchdog.c" + ) +endif() + +add_executable(bbs-fw ${SOURCE_FILES}) + +target_compile_definitions(bbs-fw PRIVATE ${TARGET_CONTROLLER}) +target_include_directories(bbs-fw PRIVATE src) + +if (${TARGET_CONTROLLER} STREQUAL "TSDZ2") + target_link_libraries(bbs-fw stm8s) +endif() + + +# Generate hex file (SDCC) +if (${CMAKE_C_COMPILER} STREQUAL "sdcc" OR ${TARGET_CONTROLLER} STREQUAL "BBS02" OR ${TARGET_CONTROLLER} STREQUAL "TSDZ2") + add_custom_command( + POST_BUILD + COMMENT Generating bbs-fw.hex + TARGET bbs-fw + COMMAND packihx bbs-fw.ihx > bbs-fw.hex + ) +endif() diff --git a/code/firmware/CMakePresets.json b/code/firmware/CMakePresets.json new file mode 100644 index 00000000..3e471309 --- /dev/null +++ b/code/firmware/CMakePresets.json @@ -0,0 +1,65 @@ +{ + "version": 6, + "cmakeMinimumRequired": { + "major": 3, + "minor": 15, + "patch": 0 + }, + "configurePresets": [ + { + "name": "bbshd-release", + "displayName": "BBSHD", + "description": "Build for BBSHD", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/bbshd-release", + "toolchainFile": "${sourceDir}/cmake/sdcc-8051-toolchain.cmake", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "TARGET_CONTROLLER": { + "type": "STRING", + "value": "BBSHD" + } + } + }, + { + "name": "bbs02-release", + "displayName": "BBS02", + "description": "Build for BBS02", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/bbs02-release", + "toolchainFile": "${sourceDir}/cmake/sdcc-8051-toolchain.cmake", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "TARGET_CONTROLLER": { + "type": "STRING", + "value": "BBS02" + } + } + }, + { + "name": "tsdz2-release", + "displayName": "TSDZ2", + "description": "Build for TSDZ2", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/tsdz2-release", + "toolchainFile": "${sourceDir}/cmake/sdcc-stm8-toolchain.cmake", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "TARGET_CONTROLLER": { + "type": "STRING", + "value": "TSDZ2" + } + } + } + ], + "buildPresets": [ + { + "name": "bbshd-release", + "configurePreset": "bbshd-release" + }, + { + "name": "tsdz2-release", + "configurePreset": "tsdz2-release" + } + ] +} diff --git a/code/firmware/cmake/sdcc-8051-toolchain.cmake b/code/firmware/cmake/sdcc-8051-toolchain.cmake new file mode 100644 index 00000000..ffdac8c6 --- /dev/null +++ b/code/firmware/cmake/sdcc-8051-toolchain.cmake @@ -0,0 +1,27 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR 8051) + +set(CMAKE_C_FLAGS_INIT "-mmcs51 --model-large --std-sdcc11 -Ddouble=float") +set(CMAKE_EXE_LINKER_FLAGS_INIT "") + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +# which compilers to use for C and ASM +set(CMAKE_C_COMPILER sdcc) + +find_program (SDCC NAMES sdcc) +get_filename_component(SDCC_BIN_DIR ${SDCC} DIRECTORY) +get_filename_component(SDCC_PATH_DIR ${SDCC_BIN_DIR} DIRECTORY) + +# here is the target environment is located +set(CMAKE_FIND_ROOT_PATH ${SDCC_PATH_DIR}/usr/share/sdcc) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(CMAKE_ASM_OUTPUT_EXTENSION ".rel") diff --git a/code/firmware/cmake/sdcc-stm8-toolchain.cmake b/code/firmware/cmake/sdcc-stm8-toolchain.cmake new file mode 100644 index 00000000..daa55bb3 --- /dev/null +++ b/code/firmware/cmake/sdcc-stm8-toolchain.cmake @@ -0,0 +1,30 @@ +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR stm8) + +set(CMAKE_C_FLAGS_INIT "-mstm8 --std-sdcc11 -Ddouble=float") +set(CMAKE_EXE_LINKER_FLAGS_INIT "") + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +# which compilers to use for C and ASM +set(CMAKE_C_COMPILER sdcc) +set(CMAKE_ASM_COMPILER sdasstm8) + +find_program (SDCC NAMES sdcc) +get_filename_component(SDCC_BIN_DIR ${SDCC} DIRECTORY) +get_filename_component(SDCC_PATH_DIR ${SDCC_BIN_DIR} DIRECTORY) + +# here is the target environment is located +set(CMAKE_FIND_ROOT_PATH ${SDCC_PATH_DIR}/usr/share/sdcc) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(CMAKE_ASM_OUTPUT_EXTENSION ".rel") + +set(CMAKE_ASM_COMPILE_OBJECT "${CMAKE_ASM_COMPILER} -o ") diff --git a/code/firmware/lib/.clang-format b/code/firmware/lib/.clang-format new file mode 100644 index 00000000..47a38a93 --- /dev/null +++ b/code/firmware/lib/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: Never diff --git a/code/firmware/lib/stm8s/CMakeLists.txt b/code/firmware/lib/stm8s/CMakeLists.txt new file mode 100644 index 00000000..876aa78a --- /dev/null +++ b/code/firmware/lib/stm8s/CMakeLists.txt @@ -0,0 +1,31 @@ + +add_library(stm8s INTERFACE + "include/stm8/stm8s_adc1.h" + "include/stm8/stm8s_adc2.h" + "include/stm8/stm8s_awu.h" + "include/stm8/stm8s_beep.h" + "include/stm8/stm8s_can.h" + "include/stm8/stm8s_clk.h" + "include/stm8/stm8s_exti.h" + "include/stm8/stm8s_flash.h" + "include/stm8/stm8s_gpio.h" + "include/stm8/stm8s_i2c.h" + "include/stm8/stm8s_itc.h" + "include/stm8/stm8s_iwdg.h" + "include/stm8/stm8s_rst.h" + "include/stm8/stm8s_spi.h" + "include/stm8/stm8s_tim1.h" + "include/stm8/stm8s_tim2.h" + "include/stm8/stm8s_tim3.h" + "include/stm8/stm8s_tim4.h" + "include/stm8/stm8s_tim5.h" + "include/stm8/stm8s_tim6.h" + "include/stm8/stm8s_uart1.h" + "include/stm8/stm8s_uart2.h" + "include/stm8/stm8s_uart3.h" + "include/stm8/stm8s_uart4.h" + "include/stm8/stm8s_wwdg.h" + "include/stm8/stm8s.h" +) + +target_include_directories(stm8s INTERFACE include) diff --git a/src/firmware/tsdz2/stm8s/stm8s.h b/code/firmware/lib/stm8s/include/stm8/stm8s.h similarity index 97% rename from src/firmware/tsdz2/stm8s/stm8s.h rename to code/firmware/lib/stm8s/include/stm8/stm8s.h index a17d7248..d919393f 100644 --- a/src/firmware/tsdz2/stm8s/stm8s.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. @@ -32,7 +32,7 @@ /** @addtogroup STM8S_StdPeriph_Driver * @{ */ - + /* Uncomment the line below according to the target STM8S or STM8A device used in your application. */ @@ -51,27 +51,27 @@ /* #define STM8S001 */ /*!< STM8S Value Line Low denisty devices */ /* Tip: To avoid modifying this file each time you need to switch between these - devices, you can define the device in your toolchain compiler preprocessor. + devices, you can define the device in your toolchain compiler preprocessor. - High-Density STM8A devices are the STM8AF52xx STM8AF6269/8x/Ax, STM8AF51xx, and STM8AF6169/7x/8x/9x/Ax microcontrollers where the Flash memory density ranges between 32 to 128 Kbytes - Medium-Density STM8A devices are the STM8AF622x/4x, STM8AF6266/68, - STM8AF612x/4x, and STM8AF6166/68 microcontrollers where the Flash memory + STM8AF612x/4x, and STM8AF6166/68 microcontrollers where the Flash memory density ranges between 8 to 32 Kbytes - High-Density STM8S devices are the STM8S207xx, STM8S007 and STM8S208xx microcontrollers where the Flash memory density ranges between 32 to 128 Kbytes. - Medium-Density STM8S devices are the STM8S105x and STM8S005 microcontrollers where the Flash memory density ranges between 16 to 32-Kbytes. - Low-Density STM8A devices are the STM8AF622x microcontrollers where the Flash - density is 8 Kbytes. + density is 8 Kbytes. - Low-Density STM8S devices are the STM8S103xx, STM8S003, STM8S903xx and STM8S001 microcontrollers where the Flash density is 8 Kbytes. */ #if !defined (STM8S208) && !defined (STM8S207) && !defined (STM8S105) && \ !defined (STM8S103) && !defined (STM8S903) && !defined (STM8AF52Ax) && \ !defined (STM8AF62Ax) && !defined (STM8AF626x) && !defined (STM8S007) && \ - !defined (STM8S003)&& !defined (STM8S005) && !defined(STM8S001) && !defined (STM8AF622x) + !defined (STM8S003)&& !defined (STM8S005) && !defined(STM8S001) && !defined (STM8AF622x) #error "Please select first the target STM8S/A device used in your application (in stm8s.h file)" #endif @@ -153,7 +153,7 @@ #endif /* __CSMC__ */ /* For FLASH routines, select whether pointer will be declared as near (2 bytes, - to handle code smaller than 64KB) or far (3 bytes, to handle code larger + to handle code smaller than 64KB) or far (3 bytes, to handle code larger than 64K) */ #if defined (STM8S105) || defined (STM8S005) || defined (STM8S103) || defined (STM8S003) || \ @@ -180,15 +180,15 @@ #else /*_IAR_*/ #define IN_RAM(a) __ramfunc a #endif /* _COSMIC_ */ -#else +#else #define IN_RAM(a) a #endif /* RAM_EXECUTION */ /*!< [31:16] STM8S Standard Peripheral Library main version V2.3.0*/ -#define __STM8S_STDPERIPH_VERSION_MAIN ((uint8_t)0x02) /*!< [31:24] main version */ +#define __STM8S_STDPERIPH_VERSION_MAIN ((uint8_t)0x02) /*!< [31:24] main version */ #define __STM8S_STDPERIPH_VERSION_SUB1 ((uint8_t)0x03) /*!< [23:16] sub1 version */ #define __STM8S_STDPERIPH_VERSION_SUB2 ((uint8_t)0x00) /*!< [15:8] sub2 version */ -#define __STM8S_STDPERIPH_VERSION_RC ((uint8_t)0x00) /*!< [7:0] release candidate */ +#define __STM8S_STDPERIPH_VERSION_RC ((uint8_t)0x00) /*!< [7:0] release candidate */ #define __STM8S_STDPERIPH_VERSION ( (__STM8S_STDPERIPH_VERSION_MAIN << 24)\ |(__STM8S_STDPERIPH_VERSION_SUB1 << 16)\ |(__STM8S_STDPERIPH_VERSION_SUB2 << 8)\ @@ -236,7 +236,7 @@ typedef enum {ERROR = 0, SUCCESS = !ERROR} ErrorStatus; /** * @} */ - + /** @addtogroup MAP_FILE_Exported_Types_and_Constants * @{ */ @@ -556,7 +556,7 @@ CLK_TypeDef; #define CLK_CKDIVR_HSIDIV ((uint8_t)0x18) /*!< High speed internal clock prescaler */ #define CLK_CKDIVR_CPUDIV ((uint8_t)0x07) /*!< CPU clock prescaler */ -#define CLK_PCKENR1_TIM1 ((uint8_t)0x80) /*!< Timer 1 clock enable */ +#define CLK_PCKENR1_TIM1 ((uint8_t)0x80) /*!< Timer 1 clock enable */ #define CLK_PCKENR1_TIM3 ((uint8_t)0x40) /*!< Timer 3 clock enable */ #define CLK_PCKENR1_TIM2 ((uint8_t)0x20) /*!< Timer 2 clock enable */ #define CLK_PCKENR1_TIM5 ((uint8_t)0x20) /*!< Timer 5 clock enable */ @@ -1237,7 +1237,7 @@ typedef struct TIM5_struct /** * @} */ - + /*----------------------------------------------------------------------------*/ /** * @brief 8-bit system timer with synchro module(TIM6) @@ -2200,7 +2200,7 @@ typedef struct __IO uint8_t FR06; __IO uint8_t FR07; __IO uint8_t FR08; - + __IO uint8_t FR09; __IO uint8_t FR10; __IO uint8_t FR11; @@ -2231,7 +2231,7 @@ typedef struct __IO uint8_t F1R7; __IO uint8_t F1R8; }Filter01; - + struct { __IO uint8_t F2R1; @@ -2242,7 +2242,7 @@ typedef struct __IO uint8_t F2R6; __IO uint8_t F2R7; __IO uint8_t F2R8; - + __IO uint8_t F3R1; __IO uint8_t F3R2; __IO uint8_t F3R3; @@ -2252,7 +2252,7 @@ typedef struct __IO uint8_t F3R7; __IO uint8_t F3R8; }Filter23; - + struct { __IO uint8_t F4R1; @@ -2263,7 +2263,7 @@ typedef struct __IO uint8_t F4R6; __IO uint8_t F4R7; __IO uint8_t F4R8; - + __IO uint8_t F5R1; __IO uint8_t F5R2; __IO uint8_t F5R3; @@ -2273,7 +2273,7 @@ typedef struct __IO uint8_t F5R7; __IO uint8_t F5R8; } Filter45; - + struct { __IO uint8_t ESR; @@ -2290,7 +2290,7 @@ typedef struct __IO uint8_t FCR3; uint8_t Reserved2[3]; }Config; - + struct { __IO uint8_t MFMI; @@ -2310,7 +2310,7 @@ typedef struct __IO uint8_t MTSRL; __IO uint8_t MTSRH; }RxFIFO; - }Page; + }Page; } CAN_TypeDef; /** @addtogroup CAN_Registers_Bits_Definition @@ -2409,62 +2409,62 @@ typedef struct #define CAN_EIER_EPVIE ((uint8_t)0x02) #define CAN_EIER_BOFIE ((uint8_t)0x04) #define CAN_EIER_LECIE ((uint8_t)0x10) -#define CAN_EIER_ERRIE ((uint8_t)0x80) +#define CAN_EIER_ERRIE ((uint8_t)0x80) /* CAN transmit error counter Register bits(CAN_TECR) */ -#define CAN_TECR_TEC0 ((uint8_t)0x01) -#define CAN_TECR_TEC1 ((uint8_t)0x02) -#define CAN_TECR_TEC2 ((uint8_t)0x04) -#define CAN_TECR_TEC3 ((uint8_t)0x08) -#define CAN_TECR_TEC4 ((uint8_t)0x10) -#define CAN_TECR_TEC5 ((uint8_t)0x20) -#define CAN_TECR_TEC6 ((uint8_t)0x40) -#define CAN_TECR_TEC7 ((uint8_t)0x80) +#define CAN_TECR_TEC0 ((uint8_t)0x01) +#define CAN_TECR_TEC1 ((uint8_t)0x02) +#define CAN_TECR_TEC2 ((uint8_t)0x04) +#define CAN_TECR_TEC3 ((uint8_t)0x08) +#define CAN_TECR_TEC4 ((uint8_t)0x10) +#define CAN_TECR_TEC5 ((uint8_t)0x20) +#define CAN_TECR_TEC6 ((uint8_t)0x40) +#define CAN_TECR_TEC7 ((uint8_t)0x80) /* CAN RECEIVE error counter Register bits(CAN_TECR) */ -#define CAN_RECR_REC0 ((uint8_t)0x01) -#define CAN_RECR_REC1 ((uint8_t)0x02) -#define CAN_RECR_REC2 ((uint8_t)0x04) -#define CAN_RECR_REC3 ((uint8_t)0x08) -#define CAN_RECR_REC4 ((uint8_t)0x10) -#define CAN_RECR_REC5 ((uint8_t)0x20) -#define CAN_RECR_REC6 ((uint8_t)0x40) -#define CAN_RECR_REC7 ((uint8_t)0x80) +#define CAN_RECR_REC0 ((uint8_t)0x01) +#define CAN_RECR_REC1 ((uint8_t)0x02) +#define CAN_RECR_REC2 ((uint8_t)0x04) +#define CAN_RECR_REC3 ((uint8_t)0x08) +#define CAN_RECR_REC4 ((uint8_t)0x10) +#define CAN_RECR_REC5 ((uint8_t)0x20) +#define CAN_RECR_REC6 ((uint8_t)0x40) +#define CAN_RECR_REC7 ((uint8_t)0x80) /* CAN filter mode register bits (CAN_FMR) */ -#define CAN_FMR1_FML0 ((uint8_t)0x01) -#define CAN_FMR1_FMH0 ((uint8_t)0x02) -#define CAN_FMR1_FML1 ((uint8_t)0x04) -#define CAN_FMR1_FMH1 ((uint8_t)0x08) -#define CAN_FMR1_FML2 ((uint8_t)0x10) -#define CAN_FMR1_FMH2 ((uint8_t)0x20) -#define CAN_FMR1_FML3 ((uint8_t)0x40) -#define CAN_FMR1_FMH3 ((uint8_t)0x80) - -#define CAN_FMR2_FML4 ((uint8_t)0x01) -#define CAN_FMR2_FMH4 ((uint8_t)0x02) -#define CAN_FMR2_FML5 ((uint8_t)0x04) -#define CAN_FMR2_FMH5 ((uint8_t)0x08) +#define CAN_FMR1_FML0 ((uint8_t)0x01) +#define CAN_FMR1_FMH0 ((uint8_t)0x02) +#define CAN_FMR1_FML1 ((uint8_t)0x04) +#define CAN_FMR1_FMH1 ((uint8_t)0x08) +#define CAN_FMR1_FML2 ((uint8_t)0x10) +#define CAN_FMR1_FMH2 ((uint8_t)0x20) +#define CAN_FMR1_FML3 ((uint8_t)0x40) +#define CAN_FMR1_FMH3 ((uint8_t)0x80) + +#define CAN_FMR2_FML4 ((uint8_t)0x01) +#define CAN_FMR2_FMH4 ((uint8_t)0x02) +#define CAN_FMR2_FML5 ((uint8_t)0x04) +#define CAN_FMR2_FMH5 ((uint8_t)0x08) /* CAN filter Config register bits (CAN_FCR) */ -#define CAN_FCR1_FACT0 ((uint8_t)0x01) -#define CAN_FCR1_FACT1 ((uint8_t)0x10) -#define CAN_FCR2_FACT2 ((uint8_t)0x01) -#define CAN_FCR2_FACT3 ((uint8_t)0x10) -#define CAN_FCR3_FACT4 ((uint8_t)0x01) -#define CAN_FCR3_FACT5 ((uint8_t)0x10) - -#define CAN_FCR1_FSC00 ((uint8_t)0x02) -#define CAN_FCR1_FSC01 ((uint8_t)0x04) -#define CAN_FCR1_FSC10 ((uint8_t)0x20) -#define CAN_FCR1_FSC11 ((uint8_t)0x40) -#define CAN_FCR2_FSC20 ((uint8_t)0x02) -#define CAN_FCR2_FSC21 ((uint8_t)0x04) -#define CAN_FCR2_FSC30 ((uint8_t)0x20) -#define CAN_FCR2_FSC31 ((uint8_t)0x40) -#define CAN_FCR3_FSC40 ((uint8_t)0x02) -#define CAN_FCR3_FSC41 ((uint8_t)0x04) -#define CAN_FCR3_FSC50 ((uint8_t)0x20) +#define CAN_FCR1_FACT0 ((uint8_t)0x01) +#define CAN_FCR1_FACT1 ((uint8_t)0x10) +#define CAN_FCR2_FACT2 ((uint8_t)0x01) +#define CAN_FCR2_FACT3 ((uint8_t)0x10) +#define CAN_FCR3_FACT4 ((uint8_t)0x01) +#define CAN_FCR3_FACT5 ((uint8_t)0x10) + +#define CAN_FCR1_FSC00 ((uint8_t)0x02) +#define CAN_FCR1_FSC01 ((uint8_t)0x04) +#define CAN_FCR1_FSC10 ((uint8_t)0x20) +#define CAN_FCR1_FSC11 ((uint8_t)0x40) +#define CAN_FCR2_FSC20 ((uint8_t)0x02) +#define CAN_FCR2_FSC21 ((uint8_t)0x04) +#define CAN_FCR2_FSC30 ((uint8_t)0x20) +#define CAN_FCR2_FSC31 ((uint8_t)0x40) +#define CAN_FCR3_FSC40 ((uint8_t)0x02) +#define CAN_FCR3_FSC41 ((uint8_t)0x04) +#define CAN_FCR3_FSC50 ((uint8_t)0x20) #define CAN_FCR3_FSC51 ((uint8_t)0x40) /** @@ -2681,7 +2681,7 @@ CFG_TypeDef; #if defined (STM8S903) || defined (STM8AF622x) #define TIM5 ((TIM5_TypeDef *) TIM5_BaseAddress) #define TIM6 ((TIM6_TypeDef *) TIM6_BaseAddress) -#endif /* (STM8S903) || (STM8AF622x) */ +#endif /* (STM8S903) || (STM8AF622x) */ #define ITC ((ITC_TypeDef *) ITC_BaseAddress) @@ -2781,7 +2781,7 @@ CFG_TypeDef; __interrupt void (a)( void ) #define INTERRUPT_HANDLER_TRAP(a) \ _Pragma( VECTOR_ID( 1 ) ) \ - __interrupt void (a) (void) + __interrupt void (a) (void) #endif /* _IAR_ */ #ifdef _SDCC_ diff --git a/src/firmware/tsdz2/stm8s/stm8s_adc1.h b/code/firmware/lib/stm8s/include/stm8/stm8s_adc1.h similarity index 96% rename from src/firmware/tsdz2/stm8s/stm8s_adc1.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_adc1.h index 2f050a70..1d2c25cd 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_adc1.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_adc1.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. @@ -41,7 +41,7 @@ * @brief ADC1 clock prescaler selection */ -typedef enum +typedef enum { ADC1_PRESSEL_FCPU_D2 = (uint8_t)0x00, /**< Prescaler selection fADC1 = fcpu/2 */ ADC1_PRESSEL_FCPU_D3 = (uint8_t)0x10, /**< Prescaler selection fADC1 = fcpu/3 */ @@ -56,7 +56,7 @@ typedef enum /** * @brief ADC1 External conversion trigger event selection */ -typedef enum +typedef enum { ADC1_EXTTRIG_TIM = (uint8_t)0x00, /**< Conversion from Internal TIM1 TRGO event */ ADC1_EXTTRIG_GPIO = (uint8_t)0x10 /**< Conversion from External interrupt on ADC_ETR pin*/ @@ -65,7 +65,7 @@ typedef enum /** * @brief ADC1 data alignment */ -typedef enum +typedef enum { ADC1_ALIGN_LEFT = (uint8_t)0x00, /**< Data alignment left */ ADC1_ALIGN_RIGHT = (uint8_t)0x08 /**< Data alignment right */ @@ -74,7 +74,7 @@ typedef enum /** * @brief ADC1 Interrupt source */ -typedef enum +typedef enum { ADC1_IT_AWDIE = (uint16_t)0x010, /**< Analog WDG interrupt enable */ ADC1_IT_EOCIE = (uint16_t)0x020, /**< EOC interrupt enable */ @@ -98,7 +98,7 @@ typedef enum /** * @brief ADC1 Flags */ -typedef enum +typedef enum { ADC1_FLAG_OVR = (uint8_t)0x41, /**< Overrun status flag */ ADC1_FLAG_AWD = (uint8_t)0x40, /**< Analog WDG status */ @@ -121,7 +121,7 @@ typedef enum /** * @brief ADC1 schmitt Trigger */ -typedef enum +typedef enum { ADC1_SCHMITTTRIG_CHANNEL0 = (uint8_t)0x00, /**< Schmitt trigger disable on AIN0 */ ADC1_SCHMITTTRIG_CHANNEL1 = (uint8_t)0x01, /**< Schmitt trigger disable on AIN1 */ @@ -133,16 +133,16 @@ typedef enum ADC1_SCHMITTTRIG_CHANNEL7 = (uint8_t)0x07, /**< Schmitt trigger disable on AIN7 */ ADC1_SCHMITTTRIG_CHANNEL8 = (uint8_t)0x08, /**< Schmitt trigger disable on AIN8 */ ADC1_SCHMITTTRIG_CHANNEL9 = (uint8_t)0x09, /**< Schmitt trigger disable on AIN9 */ - ADC1_SCHMITTTRIG_CHANNEL12 = (uint8_t)0x0C, /**< Schmitt trigger disable on AIN12 */ - /* refer to product datasheet for channel 12 availability */ - ADC1_SCHMITTTRIG_ALL = (uint8_t)0xFF /**< Schmitt trigger disable on All channels */ + ADC1_SCHMITTTRIG_CHANNEL12 = (uint8_t)0x0C, /**< Schmitt trigger disable on AIN12 */ + /* refer to product datasheet for channel 12 availability */ + ADC1_SCHMITTTRIG_ALL = (uint8_t)0xFF /**< Schmitt trigger disable on All channels */ } ADC1_SchmittTrigg_TypeDef; /** * @brief ADC1 conversion mode selection */ -typedef enum +typedef enum { ADC1_CONVERSIONMODE_SINGLE = (uint8_t)0x00, /**< Single conversion mode */ ADC1_CONVERSIONMODE_CONTINUOUS = (uint8_t)0x01 /**< Continuous conversion mode */ @@ -152,7 +152,7 @@ typedef enum * @brief ADC1 analog channel selection */ -typedef enum +typedef enum { ADC1_CHANNEL_0 = (uint8_t)0x00, /**< Analog channel 0 */ ADC1_CHANNEL_1 = (uint8_t)0x01, /**< Analog channel 1 */ @@ -164,7 +164,7 @@ typedef enum ADC1_CHANNEL_7 = (uint8_t)0x07, /**< Analog channel 7 */ ADC1_CHANNEL_8 = (uint8_t)0x08, /**< Analog channel 8 */ ADC1_CHANNEL_9 = (uint8_t)0x09, /**< Analog channel 9 */ - ADC1_CHANNEL_12 = (uint8_t)0x0C /**< Analog channel 12 */ + ADC1_CHANNEL_12 = (uint8_t)0x0C /**< Analog channel 12 */ /* refer to product datasheet for channel 12 availability */ } ADC1_Channel_TypeDef; @@ -299,12 +299,12 @@ typedef enum * @{ */ void ADC1_DeInit(void); -void ADC1_Init(ADC1_ConvMode_TypeDef ADC1_ConversionMode, +void ADC1_Init(ADC1_ConvMode_TypeDef ADC1_ConversionMode, ADC1_Channel_TypeDef ADC1_Channel, - ADC1_PresSel_TypeDef ADC1_PrescalerSelection, - ADC1_ExtTrig_TypeDef ADC1_ExtTrigger, - FunctionalState ADC1_ExtTriggerState, ADC1_Align_TypeDef ADC1_Align, - ADC1_SchmittTrigg_TypeDef ADC1_SchmittTriggerChannel, + ADC1_PresSel_TypeDef ADC1_PrescalerSelection, + ADC1_ExtTrig_TypeDef ADC1_ExtTrigger, + FunctionalState ADC1_ExtTriggerState, ADC1_Align_TypeDef ADC1_Align, + ADC1_SchmittTrigg_TypeDef ADC1_SchmittTriggerChannel, FunctionalState ADC1_SchmittTriggerState); void ADC1_Cmd(FunctionalState NewState); void ADC1_ScanModeCmd(FunctionalState NewState); @@ -313,8 +313,8 @@ void ADC1_ITConfig(ADC1_IT_TypeDef ADC1_IT, FunctionalState NewState); void ADC1_PrescalerConfig(ADC1_PresSel_TypeDef ADC1_Prescaler); void ADC1_SchmittTriggerConfig(ADC1_SchmittTrigg_TypeDef ADC1_SchmittTriggerChannel, FunctionalState NewState); -void ADC1_ConversionConfig(ADC1_ConvMode_TypeDef ADC1_ConversionMode, - ADC1_Channel_TypeDef ADC1_Channel, +void ADC1_ConversionConfig(ADC1_ConvMode_TypeDef ADC1_ConversionMode, + ADC1_Channel_TypeDef ADC1_Channel, ADC1_Align_TypeDef ADC1_Align); void ADC1_ExternalTriggerConfig(ADC1_ExtTrig_TypeDef ADC1_ExtTrigger, FunctionalState NewState); void ADC1_AWDChannelConfig(ADC1_Channel_TypeDef Channel, FunctionalState NewState); diff --git a/src/firmware/tsdz2/stm8s/stm8s_adc2.h b/code/firmware/lib/stm8s/include/stm8/stm8s_adc2.h similarity index 96% rename from src/firmware/tsdz2/stm8s/stm8s_adc2.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_adc2.h index 6c1d637c..ddfee30c 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_adc2.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_adc2.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. @@ -223,21 +223,21 @@ typedef enum { * @{ */ void ADC2_DeInit(void); -void ADC2_Init(ADC2_ConvMode_TypeDef ADC2_ConversionMode, - ADC2_Channel_TypeDef ADC2_Channel, - ADC2_PresSel_TypeDef ADC2_PrescalerSelection, - ADC2_ExtTrig_TypeDef ADC2_ExtTrigger, - FunctionalState ADC2_ExtTriggerState, - ADC2_Align_TypeDef ADC2_Align, - ADC2_SchmittTrigg_TypeDef ADC2_SchmittTriggerChannel, +void ADC2_Init(ADC2_ConvMode_TypeDef ADC2_ConversionMode, + ADC2_Channel_TypeDef ADC2_Channel, + ADC2_PresSel_TypeDef ADC2_PrescalerSelection, + ADC2_ExtTrig_TypeDef ADC2_ExtTrigger, + FunctionalState ADC2_ExtTriggerState, + ADC2_Align_TypeDef ADC2_Align, + ADC2_SchmittTrigg_TypeDef ADC2_SchmittTriggerChannel, FunctionalState ADC2_SchmittTriggerState); void ADC2_Cmd(FunctionalState NewState); void ADC2_ITConfig(FunctionalState NewState); void ADC2_PrescalerConfig(ADC2_PresSel_TypeDef ADC2_Prescaler); -void ADC2_SchmittTriggerConfig(ADC2_SchmittTrigg_TypeDef ADC2_SchmittTriggerChannel, +void ADC2_SchmittTriggerConfig(ADC2_SchmittTrigg_TypeDef ADC2_SchmittTriggerChannel, FunctionalState NewState); -void ADC2_ConversionConfig(ADC2_ConvMode_TypeDef ADC2_ConversionMode, - ADC2_Channel_TypeDef ADC2_Channel, +void ADC2_ConversionConfig(ADC2_ConvMode_TypeDef ADC2_ConversionMode, + ADC2_Channel_TypeDef ADC2_Channel, ADC2_Align_TypeDef ADC2_Align); void ADC2_ExternalTriggerConfig(ADC2_ExtTrig_TypeDef ADC2_ExtTrigger, FunctionalState NewState); void ADC2_StartConversion(void); diff --git a/src/firmware/tsdz2/stm8s/stm8s_awu.h b/code/firmware/lib/stm8s/include/stm8/stm8s_awu.h similarity index 99% rename from src/firmware/tsdz2/stm8s/stm8s_awu.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_awu.h index 95455c9e..802a6a01 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_awu.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_awu.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. diff --git a/src/firmware/tsdz2/stm8s/stm8s_beep.h b/code/firmware/lib/stm8s/include/stm8/stm8s_beep.h similarity index 99% rename from src/firmware/tsdz2/stm8s/stm8s_beep.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_beep.h index 994f96bc..5ba7498d 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_beep.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_beep.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. diff --git a/src/firmware/tsdz2/stm8s/stm8s_can.h b/code/firmware/lib/stm8s/include/stm8/stm8s_can.h similarity index 97% rename from src/firmware/tsdz2/stm8s/stm8s_can.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_can.h index 3d47e83b..83348667 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_can.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_can.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. @@ -61,14 +61,14 @@ typedef enum /** - * @brief CAN sleep constants + * @brief CAN sleep constants */ typedef enum { CAN_InitStatus_Failed =0, /*!< CAN initialization failed */ CAN_InitStatus_Success =! CAN_InitStatus_Failed /*!< CAN initialization OK*/ } CAN_InitStatus_TypeDef; - + /** * @brief CAN operating mode */ typedef enum @@ -87,7 +87,7 @@ typedef enum { }CAN_ModeStatus_TypeDef; /** - * @brief CAN Time Triggered Communication mode + * @brief CAN Time Triggered Communication mode */ typedef enum { @@ -161,13 +161,13 @@ typedef enum /** * @brief CAN filter number */ typedef enum -{ - CAN_FilterNumber_0 = ((uint8_t)0x00), /*!< Filter number 0 */ - CAN_FilterNumber_1 = ((uint8_t)0x01), /*!< Filter number 1 */ +{ + CAN_FilterNumber_0 = ((uint8_t)0x00), /*!< Filter number 0 */ + CAN_FilterNumber_1 = ((uint8_t)0x01), /*!< Filter number 1 */ CAN_FilterNumber_2 = ((uint8_t)0x02), /*!< Filter number 2 */ CAN_FilterNumber_3 = ((uint8_t)0x03), /*!< Filter number 3 */ - CAN_FilterNumber_4 = ((uint8_t)0x04), /*!< Filter number 4 */ - CAN_FilterNumber_5 = ((uint8_t)0x05) /*!< Filter number 5 */ + CAN_FilterNumber_4 = ((uint8_t)0x04), /*!< Filter number 4 */ + CAN_FilterNumber_5 = ((uint8_t)0x05) /*!< Filter number 5 */ }CAN_FilterNumber_TypeDef; /** @@ -285,7 +285,7 @@ typedef enum /*Transmit Interruption*/ CAN_IT_TME =((uint16_t)0x0001), /*!< Transmit mailbox empty interrupt */ /*Receive Interruptions*/ - CAN_IT_FMP =((uint16_t)0x0002), /*!< FIFO message pending interrupt */ + CAN_IT_FMP =((uint16_t)0x0002), /*!< FIFO message pending interrupt */ CAN_IT_FF =((uint16_t)0x0004), /*!< FIFO full interrupt */ CAN_IT_FOV =((uint16_t)0x0008), /*!< FIFO overrun interrupt */ /*Wake Up Interruption*/ @@ -309,15 +309,15 @@ typedef enum /** * @brief CAN Error Code description */ typedef enum -{ - CAN_ErrorCode_NoErr = ((uint8_t)0x00), /*!< No Error */ - CAN_ErrorCode_StuffErr = ((uint8_t)0x10), /*!< Stuff Error */ - CAN_ErrorCode_FormErr = ((uint8_t)0x20), /*!< Form Error */ - CAN_ErrorCode_ACKErr = ((uint8_t)0x30), /*!< Acknowledgment Error */ - CAN_ErrorCode_BitRecessiveErr = ((uint8_t)0x40), /*!< Bit Recessive Error */ - CAN_ErrorCode_BitDominantErr = ((uint8_t)0x50), /*!< Bit Dominant Error */ - CAN_ErrorCode_CRCErr = ((uint8_t)0x60), /*!< CRC Error */ - CAN_ErrorCode_SoftwareSetErr = ((uint8_t)0x70) /*!< Software Set Error */ +{ + CAN_ErrorCode_NoErr = ((uint8_t)0x00), /*!< No Error */ + CAN_ErrorCode_StuffErr = ((uint8_t)0x10), /*!< Stuff Error */ + CAN_ErrorCode_FormErr = ((uint8_t)0x20), /*!< Form Error */ + CAN_ErrorCode_ACKErr = ((uint8_t)0x30), /*!< Acknowledgment Error */ + CAN_ErrorCode_BitRecessiveErr = ((uint8_t)0x40), /*!< Bit Recessive Error */ + CAN_ErrorCode_BitDominantErr = ((uint8_t)0x50), /*!< Bit Dominant Error */ + CAN_ErrorCode_CRCErr = ((uint8_t)0x60), /*!< CRC Error */ + CAN_ErrorCode_SoftwareSetErr = ((uint8_t)0x70) /*!< Software Set Error */ }CAN_ErrorCode_TypeDef; /** * @} @@ -474,7 +474,7 @@ void CAN_FilterInit(CAN_FilterNumber_TypeDef CAN_FilterNumber, FunctionalState CAN_FilterActivation, CAN_FilterMode_TypeDef CAN_FilterMode, CAN_FilterScale_TypeDef CAN_FilterScale, - uint8_t CAN_FilterID1, + uint8_t CAN_FilterID1, uint8_t CAN_FilterID2, uint8_t CAN_FilterID3, uint8_t CAN_FilterID4, diff --git a/src/firmware/tsdz2/stm8s/stm8s_clk.h b/code/firmware/lib/stm8s/include/stm8/stm8s_clk.h similarity index 99% rename from src/firmware/tsdz2/stm8s/stm8s_clk.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_clk.h index 1674c491..4b310057 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_clk.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_clk.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. diff --git a/src/firmware/tsdz2/stm8s/stm8s_exti.h b/code/firmware/lib/stm8s/include/stm8/stm8s_exti.h similarity index 99% rename from src/firmware/tsdz2/stm8s/stm8s_exti.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_exti.h index 45d2fa95..93f61af2 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_exti.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_exti.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. diff --git a/src/firmware/tsdz2/stm8s/stm8s_flash.h b/code/firmware/lib/stm8s/include/stm8/stm8s_flash.h similarity index 98% rename from src/firmware/tsdz2/stm8s/stm8s_flash.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_flash.h index 73221ce0..11fd2538 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_flash.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_flash.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. @@ -72,7 +72,7 @@ #define OPTION_BYTE_START_PHYSICAL_ADDRESS ((uint16_t)0x4800) #define OPTION_BYTE_END_PHYSICAL_ADDRESS ((uint16_t)0x487F) -#define FLASH_OPTIONBYTE_ERROR ((uint16_t)0x5555) /*!< Error code option byte +#define FLASH_OPTIONBYTE_ERROR ((uint16_t)0x5555) /*!< Error code option byte (if value read is not equal to complement value read) */ /** * @} @@ -124,7 +124,7 @@ FLASH_LPMode_TypeDef; */ typedef enum { #if defined (STM8S208) || defined(STM8S207) || defined(STM8S007) || defined(STM8S105) || \ - defined(STM8S005) || defined (STM8AF52Ax) || defined (STM8AF62Ax) || defined(STM8AF626x) + defined(STM8S005) || defined (STM8AF52Ax) || defined (STM8AF62Ax) || defined(STM8AF626x) FLASH_STATUS_END_HIGH_VOLTAGE = (uint8_t)0x40, /*!< End of high voltage */ #endif /* STM8S208, STM8S207, STM8S105, STM8AF62Ax, STM8AF52Ax, STM8AF626x */ FLASH_STATUS_SUCCESSFUL_OPERATION = (uint8_t)0x04, /*!< End of operation flag */ @@ -211,7 +211,7 @@ typedef enum { ((TIME) == FLASH_PROGRAMTIME_TPROG)) /** - * @brief Macro used by the assert function in order to check the different + * @brief Macro used by the assert function in order to check the different * sensitivity values for the low power mode */ @@ -221,7 +221,7 @@ typedef enum { ((LPMODE) == FLASH_LPMODE_STANDBY_POWERDOWN)) /** - * @brief Macro used by the assert function in order to check the different + * @brief Macro used by the assert function in order to check the different * sensitivity values for the option bytes Address */ #define IS_OPTION_BYTE_ADDRESS_OK(ADDRESS) (((ADDRESS) >= OPTION_BYTE_START_PHYSICAL_ADDRESS) && \ @@ -273,20 +273,20 @@ FlagStatus FLASH_GetFlagStatus(FLASH_Flag_TypeDef FLASH_FLAG); /** @code - All the functions declared below must be executed from RAM exclusively, except + All the functions declared below must be executed from RAM exclusively, except for the FLASH_WaitForLastOperation function which can be executed from Flash. - + Steps of the execution from RAM differs from one toolchain to another. for more details refer to stm8s_flash.c file. - - To enable execution from RAM you can either uncomment the following define + + To enable execution from RAM you can either uncomment the following define in the stm8s.h file or define it in your toolchain compiler preprocessor - - #define RAM_EXECUTION (1) + - #define RAM_EXECUTION (1) @endcode */ IN_RAM(void FLASH_EraseBlock(uint16_t BlockNum, FLASH_MemType_TypeDef FLASH_MemType)); -IN_RAM(void FLASH_ProgramBlock(uint16_t BlockNum, FLASH_MemType_TypeDef FLASH_MemType, +IN_RAM(void FLASH_ProgramBlock(uint16_t BlockNum, FLASH_MemType_TypeDef FLASH_MemType, FLASH_ProgramMode_TypeDef FLASH_ProgMode, uint8_t *Buffer)); IN_RAM(FLASH_Status_TypeDef FLASH_WaitForLastOperation(FLASH_MemType_TypeDef FLASH_MemType)); diff --git a/src/firmware/tsdz2/stm8s/stm8s_gpio.h b/code/firmware/lib/stm8s/include/stm8/stm8s_gpio.h similarity index 99% rename from src/firmware/tsdz2/stm8s/stm8s_gpio.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_gpio.h index 9b67ac71..be48a261 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_gpio.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_gpio.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. diff --git a/src/firmware/tsdz2/stm8s/stm8s_i2c.h b/code/firmware/lib/stm8s/include/stm8/stm8s_i2c.h similarity index 99% rename from src/firmware/tsdz2/stm8s/stm8s_i2c.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_i2c.h index 076e5ca4..9d48274d 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_i2c.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_i2c.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. @@ -269,10 +269,10 @@ typedef enum * I2C_OwnAddress1 field) the I2C_EVENT_SLAVE_XXX_ADDRESS_MATCHED event is set * (where XXX could be TRANSMITTER or RECEIVER). * - * 2) In case the address sent by the master is General Call (address 0x00) and - * if the General Call is enabled for the peripheral (using function I2C_GeneralCallCmd()) - * the following event is set I2C_EVENT_SLAVE_GENERALCALLADDRESS_MATCHED. - * + * 2) In case the address sent by the master is General Call (address 0x00) and + * if the General Call is enabled for the peripheral (using function I2C_GeneralCallCmd()) + * the following event is set I2C_EVENT_SLAVE_GENERALCALLADDRESS_MATCHED. + * */ /* --EV1 (all the events below are variants of EV1) */ @@ -334,7 +334,7 @@ typedef enum */ #define I2C_MAX_STANDARD_FREQ ((uint32_t)100000) #define I2C_MAX_FAST_FREQ ((uint32_t)400000) -#if defined(STM8S208) || defined(STM8S207) || defined(STM8S007) +#if defined(STM8S208) || defined(STM8S207) || defined(STM8S007) #define I2C_MAX_INPUT_FREQ ((uint8_t)24) #else #define I2C_MAX_INPUT_FREQ ((uint8_t)16) @@ -438,7 +438,7 @@ typedef enum ((ITPENDINGBIT) == I2C_ITPENDINGBIT_ACKNOWLEDGEFAILURE) || \ ((ITPENDINGBIT) == I2C_ITPENDINGBIT_ARBITRATIONLOSS) || \ ((ITPENDINGBIT) == I2C_ITPENDINGBIT_BUSERROR)) - + /** * @brief Macro used by the assert function to check the different I2C possible * pending bits to clear by writing 0. @@ -449,7 +449,7 @@ typedef enum ((ITPENDINGBIT) == I2C_ITPENDINGBIT_ACKNOWLEDGEFAILURE) || \ ((ITPENDINGBIT) == I2C_ITPENDINGBIT_ARBITRATIONLOSS) || \ ((ITPENDINGBIT) == I2C_ITPENDINGBIT_BUSERROR)) - + /** * @brief Macro used by the assert function to check the different I2C possible events. */ @@ -503,8 +503,8 @@ typedef enum */ void I2C_DeInit(void); -void I2C_Init(uint32_t OutputClockFrequencyHz, uint16_t OwnAddress, - I2C_DutyCycle_TypeDef I2C_DutyCycle, I2C_Ack_TypeDef Ack, +void I2C_Init(uint32_t OutputClockFrequencyHz, uint16_t OwnAddress, + I2C_DutyCycle_TypeDef I2C_DutyCycle, I2C_Ack_TypeDef Ack, I2C_AddMode_TypeDef AddMode, uint8_t InputClockFrequencyMHz ); void I2C_Cmd(FunctionalState NewState); void I2C_GeneralCallCmd(FunctionalState NewState); diff --git a/src/firmware/tsdz2/stm8s/stm8s_itc.h b/code/firmware/lib/stm8s/include/stm8/stm8s_itc.h similarity index 96% rename from src/firmware/tsdz2/stm8s/stm8s_itc.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_itc.h index 79786279..cddcc856 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_itc.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_itc.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. @@ -50,13 +50,13 @@ typedef enum { ITC_IRQ_PORTC = (uint8_t)5, /*!< Port C external interrupts */ ITC_IRQ_PORTD = (uint8_t)6, /*!< Port D external interrupts */ ITC_IRQ_PORTE = (uint8_t)7, /*!< Port E external interrupts */ - + #if defined(STM8S208) || defined(STM8AF52Ax) ITC_IRQ_CAN_RX = (uint8_t)8, /*!< beCAN RX interrupt */ ITC_IRQ_CAN_TX = (uint8_t)9, /*!< beCAN TX/ER/SC interrupt */ #endif /*STM8S208 or STM8AF52Ax */ -#if defined(STM8S903) || defined(STM8AF622x) +#if defined(STM8S903) || defined(STM8AF622x) ITC_IRQ_PORTF = (uint8_t)8, /*!< Port F external interrupts */ #endif /*STM8S903 or STM8AF622x */ @@ -64,12 +64,12 @@ typedef enum { ITC_IRQ_TIM1_OVF = (uint8_t)11, /*!< TIM1 update/overflow/underflow/trigger/ break interrupt*/ ITC_IRQ_TIM1_CAPCOM = (uint8_t)12, /*!< TIM1 capture/compare interrupt */ - + #if defined(STM8S903) || defined(STM8AF622x) ITC_IRQ_TIM5_OVFTRI = (uint8_t)13, /*!< TIM5 update/overflow/underflow/trigger/ interrupt */ ITC_IRQ_TIM5_CAPCOM = (uint8_t)14, /*!< TIM5 capture/compare interrupt */ -#else +#else ITC_IRQ_TIM2_OVF = (uint8_t)13, /*!< TIM2 update /overflow interrupt */ ITC_IRQ_TIM2_CAPCOM = (uint8_t)14, /*!< TIM2 capture/compare interrupt */ #endif /*STM8S903 or STM8AF622x */ @@ -78,17 +78,17 @@ typedef enum { ITC_IRQ_TIM3_CAPCOM = (uint8_t)16, /*!< TIM3 update /overflow interrupt */ #if defined(STM8S208) ||defined(STM8S207) || defined (STM8S007) || defined(STM8S103) || \ - defined(STM8S003) || defined(STM8S001) ||defined(STM8S903) || defined (STM8AF52Ax) || defined (STM8AF62Ax) + defined(STM8S003) || defined(STM8S001) ||defined(STM8S903) || defined (STM8AF52Ax) || defined (STM8AF62Ax) ITC_IRQ_UART1_TX = (uint8_t)17, /*!< UART1 TX interrupt */ ITC_IRQ_UART1_RX = (uint8_t)18, /*!< UART1 RX interrupt */ -#endif /*STM8S208 or STM8S207 or STM8S007 or STM8S103 or STM8S003 or STM8S001 or STM8S903 or STM8AF52Ax or STM8AF62Ax */ +#endif /*STM8S208 or STM8S207 or STM8S007 or STM8S103 or STM8S003 or STM8S001 or STM8S903 or STM8AF52Ax or STM8AF62Ax */ #if defined(STM8AF622x) ITC_IRQ_UART4_TX = (uint8_t)17, /*!< UART4 TX interrupt */ ITC_IRQ_UART4_RX = (uint8_t)18, /*!< UART4 RX interrupt */ #endif /*STM8AF622x */ - + ITC_IRQ_I2C = (uint8_t)19, /*!< I2C interrupt */ - + #if defined(STM8S105) || defined(STM8S005) || defined(STM8AF626x) ITC_IRQ_UART2_TX = (uint8_t)20, /*!< USART2 TX interrupt */ ITC_IRQ_UART2_RX = (uint8_t)21, /*!< USART2 RX interrupt */ @@ -100,7 +100,7 @@ typedef enum { ITC_IRQ_ADC2 = (uint8_t)22, /*!< ADC2 interrupt */ #endif /*STM8S208 or STM8S207 or STM8AF52Ax or STM8AF62Ax */ -#if defined(STM8S105) || defined(STM8S005) || defined(STM8S103) || defined(STM8S003) || defined(STM8S001) || defined(STM8S903) || defined(STM8AF626x) || defined(STM8AF622x) +#if defined(STM8S105) || defined(STM8S005) || defined(STM8S103) || defined(STM8S003) || defined(STM8S001) || defined(STM8S903) || defined(STM8AF626x) || defined(STM8AF622x) ITC_IRQ_ADC1 = (uint8_t)22, /*!< ADC1 interrupt */ #endif /*STM8S105 or STM8S005 or STM8S003 or STM8S103 or STM8S001 or STM8S903 or STM8AF626x or STM8AF622x */ diff --git a/src/firmware/tsdz2/stm8s/stm8s_iwdg.h b/code/firmware/lib/stm8s/include/stm8/stm8s_iwdg.h similarity index 99% rename from src/firmware/tsdz2/stm8s/stm8s_iwdg.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_iwdg.h index 56697724..02b7452d 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_iwdg.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_iwdg.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. diff --git a/src/firmware/tsdz2/stm8s/stm8s_rst.h b/code/firmware/lib/stm8s/include/stm8/stm8s_rst.h similarity index 99% rename from src/firmware/tsdz2/stm8s/stm8s_rst.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_rst.h index 7b199e01..0364c705 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_rst.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_rst.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. diff --git a/src/firmware/tsdz2/stm8s/stm8s_spi.h b/code/firmware/lib/stm8s/include/stm8/stm8s_spi.h similarity index 97% rename from src/firmware/tsdz2/stm8s/stm8s_spi.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_spi.h index 8ace561e..7abbf06d 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_spi.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_spi.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. @@ -180,14 +180,14 @@ typedef enum ((MODE) == SPI_DATADIRECTION_1LINE_TX)) /** - * @brief Macro used by the assert_param function in order to check the mode + * @brief Macro used by the assert_param function in order to check the mode * half duplex data direction values */ #define IS_SPI_DIRECTION_OK(DIRECTION) (((DIRECTION) == SPI_DIRECTION_RX) || \ ((DIRECTION) == SPI_DIRECTION_TX)) /** - * @brief Macro used by the assert_param function in order to check the NSS + * @brief Macro used by the assert_param function in order to check the NSS * management values */ #define IS_SPI_SLAVEMANAGEMENT_OK(NSS) (((NSS) == SPI_NSS_SOFT) || \ @@ -231,21 +231,21 @@ typedef enum ((CLKPHA) == SPI_CLOCKPHASE_2EDGE)) /** - * @brief Macro used by the assert_param function in order to check the first + * @brief Macro used by the assert_param function in order to check the first * bit to be transmited values */ #define IS_SPI_FIRSTBIT_OK(BIT) (((BIT) == SPI_FIRSTBIT_MSB) || \ ((BIT) == SPI_FIRSTBIT_LSB)) /** - * @brief Macro used by the assert_param function in order to check the CRC + * @brief Macro used by the assert_param function in order to check the CRC * Transmit/Receive */ #define IS_SPI_CRC_OK(CRC) (((CRC) == SPI_CRC_TX) || \ ((CRC) == SPI_CRC_RX)) /** - * @brief Macro used by the assert_param function in order to check the + * @brief Macro used by the assert_param function in order to check the * different flags values */ #define IS_SPI_FLAGS_OK(FLAG) (((FLAG) == SPI_FLAG_OVR) || \ @@ -257,15 +257,15 @@ typedef enum ((FLAG) == SPI_FLAG_BSY)) /** - * @brief Macro used by the assert_param function in order to check the - * different sensitivity values for the flag that can be cleared + * @brief Macro used by the assert_param function in order to check the + * different sensitivity values for the flag that can be cleared * by writing 0 */ #define IS_SPI_CLEAR_FLAGS_OK(FLAG) (((FLAG) == SPI_FLAG_CRCERR) || \ ((FLAG) == SPI_FLAG_WKUP)) /** - * @brief Macro used by the assert_param function in order to check the + * @brief Macro used by the assert_param function in order to check the * different sensitivity values for the Interrupts */ #define IS_SPI_CONFIG_IT_OK(Interrupt) (((Interrupt) == SPI_IT_TXE) || \ @@ -274,7 +274,7 @@ typedef enum ((Interrupt) == SPI_IT_WKUP)) /** - * @brief Macro used by the assert_param function in order to check the + * @brief Macro used by the assert_param function in order to check the * different sensitivity values for the pending bit */ #define IS_SPI_GET_IT_OK(ITPendingBit) (((ITPendingBit) == SPI_IT_OVR) || \ @@ -285,7 +285,7 @@ typedef enum ((ITPendingBit) == SPI_IT_RXNE)) /** - * @brief Macro used by the assert_param function in order to check the + * @brief Macro used by the assert_param function in order to check the * different sensitivity values for the pending bit that can be cleared * by writing 0 */ @@ -300,11 +300,11 @@ typedef enum * @{ */ void SPI_DeInit(void); -void SPI_Init(SPI_FirstBit_TypeDef FirstBit, - SPI_BaudRatePrescaler_TypeDef BaudRatePrescaler, - SPI_Mode_TypeDef Mode, SPI_ClockPolarity_TypeDef ClockPolarity, - SPI_ClockPhase_TypeDef ClockPhase, - SPI_DataDirection_TypeDef Data_Direction, +void SPI_Init(SPI_FirstBit_TypeDef FirstBit, + SPI_BaudRatePrescaler_TypeDef BaudRatePrescaler, + SPI_Mode_TypeDef Mode, SPI_ClockPolarity_TypeDef ClockPolarity, + SPI_ClockPhase_TypeDef ClockPhase, + SPI_DataDirection_TypeDef Data_Direction, SPI_NSS_TypeDef Slave_Management, uint8_t CRCPolynomial); void SPI_Cmd(FunctionalState NewState); void SPI_ITConfig(SPI_IT_TypeDef SPI_IT, FunctionalState NewState); @@ -331,6 +331,6 @@ void SPI_ClearITPendingBit(SPI_IT_TypeDef SPI_IT); /** * @} */ - + /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/src/firmware/tsdz2/stm8s/stm8s_tim1.h b/code/firmware/lib/stm8s/include/stm8/stm8s_tim1.h similarity index 93% rename from src/firmware/tsdz2/stm8s/stm8s_tim1.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_tim1.h index 9f1819be..3c6b1a79 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_tim1.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_tim1.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. @@ -35,7 +35,7 @@ /** @addtogroup STM8S_StdPeriph_Driver * @{ */ - + /** @addtogroup TIM1_Exported_Types * @{ */ @@ -492,62 +492,62 @@ typedef enum */ void TIM1_DeInit(void); -void TIM1_TimeBaseInit(uint16_t TIM1_Prescaler, +void TIM1_TimeBaseInit(uint16_t TIM1_Prescaler, TIM1_CounterMode_TypeDef TIM1_CounterMode, uint16_t TIM1_Period, uint8_t TIM1_RepetitionCounter); -void TIM1_OC1Init(TIM1_OCMode_TypeDef TIM1_OCMode, - TIM1_OutputState_TypeDef TIM1_OutputState, - TIM1_OutputNState_TypeDef TIM1_OutputNState, - uint16_t TIM1_Pulse, TIM1_OCPolarity_TypeDef TIM1_OCPolarity, - TIM1_OCNPolarity_TypeDef TIM1_OCNPolarity, - TIM1_OCIdleState_TypeDef TIM1_OCIdleState, +void TIM1_OC1Init(TIM1_OCMode_TypeDef TIM1_OCMode, + TIM1_OutputState_TypeDef TIM1_OutputState, + TIM1_OutputNState_TypeDef TIM1_OutputNState, + uint16_t TIM1_Pulse, TIM1_OCPolarity_TypeDef TIM1_OCPolarity, + TIM1_OCNPolarity_TypeDef TIM1_OCNPolarity, + TIM1_OCIdleState_TypeDef TIM1_OCIdleState, TIM1_OCNIdleState_TypeDef TIM1_OCNIdleState); -void TIM1_OC2Init(TIM1_OCMode_TypeDef TIM1_OCMode, - TIM1_OutputState_TypeDef TIM1_OutputState, - TIM1_OutputNState_TypeDef TIM1_OutputNState, - uint16_t TIM1_Pulse, TIM1_OCPolarity_TypeDef TIM1_OCPolarity, - TIM1_OCNPolarity_TypeDef TIM1_OCNPolarity, - TIM1_OCIdleState_TypeDef TIM1_OCIdleState, +void TIM1_OC2Init(TIM1_OCMode_TypeDef TIM1_OCMode, + TIM1_OutputState_TypeDef TIM1_OutputState, + TIM1_OutputNState_TypeDef TIM1_OutputNState, + uint16_t TIM1_Pulse, TIM1_OCPolarity_TypeDef TIM1_OCPolarity, + TIM1_OCNPolarity_TypeDef TIM1_OCNPolarity, + TIM1_OCIdleState_TypeDef TIM1_OCIdleState, TIM1_OCNIdleState_TypeDef TIM1_OCNIdleState); -void TIM1_OC3Init(TIM1_OCMode_TypeDef TIM1_OCMode, - TIM1_OutputState_TypeDef TIM1_OutputState, - TIM1_OutputNState_TypeDef TIM1_OutputNState, - uint16_t TIM1_Pulse, TIM1_OCPolarity_TypeDef TIM1_OCPolarity, - TIM1_OCNPolarity_TypeDef TIM1_OCNPolarity, - TIM1_OCIdleState_TypeDef TIM1_OCIdleState, +void TIM1_OC3Init(TIM1_OCMode_TypeDef TIM1_OCMode, + TIM1_OutputState_TypeDef TIM1_OutputState, + TIM1_OutputNState_TypeDef TIM1_OutputNState, + uint16_t TIM1_Pulse, TIM1_OCPolarity_TypeDef TIM1_OCPolarity, + TIM1_OCNPolarity_TypeDef TIM1_OCNPolarity, + TIM1_OCIdleState_TypeDef TIM1_OCIdleState, TIM1_OCNIdleState_TypeDef TIM1_OCNIdleState); -void TIM1_OC4Init(TIM1_OCMode_TypeDef TIM1_OCMode, +void TIM1_OC4Init(TIM1_OCMode_TypeDef TIM1_OCMode, TIM1_OutputState_TypeDef TIM1_OutputState, uint16_t TIM1_Pulse, - TIM1_OCPolarity_TypeDef TIM1_OCPolarity, + TIM1_OCPolarity_TypeDef TIM1_OCPolarity, TIM1_OCIdleState_TypeDef TIM1_OCIdleState); -void TIM1_BDTRConfig(TIM1_OSSIState_TypeDef TIM1_OSSIState, +void TIM1_BDTRConfig(TIM1_OSSIState_TypeDef TIM1_OSSIState, TIM1_LockLevel_TypeDef TIM1_LockLevel, uint8_t TIM1_DeadTime, - TIM1_BreakState_TypeDef TIM1_Break, - TIM1_BreakPolarity_TypeDef TIM1_BreakPolarity, + TIM1_BreakState_TypeDef TIM1_Break, + TIM1_BreakPolarity_TypeDef TIM1_BreakPolarity, TIM1_AutomaticOutput_TypeDef TIM1_AutomaticOutput); -void TIM1_ICInit(TIM1_Channel_TypeDef TIM1_Channel, - TIM1_ICPolarity_TypeDef TIM1_ICPolarity, - TIM1_ICSelection_TypeDef TIM1_ICSelection, +void TIM1_ICInit(TIM1_Channel_TypeDef TIM1_Channel, + TIM1_ICPolarity_TypeDef TIM1_ICPolarity, + TIM1_ICSelection_TypeDef TIM1_ICSelection, TIM1_ICPSC_TypeDef TIM1_ICPrescaler, uint8_t TIM1_ICFilter); -void TIM1_PWMIConfig(TIM1_Channel_TypeDef TIM1_Channel, - TIM1_ICPolarity_TypeDef TIM1_ICPolarity, - TIM1_ICSelection_TypeDef TIM1_ICSelection, +void TIM1_PWMIConfig(TIM1_Channel_TypeDef TIM1_Channel, + TIM1_ICPolarity_TypeDef TIM1_ICPolarity, + TIM1_ICSelection_TypeDef TIM1_ICSelection, TIM1_ICPSC_TypeDef TIM1_ICPrescaler, uint8_t TIM1_ICFilter); void TIM1_Cmd(FunctionalState NewState); void TIM1_CtrlPWMOutputs(FunctionalState NewState); void TIM1_ITConfig(TIM1_IT_TypeDef TIM1_IT, FunctionalState NewState); void TIM1_InternalClockConfig(void); -void TIM1_ETRClockMode1Config(TIM1_ExtTRGPSC_TypeDef TIM1_ExtTRGPrescaler, - TIM1_ExtTRGPolarity_TypeDef TIM1_ExtTRGPolarity, +void TIM1_ETRClockMode1Config(TIM1_ExtTRGPSC_TypeDef TIM1_ExtTRGPrescaler, + TIM1_ExtTRGPolarity_TypeDef TIM1_ExtTRGPolarity, uint8_t ExtTRGFilter); -void TIM1_ETRClockMode2Config(TIM1_ExtTRGPSC_TypeDef TIM1_ExtTRGPrescaler, - TIM1_ExtTRGPolarity_TypeDef TIM1_ExtTRGPolarity, +void TIM1_ETRClockMode2Config(TIM1_ExtTRGPSC_TypeDef TIM1_ExtTRGPrescaler, + TIM1_ExtTRGPolarity_TypeDef TIM1_ExtTRGPolarity, uint8_t ExtTRGFilter); -void TIM1_ETRConfig(TIM1_ExtTRGPSC_TypeDef TIM1_ExtTRGPrescaler, - TIM1_ExtTRGPolarity_TypeDef TIM1_ExtTRGPolarity, +void TIM1_ETRConfig(TIM1_ExtTRGPSC_TypeDef TIM1_ExtTRGPrescaler, + TIM1_ExtTRGPolarity_TypeDef TIM1_ExtTRGPolarity, uint8_t ExtTRGFilter); -void TIM1_TIxExternalClockConfig(TIM1_TIxExternalCLK1Source_TypeDef TIM1_TIxExternalCLKSource, - TIM1_ICPolarity_TypeDef TIM1_ICPolarity, +void TIM1_TIxExternalClockConfig(TIM1_TIxExternalCLK1Source_TypeDef TIM1_TIxExternalCLKSource, + TIM1_ICPolarity_TypeDef TIM1_ICPolarity, uint8_t ICFilter); void TIM1_SelectInputTrigger(TIM1_TS_TypeDef TIM1_InputTriggerSource); void TIM1_UpdateDisableConfig(FunctionalState NewState); @@ -557,8 +557,8 @@ void TIM1_SelectOnePulseMode(TIM1_OPMode_TypeDef TIM1_OPMode); void TIM1_SelectOutputTrigger(TIM1_TRGOSource_TypeDef TIM1_TRGOSource); void TIM1_SelectSlaveMode(TIM1_SlaveMode_TypeDef TIM1_SlaveMode); void TIM1_SelectMasterSlaveMode(FunctionalState NewState); -void TIM1_EncoderInterfaceConfig(TIM1_EncoderMode_TypeDef TIM1_EncoderMode, - TIM1_ICPolarity_TypeDef TIM1_IC1Polarity, +void TIM1_EncoderInterfaceConfig(TIM1_EncoderMode_TypeDef TIM1_EncoderMode, + TIM1_ICPolarity_TypeDef TIM1_IC1Polarity, TIM1_ICPolarity_TypeDef TIM1_IC2Polarity); void TIM1_PrescalerConfig(uint16_t Prescaler, TIM1_PSCReloadMode_TypeDef TIM1_PSCReloadMode); void TIM1_CounterModeConfig(TIM1_CounterMode_TypeDef TIM1_CounterMode); @@ -620,4 +620,4 @@ void TIM1_ClearITPendingBit(TIM1_IT_TypeDef TIM1_IT); */ -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ \ No newline at end of file +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/src/firmware/tsdz2/stm8s/stm8s_tim2.h b/code/firmware/lib/stm8s/include/stm8/stm8s_tim2.h similarity index 99% rename from src/firmware/tsdz2/stm8s/stm8s_tim2.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_tim2.h index 981825d9..517b3553 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_tim2.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_tim2.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. @@ -269,7 +269,7 @@ typedef enum ((FLAG) == TIM2_FLAG_CC3OF)) #define IS_TIM2_CLEAR_FLAG_OK(FLAG) ((((uint16_t)(FLAG) & 0xF1F0) == 0x0000) && ((uint16_t)(FLAG) != 0x0000)) - + /** * @} */ diff --git a/src/firmware/tsdz2/stm8s/stm8s_tim3.h b/code/firmware/lib/stm8s/include/stm8/stm8s_tim3.h similarity index 99% rename from src/firmware/tsdz2/stm8s/stm8s_tim3.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_tim3.h index 97374bff..ab680015 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_tim3.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_tim3.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. diff --git a/src/firmware/tsdz2/stm8s/stm8s_tim4.h b/code/firmware/lib/stm8s/include/stm8/stm8s_tim4.h similarity index 99% rename from src/firmware/tsdz2/stm8s/stm8s_tim4.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_tim4.h index 7710f48e..7a2ad1d6 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_tim4.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_tim4.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. diff --git a/src/firmware/tsdz2/stm8s/stm8s_tim5.h b/code/firmware/lib/stm8s/include/stm8/stm8s_tim5.h similarity index 99% rename from src/firmware/tsdz2/stm8s/stm8s_tim5.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_tim5.h index bb6386be..c3fc9ca1 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_tim5.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_tim5.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. @@ -275,7 +275,7 @@ typedef enum ((SOURCE) == TIM5_TRGOSOURCE_OC1) || \ ((SOURCE) == TIM5_TRGOSOURCE_OC1REF) || \ ((SOURCE) == TIM5_TRGOSOURCE_OC2REF)) - + /** TIM5 Flags */ typedef enum { @@ -320,7 +320,7 @@ typedef enum ((MODE) == TIM5_SLAVEMODE_GATED) || \ ((MODE) == TIM5_SLAVEMODE_TRIGGER) || \ ((MODE) == TIM5_SLAVEMODE_EXTERNAL1)) - + /** * @brief TIM5 Internal Trigger Selection */ @@ -360,7 +360,7 @@ typedef enum (((MODE) == TIM5_ENCODERMODE_TI1) || \ ((MODE) == TIM5_ENCODERMODE_TI2) || \ ((MODE) == TIM5_ENCODERMODE_TI12)) - + /** * @brief TIM5 External Trigger Prescaler */ @@ -380,7 +380,7 @@ typedef enum ((PRESCALER) == TIM5_EXTTRGPSC_DIV2) || \ ((PRESCALER) == TIM5_EXTTRGPSC_DIV4) || \ ((PRESCALER) == TIM5_EXTTRGPSC_DIV8)) - + /** * @brief TIM5 External Trigger Polarity */ @@ -396,7 +396,7 @@ typedef enum #define IS_TIM5_EXT_POLARITY_OK(POLARITY) \ (((POLARITY) == TIM5_EXTTRGPOLARITY_INVERTED) || \ ((POLARITY) == TIM5_EXTTRGPOLARITY_NONINVERTED)) - + /** * @brief Macro TIM5 External Trigger Filter */ diff --git a/src/firmware/tsdz2/stm8s/stm8s_tim6.h b/code/firmware/lib/stm8s/include/stm8/stm8s_tim6.h similarity index 99% rename from src/firmware/tsdz2/stm8s/stm8s_tim6.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_tim6.h index 3b391f58..a659d91b 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_tim6.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_tim6.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. @@ -282,6 +282,6 @@ void TIM6_SelectSlaveMode(TIM6_SlaveMode_TypeDef TIM6_SlaveMode); /** * @} */ - + /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/src/firmware/tsdz2/stm8s/stm8s_uart1.h b/code/firmware/lib/stm8s/include/stm8/stm8s_uart1.h similarity index 98% rename from src/firmware/tsdz2/stm8s/stm8s_uart1.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_uart1.h index 7941b52f..f09b6cee 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_uart1.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_uart1.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. @@ -137,8 +137,8 @@ typedef enum { UART1_FLAG_TXE = (uint16_t)0x0080, /*!< Transmit Data Register * UART1_IT_TXE * UART1_IT_TC * UART1_IT_RXNE - * UART1_IT_IDLE - * UART1_IT_OR + * UART1_IT_IDLE + * UART1_IT_OR * - For the UART1_IT_PE value, X means the flag position in the CR1 register. * - For the UART1_IT_LBDF value, X means the flag position in the CR4 register. * Y: Flag position @@ -146,7 +146,7 @@ typedef enum { UART1_FLAG_TXE = (uint16_t)0x0080, /*!< Transmit Data Register * UART1_IT_TXE * UART1_IT_TC * UART1_IT_RXNE - * UART1_IT_IDLE + * UART1_IT_IDLE * UART1_IT_OR * UART1_IT_PE * - For the UART1_IT_LBDF value, Y means the flag position in the CR4 register. @@ -208,7 +208,7 @@ typedef enum { UART1_IT_TXE = (uint16_t)0x0277, /*!< Transmit interrupt * /** * @brief Macro used by the assert_param function in order to check the different - * sensitivity values for the SyncModes; it should exclude values such + * sensitivity values for the SyncModes; it should exclude values such * as UART1_CLOCK_ENABLE|UART1_CLOCK_DISABLE */ #define IS_UART1_SYNCMODE_OK(SyncMode) \ @@ -243,7 +243,7 @@ typedef enum { UART1_IT_TXE = (uint16_t)0x0277, /*!< Transmit interrupt * /** - * @brief Macro used by the assert_param function in order to check the different + * @brief Macro used by the assert_param function in order to check the different * sensitivity values for the Interrupts */ @@ -256,7 +256,7 @@ typedef enum { UART1_IT_TXE = (uint16_t)0x0277, /*!< Transmit interrupt * ((Interrupt) == UART1_IT_LBDF)) /** - * @brief Macro used by the assert function in order to check the different + * @brief Macro used by the assert function in order to check the different * sensitivity values for the pending bit */ #define IS_UART1_GET_IT_OK(ITPendingBit) \ @@ -269,7 +269,7 @@ typedef enum { UART1_IT_TXE = (uint16_t)0x0277, /*!< Transmit interrupt * ((ITPendingBit) == UART1_IT_PE)) /** - * @brief Macro used by the assert function in order to check the different + * @brief Macro used by the assert function in order to check the different * sensitivity values for the pending bit that can be cleared by writing 0 */ #define IS_UART1_CLEAR_IT_OK(ITPendingBit) \ @@ -294,7 +294,7 @@ typedef enum { UART1_IT_TXE = (uint16_t)0x0277, /*!< Transmit interrupt * ((WakeUp) == UART1_WAKEUP_ADDRESSMARK)) /** - * @brief Macro used by the assert_param function in order to check the different + * @brief Macro used by the assert_param function in order to check the different * sensitivity values for the LINBreakDetectionLengths */ #define IS_UART1_LINBREAKDETECTIONLENGTH_OK(LINBreakDetectionLength) \ @@ -343,8 +343,8 @@ typedef enum { UART1_IT_TXE = (uint16_t)0x0277, /*!< Transmit interrupt * */ void UART1_DeInit(void); -void UART1_Init(uint32_t BaudRate, UART1_WordLength_TypeDef WordLength, - UART1_StopBits_TypeDef StopBits, UART1_Parity_TypeDef Parity, +void UART1_Init(uint32_t BaudRate, UART1_WordLength_TypeDef WordLength, + UART1_StopBits_TypeDef StopBits, UART1_Parity_TypeDef Parity, UART1_SyncMode_TypeDef SyncMode, UART1_Mode_TypeDef Mode); void UART1_Cmd(FunctionalState NewState); void UART1_ITConfig(UART1_IT_TypeDef UART1_IT, FunctionalState NewState); diff --git a/src/firmware/tsdz2/stm8s/stm8s_uart2.h b/code/firmware/lib/stm8s/include/stm8/stm8s_uart2.h similarity index 98% rename from src/firmware/tsdz2/stm8s/stm8s_uart2.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_uart2.h index a8d75581..c6702a1e 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_uart2.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_uart2.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. @@ -161,8 +161,8 @@ typedef enum * UART2_IT_TXE * UART2_IT_TC * UART2_IT_RXNE - * UART2_IT_IDLE - * UART2_IT_OR + * UART2_IT_IDLE + * UART2_IT_OR * - For the UART2_IT_PE value, X means the flag position in the CR1 register. * - For the UART2_IT_LBDF value, X means the flag position in the CR4 register. * - For the UART2_IT_LHDF value, X means the flag position in the CR6 register. @@ -171,7 +171,7 @@ typedef enum * UART2_IT_TXE * UART2_IT_TC * UART2_IT_RXNE - * UART2_IT_IDLE + * UART2_IT_IDLE * UART2_IT_OR * UART2_IT_PE * - For the UART2_IT_LBDF value, Y means the flag position in the CR4 register. @@ -215,7 +215,7 @@ typedef enum { UART2_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * * sensitivity values for the MODEs possible combination should be one of * the following. */ - + #define IS_UART2_MODE_OK(Mode) \ (((Mode) == (uint8_t)UART2_MODE_RX_ENABLE) || \ ((Mode) == (uint8_t)UART2_MODE_RX_DISABLE) || \ @@ -237,7 +237,7 @@ typedef enum { UART2_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * /** * @brief Macro used by the assert_param function in order to check the different - * sensitivity values for the SyncModes; it should exclude values such + * sensitivity values for the SyncModes; it should exclude values such * as UART2_CLOCK_ENABLE|UART2_CLOCK_DISABLE */ #define IS_UART2_SYNCMODE_OK(SyncMode) \ @@ -247,7 +247,7 @@ typedef enum { UART2_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * (((SyncMode)&(((uint8_t)UART2_SYNCMODE_LASTBIT_DISABLE)|((uint8_t)UART2_SYNCMODE_LASTBIT_ENABLE))) == (((uint8_t)UART2_SYNCMODE_LASTBIT_DISABLE)|((uint8_t)UART2_SYNCMODE_LASTBIT_ENABLE))))) /** - * @brief Macro used by the assert_param function in order to check the + * @brief Macro used by the assert_param function in order to check the * different sensitivity values for the FLAGs */ #define IS_UART2_FLAG_OK(Flag) \ @@ -266,7 +266,7 @@ typedef enum { UART2_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * /** - * @brief Macro used by the assert_param function in order to check the + * @brief Macro used by the assert_param function in order to check the * different sensitivity values for the FLAGs that can be cleared by writing 0 */ #define IS_UART2_CLEAR_FLAG_OK(Flag) \ @@ -276,7 +276,7 @@ typedef enum { UART2_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * ((Flag) == UART2_FLAG_LBDF)) /** - * @brief Macro used by the assert_param function in order to check + * @brief Macro used by the assert_param function in order to check * the different sensitivity values for the Interrupts */ @@ -290,7 +290,7 @@ typedef enum { UART2_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * ((Interrupt) == UART2_IT_LBDF)) /** - * @brief Macro used by the assert function in order to check the different + * @brief Macro used by the assert function in order to check the different * sensitivity values for the pending bit */ #define IS_UART2_GET_IT_OK(ITPendingBit) \ @@ -304,7 +304,7 @@ typedef enum { UART2_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * ((ITPendingBit) == UART2_IT_PE)) /** - * @brief Macro used by the assert function in order to check the different + * @brief Macro used by the assert function in order to check the different * sensitivity values for the pending bit that can be cleared by writing 0 */ #define IS_UART2_CLEAR_IT_OK(ITPendingBit) \ @@ -362,7 +362,7 @@ typedef enum { UART2_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * /** - * @brief Macro used by the assert_param function in order to check the address + * @brief Macro used by the assert_param function in order to check the address * of the UART2 or UART node */ #define UART2_ADDRESS_MAX ((uint8_t)16) @@ -376,7 +376,7 @@ typedef enum { UART2_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * ((Mode) == UART2_LIN_MODE_SLAVE)) /** - * @brief Macro used by the assert_param function in order to check the LIN + * @brief Macro used by the assert_param function in order to check the LIN * automatic resynchronization mode */ #define IS_UART2_AUTOSYNC_OK(AutosyncMode) \ @@ -402,8 +402,8 @@ typedef enum { UART2_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * */ void UART2_DeInit(void); -void UART2_Init(uint32_t BaudRate, UART2_WordLength_TypeDef WordLength, - UART2_StopBits_TypeDef StopBits, UART2_Parity_TypeDef Parity, +void UART2_Init(uint32_t BaudRate, UART2_WordLength_TypeDef WordLength, + UART2_StopBits_TypeDef StopBits, UART2_Parity_TypeDef Parity, UART2_SyncMode_TypeDef SyncMode, UART2_Mode_TypeDef Mode); void UART2_Cmd(FunctionalState NewState); void UART2_ITConfig(UART2_IT_TypeDef UART2_IT, FunctionalState NewState); @@ -411,8 +411,8 @@ void UART2_HalfDuplexCmd(FunctionalState NewState); void UART2_IrDAConfig(UART2_IrDAMode_TypeDef UART2_IrDAMode); void UART2_IrDACmd(FunctionalState NewState); void UART2_LINBreakDetectionConfig(UART2_LINBreakDetectionLength_TypeDef UART2_LINBreakDetectionLength); -void UART2_LINConfig(UART2_LinMode_TypeDef UART2_Mode, - UART2_LinAutosync_TypeDef UART2_Autosync, +void UART2_LINConfig(UART2_LinMode_TypeDef UART2_Mode, + UART2_LinAutosync_TypeDef UART2_Autosync, UART2_LinDivUp_TypeDef UART2_DivUp); void UART2_LINCmd(FunctionalState NewState); void UART2_SmartCardCmd(FunctionalState NewState); @@ -442,6 +442,6 @@ void UART2_ClearITPendingBit(UART2_IT_TypeDef UART2_IT); /** * @} */ - + /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/src/firmware/tsdz2/stm8s/stm8s_uart3.h b/code/firmware/lib/stm8s/include/stm8/stm8s_uart3.h similarity index 97% rename from src/firmware/tsdz2/stm8s/stm8s_uart3.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_uart3.h index 4a100cae..cee2f132 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_uart3.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_uart3.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. @@ -137,8 +137,8 @@ typedef enum * UART3_IT_TXE * UART3_IT_TC * UART3_IT_RXNE - * UART3_IT_IDLE - * UART3_IT_OR + * UART3_IT_IDLE + * UART3_IT_OR * - For the UART3_IT_PE value, X means the flag position in the CR1 register. * - For the UART3_IT_LBDF value, X means the flag position in the CR4 register. * - For the UART3_IT_LHDF value, X means the flag position in the CR6 register. @@ -147,7 +147,7 @@ typedef enum * UART3_IT_TXE * UART3_IT_TC * UART3_IT_RXNE - * UART3_IT_IDLE + * UART3_IT_IDLE * UART3_IT_OR * UART3_IT_PE * - For the UART3_IT_LBDF value, Y means the flag position in the CR4 register. @@ -184,7 +184,7 @@ typedef enum { UART3_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * */ /** - * @brief Macro used by the assert_param function in order to check the + * @brief Macro used by the assert_param function in order to check the * different sensitivity values for the FLAGs */ #define IS_UART3_FLAG_OK(Flag) \ @@ -203,7 +203,7 @@ typedef enum { UART3_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * /** - * @brief Macro used by the assert_param function in order to check the + * @brief Macro used by the assert_param function in order to check the * different sensitivity values for the FLAGs that can be cleared by writing 0 */ #define IS_UART3_CLEAR_FLAG_OK(Flag) \ @@ -213,7 +213,7 @@ typedef enum { UART3_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * ((Flag) == UART3_FLAG_LBDF)) /** - * @brief Macro used by the assert_param function in order to check the + * @brief Macro used by the assert_param function in order to check the * different sensitivity values for the Interrupts */ @@ -227,7 +227,7 @@ typedef enum { UART3_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * ((Interrupt) == UART3_IT_LBDF)) /** - * @brief Macro used by the assert function in order to check the different + * @brief Macro used by the assert function in order to check the different * sensitivity values for the pending bit */ #define IS_UART3_GET_IT_OK(ITPendingBit) \ @@ -241,7 +241,7 @@ typedef enum { UART3_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * ((ITPendingBit) == UART3_IT_PE)) /** - * @brief Macro used by the assert function in order to check the different + * @brief Macro used by the assert function in order to check the different * sensitivity values for the pending bit that can be cleared by writing 0 */ #define IS_UART3_CLEAR_IT_OK(ITPendingBit) \ @@ -307,13 +307,13 @@ typedef enum { UART3_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * ((Parity) == UART3_PARITY_ODD )) /** - * @brief Macro used by the assert_param function in order to check the maximum + * @brief Macro used by the assert_param function in order to check the maximum * baudrate value */ #define IS_UART3_BAUDRATE_OK(NUM) ((NUM) <= (uint32_t)625000) /** - * @brief Macro used by the assert_param function in order to check the address + * @brief Macro used by the assert_param function in order to check the address * of the UART3 or UART node */ #define UART3_ADDRESS_MAX ((uint8_t)16) @@ -327,7 +327,7 @@ typedef enum { UART3_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * ((Mode) == UART3_LIN_MODE_SLAVE)) /** - * @brief Macro used by the assert_param function in order to check the LIN + * @brief Macro used by the assert_param function in order to check the LIN * automatic resynchronization mode */ #define IS_UART3_AUTOSYNC_OK(AutosyncMode) \ @@ -335,7 +335,7 @@ typedef enum { UART3_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * ((AutosyncMode) == UART3_LIN_AUTOSYNC_DISABLE)) /** - * @brief Macro used by the assert_param function in order to check the LIN + * @brief Macro used by the assert_param function in order to check the LIN * divider update method */ #define IS_UART3_DIVUP_OK(DivupMethod) \ @@ -353,14 +353,14 @@ typedef enum { UART3_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * */ void UART3_DeInit(void); -void UART3_Init(uint32_t BaudRate, UART3_WordLength_TypeDef WordLength, - UART3_StopBits_TypeDef StopBits, UART3_Parity_TypeDef Parity, +void UART3_Init(uint32_t BaudRate, UART3_WordLength_TypeDef WordLength, + UART3_StopBits_TypeDef StopBits, UART3_Parity_TypeDef Parity, UART3_Mode_TypeDef Mode); void UART3_Cmd(FunctionalState NewState); void UART3_ITConfig(UART3_IT_TypeDef UART3_IT, FunctionalState NewState); void UART3_LINBreakDetectionConfig(UART3_LINBreakDetectionLength_TypeDef UART3_LINBreakDetectionLength); -void UART3_LINConfig(UART3_LinMode_TypeDef UART3_Mode, - UART3_LinAutosync_TypeDef UART3_Autosync, +void UART3_LINConfig(UART3_LinMode_TypeDef UART3_Mode, + UART3_LinAutosync_TypeDef UART3_Autosync, UART3_LinDivUp_TypeDef UART3_DivUp); void UART3_LINCmd(FunctionalState NewState); void UART3_ReceiverWakeUpCmd(FunctionalState NewState); diff --git a/src/firmware/tsdz2/stm8s/stm8s_uart4.h b/code/firmware/lib/stm8s/include/stm8/stm8s_uart4.h similarity index 98% rename from src/firmware/tsdz2/stm8s/stm8s_uart4.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_uart4.h index 197bd74e..b8d8d6c8 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_uart4.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_uart4.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. @@ -161,8 +161,8 @@ typedef enum * UART4_IT_TXE * UART4_IT_TC * UART4_IT_RXNE - * UART4_IT_IDLE - * UART4_IT_OR + * UART4_IT_IDLE + * UART4_IT_OR * - For the UART4_IT_PE value, X means the flag position in the CR1 register. * - For the UART4_IT_LBDF value, X means the flag position in the CR4 register. * - For the UART4_IT_LHDF value, X means the flag position in the CR6 register. @@ -171,7 +171,7 @@ typedef enum * UART4_IT_TXE * UART4_IT_TC * UART4_IT_RXNE - * UART4_IT_IDLE + * UART4_IT_IDLE * UART4_IT_OR * UART4_IT_PE * - For the UART4_IT_LBDF value, Y means the flag position in the CR4 register. @@ -214,7 +214,7 @@ typedef enum { UART4_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * * sensitivity values for the MODEs possible combination should be one of * the following. */ - + #define IS_UART4_MODE_OK(Mode) \ (((Mode) == (uint8_t)UART4_MODE_RX_ENABLE) || \ ((Mode) == (uint8_t)UART4_MODE_RX_DISABLE) || \ @@ -236,7 +236,7 @@ typedef enum { UART4_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * /** * @brief Macro used by the assert_param function in order to check the different - * sensitivity values for the SyncModes; it should exclude values such + * sensitivity values for the SyncModes; it should exclude values such * as UART4_CLOCK_ENABLE|UART4_CLOCK_DISABLE */ #define IS_UART4_SYNCMODE_OK(SyncMode) \ @@ -246,7 +246,7 @@ typedef enum { UART4_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * (((SyncMode)&(((uint8_t)UART4_SYNCMODE_LASTBIT_DISABLE)|((uint8_t)UART4_SYNCMODE_LASTBIT_ENABLE))) == (((uint8_t)UART4_SYNCMODE_LASTBIT_DISABLE)|((uint8_t)UART4_SYNCMODE_LASTBIT_ENABLE))))) /** - * @brief Macro used by the assert_param function in order to check the + * @brief Macro used by the assert_param function in order to check the * different sensitivity values for the FLAGs */ #define IS_UART4_FLAG_OK(Flag) \ @@ -265,7 +265,7 @@ typedef enum { UART4_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * /** - * @brief Macro used by the assert_param function in order to check the + * @brief Macro used by the assert_param function in order to check the * different sensitivity values for the FLAGs that can be cleared by writing 0 */ #define IS_UART4_CLEAR_FLAG_OK(Flag) \ @@ -275,7 +275,7 @@ typedef enum { UART4_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * ((Flag) == UART4_FLAG_LBDF)) /** - * @brief Macro used by the assert_param function in order to check + * @brief Macro used by the assert_param function in order to check * the different sensitivity values for the Interrupts */ @@ -289,7 +289,7 @@ typedef enum { UART4_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * ((Interrupt) == UART4_IT_LBDF)) /** - * @brief Macro used by the assert function in order to check the different + * @brief Macro used by the assert function in order to check the different * sensitivity values for the pending bit */ #define IS_UART4_GET_IT_OK(ITPendingBit) \ @@ -303,7 +303,7 @@ typedef enum { UART4_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * ((ITPendingBit) == UART4_IT_PE)) /** - * @brief Macro used by the assert function in order to check the different + * @brief Macro used by the assert function in order to check the different * sensitivity values for the pending bit that can be cleared by writing 0 */ #define IS_UART4_CLEAR_IT_OK(ITPendingBit) \ @@ -360,7 +360,7 @@ typedef enum { UART4_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * #define IS_UART4_BAUDRATE_OK(NUM) ((NUM) <= (uint32_t)625000) /** - * @brief Macro used by the assert_param function in order to check the address + * @brief Macro used by the assert_param function in order to check the address * of the UART4 or UART node */ #define UART4_ADDRESS_MAX ((uint8_t)16) @@ -374,7 +374,7 @@ typedef enum { UART4_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * ((Mode) == UART4_LIN_MODE_SLAVE)) /** - * @brief Macro used by the assert_param function in order to check the LIN + * @brief Macro used by the assert_param function in order to check the LIN * automatic resynchronization mode */ #define IS_UART4_AUTOSYNC_OK(AutosyncMode) \ @@ -399,8 +399,8 @@ typedef enum { UART4_IT_TXE = (uint16_t)0x0277, /**< Transmit interrupt * * @{ */ void UART4_DeInit(void); -void UART4_Init(uint32_t BaudRate, UART4_WordLength_TypeDef WordLength, - UART4_StopBits_TypeDef StopBits, UART4_Parity_TypeDef Parity, +void UART4_Init(uint32_t BaudRate, UART4_WordLength_TypeDef WordLength, + UART4_StopBits_TypeDef StopBits, UART4_Parity_TypeDef Parity, UART4_SyncMode_TypeDef SyncMode, UART4_Mode_TypeDef Mode); void UART4_Cmd(FunctionalState NewState); void UART4_ITConfig(UART4_IT_TypeDef UART4_IT, FunctionalState NewState); @@ -408,8 +408,8 @@ void UART4_HalfDuplexCmd(FunctionalState NewState); void UART4_IrDAConfig(UART4_IrDAMode_TypeDef UART4_IrDAMode); void UART4_IrDACmd(FunctionalState NewState); void UART4_LINBreakDetectionConfig(UART4_LINBreakDetectionLength_TypeDef UART4_LINBreakDetectionLength); -void UART4_LINConfig(UART4_LinMode_TypeDef UART4_Mode, - UART4_LinAutosync_TypeDef UART4_Autosync, +void UART4_LINConfig(UART4_LinMode_TypeDef UART4_Mode, + UART4_LinAutosync_TypeDef UART4_Autosync, UART4_LinDivUp_TypeDef UART4_DivUp); void UART4_LINCmd(FunctionalState NewState); void UART4_SmartCardCmd(FunctionalState NewState); @@ -439,6 +439,6 @@ void UART4_ClearITPendingBit(UART4_IT_TypeDef UART4_IT); /** * @} */ - + /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/src/firmware/tsdz2/stm8s/stm8s_wwdg.h b/code/firmware/lib/stm8s/include/stm8/stm8s_wwdg.h similarity index 99% rename from src/firmware/tsdz2/stm8s/stm8s_wwdg.h rename to code/firmware/lib/stm8s/include/stm8/stm8s_wwdg.h index a2d3d1e4..30cb512c 100644 --- a/src/firmware/tsdz2/stm8s/stm8s_wwdg.h +++ b/code/firmware/lib/stm8s/include/stm8/stm8s_wwdg.h @@ -16,8 +16,8 @@ * * http://www.st.com/software_license_agreement_liberty_v2 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. diff --git a/src/firmware/adc.h b/code/firmware/src/adc.h similarity index 84% rename from src/firmware/adc.h rename to code/firmware/src/adc.h index b8c3ac6c..4155c512 100644 --- a/src/firmware/adc.h +++ b/code/firmware/src/adc.h @@ -1,24 +1,24 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#ifndef _ADC_H_ -#define _ADC_H_ -#include - -void adc_init(); -void adc_process(); - -uint8_t adc_get_throttle(); -uint16_t adc_get_torque(); - -uint16_t adc_get_temperature_contr(); -uint16_t adc_get_temperature_motor(); - -uint16_t adc_get_battery_voltage(); - -#endif +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#ifndef _ADC_H_ +#define _ADC_H_ +#include + +void adc_init(); +void adc_process(); + +uint8_t adc_get_throttle(); +uint16_t adc_get_torque(); + +uint16_t adc_get_temperature_contr(); +uint16_t adc_get_temperature_motor(); + +uint16_t adc_get_battery_voltage(); + +#endif diff --git a/code/firmware/src/app.c b/code/firmware/src/app.c new file mode 100644 index 00000000..b0c88f73 --- /dev/null +++ b/code/firmware/src/app.c @@ -0,0 +1,974 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "app.h" +#include "cfgstore.h" +#include "eventlog.h" +#include "fwconfig.h" +#include "lights.h" +#include "motor.h" +#include "sensors.h" +#include "system.h" +#include "throttle.h" +#include "uart.h" +#include "util.h" + +typedef struct +{ + assist_level_t level; + + // cached precomputed values + // --------------------------------- + + // speed + int32_t max_wheel_speed_rpm_x10; + + // pas + uint8_t keep_current_target_percent; + uint16_t keep_current_ramp_start_rpm_x10; + uint16_t keep_current_ramp_end_rpm_x10; + +} assist_level_data_t; + +static uint8_t assist_level; +static uint8_t operation_mode; +static uint16_t global_speed_limit_rpm; +static int32_t global_throttle_speed_limit_rpm_x10; + +static uint16_t lvc_voltage_x100; +static uint16_t lvc_ramp_down_start_voltage_x100; +static uint16_t lvc_ramp_down_end_voltage_x100; + +static assist_level_data_t assist_level_data; +static uint16_t speed_limit_ramp_interval_rpm_x10; + +static bool cruise_paused; +static int8_t temperature_contr_c; +static int8_t temperature_motor_c; + +static uint16_t ramp_up_current_interval_ms; +static uint32_t power_blocked_until_ms; + +static uint16_t pretension_cutoff_speed_rpm_x10; + +static bool lights_state = false; + +void apply_pas_cadence(uint8_t *target_current, uint8_t throttle_percent); +#if HAS_TORQUE_SENSOR +void apply_pas_torque(uint8_t *target_current); +#endif + +void apply_pretension(uint8_t *target_current); +void apply_cruise(uint8_t *target_current, uint8_t throttle_percent); +bool apply_throttle(uint8_t *target_current, uint8_t throttle_percent); +bool apply_speed_limit(uint8_t *target_current, uint8_t throttle_percent, bool pas_engaged, bool throttle_override); +bool apply_thermal_limit(uint8_t *target_current); +bool apply_low_voltage_limit(uint8_t *target_current); +bool apply_shift_sensor_interrupt(uint8_t *target_current); +bool apply_brake(uint8_t *target_current); +void apply_current_ramp_up(uint8_t *target_current, bool enable); +void apply_current_ramp_down(uint8_t *target_current, bool enable); + +bool check_power_block(); +void block_power_for(uint16_t ms); + +void reload_assist_params(); + +uint16_t convert_wheel_speed_kph_to_rpm(uint8_t speed_kph); + +void app_init() +{ + motor_disable(); + lights_disable(); + lights_set(g_config.lights_mode == LIGHTS_MODE_ALWAYS_ON); + + lvc_voltage_x100 = g_config.low_cut_off_v * 100u; + + uint16_t full_voltage_range_x100 = + EXPAND_U16(g_config.max_battery_x100v_u16h, g_config.max_battery_x100v_u16l) - lvc_voltage_x100; + uint16_t padded_voltage_range_x100 = + (uint16_t)(full_voltage_range_x100 * (100 - BATTERY_FULL_OFFSET_PERCENT - BATTERY_EMPTY_OFFSET_PERCENT) / 100); + + lvc_ramp_down_end_voltage_x100 = + (uint16_t)(lvc_voltage_x100 + (full_voltage_range_x100 * BATTERY_EMPTY_OFFSET_PERCENT / 100)); + lvc_ramp_down_start_voltage_x100 = + (uint16_t)(lvc_ramp_down_end_voltage_x100 + ((padded_voltage_range_x100 * LVC_RAMP_DOWN_OFFSET_PERCENT) / 100)); + + global_speed_limit_rpm = 0; + global_throttle_speed_limit_rpm_x10 = 0; + temperature_contr_c = 0; + temperature_motor_c = 0; + + ramp_up_current_interval_ms = (g_config.max_current_amps * 10u) / g_config.current_ramp_amps_s; + power_blocked_until_ms = 0; + + speed_limit_ramp_interval_rpm_x10 = convert_wheel_speed_kph_to_rpm(SPEED_LIMIT_RAMP_DOWN_INTERVAL_KPH) * 10; + + pretension_cutoff_speed_rpm_x10 = convert_wheel_speed_kph_to_rpm(g_config.pretension_speed_cutoff_kph) * 10; + + cruise_paused = true; + operation_mode = OPERATION_MODE_DEFAULT; + + app_set_wheel_max_speed_rpm(convert_wheel_speed_kph_to_rpm(g_config.max_speed_kph)); + app_set_assist_level(g_config.assist_startup_level); + reload_assist_params(); + + if (g_config.assist_mode_select == ASSIST_MODE_SELECT_BRAKE_BOOT && brake_is_activated()) + { + app_set_operation_mode(OPERATION_MODE_SPORT); + } +} + +void app_process() +{ + uint8_t target_current = 0; + uint8_t target_cadence = assist_level_data.level.max_cadence_percent; + uint8_t throttle_percent = throttle_map_response(throttle_read()); + + bool pas_engaged = false; + bool throttle_override = false; + + if (check_power_block()) + { + target_current = 0; + } + else if (assist_level == ASSIST_PUSH && g_config.use_push_walk) + { + target_current = 10; + } + else + { + apply_pretension(&target_current); + apply_pas_cadence(&target_current, throttle_percent); +#if HAS_TORQUE_SENSOR + apply_pas_torque(&target_current); +#endif // HAS_TORQUE_SENSOR + + pas_engaged = target_current > 0; + + apply_cruise(&target_current, throttle_percent); + + throttle_override = apply_throttle(&target_current, throttle_percent); + + // override target cadence if configured in assist level + if (throttle_override && (assist_level_data.level.flags & ASSIST_FLAG_PAS) && + (assist_level_data.level.flags & ASSIST_FLAG_OVERRIDE_CADENCE)) + { + target_cadence = THROTTLE_CADENCE_OVERRIDE_PERCENT; + } + } + + bool speed_limiting = apply_speed_limit(&target_current, throttle_percent, pas_engaged, throttle_override); + bool thermal_limiting = apply_thermal_limit(&target_current); + bool lvc_limiting = apply_low_voltage_limit(&target_current); + bool shift_limiting = +#if HAS_SHIFT_SENSOR_SUPPORT + apply_shift_sensor_interrupt(&target_current); +#else + false; +#endif + bool is_limiting = speed_limiting || thermal_limiting || lvc_limiting || shift_limiting; + bool is_braking = apply_brake(&target_current); + + apply_current_ramp_up(&target_current, is_limiting || !throttle_override); + apply_current_ramp_down(&target_current, !is_braking && !shift_limiting); + + motor_set_target_speed(target_cadence); + motor_set_target_current(target_current); + + if (target_current > 0) + { + motor_enable(); + } + else + { + motor_disable(); + } + + if (g_config.lights_mode == LIGHTS_MODE_DISABLED /*|| (motor_status() & MOTOR_ERROR_LVC) */) + { + lights_disable(); + } + else + { + lights_enable(); + } +} + +void app_set_assist_level(uint8_t level) +{ + if (assist_level != level) + { + if (assist_level == ASSIST_PUSH && g_config.use_push_walk) + { + // When releasig push walk mode pedals may have been rotating + // with the motor, block motor power for 2 seconds to prevent PAS + // sensor from incorrectly applying power if returning to a PAS level. + block_power_for(1000); + } + + assist_level = level; + eventlog_write_data(EVT_DATA_ASSIST_LEVEL, assist_level); + reload_assist_params(); + } +} + +void app_set_lights(bool on) +{ + if ( // it's ok to write ugly code if you say it's ugly... + (g_config.assist_mode_select == ASSIST_MODE_SELECT_LIGHTS) || + (assist_level == ASSIST_0 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS0_LIGHT) || + (assist_level == ASSIST_1 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS1_LIGHT) || + (assist_level == ASSIST_2 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS2_LIGHT) || + (assist_level == ASSIST_3 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS3_LIGHT) || + (assist_level == ASSIST_4 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS4_LIGHT) || + (assist_level == ASSIST_5 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS5_LIGHT) || + (assist_level == ASSIST_6 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS6_LIGHT) || + (assist_level == ASSIST_7 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS7_LIGHT) || + (assist_level == ASSIST_8 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS8_LIGHT) || + (assist_level == ASSIST_9 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS9_LIGHT)) + { + if (on) + { + app_set_operation_mode(OPERATION_MODE_SPORT); + } + else + { + app_set_operation_mode(OPERATION_MODE_DEFAULT); + } + } + else + { + if (g_config.lights_mode == LIGHTS_MODE_DEFAULT && lights_state != on) + { + lights_state = on; + eventlog_write_data(EVT_DATA_LIGHTS, on); + lights_set(on); + } + } +} + +void app_set_operation_mode(uint8_t mode) +{ + if (operation_mode != mode) + { + operation_mode = mode; + eventlog_write_data(EVT_DATA_OPERATION_MODE, operation_mode); + reload_assist_params(); + } +} + +void app_set_wheel_max_speed_rpm(uint16_t value) +{ + if (global_speed_limit_rpm != value) + { + global_speed_limit_rpm = value; + global_throttle_speed_limit_rpm_x10 = + ((int32_t)global_speed_limit_rpm * g_config.throttle_global_spd_lim_percent) / 10; + + eventlog_write_data(EVT_DATA_WHEEL_SPEED_PPM, value); + reload_assist_params(); + } +} + +uint8_t app_get_assist_level() +{ + return assist_level; +} + +uint8_t app_get_lights() +{ + return lights_state; +} + +uint8_t app_get_status_code() +{ + uint16_t motor = motor_status(); + + if (motor & MOTOR_ERROR_HALL_SENSOR) + { + return STATUS_ERROR_HALL_SENSOR; + } + + if (motor & MOTOR_ERROR_CURRENT_SENSE) + { + return STATUS_ERROR_CURRENT_SENSE; + } + + if (motor & MOTOR_ERROR_POWER_RESET) + { + // Phase line error code reused, cause and meaning + // of MOTOR_ERROR_POWER_RESET triggered on bbs02 is currently unknown + return STATUS_ERROR_PHASE_LINE; + } + + if (!throttle_ok()) + { + return STATUS_ERROR_THROTTLE; + } + + if (!torque_sensor_ok()) + { + return STATUS_ERROR_TORQUE_SENSOR; + } + + if (temperature_motor_c > MAX_TEMPERATURE) + { + return STATUS_ERROR_MOTOR_OVER_TEMP; + } + + if (temperature_contr_c > MAX_TEMPERATURE) + { + return STATUS_ERROR_CONTROLLER_OVER_TEMP; + } + + // Disable LVC error since it is not shown on display in original firmware + // Uncomment if you want to enable + // if (motor & MOTOR_ERROR_LVC) + // { + // return STATUS_ERROR_LVC; + // } + + if (brake_is_activated()) + { + return STATUS_BRAKING; + } + + return STATUS_NORMAL; +} + +uint8_t app_get_temperature() +{ + int8_t temp_max = MAX(temperature_contr_c, temperature_motor_c); + + if (temp_max < 0) + { + return 0; + } + + return (uint8_t)temp_max; +} + +void apply_pretension(uint8_t *target_current) +{ + uint16_t current_speed_rpm_x10 = speed_sensor_get_rpm_x10(); + + if (g_config.use_speed_sensor && g_config.use_pretension && current_speed_rpm_x10 > pretension_cutoff_speed_rpm_x10) + { + *target_current = 1; + } + return; +} + +void apply_pas_cadence(uint8_t *target_current, uint8_t throttle_percent) +{ + if ((assist_level_data.level.flags & ASSIST_FLAG_PAS) && !(assist_level_data.level.flags & ASSIST_FLAG_PAS_TORQUE)) + { + if (pas_is_pedaling_forwards() && pas_get_pulse_counter() > g_config.pas_start_delay_pulses) + { + if (assist_level_data.level.flags & ASSIST_FLAG_PAS_VARIABLE) + { + uint8_t current = + (uint8_t)MAP16(throttle_percent, 0, 100, 0, assist_level_data.level.target_current_percent); + if (current > *target_current) + { + *target_current = current; + } + } + else + { + if (assist_level_data.level.target_current_percent > *target_current) + { + *target_current = assist_level_data.level.target_current_percent; + } + + // apply "keep current" ramp + if (g_config.pas_keep_current_percent < 100) + { + if (*target_current > assist_level_data.keep_current_target_percent && + pas_get_cadence_rpm_x10() > assist_level_data.keep_current_ramp_start_rpm_x10) + { + uint32_t cadence = + MIN(pas_get_cadence_rpm_x10(), assist_level_data.keep_current_ramp_end_rpm_x10); + + // ramp down current towards keep_current_target_percent with rpm above + // keep_current_ramp_start_rpm_x10 + *target_current = MAP32(cadence, // in + assist_level_data.keep_current_ramp_start_rpm_x10, // in_min + assist_level_data.keep_current_ramp_end_rpm_x10, // in_max + *target_current, // out_min + assist_level_data.keep_current_target_percent); // out_max + } + } + } + } + } +} + +#if HAS_TORQUE_SENSOR +void apply_pas_torque(uint8_t *target_current) +{ + if ((assist_level_data.level.flags & ASSIST_FLAG_PAS) && (assist_level_data.level.flags & ASSIST_FLAG_PAS_TORQUE)) + { + if (pas_is_pedaling_forwards() && + (pas_get_pulse_counter() > g_config.pas_start_delay_pulses || speed_sensor_is_moving())) + { + uint16_t torque_nm_x100 = torque_sensor_get_nm_x100(); + uint16_t cadence_rpm_x10 = pas_get_cadence_rpm_x10(); + if (cadence_rpm_x10 < TORQUE_POWER_LOWER_RPM_X10) + { + cadence_rpm_x10 = TORQUE_POWER_LOWER_RPM_X10; + } + + uint16_t pedal_power_w_x10 = (uint16_t)(((uint32_t)torque_nm_x100 * cadence_rpm_x10) / 955); + + // used in division below to calculate target current, + // clamp to 24V if no reading available (unexpected error). + uint16_t battery_voltage_x10 = MAX(motor_get_battery_voltage_x10(), 240); + + uint16_t target_current_amp_x100 = (uint16_t)(((uint32_t)10 * pedal_power_w_x10 * + assist_level_data.level.torque_amplification_factor_x10) / + battery_voltage_x10); + + uint16_t max_current_amp_x100 = g_config.max_current_amps * 100; + + // limit target to ensure no overflow in map result + if (target_current_amp_x100 > max_current_amp_x100) + { + target_current_amp_x100 = max_current_amp_x100; + } + uint8_t tmp_percent = (uint8_t)MAP32(target_current_amp_x100, 0, max_current_amp_x100, 0, 100); + + // minimum 1 percent current if pedaling + if (tmp_percent < 1) + { + tmp_percent = 1; + } + // limit to maximum assist current for set level + else if (tmp_percent > assist_level_data.level.target_current_percent) + { + tmp_percent = assist_level_data.level.target_current_percent; + } + + if (tmp_percent > *target_current) + { + *target_current = tmp_percent; + } + } + } +} +#endif + +void apply_cruise(uint8_t *target_current, uint8_t throttle_percent) +{ + static bool cruise_block_throttle_return = false; + + if ((assist_level_data.level.flags & ASSIST_FLAG_CRUISE) && throttle_ok()) + { + // pause cruise if brake activated + if (brake_is_activated()) + { + cruise_paused = true; + cruise_block_throttle_return = true; + } + + // pause cruise if started pedaling backwards + else if (pas_is_pedaling_backwards() && pas_get_pulse_counter() > CRUISE_DISENGAGE_PAS_PULSES) + { + cruise_paused = true; + cruise_block_throttle_return = true; + } + + // pause cruise if throttle touched while cruise active + else if (!cruise_paused && !cruise_block_throttle_return && throttle_percent > 0) + { + cruise_paused = true; + cruise_block_throttle_return = true; + } + + // unpause cruise if pedaling forward while engaging throttle > 50% + else if (cruise_paused && !cruise_block_throttle_return && throttle_percent > 50 && + pas_is_pedaling_forwards() && pas_get_pulse_counter() > CRUISE_ENGAGE_PAS_PULSES) + { + cruise_paused = false; + cruise_block_throttle_return = true; + } + + // reset flag tracking throttle to make sure throttle returns to idle position before engage/disenage cruise + // with throttle touch + else if (cruise_block_throttle_return && throttle_percent == 0) + { + cruise_block_throttle_return = false; + } + + if (cruise_paused) + { + *target_current = 0; + } + else + { + if (assist_level_data.level.target_current_percent > *target_current) + { + *target_current = assist_level_data.level.target_current_percent; + } + } + } +} + +bool apply_throttle(uint8_t *target_current, uint8_t throttle_percent) +{ + if ((assist_level_data.level.flags & ASSIST_FLAG_THROTTLE) && throttle_percent > 0 && throttle_ok()) + { + uint8_t current = (uint8_t)MAP16(throttle_percent, 0, 100, g_config.throttle_start_percent, + assist_level_data.level.max_throttle_current_percent); + + if (current >= *target_current) + { + *target_current = current; + return true; + } + } + + return false; +} + +bool apply_speed_limit(uint8_t *target_current, uint8_t throttle_percent, bool pas_engaged, bool throttle_override) +{ + static bool speed_limiting = false; + + if (!g_config.use_speed_sensor) + { + return false; + } + + // global throttle speed limit applies if enabled in configuration, PAS is not engaged and throttle is used + bool global_throttle_limit_active = + !pas_engaged && throttle_percent > 0 && g_config.throttle_global_spd_lim_percent > 0 && + (g_config.throttle_global_spd_lim_opt == THROTTLE_GLOBAL_SPEED_LIMIT_ENABLED || + (g_config.throttle_global_spd_lim_opt == THROTTLE_GLOBAL_SPEED_LIMIT_STD_LVLS && + operation_mode == OPERATION_MODE_DEFAULT)); + + bool throttle_speed_override_active = !global_throttle_limit_active && throttle_override && + (assist_level_data.level.flags & ASSIST_FLAG_PAS) && + (assist_level_data.level.flags & ASSIST_FLAG_OVERRIDE_SPEED); + + int32_t max_speed_rpm_x10; + if (global_throttle_limit_active) + { + // use configured global throttle override speed limit + max_speed_rpm_x10 = global_throttle_speed_limit_rpm_x10; + } + else if (throttle_speed_override_active) + { + // override assist level speed limit to global speed limit + max_speed_rpm_x10 = global_speed_limit_rpm * 10; + } + else + { + // normal operation, use configured assist level speed limit + max_speed_rpm_x10 = assist_level_data.max_wheel_speed_rpm_x10; + } + + int32_t max_speed_ramp_low_rpm_x10 = max_speed_rpm_x10 - speed_limit_ramp_interval_rpm_x10; + int32_t max_speed_ramp_high_rpm_x10 = max_speed_rpm_x10 + speed_limit_ramp_interval_rpm_x10; + + if (max_speed_rpm_x10 > 0) + { + int16_t current_speed_rpm_x10 = speed_sensor_get_rpm_x10(); + + if (current_speed_rpm_x10 < max_speed_ramp_low_rpm_x10) + { + // no limiting + if (speed_limiting) + { + speed_limiting = false; + eventlog_write_data(EVT_DATA_SPEED_LIMITING, 0); + } + } + else + { + if (!speed_limiting) + { + speed_limiting = true; + eventlog_write_data(EVT_DATA_SPEED_LIMITING, 1); + } + + if (current_speed_rpm_x10 > max_speed_ramp_high_rpm_x10) + { + if (*target_current > 1) + { + *target_current = 1; + return true; + } + } + else + { + // linear ramp down when approaching max speed. + uint8_t tmp = (uint8_t)MAP32(current_speed_rpm_x10, max_speed_ramp_low_rpm_x10, + max_speed_ramp_high_rpm_x10, *target_current, 1); + if (*target_current > tmp) + { + *target_current = tmp; + return true; + } + } + } + } + + return false; +} + +bool apply_thermal_limit(uint8_t *target_current) +{ + static uint32_t next_log_temp_ms = 10000; + + static bool temperature_limiting = false; + + int16_t temp_contr_x100 = temperature_contr_x100(); + temperature_contr_c = temp_contr_x100 / 100; + + int16_t temp_motor_x100 = temperature_motor_x100(); + temperature_motor_c = temp_motor_x100 / 100; + + int16_t max_temp_x100 = MAX(temp_contr_x100, temp_motor_x100); + int8_t max_temp = MAX(temperature_contr_c, temperature_motor_c); + + if (eventlog_is_enabled() && g_config.use_temperature_sensor && system_ms() > next_log_temp_ms) + { + next_log_temp_ms = system_ms() + 10000; + eventlog_write_data(EVT_DATA_TEMPERATURE, (uint16_t)temperature_motor_c << 8 | temperature_contr_c); + } + + if (max_temp >= (MAX_TEMPERATURE - MAX_TEMPERATURE_RAMP_DOWN_INTERVAL)) + { + if (!temperature_limiting) + { + temperature_limiting = true; + eventlog_write_data(EVT_DATA_THERMAL_LIMITING, 1); + } + + if (max_temp_x100 > MAX_TEMPERATURE * 100) + { + max_temp_x100 = MAX_TEMPERATURE * 100; + } + + uint8_t tmp = (uint8_t)MAP32(max_temp_x100, // value + (MAX_TEMPERATURE - MAX_TEMPERATURE_RAMP_DOWN_INTERVAL) * 100, // in_min + MAX_TEMPERATURE * 100, // in_max + 100, // out_min + MAX_TEMPERATURE_LOW_CURRENT_PERCENT // out_max + ); + + if (*target_current > tmp) + { + *target_current = tmp; + return true; + } + } + else + { + if (temperature_limiting) + { + temperature_limiting = false; + eventlog_write_data(EVT_DATA_THERMAL_LIMITING, 0); + } + } + + return false; +} + +bool apply_low_voltage_limit(uint8_t *target_current) +{ + static uint32_t next_log_volt_ms = 10000; + static bool lvc_limiting = false; + + static uint32_t next_voltage_reading_ms = 125; + static int32_t flt_min_bat_volt_x100 = 100 * 100; + + if (system_ms() > next_voltage_reading_ms) + { + next_voltage_reading_ms = system_ms() + 125; + int32_t voltage_reading_x100 = motor_get_battery_voltage_x10() * 10ul; + + if (voltage_reading_x100 < flt_min_bat_volt_x100) + { + flt_min_bat_volt_x100 = EXPONENTIAL_FILTER(flt_min_bat_volt_x100, voltage_reading_x100, 8); + } + + if (eventlog_is_enabled() && system_ms() > next_log_volt_ms) + { + next_log_volt_ms = system_ms() + 10000; + eventlog_write_data(EVT_DATA_VOLTAGE, (uint16_t)voltage_reading_x100); + } + } + + uint16_t voltage_x100 = flt_min_bat_volt_x100; + + if (voltage_x100 <= lvc_ramp_down_start_voltage_x100) + { + if (!lvc_limiting) + { + eventlog_write_data(EVT_DATA_LVC_LIMITING, voltage_x100); + lvc_limiting = true; + } + + if (voltage_x100 < lvc_voltage_x100) + { + voltage_x100 = lvc_voltage_x100; + } + + // Ramp down power until LVC_LOW_CURRENT_PERCENT when approaching LVC + uint8_t tmp = (uint8_t)MAP32(voltage_x100, // value + lvc_ramp_down_end_voltage_x100, // in_min + lvc_ramp_down_start_voltage_x100, // in_max + LVC_LOW_CURRENT_PERCENT, // out_min + 100 // out_max + ); + + if (*target_current > tmp) + { + *target_current = tmp; + return true; + } + } + + return false; +} + +#if HAS_SHIFT_SENSOR_SUPPORT +bool apply_shift_sensor_interrupt(uint8_t *target_current) +{ + static uint32_t shift_sensor_act_ms = 0; + static bool shift_sensor_last = false; + static bool shift_sensor_interrupting = false; + static bool shift_sensor_logged = false; + + // Exit immediately if shift interrupts disabled. + if (!g_config.use_shift_sensor) + { + return false; + } + + bool active = shift_sensor_is_activated(); + if (active) + { + // Check for new pulse from the gear sensor during shift interrupt + if (!shift_sensor_last && shift_sensor_interrupting) + { + // Consecutive gear change, do restart. + shift_sensor_interrupting = false; + } + if (!shift_sensor_interrupting) + { + uint16_t duration_ms = + EXPAND_U16(g_config.shift_interrupt_duration_ms_u16h, g_config.shift_interrupt_duration_ms_u16l); + shift_sensor_act_ms = system_ms() + duration_ms; + shift_sensor_interrupting = true; + } + shift_sensor_last = true; + } + else + { + shift_sensor_last = false; + } + + if (!shift_sensor_interrupting) + { + return false; + } + + if (system_ms() >= shift_sensor_act_ms) + { + // Shift is finished, reset function state. + shift_sensor_interrupting = false; + // Logging is skipped, unless current has been clamped during shift interrupt. + if (shift_sensor_logged) + { + shift_sensor_logged = false; + eventlog_write_data(EVT_DATA_SHIFT_SENSOR, 0); + } + return false; + } + + if ((*target_current) > g_config.shift_interrupt_current_threshold_percent) + { + if (!shift_sensor_logged) + { + // Logging only once per shifting interrupt. + shift_sensor_logged = true; + eventlog_write_data(EVT_DATA_SHIFT_SENSOR, 1); + } + // Set target current based on desired current threshold during shift. + *target_current = g_config.shift_interrupt_current_threshold_percent; + + return true; + } + + return false; +} +#endif + +bool apply_brake(uint8_t *target_current) +{ + bool is_braking = brake_is_activated(); + + if (g_config.lights_mode == LIGHTS_MODE_BRAKE_LIGHT) + { + lights_set(is_braking); + } + + if (is_braking) + { + *target_current = 0; + } + + return is_braking; +} + +void apply_current_ramp_up(uint8_t *target_current, bool enable) +{ + static uint8_t ramp_up_target_current = 0; + static uint32_t last_ramp_up_increment_ms = 0; + + if (enable && *target_current > ramp_up_target_current) + { + uint32_t now = system_ms(); + uint16_t time_diff = now - last_ramp_up_increment_ms; + + if (time_diff >= ramp_up_current_interval_ms) + { + ++ramp_up_target_current; + + if (last_ramp_up_increment_ms == 0) + { + last_ramp_up_increment_ms = now; + } + else + { + // offset for time overshoot to not accumulate large ramp error + last_ramp_up_increment_ms = now - (uint8_t)(time_diff - ramp_up_current_interval_ms); + } + } + + *target_current = ramp_up_target_current; + } + else + { + ramp_up_target_current = *target_current; + last_ramp_up_increment_ms = 0; + } +} + +void apply_current_ramp_down(uint8_t *target_current, bool enable) +{ + static uint8_t ramp_down_target_current = 0; + static uint32_t last_ramp_down_decrement_ms = 0; + + // apply fast ramp down if coming from high target current (> 50%) + if (enable && *target_current < ramp_down_target_current) + { + uint32_t now = system_ms(); + uint16_t time_diff = now - last_ramp_down_decrement_ms; + + if (time_diff >= 10) + { + uint8_t diff = ramp_down_target_current - *target_current; + + if (diff >= CURRENT_RAMP_DOWN_PERCENT_10MS) + { + ramp_down_target_current -= CURRENT_RAMP_DOWN_PERCENT_10MS; + } + else + { + ramp_down_target_current -= diff; + } + + if (last_ramp_down_decrement_ms == 0) + { + last_ramp_down_decrement_ms = now; + } + else + { + // offset for time overshoot to not accumulate large ramp error + last_ramp_down_decrement_ms = now - (uint8_t)(time_diff - 10); + } + } + + *target_current = ramp_down_target_current; + } + else + { + ramp_down_target_current = *target_current; + last_ramp_down_decrement_ms = 0; + } +} + +bool check_power_block() +{ + if (power_blocked_until_ms != 0) + { + // power block is active, check if time to release + if (system_ms() > power_blocked_until_ms) + { + power_blocked_until_ms = 0; + return false; + } + + return true; + } + + return false; +} + +void block_power_for(uint16_t ms) +{ + power_blocked_until_ms = system_ms() + ms; +} + +void reload_assist_params() +{ + if (assist_level < ASSIST_PUSH) + { + assist_level_data.level = g_config.assist_levels[operation_mode][assist_level]; + + assist_level_data.max_wheel_speed_rpm_x10 = + ((int32_t)global_speed_limit_rpm * assist_level_data.level.max_speed_percent) / 10; + + if (assist_level_data.level.flags & ASSIST_FLAG_PAS) + { + assist_level_data.keep_current_target_percent = + (uint8_t)((uint16_t)g_config.pas_keep_current_percent * assist_level_data.level.target_current_percent / + 100); + assist_level_data.keep_current_ramp_start_rpm_x10 = g_config.pas_keep_current_cadence_rpm * 10; + assist_level_data.keep_current_ramp_end_rpm_x10 = + (uint16_t)(((uint32_t)assist_level_data.level.max_cadence_percent * MAX_CADENCE_RPM_X10) / 100); + } + + // pause cruise if swiching level + cruise_paused = true; + } + // only apply push walk params if push walk is active in config, + // otherwise data of previous assist level is kept. + else if (assist_level == ASSIST_PUSH && g_config.use_push_walk) + { + assist_level_data.level.flags = 0; + assist_level_data.level.target_current_percent = 0; + assist_level_data.level.max_speed_percent = 0; + assist_level_data.level.max_cadence_percent = 15; + assist_level_data.level.max_throttle_current_percent = 0; + + assist_level_data.max_wheel_speed_rpm_x10 = convert_wheel_speed_kph_to_rpm(WALK_MODE_SPEED_KPH) * 10; + } +} + +uint16_t convert_wheel_speed_kph_to_rpm(uint8_t speed_kph) +{ + float radius_mm = EXPAND_U16(g_config.wheel_size_inch_x10_u16h, g_config.wheel_size_inch_x10_u16l) * + 1.27f; // g_config.wheel_size_inch_x10 / 2.f * 2.54f; + return (uint16_t)(25000.f / (3 * 3.14159f * radius_mm) * speed_kph); +} diff --git a/code/firmware/src/app.h b/code/firmware/src/app.h new file mode 100644 index 00000000..affc01b5 --- /dev/null +++ b/code/firmware/src/app.h @@ -0,0 +1,69 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#ifndef _APP_H_ +#define _APP_H_ + +#include +#include + +#define ASSIST_0 0x00 +#define ASSIST_1 0x01 +#define ASSIST_2 0x02 +#define ASSIST_3 0x03 +#define ASSIST_4 0x04 +#define ASSIST_5 0x05 +#define ASSIST_6 0x06 +#define ASSIST_7 0x07 +#define ASSIST_8 0x08 +#define ASSIST_9 0x09 +#define ASSIST_PUSH 0x0A + +#define OPERATION_MODE_DEFAULT 0x00 +#define OPERATION_MODE_SPORT 0x01 + +// Matches status codes used by Bafang +#define STATUS_NORMAL 0x01 +#define STATUS_BRAKING 0x03 + +#define STATUS_ERROR_THROTTLE_HIGH 0x04 +#define STATUS_ERROR_THROTTLE 0x05 +#define STATUS_ERROR_LVC 0x06 +#define STATUS_ERROR_HIGH_VOLTAGE 0x07 // not implemented +#define STATUS_ERROR_HALL_SENSOR 0x08 +#define STATUS_ERROR_PHASE_LINE 0x09 +#define STATUS_ERROR_CONTROLLER_OVER_TEMP 0x10 +#define STATUS_ERROR_MOTOR_OVER_TEMP 0x11 +#define STATUS_ERROR_CURRENT_SENSE 0x12 +#define STATUS_ERROR_BATTERY_TEMP_SENSOR 0x13 // n/a +#define STATUS_ERROR_MOTOR_TEMP_SENSOR 0x14 // not implemented +#define STATUS_ERROR_CONTROLLER_TEMP_SENSOR 0x15 // not implemented +#define STATUS_ERROR_SPEED_SENSOR 0x21 // not implemented +#define STATUS_ERROR_BMS_COMMUNICATION 0x22 // n/a +#define STATUS_ERROR_HEAD_LIGHT 0x23 // not implemented +#define STATUS_ERROR_HEAD_LIGHT_SENSOR 0x24 // not implemented +#define STATUS_ERROR_TORQUE_SENSOR 0x25 +#define STATUS_ERROR_TORQUE_SPEED 0x26 // n/a +#define STATUS_ERROR_COMMUNICATION 0x30 // n/a + +void app_init(); + +void app_process(); + +void app_set_assist_level(uint8_t level); +void app_set_lights(bool on); + +void app_set_operation_mode(uint8_t mode); +void app_set_wheel_max_speed_rpm(uint16_t value); + +uint8_t app_get_assist_level(); +uint8_t app_get_lights(); +uint8_t app_get_status_code(); +uint8_t app_get_temperature(); + +#endif diff --git a/code/firmware/src/battery.c b/code/firmware/src/battery.c new file mode 100644 index 00000000..6e55ac9b --- /dev/null +++ b/code/firmware/src/battery.c @@ -0,0 +1,155 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "battery.h" +#include "cfgstore.h" +#include "fwconfig.h" +#include "motor.h" +#include "system.h" +#include "util.h" + +static int16_t battery_empty_x100v; +static int16_t battery_full_x100v; + +static uint8_t battery_percent; +static uint32_t motor_disabled_at_ms; +static bool first_reading_done; + +/* +No attempt is made to have accurate battery state of charge display. + +This is only a voltage based approch using configured max and min battery voltages. +The end values are padded 8% on each side (BATTERY_EMPTY_OFFSET_PERCENT, BATTERY_FULL_OFFSET_PERCENT). + +Battery voltage is measured when no motor power has been applied for +at least 2 seconds (BATTERY_NO_LOAD_DELAY_MS). This is to mitigate measuring voltage sag +but is still problematic in cold weather. + +Battery SOC percentage is calculated from measured voltage using linear interpolation +between the padded ranges. + +The LVC rampdown starts at 10% battery SOC (LVC_RAMP_DOWN_OFFSET_PERCENT) and will linearly +ramp the current down to 20% (LVC_LOW_CURRENT_PERCENT) of the maximum configured current. + +For example, if the maximum battery voltage is 58.8V and the low cutoff voltage is 42V, then: + +- The full voltage range is 58.8V - 42V = 16.8V +- The padding amount is 0.08 * 16.8V = 1.3V +- The battery is considered at 100% SOC at 58.8V - 1.3V = 57.5V +- The battery is considered at 0% SOC at 42.0V + 1.3V = 43.3V +- LVC rampdown will start at 10% SOC, so: 43.3V + 0.1 * (57.5V - 43.3V) = 44.7V +- Full LVC limiting will occur at 0% SOC, so: 43.3V +*/ + +static uint8_t compute_battery_percent() +{ + int16_t value_x100v = motor_get_battery_voltage_x10() * 10l; + int16_t percent = (int16_t)MAP32(value_x100v, battery_empty_x100v, battery_full_x100v, 0, 100); + + return (uint8_t)CLAMP(percent, 0, 100); +} + +#if (BATTERY_PERCENT_MAP == BATTERY_PERCENT_MAP_SW102) +static uint8_t map_percent_sw102(uint8_t percent) +{ + // Measured on Display + // ----------------------- + // 0bar 0-5 + // 1bar 5 - 10 + // 2bar 10 - 30 + // 3bar 31 - 51 + // 4bar 52 - 78 + // 5bar 78 - 100 + + if (percent < 5) // 0bar + { + return 0; + } + else if (percent < 21) // 1bar + { + return 7; + } + else if (percent < 41) // 2bar + { + return 20; + } + else if (percent < 61) // 3bar + { + return 40; + } + else if (percent < 81) // 4bar + { + return 60; + } + else // 5bar + { + return 100; + } +} +#endif + +void battery_init() +{ + // default to 70% until first reading is available + battery_percent = 70; + motor_disabled_at_ms = 0; + first_reading_done = false; + + uint16_t battery_min_voltage_x100v = g_config.low_cut_off_v * 100u; + uint16_t battery_max_voltage_x100v = EXPAND_U16(g_config.max_battery_x100v_u16h, g_config.max_battery_x100v_u16l); + + uint16_t battery_range_x100v = battery_max_voltage_x100v - battery_min_voltage_x100v; + + battery_full_x100v = battery_max_voltage_x100v - ((BATTERY_FULL_OFFSET_PERCENT * battery_range_x100v) / 100); + + battery_empty_x100v = battery_min_voltage_x100v + ((BATTERY_EMPTY_OFFSET_PERCENT * battery_range_x100v) / 100); +} + +void battery_process() +{ + if (!first_reading_done) + { + if (motor_get_battery_voltage_x10() > 0) + { + battery_percent = compute_battery_percent(); + first_reading_done = true; + } + } + else + { + uint8_t target_current = motor_get_target_current(); + + if (motor_disabled_at_ms == 0 && target_current == 0) + { + motor_disabled_at_ms = system_ms(); + } + else if (target_current > 0) + { + motor_disabled_at_ms = 0; + } + + if (target_current == 0 && (system_ms() - motor_disabled_at_ms) > BATTERY_NO_LOAD_DELAY_MS) + { + battery_percent = compute_battery_percent(); + } + } +} + +uint8_t battery_get_percent() +{ + return battery_percent; +} + +uint8_t battery_get_mapped_percent() +{ +#if (BATTERY_PERCENT_MAP == BATTERY_PERCENT_MAP_SW102) + return map_percent_sw102(battery_percent); +#else + return battery_percent; +#endif +} diff --git a/src/firmware/battery.h b/code/firmware/src/battery.h similarity index 86% rename from src/firmware/battery.h rename to code/firmware/src/battery.h index 6bf324a3..38482d47 100644 --- a/src/firmware/battery.h +++ b/code/firmware/src/battery.h @@ -1,7 +1,7 @@ /* * bbs-fw * - * Copyright (C) Daniel Nilsson, 2022. + * Copyright (C) Daniel Nilsson, 2024. * * Released under the GPL License, Version 3 */ diff --git a/code/firmware/src/bbsx/adc.c b/code/firmware/src/bbsx/adc.c new file mode 100644 index 00000000..b6c12cfa --- /dev/null +++ b/code/firmware/src/bbsx/adc.c @@ -0,0 +1,151 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "adc.h" +#include "bbsx/pins.h" +#include "bbsx/stc15.h" + +static uint8_t next_channel; +static uint8_t no_adc_reading_counter; + +static uint8_t throttle_value; +static uint16_t temperature_contr_value; +static uint16_t temperature_motor_value; + +void adc_init() +{ + // Setup pin voltage as high impedance input even though it is not used + SET_PIN_INPUT(PIN_VOLTAGE); + + // Setup pin throttle as adc input + SET_PIN_INPUT(PIN_THROTTLE); + SET_PIN_LOW(PIN_THROTTLE); + SET_BIT(P1ASF, GET_PIN_NUM(PIN_THROTTLE)); + + // Setup pin controller temperature pin as adc input + SET_PIN_INPUT(PIN_TEMPERATURE_CONTR); + SET_PIN_LOW(PIN_TEMPERATURE_CONTR); + SET_BIT(P1ASF, GET_PIN_NUM(PIN_TEMPERATURE_CONTR)); + +#ifdef BBSHD + // Setup pin motor temperature pin as adc input + SET_PIN_INPUT(PIN_TEMPERATURE_MOTOR); + SET_PIN_LOW(PIN_TEMPERATURE_MOTOR); + SET_BIT(P1ASF, GET_PIN_NUM(PIN_TEMPERATURE_MOTOR)); +#endif + + ADC_RES = 0; + ADC_RESL = 0; + + // Arrange adc result for 8bit reading + CLEAR_BIT(PCON2, 5); + + ADC_CONTR = (uint8_t)((1 << 7)); + + no_adc_reading_counter = 0; + throttle_value = 0; + temperature_contr_value = 0; + temperature_motor_value = 0; + next_channel = GET_PIN_NUM(PIN_THROTTLE); + + // throttle is read during init since a valid value must be + // needs to be available for fir throttle_process to not mess + // up safeguard logic + + // enable adc power and read throttle + ADC_CONTR = (uint8_t)((1 << 7) | (1 << 3) | next_channel); + + // wait for throttle reading and process + while (!IS_BIT_SET(ADC_CONTR, 4)) + ; + adc_process(); +} + +void adc_process() +{ + // adc reading available + if (IS_BIT_SET(ADC_CONTR, 4)) + { + no_adc_reading_counter = 0; + + ADC_CONTR = (uint8_t)((1 << 7)); // Clear ADC_FLAG + + switch (next_channel) + { + case GET_PIN_NUM(PIN_THROTTLE): + { + throttle_value = ADC_RES; + next_channel = GET_PIN_NUM(PIN_TEMPERATURE_CONTR); + break; + } + case GET_PIN_NUM(PIN_TEMPERATURE_CONTR): + { + temperature_contr_value = (((uint16_t)ADC_RES) << 2) | ADC_RESL; +#ifdef BBSHD + next_channel = GET_PIN_NUM(PIN_TEMPERATURE_MOTOR); +#else + next_channel = GET_PIN_NUM(PIN_THROTTLE); +#endif + break; + } +#ifdef BBSHD + case GET_PIN_NUM(PIN_TEMPERATURE_MOTOR): + { + temperature_motor_value = (((uint16_t)ADC_RES) << 2) | ADC_RESL; + next_channel = GET_PIN_NUM(PIN_THROTTLE); + break; + } +#endif + } + } + else if (++no_adc_reading_counter == 0) + { + // reinitialize adc + ADC_RES = 0; + ADC_CONTR = (uint8_t)(1 << 7); + no_adc_reading_counter = 0; + throttle_value = 0; + temperature_motor_value = 0; + temperature_contr_value = 0; + next_channel = GET_PIN_NUM(PIN_THROTTLE); + } + else + { + return; + } + + // start next reading + ADC_RES = 0; + ADC_CONTR = (uint8_t)((1 << 7) | (1 << 3) | next_channel); +} + +uint8_t adc_get_throttle() +{ + return throttle_value; +} + +uint16_t adc_get_torque() +{ + return 0; +} + +uint16_t adc_get_temperature_contr() +{ + return temperature_contr_value; +} + +uint16_t adc_get_temperature_motor() +{ + return temperature_motor_value; +} + +uint16_t adc_get_battery_voltage() +{ + // not implemented, motor MCU sends adc battery voltage value + return 0; +} diff --git a/src/firmware/bbsx/cpu.h b/code/firmware/src/bbsx/cpu.h similarity index 64% rename from src/firmware/bbsx/cpu.h rename to code/firmware/src/bbsx/cpu.h index f3761f4c..a32d44a4 100644 --- a/src/firmware/bbsx/cpu.h +++ b/code/firmware/src/bbsx/cpu.h @@ -1,7 +1,7 @@ /* * bbs-fw * - * Copyright (C) Daniel Nilsson, 2022. + * Copyright (C) Daniel Nilsson, 2024. * * Released under the GPL License, Version 3 */ @@ -9,6 +9,6 @@ #ifndef _BBSX_CPU_H_ #define _BBSX_CPU_H_ -#define CPU_FREQ 20000000L +#define CPU_FREQ 20000000L #endif diff --git a/code/firmware/src/bbsx/eeprom.c b/code/firmware/src/bbsx/eeprom.c new file mode 100644 index 00000000..759970a9 --- /dev/null +++ b/code/firmware/src/bbsx/eeprom.c @@ -0,0 +1,127 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "eeprom.h" +#include "bbsx/stc15.h" + +#define EEPROM_NUM_SECTORS 4 + +// STC chips has a special area in flash for eeprom. +#define EEPROM_STC_ADDRESS_OFFSET 0x0000 + +// IAP chips have no special area, same area as program +// memory and address space is the same. We define the last +// four sectors for eeprom usage ourself. +#define EEPROM_IAP_ADDRESS_OFFSET 0xEC00 + +#define IAP_CMD_IDLE 0 +#define IAP_CMD_READ 1 +#define IAP_CMD_PROGRAM 2 +#define IAP_CMD_ERASE 3 + +#define IAP_ENABLE 0x82 // Wait time, CPU_FREQ < 20MHz + +static uint16_t address_offset = 0x0000; +static uint16_t selected_sector_offset = 0; + +static void eeprom_begin(uint8_t cmd, int offset) +{ + IAP_CONTR = IAP_ENABLE; + IAP_CMD = cmd; + + uint16_t addr = selected_sector_offset + offset; + IAP_ADDRH = addr >> 8; + IAP_ADDRL = addr; +} + +static bool eeprom_trigger() +{ + IAP_TRIG = 0x5a; + IAP_TRIG = 0xa5; + NOP(); + + return !IS_BIT_SET(IAP_CONTR, 4); +} + +static void eeprom_end() +{ + IAP_CONTR = 0; + IAP_CMD = 0; + IAP_TRIG = 0; + IAP_ADDRH = 0xff; + IAP_ADDRL = 0xff; +} + +void eeprom_init() +{ + // Detect if we are running on IAP or STC model dependeing on if + // we can read from IAP address offset which is outside eeprom + // address space on STC model. + + address_offset = EEPROM_IAP_ADDRESS_OFFSET; + eeprom_select_page(0); + if (eeprom_read_byte(0) == -1) + { + address_offset = EEPROM_STC_ADDRESS_OFFSET; + } +} + +bool eeprom_select_page(int page) +{ + if (page >= 0 && page < EEPROM_NUM_SECTORS) + { + selected_sector_offset = address_offset + page * 512; + return true; + } + + return false; +} + +bool eeprom_erase_page() +{ + bool res; + + eeprom_begin(IAP_CMD_ERASE, 0); + res = eeprom_trigger(); + eeprom_end(); + + return res; +} + +int eeprom_read_byte(int offset) +{ + int res = -1; + + eeprom_begin(IAP_CMD_READ, offset); + + if (eeprom_trigger()) + { + res = IAP_DATA; + } + + eeprom_end(); + + return res; +} + +bool eeprom_write_byte(int offset, uint8_t value) +{ + bool res; + + eeprom_begin(IAP_CMD_PROGRAM, offset); + IAP_DATA = value; + res = eeprom_trigger(); + eeprom_end(); + + return res; +} + +bool eeprom_end_write() +{ + return true; +} diff --git a/code/firmware/src/bbsx/interrupt.h b/code/firmware/src/bbsx/interrupt.h new file mode 100644 index 00000000..fa91c0a8 --- /dev/null +++ b/code/firmware/src/bbsx/interrupt.h @@ -0,0 +1,22 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#ifndef _BBSX_INTERRUPT_H_ +#define _BBSX_INTERRUPT_H_ + +#include + +#define IRQ_TIMER0 1 +#define IRQ_UART1 4 +#define IRQ_UART2 8 + +INTERRUPT_USING(isr_timer0, IRQ_TIMER0, 1); // system.c +INTERRUPT_USING(isr_uart1, IRQ_UART1, 3); // uart.c +INTERRUPT_USING(isr_uart2, IRQ_UART2, 3); // uart.c + +#endif diff --git a/code/firmware/src/bbsx/lights.c b/code/firmware/src/bbsx/lights.c new file mode 100644 index 00000000..62fc2af1 --- /dev/null +++ b/code/firmware/src/bbsx/lights.c @@ -0,0 +1,52 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "lights.h" +#include "bbsx/pins.h" +#include "bbsx/stc15.h" + +void lights_init() +{ + SET_PIN_OUTPUT(PIN_LIGHTS_POWER); + SET_PIN_OUTPUT(PIN_LIGHTS); + + lights_disable(); + lights_set(false); +} + +void lights_enable() +{ + // enable signal level is swapped on BBSHD vs BBS02... +#if defined(BBSHD) + SET_PIN_HIGH(PIN_LIGHTS_POWER); +#elif defined(BBS02) + SET_PIN_LOW(PIN_LIGHTS_POWER); +#endif +} + +void lights_disable() +{ + // enable signal level is swapped on BBSHD vs BBS02... +#if defined(BBSHD) + SET_PIN_LOW(PIN_LIGHTS_POWER); +#elif defined(BBS02) + SET_PIN_HIGH(PIN_LIGHTS_POWER); +#endif +} + +void lights_set(bool on) +{ + if (on) + { + SET_PIN_LOW(PIN_LIGHTS); + } + else + { + SET_PIN_HIGH(PIN_LIGHTS); + } +} diff --git a/code/firmware/src/bbsx/motor.c b/code/firmware/src/bbsx/motor.c new file mode 100644 index 00000000..a3f1665d --- /dev/null +++ b/code/firmware/src/bbsx/motor.c @@ -0,0 +1,701 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "motor.h" +#include "bbsx/pins.h" +#include "bbsx/uart_motor.h" +#include "eventlog.h" +#include "sensors.h" +#include "system.h" + +#include + +#define OPCODE_LVC 0x60 +#define OPCODE_MAX_CURRENT 0x61 +#define OPCODE_TARGET_SPEED 0x63 +#define OPCODE_TARGET_CURRENT 0x64 +#define OPCODE_HELLO 0x67 +#define OPCODE_UNKNOWN1 0x68 +#define OPCODE_UNKNOWN2 0x69 +#define OPCODE_UNKNOWN3 0x6A +#define OPCODE_UNKNOWN4 0x6B +#define OPCODE_UNKNOWN5 0x6C +#define OPCODE_UNKNOWN5 0x6C +#define OPCODE_UNKNOWN6 0x6D +#define OPCODE_UNKNOWN7 0x6E + +#define OPCODE_READ_STATUS 0x40 +#define OPCODE_READ_CURRENT 0x41 +#define OPCODE_READ_VOLTAGE 0x42 + +#define READ_TIMEOUT 100 + +#if defined(BBSHD) +#define ADC_STEPS_PER_AMP_X10 69 +#define ADC_STEPS_PER_VOLT_X100 1490 // 1460 in orginal firmware +#elif defined(BBS02) +#define ADC_STEPS_PER_AMP_X10 56 +#define ADC_STEPS_PER_VOLT_X100 1510 +#endif + +#define SPEED_STEPS 250 + +// async om state machine +#define COM_STATE_IDLE 0x01 +#define COM_STATE_WAIT_RESPONSE 0x02 +#define COM_STATE_SET_CURRENT 0x03 +#define COM_STATE_SET_SPEED 0x04 +#define COM_STATE_READ_STATUS 0x05 +#define COM_STATE_READ_CURRENT 0x06 +#define COM_STATE_READ_VOLTAGE 0x07 + +#define MSGBUF_SIZE 8 + +static uint8_t is_connected; +static uint8_t msgbuf[MSGBUF_SIZE]; + +static bool target_speed_changed; +static uint8_t target_speed; + +static bool target_current_changed; +static uint8_t target_current; + +static uint16_t adc_steps_per_volt_x100; +static uint16_t lvc_volt_x10; + +static uint16_t status_flags; +static uint16_t battery_volt_x10; +static uint16_t battery_adc_steps; +static uint16_t battery_amp_x10; + +// state machine state +static uint8_t com_state; +static uint8_t last_sent_opcode; +static uint32_t last_request_write_ms; +static uint32_t last_status_read_ms; +static uint8_t next_status_read_opcode; + +static uint8_t compute_checksum(uint8_t *msg, uint8_t len); +static void send_request(uint8_t opcode, uint16_t data); +static void send_request_async(uint8_t opcode, uint16_t data); + +static int read_response(uint8_t opcode, uint16_t *out_data); +static int try_read_response(uint8_t opcode, uint16_t *out_data); +static int connect(); +static int configure(uint16_t max_current_mA, uint8_t lvc_V); + +static void process_com_state_machine(); + +void motor_pre_init() +{ + SET_PIN_OUTPUT(PIN_MOTOR_POWER_ENABLE); + SET_PIN_OUTPUT(PIN_MOTOR_CONTROL_ENABLE); + SET_PIN_OUTPUT(PIN_MOTOR_EXTRA); + + SET_PIN_LOW(PIN_MOTOR_POWER_ENABLE); + SET_PIN_HIGH(PIN_MOTOR_CONTROL_ENABLE); + SET_PIN_HIGH(PIN_MOTOR_EXTRA); +} + +void motor_init(uint16_t max_current_mA, uint8_t lvc_V, int16_t adc_calib_volt_steps_x100) +{ + motor_pre_init(); + + is_connected = 0; + target_speed_changed = false; + target_speed = 0; + target_current_changed = false; + target_current = 0; + status_flags = 0; + adc_steps_per_volt_x100 = ADC_STEPS_PER_VOLT_X100 + adc_calib_volt_steps_x100; + lvc_volt_x10 = (uint16_t)lvc_V * 10; + battery_volt_x10 = 0; + battery_adc_steps = 0; + battery_amp_x10 = 0; + + com_state = COM_STATE_IDLE; + last_sent_opcode = 0; + last_request_write_ms = 0; + last_status_read_ms = 0; + next_status_read_opcode = OPCODE_READ_STATUS; + + uart_motor_open(4800); + + // Give other MCU time to power on + while (system_ms() < 100) + ; + + if (connect() && configure(max_current_mA, lvc_V)) + { + is_connected = 1; + + eventlog_write(EVT_MSG_MOTOR_INIT_OK); + + motor_set_target_speed(0); + motor_set_target_current(0); + target_current_changed = true; + target_speed_changed = true; + } + else + { + eventlog_write(EVT_ERROR_INIT_MOTOR); + } +} + +void motor_process() +{ + if (!is_connected) + { + return; + } + + process_com_state_machine(); +} + +void motor_enable() +{ + SET_PIN_HIGH(PIN_MOTOR_POWER_ENABLE); +} + +void motor_disable() +{ + if (!brake_is_activated()) + { + // Brake signal is also connected to motor control MCU. + // If we disable motor power here during braking it causes + // a small issue where change in target current is not accepted + // while in disabled state. This will result in a short power spike + // when brake eventually released. + + SET_PIN_LOW(PIN_MOTOR_POWER_ENABLE); + } +} + +uint16_t motor_status() +{ + return status_flags; +} + +uint8_t motor_get_target_speed() +{ + return target_speed; +} + +uint8_t motor_get_target_current() +{ + return target_current; +} + +void motor_set_target_speed(uint8_t percent) +{ + if (percent > 100) + { + percent = 100; + } + + if (target_speed != percent) + { + target_speed = percent; + target_speed_changed = true; + } +} + +void motor_set_target_current(uint8_t percent) +{ + if (percent > 100) + { + percent = 100; + } + + if (target_current != percent) + { + target_current = percent; + target_current_changed = true; + } +} + +int16_t motor_calibrate_battery_voltage(uint16_t actual_voltage_x100) +{ + int16_t diff = 0; + if (actual_voltage_x100 != 0) + { + uint16_t calibrated_adc_steps_volt_x100 = + (uint16_t)(((uint32_t)battery_adc_steps * 10000u) / actual_voltage_x100); + diff = calibrated_adc_steps_volt_x100 - ADC_STEPS_PER_VOLT_X100; + + adc_steps_per_volt_x100 = calibrated_adc_steps_volt_x100; + } + else + { + // reset calibration if 0 is received + adc_steps_per_volt_x100 = ADC_STEPS_PER_VOLT_X100; + diff = 0; + } + + eventlog_write_data(EVT_DATA_CALIBRATE_VOLTAGE, adc_steps_per_volt_x100); + + return diff; +} + +uint16_t motor_get_battery_lvc_x10() +{ + return lvc_volt_x10; +} + +uint16_t motor_get_battery_current_x10() +{ + return battery_amp_x10; +} + +uint16_t motor_get_battery_voltage_x10() +{ + return battery_volt_x10; +} + +static uint8_t compute_checksum(uint8_t *msg, uint8_t len) +{ + uint8_t checksum = 0; + for (int i = 0; i < len; ++i) + { + checksum += *(msg + i); + } + + return checksum; +} + +static void send_request(uint8_t opcode, uint16_t data) +{ + // empty rx buffer + while (uart_motor_available()) + uart_motor_read(); + + send_request_async(opcode, data); + + uart_motor_flush(); +} + +static void send_request_async(uint8_t opcode, uint16_t data) +{ + uint8_t idx = 0; + + msgbuf[idx++] = 0xaa; // start of message + msgbuf[idx++] = opcode; + + if (opcode == OPCODE_LVC) + { + msgbuf[idx++] = data >> 8; + msgbuf[idx++] = data; + } + else if (opcode != OPCODE_READ_STATUS && opcode != OPCODE_READ_CURRENT && opcode != OPCODE_READ_VOLTAGE) + { + msgbuf[idx++] = data; + } + + uint8_t checksum = compute_checksum(msgbuf + 1, idx - 1); + msgbuf[idx++] = checksum; + + for (uint8_t i = 0; i < idx; ++i) + { + uart_motor_write(msgbuf[i]); + } +} + +static int read_response(uint8_t opcode, uint16_t *out_data) +{ + uint32_t end = system_ms() + READ_TIMEOUT; + + uint8_t len = (opcode == OPCODE_LVC || opcode == OPCODE_READ_STATUS || opcode == OPCODE_READ_VOLTAGE) ? 5 : 4; + + uint8_t i = 0; + while (i < len && system_ms() < end) + { + if (uart_motor_available()) + { + msgbuf[i++] = uart_motor_read(); + } + } + + if (i == len && msgbuf[1] == opcode) + { + uint8_t checksum = compute_checksum(&msgbuf[1], (uint8_t)(i - 2)); + if (checksum == msgbuf[i - 1]) + { + if (out_data != 0) + { + if (opcode == OPCODE_LVC || opcode == OPCODE_READ_STATUS || opcode == OPCODE_READ_VOLTAGE) + { + *out_data = msgbuf[2] << 8 | msgbuf[3]; + } + else + { + *out_data = msgbuf[2]; + } + } + + return 1; + } + + return 0; // failed to verify message + } + + // read failure + return 0; +} + +static int try_read_response(uint8_t opcode, uint16_t *out_data) +{ + uint8_t len = (opcode == OPCODE_LVC || opcode == OPCODE_READ_STATUS || opcode == OPCODE_READ_VOLTAGE) ? 5 : 4; + + uint8_t i = 0; + while (uart_motor_available() && i < MSGBUF_SIZE) + { + msgbuf[i++] = uart_motor_read(); + } + + // clear anything that could be left in rxbuffer in case of error. + while (uart_motor_available()) + uart_motor_read(); + + if (i < len) + { + // failed to read entire response + return 0; + } + + if (i == len && msgbuf[1] == opcode) + { + uint8_t checksum = compute_checksum(&msgbuf[1], (uint8_t)(i - 2)); + if (checksum == msgbuf[i - 1]) + { + if (out_data != 0) + { + if (opcode == OPCODE_LVC || opcode == OPCODE_READ_STATUS || opcode == OPCODE_READ_VOLTAGE) + { + *out_data = ((uint16_t)msgbuf[2] << 8) | msgbuf[3]; + } + else + { + *out_data = msgbuf[2]; + } + } + + return 1; + } + + return 0; // failed to verify message + } + + // read failure + return 0; +} + +static int connect() +{ + for (int i = 0; i < 10; ++i) + { + send_request(OPCODE_HELLO, 0x00); + + if (read_response(OPCODE_HELLO, 0)) + { + system_delay_ms(4); + return 1; + } + else + { + system_delay_ms(1000); + } + } + + return 0; +} + +static int configure(uint16_t max_current_mA, uint8_t lvc_V) +{ + uint16_t tmp = 0; + + // This initialization is done exactly as in orginal firmware for BBSHD/BBS02. + // The meaning of most parameters is unknown. + +#if defined(BBSHD) + send_request(OPCODE_UNKNOWN1, 0x5a); +#elif defined(BBS02) + send_request(OPCODE_UNKNOWN1, 0x5f); +#else + return 0; +#endif + + if (!read_response(OPCODE_UNKNOWN1, 0)) + { + return 0; + } + + system_delay_ms(4); + + send_request(OPCODE_UNKNOWN2, 0x11); + if (!read_response(OPCODE_UNKNOWN2, 0)) + { + return 0; + } + + system_delay_ms(4); + + send_request(OPCODE_UNKNOWN3, 0x78); + if (!read_response(OPCODE_UNKNOWN3, 0)) + { + return 0; + } + + system_delay_ms(4); + + send_request(OPCODE_UNKNOWN4, 0x64); + if (!read_response(OPCODE_UNKNOWN4, 0)) + { + return 0; + } + + system_delay_ms(4); + + send_request(OPCODE_UNKNOWN5, 0x50); + if (!read_response(OPCODE_UNKNOWN5, 0)) + { + return 0; + } + + system_delay_ms(4); + + send_request(OPCODE_UNKNOWN6, 0x46); + if (!read_response(OPCODE_UNKNOWN6, 0)) + { + return 0; + } + + system_delay_ms(4); + + send_request(OPCODE_UNKNOWN7, 0x0c); + if (!read_response(OPCODE_UNKNOWN7, 0)) + { + return 0; + } + + system_delay_ms(4); + + send_request(OPCODE_LVC, (uint32_t)(((uint32_t)lvc_V * adc_steps_per_volt_x100) / 100u)); + if (!read_response(OPCODE_LVC, 0)) + { + return 0; + } + + system_delay_ms(4); + + tmp = (uint16_t)((max_current_mA * (uint32_t)ADC_STEPS_PER_AMP_X10) / 10000UL); + if (tmp > 255) + { + tmp = 255; + } + eventlog_write_data(EVT_DATA_MAX_CURRENT_ADC_REQUEST, tmp); + + send_request(OPCODE_MAX_CURRENT, tmp); + if (!read_response(OPCODE_MAX_CURRENT, &tmp)) + { + return 0; + } + else + { + eventlog_write_data(EVT_DATA_MAX_CURRENT_ADC_RESPONSE, tmp); + } + + system_delay_ms(4); + + return 1; +} + +static void process_com_state_machine_idle() +{ + // Async state machine loop for serial communication with motor control MCU. + // + // Handles: + // * Set target current + // * Set target speed + // * Read motor status + // * Read motor current + // * Read battery voltage + // + // Set target speed/current are prioritzed over status reading (shorter check interval). + + uint32_t now = system_ms(); + + // make sure requests have some space between them + if (now - last_request_write_ms < 32) + { + return; + } + + if (target_current_changed) + { + send_request_async(OPCODE_TARGET_CURRENT, target_current); + last_sent_opcode = OPCODE_TARGET_CURRENT; + last_request_write_ms = now; + com_state = COM_STATE_WAIT_RESPONSE; + target_current_changed = false; + return; + } + + if (target_speed_changed) + { + send_request_async(OPCODE_TARGET_SPEED, (uint8_t)(((uint16_t)SPEED_STEPS * target_speed) / 100)); + last_sent_opcode = OPCODE_TARGET_SPEED; + last_request_write_ms = now; + com_state = COM_STATE_WAIT_RESPONSE; + target_speed_changed = false; + return; + } + + if ((now - last_status_read_ms) > 200) + { + send_request_async(next_status_read_opcode, 0); + last_sent_opcode = next_status_read_opcode; + last_request_write_ms = now; + com_state = COM_STATE_WAIT_RESPONSE; + if (next_status_read_opcode == OPCODE_READ_STATUS) + { + last_status_read_ms = now; + } + return; + } +} + +static void process_com_state_machine_wait_response() +{ + uint8_t response_length = 0; + + switch (last_sent_opcode) + { + case OPCODE_TARGET_CURRENT: + case OPCODE_TARGET_SPEED: + case OPCODE_READ_CURRENT: + response_length = 4; + break; + case OPCODE_READ_VOLTAGE: + case OPCODE_READ_STATUS: + response_length = 5; + break; + } + + if (uart_motor_available() >= response_length || (system_ms() - last_request_write_ms) > 32) + { + switch (last_sent_opcode) + { + case OPCODE_TARGET_CURRENT: + com_state = COM_STATE_SET_CURRENT; + break; + case OPCODE_TARGET_SPEED: + com_state = COM_STATE_SET_SPEED; + break; + case OPCODE_READ_CURRENT: + com_state = COM_STATE_READ_CURRENT; + break; + case OPCODE_READ_VOLTAGE: + com_state = COM_STATE_READ_VOLTAGE; + break; + case OPCODE_READ_STATUS: + com_state = COM_STATE_READ_STATUS; + break; + default: + com_state = COM_STATE_IDLE; + break; + } + } +} + +static void process_com_state_machine() +{ + uint16_t data; + switch (com_state) + { + case COM_STATE_IDLE: + process_com_state_machine_idle(); + break; + + case COM_STATE_WAIT_RESPONSE: + process_com_state_machine_wait_response(); + break; + + case COM_STATE_SET_CURRENT: + if (try_read_response(OPCODE_TARGET_CURRENT, &data)) + { + eventlog_write_data(EVT_DATA_TARGET_CURRENT, data); + } + else + { + eventlog_write(EVT_ERROR_CHANGE_TARGET_CURRENT); + } + + com_state = COM_STATE_IDLE; + break; + + case COM_STATE_SET_SPEED: + if (try_read_response(OPCODE_TARGET_SPEED, &data)) + { + eventlog_write_data(EVT_DATA_TARGET_SPEED, (uint8_t)((data * 100) / SPEED_STEPS)); + } + else + { + eventlog_write(EVT_ERROR_CHANGE_TARGET_SPEED); + } + + com_state = COM_STATE_IDLE; + break; + + case COM_STATE_READ_STATUS: + if (try_read_response(OPCODE_READ_STATUS, &data)) + { + if (data != status_flags) + { + status_flags = data; + eventlog_write_data(EVT_DATA_MOTOR_STATUS, status_flags); + } + } + else + { + eventlog_write(EVT_ERROR_READ_MOTOR_STATUS); + } + + next_status_read_opcode = OPCODE_READ_CURRENT; + com_state = COM_STATE_IDLE; + break; + + case COM_STATE_READ_CURRENT: + if (try_read_response(OPCODE_READ_CURRENT, &data)) + { + battery_amp_x10 = (data * 100) / ADC_STEPS_PER_AMP_X10; + } + else + { + eventlog_write(EVT_ERROR_READ_MOTOR_CURRENT); + } + + next_status_read_opcode = OPCODE_READ_VOLTAGE; + com_state = COM_STATE_IDLE; + break; + + case COM_STATE_READ_VOLTAGE: + if (try_read_response(OPCODE_READ_VOLTAGE, &data)) + { + battery_adc_steps = data; + battery_volt_x10 = (uint16_t)(((uint32_t)battery_adc_steps * 1000) / adc_steps_per_volt_x100); + } + else + { + eventlog_write(EVT_ERROR_READ_MOTOR_VOLTAGE); + } + + next_status_read_opcode = OPCODE_READ_STATUS; + com_state = COM_STATE_IDLE; + break; + } +} diff --git a/code/firmware/src/bbsx/pins.h b/code/firmware/src/bbsx/pins.h new file mode 100644 index 00000000..7f81ebcf --- /dev/null +++ b/code/firmware/src/bbsx/pins.h @@ -0,0 +1,69 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#ifndef _PINS_H_ +#define _PINS_H_ + +// PORT, PIN + +#if defined(BBSHD) + +#define PIN_MOTOR_POWER_ENABLE 2, 0 +#define PIN_MOTOR_CONTROL_ENABLE 2, 1 +#define PIN_MOTOR_EXTRA 4, 4 +#define PIN_MOTOR_RX 1, 0 +#define PIN_MOTOR_TX 1, 1 + +#define PIN_VOLTAGE 1, 6 +#define PIN_TEMPERATURE_CONTR 1, 7 +#define PIN_TEMPERATURE_MOTOR 1, 4 + +#define PIN_PAS1 4, 5 +#define PIN_PAS2 4, 6 + +// #define PIN_HALL_U 5, 0 +// #define PIN_HALL_V 3, 4 +// #define PIN_HALL_W 0, 6 + +#define PIN_SPEED_SENSOR 2, 2 +#define PIN_BRAKE 2, 4 +#define PIN_SHIFT_SENSOR 2, 6 +#define PIN_THROTTLE 1, 3 +#define PIN_LIGHTS_POWER 2, 3 // P+ +#define PIN_LIGHTS 5, 1 // Q + +#define PIN_EXTERNAL_RX 3, 0 +#define PIN_EXTERNAL_TX 3, 1 + +#elif defined(BBS02) + +#define PIN_MOTOR_POWER_ENABLE 2, 0 +#define PIN_MOTOR_CONTROL_ENABLE 5, 4 +#define PIN_MOTOR_EXTRA 5, 5 +#define PIN_MOTOR_RX 1, 0 +#define PIN_MOTOR_TX 1, 1 + +#define PIN_VOLTAGE 1, 7 +#define PIN_TEMPERATURE_CONTR 1, 2 + +#define PIN_PAS1 2, 3 +#define PIN_PAS2 2, 4 + +#define PIN_SPEED_SENSOR 2, 6 +#define PIN_BRAKE 3, 3 +#define PIN_SHIFT_SENSOR 3, 6 +#define PIN_THROTTLE 1, 5 +#define PIN_LIGHTS_POWER 0, 3 // P+ +#define PIN_LIGHTS 0, 2 // Q + +#define PIN_EXTERNAL_RX 3, 0 +#define PIN_EXTERNAL_TX 3, 1 + +#endif + +#endif diff --git a/code/firmware/src/bbsx/sensors.c b/code/firmware/src/bbsx/sensors.c new file mode 100644 index 00000000..0a67c5f1 --- /dev/null +++ b/code/firmware/src/bbsx/sensors.c @@ -0,0 +1,424 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "sensors.h" +#include "adc.h" +#include "bbsx/pins.h" +#include "bbsx/stc15.h" +#include "bbsx/timers.h" +#include "cfgstore.h" +#include "eventlog.h" +#include "fwconfig.h" +#include "system.h" +#include "util.h" + +#include +#include +#include + +// interrupt runs at 100us interval, see timer0 in timers.c +// timer0 is shared between system and sensors modules + +#define PAS_SENSOR_NUM_SIGNALS PAS_PULSES_REVOLUTION +#define PAS_SENSOR_MIN_PULSE_MS_X10 50 // 500rpm limit + +#define SPEED_SENSOR_MIN_PULSE_MS_X10 500 +#define SPEED_SENSOR_TIMEOUT_MS_X10 25000 + +// Some versions of the BBSHD motor (hall sensor board) +// has a PTC thermistor instead of a NTC thermistor. +// Using standard PT1000 table. +// [R_x100, C_x100] + +#ifdef BBSHD +#define BBSHD_PTC_LUT_SIZE 21 +typedef struct +{ + int32_t x; + int16_t y; +} pt_t; +static const pt_t bbshd_ptc_lut[BBSHD_PTC_LUT_SIZE] = { + {92100, -2000}, {96090, -1000}, {100000, 0000}, {103900, 1000}, {107790, 2000}, {109730, 2500}, {111670, 3000}, + {113610, 3500}, {115540, 4000}, {117470, 4500}, {119400, 5000}, {121320, 5500}, {123240, 6000}, {125160, 6500}, + {127080, 7000}, {128990, 7500}, {130900, 8000}, {132800, 8500}, {134710, 9000}, {136610, 9500}, {138510, 10000}}; +static bool bbshd_ptc_thermistor; +#endif + +static volatile uint16_t pas_pulse_counter; +static volatile bool pas_direction_backward; +static volatile uint16_t pas_period_length; // pulse length counted in interrupt frequency (100us) +static uint16_t pas_period_counter; +static bool pas_prev1; +static bool pas_prev2; +static uint16_t pas_stop_delay_periods; + +static volatile uint16_t speed_ticks_period_length; // pulse length counted in interrupt frequency (100us) +static uint16_t speed_period_counter; +static bool speed_prev_state; +static uint8_t speed_ticks_per_rpm; + +static float thermistor_ntc_calculate_temperature(float R, float invBeta) +{ + const float invT0 = 1.f / 298.15f; + + float K = 1.f / (invT0 + invBeta * (logf(R / 10000.f))); + float C = K - 273.15f; + + return C; +} + +#ifdef BBSHD +static int16_t thermistor_ptc_bbshd_calculate_temperature(int32_t R_x100) +{ + // interpolate in lookup table + + if (R_x100 < bbshd_ptc_lut[0].x) + { + // use minimum value + return bbshd_ptc_lut[0].y; + } + else if (R_x100 > bbshd_ptc_lut[BBSHD_PTC_LUT_SIZE - 1].x) + { + // use maximum value + return bbshd_ptc_lut[BBSHD_PTC_LUT_SIZE - 1].y; + } + + uint8_t i = 0; + for (i = 0; i < BBSHD_PTC_LUT_SIZE - 1; i++) + { + if (bbshd_ptc_lut[i + 1].x > R_x100) + { + break; + } + } + + return (uint16_t)MAP32(R_x100, bbshd_ptc_lut[i].x, bbshd_ptc_lut[i + 1].x, bbshd_ptc_lut[i].y, + bbshd_ptc_lut[i + 1].y); +} +#endif + +void sensors_init() +{ + // will be evaulated when first reading take place +#ifdef BBSHD + bbshd_ptc_thermistor = false; +#endif + + pas_period_counter = 0; + pas_pulse_counter = 0; + pas_direction_backward = false; + pas_period_length = 0; + pas_stop_delay_periods = 1500; + speed_period_counter = 0; + speed_ticks_period_length = 0; + speed_prev_state = false; + speed_ticks_per_rpm = 1; + + // pins do not have external interrupt, use timer0 to evaluate state frequently + SET_PIN_INPUT(PIN_PAS1); + SET_PIN_INPUT(PIN_PAS2); + SET_PIN_INPUT(PIN_SPEED_SENSOR); + + SET_PIN_QUASI(PIN_BRAKE); // input pullup + SET_PIN_QUASI(PIN_SHIFT_SENSOR); // input pullup + + pas_prev1 = GET_PIN_STATE(PIN_PAS1); + pas_prev2 = GET_PIN_STATE(PIN_PAS2); + + timer0_init_sensors(); +} + +void sensors_process() +{ +} + +void pas_set_stop_delay(uint16_t delay_ms) +{ + pas_stop_delay_periods = delay_ms * 10; +} + +uint16_t pas_get_cadence_rpm_x10() +{ + uint16_t tmp; + ET0 = 0; // disable timer0 interrupts + tmp = pas_period_length; + ET0 = 1; + + if (tmp > 0) + { + return (uint16_t)((6000000ul / PAS_SENSOR_NUM_SIGNALS) / tmp); + } + else + { + return 0; + } +} + +uint16_t pas_get_pulse_counter() +{ + uint16_t tmp; + ET0 = 0; // disable timer0 interrupts + tmp = pas_pulse_counter; + ET0 = 1; + + return tmp; +} + +bool pas_is_pedaling_forwards() +{ + uint16_t period_length; + uint8_t direction_backward; + ET0 = 0; // disable timer0 interrupts + period_length = pas_period_length; + direction_backward = pas_direction_backward; + ET0 = 1; + + // atomic read operation, no need to disable timer interrupt + return period_length > 0 && !direction_backward; +} + +bool pas_is_pedaling_backwards() +{ + uint16_t period_length; + uint8_t direction_backward; + ET0 = 0; // disable timer0 interrupts + period_length = pas_period_length; + direction_backward = pas_direction_backward; + ET0 = 1; + + return period_length > 0 && direction_backward; +} + +void speed_sensor_set_signals_per_rpm(uint8_t num_signals) +{ + speed_ticks_per_rpm = num_signals; +} + +bool speed_sensor_is_moving() +{ + uint16_t tmp; + ET0 = 0; // disable timer0 interrupts + tmp = speed_ticks_period_length; + ET0 = 1; + + return tmp > 0; +} + +uint16_t speed_sensor_get_rpm_x10() +{ + uint16_t tmp; + ET0 = 0; // disable timer0 interrupts + tmp = speed_ticks_period_length; + ET0 = 1; + + if (tmp > 0) + { + return 6000000ul / tmp / speed_ticks_per_rpm; + } + + return 0; +} + +uint16_t torque_sensor_get_nm_x100() +{ + return 0; +} + +bool torque_sensor_ok() +{ + return true; +} + +int16_t temperature_contr_x100() +{ + const float R1 = 5100.f; + const float invBeta = 1.f / 3600.f; + static int32_t adc_contr_x100 = 0; + + if (g_config.use_temperature_sensor & TEMPERATURE_SENSOR_CONTR) + { + if (adc_contr_x100 == 0) + { + adc_contr_x100 = adc_get_temperature_contr() * 100l; + } + else + { + adc_contr_x100 = EXPONENTIAL_FILTER(adc_contr_x100, adc_get_temperature_contr() * 100l, 4); + } + + if (adc_contr_x100 != 0) + { + float R = R1 * ((102300.f / (102300.f - adc_contr_x100)) - 1.f); + return (int16_t)(thermistor_ntc_calculate_temperature(R, invBeta) * 100.f + 0.5f); + } + } + + return 0; +} + +int16_t temperature_motor_x100() +{ + // Sensor only present in the BBSHD motor +#if HAS_MOTOR_TEMP_SENSOR + const float R1 = 5100.f; + const float invBeta = 1.f / 3990.f; + + static int32_t adc_motor_x100 = 0; + + if (g_config.use_temperature_sensor & TEMPERATURE_SENSOR_MOTOR) + { + bool first = false; + if (adc_motor_x100 == 0) + { + first = true; + adc_motor_x100 = adc_get_temperature_motor() * 100l; + } + else + { + adc_motor_x100 = EXPONENTIAL_FILTER(adc_motor_x100, adc_get_temperature_motor() * 100l, 4); + } + + if (adc_motor_x100 != 0) + { + float R = R1 * ((102300.f / (102300.f - adc_motor_x100)) - 1.f); + + if (first) + { + if (R > 1500.f) + { + // not likely to be a 1k ptc thermistor, assume 10k ntc + bbshd_ptc_thermistor = false; + eventlog_write_data(EVT_DATA_BBSHD_THERMISTOR, 0); + } + else + { + bbshd_ptc_thermistor = true; + eventlog_write_data(EVT_DATA_BBSHD_THERMISTOR, 1); + } + } + + if (bbshd_ptc_thermistor) + { + return thermistor_ptc_bbshd_calculate_temperature((int32_t)(R * 100.f + 0.5f)); + } + else + { + return (int16_t)(thermistor_ntc_calculate_temperature(R, invBeta) * 100.f + 0.5f); + } + } + } +#endif + + return 0; +} + +bool brake_is_activated() +{ + return !GET_PIN_STATE(PIN_BRAKE); +} + +bool shift_sensor_is_activated() +{ + return !GET_PIN_STATE(PIN_SHIFT_SENSOR); +} + +#pragma save +#pragma nooverlay // See SDCC manual about function calls in ISR +void sensors_timer0_isr() // runs every 100us, see timers.c +{ + // WARNING: + // No 16/32 bit or float computations in ISR (multiply/divide/modulo). + // Read SDCC compiler manual for more info. + + // Pas + { + bool pas1 = GET_PIN_STATE(PIN_PAS1); + bool pas2 = GET_PIN_STATE(PIN_PAS2); + + if (pas1 && !pas_prev1 /* && pas_period_counter > PAS_SENSOR_MIN_PULSE_MS_X10 */) + { + pas_pulse_counter++; + + if (pas_direction_backward != pas2) + { + pas_direction_backward = pas2; + + // Reset pas pulse counter if pedal direction is changed, + // this variable counts the number of pulses since start of pedaling session. + pas_pulse_counter = 0; + } + + if (pas_period_counter > 0) + { + if (pas_period_counter <= pas_stop_delay_periods) + { + pas_period_length = pas_period_counter; // save in order to be able to calculate rpm when needed + } + else + { + pas_period_length = 0; + } + + pas_period_counter = 0; + } + } + else + { + // Do not allow wraparound or computed pedaling cadence will wrong after pedals has been still. + if (pas_period_counter < 65535) + { + pas_period_counter++; + } + + if (pas_period_length > 0 && pas_period_counter > pas_stop_delay_periods) + { + pas_period_length = 0; + pas_pulse_counter = 0; + pas_direction_backward = false; + } + } + + pas_prev1 = pas1; + pas_prev2 = pas2; + } + + // Speed sensor + { + bool spd = GET_PIN_STATE(PIN_SPEED_SENSOR); + + if (spd && !speed_prev_state && speed_period_counter > SPEED_SENSOR_MIN_PULSE_MS_X10) + { + if (speed_period_counter <= SPEED_SENSOR_TIMEOUT_MS_X10) + { + speed_ticks_period_length = speed_period_counter; + } + else + { + speed_ticks_period_length = 0; + } + + speed_period_counter = 0; + } + else + { + // Do not allow wraparound or computed speed will wrong after bike has been still. + if (speed_period_counter < 65535) + { + speed_period_counter++; + } + + if (speed_ticks_period_length > 0 && speed_period_counter > SPEED_SENSOR_TIMEOUT_MS_X10) + { + speed_ticks_period_length = 0; + } + } + + speed_prev_state = spd; + } +} +#pragma restore diff --git a/code/firmware/src/bbsx/stc15.h b/code/firmware/src/bbsx/stc15.h new file mode 100644 index 00000000..3c8b3eb6 --- /dev/null +++ b/code/firmware/src/bbsx/stc15.h @@ -0,0 +1,75 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#ifndef _STC_15_H_ +#define _STC_15_H_ + +#if !defined(SDCC) && !defined(__SDCC) + +#define __sfr unsigned char +#define __sbit bool +#define __at(X) + +#define __xdata +#define __data + +#endif + +#include <8051.h> +#include + +// Peripheral function switch +SFR(P_SW2, 0xBA); + +SFR(PCON2, 0x97); + +SFR(T2H, 0xD6); +SFR(T2L, 0xD7); + +SBIT(P5_4, 0xC8, 4); +SBIT(P5_5, 0xC8, 5); + +#define IS_BIT_SET(REG, BIT_NUM) ((REG >> BIT_NUM) & 1) + +#define SET_BIT(REG, BIT_NUM) (REG |= (1 << BIT_NUM)) +#define CLEAR_BIT(REG, BIT_NUM) (REG &= ~(1 << BIT_NUM)) +#define TOGGLE_BIT(REG, BIT_NUM) (REG ^= (1 << BIT_NUM)) + +#define EXPAND(x) x + +#define SET_PIN_INPUT_(PORT, PIN) \ + CLEAR_BIT(P##PORT##M0, PIN); \ + SET_BIT(P##PORT##M1, PIN) +#define SET_PIN_INPUT(...) EXPAND(SET_PIN_INPUT_(__VA_ARGS__)) + +#define SET_PIN_QUASI_(PORT, PIN) \ + CLEAR_BIT(P##PORT##M0, PIN); \ + CLEAR_BIT(P##PORT##M1, PIN) +#define SET_PIN_QUASI(...) EXPAND(SET_PIN_QUASI_(__VA_ARGS__)) + +#define SET_PIN_OUTPUT_(PORT, PIN) \ + SET_BIT(P##PORT##M0, PIN); \ + CLEAR_BIT(P##PORT##M1, PIN) +#define SET_PIN_OUTPUT(...) EXPAND(SET_PIN_OUTPUT_(__VA_ARGS__)) + +#define GET_PIN_STATE_(PORT, PIN) P##PORT##_##PIN +#define GET_PIN_STATE(...) EXPAND(GET_PIN_STATE_(__VA_ARGS__)) + +#define SET_PIN_HIGH_(PORT, PIN) P##PORT##_##PIN = 1 +#define SET_PIN_HIGH(...) EXPAND(SET_PIN_HIGH_(__VA_ARGS__)) + +#define SET_PIN_LOW_(PORT, PIN) P##PORT##_##PIN = 0 +#define SET_PIN_LOW(...) EXPAND(SET_PIN_LOW_(__VA_ARGS__)) + +#define GET_PIN_NUM_(PORT, PIN) PIN +#define GET_PIN_NUM(...) EXPAND(GET_PIN_NUM_(__VA_ARGS__)) + +#define GET_PORT_NUM_(PORT, PIN) PORT +#define GET_PORT_NUM(...) EXPAND(GET_PORT_NUM_(__VA_ARGS__)) + +#endif diff --git a/code/firmware/src/bbsx/system.c b/code/firmware/src/bbsx/system.c new file mode 100644 index 00000000..a7d223cf --- /dev/null +++ b/code/firmware/src/bbsx/system.c @@ -0,0 +1,66 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "system.h" +#include "bbsx/stc15.h" +#include "timers.h" +#include "watchdog.h" + +static volatile uint32_t _ms; +static volatile uint8_t _x100us; + +void system_init() +{ + _ms = 0; + _x100us = 0; + + // Wait for stable voltage (above lvd) + while (IS_BIT_SET(PCON, 5)) + { + CLEAR_BIT(PCON, 5); + } + + timer0_init_system(); +} + +uint32_t system_ms() +{ + uint32_t val; + uint8_t et0 = ET0; + ET0 = 0; // disable timer0 interrupts + val = _ms; + ET0 = et0; + return val; +} + +void system_delay_ms(uint16_t ms) +{ + if (!ms) + { + return; + } + + uint32_t end = system_ms() + ms; + while (system_ms() != end) + { + watchdog_yeild(); + } +} + +#pragma save +#pragma nooverlay // See SDCC manual about function calls in ISR +void system_timer0_isr() +{ + _x100us++; + if (_x100us == 10) + { + _x100us = 0; + _ms++; + } +} +#pragma restore diff --git a/code/firmware/src/bbsx/timers.c b/code/firmware/src/bbsx/timers.c new file mode 100644 index 00000000..966f7d38 --- /dev/null +++ b/code/firmware/src/bbsx/timers.c @@ -0,0 +1,101 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "timers.h" +#include "bbsx/cpu.h" +#include "bbsx/interrupt.h" +#include "bbsx/timers.h" + +#include + +#define TIMER0_RELOAD ((65535 - CPU_FREQ / 10000) + 1) + +extern void system_timer0_isr(); +extern void sensors_timer0_isr(); + +static bool timer0_system_ready; +static bool timer0_sensors_ready; + +static void timer0_init() +{ + if (timer0_system_ready || timer0_sensors_ready) + { + // already initialized + return; + } + + EA = 0; // disable interrupts + + TMOD = (TMOD & 0xf0) | 0x00; // Timer 0: 16-bit with autoreload + AUXR |= 0x80; // Run timer 0 at CPU_FREQ + + TH0 = TIMER0_RELOAD >> 8; + TL0 = TIMER0_RELOAD; + + EA = 1; // enable interrupts + ET0 = 1; // enable timer0 interrupts + TR0 = 1; // start timer 0 +} + +void timers_init() +{ + timer0_system_ready = false; + timer0_sensors_ready = false; +} + +void timer0_init_system() +{ + timer0_init(); + timer0_system_ready = true; +} + +void timer0_init_sensors() +{ + timer0_init(); + timer0_sensors_ready = true; +} + +void timer1_init_uart1(uint32_t baudrate) +{ + unsigned short reload = 65535 - CPU_FREQ / 4 / baudrate + 1; + + // Set up timer 1 for baudrate + TMOD = (TMOD & 0x0f) | 0x00; // Run T1 in mode 0 (16-bit reload) + AUXR |= 0x40; // Run T1 at CPU_FREQ + TL1 = reload; // Set the reload value for given baudrate. + TH1 = reload >> 8; + ET1 = 0; // No interrupts from timer 1. + TR1 = 1; // Start timer 1 +} + +void timer2_init_uart2(uint32_t baudrate) +{ + unsigned short reload = 65535 - CPU_FREQ / 4 / baudrate + 1; + + // Set up timer 2 for baudrate + AUXR &= ~(1 << 3); // as timer + AUXR |= (1 << 2); // Run T2 at CPU_FREQ + T2H = reload >> 8; + T2L = reload; + IE2 &= ~(1 << 2); // No interrupts from timer 2 + AUXR |= (1 << 4); // Start timer 2 +} + +// timer0 is shared between system ms counter and sensors check +INTERRUPT_USING(isr_timer0, IRQ_TIMER0, 1) +{ + if (timer0_system_ready) + { + system_timer0_isr(); + } + + if (timer0_sensors_ready) + { + sensors_timer0_isr(); + } +} diff --git a/src/firmware/bbsx/timers.h b/code/firmware/src/bbsx/timers.h similarity index 88% rename from src/firmware/bbsx/timers.h rename to code/firmware/src/bbsx/timers.h index 2e6c7d1f..2e68527a 100644 --- a/src/firmware/bbsx/timers.h +++ b/code/firmware/src/bbsx/timers.h @@ -1,7 +1,7 @@ /* * bbs-fw * - * Copyright (C) Daniel Nilsson, 2022. + * Copyright (C) Daniel Nilsson, 2024. * * Released under the GPL License, Version 3 */ diff --git a/code/firmware/src/bbsx/uart.c b/code/firmware/src/bbsx/uart.c new file mode 100644 index 00000000..3cb1707d --- /dev/null +++ b/code/firmware/src/bbsx/uart.c @@ -0,0 +1,280 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "uart.h" +#include "bbsx/pins.h" +#include "bbsx/stc15.h" +#include "bbsx/timers.h" +#include "bbsx/uart_motor.h" +#include "system.h" +#include "watchdog.h" + +#include + +// NOTE: +// Variables located in __data are there for atomic access. + +// UART1 (main) +#define RX1_BUFFER_SIZE 64 +#define RX1_BUFFER_MASK (RX1_BUFFER_SIZE - 1) + +#define TX1_BUFFER_SIZE 32 +#define TX1_BUFFER_MASK (TX1_BUFFER_SIZE - 1) + +static volatile __data uint8_t rx1_head; +static volatile __data uint8_t rx1_tail; +static volatile uint8_t rx1_buf[RX1_BUFFER_SIZE]; +static volatile __data uint8_t tx1_head; +static volatile __data uint8_t tx1_tail; +static volatile __data uint8_t tx1_sending; +static volatile uint8_t tx1_buf[TX1_BUFFER_SIZE]; + +// UART2 (motor) +#define RX2_BUFFER_SIZE 16 +#define RX2_BUFFER_MASK (RX2_BUFFER_SIZE - 1) + +#define TX2_BUFFER_SIZE 16 +#define TX2_BUFFER_MASK (TX2_BUFFER_SIZE - 1) + +static volatile __data uint8_t rx2_head; +static volatile __data uint8_t rx2_tail; +static volatile uint8_t rx2_buf[RX2_BUFFER_SIZE]; +static volatile __data uint8_t tx2_head; +static volatile __data uint8_t tx2_tail; +static volatile __data uint8_t tx2_sending; +static volatile uint8_t tx2_buf[TX2_BUFFER_SIZE]; + +void uart_open(uint32_t baudrate) +{ + rx1_head = 0; + rx1_tail = 0; + tx1_head = 0; + tx1_tail = 0; + tx1_sending = 0; + +#if (GET_PORT_NUM(PIN_EXTERNAL_RX) == 3 && GET_PORT_NUM(PIN_EXTERNAL_TX) == 3) + AUXR1 = (AUXR1 & 0x3f) | 0x00; // Keep UART1 on P3.0/P3.1 +#else +#error Unupported UART port configured. +#endif + + SET_PIN_QUASI(PIN_EXTERNAL_RX); + SET_PIN_QUASI(PIN_EXTERNAL_TX); + + AUXR &= ~0x01; // Clock UART1 from T1 + PCON &= ~0x40; // Expose SM0 bit + SM1 = 1; // UART 8-N-1 + SM0 = 0; + SM2 = 0; // Point-to-point UART + ES = 1; // Enable serial interrupt + + timer1_init_uart1(baudrate); + + REN = 1; // Rx enable +} + +void uart_motor_open(uint32_t baudrate) +{ + rx2_head = 0; + rx2_tail = 0; + tx2_head = 0; + tx2_tail = 0; + tx2_sending = 0; + +#if (GET_PORT_NUM(PIN_MOTOR_RX) == 1 && GET_PORT_NUM(PIN_MOTOR_TX) == 1) + { + P_SW2 = (P_SW2 & 0xfe) | 0x00; // Keep UART2 on P1.0/P1.1 + } +#else +#error Unupported UART port configured. +#endif + + SET_PIN_QUASI(PIN_MOTOR_RX); + SET_PIN_QUASI(PIN_MOTOR_TX); + + // UART 2 can only user timer 2 + S2CON &= ~(1 << 7); // UART 8-N-1 + S2CON &= ~(1 << 5); // Point-to-point UART + IE2 |= (1 << 0); // Enable serial 2 interrupt + + timer2_init_uart2(baudrate); + + S2CON |= (1 << 4); // Rx enable +} + +void uart_close() +{ + REN = 0; + uart_flush(); + TR1 = 0; +} + +void uart_motor_close() +{ + S2CON &= ~(1 << 4); // Rx disable + uart_motor_flush(); + AUXR &= ~(1 << 4); // Stop timer 2 +} + +uint8_t uart_available() +{ + return (RX1_BUFFER_SIZE + rx1_head - rx1_tail) & RX1_BUFFER_MASK; +} + +uint8_t uart_motor_available() +{ + return (RX2_BUFFER_SIZE + rx2_head - rx2_tail) & RX2_BUFFER_MASK; +} + +uint8_t uart_read() +{ + uint8_t byte = rx1_buf[rx1_tail]; + rx1_tail = (rx1_tail + 1) & RX1_BUFFER_MASK; + return byte; +} + +uint8_t uart_motor_read() +{ + uint8_t byte = rx2_buf[rx2_tail]; + rx2_tail = (rx2_tail + 1) & RX2_BUFFER_MASK; + return byte; +} + +void uart_write(uint8_t byte) +{ + if (!tx1_sending) + { + tx1_sending = 1; + SBUF = byte; + + return; + } + + uint8_t i = (tx1_head + 1) & TX1_BUFFER_MASK; + + // wait for free space in buffer + uint8_t prev_tail = tx1_tail; + while (i == tx1_tail) + { + if (tx1_tail != prev_tail) + { + prev_tail = tx1_tail; + watchdog_yeild(); + } + } + + tx1_buf[tx1_head] = byte; + tx1_head = i; +} + +void uart_motor_write(uint8_t byte) +{ + if (!tx2_sending) + { + tx2_sending = 1; + S2BUF = byte; + + return; + } + + uint8_t i = (tx2_head + 1) & TX2_BUFFER_MASK; + + // wait for free space in buffer + uint8_t prev_tail = tx2_tail; + while (i == tx2_tail) + { + if (tx2_tail != prev_tail) + { + prev_tail = tx2_tail; + watchdog_yeild(); + } + } + + tx2_buf[tx2_head] = byte; + tx2_head = i; +} + +void uart_flush() +{ + while (tx1_sending) + ; +} + +void uart_motor_flush() +{ + while (tx2_sending) + ; +} + +INTERRUPT_USING(isr_uart1, IRQ_UART1, 3) +{ + if (RI) // rx interrupt + { + RI = 0; + + uint8_t c = SBUF; + uint8_t i = (rx1_head + 1) & RX1_BUFFER_MASK; + + if (i != rx1_tail) + { + rx1_buf[rx1_head] = c; + rx1_head = i; + } + } + + if (TI) // tx interrupt + { + TI = 0; + + if (tx1_head != tx1_tail) + { + tx1_sending = 1; + + SBUF = tx1_buf[tx1_tail]; + tx1_tail = (tx1_tail + 1) & TX1_BUFFER_MASK; + } + else + { + tx1_sending = 0; + } + } +} + +INTERRUPT_USING(isr_uart2, IRQ_UART2, 3) +{ + if (S2CON & (1 << 0)) // rx interrupt + { + S2CON &= ~(1 << 0); + + uint8_t c = S2BUF; + uint8_t i = (rx2_head + 1) & RX2_BUFFER_MASK; + + if (i != rx2_tail) + { + rx2_buf[rx2_head] = c; + rx2_head = i; + } + } + + if (S2CON & (1 << 1)) // tx interrupt + { + S2CON &= ~(1 << 1); + + if (tx2_head != tx2_tail) + { + tx2_sending = 1; + + S2BUF = tx2_buf[tx2_tail]; + tx2_tail = (tx2_tail + 1) & TX2_BUFFER_MASK; + } + else + { + tx2_sending = 0; + } + } +} diff --git a/src/firmware/bbsx/uart_motor.h b/code/firmware/src/bbsx/uart_motor.h similarity index 91% rename from src/firmware/bbsx/uart_motor.h rename to code/firmware/src/bbsx/uart_motor.h index 0f778f8b..325ff664 100644 --- a/src/firmware/bbsx/uart_motor.h +++ b/code/firmware/src/bbsx/uart_motor.h @@ -1,7 +1,7 @@ /* * bbs-fw * - * Copyright (C) Daniel Nilsson, 2022. + * Copyright (C) Daniel Nilsson, 2024. * * Released under the GPL License, Version 3 */ @@ -9,8 +9,8 @@ #ifndef _BBSX_UART_MOTOR_H_ #define _BBSX_UART_MOTOR_H_ -#include "bbsx/stc15.h" #include "bbsx/interrupt.h" +#include "bbsx/stc15.h" #include diff --git a/src/firmware/bbsx/watchdog.c b/code/firmware/src/bbsx/watchdog.c similarity index 51% rename from src/firmware/bbsx/watchdog.c rename to code/firmware/src/bbsx/watchdog.c index 97d2f6ea..2f2f1fa5 100644 --- a/src/firmware/bbsx/watchdog.c +++ b/code/firmware/src/bbsx/watchdog.c @@ -1,7 +1,7 @@ /* * bbs-fw * - * Copyright (C) Daniel Nilsson, 2022. + * Copyright (C) Daniel Nilsson, 2024. * * Released under the GPL License, Version 3 */ @@ -13,16 +13,16 @@ static bool triggered; void watchdog_init() { - triggered = IS_BIT_SET(WDT_CONTR, 7); - WDT_CONTR = 0x34; // Enable watchdog timer, pre-scaler 32 (625ms, 20MHz) + triggered = IS_BIT_SET(WDT_CONTR, 7); + WDT_CONTR = 0x34; // Enable watchdog timer, pre-scaler 32 (625ms, 20MHz) } void watchdog_yeild() { - SET_BIT(WDT_CONTR, 4); + SET_BIT(WDT_CONTR, 4); } bool watchdog_triggered() { - return triggered; + return triggered; } diff --git a/code/firmware/src/cfgstore.c b/code/firmware/src/cfgstore.c new file mode 100644 index 00000000..84e40e83 --- /dev/null +++ b/code/firmware/src/cfgstore.c @@ -0,0 +1,385 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2022. + * + * Released under the GPL License, Version 3 + */ + +#include "cfgstore.h" +#include "eeprom.h" +#include "eventlog.h" +#include "fwconfig.h" +#include "uart.h" + +#include + +#define EEPROM_CONFIG_PAGE 0 +#define EEPROM_PSTATE_PAGE 1 + +#define EEPROM_OK 0 +#define EEPROM_ERROR_SELECT_PAGE 1 +#define EEPROM_ERROR_READ 2 +#define EEPROM_ERROR_VERSION 3 +#define EEPROM_ERROR_LENGHT 4 +#define EEPROM_ERROR_CHECKSUM 5 +#define EEPROM_ERROR_ERASE 6 +#define EEPROM_ERROR_WRITE 7 + +static const uint8_t default_current_limits[] = {7, 10, 14, 19, 26, 36, 50, 70, 98}; + +#if HAS_TORQUE_SENSOR +static const uint8_t default_torque_factors[] = {10, 15, 23, 44, 57, 74, 88, 105, 126}; +#endif + +typedef struct +{ + uint8_t version; + uint8_t length; + uint8_t checksum; +} header_t; + +static header_t header; + +config_t g_config; +pstate_t g_pstate; + +static uint8_t read(uint8_t page, uint8_t version, uint8_t *dst, uint8_t size); +static uint8_t write(uint8_t page, uint8_t version, uint8_t *src, uint8_t size); + +static bool read_config(); +static bool write_config(); +static void load_default_config(); + +static bool read_pstate(); +static bool write_pstate(); +static void load_default_pstate(); + +void cfgstore_init() +{ + if (!read_config()) + { + cfgstore_reset_config(); + } + + if (!read_pstate()) + { + cfgstore_reset_pstate(); + } +} + +bool cfgstore_reset_config() +{ + load_default_config(); + if (write_config()) + { + eventlog_write(EVT_MSG_CONFIG_RESET); + return true; + } + + return false; +} + +bool cfgstore_save_config() +{ + return write_config(); +} + +bool cfgstore_reset_pstate() +{ + load_default_pstate(); + return write_pstate(); +} + +bool cfgstore_save_pstate() +{ + return write_pstate(); +} + +static bool read_config() +{ + eventlog_write(EVT_MSG_CONFIG_READ_BEGIN); + + uint8_t res = read(EEPROM_CONFIG_PAGE, CONFIG_VERSION, (uint8_t *)&g_config, sizeof(config_t)); + switch (res) + { + default: + eventlog_write(EVT_ERROR_EEPROM_READ); + break; + case EEPROM_ERROR_VERSION: + eventlog_write(EVT_ERROR_EEPROM_VERIFY_VERSION); + break; + case EEPROM_ERROR_LENGHT: + case EEPROM_ERROR_CHECKSUM: + eventlog_write(EVT_ERROR_EEPROM_VERIFY_CHECKSUM); + break; + case EEPROM_OK: + eventlog_write(EVT_MSG_CONFIG_READ_DONE); + break; + } + + return res == EEPROM_OK; +} + +static bool write_config() +{ + eventlog_write(EVT_MSG_CONFIG_WRITE_BEGIN); + + uint8_t res = write(EEPROM_CONFIG_PAGE, CONFIG_VERSION, (uint8_t *)&g_config, sizeof(config_t)); + switch (res) + { + default: + eventlog_write(EVT_ERROR_EEPROM_WRITE); + break; + case EEPROM_ERROR_ERASE: + eventlog_write(EVT_ERROR_EEPROM_ERASE); + break; + case EEPROM_OK: + eventlog_write(EVT_MSG_CONFIG_WRITE_DONE); + break; + } + + return res == EEPROM_OK; +} + +static void load_default_config() +{ + g_config.use_freedom_units = 0; + +#if defined(BBSHD) + g_config.max_current_amps = 30; +#elif defined(BBS02) + g_config.max_current_amps = 25; +#else + g_config.max_current_amps = 20; +#endif + + g_config.current_ramp_amps_s = 10; + g_config.max_battery_x100v_u16l = (uint8_t)5460; + g_config.max_battery_x100v_u16h = (uint8_t)(5460 >> 8); + g_config.low_cut_off_v = 42; + + g_config.use_speed_sensor = 1; + g_config.use_shift_sensor = HAS_SHIFT_SENSOR_SUPPORT; + g_config.use_push_walk = 1; + g_config.use_pretension = 0; + g_config.pretension_speed_cutoff_kph = 16; + g_config.use_temperature_sensor = TEMPERATURE_SENSOR_CONTR | TEMPERATURE_SENSOR_MOTOR; + + g_config.lights_mode = LIGHTS_MODE_DEFAULT; + + g_config.wheel_size_inch_x10_u16l = (uint8_t)280; + g_config.wheel_size_inch_x10_u16h = (uint8_t)(280 >> 8); + + g_config.speed_sensor_signals = 1; + g_config.max_speed_kph = 100; + + g_config.pas_start_delay_pulses = 5; + g_config.pas_stop_delay_x100s = 20; + g_config.pas_keep_current_percent = 60; + g_config.pas_keep_current_cadence_rpm = 40; + + g_config.throttle_start_voltage_mv_u16l = (uint8_t)1000; + g_config.throttle_start_voltage_mv_u16h = (uint8_t)(1000 >> 8); + g_config.throttle_end_voltage_mv_u16l = (uint8_t)3600; + g_config.throttle_end_voltage_mv_u16h = (uint8_t)(3600 >> 8); + g_config.throttle_start_percent = 1; + g_config.throttle_global_spd_lim_opt = THROTTLE_GLOBAL_SPEED_LIMIT_DISABLED; + g_config.throttle_global_spd_lim_percent = 100; + + g_config.shift_interrupt_duration_ms_u16l = (uint8_t)600; + g_config.shift_interrupt_duration_ms_u16h = (uint8_t)(600 >> 8); + g_config.shift_interrupt_current_threshold_percent = 10; + + g_config.walk_mode_data_display = WALK_MODE_DATA_SPEED; + + g_config.assist_mode_select = ASSIST_MODE_SELECT_OFF; + g_config.assist_startup_level = 3; + + memset(&g_config.assist_levels, 0, 20 * sizeof(assist_level_t)); + + for (uint8_t i = 0; i < 9; ++i) + { + g_config.assist_levels[0][i + 1].flags = ASSIST_FLAG_PAS | ASSIST_FLAG_THROTTLE; + g_config.assist_levels[0][i + 1].max_cadence_percent = 100; + g_config.assist_levels[0][i + 1].max_speed_percent = 100; + g_config.assist_levels[0][i + 1].max_throttle_current_percent = 100; + +#if HAS_TORQUE_SENSOR + g_config.assist_levels[0][i + 1].flags |= ASSIST_FLAG_PAS_TORQUE; + g_config.assist_levels[0][i + 1].target_current_percent = 100; + g_config.assist_levels[0][i + 1].torque_amplification_factor_x10 = default_torque_factors[i]; +#else + g_config.assist_levels[0][i + 1].target_current_percent = default_current_limits[i]; + g_config.assist_levels[0][i + 1].torque_amplification_factor_x10 = 0; +#endif + } +} + +static bool read_pstate() +{ + eventlog_write(EVT_MSG_PSTATE_READ_BEGIN); + + uint8_t res = read(EEPROM_PSTATE_PAGE, PSTATE_VERSION, (uint8_t *)&g_pstate, sizeof(pstate_t)); + switch (res) + { + default: + eventlog_write(EVT_ERROR_EEPROM_READ); + break; + case EEPROM_ERROR_VERSION: + eventlog_write(EVT_ERROR_EEPROM_VERIFY_VERSION); + break; + case EEPROM_ERROR_LENGHT: + case EEPROM_ERROR_CHECKSUM: + eventlog_write(EVT_ERROR_EEPROM_VERIFY_CHECKSUM); + break; + case EEPROM_OK: + eventlog_write(EVT_MSG_PSTATE_READ_DONE); + break; + } + + return res == EEPROM_OK; +} + +static bool write_pstate() +{ + eventlog_write(EVT_MSG_PSTATE_WRITE_BEGIN); + + uint8_t res = write(EEPROM_PSTATE_PAGE, PSTATE_VERSION, (uint8_t *)&g_pstate, sizeof(pstate_t)); + switch (res) + { + default: + eventlog_write(EVT_ERROR_EEPROM_WRITE); + break; + case EEPROM_ERROR_ERASE: + eventlog_write(EVT_ERROR_EEPROM_ERASE); + break; + case EEPROM_OK: + eventlog_write(EVT_MSG_PSTATE_WRITE_DONE); + break; + } + + return res == EEPROM_OK; +} + +static void load_default_pstate() +{ + g_pstate.adc_voltage_calibration_steps_x100_i16l = 0; + g_pstate.adc_voltage_calibration_steps_x100_i16h = 0; +} + +static uint8_t read(uint8_t page, uint8_t version, uint8_t *dst, uint8_t size) +{ + uint8_t read_offset = 0; + uint8_t *ptr = 0; + uint8_t i = 0; + int data; + + if (!eeprom_select_page(page)) + { + return EEPROM_ERROR_SELECT_PAGE; + } + + ptr = (uint8_t *)&header; + for (i = 0; i < sizeof(header_t); ++i) + { + data = eeprom_read_byte(read_offset); + if (data < 0) + { + return EEPROM_ERROR_READ; + } + *ptr = (uint8_t)data; + ++read_offset; + ++ptr; + } + + // verify header ok + if (header.version != version) + { + return EEPROM_ERROR_VERSION; + } + + if (header.length != size) + { + return EEPROM_ERROR_LENGHT; + } + + uint8_t checksum = 0; + + ptr = dst; + for (i = 0; i < size; ++i) + { + data = eeprom_read_byte(read_offset); + if (data < 0) + { + return EEPROM_ERROR_READ; + } + + checksum += (uint8_t)data; + *ptr = (uint8_t)data; + ++read_offset; + ++ptr; + } + + if (header.checksum != checksum) + { + return EEPROM_ERROR_CHECKSUM; + } + + return EEPROM_OK; +} + +static uint8_t write(uint8_t page, uint8_t version, uint8_t *src, uint8_t size) +{ + uint8_t write_offset = 0; + uint8_t *ptr = 0; + uint8_t i = 0; + + header.version = version; + header.length = size; + header.checksum = 0; + + if (!eeprom_select_page(page)) + { + return EEPROM_ERROR_SELECT_PAGE; + } + + if (!eeprom_erase_page()) + { + return EEPROM_ERROR_ERASE; + } + + write_offset += sizeof(header_t); + + ptr = src; + for (i = 0; i < size; ++i) + { + if (!eeprom_write_byte(write_offset, *ptr)) + { + eeprom_end_write(); + return EEPROM_ERROR_WRITE; + } + + header.checksum += *ptr; + ++write_offset; + ++ptr; + } + + write_offset = 0; + ptr = (uint8_t *)&header; + for (i = 0; i < sizeof(header_t); ++i) + { + if (!eeprom_write_byte(write_offset, *ptr)) + { + eeprom_end_write(); + return EEPROM_ERROR_WRITE; + } + + ++write_offset; + ++ptr; + } + + eeprom_end_write(); + + return EEPROM_OK; +} diff --git a/code/firmware/src/cfgstore.h b/code/firmware/src/cfgstore.h new file mode 100644 index 00000000..07b0034b --- /dev/null +++ b/code/firmware/src/cfgstore.h @@ -0,0 +1,144 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#ifndef _CFGSTORE_H_ +#define _CFGSTORE_H_ + +#include +#include + +#define ASSIST_FLAG_PAS 0x01 +#define ASSIST_FLAG_THROTTLE 0x02 +#define ASSIST_FLAG_CRUISE 0x04 +#define ASSIST_FLAG_PAS_VARIABLE 0x08 // pas mode using throttle to set power level +#define ASSIST_FLAG_PAS_TORQUE 0x10 // pas mode using torque sensor reading +#define ASSIST_FLAG_OVERRIDE_CADENCE 0x20 // pas option where max cadence is set to 100% when throttle overrides pas +#define ASSIST_FLAG_OVERRIDE_SPEED 0x40 // pas option where max speed is set to 100% when throttle overrides pas + +#define ASSIST_MODE_SELECT_OFF 0x00 +#define ASSIST_MODE_SELECT_STANDARD 0x01 +#define ASSIST_MODE_SELECT_LIGHTS 0x02 +#define ASSIST_MODE_SELECT_PAS0_LIGHT 0x03 +#define ASSIST_MODE_SELECT_PAS1_LIGHT 0x04 +#define ASSIST_MODE_SELECT_PAS2_LIGHT 0x05 +#define ASSIST_MODE_SELECT_PAS3_LIGHT 0x06 +#define ASSIST_MODE_SELECT_PAS4_LIGHT 0x07 +#define ASSIST_MODE_SELECT_PAS5_LIGHT 0x08 +#define ASSIST_MODE_SELECT_PAS6_LIGHT 0x09 +#define ASSIST_MODE_SELECT_PAS7_LIGHT 0x0A +#define ASSIST_MODE_SELECT_PAS8_LIGHT 0x0B +#define ASSIST_MODE_SELECT_PAS9_LIGHT 0x0C +#define ASSIST_MODE_SELECT_BRAKE_BOOT 0x0D + +#define TEMPERATURE_SENSOR_CONTR 0x01 +#define TEMPERATURE_SENSOR_MOTOR 0x02 + +#define WALK_MODE_DATA_SPEED 0 +#define WALK_MODE_DATA_TEMPERATURE 1 +#define WALK_MODE_DATA_REQUESTED_POWER 2 +#define WALK_MODE_DATA_BATTERY_PERCENT 3 + +#define THROTTLE_GLOBAL_SPEED_LIMIT_DISABLED 0 +#define THROTTLE_GLOBAL_SPEED_LIMIT_ENABLED 1 +#define THROTTLE_GLOBAL_SPEED_LIMIT_STD_LVLS 2 + +#define LIGHTS_MODE_DEFAULT 0 +#define LIGHTS_MODE_DISABLED 1 +#define LIGHTS_MODE_ALWAYS_ON 2 +#define LIGHTS_MODE_BRAKE_LIGHT 3 + +#define CONFIG_VERSION 5 +#define PSTATE_VERSION 1 + +typedef struct +{ + uint8_t flags; + uint8_t target_current_percent; + uint8_t max_throttle_current_percent; + uint8_t max_cadence_percent; + uint8_t max_speed_percent; + + // 10 => 1.0: 100w human power gives and additional 100w motor power + uint8_t torque_amplification_factor_x10; +} assist_level_t; + +// SDCC uses little endian for MCS51 and big endian for STM8... +typedef struct +{ + // hmi units + uint8_t use_freedom_units; + + // global + uint8_t max_current_amps; + uint8_t current_ramp_amps_s; + uint8_t max_battery_x100v_u16l; + uint8_t max_battery_x100v_u16h; + uint8_t low_cut_off_v; + uint8_t max_speed_kph; + + // externals + uint8_t use_speed_sensor; + uint8_t use_shift_sensor; + uint8_t use_push_walk; + uint8_t use_temperature_sensor; + uint8_t lights_mode; + uint8_t use_pretension; + uint8_t pretension_speed_cutoff_kph; + + // speed sensor + uint8_t wheel_size_inch_x10_u16l; + uint8_t wheel_size_inch_x10_u16h; + uint8_t speed_sensor_signals; + + // pas options + uint8_t pas_start_delay_pulses; + uint8_t pas_stop_delay_x100s; + uint8_t pas_keep_current_percent; + uint8_t pas_keep_current_cadence_rpm; + + // throttle options + uint8_t throttle_start_voltage_mv_u16l; + uint8_t throttle_start_voltage_mv_u16h; + uint8_t throttle_end_voltage_mv_u16l; + uint8_t throttle_end_voltage_mv_u16h; + uint8_t throttle_start_percent; + uint8_t throttle_global_spd_lim_opt; + uint8_t throttle_global_spd_lim_percent; + + // shift interrupt options + uint8_t shift_interrupt_duration_ms_u16l; + uint8_t shift_interrupt_duration_ms_u16h; + uint8_t shift_interrupt_current_threshold_percent; + + // misc + uint8_t walk_mode_data_display; + + // assist levels + uint8_t assist_mode_select; + uint8_t assist_startup_level; + assist_level_t assist_levels[2][10]; +} config_t; + +typedef struct +{ + uint8_t adc_voltage_calibration_steps_x100_i16l; + uint8_t adc_voltage_calibration_steps_x100_i16h; +} pstate_t; + +extern config_t g_config; +extern pstate_t g_pstate; + +void cfgstore_init(); + +bool cfgstore_reset_config(); +bool cfgstore_save_config(); + +bool cfgstore_reset_pstate(); +bool cfgstore_save_pstate(); + +#endif diff --git a/src/firmware/eeprom.h b/code/firmware/src/eeprom.h similarity index 84% rename from src/firmware/eeprom.h rename to code/firmware/src/eeprom.h index 42febeaa..8ee59566 100644 --- a/src/firmware/eeprom.h +++ b/code/firmware/src/eeprom.h @@ -1,7 +1,7 @@ /* * bbs-fw * - * Copyright (C) Daniel Nilsson, 2022. + * Copyright (C) Daniel Nilsson, 2024. * * Released under the GPL License, Version 3 */ @@ -9,9 +9,8 @@ #ifndef _EEPROM_H_ #define _EEPROM_H_ -#include "intellisense.h" -#include #include +#include void eeprom_init(); bool eeprom_select_page(int page); diff --git a/code/firmware/src/eventlog.c b/code/firmware/src/eventlog.c new file mode 100644 index 00000000..c89c972b --- /dev/null +++ b/code/firmware/src/eventlog.c @@ -0,0 +1,58 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "eventlog.h" +#include "uart.h" + +static bool is_enabled; + +void eventlog_init(bool enabled) +{ + is_enabled = enabled; +} + +bool eventlog_is_enabled() +{ + return is_enabled; +} + +void eventlog_set_enabled(bool enabled) +{ + is_enabled = enabled; +} + +void eventlog_write(uint8_t evt) +{ + if (!is_enabled) + { + return; + } + + uart_write(0xee); + uart_write(evt); + uart_write((uint8_t)0xee + evt); +} +void eventlog_write_data(uint8_t evt, int16_t data) +{ + if (!is_enabled) + { + return; + } + + uint8_t checksum = 0; + + uart_write(0xed); + checksum += (uint8_t)0xed; + uart_write(evt); + checksum += evt; + uart_write((uint8_t)(data >> 8)); + checksum += (uint8_t)(data >> 8); + uart_write((uint8_t)data); + checksum += (uint8_t)data; + uart_write(checksum); +} diff --git a/code/firmware/src/eventlog.h b/code/firmware/src/eventlog.h new file mode 100644 index 00000000..516b7dca --- /dev/null +++ b/code/firmware/src/eventlog.h @@ -0,0 +1,74 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#ifndef _EVENTLOG_H_ +#define _EVENTLOG_H_ + +#include +#include + +#define EVT_MSG_MOTOR_INIT_OK 1 +#define EVT_MSG_CONFIG_READ_DONE 2 +#define EVT_MSG_CONFIG_RESET 3 +#define EVT_MSG_CONFIG_WRITE_DONE 4 +#define EVT_MSG_CONFIG_READ_BEGIN 5 +#define EVT_MSG_CONFIG_WRITE_BEGIN 6 +#define EVT_MSG_PSTATE_READ_BEGIN 7 +#define EVT_MSG_PSTATE_READ_DONE 8 +#define EVT_MSG_PSTATE_WRITE_BEGIN 9 +#define EVT_MSG_PSTATE_WRITE_DONE 10 + +#define EVT_ERROR_INIT_MOTOR 64 +#define EVT_ERROR_CHANGE_TARGET_SPEED 65 +#define EVT_ERROR_CHANGE_TARGET_CURRENT 66 +#define EVT_ERROR_READ_MOTOR_STATUS 67 +#define EVT_ERROR_READ_MOTOR_CURRENT 68 +#define EVT_ERROR_READ_MOTOR_VOLTAGE 69 + +#define EVT_ERROR_EEPROM_READ 70 +#define EVT_ERROR_EEPROM_WRITE 71 +#define EVT_ERROR_EEPROM_ERASE 72 +#define EVT_ERROR_EEPROM_VERIFY_VERSION 73 +#define EVT_ERROR_EEPROM_VERIFY_CHECKSUM 74 +#define EVT_ERROR_THROTTLE_LOW_LIMIT 75 +#define EVT_ERROR_THROTTLE_HIGH_LIMIT 76 +#define EVT_ERROR_WATCHDOG_TRIGGERED 77 +#define EVT_ERROR_EXTCOM_CHEKSUM 78 +#define EVT_ERROR_EXTCOM_DISCARD 79 + +#define EVT_DATA_TARGET_CURRENT 128 +#define EVT_DATA_TARGET_SPEED 129 +#define EVT_DATA_MOTOR_STATUS 130 +#define EVT_DATA_ASSIST_LEVEL 131 +#define EVT_DATA_OPERATION_MODE 132 +#define EVT_DATA_WHEEL_SPEED_PPM 133 +#define EVT_DATA_LIGHTS 134 +#define EVT_DATA_TEMPERATURE 135 +#define EVT_DATA_THERMAL_LIMITING 136 +#define EVT_DATA_SPEED_LIMITING 137 +#define EVT_DATA_MAX_CURRENT_ADC_REQUEST 138 +#define EVT_DATA_MAX_CURRENT_ADC_RESPONSE 139 +#define EVT_DATA_MAIN_LOOP_TIME 140 +#define EVT_DATA_THROTTLE_ADC 141 +#define EVT_DATA_LVC_LIMITING 142 +#define EVT_DATA_SHIFT_SENSOR 143 +#define EVT_DATA_BBSHD_THERMISTOR 144 +#define EVT_DATA_VOLTAGE 145 +#define EVT_DATA_CALIBRATE_VOLTAGE 146 +#define EVT_DATA_TORQUE_ADC 147 +#define EVT_DATA_TORQUE_ADC_CALIBRATED 148 + +void eventlog_init(bool enabled); + +bool eventlog_is_enabled(); +void eventlog_set_enabled(bool enabled); + +void eventlog_write(uint8_t evt); +void eventlog_write_data(uint8_t evt, int16_t data); + +#endif diff --git a/code/firmware/src/extcom.c b/code/firmware/src/extcom.c new file mode 100644 index 00000000..97596975 --- /dev/null +++ b/code/firmware/src/extcom.c @@ -0,0 +1,882 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "extcom.h" +#include "app.h" +#include "battery.h" +#include "cfgstore.h" +#include "eventlog.h" +#include "fwconfig.h" +#include "motor.h" +#include "sensors.h" +#include "system.h" +#include "uart.h" +#include "util.h" +#include "version.h" + +#include +#include +#include + +#define KEEP 0 +#define DISCARD -1 + +#define BUFFER_SIZE 192 +#define DISCARD_TIMEOUT_MS 50 + +#define REQUEST_TYPE_READ 0x01 +#define REQUEST_TYPE_WRITE 0x02 + +#define REQUEST_TYPE_BAFANG_READ 0x11 +#define REQUEST_TYPE_BAFANG_WRITE 0x16 + +// Firmware config tool communication +#define OPCODE_READ_FW_VERSION 0x01 +#define OPCODE_READ_EVTLOG_ENABLE 0x02 +#define OPCODE_READ_CONFIG 0x03 +#define OPCODE_READ_STATUS 0x04 + +#define OPCODE_WRITE_EVTLOG_ENABLE 0xf0 +#define OPCODE_WRITE_CONFIG 0xf1 +#define OPCODE_WRITE_RESET_CONFIG 0xf2 +#define OPCODE_WRITE_ADC_VOLTAGE_CALIBRATION 0xf3 + +// Bafang display communication +#define OPCODE_BAFANG_DISPLAY_READ_STATUS 0x08 +#define OPCODE_BAFANG_DISPLAY_READ_CURRENT 0x0a +#define OPCODE_BAFANG_DISPLAY_READ_BATTERY 0x11 +#define OPCODE_BAFANG_DISPLAY_READ_SPEED 0x20 +#define OPCODE_BAFANG_DISPLAY_READ_UNKNOWN1 0x21 +#define OPCODE_BAFANG_DISPLAY_READ_RANGE 0x22 +#define OPCODE_BAFANG_DISPLAY_READ_CALORIES 0x24 +#define OPCODE_BAFANG_DISPLAY_READ_UNKNOWN3 0x25 +#define OPCODE_BAFANG_DISPLAY_READ_MOVING 0x31 + +#define OPCODE_BAFANG_DISPLAY_WRITE_PAS 0x0b +#define OPCODE_BAFANG_DISPLAY_WRITE_MODE 0x0c +#define OPCODE_BAFANG_DISPLAY_WRITE_LIGHTS 0x1a +#define OPCODE_BAFANG_DISPLAY_WRITE_SPEED_LIM 0x1f + +// Bafang config tool communication (not supported, just discard messages) +#define OPCODE_BAFANG_TOOL_READ_CONNECT 0x51 +#define OPCODE_BAFANG_TOOL_READ_BASIC 0x52 +#define OPCODE_BAFANG_TOOL_READ_PAS 0x53 +#define OPCODE_BAFANG_TOOL_READ_THROTTLE 0x54 + +#define OPCODE_BAFANG_TOOL_WRITE_BASIC 0x52 +#define OPCODE_BAFANG_TOOL_WRITE_PAS 0x53 +#define OPCODE_BAFANG_TOOL_WRITE_THROTTLE 0x54 + +static uint8_t msg_len; +static uint8_t msgbuf[BUFFER_SIZE]; +static uint32_t last_recv_ms; +static uint32_t discard_until_ms; + +static uint8_t compute_checksum(uint8_t *buf, uint8_t length); +static void write_uart_and_increment_checksum(uint8_t data, uint8_t *checksum); + +static int16_t try_process_request(); +static int16_t try_process_read_request(); +static int16_t try_process_write_request(); +static int16_t try_process_bafang_read_request(); +static int16_t try_process_bafang_write_request(); + +static int16_t process_read_fw_version(); +static int16_t process_read_evtlog_enable(); +static int16_t process_read_config(); +static int16_t process_read_status(); + +static int16_t process_write_evtlog_enable(); +static int16_t process_write_config(); +static int16_t process_write_reset_config(); +static int16_t process_write_adc_voltage_calibration(); + +static int16_t process_bafang_display_read_status(); +static int16_t process_bafang_display_read_current(); +static int16_t process_bafang_display_read_battery(); +static int16_t process_bafang_display_read_speed(); +static int16_t process_bafang_display_read_unknown1(); +static int16_t process_bafang_display_read_range(); +static int16_t process_bafang_display_read_calories(); +static int16_t process_bafang_display_read_unknown3(); +static int16_t process_bafang_display_read_moving(); + +static int16_t process_bafang_display_write_pas(); +static int16_t process_bafang_display_write_mode(); +static int16_t process_bafang_display_write_lights(); +static int16_t process_bafang_display_write_speed_limit(); + +void extcom_init() +{ + msg_len = 0; + last_recv_ms = 0; + discard_until_ms = 0; + + // Bafang standard baud rate + uart_open(1200); + + // Wait one second for config tool connection. + // This is here to that the config tool can enable + // the event log before system proceeds with initialization. + uint32_t end = system_ms() + 1000; + while (system_ms() < end) + { + extcom_process(); + system_delay_ms(10); + } +} + +void extcom_process() +{ + uint32_t now = system_ms(); + + while (uart_available()) + { + if (msg_len == BUFFER_SIZE || (discard_until_ms != 0 && now < discard_until_ms)) + { + // communication error, reset + msg_len = 0; + while (uart_available()) + uart_read(); + } + else + { + msgbuf[msg_len++] = uart_read(); + last_recv_ms = now; + discard_until_ms = 0; + } + } + + if (msg_len > 0 && now - last_recv_ms > 100) + { + // communication error, reset + msg_len = 0; + } + + int16_t res = try_process_request(); + if (res == DISCARD) + { + msg_len = 0; + last_recv_ms = 0; + // Discard received data for the next DISCARD_TIMEOUT_MS milliseconds + discard_until_ms = now + DISCARD_TIMEOUT_MS; + + eventlog_write(EVT_ERROR_EXTCOM_DISCARD); + } + else if (res > 0) + { + if (res < msg_len) + { + // will not occur due to request/response communication + memcpy(msgbuf, msgbuf + res, msg_len - res); + msg_len -= res; + } + else + { + msg_len = 0; + last_recv_ms = 0; + } + } +} + +static uint8_t compute_checksum(uint8_t *buf, uint8_t length) +{ + uint8_t result = 0; + + for (uint8_t i = 0; i < length; ++i) + { + result += buf[i]; + } + + return result; +} + +static void write_uart_and_increment_checksum(uint8_t data, uint8_t *checksum) +{ + *checksum += data; + uart_write(data); +} + +static int16_t try_process_request() +{ + if (msg_len < 1) + { + return KEEP; + } + + switch (msgbuf[0]) + { + case REQUEST_TYPE_READ: + return try_process_read_request(); + case REQUEST_TYPE_WRITE: + return try_process_write_request(); + case REQUEST_TYPE_BAFANG_READ: + return try_process_bafang_read_request(); + case REQUEST_TYPE_BAFANG_WRITE: + return try_process_bafang_write_request(); + } + + return DISCARD; // unknown message +} + +static int16_t try_process_read_request() +{ + if (msg_len < 2) + { + return KEEP; + } + + switch (msgbuf[1]) + { + case OPCODE_READ_FW_VERSION: + return process_read_fw_version(); + case OPCODE_READ_EVTLOG_ENABLE: + return process_read_evtlog_enable(); + case OPCODE_READ_CONFIG: + return process_read_config(); + case OPCODE_READ_STATUS: + return process_read_status(); + } + + return DISCARD; +} + +static int16_t try_process_write_request() +{ + if (msg_len < 2) + { + return KEEP; + } + + switch (msgbuf[1]) + { + case OPCODE_WRITE_EVTLOG_ENABLE: + return process_write_evtlog_enable(); + case OPCODE_WRITE_CONFIG: + return process_write_config(); + case OPCODE_WRITE_RESET_CONFIG: + return process_write_reset_config(); + case OPCODE_WRITE_ADC_VOLTAGE_CALIBRATION: + return process_write_adc_voltage_calibration(); + } + + return DISCARD; +} + +static int16_t try_process_bafang_read_request() +{ + if (msg_len < 2) + { + return KEEP; + } + + switch (msgbuf[1]) + { + case OPCODE_BAFANG_DISPLAY_READ_STATUS: + return process_bafang_display_read_status(); + case OPCODE_BAFANG_DISPLAY_READ_CURRENT: + return process_bafang_display_read_current(); + case OPCODE_BAFANG_DISPLAY_READ_BATTERY: + return process_bafang_display_read_battery(); + case OPCODE_BAFANG_DISPLAY_READ_SPEED: + return process_bafang_display_read_speed(); + case OPCODE_BAFANG_DISPLAY_READ_UNKNOWN1: + return process_bafang_display_read_unknown1(); + case OPCODE_BAFANG_DISPLAY_READ_RANGE: + return process_bafang_display_read_range(); + case OPCODE_BAFANG_DISPLAY_READ_CALORIES: + return process_bafang_display_read_calories(); + case OPCODE_BAFANG_DISPLAY_READ_UNKNOWN3: + return process_bafang_display_read_unknown3(); + case OPCODE_BAFANG_DISPLAY_READ_MOVING: + return process_bafang_display_read_moving(); + } + + return DISCARD; +} + +static int16_t try_process_bafang_write_request() +{ + if (msg_len < 2) + { + return KEEP; + } + + switch (msgbuf[1]) + { + case OPCODE_BAFANG_DISPLAY_WRITE_PAS: + return process_bafang_display_write_pas(); + case OPCODE_BAFANG_DISPLAY_WRITE_MODE: + return process_bafang_display_write_mode(); + case OPCODE_BAFANG_DISPLAY_WRITE_LIGHTS: + return process_bafang_display_write_lights(); + case OPCODE_BAFANG_DISPLAY_WRITE_SPEED_LIM: + return process_bafang_display_write_speed_limit(); + } + + return DISCARD; +} + +static int16_t process_read_fw_version() +{ + if (msg_len < 3) + { + return KEEP; + } + + if (compute_checksum(msgbuf, 2) == msgbuf[2]) + { + uint8_t checksum = 0; + write_uart_and_increment_checksum(REQUEST_TYPE_READ, &checksum); + write_uart_and_increment_checksum(OPCODE_READ_FW_VERSION, &checksum); + write_uart_and_increment_checksum(VERSION_MAJOR, &checksum); + write_uart_and_increment_checksum(VERSION_MINOR, &checksum); + write_uart_and_increment_checksum(VERSION_PATCH, &checksum); + write_uart_and_increment_checksum(CONFIG_VERSION, &checksum); + write_uart_and_increment_checksum(CTRL_TYPE, &checksum); + uart_write(checksum); + } + else + { + eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); + return DISCARD; + } + + return 3; +} + +static int16_t process_read_evtlog_enable() +{ + if (msg_len < 3) + { + return KEEP; + } + + if (compute_checksum(msgbuf, 2) == msgbuf[2]) + { + uint8_t checksum = 0; + write_uart_and_increment_checksum(REQUEST_TYPE_READ, &checksum); + write_uart_and_increment_checksum(OPCODE_READ_EVTLOG_ENABLE, &checksum); + write_uart_and_increment_checksum((uint8_t)eventlog_is_enabled(), &checksum); + uart_write(checksum); + } + else + { + eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); + return DISCARD; + } + + return 3; +} + +static int16_t process_read_config() +{ + if (msg_len < 3) + { + return KEEP; + } + + if (compute_checksum(msgbuf, 2) == msgbuf[2]) + { + uint8_t checksum = 0; + write_uart_and_increment_checksum(REQUEST_TYPE_READ, &checksum); + write_uart_and_increment_checksum(OPCODE_READ_CONFIG, &checksum); + write_uart_and_increment_checksum(CONFIG_VERSION, &checksum); + write_uart_and_increment_checksum(sizeof(config_t), &checksum); + + uint8_t *cfg = (uint8_t *)&g_config; + for (uint8_t i = 0; i < sizeof(config_t); ++i) + { + write_uart_and_increment_checksum(*(cfg + i), &checksum); + } + + uart_write(checksum); + } + else + { + eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); + return DISCARD; + } + + return 3; +} + +static int16_t process_read_status() +{ + // :TODO: + return 0; +} + +static int16_t process_write_evtlog_enable() +{ + if (msg_len < 4) + { + return KEEP; + } + + if (compute_checksum(msgbuf, 3) == msgbuf[3]) + { + eventlog_set_enabled((bool)msgbuf[2]); + + uint8_t checksum = 0; + write_uart_and_increment_checksum(REQUEST_TYPE_WRITE, &checksum); + write_uart_and_increment_checksum(OPCODE_WRITE_EVTLOG_ENABLE, &checksum); + write_uart_and_increment_checksum(msgbuf[2], &checksum); + uart_write(checksum); + } + else + { + eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); + return DISCARD; + } + + return 4; +} + +static int16_t process_write_config() +{ + if (msg_len < 4) + { + return KEEP; + } + + uint8_t version = msgbuf[2]; + uint8_t length = msgbuf[3]; + + if (msg_len < 4 + length + 1) + { + return KEEP; + } + + if (compute_checksum(msgbuf, (uint8_t)(4 + sizeof(config_t))) == msgbuf[4 + sizeof(config_t)]) + { + bool result = false; + if (version == CONFIG_VERSION && length == sizeof(config_t)) + { + memcpy(&g_config, msgbuf + 4, sizeof(config_t)); + result = cfgstore_save_config(); + } + + uint8_t checksum = 0; + write_uart_and_increment_checksum(REQUEST_TYPE_WRITE, &checksum); + write_uart_and_increment_checksum(OPCODE_WRITE_CONFIG, &checksum); + write_uart_and_increment_checksum(result, &checksum); + uart_write(checksum); + } + else + { + eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); + return DISCARD; + } + + return 4 + length + 1; +} + +static int16_t process_write_reset_config() +{ + if (msg_len < 3) + { + return KEEP; + } + + if (compute_checksum(msgbuf, 2) == msgbuf[2]) + { + + bool res = cfgstore_reset_config(); + + uint8_t checksum = 0; + write_uart_and_increment_checksum(REQUEST_TYPE_WRITE, &checksum); + write_uart_and_increment_checksum(OPCODE_WRITE_RESET_CONFIG, &checksum); + write_uart_and_increment_checksum((uint8_t)res, &checksum); + uart_write(checksum); + } + else + { + eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); + return DISCARD; + } + + return 3; +} + +static int16_t process_write_adc_voltage_calibration() +{ + if (msg_len < 5) + { + return KEEP; + } + + if (compute_checksum(msgbuf, 4) == msgbuf[4]) + { + uint16_t actual_volt_x100 = ((uint16_t)msgbuf[2] << 8) | msgbuf[3]; + + int16_t calibration_offset = motor_calibrate_battery_voltage(actual_volt_x100); + g_pstate.adc_voltage_calibration_steps_x100_i16l = (uint8_t)(calibration_offset); + g_pstate.adc_voltage_calibration_steps_x100_i16h = (uint8_t)(calibration_offset >> 8); + + cfgstore_save_pstate(); + + uint8_t checksum = 0; + write_uart_and_increment_checksum(REQUEST_TYPE_WRITE, &checksum); + write_uart_and_increment_checksum(OPCODE_WRITE_ADC_VOLTAGE_CALIBRATION, &checksum); + write_uart_and_increment_checksum(msgbuf[2], &checksum); + write_uart_and_increment_checksum(msgbuf[3], &checksum); + uart_write(checksum); + } + else + { + eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); + return DISCARD; + } + + return 5; +} + +static int16_t process_bafang_display_read_status() +{ + if (msg_len < 2) + { + return KEEP; + } + + uart_write(app_get_status_code()); + + return 2; +} + +static int16_t process_bafang_display_read_current() +{ + if (msg_len < 2) + { + return KEEP; + } + + uint8_t amp_x2 = (uint8_t)((motor_get_battery_current_x10() * 2) / 10); + + uart_write(amp_x2); + uart_write(amp_x2); // checksum + + return 2; +} + +static int16_t process_bafang_display_read_battery() +{ + if (msg_len < 2) + { + return KEEP; + } + + uint8_t value = battery_get_mapped_percent(); + + uart_write(value); + uart_write(value); // checksum + + return 2; +} + +static int16_t process_bafang_display_read_speed() +{ + if (msg_len < 2) + { + return KEEP; + } + + uint16_t speed = 0; + + if (g_config.walk_mode_data_display != WALK_MODE_DATA_SPEED && app_get_assist_level() == ASSIST_PUSH) + { + uint16_t data = 0; + + switch (g_config.walk_mode_data_display) + { + case WALK_MODE_DATA_TEMPERATURE: + // Keep temperature in C, farenheit would be out of range + data = app_get_temperature(); + break; + case WALK_MODE_DATA_REQUESTED_POWER: + data = motor_get_target_current(); + break; + case WALK_MODE_DATA_BATTERY_PERCENT: + data = battery_get_percent(); + break; + } + + if (g_config.use_freedom_units) + { + // Compensate for kph -> mph conversion display will do. + data = (data * 161) / 100; + } + + // T_kph -> rpm + speed = (uint16_t)(25000.f / + (3 * 3.14159f * 1.27f * + EXPAND_U16(g_config.wheel_size_inch_x10_u16h, g_config.wheel_size_inch_x10_u16l)) * + data); + } + else + { + speed = speed_sensor_get_rpm_x10() / 10; + } + + uint8_t checksum = 0; + + write_uart_and_increment_checksum(speed >> 8, &checksum); + write_uart_and_increment_checksum((uint8_t)speed, &checksum); + uart_write(checksum + (uint8_t)0x20); // weird checksum + + return 2; +} + +static int16_t process_bafang_display_read_unknown1() +{ + if (msg_len < 3) + { + return KEEP; + } + + uart_write(0x00); + uart_write(0x00); + uart_write(0x00); // checksum + + return 3; +} + +static int16_t process_bafang_display_read_range() +{ + if (msg_len < 3) + { + return KEEP; + } + + uint16_t value = 0; + +#if DISPLAY_RANGE_FIELD_DATA == DISPLAY_RANGE_FIELD_TEMPERATURE + value = app_get_temperature(); + if (g_config.use_freedom_units) + { + // Convert to farenheit and compensate for the km -> miles conversion the diplay will do + // F_miles = (C * 9/5 + 32) * 161 / 100 + // Approximistation: + // F_miles = 2.9C + 50.5 + + value = ((290u * value) + 5050u) / 100u; + } +#elif DISPLAY_RANGE_FIELD_DATA == DISPLAY_RANGE_FIELD_POWER + if (app_get_lights()) + { + value = motor_get_battery_current_x10(); + } + else + { + uint16_t max_current_amp_x10 = g_config.max_current_amps * 10; + value = MAP32(motor_get_target_current(), 0, 100, 0, max_current_amp_x10); + } + + if (g_config.use_freedom_units) + { + // compensate for km -> miles conversion the display will do + value = (value * 161u) / 100u; + } +#endif + + uint8_t checksum = 0; + + write_uart_and_increment_checksum((uint8_t)(value >> 8), &checksum); + write_uart_and_increment_checksum((uint8_t)value, &checksum); + uart_write(checksum); // checksum + + return 3; +} + +static int16_t process_bafang_display_read_calories() +{ + if (msg_len < 3) + { + return KEEP; + } + + uint8_t checksum = 0; + + // send battery voltage x10 to show in calories field + uint16_t volt = motor_get_battery_voltage_x10(); + + write_uart_and_increment_checksum(volt >> 8, &checksum); + write_uart_and_increment_checksum(volt & 0xff, &checksum); + uart_write(checksum); // checksum + + return 3; +} + +static int16_t process_bafang_display_read_unknown3() +{ + if (msg_len < 3) + { + return KEEP; + } + + uart_write(0x00); + uart_write(0x00); + uart_write(0x00); + uart_write(0x00); + uart_write(0x00); // checksum + + return 3; +} + +static int16_t process_bafang_display_read_moving() +{ + if (msg_len < 2) + { + return KEEP; + } + + uint8_t data = speed_sensor_is_moving() ? 0x31 : 0x30; + uart_write(data); + uart_write(data); // checksum + + return 2; +} + +static int16_t process_bafang_display_write_pas() +{ + if (msg_len < 4) + { + return KEEP; + } + + if (compute_checksum(msgbuf, 3) == msgbuf[3]) + { + switch (msgbuf[2]) + { + case 0x00: + app_set_assist_level(ASSIST_0); + break; + case 0x01: + app_set_assist_level(ASSIST_1); + break; + case 0x0b: + app_set_assist_level(ASSIST_2); + break; + case 0x0c: + app_set_assist_level(ASSIST_3); + break; + case 0x0d: + app_set_assist_level(ASSIST_4); + break; + case 0x02: + app_set_assist_level(ASSIST_5); + break; + case 0x15: + app_set_assist_level(ASSIST_6); + break; + case 0x16: + app_set_assist_level(ASSIST_7); + break; + case 0x17: + app_set_assist_level(ASSIST_8); + break; + case 0x03: + app_set_assist_level(ASSIST_9); + break; + case 0x06: + app_set_assist_level(ASSIST_PUSH); + break; + default: + // Unsupported level, ignore + break; + } + } + else + { + eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); + return DISCARD; + } + + return 4; +} + +static int16_t process_bafang_display_write_mode() +{ + if (msg_len < 4) + { + return KEEP; + } + + if (compute_checksum(msgbuf, 3) == msgbuf[3]) + { + switch (msgbuf[2]) + { + case 0x02: + app_set_operation_mode(OPERATION_MODE_DEFAULT); + break; + case 0x04: + app_set_operation_mode(OPERATION_MODE_SPORT); + break; + default: + // Unsupported mode, ignore + break; + } + } + else + { + eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); + return DISCARD; + } + + return 4; +} + +static int16_t process_bafang_display_write_lights() +{ + if (msg_len < 3) + { + return KEEP; + } + + // No checksum + + switch (msgbuf[2]) + { + case 0xf0: + app_set_lights(false); + break; + case 0xf1: + app_set_lights(true); + break; + default: + return DISCARD; // unsupported state, assume communication error + } + + return 3; +} + +static int16_t process_bafang_display_write_speed_limit() +{ + if (msg_len < 5) + { + return KEEP; + } + + /* + if (compute_checksum(msgbuf, 4) == msgbuf[4]) + { + // Ignoring speed limit requested by display, + // Global speed limit is configured in firmware config tool. + + uint16_t value = ((msgbuf[2] << 8) | msgbuf[3]); + app_set_wheel_max_speed_rpm(value); + } + else + { + eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); + return DISCARD; + } + */ + + return 5; +} diff --git a/src/firmware/extcom.h b/code/firmware/src/extcom.h similarity index 79% rename from src/firmware/extcom.h rename to code/firmware/src/extcom.h index ad51279b..291db9f0 100644 --- a/src/firmware/extcom.h +++ b/code/firmware/src/extcom.h @@ -1,7 +1,7 @@ /* * bbs-fw * - * Copyright (C) Daniel Nilsson, 2022. + * Copyright (C) Daniel Nilsson, 2024. * * Released under the GPL License, Version 3 */ @@ -13,4 +13,3 @@ void extcom_init(); void extcom_process(); #endif - diff --git a/src/firmware/fwconfig.h b/code/firmware/src/fwconfig.h similarity index 51% rename from src/firmware/fwconfig.h rename to code/firmware/src/fwconfig.h index 45821ff5..37d23e43 100644 --- a/src/firmware/fwconfig.h +++ b/code/firmware/src/fwconfig.h @@ -1,172 +1,161 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ -#ifndef _FWCONFIG_H_ -#define _FWCONFIG_H_ - -#if defined(BBSHD) - #define HAS_MOTOR_TEMP_SENSOR 1 -#else - #define HAS_MOTOR_TEMP_SENSOR 0 -#endif - - -#if defined(BBSHD) || defined(BBS02) - #define HAS_CONTROLLER_TEMP_SENSOR 1 -#else - #define HAS_CONTROLLER_TEMP_SENSOR 0 -#endif - - -#if defined(TSDZ2) - #define HAS_TORQUE_SENSOR 1 -#else - #define HAS_TORQUE_SENSOR 0 -#endif - -#if defined(BBSHD) || defined(BBS02) - #define HAS_SHIFT_SENSOR_SUPPORT 1 -#else - #define HAS_SHIFT_SENSOR_SUPPORT 0 -#endif - -#if defined(BBS02) - #define MAX_CADENCE_RPM_X10 1500 -#elif defined(BBSHD) - // Measured on BBSHD at 48V - #define MAX_CADENCE_RPM_X10 1680 -#else - #define MAX_CADENCE_RPM_X10 1200 -#endif - -#if defined(BBS02) || defined(BBSHD) - #define PAS_PULSES_REVOLUTION 24 -#elif defined(TSDZ2) - #define PAS_PULSES_REVOLUTION 20 -#endif - - // Applied to both motor and controller tmeperature sensor -#define MAX_TEMPERATURE 85 - -// Current ramp down starts at MAX_TEMPERATURE - 5. -#define MAX_TEMPERATURE_RAMP_DOWN_INTERVAL 5 - -// Maximum allowed motor current in percent of maximum configured current (A) -// to still apply when maximum temperature has been reached. -// Motor current is ramped down linearly until this value when approaching -// max temperature. -#define MAX_TEMPERATURE_LOW_CURRENT_PERCENT 20 - -// No battery percent mapping -#define BATTERY_PERCENT_MAP_NONE 0 -// Map battery percent to provide a linear relationship on the -// 5-bar battery indicator of the SW102 display. -#define BATTERY_PERCENT_MAP_SW102 1 - -// Select battery percent mapping -#define BATTERY_PERCENT_MAP BATTERY_PERCENT_MAP_NONE - -// Time with no motor load until battery voltage is updated to avoid voltage sag. -#define BATTERY_NO_LOAD_DELAY_MS 2000 - -// Padding values for voltage range of battery. -#define BATTERY_FULL_OFFSET_PERCENT 8 -#define BATTERY_EMPTY_OFFSET_PERCENT 8 - -// Battery SOC percentage when current ramp down starts. -#define LVC_RAMP_DOWN_OFFSET_PERCENT 10 - -// Maximum allowed motor current in percent of maximum configured current (A) -// to still apply when 0% battery has been reached. -// Motor current is ramped down linearly until this value when approaching "empty". -#define LVC_LOW_CURRENT_PERCENT 20 - -// Size of speed limit ramp down interval. -// If max speed is 50 and this is set to 3 then the -// target current will start ramping down when passing 47 -// and be at 50% of assist target current when reaching 50. -#define SPEED_LIMIT_RAMP_DOWN_INTERVAL_KPH 3 - -// Current ramp down (e.g. when releasing throttle, stop pedaling etc.) in percent per 10 millisecond. -// Specifying 1 will make ramp down periond 1 second if releasing from full throttle. -// Set to 100 to disable -#define CURRENT_RAMP_DOWN_PERCENT_10MS 5 - -// Target speed in km/h when walk mode is engaged -#define WALK_MODE_SPEED_KPH 4 - - -#define THROTTLE_RESPONSE_LINEAR 1 -#define THROTTLE_RESPONSE_QUADRATIC 2 -#define THROTTLE_RESPONSE_CUSTOM 3 - -#define THROTTLE_RESPONSE_CURVE THROTTLE_RESPONSE_CUSTOM - -// Custom throttle map -// y = pow(x / 100.0, 1.5) * 100.0 -#define THROTTLE_CUSTOM_MAP \ - 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, \ - 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, \ - 9, 10, 10, 11, 12, 12, 13, 14, 15, 16, \ - 16, 17, 18, 19, 20, 21, 22, 23, 23, 24, \ - 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, \ - 35, 36, 37, 39, 40, 41, 42, 43, 44, 45, \ - 46, 48, 49, 50, 51, 52, 54, 55, 56, 57, \ - 59, 60, 61, 62, 64, 65, 66, 68, 69, 70, \ - 72, 73, 74, 76, 77, 78, 80, 81, 83, 84, \ - 85, 87, 88, 90, 91, 93, 94, 96, 97, 99, \ - 100 - - -// This value is used when assist level is configured with throttle cadence -// override flag in config tool. Default is 100%. -#define THROTTLE_CADENCE_OVERRIDE_PERCENT 100 - -// Lower limit for cadence rpm in power calculation -// for torque pas assist. When cadence is below this -// limit you will get extra power. -// -// power_w = torque_Nm * cadence_rpm * 0.105 -// -// The calculated power is then multipled by a factor -// set by the assist level to get the final power which -// the motor will contribute. -// -// The value configured below is the minimum value for -// cadence_rpm to be used in the formula above. If the -// actual cadence is lower it will be overriden by this -// configured value. -#define TORQUE_POWER_LOWER_RPM_X10 300 - -// Number of PAS sensor pulses to engage cruise mode, -// there are 24 pulses per revolution. -#define CRUISE_ENGAGE_PAS_PULSES PAS_PULSES_REVOLUTION / 2 - -// Number of PAS sensor pulses to disengage curise mode -// by pedaling backwards. -#define CRUISE_DISENGAGE_PAS_PULSES PAS_PULSES_REVOLUTION / 2 - - -// Option to control what data is displayed in "Range" field on display -// since range calculation is not implemented. -#define DISPLAY_RANGE_FIELD_ZERO 0 -#define DISPLAY_RANGE_FIELD_TEMPERATURE 1 // max temperature of controller / motor -#define DISPLAY_RANGE_FIELD_POWER 2 // requested current x10 (lights off) / actual current x10 (lights on) - -// uncomment and select option above -// #define DISPLAY_RANGE_FIELD_DATA DISPLAY_RANGE_FIELD_ZERO - -// default to temperature if temperature sensors available (BBS2/BBSHD), else power (TSDZ2) -#ifndef DISPLAY_RANGE_FIELD_DATA - #if HAS_CONTROLLER_TEMP_SENSOR || HAS_MOTOR_TEMP_SENSOR - #define DISPLAY_RANGE_FIELD_DATA DISPLAY_RANGE_FIELD_TEMPERATURE - #else - #define DISPLAY_RANGE_FIELD_DATA DISPLAY_RANGE_FIELD_POWER - #endif -#endif - -#endif +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#ifndef _FWCONFIG_H_ +#define _FWCONFIG_H_ + +#if defined(BBSHD) +#define HAS_MOTOR_TEMP_SENSOR 1 +#else +#define HAS_MOTOR_TEMP_SENSOR 0 +#endif + +#if defined(BBSHD) || defined(BBS02) +#define HAS_CONTROLLER_TEMP_SENSOR 1 +#else +#define HAS_CONTROLLER_TEMP_SENSOR 0 +#endif + +#if defined(TSDZ2) +#define HAS_TORQUE_SENSOR 1 +#else +#define HAS_TORQUE_SENSOR 0 +#endif + +#if defined(BBSHD) || defined(BBS02) +#define HAS_SHIFT_SENSOR_SUPPORT 1 +#else +#define HAS_SHIFT_SENSOR_SUPPORT 0 +#endif + +#if defined(BBS02) +#define MAX_CADENCE_RPM_X10 1500 +#elif defined(BBSHD) +// Measured on BBSHD at 48V +#define MAX_CADENCE_RPM_X10 1680 +#else +#define MAX_CADENCE_RPM_X10 1200 +#endif + +#if defined(BBS02) || defined(BBSHD) +#define PAS_PULSES_REVOLUTION 24 +#elif defined(TSDZ2) +#define PAS_PULSES_REVOLUTION 20 +#endif + +// Applied to both motor and controller tmeperature sensor +#define MAX_TEMPERATURE 85 + +// Current ramp down starts at MAX_TEMPERATURE - 5. +#define MAX_TEMPERATURE_RAMP_DOWN_INTERVAL 5 + +// Maximum allowed motor current in percent of maximum configured current (A) +// to still apply when maximum temperature has been reached. +// Motor current is ramped down linearly until this value when approaching +// max temperature. +#define MAX_TEMPERATURE_LOW_CURRENT_PERCENT 20 + +// No battery percent mapping +#define BATTERY_PERCENT_MAP_NONE 0 +// Map battery percent to provide a linear relationship on the +// 5-bar battery indicator of the SW102 display. +#define BATTERY_PERCENT_MAP_SW102 1 + +// Select battery percent mapping +#define BATTERY_PERCENT_MAP BATTERY_PERCENT_MAP_NONE + +// Time with no motor load until battery voltage is updated to avoid voltage sag. +#define BATTERY_NO_LOAD_DELAY_MS 2000 + +// Padding values for voltage range of battery. +#define BATTERY_FULL_OFFSET_PERCENT 8 +#define BATTERY_EMPTY_OFFSET_PERCENT 8 + +// Battery SOC percentage when current ramp down starts. +#define LVC_RAMP_DOWN_OFFSET_PERCENT 10 + +// Maximum allowed motor current in percent of maximum configured current (A) +// to still apply when 0% battery has been reached. +// Motor current is ramped down linearly until this value when approaching "empty". +#define LVC_LOW_CURRENT_PERCENT 20 + +// Size of speed limit ramp down interval. +// If max speed is 50 and this is set to 3 then the +// target current will start ramping down when passing 47 +// and be at 50% of assist target current when reaching 50. +#define SPEED_LIMIT_RAMP_DOWN_INTERVAL_KPH 3 + +// Current ramp down (e.g. when releasing throttle, stop pedaling etc.) in percent per 10 millisecond. +// Specifying 1 will make ramp down periond 1 second if releasing from full throttle. +// Set to 100 to disable +#define CURRENT_RAMP_DOWN_PERCENT_10MS 5 + +// Target speed in km/h when walk mode is engaged +#define WALK_MODE_SPEED_KPH 4 + +#define THROTTLE_RESPONSE_LINEAR 1 +#define THROTTLE_RESPONSE_QUADRATIC 2 +#define THROTTLE_RESPONSE_CUSTOM 3 + +#define THROTTLE_RESPONSE_CURVE THROTTLE_RESPONSE_CUSTOM + +// Custom throttle map +// y = pow(x / 100.0, 1.5) * 100.0 +#define THROTTLE_CUSTOM_MAP \ + 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, 11, 12, 12, 13, 14, 15, 16, 16, 17, 18, 19, \ + 20, 21, 22, 23, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 39, 40, 41, 42, 43, 44, 45, 46, \ + 48, 49, 50, 51, 52, 54, 55, 56, 57, 59, 60, 61, 62, 64, 65, 66, 68, 69, 70, 72, 73, 74, 76, 77, 78, 80, 81, \ + 83, 84, 85, 87, 88, 90, 91, 93, 94, 96, 97, 99, 100 + +// This value is used when assist level is configured with throttle cadence +// override flag in config tool. Default is 100%. +#define THROTTLE_CADENCE_OVERRIDE_PERCENT 100 + +// Lower limit for cadence rpm in power calculation +// for torque pas assist. When cadence is below this +// limit you will get extra power. +// +// power_w = torque_Nm * cadence_rpm * 0.105 +// +// The calculated power is then multipled by a factor +// set by the assist level to get the final power which +// the motor will contribute. +// +// The value configured below is the minimum value for +// cadence_rpm to be used in the formula above. If the +// actual cadence is lower it will be overriden by this +// configured value. +#define TORQUE_POWER_LOWER_RPM_X10 300 + +// Number of PAS sensor pulses to engage cruise mode, +// there are 24 pulses per revolution. +#define CRUISE_ENGAGE_PAS_PULSES PAS_PULSES_REVOLUTION / 2 + +// Number of PAS sensor pulses to disengage curise mode +// by pedaling backwards. +#define CRUISE_DISENGAGE_PAS_PULSES PAS_PULSES_REVOLUTION / 2 + +// Option to control what data is displayed in "Range" field on display +// since range calculation is not implemented. +#define DISPLAY_RANGE_FIELD_ZERO 0 +#define DISPLAY_RANGE_FIELD_TEMPERATURE 1 // max temperature of controller / motor +#define DISPLAY_RANGE_FIELD_POWER 2 // requested current x10 (lights off) / actual current x10 (lights on) + +// uncomment and select option above +// #define DISPLAY_RANGE_FIELD_DATA DISPLAY_RANGE_FIELD_ZERO + +// default to temperature if temperature sensors available (BBS2/BBSHD), else power (TSDZ2) +#ifndef DISPLAY_RANGE_FIELD_DATA +#if HAS_CONTROLLER_TEMP_SENSOR || HAS_MOTOR_TEMP_SENSOR +#define DISPLAY_RANGE_FIELD_DATA DISPLAY_RANGE_FIELD_TEMPERATURE +#else +#define DISPLAY_RANGE_FIELD_DATA DISPLAY_RANGE_FIELD_POWER +#endif +#endif + +#endif diff --git a/src/firmware/interrupt.h b/code/firmware/src/interrupt.h similarity index 82% rename from src/firmware/interrupt.h rename to code/firmware/src/interrupt.h index 33fc9a0e..a94c46df 100644 --- a/src/firmware/interrupt.h +++ b/code/firmware/src/interrupt.h @@ -1,12 +1,12 @@ /* * bbs-fw * - * Copyright (C) Daniel Nilsson, 2022. + * Copyright (C) Daniel Nilsson, 2024. * * Released under the GPL License, Version 3 */ -#ifndef _INTERRUPT_H_ +#ifndef _INTERRUPT_H_ #define _INTERRUPT_H_ // Interrupt rouines declarations required to be included from main.c diff --git a/src/firmware/lights.h b/code/firmware/src/lights.h similarity index 74% rename from src/firmware/lights.h rename to code/firmware/src/lights.h index 3b9d22a0..98d3a8d2 100644 --- a/src/firmware/lights.h +++ b/code/firmware/src/lights.h @@ -1,23 +1,22 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#ifndef _LIGHTS_H_ -#define _LIGHTS_H_ - -#include "intellisense.h" -#include -#include - -void lights_init(); - -void lights_enable(); -void lights_disable(); - -void lights_set(bool on); - -#endif +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#ifndef _LIGHTS_H_ +#define _LIGHTS_H_ + +#include +#include + +void lights_init(); + +void lights_enable(); +void lights_disable(); + +void lights_set(bool on); + +#endif diff --git a/code/firmware/src/main.c b/code/firmware/src/main.c new file mode 100644 index 00000000..c9f5db52 --- /dev/null +++ b/code/firmware/src/main.c @@ -0,0 +1,90 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "adc.h" +#include "app.h" +#include "battery.h" +#include "cfgstore.h" +#include "eeprom.h" +#include "eventlog.h" +#include "extcom.h" +#include "interrupt.h" // IMPORTANT: interrupt vector declarations must be included from main.c! +#include "lights.h" +#include "motor.h" +#include "sensors.h" +#include "system.h" +#include "throttle.h" +#include "timers.h" +#include "uart.h" +#include "util.h" +#include "watchdog.h" + +#define APP_PROCESS_INTERVAL_MS 5 + +void main(void) +{ + motor_pre_init(); + + watchdog_init(); + timers_init(); + system_init(); + + eventlog_init(false); + extcom_init(); + + if (watchdog_triggered()) + { + // force write watchdog reset to eventlog + bool prev = eventlog_is_enabled(); + eventlog_set_enabled(true); + eventlog_write(EVT_ERROR_WATCHDOG_TRIGGERED); + eventlog_set_enabled(prev); + } + + eeprom_init(); + cfgstore_init(); + + adc_init(); + sensors_init(); + + speed_sensor_set_signals_per_rpm(g_config.speed_sensor_signals); + pas_set_stop_delay((uint16_t)g_config.pas_stop_delay_x100s * 10); + + battery_init(); + throttle_init(EXPAND_U16(g_config.throttle_start_voltage_mv_u16h, g_config.throttle_start_voltage_mv_u16l), + EXPAND_U16(g_config.throttle_end_voltage_mv_u16h, g_config.throttle_end_voltage_mv_u16l)); + + motor_init( + g_config.max_current_amps * 1000, g_config.low_cut_off_v, + EXPAND_I16(g_pstate.adc_voltage_calibration_steps_x100_i16h, g_pstate.adc_voltage_calibration_steps_x100_i16l)); + + lights_init(); + + app_init(); + + uint32_t next_app_proccess = system_ms(); + while (1) + { + uint32_t now = system_ms(); + + adc_process(); + motor_process(); + + if (now >= next_app_proccess) + { + next_app_proccess = now + APP_PROCESS_INTERVAL_MS; + + battery_process(); + sensors_process(); + extcom_process(); + app_process(); + } + + watchdog_yeild(); + } +} diff --git a/src/firmware/motor.h b/code/firmware/src/motor.h similarity index 77% rename from src/firmware/motor.h rename to code/firmware/src/motor.h index caee25d8..27a2ed55 100644 --- a/src/firmware/motor.h +++ b/code/firmware/src/motor.h @@ -1,7 +1,7 @@ /* * bbs-fw * - * Copyright (C) Daniel Nilsson, 2022. + * Copyright (C) Daniel Nilsson, 2024. * * Released under the GPL License, Version 3 */ @@ -11,10 +11,10 @@ #include -#define MOTOR_ERROR_LVC 0x0800 -#define MOTOR_ERROR_HALL_SENSOR 0x2000 -#define MOTOR_ERROR_CURRENT_SENSE 0x0004 -#define MOTOR_ERROR_POWER_RESET 0x0020 +#define MOTOR_ERROR_LVC 0x0800 +#define MOTOR_ERROR_HALL_SENSOR 0x2000 +#define MOTOR_ERROR_CURRENT_SENSE 0x0004 +#define MOTOR_ERROR_POWER_RESET 0x0020 void motor_pre_init(); void motor_init(uint16_t max_current_mA, uint8_t lvc_V, int16_t adc_calib_volt_step_offset); diff --git a/src/firmware/sensors.h b/code/firmware/src/sensors.h similarity index 91% rename from src/firmware/sensors.h rename to code/firmware/src/sensors.h index 1fa4ae98..f3162940 100644 --- a/src/firmware/sensors.h +++ b/code/firmware/src/sensors.h @@ -1,7 +1,7 @@ /* * bbs-fw * - * Copyright (C) Daniel Nilsson, 2022. + * Copyright (C) Daniel Nilsson, 2024. * * Released under the GPL License, Version 3 */ @@ -9,10 +9,8 @@ #ifndef _SENSORS_H_ #define _SENSORS_H_ -#include "intellisense.h" - -#include #include +#include void sensors_init(); void sensors_process(); diff --git a/src/firmware/system.h b/code/firmware/src/system.h similarity index 89% rename from src/firmware/system.h rename to code/firmware/src/system.h index 4a48cb4c..740eeee8 100644 --- a/src/firmware/system.h +++ b/code/firmware/src/system.h @@ -1,7 +1,7 @@ /* * bbs-fw * - * Copyright (C) Daniel Nilsson, 2022. + * Copyright (C) Daniel Nilsson, 2024. * * Released under the GPL License, Version 3 */ @@ -25,4 +25,3 @@ uint32_t system_ms(); void system_delay_ms(uint16_t ms); #endif - diff --git a/code/firmware/src/throttle.c b/code/firmware/src/throttle.c new file mode 100644 index 00000000..08eaae6d --- /dev/null +++ b/code/firmware/src/throttle.c @@ -0,0 +1,144 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "throttle.h" +#include "adc.h" +#include "eventlog.h" +#include "fwconfig.h" +#include "system.h" +#include "util.h" + +#include + +static uint8_t min_voltage_adc; +static uint8_t max_voltage_adc; + +static bool throttle_detected; +static bool throttle_low_ok; +static bool throttle_hard_ok; +static uint32_t throttle_hard_limit_hit_at; + +// #define LOG_THROTTLE_ADC + +#define ADC_VOLTAGE_MV 5000ul + +#define THROTTLE_HARD_LOW_LIMIT_MV 500ul +#define THROTTLE_HARD_HIGH_LIMIT_MV 4500ul + +#define THROTTLE_HARD_LOW_LIMIT_ADC ((THROTTLE_HARD_LOW_LIMIT_MV * 256) / ADC_VOLTAGE_MV) +#define THROTTLE_HARD_HIGH_LIMIT_ADC ((THROTTLE_HARD_HIGH_LIMIT_MV * 256) / ADC_VOLTAGE_MV) +#define THROTTLE_HARD_LIMIT_TOLERANCE_MS 100 + +#if (THROTTLE_RESPONSE_CURVE == THROTTLE_RESPONSE_CUSTOM) +static const uint8_t throttle_custom_map_lut[101] = {THROTTLE_CUSTOM_MAP}; +#endif + +void throttle_init(uint16_t min_mv, uint16_t max_mv) +{ + min_voltage_adc = (uint8_t)(((uint32_t)min_mv * 256) / ADC_VOLTAGE_MV); + max_voltage_adc = (uint8_t)(((uint32_t)max_mv * 256) / ADC_VOLTAGE_MV); + throttle_detected = false; + throttle_low_ok = false; + throttle_hard_ok = true; + throttle_hard_limit_hit_at = 0; +} + +bool throttle_ok() +{ + return !throttle_detected || (throttle_low_ok && throttle_hard_ok); +} + +uint8_t throttle_read() +{ + static uint8_t throttle_percent = 0; + + int16_t value = adc_get_throttle(); + +#ifdef LOG_THROTTLE_ADC + static uint8_t last_logged_throttle_adc = 0; + if (ABS(value - last_logged_throttle_adc) > 1) + { + last_logged_throttle_adc = value; + eventlog_write_data(EVT_DATA_THROTTLE_ADC, value); + } +#endif + + if (value < THROTTLE_HARD_LOW_LIMIT_ADC || value > THROTTLE_HARD_HIGH_LIMIT_ADC) + { + // allow invalid throttle input value for a number of milliseconds before reporting throttle error. + if (throttle_hard_limit_hit_at != 0) + { + if (throttle_hard_ok && (system_ms() - throttle_hard_limit_hit_at) > THROTTLE_HARD_LIMIT_TOLERANCE_MS) + { + if (throttle_detected && value < THROTTLE_HARD_LOW_LIMIT_ADC) + { + eventlog_write(EVT_ERROR_THROTTLE_LOW_LIMIT); + } + else if (value > THROTTLE_HARD_HIGH_LIMIT_ADC) + { + eventlog_write(EVT_ERROR_THROTTLE_HIGH_LIMIT); + } + + throttle_hard_ok = false; + } + } + else + { + throttle_hard_limit_hit_at = system_ms(); + } + } + else + { + if (value >= THROTTLE_HARD_LOW_LIMIT_ADC) + { + throttle_detected = true; + } + + throttle_hard_limit_hit_at = 0; + throttle_hard_ok = true; + } + + if (value < min_voltage_adc) + { + // throttle is considered not working until it has given a signal below minimum + // configured value but more than 0. + throttle_low_ok = true; + + // hysteresis + if (throttle_percent > 0) + { + value += 1; + } + + if (value < min_voltage_adc) + { + throttle_percent = 0; + return throttle_percent; + } + } + + if (value > max_voltage_adc) + { + value = max_voltage_adc; + } + + throttle_percent = (uint8_t)MAP16(value, min_voltage_adc, max_voltage_adc, 1, 100); + + return throttle_percent; +} + +uint8_t throttle_map_response(uint8_t throttle_percent) +{ +#if (THROTTLE_RESPONSE_CURVE == THROTTLE_RESPONSE_QUADRATIC) + return (uint8_t)(((uint16_t)throttle_percent * throttle_percent) / 100); +#elif (THROTTLE_RESPONSE_CURVE == THROTTLE_RESPONSE_CUSTOM) + return throttle_custom_map_lut[throttle_percent]; +#else + return throttle_percent; +#endif +} diff --git a/src/firmware/throttle.h b/code/firmware/src/throttle.h similarity index 78% rename from src/firmware/throttle.h rename to code/firmware/src/throttle.h index 1d41aad7..16f812da 100644 --- a/src/firmware/throttle.h +++ b/code/firmware/src/throttle.h @@ -1,24 +1,22 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#ifndef _THROTTLE_H_ -#define _THROTTLE_H_ - -#include "intellisense.h" -#include -#include - -void throttle_init(uint16_t min_mv, uint16_t max_mv); - -bool throttle_ok(); -uint8_t throttle_read(); - -uint8_t throttle_map_response(uint8_t throttle_percent); - -#endif - +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#ifndef _THROTTLE_H_ +#define _THROTTLE_H_ + +#include +#include + +void throttle_init(uint16_t min_mv, uint16_t max_mv); + +bool throttle_ok(); +uint8_t throttle_read(); + +uint8_t throttle_map_response(uint8_t throttle_percent); + +#endif diff --git a/src/firmware/timers.h b/code/firmware/src/timers.h similarity index 77% rename from src/firmware/timers.h rename to code/firmware/src/timers.h index 92de7157..98787cee 100644 --- a/src/firmware/timers.h +++ b/code/firmware/src/timers.h @@ -1,7 +1,7 @@ /* * bbs-fw * - * Copyright (C) Daniel Nilsson, 2022. + * Copyright (C) Daniel Nilsson, 2024. * * Released under the GPL License, Version 3 */ diff --git a/code/firmware/src/tsdz2/adc.c b/code/firmware/src/tsdz2/adc.c new file mode 100644 index 00000000..0d2e220d --- /dev/null +++ b/code/firmware/src/tsdz2/adc.c @@ -0,0 +1,121 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include + +#include "adc.h" +#include "tsdz2/interrupt.h" +#include "tsdz2/pins.h" +#include "tsdz2/stm8.h" + +#include +#include + +static volatile uint8_t adc_throttle; +static volatile uint16_t adc_battery_voltage; +static volatile uint16_t adc_torque; + +// cached variables read from voltatile uint16_t vars while ADC1 interrupt disabled +static uint16_t adc_battery_voltage_cache; +static uint16_t adc_torque_cache; + +void adc_init() +{ + SET_PIN_INPUT(PIN_BATTERY_CURRENT); + SET_PIN_INPUT(PIN_BATTERY_VOLTAGE); + SET_PIN_INPUT(PIN_THROTTLE); + SET_PIN_INPUT(PIN_TORQUE_SENSOR); + + // NOTE: + // adc configuration (except ADC1->CR1) is overwritten in motor.c/isr_timer1_cmp + // which triggeres the conversion. + // + // The motor control interrupt routines performs single mode + // adc conversion of battery current, reads the result and + // then starts buffered scan mode conversion of all adc channels + // with end of conversion interrupt enabled which is handled here. + + ADC1->CR1 = ADC1_PRESSEL_FCPU_D2; + ADC1->CR2 = ADC1_ALIGN_LEFT; + + // channel (none) + ADC1->CSR = 0x00; + + // schmittrig disable all + ADC1->TDRL |= (uint8_t)0xFF; + ADC1->TDRH |= (uint8_t)0xFF; + + // Enable the ADC1 peripheral + ADC1->CR1 |= ADC1_CR1_ADON; +} + +void adc_process() +{ + // Have to disable interrupts globally since ADC1->CSR register + // is manipulated from motor control isr. Very short time, should have no effect. + disableInterrupts(); + adc_battery_voltage_cache = adc_battery_voltage; // adc_battery_voltage; + adc_torque_cache = adc_torque; + enableInterrupts(); +} + +uint8_t adc_get_throttle() +{ + // atomic read + return adc_throttle; +} + +uint16_t adc_get_torque() +{ + // 10 bit resolution + return adc_torque_cache; +} + +uint16_t adc_get_temperature_contr() +{ + return 0; +} + +uint16_t adc_get_temperature_motor() +{ + return 0; +} + +uint16_t adc_get_battery_voltage() +{ + return adc_battery_voltage_cache; +} + +void isr_adc1(void) __interrupt(ITC_IRQ_ADC1) +{ + if (ADC1->CSR & ADC1_CSR_EOC) + { + // all adc channels converted, data available in buffers + + // clear EOC and disable EOC interrupt + ADC1->CSR = 0x00; + + // scan mode reads are setup to be left aligned in motor isr + + // update cached values + adc_throttle = ADC1->DB7RH; // only 8bit resolution used + + // must read in high -> low order according to data sheet + uint8_t high, low; + + // read torque + high = ADC1->DB4RH; + low = ADC1->DB4RL; + adc_torque = (uint16_t)high << 2 | low; + + // read battery voltage + high = ADC1->DB6RH; + low = ADC1->DB6RL; + adc_battery_voltage = (uint16_t)high << 2 | low; + } +} diff --git a/src/firmware/tsdz2/cpu.h b/code/firmware/src/tsdz2/cpu.h similarity index 67% rename from src/firmware/tsdz2/cpu.h rename to code/firmware/src/tsdz2/cpu.h index 4bda185d..dd16f889 100644 --- a/src/firmware/tsdz2/cpu.h +++ b/code/firmware/src/tsdz2/cpu.h @@ -1,7 +1,7 @@ /* * bbs-fw * - * Copyright (C) Daniel Nilsson, 2022. + * Copyright (C) Daniel Nilsson, 2024. * * Released under the GPL License, Version 3 */ @@ -10,6 +10,6 @@ #define _TSDZ2_CPU_H_ #define STM8S105 -#define CPU_FREQ 16000000L +#define CPU_FREQ 16000000L #endif diff --git a/code/firmware/src/tsdz2/eeprom.c b/code/firmware/src/tsdz2/eeprom.c new file mode 100644 index 00000000..ff231cd3 --- /dev/null +++ b/code/firmware/src/tsdz2/eeprom.c @@ -0,0 +1,75 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "eeprom.h" +#include "tsdz2/cpu.h" +#include "watchdog.h" +#include +#include + +#define EEPROM_START_ADDRESS 0x4000 + +static uint16_t selected_address; + +void eeprom_init() +{ + selected_address = EEPROM_START_ADDRESS; +} + +bool eeprom_select_page(int page) +{ + if (page >= 0 && page < 2) + { + selected_address = EEPROM_START_ADDRESS + (page * 256); + return true; + } + + return false; +} + +int eeprom_read_byte(int offset) +{ + uint8_t *address = (uint8_t *)(selected_address + offset); + return *address; +} + +bool eeprom_erase_page() +{ + return true; // not needed +} + +bool eeprom_write_byte(int offset, uint8_t value) +{ + uint8_t *address = (uint8_t *)(selected_address + offset); + + // disable flash write protection if enabled + if (!(FLASH->IAPSR & FLASH_IAPSR_DUL)) + { + FLASH->DUKR = FLASH_RASS_KEY2; + FLASH->DUKR = FLASH_RASS_KEY1; + + while (!(FLASH->IAPSR & FLASH_IAPSR_DUL)) + ; + } + + watchdog_yeild(); // :TODO: use faster api to write entire page + + *address = value; + while (!(FLASH->IAPSR & FLASH_IAPSR_EOP)) + ; + + return true; +} + +bool eeprom_end_write() +{ + // enable write protection + FLASH->IAPSR &= ~FLASH_IAPSR_DUL; + + return true; +} diff --git a/code/firmware/src/tsdz2/interrupt.h b/code/firmware/src/tsdz2/interrupt.h new file mode 100644 index 00000000..33d112c2 --- /dev/null +++ b/code/firmware/src/tsdz2/interrupt.h @@ -0,0 +1,24 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#ifndef _TSDZ2_INTERRUPT_H_ +#define _TSDZ2_INTERRUPT_H_ + +#include "tsdz2/cpu.h" +#include + +void isr_timer1_cmp(void) __interrupt(ITC_IRQ_TIM1_CAPCOM); // motor.c +void isr_timer3_ovf(void) __interrupt(ITC_IRQ_TIM3_OVF); // system.c +void isr_timer4_ovf(void) __interrupt(ITC_IRQ_TIM4_OVF); // sensors.c + +void isr_adc1(void) __interrupt(ITC_IRQ_ADC1); // adc.c + +void isr_uart2_rx(void) __interrupt(ITC_IRQ_UART2_RX); // uart.c +void isr_uart2_tx(void) __interrupt(ITC_IRQ_UART2_TX); // uart.c + +#endif diff --git a/code/firmware/src/tsdz2/lights.c b/code/firmware/src/tsdz2/lights.c new file mode 100644 index 00000000..235b5f85 --- /dev/null +++ b/code/firmware/src/tsdz2/lights.c @@ -0,0 +1,47 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "lights.h" +#include "tsdz2/pins.h" +#include "tsdz2/stm8.h" + +static bool lights_enabled; +static bool lights_on; + +void lights_init() +{ + SET_PIN_OUTPUT(PIN_LIGHTS); + + lights_enabled = false; + lights_set(false); +} + +void lights_enable() +{ + lights_enabled = true; + lights_set(lights_on); +} + +void lights_disable() +{ + lights_enabled = false; + lights_set(lights_on); +} + +void lights_set(bool on) +{ + lights_on = on; + if (lights_on && lights_enabled) + { + SET_PIN_HIGH(PIN_LIGHTS); + } + else + { + SET_PIN_LOW(PIN_LIGHTS); + } +} diff --git a/code/firmware/src/tsdz2/motor.c b/code/firmware/src/tsdz2/motor.c new file mode 100644 index 00000000..f4242ff3 --- /dev/null +++ b/code/firmware/src/tsdz2/motor.c @@ -0,0 +1,879 @@ +/* + * TongSheng TSDZ2 motor controller firmware/ + * + * Copyright (C) Casainho, 2018. + * + * Released under the GPL License, Version 3 + * + * - Original motor control code from TongSheng TSDZ2 motor controller firmware. + * - 9bit motor pwm from fork by Frans-Willem. + * - Cleaned up and integrated into bbs-fw by Daniel Nilsson. + */ +#include + +#include "adc.h" +#include "eventlog.h" +#include "motor.h" +#include "system.h" +#include "tsdz2/cpu.h" +#include "tsdz2/pins.h" +#include "tsdz2/stm8.h" +#include "tsdz2/timers.h" +#include "uart.h" +#include "util.h" + +#include +#include +#include +#include +#include + +// Motor +// --------------------------------------------------------------------------------- + +// hard current limits +#define MAX_BATTERY_CURRENT_AMPS_X10 200 +#define MAX_MOTOR_PHASE_CURRENT_AMPS_X10 300 + +// Maximum current ramp +// ---------------------------------------------- +// Every second has 15625 pwm cycles interrupts, +// one ADC battery current step -> 0.156 amps: +// +// A / 0.156 = X (we need to do X steps ramp up per second) +// Therefore : +// 15625 / (A / 0.156) => (15625 * 0.156) / A +// +// 20A/s: (15625 * 0.156) / 20 = 135 +#define CURRENT_RAMP_UP_INVERSE_STEP 135 + +// Choose PWM ramp up/down step (higher value will make the motor acceleration slower) +// +// For a 24V battery, 25 for ramp up seems ok. For an higher voltage battery, this values should be higher +#define PWM_DUTY_CYCLE_RAMP_UP_INVERSE_STEP 24 +#define PWM_DUTY_CYCLE_RAMP_DOWN_INVERSE_STEP 28 + +// This value should be near 0. +// You can try to tune with the whell on the air, full throttle and look at batttery current: adjust for lower battery +// current +#define MOTOR_ROTOR_OFFSET_ANGLE 11 + +// This value is ERPS speed after which a transition happens from sinewave no interpolation to have +// interpolation 60 degrees and must be found experimentally +#define MOTOR_ROTOR_ERPS_START_INTERPOLATION_60_DEGREES 10 + +#define PWM_CYCLES_COUNTER_MAX 3125U // 5 erps minimum speed; 1/5 = 200ms; 200ms/64us = 3125 +#define PWM_CYCLES_SECOND 15625U // 1 / 64us (PWM period) +#define PWM_DUTY_CYCLE_MAX 254 +#define PWM_DUTY_CYCLE_MIN 20 + +#define MOTOR_ROTOR_ANGLE_90 (63 + MOTOR_ROTOR_OFFSET_ANGLE) +#define MOTOR_ROTOR_ANGLE_150 (106 + MOTOR_ROTOR_OFFSET_ANGLE) +#define MOTOR_ROTOR_ANGLE_210 (148 + MOTOR_ROTOR_OFFSET_ANGLE) +#define MOTOR_ROTOR_ANGLE_270 (191 + MOTOR_ROTOR_OFFSET_ANGLE) +#define MOTOR_ROTOR_ANGLE_330 (233 + MOTOR_ROTOR_OFFSET_ANGLE) +#define MOTOR_ROTOR_ANGLE_30 (20 + MOTOR_ROTOR_OFFSET_ANGLE) + +// motor maximum rotation +// 700 is equal to 124 cadence, as TSDZ2 has a reduction ratio of 41.8 +#define MAX_MOTOR_SPEED_ERPS 700 + +// Set how often the motor speed limit controller runs in the isr +#define SPEED_CONTROLLER_CHECK_PERIODS 2000 + +// Set how oftern the current controller runs in the isr +#define CURRENT_CONTROLLER_CHECK_PERIODS 14 + +// adc measurements +// ------------------------------------------ +// 10bit: 0.086V per step +// 0.156A per step +#define ADC_10BIT_VOLTAGE_PER_ADC_STEP_X512 44 +#define ADC_10BIT_CURRENT_PER_ADC_STEP_X512 80 + +#define ADC_10BIT_STEPS_PER_VOLT_X512 5953 + +// filter coefficients +#define BATTERY_CURRENT_FILTER_COEFFICIENT 2 +#define PHASE_CURRENT_FILTER_COEFFICIENT 2 +#define BATTERY_VOLTAGE_FILTER_COEFFICIENT 2 + +#define SVM_TABLE_LEN 256 +#define SVM_TABLE_MIDDLE 127 +#define SIN_TABLE_LEN 60 + +// motor states +#define BLOCK_COMMUTATION 1 +#define SINEWAVE_INTERPOLATION_60_DEGREES 2 + +// index 0-256 to degrees 0-360 +// table is -90 degree preadjusted +static const uint8_t svm_table[SVM_TABLE_LEN] = { + 0, 11, 22, 32, 43, 54, 65, 75, 86, 96, 107, 117, 128, 138, 148, 158, 168, 178, 188, 198, 207, 217, + 222, 225, 228, 230, 233, 235, 238, 240, 242, 244, 245, 247, 248, 250, 251, 252, 252, 253, 253, 254, 254, 254, + 254, 254, 253, 253, 252, 251, 250, 249, 247, 246, 244, 242, 241, 238, 236, 234, 231, 229, 226, 223, 220, 223, + 226, 229, 231, 234, 236, 238, 241, 242, 244, 246, 247, 249, 250, 251, 252, 253, 253, 254, 254, 254, 254, 254, + 253, 253, 252, 252, 251, 250, 248, 247, 245, 244, 242, 240, 238, 235, 233, 230, 228, 225, 222, 217, 207, 198, + 188, 178, 168, 158, 148, 138, 128, 117, 107, 96, 86, 75, 65, 54, 43, 32, 22, 11, 0, 11, 22, 32, + 43, 54, 65, 75, 86, 96, 107, 117, 128, 138, 148, 158, 168, 178, 188, 198, 207, 217, 222, 225, 228, 230, + 233, 235, 238, 240, 242, 244, 245, 247, 248, 250, 251, 252, 252, 253, 253, 254, 254, 254, 254, 254, 253, 253, + 252, 251, 250, 249, 247, 246, 244, 242, 241, 238, 236, 234, 231, 229, 226, 223, 220, 223, 226, 229, 231, 234, + 236, 238, 241, 242, 244, 246, 247, 249, 250, 251, 252, 253, 253, 254, 254, 254, 254, 254, 253, 253, 252, 252, + 251, 250, 248, 247, 245, 244, 242, 240, 238, 235, 233, 230, 228, 225, 222, 217, 207, 198, 188, 178, 168, 158, + 148, 138, 128, 117, 107, 96, 86, 75, 65, 54, 43, 32, 22, 11}; + +static const uint8_t sin_table[SIN_TABLE_LEN] = { + 0, 3, 6, 9, 12, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46, 49, 52, 54, 57, + 60, 63, 66, 68, 71, 73, 76, 78, 81, 83, 86, 88, 90, 92, 95, 97, 99, 101, 102, 104, + 106, 108, 109, 111, 113, 114, 115, 117, 118, 119, 120, 121, 122, 123, 124, 125, 125, 126, 126, 127}; + +// motor control state (shared with isr) +// ------------------------------------------------------ +#define CONTROL_STATE_DISABLE 0 +#define CONTROL_STATE_PREPARE 1 +#define CONTROL_STATE_START 2 +#define CONTROL_STATE_RUNNING 3 + +static volatile uint8_t control_state = CONTROL_STATE_DISABLE; +static volatile bool is_lvc_triggered = false; +static volatile bool hall_sensor_error = false; + +// not atomic, protected by disabling interrupt while read in compute_foc_angle +static volatile uint16_t speed_erps = 0; + +// current reading saved in 8 bits for atomic access, not expected to exceed 255 (40A) +static volatile uint8_t adc_battery_current = 0; +static volatile uint8_t adc_phase_current = 0; +static volatile uint8_t adc_battery_target_current = 0; + +static volatile uint8_t foc_angle = 0; + +static volatile uint8_t pwm_duty_cycle = 0; +static volatile uint8_t pwm_duty_cycle_target = 0; + +// calculated constant limits (from config) +static uint16_t adc_low_voltage_limit = 0; +static uint8_t adc_battery_max_current = 0; +static uint8_t adc_phase_max_current = 0; + +// ------------------------------------------------------ + +// foc angle filter +static uint16_t foc_angle_accumulated = 0; + +// battery voltage filter +static uint16_t adc_battery_voltage_accumulated = 0; +static uint16_t adc_battery_voltage_filtered = 0; + +// battery current filter +static uint16_t adc_battery_current_accumulated = 0; +static uint16_t adc_battery_current_filtered = 0; + +// motor phase current filter +static uint16_t adc_phase_current_accumulated = 0; +static uint16_t adc_phase_current_filtered = 0; + +static uint16_t lvc_x10V = 0; +static uint8_t target_speed_percent = 0; +static uint8_t target_current_percent = 0; + +static uint16_t adc_steps_per_volt_x512 = ADC_10BIT_STEPS_PER_VOLT_X512; + +static void flash_opt2_afr5() +{ + // verify if PWM N channels are active on option bytes, if not, enable + static const uint8_t Value = 0x20; + + if (OPT->OPT2 != Value) + { + // unlock data memory + if (!(FLASH->IAPSR & FLASH_IAPSR_DUL)) + { + FLASH->DUKR = FLASH_RASS_KEY2; + FLASH->DUKR = FLASH_RASS_KEY1; + + while (!(FLASH->IAPSR & FLASH_IAPSR_DUL)) + ; + } + + // Enable write access to option bytes + FLASH->CR2 |= FLASH_CR2_OPT; + FLASH->NCR2 &= (uint8_t)(~FLASH_NCR2_NOPT); + + // program option byte and complement + OPT->OPT2 = Value; + OPT->NOPT2 = (uint8_t)(~Value); + + while (!(FLASH->IAPSR & FLASH_IAPSR_EOP)) + ; + + // Disable write access to option bytes + FLASH->CR2 &= (uint8_t)(~FLASH_CR2_OPT); + FLASH->NCR2 |= FLASH_NCR2_NOPT; + + // lock data memory + FLASH->IAPSR &= ~FLASH_IAPSR_DUL; + } +} + +static void read_battery_voltage() +{ + // low pass filter the voltage readed value, to avoid possible fast spikes/noise + adc_battery_voltage_accumulated -= adc_battery_voltage_accumulated >> BATTERY_VOLTAGE_FILTER_COEFFICIENT; + adc_battery_voltage_accumulated += adc_get_battery_voltage(); + adc_battery_voltage_filtered = adc_battery_voltage_accumulated >> BATTERY_VOLTAGE_FILTER_COEFFICIENT; + + is_lvc_triggered = (adc_battery_voltage_filtered < adc_low_voltage_limit); +} + +static void read_battery_current() +{ + // low pass filter the positive battery readed value (no regen current), to avoid possible fast spikes/noise + adc_battery_current_accumulated -= adc_battery_current_accumulated >> BATTERY_CURRENT_FILTER_COEFFICIENT; + adc_battery_current_accumulated += adc_battery_current; + adc_battery_current_filtered = adc_battery_current_accumulated >> BATTERY_CURRENT_FILTER_COEFFICIENT; +} + +static void read_phase_current() +{ + // low pass filter the positive motor pahse value (no regen current), to avoid possible fast spikes/noise + adc_phase_current_accumulated -= adc_phase_current_accumulated >> PHASE_CURRENT_FILTER_COEFFICIENT; + adc_phase_current_accumulated += adc_phase_current; + adc_phase_current_filtered = adc_phase_current_accumulated >> PHASE_CURRENT_FILTER_COEFFICIENT; +} + +static uint8_t asin_table(uint8_t inverted_angle_x128) +{ + // calc asin also converts the final result to degrees + uint8_t index = 0; + while (index < SIN_TABLE_LEN) + { + if (inverted_angle_x128 < sin_table[index]) + { + break; + } + + index++; + } + + // first value of table is 0 so index will always increment to at least 1 and return 0 + return index--; +} + +static void compute_foc_angle() +{ + uint16_t ui16_temp; + uint32_t ui32_temp; + uint16_t e_phase_voltage; + uint32_t i_phase_current_x2; + uint32_t l_x1048576; + uint32_t w_angular_velocity_x16; + uint16_t iwl_128; + + // FOC implementation by calculating the angle between phase current and rotor magnetic flux (BEMF) + // 1. phase voltage is calculate + // 2. I*w*L is calculated, where I is the phase current. L was a measured value for 48V motor. + // 3. inverse sin is calculated of (I*w*L) / phase voltage, were we obtain the angle + // 4. previous calculated angle is applied to phase voltage vector angle and so the + // angle between phase current and rotor magnetic flux (BEMF) is kept at 0 (max torque per amp) + + // calc E phase voltage + ui16_temp = adc_battery_voltage_filtered * ADC_10BIT_VOLTAGE_PER_ADC_STEP_X512; + ui16_temp = (ui16_temp >> 8) * pwm_duty_cycle; + e_phase_voltage = ui16_temp >> 9; + + // calc I phase current + if (pwm_duty_cycle > 10) + { + ui16_temp = ((uint16_t)adc_battery_current_filtered) * ADC_10BIT_CURRENT_PER_ADC_STEP_X512; + i_phase_current_x2 = ui16_temp / pwm_duty_cycle; + } + else + { + i_phase_current_x2 = 0; + } + + // calc W angular velocity: erps * 6.3 + // 101 = 6.3 * 16 + TIM1->IER &= ~(uint8_t)TIM1_IT_CC4; + ui16_temp = speed_erps; + TIM1->IER |= TIM1_IT_CC4; + w_angular_velocity_x16 = ui16_temp * 101; + + // --------------------------------------------------------------------------------------------------------------------- + // 36 V motor: L = 76uH + // 48 V motor: L = 135uH + // ui32_l_x1048576 = 142; // 1048576 = 2^20 | 48V + // ui32_l_x1048576 = 84; // 1048576 = 2^20 | 36V + // + // ui32_l_x1048576 = 142 <--- THIS VALUE WAS verified experimentaly on 2018.07 to be near the best value for a 48V + // motor Test done with a fixed mechanical load, duty_cycle = 200 and 100 and measured battery current was 16 and 6 + // (10 and 4 amps) + // --------------------------------------------------------------------------------------------------------------------- + +#if 0 + l_x1048576 = 84; // 36 V motor +#else + l_x1048576 = 142; // 48 V motor +#endif + + // calc IwL + ui32_temp = i_phase_current_x2 * l_x1048576; + ui32_temp *= w_angular_velocity_x16; + iwl_128 = ui32_temp >> 18; + + // calc FOC angle + uint8_t foc_angle_unfiltered = asin_table(iwl_128 / e_phase_voltage); + + // low pass filter FOC angle + foc_angle_accumulated -= foc_angle_accumulated >> 4; + foc_angle_accumulated += foc_angle_unfiltered; + foc_angle = foc_angle_accumulated >> 4; +} + +void motor_pre_init() +{ + SET_PIN_INPUT(PIN_HALL_SENSOR_A); + SET_PIN_INPUT(PIN_HALL_SENSOR_B); + SET_PIN_INPUT(PIN_HALL_SENSOR_C); + + SET_PIN_LOW(PIN_PWM_PHASE_A_LOW); + SET_PIN_LOW(PIN_PWM_PHASE_A_HIGH); + SET_PIN_LOW(PIN_PWM_PHASE_B_LOW); + SET_PIN_LOW(PIN_PWM_PHASE_B_HIGH); + SET_PIN_LOW(PIN_PWM_PHASE_C_LOW); + SET_PIN_LOW(PIN_PWM_PHASE_C_HIGH); + + SET_PIN_OUTPUT(PIN_PWM_PHASE_A_LOW); + SET_PIN_OUTPUT(PIN_PWM_PHASE_A_HIGH); + SET_PIN_OUTPUT(PIN_PWM_PHASE_B_LOW); + SET_PIN_OUTPUT(PIN_PWM_PHASE_B_HIGH); + SET_PIN_OUTPUT(PIN_PWM_PHASE_C_LOW); + SET_PIN_OUTPUT(PIN_PWM_PHASE_C_HIGH); +} + +void motor_init(uint16_t max_current_mA, uint8_t lvc_V, int16_t adc_calib_volt_step_offset) +{ + lvc_x10V = lvc_V * 10; + + uint32_t max_current_x10A = max_current_mA / 100; + + adc_steps_per_volt_x512 = ADC_10BIT_STEPS_PER_VOLT_X512 + adc_calib_volt_step_offset; + + // compute hard current limits (not changed after here) + adc_battery_max_current = (uint8_t)(((((uint32_t)MIN(max_current_x10A, MAX_BATTERY_CURRENT_AMPS_X10)) * 512) / 10) / + ADC_10BIT_CURRENT_PER_ADC_STEP_X512); + + adc_phase_max_current = + (uint8_t)(((((uint32_t)MAX_MOTOR_PHASE_CURRENT_AMPS_X10) * 512) / 10) / ADC_10BIT_CURRENT_PER_ADC_STEP_X512); + + adc_low_voltage_limit = (uint16_t)((((uint32_t)lvc_V) * adc_steps_per_volt_x512) / 512); + + flash_opt2_afr5(); + timer1_init_motor_pwm(); + motor_disable(); +} + +void motor_process() +{ + read_battery_voltage(); + read_battery_current(); + read_phase_current(); + compute_foc_angle(); +} + +void motor_enable() +{ + if (control_state == CONTROL_STATE_DISABLE) + { + control_state = CONTROL_STATE_PREPARE; + } +} + +void motor_disable() +{ + control_state = CONTROL_STATE_DISABLE; +} + +uint16_t motor_status() +{ + static uint16_t last_status = 0; + + uint16_t status = 0; + if (hall_sensor_error) + status |= MOTOR_ERROR_HALL_SENSOR; + + if (is_lvc_triggered) + status |= MOTOR_ERROR_LVC; + + if (status != last_status) + { + last_status = status; + eventlog_write_data(EVT_DATA_MOTOR_STATUS, status); + } + + return status; +} + +uint8_t motor_get_target_speed() +{ + return target_speed_percent; +} + +uint8_t motor_get_target_current() +{ + return target_current_percent; +} + +void motor_set_target_speed(uint8_t percent) +{ + if (percent > 100) + { + percent = 100; + } + + if (percent != target_speed_percent) + { + target_speed_percent = percent; + eventlog_write_data(EVT_DATA_TARGET_SPEED, percent); + + if (percent == 0) + { + pwm_duty_cycle_target = 0; + } + else + { + pwm_duty_cycle_target = (uint8_t)MAP16(percent, 1, 100, PWM_DUTY_CYCLE_MIN, PWM_DUTY_CYCLE_MAX); + } + } +} + +void motor_set_target_current(uint8_t percent) +{ + if (percent > 100) + { + percent = 100; + } + + if (percent != target_current_percent) + { + target_current_percent = percent; + eventlog_write_data(EVT_DATA_TARGET_CURRENT, percent); + + adc_battery_target_current = ((uint16_t)percent * adc_battery_max_current) / 100; + } +} + +int16_t motor_calibrate_battery_voltage(uint16_t actual_voltage_x100) +{ + int16_t diff = 0; + if (actual_voltage_x100 != 0) + { + uint16_t calibrated_adc_steps_volt_x512 = + (uint16_t)(((uint32_t)adc_battery_voltage_filtered * 51200u) / actual_voltage_x100); + + diff = calibrated_adc_steps_volt_x512 - ADC_10BIT_STEPS_PER_VOLT_X512; + adc_steps_per_volt_x512 = calibrated_adc_steps_volt_x512; + } + else + { + // reset calibration if 0 is received + adc_steps_per_volt_x512 = ADC_10BIT_STEPS_PER_VOLT_X512; + diff = 0; + } + + eventlog_write_data(EVT_DATA_CALIBRATE_VOLTAGE, adc_steps_per_volt_x512); + + return diff; +} + +uint16_t motor_get_battery_lvc_x10() +{ + return lvc_x10V; +} + +uint16_t motor_get_battery_current_x10() +{ + return (uint16_t)((((uint32_t)adc_battery_current_filtered * 10) * ADC_10BIT_CURRENT_PER_ADC_STEP_X512) >> 9); +} + +uint16_t motor_get_battery_voltage_x10() +{ + return (uint16_t)(((uint32_t)adc_battery_voltage_filtered * 5120) / adc_steps_per_volt_x512); +} + +// state variables only used by isr +// --------------------------------------------- +static uint8_t hall_sensors_state_last = 0; +static uint8_t rotor_absolute_angle = 0; +static uint8_t half_erps_flag = 0; +static uint8_t commutation_type = BLOCK_COMMUTATION; + +static uint16_t pwm_duty_cycle_ramp_up_counter = 0; +static uint16_t pwm_duty_cycle_ramp_down_counter = 0; + +static uint16_t pwm_cycles_counter = 1; +static uint16_t pwm_cycles_counter_6 = 1; +static uint16_t pwm_cycles_counter_total = 0xffff; + +static uint16_t adc_current_ramp_up_counter = 0; +static uint8_t current_controller_counter = 0; +static uint16_t speed_controller_counter = 0; + +static uint8_t adc_battery_ramp_max_current = 0; + +// Measures did with a 24V Q85 328 RPM motor, rotating motor backwards by hand: +// Hall sensor A positive to negative transition | BEMF phase B at max value / top of sinewave +// Hall sensor B positive to negative transition | BEMF phase A at max value / top of sinewave +// Hall sensor C positive to negative transition | BEMF phase C at max value / top of sinewave + +// runs every 64us (PWM frequency) +// Measured on 2022-12-04, the interrupt code takes about 45% of the total 64us +void isr_timer1_cmp(void) __interrupt(ITC_IRQ_TIM1_CAPCOM) +{ + // read battery current adc value, should happen at middle of the pwm duty cycle + // no scan, align data right since we are only interested in the 8 lsb. + ADC1->CR2 = (ADC1_ALIGN_RIGHT); + + // disable eoc interrupt, clear EOC flag and select channel 5 (current sense) + ADC1->CSR = 0x05; + + // perform single mode ADC1 conversion + ADC1->CR1 |= ADC1_CR1_ADON; + while (!(ADC1->CSR & ADC1_CSR_EOC)) + ; + + // adc current reading is truncated to 8bit since that allows a + // range of up to 40A which it is not expected to be surpassed. + // check of 8bit overflow and save result, flag is used to limit + // current in isr if overflow for some reason would occur. + uint8_t adc_battery_current_ovf = ADC1->DRH; + + // atomic write (uint8), current is not expected to exceed adc 255 (40A) + adc_battery_current = ADC1->DRL; + + switch (control_state) + { + case CONTROL_STATE_DISABLE: + // disable outputs + TIM1->CCER1 &= ~(uint8_t)(TIM1_CCER1_CC1E | TIM1_CCER1_CC1NE); // OC1 + TIM1->CCER1 &= ~(uint8_t)(TIM1_CCER1_CC2E | TIM1_CCER1_CC2NE); // OC2 + TIM1->CCER2 &= ~(uint8_t)(TIM1_CCER2_CC3E | TIM1_CCER2_CC3NE); // OC3 + break; + case CONTROL_STATE_PREPARE: + if (speed_erps > 0) + { + // Restart from duty cycle mapped from erps. + // This is probably not the correct way to do this, but + // it seems to work reasonably well. VESC tracks back-emf + // to calculate duty cyle to restart from... + pwm_duty_cycle = + (uint8_t)MAP32(speed_erps, 0, MAX_MOTOR_SPEED_ERPS, PWM_DUTY_CYCLE_MIN, PWM_DUTY_CYCLE_MAX); + } + control_state = CONTROL_STATE_START; + break; + case CONTROL_STATE_START: + // enable outputs + TIM1->CCER1 |= (uint8_t)(TIM1_CCER1_CC1E | TIM1_CCER1_CC1NE); // OC1 + TIM1->CCER1 |= (uint8_t)(TIM1_CCER1_CC2E | TIM1_CCER1_CC2NE); // OC2 + TIM1->CCER2 |= (uint8_t)(TIM1_CCER2_CC3E | TIM1_CCER2_CC3NE); // OC3 + control_state = CONTROL_STATE_RUNNING; + break; + default: + break; + } + + // calculate motor current adc value + if (pwm_duty_cycle > 0) + { + // atomic write (uint8), current is not expected to exceed adc 255 (40A) + adc_phase_current = (uint8_t)((adc_battery_current * 256u) / pwm_duty_cycle); + } + else + { + adc_phase_current = 0; + } + + // trigger adc conversion of all channels (scan conversion, buffered) + // adc scan mode conversion will finish before + // this motor control interrupt will be run next time + // + // enable scan, align left + ADC1->CR2 = (ADC1_ALIGN_LEFT | ADC1_CR2_SCAN); + + // clear EOC flag, enable eoc interrupt, scan read all channel 0-7 + ADC1->CSR = (ADC1_CSR_EOCIE | 0x07); + + // start adc scan mode conversion + ADC1->CR1 |= ADC1_CR1_ADON; + + // read hall sensor signals + // find the motor rotor absolute angle + // calc motor speed in erps (speed_erps) + + // read hall sensors signal pins and mask other pins + // hall sensors sequence with motor forward rotation: 4, 6, 2, 3, 1, 5 + uint8_t hall_sensors_state = ((GET_PORT(PIN_HALL_SENSOR_A)->IDR & GET_PIN(PIN_HALL_SENSOR_A)) >> 5) | + ((GET_PORT(PIN_HALL_SENSOR_B)->IDR & GET_PIN(PIN_HALL_SENSOR_B)) >> 1) | + ((GET_PORT(PIN_HALL_SENSOR_C)->IDR & GET_PIN(PIN_HALL_SENSOR_C)) >> 3); + + // make sure we run next code only when there is a change on the hall sensors signal + if (hall_sensors_state != hall_sensors_state_last) + { + hall_sensors_state_last = hall_sensors_state; + + switch (hall_sensors_state) + { + case 3: + rotor_absolute_angle = (uint8_t)MOTOR_ROTOR_ANGLE_150; + break; + + case 1: + if (half_erps_flag == 1) + { + half_erps_flag = 0; + pwm_cycles_counter_total = pwm_cycles_counter; + pwm_cycles_counter = 1; + + if (pwm_cycles_counter_total > 0) + { + // This division takes 4.4us + speed_erps = PWM_CYCLES_SECOND / pwm_cycles_counter_total; + } + else + { + speed_erps = PWM_CYCLES_SECOND; + } + + // update motor commutation state based on motor speed + if (speed_erps > MOTOR_ROTOR_ERPS_START_INTERPOLATION_60_DEGREES) + { + if (commutation_type == BLOCK_COMMUTATION) + { + commutation_type = SINEWAVE_INTERPOLATION_60_DEGREES; + } + } + else + { + if (commutation_type == SINEWAVE_INTERPOLATION_60_DEGREES) + { + commutation_type = BLOCK_COMMUTATION; + foc_angle = 0; + } + } + } + + rotor_absolute_angle = (uint8_t)MOTOR_ROTOR_ANGLE_210; + break; + + case 5: + rotor_absolute_angle = (uint8_t)MOTOR_ROTOR_ANGLE_270; + break; + + case 4: + rotor_absolute_angle = (uint8_t)MOTOR_ROTOR_ANGLE_330; + break; + + case 6: + half_erps_flag = 1; + + rotor_absolute_angle = (uint8_t)MOTOR_ROTOR_ANGLE_30; + break; + + // BEMF is always 90 degrees advanced over motor rotor position degree zero + // and here (hall sensor C blue wire, signal transition from positive to negative), + // phase B BEMF is at max value (measured on osciloscope by rotating the motor) + case 2: + rotor_absolute_angle = (uint8_t)MOTOR_ROTOR_ANGLE_90; + break; + + default: + // invalid hall sensor signal + hall_sensor_error = true; + return; + } + + hall_sensor_error = false; + pwm_cycles_counter_6 = 1; + } + + // count number of fast loops / pwm cycles and reset some states when motor is near zero speed + if (pwm_cycles_counter < PWM_CYCLES_COUNTER_MAX) + { + pwm_cycles_counter++; + pwm_cycles_counter_6++; + } + else // happens when motor is stopped or near zero speed + { + pwm_cycles_counter = 1; // don't put to 0 to avoid 0 divisions + pwm_cycles_counter_6 = 1; + half_erps_flag = 0; + speed_erps = 0; + pwm_cycles_counter_total = 0xffff; + foc_angle = 0; + commutation_type = BLOCK_COMMUTATION; + hall_sensors_state_last = 0; // this way we force execution of hall sensors code next time + } + + // calc interpolation angle and sinewave table index + uint8_t svm_table_index = rotor_absolute_angle + foc_angle; + +#if 1 // may be useful to disable interpolation when debugging + + // calculate the interpolation angle (and it doesn't work when motor starts and at very low speeds) + if (commutation_type == SINEWAVE_INTERPOLATION_60_DEGREES) + { + // division by 0: motor_pwm_cycles_counter_total should never be 0 + // TODO: verifiy if (motor_pwm_cycles_counter_6 << 8) do not overflow + uint8_t interpolation_angle = + (pwm_cycles_counter_6 << 8) / pwm_cycles_counter_total; // this operations take 4.4us + svm_table_index += interpolation_angle; + } +#endif + + // pwm duty cycle controller + // ---------------------------------------------------------------------- + // brakes are active + // limit battery undervoltage + // limit battery max current + // limit motor max erps + // ramp up/down pwm duty cycle towards target + + ++current_controller_counter; + ++speed_controller_counter; + + if (control_state == CONTROL_STATE_DISABLE || is_lvc_triggered || (pwm_duty_cycle_target == 0) || + (GET_PIN_INPUT_STATE(PIN_BRAKE) == 0) // active low + ) + { + if (pwm_duty_cycle) + { + --pwm_duty_cycle; + } + } + // do not control current at every PWM cycle, that will measure and control too fast. Use counter to limit + else if (current_controller_counter > CURRENT_CONTROLLER_CHECK_PERIODS && + ( + // check if truncated 8bit current reading did overflow + adc_battery_current_ovf || + // compare against ramp controller current limit + adc_battery_current > adc_battery_ramp_max_current || + // or hard motor phase current limit + adc_phase_current > adc_phase_max_current)) + { + if (pwm_duty_cycle) + { + --pwm_duty_cycle; + } + } + else if (speed_controller_counter > SPEED_CONTROLLER_CHECK_PERIODS && // test about every 100ms + speed_erps > MAX_MOTOR_SPEED_ERPS) + { + if (pwm_duty_cycle) + { + --pwm_duty_cycle; + } + } + else // nothing to limit, so adjust duty_cycle to duty_cycle_target + { + if (pwm_duty_cycle_target > pwm_duty_cycle) + { + if (pwm_duty_cycle_ramp_up_counter++ >= PWM_DUTY_CYCLE_RAMP_UP_INVERSE_STEP) + { + pwm_duty_cycle_ramp_up_counter = 0; + ++pwm_duty_cycle; + } + } + else if (pwm_duty_cycle_target < pwm_duty_cycle) + { + if (pwm_duty_cycle_ramp_down_counter++ >= PWM_DUTY_CYCLE_RAMP_DOWN_INVERSE_STEP) + { + pwm_duty_cycle_ramp_down_counter = 0; + --pwm_duty_cycle; + } + } + } + + // reset periodic check counters + if (speed_controller_counter > SPEED_CONTROLLER_CHECK_PERIODS) + { + speed_controller_counter = 0; + } + + if (current_controller_counter > CURRENT_CONTROLLER_CHECK_PERIODS) + { + current_controller_counter = 0; + } + +// calculate final pwm duty cycle values to be applied to TIMER1 + +// The first half of the table is the positive offset from the middle (0x100), +// in that case just set MSB to 0x1, and the value from the table*duty cycle to LSB. +// The second half of the table is a negative offset from that same middle, +// and should be substracted from 0x100. +// To cheat, we leave it as 0x100 when this value * duty cycle is 0, +// otherwise we assume MSB is 0, and just invert the value from the table from LSB. +// Checking to see if svm_table_index >= 128 (180 degrees) by & 0x80, +// as SDCC is not yet smart enough to do that automatically. +#define CALC_PHASE(PHASE_OUTPUT) \ + do \ + { \ + uint8_t tmp = ((uint16_t)(pwm_duty_cycle * svm_table[svm_table_index]) / 256); \ + if (tmp > 0 && (svm_table_index & 0x80)) \ + { \ + PHASE_OUTPUT##_lsb = 0 - tmp; \ + PHASE_OUTPUT##_msb = 0; \ + } \ + else \ + { \ + PHASE_OUTPUT##_lsb = tmp; \ + PHASE_OUTPUT##_msb = 1; \ + } \ + } while (0) + + // phase B as reference phase + uint8_t phase_b_voltage_msb; + uint8_t phase_b_voltage_lsb; + CALC_PHASE(phase_b_voltage); + + // phase C is advanced 120 degrees over phase B + svm_table_index += 85; // 120º / 360 * 256 = 85 + uint8_t phase_c_voltage_msb; + uint8_t phase_c_voltage_lsb; + CALC_PHASE(phase_c_voltage); + + // phase A is advanced 240 degrees over phase B + svm_table_index += 86; // 240º / 360 * 256 = 171 - 85 already added = 86 + uint8_t phase_a_voltage_msb; + uint8_t phase_a_voltage_lsb; + CALC_PHASE(phase_a_voltage); + + // set final duty cycle value to pwm timers + // phase B + TIM1->CCR3H = phase_b_voltage_msb; + TIM1->CCR3L = phase_b_voltage_lsb; + // phase C + TIM1->CCR2H = phase_c_voltage_msb; + TIM1->CCR2L = phase_c_voltage_lsb; + // phase A + TIM1->CCR1H = phase_a_voltage_msb; + TIM1->CCR1L = phase_a_voltage_lsb; + + // ramp up motor current + if (adc_battery_target_current > adc_battery_ramp_max_current) + { + if (adc_current_ramp_up_counter++ >= CURRENT_RAMP_UP_INVERSE_STEP) + { + adc_current_ramp_up_counter = 0; + adc_battery_ramp_max_current++; + } + } + else if (adc_battery_target_current < adc_battery_ramp_max_current) + { + // we are not doing a ramp down here, just directly setting to the target value + adc_battery_ramp_max_current = adc_battery_target_current; + } + + // clears the timer1 interrupt CC4 pending bit + TIM1->SR1 = (uint8_t)(~(uint8_t)TIM1_IT_CC4); +} diff --git a/code/firmware/src/tsdz2/pins.h b/code/firmware/src/tsdz2/pins.h new file mode 100644 index 00000000..b962f4db --- /dev/null +++ b/code/firmware/src/tsdz2/pins.h @@ -0,0 +1,46 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#ifndef _TSDZ2_PINS_H_ +#define _TSDZ2_PINS_H_ + +#include "tsdz2/cpu.h" + +#include +#include + +#define PIN_HALL_SENSOR_A GPIOE, GPIO_PIN_5 +#define PIN_HALL_SENSOR_B GPIOD, GPIO_PIN_2 +#define PIN_HALL_SENSOR_C GPIOC, GPIO_PIN_5 + +#define PIN_PWM_PHASE_A_LOW GPIOB, GPIO_PIN_2 +#define PIN_PWM_PHASE_A_HIGH GPIOC, GPIO_PIN_3 + +#define PIN_PWM_PHASE_B_LOW GPIOB, GPIO_PIN_1 +#define PIN_PWM_PHASE_B_HIGH GPIOC, GPIO_PIN_2 + +#define PIN_PWM_PHASE_C_LOW GPIOB, GPIO_PIN_0 +#define PIN_PWM_PHASE_C_HIGH GPIOC, GPIO_PIN_1 + +#define PIN_BATTERY_CURRENT GPIOB, GPIO_PIN_5 +#define PIN_BATTERY_VOLTAGE GPIOB, GPIO_PIN_6 + +#define PIN_PAS1 GPIOD, GPIO_PIN_7 +#define PIN_PAS2 GPIOE, GPIO_PIN_0 +#define PIN_SPEED_SENSOR GPIOA, GPIO_PIN_1 +#define PIN_BRAKE GPIOC, GPIO_PIN_6 +#define PIN_THROTTLE GPIOB, GPIO_PIN_7 +#define PIN_LIGHTS GPIOD, GPIO_PIN_4 + +#define PIN_TORQUE_SENSOR GPIOB, GPIO_PIN_3 +#define PIN_TORQUE_SENSOR_EXC GPIOD, GPIO_PIN_3 + +#define PIN_EXTERNAL_RX GPIOD, GPIO_PIN_6 +#define PIN_EXTERNAL_TX GPIOD, GPIO_PIN_5 + +#endif diff --git a/code/firmware/src/tsdz2/sensors.c b/code/firmware/src/tsdz2/sensors.c new file mode 100644 index 00000000..f4d85a64 --- /dev/null +++ b/code/firmware/src/tsdz2/sensors.c @@ -0,0 +1,273 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "sensors.h" +#include "fwconfig.h" +#include "tsdz2/interrupt.h" +#include "tsdz2/pins.h" +#include "tsdz2/stm8.h" +#include "tsdz2/timers.h" + +#include + +// interrupt runs at 100us interval, see timer4 setup in timers.c + +// :TODO: this file contains a lot of duplicated code from bbsx version, try to share code + +#define PAS_SENSOR_NUM_SIGNALS PAS_PULSES_REVOLUTION +#define PAS_SENSOR_MIN_PULSE_MS_X10 50 // 500rpm limit + +#define SPEED_SENSOR_MIN_PULSE_MS_X10 500 +#define SPEED_SENSOR_TIMEOUT_MS_X10 25000 + +static volatile uint16_t pas_pulse_counter; +static volatile bool pas_direction_backward; +static volatile uint16_t pas_period_length; // pulse length counted in interrupt frequency (100us) +static uint16_t pas_period_counter; +static bool pas_prev1; +static bool pas_prev2; +static uint16_t pas_stop_delay_periods; + +static volatile uint16_t speed_ticks_period_length; // pulse length counted in interrupt frequency (100us) +static uint16_t speed_period_counter; +static bool speed_prev_state; +static uint8_t speed_ticks_per_rpm; + +extern void torque_sensor_init(); +extern void torque_sensor_process(); + +void sensors_init() +{ + pas_period_counter = 0; + pas_pulse_counter = 0; + pas_direction_backward = false; + pas_period_length = 0; + pas_stop_delay_periods = 1500; + speed_period_counter = 0; + speed_ticks_period_length = 0; + speed_prev_state = false; + speed_ticks_per_rpm = 1; + + // pins do not have external interrupt, use timer0 to evaluate state frequently + SET_PIN_INPUT(PIN_PAS1); + SET_PIN_INPUT(PIN_PAS2); + SET_PIN_INPUT(PIN_SPEED_SENSOR); + SET_PIN_INPUT_PULLUP(PIN_BRAKE); + + pas_prev1 = GET_PIN_INPUT_STATE(PIN_PAS1); + pas_prev2 = GET_PIN_INPUT_STATE(PIN_PAS2); + + torque_sensor_init(); + torque_sensor_process(); + + timer4_init_sensors(); +} + +void sensors_process() +{ + torque_sensor_process(); +} + +void pas_set_stop_delay(uint16_t delay_ms) +{ + pas_stop_delay_periods = delay_ms * 10; +} + +uint16_t pas_get_cadence_rpm_x10() +{ + uint16_t tmp; + TIM4->IER &= ~TIM4_IT_UPDATE; // disable timer4 interrupt + tmp = pas_period_length; + TIM4->IER |= TIM4_IT_UPDATE; + + if (tmp > 0) + { + return (uint16_t)((6000000ul / PAS_SENSOR_NUM_SIGNALS) / tmp); + } + else + { + return 0; + } +} + +uint16_t pas_get_pulse_counter() +{ + uint16_t tmp; + TIM4->IER &= ~TIM4_IT_UPDATE; // disable timer4 interrupts + tmp = pas_pulse_counter; + TIM4->IER |= TIM4_IT_UPDATE; + + return tmp; +} + +bool pas_is_pedaling_forwards() +{ + uint16_t period_length; + uint8_t direction_backward; + TIM4->IER &= ~TIM4_IT_UPDATE; // disable timer4 interrupts + period_length = pas_period_length; + direction_backward = pas_direction_backward; + TIM4->IER |= TIM4_IT_UPDATE; + + // atomic read operation, no need to disable timer interrupt + return period_length > 0 && !direction_backward; +} + +bool pas_is_pedaling_backwards() +{ + uint16_t period_length; + uint8_t direction_backward; + TIM4->IER &= ~TIM4_IT_UPDATE; // disable timer4 interrupts + period_length = pas_period_length; + direction_backward = pas_direction_backward; + TIM4->IER |= TIM4_IT_UPDATE; + + return (period_length > 0) && direction_backward; +} + +void speed_sensor_set_signals_per_rpm(uint8_t num_signals) +{ + speed_ticks_per_rpm = num_signals; +} + +bool speed_sensor_is_moving() +{ + uint16_t tmp; + TIM4->IER &= ~TIM4_IT_UPDATE; // disable timer4 interrupts + tmp = speed_ticks_period_length; + TIM4->IER |= TIM4_IT_UPDATE; + + return tmp > 0; +} + +uint16_t speed_sensor_get_rpm_x10() +{ + uint16_t tmp; + TIM4->IER &= ~TIM4_IT_UPDATE; // disable timer4 interrupts + tmp = speed_ticks_period_length; + TIM4->IER |= TIM4_IT_UPDATE; + + if (tmp > 0) + { + return 6000000ul / tmp / speed_ticks_per_rpm; + } + + return 0; +} + +int16_t temperature_contr_x100() +{ + return 0; // n/a +} + +int16_t temperature_motor_x100() +{ + return 0; // n/a +} + +bool brake_is_activated() +{ + return !GET_PIN_INPUT_STATE(PIN_BRAKE); +} + +bool shift_sensor_is_activated() +{ + return false; // n/a +} + +void isr_timer4_ovf(void) __interrupt(ITC_IRQ_TIM4_OVF) +{ + // clear interrupt bit + TIM4->SR1 &= (uint8_t)(~TIM4_IT_UPDATE); + + // Pas + { + bool pas1 = GET_PIN_INPUT_STATE(PIN_PAS1); + bool pas2 = GET_PIN_INPUT_STATE(PIN_PAS2); + + if (pas1 && !pas_prev1 /* && pas_period_counter > PAS_SENSOR_MIN_PULSE_MS_X10 */) + { + pas_pulse_counter++; + + if (pas_direction_backward != pas2) + { + pas_direction_backward = pas2; + + // Reset pas pulse counter if pedal direction is changed, + // this variable counts the number of pulses since start of pedaling session. + pas_pulse_counter = 0; + } + + if (pas_period_counter > 0) + { + if (pas_period_counter <= pas_stop_delay_periods) + { + pas_period_length = pas_period_counter; // save in order to be able to calculate rpm when needed + } + else + { + pas_period_length = 0; + } + + pas_period_counter = 0; + } + } + else + { + // Do not allow wraparound or computed pedaling cadence will wrong after pedals has been still. + if (pas_period_counter < 65535) + { + pas_period_counter++; + } + + if (pas_period_length > 0 && pas_period_counter > pas_stop_delay_periods) + { + pas_period_length = 0; + pas_pulse_counter = 0; + pas_direction_backward = false; + } + } + + pas_prev1 = pas1; + pas_prev2 = pas2; + } + + // Speed sensor + { + bool spd = GET_PIN_INPUT_STATE(PIN_SPEED_SENSOR); + + if (spd && !speed_prev_state && speed_period_counter > SPEED_SENSOR_MIN_PULSE_MS_X10) + { + if (speed_period_counter <= SPEED_SENSOR_TIMEOUT_MS_X10) + { + speed_ticks_period_length = speed_period_counter; + } + else + { + speed_ticks_period_length = 0; + } + + speed_period_counter = 0; + } + else + { + // Do not allow wraparound or computed speed will wrong after bike has been still. + if (speed_period_counter < 65535) + { + speed_period_counter++; + } + + if (speed_ticks_period_length > 0 && speed_period_counter > SPEED_SENSOR_TIMEOUT_MS_X10) + { + speed_ticks_period_length = 0; + } + } + + speed_prev_state = spd; + } +} diff --git a/code/firmware/src/tsdz2/stm8.h b/code/firmware/src/tsdz2/stm8.h new file mode 100644 index 00000000..2f51ba34 --- /dev/null +++ b/code/firmware/src/tsdz2/stm8.h @@ -0,0 +1,56 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#ifndef _TSDZ2_STM_8_H_ +#define _TSDZ2_STM_8_H_ + +#include + +#define EXPAND(x) x + +#define SET_PIN_INPUT_(PORT, PIN) \ + PORT->DDR &= (uint8_t)(~(PIN)); \ + PORT->CR1 &= (uint8_t)(~(PIN)) +#define SET_PIN_INPUT(...) EXPAND(SET_PIN_INPUT_(__VA_ARGS__)) + +#define SET_PIN_INPUT_PULLUP_(PORT, PIN) \ + PORT->DDR &= (uint8_t)(~(PIN)); \ + PORT->CR1 |= (uint8_t)PIN +#define SET_PIN_INPUT_PULLUP(...) EXPAND(SET_PIN_INPUT_PULLUP_(__VA_ARGS__)) + +#define SET_PIN_OUTPUT_(PORT, PIN) \ + PORT->DDR |= (uint8_t)PIN; \ + PORT->CR1 |= (uint8_t)PIN; \ + PORT->CR2 |= (uint8_t)(PIN) +#define SET_PIN_OUTPUT(...) EXPAND(SET_PIN_OUTPUT_(__VA_ARGS__)) + +#define SET_PIN_OUTPUT_OPEN_DRAIN_(PORT, PIN) \ + PORT->DDR |= (uint8_t)PIN; \ + PORT->CR1 &= (uint8_t)(~(PIN)); \ + PORT->CR2 |= (uint8_t)(PIN) +#define SET_PIN_OUTPUT_OPEN_DRAIN(...) EXPAND(SET_PIN_OUTPUT_OPEN_DRAIN_(__VA_ARGS__)) + +#define GET_PIN_INPUT_STATE_(PORT, PIN) ((PORT->IDR & (uint8_t)PIN) != 0) +#define GET_PIN_INPUT_STATE(...) EXPAND(GET_PIN_INPUT_STATE_(__VA_ARGS__)) + +#define SET_PIN_HIGH_(PORT, PIN) PORT->ODR |= (uint8_t)PIN +#define SET_PIN_HIGH(...) EXPAND(SET_PIN_HIGH_(__VA_ARGS__)) + +#define SET_PIN_LOW_(PORT, PIN) PORT->ODR &= (uint8_t)(~PIN) +#define SET_PIN_LOW(...) EXPAND(SET_PIN_LOW_(__VA_ARGS__)) + +#define TOGGLE_PIN_(PORT, PIN) PORT->ODR ^= (PIN) +#define TOGGLE_PIN(...) EXPAND(TOGGLE_PIN_(__VA_ARGS__)) + +#define GET_PIN_(PORT, PIN) PIN +#define GET_PIN(...) EXPAND(GET_PIN_(__VA_ARGS__)) + +#define GET_PORT_(PORT, PIN) PORT +#define GET_PORT(...) EXPAND(GET_PORT_(__VA_ARGS__)) + +#endif diff --git a/code/firmware/src/tsdz2/system.c b/code/firmware/src/tsdz2/system.c new file mode 100644 index 00000000..96fc8373 --- /dev/null +++ b/code/firmware/src/tsdz2/system.c @@ -0,0 +1,68 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "system.h" +#include "cpu.h" +#include "tsdz2/interrupt.h" +#include "tsdz2/timers.h" +#include "watchdog.h" + +#include +#include +#include + +static volatile uint32_t _ms; + +void system_init() +{ + CLK->CKDIVR = 0x00; // Set 16MHz + while ((CLK->ICKR & CLK_ICKR_HSIRDY) == 0) + ; // Wait for stable clock + + _ms = 0; + + // Setup timer3 as a ms counter + timer3_init_system(); + + enableInterrupts(); +} + +uint32_t system_ms() +{ + uint32_t val; + uint8_t ier = TIM3->IER; + + TIM3->IER &= ~(TIM3_IT_UPDATE); // disable timer3 interrupt + val = _ms; + + TIM3->IER = ier; + + return val; +} + +void system_delay_ms(uint16_t ms) +{ + if (!ms) + { + return; + } + + uint32_t end = system_ms() + ms; + while (system_ms() != end) + { + watchdog_yeild(); + } +} + +void isr_timer3_ovf(void) __interrupt(ITC_IRQ_TIM3_OVF) +{ + _ms++; + + // Clear interrupt pending bit + TIM3->SR1 &= (uint8_t)(~TIM3_IT_UPDATE); +} diff --git a/code/firmware/src/tsdz2/timers.c b/code/firmware/src/tsdz2/timers.c new file mode 100644 index 00000000..c4d68909 --- /dev/null +++ b/code/firmware/src/tsdz2/timers.c @@ -0,0 +1,186 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "timers.h" +#include "cpu.h" + +#include +#include +#include +#include +#include +#include + +#define TIM1_AUTO_RELOAD_PERIOD 511 +#define TIM2_AUTO_RELOAD_PERIOD 159 // 20us +#define TIM3_AUTO_RELOAD_PERIOD 15999 // 1ms +#define TIM4_AUTO_RELOAD_PERIOD 99 // 100us + +void timers_init() +{ + // nothing to do here +} + +void timer1_init_motor_pwm() +{ + CLK->PCKENR1 |= CLK_PCKENR1_TIM1; + + // prescaler + TIM1->PSCRH = 0; + TIM1->PSCRL = 0; + + // auto reload + // clock = 16MHz, counter period = 1024, PWM freq = 16MHz / 1024 = 15.625MHz + // (BUT PWM center aligned mode needs double frequency) + TIM1->ARRH = (uint8_t)(TIM1_AUTO_RELOAD_PERIOD >> 8); + TIM1->ARRL = (uint8_t)TIM1_AUTO_RELOAD_PERIOD; + + TIM1->CR1 |= TIM1_COUNTERMODE_CENTERALIGNED1; + TIM1->RCR = 1; + + // OC1 + TIM1->CCER1 |= (uint8_t)((uint8_t)(TIM1_OUTPUTSTATE_DISABLE & TIM1_CCER1_CC1E) | + (uint8_t)(TIM1_OUTPUTNSTATE_DISABLE & TIM1_CCER1_CC1NE) | + (uint8_t)(TIM1_OCPOLARITY_HIGH & TIM1_CCER1_CC1P) | + (uint8_t)(TIM1_OCNPOLARITY_HIGH & TIM1_CCER1_CC1NP)); + + TIM1->CCMR1 |= TIM1_OCMODE_PWM1; + + TIM1->OISR |= (uint8_t)((uint8_t)(TIM1_OCIDLESTATE_RESET & TIM1_OISR_OIS1) | + (uint8_t)(TIM1_OCNIDLESTATE_SET & TIM1_OISR_OIS1N)); + + TIM1->CCR1H = 0; + TIM1->CCR1L = 255; + + // OC2 + TIM1->CCER1 |= (uint8_t)((uint8_t)(TIM1_OUTPUTSTATE_DISABLE & TIM1_CCER1_CC2E) | + (uint8_t)(TIM1_OUTPUTNSTATE_DISABLE & TIM1_CCER1_CC2NE) | + (uint8_t)(TIM1_OCPOLARITY_HIGH & TIM1_CCER1_CC2P) | + (uint8_t)(TIM1_OCNPOLARITY_HIGH & TIM1_CCER1_CC2NP)); + + TIM1->CCMR2 |= TIM1_OCMODE_PWM1; + + TIM1->OISR |= (uint8_t)((uint8_t)(TIM1_OCIDLESTATE_RESET & TIM1_OISR_OIS2) | + (uint8_t)(TIM1_OCNIDLESTATE_SET & TIM1_OISR_OIS2N)); + + TIM1->CCR2H = 0; + TIM1->CCR2L = 255; + + // OC3 + TIM1->CCER2 |= (uint8_t)((uint8_t)(TIM1_OUTPUTSTATE_DISABLE & TIM1_CCER2_CC3E) | + (uint8_t)(TIM1_OUTPUTNSTATE_DISABLE & TIM1_CCER2_CC3NE) | + (uint8_t)(TIM1_OCPOLARITY_HIGH & TIM1_CCER2_CC3P) | + (uint8_t)(TIM1_OCNPOLARITY_HIGH & TIM1_CCER2_CC3NP)); + + TIM1->CCMR3 |= TIM1_OCMODE_PWM1; + + TIM1->OISR |= (uint8_t)((uint8_t)(TIM1_OCIDLESTATE_RESET & TIM1_OISR_OIS3) | + (uint8_t)(TIM1_OCNIDLESTATE_SET & TIM1_OISR_OIS3N)); + + TIM1->CCR3H = 0; + TIM1->CCR3L = 255; + + // OC4 + // Used for to fire interrupt at a specific time (middle of DC link current pulses) + // and is always syncronized with PWM + + TIM1->CCER2 |= (uint8_t)((uint8_t)(TIM1_OUTPUTSTATE_DISABLE & TIM1_CCER2_CC4E) | + (uint8_t)(TIM1_OCPOLARITY_HIGH & TIM1_CCER2_CC4P)); + + TIM1->OISR &= (uint8_t)(~TIM1_OISR_OIS4); + + // timming for interrupt firing (hand adjusted) + const uint16_t Timing = 285; + + TIM1->CCR4H = (uint8_t)(Timing >> 8); + TIM1->CCR4L = (uint8_t)Timing; + + // hardware needs a dead time of 1us + // 16, // DTG = 0; dead time in 62.5 ns steps; 1us/62.5ns = 16 + TIM1->DTR = (uint8_t)16; + + TIM1->BKR = (uint8_t)(TIM1_OSSISTATE_ENABLE | TIM1_LOCKLEVEL_OFF | TIM1_BREAK_DISABLE | TIM1_BREAKPOLARITY_LOW | + TIM1_AUTOMATICOUTPUT_DISABLE); + + // enable cc4 interrupt + TIM1->IER |= TIM1_IT_CC4; + + // enable timer + TIM1->CR1 |= TIM1_CR1_CEN; + + TIM1->BKR |= TIM1_BKR_MOE; +} + +void timer2_init_torque_sensor_pwm() +{ + // Timer2 is used to create the pulse signal for excitation of the torque sensor circuit + // Timer2 clock = 16MHz; target: 20us period --> 50khz + // counter period = (1 / (16000000 / prescaler)) * (159 + 1) = 20us + + // set period + TIM2->PSCR = TIM2_PRESCALER_2; + TIM2->ARRH = (uint8_t)(TIM2_AUTO_RELOAD_PERIOD >> 8); + TIM2->ARRL = (uint8_t)(TIM2_AUTO_RELOAD_PERIOD); + + // pulse of 2us + TIM2->CCER1 |= TIM2_CCER1_CC2E; // output enable + TIM2->CCMR2 |= TIM2_OCMODE_PWM1; + TIM2->CCR2H = 0; + TIM2->CCR2L = 16; + + // enable + TIM2->CCMR2 |= TIM2_CCMR_OCxPE; + TIM2->CR1 |= TIM2_CR1_ARPE; + TIM2->CR1 |= TIM2_CR1_CEN; +} + +void timer3_init_system() +{ + // enable timer3 clock source + CLK->PCKENR1 |= CLK_PCKENR1_TIM3; + + // set period + TIM3->PSCR = TIM3_PRESCALER_1; + TIM3->ARRH = (uint8_t)(TIM3_AUTO_RELOAD_PERIOD >> 8); + TIM3->ARRL = (uint8_t)(TIM3_AUTO_RELOAD_PERIOD); + + // clear counter + TIM3->CNTRH = 0; + TIM3->CNTRL = 0; + + // enable TIM3 interrupt + TIM3->IER |= TIM3_IT_UPDATE; + + // clear interrupt pending bit + TIM3->SR1 &= ~TIM3_IT_UPDATE; + + // TIM3 enable + TIM3->CR1 |= TIM3_CR1_CEN; +} + +void timer4_init_sensors() +{ + // enable timer4 clock source + CLK->PCKENR1 |= CLK_PCKENR1_TIM4; + + // set period + TIM4->PSCR = TIM4_PRESCALER_16; + TIM4->ARR = TIM4_AUTO_RELOAD_PERIOD; + + // clear counter + TIM4->CNTR = 0; + + // enable TIM4 interrupt + TIM4->IER |= TIM4_IT_UPDATE; + + // clear interrupt pending bit + TIM4->SR1 &= ~TIM4_IT_UPDATE; + + // TIM4 enable + TIM4->CR1 |= TIM4_CR1_CEN; +} diff --git a/src/firmware/tsdz2/timers.h b/code/firmware/src/tsdz2/timers.h similarity index 86% rename from src/firmware/tsdz2/timers.h rename to code/firmware/src/tsdz2/timers.h index a46fba04..0d9944fa 100644 --- a/src/firmware/tsdz2/timers.h +++ b/code/firmware/src/tsdz2/timers.h @@ -1,7 +1,7 @@ /* * bbs-fw * - * Copyright (C) Daniel Nilsson, 2022. + * Copyright (C) Daniel Nilsson, 2024. * * Released under the GPL License, Version 3 */ @@ -9,7 +9,6 @@ #ifndef _TSDZ2_TIMERS_H_ #define _TSDZ2_TIMERS_H_ - void timer1_init_motor_pwm(); void timer2_init_torque_sensor_pwm(); void timer3_init_system(); diff --git a/code/firmware/src/tsdz2/torquesensor.c b/code/firmware/src/tsdz2/torquesensor.c new file mode 100644 index 00000000..5c479450 --- /dev/null +++ b/code/firmware/src/tsdz2/torquesensor.c @@ -0,0 +1,149 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "adc.h" +#include "eventlog.h" +#include "sensors.h" +#include "system.h" +#include "tsdz2/pins.h" +#include "tsdz2/stm8.h" +#include "tsdz2/timers.h" +#include "util.h" +#include + +#include + +#define AUTO_BIAS_START_TIME_MS 2000 +#define AUTO_BIAS_DURATION_MS 3000 + +// Hard coded default torque sensor calibration table for now +// +// Torque sensor readings on different TSDZ2 differs by a lot. +// This table is therefore not perfect for every motor but +// it will have to be good enough for now. +// +// Default firmware has no way to calibrate, no idea if calibrations +// is done at factory though. +// +// Consider adding manual calibration though config tool at some point. +// Until then, sensitivity can be set using torque amplification factor, +// which works well enough even if response will potentially not be linear. + +#define TORQUE_SENSOR_LUT_SIZE 8 + +typedef struct +{ + uint8_t adc; + uint16_t nm_x100; +} torque_lut_t; +static const torque_lut_t torque_sensor_lut[TORQUE_SENSOR_LUT_SIZE] = { + // (adc value - bias), (Nm x 100) + {0, 0}, // 0kg + {30, 834}, // 5kg + {55, 1668}, // 10kg + {78, 2502}, // 15kg + {93, 3169}, // 19kg + {188, 7004}, // 42kg + {204, 8672}, // 52kg + {224, 17511} // 105kg +}; + +static uint16_t torque_adc_to_nm_x100(uint16_t torque_adc) +{ + // interpolate in lookup table + + if (torque_adc < torque_sensor_lut[0].adc) + { + // use minimum value + return torque_sensor_lut[0].nm_x100; + } + else if (torque_adc > torque_sensor_lut[TORQUE_SENSOR_LUT_SIZE - 1].adc) + { + // use maximum value + return torque_sensor_lut[TORQUE_SENSOR_LUT_SIZE - 1].nm_x100; + } + + uint8_t i = 0; + for (i = 0; i < TORQUE_SENSOR_LUT_SIZE - 1; i++) + { + if (torque_sensor_lut[i + 1].adc > torque_adc) + { + break; + } + } + + return (uint16_t)MAP32(torque_adc, torque_sensor_lut[i].adc, torque_sensor_lut[i + 1].adc, + torque_sensor_lut[i].nm_x100, torque_sensor_lut[i + 1].nm_x100); +} + +static uint16_t torque_nm_x100 = 0; + +static bool adc_bias_set = false; +static uint16_t adc_bias_steps = 0; + +void torque_sensor_init() +{ + SET_PIN_OUTPUT_OPEN_DRAIN(PIN_TORQUE_SENSOR_EXC); + + timer2_init_torque_sensor_pwm(); + + // some delay for torque sensor to power on + system_delay_ms(50); +} + +void torque_sensor_process() +{ + if (adc_bias_set) + { + uint16_t adc_val = adc_get_torque(); + if (adc_val > adc_bias_steps) + { + adc_val -= adc_bias_steps; + } + else + { + adc_val = 0; + } + + // IDEA: Find max over pedal revolution period and use sin average (0.637)? + // Doesn't seem to be needed, hw filtering seems to be very slow and should average just fine + torque_nm_x100 = torque_adc_to_nm_x100(adc_val); + } + else + { + // find torque sensor adc bias during startup, torque sensor lookup table is relative to bias + + uint32_t now = system_ms(); + if (now < (AUTO_BIAS_START_TIME_MS + AUTO_BIAS_DURATION_MS)) + { + if (now > AUTO_BIAS_START_TIME_MS) + { + uint16_t adc_val = adc_get_torque(); + if (adc_val > adc_bias_steps) + { + adc_bias_steps = adc_val; + } + } + } + else + { + adc_bias_set = true; + eventlog_write_data(EVT_DATA_TORQUE_ADC_CALIBRATED, adc_bias_steps); + } + } +} + +uint16_t torque_sensor_get_nm_x100() +{ + return torque_nm_x100; +} + +bool torque_sensor_ok() +{ + return !adc_bias_set || adc_bias_steps > 50; +} diff --git a/code/firmware/src/tsdz2/uart.c b/code/firmware/src/tsdz2/uart.c new file mode 100644 index 00000000..645c3b21 --- /dev/null +++ b/code/firmware/src/tsdz2/uart.c @@ -0,0 +1,162 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "uart.h" +#include "interrupt.h" +#include "watchdog.h" + +#include + +#define RX1_BUFFER_SIZE 64 +#define RX1_BUFFER_MASK (RX1_BUFFER_SIZE - 1) + +#define TX1_BUFFER_SIZE 32 +#define TX1_BUFFER_MASK (TX1_BUFFER_SIZE - 1) + +static volatile uint8_t rx1_head; +static volatile uint8_t rx1_tail; +static volatile uint8_t rx1_buf[RX1_BUFFER_SIZE]; +static volatile uint8_t tx1_head; +static volatile uint8_t tx1_tail; +static volatile uint8_t tx1_sending; +static volatile uint8_t tx1_buf[TX1_BUFFER_SIZE]; + +void uart_open(uint32_t baudrate) +{ + rx1_head = 0; + rx1_tail = 0; + tx1_head = 0; + tx1_tail = 0; + tx1_sending = 0; + + // enable uart2 clock + CLK->PCKENR1 |= CLK_PCKENR1_UART2; + + // default, 8bit, no parity, 1 stop bit etc + UART2->CR1 = 0x00; + UART2->CR2 = 0x00; + UART2->CR3 = 0x00; + + // clear the LSB mantissa of UART2DIV + UART2->BRR1 &= (uint8_t)(~UART2_BRR1_DIVM); + // clear the MSB mantissa of UART2DIV + UART2->BRR2 &= (uint8_t)(~UART2_BRR2_DIVM); + // clear the fraction bits of UART2DIV + UART2->BRR2 &= (uint8_t)(~UART2_BRR2_DIVF); + + // set the UART2 baudrate in BRR1 and BRR2 registers according to baudrate value + uint32_t baud_mantissa = ((uint32_t)CPU_FREQ / (baudrate << 4)); + uint32_t baud_mantissa100 = (((uint32_t)CPU_FREQ * 100) / (baudrate << 4)); + + uint8_t BRR2_1 = (uint8_t)((uint8_t)(((baud_mantissa100 - (baud_mantissa * 100)) << 4) / 100) & (uint8_t)0x0F); + uint8_t BRR2_2 = (uint8_t)((baud_mantissa >> 4) & (uint8_t)0xF0); + + UART2->BRR2 = (uint8_t)(BRR2_1 | BRR2_2); + UART2->BRR1 = (uint8_t)baud_mantissa; + + // enable rx and tx + UART2->CR2 |= UART2_CR2_TEN; + UART2->CR2 |= UART2_CR2_REN; + + // clear rx and tx interrupt flags + UART2->SR &= ~UART2_SR_RXNE; + + // enable rx interrupts + UART2->CR2 |= UART2_CR2_RIEN; +} + +void uart_close() +{ + UART2->BRR2 = 0x00; + UART2->BRR1 = 0x00; + + UART2->CR1 = 0x00; + UART2->CR2 = 0x00; + UART2->CR3 = 0x00; +} + +uint8_t uart_available() +{ + return (RX1_BUFFER_SIZE + rx1_head - rx1_tail) & RX1_BUFFER_MASK; +} + +uint8_t uart_read() +{ + uint8_t byte = rx1_buf[rx1_tail]; + rx1_tail = (rx1_tail + 1) & RX1_BUFFER_MASK; + return byte; +} + +void uart_write(uint8_t byte) +{ + if (!tx1_sending) + { + tx1_sending = 1; + UART2->DR = byte; + UART2->CR2 |= UART2_CR2_TIEN; // enable tx done interrupt + + return; + } + + uint8_t i = (tx1_head + 1) & TX1_BUFFER_MASK; + + // wait for free space in buffer + uint8_t prev_tail = tx1_tail; + while (i == tx1_tail) + { + if (tx1_tail != prev_tail) + { + prev_tail = tx1_tail; + watchdog_yeild(); + } + } + + tx1_buf[tx1_head] = byte; + tx1_head = i; +} + +void uart_flush() +{ + while (tx1_sending) + ; +} + +void isr_uart2_rx(void) __interrupt(ITC_IRQ_UART2_RX) +{ + if (UART2->SR & UART2_SR_RXNE) + { + uint8_t c = UART2->DR; + uint8_t i = (rx1_head + 1) & RX1_BUFFER_MASK; + + if (i != rx1_tail) + { + rx1_buf[rx1_head] = c; + rx1_head = i; + } + } +} + +void isr_uart2_tx(void) __interrupt(ITC_IRQ_UART2_TX) +{ + if (UART2->SR & UART2_SR_TXE) + { + if (tx1_head != tx1_tail) + { + tx1_sending = 1; + + UART2->DR = tx1_buf[tx1_tail]; + tx1_tail = (tx1_tail + 1) & TX1_BUFFER_MASK; + } + else + { + tx1_sending = 0; + // no more data clear tx empty flag + UART2->CR2 &= ~UART2_CR2_TIEN; + } + } +} diff --git a/code/firmware/src/tsdz2/watchdog.c b/code/firmware/src/tsdz2/watchdog.c new file mode 100644 index 00000000..2a273f0d --- /dev/null +++ b/code/firmware/src/tsdz2/watchdog.c @@ -0,0 +1,37 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#include "watchdog.h" +#include "tsdz2/cpu.h" + +#include + +static bool triggered; + +void watchdog_init() +{ + // :TODO: implement if possible, check if reset triggered by watchdog + triggered = false; + + IWDG->KR = 0xcc; // start + IWDG->KR = 0x55; // unlock + IWDG->PR = 6; // divide by 256 + IWDG->RLR = 156; // reload to 625 milliseconds + + watchdog_yeild(); +} + +void watchdog_yeild() +{ + IWDG->KR = 0xaa; +} + +bool watchdog_triggered() +{ + return triggered; +} diff --git a/src/firmware/uart.h b/code/firmware/src/uart.h similarity index 88% rename from src/firmware/uart.h rename to code/firmware/src/uart.h index cb9b6147..b4340cdd 100644 --- a/src/firmware/uart.h +++ b/code/firmware/src/uart.h @@ -1,7 +1,7 @@ /* * bbs-fw * - * Copyright (C) Daniel Nilsson, 2022. + * Copyright (C) Daniel Nilsson, 2024. * * Released under the GPL License, Version 3 */ diff --git a/code/firmware/src/util.h b/code/firmware/src/util.h new file mode 100644 index 00000000..aac90f23 --- /dev/null +++ b/code/firmware/src/util.h @@ -0,0 +1,33 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#ifndef _UTIL_H_ +#define _UTIL_H_ + +#include + +#define MAP16(x, in_min, in_max, out_min, out_max) \ + ((((int16_t)x) - (in_min)) * ((out_max) - (out_min)) / ((in_max) - (in_min)) + (out_min)) +#define MAP32(x, in_min, in_max, out_min, out_max) \ + ((((int32_t)x) - (in_min)) * ((out_max) - (out_min)) / ((in_max) - (in_min)) + (out_min)) + +#define EXPAND_U16(high, low) ((((uint16_t)high) << 8) | (uint8_t)low) +#define EXPAND_I16(high, low) ((int16_t)EXPAND_U16(high, low)) + +#define ABS(x) (x) < 0 ? -(x) : (x) + +#define MAX(x, y) (x) > (y) ? (x) : (y) +#define MIN(x, y) (x) < (y) ? (x) : (y) + +#define CLAMP(x, min, max) (MIN(MAX(x, min), max)) + +// Low pass filter +// value + (new_value - value) / n; +#define EXPONENTIAL_FILTER(value, new_value, n) (value) + ((new_value) - (value)) / (n) + +#endif diff --git a/code/firmware/src/version.h b/code/firmware/src/version.h new file mode 100644 index 00000000..daa3c1d1 --- /dev/null +++ b/code/firmware/src/version.h @@ -0,0 +1,26 @@ +/* + * bbs-fw + * + * Copyright (C) Daniel Nilsson, 2024. + * + * Released under the GPL License, Version 3 + */ + +#ifndef _VERSION_H_ +#define _VERSION_H_ + +#define VERSION_MAJOR 1 +#define VERSION_MINOR 5 +#define VERSION_PATCH 99 + +#if defined(BBSHD) +#define CTRL_TYPE 1 +#elif defined(BBS02) +#define CTRL_TYPE 2 +#elif defined(TSDZ2) +#define CTRL_TYPE 3 +#else +#define CTRL_TYPE 0 +#endif + +#endif diff --git a/src/firmware/watchdog.h b/code/firmware/src/watchdog.h similarity index 77% rename from src/firmware/watchdog.h rename to code/firmware/src/watchdog.h index 060b8cb2..0b3ce1b4 100644 --- a/src/firmware/watchdog.h +++ b/code/firmware/src/watchdog.h @@ -1,7 +1,7 @@ /* * bbs-fw * - * Copyright (C) Daniel Nilsson, 2022. + * Copyright (C) Daniel Nilsson, 2024. * * Released under the GPL License, Version 3 */ @@ -9,9 +9,9 @@ #ifndef _WATCHDOG_H_ #define _WATCHDOG_H_ -#include "intellisense.h" -#include +// #include "intellisense.h" #include +#include void watchdog_init(); void watchdog_yeild(); @@ -19,4 +19,3 @@ void watchdog_yeild(); bool watchdog_triggered(); #endif - diff --git a/src/logger/.gitignore b/code/logger/.gitignore similarity index 100% rename from src/logger/.gitignore rename to code/logger/.gitignore diff --git a/src/logger/.vscode/extensions.json b/code/logger/.vscode/extensions.json similarity index 100% rename from src/logger/.vscode/extensions.json rename to code/logger/.vscode/extensions.json diff --git a/code/logger/include/ComProxy.h b/code/logger/include/ComProxy.h new file mode 100644 index 00000000..ca128173 --- /dev/null +++ b/code/logger/include/ComProxy.h @@ -0,0 +1,87 @@ + +#include +#include + +#define EVT_MSG_MOTOR_INIT_OK 1 +#define EVT_MSG_CONFIG_READ 2 +#define EVT_MSG_CONFIG_RESET 3 +#define EVT_MSG_CONFIG_WRITTEN 4 + +#define EVT_ERROR_INIT_MOTOR 64 +#define EVT_ERROR_CHANGE_TARGET_SPEED 65 +#define EVT_ERROR_CHANGE_TARGET_CURRENT 66 +#define EVT_ERROR_READ_MOTOR_STATUS 67 +#define EVT_ERROR_READ_MOTOR_CURRENT 68 +#define EVT_ERROR_READ_MOTOR_VOLTAGE 69 + +#define EVT_ERROR_CONFIG_READ_EEPROM 70 +#define EVT_ERROR_CONFIG_WRITE_EEPROM 71 +#define EVT_ERROR_CONFIG_ERASE_EEPROM 72 +#define EVT_ERROR_CONFIG_VERSION 73 +#define EVT_ERROR_CONFIG_CHECKSUM 74 +#define EVT_ERROR_THROTTLE_LOW_LIMIT 75 +#define EVT_ERROR_THROTTLE_HIGH_LIMIT 76 + +#define EVT_DATA_TARGET_CURRENT 128 +#define EVT_DATA_TARGET_SPEED 129 +#define EVT_DATA_MOTOR_STATUS 130 +#define EVT_DATA_ASSIST_LEVEL 131 +#define EVT_DATA_OPERATION_MODE 132 +#define EVT_DATA_WHEEL_SPEED_PPM 133 +#define EVT_DATA_LIGHTS 134 +#define EVT_DATA_TEMPERATURE 135 +#define EVT_DATA_THERMAL_LIMITING 136 +#define EVT_DATA_SPEED_LIMITING 137 +#define EVT_DATA_MAX_CURRENT_ADC_REQUEST 138 +#define EVT_DATA_MAX_CURRENT_ADC_RESPONSE 139 +#define EVT_DATA_MAIN_LOOP_TIME 140 +#define EVT_DATA_THROTTLE_ADC 141 +#define EVT_DATA_LVC_LIMITING 142 +#define EVT_DATA_SHIFT_SENSOR 143 + +class ComProxy +{ + public: + struct Event + { + uint32_t timestamp; + uint8_t id; + int16_t data; + }; + + static void printFormat(Stream &stream, const Event &e); + + ComProxy(Stream &controller, Stream &display, Stream &log); + + bool connect(); + bool isConnected() const; + + void process(); + + bool hasLogEvent() const; + bool getLogEvent(Event &e); + + private: + void processControllerTx(); + void processDisplayTx(); + + void flushInterceptbuffer(); + bool interceptMessage(); + + int tryProcessControllerMessage(); + int processReadRequestResponse(); + int processWriteRequestResponse(); + int processEventLogMessage(); + + private: + Stream &_log; + Stream &_controller; + Stream &_display; + bool _connected; + uint8_t _msgLen; + uint8_t _msgBuf[128]; + uint32_t _lastRecv; + + bool _hasEvent; + Event _event; +}; diff --git a/src/logger/platformio.ini b/code/logger/platformio.ini similarity index 97% rename from src/logger/platformio.ini rename to code/logger/platformio.ini index e49cb778..4e21fbee 100644 --- a/src/logger/platformio.ini +++ b/code/logger/platformio.ini @@ -12,7 +12,7 @@ platform = atmelavr board = nanoatmega328 framework = arduino -lib_deps = +lib_deps = featherfly/SoftwareSerial@^1.0 paulstoffregen/AltSoftSerial@^1.4 upload_port = COM8 diff --git a/code/logger/src/ComProxy.cpp b/code/logger/src/ComProxy.cpp new file mode 100644 index 00000000..5010736e --- /dev/null +++ b/code/logger/src/ComProxy.cpp @@ -0,0 +1,446 @@ +#include "ComProxy.h" + +#define KEEP 0 +#define FAIL -1 + +#define REQUEST_TYPE_READ 0x01 +#define REQUEST_TYPE_WRITE 0x02 + +// Firmware config tool communication (only expected opcodes) +#define OPCODE_READ_FW_VERSION 0x01 +#define OPCODE_READ_EVTLOG_ENABLE 0x02 + +#define OPCODE_WRITE_EVTLOG_ENABLE 0xf0 + +#define EVENT_LOG_ENTRY 0xee +#define EVENT_LOG_DATA_ENTRY 0xed + +static uint8_t computeChecksum(uint8_t *buf, uint8_t length) +{ + uint8_t result = 0; + for (uint8_t i = 0; i < length; ++i) + { + result += buf[i]; + } + + return result; +} + +int verifyControllerMessage(uint8_t *buf, uint8_t length, uint8_t requiredLength, Stream &log) +{ + if (length < requiredLength) + { + return KEEP; + } + + uint8_t checksum = computeChecksum(buf, requiredLength - 1); + if (checksum == buf[requiredLength - 1]) + { + return requiredLength; + } + /*else + { + log.print("Checksum mismatch, computed="); + log.print(checksum, HEX); + log.print(" message="); + for (uint8_t i = 0; i < requiredLength; i++) + { + log.print(buf[i], HEX); + log.print(" "); + } + log.println(); + }*/ + + return FAIL; +} + +void ComProxy::printFormat(Stream &stream, const Event &evt) +{ + switch (evt.id) + { + case EVT_MSG_MOTOR_INIT_OK: + stream.print(F("Motor initialization successful.")); + break; + case EVT_MSG_CONFIG_READ: + stream.print(F("Successfully read configuration from eeprom.")); + break; + case EVT_MSG_CONFIG_RESET: + stream.print(F("Configuration reset performed.")); + break; + case EVT_MSG_CONFIG_WRITTEN: + stream.print(F("Configuration written to eeprom.")); + break; + case EVT_ERROR_INIT_MOTOR: + stream.print(F("Failed to perform motor controller initialization.")); + break; + case EVT_ERROR_CHANGE_TARGET_CURRENT: + stream.print(F("Failed to set motor target current on motor controller.")); + break; + case EVT_ERROR_CHANGE_TARGET_SPEED: + stream.print(F("Failed to set motor target speed on motor controller.")); + break; + case EVT_ERROR_READ_MOTOR_STATUS: + stream.print(F("Failed to read status from motor controller.")); + break; + case EVT_ERROR_READ_MOTOR_CURRENT: + stream.print(F("Failed to read current from motor controller.")); + break; + case EVT_ERROR_READ_MOTOR_VOLTAGE: + stream.print(F("Failed to read voltage from motor controller.")); + break; + case EVT_ERROR_CONFIG_READ_EEPROM: + stream.print(F("Failed to read config from eeprom.")); + break; + case EVT_ERROR_CONFIG_WRITE_EEPROM: + stream.print(F("Failed to write config to eeprom.")); + break; + case EVT_ERROR_CONFIG_ERASE_EEPROM: + stream.print(F("Failed to erase eeprom before writing config.")); + break; + case EVT_ERROR_CONFIG_VERSION: + stream.print(F("Configuration read from eeprom is of the wrong version.")); + break; + case EVT_ERROR_CONFIG_CHECKSUM: + stream.print(F("Failed to verify checksum on configuration read from eeprom.")); + break; + case EVT_ERROR_THROTTLE_LOW_LIMIT: + stream.print(F("Invalid throttle reading, below low limit, check throttle.")); + break; + case EVT_ERROR_THROTTLE_HIGH_LIMIT: + stream.print(F("Invalid throttle reading, above high limit, check throttle.")); + break; + + case EVT_DATA_TARGET_CURRENT: + stream.print(F("Motor target current changed to ")); + stream.print(evt.data); + stream.print(F("%")); + break; + case EVT_DATA_TARGET_SPEED: + stream.print(F("Motor target speed changed to ")); + stream.print((evt.data * 100) / 255); + stream.print(F("%.")); + break; + case EVT_DATA_MOTOR_STATUS: + stream.print(F("Motor controller status changed to ")); + stream.print(evt.data, HEX); + stream.print(F(".")); + break; + case EVT_DATA_ASSIST_LEVEL: + stream.print(F("Assist level changed to ")); + stream.print(evt.data); + stream.print(F(".")); + break; + case EVT_DATA_OPERATION_MODE: + stream.print(F("Operation mode changed to ")); + stream.print(evt.data); + stream.print(F(".")); + break; + case EVT_DATA_WHEEL_SPEED_PPM: + stream.print(F("Max wheel speed changed to ")); + stream.print(evt.data); + stream.print(F(" rpm.")); + break; + case EVT_DATA_LIGHTS: + stream.print(F("Lights status changed to ")); + stream.print(evt.data); + stream.print(F(".")); + break; + case EVT_DATA_TEMPERATURE: + stream.print(F("Motor controller temperature changed to ")); + stream.print(evt.data); + stream.print(F("C.")); + break; + case EVT_DATA_THERMAL_LIMITING: + if (evt.data != 0) + { + stream.print(F("Thermal limit reached, power reduced to 50%.")); + } + else + { + stream.print(F("Thermal limiting removed.")); + } + break; + case EVT_DATA_SPEED_LIMITING: + if (evt.data != 0) + { + stream.print(F("Speed limiting activated.")); + } + else + { + stream.print(F("Speed limiting deactivated.")); + } + break; + case EVT_DATA_MAX_CURRENT_ADC_REQUEST: + stream.print(F("Requesting to configure max current on motor controller mcu, adc=")); + stream.print(evt.data); + stream.print("."); + break; + case EVT_DATA_MAX_CURRENT_ADC_RESPONSE: + stream.print(F("Max current configured on motor controller mcu, response was adc=")); + stream.print(evt.data); + stream.print(F(".")); + break; + case EVT_DATA_MAIN_LOOP_TIME: + stream.print(F("Main loop, interval=")); + stream.print(evt.data); + stream.print(F("ms.")); + break; + case EVT_DATA_THROTTLE_ADC: + stream.print(F("Throttle adc, value=")); + stream.print(evt.data); + stream.print(F(".")); + break; + case EVT_DATA_LVC_LIMITING: + if (evt.data != 0) + { + stream.print(F("Low voltage limiting activated, voltage=")); + stream.print(evt.data / 10.f); + stream.print(F(".")); + } + else + { + stream.print("Low voltage limiting deactivated."); + } + break; + case EVT_DATA_SHIFT_SENSOR: + if (evt.data.Value != 0) + { + stream.print("Shift sensor power ramp started."); + } + else + { + stream.print("Shift sensor power ramp ended."); + } + break; + default: + stream.print(F("Unknown entry, id=")); + stream.print(evt.id); + stream.print(F(" data=")); + stream.print(evt.data); + break; + } +} + +ComProxy::ComProxy(Stream &controller, Stream &display, Stream &log) + : _log(log), _controller(controller), _display(display), _connected(false), _msgLen(0), _lastRecv(0), + _hasEvent(false) +{ +} + +bool ComProxy::isConnected() const +{ + return _connected; +} + +bool ComProxy::connect() +{ + uint8_t buffer[4]; + + buffer[0] = REQUEST_TYPE_WRITE; + buffer[1] = OPCODE_WRITE_EVTLOG_ENABLE; + buffer[2] = 1; + buffer[3] = computeChecksum(buffer, 3); + + _controller.write(buffer, 4); + + uint32_t now = millis(); + while (!_connected && (millis() - now) < 1000) + { + processControllerTx(); + } + + return _connected; +} + +void ComProxy::process() +{ + processControllerTx(); + processDisplayTx(); +} + +bool ComProxy::hasLogEvent() const +{ + return _hasEvent; +} + +bool ComProxy::getLogEvent(Event &e) +{ + if (_hasEvent) + { + e = _event; + _hasEvent = false; + return true; + } + + return false; +} + +void ComProxy::processControllerTx() +{ + int b = -1; + while ((b = _controller.read()) != -1) + { + _lastRecv = millis(); + + _msgBuf[_msgLen++] = b; + + int res; + while (_msgLen > 0 && (res = tryProcessControllerMessage()) != KEEP) + { + if (res == FAIL) + { + _display.write(_msgBuf[0]); + if (_msgLen > 1) + { + memcpy(_msgBuf, _msgBuf + 1, _msgLen - 1); + } + _msgLen--; + continue; + } + else if (res >= 0) + { + // succesfully intercepted + _msgLen = 0; + break; + } + } + } + + if (_msgLen > 0 && millis() - _lastRecv > 20) + { + flushInterceptbuffer(); + } +} + +void ComProxy::processDisplayTx() +{ + int b = -1; + while ((b = _display.read()) != -1) + { + _controller.write((uint8_t)b); + } +} + +void ComProxy::flushInterceptbuffer() +{ + for (uint8_t i = 0; i < _msgLen; i++) + { + _display.write(_msgBuf[i]); + } + + _msgLen = 0; +} + +int ComProxy::tryProcessControllerMessage() +{ + if (_msgLen < 1) + { + return KEEP; + } + + switch (_msgBuf[0]) + { + case REQUEST_TYPE_READ: + return processReadRequestResponse(); + case REQUEST_TYPE_WRITE: + return processWriteRequestResponse(); + case EVENT_LOG_ENTRY: + case EVENT_LOG_DATA_ENTRY: + return processEventLogMessage(); + } + + return FAIL; // unknown message, forward to display +} + +int ComProxy::processReadRequestResponse() +{ + if (_msgLen < 2) + { + return KEEP; + } + + switch (_msgBuf[1]) + { + case OPCODE_READ_FW_VERSION: + return verifyControllerMessage(_msgBuf, _msgLen, 7, _log); + case OPCODE_READ_EVTLOG_ENABLE: + return verifyControllerMessage(_msgBuf, _msgLen, 4, _log); + } + + return FAIL; +} + +int ComProxy::processWriteRequestResponse() +{ + if (_msgLen < 2) + { + return KEEP; + } + + switch (_msgBuf[1]) + { + case OPCODE_WRITE_EVTLOG_ENABLE: + { + if (_msgLen < 4) + { + return KEEP; + } + + int res = verifyControllerMessage(_msgBuf, _msgLen, 4, _log); + if (res > 0) + { + _connected = _msgBuf[2] != 0; + } + + return res; + } + }; + + return FAIL; +} + +int ComProxy::processEventLogMessage() +{ + if (_msgBuf[0] == EVENT_LOG_ENTRY) + { + const int MessageSize = 3; + + if (_msgLen < MessageSize) + { + return KEEP; + } + + int res = verifyControllerMessage(_msgBuf, _msgLen, MessageSize, _log); + if (res > 0) + { + _hasEvent = true; + _event.timestamp = millis(); + _event.id = _msgBuf[1]; + _event.data = 0; + } + + return res; + } + else if (_msgBuf[0] == EVENT_LOG_DATA_ENTRY) + { + const int MessageSize = 5; + + if (_msgLen < MessageSize) + { + return KEEP; + } + + int res = verifyControllerMessage(_msgBuf, _msgLen, MessageSize, _log); + if (res > 0) + { + _hasEvent = true; + _event.timestamp = millis(); + _event.id = _msgBuf[1]; + _event.data = _msgBuf[2] << 8 | _msgBuf[3]; + } + + return res; + } + + return FAIL; +} diff --git a/src/logger/src/Main.cpp b/code/logger/src/Main.cpp similarity index 86% rename from src/logger/src/Main.cpp rename to code/logger/src/Main.cpp index 3fb0d9c7..eed7f6b3 100644 --- a/src/logger/src/Main.cpp +++ b/code/logger/src/Main.cpp @@ -1,5 +1,5 @@ -#include #include > +#include #include "ComProxy.h" @@ -11,8 +11,8 @@ // to compensate for the lack of HW ports (should have used another MCU). // // This causes a few problems since the entire CPu -// is stalled when doing transmit. You may therefore see -// error code 30 pop up on the display birefly if a lot is +// is stalled when doing transmit. You may therefore see +// error code 30 pop up on the display birefly if a lot is // printed to the log since the response is not fast enough. SoftwareSerial logSerial(11, 12); @@ -21,7 +21,6 @@ AltSoftSerial controllerSerial(8, 9); ComProxy proxy(controllerSerial, Serial, logSerial); - void initProxy() { controllerSerial.begin(1200); @@ -43,8 +42,8 @@ void initProxy() } } -void printEvent(const ComProxy::Event& evt) -{ +void printEvent(const ComProxy::Event &evt) +{ ComProxy::printFormat(logSerial, evt); logSerial.println(); } @@ -67,9 +66,9 @@ void setup() } else { - delay(100); + delay(100); initProxy(); - } + } } void loop() @@ -77,14 +76,14 @@ void loop() if (proxy.isConnected()) { proxy.process(); - + if (proxy.hasLogEvent()) { ComProxy::Event evt; if (proxy.getLogEvent(evt)) { - printEvent(evt); - } + printEvent(evt); + } } } } diff --git a/src/tool/.gitignore b/code/tool/.gitignore similarity index 99% rename from src/tool/.gitignore rename to code/tool/.gitignore index 3a8542dc..1ee53850 100644 --- a/src/tool/.gitignore +++ b/code/tool/.gitignore @@ -359,4 +359,4 @@ MigrationBackup/ .ionide/ # Fody - auto-generated XML schema -FodyWeavers.xsd \ No newline at end of file +FodyWeavers.xsd diff --git a/src/tool/App.xaml b/code/tool/App.xaml similarity index 97% rename from src/tool/App.xaml rename to code/tool/App.xaml index b96195fe..559fe896 100644 --- a/src/tool/App.xaml +++ b/code/tool/App.xaml @@ -4,6 +4,6 @@ xmlns:local="clr-namespace:BBSFW" StartupUri="View/MainWindow.xaml"> - + diff --git a/src/tool/App.xaml.cs b/code/tool/App.xaml.cs similarity index 62% rename from src/tool/App.xaml.cs rename to code/tool/App.xaml.cs index bbf93ab7..abe58665 100644 --- a/src/tool/App.xaml.cs +++ b/code/tool/App.xaml.cs @@ -8,10 +8,10 @@ namespace BBSFW { - /// - /// Interaction logic for App.xaml - /// - public partial class App : Application - { - } +/// +/// Interaction logic for App.xaml +/// +public partial class App : Application +{ +} } diff --git a/code/tool/AssemblyInfo.cs b/code/tool/AssemblyInfo.cs new file mode 100644 index 00000000..d1b4fb04 --- /dev/null +++ b/code/tool/AssemblyInfo.cs @@ -0,0 +1,9 @@ +using System.Windows; + +[assembly:ThemeInfo(ResourceDictionaryLocation.None, // where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly // where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) + )] diff --git a/code/tool/Model/BbsfwConnection.cs b/code/tool/Model/BbsfwConnection.cs new file mode 100644 index 00000000..e1698b14 --- /dev/null +++ b/code/tool/Model/BbsfwConnection.cs @@ -0,0 +1,636 @@ +using System; +using System.Collections.Generic; +using System.IO.Ports; +using System.Linq; +using System.Management; +using System.Threading; +using System.Threading.Tasks; + +namespace BBSFW.Model +{ + +public class ComPort +{ + public string Name { get; private set; } + + public string Description { get; private set; } + + public ComPort(string name, string description) + { + Name = name; + Description = description; + } +} + +public class BbsfwConnection +{ + public enum Controller + { + Unknown = 0, + BBSHD = 1, + BBS02 = 2, + TSDZ2 = 3 + } + + private const int REQUEST_TYPE_READ = 0x01; + private const int REQUEST_TYPE_WRITE = 0x02; + + private const int RESPONSE_TYPE_READ = 0x01; + private const int RESPONSE_TYPE_WRITE = 0x02; + + private const int EVENT_LOG_ENTRY = 0xee; + private const int EVENT_LOG_DATA_ENTRY = 0xed; + + private const int OPCODE_READ_FW_VERSION = 0x01; + private const int OPCODE_READ_EVTLOG_ENABLE = 0x02; + private const int OPCODE_READ_CONFIG = 0x03; + + private const int OPCODE_WRITE_EVTLOG_ENABLE = 0xf0; + private const int OPCODE_WRITE_CONFIG = 0xf1; + private const int OPCODE_WRITE_RESET_CONFIG = 0xf2; + private const int OPCODE_WRITE_ADC_VOLTAGE_CALIBRATION = 0xf3; + + private const int Keep = 0; + private const int Discard = -1; + + private SerialPort _port = null; + private volatile bool _isConnecting = false; + private volatile bool _isConnected = false; + private Controller _controllerType = Controller.Unknown; + + private DateTime _lastRecv = DateTime.Now; + private List _rxBuffer = new List(); + + private CompletionQueue _readConfigCq = new CompletionQueue(); + private CompletionQueue _writeConfigCq = new CompletionQueue(); + private CompletionQueue _writeResetConfigCq = new CompletionQueue(); + private CompletionQueue _writeVoltageCalibrationCq = new CompletionQueue(); + + private int ConfigVersion = 0; + + public bool IsConnected + { + get { + return _isConnected; + } + } + + public Controller ControllerType + { + get { + return _controllerType; + } + } + + public event Action Connected; + public event Action Disconnected; + + public event Action EventLog; + + public static List GetComPorts() + { + var result = new List(); + + using (var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%COM%'")) + { + var portNames = SerialPort.GetPortNames(); + var ports = searcher.Get().Cast().ToList(); + + foreach (var name in portNames) + { + var port = ports.FirstOrDefault(p => p["Name"].ToString().ToUpper().Contains(name.ToUpper())); + if (port != null) + { + result.Add(new ComPort(name, port["Caption"].ToString())); + } + else + { + result.Add(new ComPort(name, name)); + } + } + } + + return result; + } + + public async Task Connect(ComPort port, TimeSpan timeout) + { + _controllerType = Controller.Unknown; + _isConnected = false; + _isConnecting = true; + _port = new SerialPort(port.Name, 1200); + _port.DataReceived += OnDataReceived; + _port.Open(); + + var connected = await Task.Run(() => SetupConnection(timeout)); + if (!connected) + { + Close(); + } + + return connected; + } + + public void Close() + { + if (_port != null) + { + _isConnected = false; + _isConnecting = false; + + _port.Close(); + _port.DataReceived -= OnDataReceived; + _port = null; + + lock (_rxBuffer) + { + _rxBuffer.Clear(); + } + + Disconnected?.Invoke(); + } + } + + public async Task> ReadConfiguration(TimeSpan timeout) + { + SendReadRequest(OPCODE_READ_CONFIG); + return await _readConfigCq.WaitResponse(timeout); + } + + public async Task> WriteConfiguration(Configuration configuration, TimeSpan timeout) + { + SendWriteConfigRequest(configuration); + return await _writeConfigCq.WaitResponse(timeout); + } + + public async Task> ResetConfiguration(TimeSpan timeout) + { + SendWriteResetConfigRequest(); + return await _writeResetConfigCq.WaitResponse(timeout); + } + + public async Task> CalibrateBatteryVoltage(float actualVolts, TimeSpan timeout) + { + SendWriteVoltageCalibration(actualVolts); + return await _writeVoltageCalibrationCq.WaitResponse(timeout); + } + + private void OnDataReceived(object sender, SerialDataReceivedEventArgs e) + { + // check for communication error and reset + if (_rxBuffer.Any() && DateTime.Now - _lastRecv > TimeSpan.FromMilliseconds(1000)) + { + _rxBuffer.Clear(); + } + + bool close = false; + lock (_rxBuffer) + { + while (_port.BytesToRead > 0) + { + _lastRecv = DateTime.Now; + + var b = _port.ReadByte(); + if (b == -1) + { + close = true; + break; + } + else + { + _rxBuffer.Add((byte)b); + } + } + } + + if (close) + { + Close(); + } + else + { + ProcessInputBuffer(); + } + } + + private void ProcessInputBuffer() + { + lock (_rxBuffer) + { + while (true) + { + var result = ProcessMessage(); + if (result == Discard) + { + System.Diagnostics.Debug.WriteLine("Discarding: " + + BitConverter.ToString(_rxBuffer.ToArray()).Replace("-", " ")); + _rxBuffer.Clear(); + } + else if (result > 0) + { + if (_rxBuffer.Count > result) + { + _rxBuffer.RemoveRange(0, result); + } + else + { + _rxBuffer.Clear(); + } + } + else + { + // no data, done + break; + } + } + } + } + + private int ProcessMessage() + { + if (_rxBuffer.Count < 1) + { + return 0; + } + + switch (_rxBuffer[0]) + { + case RESPONSE_TYPE_READ: + return ProcessReadResponse(); + case RESPONSE_TYPE_WRITE: + return ProcessWriteResponse(); + case EVENT_LOG_ENTRY: + case EVENT_LOG_DATA_ENTRY: + return ProcessEventLogEntry(); + } + + return Discard; + } + + private int ProcessReadResponse() + { + if (_rxBuffer.Count < 2) + { + return 0; + } + + switch (_rxBuffer[1]) + { + case OPCODE_READ_FW_VERSION: + return ProcessReadResponseFwVersion(); + case OPCODE_READ_EVTLOG_ENABLE: + return ProcessReadResponseEvtlogEnable(); + case OPCODE_READ_CONFIG: + return ProcessReadResponseConfig(); + } + + return -1; + } + + private int ProcessReadResponseFwVersion() + { + const int MessageSizeV1 = 7; + const int MessageSizeV2 = 8; + + if (_rxBuffer.Count < MessageSizeV1) + { + return Keep; + } + + int size = MessageSizeV1; + + int major = _rxBuffer[2]; + int minor = _rxBuffer[3]; + int patch = _rxBuffer[4]; + + if (major > 1 || minor > 0) + { + // Controller model field added in firmware version 1.1 + // Keep backwards compatibility + if (_rxBuffer.Count < MessageSizeV2) + { + return Keep; + } + + size = MessageSizeV2; + } + + if (ComputeChecksum(_rxBuffer, size - 1) == _rxBuffer[size - 1]) + { + ConfigVersion = _rxBuffer[5]; + + if (_isConnecting) + { + _isConnecting = false; + _isConnected = true; + _controllerType = (size == MessageSizeV1 ? Controller.BBSHD : (Controller)_rxBuffer[6]); + + Connected?.Invoke(ControllerType, String.Format("{0}.{1}.{2}", major, minor, patch), ConfigVersion); + ; + + SendEventLogEnableRequest(true); + } + } + + return size; + } + + private int ProcessReadResponseEvtlogEnable() + { + const int MessageSize = 4; + + if (_rxBuffer.Count < MessageSize) + { + return Keep; + } + + // not used + + return 4; + } + + private int ProcessReadResponseConfig() + { + int version; + + if (_rxBuffer.Count > 3) + { + version = _rxBuffer[2]; + var size = _rxBuffer[3]; + + if (version < Configuration.MinVersion || version > Configuration.MaxVersion || + size != Configuration.GetByteSize(version)) + { + System.Diagnostics.Debug.WriteLine( + "Config read from flash is of an unsupported version or is corrupt, discarding."); + return Discard; + } + } + else + { + return Keep; + } + + int MessageSize = (4 + Configuration.GetByteSize(version) + 1); + + if (_rxBuffer.Count < MessageSize) + { + return Keep; + } + + if (ComputeChecksum(_rxBuffer, MessageSize - 1) == _rxBuffer[MessageSize - 1]) + { + var cfg = new Configuration(ControllerType); + + switch (version) + { + case 1: + cfg.ParseFromBufferV1(_rxBuffer.Skip(4).Take(Configuration.GetByteSize(version)).ToArray()); + break; + case 2: + cfg.ParseFromBufferV2(_rxBuffer.Skip(4).Take(Configuration.GetByteSize(version)).ToArray()); + break; + case 3: + cfg.ParseFromBufferV3(_rxBuffer.Skip(4).Take(Configuration.GetByteSize(version)).ToArray()); + break; + case 4: + cfg.ParseFromBufferV4(_rxBuffer.Skip(4).Take(Configuration.GetByteSize(version)).ToArray()); + break; + case 5: + cfg.ParseFromBufferV5(_rxBuffer.Skip(4).Take(Configuration.GetByteSize(version)).ToArray()); + break; + } + + _readConfigCq.Complete(cfg); + } + else + { + System.Diagnostics.Debug.WriteLine("Config read from flash has mismatching checksum, discarding."); + } + + return MessageSize; + } + + private int ProcessWriteResponse() + { + if (_rxBuffer.Count < 2) + { + return Keep; + } + + switch (_rxBuffer[1]) + { + case OPCODE_WRITE_EVTLOG_ENABLE: + return ProcessWriteResponseEvtlogEnable(); + case OPCODE_WRITE_CONFIG: + return ProcessWriteResponseConfig(); + case OPCODE_WRITE_RESET_CONFIG: + return ProcessWriteResponseResetConfig(); + case OPCODE_WRITE_ADC_VOLTAGE_CALIBRATION: + return ProcessWriteResponseVoltageCalibration(); + } + + return Discard; + } + + private int ProcessWriteResponseEvtlogEnable() + { + const int MessageSize = 4; + + if (_rxBuffer.Count < MessageSize) + { + return Keep; + } + + // don't care + + return MessageSize; + } + + private int ProcessWriteResponseConfig() + { + const int MessageSize = 4; + + if (_rxBuffer.Count < MessageSize) + { + return Keep; + } + + _writeConfigCq.Complete(_rxBuffer[2] != 0); + + return MessageSize; + } + + private int ProcessWriteResponseResetConfig() + { + const int MessageSize = 4; + + if (_rxBuffer.Count < MessageSize) + { + return Keep; + } + + _writeResetConfigCq.Complete(_rxBuffer[2] != 0); + + return MessageSize; + } + + private int ProcessWriteResponseVoltageCalibration() + { + const int MessageSize = 5; + + if (_rxBuffer.Count < MessageSize) + { + return Keep; + } + + _writeVoltageCalibrationCq.Complete(true); + + return MessageSize; + } + + private int ProcessEventLogEntry() + { + if (_rxBuffer[0] == EVENT_LOG_ENTRY) + { + const int MessageSize = 3; + + if (_rxBuffer.Count < MessageSize) + { + return Keep; + } + + if (ComputeChecksum(_rxBuffer, MessageSize - 1) == _rxBuffer[MessageSize - 1]) + { + EventLog?.Invoke(new EventLogEntry(_rxBuffer[1], null)); + return MessageSize; + } + else + { + Console.WriteLine("Event log cheksum missmatch. Discarding."); + return Discard; + } + } + else if (_rxBuffer[0] == EVENT_LOG_DATA_ENTRY) + { + const int MessageSize = 5; + + if (_rxBuffer.Count < MessageSize) + { + return Keep; + } + + if (ComputeChecksum(_rxBuffer, MessageSize - 1) == _rxBuffer[MessageSize - 1]) + { + int data = _rxBuffer[2] << 8 | _rxBuffer[3]; + EventLog?.Invoke(new EventLogEntry(_rxBuffer[1], data)); + + return MessageSize; + } + else + { + Console.WriteLine("Event log cheksum missmatch. Discarding."); + return Discard; + } + } + + return Discard; + } + + private void SendReadRequest(byte opcode) + { + var buf = new List(); + buf.Add(REQUEST_TYPE_READ); + buf.Add(opcode); + buf.Add(ComputeChecksum(buf, buf.Count)); + + _port.Write(buf.ToArray(), 0, buf.Count); + } + + private void SendEventLogEnableRequest(bool enable) + { + var buf = new List(); + buf.Add(REQUEST_TYPE_WRITE); + buf.Add(OPCODE_WRITE_EVTLOG_ENABLE); + buf.Add((byte)(enable ? 1 : 0)); + buf.Add(ComputeChecksum(buf, buf.Count)); + + _port.Write(buf.ToArray(), 0, buf.Count); + } + + private void SendWriteConfigRequest(Configuration config) + { + if (Configuration.CurrentVersion != ConfigVersion) + { + throw new InvalidOperationException("Unsupported config version."); + } + + var cfgarr = config.WriteToBuffer(); + + var buf = new List(); + buf.Add(REQUEST_TYPE_WRITE); + buf.Add(OPCODE_WRITE_CONFIG); + buf.Add((byte)Configuration.CurrentVersion); + buf.Add((byte)cfgarr.Length); + buf.AddRange(cfgarr); + buf.Add(ComputeChecksum(buf, buf.Count)); + + _port.Write(buf.ToArray(), 0, buf.Count); + } + + private void SendWriteResetConfigRequest() + { + var buf = new List(); + buf.Add(REQUEST_TYPE_WRITE); + buf.Add(OPCODE_WRITE_RESET_CONFIG); + buf.Add(ComputeChecksum(buf, buf.Count)); + + _port.Write(buf.ToArray(), 0, buf.Count); + } + + private void SendWriteVoltageCalibration(float volts) + { + uint volts_x100 = (uint)(volts * 100); + + var buf = new List(); + buf.Add(REQUEST_TYPE_WRITE); + buf.Add(OPCODE_WRITE_ADC_VOLTAGE_CALIBRATION); + buf.Add((byte)(volts_x100 >> 8)); + buf.Add((byte)volts_x100); + buf.Add(ComputeChecksum(buf, buf.Count)); + + _port.Write(buf.ToArray(), 0, buf.Count); + } + + private bool SetupConnection(TimeSpan timeout) + { + var start = DateTime.Now; + while (_isConnecting && !_isConnected) + { + if (DateTime.Now - start > timeout) + { + return false; + } + + SendReadRequest(OPCODE_READ_FW_VERSION); + Thread.Sleep(200); + } + + return true; + } + + private static byte ComputeChecksum(List buffer, int length) + { + unchecked + { + byte result = 0; + for (int i = 0; i < length; i++) + { + result += buffer[i]; + } + + return result; + } + } +} +} diff --git a/code/tool/Model/CompletionQueue.cs b/code/tool/Model/CompletionQueue.cs new file mode 100644 index 00000000..eed850db --- /dev/null +++ b/code/tool/Model/CompletionQueue.cs @@ -0,0 +1,57 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace BBSFW.Model +{ + +public class RequestResult +{ + public bool Timeout { get; private set; } + + public T Result { get; private set; } + + public RequestResult(bool timeout, T result) + { + Timeout = timeout; + Result = result; + } +} + +public class CompletionQueue +{ + private TaskCompletionSource _tcs = null; + + public void Complete(T response) + { + _tcs?.SetResult(response); + } + + public async Task> WaitResponse(TimeSpan timeout) + { + Reset(timeout); + + try + { + var res = await _tcs.Task; + _tcs = null; + return new RequestResult(false, res); + } + catch (TaskCanceledException) + { + _tcs = null; + return new RequestResult(true, default(T)); + } + } + + private void Reset(TimeSpan timeout) + { + var tcs = new TaskCompletionSource(); + + var cancelTokenSrc = new CancellationTokenSource((int)timeout.TotalMilliseconds); + cancelTokenSrc.Token.Register(() => tcs.TrySetCanceled()); + + _tcs = tcs; + } +} +} diff --git a/code/tool/Model/Configuration.cs b/code/tool/Model/Configuration.cs new file mode 100644 index 00000000..2b750a22 --- /dev/null +++ b/code/tool/Model/Configuration.cs @@ -0,0 +1,864 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using System.Xml.Serialization; + +namespace BBSFW.Model +{ + +[XmlRoot("BBSFW", Namespace = "https://github.com/danielnilsson9/bbs-fw")] +public class Configuration +{ + public const int CurrentVersion = 5; + public const int MinVersion = 1; + public const int MaxVersion = CurrentVersion; + + public const int ByteSizeV1 = 120; + public const int ByteSizeV2 = 124; + public const int ByteSizeV3 = 149; + public const int ByteSizeV4 = 152; + public const int ByteSizeV5 = 154; + + public enum Feature + { + ShiftSensor, + TorqueSensor, + ControllerTemperatureSensor, + MotorTemperatureSensor + } + + public static int GetByteSize(int version) + { + switch (version) + { + case 1: + return ByteSizeV1; + case 2: + return ByteSizeV2; + case 3: + return ByteSizeV3; + case 4: + return ByteSizeV4; + case 5: + return ByteSizeV5; + } + + return 0; + } + + public enum AssistModeSelect + { + Off = 0, + Standard = 1, + Lights = 2, + Pas0AndLights = 3, + Pas1AndLights = 4, + Pas2AndLights = 5, + Pas3AndLights = 6, + Pas4AndLights = 7, + Pas5AndLights = 8, + Pas6AndLights = 9, + Pas7AndLights = 10, + Pas8AndLights = 11, + Pas9AndLights = 12, + BrakesOnBoot = 13 + } + + [Flags] + public enum AssistFlagsType : byte + { + None = 0x00, + Pas = 0x01, + Throttle = 0x02, + Cruise = 0x04, + + PasVariable = 0x08, + PasTorque = 0x10, + CadenceOverride = 0x20, + SpeedOverride = 0x40 + } + ; + + public enum ThrottleGlobalSpeedLimitOptions + { + Disabled = 0x00, + Enabled = 0x01, + StandardLevels = 0x02 + } + + public enum TemperatureSensor + { + Disabled = 0x00, + Controller = 0x01, + Motor = 0x02, + All = 0x03 + } + + public enum WalkModeData + { + Speed = 0, + Temperature = 1, + RequestedPower = 2, + BatteryPercent = 3 + } + + public enum LightsModeOptions + { + Default = 0, + Disabled = 1, + AlwaysOn = 2, + BrakeLight = 3 + } + + public class AssistLevel + { + [XmlAttribute] + public AssistFlagsType Type; + + [XmlAttribute] + public uint MaxCurrentPercent; + + [XmlAttribute] + public uint MaxThrottlePercent; + + [XmlAttribute] + public uint MaxCadencePercent; + + [XmlAttribute] + public uint MaxSpeedPercent; + + [XmlAttribute] + public float TorqueAmplificationFactor; + } + + [XmlIgnore] + public BbsfwConnection.Controller Target { get; private set; } + + public uint MaxCurrentLimitAmps + { + get { + switch (Target) + { + case BbsfwConnection.Controller.BBSHD: + return 33; + case BbsfwConnection.Controller.BBS02: + return 30; + case BbsfwConnection.Controller.TSDZ2: + return 20; + } + + return 50; + } + } + + // hmi + [XmlIgnore] + public bool UseFreedomUnits; + + // global + public uint MaxCurrentAmps; + public uint CurrentRampAmpsSecond; + public float MaxBatteryVolts; + public uint LowCutoffVolts; + public uint MaxSpeedKph; + + // externals + public bool UseSpeedSensor; + public bool UseShiftSensor; + public bool UsePushWalk; + public bool UsePretension; + public uint PretensionSpeedCutoffKph; + public TemperatureSensor UseTemperatureSensor; + + // lights + public LightsModeOptions LightsMode; + + // speed sensor + public float WheelSizeInch; + public uint NumWheelSensorSignals; + + // pas options + public uint PasStartDelayPulses; + public uint PasStopDelayMilliseconds; + public uint PasKeepCurrentPercent; + public uint PasKeepCurrentCadenceRpm; + + // throttle options + public uint ThrottleStartMillivolts; + public uint ThrottleEndMillivolts; + public uint ThrottleStartPercent; + public ThrottleGlobalSpeedLimitOptions ThrottleGlobalSpeedLimit; + public uint ThrottleGlobalSpeedLimitPercent; + + // shift interrupt options + public uint ShiftInterruptDuration; + public uint ShiftInterruptCurrentThresholdPercent; + + // misc + public WalkModeData WalkModeDataDisplay; + + // assists options + public AssistModeSelect AssistModeSelection; + public uint AssistStartupLevel; + + public AssistLevel[] StandardAssistLevels = new AssistLevel[10]; + public AssistLevel[] SportAssistLevels = new AssistLevel[10]; + + public Configuration() : this(BbsfwConnection.Controller.Unknown) + { + } + + public Configuration(BbsfwConnection.Controller target) + { + Target = target; + + UseFreedomUnits = Properties.Settings.Default.UseFreedomUnits; + MaxCurrentAmps = 0; + CurrentRampAmpsSecond = 0; + MaxBatteryVolts = 0; + LowCutoffVolts = 0; + + UseSpeedSensor = false; + UseShiftSensor = false; + UsePushWalk = false; + UsePretension = false; + PretensionSpeedCutoffKph = 0; + UseTemperatureSensor = TemperatureSensor.All; + + LightsMode = LightsModeOptions.Default; + + WheelSizeInch = 0; + NumWheelSensorSignals = 0; + MaxSpeedKph = 0; + + PasStartDelayPulses = 0; + PasStopDelayMilliseconds = 0; + PasKeepCurrentPercent = 0; + PasKeepCurrentCadenceRpm = 0; + + ThrottleStartMillivolts = 0; + ThrottleEndMillivolts = 0; + ThrottleStartPercent = 0; + ThrottleGlobalSpeedLimit = ThrottleGlobalSpeedLimitOptions.Disabled; + ThrottleGlobalSpeedLimitPercent = 0; + + ShiftInterruptDuration = 0; + ShiftInterruptCurrentThresholdPercent = 0; + + WalkModeDataDisplay = WalkModeData.Speed; + + AssistModeSelection = AssistModeSelect.Off; + AssistStartupLevel = 0; + + for (int i = 0; i < StandardAssistLevels.Length; ++i) + { + StandardAssistLevels[i] = new AssistLevel(); + } + + for (int i = 0; i < SportAssistLevels.Length; ++i) + { + SportAssistLevels[i] = new AssistLevel(); + } + } + + public bool IsFeatureSupported(Feature feature) + { + if (Target == BbsfwConnection.Controller.Unknown) + { + return true; + } + + switch (feature) + { + case Feature.ShiftSensor: + return new[] { BbsfwConnection.Controller.BBSHD, BbsfwConnection.Controller.BBS02 }.Contains(Target); + case Feature.TorqueSensor: + return new[] { BbsfwConnection.Controller.TSDZ2 }.Contains(Target); + case Feature.ControllerTemperatureSensor: + return new[] { BbsfwConnection.Controller.BBSHD, BbsfwConnection.Controller.BBS02 }.Contains(Target); + case Feature.MotorTemperatureSensor: + return new[] { BbsfwConnection.Controller.BBSHD }.Contains(Target); + } + + return false; + } + + public bool ParseFromBufferV1(byte[] buffer) + { + if (buffer.Length != ByteSizeV1) + { + return false; + } + + using (var s = new MemoryStream(buffer)) + { + var br = new BinaryReader(s); + + UseFreedomUnits = br.ReadBoolean(); + + MaxCurrentAmps = br.ReadByte(); + CurrentRampAmpsSecond = br.ReadByte(); + LowCutoffVolts = br.ReadByte(); + MaxSpeedKph = br.ReadByte(); + + UseSpeedSensor = br.ReadBoolean(); + /* UseDisplay = */ br.ReadBoolean(); + UsePushWalk = br.ReadBoolean(); + + WheelSizeInch = br.ReadUInt16() / 10f; + NumWheelSensorSignals = br.ReadByte(); + + PasStartDelayPulses = br.ReadByte(); + PasStopDelayMilliseconds = br.ReadByte() * 10u; + + ThrottleStartMillivolts = br.ReadUInt16(); + ThrottleEndMillivolts = br.ReadUInt16(); + ThrottleStartPercent = br.ReadByte(); + + AssistModeSelection = (AssistModeSelect)br.ReadByte(); + AssistStartupLevel = br.ReadByte(); + + for (int i = 0; i < StandardAssistLevels.Length; ++i) + { + StandardAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); + StandardAssistLevels[i].MaxCurrentPercent = br.ReadByte(); + StandardAssistLevels[i].MaxThrottlePercent = br.ReadByte(); + StandardAssistLevels[i].MaxCadencePercent = br.ReadByte(); + StandardAssistLevels[i].MaxSpeedPercent = br.ReadByte(); + } + + for (int i = 0; i < SportAssistLevels.Length; ++i) + { + SportAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); + SportAssistLevels[i].MaxCurrentPercent = br.ReadByte(); + SportAssistLevels[i].MaxThrottlePercent = br.ReadByte(); + SportAssistLevels[i].MaxCadencePercent = br.ReadByte(); + SportAssistLevels[i].MaxSpeedPercent = br.ReadByte(); + } + } + + // apply default settings for non existing options in version + MaxBatteryVolts = 0f; + UseTemperatureSensor = TemperatureSensor.All; + WalkModeDataDisplay = WalkModeData.Speed; + PasKeepCurrentPercent = 100; + PasKeepCurrentCadenceRpm = 255; + UseShiftSensor = true; + ShiftInterruptDuration = 600; + ShiftInterruptCurrentThresholdPercent = 10; + LightsMode = LightsModeOptions.Default; + UsePretension = false; + PretensionSpeedCutoffKph = 16; + ThrottleGlobalSpeedLimit = ThrottleGlobalSpeedLimitOptions.Disabled; + ThrottleGlobalSpeedLimitPercent = 100; + UsePretension = false; + PretensionSpeedCutoffKph = 0; + + return true; + } + + public bool ParseFromBufferV2(byte[] buffer) + { + if (buffer.Length != ByteSizeV2) + { + return false; + } + + using (var s = new MemoryStream(buffer)) + { + var br = new BinaryReader(s); + + UseFreedomUnits = br.ReadBoolean(); + + MaxCurrentAmps = br.ReadByte(); + CurrentRampAmpsSecond = br.ReadByte(); + MaxBatteryVolts = br.ReadUInt16() / 100f; + LowCutoffVolts = br.ReadByte(); + MaxSpeedKph = br.ReadByte(); + + UseSpeedSensor = br.ReadBoolean(); + /* UseDisplay = */ br.ReadBoolean(); + UsePushWalk = br.ReadBoolean(); + UseTemperatureSensor = (TemperatureSensor)br.ReadByte(); + + WheelSizeInch = br.ReadUInt16() / 10f; + NumWheelSensorSignals = br.ReadByte(); + + PasStartDelayPulses = br.ReadByte(); + PasStopDelayMilliseconds = br.ReadByte() * 10u; + PasKeepCurrentCadenceRpm = 255; + PasKeepCurrentPercent = 100; + + ThrottleStartMillivolts = br.ReadUInt16(); + ThrottleEndMillivolts = br.ReadUInt16(); + ThrottleStartPercent = br.ReadByte(); + + WalkModeDataDisplay = (WalkModeData)br.ReadByte(); + + AssistModeSelection = (AssistModeSelect)br.ReadByte(); + AssistStartupLevel = br.ReadByte(); + + for (int i = 0; i < StandardAssistLevels.Length; ++i) + { + StandardAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); + StandardAssistLevels[i].MaxCurrentPercent = br.ReadByte(); + StandardAssistLevels[i].MaxThrottlePercent = br.ReadByte(); + StandardAssistLevels[i].MaxCadencePercent = br.ReadByte(); + StandardAssistLevels[i].MaxSpeedPercent = br.ReadByte(); + } + + for (int i = 0; i < SportAssistLevels.Length; ++i) + { + SportAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); + SportAssistLevels[i].MaxCurrentPercent = br.ReadByte(); + SportAssistLevels[i].MaxThrottlePercent = br.ReadByte(); + SportAssistLevels[i].MaxCadencePercent = br.ReadByte(); + SportAssistLevels[i].MaxSpeedPercent = br.ReadByte(); + } + } + + // apply default settings for non existing options in version + PasKeepCurrentPercent = 100; + PasKeepCurrentCadenceRpm = 255; + UseShiftSensor = true; + ShiftInterruptDuration = 600; + ShiftInterruptCurrentThresholdPercent = 10; + LightsMode = LightsModeOptions.Default; + UsePretension = false; + PretensionSpeedCutoffKph = 16; + ThrottleGlobalSpeedLimit = ThrottleGlobalSpeedLimitOptions.Disabled; + ThrottleGlobalSpeedLimitPercent = 100; + UsePretension = false; + PretensionSpeedCutoffKph = 0; + + return true; + } + + public bool ParseFromBufferV3(byte[] buffer) + { + if (buffer.Length != ByteSizeV3) + { + return false; + } + + using (var s = new MemoryStream(buffer)) + { + var br = new BinaryReader(s); + + UseFreedomUnits = br.ReadBoolean(); + + MaxCurrentAmps = br.ReadByte(); + CurrentRampAmpsSecond = br.ReadByte(); + MaxBatteryVolts = br.ReadUInt16() / 100f; + LowCutoffVolts = br.ReadByte(); + MaxSpeedKph = br.ReadByte(); + + UseSpeedSensor = br.ReadBoolean(); + UseShiftSensor = br.ReadBoolean(); + UsePushWalk = br.ReadBoolean(); + UseTemperatureSensor = (TemperatureSensor)br.ReadByte(); + + WheelSizeInch = br.ReadUInt16() / 10f; + NumWheelSensorSignals = br.ReadByte(); + + PasStartDelayPulses = br.ReadByte(); + PasStopDelayMilliseconds = br.ReadByte() * 10u; + PasKeepCurrentPercent = br.ReadByte(); + PasKeepCurrentCadenceRpm = br.ReadByte(); + + ThrottleStartMillivolts = br.ReadUInt16(); + ThrottleEndMillivolts = br.ReadUInt16(); + ThrottleStartPercent = br.ReadByte(); + + ShiftInterruptDuration = br.ReadUInt16(); + ShiftInterruptCurrentThresholdPercent = br.ReadByte(); + + WalkModeDataDisplay = (WalkModeData)br.ReadByte(); + + AssistModeSelection = (AssistModeSelect)br.ReadByte(); + AssistStartupLevel = br.ReadByte(); + + for (int i = 0; i < StandardAssistLevels.Length; ++i) + { + StandardAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); + StandardAssistLevels[i].MaxCurrentPercent = br.ReadByte(); + StandardAssistLevels[i].MaxThrottlePercent = br.ReadByte(); + StandardAssistLevels[i].MaxCadencePercent = br.ReadByte(); + StandardAssistLevels[i].MaxSpeedPercent = br.ReadByte(); + StandardAssistLevels[i].TorqueAmplificationFactor = br.ReadByte() / 10f; + } + + for (int i = 0; i < SportAssistLevels.Length; ++i) + { + SportAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); + SportAssistLevels[i].MaxCurrentPercent = br.ReadByte(); + SportAssistLevels[i].MaxThrottlePercent = br.ReadByte(); + SportAssistLevels[i].MaxCadencePercent = br.ReadByte(); + SportAssistLevels[i].MaxSpeedPercent = br.ReadByte(); + SportAssistLevels[i].TorqueAmplificationFactor = br.ReadByte() / 10f; + } + } + + // apply default settings for non existing options in version + LightsMode = LightsModeOptions.Default; + ThrottleGlobalSpeedLimit = ThrottleGlobalSpeedLimitOptions.Disabled; + ThrottleGlobalSpeedLimitPercent = 100; + UsePretension = false; + PretensionSpeedCutoffKph = 0; + + return true; + } + + public bool ParseFromBufferV4(byte[] buffer) + { + if (buffer.Length != ByteSizeV4) + { + return false; + } + + using (var s = new MemoryStream(buffer)) + { + var br = new BinaryReader(s); + + UseFreedomUnits = br.ReadBoolean(); + + MaxCurrentAmps = br.ReadByte(); + CurrentRampAmpsSecond = br.ReadByte(); + MaxBatteryVolts = br.ReadUInt16() / 100f; + LowCutoffVolts = br.ReadByte(); + MaxSpeedKph = br.ReadByte(); + + UseSpeedSensor = br.ReadBoolean(); + UseShiftSensor = br.ReadBoolean(); + UsePushWalk = br.ReadBoolean(); + UseTemperatureSensor = (TemperatureSensor)br.ReadByte(); + LightsMode = (LightsModeOptions)br.ReadByte(); + + WheelSizeInch = br.ReadUInt16() / 10f; + NumWheelSensorSignals = br.ReadByte(); + + PasStartDelayPulses = br.ReadByte(); + PasStopDelayMilliseconds = br.ReadByte() * 10u; + PasKeepCurrentPercent = br.ReadByte(); + PasKeepCurrentCadenceRpm = br.ReadByte(); + + ThrottleStartMillivolts = br.ReadUInt16(); + ThrottleEndMillivolts = br.ReadUInt16(); + ThrottleStartPercent = br.ReadByte(); + ThrottleGlobalSpeedLimit = (ThrottleGlobalSpeedLimitOptions)br.ReadByte(); + ThrottleGlobalSpeedLimitPercent = br.ReadByte(); + + ShiftInterruptDuration = br.ReadUInt16(); + ShiftInterruptCurrentThresholdPercent = br.ReadByte(); + + WalkModeDataDisplay = (WalkModeData)br.ReadByte(); + + AssistModeSelection = (AssistModeSelect)br.ReadByte(); + AssistStartupLevel = br.ReadByte(); + + for (int i = 0; i < StandardAssistLevels.Length; ++i) + { + StandardAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); + StandardAssistLevels[i].MaxCurrentPercent = br.ReadByte(); + StandardAssistLevels[i].MaxThrottlePercent = br.ReadByte(); + StandardAssistLevels[i].MaxCadencePercent = br.ReadByte(); + StandardAssistLevels[i].MaxSpeedPercent = br.ReadByte(); + StandardAssistLevels[i].TorqueAmplificationFactor = br.ReadByte() / 10f; + } + + for (int i = 0; i < SportAssistLevels.Length; ++i) + { + SportAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); + SportAssistLevels[i].MaxCurrentPercent = br.ReadByte(); + SportAssistLevels[i].MaxThrottlePercent = br.ReadByte(); + SportAssistLevels[i].MaxCadencePercent = br.ReadByte(); + SportAssistLevels[i].MaxSpeedPercent = br.ReadByte(); + SportAssistLevels[i].TorqueAmplificationFactor = br.ReadByte() / 10f; + } + } + + // apply default settings for non existing options in version + UsePretension = false; + PretensionSpeedCutoffKph = 0; + + return true; + } + + public bool ParseFromBufferV5(byte[] buffer) + { + if (buffer.Length != ByteSizeV5) + { + return false; + } + + using (var s = new MemoryStream(buffer)) + { + var br = new BinaryReader(s); + + UseFreedomUnits = br.ReadBoolean(); + + MaxCurrentAmps = br.ReadByte(); + CurrentRampAmpsSecond = br.ReadByte(); + MaxBatteryVolts = br.ReadUInt16() / 100f; + LowCutoffVolts = br.ReadByte(); + MaxSpeedKph = br.ReadByte(); + + UseSpeedSensor = br.ReadBoolean(); + UseShiftSensor = br.ReadBoolean(); + UsePushWalk = br.ReadBoolean(); + UseTemperatureSensor = (TemperatureSensor)br.ReadByte(); + LightsMode = (LightsModeOptions)br.ReadByte(); + UsePretension = br.ReadBoolean(); + PretensionSpeedCutoffKph = br.ReadByte(); + + WheelSizeInch = br.ReadUInt16() / 10f; + NumWheelSensorSignals = br.ReadByte(); + + PasStartDelayPulses = br.ReadByte(); + PasStopDelayMilliseconds = br.ReadByte() * 10u; + PasKeepCurrentPercent = br.ReadByte(); + PasKeepCurrentCadenceRpm = br.ReadByte(); + + ThrottleStartMillivolts = br.ReadUInt16(); + ThrottleEndMillivolts = br.ReadUInt16(); + ThrottleStartPercent = br.ReadByte(); + ThrottleGlobalSpeedLimit = (ThrottleGlobalSpeedLimitOptions)br.ReadByte(); + ThrottleGlobalSpeedLimitPercent = br.ReadByte(); + + ShiftInterruptDuration = br.ReadUInt16(); + ShiftInterruptCurrentThresholdPercent = br.ReadByte(); + + WalkModeDataDisplay = (WalkModeData)br.ReadByte(); + + AssistModeSelection = (AssistModeSelect)br.ReadByte(); + AssistStartupLevel = br.ReadByte(); + + for (int i = 0; i < StandardAssistLevels.Length; ++i) + { + StandardAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); + StandardAssistLevels[i].MaxCurrentPercent = br.ReadByte(); + StandardAssistLevels[i].MaxThrottlePercent = br.ReadByte(); + StandardAssistLevels[i].MaxCadencePercent = br.ReadByte(); + StandardAssistLevels[i].MaxSpeedPercent = br.ReadByte(); + StandardAssistLevels[i].TorqueAmplificationFactor = br.ReadByte() / 10f; + } + + for (int i = 0; i < SportAssistLevels.Length; ++i) + { + SportAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); + SportAssistLevels[i].MaxCurrentPercent = br.ReadByte(); + SportAssistLevels[i].MaxThrottlePercent = br.ReadByte(); + SportAssistLevels[i].MaxCadencePercent = br.ReadByte(); + SportAssistLevels[i].MaxSpeedPercent = br.ReadByte(); + SportAssistLevels[i].TorqueAmplificationFactor = br.ReadByte() / 10f; + } + } + + return true; + } + + public byte[] WriteToBuffer() + { + using (var s = new MemoryStream()) + { + var bw = new BinaryWriter(s); + + bw.Write(UseFreedomUnits); + + bw.Write((byte)MaxCurrentAmps); + bw.Write((byte)CurrentRampAmpsSecond); + bw.Write((UInt16)(MaxBatteryVolts * 100)); + bw.Write((byte)LowCutoffVolts); + bw.Write((byte)MaxSpeedKph); + + bw.Write(UseSpeedSensor); + bw.Write(UseShiftSensor); + bw.Write(UsePushWalk); + bw.Write((byte)UseTemperatureSensor); + bw.Write((byte)LightsMode); + bw.Write(UsePretension); + bw.Write((byte)PretensionSpeedCutoffKph); + + bw.Write((UInt16)(WheelSizeInch * 10)); + bw.Write((byte)NumWheelSensorSignals); + + bw.Write((byte)PasStartDelayPulses); + bw.Write((byte)(PasStopDelayMilliseconds / 10u)); + bw.Write((byte)PasKeepCurrentPercent); + bw.Write((byte)PasKeepCurrentCadenceRpm); + + bw.Write((UInt16)ThrottleStartMillivolts); + bw.Write((UInt16)ThrottleEndMillivolts); + bw.Write((byte)ThrottleStartPercent); + bw.Write((byte)ThrottleGlobalSpeedLimit); + bw.Write((byte)ThrottleGlobalSpeedLimitPercent); + + bw.Write((UInt16)ShiftInterruptDuration); + bw.Write((byte)ShiftInterruptCurrentThresholdPercent); + + bw.Write((byte)WalkModeDataDisplay); + + bw.Write((byte)AssistModeSelection); + bw.Write((byte)AssistStartupLevel); + + for (int i = 0; i < StandardAssistLevels.Length; ++i) + { + bw.Write((byte)StandardAssistLevels[i].Type); + bw.Write((byte)StandardAssistLevels[i].MaxCurrentPercent); + bw.Write((byte)StandardAssistLevels[i].MaxThrottlePercent); + bw.Write((byte)StandardAssistLevels[i].MaxCadencePercent); + bw.Write((byte)StandardAssistLevels[i].MaxSpeedPercent); + bw.Write((byte)Math.Round(StandardAssistLevels[i].TorqueAmplificationFactor * 10)); + } + + for (int i = 0; i < SportAssistLevels.Length; ++i) + { + bw.Write((byte)SportAssistLevels[i].Type); + bw.Write((byte)SportAssistLevels[i].MaxCurrentPercent); + bw.Write((byte)SportAssistLevels[i].MaxThrottlePercent); + bw.Write((byte)SportAssistLevels[i].MaxCadencePercent); + bw.Write((byte)SportAssistLevels[i].MaxSpeedPercent); + bw.Write((byte)Math.Round(SportAssistLevels[i].TorqueAmplificationFactor * 10)); + } + + return s.ToArray(); + } + } + + public void CopyFrom(Configuration cfg) + { + Target = cfg.Target; + + UseFreedomUnits = cfg.UseFreedomUnits; + MaxCurrentAmps = cfg.MaxCurrentAmps; + CurrentRampAmpsSecond = cfg.CurrentRampAmpsSecond; + MaxBatteryVolts = cfg.MaxBatteryVolts; + LowCutoffVolts = cfg.LowCutoffVolts; + UseSpeedSensor = cfg.UseSpeedSensor; + UseShiftSensor = cfg.UseShiftSensor; + UsePushWalk = cfg.UsePushWalk; + UsePretension = cfg.UsePretension; + PretensionSpeedCutoffKph = cfg.PretensionSpeedCutoffKph; + UseTemperatureSensor = cfg.UseTemperatureSensor; + LightsMode = cfg.LightsMode; + WheelSizeInch = cfg.WheelSizeInch; + NumWheelSensorSignals = cfg.NumWheelSensorSignals; + MaxSpeedKph = cfg.MaxSpeedKph; + PasStartDelayPulses = cfg.PasStartDelayPulses; + PasStopDelayMilliseconds = cfg.PasStopDelayMilliseconds; + PasKeepCurrentPercent = cfg.PasKeepCurrentPercent; + PasKeepCurrentCadenceRpm = cfg.PasKeepCurrentCadenceRpm; + ThrottleStartMillivolts = cfg.ThrottleStartMillivolts; + ThrottleEndMillivolts = cfg.ThrottleEndMillivolts; + ThrottleStartPercent = cfg.ThrottleStartPercent; + ThrottleGlobalSpeedLimit = cfg.ThrottleGlobalSpeedLimit; + ThrottleGlobalSpeedLimitPercent = cfg.ThrottleGlobalSpeedLimitPercent; + ShiftInterruptDuration = cfg.ShiftInterruptDuration; + ShiftInterruptCurrentThresholdPercent = cfg.ShiftInterruptCurrentThresholdPercent; + WalkModeDataDisplay = cfg.WalkModeDataDisplay; + AssistModeSelection = cfg.AssistModeSelection; + AssistStartupLevel = cfg.AssistStartupLevel; + + for (int i = 0; i < Math.Min(cfg.StandardAssistLevels.Length, StandardAssistLevels.Length); ++i) + { + StandardAssistLevels[i].Type = cfg.StandardAssistLevels[i].Type; + StandardAssistLevels[i].MaxCurrentPercent = cfg.StandardAssistLevels[i].MaxCurrentPercent; + StandardAssistLevels[i].MaxThrottlePercent = cfg.StandardAssistLevels[i].MaxThrottlePercent; + StandardAssistLevels[i].MaxCadencePercent = cfg.StandardAssistLevels[i].MaxCadencePercent; + StandardAssistLevels[i].MaxSpeedPercent = cfg.StandardAssistLevels[i].MaxSpeedPercent; + StandardAssistLevels[i].TorqueAmplificationFactor = cfg.StandardAssistLevels[i].TorqueAmplificationFactor; + } + + for (int i = 0; i < Math.Min(cfg.SportAssistLevels.Length, SportAssistLevels.Length); ++i) + { + SportAssistLevels[i].Type = cfg.SportAssistLevels[i].Type; + SportAssistLevels[i].MaxCurrentPercent = cfg.SportAssistLevels[i].MaxCurrentPercent; + SportAssistLevels[i].MaxThrottlePercent = cfg.SportAssistLevels[i].MaxThrottlePercent; + SportAssistLevels[i].MaxCadencePercent = cfg.SportAssistLevels[i].MaxCadencePercent; + SportAssistLevels[i].MaxSpeedPercent = cfg.SportAssistLevels[i].MaxSpeedPercent; + SportAssistLevels[i].TorqueAmplificationFactor = cfg.SportAssistLevels[i].TorqueAmplificationFactor; + } + } + + public void ReadFromFile(string filepath) + { + var serializer = new XmlSerializer(typeof(Configuration)); + + using (var reader = new FileStream(filepath, FileMode.Open)) + { + var obj = serializer.Deserialize(reader) as Configuration; + CopyFrom(obj); + } + } + + public void WriteToFile(string filepath) + { + var serializer = new XmlSerializer(typeof(Configuration)); + var settings = new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = true }; + using (var xmlWriter = XmlWriter.Create(new StreamWriter(filepath), settings)) + { + serializer.Serialize(xmlWriter, this); + } + } + + public void Validate() + { + ValidateLimits(MaxCurrentAmps, 5, MaxCurrentLimitAmps, "Max Current (A)"); + ValidateLimits(CurrentRampAmpsSecond, 1, 255, "Current Ramp (A/s)"); + ValidateLimits((uint)MaxBatteryVolts, 1, 100, "Max Battery Voltage (V)"); + ValidateLimits(LowCutoffVolts, 1, 100, "Low Voltage Cut Off (V)"); + + ValidateLimits((uint)WheelSizeInch, 10, 40, "Wheel Size (inch)"); + ValidateLimits(NumWheelSensorSignals, 1, 10, "Wheel Sensor Signals"); + ValidateLimits(MaxSpeedKph, 0, 180, "Max Speed (km/h)"); + ValidateLimits(PretensionSpeedCutoffKph, 0, 100, "Pretension Speed Cutoff (km/h)"); + + ValidateLimits(PasStartDelayPulses, 0, 24, "Pas Delay (pulses)"); + ValidateLimits(PasStopDelayMilliseconds, 50, 1000, "Pas Stop Delay (ms)"); + ValidateLimits(PasKeepCurrentPercent, 10, 100, "Pas Keep Current (%)"); + ValidateLimits(PasKeepCurrentCadenceRpm, 0, 255, "Pas Keep Current Cadence (rpm)"); + + ValidateLimits(ThrottleStartMillivolts, 200, 2500, "Throttle Start (mV)"); + ValidateLimits(ThrottleEndMillivolts, 2500, 5000, "Throttle End (mV)"); + ValidateLimits(ThrottleStartPercent, 0, 100, "Throttle Start (%)"); + ValidateLimits(ThrottleGlobalSpeedLimitPercent, 0, 100, "Throttle Global Speed Limit (%)"); + + ValidateLimits(ShiftInterruptDuration, 50, 2000, "Shift Interrupt Duration (ms)"); + ValidateLimits(ShiftInterruptCurrentThresholdPercent, 0, 100, "Shift Interrupt Current Threshold (%)"); + + ValidateLimits(AssistStartupLevel, 0, 9, "Assist Startup Level"); + + for (int i = 0; i < StandardAssistLevels.Length; ++i) + { + ValidateLimits(StandardAssistLevels[i].MaxCurrentPercent, 0, 100, + $"Standard (Level {i}): Target Power (%)"); + ValidateLimits(StandardAssistLevels[i].MaxThrottlePercent, 0, 100, + $"Standard (Level {i}): Max Throttle (%)"); + ValidateLimits(StandardAssistLevels[i].MaxCadencePercent, 0, 100, $"Standard (Level {i}): Max Cadence (%)"); + ValidateLimits(StandardAssistLevels[i].MaxSpeedPercent, 0, 100, $"Standard (Level {i}): Max Speed (%)"); + ValidateLimits((uint)StandardAssistLevels[i].TorqueAmplificationFactor, 0, 25, + $"Standard (Level {i}): Torque Amplification"); + } + + for (int i = 0; i < SportAssistLevels.Length; ++i) + { + ValidateLimits(SportAssistLevels[i].MaxCurrentPercent, 0, 100, $"Sport (Level {i}): Target Power (%)"); + ValidateLimits(SportAssistLevels[i].MaxThrottlePercent, 0, 100, $"Sport (Level {i}): Max Throttle (%)"); + ValidateLimits(SportAssistLevels[i].MaxCadencePercent, 0, 100, $"Sport (Level {i}): Max Cadence (%)"); + ValidateLimits(SportAssistLevels[i].MaxSpeedPercent, 0, 100, $"Sport (Level {i}): Max Speed (%)"); + ValidateLimits((uint)SportAssistLevels[i].TorqueAmplificationFactor, 0, 25, + $"Sport (Level {i}): Torque Amplification"); + } + } + + private void ValidateLimits(uint value, uint min, uint max, string name) + { + if (value < min || value > max) + { + throw new Exception(name + " must be in interval " + min + "-" + max + "."); + } + } +} +} diff --git a/code/tool/Model/EventLogEntry.cs b/code/tool/Model/EventLogEntry.cs new file mode 100644 index 00000000..aa913c76 --- /dev/null +++ b/code/tool/Model/EventLogEntry.cs @@ -0,0 +1,242 @@ +using System; + +namespace BBSFW.Model +{ +public class EventLogEntry +{ + private int _event; + private int? _data; + + private const int EVT_MSG_MOTOR_INIT_OK = 1; + private const int EVT_MSG_CONFIG_READ_DONE = 2; + private const int EVT_MSG_CONFIG_RESET = 3; + private const int EVT_MSG_CONFIG_WRITE_DONE = 4; + private const int EVT_MSG_CONFIG_READ_BEGIN = 5; + private const int EVT_MSG_CONFIG_WRITE_BEGIN = 6; + private const int EVT_MSG_PSTATE_READ_BEGIN = 7; + private const int EVT_MSG_PSTATE_READ_DONE = 8; + private const int EVT_MSG_PSTATE_WRITE_BEGIN = 9; + private const int EVT_MSG_PSTATE_WRITE_DONE = 10; + + private const int EVT_ERROR_INIT_MOTOR = 64; + private const int EVT_ERROR_CHANGE_TARGET_SPEED = 65; + private const int EVT_ERROR_CHANGE_TARGET_CURRENT = 66; + private const int EVT_ERROR_READ_MOTOR_STATUS = 67; + private const int EVT_ERROR_READ_MOTOR_CURRENT = 68; + private const int EVT_ERROR_READ_MOTOR_VOLTAGE = 69; + + private const int EVT_ERROR_EEPROM_READ = 70; + private const int EVT_ERROR_EEPROM_WRITE = 71; + private const int EVT_ERROR_EEPROM_ERASE = 72; + private const int EVT_ERROR_EEPROM_VERIFY_VERSION = 73; + private const int EVT_ERROR_EEPROM_VERIFY_CHECKSUM = 74; + private const int EVT_ERROR_THROTTLE_LOW_LIMIT = 75; + private const int EVT_ERROR_THROTTLE_HIGH_LIMIT = 76; + private const int EVT_ERROR_WATCHDOG_TRIGGERED = 77; + private const int EVT_ERROR_EXTCOM_CHECKSUM = 78; + private const int EVT_ERROR_EXTCOM_DISCARD = 79; + + private const int EVT_DATA_TARGET_CURRENT = 128; + private const int EVT_DATA_TARGET_SPEED = 129; + private const int EVT_DATA_MOTOR_STATUS = 130; + private const int EVT_DATA_ASSIST_LEVEL = 131; + private const int EVT_DATA_OPERATION_MODE = 132; + private const int EVT_DATA_WHEEL_SPEED_PPM = 133; + private const int EVT_DATA_LIGHTS = 134; + private const int EVT_DATA_TEMPERATURE = 135; + private const int EVT_DATA_THERMAL_LIMITING = 136; + private const int EVT_DATA_SPEED_LIMITING = 137; + private const int EVT_DATA_MAX_CURRENT_ADC_REQUEST = 138; + private const int EVT_DATA_MAX_CURRENT_ADC_RESPONSE = 139; + private const int EVT_DATA_MAIN_LOOP_TIME = 140; + private const int EVT_DATA_THROTTLE_ADC = 141; + private const int EVT_DATA_LVC_LIMITING = 142; + private const int EVT_DATA_SHIFT_SENSOR = 143; + private const int EVT_DATA_BBSHD_THERMISTOR = 144; + private const int EVT_DATA_VOLTAGE = 145; + private const int EVT_DATA_VOLTAGE_CALIBRATION = 146; + private const int EVT_DATA_TORQUE_ADC = 147; + private const int EVT_DATA_TORQUE_ADC_CALIBRATED = 148; + + public enum LogLevel + { + Info, + Warning, + Error + } + + public DateTime Timestamp { get; private set; } + + public LogLevel Level { get; private set; } + + public string Message { get; private set; } + + public EventLogEntry(int evt, int? data) + { + Timestamp = DateTime.Now; + _event = evt; + if (evt >= 64 & evt < 128) + { + Level = LogLevel.Error; + } + else + { + Level = LogLevel.Info; + } + + _data = data; + Message = Parse(); + } + + public string Parse() + { + switch (_event) + { + case EVT_MSG_MOTOR_INIT_OK: + return "Motor initialization successful."; + case EVT_MSG_CONFIG_READ_DONE: + return "Successfully read configuration from eeprom."; + case EVT_MSG_CONFIG_RESET: + Level = LogLevel.Warning; + return "Configuration reset performed."; + case EVT_MSG_CONFIG_WRITE_DONE: + return "Configuration successfully written to eeprom."; + case EVT_MSG_CONFIG_READ_BEGIN: + return "Reading configuration from eeprom."; + case EVT_MSG_CONFIG_WRITE_BEGIN: + return "Writing configuration to eeprom."; + case EVT_MSG_PSTATE_READ_BEGIN: + return "Reading persisted state from eeprom."; + case EVT_MSG_PSTATE_READ_DONE: + return "Successfully read persisted state from eeprom."; + case EVT_MSG_PSTATE_WRITE_BEGIN: + return "Writing persisted stated to eeprom."; + case EVT_MSG_PSTATE_WRITE_DONE: + return "Persisted state successfully written to eeprom."; + + case EVT_ERROR_INIT_MOTOR: + return "Failed to perform motor controller initialization."; + case EVT_ERROR_CHANGE_TARGET_CURRENT: + return "Failed to set motor target current on motor controller."; + case EVT_ERROR_CHANGE_TARGET_SPEED: + return "Failed to set motor target speed on motor controller."; + case EVT_ERROR_READ_MOTOR_STATUS: + return "Failed to read status from motor controller."; + case EVT_ERROR_READ_MOTOR_CURRENT: + return "Failed to read current from motor controller."; + case EVT_ERROR_READ_MOTOR_VOLTAGE: + return "Failed to read voltage from motor controller."; + case EVT_ERROR_EEPROM_READ: + return "Failed to read data from eeprom."; + case EVT_ERROR_EEPROM_WRITE: + return "Failed to write data to eeprom."; + case EVT_ERROR_EEPROM_ERASE: + return "Failed to erase eeprom before writing data."; + case EVT_ERROR_EEPROM_VERIFY_VERSION: + return "Data read from eeprom is of the wrong version."; + case EVT_ERROR_EEPROM_VERIFY_CHECKSUM: + return "Failed to verify checksum on data read from eeprom."; + case EVT_ERROR_THROTTLE_LOW_LIMIT: + return "Invalid throttle reading, below low limit, check throttle."; + case EVT_ERROR_THROTTLE_HIGH_LIMIT: + return "Invalid throttle reading, above high limit, check throttle."; + case EVT_ERROR_WATCHDOG_TRIGGERED: + return "Software reset by watchdog, software error."; + case EVT_ERROR_EXTCOM_CHECKSUM: + return "Message received with invalid checksum."; + case EVT_ERROR_EXTCOM_DISCARD: + return "Invalid message received on serial port, discarded."; + + case EVT_DATA_TARGET_CURRENT: + return $"Motor target current changed to {_data}%."; + case EVT_DATA_TARGET_SPEED: + return $"Motor target speed changed to {_data}%."; + case EVT_DATA_MOTOR_STATUS: + Level = _data != 0 ? LogLevel.Error : LogLevel.Info; + return $"Motor controller status changed to 0x{_data:X}."; + case EVT_DATA_ASSIST_LEVEL: + return $"Assist level changed to {_data}."; + case EVT_DATA_OPERATION_MODE: + return $"Operation mode changed to {_data}."; + case EVT_DATA_WHEEL_SPEED_PPM: + return $"Max wheel speed changed to {_data} rpm."; + case EVT_DATA_LIGHTS: + return $"Lights status changed to {_data}."; + case EVT_DATA_TEMPERATURE: + { + byte[] raw = BitConverter.GetBytes(_data.Value); + return $"Temperature, motor={(sbyte)raw[1]}C, controller={(sbyte)raw[0]}C."; + } + case EVT_DATA_THERMAL_LIMITING: + if (_data.Value != 0) + { + Level = LogLevel.Warning; + return "Thermal limiting activated, reducing power."; + } + else + { + return "Thermal limiting deactivated."; + } + case EVT_DATA_SPEED_LIMITING: + if (_data.Value != 0) + { + return "Speed limiting activated."; + } + else + { + return "Speed limiting deactivated."; + } + case EVT_DATA_MAX_CURRENT_ADC_REQUEST: + return $"Requesting to configure max current on motor controller mcu, adc={_data}."; + case EVT_DATA_MAX_CURRENT_ADC_RESPONSE: + return $"Max current configured on motor controller mcu, response was adc={_data}."; + case EVT_DATA_MAIN_LOOP_TIME: + return $"Main loop, interval={_data}ms."; + case EVT_DATA_THROTTLE_ADC: + return $"Throttle adc, value={_data}."; + case EVT_DATA_LVC_LIMITING: + if (_data.Value != 0) + { + return $"Low voltage limiting activated, voltage={(_data / 100f):0.0}"; + } + else + { + return "Low voltage limiting deactivated."; + } + case EVT_DATA_SHIFT_SENSOR: + if (_data.Value != 0) + { + return $"Shift sensor power ramp started."; + } + else + { + return $"Shift sensor power ramp ended."; + } + case EVT_DATA_BBSHD_THERMISTOR: + if (_data.Value != 0) + { + return "BBSHD motor with PTC thermistor detected."; + } + else + { + return "BBSHD motor with NTC thermistor detected."; + } + case EVT_DATA_VOLTAGE: + return $"Battery voltage reading, value={_data / 100f}V."; + case EVT_DATA_VOLTAGE_CALIBRATION: + return $"Battery voltage calibration updated, adc_steps_per_volt={_data / 100f}."; + case EVT_DATA_TORQUE_ADC: + return $"Torque adc, value={_data}."; + case EVT_DATA_TORQUE_ADC_CALIBRATED: + return $"Torque sensor calibrated, adc_bias={_data}."; + } + + if (_data.HasValue) + { + return $"Unknown ({_event}, value={_data.Value})"; + } + + return $"Unknown ({_event})"; + } +} +} diff --git a/code/tool/Properties/Settings.Designer.cs b/code/tool/Properties/Settings.Designer.cs new file mode 100644 index 00000000..c24f80c3 --- /dev/null +++ b/code/tool/Properties/Settings.Designer.cs @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace BBSFW.Properties +{ + +[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] +[global::System.CodeDom.Compiler.GeneratedCodeAttribute( + "Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.7.0.0")] +internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase +{ + + private static Settings defaultInstance = + ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool UseFreedomUnits + { + get { + return ((bool)(this["UseFreedomUnits"])); + } + set { + this["UseFreedomUnits"] = value; + } + } +} +} diff --git a/src/tool/Properties/Settings.settings b/code/tool/Properties/Settings.settings similarity index 96% rename from src/tool/Properties/Settings.settings rename to code/tool/Properties/Settings.settings index 6e0000b8..f45026c4 100644 --- a/src/tool/Properties/Settings.settings +++ b/code/tool/Properties/Settings.settings @@ -6,4 +6,4 @@ False - \ No newline at end of file + diff --git a/src/tool/View/AssistLevelCruiseView.xaml b/code/tool/View/AssistLevelCruiseView.xaml similarity index 95% rename from src/tool/View/AssistLevelCruiseView.xaml rename to code/tool/View/AssistLevelCruiseView.xaml index 367bf9f9..1c206c2b 100644 --- a/src/tool/View/AssistLevelCruiseView.xaml +++ b/code/tool/View/AssistLevelCruiseView.xaml @@ -1,10 +1,10 @@ @@ -27,6 +27,6 @@ - + diff --git a/src/tool/View/AssistLevelCruiseView.xaml.cs b/code/tool/View/AssistLevelCruiseView.xaml.cs similarity index 65% rename from src/tool/View/AssistLevelCruiseView.xaml.cs rename to code/tool/View/AssistLevelCruiseView.xaml.cs index 9f5e4af6..23b5ae27 100644 --- a/src/tool/View/AssistLevelCruiseView.xaml.cs +++ b/code/tool/View/AssistLevelCruiseView.xaml.cs @@ -15,14 +15,14 @@ namespace BBSFW.View { - /// - /// Interaction logic for AssistLevelCruiseView.xaml - /// - public partial class AssistLevelCruiseView : UserControl - { - public AssistLevelCruiseView() - { - InitializeComponent(); - } - } +/// +/// Interaction logic for AssistLevelCruiseView.xaml +/// +public partial class AssistLevelCruiseView : UserControl +{ + public AssistLevelCruiseView() + { + InitializeComponent(); + } +} } diff --git a/src/tool/View/AssistLevelPasView.xaml b/code/tool/View/AssistLevelPasView.xaml similarity index 98% rename from src/tool/View/AssistLevelPasView.xaml rename to code/tool/View/AssistLevelPasView.xaml index b74ec4f1..d2bc0f63 100644 --- a/src/tool/View/AssistLevelPasView.xaml +++ b/code/tool/View/AssistLevelPasView.xaml @@ -1,10 +1,10 @@ @@ -51,7 +51,7 @@ - + @@ -145,8 +145,8 @@ - - + + - + @@ -97,7 +97,7 @@ - + @@ -133,7 +133,7 @@ - + diff --git a/src/tool/View/AssistLevelsView.xaml.cs b/code/tool/View/AssistLevelsView.xaml.cs similarity index 63% rename from src/tool/View/AssistLevelsView.xaml.cs rename to code/tool/View/AssistLevelsView.xaml.cs index 53f9e729..2751c67a 100644 --- a/src/tool/View/AssistLevelsView.xaml.cs +++ b/code/tool/View/AssistLevelsView.xaml.cs @@ -13,14 +13,14 @@ namespace BBSFW.View { - /// - /// Interaction logic for AssistLevelsView.xaml - /// - public partial class AssistLevelsView : UserControl - { - public AssistLevelsView() - { - InitializeComponent(); - } - } +/// +/// Interaction logic for AssistLevelsView.xaml +/// +public partial class AssistLevelsView : UserControl +{ + public AssistLevelsView() + { + InitializeComponent(); + } +} } diff --git a/src/tool/View/CalibrationView.xaml b/code/tool/View/CalibrationView.xaml similarity index 94% rename from src/tool/View/CalibrationView.xaml rename to code/tool/View/CalibrationView.xaml index 6d69a609..aec0ada2 100644 --- a/src/tool/View/CalibrationView.xaml +++ b/code/tool/View/CalibrationView.xaml @@ -1,10 +1,10 @@ @@ -35,9 +35,9 @@ Measure you battery voltage using a multi meter or use the reading from you display or bms and enter the value - in "Measured Battery Voltage (V)" above, then press save. Check the event log to confirm that the battery voltage + in "Measured Battery Voltage (V)" above, then press save. Check the event log to confirm that the battery voltage reading is now accurate. - + diff --git a/src/tool/View/CalibrationView.xaml.cs b/code/tool/View/CalibrationView.xaml.cs similarity index 66% rename from src/tool/View/CalibrationView.xaml.cs rename to code/tool/View/CalibrationView.xaml.cs index 63641dde..fa9b4b6e 100644 --- a/src/tool/View/CalibrationView.xaml.cs +++ b/code/tool/View/CalibrationView.xaml.cs @@ -15,14 +15,14 @@ namespace BBSFW.View { - /// - /// Interaction logic for CalibrationView.xaml - /// - public partial class CalibrationView : UserControl - { - public CalibrationView() - { - InitializeComponent(); - } - } +/// +/// Interaction logic for CalibrationView.xaml +/// +public partial class CalibrationView : UserControl +{ + public CalibrationView() + { + InitializeComponent(); + } +} } diff --git a/src/tool/View/ConnectionView.xaml b/code/tool/View/ConnectionView.xaml similarity index 98% rename from src/tool/View/ConnectionView.xaml rename to code/tool/View/ConnectionView.xaml index da0cf811..22615ca6 100644 --- a/src/tool/View/ConnectionView.xaml +++ b/code/tool/View/ConnectionView.xaml @@ -1,16 +1,16 @@ - + @@ -85,7 +85,7 @@ - + diff --git a/src/tool/View/ConnectionView.xaml.cs b/code/tool/View/ConnectionView.xaml.cs similarity index 64% rename from src/tool/View/ConnectionView.xaml.cs rename to code/tool/View/ConnectionView.xaml.cs index 5cbd8a3e..f7554df0 100644 --- a/src/tool/View/ConnectionView.xaml.cs +++ b/code/tool/View/ConnectionView.xaml.cs @@ -13,14 +13,14 @@ namespace BBSFW.View { - /// - /// Interaction logic for ConnectionView.xaml - /// - public partial class ConnectionView : UserControl - { - public ConnectionView() - { - InitializeComponent(); - } - } +/// +/// Interaction logic for ConnectionView.xaml +/// +public partial class ConnectionView : UserControl +{ + public ConnectionView() + { + InitializeComponent(); + } +} } diff --git a/code/tool/View/Converter/TimestampConverter.cs b/code/tool/View/Converter/TimestampConverter.cs new file mode 100644 index 00000000..09be241b --- /dev/null +++ b/code/tool/View/Converter/TimestampConverter.cs @@ -0,0 +1,26 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace BBSFW.View.Converter +{ +public class TimestampConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is DateTime) + { + var dt = (DateTime)value; + + return dt.ToString("yyyy-MM-dd HH:mm:ss.fff"); + } + + return null; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} +} diff --git a/src/tool/View/EventLogView.xaml b/code/tool/View/EventLogView.xaml similarity index 90% rename from src/tool/View/EventLogView.xaml rename to code/tool/View/EventLogView.xaml index 31f86bac..ab03ab0f 100644 --- a/src/tool/View/EventLogView.xaml +++ b/code/tool/View/EventLogView.xaml @@ -1,12 +1,12 @@ @@ -37,12 +37,12 @@ Content="Clear" Command="{Binding ClearCommand}" /> - @@ -64,19 +64,19 @@ VerticalAlignment="Center" IsChecked="{Binding TailLog}" /> - - - /// Interaction logic for EventLogView.xaml - /// - public partial class EventLogView : UserControl - { - public EventLogView() - { - InitializeComponent(); - } - } +/// +/// Interaction logic for EventLogView.xaml +/// +public partial class EventLogView : UserControl +{ + public EventLogView() + { + InitializeComponent(); + } +} } diff --git a/code/tool/View/Extension/DataGridExtension.cs b/code/tool/View/Extension/DataGridExtension.cs new file mode 100644 index 00000000..67fa9cfb --- /dev/null +++ b/code/tool/View/Extension/DataGridExtension.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Windows; +using System.Windows.Controls; + +namespace BBSFW.View.Extension +{ +public static class DataGridExtension +{ + + public static readonly DependencyProperty AutoScrollToEndProperty = + DependencyProperty.RegisterAttached("AutoScrollToEnd", typeof(bool), typeof(DataGridExtension), + new PropertyMetadata(default(bool), AutoScrollToEndChangedCallback)); + + private static readonly Dictionary handlersDict = + new Dictionary(); + + private static void AutoScrollToEndChangedCallback(DependencyObject dependencyObject, + DependencyPropertyChangedEventArgs args) + { + var dataGrid = dependencyObject as DataGrid; + if (dataGrid == null) + { + throw new InvalidOperationException("Dependency object is not DataGrid."); + } + + if ((bool)args.NewValue) + { + Subscribe(dataGrid); + dataGrid.Unloaded += DataGridOnUnloaded; + dataGrid.Loaded += DataGridOnLoaded; + } + else + { + Unsubscribe(dataGrid); + dataGrid.Unloaded -= DataGridOnUnloaded; + dataGrid.Loaded -= DataGridOnLoaded; + } + } + + private static void Subscribe(DataGrid dataGrid) + { + var handler = new NotifyCollectionChangedEventHandler((sender, eventArgs) => ScrollToEnd(dataGrid)); + handlersDict.Add(dataGrid, handler); + ((INotifyCollectionChanged)dataGrid.Items).CollectionChanged += handler; + ScrollToEnd(dataGrid); + } + + private static void Unsubscribe(DataGrid dataGrid) + { + NotifyCollectionChangedEventHandler handler; + handlersDict.TryGetValue(dataGrid, out handler); + if (handler == null) + { + return; + } + ((INotifyCollectionChanged)dataGrid.Items).CollectionChanged -= handler; + handlersDict.Remove(dataGrid); + } + + private static void DataGridOnLoaded(object sender, RoutedEventArgs routedEventArgs) + { + var dataGrid = (DataGrid)sender; + if (GetAutoScrollToEnd(dataGrid)) + { + Subscribe(dataGrid); + } + } + + private static void DataGridOnUnloaded(object sender, RoutedEventArgs routedEventArgs) + { + var dataGrid = (DataGrid)sender; + if (GetAutoScrollToEnd(dataGrid)) + { + Unsubscribe(dataGrid); + } + } + + private static void ScrollToEnd(DataGrid datagrid) + { + if (datagrid.Items.Count == 0) + { + return; + } + datagrid.ScrollIntoView(datagrid.Items[datagrid.Items.Count - 1]); + } + + public static void SetAutoScrollToEnd(DependencyObject element, bool value) + { + element.SetValue(AutoScrollToEndProperty, value); + } + + public static bool GetAutoScrollToEnd(DependencyObject element) + { + return (bool)element.GetValue(AutoScrollToEndProperty); + } +} +} diff --git a/src/tool/View/MainWindow.xaml b/code/tool/View/MainWindow.xaml similarity index 99% rename from src/tool/View/MainWindow.xaml rename to code/tool/View/MainWindow.xaml index 8d9d0e5a..ebe50caf 100644 --- a/src/tool/View/MainWindow.xaml +++ b/code/tool/View/MainWindow.xaml @@ -20,7 +20,7 @@ - + @@ -64,6 +64,6 @@ - + diff --git a/src/tool/View/MainWindow.xaml.cs b/code/tool/View/MainWindow.xaml.cs similarity index 68% rename from src/tool/View/MainWindow.xaml.cs rename to code/tool/View/MainWindow.xaml.cs index 0c1c0831..fde83f6d 100644 --- a/src/tool/View/MainWindow.xaml.cs +++ b/code/tool/View/MainWindow.xaml.cs @@ -15,14 +15,14 @@ namespace BBSFW { - /// - /// Interaction logic for MainWindow.xaml - /// - public partial class MainWindow : Window - { - public MainWindow() - { - InitializeComponent(); - } - } +/// +/// Interaction logic for MainWindow.xaml +/// +public partial class MainWindow : Window +{ + public MainWindow() + { + InitializeComponent(); + } +} } diff --git a/src/tool/View/SystemView.xaml b/code/tool/View/SystemView.xaml similarity index 99% rename from src/tool/View/SystemView.xaml rename to code/tool/View/SystemView.xaml index e9ff0da3..491d6533 100644 --- a/src/tool/View/SystemView.xaml +++ b/code/tool/View/SystemView.xaml @@ -1,16 +1,16 @@ - + @@ -24,7 +24,7 @@ - + @@ -98,8 +98,8 @@ be used in order to comply with such laws. - When this option is enabled the configured global throttle speed limit will override the - assist level speed limit while using throttle and not pedaling. Throttle must still + When this option is enabled the configured global throttle speed limit will override the + assist level speed limit while using throttle and not pedaling. Throttle must still be enabled on each individual assist level for this option to apply. @@ -314,7 +314,7 @@ - + @@ -345,7 +345,7 @@ Text="{Binding ConfigVm.ShiftInterruptCurrentThresholdPercent, UpdateSourceTrigger=PropertyChanged}" /> - + diff --git a/src/tool/View/SystemView.xaml.cs b/code/tool/View/SystemView.xaml.cs similarity index 65% rename from src/tool/View/SystemView.xaml.cs rename to code/tool/View/SystemView.xaml.cs index d7977b4c..5d70fe09 100644 --- a/src/tool/View/SystemView.xaml.cs +++ b/code/tool/View/SystemView.xaml.cs @@ -13,14 +13,14 @@ namespace BBSFW.View { - /// - /// Interaction logic for SystemView.xaml - /// - public partial class SystemView : UserControl - { - public SystemView() - { - InitializeComponent(); - } - } +/// +/// Interaction logic for SystemView.xaml +/// +public partial class SystemView : UserControl +{ + public SystemView() + { + InitializeComponent(); + } +} } diff --git a/code/tool/ViewModel/AssistLevelViewModel.cs b/code/tool/ViewModel/AssistLevelViewModel.cs new file mode 100644 index 00000000..df8784a6 --- /dev/null +++ b/code/tool/ViewModel/AssistLevelViewModel.cs @@ -0,0 +1,398 @@ +using BBSFW.Model; +using BBSFW.ViewModel.Base; +using System.Collections.Generic; +using System.Linq; + +namespace BBSFW.ViewModel +{ +public class AssistLevelViewModel : ObservableObject +{ + private ConfigurationViewModel _configVm; + private Configuration.AssistLevel _level; + + public enum AssistBaseType + { + Disabled, + Pas, + Throttle, + Cruise + } + + public enum AssistPasVariant + { + Cadence, + Torque, + Variable + } + + public List> AssistBaseTypeOptions { + get; + } = new List>() { + new ValueItemViewModel(AssistBaseType.Disabled, "Motor Disabled"), + new ValueItemViewModel(AssistBaseType.Pas, "PAS"), + new ValueItemViewModel(AssistBaseType.Throttle, "Throttle"), + new ValueItemViewModel(AssistBaseType.Cruise, "Cruise") + }; + + public List> AssistPasVariantOptions + { + get { + var variants = new List> { new ValueItemViewModel( + AssistPasVariant.Cadence, "Cadence") }; + + if (_configVm.IsTorqueSensorSupported) + { + variants.Add(new ValueItemViewModel(AssistPasVariant.Torque, "Torque")); + } + + variants.Add(new ValueItemViewModel(AssistPasVariant.Variable, "Variable")); + + return variants; + } + } + + private int _id; + public int Id + { + get { + return _id; + } + } + + public ValueItemViewModel SelectedType + { + get { + var type = AssistBaseType.Disabled; + + if (_level.Type.HasFlag(Configuration.AssistFlagsType.Pas)) + { + type = AssistBaseType.Pas; + } + else if (_level.Type.HasFlag(Configuration.AssistFlagsType.Throttle)) + { + type = AssistBaseType.Throttle; + } + else if (_level.Type.HasFlag(Configuration.AssistFlagsType.Cruise)) + { + type = AssistBaseType.Cruise; + } + + return AssistBaseTypeOptions.FirstOrDefault((e) => e.Value == type); + } + set { + if (value.Value != SelectedType.Value) + { + _level.Type = ApplyBaseTypeFlag(value.Value, _level.Type); + OnPropertyChanged(nameof(SelectedType)); + + switch (value.Value) + { + case AssistBaseType.Disabled: + TargetCurrentPercent = 0; + MaxThrottlePercent = 0; + MaxSpeedPercent = 0; + TorqueAmplificationFactor = 0; + _level.Type = ClearThrottleFlag(_level.Type); + _level.Type = ClearPasVariantFlag(_level.Type); + IsThrottleCadenceOverrideEnabled = false; + IsThrottleSpeedOverrideEnabled = false; + break; + case AssistBaseType.Throttle: + TargetCurrentPercent = 0; + TorqueAmplificationFactor = 0; + TargetCurrentPercent = 0; + _level.Type = ClearPasVariantFlag(_level.Type); + IsThrottleCadenceOverrideEnabled = false; + IsThrottleSpeedOverrideEnabled = false; + break; + case AssistBaseType.Pas: + MaxThrottlePercent = 0; + _level.Type = ClearThrottleFlag(_level.Type); + break; + } + } + } + } + + public ValueItemViewModel SelectedPasVariant + { + get { + var variant = AssistPasVariant.Cadence; + if (_level.Type.HasFlag(Configuration.AssistFlagsType.PasTorque)) + { + variant = AssistPasVariant.Torque; + } + else if (_level.Type.HasFlag(Configuration.AssistFlagsType.PasVariable)) + { + variant = AssistPasVariant.Variable; + } + + return AssistPasVariantOptions.FirstOrDefault((e) => e.Value == variant); + } + set { + if (value.Value != SelectedPasVariant.Value) + { + _level.Type = ApplyPasVariantFlag(value.Value, _level.Type); + OnPropertyChanged(nameof(SelectedPasVariant)); + OnPropertyChanged(nameof(IsPasAssistVariableVariant)); + OnPropertyChanged(nameof(IsPasAssistTorqueVariant)); + + switch (value.Value) + { + case AssistPasVariant.Variable: + TorqueAmplificationFactor = 0; + IsThrottleEnabled = false; + IsThrottleCadenceOverrideEnabled = false; + IsThrottleSpeedOverrideEnabled = false; + MaxThrottlePercent = 0; + break; + case AssistPasVariant.Cadence: + TorqueAmplificationFactor = 0; + break; + } + } + } + } + + public bool IsThrottleEnabled + { + get { + return _level.Type.HasFlag(Configuration.AssistFlagsType.Throttle); + } + set { + if (value != IsThrottleEnabled) + { + _level.Type = ApplyThrottleFlag(value, _level.Type); + OnPropertyChanged(nameof(IsThrottleEnabled)); + } + } + } + + public bool IsThrottleCadenceOverrideEnabled + { + get { + return _level.Type.HasFlag(Configuration.AssistFlagsType.CadenceOverride); + } + set { + if (value != IsThrottleCadenceOverrideEnabled) + { + _level.Type = ApplyThrottleCadenceOverrideFlag(value, _level.Type); + OnPropertyChanged(nameof(IsThrottleCadenceOverrideEnabled)); + } + } + } + + public bool IsThrottleSpeedOverrideEnabled + { + get { + return _level.Type.HasFlag(Configuration.AssistFlagsType.SpeedOverride); + } + set { + if (value != IsThrottleSpeedOverrideEnabled) + { + _level.Type = ApplyThrottleSpeedOverrideFlag(value, _level.Type); + OnPropertyChanged(nameof(IsThrottleSpeedOverrideEnabled)); + } + } + } + + public bool IsPasAssistVariableVariant + { + get { + return _level.Type.HasFlag(Configuration.AssistFlagsType.PasVariable); + } + } + + public bool IsPasAssistTorqueVariant + { + get { + return _level.Type.HasFlag(Configuration.AssistFlagsType.PasTorque); + } + } + + public uint TargetCurrentPercent + { + get { + return _level.MaxCurrentPercent; + } + set { + if (_level.MaxCurrentPercent != value) + { + _level.MaxCurrentPercent = value; + OnPropertyChanged(nameof(TargetCurrentPercent)); + } + } + } + + public uint MaxThrottlePercent + { + get { + return _level.MaxThrottlePercent; + } + set { + if (_level.MaxThrottlePercent != value) + { + _level.MaxThrottlePercent = value; + OnPropertyChanged(nameof(MaxThrottlePercent)); + } + } + } + + public uint MaxCadencePercent + { + get { + return _level.MaxCadencePercent; + } + set { + if (_level.MaxCadencePercent != value) + { + _level.MaxCadencePercent = value; + OnPropertyChanged(nameof(MaxCadencePercent)); + } + } + } + + public uint MaxSpeedPercent + { + get { + return _level.MaxSpeedPercent; + } + set { + if (_level.MaxSpeedPercent != value) + { + _level.MaxSpeedPercent = value; + OnPropertyChanged(nameof(MaxSpeedPercent)); + } + } + } + + public float TorqueAmplificationFactor + { + get { + return _level.TorqueAmplificationFactor; + } + set { + if (_level.TorqueAmplificationFactor != value) + { + _level.TorqueAmplificationFactor = value; + OnPropertyChanged(nameof(TorqueAmplificationFactor)); + } + } + } + + public AssistLevelViewModel(ConfigurationViewModel configVm, int id, Configuration.AssistLevel level) + { + _configVm = configVm; + _id = id; + _level = level; + } + + private static Configuration.AssistFlagsType ApplyBaseTypeFlag(AssistBaseType baseType, + Configuration.AssistFlagsType flags) + { + byte f = (byte)flags; + f &= (byte) ~(Configuration.AssistFlagsType.Pas | Configuration.AssistFlagsType.Throttle | + Configuration.AssistFlagsType.Cruise); + + var result = (Configuration.AssistFlagsType)f; + switch (baseType) + { + case AssistBaseType.Pas: + result |= Configuration.AssistFlagsType.Pas; + break; + case AssistBaseType.Throttle: + result |= Configuration.AssistFlagsType.Throttle; + break; + case AssistBaseType.Cruise: + result |= Configuration.AssistFlagsType.Cruise; + break; + } + + return result; + } + + private static Configuration.AssistFlagsType ClearPasVariantFlag(Configuration.AssistFlagsType flags) + { + byte f = (byte)flags; + f &= (byte) ~(Configuration.AssistFlagsType.PasTorque | Configuration.AssistFlagsType.PasVariable); + + return (Configuration.AssistFlagsType)f; + } + + private static Configuration.AssistFlagsType ApplyPasVariantFlag(AssistPasVariant pasVariant, + Configuration.AssistFlagsType flags) + { + var result = ClearPasVariantFlag(flags); + switch (pasVariant) + { + case AssistPasVariant.Torque: + result |= Configuration.AssistFlagsType.PasTorque; + break; + case AssistPasVariant.Variable: + result |= Configuration.AssistFlagsType.PasVariable; + break; + } + + return result; + } + + private static Configuration.AssistFlagsType ClearThrottleFlag(Configuration.AssistFlagsType flags) + { + byte f = (byte)flags; + f &= (byte) ~(Configuration.AssistFlagsType.Throttle); + + return (Configuration.AssistFlagsType)f; + } + + private static Configuration.AssistFlagsType ApplyThrottleFlag(bool enabled, Configuration.AssistFlagsType flags) + { + var result = ClearThrottleFlag(flags); + if (enabled) + { + result |= Configuration.AssistFlagsType.Throttle; + } + + return result; + } + + private static Configuration.AssistFlagsType ClearThrottleCadenceOverrideFlag(Configuration.AssistFlagsType flags) + { + byte f = (byte)flags; + f &= (byte) ~(Configuration.AssistFlagsType.CadenceOverride); + + return (Configuration.AssistFlagsType)f; + } + + private static Configuration.AssistFlagsType ApplyThrottleCadenceOverrideFlag(bool enabled, + Configuration.AssistFlagsType flags) + { + var result = ClearThrottleCadenceOverrideFlag(flags); + if (enabled) + { + result |= Configuration.AssistFlagsType.CadenceOverride; + } + + return result; + } + + private static Configuration.AssistFlagsType ClearThrottleSpeedOverrideFlag(Configuration.AssistFlagsType flags) + { + byte f = (byte)flags; + f &= (byte) ~(Configuration.AssistFlagsType.SpeedOverride); + + return (Configuration.AssistFlagsType)f; + } + + private static Configuration.AssistFlagsType ApplyThrottleSpeedOverrideFlag(bool enabled, + Configuration.AssistFlagsType flags) + { + var result = ClearThrottleSpeedOverrideFlag(flags); + if (enabled) + { + result |= Configuration.AssistFlagsType.SpeedOverride; + } + + return result; + } +} +} diff --git a/code/tool/ViewModel/AssistLevelsViewModel.cs b/code/tool/ViewModel/AssistLevelsViewModel.cs new file mode 100644 index 00000000..530c7713 --- /dev/null +++ b/code/tool/ViewModel/AssistLevelsViewModel.cs @@ -0,0 +1,66 @@ +using BBSFW.ViewModel.Base; +using System.Collections.Generic; + +namespace BBSFW.ViewModel +{ +public class AssistLevelsViewModel : ObservableObject +{ + + public enum OperationMode + { + Standard, + Sport + } + + public static List> OperationModes { + get; + } = new List> { + new ValueItemViewModel(OperationMode.Standard, "Standard"), + new ValueItemViewModel(OperationMode.Sport, "Sport") + }; + + private ConfigurationViewModel _configVm; + public ConfigurationViewModel ConfigVm + { + get { + return _configVm; + } + } + + private ValueItemViewModel _selectedOperationModePage; + public ValueItemViewModel SelectedOperationModePage + { + get { + return _selectedOperationModePage; + } + set { + if (_selectedOperationModePage != value) + { + _selectedOperationModePage = value; + OnPropertyChanged(nameof(SelectedOperationModePage)); + } + } + } + + private AssistLevelViewModel _selectedAssistLevel; + public AssistLevelViewModel SelectedAssistLevel + { + get { + return _selectedAssistLevel; + } + set { + if (_selectedAssistLevel != value) + { + _selectedAssistLevel = value; + OnPropertyChanged(nameof(SelectedAssistLevel)); + } + } + } + + public AssistLevelsViewModel(ConfigurationViewModel config) + { + _configVm = config; + SelectedOperationModePage = OperationModes[0]; + } +} +} diff --git a/code/tool/ViewModel/Base/DelegateCommand.cs b/code/tool/ViewModel/Base/DelegateCommand.cs new file mode 100644 index 00000000..d93828b5 --- /dev/null +++ b/code/tool/ViewModel/Base/DelegateCommand.cs @@ -0,0 +1,36 @@ +using System; +using System.Windows.Input; + +namespace BBSFW.ViewModel.Base +{ +public class DelegateCommand : ICommand +{ + private readonly Action _action; + private readonly Action _actionWithParam; + + public event EventHandler CanExecuteChanged; + + public DelegateCommand(Action action) + { + _action = action; + _actionWithParam = null; + } + + public DelegateCommand(Action action) + { + _actionWithParam = action; + _action = null; + } + + public bool CanExecute(object parameter) + { + return true; + } + + public void Execute(object parameter) + { + _action?.Invoke(); + _actionWithParam?.Invoke(parameter); + } +} +} diff --git a/code/tool/ViewModel/Base/ObservableObject.cs b/code/tool/ViewModel/Base/ObservableObject.cs new file mode 100644 index 00000000..d582a7fb --- /dev/null +++ b/code/tool/ViewModel/Base/ObservableObject.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; + +namespace BBSFW.ViewModel.Base +{ +public class ObservableObject : INotifyPropertyChanged, INotifyDataErrorInfo +{ + +#region Private Members + + private Dictionary _errors = new Dictionary(); + +#endregion + +#region Public Properties + + public bool HasErrors + { + get { + return _errors.Count > 0; + } + } + +#endregion + +#region Public events + + public event EventHandler ErrorsChanged; + public event PropertyChangedEventHandler PropertyChanged; + +#endregion + +#region Public Functions + + public void AddError(string propertyName, string error) + { + if (!_errors.ContainsKey(propertyName) || _errors[propertyName] != error) + { + _errors[propertyName] = error; + ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); + } + } + + public void RemoveError(string propertyName) + { + if (_errors.Remove(propertyName)) + { + ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); + } + } + + public string GetError(string propertyName) + { + if (_errors.ContainsKey(propertyName)) + { + return _errors[propertyName]; + } + + return null; + } + + public bool HasError(string propertyName) + { + return _errors.ContainsKey(propertyName); + } + + public IEnumerable GetErrors(string propertyName) + { + if (propertyName == null) + return null; + + List err = new List(); + if (_errors.ContainsKey(propertyName)) + { + err.Add(_errors[propertyName]); + } + + return err; + } + +#endregion + +#region Protected Functions + + protected void OnPropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + +#endregion +} +} diff --git a/code/tool/ViewModel/CalibrationViewModel.cs b/code/tool/ViewModel/CalibrationViewModel.cs new file mode 100644 index 00000000..309bd612 --- /dev/null +++ b/code/tool/ViewModel/CalibrationViewModel.cs @@ -0,0 +1,127 @@ +using BBSFW.ViewModel.Base; +using System; +using System.Windows; +using System.Windows.Input; + +namespace BBSFW.ViewModel +{ +public class CalibrationViewModel : ObservableObject +{ + private ConnectionViewModel _connectionVm; + + private float _batteryStatusVolts; + public float BatteryStatusVolts + { + get { + return _batteryStatusVolts; + } + set { + if (_batteryStatusVolts != value) + { + _batteryStatusVolts = value; + OnPropertyChanged(nameof(BatteryStatusVolts)); + } + } + } + + private float _measuredBatteryVolts; + public float MeasuredBatteryVolts + { + get { + return _measuredBatteryVolts; + } + set { + if (_measuredBatteryVolts != value) + { + _measuredBatteryVolts = value; + OnPropertyChanged(nameof(MeasuredBatteryVolts)); + } + } + } + + public ICommand SaveVoltageCommand + { + get { + return new DelegateCommand(OnSaveVoltageCalibration); + } + } + + public ICommand ResetVoltageCommand + { + get { + return new DelegateCommand(OnResetVoltageCalibration); + } + } + + public CalibrationViewModel(ConnectionViewModel connectionVm) + { + _connectionVm = connectionVm; + } + + private async void OnSaveVoltageCalibration() + { + if (!_connectionVm.IsConnected) + { + MessageBox.Show("Not Connected!", "Error", MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + + if (MeasuredBatteryVolts < 1 || MeasuredBatteryVolts > 100) + { + MessageBox.Show("Measured Battery Voltage must be in range [1, 100]", "Error", MessageBoxButton.OK, + MessageBoxImage.Error); + return; + } + + var res = + await _connectionVm.GetConnection().CalibrateBatteryVoltage(MeasuredBatteryVolts, TimeSpan.FromSeconds(3)); + if (!res.Timeout) + { + if (res.Result) + { + MessageBox.Show("Voltage calibration saved!", "Success", MessageBoxButton.OK, + MessageBoxImage.Information); + } + else + { + MessageBox.Show("Failed to save voltage calibration, check log.", "Error", MessageBoxButton.OK, + MessageBoxImage.Error); + } + } + else + { + MessageBox.Show("Failed to save voltage calibration, timeout occured.", "Error", MessageBoxButton.OK, + MessageBoxImage.Error); + } + } + + private async void OnResetVoltageCalibration() + { + if (!_connectionVm.IsConnected) + { + MessageBox.Show("Not Connected!", "Error", MessageBoxButton.OK, MessageBoxImage.Error); + return; + } + + var res = await _connectionVm.GetConnection().CalibrateBatteryVoltage(0f, TimeSpan.FromSeconds(3)); + if (!res.Timeout) + { + if (res.Result) + { + MessageBox.Show("Voltage calibration reset!", "Success", MessageBoxButton.OK, + MessageBoxImage.Information); + } + else + { + MessageBox.Show("Failed to reset voltage calibration, check log.", "Error", MessageBoxButton.OK, + MessageBoxImage.Error); + } + } + else + { + MessageBox.Show("Failed to reset voltage calibration, timeout occured.", "Error", MessageBoxButton.OK, + MessageBoxImage.Error); + } + } +} +} diff --git a/code/tool/ViewModel/ConfigurationViewModel.cs b/code/tool/ViewModel/ConfigurationViewModel.cs new file mode 100644 index 00000000..e9622fe2 --- /dev/null +++ b/code/tool/ViewModel/ConfigurationViewModel.cs @@ -0,0 +1,675 @@ +using BBSFW.Model; +using BBSFW.ViewModel.Base; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace BBSFW.ViewModel +{ +public class ConfigurationViewModel : ObservableObject +{ + private Configuration _config; + + public static List PasStartDelayOptions { + get; + } = new List() { 0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, + 195, 210, 225, 240, 255, 270, 285, 300, 315, 330, 345, 360 }; + + public static List TemperatureSensorOptions + { + get { + return Enum.GetValues().ToList(); + } + } + + public static List StartupAssistLevelOptions { get; } = new List() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + public static List> AssistModeSelectOptions { + get; + } = new List> { + new ValueItemViewModel(Configuration.AssistModeSelect.Off, "Off"), + new ValueItemViewModel(Configuration.AssistModeSelect.Standard, "Sport Button"), + new ValueItemViewModel(Configuration.AssistModeSelect.Lights, "Lights Button"), + new ValueItemViewModel(Configuration.AssistModeSelect.BrakesOnBoot, + "Brakes @ Power On"), + new ValueItemViewModel(Configuration.AssistModeSelect.Pas0AndLights, + "PAS 0 + Lights Button"), + new ValueItemViewModel(Configuration.AssistModeSelect.Pas1AndLights, + "PAS 1 + Lights Button"), + new ValueItemViewModel(Configuration.AssistModeSelect.Pas2AndLights, + "PAS 2 + Lights Button"), + new ValueItemViewModel(Configuration.AssistModeSelect.Pas3AndLights, + "PAS 3 + Lights Button"), + new ValueItemViewModel(Configuration.AssistModeSelect.Pas4AndLights, + "PAS 4 + Lights Button"), + new ValueItemViewModel(Configuration.AssistModeSelect.Pas5AndLights, + "PAS 5 + Lights Button"), + new ValueItemViewModel(Configuration.AssistModeSelect.Pas6AndLights, + "PAS 6 + Lights Button"), + new ValueItemViewModel(Configuration.AssistModeSelect.Pas7AndLights, + "PAS 7 + Lights Button"), + new ValueItemViewModel(Configuration.AssistModeSelect.Pas8AndLights, + "PAS 8 + Lights Button"), + new ValueItemViewModel(Configuration.AssistModeSelect.Pas9AndLights, + "PAS 9 + Lights Button"), + }; + + public static List> WalkModeDataDisplayOptions { + get; + } = new List> { + new ValueItemViewModel(Configuration.WalkModeData.Speed, "Speed"), + new ValueItemViewModel(Configuration.WalkModeData.Temperature, "Temperature (C)"), + new ValueItemViewModel(Configuration.WalkModeData.RequestedPower, + "Requested Power (%)"), + new ValueItemViewModel(Configuration.WalkModeData.BatteryPercent, + "Battery Level (%)") + }; + + public static List> + ThrottleGlobalSpeedLimitOptions { + get; + } = new List> { + new ValueItemViewModel( + Configuration.ThrottleGlobalSpeedLimitOptions.Disabled, "Disabled"), + new ValueItemViewModel( + Configuration.ThrottleGlobalSpeedLimitOptions.Enabled, "Enabled"), + new ValueItemViewModel( + Configuration.ThrottleGlobalSpeedLimitOptions.StandardLevels, "Standard Levels"), + }; + + public static List> LightsModeOptions { + get; + } = new List> { + new ValueItemViewModel(Configuration.LightsModeOptions.Default, "Default"), + new ValueItemViewModel(Configuration.LightsModeOptions.Disabled, "Disabled"), + new ValueItemViewModel(Configuration.LightsModeOptions.AlwaysOn, "Always On"), + new ValueItemViewModel(Configuration.LightsModeOptions.BrakeLight, + "Brake Light"), + }; + + // support + + public bool IsTorqueSensorSupported + { + get { + return _config.IsFeatureSupported(Configuration.Feature.TorqueSensor); + } + } + + public bool IsShiftSensorSupported + { + get { + return _config.IsFeatureSupported(Configuration.Feature.ShiftSensor); + } + } + + // configuration + + public bool UseMetricUnits + { + get { + return !_config.UseFreedomUnits; + } + set { + if (_config.UseFreedomUnits == value) + { + _config.UseFreedomUnits = !value; + + Properties.Settings.Default.UseFreedomUnits = _config.UseFreedomUnits; + Properties.Settings.Default.Save(); + + OnPropertyChanged(nameof(UseImperialUnits)); + OnPropertyChanged(nameof(UseMetricUnits)); + } + } + } + + public bool UseImperialUnits + { + get { + return _config.UseFreedomUnits; + } + set { + if (_config.UseFreedomUnits != value) + { + _config.UseFreedomUnits = value; + + Properties.Settings.Default.UseFreedomUnits = _config.UseFreedomUnits; + Properties.Settings.Default.Save(); + + OnPropertyChanged(nameof(UseImperialUnits)); + OnPropertyChanged(nameof(UseMetricUnits)); + } + } + } + + public uint MaxCurrentAmps + { + get { + return _config.MaxCurrentAmps; + } + set { + if (_config.MaxCurrentAmps != value) + { + _config.MaxCurrentAmps = value; + OnPropertyChanged(nameof(MaxCurrentAmps)); + } + } + } + + public uint CurrentRampAmpsSecond + { + get { + return _config.CurrentRampAmpsSecond; + } + set { + if (_config.CurrentRampAmpsSecond != value) + { + _config.CurrentRampAmpsSecond = value; + OnPropertyChanged(nameof(CurrentRampAmpsSecond)); + } + } + } + + public float MaxBatteryVolts + { + get { + return _config.MaxBatteryVolts; + } + set { + if (_config.MaxBatteryVolts != value) + { + _config.MaxBatteryVolts = value; + OnPropertyChanged(nameof(MaxBatteryVolts)); + } + } + } + + public uint LowCutoffVolts + { + get { + return _config.LowCutoffVolts; + } + set { + if (_config.LowCutoffVolts != value) + { + _config.LowCutoffVolts = value; + OnPropertyChanged(nameof(LowCutoffVolts)); + } + } + } + + public uint MaxSpeedKph + { + get { + return _config.MaxSpeedKph; + } + set { + if (_config.MaxSpeedKph != value) + { + _config.MaxSpeedKph = value; + OnPropertyChanged(nameof(MaxSpeedKph)); + OnPropertyChanged(nameof(MaxSpeedMph)); + } + } + } + + public uint MaxSpeedMph + { + get { + return KphToMph(_config.MaxSpeedKph); + } + set { + if (_config.MaxSpeedKph != MphToKph(value)) + { + _config.MaxSpeedKph = MphToKph(value); + OnPropertyChanged(nameof(MaxSpeedKph)); + OnPropertyChanged(nameof(MaxSpeedMph)); + } + } + } + + public bool UseSpeedSensor + { + get { + return _config.UseSpeedSensor; + } + set { + if (_config.UseSpeedSensor != value) + { + _config.UseSpeedSensor = value; + OnPropertyChanged(nameof(UseSpeedSensor)); + } + + // Require use of speed sensor for pretension feature. + if (_config.UseSpeedSensor == false) + { + _config.UsePretension = false; + OnPropertyChanged(nameof(UsePretension)); + } + } + } + + public bool UseShiftSensor + { + get { + return _config.UseShiftSensor; + } + set { + if (_config.UseShiftSensor != value) + { + _config.UseShiftSensor = value; + OnPropertyChanged(nameof(UseShiftSensor)); + } + } + } + + public bool UsePushWalk + { + get { + return _config.UsePushWalk; + } + set { + if (_config.UsePushWalk != value) + { + _config.UsePushWalk = value; + OnPropertyChanged(nameof(UsePushWalk)); + } + } + } + + public bool UsePretension + { + get { + return _config.UsePretension; + } + set { + if (_config.UsePretension != value) + { + _config.UsePretension = value; + OnPropertyChanged(nameof(UsePretension)); + } + } + } + + public uint PretensionSpeedCutoffKph + { + get { + return _config.PretensionSpeedCutoffKph; + } + set { + if (_config.PretensionSpeedCutoffKph != value) + { + _config.PretensionSpeedCutoffKph = value; + OnPropertyChanged(nameof(PretensionSpeedCutoffKph)); + OnPropertyChanged(nameof(PretensionSpeedCutoffMph)); + } + } + } + + public uint PretensionSpeedCutoffMph + { + get { + return KphToMph(_config.PretensionSpeedCutoffKph); + } + set { + if (_config.PretensionSpeedCutoffKph != MphToKph(value)) + { + _config.PretensionSpeedCutoffKph = MphToKph(value); + OnPropertyChanged(nameof(PretensionSpeedCutoffKph)); + OnPropertyChanged(nameof(PretensionSpeedCutoffMph)); + } + } + } + + public Configuration.TemperatureSensor UseTemperatureSensor + { + get { + return _config.UseTemperatureSensor; + } + set { + if (_config.UseTemperatureSensor != value) + { + _config.UseTemperatureSensor = value; + OnPropertyChanged(nameof(UseTemperatureSensor)); + } + } + } + + public ValueItemViewModel LightsMode + { + get { + return LightsModeOptions.FirstOrDefault((e) => e.Value == _config.LightsMode); + } + set { + if (_config.LightsMode != value.Value) + { + _config.LightsMode = value.Value; + OnPropertyChanged(nameof(LightsMode)); + } + } + } + + public uint ThrottleStartVoltageMillivolts + { + get { + return _config.ThrottleStartMillivolts; + } + set { + if (_config.ThrottleStartMillivolts != value) + { + _config.ThrottleStartMillivolts = value; + OnPropertyChanged(nameof(ThrottleStartVoltageMillivolts)); + } + } + } + + public uint ThrottleEndVoltageMillivolts + { + get { + return _config.ThrottleEndMillivolts; + } + set { + if (_config.ThrottleEndMillivolts != value) + { + _config.ThrottleEndMillivolts = value; + OnPropertyChanged(nameof(ThrottleEndVoltageMillivolts)); + } + } + } + + public uint ThrottleStartCurrentPercent + { + get { + return _config.ThrottleStartPercent; + } + set { + if (_config.ThrottleStartPercent != value) + { + _config.ThrottleStartPercent = value; + OnPropertyChanged(nameof(ThrottleStartCurrentPercent)); + } + } + } + + public ValueItemViewModel ThrottleGlobalSpeedLimit + { + get { + return ThrottleGlobalSpeedLimitOptions.FirstOrDefault((e) => e.Value == _config.ThrottleGlobalSpeedLimit); + } + set { + if (_config.ThrottleGlobalSpeedLimit != value.Value) + { + _config.ThrottleGlobalSpeedLimit = value.Value; + OnPropertyChanged(nameof(ThrottleGlobalSpeedLimit)); + } + } + } + + public uint ThrottleGlobalSpeedLimitPercent + { + get { + return _config.ThrottleGlobalSpeedLimitPercent; + } + set { + if (_config.ThrottleGlobalSpeedLimitPercent != value) + { + _config.ThrottleGlobalSpeedLimitPercent = value; + OnPropertyChanged(nameof(ThrottleGlobalSpeedLimitPercent)); + } + } + } + + public uint PasStartDelayDegrees + { + get { + return _config.PasStartDelayPulses * 15; + } + set { + if (_config.PasStartDelayPulses * 15 != value) + { + _config.PasStartDelayPulses = value / 15; + OnPropertyChanged(nameof(PasStartDelayDegrees)); + } + } + } + + public uint PasStopDelayMilliseconds + { + get { + return _config.PasStopDelayMilliseconds; + } + set { + if (_config.PasStopDelayMilliseconds != value) + { + _config.PasStopDelayMilliseconds = value; + OnPropertyChanged(nameof(PasStopDelayMilliseconds)); + } + } + } + + public uint PasKeepCurrentPercent + { + get { + return _config.PasKeepCurrentPercent; + } + set { + if (_config.PasKeepCurrentPercent != value) + { + _config.PasKeepCurrentPercent = value; + OnPropertyChanged(nameof(PasKeepCurrentPercent)); + } + } + } + + public uint PasKeepCurrentCadenceRpm + { + get { + return _config.PasKeepCurrentCadenceRpm; + } + set { + if (_config.PasKeepCurrentCadenceRpm != value) + { + _config.PasKeepCurrentCadenceRpm = value; + OnPropertyChanged(nameof(PasKeepCurrentCadenceRpm)); + } + } + } + + public float WheelSizeInch + { + get { + return _config.WheelSizeInch; + } + set { + if (_config.WheelSizeInch != value) + { + _config.WheelSizeInch = value; + OnPropertyChanged(nameof(WheelSizeInch)); + } + } + } + + public uint SpeedSensorSignals + { + get { + return _config.NumWheelSensorSignals; + } + set { + if (_config.NumWheelSensorSignals != value) + { + _config.NumWheelSensorSignals = value; + OnPropertyChanged(nameof(SpeedSensorSignals)); + } + } + } + + public uint ShiftInterruptDuration + { + get { + return _config.ShiftInterruptDuration; + } + set { + if (_config.ShiftInterruptDuration != value) + { + _config.ShiftInterruptDuration = value; + OnPropertyChanged(nameof(ShiftInterruptDuration)); + } + } + } + + public uint ShiftInterruptCurrentThresholdPercent + { + get { + return _config.ShiftInterruptCurrentThresholdPercent; + } + set { + if (_config.ShiftInterruptCurrentThresholdPercent != value) + { + _config.ShiftInterruptCurrentThresholdPercent = value; + OnPropertyChanged(nameof(ShiftInterruptCurrentThresholdPercent)); + } + } + } + + public ValueItemViewModel WalkModeDataDisplay + { + get { + return WalkModeDataDisplayOptions.FirstOrDefault((e) => e.Value == _config.WalkModeDataDisplay); + } + set { + if (_config.WalkModeDataDisplay != value.Value) + { + _config.WalkModeDataDisplay = value.Value; + OnPropertyChanged(nameof(WalkModeDataDisplay)); + } + } + } + + public uint StartupAssistLevel + { + get { + return _config.AssistStartupLevel; + } + set { + if (_config.AssistStartupLevel != value) + { + _config.AssistStartupLevel = value; + OnPropertyChanged(nameof(StartupAssistLevel)); + } + } + } + + public ValueItemViewModel AssistModeSelection + { + get { + return AssistModeSelectOptions.FirstOrDefault((e) => e.Value == _config.AssistModeSelection); + } + set { + if (_config.AssistModeSelection != value.Value) + { + _config.AssistModeSelection = value.Value; + OnPropertyChanged(nameof(AssistModeSelection)); + } + } + } + + private List _standardAssistLevels; + public List StandardAssistLevels + { + get { + return _standardAssistLevels; + } + private + set { + if (_standardAssistLevels != value) + { + _standardAssistLevels = value; + OnPropertyChanged(nameof(StandardAssistLevels)); + } + } + } + + private List _sportAssistLevels; + + public List SportAssistLevels + { + get { + return _sportAssistLevels; + } + private + set { + if (_sportAssistLevels != value) + { + _sportAssistLevels = value; + OnPropertyChanged(nameof(SportAssistLevels)); + } + } + } + + public ConfigurationViewModel() + { + _config = new Configuration(BbsfwConnection.Controller.Unknown); + + StandardAssistLevels = new List(); + SportAssistLevels = new List(); + + for (int i = 0; i < _config.StandardAssistLevels.Length; ++i) + { + _standardAssistLevels.Add(new AssistLevelViewModel(this, i, _config.StandardAssistLevels[i])); + } + + for (int i = 0; i < _config.SportAssistLevels.Length; ++i) + { + _sportAssistLevels.Add(new AssistLevelViewModel(this, i, _config.SportAssistLevels[i])); + } + } + + public void ReadConfiguration(string filepath) + { + _config.ReadFromFile(filepath); + TriggerPropertyChanges(); + } + + public void WriteConfiguration(string filepath) + { + _config.WriteToFile(filepath); + } + + public void UpdateFrom(Configuration config) + { + _config.CopyFrom(config); + TriggerPropertyChanges(); + } + + public Configuration GetConfig() + { + return _config; + } + + private static uint KphToMph(uint kph) + { + return (uint)Math.Round(kph * 0.621371192); + } + + private static uint MphToKph(uint mph) + { + return (uint)Math.Round(mph * 1.609344); + } + + private void TriggerPropertyChanges() + { + foreach (var prop in typeof(ConfigurationViewModel).GetProperties()) + { + if (prop.GetGetMethod(false) != null) + { + OnPropertyChanged(prop.Name); + } + } + + // force update by creating new list + StandardAssistLevels = StandardAssistLevels.ToList(); + SportAssistLevels = SportAssistLevels.ToList(); + } +} +} diff --git a/code/tool/ViewModel/ConnectionViewModel.cs b/code/tool/ViewModel/ConnectionViewModel.cs new file mode 100644 index 00000000..a63930b2 --- /dev/null +++ b/code/tool/ViewModel/ConnectionViewModel.cs @@ -0,0 +1,224 @@ +using BBSFW.Model; +using BBSFW.ViewModel.Base; +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Input; + +namespace BBSFW.ViewModel +{ +public class ConnectionViewModel : ObservableObject +{ + + private BbsfwConnection _connection; + + private List _comPorts; + public List ComPorts + { + get { + return _comPorts; + } + set { + if (_comPorts != value) + { + _comPorts = value; + OnPropertyChanged(nameof(ComPorts)); + } + } + } + + private ComPort _selectedComPort; + public ComPort SelectedComPort + { + get { + return _selectedComPort; + } + set { + if (_selectedComPort != value) + { + _selectedComPort = value; + OnPropertyChanged(nameof(SelectedComPort)); + } + } + } + + private bool _isConnected; + public bool IsConnected + { + get { + return _isConnected; + } + set { + if (_isConnected != value) + { + _isConnected = value; + OnPropertyChanged(nameof(IsConnected)); + OnPropertyChanged(nameof(IsDisconnected)); + } + } + } + + public bool IsDisconnected + { + get { + return !IsConnected; + } + } + + private bool _isConnecting; + public bool IsConnecting + { + get { + return _isConnecting; + } + set { + if (_isConnecting != value) + { + _isConnecting = value; + OnPropertyChanged(nameof(IsConnecting)); + } + } + } + + private BbsfwConnection.Controller _controller; + public BbsfwConnection.Controller Controller + { + get { + return _controller; + } + set { + if (_controller != value) + { + _controller = value; + OnPropertyChanged(nameof(Controller)); + } + } + } + + private string _firmwareVersion = "N/A"; + public string FirmwareVersion + { + get { + return _firmwareVersion; + } + private + set { + if (_firmwareVersion != value) + { + _firmwareVersion = value; + OnPropertyChanged(nameof(FirmwareVersion)); + } + } + } + + private int _configVersion = 0; + public int ConfigVersion + { + get { + return _configVersion; + } + private + set { + if (_configVersion != value) + { + _configVersion = value; + OnPropertyChanged(nameof(ConfigVersion)); + } + } + } + + public event Action EventLogReceived; + + public ICommand RefreshCommand + { + get { + return new DelegateCommand(OnRefresh); + } + } + + public ICommand ConnectCommand + { + get { + return new DelegateCommand(OnConnect); + } + } + + public ICommand DisconnectCommand + { + get { + return new DelegateCommand(OnDisconnect); + } + } + + public ConnectionViewModel() + { + _connection = new BbsfwConnection(); + + _connection.Connected += OnConnected; + _connection.Disconnected += OnDisconnected; + _connection.EventLog += (e) => + { EventLogReceived?.Invoke(e); }; + + ComPorts = BbsfwConnection.GetComPorts(); + } + + public BbsfwConnection GetConnection() + { + return _connection; + } + + private void OnConnected(BbsfwConnection.Controller controller, string fwversion, int configVersion) + { + IsConnected = true; + IsConnecting = false; + + Controller = controller; + FirmwareVersion = fwversion; + ConfigVersion = configVersion; + } + + private void OnDisconnected() + { + IsConnected = false; + IsConnecting = false; + FirmwareVersion = "N/A"; + ConfigVersion = 0; + } + + private void OnRefresh() + { + ComPorts = BbsfwConnection.GetComPorts(); + OnPropertyChanged(nameof(ComPorts)); + } + + private async void OnConnect() + { + if (SelectedComPort != null) + { + IsConnecting = true; + + try + { + var connected = await _connection.Connect(SelectedComPort, TimeSpan.FromSeconds(120)); + + if (!connected) + { + MessageBox.Show("Failed to connect, timeout occured.", "Error", MessageBoxButton.OK, + MessageBoxImage.Error); + } + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); + IsConnected = false; + IsConnecting = false; + } + } + } + + private void OnDisconnect() + { + _connection.Close(); + } +} +} diff --git a/code/tool/ViewModel/EventLogViewModel.cs b/code/tool/ViewModel/EventLogViewModel.cs new file mode 100644 index 00000000..1c924034 --- /dev/null +++ b/code/tool/ViewModel/EventLogViewModel.cs @@ -0,0 +1,152 @@ +using BBSFW.Model; +using BBSFW.ViewModel.Base; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.IO; +using System.Windows; +using System.Windows.Data; +using System.Windows.Input; + +namespace BBSFW.ViewModel +{ +public class EventLogViewModel : ObservableObject +{ + + private ObservableCollection _events = new ObservableCollection(); + public ObservableCollection LogEvents + { + get { + return _events; + } + set { + if (_events != value) + { + _events = value; + OnPropertyChanged(nameof(LogEvents)); + } + } + } + + private ICollectionView _filtedLogEvents; + public ICollectionView FilteredLogEvents + { + get { + return _filtedLogEvents; + } + } + + public IEnumerable AvailableLogLevels + { + get { + return new[] { EventLogEntry.LogLevel.Info, EventLogEntry.LogLevel.Warning, EventLogEntry.LogLevel.Error }; + } + } + + private EventLogEntry.LogLevel _selectedLogLevel; + public EventLogEntry.LogLevel SelectedLogLevel + { + get { + return _selectedLogLevel; + } + set { + if (_selectedLogLevel != value) + { + _selectedLogLevel = value; + OnPropertyChanged(nameof(SelectedLogLevel)); + + FilteredLogEvents.Refresh(); + } + } + } + + private string _filterText; + public string FilterText + { + get { + return _filterText; + } + set { + if (_filterText != value) + { + _filterText = value; + OnPropertyChanged(nameof(FilterText)); + _filtedLogEvents.Refresh(); + } + } + } + + private bool _tailLog; + public bool TailLog + { + get { + return _tailLog; + } + set { + if (_tailLog != value) + { + _tailLog = value; + OnPropertyChanged(nameof(TailLog)); + } + } + } + + public ICommand ClearCommand + { + get { + return new DelegateCommand(OnClear); + } + } + + public EventLogViewModel() + { + _filtedLogEvents = (CollectionView)CollectionViewSource.GetDefaultView(LogEvents); + _filtedLogEvents.Filter += OnFilterTriggered; + } + + public void AddEvent(EventLogEntry e) + { + Application.Current.Dispatcher.InvokeAsync(() => LogEvents.Add(e)); + } + + public void ExportLog(string filepath) + { + using (var writer = new StreamWriter(filepath)) + { + foreach (var e in LogEvents) + { + writer.WriteLine($"{e.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff")} {e.Level} {e.Message}"); + } + } + } + + private bool OnFilterTriggered(object obj) + { + var e = obj as EventLogEntry; + + if (e != null) + { + if (e.Level >= SelectedLogLevel) + { + if (String.IsNullOrEmpty(FilterText)) + { + return true; + } + + if (e.Message.IndexOf(FilterText, StringComparison.OrdinalIgnoreCase) >= 0) + { + return true; + } + } + } + + return false; + } + + private void OnClear() + { + LogEvents.Clear(); + } +} +} diff --git a/code/tool/ViewModel/MainViewModel.cs b/code/tool/ViewModel/MainViewModel.cs new file mode 100644 index 00000000..9d806885 --- /dev/null +++ b/code/tool/ViewModel/MainViewModel.cs @@ -0,0 +1,308 @@ +using BBSFW.Model; +using BBSFW.ViewModel.Base; +using Microsoft.Win32; +using System; +using System.Reflection; +using System.Windows; +using System.Windows.Input; + +namespace BBSFW.ViewModel +{ + +public class MainViewModel : ObservableObject +{ + + public ConfigurationViewModel ConfigVm { get; private set; } + + public ConnectionViewModel ConnectionVm { get; private set; } + + public SystemViewModel SystemVm { get; private set; } + + public AssistLevelsViewModel AssistLevelsVm { get; private set; } + + public CalibrationViewModel CalibrationVm { get; private set; } + + public EventLogViewModel EventLogVm { get; private set; } + + public ICommand OpenConfigCommand + { + get { + return new DelegateCommand(OnOpenConfig); + } + } + + public ICommand SaveConfigCommand + { + get { + return new DelegateCommand(OnSaveConfig); + } + } + + public ICommand SaveLogCommand + { + get { + return new DelegateCommand(OnSaveLog); + } + } + + public ICommand ReadFlashCommand + { + get { + return new DelegateCommand(OnReadFlash); + } + } + + public ICommand WriteFlashCommand + { + get { + return new DelegateCommand(OnWriteFlash); + } + } + + public ICommand ResetFlashCommand + { + get { + return new DelegateCommand(OnResetFlash); + } + } + + public ICommand ExitCommand + { + get { + return new DelegateCommand(OnExit); + } + } + + public ICommand ShowAboutCommand + { + get { + return new DelegateCommand(OnShowAbout); + } + } + + public MainViewModel() + { + ConfigVm = new ConfigurationViewModel(); + + ConnectionVm = new ConnectionViewModel(); + SystemVm = new SystemViewModel(ConfigVm); + AssistLevelsVm = new AssistLevelsViewModel(ConfigVm); + CalibrationVm = new CalibrationViewModel(ConnectionVm); + EventLogVm = new EventLogViewModel(); + + ConnectionVm.EventLogReceived += EventLogVm.AddEvent; + } + + private void OnSaveLog() + { + var dialog = new SaveFileDialog(); + + dialog.Filter = "Log File|*.log"; + dialog.Title = "Save Log"; + dialog.FileName = "bbsfw.log"; + + var result = dialog.ShowDialog(); + if (result.HasValue && result.Value) + { + try + { + EventLogVm.ExportLog(dialog.FileName); + } + catch (Exception e) + { + MessageBox.Show(e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + } + + private void OnOpenConfig() + { + var dialog = new OpenFileDialog(); + dialog.Filter = "XML File|*.xml"; + dialog.Title = "Open Configuration"; + + var result = dialog.ShowDialog(); + if (result.HasValue && result.Value) + { + try + { + ConfigVm.ReadConfiguration(dialog.FileName); + } + catch (Exception e) + { + MessageBox.Show(e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + } + + private void OnSaveConfig() + { + if (!ValidateConfig()) + { + return; + } + + var dialog = new SaveFileDialog(); + + dialog.Filter = "XML File|*.xml"; + dialog.Title = "Save Configuration"; + dialog.FileName = "bbsfw.xml"; + + var result = dialog.ShowDialog(); + if (result.HasValue && result.Value) + { + try + { + ConfigVm.WriteConfiguration(dialog.FileName); + } + catch (Exception e) + { + MessageBox.Show(e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + } + + private async void OnReadFlash() + { + if (!ConnectionVm.IsConnected) + { + return; + } + + if (!VerifyConfigVersionForRead()) + { + return; + } + + var res = await ConnectionVm.GetConnection().ReadConfiguration(TimeSpan.FromSeconds(5)); + if (!res.Timeout && res.Result != null) + { + ConfigVm.UpdateFrom(res.Result); + } + else + { + MessageBox.Show("Failed to read configuration from flash, timeout occured.", "Error", MessageBoxButton.OK, + MessageBoxImage.Error); + } + } + + private async void OnWriteFlash() + { + if (!ConnectionVm.IsConnected) + { + return; + } + + if (!ValidateConfig()) + { + return; + } + + if (!VerifyConfigVersionForWrite()) + { + return; + } + + var res = await ConnectionVm.GetConnection().WriteConfiguration(ConfigVm.GetConfig(), TimeSpan.FromSeconds(5)); + if (!res.Timeout) + { + if (res.Result) + { + MessageBox.Show("Configuration Written!", "Success", MessageBoxButton.OK, MessageBoxImage.Information); + } + else + { + MessageBox.Show("Failed to write configuration to flash, try again.", "Error", MessageBoxButton.OK, + MessageBoxImage.Error); + } + } + else + { + MessageBox.Show("Failed to write configuration to flash, timeout occured.", "Error", MessageBoxButton.OK, + MessageBoxImage.Error); + } + } + + private async void OnResetFlash() + { + if (!ConnectionVm.IsConnected) + { + return; + } + + var res = await ConnectionVm.GetConnection().ResetConfiguration(TimeSpan.FromSeconds(5)); + if (!res.Timeout) + { + if (res.Result) + { + OnReadFlash(); + } + else + { + MessageBox.Show("Failed to reset configuration, try again.", "Error", MessageBoxButton.OK, + MessageBoxImage.Error); + } + } + else + { + MessageBox.Show("Failed to reset configuration, timeout occured.", "Error", MessageBoxButton.OK, + MessageBoxImage.Error); + } + } + + private void OnShowAbout() + { + var version = Assembly.GetExecutingAssembly().GetName().Version; + MessageBox.Show($"Version: {version.Major}.{version.Minor}.{version.Build}\nAuthor: Daniel Nilsson", + "BBS-FW Tool", MessageBoxButton.OK, MessageBoxImage.Information); + } + + private void OnExit() + { + Application.Current.Shutdown(); + } + + private bool VerifyConfigVersionForRead() + { + if (ConnectionVm.ConfigVersion < Configuration.MinVersion || + ConnectionVm.ConfigVersion > Configuration.MaxVersion) + { + MessageBox.Show("Unsupported firmware config version. Please use BBS-FW Config Tool for firmware version " + + ConnectionVm.FirmwareVersion + " to read configuration from flash.", + "Error", MessageBoxButton.OK, MessageBoxImage.Error); + return false; + } + + return true; + } + + private bool VerifyConfigVersionForWrite() + { + if (ConnectionVm.ConfigVersion != Configuration.CurrentVersion) + { + MessageBox.Show("Unsupported firmware config version. Please use BBS-FW Config Tool for firmware version " + + ConnectionVm.FirmwareVersion + + " in order to write configuration to flash, or upgrade firmware to latest version.", + "Error", MessageBoxButton.OK, MessageBoxImage.Error); + return false; + } + + return true; + } + + private bool ValidateConfig() + { + try + { + ConfigVm.GetConfig().Validate(); + return true; + } + catch (Exception e) + { + MessageBox.Show(e.Message, "Validation Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + + return false; + } +} +} diff --git a/code/tool/ViewModel/SystemViewModel.cs b/code/tool/ViewModel/SystemViewModel.cs new file mode 100644 index 00000000..a1af40e3 --- /dev/null +++ b/code/tool/ViewModel/SystemViewModel.cs @@ -0,0 +1,24 @@ +using BBSFW.ViewModel.Base; +using System; +using System.Collections.Generic; +using System.Text; + +namespace BBSFW.ViewModel +{ +public class SystemViewModel : ObservableObject +{ + + private ConfigurationViewModel _configVm; + public ConfigurationViewModel ConfigVm + { + get { + return _configVm; + } + } + + public SystemViewModel(ConfigurationViewModel config) + { + _configVm = config; + } +} +} diff --git a/code/tool/ViewModel/ValueItemViewModel.cs b/code/tool/ViewModel/ValueItemViewModel.cs new file mode 100644 index 00000000..63e7406b --- /dev/null +++ b/code/tool/ViewModel/ValueItemViewModel.cs @@ -0,0 +1,30 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace BBSFW.ViewModel +{ +public class ValueItemViewModel : IEquatable> +{ + public T Value { get; private set; } + + public string Name { get; private set; } + + public ValueItemViewModel(T value, string name) + { + Value = value; + Name = name; + } + + public static implicit operator T(ValueItemViewModel v) => v.Value; + + public override string ToString() + { + return Name; + } + + public bool Equals([AllowNull] ValueItemViewModel other) + { + return Value.Equals(other.Value); + } +} +} diff --git a/src/tool/bbs-fw-tool.csproj b/code/tool/bbs-fw-tool.csproj similarity index 97% rename from src/tool/bbs-fw-tool.csproj rename to code/tool/bbs-fw-tool.csproj index 7237ad7b..dbcb4b51 100644 --- a/src/tool/bbs-fw-tool.csproj +++ b/code/tool/bbs-fw-tool.csproj @@ -1,39 +1,39 @@ - - - - WinExe - net6.0-windows - true - false - true - win-x64 - true - BBSFW - true - BBSFWTool - Daniel Nilsson - 1.5.99.0 - 1.5.99.0 - - - - - - - - - - True - True - Settings.settings - - - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - - + + + + WinExe + net6.0-windows + true + false + true + win-x64 + true + BBSFW + true + BBSFWTool + Daniel Nilsson + 1.5.99.0 + 1.5.99.0 + + + + + + + + + + True + True + Settings.settings + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + diff --git a/src/tool/bbs-fw-tool.sln b/code/tool/bbs-fw-tool.sln similarity index 100% rename from src/tool/bbs-fw-tool.sln rename to code/tool/bbs-fw-tool.sln diff --git a/drawings/pcb/bbshd.sch b/drawings/pcb/bbshd.sch index cec4ff2b..0ffc4f0f 100644 --- a/drawings/pcb/bbshd.sch +++ b/drawings/pcb/bbshd.sch @@ -662,7 +662,7 @@ Source: www.st.com, BAT60J.pdf Based on the previous libraries: <ul> <li>r.lbr -<li>cap.lbr +<li>cap.lbr <li>cap-fe.lbr <li>captant.lbr <li>polcap.lbr @@ -8236,10 +8236,10 @@ Siemens, Philips, Valvo<p> -Components missing, located on +Components missing, located on the bottom side of pcb right under STC MCU. -Two SOT-23, likely two transistors, +Two SOT-23, likely two transistors, some form of input protection maybe? PAS1 PAS2 diff --git a/src/firmware/.gitignore b/src/firmware/.gitignore deleted file mode 100644 index ebe43bbe..00000000 --- a/src/firmware/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -**/*.asm -**/*.elf -**/*.ihx -**/*.cdb -**/*.lk -**/*.map -**/*.adb -**/*.lst -**/*.rel -**/*.rst -**/*.sym - -*.hex -*.mem -.vs -build -*.vcxproj.user \ No newline at end of file diff --git a/src/firmware/Makefile b/src/firmware/Makefile deleted file mode 100644 index 63d38493..00000000 --- a/src/firmware/Makefile +++ /dev/null @@ -1,101 +0,0 @@ -.PHONY: all clean - -ifeq '$(findstring ;,$(PATH))' ';' - UNAME := Windows -else - UNAME := $(shell uname 2>/dev/null || echo Unknown) -endif - -# Select target controller (normally done from cmd line): -#TARGET_CONTROLLER = BBSHD -#TARGET_CONTROLLER = BBS02 -#TARGET_CONTROLLER = TSDZ2 - -# Compiler -CC = sdcc - -# Target name -TARGET = bbs-fw - -MAINSRC = main.c -SUBDIRS = - -CFLAGS = -Ddouble=float --std-c99 -D$(TARGET_CONTROLLER) - -# Target Specific -ifeq ($(TARGET_CONTROLLER), BBSHD) - CFLAGS += -mmcs51 --model-large --xram-size 3840 - SUBDIRS += bbsx -endif - -ifeq ($(TARGET_CONTROLLER), BBS02) - CFLAGS += -mmcs51 --model-large --xram-size 1792 - SUBDIRS += bbsx -endif - -ifeq ($(TARGET_CONTROLLER), TSDZ2) - CFLAGS += -mstm8 - SUBDIRS += tsdz2 -endif - - - -INCS = $(wildcard *.h $(foreach fd, $(SUBDIRS), $(fd)/*.h)) -SRCS = $(filter-out main.c, $(wildcard *.c $(foreach fd, $(SUBDIRS), $(fd)/*.c))) -RELS := $(SRCS:.c=.rel) - -INC_DIRS = -I./ $(addprefix -I, $(SUBDIRS)) - - -all: precheck $(TARGET) hex - -$(TARGET): $(MAINSRC) $(RELS) - $(CC) -o $(TARGET).ihx $(INC_DIRS) $(CFLAGS) $(MAINSRC) $(RELS) - -%.rel: %.c $(INCS) - $(CC) -o $@ -c $(INC_DIRS) $(CFLAGS) $< - -echo: - $(info SRCS: $(SRCS)) - $(info RELS: $(RELS)) - $(info INCS: $(INCS)) - -precheck: -ifndef TARGET_CONTROLLER - $(info TARGET_CONTROLLER is not specified.) - $(info Set to one of [BBSHD, BBS02, TSDZ2]) - $(info Example:) - $(info $(null) make all TARGET_CONTROLLER=BBSHD) - $(error ) -endif - $(info Building bbs-fw for $(TARGET_CONTROLLER)) - -hex: -ifeq ($(UNAME), Linux) - @packihx bbs-fw.ihx > bbs-fw.hex -else - @cmd /C tohex.bat -endif - -clean: -ifeq ($(UNAME), Linux) - @rm -f bbsx/*.hex tsdz2/*.hex *.hex - @rm -f bbsx/*.ihx tsdz2/*.ihx *.ihx - @rm -f bbsx/*.asm tsdz2/*.asm *.asm - @rm -f bbsx/*.rel tsdz2/*.rel *.rel - @rm -f bbsx/*.lk tsdz2/*.lk *.lk - @rm -f bbsx/*.lst tsdz2/*.lst *.lst - @rm -f bbsx/*.rst tsdz2/*.rst *.rst - @rm -f bbsx/*.sym tsdz2/*.sym *.sym - @rm -f bbsx/*.cdb tsdz2/*.cdb *.cdb - @rm -f bbsx/*.map tsdz2/*.map *.map - @rm -f bbsx/*.elf tsdz2/*.elf *.elf - @rm -f bbsx/*.adb tsdz2/*.adb *.adb - @rm -f bbsx/*.mem tsdz2/*.mem *.mem -else - @cmd /C clean.bat -endif - $(info Clean Finished) - -.PHONY = all hex clean precheck echo -.SUFFIXES: .c .rel diff --git a/src/firmware/app.c b/src/firmware/app.c deleted file mode 100644 index 1c6b52fa..00000000 --- a/src/firmware/app.c +++ /dev/null @@ -1,974 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "app.h" -#include "fwconfig.h" -#include "cfgstore.h" -#include "motor.h" -#include "sensors.h" -#include "throttle.h" -#include "lights.h" -#include "uart.h" -#include "eventlog.h" -#include "util.h" -#include "system.h" - - -typedef struct -{ - assist_level_t level; - - // cached precomputed values - // --------------------------------- - - // speed - int32_t max_wheel_speed_rpm_x10; - - // pas - uint8_t keep_current_target_percent; - uint16_t keep_current_ramp_start_rpm_x10; - uint16_t keep_current_ramp_end_rpm_x10; - -} assist_level_data_t; - -static uint8_t assist_level; -static uint8_t operation_mode; -static uint16_t global_speed_limit_rpm; -static int32_t global_throttle_speed_limit_rpm_x10; - -static uint16_t lvc_voltage_x100; -static uint16_t lvc_ramp_down_start_voltage_x100; -static uint16_t lvc_ramp_down_end_voltage_x100; - -static assist_level_data_t assist_level_data; -static uint16_t speed_limit_ramp_interval_rpm_x10; - -static bool cruise_paused; -static int8_t temperature_contr_c; -static int8_t temperature_motor_c; - -static uint16_t ramp_up_current_interval_ms; -static uint32_t power_blocked_until_ms; - -static uint16_t pretension_cutoff_speed_rpm_x10; - -static bool lights_state = false; - -void apply_pas_cadence(uint8_t* target_current, uint8_t throttle_percent); -#if HAS_TORQUE_SENSOR -void apply_pas_torque(uint8_t* target_current); -#endif - -void apply_pretension(uint8_t* target_current); -void apply_cruise(uint8_t* target_current, uint8_t throttle_percent); -bool apply_throttle(uint8_t* target_current, uint8_t throttle_percent); -bool apply_speed_limit(uint8_t* target_current, uint8_t throttle_percent, bool pas_engaged, bool throttle_override); -bool apply_thermal_limit(uint8_t* target_current); -bool apply_low_voltage_limit(uint8_t* target_current); -bool apply_shift_sensor_interrupt(uint8_t* target_current); -bool apply_brake(uint8_t* target_current); -void apply_current_ramp_up(uint8_t* target_current, bool enable); -void apply_current_ramp_down(uint8_t* target_current, bool enable); - -bool check_power_block(); -void block_power_for(uint16_t ms); - -void reload_assist_params(); - -uint16_t convert_wheel_speed_kph_to_rpm(uint8_t speed_kph); - -void app_init() -{ - motor_disable(); - lights_disable(); - lights_set(g_config.lights_mode == LIGHTS_MODE_ALWAYS_ON); - - lvc_voltage_x100 = g_config.low_cut_off_v * 100u; - - uint16_t full_voltage_range_x100 = - EXPAND_U16(g_config.max_battery_x100v_u16h, g_config.max_battery_x100v_u16l) - lvc_voltage_x100; - uint16_t padded_voltage_range_x100 = (uint16_t)(full_voltage_range_x100 * - (100 - BATTERY_FULL_OFFSET_PERCENT - BATTERY_EMPTY_OFFSET_PERCENT) / 100); - - lvc_ramp_down_end_voltage_x100 = (uint16_t)(lvc_voltage_x100 + - (full_voltage_range_x100 * BATTERY_EMPTY_OFFSET_PERCENT / 100)); - lvc_ramp_down_start_voltage_x100 = (uint16_t)(lvc_ramp_down_end_voltage_x100 + - ((padded_voltage_range_x100 * LVC_RAMP_DOWN_OFFSET_PERCENT) / 100)); - - global_speed_limit_rpm = 0; - global_throttle_speed_limit_rpm_x10 = 0; - temperature_contr_c = 0; - temperature_motor_c = 0; - - ramp_up_current_interval_ms = (g_config.max_current_amps * 10u) / g_config.current_ramp_amps_s; - power_blocked_until_ms = 0; - - speed_limit_ramp_interval_rpm_x10 = convert_wheel_speed_kph_to_rpm(SPEED_LIMIT_RAMP_DOWN_INTERVAL_KPH) * 10; - - pretension_cutoff_speed_rpm_x10 = convert_wheel_speed_kph_to_rpm(g_config.pretension_speed_cutoff_kph) * 10; - - cruise_paused = true; - operation_mode = OPERATION_MODE_DEFAULT; - - app_set_wheel_max_speed_rpm(convert_wheel_speed_kph_to_rpm(g_config.max_speed_kph)); - app_set_assist_level(g_config.assist_startup_level); - reload_assist_params(); - - if (g_config.assist_mode_select == ASSIST_MODE_SELECT_BRAKE_BOOT && brake_is_activated()) - { - app_set_operation_mode(OPERATION_MODE_SPORT); - } -} - -void app_process() -{ - uint8_t target_current = 0; - uint8_t target_cadence = assist_level_data.level.max_cadence_percent; - uint8_t throttle_percent = throttle_map_response(throttle_read()); - - bool pas_engaged = false; - bool throttle_override = false; - - if (check_power_block()) - { - target_current = 0; - } - else if (assist_level == ASSIST_PUSH && g_config.use_push_walk) - { - target_current = 10; - } - else - { - apply_pretension(&target_current); - apply_pas_cadence(&target_current, throttle_percent); -#if HAS_TORQUE_SENSOR - apply_pas_torque(&target_current); -#endif // HAS_TORQUE_SENSOR - - pas_engaged = target_current > 0; - - apply_cruise(&target_current, throttle_percent); - - throttle_override = apply_throttle(&target_current, throttle_percent); - - // override target cadence if configured in assist level - if (throttle_override && - (assist_level_data.level.flags & ASSIST_FLAG_PAS) && - (assist_level_data.level.flags & ASSIST_FLAG_OVERRIDE_CADENCE)) - { - target_cadence = THROTTLE_CADENCE_OVERRIDE_PERCENT; - } - } - - bool speed_limiting = apply_speed_limit(&target_current, throttle_percent, pas_engaged, throttle_override); - bool thermal_limiting = apply_thermal_limit(&target_current); - bool lvc_limiting = apply_low_voltage_limit(&target_current); - bool shift_limiting = -#if HAS_SHIFT_SENSOR_SUPPORT - apply_shift_sensor_interrupt(&target_current); -#else - false; -#endif - bool is_limiting = speed_limiting || thermal_limiting || lvc_limiting || shift_limiting; - bool is_braking = apply_brake(&target_current); - - apply_current_ramp_up(&target_current, is_limiting || !throttle_override); - apply_current_ramp_down(&target_current, !is_braking && !shift_limiting); - - motor_set_target_speed(target_cadence); - motor_set_target_current(target_current); - - if (target_current > 0) - { - motor_enable(); - } - else - { - motor_disable(); - } - - if (g_config.lights_mode == LIGHTS_MODE_DISABLED /*|| (motor_status() & MOTOR_ERROR_LVC) */) - { - lights_disable(); - } - else - { - lights_enable(); - } -} - - -void app_set_assist_level(uint8_t level) -{ - if (assist_level != level) - { - if (assist_level == ASSIST_PUSH && g_config.use_push_walk) - { - // When releasig push walk mode pedals may have been rotating - // with the motor, block motor power for 2 seconds to prevent PAS - // sensor from incorrectly applying power if returning to a PAS level. - block_power_for(1000); - } - - assist_level = level; - eventlog_write_data(EVT_DATA_ASSIST_LEVEL, assist_level); - reload_assist_params(); - } -} - -void app_set_lights(bool on) -{ - if ( // it's ok to write ugly code if you say it's ugly... - (g_config.assist_mode_select == ASSIST_MODE_SELECT_LIGHTS) || - (assist_level == ASSIST_0 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS0_LIGHT) || - (assist_level == ASSIST_1 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS1_LIGHT) || - (assist_level == ASSIST_2 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS2_LIGHT) || - (assist_level == ASSIST_3 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS3_LIGHT) || - (assist_level == ASSIST_4 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS4_LIGHT) || - (assist_level == ASSIST_5 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS5_LIGHT) || - (assist_level == ASSIST_6 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS6_LIGHT) || - (assist_level == ASSIST_7 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS7_LIGHT) || - (assist_level == ASSIST_8 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS8_LIGHT) || - (assist_level == ASSIST_9 && g_config.assist_mode_select == ASSIST_MODE_SELECT_PAS9_LIGHT) - ) - { - if (on) - { - app_set_operation_mode(OPERATION_MODE_SPORT); - } - else - { - app_set_operation_mode(OPERATION_MODE_DEFAULT); - } - } - else - { - if (g_config.lights_mode == LIGHTS_MODE_DEFAULT && lights_state != on) - { - lights_state = on; - eventlog_write_data(EVT_DATA_LIGHTS, on); - lights_set(on); - } - } -} - -void app_set_operation_mode(uint8_t mode) -{ - if (operation_mode != mode) - { - operation_mode = mode; - eventlog_write_data(EVT_DATA_OPERATION_MODE, operation_mode); - reload_assist_params(); - } -} - -void app_set_wheel_max_speed_rpm(uint16_t value) -{ - if (global_speed_limit_rpm != value) - { - global_speed_limit_rpm = value; - global_throttle_speed_limit_rpm_x10 = ((int32_t)global_speed_limit_rpm * - g_config.throttle_global_spd_lim_percent) / 10; - - eventlog_write_data(EVT_DATA_WHEEL_SPEED_PPM, value); - reload_assist_params(); - } -} - -uint8_t app_get_assist_level() -{ - return assist_level; -} - -uint8_t app_get_lights() -{ - return lights_state; -} - -uint8_t app_get_status_code() -{ - uint16_t motor = motor_status(); - - if (motor & MOTOR_ERROR_HALL_SENSOR) - { - return STATUS_ERROR_HALL_SENSOR; - } - - if (motor & MOTOR_ERROR_CURRENT_SENSE) - { - return STATUS_ERROR_CURRENT_SENSE; - } - - if (motor & MOTOR_ERROR_POWER_RESET) - { - // Phase line error code reused, cause and meaning - // of MOTOR_ERROR_POWER_RESET triggered on bbs02 is currently unknown - return STATUS_ERROR_PHASE_LINE; - } - - if (!throttle_ok()) - { - return STATUS_ERROR_THROTTLE; - } - - if (!torque_sensor_ok()) - { - return STATUS_ERROR_TORQUE_SENSOR; - } - - if (temperature_motor_c > MAX_TEMPERATURE) - { - return STATUS_ERROR_MOTOR_OVER_TEMP; - } - - if (temperature_contr_c > MAX_TEMPERATURE) - { - return STATUS_ERROR_CONTROLLER_OVER_TEMP; - } - - // Disable LVC error since it is not shown on display in original firmware - // Uncomment if you want to enable - // if (motor & MOTOR_ERROR_LVC) - // { - // return STATUS_ERROR_LVC; - // } - - if (brake_is_activated()) - { - return STATUS_BRAKING; - } - - return STATUS_NORMAL; -} - -uint8_t app_get_temperature() -{ - int8_t temp_max = MAX(temperature_contr_c, temperature_motor_c); - - if (temp_max < 0) - { - return 0; - } - - return (uint8_t)temp_max; -} - -void apply_pretension(uint8_t* target_current) -{ - uint16_t current_speed_rpm_x10 = speed_sensor_get_rpm_x10(); - - if (g_config.use_speed_sensor && g_config.use_pretension && current_speed_rpm_x10 > pretension_cutoff_speed_rpm_x10) - { - *target_current = 1; - } - return; -} - -void apply_pas_cadence(uint8_t* target_current, uint8_t throttle_percent) -{ - if ((assist_level_data.level.flags & ASSIST_FLAG_PAS) && !(assist_level_data.level.flags & ASSIST_FLAG_PAS_TORQUE)) - { - if (pas_is_pedaling_forwards() && pas_get_pulse_counter() > g_config.pas_start_delay_pulses) - { - if (assist_level_data.level.flags & ASSIST_FLAG_PAS_VARIABLE) - { - uint8_t current = (uint8_t)MAP16(throttle_percent, 0, 100, 0, assist_level_data.level.target_current_percent); - if (current > *target_current) - { - *target_current = current; - } - } - else - { - if (assist_level_data.level.target_current_percent > *target_current) - { - *target_current = assist_level_data.level.target_current_percent; - } - - // apply "keep current" ramp - if (g_config.pas_keep_current_percent < 100) - { - if (*target_current > assist_level_data.keep_current_target_percent && - pas_get_cadence_rpm_x10() > assist_level_data.keep_current_ramp_start_rpm_x10) - { - uint32_t cadence = MIN(pas_get_cadence_rpm_x10(), assist_level_data.keep_current_ramp_end_rpm_x10); - - // ramp down current towards keep_current_target_percent with rpm above keep_current_ramp_start_rpm_x10 - *target_current = MAP32( - cadence, // in - assist_level_data.keep_current_ramp_start_rpm_x10, // in_min - assist_level_data.keep_current_ramp_end_rpm_x10, // in_max - *target_current, // out_min - assist_level_data.keep_current_target_percent); // out_max - } - } - } - } - } -} - -#if HAS_TORQUE_SENSOR -void apply_pas_torque(uint8_t* target_current) -{ - if ((assist_level_data.level.flags & ASSIST_FLAG_PAS) && (assist_level_data.level.flags & ASSIST_FLAG_PAS_TORQUE)) - { - if (pas_is_pedaling_forwards() && (pas_get_pulse_counter() > g_config.pas_start_delay_pulses || speed_sensor_is_moving())) - { - uint16_t torque_nm_x100 = torque_sensor_get_nm_x100(); - uint16_t cadence_rpm_x10 = pas_get_cadence_rpm_x10(); - if (cadence_rpm_x10 < TORQUE_POWER_LOWER_RPM_X10) - { - cadence_rpm_x10 = TORQUE_POWER_LOWER_RPM_X10; - } - - uint16_t pedal_power_w_x10 = (uint16_t)(((uint32_t)torque_nm_x100 * cadence_rpm_x10) / 955); - - // used in division below to calculate target current, - // clamp to 24V if no reading available (unexpected error). - uint16_t battery_voltage_x10 = MAX(motor_get_battery_voltage_x10(), 240); - - uint16_t target_current_amp_x100 = (uint16_t)(((uint32_t)10 * pedal_power_w_x10 * - assist_level_data.level.torque_amplification_factor_x10) / battery_voltage_x10); - - uint16_t max_current_amp_x100 = g_config.max_current_amps * 100; - - // limit target to ensure no overflow in map result - if (target_current_amp_x100 > max_current_amp_x100) - { - target_current_amp_x100 = max_current_amp_x100; - } - uint8_t tmp_percent = (uint8_t)MAP32(target_current_amp_x100, 0, max_current_amp_x100, 0, 100); - - // minimum 1 percent current if pedaling - if (tmp_percent < 1) - { - tmp_percent = 1; - } - // limit to maximum assist current for set level - else if (tmp_percent > assist_level_data.level.target_current_percent) - { - tmp_percent = assist_level_data.level.target_current_percent; - } - - if (tmp_percent > *target_current) - { - *target_current = tmp_percent; - } - } - } -} -#endif - -void apply_cruise(uint8_t* target_current, uint8_t throttle_percent) -{ - static bool cruise_block_throttle_return = false; - - if ((assist_level_data.level.flags & ASSIST_FLAG_CRUISE) && throttle_ok()) - { - // pause cruise if brake activated - if (brake_is_activated()) - { - cruise_paused = true; - cruise_block_throttle_return = true; - } - - // pause cruise if started pedaling backwards - else if (pas_is_pedaling_backwards() && pas_get_pulse_counter() > CRUISE_DISENGAGE_PAS_PULSES) - { - cruise_paused = true; - cruise_block_throttle_return = true; - } - - // pause cruise if throttle touched while cruise active - else if (!cruise_paused && !cruise_block_throttle_return && throttle_percent > 0) - { - cruise_paused = true; - cruise_block_throttle_return = true; - } - - // unpause cruise if pedaling forward while engaging throttle > 50% - else if (cruise_paused && !cruise_block_throttle_return && throttle_percent > 50 && pas_is_pedaling_forwards() && pas_get_pulse_counter() > CRUISE_ENGAGE_PAS_PULSES) - { - cruise_paused = false; - cruise_block_throttle_return = true; - } - - // reset flag tracking throttle to make sure throttle returns to idle position before engage/disenage cruise with throttle touch - else if (cruise_block_throttle_return && throttle_percent == 0) - { - cruise_block_throttle_return = false; - } - - if (cruise_paused) - { - *target_current = 0; - } - else - { - if (assist_level_data.level.target_current_percent > *target_current) - { - *target_current = assist_level_data.level.target_current_percent; - } - } - } -} - -bool apply_throttle(uint8_t* target_current, uint8_t throttle_percent) -{ - if ((assist_level_data.level.flags & ASSIST_FLAG_THROTTLE) && throttle_percent > 0 && throttle_ok()) - { - uint8_t current = (uint8_t)MAP16(throttle_percent, 0, 100, g_config.throttle_start_percent, assist_level_data.level.max_throttle_current_percent); - - if (current >= *target_current) - { - *target_current = current; - return true; - } - } - - return false; -} - - -bool apply_speed_limit(uint8_t* target_current, uint8_t throttle_percent, bool pas_engaged, bool throttle_override) -{ - static bool speed_limiting = false; - - if (!g_config.use_speed_sensor) - { - return false; - } - - // global throttle speed limit applies if enabled in configuration, PAS is not engaged and throttle is used - bool global_throttle_limit_active = - !pas_engaged && - throttle_percent > 0 && - g_config.throttle_global_spd_lim_percent > 0 && - ( - g_config.throttle_global_spd_lim_opt == THROTTLE_GLOBAL_SPEED_LIMIT_ENABLED || - (g_config.throttle_global_spd_lim_opt == THROTTLE_GLOBAL_SPEED_LIMIT_STD_LVLS && operation_mode == OPERATION_MODE_DEFAULT) - ); - - bool throttle_speed_override_active = !global_throttle_limit_active && throttle_override && - (assist_level_data.level.flags & ASSIST_FLAG_PAS) && - (assist_level_data.level.flags & ASSIST_FLAG_OVERRIDE_SPEED); - - int32_t max_speed_rpm_x10; - if (global_throttle_limit_active) - { - // use configured global throttle override speed limit - max_speed_rpm_x10 = global_throttle_speed_limit_rpm_x10; - } - else if (throttle_speed_override_active) - { - // override assist level speed limit to global speed limit - max_speed_rpm_x10 = global_speed_limit_rpm * 10; - } - else - { - // normal operation, use configured assist level speed limit - max_speed_rpm_x10 = assist_level_data.max_wheel_speed_rpm_x10; - } - - int32_t max_speed_ramp_low_rpm_x10 = max_speed_rpm_x10 - speed_limit_ramp_interval_rpm_x10; - int32_t max_speed_ramp_high_rpm_x10 = max_speed_rpm_x10 + speed_limit_ramp_interval_rpm_x10; - - if (max_speed_rpm_x10 > 0) - { - int16_t current_speed_rpm_x10 = speed_sensor_get_rpm_x10(); - - if (current_speed_rpm_x10 < max_speed_ramp_low_rpm_x10) - { - // no limiting - if (speed_limiting) - { - speed_limiting = false; - eventlog_write_data(EVT_DATA_SPEED_LIMITING, 0); - } - } - else - { - if (!speed_limiting) - { - speed_limiting = true; - eventlog_write_data(EVT_DATA_SPEED_LIMITING, 1); - } - - if (current_speed_rpm_x10 > max_speed_ramp_high_rpm_x10) - { - if (*target_current > 1) - { - *target_current = 1; - return true; - } - } - else - { - // linear ramp down when approaching max speed. - uint8_t tmp = (uint8_t)MAP32(current_speed_rpm_x10, max_speed_ramp_low_rpm_x10, max_speed_ramp_high_rpm_x10, *target_current, 1); - if (*target_current > tmp) - { - *target_current = tmp; - return true; - } - } - } - } - - return false; -} - -bool apply_thermal_limit(uint8_t* target_current) -{ - static uint32_t next_log_temp_ms = 10000; - - static bool temperature_limiting = false; - - int16_t temp_contr_x100 = temperature_contr_x100(); - temperature_contr_c = temp_contr_x100 / 100; - - int16_t temp_motor_x100 = temperature_motor_x100(); - temperature_motor_c = temp_motor_x100 / 100; - - int16_t max_temp_x100 = MAX(temp_contr_x100, temp_motor_x100); - int8_t max_temp = MAX(temperature_contr_c, temperature_motor_c); - - if (eventlog_is_enabled() && g_config.use_temperature_sensor && system_ms() > next_log_temp_ms) - { - next_log_temp_ms = system_ms() + 10000; - eventlog_write_data(EVT_DATA_TEMPERATURE, (uint16_t)temperature_motor_c << 8 | temperature_contr_c); - } - - if (max_temp >= (MAX_TEMPERATURE - MAX_TEMPERATURE_RAMP_DOWN_INTERVAL)) - { - if (!temperature_limiting) - { - temperature_limiting = true; - eventlog_write_data(EVT_DATA_THERMAL_LIMITING, 1); - } - - if (max_temp_x100 > MAX_TEMPERATURE * 100) - { - max_temp_x100 = MAX_TEMPERATURE * 100; - } - - uint8_t tmp = (uint8_t)MAP32( - max_temp_x100, // value - (MAX_TEMPERATURE - MAX_TEMPERATURE_RAMP_DOWN_INTERVAL) * 100, // in_min - MAX_TEMPERATURE * 100, // in_max - 100, // out_min - MAX_TEMPERATURE_LOW_CURRENT_PERCENT // out_max - ); - - if (*target_current > tmp) - { - *target_current = tmp; - return true; - } - } - else - { - if (temperature_limiting) - { - temperature_limiting = false; - eventlog_write_data(EVT_DATA_THERMAL_LIMITING, 0); - } - } - - return false; -} - -bool apply_low_voltage_limit(uint8_t* target_current) -{ - static uint32_t next_log_volt_ms = 10000; - static bool lvc_limiting = false; - - static uint32_t next_voltage_reading_ms = 125; - static int32_t flt_min_bat_volt_x100 = 100 * 100; - - if (system_ms() > next_voltage_reading_ms) - { - next_voltage_reading_ms = system_ms() + 125; - int32_t voltage_reading_x100 = motor_get_battery_voltage_x10() * 10ul; - - if (voltage_reading_x100 < flt_min_bat_volt_x100) - { - flt_min_bat_volt_x100 = EXPONENTIAL_FILTER(flt_min_bat_volt_x100, voltage_reading_x100, 8); - } - - if (eventlog_is_enabled() && system_ms() > next_log_volt_ms) - { - next_log_volt_ms = system_ms() + 10000; - eventlog_write_data(EVT_DATA_VOLTAGE, (uint16_t)voltage_reading_x100); - } - } - - uint16_t voltage_x100 = flt_min_bat_volt_x100; - - if (voltage_x100 <= lvc_ramp_down_start_voltage_x100) - { - if (!lvc_limiting) - { - eventlog_write_data(EVT_DATA_LVC_LIMITING, voltage_x100); - lvc_limiting = true; - } - - if (voltage_x100 < lvc_voltage_x100) - { - voltage_x100 = lvc_voltage_x100; - } - - // Ramp down power until LVC_LOW_CURRENT_PERCENT when approaching LVC - uint8_t tmp = (uint8_t)MAP32( - voltage_x100, // value - lvc_ramp_down_end_voltage_x100, // in_min - lvc_ramp_down_start_voltage_x100, // in_max - LVC_LOW_CURRENT_PERCENT, // out_min - 100 // out_max - ); - - if (*target_current > tmp) - { - *target_current = tmp; - return true; - } - } - - return false; -} - -#if HAS_SHIFT_SENSOR_SUPPORT -bool apply_shift_sensor_interrupt(uint8_t* target_current) -{ - static uint32_t shift_sensor_act_ms = 0; - static bool shift_sensor_last = false; - static bool shift_sensor_interrupting = false; - static bool shift_sensor_logged = false; - - // Exit immediately if shift interrupts disabled. - if (!g_config.use_shift_sensor) - { - return false; - } - - bool active = shift_sensor_is_activated(); - if (active) - { - // Check for new pulse from the gear sensor during shift interrupt - if (!shift_sensor_last && shift_sensor_interrupting) - { - // Consecutive gear change, do restart. - shift_sensor_interrupting = false; - } - if (!shift_sensor_interrupting) - { - uint16_t duration_ms = EXPAND_U16( - g_config.shift_interrupt_duration_ms_u16h, - g_config.shift_interrupt_duration_ms_u16l - ); - shift_sensor_act_ms = system_ms() + duration_ms; - shift_sensor_interrupting = true; - } - shift_sensor_last = true; - } - else - { - shift_sensor_last = false; - } - - if (!shift_sensor_interrupting) - { - return false; - } - - if (system_ms() >= shift_sensor_act_ms) - { - // Shift is finished, reset function state. - shift_sensor_interrupting = false; - // Logging is skipped, unless current has been clamped during shift interrupt. - if (shift_sensor_logged) - { - shift_sensor_logged = false; - eventlog_write_data(EVT_DATA_SHIFT_SENSOR, 0); - } - return false; - } - - if ((*target_current) > g_config.shift_interrupt_current_threshold_percent) - { - if (!shift_sensor_logged) - { - // Logging only once per shifting interrupt. - shift_sensor_logged = true; - eventlog_write_data(EVT_DATA_SHIFT_SENSOR, 1); - } - // Set target current based on desired current threshold during shift. - *target_current = g_config.shift_interrupt_current_threshold_percent; - - return true; - } - - return false; -} -#endif - -bool apply_brake(uint8_t* target_current) -{ - bool is_braking = brake_is_activated(); - - if (g_config.lights_mode == LIGHTS_MODE_BRAKE_LIGHT) - { - lights_set(is_braking); - } - - if (is_braking) - { - *target_current = 0; - } - - return is_braking; -} - -void apply_current_ramp_up(uint8_t* target_current, bool enable) -{ - static uint8_t ramp_up_target_current = 0; - static uint32_t last_ramp_up_increment_ms = 0; - - if (enable && *target_current > ramp_up_target_current) - { - uint32_t now = system_ms(); - uint16_t time_diff = now - last_ramp_up_increment_ms; - - if (time_diff >= ramp_up_current_interval_ms) - { - ++ramp_up_target_current; - - if (last_ramp_up_increment_ms == 0) - { - last_ramp_up_increment_ms = now; - } - else - { - // offset for time overshoot to not accumulate large ramp error - last_ramp_up_increment_ms = now - (uint8_t)(time_diff - ramp_up_current_interval_ms); - } - } - - *target_current = ramp_up_target_current; - } - else - { - ramp_up_target_current = *target_current; - last_ramp_up_increment_ms = 0; - } -} - -void apply_current_ramp_down(uint8_t* target_current, bool enable) -{ - static uint8_t ramp_down_target_current = 0; - static uint32_t last_ramp_down_decrement_ms = 0; - - // apply fast ramp down if coming from high target current (> 50%) - if (enable && *target_current < ramp_down_target_current) - { - uint32_t now = system_ms(); - uint16_t time_diff = now - last_ramp_down_decrement_ms; - - if (time_diff >= 10) - { - uint8_t diff = ramp_down_target_current - *target_current; - - if (diff >= CURRENT_RAMP_DOWN_PERCENT_10MS) - { - ramp_down_target_current -= CURRENT_RAMP_DOWN_PERCENT_10MS; - } - else - { - ramp_down_target_current -= diff; - } - - if (last_ramp_down_decrement_ms == 0) - { - last_ramp_down_decrement_ms = now; - } - else - { - // offset for time overshoot to not accumulate large ramp error - last_ramp_down_decrement_ms = now - (uint8_t)(time_diff - 10); - } - } - - *target_current = ramp_down_target_current; - } - else - { - ramp_down_target_current = *target_current; - last_ramp_down_decrement_ms = 0; - } -} - - -bool check_power_block() -{ - if (power_blocked_until_ms != 0) - { - // power block is active, check if time to release - if (system_ms() > power_blocked_until_ms) - { - power_blocked_until_ms = 0; - return false; - } - - return true; - } - - return false; -} - -void block_power_for(uint16_t ms) -{ - power_blocked_until_ms = system_ms() + ms; -} - -void reload_assist_params() -{ - if (assist_level < ASSIST_PUSH) - { - assist_level_data.level = g_config.assist_levels[operation_mode][assist_level]; - - assist_level_data.max_wheel_speed_rpm_x10 = ((int32_t)global_speed_limit_rpm * assist_level_data.level.max_speed_percent) / 10; - - if (assist_level_data.level.flags & ASSIST_FLAG_PAS) - { - assist_level_data.keep_current_target_percent = (uint8_t)((uint16_t)g_config.pas_keep_current_percent * assist_level_data.level.target_current_percent / 100); - assist_level_data.keep_current_ramp_start_rpm_x10 = g_config.pas_keep_current_cadence_rpm * 10; - assist_level_data.keep_current_ramp_end_rpm_x10 = (uint16_t)(((uint32_t)assist_level_data.level.max_cadence_percent * MAX_CADENCE_RPM_X10) / 100); - } - - // pause cruise if swiching level - cruise_paused = true; - } - // only apply push walk params if push walk is active in config, - // otherwise data of previous assist level is kept. - else if (assist_level == ASSIST_PUSH && g_config.use_push_walk) - { - assist_level_data.level.flags = 0; - assist_level_data.level.target_current_percent = 0; - assist_level_data.level.max_speed_percent = 0; - assist_level_data.level.max_cadence_percent = 15; - assist_level_data.level.max_throttle_current_percent = 0; - - assist_level_data.max_wheel_speed_rpm_x10 = convert_wheel_speed_kph_to_rpm(WALK_MODE_SPEED_KPH) * 10; - } -} - -uint16_t convert_wheel_speed_kph_to_rpm(uint8_t speed_kph) -{ - float radius_mm = EXPAND_U16(g_config.wheel_size_inch_x10_u16h, g_config.wheel_size_inch_x10_u16l) * 1.27f; // g_config.wheel_size_inch_x10 / 2.f * 2.54f; - return (uint16_t)(25000.f / (3 * 3.14159f * radius_mm) * speed_kph); -} diff --git a/src/firmware/app.h b/src/firmware/app.h deleted file mode 100644 index 66b3164f..00000000 --- a/src/firmware/app.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#ifndef _APP_H_ -#define _APP_H_ - -#include "intellisense.h" -#include -#include - - -#define ASSIST_0 0x00 -#define ASSIST_1 0x01 -#define ASSIST_2 0x02 -#define ASSIST_3 0x03 -#define ASSIST_4 0x04 -#define ASSIST_5 0x05 -#define ASSIST_6 0x06 -#define ASSIST_7 0x07 -#define ASSIST_8 0x08 -#define ASSIST_9 0x09 -#define ASSIST_PUSH 0x0A - -#define OPERATION_MODE_DEFAULT 0x00 -#define OPERATION_MODE_SPORT 0x01 - -// Matches status codes used by Bafang -#define STATUS_NORMAL 0x01 -#define STATUS_BRAKING 0x03 - -#define STATUS_ERROR_THROTTLE_HIGH 0x04 -#define STATUS_ERROR_THROTTLE 0x05 -#define STATUS_ERROR_LVC 0x06 -#define STATUS_ERROR_HIGH_VOLTAGE 0x07 // not implemented -#define STATUS_ERROR_HALL_SENSOR 0x08 -#define STATUS_ERROR_PHASE_LINE 0x09 -#define STATUS_ERROR_CONTROLLER_OVER_TEMP 0x10 -#define STATUS_ERROR_MOTOR_OVER_TEMP 0x11 -#define STATUS_ERROR_CURRENT_SENSE 0x12 -#define STATUS_ERROR_BATTERY_TEMP_SENSOR 0x13 // n/a -#define STATUS_ERROR_MOTOR_TEMP_SENSOR 0x14 // not implemented -#define STATUS_ERROR_CONTROLLER_TEMP_SENSOR 0x15 // not implemented -#define STATUS_ERROR_SPEED_SENSOR 0x21 // not implemented -#define STATUS_ERROR_BMS_COMMUNICATION 0x22 // n/a -#define STATUS_ERROR_HEAD_LIGHT 0x23 // not implemented -#define STATUS_ERROR_HEAD_LIGHT_SENSOR 0x24 // not implemented -#define STATUS_ERROR_TORQUE_SENSOR 0x25 -#define STATUS_ERROR_TORQUE_SPEED 0x26 // n/a -#define STATUS_ERROR_COMMUNICATION 0x30 // n/a - - -void app_init(); - -void app_process(); - -void app_set_assist_level(uint8_t level); -void app_set_lights(bool on); - -void app_set_operation_mode(uint8_t mode); -void app_set_wheel_max_speed_rpm(uint16_t value); - -uint8_t app_get_assist_level(); -uint8_t app_get_lights(); -uint8_t app_get_status_code(); -uint8_t app_get_temperature(); - -#endif diff --git a/src/firmware/battery.c b/src/firmware/battery.c deleted file mode 100644 index 328be621..00000000 --- a/src/firmware/battery.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "battery.h" -#include "motor.h" -#include "system.h" -#include "util.h" -#include "cfgstore.h" -#include "fwconfig.h" - -static int16_t battery_empty_x100v; -static int16_t battery_full_x100v; - -static uint8_t battery_percent; -static uint32_t motor_disabled_at_ms; -static bool first_reading_done; - -/* -No attempt is made to have accurate battery state of charge display. - -This is only a voltage based approch using configured max and min battery voltages. -The end values are padded 8% on each side (BATTERY_EMPTY_OFFSET_PERCENT, BATTERY_FULL_OFFSET_PERCENT). - -Battery voltage is measured when no motor power has been applied for -at least 2 seconds (BATTERY_NO_LOAD_DELAY_MS). This is to mitigate measuring voltage sag -but is still problematic in cold weather. - -Battery SOC percentage is calculated from measured voltage using linear interpolation -between the padded ranges. - -The LVC rampdown starts at 10% battery SOC (LVC_RAMP_DOWN_OFFSET_PERCENT) and will linearly -ramp the current down to 20% (LVC_LOW_CURRENT_PERCENT) of the maximum configured current. - -For example, if the maximum battery voltage is 58.8V and the low cutoff voltage is 42V, then: - -- The full voltage range is 58.8V - 42V = 16.8V -- The padding amount is 0.08 * 16.8V = 1.3V -- The battery is considered at 100% SOC at 58.8V - 1.3V = 57.5V -- The battery is considered at 0% SOC at 42.0V + 1.3V = 43.3V -- LVC rampdown will start at 10% SOC, so: 43.3V + 0.1 * (57.5V - 43.3V) = 44.7V -- Full LVC limiting will occur at 0% SOC, so: 43.3V -*/ - -static uint8_t compute_battery_percent() -{ - int16_t value_x100v = motor_get_battery_voltage_x10() * 10l; - int16_t percent = (int16_t)MAP32(value_x100v, battery_empty_x100v, battery_full_x100v, 0, 100); - - return (uint8_t)CLAMP(percent, 0, 100); -} - -#if (BATTERY_PERCENT_MAP == BATTERY_PERCENT_MAP_SW102) -static uint8_t map_percent_sw102(uint8_t percent) -{ - // Measured on Display - // ----------------------- - // 0bar 0-5 - // 1bar 5 - 10 - // 2bar 10 - 30 - // 3bar 31 - 51 - // 4bar 52 - 78 - // 5bar 78 - 100 - - if (percent < 5) // 0bar - { - return 0; - } - else if (percent < 21) // 1bar - { - return 7; - } - else if (percent < 41) // 2bar - { - return 20; - } - else if (percent < 61) // 3bar - { - return 40; - } - else if (percent < 81) // 4bar - { - return 60; - } - else // 5bar - { - return 100; - } -} -#endif - - -void battery_init() -{ - // default to 70% until first reading is available - battery_percent = 70; - motor_disabled_at_ms = 0; - first_reading_done = false; - - uint16_t battery_min_voltage_x100v = g_config.low_cut_off_v * 100u; - uint16_t battery_max_voltage_x100v = - EXPAND_U16(g_config.max_battery_x100v_u16h, g_config.max_battery_x100v_u16l); - - uint16_t battery_range_x100v = battery_max_voltage_x100v - battery_min_voltage_x100v; - - battery_full_x100v = battery_max_voltage_x100v - - ((BATTERY_FULL_OFFSET_PERCENT * battery_range_x100v) / 100); - - battery_empty_x100v = battery_min_voltage_x100v + - ((BATTERY_EMPTY_OFFSET_PERCENT * battery_range_x100v) / 100); -} - -void battery_process() -{ - if (!first_reading_done) - { - if (motor_get_battery_voltage_x10() > 0) - { - battery_percent = compute_battery_percent(); - first_reading_done = true; - } - } - else - { - uint8_t target_current = motor_get_target_current(); - - if (motor_disabled_at_ms == 0 && target_current == 0) - { - motor_disabled_at_ms = system_ms(); - } - else if (target_current > 0) - { - motor_disabled_at_ms = 0; - } - - if (target_current == 0 && (system_ms() - motor_disabled_at_ms) > BATTERY_NO_LOAD_DELAY_MS) - { - battery_percent = compute_battery_percent(); - } - } -} - -uint8_t battery_get_percent() -{ - return battery_percent; -} - -uint8_t battery_get_mapped_percent() -{ -#if (BATTERY_PERCENT_MAP == BATTERY_PERCENT_MAP_SW102) - return map_percent_sw102(battery_percent); -#else - return battery_percent; -#endif -} diff --git a/src/firmware/bbs-fw.sln b/src/firmware/bbs-fw.sln deleted file mode 100644 index f5f5e2ee..00000000 --- a/src/firmware/bbs-fw.sln +++ /dev/null @@ -1,28 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.4.33205.214 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bbs-fw", "bbs-fw.vcxproj", "{1D7732D0-C5BC-4E67-9A24-EFF8000C007D}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - BBS02|SDCC = BBS02|SDCC - BBSHD|SDCC = BBSHD|SDCC - TSDZ2|SDCC = TSDZ2|SDCC - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1D7732D0-C5BC-4E67-9A24-EFF8000C007D}.BBS02|SDCC.ActiveCfg = BBS02|x64 - {1D7732D0-C5BC-4E67-9A24-EFF8000C007D}.BBS02|SDCC.Build.0 = BBS02|x64 - {1D7732D0-C5BC-4E67-9A24-EFF8000C007D}.BBSHD|SDCC.ActiveCfg = BBSHD|x64 - {1D7732D0-C5BC-4E67-9A24-EFF8000C007D}.BBSHD|SDCC.Build.0 = BBSHD|x64 - {1D7732D0-C5BC-4E67-9A24-EFF8000C007D}.TSDZ2|SDCC.ActiveCfg = TSDZ2|x64 - {1D7732D0-C5BC-4E67-9A24-EFF8000C007D}.TSDZ2|SDCC.Build.0 = TSDZ2|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {2359A0E3-B5F4-4356-ACB7-0730EF01FF2F} - EndGlobalSection -EndGlobal diff --git a/src/firmware/bbs-fw.vcxproj b/src/firmware/bbs-fw.vcxproj deleted file mode 100644 index f42758d5..00000000 --- a/src/firmware/bbs-fw.vcxproj +++ /dev/null @@ -1,155 +0,0 @@ - - - - - BBS02 - x64 - - - BBSHD - x64 - - - TSDZ2 - x64 - - - - 16.0 - {1D7732D0-C5BC-4E67-9A24-EFF8000C007D} - Win32Proj - - - - Makefile - true - v143 - - - Makefile - true - v143 - - - Makefile - true - v143 - - - - - - - - - - - - - - - - - - make all TARGET_CONTROLLER=BBSHD - bbs-fw.hex - make clean - make clean -make all TARGET_CONTROLLER=BBSHD - BBSHD;$(NMakePreprocessorDefinitions) - C:/Program Files/SDCC/include;C:/Program Files/SDCC/include/mcs51;./ - $(SolutionDir) - build\$(Platform)\$(Configuration)\ - - - make all TARGET_CONTROLLER=TSDZ2 - bbs-fw.hex - make clean - make clean -make all TARGET_CONTROLLER=TSDZ2 - TSDZ2;$(NMakePreprocessorDefinitions) - C:/Program Files/SDCC/include;./ - $(SolutionDir) - build\$(Platform)\$(Configuration)\ - - - make all TARGET_CONTROLLER=BBS02 - bbs-fw.hex - make clean - make clean -make all TARGET_CONTROLLER=BBS02 - BBS02;$(NMakePreprocessorDefinitions) - C:/Program Files/SDCC/include;C:/Program Files/SDCC/include/mcs51;./ - $(SolutionDir) - build\$(Platform)\$(Configuration)\ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/firmware/bbs-fw.vcxproj.filters b/src/firmware/bbs-fw.vcxproj.filters deleted file mode 100644 index ff923157..00000000 --- a/src/firmware/bbs-fw.vcxproj.filters +++ /dev/null @@ -1,201 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - {3afcc16a-9b6d-48ec-8c37-3d76d5814243} - - - {0753682d-650f-4c13-a2b2-7a748cc9fdee} - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files\bbsx - - - Source Files\bbsx - - - Source Files\bbsx - - - Source Files\bbsx - - - Source Files\bbsx - - - Source Files\bbsx - - - Source Files\tsdz2 - - - Source Files\tsdz2 - - - Source Files\tsdz2 - - - Source Files\bbsx - - - Source Files\bbsx - - - Source Files\bbsx - - - Source Files\tsdz2 - - - Source Files\tsdz2 - - - Source Files\tsdz2 - - - Source Files\tsdz2 - - - Source Files\tsdz2 - - - Source Files\tsdz2 - - - Source Files\tsdz2 - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Source Files\bbsx - - - Source Files\bbsx - - - Source Files\bbsx - - - Source Files\bbsx - - - Header Files - - - Source Files\tsdz2 - - - Header Files - - - Source Files\bbsx - - - Source Files\bbsx - - - Source Files\tsdz2 - - - Source Files\tsdz2 - - - Source Files\tsdz2 - - - Source Files\tsdz2 - - - Header Files - - - - - - \ No newline at end of file diff --git a/src/firmware/bbsx/adc.c b/src/firmware/bbsx/adc.c deleted file mode 100644 index 59b43f65..00000000 --- a/src/firmware/bbsx/adc.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "adc.h" -#include "bbsx/stc15.h" -#include "bbsx/pins.h" - - -static uint8_t next_channel; -static uint8_t no_adc_reading_counter; - -static uint8_t throttle_value; -static uint16_t temperature_contr_value; -static uint16_t temperature_motor_value; - - -void adc_init() -{ - // Setup pin voltage as high impedance input even though it is not used - SET_PIN_INPUT(PIN_VOLTAGE); - - // Setup pin throttle as adc input - SET_PIN_INPUT(PIN_THROTTLE); - SET_PIN_LOW(PIN_THROTTLE); - SET_BIT(P1ASF, GET_PIN_NUM(PIN_THROTTLE)); - - // Setup pin controller temperature pin as adc input - SET_PIN_INPUT(PIN_TEMPERATURE_CONTR); - SET_PIN_LOW(PIN_TEMPERATURE_CONTR); - SET_BIT(P1ASF, GET_PIN_NUM(PIN_TEMPERATURE_CONTR)); - -#ifdef BBSHD - // Setup pin motor temperature pin as adc input - SET_PIN_INPUT(PIN_TEMPERATURE_MOTOR); - SET_PIN_LOW(PIN_TEMPERATURE_MOTOR); - SET_BIT(P1ASF, GET_PIN_NUM(PIN_TEMPERATURE_MOTOR)); -#endif - - ADC_RES = 0; - ADC_RESL = 0; - - // Arrange adc result for 8bit reading - CLEAR_BIT(PCON2, 5); - - ADC_CONTR = (uint8_t)((1 << 7)); - - no_adc_reading_counter = 0; - throttle_value = 0; - temperature_contr_value = 0; - temperature_motor_value = 0; - next_channel = GET_PIN_NUM(PIN_THROTTLE); - - - // throttle is read during init since a valid value must be - // needs to be available for fir throttle_process to not mess - // up safeguard logic - - // enable adc power and read throttle - ADC_CONTR = (uint8_t)((1 << 7) | (1 << 3) | next_channel); - - // wait for throttle reading and process - while (!IS_BIT_SET(ADC_CONTR, 4)); - adc_process(); -} - -void adc_process() -{ - // adc reading available - if (IS_BIT_SET(ADC_CONTR, 4)) - { - no_adc_reading_counter = 0; - - ADC_CONTR = (uint8_t)((1 << 7)); // Clear ADC_FLAG - - switch (next_channel) - { - case GET_PIN_NUM(PIN_THROTTLE): - { - throttle_value = ADC_RES; - next_channel = GET_PIN_NUM(PIN_TEMPERATURE_CONTR); - break; - } - case GET_PIN_NUM(PIN_TEMPERATURE_CONTR): - { - temperature_contr_value = (((uint16_t)ADC_RES) << 2) | ADC_RESL; -#ifdef BBSHD - next_channel = GET_PIN_NUM(PIN_TEMPERATURE_MOTOR); -#else - next_channel = GET_PIN_NUM(PIN_THROTTLE); -#endif - break; - } -#ifdef BBSHD - case GET_PIN_NUM(PIN_TEMPERATURE_MOTOR): - { - temperature_motor_value = (((uint16_t)ADC_RES) << 2) | ADC_RESL; - next_channel = GET_PIN_NUM(PIN_THROTTLE); - break; - } -#endif - } - } - else if (++no_adc_reading_counter == 0) - { - // reinitialize adc - ADC_RES = 0; - ADC_CONTR = (uint8_t)(1 << 7); - no_adc_reading_counter = 0; - throttle_value = 0; - temperature_motor_value = 0; - temperature_contr_value = 0; - next_channel = GET_PIN_NUM(PIN_THROTTLE); - } - else - { - return; - } - - // start next reading - ADC_RES = 0; - ADC_CONTR = (uint8_t)((1 << 7) | (1 << 3) | next_channel); -} - - -uint8_t adc_get_throttle() -{ - return throttle_value; -} - -uint16_t adc_get_torque() -{ - return 0; -} - -uint16_t adc_get_temperature_contr() -{ - return temperature_contr_value; -} - -uint16_t adc_get_temperature_motor() -{ - return temperature_motor_value; -} - -uint16_t adc_get_battery_voltage() -{ - // not implemented, motor MCU sends adc battery voltage value - return 0; -} diff --git a/src/firmware/bbsx/eeprom.c b/src/firmware/bbsx/eeprom.c deleted file mode 100644 index 7a241907..00000000 --- a/src/firmware/bbsx/eeprom.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "eeprom.h" -#include "bbsx/stc15.h" - - -#define EEPROM_NUM_SECTORS 4 - - // STC chips has a special area in flash for eeprom. -#define EEPROM_STC_ADDRESS_OFFSET 0x0000 - -// IAP chips have no special area, same area as program -// memory and address space is the same. We define the last -// four sectors for eeprom usage ourself. -#define EEPROM_IAP_ADDRESS_OFFSET 0xEC00 - -#define IAP_CMD_IDLE 0 -#define IAP_CMD_READ 1 -#define IAP_CMD_PROGRAM 2 -#define IAP_CMD_ERASE 3 - -#define IAP_ENABLE 0x82 // Wait time, CPU_FREQ < 20MHz - - -static uint16_t address_offset = 0x0000; -static uint16_t selected_sector_offset = 0; - - -static void eeprom_begin(uint8_t cmd, int offset) -{ - IAP_CONTR = IAP_ENABLE; - IAP_CMD = cmd; - - uint16_t addr = selected_sector_offset + offset; - IAP_ADDRH = addr >> 8; - IAP_ADDRL = addr; -} - -static bool eeprom_trigger() -{ - IAP_TRIG = 0x5a; - IAP_TRIG = 0xa5; - NOP(); - - return !IS_BIT_SET(IAP_CONTR, 4); -} - -static void eeprom_end() -{ - IAP_CONTR = 0; - IAP_CMD = 0; - IAP_TRIG = 0; - IAP_ADDRH = 0xff; - IAP_ADDRL = 0xff; -} - -void eeprom_init() -{ - // Detect if we are running on IAP or STC model dependeing on if - // we can read from IAP address offset which is outside eeprom - // address space on STC model. - - address_offset = EEPROM_IAP_ADDRESS_OFFSET; - eeprom_select_page(0); - if (eeprom_read_byte(0) == -1) - { - address_offset = EEPROM_STC_ADDRESS_OFFSET; - } -} - -bool eeprom_select_page(int page) -{ - if (page >= 0 && page < EEPROM_NUM_SECTORS) - { - selected_sector_offset = address_offset + page * 512; - return true; - } - - return false; -} - -bool eeprom_erase_page() -{ - bool res; - - eeprom_begin(IAP_CMD_ERASE, 0); - res = eeprom_trigger(); - eeprom_end(); - - return res; -} - -int eeprom_read_byte(int offset) -{ - int res = -1; - - eeprom_begin(IAP_CMD_READ, offset); - - if (eeprom_trigger()) - { - res = IAP_DATA; - } - - eeprom_end(); - - return res; -} - -bool eeprom_write_byte(int offset, uint8_t value) -{ - bool res; - - eeprom_begin(IAP_CMD_PROGRAM, offset); - IAP_DATA = value; - res = eeprom_trigger(); - eeprom_end(); - - return res; -} - -bool eeprom_end_write() -{ - return true; -} diff --git a/src/firmware/bbsx/interrupt.h b/src/firmware/bbsx/interrupt.h deleted file mode 100644 index ea3809cb..00000000 --- a/src/firmware/bbsx/interrupt.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#ifndef _BBSX_INTERRUPT_H_ -#define _BBSX_INTERRUPT_H_ - -#include -#include "intellisense.h" - -#define IRQ_TIMER0 1 -#define IRQ_UART1 4 -#define IRQ_UART2 8 - -INTERRUPT_USING(isr_timer0, IRQ_TIMER0, 1); // system.c -INTERRUPT_USING(isr_uart1, IRQ_UART1, 3); // uart.c -INTERRUPT_USING(isr_uart2, IRQ_UART2, 3); // uart.c - -#endif diff --git a/src/firmware/bbsx/lights.c b/src/firmware/bbsx/lights.c deleted file mode 100644 index 893dd090..00000000 --- a/src/firmware/bbsx/lights.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "lights.h" -#include "bbsx/pins.h" -#include "bbsx/stc15.h" - - -void lights_init() -{ - SET_PIN_OUTPUT(PIN_LIGHTS_POWER); - SET_PIN_OUTPUT(PIN_LIGHTS); - - lights_disable(); - lights_set(false); -} - -void lights_enable() -{ - // enable signal level is swapped on BBSHD vs BBS02... -#if defined(BBSHD) - SET_PIN_HIGH(PIN_LIGHTS_POWER); -#elif defined(BBS02) - SET_PIN_LOW(PIN_LIGHTS_POWER); -#endif -} - -void lights_disable() -{ - // enable signal level is swapped on BBSHD vs BBS02... -#if defined(BBSHD) - SET_PIN_LOW(PIN_LIGHTS_POWER); -#elif defined(BBS02) - SET_PIN_HIGH(PIN_LIGHTS_POWER); -#endif -} - -void lights_set(bool on) -{ - if (on) - { - SET_PIN_LOW(PIN_LIGHTS); - } - else - { - SET_PIN_HIGH(PIN_LIGHTS); - } -} diff --git a/src/firmware/bbsx/motor.c b/src/firmware/bbsx/motor.c deleted file mode 100644 index a8e66af6..00000000 --- a/src/firmware/bbsx/motor.c +++ /dev/null @@ -1,704 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "motor.h" -#include "sensors.h" -#include "system.h" -#include "eventlog.h" -#include "bbsx/uart_motor.h" -#include "bbsx/pins.h" - -#include - -#define OPCODE_LVC 0x60 -#define OPCODE_MAX_CURRENT 0x61 -#define OPCODE_TARGET_SPEED 0x63 -#define OPCODE_TARGET_CURRENT 0x64 -#define OPCODE_HELLO 0x67 -#define OPCODE_UNKNOWN1 0x68 -#define OPCODE_UNKNOWN2 0x69 -#define OPCODE_UNKNOWN3 0x6A -#define OPCODE_UNKNOWN4 0x6B -#define OPCODE_UNKNOWN5 0x6C -#define OPCODE_UNKNOWN5 0x6C -#define OPCODE_UNKNOWN6 0x6D -#define OPCODE_UNKNOWN7 0x6E - -#define OPCODE_READ_STATUS 0x40 -#define OPCODE_READ_CURRENT 0x41 -#define OPCODE_READ_VOLTAGE 0x42 - -#define READ_TIMEOUT 100 - -#if defined(BBSHD) - #define ADC_STEPS_PER_AMP_X10 69 - #define ADC_STEPS_PER_VOLT_X100 1490 // 1460 in orginal firmware -#elif defined(BBS02) - #define ADC_STEPS_PER_AMP_X10 56 - #define ADC_STEPS_PER_VOLT_X100 1510 -#endif - -#define SPEED_STEPS 250 - -// async om state machine -#define COM_STATE_IDLE 0x01 -#define COM_STATE_WAIT_RESPONSE 0x02 -#define COM_STATE_SET_CURRENT 0x03 -#define COM_STATE_SET_SPEED 0x04 -#define COM_STATE_READ_STATUS 0x05 -#define COM_STATE_READ_CURRENT 0x06 -#define COM_STATE_READ_VOLTAGE 0x07 - -#define MSGBUF_SIZE 8 - -static uint8_t is_connected; -static uint8_t msgbuf[MSGBUF_SIZE]; - -static bool target_speed_changed; -static uint8_t target_speed; - -static bool target_current_changed; -static uint8_t target_current; - -static uint16_t adc_steps_per_volt_x100; -static uint16_t lvc_volt_x10; - -static uint16_t status_flags; -static uint16_t battery_volt_x10; -static uint16_t battery_adc_steps; -static uint16_t battery_amp_x10; - -// state machine state -static uint8_t com_state; -static uint8_t last_sent_opcode; -static uint32_t last_request_write_ms; -static uint32_t last_status_read_ms; -static uint8_t next_status_read_opcode; - - -static uint8_t compute_checksum(uint8_t* msg, uint8_t len); -static void send_request(uint8_t opcode, uint16_t data); -static void send_request_async(uint8_t opcode, uint16_t data); - -static int read_response(uint8_t opcode, uint16_t* out_data); -static int try_read_response(uint8_t opcode, uint16_t* out_data); -static int connect(); -static int configure(uint16_t max_current_mA, uint8_t lvc_V); - -static void process_com_state_machine(); - - -void motor_pre_init() -{ - SET_PIN_OUTPUT(PIN_MOTOR_POWER_ENABLE); - SET_PIN_OUTPUT(PIN_MOTOR_CONTROL_ENABLE); - SET_PIN_OUTPUT(PIN_MOTOR_EXTRA); - - SET_PIN_LOW(PIN_MOTOR_POWER_ENABLE); - SET_PIN_HIGH(PIN_MOTOR_CONTROL_ENABLE); - SET_PIN_HIGH(PIN_MOTOR_EXTRA); -} - -void motor_init(uint16_t max_current_mA, uint8_t lvc_V, int16_t adc_calib_volt_steps_x100) -{ - motor_pre_init(); - - is_connected = 0; - target_speed_changed = false; - target_speed = 0; - target_current_changed = false; - target_current = 0; - status_flags = 0; - adc_steps_per_volt_x100 = ADC_STEPS_PER_VOLT_X100 + adc_calib_volt_steps_x100; - lvc_volt_x10 = (uint16_t)lvc_V * 10; - battery_volt_x10 = 0; - battery_adc_steps = 0; - battery_amp_x10 = 0; - - com_state = COM_STATE_IDLE; - last_sent_opcode = 0; - last_request_write_ms = 0; - last_status_read_ms = 0; - next_status_read_opcode = OPCODE_READ_STATUS; - - uart_motor_open(4800); - - // Give other MCU time to power on - while (system_ms() < 100); - - if (connect() && configure(max_current_mA, lvc_V)) - { - is_connected = 1; - - eventlog_write(EVT_MSG_MOTOR_INIT_OK); - - motor_set_target_speed(0); - motor_set_target_current(0); - target_current_changed = true; - target_speed_changed = true; - } - else - { - eventlog_write(EVT_ERROR_INIT_MOTOR); - } -} - -void motor_process() -{ - if (!is_connected) - { - return; - } - - process_com_state_machine(); -} - -void motor_enable() -{ - SET_PIN_HIGH(PIN_MOTOR_POWER_ENABLE); -} - -void motor_disable() -{ - if (!brake_is_activated()) - { - // Brake signal is also connected to motor control MCU. - // If we disable motor power here during braking it causes - // a small issue where change in target current is not accepted - // while in disabled state. This will result in a short power spike - // when brake eventually released. - - SET_PIN_LOW(PIN_MOTOR_POWER_ENABLE); - } -} - -uint16_t motor_status() -{ - return status_flags; -} - -uint8_t motor_get_target_speed() -{ - return target_speed; -} - -uint8_t motor_get_target_current() -{ - return target_current; -} - - -void motor_set_target_speed(uint8_t percent) -{ - if (percent > 100) - { - percent = 100; - } - - if (target_speed != percent) - { - target_speed = percent; - target_speed_changed = true; - } -} - -void motor_set_target_current(uint8_t percent) -{ - if (percent > 100) - { - percent = 100; - } - - if (target_current != percent) - { - target_current = percent; - target_current_changed = true; - } -} - -int16_t motor_calibrate_battery_voltage(uint16_t actual_voltage_x100) -{ - int16_t diff = 0; - if (actual_voltage_x100 != 0) - { - uint16_t calibrated_adc_steps_volt_x100 = (uint16_t)(((uint32_t)battery_adc_steps * 10000u) / actual_voltage_x100); - diff = calibrated_adc_steps_volt_x100 - ADC_STEPS_PER_VOLT_X100; - - adc_steps_per_volt_x100 = calibrated_adc_steps_volt_x100; - } - else - { - // reset calibration if 0 is received - adc_steps_per_volt_x100 = ADC_STEPS_PER_VOLT_X100; - diff = 0; - } - - eventlog_write_data(EVT_DATA_CALIBRATE_VOLTAGE, adc_steps_per_volt_x100); - - return diff; -} - - -uint16_t motor_get_battery_lvc_x10() -{ - return lvc_volt_x10; -} - -uint16_t motor_get_battery_current_x10() -{ - return battery_amp_x10; -} - -uint16_t motor_get_battery_voltage_x10() -{ - return battery_volt_x10; -} - -static uint8_t compute_checksum(uint8_t* msg, uint8_t len) -{ - uint8_t checksum = 0; - for (int i = 0; i < len; ++i) - { - checksum += *(msg + i); - } - - return checksum; -} - -static void send_request(uint8_t opcode, uint16_t data) -{ - // empty rx buffer - while (uart_motor_available()) uart_motor_read(); - - send_request_async(opcode, data); - - uart_motor_flush(); -} - -static void send_request_async(uint8_t opcode, uint16_t data) -{ - uint8_t idx = 0; - - msgbuf[idx++] = 0xaa; // start of message - msgbuf[idx++] = opcode; - - if (opcode == OPCODE_LVC) - { - msgbuf[idx++] = data >> 8; - msgbuf[idx++] = data; - } - else if (opcode != OPCODE_READ_STATUS && opcode != OPCODE_READ_CURRENT && opcode != OPCODE_READ_VOLTAGE) - { - msgbuf[idx++] = data; - } - - uint8_t checksum = compute_checksum(msgbuf + 1, idx - 1); - msgbuf[idx++] = checksum; - - for (uint8_t i = 0; i < idx; ++i) - { - uart_motor_write(msgbuf[i]); - } -} - -static int read_response(uint8_t opcode, uint16_t* out_data) -{ - uint32_t end = system_ms() + READ_TIMEOUT; - - uint8_t len = (opcode == OPCODE_LVC || opcode == OPCODE_READ_STATUS || opcode == OPCODE_READ_VOLTAGE) ? 5 : 4; - - uint8_t i = 0; - while (i < len && system_ms() < end) - { - if (uart_motor_available()) - { - msgbuf[i++] = uart_motor_read(); - } - } - - if (i == len && msgbuf[1] == opcode) - { - uint8_t checksum = compute_checksum(&msgbuf[1], (uint8_t)(i - 2)); - if (checksum == msgbuf[i - 1]) - { - if (out_data != 0) - { - if (opcode == OPCODE_LVC || opcode == OPCODE_READ_STATUS || opcode == OPCODE_READ_VOLTAGE) - { - *out_data = msgbuf[2] << 8 | msgbuf[3]; - } - else - { - *out_data = msgbuf[2]; - } - } - - return 1; - } - - return 0; // failed to verify message - } - - // read failure - return 0; -} - -static int try_read_response(uint8_t opcode, uint16_t* out_data) -{ - uint8_t len = (opcode == OPCODE_LVC || opcode == OPCODE_READ_STATUS || opcode == OPCODE_READ_VOLTAGE) ? 5 : 4; - - uint8_t i = 0; - while (uart_motor_available() && i < MSGBUF_SIZE) - { - msgbuf[i++] = uart_motor_read(); - } - - // clear anything that could be left in rxbuffer in case of error. - while (uart_motor_available()) uart_motor_read(); - - if (i < len) - { - // failed to read entire response - return 0; - } - - if (i == len && msgbuf[1] == opcode) - { - uint8_t checksum = compute_checksum(&msgbuf[1], (uint8_t)(i - 2)); - if (checksum == msgbuf[i - 1]) - { - if (out_data != 0) - { - if (opcode == OPCODE_LVC || opcode == OPCODE_READ_STATUS || opcode == OPCODE_READ_VOLTAGE) - { - *out_data = ((uint16_t)msgbuf[2] << 8) | msgbuf[3]; - } - else - { - *out_data = msgbuf[2]; - } - } - - return 1; - } - - return 0; // failed to verify message - } - - // read failure - return 0; -} - - -static int connect() -{ - for (int i = 0; i < 10; ++i) - { - send_request(OPCODE_HELLO, 0x00); - - if (read_response(OPCODE_HELLO, 0)) - { - system_delay_ms(4); - return 1; - } - else - { - system_delay_ms(1000); - } - } - - return 0; -} - -static int configure(uint16_t max_current_mA, uint8_t lvc_V) -{ - uint16_t tmp = 0; - - // This initialization is done exactly as in orginal firmware for BBSHD/BBS02. - // The meaning of most parameters is unknown. - -#if defined (BBSHD) - send_request(OPCODE_UNKNOWN1, 0x5a); -#elif defined (BBS02) - send_request(OPCODE_UNKNOWN1, 0x5f); -#else - return 0; -#endif - - if (!read_response(OPCODE_UNKNOWN1, 0)) - { - return 0; - } - - system_delay_ms(4); - - send_request(OPCODE_UNKNOWN2, 0x11); - if (!read_response(OPCODE_UNKNOWN2, 0)) - { - return 0; - } - - system_delay_ms(4); - - send_request(OPCODE_UNKNOWN3, 0x78); - if (!read_response(OPCODE_UNKNOWN3, 0)) - { - return 0; - } - - system_delay_ms(4); - - send_request(OPCODE_UNKNOWN4, 0x64); - if (!read_response(OPCODE_UNKNOWN4, 0)) - { - return 0; - } - - system_delay_ms(4); - - send_request(OPCODE_UNKNOWN5, 0x50); - if (!read_response(OPCODE_UNKNOWN5, 0)) - { - return 0; - } - - system_delay_ms(4); - - send_request(OPCODE_UNKNOWN6, 0x46); - if (!read_response(OPCODE_UNKNOWN6, 0)) - { - return 0; - } - - system_delay_ms(4); - - send_request(OPCODE_UNKNOWN7, 0x0c); - if (!read_response(OPCODE_UNKNOWN7, 0)) - { - return 0; - } - - system_delay_ms(4); - - send_request(OPCODE_LVC, (uint32_t)(((uint32_t)lvc_V * adc_steps_per_volt_x100) / 100u)); - if (!read_response(OPCODE_LVC, 0)) - { - return 0; - } - - system_delay_ms(4); - - tmp = (uint16_t)((max_current_mA * (uint32_t)ADC_STEPS_PER_AMP_X10) / 10000UL); - if (tmp > 255) - { - tmp = 255; - } - eventlog_write_data(EVT_DATA_MAX_CURRENT_ADC_REQUEST, tmp); - - send_request(OPCODE_MAX_CURRENT, tmp); - if (!read_response(OPCODE_MAX_CURRENT, &tmp)) - { - return 0; - } - else - { - eventlog_write_data(EVT_DATA_MAX_CURRENT_ADC_RESPONSE, tmp); - } - - system_delay_ms(4); - - return 1; -} - - -static void process_com_state_machine_idle() -{ - // Async state machine loop for serial communication with motor control MCU. - // - // Handles: - // * Set target current - // * Set target speed - // * Read motor status - // * Read motor current - // * Read battery voltage - // - // Set target speed/current are prioritzed over status reading (shorter check interval). - - uint32_t now = system_ms(); - - // make sure requests have some space between them - if (now - last_request_write_ms < 32) - { - return; - } - - if (target_current_changed) - { - send_request_async(OPCODE_TARGET_CURRENT, target_current); - last_sent_opcode = OPCODE_TARGET_CURRENT; - last_request_write_ms = now; - com_state = COM_STATE_WAIT_RESPONSE; - target_current_changed = false; - return; - } - - if (target_speed_changed) - { - send_request_async(OPCODE_TARGET_SPEED, (uint8_t)(((uint16_t)SPEED_STEPS * target_speed) / 100)); - last_sent_opcode = OPCODE_TARGET_SPEED; - last_request_write_ms = now; - com_state = COM_STATE_WAIT_RESPONSE; - target_speed_changed = false; - return; - } - - if ((now - last_status_read_ms) > 200) - { - send_request_async(next_status_read_opcode, 0); - last_sent_opcode = next_status_read_opcode; - last_request_write_ms = now; - com_state = COM_STATE_WAIT_RESPONSE; - if (next_status_read_opcode == OPCODE_READ_STATUS) - { - last_status_read_ms = now; - } - return; - } -} - -static void process_com_state_machine_wait_response() -{ - uint8_t response_length = 0; - - switch (last_sent_opcode) - { - case OPCODE_TARGET_CURRENT: - case OPCODE_TARGET_SPEED: - case OPCODE_READ_CURRENT: - response_length = 4; - break; - case OPCODE_READ_VOLTAGE: - case OPCODE_READ_STATUS: - response_length = 5; - break; - } - - if (uart_motor_available() >= response_length || (system_ms() - last_request_write_ms) > 32) - { - switch (last_sent_opcode) - { - case OPCODE_TARGET_CURRENT: - com_state = COM_STATE_SET_CURRENT; - break; - case OPCODE_TARGET_SPEED: - com_state = COM_STATE_SET_SPEED; - break; - case OPCODE_READ_CURRENT: - com_state = COM_STATE_READ_CURRENT; - break; - case OPCODE_READ_VOLTAGE: - com_state = COM_STATE_READ_VOLTAGE; - break; - case OPCODE_READ_STATUS: - com_state = COM_STATE_READ_STATUS; - break; - default: - com_state = COM_STATE_IDLE; - break; - } - } -} - -static void process_com_state_machine() -{ - uint16_t data; - switch (com_state) - { - case COM_STATE_IDLE: - process_com_state_machine_idle(); - break; - - case COM_STATE_WAIT_RESPONSE: - process_com_state_machine_wait_response(); - break; - - case COM_STATE_SET_CURRENT: - if (try_read_response(OPCODE_TARGET_CURRENT, &data)) - { - eventlog_write_data(EVT_DATA_TARGET_CURRENT, data); - } - else - { - eventlog_write(EVT_ERROR_CHANGE_TARGET_CURRENT); - } - - com_state = COM_STATE_IDLE; - break; - - case COM_STATE_SET_SPEED: - if (try_read_response(OPCODE_TARGET_SPEED, &data)) - { - eventlog_write_data(EVT_DATA_TARGET_SPEED, (uint8_t)((data * 100) / SPEED_STEPS)); - } - else - { - eventlog_write(EVT_ERROR_CHANGE_TARGET_SPEED); - } - - com_state = COM_STATE_IDLE; - break; - - case COM_STATE_READ_STATUS: - if (try_read_response(OPCODE_READ_STATUS, &data)) - { - if (data != status_flags) - { - status_flags = data; - eventlog_write_data(EVT_DATA_MOTOR_STATUS, status_flags); - } - } - else - { - eventlog_write(EVT_ERROR_READ_MOTOR_STATUS); - } - - next_status_read_opcode = OPCODE_READ_CURRENT; - com_state = COM_STATE_IDLE; - break; - - case COM_STATE_READ_CURRENT: - if (try_read_response(OPCODE_READ_CURRENT, &data)) - { - battery_amp_x10 = (data * 100) / ADC_STEPS_PER_AMP_X10; - } - else - { - eventlog_write(EVT_ERROR_READ_MOTOR_CURRENT); - } - - next_status_read_opcode = OPCODE_READ_VOLTAGE; - com_state = COM_STATE_IDLE; - break; - - case COM_STATE_READ_VOLTAGE: - if (try_read_response(OPCODE_READ_VOLTAGE, &data)) - { - battery_adc_steps = data; - battery_volt_x10 = (uint16_t)(((uint32_t)battery_adc_steps * 1000) / adc_steps_per_volt_x100); - } - else - { - eventlog_write(EVT_ERROR_READ_MOTOR_VOLTAGE); - } - - next_status_read_opcode = OPCODE_READ_STATUS; - com_state = COM_STATE_IDLE; - break; - - } -} diff --git a/src/firmware/bbsx/pins.h b/src/firmware/bbsx/pins.h deleted file mode 100644 index 5398c77d..00000000 --- a/src/firmware/bbsx/pins.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#ifndef _PINS_H_ -#define _PINS_H_ - -// PORT, PIN - -#if defined(BBSHD) - - #define PIN_MOTOR_POWER_ENABLE 2, 0 - #define PIN_MOTOR_CONTROL_ENABLE 2, 1 - #define PIN_MOTOR_EXTRA 4, 4 - #define PIN_MOTOR_RX 1, 0 - #define PIN_MOTOR_TX 1, 1 - - #define PIN_VOLTAGE 1, 6 - #define PIN_TEMPERATURE_CONTR 1, 7 - #define PIN_TEMPERATURE_MOTOR 1, 4 - - #define PIN_PAS1 4, 5 - #define PIN_PAS2 4, 6 - - //#define PIN_HALL_U 5, 0 - //#define PIN_HALL_V 3, 4 - //#define PIN_HALL_W 0, 6 - - #define PIN_SPEED_SENSOR 2, 2 - #define PIN_BRAKE 2, 4 - #define PIN_SHIFT_SENSOR 2, 6 - #define PIN_THROTTLE 1, 3 - #define PIN_LIGHTS_POWER 2, 3 // P+ - #define PIN_LIGHTS 5, 1 // Q - - #define PIN_EXTERNAL_RX 3, 0 - #define PIN_EXTERNAL_TX 3, 1 - -#elif defined(BBS02) - - #define PIN_MOTOR_POWER_ENABLE 2, 0 - #define PIN_MOTOR_CONTROL_ENABLE 5, 4 - #define PIN_MOTOR_EXTRA 5, 5 - #define PIN_MOTOR_RX 1, 0 - #define PIN_MOTOR_TX 1, 1 - - #define PIN_VOLTAGE 1, 7 - #define PIN_TEMPERATURE_CONTR 1, 2 - - #define PIN_PAS1 2, 3 - #define PIN_PAS2 2, 4 - - #define PIN_SPEED_SENSOR 2, 6 - #define PIN_BRAKE 3, 3 - #define PIN_SHIFT_SENSOR 3, 6 - #define PIN_THROTTLE 1, 5 - #define PIN_LIGHTS_POWER 0, 3 // P+ - #define PIN_LIGHTS 0, 2 // Q - - #define PIN_EXTERNAL_RX 3, 0 - #define PIN_EXTERNAL_TX 3, 1 - -#endif - -#endif diff --git a/src/firmware/bbsx/sensors.c b/src/firmware/bbsx/sensors.c deleted file mode 100644 index a5c3576a..00000000 --- a/src/firmware/bbsx/sensors.c +++ /dev/null @@ -1,453 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "sensors.h" -#include "system.h" -#include "adc.h" -#include "util.h" -#include "cfgstore.h" -#include "eventlog.h" -#include "fwconfig.h" -#include "bbsx/pins.h" -#include "bbsx/stc15.h" -#include "bbsx/timers.h" - -#include -#include -#include - -// interrupt runs at 100us interval, see timer0 in timers.c -// timer0 is shared between system and sensors modules - -#define PAS_SENSOR_NUM_SIGNALS PAS_PULSES_REVOLUTION -#define PAS_SENSOR_MIN_PULSE_MS_X10 50 // 500rpm limit - -#define SPEED_SENSOR_MIN_PULSE_MS_X10 500 -#define SPEED_SENSOR_TIMEOUT_MS_X10 25000 - - -// Some versions of the BBSHD motor (hall sensor board) -// has a PTC thermistor instead of a NTC thermistor. -// Using standard PT1000 table. -// [R_x100, C_x100] - -#ifdef BBSHD -#define BBSHD_PTC_LUT_SIZE 21 -typedef struct { int32_t x; int16_t y; } pt_t; -static const pt_t bbshd_ptc_lut[BBSHD_PTC_LUT_SIZE] = -{ - { 92100, -2000 }, - { 96090, -1000 }, - { 100000, 0000 }, - { 103900, 1000 }, - { 107790, 2000 }, - { 109730, 2500 }, - { 111670, 3000 }, - { 113610, 3500 }, - { 115540, 4000 }, - { 117470, 4500 }, - { 119400, 5000 }, - { 121320, 5500 }, - { 123240, 6000 }, - { 125160, 6500 }, - { 127080, 7000 }, - { 128990, 7500 }, - { 130900, 8000 }, - { 132800, 8500 }, - { 134710, 9000 }, - { 136610, 9500 }, - { 138510, 10000 } -}; -static bool bbshd_ptc_thermistor; -#endif - - -static volatile uint16_t pas_pulse_counter; -static volatile bool pas_direction_backward; -static volatile uint16_t pas_period_length; // pulse length counted in interrupt frequency (100us) -static uint16_t pas_period_counter; -static bool pas_prev1; -static bool pas_prev2; -static uint16_t pas_stop_delay_periods; - -static volatile uint16_t speed_ticks_period_length; // pulse length counted in interrupt frequency (100us) -static uint16_t speed_period_counter; -static bool speed_prev_state; -static uint8_t speed_ticks_per_rpm; - - -static float thermistor_ntc_calculate_temperature(float R, float invBeta) -{ - const float invT0 = 1.f / 298.15f; - - float K = 1.f / (invT0 + invBeta * (logf(R / 10000.f))); - float C = K - 273.15f; - - return C; -} - -#ifdef BBSHD -static int16_t thermistor_ptc_bbshd_calculate_temperature(int32_t R_x100) -{ - // interpolate in lookup table - - if (R_x100 < bbshd_ptc_lut[0].x) - { - // use minimum value - return bbshd_ptc_lut[0].y; - } - else if (R_x100 > bbshd_ptc_lut[BBSHD_PTC_LUT_SIZE - 1].x) - { - // use maximum value - return bbshd_ptc_lut[BBSHD_PTC_LUT_SIZE - 1].y; - } - - uint8_t i = 0; - for (i = 0; i < BBSHD_PTC_LUT_SIZE - 1; i++) - { - if (bbshd_ptc_lut[i + 1].x > R_x100) - { - break; - } - } - - return (uint16_t)MAP32(R_x100, - bbshd_ptc_lut[i].x, - bbshd_ptc_lut[i + 1].x, - bbshd_ptc_lut[i].y, - bbshd_ptc_lut[i + 1].y); -} -#endif - - -void sensors_init() -{ - // will be evaulated when first reading take place -#ifdef BBSHD - bbshd_ptc_thermistor = false; -#endif - - pas_period_counter = 0; - pas_pulse_counter = 0; - pas_direction_backward = false; - pas_period_length = 0; - pas_stop_delay_periods = 1500; - speed_period_counter = 0; - speed_ticks_period_length = 0; - speed_prev_state = false; - speed_ticks_per_rpm = 1; - - // pins do not have external interrupt, use timer0 to evaluate state frequently - SET_PIN_INPUT(PIN_PAS1); - SET_PIN_INPUT(PIN_PAS2); - SET_PIN_INPUT(PIN_SPEED_SENSOR); - - SET_PIN_QUASI(PIN_BRAKE); // input pullup - SET_PIN_QUASI(PIN_SHIFT_SENSOR); // input pullup - - pas_prev1 = GET_PIN_STATE(PIN_PAS1); - pas_prev2 = GET_PIN_STATE(PIN_PAS2); - - timer0_init_sensors(); -} - -void sensors_process() -{ - -} - -void pas_set_stop_delay(uint16_t delay_ms) -{ - pas_stop_delay_periods = delay_ms * 10; -} - -uint16_t pas_get_cadence_rpm_x10() -{ - uint16_t tmp; - ET0 = 0; // disable timer0 interrupts - tmp = pas_period_length; - ET0 = 1; - - if (tmp > 0) - { - return (uint16_t)((6000000ul / PAS_SENSOR_NUM_SIGNALS) / tmp); - } - else - { - return 0; - } -} - -uint16_t pas_get_pulse_counter() -{ - uint16_t tmp; - ET0 = 0; // disable timer0 interrupts - tmp = pas_pulse_counter; - ET0 = 1; - - return tmp; -} - -bool pas_is_pedaling_forwards() -{ - uint16_t period_length; - uint8_t direction_backward; - ET0 = 0; // disable timer0 interrupts - period_length = pas_period_length; - direction_backward = pas_direction_backward; - ET0 = 1; - - // atomic read operation, no need to disable timer interrupt - return period_length > 0 && !direction_backward; -} - -bool pas_is_pedaling_backwards() -{ - uint16_t period_length; - uint8_t direction_backward; - ET0 = 0; // disable timer0 interrupts - period_length = pas_period_length; - direction_backward = pas_direction_backward; - ET0 = 1; - - return period_length > 0 && direction_backward; -} - -void speed_sensor_set_signals_per_rpm(uint8_t num_signals) -{ - speed_ticks_per_rpm = num_signals; -} - -bool speed_sensor_is_moving() -{ - uint16_t tmp; - ET0 = 0; // disable timer0 interrupts - tmp = speed_ticks_period_length; - ET0 = 1; - - return tmp > 0; -} - -uint16_t speed_sensor_get_rpm_x10() -{ - uint16_t tmp; - ET0 = 0; // disable timer0 interrupts - tmp = speed_ticks_period_length; - ET0 = 1; - - if (tmp > 0) - { - return 6000000ul / tmp / speed_ticks_per_rpm; - } - - return 0; -} - -uint16_t torque_sensor_get_nm_x100() -{ - return 0; -} - -bool torque_sensor_ok() -{ - return true; -} - - -int16_t temperature_contr_x100() -{ - const float R1 = 5100.f; - const float invBeta = 1.f / 3600.f; - static int32_t adc_contr_x100 = 0; - - if (g_config.use_temperature_sensor & TEMPERATURE_SENSOR_CONTR) - { - if (adc_contr_x100 == 0) - { - adc_contr_x100 = adc_get_temperature_contr() * 100l; - } - else - { - adc_contr_x100 = EXPONENTIAL_FILTER(adc_contr_x100, adc_get_temperature_contr() * 100l, 4); - } - - if (adc_contr_x100 != 0) - { - float R = R1 * ((102300.f / (102300.f - adc_contr_x100)) - 1.f); - return (int16_t)(thermistor_ntc_calculate_temperature(R, invBeta) * 100.f + 0.5f); - } - } - - return 0; -} - -int16_t temperature_motor_x100() -{ - // Sensor only present in the BBSHD motor -#if HAS_MOTOR_TEMP_SENSOR - const float R1 = 5100.f; - const float invBeta = 1.f / 3990.f; - - static int32_t adc_motor_x100 = 0; - - if (g_config.use_temperature_sensor & TEMPERATURE_SENSOR_MOTOR) - { - bool first = false; - if (adc_motor_x100 == 0) - { - first = true; - adc_motor_x100 = adc_get_temperature_motor() * 100l; - } - else - { - adc_motor_x100 = EXPONENTIAL_FILTER(adc_motor_x100, adc_get_temperature_motor() * 100l, 4); - } - - if (adc_motor_x100 != 0) - { - float R = R1 * ((102300.f / (102300.f - adc_motor_x100)) - 1.f); - - if (first) - { - if (R > 1500.f) - { - // not likely to be a 1k ptc thermistor, assume 10k ntc - bbshd_ptc_thermistor = false; - eventlog_write_data(EVT_DATA_BBSHD_THERMISTOR, 0); - } - else - { - bbshd_ptc_thermistor = true; - eventlog_write_data(EVT_DATA_BBSHD_THERMISTOR, 1); - } - } - - if (bbshd_ptc_thermistor) - { - return thermistor_ptc_bbshd_calculate_temperature((int32_t)(R * 100.f + 0.5f)); - } - else - { - return(int16_t)(thermistor_ntc_calculate_temperature(R, invBeta) * 100.f + 0.5f); - } - } - } -#endif - - return 0; -} - - -bool brake_is_activated() -{ - return !GET_PIN_STATE(PIN_BRAKE); -} - -bool shift_sensor_is_activated() -{ - return !GET_PIN_STATE(PIN_SHIFT_SENSOR); -} - - -#pragma save -#pragma nooverlay // See SDCC manual about function calls in ISR -void sensors_timer0_isr() // runs every 100us, see timers.c -{ - // WARNING: - // No 16/32 bit or float computations in ISR (multiply/divide/modulo). - // Read SDCC compiler manual for more info. - - // Pas - { - bool pas1 = GET_PIN_STATE(PIN_PAS1); - bool pas2 = GET_PIN_STATE(PIN_PAS2); - - if (pas1 && !pas_prev1 /* && pas_period_counter > PAS_SENSOR_MIN_PULSE_MS_X10 */) - { - pas_pulse_counter++; - - if (pas_direction_backward != pas2) - { - pas_direction_backward = pas2; - - // Reset pas pulse counter if pedal direction is changed, - // this variable counts the number of pulses since start of pedaling session. - pas_pulse_counter = 0; - } - - if (pas_period_counter > 0) - { - if (pas_period_counter <= pas_stop_delay_periods) - { - pas_period_length = pas_period_counter; // save in order to be able to calculate rpm when needed - } - else - { - pas_period_length = 0; - } - - pas_period_counter = 0; - } - } - else - { - // Do not allow wraparound or computed pedaling cadence will wrong after pedals has been still. - if (pas_period_counter < 65535) - { - pas_period_counter++; - } - - if (pas_period_length > 0 && pas_period_counter > pas_stop_delay_periods) - { - pas_period_length = 0; - pas_pulse_counter = 0; - pas_direction_backward = false; - } - } - - pas_prev1 = pas1; - pas_prev2 = pas2; - } - - - // Speed sensor - { - bool spd = GET_PIN_STATE(PIN_SPEED_SENSOR); - - if (spd && !speed_prev_state && speed_period_counter > SPEED_SENSOR_MIN_PULSE_MS_X10) - { - if (speed_period_counter <= SPEED_SENSOR_TIMEOUT_MS_X10) - { - speed_ticks_period_length = speed_period_counter; - } - else - { - speed_ticks_period_length = 0; - } - - speed_period_counter = 0; - } - else - { - // Do not allow wraparound or computed speed will wrong after bike has been still. - if (speed_period_counter < 65535) - { - speed_period_counter++; - } - - if (speed_ticks_period_length > 0 && speed_period_counter > SPEED_SENSOR_TIMEOUT_MS_X10) - { - speed_ticks_period_length = 0; - } - } - - speed_prev_state = spd; - } - -} -#pragma restore diff --git a/src/firmware/bbsx/stc15.h b/src/firmware/bbsx/stc15.h deleted file mode 100644 index b8601ddb..00000000 --- a/src/firmware/bbsx/stc15.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#ifndef _STC_15_H_ -#define _STC_15_H_ - -#include "intellisense.h" - -#if !defined (SDCC) && !defined (__SDCC) - -#define __sfr unsigned char -#define __sbit bool -#define __at(X) - -#define __xdata -#define __data - -#endif - -#include <8051.h> -#include - - -// Peripheral function switch -SFR(P_SW2, 0xBA); - -SFR(PCON2, 0x97); - -SFR(T2H, 0xD6); -SFR(T2L, 0xD7); - -SBIT(P5_4, 0xC8, 4); -SBIT(P5_5, 0xC8, 5); - - -#define IS_BIT_SET(REG, BIT_NUM) ((REG >> BIT_NUM) & 1) - -#define SET_BIT(REG, BIT_NUM) (REG |= (1 << BIT_NUM)) -#define CLEAR_BIT(REG, BIT_NUM) (REG &= ~(1 << BIT_NUM)) -#define TOGGLE_BIT(REG, BIT_NUM) (REG ^= (1 << BIT_NUM)) - -#define EXPAND(x) x - -#define SET_PIN_INPUT_(PORT, PIN) CLEAR_BIT(P##PORT##M0, PIN); SET_BIT(P##PORT##M1, PIN) -#define SET_PIN_INPUT(...) EXPAND(SET_PIN_INPUT_(__VA_ARGS__)) - -#define SET_PIN_QUASI_(PORT, PIN) CLEAR_BIT(P##PORT##M0, PIN); CLEAR_BIT(P##PORT##M1, PIN) -#define SET_PIN_QUASI(...) EXPAND(SET_PIN_QUASI_(__VA_ARGS__)) - -#define SET_PIN_OUTPUT_(PORT, PIN) SET_BIT(P##PORT##M0, PIN); CLEAR_BIT(P##PORT##M1, PIN) -#define SET_PIN_OUTPUT(...) EXPAND(SET_PIN_OUTPUT_(__VA_ARGS__)) - - -#define GET_PIN_STATE_(PORT, PIN) P##PORT##_##PIN -#define GET_PIN_STATE(...) EXPAND(GET_PIN_STATE_(__VA_ARGS__)) - -#define SET_PIN_HIGH_(PORT, PIN) P##PORT##_##PIN = 1 -#define SET_PIN_HIGH(...) EXPAND(SET_PIN_HIGH_(__VA_ARGS__)) - -#define SET_PIN_LOW_(PORT, PIN) P##PORT##_##PIN = 0 -#define SET_PIN_LOW(...) EXPAND(SET_PIN_LOW_(__VA_ARGS__)) - - -#define GET_PIN_NUM_(PORT, PIN) PIN -#define GET_PIN_NUM(...) EXPAND(GET_PIN_NUM_(__VA_ARGS__)) - -#define GET_PORT_NUM_(PORT, PIN) PORT -#define GET_PORT_NUM(...) EXPAND(GET_PORT_NUM_(__VA_ARGS__)) - -#endif diff --git a/src/firmware/bbsx/system.c b/src/firmware/bbsx/system.c deleted file mode 100644 index 42663e5c..00000000 --- a/src/firmware/bbsx/system.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "system.h" -#include "watchdog.h" -#include "timers.h" -#include "bbsx/stc15.h" - -static volatile uint32_t _ms; -static volatile uint8_t _x100us; - -void system_init() -{ - _ms = 0; - _x100us = 0; - - // Wait for stable voltage (above lvd) - while (IS_BIT_SET(PCON, 5)) - { - CLEAR_BIT(PCON, 5); - } - - timer0_init_system(); -} - -uint32_t system_ms() -{ - uint32_t val; - uint8_t et0 = ET0; - ET0 = 0; // disable timer0 interrupts - val = _ms; - ET0 = et0; - return val; -} - -void system_delay_ms(uint16_t ms) -{ - if (!ms) - { - return; - } - - uint32_t end = system_ms() + ms; - while (system_ms() != end) - { - watchdog_yeild(); - } -} - -#pragma save -#pragma nooverlay // See SDCC manual about function calls in ISR -void system_timer0_isr() -{ - _x100us++; - if (_x100us == 10) - { - _x100us = 0; - _ms++; - } -} -#pragma restore diff --git a/src/firmware/bbsx/timers.c b/src/firmware/bbsx/timers.c deleted file mode 100644 index f92d93e9..00000000 --- a/src/firmware/bbsx/timers.c +++ /dev/null @@ -1,96 +0,0 @@ -#include "timers.h" -#include "bbsx/timers.h" -#include "bbsx/interrupt.h" -#include "bbsx/cpu.h" - -#include - -#define TIMER0_RELOAD ((65535 - CPU_FREQ / 10000) + 1) - - -extern void system_timer0_isr(); -extern void sensors_timer0_isr(); - -static bool timer0_system_ready; -static bool timer0_sensors_ready; - -static void timer0_init() -{ - if (timer0_system_ready || timer0_sensors_ready) - { - // already initialized - return; - } - - EA = 0; // disable interrupts - - TMOD = (TMOD & 0xf0) | 0x00; // Timer 0: 16-bit with autoreload - AUXR |= 0x80; // Run timer 0 at CPU_FREQ - - TH0 = TIMER0_RELOAD >> 8; - TL0 = TIMER0_RELOAD; - - EA = 1; // enable interrupts - ET0 = 1; // enable timer0 interrupts - TR0 = 1; // start timer 0 -} - - -void timers_init() -{ - timer0_system_ready = false; - timer0_sensors_ready = false; -} - -void timer0_init_system() -{ - timer0_init(); - timer0_system_ready = true; -} - -void timer0_init_sensors() -{ - timer0_init(); - timer0_sensors_ready = true; -} - -void timer1_init_uart1(uint32_t baudrate) -{ - unsigned short reload = 65535 - CPU_FREQ / 4 / baudrate + 1; - - // Set up timer 1 for baudrate - TMOD = (TMOD & 0x0f) | 0x00; // Run T1 in mode 0 (16-bit reload) - AUXR |= 0x40; // Run T1 at CPU_FREQ - TL1 = reload; // Set the reload value for given baudrate. - TH1 = reload >> 8; - ET1 = 0; // No interrupts from timer 1. - TR1 = 1; // Start timer 1 -} - -void timer2_init_uart2(uint32_t baudrate) -{ - unsigned short reload = 65535 - CPU_FREQ / 4 / baudrate + 1; - - // Set up timer 2 for baudrate - AUXR &= ~(1 << 3); // as timer - AUXR |= (1 << 2); // Run T2 at CPU_FREQ - T2H = reload >> 8; - T2L = reload; - IE2 &= ~(1 << 2); // No interrupts from timer 2 - AUXR |= (1 << 4); // Start timer 2 -} - - -// timer0 is shared between system ms counter and sensors check -INTERRUPT_USING(isr_timer0, IRQ_TIMER0, 1) -{ - if (timer0_system_ready) - { - system_timer0_isr(); - } - - if (timer0_sensors_ready) - { - sensors_timer0_isr(); - } -} diff --git a/src/firmware/bbsx/uart.c b/src/firmware/bbsx/uart.c deleted file mode 100644 index 498ac5b7..00000000 --- a/src/firmware/bbsx/uart.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "uart.h" -#include "system.h" -#include "watchdog.h" -#include "bbsx/stc15.h" -#include "bbsx/uart_motor.h" -#include "bbsx/timers.h" -#include "bbsx/pins.h" - -#include - -// NOTE: -// Variables located in __data are there for atomic access. - -// UART1 (main) -#define RX1_BUFFER_SIZE 64 -#define RX1_BUFFER_MASK (RX1_BUFFER_SIZE - 1) - -#define TX1_BUFFER_SIZE 32 -#define TX1_BUFFER_MASK (TX1_BUFFER_SIZE - 1) - -static volatile __data uint8_t rx1_head; -static volatile __data uint8_t rx1_tail; -static volatile uint8_t rx1_buf[RX1_BUFFER_SIZE]; -static volatile __data uint8_t tx1_head; -static volatile __data uint8_t tx1_tail; -static volatile __data uint8_t tx1_sending; -static volatile uint8_t tx1_buf[TX1_BUFFER_SIZE]; - -// UART2 (motor) -#define RX2_BUFFER_SIZE 16 -#define RX2_BUFFER_MASK (RX2_BUFFER_SIZE - 1) - -#define TX2_BUFFER_SIZE 16 -#define TX2_BUFFER_MASK (TX2_BUFFER_SIZE - 1) - -static volatile __data uint8_t rx2_head; -static volatile __data uint8_t rx2_tail; -static volatile uint8_t rx2_buf[RX2_BUFFER_SIZE]; -static volatile __data uint8_t tx2_head; -static volatile __data uint8_t tx2_tail; -static volatile __data uint8_t tx2_sending; -static volatile uint8_t tx2_buf[TX2_BUFFER_SIZE]; - - -void uart_open(uint32_t baudrate) -{ - rx1_head = 0; - rx1_tail = 0; - tx1_head = 0; - tx1_tail = 0; - tx1_sending = 0; - -#if (GET_PORT_NUM(PIN_EXTERNAL_RX) == 3 && GET_PORT_NUM(PIN_EXTERNAL_TX) == 3) - AUXR1 = (AUXR1 & 0x3f) | 0x00; // Keep UART1 on P3.0/P3.1 -#else - #error Unupported UART port configured. -#endif - - SET_PIN_QUASI(PIN_EXTERNAL_RX); - SET_PIN_QUASI(PIN_EXTERNAL_TX); - - AUXR &= ~0x01; // Clock UART1 from T1 - PCON &= ~0x40; // Expose SM0 bit - SM1 = 1; // UART 8-N-1 - SM0 = 0; - SM2 = 0; // Point-to-point UART - ES = 1; // Enable serial interrupt - - timer1_init_uart1(baudrate); - - REN = 1; // Rx enable -} - -void uart_motor_open(uint32_t baudrate) -{ - rx2_head = 0; - rx2_tail = 0; - tx2_head = 0; - tx2_tail = 0; - tx2_sending = 0; - -#if (GET_PORT_NUM(PIN_MOTOR_RX) == 1 && GET_PORT_NUM(PIN_MOTOR_TX) == 1) - { - P_SW2 = (P_SW2 & 0xfe) | 0x00; // Keep UART2 on P1.0/P1.1 - } -#else - #error Unupported UART port configured. -#endif - - SET_PIN_QUASI(PIN_MOTOR_RX); - SET_PIN_QUASI(PIN_MOTOR_TX); - - // UART 2 can only user timer 2 - S2CON &= ~(1 << 7); // UART 8-N-1 - S2CON &= ~(1 << 5); // Point-to-point UART - IE2 |= (1 << 0); // Enable serial 2 interrupt - - timer2_init_uart2(baudrate); - - S2CON |= (1 << 4); // Rx enable -} - -void uart_close() -{ - REN = 0; - uart_flush(); - TR1 = 0; -} - -void uart_motor_close() -{ - S2CON &= ~(1 << 4); // Rx disable - uart_motor_flush(); - AUXR &= ~(1 << 4); // Stop timer 2 -} - - -uint8_t uart_available() -{ - return (RX1_BUFFER_SIZE + rx1_head - rx1_tail) & RX1_BUFFER_MASK; -} - -uint8_t uart_motor_available() -{ - return (RX2_BUFFER_SIZE + rx2_head - rx2_tail) & RX2_BUFFER_MASK; -} - -uint8_t uart_read() -{ - uint8_t byte = rx1_buf[rx1_tail]; - rx1_tail = (rx1_tail + 1) & RX1_BUFFER_MASK; - return byte; -} - -uint8_t uart_motor_read() -{ - uint8_t byte = rx2_buf[rx2_tail]; - rx2_tail = (rx2_tail + 1) & RX2_BUFFER_MASK; - return byte; -} - -void uart_write(uint8_t byte) -{ - if (!tx1_sending) - { - tx1_sending = 1; - SBUF = byte; - - return; - } - - uint8_t i = (tx1_head + 1) & TX1_BUFFER_MASK; - - // wait for free space in buffer - uint8_t prev_tail = tx1_tail; - while (i == tx1_tail) - { - if (tx1_tail != prev_tail) - { - prev_tail = tx1_tail; - watchdog_yeild(); - } - } - - tx1_buf[tx1_head] = byte; - tx1_head = i; - -} - -void uart_motor_write(uint8_t byte) -{ - if (!tx2_sending) - { - tx2_sending = 1; - S2BUF = byte; - - return; - } - - uint8_t i = (tx2_head + 1) & TX2_BUFFER_MASK; - - // wait for free space in buffer - uint8_t prev_tail = tx2_tail; - while (i == tx2_tail) - { - if (tx2_tail != prev_tail) - { - prev_tail = tx2_tail; - watchdog_yeild(); - } - } - - tx2_buf[tx2_head] = byte; - tx2_head = i; -} - -void uart_flush() -{ - while (tx1_sending); -} - -void uart_motor_flush() -{ - while (tx2_sending); -} - - -INTERRUPT_USING(isr_uart1, IRQ_UART1, 3) -{ - if (RI) // rx interrupt - { - RI = 0; - - uint8_t c = SBUF; - uint8_t i = (rx1_head + 1) & RX1_BUFFER_MASK; - - if (i != rx1_tail) - { - rx1_buf[rx1_head] = c; - rx1_head = i; - } - } - - if (TI) // tx interrupt - { - TI = 0; - - if (tx1_head != tx1_tail) - { - tx1_sending = 1; - - SBUF = tx1_buf[tx1_tail]; - tx1_tail = (tx1_tail + 1) & TX1_BUFFER_MASK; - } - else - { - tx1_sending = 0; - } - } -} - -INTERRUPT_USING(isr_uart2, IRQ_UART2, 3) -{ - if (S2CON & (1 << 0)) // rx interrupt - { - S2CON &= ~(1 << 0); - - uint8_t c = S2BUF; - uint8_t i = (rx2_head + 1) & RX2_BUFFER_MASK; - - if (i != rx2_tail) - { - rx2_buf[rx2_head] = c; - rx2_head = i; - } - } - - if (S2CON & (1 << 1)) // tx interrupt - { - S2CON &= ~(1 << 1); - - if (tx2_head != tx2_tail) - { - tx2_sending = 1; - - S2BUF = tx2_buf[tx2_tail]; - tx2_tail = (tx2_tail + 1) & TX2_BUFFER_MASK; - } - else - { - tx2_sending = 0; - } - } -} - diff --git a/src/firmware/cfgstore.c b/src/firmware/cfgstore.c deleted file mode 100644 index d3d06221..00000000 --- a/src/firmware/cfgstore.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "cfgstore.h" -#include "eeprom.h" -#include "eventlog.h" -#include "uart.h" -#include "fwconfig.h" - -#include - -#define EEPROM_CONFIG_PAGE 0 -#define EEPROM_PSTATE_PAGE 1 - -#define EEPROM_OK 0 -#define EEPROM_ERROR_SELECT_PAGE 1 -#define EEPROM_ERROR_READ 2 -#define EEPROM_ERROR_VERSION 3 -#define EEPROM_ERROR_LENGHT 4 -#define EEPROM_ERROR_CHECKSUM 5 -#define EEPROM_ERROR_ERASE 6 -#define EEPROM_ERROR_WRITE 7 - -static const uint8_t default_current_limits[] = { 7, 10, 14, 19, 26, 36, 50, 70, 98 }; - -#if HAS_TORQUE_SENSOR -static const uint8_t default_torque_factors[] = { 10, 15, 23, 44, 57, 74, 88, 105, 126 }; -#endif - -typedef struct -{ - uint8_t version; - uint8_t length; - uint8_t checksum; -} header_t; - -static header_t header; - -config_t g_config; -pstate_t g_pstate; - -static uint8_t read(uint8_t page, uint8_t version, uint8_t* dst, uint8_t size); -static uint8_t write(uint8_t page, uint8_t version, uint8_t* src, uint8_t size); - -static bool read_config(); -static bool write_config(); -static void load_default_config(); - -static bool read_pstate(); -static bool write_pstate(); -static void load_default_pstate(); - -void cfgstore_init() -{ - if (!read_config()) - { - cfgstore_reset_config(); - } - - if (!read_pstate()) - { - cfgstore_reset_pstate(); - } -} - -bool cfgstore_reset_config() -{ - load_default_config(); - if (write_config()) - { - eventlog_write(EVT_MSG_CONFIG_RESET); - return true; - } - - return false; -} - -bool cfgstore_save_config() -{ - return write_config(); -} - -bool cfgstore_reset_pstate() -{ - load_default_pstate(); - return write_pstate(); -} - -bool cfgstore_save_pstate() -{ - return write_pstate(); -} - -static bool read_config() -{ - eventlog_write(EVT_MSG_CONFIG_READ_BEGIN); - - uint8_t res = read(EEPROM_CONFIG_PAGE, CONFIG_VERSION, (uint8_t*)&g_config, sizeof(config_t)); - switch (res) - { - default: - eventlog_write(EVT_ERROR_EEPROM_READ); - break; - case EEPROM_ERROR_VERSION: - eventlog_write(EVT_ERROR_EEPROM_VERIFY_VERSION); - break; - case EEPROM_ERROR_LENGHT: - case EEPROM_ERROR_CHECKSUM: - eventlog_write(EVT_ERROR_EEPROM_VERIFY_CHECKSUM); - break; - case EEPROM_OK: - eventlog_write(EVT_MSG_CONFIG_READ_DONE); - break; - } - - return res == EEPROM_OK; -} - -static bool write_config() -{ - eventlog_write(EVT_MSG_CONFIG_WRITE_BEGIN); - - uint8_t res = write(EEPROM_CONFIG_PAGE, CONFIG_VERSION, (uint8_t*)&g_config, sizeof(config_t)); - switch (res) - { - default: - eventlog_write(EVT_ERROR_EEPROM_WRITE); - break; - case EEPROM_ERROR_ERASE: - eventlog_write(EVT_ERROR_EEPROM_ERASE); - break; - case EEPROM_OK: - eventlog_write(EVT_MSG_CONFIG_WRITE_DONE); - break; - } - - return res == EEPROM_OK; -} - -static void load_default_config() -{ - g_config.use_freedom_units = 0; - -#if defined(BBSHD) - g_config.max_current_amps = 30; -#elif defined(BBS02) - g_config.max_current_amps = 25; -#else - g_config.max_current_amps = 20; -#endif - - g_config.current_ramp_amps_s = 10; - g_config.max_battery_x100v_u16l = (uint8_t)5460; - g_config.max_battery_x100v_u16h = (uint8_t)(5460 >> 8); - g_config.low_cut_off_v = 42; - - g_config.use_speed_sensor = 1; - g_config.use_shift_sensor = HAS_SHIFT_SENSOR_SUPPORT; - g_config.use_push_walk = 1; - g_config.use_pretension = 0; - g_config.pretension_speed_cutoff_kph = 16; - g_config.use_temperature_sensor = TEMPERATURE_SENSOR_CONTR | TEMPERATURE_SENSOR_MOTOR; - - g_config.lights_mode = LIGHTS_MODE_DEFAULT; - - g_config.wheel_size_inch_x10_u16l = (uint8_t)280; - g_config.wheel_size_inch_x10_u16h = (uint8_t)(280 >> 8); - - g_config.speed_sensor_signals = 1; - g_config.max_speed_kph = 100; - - g_config.pas_start_delay_pulses = 5; - g_config.pas_stop_delay_x100s = 20; - g_config.pas_keep_current_percent = 60; - g_config.pas_keep_current_cadence_rpm = 40; - - g_config.throttle_start_voltage_mv_u16l = (uint8_t)1000; - g_config.throttle_start_voltage_mv_u16h = (uint8_t)(1000 >> 8); - g_config.throttle_end_voltage_mv_u16l = (uint8_t)3600; - g_config.throttle_end_voltage_mv_u16h = (uint8_t)(3600 >> 8); - g_config.throttle_start_percent = 1; - g_config.throttle_global_spd_lim_opt = THROTTLE_GLOBAL_SPEED_LIMIT_DISABLED; - g_config.throttle_global_spd_lim_percent = 100; - - g_config.shift_interrupt_duration_ms_u16l = (uint8_t)600; - g_config.shift_interrupt_duration_ms_u16h = (uint8_t)(600 >> 8); - g_config.shift_interrupt_current_threshold_percent = 10; - - g_config.walk_mode_data_display = WALK_MODE_DATA_SPEED; - - g_config.assist_mode_select = ASSIST_MODE_SELECT_OFF; - g_config.assist_startup_level = 3; - - memset(&g_config.assist_levels, 0, 20 * sizeof(assist_level_t)); - - for (uint8_t i = 0; i < 9; ++i) - { - g_config.assist_levels[0][i+1].flags = ASSIST_FLAG_PAS | ASSIST_FLAG_THROTTLE; - g_config.assist_levels[0][i+1].max_cadence_percent = 100; - g_config.assist_levels[0][i+1].max_speed_percent = 100; - g_config.assist_levels[0][i+1].max_throttle_current_percent = 100; - -#if HAS_TORQUE_SENSOR - g_config.assist_levels[0][i+1].flags |= ASSIST_FLAG_PAS_TORQUE; - g_config.assist_levels[0][i+1].target_current_percent = 100; - g_config.assist_levels[0][i+1].torque_amplification_factor_x10 = default_torque_factors[i]; -#else - g_config.assist_levels[0][i+1].target_current_percent = default_current_limits[i]; - g_config.assist_levels[0][i+1].torque_amplification_factor_x10 = 0; -#endif - } -} - -static bool read_pstate() -{ - eventlog_write(EVT_MSG_PSTATE_READ_BEGIN); - - uint8_t res = read(EEPROM_PSTATE_PAGE, PSTATE_VERSION, (uint8_t*)&g_pstate, sizeof(pstate_t)); - switch (res) - { - default: - eventlog_write(EVT_ERROR_EEPROM_READ); - break; - case EEPROM_ERROR_VERSION: - eventlog_write(EVT_ERROR_EEPROM_VERIFY_VERSION); - break; - case EEPROM_ERROR_LENGHT: - case EEPROM_ERROR_CHECKSUM: - eventlog_write(EVT_ERROR_EEPROM_VERIFY_CHECKSUM); - break; - case EEPROM_OK: - eventlog_write(EVT_MSG_PSTATE_READ_DONE); - break; - } - - return res == EEPROM_OK; -} - -static bool write_pstate() -{ - eventlog_write(EVT_MSG_PSTATE_WRITE_BEGIN); - - uint8_t res = write(EEPROM_PSTATE_PAGE, PSTATE_VERSION, (uint8_t*)&g_pstate, sizeof(pstate_t)); - switch (res) - { - default: - eventlog_write(EVT_ERROR_EEPROM_WRITE); - break; - case EEPROM_ERROR_ERASE: - eventlog_write(EVT_ERROR_EEPROM_ERASE); - break; - case EEPROM_OK: - eventlog_write(EVT_MSG_PSTATE_WRITE_DONE); - break; - } - - return res == EEPROM_OK; - -} - -static void load_default_pstate() -{ - g_pstate.adc_voltage_calibration_steps_x100_i16l = 0; - g_pstate.adc_voltage_calibration_steps_x100_i16h = 0; -} - -static uint8_t read(uint8_t page, uint8_t version, uint8_t* dst, uint8_t size) -{ - uint8_t read_offset = 0; - uint8_t* ptr = 0; - uint8_t i = 0; - int data; - - if (!eeprom_select_page(page)) - { - return EEPROM_ERROR_SELECT_PAGE; - } - - ptr = (uint8_t*)&header; - for (i = 0; i < sizeof(header_t); ++i) - { - data = eeprom_read_byte(read_offset); - if (data < 0) - { - return EEPROM_ERROR_READ; - } - *ptr = (uint8_t)data; - ++read_offset; - ++ptr; - } - - // verify header ok - if (header.version != version) - { - return EEPROM_ERROR_VERSION; - } - - if (header.length != size) - { - return EEPROM_ERROR_LENGHT; - } - - uint8_t checksum = 0; - - ptr = dst; - for (i = 0; i < size; ++i) - { - data = eeprom_read_byte(read_offset); - if (data < 0) - { - return EEPROM_ERROR_READ; - } - - checksum += (uint8_t)data; - *ptr = (uint8_t)data; - ++read_offset; - ++ptr; - } - - if (header.checksum != checksum) - { - return EEPROM_ERROR_CHECKSUM; - } - - return EEPROM_OK; -} - -static uint8_t write(uint8_t page, uint8_t version, uint8_t* src, uint8_t size) -{ - uint8_t write_offset = 0; - uint8_t* ptr = 0; - uint8_t i = 0; - - header.version = version; - header.length = size; - header.checksum = 0; - - if (!eeprom_select_page(page)) - { - return EEPROM_ERROR_SELECT_PAGE; - } - - if (!eeprom_erase_page()) - { - return EEPROM_ERROR_ERASE; - } - - write_offset += sizeof(header_t); - - ptr = src; - for (i = 0; i < size; ++i) - { - if (!eeprom_write_byte(write_offset, *ptr)) - { - eeprom_end_write(); - return EEPROM_ERROR_WRITE; - } - - header.checksum += *ptr; - ++write_offset; - ++ptr; - } - - write_offset = 0; - ptr = (uint8_t*)&header; - for (i = 0; i < sizeof(header_t); ++i) - { - if (!eeprom_write_byte(write_offset, *ptr)) - { - eeprom_end_write(); - return EEPROM_ERROR_WRITE; - } - - ++write_offset; - ++ptr; - } - - eeprom_end_write(); - - return EEPROM_OK; -} diff --git a/src/firmware/cfgstore.h b/src/firmware/cfgstore.h deleted file mode 100644 index 8253bdd9..00000000 --- a/src/firmware/cfgstore.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#ifndef _CFGSTORE_H_ -#define _CFGSTORE_H_ - -#include "intellisense.h" -#include -#include - -#define ASSIST_FLAG_PAS 0x01 -#define ASSIST_FLAG_THROTTLE 0x02 -#define ASSIST_FLAG_CRUISE 0x04 -#define ASSIST_FLAG_PAS_VARIABLE 0x08 // pas mode using throttle to set power level -#define ASSIST_FLAG_PAS_TORQUE 0x10 // pas mode using torque sensor reading -#define ASSIST_FLAG_OVERRIDE_CADENCE 0x20 // pas option where max cadence is set to 100% when throttle overrides pas -#define ASSIST_FLAG_OVERRIDE_SPEED 0x40 // pas option where max speed is set to 100% when throttle overrides pas - -#define ASSIST_MODE_SELECT_OFF 0x00 -#define ASSIST_MODE_SELECT_STANDARD 0x01 -#define ASSIST_MODE_SELECT_LIGHTS 0x02 -#define ASSIST_MODE_SELECT_PAS0_LIGHT 0x03 -#define ASSIST_MODE_SELECT_PAS1_LIGHT 0x04 -#define ASSIST_MODE_SELECT_PAS2_LIGHT 0x05 -#define ASSIST_MODE_SELECT_PAS3_LIGHT 0x06 -#define ASSIST_MODE_SELECT_PAS4_LIGHT 0x07 -#define ASSIST_MODE_SELECT_PAS5_LIGHT 0x08 -#define ASSIST_MODE_SELECT_PAS6_LIGHT 0x09 -#define ASSIST_MODE_SELECT_PAS7_LIGHT 0x0A -#define ASSIST_MODE_SELECT_PAS8_LIGHT 0x0B -#define ASSIST_MODE_SELECT_PAS9_LIGHT 0x0C -#define ASSIST_MODE_SELECT_BRAKE_BOOT 0x0D - - -#define TEMPERATURE_SENSOR_CONTR 0x01 -#define TEMPERATURE_SENSOR_MOTOR 0x02 - -#define WALK_MODE_DATA_SPEED 0 -#define WALK_MODE_DATA_TEMPERATURE 1 -#define WALK_MODE_DATA_REQUESTED_POWER 2 -#define WALK_MODE_DATA_BATTERY_PERCENT 3 - -#define THROTTLE_GLOBAL_SPEED_LIMIT_DISABLED 0 -#define THROTTLE_GLOBAL_SPEED_LIMIT_ENABLED 1 -#define THROTTLE_GLOBAL_SPEED_LIMIT_STD_LVLS 2 - -#define LIGHTS_MODE_DEFAULT 0 -#define LIGHTS_MODE_DISABLED 1 -#define LIGHTS_MODE_ALWAYS_ON 2 -#define LIGHTS_MODE_BRAKE_LIGHT 3 - -#define CONFIG_VERSION 5 -#define PSTATE_VERSION 1 - - -typedef struct -{ - uint8_t flags; - uint8_t target_current_percent; - uint8_t max_throttle_current_percent; - uint8_t max_cadence_percent; - uint8_t max_speed_percent; - - // 10 => 1.0: 100w human power gives and additional 100w motor power - uint8_t torque_amplification_factor_x10; -} assist_level_t; - -// SDCC uses little endian for MCS51 and big endian for STM8... -typedef struct -{ - // hmi units - uint8_t use_freedom_units; - - // global - uint8_t max_current_amps; - uint8_t current_ramp_amps_s; - uint8_t max_battery_x100v_u16l; - uint8_t max_battery_x100v_u16h; - uint8_t low_cut_off_v; - uint8_t max_speed_kph; - - // externals - uint8_t use_speed_sensor; - uint8_t use_shift_sensor; - uint8_t use_push_walk; - uint8_t use_temperature_sensor; - uint8_t lights_mode; - uint8_t use_pretension; - uint8_t pretension_speed_cutoff_kph; - - // speed sensor - uint8_t wheel_size_inch_x10_u16l; - uint8_t wheel_size_inch_x10_u16h; - uint8_t speed_sensor_signals; - - // pas options - uint8_t pas_start_delay_pulses; - uint8_t pas_stop_delay_x100s; - uint8_t pas_keep_current_percent; - uint8_t pas_keep_current_cadence_rpm; - - // throttle options - uint8_t throttle_start_voltage_mv_u16l; - uint8_t throttle_start_voltage_mv_u16h; - uint8_t throttle_end_voltage_mv_u16l; - uint8_t throttle_end_voltage_mv_u16h; - uint8_t throttle_start_percent; - uint8_t throttle_global_spd_lim_opt; - uint8_t throttle_global_spd_lim_percent; - - // shift interrupt options - uint8_t shift_interrupt_duration_ms_u16l; - uint8_t shift_interrupt_duration_ms_u16h; - uint8_t shift_interrupt_current_threshold_percent; - - // misc - uint8_t walk_mode_data_display; - - // assist levels - uint8_t assist_mode_select; - uint8_t assist_startup_level; - assist_level_t assist_levels[2][10]; -} config_t; - -typedef struct -{ - uint8_t adc_voltage_calibration_steps_x100_i16l; - uint8_t adc_voltage_calibration_steps_x100_i16h; -} pstate_t; - - -extern config_t g_config; -extern pstate_t g_pstate; - -void cfgstore_init(); - -bool cfgstore_reset_config(); -bool cfgstore_save_config(); - -bool cfgstore_reset_pstate(); -bool cfgstore_save_pstate(); - -#endif diff --git a/src/firmware/clean.bat b/src/firmware/clean.bat deleted file mode 100644 index cfb7def4..00000000 --- a/src/firmware/clean.bat +++ /dev/null @@ -1,18 +0,0 @@ -@ECHO OFF - -del /s /q *.hex >NUL 2>NUL -del /s /q *.ihx >NUL 2>NUL - -del /s /q *.asm >NUL 2>NUL -del /s /q *.rel >NUL 2>NUL -del /s /q *.lk >NUL 2>NUL -del /s /q *.lst >NUL 2>NUL -del /s /q *.rst >NUL 2>NUL -del /s /q *.sym >NUL 2>NUL -del /s /q *.cdb >NUL 2>NUL -del /s /q *.map >NUL 2>NUL -del /s /q *.elf >NUL 2>NUL -del /s /q *.adb >NUL 2>NUL -del /s /q *.mem >NUL 2>NUL - -@ECHO ON \ No newline at end of file diff --git a/src/firmware/eventlog.c b/src/firmware/eventlog.c deleted file mode 100644 index 8061755f..00000000 --- a/src/firmware/eventlog.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "eventlog.h" -#include "uart.h" - -static bool is_enabled; - -void eventlog_init(bool enabled) -{ - is_enabled = enabled; -} - -bool eventlog_is_enabled() -{ - return is_enabled; -} - -void eventlog_set_enabled(bool enabled) -{ - is_enabled = enabled; -} - -void eventlog_write(uint8_t evt) -{ - if (!is_enabled) - { - return; - } - - uart_write(0xee); - uart_write(evt); - uart_write((uint8_t)0xee + evt); -} -void eventlog_write_data(uint8_t evt, int16_t data) -{ - if (!is_enabled) - { - return; - } - - uint8_t checksum = 0; - - uart_write(0xed); checksum += (uint8_t)0xed; - uart_write(evt); checksum += evt; - uart_write((uint8_t)(data >> 8)); checksum += (uint8_t)(data >> 8); - uart_write((uint8_t)data); checksum += (uint8_t)data; - uart_write(checksum); -} diff --git a/src/firmware/eventlog.h b/src/firmware/eventlog.h deleted file mode 100644 index bf6aa9a0..00000000 --- a/src/firmware/eventlog.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#ifndef _EVENTLOG_H_ -#define _EVENTLOG_H_ - -#include "intellisense.h" - -#include -#include - -#define EVT_MSG_MOTOR_INIT_OK 1 -#define EVT_MSG_CONFIG_READ_DONE 2 -#define EVT_MSG_CONFIG_RESET 3 -#define EVT_MSG_CONFIG_WRITE_DONE 4 -#define EVT_MSG_CONFIG_READ_BEGIN 5 -#define EVT_MSG_CONFIG_WRITE_BEGIN 6 -#define EVT_MSG_PSTATE_READ_BEGIN 7 -#define EVT_MSG_PSTATE_READ_DONE 8 -#define EVT_MSG_PSTATE_WRITE_BEGIN 9 -#define EVT_MSG_PSTATE_WRITE_DONE 10 - - -#define EVT_ERROR_INIT_MOTOR 64 -#define EVT_ERROR_CHANGE_TARGET_SPEED 65 -#define EVT_ERROR_CHANGE_TARGET_CURRENT 66 -#define EVT_ERROR_READ_MOTOR_STATUS 67 -#define EVT_ERROR_READ_MOTOR_CURRENT 68 -#define EVT_ERROR_READ_MOTOR_VOLTAGE 69 - -#define EVT_ERROR_EEPROM_READ 70 -#define EVT_ERROR_EEPROM_WRITE 71 -#define EVT_ERROR_EEPROM_ERASE 72 -#define EVT_ERROR_EEPROM_VERIFY_VERSION 73 -#define EVT_ERROR_EEPROM_VERIFY_CHECKSUM 74 -#define EVT_ERROR_THROTTLE_LOW_LIMIT 75 -#define EVT_ERROR_THROTTLE_HIGH_LIMIT 76 -#define EVT_ERROR_WATCHDOG_TRIGGERED 77 -#define EVT_ERROR_EXTCOM_CHEKSUM 78 -#define EVT_ERROR_EXTCOM_DISCARD 79 - - -#define EVT_DATA_TARGET_CURRENT 128 -#define EVT_DATA_TARGET_SPEED 129 -#define EVT_DATA_MOTOR_STATUS 130 -#define EVT_DATA_ASSIST_LEVEL 131 -#define EVT_DATA_OPERATION_MODE 132 -#define EVT_DATA_WHEEL_SPEED_PPM 133 -#define EVT_DATA_LIGHTS 134 -#define EVT_DATA_TEMPERATURE 135 -#define EVT_DATA_THERMAL_LIMITING 136 -#define EVT_DATA_SPEED_LIMITING 137 -#define EVT_DATA_MAX_CURRENT_ADC_REQUEST 138 -#define EVT_DATA_MAX_CURRENT_ADC_RESPONSE 139 -#define EVT_DATA_MAIN_LOOP_TIME 140 -#define EVT_DATA_THROTTLE_ADC 141 -#define EVT_DATA_LVC_LIMITING 142 -#define EVT_DATA_SHIFT_SENSOR 143 -#define EVT_DATA_BBSHD_THERMISTOR 144 -#define EVT_DATA_VOLTAGE 145 -#define EVT_DATA_CALIBRATE_VOLTAGE 146 -#define EVT_DATA_TORQUE_ADC 147 -#define EVT_DATA_TORQUE_ADC_CALIBRATED 148 - - -void eventlog_init(bool enabled); - -bool eventlog_is_enabled(); -void eventlog_set_enabled(bool enabled); - -void eventlog_write(uint8_t evt); -void eventlog_write_data(uint8_t evt, int16_t data); - - -#endif diff --git a/src/firmware/extcom.c b/src/firmware/extcom.c deleted file mode 100644 index bf0d5c79..00000000 --- a/src/firmware/extcom.c +++ /dev/null @@ -1,893 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "extcom.h" -#include "cfgstore.h" -#include "eventlog.h" -#include "uart.h" -#include "system.h" -#include "sensors.h" -#include "motor.h" -#include "battery.h" -#include "app.h" -#include "util.h" -#include "version.h" -#include "intellisense.h" -#include "fwconfig.h" - -#include -#include -#include - -#define KEEP 0 -#define DISCARD -1 - - -#define BUFFER_SIZE 192 -#define DISCARD_TIMEOUT_MS 50 - -#define REQUEST_TYPE_READ 0x01 -#define REQUEST_TYPE_WRITE 0x02 - -#define REQUEST_TYPE_BAFANG_READ 0x11 -#define REQUEST_TYPE_BAFANG_WRITE 0x16 - - -// Firmware config tool communication -#define OPCODE_READ_FW_VERSION 0x01 -#define OPCODE_READ_EVTLOG_ENABLE 0x02 -#define OPCODE_READ_CONFIG 0x03 -#define OPCODE_READ_STATUS 0x04 - -#define OPCODE_WRITE_EVTLOG_ENABLE 0xf0 -#define OPCODE_WRITE_CONFIG 0xf1 -#define OPCODE_WRITE_RESET_CONFIG 0xf2 -#define OPCODE_WRITE_ADC_VOLTAGE_CALIBRATION 0xf3 - - -// Bafang display communication -#define OPCODE_BAFANG_DISPLAY_READ_STATUS 0x08 -#define OPCODE_BAFANG_DISPLAY_READ_CURRENT 0x0a -#define OPCODE_BAFANG_DISPLAY_READ_BATTERY 0x11 -#define OPCODE_BAFANG_DISPLAY_READ_SPEED 0x20 -#define OPCODE_BAFANG_DISPLAY_READ_UNKNOWN1 0x21 -#define OPCODE_BAFANG_DISPLAY_READ_RANGE 0x22 -#define OPCODE_BAFANG_DISPLAY_READ_CALORIES 0x24 -#define OPCODE_BAFANG_DISPLAY_READ_UNKNOWN3 0x25 -#define OPCODE_BAFANG_DISPLAY_READ_MOVING 0x31 - -#define OPCODE_BAFANG_DISPLAY_WRITE_PAS 0x0b -#define OPCODE_BAFANG_DISPLAY_WRITE_MODE 0x0c -#define OPCODE_BAFANG_DISPLAY_WRITE_LIGHTS 0x1a -#define OPCODE_BAFANG_DISPLAY_WRITE_SPEED_LIM 0x1f - -// Bafang config tool communication (not supported, just discard messages) -#define OPCODE_BAFANG_TOOL_READ_CONNECT 0x51 -#define OPCODE_BAFANG_TOOL_READ_BASIC 0x52 -#define OPCODE_BAFANG_TOOL_READ_PAS 0x53 -#define OPCODE_BAFANG_TOOL_READ_THROTTLE 0x54 - -#define OPCODE_BAFANG_TOOL_WRITE_BASIC 0x52 -#define OPCODE_BAFANG_TOOL_WRITE_PAS 0x53 -#define OPCODE_BAFANG_TOOL_WRITE_THROTTLE 0x54 - - - -static uint8_t msg_len; -static uint8_t msgbuf[BUFFER_SIZE]; -static uint32_t last_recv_ms; -static uint32_t discard_until_ms; - -static uint8_t compute_checksum(uint8_t* buf, uint8_t length); -static void write_uart_and_increment_checksum(uint8_t data, uint8_t* checksum); - -static int16_t try_process_request(); -static int16_t try_process_read_request(); -static int16_t try_process_write_request(); -static int16_t try_process_bafang_read_request(); -static int16_t try_process_bafang_write_request(); - - -static int16_t process_read_fw_version(); -static int16_t process_read_evtlog_enable(); -static int16_t process_read_config(); -static int16_t process_read_status(); - -static int16_t process_write_evtlog_enable(); -static int16_t process_write_config(); -static int16_t process_write_reset_config(); -static int16_t process_write_adc_voltage_calibration(); - - -static int16_t process_bafang_display_read_status(); -static int16_t process_bafang_display_read_current(); -static int16_t process_bafang_display_read_battery(); -static int16_t process_bafang_display_read_speed(); -static int16_t process_bafang_display_read_unknown1(); -static int16_t process_bafang_display_read_range(); -static int16_t process_bafang_display_read_calories(); -static int16_t process_bafang_display_read_unknown3(); -static int16_t process_bafang_display_read_moving(); - -static int16_t process_bafang_display_write_pas(); -static int16_t process_bafang_display_write_mode(); -static int16_t process_bafang_display_write_lights(); -static int16_t process_bafang_display_write_speed_limit(); - -void extcom_init() -{ - msg_len = 0; - last_recv_ms = 0; - discard_until_ms = 0; - - // Bafang standard baud rate - uart_open(1200); - - - // Wait one second for config tool connection. - // This is here to that the config tool can enable - // the event log before system proceeds with initialization. - uint32_t end = system_ms() + 1000; - while (system_ms() < end) - { - extcom_process(); - system_delay_ms(10); - } -} - -void extcom_process() -{ - uint32_t now = system_ms(); - - while (uart_available()) - { - if (msg_len == BUFFER_SIZE || (discard_until_ms != 0 && now < discard_until_ms)) - { - // communication error, reset - msg_len = 0; - while (uart_available()) uart_read(); - } - else - { - msgbuf[msg_len++] = uart_read(); - last_recv_ms = now; - discard_until_ms = 0; - } - } - - if (msg_len > 0 && now - last_recv_ms > 100) - { - // communication error, reset - msg_len = 0; - } - - int16_t res = try_process_request(); - if (res == DISCARD) - { - msg_len = 0; - last_recv_ms = 0; - // Discard received data for the next DISCARD_TIMEOUT_MS milliseconds - discard_until_ms = now + DISCARD_TIMEOUT_MS; - - eventlog_write(EVT_ERROR_EXTCOM_DISCARD); - } - else if (res > 0) - { - if (res < msg_len) - { - // will not occur due to request/response communication - memcpy(msgbuf, msgbuf + res, msg_len - res); - msg_len -= res; - } - else - { - msg_len = 0; - last_recv_ms = 0; - } - } -} - - -static uint8_t compute_checksum(uint8_t* buf, uint8_t length) -{ - uint8_t result = 0; - - for (uint8_t i = 0; i < length; ++i) - { - result += buf[i]; - } - - return result; -} - -static void write_uart_and_increment_checksum(uint8_t data, uint8_t* checksum) -{ - *checksum += data; - uart_write(data); -} - -static int16_t try_process_request() -{ - if (msg_len < 1) - { - return KEEP; - } - - switch (msgbuf[0]) - { - case REQUEST_TYPE_READ: - return try_process_read_request(); - case REQUEST_TYPE_WRITE: - return try_process_write_request(); - case REQUEST_TYPE_BAFANG_READ: - return try_process_bafang_read_request(); - case REQUEST_TYPE_BAFANG_WRITE: - return try_process_bafang_write_request(); - } - - return DISCARD; // unknown message -} - -static int16_t try_process_read_request() -{ - if (msg_len < 2) - { - return KEEP; - } - - switch (msgbuf[1]) - { - case OPCODE_READ_FW_VERSION: - return process_read_fw_version(); - case OPCODE_READ_EVTLOG_ENABLE: - return process_read_evtlog_enable(); - case OPCODE_READ_CONFIG: - return process_read_config(); - case OPCODE_READ_STATUS: - return process_read_status(); - } - - return DISCARD; -} - -static int16_t try_process_write_request() -{ - if (msg_len < 2) - { - return KEEP; - } - - switch (msgbuf[1]) - { - case OPCODE_WRITE_EVTLOG_ENABLE: - return process_write_evtlog_enable(); - case OPCODE_WRITE_CONFIG: - return process_write_config(); - case OPCODE_WRITE_RESET_CONFIG: - return process_write_reset_config(); - case OPCODE_WRITE_ADC_VOLTAGE_CALIBRATION: - return process_write_adc_voltage_calibration(); - } - - return DISCARD; -} - -static int16_t try_process_bafang_read_request() -{ - if (msg_len < 2) - { - return KEEP; - } - - switch (msgbuf[1]) - { - case OPCODE_BAFANG_DISPLAY_READ_STATUS: - return process_bafang_display_read_status(); - case OPCODE_BAFANG_DISPLAY_READ_CURRENT: - return process_bafang_display_read_current(); - case OPCODE_BAFANG_DISPLAY_READ_BATTERY: - return process_bafang_display_read_battery(); - case OPCODE_BAFANG_DISPLAY_READ_SPEED: - return process_bafang_display_read_speed(); - case OPCODE_BAFANG_DISPLAY_READ_UNKNOWN1: - return process_bafang_display_read_unknown1(); - case OPCODE_BAFANG_DISPLAY_READ_RANGE: - return process_bafang_display_read_range(); - case OPCODE_BAFANG_DISPLAY_READ_CALORIES: - return process_bafang_display_read_calories(); - case OPCODE_BAFANG_DISPLAY_READ_UNKNOWN3: - return process_bafang_display_read_unknown3(); - case OPCODE_BAFANG_DISPLAY_READ_MOVING: - return process_bafang_display_read_moving(); - } - - return DISCARD; -} - -static int16_t try_process_bafang_write_request() -{ - if (msg_len < 2) - { - return KEEP; - } - - switch (msgbuf[1]) - { - case OPCODE_BAFANG_DISPLAY_WRITE_PAS: - return process_bafang_display_write_pas(); - case OPCODE_BAFANG_DISPLAY_WRITE_MODE: - return process_bafang_display_write_mode(); - case OPCODE_BAFANG_DISPLAY_WRITE_LIGHTS: - return process_bafang_display_write_lights(); - case OPCODE_BAFANG_DISPLAY_WRITE_SPEED_LIM: - return process_bafang_display_write_speed_limit(); - } - - return DISCARD; -} - - - -static int16_t process_read_fw_version() -{ - if (msg_len < 3) - { - return KEEP; - } - - if (compute_checksum(msgbuf, 2) == msgbuf[2]) - { - uint8_t checksum = 0; - write_uart_and_increment_checksum(REQUEST_TYPE_READ, &checksum); - write_uart_and_increment_checksum(OPCODE_READ_FW_VERSION, &checksum); - write_uart_and_increment_checksum(VERSION_MAJOR, &checksum); - write_uart_and_increment_checksum(VERSION_MINOR, &checksum); - write_uart_and_increment_checksum(VERSION_PATCH, &checksum); - write_uart_and_increment_checksum(CONFIG_VERSION, &checksum); - write_uart_and_increment_checksum(CTRL_TYPE, &checksum); - uart_write(checksum); - } - else - { - eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); - return DISCARD; - } - - return 3; -} - -static int16_t process_read_evtlog_enable() -{ - if (msg_len < 3) - { - return KEEP; - } - - if (compute_checksum(msgbuf, 2) == msgbuf[2]) - { - uint8_t checksum = 0; - write_uart_and_increment_checksum(REQUEST_TYPE_READ, &checksum); - write_uart_and_increment_checksum(OPCODE_READ_EVTLOG_ENABLE, &checksum); - write_uart_and_increment_checksum((uint8_t)eventlog_is_enabled(), &checksum); - uart_write(checksum); - } - else - { - eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); - return DISCARD; - } - - return 3; -} - -static int16_t process_read_config() -{ - if (msg_len < 3) - { - return KEEP; - } - - if (compute_checksum(msgbuf, 2) == msgbuf[2]) - { - uint8_t checksum = 0; - write_uart_and_increment_checksum(REQUEST_TYPE_READ, &checksum); - write_uart_and_increment_checksum(OPCODE_READ_CONFIG, &checksum); - write_uart_and_increment_checksum(CONFIG_VERSION, &checksum); - write_uart_and_increment_checksum(sizeof(config_t), &checksum); - - uint8_t* cfg = (uint8_t*)&g_config; - for (uint8_t i = 0; i < sizeof(config_t); ++i) - { - write_uart_and_increment_checksum(*(cfg + i), &checksum); - } - - uart_write(checksum); - } - else - { - eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); - return DISCARD; - } - - return 3; -} - -static int16_t process_read_status() -{ - // :TODO: - return 0; -} - -static int16_t process_write_evtlog_enable() -{ - if (msg_len < 4) - { - return KEEP; - } - - if (compute_checksum(msgbuf, 3) == msgbuf[3]) - { - eventlog_set_enabled((bool)msgbuf[2]); - - uint8_t checksum = 0; - write_uart_and_increment_checksum(REQUEST_TYPE_WRITE, &checksum); - write_uart_and_increment_checksum(OPCODE_WRITE_EVTLOG_ENABLE, &checksum); - write_uart_and_increment_checksum(msgbuf[2], &checksum); - uart_write(checksum); - } - else - { - eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); - return DISCARD; - } - - return 4; -} - -static int16_t process_write_config() -{ - if (msg_len < 4) - { - return KEEP; - } - - uint8_t version = msgbuf[2]; - uint8_t length = msgbuf[3]; - - if (msg_len < 4 + length + 1) - { - return KEEP; - } - - if (compute_checksum(msgbuf, (uint8_t)(4 + sizeof(config_t))) == msgbuf[4 + sizeof(config_t)]) - { - bool result = false; - if (version == CONFIG_VERSION && length == sizeof(config_t)) - { - memcpy(&g_config, msgbuf + 4, sizeof(config_t)); - result = cfgstore_save_config(); - } - - uint8_t checksum = 0; - write_uart_and_increment_checksum(REQUEST_TYPE_WRITE, &checksum); - write_uart_and_increment_checksum(OPCODE_WRITE_CONFIG, &checksum); - write_uart_and_increment_checksum(result, &checksum); - uart_write(checksum); - } - else - { - eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); - return DISCARD; - } - - return 4 + length + 1; -} - -static int16_t process_write_reset_config() -{ - if (msg_len < 3) - { - return KEEP; - } - - if (compute_checksum(msgbuf, 2) == msgbuf[2]) - { - - bool res = cfgstore_reset_config(); - - uint8_t checksum = 0; - write_uart_and_increment_checksum(REQUEST_TYPE_WRITE, &checksum); - write_uart_and_increment_checksum(OPCODE_WRITE_RESET_CONFIG, &checksum); - write_uart_and_increment_checksum((uint8_t)res, &checksum); - uart_write(checksum); - } - else - { - eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); - return DISCARD; - } - - return 3; -} - -static int16_t process_write_adc_voltage_calibration() -{ - if (msg_len < 5) - { - return KEEP; - } - - if (compute_checksum(msgbuf, 4) == msgbuf[4]) - { - uint16_t actual_volt_x100 = ((uint16_t)msgbuf[2] << 8) | msgbuf[3]; - - int16_t calibration_offset = motor_calibrate_battery_voltage(actual_volt_x100); - g_pstate.adc_voltage_calibration_steps_x100_i16l = (uint8_t)(calibration_offset); - g_pstate.adc_voltage_calibration_steps_x100_i16h = (uint8_t)(calibration_offset >> 8); - - cfgstore_save_pstate(); - - uint8_t checksum = 0; - write_uart_and_increment_checksum(REQUEST_TYPE_WRITE, &checksum); - write_uart_and_increment_checksum(OPCODE_WRITE_ADC_VOLTAGE_CALIBRATION, &checksum); - write_uart_and_increment_checksum(msgbuf[2], &checksum); - write_uart_and_increment_checksum(msgbuf[3], &checksum); - uart_write(checksum); - } - else - { - eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); - return DISCARD; - } - - return 5; -} - - -static int16_t process_bafang_display_read_status() -{ - if (msg_len < 2) - { - return KEEP; - } - - uart_write(app_get_status_code()); - - return 2; -} - -static int16_t process_bafang_display_read_current() -{ - if (msg_len < 2) - { - return KEEP; - } - - uint8_t amp_x2 = (uint8_t)((motor_get_battery_current_x10() * 2) / 10); - - uart_write(amp_x2); - uart_write(amp_x2); // checksum - - return 2; -} - -static int16_t process_bafang_display_read_battery() -{ - if (msg_len < 2) - { - return KEEP; - } - - uint8_t value = battery_get_mapped_percent(); - - uart_write(value); - uart_write(value); // checksum - - return 2; -} - -static int16_t process_bafang_display_read_speed() -{ - if (msg_len < 2) - { - return KEEP; - } - - uint16_t speed = 0; - - if (g_config.walk_mode_data_display != WALK_MODE_DATA_SPEED && app_get_assist_level() == ASSIST_PUSH) - { - uint16_t data = 0; - - switch (g_config.walk_mode_data_display) - { - case WALK_MODE_DATA_TEMPERATURE: - // Keep temperature in C, farenheit would be out of range - data = app_get_temperature(); - break; - case WALK_MODE_DATA_REQUESTED_POWER: - data = motor_get_target_current(); - break; - case WALK_MODE_DATA_BATTERY_PERCENT: - data = battery_get_percent(); - break; - } - - if (g_config.use_freedom_units) - { - // Compensate for kph -> mph conversion display will do. - data = (data * 161) / 100; - } - - // T_kph -> rpm - speed = (uint16_t)(25000.f / (3 * 3.14159f * 1.27f * EXPAND_U16(g_config.wheel_size_inch_x10_u16h, g_config.wheel_size_inch_x10_u16l)) * data); - } - else - { - speed = speed_sensor_get_rpm_x10() / 10; - } - - - uint8_t checksum = 0; - - write_uart_and_increment_checksum(speed >> 8, &checksum); - write_uart_and_increment_checksum((uint8_t)speed, &checksum); - uart_write(checksum + (uint8_t)0x20); // weird checksum - - return 2; -} - -static int16_t process_bafang_display_read_unknown1() -{ - if (msg_len < 3) - { - return KEEP; - } - - uart_write(0x00); - uart_write(0x00); - uart_write(0x00); // checksum - - return 3; -} - -static int16_t process_bafang_display_read_range() -{ - if (msg_len < 3) - { - return KEEP; - } - - uint16_t value = 0; - -#if DISPLAY_RANGE_FIELD_DATA == DISPLAY_RANGE_FIELD_TEMPERATURE - value = app_get_temperature(); - if (g_config.use_freedom_units) - { - // Convert to farenheit and compensate for the km -> miles conversion the diplay will do - // F_miles = (C * 9/5 + 32) * 161 / 100 - // Approximistation: - // F_miles = 2.9C + 50.5 - - value = ((290u * value) + 5050u) / 100u; - } -#elif DISPLAY_RANGE_FIELD_DATA == DISPLAY_RANGE_FIELD_POWER - if (app_get_lights()) - { - value = motor_get_battery_current_x10(); - } - else - { - uint16_t max_current_amp_x10 = g_config.max_current_amps * 10; - value = MAP32(motor_get_target_current(), 0, 100, 0, max_current_amp_x10); - } - - if (g_config.use_freedom_units) - { - // compensate for km -> miles conversion the display will do - value = (value * 161u) / 100u; - } -#endif - - uint8_t checksum = 0; - - write_uart_and_increment_checksum((uint8_t)(value >> 8), &checksum); - write_uart_and_increment_checksum((uint8_t)value, &checksum); - uart_write(checksum); // checksum - - return 3; -} - -static int16_t process_bafang_display_read_calories() -{ - if (msg_len < 3) - { - return KEEP; - } - - uint8_t checksum = 0; - - // send battery voltage x10 to show in calories field - uint16_t volt = motor_get_battery_voltage_x10(); - - write_uart_and_increment_checksum(volt >> 8, & checksum); - write_uart_and_increment_checksum(volt & 0xff, & checksum); - uart_write(checksum); // checksum - - return 3; -} - -static int16_t process_bafang_display_read_unknown3() -{ - if (msg_len < 3) - { - return KEEP; - } - - uart_write(0x00); - uart_write(0x00); - uart_write(0x00); - uart_write(0x00); - uart_write(0x00); // checksum - - return 3; -} - -static int16_t process_bafang_display_read_moving() -{ - if (msg_len < 2) - { - return KEEP; - } - - uint8_t data = speed_sensor_is_moving() ? 0x31 : 0x30; - uart_write(data); - uart_write(data); // checksum - - return 2; -} - - -static int16_t process_bafang_display_write_pas() -{ - if (msg_len < 4) - { - return KEEP; - } - - if (compute_checksum(msgbuf, 3) == msgbuf[3]) - { - switch (msgbuf[2]) - { - case 0x00: - app_set_assist_level(ASSIST_0); - break; - case 0x01: - app_set_assist_level(ASSIST_1); - break; - case 0x0b: - app_set_assist_level(ASSIST_2); - break; - case 0x0c: - app_set_assist_level(ASSIST_3); - break; - case 0x0d: - app_set_assist_level(ASSIST_4); - break; - case 0x02: - app_set_assist_level(ASSIST_5); - break; - case 0x15: - app_set_assist_level(ASSIST_6); - break; - case 0x16: - app_set_assist_level(ASSIST_7); - break; - case 0x17: - app_set_assist_level(ASSIST_8); - break; - case 0x03: - app_set_assist_level(ASSIST_9); - break; - case 0x06: - app_set_assist_level(ASSIST_PUSH); - break; - default: - // Unsupported level, ignore - break; - } - } - else - { - eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); - return DISCARD; - } - - return 4; -} - -static int16_t process_bafang_display_write_mode() -{ - if (msg_len < 4) - { - return KEEP; - } - - if (compute_checksum(msgbuf, 3) == msgbuf[3]) - { - switch (msgbuf[2]) - { - case 0x02: - app_set_operation_mode(OPERATION_MODE_DEFAULT); - break; - case 0x04: - app_set_operation_mode(OPERATION_MODE_SPORT); - break; - default: - // Unsupported mode, ignore - break; - } - } - else - { - eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); - return DISCARD; - } - - return 4; -} - -static int16_t process_bafang_display_write_lights() -{ - if (msg_len < 3) - { - return KEEP; - } - - // No checksum - - switch (msgbuf[2]) - { - case 0xf0: - app_set_lights(false); - break; - case 0xf1: - app_set_lights(true); - break; - default: - return DISCARD; // unsupported state, assume communication error - } - - return 3; -} - -static int16_t process_bafang_display_write_speed_limit() -{ - if (msg_len < 5) - { - return KEEP; - } - - /* - if (compute_checksum(msgbuf, 4) == msgbuf[4]) - { - // Ignoring speed limit requested by display, - // Global speed limit is configured in firmware config tool. - - uint16_t value = ((msgbuf[2] << 8) | msgbuf[3]); - app_set_wheel_max_speed_rpm(value); - } - else - { - eventlog_write(EVT_ERROR_EXTCOM_CHEKSUM); - return DISCARD; - } - */ - - return 5; -} diff --git a/src/firmware/intellisense.h b/src/firmware/intellisense.h deleted file mode 100644 index f1fe6197..00000000 --- a/src/firmware/intellisense.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef _INTELLISENSE_H_ -#define _INTELLISENSE_H_ - -// NOTE: -// The defines below are here to keep IntelliSense -// in Visual Studio happy and not throw incorrect errors. - -#if !defined (SDCC) && !defined (__SDCC) - -#define INTERRUPT(name, vector) void name() -#define INTERRUPT_USING(name, vector,regnum) void name() - -#define __interrupt(vector) - -#define enableInterrupts() -#define disableInterrupts() - -#define NOP() - -#define _Bool uint8_t - -#endif - -#endif diff --git a/src/firmware/main.c b/src/firmware/main.c deleted file mode 100644 index ef66f879..00000000 --- a/src/firmware/main.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "interrupt.h" // IMPORTANT: interrupt vector declarations must be included from main.c! -#include "timers.h" -#include "system.h" -#include "eeprom.h" -#include "cfgstore.h" -#include "eventlog.h" -#include "app.h" -#include "battery.h" -#include "watchdog.h" -#include "adc.h" -#include "motor.h" -#include "extcom.h" -#include "sensors.h" -#include "throttle.h" -#include "lights.h" -#include "uart.h" -#include "util.h" - -#define APP_PROCESS_INTERVAL_MS 5 - -void main(void) -{ - motor_pre_init(); - - watchdog_init(); - timers_init(); - system_init(); - - eventlog_init(false); - extcom_init(); - - if (watchdog_triggered()) - { - // force write watchdog reset to eventlog - bool prev = eventlog_is_enabled(); - eventlog_set_enabled(true); - eventlog_write(EVT_ERROR_WATCHDOG_TRIGGERED); - eventlog_set_enabled(prev); - } - - eeprom_init(); - cfgstore_init(); - - adc_init(); - sensors_init(); - - speed_sensor_set_signals_per_rpm(g_config.speed_sensor_signals); - pas_set_stop_delay((uint16_t)g_config.pas_stop_delay_x100s * 10); - - battery_init(); - throttle_init( - EXPAND_U16(g_config.throttle_start_voltage_mv_u16h, g_config.throttle_start_voltage_mv_u16l), - EXPAND_U16(g_config.throttle_end_voltage_mv_u16h, g_config.throttle_end_voltage_mv_u16l) - ); - - motor_init(g_config.max_current_amps * 1000, g_config.low_cut_off_v, - EXPAND_I16(g_pstate.adc_voltage_calibration_steps_x100_i16h, g_pstate.adc_voltage_calibration_steps_x100_i16l)); - - lights_init(); - - app_init(); - - uint32_t next_app_proccess = system_ms(); - while (1) - { - uint32_t now = system_ms(); - - adc_process(); - motor_process(); - - if (now >= next_app_proccess) - { - next_app_proccess = now + APP_PROCESS_INTERVAL_MS; - - battery_process(); - sensors_process(); - extcom_process(); - app_process(); - } - - watchdog_yeild(); - } -} diff --git a/src/firmware/throttle.c b/src/firmware/throttle.c deleted file mode 100644 index f1872fa4..00000000 --- a/src/firmware/throttle.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "throttle.h" -#include "intellisense.h" -#include "system.h" -#include "eventlog.h" -#include "util.h" -#include "adc.h" -#include "fwconfig.h" - -#include - -static uint8_t min_voltage_adc; -static uint8_t max_voltage_adc; - -static bool throttle_detected; -static bool throttle_low_ok; -static bool throttle_hard_ok; -static uint32_t throttle_hard_limit_hit_at; - - -//#define LOG_THROTTLE_ADC - -#define ADC_VOLTAGE_MV 5000ul - -#define THROTTLE_HARD_LOW_LIMIT_MV 500ul -#define THROTTLE_HARD_HIGH_LIMIT_MV 4500ul - -#define THROTTLE_HARD_LOW_LIMIT_ADC ((THROTTLE_HARD_LOW_LIMIT_MV * 256) / ADC_VOLTAGE_MV) -#define THROTTLE_HARD_HIGH_LIMIT_ADC ((THROTTLE_HARD_HIGH_LIMIT_MV * 256) / ADC_VOLTAGE_MV) -#define THROTTLE_HARD_LIMIT_TOLERANCE_MS 100 - -#if (THROTTLE_RESPONSE_CURVE == THROTTLE_RESPONSE_CUSTOM) -static const uint8_t throttle_custom_map_lut[101] = -{ - THROTTLE_CUSTOM_MAP -}; -#endif - - - -void throttle_init(uint16_t min_mv, uint16_t max_mv) -{ - min_voltage_adc = (uint8_t)(((uint32_t)min_mv * 256) / ADC_VOLTAGE_MV); - max_voltage_adc = (uint8_t)(((uint32_t)max_mv * 256) / ADC_VOLTAGE_MV); - throttle_detected = false; - throttle_low_ok = false; - throttle_hard_ok = true; - throttle_hard_limit_hit_at = 0; -} - -bool throttle_ok() -{ - return !throttle_detected || (throttle_low_ok && throttle_hard_ok); -} - -uint8_t throttle_read() -{ - static uint8_t throttle_percent = 0; - - int16_t value = adc_get_throttle(); - -#ifdef LOG_THROTTLE_ADC - static uint8_t last_logged_throttle_adc = 0; - if (ABS(value - last_logged_throttle_adc) > 1) - { - last_logged_throttle_adc = value; - eventlog_write_data(EVT_DATA_THROTTLE_ADC, value); - } -#endif - - if (value < THROTTLE_HARD_LOW_LIMIT_ADC || value > THROTTLE_HARD_HIGH_LIMIT_ADC) - { - // allow invalid throttle input value for a number of milliseconds before reporting throttle error. - if (throttle_hard_limit_hit_at != 0) - { - if (throttle_hard_ok && (system_ms() - throttle_hard_limit_hit_at) > THROTTLE_HARD_LIMIT_TOLERANCE_MS) - { - if (throttle_detected && value < THROTTLE_HARD_LOW_LIMIT_ADC) - { - eventlog_write(EVT_ERROR_THROTTLE_LOW_LIMIT); - } - else if (value > THROTTLE_HARD_HIGH_LIMIT_ADC) - { - eventlog_write(EVT_ERROR_THROTTLE_HIGH_LIMIT); - } - - throttle_hard_ok = false; - } - } - else - { - throttle_hard_limit_hit_at = system_ms(); - } - } - else - { - if (value >= THROTTLE_HARD_LOW_LIMIT_ADC) - { - throttle_detected = true; - } - - throttle_hard_limit_hit_at = 0; - throttle_hard_ok = true; - } - - if (value < min_voltage_adc) - { - // throttle is considered not working until it has given a signal below minimum - // configured value but more than 0. - throttle_low_ok = true; - - // hysteresis - if (throttle_percent > 0) - { - value += 1; - } - - if (value < min_voltage_adc) - { - throttle_percent = 0; - return throttle_percent; - } - } - - if (value > max_voltage_adc) - { - value = max_voltage_adc; - } - - throttle_percent = (uint8_t)MAP16(value, min_voltage_adc, max_voltage_adc, 1, 100); - - return throttle_percent; -} - - -uint8_t throttle_map_response(uint8_t throttle_percent) -{ -#if (THROTTLE_RESPONSE_CURVE == THROTTLE_RESPONSE_QUADRATIC) - return (uint8_t)(((uint16_t)throttle_percent * throttle_percent) / 100); -#elif (THROTTLE_RESPONSE_CURVE == THROTTLE_RESPONSE_CUSTOM) - return throttle_custom_map_lut[throttle_percent]; -#else - return throttle_percent; -#endif -} diff --git a/src/firmware/tohex.bat b/src/firmware/tohex.bat deleted file mode 100644 index e464f69e..00000000 --- a/src/firmware/tohex.bat +++ /dev/null @@ -1,2 +0,0 @@ -@echo off -packihx bbs-fw.ihx > bbs-fw.hex \ No newline at end of file diff --git a/src/firmware/tsdz2/adc.c b/src/firmware/tsdz2/adc.c deleted file mode 100644 index 0608f78b..00000000 --- a/src/firmware/tsdz2/adc.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ -#include -#include "adc.h" -#include "tsdz2/interrupt.h" -#include "tsdz2/stm8.h" -#include "tsdz2/pins.h" -#include "tsdz2/stm8s/stm8s_adc1.h" -#include "tsdz2/stm8s/stm8s.h" - - -static volatile uint8_t adc_throttle; -static volatile uint16_t adc_battery_voltage; -static volatile uint16_t adc_torque; - -// cached variables read from voltatile uint16_t vars while ADC1 interrupt disabled -static uint16_t adc_battery_voltage_cache; -static uint16_t adc_torque_cache; - - -void adc_init() -{ - SET_PIN_INPUT(PIN_BATTERY_CURRENT); - SET_PIN_INPUT(PIN_BATTERY_VOLTAGE); - SET_PIN_INPUT(PIN_THROTTLE); - SET_PIN_INPUT(PIN_TORQUE_SENSOR); - - // NOTE: - // adc configuration (except ADC1->CR1) is overwritten in motor.c/isr_timer1_cmp - // which triggeres the conversion. - // - // The motor control interrupt routines performs single mode - // adc conversion of battery current, reads the result and - // then starts buffered scan mode conversion of all adc channels - // with end of conversion interrupt enabled which is handled here. - - ADC1->CR1 = ADC1_PRESSEL_FCPU_D2; - ADC1->CR2 = ADC1_ALIGN_LEFT; - - // channel (none) - ADC1->CSR = 0x00; - - // schmittrig disable all - ADC1->TDRL |= (uint8_t)0xFF; - ADC1->TDRH |= (uint8_t)0xFF; - - // Enable the ADC1 peripheral - ADC1->CR1 |= ADC1_CR1_ADON; -} - -void adc_process() -{ - // Have to disable interrupts globally since ADC1->CSR register - // is manipulated from motor control isr. Very short time, should have no effect. - disableInterrupts(); - adc_battery_voltage_cache = adc_battery_voltage; // adc_battery_voltage; - adc_torque_cache = adc_torque; - enableInterrupts(); -} - - -uint8_t adc_get_throttle() -{ - // atomic read - return adc_throttle; -} - -uint16_t adc_get_torque() -{ - // 10 bit resolution - return adc_torque_cache; -} - -uint16_t adc_get_temperature_contr() -{ - return 0; -} - -uint16_t adc_get_temperature_motor() -{ - return 0; -} - -uint16_t adc_get_battery_voltage() -{ - return adc_battery_voltage_cache; -} - -void isr_adc1(void) __interrupt(ITC_IRQ_ADC1) -{ - if (ADC1->CSR & ADC1_CSR_EOC) - { - // all adc channels converted, data available in buffers - - // clear EOC and disable EOC interrupt - ADC1->CSR = 0x00; - - // scan mode reads are setup to be left aligned in motor isr - - // update cached values - adc_throttle = ADC1->DB7RH; // only 8bit resolution used - - // must read in high -> low order according to data sheet - uint8_t high, low; - - // read torque - high = ADC1->DB4RH; - low = ADC1->DB4RL; - adc_torque = (uint16_t)high << 2 | low; - - // read battery voltage - high = ADC1->DB6RH; - low = ADC1->DB6RL; - adc_battery_voltage = (uint16_t)high << 2 | low; - } -} diff --git a/src/firmware/tsdz2/eeprom.c b/src/firmware/tsdz2/eeprom.c deleted file mode 100644 index 44dba584..00000000 --- a/src/firmware/tsdz2/eeprom.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "eeprom.h" -#include "watchdog.h" -#include "tsdz2/cpu.h" -#include "stm8s/stm8s.h" -#include "stm8s/stm8s_flash.h" - -#define EEPROM_START_ADDRESS 0x4000 - -static uint16_t selected_address; - -void eeprom_init() -{ - selected_address = EEPROM_START_ADDRESS; -} - -bool eeprom_select_page(int page) -{ - if (page >= 0 && page < 2) - { - selected_address = EEPROM_START_ADDRESS + (page * 256); - return true; - } - - return false; -} - -int eeprom_read_byte(int offset) -{ - uint8_t* address = (uint8_t*)(selected_address + offset); - return *address; -} - -bool eeprom_erase_page() -{ - return true; // not needed -} - -bool eeprom_write_byte(int offset, uint8_t value) -{ - uint8_t* address = (uint8_t*)(selected_address + offset); - - // disable flash write protection if enabled - if (!(FLASH->IAPSR & FLASH_IAPSR_DUL)) - { - FLASH->DUKR = FLASH_RASS_KEY2; - FLASH->DUKR = FLASH_RASS_KEY1; - - while (!(FLASH->IAPSR & FLASH_IAPSR_DUL)); - } - - watchdog_yeild(); // :TODO: use faster api to write entire page - - *address = value; - while (!(FLASH->IAPSR & FLASH_IAPSR_EOP)); - - return true; -} - -bool eeprom_end_write() -{ - // enable write protection - FLASH->IAPSR &= ~FLASH_IAPSR_DUL; - - return true; -} diff --git a/src/firmware/tsdz2/interrupt.h b/src/firmware/tsdz2/interrupt.h deleted file mode 100644 index 416b2d67..00000000 --- a/src/firmware/tsdz2/interrupt.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#ifndef _TSDZ2_INTERRUPT_H_ -#define _TSDZ2_INTERRUPT_H_ -#include "intellisense.h" -#include "tsdz2/cpu.h" -#include "tsdz2/stm8s/stm8s_itc.h" - -void isr_timer1_cmp(void) __interrupt(ITC_IRQ_TIM1_CAPCOM); // motor.c -void isr_timer3_ovf(void) __interrupt(ITC_IRQ_TIM3_OVF); // system.c -void isr_timer4_ovf(void) __interrupt(ITC_IRQ_TIM4_OVF); // sensors.c - -void isr_adc1(void) __interrupt(ITC_IRQ_ADC1); // adc.c - -void isr_uart2_rx(void) __interrupt(ITC_IRQ_UART2_RX); // uart.c -void isr_uart2_tx(void) __interrupt(ITC_IRQ_UART2_TX); // uart.c - -#endif diff --git a/src/firmware/tsdz2/lights.c b/src/firmware/tsdz2/lights.c deleted file mode 100644 index 34e7cd4c..00000000 --- a/src/firmware/tsdz2/lights.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "lights.h" -#include "stm8.h" -#include "pins.h" - -static bool lights_enabled; -static bool lights_on; - -void lights_init() -{ - SET_PIN_OUTPUT(PIN_LIGHTS); - - lights_enabled = false; - lights_set(false); -} - -void lights_enable() -{ - lights_enabled = true; - lights_set(lights_on); -} - -void lights_disable() -{ - lights_enabled = false; - lights_set(lights_on); -} - -void lights_set(bool on) -{ - lights_on = on; - if (lights_on && lights_enabled) - { - SET_PIN_HIGH(PIN_LIGHTS); - } - else - { - SET_PIN_LOW(PIN_LIGHTS); - } -} diff --git a/src/firmware/tsdz2/motor.c b/src/firmware/tsdz2/motor.c deleted file mode 100644 index f2b1efe4..00000000 --- a/src/firmware/tsdz2/motor.c +++ /dev/null @@ -1,907 +0,0 @@ -/* - * TongSheng TSDZ2 motor controller firmware/ - * - * Copyright (C) Casainho, 2018. - * - * Released under the GPL License, Version 3 - * - * - Original motor control code from TongSheng TSDZ2 motor controller firmware. - * - 9bit motor pwm from fork by Frans-Willem. - * - Cleaned up and integrated into bbs-fw by Daniel Nilsson. - */ -#include -#include "motor.h" -#include "system.h" -#include "uart.h" -#include "eventlog.h" -#include "util.h" -#include "adc.h" -#include "tsdz2/cpu.h" -#include "tsdz2/timers.h" -#include "tsdz2/pins.h" -#include "tsdz2/stm8.h" -#include "tsdz2/stm8s/stm8s.h" -#include "tsdz2/stm8s/stm8s_tim1.h" -#include "tsdz2/stm8s/stm8s_itc.h" -#include "tsdz2/stm8s/stm8s_adc1.h" -#include "tsdz2/stm8s/stm8s_flash.h" - - -// Motor -// --------------------------------------------------------------------------------- - -// hard current limits -#define MAX_BATTERY_CURRENT_AMPS_X10 200 -#define MAX_MOTOR_PHASE_CURRENT_AMPS_X10 300 - - -// Maximum current ramp -// ---------------------------------------------- -// Every second has 15625 pwm cycles interrupts, -// one ADC battery current step -> 0.156 amps: -// -// A / 0.156 = X (we need to do X steps ramp up per second) -// Therefore : -// 15625 / (A / 0.156) => (15625 * 0.156) / A -// -// 20A/s: (15625 * 0.156) / 20 = 135 -#define CURRENT_RAMP_UP_INVERSE_STEP 135 - -// Choose PWM ramp up/down step (higher value will make the motor acceleration slower) -// -// For a 24V battery, 25 for ramp up seems ok. For an higher voltage battery, this values should be higher -#define PWM_DUTY_CYCLE_RAMP_UP_INVERSE_STEP 24 -#define PWM_DUTY_CYCLE_RAMP_DOWN_INVERSE_STEP 28 - -// This value should be near 0. -// You can try to tune with the whell on the air, full throttle and look at batttery current: adjust for lower battery current -#define MOTOR_ROTOR_OFFSET_ANGLE 11 - -// This value is ERPS speed after which a transition happens from sinewave no interpolation to have -// interpolation 60 degrees and must be found experimentally -#define MOTOR_ROTOR_ERPS_START_INTERPOLATION_60_DEGREES 10 - -#define PWM_CYCLES_COUNTER_MAX 3125U // 5 erps minimum speed; 1/5 = 200ms; 200ms/64us = 3125 -#define PWM_CYCLES_SECOND 15625U // 1 / 64us (PWM period) -#define PWM_DUTY_CYCLE_MAX 254 -#define PWM_DUTY_CYCLE_MIN 20 - -#define MOTOR_ROTOR_ANGLE_90 (63 + MOTOR_ROTOR_OFFSET_ANGLE) -#define MOTOR_ROTOR_ANGLE_150 (106 + MOTOR_ROTOR_OFFSET_ANGLE) -#define MOTOR_ROTOR_ANGLE_210 (148 + MOTOR_ROTOR_OFFSET_ANGLE) -#define MOTOR_ROTOR_ANGLE_270 (191 + MOTOR_ROTOR_OFFSET_ANGLE) -#define MOTOR_ROTOR_ANGLE_330 (233 + MOTOR_ROTOR_OFFSET_ANGLE) -#define MOTOR_ROTOR_ANGLE_30 (20 + MOTOR_ROTOR_OFFSET_ANGLE) - -// motor maximum rotation -// 700 is equal to 124 cadence, as TSDZ2 has a reduction ratio of 41.8 -#define MAX_MOTOR_SPEED_ERPS 700 - -// Set how often the motor speed limit controller runs in the isr -#define SPEED_CONTROLLER_CHECK_PERIODS 2000 - -// Set how oftern the current controller runs in the isr -#define CURRENT_CONTROLLER_CHECK_PERIODS 14 - -// adc measurements -// ------------------------------------------ -// 10bit: 0.086V per step -// 0.156A per step -#define ADC_10BIT_VOLTAGE_PER_ADC_STEP_X512 44 -#define ADC_10BIT_CURRENT_PER_ADC_STEP_X512 80 - -#define ADC_10BIT_STEPS_PER_VOLT_X512 5953 - - -// filter coefficients -#define BATTERY_CURRENT_FILTER_COEFFICIENT 2 -#define PHASE_CURRENT_FILTER_COEFFICIENT 2 -#define BATTERY_VOLTAGE_FILTER_COEFFICIENT 2 - - -#define SVM_TABLE_LEN 256 -#define SVM_TABLE_MIDDLE 127 -#define SIN_TABLE_LEN 60 - - // motor states -#define BLOCK_COMMUTATION 1 -#define SINEWAVE_INTERPOLATION_60_DEGREES 2 - - -// index 0-256 to degrees 0-360 -// table is -90 degree preadjusted -static const uint8_t svm_table[SVM_TABLE_LEN] = { - 0, 11, 22, 32, 43, 54, 65, 75, 86, 96, 107, 117, 128, 138, 148, 158, - 168, 178, 188, 198, 207, 217, 222, 225, 228, 230, 233, 235, 238, 240, 242, 244, - 245, 247, 248, 250, 251, 252, 252, 253, 253, 254, 254, 254, 254, 254, 253, 253, - 252, 251, 250, 249, 247, 246, 244, 242, 241, 238, 236, 234, 231, 229, 226, 223, - 220, 223, 226, 229, 231, 234, 236, 238, 241, 242, 244, 246, 247, 249, 250, 251, - 252, 253, 253, 254, 254, 254, 254, 254, 253, 253, 252, 252, 251, 250, 248, 247, - 245, 244, 242, 240, 238, 235, 233, 230, 228, 225, 222, 217, 207, 198, 188, 178, - 168, 158, 148, 138, 128, 117, 107, 96, 86, 75, 65, 54, 43, 32, 22, 11, - 0, 11, 22, 32, 43, 54, 65, 75, 86, 96, 107, 117, 128, 138, 148, 158, - 168, 178, 188, 198, 207, 217, 222, 225, 228, 230, 233, 235, 238, 240, 242, 244, - 245, 247, 248, 250, 251, 252, 252, 253, 253, 254, 254, 254, 254, 254, 253, 253, - 252, 251, 250, 249, 247, 246, 244, 242, 241, 238, 236, 234, 231, 229, 226, 223, - 220, 223, 226, 229, 231, 234, 236, 238, 241, 242, 244, 246, 247, 249, 250, 251, - 252, 253, 253, 254, 254, 254, 254, 254, 253, 253, 252, 252, 251, 250, 248, 247, - 245, 244, 242, 240, 238, 235, 233, 230, 228, 225, 222, 217, 207, 198, 188, 178, - 168, 158, 148, 138, 128, 117, 107, 96, 86, 75, 65, 54, 43, 32, 22, 11 -}; - - -static const uint8_t sin_table[SIN_TABLE_LEN] = -{ - 0, 3, 6, 9, 12, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, - 46, 49, 52, 54, 57, 60, 63, 66, 68, 71, 73, 76, 78, 81, 83, - 86, 88, 90, 92, 95, 97, 99, 101, 102, 104, 106, 108, 109, 111, 113, - 114, 115, 117, 118, 119, 120, 121, 122, 123, 124, 125, 125, 126, 126, 127 -}; - - -// motor control state (shared with isr) -// ------------------------------------------------------ -#define CONTROL_STATE_DISABLE 0 -#define CONTROL_STATE_PREPARE 1 -#define CONTROL_STATE_START 2 -#define CONTROL_STATE_RUNNING 3 - - -static volatile uint8_t control_state = CONTROL_STATE_DISABLE; -static volatile bool is_lvc_triggered = false; -static volatile bool hall_sensor_error = false; - -// not atomic, protected by disabling interrupt while read in compute_foc_angle -static volatile uint16_t speed_erps = 0; - -// current reading saved in 8 bits for atomic access, not expected to exceed 255 (40A) -static volatile uint8_t adc_battery_current = 0; -static volatile uint8_t adc_phase_current = 0; -static volatile uint8_t adc_battery_target_current = 0; - -static volatile uint8_t foc_angle = 0; - -static volatile uint8_t pwm_duty_cycle = 0; -static volatile uint8_t pwm_duty_cycle_target = 0; - -// calculated constant limits (from config) -static uint16_t adc_low_voltage_limit = 0; -static uint8_t adc_battery_max_current = 0; -static uint8_t adc_phase_max_current = 0; - -// ------------------------------------------------------ - -// foc angle filter -static uint16_t foc_angle_accumulated = 0; - -// battery voltage filter -static uint16_t adc_battery_voltage_accumulated = 0; -static uint16_t adc_battery_voltage_filtered = 0; - -// battery current filter -static uint16_t adc_battery_current_accumulated = 0; -static uint16_t adc_battery_current_filtered = 0; - -// motor phase current filter -static uint16_t adc_phase_current_accumulated = 0; -static uint16_t adc_phase_current_filtered = 0; - -static uint16_t lvc_x10V = 0; -static uint8_t target_speed_percent = 0; -static uint8_t target_current_percent = 0; - -static uint16_t adc_steps_per_volt_x512 = ADC_10BIT_STEPS_PER_VOLT_X512; - - -static void flash_opt2_afr5() -{ - // verify if PWM N channels are active on option bytes, if not, enable - static const uint8_t Value = 0x20; - - if (OPT->OPT2 != Value) - { - // unlock data memory - if (!(FLASH->IAPSR & FLASH_IAPSR_DUL)) - { - FLASH->DUKR = FLASH_RASS_KEY2; - FLASH->DUKR = FLASH_RASS_KEY1; - - while (!(FLASH->IAPSR & FLASH_IAPSR_DUL)); - } - - // Enable write access to option bytes - FLASH->CR2 |= FLASH_CR2_OPT; - FLASH->NCR2 &= (uint8_t)(~FLASH_NCR2_NOPT); - - // program option byte and complement - OPT->OPT2 = Value; - OPT->NOPT2 = (uint8_t)(~Value); - - while (!(FLASH->IAPSR & FLASH_IAPSR_EOP)); - - // Disable write access to option bytes - FLASH->CR2 &= (uint8_t)(~FLASH_CR2_OPT); - FLASH->NCR2 |= FLASH_NCR2_NOPT; - - // lock data memory - FLASH->IAPSR &= ~FLASH_IAPSR_DUL; - } -} - -static void read_battery_voltage() -{ - // low pass filter the voltage readed value, to avoid possible fast spikes/noise - adc_battery_voltage_accumulated -= adc_battery_voltage_accumulated >> BATTERY_VOLTAGE_FILTER_COEFFICIENT; - adc_battery_voltage_accumulated += adc_get_battery_voltage(); - adc_battery_voltage_filtered = adc_battery_voltage_accumulated >> BATTERY_VOLTAGE_FILTER_COEFFICIENT; - - is_lvc_triggered = (adc_battery_voltage_filtered < adc_low_voltage_limit); -} - -static void read_battery_current() -{ - // low pass filter the positive battery readed value (no regen current), to avoid possible fast spikes/noise - adc_battery_current_accumulated -= adc_battery_current_accumulated >> BATTERY_CURRENT_FILTER_COEFFICIENT; - adc_battery_current_accumulated += adc_battery_current; - adc_battery_current_filtered = adc_battery_current_accumulated >> BATTERY_CURRENT_FILTER_COEFFICIENT; -} - -static void read_phase_current() -{ - // low pass filter the positive motor pahse value (no regen current), to avoid possible fast spikes/noise - adc_phase_current_accumulated -= adc_phase_current_accumulated >> PHASE_CURRENT_FILTER_COEFFICIENT; - adc_phase_current_accumulated += adc_phase_current; - adc_phase_current_filtered = adc_phase_current_accumulated >> PHASE_CURRENT_FILTER_COEFFICIENT; -} - -static uint8_t asin_table(uint8_t inverted_angle_x128) -{ - // calc asin also converts the final result to degrees - uint8_t index = 0; - while (index < SIN_TABLE_LEN) - { - if (inverted_angle_x128 < sin_table[index]) - { - break; - } - - index++; - } - - // first value of table is 0 so index will always increment to at least 1 and return 0 - return index--; -} - -static void compute_foc_angle() -{ - uint16_t ui16_temp; - uint32_t ui32_temp; - uint16_t e_phase_voltage; - uint32_t i_phase_current_x2; - uint32_t l_x1048576; - uint32_t w_angular_velocity_x16; - uint16_t iwl_128; - - // FOC implementation by calculating the angle between phase current and rotor magnetic flux (BEMF) - // 1. phase voltage is calculate - // 2. I*w*L is calculated, where I is the phase current. L was a measured value for 48V motor. - // 3. inverse sin is calculated of (I*w*L) / phase voltage, were we obtain the angle - // 4. previous calculated angle is applied to phase voltage vector angle and so the - // angle between phase current and rotor magnetic flux (BEMF) is kept at 0 (max torque per amp) - - // calc E phase voltage - ui16_temp = adc_battery_voltage_filtered * ADC_10BIT_VOLTAGE_PER_ADC_STEP_X512; - ui16_temp = (ui16_temp >> 8) * pwm_duty_cycle; - e_phase_voltage = ui16_temp >> 9; - - // calc I phase current - if (pwm_duty_cycle > 10) - { - ui16_temp = ((uint16_t)adc_battery_current_filtered) * ADC_10BIT_CURRENT_PER_ADC_STEP_X512; - i_phase_current_x2 = ui16_temp / pwm_duty_cycle; - } - else - { - i_phase_current_x2 = 0; - } - - // calc W angular velocity: erps * 6.3 - // 101 = 6.3 * 16 - TIM1->IER &= ~(uint8_t)TIM1_IT_CC4; - ui16_temp = speed_erps; - TIM1->IER |= TIM1_IT_CC4; - w_angular_velocity_x16 = ui16_temp * 101; - - // --------------------------------------------------------------------------------------------------------------------- - // 36 V motor: L = 76uH - // 48 V motor: L = 135uH - // ui32_l_x1048576 = 142; // 1048576 = 2^20 | 48V - // ui32_l_x1048576 = 84; // 1048576 = 2^20 | 36V - // - // ui32_l_x1048576 = 142 <--- THIS VALUE WAS verified experimentaly on 2018.07 to be near the best value for a 48V motor - // Test done with a fixed mechanical load, duty_cycle = 200 and 100 and measured battery current was 16 and 6 (10 and 4 amps) - // --------------------------------------------------------------------------------------------------------------------- - -#if 0 - l_x1048576 = 84; // 36 V motor -#else - l_x1048576 = 142; // 48 V motor -#endif - - // calc IwL - ui32_temp = i_phase_current_x2 * l_x1048576; - ui32_temp *= w_angular_velocity_x16; - iwl_128 = ui32_temp >> 18; - - // calc FOC angle - uint8_t foc_angle_unfiltered = asin_table(iwl_128 / e_phase_voltage); - - // low pass filter FOC angle - foc_angle_accumulated -= foc_angle_accumulated >> 4; - foc_angle_accumulated += foc_angle_unfiltered; - foc_angle = foc_angle_accumulated >> 4; -} - - -void motor_pre_init() -{ - SET_PIN_INPUT(PIN_HALL_SENSOR_A); - SET_PIN_INPUT(PIN_HALL_SENSOR_B); - SET_PIN_INPUT(PIN_HALL_SENSOR_C); - - SET_PIN_LOW(PIN_PWM_PHASE_A_LOW); - SET_PIN_LOW(PIN_PWM_PHASE_A_HIGH); - SET_PIN_LOW(PIN_PWM_PHASE_B_LOW); - SET_PIN_LOW(PIN_PWM_PHASE_B_HIGH); - SET_PIN_LOW(PIN_PWM_PHASE_C_LOW); - SET_PIN_LOW(PIN_PWM_PHASE_C_HIGH); - - SET_PIN_OUTPUT(PIN_PWM_PHASE_A_LOW); - SET_PIN_OUTPUT(PIN_PWM_PHASE_A_HIGH); - SET_PIN_OUTPUT(PIN_PWM_PHASE_B_LOW); - SET_PIN_OUTPUT(PIN_PWM_PHASE_B_HIGH); - SET_PIN_OUTPUT(PIN_PWM_PHASE_C_LOW); - SET_PIN_OUTPUT(PIN_PWM_PHASE_C_HIGH); -} - -void motor_init(uint16_t max_current_mA, uint8_t lvc_V, int16_t adc_calib_volt_step_offset) -{ - lvc_x10V = lvc_V * 10; - - uint32_t max_current_x10A = max_current_mA / 100; - - adc_steps_per_volt_x512 = ADC_10BIT_STEPS_PER_VOLT_X512 + adc_calib_volt_step_offset; - - // compute hard current limits (not changed after here) - adc_battery_max_current = (uint8_t)( - ((((uint32_t)MIN(max_current_x10A, MAX_BATTERY_CURRENT_AMPS_X10)) * 512) / 10) / ADC_10BIT_CURRENT_PER_ADC_STEP_X512 - ); - - adc_phase_max_current = (uint8_t)( - ((((uint32_t)MAX_MOTOR_PHASE_CURRENT_AMPS_X10) * 512) / 10) / ADC_10BIT_CURRENT_PER_ADC_STEP_X512 - ); - - adc_low_voltage_limit = (uint16_t)((((uint32_t)lvc_V) * adc_steps_per_volt_x512) / 512); - - flash_opt2_afr5(); - timer1_init_motor_pwm(); - motor_disable(); -} - -void motor_process() -{ - read_battery_voltage(); - read_battery_current(); - read_phase_current(); - compute_foc_angle(); -} - - -void motor_enable() -{ - if (control_state == CONTROL_STATE_DISABLE) - { - control_state = CONTROL_STATE_PREPARE; - } -} - -void motor_disable() -{ - control_state = CONTROL_STATE_DISABLE; -} - -uint16_t motor_status() -{ - static uint16_t last_status = 0; - - uint16_t status = 0; - if (hall_sensor_error) - status |= MOTOR_ERROR_HALL_SENSOR; - - if (is_lvc_triggered) - status |= MOTOR_ERROR_LVC; - - if (status != last_status) - { - last_status = status; - eventlog_write_data(EVT_DATA_MOTOR_STATUS, status); - } - - return status; -} - -uint8_t motor_get_target_speed() -{ - return target_speed_percent; -} - -uint8_t motor_get_target_current() -{ - return target_current_percent; -} - - -void motor_set_target_speed(uint8_t percent) -{ - if (percent > 100) - { - percent = 100; - } - - if (percent != target_speed_percent) - { - target_speed_percent = percent; - eventlog_write_data(EVT_DATA_TARGET_SPEED, percent); - - if (percent == 0) - { - pwm_duty_cycle_target = 0; - } - else - { - pwm_duty_cycle_target = (uint8_t)MAP16(percent, 1, 100, PWM_DUTY_CYCLE_MIN, PWM_DUTY_CYCLE_MAX); - } - } -} - -void motor_set_target_current(uint8_t percent) -{ - if (percent > 100) - { - percent = 100; - } - - if (percent != target_current_percent) - { - target_current_percent = percent; - eventlog_write_data(EVT_DATA_TARGET_CURRENT, percent); - - adc_battery_target_current = ((uint16_t)percent * adc_battery_max_current) / 100; - } -} - -int16_t motor_calibrate_battery_voltage(uint16_t actual_voltage_x100) -{ - int16_t diff = 0; - if (actual_voltage_x100 != 0) - { - uint16_t calibrated_adc_steps_volt_x512 = (uint16_t)(((uint32_t)adc_battery_voltage_filtered * 51200u) / actual_voltage_x100); - - diff = calibrated_adc_steps_volt_x512 - ADC_10BIT_STEPS_PER_VOLT_X512; - adc_steps_per_volt_x512 = calibrated_adc_steps_volt_x512; - } - else - { - // reset calibration if 0 is received - adc_steps_per_volt_x512 = ADC_10BIT_STEPS_PER_VOLT_X512; - diff = 0; - } - - eventlog_write_data(EVT_DATA_CALIBRATE_VOLTAGE, adc_steps_per_volt_x512); - - return diff; -} - - -uint16_t motor_get_battery_lvc_x10() -{ - return lvc_x10V; -} - -uint16_t motor_get_battery_current_x10() -{ - return (uint16_t)((((uint32_t)adc_battery_current_filtered * 10) * ADC_10BIT_CURRENT_PER_ADC_STEP_X512) >> 9); -} - -uint16_t motor_get_battery_voltage_x10() -{ - return (uint16_t)(((uint32_t)adc_battery_voltage_filtered * 5120) / adc_steps_per_volt_x512); -} - - -// state variables only used by isr -// --------------------------------------------- -static uint8_t hall_sensors_state_last = 0; -static uint8_t rotor_absolute_angle = 0; -static uint8_t half_erps_flag = 0; -static uint8_t commutation_type = BLOCK_COMMUTATION; - -static uint16_t pwm_duty_cycle_ramp_up_counter = 0; -static uint16_t pwm_duty_cycle_ramp_down_counter = 0; - -static uint16_t pwm_cycles_counter = 1; -static uint16_t pwm_cycles_counter_6 = 1; -static uint16_t pwm_cycles_counter_total = 0xffff; - -static uint16_t adc_current_ramp_up_counter = 0; -static uint8_t current_controller_counter = 0; -static uint16_t speed_controller_counter = 0; - -static uint8_t adc_battery_ramp_max_current = 0; - -// Measures did with a 24V Q85 328 RPM motor, rotating motor backwards by hand: -// Hall sensor A positive to negative transition | BEMF phase B at max value / top of sinewave -// Hall sensor B positive to negative transition | BEMF phase A at max value / top of sinewave -// Hall sensor C positive to negative transition | BEMF phase C at max value / top of sinewave - -// runs every 64us (PWM frequency) -// Measured on 2022-12-04, the interrupt code takes about 45% of the total 64us -void isr_timer1_cmp(void) __interrupt(ITC_IRQ_TIM1_CAPCOM) -{ - // read battery current adc value, should happen at middle of the pwm duty cycle - // no scan, align data right since we are only interested in the 8 lsb. - ADC1->CR2 = (ADC1_ALIGN_RIGHT); - - // disable eoc interrupt, clear EOC flag and select channel 5 (current sense) - ADC1->CSR = 0x05; - - // perform single mode ADC1 conversion - ADC1->CR1 |= ADC1_CR1_ADON; - while (!(ADC1->CSR & ADC1_CSR_EOC)); - - // adc current reading is truncated to 8bit since that allows a - // range of up to 40A which it is not expected to be surpassed. - // check of 8bit overflow and save result, flag is used to limit - // current in isr if overflow for some reason would occur. - uint8_t adc_battery_current_ovf = ADC1->DRH; - - // atomic write (uint8), current is not expected to exceed adc 255 (40A) - adc_battery_current = ADC1->DRL; - - switch (control_state) - { - case CONTROL_STATE_DISABLE: - // disable outputs - TIM1->CCER1 &= ~(uint8_t)(TIM1_CCER1_CC1E | TIM1_CCER1_CC1NE); // OC1 - TIM1->CCER1 &= ~(uint8_t)(TIM1_CCER1_CC2E | TIM1_CCER1_CC2NE); // OC2 - TIM1->CCER2 &= ~(uint8_t)(TIM1_CCER2_CC3E | TIM1_CCER2_CC3NE); // OC3 - break; - case CONTROL_STATE_PREPARE: - if (speed_erps > 0) - { - // Restart from duty cycle mapped from erps. - // This is probably not the correct way to do this, but - // it seems to work reasonably well. VESC tracks back-emf - // to calculate duty cyle to restart from... - pwm_duty_cycle = (uint8_t)MAP32(speed_erps, 0, MAX_MOTOR_SPEED_ERPS, PWM_DUTY_CYCLE_MIN, PWM_DUTY_CYCLE_MAX); - } - control_state = CONTROL_STATE_START; - break; - case CONTROL_STATE_START: - // enable outputs - TIM1->CCER1 |= (uint8_t)(TIM1_CCER1_CC1E | TIM1_CCER1_CC1NE); // OC1 - TIM1->CCER1 |= (uint8_t)(TIM1_CCER1_CC2E | TIM1_CCER1_CC2NE); // OC2 - TIM1->CCER2 |= (uint8_t)(TIM1_CCER2_CC3E | TIM1_CCER2_CC3NE); // OC3 - control_state = CONTROL_STATE_RUNNING; - break; - default: - break; - } - - // calculate motor current adc value - if (pwm_duty_cycle > 0) - { - // atomic write (uint8), current is not expected to exceed adc 255 (40A) - adc_phase_current = (uint8_t)((adc_battery_current * 256u) / pwm_duty_cycle); - } - else - { - adc_phase_current = 0; - } - - // trigger adc conversion of all channels (scan conversion, buffered) - // adc scan mode conversion will finish before - // this motor control interrupt will be run next time - // - // enable scan, align left - ADC1->CR2 = (ADC1_ALIGN_LEFT | ADC1_CR2_SCAN); - - // clear EOC flag, enable eoc interrupt, scan read all channel 0-7 - ADC1->CSR = (ADC1_CSR_EOCIE | 0x07); - - // start adc scan mode conversion - ADC1->CR1 |= ADC1_CR1_ADON; - - - // read hall sensor signals - // find the motor rotor absolute angle - // calc motor speed in erps (speed_erps) - - // read hall sensors signal pins and mask other pins - // hall sensors sequence with motor forward rotation: 4, 6, 2, 3, 1, 5 - uint8_t hall_sensors_state = - ((GET_PORT(PIN_HALL_SENSOR_A)->IDR & GET_PIN(PIN_HALL_SENSOR_A)) >> 5) | - ((GET_PORT(PIN_HALL_SENSOR_B)->IDR & GET_PIN(PIN_HALL_SENSOR_B)) >> 1) | - ((GET_PORT(PIN_HALL_SENSOR_C)->IDR & GET_PIN(PIN_HALL_SENSOR_C)) >> 3); - - // make sure we run next code only when there is a change on the hall sensors signal - if (hall_sensors_state != hall_sensors_state_last) - { - hall_sensors_state_last = hall_sensors_state; - - switch (hall_sensors_state) - { - case 3: - rotor_absolute_angle = (uint8_t)MOTOR_ROTOR_ANGLE_150; - break; - - case 1: - if (half_erps_flag == 1) - { - half_erps_flag = 0; - pwm_cycles_counter_total = pwm_cycles_counter; - pwm_cycles_counter = 1; - - if (pwm_cycles_counter_total > 0) - { - // This division takes 4.4us - speed_erps = PWM_CYCLES_SECOND / pwm_cycles_counter_total; - } - else - { - speed_erps = PWM_CYCLES_SECOND; - } - - // update motor commutation state based on motor speed - if (speed_erps > MOTOR_ROTOR_ERPS_START_INTERPOLATION_60_DEGREES) - { - if (commutation_type == BLOCK_COMMUTATION) - { - commutation_type = SINEWAVE_INTERPOLATION_60_DEGREES; - } - } - else - { - if (commutation_type == SINEWAVE_INTERPOLATION_60_DEGREES) - { - commutation_type = BLOCK_COMMUTATION; - foc_angle = 0; - } - } - } - - rotor_absolute_angle = (uint8_t)MOTOR_ROTOR_ANGLE_210; - break; - - case 5: - rotor_absolute_angle = (uint8_t)MOTOR_ROTOR_ANGLE_270; - break; - - case 4: - rotor_absolute_angle = (uint8_t)MOTOR_ROTOR_ANGLE_330; - break; - - case 6: - half_erps_flag = 1; - - rotor_absolute_angle = (uint8_t)MOTOR_ROTOR_ANGLE_30; - break; - - // BEMF is always 90 degrees advanced over motor rotor position degree zero - // and here (hall sensor C blue wire, signal transition from positive to negative), - // phase B BEMF is at max value (measured on osciloscope by rotating the motor) - case 2: - rotor_absolute_angle = (uint8_t)MOTOR_ROTOR_ANGLE_90; - break; - - default: - // invalid hall sensor signal - hall_sensor_error = true; - return; - } - - hall_sensor_error = false; - pwm_cycles_counter_6 = 1; - } - - // count number of fast loops / pwm cycles and reset some states when motor is near zero speed - if (pwm_cycles_counter < PWM_CYCLES_COUNTER_MAX) - { - pwm_cycles_counter++; - pwm_cycles_counter_6++; - } - else // happens when motor is stopped or near zero speed - { - pwm_cycles_counter = 1; // don't put to 0 to avoid 0 divisions - pwm_cycles_counter_6 = 1; - half_erps_flag = 0; - speed_erps = 0; - pwm_cycles_counter_total = 0xffff; - foc_angle = 0; - commutation_type = BLOCK_COMMUTATION; - hall_sensors_state_last = 0; // this way we force execution of hall sensors code next time - } - - // calc interpolation angle and sinewave table index - uint8_t svm_table_index = rotor_absolute_angle + foc_angle; - -#if 1 // may be useful to disable interpolation when debugging - - // calculate the interpolation angle (and it doesn't work when motor starts and at very low speeds) - if (commutation_type == SINEWAVE_INTERPOLATION_60_DEGREES) - { - // division by 0: motor_pwm_cycles_counter_total should never be 0 - // TODO: verifiy if (motor_pwm_cycles_counter_6 << 8) do not overflow - uint8_t interpolation_angle = (pwm_cycles_counter_6 << 8) / pwm_cycles_counter_total; // this operations take 4.4us - svm_table_index += interpolation_angle; - } -#endif - - - // pwm duty cycle controller - // ---------------------------------------------------------------------- - // brakes are active - // limit battery undervoltage - // limit battery max current - // limit motor max erps - // ramp up/down pwm duty cycle towards target - - ++current_controller_counter; - ++speed_controller_counter; - - if ( - control_state == CONTROL_STATE_DISABLE || - is_lvc_triggered || - (pwm_duty_cycle_target == 0) || - (GET_PIN_INPUT_STATE(PIN_BRAKE) == 0) //active low - ) - { - if (pwm_duty_cycle) - { - --pwm_duty_cycle; - } - } - // do not control current at every PWM cycle, that will measure and control too fast. Use counter to limit - else if - ( - current_controller_counter > CURRENT_CONTROLLER_CHECK_PERIODS && - ( - // check if truncated 8bit current reading did overflow - adc_battery_current_ovf || - // compare against ramp controller current limit - adc_battery_current > adc_battery_ramp_max_current || - // or hard motor phase current limit - adc_phase_current > adc_phase_max_current - ) - ) - { - if (pwm_duty_cycle) - { - --pwm_duty_cycle; - } - } - else if ( - speed_controller_counter > SPEED_CONTROLLER_CHECK_PERIODS && // test about every 100ms - speed_erps > MAX_MOTOR_SPEED_ERPS - ) - { - if (pwm_duty_cycle) - { - --pwm_duty_cycle; - } - } - else // nothing to limit, so adjust duty_cycle to duty_cycle_target - { - if (pwm_duty_cycle_target > pwm_duty_cycle) - { - if (pwm_duty_cycle_ramp_up_counter++ >= PWM_DUTY_CYCLE_RAMP_UP_INVERSE_STEP) - { - pwm_duty_cycle_ramp_up_counter = 0; - ++pwm_duty_cycle; - } - } - else if (pwm_duty_cycle_target < pwm_duty_cycle) - { - if (pwm_duty_cycle_ramp_down_counter++ >= PWM_DUTY_CYCLE_RAMP_DOWN_INVERSE_STEP) - { - pwm_duty_cycle_ramp_down_counter = 0; - --pwm_duty_cycle; - } - } - } - - // reset periodic check counters - if (speed_controller_counter > SPEED_CONTROLLER_CHECK_PERIODS) - { - speed_controller_counter = 0; - } - - if (current_controller_counter > CURRENT_CONTROLLER_CHECK_PERIODS) - { - current_controller_counter = 0; - } - - - // calculate final pwm duty cycle values to be applied to TIMER1 - - // The first half of the table is the positive offset from the middle (0x100), - // in that case just set MSB to 0x1, and the value from the table*duty cycle to LSB. - // The second half of the table is a negative offset from that same middle, - // and should be substracted from 0x100. - // To cheat, we leave it as 0x100 when this value * duty cycle is 0, - // otherwise we assume MSB is 0, and just invert the value from the table from LSB. - // Checking to see if svm_table_index >= 128 (180 degrees) by & 0x80, - // as SDCC is not yet smart enough to do that automatically. - #define CALC_PHASE(PHASE_OUTPUT) do { \ - uint8_t tmp = ((uint16_t)(pwm_duty_cycle * svm_table[svm_table_index]) / 256); \ - if (tmp > 0 && (svm_table_index & 0x80)) \ - { \ - PHASE_OUTPUT##_lsb = 0 - tmp; \ - PHASE_OUTPUT##_msb = 0; \ - } \ - else \ - { \ - PHASE_OUTPUT##_lsb = tmp; \ - PHASE_OUTPUT##_msb = 1; \ - } \ - } while (0) - - - // phase B as reference phase - uint8_t phase_b_voltage_msb; - uint8_t phase_b_voltage_lsb; - CALC_PHASE(phase_b_voltage); - - // phase C is advanced 120 degrees over phase B - svm_table_index += 85; // 120º / 360 * 256 = 85 - uint8_t phase_c_voltage_msb; - uint8_t phase_c_voltage_lsb; - CALC_PHASE(phase_c_voltage); - - // phase A is advanced 240 degrees over phase B - svm_table_index += 86; // 240º / 360 * 256 = 171 - 85 already added = 86 - uint8_t phase_a_voltage_msb; - uint8_t phase_a_voltage_lsb; - CALC_PHASE(phase_a_voltage); - - - // set final duty cycle value to pwm timers - // phase B - TIM1->CCR3H = phase_b_voltage_msb; - TIM1->CCR3L = phase_b_voltage_lsb; - // phase C - TIM1->CCR2H = phase_c_voltage_msb; - TIM1->CCR2L = phase_c_voltage_lsb; - // phase A - TIM1->CCR1H = phase_a_voltage_msb; - TIM1->CCR1L = phase_a_voltage_lsb; - - - // ramp up motor current - if (adc_battery_target_current > adc_battery_ramp_max_current) - { - if (adc_current_ramp_up_counter++ >= CURRENT_RAMP_UP_INVERSE_STEP) - { - adc_current_ramp_up_counter = 0; - adc_battery_ramp_max_current++; - } - } - else if (adc_battery_target_current < adc_battery_ramp_max_current) - { - // we are not doing a ramp down here, just directly setting to the target value - adc_battery_ramp_max_current = adc_battery_target_current; - } - - // clears the timer1 interrupt CC4 pending bit - TIM1->SR1 = (uint8_t)(~(uint8_t)TIM1_IT_CC4); -} diff --git a/src/firmware/tsdz2/pins.h b/src/firmware/tsdz2/pins.h deleted file mode 100644 index 742b7b53..00000000 --- a/src/firmware/tsdz2/pins.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ -#ifndef _TSDZ2_PINS_H_ -#define _TSDZ2_PINS_H_ - -#include "tsdz2/cpu.h" -#include "tsdz2/stm8s/stm8s.h" -#include "tsdz2/stm8s/stm8s_gpio.h" - -#define PIN_HALL_SENSOR_A GPIOE, GPIO_PIN_5 -#define PIN_HALL_SENSOR_B GPIOD, GPIO_PIN_2 -#define PIN_HALL_SENSOR_C GPIOC, GPIO_PIN_5 - -#define PIN_PWM_PHASE_A_LOW GPIOB, GPIO_PIN_2 -#define PIN_PWM_PHASE_A_HIGH GPIOC, GPIO_PIN_3 - -#define PIN_PWM_PHASE_B_LOW GPIOB, GPIO_PIN_1 -#define PIN_PWM_PHASE_B_HIGH GPIOC, GPIO_PIN_2 - -#define PIN_PWM_PHASE_C_LOW GPIOB, GPIO_PIN_0 -#define PIN_PWM_PHASE_C_HIGH GPIOC, GPIO_PIN_1 - -#define PIN_BATTERY_CURRENT GPIOB, GPIO_PIN_5 -#define PIN_BATTERY_VOLTAGE GPIOB, GPIO_PIN_6 - -#define PIN_PAS1 GPIOD, GPIO_PIN_7 -#define PIN_PAS2 GPIOE, GPIO_PIN_0 -#define PIN_SPEED_SENSOR GPIOA, GPIO_PIN_1 -#define PIN_BRAKE GPIOC, GPIO_PIN_6 -#define PIN_THROTTLE GPIOB, GPIO_PIN_7 -#define PIN_LIGHTS GPIOD, GPIO_PIN_4 - -#define PIN_TORQUE_SENSOR GPIOB, GPIO_PIN_3 -#define PIN_TORQUE_SENSOR_EXC GPIOD, GPIO_PIN_3 - -#define PIN_EXTERNAL_RX GPIOD, GPIO_PIN_6 -#define PIN_EXTERNAL_TX GPIOD, GPIO_PIN_5 - -#endif diff --git a/src/firmware/tsdz2/sensors.c b/src/firmware/tsdz2/sensors.c deleted file mode 100644 index aeb798c4..00000000 --- a/src/firmware/tsdz2/sensors.c +++ /dev/null @@ -1,279 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "sensors.h" -#include "intellisense.h" -#include "fwconfig.h" -#include "tsdz2/interrupt.h" -#include "tsdz2/timers.h" -#include "tsdz2/stm8.h" -#include "tsdz2/pins.h" -#include "tsdz2/stm8s/stm8s_tim4.h" - -// interrupt runs at 100us interval, see timer4 setup in timers.c - -// :TODO: this file contains a lot of duplicated code from bbsx version, try to share code - -#define PAS_SENSOR_NUM_SIGNALS PAS_PULSES_REVOLUTION -#define PAS_SENSOR_MIN_PULSE_MS_X10 50 // 500rpm limit - -#define SPEED_SENSOR_MIN_PULSE_MS_X10 500 -#define SPEED_SENSOR_TIMEOUT_MS_X10 25000 - - -static volatile uint16_t pas_pulse_counter; -static volatile bool pas_direction_backward; -static volatile uint16_t pas_period_length; // pulse length counted in interrupt frequency (100us) -static uint16_t pas_period_counter; -static bool pas_prev1; -static bool pas_prev2; -static uint16_t pas_stop_delay_periods; - -static volatile uint16_t speed_ticks_period_length; // pulse length counted in interrupt frequency (100us) -static uint16_t speed_period_counter; -static bool speed_prev_state; -static uint8_t speed_ticks_per_rpm; - -extern void torque_sensor_init(); -extern void torque_sensor_process(); - -void sensors_init() -{ - pas_period_counter = 0; - pas_pulse_counter = 0; - pas_direction_backward = false; - pas_period_length = 0; - pas_stop_delay_periods = 1500; - speed_period_counter = 0; - speed_ticks_period_length = 0; - speed_prev_state = false; - speed_ticks_per_rpm = 1; - - // pins do not have external interrupt, use timer0 to evaluate state frequently - SET_PIN_INPUT(PIN_PAS1); - SET_PIN_INPUT(PIN_PAS2); - SET_PIN_INPUT(PIN_SPEED_SENSOR); - SET_PIN_INPUT_PULLUP(PIN_BRAKE); - - pas_prev1 = GET_PIN_INPUT_STATE(PIN_PAS1); - pas_prev2 = GET_PIN_INPUT_STATE(PIN_PAS2); - - torque_sensor_init(); - torque_sensor_process(); - - timer4_init_sensors(); -} - -void sensors_process() -{ - torque_sensor_process(); -} - - -void pas_set_stop_delay(uint16_t delay_ms) -{ - pas_stop_delay_periods = delay_ms * 10; -} - -uint16_t pas_get_cadence_rpm_x10() -{ - uint16_t tmp; - TIM4->IER &= ~TIM4_IT_UPDATE; // disable timer4 interrupt - tmp = pas_period_length; - TIM4->IER |= TIM4_IT_UPDATE; - - if (tmp > 0) - { - return (uint16_t)((6000000ul / PAS_SENSOR_NUM_SIGNALS) / tmp); - } - else - { - return 0; - } -} - -uint16_t pas_get_pulse_counter() -{ - uint16_t tmp; - TIM4->IER &= ~TIM4_IT_UPDATE; // disable timer4 interrupts - tmp = pas_pulse_counter; - TIM4->IER |= TIM4_IT_UPDATE; - - return tmp; -} - -bool pas_is_pedaling_forwards() -{ - uint16_t period_length; - uint8_t direction_backward; - TIM4->IER &= ~TIM4_IT_UPDATE; // disable timer4 interrupts - period_length = pas_period_length; - direction_backward = pas_direction_backward; - TIM4->IER |= TIM4_IT_UPDATE; - - // atomic read operation, no need to disable timer interrupt - return period_length > 0 && !direction_backward; -} - -bool pas_is_pedaling_backwards() -{ - uint16_t period_length; - uint8_t direction_backward; - TIM4->IER &= ~TIM4_IT_UPDATE; // disable timer4 interrupts - period_length = pas_period_length; - direction_backward = pas_direction_backward; - TIM4->IER |= TIM4_IT_UPDATE; - - return (period_length > 0) && direction_backward; -} - -void speed_sensor_set_signals_per_rpm(uint8_t num_signals) -{ - speed_ticks_per_rpm = num_signals; -} - -bool speed_sensor_is_moving() -{ - uint16_t tmp; - TIM4->IER &= ~TIM4_IT_UPDATE; // disable timer4 interrupts - tmp = speed_ticks_period_length; - TIM4->IER |= TIM4_IT_UPDATE; - - return tmp > 0; -} - -uint16_t speed_sensor_get_rpm_x10() -{ - uint16_t tmp; - TIM4->IER &= ~TIM4_IT_UPDATE; // disable timer4 interrupts - tmp = speed_ticks_period_length; - TIM4->IER |= TIM4_IT_UPDATE; - - if (tmp > 0) - { - return 6000000ul / tmp / speed_ticks_per_rpm; - } - - return 0; -} - - -int16_t temperature_contr_x100() -{ - return 0; // n/a -} - -int16_t temperature_motor_x100() -{ - return 0; // n/a -} - - -bool brake_is_activated() -{ - return !GET_PIN_INPUT_STATE(PIN_BRAKE); -} - -bool shift_sensor_is_activated() -{ - return false; // n/a -} - - -void isr_timer4_ovf(void) __interrupt(ITC_IRQ_TIM4_OVF) -{ - // clear interrupt bit - TIM4->SR1 &= (uint8_t)(~TIM4_IT_UPDATE); - - // Pas - { - bool pas1 = GET_PIN_INPUT_STATE(PIN_PAS1); - bool pas2 = GET_PIN_INPUT_STATE(PIN_PAS2); - - if (pas1 && !pas_prev1 /* && pas_period_counter > PAS_SENSOR_MIN_PULSE_MS_X10 */) - { - pas_pulse_counter++; - - if (pas_direction_backward != pas2) - { - pas_direction_backward = pas2; - - // Reset pas pulse counter if pedal direction is changed, - // this variable counts the number of pulses since start of pedaling session. - pas_pulse_counter = 0; - } - - if (pas_period_counter > 0) - { - if (pas_period_counter <= pas_stop_delay_periods) - { - pas_period_length = pas_period_counter; // save in order to be able to calculate rpm when needed - } - else - { - pas_period_length = 0; - } - - pas_period_counter = 0; - } - } - else - { - // Do not allow wraparound or computed pedaling cadence will wrong after pedals has been still. - if (pas_period_counter < 65535) - { - pas_period_counter++; - } - - if (pas_period_length > 0 && pas_period_counter > pas_stop_delay_periods) - { - pas_period_length = 0; - pas_pulse_counter = 0; - pas_direction_backward = false; - } - } - - pas_prev1 = pas1; - pas_prev2 = pas2; - } - - - // Speed sensor - { - bool spd = GET_PIN_INPUT_STATE(PIN_SPEED_SENSOR); - - if (spd && !speed_prev_state && speed_period_counter > SPEED_SENSOR_MIN_PULSE_MS_X10) - { - if (speed_period_counter <= SPEED_SENSOR_TIMEOUT_MS_X10) - { - speed_ticks_period_length = speed_period_counter; - } - else - { - speed_ticks_period_length = 0; - } - - speed_period_counter = 0; - } - else - { - // Do not allow wraparound or computed speed will wrong after bike has been still. - if (speed_period_counter < 65535) - { - speed_period_counter++; - } - - if (speed_ticks_period_length > 0 && speed_period_counter > SPEED_SENSOR_TIMEOUT_MS_X10) - { - speed_ticks_period_length = 0; - } - } - - speed_prev_state = spd; - } -} diff --git a/src/firmware/tsdz2/stm8.h b/src/firmware/tsdz2/stm8.h deleted file mode 100644 index 55ee1a95..00000000 --- a/src/firmware/tsdz2/stm8.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#ifndef _TSDZ2_STM_8_H_ -#define _TSDZ2_STM_8_H_ - -#include - -#define EXPAND(x) x - -#define SET_PIN_INPUT_(PORT, PIN) PORT->DDR &= (uint8_t)(~(PIN)); PORT->CR1 &= (uint8_t)(~(PIN)) -#define SET_PIN_INPUT(...) EXPAND(SET_PIN_INPUT_(__VA_ARGS__)) - -#define SET_PIN_INPUT_PULLUP_(PORT, PIN) PORT->DDR &= (uint8_t)(~(PIN)); PORT->CR1 |= (uint8_t)PIN -#define SET_PIN_INPUT_PULLUP(...) EXPAND(SET_PIN_INPUT_PULLUP_(__VA_ARGS__)) - -#define SET_PIN_OUTPUT_(PORT, PIN) PORT->DDR |= (uint8_t)PIN; PORT->CR1 |= (uint8_t)PIN; PORT->CR2 |= (uint8_t)(PIN) -#define SET_PIN_OUTPUT(...) EXPAND(SET_PIN_OUTPUT_(__VA_ARGS__)) - -#define SET_PIN_OUTPUT_OPEN_DRAIN_(PORT, PIN) PORT->DDR |= (uint8_t)PIN; PORT->CR1 &= (uint8_t)(~(PIN)); PORT->CR2 |= (uint8_t)(PIN) -#define SET_PIN_OUTPUT_OPEN_DRAIN(...) EXPAND(SET_PIN_OUTPUT_OPEN_DRAIN_(__VA_ARGS__)) - - -#define GET_PIN_INPUT_STATE_(PORT, PIN) ((PORT->IDR & (uint8_t)PIN) != 0) -#define GET_PIN_INPUT_STATE(...) EXPAND(GET_PIN_INPUT_STATE_(__VA_ARGS__)) - -#define SET_PIN_HIGH_(PORT, PIN) PORT->ODR |= (uint8_t)PIN -#define SET_PIN_HIGH(...) EXPAND(SET_PIN_HIGH_(__VA_ARGS__)) - -#define SET_PIN_LOW_(PORT, PIN) PORT->ODR &= (uint8_t)(~PIN) -#define SET_PIN_LOW(...) EXPAND(SET_PIN_LOW_(__VA_ARGS__)) - -#define TOGGLE_PIN_(PORT, PIN) PORT->ODR ^= (PIN) -#define TOGGLE_PIN(...) EXPAND(TOGGLE_PIN_(__VA_ARGS__)) - - -#define GET_PIN_(PORT, PIN) PIN -#define GET_PIN(...) EXPAND(GET_PIN_(__VA_ARGS__)) - -#define GET_PORT_(PORT, PIN) PORT -#define GET_PORT(...) EXPAND(GET_PORT_(__VA_ARGS__)) - - -#endif diff --git a/src/firmware/tsdz2/system.c b/src/firmware/tsdz2/system.c deleted file mode 100644 index 1f77133c..00000000 --- a/src/firmware/tsdz2/system.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "system.h" -#include "watchdog.h" -#include "cpu.h" -#include "tsdz2/interrupt.h" -#include "tsdz2/timers.h" - -#include "tsdz2/stm8s/stm8s.h" -#include "tsdz2/stm8s/stm8s_clk.h" -#include "tsdz2/stm8s/stm8s_tim3.h" - -static volatile uint32_t _ms; - -void system_init() -{ - CLK->CKDIVR = 0x00; // Set 16MHz - while ((CLK->ICKR & CLK_ICKR_HSIRDY) == 0); // Wait for stable clock - - _ms = 0; - - // Setup timer3 as a ms counter - timer3_init_system(); - - enableInterrupts(); -} - -uint32_t system_ms() -{ - uint32_t val; - uint8_t ier = TIM3->IER; - - TIM3->IER &= ~(TIM3_IT_UPDATE); // disable timer3 interrupt - val = _ms; - - TIM3->IER = ier; - - return val; -} - -void system_delay_ms(uint16_t ms) -{ - if (!ms) - { - return; - } - - uint32_t end = system_ms() + ms; - while (system_ms() != end) - { - watchdog_yeild(); - } -} - -void isr_timer3_ovf(void) __interrupt(ITC_IRQ_TIM3_OVF) -{ - _ms++; - - // Clear interrupt pending bit - TIM3->SR1 &= (uint8_t)(~TIM3_IT_UPDATE); -} diff --git a/src/firmware/tsdz2/timers.c b/src/firmware/tsdz2/timers.c deleted file mode 100644 index b9d63dad..00000000 --- a/src/firmware/tsdz2/timers.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "timers.h" -#include "cpu.h" -#include "tsdz2/stm8s/stm8s.h" -#include "tsdz2/stm8s/stm8s_clk.h" -#include "tsdz2/stm8s/stm8s_tim1.h" -#include "tsdz2/stm8s/stm8s_tim2.h" -#include "tsdz2/stm8s/stm8s_tim3.h" -#include "tsdz2/stm8s/stm8s_tim4.h" - -#define TIM1_AUTO_RELOAD_PERIOD 511 -#define TIM2_AUTO_RELOAD_PERIOD 159 // 20us -#define TIM3_AUTO_RELOAD_PERIOD 15999 // 1ms -#define TIM4_AUTO_RELOAD_PERIOD 99 // 100us - - -void timers_init() -{ - // nothing to do here -} - - -void timer1_init_motor_pwm() -{ - CLK->PCKENR1 |= CLK_PCKENR1_TIM1; - - // prescaler - TIM1->PSCRH = 0; - TIM1->PSCRL = 0; - - // auto reload - // clock = 16MHz, counter period = 1024, PWM freq = 16MHz / 1024 = 15.625MHz - // (BUT PWM center aligned mode needs double frequency) - TIM1->ARRH = (uint8_t)(TIM1_AUTO_RELOAD_PERIOD >> 8); - TIM1->ARRL = (uint8_t)TIM1_AUTO_RELOAD_PERIOD; - - TIM1->CR1 |= TIM1_COUNTERMODE_CENTERALIGNED1; - TIM1->RCR = 1; - - // OC1 - TIM1->CCER1 |= (uint8_t)( - (uint8_t)(TIM1_OUTPUTSTATE_DISABLE & TIM1_CCER1_CC1E) | - (uint8_t)(TIM1_OUTPUTNSTATE_DISABLE & TIM1_CCER1_CC1NE) | - (uint8_t)(TIM1_OCPOLARITY_HIGH & TIM1_CCER1_CC1P) | - (uint8_t)(TIM1_OCNPOLARITY_HIGH & TIM1_CCER1_CC1NP) - ); - - TIM1->CCMR1 |= TIM1_OCMODE_PWM1; - - TIM1->OISR |= (uint8_t)( - (uint8_t)(TIM1_OCIDLESTATE_RESET & TIM1_OISR_OIS1) | - (uint8_t)(TIM1_OCNIDLESTATE_SET & TIM1_OISR_OIS1N) - ); - - TIM1->CCR1H = 0; - TIM1->CCR1L = 255; - - - // OC2 - TIM1->CCER1 |= (uint8_t)( - (uint8_t)(TIM1_OUTPUTSTATE_DISABLE & TIM1_CCER1_CC2E) | - (uint8_t)(TIM1_OUTPUTNSTATE_DISABLE & TIM1_CCER1_CC2NE) | - (uint8_t)(TIM1_OCPOLARITY_HIGH & TIM1_CCER1_CC2P) | - (uint8_t)(TIM1_OCNPOLARITY_HIGH & TIM1_CCER1_CC2NP) - ); - - TIM1->CCMR2 |= TIM1_OCMODE_PWM1; - - TIM1->OISR |= (uint8_t)( - (uint8_t)(TIM1_OCIDLESTATE_RESET & TIM1_OISR_OIS2) | - (uint8_t)(TIM1_OCNIDLESTATE_SET & TIM1_OISR_OIS2N) - ); - - TIM1->CCR2H = 0; - TIM1->CCR2L = 255; - - // OC3 - TIM1->CCER2 |= (uint8_t)( - (uint8_t)(TIM1_OUTPUTSTATE_DISABLE & TIM1_CCER2_CC3E) | - (uint8_t)(TIM1_OUTPUTNSTATE_DISABLE & TIM1_CCER2_CC3NE) | - (uint8_t)(TIM1_OCPOLARITY_HIGH & TIM1_CCER2_CC3P) | - (uint8_t)(TIM1_OCNPOLARITY_HIGH & TIM1_CCER2_CC3NP) - ); - - TIM1->CCMR3 |= TIM1_OCMODE_PWM1; - - TIM1->OISR |= (uint8_t)( - (uint8_t)(TIM1_OCIDLESTATE_RESET & TIM1_OISR_OIS3) | - (uint8_t)(TIM1_OCNIDLESTATE_SET & TIM1_OISR_OIS3N) - ); - - TIM1->CCR3H = 0; - TIM1->CCR3L = 255; - - // OC4 - // Used for to fire interrupt at a specific time (middle of DC link current pulses) - // and is always syncronized with PWM - - TIM1->CCER2 |= (uint8_t)( - (uint8_t)(TIM1_OUTPUTSTATE_DISABLE & TIM1_CCER2_CC4E) | - (uint8_t)(TIM1_OCPOLARITY_HIGH & TIM1_CCER2_CC4P) - ); - - TIM1->OISR &= (uint8_t)(~TIM1_OISR_OIS4); - - // timming for interrupt firing (hand adjusted) - const uint16_t Timing = 285; - - TIM1->CCR4H = (uint8_t)(Timing >> 8); - TIM1->CCR4L = (uint8_t)Timing; - - // hardware needs a dead time of 1us - // 16, // DTG = 0; dead time in 62.5 ns steps; 1us/62.5ns = 16 - TIM1->DTR = (uint8_t)16; - - TIM1->BKR = (uint8_t)( - TIM1_OSSISTATE_ENABLE | - TIM1_LOCKLEVEL_OFF | - TIM1_BREAK_DISABLE | - TIM1_BREAKPOLARITY_LOW | - TIM1_AUTOMATICOUTPUT_DISABLE - ); - - // enable cc4 interrupt - TIM1->IER |= TIM1_IT_CC4; - - // enable timer - TIM1->CR1 |= TIM1_CR1_CEN; - - TIM1->BKR |= TIM1_BKR_MOE; -} - -void timer2_init_torque_sensor_pwm() -{ - // Timer2 is used to create the pulse signal for excitation of the torque sensor circuit - // Timer2 clock = 16MHz; target: 20us period --> 50khz - // counter period = (1 / (16000000 / prescaler)) * (159 + 1) = 20us - - // set period - TIM2->PSCR = TIM2_PRESCALER_2; - TIM2->ARRH = (uint8_t)(TIM2_AUTO_RELOAD_PERIOD >> 8); - TIM2->ARRL = (uint8_t)(TIM2_AUTO_RELOAD_PERIOD); - - // pulse of 2us - TIM2->CCER1 |= TIM2_CCER1_CC2E; // output enable - TIM2->CCMR2 |= TIM2_OCMODE_PWM1; - TIM2->CCR2H = 0; - TIM2->CCR2L = 16; - - // enable - TIM2->CCMR2 |= TIM2_CCMR_OCxPE; - TIM2->CR1 |= TIM2_CR1_ARPE; - TIM2->CR1 |= TIM2_CR1_CEN; -} - -void timer3_init_system() -{ - // enable timer3 clock source - CLK->PCKENR1 |= CLK_PCKENR1_TIM3; - - // set period - TIM3->PSCR = TIM3_PRESCALER_1; - TIM3->ARRH = (uint8_t)(TIM3_AUTO_RELOAD_PERIOD >> 8); - TIM3->ARRL = (uint8_t)(TIM3_AUTO_RELOAD_PERIOD); - - // clear counter - TIM3->CNTRH = 0; - TIM3->CNTRL = 0; - - // enable TIM3 interrupt - TIM3->IER |= TIM3_IT_UPDATE; - - // clear interrupt pending bit - TIM3->SR1 &= ~TIM3_IT_UPDATE; - - // TIM3 enable - TIM3->CR1 |= TIM3_CR1_CEN; -} - -void timer4_init_sensors() -{ - // enable timer4 clock source - CLK->PCKENR1 |= CLK_PCKENR1_TIM4; - - // set period - TIM4->PSCR = TIM4_PRESCALER_16; - TIM4->ARR = TIM4_AUTO_RELOAD_PERIOD; - - // clear counter - TIM4->CNTR = 0; - - // enable TIM4 interrupt - TIM4->IER |= TIM4_IT_UPDATE; - - // clear interrupt pending bit - TIM4->SR1 &= ~TIM4_IT_UPDATE; - - // TIM4 enable - TIM4->CR1 |= TIM4_CR1_CEN; -} diff --git a/src/firmware/tsdz2/torquesensor.c b/src/firmware/tsdz2/torquesensor.c deleted file mode 100644 index dc73c167..00000000 --- a/src/firmware/tsdz2/torquesensor.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ -#include -#include "system.h" -#include "sensors.h" -#include "adc.h" -#include "util.h" -#include "eventlog.h" -#include "tsdz2/stm8.h" -#include "tsdz2/pins.h" -#include "tsdz2/stm8s/stm8s_adc1.h" -#include "tsdz2/timers.h" - - -#define AUTO_BIAS_START_TIME_MS 2000 -#define AUTO_BIAS_DURATION_MS 3000 - -// Hard coded default torque sensor calibration table for now -// -// Torque sensor readings on different TSDZ2 differs by a lot. -// This table is therefore not perfect for every motor but -// it will have to be good enough for now. -// -// Default firmware has no way to calibrate, no idea if calibrations -// is done at factory though. -// -// Consider adding manual calibration though config tool at some point. -// Until then, sensitivity can be set using torque amplification factor, -// which works well enough even if response will potentially not be linear. - -#define TORQUE_SENSOR_LUT_SIZE 8 - -typedef struct { uint8_t adc; uint16_t nm_x100; } torque_lut_t; -static const torque_lut_t torque_sensor_lut[TORQUE_SENSOR_LUT_SIZE] = -{ - // (adc value - bias), (Nm x 100) - { 0, 0 }, // 0kg - { 30, 834 }, // 5kg - { 55, 1668 }, // 10kg - { 78, 2502 }, // 15kg - { 93, 3169 }, // 19kg - { 188, 7004 }, // 42kg - { 204, 8672 }, // 52kg - { 224, 17511 } // 105kg -}; - -static uint16_t torque_adc_to_nm_x100(uint16_t torque_adc) -{ - // interpolate in lookup table - - if (torque_adc < torque_sensor_lut[0].adc) - { - // use minimum value - return torque_sensor_lut[0].nm_x100; - } - else if (torque_adc > torque_sensor_lut[TORQUE_SENSOR_LUT_SIZE - 1].adc) - { - // use maximum value - return torque_sensor_lut[TORQUE_SENSOR_LUT_SIZE - 1].nm_x100; - } - - uint8_t i = 0; - for (i = 0; i < TORQUE_SENSOR_LUT_SIZE - 1; i++) - { - if (torque_sensor_lut[i + 1].adc > torque_adc) - { - break; - } - } - - return (uint16_t)MAP32(torque_adc, - torque_sensor_lut[i].adc, - torque_sensor_lut[i + 1].adc, - torque_sensor_lut[i].nm_x100, - torque_sensor_lut[i + 1].nm_x100); -} - -static uint16_t torque_nm_x100 = 0; - -static bool adc_bias_set = false; -static uint16_t adc_bias_steps = 0; - - -void torque_sensor_init() -{ - SET_PIN_OUTPUT_OPEN_DRAIN(PIN_TORQUE_SENSOR_EXC); - - timer2_init_torque_sensor_pwm(); - - // some delay for torque sensor to power on - system_delay_ms(50); -} - -void torque_sensor_process() -{ - if (adc_bias_set) - { - uint16_t adc_val = adc_get_torque(); - if (adc_val > adc_bias_steps) - { - adc_val -= adc_bias_steps; - } - else - { - adc_val = 0; - } - - // IDEA: Find max over pedal revolution period and use sin average (0.637)? - // Doesn't seem to be needed, hw filtering seems to be very slow and should average just fine - torque_nm_x100 = torque_adc_to_nm_x100(adc_val); - } - else - { - // find torque sensor adc bias during startup, torque sensor lookup table is relative to bias - - uint32_t now = system_ms(); - if (now < (AUTO_BIAS_START_TIME_MS + AUTO_BIAS_DURATION_MS)) - { - if (now > AUTO_BIAS_START_TIME_MS) - { - uint16_t adc_val = adc_get_torque(); - if (adc_val > adc_bias_steps) - { - adc_bias_steps = adc_val; - } - } - } - else - { - adc_bias_set = true; - eventlog_write_data(EVT_DATA_TORQUE_ADC_CALIBRATED, adc_bias_steps); - } - } -} - -uint16_t torque_sensor_get_nm_x100() -{ - return torque_nm_x100; -} - -bool torque_sensor_ok() -{ - return !adc_bias_set || adc_bias_steps > 50; -} diff --git a/src/firmware/tsdz2/uart.c b/src/firmware/tsdz2/uart.c deleted file mode 100644 index 4aef0b1d..00000000 --- a/src/firmware/tsdz2/uart.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "uart.h" -#include "interrupt.h" -#include "watchdog.h" - -#include - - -#define RX1_BUFFER_SIZE 64 -#define RX1_BUFFER_MASK (RX1_BUFFER_SIZE - 1) - -#define TX1_BUFFER_SIZE 32 -#define TX1_BUFFER_MASK (TX1_BUFFER_SIZE - 1) - -static volatile uint8_t rx1_head; -static volatile uint8_t rx1_tail; -static volatile uint8_t rx1_buf[RX1_BUFFER_SIZE]; -static volatile uint8_t tx1_head; -static volatile uint8_t tx1_tail; -static volatile uint8_t tx1_sending; -static volatile uint8_t tx1_buf[TX1_BUFFER_SIZE]; - -void uart_open(uint32_t baudrate) -{ - rx1_head = 0; - rx1_tail = 0; - tx1_head = 0; - tx1_tail = 0; - tx1_sending = 0; - - // enable uart2 clock - CLK->PCKENR1 |= CLK_PCKENR1_UART2; - - // default, 8bit, no parity, 1 stop bit etc - UART2->CR1 = 0x00; - UART2->CR2 = 0x00; - UART2->CR3 = 0x00; - - // clear the LSB mantissa of UART2DIV - UART2->BRR1 &= (uint8_t)(~UART2_BRR1_DIVM); - // clear the MSB mantissa of UART2DIV - UART2->BRR2 &= (uint8_t)(~UART2_BRR2_DIVM); - // clear the fraction bits of UART2DIV - UART2->BRR2 &= (uint8_t)(~UART2_BRR2_DIVF); - - // set the UART2 baudrate in BRR1 and BRR2 registers according to baudrate value - uint32_t baud_mantissa = ((uint32_t)CPU_FREQ / (baudrate << 4)); - uint32_t baud_mantissa100 = (((uint32_t)CPU_FREQ * 100) / (baudrate << 4)); - - uint8_t BRR2_1 = (uint8_t)((uint8_t)(((baud_mantissa100 - (baud_mantissa * 100)) << 4) / 100) & (uint8_t)0x0F); - uint8_t BRR2_2 = (uint8_t)((baud_mantissa >> 4) & (uint8_t)0xF0); - - UART2->BRR2 = (uint8_t)(BRR2_1 | BRR2_2); - UART2->BRR1 = (uint8_t)baud_mantissa; - - // enable rx and tx - UART2->CR2 |= UART2_CR2_TEN; - UART2->CR2 |= UART2_CR2_REN; - - // clear rx and tx interrupt flags - UART2->SR &= ~UART2_SR_RXNE; - - // enable rx interrupts - UART2->CR2 |= UART2_CR2_RIEN; -} - -void uart_close() -{ - UART2->BRR2 = 0x00; - UART2->BRR1 = 0x00; - - UART2->CR1 = 0x00; - UART2->CR2 = 0x00; - UART2->CR3 = 0x00; -} - -uint8_t uart_available() -{ - return (RX1_BUFFER_SIZE + rx1_head - rx1_tail) & RX1_BUFFER_MASK; -} - -uint8_t uart_read() -{ - uint8_t byte = rx1_buf[rx1_tail]; - rx1_tail = (rx1_tail + 1) & RX1_BUFFER_MASK; - return byte; -} - -void uart_write(uint8_t byte) -{ - if (!tx1_sending) - { - tx1_sending = 1; - UART2->DR = byte; - UART2->CR2 |= UART2_CR2_TIEN; // enable tx done interrupt - - return; - } - - uint8_t i = (tx1_head + 1) & TX1_BUFFER_MASK; - - // wait for free space in buffer - uint8_t prev_tail = tx1_tail; - while (i == tx1_tail) - { - if (tx1_tail != prev_tail) - { - prev_tail = tx1_tail; - watchdog_yeild(); - } - } - - tx1_buf[tx1_head] = byte; - tx1_head = i; -} - -void uart_flush() -{ - while (tx1_sending); -} - - - -void isr_uart2_rx(void) __interrupt(ITC_IRQ_UART2_RX) -{ - if (UART2->SR & UART2_SR_RXNE) - { - uint8_t c = UART2->DR; - uint8_t i = (rx1_head + 1) & RX1_BUFFER_MASK; - - if (i != rx1_tail) - { - rx1_buf[rx1_head] = c; - rx1_head = i; - } - } -} - -void isr_uart2_tx(void) __interrupt(ITC_IRQ_UART2_TX) -{ - if (UART2->SR & UART2_SR_TXE) - { - if (tx1_head != tx1_tail) - { - tx1_sending = 1; - - UART2->DR = tx1_buf[tx1_tail]; - tx1_tail = (tx1_tail + 1) & TX1_BUFFER_MASK; - } - else - { - tx1_sending = 0; - // no more data clear tx empty flag - UART2->CR2 &= ~UART2_CR2_TIEN; - } - } -} diff --git a/src/firmware/tsdz2/watchdog.c b/src/firmware/tsdz2/watchdog.c deleted file mode 100644 index b87a3f93..00000000 --- a/src/firmware/tsdz2/watchdog.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#include "watchdog.h" -#include "tsdz2/cpu.h" -#include "tsdz2/stm8s/stm8s_iwdg.h" - -static bool triggered; - -void watchdog_init() -{ - // :TODO: implement if possible, check if reset triggered by watchdog - triggered = false; - - IWDG->KR = 0xcc; // start - IWDG->KR = 0x55; // unlock - IWDG->PR = 6; // divide by 256 - IWDG->RLR = 156; // reload to 625 milliseconds - - watchdog_yeild(); -} - -void watchdog_yeild() -{ - IWDG->KR = 0xaa; -} - -bool watchdog_triggered() -{ - return triggered; -} diff --git a/src/firmware/util.h b/src/firmware/util.h deleted file mode 100644 index cdd5fe2d..00000000 --- a/src/firmware/util.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#ifndef _UTIL_H_ -#define _UTIL_H_ - -#include - -#define MAP16(x, in_min, in_max, out_min, out_max) ((((int16_t)x) - (in_min)) * ((out_max) - (out_min)) / ((in_max) - (in_min)) + (out_min)) -#define MAP32(x, in_min, in_max, out_min, out_max) ((((int32_t)x) - (in_min)) * ((out_max) - (out_min)) / ((in_max) - (in_min)) + (out_min)) - -#define EXPAND_U16(high, low) ((((uint16_t)high) << 8) | (uint8_t)low) -#define EXPAND_I16(high, low) ((int16_t)EXPAND_U16(high,low)) - -#define ABS(x) (x) < 0 ? -(x) : (x) - -#define MAX(x, y) (x) > (y) ? (x) : (y) -#define MIN(x, y) (x) < (y) ? (x) : (y) - -#define CLAMP(x, min, max) (MIN(MAX(x, min), max)) - -// Low pass filter -// value + (new_value - value) / n; -#define EXPONENTIAL_FILTER(value, new_value, n) (value) + ((new_value) - (value)) / (n) - -#endif - diff --git a/src/firmware/version.h b/src/firmware/version.h deleted file mode 100644 index e7d3a2d0..00000000 --- a/src/firmware/version.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * bbs-fw - * - * Copyright (C) Daniel Nilsson, 2022. - * - * Released under the GPL License, Version 3 - */ - -#ifndef _VERSION_H_ -#define _VERSION_H_ - -#define VERSION_MAJOR 1 -#define VERSION_MINOR 5 -#define VERSION_PATCH 99 - - -#if defined(BBSHD) - #define CTRL_TYPE 1 -#elif defined(BBS02) - #define CTRL_TYPE 2 -#elif defined(TSDZ2) - #define CTRL_TYPE 3 -#else - #define CTRL_TYPE 0 -#endif - -#endif diff --git a/src/logger/include/ComProxy.h b/src/logger/include/ComProxy.h deleted file mode 100644 index a64ed31b..00000000 --- a/src/logger/include/ComProxy.h +++ /dev/null @@ -1,89 +0,0 @@ - -#include -#include - -#define EVT_MSG_MOTOR_INIT_OK 1 -#define EVT_MSG_CONFIG_READ 2 -#define EVT_MSG_CONFIG_RESET 3 -#define EVT_MSG_CONFIG_WRITTEN 4 - -#define EVT_ERROR_INIT_MOTOR 64 -#define EVT_ERROR_CHANGE_TARGET_SPEED 65 -#define EVT_ERROR_CHANGE_TARGET_CURRENT 66 -#define EVT_ERROR_READ_MOTOR_STATUS 67 -#define EVT_ERROR_READ_MOTOR_CURRENT 68 -#define EVT_ERROR_READ_MOTOR_VOLTAGE 69 - -#define EVT_ERROR_CONFIG_READ_EEPROM 70 -#define EVT_ERROR_CONFIG_WRITE_EEPROM 71 -#define EVT_ERROR_CONFIG_ERASE_EEPROM 72 -#define EVT_ERROR_CONFIG_VERSION 73 -#define EVT_ERROR_CONFIG_CHECKSUM 74 -#define EVT_ERROR_THROTTLE_LOW_LIMIT 75 -#define EVT_ERROR_THROTTLE_HIGH_LIMIT 76 - - -#define EVT_DATA_TARGET_CURRENT 128 -#define EVT_DATA_TARGET_SPEED 129 -#define EVT_DATA_MOTOR_STATUS 130 -#define EVT_DATA_ASSIST_LEVEL 131 -#define EVT_DATA_OPERATION_MODE 132 -#define EVT_DATA_WHEEL_SPEED_PPM 133 -#define EVT_DATA_LIGHTS 134 -#define EVT_DATA_TEMPERATURE 135 -#define EVT_DATA_THERMAL_LIMITING 136 -#define EVT_DATA_SPEED_LIMITING 137 -#define EVT_DATA_MAX_CURRENT_ADC_REQUEST 138 -#define EVT_DATA_MAX_CURRENT_ADC_RESPONSE 139 -#define EVT_DATA_MAIN_LOOP_TIME 140 -#define EVT_DATA_THROTTLE_ADC 141 -#define EVT_DATA_LVC_LIMITING 142 -#define EVT_DATA_SHIFT_SENSOR 143 - - -class ComProxy -{ -public: - struct Event - { - uint32_t timestamp; - uint8_t id; - int16_t data; - }; - - static void printFormat(Stream& stream, const Event& e); - - ComProxy(Stream& controller, Stream& display, Stream& log); - - bool connect(); - bool isConnected() const; - - void process(); - - bool hasLogEvent() const; - bool getLogEvent(Event& e); - -private: - void processControllerTx(); - void processDisplayTx(); - - void flushInterceptbuffer(); - bool interceptMessage(); - - int tryProcessControllerMessage(); - int processReadRequestResponse(); - int processWriteRequestResponse(); - int processEventLogMessage(); - -private: - Stream& _log; - Stream& _controller; - Stream& _display; - bool _connected; - uint8_t _msgLen; - uint8_t _msgBuf[128]; - uint32_t _lastRecv; - - bool _hasEvent; - Event _event; -}; diff --git a/src/logger/src/ComProxy.cpp b/src/logger/src/ComProxy.cpp deleted file mode 100644 index 6e07ed17..00000000 --- a/src/logger/src/ComProxy.cpp +++ /dev/null @@ -1,457 +0,0 @@ -#include "ComProxy.h" - -#define KEEP 0 -#define FAIL -1 - - -#define REQUEST_TYPE_READ 0x01 -#define REQUEST_TYPE_WRITE 0x02 - -// Firmware config tool communication (only expected opcodes) -#define OPCODE_READ_FW_VERSION 0x01 -#define OPCODE_READ_EVTLOG_ENABLE 0x02 - -#define OPCODE_WRITE_EVTLOG_ENABLE 0xf0 - -#define EVENT_LOG_ENTRY 0xee -#define EVENT_LOG_DATA_ENTRY 0xed - - -static uint8_t computeChecksum(uint8_t* buf, uint8_t length) -{ - uint8_t result = 0; - for (uint8_t i = 0; i < length; ++i) - { - result += buf[i]; - } - - return result; -} - -int verifyControllerMessage(uint8_t* buf, uint8_t length, uint8_t requiredLength, Stream& log) -{ - if (length < requiredLength) - { - return KEEP; - } - - uint8_t checksum = computeChecksum(buf, requiredLength - 1); - if (checksum == buf[requiredLength - 1]) - { - return requiredLength; - } - /*else - { - log.print("Checksum mismatch, computed="); - log.print(checksum, HEX); - log.print(" message="); - for (uint8_t i = 0; i < requiredLength; i++) - { - log.print(buf[i], HEX); - log.print(" "); - } - log.println(); - }*/ - - return FAIL; -} - - -void ComProxy::printFormat(Stream& stream, const Event& evt) -{ - switch (evt.id) - { - case EVT_MSG_MOTOR_INIT_OK: - stream.print(F("Motor initialization successful.")); - break; - case EVT_MSG_CONFIG_READ: - stream.print(F("Successfully read configuration from eeprom.")); - break; - case EVT_MSG_CONFIG_RESET: - stream.print(F("Configuration reset performed.")); - break; - case EVT_MSG_CONFIG_WRITTEN: - stream.print(F("Configuration written to eeprom.")); - break; - case EVT_ERROR_INIT_MOTOR: - stream.print(F("Failed to perform motor controller initialization.")); - break; - case EVT_ERROR_CHANGE_TARGET_CURRENT: - stream.print(F("Failed to set motor target current on motor controller.")); - break; - case EVT_ERROR_CHANGE_TARGET_SPEED: - stream.print(F("Failed to set motor target speed on motor controller.")); - break; - case EVT_ERROR_READ_MOTOR_STATUS: - stream.print(F("Failed to read status from motor controller.")); - break; - case EVT_ERROR_READ_MOTOR_CURRENT: - stream.print(F("Failed to read current from motor controller.")); - break; - case EVT_ERROR_READ_MOTOR_VOLTAGE: - stream.print(F("Failed to read voltage from motor controller.")); - break; - case EVT_ERROR_CONFIG_READ_EEPROM: - stream.print(F("Failed to read config from eeprom.")); - break; - case EVT_ERROR_CONFIG_WRITE_EEPROM: - stream.print(F("Failed to write config to eeprom.")); - break; - case EVT_ERROR_CONFIG_ERASE_EEPROM: - stream.print(F("Failed to erase eeprom before writing config.")); - break; - case EVT_ERROR_CONFIG_VERSION: - stream.print(F("Configuration read from eeprom is of the wrong version.")); - break; - case EVT_ERROR_CONFIG_CHECKSUM: - stream.print(F("Failed to verify checksum on configuration read from eeprom.")); - break; - case EVT_ERROR_THROTTLE_LOW_LIMIT: - stream.print(F("Invalid throttle reading, below low limit, check throttle.")); - break; - case EVT_ERROR_THROTTLE_HIGH_LIMIT: - stream.print(F("Invalid throttle reading, above high limit, check throttle.")); - break; - - case EVT_DATA_TARGET_CURRENT: - stream.print(F("Motor target current changed to ")); - stream.print(evt.data); - stream.print(F("%")); - break; - case EVT_DATA_TARGET_SPEED: - stream.print(F("Motor target speed changed to ")); - stream.print((evt.data * 100) / 255); - stream.print(F("%.")); - break; - case EVT_DATA_MOTOR_STATUS: - stream.print(F("Motor controller status changed to ")); - stream.print(evt.data, HEX); - stream.print(F(".")); - break; - case EVT_DATA_ASSIST_LEVEL: - stream.print(F("Assist level changed to ")); - stream.print(evt.data); - stream.print(F(".")); - break; - case EVT_DATA_OPERATION_MODE: - stream.print(F("Operation mode changed to ")); - stream.print(evt.data); - stream.print(F(".")); - break; - case EVT_DATA_WHEEL_SPEED_PPM: - stream.print(F("Max wheel speed changed to ")); - stream.print(evt.data); - stream.print(F(" rpm.")); - break; - case EVT_DATA_LIGHTS: - stream.print(F("Lights status changed to ")); - stream.print(evt.data); - stream.print(F(".")); - break; - case EVT_DATA_TEMPERATURE: - stream.print(F("Motor controller temperature changed to ")); - stream.print(evt.data); - stream.print(F("C.")); - break; - case EVT_DATA_THERMAL_LIMITING: - if (evt.data != 0) - { - stream.print(F("Thermal limit reached, power reduced to 50%.")); - } - else - { - stream.print(F("Thermal limiting removed.")); - } - break; - case EVT_DATA_SPEED_LIMITING: - if (evt.data != 0) - { - stream.print(F("Speed limiting activated.")); - } - else - { - stream.print(F("Speed limiting deactivated.")); - } - break; - case EVT_DATA_MAX_CURRENT_ADC_REQUEST: - stream.print(F("Requesting to configure max current on motor controller mcu, adc=")); - stream.print(evt.data); - stream.print("."); - break; - case EVT_DATA_MAX_CURRENT_ADC_RESPONSE: - stream.print(F("Max current configured on motor controller mcu, response was adc=")); - stream.print(evt.data); - stream.print(F(".")); - break; - case EVT_DATA_MAIN_LOOP_TIME: - stream.print(F("Main loop, interval=")); - stream.print(evt.data); - stream.print(F("ms.")); - break; - case EVT_DATA_THROTTLE_ADC: - stream.print(F("Throttle adc, value=")); - stream.print(evt.data); - stream.print(F(".")); - break; - case EVT_DATA_LVC_LIMITING: - if (evt.data != 0) - { - stream.print(F("Low voltage limiting activated, voltage=")); - stream.print(evt.data / 10.f); - stream.print(F(".")); - } - else - { - stream.print("Low voltage limiting deactivated."); - } - break; - case EVT_DATA_SHIFT_SENSOR: - if (evt.data.Value != 0) - { - stream.print("Shift sensor power ramp started."); - } - else - { - stream.print("Shift sensor power ramp ended."); - } - break; - default: - stream.print(F("Unknown entry, id=")); - stream.print(evt.id); - stream.print(F(" data=")); - stream.print(evt.data); - break; - } -} - - - -ComProxy::ComProxy(Stream& controller, Stream& display, Stream& log) - : _log(log) - , _controller(controller) - , _display(display) - , _connected(false) - , _msgLen(0) - , _lastRecv(0) - , _hasEvent(false) -{ } - -bool ComProxy::isConnected() const -{ - return _connected; -} - -bool ComProxy::connect() -{ - uint8_t buffer[4]; - - buffer[0] = REQUEST_TYPE_WRITE; - buffer[1] = OPCODE_WRITE_EVTLOG_ENABLE; - buffer[2] = 1; - buffer[3] = computeChecksum(buffer, 3); - - _controller.write(buffer, 4); - - uint32_t now = millis(); - while(!_connected && (millis() - now) < 1000) - { - processControllerTx(); - } - - return _connected; -} - -void ComProxy::process() -{ - processControllerTx(); - processDisplayTx(); -} - -bool ComProxy::hasLogEvent() const -{ - return _hasEvent; -} - -bool ComProxy::getLogEvent(Event& e) -{ - if (_hasEvent) - { - e = _event; - _hasEvent = false; - return true; - } - - return false; -} - - -void ComProxy::processControllerTx() -{ - int b = -1; - while ((b = _controller.read()) != -1) - { - _lastRecv = millis(); - - _msgBuf[_msgLen++] = b; - - int res; - while (_msgLen > 0 && (res = tryProcessControllerMessage()) != KEEP) - { - if (res == FAIL) - { - _display.write(_msgBuf[0]); - if (_msgLen > 1) - { - memcpy(_msgBuf, _msgBuf + 1, _msgLen - 1); - } - _msgLen--; - continue; - } - else if (res >= 0) - { - // succesfully intercepted - _msgLen = 0; - break; - } - } - } - - if (_msgLen > 0 && millis() - _lastRecv > 20) - { - flushInterceptbuffer(); - } -} - -void ComProxy::processDisplayTx() -{ - int b = -1; - while ((b = _display.read()) != -1) - { - _controller.write((uint8_t)b); - } -} - - -void ComProxy::flushInterceptbuffer() -{ - for (uint8_t i = 0; i < _msgLen; i++) - { - _display.write(_msgBuf[i]); - } - - _msgLen = 0; -} - - -int ComProxy::tryProcessControllerMessage() - { - if (_msgLen < 1) - { - return KEEP; - } - - switch (_msgBuf[0]) - { - case REQUEST_TYPE_READ: - return processReadRequestResponse(); - case REQUEST_TYPE_WRITE: - return processWriteRequestResponse(); - case EVENT_LOG_ENTRY: - case EVENT_LOG_DATA_ENTRY: - return processEventLogMessage(); - } - - return FAIL; // unknown message, forward to display -} - -int ComProxy::processReadRequestResponse() -{ - if (_msgLen < 2) - { - return KEEP; - } - - switch (_msgBuf[1]) - { - case OPCODE_READ_FW_VERSION: - return verifyControllerMessage(_msgBuf, _msgLen, 7, _log); - case OPCODE_READ_EVTLOG_ENABLE: - return verifyControllerMessage(_msgBuf, _msgLen, 4, _log); - } - - return FAIL; -} - -int ComProxy::processWriteRequestResponse() -{ - if (_msgLen < 2) - { - return KEEP; - } - - switch(_msgBuf[1]) - { - case OPCODE_WRITE_EVTLOG_ENABLE: - { - if (_msgLen < 4) - { - return KEEP; - } - - int res = verifyControllerMessage(_msgBuf, _msgLen, 4, _log); - if (res > 0) - { - _connected = _msgBuf[2] != 0; - } - - return res; - }}; - - return FAIL; -} - -int ComProxy::processEventLogMessage() -{ - if (_msgBuf[0] == EVENT_LOG_ENTRY) - { - const int MessageSize = 3; - - if (_msgLen < MessageSize) - { - return KEEP; - } - - int res = verifyControllerMessage(_msgBuf, _msgLen, MessageSize, _log); - if (res > 0) - { - _hasEvent = true; - _event.timestamp = millis(); - _event.id = _msgBuf[1]; - _event.data = 0; - } - - return res; - } - else if (_msgBuf[0] == EVENT_LOG_DATA_ENTRY) - { - const int MessageSize = 5; - - if (_msgLen < MessageSize) - { - return KEEP; - } - - int res = verifyControllerMessage(_msgBuf, _msgLen, MessageSize, _log); - if (res > 0) - { - _hasEvent = true; - _event.timestamp = millis(); - _event.id = _msgBuf[1]; - _event.data = _msgBuf[2] << 8 | _msgBuf[3]; - } - - return res; - } - - return FAIL; -} diff --git a/src/tool/AssemblyInfo.cs b/src/tool/AssemblyInfo.cs deleted file mode 100644 index 427f2025..00000000 --- a/src/tool/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Windows; - -[assembly: ThemeInfo( - ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located - //(used if a resource is not found in the page, - // or application resource dictionaries) - ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located - //(used if a resource is not found in the page, - // app, or any theme specific resource dictionaries) -)] diff --git a/src/tool/Model/BbsfwConnection.cs b/src/tool/Model/BbsfwConnection.cs deleted file mode 100644 index 87834c39..00000000 --- a/src/tool/Model/BbsfwConnection.cs +++ /dev/null @@ -1,652 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO.Ports; -using System.Linq; -using System.Management; -using System.Threading; -using System.Threading.Tasks; - -namespace BBSFW.Model -{ - - public class ComPort - { - public string Name { get; private set; } - - public string Description { get; private set; } - - public ComPort(string name, string description) - { - Name = name; - Description = description; - } - } - - public class BbsfwConnection - { - public enum Controller - { - Unknown = 0, - BBSHD = 1, - BBS02 = 2, - TSDZ2 = 3 - } - - private const int REQUEST_TYPE_READ = 0x01; - private const int REQUEST_TYPE_WRITE = 0x02; - - private const int RESPONSE_TYPE_READ = 0x01; - private const int RESPONSE_TYPE_WRITE = 0x02; - - private const int EVENT_LOG_ENTRY = 0xee; - private const int EVENT_LOG_DATA_ENTRY = 0xed; - - private const int OPCODE_READ_FW_VERSION = 0x01; - private const int OPCODE_READ_EVTLOG_ENABLE = 0x02; - private const int OPCODE_READ_CONFIG = 0x03; - - private const int OPCODE_WRITE_EVTLOG_ENABLE = 0xf0; - private const int OPCODE_WRITE_CONFIG = 0xf1; - private const int OPCODE_WRITE_RESET_CONFIG = 0xf2; - private const int OPCODE_WRITE_ADC_VOLTAGE_CALIBRATION = 0xf3; - - private const int Keep = 0; - private const int Discard = -1; - - private SerialPort _port = null; - private volatile bool _isConnecting = false; - private volatile bool _isConnected = false; - private Controller _controllerType = Controller.Unknown; - - private DateTime _lastRecv = DateTime.Now; - private List _rxBuffer = new List(); - - - private CompletionQueue _readConfigCq = new CompletionQueue(); - private CompletionQueue _writeConfigCq = new CompletionQueue(); - private CompletionQueue _writeResetConfigCq = new CompletionQueue(); - private CompletionQueue _writeVoltageCalibrationCq = new CompletionQueue(); - - - private int ConfigVersion = 0; - - - - public bool IsConnected - { - get - { - return _isConnected; - } - } - - public Controller ControllerType - { - get - { - return _controllerType; - } - } - - public event Action Connected; - public event Action Disconnected; - - - public event Action EventLog; - - - public static List GetComPorts() - { - var result = new List(); - - using (var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%COM%'")) - { - var portNames = SerialPort.GetPortNames(); - var ports = searcher.Get().Cast().ToList(); - - foreach (var name in portNames) - { - var port = ports.FirstOrDefault(p => p["Name"].ToString().ToUpper().Contains(name.ToUpper())); - if (port != null) - { - result.Add(new ComPort(name, port["Caption"].ToString())); - } - else - { - result.Add(new ComPort(name, name)); - } - } - } - - return result; - } - - - - public async Task Connect(ComPort port, TimeSpan timeout) - { - _controllerType = Controller.Unknown; - _isConnected = false; - _isConnecting = true; - _port = new SerialPort(port.Name, 1200); - _port.DataReceived += OnDataReceived; - _port.Open(); - - var connected = await Task.Run(() => SetupConnection(timeout)); - if (!connected) - { - Close(); - } - - return connected; - } - - public void Close() - { - if (_port != null) - { - _isConnected = false; - _isConnecting = false; - - _port.Close(); - _port.DataReceived -= OnDataReceived; - _port = null; - - lock (_rxBuffer) - { - _rxBuffer.Clear(); - } - - Disconnected?.Invoke(); - } - } - - - public async Task> ReadConfiguration(TimeSpan timeout) - { - SendReadRequest(OPCODE_READ_CONFIG); - return await _readConfigCq.WaitResponse(timeout); - } - - public async Task> WriteConfiguration(Configuration configuration, TimeSpan timeout) - { - SendWriteConfigRequest(configuration); - return await _writeConfigCq.WaitResponse(timeout); - } - - public async Task> ResetConfiguration(TimeSpan timeout) - { - SendWriteResetConfigRequest(); - return await _writeResetConfigCq.WaitResponse(timeout); - } - - public async Task> CalibrateBatteryVoltage(float actualVolts, TimeSpan timeout) - { - SendWriteVoltageCalibration(actualVolts); - return await _writeVoltageCalibrationCq.WaitResponse(timeout); - } - - - private void OnDataReceived(object sender, SerialDataReceivedEventArgs e) - { - // check for communication error and reset - if (_rxBuffer.Any() && DateTime.Now - _lastRecv > TimeSpan.FromMilliseconds(1000)) - { - _rxBuffer.Clear(); - } - - bool close = false; - lock(_rxBuffer) - { - while (_port.BytesToRead > 0) - { - _lastRecv = DateTime.Now; - - var b = _port.ReadByte(); - if (b == -1) - { - close = true; - break; - } - else - { - _rxBuffer.Add((byte)b); - } - } - } - - if (close) - { - Close(); - } - else - { - ProcessInputBuffer(); - } - } - - - private void ProcessInputBuffer() - { - lock(_rxBuffer) - { - while(true) - { - var result = ProcessMessage(); - if (result == Discard) - { - System.Diagnostics.Debug.WriteLine("Discarding: " + BitConverter.ToString(_rxBuffer.ToArray()).Replace("-", " ")); - _rxBuffer.Clear(); - } - else if (result > 0) - { - if (_rxBuffer.Count > result) - { - _rxBuffer.RemoveRange(0, result); - } - else - { - _rxBuffer.Clear(); - } - } - else - { - // no data, done - break; - } - } - - } - } - - - private int ProcessMessage() - { - if (_rxBuffer.Count < 1) - { - return 0; - } - - switch(_rxBuffer[0]) - { - case RESPONSE_TYPE_READ: - return ProcessReadResponse(); - case RESPONSE_TYPE_WRITE: - return ProcessWriteResponse(); - case EVENT_LOG_ENTRY: - case EVENT_LOG_DATA_ENTRY: - return ProcessEventLogEntry(); - } - - return Discard; - } - - - private int ProcessReadResponse() - { - if (_rxBuffer.Count < 2) - { - return 0; - } - - switch(_rxBuffer[1]) - { - case OPCODE_READ_FW_VERSION: - return ProcessReadResponseFwVersion(); - case OPCODE_READ_EVTLOG_ENABLE: - return ProcessReadResponseEvtlogEnable(); - case OPCODE_READ_CONFIG: - return ProcessReadResponseConfig(); - } - - return -1; - } - - private int ProcessReadResponseFwVersion() - { - const int MessageSizeV1 = 7; - const int MessageSizeV2 = 8; - - if (_rxBuffer.Count < MessageSizeV1) - { - return Keep; - } - - int size = MessageSizeV1; - - int major = _rxBuffer[2]; - int minor = _rxBuffer[3]; - int patch = _rxBuffer[4]; - - if (major > 1 || minor > 0) - { - // Controller model field added in firmware version 1.1 - // Keep backwards compatibility - if (_rxBuffer.Count < MessageSizeV2) - { - return Keep; - } - - size = MessageSizeV2; - } - - if (ComputeChecksum(_rxBuffer, size - 1) == _rxBuffer[size - 1]) - { - ConfigVersion = _rxBuffer[5]; - - if (_isConnecting) - { - _isConnecting = false; - _isConnected = true; - _controllerType = (size == MessageSizeV1 ? Controller.BBSHD : (Controller)_rxBuffer[6]); - - Connected?.Invoke(ControllerType, String.Format("{0}.{1}.{2}", major, minor, patch), ConfigVersion); ; - - SendEventLogEnableRequest(true); - } - } - - return size; - } - - private int ProcessReadResponseEvtlogEnable() - { - const int MessageSize = 4; - - if (_rxBuffer.Count < MessageSize) - { - return Keep; - } - - // not used - - return 4; - } - - private int ProcessReadResponseConfig() - { - int version; - - if (_rxBuffer.Count > 3) - { - version = _rxBuffer[2]; - var size = _rxBuffer[3]; - - if (version < Configuration.MinVersion || version > Configuration.MaxVersion || size != Configuration.GetByteSize(version)) - { - System.Diagnostics.Debug.WriteLine("Config read from flash is of an unsupported version or is corrupt, discarding."); - return Discard; - } - } - else - { - return Keep; - } - - int MessageSize = (4 + Configuration.GetByteSize(version) + 1); - - if (_rxBuffer.Count < MessageSize) - { - return Keep; - } - - if (ComputeChecksum(_rxBuffer, MessageSize - 1) == _rxBuffer[MessageSize - 1]) - { - var cfg = new Configuration(ControllerType); - - switch(version) - { - case 1: - cfg.ParseFromBufferV1(_rxBuffer.Skip(4).Take(Configuration.GetByteSize(version)).ToArray()); - break; - case 2: - cfg.ParseFromBufferV2(_rxBuffer.Skip(4).Take(Configuration.GetByteSize(version)).ToArray()); - break; - case 3: - cfg.ParseFromBufferV3(_rxBuffer.Skip(4).Take(Configuration.GetByteSize(version)).ToArray()); - break; - case 4: - cfg.ParseFromBufferV4(_rxBuffer.Skip(4).Take(Configuration.GetByteSize(version)).ToArray()); - break; - case 5: - cfg.ParseFromBufferV5(_rxBuffer.Skip(4).Take(Configuration.GetByteSize(version)).ToArray()); - break; - } - - _readConfigCq.Complete(cfg); - } - else - { - System.Diagnostics.Debug.WriteLine("Config read from flash has mismatching checksum, discarding."); - } - - return MessageSize; - } - - - private int ProcessWriteResponse() - { - if (_rxBuffer.Count < 2) - { - return Keep; - } - - switch (_rxBuffer[1]) - { - case OPCODE_WRITE_EVTLOG_ENABLE: - return ProcessWriteResponseEvtlogEnable(); - case OPCODE_WRITE_CONFIG: - return ProcessWriteResponseConfig(); - case OPCODE_WRITE_RESET_CONFIG: - return ProcessWriteResponseResetConfig(); - case OPCODE_WRITE_ADC_VOLTAGE_CALIBRATION: - return ProcessWriteResponseVoltageCalibration(); - } - - return Discard; - } - - private int ProcessWriteResponseEvtlogEnable() - { - const int MessageSize = 4; - - if (_rxBuffer.Count < MessageSize) - { - return Keep; - } - - // don't care - - return MessageSize; - } - - private int ProcessWriteResponseConfig() - { - const int MessageSize = 4; - - if (_rxBuffer.Count < MessageSize) - { - return Keep; - } - - _writeConfigCq.Complete(_rxBuffer[2] != 0); - - return MessageSize; - } - - private int ProcessWriteResponseResetConfig() - { - const int MessageSize = 4; - - if (_rxBuffer.Count < MessageSize) - { - return Keep; - } - - _writeResetConfigCq.Complete(_rxBuffer[2] != 0); - - return MessageSize; - } - - private int ProcessWriteResponseVoltageCalibration() - { - const int MessageSize = 5; - - if (_rxBuffer.Count < MessageSize) - { - return Keep; - } - - _writeVoltageCalibrationCq.Complete(true); - - return MessageSize; - } - - private int ProcessEventLogEntry() - { - if (_rxBuffer[0] == EVENT_LOG_ENTRY) - { - const int MessageSize = 3; - - if (_rxBuffer.Count < MessageSize) - { - return Keep; - } - - if (ComputeChecksum(_rxBuffer, MessageSize - 1) == _rxBuffer[MessageSize - 1]) - { - EventLog?.Invoke(new EventLogEntry(_rxBuffer[1], null)); - return MessageSize; - } - else - { - Console.WriteLine("Event log cheksum missmatch. Discarding."); - return Discard; - } - } - else if (_rxBuffer[0] == EVENT_LOG_DATA_ENTRY) - { - const int MessageSize = 5; - - if (_rxBuffer.Count < MessageSize) - { - return Keep; - } - - if (ComputeChecksum(_rxBuffer, MessageSize - 1) == _rxBuffer[MessageSize - 1]) - { - int data = _rxBuffer[2] << 8 | _rxBuffer[3]; - EventLog?.Invoke(new EventLogEntry(_rxBuffer[1], data)); - - return MessageSize; - } - else - { - Console.WriteLine("Event log cheksum missmatch. Discarding."); - return Discard; - } - } - - return Discard; - } - - - private void SendReadRequest(byte opcode) - { - var buf = new List(); - buf.Add(REQUEST_TYPE_READ); - buf.Add(opcode); - buf.Add(ComputeChecksum(buf, buf.Count)); - - _port.Write(buf.ToArray(), 0, buf.Count); - } - - private void SendEventLogEnableRequest(bool enable) - { - var buf = new List(); - buf.Add(REQUEST_TYPE_WRITE); - buf.Add(OPCODE_WRITE_EVTLOG_ENABLE); - buf.Add((byte)(enable ? 1 : 0)); - buf.Add(ComputeChecksum(buf, buf.Count)); - - _port.Write(buf.ToArray(), 0, buf.Count); - } - - private void SendWriteConfigRequest(Configuration config) - { - if (Configuration.CurrentVersion != ConfigVersion) - { - throw new InvalidOperationException("Unsupported config version."); - } - - var cfgarr = config.WriteToBuffer(); - - var buf = new List(); - buf.Add(REQUEST_TYPE_WRITE); - buf.Add(OPCODE_WRITE_CONFIG); - buf.Add((byte)Configuration.CurrentVersion); - buf.Add((byte)cfgarr.Length); - buf.AddRange(cfgarr); - buf.Add(ComputeChecksum(buf, buf.Count)); - - _port.Write(buf.ToArray(), 0, buf.Count); - } - - private void SendWriteResetConfigRequest() - { - var buf = new List(); - buf.Add(REQUEST_TYPE_WRITE); - buf.Add(OPCODE_WRITE_RESET_CONFIG); - buf.Add(ComputeChecksum(buf, buf.Count)); - - _port.Write(buf.ToArray(), 0, buf.Count); - } - - private void SendWriteVoltageCalibration(float volts) - { - uint volts_x100 = (uint)(volts * 100); - - var buf = new List(); - buf.Add(REQUEST_TYPE_WRITE); - buf.Add(OPCODE_WRITE_ADC_VOLTAGE_CALIBRATION); - buf.Add((byte)(volts_x100 >> 8)); - buf.Add((byte)volts_x100); - buf.Add(ComputeChecksum(buf, buf.Count)); - - _port.Write(buf.ToArray(), 0, buf.Count); - } - - private bool SetupConnection(TimeSpan timeout) - { - var start = DateTime.Now; - while (_isConnecting && !_isConnected) - { - if (DateTime.Now - start > timeout) - { - return false; - } - - SendReadRequest(OPCODE_READ_FW_VERSION); - Thread.Sleep(200); - } - - return true; - } - - - private static byte ComputeChecksum(List buffer, int length) - { - unchecked - { - byte result = 0; - for (int i = 0; i < length; i++) - { - result += buffer[i]; - } - - return result; - } - } - - } -} diff --git a/src/tool/Model/CompletionQueue.cs b/src/tool/Model/CompletionQueue.cs deleted file mode 100644 index 730eb8a9..00000000 --- a/src/tool/Model/CompletionQueue.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace BBSFW.Model -{ - - public class RequestResult - { - public bool Timeout { get; private set; } - - public T Result { get; private set; } - - - public RequestResult(bool timeout, T result) - { - Timeout = timeout; - Result = result; - } - } - - - public class CompletionQueue - { - private TaskCompletionSource _tcs = null; - - public void Complete(T response) - { - _tcs?.SetResult(response); - } - - public async Task> WaitResponse(TimeSpan timeout) - { - Reset(timeout); - - try - { - var res = await _tcs.Task; - _tcs = null; - return new RequestResult(false, res); - } - catch(TaskCanceledException) - { - _tcs = null; - return new RequestResult(true, default(T)); - } - } - - private void Reset(TimeSpan timeout) - { - var tcs = new TaskCompletionSource(); - - var cancelTokenSrc = new CancellationTokenSource((int)timeout.TotalMilliseconds); - cancelTokenSrc.Token.Register(() => tcs.TrySetCanceled()); - - _tcs = tcs; - } - - } -} diff --git a/src/tool/Model/Configuration.cs b/src/tool/Model/Configuration.cs deleted file mode 100644 index b8b74d2a..00000000 --- a/src/tool/Model/Configuration.cs +++ /dev/null @@ -1,859 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Text; -using System.Xml; -using System.Xml.Serialization; - -namespace BBSFW.Model -{ - - [XmlRoot("BBSFW", Namespace ="https://github.com/danielnilsson9/bbs-fw")] - public class Configuration - { - public const int CurrentVersion = 5; - public const int MinVersion = 1; - public const int MaxVersion = CurrentVersion; - - public const int ByteSizeV1 = 120; - public const int ByteSizeV2 = 124; - public const int ByteSizeV3 = 149; - public const int ByteSizeV4 = 152; - public const int ByteSizeV5 = 154; - - public enum Feature - { - ShiftSensor, - TorqueSensor, - ControllerTemperatureSensor, - MotorTemperatureSensor - } - - public static int GetByteSize(int version) - { - switch (version) - { - case 1: - return ByteSizeV1; - case 2: - return ByteSizeV2; - case 3: - return ByteSizeV3; - case 4: - return ByteSizeV4; - case 5: - return ByteSizeV5; - } - - return 0; - } - - public enum AssistModeSelect - { - Off = 0, - Standard = 1, - Lights = 2, - Pas0AndLights = 3, - Pas1AndLights = 4, - Pas2AndLights = 5, - Pas3AndLights = 6, - Pas4AndLights = 7, - Pas5AndLights = 8, - Pas6AndLights = 9, - Pas7AndLights = 10, - Pas8AndLights = 11, - Pas9AndLights = 12, - BrakesOnBoot = 13 - } - - [Flags] - public enum AssistFlagsType : byte - { - None = 0x00, - Pas = 0x01, - Throttle = 0x02, - Cruise = 0x04, - - PasVariable = 0x08, - PasTorque = 0x10, - CadenceOverride = 0x20, - SpeedOverride = 0x40 - }; - - public enum ThrottleGlobalSpeedLimitOptions - { - Disabled = 0x00, - Enabled = 0x01, - StandardLevels = 0x02 - } - - public enum TemperatureSensor - { - Disabled = 0x00, - Controller = 0x01, - Motor = 0x02, - All = 0x03 - } - - public enum WalkModeData - { - Speed = 0, - Temperature = 1, - RequestedPower = 2, - BatteryPercent = 3 - } - - public enum LightsModeOptions - { - Default = 0, - Disabled = 1, - AlwaysOn = 2, - BrakeLight = 3 - } - - public class AssistLevel - { - [XmlAttribute] - public AssistFlagsType Type; - - [XmlAttribute] - public uint MaxCurrentPercent; - - [XmlAttribute] - public uint MaxThrottlePercent; - - [XmlAttribute] - public uint MaxCadencePercent; - - [XmlAttribute] - public uint MaxSpeedPercent; - - [XmlAttribute] - public float TorqueAmplificationFactor; - } - - [XmlIgnore] - public BbsfwConnection.Controller Target { get; private set; } - - public uint MaxCurrentLimitAmps - { - get - { - switch (Target) - { - case BbsfwConnection.Controller.BBSHD: - return 33; - case BbsfwConnection.Controller.BBS02: - return 30; - case BbsfwConnection.Controller.TSDZ2: - return 20; - } - - return 50; - } - } - - // hmi - [XmlIgnore] - public bool UseFreedomUnits; - - // global - public uint MaxCurrentAmps; - public uint CurrentRampAmpsSecond; - public float MaxBatteryVolts; - public uint LowCutoffVolts; - public uint MaxSpeedKph; - - // externals - public bool UseSpeedSensor; - public bool UseShiftSensor; - public bool UsePushWalk; - public bool UsePretension; - public uint PretensionSpeedCutoffKph; - public TemperatureSensor UseTemperatureSensor; - - // lights - public LightsModeOptions LightsMode; - - // speed sensor - public float WheelSizeInch; - public uint NumWheelSensorSignals; - - // pas options - public uint PasStartDelayPulses; - public uint PasStopDelayMilliseconds; - public uint PasKeepCurrentPercent; - public uint PasKeepCurrentCadenceRpm; - - // throttle options - public uint ThrottleStartMillivolts; - public uint ThrottleEndMillivolts; - public uint ThrottleStartPercent; - public ThrottleGlobalSpeedLimitOptions ThrottleGlobalSpeedLimit; - public uint ThrottleGlobalSpeedLimitPercent; - - // shift interrupt options - public uint ShiftInterruptDuration; - public uint ShiftInterruptCurrentThresholdPercent; - - // misc - public WalkModeData WalkModeDataDisplay; - - // assists options - public AssistModeSelect AssistModeSelection; - public uint AssistStartupLevel; - - public AssistLevel[] StandardAssistLevels = new AssistLevel[10]; - public AssistLevel[] SportAssistLevels = new AssistLevel[10]; - - public Configuration() : this(BbsfwConnection.Controller.Unknown) - { } - - public Configuration(BbsfwConnection.Controller target) - { - Target = target; - - UseFreedomUnits = Properties.Settings.Default.UseFreedomUnits; - MaxCurrentAmps = 0; - CurrentRampAmpsSecond = 0; - MaxBatteryVolts = 0; - LowCutoffVolts = 0; - - UseSpeedSensor = false; - UseShiftSensor = false; - UsePushWalk = false; - UsePretension = false; - PretensionSpeedCutoffKph = 0; - UseTemperatureSensor = TemperatureSensor.All; - - LightsMode = LightsModeOptions.Default; - - WheelSizeInch = 0; - NumWheelSensorSignals = 0; - MaxSpeedKph = 0; - - PasStartDelayPulses = 0; - PasStopDelayMilliseconds = 0; - PasKeepCurrentPercent = 0; - PasKeepCurrentCadenceRpm = 0; - - ThrottleStartMillivolts = 0; - ThrottleEndMillivolts = 0; - ThrottleStartPercent = 0; - ThrottleGlobalSpeedLimit = ThrottleGlobalSpeedLimitOptions.Disabled; - ThrottleGlobalSpeedLimitPercent = 0; - - ShiftInterruptDuration = 0; - ShiftInterruptCurrentThresholdPercent = 0; - - WalkModeDataDisplay = WalkModeData.Speed; - - AssistModeSelection = AssistModeSelect.Off; - AssistStartupLevel = 0; - - for (int i = 0; i < StandardAssistLevels.Length; ++i) - { - StandardAssistLevels[i] = new AssistLevel(); - } - - for (int i = 0; i < SportAssistLevels.Length; ++i) - { - SportAssistLevels[i] = new AssistLevel(); - } - } - - public bool IsFeatureSupported(Feature feature) - { - if (Target == BbsfwConnection.Controller.Unknown) - { - return true; - } - - switch (feature) - { - case Feature.ShiftSensor: - return new[] { BbsfwConnection.Controller.BBSHD, BbsfwConnection.Controller.BBS02 }.Contains(Target); - case Feature.TorqueSensor: - return new[] { BbsfwConnection.Controller.TSDZ2 }.Contains(Target); - case Feature.ControllerTemperatureSensor: - return new[] { BbsfwConnection.Controller.BBSHD, BbsfwConnection.Controller.BBS02 }.Contains(Target); - case Feature.MotorTemperatureSensor: - return new[] { BbsfwConnection.Controller.BBSHD }.Contains(Target); - } - - return false; - } - - public bool ParseFromBufferV1(byte[] buffer) - { - if (buffer.Length != ByteSizeV1) - { - return false; - } - - using (var s = new MemoryStream(buffer)) - { - var br = new BinaryReader(s); - - UseFreedomUnits = br.ReadBoolean(); - - MaxCurrentAmps = br.ReadByte(); - CurrentRampAmpsSecond = br.ReadByte(); - LowCutoffVolts = br.ReadByte(); - MaxSpeedKph = br.ReadByte(); - - UseSpeedSensor = br.ReadBoolean(); - /* UseDisplay = */ br.ReadBoolean(); - UsePushWalk = br.ReadBoolean(); - - WheelSizeInch = br.ReadUInt16() / 10f; - NumWheelSensorSignals = br.ReadByte(); - - PasStartDelayPulses = br.ReadByte(); - PasStopDelayMilliseconds = br.ReadByte() * 10u; - - ThrottleStartMillivolts = br.ReadUInt16(); - ThrottleEndMillivolts = br.ReadUInt16(); - ThrottleStartPercent = br.ReadByte(); - - AssistModeSelection = (AssistModeSelect)br.ReadByte(); - AssistStartupLevel = br.ReadByte(); - - for (int i = 0; i < StandardAssistLevels.Length; ++i) - { - StandardAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); - StandardAssistLevels[i].MaxCurrentPercent = br.ReadByte(); - StandardAssistLevels[i].MaxThrottlePercent = br.ReadByte(); - StandardAssistLevels[i].MaxCadencePercent = br.ReadByte(); - StandardAssistLevels[i].MaxSpeedPercent = br.ReadByte(); - } - - for (int i = 0; i < SportAssistLevels.Length; ++i) - { - SportAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); - SportAssistLevels[i].MaxCurrentPercent = br.ReadByte(); - SportAssistLevels[i].MaxThrottlePercent = br.ReadByte(); - SportAssistLevels[i].MaxCadencePercent = br.ReadByte(); - SportAssistLevels[i].MaxSpeedPercent = br.ReadByte(); - } - } - - // apply default settings for non existing options in version - MaxBatteryVolts = 0f; - UseTemperatureSensor = TemperatureSensor.All; - WalkModeDataDisplay = WalkModeData.Speed; - PasKeepCurrentPercent = 100; - PasKeepCurrentCadenceRpm = 255; - UseShiftSensor = true; - ShiftInterruptDuration = 600; - ShiftInterruptCurrentThresholdPercent = 10; - LightsMode = LightsModeOptions.Default; - UsePretension = false; - PretensionSpeedCutoffKph = 16; - ThrottleGlobalSpeedLimit = ThrottleGlobalSpeedLimitOptions.Disabled; - ThrottleGlobalSpeedLimitPercent = 100; - UsePretension = false; - PretensionSpeedCutoffKph = 0; - - return true; - } - - public bool ParseFromBufferV2(byte[] buffer) - { - if (buffer.Length != ByteSizeV2) - { - return false; - } - - using (var s = new MemoryStream(buffer)) - { - var br = new BinaryReader(s); - - UseFreedomUnits = br.ReadBoolean(); - - MaxCurrentAmps = br.ReadByte(); - CurrentRampAmpsSecond = br.ReadByte(); - MaxBatteryVolts = br.ReadUInt16() / 100f; - LowCutoffVolts = br.ReadByte(); - MaxSpeedKph = br.ReadByte(); - - UseSpeedSensor = br.ReadBoolean(); - /* UseDisplay = */ br.ReadBoolean(); - UsePushWalk = br.ReadBoolean(); - UseTemperatureSensor = (TemperatureSensor)br.ReadByte(); - - WheelSizeInch = br.ReadUInt16() / 10f; - NumWheelSensorSignals = br.ReadByte(); - - PasStartDelayPulses = br.ReadByte(); - PasStopDelayMilliseconds = br.ReadByte() * 10u; - PasKeepCurrentCadenceRpm = 255; - PasKeepCurrentPercent = 100; - - ThrottleStartMillivolts = br.ReadUInt16(); - ThrottleEndMillivolts = br.ReadUInt16(); - ThrottleStartPercent = br.ReadByte(); - - WalkModeDataDisplay = (WalkModeData)br.ReadByte(); - - AssistModeSelection = (AssistModeSelect)br.ReadByte(); - AssistStartupLevel = br.ReadByte(); - - for (int i = 0; i < StandardAssistLevels.Length; ++i) - { - StandardAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); - StandardAssistLevels[i].MaxCurrentPercent = br.ReadByte(); - StandardAssistLevels[i].MaxThrottlePercent = br.ReadByte(); - StandardAssistLevels[i].MaxCadencePercent = br.ReadByte(); - StandardAssistLevels[i].MaxSpeedPercent = br.ReadByte(); - } - - for (int i = 0; i < SportAssistLevels.Length; ++i) - { - SportAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); - SportAssistLevels[i].MaxCurrentPercent = br.ReadByte(); - SportAssistLevels[i].MaxThrottlePercent = br.ReadByte(); - SportAssistLevels[i].MaxCadencePercent = br.ReadByte(); - SportAssistLevels[i].MaxSpeedPercent = br.ReadByte(); - } - } - - // apply default settings for non existing options in version - PasKeepCurrentPercent = 100; - PasKeepCurrentCadenceRpm = 255; - UseShiftSensor = true; - ShiftInterruptDuration = 600; - ShiftInterruptCurrentThresholdPercent = 10; - LightsMode = LightsModeOptions.Default; - UsePretension = false; - PretensionSpeedCutoffKph = 16; - ThrottleGlobalSpeedLimit = ThrottleGlobalSpeedLimitOptions.Disabled; - ThrottleGlobalSpeedLimitPercent = 100; - UsePretension = false; - PretensionSpeedCutoffKph = 0; - - return true; - } - - public bool ParseFromBufferV3(byte[] buffer) - { - if (buffer.Length != ByteSizeV3) - { - return false; - } - - using (var s = new MemoryStream(buffer)) - { - var br = new BinaryReader(s); - - UseFreedomUnits = br.ReadBoolean(); - - MaxCurrentAmps = br.ReadByte(); - CurrentRampAmpsSecond = br.ReadByte(); - MaxBatteryVolts = br.ReadUInt16() / 100f; - LowCutoffVolts = br.ReadByte(); - MaxSpeedKph = br.ReadByte(); - - UseSpeedSensor = br.ReadBoolean(); - UseShiftSensor = br.ReadBoolean(); - UsePushWalk = br.ReadBoolean(); - UseTemperatureSensor = (TemperatureSensor)br.ReadByte(); - - WheelSizeInch = br.ReadUInt16() / 10f; - NumWheelSensorSignals = br.ReadByte(); - - PasStartDelayPulses = br.ReadByte(); - PasStopDelayMilliseconds = br.ReadByte() * 10u; - PasKeepCurrentPercent = br.ReadByte(); - PasKeepCurrentCadenceRpm = br.ReadByte(); - - ThrottleStartMillivolts = br.ReadUInt16(); - ThrottleEndMillivolts = br.ReadUInt16(); - ThrottleStartPercent = br.ReadByte(); - - ShiftInterruptDuration = br.ReadUInt16(); - ShiftInterruptCurrentThresholdPercent = br.ReadByte(); - - WalkModeDataDisplay = (WalkModeData)br.ReadByte(); - - AssistModeSelection = (AssistModeSelect)br.ReadByte(); - AssistStartupLevel = br.ReadByte(); - - for (int i = 0; i < StandardAssistLevels.Length; ++i) - { - StandardAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); - StandardAssistLevels[i].MaxCurrentPercent = br.ReadByte(); - StandardAssistLevels[i].MaxThrottlePercent = br.ReadByte(); - StandardAssistLevels[i].MaxCadencePercent = br.ReadByte(); - StandardAssistLevels[i].MaxSpeedPercent = br.ReadByte(); - StandardAssistLevels[i].TorqueAmplificationFactor = br.ReadByte() / 10f; - } - - for (int i = 0; i < SportAssistLevels.Length; ++i) - { - SportAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); - SportAssistLevels[i].MaxCurrentPercent = br.ReadByte(); - SportAssistLevels[i].MaxThrottlePercent = br.ReadByte(); - SportAssistLevels[i].MaxCadencePercent = br.ReadByte(); - SportAssistLevels[i].MaxSpeedPercent = br.ReadByte(); - SportAssistLevels[i].TorqueAmplificationFactor = br.ReadByte() / 10f; - } - } - - // apply default settings for non existing options in version - LightsMode = LightsModeOptions.Default; - ThrottleGlobalSpeedLimit = ThrottleGlobalSpeedLimitOptions.Disabled; - ThrottleGlobalSpeedLimitPercent = 100; - UsePretension = false; - PretensionSpeedCutoffKph = 0; - - return true; - } - - public bool ParseFromBufferV4(byte[] buffer) - { - if (buffer.Length != ByteSizeV4) - { - return false; - } - - using (var s = new MemoryStream(buffer)) - { - var br = new BinaryReader(s); - - UseFreedomUnits = br.ReadBoolean(); - - MaxCurrentAmps = br.ReadByte(); - CurrentRampAmpsSecond = br.ReadByte(); - MaxBatteryVolts = br.ReadUInt16() / 100f; - LowCutoffVolts = br.ReadByte(); - MaxSpeedKph = br.ReadByte(); - - UseSpeedSensor = br.ReadBoolean(); - UseShiftSensor = br.ReadBoolean(); - UsePushWalk = br.ReadBoolean(); - UseTemperatureSensor = (TemperatureSensor)br.ReadByte(); - LightsMode = (LightsModeOptions)br.ReadByte(); - - WheelSizeInch = br.ReadUInt16() / 10f; - NumWheelSensorSignals = br.ReadByte(); - - PasStartDelayPulses = br.ReadByte(); - PasStopDelayMilliseconds = br.ReadByte() * 10u; - PasKeepCurrentPercent = br.ReadByte(); - PasKeepCurrentCadenceRpm = br.ReadByte(); - - ThrottleStartMillivolts = br.ReadUInt16(); - ThrottleEndMillivolts = br.ReadUInt16(); - ThrottleStartPercent = br.ReadByte(); - ThrottleGlobalSpeedLimit = (ThrottleGlobalSpeedLimitOptions)br.ReadByte(); - ThrottleGlobalSpeedLimitPercent = br.ReadByte(); - - ShiftInterruptDuration = br.ReadUInt16(); - ShiftInterruptCurrentThresholdPercent = br.ReadByte(); - - WalkModeDataDisplay = (WalkModeData)br.ReadByte(); - - AssistModeSelection = (AssistModeSelect)br.ReadByte(); - AssistStartupLevel = br.ReadByte(); - - for (int i = 0; i < StandardAssistLevels.Length; ++i) - { - StandardAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); - StandardAssistLevels[i].MaxCurrentPercent = br.ReadByte(); - StandardAssistLevels[i].MaxThrottlePercent = br.ReadByte(); - StandardAssistLevels[i].MaxCadencePercent = br.ReadByte(); - StandardAssistLevels[i].MaxSpeedPercent = br.ReadByte(); - StandardAssistLevels[i].TorqueAmplificationFactor = br.ReadByte() / 10f; - } - - for (int i = 0; i < SportAssistLevels.Length; ++i) - { - SportAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); - SportAssistLevels[i].MaxCurrentPercent = br.ReadByte(); - SportAssistLevels[i].MaxThrottlePercent = br.ReadByte(); - SportAssistLevels[i].MaxCadencePercent = br.ReadByte(); - SportAssistLevels[i].MaxSpeedPercent = br.ReadByte(); - SportAssistLevels[i].TorqueAmplificationFactor = br.ReadByte() / 10f; - } - } - - // apply default settings for non existing options in version - UsePretension = false; - PretensionSpeedCutoffKph = 0; - - return true; - } - - public bool ParseFromBufferV5(byte[] buffer) - { - if (buffer.Length != ByteSizeV5) - { - return false; - } - - using (var s = new MemoryStream(buffer)) - { - var br = new BinaryReader(s); - - UseFreedomUnits = br.ReadBoolean(); - - MaxCurrentAmps = br.ReadByte(); - CurrentRampAmpsSecond = br.ReadByte(); - MaxBatteryVolts = br.ReadUInt16() / 100f; - LowCutoffVolts = br.ReadByte(); - MaxSpeedKph = br.ReadByte(); - - UseSpeedSensor = br.ReadBoolean(); - UseShiftSensor = br.ReadBoolean(); - UsePushWalk = br.ReadBoolean(); - UseTemperatureSensor = (TemperatureSensor)br.ReadByte(); - LightsMode = (LightsModeOptions)br.ReadByte(); - UsePretension = br.ReadBoolean(); - PretensionSpeedCutoffKph = br.ReadByte(); - - WheelSizeInch = br.ReadUInt16() / 10f; - NumWheelSensorSignals = br.ReadByte(); - - PasStartDelayPulses = br.ReadByte(); - PasStopDelayMilliseconds = br.ReadByte() * 10u; - PasKeepCurrentPercent = br.ReadByte(); - PasKeepCurrentCadenceRpm = br.ReadByte(); - - ThrottleStartMillivolts = br.ReadUInt16(); - ThrottleEndMillivolts = br.ReadUInt16(); - ThrottleStartPercent = br.ReadByte(); - ThrottleGlobalSpeedLimit = (ThrottleGlobalSpeedLimitOptions)br.ReadByte(); - ThrottleGlobalSpeedLimitPercent = br.ReadByte(); - - ShiftInterruptDuration = br.ReadUInt16(); - ShiftInterruptCurrentThresholdPercent = br.ReadByte(); - - WalkModeDataDisplay = (WalkModeData)br.ReadByte(); - - AssistModeSelection = (AssistModeSelect)br.ReadByte(); - AssistStartupLevel = br.ReadByte(); - - for (int i = 0; i < StandardAssistLevels.Length; ++i) - { - StandardAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); - StandardAssistLevels[i].MaxCurrentPercent = br.ReadByte(); - StandardAssistLevels[i].MaxThrottlePercent = br.ReadByte(); - StandardAssistLevels[i].MaxCadencePercent = br.ReadByte(); - StandardAssistLevels[i].MaxSpeedPercent = br.ReadByte(); - StandardAssistLevels[i].TorqueAmplificationFactor = br.ReadByte() / 10f; - } - - for (int i = 0; i < SportAssistLevels.Length; ++i) - { - SportAssistLevels[i].Type = (AssistFlagsType)br.ReadByte(); - SportAssistLevels[i].MaxCurrentPercent = br.ReadByte(); - SportAssistLevels[i].MaxThrottlePercent = br.ReadByte(); - SportAssistLevels[i].MaxCadencePercent = br.ReadByte(); - SportAssistLevels[i].MaxSpeedPercent = br.ReadByte(); - SportAssistLevels[i].TorqueAmplificationFactor = br.ReadByte() / 10f; - } - } - - return true; - } - - public byte[] WriteToBuffer() - { - using (var s = new MemoryStream()) - { - var bw = new BinaryWriter(s); - - bw.Write(UseFreedomUnits); - - bw.Write((byte)MaxCurrentAmps); - bw.Write((byte)CurrentRampAmpsSecond); - bw.Write((UInt16)(MaxBatteryVolts * 100)); - bw.Write((byte)LowCutoffVolts); - bw.Write((byte)MaxSpeedKph); - - bw.Write(UseSpeedSensor); - bw.Write(UseShiftSensor); - bw.Write(UsePushWalk); - bw.Write((byte)UseTemperatureSensor); - bw.Write((byte)LightsMode); - bw.Write(UsePretension); - bw.Write((byte)PretensionSpeedCutoffKph); - - bw.Write((UInt16)(WheelSizeInch * 10)); - bw.Write((byte)NumWheelSensorSignals); - - bw.Write((byte)PasStartDelayPulses); - bw.Write((byte)(PasStopDelayMilliseconds / 10u)); - bw.Write((byte)PasKeepCurrentPercent); - bw.Write((byte)PasKeepCurrentCadenceRpm); - - bw.Write((UInt16)ThrottleStartMillivolts); - bw.Write((UInt16)ThrottleEndMillivolts); - bw.Write((byte)ThrottleStartPercent); - bw.Write((byte)ThrottleGlobalSpeedLimit); - bw.Write((byte)ThrottleGlobalSpeedLimitPercent); - - bw.Write((UInt16)ShiftInterruptDuration); - bw.Write((byte)ShiftInterruptCurrentThresholdPercent); - - bw.Write((byte)WalkModeDataDisplay); - - bw.Write((byte)AssistModeSelection); - bw.Write((byte)AssistStartupLevel); - - for (int i = 0; i < StandardAssistLevels.Length; ++i) - { - bw.Write((byte)StandardAssistLevels[i].Type); - bw.Write((byte)StandardAssistLevels[i].MaxCurrentPercent); - bw.Write((byte)StandardAssistLevels[i].MaxThrottlePercent); - bw.Write((byte)StandardAssistLevels[i].MaxCadencePercent); - bw.Write((byte)StandardAssistLevels[i].MaxSpeedPercent); - bw.Write((byte)Math.Round(StandardAssistLevels[i].TorqueAmplificationFactor * 10)); - } - - for (int i = 0; i < SportAssistLevels.Length; ++i) - { - bw.Write((byte)SportAssistLevels[i].Type); - bw.Write((byte)SportAssistLevels[i].MaxCurrentPercent); - bw.Write((byte)SportAssistLevels[i].MaxThrottlePercent); - bw.Write((byte)SportAssistLevels[i].MaxCadencePercent); - bw.Write((byte)SportAssistLevels[i].MaxSpeedPercent); - bw.Write((byte)Math.Round(SportAssistLevels[i].TorqueAmplificationFactor * 10)); - } - - return s.ToArray(); - } - } - - public void CopyFrom(Configuration cfg) - { - Target = cfg.Target; - - UseFreedomUnits = cfg.UseFreedomUnits; - MaxCurrentAmps = cfg.MaxCurrentAmps; - CurrentRampAmpsSecond = cfg.CurrentRampAmpsSecond; - MaxBatteryVolts = cfg.MaxBatteryVolts; - LowCutoffVolts = cfg.LowCutoffVolts; - UseSpeedSensor = cfg.UseSpeedSensor; - UseShiftSensor = cfg.UseShiftSensor; - UsePushWalk = cfg.UsePushWalk; - UsePretension = cfg.UsePretension; - PretensionSpeedCutoffKph = cfg.PretensionSpeedCutoffKph; - UseTemperatureSensor = cfg.UseTemperatureSensor; - LightsMode = cfg.LightsMode; - WheelSizeInch = cfg.WheelSizeInch; - NumWheelSensorSignals = cfg.NumWheelSensorSignals; - MaxSpeedKph = cfg.MaxSpeedKph; - PasStartDelayPulses = cfg.PasStartDelayPulses; - PasStopDelayMilliseconds = cfg.PasStopDelayMilliseconds; - PasKeepCurrentPercent = cfg.PasKeepCurrentPercent; - PasKeepCurrentCadenceRpm = cfg.PasKeepCurrentCadenceRpm; - ThrottleStartMillivolts = cfg.ThrottleStartMillivolts; - ThrottleEndMillivolts = cfg.ThrottleEndMillivolts; - ThrottleStartPercent = cfg.ThrottleStartPercent; - ThrottleGlobalSpeedLimit = cfg.ThrottleGlobalSpeedLimit; - ThrottleGlobalSpeedLimitPercent = cfg.ThrottleGlobalSpeedLimitPercent; - ShiftInterruptDuration = cfg.ShiftInterruptDuration; - ShiftInterruptCurrentThresholdPercent = cfg.ShiftInterruptCurrentThresholdPercent; - WalkModeDataDisplay = cfg.WalkModeDataDisplay; - AssistModeSelection = cfg.AssistModeSelection; - AssistStartupLevel = cfg.AssistStartupLevel; - - for (int i = 0; i < Math.Min(cfg.StandardAssistLevels.Length, StandardAssistLevels.Length); ++i) - { - StandardAssistLevels[i].Type = cfg.StandardAssistLevels[i].Type; - StandardAssistLevels[i].MaxCurrentPercent = cfg.StandardAssistLevels[i].MaxCurrentPercent; - StandardAssistLevels[i].MaxThrottlePercent = cfg.StandardAssistLevels[i].MaxThrottlePercent; - StandardAssistLevels[i].MaxCadencePercent = cfg.StandardAssistLevels[i].MaxCadencePercent; - StandardAssistLevels[i].MaxSpeedPercent = cfg.StandardAssistLevels[i].MaxSpeedPercent; - StandardAssistLevels[i].TorqueAmplificationFactor = cfg.StandardAssistLevels[i].TorqueAmplificationFactor; - } - - for (int i = 0; i < Math.Min(cfg.SportAssistLevels.Length, SportAssistLevels.Length); ++i) - { - SportAssistLevels[i].Type = cfg.SportAssistLevels[i].Type; - SportAssistLevels[i].MaxCurrentPercent = cfg.SportAssistLevels[i].MaxCurrentPercent; - SportAssistLevels[i].MaxThrottlePercent = cfg.SportAssistLevels[i].MaxThrottlePercent; - SportAssistLevels[i].MaxCadencePercent = cfg.SportAssistLevels[i].MaxCadencePercent; - SportAssistLevels[i].MaxSpeedPercent = cfg.SportAssistLevels[i].MaxSpeedPercent; - SportAssistLevels[i].TorqueAmplificationFactor = cfg.SportAssistLevels[i].TorqueAmplificationFactor; - } - } - - public void ReadFromFile(string filepath) - { - var serializer = new XmlSerializer(typeof(Configuration)); - - using (var reader = new FileStream(filepath, FileMode.Open)) - { - var obj = serializer.Deserialize(reader) as Configuration; - CopyFrom(obj); - } - } - - public void WriteToFile(string filepath) - { - var serializer = new XmlSerializer(typeof(Configuration)); - var settings = new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = true }; - using (var xmlWriter = XmlWriter.Create(new StreamWriter(filepath), settings)) - { - serializer.Serialize(xmlWriter, this); - } - } - - public void Validate() - { - ValidateLimits(MaxCurrentAmps, 5, MaxCurrentLimitAmps, "Max Current (A)"); - ValidateLimits(CurrentRampAmpsSecond, 1, 255, "Current Ramp (A/s)"); - ValidateLimits((uint)MaxBatteryVolts, 1, 100, "Max Battery Voltage (V)"); - ValidateLimits(LowCutoffVolts, 1, 100, "Low Voltage Cut Off (V)"); - - ValidateLimits((uint)WheelSizeInch, 10, 40, "Wheel Size (inch)"); - ValidateLimits(NumWheelSensorSignals, 1, 10, "Wheel Sensor Signals"); - ValidateLimits(MaxSpeedKph, 0, 180, "Max Speed (km/h)"); - ValidateLimits(PretensionSpeedCutoffKph, 0, 100, "Pretension Speed Cutoff (km/h)"); - - ValidateLimits(PasStartDelayPulses, 0, 24, "Pas Delay (pulses)"); - ValidateLimits(PasStopDelayMilliseconds, 50, 1000, "Pas Stop Delay (ms)"); - ValidateLimits(PasKeepCurrentPercent, 10, 100, "Pas Keep Current (%)"); - ValidateLimits(PasKeepCurrentCadenceRpm, 0, 255, "Pas Keep Current Cadence (rpm)"); - - ValidateLimits(ThrottleStartMillivolts, 200, 2500, "Throttle Start (mV)"); - ValidateLimits(ThrottleEndMillivolts, 2500, 5000, "Throttle End (mV)"); - ValidateLimits(ThrottleStartPercent, 0, 100, "Throttle Start (%)"); - ValidateLimits(ThrottleGlobalSpeedLimitPercent, 0, 100, "Throttle Global Speed Limit (%)"); - - ValidateLimits(ShiftInterruptDuration, 50, 2000, "Shift Interrupt Duration (ms)"); - ValidateLimits(ShiftInterruptCurrentThresholdPercent, 0, 100, "Shift Interrupt Current Threshold (%)"); - - ValidateLimits(AssistStartupLevel, 0, 9, "Assist Startup Level"); - - for (int i = 0; i < StandardAssistLevels.Length; ++i) - { - ValidateLimits(StandardAssistLevels[i].MaxCurrentPercent, 0, 100, $"Standard (Level {i}): Target Power (%)"); - ValidateLimits(StandardAssistLevels[i].MaxThrottlePercent, 0, 100, $"Standard (Level {i}): Max Throttle (%)"); - ValidateLimits(StandardAssistLevels[i].MaxCadencePercent, 0, 100, $"Standard (Level {i}): Max Cadence (%)"); - ValidateLimits(StandardAssistLevels[i].MaxSpeedPercent, 0, 100, $"Standard (Level {i}): Max Speed (%)"); - ValidateLimits((uint)StandardAssistLevels[i].TorqueAmplificationFactor, 0, 25, $"Standard (Level {i}): Torque Amplification"); - } - - for (int i = 0; i < SportAssistLevels.Length; ++i) - { - ValidateLimits(SportAssistLevels[i].MaxCurrentPercent, 0, 100, $"Sport (Level {i}): Target Power (%)"); - ValidateLimits(SportAssistLevels[i].MaxThrottlePercent, 0, 100, $"Sport (Level {i}): Max Throttle (%)"); - ValidateLimits(SportAssistLevels[i].MaxCadencePercent, 0, 100, $"Sport (Level {i}): Max Cadence (%)"); - ValidateLimits(SportAssistLevels[i].MaxSpeedPercent, 0, 100, $"Sport (Level {i}): Max Speed (%)"); - ValidateLimits((uint)SportAssistLevels[i].TorqueAmplificationFactor, 0, 25, $"Sport (Level {i}): Torque Amplification"); - } - } - - private void ValidateLimits(uint value, uint min, uint max, string name) - { - if (value < min || value > max) - { - throw new Exception(name + " must be in interval " + min + "-" + max + "."); - } - } - } -} diff --git a/src/tool/Model/EventLogEntry.cs b/src/tool/Model/EventLogEntry.cs deleted file mode 100644 index a364e9cb..00000000 --- a/src/tool/Model/EventLogEntry.cs +++ /dev/null @@ -1,249 +0,0 @@ -using System; - -namespace BBSFW.Model -{ - public class EventLogEntry - { - private int _event; - private int? _data; - - - private const int EVT_MSG_MOTOR_INIT_OK = 1; - private const int EVT_MSG_CONFIG_READ_DONE = 2; - private const int EVT_MSG_CONFIG_RESET = 3; - private const int EVT_MSG_CONFIG_WRITE_DONE = 4; - private const int EVT_MSG_CONFIG_READ_BEGIN = 5; - private const int EVT_MSG_CONFIG_WRITE_BEGIN = 6; - private const int EVT_MSG_PSTATE_READ_BEGIN = 7; - private const int EVT_MSG_PSTATE_READ_DONE = 8; - private const int EVT_MSG_PSTATE_WRITE_BEGIN = 9; - private const int EVT_MSG_PSTATE_WRITE_DONE = 10; - - private const int EVT_ERROR_INIT_MOTOR = 64; - private const int EVT_ERROR_CHANGE_TARGET_SPEED = 65; - private const int EVT_ERROR_CHANGE_TARGET_CURRENT = 66; - private const int EVT_ERROR_READ_MOTOR_STATUS = 67; - private const int EVT_ERROR_READ_MOTOR_CURRENT = 68; - private const int EVT_ERROR_READ_MOTOR_VOLTAGE = 69; - - private const int EVT_ERROR_EEPROM_READ = 70; - private const int EVT_ERROR_EEPROM_WRITE = 71; - private const int EVT_ERROR_EEPROM_ERASE = 72; - private const int EVT_ERROR_EEPROM_VERIFY_VERSION = 73; - private const int EVT_ERROR_EEPROM_VERIFY_CHECKSUM = 74; - private const int EVT_ERROR_THROTTLE_LOW_LIMIT = 75; - private const int EVT_ERROR_THROTTLE_HIGH_LIMIT = 76; - private const int EVT_ERROR_WATCHDOG_TRIGGERED = 77; - private const int EVT_ERROR_EXTCOM_CHECKSUM = 78; - private const int EVT_ERROR_EXTCOM_DISCARD = 79; - - private const int EVT_DATA_TARGET_CURRENT = 128; - private const int EVT_DATA_TARGET_SPEED = 129; - private const int EVT_DATA_MOTOR_STATUS = 130; - private const int EVT_DATA_ASSIST_LEVEL = 131; - private const int EVT_DATA_OPERATION_MODE = 132; - private const int EVT_DATA_WHEEL_SPEED_PPM = 133; - private const int EVT_DATA_LIGHTS = 134; - private const int EVT_DATA_TEMPERATURE = 135; - private const int EVT_DATA_THERMAL_LIMITING = 136; - private const int EVT_DATA_SPEED_LIMITING = 137; - private const int EVT_DATA_MAX_CURRENT_ADC_REQUEST = 138; - private const int EVT_DATA_MAX_CURRENT_ADC_RESPONSE = 139; - private const int EVT_DATA_MAIN_LOOP_TIME = 140; - private const int EVT_DATA_THROTTLE_ADC = 141; - private const int EVT_DATA_LVC_LIMITING = 142; - private const int EVT_DATA_SHIFT_SENSOR = 143; - private const int EVT_DATA_BBSHD_THERMISTOR = 144; - private const int EVT_DATA_VOLTAGE = 145; - private const int EVT_DATA_VOLTAGE_CALIBRATION = 146; - private const int EVT_DATA_TORQUE_ADC = 147; - private const int EVT_DATA_TORQUE_ADC_CALIBRATED = 148; - - - public enum LogLevel - { - Info, - Warning, - Error - } - - - public DateTime Timestamp { get; private set; } - - public LogLevel Level { get; private set; } - - public string Message { get; private set; } - - - public EventLogEntry(int evt, int? data) - { - Timestamp = DateTime.Now; - _event = evt; - if (evt >= 64 & evt < 128) - { - Level = LogLevel.Error; - } - else - { - Level = LogLevel.Info; - } - - _data = data; - Message = Parse(); - } - - - public string Parse() - { - switch (_event) - { - case EVT_MSG_MOTOR_INIT_OK: - return "Motor initialization successful."; - case EVT_MSG_CONFIG_READ_DONE: - return "Successfully read configuration from eeprom."; - case EVT_MSG_CONFIG_RESET: - Level = LogLevel.Warning; - return "Configuration reset performed."; - case EVT_MSG_CONFIG_WRITE_DONE: - return "Configuration successfully written to eeprom."; - case EVT_MSG_CONFIG_READ_BEGIN: - return "Reading configuration from eeprom."; - case EVT_MSG_CONFIG_WRITE_BEGIN: - return "Writing configuration to eeprom."; - case EVT_MSG_PSTATE_READ_BEGIN: - return "Reading persisted state from eeprom."; - case EVT_MSG_PSTATE_READ_DONE: - return "Successfully read persisted state from eeprom."; - case EVT_MSG_PSTATE_WRITE_BEGIN: - return "Writing persisted stated to eeprom."; - case EVT_MSG_PSTATE_WRITE_DONE: - return "Persisted state successfully written to eeprom."; - - case EVT_ERROR_INIT_MOTOR: - return "Failed to perform motor controller initialization."; - case EVT_ERROR_CHANGE_TARGET_CURRENT: - return "Failed to set motor target current on motor controller."; - case EVT_ERROR_CHANGE_TARGET_SPEED: - return "Failed to set motor target speed on motor controller."; - case EVT_ERROR_READ_MOTOR_STATUS: - return "Failed to read status from motor controller."; - case EVT_ERROR_READ_MOTOR_CURRENT: - return "Failed to read current from motor controller."; - case EVT_ERROR_READ_MOTOR_VOLTAGE: - return "Failed to read voltage from motor controller."; - case EVT_ERROR_EEPROM_READ: - return "Failed to read data from eeprom."; - case EVT_ERROR_EEPROM_WRITE: - return "Failed to write data to eeprom."; - case EVT_ERROR_EEPROM_ERASE: - return "Failed to erase eeprom before writing data."; - case EVT_ERROR_EEPROM_VERIFY_VERSION: - return "Data read from eeprom is of the wrong version."; - case EVT_ERROR_EEPROM_VERIFY_CHECKSUM: - return "Failed to verify checksum on data read from eeprom."; - case EVT_ERROR_THROTTLE_LOW_LIMIT: - return "Invalid throttle reading, below low limit, check throttle."; - case EVT_ERROR_THROTTLE_HIGH_LIMIT: - return "Invalid throttle reading, above high limit, check throttle."; - case EVT_ERROR_WATCHDOG_TRIGGERED: - return "Software reset by watchdog, software error."; - case EVT_ERROR_EXTCOM_CHECKSUM: - return "Message received with invalid checksum."; - case EVT_ERROR_EXTCOM_DISCARD: - return "Invalid message received on serial port, discarded."; - - case EVT_DATA_TARGET_CURRENT: - return $"Motor target current changed to {_data}%."; - case EVT_DATA_TARGET_SPEED: - return $"Motor target speed changed to {_data}%."; - case EVT_DATA_MOTOR_STATUS: - Level = _data != 0 ? LogLevel.Error : LogLevel.Info; - return $"Motor controller status changed to 0x{_data:X}."; - case EVT_DATA_ASSIST_LEVEL: - return $"Assist level changed to {_data}."; - case EVT_DATA_OPERATION_MODE: - return $"Operation mode changed to {_data}."; - case EVT_DATA_WHEEL_SPEED_PPM: - return $"Max wheel speed changed to {_data} rpm."; - case EVT_DATA_LIGHTS: - return $"Lights status changed to {_data}."; - case EVT_DATA_TEMPERATURE: - { - byte[] raw = BitConverter.GetBytes(_data.Value); - return $"Temperature, motor={(sbyte)raw[1]}C, controller={(sbyte)raw[0]}C."; - } - case EVT_DATA_THERMAL_LIMITING: - if (_data.Value != 0) - { - Level = LogLevel.Warning; - return "Thermal limiting activated, reducing power."; - } - else - { - return "Thermal limiting deactivated."; - } - case EVT_DATA_SPEED_LIMITING: - if (_data.Value != 0) - { - return "Speed limiting activated."; - } - else - { - return "Speed limiting deactivated."; - } - case EVT_DATA_MAX_CURRENT_ADC_REQUEST: - return $"Requesting to configure max current on motor controller mcu, adc={_data}."; - case EVT_DATA_MAX_CURRENT_ADC_RESPONSE: - return $"Max current configured on motor controller mcu, response was adc={_data}."; - case EVT_DATA_MAIN_LOOP_TIME: - return $"Main loop, interval={_data}ms."; - case EVT_DATA_THROTTLE_ADC: - return $"Throttle adc, value={_data}."; - case EVT_DATA_LVC_LIMITING: - if (_data.Value != 0) - { - return $"Low voltage limiting activated, voltage={(_data / 100f):0.0}"; - } - else - { - return "Low voltage limiting deactivated."; - } - case EVT_DATA_SHIFT_SENSOR: - if (_data.Value != 0) - { - return $"Shift sensor power ramp started."; - } - else - { - return $"Shift sensor power ramp ended."; - } - case EVT_DATA_BBSHD_THERMISTOR: - if (_data.Value != 0) - { - return "BBSHD motor with PTC thermistor detected."; - } - else - { - return "BBSHD motor with NTC thermistor detected."; - } - case EVT_DATA_VOLTAGE: - return $"Battery voltage reading, value={_data / 100f}V."; - case EVT_DATA_VOLTAGE_CALIBRATION: - return $"Battery voltage calibration updated, adc_steps_per_volt={_data / 100f}."; - case EVT_DATA_TORQUE_ADC: - return $"Torque adc, value={_data}."; - case EVT_DATA_TORQUE_ADC_CALIBRATED: - return $"Torque sensor calibrated, adc_bias={_data}."; - } - - if (_data.HasValue) - { - return $"Unknown ({_event}, value={_data.Value})"; - } - - return $"Unknown ({_event})"; - } - - - } -} diff --git a/src/tool/Properties/Settings.Designer.cs b/src/tool/Properties/Settings.Designer.cs deleted file mode 100644 index 5c5cc2be..00000000 --- a/src/tool/Properties/Settings.Designer.cs +++ /dev/null @@ -1,38 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace BBSFW.Properties { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.7.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { - return defaultInstance; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool UseFreedomUnits { - get { - return ((bool)(this["UseFreedomUnits"])); - } - set { - this["UseFreedomUnits"] = value; - } - } - } -} diff --git a/src/tool/View/Converter/TimestampConverter.cs b/src/tool/View/Converter/TimestampConverter.cs deleted file mode 100644 index 420ac889..00000000 --- a/src/tool/View/Converter/TimestampConverter.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Globalization; -using System.Windows.Data; - -namespace BBSFW.View.Converter -{ - public class TimestampConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (value is DateTime) - { - var dt = (DateTime)value; - - return dt.ToString("yyyy-MM-dd HH:mm:ss.fff"); - } - - return null; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } - } -} diff --git a/src/tool/View/Extension/DataGridExtension.cs b/src/tool/View/Extension/DataGridExtension.cs deleted file mode 100644 index fb0525d6..00000000 --- a/src/tool/View/Extension/DataGridExtension.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Windows; -using System.Windows.Controls; - -namespace BBSFW.View.Extension -{ - public static class DataGridExtension - { - - public static readonly DependencyProperty AutoScrollToEndProperty = DependencyProperty.RegisterAttached( - "AutoScrollToEnd", typeof(bool), typeof(DataGridExtension), new PropertyMetadata(default(bool), AutoScrollToEndChangedCallback)); - - private static readonly Dictionary handlersDict = new Dictionary(); - - private static void AutoScrollToEndChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args) - { - var dataGrid = dependencyObject as DataGrid; - if (dataGrid == null) - { - throw new InvalidOperationException("Dependency object is not DataGrid."); - } - - if ((bool)args.NewValue) - { - Subscribe(dataGrid); - dataGrid.Unloaded += DataGridOnUnloaded; - dataGrid.Loaded += DataGridOnLoaded; - } - else - { - Unsubscribe(dataGrid); - dataGrid.Unloaded -= DataGridOnUnloaded; - dataGrid.Loaded -= DataGridOnLoaded; - } - } - - private static void Subscribe(DataGrid dataGrid) - { - var handler = new NotifyCollectionChangedEventHandler((sender, eventArgs) => ScrollToEnd(dataGrid)); - handlersDict.Add(dataGrid, handler); - ((INotifyCollectionChanged)dataGrid.Items).CollectionChanged += handler; - ScrollToEnd(dataGrid); - } - - private static void Unsubscribe(DataGrid dataGrid) - { - NotifyCollectionChangedEventHandler handler; - handlersDict.TryGetValue(dataGrid, out handler); - if (handler == null) - { - return; - } - ((INotifyCollectionChanged)dataGrid.Items).CollectionChanged -= handler; - handlersDict.Remove(dataGrid); - } - - private static void DataGridOnLoaded(object sender, RoutedEventArgs routedEventArgs) - { - var dataGrid = (DataGrid)sender; - if (GetAutoScrollToEnd(dataGrid)) - { - Subscribe(dataGrid); - } - } - - private static void DataGridOnUnloaded(object sender, RoutedEventArgs routedEventArgs) - { - var dataGrid = (DataGrid)sender; - if (GetAutoScrollToEnd(dataGrid)) - { - Unsubscribe(dataGrid); - } - } - - private static void ScrollToEnd(DataGrid datagrid) - { - if (datagrid.Items.Count == 0) - { - return; - } - datagrid.ScrollIntoView(datagrid.Items[datagrid.Items.Count - 1]); - } - - public static void SetAutoScrollToEnd(DependencyObject element, bool value) - { - element.SetValue(AutoScrollToEndProperty, value); - } - - public static bool GetAutoScrollToEnd(DependencyObject element) - { - return (bool)element.GetValue(AutoScrollToEndProperty); - } - - } -} diff --git a/src/tool/ViewModel/AssistLevelViewModel.cs b/src/tool/ViewModel/AssistLevelViewModel.cs deleted file mode 100644 index efe71eb7..00000000 --- a/src/tool/ViewModel/AssistLevelViewModel.cs +++ /dev/null @@ -1,397 +0,0 @@ -using BBSFW.Model; -using BBSFW.ViewModel.Base; -using System.Collections.Generic; -using System.Linq; - -namespace BBSFW.ViewModel -{ - public class AssistLevelViewModel : ObservableObject - { - private ConfigurationViewModel _configVm; - private Configuration.AssistLevel _level; - - - public enum AssistBaseType - { - Disabled, - Pas, - Throttle, - Cruise - } - - public enum AssistPasVariant - { - Cadence, - Torque, - Variable - } - - - public List> AssistBaseTypeOptions { get; } = - new List>() - { - new ValueItemViewModel(AssistBaseType.Disabled, "Motor Disabled"), - new ValueItemViewModel(AssistBaseType.Pas, "PAS"), - new ValueItemViewModel(AssistBaseType.Throttle, "Throttle"), - new ValueItemViewModel(AssistBaseType.Cruise, "Cruise") - }; - - public List> AssistPasVariantOptions - { - get - { - var variants = new List> - { - new ValueItemViewModel(AssistPasVariant.Cadence, "Cadence") - }; - - if (_configVm.IsTorqueSensorSupported) - { - variants.Add(new ValueItemViewModel(AssistPasVariant.Torque, "Torque")); - } - - variants.Add(new ValueItemViewModel(AssistPasVariant.Variable, "Variable")); - - return variants; - } - } - - - - - private int _id; - public int Id - { - get { return _id; } - } - - - public ValueItemViewModel SelectedType - { - get - { - var type = AssistBaseType.Disabled; - - if (_level.Type.HasFlag(Configuration.AssistFlagsType.Pas)) - { - type = AssistBaseType.Pas; - } - else if (_level.Type.HasFlag(Configuration.AssistFlagsType.Throttle)) - { - type = AssistBaseType.Throttle; - } - else if (_level.Type.HasFlag(Configuration.AssistFlagsType.Cruise)) - { - type = AssistBaseType.Cruise; - } - - return AssistBaseTypeOptions.FirstOrDefault((e) => e.Value == type); - } - set - { - if (value.Value != SelectedType.Value) - { - _level.Type = ApplyBaseTypeFlag(value.Value, _level.Type); - OnPropertyChanged(nameof(SelectedType)); - - switch (value.Value) - { - case AssistBaseType.Disabled: - TargetCurrentPercent = 0; - MaxThrottlePercent = 0; - MaxSpeedPercent = 0; - TorqueAmplificationFactor = 0; - _level.Type = ClearThrottleFlag(_level.Type); - _level.Type = ClearPasVariantFlag(_level.Type); - IsThrottleCadenceOverrideEnabled = false; - IsThrottleSpeedOverrideEnabled = false; - break; - case AssistBaseType.Throttle: - TargetCurrentPercent = 0; - TorqueAmplificationFactor = 0; - TargetCurrentPercent = 0; - _level.Type = ClearPasVariantFlag(_level.Type); - IsThrottleCadenceOverrideEnabled = false; - IsThrottleSpeedOverrideEnabled = false; - break; - case AssistBaseType.Pas: - MaxThrottlePercent = 0; - _level.Type = ClearThrottleFlag(_level.Type); - break; - } - } - } - } - - public ValueItemViewModel SelectedPasVariant - { - get - { - var variant = AssistPasVariant.Cadence; - if (_level.Type.HasFlag(Configuration.AssistFlagsType.PasTorque)) - { - variant = AssistPasVariant.Torque; - } - else if (_level.Type.HasFlag(Configuration.AssistFlagsType.PasVariable)) - { - variant = AssistPasVariant.Variable; - } - - return AssistPasVariantOptions.FirstOrDefault((e) => e.Value == variant); - } - set - { - if (value.Value != SelectedPasVariant.Value) - { - _level.Type = ApplyPasVariantFlag(value.Value, _level.Type); - OnPropertyChanged(nameof(SelectedPasVariant)); - OnPropertyChanged(nameof(IsPasAssistVariableVariant)); - OnPropertyChanged(nameof(IsPasAssistTorqueVariant)); - - switch(value.Value) - { - case AssistPasVariant.Variable: - TorqueAmplificationFactor = 0; - IsThrottleEnabled = false; - IsThrottleCadenceOverrideEnabled = false; - IsThrottleSpeedOverrideEnabled = false; - MaxThrottlePercent = 0; - break; - case AssistPasVariant.Cadence: - TorqueAmplificationFactor = 0; - break; - } - } - } - } - - public bool IsThrottleEnabled - { - get { return _level.Type.HasFlag(Configuration.AssistFlagsType.Throttle); } - set - { - if (value != IsThrottleEnabled) - { - _level.Type = ApplyThrottleFlag(value, _level.Type); - OnPropertyChanged(nameof(IsThrottleEnabled)); - } - } - } - - public bool IsThrottleCadenceOverrideEnabled - { - get { return _level.Type.HasFlag(Configuration.AssistFlagsType.CadenceOverride); } - set - { - if (value != IsThrottleCadenceOverrideEnabled) - { - _level.Type = ApplyThrottleCadenceOverrideFlag(value, _level.Type); - OnPropertyChanged(nameof(IsThrottleCadenceOverrideEnabled)); - } - } - } - - public bool IsThrottleSpeedOverrideEnabled - { - get { return _level.Type.HasFlag(Configuration.AssistFlagsType.SpeedOverride); } - set - { - if (value != IsThrottleSpeedOverrideEnabled) - { - _level.Type = ApplyThrottleSpeedOverrideFlag(value, _level.Type); - OnPropertyChanged(nameof(IsThrottleSpeedOverrideEnabled)); - } - } - } - - public bool IsPasAssistVariableVariant - { - get { return _level.Type.HasFlag(Configuration.AssistFlagsType.PasVariable); } - } - - public bool IsPasAssistTorqueVariant - { - get { return _level.Type.HasFlag(Configuration.AssistFlagsType.PasTorque); } - } - - - - public uint TargetCurrentPercent - { - get { return _level.MaxCurrentPercent; } - set - { - if (_level.MaxCurrentPercent != value) - { - _level.MaxCurrentPercent = value; - OnPropertyChanged(nameof(TargetCurrentPercent)); - } - } - } - - public uint MaxThrottlePercent - { - get { return _level.MaxThrottlePercent; } - set - { - if (_level.MaxThrottlePercent != value) - { - _level.MaxThrottlePercent = value; - OnPropertyChanged(nameof(MaxThrottlePercent)); - } - } - } - - public uint MaxCadencePercent - { - get { return _level.MaxCadencePercent; } - set - { - if (_level.MaxCadencePercent != value) - { - _level.MaxCadencePercent = value; - OnPropertyChanged(nameof(MaxCadencePercent)); - } - } - } - - public uint MaxSpeedPercent - { - get { return _level.MaxSpeedPercent; } - set - { - if (_level.MaxSpeedPercent != value) - { - _level.MaxSpeedPercent = value; - OnPropertyChanged(nameof(MaxSpeedPercent)); - } - } - } - - public float TorqueAmplificationFactor - { - get { return _level.TorqueAmplificationFactor; } - set - { - if (_level.TorqueAmplificationFactor != value) - { - _level.TorqueAmplificationFactor = value; - OnPropertyChanged(nameof(TorqueAmplificationFactor)); - } - } - } - - - public AssistLevelViewModel(ConfigurationViewModel configVm, int id, Configuration.AssistLevel level) - { - _configVm = configVm; - _id = id; - _level = level; - } - - - private static Configuration.AssistFlagsType ApplyBaseTypeFlag(AssistBaseType baseType, Configuration.AssistFlagsType flags) - { - byte f = (byte)flags; - f &= (byte)~(Configuration.AssistFlagsType.Pas | Configuration.AssistFlagsType.Throttle | Configuration.AssistFlagsType.Cruise); - - var result = (Configuration.AssistFlagsType)f; - switch (baseType) - { - case AssistBaseType.Pas: - result |= Configuration.AssistFlagsType.Pas; - break; - case AssistBaseType.Throttle: - result |= Configuration.AssistFlagsType.Throttle; - break; - case AssistBaseType.Cruise: - result |= Configuration.AssistFlagsType.Cruise; - break; - } - - return result; - } - - private static Configuration.AssistFlagsType ClearPasVariantFlag(Configuration.AssistFlagsType flags) - { - byte f = (byte)flags; - f &= (byte)~(Configuration.AssistFlagsType.PasTorque | Configuration.AssistFlagsType.PasVariable); - - return (Configuration.AssistFlagsType)f; - } - - private static Configuration.AssistFlagsType ApplyPasVariantFlag(AssistPasVariant pasVariant, Configuration.AssistFlagsType flags) - { - var result = ClearPasVariantFlag(flags); - switch (pasVariant) - { - case AssistPasVariant.Torque: - result |= Configuration.AssistFlagsType.PasTorque; - break; - case AssistPasVariant.Variable: - result |= Configuration.AssistFlagsType.PasVariable; - break; - } - - return result; - } - - private static Configuration.AssistFlagsType ClearThrottleFlag(Configuration.AssistFlagsType flags) - { - byte f = (byte)flags; - f &= (byte)~(Configuration.AssistFlagsType.Throttle); - - return (Configuration.AssistFlagsType)f; - } - - private static Configuration.AssistFlagsType ApplyThrottleFlag(bool enabled, Configuration.AssistFlagsType flags) - { - var result = ClearThrottleFlag(flags); - if (enabled) - { - result |= Configuration.AssistFlagsType.Throttle; - } - - return result; - } - - private static Configuration.AssistFlagsType ClearThrottleCadenceOverrideFlag(Configuration.AssistFlagsType flags) - { - byte f = (byte)flags; - f &= (byte)~(Configuration.AssistFlagsType.CadenceOverride); - - return (Configuration.AssistFlagsType)f; - } - - private static Configuration.AssistFlagsType ApplyThrottleCadenceOverrideFlag(bool enabled, Configuration.AssistFlagsType flags) - { - var result = ClearThrottleCadenceOverrideFlag(flags); - if (enabled) - { - result |= Configuration.AssistFlagsType.CadenceOverride; - } - - return result; - } - - private static Configuration.AssistFlagsType ClearThrottleSpeedOverrideFlag(Configuration.AssistFlagsType flags) - { - byte f = (byte)flags; - f &= (byte)~(Configuration.AssistFlagsType.SpeedOverride); - - return (Configuration.AssistFlagsType)f; - } - - private static Configuration.AssistFlagsType ApplyThrottleSpeedOverrideFlag(bool enabled, Configuration.AssistFlagsType flags) - { - var result = ClearThrottleSpeedOverrideFlag(flags); - if (enabled) - { - result |= Configuration.AssistFlagsType.SpeedOverride; - } - - return result; - } - - } -} diff --git a/src/tool/ViewModel/AssistLevelsViewModel.cs b/src/tool/ViewModel/AssistLevelsViewModel.cs deleted file mode 100644 index 01b72e22..00000000 --- a/src/tool/ViewModel/AssistLevelsViewModel.cs +++ /dev/null @@ -1,67 +0,0 @@ -using BBSFW.ViewModel.Base; -using System.Collections.Generic; - - -namespace BBSFW.ViewModel -{ - public class AssistLevelsViewModel : ObservableObject - { - - public enum OperationMode - { - Standard, - Sport - } - - - public static List> OperationModes { get; } = - new List> - { - new ValueItemViewModel(OperationMode.Standard, "Standard"), - new ValueItemViewModel(OperationMode.Sport, "Sport") - }; - - - private ConfigurationViewModel _configVm; - public ConfigurationViewModel ConfigVm - { - get { return _configVm; } - } - - - private ValueItemViewModel _selectedOperationModePage; - public ValueItemViewModel SelectedOperationModePage - { - get { return _selectedOperationModePage; } - set - { - if (_selectedOperationModePage != value) - { - _selectedOperationModePage = value; - OnPropertyChanged(nameof(SelectedOperationModePage)); - } - } - } - - private AssistLevelViewModel _selectedAssistLevel; - public AssistLevelViewModel SelectedAssistLevel - { - get { return _selectedAssistLevel; } - set - { - if (_selectedAssistLevel != value) - { - _selectedAssistLevel = value; - OnPropertyChanged(nameof(SelectedAssistLevel)); - } - } - } - - public AssistLevelsViewModel(ConfigurationViewModel config) - { - _configVm = config; - SelectedOperationModePage = OperationModes[0]; - } - - } -} diff --git a/src/tool/ViewModel/Base/DelegateCommand.cs b/src/tool/ViewModel/Base/DelegateCommand.cs deleted file mode 100644 index 18f2feb6..00000000 --- a/src/tool/ViewModel/Base/DelegateCommand.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Windows.Input; - -namespace BBSFW.ViewModel.Base -{ - public class DelegateCommand : ICommand - { - private readonly Action _action; - private readonly Action _actionWithParam; - - public event EventHandler CanExecuteChanged; - - public DelegateCommand(Action action) - { - _action = action; - _actionWithParam = null; - } - - public DelegateCommand(Action action) - { - _actionWithParam = action; - _action = null; - } - - public bool CanExecute(object parameter) - { - return true; - } - - public void Execute(object parameter) - { - _action?.Invoke(); - _actionWithParam?.Invoke(parameter); - } - } -} diff --git a/src/tool/ViewModel/Base/ObservableObject.cs b/src/tool/ViewModel/Base/ObservableObject.cs deleted file mode 100644 index cf092fea..00000000 --- a/src/tool/ViewModel/Base/ObservableObject.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; - -namespace BBSFW.ViewModel.Base -{ - public class ObservableObject : INotifyPropertyChanged, INotifyDataErrorInfo - { - - #region Private Members - - private Dictionary _errors = new Dictionary(); - - #endregion - - #region Public Properties - - public bool HasErrors - { - get - { - return _errors.Count > 0; - } - } - - #endregion - - #region Public events - - public event EventHandler ErrorsChanged; - public event PropertyChangedEventHandler PropertyChanged; - - #endregion - - #region Public Functions - - public void AddError(string propertyName, string error) - { - if (!_errors.ContainsKey(propertyName) || _errors[propertyName] != error) - { - _errors[propertyName] = error; - ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); - } - } - - public void RemoveError(string propertyName) - { - if (_errors.Remove(propertyName)) - { - ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); - } - } - - public string GetError(string propertyName) - { - if (_errors.ContainsKey(propertyName)) - { - return _errors[propertyName]; - } - - return null; - } - - public bool HasError(string propertyName) - { - return _errors.ContainsKey(propertyName); - } - - public IEnumerable GetErrors(string propertyName) - { - if (propertyName == null) - return null; - - List err = new List(); - if (_errors.ContainsKey(propertyName)) - { - err.Add(_errors[propertyName]); - } - - return err; - } - - #endregion - - #region Protected Functions - - protected void OnPropertyChanged(string propertyName) - { - PropertyChanged?.Invoke(this, - new PropertyChangedEventArgs(propertyName)); - } - - #endregion - - } -} diff --git a/src/tool/ViewModel/CalibrationViewModel.cs b/src/tool/ViewModel/CalibrationViewModel.cs deleted file mode 100644 index 5287d485..00000000 --- a/src/tool/ViewModel/CalibrationViewModel.cs +++ /dev/null @@ -1,117 +0,0 @@ -using BBSFW.ViewModel.Base; -using System; -using System.Windows; -using System.Windows.Input; - -namespace BBSFW.ViewModel -{ - public class CalibrationViewModel : ObservableObject - { - private ConnectionViewModel _connectionVm; - - private float _batteryStatusVolts; - public float BatteryStatusVolts - { - get { return _batteryStatusVolts; } - set - { - if (_batteryStatusVolts != value) - { - _batteryStatusVolts = value; - OnPropertyChanged(nameof(BatteryStatusVolts)); - } - } - } - - private float _measuredBatteryVolts; - public float MeasuredBatteryVolts - { - get { return _measuredBatteryVolts; } - set - { - if (_measuredBatteryVolts != value) - { - _measuredBatteryVolts = value; - OnPropertyChanged(nameof(MeasuredBatteryVolts)); - } - } - } - - - public ICommand SaveVoltageCommand - { - get { return new DelegateCommand(OnSaveVoltageCalibration); } - } - - public ICommand ResetVoltageCommand - { - get { return new DelegateCommand(OnResetVoltageCalibration); } - } - - - public CalibrationViewModel(ConnectionViewModel connectionVm) - { - _connectionVm = connectionVm; - } - - - private async void OnSaveVoltageCalibration() - { - if (!_connectionVm.IsConnected) - { - MessageBox.Show("Not Connected!", "Error", MessageBoxButton.OK, MessageBoxImage.Error); - return; - } - - if (MeasuredBatteryVolts < 1 || MeasuredBatteryVolts > 100) - { - MessageBox.Show("Measured Battery Voltage must be in range [1, 100]", "Error", MessageBoxButton.OK, MessageBoxImage.Error); - return; - } - - var res = await _connectionVm.GetConnection().CalibrateBatteryVoltage(MeasuredBatteryVolts, TimeSpan.FromSeconds(3)); - if (!res.Timeout) - { - if (res.Result) - { - MessageBox.Show("Voltage calibration saved!", "Success", MessageBoxButton.OK, MessageBoxImage.Information); - } - else - { - MessageBox.Show("Failed to save voltage calibration, check log.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - else - { - MessageBox.Show("Failed to save voltage calibration, timeout occured.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - private async void OnResetVoltageCalibration() - { - if (!_connectionVm.IsConnected) - { - MessageBox.Show("Not Connected!", "Error", MessageBoxButton.OK, MessageBoxImage.Error); - return; - } - - var res = await _connectionVm.GetConnection().CalibrateBatteryVoltage(0f, TimeSpan.FromSeconds(3)); - if (!res.Timeout) - { - if (res.Result) - { - MessageBox.Show("Voltage calibration reset!", "Success", MessageBoxButton.OK, MessageBoxImage.Information); - } - else - { - MessageBox.Show("Failed to reset voltage calibration, check log.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - else - { - MessageBox.Show("Failed to reset voltage calibration, timeout occured.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - } -} diff --git a/src/tool/ViewModel/ConfigurationViewModel.cs b/src/tool/ViewModel/ConfigurationViewModel.cs deleted file mode 100644 index 7112db7b..00000000 --- a/src/tool/ViewModel/ConfigurationViewModel.cs +++ /dev/null @@ -1,643 +0,0 @@ -using BBSFW.Model; -using BBSFW.ViewModel.Base; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace BBSFW.ViewModel -{ - public class ConfigurationViewModel : ObservableObject - { - private Configuration _config; - - public static List PasStartDelayOptions { get; } = - new List() { - 0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180, - 195, 210, 225, 240, 255, 270, 285, 300, 315, 330, 345, 360 - }; - - public static List TemperatureSensorOptions - { - get - { - return Enum.GetValues().ToList(); - } - } - - public static List StartupAssistLevelOptions { get; } = - new List() { 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9 }; - - public static List> AssistModeSelectOptions { get; } = - new List> - { - new ValueItemViewModel(Configuration.AssistModeSelect.Off, "Off"), - new ValueItemViewModel(Configuration.AssistModeSelect.Standard, "Sport Button"), - new ValueItemViewModel(Configuration.AssistModeSelect.Lights, "Lights Button"), - new ValueItemViewModel(Configuration.AssistModeSelect.BrakesOnBoot, "Brakes @ Power On"), - new ValueItemViewModel(Configuration.AssistModeSelect.Pas0AndLights, "PAS 0 + Lights Button"), - new ValueItemViewModel(Configuration.AssistModeSelect.Pas1AndLights, "PAS 1 + Lights Button"), - new ValueItemViewModel(Configuration.AssistModeSelect.Pas2AndLights, "PAS 2 + Lights Button"), - new ValueItemViewModel(Configuration.AssistModeSelect.Pas3AndLights, "PAS 3 + Lights Button"), - new ValueItemViewModel(Configuration.AssistModeSelect.Pas4AndLights, "PAS 4 + Lights Button"), - new ValueItemViewModel(Configuration.AssistModeSelect.Pas5AndLights, "PAS 5 + Lights Button"), - new ValueItemViewModel(Configuration.AssistModeSelect.Pas6AndLights, "PAS 6 + Lights Button"), - new ValueItemViewModel(Configuration.AssistModeSelect.Pas7AndLights, "PAS 7 + Lights Button"), - new ValueItemViewModel(Configuration.AssistModeSelect.Pas8AndLights, "PAS 8 + Lights Button"), - new ValueItemViewModel(Configuration.AssistModeSelect.Pas9AndLights, "PAS 9 + Lights Button"), - }; - - public static List> WalkModeDataDisplayOptions { get; } = - new List> - { - new ValueItemViewModel(Configuration.WalkModeData.Speed, "Speed"), - new ValueItemViewModel(Configuration.WalkModeData.Temperature, "Temperature (C)"), - new ValueItemViewModel(Configuration.WalkModeData.RequestedPower, "Requested Power (%)"), - new ValueItemViewModel(Configuration.WalkModeData.BatteryPercent, "Battery Level (%)") - }; - - public static List> ThrottleGlobalSpeedLimitOptions { get; } = - new List> - { - new ValueItemViewModel(Configuration.ThrottleGlobalSpeedLimitOptions.Disabled, "Disabled"), - new ValueItemViewModel(Configuration.ThrottleGlobalSpeedLimitOptions.Enabled, "Enabled"), - new ValueItemViewModel(Configuration.ThrottleGlobalSpeedLimitOptions.StandardLevels, "Standard Levels"), - }; - - public static List> LightsModeOptions { get; } = - new List> - { - new ValueItemViewModel(Configuration.LightsModeOptions.Default, "Default"), - new ValueItemViewModel(Configuration.LightsModeOptions.Disabled, "Disabled"), - new ValueItemViewModel(Configuration.LightsModeOptions.AlwaysOn, "Always On"), - new ValueItemViewModel(Configuration.LightsModeOptions.BrakeLight, "Brake Light"), - }; - - - // support - - public bool IsTorqueSensorSupported - { - get { return _config.IsFeatureSupported(Configuration.Feature.TorqueSensor); } - } - - public bool IsShiftSensorSupported - { - get { return _config.IsFeatureSupported(Configuration.Feature.ShiftSensor); } - } - - - // configuration - - public bool UseMetricUnits - { - get - { - return !_config.UseFreedomUnits; - } - set - { - if (_config.UseFreedomUnits == value) - { - _config.UseFreedomUnits = !value; - - Properties.Settings.Default.UseFreedomUnits = _config.UseFreedomUnits; - Properties.Settings.Default.Save(); - - OnPropertyChanged(nameof(UseImperialUnits)); - OnPropertyChanged(nameof(UseMetricUnits)); - } - } - } - - public bool UseImperialUnits - { - get - { - return _config.UseFreedomUnits; - } - set - { - if (_config.UseFreedomUnits != value) - { - _config.UseFreedomUnits = value; - - Properties.Settings.Default.UseFreedomUnits = _config.UseFreedomUnits; - Properties.Settings.Default.Save(); - - OnPropertyChanged(nameof(UseImperialUnits)); - OnPropertyChanged(nameof(UseMetricUnits)); - } - } - } - - public uint MaxCurrentAmps - { - get { return _config.MaxCurrentAmps; } - set - { - if (_config.MaxCurrentAmps != value) - { - _config.MaxCurrentAmps = value; - OnPropertyChanged(nameof(MaxCurrentAmps)); - } - } - } - - public uint CurrentRampAmpsSecond - { - get { return _config.CurrentRampAmpsSecond; } - set - { - if (_config.CurrentRampAmpsSecond != value) - { - _config.CurrentRampAmpsSecond = value; - OnPropertyChanged(nameof(CurrentRampAmpsSecond)); - } - } - } - - public float MaxBatteryVolts - { - get { return _config.MaxBatteryVolts; } - set - { - if (_config.MaxBatteryVolts != value) - { - _config.MaxBatteryVolts = value; - OnPropertyChanged(nameof(MaxBatteryVolts)); - } - } - } - - public uint LowCutoffVolts - { - get { return _config.LowCutoffVolts; } - set - { - if (_config.LowCutoffVolts != value) - { - _config.LowCutoffVolts = value; - OnPropertyChanged(nameof(LowCutoffVolts)); - } - } - } - - public uint MaxSpeedKph - { - get { return _config.MaxSpeedKph; } - set - { - if (_config.MaxSpeedKph != value) - { - _config.MaxSpeedKph = value; - OnPropertyChanged(nameof(MaxSpeedKph)); - OnPropertyChanged(nameof(MaxSpeedMph)); - } - } - } - - public uint MaxSpeedMph - { - get { return KphToMph(_config.MaxSpeedKph); } - set - { - if (_config.MaxSpeedKph != MphToKph(value)) - { - _config.MaxSpeedKph = MphToKph(value); - OnPropertyChanged(nameof(MaxSpeedKph)); - OnPropertyChanged(nameof(MaxSpeedMph)); - } - } - } - - public bool UseSpeedSensor - { - get { return _config.UseSpeedSensor; } - set - { - if (_config.UseSpeedSensor != value) - { - _config.UseSpeedSensor = value; - OnPropertyChanged(nameof(UseSpeedSensor)); - } - - // Require use of speed sensor for pretension feature. - if (_config.UseSpeedSensor == false) - { - _config.UsePretension = false; - OnPropertyChanged(nameof(UsePretension)); - } - } - } - - public bool UseShiftSensor - { - get { return _config.UseShiftSensor; } - set - { - if (_config.UseShiftSensor != value) - { - _config.UseShiftSensor = value; - OnPropertyChanged(nameof(UseShiftSensor)); - } - } - } - - public bool UsePushWalk - { - get { return _config.UsePushWalk; } - set - { - if (_config.UsePushWalk != value) - { - _config.UsePushWalk = value; - OnPropertyChanged(nameof(UsePushWalk)); - } - } - } - - public bool UsePretension - { - get { return _config.UsePretension; } - set - { - if (_config.UsePretension != value) - { - _config.UsePretension = value; - OnPropertyChanged(nameof(UsePretension)); - } - } - } - - public uint PretensionSpeedCutoffKph - { - get { return _config.PretensionSpeedCutoffKph; } - set - { - if (_config.PretensionSpeedCutoffKph != value) - { - _config.PretensionSpeedCutoffKph = value; - OnPropertyChanged(nameof(PretensionSpeedCutoffKph)); - OnPropertyChanged(nameof(PretensionSpeedCutoffMph)); - } - } - } - - public uint PretensionSpeedCutoffMph - { - get { return KphToMph(_config.PretensionSpeedCutoffKph); } - set - { - if (_config.PretensionSpeedCutoffKph != MphToKph(value)) - { - _config.PretensionSpeedCutoffKph = MphToKph(value); - OnPropertyChanged(nameof(PretensionSpeedCutoffKph)); - OnPropertyChanged(nameof(PretensionSpeedCutoffMph)); - } - } - } - - public Configuration.TemperatureSensor UseTemperatureSensor - { - get { return _config.UseTemperatureSensor; } - set - { - if (_config.UseTemperatureSensor != value) - { - _config.UseTemperatureSensor = value; - OnPropertyChanged(nameof(UseTemperatureSensor)); - } - } - } - - public ValueItemViewModel LightsMode - { - get - { - return LightsModeOptions.FirstOrDefault((e) => e.Value == _config.LightsMode); - } - set - { - if (_config.LightsMode != value.Value) - { - _config.LightsMode = value.Value; - OnPropertyChanged(nameof(LightsMode)); - } - } - } - - public uint ThrottleStartVoltageMillivolts - { - get { return _config.ThrottleStartMillivolts; } - set - { - if (_config.ThrottleStartMillivolts != value) - { - _config.ThrottleStartMillivolts = value; - OnPropertyChanged(nameof(ThrottleStartVoltageMillivolts)); - } - } - } - - public uint ThrottleEndVoltageMillivolts - { - get { return _config.ThrottleEndMillivolts; } - set - { - if (_config.ThrottleEndMillivolts != value) - { - _config.ThrottleEndMillivolts = value; - OnPropertyChanged(nameof(ThrottleEndVoltageMillivolts)); - } - } - } - - public uint ThrottleStartCurrentPercent - { - get { return _config.ThrottleStartPercent; } - set - { - if (_config.ThrottleStartPercent != value) - { - _config.ThrottleStartPercent = value; - OnPropertyChanged(nameof(ThrottleStartCurrentPercent)); - } - } - } - - public ValueItemViewModel ThrottleGlobalSpeedLimit - { - get - { - return ThrottleGlobalSpeedLimitOptions.FirstOrDefault((e) => e.Value == _config.ThrottleGlobalSpeedLimit); - } - set - { - if (_config.ThrottleGlobalSpeedLimit != value.Value) - { - _config.ThrottleGlobalSpeedLimit = value.Value; - OnPropertyChanged(nameof(ThrottleGlobalSpeedLimit)); - } - } - } - - public uint ThrottleGlobalSpeedLimitPercent - { - get { return _config.ThrottleGlobalSpeedLimitPercent; } - set - { - if (_config.ThrottleGlobalSpeedLimitPercent != value) - { - _config.ThrottleGlobalSpeedLimitPercent = value; - OnPropertyChanged(nameof(ThrottleGlobalSpeedLimitPercent)); - } - } - } - - - - public uint PasStartDelayDegrees - { - get { return _config.PasStartDelayPulses * 15; } - set - { - if (_config.PasStartDelayPulses * 15 != value) - { - _config.PasStartDelayPulses = value / 15; - OnPropertyChanged(nameof(PasStartDelayDegrees)); - } - } - } - - public uint PasStopDelayMilliseconds - { - get { return _config.PasStopDelayMilliseconds; } - set - { - if (_config.PasStopDelayMilliseconds != value) - { - _config.PasStopDelayMilliseconds = value; - OnPropertyChanged(nameof(PasStopDelayMilliseconds)); - } - } - } - - public uint PasKeepCurrentPercent - { - get { return _config.PasKeepCurrentPercent; } - set - { - if (_config.PasKeepCurrentPercent != value) - { - _config.PasKeepCurrentPercent = value; - OnPropertyChanged(nameof(PasKeepCurrentPercent)); - } - } - } - - public uint PasKeepCurrentCadenceRpm - { - get { return _config.PasKeepCurrentCadenceRpm; } - set - { - if (_config.PasKeepCurrentCadenceRpm != value) - { - _config.PasKeepCurrentCadenceRpm = value; - OnPropertyChanged(nameof(PasKeepCurrentCadenceRpm)); - } - } - } - - public float WheelSizeInch - { - get { return _config.WheelSizeInch; } - set - { - if (_config.WheelSizeInch != value) - { - _config.WheelSizeInch = value; - OnPropertyChanged(nameof(WheelSizeInch)); - } - } - } - - public uint SpeedSensorSignals - { - get { return _config.NumWheelSensorSignals; } - set - { - if (_config.NumWheelSensorSignals != value) - { - _config.NumWheelSensorSignals = value; - OnPropertyChanged(nameof(SpeedSensorSignals)); - } - } - } - - public uint ShiftInterruptDuration - { - get { return _config.ShiftInterruptDuration; } - set - { - if (_config.ShiftInterruptDuration != value) - { - _config.ShiftInterruptDuration = value; - OnPropertyChanged(nameof(ShiftInterruptDuration)); - } - } - } - - public uint ShiftInterruptCurrentThresholdPercent - { - get { return _config.ShiftInterruptCurrentThresholdPercent; } - set - { - if (_config.ShiftInterruptCurrentThresholdPercent != value) - { - _config.ShiftInterruptCurrentThresholdPercent = value; - OnPropertyChanged(nameof(ShiftInterruptCurrentThresholdPercent)); - } - } - } - - public ValueItemViewModel WalkModeDataDisplay - { - get - { - return WalkModeDataDisplayOptions.FirstOrDefault((e) => e.Value == _config.WalkModeDataDisplay); - } - set - { - if (_config.WalkModeDataDisplay != value.Value) - { - _config.WalkModeDataDisplay = value.Value; - OnPropertyChanged(nameof(WalkModeDataDisplay)); - } - } - } - - - public uint StartupAssistLevel - { - get { return _config.AssistStartupLevel; } - set - { - if (_config.AssistStartupLevel != value) - { - _config.AssistStartupLevel = value; - OnPropertyChanged(nameof(StartupAssistLevel)); - } - } - } - - public ValueItemViewModel AssistModeSelection - { - get - { - return AssistModeSelectOptions.FirstOrDefault((e) => e.Value == _config.AssistModeSelection); - } - set - { - if (_config.AssistModeSelection != value.Value) - { - _config.AssistModeSelection = value.Value; - OnPropertyChanged(nameof(AssistModeSelection)); - } - } - } - - private List _standardAssistLevels; - public List StandardAssistLevels - { - get { return _standardAssistLevels; } - private set - { - if (_standardAssistLevels != value) - { - _standardAssistLevels = value; - OnPropertyChanged(nameof(StandardAssistLevels)); - } - } - } - - private List _sportAssistLevels; - - public List SportAssistLevels - { - get { return _sportAssistLevels; } - private set - { - if (_sportAssistLevels != value) - { - _sportAssistLevels = value; - OnPropertyChanged(nameof(SportAssistLevels)); - } - } - } - - public ConfigurationViewModel() - { - _config = new Configuration(BbsfwConnection.Controller.Unknown); - - StandardAssistLevels = new List(); - SportAssistLevels = new List(); - - for (int i = 0; i < _config.StandardAssistLevels.Length; ++i) - { - _standardAssistLevels.Add(new AssistLevelViewModel(this, i, _config.StandardAssistLevels[i])); - } - - for (int i = 0; i < _config.SportAssistLevels.Length; ++i) - { - _sportAssistLevels.Add(new AssistLevelViewModel(this, i, _config.SportAssistLevels[i])); - } - } - - public void ReadConfiguration(string filepath) - { - _config.ReadFromFile(filepath); - TriggerPropertyChanges(); - } - - public void WriteConfiguration(string filepath) - { - _config.WriteToFile(filepath); - } - - public void UpdateFrom(Configuration config) - { - _config.CopyFrom(config); - TriggerPropertyChanges(); - } - - public Configuration GetConfig() - { - return _config; - } - - private static uint KphToMph(uint kph) - { - return (uint)Math.Round(kph * 0.621371192); - } - - private static uint MphToKph(uint mph) - { - return (uint)Math.Round(mph * 1.609344); - } - - private void TriggerPropertyChanges() - { - foreach (var prop in typeof(ConfigurationViewModel).GetProperties()) - { - if (prop.GetGetMethod(false) != null) - { - OnPropertyChanged(prop.Name); - } - } - - // force update by creating new list - StandardAssistLevels = StandardAssistLevels.ToList(); - SportAssistLevels = SportAssistLevels.ToList(); - } - } -} diff --git a/src/tool/ViewModel/ConnectionViewModel.cs b/src/tool/ViewModel/ConnectionViewModel.cs deleted file mode 100644 index 003fe9d4..00000000 --- a/src/tool/ViewModel/ConnectionViewModel.cs +++ /dev/null @@ -1,233 +0,0 @@ -using BBSFW.Model; -using BBSFW.ViewModel.Base; -using System; -using System.Collections.Generic; -using System.Windows; -using System.Windows.Input; - -namespace BBSFW.ViewModel -{ - public class ConnectionViewModel : ObservableObject - { - - private BbsfwConnection _connection; - - private List _comPorts; - public List ComPorts - { - get { return _comPorts; } - set - { - if (_comPorts != value) - { - _comPorts = value; - OnPropertyChanged(nameof(ComPorts)); - } - } - } - - private ComPort _selectedComPort; - public ComPort SelectedComPort - { - get { return _selectedComPort; } - set - { - if (_selectedComPort != value) - { - _selectedComPort = value; - OnPropertyChanged(nameof(SelectedComPort)); - } - } - } - - private bool _isConnected; - public bool IsConnected - { - get { return _isConnected; } - set - { - if (_isConnected != value) - { - _isConnected = value; - OnPropertyChanged(nameof(IsConnected)); - OnPropertyChanged(nameof(IsDisconnected)); - } - } - } - - public bool IsDisconnected - { - get - { - return !IsConnected; - } - } - - private bool _isConnecting; - public bool IsConnecting - { - get { return _isConnecting; } - set - { - if (_isConnecting != value) - { - _isConnecting = value; - OnPropertyChanged(nameof(IsConnecting)); - } - } - } - - - private BbsfwConnection.Controller _controller; - public BbsfwConnection.Controller Controller - { - get { return _controller; } - set - { - if (_controller != value) - { - _controller = value; - OnPropertyChanged(nameof(Controller)); - } - } - } - - private string _firmwareVersion = "N/A"; - public string FirmwareVersion - { - get - { - return _firmwareVersion; - } - private set - { - if (_firmwareVersion != value) - { - _firmwareVersion = value; - OnPropertyChanged(nameof(FirmwareVersion)); - } - } - } - - private int _configVersion = 0; - public int ConfigVersion - { - get - { - return _configVersion; - } - private set - { - if (_configVersion != value) - { - _configVersion = value; - OnPropertyChanged(nameof(ConfigVersion)); - } - } - } - - - public event Action EventLogReceived; - - public ICommand RefreshCommand - { - get - { - return new DelegateCommand(OnRefresh); - } - } - - - public ICommand ConnectCommand - { - get - { - return new DelegateCommand(OnConnect); - } - } - - public ICommand DisconnectCommand - { - get - { - return new DelegateCommand(OnDisconnect); - } - } - - public ConnectionViewModel() - { - _connection = new BbsfwConnection(); - - _connection.Connected += OnConnected; - _connection.Disconnected += OnDisconnected; - _connection.EventLog += (e) => - { - EventLogReceived?.Invoke(e); - }; - - - ComPorts = BbsfwConnection.GetComPorts(); - } - - - public BbsfwConnection GetConnection() - { - return _connection; - } - - - private void OnConnected(BbsfwConnection.Controller controller, string fwversion, int configVersion) - { - IsConnected = true; - IsConnecting = false; - - Controller = controller; - FirmwareVersion = fwversion; - ConfigVersion = configVersion; - } - - private void OnDisconnected() - { - IsConnected = false; - IsConnecting = false; - FirmwareVersion = "N/A"; - ConfigVersion = 0; - } - - private void OnRefresh() - { - ComPorts = BbsfwConnection.GetComPorts(); - OnPropertyChanged(nameof(ComPorts)); - } - - private async void OnConnect() - { - if (SelectedComPort != null) - { - IsConnecting = true; - - try - { - var connected = await _connection.Connect(SelectedComPort, TimeSpan.FromSeconds(120)); - - if (!connected) - { - MessageBox.Show("Failed to connect, timeout occured.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - catch (Exception ex) - { - MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); - IsConnected = false; - IsConnecting = false; - } - } - } - - private void OnDisconnect() - { - _connection.Close(); - } - - } -} diff --git a/src/tool/ViewModel/EventLogViewModel.cs b/src/tool/ViewModel/EventLogViewModel.cs deleted file mode 100644 index a4ddabf8..00000000 --- a/src/tool/ViewModel/EventLogViewModel.cs +++ /dev/null @@ -1,152 +0,0 @@ -using BBSFW.Model; -using BBSFW.ViewModel.Base; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.IO; -using System.Windows; -using System.Windows.Data; -using System.Windows.Input; - -namespace BBSFW.ViewModel -{ - public class EventLogViewModel : ObservableObject - { - - private ObservableCollection _events = new ObservableCollection(); - public ObservableCollection LogEvents - { - get { return _events; } - set - { - if (_events != value) - { - _events = value; - OnPropertyChanged(nameof(LogEvents)); - } - } - } - - private ICollectionView _filtedLogEvents; - public ICollectionView FilteredLogEvents - { - get { return _filtedLogEvents; } - } - - - public IEnumerable AvailableLogLevels - { - get { return new[] { EventLogEntry.LogLevel.Info, EventLogEntry.LogLevel.Warning, EventLogEntry.LogLevel.Error }; } - } - - private EventLogEntry.LogLevel _selectedLogLevel; - public EventLogEntry.LogLevel SelectedLogLevel - { - get { return _selectedLogLevel; } - set - { - if (_selectedLogLevel != value) - { - _selectedLogLevel = value; - OnPropertyChanged(nameof(SelectedLogLevel)); - - FilteredLogEvents.Refresh(); - } - } - } - - private string _filterText; - public string FilterText - { - get { return _filterText; } - set - { - if (_filterText != value) - { - _filterText = value; - OnPropertyChanged(nameof(FilterText)); - _filtedLogEvents.Refresh(); - } - } - } - - - private bool _tailLog; - public bool TailLog - { - get { return _tailLog; } - set - { - if (_tailLog != value) - { - _tailLog = value; - OnPropertyChanged(nameof(TailLog)); - } - } - } - - - - public ICommand ClearCommand - { - get { return new DelegateCommand(OnClear); } - } - - - public EventLogViewModel() - { - _filtedLogEvents = (CollectionView)CollectionViewSource.GetDefaultView(LogEvents); - _filtedLogEvents.Filter += OnFilterTriggered; - } - - - - public void AddEvent(EventLogEntry e) - { - Application.Current.Dispatcher.InvokeAsync(() => LogEvents.Add(e)); - } - - public void ExportLog(string filepath) - { - using (var writer = new StreamWriter(filepath)) - { - foreach (var e in LogEvents) - { - writer.WriteLine($"{e.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff")} {e.Level} {e.Message}"); - } - } - } - - - - private bool OnFilterTriggered(object obj) - { - var e = obj as EventLogEntry; - - if (e != null) - { - if (e.Level >= SelectedLogLevel) - { - if (String.IsNullOrEmpty(FilterText)) - { - return true; - } - - if (e.Message.IndexOf(FilterText, StringComparison.OrdinalIgnoreCase) >= 0) - { - return true; - } - } - } - - return false; - } - - - private void OnClear() - { - LogEvents.Clear(); - } - } -} diff --git a/src/tool/ViewModel/MainViewModel.cs b/src/tool/ViewModel/MainViewModel.cs deleted file mode 100644 index e872a698..00000000 --- a/src/tool/ViewModel/MainViewModel.cs +++ /dev/null @@ -1,288 +0,0 @@ -using BBSFW.Model; -using BBSFW.ViewModel.Base; -using Microsoft.Win32; -using System; -using System.Reflection; -using System.Windows; -using System.Windows.Input; - -namespace BBSFW.ViewModel -{ - - public class MainViewModel : ObservableObject - { - - public ConfigurationViewModel ConfigVm { get; private set; } - - public ConnectionViewModel ConnectionVm { get; private set; } - - public SystemViewModel SystemVm { get; private set; } - - public AssistLevelsViewModel AssistLevelsVm { get; private set; } - - public CalibrationViewModel CalibrationVm { get; private set; } - - public EventLogViewModel EventLogVm { get; private set; } - - - - public ICommand OpenConfigCommand - { - get { return new DelegateCommand(OnOpenConfig); } - } - - public ICommand SaveConfigCommand - { - get { return new DelegateCommand(OnSaveConfig); } - } - - public ICommand SaveLogCommand - { - get { return new DelegateCommand(OnSaveLog); } - } - - public ICommand ReadFlashCommand - { - get { return new DelegateCommand(OnReadFlash); } - } - - public ICommand WriteFlashCommand - { - get { return new DelegateCommand(OnWriteFlash); } - } - - public ICommand ResetFlashCommand - { - get { return new DelegateCommand(OnResetFlash); } - } - - public ICommand ExitCommand - { - get { return new DelegateCommand(OnExit); } - } - - public ICommand ShowAboutCommand - { - get { return new DelegateCommand(OnShowAbout); } - } - - - - public MainViewModel() - { - ConfigVm = new ConfigurationViewModel(); - - ConnectionVm = new ConnectionViewModel(); - SystemVm = new SystemViewModel(ConfigVm); - AssistLevelsVm = new AssistLevelsViewModel(ConfigVm); - CalibrationVm = new CalibrationViewModel(ConnectionVm); - EventLogVm = new EventLogViewModel(); - - - ConnectionVm.EventLogReceived += EventLogVm.AddEvent; - } - - - private void OnSaveLog() - { - var dialog = new SaveFileDialog(); - - dialog.Filter = "Log File|*.log"; - dialog.Title = "Save Log"; - dialog.FileName = "bbsfw.log"; - - var result = dialog.ShowDialog(); - if (result.HasValue && result.Value) - { - try - { - EventLogVm.ExportLog(dialog.FileName); - } - catch(Exception e) - { - MessageBox.Show(e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - } - - private void OnOpenConfig() - { - var dialog = new OpenFileDialog(); - dialog.Filter = "XML File|*.xml"; - dialog.Title = "Open Configuration"; - - var result = dialog.ShowDialog(); - if (result.HasValue && result.Value) - { - try - { - ConfigVm.ReadConfiguration(dialog.FileName); - } - catch (Exception e) - { - MessageBox.Show(e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - } - - private void OnSaveConfig() - { - if (!ValidateConfig()) - { - return; - } - - var dialog = new SaveFileDialog(); - - dialog.Filter = "XML File|*.xml"; - dialog.Title = "Save Configuration"; - dialog.FileName = "bbsfw.xml"; - - var result = dialog.ShowDialog(); - if (result.HasValue && result.Value) - { - try - { - ConfigVm.WriteConfiguration(dialog.FileName); - } - catch (Exception e) - { - MessageBox.Show(e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - } - - private async void OnReadFlash() - { - if (!ConnectionVm.IsConnected) - { - return; - } - - if (!VerifyConfigVersionForRead()) - { - return; - } - - var res = await ConnectionVm.GetConnection().ReadConfiguration(TimeSpan.FromSeconds(5)); - if (!res.Timeout && res.Result != null) - { - ConfigVm.UpdateFrom(res.Result); - } - else - { - MessageBox.Show("Failed to read configuration from flash, timeout occured.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - private async void OnWriteFlash() - { - if (!ConnectionVm.IsConnected) - { - return; - } - - if (!ValidateConfig()) - { - return; - } - - if (!VerifyConfigVersionForWrite()) - { - return; - } - - var res = await ConnectionVm.GetConnection().WriteConfiguration(ConfigVm.GetConfig(), TimeSpan.FromSeconds(5)); - if (!res.Timeout) - { - if (res.Result) - { - MessageBox.Show("Configuration Written!", "Success", MessageBoxButton.OK, MessageBoxImage.Information); - } - else - { - MessageBox.Show("Failed to write configuration to flash, try again.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - else - { - MessageBox.Show("Failed to write configuration to flash, timeout occured.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - private async void OnResetFlash() - { - if (!ConnectionVm.IsConnected) - { - return; - } - - var res = await ConnectionVm.GetConnection().ResetConfiguration(TimeSpan.FromSeconds(5)); - if (!res.Timeout) - { - if (res.Result) - { - OnReadFlash(); - } - else - { - MessageBox.Show("Failed to reset configuration, try again.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - else - { - MessageBox.Show("Failed to reset configuration, timeout occured.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - } - - - private void OnShowAbout() - { - var version = Assembly.GetExecutingAssembly().GetName().Version; - MessageBox.Show($"Version: {version.Major}.{version.Minor}.{version.Build}\nAuthor: Daniel Nilsson", "BBS-FW Tool", MessageBoxButton.OK, MessageBoxImage.Information); - } - - private void OnExit() - { - Application.Current.Shutdown(); - } - - private bool VerifyConfigVersionForRead() - { - if (ConnectionVm.ConfigVersion < Configuration.MinVersion || ConnectionVm.ConfigVersion > Configuration.MaxVersion) - { - MessageBox.Show("Unsupported firmware config version. Please use BBS-FW Config Tool for firmware version " + ConnectionVm.FirmwareVersion + " to read configuration from flash.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); - return false; - } - - return true; - } - - private bool VerifyConfigVersionForWrite() - { - if (ConnectionVm.ConfigVersion != Configuration.CurrentVersion) - { - MessageBox.Show("Unsupported firmware config version. Please use BBS-FW Config Tool for firmware version " + ConnectionVm.FirmwareVersion + " in order to write configuration to flash, or upgrade firmware to latest version.", "Error", MessageBoxButton.OK, MessageBoxImage.Error); - return false; - } - - return true; - } - - private bool ValidateConfig() - { - try - { - ConfigVm.GetConfig().Validate(); - return true; - } - catch (Exception e) - { - MessageBox.Show(e.Message, "Validation Error", MessageBoxButton.OK, MessageBoxImage.Error); - } - - return false; - } - - } -} diff --git a/src/tool/ViewModel/SystemViewModel.cs b/src/tool/ViewModel/SystemViewModel.cs deleted file mode 100644 index 2dd79a6c..00000000 --- a/src/tool/ViewModel/SystemViewModel.cs +++ /dev/null @@ -1,24 +0,0 @@ -using BBSFW.ViewModel.Base; -using System; -using System.Collections.Generic; -using System.Text; - -namespace BBSFW.ViewModel -{ - public class SystemViewModel : ObservableObject - { - - - private ConfigurationViewModel _configVm; - public ConfigurationViewModel ConfigVm - { - get { return _configVm; } - } - - public SystemViewModel(ConfigurationViewModel config) - { - _configVm = config; - } - - } -} diff --git a/src/tool/ViewModel/ValueItemViewModel.cs b/src/tool/ViewModel/ValueItemViewModel.cs deleted file mode 100644 index 4e11d3a6..00000000 --- a/src/tool/ViewModel/ValueItemViewModel.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; - -namespace BBSFW.ViewModel -{ - public class ValueItemViewModel : IEquatable> - { - public T Value { get; private set; } - - - public string Name { get; private set; } - - - public ValueItemViewModel(T value, string name) - { - Value = value; - Name = name; - } - - public static implicit operator T(ValueItemViewModel v) => v.Value; - - - public override string ToString() - { - return Name; - } - - public bool Equals([AllowNull] ValueItemViewModel other) - { - return Value.Equals(other.Value); - } - } -}