Lucky Luka
Starting Member
- Total Posts : 42
- Reward points : 0
- Joined: 2019/04/02 09:39:39
- Location: Italy
- Status: offline
PIC18F4580 timer 0 question - [SOLVED]
Hi all I am trying to use timer 0 to blink an LED: 1sec ON, 1 sec OFF. I tought that using the formula in the attached pic I could have reached my goal. I was wrong. I obtained 1.114 sec ON and and 1.114 sec OFF. Maybe the issue is related to what is written in the datasheet: "If the TMR0 register is written to, the increment is inhibited for the following two instruction cycles. The usercan work around this by writing an adjusted value to the TMR0 register."Is this the case? What values do I have to select as TMR0 in my formula? (I hope the formula is ok and the way I proceed is the one I chose... Any suggestions are welcome.) Thanks #include <xc.h> #include "timer_header.h" #include "stdint.h" #include "stdbool.h"
#define LED1 LATCbits.LATC0 // Green LED //#define LED2 LATCbits.LATC1 // Yellow LED
//#define BUTTON PORTBbits.RB0 // Button
uint16_t blink_count = 0; //uint16_t button_count = 0; //bool button_press = false;
// HIGH priority ISR void __interrupt(high_priority) HPbuttonISR(void){ // Check The Timer 0 Flag Bit if (INTCONbits.TMR0IF) { blink_count++; if(blink_count==10000) // 10000 timer overflows! { // Toggle green LED LED1 = ~LED1; // Clear The Global Counter blink_count = 0; } // Preload The Value To TMR1 Register Every Overflow Interrupt TMR0L = 55; INTCONbits.TMR0IF = 0; // Clear The Flag Bit } }
void main(void) { // Configure the pins: OUT = 0, IN = 1 TRISBbits.RB0 = 1; TRISCbits.RC0 = 0; TRISCbits.RC1 = 0; // 8MHz internal oscillator OSCCONbits.IRCF = 0x07; // Internal oscillator selected OSCCONbits.SCS = 0x03; // I wait until the oscillator's freq is stable while(OSCCONbits.IOFS!=1); // Timer 0 OFF T0CONbits.TMR0ON=0; // Timer 0: 8 bit T0CONbits.T08BIT=1; // Internal source clock T0CONbits.T0CS = 0; // Prescaler is bypassed --> 1:1 T0CONbits.PSA = 1; // Prescaler value //T0CONbits.T0PS=2; //1:8 // Preload The Value Which We've Calculated To The TMR0 8-Bit Register! TMR0L = 55; //RCONbits.IPEN = 1; // Enable Timer 0 interrupt INTCONbits.TMR0IE=1; // High priority for timer 0 interrupt INTCON2bits.TMR0IP=1; ei(); // This is like flipping the master switch to enable // all interrupts: it's like GIE = 1 // Timer 0 ON T0CONbits.TMR0ON=1; LED1 = 0; // Switch OFF Green LED //LED2 = 0; // Switch OFF Yellow LED // Infinite loop while(1){ } return; }
post edited by Lucky Luka - 2021/01/04 09:49:07
Attached Image(s) 
|
du00000001
Just Some Member
- Total Posts : 4120
- Reward points : 0
- Joined: 2016/05/03 13:52:42
- Location: Germany
- Status: online
Re: PIC18F4580 timer 0 question
2021/01/01 18:01:53
(permalink)
☄ Helpfulby Lucky Luka 2021/01/02 06:09:22
Any reason for the 10 kHz interrupt (other than counting the interrupts)? I'd suggest to lower the interrupt frequency by - using the highest prescaler value available/practical
- if possible using TMR0 in 16-Bit mode
- using the highest applicable TMR0 reload value
Beyond that: reloading TMR0 as soon as possible - directly following the test for the TMR0IF flag. Other options apply - like setting your cnt test to 8976 without changing anything else on your current code. (Although this is the worst of all possible solutions, it is the fastest way to realize a 0.5 Hz blink rate.)
PEBKAC / EBKAC / POBCAK / PICNIC (eventually see en.wikipedia.org)
|
ric
Super Member
- Total Posts : 29951
- Reward points : 0
- Joined: 2003/11/07 12:41:26
- Location: Australia, Melbourne
- Status: online
Re: PIC18F4580 timer 0 question
2021/01/01 18:36:14
(permalink)
☄ Helpfulby Lucky Luka 2021/01/02 02:37:04
Your existing code will run slow because you are not allowing for all the instructions that execute before you update the timer value. Just changing TMR0L = 55; to TMR0L += 55; will allow for it automatically. It is instructive to work out for yourself why this works.
To get a useful answer, always state which PIC you are using!
|
1and0
Access is Denied
- Total Posts : 12108
- Reward points : 0
- Joined: 2007/05/06 12:03:20
- Location: Harry's Gray Matter
- Status: offline
Re: PIC18F4580 timer 0 question
2021/01/01 20:09:29
(permalink)
☄ Helpfulby Lucky Luka 2021/01/02 06:09:10
Lucky Luka Maybe the issue is related to what is written in the datasheet: "If the TMR0 register is written to, the increment is inhibited for the following two instruction cycles. The user can work around this by writing an adjusted value to the TMR0 register." Is this the case? What values do I have to select as TMR0 in my formula? (I hope the formula is ok and the way I proceed is the one I chose... Any suggestions are welcome.)
That is part of the reason, and Ric gave the other reason. Also, your formula should subtract from 256, not 255. To get a more accurate count, you'll have to account for all those: TMR0L += 256 - 200 + 2;
|
Lucky Luka
Starting Member
- Total Posts : 42
- Reward points : 0
- Joined: 2019/04/02 09:39:39
- Location: Italy
- Status: offline
Re: PIC18F4580 timer 0 question
2021/01/02 02:43:52
(permalink)
1and0 To get a more accurate count, you'll have to account for all those:
TMR0L += 256 - 200 + 2;
I'm about to modify my program but I've thought about something... Looking at the datasheet shouldn't an instruction cycle be over in 4 clock? With that in mind shouldn't the formula be: TMR0L += 256 - 200 + 8? Thanks
Attached Image(s)
|
Lucky Luka
Starting Member
- Total Posts : 42
- Reward points : 0
- Joined: 2019/04/02 09:39:39
- Location: Italy
- Status: offline
Re: PIC18F4580 timer 0 question
2021/01/02 04:51:03
(permalink)
Using the formula TMR0L += 256 - 200 + 2; The situation improved a lot: I obtain 1.002 sec ON and and 1.002 sec OFF. Substituting 8 to 2 in the formula as I suggested the result is much worse. I still don't understand why tough. Then I tried to follow the idea of du00000001: I used timer 0 = as a 16 bit timer. Something went wrong but I don't know where I made a mistake... I obtain more than 1.5sec ON and 1.5 sec OFF LED blinking... I have updated the formula and I've attached it to this post. I don't understand what is the source of error: the formula or the program? Maybe I've written the wrong register (TMR0L)? Even writing TMRL0 the result isn't good. #include <xc.h> #include "timer_header.h" #include "stdint.h" #include "stdbool.h"
#define LED1 LATCbits.LATC0 // Green LED //#define LED2 LATCbits.LATC1 // Yellow LED
//#define BUTTON PORTBbits.RB0 // Button
uint32_t blink_count = 0; //uint16_t button_count = 0; //bool button_press = false;
// HIGH priority ISR void __interrupt(high_priority) HPbuttonISR(void){ // Check The Timer 0 Flag Bit if (INTCONbits.TMR0IF) { blink_count++; if(blink_count==50) // with 8 bit it is 10000 timer overflows! { // Toggle green LED LED1 = ~LED1; // Clear The Global Counter blink_count = 0; } // Preload The Value To TMR1 Register Every Overflow Interrupt TMR0L += 25538; INTCONbits.TMR0IF = 0; // Clear The Flag Bit } }
void main(void) { // Configure the pins: OUT = 0, IN = 1 TRISBbits.RB0 = 1; TRISCbits.RC0 = 0; TRISCbits.RC1 = 0; // 8MHz internal oscillator OSCCONbits.IRCF = 0x07; // Internal oscillator selected OSCCONbits.SCS = 0x03; // I wait until the oscillator's freq is stable while(OSCCONbits.IOFS!=1); // Timer 0 OFF T0CONbits.TMR0ON=0; // Timer 0: 16 bit T0CONbits.T08BIT=0; // Internal source clock T0CONbits.T0CS = 0; // Prescaler is bypassed --> 1:1 T0CONbits.PSA = 1; // Preload The Value Which We've Calculated To The TMR0 16-Bit Register! TMR0L += 25538; // Enable Timer 0 interrupt INTCONbits.TMR0IE=1; // High priority for timer 0 interrupt INTCON2bits.TMR0IP=1; ei(); // This is like flipping the master switch to enable // all interrupts: it's like GIE = 1 // Timer 0 ON T0CONbits.TMR0ON=1; LED1 = 0; // Switch OFF Green LED //LED2 = 0; // Switch OFF Yellow LED // Infinite loop while(1){ } return; }
Attached Image(s)
|
du00000001
Just Some Member
- Total Posts : 4120
- Reward points : 0
- Joined: 2016/05/03 13:52:42
- Location: Germany
- Status: online
Re: PIC18F4580 timer 0 question
2021/01/02 05:35:02
(permalink)
☄ Helpfulby Lucky Luka 2021/01/02 06:09:02
The mistake? TMR0L is an 8-Bit register while you have to add a 16-Bit value. If writing to TMR0 is not supported (not sure about that), you have to update TMR0H and TMR0L in the right sequence.
PEBKAC / EBKAC / POBCAK / PICNIC (eventually see en.wikipedia.org)
|
ric
Super Member
- Total Posts : 29951
- Reward points : 0
- Joined: 2003/11/07 12:41:26
- Location: Australia, Melbourne
- Status: online
Re: PIC18F4580 timer 0 question
2021/01/02 05:58:56
(permalink)
☄ Helpfulby Lucky Luka 2021/01/02 06:09:59
Lucky Luka I'm about to modify my program but I've thought about something... Looking at the datasheet shouldn't an instruction cycle be over in 4 clock? With that in mind shouldn't the formula be: TMR0L += 256 - 200 + 8?
No, because the timer is also being clocked by Fosc/4, so one count per instruction.
To get a useful answer, always state which PIC you are using!
|
ric
Super Member
- Total Posts : 29951
- Reward points : 0
- Joined: 2003/11/07 12:41:26
- Location: Australia, Melbourne
- Status: online
Re: PIC18F4580 timer 0 question
2021/01/02 06:13:18
(permalink)
du00000001 The mistake? TMR0L is an 8-Bit register while you have to add a 16-Bit value. If writing to TMR0 is not supported (not sure about that), you have to update TMR0H and TMR0L in the right sequence.
TMR0 does support latched read and writes, but then you lose the elegance of an 8 bit add. You have to read the 16 bit value as two 8 bit values into temporary storage (carefully reading TMR0L then TMR0H), then do the 16 bit addition, then do two 8 bit writes (carefully writing TMR0H then TMR0L). There's no automatic way to compensate for however many assembly instructions that process takes. This is when you discover that TMR2 is the best one for a periodic interrupt, as it has a built in reload register (PR2). You can get 10 KHz with these settings: TMR2 prescale = 1:1 TMR2 postscale = 1:1 TMR2 PR2 = 199 Rate = (8000000/4)/(199+1) = 10000 I would agree that 1kHz is probably less overhead. Just use the above settings but change the TMR2 postscale value to 1:10 to get 1kHz.
To get a useful answer, always state which PIC you are using!
|
1and0
Access is Denied
- Total Posts : 12108
- Reward points : 0
- Joined: 2007/05/06 12:03:20
- Location: Harry's Gray Matter
- Status: offline
Re: PIC18F4580 timer 0 question
2021/01/02 07:05:01
(permalink)
Lucky Luka Using the formula TMR0L += 256 - 200 + 2; The situation improved a lot: I obtain 1.002 sec ON and and 1.002 sec OFF.
I forgot to account for one more instruction cycle for the write to the TMR0 register; that is, TMR0L += 256 - 200 + 2 + 1; Lucky Luka Substituting 8 to 2 in the formula as I suggested the result is much worse. I still don't understand why tough.
As Ric said, Timer0 is clocked at the rate of Fosc/4.
|
1and0
Access is Denied
- Total Posts : 12108
- Reward points : 0
- Joined: 2007/05/06 12:03:20
- Location: Harry's Gray Matter
- Status: offline
Re: PIC18F4580 timer 0 question
2021/01/02 07:10:16
(permalink)
☄ Helpfulby Lucky Luka 2021/01/02 07:39:37
ric TMR0 does support latched read and writes, but then you lose the elegance of an 8 bit add. You have to read the 16 bit value as two 8 bit values into temporary storage (carefully reading TMR0L then TMR0H), then do the 16 bit addition, then do two 8 bit writes (carefully writing TMR0H then TMR0L). There's no automatic way to compensate for however many assembly instructions that process takes.
Early on into the ISR, assuming TMR0H is still 0x00 and there will be no carry. I wonder if the write to TMR0L can be performed with an addition instead? That is, will ADDWF instead of MOVWF on TMR0L work: movlw high(.65536 - .40000 + 2 + 1) movwf TMR0H movlw low (.65536 - .40000 + 2 + 1) addwf TMR0L
ric This is when you discover that TMR2 is the best one for a periodic interrupt, as it has a built in reload register (PR2).
+1
|
Lucky Luka
Starting Member
- Total Posts : 42
- Reward points : 0
- Joined: 2019/04/02 09:39:39
- Location: Italy
- Status: offline
Re: PIC18F4580 timer 0 question
2021/01/02 08:03:56
(permalink)
Thank you all In the end I think I've chosen the easiest way (for me): Timer 0 @ 8bit, internal clock @ 500kHz, prescaler @ 1:1 1and0 I forgot to account for one more instruction cycle for the write to the TMR0 register; that is,
TMR0L += 256 - 200 + 2 + 1;
I have added 2 to the counter and I've obtained 1,004s blinking period. I then tried to add 3 to the counter and i've obrained 0.996s blinking period. I don't know if this issue can be solved using this timer. #include <xc.h> #include "timer_header.h" #include "stdint.h" #include "stdbool.h"
#define LED1 LATCbits.LATC0 // Green LED //#define LED2 LATCbits.LATC1 // Yellow LED
//#define BUTTON PORTBbits.RB0 // Button
uint16_t blink_count = 0; //uint16_t button_count = 0; //bool button_press = false;
// HIGH priority ISR void __interrupt(high_priority) HPbuttonISR(void){ // Check The Timer 0 Flag Bit if (INTCONbits.TMR0IF) { blink_count++; if(blink_count==500) // with 8 bit 8MHz it was 10000 timer overflows! { // Toggle green LED LED1 = ~LED1; // Clear The Global Counter blink_count = 0; } // Preload The Value To TMR1 Register Every Overflow Interrupt TMR0L += 8; INTCONbits.TMR0IF = 0; // Clear The Flag Bit } }
void main(void) { // Configure the pins: OUT = 0, IN = 1 TRISBbits.RB0 = 1; TRISCbits.RC0 = 0; TRISCbits.RC1 = 0; // 500 kHz internal oscillator OSCCONbits.IRCF = 0x03; // Internal oscillator selected OSCCONbits.SCS = 0x03; // I wait until the oscillator's freq is stable while(OSCCONbits.IOFS!=1); // Timer 0 OFF T0CONbits.TMR0ON=0; // Timer 0: 8 bit T0CONbits.T08BIT=1; // Internal source clock T0CONbits.T0CS = 0; // Prescaler is bypassed --> 1:1 T0CONbits.PSA = 1; // Preload The Value Which We've Calculated To The TMR0 8-Bit Register! TMR0L += 8; // Enable Timer 0 interrupt INTCONbits.TMR0IE=1; // High priority for timer 0 interrupt INTCON2bits.TMR0IP=1; ei(); // This is like flipping the master switch to enable // all interrupts: it's like GIE = 1 // Timer 0 ON T0CONbits.TMR0ON=1; LED1 = 0; // Switch OFF Green LED //LED2 = 0; // Switch OFF Yellow LED // Infinite loop while(1){ } return; }
Attached Image(s)
|
1and0
Access is Denied
- Total Posts : 12108
- Reward points : 0
- Joined: 2007/05/06 12:03:20
- Location: Harry's Gray Matter
- Status: offline
Re: PIC18F4580 timer 0 question
2021/01/02 08:55:11
(permalink)
Lucky Luka I have added 2 to the counter and I've obtained 1,004s blinking period. I then tried to add 3 to the counter and i've obrained 0.996s blinking period. I don't know if this issue can be solved using this timer.
void __interrupt(high_priority) HPbuttonISR(void){ // Check The Timer 0 Flag Bit if (INTCONbits.TMR0IF) { blink_count++; if(blink_count==500) // with 8 bit 8MHz it was 10000 timer overflows! { // Toggle green LED LED1 = ~LED1; // Clear The Global Counter blink_count = 0; } // Preload The Value To TMR1 Register Every Overflow Interrupt TMR0L += 8; INTCONbits.TMR0IF = 0; // Clear The Flag Bit } }
Adding 3 is the correct solution, and I would expect you to get an on-off period of exactly 2 seconds. I suspect the different on time and off time are caused by this LED1 = ~LED1; generating different paths thru the code. So, replace these two lines LED1 = ~LED1; TMR0L += 8;
with these LED1 ^= 1;
TMR0L += 256 - 250 + 2 + 1; // add 9
and see. ;)
|
Lucky Luka
Starting Member
- Total Posts : 42
- Reward points : 0
- Joined: 2019/04/02 09:39:39
- Location: Italy
- Status: offline
Re: PIC18F4580 timer 0 question
2021/01/02 10:06:02
(permalink)
Hi I've tried to modify the code as you said but I obtained the same period of 2 - 0.004s That was my first eye measure with the scope. Then I made an auto measure of the period and it looks like I was right: it's not precisely 2 sec. I hope I haven't done some mistake in the measurements since I'm still learning both in the microcontrllers field and in oscilloscopes measures but that's what I've obtained... #include <xc.h> #include "timer_header.h" #include "stdint.h" #include "stdbool.h"
#define LED1 LATCbits.LATC0 // Green LED //#define LED2 LATCbits.LATC1 // Yellow LED
//#define BUTTON PORTBbits.RB0 // Button
uint16_t blink_count = 0; //uint16_t button_count = 0; //bool button_press = false;
// HIGH priority ISR void __interrupt(high_priority) HPbuttonISR(void){ // Check The Timer 0 Flag Bit if (INTCONbits.TMR0IF) { blink_count++; if(blink_count==500) // with 8 bit 8MHz it was 10000 timer overflows! { // Toggle green LED LED1 ^= 1; // Clear The Global Counter blink_count = 0; } // Preload The Value To TMR1 Register Every Overflow Interrupt TMR0L += 9; INTCONbits.TMR0IF = 0; // Clear The Flag Bit } }
void main(void) { // Configure the pins: OUT = 0, IN = 1 TRISBbits.RB0 = 1; TRISCbits.RC0 = 0; TRISCbits.RC1 = 0; // 500 kHz internal oscillator OSCCONbits.IRCF = 0x03; // Internal oscillator selected OSCCONbits.SCS = 0x03; // I wait until the oscillator's freq is stable while(OSCCONbits.IOFS!=1); // Timer 0 OFF T0CONbits.TMR0ON=0; // Timer 0: 8 bit T0CONbits.T08BIT=1; // Internal source clock T0CONbits.T0CS = 0; // Prescaler is bypassed --> 1:1 T0CONbits.PSA = 1; // Preload The Value Which We've Calculated To The TMR0 8-Bit Register! TMR0L += 9; // Enable Timer 0 interrupt INTCONbits.TMR0IE=1; // High priority for timer 0 interrupt INTCON2bits.TMR0IP=1; ei(); // This is like flipping the master switch to enable // all interrupts: it's like GIE = 1 // Timer 0 ON T0CONbits.TMR0ON=1; LED1 = 0; // Switch OFF Green LED //LED2 = 0; // Switch OFF Yellow LED // Infinite loop while(1){ } return; }
Attached Image(s)
|
1and0
Access is Denied
- Total Posts : 12108
- Reward points : 0
- Joined: 2007/05/06 12:03:20
- Location: Harry's Gray Matter
- Status: offline
Re: PIC18F4580 timer 0 question
2021/01/02 10:29:43
(permalink)
☄ Helpfulby Lucky Luka 2021/01/02 10:31:42
Lucky Luka I've tried to modify the code as you said but I obtained the same period of 2 - 0.004s That was my first eye measure with the scope. Then I made an auto measure of the period and it looks like I was right: it's not precisely 2 sec. I hope I haven't done some mistake in the measurements since I'm still learning both in the microcontrllers field and in oscilloscopes measures but that's what I've obtained...
I just tested your code in the MPLAB Simulator and the LED blinks at exactly one-second on-time and one-second off-time. So, it is either your scope or the accuracy of the internal oscillator which can be ±2%, ±5% or ±10% depending on the temperature.
|
ric
Super Member
- Total Posts : 29951
- Reward points : 0
- Joined: 2003/11/07 12:41:26
- Location: Australia, Melbourne
- Status: online
Re: PIC18F4580 timer 0 question
2021/01/02 17:41:31
(permalink)
☄ Helpfulby Lucky Luka 2021/01/04 09:47:12
1.004s is an error of only 0.4% As 1and0 mentions, even at just 25C, the internal oscillator is only specified to +/- 2%, it's even worse over a range of temperatures. https://ww1.microchip.com...oc/39637d.pdf#page=441So, your error is well under what the datasheet guarantees. You need an external crystal, or a newer PIC with a more precise internal osciullator if you want a more accurate clock rate.
post edited by ric - 2021/01/02 17:42:48
To get a useful answer, always state which PIC you are using!
|
du00000001
Just Some Member
- Total Posts : 4120
- Reward points : 0
- Joined: 2016/05/03 13:52:42
- Location: Germany
- Status: online
Re: PIC18F4580 timer 0 question
2021/01/02 17:57:06
(permalink)
☄ Helpfulby Lucky Luka 2021/01/04 09:47:35
And don't forget that the scope inserts its own error. Although digital scopes might be better than 1% (the magic number for good analog scopes), the timebase error might easily add 0.1 % (or more).
PEBKAC / EBKAC / POBCAK / PICNIC (eventually see en.wikipedia.org)
|
Lucky Luka
Starting Member
- Total Posts : 42
- Reward points : 0
- Joined: 2019/04/02 09:39:39
- Location: Italy
- Status: offline
Re: PIC18F4580 timer 0 question
2021/01/04 09:47:48
(permalink)
|
dan1138
Super Member
- Total Posts : 4246
- Reward points : 0
- Joined: 2007/02/21 23:04:16
- Location: 0
- Status: offline
Re: PIC18F4580 timer 0 question
2021/01/04 13:05:31
(permalink)
1and0 I just tested your code in the MPLAB Simulator and the LED blinks at exactly one-second on-time and one-second off-time. The MPLABX simulator has some dodgy behavior for instruction cycle counts with the TIMER0 in PIC18F targets. Sometime in 2019 I have raised an issue with Microchip support on the TIMER0 count behavior being different from how the real part work. I have not checked the v5.45 of MPLABX to see if it has been fixed. The specific issue I raised is that in the real device any write to the TIMER0 count register results in a two instruction cycle delay before TIMER0 starts to count, but there are some opcodes that can be used to write the TIMER0 count register where the simulator starts the TIMER0 count without a delay. So the bottom line here is that getting a cycle accurate count period from TIMER0 is tricky and hard to verify with the dodgy simulator.
|
Lucky Luka
Starting Member
- Total Posts : 42
- Reward points : 0
- Joined: 2019/04/02 09:39:39
- Location: Italy
- Status: offline
Re: PIC18F4580 timer 0 question
2021/01/04 13:14:54
(permalink)
dan1138 The specific issue I raised is that in the real device any write to the TIMER0 count register results in a two instruction cycle delay before TIMER0 starts to count, but there are some opcodes that can be used to write the TIMER0 count register where the simulator starts the TIMER0 count without a delay. Do you confirm that there is to account for one more instruction cycle for the write to the TMR0 register other than those two mentioned?
|