User Tools

Site Tools


shift_registers_and_how_to_use_them_in_smart_ways

From: Thomas McGahee tom_mcgahee@sigmais.com To: pic microcontroller discussion list PICLIST@MITVMA.MIT.EDU Subject: Fr. Tom's Shift Register Tutorial Date: Thursday, June 18, 1998 1:34 PM

Stewart, I do not use Basic Stamps at all, but I do use shift registers with PICs. The info I will give is pretty much applicable to any kind of microcontroller, and I have even used the techniques with IBM PC parallel ports when necessary. You should be able to apply the info given to a Basic Stamp without much trouble, but for the sake of helping the most PIC list members as possible, I will speak in terms of the PIC, not the Basic Stamp.

This posting will contain just the info concerning multi-bit INPUT to a PIC using serial techniques. If enough members find this useful, then I might prepare a similar tutorial on multi-bit OUTPUT from a PIC using serial techniques. No sense in my wasting my time if no one is interested in this stuff.

* PARALLEL TO SERIAL INPUT TO A PIC USING A 74165 (Basic info will also apply to several other chips) Written by Fr. Tom McGahee tom_mcgahee@sigmais,com copyright 1998 As a courtesy to the author, please include the above info if you share this information with others. Permission granted for personal use.

If you find any errors, or have any additions to be made to the text, or have any comments to make about this tutorial, please notify me by e-mail: tom_mcgahee@sigmais.com

INPUT: To get data INTO a PIC you can use a 74165 type parallel to serial chip. These chips have:

8 parallel input lines
A serial input line that allows you to cascade multiple chips
A Clock and /Clock Inhibit
An asynchronous Shift /Load control
Both a normal and inverted serial output (Q and /Q)

Let's look at how each of these is used: All lines are TTL level and will interface directly to the PIC.

* 8 parallel input lines These receive the parallel data from the 'outside' world. This can be from switches, transistors, TTL or CMOS devices, etc. Note that more than one 74165 can be cascaded, so that any number of parallel input lines get funneled into a single bit input port.

* The serial input line that allows you to cascade multiple chips. You only use this if you are connecting multiple 74165 chips together. If this is the case, then the Q output of the preceding stage connects to this line. If you do not wish to use it, then tie it either to ground or +5.

Hint: When cascading 74165 chips together, connect the Clock1 to Clock2, /Clock Inhibit1 to /Clock Inhibit2, and Shift /Load Control1 to Shift /Load Control2. That way the PIC will control both in synchronization. You can cascade as many 74165s as you want.

Sneaky Trick (1): You can use the serial input as a 9th input so long as you keep the following in mind: All other inputs are latched when the /Load signal is pulsed low, but NOT *this* 'input'. It is effectively 'captured' as soon as you perform the first shift using the clock, however.

* The Clock and /Clock Inhibit Tie the Clock Inhibit LOW to disable the inhibit permanently. The rising edge of the Clock will cause the latched data present in the shift register to be shifted.

Gotchas: Note that the rightmost bit of parallel data should be read in BEFORE doing any shifting with the clock! Once you perform a shift, the data moves to the right, and the rightmost digit is LOST. In other words, always perform a Read *before* Shifting.

Sneaky Trick (2A): The Clock and the /Clock Inhibit lines are inter-changeable. When laying out a PC board, you may swap pins 1 and 2. Also, you can just tie Clock and /Clock Inhibit together and treat them simply as a combined Clock. This does increase the TTL loading, but it is not a problem.

Sneaky Trick (2B): You can often SHARE this output with other devices. If you are not running the input routine via an interrupt, then you can safely share this output. You can get away with this, because you will generally do a /Load just prior to assembling the data from the serial port. So, if some other routine has previously used this output, *who cares*? You *DO* have to make sure that the sharing is OK from the other device's point of view, of course. Some outputs are sort of like 'set up' bits, such as the R/W line on an LCD display. You can flip 'em around all you want, as long as they are set up properly JUST PRIOR to actually issuing a device command. Sharing a single output line in such a case makes a lot of sense, but it is not immediately evident to some people that this is allowable.

* The asynchronous Shift /Load control This line is normally held high. You pulse it low when you want to latch the current state of the inputs. While this line is low, clocking is automatically inhibited.

