# PIC 16f877 timer question

Status
Not open for further replies.

#### Soundguy

##### New Member
Hi guys,
I am a newbie to the forum and I hope that someone can help me keep what hair I have remaining.

I am trying to code a project with a Pic 16f877A that has a telephone style keypad with a dedicated keypad decoder chip for debouncing that provides a data valid signal for the interrupt function, an HD44780 based LCD module, and 40 relays for output.

The display shows a welcoming message until you press a key on the keypad, at which time it shows a different display with spaces prompting for more keys ( numbers ) to be entered. After the 3rd key is pressed, the code checks for validity and activates the appropriate relay for a set time duration.
I am using the PORTB,0 interrupt feature to detect keypresses.
I have been able to code each of the routines for the different functions successfully, but when I try to combine the code snippets, I run into problems with the system not responding to keypresses and/or the display showing garbage.

I am pretty sure that the multiple timing loops needed for the display are causing the processor to be tied up for too long in some cases which is causing it to miss keypresses and other events.

I am going to want to activate the watchdog timer function for the finished device, as well, which means that I may need the pre-scaler for it.

My thought is to use Timer 1 or 2 to handle the timing for the display functions and eliminate, or at least reduce the number of delay loops in the code. I expect that I would have to change the timer settings on the fly, depending what times I needed for the various functions and poll the overflow bit(s).

Does this sound like the best way to go?
Will there be a problem with using either or both of these timers for the delays?
How will the keypad interrupt affect the timer(s) as I am polling them?

Sorry for the long post.
Any help would be greatly appreciated.

Cheers,

Soundguy

#### ericgibbs

##### Well-Known Member
hi,

Are you using the PIC's Interrupt options within your program OR is it all Polling.?

#### BeeBop

##### Active Member
Hi guys,
I am a newbie to the forum and I hope that someone can help me keep what hair I have remaining.
Hi, welcome.
I am trying to code a project with a Pic 16f877A that has a telephone style keypad with a dedicated keypad decoder chip for debouncing that provides a data valid signal for the interrupt function, an HD44780 based LCD module, and 40 relays for output.
So then the data is on the pins of the decoder? How do you read them? Do you try to read them in your interrupt? Or do you set a flag in the interrupt and act on that in your main loop?
It sounds like you have the issues without using the relays, is that the case? How are you driving the relays, btw?

The display shows a welcoming message until you press a key on the keypad, at which time it shows a different display with spaces prompting for more keys ( numbers ) to be entered. After the 3rd key is pressed, the code checks for validity and activates the appropriate relay for a set time duration.
I am using the PORTB,0 interrupt feature to detect keypresses.
I have been able to code each of the routines for the different functions successfully, but when I try to combine the code snippets, I run into problems with the system not responding to keypresses and/or the display showing garbage.

I am pretty sure that the multiple timing loops needed for the display are causing the processor to be tied up for too long in some cases which is causing it to miss keypresses and other events.
Yes, timing loops are blocking... polling doesn't have to be. LCDs are slow...
and there are some posters on here doing some incredible things with keypads and LCDs or 7segment displays... if I recall, most of them are polling and debouncing the keys in software...

I'd like to know how you are servicing that key press - code snippet, perhaps?
I am going to want to activate the watchdog timer function for the finished device, as well, which means that I may need the pre-scaler for it.

My thought is to use Timer 1 or 2 to handle the timing for the display functions and eliminate, or at least reduce the number of delay loops in the code. I expect that I would have to change the timer settings on the fly, depending what times I needed for the various functions and poll the overflow bit(s).

Does this sound like the best way to go?
Will there be a problem with using either or both of these timers for the delays?
How will the keypad interrupt affect the timer(s) as I am polling them?

Sorry for the long post.
Any help would be greatly appreciated.

Cheers,

Soundguy

The long post isn't so bad; you explained the problem. I think you should go into some details to get help with it, though. A link to the relevant data sheet, or at least a number for the chip, and your source, or relevant parts of it... make sure you put it between [code] and [/code] tags to keep your format intact...

#### BeeBop

##### Active Member
Hi Eric,
guess you were posting while I was typing...
He is...
I am using the PORTB,0 interrupt feature to detect keypresses.

#### ericgibbs

##### Well-Known Member
Hi Eric,
guess you were posting while I was typing...
He is...
hi BeeBop,
I thought no one else was going to answer.

