-
Notifications
You must be signed in to change notification settings - Fork 13
Add ShiftRegister74HC595Helper for SPI-based shift register control #393
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Parent:
Updated SupportedHardware.adoc
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
0b3405f
Add ShiftRegister74HC595Helper class with SPI communication methods a…
j-heitz 1359401
added getState method in ShiftRegister74HC595Helper
j-heitz 1c87241
Add unit tests for ShiftRegister74HC595Helper methods and improve tes…
j-heitz 4751820
Added Java doc comments to ShiftRegister74HC595Helper
j-heitz ab1cd33
Refactor documentation for ShiftRegister74HC595Helper class
j-heitz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
139 changes: 139 additions & 0 deletions
139
...t-utils/src/main/java/com/opensourcewithslu/outputdevices/ShiftRegister74HC595Helper.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| package com.opensourcewithslu.outputdevices; | ||
|
|
||
| import java.io.IOException; | ||
| import java.util.Objects; | ||
|
|
||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| import com.pi4j.io.spi.Spi; | ||
|
|
||
| /** | ||
| * Helper for controlling a 74HC595 shift register over SPI (Pi4J). | ||
| * | ||
| * Provides simple operations to write a full byte to the shift register, | ||
| * set or clear individual output bits, clear the register, | ||
| * and read the last written state. All SPI communication is performed using | ||
| * the provided {@link com.pi4j.io.spi.Spi} instance. | ||
| * | ||
| * The helper maintains an internal state byte reflecting the last value | ||
| * written to the device. | ||
| * | ||
| */ | ||
| public class ShiftRegister74HC595Helper { | ||
|
|
||
| private static final Logger log = LoggerFactory.getLogger(ShiftRegister74HC595Helper.class); | ||
| private final Spi spi; | ||
| private byte state = 0x00; | ||
|
|
||
j-heitz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** | ||
| * Constructs a new ShiftRegister74HC595Helper with the specified SPI interface. | ||
| * | ||
| * Initializes the helper with a clean state (0x00) and sets up logging. | ||
| * | ||
| * | ||
| * @param spi the SPI interface to use for communication with the shift register | ||
| * @throws NullPointerException if spi is null | ||
| */ | ||
| public ShiftRegister74HC595Helper(Spi spi) { | ||
| this.spi = Objects.requireNonNull(spi, "spi must not be null"); | ||
| log.info("74HC595 helper initialized; state=0x00"); | ||
| } | ||
|
|
||
| /** | ||
| * Shifts out a byte value to the 74HC595 shift register via SPI. | ||
| * | ||
| * This method writes the specified byte value to the shift register and | ||
| * updates the internal state tracking. The operation is logged for debugging. | ||
| * | ||
| * @param value the byte value to shift out to the register | ||
| * @throws IOException if the SPI write operation fails | ||
| */ | ||
| public void shiftOut(byte value) throws IOException { | ||
| try { | ||
| spi.write(value); | ||
| this.state = value; | ||
| log.info("Shifted out 0x{}", toHex(this.state)); | ||
| } catch (Exception e) { | ||
| String msg = String.format("SPI write failed for 0x%s: %s", toHex(value), e.getMessage()); | ||
| log.error(msg, e); | ||
| throw new IOException(msg, e); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Clears the shift register by setting all outputs to LOW (0x00). | ||
| * | ||
| * This is equivalent to calling {@code shiftOut((byte) 0x00)}. | ||
| * | ||
| * @throws IOException if the SPI write operation fails | ||
| */ | ||
| public void clear() throws IOException { | ||
| shiftOut((byte) 0x00); | ||
| log.info("Shift register cleared"); | ||
| } | ||
|
|
||
| /** | ||
| * Sets or clears a specific bit in the shift register. | ||
| * | ||
| * This method modifies the internal state by setting or clearing the specified | ||
| * bit index (0-7), then shifts out the updated state to the register. | ||
| * | ||
| * @param bitIndex the index of the bit to modify (0-7, where 0 is LSB) | ||
| * @param value {@code true} to set the bit to HIGH, {@code false} to set to LOW | ||
| * @throws IllegalArgumentException if bitIndex is not in the range 0-7 | ||
| * @throws IOException if the SPI write operation fails | ||
| */ | ||
| public void setBit(int bitIndex, boolean value) throws IOException { | ||
| if (bitIndex < 0 || bitIndex > 7) { | ||
| throw new IllegalArgumentException("bitIndex must be 0..7; got " + bitIndex); | ||
| } | ||
| if (value) { | ||
| state |= (1 << bitIndex); | ||
| } else { | ||
| state &= ~(1 << bitIndex); | ||
| } | ||
| shiftOut(state); | ||
| log.info("Bit {} set to {}", bitIndex, value); | ||
| } | ||
|
|
||
| /** | ||
| * Clears (sets to LOW) a specific bit in the shift register. | ||
| * | ||
| * This is a convenience method equivalent to calling {@code setBit(bitIndex, false)}. | ||
| * | ||
| * @param bitIndex the index of the bit to clear (0-7, where 0 is LSB) | ||
| * @throws IllegalArgumentException if bitIndex is not in the range 0-7 | ||
| * @throws IOException if the SPI write operation fails | ||
| */ | ||
| public void clearBit(int bitIndex) throws IOException { | ||
| setBit(bitIndex, false); | ||
| log.info("Bit {} cleared", bitIndex); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the current state of the shift register. | ||
| * | ||
| * This method returns the internally tracked state without performing | ||
| * any SPI operations. The state reflects the last value written to | ||
| * the shift register. | ||
| * | ||
| * @return the current state as a byte value | ||
| */ | ||
| public byte getState() { | ||
| return state; | ||
| } | ||
|
|
||
| /** | ||
| * Converts a byte value to its hexadecimal string representation. | ||
| * | ||
| * This is a utility method used for logging purposes to display | ||
| * byte values in a readable hexadecimal format. | ||
| * | ||
| * @param b the byte value to convert | ||
| * @return a two-character uppercase hexadecimal string representation | ||
| */ | ||
| private static String toHex(byte b) { | ||
| return String.format("%02X", b & 0xFF); | ||
| } | ||
| } | ||
78 changes: 78 additions & 0 deletions
78
...ils/src/test/java/com/opensourcewithslu/outputdevices/ShiftRegister74HC595HelperTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| package com.opensourcewithslu.outputdevices; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.*; | ||
| import static org.mockito.Mockito.*; | ||
|
|
||
| import java.io.IOException; | ||
|
|
||
| import org.junit.jupiter.api.BeforeEach; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.mockito.Mock; | ||
| import org.mockito.MockitoAnnotations; | ||
|
|
||
| import com.pi4j.io.spi.Spi; | ||
|
|
||
| public class ShiftRegister74HC595HelperTest { | ||
|
|
||
| @Mock | ||
| private Spi spi; | ||
|
|
||
| private ShiftRegister74HC595Helper helper; | ||
|
|
||
| @BeforeEach | ||
| public void setUp() { | ||
| MockitoAnnotations.openMocks(this); | ||
| helper = new ShiftRegister74HC595Helper(spi); | ||
| } | ||
|
|
||
| @Test | ||
| public void testShiftOut_ValidByte() throws IOException { | ||
| byte value = 0x5A; | ||
| helper.shiftOut(value); | ||
| verify(spi, times(1)).write(value); | ||
| } | ||
|
|
||
| @Test | ||
| public void testShiftOut_SpiException() throws IOException { | ||
| byte value = 0x5A; | ||
| doThrow(new RuntimeException("SPI error")).when(spi).write(value); | ||
| IOException exception = assertThrows(IOException.class, () -> helper.shiftOut(value)); | ||
| assertTrue(exception.getMessage().contains("SPI write failed")); | ||
| } | ||
|
|
||
| @Test | ||
| public void testClear() throws IOException { | ||
| helper.clear(); | ||
| verify(spi, times(1)).write((byte) 0x00); | ||
| } | ||
|
|
||
| @Test | ||
| public void testSetBit_ValidIndex() throws IOException { | ||
| helper.setBit(3, true); | ||
| verify(spi, times(1)).write((byte) 0x08); | ||
| helper.setBit(3, false); | ||
| verify(spi, times(1)).write((byte) 0x00); | ||
| } | ||
|
|
||
| @Test | ||
| public void testSetBit_InvalidIndex() { | ||
| IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> helper.setBit(8, true)); | ||
| assertEquals("bitIndex must be 0..7; got 8", exception.getMessage()); | ||
| } | ||
|
|
||
| @Test | ||
| public void testClearBit() throws IOException { | ||
| helper.setBit(2, true); // Set bit 2 first | ||
| verify(spi, times(1)).write((byte) 0x04); | ||
| helper.clearBit(2); | ||
| verify(spi, times(1)).write((byte) 0x00); | ||
| } | ||
|
|
||
| @Test | ||
| public void testGetState() throws IOException { | ||
| assertEquals(0x00, helper.getState()); | ||
| helper.setBit(1, true); | ||
| assertEquals(0x02, helper.getState()); | ||
| } | ||
|
|
||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.