001/*----------------------------------------------------------------------------*/ 002/* Copyright (c) FIRST 2008-2012. 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 java.nio.ByteBuffer; 011import java.nio.ByteOrder; 012 013import edu.wpi.first.wpilibj.hal.HALUtil; 014import edu.wpi.first.wpilibj.hal.InterruptJNI; 015import edu.wpi.first.wpilibj.util.AllocationException; 016import edu.wpi.first.wpilibj.util.CheckedAllocationException; 017 018/** 019 * Base for sensors to be used with interrupts 020 */ 021public abstract class InterruptableSensorBase extends SensorBase { 022 /** 023 * This is done to store the JVM variable in the InterruptJNI 024 * This is done because the HAL must have access to the JVM variable 025 * in order to attach the newly spawned thread when an interrupt is fired. 026 */ 027 static{ 028 ByteBuffer status = ByteBuffer.allocateDirect(4); 029 status.order(ByteOrder.LITTLE_ENDIAN); 030 InterruptJNI.initializeInterruptJVM(status.asIntBuffer()); 031 HALUtil.checkStatus(status.asIntBuffer()); 032 } 033 034 /** 035 * The interrupt resource 036 */ 037 protected ByteBuffer m_interrupt = null; 038 039 /** 040 * Flags if the interrupt being allocated is synchronous 041 */ 042 protected boolean m_isSynchronousInterrupt = false; 043 044 /** 045 * The index of the interrupt 046 */ 047 protected int m_interruptIndex; 048 /** 049 * Resource manager 050 */ 051 protected static Resource interrupts = new Resource(8); 052 053 /** 054 * Create a new InterrupatableSensorBase 055 */ 056 public InterruptableSensorBase() { 057 m_interrupt = null; 058 } 059 060 /** 061 * @return true if this is an analog trigger 062 */ 063 abstract boolean getAnalogTriggerForRouting(); 064 065 /** 066 * @return channel routing number 067 */ 068 abstract int getChannelForRouting(); 069 070 /** 071 * @return module routing number 072 */ 073 abstract byte getModuleForRouting(); 074 075 /** 076 * Request one of the 8 interrupts asynchronously on this digital input. 077 * 078 * @param handler 079 * The {@link InterruptHandlerFunction} that contains the method 080 * {@link InterruptHandlerFunction#interruptFired(int, Object)} that 081 * will be called whenever there is an interrupt on this device. 082 * Request interrupts in synchronous mode where the user program 083 * interrupt handler will be called when an interrupt occurs. The 084 * default is interrupt on rising edges only. 085 */ 086 public void requestInterrupts(InterruptHandlerFunction<?> handler) { 087 if(m_interrupt != null){ 088 throw new AllocationException("The interrupt has already been allocated"); 089 } 090 091 allocateInterrupts(false); 092 093 assert (m_interrupt != null); 094 095 ByteBuffer status = ByteBuffer.allocateDirect(4); 096 // set the byte order 097 status.order(ByteOrder.LITTLE_ENDIAN); 098 InterruptJNI.requestInterrupts(m_interrupt, getModuleForRouting(), 099 getChannelForRouting(), 100 (byte) (getAnalogTriggerForRouting() ? 1 : 0), status.asIntBuffer()); 101 HALUtil.checkStatus(status.asIntBuffer()); 102 setUpSourceEdge(true, false); 103 InterruptJNI.attachInterruptHandler(m_interrupt, handler.function, handler.overridableParamater(), status.asIntBuffer()); 104 HALUtil.checkStatus(status.asIntBuffer()); 105 } 106 107 /** 108 * Request one of the 8 interrupts synchronously on this digital input. Request 109 * interrupts in synchronous mode where the user program will have to 110 * explicitly wait for the interrupt to occur using {@link #waitForInterrupt}. 111 * The default is interrupt on rising edges only. 112 */ 113 public void requestInterrupts() { 114 if(m_interrupt != null){ 115 throw new AllocationException("The interrupt has already been allocated"); 116 } 117 118 allocateInterrupts(true); 119 120 assert (m_interrupt != null); 121 122 ByteBuffer status = ByteBuffer.allocateDirect(4); 123 // set the byte order 124 status.order(ByteOrder.LITTLE_ENDIAN); 125 InterruptJNI.requestInterrupts(m_interrupt, getModuleForRouting(), 126 getChannelForRouting(), 127 (byte) (getAnalogTriggerForRouting() ? 1 : 0), status.asIntBuffer()); 128 HALUtil.checkStatus(status.asIntBuffer()); 129 setUpSourceEdge(true, false); 130 131 } 132 133 /** 134 * Allocate the interrupt 135 * 136 * @param watcher true if the interrupt should be in synchronous mode where the user 137 * program will have to explicitly wait for the interrupt to occur. 138 */ 139 protected void allocateInterrupts(boolean watcher) { 140 try { 141 m_interruptIndex = interrupts.allocate(); 142 } catch (CheckedAllocationException e) { 143 throw new AllocationException( 144 "No interrupts are left to be allocated"); 145 } 146 m_isSynchronousInterrupt = watcher; 147 148 ByteBuffer status = ByteBuffer.allocateDirect(4); 149 status.order(ByteOrder.LITTLE_ENDIAN); 150 m_interrupt = InterruptJNI.initializeInterrupts(m_interruptIndex, 151 (byte) (watcher ? 1 : 0), status.asIntBuffer()); 152 HALUtil.checkStatus(status.asIntBuffer()); 153 } 154 155 /** 156 * Cancel interrupts on this device. This deallocates all the chipobject 157 * structures and disables any interrupts. 158 */ 159 public void cancelInterrupts() { 160 if (m_interrupt == null) { 161 throw new IllegalStateException("The interrupt is not allocated."); 162 } 163 ByteBuffer status = ByteBuffer.allocateDirect(4); 164 status.order(ByteOrder.LITTLE_ENDIAN); 165 InterruptJNI.cleanInterrupts(m_interrupt, status.asIntBuffer()); 166 HALUtil.checkStatus(status.asIntBuffer()); 167 m_interrupt = null; 168 interrupts.free(m_interruptIndex); 169 } 170 171 /** 172 * In synchronous mode, wait for the defined interrupt to occur. 173 * 174 * @param timeout 175 * Timeout in seconds 176 * @param ignorePrevious 177 * If true, ignore interrupts that happened before 178 * waitForInterrupt was called. 179 */ 180 public void waitForInterrupt(double timeout, boolean ignorePrevious) { 181 if (m_interrupt == null) { 182 throw new IllegalStateException("The interrupt is not allocated."); 183 } 184 ByteBuffer status = ByteBuffer.allocateDirect(4); 185 status.order(ByteOrder.LITTLE_ENDIAN); 186 InterruptJNI.waitForInterrupt(m_interrupt, (float) timeout, ignorePrevious, status.asIntBuffer()); 187 HALUtil.checkStatus(status.asIntBuffer()); 188 } 189 190 /** 191 * In synchronous mode, wait for the defined interrupt to occur. 192 * 193 * @param timeout 194 * Timeout in seconds 195 */ 196 public void waitForInterrupt(double timeout) { 197 waitForInterrupt(timeout, true); 198 } 199 200 /** 201 * Enable interrupts to occur on this input. Interrupts are disabled when 202 * the RequestInterrupt call is made. This gives time to do the setup of the 203 * other options before starting to field interrupts. 204 */ 205 public void enableInterrupts() { 206 if (m_interrupt == null) { 207 throw new IllegalStateException("The interrupt is not allocated."); 208 } 209 if(m_isSynchronousInterrupt){ 210 throw new IllegalStateException("You do not need to enable synchronous interrupts"); 211 } 212 ByteBuffer status = ByteBuffer.allocateDirect(4); 213 status.order(ByteOrder.LITTLE_ENDIAN); 214 InterruptJNI.enableInterrupts(m_interrupt, status.asIntBuffer()); 215 HALUtil.checkStatus(status.asIntBuffer()); 216 } 217 218 /** 219 * Disable Interrupts without without deallocating structures. 220 */ 221 public void disableInterrupts() { 222 if (m_interrupt == null) { 223 throw new IllegalStateException("The interrupt is not allocated."); 224 } 225 if(m_isSynchronousInterrupt){ 226 throw new IllegalStateException("You can not disable synchronous interrupts"); 227 } 228 ByteBuffer status = ByteBuffer.allocateDirect(4); 229 status.order(ByteOrder.LITTLE_ENDIAN); 230 InterruptJNI.disableInterrupts(m_interrupt, status.asIntBuffer()); 231 HALUtil.checkStatus(status.asIntBuffer()); 232 } 233 234 /** 235 * Return the timestamp for the rising interrupt that occurred most 236 * recently. This is in the same time domain as getClock(). 237 * The rising-edge interrupt should be enabled with 238 * {@link #setUpSourceEdge} 239 * @return Timestamp in seconds since boot. 240 */ 241 public double readRisingTimestamp() { 242 if (m_interrupt == null) { 243 throw new IllegalStateException("The interrupt is not allocated."); 244 } 245 ByteBuffer status = ByteBuffer.allocateDirect(4); 246 status.order(ByteOrder.LITTLE_ENDIAN); 247 double timestamp = InterruptJNI.readRisingTimestamp(m_interrupt, status.asIntBuffer()); 248 HALUtil.checkStatus(status.asIntBuffer()); 249 return timestamp; 250 } 251 252 /** 253 * Return the timestamp for the falling interrupt that occurred most 254 * recently. This is in the same time domain as getClock(). 255 * The falling-edge interrupt should be enabled with 256 * {@link #setUpSourceEdge} 257 * @return Timestamp in seconds since boot. 258 */ 259 public double readFallingTimestamp() { 260 if (m_interrupt == null) { 261 throw new IllegalStateException("The interrupt is not allocated."); 262 } 263 ByteBuffer status = ByteBuffer.allocateDirect(4); 264 status.order(ByteOrder.LITTLE_ENDIAN); 265 double timestamp = InterruptJNI.readFallingTimestamp(m_interrupt, status.asIntBuffer()); 266 HALUtil.checkStatus(status.asIntBuffer()); 267 return timestamp; 268 } 269 270 /** 271 * Set which edge to trigger interrupts on 272 * 273 * @param risingEdge 274 * true to interrupt on rising edge 275 * @param fallingEdge 276 * true to interrupt on falling edge 277 */ 278 public void setUpSourceEdge(boolean risingEdge, boolean fallingEdge) { 279 if (m_interrupt != null) { 280 ByteBuffer status = ByteBuffer.allocateDirect(4); 281 // set the byte order 282 status.order(ByteOrder.LITTLE_ENDIAN); 283 InterruptJNI.setInterruptUpSourceEdge(m_interrupt, 284 (byte) (risingEdge ? 1 : 0), (byte) (fallingEdge ? 1 : 0), 285 status.asIntBuffer()); 286 HALUtil.checkStatus(status.asIntBuffer()); 287 } else { 288 throw new IllegalArgumentException( 289 "You must call RequestInterrupts before setUpSourceEdge"); 290 } 291 } 292}