From cc76116a59eed4fc25334045b4674adc7013bf10 Mon Sep 17 00:00:00 2001 From: preranaK007 <86585407+preranaK007@users.noreply.github.com> Date: Tue, 29 Oct 2024 20:41:04 -0700 Subject: [PATCH 1/2] Create Battery Balance & Management Board Specifications - Firmware.pdf --- ...nagement Board Specifications - Firmware.pdf | Bin 0 -> 62357 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 battery management board 2025/Battery Balance & Management Board Specifications - Firmware.pdf diff --git a/battery management board 2025/Battery Balance & Management Board Specifications - Firmware.pdf b/battery management board 2025/Battery Balance & Management Board Specifications - Firmware.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4296154e2d5cf2be44b1bc08e92a217f21503db4 GIT binary patch literal 62357 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

xiwoJKoHbCck@6?n}=0!|QXQ=1+HRF68hn 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 Pz)>g=FR!w;3g~|T{IPYk literal 0 HcmV?d00001 From b592025b305e016d1b0ecb4838433d76d1a84854 Mon Sep 17 00:00:00 2001 From: preranaK007 <86585407+preranaK007@users.noreply.github.com> Date: Tue, 5 Nov 2024 18:56:49 -0800 Subject: [PATCH 2/2] loading last year's code --- .../Battery Management Board-000.cywrk | 22 + .../Battery Management Board.cydwr | 4063 +++++++++++++++++ .../Battery Management Board.cyprj | 2230 +++++++++ .../CAN_Stuff.c | 96 + .../CAN_Stuff.h | 33 + .../FSM_Stuff.c | 36 + .../FSM_Stuff.h | 35 + .../HindsightCAN/.gitignore | 55 + .../HindsightCAN/CANCommon.c | 260 ++ .../HindsightCAN/CANCommon.h | 137 + .../HindsightCAN/CANGPIO.c | 170 + .../HindsightCAN/CANGPIO.h | 91 + .../HindsightCAN/CANLibrary.h | 21 + .../HindsightCAN/CANLocalization.c | 9 + .../HindsightCAN/CANLocalization.h | 12 + .../HindsightCAN/CANMotorUnit.c | 271 ++ .../HindsightCAN/CANMotorUnit.h | 212 + .../HindsightCAN/CANPacket.c | 228 + .../HindsightCAN/CANPacket.h | 95 + .../HindsightCAN/CANPower.c | 50 + .../HindsightCAN/CANPower.h | 42 + .../HindsightCAN/CANScience.c | 59 + .../HindsightCAN/CANScience.h | 136 + .../HindsightCAN/CANSerialNumbers.h | 68 + .../HindsightCAN/CMakeLists.txt | 106 + .../HindsightCAN/Port.h | 52 + .../HindsightCAN/PortFiles/PortAT90CANxxx.c | 233 + .../PortFiles/PortPSOC_CY8C4248AZI_L485.c | 202 + .../HindsightCAN/PortFiles/PortTemplate.c | 39 + .../HindsightCAN/README.md | 37 + .../cmake/HindsightCANConfig.cmake.in | 10 + .../Battery Management Board.cydsn/INA226.c | 221 + .../Battery Management Board.cydsn/INA226.h | 65 + .../TopDesign/TopDesign.cysch | Bin 0 -> 124052 bytes .../Battery Management Board.cydsn/battery.c | 125 + .../cyapicallbacks.h | 21 + .../Battery Management Board.cydsn/io.h | 42 + .../Battery Management Board.cydsn/main.c | 175 + .../Battery Management Board.cydsn/main.h | 33 + .../Battery Management Board.cydsn/main_old.c | 58 + 40 files changed, 9850 insertions(+) create mode 100644 battery management board 2025/Battery Management Board.cydsn/Battery Management Board-000.cywrk create mode 100644 battery management board 2025/Battery Management Board.cydsn/Battery Management Board.cydwr create mode 100644 battery management board 2025/Battery Management Board.cydsn/Battery Management Board.cyprj create mode 100644 battery management board 2025/Battery Management Board.cydsn/CAN_Stuff.c create mode 100644 battery management board 2025/Battery Management Board.cydsn/CAN_Stuff.h create mode 100644 battery management board 2025/Battery Management Board.cydsn/FSM_Stuff.c create mode 100644 battery management board 2025/Battery Management Board.cydsn/FSM_Stuff.h create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/.gitignore create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANCommon.c create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANCommon.h create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANGPIO.c create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANGPIO.h create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANLibrary.h create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANLocalization.c create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANLocalization.h create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANMotorUnit.c create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANMotorUnit.h create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANPacket.c create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANPacket.h create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANPower.c create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANPower.h create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANScience.c create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANScience.h create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANSerialNumbers.h create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CMakeLists.txt create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/Port.h create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/PortFiles/PortAT90CANxxx.c create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/PortFiles/PortPSOC_CY8C4248AZI_L485.c create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/PortFiles/PortTemplate.c create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/README.md create mode 100644 battery management board 2025/Battery Management Board.cydsn/HindsightCAN/cmake/HindsightCANConfig.cmake.in create mode 100644 battery management board 2025/Battery Management Board.cydsn/INA226.c create mode 100644 battery management board 2025/Battery Management Board.cydsn/INA226.h create mode 100644 battery management board 2025/Battery Management Board.cydsn/TopDesign/TopDesign.cysch create mode 100644 battery management board 2025/Battery Management Board.cydsn/battery.c create mode 100644 battery management board 2025/Battery Management Board.cydsn/cyapicallbacks.h create mode 100644 battery management board 2025/Battery Management Board.cydsn/io.h create mode 100644 battery management board 2025/Battery Management Board.cydsn/main.c create mode 100644 battery management board 2025/Battery Management Board.cydsn/main.h create mode 100644 battery management board 2025/Battery Management Board.cydsn/main_old.c diff --git a/battery management board 2025/Battery Management Board.cydsn/Battery Management Board-000.cywrk b/battery management board 2025/Battery Management Board.cydsn/Battery Management Board-000.cywrk new file mode 100644 index 0000000..9629756 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/Battery Management Board-000.cywrk @@ -0,0 +1,22 @@ + + + + + + + + + +.\Battery Management Board.cyprj + + + + + + + + + + + + \ No newline at end of file diff --git a/battery management board 2025/Battery Management Board.cydsn/Battery Management Board.cydwr b/battery management board 2025/Battery Management Board.cydsn/Battery Management Board.cydwr new file mode 100644 index 0000000..0f96124 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/Battery Management Board.cydwr @@ -0,0 +1,4063 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/battery management board 2025/Battery Management Board.cydsn/Battery Management Board.cyprj b/battery management board 2025/Battery Management Board.cydsn/Battery Management Board.cyprj new file mode 100644 index 0000000..f6002ed --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/Battery Management Board.cyprj @@ -0,0 +1,2230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/battery management board 2025/Battery Management Board.cydsn/CAN_Stuff.c b/battery management board 2025/Battery Management Board.cydsn/CAN_Stuff.c new file mode 100644 index 0000000..a38e8e2 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/CAN_Stuff.c @@ -0,0 +1,96 @@ +/* ======================================== + * + * Copyright YOUR COMPANY, THE YEAR + * All Rights Reserved + * UNPUBLISHED, LICENSED SOFTWARE. + * + * CONFIDENTIAL AND PROPRIETARY INFORMATION + * WHICH IS THE PROPERTY OF your company. + * + * ======================================== +*/ + +#include +#include "main.h" +#include "CAN_Stuff.h" +#include "FSM_Stuff.h" +#include "HindsightCAN/CANLibrary.h" + +extern char txData[TX_DATA_SIZE]; +extern uint8 address; + +//Reads from CAN FIFO and changes the state and mode accordingly +int ProcessCAN(CANPacket* receivedPacket, CANPacket* packetToSend) { + uint16_t packageID = GetPacketID(receivedPacket); + uint8_t sender_DG = GetSenderDeviceGroupCode(receivedPacket); + uint8_t sender_SN = GetSenderDeviceSerialNumber(receivedPacket); + int32_t data = 0; + int32_t Michael = 0; + int err = 0; + + switch(packageID){ + // Board-specific packets + case(ID_MOTOR_UNIT_MODE_SEL): + data = GetModeFromPacket(receivedPacket); + + if(data == KENMODE) { + SetModeTo(KENMODE); + // initialize MODE1 + } else { + err = ERROR_INVALID_MODE; + } + break; + + // Common Packets + case(ID_ESTOP): + Print("\r\n\r\nSTOP\r\n\r\n"); + // stop all movement + GotoUninitState(); + err = ESTOP_ERR_GENERAL; + break; + + case(ID_TELEMETRY_PULL): + switch(DecodeTelemetryType(receivedPacket)) + { + // USE CONSTANTS FOR CASES + case(0): + data = 105; + break; + case(TT_CURRENT): + Michael = 0x01; + SetModeTo(KENMODE); + default: + err = ERROR_INVALID_TTC; + break; + } + + if (!Michael) { + // Assemble and send packet + AssembleTelemetryReportPacket(packetToSend, sender_DG, sender_SN, receivedPacket->data[3], data); + if (err == 0) + SendCANPacket(packetToSend); + } + Michael = 0; + break; + + default: //recieved Packet with non-valid ID + // could be due to corruption, don't uninit + return ERROR_INVALID_PACKET; + } + + return err; +} + +void PrintCanPacket(CANPacket packet){ + for(int i = 0; i < packet.dlc; i++ ) { + sprintf(txData,"Byte%d %x ", i+1, packet.data[i]); + Print(txData); + } + + sprintf(txData,"ID:%x %x %x\r\n",packet.id >> 10, + (packet.id >> 6) & 0xF , packet.id & 0x3F); + Print(txData); +} + + +/* [] END OF FILE */ \ No newline at end of file diff --git a/battery management board 2025/Battery Management Board.cydsn/CAN_Stuff.h b/battery management board 2025/Battery Management Board.cydsn/CAN_Stuff.h new file mode 100644 index 0000000..fc17745 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/CAN_Stuff.h @@ -0,0 +1,33 @@ +/* ======================================== + * + * Copyright YOUR COMPANY, THE YEAR + * All Rights Reserved + * UNPUBLISHED, LICENSED SOFTWARE. + * + * CONFIDENTIAL AND PROPRIETARY INFORMATION + * WHICH IS THE PROPERTY OF your company. + * + * ======================================== +*/ + +#pragma once + +#include +#include "HindsightCAN/CANLibrary.h" + +// CAN Errors (0x10-0x1F) +#define ERROR_NO_NEW_PACKET 0xFFFF +#define ERROR_WRONG_MODE 0x10 +#define ERROR_INVALID_MODE 0x11 +#define ERROR_INVALID_TTC 0x12 +#define ERROR_INVALID_PACKET 0x13 + +#define TT_CURRENT 0x1 + +int ReadCAN(CANPacket *receivedPacket); +int ProcessCAN(CANPacket* receivedPacket, CANPacket* packetToSend); +void PrintCanPacket(CANPacket packet); + + +/* [] END OF FILE */ + \ No newline at end of file diff --git a/battery management board 2025/Battery Management Board.cydsn/FSM_Stuff.c b/battery management board 2025/Battery Management Board.cydsn/FSM_Stuff.c new file mode 100644 index 0000000..c5f989c --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/FSM_Stuff.c @@ -0,0 +1,36 @@ +/* ======================================== + * + * Copyright YOUR COMPANY, THE YEAR + * All Rights Reserved + * UNPUBLISHED, LICENSED SOFTWARE. + * + * CONFIDENTIAL AND PROPRIETARY INFORMATION + * WHICH IS THE PROPERTY OF your company. + * + * ======================================== +*/ + +#include "FSM_Stuff.h" +#include "project.h" + +uint8_t currentState = UNINIT; +uint8_t currentMode = 0xFF; + +void GotoUninitState() { + currentState = UNINIT; + // reset any parameters +} +void SetStateTo(uint8_t state) { + currentState = state; +} +void SetModeTo(uint8_t mode) { + currentMode = mode; +} +uint8_t GetState(){ + return currentState; +} +uint8_t GetMode(){ + return currentMode; +} + +/* [] END OF FILE */ \ No newline at end of file diff --git a/battery management board 2025/Battery Management Board.cydsn/FSM_Stuff.h b/battery management board 2025/Battery Management Board.cydsn/FSM_Stuff.h new file mode 100644 index 0000000..ef2962c --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/FSM_Stuff.h @@ -0,0 +1,35 @@ +/* ======================================== + * + * Copyright YOUR COMPANY, THE YEAR + * All Rights Reserved + * UNPUBLISHED, LICENSED SOFTWARE. + * + * CONFIDENTIAL AND PROPRIETARY INFORMATION + * WHICH IS THE PROPERTY OF your company. + * + * ======================================== +*/ + +#pragma once +#include + +//States in FSM +#define UNINIT 0x0 +#define CHECK_CAN 0x1 +#define DO_KENMODE 0x2 + +//Operation modes +#define KENMODE 0x2 +#define MRBEASTMODE 0xFF + +// FSM Errors (0x20-0x2F) +#define ERROR_ESTOP 0x20 +#define ERROR_INVALID_STATE 0x21 + +void GotoUninitState(); +void SetStateTo(uint8_t state); +void SetModeTo(uint8_t mode); +uint8_t GetState(); +uint8_t GetMode(); + +/* [] END OF FILE */ \ No newline at end of file diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/.gitignore b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/.gitignore new file mode 100644 index 0000000..dcb3349 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/.gitignore @@ -0,0 +1,55 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf +.vscode/c_cpp_properties.json +.vscode/tasks.json +/build/ diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANCommon.c b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANCommon.c new file mode 100644 index 0000000..2c0662a --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANCommon.c @@ -0,0 +1,260 @@ +/* File: CANCommon.c + * Authors: Jaden Bottemiller, Benton Kwong, Dylan Tomberlin. + * Organization: Husky Robotics Team + * + * This file includes fuction definitions for Common Mode CAN Communication + * using the Hindsight CAN Communication standard. + * Documentation: https://huskyroboticsteam.slite.com/app/channels/iU0BryG7M9/collections/aXvWTcIR6c/notes/4otlSFsSp2 + */ + +#include "CANPacket.h" +#include "CANCommon.h" +#include "Port.h" +// Assembles Emergency Stop Packet with given parameters +// Inputs: +// packet: CAN Packet to assemble (will overwrite). +// targetDeviceGroup: Group to target +// targetDeviceSerialNumber: Serial number of target device +// errorCode: Emergency stop error code. E.G. ESTOP_ERR_GENERAL +void AssembleEmergencyStopPacket(CANPacket *packet, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerialNumber, + uint8_t errorCode) +{ + packet->dlc = DLC_ESTOP; + packet->id = ConstructCANID(PACKET_PRIORITY_HIGH, targetDeviceGroup, targetDeviceSerialNumber); + int nextByte = WriteSenderSerialAndPacketID(packet->data, ID_ESTOP); + packet->data[nextByte] = errorCode; +} + +// Assembles Emergency Stop Packet with given parameters. +// This will broadcast the emergency stop command to a desired device group. +// Inputs: +// packet: CAN Packet to assemble (will overwrite). +// deviceGroup: Group to target +// errorCode: Emergency stop error code. E.G. ESTOP_ERR_GENERAL +void AssembleGroupBroadcastingEmergencyStopPacket(CANPacket *packet, + uint8_t groupCode, + uint8_t errorCode) +{ + AssembleEmergencyStopPacket(packet, groupCode, DEVICE_SERIAL_BROADCAST, errorCode); +} + +// Assembles Emergency Stop Packet with given parameters. +// This will broadcast the emergency stop command to all devices +// Inputs: +// packet: CAN Packet to assemble (will overwrite). +// errorCode: Emergency stop error code. E.G. ESTOP_ERR_GENERAL +void AssembleBrodcastEmergencyStopPacket(CANPacket *packet, + uint8_t errorCode) +{ + AssembleGroupBroadcastingEmergencyStopPacket(packet, DEVICE_GROUP_BROADCAST, errorCode); +} + +// Gets the Error Code reported from an emergency stop packet. +// Inputs: +// packet: Packet to check. +uint8_t GetEmergencyStopErrorCode(CANPacket *packet) +{ + if (PacketIsOfID(packet, ID_ESTOP)) + { + return packet->data[2]; + } + else { return -1; } +} + +// Validates the Heartbeat Packet, returns time between previous Heartbeat packets +// Inputs: +// packet: CAN Packet to check +// lastHeartbeat: Timestamp (ms) of last detected heartbeat +// Outputs: +// Time (in ms) between this heartbeat and the previous detected heartbeat +// Negative value if packet is not valid heartbeat packet +uint32_t GetTimeBetweenHeartbeatPacket(CANPacket *packet, uint32_t lastHeartbeat) +{ + if (PacketIsOfID(packet, ID_HEARTBEAT)) + { + return GetHeartbeatTimeStamp(packet) - lastHeartbeat; + } + else { return -1; } +} + +// Validates the Heartbeat Packet, returns time between previous Heartbeat packets +// Inputs: +// packet: CAN Packet to check +// lastHeartbeat: Timestamp (ms) of last detected heartbeat +// Outputs: +// Time (in ms) of the timestamp within the packet +// Default return value is uint32 max value, which is used +// if the packet is corrupt or not a heartbeat packet. +uint32_t GetHeartbeatTimeStamp(CANPacket *packet) +{ + if (PacketIsOfID(packet, ID_HEARTBEAT)) + { + uint32_t time = ((uint32_t)packet->data[3] << 24); + time |= ((uint32_t)packet->data[4] << 16); + time |= ((uint32_t)packet->data[5] << 8); + time |= packet->data[6]; + return time; + } + else { return -1; } +} + +// Validates the Heartbeat Packet, returns the heartbeat leniency code of the packet +// Inputs: +// packet: CAN Packet to check +// Outputs: +// Heartbeat leniency code of given packet +uint8_t GetHeartbeatLeniencyCode(CANPacket *packet) +{ + if (PacketIsOfID(packet, ID_HEARTBEAT)) + { + return packet->data[2]; + } else { + return 0x00; + } +} + +// Assembles Heartbeat Packet with given parameters +// Inputs: +// packetToAssemble: CAN Packet to assemble (will overwrite). +// broadcast: 1 if broadcast to all devices. 0 to return to MAIN_CPU / Jetson. +// heartbeatLeniencyCode: Max time between heartbeats before system automatically enters a safe operating condition. +// timestamp: Current timestamp as seen by the sender device. (ms) +//TODO, upon approval from @jaden, delete senderGroup and senderSerial params, as these are handled by getLocal functs +void AssembleHeartbeatPacket(CANPacket *packetToAssemble, + int broadcast, + uint8_t heartbeatLeniencyCode, + uint32_t timestamp) +{ + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_HIGH, DEVICE_GROUP_BROADCAST, DEVICE_SERIAL_BROADCAST); + if (!broadcast) + { + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_HIGH, DEVICE_GROUP_JETSON, DEVICE_SERIAL_JETSON); + } + packetToAssemble->dlc = DLC_HEARTBEAT; + int nextByte = WriteSenderSerialAndPacketID(packetToAssemble->data, ID_HEARTBEAT); + packetToAssemble->data[nextByte] = heartbeatLeniencyCode; + PackIntIntoDataMSBFirst(packetToAssemble->data, timestamp, nextByte + 1); +} + +void AssembleFailReportPacket(CANPacket *packetToAssemble, + uint8_t targetGroup, + uint8_t targetSerial, + uint8_t failedPacketID) +{ + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetGroup, targetSerial); + packetToAssemble->dlc = DLC_FAIL_REPORT; + int nextByte = WriteSenderSerialAndPacketID(packetToAssemble->data, ID_FAIL_REPORT); + packetToAssemble->data[nextByte] = failedPacketID; +} + +// Assembles override protection packet with given parameters +// Inputs: +// packetToAssemble: CAN Packet to assemble (will overwrite). +// targetGroup: Device gorup of target device. +// targetSerial: Device serial of target device. +void AssembleOverrideProtectionPacket(CANPacket *packetToAssemble, uint8_t targetGroup, uint8_t targetSerial) +{ + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetGroup, targetSerial); + packetToAssemble->dlc = DLC_OVRD_PROTECTION; + WritePacketIDOnly(packetToAssemble->data, ID_OVRD_PROTECTION); +} + +void AssembleChipTypePullPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial) +{ + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_CHIP_TYPE_PULL; + int nextByte = WriteSenderSerialAndPacketID(packetToAssemble->data, ID_CHIP_TYPE_PULL); + packetToAssemble->data[nextByte] = getChipType(); +} + +void AssembleChipTypeReportPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial) +{ + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_CHIP_TYPE_REP; + int nextByte = WriteSenderSerialAndPacketID(packetToAssemble->data, ID_CHIP_TYPE_REP); + packetToAssemble->data[nextByte] = getChipType(); +} + +uint8_t GetChipTypeFromPacket(CANPacket *packet) +{ + return packet->data[1]; +} + +void AssembleTelemetryTimingPacket(CANPacket *packetToAssemble, + uint8_t targetGroup, + uint8_t targetSerial, + uint8_t telemetryTypeCode, + uint32_t msBetweenReports) +{ + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetGroup, targetSerial); + packetToAssemble->dlc = DLC_TELEMETRY_TIMING; + int nextByte = WriteSenderSerialAndPacketID(packetToAssemble->data, ID_TELEMETRY_TIMING); + packetToAssemble->data[nextByte] = telemetryTypeCode; + PackIntIntoDataMSBFirst(packetToAssemble->data, msBetweenReports, nextByte + 1); +} +uint32_t GetTelemetryTimingFromPacket(CANPacket *packetToAssemble) +{ + return DecodeTelemetryDataUnsigned(packetToAssemble); +} + +void AssembleTelemetryPullPacket(CANPacket *packetToAssemble, + uint8_t targetGroup, + uint8_t targetSerial, + uint8_t telemetryTypeCode) +{ + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetGroup, targetSerial); + packetToAssemble->dlc = DLC_TELEMETRY_PULL; + int nextByte = WriteSenderSerialAndPacketID(packetToAssemble->data, ID_TELEMETRY_PULL); + packetToAssemble->data[nextByte] = telemetryTypeCode; +} + +void AssembleTelemetryReportPacket(CANPacket *packetToAssemble, + uint8_t targetGroup, + uint8_t targetSerial, + uint8_t telemetryTypeCode, + int32_t data) +{ + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetGroup, targetSerial); + packetToAssemble->dlc = DLC_TELEMETRY_REPORT; + int nextByte = WriteSenderSerialAndPacketID(packetToAssemble->data, ID_TELEMETRY_REPORT); + packetToAssemble->data[nextByte] = telemetryTypeCode; + PackIntIntoDataMSBFirst(packetToAssemble->data, data, nextByte + 1); +} + +int32_t DecodeTelemetryDataSigned(CANPacket *packet) +{ + return DecodeBytesToIntMSBFirst(packet->data, 4, 8); +} + +uint32_t DecodeTelemetryDataUnsigned(CANPacket *packet) +{ + return (uint32_t) DecodeTelemetryDataSigned(packet); +} + +uint8_t DecodeTelemetryType(CANPacket *packet) +{ + return packet->data[3]; +} + +void AssembleRGBColorPacket(CANPacket *packetToAssemble, + uint8_t targetGroup, + uint8_t targetSerial, + uint8_t addrLED, + uint8_t R, + uint8_t G, + uint8_t B) +{ + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetGroup, targetSerial); + packetToAssemble->dlc = DLC_LED_COLOR; + int nextByte = WritePacketIDOnly(packetToAssemble->data, ID_LED_COLOR); + packetToAssemble->data[nextByte] = R; + packetToAssemble->data[nextByte + 1] = G; + packetToAssemble->data[nextByte + 2] = B; + packetToAssemble->data[nextByte + 3] = addrLED; +} \ No newline at end of file diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANCommon.h b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANCommon.h new file mode 100644 index 0000000..7a991d2 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANCommon.h @@ -0,0 +1,137 @@ +/* File: CANCommon.h + * Authors: Jaden Bottemiller, Benton Kwong, Dylan Tomberlin. + * Organization: Husky Robotics Team + * + * This file includes fuction prototypes for Common Mode CAN Communication + * using the Hindsight CAN Communication standard. + * Documentation: https://huskyroboticsteam.slite.com/app/channels/iU0BryG7M9/collections/aXvWTcIR6c/notes/4otlSFsSp2 + */ + +#pragma once + +#include "CANPacket.h" + +void AssembleEmergencyStopPacket(CANPacket *packet, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerialNumber, + uint8_t errorCode); +void AssembleGroupBroadcastingEmergencyStopPacket(CANPacket *packet, + uint8_t groupCode, + uint8_t errorCode); +void AssembleBrodcastEmergencyStopPacket(CANPacket *packet, + uint8_t errorCode); +uint8_t GetEmergencyStopErrorCode(CANPacket *packet); + +uint32_t GetTimeBetweenHeartbeatPacket(CANPacket *packet, uint32_t lastHeartbeat); +uint32_t GetHeartbeatTimeStamp(CANPacket *packet); +void AssembleHeartbeatPacket(CANPacket *packetToAssemble, + int broadcast, + uint8_t heartbeatLeniencyCode, + uint32_t timestamp); + +void AssembleFailReportPacket(CANPacket *packetToAssemble, + uint8_t targetGroup, + uint8_t targetSerial, + uint8_t failedPacketID); + +void AssembleOverrideProtectionPacket(CANPacket *packetToAssemble, uint8_t targetGroup, uint8_t targetSerial); + +//Chip type pull +void AssembleChipTypePullPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial); +void AssembleChipTypeReportPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial); +uint8_t GetChipTypeFromPacket(CANPacket *packet); + +void AssembleTelemetryTimingPacket(CANPacket *packetToAssemble, + uint8_t targetGroup, + uint8_t targetSerial, + uint8_t telemetryTypeCode, + uint32_t msBetweenReports); +uint32_t GetTelemetryTimingFromPacket(CANPacket *packetToAssemble); + +void AssembleTelemetryPullPacket(CANPacket *packetToAssemble, + uint8_t targetGroup, + uint8_t targetSerial, + uint8_t telemetryTypeCode); + +void AssembleTelemetryReportPacket(CANPacket *packetToAssemble, + uint8_t targetGroup, + uint8_t targetSerial, + uint8_t telemetryTypeCode, + int32_t data); + +int32_t DecodeTelemetryDataSigned(CANPacket *packet); +uint32_t DecodeTelemetryDataUnsigned(CANPacket *packet); +uint8_t DecodeTelemetryType(CANPacket *packet); + +void AssembleRGBColorPacket(CANPacket *packetToAssemble, + uint8_t targetGroup, + uint8_t targetSerial, + uint8_t addrLED, + uint8_t R, + uint8_t G, + uint8_t B); + +// Common Mode Packet IDs +#define ID_ESTOP (uint8_t) 0xF0 +#define ID_HEARTBEAT (uint8_t) 0xF1 +#define ID_FAIL_REPORT (uint8_t) 0xF2 +#define ID_OVRD_PROTECTION (uint8_t) 0xF3 +#define ID_TELEMETRY_TIMING (uint8_t) 0xF4 +#define ID_TELEMETRY_PULL (uint8_t) 0xF5 +#define ID_TELEMETRY_REPORT (uint8_t) 0xF6 +#define ID_LED_COLOR (uint8_t) 0xF7 +#define ID_CHIP_TYPE_PULL (uint8_t) 0xF8 +#define ID_CHIP_TYPE_REP (uint8_t) 0xF9 + +// DLC Common Mode Packets +#define DLC_ESTOP (uint8_t) 0x04 +#define DLC_HEARTBEAT (uint8_t) 0x08 +#define DLC_FAIL_REPORT (uint8_t) 0x04 +#define DLC_OVRD_PROTECTION (uint8_t) 0x01 +#define DLC_TELEMETRY_TIMING (uint8_t) 0x08 +#define DLC_TELEMETRY_PULL (uint8_t) 0x04 +#define DLC_TELEMETRY_REPORT (uint8_t) 0x08 +#define DLC_LED_COLOR (uint8_t) 0x06 +#define DLC_CHIP_TYPE_PULL (uint8_t) 0x04 +#define DLC_CHIP_TYPE_REP (uint8_t) 0x04 + +//Packet priorities +#define PRIO_CHIP_TYPE_REP PACKET_PRIORITY_NORMAL + +// Telemetry Types +#define PACKET_TELEMETRY_VOLTAGE ((uint8_t) 0x00) +#define PACKET_TELEMETRY_CURRENT ((uint8_t) 0x01) +#define PACKET_TELEMETRY_PWR_RAIL_STATE ((uint8_t) 0x02) +#define PACKET_TELEMETRY_TEMPERATURE ((uint8_t) 0x03) +#define PACKET_TELEMETRY_ANG_POSITION ((uint8_t) 0x04) +#define PACKET_TELEMETRY_GPS_LAT ((uint8_t) 0x05) +#define PACKET_TELEMETRY_GPS_LON ((uint8_t) 0x06) +#define PACKET_TELEMETRY_MAG_DIR ((uint8_t) 0x07) +#define PACKET_TELEMETRY_ACCEL_X ((uint8_t) 0x08) +#define PACKET_TELEMETRY_ACCEL_Y ((uint8_t) 0x09) +#define PACKET_TELEMETRY_ACCEL_Z ((uint8_t) 0x0A) +#define PACKET_TELEMETRY_GYRO_X ((uint8_t) 0x0B) +#define PACKET_TELEMETRY_GYRO_Y ((uint8_t) 0x0C) +#define PACKET_TELEMETRY_GYRO_Z ((uint8_t) 0x0D) +#define PACKET_TELEMETRY_LIM_SW_STATE ((uint8_t) 0x0E) +#define PACKET_TELEMETRY_ADC_RAW ((uint8_t) 0x0F) +#define PACKET_TELEMETRY_GPIO_STATE ((uint8_t) 0x10) +#define PACKET_TELEMETRY_CHIP_TYPE ((uint8_t) 0x11) +#define PACKET_TELEMETRY_QUATERNION_W ((uint8_t) 0x12) +#define PACKET_TELEMETRY_QUATERNION_X ((uint8_t) 0x13) +#define PACKET_TELEMETRY_QUATERNION_Y ((uint8_t) 0x14) +#define PACKET_TELEMETRY_QUATERNION_Z ((uint8_t) 0x15) +#define PACKET_TELEMETRY_SENSOR1 ((uint8_t) 0x16) +#define PACKET_TELEMETRY_SENSOR2 ((uint8_t) 0x17) +#define PACKET_TELEMETRY_SENSOR3 ((uint8_t) 0x18) +#define PACKET_TELEMETRY_SENSOR4 ((uint8_t) 0x19) +#define PACKET_TELEMETRY_SENSOR5 ((uint8_t) 0x1A) +#define PACKET_TELEMETRY_SENSOR6 ((uint8_t) 0x1B) + +// ESTOP ERROR CODES +#define ESTOP_ERR_GENERAL (uint8_t) 0x00 +// MORE TBD... diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANGPIO.c b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANGPIO.c new file mode 100644 index 0000000..91671b2 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANGPIO.c @@ -0,0 +1,170 @@ +/* File: CANGPIO.c + * Authors: Jaden Bottemiller, Benton Kwong, Dylan Tomberlin. + * Organization: Husky Robotics Team + * + * This file includes fuction implementations for the GPIO board CAN Communication + * using the Hindsight CAN Communication standard. + * Documentation: https://huskyroboticsteam.slite.com/app/channels/iU0BryG7M9/collections/aXvWTcIR6c/notes/4otlSFsSp2 + */ +//#include "CANPacket.h" +#include "CANGPIO.h" + +//Set PWM Frequency Packet +//Assembles a CAN Packet to set GPIO PWM Frequency +// Inputs: CANPacket pointer to assemble packet +// targetGroup & targetSerial - for CAN ID +// pwmChannel - board specific PWM channel +// frequency - frequency in Hz +void AssembleGPIOSetPWMFrequencyPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t pwmChannel, + uint16_t frequency){ + + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_GPIO_PWM_FREQ; + + int nextByte = WritePacketIDOnly(packetToAssemble->data, ID_GPIO_PWM_FREQ); + packetToAssemble->data[nextByte++] = pwmChannel; + PackIntIntoDataMSBFirst(packetToAssemble->data, frequency, nextByte); //consider making a 16bit version of this function? + +} + +//returns GPIO PWM Channel +//accepts packet to return data from +uint8_t GetGPIOPWMChannelFromPacket(CANPacket *packet){ + return packet->data[1]; +} +//returns GPIO PWM frequency +//accepts packet to return data from +uint16_t GetGPIOPWMFrequencyFromPacket(CANPacket *packet){ + return DecodeBytesToIntMSBFirst(packet->data, 2, 3); +} + +//Set PWM Duty Cycle +//Assembles a CAN Packet to set GPIO PWM Duty Cycle +// Inputs: CANPacket pointer to assemble packet +// targetGroup & targetSerial - for CAN ID +// pwmChannel - board specific PWM channel +// dutyCycle - duty cycle resolution +void AssembleGPIOSetPWMDutyCyclePacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t pwmChannel, + uint16_t dutyCycle){ + + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_GPIO_PWM_DUTY_CYCLE; + + int nextByte = WritePacketIDOnly(packetToAssemble->data, ID_GPIO_PWM_DUTY_CYCLE); + packetToAssemble->data[nextByte++] = pwmChannel; + PackIntIntoDataMSBFirst(packetToAssemble->data, dutyCycle, nextByte); +} + +//returns GPIO PWM duty cycle +//accepts packet to return data from +uint16_t GetGPIOPWMDutyCycle(CANPacket *packetToAssemble){ + return DecodeBytesToIntMSBFirst(packetToAssemble->data, 2, 3); +} + + +//Set ADC State +//Assembles a CAN Packet to set GPIO ADC +// Inputs: CANPacket pointer to assemble packet +// targetGroup & targetSerial - for CAN ID +// ADCChannel - board specific ADC channel +// state - bool (1 enable 0 disable) +void AssembleGPIOSetADCStateConfiguration(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t ADCChannel, + uint8_t state){ + + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_GPIO_ADC_STATE; + + int nextByte = WritePacketIDOnly(packetToAssemble->data, ID_GPIO_ADC_STATE); + packetToAssemble->data[nextByte++] = ADCChannel; + packetToAssemble->data[nextByte] = state; +} +//returns GPIO ADC channel +//accepts packet to return data from +uint8_t GetGPIOADCChannelFromPacket(CANPacket *packet){ + return packet->data[1]; +} +//returns GPIO ADC state +//accepts packet to return data from +uint8_t GetGPIOADCStateFromPacket(CANPacket *packet){ + return packet->data[2]; +} + + +//Set GPIO Configuration +//Assembles a CAN Packet to set GPIO Config +// Inputs: CANPacket pointer to assemble packet +// targetGroup & targetSerial - for CAN ID +// GPIORegister - GPIO Register +// GPIO bit number - (number is the bit which is being set) +// GPIO bit state - off, in, out, in/out, adc, pwm +void AssembleGPIOSetConfigurationPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t GPIORegister, + uint8_t bitNumber, + uint8_t bitConfig){ + + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_GPIO_CONFIG; + + int nextByte = WritePacketIDOnly(packetToAssemble->data, ID_GPIO_CONFIG); + packetToAssemble->data[nextByte++] = GPIORegister; + packetToAssemble->data[nextByte++] = bitNumber; + packetToAssemble->data[nextByte] = bitConfig; + +} +//returns GPIO register +//accepts packet to return data from +uint8_t GetGPIORegisterFromPacket(CANPacket *packet){ + return packet->data[1]; +} +//returns GPIO bit number +//accepts packet to return data from +uint8_t GetGPIOBitNumberFromPacket(CANPacket *packet){ + return packet->data[2]; +} +//returns GPIO bit config +//accepts packet to return data from +uint8_t GetGPIOBitConfigFromPacket(CANPacket *packet){ + return packet->data[3]; +} + + +//GPIO Write +//Assembles a CAN Packet to set GPIO Config +// Inputs: CANPacket pointer to assemble packet +// targetGroup & targetSerial - for CAN ID +// GPIORegister - GPIO Register +// GPIO bit number - (number is the bit which is being set) +// GPIO bit writevalues - off, on, flip +void AssembleGPIOWrite(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t GPIORegister, + uint8_t bitNumber, + uint8_t bitWriteValue){ + + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_GPIO_WRITE; + + int nextByte = WritePacketIDOnly(packetToAssemble->data, ID_GPIO_WRITE); + packetToAssemble->data[nextByte++] = GPIORegister; + packetToAssemble->data[nextByte++] = bitNumber; + packetToAssemble->data[nextByte] = bitWriteValue; + +} + +//returns GPIO write values +//accepts packet to return data from +uint8_t GetGPIOWriteValuesFromPacket(CANPacket *packet){ + return packet->data[3]; +} \ No newline at end of file diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANGPIO.h b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANGPIO.h new file mode 100644 index 0000000..b047c95 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANGPIO.h @@ -0,0 +1,91 @@ +/* File: CANGPIO.h + * Authors: Jaden Bottemiller, Benton Kwong, Dylan Tomberlin. + * Organization: Husky Robotics Team + * + * This file includes fuction prototypes for the GPIO board CAN Communication + * using the Hindsight CAN Communication standard. + * Documentation: https://huskyroboticsteam.slite.com/app/channels/iU0BryG7M9/collections/aXvWTcIR6c/notes/4otlSFsSp2 + */ + +#pragma once + +#include "CANPacket.h" + +//Set PWM Frequency +void AssembleGPIOSetPWMFrequencyPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t pwmChannel, + uint16_t frequency); +uint8_t GetGPIOPWMChannelFromPacket(CANPacket *packet); +uint16_t GetGPIOPWMFrequencyFromPacket(CANPacket *packet); + +//Set PWM Duty Cycle +void AssembleGPIOSetPWMDutyCyclePacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t pwmChannel, + uint16_t dutyCycle); +uint16_t GetGPIOPWMDutyCycle(CANPacket *packetToAssemble); + + +//Set ADC State +void AssembleGPIOSetADCStateConfiguration(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t ADCChannel, + uint8_t state); +uint8_t GetGPIOADCChannelFromPacket(CANPacket *packet); +uint8_t GetGPIOADCStateFromPacket(CANPacket *packet); + + +//Set GPIO Configuration +void AssembleGPIOSetConfigurationPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t GPIORegister, + uint8_t bitNumber, + uint8_t bitConfig); +uint8_t GetGPIORegisterFromPacket(CANPacket *packet); +uint8_t GetGPIOBitNumberFromPacket(CANPacket *packet); +uint8_t GetGPIOBitConfigFromPacket(CANPacket *packet); + + +//GPIO Write +void AssembleGPIOWrite(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t GPIORegister, + uint8_t bitNumber, + uint8_t bitWriteValue); +uint8_t GetGPIOWriteValuesFromPacket(CANPacket *packet); + +//use this as bit number to configure or write to a whole register at once +#define GPIO_WHOLE_REG_NUMBER 0xFF + +//GPIO configuration constants +#define GPIO_CONFIG_OFF 0x0 +#define GPIO_CONFIG_INPUT 0x1 +#define GPIO_CONFIG_OUTPUT 0x2 +#define GPIO_CONFIG_IO 0x3 +#define GPIO_CONFIG_ADC 0x4 +#define GPIO_CONFIG_PWM 0x5 + +//GPIO write values +#define GPIO_WRITE_OFF 0x0 +#define GPIO_WRITE_ON 0x1 +#define GPIO_WRITE_FLIP 0x2 + +//GPIO DLC +#define DLC_GPIO_PWM_FREQ (uint8_t) 0x04 +#define DLC_GPIO_PWM_DUTY_CYCLE (uint8_t) 0x04 +#define DLC_GPIO_ADC_STATE (uint8_t) 0x03 +#define DLC_GPIO_CONFIG (uint8_t) 0x04 +#define DLC_GPIO_WRITE (uint8_t) 0x04 + +//GPIO Packet IDs +#define ID_GPIO_PWM_FREQ (uint8_t) 0x00 +#define ID_GPIO_PWM_DUTY_CYCLE (uint8_t) 0x01 +#define ID_GPIO_ADC_STATE (uint8_t) 0x02 +#define ID_GPIO_CONFIG (uint8_t) 0x03 +#define ID_GPIO_WRITE (uint8_t) 0x04 diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANLibrary.h b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANLibrary.h new file mode 100644 index 0000000..57cced2 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANLibrary.h @@ -0,0 +1,21 @@ +/* + *This file includes all others for simple library integration. Better + *form would be just to include what you need. Essentials are: + *CANPacket.h, port.h, CANCommon.h + *You will also need any utility files required by your board, i.e. + *CANMotorUnit.h or CANLocalization. + */ + +#pragma once + +//Essentials +#include "CANPacket.h" +#include "CANCommon.h" +#include "Port.h" + +//Board specifics +#include "CANGPIO.h" +#include "CANLocalization.h" +#include "CANMotorUnit.h" +#include "CANPower.h" +#include "CANSerialNumbers.h" \ No newline at end of file diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANLocalization.c b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANLocalization.c new file mode 100644 index 0000000..ae15bca --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANLocalization.c @@ -0,0 +1,9 @@ +/* File: CANLocalization.c + * Authors: Jaden Bottemiller, Benton Kwong, Dylan Tomberlin. + * Organization: Husky Robotics Team + * + * This file includes fuction implementations for the Localization board CAN Communication + * using the Hindsight CAN Communication standard. + * Documentation: https://huskyroboticsteam.slite.com/app/channels/iU0BryG7M9/collections/aXvWTcIR6c/notes/4otlSFsSp2 + */ +#include "CANPacket.h" diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANLocalization.h b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANLocalization.h new file mode 100644 index 0000000..6fa91b4 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANLocalization.h @@ -0,0 +1,12 @@ +/* File: CANLocalization.h + * Authors: Jaden Bottemiller, Benton Kwong, Dylan Tomberlin. + * Organization: Husky Robotics Team + * + * This file includes fuction prototypes for the Localization board CAN Communication + * using the Hindsight CAN Communication standard. + * Documentation: https://huskyroboticsteam.slite.com/app/channels/iU0BryG7M9/collections/aXvWTcIR6c/notes/4otlSFsSp2 + */ + +#pragma once + +#include "CANPacket.h" diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANMotorUnit.c b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANMotorUnit.c new file mode 100644 index 0000000..13374b5 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANMotorUnit.c @@ -0,0 +1,271 @@ +/* File: CANMotorUnit.c + * Authors: Jaden Bottemiller, Benton Kwong, Dylan Tomberlin, Austin Chan. + * Organization: Husky Robotics Team + * + * This file includes function definitions for CAN Packet manipulation + * using the Hindsight CAN Communication standard. Specific files + * for the motor unit boards. + * Documentation: https://huskyroboticsteam.slite.com/app/channels/iU0BryG7M9/collections/aXvWTcIR6c/notes/4otlSFsSp2 + */ + +#include "CANPacket.h" +#include "CANMotorUnit.h" +#include "Port.h" + +void AssembleModeSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t mode) +{ + packetToAssemble->id = ConstructCANID(PRIO_MOTOR_UNIT_MODE_SEL, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_MOTOR_UNIT_MODE_SEL; + int nextByte = WritePacketIDOnly(packetToAssemble->data, ID_MOTOR_UNIT_MODE_SEL); + packetToAssemble->data[nextByte] = mode; +} + +uint8_t GetModeFromPacket(CANPacket *packet) +{ + return packet->data[1]; +} + + +void AssemblePWMDirSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + int16_t PWMSet) +{ + packetToAssemble->id = ConstructCANID(PRIO_MOTOR_UNIT_PWM_DIR_SET, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_MOTOR_UNIT_PWM_DIR_SET; + int nextByte = WritePacketIDOnly(packetToAssemble->data, ID_MOTOR_UNIT_PWM_DIR_SET); + PackShortIntoDataMSBFirst(packetToAssemble->data, PWMSet, nextByte); +} + +int16_t GetPWMFromPacket(CANPacket *packet) +{ + return DecodeBytesToIntMSBFirst(packet->data, 1, 2); +} + +//Returns 2's compliment MSB (0 for stopped or forward, 1 for reverse) +int32_t GetDirectionFromPacket(CANPacket *packet) +{ + return ((packet->data[1]) >> 7) & 0x1; +} + +void AssemblePIDTargetSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + int32_t target) +{ + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_MOTOR_UNIT_PID_POS_TGT_SET; + int nextByte = WritePacketIDOnly(packetToAssemble->data, ID_MOTOR_UNIT_PID_POS_TGT_SET); + PackIntIntoDataMSBFirst(packetToAssemble->data, target, nextByte); +} + +int32_t GetPIDTargetFromPacket(CANPacket *packet) +{ + return DecodeBytesToIntMSBFirst(packet->data, DLC_MOTOR_UNIT_PID_POS_TGT_SET - 4, DLC_MOTOR_UNIT_PID_POS_TGT_SET); +} + +void AssemblePSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + int32_t pCoef) +{ + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_MOTOR_UNIT_PID_P_SET; + int nextByte = WritePacketIDOnly(packetToAssemble->data, ID_MOTOR_UNIT_PID_P_SET); + PackIntIntoDataMSBFirst(packetToAssemble->data, pCoef, nextByte); +} + +int32_t GetPFromPacket(CANPacket *packet) +{ + return DecodeBytesToIntMSBFirst(packet->data, DLC_MOTOR_UNIT_PID_P_SET - 4, DLC_MOTOR_UNIT_PID_P_SET ); +} + +void AssembleISetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + int32_t iCoef) +{ + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_MOTOR_UNIT_PID_I_SET; + int nextByte = WritePacketIDOnly(packetToAssemble->data, ID_MOTOR_UNIT_PID_I_SET); + PackIntIntoDataMSBFirst(packetToAssemble->data, iCoef, nextByte); +} + +int32_t GetIFromPacket(CANPacket *packet) +{ + return DecodeBytesToIntMSBFirst(packet->data, DLC_MOTOR_UNIT_PID_I_SET - 4, DLC_MOTOR_UNIT_PID_I_SET ); +} + +void AssembleDSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + int32_t dCoef) +{ + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_MOTOR_UNIT_PID_D_SET; + int nextByte = WritePacketIDOnly(packetToAssemble->data, ID_MOTOR_UNIT_PID_D_SET); + PackIntIntoDataMSBFirst(packetToAssemble->data, dCoef, nextByte); +} + +int32_t GetDFromPacket(CANPacket *packet) +{ + return DecodeBytesToIntMSBFirst(packet->data, DLC_MOTOR_UNIT_PID_D_SET - 4, DLC_MOTOR_UNIT_PID_D_SET); +} + +void AssembleInitializePacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t initMode) +{ + packetToAssemble->id = ConstructCANID(PRIO_MOTOR_UNIT_INIT, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_MOTOR_UNIT_INIT; + int nextByte = WritePacketIDOnly(packetToAssemble->data, ID_MOTOR_UNIT_INIT); + packetToAssemble->data[nextByte] = initMode; +} +uint8_t GetInitModeFromPacket(CANPacket *packet) +{ + return packet->data[1]; +} + +void AssembleLimitSwitchAlertPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t switches) +{ + packetToAssemble->id = ConstructCANID(PRIO_MOTOR_UNIT_LIM_ALERT, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_MOTOR_UNIT_LIM_ALERT; + int nextByte = WriteSenderSerialAndPacketID(packetToAssemble->data, ID_MOTOR_UNIT_LIM_ALERT); + packetToAssemble->data[nextByte] = switches; +} +uint8_t GetLimStatusFromPacket(CANPacket *packet) +{ + return packet->data[1]; +} + +void AssembleEncoderPPJRSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint32_t pulses) +{ + packetToAssemble->id = ConstructCANID(PRIO_MOTOR_UNIT_ENC_PPJR_SET, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_MOTOR_UNIT_ENC_PPJR_SET; + int nextByte = WritePacketIDOnly(packetToAssemble->data, ID_MOTOR_UNIT_ENC_PPJR_SET); + PackIntIntoDataMSBFirst(packetToAssemble->data, pulses, nextByte); +} + +uint32_t GetEncoderPPJRFromPacket(CANPacket *packet) +{ + return DecodeBytesToIntMSBFirst(packet->data, 1, 4); +} + +void AssembleMaxJointRevolutionPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint32_t revolutions) +{ + packetToAssemble->id = ConstructCANID(PRIO_MOTOR_UNIT_MAX_JNT_REV_SET, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_MOTOR_UNIT_MAX_JNT_REV_SET; + int nextByte = WritePacketIDOnly(packetToAssemble->data, ID_MOTOR_UNIT_MAX_JNT_REV_SET); + PackIntIntoDataMSBFirst(packetToAssemble->data, revolutions, nextByte); + +} +uint32_t GetMaxJointRevolutionsFromPacket(CANPacket *packet) +{ + return DecodeBytesToIntMSBFirst(packet->data, 1, 4); +} + +void AssemblePotHiSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint16_t adcHi, + int32_t mdegHi) +{ + packetToAssemble->id = ConstructCANID(PRIO_MOTOR_UNIT_POT_INIT, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_MOTOR_UNIT_POT_INIT; + + int idx = WritePacketIDOnly(packetToAssemble->data, ID_MOTOR_UNIT_POT_INIT_HI); + PackShortIntoDataMSBFirst(packetToAssemble->data, adcHi, idx); + idx += 2; + PackIntIntoDataMSBFirst(packetToAssemble->data, mdegHi, idx); +} + +void AssemblePotLoSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint16_t adcLo, + int32_t mdegLo) +{ + packetToAssemble->id = ConstructCANID(PRIO_MOTOR_UNIT_POT_INIT, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_MOTOR_UNIT_POT_INIT; + + int idx = WritePacketIDOnly(packetToAssemble->data, ID_MOTOR_UNIT_POT_INIT_LO); + PackShortIntoDataMSBFirst(packetToAssemble->data, adcLo, idx); + idx += 2; + PackIntIntoDataMSBFirst(packetToAssemble->data, mdegLo, idx); +} + +uint16_t GetPotADCFromPacket(const CANPacket *packet) +{ + return DecodeBytesToIntMSBFirst(packet->data, 1, 2); +} + +int32_t GetPotmDegFromPacket(const CANPacket *packet) +{ + return DecodeBytesToIntMSBFirst(packet->data, 3, 6); +} + +void AssembleEncoderInitializePacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t encoderType, + uint8_t angleDirection, + uint8_t zeroAngle) +{ + packetToAssemble->id = ConstructCANID(PRIO_MOTOR_UNIT_ENC_INIT, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_MOTOR_UNIT_ENC_INIT; + int nextByte = WritePacketIDOnly(packetToAssemble->data, ID_MOTOR_UNIT_ENC_INIT); + packetToAssemble->data[nextByte] = 0; + if(encoderType) + { + packetToAssemble->data[nextByte] |= 0b100; + } + if(angleDirection) + { + packetToAssemble->data[nextByte] |= 0b010; + } + if(zeroAngle) + { + packetToAssemble->data[nextByte] |= 0b001; + } +} + +uint8_t GetEncoderTypeFromPacket(CANPacket *packet) +{ + return(packet->data[1] & 0b100); +} +uint8_t GetEncoderDirectionFromPacket(CANPacket *packet) +{ + return(packet->data[1] & 0b010); +} +uint8_t GetEncoderZeroFromPacket(CANPacket *packet) +{ + return(packet->data[1] & 0b001); +} + +void AssembleMaxPIDPWMPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint16_t PWMSetMax) + { + packetToAssemble->id = ConstructCANID(PRIO_MOTOR_UNIT_MAX_PID_PWM, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = DLC_MOTOR_UNIT_MAX_PID_PWM; + int nextByte = WritePacketIDOnly(packetToAssemble->data, DLC_MOTOR_UNIT_MAX_PID_PWM); + PackShortIntoDataMSBFirst(packetToAssemble->data, PWMSetMax, nextByte); +} + +uint16_t GetMaxPIDPWMFromPacket(CANPacket *packet){ + return DecodeBytesToIntMSBFirst(packet->data, 1, 2); +} diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANMotorUnit.h b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANMotorUnit.h new file mode 100644 index 0000000..6fac21b --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANMotorUnit.h @@ -0,0 +1,212 @@ +/* File: CANMotorUnit.h + * Authors: Jaden Bottemiller, Benton Kwong, Dylan Tomberlin. + * Organization: Husky Robotics Team + * + * This file includes fuction prototypes for CAN Packet manipulation + * using the Hindsight CAN Communication standard. Specific files + * for the motor unit boards. + * Documentation: https://huskyroboticsteam.slite.com/app/channels/iU0BryG7M9/collections/aXvWTcIR6c/notes/4otlSFsSp2 + */ + +#include "CANPacket.h" + +// TODO: Add parameters to packet assembly +//Mode set (PWM or PID) +void AssembleModeSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t mode); +uint8_t GetModeFromPacket(CANPacket *packet); + +//PWM value and direction set +void AssemblePWMDirSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + int16_t PWMSet); +int16_t GetPWMFromPacket(CANPacket *packet); +int32_t GetDirectionFromPacket(CANPacket *packet); + +//PID postional target set +void AssemblePIDTargetSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + int32_t target); +int32_t GetPIDTargetFromPacket(CANPacket *packet); + +//P coeffiecent set +void AssemblePSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + int32_t pCoef); +int32_t GetPFromPacket(CANPacket *packet); + +//I coeffiecent set +void AssembleISetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + int32_t iCoef); +int32_t GetIFromPacket(CANPacket *packet); + +//D coeffiecent set +void AssembleDSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + int32_t dCoef); +int32_t GetDFromPacket(CANPacket *packet); + +//Initialize with mode (motors shall not move until have been inited) +void AssembleInitializePacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t initMode); +uint8_t GetInitModeFromPacket(CANPacket *packet); + +//Limit switch alert +//each bit represents one limit switch, 1 for closed, 0 for open, +//switch number corresponds to the bit number +void AssembleLimitSwitchAlertPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t switches); +uint8_t GetLimStatusFromPacket(CANPacket *packet); + +//Encoder pulses per joint revolution set +void AssembleEncoderPPJRSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint32_t pulses); +uint32_t GetEncoderPPJRFromPacket(CANPacket *packet); + +//Maximum joint rotations set +void AssembleMaxJointRevolutionPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint32_t revolutions); +uint32_t GetMaxJointRevolutionsFromPacket(CANPacket *packet); + + +// Potentiometer configuration packets +/** + * @brief Assemble a packet to set the high point of the potentiometer. + * + * This, along with AssemblePotLoSetPacket() are required to initialize the potentiometer. + * Behavior is undefined if only one packet is sent and not the other. + * + * @param packetToAssemble The packet to write the data into. + * @param targetDeviceGroup The group of the target device. + * @param targetDeviceSerial Ther serial code of the target device. + * @param adcHi The raw ADC value of the pot at the max. + * @param mdegHi The joint pos in millideg at the max. + * + * @see https://github.com/huskyroboticsteam/HindsightCAN/wiki/Motor-Unit-Packets + */ +void AssemblePotHiSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint16_t adcHi, + int32_t mdegHi); + +/** + * @brief Assemble a packet to set the low point of the potentiometer. + * + * This, along with AssemblePotHiSetPacket() are required to initialize the potentiometer. + * Behavior is undefined if only one packet is sent and not the other. + * + * @param packetToAssemble The packet to write the data into. + * @param targetDeviceGroup The group of the target device. + * @param targetDeviceSerial Ther serial code of the target device. + * @param adcHi The raw ADC value of the pot at the low. + * @param mdegHi The joint pos in millideg at the low. + * + * @see https://github.com/huskyroboticsteam/HindsightCAN/wiki/Motor-Unit-Packets + */ +void AssemblePotLoSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint16_t adcLo, + int32_t mdegLo); + +/** + * @brief Get the raw ADC value from a pot initialization packet. + * + * @param packet The packet, produced by either AssemblePotHiSetPacket() + * or AssemblePotLoSetPacket(), to read from. + * @return uint16_t The raw ADC value. + */ +uint16_t GetPotInitADCFromPacket(const CANPacket *packet); + +/** + * @brief Get the joint position from a pot initialization packet. + * + * @param packet The packet, produced by either AssemblePotHiSetPacket() + * or AssemblePotLoSetPacket(), to read from. + * @return int32_t The joint position in millidegrees. + */ +int32_t GetPotInitmDegFromPacket(const CANPacket *packet); + +//Initialize Encoder Settings +void AssembleEncoderInitializePacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t encoderType, + uint8_t angleDirection, + uint8_t zeroAngle); +uint8_t GetEncoderTypeFromPacket(CANPacket *packet); +uint8_t GetEncoderDirectionFromPacket(CANPacket *packet); +uint8_t GetEncoderZeroFromPacket(CANPacket *packet); + +void AssembleMaxPIDPWMPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint16_t PWMSetMax); +uint16_t GetMaxPIDPWMFromPacket(CANPacket *packet); + +// Motor Unit Packet IDs +#define ID_MOTOR_UNIT_MODE_SEL (uint8_t) 0x00 +#define ID_MOTOR_UNIT_PWM_DIR_SET (uint8_t) 0x03 +#define ID_MOTOR_UNIT_PID_POS_TGT_SET (uint8_t) 0x04 +#define ID_MOTOR_UNIT_PID_P_SET (uint8_t) 0x05 +#define ID_MOTOR_UNIT_PID_I_SET (uint8_t) 0x06 +#define ID_MOTOR_UNIT_PID_D_SET (uint8_t) 0x07 +#define ID_MOTOR_UNIT_INIT (uint8_t) 0x08 +#define ID_MOTOR_UNIT_LIM_ALERT (uint8_t) 0x09 +#define ID_MOTOR_UNIT_ENC_PPJR_SET (uint8_t) 0x0A +#define ID_MOTOR_UNIT_MAX_JNT_REV_SET (uint8_t) 0x0B +#define ID_MOTOR_UNIT_ENC_INIT (uint8_t) 0x0C +#define ID_MOTOR_UNIT_MAX_PID_PWM (uint8_t) 0x0D +#define ID_MOTOR_UNIT_POT_INIT_LO (uint8_t) 0x0F +#define ID_MOTOR_UNIT_POT_INIT_HI (uint8_t) 0x10 + +// Packet DLCs +#define DLC_MOTOR_UNIT_MODE_SEL (uint8_t) 0x02 +#define DLC_MOTOR_UNIT_PWM_DIR_SET (uint8_t) 0x03 +#define DLC_MOTOR_UNIT_PID_POS_TGT_SET (uint8_t) 0x05 +#define DLC_MOTOR_UNIT_PID_P_SET (uint8_t) 0x05 +#define DLC_MOTOR_UNIT_PID_I_SET (uint8_t) 0x05 +#define DLC_MOTOR_UNIT_PID_D_SET (uint8_t) 0x05 +#define DLC_MOTOR_UNIT_INIT (uint8_t) 0x02 +#define DLC_MOTOR_UNIT_LIM_ALERT (uint8_t) 0x04 +#define DLC_MOTOR_UNIT_ENC_PPJR_SET (uint8_t) 0x05 +#define DLC_MOTOR_UNIT_MAX_JNT_REV_SET (uint8_t) 0x02 +#define DLC_MOTOR_UNIT_ENC_INIT (uint8_t) 0x02 +#define DLC_MOTOR_UNIT_MAX_PID_PWM (uint8_t) 0x03 +#define DLC_MOTOR_UNIT_POT_INIT (uint8_t) 0x07 + +//Packet priorities +#define PRIO_MOTOR_UNIT_MODE_SEL PACKET_PRIORITY_NORMAL +#define PRIO_MOTOR_UNIT_PWM_DIR_SET PACKET_PRIORITY_NORMAL +#define PRIO_MOTOR_UNIT_PID_POS_TGT_SET PACKET_PRIORITY_NORMAL +#define PRIO_MOTOR_UNIT_PID_P_SET PACKET_PRIORITY_NORMAL +#define PRIO_MOTOR_UNIT_PID_I_SET PACKET_PRIORITY_NORMAL +#define PRIO_MOTOR_UNIT_PID_D_SET PACKET_PRIORITY_NORMAL +#define PRIO_MOTOR_UNIT_INIT PACKET_PRIORITY_NORMAL +#define PRIO_MOTOR_UNIT_LIM_ALERT PACKET_PRIORITY_HIGH +#define PRIO_MOTOR_UNIT_ENC_PPJR_SET PACKET_PRIORITY_NORMAL +#define PRIO_MOTOR_UNIT_MAX_JNT_REV_SET PACKET_PRIORITY_NORMAL +#define PRIO_MOTOR_UNIT_ENC_INIT PACKET_PRIORITY_NORMAL +#define PRIO_MOTOR_UNIT_MAX_PID_PWM PACKET_PRIORITY_NORMAL +#define PRIO_MOTOR_UNIT_POT_INIT PACKET_PRIORITY_NORMAL + +// Motor Unit Mode IDs +#define MOTOR_UNIT_MODE_PWM (uint8_t) 0x00 +#define MOTOR_UNIT_MODE_PID (uint8_t) 0x01 diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANPacket.c b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANPacket.c new file mode 100644 index 0000000..c613974 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANPacket.c @@ -0,0 +1,228 @@ +/* File: CANPacket.c + * Authors: Jaden Bottemiller, Benton Kwong, Dylan Tomberlin. + * Organization: Husky Robotics Team + * + * This file includes fuction definitions for CAN Packet manipulation + * using the Hindsight CAN Communication standard. + * Documentation: https://huskyroboticsteam.slite.com/app/channels/iU0BryG7M9/collections/aXvWTcIR6c/notes/4otlSFsSp2 + */ + +#include "CANPacket.h" +#include "Port.h" + +// Constructs a CAN ID according to standards set by electronics subsystem +// for hindsight (PY2020 rover). Not compatible with Orpheus (PY2019) +// Inputs: +// priority: A byte determing if the packet should be prioritized +// High priority would mean setting this value to 0 +// Low priority would mean setting this value to 1 +// devGroup: ID of device group +// devSerial: Serial value of device in the device group +// Output: +// CANID: CAN ID with correct formatting +uint16_t ConstructCANID(uint8_t priority, uint8_t devGroup, uint8_t devSerial) +{ + uint16_t CANID = 0x0000; + CANID = CANID | ((priority & 0x01) << 10); + CANID = CANID | ((devGroup & 0x0F) << 6); + CANID = CANID | (devSerial & 0x3F); + + return CANID; +} + +// Creates a CANPacket that can be used by fuctions in this file +// Inputs: +// id: CAN ID for the packet +// dlc: Data length for the packet. It's the number of bytes used +// in data payload for packet +// data: An array of bytes used for sending data over CAN +// Outputs: +// CANPacket: A struct used for storing the parts needed for a CAN Packet +CANPacket ConstructCANPacket(uint16_t id, uint8_t dlc, uint8_t* data) +{ + CANPacket cp; + cp.id = id; + cp.dlc = dlc; + for(int i = 0; i < dlc; i++) + { + cp.data[i] = data[i]; + } + + return cp; +} + +// Writes sender group, serial number, and packet ID to data bytes. Writes to bytes 0 and 1 in data. +// DO NOT OVERWRITE BYTES 0 AND 1 AFTER CALLING THIS FUNCTION. +// Inputs: +// data: Data to write to. +//TODO, upon approval from @jaden, delete senderGroup and senderSerial params, as these are handled by getLocal functs +// senderGroup: Device group the sender device is a part of. +// senderSerial: Device serial number for sender. +// packetID: ID for packet to be sent. +// Output: +// Index of next byte in `data` that can be written +int WriteSenderSerialAndPacketID(uint8_t *data, uint8_t packetID) +{ + data[0] = packetID; + data[1] = getLocalDeviceGroup(); + data[2] = getLocalDeviceSerial(); + return 3; +} + +// Writes packet ID to data bytes. Writes to byte 0 data. +// DO NOT OVERWRITE BYTE 0 AFTER CALLING THIS FUNCTION. +// Inputs: +// data: Data to write to. +// packetID: ID for packet to be sent. +// Outputs: +// Index to next byte in `data` that can be written; +int WritePacketIDOnly(uint8_t *data, uint8_t packetID) +{ + data[0] = packetID; + return 1; +} + +// Gets the priority of a given packet +// Inputs: +// packet: CAN Packet to analyze +// Outputs: +// priority byte representing packet priority, +// (0 for high, 1 for low) +uint8_t GetPacketPriority(CANPacket *packet) +{ + return (packet->id >> 10) & 0x1; +} + +// Gets the device serial number from CAN packet +// Inputs: +// packet: CAN Packet to analyze +// Outputs: +// A byte representing the device +// serial number. +uint8_t GetDeviceSerialNumber(CANPacket *packet) +{ + uint8_t id = (packet->id & 0x00FF); + // Strip fo only serial number portion of id + return id & 0x3F; +} + +// Returns Sender device serial number as +// packet: CAN packet from which to resolve serial number +// Outputs: +// A byte representing the sender device number +uint8_t GetSenderDeviceSerialNumber(CANPacket *packet) +{ + return packet->data[2]; +} + +// Gets the device group code from CAN packet +// Inputs: +// packet: CAN Packet to analyze +// Outputs: +// A byte representing the device +// group code. +uint8_t GetDeviceGroupCode(CANPacket *packet) +{ + uint8_t group = 0; + int id = packet->id; + group = (uint8_t) ((id & 0x03C0) >> 6); + return group; +} + +// Gets the sender device group number from the payload data +// Inputs: +// data: Address of the byte array of the payload from CAN packet +// Outputs: +// A byte representing the sender device number +uint8_t GetSenderDeviceGroupCode(CANPacket *packet) +{ + return packet->data[1]; +} + +// Ensures that the given packet is of a specified group +// Inputs: +// packet: CAN Packet to check +// expectedType: ExpectedType of CAN packet +// Outputs: +// 0 if packet not of expectedType, +// Other int otherwise +int PacketIsInGroup(CANPacket *packet, uint8_t expectedType) +{ + return GetDeviceGroupCode(packet) == expectedType; +} + +int SenderPacketIsInGroup(CANPacket *packet, uint8_t expectedType) +{ + return GetSenderDeviceGroupCode(packet) == expectedType; +} + +int SenderPacketIsOfDevice(CANPacket *packet, uint8_t expectedType) +{ + return GetSenderDeviceSerialNumber(packet) == expectedType; +} + +int GetPacketID(CANPacket *packet) +{ + return packet->data[0]; +} + +int PacketIsOfID(CANPacket *packet, uint8_t expectedID) +{ + return GetPacketID(packet) == expectedID; +} + +// Determines if a given packet targets a specific device +// Useful for determing if a packet should be interpreted by +// the device +// Inputs: +// packet: CAN Packet to check +// targetDeviceGroup: Device group of target device +// targetDeviceSerialNumber: Serial number of target device +// Outputs: +// Returns 0 if packet does not target device +// Returns any other int if packet does +int TargetsDevice(CANPacket *packet, uint8_t targetDeviceGroup, uint8_t targetDeviceSerialNumber) +{ + uint8_t packetGroup = GetDeviceGroupCode(packet); + if (packetGroup == targetDeviceGroup) + { + uint8_t serialNumber = GetDeviceSerialNumber(packet); + // Return if serial number matches target + // Otherwise only return true if packet is broadcast to group + return serialNumber == DEVICE_SERIAL_BROADCAST || serialNumber == targetDeviceSerialNumber; + } + // Otherwise only return true if packet is broadcast to all devices + return packetGroup == DEVICE_GROUP_BROADCAST; +} + +void PackIntIntoDataMSBFirst(uint8_t *data, int32_t dataToPack, int startIndex) +{ + data[startIndex] = (dataToPack & 0xFF000000) >> 24; + data[startIndex + 1] = (dataToPack & 0x00FF0000) >> 16; + data[startIndex + 2] = (dataToPack & 0x0000FF00) >> 8; + data[startIndex + 3] = (dataToPack & 0x000000FF); +} + +void PackShortIntoDataMSBFirst(uint8_t *data, int16_t dataToPack, int startIndex) +{ + data[startIndex + 0] = (dataToPack & 0xFF00) >> 8; + data[startIndex + 1] = (dataToPack & 0x00FF); +} + +int32_t DecodeBytesToIntMSBFirst(const uint8_t *data, int startIndex, int endIndex) +{ + int length = 4; + int32_t decodedData = 0; + + if (endIndex > 0 && startIndex >= 0) { + length = endIndex - startIndex + 1; + if (length > 4) { length = 4; } + if (length < 1) { length = 0; } + } + + for (int i = 0; i < length; i++) + { + decodedData |= (int32_t)data[startIndex + i] << (int32_t)(8 * (length-1-i)); + } + return decodedData; +} diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANPacket.h b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANPacket.h new file mode 100644 index 0000000..6989ccb --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANPacket.h @@ -0,0 +1,95 @@ +/* File: CANPacket.h + * Authors: Jaden Bottemiller, Benton Kwong, Dylan Tomberlin. + * Organization: Husky Robotics Team + * + * This file includes fuction prototypes for CAN Packet manipulation + * using the Hindsight CAN Communication standard. + * Documentation: https://huskyroboticsteam.slite.com/app/channels/iU0BryG7M9/collections/aXvWTcIR6c/notes/4otlSFsSp2 + */ + +#pragma once + +#include +#include "CANSerialNumbers.h" + +typedef struct +{ + uint16_t id; + uint8_t dlc; + uint8_t data[8]; +} CANPacket; + +CANPacket ConstructCANPacket(uint16_t id, uint8_t dlc, uint8_t* data); +uint16_t ConstructCANID(uint8_t priority, uint8_t devGroup, uint8_t devSerial); + +//Private library functions +int WriteSenderSerialAndPacketID(uint8_t *data, uint8_t packetID); +int WritePacketIDOnly(uint8_t *data, uint8_t packetID); + +uint8_t GetPacketPriority(CANPacket *packet); + +uint8_t GetDeviceGroupCode(CANPacket *packet); +uint8_t GetDeviceSerialNumber(CANPacket *packet); +uint8_t GetSenderDeviceSerialNumber(CANPacket *packet); +uint8_t GetSenderDeviceGroupCode(CANPacket *packet); + +int PacketIsInGroup(CANPacket *packet, uint8_t expectedType); +int SenderPacketIsInGroup(CANPacket *packet, uint8_t expectedType); +int SenderPacketIsOfDevice(CANPacket *packet, uint8_t expectedType); +int TargetsDevice(CANPacket *packet, uint8_t targetDeviceGroup, uint8_t targetDeviceSerialNumber); + +int GetPacketID(CANPacket *packet); +int PacketIsOfID(CANPacket *packet, uint8_t expectedID); + +void PackIntIntoDataMSBFirst(uint8_t *data, int32_t dataToPack, int startIndex); +void PackShortIntoDataMSBFirst(uint8_t *data, int16_t dataToPack, int startIndex); +/** + * @brief Read at most 4 bytes into a signed int. + * + * @param data The array of bytes to read from, storing the bytes in big-endian order. + * @param startIndex The index of the MSB. + * @param endIndex The index after the LSB. + * @return int32_t The decoded integer. + * + * @warning Be careful using this for unsigned data, as it could overflow. + */ +int32_t DecodeBytesToIntMSBFirst(const uint8_t *data, int startIndex, int endIndex); + +// Device group nibbles +#define DEVICE_GROUP_BROADCAST (uint8_t) 0x00 +#define DEVICE_GROUP_RESERVED (uint8_t) 0x01 // DO NOT USE. For future expansion +#define DEVICE_GROUP_JETSON (uint8_t) 0x02 +#define DEVICE_GROUP_MASTER DEVICE_GROUP_JETSON +#define DEVICE_GROUP_POWER (uint8_t) 0x03 +#define DEVICE_GROUP_MOTOR_CONTROL (uint8_t) 0x04 +#define DEVICE_GROUP_TELEMETRY (uint8_t) 0x05 +#define DEVICE_GROUP_GPIO_BOARDS (uint8_t) 0x06 +#define DEVICE_GROUP_SCIENCE (uint8_t) 0x07 + +// Priority bits +#define PACKET_PRIORITY_HIGH (uint8_t) 0x00 +#define PACKET_PRIORITY_NORMAL (uint8_t) 0x01 +#define PACKET_GROUP_NO_SENDER_SERIAL (uint8_t) 0x0C + +// GPIO Board Packet IDs +#define ID_GPIO_BOARD_PWM_SET_STATE (uint8_t) 0x00 +#define ID_GPIO_BOARD_PWM_SET (uint8_t) 0x01 +#define ID_GPIO_BOARD_ADC_EN_SET (uint8_t) 0x02 +#define ID_GPIO_BOARD_ADC_READ (uint8_t) 0x03 +#define ID_GPIO_BOARD_ADC_READ_RESPONSE (uint8_t) 0x04 +#define ID_GPIO_BOARD_IO_SET_STATE (uint8_t) 0x05 +#define ID_GPIO_BOARD_IO_READ (uint8_t) 0x06 +#define ID_GPIO_BOARD_IO_READ_RESPONSE (uint8_t) 0x07 +#define ID_GPIO_BOARD_IO_WRITE (uint8_t) 0x08 + +// Power Distribution Packet IDs +/*No longer needed, some of this was put into telemetry, other was put into CANPower.h + +#define ID_POWER_DIST_RAIL_SET_STATE (uint8_t) 0x00 +#define ID_POWER_DIST_RAIL_REQ_STATE (uint8_t) 0x01 +#define ID_POWER_DIST_RAIL_RESPONSE (uint8_t) 0x02 +#define ID_POWER_DIST_OVC_LIM_SET (uint8_t) 0x03 +*/ + +// Telemetry Packet IDs +#define ID_TELEMETRY_SET_MAG_OFFSET (uint8_t) 0x00 diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANPower.c b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANPower.c new file mode 100644 index 0000000..9c6d680 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANPower.c @@ -0,0 +1,50 @@ +/* File: CANPower.c + * Authors: Jaden Bottemiller, Benton Kwong, Dylan Tomberlin. + * Organization: Husky Robotics Team + * + * This file includes fuction implementations for the power boards CAN Communication + * using the Hindsight CAN Communication standard. + * Documentation: https://huskyroboticsteam.slite.com/app/channels/iU0BryG7M9/collections/aXvWTcIR6c/notes/4otlSFsSp2 + */ +#include "CANPower.h" +#include "CANPacket.h" + +//Power Rail Set State +void AssemblePowerRailsSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerialNumber, + uint8_t state) +{ + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetDeviceGroup, targetDeviceSerialNumber); + packetToAssemble->dlc = DLC_POWER_RAIL_SET; + int nextByte = WriteSenderSerialAndPacketID(packetToAssemble->data, ID_POWER_RAIL_SET); + packetToAssemble->data[nextByte] = state; + +} +uint8_t GetPowerRailsStateFromPacket(CANPacket *packet) +{ + return packet->data[2]; +} + + +//Power Set Over Current Limit +void AssembleOverCurrentPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerialNumber, + uint8_t railNumber, + uint32_t currentLim) +{ + packetToAssemble->id = ConstructCANID(PRIO_POWER_CURRENT_LIM_SET, targetDeviceGroup, targetDeviceSerialNumber); + packetToAssemble->dlc = DLC_POWER_CURRENT_LIM_SET; + int nextByte = WriteSenderSerialAndPacketID(packetToAssemble->data, ID_POWER_CURRENT_LIM_SET); + packetToAssemble->data[nextByte] = railNumber; + PackIntIntoDataMSBFirst(packetToAssemble->data, currentLim, nextByte + 1); +} +uint8_t GetOverCurrentRailNumFromPacket(CANPacket *packetToAssemble) +{ + return packetToAssemble->data[2]; +} +uint8_t GetOverCurrentLimitFromPacket(CANPacket *packetToAssemble) +{ + return DecodeBytesToIntMSBFirst(packetToAssemble->data, 3, 6); +} \ No newline at end of file diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANPower.h b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANPower.h new file mode 100644 index 0000000..edebc68 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANPower.h @@ -0,0 +1,42 @@ +/* File: CANPower.h + * Authors: Jaden Bottemiller, Benton Kwong, Dylan Tomberlin. + * Organization: Husky Robotics Team + * + * This file includes fuction prototypes for the power boards CAN Communication + * using the Hindsight CAN Communication standard. + * Documentation: https://huskyroboticsteam.slite.com/app/channels/iU0BryG7M9/collections/aXvWTcIR6c/notes/4otlSFsSp2 + */ + +#pragma once + +#include "CANPacket.h" + + +void AssemblePowerRailsSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerialNumber, + uint8_t state); +uint8_t GetPowerRailsStateFromPacket(CANPacket *packet); + + +void AssembleOverCurrentPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerialNumber, + uint8_t railNumber, + uint32_t currentLim); +uint8_t GetOverCurrentRailNumFromPacket(CANPacket *packetToAssemble); +uint8_t GetOverCurrentLimitFromPacket(CANPacket *packetToAssemble); + + +//TODO: Should we include all of this stuff in CANPacket.h? +// Motor Unit Packet IDs +#define ID_POWER_RAIL_SET (uint8_t) 0x00 +#define ID_POWER_CURRENT_LIM_SET (uint8_t) 0x01 + +// Packet DLCs +#define DLC_POWER_RAIL_SET (uint8_t) 0x02 +#define DLC_POWER_CURRENT_LIM_SET (uint8_t) 0x05 + +//Packet priorities +#define PRIO_POWER_RAIL_SET PACKET_PRIORITY_HIGH +#define PRIO_POWER_CURRENT_LIM_SET PACKET_PRIORITY_NORMAL \ No newline at end of file diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANScience.c b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANScience.c new file mode 100644 index 0000000..c8dd185 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANScience.c @@ -0,0 +1,59 @@ +#include "CANPacket.h" +#include "CANCommon.h" +#include "CANScience.h" +#include "CANMotorUnit.h" + +void AssembleScienceLazySusanPosSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t position) +{ + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = 2; + WritePacketIDOnly(packetToAssemble->data, ID_SCIENCE_LAZY_SUSAN_POS_SET); + packetToAssemble->data[1] = position; +} + +void AssembleScienceServoPacket(CANPacket *packetToAssemble, + uint8_t targetGroup, + uint8_t targetSerial, + uint8_t servo, + uint8_t degrees) +{ + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetGroup, targetSerial); + packetToAssemble->dlc = 3; + WritePacketIDOnly(packetToAssemble->data, ID_SCIENCE_SERVO_SET); + packetToAssemble->data[1] = servo; + packetToAssemble->data[2] = degrees; +} + +void AssembleScienceContServoPowerSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t servo, + int8_t power) +{ + packetToAssemble->id = ConstructCANID(PACKET_PRIORITY_NORMAL, targetDeviceGroup, targetDeviceSerial); + packetToAssemble->dlc = 3; + WritePacketIDOnly(packetToAssemble->data, ID_SCIENCE_CONT_SERVO_POWER_SET); + packetToAssemble->data[1] = servo; + packetToAssemble->data[2] = power; +} + +int8_t GetScienceContServoPowerFromPacket(const CANPacket *packet) { + return packet->data[2]; +} + +uint8_t GetScienceServoAngleFromPacket(const CANPacket *packet){ + return packet->data[2]; +} + +uint8_t GetScienceLazySusanPosFromPacket(const CANPacket *packet) { + return packet->data[1]; +} + +uint8_t GetScienceServoIDFromPacket(const CANPacket *packet){ + return packet->data[1]; +} + + diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANScience.h b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANScience.h new file mode 100644 index 0000000..6ce3134 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANScience.h @@ -0,0 +1,136 @@ +#ifndef CAN_SCIENCE_H +#define CAN_SCIENCE_H + +/** + Telemetry ID for the temperature sensor on the science station. + */ +#define CAN_SCIENCE_SENSOR_TEMPERATURE PACKET_TELEMETRY_SENSOR1 +/** + Telemetry ID for the UV sensor on the science station. + */ +#define CAN_SCIENCE_SENSOR_UV PACKET_TELEMETRY_SENSOR2 +/** + Telemetry ID for the moisture sensor on the science station. + */ +#define CAN_SCIENCE_SENSOR_MOISTURE PACKET_TELEMETRY_SENSOR3 + +/** + Packet ID for the Lazy Susan position set packet. + */ +#define ID_SCIENCE_LAZY_SUSAN_POS_SET ((uint8_t)0x0C) +/** + Packet ID for the positional servo set packet. + */ +#define ID_SCIENCE_SERVO_SET ((uint8_t) 0x0D) +/** + Packet ID for the continuous rotation servo power set packet. + */ +#define ID_SCIENCE_CONT_SERVO_POWER_SET ((uint8_t) 0x0E) + +#include "CANPacket.h" + +/** + * @brief Assemble a packet to set the position of a servo for the science station. + * + * @warning This packet is intended only for positional servos; the behavior is undefined if a + * continuous rotation servo is used. + * + * @param packetToAssemble The packet to write the data into. + * @param targetDeviceGroup The group of the target device. + * @param targetDeviceSerial Ther serial code of the target device. + * @param servo The ID of the servo. + * @param degrees The position of the servo in degrees, from 0-179. + * + * @see https://github.com/huskyroboticsteam/HindsightCAN/wiki/Science-(Sensor)-Board-Packets + */ +void AssembleScienceServoPacket(CANPacket *packetToAssemble, + uint8_t targetGroup, uint8_t targetSerial, + uint8_t servo, uint8_t degrees); + +/** + * @brief Assemble a packet to set the position of the Lazy Susan on the science + * station. + * + * Sets the position of the first cup (whichever cup is closest to the funnel on + * startup) to one of twelve positions around the circle, where 0 is the funnel. Even + * positions are under holes in the divider and odd positions are in between holes. + * + * @param packetToAssemble The packet to write the data into. + * @param targetDeviceGroup The group of the target device. + * @param targetDeviceSerial Ther serial code of the target device. + * @param position The position, from 0-11. + * + * @warning Behavior is undefined if a position above 11 is given. + * + * @see https://github.com/huskyroboticsteam/HindsightCAN/wiki/Science-(Sensor)-Board-Packets + */ +void AssembleScienceLazySusanPosSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t position); + +/** + * @brief Assemble a packet to set the power of a continuous rotation servo on + * the science station. + * + * @warning This packet is intended only for continuous rotation servos; the + * behavior is undefined if a positional servo is used. + * + * @param packetToAssemble The packet to write the data into. + * @param targetDeviceGroup The group of the target device. + * @param targetDeviceSerial Ther serial code of the target device. + * @param servo The ID of the servo. + * @param power The power of the servo, from -100 to 100. + * + * @warning Behavior is undefined if the power is outside of the [-100, 100] + * range. + * + * @see https://github.com/huskyroboticsteam/HindsightCAN/wiki/Science-(Sensor)-Board-Packets + */ +void AssembleScienceContServoPowerSetPacket(CANPacket *packetToAssemble, + uint8_t targetDeviceGroup, + uint8_t targetDeviceSerial, + uint8_t servo, + int8_t power); + +/** + * @brief Gets the servo ID from a science station servo packet. + * @param A CANPacket, that is one of the two science station servo-related packets. + * @return The servo ID for the packet. + * + * @warning This function is intended to be used only on servo-related packets; return value + * is undefined if packet is not a servo packet. + */ +uint8_t GetScienceServoIDFromPacket(const CANPacket *packet); + +/** + * @brief Gets the servo angle from a science station servo set packet. + * @param A science station servo set packet, as a CANPacket + * @return The servo angle in this packet. + * + * @warning This function is intended to be used only on positional servo set packets; return + * value is undefined otherwise. + */ +uint8_t GetScienceServoAngleFromPacket(const CANPacket *packet); + +/** + * @brief Gets the Lazy Susan position from a science station Lazy Susan position set packet. + * @param A science station Lazy Susan position set packet, as a CANPacket + * @return The Lazy Susan position in this packet. + * + * @warning This function is intended to be used only on Lazy Susan packets; return value + * is undefined otherwise. + */ +uint8_t GetScienceLazySusanPosFromPacket(const CANPacket *packet); + +/** + * @brief Gets the servo power from a science station continuous rotation servo set packet. + * @param A science station continuous rotation servo set packet, as a CANPacket + * @return The servo power in this packet. + * + * @warning This function is intended to be used only on continuous rotation servo set + * packets; return value is undefined otherwise. + */ +int8_t GetScienceContServoPowerFromPacket(const CANPacket *packet); + +#endif diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANSerialNumbers.h b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANSerialNumbers.h new file mode 100644 index 0000000..2c1df24 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CANSerialNumbers.h @@ -0,0 +1,68 @@ +/* File: CANSerialNumbers.h + * Authors: Jaden Bottemiller, Benton Kwong, Dylan Tomberlin + * Organization: Husky Robotics Team + * + * This file defines the serial numbers for each device + * on the Hindsight (PY2020 Rover). Sorted by Device Group + * + * Serial Numbers are 6bits wide. + * Documentation: https://huskyroboticsteam.slite.com/app/channels/iU0BryG7M9/collections/aXvWTcIR6c/notes/4otlSFsSp2 + */ + +// BROADCAST GROUP +// Use this serial number and the BROADCAST device group to +// broadcast a packet to all devices. +// Use this serial number and a specific device group to +// broadcast a packet to all devices within a group. +#pragma once + +#define DEVICE_SERIAL_BROADCAST (uint8_t) 0x00 + +// JETSON GROUP +#define DEVICE_SERIAL_JETSON (uint8_t) 0x01 + +// MOTOR UNIT GROUP +#define DEVICE_SERIAL_MOTOR_BASE (uint8_t) 0x01 +#define DEVICE_SERIAL_MOTOR_SHOULDER (uint8_t) 0x02 +#define DEVICE_SERIAL_MOTOR_ELBOW (uint8_t) 0x03 +#define DEVICE_SERIAL_MOTOR_FOREARM (uint8_t) 0x04 +#define DEVICE_SERIAL_MOTOR_DIFF_WRIST_L (uint8_t) 0x05 +#define DEVICE_SERIAL_MOTOR_DIFF_WRIST_R (uint8_t) 0x06 +#define DEVICE_SERIAL_MOTOR_HAND (uint8_t) 0x07 + +#define DEVICE_SERIAL_MOTOR_CHASSIS_FL (uint8_t) 0x08 //Front Left +#define DEVICE_SERIAL_MOTOR_CHASSIS_FR (uint8_t) 0x09 +#define DEVICE_SERIAL_MOTOR_CHASSIS_BL (uint8_t) 0x0a +#define DEVICE_SERIAL_MOTOR_CHASSIS_BR (uint8_t) 0x0b //Back Right + +// if science has multiple motors, should name for function, not increment +// TODO: add drill arm motors +#define DEVICE_SERIAL_MOTOR_SCIENCE_1 (uint8_t) 0x0c + +//Power group +#define DEVICE_SERIAL_POWER_BATT_MAN (uint8_t) 0x01 +#define DEVICE_SERIAL_POWER_CHASSIS_MAIN (uint8_t) 0x02 +#define DEVICE_SERIAL_POWER_CHASSIS_DRIVE_L (uint8_t) 0x03 //Left +#define DEVICE_SERIAL_POWER_CHASSIS_DRIVE_R (uint8_t) 0x04 //Right +#define DEVICE_SERIAL_POWER_ARM_LOWER_1 (uint8_t) 0x05 //may have more arm +#define DEVICE_SERIAL_POWER_ARM_UPPER_1 (uint8_t) 0x06 +#define DEVICE_SERIAL_POWER_SCIENCE (uint8_t) 0x07 + +//Telemetry group +#define DEVICE_SERIAL_TELEM_LOCALIZATION (uint8_t) 0x01 +#define DEVICE_SERIAL_TELEM_IMU (uint8_t) 0x02 +#define DEVICE_SERIAL_TELEM_TEMPERATURE (uint8_t)0x03 + +// Science group +#define DEVICE_SERIAL_SCIENCE_STATION ((uint8_t) 0x01) + +//Group numbers +/* +#define DEVICE_GROUP_BROADCAST 0x0 +#define DEVICE_GROUP_RESERVED 0x1 +#define DEVICE_GROUP_MASTER 0x2 +#define DEVICE_GROUP_POWER 0x3 +#define DEVICE_GROUP_MOTORS 0x4 +#define DEVICE_GROUP_TELEM 0x5 +#define DEVICE_GROUP_GPIO 0x6 +*/ diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CMakeLists.txt b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CMakeLists.txt new file mode 100644 index 0000000..c068578 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/CMakeLists.txt @@ -0,0 +1,106 @@ +cmake_minimum_required(VERSION 3.12) + +# Include some helper packages for installation +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +# Define project. Version number should be updated with changes according to +# Semantic Versioning (https://semver.org) +# - Major version (first number) should be incremented when +# backwards-incompatible changes are made (such as removing packets) +# - Minor version (second number) should be incremented when features are added +# in a backwards-compatible manner (such as adding packets, or adding packet +# fields) +# - Patch version (third number) should be incremented when you make small +# backwards-compatible changes or bug fixes. +project(HindsightCAN + LANGUAGES C + VERSION 1.1.3) # <-- Change when you make commits to master; see above + +# The list of header files for the CAN library +set(CAN_HEADERS + CANCommon.h + CANGPIO.h + CANLibrary.h + CANLocalization.h + CANMotorUnit.h + CANPacket.h + CANPower.h + CANScience.h + CANSerialNumbers.h + Port.h) + +# The list of sources for the CAN library +set(CAN_SOURCES + CANCommon.c + CANGPIO.c + CANLocalization.c + CANMotorUnit.c + CANPacket.c + CANPower.c + CANScience.c + PortFiles/PortTemplate.c) + +# Preprocessor flag required for the CAN library; see +# https://github.com/huskyroboticsteam/HindsightCAN#how-to-port-to-a-new-chip +add_definitions(-DCHIP_TYPE=CHIP_TYPE_TEMPLATE) + +# Define a static library, compiled from the files in the CAN_SOURCES list +add_library(HindsightCAN STATIC ${CAN_SOURCES}) +# Include the header files when installed to the system +target_include_directories(HindsightCAN + PUBLIC $) + +### Installation config ### + +# If the project name is defined (i.e. if this project is not being included as +# a submodule) +if (DEFINED PROJECT_NAME) + # Install the static library to the system, and export the list of installed + # targets to hindsight-can-targets + install(TARGETS HindsightCAN + EXPORT hindsight-can-targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + # Install the header files to the system + install(FILES ${CAN_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/HindsightCAN) + + # Install the list of targets to the system + install(EXPORT hindsight-can-targets + FILE HindsightCANTargets.cmake + NAMESPACE HindsightCAN:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/HindsightCAN) + + # Generate a CMake package configuration file for the library + configure_package_config_file( + ${CMAKE_CURRENT_LIST_DIR}/cmake/HindsightCANConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/HindsightCANConfig.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/HindsightCAN) + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/HindsightCANConfigVersion.cmake + VERSION ${CMAKE_PROJECT_VERSION} + COMPATIBILITY SameMinorVersion) + # Install the CMake package configuration file + install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/HindsightCANConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/HindsightCANConfigVersion.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/HindsightCAN) + + ### Packaging configuration ### + # Borrowed/adapted from https://www.scivision.dev/cmake-cpack-basic/ + set(CPACK_GENERATOR "DEB") + set(CPACK_PACKAGE_NAME "hindsight-can") + # Version format: major.minor.patch + set(CPACK_PACKAGE_VERSION "${CMAKE_PROJECT_VERSION}") + set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}) + set(CPACK_PACKAGE_CONTACT "Husky Robotics Team ") + set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md") + set(CPACK_PACKAGE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + install(FILES ${CPACK_RESOURCE_FILE_README} + DESTINATION share/docs/${PROJECT_NAME}) + + include(CPack) + +endif() + + diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/Port.h b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/Port.h new file mode 100644 index 0000000..2ac3a7b --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/Port.h @@ -0,0 +1,52 @@ +/* File: Port.h + * Authors: Jaden Bottemiller, Benton Kwong, Dylan Tomberlin + * Organization: Husky Robotics Team + * + * This file includes function prototypes for all functions which must be + * implemented for each ported device. Just make a .c file called + * Port[DeviceName].c based on PortTemplate. + * + * Compile insturctions: in addition to cross compiling, you will need to + * define the constant CHIP_TYPE in your compiler options. On GCC, use + * -D CHIP_TYPE=CHIP_TYPE_TEMPLATE + * But obviously substitute template for your own constant. Add it to the list + * of constants below if its not already been defined. + * Documentation: https://huskyroboticsteam.slite.com/app/channels/iU0BryG7M9/collections/aXvWTcIR6c/notes/4otlSFsSp2 + */ +#pragma once + +#include "CANPacket.h" + + +void InitCAN(int deviceGroup, int deviceAddress); + +//TODO: define constants for these error codes +//Returns 0x0 for successful send +//returns 0x1 for generic error +//returns 0x2 all output buffers are full +//Reserve higher numbers for future error codes +int SendCANPacket(CANPacket *packetToSend); + +//Returns 0x0 for SUCCESSFUL packet return +//Returns 0x1 for no message received +//Returns 0x2 for generic error +//Reserve higher numbers for future error codes +int PollAndReceiveCANPacket(CANPacket *receivedPacket); + +uint8_t getLocalDeviceSerial(); +uint8_t getLocalDeviceGroup(); + +//Returns constant +uint8_t getChipType(); + +//Chip type constants +//TODO: Find specific chip names +#define CHIP_TYPE_TEMPLATE 0x00 +#define CHIP_TYPE_STM32Fxxx 0x01 +#define CHIP_TYPE_PSOC_CY8C4248AZI_L485 0x02 +#define CHIP_TYPE_AT90CANxxx 0x03 + +//Error code constants +#define ERROR_NONE 0x00 +#define ERROR_GENERIC_ERROR 0x01 +#define ERROR_NULL_POINTER 0x02 diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/PortFiles/PortAT90CANxxx.c b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/PortFiles/PortAT90CANxxx.c new file mode 100644 index 0000000..facd603 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/PortFiles/PortAT90CANxxx.c @@ -0,0 +1,233 @@ +/* + * Documentation: https://huskyroboticsteam.slite.com/app/channels/iU0BryG7M9/collections/aXvWTcIR6c/notes/4otlSFsSp2 + */ +#if CHIP_TYPE == CHIP_TYPE_AT90CANxxx + +#include "Port.h" +#include + +#ifndef F_CPU +#define F_CPU 16000000L +#endif + +#include "config.h" + +#define CAN_1000_BAUD 0x020413L +#define CAN_500_BAUD 0x060413L +#define CAN_250_BAUD 0x0E0413L +#define CAN_125_BAUD 0x1E0413L +#define CAN_100_BAUD 0x260413L + +#include +#include +#include + +volatile uint8_t msgs_av; //Number of messages unclaimed messages +volatile uint8_t rxed_mobs[2]; //Tracks which MObs have messages received + +uint8_t devGrp, devSer; + +/*Selects the MOB to operate on*/ +void inline select_mob(uint8_t mob){ + CANPAGE = ((mob & 0x0F) << 4); +} + +/*Disables the interrupt for the specified MOb*/ +void disable_mob_interrupt(uint8_t mob){ + if(mob < 8){ + CANIE2 &= ~(1 << mob); + } else { + CANIE1 &= ~(1 << (mob - 8)); + } +} + +/*Enable the interrupt for the specified MOb*/ +void enable_mob_interrupt(uint8_t mob){ + if(mob < 8){ + CANIE2 |= (1 << mob); + } else { + CANIE1 |= (1 << (mob - 8)); + } +} + +/*CAN controller interrupt handler*/ +ISR(CANIT_vect){ + uint8_t canpage = CANPAGE; //Save CAN page + if((CANHPMOB & 0xF0) != 0xF0){ //Message io? + int mob = (CANHPMOB >> 4); + select_mob(mob); + if(CANSTMOB & (1 << TXOK)){ //TX + /*Reset the MOb*/ + CANSTMOB &= 0; + CANCDMOB = 0; + enable_mob_interrupt(mob); + } else { //RX + msgs_av++; //Increase count of messages + rxed_mobs[!!(mob & 8)] |= (1 << (mob & 7)); // Mark which MOb has a message + CANSTMOB &= 0; //Reset the MOb + disable_mob_interrupt(mob); + } + } else { + CANGIT |= 0; //Error interrupt - Handle these? + } + CANPAGE = canpage; //restore CAN page +} + +/*Reset the receive filter for the given MOb*/ +void set_mob_rx_filter(int mob){ + select_mob(mob); + uint16_t RX_mask = 0x3FF; // mask out priority bit, compare on everything else + uint16_t RX_tag = 0x3F; //0th MOb is for broadcast packets, group = 0, serial = 0x3F + if(mob > 0 && mob <= 2){ //1st und 2nd MOb is for device group broadcasts, match device group and 0x3F serial + RX_tag = (devGrp << 6) | 0x3F; + } else if(mob > 2 && mob <= 4){ //2nd and 3rd MOb is for the device specific message. Match the whole ID + RX_tag = (devGrp << 6) | devSer; + } + + CANIDM4 = 0; + CANIDM3 = 0; + CANIDT4 = 0; + CANIDT3 = 0; + CANIDT2 = ((RX_tag & 7) << 5); + CANIDT1 = ((RX_tag & 0x7F8) >> 3); + CANIDM2 = ((RX_mask & 7) << 5); + CANIDM1 = ((RX_mask & 0x7F8) >> 3); +} + +/*Initalizes and enables the CAN controller +Parameters: +uint32_t rate: the baud rate selection +uint8_t txmobs: how many MOBs to dedicate to transmission +uint8_t mode: The mode to operate the CAN controller in +*/ +void init_CAN(uint32_t rate, uint16_t deviceGroup, uint16_t deviceSerial){ + CANGCON |= (1<> 16; + CANBT2 = (uint32_t)(rate & 0x00FF00L) >> 8; + CANBT3 = (uint32_t)(rate & 0x0000FFL); + CANGIE = (1 << CANIT) | (1 << ENRX) | (1 << ENTX); //Enable CAN interrupts + CANTCON = 255; //Set the can timer to run at 1/2048th of F_CPU + rxed_mobs[0] = rxed_mobs[1] = 0; + uint8_t i; + /*Initialize MOBs*/ + for(i = 0;i < 15;i++){ + if(i <= 4){ /*MObs <= 4 are RX mobs*/ + /*Set up the match registers*/ + CANSTMOB &= 0; + set_mob_rx_filter(i); + CANCDMOB = (1 << CONMOB1); //Mark RX mobs + enable_mob_interrupt(i); // enable the receive interrupt + } else { + CANCDMOB = 0; //Mark as TX MOb + } + } + msgs_av = 0; + //Enable the CAN controller + CANGCON = (1 << ENASTB); +} + +/*Returns the number of CAN messages waiting*/ +uint8_t inline CAN_msg_available(){ + return msgs_av; +} + +/*Finds a free MOb or returns -1 if they're all used*/ +int8_t find_free_mob(){ + uint8_t i; + uint8_t status; + for(i = 0;i < 15;i++){ + select_mob(i); + status = CANCDMOB; + if(!(status & ((1 << CONMOB1) | (1 << CONMOB0)))){ + return i; + } + } + return -1; +} + + +void InitCAN(int deviceGroup, int deviceAddress) +{ + init_CAN(CAN_125_BAUD, deviceGroup, deviceAddress); +} + +int SendCANPacket(CANPacket *packetToSend) +{ + uint8_t i; + int8_t mob = find_free_mob(); + if(mob == -1){ + return 0x02; //No MObs available + } + select_mob(mob); + CANSTMOB &= 0; + CANCDMOB = packetToSend->dlc & 0x0F; + for(i = 0;i < packetToSend->dlc && i < 8;i++){ //Copy the data into the MOb + CANMSG = packetToSend->data[i]; + } + CANIDT4 = 0; //CAN v2.0 - we don't care + CANIDT3 = 0; + CANIDT2 = ((packetToSend->id & 7) << 5); + CANIDT1 = ((packetToSend->id & 0x7F8) >> 3); + CANCDMOB |= (1<dlc = CANCDMOB & 0x0F; //Length in the lower 8 bits + receivedPacket->id = (CANIDT2 >> 5) | ((uint16_t)CANIDT1 << 3); + for(i = 0;i < receivedPacket->dlc && i < 8;i++){ + receivedPacket->data[i] = CANMSG; //Get the data from the MOb and copy it into the buffer + } + //Atomically decrement the number of messages available + cli(); + msgs_av--; + sei(); + /*Reset the MOb*/ + set_mob_rx_filter(mob); + enable_mob_interrupt(mob); + rxed_mobs[!!(mob & 8)] &= ~(1 << (mob & 7)); //Mark that the message has been taken + CANCDMOB = (1<id; + PSoCMessage.rtr = 0x0; + PSoCMessage.ide = 0x0;//Not extended + PSoCMessage.dlc = packetToSend->dlc; + PSoCMessage.irq = 0x0; + PSoCMessage.msg = &PSoCData; + + memcpy(PSoCData.byte, packetToSend->data, 8); + + if(CAN_SendMsg(&PSoCMessage) == CYRET_SUCCESS) { + return ERROR_NONE; + } else + { + return ERROR_GENERIC_ERROR; + } +} +// +int PollAndReceiveCANPacket(CANPacket *receivedPacket) +{ + if(!receivedPacket) { + return ERROR_NULL_POINTER; + } + volatile uint8_t size = FIFOSize(); + if(size) + { + *(receivedPacket) = latestMessage[latestMessageHead]; + countRemoveFIFO(); + return ERROR_NONE; + } + return 0x02; //No message received error +} + +uint8_t getLocalDeviceSerial() +{ + return deviceAddress; +} +uint8_t getLocalDeviceGroup() +{ + return deviceGroup; +} + +uint8_t getChipType() +{ + return CHIP_TYPE; +} + +//helper function calculate size of Fifo +uint8_t FIFOSize(){ + if(latestMessageFull) { + return FIFO_SIZE; + } + else if(latestMessageHead < latestMessageTail) { + return latestMessageTail - latestMessageHead; + } + else if(latestMessageHead > latestMessageTail) { + return (FIFO_SIZE - latestMessageHead) + latestMessageTail; + } + else { // latestMessageHead == latestMessageTail && !latestMessageFull + return 0; + } +} + +void countAddFIFO(){ + latestMessageTail++; + if(latestMessageTail >= FIFO_SIZE){ + latestMessageTail = 0; + } + if(latestMessageFull) { + latestMessageHead++; + if(latestMessageHead >= FIFO_SIZE) { + latestMessageHead = 0; + } + } + latestMessageFull = (latestMessageHead == latestMessageTail); +} + +void countRemoveFIFO(){ + if(FIFOSize() > 0) { + latestMessageHead++; + if(latestMessageHead >= FIFO_SIZE) { + latestMessageHead = 0; + } + } +} + + + +CY_ISR(CAN_FLAG_ISR) +{ + + //*(reg32*)0x402F0000 = CAN_RX_MESSAGE_MASK & CAN_SST_FAILURE_MASK & CAN_CRC_ERROR_MASK; //Clear Receive Message flag + CAN_INT_SR_REG = CAN_RX_MESSAGE_MASK; + uint32_t statusReg = (uint32_t) CAN_BUF_SR_REG; //Hardcoded for speed, translation from reg + uint8_t mailbox; + + if(statusReg & 0b1) { // mailbox0 is full (individual) + mailbox = CAN_RX_MAILBOX_0; + } + else if(statusReg & 0b10) { // mailbox1 is full (broadcast) + mailbox = CAN_RX_MAILBOX_1; + } + else if(statusReg & 0b100) { // mailbox2 is full (group broadcast) + mailbox = CAN_RX_MAILBOX_2; + } + else if(statusReg & 0b1000) { // mailbox3 is full currently recieves anything enable in top design + mailbox = CAN_RX_MAILBOX_3; + } + + latestMessage[latestMessageTail].id = CAN_GET_RX_ID(mailbox); + latestMessage[latestMessageTail].dlc = CAN_GET_DLC(mailbox); + latestMessage[latestMessageTail].data[0] = CAN_RX_DATA_BYTE1(mailbox); + latestMessage[latestMessageTail].data[1] = CAN_RX_DATA_BYTE2(mailbox); + latestMessage[latestMessageTail].data[2] = CAN_RX_DATA_BYTE3(mailbox); + latestMessage[latestMessageTail].data[3] = CAN_RX_DATA_BYTE4(mailbox); + latestMessage[latestMessageTail].data[4] = CAN_RX_DATA_BYTE5(mailbox); + latestMessage[latestMessageTail].data[5] = CAN_RX_DATA_BYTE6(mailbox); + latestMessage[latestMessageTail].data[6] = CAN_RX_DATA_BYTE7(mailbox); + latestMessage[latestMessageTail].data[7] = CAN_RX_DATA_BYTE8(mailbox); + countAddFIFO(); + + //CAN_ReceiveMsg(messagePresentFlag); + CAN_RX_ACK_MESSAGE(mailbox); +} +#endif //CHIP_TYPE == CHIP_TYPE_PSOC_CY8C4248AZI_L485 diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/PortFiles/PortTemplate.c b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/PortFiles/PortTemplate.c new file mode 100644 index 0000000..87244dc --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/PortFiles/PortTemplate.c @@ -0,0 +1,39 @@ +/* + * Documentation: https://huskyroboticsteam.slite.com/app/channels/iU0BryG7M9/collections/aXvWTcIR6c/notes/4otlSFsSp2 + */ +#if CHIP_TYPE == CHIP_TYPE_TEMPLATE//Replace this with the chip you are porting + +#include "../Port.h" + +void InitCAN(int deviceGroup, int deviceAddress) +{ + // Implement hardware initialization of CAN + // Including receive filters +} +int SendCANPacket(CANPacket *packetToSend) +{ + //Implement sending/ queing to send packet +} +int PollAndReceiveCANPacket(CANPacket *receivedPacket) +{ + //Implement get next CAN packet from buffers/registers +} + +uint8_t getLocalDeviceSerial() +{ + //Reading DIP switches? Hard coded? + //This might be board specific, rather than chip specific. + return DEVICE_SERIAL_MOTOR_CHASSIS_FR; // example value (also used for testing) +} +uint8_t getLocalDeviceGroup() +{ + //Definitely board specific. + return DEVICE_GROUP_MOTOR_CONTROL; // example value (also used for testing) +} + +uint8_t getChipType() +{ + return CHIP_TYPE; + //Should be same for all ports, just not sure where to put it. +} +#endif //CHIP_TYPE == CHIP_TYPE_TEMPLATE diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/README.md b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/README.md new file mode 100644 index 0000000..b1c189f --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/README.md @@ -0,0 +1,37 @@ +# Hindsight CAN Drivers +## Use these drivers for Embedded CAN Communications on Hindsight (PY2020) Rover. + +### More information +For more information on the protocol standard, please visit the Slite documentation HUB for Husky Robotics. The protocol documentation is under the Electronics/CAN/CAN Protocol sheet.
+ +## Implementation information + +### How To Use +_Note: You will need to have the standard C libraries compiled with your code. This is usually done automatically. If you have problems with `stdint.h` let Jaden know._

