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 interrupt that returns the raw result value from the hardware. 089 * 090 * <p>Used by AsynchronousInterrupt. Users should use waitForInterrupt. 091 * 092 * @param timeoutSeconds The timeout in seconds. 0 or less will return immediately. 093 * @param ignorePrevious True to ignore if a previous interrupt has occurred, and only wait for a 094 * new trigger. False will consider if an interrupt has occurred since the last time the 095 * interrupt was read. 096 * @return The raw hardware interrupt result 097 */ 098 long waitForInterruptRaw(double timeoutSeconds, boolean ignorePrevious) { 099 return InterruptJNI.waitForInterrupt(m_handle, timeoutSeconds, ignorePrevious); 100 } 101 102 /** 103 * Wait for an interrupt. 104 * 105 * @param timeoutSeconds The timeout in seconds. 0 or less will return immediately. 106 * @param ignorePrevious True to ignore if a previous interrupt has occurred, and only wait for a 107 * new trigger. False will consider if an interrupt has occurred since the last time the 108 * interrupt was read. 109 * @return Result of which edges were triggered, or if a timeout occurred. 110 */ 111 public WaitResult waitForInterrupt(double timeoutSeconds, boolean ignorePrevious) { 112 long result = InterruptJNI.waitForInterrupt(m_handle, timeoutSeconds, ignorePrevious); 113 114 // Rising edge result is the interrupt bit set in the byte 0xFF 115 // Falling edge result is the interrupt bit set in the byte 0xFF00 116 // Set any bit set to be true for that edge, and then conduct a logical AND on the 2 results 117 // together to match the existing enum for all interrupts 118 boolean rising = (result & 0xFF) != 0; 119 boolean falling = (result & 0xFF00) != 0; 120 return WaitResult.getValue(rising, falling); 121 } 122 123 /** 124 * Wait for an interrupt, ignoring any previously occurring interrupts. 125 * 126 * @param timeoutSeconds The timeout in seconds. 0 or less will return immediately. 127 * @return Result of which edges were triggered, or if a timeout occurred. 128 */ 129 public WaitResult waitForInterrupt(double timeoutSeconds) { 130 return waitForInterrupt(timeoutSeconds, true); 131 } 132 133 /** 134 * Set which edges to trigger the interrupt on. 135 * 136 * @param risingEdge Trigger on rising edge 137 * @param fallingEdge Trigger on falling edge 138 */ 139 public void setInterruptEdges(boolean risingEdge, boolean fallingEdge) { 140 InterruptJNI.setInterruptUpSourceEdge(m_handle, risingEdge, fallingEdge); 141 } 142 143 /** 144 * Get the timestamp of the last rising edge. 145 * 146 * <p>This only works if rising edge was configured using setInterruptEdges. 147 * 148 * @return the timestamp in seconds relative to getFPGATime 149 */ 150 public double getRisingTimestamp() { 151 return InterruptJNI.readInterruptRisingTimestamp(m_handle) * 1e-6; 152 } 153 154 /** 155 * Get the timestamp of the last falling edge. 156 * 157 * <p>This only works if falling edge was configured using setInterruptEdges. 158 * 159 * @return the timestamp in seconds relative to getFPGATime 160 */ 161 public double getFallingTimestamp() { 162 return InterruptJNI.readInterruptFallingTimestamp(m_handle) * 1e-6; 163 } 164 165 /** Force triggering of any waiting interrupt, which will be seen as a timeout. */ 166 public void wakeupWaitingInterrupt() { 167 InterruptJNI.releaseWaitingInterrupt(m_handle); 168 } 169}