blob: 9ecc4c4b8465641ac30954ef8c3e9966a4268984 [file] [log] [blame]
James Kuszmaul4b81d302019-12-14 20:53:14 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) 2019 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 "hal/AddressableLED.h"
9
10#include <cstring>
11
12#include "ConstantsInternal.h"
13#include "DigitalInternal.h"
14#include "HALInitializer.h"
15#include "PortsInternal.h"
16#include "hal/ChipObject.h"
17#include "hal/handles/HandlesInternal.h"
18#include "hal/handles/LimitedHandleResource.h"
19
20using namespace hal;
21
22extern "C" {
23NiFpga_Status NiFpga_ClientFunctionCall(NiFpga_Session session, uint32_t group,
24 uint32_t functionId,
25 const void* inBuffer,
26 size_t inBufferSize, void* outBuffer,
27 size_t outBufferSize);
28} // extern "C"
29
30namespace {
31struct AddressableLED {
32 std::unique_ptr<tLED> led;
33 void* ledBuffer;
34 size_t ledBufferSize;
35 int32_t stringLength = 1;
36};
37} // namespace
38
39static LimitedHandleResource<
40 HAL_AddressableLEDHandle, AddressableLED, kNumAddressableLEDs,
41 HAL_HandleEnum::AddressableLED>* addressableLEDHandles;
42
43namespace hal {
44namespace init {
45void InitializeAddressableLED() {
46 static LimitedHandleResource<HAL_AddressableLEDHandle, AddressableLED,
47 kNumAddressableLEDs,
48 HAL_HandleEnum::AddressableLED>
49 alH;
50 addressableLEDHandles = &alH;
51}
52} // namespace init
53} // namespace hal
54
55// Shim for broken ChipObject function
56static const uint32_t clientFeature_hostMemoryBuffer = 0;
57static const uint32_t hostMemoryBufferFunction_open = 2;
58
59// Input arguments for HMB open
60struct AtomicHMBOpenInputs {
61 const char* memoryName;
62};
63
64// Output arguments for HMB open
65struct AtomicHMBOpenOutputs {
66 size_t size;
67 void* virtualAddress;
68};
69
70static NiFpga_Status OpenHostMemoryBuffer(NiFpga_Session session,
71 const char* memoryName,
72 void** virtualAddress, size_t* size) {
73 struct AtomicHMBOpenOutputs outputs;
74
75 struct AtomicHMBOpenInputs inputs;
76 inputs.memoryName = memoryName;
77
78 NiFpga_Status retval = NiFpga_ClientFunctionCall(
79 session, clientFeature_hostMemoryBuffer, hostMemoryBufferFunction_open,
80 &inputs, sizeof(struct AtomicHMBOpenInputs), &outputs,
81 sizeof(struct AtomicHMBOpenOutputs));
82 if (NiFpga_IsError(retval)) {
83 return retval;
84 }
85 *virtualAddress = outputs.virtualAddress;
86 if (size != NULL) {
87 *size = outputs.size;
88 }
89 return retval;
90}
91
92extern "C" {
93
94HAL_AddressableLEDHandle HAL_InitializeAddressableLED(
95 HAL_DigitalHandle outputPort, int32_t* status) {
96 hal::init::CheckInit();
97
98 auto digitalPort =
99 hal::digitalChannelHandles->Get(outputPort, hal::HAL_HandleEnum::PWM);
100
101 if (!digitalPort) {
102 // If DIO was passed, channel error, else generic error
103 if (getHandleType(outputPort) == hal::HAL_HandleEnum::DIO) {
104 *status = HAL_LED_CHANNEL_ERROR;
105 } else {
106 *status = HAL_HANDLE_ERROR;
107 }
108 return HAL_kInvalidHandle;
109 }
110
111 if (digitalPort->channel >= kNumPWMHeaders) {
112 *status = HAL_LED_CHANNEL_ERROR;
113 return HAL_kInvalidHandle;
114 }
115
116 auto handle = addressableLEDHandles->Allocate();
117
118 if (handle == HAL_kInvalidHandle) {
119 *status = NO_AVAILABLE_RESOURCES;
120 return HAL_kInvalidHandle;
121 }
122
123 auto led = addressableLEDHandles->Get(handle);
124
125 if (!led) {
126 *status = HAL_HANDLE_ERROR;
127 return HAL_kInvalidHandle;
128 }
129
130 led->led.reset(tLED::create(status));
131
132 if (*status != 0) {
133 addressableLEDHandles->Free(handle);
134 return HAL_kInvalidHandle;
135 }
136
137 led->led->writeOutputSelect(digitalPort->channel, status);
138
139 if (*status != 0) {
140 addressableLEDHandles->Free(handle);
141 return HAL_kInvalidHandle;
142 }
143
144 led->ledBuffer = nullptr;
145 led->ledBufferSize = 0;
146
147 uint32_t session = led->led->getSystemInterface()->getHandle();
148
149 *status = OpenHostMemoryBuffer(session, "HMB_0_LED", &led->ledBuffer,
150 &led->ledBufferSize);
151
152 if (*status != 0) {
153 addressableLEDHandles->Free(handle);
154 return HAL_kInvalidHandle;
155 }
156
157 return handle;
158}
159
160void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle) {
161 addressableLEDHandles->Free(handle);
162}
163
164void HAL_SetAddressableLEDOutputPort(HAL_AddressableLEDHandle handle,
165 HAL_DigitalHandle outputPort,
166 int32_t* status) {
167 auto digitalPort =
168 hal::digitalChannelHandles->Get(outputPort, hal::HAL_HandleEnum::PWM);
169
170 if (!digitalPort) {
171 *status = HAL_HANDLE_ERROR;
172 return;
173 }
174
175 auto led = addressableLEDHandles->Get(handle);
176 if (!led) {
177 *status = HAL_HANDLE_ERROR;
178 return;
179 }
180
181 led->led->writeOutputSelect(digitalPort->channel, status);
182}
183
184void HAL_SetAddressableLEDLength(HAL_AddressableLEDHandle handle,
185 int32_t length, int32_t* status) {
186 auto led = addressableLEDHandles->Get(handle);
187 if (!led) {
188 *status = HAL_HANDLE_ERROR;
189 return;
190 }
191
192 if (length > HAL_kAddressableLEDMaxLength) {
193 *status = PARAMETER_OUT_OF_RANGE;
194 return;
195 }
196
197 led->led->strobeReset(status);
198
199 while (led->led->readPixelWriteIndex(status) != 0) {
200 }
201
202 if (*status != 0) {
203 return;
204 }
205
206 led->led->writeStringLength(length, status);
207
208 led->stringLength = length;
209}
210
211static_assert(sizeof(HAL_AddressableLEDData) == sizeof(uint32_t),
212 "LED Data must be 32 bit");
213
214void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle,
215 const struct HAL_AddressableLEDData* data,
216 int32_t length, int32_t* status) {
217 auto led = addressableLEDHandles->Get(handle);
218 if (!led) {
219 *status = HAL_HANDLE_ERROR;
220 return;
221 }
222
223 if (length > led->stringLength) {
224 *status = PARAMETER_OUT_OF_RANGE;
225 return;
226 }
227
228 std::memcpy(led->ledBuffer, data, length * sizeof(HAL_AddressableLEDData));
229
230 asm("dmb");
231
232 led->led->strobeLoad(status);
233}
234
235void HAL_SetAddressableLEDBitTiming(HAL_AddressableLEDHandle handle,
236 int32_t lowTime0NanoSeconds,
237 int32_t highTime0NanoSeconds,
238 int32_t lowTime1NanoSeconds,
239 int32_t highTime1NanoSeconds,
240 int32_t* status) {
241 auto led = addressableLEDHandles->Get(handle);
242 if (!led) {
243 *status = HAL_HANDLE_ERROR;
244 return;
245 }
246
247 led->led->writeLowBitTickTiming(1, highTime0NanoSeconds / 25, status);
248 led->led->writeLowBitTickTiming(0, lowTime0NanoSeconds / 25, status);
249 led->led->writeHighBitTickTiming(1, highTime1NanoSeconds / 25, status);
250 led->led->writeHighBitTickTiming(0, lowTime1NanoSeconds / 25, status);
251}
252
253void HAL_SetAddressableLEDSyncTime(HAL_AddressableLEDHandle handle,
254 int32_t syncTimeMicroSeconds,
255 int32_t* status) {
256 auto led = addressableLEDHandles->Get(handle);
257 if (!led) {
258 *status = HAL_HANDLE_ERROR;
259 return;
260 }
261
262 led->led->writeSyncTiming(syncTimeMicroSeconds, status);
263}
264
265void HAL_StartAddressableLEDOutput(HAL_AddressableLEDHandle handle,
266 int32_t* status) {
267 auto led = addressableLEDHandles->Get(handle);
268 if (!led) {
269 *status = HAL_HANDLE_ERROR;
270 return;
271 }
272
273 led->led->strobeStart(status);
274}
275
276void HAL_StopAddressableLEDOutput(HAL_AddressableLEDHandle handle,
277 int32_t* status) {
278 auto led = addressableLEDHandles->Get(handle);
279 if (!led) {
280 *status = HAL_HANDLE_ERROR;
281 return;
282 }
283
284 led->led->strobeAbort(status);
285}
286} // extern "C"