001/*----------------------------------------------------------------------------*/ 002/* Copyright (c) 2008-2018 FIRST. All Rights Reserved. */ 003/* Open Source Software - may be modified and shared by FRC teams. The code */ 004/* must be accompanied by the FIRST BSD license file in the root directory of */ 005/* the project. */ 006/*----------------------------------------------------------------------------*/ 007 008package edu.wpi.first.wpilibj; 009 010import edu.wpi.first.wpilibj.hal.InterruptJNI; 011import edu.wpi.first.wpilibj.util.AllocationException; 012 013 014/** 015 * Base for sensors to be used with interrupts. 016 */ 017public abstract class InterruptableSensorBase extends SensorBase { 018 @SuppressWarnings("JavadocMethod") 019 public enum WaitResult { 020 kTimeout(0x0), kRisingEdge(0x1), kFallingEdge(0x100), kBoth(0x101); 021 022 @SuppressWarnings("MemberName") 023 public final int value; 024 025 WaitResult(int value) { 026 this.value = value; 027 } 028 } 029 030 /** 031 * The interrupt resource. 032 */ 033 protected int m_interrupt = InterruptJNI.HalInvalidHandle; 034 035 /** 036 * Flags if the interrupt being allocated is synchronous. 037 */ 038 protected boolean m_isSynchronousInterrupt = false; 039 040 /** 041 * Create a new InterrupatableSensorBase. 042 */ 043 public InterruptableSensorBase() { 044 m_interrupt = 0; 045 } 046 047 /** 048 * Frees the resources for this output. 049 */ 050 @Override 051 public void free() { 052 super.free(); 053 if (m_interrupt != 0) { 054 cancelInterrupts(); 055 } 056 } 057 058 /** 059 * If this is an analog trigger. 060 * 061 * @return true if this is an analog trigger. 062 */ 063 public abstract int getAnalogTriggerTypeForRouting(); 064 065 /** 066 * The channel routing number. 067 * 068 * @return channel routing number 069 */ 070 public abstract int getPortHandleForRouting(); 071 072 /** 073 * Request one of the 8 interrupts asynchronously on this digital input. 074 * 075 * @param handler The {@link InterruptHandlerFunction} that contains the method {@link 076 * InterruptHandlerFunction#interruptFired(int, Object)} that will be called 077 * whenever there is an interrupt on this device. Request interrupts in synchronous 078 * mode where the user program interrupt handler will be called when an interrupt 079 * occurs. The default is interrupt on rising edges only. 080 */ 081 public void requestInterrupts(InterruptHandlerFunction<?> handler) { 082 if (m_interrupt != 0) { 083 throw new AllocationException("The interrupt has already been allocated"); 084 } 085 086 allocateInterrupts(false); 087 088 assert m_interrupt != 0; 089 090 InterruptJNI.requestInterrupts(m_interrupt, getPortHandleForRouting(), 091 getAnalogTriggerTypeForRouting()); 092 setUpSourceEdge(true, false); 093 InterruptJNI.attachInterruptHandler(m_interrupt, handler.m_function, 094 handler.overridableParameter()); 095 } 096 097 /** 098 * Request one of the 8 interrupts synchronously on this digital input. Request interrupts in 099 * synchronous mode where the user program will have to explicitly wait for the interrupt to occur 100 * using {@link #waitForInterrupt}. The default is interrupt on rising edges only. 101 */ 102 public void requestInterrupts() { 103 if (m_interrupt != 0) { 104 throw new AllocationException("The interrupt has already been allocated"); 105 } 106 107 allocateInterrupts(true); 108 109 assert m_interrupt != 0; 110 111 InterruptJNI.requestInterrupts(m_interrupt, getPortHandleForRouting(), 112 getAnalogTriggerTypeForRouting()); 113 setUpSourceEdge(true, false); 114 115 } 116 117 /** 118 * Allocate the interrupt. 119 * 120 * @param watcher true if the interrupt should be in synchronous mode where the user program will 121 * have to explicitly wait for the interrupt to occur. 122 */ 123 protected void allocateInterrupts(boolean watcher) { 124 m_isSynchronousInterrupt = watcher; 125 126 m_interrupt = InterruptJNI.initializeInterrupts(watcher); 127 } 128 129 /** 130 * Cancel interrupts on this device. This deallocates all the chipobject structures and disables 131 * any interrupts. 132 */ 133 public void cancelInterrupts() { 134 if (m_interrupt == 0) { 135 throw new IllegalStateException("The interrupt is not allocated."); 136 } 137 InterruptJNI.cleanInterrupts(m_interrupt); 138 m_interrupt = 0; 139 } 140 141 /** 142 * In synchronous mode, wait for the defined interrupt to occur. 143 * 144 * @param timeout Timeout in seconds 145 * @param ignorePrevious If true, ignore interrupts that happened before waitForInterrupt was 146 * called. 147 * @return Result of the wait. 148 */ 149 public WaitResult waitForInterrupt(double timeout, boolean ignorePrevious) { 150 if (m_interrupt == 0) { 151 throw new IllegalStateException("The interrupt is not allocated."); 152 } 153 int result = InterruptJNI.waitForInterrupt(m_interrupt, timeout, ignorePrevious); 154 155 // Rising edge result is the interrupt bit set in the byte 0xFF 156 // Falling edge result is the interrupt bit set in the byte 0xFF00 157 // Set any bit set to be true for that edge, and AND the 2 results 158 // together to match the existing enum for all interrupts 159 int rising = ((result & 0xFF) != 0) ? 0x1 : 0x0; 160 int falling = ((result & 0xFF00) != 0) ? 0x0100 : 0x0; 161 result = rising | falling; 162 163 for (WaitResult mode : WaitResult.values()) { 164 if (mode.value == result) { 165 return mode; 166 } 167 } 168 return null; 169 } 170 171 /** 172 * In synchronous mode, wait for the defined interrupt to occur. 173 * 174 * @param timeout Timeout in seconds 175 * @return Result of the wait. 176 */ 177 public WaitResult waitForInterrupt(double timeout) { 178 return waitForInterrupt(timeout, true); 179 } 180 181 /** 182 * Enable interrupts to occur on this input. Interrupts are disabled when the RequestInterrupt 183 * call is made. This gives time to do the setup of the other options before starting to field 184 * interrupts. 185 */ 186 public void enableInterrupts() { 187 if (m_interrupt == 0) { 188 throw new IllegalStateException("The interrupt is not allocated."); 189 } 190 if (m_isSynchronousInterrupt) { 191 throw new IllegalStateException("You do not need to enable synchronous interrupts"); 192 } 193 InterruptJNI.enableInterrupts(m_interrupt); 194 } 195 196 /** 197 * Disable Interrupts without without deallocating structures. 198 */ 199 public void disableInterrupts() { 200 if (m_interrupt == 0) { 201 throw new IllegalStateException("The interrupt is not allocated."); 202 } 203 if (m_isSynchronousInterrupt) { 204 throw new IllegalStateException("You can not disable synchronous interrupts"); 205 } 206 InterruptJNI.disableInterrupts(m_interrupt); 207 } 208 209 /** 210 * Return the timestamp for the rising interrupt that occurred most recently. This is in the same 211 * time domain as getClock(). The rising-edge interrupt should be enabled with {@link 212 * #setUpSourceEdge}. 213 * 214 * @return Timestamp in seconds since boot. 215 */ 216 public double readRisingTimestamp() { 217 if (m_interrupt == 0) { 218 throw new IllegalStateException("The interrupt is not allocated."); 219 } 220 return InterruptJNI.readInterruptRisingTimestamp(m_interrupt); 221 } 222 223 /** 224 * Return the timestamp for the falling interrupt that occurred most recently. This is in the same 225 * time domain as getClock(). The falling-edge interrupt should be enabled with {@link 226 * #setUpSourceEdge}. 227 * 228 * @return Timestamp in seconds since boot. 229 */ 230 public double readFallingTimestamp() { 231 if (m_interrupt == 0) { 232 throw new IllegalStateException("The interrupt is not allocated."); 233 } 234 return InterruptJNI.readInterruptFallingTimestamp(m_interrupt); 235 } 236 237 /** 238 * Set which edge to trigger interrupts on. 239 * 240 * @param risingEdge true to interrupt on rising edge 241 * @param fallingEdge true to interrupt on falling edge 242 */ 243 public void setUpSourceEdge(boolean risingEdge, boolean fallingEdge) { 244 if (m_interrupt != 0) { 245 InterruptJNI.setInterruptUpSourceEdge(m_interrupt, risingEdge, 246 fallingEdge); 247 } else { 248 throw new IllegalArgumentException("You must call RequestInterrupts before setUpSourceEdge"); 249 } 250 } 251}