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