diff --git a/FprimeZephyrReference/Components/CMakeLists.txt b/FprimeZephyrReference/Components/CMakeLists.txt index 9b9cab94..6d9f085f 100644 --- a/FprimeZephyrReference/Components/CMakeLists.txt +++ b/FprimeZephyrReference/Components/CMakeLists.txt @@ -11,3 +11,4 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ImuManager/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/NullPrmDb/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PowerMonitor/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Watchdog") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/LoadSwitch/") diff --git a/FprimeZephyrReference/Components/LoadSwitch/CMakeLists.txt b/FprimeZephyrReference/Components/LoadSwitch/CMakeLists.txt new file mode 100644 index 00000000..5be47b2d --- /dev/null +++ b/FprimeZephyrReference/Components/LoadSwitch/CMakeLists.txt @@ -0,0 +1,36 @@ +#### +# F Prime CMakeLists.txt: +# +# SOURCES: list of source files (to be compiled) +# AUTOCODER_INPUTS: list of files to be passed to the autocoders +# DEPENDS: list of libraries that this module depends on +# +# More information in the F´ CMake API documentation: +# https://fprime.jpl.nasa.gov/latest/docs/reference/api/cmake/API/ +# +#### + +# Module names are derived from the path from the nearest project/library/framework +# root when not specifically overridden by the developer. i.e. The module defined by +# `Ref/SignalGen/CMakeLists.txt` will be named `Ref_SignalGen`. + +register_fprime_library( + AUTOCODER_INPUTS + "${CMAKE_CURRENT_LIST_DIR}/LoadSwitch.fpp" + SOURCES + "${CMAKE_CURRENT_LIST_DIR}/LoadSwitch.cpp" +# DEPENDS +# MyPackage_MyOtherModule +) + +### Unit Tests ### +# register_fprime_ut( +# AUTOCODER_INPUTS +# "${CMAKE_CURRENT_LIST_DIR}/LoadSwitch.fpp" +# SOURCES +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/LoadSwitchTestMain.cpp" +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/LoadSwitchTester.cpp" +# DEPENDS +# STest # For rules-based testing +# UT_AUTO_HELPERS +# ) diff --git a/FprimeZephyrReference/Components/LoadSwitch/LoadSwitch.cpp b/FprimeZephyrReference/Components/LoadSwitch/LoadSwitch.cpp new file mode 100644 index 00000000..ea769e63 --- /dev/null +++ b/FprimeZephyrReference/Components/LoadSwitch/LoadSwitch.cpp @@ -0,0 +1,53 @@ +// ====================================================================== +// \title LoadSwitch.cpp +// \author Moises, sarah +// \brief cpp file for LoadSwitch component implementation class +// ====================================================================== + +#include "FprimeZephyrReference/Components/LoadSwitch/LoadSwitch.hpp" + +#include + +namespace Components { + +// ---------------------------------------------------------------------- +// Component construction and destruction +// ---------------------------------------------------------------------- + +LoadSwitch ::LoadSwitch(const char* const compName) : LoadSwitchComponentBase(compName) {} + +LoadSwitch ::~LoadSwitch() {} + +// ---------------------------------------------------------------------- +// Handler implementations for typed input ports +// ---------------------------------------------------------------------- + +void LoadSwitch ::Reset_handler(FwIndexType portNum) { + this->gpioSet_out(0, Fw::Logic::LOW); + this->log_ACTIVITY_HI_StatusChanged(Fw::On::OFF); + this->tlmWrite_IsOn(Fw::On::OFF); + k_sleep(K_MSEC(100)); + this->gpioSet_out(0, Fw::Logic::HIGH); + this->log_ACTIVITY_HI_StatusChanged(Fw::On::ON); + this->tlmWrite_IsOn(Fw::On::ON); +} + +// ---------------------------------------------------------------------- +// Handler implementations for commands +// ---------------------------------------------------------------------- + +void LoadSwitch ::TURN_ON_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) { + this->gpioSet_out(0, Fw::Logic::HIGH); + this->log_ACTIVITY_HI_StatusChanged(Fw::On::ON); + this->tlmWrite_IsOn(Fw::On::ON); + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); +} + +void LoadSwitch ::TURN_OFF_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) { + this->gpioSet_out(0, Fw::Logic::LOW); + this->log_ACTIVITY_HI_StatusChanged(Fw::On::OFF); + this->tlmWrite_IsOn(Fw::On::OFF); + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); +} + +} // namespace Components diff --git a/FprimeZephyrReference/Components/LoadSwitch/LoadSwitch.fpp b/FprimeZephyrReference/Components/LoadSwitch/LoadSwitch.fpp new file mode 100644 index 00000000..284d3657 --- /dev/null +++ b/FprimeZephyrReference/Components/LoadSwitch/LoadSwitch.fpp @@ -0,0 +1,72 @@ +module Components { + @ A generic load switch for controlling power to components + passive component LoadSwitch { + + # One async command/port is required for active components + # This should be overridden by the developers with a useful command/port + + ############################################################################## + #### Uncomment the following examples to start customizing your component #### + ############################################################################## + + # @ Example async command + # async command COMMAND_NAME(param_name: U32) + sync command TURN_ON() + sync command TURN_OFF() + + # @ Example telemetry counter + # telemetry ExampleCounter: U64 + telemetry IsOn: Fw.On + + # @ Example event + # event ExampleStateEvent(example_state: Fw.On) severity activity high id 0 format "State set to {}" + event StatusChanged($state: Fw.On) severity activity high id 1 format "Load switch state changed to {}" + + # @ Example port: receiving calls from the rate group + # sync input port run: Svc.Sched + #output port Status: Drv.GpioRead + #We will not be putting a Drv.GpioRead port here, we are using the Gpio Driver component which has this already! + + @ Port sending calls to the GPIO driver + output port gpioSet: Drv.GpioWrite + + + # Input that will be used by other components if they want to force a reset + # (off and on again) of the load switch + sync input port Reset: Fw.Signal + + # @ Example parameter + # param PARAMETER_NAME: U32 + + ############################################################################### + # Standard AC Ports: Required for Channels, Events, Commands, and Parameters # + ############################################################################### + @ Port for requesting the current time + time get port timeCaller + + @ Port for sending command registrations + command reg port cmdRegOut + + @ Port for receiving commands + command recv port cmdIn + + @ Port for sending command responses + command resp port cmdResponseOut + + @ Port for sending textual representation of events + text event port logTextOut + + @ Port for sending events to downlink + event port logOut + + @ Port for sending telemetry channels to downlink + telemetry port tlmOut + + @ Port to return the value of a parameter + param get port prmGetOut + + @Port to set the value of a parameter + param set port prmSetOut + + } +} diff --git a/FprimeZephyrReference/Components/LoadSwitch/LoadSwitch.hpp b/FprimeZephyrReference/Components/LoadSwitch/LoadSwitch.hpp new file mode 100644 index 00000000..acaa5d23 --- /dev/null +++ b/FprimeZephyrReference/Components/LoadSwitch/LoadSwitch.hpp @@ -0,0 +1,57 @@ +// ====================================================================== +// \title LoadSwitch.hpp +// \author Moises, sarah +// \brief hpp file for LoadSwitch component implementation class +// ====================================================================== + +#ifndef Components_LoadSwitch_HPP +#define Components_LoadSwitch_HPP + +#include "FprimeZephyrReference/Components/LoadSwitch/LoadSwitchComponentAc.hpp" +#include + +// Forward declare Zephyr types to avoid header conflicts +struct device; + +namespace Components { + +class LoadSwitch final : public LoadSwitchComponentBase { + public: + // ---------------------------------------------------------------------- + // Component construction and destruction + // ---------------------------------------------------------------------- + + //! Construct LoadSwitch object + LoadSwitch(const char* const compName //!< The component name + ); + + //! Destroy LoadSwitch object + ~LoadSwitch(); + + private: + // ---------------------------------------------------------------------- + // Handler implementations for commands + // ---------------------------------------------------------------------- + + //! Handler implementation for command TURN_ON + void TURN_ON_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq //!< The command sequence number + ) override; + + //! Handler implementation for command TURN_OFF + void TURN_OFF_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq //!< The command sequence number + ) override; + + // ---------------------------------------------------------------------- + // Handler implementations for typed input ports + // ---------------------------------------------------------------------- + + //! Handler implementation for Reset + void Reset_handler(FwIndexType portNum //!< The port number + ) override; +}; + +} // namespace Components + +#endif diff --git a/FprimeZephyrReference/Components/LoadSwitch/docs/LoadSwitch.svg b/FprimeZephyrReference/Components/LoadSwitch/docs/LoadSwitch.svg new file mode 100644 index 00000000..a3cfb121 --- /dev/null +++ b/FprimeZephyrReference/Components/LoadSwitch/docs/LoadSwitch.svg @@ -0,0 +1 @@ +LoadSwitchResettimeCallercmdRegOutcmdIncmdResponseOutlogTextOutlogOuttlmOutprmGetOutprmSetOutgpioSet diff --git a/FprimeZephyrReference/Components/LoadSwitch/docs/sdd.md b/FprimeZephyrReference/Components/LoadSwitch/docs/sdd.md new file mode 100644 index 00000000..51d9dec6 --- /dev/null +++ b/FprimeZephyrReference/Components/LoadSwitch/docs/sdd.md @@ -0,0 +1,53 @@ +# Components::LoadSwitch + +![LoadSwitch](LoadSwitch.svg) + +## Overview + +The `LoadSwitch` component is an active F' component that controls a single load switch output +through the `gpioSet` output port (connected to the platform's GPIO driver). It exposes two +async commands to turn the switch on and off, telemetry reporting the current state, and an +async `Reset` input which toggles the switch (off, short delay, on). + +## Responsibility + +- Control the power rail for a connected peripheral by asserting/deasserting a GPIO. +- Report state changes via an event and telemetry channel. + +## External interface + +### Commands + +| Name | Description | Implementation notes | +|---|---|---| +| TURN_ON | Turn on the associated power rail | `TURN_ON_cmdHandler` sets the gpio via `gpioSet_out(0, Fw::Logic::HIGH)`, emits `StatusChanged` (ON), updates `IsOn` telemetry, replies OK. | +| TURN_OFF | Turn off the associated power rail | `TURN_OFF_cmdHandler` sets the gpio via `gpioSet_out(0, Fw::Logic::LOW)`, emits `StatusChanged` (OFF), updates `IsOn` telemetry, replies OK. | + +### Telemetry + +| Name | Type | Description | +|---|---:|---| +| IsOn | Fw.On | Current power state; written after commands and on Reset handling. | + +### Events + +| Name | Severity | ID | Format | +|---|---|---:|---| +| StatusChanged | activity high | 1 | "Load switch state changed to {}" | + +The component logs the `StatusChanged` event whenever the switch transitions due to a command or a Reset. + +### Ports + +| Port name | Direction | Port type | Notes | +|---|---|---|---| +| gpioSet | output | Drv.GpioWrite | Used to write the physical GPIO. Implementation always uses index 0 (`gpioSet_out(0, ...)`). | +| Reset | input (async) | Fw.Signal | Causes the component to perform a hardware reset sequence: LOW -> wait 100ms -> HIGH. | + + +## Change Log + +| Date | Description | +|---|---| +| 10-22-2025 | Sarah, Kevin, and MoMata's first commit | +| 11-07-2025 | Updated SDD to match implementation in `LoadSwitch.cpp/.hpp/.fpp` (commands, telemetry, event, ports, reset behavior). | diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi b/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi index e8e74ee4..7c0bd5b1 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi +++ b/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi @@ -61,7 +61,18 @@ telemetry packets ReferenceDeploymentPackets { ReferenceDeployment.antennaDeployer.LastDistance } - packet PowerMonitor id 9 group 4 { + packet LoadSwitches id 9 group 4 { + ReferenceDeployment.face4LoadSwitch.IsOn + ReferenceDeployment.face0LoadSwitch.IsOn + ReferenceDeployment.face1LoadSwitch.IsOn + ReferenceDeployment.face2LoadSwitch.IsOn + ReferenceDeployment.face3LoadSwitch.IsOn + ReferenceDeployment.face5LoadSwitch.IsOn + ReferenceDeployment.payloadPowerLoadSwitch.IsOn + ReferenceDeployment.payloadBatteryLoadSwitch.IsOn + } + + packet PowerMonitor id 10 group 4 { ReferenceDeployment.ina219SysManager.Voltage ReferenceDeployment.ina219SysManager.Current ReferenceDeployment.ina219SysManager.Power diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp b/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp index 0580479a..34f7736c 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp +++ b/FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp @@ -16,6 +16,15 @@ static const struct gpio_dt_spec ledGpio = GPIO_DT_SPEC_GET(DT_NODELABEL(led0), gpios); static const struct gpio_dt_spec burnwire0Gpio = GPIO_DT_SPEC_GET(DT_NODELABEL(burnwire0), gpios); static const struct gpio_dt_spec burnwire1Gpio = GPIO_DT_SPEC_GET(DT_NODELABEL(burnwire1), gpios); +static const struct gpio_dt_spec face0LoadSwitchGpio = GPIO_DT_SPEC_GET(DT_NODELABEL(face0_enable), gpios); +static const struct gpio_dt_spec face1LoadSwitchGpio = GPIO_DT_SPEC_GET(DT_NODELABEL(face1_enable), gpios); +static const struct gpio_dt_spec face2LoadSwitchGpio = GPIO_DT_SPEC_GET(DT_NODELABEL(face2_enable), gpios); +static const struct gpio_dt_spec face3LoadSwitchGpio = GPIO_DT_SPEC_GET(DT_NODELABEL(face3_enable), gpios); +static const struct gpio_dt_spec face4LoadSwitchGpio = GPIO_DT_SPEC_GET(DT_NODELABEL(face4_enable), gpios); +static const struct gpio_dt_spec face5LoadSwitchGpio = GPIO_DT_SPEC_GET(DT_NODELABEL(face5_enable), gpios); +static const struct gpio_dt_spec payloadPowerLoadSwitchGpio = GPIO_DT_SPEC_GET(DT_NODELABEL(payload_pwr_enable), gpios); +static const struct gpio_dt_spec payloadBatteryLoadSwitchGpio = + GPIO_DT_SPEC_GET(DT_NODELABEL(payload_batt_enable), gpios); // Allows easy reference to objects in FPP/autocoder required namespaces using namespace ReferenceDeployment; @@ -57,9 +66,17 @@ void configureTopology() { rateGroup10Hz.configure(rateGroup10HzContext, FW_NUM_ARRAY_ELEMENTS(rateGroup10HzContext)); rateGroup1Hz.configure(rateGroup1HzContext, FW_NUM_ARRAY_ELEMENTS(rateGroup1HzContext)); - gpioDriver.open(ledGpio, Zephyr::ZephyrGpioDriver::GpioConfiguration::OUT); + gpioWatchdog.open(ledGpio, Zephyr::ZephyrGpioDriver::GpioConfiguration::OUT); gpioBurnwire0.open(burnwire0Gpio, Zephyr::ZephyrGpioDriver::GpioConfiguration::OUT); gpioBurnwire1.open(burnwire1Gpio, Zephyr::ZephyrGpioDriver::GpioConfiguration::OUT); + gpioface4LS.open(face4LoadSwitchGpio, Zephyr::ZephyrGpioDriver::GpioConfiguration::OUT); + gpioface0LS.open(face0LoadSwitchGpio, Zephyr::ZephyrGpioDriver::GpioConfiguration::OUT); + gpioface1LS.open(face1LoadSwitchGpio, Zephyr::ZephyrGpioDriver::GpioConfiguration::OUT); + gpioface2LS.open(face2LoadSwitchGpio, Zephyr::ZephyrGpioDriver::GpioConfiguration::OUT); + gpioface3LS.open(face3LoadSwitchGpio, Zephyr::ZephyrGpioDriver::GpioConfiguration::OUT); + gpioface5LS.open(face5LoadSwitchGpio, Zephyr::ZephyrGpioDriver::GpioConfiguration::OUT); + gpioPayloadPowerLS.open(payloadPowerLoadSwitchGpio, Zephyr::ZephyrGpioDriver::GpioConfiguration::OUT); + gpioPayloadBatteryLS.open(payloadBatteryLoadSwitchGpio, Zephyr::ZephyrGpioDriver::GpioConfiguration::OUT); } // Public functions for use in main program are namespaced with deployment name ReferenceDeployment diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp b/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp index 4cf1dbf9..d2ff5dfd 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp +++ b/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp @@ -37,7 +37,8 @@ module ReferenceDeployment { stack size Default.STACK_SIZE \ priority 4 - instance prmDb: Svc.PrmDb base id 0x10003000 \ + + instance prmDb: Svc.PrmDb base id 0x1000B000 \ queue size Default.QUEUE_SIZE \ stack size Default.STACK_SIZE \ priority 5 @@ -58,7 +59,7 @@ module ReferenceDeployment { instance comDriver: Zephyr.ZephyrUartDriver base id 0x10013000 - instance gpioDriver: Zephyr.ZephyrGpioDriver base id 0x10014000 + instance gpioWatchdog: Zephyr.ZephyrGpioDriver base id 0x10014000 instance watchdog: Components.Watchdog base id 0x10015000 @@ -88,11 +89,43 @@ module ReferenceDeployment { instance antennaDeployer: Components.AntennaDeployer base id 0x10029000 - instance fsSpace: Components.FsSpace base id 0x10030000 + instance gpioface4LS: Zephyr.ZephyrGpioDriver base id 0x1002A000 + + instance gpioface0LS: Zephyr.ZephyrGpioDriver base id 0x1002B000 + + instance gpioface1LS: Zephyr.ZephyrGpioDriver base id 0x1002C000 + + instance gpioface2LS: Zephyr.ZephyrGpioDriver base id 0x1002D000 + + instance gpioface3LS: Zephyr.ZephyrGpioDriver base id 0x1002E000 + + instance gpioface5LS: Zephyr.ZephyrGpioDriver base id 0x1002F000 + + instance gpioPayloadPowerLS: Zephyr.ZephyrGpioDriver base id 0x10030000 + + instance gpioPayloadBatteryLS: Zephyr.ZephyrGpioDriver base id 0x10031000 + + instance fsSpace: Components.FsSpace base id 0x10032000 + + instance face4LoadSwitch: Components.LoadSwitch base id 0x10033000 + + instance face0LoadSwitch: Components.LoadSwitch base id 0x10034000 + + instance face1LoadSwitch: Components.LoadSwitch base id 0x10035000 + + instance face2LoadSwitch: Components.LoadSwitch base id 0x10036000 + + instance face3LoadSwitch: Components.LoadSwitch base id 0x10037000 + + instance face5LoadSwitch: Components.LoadSwitch base id 0x10038000 + + instance payloadPowerLoadSwitch: Components.LoadSwitch base id 0x10039000 + + instance payloadBatteryLoadSwitch: Components.LoadSwitch base id 0x1003A000 - instance powerMonitor: Components.PowerMonitor base id 0x10031000 + instance powerMonitor: Components.PowerMonitor base id 0x1003C000 - instance ina219SysManager: Drv.Ina219Manager base id 0x10032000 + instance ina219SysManager: Drv.Ina219Manager base id 0x1003D000 - instance ina219SolManager: Drv.Ina219Manager base id 0x10033000 + instance ina219SolManager: Drv.Ina219Manager base id 0x1003E000 } diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp b/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp index 79169a2e..b4f19ebb 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp +++ b/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp @@ -26,9 +26,17 @@ module ReferenceDeployment { instance rateGroupDriver instance timer instance lora - instance gpioDriver + instance gpioWatchdog instance gpioBurnwire0 instance gpioBurnwire1 + instance gpioface0LS + instance gpioface1LS + instance gpioface2LS + instance gpioface3LS + instance gpioface4LS + instance gpioface5LS + instance gpioPayloadPowerLS + instance gpioPayloadBatteryLS instance watchdog instance prmDb instance rtcManager @@ -43,6 +51,15 @@ module ReferenceDeployment { instance comSplitterTelemetry # For UART sideband communication instance comDriver + + instance face4LoadSwitch + instance face0LoadSwitch + instance face1LoadSwitch + instance face2LoadSwitch + instance face3LoadSwitch + instance face5LoadSwitch + instance payloadPowerLoadSwitch + instance payloadBatteryLoadSwitch instance fsSpace instance powerMonitor instance ina219SysManager @@ -146,7 +163,18 @@ module ReferenceDeployment { connections Watchdog { - watchdog.gpioSet -> gpioDriver.gpioWrite + watchdog.gpioSet -> gpioWatchdog.gpioWrite + } + + connections LoadSwitches { + face4LoadSwitch.gpioSet -> gpioface4LS.gpioWrite + face0LoadSwitch.gpioSet -> gpioface0LS.gpioWrite + face1LoadSwitch.gpioSet -> gpioface1LS.gpioWrite + face2LoadSwitch.gpioSet -> gpioface2LS.gpioWrite + face3LoadSwitch.gpioSet -> gpioface3LS.gpioWrite + face5LoadSwitch.gpioSet -> gpioface5LS.gpioWrite + payloadPowerLoadSwitch.gpioSet -> gpioPayloadPowerLS.gpioWrite + payloadBatteryLoadSwitch.gpioSet -> gpioPayloadBatteryLS.gpioWrite } connections BurnwireGpio { diff --git a/FprimeZephyrReference/project/config/CommandDispatcherImplCfg.hpp b/FprimeZephyrReference/project/config/CommandDispatcherImplCfg.hpp index ab88a46b..350367d4 100644 --- a/FprimeZephyrReference/project/config/CommandDispatcherImplCfg.hpp +++ b/FprimeZephyrReference/project/config/CommandDispatcherImplCfg.hpp @@ -11,7 +11,7 @@ // Define configuration values for dispatcher enum { - CMD_DISPATCHER_DISPATCH_TABLE_SIZE = 50, // !< The size of the table holding opcodes to dispatch + CMD_DISPATCHER_DISPATCH_TABLE_SIZE = 100, // !< The size of the table holding opcodes to dispatch CMD_DISPATCHER_SEQUENCER_TABLE_SIZE = 10, // !< The size of the table holding commands in progress }; diff --git a/FprimeZephyrReference/test/int/load_switch_test.py b/FprimeZephyrReference/test/int/load_switch_test.py new file mode 100644 index 00000000..7196c856 --- /dev/null +++ b/FprimeZephyrReference/test/int/load_switch_test.py @@ -0,0 +1,87 @@ +""" +load_switch_test.py: + +Integration tests for the Load-Switch component. +""" + +import pytest +from common import proves_send_and_assert_command +from fprime_gds.common.data_types.ch_data import ChData +from fprime_gds.common.testing_fw.api import IntegrationTestAPI + +ON = "ON" +OFF = "OFF" + +loadswitch = "ReferenceDeployment.face0LoadSwitch" + + +@pytest.fixture(autouse=True) +def ensure_loadswitch_off(fprime_test_api: IntegrationTestAPI, start_gds): + """Ensure LoadSwitch starts in OFF state""" + turn_off(fprime_test_api) + yield + turn_off(fprime_test_api) + + +def turn_on(fprime_test_api: IntegrationTestAPI): + """Helper function to turn on the loadswitch""" + proves_send_and_assert_command( + fprime_test_api, + f"{loadswitch}.TURN_ON", + ) + + +def turn_off(fprime_test_api: IntegrationTestAPI): + """Helper function to turn off the loadswitch""" + proves_send_and_assert_command(fprime_test_api, f"{loadswitch}.TURN_OFF") + + +def get_is_on(fprime_test_api: IntegrationTestAPI) -> str: + """Helper function to request packet and get fresh IsOn telemetry""" + proves_send_and_assert_command( + fprime_test_api, + "CdhCore.tlmSend.SEND_PKT", + ["9"], + ) + result: ChData = fprime_test_api.assert_telemetry( + f"{loadswitch}.IsOn", start="NOW", timeout=3 + ) + return result.get_val() + + +def test_01_loadswitch_telemetry_basic(fprime_test_api: IntegrationTestAPI, start_gds): + """Test that we can read IsOn telemetry""" + value = get_is_on(fprime_test_api) + assert value in (ON, OFF), f"IsOn should be {ON} or {OFF}, got {value}" + + +def test_02_turn_on_sets_high(fprime_test_api: IntegrationTestAPI, start_gds): + """ + Test TURN_ON command sets GPIO high, emits ON event, and updates telemetry + """ + + # Send turn_on command + turn_on(fprime_test_api) + + # Confirm Load-Switch turned ON + fprime_test_api.assert_event(f"{loadswitch}.StatusChanged", args=[ON], timeout=2) + + # Confirm telemetry IsOn is 1 + value = get_is_on(fprime_test_api) + assert value == ON, f"Expected IsOn = {ON} after TURN_ON, got {value}" + + +def test_03_turn_off_sets_low(fprime_test_api: IntegrationTestAPI, start_gds): + """ + Test TURN_OFF command sets GPIO low, emits OFF event, and updates telemetry + """ + + # Send turn_on command + turn_off(fprime_test_api) + + # Confirm Load-Switch turned OFF + fprime_test_api.assert_event(f"{loadswitch}.StatusChanged", args=[OFF], timeout=2) + + # Confirm telemetry IsOn is 0 + value = get_is_on(fprime_test_api) + assert value == OFF, f"Expected IsOn = {OFF} after TURN_OFF, got {value}" diff --git a/FprimeZephyrReference/test/int/power_monitor_test.py b/FprimeZephyrReference/test/int/power_monitor_test.py index 495ee55e..0ab97c45 100644 --- a/FprimeZephyrReference/test/int/power_monitor_test.py +++ b/FprimeZephyrReference/test/int/power_monitor_test.py @@ -22,7 +22,7 @@ def send_packet(fprime_test_api: IntegrationTestAPI, start_gds): proves_send_and_assert_command( fprime_test_api, "CdhCore.tlmSend.SEND_PKT", - ["9"], + ["10"], ) diff --git a/boards/bronco_space/proves_flight_control_board_v5/proves_flight_control_board_v5.dtsi b/boards/bronco_space/proves_flight_control_board_v5/proves_flight_control_board_v5.dtsi index 363ac07c..87746b0b 100644 --- a/boards/bronco_space/proves_flight_control_board_v5/proves_flight_control_board_v5.dtsi +++ b/boards/bronco_space/proves_flight_control_board_v5/proves_flight_control_board_v5.dtsi @@ -190,4 +190,155 @@ zephyr_udc0: &usbd { backup-switch-mode = "direct"; label = "RV3028"; }; + ina219: ina219@40 { + compatible = "ti,ina219"; + reg = <0x40>; + shunt-milliohm = <2>; + lsb-microamp = <100>; + label = "INA219"; + }; + mcp23017: mcp23017@20 { + compatible = "microchip,mcp23017"; + reg = <0x20>; + gpio-controller; + #gpio-cells = <2>; + + /* gpio-line-names must match the numeric indexes used below. The + * topology and gpio nodes expect the following mapping (index : name): + * 0: ENABLE_HEATER (GPA0) + * 1: PAYLOAD_PWR_ENABLE (GPA1) + * 2: FIRE_DEPLOY2_B (GPA2) + * 3: PAYLOAD_BATT_ENABLE (GPA3) + * 4: RF2_IO2 (GPA4) + * 5: RF2_IO1 (GPA5) + * 6: RF2_IO0 (GPA6) + * 7: RF2_IO3 (GPA7) + * 8: FACE4_ENABLE (GPB0) + * 9: FACE0_ENABLE (GPB1) + * 10: FACE1_ENABLE (GPB2) + * 11: FACE2_ENABLE (GPB3) + * 12: FACE3_ENABLE (GPB4) + * 13: FACE5_ENABLE (GPB5) + * 14: READONLY (GPB6) + * 15: CHARGE (GPB7) + */ + gpio-line-names = "ENABLE_HEATER", "PAYLOAD_PWR_ENABLE", "FIRE_DEPLOY2_B", "PAYLOAD_BATT_ENABLE", + "RF2_IO2", "RF2_IO1", "RF2_IO0", "RF2_IO3", + "FACE4_ENABLE", "FACE0_ENABLE", "FACE1_ENABLE", "FACE2_ENABLE", + "FACE3_ENABLE", "FACE5_ENABLE", "READONLY", "CHARGE"; + + reset-gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; + + ngpios = <16>; + }; +}; + +// GPIO Expander IO +/ { + + // Define the GPIO outputs based on your schematic + gpio_outputs { + compatible = "gpio-leds"; // Using gpio-leds as a convenient container + + face4_enable: face4-enable { + + gpios = <&mcp23017 8 GPIO_ACTIVE_HIGH>; // GPB0 + label = "FACE4_ENABLE"; + }; + + face0_enable: face0-enable { + + gpios = <&mcp23017 9 GPIO_ACTIVE_HIGH>; // GPB1 + label = "FACE0_ENABLE"; + }; + + face1_enable: face1-enable { + + gpios = <&mcp23017 10 GPIO_ACTIVE_HIGH>; // GPB2 + label = "FACE1_ENABLE"; + }; + + face2_enable: face2-enable { + + gpios = <&mcp23017 11 GPIO_ACTIVE_HIGH>; // GPB3 + label = "FACE2_ENABLE"; + }; + + face3_enable: face3-enable { + + gpios = <&mcp23017 12 GPIO_ACTIVE_HIGH>; // GPB4 + label = "FACE3_ENABLE"; + }; + + face5_enable: face5-enable { + + gpios = <&mcp23017 13 GPIO_ACTIVE_HIGH>; // GPB5 + label = "FACE5_ENABLE"; + }; + + enable_heater: enable-heater { + + gpios = <&mcp23017 0 GPIO_ACTIVE_HIGH>; // GPA0 + label = "ENABLE_Heater"; + }; + + payload_pwr_enable: payload-pwr-enable { + + gpios = <&mcp23017 1 GPIO_ACTIVE_HIGH>; // GPA1 + label = "PAYLOAD_PWR_ENABLE"; + }; + + fire_deploy2_b: fire-deploy2-b { + + gpios = <&mcp23017 2 GPIO_ACTIVE_HIGH>; // GPA2 + label = "FIRE_DEPLOY2_B"; + }; + + payload_batt_enable: payload-batt-enable { + + gpios = <&mcp23017 3 GPIO_ACTIVE_HIGH>; // GPA3 + label = "PAYLOAD_BATT_ENABLE"; + }; + }; + + // Define GPIO inputs + gpio_inputs { + compatible = "gpio-keys"; + + charge: charge { + + gpios = <&mcp23017 16 GPIO_ACTIVE_HIGH>; // GPB7 + label = "CHARGE"; + }; + + readonly: readonly { + + gpios = <&mcp23017 15 GPIO_ACTIVE_HIGH>; // GPB6 + label = "READONLY"; + }; + + rf2_io2: rf2-io2 { + + gpios = <&mcp23017 4 GPIO_ACTIVE_HIGH>; // GPA4 + label = "RF2_IO2"; + }; + + rf2_io1: rf2-io1 { + + gpios = <&mcp23017 5 GPIO_ACTIVE_HIGH>; // GPA5 + label = "RF2_IO1"; + }; + + rf2_io0: rf2-io0 { + + gpios = <&mcp23017 6 GPIO_ACTIVE_HIGH>; // GPA6 + label = "RF2_IO0"; + }; + + rf2_io3: rf2-io3 { + + gpios = <&mcp23017 7 GPIO_ACTIVE_HIGH>; // GPA7 + label = "RF2_IO3"; + }; + }; };