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