1
\$\begingroup\$

I am completely new to assembly, and I must develop a counter using PIC16F628A, a push button, and a display. Additionally it there will be an external oscillator (555).

I made some progress on this, but I think I need some help from you people. At first I did a delay based on decrements in order to be able to watch numbers on the display.

My problem now is that once I press the button, I need it to count only one number, independently how much time I keep it pressed. Something like, if it changes state up, it will increment 1. I believe this must be done with interrupts, I guess.

Now, what is the best solution to my problem? External interrupt, by state interrupt? Any other thing?

Olin Lathrop
317k37 gold badges447 silver badges935 bronze badges
asked Jul 2, 2017 at 12:32
\$\endgroup\$
5
  • \$\begingroup\$ How is the switch wired to the pin? Is it so that you get a 1 when pressed? Or a 0 when pressed? I also gather you are not allowed to use a timer for this. Is that correct? \$\endgroup\$ Commented Jul 2, 2017 at 16:15
  • \$\begingroup\$ A zero, actually it is demanded to be used pull up buttons. \$\endgroup\$ Commented Jul 2, 2017 at 16:37
  • \$\begingroup\$ If you can test for the button being pressed, you can also test for when the button has been released. \$\endgroup\$ Commented Jul 2, 2017 at 16:55
  • \$\begingroup\$ The problem is not testing it being or not being pressed. The problem is doing it on a way that you press the button for one minute, a number is incremented on the display, one and only one number. The way I was doing this, while I kept the button pressed, the display was adding numbers. \$\endgroup\$ Commented Jul 2, 2017 at 17:03
  • \$\begingroup\$ No reason it has to be an interrupt. Why can't the program check that the button is pressed now and that it was unpressed last time the program checked the button? \$\endgroup\$ Commented Jul 3, 2017 at 5:55

3 Answers 3

1
\$\begingroup\$

Since you cannot use a timer (gathered from comments you've made), you need a suitable delay routine to provide a specific period of time. I like the period of \8ドル\:\textrm{ms}\,ドル from prior experience. But you can use any period you feel is appropriate. Assuming that your processor is using the factory calibrated \4ドル\:\textrm{MHz}\$ rate, the instruction cycle will be \1ドル\:\mu\textrm{s}\$ and it will take \8,000ドル\$ cycles to make up an \8ドル\:\textrm{ms}\$ period.

The delay code should probably be made into a subroutine, to avoid having to replicate it over and over.

DELAY8MS MOVLW 0x3E
 MOVWF DLO
 MOVLW 0x07
 MOVWF DHI
 DECFSZ DLO, F
 GOTO $+2
 DECFSZ DHI, F
 GOTO $-3
 NOP
 GOTO $+1
 RETURN

The total time occupied by the above routine can be computed as:

$$t=5\cdot\left[D_{LO}+2+256\cdot\left(D_{HI}-1\right)\right]$$

where \1ドル \le D_{LO}\le 256\$ and \1ドル \le D_{HI}\le 256\,ドル with 0 interpreted as 256. The CALL and RETURN instructions take up 2 cycles each and the above code takes all of that into account. Calling it should take exactly \8,000ドル\$ cycles and, at \4ドル\:\textrm{MHz}\$ this means \8ドル\:\textrm{ms}\$.

You will have to create those two variables, \$D_{LO}\$ and \$D_{HI}\$ somewhere. That can be done like this, I think:

 CBLOCK
DLO
DHI
 ENDC

There are, of course, other ways. And you can add an absolute address to the CBLOCK line if you want to place the block somewhere specific.

Now that you have a delay routine, you can proceed to the next step. You need two new routines. One that repeatedly delays until the button becomes active and one that repeatedly delays until the button becomes inactive. Debouncing is included here:

ACTIVE CALL DELAY8MS
 BTFSC PORTx, PINy
 GOTO ACTIVE
 CALL DELAY8MS
 BTFSC PORTx, PINy
 GOTO ACTIVE
 RETURN
INACTIVE CALL DELAY8MS
 BTFSS PORTx, PINy
 GOTO INACTIVE
 CALL DELAY8MS
 BTFSS PORTx, PINy
 GOTO INACTIVE
 RETURN

I don't know your port or pin number, so I just put in "dummy" values there. You need to replace them, properly. The above two routines assume that 0 is active and 1 is inactive.

Now you can write your main code:

MAIN ; <code to reset your counter value>
 GOTO LOOP_NXT
LOOP CALL ACTIVE
 ; <code to increment your counter value>
LOOP_NXT ; <code to display your counter value>
 CALL INACTIVE
 GOTO LOOP

The above code resets your counter value to whatever you want to start at and then it jumps into the loop where it displays the value and waits for the button to become inactive. The effect here is that if you start up your code with the button pressed (it should not be, but what if it is?), then the code will still reset the counter and display it... but it will wait until you release it before continuing. So you have to let up on the switch.

Then, once that has happened, the basic loop just waits for a debounced active state of the switch. When it sees that, it increments the counter immediately (on the press, not on the release) but then waits for the button to be released before continuing, again.

That's about it. You still need to write appropriate code for the counter and display. But that gets the idea across for the rest.

answered Jul 2, 2017 at 18:22
\$\endgroup\$
15
  • 1
    \$\begingroup\$ Mr. I have no words to thank you! Really! I already had it working with delays to take care of debouncing somehow, I just adapted all things to your code and it is almost done, I am just working on the reset button that is not working now (RA7), but that is a matter of time and analyzing. Thanks a lot! You saved my day. THANKS!!!!! \$\endgroup\$ Commented Jul 2, 2017 at 22:51
  • 1
    \$\begingroup\$ @ERS Glad to hear it helps out. Study it a bit and make sure you follow the logic well. If not, feel free to ask more questions. Also, don't forget to select an answer if you feel you have what you need (and if you feel you've waited long enough for others to add their thoughts, as well.) \$\endgroup\$ Commented Jul 2, 2017 at 23:28
  • \$\begingroup\$ I will definitely study it, since after the project is done, I will have to right a report on every aspects of it. Ok if I have any doubt I will reach you here. Thanks again \$\endgroup\$ Commented Jul 2, 2017 at 23:38
  • \$\begingroup\$ just two questions, any reason to repeat code in the INATIVE and ACTIVE routines? What exactly does, for example GOTO $-3 ? That minus 3? Thanks in advance \$\endgroup\$ Commented Jul 3, 2017 at 21:26
  • \$\begingroup\$ @ERS ACTIVE and INACTIVE aren't the same code. Note BTFSS and BTFSC. And I could just use a label in the other routine with the $-3 present, I suppose. The $ means "here" and the -3 means backwards three instruction words. \$\endgroup\$ Commented Jul 3, 2017 at 23:59
