SparkFun Forums 

Where electronics enthusiasts find answers.

Your source for all things Atmel.
By altontoth
#50085
I have a system I'm trying to build where I have a starting gate/switch, then when activated, triggers a timer in the Atmega32 to begin. There are 5 lanes to be monitored, and I want to be able to record the completion times from start to crossing the finish sensor for each of the 5 lanes. That information is then sent back to the computer through a FT232R USB connection.

My idea is to use a light dependent resistor and voltage divider to pull each of the input pins high....but can I even time 5 separate events at once?

From what I was reading in the PDF files, I can only monitor 2 inputs? Is this true? I can't seem to find any sort of code samples on the net for the Atmega32 (I have several of these chips from an aborted project that I would like to use).

My apologies for asking what may be a foolishly simple question. I have not yet built a programmer, so haven't had a chance to 'experiment' with code for the Atmega. Thanks!
By n1ist
#50088
You can always repeatedly read (maybe in a timer isr) an input port and if a bit indicates a lane has been crossed, save away the time.
/mike
By muntron
#50089
altontoth wrote:My idea is to use a light dependent resistor and voltage divider to pull each of the input pins high....but can I even time 5 separate events at once?
The response time of LDRs is not going to be satisfactory for this application. You should look into a phototransistor. Depending on expected ambient conditions, you may need illumination by an LED.
altontoth wrote:From what I was reading in the PDF files, I can only monitor 2 inputs? Is this true? I can't seem to find any sort of code samples on the net for the Atmega32 (I have several of these chips from an aborted project that I would like to use).
I am not familiar with the ATmega32, but there are several generic solutions. Depending on your required resolution (say 1/100 S), the system interrupts at a higher rate 4X to 10X. On each interupt, read timer and check inputs for high condition. If your finish pulses are shorter then your interrupt period, you can stretch them. The alternate to polling is to interrupt the system with a logical combination from the various inputs. On each interrupt, check the inputs. Hope this makes some sence.
By altontoth
#50611
Somewhat. My original design actually calls for a IR photo detector with a IR emission source mounted above my track (be it LED or standard incandescent lamps). Someone suggested an LDR, so I was looking into that.

The timer that I had previously been using would report results to the 10000th of a second. Over a parallel port, I'm not sure how accurately the computer/software was resolving that time, but it prevented a tie (only 6/10000 of a second difference between 1st and second places). Because this is a setup for youth, if the timing system has a slight drift (if that makes sense), that's alright. They have a ton of fun with it anyways, and everyone races in each lane of the track system once anyways, to average out errors.

Now, to repeat what I think I've understood...As soon as it detects the starting gate triggered, the program would start the timer and drop into a loop. Essentially the only purpose of this loop would be to continually poll the 5 inputs and see if any of them are triggered. If it was, it would simply record the 'tick' count and carry on the routine until either all 5 lanes finished or the timer reached maximum value (9.99 seconds). IT would then compute what each of those tick counts really means, and then send the data back to the computer. Is that right? Or am I able to record the time from the timer directly? I was planning on using the built in oscillator for this circuit, but board revisions allow me to attach an external one if that would be better.

Sorry for the long winded post!

Cheers,
Alton
By stevech
#50637
Suggestion: buy some $3 IR remote control receiver modules. Available even at Radio Shack. These have an IR receiving diode and a narrowband filter and amplifer. This is what makes IR reliable versus range and ambient light.

On the sending side, use a 555 chip set to generate 38KHz or whatever the receiver module is set for, then drive an IR emitter diode. Lens preferred. Or use a little microprocessor instead of a 555.

On the receiving side, a microprocessor or PC can detect on/off (IR beam-break) and log the time.

You should get good reliable performance with this, due to the carrier (38KHz) and the filter/amp.
By SecretSquirrel
#51055
You will have a hard time using the system timers due to resolution problems. If you take the maximum time to be 10 seconds then a count of 65k is ~0.00015 seconds.

I might take a slightly different tact here. This would let you monitor up to eight lanes easily, more if you have the ports.

You main timing loop needs to be responsible for updating the timer count. Once the timer is started, go into a tight loop that just updates a 24 bit value. You need to have exit cases for time overflow and for all lanes reporting.

Your timing loop looks something like this:

disable interrupts
load global timing variable into registers
increment timing variable
store global timing variable into SRAM
enable interrupts
check exit cases and loop

