Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Baseline PIC TMR0 Question

Status
Not open for further replies.

throbscottle

Well-Known Member
I've managed to get press or double-press detection of a switch using TMR0. What happens is, the timer is tested periodically during the main loop. When a switch is pressed once, it sets a countdown, which is changed every time the timer overflows a set value. If the switch is pressed again within that time, it counts as a double press. If the counter reaches zero, it counts as another single press.

This is where it gets interesting. When I look at tutorials such as Gooligum, the timer is always tested for a specific value using XOR. This can't work in my case, because the amount of instruction cycles in-between tests can be different depending on which switch was pressed, so the timer can be at various values when it hits the overflow. I've used subtraction instead - if the timer gets over 200 the counter increments.

So the solution works, but the timer can't be accurate. What's a better way?

Code:
; test and read inputs up to here
; tactile press timer
    movlw 0xC8 ; test the timer for double press events
    subwf TMR0,W ; carry will be clear if under 200
    btfss STATUS,C ; carry will be set if it's over 200
    goto CLR_C ; next stage
    movf tact_counter,f ; counter is set when single press occurs
    btfss STATUS,Z ; don't decrement the counter past zero
    decfsz tact_counter,f ; double press timer
    goto CLR_C
    bcf pre_fl ; flag that is set when single press occurs
CLR_C
; start reading tactile data
; loop back
 
You're using only the low byte of timer 0 and testing if under 200, is that correct? I don't know which chip you're using but on most pics timer 0 is 16 bit and free running.

I can't understand your code from the snippet you posted. I'm guessing you're trying to detect single and double presses but don't see how.

Mike.
Edit, see in the title you state baseline which suggest a 16 bit timer 0 with no period register. Correct?
 
which is changed every time the timer overflows a set value

Are you clearing the timer at some point? If not, the value will be random on the first comparison?

If it's free running, you could store the present counter value at the end of each comparison, and subtract that from the new timer value at the start of each test & comparison.
 
Most PIC processors have TMR0 as a 8 bit timer. So, the TS has to give a bit more information as to what processor this is for, and a bit more code showing...
 
Most PIC processors have TMR0 as a 8 bit timer. So, the TS has to give a bit more information as to what processor this is for, and a bit more code showing...
I wouldn't say 'most', but old PIC devices are only 8 bit TMR0, 'modern' devices are usually 8/16 bit TMR0 - with the 8 bit mode for compatibility.

On a fairly random selection of devices I use, only one was 8 bit (from 2011) the other four were 8/16 bit (from 2015-2017).
 
It's ancient! 16F57. So it's an 8 bit timer with nothing extra. It just rolls over at 255 and that's all it does.

The snippet I posted is the only part that's really relevant to the timer, the single/double click detection relies on the flag bit, pre_fl, being set or unset. First click sets it, then either it gets unset by the timer finishing, or by a double-click being detected.

rjenkins - do you mean save the present timer value? I might try that.

I started off clearing the timer, not realising it runs all the time anyway. When I found that out, I removed the clrf tmr0 line because there didn't seem to be any point as the code just falls through to test the next button, so the number of lines executed can be different.

Using the timer is still something of a Dark Art to me so I don't really know what I'm doing here. The upside is that it's only timing button presses so accuracy doesn't matter. But what if it did matter?

