// Copyright (c) FIRST and other WPILib contributors. // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. #include "wpi/hal/PWM.h" #include #include #include #include #include "HALInitializer.hpp" #include "PortsInternal.hpp" #include "SmartIo.hpp" #include "wpi/hal/ErrorHandling.hpp" #include "wpi/hal/Errors.h" #include "wpi/hal/handles/HandlesInternal.hpp" #include "wpi/hal/monotonic_clock.hpp" using namespace wpi::hal; namespace wpi::hal::init { void InitializePWM() {} } // namespace wpi::hal::init extern "C" { HAL_DigitalHandle HAL_InitializePWMPort(int32_t channel, const char* allocationLocation, int32_t* status) { wpi::hal::init::CheckInit(); if (channel < 0 || channel >= kNumSmartIo) { *status = MakeErrorIndexOutOfRange(HAL_RESOURCE_OUT_OF_RANGE, "Invalid Index for PWM", 0, kNumSmartIo, channel); return HAL_INVALID_HANDLE; } auto resource = smartIoHandles->Allocate(channel, HAL_HandleEnum::PWM, "PWM"); if (!resource) { *status = resource.error(); return HAL_INVALID_HANDLE; // failed to allocate. Pass error back. } auto [handle, port] = *resource; port->channel = channel; *status = port->InitializeMode(SmartIoMode::PwmOutput); if (*status != 0) { smartIoHandles->Free(handle, HAL_HandleEnum::PWM); return HAL_INVALID_HANDLE; } // Disable the PWM output. HAL_SetPWMPulseTimeMicroseconds(handle, 0, status); if (*status != 0) { smartIoHandles->Free(handle, HAL_HandleEnum::PWM); return HAL_INVALID_HANDLE; } port->previousAllocation = allocationLocation ? allocationLocation : ""; return handle; } void HAL_FreePWMPort(HAL_DigitalHandle pwmPortHandle) { auto port = smartIoHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM); if (port == nullptr) { return; } smartIoHandles->Free(pwmPortHandle, HAL_HandleEnum::PWM); // Wait for no other object to hold this handle. auto start = wpi::hal::monotonic_clock::now(); while (port.use_count() != 1) { auto current = wpi::hal::monotonic_clock::now(); if (start + std::chrono::seconds(1) < current) { std::puts("PWM handle free timeout"); std::fflush(stdout); break; } std::this_thread::yield(); } } void HAL_SetPWMSimDevice(HAL_DigitalHandle handle, HAL_SimDeviceHandle device) { } HAL_Bool HAL_CheckPWMChannel(int32_t channel) { return channel < kNumSmartIo && channel >= 0; } void HAL_SetPWMPulseTimeMicroseconds(HAL_DigitalHandle pwmPortHandle, int32_t microsecondPulseTime, int32_t* status) { auto port = smartIoHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM); if (port == nullptr) { *status = HAL_HANDLE_ERROR; return; } if (microsecondPulseTime < 0 || (microsecondPulseTime != 0xFFFF && microsecondPulseTime >= 4096)) { *status = MakeError( HAL_PARAMETER_OUT_OF_RANGE, fmt::format("Pulse time {} out of range. Expect [0-4096) or 0xFFFF", microsecondPulseTime)); return; } *status = port->SetPwmMicroseconds(microsecondPulseTime); } int32_t HAL_GetPWMPulseTimeMicroseconds(HAL_DigitalHandle pwmPortHandle, int32_t* status) { auto port = smartIoHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM); if (port == nullptr) { *status = HAL_HANDLE_ERROR; return 0; } uint16_t microseconds = 0; *status = port->GetPwmMicroseconds(µseconds); return microseconds; } void HAL_SetPWMOutputPeriod(HAL_DigitalHandle pwmPortHandle, int32_t period, int32_t* status) { auto port = smartIoHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM); if (port == nullptr) { *status = HAL_HANDLE_ERROR; return; } switch (period) { case 0: *status = port->SetPwmOutputPeriod(wpi::hal::PwmOutputPeriod::k5ms); break; case 1: case 2: *status = port->SetPwmOutputPeriod(wpi::hal::PwmOutputPeriod::k10ms); break; case 3: *status = port->SetPwmOutputPeriod(wpi::hal::PwmOutputPeriod::k20ms); break; default: *status = HAL_PARAMETER_OUT_OF_RANGE; return; } } } // extern "C"