Author Topic: PIC16F877A TIMER1  (Read 3262 times)

0 Members and 1 Guest are viewing this topic.

Offline wuiven64Topic starter

  • Newbie
  • Posts: 7
  • Country: my
PIC16F877A TIMER1
« on: September 22, 2020, 03:45:29 am »
Hi. I'm using PIC16F877A with 20 MHz crystal and PICkit 2 programmer to generate a 180 kHz square wave using TIMER1. However, the waveform that I obtained from Tektronix TPS2014 oscilloscope s not the desired waveform. Besides that, the measured frequency from the oscilloscope is far from the desired frequency. I have attached the code (Using CCS C compiler Ver. 5 demo) and waveform that capture from the oscilloscope. Kindly give some advice. Thanks.

Code to generate 180 kHz square wave using TIMER1:

#include <TIMER1.h>

#INT_TIMER1
void timer1_isr(void)
{
   output_toggle(PIN_B0);
   clear_interrupt(INT_TIMER1);
   set_timer1(65508);
}

void main()
{
   setup_timer_1 ( T1_INTERNAL | T1_DIV_BY_1 ); // Internal clock and prescaler 1
   set_timer1(65508);                           // Preload value
   clear_interrupt(INT_TIMER1);                 // Clear Timer1 interrupt flag bit
   enable_interrupts(INT_TIMER1);               // Enable Timer1 interrupt
   enable_interrupts(GLOBAL);                   // Enable global interrupts
   output_low(PIN_B0);
 
   while(TRUE) ; // Endless loop
}
« Last Edit: September 22, 2020, 05:19:06 am by wuiven64 »
 

Offline Andreas

  • Super Contributor
  • ***
  • Posts: 3588
  • Country: de
Re: PIC16F877A TIMER1
« Reply #1 on: September 22, 2020, 05:58:28 am »

Code to generate 180 kHz square wave using TIMER1:


Think twice:
if you could update the timer in zero time and toggle the pin after 28 instruction cycles you are getting effectively half the intended frequency.
But the updating of the timer needs also some time so the 28 instruction cycles start from that point.

For the wave form:
you have either a very large capacitive load at the output which has around 50-100 Ohms output resistance.
Or you are using a non calibrated 10:1 probe.
Use always a 10:1 probe which is adjusted with the test signal from the scope.

with best regards

Andreas
 
The following users thanked this post: Ian.M

Offline Andreas

  • Super Contributor
  • ***
  • Posts: 3588
  • Country: de
Re: PIC16F877A TIMER1
« Reply #2 on: September 22, 2020, 06:02:40 am »
Hello,

i would use the timer 1 in PWM mode for such high frequencies.

with best regards

Andreas
 

Offline JPortici

  • Super Contributor
  • ***
  • Posts: 3921
  • Country: it
Re: PIC16F877A TIMER1
« Reply #3 on: September 22, 2020, 11:24:03 am »
Or even TMR2+CCP, with no software intervention.
Resolution is pretty low anyway:
for about 180kHz carrier, from 4MHz PR2 = (4.000.000 / 180.000) - 1 = 21

How did you get that reload value? It doesn't look right (as if you subracted the number of instructions needed to account for the ISR latency instead of adding them)
 

Offline Ian.M

  • Super Contributor
  • ***
  • Posts: 14135
Re: PIC16F877A TIMER1
« Reply #4 on: September 22, 2020, 12:31:42 pm »
Timer 1 doesn't support PWM on 'classic' midrange PICs like the PIC16F877A.  Nor does it have the enhanced CCP module that can toggle a pin on compare match.

