arduino / arduino/ArduinoCore-API
shiftIn and shiftOut do not work with SPI_MODE 3 and 4
- Dominant language
- C++
- Stars
- 306
- Forks
- 150
- PR merge metrics
- No merged PRs in 30d
Description
The solution described here might well fix several other problems in this thread where bits go missing or additional bits appear...
I was trying to use the standard Arduino shiftIn and shiftOut to operate a 74HC165 shift register. I found that it always clocked one too many bits, and therefore shifted all the bits to the left, losing the top one. This did not happen when I used hardware SPI (the SPI library).
I therefore took a close look at the source code for shiftIn and shiftOut in (wiring_shift.c) and soon spotted the problem: shiftIn and shiftOut assume a clock polarity of 0. _**They will therefore only work with SPI_MODE0 and 1.**_
This can be fixed by inverting the clock signal if the SPI_MODE is 3 or 4. I give my solution below. The functions require the SPI mode as an additional parameter, using the Arduino standard definitions SPI_MODE0 etc.
While I was at it, I gained a little speed by taking the test for MSBFIRST or LSBFIRST outside the bit loop and by declaring the method ‘inline’.
I have tested NEWshiftIn with the 74HC165 shift register (SPI_MODE3) and it works fine. I have not tested NEWshiftOut yet as I do not have a MOD3 or 4 device output to test it with. I have also not tested the timings to see if there is a real improvement, as I do not have access to the necessary equipment at the moment. I will be happy to hear from anyone who does any of these tests.
Happy shifting in SPI_MODES 3 and 4!
```cpp
// ===============================================================================================================================
// New version of shiftIn() and shiftOut with the clock polarity bug removed
// Requires SPI_MODE as a parameter
// Also speeded up by taking the bit direction test out of the loop
// Andrew W Symons 9-Aug-2018
//
// SHIFT IN
//
inline uint8_t NEWshiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t spiMode )
{
uint8_t value = 0;
uint8_t i;
uint8_t clock_polarity = spiMode >> 1 ;
switch ( bitOrder )
{
case LSBFIRST:
{
for (i = 0; i < 8; ++i)
{
digitalWrite(clockPin, HIGH ^ clock_polarity );
value |= digitalRead(dataPin) << i;
digitalWrite(clockPin, LOW ^ clock_polarity );
} ;
} ;
break ;
case MSBFIRST:
{
for (i = 0; i < 8; ++i)
{
digitalWrite(clockPin, HIGH ^ clock_polarity );
value |= digitalRead(dataPin) << (7 - i);
digitalWrite(clockPin, LOW ^ clock_polarity );
} ;
} ;
break ;
} ;
return value;
} ;
//
// ===============================================================================================================================
// SHIFT OUT
//
inline void NEWshiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val, uint8_t spiMode )
{
uint8_t i;
uint8_t clock_polarity = spiMode >> 1 ;
switch ( bitOrder )
{
case LSBFIRST:
{
digitalWrite(dataPin, !!(val & (1 << i)));
digitalWrite(clockPin, HIGH ^ clock_polarity );
digitalWrite(clockPin, LOW ^ clock_polarity );
} ;
break ;
case MSBFIRST:
{
digitalWrite(dataPin, !!(val & (1 << (7 - i))));
digitalWrite(clockPin, HIGH ^ clock_polarity );
digitalWrite(clockPin, LOW ^ clock_polarity );
} ;
break ;
} ;
} ;
//
// ===============================================================================================================================
// END OF FILE
//
```
Contributor guide
No contributing guide indexed for this repository
Research direction
Start in wiring_shift.c and compare shiftIn() and shiftOut() with the SPI library's handling of SPI modes. Reproduce the reported 74HC165 behavior in the affected clock modes, then verify that the standard shifting behavior remains correct for existing modes and bit orders. Done means the software functions handle the reported modes without missing or adding bits.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- embedded-iot
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100