+*STEP 1*
+Within your repository for your firmware create a submodule with this repo in it. You can follow [this guide](https://git-scm.com/book/en/v2/Git-Tools-Submodules) for instructions on how to do that and explanations for what exactly a submodule is.
+Usually this command is done as follows:
+```git submodule add git@github.com:huskyroboticsteam/HindsightCAN.git path_to_your_source```
+And then:
+```git submodule init```
+If you **put these files in the same folder as your source code**, you can skip the next step.
+ +*STEP 2*
+Add all of the source files to your compiler path within whatever IDE you are using. This will be dependent on what IDE you are using and how you are building your code. Since most people here will not be using a Makefile, we cannot give explicit direction on how to do this. If you have trouble, talk to Jaden, Dylan, or someone else who has done it. Please try Googling it and figuring it out yourself first though.

+*STEP 3*
+Import all of the relevant headers into your project. All devices will need to import `CANCommon.h`, which gives you access to the files that are necessary for interpretting and creating Common Mode packets. But if you are working on a Motor Unit for example, you will need to also import `CANMotorUnit.h` to give you access to the functions pertinent to specifically a motor unit. If you are writing software for the Jetson, you will need to import all of them! Yay! /s

+*STEP 4*
+You have to implement all of the hardware functionality for physically sending and receiving packets. In this repo, you will find a `Port.h` file. Please create a C-code file called `Port[DeviceName].c`, in that file `#include "Port.h"` and implement all of the functions in that header. Right now these CAN Drivers do not actually call those functions, so arbitrating when to send and receive is up to you as the firmware developer, so you will have to manually call those functions. This header is just to get you started and to make sure everyone is on the same page. + +### How to Update This Library in Your Project +To update your submodule call: ```git submodule update```, this should pull the changes from the master branch of this repo so you can get updates when we fix our broken code. + +### How to Port to a New Chip +This library was built to be portable to any microcontroller or processor. To port it: +1.) Create Port[chipname].c file in a folder of port files (you can copy the PortTemplate.c file to get started) +2.) Add #if CHIP_TYPE == CHIP_TYPE_xxx to top of C file, #endif at the bottom +3.) Add CHIP_TYPE_xxx to the list of defined chips in port.h, with an incremented value. +4.) Implement functs whose prototypes are in port.h +5.) Compile, passing in constant to gcc using -D CHIP_TYPE=CHIP_TYPE_xxx . + +Using this method, there should only be one implementation of these port functions visible to the complier after the preprocessor has run. diff --git a/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/cmake/HindsightCANConfig.cmake.in b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/cmake/HindsightCANConfig.cmake.in new file mode 100644 index 0000000..f557940 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/HindsightCAN/cmake/HindsightCANConfig.cmake.in @@ -0,0 +1,10 @@ +@PACKAGE_INIT@ + + +# Avoid repeatedly including the targets +if(NOT TARGET HindsightCAN::HindsightCAN) + # Provide path for scripts + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") + + include(${CMAKE_CURRENT_LIST_DIR}/HindsightCANTargets.cmake) +endif() diff --git a/battery management board 2025/Battery Management Board.cydsn/INA226.c b/battery management board 2025/Battery Management Board.cydsn/INA226.c new file mode 100644 index 0000000..459d5b4 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/INA226.c @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2016 by Stefano Speretta + * + * INA226: a library to provide high level APIs to interface with the + * TI INA226 current sensor. It is possible to use this library in + * Energia (the Arduino port for MSP microcontrollers) or in other + * toolchains. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * version 3, both as published by the Free Software Foundation. + * + */ + +#include + + +uint8_t init_INA226() +{ + I2C_INA226_Start(); + reset(); + setShuntResistor(100); + return ping(); +} + +/** + * Reset the INA226 + * + * Returns: + * unsigned char 0 succes + * 1 fail + */ +uint8_t reset() +{ + uint8_t data[2]; + data[0] = (INA226_RESET) >> 8; + data[1] = 0; //INA226_RESET; + return writeRegister(INA226_REG_CONFIG, data, 2); +} + +/** + * Verify if INA226 is present + * + * Returns: + * unsigned char 1 device found + * 0 device not found + */ +uint8_t ping() +{ + uint8_t id[2]; + readRegister(INA226_REG_ID, id, 2); + return ((id[0] << 8) + id[1]); +} + +/** + * Sets the shunt resistor value in mOhm + * + * Parameters: + * unsigned short shunt shunt resistor value in mOhm + * + * Returns: + * unsigned char 0 success + * 1 fail + */ +uint8_t setShuntResistor(uint8_t shunt) +{ + uint8_t data[2]; + data[0] = (INA226_CALIBRATION_REF / shunt) >> 8; + data[1] = INA226_CALIBRATION_REF / shunt; + return writeRegister(INA226_REG_CALIBRATION, data, 2); +} + +uint8_t setAlertLimitBusVoltage(uint8_t limit) +{ + uint8_t data[1]; + data[0] = (uint8_t)(0.8 * limit); + return writeRegister(INA226_REG_ALERTLIMIT, data, 1); //translate voltage to whole decimal (/2.5uV) +} + +uint8_t setAlertEnableBusUnderVoltage() +{ + uint8_t data[2]; + data[0] = (INA226_BIT_BUL) >> 8; + data[1] = 0; //INA226_BIT_BUL; + return writeRegister(INA226_REG_MASKENABLE, data, 2); +} + +/** + * Returns the bus voltage in mV + * + * Parameters: + * unsigned short & bus voltage in mV + * + * Returns: + * unsigned char 0 success + * 1 fail + */ +uint8_t getVoltage(uint8_t *v) +{ + uint8_t ret = readRegister(INA226_REG_BUSVOLTAGE, v, 2); + *v = *v + (*v >> 2); + if (ret) + { + *v = 0; + } + return ret; +} + +/** + * Returns the voltage across the shunt resistor + * + * Parameters: + * signed short & bus voltage (LSB = 2.5 uV) + * + * Returns: + * unsigned char 0 success + * 1 fail + */ +uint8_t getShuntVoltage(uint8_t *v) +{ + uint8_t ret = readRegister(INA226_REG_SHUNTVOLTAGE, v, 2); + if (ret) + { + *v = 0; + } + return ret; +} + +/** + * Returns the current through the shunt resistor + * + * Parameters: + * signed short & current in mA + * + * Returns: + * unsigned char 0 success + * 1 fail + */ +uint8_t getCurrent(uint8_t *c) +{ + uint8_t ret = readRegister(INA226_REG_CURRENT, c, 2); + *c/=8; + if (ret) + { + *c = 0; + } + return ret; +} + +/** + * Returns the power across the load in mW + * + * Parameters: + * unsigned short & power in mW + * + * Returns: + * unsigned char 0 success + * 1 fail + */ +uint8_t getPower(uint8_t *p) +{ + uint8_t ret = readRegister(INA226_REG_POWER, p, 2); + *p = (*p * 3) + (*p >> 3); + if (ret) + { + *p = 0; + } + return ret; +} + +// Returns the value of the selected internal register +uint8_t readRegister(uint8_t reg, uint8_t *output, uint8_t cnt) +{ + I2C_INA226_I2CMasterClearStatus(); //clear the garbage + + int ms_timeout = 20; + uint32_t error = 0; // this is the "status" we usually use in our R/W functions + uint8_t idx; + error = I2C_INA226_I2CMasterSendStart(DEVICE_ADDR, I2C_INA226_I2C_WRITE_XFER_MODE, ms_timeout); + + error = I2C_INA226_I2CMasterWriteByte(reg, ms_timeout); + + error = I2C_INA226_I2CMasterSendStop(ms_timeout); + + error = I2C_INA226_I2CMasterSendStart(DEVICE_ADDR, I2C_INA226_I2C_READ_XFER_MODE, ms_timeout); + //PrintInt(BNO055_iERROR); + for (idx = 0; (idx < cnt) && (error == 0); idx++) + { + if (idx < cnt-1) + { + I2C_INA226_I2CMasterReadByte(I2C_INA226_I2C_ACK_DATA, &output[idx], ms_timeout); + } + else + { + I2C_INA226_I2CMasterReadByte(I2C_INA226_I2C_NAK_DATA, &output[idx], ms_timeout); + } + } + // Check for BNO055_iERROR before proceeding + error = I2C_INA226_I2CMasterSendStop(ms_timeout); + + return (uint8_t)error; +} + +//Sets the value of the selected internal register +uint8_t writeRegister(uint8_t reg, uint8_t *data, uint8_t cnt) +{ + + I2C_INA226_I2CMasterClearStatus(); //clear the garbage + uint8_t data_pack[cnt + 1]; + data_pack[0] = reg; + data_pack[1] = data[0]; + data_pack[2] = data[1]; + + int status = I2C_INA226_I2CMasterWriteBuf(DEVICE_ADDR, data_pack, cnt, I2C_INA226_I2C_MODE_COMPLETE_XFER); + while ((I2C_INA226_I2CMasterStatus() & I2C_INA226_I2C_MSTAT_WR_CMPLT) == 0u) //should wait for write buffer to complete + { + Print("\r\nWRITE TO: \n\r"); + PrintInt(reg); + } + return status; +} diff --git a/battery management board 2025/Battery Management Board.cydsn/INA226.h b/battery management board 2025/Battery Management Board.cydsn/INA226.h new file mode 100644 index 0000000..48fc8f8 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/INA226.h @@ -0,0 +1,65 @@ +#include "project.h" +#include "stdlib.h" +#include + +#define DEVICE_ADDR 0x40 + +#define INA226_DEVICE_ID (0x2260) +#define INA226_RESET (0x8000) +#define INA226_CALIBRATION_REF (0xA000) + +#define INA226_REG_CONFIG (0x00) +#define INA226_REG_SHUNTVOLTAGE (0x01) +#define INA226_REG_BUSVOLTAGE (0x02) +#define INA226_REG_POWER (0x03) +#define INA226_REG_CURRENT (0x04) +#define INA226_REG_CALIBRATION (0x05) +#define INA226_REG_MASKENABLE (0x06) +#define INA226_REG_ALERTLIMIT (0x07) +#define INA226_REG_ID (0xFF) + +#define INA226_BIT_SOL (0x8000) +#define INA226_BIT_SUL (0x4000) +#define INA226_BIT_BOL (0x2000) +#define INA226_BIT_BUL (0x1000) +#define INA226_BIT_POL (0x0800) +#define INA226_BIT_CNVR (0x0400) +#define INA226_BIT_AFF (0x0010) +#define INA226_BIT_CVRF (0x0008) +#define INA226_BIT_OVF (0x0004) +#define INA226_BIT_APOL (0x0002) +#define INA226_BIT_LEN (0x0001) + +//Debugging macros +#define Print(message) DBG_UART_UartPutString(message) +#define PrintChar(character) DBG_UART_UartPutChar(character) +#define PrintInt(integer) DBG_UART_UartPutString(itoa(integer, debugOutput, 10)) +#define PrintIntBin(integer) DBG_UART_UartPutString(itoa(integer, debugOutput, 2)) +#define BlinkDBG() DBG_Write( ! DBG_Read() ) + +char debugOutput[32]; + + +uint8_t address; + +uint8_t reset(); +uint8_t ping(); + +uint8_t init_INA226(); + +// configure the device +uint8_t setShuntResistor(uint8_t); +uint8_t setAlertLimitBusVoltage(uint8_t); +uint8_t setAlertEnableBusUnderVoltage(); + + +// functions used to retrieve the measurements from the device +uint8_t getShuntVoltage(uint8_t*); +uint8_t getVoltage(uint8_t*); +uint8_t getCurrent(uint8_t*); +uint8_t getPower(uint8_t*); + + +// only for use +uint8_t readRegister(uint8_t, uint8_t*, uint8_t); +uint8_t writeRegister(uint8_t, uint8_t*, uint8_t); diff --git a/battery management board 2025/Battery Management Board.cydsn/TopDesign/TopDesign.cysch b/battery management board 2025/Battery Management Board.cydsn/TopDesign/TopDesign.cysch new file mode 100644 index 0000000000000000000000000000000000000000..ddaa1e5d52a753bef9b07e311d3e84cd31e24165 GIT binary patch literal 124052 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 M_A5 1 || read < 0) ? 0 : + (baseAddress << 1 | read); +} + +/* +uint8 INA226Read(uint32 address, uint8* store) +{ + while ( + I2C_Brain_I2CMasterStatus() == I2C_Brain_I2C_MSTAT_XFER_INP || + I2C_INA226_I2CMasterStatus() == I2C_INA226_I2C_MSTAT_XFER_INP + ) {} + uint32 status = I2C_Brain_I2CMasterSendStart(address, I2C_Brain_I2C_READ_XFER_MODE, + I2C_TIMEOUT); + while (status != I2C_Brain_I2C_MSTR_NOT_READY) { + status = I2C_Brain_I2CMasterSendStart(address, I2C_Brain_I2C_READ_XFER_MODE, + I2C_TIMEOUT); + } + if (status == I2C_Brain_I2C_MSTR_NO_ERROR) { + I2C_Brain_I2CMasterReadByte(I2C_Brain_I2C_NAK_DATA, store, I2C_TIMEOUT); + return SUCCESS; + } else { + return FAIL; + } +} +*/ + +/* +uint8_t JettySend(uint32 address, uint8* store) +{ + while ( + I2C_Brain_I2CMasterStatus() == I2C_Brain_I2C_MSTAT_XFER_INP || + Jetty_I2CMasterStatus() == I2C_INA226_I2C_MSTAT_XFER_INP + ) {} + uint32 status = I2C_Brain_I2CMasterSendStart(address, I2C_Brain_I2C_WRITE_XFER_MODE, + I2C_TIMEOUT); + while (status != I2C_Brain_I2C_MSTR_NOT_READY) { + status = I2C_Brain_I2CMasterSendStart(address, I2C_Brain_I2C_READ_XFER_MODE, + I2C_TIMEOUT); + } + if (status == I2C_Brain_I2C_MSTR_NO_ERROR) { + I2C_Brain_I2CMasterReadByte(I2C_Brain_I2C_NAK_DATA, store, I2C_TIMEOUT); + return SUCCESS; + } else { + return FAIL; + } +} +*/ + +uint32 convertValue(uint8 adc_value) { + return (uint32)adc_value; +} + + +void BatteryBalanceInit(void) +{ + // I2C_Brain_Start(); + + // Jetty_Start(); + //Jetty_I2CSlaveSetAddress(JETTY); +} + +/* +void BatteryBalance(uint8* battery, uint8* value, float32* threshold) +{ + float32 newVal = convertValue(*value); + + if (newVal < 0.1) { + if (*battery == MICHAEL) { + MICHAEL_LOW = 1; + // update LED + } else { + KEN_LOW = 1; + // update LED + } + } + + if (MICHAEL_LOW && *battery == MICHAEL && newVal >= 0.1) { + MICHAEL_LOW = 0; + // update LED + } else if (KEN_LOW && *battery == KEN && newVal >= 0.1) { + KEN_LOW = 0; + // update LED + } + + if (MICHAEL_LOW && !KEN_LOW) { + // switch to KEN + *battery = KEN; + } else if (KEN_LOW && !MICHAEL_LOW) { + // switch to MICHAEL + *battery = MICHAEL; + } else if (!(KEN_LOW || MICHAEL_LOW)) { + if (newVal < *threshold - 0.1) { + // switch battery + if (*battery == MICHAEL) { + *battery = KEN; + } else { + *battery = MICHAEL; + } + *threshold = newVal; + } + } +} +*/ + +/* [] END OF FILE */ diff --git a/battery management board 2025/Battery Management Board.cydsn/cyapicallbacks.h b/battery management board 2025/Battery Management Board.cydsn/cyapicallbacks.h new file mode 100644 index 0000000..1d312fa --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/cyapicallbacks.h @@ -0,0 +1,21 @@ +/* ======================================== + * + * Copyright YOUR COMPANY, THE YEAR + * All Rights Reserved + * UNPUBLISHED, LICENSED SOFTWARE. + * + * CONFIDENTIAL AND PROPRIETARY INFORMATION + * WHICH IS THE PROPERTY OF your company. + * + * ======================================== +*/ +#ifndef CYAPICALLBACKS_H + #define CYAPICALLBACKS_H + + + /*Define your macro callbacks here */ + /*For more information, refer to the Writing Code topic in the PSoC Creator Help.*/ + + +#endif /* CYAPICALLBACKS_H */ +/* [] */ diff --git a/battery management board 2025/Battery Management Board.cydsn/io.h b/battery management board 2025/Battery Management Board.cydsn/io.h new file mode 100644 index 0000000..77b40b9 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/io.h @@ -0,0 +1,42 @@ +/* ======================================== + * + * Copyright YOUR COMPANY, THE YEAR + * All Rights Reserved + * UNPUBLISHED, LICENSED SOFTWARE. + * + * CONFIDENTIAL AND PROPRIETARY INFORMATION + * WHICH IS THE PROPERTY OF your company. + * + * ======================================== +*/ + +#include "project.h" +#include + +#ifndef __IO__ +#define __IO__ + +#define SUCCESS (uint8)0 +#define FAIL (uint8)1 + +#define I2C_TIMEOUT (uint32)500 +#define DATA_ADDRESS (uint32)0x8 +#define JETTY (uint32)0xC +#define MICHAEL (uint8)0x1 +#define KEN (uint8)0x0 + +uint32 I2C_RW_Address(uint32 address, char read); + +uint32 convertValue(uint8 adc_value); + +void BatteryBalanceInit(void); + +uint8_t INA226Read(uint32 address, uint8* store); + +uint8_t JettySend(uint32 address, uint8* store); + +// void BatteryBalance(uint8* battery, uint8* value, float32* threshold); + +#endif // __IO__ + +/* [] END OF FILE */ diff --git a/battery management board 2025/Battery Management Board.cydsn/main.c b/battery management board 2025/Battery Management Board.cydsn/main.c new file mode 100644 index 0000000..04715b0 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/main.c @@ -0,0 +1,175 @@ +/* ======================================== + * + * Copyright YOUR COMPANY, THE YEAR + * All Rights Reserved + * UNPUBLISHED, LICENSED SOFTWARE. + * + * CONFIDENTIAL AND PROPRIETARY INFORMATION + * WHICH IS THE PROPERTY OF your company. + * + * ======================================== +*/ + +#include +#include +#include +#include +#include "main.h" +#include "cyapicallbacks.h" +#include "CAN_Stuff.h" +#include "FSM_Stuff.h" +#include "HindsightCAN/CANLibrary.h" +#include "io.h" + +#include + +// LED stuff +volatile uint8_t CAN_time_LED = 0; +volatile uint8_t ERROR_time_LED = 0; + +// UART stuff +char txData[TX_DATA_SIZE]; + +// CAN stuff +CANPacket can_recieve; +CANPacket can_send; +uint8 address = 0; + +CY_ISR(Period_Reset_Handler) { + CAN_time_LED++; + ERROR_time_LED++; + + if (ERROR_time_LED >= 3) { + LED_ERR_Write(OFF); + } + if (CAN_time_LED >= 3) { + LED_CAN_Write(OFF); + } +} + +CY_ISR(Button_1_Handler) { + LED_DBG_Write(!LED_DBG_Read()); +} + +int main(void) +{ + Initialize(); + int err; + uint8_t current; + uint8_t status; + for(;;) + { + err = 0; + switch(GetState()) { + case(UNINIT): + SetStateTo(CHECK_CAN); + break; + case(CHECK_CAN): + if (!PollAndReceiveCANPacket(&can_recieve)) { + LED_CAN_Write(ON); + CAN_time_LED = 0; + err = ProcessCAN(&can_recieve, &can_send); + } + if (GetMode() == KENMODE) + SetStateTo(DO_KENMODE); + else + SetStateTo(CHECK_CAN); + break; + case(DO_KENMODE): + // mode 1 tasks + status = getVoltage(¤t); + + if (status == SUCCESS) { + AssembleTelemetryReportPacket(&can_send, 0x2, 0x1, 0x1, current); + SendCANPacket(&can_send); + } else { + err = ERROR_INVALID_PACKET; + } + + SetStateTo(CHECK_CAN); + SetModeTo(MRBEASTMODE); + break; + default: + err = ERROR_INVALID_STATE; + SetStateTo(UNINIT); + break; + } + + if (err) DisplayErrorCode(err); + + if (DBG_UART_SpiUartGetRxBufferSize()) { + DebugPrint(DBG_UART_UartGetByte()); + } + + CyDelay(100); + } +} + +void Initialize(void) { + CyGlobalIntEnable; /* Enable global interrupts. LED arrays need this first */ + + address = getSerialAddress(); + + DBG_UART_Start(); + sprintf(txData, "Dip Addr: %x \r\n", address); + Print(txData); + + LED_DBG_Write(0); + + InitCAN(0x3, (int)address); + Timer_Period_Reset_Start(); + + isr_Button_1_StartEx(Button_1_Handler); + isr_Period_Reset_StartEx(Period_Reset_Handler); + + init_INA226(); +} + +void DebugPrint(char input) { + switch(input) { + case 'f': + sprintf(txData, "Mode: %x State:%x \r\n", GetMode(), GetState()); + break; + case 'x': + sprintf(txData, "bruh\r\n"); + break; + default: + sprintf(txData, "what\r\n"); + break; + } + Print(txData); +} + +int getSerialAddress() { + int address = 0; + + if (DIP1_Read()==0) address += 1; + if (DIP2_Read()==0) address += 2; + if (DIP3_Read()==0) address += 4; + if (DIP4_Read()==0) address += 8; + + if (address == 0) + address = DEVICE_SERIAL_TELEM_LOCALIZATION; + + return address; +} + +void DisplayErrorCode(uint8_t code) { + ERROR_time_LED = 0; + LED_ERR_Write(ON); + + sprintf(txData, "Error %X\r\n", code); + Print(txData); + + switch(code) + { + case ERROR_INVALID_TTC: + Print("Cannot Send That Data Type!\n\r"); + break; + default: + //some error + break; + } +} + +/* [] END OF FILE */ diff --git a/battery management board 2025/Battery Management Board.cydsn/main.h b/battery management board 2025/Battery Management Board.cydsn/main.h new file mode 100644 index 0000000..3729811 --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/main.h @@ -0,0 +1,33 @@ +/* ======================================== + * + * Copyright YOUR COMPANY, THE YEAR + * All Rights Reserved + * UNPUBLISHED, LICENSED SOFTWARE. + * + * CONFIDENTIAL AND PROPRIETARY INFORMATION + * WHICH IS THE PROPERTY OF your company. + * + * ======================================== +*/ +#pragma once + +#include "cyapicallbacks.h" +#include + +#define ON 1 +#define OFF 0 + +#define TX_DATA_SIZE (100u) + +#define Print(message) DBG_UART_UartPutString(message) +#define PrintChar(character) DBG_UART_UartPutChar(character) +#define PrintInt(integer) DBG_UART_UartPutString(itoa(integer, txData, 10)) +#define PrintIntBin(integer) DBG_UART_UartPutString(itoa(integer, txData, 2)) + +void Initialize(void); +int getSerialAddress(); +void DebugPrint(char input); +void DisplayErrorCode(uint8_t code); + + +/* [] END OF FILE */ diff --git a/battery management board 2025/Battery Management Board.cydsn/main_old.c b/battery management board 2025/Battery Management Board.cydsn/main_old.c new file mode 100644 index 0000000..afb939c --- /dev/null +++ b/battery management board 2025/Battery Management Board.cydsn/main_old.c @@ -0,0 +1,58 @@ +/* ======================================== + * + * Copyright YOUR COMPANY, THE YEAR + * All Rights Reserved + * UNPUBLISHED, LICENSED SOFTWARE. + * + * CONFIDENTIAL AND PROPRIETARY INFORMATION + * WHICH IS THE PROPERTY OF your company. + * + * ======================================== +*/ +#include "project.h" +#include "io.h" +#include +#include +#include "./HindsightCAN/CANCommon.h" +#include "./HindsightCAN/CANPacket.h" +#include "./HindsightCAN/Port.h" + +int main_old(void) +{ + CyGlobalIntEnable; /* Enable global interrupts. */ + + /* Place your initialization/startup code here (e.g. MyInst_Start()) */ + BatteryBalanceInit(); + init_INA226(); + DBG_UART_Start(); + InitCAN(0x3, 0x3); + + uint8_t current; + uint8_t status; + uint16_t* id = 0; + CANPacket packet; + CANPacket receivePacket; + + for(;;) + { + while (PollAndReceiveCANPacket(&receivePacket) > 2) {} + + + status = getVoltage(¤t); + + if (status == SUCCESS) { + // PrintInt(current); + // DBG_UART_UartPutString("mA\n\r"); + // DBG_UART_UartPutString("Constructing Packet\n\r"); + AssembleTelemetryReportPacket(&packet, 0x2, 0x1, 0x1, current); + // DBG_UART_UartPutString("Packet Constructed\n\r"); + SendCANPacket(&packet); + } else { + DBG_UART_UartPutString("FAIL"); + } + + CyDelay(100); + } +} + +/* [] END OF FILE */