Your post is far more useful than mine, I was trying to get a little more detail.
[I'll shut up!]

I saw the ref to one Intr but he didnt mention others.

Last edited:

#### gramo

##### New Member
I'd be more inclined to manually poll something as simple as the keypad, freeing up interrupt driven overheads for other tasks.

A code snippet would help out, other things might be at play such as context saving not incorporated?

#### BeeBop

##### Active Member
hi BeeBop,
I thought no one else was going to answer.

Your post is far more useful than mine, I was trying to get a little more detail.
[I'll shut up!]

I saw the ref to one Intr but he didnt mention others.
I was so slow typing it though.... No, don't shut up....
I thought no one else was going to answer either

I'd be more inclined to manually poll something as simple as the keypad, freeing up interrupt driven overheads for other tasks.

A code snippet would help out, other things might be at play such as context saving not incorporated?
I'd be inclined that way as well...

and I too suspect it has to do with the interrupt he is using... but I think he is trying to service the whole thing in the isr...

I has a similar issue a few years back, with an IR rX which blocked.... so it could also be the decoder....

#### Soundguy

##### New Member
Thank you for the quick responses, everyone.

The keypad decoder is a 74HC922 decoder chip that handles keypad scanning, debouncing, and binary output conversion. It provides a "data valid" output after the debounce period, that I am using as an interrupt on PORTB,0.

The binary output of this chip is a little strange and as such, I am using a lookup table to convert to BCD, as well as masking out unwanted characters/digits. ( * and # ) Once the keypad is decoded, the BCD number for each digit is stored in a seperate register according to whether the digit is the 1st, 2nd, or 3rd entry. The 1st digit is rejected if it is anything other than a 0. The 2nd digit is used with a lookup table that returns the same number multiplied by 10,as well as rejecting any number above 4, which then is placed in a register that is used for the relay decoding routine. The 3rd digit, after decoding is added to the same register that the 2nd digit ,after multiplying, was placed in with the result that the register now holds a number between 1 and 40, which in conjunction with the relay lookup table, will activate the proper relay for the keypad total entry.

At the same time, after the digits entered are decoded, they need to be placed in the appropriate locations on the display with the 1st location being entered as part of the routine that changes the displayed message and prompts for keypad input.What I have done in the code is to put the decoded digits into the display as they are received nad before the 2nd digit is multiplied. The display is set for cursor advancement, so I only need to set the location for the 1st digit. The cursor advances to the 2nd and 3rd locations by itself.

I have tried both handling the entire keypad routine in the ISR and outside of the ISR by just using flags created when a key is pressed.Both methods did not alleviate the problems. The displayed message has to change when the first keypress is detected, and I think that the delays needed for the display are at the root of my troubles.

I am saving the "w" and "STATUS" registers with the Microchip approved method upon entering and exiting the ISR, and the simulation in MPLAB shows that nothing is getting corrupted in those registers.

I am using an existing display in a panel that also incorporates the keypad, and is hardwired with the display R/W grounded, otherwise I wopuld have tried polling the display to check if it was ready to accept new data, instead of creating delays to insure that the display could accept new data.

With regards to the relays, I am using 74HC138 3 to 8 decoders in a row/column matrix to decode and drive the relays through a ULN2003 for the 8 rows, and transistors for the 5 columns.
The decoding for the relays is done in a lookup table that returns the appropriate output on PORTC for the 74HC138's. The lookup table has 40 entries in it to decode for the relays.

I have tested all of the routines and they work individually. It is just when I try to stitch them together that I run into problems.

Does it make sense to you to create delays as needed with Timer1 polling the overflow bit and then changing the timer loading and re-starting the timer when the next delay is needed?
I should have mentioned that the delays that I think are causing the problems are the delays that are used to change the display message repeatedly, i.e. the welcoming message that uses both lines in the display, and has 3 messages the show in sequence, after which the sequence repeats. The delays used for this cause the message lines to display for 1 second before changing to the next message.
The delays used for housekeeping for the display are of 1-5 milliseconds in duration depending on what is being done at that time.(Sending commands or characters.)

The idea was for the interrupt to clear the display and show a new message with spaces for the entered keypad digits to display with a blinking cursor as a prompt. As I mentioned before, this routine does work, including the response to the interrupt for the first digit. When the second digit is entered, then the problems surface.

The display messages are done in lookup tables as well, and as such, it would be a very long snippet of code to include here, but if needed, I will try to condense it down. Otherwise I might have to include the whole listing.

Is there some method of loading display data into a register ahead of time and then sending each message in a quick routine?

Would it help if I used the "DT" method of putting data in a lookup table instead of having seperate lines for each character in the table along with their respective "RETLW" statements?

I thought that this would be a simple project when I started it many months ago. I never dreamed that it could be so hard to create the software needed.

As this is something that I need to resolve ASAP, is there anyone in the Vancouver area that I could connect with to try to finish this? I will compensate them for their time. If so, please tell me what I need to do for us to connect.

Thank you.

Soundguy

#### Mr RB

##### Well-Known Member
You got me guessing what this is for?? Some type of security access controller with 40 door-lock relays?

A mailbox for apartments with 40 little mail doors?

A bicycle hire rack!!

Darn the intrigue...

#### Soundguy

##### New Member
You're close.
It is a buzz button replacement for an apartment building that has 38 individual, un-reliable, buttons for buzzing the suites.

#### BeeBop

##### Active Member
hi,
The idea was for the interrupt to clear the display and show a new message with spaces for the entered keypad digits to display with a blinking cursor as a prompt. As I mentioned before, this routine does work, including the response to the interrupt for the first digit. When the second digit is entered, then the problems surface.
So when you read the first key, do you disable interrupts while reading the other key presses? What I'm thinking is that while you are processing the first key an interrupt occurs...

The decoder latches the key on its outputs, so once you have the first one, you expect more, so polling, and collecting the keys in a buffer before displaying anything on the LCD should (yea right, eh?) work. I'm thinking you have a buffer:
Once the keypad is decoded, the BCD number for each digit is stored in a seperate register according to whether the digit is the 1st, 2nd, or 3rd entry.
but am wondering if you disable interrupts while collecting these.

The delays used for housekeeping for the display are of 1-5 milliseconds in duration depending on what is being done at that time.(Sending commands or characters.)
5 milliseconds is a long time for the micro, but not for us. I think most debounce routines are between 10 and 20 mSec.... and I don't think it is your display which is causing the problem (but I could be wrong,) or the way you are storing the strings in program memory.

If you could post a snippet which showed how you are collecting the keystrokes, and perhaps your ISR, it would help out a lot.

EDIT:

I was just having a look at the data sheet for that decoder..
Outputs are in 3-STATE until key is pressed, then data is placed on bus. When key is released, outputs return to 3-STATE.
OK, starting to see what is happening... Everything hinges on that statement, then. If I've read your text correctly, your micro is just doing nothing until someone comes to the door, then all the work begins. So they punch the first number in, and your micro wakes up and puts that number (?)
routine that changes the displayed message and prompts for keypad input.What I have done in the code is to put the decoded digits into the display as they are received nad before the 2nd digit is multiplied. The display is set for cursor advancement, so I only need to set the location for the 1st digit. The cursor advances to the 2nd and 3rd locations by itself.
on the screen. OK, I'm there, but what about the prompt? I would think the blinking cursor would be prompt enough, or am I missing something? The visitors must somehow know they have to enter three digits, no?
I can see how the delays in the LCD routines could be an issue. But just putting the digit in the display shouldn't be a problem; I'd think quite a few milliseconds would pass before a human finger would get to the next key. What is the 'displayed message,' and what is it changed to? Are there a lot of characters?

You could take the capacitor out of the cct, and poll the keys...

Last edited:

#### Soundguy

##### New Member
Hi BEEBOP,

You are right that the processor usually just sits there displaying the welcoming message sequence : WELCOME TO (building name), PLEASE ENTER THE RESIDENTS CODE...

When a key is pressed, the data valid output from the decoder, which is connected to PORTB,0 triggers an interrupt.

The ISR first saves the W and STATUS registers, then checks for the 1st digit to be a zero. If it is not a zero, the routine goes to its exit section, which restores the W and STATUS registers, renables the interrupts and returns to the main loop.
(There are no other interrupts enabled in the program besides the PORTB,0 interrupt. I have not yet learned to work with timer based interrupts.)

If the 1st digit is a zero, the ISR routine then stores the zero in a buffer labelled for the 1st digit. It then looks for the 2nd digit to be entered during a 5sec countdown delay.If there is no entry within that time, the routiine clears the buffers, and exits as mentioned above.

If there is a 2nd digit entered, the routine checks to make sure that the digit is 4 or less. if so, the number is placed in a buffer for the 2nd digit.The multiplication routine that I mentioned before is also invoked.

Then there is the 5sec coundown, again, while the routine looks for the 3rd digit. ( Each time a digit is sucessfully entered and checked, a bit is incremented in a flag register that indicates to the main loop how many digits have been entered. This is for the final output routine that activates the appropriate relay.)

After the 3rd digit is entered and stored, the routine exits to the main loop which is constantly polling the flag register to see if 3 digits have been entered. If so, the program jumps to the output routine which activates the relay for a few seconds.

You are also right in stating that the blinking cursor in the display is actually the prompt to the user to enter the next digit. The display shows a line of asterisks across the top of the display and a line of the same across the bottom display line, broken by blank spaces with the blinking cursor and any already entered digits.
I neglected to mention before that there is a directory next to the panel that shows the visitor what code to press to call the intended resident.

Regarding debouncing, that was the reason that I used the decoder chip in the first place, so that I wouldn't have to deal withmore delay routines for the keypad. In retrospect, I probably should have stuck with the tried and true method of keyboard scanning/debouncing in view of the needed conversion routine to generate BCD numbers representing the digits.

The keypad decoder chip keeps the data on the output lines after a keypress, but the data valid signal only is present while the key is being pressed. Even so, I would think that the signal is present long enough for the chip to generate the interrupt. At least, after the chip responds, it can get the data from the decoder at its leisure.

I have tried 2 different ISR routines, one doing all of the keypad decoding, testing, multiplying, storing, and display changing routines withiin the ISR.

The other routine I tried just checked for the leading zero, and went back to the main loop, waiting for another interrupt to process the next digit.

In both cases I ran into problems with the digits sometimes being ignored, or multiple digits being stored, or the wrong data being displayed on the LCD.

In the first ISR routine, I began to think that I was trying to do too much within the ISR, which is why I tried the 2nd routine. The code got so convoluted, that I started to think that I was going about this whole projects code the wrong way.

This is why it is difficult to provide a code snippet as there are so many versions of this code now, each with some working parts, and non-working parts. I feel that I am going in circles now.

This is also why I was wondering in my original post whether using timer 1 and/or timer 2 for the longer countdown and delay periods might be the answer. I have never tried to do this before, and I was wondering if it is feasible, with having to change the timer functions on the fly all of the time .

The 2-5 msec delays I mentioned are mostly to do with allowing the LCD time to process the updated screen displays, and I wonder just how much time I really need to allow for the display to do it's thing.

I keep thinking that the processor is so busy decrementing registers for the delays, that it is missing the interrupt sometimes.

I am wondering if I should be thinking along the lines of an operating system approach or a state machine approach?

So far, most of the code has been linear, aside from the ISR routine, although there are subroutines for the table calls and time delays. ( I guess that it isn't so linear, after all.)

Regards,

Soundguy.

#### Mr RB

##### Well-Known Member
This may not be the advice you want to hear.

You took on a difficult and complex project for a beginner, but through intelligence and organisation you're bringing a lot of complex tasks together.

So now it's almost working, (hehe) it might be time to re evaluate the whole system. So start with a flowchart, and decide on the basic modules and their interactions now you know more about what their needs are in terms of timing and sequence etc.

I really believe it will be less work (and more enjoyable) to rebuild it from scratch but using some of the code modules you have already debugged.

This will let you clean up the code and the operation, and might provide you with usable code modules you can use in other projects.

I would use one interrupt only, to track your 5 second auto reset. Then a basic flowchart;

1. clear all keys entered
2. display welcome message
3. Wait here for keypress, then;
* start 5 sec timer
* update buttons (debounce with code delays, no int)
* if it gets to 3rd key ok; do relay
(int) if 5 seconds times out; goto 1.

As for code debouncing, its pretty easy to add a short delay after the button is first pressed (10mS), then if still pressed; update the key (show on LCD for user) then wait for release, add a longer delay (25mS) and then ready to check for next press. This will remove double bouncing but should be fast enough to still catch hasty users who mash the second key before they fully release the first key.

I think a rebuilt system like that will be much easier to debug and maintain than an interrupt on keypress system especially a dual int one on your first project.

#### Soundguy

##### New Member
Hi MR RB,

I have to agree that this task was much more difficult than I first thought it would be.

I agree that the best way to deal with this is to start again from scratch regarding the code.

Regarding the debouncing, I have mentioned before in my posts that the debouncing is taken care of by the 74HC922 decoder chip, so timing overhead for debouncing is not an issue. The time taken to decode the output of the chip, with the sub-routines needed, is however.
Again, it seems to come down to the processor having to deal with a lot of decisions, while it is repeatedly doing mundane tasks, i.e. the display messages.

I had, at one time, thought of using a seperate processor to handle the display part of things, and have it just put up the appropriate message as needed, whilst having the main processor do the decision making and take care of the input/ouput concerns, but I thought that it shouldn't be necessary to use 2 processors for this project.

I guess in a nutshell what I am really asking for help with is how to implement the timer 1 and/or timer2 modules to deal with the delay needs for the LCD messages, (i.e. showing a message, then the next, then the next, repeatedly),while being able to respond to the keypresses and process them, as well as the 5 second timer, and how to change the timer loaded values on the fly, if that is the best way to do things.

I assume that the shorter delays needed for initializing the LCD and pulsing the "E" line to the LCD can be taken care of with simple file decrementing type delays, as the initialization routine only happens once, and the "E" pulse can be covered with a NOP or two.

I have been coding this project based on a 4MHZ clock, but in thinking about it, I could use a 1MHZ crystal instead, which would simplify pre-scaler and timer setups.

I'll keep at it...

Soundguy

#### Mr RB

##### Well-Known Member
Reading the keyboard decoder chip is probably costing more hassle than it is worth, but I can understand you wanting to keep it if you have already built the hardware that way.

Writing to the LCD should be almost instant, I checked my LCD functions and you only need a 50uS delay after sending each char to the LCD. To generate a 50uS delay on the fly you only need to reset TMR0, at 4MHz that is 1MHz per timer tick so you need to sense 50 timer ticks. So when TMR0 >50 you're done. You don't need ints or anything fancy for the LCD timing. The LCD initialisation only needs to be done at startup.

As for the main 5 second timer, you don't need to change anything on the fly. Ive been building PIC apps for 10 years and these days I rarely even write to a timer, never mind change timer settings on the fly.

Setup TMR1 at 1:1 prescale and free running (you never need to clear it or write a value to it) so it overflows at 1M / 256 /256 which is 15 times per second. Everytime it overflows, catch that in your int by checking the TMR1 overflow int flag. In your int, all you do is inc a variable which will happen at 15 times per second. Very simple.

When that variable gets to (5sec * 15) ie 75 you have detected the 5 second event and can clear the screen etc (go back to step1).
To disable the 5 second timer just keep resetting that variable. The timer is continually reset back to zero seconds. So each time they press a key you reset the variable, giving 5 seconds to press the next key.

Thos should be the only things you need timers for? With the LCD initialisation delays you need some 6mS delays if I remember right so you can just reset TMR1H and when it gets to 30 is about 6mS. You won't need the 5 second timer during LCD initialisation of course.

Now I read your previous post about the keyboard chip, why do you multiple the second digit by 10?? Why not just store the 2 digits in 2 byte variables as they are. Then you just display each one to LCD as a char? That makes more sense to me and is much better from a systems design point.

Then decoding these 2 bytes to control the relay is part of your relay output function. But that's just the way i'd do it.

What is the need to write messages to the LCD when they are doing keypresses? Surely you just clear the welcome message and display 0 on receipt of the first keypress, then display the next 2 chars as they are entered?

Last edited:

#### Soundguy

##### New Member
Hi again, MR. RB

Thank you for your input regarding the timer setups.

Just to make sure that I understand, are you suggesting that I use timer0 for a system tick, for general use, and timer 1 for special items such as the 5 second countdown?
If, so, I think that I can handle that and will try it tomorrow.

In response to your question about multiplying the 2nd digit, the idea was to add up the numbers created by the keypad decoding and end up with a number that would translate directly to the relay number that I want to activate. I originally wrote some code that simply incremented a register form 0-40, and output the appropriate code to work with the 5 X 8 matrix arrangement for the relays and output indicating LED's as a test.

The LED's are in parallel with the relays in the prtotype.

When I first started designing this project, I built a circuit board with 40 LED's arranged in a 5 X 8 matrix in order to control all of them with a single port, including a seperate enable line, which is used to turn the selected output on for whatever length of time you want, as well as intoducing a cadence to the output, if needed, such as duplicating a telephone ring signal, be it either North American or European style ring cycles. This is the method I have used in the prtototype for controlling the relays.

I was trying to write the code so that it would be useable in future projects.

Lastly, the message displayed in the LCD after the 1st keypress is really just a screen full of asterisks with a space in the middle of the bottom line where the entered digits go. This is based on a display used by a prominent manufacturer of similar type "user friendly" entry panels that I have seen.

After the 3 keypad digits have been entered, the display just shows "CALLING" whilst the relay is activated.That is the last message displayed in the whole sequence of events. After that, the system resets and reverts to the welcoming message.

I will let you know how I make out with the new coding.

Cheers,

Soundguy.

#### Mr RB

##### Well-Known Member
Hi again, MR. RB
Just to make sure that I understand, are you suggesting that I use timer0 for a system tick, for general use, and timer 1 for special items such as the 5 second countdown?
Yep, leave freerunning TMR1 for the 5 sec system. The TMR0 you can trash for general use, ie clr it then use for simple delays up to 256 uS etc.

In response to your question about multiplying the 2nd digit, the idea was to add up the numbers created by the keypad decoding and end up with a number that would translate directly to the relay number that I want to activate. I originally wrote some code that simply incremented a register form 0-40, and output the appropriate code to work with the 5 X 8 matrix arrangement for the relays and output indicating LED's as a test.
Yeah I got what you did, its a sensible system. To get the single value, you could just add 10 to that value, for each count in the 10's digit.

Personally I would have kept the data entry/display tasks immediate and as simplified as possible, with the relay decoding all done at the end in the relay driver code.

When I first started designing this project, I built a circuit board with 40 LED's arranged in a 5 X 8 matrix in order to control all of them with a single port, including a seperate enable line, which is used to turn the selected output on for whatever length of time you want, as well as intoducing a cadence to the output, if needed, such as duplicating a telephone ring signal, be it either North American or European style ring cycles. This is the method I have used in the prtototype for controlling the relays.
Sounds nice and professional.

Lastly, the message displayed in the LCD after the 1st keypress is really just a screen full of asterisks with a space in the middle of the bottom line where the entered digits go. This is based on a display used by a prominent manufacturer of similar type "user friendly" entry panels that I have seen.

After the 3 keypad digits have been entered, the display just shows "CALLING" whilst the relay is activated.That is the last message displayed in the whole sequence of events. After that, the system resets and reverts to the welcoming message.
Cool I get it. To fill the screen with * should take less than 1mS to write those 30 or so chars. That would just be an immediate task on receipt of the first keypress.

Cheers.

Last edited:

#### Mike - K8LH

##### Well-Known Member
Soundguy,

Forgive me for butting in but I wonder if you couldn't just poll the keypad encoder chip? For example, assuming the 'keypressed' line on RB4 and the four data bits on RB3..RB0, there should be plenty of time in your application to sample the switches without ever missing a new key press.

Code:
sample
movf    PORTB,W         ;                                 |B0
andlw   b'00011111'     ; mask off unused bits            |B0
xorwf   swlatch,W       ; changes?                        |B0
bz      GetLp           ; no, branch, else                |B0
xorwf   swlatch,F       ; update switch state latch       |B0
btfss   swlatch,4       ; new release? no, skip, else     |B0
goto    sample          ; branch, try again               |B0
call    beep            ; send new press beep             |B0
movf    swlatch,W       ; get data again                  |B0
andlw   0x0F            ; mask off unused bits            |B0
addwf   PCL,F           ; return key value                |B0
dt      "0123456789ABCDEF"
That's a "blocking" subroutine which probably wouldn't do you much good but if you put it inside a 5 second timeout routine then it might do what you want.

Code:
;
;  get key with 5 second time-out
;
GetKey
movlw   5               ; timeout = 5.000 seconds         |B0
movwf   secs            ;                                 |B0
clrf    msecslo         ;                                 |B0
clrf    msecshi         ;                                 |B0
GetLp   DelayCy(1*msecs-23)     ; sample at 1 msec intervals      |B0
movlw   d'99'           ; reset value for 'msecslo'       |B0
decf    msecslo,F       ; dec msecs lo                    |B0
btfsc   msecslo,7       ; negative? no, skip, else        |B0
decf    msecshi,F       ; dec msecs hi                    |B0
btfsc   msecslo,7       ; negative? no, skip, else        |B0
movwf   msecslo         ; reset msecslo = 100             |B0
movlw   d'9'            ; reset value for 'msecshi'       |B0
btfsc   msecshi,7       ; negative? no, skip, else        |B0
decf    secs,F          ; dec seconds counter             |B0
btfsc   msecshi,7       ; negative? no, skip, else        |B0
movwf   msecshi         ; reset msecshi = 10              |B0
setc                    ; preset timeout indicator        |B0
movf    secs,W          ;                                 |B0
iorwf   msecslo,W       ;                                 |B0
iorwf   msecshi,W       ; timer timed out?                |B0
skpnz                   ; no, skip, else                  |B0
return                  ; return W = 0 and C = 1          |B0
movf    PORTB,W         ;                                 |B0
andlw   b'00011111'     ; mask off unused bits            |B0
xorwf   swlatch,W       ; changes?                        |B0
bz      GetLp           ; no, branch, else                |B0
xorwf   swlatch,F       ; update switch state latch       |B0
btfss   swlatch,4       ; new press? yes, skip, else      |B0
goto    GetLp           ; branch, ignore 'new release'    |B0
call    beep            ; send new press beep             |B0
movf    swlatch,W       ; get data again                  |B0
andlw   0x0F            ; mask off unused bits            |B0
addwf   PCL,F           ; return W = key and C = 0        |B0
dt      "0123456789ABCDEF"
;
;  beep
;
beep
bsf     beepctr,4       ; task 32 msec "beep"             |B0
toggle  movf    PORTA,W         ;                                 |B0
xorlw   1<<spkr         ; toggle spkr bit                 |B0
movwf   PORTA           ; toggle spkr pin                 |B0
DelayCy(1*msecs-6)      ; interval for 500 Hz tone        |B0
decfsz  beepctr,F       ; done? yes, skip, else           |B0
goto    toggle          ; toggle spkr again               |B0
return                  ;                                 |B0
;
A GetKey subroutine with 5 second timeout should make your main program a lot simpler. Just insert your LCD strings as required.

Code:
Loop
PutLCD (cmd,clearscreen);                                 |B0
PutSTR  "Welcome"
PutLCD (cmd,line2+0)    ; line 2, htab 0                  |B0
PutSTR  "Resident code? "
Digit1  call    GetKey          ; get key (5 second timeout)      |B0
bc      Digit1          ; branch if timeout               |B0
xorlw   '0'             ; is it '0'?                      |B0
bnz     Digit1          ; no, branch, else                |B0
movlw   '0'             ;                                 |B0
call    PutDat          ; print the '0'                   |B0
Digit2  call    GetKey          ; get key (5 second timeout)      |B0
bc      Loop            ; branch if timeout               |B0
movwf   tens            ; save key, '0'..'9'              |B0
sublw   '3'             ; '0'..'3' range?                 |B0
bnc     Digit2          ; no, branch, else                |B0
movf    tens,W          ;                                 |B0
call    PutDat          ; print to LCD                    |B0
Digit3  call    GetKey          ; get key (5 second timeout)      |B0
bc      Loop            ; branch if timeout               |B0
.......

Regards, Mike

Last edited:

#### Soundguy

##### New Member
Thanks for the input, Mike.

I am going to poll the keypad decoder now, instead of using it for an interrupt. I intend tp poll it during a TMRO interrupt period, based on what MR.RB suggested.

I like his idea of using the TMRO for a system "tick", and using the TMR1 for any other timing issues that I need to deal with.

I will look carefully at the code you provided, as I think that it will be usefull.
BEEBOP has also been very helpful.

I am not uncomfortable with PIC's and assembly language, but I know that I still have a lot to learn. The timer modules and such are items that I have not utilized yet, as I am having trouble getting my head around some of the concepts.

I really appreciate everybody's help and input on this project, and hope to be able to contribute, myself in the future to this forum as well as the other forums on this site.
I welcome any and all comments on this project, as I think that if I hit myself in the head with all of this information, it is bound to sink in.

When it is all done, I'll post the results.

Regards,

Soundguy

Status
Not open for further replies.