blob: a757bee3bd05d4a26227c8cc162b857f7fa8fd79 [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/InterruptableSensorBase.h"
9
10#include "HAL/HAL.h"
11#include "frc971/wpilib/ahal/Utility.h"
12#include "frc971/wpilib/ahal/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 // Rising edge result is the interrupt bit set in the byte 0xFF
112 // Falling edge result is the interrupt bit set in the byte 0xFF00
113 // Set any bit set to be true for that edge, and AND the 2 results
114 // together to match the existing enum for all interrupts
115 int32_t rising = (result & 0xFF) ? 0x1 : 0x0;
116 int32_t falling = ((result & 0xFF00) ? 0x0100 : 0x0);
117 return static_cast<WaitResult>(falling | rising);
118}
119
120/**
121 * Enable interrupts to occur on this input.
122 *
123 * Interrupts are disabled when the RequestInterrupt call is made. This gives
124 * time to do the setup of the other options before starting to field
125 * interrupts.
126 */
127void InterruptableSensorBase::EnableInterrupts() {
128 if (StatusIsFatal()) return;
129 wpi_assert(m_interrupt != HAL_kInvalidHandle);
130 int32_t status = 0;
131 HAL_EnableInterrupts(m_interrupt, &status);
132 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
133}
134
135/**
136 * Disable Interrupts without without deallocating structures.
137 */
138void InterruptableSensorBase::DisableInterrupts() {
139 if (StatusIsFatal()) return;
140 wpi_assert(m_interrupt != HAL_kInvalidHandle);
141 int32_t status = 0;
142 HAL_DisableInterrupts(m_interrupt, &status);
143 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
144}
145
146/**
147 * Return the timestamp for the rising interrupt that occurred most recently.
148 *
149 * This is in the same time domain as GetClock().
150 * The rising-edge interrupt should be enabled with
151 * {@link #DigitalInput.SetUpSourceEdge}
152 *
153 * @return Timestamp in seconds since boot.
154 */
155double InterruptableSensorBase::ReadRisingTimestamp() {
156 if (StatusIsFatal()) return 0.0;
157 wpi_assert(m_interrupt != HAL_kInvalidHandle);
158 int32_t status = 0;
159 double timestamp = HAL_ReadInterruptRisingTimestamp(m_interrupt, &status);
160 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
161 return timestamp;
162}
163
164/**
165 * Return the timestamp for the falling interrupt that occurred most recently.
166 *
167 * This is in the same time domain as GetClock().
168 * The falling-edge interrupt should be enabled with
169 * {@link #DigitalInput.SetUpSourceEdge}
170 *
171 * @return Timestamp in seconds since boot.
172*/
173double InterruptableSensorBase::ReadFallingTimestamp() {
174 if (StatusIsFatal()) return 0.0;
175 wpi_assert(m_interrupt != HAL_kInvalidHandle);
176 int32_t status = 0;
177 double timestamp = HAL_ReadInterruptFallingTimestamp(m_interrupt, &status);
178 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
179 return timestamp;
180}
181
182/**
183 * Set which edge to trigger interrupts on
184 *
185 * @param risingEdge true to interrupt on rising edge
186 * @param fallingEdge true to interrupt on falling edge
187 */
188void InterruptableSensorBase::SetUpSourceEdge(bool risingEdge,
189 bool fallingEdge) {
190 if (StatusIsFatal()) return;
191 if (m_interrupt == HAL_kInvalidHandle) {
192 wpi_setWPIErrorWithContext(
193 NullParameter,
194 "You must call RequestInterrupts before SetUpSourceEdge");
195 return;
196 }
197 if (m_interrupt != HAL_kInvalidHandle) {
198 int32_t status = 0;
199 HAL_SetInterruptUpSourceEdge(m_interrupt, risingEdge, fallingEdge, &status);
200 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
201 }
202}