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