C programming - How to compare timer0 reading to an earlier timer reading

Thread Starter

anon1

Joined Jan 25, 2018
13
Hi there. I've got a piece of a C programming exercise that I'm struggling with.

Info: Using PicKit 3 with PIC18F1220. Master clock is 10 MHz. Using timer0 (16-bit mode). The maximum number of combinations on timer0 is 65536. Language: C. Here's the lab exercise:

The Program

The pulse train will be fed through to RB2 or RB7 of the PIC depending on the switch setting. The section below is assuming the pulse train (not the sync pulse) is fed into RB2.

The waveform has equal mark-space for every 6 degrees of crank rotation apart from when the missing teeth occur. At this point the waveform remains low for 15 degrees as shown in figure 2.

You are required to write a program that will detect the missing teeth and each time this event happens to change the state of one of the display segments.

The algorithm that I suggest you use is as follows[1]:

  1. Measure the length of the high part of the pulse using a timer. This timer value which will, of course, vary slightly from pulse to pulse, then becomes our basic unit of time which represents 3° of crank angle.

  2. Wait for the next high pulse to start (ie the next rising edge on RB2) OR wait for 1.5 time units whichever happens soonest.

  3. If we have proceeded from step 2 because we have exceeded 1.5 units then we have found the missing pulse. If we have not exceeded 1.5 units then go back to step 1.

One important characteristic of this algorithm is that when it finishes step 3 and has not detected the missing pulse it can go straight back to step 1 and will not miss out any pulses.

Another important point is the use of mathematics. Any use of multiplication or division (or more complex functions) takes a lot of processing cycles[2] even though the PIC has a hardware multiplier. The cycles increase if one was to use 16 bit variables and increases substantially if floating point numbers are used. You must therefore be very careful how you calculate 1.5 units. 1.5 has been deliberately chosen because it need not incur a large calculation overhead.

To measure the length of a high pulse one must:
  1. Wait until RB2 equals 1

  2. Set the timer to zero

  3. Wait until RB2 equals 0
  4. Read the timer value and save it for later use.

[1]The best solution for real-time applications would use interrupts (because loops can be dangerous depending on the application) and some students in the past have created working missing tooth detection routines using interrupts BUT many more have tried and failed after wasting large amounts of time. I therefore suggest that initially you get an algorithm working without interrupts before trying something more complex, then you will have a contingency if things go horribly wrong!

[2]Calculation time is extremely important to consider. It is of no value if a calculation consumes so much time that it takes longer than when the event should be generated. Remember that at 6000 rpm each 360 ° cycle takes 10 ms. That is not a lot of time to perform complex mathematics even with a 100 MHz microcontroller and some modern motorcycles are red-lined at 16,000 rpm.
The problem I seem to be having is that the first timer reading doesn't seem to have been stored as intended for future use, or it gets reset to 0 at some point. If it doesn't detect a tooth at the second reading, then that's supposed to indicate that it's found the missing tooth. But it doesn't seem to store variable "time_unit" as intended as it seems to think the time_unit is 0. That would explain why it thinks that the next reading is always more than 1.5 times as large as the first reading. How could I store the timer reading as a variable ("time_unit") and then refer to it later without it being reset to 0 the next time I read the timer?
 

Attachments

Last edited:

Thread Starter

anon1

Joined Jan 25, 2018
13
next_reading includes both pulse times.
if((next_reading - time_unit) > time_unit *1.5))
Good point. Not sure what to do next though. After I subtract the original reading, it now seems to assume that the statement is always false. So it fails to detect the missing teeth. What am I missing?
 

AlbertHall

Joined Jun 4, 2014
12,346
You check for two teeth within that loop. It would be better to wait for high, read the timer and then go into the loop. Wait low, then high, then read the timer again. The pulse time is the new reading - the previous reading. The new reading now becomes the last reading for the next time round the loop.
 

Thread Starter

anon1

Joined Jan 25, 2018
13
You check for two teeth within that loop. It would be better to wait for high, read the timer and then go into the loop. Wait low, then high, then read the timer again. The pulse time is the new reading - the previous reading. The new reading now becomes the last reading for the next time round the loop.
How do you mean? I thought it was active low so 0 means high.
 

Thread Starter

anon1

Joined Jan 25, 2018
13
You check for two teeth within that loop. It would be better to wait for high, read the timer and then go into the loop. Wait low, then high, then read the timer again. The pulse time is the new reading - the previous reading. The new reading now becomes the last reading for the next time round the loop.
I changed the code but it still seems not to be detecting the gap with the missing teeth. Let's suppose the length of the first pulse that is measured is X, which is supposed to represent the time unit. I store this value as time_unit for later use. I only want to do this once and I want this variable to keep its value. Let's suppose the first reading is 4000 (=the length of the first pulse=time unit). If time_unit=4000, then the next reading should be about 8000 if the gap is the same length as the first pulse. So the gap between the pulses must be 8000 minus 4000 (and when the gap is significantly longer than X, we have found the missing teeth). If the gap (reading2 minus time_unit) is smaller than 1.5*time_unit (because of the instructions), I want to repeat the check. I reset the timer0 to 0 but want to keep the original value of time_unit and keep referring back to that. When I put "return;" in the super loop, it seems to go back to the beginning of the main program and measure the time unit again. How could I get it to skip the part where it measures the length of the first pulse (which is outside the super loop) and only repeat the super loop? I want to perform the first checks only once, then keep referring back to the time_unit as that value shouldn't change. How could I modify the new code so that I can control which loops I want to repeat and which parts of my program I only want to perform once (step1)?
 
Last edited:

AlbertHall

Joined Jun 4, 2014
12,346
You don't need to reset the timer.
A = ReadTimer
(time passes)
B = ReadTimer
C = B - A ; C holds the time between events A and B
(time passes)
D = ReadTimer
E = D - B ; E holds the time between events B and D
(and so on)

This works correctly even if the timer overflows between events - you still get the right result.
There is a problem if the time between consecutive events is greater then the rollover time of the timer.
 
Top