blob: fb0cf980835fb71b044dee9392aa13b120730f3b [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);
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800115 pwmSystem->writeConfig_MinHigh(0, status);
Brian Silverman8fce7482020-01-05 13:18:21 -0800116 // Ensure that PWM output values are set to OFF
117 for (uint8_t pwmIndex = 0; pwmIndex < kNumPWMChannels; pwmIndex++) {
118 // Copy of SetPWM
119 if (pwmIndex < tPWM::kNumHdrRegisters) {
120 pwmSystem->writeHdr(pwmIndex, kPwmDisabled, status);
121 } else {
122 pwmSystem->writeMXP(pwmIndex - tPWM::kNumHdrRegisters, kPwmDisabled,
123 status);
124 }
125
126 // Copy of SetPWMPeriodScale, set to 4x by default.
127 if (pwmIndex < tPWM::kNumPeriodScaleHdrElements) {
128 pwmSystem->writePeriodScaleHdr(pwmIndex, 3, status);
129 } else {
130 pwmSystem->writePeriodScaleMXP(
131 pwmIndex - tPWM::kNumPeriodScaleHdrElements, 3, status);
132 }
133 }
134
135 // SPI setup
136 spiSystem.reset(tSPI::create(status));
137
138 initialized = true;
139}
140
141bool remapDigitalSource(HAL_Handle digitalSourceHandle,
142 HAL_AnalogTriggerType analogTriggerType,
143 uint8_t& channel, uint8_t& module,
144 bool& analogTrigger) {
145 if (isHandleType(digitalSourceHandle, HAL_HandleEnum::AnalogTrigger)) {
146 // If handle passed, index is not negative
147 int32_t index = getHandleIndex(digitalSourceHandle);
148 channel = (index << 2) + analogTriggerType;
149 module = channel >> 4;
150 analogTrigger = true;
151 return true;
152 } else if (isHandleType(digitalSourceHandle, HAL_HandleEnum::DIO)) {
153 int32_t index = getHandleIndex(digitalSourceHandle);
154 if (index >= kNumDigitalHeaders + kNumDigitalMXPChannels) {
155 // channels 10-15, so need to add headers to remap index
156 channel = remapSPIChannel(index) + kNumDigitalHeaders;
157 module = 0;
158 } else if (index >= kNumDigitalHeaders) {
159 channel = remapMXPChannel(index);
160 module = 1;
161 } else {
162 channel = index;
163 module = 0;
164 }
165 analogTrigger = false;
166 return true;
Austin Schuh812d0d12021-11-04 20:16:48 -0700167 } else if (isHandleType(digitalSourceHandle, HAL_HandleEnum::PWM)) {
168 // PWM's on MXP port are supported as a digital source
169 int32_t index = getHandleIndex(digitalSourceHandle);
170 if (index >= kNumPWMHeaders) {
171 channel = remapMXPPWMChannel(index);
172 module = 1;
173 analogTrigger = false;
174 return true;
175 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800176 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700177 return false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800178}
179
Austin Schuh812d0d12021-11-04 20:16:48 -0700180int32_t remapMXPChannel(int32_t channel) {
181 return channel - 10;
182}
Brian Silverman8fce7482020-01-05 13:18:21 -0800183
184int32_t remapMXPPWMChannel(int32_t channel) {
185 if (channel < 14) {
186 return channel - 10; // first block of 4 pwms (MXP 0-3)
187 } else {
188 return channel - 6; // block of PWMs after SPI
189 }
190}
191
Austin Schuh812d0d12021-11-04 20:16:48 -0700192int32_t remapSPIChannel(int32_t channel) {
193 return channel - 26;
194}
Brian Silverman8fce7482020-01-05 13:18:21 -0800195
196} // namespace hal
197
198// Unused function here to test template compile.
199__attribute__((unused)) static void CompileFunctorTest() {
200 hal::UnsafeManipulateDIO(0, nullptr, [](hal::DIOSetProxy& proxy) {});
201}