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