001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package edu.wpi.first.wpilibj; 006 007import static edu.wpi.first.util.ErrorMessages.requireNonNullParam; 008 009import edu.wpi.first.hal.InterruptJNI; 010 011/** 012 * Class for handling synchronous (blocking) interrupts. 013 * 014 * <p>By default, interrupts will occur on rising edge. 015 * 016 * <p>Asynchronous interrupts are handled by the AsynchronousInterrupt class. 017 */ 018public class SynchronousInterrupt implements AutoCloseable { 019 private final DigitalSource m_source; 020 021 private final int m_handle; 022 023 /** Event trigger combinations for a synchronous interrupt. */ 024 public enum WaitResult { 025 /** Timeout event. */ 026 kTimeout(0x0), 027 /** Rising edge event. */ 028 kRisingEdge(0x1), 029 /** Falling edge event. */ 030 kFallingEdge(0x100), 031 /** Both rising and falling edge events. */ 032 kBoth(0x101); 033 034 /** WaitResult value. */ 035 public final int value; 036 037 WaitResult(int value) { 038 this.value = value; 039 } 040 041 /** 042 * Create a wait result enum. 043 * 044 * @param rising True if a rising edge occurred. 045 * @param falling True if a falling edge occurred. 046 * @return A wait result enum. 047 */ 048 public static WaitResult getValue(boolean rising, boolean falling) { 049 if (rising && falling) { 050 return kBoth; 051 } else if (rising) { 052 return kRisingEdge; 053 } else if (falling) { 054 return kFallingEdge; 055 } else { 056 return kTimeout; 057 } 058 } 059 } 060 061 /** 062 * Constructs a new synchronous interrupt using a DigitalSource. 063 * 064 * <p>At construction, the interrupt will trigger on the rising edge. 065 * 066 * @param source The digital source to use. 067 */ 068 public SynchronousInterrupt(DigitalSource source) { 069 m_source = requireNonNullParam(source, "source", "SynchronousInterrupt"); 070 m_handle = InterruptJNI.initializeInterrupts(); 071 InterruptJNI.requestInterrupts( 072 m_handle, m_source.getPortHandleForRouting(), m_source.getAnalogTriggerTypeForRouting()); 073 InterruptJNI.setInterruptUpSourceEdge(m_handle, true, false); 074 } 075 076 /** 077 * Closes the interrupt. 078 * 079 * <p>This does not close the associated digital source. 080 */ 081 @Override 082 public void close() { 083 InterruptJNI.cleanInterrupts(m_handle); 084 } 085 086 /** 087 * Wait for an interrupt. 088 * 089 * @param timeoutSeconds The timeout in seconds. 0 or less will return immediately. 090 * @param ignorePrevious True to ignore if a previous interrupt has occurred, and only wait for a 091 * new trigger. False will consider if an interrupt has occurred since the last time the 092 * interrupt was read. 093 * @return Result of which edges were triggered, or if a timeout occurred. 094 */ 095 public WaitResult waitForInterrupt(double timeoutSeconds, boolean ignorePrevious) { 096 long result = InterruptJNI.waitForInterrupt(m_handle, timeoutSeconds, ignorePrevious); 097 098 // Rising edge result is the interrupt bit set in the byte 0xFF 099 // Falling edge result is the interrupt bit set in the byte 0xFF00 100 // Set any bit set to be true for that edge, and then conduct a logical AND on the 2 results 101 // together to match the existing enum for all interrupts 102 boolean rising = (result & 0xFF) != 0; 103 boolean falling = (result & 0xFF00) != 0; 104 return WaitResult.getValue(rising, falling); 105 } 106 107 /** 108 * Wait for an interrupt, ignoring any previously occurring interrupts. 109 * 110 * @param timeoutSeconds The timeout in seconds. 0 or less will return immediately. 111 * @return Result of which edges were triggered, or if a timeout occurred. 112 */ 113 public WaitResult waitForInterrupt(double timeoutSeconds) { 114 return waitForInterrupt(timeoutSeconds, true); 115 } 116 117 /** 118 * Set which edges to trigger the interrupt on. 119 * 120 * @param risingEdge Trigger on rising edge 121 * @param fallingEdge Trigger on falling edge 122 */ 123 public void setInterruptEdges(boolean risingEdge, boolean fallingEdge) { 124 InterruptJNI.setInterruptUpSourceEdge(m_handle, risingEdge, fallingEdge); 125 } 126 127 /** 128 * Get the timestamp of the last rising edge. 129 * 130 * <p>This only works if rising edge was configured using setInterruptEdges. 131 * 132 * @return the timestamp in seconds relative to getFPGATime 133 */ 134 public double getRisingTimestamp() { 135 return InterruptJNI.readInterruptRisingTimestamp(m_handle) * 1e-6; 136 } 137 138 /** 139 * Get the timestamp of the last falling edge. 140 * 141 * <p>This only works if falling edge was configured using setInterruptEdges. 142 * 143 * @return the timestamp in seconds relative to getFPGATime 144 */ 145 public double getFallingTimestamp() { 146 return InterruptJNI.readInterruptFallingTimestamp(m_handle) * 1e-6; 147 } 148 149 /** Force triggering of any waiting interrupt, which will be seen as a timeout. */ 150 public void wakeupWaitingInterrupt() { 151 InterruptJNI.releaseWaitingInterrupt(m_handle); 152 } 153}