forked from rbmj/wpilib
-
Notifications
You must be signed in to change notification settings - Fork 0
/
DigitalOutput.cpp
316 lines (281 loc) · 9.08 KB
/
DigitalOutput.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2008. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
/*----------------------------------------------------------------------------*/
#include "DigitalOutput.h"
#include "DigitalModule.h"
#include "NetworkCommunication/UsageReporting.h"
#include "Resource.h"
#include "WPIErrors.h"
extern Resource *interruptsResource;
/**
* Create an instance of a DigitalOutput.
* Creates a digital output given a slot and channel. Common creation routine
* for all constructors.
*/
void DigitalOutput::InitDigitalOutput(uint8_t moduleNumber, uint32_t channel)
{
m_table = NULL;
char buf[64];
if (!CheckDigitalModule(moduleNumber))
{
snprintf(buf, 64, "Digital Module %d", moduleNumber);
wpi_setWPIErrorWithContext(ModuleIndexOutOfRange, buf);
return;
}
if (!CheckDigitalChannel(channel))
{
snprintf(buf, 64, "Digital Channel %lu", channel);
wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, buf);
return;
}
m_channel = channel;
m_pwmGenerator = ~0ul;
m_module = DigitalModule::GetInstance(moduleNumber);
m_module->AllocateDIO(m_channel, false);
nUsageReporting::report(nUsageReporting::kResourceType_DigitalOutput, channel, moduleNumber - 1);
}
/**
* Create an instance of a digital output.
* Create a digital output given a channel. The default module is used.
*
* @param channel The digital channel (1..14).
*/
DigitalOutput::DigitalOutput(uint32_t channel)
{
InitDigitalOutput(GetDefaultDigitalModule(), channel);
}
/**
* Create an instance of a digital output.
* Create an instance of a digital output given a module number and channel.
*
* @param moduleNumber The digital module (1 or 2).
* @param channel The digital channel (1..14).
*/
DigitalOutput::DigitalOutput(uint8_t moduleNumber, uint32_t channel)
{
InitDigitalOutput(moduleNumber, channel);
}
/**
* Free the resources associated with a digital output.
*/
DigitalOutput::~DigitalOutput()
{
if (StatusIsFatal()) return;
// Disable the PWM in case it was running.
DisablePWM();
m_module->FreeDIO(m_channel);
}
/**
* Set the value of a digital output.
* Set the value of a digital output to either one (true) or zero (false).
*/
void DigitalOutput::Set(uint32_t value)
{
if (StatusIsFatal()) return;
m_module->SetDIO(m_channel, value);
}
/**
* @return The GPIO channel number that this object represents.
*/
uint32_t DigitalOutput::GetChannel()
{
return m_channel;
}
/**
* Output a single pulse on the digital output line.
* Send a single pulse on the digital output line where the pulse diration is specified in seconds.
* Maximum pulse length is 0.0016 seconds.
* @param length The pulselength in seconds
*/
void DigitalOutput::Pulse(float length)
{
if (StatusIsFatal()) return;
m_module->Pulse(m_channel, length);
}
/**
* Determine if the pulse is still going.
* Determine if a previously started pulse is still going.
*/
bool DigitalOutput::IsPulsing()
{
if (StatusIsFatal()) return false;
return m_module->IsPulsing(m_channel);
}
/**
* Change the PWM frequency of the PWM output on a Digital Output line.
*
* The valid range is from 0.6 Hz to 19 kHz. The frequency resolution is logarithmic.
*
* There is only one PWM frequency per digital module.
*
* @param rate The frequency to output all digital output PWM signals on this module.
*/
void DigitalOutput::SetPWMRate(float rate)
{
if (StatusIsFatal()) return;
m_module->SetDO_PWMRate(rate);
}
/**
* Enable a PWM Output on this line.
*
* Allocate one of the 4 DO PWM generator resources from this module.
*
* Supply the initial duty-cycle to output so as to avoid a glitch when first starting.
*
* The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less)
* but is reduced the higher the frequency of the PWM signal is.
*
* @param initialDutyCycle The duty-cycle to start generating. [0..1]
*/
void DigitalOutput::EnablePWM(float initialDutyCycle)
{
if (StatusIsFatal()) return;
if (m_pwmGenerator != ~0ul) return;
m_pwmGenerator = m_module->AllocateDO_PWM();
m_module->SetDO_PWMDutyCycle(m_pwmGenerator, initialDutyCycle);
m_module->SetDO_PWMOutputChannel(m_pwmGenerator, m_channel);
}
/**
* Change this line from a PWM output back to a static Digital Output line.
*
* Free up one of the 4 DO PWM generator resources that were in use.
*/
void DigitalOutput::DisablePWM()
{
if (StatusIsFatal()) return;
// Disable the output by routing to a dead bit.
m_module->SetDO_PWMOutputChannel(m_pwmGenerator, kDigitalChannels);
m_module->FreeDO_PWM(m_pwmGenerator);
m_pwmGenerator = ~0ul;
}
/**
* Change the duty-cycle that is being generated on the line.
*
* The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less)
* but is reduced the higher the frequency of the PWM signal is.
*
* @param dutyCycle The duty-cycle to change to. [0..1]
*/
void DigitalOutput::UpdateDutyCycle(float dutyCycle)
{
if (StatusIsFatal()) return;
m_module->SetDO_PWMDutyCycle(m_pwmGenerator, dutyCycle);
}
/**
* @return The value to be written to the channel field of a routing mux.
*/
uint32_t DigitalOutput::GetChannelForRouting()
{
return DigitalModule::RemapDigitalChannel(GetChannel() - 1);
}
/**
* @return The value to be written to the module field of a routing mux.
*/
uint32_t DigitalOutput::GetModuleForRouting()
{
if (StatusIsFatal()) return 0;
return m_module->GetNumber() - 1;
}
/**
* @return The value to be written to the analog trigger field of a routing mux.
*/
bool DigitalOutput::GetAnalogTriggerForRouting()
{
return false;
}
/**
* Request interrupts asynchronously on this digital output.
* @param handler The address of the interrupt handler function of type tInterruptHandler that
* will be called whenever there is an interrupt on the digitial output port.
* Request interrupts in synchronus mode where the user program interrupt handler will be
* called when an interrupt occurs.
* The default is interrupt on rising edges only.
*/
void DigitalOutput::RequestInterrupts(tInterruptHandler handler, void *param)
{
if (StatusIsFatal()) return;
uint32_t index = interruptsResource->Allocate("Sync Interrupt");
if (index == ~0ul)
{
CloneError(interruptsResource);
return;
}
m_interruptIndex = index;
// Creates a manager too
AllocateInterrupts(false);
tRioStatusCode localStatus = NiFpga_Status_Success;
m_interrupt->writeConfig_WaitForAck(false, &localStatus);
m_interrupt->writeConfig_Source_AnalogTrigger(GetAnalogTriggerForRouting(), &localStatus);
m_interrupt->writeConfig_Source_Channel(GetChannelForRouting(), &localStatus);
m_interrupt->writeConfig_Source_Module(GetModuleForRouting(), &localStatus);
SetUpSourceEdge(true, false);
m_manager->registerHandler(handler, param, &localStatus);
wpi_setError(localStatus);
}
/**
* Request interrupts synchronously on this digital output.
* Request interrupts in synchronus mode where the user program will have to explicitly
* wait for the interrupt to occur.
* The default is interrupt on rising edges only.
*/
void DigitalOutput::RequestInterrupts()
{
if (StatusIsFatal()) return;
uint32_t index = interruptsResource->Allocate("Sync Interrupt");
if (index == ~0ul)
{
CloneError(interruptsResource);
return;
}
m_interruptIndex = index;
AllocateInterrupts(true);
tRioStatusCode localStatus = NiFpga_Status_Success;
m_interrupt->writeConfig_Source_AnalogTrigger(GetAnalogTriggerForRouting(), &localStatus);
m_interrupt->writeConfig_Source_Channel(GetChannelForRouting(), &localStatus);
m_interrupt->writeConfig_Source_Module(GetModuleForRouting(), &localStatus);
SetUpSourceEdge(true, false);
wpi_setError(localStatus);
}
void DigitalOutput::SetUpSourceEdge(bool risingEdge, bool fallingEdge)
{
if (StatusIsFatal()) return;
if (m_interrupt == NULL)
{
wpi_setWPIErrorWithContext(NullParameter, "You must call RequestInterrupts before SetUpSourceEdge");
return;
}
tRioStatusCode localStatus = NiFpga_Status_Success;
if (m_interrupt != NULL)
{
m_interrupt->writeConfig_RisingEdge(risingEdge, &localStatus);
m_interrupt->writeConfig_FallingEdge(fallingEdge, &localStatus);
}
wpi_setError(localStatus);
}
void DigitalOutput::ValueChanged(ITable* source, const std::string& key, EntryValue value, bool isNew) {
Set(value.b);
}
void DigitalOutput::UpdateTable() {
}
void DigitalOutput::StartLiveWindowMode() {
if (m_table != NULL) {
m_table->AddTableListener("Value", this, true);
}
}
void DigitalOutput::StopLiveWindowMode() {
if (m_table != NULL) {
m_table->RemoveTableListener(this);
}
}
std::string DigitalOutput::GetSmartDashboardType() {
return "Digital Output";
}
void DigitalOutput::InitTable(ITable *subTable) {
m_table = subTable;
UpdateTable();
}
ITable * DigitalOutput::GetTable() {
return m_table;
}