From 193211b772d538539912cefd34c9f6d83c60cdac Mon Sep 17 00:00:00 2001 From: ZodiusInfuser Date: Wed, 2 Aug 2023 00:14:58 +0100 Subject: [PATCH] Initial detection of modules --- CMakeLists.txt | 1 + examples/yukon/yukon_simple_enable.cpp | 13 +++ libraries/yukon/yukon.cpp | 114 ++++++++++++++++++++----- libraries/yukon/yukon.hpp | 112 ++++++++++++++++++++++-- 4 files changed, 214 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ea86f26..f20eb6ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ project(pico_examples C CXX ASM) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) set(PICO_CXX_ENABLE_EXCEPTIONS 1) +set(PICO_CXX_ENABLE_RTTI 1) # Initialize the SDK pico_sdk_init() diff --git a/examples/yukon/yukon_simple_enable.cpp b/examples/yukon/yukon_simple_enable.cpp index ebc08474..fac09814 100644 --- a/examples/yukon/yukon_simple_enable.cpp +++ b/examples/yukon/yukon_simple_enable.cpp @@ -26,6 +26,8 @@ Yukon y = Yukon(); int main() { stdio_init_all(); + y.change_logging(3); + // Initialise the servo y.init(); @@ -35,6 +37,17 @@ int main() { sleep_ms(1000); printf("tud_cdc_connected()\n"); + y.find_slots_with_module(LEDStripModule::info()); + y.find_slots_with_module(DualSwitchedModule::info()); + y.find_slots_with_module(BenchPowerModule::info()); + + //y.detect_module(Yukon::SLOT1); + //y.detect_module(Yukon::SLOT2); + //y.detect_module(Yukon::SLOT3); + //y.detect_module(Yukon::SLOT4); + //y.detect_module(Yukon::SLOT5); + //y.detect_module(Yukon::SLOT6); + try { y.enable_main_output(); diff --git a/libraries/yukon/yukon.cpp b/libraries/yukon/yukon.cpp index 7709c476..bdd1ea6e 100644 --- a/libraries/yukon/yukon.cpp +++ b/libraries/yukon/yukon.cpp @@ -91,6 +91,32 @@ namespace pimoroni { 10, // ADC2_TEMP_ADDR (0b1010) }; + bool LEDStripModule::is_module(uint adc_level, bool slow1, bool slow2, bool slow3) { + return adc_level == ADC_LOW && slow1 == HIGH && slow2 == HIGH && slow3 == HIGH; + } + + bool DualMotorModule::is_module(uint adc_level, bool slow1, bool slow2, bool slow3) { + return adc_level == ADC_HIGH && slow2 == HIGH && slow3 == HIGH; + } + + bool DualSwitchedModule::is_module(uint adc_level, bool slow1, bool slow2, bool slow3) { + return adc_level == ADC_FLOAT && slow1 == HIGH && slow2 == LOW && slow3 == HIGH; + } + + bool BenchPowerModule::is_module(uint adc_level, bool slow1, bool slow2, bool slow3) { + return slow1 == HIGH && slow2 == LOW && slow3 == LOW; + } + + + //module_callback Yukon::KNOWN_MODULES[] = {&LEDStripModule::is_module, &DualMotorModule::is_module}; + //const std::type_index Yukon::KNOWN_MODULES[] = {typeid(LEDStripModule), typeid(DualMotorModule)}; + + const ModuleInfo Yukon::KNOWN_MODULES[] = { + LEDStripModule::info(), + DualMotorModule::info(), + DualSwitchedModule::info(), + BenchPowerModule::info() + }; const TCA Yukon::MAIN_EN = {0, 6}; const TCA Yukon::USER_SW = {0, 7}; @@ -253,25 +279,25 @@ namespace pimoroni { return slot; } - std::vector Yukon::find_slots_with_module(std::type_info module_type) { + std::vector Yukon::find_slots_with_module(ModuleInfo module_type) { if(is_main_output()) { throw std::runtime_error("Cannot find slots with modules whilst the main output is active\n"); } - logging.info("> Finding slots with '{module_type.NAME}' module\n"); + logging.info("> Finding slots with '" + module_type.name + "' module\n"); std::vector slot_ids; for(auto it = slot_assignments.begin(); it != slot_assignments.end(); it++) { SLOT slot = it->first; logging.info("[Slot" + std::to_string(slot.ID) + "] "); - std::type_info* detected = __detect_module(slot); // Need to have a return type that can be null + const ModuleInfo* detected = __detect_module(slot); // Need to have a return type that can be null - if(detected != nullptr && (*detected) == module_type) { - logging.info("Found '{detected.NAME}' module\n"); + if(detected != nullptr && detected->type == module_type.type) { + logging.info("Found '" + detected->name + "' module\n"); slot_ids.push_back(slot.ID); } else { - logging.info("No '{module_type.NAME}` module\n"); + logging.info("No '" + module_type.name + "' module\n"); } } @@ -332,20 +358,72 @@ namespace pimoroni { } } - uint __match_module(uint adc_level, bool slow1, bool slow2, bool slow3) { - return 0; //TODO + const ModuleInfo* Yukon::__match_module(uint adc_level, bool slow1, bool slow2, bool slow3) { + for(uint i = 0; i < count_of(KNOWN_MODULES); i++) { + const ModuleInfo& m = KNOWN_MODULES[i]; + //printf("%s\n", std::get<0>(KNOWN_MODULES[i]).name()); + //printf("%s\n", std::get<1>(KNOWN_MODULES[i]).c_str()); + //printf("%d\n", std::get<2>(KNOWN_MODULES[i])(adc_level, slow1, slow2, slow3)); + //printf("%s\n", KNOWN_MODULES[i]..name()); + if(m.is_module(adc_level, slow1, slow2, slow3)) { + return &m; + } + } + return nullptr; } - std::type_info* __detect_module(uint slot_id) { - return nullptr; //TODO + + const ModuleInfo* Yukon::__detect_module(SLOT slot) { + set_slow_config(slot.SLOW1, false); + set_slow_config(slot.SLOW2, false); + set_slow_config(slot.SLOW3, false); + + get_slow_input(USER_SW); + + __select_address(slot.ADC1_ADDR); + float adc_val = 0.0f; + for(uint i = 0; i < DETECTION_SAMPLES; i++) { + adc_val += __shared_adc_voltage(); + } + adc_val /= DETECTION_SAMPLES; + + bool slow1 = get_slow_input(slot.SLOW1); + bool slow2 = get_slow_input(slot.SLOW2); + bool slow3 = get_slow_input(slot.SLOW3); + + logging.debug("ADC1 = " + std::to_string(adc_val) + ", SLOW1 = " + std::to_string((int)slow1) + ", SLOW2 = " + std::to_string((int)slow2) + ", SLOW3 = " + std::to_string((int)slow3) + ", "); + + uint adc_level = ADC_FLOAT; + if(adc_val <= DETECTION_ADC_LOW) { + adc_level = ADC_LOW; + } + else if(adc_val >= DETECTION_ADC_HIGH) { + adc_level = ADC_HIGH; + } + + const ModuleInfo* detected = __match_module(adc_level, slow1, slow2, slow3); + + __deselect_address(); + + return detected; } - std::type_info* __detect_module(SLOT slot) { - return nullptr; //TODO + const ModuleInfo* Yukon::detect_module(uint slot_id) { + if(is_main_output()) { + throw std::runtime_error("Cannot detect modules whilst the main output is active\n"); + } + + SLOT slot = __check_slot(slot_id); + + return __detect_module(slot); } - uint detect_module(uint slot) { - return 0; - } - uint detect_module(SLOT slot) { - return 0; + + const ModuleInfo* Yukon::detect_module(SLOT slot) { + if(is_main_output()) { + throw std::runtime_error("Cannot detect modules whilst the main output is active\n"); + } + + slot = __check_slot(slot); + + return __detect_module(slot); } void __expand_slot_list(std::vector slot_list) { @@ -535,8 +613,6 @@ namespace pimoroni { return __shared_adc_voltage(); } /* - float time(); - void assign_monitor_action(void* callback_function); */ void Yukon::monitor() { diff --git a/libraries/yukon/yukon.hpp b/libraries/yukon/yukon.hpp index e14b2c3b..6e5299a3 100644 --- a/libraries/yukon/yukon.hpp +++ b/libraries/yukon/yukon.hpp @@ -6,9 +6,101 @@ #include "errors.hpp" #include #include +#include namespace pimoroni { + class YukonModule { + public: + //static const std::string NAME = "Unnamed"; + + static constexpr float ROOM_TEMP = 273.15f + 25.0f; + static constexpr float RESISTOR_AT_ROOM_TEMP = 10000.0f; + static constexpr float BETA = 3435; + + static bool is_module(uint adc_level, bool slow1, bool slow2, bool slow3) { + return false; + } + }; + + enum ADC { + ADC_LOW = 0, + ADC_HIGH = 1, + ADC_FLOAT = 2, + ADC_ANY = 3 + }; + + enum IO { + LOW = false, + HIGH = true + }; + + typedef bool (*module_callback)(uint, bool, bool, bool) ; + + struct ModuleInfo { + std::type_index type; + std::string name; + module_callback is_module; + }; + +#define INFO_FUNC(module_name) \ + static ModuleInfo info() { \ + return { typeid(module_name), module_name::name(), &module_name::is_module }; \ + } + + class LEDStripModule : public YukonModule { + public: + //static const std::string NAME = "Unnamed"; + + static bool is_module(uint adc_level, bool slow1, bool slow2, bool slow3); + + static std::string name() { + return "LED Strip"; + } + + INFO_FUNC(LEDStripModule) + }; + + + class DualMotorModule : public YukonModule { + public: + //static const std::string NAME = "Unnamed"; + + static bool is_module(uint adc_level, bool slow1, bool slow2, bool slow3); + + static std::string name() { + return "Dual Motor"; + } + + INFO_FUNC(DualMotorModule) + }; + + class DualSwitchedModule : public YukonModule { + public: + //static const std::string NAME = "Unnamed"; + + static bool is_module(uint adc_level, bool slow1, bool slow2, bool slow3); + + static std::string name() { + return "Dual Switched Output"; + } + + INFO_FUNC(DualSwitchedModule) + }; + + class BenchPowerModule : public YukonModule { + public: + //static const std::string NAME = "Unnamed"; + + static bool is_module(uint adc_level, bool slow1, bool slow2, bool slow3); + + static std::string name() { + return "Bench Power"; + } + + INFO_FUNC(BenchPowerModule) + }; + struct TCA { uint CHIP; uint GPIO; @@ -67,6 +159,7 @@ namespace pimoroni { } }; + class Yukon { public: static const SLOT SLOT1; @@ -126,6 +219,13 @@ namespace pimoroni { static constexpr float DEFAULT_CURRENT_LIMIT = 20.0f; static constexpr float DEFAULT_TEMPERATURE_LIMIT = 90.0f; static constexpr float ABSOLUTE_MAX_VOLTAGE_LIMIT = 18.0f; + + static const uint DETECTION_SAMPLES = 64; + static constexpr float DETECTION_ADC_LOW = 0.1f; + static constexpr float DETECTION_ADC_HIGH = 3.2f; + + //static module_callback KNOWN_MODULES[]; + static const ModuleInfo KNOWN_MODULES[]; private: I2C i2c; TCA9555 tca0; @@ -182,7 +282,6 @@ namespace pimoroni { void set_slow_polarity(TCA gpio, bool polarity); void change_output_mask(uint8_t chip, uint16_t mask, uint16_t state); - //-------------------------------------------------- void change_logging(uint logging_level); @@ -190,7 +289,7 @@ namespace pimoroni { SLOT __check_slot(uint slot_id); SLOT __check_slot(SLOT slot); - std::vector find_slots_with_module(std::type_info module_type); + std::vector find_slots_with_module(ModuleInfo module_type); void register_with_slot(Module* module, uint slot_id); void register_with_slot(Module* module, SLOT slot); @@ -198,11 +297,10 @@ namespace pimoroni { void deregister_slot(uint slot_id); void deregister_slot(SLOT slot); - uint __match_module(uint adc_level, bool slow1, bool slow2, bool slow3); - std::type_info* __detect_module(uint slot_id); - std::type_info* __detect_module(SLOT slot); - uint detect_module(uint slot_id); - uint detect_module(SLOT slot); + const ModuleInfo* __match_module(uint adc_level, bool slow1, bool slow2, bool slow3); + const ModuleInfo* __detect_module(SLOT slot); + const ModuleInfo* detect_module(uint slot_id); + const ModuleInfo* detect_module(SLOT slot); void __expand_slot_list(std::vector slot_list); void __verify_modules(bool allow_unregistered, bool allow_undetected, bool allow_discrepencies, bool allow_no_modules);