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