The purpose of this little tutorial is to provide newcomers to the PIC with some basic information on how to use a few PIC I/O lines to get many I/O lines. My previous tutorial covered the 74165 type devices used as Parallel to Serial Converters. This was for INPUT to the PIC. This tutorial covers the use of 74164 type devices used as Serial to Parallel Converters. This is for OUTPUT from the PIC. ************ SERIAL TO PARALLEL OUTPUT FROM THE PIC USING 74164 (Basic info will also apply to several other chips, such as 4094) Written by Fr. Tom McGahee tom_mcgahee@sigmais.com 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 A / in front of a signal name indicates it is active low. e.g. /Clear OUTPUT: To get data out of a PIC you can use a 74164 type serial to parallel chip. These chips have: 8 parallel output lines (QA thru QH) a /Clear line that allows a LOW to clear QA-QH to 0 two Serial Input lines that allow serial input of data to the chip a Clock line that synchronizes data transfer on low to high transition Let's look at how each of these is used: All lines are TTL level and will interface directly to the PIC. * 8 parallel output lines. These allow the parallel data received from the serial input to be made available to the 'outside' world. The QH line will hold the first piece of data clocked out, and QA will receive the last piece of data clocked out. Because the 74164 has no output latches, the data will 'ripple' down the outputs as the data is clock in. Note that some applications cannot tolerate this. Some chips (*NOT* the 74164) include a Latch signal that transfers the data from the internal shift register. Such an arrangement may be mandatory if your application cannot tolerate having the data ripple down the Q lines during loading. If you need the latching action, you can either add latches such as 74373 to the 74164, or use a device like the 4094 that includes latching. The 4094 also includes the ability to tristate the outputs using an Output Enable pin. After PIC I/O initialization, move the default pattern you want into the SP_OUT_DATA register and call the SP_OUT routine to get it loaded. Sneaky Trick (1): You can cascade multiple 74164's by paralleling the Clock and /Clear inputs and connecting the QH of the first 74164 to the serial input(s) of the second 74164. You can cascade as many units as desired. See sneaky trick (2) below for a way to get 9 data bits! * The /Clear line is an asynchronous over-riding clear. That simply means that as soon as the /Clear line goes low QA-QH are set low. As long as you hold this line low the circuit will remain cleared. Sneaky Trick (1A): You do not have to use this line at all. Pull it to +5 to disable it. That save you an I/O pin. Of course, if you already have some "/RESET" signal available, feel free to borrow it! Sneaky Trick (1B): You can do a software clear of the 74164 by clocking a bunch of zeroes in. Or a bunch of ones, or any particular desired pattern at all, for that matter. This should be done in the Initialization routine, once the ports are all set up. * The two serial input lines are normally tied together, or one is held high by tying it to +5, and the other is then used as the serial input by itself. The only difference between these two techniques is the loading factor, which is not something you really have to worry about. This serial input line can be driven by a shared I/O line. Since data is only captured on a low to high clock transition, who cares if the PIC plays around with this line at other times? Likely shared-use candidates would be things like the R/W line on an LCD display. You have to be careful using shared I/O lines, but there are times when the techniques can save you an I/O pin. Any I/O exercised within an interrupt routine is probably NOT a good candidate! See sneaky trick (1) above for details on cascading 74164's Sneaky Trick (2): if you are NOT sharing an I/O for the serial input line of the 74164, then you can actually output NINE data bits instead of the usual 8. After Clocking out the regular 8 bits, set the I/O line to the state of the desired 9th bit. The I/O line itself becomes the 9th bit. Not only that, but this 9th bit can do some tricks the other 8 bits can't do: You can change the state of the 9th bit at any time without disturbing any of the other 8 output bits. Note that it will have the same problem that the other 8 output bits have, and that is that the data during loading will 'ripple' through this 9th 'bit'. * The Clock line synchronizes the data transfer. The data present on the serial line at the moment the Clock line goes from low to high will be transferred to the QA output. All previous Q data is shifted in the direction from QA to QH. Clock is a positive pulse. The usual procedure is to have one output from the PIC being used to If you are using a 4094 chip or any chip that has latched outputs (*NOT* the 74164), then you might be able to use the Clock line I/O pin as a shared I/O, since once the data is latched, who cares if you clock the shift registers? Likely shared-use candidates would be things like the R/W line on an LCD display. You have to be careful using shared I/O lines, but there are times when the techniques can save you an I/O pin. Any I/O exercised within an interrupt routine is probably NOT a good candidate! If you do decide to share this pin, then make sure that you create a pulse by setting it high, then low, then high. Do not assume that on initial entry it is in a high state, even though the routine you are in always leaves it in a high state. ************ Now that you understand the basic operation of the 74164, let's look at what PIC resources are required to use it. Remember that you *may* be able to share some of the PIC resources with other devices. Minimal configuration requires two outputs, no inputs. Outputs: (SP_OUT stands for Serial to Parallel Output) SP_OUT_CLOCK SP_OUT_DATA SAMPLE ROUTINE TO OUTPUT 8 BITS VIA SERIAL to PARALLEL 74164 Assume 74164 chip used, and allow port sharing on SP_OUT_DATA Assume Serial Input B is tied to +5 to disable it. Assume Clear is tied to +5 to disable it. SP_OUT_MASK equ 0x10 ;Serial to Parallel OUTput MASK (AND) ; Used to keep track of current bit ; Use any SRAM location desired. Sample 0x10 ; *** This register can be shared with o ther ; *** routines. Local Variable. SP_OUT_REG equ 0x12 ;Storage for 8 bits to be sent out. SP_OUT_CLOCK equ 0x01 ;Assign to PORTA, bit 1 (OUTPUT) SP_OUT_DATA equ 0x03 ;Assign to PORTA, bit 3 (OUTPUT) ;Assume user has set up PIC, including PORTA I/O direction for each bit ;Assume the user enters with the desired output data already ; loaded into SP_OUT_REG SP_OUT: ;Entry label for Serial to Parallel OUTput MOVLW B'00000001' ;Moving Bit Mask (AND). LSB goes out fir st MOVWF SP_OUT_MASK ;Save mask BCF SP_OUT_CLOCK ;Ensure clock is low SP_OUT_LOOP: MOVF SP_OUT_MASK,W ;Load current mask into W ANDWF SP_OUT_REG,W ;AND MASK current bit BTFSS STATUS,Z ;Check for ALL ZERO GOTO SP_OUT_ONE ;If it was a ONE, set data=1 SP_OUT_ZERO: BCF PORTA,SP_OUT_DATA ; else, data is set to 0 GOTO SP_OUT_SHIFT ;Continue... SP_OUT_ONE: BSF PORTA,SP_OUT_DATA ;Data is set to 1 SP_OUT_SHIFT: BSF SP_OUT_CLOCK ;Pulse Clock HIGH BCF SP_OUT_CLOCK ;Then set Clock back to LOW ;At this point the current data bit has been clocked into the shift reg BCF STATUS,C ;Mask shifts in zeroes only via carry RLF SP_OUT_MASK,F ;Shift the current Mask left one bit BTFSS STATUS,C ;If mask shifted into carry, we done! GOTO SP_OUT_LOOP ; else mask is still there, do another b it! RETURN ;Return. Data in SP_OUT_REG is unchanged. As you can see, a moving Mask is used to AND MASK the desired current bit. When that bit exits into the carry, we detect this and use it to signal the end of 8 bits worth of processing. The nice thing about this technique is that it uses few resources, and leaves the original data unchanged. ************* HANDLING THE EXTRA NINTH BIT To handle the extra ninth bit mentioned in Sneaky Trick (2) all we have to do is have a place where the extra ninth bit is stored, and make a small change at the very end of the original program. Minimal configuration requires two outputs, no inputs. Outputs: (SP_OUT stands for Serial to Parallel Output) SP_OUT_CLOCK SP_OUT_DATA SAMPLE ROUTINE TO OUTPUT 8 BITS VIA SERIAL to PARALLEL 74164 Assume 74164 chip used, and allow port sharing on SP_OUT_DATA Assume Serial Input B is tied to +5 to disable it. Assume Clear is tied to +5 to disable it. SP_OUT_MASK equ 0x10 ;Serial to Parallel OUTput MASK (AND) ; Used to keep track of current bit ; Use any SRAM location desired. Sample 0x10 ; *** This register can be shared with o ther ; *** routines. Local Variable. SP_OUT_REG equ 0x12 ;Storage for 8 bits to be sent out. SOME_REGISTER equ 0x13 ;Include storage for extra ninth bit SP_OUT_NINTH_BIT equ 0x07 ;Any available bit will do! SP_OUT_CLOCK equ 0x01 ;Assign to PORTA, bit 1 (OUTPUT) SP_OUT_DATA equ 0x03 ;Assign to PORTA, bit 3 (OUTPUT) ;Assume user has set up PIC, including PORTA I/O direction for each bit ;Assume the user enters with the desired output data already ; loaded into SP_OUT_REG and the ninth bit data is in SOME_REGISTER SP_OUT: ;Entry label for Serial to Parallel OUTput MOVLW B'00000001' ;Moving Bit Mask (AND). LSB goes out fir st MOVWF SP_OUT_MASK ;Save mask BCF SP_OUT_CLOCK ;Ensure clock is low SP_OUT_LOOP: MOVF SP_OUT_MASK,W ;Load current mask into W ANDWF SP_OUT_REG,W ;AND MASK current bit BTFSS STATUS,Z ;Check for ALL ZERO GOTO SP_OUT_ONE ;If it was a ONE, set data=1 SP_OUT_ZERO: BCF PORTA,SP_OUT_DATA ; else, data is set to 0 GOTO SP_OUT_SHIFT ;Continue... SP_OUT_ONE: BSF PORTA,SP_OUT_DATA ;Data is set to 1 SP_OUT_SHIFT: BSF SP_OUT_CLOCK ;Pulse Clock HIGH BCF SP_OUT_CLOCK ;Then set Clock back to LOW ;At this point the current data bit has been clocked into the shift reg BCF STATUS,C ;Mask shifts in zeroes only via carry RLF SP_OUT_MASK,F ;Shift the current Mask left one bit BTFSS STATUS,C ;If mask shifted into carry, we done! GOTO SP_OUT_LOOP ; else mask is still there, do another b it! SP_OUT_BIT_9: BCF PORTA,SP_OUT_DATA ;Assume ninth bit is a zero... BTFSS SOME_REGISTER,SP_OUT_NINTH_BIT ;What is it really? RETURN ;If zero, we guessed right. All done. BSF PORTA,SP_OUT_DATA ; else it was a one, so make it so! RETURN ;Return. Data in SP_OUT_REG is unchanged. Anytime you want to change the data in bit 9, first use BCF SOME_REGISTER,SP_OUT_NINTH_BIT or BSF SOME_REGISTER,SP_OUT_NINTH_BIT to make it 'permanent', and then CALL SP_OUT_BIT_9 to have the existing end of the routine get it into the effective 9th bit of the 74164, which is simply the SP_OUT_DATA line. If you don't use this method and instead just set the 9th bit directly at the SP_OUT_DATA line, then you can simply use the following in-line code: BCF PORTA,SP_OUT_DATA ;Assume ninth bit is a zero... BTFSS SOME_REGISTER,SP_OUT_NINTH_BIT ;What is it really? GOTO CONTINUE ;If zero, we guessed right. All done. BSF PORTA,SP_OUT_DATA ; else it was a one, so make it so! CONTINUE: ;end of in-line coding This inline method has not 'registered' the change permanently. The next time the SP_OUT routine is called, the stored value will be set up as usual. *************** HANDLING MULTIPLES OF 8 BITS, such as a 16 bit register The hardware must be properly cascaded as mentioned earlier. To handle multiples of 8 bits you COULD just load the bytes into SP_OUT_REG and call SP_OUT once for each byte. I will illustrate a 2 byte example, where one of the bytes is in a register name REG1, and the other in REG2. MOVF REG1,W MOVWF SP_OUT_REG CALL SP_OUT MOVF REG2,W MOVWF SP_OUT_REG CALL SP_OUT In my earlier tutorial on Parallel to Serial INPUT Converters, I showed the multi-byte example using an indirect addressing method. The indirect addressing method does have the advantage of saving one memory register. Minimal configuration requires two outputs, no inputs. Outputs: (SP_OUT stands for Serial to Parallel Output) SP_OUT_CLOCK SP_OUT_DATA SAMPLE ROUTINE TO OUTPUT 8 BITS VIA SERIAL to PARALLEL 74164 Assume 74164 chip used, and allow port sharing on SP_OUT_DATA Assume Serial Input B is tied to +5 to disable it. Assume Clear is tied to +5 to disable it. SP_OUT_MASK equ 0x10 ;Serial to Parallel OUTput MASK (AND) ; Used to keep track of current bit ; Use any SRAM location desired. Sample 0x10 ; *** This register can be shared with o ther ; *** routines. Local Variable. SP_OUT_REG1 equ 0x12 ;Storage for 1st 8 bits to be sent out. SP_OUT_REG2 equ 0x13 ;Storage for 2nd 8 bits to be sent out. SP_OUT_CLOCK equ 0x01 ;Assign to PORTA, bit 1 (OUTPUT) SP_OUT_DATA equ 0x03 ;Assign to PORTA, bit 3 (OUTPUT) ;Assume user has set up PIC, including PORTA I/O direction for each bit ;Assume the user enters with the desired output data already ; loaded into SP_OUT_REG1 and SP_OUT_REG2 SP_OUT_FSR: MOVLW PS_OUT_REG1 ;Set FSR to PS_OUT_REG1 and Send it! MOVWF FSR CALL SP_OUT MOVLW PS_OUT_REG2 ;Set FSR to PS_OUT_REG2 and Send it! MOVWF FSR ;No need for a CALL to SP_OUT here... ;We just fall right into it! SP_OUT: ;Entry label for Serial to Parallel OUTput MOVLW B'00000001' ;Moving Bit Mask (AND). LSB goes out fir st MOVWF SP_OUT_MASK ;Save mask BCF SP_OUT_CLOCK ;Ensure clock is low SP_OUT_LOOP: MOVF SP_OUT_MASK,W ;Load current mask into W ANDWF INDF,W ;AND MASK current bit BTFSS STATUS,Z ;Check for ALL ZERO GOTO SP_OUT_ONE ;If it was a ONE, set data=1 SP_OUT_ZERO: BCF PORTA,SP_OUT_DATA ; else, data is set to 0 GOTO SP_OUT_SHIFT ;Continue... SP_OUT_ONE: BSF PORTA,SP_OUT_DATA ;Data is set to 1 SP_OUT_SHIFT: BSF SP_OUT_CLOCK ;Pulse Clock HIGH BCF SP_OUT_CLOCK ;Then set Clock back to LOW ;At this point the current data bit has been clocked into the shift reg BCF STATUS,C ;Mask shifts in zeroes only via carry RLF SP_OUT_MASK,F ;Shift the current Mask left one bit BTFSS STATUS,C ;If mask shifted into carry, we done! GOTO SP_OUT_LOOP ; else mask is still there, do another b it! RETURN ;Return. Data in SP_OUT_REG is unchanged. You can add in the stuff to handle the 9th bit, too if you want. It is added at the same place as before. File away for future reference. Hope this helps. Fr. Tom McGahee