Sneaky Trick (3A): If you have the Shift /Load Control held low (/Load), then the Q output will follow the state of the rightmost parallel input. This allows you to monitor this particular line in real-time. No shifting required to read it, and if attached to an input that allows interrupt on change, then you can use this trick to cause the interrupt routine to automatically be triggered by a pulse on the last parallel input. The interrupt routine can then shift in the rest of the data. Note that if you combine this trick with the sneaky trick (1), then you can get a full 8 'static' data bits and one 'dynamic' data bit that can initiate an interrupt.

Sneaky Trick (3B): You can often SHARE this output with other devices. If you are not running the input routine via an interrupt, then you can safely share this output. You can get away with this, because you will generally do a /Load just prior to assembling the data from the serial port. You may have problems if you try to combine sneaky tricks (3A) and (3B). So, if some other routine has previously used this output, *who cares*? You *DO* have to make sure that the sharing is OK from the other device's point of view, of course. Some outputs are sort of like 'set up' bits, such as the R/W line on an LCD display. You can flip 'em around all you want, as long as they are set up properly JUST PRIOR to actually issuing a device command. Sharing a single output line in such a case makes a lot of sense, but it is not immediately evident to some people that this is allowable.

* Normal and inverted serial output (Q and /Q) Usually you just use the normal Q output. This will become our serial input. As mentioned above, Q will follow the state of the rightmost bit whenever Shift /Load is low. Immediately following a /Load pulse, Q will show the latched state of the rightmost parallel input. Following each Clock pulse the data at Q will reflect the 'latched' state of the next bit.

Sneaky Trick (4) You might be wondering whether or not you can 'share' a single PIC input to handle this and another device. You can, but it requires some way of determining which input is 'active'. Here is where you can use the same PIC output that controls Shift /Load to do double duty! Whenever you are outputting a HIGH to the Shift /Load control, you could use that same high to control a two-input multiplexer such that it directs the Q output data into the PIC serial input port. If using trick (3B) in this context, you CANNOT also use trick (3A), since activating /Load would disable the Q from getting to the PIC port. If you want to use trick (3A), you would have to control the multiplexer with some other PIC output bit.

Now that you understand the operation of the 74165, let's look at what PIC resources are required to use it. Remember that you *may* be able to share the PIC resources with other devices.

PIC PORT REQUIREMENTS FOR PARALLEL to SERIAL INPUT PORT

Two outputs, one input. (PS_IN stands for Parallel to Serial INput device) Outputs: PS_IN_CLOCK and PS_IN_SHIFT_NOT_LOAD (I like descriptive names). Input: PS_IN_SDATA

SAMPLE ROUTINE TO READ 8 BITS FROM PARALLEL TO SERIAL PORT Assume 74165 chip used, and allow port sharing on both outputs. Assume Clock Inhibit is tied LOW.

PS_IN_CTR equ 0x10 ;Parallel to Serial INput CouNTer

					; Used to keep track of current bit
					; Use any SRAM location desired. Sample 0x10
					; *** This register can be shared with other
					; *** routines. Local Variable.

PS_IN_REG equ 0x11 ;Storage for 8 bit serial input data

					; Use any SRAM location desired. Sample 0x11

PS_IN_CLOCK equ 0x01 ;Assign to PORTA, bit 1 (OUTPUT) PS_IN_SHIFT_NOT_LOAD equ 0x02 ;Assign to PORTA, bit 2 (OUTPUT) PS_IN_DATA equ 0x03 ;Assign to PORTA, bit 3 (INPUT)

;Assume user has set up PIC, including PORTA I/O direction for each bit

PS_IN: ;Entry Label for Parallel to Serial INput

	MOVLW	D'8'			;8 bits to be read
	MOVWF	PS_IN_CTR		;Save count
	BCF	PS_IN_CLOCK		;Ensure that clock is low (may be shared)
	BCF	PORTA,PS_IN_SHIFT_NOT_LOAD
					;That LOW latched the data for sure
	BSF	PORTA,PS_IN_SHIFT_NOT_LOAD
					;Now we keep it high so data doesn't change

PS_IN_LOOP:

	BTFSS	PORTA,PS_IN_DATA	;Read serial data from port one bit at a time
	GOTO	GOT_ZERO
	BSF	STATUS,C		;Load a 1 into carry if bit was a 1
	GOTO	SHIFT_IN

GOT_ZERO:

	BCF	STATUS,C		;Load a 0 into carry if bit was a 0	

