How to use dual 7 segment LED's

These tutorials demonstrate how to use a multiplexed 7 segment LED display, these tutorials use just a dual display (because it fits easily on a single port), but the technique can easily be extended to use larger displays (and commonly is).

To reduce the pin count required, the two displays are multiplexed - this means we need seven I/O pins, one for each segment of the LED (ignoring the decimal point), and two to select the individual sections - unfortunately this would require nine I/O pins - taking more than one port. To over come this I've designed the 7 segment board to select the two sections using just one pin - the two transistors are wired as inverters, this means that when Q1 is ON, Q2 is OFF, and when Q1 is OFF, Q2 is ON - so by switching port pin 7 we can alternate between the two sections.

The technique used for the display is to show each section in turn, so we display section 1, then section 2 - as long as we do this fast enough you can't see any flickering - this obviously restricts what else the program can be doing, as we must refresh the display regularly enough to prevent visible flicker. While this can be done in normal straight code, it does mean that the display code has to be an integral part of the program - precluding it's use as a reusable module. For this reason I've chosen to use interrupts!.

The interrupts are generated by Timer2, which is set to give an interrupt roughly every 16mS. Interrupts in a PIC cause the program to stop what it's doing, save it's current location, and jump to the 'Interrupt Vector' - which on a PIC is address 0x0004. The interrupt routine then does what it needs to do and exits using the 'REFIE' (REturn From IntErrupt) command - this works rather like a normal 'RETURN' in that it returns the program to where it was when the interrupt was called. An important point to bear in mind when using interrupts is that you mustn't change anything the main program is using - for that reason the first thing we must do is save various resisters - the W register, the PCLATH register, and the STATUS register - these are saved in data registers allocated for their use, and are restored before the routine exits via 'RETFIE'.

The interrupt routine is shown below, the first 5 lines save the registers mentioned above, the 'swapf' command is used as it doesn't affect the STATUS register, there's not much point saving the register if we've already changed it!. Next we check is the interrupt was generated by the timer or not, in this case we're only using one interrupt source (TIMER2) - but often you may be using multiple interrupt sources, if it's not TIMER2 we simply jump to the EXIT routine, which restores the registers saved and exits via RETFIE.

Now we start the actual interrupt routine itself, the first thing we have to do is reset TIMER2 - once it's triggered an interrupt it turns itself off, so we turn it back on here (bcf PIR1,TMR2IF). Next we start our display routine, first we need to find out which display we're updating - in this particular case it's very simple, bit 7 of the port selects each display, so by testing bit 7 we can see which we need to update. The values for the two digits are stored in the variables 'ones' and 'tens', and can contain the value 0 to 15 (0 to 0x0F HEX), although only 0-9 are used here (A-F give a blank display) - so we first move the value into the W register and AND it with 0x0F (to make sure it can't be any higher than 15), then 'call LED_Table' - this is a table which gives the correct segment pattern for each number - you'll notice that on the circuit diagram I didn't label which segment was which, this was because I wired them as simply as possible - we can sort out which is which in this table. Here the two digits vary slightly, for the 'ones' digit we need to clear bit 7 - and do so with the 'andlw 0x7F', for the 'tens' we need to set bit 7 - and do so with the 'iorlw 0x80', in both case we then simply write the W register to the port and exit the interrupt routine.

; Interrupt vector

ORG 0x0004


INT
movwf w_temp ; Save W register
swapf STATUS,W ; Swap status to be saved into W
movwf s_temp ; Save STATUS register
movfw PCLATH
movwf p_temp ; Save PCLATH

btfss PIR1,TMR2IF ; Flag set if TMR2 interrupt
goto INTX ; Jump if not timed out

; Timer (TMR2) timeout every 16 milliseconds


bcf PIR1,TMR2IF ; Clear the calling flag


btfss 7SEG_PORT, 7 ;check which LED was last
goto Do_tens
movfw ones
andlw 0x0F ;make sure in range of table
call LED_Table
andlw 0x7F ;set to correct LED
movwf 7SEG_PORT
goto INTX

Do_tens movfw tens
andlw 0x0F ;make sure in range of table
call LED_Table
iorlw 0x80 ;set to correct LED
movwf 7SEG_PORT



INTX
movfw p_temp
movwf PCLATH ; Restore PCLATH
swapf s_temp,W
movwf STATUS ; Restore STATUS register - restores bank
swapf w_temp,F
swapf w_temp,W ; Restore W register
retfie


The interrupt routine above is complete and working, but it requires TIMER2 setting up before it can work, this is done in the 'Initialise' section of the program, which is the first section called when the program runs. This first turns the comparators off, then sets the direction register for the Port to all outputs, then sets TIMER2 to the correct time, you will notice there's two lines (with one commented out) for setting the value of T2CON - if you comment out the second one, and use the first one, it makes the interrupt routine timing too slow - you can actually see the digits flickering, first one then the other - worth doing so you can see what's going on. After that we set up PR2, this sets the value that the timer counts to before it times out - then we enable TIMER2 interrupts by setting the TMR2IE flag in register PIE1. This still isn't enough, so finally we write to the INTCON register to enable 'Peripheral Interrupts' and 'Global Interrupts'. The last two lines simply zero the two display variables, and aren't part of the TIMER2 setup routine.

Initialise movlw 0x07
movwf CMCON ;turn comparators off (make it like a 16F84)
bsf STATUS, RP0 ;select bank 1
movlw b'00000000' ;Set port data directions, data output
movwf 7SEG_TRIS
bcf STATUS, RP0 ;select bank 0


; Set up Timer 2.

;movlw b'01111110' ; Post scale /16, pre scale /16, TMR2 ON
movlw b'00010110' ; Post scale /4, pre scale /16, TMR2 ON
movwf T2CON

bsf STATUS, RP0 ;select bank 1

movlw .249 ; Set up comparator
movwf PR2

bsf PIE1,TMR2IE ; Enable TMR2 interrupt

bcf STATUS, RP0 ;select bank 0

; Global interrupt enable

bsf INTCON,PEIE ; Enable all peripheral interrupts
bsf INTCON,GIE ; Global interrupt enable

bcf STATUS, RP0 ;select bank 0

clrf tens
clrf ones

This is the main section of the first program, as you can see there's not a great deal to it, all it does it delay about 1 second, then do a decimal increment of 'ones' and 'tens' - by a decimal increment I mean once the 'ones' digit gets to '10' we reset the 'ones' digit to zero and increment the 'tens' digit'. We do the same with the 'tens' digit, except we don't have a 'hundreds' digit so we throw the carry away (no 'incf'). The program simply increments 'ones' and 'tens' from '00' to '99' at roughly 1 second intervals, then loops back to '00' and starts again. Notice that this section of the program has no connection to any kind of I/O or display routine, all it does is increment two variables - the display function is handled totally transparently by the interrupt routine - all we need to do is place the values we want in 'ones' and 'tens'.

Main
call Delay255
call Delay255
call Delay255
call Delay255
incf ones, f
movf ones, w ;test first digit
sublw 0x0A
btfss STATUS, Z
goto Main
clrf ones
incf tens, f
movf tens, w ;test second digit
sublw 0x0A
btfss STATUS, Z
goto Main
clrf tens
goto Main ;loop for ever

Tutorial 10.1

This tutorial implements a simple 0-99 counter.

Twitter Delicious Facebook Digg Stumbleupon Favorites More

 
Design by Engr.Arif Naseem | Bloggerized by Naseem - | Affiliate Network Reviews