blob: f5a006da7d1371f95acd491e7c575fdf67f144b0 [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -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 "InterruptableSensorBase.h"
9
10#include "HAL/HAL.h"
11#include "Utility.h"
12#include "WPIErrors.h"
13
14using namespace frc;
15
16InterruptableSensorBase::InterruptableSensorBase() {}
17
18/**
19 * Request one of the 8 interrupts asynchronously on this digital input.
20 *
21 * Request interrupts in asynchronous mode where the user's interrupt handler
22 * will be called when the interrupt fires. Users that want control over the
23 * thread priority should use the synchronous method with their own spawned
24 * thread. The default is interrupt on rising edges only.
25 */
26void InterruptableSensorBase::RequestInterrupts(
27 HAL_InterruptHandlerFunction handler, void* param) {
28 if (StatusIsFatal()) return;
29
30 wpi_assert(m_interrupt == HAL_kInvalidHandle);
31 AllocateInterrupts(false);
32 if (StatusIsFatal()) return; // if allocate failed, out of interrupts
33
34 int32_t status = 0;
35 HAL_RequestInterrupts(
36 m_interrupt, GetPortHandleForRouting(),
37 static_cast<HAL_AnalogTriggerType>(GetAnalogTriggerTypeForRouting()),
38 &status);
39 SetUpSourceEdge(true, false);
40 HAL_AttachInterruptHandler(m_interrupt, handler, param, &status);
41 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
42}
43
44/**
45 * Request one of the 8 interrupts synchronously on this digital input.
46 *
47 * Request interrupts in synchronous mode where the user program will have to
48 * explicitly wait for the interrupt to occur using WaitForInterrupt.
49 * The default is interrupt on rising edges only.
50 */
51void InterruptableSensorBase::RequestInterrupts() {
52 if (StatusIsFatal()) return;
53
54 wpi_assert(m_interrupt == HAL_kInvalidHandle);
55 AllocateInterrupts(true);
56 if (StatusIsFatal()) return; // if allocate failed, out of interrupts
57
58 int32_t status = 0;
59 HAL_RequestInterrupts(
60 m_interrupt, GetPortHandleForRouting(),
61 static_cast<HAL_AnalogTriggerType>(GetAnalogTriggerTypeForRouting()),
62 &status);
63 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
64 SetUpSourceEdge(true, false);
65}
66
67void InterruptableSensorBase::AllocateInterrupts(bool watcher) {
68 wpi_assert(m_interrupt == HAL_kInvalidHandle);
69 // Expects the calling leaf class to allocate an interrupt index.
70 int32_t status = 0;
71 m_interrupt = HAL_InitializeInterrupts(watcher, &status);
72 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
73}
74
75/**
76 * Cancel interrupts on this device.
77 *
78 * This deallocates all the chipobject structures and disables any interrupts.
79 */
80void InterruptableSensorBase::CancelInterrupts() {
81 if (StatusIsFatal()) return;
82 wpi_assert(m_interrupt != HAL_kInvalidHandle);
83 int32_t status = 0;
84 HAL_CleanInterrupts(m_interrupt, &status);
85 // ignore status, as an invalid handle just needs to be ignored.
86 m_interrupt = HAL_kInvalidHandle;
87}
88
89/**
90 * In synchronous mode, wait for the defined interrupt to occur.
91 *
92 * You should <b>NOT</b> attempt to read the sensor from another thread while
93 * waiting for an interrupt. This is not threadsafe, and can cause memory
94 * corruption
95 *
96 * @param timeout Timeout in seconds
97 * @param ignorePrevious If true, ignore interrupts that happened before
98 * WaitForInterrupt was called.
99 * @return What interrupts fired
100 */
101InterruptableSensorBase::WaitResult InterruptableSensorBase::WaitForInterrupt(
102 double timeout, bool ignorePrevious) {
103 if (StatusIsFatal()) return InterruptableSensorBase::kTimeout;
104 wpi_assert(m_interrupt != HAL_kInvalidHandle);
105 int32_t status = 0;
106 int result;
107
108 result = HAL_WaitForInterrupt(m_interrupt, timeout, ignorePrevious, &status);
109 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
110
111 return static_cast<WaitResult>(result);
112}
113
114/**
115 * Enable interrupts to occur on this input.
116 *
117 * Interrupts are disabled when the RequestInterrupt call is made. This gives
118 * time to do the setup of the other options before starting to field
119 * interrupts.
120 */
121void InterruptableSensorBase::EnableInterrupts() {
122 if (StatusIsFatal()) return;
123 wpi_assert(m_interrupt != HAL_kInvalidHandle);
124 int32_t status = 0;
125 HAL_EnableInterrupts(m_interrupt, &status);
126 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
127}
128
129/**
130 * Disable Interrupts without without deallocating structures.
131 */
132void InterruptableSensorBase::DisableInterrupts() {
133 if (StatusIsFatal()) return;
134 wpi_assert(m_interrupt != HAL_kInvalidHandle);
135 int32_t status = 0;
136 HAL_DisableInterrupts(m_interrupt, &status);
137 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
138}
139
140/**
141 * Return the timestamp for the rising interrupt that occurred most recently.
142 *
143 * This is in the same time domain as GetClock().
144 * The rising-edge interrupt should be enabled with
145 * {@link #DigitalInput.SetUpSourceEdge}
146 *
147 * @return Timestamp in seconds since boot.
148 */
149double InterruptableSensorBase::ReadRisingTimestamp() {
150 if (StatusIsFatal()) return 0.0;
151 wpi_assert(m_interrupt != HAL_kInvalidHandle);
152 int32_t status = 0;
153 double timestamp = HAL_ReadInterruptRisingTimestamp(m_interrupt, &status);
154 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
155 return timestamp;
156}
157
158/**
159 * Return the timestamp for the falling interrupt that occurred most recently.
160 *
161 * This is in the same time domain as GetClock().
162 * The falling-edge interrupt should be enabled with
163 * {@link #DigitalInput.SetUpSourceEdge}
164 *
165 * @return Timestamp in seconds since boot.
166*/
167double InterruptableSensorBase::ReadFallingTimestamp() {
168 if (StatusIsFatal()) return 0.0;
169 wpi_assert(m_interrupt != HAL_kInvalidHandle);
170 int32_t status = 0;
171 double timestamp = HAL_ReadInterruptFallingTimestamp(m_interrupt, &status);
172 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
173 return timestamp;
174}
175
176/**
177 * Set which edge to trigger interrupts on
178 *
179 * @param risingEdge true to interrupt on rising edge
180 * @param fallingEdge true to interrupt on falling edge
181 */
182void InterruptableSensorBase::SetUpSourceEdge(bool risingEdge,
183 bool fallingEdge) {
184 if (StatusIsFatal()) return;
185 if (m_interrupt == HAL_kInvalidHandle) {
186 wpi_setWPIErrorWithContext(
187 NullParameter,
188 "You must call RequestInterrupts before SetUpSourceEdge");
189 return;
190 }
191 if (m_interrupt != HAL_kInvalidHandle) {
192 int32_t status = 0;
193 HAL_SetInterruptUpSourceEdge(m_interrupt, risingEdge, fallingEdge, &status);
194 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
195 }
196}