SHIFT_IN:

	RRC	PS_IN_REG		;Assemble bit into right-shifting byte
	BSF	PORTA,PS_IN_CLOCK	;Shift the data in the 74165 to get ready
					; to possibly read the NEXT bit...
	BCF	PORTA,PS_IN_CLOCK	;Terminate the Clock pulse!
	DECFSZ PS_IN_CTR,F	;Update our counter
	GOTO	PS_IN_LOOP		;If not done, do next bit
	RETURN			;Return with data in PS_IN_REG

There, now, that wasn't so hard, was it? There is more than one way to skin a cat, of course, and I am sure that there are others out there who have implemented this differently.

* HANDLING EXTRA NINTH BIT

With a few additions, this routine can be modified to handle ANY number of inputs, though you would usually handle multiples of 8.

To handle the '9th' bit referenced in sneaky trick (1), requires some additional code. Here is the simplest way:

Assume user has defined a register called SOME_REGISTER, and assigned a bit position called PS_IN_NINTH_BIT

PIC PORT REQUIREMENTS FOR PARALLEL to SERIAL INPUT PORT

Two outputs, one input. (PS_IN stands for Parallel to Serial INput device) Outputs: PS_IN_CLOCK and PS_IN_SHIFT_NOT_LOAD (I like descriptive names). Input: PS_IN_SDATA

SAMPLE ROUTINE TO READ 8 BITS FROM PARALLEL TO SERIAL PORT Assume 74165 chip used, and allow port sharing on both outputs. Assume Clock Inhibit is tied LOW.

PS_IN_CTR equ 0x10 ;Parallel to Serial INput CouNTer

					; Used to keep track of current bit
					; Use any SRAM location desired. Sample 0x10
					; *** This register can be shared with other
					; *** routines. Local Variable.

PS_IN_REG equ 0x11 ;Storage for 8 bit serial input data

					; Use any SRAM location desired. Sample 0x11

SOME_REGISTER equ 0x12 ;We need one bit of this register PS_IN_NINTH_BIT equ 0x07 ;Any available bit will do!

PS_IN_CLOCK equ 0x01 ;Assign to PORTA, bit 1 (OUTPUT) PS_IN_SHIFT_NOT_LOAD equ 0x02 ;Assign to PORTA, bit 2 (OUTPUT) PS_IN_DATA equ 0x03 ;Assign to PORTA, bit 3 (INPUT)

;Assume user has set up PIC, including PORTA I/O direction for each bit

PS_IN: ;Entry Label for Parallel to Serial INput

	MOVLW	D'8'			;8 bits to be read
	MOVWF	PS_IN_CTR		;Save count
	BCF	PS_IN_CLOCK		;Ensure that clock is low (may be shared)
	BCF	PORTA,PS_IN_SHIFT_NOT_LOAD
					;That LOW latched the data for sure
	BSF	PORTA,PS_IN_SHIFT_NOT_LOAD
					;Now we keep it high so data doesn't change

PS_IN_LOOP:

	BTFSS	PORTA,PS_IN_DATA	;Read serial data from port one bit at a time
	GOTO	GOT_ZERO
	BSF	STATUS,C		;Load a 1 into carry if bit was a 1
	GOTO	SHIFT_IN

GOT_ZERO:

	BCF	STATUS,C		;Load a 0 into carry if bit was a 0	

SHIFT_IN:

	RRC	PS_IN_REG		;Assemble bit into right-shifting byte
	BSF	PORTA,PS_IN_CLOCK	;Shift the data in the 74165 to get ready
					; to possibly read the NEXT bit...
	BCF	PORTA,PS_IN_CLOCK	;Terminate the Clock pulse!
	DECFSZ PS_IN_CTR,F	;Update our counter
	GOTO	PS_IN_LOOP		;If not done, do next bit

GET_BIT_9: ;So far all has been exactly the same

					; as the 8 bit example. Now we add the
					; extra code to handle the ninth bit.
	BCF	SOME_REGISTER,PS_IN_NINTH_BIT
					;Assume it's a 0
	BTFSS PORTA,PS_IN_DATA	;Get extra (for free!) ninth bit
	GOTO	PS_IN_RET		;If it was a 0, we are already done!
	BSF	SOME_REGISTER,PS_IN_NINTH_BIT
					;If it was a 1, make it so! 

PS_IN_RET:

	RETURN			;Return with data in PS_IN_REG

