blob: b9339ee3f77f6b6c6d0206908137a3e2c955000c [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.
James Kuszmaulcf324122023-01-14 14:07:17 -0800101 while (pwmSystem->readLoopTiming(status) == 0) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700102 std::this_thread::yield();
James Kuszmaulcf324122023-01-14 14:07:17 -0800103 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800104
105 if (pwmSystem->readLoopTiming(status) != kExpectedLoopTiming) {
106 *status = LOOP_TIMING_ERROR; // NOTE: Doesn't display the error
107 }
108
109 // Calculate the length, in ms, of one DIO loop
110 double loopTime = pwmSystem->readLoopTiming(status) /
111 (kSystemClockTicksPerMicrosecond * 1e3);
112
Austin Schuh812d0d12021-11-04 20:16:48 -0700113 pwmSystem->writeConfig_Period(std::lround(kDefaultPwmPeriod / loopTime),
114 status);
115 uint16_t minHigh = std::lround(
116 (kDefaultPwmCenter - kDefaultPwmStepsDown * loopTime) / loopTime);
Brian Silverman8fce7482020-01-05 13:18:21 -0800117 pwmSystem->writeConfig_MinHigh(minHigh, status);
118 // Ensure that PWM output values are set to OFF
119 for (uint8_t pwmIndex = 0; pwmIndex < kNumPWMChannels; pwmIndex++) {
120 // Copy of SetPWM
121 if (pwmIndex < tPWM::kNumHdrRegisters) {
122 pwmSystem->writeHdr(pwmIndex, kPwmDisabled, status);
123 } else {
124 pwmSystem->writeMXP(pwmIndex - tPWM::kNumHdrRegisters, kPwmDisabled,
125 status);
126 }
127
128 // Copy of SetPWMPeriodScale, set to 4x by default.
129 if (pwmIndex < tPWM::kNumPeriodScaleHdrElements) {
130 pwmSystem->writePeriodScaleHdr(pwmIndex, 3, status);
131 } else {
132 pwmSystem->writePeriodScaleMXP(
133 pwmIndex - tPWM::kNumPeriodScaleHdrElements, 3, status);
134 }
135 }
136
137 // SPI setup
138 spiSystem.reset(tSPI::create(status));
139
140 initialized = true;
141}
142
143bool remapDigitalSource(HAL_Handle digitalSourceHandle,
144 HAL_AnalogTriggerType analogTriggerType,
145 uint8_t& channel, uint8_t& module,
146 bool& analogTrigger) {
147 if (isHandleType(digitalSourceHandle, HAL_HandleEnum::AnalogTrigger)) {
148 // If handle passed, index is not negative
149 int32_t index = getHandleIndex(digitalSourceHandle);
150 channel = (index << 2) + analogTriggerType;
151 module = channel >> 4;
152 analogTrigger = true;
153 return true;
154 } else if (isHandleType(digitalSourceHandle, HAL_HandleEnum::DIO)) {
155 int32_t index = getHandleIndex(digitalSourceHandle);
156 if (index >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
157 // channels 10-15, so need to add headers to remap index
158 channel = remapSPIChannel(index) + kNumDigitalHeaders;
159 module = 0;
160 } else if (index >= kNumDigitalHeaders) {
161 channel = remapMXPChannel(index);
162 module = 1;
163 } else {
164 channel = index;
165 module = 0;
166 }
167 analogTrigger = false;
168 return true;
Austin Schuh812d0d12021-11-04 20:16:48 -0700169 } else if (isHandleType(digitalSourceHandle, HAL_HandleEnum::PWM)) {
170 // PWM's on MXP port are supported as a digital source
171 int32_t index = getHandleIndex(digitalSourceHandle);
172 if (index >= kNumPWMHeaders) {
173 channel = remapMXPPWMChannel(index);
174 module = 1;
175 analogTrigger = false;
176 return true;
177 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800178 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700179 return false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800180}
181
Austin Schuh812d0d12021-11-04 20:16:48 -0700182int32_t remapMXPChannel(int32_t channel) {
183 return channel - 10;
184}
Brian Silverman8fce7482020-01-05 13:18:21 -0800185
186int32_t remapMXPPWMChannel(int32_t channel) {
187 if (channel < 14) {
188 return channel - 10; // first block of 4 pwms (MXP 0-3)
189 } else {
190 return channel - 6; // block of PWMs after SPI
191 }
192}
193
Austin Schuh812d0d12021-11-04 20:16:48 -0700194int32_t remapSPIChannel(int32_t channel) {
195 return channel - 26;
196}
Brian Silverman8fce7482020-01-05 13:18:21 -0800197
198} // namespace hal
199
200// Unused function here to test template compile.
201__attribute__((unused)) static void CompileFunctorTest() {
202 hal::UnsafeManipulateDIO(0, nullptr, [](hal::DIOSetProxy& proxy) {});
203}