PDA

View Full Version : GOTO statement causing jitter in output frequency, can be this fixed?



CuriousOne
- 4th November 2023, 15:39
Hello.

I have this simple code - it generates pulses of 300, 600, 1200 and 2400 Hz on PORTC Outputs at same time.



;----[16F886 Hardware Configuration]--------------------------------------------
#CONFIG
cfg1 = _INTRC_OSC_NOCLKOUT ; INTOSCIO oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN
cfg1&= _WDT_ON ; WDT enabled
cfg1&= _PWRTE_OFF ; PWRT disabled
cfg1&= _MCLRE_OFF ; RE3/MCLR pin function is digital input, MCLR internally tied to VDD
cfg1&= _CP_OFF ; Program memory code protection is disabled
cfg1&= _CPD_OFF ; Data memory code protection is disabled
cfg1&= _BOR_OFF ; BOR disabled
cfg1&= _IESO_ON ; Internal/External Switchover mode is enabled
cfg1&= _FCMEN_ON ; Fail-Safe Clock Monitor is enabled
cfg1&= _LVP_OFF ; RB3 pin has digital I/O, HV on MCLR must be used for programming
cfg1&= _DEBUG_OFF ; In-Circuit Debugger disabled, RB6/ICSPCLK and RB7/ICSPDAT are general purpose I/O pins
__CONFIG _CONFIG1, cfg1


cfg2 = _BOR40V ; Brown-out Reset set to 4.0V
cfg2&= _WRT_OFF ; Write protection off
__CONFIG _CONFIG2, cfg2


#ENDCONFIG


'chip configs


TRISA=%00000000 'SET A TO OUTPUT 0=INPUT 1 OUTPUT
TRISC=0 'set
TRISB=0 'set
ANSELH=%00000000 ' ADC OFF B
ANSEL=%000000000 'configure PortA as digital
ADCON1=%10000000 'adc justify
OSCCON=%01110101 'SET FREQUENCY TO 8MHZ
WPUB=%00000000 'turn off Pullups
CM1CON0=0 'DISABLE COMPARATORS
CM2CON0=0 'SAME HERE
'CCP1CON=%01000000 ' configure pwm
'PSTRCON=%00010110 'disable C pwm <--- all above enables HPWM on PORTB.4


DEFINE OSC 8 'set oscillator speed


'adcin config
DEFINE ADC_BITS 10
DEFINE ADC_CLOCK 3
DEFINE ADC_SAMPLEUS 50


A VAR PORTC.4
B VAR PORTC.5
C VAR PORTC.6
D VAR PORTC.7


x var byte


x=0




masfrk:
for x=0 to 15
a=x.0
b=x.1
c=x.2
d=x.3
pauseus 194
next
goto masfrk


I noticed that there's an output jitter, and it is directly related to the GOTO statement - on each iteration, it takes different time to code to start again, so there is a noticeable jitter.
If I change X maximum value from 15 to 255, jitter is significantly reduced, because most time code operates inside for-next loop, which des not has this problem.

Here is a small video showing it - first half of the video the X changes from 0 to 255, and after that I upload another code, where X changes from 0 to 15, and jitter can be clearly seen.

Currently I'm running 16F886 @ 8mhz internal oscillator. If that would be intosc issue, then it should affect the for-next loop too, right? but why it only happens on GOTO, any ways to fix this?

https://youtu.be/e-ms9OKfBUI

tumbleweed
- 4th November 2023, 16:27
If you replace the two loops with a single loop then the timing should be consistent...


x = 0
masfrk:
a=x.0
b=x.1
c=x.2
d=x.3
pauseus 194
x = x + 1
goto masfrk


You may have to adjust the 'pauseus' statement...

CuriousOne
- 4th November 2023, 18:16
Thanks, that worked!
But why it happens at all?

Ioannis
- 4th November 2023, 20:32
Isn't obvious? The inner loop takes t time to finish. After 16 iterations it takes t+t(goto) time to start over again.

With the tumbleweed solution you have only one loop running forever. But x will not stop at 15 if it matter. It will reach 255 then cycle to 0.

Ioannis

CuriousOne
- 4th November 2023, 20:52
No it is not obvious, because it takes DIFFERENT times on each iteration of GOTO statement.

tumbleweed
- 4th November 2023, 22:10
If you reduce the timebase of your analyzer so you can capture an entire set of the FOR-NEXT loop, you'll see that it's not jitter but the increased time when the FOR-NEXT loop exits and the GOTO loop occurs. You have a few extra cycles there to do the GOTO and the exit/initialization of the FOR-NEXT loop so every 16 iterations you get a small gap.

The code I posted takes advantage of the fact that incrementing a byte from 255 just rolls over to 0, so it has the same effect as your FOR x=0 to 15 loop without the extra loop involved.

Obviously, your pic can't do anything else but this, otherwise the timing will change. Your other option would be to setup a timer and do this in an interrupt routine but you won't be able to get exact timing with that since there can be a slight difference in the timing of when the ISR runs.

HenrikOlsson
- 5th November 2023, 16:26
And since the four pins are on the same port you could do away with the individual RMW-instructions for each pin and just do

x = 0
masfrk:
PortC = x
x = x + 16
pauseus 194
goto masfrk

Or even

PortC = 0
masfrk:
PortC = PortC + 16
pauseus 194
goto masfrk

Obviously this will cause the bottom 4 bits of PortC to be 0 but it doesn't look like you're using those for anything.