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