Anyway, the part that does the actual click detection is here (from PRES_1) (when I'm happy with it I'll copy it down to PRES_2 and PRES_3)
(I know I can make the PRES_x code more efficient - but I've only just got it to work at all)

Code:
BUTTONS ; scans keypad, rotary encoder, runs subs as necessary according to inputs
...
TACTILES ; detects keypresses in variable scanned from PT6315 VFD driver, which also has switch inputs.
... ; set up PT6315
; tactile press timer
    movlw 0xC8 ; test the timer for double press events
    subwf TMR0,W ; carry will be clear if under 200
    btfss STATUS,C ; carry will be set if it's over 200
    goto CLR_C ; next stage
    movf tact_counter,f ; counter is set when single press occurs
    btfss STATUS,Z ; don't decrement the counter past zero
    decfsz tact_counter,f ; double press timer
    goto CLR_C
    bcf pre_fl ; flag that is set when single press occurs gets cleared if counter reaches zero
CLR_C
    bcf STATUS,C
TACTILE_LOOP
; read switches from PT6315
PRES_1
    btfss tactiles+1,3 ; first preset
    goto PRES_2
    btfsc pre_fl ; has a button already been pressed?
    goto DOUBLE_1 ; do double press actions - assume it's the same button
    banksel preset_1 ; presets are in MATHS2 bank
    movlw preset_1 ; get address of variable, copy to BCD
    pscall PRESET_COPY ; sub does return banksel and sets timer-runs count
    bsf pre_fl ; set the flag, do double-press test next time
;    clrf TMR0 ; not using this now, demo only
    goto END_PRESETS ; exit the sub
DOUBLE_1
    movf tact_counter,f
    btfsc STATUS,Z
    goto END_PRESETS ; timer has finished, this is another single press
SAVE_1
    call GO_REVERT_BCD ; get BCD back from copy
    pagesel$
    banksel preset_1
    movlw preset_1
    goto SAVE_PRESETS
    
PRES_2
    ; detect bit 4
PRES_3
    ; detect bit 5
SAVE_PRESETS
    pscall PRESET_SAVE ; save BCD to the preset
    bcf pre_fl
END_PRESETS
    call GO_DISPLAY ; show the preset
;    pscall ENTER ; write the new frequency to AD9850 ; WANT MANUAL ENTER?
    goto BUTTONS ; finished with timers, go back. No double-press occurred
 
To really beat the oldnest stakes, it will eventually end up on a 16C57 since I bought 2 by mistake when I was (more) clueless.
 
OK, with the restricted hardware available, the way I'd do it is have a byte variable as a timer for each button.

When a button is pressed, first check the value in that variable. If it's zero, it is a first or single press.
Then store a value, eg. 100 and set a bit variable to show the time countdown was started.

If it's not zero, there was a previous press, so clear the value and set your double-press flag.

In your main loop, check for any set bit variables and see if the respective timer has reached zero for any button.
If it has, that was a single press. Set your double press flag and reset the bit variable for that button.

Set up an interrupt on timer overflow, and in the interrupt routine check each button variable value & decrement any that are not zero.

That will give a consistent "count down" time in the button variables.

If the counter interrupt is too fast to get a reasonable time value, use an extra byte variable as a prescale counter; eg. if it's interrupting at 4KHz, only decrement the counters every eg. 8th interrupt, to give a count rate around 500 Hz, so a time range up to around half a second in 2mS increments.
 
To really beat the oldnest stakes, it will eventually end up on a 16C57 since I bought 2 by mistake when I was (more) clueless.
I gave away 200 'C' series PIC's a number of years ago - I was given them where I used to work, by a guy who was clearing out an industrial unit he'd taken over (along with more stuff). I offered them on here, and arranged to meet a guy at a local radio rally - bit of a cock-up though, we arranged to meet at a certain time in front of the Soviet radio truck, which was there every year. So at the appointed time I was there, but the other guy wasn't? - turned out that year, for the first time ever, there were TWO Soviet radio trucks :D

Eventually we met up, and I gave him the tubes of chips.

To be honest, I would just dump the 16C57's, it's not worth messing about with OTP antique devices, when modern PIC's are so much better.
 
To really beat the oldnest stakes, it will eventually end up on a 16C57 since I bought 2 by mistake when I was (more) clueless.
Have you checked whether your current programmer can program the 16C57? If not, it would probably be cheaper to buy a mid-range or enhanced mid range chip. (e.g., 12F1840 or 16F1829) .
 
Well, it (a PICKit 2 clone) works with the 16F57 I'm using for development. If the 16C57s fail it doesn't matter, they were 50p for 2. If it works my long ago mistake is useful for something. I think there are 1 or 2 config bits I need to change. Ultimately I still have the F to fall back on.

TBH those 2 PICs have 100%+ more features than I need. The '57 has 20 port pins available and I'm using 19 of them. I'm using it to get user inputs and run a display controller and DDS chip. Basic stuff. If I had less i/o on the PIC I would need support chips - as it is it doesn't need any.

The press / double-press routine works btw, but I discovered a significant bug - it needs a timer for single press events, otherwise everything is registered as a double press! At least I already have one to call.
 
I use MPLab 8.92. The 16F57 is not the same as the 16C57. Your PK2 will not work:

1679180595673.png


I have a little experience with some 16Cxx chips. That's why I asked the question. Of those 3 programmers, the one you will most likely find is the PICStart Plus. It should be around $20 USD. It's price has inflated considerably since I last looked: https://www.ebay.com/sch/i.html?_from=R40&_trksid=p3519243.m570.l1313&_nkw=PICStart+PLus&_sacat=0

Buying several 16Fxxx or 16F1xxx (even a 12F1xxx) will be cheaper.
 
Oh. So the 'C's are junk to me anyway then. Never mind. I did get them at a time when I was trying to make a serial port programmer using a usb adapter (which /almost/ worked!). They've been sitting in a drawer since about 2010! Still experiments to be had in that direction I suppose.
 
Oh. So the 'C's are junk to me anyway then. Never mind. I did get them at a time when I was trying to make a serial port programmer using a usb adapter (which /almost/ worked!). They've been sitting in a drawer since about 2010! Still experiments to be had in that direction I suppose.
As the author of the worlds first Windows based PIC programmer software (WinPicProg) I would seriously suggest you give up any such ideas :D The advent of cheap PK series programmers and their integration with MPLAB has rendered such items pretty pointless.

Even 'back in the day' I purposely only supported parallel port programmers, even though it would have been trivial to add serial port programmers (which don't actually even use the serial port anyway), as I considered them far too unreliable - as they relied on the PC's serial port exceeding the RS232 specification.

I gave free support on my free software, I wasn't prepared to do that when people were going to try and use hardware that only had a small of chance of working in the first place.

Those that supported serial port programmers didn't offer any kind of customer support, and for good reason - and some were even based on my original Windows 3.1 code, which I released for free later on - complete with my copyright message still intact :D

For anyone who might be vaguely interested, my original software (PicProg) was written under DOS using Turbo Pascal - and the Windows version was written as a learning exercise using Delphi (effectively Visual Turbo Pascal for Windows).
 
Back to the code.... When I first looked at it, it wasn't clear what you were trying to do. I am not referring to the instructions, just the purpose. For example, is the click/double click distinction for debouncing? Second, TMR0 is pretty good in that you can have an 8-bit pre-scaler and can pre-load a value (as already mentioned) to use the interrupt rather than polling for defined intervals.

It would help enormously if you could describe in plain English what that section is intended to do and why. What period in units of time are you trying to achieve? A flow chart might help but is not necessary once we know the what and why.
 
I vaguely remember someone wrote a pic programmer using an Arduino. It was probably LVP only but it wouldn't be hard to add the high voltage bit.

Mike.
 
This manual from Microchip might help:

From: 2.1.2 (12C5XX)
VPP: VPP can be a fixed 13.0V to 13.25V supply. It must not exceed 14.0V to avoid damage to the pin and should be current limited to approximately 100 mA. VDD: 2.0V to 6.5V with 0.25V granularity. Since this
method calls for verification at different VDD values, a programmable VDD power supply is needed.
Current Requirement: 40 mA maximum
It even tells how to construct appropriate drivers to provide the voltage and current requirements. I believe the PK3 is limited to 20 mA or less. Don't know about the PK2. Of course, having the physical tool to program is only half the problem.. Somehow, you need to make it follow the correct protocol, etc. , and trying to get a Microchip programmer to do something that it is not natively designed to do can be "trying" at best. Solving "failure to connect to target" and "target ID returned not as expected" can be very difficult. I spent 3 months trying to get my ICD3 running with MPLab8.92 to recognize a 16F1789. Microchip was no help. Eventually I got an older .jam file, loaded that into my ICD3, and the problems went away. In other words, using a PK2 with an OTP chip might be doable, but it will take some work and maybe additional hardware.

Today's prices on DigiKey:
12F509 $1.20 (baseline)
12F683 $2.30 (midline)
12F1840 $1.85 (enhanced midline)

The question is whether it is worth it.

EDIT: I forgot to mention that baseline chips like the 16C57 (12-bit core) do not have interrupts. One needs to go to midline chips ( 14-bit core) like the 12F6xx to get interrupts. The 16F84 was one of the earliest MCC chips with interrupts as I recall. An interrupt-capable chip, particularly one that automatically saves context could be helpful.
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top