This will give a vary fixed repeat rate for your timing (I'm guessing around 20-30 cycles). To capture timing info, connect each lane to a port line and set the port lines up for pin change interrupt. When the PCI occurs, you can see which of the lanes had triggered the interrupt and store the timer count for that lane then mask off that pin from triggering another interrupt.

Assuming you can get the ISR into around 70-80 cycles, on the 8Mhz internal oscillator, you should be able to get around 0.0000125s accuracy.

As a side note, if you don't mask out a pin once it has triggered an interrupt you will get another interrupt when the car clears the sensor too. If you can guarantee that the begin to end distance is the same (the thing triggering the lane sensor is the same distance on all cars) then you can actually calculate the speed of the car at the end of the race too.
By SecretSquirrel
#58448
Sorry to resurrect an old thread, but altontoth had sent me a note asking for a bit more detail on my prior posting and had asked that I post to the thread to "share the knowledge". So, I am.

I don't know if you are using C or assembler, but I'm going to post assembler as the timing will be much easier to handle. This completely untested code and may not even compile, but it will be a starting point.
Code: Select all
//Registers R0, R1, R2 will be your time value.
//XORing a register with itself will zero its value
eor R0, R0
eor R1, R1
eor R2, R2

//R3 will be used as a constant zero
EOR R3, R3

//R10 will be used to track lanes that have started
//R11 will be used to track lanes that have finished
eor R10, R10
eor R11, R11

//configure port B for your starting sensors and port C for
//your ending sensors.  This will configure port B and C as
//inputs with pull ups disabled.  This means that the lines
//driving the port must have valid logic levels at all times. 
//This code will assume that the inputs will go high when 
//the start or end sensors are triggered
ldi R16, 0;
mov DDRB, R16
mov DDRC, R16
mov PORTB, R16
mov PORTC, R16

//Register pair X will point to the starting time data.  You would
//need to make the constants referred to in the following instructions
//point to somewhere in RAM
ldi R27, START_HIGH
ldi R26, START_LOW
ldi R29, END_HIGH
ldi R28, END_LOW

//Disable all interrupts
cli;

LOOP:
	//check to see if any lanes have started.  Skip the 
	//completed part of the check if no lanes have started.
	cp R10, R3
	breq NO_START_DELAY
	//If we have lanes started, check to see if all lanes that
	//started have finished.
	cp R10, R11
	brne NO_START
	jmp DONE
NO_START_DELAY:
	nop
	nop
NO_START:
	//Now lets get the values of the start and finish sensors in 
	//case they change while we are working with them.
	mov R16, PINB
	mov R17, PINC
	//check to see if any lanes have started
	cp R16, R3
	//We insert a delay to account for the unexecuted instructions
	//if we have no lanes started
	breq CHECK_FIN_DELAY
	//store the lanes that have started
	st X+, R16
	//now store the time stamp for the start
	st X+, R0
	st X+, R1
	st X+, R2
	//set flags for the lanes that have started
	or R10, R16
	jmp CHECK_FIN
CHECK_FIN_DELAY:
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
CHECK_FIN:
	//check to see if any lanes have finished
	cp R17, R3
	breq INC_TIME_DELAY
	//store the lanes that have started
	st Y+, R17
	//now store the time stamp for the start
	st Y+, R0
	st Y+, R1
	st Y+, R2
	//set flags for lanes that have finished
	or R11, R17
	jmp INC_TIME
INC_TIME_DELAY:
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
INC_TIME:
	//set the carry flag.  When we add zero to the low byte of our time
	//stamp the set carry will also be added effectively incrementing by one
	sec
	adc R0, R3
	adc R1, R3
	adc R2, R3
	//at this point, if the carry is set then we have overflowed our time stamp
	//counter and need to exit otherwise we loop back and do the whole thing
	//over
	brcc LOOP

DONE:
	//terminate the series of timestamps for start and end with a set of four
	//0x0FF bytes to signify the end of data during read out.
	ldi R16, 0x0FF
	st X+, R16
	st X+, R16
	st X+, R16
	st X+, R16
	st Y+, R16
	st Y+, R16
	st Y+, R16
	st Y+, R16
	//When you get here, one of two conditions is true
	//1.  The timer overflowed before all lanes completed or any lanes started
	//2.  All lanes that started have completed.
At the end, R10 will have a bit set for each lane that started and starting at the original address pointed to by X, you will have a sequence of bytes with the first byte containing the lanes that started and the next three being the 24 bit time stamp for when they started. Likewise, R11 will have a bit set for each ending lane and starting at the original Y address will be the sequence for ending lanes. You will then need to read out each of the timestamp sequences and send it to the host computer. Each "tick" of the 24 bit timer is going to represent around 41 clock cycles or 0.000005125 seconds at 8MHz. The 24 bit timer would overflow at just under 86 seconds (85.98322...).

I should point out that there is a lot of error checking that is not included. What happens if the start sensor triggers multiple times for the same lane, or the end sensor triggers before the start sensor, etc. The way the code about is layed out, all the program does it collect time stamps for trigger events. It is much simply to let the host computer program do the error checking and stuff. If you do add more error checking to the above code, you have to make sure that the number of clock cycles for every path through the code is the same or timing will differ depending on which path is taken. That is the reason for the NOPs sections in the above code. You could probably reduce the number of clock cycles used with more careful coding, but I figured that 5 millionths of a second was accurate enough. If you need a longer maximum time, you can easily extend the timer to 32 bits.

Hope this helps some.
Code: Select all
By signal7
#58531
If it were me, I would use the 16 bit timer (assuming the mega32 has one and I'm pretty sure it does) and then use one of the other general purpose registers to count the overflow. You still get a 24 bit count, but you then don't have to deal with counting code cycles and worrying that one code path takes significantly longer than another code path. The timer has an interrupt-on-overflow that you can use to detect when it rolls over so you can increment your most significant byte of the count. When a lane triggers, just read the register holding the most significant byte and the TCNTxH and the TCNTxL registers for the full count value.

The nice thing about this is that the timer runs independently of everything else going on in the processor. You also will know the frequency of the counter and can get better accuracy using an external crystal if you really want/need to. You could also convert your project over to C instead of assembly and not worry too much about losing accuracy.

The only problem you'll have is trying to do 24bit math, but you would have that problem no matter how you implemented the timing routine due to the time length (10 seconds) and the resolution you desire.
By SecretSquirrel
#58669
signal7 wrote:If it were me, I would use the 16 bit timer (assuming the mega32 has one and I'm pretty sure it does) and then use one of the other general purpose registers to count the overflow. You still get a 24 bit count, but you then don't have to deal with counting code cycles and worrying that one code path takes significantly longer than another code path. The timer has an interrupt-on-overflow that you can use to detect when it rolls over so you can increment your most significant byte of the count. When a lane triggers, just read the register holding the most significant byte and the TCNTxH and the TCNTxL registers for the full count value.
The only issue I would have here is that there is no atomic way of doing the timer/overflow read. You will have a race condition if a lane triggers during an overflow period. Assume the counter (including the overflow byte is 0x00FFFE when you start the read. Reading the overflow byte gets 0x00 and the timer overflows. By the time you get back from the ISR the timer is at 0x0010 and you the TH and TL. The value your program has read is 0x000010 instead of the expected 0x00FFEE. Your accuracy is off by about 0.31sec (assuming a max period of 86 seconds). The same thing can happen if you read the overflow byte last -- the timer overflows after reading TH and TL, but before reading the overflow. This race condition will occur a bit over three times per second on average.

In order to avoid this, you have to turn of interrupts during the reading of the timer+overflow values. If I were heading down this route, I would probably use the pin change interrupts for gate triggers as well. But, all this makes for a much more complex set of coding than is actually needed and in the end decreases accuracy, IMHO.


--SS
By signal7
#58702
I don't know how anyone could have arrived at a figure of 0.31 seconds of inaccuracy. At an 8MHz clock, your interrupt service routine(ISR) would have to take over 2.4 *million* cycles to complete. I don't know about you, but I have a hard time wasting that many cycles on purpose much less doing it on accident. :shock:

Remember - most instructions only take one cycle and most ATMega parts can run at a full 20MHz if need be. You have more than enough time to track an overflow byte and you don't need (or want) to have your counter running at the full clock speed of the AVR core. I'd even bet that you could easily have your ISR for tracking overflow complete in less than one tick of the counter.... and still maintain the required accuracy.
By SecretSquirrel
#58754
signal7 wrote:I don't know how anyone could have arrived at a figure of 0.31 seconds of inaccuracy. At an 8MHz clock, your interrupt service routine(ISR) would have to take over 2.4 *million* cycles to complete. I don't know about you, but I have a hard time wasting that many cycles on purpose much less doing it on accident. :shock:
You are not wasting that many cycles. In fact, the inaccuracy isn't at all dependent on the execution time of the ISR, or any other part of the program. It is purely dependent of the clock speed used to drive the timer and hence the time span represented by the full 24 bit count.

It has to do with the fact that you counter gets updated in the middle of reading it. In the above example, you read the value of the overflow count as '0', when the counter value is 0xFFFE. Assuming you store this to a RAM location (as a C program likely will), the timer will overflow during the store and trigger the ISR which will update the overflow byte to '1' and the timer value will be slightly above 0x0000 by ISR return. I used 0x0010 in the above example. This means that when the ISR returns and you continue with the reading of the timer, the value you store will be 0x0010 (or something slightly above it in reality). So, instead of reading 0x00FFEE (which is the value when you started reading everything), you end up reading 0x000023 (for example). Remember that you read the MSB (the overflow counter) prior to timer overflow and ISR. This means that your count is off by 65483 'ticks' or 0.3356665822 sec (assuming 86 second full count again).
By altontoth
#58762
From what I've been reading up this evening and playing around with the calculators I can find, would I perhaps not be better off to pre-load my timer in such a way that it only counts as far as 250mSec, then triggers an overflow, which increments a variable by 1? This way, as soon as a lane is detected having finished, it would simply need only to record the major increments, and the number of timer ticks. From there it would be able to calculate the run time.

A clock frequency of 8 Mhz, with pre-scaling of 32 on the 16 bit timer, should give me a maximum of 262.144 mSec between overflows with .1mSec accuracy. Preloaded so that the timer overflows every 250mSec....should work right? Or am I totally out to lunch?

Btw, I don't think I mentioned it before, but I'm only looking at a 10 Second maximum count, 5 lane timing. Also, I do my programming in C. I haven't the faintest clue what I'm doing in Assembler, and I'm new to microcontrollers. KISS methodology after all ;-)
By signal7
#58786
SecretSquirrel wrote: It has to do with the fact that you counter gets updated in the middle of reading it. In the above example, you read the value of the overflow count as '0', when the counter value is 0xFFFE. Assuming you store this to a RAM location (as a C program likely will), the timer will overflow during the store and trigger the ISR which will update the overflow byte to '1' and the timer value will be slightly above 0x0000 by ISR return.
That is what I was trying to point out. The timer can not overflow while you are storing the value. If you are using a pin change interrupt to store the value, the overflow interrupt can not be serviced while you are storing the value. Taking that a bit further, either the overflow interrupt will occur or the pin change interrupt will occur. The one that happens first is the one that runs. If the ISR is very short, the actual difference in the measured value won't be more than one significant digit off. Furthermore, if we assume an 8MHz clock *and* we assume the counter is counting at 10KHz, you will have up to 800 cycles for storing the value! The amount in inaccuracy you're talking about is so infinitely small that no one would care about it.

I mean, what's the difference between 0x00ffff and 0x010000? It would be 0.0001 seconds and the OP clearly stated that at that resolution, the actual observed relative difference between events was 0.0006. In terms of core clock cycles, you will not waste 800 cycles storing a value anyway, so you would only have 0.0001 of an inaccuracy probably once in 40 measurements, if that.
By signal7
#58788
altontoth wrote:From what I've been reading up this evening and playing around with the calculators I can find, would I perhaps not be better off to pre-load my timer in such a way that it only counts as far as 250mSec, then triggers an overflow, which increments a variable by 1? This way, as soon as a lane is detected having finished, it would simply need only to record the major increments, and the number of timer ticks. From there it would be able to calculate the run time.

A clock frequency of 8 Mhz, with pre-scaling of 32 on the 16 bit timer, should give me a maximum of 262.144 mSec between overflows with .1mSec accuracy. Preloaded so that the timer overflows every 250mSec....should work right? Or am I totally out to lunch?

Btw, I don't think I mentioned it before, but I'm only looking at a 10 Second maximum count, 5 lane timing. Also, I do my programming in C. I haven't the faintest clue what I'm doing in Assembler, and I'm new to microcontrollers. KISS methodology after all ;-)
That would work. The code I sent you did basically just that - the only difference is that without the conveniences of a C compiler, I had to take care of retrieving and storing the "variables" to memory. One exercise that might give you more experience with assembly is to try writing a simple program in C and then telling gcc to only process the code to the assembly stage (which, I think, is option -E on the command line). Then you can view the output and compare it to the program you wrote. At the least, it will give you an idea of how the compiler converts your code to assembly.

One thing I would do differently is to clock the counter with an external crystal at 10KHz while running the core on the internal 8MHz oscillator. It makes the math a little easier, imho.

Going with the 8MHz divided by 32 idea, each clock tick is 4uS with an overflow every 262.144mS by my calculations. I think you could use a much more coarsely divided clock and still achieve the required accuracy. You would also reduce the frequency with which you would have an overflow condition which would give more time to the interrupt service routine.
By altontoth
#58803
Being a noob, I'm using the KAVRCalc to fiddle with various timer settings. I only mention 32 as the prescaler because it is the last setting at which the program says that I have zero error. Thanks for the ideas. I'm off to see another programmer later today where we can sit down face to face and go over some of this stuff.

One other point of mention. I haven't looked too much into these pin change interrupts, but my initial plan had been to poll each lane sensor rapidly in succession. Running at 8 Mhz, I figure that any errors in time count would be 1)Infinitesimally small, and 2)Average out since each kid races in each lane at least once.

Questions? Comments? Telling me I'm a rediculous noob who has no idea what he's doing? ;-)