diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index fbd8e81..e43cf2a 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -26,22 +26,17 @@ jobs: ref: ${{ github.event.pull_request.head.ref }} submodules: recursive - # Install the gcc-arm tools and clang-format, and ensure clang-format 12 is being used + # Install the gcc-arm tools and clang-format, and ensure clang-format 15 is being used - name: Install Compiler and Linter run: | sudo apt-get install gcc-arm-none-eabi - sudo apt-get install clang-format-12 - sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-12 10000 + sudo apt-get install clang-format-15 + sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-15 10000 # Build the code for all supported chips - - name: F302 Build + - name: Project Build run: | - cmake -DTARGET_DEV=STM32F302x8 -B ${{github.workspace}}/build - cmake --build ${{github.workspace}}/build - - - name: F334 Build - run: | - cmake -DTARGET_DEV=STM32F334x8 -B ${{github.workspace}}/build + cmake -B ${{github.workspace}}/build cmake --build ${{github.workspace}}/build # Apply clang-format formatting to the branch and create a new commit if any files are changed diff --git a/CMakeLists.txt b/CMakeLists.txt index c9e9602..5f1c48f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,9 +11,9 @@ if(EVT_CORE_LOG_ENABLE) endif() # Handle selection of the target device -option(TARGET_DEV "Target device" "STM32F334x8") +option(TARGET_DEV "Target device" "STM32F446xx") if(NOT TARGET_DEV) - set(TARGET_DEV "STM32F334x8") + set(TARGET_DEV "STM32F446xx") endif() if(TARGET_DEV STREQUAL "STM32F302x8") @@ -22,6 +22,9 @@ if(TARGET_DEV STREQUAL "STM32F302x8") elseif(TARGET_DEV STREQUAL "STM32F334x8") add_compile_definitions(STM32F334x8) add_compile_definitions(STM32F3xx) +elseif(TARGET_DEV STREQUAL "STM32F446xx") + add_compile_definitions(STM32F446xx) + add_compile_definitions(STM32F4xx) else() message(FATAL_ERROR "The target device is not supported") endif() @@ -57,12 +60,17 @@ project(${BOARD_LIB_NAME} LANGUAGES CXX C ) -add_library(${PROJECT_NAME} STATIC) +add_library(${PROJECT_NAME} STATIC + include/dev/ADS8689IPWR.hpp + include/dev/RedundantADC.hpp + include/HIB.hpp +) # Add sources target_sources(${PROJECT_NAME} PRIVATE src/HIB.cpp src/dev/RedundantADC.cpp + src/dev/ADS8689IPWR.cpp ) ############################################################################### diff --git a/Changelog.md b/Changelog.md index e90a095..12d14f3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,3 +11,18 @@ Contains notable changes there were added, fixed, or removed in each release. * Build Board source as a library * Template to build arbitrary number of targets * Auto-generating documentation template + +## 0.2.0 + +### Changes + +* ADS8689IPWR driver created and implemented +* RedundantADC driver created and implemented +* HIB driver created and implemented +* Updated README.md +* Created targets for HIB, RedundantADC testing, and ADS88689IPWR testing +* Improved dependency order +* Verified ADC results +* Added error checking for ADCs +* Added logger statements for easier debugging +* Documentation added for each class as well as each target diff --git a/include/HIB.hpp b/include/HIB.hpp index 68eca95..dc49fbf 100644 --- a/include/HIB.hpp +++ b/include/HIB.hpp @@ -1,13 +1,105 @@ -#pragma once +#ifndef HIB_ +#define HIB_ + +#include "core/io/types/CANMessage.hpp" +#include +#include + +namespace io = core::io; namespace HIB { +/** + * Id of the message being sent from the HIB + */ +#define HIB_MESSAGE_ID 0xD0 + +/** + * Deadzone for the ADCs so they do not send a signal before the throttle is twisted + */ +#define VOLTAGE_DEADZONE 475 + +/** + * Start Switch + */ +#define START io::Pin::PB_1 + +/** + * Throttle Switch + */ +#define THROTTLE_SWITCH io::Pin::PB_2 /** - * HIB header file + * Forward Enable + */ +#define FORWARD_ENABLE_0 io::Pin::PB_4 +#define FORWARD_ENABLE_1 io::Pin::PB_5 +#define FORWARD_ENABLE_2 io::Pin::PB_6 + +constexpr size_t payloadLength = 6; + +/** + * The Handlebar Interface Board Takes in a 0.0 to 12.0 volt signal from the throttle and brake + * which it then converts to a 16 bit value through its "double-triple" ADC setup. It then sends + * this voltage value along with any error codes that were generated to the Vehicle Control Unit + * (VCU). + * + * It reads in the voltage from either the throttle or the brake through one set of three ADCs + * that communicate with the microcontroller using SPI. After the controller sets up the devices + * they are read in and then averaged and contrasted with the average to find any margin errors. + * + * The data after being correctly parsed and checked for errors is then sent through CAN to the + * VCU for decision-making. */ class HIB { public: + HIB(RedundantADC& throttle, RedundantADC& brake); + + /** + * Reads the voltages and errors from the redundant ADCs and returns a can message containing + * the data from each read + * @return The can message that should be transmitted + */ + io::CANMessage process(); + + /** + * Payload for CAN transmission. + * Byte 0: MSB for Throttle Voltage + * Byte 1: LSB for Throttle Voltage + * Byte 2: MSB for Brake Voltage + * Byte 3: LSB for Brake Voltage + * Byte 4: Error Byte for Throttle + * Byte 5: Error Byte for Brake + * Error Byte Layout: 0: No Error, 1: Precision Error, 2: Margin Error, 3: Comparison Error + */ + struct { + uint8_t throttleVoltageMSB = 0; + uint8_t throttleVoltageLSB = 0; + uint8_t brakeVoltageMSB = 0; + uint8_t brakeVoltageLSB = 0; + uint8_t throttleError = 0; + uint8_t brakeError = 0; + } hibPayload; + private: -}; + void readThrottleVoltage(); + + void readBrakeVoltage(); + + RedundantADC& throttle; + RedundantADC& brake; -}// namespace HIB \ No newline at end of file + uint16_t throttleVoltage = 0; + uint16_t brakeVoltage = 0; + + // Counters for throttle errors + uint32_t acceptableThrottleMarginErrors = 0; + uint32_t precisionThrottleMarginErrors = 0; + uint32_t comparisonThrottleErrors = 0; + + // Counters for brake errors + uint32_t acceptableBrakeMarginErrors = 0; + uint32_t precisionBrakeMarginErrors = 0; + uint32_t comparisonBrakeErrors = 0; +}; +}// namespace HIB +#endif \ No newline at end of file diff --git a/include/dev/ADS8689IPWR.hpp b/include/dev/ADS8689IPWR.hpp new file mode 100644 index 0000000..1ad8648 --- /dev/null +++ b/include/dev/ADS8689IPWR.hpp @@ -0,0 +1,106 @@ +#ifndef ADS8689IPWR_ +#define ADS8689IPWR_ + +#include + +/** + * Read/Write Range selection register for changing the range of the ADC to 0V-12V + */ +#define RANGE_SEL_REG 0x14 + +/** + * The 2 byte value used to change the range selection register from 3V3 to 12V0 when writing to it + */ +#define TWELVE_VOLT_SCALER 0b1000 + +/** + * Used for writing to the range selection register to alter the range read in by the ADC + */ +#define HALF_WORD_WRITE 0b11010000 + +/** + * Empty byte defined for use in the No-Operation command + */ +#define EMPTY_BYTE 0b00000000 + +/** + * Defined No-Operation command + */ +#define NOP \ + { EMPTY_BYTE, EMPTY_BYTE, EMPTY_BYTE, EMPTY_BYTE } + +/** + * The maximum voltage that can be read in by the ADC | Used for transforming the given 2 bytes + * from the ADC into a usable value that will be sent to the Vehicle Control Unit + */ +#define VOLTAGE_MAX 12288 + +/** + * EVT info logger macro + * @param text The formatted text that should be logged out + */ +#define LOG_INFO(text, ...) core::log::LOGGER.log(core::log::Logger::LogLevel::INFO, text, ##__VA_ARGS__) + +/** + * Brake Chip Select Pins + */ +#define BRAKE_0 io::Pin::PB_9 +#define BRAKE_1 io::Pin::PB_8 +#define BRAKE_2 io::Pin::PB_7 + +/** + * Throttle Chip Select Pins + * */ +#define THROTTLE_0 io::Pin::PC_9 +#define THROTTLE_1 io::Pin::PC_8 +#define THROTTLE_2 io::Pin::PC_7 + +/** + * The exact speed that the ADCs need to run at + */ +#define SPI_SPEED SPI_SPEED_500KHZ + +/** + * The mode that spi should run in + */ +#define SPI_MODE io::SPI::SPIMode::SPI_MODE0 + +#define BRAKE_SPI_SCK io::Pin::PC_10 +#define BRAKE_SPI_MOSI io::Pin::PC_12 +#define BRAKE_SPI_MISO io::Pin::PC_11 + +#define THROTTLE_SPI_SCK io::Pin::PB_10 +#define THROTTLE_SPI_MOSI io::Pin::PB_15 +#define THROTTLE_SPI_MISO io::Pin::PB_14 + +namespace io = core::io; + +namespace HIB { +/** + * Device driver for the ADS8689IPWR 16-bit ADC. Contains a constructor that runs the necessary + * setup for the device and a read function that reads in the voltage from the ADC and transforms + * it into a useful number (the actual numerical millivoltage) + */ +class ADS8689IPWR { +public: + /** + * Creates a ADS8689IPWR object while configuring the range selector. + * + * @param spi SPI reference to interface with the ADC + * @param deviceNumber The number of the cs pin that the device is + */ + ADS8689IPWR(io::SPI& spi, uint8_t deviceNumber); + + /** + * Reads out the voltage from the ADC with a blank command through SPI + * + * @return the status of whether the transaction was successful + */ + uint16_t readVoltage() const; + +private: + io::SPI& spi; + const uint8_t deviceNumber; +}; +}// namespace HIB +#endif diff --git a/include/dev/RedundantADC.hpp b/include/dev/RedundantADC.hpp index 72e42db..c8bb71e 100644 --- a/include/dev/RedundantADC.hpp +++ b/include/dev/RedundantADC.hpp @@ -1,15 +1,30 @@ -#pragma once +#ifndef REDUNDANT_ADC_ +#define REDUNDANT_ADC_ -#include +#include -namespace IO = EVT::core::IO; - -namespace HIB::DEV { +/** + * Defined limits for the amount of times an error can occur before a restart is required + */ +#define COMPARISON_ERROR_COUNT 1 +#define PRECISION_MARGIN_ERROR_COUNT 3 +#define ACCEPTABLE_MARGIN_ERROR_COUNT 5 /** - * This class allows processing readings from redundant ADCs and checking for errors + * CAN TX and RX pins */ +#define CAN_TX io::Pin::PA_12 +#define CAN_RX io::Pin::PA_11 +namespace io = core::io; + +namespace HIB { +/** + * An object that takes in 3 different ADS8689IPWR objects to read out and compare their output + * voltages. It compares and contrasts these voltages against their average to find the marginal + * error for each of them. If the margin error is a little high, too high, or completely off in + * one or more of the devices then it will return a corresponding error the HIB object. + */ class RedundantADC { public: /** @@ -33,7 +48,7 @@ class RedundantADC { * @param[in] adc1 The second ADC instance. * @param[in] adc2 The third ADC instance. */ - RedundantADC(IO::ADC& adc0, IO::ADC& adc1, IO::ADC& adc2); + RedundantADC(ADS8689IPWR& adc0, ADS8689IPWR& adc1, ADS8689IPWR& adc2); /** * Read voltage readings from the ADCs and check for redundancy. @@ -41,19 +56,15 @@ class RedundantADC { * This function reads values from three ADCs and checks for redundancy. * * @param[out] return_val Reference to the variable to store the value read from the ADCs - * @param[in] val2 Reference to the variable to store the value read from the second ADC. - * @param[in] val3 Reference to the variable to store the value read from the third ADC. * @return RedundantADC::Status The status of the processing. */ - RedundantADC::Status readVoltage(uint32_t& return_val); + Status read(uint16_t& return_val) const; private: - /** Reference to the first ADC. */ - IO::ADC& adc0; - /** Reference to the second ADC. */ - IO::ADC& adc1; - /** Reference to the third ADC. */ - IO::ADC& adc2; + ADS8689IPWR& adc0; + ADS8689IPWR& adc1; + ADS8689IPWR& adc2; }; -}// namespace HIB::DEV +}// namespace HIB +#endif \ No newline at end of file diff --git a/libs/EVT-core b/libs/EVT-core index b3321e9..2b03616 160000 --- a/libs/EVT-core +++ b/libs/EVT-core @@ -1 +1 @@ -Subproject commit b3321e978e7a7dd057f932aa252653b7002bcb7a +Subproject commit 2b036169c6ca442970b0e3965e74f44927d1b581 diff --git a/src/HIB.cpp b/src/HIB.cpp index bd5129d..5422620 100644 --- a/src/HIB.cpp +++ b/src/HIB.cpp @@ -1,5 +1,106 @@ +#include "core/utils/log.hpp" #include +#include + +namespace io = core::io; namespace HIB { +HIB::HIB(RedundantADC& throttle, RedundantADC& brake) + : throttle(throttle), brake(brake) {} + +// Reads the 2 sets of 3 ADCs and processes their errors while packaging them for CAN +io::CANMessage HIB::process() { + // Read in the voltages from the throttle + readThrottleVoltage(); + readBrakeVoltage();// Brake is currently not implemented, but read the ADCs anyways + + if (throttleVoltage < VOLTAGE_DEADZONE) { + throttleVoltage = 0; + } + + if (brakeVoltage < VOLTAGE_DEADZONE) { + brakeVoltage = 0; + } + + hibPayload.throttleVoltageMSB = static_cast(throttleVoltage >> 8 & 0xFF); + hibPayload.throttleVoltageLSB = static_cast(throttleVoltage & 0xFF); + hibPayload.brakeVoltageMSB = static_cast(brakeVoltage >> 8 & 0xFF); + hibPayload.brakeVoltageLSB = static_cast(brakeVoltage & 0xFF); + + if (acceptableThrottleMarginErrors > ACCEPTABLE_MARGIN_ERROR_COUNT) { + acceptableThrottleMarginErrors = ACCEPTABLE_MARGIN_ERROR_COUNT + 1; + hibPayload.throttleError = 1; + } + + if (acceptableBrakeMarginErrors > ACCEPTABLE_MARGIN_ERROR_COUNT) { + acceptableBrakeMarginErrors = ACCEPTABLE_MARGIN_ERROR_COUNT + 1; + hibPayload.brakeError = 1; + } + + if (precisionThrottleMarginErrors > PRECISION_MARGIN_ERROR_COUNT) { + precisionThrottleMarginErrors = PRECISION_MARGIN_ERROR_COUNT + 1; + hibPayload.throttleError = 2; + } + + if (precisionBrakeMarginErrors > PRECISION_MARGIN_ERROR_COUNT) { + precisionBrakeMarginErrors = PRECISION_MARGIN_ERROR_COUNT + 1; + hibPayload.brakeError = 2; + } + + if (comparisonThrottleErrors > COMPARISON_ERROR_COUNT) { + comparisonThrottleErrors = COMPARISON_ERROR_COUNT + 1; + hibPayload.throttleError = 3; + } + + if (comparisonBrakeErrors > COMPARISON_ERROR_COUNT) { + comparisonBrakeErrors = COMPARISON_ERROR_COUNT + 1; + hibPayload.throttleError = 3; + } + + uint8_t payload[payloadLength]; + payload[0] = hibPayload.throttleVoltageMSB; + payload[1] = hibPayload.throttleVoltageLSB; + payload[2] = hibPayload.brakeVoltageMSB; + payload[3] = hibPayload.brakeVoltageLSB; + payload[4] = hibPayload.throttleError; + payload[5] = hibPayload.brakeError; + + return io::CANMessage{HIB_MESSAGE_ID, payloadLength, payload, false}; +} + +void HIB::readThrottleVoltage() { + const RedundantADC::Status status = throttle.read(throttleVoltage);// gets the errors and voltage from the ADC cluster + + // Increment the status of each error if it is received + if (status == RedundantADC::Status::ACCEPTABLE_MARGIN_EXCEEDED) { + acceptableThrottleMarginErrors++; + } + + if (status == RedundantADC::Status::PRECISION_MARGIN_EXCEEDED) { + precisionThrottleMarginErrors++; + } + + if (status == RedundantADC::Status::COMPARISON_ERROR) { + comparisonThrottleErrors++; + } +} + +void HIB::readBrakeVoltage() { + const RedundantADC::Status status = brake.read(brakeVoltage);// gets the errors and voltage from the ADC cluster + + // Increment the status of each error if it is received + if (status == RedundantADC::Status::ACCEPTABLE_MARGIN_EXCEEDED) { + acceptableBrakeMarginErrors++; + } + + if (status == RedundantADC::Status::PRECISION_MARGIN_EXCEEDED) { + precisionBrakeMarginErrors++; + } + + if (status == RedundantADC::Status::COMPARISON_ERROR) { + comparisonBrakeErrors++; + } +} + }// namespace HIB \ No newline at end of file diff --git a/src/dev/ADS8689IPWR.cpp b/src/dev/ADS8689IPWR.cpp new file mode 100644 index 0000000..ce599bd --- /dev/null +++ b/src/dev/ADS8689IPWR.cpp @@ -0,0 +1,30 @@ +#include +#include + +namespace HIB { +ADS8689IPWR::ADS8689IPWR(io::SPI& spi, const uint8_t deviceNumber) : spi(spi), deviceNumber(deviceNumber) { + uint8_t message[4] = {HALF_WORD_WRITE, RANGE_SEL_REG, EMPTY_BYTE, TWELVE_VOLT_SCALER}; + spi.startTransmission(deviceNumber); + spi.write(message, 4); + spi.endTransmission(deviceNumber); + + // Begin the conversion with a NOP command + uint8_t nop[4] = NOP; + spi.startTransmission(deviceNumber); + spi.write(nop, 4); + spi.endTransmission(deviceNumber); +} + +uint16_t ADS8689IPWR::readVoltage() const { + uint8_t bytes[4] = {0}; + spi.startTransmission(deviceNumber); + spi.read(bytes, 2); + spi.endTransmission(deviceNumber); + + const uint16_t raw = (static_cast(bytes[0]) << 8) | static_cast(bytes[1]); + + const uint32_t scaled = static_cast(raw) * VOLTAGE_MAX / UINT16_MAX; + + return static_cast(scaled); +} +}// namespace HIB diff --git a/src/dev/RedundantADC.cpp b/src/dev/RedundantADC.cpp index 797d5e5..03a18c6 100644 --- a/src/dev/RedundantADC.cpp +++ b/src/dev/RedundantADC.cpp @@ -1,37 +1,49 @@ -#include #include -#include +#include -namespace IO = EVT::core::IO; +#include +#include -constexpr uint32_t LOW_MARGIN = 1; -constexpr uint32_t HIGH_MARGIN = 5; +namespace io = core::io; +using namespace HIB; -namespace HIB::DEV { +/* Percentage differences + * Values are approximately 1% and 5% of 12288 Mv, but "approximately" is a stretch. + * These can be altered as needed in the future after stress testing the ADCs and finding out + * how they perform while running on the bike + */ +constexpr uint16_t LOW_MARGIN = 400; +constexpr uint16_t HIGH_MARGIN = 800; -RedundantADC::RedundantADC(IO::ADC& adc0, IO::ADC& adc1, IO::ADC& adc2) : adc0(adc0), adc1(adc1), adc2(adc2) {} +RedundantADC::RedundantADC(ADS8689IPWR& adc0, ADS8689IPWR& adc1, ADS8689IPWR& adc2) + : adc0(adc0), adc1(adc1), adc2(adc2) {} -RedundantADC::Status RedundantADC::readVoltage(uint32_t& return_val) { - // Read ADC values - int32_t adcValues[3]; - adcValues[0] = static_cast(adc0.read() * 1000); - adcValues[1] = static_cast(adc1.read() * 1000); - adcValues[2] = static_cast(adc2.read() * 1000); +RedundantADC::Status RedundantADC::read(uint16_t& return_val) const { + // Read in the millivoltage of each ADC + int16_t adcValues[3] = {0}; + adcValues[0] = static_cast(adc0.readVoltage()); + adcValues[1] = static_cast(adc1.readVoltage()); + adcValues[2] = static_cast(adc2.readVoltage()); - // Calculate average of all ADC values - int32_t average = (adcValues[0] + adcValues[1] + adcValues[2]) / 3; + // Calculate average of all ADC millivoltages + const auto average = static_cast((adcValues[0] + adcValues[1] + adcValues[2]) / 3); - // Check for margin error - bool adc0underLow = (std::abs(adcValues[0] - average) * 100 / average) < LOW_MARGIN; - bool adc1underLow = (std::abs(adcValues[1] - average) * 100 / average) < LOW_MARGIN; - bool adc2underLow = (std::abs(adcValues[2] - average) * 100 / average) < LOW_MARGIN; + // Check for deviation errors + const bool adc0underLow = static_cast(std::abs(adcValues[0] - average)) < LOW_MARGIN; + const bool adc1underLow = static_cast(std::abs(adcValues[1] - average)) < LOW_MARGIN; + const bool adc2underLow = static_cast(std::abs(adcValues[2] - average)) < LOW_MARGIN; - bool adc0underHigh = (std::abs(adcValues[0] - average) * 100 / average) < HIGH_MARGIN; - bool adc1underHigh = (std::abs(adcValues[1] - average) * 100 / average) < HIGH_MARGIN; - bool adc2underHigh = (std::abs(adcValues[2] - average) * 100 / average) < HIGH_MARGIN; + const bool adc0underHigh = static_cast(std::abs(adcValues[0] - average)) < HIGH_MARGIN; + const bool adc1underHigh = static_cast(std::abs(adcValues[1] - average)) < HIGH_MARGIN; + const bool adc2underHigh = static_cast(std::abs(adcValues[2] - average)) < HIGH_MARGIN; // Check for redundancy - bool allUnderLow = adc0underLow && adc1underLow && adc2underLow; + const bool allUnderLow = adc0underLow && adc1underLow && adc2underLow; + + if (average == 0) { + return_val = average; + return RedundantADC::Status::OK; + } if (allUnderLow) { return_val = average; @@ -63,7 +75,6 @@ RedundantADC::Status RedundantADC::readVoltage(uint32_t& return_val) { } return_val = 0; + return RedundantADC::Status::COMPARISON_ERROR; } - -}// namespace HIB::DEV diff --git a/targets/ADS8689IPWR/CMakeLists.txt b/targets/ADS8689IPWR/CMakeLists.txt new file mode 100644 index 0000000..4ee2133 --- /dev/null +++ b/targets/ADS8689IPWR/CMakeLists.txt @@ -0,0 +1,7 @@ +include(${EVT_CORE_DIR}/cmake/evt-core_build.cmake) + +project(ADS8689IPWR) +cmake_minimum_required(VERSION 3.15) + +make_exe(${PROJECT_NAME} main.cpp) +target_link_libraries(${PROJECT_NAME} PUBLIC ${BOARD_LIB_NAME}) diff --git a/targets/ADS8689IPWR/main.cpp b/targets/ADS8689IPWR/main.cpp new file mode 100644 index 0000000..6ba27d5 --- /dev/null +++ b/targets/ADS8689IPWR/main.cpp @@ -0,0 +1,72 @@ +/** + * Test target for the ASD8689IPWR Analog to Digital converter. Use this if you just need to + * verify that they are properly taking in and reading out an expected voltage + */ +#include +#include +#include +#include +#include +#include +#include + +namespace io = core::io; +namespace time = core::time; + +constexpr uint8_t deviceCount = 3; + +io::GPIO* throttleDevices[deviceCount]; +io::GPIO* brakeDevices[deviceCount]; + +int main() { + // Initialize system + core::platform::init(); + + // Initialize UART and logger + io::UART& uart = io::getUART(9600); + core::log::LOGGER.setUART(&uart); + core::log::LOGGER.setLogLevel(core::log::Logger::LogLevel::INFO); + + // Set up each chip select pin + brakeDevices[0] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + brakeDevices[0]->writePin(io::GPIO::State::HIGH); + brakeDevices[1] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + brakeDevices[1]->writePin(io::GPIO::State::HIGH); + brakeDevices[2] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + brakeDevices[2]->writePin(io::GPIO::State::HIGH); + + throttleDevices[0] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + throttleDevices[0]->writePin(io::GPIO::State::HIGH); + throttleDevices[1] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + throttleDevices[1]->writePin(io::GPIO::State::HIGH); + throttleDevices[2] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + throttleDevices[2]->writePin(io::GPIO::State::HIGH); + + // Create the two SPI buses and configure them + io::SPI& spiThrottle = io::getSPI(throttleDevices, deviceCount); + spiThrottle.configureSPI(SPI_SPEED, SPI_MODE, SPI_MSB_FIRST); + io::SPI& spiBrake = io::getSPI(brakeDevices, deviceCount); + spiBrake.configureSPI(SPI_SPEED, SPI_MODE, SPI_MSB_FIRST); + + // Create all 6 ADS8689IPWR objects + auto throttleAdc1 = HIB::ADS8689IPWR(spiThrottle, 0); + auto throttleAdc2 = HIB::ADS8689IPWR(spiThrottle, 1); + auto throttleAdc3 = HIB::ADS8689IPWR(spiThrottle, 2); + auto brakeAdc1 = HIB::ADS8689IPWR(spiBrake, 0); + auto brakeAdc2 = HIB::ADS8689IPWR(spiBrake, 1); + auto brakeAdc3 = HIB::ADS8689IPWR(spiBrake, 2); + + // Test each adc one by one on loop + while (true) { + LOG_INFO("Throttle ADC1: %dV\r\n", throttleAdc1.readVoltage()); + LOG_INFO("Throttle ADC2: %dV\r\n", throttleAdc2.readVoltage()); + LOG_INFO("Throttle ADC3: %dV\r\n", throttleAdc3.readVoltage()); + LOG_INFO("Brake ADC1: %dV\r\n", brakeAdc1.readVoltage()); + LOG_INFO("Brake ADC2: %dV\r\n", brakeAdc2.readVoltage()); + LOG_INFO("Brake ADC3: %dV\r\n\r\n", brakeAdc3.readVoltage()); + + time::wait(1000); + } + + return 0; +} \ No newline at end of file diff --git a/targets/CMakeLists.txt b/targets/CMakeLists.txt index b84c011..70fd474 100644 --- a/targets/CMakeLists.txt +++ b/targets/CMakeLists.txt @@ -1,3 +1,4 @@ # Add all targets add_subdirectory(REV3-HIB) -add_subdirectory(RedundantADC) +add_subdirectory(ADS8689IPWR) +add_subdirectory(RedundantADC) \ No newline at end of file diff --git a/targets/REV3-HIB/main.cpp b/targets/REV3-HIB/main.cpp index b30c56b..16ff026 100644 --- a/targets/REV3-HIB/main.cpp +++ b/targets/REV3-HIB/main.cpp @@ -1,29 +1,128 @@ /** - * This is a basic sample of using the UART module. The program provides a - * basic echo functionality where the uart will write back whatever the user - * enters. + * REV3-HIB main target. Runs the setup for the SPI, UART, and CAN buses. Creates the ADS8689IPWR, + * RedundantADC, and HIB objects and then runs the hib.process(). Optionally logs out the data stream + * from the HIB if the logger is enabled. + * + * The goal of this device is to read in voltage data from the throttle and the brake through a pair + * of three redundant 16 bit ADCs. These readings are then compared to each other to find the + * average voltage and any substantial errors or offset in the readings of each device. If the + * errors are too great, then the HIB will send an error signal to the VCU, otherwise it just + * data pertaining to the voltage of the break and the throttle */ +#include +#include +#include +#include +#include +#include +#include +#include -#include -#include -#include +namespace io = core::io; +namespace time = core::time; -namespace IO = EVT::core::IO; +constexpr uint8_t deviceCount = 3; + +io::GPIO* throttleDevices[deviceCount]; +io::GPIO* brakeDevices[deviceCount]; int main() { // Initialize system - EVT::core::platform::init(); + core::platform::init(); // Setup UART - IO::UART& uart = IO::getUART(9600); + io::UART& uart = io::getUART(9600); + core::log::LOGGER.setUART(&uart); + core::log::LOGGER.setLogLevel(core::log::Logger::LogLevel::INFO); + + // // Setup GPIO pins for the throttle switch, start, and forward enable + // auto throttleSwitch = &io::getGPIO(io::GPIO::Direction::INPUT); + // auto start = &io::getGPIO(io::GPIO::Direction::INPUT); + // auto forwardEnable0 = &io::getGPIO(io::GPIO::Direction::INPUT); + // auto forwardEnable1 = &io::getGPIO(io::GPIO::Direction::INPUT); + // auto forwardEnable2 = &io::getGPIO(io::GPIO::Direction::INPUT); + + // Set up each chip select pink + brakeDevices[0] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + brakeDevices[0]->writePin(io::GPIO::State::HIGH); + brakeDevices[1] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + brakeDevices[1]->writePin(io::GPIO::State::HIGH); + brakeDevices[2] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + brakeDevices[2]->writePin(io::GPIO::State::HIGH); + + throttleDevices[0] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + throttleDevices[0]->writePin(io::GPIO::State::HIGH); + throttleDevices[1] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + throttleDevices[1]->writePin(io::GPIO::State::HIGH); + throttleDevices[2] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + throttleDevices[2]->writePin(io::GPIO::State::HIGH); + + // Create the two SPI buses + io::SPI& spiThrottle = io::getSPI(throttleDevices, deviceCount); + spiThrottle.configureSPI(SPI_SPEED, SPI_MODE, SPI_MSB_FIRST); + io::SPI& spiBrake = io::getSPI(brakeDevices, deviceCount); + spiBrake.configureSPI(SPI_SPEED, SPI_MODE, SPI_MSB_FIRST); + + // Create all 6 ADS8689IPWR objects + auto throttleAdc1 = HIB::ADS8689IPWR(spiThrottle, 0); + auto throttleAdc2 = HIB::ADS8689IPWR(spiThrottle, 1); + auto throttleAdc3 = HIB::ADS8689IPWR(spiThrottle, 2); + auto brakeAdc1 = HIB::ADS8689IPWR(spiBrake, 0); + auto brakeAdc2 = HIB::ADS8689IPWR(spiBrake, 1); + auto brakeAdc3 = HIB::ADS8689IPWR(spiBrake, 2); + + // Initialize the 2 redundant ADCs + auto throttle = HIB::RedundantADC(throttleAdc1, throttleAdc2, throttleAdc3); + auto brake = HIB::RedundantADC(brakeAdc1, brakeAdc2, brakeAdc3); + + // Initialize the HIB object to begin processing data + auto hib = HIB::HIB(throttle, brake); - // String to store user input - char buf[100]; + // Initialize CAN + io::CAN& can = io::getCAN(true); - while (1) { - // Read user input - uart.printf("Enter message: "); - uart.gets(buf, 100); - uart.printf("\n\recho: %s\n\r", buf); + // Try to join the network + io::CAN::CANStatus result = can.connect(); + if (result != io::CAN::CANStatus::OK) { + LOG_INFO("Failed to connect to the CAN network.\r\n"); } -} + + // ID for HIB is 0x0D0 + io::CANMessage transmit_message(0x0D0, 0, {}, false); + + // Try to send the message + result = can.transmit(transmit_message); + if (result != io::CAN::CANStatus::OK) { + LOG_INFO("Failed to transmit message\r\n"); + } else { + LOG_INFO("Transmitted message.\r\n"); + } + + // Read voltage and errors and send them through the CAN bus + while (true) { + // Process the voltage + transmit_message = hib.process(); + + // Try to send the message + result = can.transmit(transmit_message); + if (result != io::CAN::CANStatus::OK) { + LOG_INFO("Failed to transmit message\r\n"); + } + + // Uncomment to print results through UART when a P-CAN dongle is unavailable + // io::CANMessage message; + // auto status = can.receive(&message); + // uint8_t* payload = message.getPayload(); + // LOG_INFO("Throttle Voltage: %imV\r\n" + // "Brake Voltage: %imV\r\n" + // "Throttle Error Information: %x\r\n", + // "Brake Error Information: %x\r\n", + // (payload[0] << 8) + payload[1], + // (payload[2] << 8) + payload[3], + // payload[4], + // payload[5]); + // time::wait(5000); + } + + return 0; +} \ No newline at end of file diff --git a/targets/RedundantADC/main.cpp b/targets/RedundantADC/main.cpp index 7d04d04..dc697c8 100644 --- a/targets/RedundantADC/main.cpp +++ b/targets/RedundantADC/main.cpp @@ -1,43 +1,95 @@ -#include -#include -#include -#include +/** + * RedundantADC test target. Run this on the board to test the functionality of the RedundantADC + * class and the physical setup of the two redundant adc setups on thd board + */ +#include +#include +#include +#include +#include #include -namespace IO = EVT::core::IO; -namespace DEV = EVT::core::DEV; +namespace io = core::io; +namespace time = core::time; + +constexpr uint8_t deviceCount = 3; + +io::GPIO* throttleDevices[deviceCount]; +io::GPIO* brakeDevices[deviceCount]; int main() { // Initialize system - EVT::core::platform::init(); + core::platform::init(); + + // Initialize UART and logger + io::UART& uart = io::getUART(9600); + core::log::LOGGER.setUART(&uart); + core::log::LOGGER.setLogLevel(core::log::Logger::LogLevel::INFO); + + // Set up each chip select pin + brakeDevices[0] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + brakeDevices[0]->writePin(io::GPIO::State::HIGH); + brakeDevices[1] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + brakeDevices[1]->writePin(io::GPIO::State::HIGH); + brakeDevices[2] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + brakeDevices[2]->writePin(io::GPIO::State::HIGH); + + throttleDevices[0] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + throttleDevices[0]->writePin(io::GPIO::State::HIGH); + throttleDevices[1] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + throttleDevices[1]->writePin(io::GPIO::State::HIGH); + throttleDevices[2] = &io::getGPIO(io::GPIO::Direction::OUTPUT); + throttleDevices[2]->writePin(io::GPIO::State::HIGH); - // Setup UART - IO::UART& uart = IO::getUART(9600); - uart.printf("UART initialized\r\n"); + // Create the two SPI buses and configure them + io::SPI& spiThrottle = io::getSPI(throttleDevices, deviceCount); + spiThrottle.configureSPI(SPI_SPEED, SPI_MODE, SPI_MSB_FIRST); + io::SPI& spiBrake = io::getSPI(brakeDevices, deviceCount); + spiBrake.configureSPI(SPI_SPEED, SPI_MODE, SPI_MSB_FIRST); - IO::ADC& adc0 = IO::getADC(); - IO::ADC& adc1 = IO::getADC(); - IO::ADC& adc2 = IO::getADC(); + // Create all 6 ADS8689IPWR objects + auto throttleAdc1 = HIB::ADS8689IPWR(spiThrottle, 0); + auto throttleAdc2 = HIB::ADS8689IPWR(spiThrottle, 1); + auto throttleAdc3 = HIB::ADS8689IPWR(spiThrottle, 2); + auto brakeAdc1 = HIB::ADS8689IPWR(spiBrake, 0); + auto brakeAdc2 = HIB::ADS8689IPWR(spiBrake, 1); + auto brakeAdc3 = HIB::ADS8689IPWR(spiBrake, 2); - // Create RedundantADC object - HIB::DEV::RedundantADC redundantADC(adc0, adc1, adc2); + // Initialize the two redundant ADCs + auto throttle = HIB::RedundantADC(throttleAdc1, throttleAdc2, throttleAdc3); + auto brake = HIB::RedundantADC(brakeAdc1, brakeAdc2, brakeAdc3); - // Variables to store ADC values - uint32_t return_val; + // Declare voltage variables + uint16_t throttleVoltage = 0; + uint16_t brakeVoltage = 0; - while (1) { + while (true) { // Process ADC values - HIB::DEV::RedundantADC::Status status = redundantADC.readVoltage(return_val); - - //check ADC Statuses0 - if (status == HIB::DEV::RedundantADC::Status::OK) { - uart.printf("Average Voltage Reading %d\r\n", return_val); - } else if (status == HIB::DEV::RedundantADC::Status::PRECISION_MARGIN_EXCEEDED) { - uart.printf("One error detected\r\n"); - } else if (status == HIB::DEV::RedundantADC::Status::ACCEPTABLE_MARGIN_EXCEEDED) { - uart.printf("Margin error detected\r\n"); - } else if (status == HIB::DEV::RedundantADC::Status::COMPARISON_ERROR) { - uart.printf("Comparison error detected\r\n"); + HIB::RedundantADC::Status throttleStatus = throttle.read(throttleVoltage); + HIB::RedundantADC::Status brakeStatus = brake.read(brakeVoltage); + + //check ADC Statuses + if (throttleStatus == HIB::RedundantADC::Status::OK) { + LOG_INFO("Throttle Average Voltage Reading: %dV\r\n", throttleVoltage); + } else if (throttleStatus == HIB::RedundantADC::Status::PRECISION_MARGIN_EXCEEDED) { + LOG_INFO("Throttle Precision error detected\r\n"); + } else if (throttleStatus == HIB::RedundantADC::Status::ACCEPTABLE_MARGIN_EXCEEDED) { + LOG_INFO("Throttle Margin error detected\r\n"); + } else if (throttleStatus == HIB::RedundantADC::Status::COMPARISON_ERROR) { + LOG_INFO("Throttle Comparison error detected\r\n"); + } + + //check ADC Statuses + if (brakeStatus == HIB::RedundantADC::Status::OK) { + LOG_INFO("Brake Average Voltage Reading: %dV\r\n", brakeVoltage); + } else if (brakeStatus == HIB::RedundantADC::Status::PRECISION_MARGIN_EXCEEDED) { + LOG_INFO("Brake Precision error detected\r\n"); + } else if (brakeStatus == HIB::RedundantADC::Status::ACCEPTABLE_MARGIN_EXCEEDED) { + LOG_INFO("Brake Margin error detected\r\n"); + } else if (brakeStatus == HIB::RedundantADC::Status::COMPARISON_ERROR) { + LOG_INFO("Brake Comparison error detected\r\n\r\n"); } + + time::wait(1000); } }