Jitter

Discussion about the ZBasic language including the System Library. If you're not sure where to post your message, do it here. However, do not make test posts here; that's the purpose of the Sandbox.
Paul Lamar
Posts: 65
Joined: 14 May 2010, 16:01 PM

Jitter

Post by Paul Lamar »

Don,
Why do I get jitter on my scope on pin 14?
Pin 12 is fed with a rock solid function generator with a one micro second pulse.
I still get jitter if pin 12 is fed with a 10 micro second pulse.
Is the 1280n not a static device?
Paul Lamar

Code: Select all

Public x as long
Public y as integer

Sub Main()
  for x = 0 to 3000000 
    If (GetPin(12)=1) then  
      Call rotor1
    end if
  next x           
end sub 'main

sub rotor1()         
  Call PutPin(14, zxOutputHigh) 

  for y = 1 to 10 :next y   

  Call PutPin(14, zxOutputLow) 
end sub 'rotor1
[Edit: added code tags to make the code more readable]
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Re: Jitter

Post by dkinzer »

Paul Lamar wrote:I still get jitter if pin 12 is fed with a 10 micro second pulse.
There are two sources of jitter. The first one is due to the absence of synchronization between the ZX clock and the external source. With a 10 microsecond pulse (at what frequency, by the way?), the ZX could detect the logic high at any point during the 10uS interval or even miss it altogether. A 10uS interval corresponds to only 147 CPU cycles. A single machine instruction can take 1 to 4 cycles and each ZBasic "instruction" corresponds to several, dozens, hundreds or more machine instructions.

Even if the ZX were perfectly synchronized with the external source so that it consistently detected the logic high at X nanoseconds after the rising edge, there would still be jitter in the output unless you disable interrupts. The RTC is always running, generating an interrupt at 1024Hz. There are other possible interrupt sources depending on what your code is doing.

You may be able to get better synchronization with an external signal by using WaitForInterrupt() configured for awaiting either a pin change or an external interrupt. Even then, there will still be some "uncertainty" as to when the ZX responds to the interrupt.
- Don Kinzer
Paul Lamar
Posts: 65
Joined: 14 May 2010, 16:01 PM

Post by Paul Lamar »

Repetition frequency is 250 Hertz

A 30 usec pulse helped a lot.

How can I turn off the RTC as I will not need it.
What functions if any will be disabled?

Paul Lamar
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

Paul Lamar wrote:How can I turn off the RTC as I will not need it.
We've never tested a ZX with the RTC disabled. A ZX with the RTC disabled may work fine except for those functions that rely on it. Again, I want to underscore the fact that this has never been tested.

There is no built-in function to disable the timer used for the RTC but you can manipulate the timer registers directly. The particular timer used for the RTC varies across the set of ZX devices but it is generally Timer0 or Timer2. On the ZX-1280 and ZX-1280n it happens to be Timer2. The code below shows how to turn off the Timer2 interrupt for the ZX-1280, it may be different for other ZX devices.

Code: Select all

Register.TIMSK2 = 0
Paul Lamar wrote:What functions if any will be disabled?
The RTC is key to multi-tasking so if you turn it off you'll have a single task environment. You can still invoke CallTask but the additional tasks will never run.

Any functions directly related to the RTC obviously will not work (see the list of Date/Time routines). Other functions that use the RTC timer and therefore won't work correctly are:
  • Delay()
  • DelayUntilClockTick()
  • Pause()
  • SetInterval()
  • Sleep()
  • WaitForInterval()
  • GetMicroTime()
  • GetElapsedMicroTime()
Also, the low-level X-10 functionality requires the RTC timer, i.e. DefineX10(), OpenX10(), StatusX10(), CloseX10().
- Don Kinzer
Paul Lamar
Posts: 65
Joined: 14 May 2010, 16:01 PM

Post by Paul Lamar »

OK Plan B.

tried wait for interrupt

Public x as long

Public y as integer

Sub Main()


for x = 0 to 3000000 'develop only

Call WaitForInterrupt(ZXpinRisingEdge, WaitINT0)
'port D bit 1 pin 43

Call rotor1


next x
end sub 'main


sub rotor1()
for y = 1 to 20 :next y
Call PutPin(14, zxOutputHigh)
for y = 1 to 50 :next y

Call PutPin(14, zxOutputLow)
Call PutPin(12, zxOutputLow)
end sub 'rotor1


This is the verbose message I got

"C:\Program Files\ZBasic\zbasic.exe" --target-device=ZX1280n --directory="C:\Documents and Settings\junk\Desktop\ZBasic/" --project="4th-EFI-testj.pjt"

