Note: I originally posted this answer for the Stack Overflow copy of this question, before realizing that it had also been asked here. It somewhat duplicates pichenettes' answer, but I felt it still worth (re)posting here, since it includes some extra details. (Whether those details are useful or not, I'll leave for you and the OP to judge.)
If you know your signal is a pure sine wave, you can just use zero crossing detection. Each cycle of the sine wave will have two zero crossings: one from negative to positive, and one from positive to negative. This will be a lot simpler and more efficient than trying to do something fancy like Fourier transforms.
There are a few details to keep in mind, though:
It's OK for the signal to be slightly biased, but if the bias might exceed the amplitude of the signal, you'll need to correct it somehow.
You can either do this before sampling with an analog high-pass filter, or you can track a moving average of your sampled signal and use it as the "zero level" to compare to. Or you can, instead, look at zero crossings of the difference between successive samples (which correspond to the maximum and minimum of each cycle in the signal) to avoid any bias issues.
If your input (or ADC) is noisy, your samples might randomly fluctuate around the zero level when the signal is close to it. In such cases, naïvely comparing successive samples might detect multiple zero crossings where there's really just one.
One way to fix this issue is to smooth your signal before processing it, but it may be easier and more efficient to implement hysteresis in your zero-crossing detector. That is, you'd only detect a positive-to-negative crossing when the signal dips below some pre-set threshold level −ε, and a negative-to-positive crossing only when it rises above +ε, like this pseudo-C code:
boolean isPositive = (firstSample > 0);
while (running) {
int signalLevel = getNextSample();
if (isPositive && signalLevel < -threshold) {
isPositive = false;
handleZeroCrossing();
}
else if (!isPositive && signalLevel > +threshold) {
isPositive = true;
handleZeroCrossing();
}
}
In fact, you may not even need an algorithm for this at all, since, as noted in the comments below, you can implement zero-crossing detection with hysteresis in hardware with a simple Schmitt trigger, which basically converts the sine wave input into a square wave signal with the same frequency and (almost) the same phase, which you can then read as a simple digital input. You might even be able to use the output of the Schmitt trigger to drive the MCU interrupt pin directly.