PIC16F877A TIMER1  - Page 1
Forum section: electronics / general

author: wuiven64
author_rank: Newbie
timestamp: September 22, 2020, 03:45:29 am
content: 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_TIMER1void 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 1set_timer1(65508); // Preload valueclear_interrupt(INT_TIMER1); // Clear Timer1 interrupt flag bitenable_interrupts(INT_TIMER1); // Enable Timer1 interruptenable_interrupts(GLOBAL); // Enable global interruptsoutput_low(PIN_B0);while(TRUE) ; // Endless loop}

author: Andreas
author_rank: Super Contributor
timestamp: September 22, 2020, 05:58:28 am
content: Quote from: wuiven64 on September 22, 2020, 03:45:29 amThink 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 regardsAndreas

author: Andreas
author_rank: Super Contributor
timestamp: September 22, 2020, 06:02:40 am
content: Hello,i would use the timer 1 in PWM mode for such high frequencies.with best regardsAndreas

author: JPortici
author_rank: Super Contributor
timestamp: September 22, 2020, 11:24:03 am
content: 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 = 21How 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)

author: Ian.M
author_rank: Super Contributor
timestamp: September 22, 2020, 12:31:42 pm
content: 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:ORG 0x004 ; interrupt vector locationmovwf w_temp ; save off current W register contentsmovfSTATUS,w ; move status register into W registermovwfstatus_temp ; save off contents of STATUS registermovfPCLATH,w; move pclath register into w registermovwfpclath_temp; save off contents of PCLATH register; isr code can go here or be located as a call subroutine elsewheremovfpclath_temp,w; retrieve copy of PCLATH registermovwfPCLATH; restore pre-isr PCLATH register contentsmovf status_temp,w ; retrieve copy of STATUS registermovwfSTATUS ; restore pre-isr STATUS register contentsswapf w_temp,fswapf w_temp,w ; restore pre-isr W register contentsretfie ; return from interruptwhich is twelve instructions, one of which takes two cycles, and ISR entry has a further overhead of two cycles (SeePICmicro Mid-Range MCU Family Reference Manualfig. 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 . . . .

author: Andreas
author_rank: Super Contributor
timestamp: September 22, 2020, 06:19:40 pm
content: Sorry I meant PWM with Timer2.A better PWM frequency accuracy could be get with a device with NCOE.g. PIC16F15313 or similar.with best regardsAndreas

author: NivagSwerdna
author_rank: Super Contributor
timestamp: September 22, 2020, 06:43:12 pm
content: 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 secondsT1CON.T1CKPS1 = 0; // bits 5-4 Prescaler Rate Select bitsT1CON.T1CKPS0 = 0; // bit 4T1CON.T1OSCEN = 1; // bit 3 Timer1 Oscillator Enable Control bit 1 = onT1CON.T1SYNC = 1; // bit 2 Timer1 External Clock Input Synchronization Control bit...1 = Do not synchronize external clock inputT1CON.TMR1CS = 0; // bit 1 Timer1 Clock Source Select bit...0 = Internal clock (FOSC/4)T1CON.TMR1ON = 1; // bit 0 enables timerTMR1H = 255; // preset for timer1 MSB registerTMR1L = 228; // preset for timer1 LSB registerShould 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.htmlmight 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 onhttp://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.

author: Ian.M
author_rank: Super Contributor
timestamp: September 22, 2020, 09:34:53 pm
content: 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.