blob: 74a323a46d55fe71d9d8303c8697caa906aefb32 [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 "hal/AddressableLED.h"
6
7#include <cstring>
8
9#include <FRC_FPGA_ChipObject/fpgainterfacecapi/NiFpga_HMB.h>
Austin Schuh812d0d12021-11-04 20:16:48 -070010#include <fmt/format.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080011
12#include "ConstantsInternal.h"
13#include "DigitalInternal.h"
14#include "HALInitializer.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070015#include "HALInternal.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080016#include "PortsInternal.h"
Austin Schuh1e69f942020-11-14 15:06:14 -080017#include "hal/AddressableLEDTypes.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080018#include "hal/ChipObject.h"
19#include "hal/handles/HandlesInternal.h"
20#include "hal/handles/LimitedHandleResource.h"
21
22using namespace hal;
23
24namespace {
25struct AddressableLED {
26 std::unique_ptr<tLED> led;
27 void* ledBuffer;
28 size_t ledBufferSize;
29 int32_t stringLength = 1;
30};
31} // namespace
32
33static LimitedHandleResource<
34 HAL_AddressableLEDHandle, AddressableLED, kNumAddressableLEDs,
35 HAL_HandleEnum::AddressableLED>* addressableLEDHandles;
36
Austin Schuh812d0d12021-11-04 20:16:48 -070037namespace hal::init {
Brian Silverman8fce7482020-01-05 13:18:21 -080038void InitializeAddressableLED() {
39 static LimitedHandleResource<HAL_AddressableLEDHandle, AddressableLED,
40 kNumAddressableLEDs,
41 HAL_HandleEnum::AddressableLED>
42 alH;
43 addressableLEDHandles = &alH;
44}
Austin Schuh812d0d12021-11-04 20:16:48 -070045} // namespace hal::init
Brian Silverman8fce7482020-01-05 13:18:21 -080046
47extern "C" {
48
49HAL_AddressableLEDHandle HAL_InitializeAddressableLED(
50 HAL_DigitalHandle outputPort, int32_t* status) {
51 hal::init::CheckInit();
52
53 auto digitalPort =
54 hal::digitalChannelHandles->Get(outputPort, hal::HAL_HandleEnum::PWM);
55
56 if (!digitalPort) {
57 // If DIO was passed, channel error, else generic error
58 if (getHandleType(outputPort) == hal::HAL_HandleEnum::DIO) {
59 *status = HAL_LED_CHANNEL_ERROR;
60 } else {
61 *status = HAL_HANDLE_ERROR;
62 }
63 return HAL_kInvalidHandle;
64 }
65
66 if (digitalPort->channel >= kNumPWMHeaders) {
67 *status = HAL_LED_CHANNEL_ERROR;
68 return HAL_kInvalidHandle;
69 }
70
71 auto handle = addressableLEDHandles->Allocate();
72
73 if (handle == HAL_kInvalidHandle) {
74 *status = NO_AVAILABLE_RESOURCES;
75 return HAL_kInvalidHandle;
76 }
77
78 auto led = addressableLEDHandles->Get(handle);
79
80 if (!led) {
81 *status = HAL_HANDLE_ERROR;
82 return HAL_kInvalidHandle;
83 }
84
85 led->led.reset(tLED::create(status));
86
87 if (*status != 0) {
88 addressableLEDHandles->Free(handle);
89 return HAL_kInvalidHandle;
90 }
91
92 led->led->writeOutputSelect(digitalPort->channel, status);
93
94 if (*status != 0) {
95 addressableLEDHandles->Free(handle);
96 return HAL_kInvalidHandle;
97 }
98
99 led->ledBuffer = nullptr;
100 led->ledBufferSize = 0;
101
102 uint32_t session = led->led->getSystemInterface()->getHandle();
103
104 *status = NiFpga_OpenHostMemoryBuffer(session, "HMB_0_LED", &led->ledBuffer,
105 &led->ledBufferSize);
106
107 if (*status != 0) {
108 addressableLEDHandles->Free(handle);
109 return HAL_kInvalidHandle;
110 }
111
112 return handle;
113}
114
115void HAL_FreeAddressableLED(HAL_AddressableLEDHandle handle) {
116 addressableLEDHandles->Free(handle);
117}
118
119void HAL_SetAddressableLEDOutputPort(HAL_AddressableLEDHandle handle,
120 HAL_DigitalHandle outputPort,
121 int32_t* status) {
122 auto digitalPort =
123 hal::digitalChannelHandles->Get(outputPort, hal::HAL_HandleEnum::PWM);
124
125 if (!digitalPort) {
126 *status = HAL_HANDLE_ERROR;
127 return;
128 }
129
130 auto led = addressableLEDHandles->Get(handle);
131 if (!led) {
132 *status = HAL_HANDLE_ERROR;
133 return;
134 }
135
136 led->led->writeOutputSelect(digitalPort->channel, status);
137}
138
139void HAL_SetAddressableLEDLength(HAL_AddressableLEDHandle handle,
140 int32_t length, int32_t* status) {
141 auto led = addressableLEDHandles->Get(handle);
142 if (!led) {
143 *status = HAL_HANDLE_ERROR;
144 return;
145 }
146
Austin Schuh812d0d12021-11-04 20:16:48 -0700147 if (length > HAL_kAddressableLEDMaxLength || length < 0) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800148 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700149 hal::SetLastError(
150 status,
151 fmt::format(
152 "LED length must be less than or equal to {}. {} was requested",
153 HAL_kAddressableLEDMaxLength, length));
Brian Silverman8fce7482020-01-05 13:18:21 -0800154 return;
155 }
156
157 led->led->strobeReset(status);
158
159 while (led->led->readPixelWriteIndex(status) != 0) {
160 }
161
162 if (*status != 0) {
163 return;
164 }
165
166 led->led->writeStringLength(length, status);
167
168 led->stringLength = length;
169}
170
171static_assert(sizeof(HAL_AddressableLEDData) == sizeof(uint32_t),
172 "LED Data must be 32 bit");
173
174void HAL_WriteAddressableLEDData(HAL_AddressableLEDHandle handle,
175 const struct HAL_AddressableLEDData* data,
176 int32_t length, int32_t* status) {
177 auto led = addressableLEDHandles->Get(handle);
178 if (!led) {
179 *status = HAL_HANDLE_ERROR;
180 return;
181 }
182
Austin Schuh812d0d12021-11-04 20:16:48 -0700183 if (length > led->stringLength || length < 0) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800184 *status = PARAMETER_OUT_OF_RANGE;
Austin Schuh812d0d12021-11-04 20:16:48 -0700185 hal::SetLastError(
186 status,
187 fmt::format(
188 "Data length must be less than or equal to {}. {} was requested",
189 led->stringLength, length));
Brian Silverman8fce7482020-01-05 13:18:21 -0800190 return;
191 }
192
193 std::memcpy(led->ledBuffer, data, length * sizeof(HAL_AddressableLEDData));
194
195 asm("dmb");
196
197 led->led->strobeLoad(status);
198}
199
200void HAL_SetAddressableLEDBitTiming(HAL_AddressableLEDHandle handle,
201 int32_t lowTime0NanoSeconds,
202 int32_t highTime0NanoSeconds,
203 int32_t lowTime1NanoSeconds,
204 int32_t highTime1NanoSeconds,
205 int32_t* status) {
206 auto led = addressableLEDHandles->Get(handle);
207 if (!led) {
208 *status = HAL_HANDLE_ERROR;
209 return;
210 }
211
212 led->led->writeLowBitTickTiming(1, highTime0NanoSeconds / 25, status);
213 led->led->writeLowBitTickTiming(0, lowTime0NanoSeconds / 25, status);
214 led->led->writeHighBitTickTiming(1, highTime1NanoSeconds / 25, status);
215 led->led->writeHighBitTickTiming(0, lowTime1NanoSeconds / 25, status);
216}
217
218void HAL_SetAddressableLEDSyncTime(HAL_AddressableLEDHandle handle,
219 int32_t syncTimeMicroSeconds,
220 int32_t* status) {
221 auto led = addressableLEDHandles->Get(handle);
222 if (!led) {
223 *status = HAL_HANDLE_ERROR;
224 return;
225 }
226
227 led->led->writeSyncTiming(syncTimeMicroSeconds, status);
228}
229
230void HAL_StartAddressableLEDOutput(HAL_AddressableLEDHandle handle,
231 int32_t* status) {
232 auto led = addressableLEDHandles->Get(handle);
233 if (!led) {
234 *status = HAL_HANDLE_ERROR;
235 return;
236 }
237
238 led->led->strobeStart(status);
239}
240
241void HAL_StopAddressableLEDOutput(HAL_AddressableLEDHandle handle,
242 int32_t* status) {
243 auto led = addressableLEDHandles->Get(handle);
244 if (!led) {
245 *status = HAL_HANDLE_ERROR;
246 return;
247 }
248
249 led->led->strobeAbort(status);
250}
251} // extern "C"