blob: ac00df37bb5de36114ca4de93f63fd446163503e [file] [log] [blame]
Parker Schuhd3b7a8872018-02-19 16:42:27 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2008-2017. 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 "frc971/wpilib/ahal/DigitalOutput.h"
9
10#include <limits>
11#include <sstream>
12
Austin Schuhf6b94632019-02-02 22:11:27 -080013#include "hal/DIO.h"
14#include "hal/HAL.h"
15#include "hal/Ports.h"
Parker Schuhd3b7a8872018-02-19 16:42:27 -080016#include "frc971/wpilib/ahal/WPIErrors.h"
17
18using namespace frc;
19
20/**
21 * Create an instance of a digital output.
22 *
23 * Create a digital output given a channel.
24 *
25 * @param channel The digital channel 0-9 are on-board, 10-25 are on the MXP
26 * port
27 */
28DigitalOutput::DigitalOutput(int channel) {
29 std::stringstream buf;
30
31 m_pwmGenerator = HAL_kInvalidHandle;
32 if (!CheckDigitalChannel(channel)) {
33 buf << "Digital Channel " << channel;
34 wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, buf.str());
35 m_channel = std::numeric_limits<int>::max();
36 return;
37 }
38 m_channel = channel;
39
40 int32_t status = 0;
Austin Schuh9950f682021-11-06 15:27:58 -070041 m_handle =
42 HAL_InitializeDIOPort(HAL_GetPort(channel), false, nullptr, &status);
Parker Schuhd3b7a8872018-02-19 16:42:27 -080043 if (status != 0) {
44 wpi_setErrorWithContextRange(status, 0, HAL_GetNumDigitalChannels(),
45 channel, HAL_GetErrorMessage(status));
James Kuszmauleb9f6fb2022-02-27 21:04:00 -080046 HAL_CHECK_STATUS(status);
Parker Schuhd3b7a8872018-02-19 16:42:27 -080047 m_channel = std::numeric_limits<int>::max();
48 m_handle = HAL_kInvalidHandle;
49 return;
50 }
51
52 HAL_Report(HALUsageReporting::kResourceType_DigitalOutput, channel);
53}
54
55/**
56 * Free the resources associated with a digital output.
57 */
58DigitalOutput::~DigitalOutput() {
59 if (StatusIsFatal()) return;
60 // Disable the PWM in case it was running.
61 DisablePWM();
62
63 HAL_FreeDIOPort(m_handle);
64}
65
66/**
67 * Set the value of a digital output.
68 *
69 * Set the value of a digital output to either one (true) or zero (false).
70 *
71 * @param value 1 (true) for high, 0 (false) for disabled
72 */
73void DigitalOutput::Set(bool value) {
74 if (StatusIsFatal()) return;
75
76 int32_t status = 0;
77 HAL_SetDIO(m_handle, value, &status);
78 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
James Kuszmauleb9f6fb2022-02-27 21:04:00 -080079 HAL_CHECK_STATUS(status);
Parker Schuhd3b7a8872018-02-19 16:42:27 -080080}
81
82/**
83 * Gets the value being output from the Digital Output.
84 *
85 * @return the state of the digital output.
86 */
87bool DigitalOutput::Get() const {
88 if (StatusIsFatal()) return false;
89
90 int32_t status = 0;
91 bool val = HAL_GetDIO(m_handle, &status);
92 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
James Kuszmauleb9f6fb2022-02-27 21:04:00 -080093 HAL_CHECK_STATUS(status);
Parker Schuhd3b7a8872018-02-19 16:42:27 -080094 return val;
95}
96
97/**
98 * @return The GPIO channel number that this object represents.
99 */
100int DigitalOutput::GetChannel() const { return m_channel; }
101
102/**
103 * Output a single pulse on the digital output line.
104 *
105 * Send a single pulse on the digital output line where the pulse duration is
106 * specified in seconds. Maximum pulse length is 0.0016 seconds.
107 *
108 * @param length The pulse length in seconds
109 */
110void DigitalOutput::Pulse(double length) {
111 if (StatusIsFatal()) return;
112
113 int32_t status = 0;
114 HAL_Pulse(m_handle, length, &status);
115 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
James Kuszmauleb9f6fb2022-02-27 21:04:00 -0800116 HAL_CHECK_STATUS(status);
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800117}
118
119/**
120 * Determine if the pulse is still going.
121 *
122 * Determine if a previously started pulse is still going.
123 */
124bool DigitalOutput::IsPulsing() const {
125 if (StatusIsFatal()) return false;
126
127 int32_t status = 0;
128 bool value = HAL_IsPulsing(m_handle, &status);
129 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
James Kuszmauleb9f6fb2022-02-27 21:04:00 -0800130 HAL_CHECK_STATUS(status);
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800131 return value;
132}
133
134/**
135 * Change the PWM frequency of the PWM output on a Digital Output line.
136 *
137 * The valid range is from 0.6 Hz to 19 kHz. The frequency resolution is
138 * logarithmic.
139 *
140 * There is only one PWM frequency for all digital channels.
141 *
142 * @param rate The frequency to output all digital output PWM signals.
143 */
144void DigitalOutput::SetPWMRate(double rate) {
145 if (StatusIsFatal()) return;
146
147 int32_t status = 0;
148 HAL_SetDigitalPWMRate(rate, &status);
149 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
James Kuszmauleb9f6fb2022-02-27 21:04:00 -0800150 HAL_CHECK_STATUS(status);
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800151}
152
153/**
154 * Enable a PWM Output on this line.
155 *
156 * Allocate one of the 6 DO PWM generator resources from this module.
157 *
158 * Supply the initial duty-cycle to output so as to avoid a glitch when first
159 * starting.
160 *
161 * The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less)
162 * but is reduced the higher the frequency of the PWM signal is.
163 *
164 * @param initialDutyCycle The duty-cycle to start generating. [0..1]
165 */
166void DigitalOutput::EnablePWM(double initialDutyCycle) {
167 if (m_pwmGenerator != HAL_kInvalidHandle) return;
168
169 int32_t status = 0;
170
171 if (StatusIsFatal()) return;
172 m_pwmGenerator = HAL_AllocateDigitalPWM(&status);
173 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
174
175 if (StatusIsFatal()) return;
176 HAL_SetDigitalPWMDutyCycle(m_pwmGenerator, initialDutyCycle, &status);
177 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
178
179 if (StatusIsFatal()) return;
180 HAL_SetDigitalPWMOutputChannel(m_pwmGenerator, m_channel, &status);
181 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
James Kuszmauleb9f6fb2022-02-27 21:04:00 -0800182
183 HAL_CHECK_STATUS(status);
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800184}
185
186/**
187 * Change this line from a PWM output back to a static Digital Output line.
188 *
189 * Free up one of the 6 DO PWM generator resources that were in use.
190 */
191void DigitalOutput::DisablePWM() {
192 if (StatusIsFatal()) return;
193 if (m_pwmGenerator == HAL_kInvalidHandle) return;
194
195 int32_t status = 0;
196
197 // Disable the output by routing to a dead bit.
198 HAL_SetDigitalPWMOutputChannel(m_pwmGenerator, kDigitalChannels, &status);
199 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
200
201 HAL_FreeDigitalPWM(m_pwmGenerator, &status);
202 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
203
204 m_pwmGenerator = HAL_kInvalidHandle;
James Kuszmauleb9f6fb2022-02-27 21:04:00 -0800205
206 HAL_CHECK_STATUS(status);
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800207}
208
209/**
210 * Change the duty-cycle that is being generated on the line.
211 *
212 * The resolution of the duty cycle is 8-bit for low frequencies (1kHz or less)
213 * but is reduced the higher the frequency of the PWM signal is.
214 *
215 * @param dutyCycle The duty-cycle to change to. [0..1]
216 */
217void DigitalOutput::UpdateDutyCycle(double dutyCycle) {
218 if (StatusIsFatal()) return;
219
220 int32_t status = 0;
221 HAL_SetDigitalPWMDutyCycle(m_pwmGenerator, dutyCycle, &status);
222 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
James Kuszmauleb9f6fb2022-02-27 21:04:00 -0800223
224 HAL_CHECK_STATUS(status);
Parker Schuhd3b7a8872018-02-19 16:42:27 -0800225}
226
227/**
228 * @return The HAL Handle to the specified source.
229 */
230HAL_Handle DigitalOutput::GetPortHandleForRouting() const { return m_handle; }
231
232/**
233 * Is source an AnalogTrigger
234 */
235bool DigitalOutput::IsAnalogTrigger() const { return false; }
236
237/**
238 * @return The type of analog trigger output to be used. 0 for Digitals
239 */
240AnalogTriggerType DigitalOutput::GetAnalogTriggerTypeForRouting() const {
241 return (AnalogTriggerType)0;
242}