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