Building a 2-person "quiz reaction timer" with a microcontroller

Discussion in 'The Projects Forum' started by farscape, May 16, 2010.

  1. farscape

    Thread Starter New Member

    May 16, 2010
    26
    0
    Hi,

    I'm taking an intro to digital electronics class and we have a final project where we need to build a 2 person "game show" quiz reaction timer. I feel like I'm in way over my head at this point and I would appreciate any and all help to get me started with the basic setup.

    Here are the basic instructions:

    The game has 2 players, each with a a button connected to an LED

    A reset button is pushed and there is a 9-second countdown on a 7-segment LED display. After the last count, there is a final 1 second delay, after which the first player to hit his/her button has their LED light up while the second player's LED is locked out

    If somebody "jumps the gun" and hits their button before the time has run out then the other player's LED is lit and the round is over.

    Were required to use PIC microcontroller, so I have to figure out how the circuit should be set up as well as the code for the process I described.

    We've had a little practice with basic programming (decrement commands, etc.) but I'm at a loss as to how to program the PIC to carry out all these requirements. If this doesn't make sense, let me know, and I'll try to clarify.

    Thanks
     
  2. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    Which PIC will you be using? As long as you have at least 12 IO ports the circuit should be simple.
    Will you be using assembler or C? And if C which make?
    You should draw up a schematic and post it here for someone to check. Hardware first, then software.
    You could consider using an interrupt to check if the buttons have been pressed.
    You could use the MCLR pin for the reset button or just a normal IO pin.
     
  3. farscape

    Thread Starter New Member

    May 16, 2010
    26
    0
    Hi, thanks for the response.

    -We've been using the PIC16F84A which has 13 I/O ports so I'm guessing from what you said that will be adequate. We're not required to use a specific one.
    -We are using assembly language

    Ive drawn up and attached a basic hardware schematic, but any corrections will be much appreciated.
    -I'm confused about the function/purpose of the crystal oscillator and how it interacts with the OSC1 and OSC2 pins of the chip. Is it necessary? (We used this in an earlier lab this semester with the same connections).
    -Are my resistor values in the right ballpark?
    -Can the RB0/INT pin be used to block signals from the slower players input? I cant quite figure out how this comes into play.

    The programming is whats really scaring me, but Ill start on that and see what I can come up with. Any tips on how to get started?

    Thanks for the help
     
  4. farscape

    Thread Starter New Member

    May 16, 2010
    26
    0
    Edit: Here's the attachment
     
  5. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    Not far off. The MCLR switch and resistor should be swapped because reset is low voltage.
    If your crystal is a 2 pin device you need a couple of capacitors to ground. If it is a 3 pin resonator then your circuit is fine.
    The LEDs are drawn the wrong way round, but you would probably spot that quickly enough.
    VSS and VDD are the wrong way round too.
    Assuming you have a common cathode 7 segment, that is fine.

    I'd be tempted to use RA0-Ra3 and RB0-RB2 for the 7 segment and then you can use the interrupt on change feature of RB4 and RB5 for the user buttons.

    The crystal is necessary with this PIC but if you used the 16F628 instead you could use the internal oscillator.

    Resistor values are fine.

    Using the code template from the Microchip site makes things easier. You will have to change the CONFIG statement for your requirements.
    Code ( (Unknown Language)):
    1.  
    2. ;**********************************************************************
    3. ;   This file is a basic code template for assembly code generation   *
    4. ;   on the PIC16F84A. This file contains the basic code               *
    5. ;   building blocks to build upon.                                    *  
    6. ;                                                                     *
    7. ;   Refer to the MPASM User's Guide for additional information on     *
    8. ;   features of the assembler (Document DS33014).                     *
    9. ;                                                                     *
    10. ;   Refer to the respective PIC data sheet for additional             *
    11. ;   information on the instruction set.                               *
    12. ;                                                                     *
    13. ;**********************************************************************
    14. ;                                                                     *
    15. ;    Filename:        xxx.asm                                           *
    16. ;    Date:                                                            *
    17. ;    File Version:                                                    *
    18. ;                                                                     *
    19. ;    Author:                                                          *
    20. ;    Company:                                                         *
    21. ;                                                                     *
    22. ;                                                                     *
    23. ;**********************************************************************
    24. ;                                                                     *
    25. ;    Files Required: P16F84A.INC                                      *
    26. ;                                                                     *
    27. ;**********************************************************************
    28. ;                                                                     *
    29. ;    Notes:                                                           *
    30. ;                                                                     *
    31. ;**********************************************************************
    32.  
    33.  
    34.     list      p=16F84A            ; list directive to define processor
    35.     #include <p16F84A.inc>        ; processor specific variable definitions
    36.  
    37.     __CONFIG   _CP_OFF & _WDT_ON & _PWRTE_ON & _RC_OSC
    38.  
    39. ; '__CONFIG' directive is used to embed configuration data within .asm file.
    40. ; The lables following the directive are located in the respective .inc file.
    41. ; See respective data sheet for additional information on configuration word.
    42.  
    43.  
    44.  
    45.  
    46. ;***** VARIABLE DEFINITIONS
    47. w_temp        EQU     0x0C        ; variable used for context saving
    48. status_temp   EQU     0x0D        ; variable used for context saving
    49.  
    50.  
    51.  
    52.  
    53.  
    54.  
    55.  
    56.  
    57. ;**********************************************************************
    58.         ORG     0x000             ; processor reset vector
    59.           goto    main              ; go to beginning of program
    60.  
    61.  
    62.         ORG     0x004             ; interrupt vector location
    63.         movwf   w_temp            ; save off current W register contents
    64.         movf    STATUS,w          ; move status register into W register
    65.         movwf    status_temp       ; save off contents of STATUS register
    66.  
    67.  
    68. ; isr code can go here or be located as a call subroutine elsewhere
    69.  
    70.  
    71.         movf    status_temp,w     ; retrieve copy of STATUS register
    72.         movwf    STATUS            ; restore pre-isr STATUS register contents
    73.         swapf   w_temp,f
    74.         swapf   w_temp,w          ; restore pre-isr W register contents
    75.         retfie                    ; return from interrupt
    76.  
    77.  
    78.  
    79. main
    80.  
    81. ; remaining code goes here
    82.  
    83.  
    84.  
    85.  
    86.  
    87.  
    88.  
    89.  
    90.  
    91.  
    92.         END                     ; directive 'end of program'
    93.  
    94.  
     
    Last edited: May 17, 2010
  6. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    As far as programming goes, the counting and 7 segment goes on in the main loop and as soon as any button is pressed you have a winner and it is game over, so really there are only 2 states. If you have the user buttons on interrupt on change pins it becomes fairly simple.
     
  7. BMorse

    Senior Member

    Sep 26, 2009
    2,675
    234
    I would have gone with the pic16f628a (as MarkD pointed out) instead of the F84, since it is newer, cheaper, and has more built in features, such as an internal 4mhz oscillator, you could have just used this to minimize board components....

    also, I would connect the LED's the other way around,

    [​IMG]

    Better to sink than source current from the pic pin.



    B. Morse
     
  8. farscape

    Thread Starter New Member

    May 16, 2010
    26
    0
    Thank you both very much for the tips.

    Ive updated the schematic and hopefully fixed most of my errors.
    -I dont recall seeing the pic16f828a in our lab so I think Ill stick with the 84a just to be safe.
    -If I switch the LED's around so that they are powered from VCC then with my vague understanding, it seems like both LED's will always be lit... Or will the pins be floating so that there is no current?

    About the interrupt pins (RB4, RB5)
    -Ive been reading about the interrupt function, but I'm not quite sure if I'm understanding... So when there is a change of state in one of the player inputs (RB4 or RB5) the program enters a subroutine and I can specify that the unchanged input is cut off in this case? Or does this have something to do with a player incorrectly powering his/her input before the counting sequence has finished? Can you dumb this down for me a bit?

    As far as the programming goes, I'll start to stumble through it and get something posted later today. But in general this is how I will try to organize things. I hope I'm not too far off...
    -Once reset is asserted, CLKOUT goes high and powers the crystal oscillator which drives CLKIN at 4Mhz.
    -I want six out of seven of the output bits to start high to show a "9" on the display, after 1 second all 7 outputs high to show a "8" and continue this until I get to 0 at 9 seconds.
    -Is this sort of a trial and error process where I need to keep adding counting sequences until I come up with approximately 1 second in terms of oscillations of CLKIN or is there a less clumsy way to go about this?
    -After 9 seconds, the display shows "0" for 1 second and then the inputs are enabled and the first player's input to go high is sent to the output and the LED is lit, disabling the second player's input.
    -I'm at a loss as to how I'm going to program the event where a player asserts his input before the counting sequence is complete. I forgot to mention this but the player who "jumps the gun" is supposed to have his LED flash for a moment and then the other player has his LED turn on and remain lit until the reset in pushed.

    Anyways, Ill see what I can come up with this afternoon. Thanks a lot
     
  9. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    With the alternate LED wiring (delete the ground symbols - I'm not sure what they are doing there) If you output a 1 they will be off, a 0 they will be on. If both ends of the LED are at 5V no current can flow.

    If you set the interrupt to trigger on pin change the program executes the main timing and 7 segment update loop until one of the buttons is pressed then enters the interrupt. At this point the game is over, you just need to use a bit of logic to decide who has won and then display the result. Counting doesen't need to carry on at this point.
    It doesn't matter at which point a player presses a button - as soon as they press they have either won or lost depending on the count timer.

    You can use this to get your 1 second delay:
    http://www.piclist.com/tecHREF/piclist/codegen/delay.htm
     
  10. farscape

    Thread Starter New Member

    May 16, 2010
    26
    0
    Sorry for the delay, this is turning out to be more difficult for me then I had imagined. I think I may be going about this programming the most primitive way possible but here it goes... (My code is attached on the next page)

    In an earlier lab we set up a pic16f84a to blink an LED once a second so I'm hoping I can use that code to break things down into 1 second segments and then after each 1 second segment I can change the state of the outputs to reflect the countdown sequence on the LED display.

    I hope this isn't completely wrong, but I'm hoping in my initialization, that I can assign different output states
    (pin values) that correspond to the value of the count.
    -So, initially, when the count is at 10 the output pins to the display show a "9"
    -and then after 1 second, count=9 and this corresponds to a different state I specified in initialization
    where the output displays an "8" and so on

    If by some miracle, this is one way to go about this, do you know how I could set these initial values for my display?

    Thanks for your patience
     
    Last edited: May 18, 2010
  11. farscape

    Thread Starter New Member

    May 16, 2010
    26
    0
    I couldn't get any spacing to work in the code, so here it is attached
     
  12. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    Try this. I put a table in which obviously you will have to alter according to how your 7seg is wired.

    I also put the donothing loop which stops it from rolling through and restarting.

    I presume you have set the CONFIG elsewhere but you might want to post that to check.

    You can insert code by going advanced, highlighting the code and clicking the [​IMG]button.

    Still more work to do but it's a good start.

    If you want to know more about tables read AN556 on the microchip site. You probably only need to look at the most simple method as your program will probably not be very long.

    Code ( (Unknown Language)):
    1. ;----------------------------------------------------------------------
    2. ;    cpu equates (memory map)
    3. portB    equ    0x06        ; (p. 10 defines port address)
    4. count    equ    0x0c
    5. nCount    equ    0x0d
    6. mCount    equ    0x0e
    7. ;----------------------------------------------------------------------
    8.  
    9.     org    0x000
    10.  
    11. start    movlw    0x00        ; load W with 0x00 make port B output (p. 45)
    12.     tris    portB        ; copy W tristate, port B outputs (p. 58)
    13.     clrf    portB        ; clear all lines low
    14.  
    15.  
    16.  
    17. get_cnt    movf    count, w    ; move count to W
    18.     ;movlw   0x0A                ; set w=10 decimal
    19.    
    20.     call table
    21.             movwf    portB        ; move W to port B
    22.     call    pause        ; delay by subroutine
    23.     call    pause        
    24.     call    pause
    25.     call    pause
    26.     call    pause        ; five pause executions equals ~ 1 second
    27.     decfsz    count, f                ; decrement counter, skip if=0
    28.     goto    get_cnt        ; repeat forever
    29.  
    30. doNothing
    31.     goto doNothing
    32.  
    33.  
    34. table    addwf PCL
    35.     retlw b'00000001'
    36.     retlw b'00000010'
    37.     retlw b'00000100'
    38.     retlw b'00001000'
    39.     retlw b'00010000'
    40.     retlw b'00100000'
    41.     retlw b'01000000'
    42.     retlw b'10000000'
    43.     retlw b'11111111'
    44.     retlw b'10101010'
    45.     retlw b'01010101'
    46.  
    47.  
    48. pause    movlw    0xff        ; set w = 255 decimal
    49.     movwf    mCount        ; mCount = w
    50. loadN    movlw    0xff        ; set w = 255 decimal
    51.     movwf    nCount        ; nCount = w
    52. decN    decfsz    nCount, f    ; nCount--
    53.     goto    decN        ; if nCount != 0 then repeat nCount--
    54.     decfsz    mCount, f    ; else decrement Count
    55.     goto    loadN        ; if mCount != 0 then
    56.                 ;   reload nCount to 255 and decrement
    57.     return            ; else exit subroutine
    58.  
    59.     end
    60. ---------------------------------------------------------------------------------------
    61.  
     
    Last edited: May 18, 2010
    farscape likes this.
  13. farscape

    Thread Starter New Member

    May 16, 2010
    26
    0
    Sorry to be such a nuisance, but I read the an556 and I'm not quite sure exactly what the table does.

    Does each line of the table correspond to one of my outputs to the 7 segment? And so does that mean I'll have to make 9 seperate tables for each number I want displayed?

    Thanks
     
  14. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    Actually I just spotted a mistake I made which could be confusing you; this bit:

    Code ( (Unknown Language)):
    1. get_cnt    movf    count, w    ; move count to W
    2.          ;movlw   0x0A                ; set w=10 decimal
    should change to:

    Code ( (Unknown Language)):
    1.      movlw   0x0A                ; set w=10 decimal
    2.      movf count, F
    3. get_cnt    movf    count, w    ; move count to W
    Each line is for 1 second and displays a digit.

    When you call table the addwf PCL makes it jump forward to one of the retlw instructions.
    If W is 0 it goes to the line immediately after the addwf line.
    If W is 10 it goes to the last one (the 11th retlw)
    Whatever value is in the retlw instruction is now in W and it returns to the instruction after "call table" which puts W onto port B.
    The b'11111111' just means binary and is the equivalent of FF hex or 255 decimal. It would light up all the segments which would display an "8" on the 7 seg. The left 1 equates to PORTB7 and the right 1 is B0 (which you aren't using so doesen't matter.
    I hope I've explained this clearly, but if not, ask away.
     
  15. farscape

    Thread Starter New Member

    May 16, 2010
    26
    0
    Yes, I think I understand now. Thank you so much.

    I've written into the table, the values for my 7 segment corresponding to the pins on my PIC. I'm assuming that I should be using RB1-RB7 for this, so I altered the wiring for the rest of the PIC to allow this. Hopefully that's correct.

    Ive attached my updated schematic and code if you don't mind taking a look.
    -I added the CONFIG I came up with, but I'm a little uncertain how much information I need to include in this...

    Code ( (Unknown Language)):
    1. ;----------------------------------------------------------------------
    2. ;    cpu equates (memory map)
    3. portB    equ    0x06        ; (p. 10 defines port address)
    4. count    equ    0x0c
    5. nCount    equ    0x0d
    6. mCount    equ    0x0e
    7. ;----------------------------------------------------------------------
    8.  
    9.    
    10. processor 16F84A
    11. INCLUDE "p16f84A.inc"
    12. __CONFIG _CP_OFF & _WDT_OFF & _XT_OSC & _PWRTE_ON
    13. org H’00’
    14.  
    15.  
    16. start    movlw    0x00        ; load W with 0x00 make port B output (p. 45)
    17.          tris    portB        ; copy W tristate, port B outputs (p. 58)
    18.          clrf    portB        ; clear all lines low
    19.  
    20.          movlw   0x0A         ; set w=10 decimal
    21.          movf    count, F
    22. get_cnt  movf    count, w     ; move count to W
    23.  
    24.    
    25.          call    table
    26.          movwf   portB        ; move W to port B
    27.          call    pause        ; delay by subroutine
    28.          call    pause        
    29.          call    pause
    30.          call    pause
    31.          call    pause        ; five pause executions equals ~ 1 second
    32.          decfsz  count, f     ; decrement counter, skip if=0
    33.          goto    get_cnt      ; repeat forever
    34.  
    35. doNothing
    36.          goto doNothing
    37.  
    38.  
    39. table    addwf PCL
    40.          retlw b'00000000'
    41.          retlw b'01111110'
    42.          retlw b'00000100'
    43.          retlw b'10110110'
    44.          retlw b'10011110'
    45.          retlw b'11001100'
    46.          retlw b'11011010'
    47.          retlw b'11111010'
    48.          retlw b'00001110'
    49.          retlw b'11111110'
    50.          retlw b'11011110'
    51.  
    52.  
    53. pause    movlw    0xff        ; set w = 255 decimal
    54.          movwf    mCount      ; mCount = w
    55. loadN    movlw    0xff        ; set w = 255 decimal
    56.          movwf    nCount      ; nCount = w
    57. decN     decfsz   nCount, f   ; nCount--
    58.          goto     decN        ; if nCount != 0 then repeat nCount--
    59.          decfsz   mCount, f   ; else decrement Count
    60.          goto     loadN       ; if mCount != 0 then
    61.                               ;   reload nCount to 255 and decrement
    62.          return               ; else exit subroutine
    63.  
    64.     end
    65. ---------------------------------------------------------------------------------------
    66.  
    67.  
    Thanks
     
  16. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    Looks ok, I hadn't noticed the revised drawing with the alternate 7seg pins.
    I think it is probably just as easy to check the user inputs in the decN loop of the pause subroutine. Saves setting up the interrupt, messing around with 2 ports for the 7 seg, and will just require a bit of tweaking to get the pause back to 1/5 second.
    If you put a pause in the doNothing loop it will then carry on checking.

    It's worth checking if you need the capacitors from the crystal to ground.
    It's probably a good idea to put a 10nF capacitor between Vdd and Vss for stability.
     
    Last edited: May 19, 2010
  17. MMcLaren

    Well-Known Member

    Feb 14, 2010
    759
    116
    Hi farscape (and gang),

    If you were to connect RB0..RB6 to segments A..G then you could use a segment data table that looks something like this;
    Code ( (Unknown Language)):
    1.  
    2. ;
    3. ;  segment data table (caveat, non-boundary tolerant)
    4. ;
    5. segtbl
    6.         addwf   PCL,F           ;                                 |B0
    7.         dt      b'00111111'     ; "0"   -|-|F|E|D|C|B|A           |B0
    8.         dt      b'00000110'     ; "1"   -|-|-|-|-|C|B|-           |B0
    9.         dt      b'01011011'     ; "2"   -|G|-|E|D|-|B|A           |B0
    10.         dt      b'01001111'     ; "3"   -|G|-|-|D|C|B|A           |B0
    11.         dt      b'01100110'     ; "4"   -|G|F|-|-|C|B|-           |B0
    12.         dt      b'01101101'     ; "5"   -|G|F|-|D|C|-|A           |B0
    13.         dt      b'01111101'     ; "6"   -|G|F|E|D|C|-|A           |B0
    14.         dt      b'00000111'     ; "7"   -|-|-|-|-|C|B|A           |B0
    15.         dt      b'01111111'     ; "8"   -|G|F|E|D|C|B|A           |B0
    16.         dt      b'01101111'     ; "9"   -|G|F|-|D|C|B|A           |B0
    17.  
    I wrote a sample program for your application yesterday which I can post when I get home tonight, if you're interested.

    Regards, Mike
     
    Last edited: May 19, 2010
  18. farscape

    Thread Starter New Member

    May 16, 2010
    26
    0
    Okay, assuming the pinouts I have specified are good to go, I think I may have figured out how to program the check into the pause routine... Am I on the right track with this?

    I also initialized the portA pins as well

    Code ( (Unknown Language)):
    1. ;----------------------------------------------------------------------
    2. ;    cpu equates (memory map)
    3. portB    equ    0x06        ; (p. 10 defines port address)
    4. count    equ    0x0c
    5. nCount   equ    0x0d
    6. mCount   equ    0x0e
    7. portA    equ    0x05
    8. ;----------------------------------------------------------------------
    9.  
    10.    
    11. processor 16F84A
    12. INCLUDE "p16f84A.inc"
    13. __CONFIG _CP_OFF & _WDT_OFF & _XT_OSC & _PWRTE_ON
    14. org H’00’
    15.  
    16.  
    17. start    movlw    0x00        ; load W with 0x00 make port B output (p. 45)
    18.          tris    portB        ; copy W tristate, port B outputs (p. 58)
    19.          clrf    portB        ; clear all lines low
    20.          movlw    0x0C        ; load w with 0x0C make RA0,RA1 output and RA2,RA3 input
    21.          tris    portA        ; copy W tristate
    22.      
    23.          movlw   0x0A         ; set w=10 decimal
    24.          movf    count, F
    25. get_cnt  movf    count, w     ; move count to W
    26.  
    27.    
    28.          call    table
    29.          movwf   portB        ; move W to port B
    30.          call    pause        ; delay by subroutine
    31.          call    pause        
    32.          call    pause
    33.          call    pause
    34.          call    pause        ; five pause executions equals ~ 1 second
    35.          decfsz  count, f     ; decrement counter, skip if=0
    36.          goto    get_cnt      ; repeat forever
    37.  
    38. doNothing
    39.          goto doNothing
    40.  
    41.  
    42. table    addwf PCL
    43.          retlw b'00000000'
    44.          retlw b'01111110'
    45.          retlw b'00000100'
    46.          retlw b'10110110'
    47.          retlw b'10011110'
    48.          retlw b'11001100'
    49.          retlw b'11011010'
    50.          retlw b'11111010'
    51.          retlw b'00001110'
    52.          retlw b'11111110'
    53.          retlw b'11011110'
    54.  
    55.  
    56. pause    movlw    0xff        ; set w = 255 decimal
    57.          movwf    mCount      ; mCount = w
    58. loadN    movlw    0xff        ; set w = 255 decimal
    59.          movwf    nCount      ; nCount = w
    60. decN     decfsz   nCount, f   ; nCount--
    61.          goto     decN        ; if nCount != 0 then repeat nCount--  
    62.          decfsz   mCount, f   ; else decrement Count
    63.          BTFSS    portA, b'10'
    64.          goto     loadN       ; if mCount != 0 then
    65.          call     flash1                    
    66.          return               ; else exit subroutine
    67.  
    68. flash1   bsf      portA, b'10'
    69.          movlw    0xff        ; set w = 255 decimal
    70.          movwf    mCount      ; mCount = w
    71. loadN1   movlw    0xff        ; set w = 255 decimal
    72.          movwf    nCount      ; nCount = w
    73. decN1    decfsz   nCount, f   ; nCount--
    74.          goto     decN1        ; if nCount != 0 then repeat nCount--  
    75.          decfsz   mCount, f   ; else decrement Count
    76.          goto     loadN1
    77.          bcf      portA, b'10'
    78.          bsf      portA, b'01'
    79.          goto     doNothing
    80. end
    81. ---------------------------------------------------------------------------------------
    82.  
    83.  
    84.  
    Thanks
     
  19. farscape

    Thread Starter New Member

    May 16, 2010
    26
    0
    Hi Mike,

    Thanks for the tip. I'm very new at this so I'm unfamiliar with a segment data table. What is the "dt" command?

    And I would love to see what you came up with for a program if you get a chance to post it.

    Thanks
     
  20. MMcLaren

    Well-Known Member

    Feb 14, 2010
    759
    116
    farscape,

    I think you're on the right track but there are a couple things that don't look right.

    The 'dt' assembler directive creates 'retlw' instructions.

    If it will help, here's the example program I came up with yesterday. Each 1-second interval is made up of 200 5-msec intervals where we sample the switches on RA0 and RA1. The display is updated at the end of each 1 second interval. A switch press takes you out of the 1-second loop. Then you determine which switch was pressed and if it was pressed early (early == time > 0).

    Regards, Mike

    Code ( (Unknown Language)):
    1. ;       #include <P16F84A.INC>
    2.         radix dec
    3.  
    4. time    equ     0x0C            ; time, 0..9 seconds
    5. sectmr  equ     0x0D            ; 1-second timer
    6. DelayHi equ     0x0E            ; DelayCy() subsystem variable
    7.  
    8. ;******************************************************************
    9. ;  K8LH DelayCy() subsystem macro generates four instructions
    10. ;
    11.         radix   dec
    12. clock   equ     4               ; clock frequency in Megahertz
    13. usecs   equ     clock/4         ; cycles/microsecond multiplier
    14. msecs   equ     clock/4*1000    ; cycles/millisecond multiplier
    15.  
    16. DelayCy macro   delay           ; 11..327690 cycle range
    17.         movlw   high((delay-11)/5)+1
    18.         movwf   DelayHi
    19.         movlw   low ((delay-11)/5)
    20.         call    uDelay-((delay-11)%5)
    21.         endm
    22.  
    23. ;******************************************************************
    24. ;  Reset Vector                                                   *
    25. ;******************************************************************
    26.         org     0x0000
    27. v_reset
    28.         clrf    STATUS          ; bank 0                          |B0
    29.         clrf    PORTA           ;                                 |B0
    30.         clrf    PORTB           ;                                 |B0
    31.         banksel TRISA           ; bank 1                          |B1
    32.         movlw   3               ;                                 |B1
    33.         movwf   TRISA           ; RA1:RA0 inputs                  |B1
    34.         clrf    TRISB           ; portb all outputs               |B1
    35.         banksel PORTA           ; bank 0                          |B0
    36. ;
    37. ;  initialize program variables
    38. ;
    39.         movlw   9               ;                                 |B0
    40.         movwf   time            ; time = 9                        |B0
    41.         call    segtbl          ; get segment data pattern        |B0
    42.         movwf   PORTB           ; display "9"                     |B0
    43. ;
    44. ;  main.loop
    45. ;
    46. newsec
    47.         movlw   1000/5          ; 1000-msecs/5-msecs              |B0
    48.         movwf   sectmr          ; turn on 1-second timer          |B0
    49. sample
    50.         DelayCy(5*msecs-7)      ; delay 5-msecs minus 7 cycles    |B0
    51.         comf    PORTA,F         ; sample active lo switches       |B0
    52.         andlw   3               ; on RA1:RA0 pins                 |B0
    53.         skpz                    ; new press? no, skip, else       |B0
    54.         goto    testsw          ; branch and test                 |B0
    55.         decfsz  sectmr,F        ; 1-sec timeout? yes, skip,else   |B0
    56.         goto    sample          ;                                 |B0
    57.         movf    time,W          ; 9 second time out?              |B0
    58.         skpnz                   ; no, skip, else                  |B0
    59.         goto    newsec          ; just waiting for a press now    |B0
    60.         decf    time,F          ; decrement time                  |B0
    61.         movf    time,W          ; W = time, 0..8                  |B0
    62.         call    segtbl          ; get segment data                |B0
    63.         movwf   PORTB           ; display new time                |B0
    64.         goto    newsec          ;                                 |B0
    65. testsw
    66.         movf    time,F          ; timer timed out (0)?            |B0
    67.         skpz                    ; yes, skip, else                 |B0
    68.         goto    early           ; branch (pressed too early)      |B0
    69.         andlw   1               ; switch 1 press?                 |B0
    70.         skpnz                   ; yes, skip, else                 |B0
    71.         goto    light2          ; light LED 2                     |B0
    72.         goto    light1          ; light LED 1                     |B0
    73. early
    74.         andlw   1               ; switch 1 pressed early?         |B0
    75.         skpnz                   ; yes, skip, else                 |B0
    76.         goto    light1          ; light LED 1                     |B0
    77.         goto    light2          ; light LED 2                     |B0
    78. light1
    79.         bsf     PORTA,2         ; light LED 1 on RA2              |B0
    80.         goto    $               ; loop forever (wait for reset)   |B0
    81. light2
    82.         bsf     PORTA,3         ; light LED 2 on RA3              |B0
    83.         goto    $               ; loop forever (wait for reset)   |B0
    84. ;******************************************************************
    85. ;  segment data lookup table (caveat, non-boundary tolerant)
    86. ;
    87. segtbl
    88.         addwf   PCL,F           ;                                 |B0
    89.         retlw   b'00111111'     ; "0"   -|-|F|E|D|C|B|A           |B0
    90.         retlw   b'00000110'     ; "1"   -|-|-|-|-|C|B|-           |B0
    91.         retlw   b'01011011'     ; "2"   -|G|-|E|D|-|B|A           |B0
    92.         retlw   b'01001111'     ; "3"   -|G|-|-|D|C|B|A           |B0
    93.         retlw   b'01100110'     ; "4"   -|G|F|-|-|C|B|-           |B0
    94.         retlw   b'01101101'     ; "5"   -|G|F|-|D|C|-|A           |B0
    95.         retlw   b'01111101'     ; "6"   -|G|F|E|D|C|-|A           |B0
    96.         retlw   b'00000111'     ; "7"   -|-|-|-|-|C|B|A           |B0
    97.         retlw   b'01111111'     ; "8"   -|G|F|E|D|C|B|A           |B0
    98.         retlw   b'01101111'     ; "9"   -|G|F|-|D|C|B|A           |B0
    99. ;******************************************************************
    100. ;  DelayCy() subsystem uDelay subroutine
    101. ;
    102.         nop                     ; entry for (delay-11)%5 == 4     |B0
    103.         nop                     ; entry for (delay-11)%5 == 3     |B0
    104.         nop                     ; entry for (delay-11)%5 == 2     |B0
    105.         nop                     ; entry for (delay-11)%5 == 1     |B0
    106. uDelay  addlw   -1              ; subtract "loop" cycle time      |B0
    107.         skpc                    ; borrow? no, skip, else          |B0
    108.         decfsz  DelayHi,F       ; done?  yes, skip, else          |B0
    109.         goto    uDelay          ; do another loop                 |B0
    110.         return                  ;                                 |B0
    111. ;******************************************************************
    112.         end
    113.  
     
    Last edited: May 20, 2010
Loading...