Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(hepa-uv): Add the initial project for the HEPA/UV module #740

Merged
merged 18 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ add_subdir_for_archs(SUBDIR gantry ARCHS cortex-m4)
add_subdir_for_archs(SUBDIR head ARCHS cortex-m4)
add_subdir_for_archs(SUBDIR gripper ARCHS cortex-m4)
add_subdir_for_archs(SUBDIR pipettes ARCHS cortex-m4 cortex-m33)
add_subdir_for_archs(SUBDIR hepa-uv ARCHS cortex-m4)
list(REMOVE_DUPLICATES LINT_TARGETS)

message(VERBOSE "Checking if cross-compiling.")
Expand Down Expand Up @@ -141,6 +142,7 @@ file(GLOB_RECURSE SOURCES_FOR_FORMAT
./spi/*.hpp ./spi/*.cpp
./eeprom/*.hpp ./eeprom/*.cpp ./rear-panel/*.hpp
./rear-panel/*.cpp
./hepa-uv/*.cpp
)

# Targets for formatting. These are here rather than in individual target CMakeLists (e.g.
Expand Down
20 changes: 17 additions & 3 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,17 @@
"gripper"
]
},
{
"name": "hepa-filter",
vegano1 marked this conversation as resolved.
Show resolved Hide resolved
"displayName": "hepa-filter binary",
"description": "Build the hepa-filter cross binary",
"configurePreset": "cross",
"jobs": 4,
"targets": [
"hepa-filter"
]
},

{
"name": "gantry",
"displayName": "gantry binaries",
Expand Down Expand Up @@ -212,7 +223,8 @@
"bootloader-gripper-applications",
"bootloader-pipettes-single-applications",
"bootloader-pipettes-multi-applications",
"bootloader-pipettes-96-applications"
"bootloader-pipettes-96-applications",
"bootloader-hepa-filter-applications"
vegano1 marked this conversation as resolved.
Show resolved Hide resolved
]
},
{
Expand All @@ -230,7 +242,8 @@
"pipettes-single-simulator",
"pipettes-multi-simulator",
"pipettes-96-simulator",
"gripper-simulator"
"gripper-simulator",
"hepa-filter-simulator"
]
},
{
Expand All @@ -248,7 +261,8 @@
"pipettes-single-simulator",
"pipettes-multi-simulator",
"pipettes-96-simulator",
"gripper-simulator"
"gripper-simulator",
"hepa-filter-simulator"
]
}
]
Expand Down
20 changes: 20 additions & 0 deletions bootloader/firmware/stm32G4/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,26 @@ foreach_revision(
NO_CREATE_INSTALL_RULES
)

set(hepauv_sources ${_g4_sources} ${CMAKE_CURRENT_SOURCE_DIR}/hepauv_startup.c)

macro(hepauv_bootloader_loop)
target_link_libraries(${REVISION_TARGET}
PUBLIC STM32G491RETx_bootloader
STM32G4xx_Drivers_Bootloader
bootloader-core)

# TODO: fix node id
target_compile_definitions(${REVISION_TARGET} PUBLIC node_id_gripper)
endmacro()

foreach_revision(
PROJECT_NAME bootloader-hepa-uv
CALL_FOREACH_REV hepauv_bootloader_loop
REVISIONS b1
SOURCES hepauv_sources
NO_CREATE_IMAGE_HEX
NO_CREATE_INSTALL_RULES
)

target_include_directories(STM32G4xx_Drivers_Bootloader
PUBLIC .)
Expand Down
28 changes: 28 additions & 0 deletions bootloader/firmware/stm32G4/hepa_filter_startup.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "bootloader/firmware/system.h"
#include "platform_specific_hal_conf.h"
#include "platform_specific_hal.h"

// This file should only be compiled by the head project. It provides
vegano1 marked this conversation as resolved.
Show resolved Hide resolved
// a startup function that sets up the mount ID lines.

void system_specific_startup() {
// Z/left: PC5
// A/right: PB2
// G (unused): PB1
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
// z/left is pulldown to conform to old EVT external pulls
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
vegano1 marked this conversation as resolved.
Show resolved Hide resolved
// gripper is pulldown because it's not used by gripper and might
// as well be grounded
GPIO_InitStruct.Pin = GPIO_PIN_1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// a/right is pullup to differentiate it from z/left
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
20 changes: 20 additions & 0 deletions bootloader/firmware/stm32G4/hepauv_startup.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include "bootloader/firmware/system.h"
#include "platform_specific_hal_conf.h"
#include "platform_specific_hal.h"
#include "stm32g4xx_hal_gpio.h"

// This file should only be compiled by the gripper project.
// It provides a startup function that properly configures the mount detect
// pin.

void system_specific_startup() {
// We need to set our tool id pin to output high so that the mount knows
// we're here. Unlike the pipette, we're not detecting what mount we're on
// since there's just one so we don't need to start out as input.
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
}
7 changes: 7 additions & 0 deletions common/firmware/gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,10 @@ __attribute__((section( ".ccmram" )))
bool gpio_is_set(void* port, uint16_t pin, uint8_t active_setting) {
return HAL_GPIO_ReadPin(port, pin) == active_setting;
}

#ifdef ENABLE_CCMRAM
__attribute__((section( ".ccmram" )))
#endif
uint8_t gpio_read(void* port, uint16_t pin) {
vegano1 marked this conversation as resolved.
Show resolved Hide resolved
return HAL_GPIO_ReadPin(port, pin);
}
8 changes: 8 additions & 0 deletions common/firmware/gpio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,11 @@ auto gpio::is_set(const gpio::PinConfig& pc)
-> bool {
return gpio_is_set(pc.port, pc.pin, pc.active_setting);
}

#ifdef ENABLE_CCMRAM
__attribute__((section( ".ccmram" )))
#endif
auto gpio::read_pin(const gpio::PinConfig& pc)
-> uint8_t {
return gpio_read(pc.port, pc.pin);
}
2 changes: 2 additions & 0 deletions common/firmware/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ void gpio_reset(void* port, uint16_t pin, uint8_t active_setting);

bool gpio_is_set(void* port, uint16_t pin, uint8_t active_setting);

uint8_t gpio_read(void* port, uint16_t pin);
vegano1 marked this conversation as resolved.
Show resolved Hide resolved

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
33 changes: 33 additions & 0 deletions hepa-uv/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# hepa-uv source tree

add_subdirectory(core)

if (${CMAKE_CROSSCOMPILING})
if (${ARM_ARCH_TYPE} STREQUAL "cortex-m4")
add_subdirectory(firmware)
endif ()
else ()
add_subdirectory(simulator)
add_subdirectory(tests)
endif ()

file(GLOB_RECURSE HEPA_SOURCES_FOR_FORMAT ./*.cpp ./*.hpp ../include/hepa-uv/*.hpp)

# Targets for formatting. These are here rather than in individual target CMakeLists (e.g.
# the ones in tests/ or firmware/) because they don't have semantic reasoning involved and
# can therefore operate on file globs, unlike lint/static analysis

# Target for use during dev - edits files
add_custom_target(
hepa-uv-format
ALL
COMMAND ${Clang_CLANGFORMAT_EXECUTABLE} -style=file -i ${HEPA_SOURCES_FOR_FORMAT}
)

# Target for use in ci - warnings are errors, doesn't edit files
add_custom_target(
hepa-uv-format-ci
COMMAND ${Clang_CLANGFORMAT_EXECUTABLE} -style=file -Werror --ferror-limit=0 -n ${HEPA_SOURCES_FOR_FORMAT}
)

set(LINT_TARGETS ${LINT_TARGETS} PARENT_SCOPE)
8 changes: 8 additions & 0 deletions hepa-uv/core/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
function(target_hepauv_core TARGET)
target_sources(${TARGET} PUBLIC
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/can_tasks.cpp
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/tasks.cpp)
target_include_directories(${TARGET} PUBLIC ${CMAKE_CURRENT_FUNCTION_LIST_DIR})
target_link_libraries(${TARGET} PUBLIC common-core)
target_include_directories(${TARGET} INTERFACE ${CMAKE_SOURCE_DIR}/cpp-utils/include)
endfunction()
144 changes: 144 additions & 0 deletions hepa-uv/core/can_tasks.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
#include <span>

#include "eeprom/core/message_handler.hpp"
#include "hepa-uv/core/can_task.hpp"

using namespace can::dispatch;

static auto& main_queues = hepauv_tasks::get_main_queues();

auto can_sender_queue = freertos_message_queue::FreeRTOSMessageQueue<
can::message_writer_task::TaskMessage>{};

/** The parsed message handler */
static auto gripper_info_handler =
gripper_info::GripperInfoMessageHandler{main_queues, main_queues};
vegano1 marked this conversation as resolved.
Show resolved Hide resolved

/** Handler of system messages. */
static auto system_message_handler =
can::message_handlers::system::SystemMessageHandler{
main_queues,
version_get()->version,
version_get()->flags,
std::span(std::cbegin(version_get()->sha),
std::cend(version_get()->sha)),
revision_get()->primary,
revision_get()->secondary};
static auto system_dispatch_target =
can_task::SystemDispatchTarget{system_message_handler};

/** Handler for eeprom messages.*/
static auto eeprom_message_handler =
eeprom::message_handler::EEPromHandler{main_queues, main_queues};
static auto eeprom_dispatch_target =
can::dispatch::DispatchParseTarget<decltype(eeprom_message_handler),
can::messages::WriteToEEPromRequest,
can::messages::ReadFromEEPromRequest>{
eeprom_message_handler};

static auto gripper_info_dispatch_target =
can_task::GripperInfoDispatchTarget{gripper_info_handler};

struct CheckForNodeId {
can::ids::NodeId node_id;
auto operator()(uint32_t arbitration_id) const {
auto arb = can::arbitration_id::ArbitrationId(arbitration_id);
auto _node_id = arb.node_id();
return ((_node_id == node_id) ||
(_node_id == can::ids::NodeId::broadcast) ||
(_node_id == can::ids::NodeId::hepa_filter));
}
};

/** Dispatcher to the various handlers */
static auto main_dispatcher = can::dispatch::Dispatcher(
[](uint32_t arbitration_id) -> bool {
auto arb = can::arbitration_id::ArbitrationId(arbitration_id);
auto node_id = arb.node_id();
return ((node_id == can::ids::NodeId::broadcast) ||
(node_id == can::ids::NodeId::hepa_filter));
},
system_dispatch_target, gripper_info_dispatch_target,
eeprom_dispatch_target);

auto static reader_message_buffer =
freertos_message_buffer::FreeRTOSMessageBuffer<1024>{};
static auto reader_message_buffer_writer =
can::message_buffer::CanMessageBufferWriter(reader_message_buffer);

/**
* New CAN message callback.
*
* @param identifier Arbitration id
* @param data Message data
* @param length Message data length
*/
void callback(void*, uint32_t identifier, uint8_t* data, uint8_t length) {
reader_message_buffer_writer.send_from_isr(identifier, data,
data + length); // NOLINT
}

/**
* Entry point for the reader task.
* TODO (2021-12-15, AL): Most of what happens in this task should be moved out
* when we move to separate motor tasks.
*/
[[noreturn]] void can_task::CanMessageReaderTask::operator()(
can::bus::CanBus* can_bus) {
can_bus->set_incoming_message_callback(nullptr, callback);

auto filter = can::arbitration_id::ArbitrationId();

// Accept broadcast
filter.node_id(can::ids::NodeId::broadcast);
can_bus->add_filter(CanFilterType::mask, CanFilterConfig::to_fifo0, filter,
can::arbitration_id::ArbitrationId::node_id_bit_mask);

// TODO: add HEPA/UV filter
filter.node_id(can::ids::NodeId::hepa_filter);
can_bus->add_filter(CanFilterType::mask, CanFilterConfig::to_fifo1, filter,
can::arbitration_id::ArbitrationId::node_id_bit_mask);

// Reject everything else.
can_bus->add_filter(CanFilterType::mask, CanFilterConfig::reject, 0, 0);

auto poller = can::freertos_dispatch::FreeRTOSCanBufferPoller(
reader_message_buffer, main_dispatcher);
poller();
}

auto static reader_task = can_task::CanMessageReaderTask{};
auto static writer_task = can_task::CanMessageWriterTask{can_sender_queue};

auto static reader_task_control =
freertos_task::FreeRTOSTask<512, can_task::CanMessageReaderTask>{
reader_task};
auto static writer_task_control =
freertos_task::FreeRTOSTask<512, can_task::CanMessageWriterTask>{
writer_task};

/**
* Start the can reader task
* @param canbus The can bus reference
* @return The task entry point.
*/
auto can_task::start_reader(can::bus::CanBus& canbus)
-> can_task::CanMessageReaderTask& {
LOG("Starting the CAN reader task");

reader_task_control.start(5, "can reader task", &canbus);
return reader_task;
}

/**
* Start the can writer task
* @param canbus The can bus reference
* @return The task entry point
*/
auto can_task::start_writer(can::bus::CanBus& canbus)
-> can_task::CanMessageWriterTask& {
LOG("Starting the CAN writer task");

writer_task_control.start(5, "can writer task", &canbus);
return writer_task;
}
Loading
Loading