make: Entering directory `C:/Documents and Settings/junk/Desktop/ZBasic/zx_e1Ljfb'

avr-gcc -c -DF_CPU=14745600UL -Dzx1280n -mmcu=atmega1280 -I"C:/Program Files/ZBasic/zxlib" -I. -gdwarf-2 -Os -Wall -funsigned-char -fpack-struct -Wstrict-prototypes -std=gnu99 -fgnu89-inline 4th_EFI_testj.c -o 4th_EFI_testj.o

avr-gcc -o 4th_EFI_testj.i1 -mmcu=atmega1280 -L"C:/Program Files/ZBasic/zxlib" -Wl,-T,"C:/Program Files/ZBasic/zxlib/zx_avr5.lds" 4th_EFI_testj.o -lzx1280n -lm

C:/Program Files/ZBasic/zxlib\libzx1280n.a(waitInterrupt_avr.o): In function `terminateWaitInt':

(.text+0x1c0): relocation truncated to fit: R_AVR_7_PCREL against `no symbol'

make: *** [4th_EFI_testj.i1] Error 1

make: Leaving directory `C:/Documents and Settings/junk/Desktop/ZBasic/zx_e1Ljfb'

Error: one or more errors occurred in the back-end build process for "4th-EFI-testj.zxb"

>Exit code: 1
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

Please, please, please use the "code tags" feature. It makes the code much easier to read because it retains the formatting. You can either type in the code tags directly or select the code you've pasted and click the Code button below the Subject edit box. The code tags consist of the sequences code and /code each enclosed in square brackets. The former marks the beginning of the code block and the latter marks the end of the code block.
- Don Kinzer
Paul Lamar
Posts: 65
Joined: 14 May 2010, 16:01 PM

Post by Paul Lamar »

Public x as long

Public y as integer

Sub Main()

for x = 0 to 3000000 'develop only

Call WaitForInterrupt(ZXpinRisingEdge, WaitINT0)

Call rotor1

next x
end sub 'main

sub rotor1()
for y = 1 to 20 :next y

Call PutPin(14, zxOutputHigh)
for y = 1 to 50 :next y

Call PutPin(14, zxOutputLow)
Call PutPin(12, zxOutputLow)
end sub
[/code]

Sorry!
Is there a manual some where on how to use this web site?

Paul
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

Paul Lamar wrote:Is there a manual some where on how to use this web site?
There is no manual, per se, but there is a Forum FAQ, a link to which appears among several at the top of the page when you're viewing the ZBasic Forum.

By the way, you're missing the opening code tag on the previous post. It is possible to edit your own posts simply by clicking the Edit button.
- Don Kinzer
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

Paul Lamar wrote:Error: one or more errors occurred in the back-end build process for "4th-EFI-testj.zxb"
I've posted an update to the ZX Library that corrects this problem. To use it, go to the Downloads Page, scroll down to the entry for the ZX Library and click the "Download" button to save the .zip file to a convenient place on your computer. Next, extract all of the files in the .zip file to the zxlib subdirectory of the ZBasic installation directory (typically C:/Program Files/ZBasic). If you wish, you can rename that directory (and then create a new one) or save off the files in that directory in case you might want to revert to the previously installed version of the ZX Library.
- Don Kinzer
Paul Lamar
Posts: 65
Joined: 14 May 2010, 16:01 PM

Post by Paul Lamar »

Hi Don,

Thanks for all your work on this.

Can I wait until the system is changed and then delete my current system and re download
and re install? I don't mind starting from scratch.

If I delete the RTC will the hardware interrupt routines still work?

I plan on implementing a RTC and RPM counter in external hardware
and disabling the internal RTC.
Of course that will disable these routines but I don't need them.

* Delay()
* DelayUntilClockTick()
* Pause()
* SetInterval()
* Sleep()
* WaitForInterval()
* GetMicroTime()
* GetElapsedMicroTime()

Hopefully that will improve my polling routine response by reducing the jitter and Int
latency time..

I'll let you know how it works out.
Here is something I did along those lines back in 1975.

BTW what happened to mm I/O port Peek and Poke?

Paul Lamar
Attachments
SuperKim-ad-200.jpg
(450.55 KiB) Downloaded 1868 times
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

Paul Lamar wrote:Can I wait until the system is changed and then delete my current system and re download and re install? I don't mind starting from scratch.
When a new installer is available, you can simply run it and it will install over the existing version with no problems. I posted the ZX Library update so that you can build your application now using WaitForInterrupt(). No changes to the compiler or IDE are required to resolve this issue.
Paul Lamar wrote:If I delete the RTC will the hardware interrupt routines still work?
The technique that I showed earlier merely disables the RTC interrupt. All other interrupts should continue to work normally.
Paul Lamar wrote:BTW what happened to mm I/O port Peek and Poke?
The I/O ports share part of the data space with RAM on an AVR. This means that you can access the I/O ports using the same instructions used to read/write RAM. If you use the Register.XXX form to access I/O ports, this is handled for you automatically. Alternately, if you know the address of an I/O port, you can also access it using RamPeek() and RamPoke() or by defining a Based variable at that address.
- Don Kinzer
Paul Lamar
Posts: 65
Joined: 14 May 2010, 16:01 PM

Post by Paul Lamar »

I decided not to use wait for interrupt as that appears to be a waste
of processor time. I think I want to use an ISR. Int0 to Int7