A stable and accurate 180KHz is not an achievable frequency for any 8 bit PIC with a 20 MHz oscillator.  The core and peripherals run at Fosc/4, (i.e. 5 MIPS), so your output frequency must be 5 MHz / N, where N is an integer.  If you require a 50% duty cycle squarewave, and are bit-banging the pin, N must be divisible by two.  (The PWM module doesn't have that limitation.)  That means that the closest you could get is slightly under 178.6 KHz.

However at 5 MIPS, you only have 14 instruction cycles per half cycle of your 178.6 KHz, and if you try to bit-bang it in an ISR, a significant number of instruction cycles are wasted by the necessary code to save the minimum context (W, STATUS, and PCLATH) to resume main program execution after the interrupt:
Code: [Select]
ORG     0x004             ; interrupt vector location

movwf   w_temp            ; save off current W register contents
movf STATUS,w          ; move status register into W register
movwf status_temp       ; save off contents of STATUS register
movf PCLATH,w   ; move pclath register into w register
movwf pclath_temp   ; save off contents of PCLATH register

; isr code can go here or be located as a call subroutine elsewhere

movf pclath_temp,w   ; retrieve copy of PCLATH register
movwf PCLATH   ; restore pre-isr PCLATH register contents
movf    status_temp,w     ; retrieve copy of STATUS register
movwf STATUS            ; restore pre-isr STATUS register contents
swapf   w_temp,f
swapf   w_temp,w          ; restore pre-isr W register contents
retfie                    ; return from interrupt
which is twelve instructions, one of which takes two cycles, and ISR entry has a further overhead of two cycles (See PICmicro Mid-Range MCU Family Reference Manual fig. 8.2 in the Interrupts section), so you are already -1 cycles (i.e. it wont fit in the time available) before you have any payload in a pure assembly language ISR.  If you settled for a short asymmetric pulse, (i.e. set pin then immediately clear it), you'd have 28 cycles to work with leaving 13 for your payload, so it would be possible, but almost certainly not in C which has extra context saving to do and a minimum four cycle overhead for each function call.

As JPortici points out, Timer 2 and the CCP module is your best option . . . .
 

Offline Andreas

  • Super Contributor
  • ***
  • Posts: 3588
  • Country: de
Re: PIC16F877A TIMER1
« Reply #5 on: September 22, 2020, 06:19:40 pm »
Sorry I meant PWM with Timer2.

A better PWM frequency accuracy could be get with a device with NCO
E.g. PIC16F15313 or similar.

with best regards

Andreas
 

Offline NivagSwerdna

  • Super Contributor
  • ***
  • Posts: 2571
  • Country: gb
Re: PIC16F877A TIMER1
« Reply #6 on: September 22, 2020, 06:43:12 pm »
Ignoring the fact that you are looking for a very fast frequency... you shouldn't be setting the timer values in the interrupt*... just let the hardware do it for you... i.e. overflow and keep the comparison fixed.

According to..

http://eng-serve.com/pic/pic_timer.html

//Timer1 Registers Prescaler= 1 - TMR1 Preset = 65508 - Freq = 178571.43 Hz - Period = 0.000006 seconds
T1CON.T1CKPS1 = 0;   // bits 5-4  Prescaler Rate Select bits
T1CON.T1CKPS0 = 0;   // bit 4
T1CON.T1OSCEN = 1;   // bit 3 Timer1 Oscillator Enable Control bit 1 = on
T1CON.T1SYNC = 1;    // bit 2 Timer1 External Clock Input Synchronization Control bit...1 = Do not synchronize external clock input
T1CON.TMR1CS = 0;    // bit 1 Timer1 Clock Source Select bit...0 = Internal clock (FOSC/4)
T1CON.TMR1ON = 1;    // bit 0 enables timer
TMR1H = 255;             // preset for timer1 MSB register
TMR1L = 228;             // preset for timer1 LSB register


Should get you 178571.43 Hz... now there isn't much going on apart from the interrupt so your interrupt runs flat out... if it doesn't have much overhead (saving and restoring registers etc) it might be able to keep up.

Now coming back to the original question...

https://www.micro-examples.com//public/microex-navig/doc/097-pwm-calculator.html might help for PWM modes...

/*
 * PWM registers configuration
 * Fosc = 20000000 Hz
 * Fpwm = 178571.43 Hz (Requested : 180000 Hz)
 * Duty Cycle = 50 %
 * Resolution is 6 bits
 * Prescaler is 4
 * Ensure that your PWM pin is configured as digital output
 * see more details on http://www.micro-examples.com/
 * this source code is provided 'as is',
 * use it at your own risks
 */
PR2 = 0b00000110 ;
T2CON = 0b00000101 ;
CCPR1L = 0b00000011 ;
CCP1CON = 0b00011100 ;

Footnote
* I looked at a few examples on the interweb and they have set_timer1 in the ISR... this is an antipattern to me as the interval will end up being affected by the ISR service time (and ISR jitter due to other interrupts)... go figure... what do I know.  ;)
« Last Edit: September 22, 2020, 06:48:06 pm by NivagSwerdna »
 

Offline Ian.M

  • Super Contributor
  • ***
  • Posts: 14135
Re: PIC16F877A TIMER1
« Reply #7 on: September 22, 2020, 09:34:53 pm »
A better method for pulse trains that aren't easily achievable by PWM is to let timer 1 free-run and use the CCP module to flip the pin on a compare match.  Your ISR has to add the interval between the previous edge (that triggered the interrupt) and the next edge to the CCP compare register, flip the CCP mode to arm it for the opposite edge polarity and clear the compare interrupt flag.  This gives cycle accurate jitter free edges provided the minimum time between edges is long enough for your ISR to keep up without missing an edge.  It can even slip past the next edge before the ISR returns provided its already prepared the next edge *if* the following interval is long enough for it to catch up.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

- - -
-->