* HANDLING MULTIPLES OF 8 BITS

To handle multiples of 8 bits you would use FSR and indirect addressing to load first 8 bits into one register, then the next 8 into the next register, etc. References to PS_IN_REG would be changed to references to INDF. Obviously FSR would have to be initially loaded with the address of the first storage register, and FSR would have to be incremented after 8 bits have been loaded.

The best way to implement that would be to have a routine that called the generic 8 bit routine using INDF twice, each time with FSR containing a different register address.

PIC PORT REQUIREMENTS FOR N*8 PARALLEL to SERIAL INPUT PORT

Two outputs, one input. (PS_IN stands for Parallel to Serial INput device) Outputs: PS_IN_CLOCK and PS_IN_SHIFT_NOT_LOAD (I like descriptive names). Input: PS_IN_SDATA

SAMPLE ROUTINE TO READ MULTIPLES of 8 BITS FROM PARALLEL TO SERIAL PORT Assume 74165 chips used, and allow port sharing on both outputs. Assume Clock Inhibit is tied LOW.

PS_IN_CTR equ 0x10 ;Parallel to Serial INput CouNTer

					; Used to keep track of current bit
					; Use any SRAM location desired. Sample 0x10
					; *** This register can be shared with other
					; *** routines. Local Variable.

PS_IN_REG1 equ 0x11 ;Storage for 8 bit serial input data

					; Use any SRAM location desired. Sample 0x11

PS_IN_REG2 equ 0x12 ;Storage for 8 bit serial input data

					; should be contiguous with PS_IN_REG1

PS_IN_CLOCK equ 0x01 ;Assign to PORTA, bit 1 (OUTPUT) PS_IN_SHIFT_NOT_LOAD equ 0x02 ;Assign to PORTA, bit 2 (OUTPUT) PS_IN_DATA equ 0x03 ;Assign to PORTA, bit 3 (INPUT)

;Assume user has set up PIC, including PORTA I/O direction for each bit

PS_IN_FSR: ;This allows multi-byte transfers.

	MOVLW	PS_IN_REG1
	MOVWF	FSR			;Point to PS_IN_REG1 for 1st byte
	CALL	PS_IN			;Do it!
	INCF	FSR,F			;Point to next register, PS_IN_REG2
	CALL	PS_IN			;Do it, too!
	RETURN			;That's all, folks!	

PS_IN: ;Entry Label for Parallel to Serial INput

	MOVLW	D'8'			;8 bits to be read
	MOVWF	PS_IN_CTR		;Save count
	BCF	PS_IN_CLOCK		;Ensure that clock is low (may be shared)
	BCF	PORTA,PS_IN_SHIFT_NOT_LOAD
					;That LOW latched the data for sure
	BSF	PORTA,PS_IN_SHIFT_NOT_LOAD
					;Now we keep it high so data doesn't change

PS_IN_LOOP:

	BTFSS	PORTA,PS_IN_DATA	;Read serial data from port one bit at a time
	GOTO	GOT_ZERO
	BSF	STATUS,C		;Load a 1 into carry if bit was a 1
	GOTO	SHIFT_IN

GOT_ZERO:

	BCF	STATUS,C		;Load a 0 into carry if bit was a 0	

SHIFT_IN:

	RRC	INDF			;Assemble bit into right-shifting byte
					; Indirect addressing handles multi bytes
	BSF	PORTA,PS_IN_CLOCK	;Shift the data in the 74165 to get ready
					; to possibly read the NEXT bit...
	BCF	PORTA,PS_IN_CLOCK	;Terminate the Clock pulse!
	DECFSZ PS_IN_CTR,F	;Update our counter
	GOTO	PS_IN_LOOP		;If not done, do next bit
	RETURN			;Return with data in register pointed to by FSR

File away for future reference.

Hope this helps, Fr. Tom McGahee


From: Stewart McCallum pdp@PC-LAND.COM
To: PICLIST@MITVMA.MIT.EDU
Subject: Shift Registers & Stamps
Date: Thursday, June 18, 1998 12:37 AM

Can anyone shed some light on using shift registers with stamps to control

outputs and inputs.


Any help would be appreciated.
Stewart McCallum
Vancouver, Canada
shift_registers_and_how_to_use_them_in_smart_ways.txt · Last modified: 2019/08/27 20:45 (external edit)