I have a good idea when several interrupt will occur time wise.

I'll have some non time critical code running in the back ground.

As I understand it so far the ISR will interrupt that code
save the context and allow me to service the that interrupt and return
restoring the context to my non time critical code running in the
background.

That way I should know the latency time exactly.
Now the question is; what routines can I interrupt and
be sure of a fixed latency time?

I think this is critical when attempting to measure RPM accurately with
an external counter and external real time clock.

I have done this before in asm.

Paul Lamar
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

Paul Lamar wrote:Now the question is; what routines can I interrupt and
be sure of a fixed latency time?
A fixed latency time is never possible with an asynchronous event like an external interrupt. The reason for this is that the active edge of the input signal can occur at any point in the CPU's clock cycle. Consequently, the best you'll ever get is a fixed amount +/- one CPU clock cycle and that will only occur if interrupts are never disabled and if no other interrupts will ever be serviced. Both of these situations increase the latency by, at most, the larger of the longest duration of interrupts being disabled and the duration of the longest executing ISR.

There are many situations in ZBasic code where interrupts are disabled for a few clock cycles, not least when necessary for guaranteeing atomic access to a multi-byte variable or for a read-modify-write operation. Beyond that, there are several of the specialized I/O routines that disable interrupts for significantly longer periods of time - these are all documented in the discussion of the respective routines in the ZBasic System Library Reference Manual.
Paul Lamar wrote:I think this is critical when attempting to measure RPM accurately with an external counter and external real time clock.
The important thing is to determine how much latency you can tolerate given the parameters of the devices you're trying to respond to/control.

If you're going to write an ISR, you should endeavor to do as little work as possible in the ISR and to try to avoid calling any external subroutines/functions in the ISR. Not only will this make that particular ISR more time-efficient but it will also reduce the latency that that ISR causes for other ISRs. If absolutely necessary, you can write your ISR in assembly language but this does require fairly in-depth knowledge of both the AVR processor architecture and the AVR assembly language.
- Don Kinzer
Paul Lamar
Posts: 65
Joined: 14 May 2010, 16:01 PM

Post by Paul Lamar »

I don't mind a few clock cycles.

This RPM hardware scheme can wait for reading the data
as the counter will be disabled in hardware and re enabled
by the program after it is read in.

Here is a preliminary schematic.

100 teeth on the flywheel coming in at 125 revs per second
at 7500 RPM is 12500 counts in one second..
In .6 second 7500 counds come in. If I read it
every .06 seconds RPM is plus minus 10.

RPM is read directly.

Do you have an example of an ISR routine embedded in a program?

I am not sure how it is implemented.

Say in the middle of a for next loop.

Paul
Attachments
RPM-hardware.jpg
(22.74 KiB) Downloaded 1879 times
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

Paul Lamar wrote:Do you have an example of an ISR routine embedded in a program? Say in the middle of a for next loop.
It is important to understand that an ISR is not conceptually "in the middle" of anything. It is essentially like a subroutine that gets called when the event happens that triggers the interrupt. Because it is invoked asynchronously with respect to "normal" subroutines, you cannot pass any parameters to it nor return any values from it except by way of global variables.

The syntax for defining an ISR is described in the ZBasic Reference Manual in the section titled Defining Interrupt Service Routines. Note that in addition to defining the ISR and writing the code for it you also need to set up the device to respond to the external stimulus. In order to know how to do this you must read and understand the relevant sections of the datasheet for the particular AVR upon which the ZX device is based. A link to the datasheet for the underlying devices can be found on the Downloads Page. Suffice to say that you will be reading/writing one or more Register.XXX elements to prepare the device to respond to external interrupts.

Given that you are probably interested in recording the elapsed time between active edges of the input signal, you'll probably want to set up a timer to free run at a convenient frequency. When the external interrupt ISR executes, you would then read the timer register and store it in an array or circular buffer. The mainline code could then compute the difference between each "timestamp" reading and the previous one thus giving you the instantaneous period of the input signal (i.e. the inverse of the frequency) from which you could compute instantaneous engine RPM. Of course, the instantaneous values are not as useful as the average value over some period of time so you'll probably want to include some sort of moving average calculation as well.

If you read the description of InputCapture() you'll probably recognize the similarity between what it does and what I described above. Consequently, you may want to look into using the input capture capability of the AVR device. Essentially, when you set up the device for input capture you enable a timer to free run at a convenient frequency. Then, when the active edge of the input capture signal occurs, the value of the timer is transferred to a holding register and an interrupt is generated (if it is enabled). Then, the ISR for the input capture interrupt can read the holding register and do something useful with it. Due to the fact that the transfer of the timer value to the holding register is done in hardware you have zero latency at the time of data capture. The only issue, then is ensuring that you respond to the input capture ISR in a timely fashion.

Although you could actually use InputCapture() to effect an RPM measurement (and I'm pretty sure that others posting here, in fact, do so) your requirements may obviate its direct use.
- Don Kinzer
Post Reply