blob: 01e6c16f74367dde08b50ec1ff5593b9b3b4c91b [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2016-2017. 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 <mutex>
12#include <thread>
13
14#include "ConstantsInternal.h"
15#include "FRC_NetworkCommunication/LoadOut.h"
16#include "HAL/AnalogTrigger.h"
17#include "HAL/ChipObject.h"
18#include "HAL/HAL.h"
19#include "HAL/Ports.h"
20#include "HAL/cpp/priority_mutex.h"
21#include "PortsInternal.h"
22
23namespace hal {
24// Create a mutex to protect changes to the DO PWM config
25priority_recursive_mutex digitalPwmMutex;
26
27std::unique_ptr<tDIO> digitalSystem;
28std::unique_ptr<tRelay> relaySystem;
29std::unique_ptr<tPWM> pwmSystem;
30std::unique_ptr<tSPI> spiSystem;
31
32static std::atomic<bool> digitalSystemsInitialized{false};
33static priority_mutex initializeMutex;
34
35DigitalHandleResource<HAL_DigitalHandle, DigitalPort,
36 kNumDigitalChannels + kNumPWMHeaders>
37 digitalChannelHandles;
38
39/**
40 * Initialize the digital system.
41 */
42void initializeDigital(int32_t* status) {
43 // Initial check, as if it's true initialization has finished
44 if (digitalSystemsInitialized) return;
45
46 std::lock_guard<priority_mutex> lock(initializeMutex);
47 // Second check in case another thread was waiting
48 if (digitalSystemsInitialized) return;
49
50 digitalSystem.reset(tDIO::create(status));
51
52 // Relay Setup
53 relaySystem.reset(tRelay::create(status));
54
55 // Turn off all relay outputs.
56 relaySystem->writeValue_Forward(0, status);
57 relaySystem->writeValue_Reverse(0, status);
58
59 // PWM Setup
60 pwmSystem.reset(tPWM::create(status));
61
62 // Make sure that the 9403 IONode has had a chance to initialize before
63 // continuing.
64 while (pwmSystem->readLoopTiming(status) == 0) std::this_thread::yield();
65
66 if (pwmSystem->readLoopTiming(status) != kExpectedLoopTiming) {
67 *status = LOOP_TIMING_ERROR; // NOTE: Doesn't display the error
68 }
69
70 // Calculate the length, in ms, of one DIO loop
71 double loopTime = pwmSystem->readLoopTiming(status) /
72 (kSystemClockTicksPerMicrosecond * 1e3);
73
74 pwmSystem->writeConfig_Period(
75 static_cast<uint16_t>(kDefaultPwmPeriod / loopTime + .5), status);
76 uint16_t minHigh = static_cast<uint16_t>(
77 (kDefaultPwmCenter - kDefaultPwmStepsDown * loopTime) / loopTime + .5);
78 pwmSystem->writeConfig_MinHigh(minHigh, status);
79 // Ensure that PWM output values are set to OFF
80 for (uint8_t pwmIndex = 0; pwmIndex < kNumPWMChannels; pwmIndex++) {
81 // Copy of SetPWM
82 if (pwmIndex < tPWM::kNumHdrRegisters) {
83 pwmSystem->writeHdr(pwmIndex, kPwmDisabled, status);
84 } else {
85 pwmSystem->writeMXP(pwmIndex - tPWM::kNumHdrRegisters, kPwmDisabled,
86 status);
87 }
88
89 // Copy of SetPWMPeriodScale, set to 4x by default.
90 if (pwmIndex < tPWM::kNumPeriodScaleHdrElements) {
91 pwmSystem->writePeriodScaleHdr(pwmIndex, 3, status);
92 } else {
93 pwmSystem->writePeriodScaleMXP(
94 pwmIndex - tPWM::kNumPeriodScaleHdrElements, 3, status);
95 }
96 }
97
98 // SPI setup
99 spiSystem.reset(tSPI::create(status));
100
101 digitalSystemsInitialized = true;
102}
103
104/**
105 * Map SPI channel numbers from their physical number (27 to 31) to their
106 * position in the bit field.
107 */
108int32_t remapSPIChannel(int32_t channel) { return channel - 26; }
109
110/**
111 * Map DIO channel numbers from their physical number (10 to 26) to their
112 * position in the bit field.
113 */
114int32_t remapMXPChannel(int32_t channel) { return channel - 10; }
115
116int32_t remapMXPPWMChannel(int32_t channel) {
117 if (channel < 14) {
118 return channel - 10; // first block of 4 pwms (MXP 0-3)
119 } else {
120 return channel - 6; // block of PWMs after SPI
121 }
122}
123
124/**
125 * remap the digital source channel and set the module.
126 * If it's an analog trigger, determine the module from the high order routing
127 * channel else do normal digital input remapping based on channel number
128 * (MXP)
129 */
130bool remapDigitalSource(HAL_Handle digitalSourceHandle,
131 HAL_AnalogTriggerType analogTriggerType,
132 uint8_t& channel, uint8_t& module,
133 bool& analogTrigger) {
134 if (isHandleType(digitalSourceHandle, HAL_HandleEnum::AnalogTrigger)) {
135 // If handle passed, index is not negative
136 int32_t index = getHandleIndex(digitalSourceHandle);
137 channel = (index << 2) + analogTriggerType;
138 module = channel >> 4;
139 analogTrigger = true;
140 return true;
141 } else if (isHandleType(digitalSourceHandle, HAL_HandleEnum::DIO)) {
142 int32_t index = getHandleIndex(digitalSourceHandle);
143 if (index > kNumDigitalHeaders + kNumDigitalMXPChannels) {
144 // channels 10-15, so need to add headers to remap index
145 channel = remapSPIChannel(index) + kNumDigitalHeaders;
146 module = 0;
147 } else if (index >= kNumDigitalHeaders) {
148 channel = remapMXPChannel(index);
149 module = 1;
150 } else {
151 channel = index;
152 module = 0;
153 }
154 analogTrigger = false;
155 return true;
156 } else {
157 return false;
158 }
159}
160} // namespace hal