2
\$\begingroup\$

You need to "debounce" your switch. This can be done in hardware by adding a small capacitor across the input or in software by checking that the switch maintains its state for long enough.

Now the other problem is that I need to add the value of 1 once and only once while pressing the button. Imagine the button pressed by 1 minute and it must be not add more than the initial increment of 1.

Now you need a software "one-shot". You need to remember if the button was pressed the last time you looked.

// Pseudo code
if(button) { // Button was pressed
 if(!buttonMemory) { // Button was off last time we looked.
 counter ++; // Increment the counter
 buttonMemory = true; // Remember the button was pressed.
 }
} else {
 buttonMemory = false; // Cancel the memory.
}
answered Jul 2, 2017 at 12:37
\$\endgroup\$
6
  • \$\begingroup\$ Thanks for your help, debounce problem its solved for now I guess. Now the other problem is that I need to add the value of 1 once and only once while pressing the button. Imagine the button pressed by 1 minute and it must be not add more than the initial increment of 1. I don't know if I am writing this in a confusing manner, english is not my main language, sorry. \$\endgroup\$ Commented Jul 2, 2017 at 14:19
  • \$\begingroup\$ See the update. Your English is quite clear. \$\endgroup\$ Commented Jul 2, 2017 at 16:44
  • \$\begingroup\$ Thanks again. It makes complete sense in C, I will try to do it in assembly \$\endgroup\$ Commented Jul 2, 2017 at 16:57
  • \$\begingroup\$ @ERS Lots of ways to do the debouncing. On a more sophisticated microcontroller you would have edge sensitive interrupts and independent timers you can use to trigger NN millisecond dead time once your button interrupt fires. Get interrupt, set flag. Timer interrupt fires, clear flag. And incidentally you want resistor and a capacitor for a proper low-pass debouncer. \$\endgroup\$ Commented Jul 2, 2017 at 16:59
  • \$\begingroup\$ @Barleyman: All good stuff but the guy is just getting started. Let him master and understand the basics first. \$\endgroup\$ Commented Jul 2, 2017 at 17:26
1
\$\begingroup\$

There are several issues. It seems from your vague description that you want to measure the time a button is pressed. You have two problems, debouncing the button and measuring the time the debounced button is down.

I would do both in a 1 ms periodic interrupt. Get used to doing that. A 1 ms clock tick is useful for lots of things.

On this particular processor, timer 2 with its built-in period register is well suited for this. Set it up to cause a interrupt every 1 ms (1 kHz rate).

In the interrupt routine, you first debounce the button. I like to use 50 ms as the debounce period. Most buttons bounce for around 10 ms, but I've seen some that bounce nearly 50 ms. Also, that's about the maximum time that humans don't notice as a delay, so there is no harm at 50 ms to the user experience.

Keep a flag that is the official debounced state of the button. When the button's instantaneous state is the same, reset the debounce counter to 50. when the button's instantaneous and debounced states differ, decrement the counter. When the counter reaches 0, change the debounced state to the current button state and reset the counter to 50.

Now that you have the debounced state of the button, simply count up whenever the button is pressed and do nothing when it is released. In the special case of a down to up transition, save the counter value and set a flag for the foreground code indicating a new measured length is available. Clear the counter.

This counter will need to be multiple bytes long. A single byte can only count to 255, which is about ¼ second. With two bytes, the button can be held down for a bit over a minute. That might be good enough. Just to be safe, you should only increment the counter when it's not already at maximum. That way a long button press just clips at the maximum representable value.

answered Jul 2, 2017 at 13:01
\$\endgroup\$
4
  • \$\begingroup\$ Hello, first of all, thanks for your reply. I am testing debouncing a button with the output of a led. So far so good. The second problem, must be solved with timer? I mean, I was told that in this work I need to avoid timers. Can it be something like while button pressed, add value one and nothing more. Some kind of interruption? \$\endgroup\$ Commented Jul 2, 2017 at 14:12
  • \$\begingroup\$ Add value one, one time only \$\endgroup\$ Commented Jul 2, 2017 at 14:16
  • \$\begingroup\$ By the way, the bouncing will be too fast to see on an LED. \$\endgroup\$ Commented Jul 2, 2017 at 16:53
  • 1
    \$\begingroup\$ You can do a whole embedded control system with a task scheduler based on a system heartbeat timer. I like to use two timers, one for high level stuff with around 1ms (500 microseconds, whatever) period and another with short period for real time metrics, performance analysis, etc. \$\endgroup\$ Commented Jul 2, 2017 at 17:06

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.