blob: 63ea0aa25f3dd53d0ff6d4a9760c69084b5397ae [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
Brian Silverman8fce7482020-01-05 13:18:21 -08004
5#include "DigitalInternal.h"
6
7#include <atomic>
Austin Schuh812d0d12021-11-04 20:16:48 -07008#include <cmath>
Brian Silverman8fce7482020-01-05 13:18:21 -08009#include <thread>
10
11#include <FRC_NetworkCommunication/LoadOut.h>
12#include <wpi/mutex.h>
13
14#include "ConstantsInternal.h"
15#include "HALInitializer.h"
16#include "PortsInternal.h"
17#include "hal/AnalogTrigger.h"
18#include "hal/ChipObject.h"
19#include "hal/HAL.h"
20#include "hal/Ports.h"
21#include "hal/cpp/UnsafeDIO.h"
22
23namespace hal {
24
25std::unique_ptr<tDIO> digitalSystem;
26std::unique_ptr<tRelay> relaySystem;
27std::unique_ptr<tPWM> pwmSystem;
28std::unique_ptr<tSPI> spiSystem;
29
30// Create a mutex to protect changes to the digital output values
31wpi::mutex digitalDIOMutex;
32
33DigitalHandleResource<HAL_DigitalHandle, DigitalPort,
34 kNumDigitalChannels + kNumPWMHeaders>*
35 digitalChannelHandles;
36
37namespace init {
38void InitializeDigitalInternal() {
39 static DigitalHandleResource<HAL_DigitalHandle, DigitalPort,
40 kNumDigitalChannels + kNumPWMHeaders>
41 dcH;
42 digitalChannelHandles = &dcH;
43}
44} // namespace init
45
46namespace detail {
Austin Schuh812d0d12021-11-04 20:16:48 -070047wpi::mutex& UnsafeGetDIOMutex() {
48 return digitalDIOMutex;
49}
50tDIO* UnsafeGetDigialSystem() {
51 return digitalSystem.get();
52}
Brian Silverman8fce7482020-01-05 13:18:21 -080053int32_t ComputeDigitalMask(HAL_DigitalHandle handle, int32_t* status) {
54 auto port = digitalChannelHandles->Get(handle, HAL_HandleEnum::DIO);
55 if (port == nullptr) {
56 *status = HAL_HANDLE_ERROR;
57 return 0;
58 }
59 tDIO::tDO output;
60 output.value = 0;
61 if (port->channel >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
62 output.SPIPort = (1u << remapSPIChannel(port->channel));
63 } else if (port->channel < kNumDigitalHeaders) {
64 output.Headers = (1u << port->channel);
65 } else {
66 output.MXP = (1u << remapMXPChannel(port->channel));
67 }
68 return output.value;
69}
70} // namespace detail
71
72void initializeDigital(int32_t* status) {
73 hal::init::CheckInit();
74 static std::atomic_bool initialized{false};
75 static wpi::mutex initializeMutex;
76 // Initial check, as if it's true initialization has finished
Austin Schuh812d0d12021-11-04 20:16:48 -070077 if (initialized) {
78 return;
79 }
Brian Silverman8fce7482020-01-05 13:18:21 -080080
81 std::scoped_lock lock(initializeMutex);
82 // Second check in case another thread was waiting
Austin Schuh812d0d12021-11-04 20:16:48 -070083 if (initialized) {
84 return;
85 }
Brian Silverman8fce7482020-01-05 13:18:21 -080086
87 digitalSystem.reset(tDIO::create(status));
88
89 // Relay Setup
90 relaySystem.reset(tRelay::create(status));
91
92 // Turn off all relay outputs.
93 relaySystem->writeValue_Forward(0, status);
94 relaySystem->writeValue_Reverse(0, status);
95
96 // PWM Setup
97 pwmSystem.reset(tPWM::create(status));
98
99 // Make sure that the 9403 IONode has had a chance to initialize before
100 // continuing.
Austin Schuh812d0d12021-11-04 20:16:48 -0700101 while (pwmSystem->readLoopTiming(status) == 0)
102 std::this_thread::yield();
Brian Silverman8fce7482020-01-05 13:18:21 -0800103
104 if (pwmSystem->readLoopTiming(status) != kExpectedLoopTiming) {
105 *status = LOOP_TIMING_ERROR; // NOTE: Doesn't display the error
106 }
107
108 // Calculate the length, in ms, of one DIO loop
109 double loopTime = pwmSystem->readLoopTiming(status) /
110 (kSystemClockTicksPerMicrosecond * 1e3);
111
Austin Schuh812d0d12021-11-04 20:16:48 -0700112 pwmSystem->writeConfig_Period(std::lround(kDefaultPwmPeriod / loopTime),
113 status);
114 uint16_t minHigh = std::lround(
115 (kDefaultPwmCenter - kDefaultPwmStepsDown * loopTime) / loopTime);
Brian Silverman8fce7482020-01-05 13:18:21 -0800116 pwmSystem->writeConfig_MinHigh(minHigh, status);
117 // Ensure that PWM output values are set to OFF
118 for (uint8_t pwmIndex = 0; pwmIndex < kNumPWMChannels; pwmIndex++) {
119 // Copy of SetPWM
120 if (pwmIndex < tPWM::kNumHdrRegisters) {
121 pwmSystem->writeHdr(pwmIndex, kPwmDisabled, status);
122 } else {
123 pwmSystem->writeMXP(pwmIndex - tPWM::kNumHdrRegisters, kPwmDisabled,
124 status);
125 }
126
127 // Copy of SetPWMPeriodScale, set to 4x by default.
128 if (pwmIndex < tPWM::kNumPeriodScaleHdrElements) {
129 pwmSystem->writePeriodScaleHdr(pwmIndex, 3, status);
130 } else {
131 pwmSystem->writePeriodScaleMXP(
132 pwmIndex - tPWM::kNumPeriodScaleHdrElements, 3, status);
133 }
134 }
135
136 // SPI setup
137 spiSystem.reset(tSPI::create(status));
138
139 initialized = true;
140}
141
142bool remapDigitalSource(HAL_Handle digitalSourceHandle,
143 HAL_AnalogTriggerType analogTriggerType,
144 uint8_t& channel, uint8_t& module,
145 bool& analogTrigger) {
146 if (isHandleType(digitalSourceHandle, HAL_HandleEnum::AnalogTrigger)) {
147 // If handle passed, index is not negative
148 int32_t index = getHandleIndex(digitalSourceHandle);
149 channel = (index << 2) + analogTriggerType;
150 module = channel >> 4;
151 analogTrigger = true;
152 return true;
153 } else if (isHandleType(digitalSourceHandle, HAL_HandleEnum::DIO)) {
154 int32_t index = getHandleIndex(digitalSourceHandle);
155 if (index >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
156 // channels 10-15, so need to add headers to remap index
157 channel = remapSPIChannel(index) + kNumDigitalHeaders;
158 module = 0;
159 } else if (index >= kNumDigitalHeaders) {
160 channel = remapMXPChannel(index);
161 module = 1;
162 } else {
163 channel = index;
164 module = 0;
165 }
166 analogTrigger = false;
167 return true;
Austin Schuh812d0d12021-11-04 20:16:48 -0700168 } else if (isHandleType(digitalSourceHandle, HAL_HandleEnum::PWM)) {
169 // PWM's on MXP port are supported as a digital source
170 int32_t index = getHandleIndex(digitalSourceHandle);
171 if (index >= kNumPWMHeaders) {
172 channel = remapMXPPWMChannel(index);
173 module = 1;
174 analogTrigger = false;
175 return true;
176 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800177 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700178 return false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800179}
180
Austin Schuh812d0d12021-11-04 20:16:48 -0700181int32_t remapMXPChannel(int32_t channel) {
182 return channel - 10;
183}
Brian Silverman8fce7482020-01-05 13:18:21 -0800184
185int32_t remapMXPPWMChannel(int32_t channel) {
186 if (channel < 14) {
187 return channel - 10; // first block of 4 pwms (MXP 0-3)
188 } else {
189 return channel - 6; // block of PWMs after SPI
190 }
191}
192
Austin Schuh812d0d12021-11-04 20:16:48 -0700193int32_t remapSPIChannel(int32_t channel) {
194 return channel - 26;
195}
Brian Silverman8fce7482020-01-05 13:18:21 -0800196
197} // namespace hal
198
199// Unused function here to test template compile.
200__attribute__((unused)) static void CompileFunctorTest() {
201 hal::UnsafeManipulateDIO(0, nullptr, [](hal::DIOSetProxy& proxy) {});
202}