Arduino shiftIn : How to get serial data from chips.
Here you can find out how Arduino shiftIn works and how fast it is. The
main use for the function to receive serial input from a parallel
to serial chip e.g. 74HC165 (8 bits).
This allows you to increase the
number of inputs to the processor using only two processor pins (you can
daisy chain chips for even more inputs on the same two pins).
Arduino shiftIn is a purely
software implementation of a serial input interface; The equivalent
hardware interface is SPI (Although shiftIn() represents half of that interface i.e. the data input part).
Many chips use a serial interface to reduce the number of physical
pins, so instead of using a parallel processor bus to transfer data 8 or
16 bits at a time, two signals (clock and data) get data from the
device, one bit at a time.
Arduino ShiftIn() uses two signal pins (a digital output pin, and a
digital input pin - i.e. almost any of the arduino pins) and generates a
clock signal on the output, and receives data on the input, using the
digitalRead() function.
The source code for Arduino shiftIn is contained in wiring_shift.c. The path is:
C:\Program Files\Arduino\hardware\arduino\avr\cores\arduino\wiring_shift.c
Note constants such as LSBFIRST are defined in Arduino.h, in the same directory.
uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {
uint8_t value = 0;
uint8_t i;
for (i = 0; i < 8; ++i) {
digitalWrite(clockPin, HIGH);
if (bitOrder == LSBFIRST)
value |= digitalRead(dataPin) << i;
else
value |= digitalRead(dataPin) << (7 - i);
digitalWrite(clockPin, LOW);
}
return value;
}
There are three parameters :
The function returns the 8 bits of data from the device as a byte (uint8_t).
The basic operation is to loop through each bit using the for-loop.
In the loop the first action is to raise the clock signal output to high.
Then input value from digitalRead() is shifted left by the current
loop value (i) and ORed into the variable 'value'. After this, the clock
signal is set low.
In this way the data line is read each time after the clock is set
high. Each input bit is placed into the 'value' variable, in its bit
shifted position, until 8 bits are retrieved. Then the function returns
the variable 'value'.
The code works by using the bit shift operator '<<' which shifts all the bits in a variable one to the left.
Assume the bitOrder is LSBFIRST then for the first loop iteration i
is zero and the term (1<<i) shifts a variable left by zero places
i.e. it leaves the variable with the value 1.
00000001B
The next time around the loop the variable holds the value 1 shifted left by one place:
00000010B
This repeats 6 more times
00000100B
00001000B
00010000B
00100000B
01000000B
10000000B
In this way a walking '1' is created moving from b0 to b7.
For a bitOrder of MSBFIRST, the value of the index is manipulated to
move from b7 to b0 for each value of i. So the walking '1' walks in the
opposite direction, starting from bit 7.
Since the variable 'value' is initialised to zero at the start of the
routine, each incoming data bit is ORed into place in the variable.
This is different to shiftOut(), where AND was used. This is because ORing adds the bits into the same variable (leaving the others unaltered), whereas ANDing isolates each bit from the rest (for output).
The function uses digitalRead() so it should be similar to the
speed of digitalWrite() - in that link the pulse period (for an Arduino
Uno) is 7us.
Since 8 pulses are needed, shiftIn() should take about 56us plus a bit more for bit manipulation and looping round the for-loop. In fact the extra operations in shiftIn() double this time (see below for timings).
The following sketch outputs a marker signal on pin 7 (so you can
trigger a scope on the start of the serial data) and alternates data
with a pattern 0x55 and 0x99 - an alternating data pattern.
// Demonstration code for shiftIn
int MARK = 7;
int SERCLK = 6;
int SERDATA = 5;
void setup() {
pinMode(MARK, OUTPUT);
pinMode(SERDATA, INPUT);
pinMode(SERCLK, OUTPUT);
noInterrupts();
}
void loop() {
byte d;
digitalWrite(MARK, HIGH);
d = shiftIn(SERDATA, SERCLK, MSBFIRST);
digitalWrite(MARK, LOW);
digitalWrite(SERDATA, LOW); // Stop scope output flicker.
delay(10);
}
The oscilloscope output marker signal
120us (longest signal). The period of the clock signal is: 14.37us (~69.6kHz).
However you would normally use the marker signal as the chip select,
so it is representative of the achievable timing (CS would normally be
inverted so that it is active low).
The yellow line shows the clock signal on pin 6
The next is the marker signal (blue) on pin 7.
The timebase is 10us and the amplitude is 2V/div.
Comments
Have your say about what you just read! Leave me a comment in the box below.
Don’t see the comments box? Log in to your Facebook account, give Facebook consent, then return to this page and refresh it.