![]() | ![]() | ![]() |
| | |||||||
| Micro Controllers Discuss all aspects of micro controllers - building them, coding them, etc. All controllers are welcome - PIC, BASIC, Z8 Encore!, etc. |
| | LinkBack | Thread Tools | Display Modes |
| | (permalink) |
| Laptimer I am building a laptimer. So far i have tried using the TMR0, with not perfect results. PIC i am using is 16F876A. My timer is doing the interupt every 8.192 ms. Isn't it possible to set it up better? I want to have a interupt every 10ms exactly. Or anything that is devideble with 10. Is that possible. What configuration should i use then? I have also looked at a I2C RTC: PCF8583 This device can manage 10ms. What would you recomend? Is there any other better choice? | |
| |
| | (permalink) |
| You want exactly 10msec and it's set for 8.192msec right now? Replace the crystal you're using right now to 81.92% of the value. For example, if it is 4.000Mhz right now, then replace it with a 3.2768Mhz crystal.
__________________ "Having to do with Motion Control" | |
| |
| | (permalink) | |
| Quote:
I found a program on the net today, it's a TMR0 calculator. Watch this image: http://w1.278.telia.com/~u27812750/Files/tmrsetting.GIF According to this, it would be possible to change the TMR0 Offset and get exactly 2 ms. If this is possible, how do i do that then?[/quote] | ||
| |
| | (permalink) |
| No need to replace the crystal. But you better get a calculator ready. All you need to do is calculate a prescaler setting that is a bit too high. For example, of calculations say you need 1:27, then choose 1:32. Then calculate how many ticks too short your timer will get before generating an interrupt and pre-load TMR0 register with this value... To say it simple, you can give TMR0 a 'head-start' by already loading a value before it starts ticking. Your pic also has a TMR1 module. If it REALY needs to be exact then you better use TMR1 - its 16 Bits so much more precise | |
| |
| | (permalink) |
| Okay, i have thougt about that too, but how do i set the TMR0 value? Look at the imgage i attached. Is it the TMR0 Offset you are talking about? | |
| |
| | (permalink) |
| MOVLW TimerOffsetValue MOVWF TMR0 and then start your timer running | |
| |
| | (permalink) |
| I am using PicBasic Plus 2.0 Can i write it just like this? asm MOVLW 6 MOVWF TMR0 endasm Or do i have to write 6 in a different way than decimal? | |
| |
| | (permalink) |
| I am using PicBasic Plus 2.0 Can i write it just like this? asm MOVLW 6 MOVWF TMR0 endasm Or do i have to write 6 in a different way than decimal? | |
| |
| | (permalink) |
| There are problems tinkering with the timer0. "Writing to TMR0, when the prescaler is assigned to Timer0, will clear the prescaler count, but will not change the prescaler assignment." Depending on when you write to Timer0, the prescaler will reset at that instant. This makes the precise time that Timer0 resumes counting after being reset unpredictable. This makes accurate time keeping impossible. If you don't want to change crystals, use the Capture, Compare, PWM module (CCP1). To setup: 1. Setup timer1 for 1:1 prescale and internal clock source. 2. Set CCP1 to mode 1011, "compare mode - trigger special event". 3. Assuming a 4.000Mhz crystal, load the CCPR1H:CCPR1L register pair with 2710h (decimal 10000). This periodically sets the CCP1F flag at a 10msec rate which can be used to trigger interrupts or can be polled in software.
__________________ "Having to do with Motion Control" | |
| |
| | (permalink) | |
| You can access the timer register straight woth PICBASIC PRO. Study this code and see how you can modify it for your needs.... If you "pre-set" the counter to certain number so that it takes so many tick to overflow... you can generate your 10msec interrupt..... Code: ' PicBasic Pro program that demonstrates the use of the Timer1
' interrupt for a real-time clock. Written for the LAB-X1
' experimenter board with a 16F877
DEFINE LOADER_USED 1
' Define interrupt handler
DEFINE INTHAND myint
wsave VAR BYTE $20 system
wsave1 VAR BYTE $a0 system ' Necessary for devices with RAM in bank1
wsave2 VAR BYTE $120 system ' Necessary for devices with RAM in bank2
wsave3 VAR BYTE $1a0 system ' Necessary for devices with RAM in bank3
ssave VAR BYTE bank0 system
psave VAR BYTE bank0 system
TICK VAR BYTE bank0 ' make sure that the variables are in bank 0 if they are to be used in the interrupt handler
seconds VAR BYTE ' Elapsed seconds
minutes VAR WORD ' Elapsed minutes
minutes = 0 ' Clear time
seconds = 0
T1CON = $01 ' Turn on Timer1, prescaler = 1
INTCON = $C0 ' Enable global interrupts, peripheral interrupts
PIE1 = $01 ' Enable TMR1 overflow interrupt
GoTo main ' jump over the interrupt handler and sub
' Assembly language interrupt handler
Asm
myint
; Uncomment the following if the device has less than 2k of code space
;movwf wsave ; Save W
;swapf STATUS, W ; Swap STATUS to W (swap avoids changing STATUS)
;clrf STATUS ; Clear STATUS
;movwf ssave ; Save swapped STATUS
;movf PCLATH, W ; Move PCLATH to W
;movwf psave ; Save PCLATH
; Set the high register of Timer1 to cause an interrupt every
; 16384 counts (65536-16384=49152 or $C000). At 4MHz, prescale
; set to 1, this equates to a tick every 16384uS. This works
; out to about 61 ticks per second, with a slight error. The
; error could be reduced substantially by setting the TMR1L
; register and playing with different values for the prescaler
; and the ticks per second.
movlw 0C0h ; Prepare to set TMR1 high register
movwf TMR1H ; Set TMR1H to C0h
incf _TICK,F ; INCREMENT TICK COUNT
bcf PIR1, 0 ; Clear interrupt flag
movf psave, W ; restore the state of everything
movwf PCLATH
swapf ssave, W
movwf Status
swapf wsave, F
swapf wsave, W
retfie ; Return from interrupt
EndAsm
' PicBasic subroutine to update the minutes and seconds variables
get_time:
' Update the time when needed. The TICK variable will
' overflow if you don't update within 4 seconds. This could
' be done in the interrupt handler, but it's easier to do
' it in PicBasic, and you usually want the interrupt handler
' to be as short and fast as possible.
PIE1 = 0 ' Mask the interrupt while we're messing with TICK
seconds = seconds + (tick / 61) ' Add the accumulated seconds
tick = tick // 61 ' Retain the left-over ticks
PIE1 = $01 ' Interrupt on again
minutes = minutes + (seconds / 60) ' Add the accumulated minutes
seconds = seconds // 60 ' Retain the left-over seconds
Return ' Return to the main program
main
' **************************************************************
' Begin program code here. The minutes and seconds variables can
' be used in your code. The time will be updated when you call the
' get_time routine. Disable interrupts while executing timing-critical
' commands, like serial communications.
DEFINE LCD_DREG PORTD
DEFINE LCD_DBIT 4
DEFINE LCD_RSREG PORTE
DEFINE LCD_RSBIT 0
DEFINE LCD_EREG PORTE
DEFINE LCD_EBIT 1
ADCON1 = 7 ' Set PORTA and PORTE for digital operation
Low PORTE.2 ' Enable the LCD
Pause 150 ' Pause to allow LCD to initialize
LCDOut $fe,1 ' Clear LCD
loops VAR WORD
loops = 0
Loop:
loops = loops + 1
LCDOut $fe,$C0,"Loops Counted: ", DEC5 loops
GoSub get_time ' Update minutes and seconds
LCDOut $fe, 2, "Time: ",DEC5 minutes, ":", DEC2 seconds ' Display the elapsed time
GoTo loop ' Repeat main loop
End Focus on the part that says: Quote:
Ley us know how it goes Ivancho | ||
| |
| | (permalink) |
| Wow, it doesn't look too easy... But i will have to try! I just want to check one thing first. I have always used software interupts in PBP. How big will the difference be if i write the interupthandler in ASM? Is it possible to write the handler in ASM but anyway, when it is doing the interupt it goes to a label i the basic code? When i am using the software interupts i am trying to avoid pauses in the code. My main loop looks like this: Main: if Lap = 1 and Second > 1 then gosub NewLap if Func = 1 then goto SelectFunc if pcDTR=1 then goto pcConnect goto Main PicBasic Plus have the float point routines, so i have tried to do like this. My tmr0 is making a interupt every 8.192ms. Code: disable
tickint:
Milli = Milli + 8.192
if Milli >= 1000 then
Milli = (Milli - 1000)
Second = Second + 1
if Second = 60 then
minute = minute + 1
Second = 0
endif
endif
Hundred = Milli / 10
INTCON.2 = 0 ' Reset timer interrupt flag
resume
enable Should this work? | |
| |
| | (permalink) |
| :arrow: Are you using a 4Mhz crystal?..... Inside the PIC this 4Mhz crystal gets divided by 4, so the actual clock is 1mHz, that is 1usec for thick. Timer1 is a 16Bit counter... that meant that it can count from 0 - 65535. If it increments every 1usec the Timer will overflow (interrupt) after 65535usec have passed. That is if you let the whole timer run you will get an interrupt every 6.5535 msec. :!: What you need to do is pre-set the timer's register with a number so that it gives you a 1msec per interrupt. So that after 10 interrupts you have your 10msec. IF you set the timer to (65535-10000=45535) then you will have an interrupt every 2msec. So every 5 interrupts you know that 10mSec have elapsed. Have in mind that Timer1 is a 16-bit timer while Timer0 is an 8-bit timer (0-255 @ 1usec is 255usec scalar 1:1) You can also play with the scalar and put it to 1:128 but you will loose resolution and gain time. You can also write straight to the timer resgister.... a TMR0 = 0 will reset the timer 0. Recheck the above code by parts... remember that only this part is assembler: Code: ' Assembly language interrupt handler
Asm
myint
movlw 0C0h ; Prepare to set TMR1 high register
movwf TMR1H ; Set TMR1H to C0h
incf _TICK,F ; INCREMENT TICK COUNT
bcf PIR1, 0 ; Clear interrupt flag
movf psave, W ; restore the state of everything
movwf PCLATH
swapf ssave, W
movwf Status
swapf wsave, F
swapf wsave, W
retfie ; Return from interrupt
EndAsm :idea: Say you have the interrupt set so that every 10 interrupts is 1 second (just an example) So if you do at the interrupt handler: Seconds= Seconds + (InterruptCounter/10) InterruptCounter/10 will only add 1 to the seconds when InterruptCounter = 10 .... other wise it will add 0. The assmebly part of the program increments the counts in the InterruptCounter. :arrow: Using a On Interrupt will make a way bigger program.... because it puts a command after every line of code to check for the interrupt flag..... Try creating a program that lights an LED every certain time..... with an interrupt. Then move forward into manipulating with values. And one last thing.... you have to get in and out of the interrupt handler ASAP. You can't do gosubs not sure about goto. But the more simple you have it the easier it is.... interrupts aren't too easy to star with.... go by KISS...Keep It Simple and Stupid :lol: | |
| |
| | (permalink) |
| Hey, thanks for the very good explanation! I will try to start using more ASM code then. I have only written program i basic before... I'll give it a try! | |
| |
| | (permalink) |
| I just have to ask on thing about the code example you wrote. What do i have to write in the beginning of the code to configurate the timer? Because this is only what happends every tick, right? This i really hard to me because i don't know a single command in ASM, i don't know what: movf means for example... | |
| |
| | (permalink) |
| That makes 2 of us..... I just know vary little ASM 1. You have to define interrupt variables 2. Jump the interrupt handler 3. Write the interrupt handler 4. Define your Program Variables 5. Start of program 6. Loop to start. So when the program runs the first time it does 1,2,4,5. Then it should stay in in 5-6 infinitly until a interupt occurs. Then it will jump to 2 and will return where it left off between 5-6. So what happens in the interrupt part? You increase a variable you can use in PICBASIC.... PICBASIC variables are the same as the ASM variables with an "_". So a PICBASIC variable named myVar will be call in the compile ASM: _myVar. That is what you are playing with. By incrementing in ASM: Code: incf _TICK,F ; INCREMENT TICK COUNT So before you enable the interrupts you set the TMR0 or TMR1 register to the value you want, so that the amount of counts left are the time you are looking for. Then you enable the interrupts and the timer will be set and will interrupt what you set it for, set the timer register again, increment the TICK and go back to your program. The timer register is set at: Code: movlw 0C0h ; Prepare to set TMR1 high register movwf TMR1H ; Set TMR1H to C0h Since the TMR! (timer resgister) is 16 bit, it is devided in two 8-bit parts TMR1H for the 8 most significat bits, and TMR1L for the remaining bits. Also you may want to set this bits before you enable your interrupts in PICBASIC by doing a TMR1H = and TMR1L = so that you can start with the interrupts right away. Then keep track of how many interrupts occur and when the TICK is at the value you want it, then you reset it in PICBASIC by TICK = 0... then the interrupts in ASM will start incrementig them by 1 Hope it helps some, could not really understand what you were asking Ivancho | |
| |