Okay, I did get it working by manually configuring timer1. Changes in capacitance are reflected as a change in frequency in the speaker output.
It’s definitely quick and dirty, but fwiw here’s the code (I’m using Paul Badger’s capacitance sensing code here: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1171076259):
int i;
unsigned int x, y;
float accum, fval = .20; // these are variables for a simple low-pass (smoothing) filter - fval of 1 = no filter - .001 = max filter
long fout;
int period = 160;
char sineovertones[] = {0,84,106,99,113,127,107,79,71,51,1,-26,7,43,23,-17,-26,-23,-43,-60,-49,-44,-66,-63,0,63,66,44,49,60,43,23,26,17,-23,-43,-8,26,-1,-51,-71,-79,-107,-127,-113,-99,-106,-84};
char sine[] = {0, 22, 44, 64, 82, 98, 111, 120, 126, 128, 126, 120, 111, 98, 82, 64, 44, 22, 0, -22, -44, -64, -82, -98, -111, -120, -126, -128, -126, -120, -111, -98, -82, -64, -44, -22};
//a simple sine wave with 36 samples
int analog0 = 160; //variable used to store analog input pin 0
byte speakerpin = 3; //audio playback on pin 3. This can also be set to pin 11.
volatile byte waveindex = 0; //index variable for position in waveform array Sine[]
volatile byte currentvalue = 0;
void setup() {
Serial.begin(57600);
/************************** PWM audio configuration ****************************/
// Configures PWM on pins 3 and 11 to run at maximum speed, rather than the default
// 500Hz, which is useless for audio output
pinMode(3,OUTPUT); //Speaker on pin 3
cli(); //disable interrupts while registers are configured
bitSet(TCCR2A, WGM20);
bitSet(TCCR2A, WGM21); //set Timer2 to fast PWM mode (doubles PWM frequency)
bitSet(TCCR2B, CS20);
bitClear(TCCR2B, CS21);
bitClear(TCCR2B, CS22);
/* set prescaler to /1 (no prescaling). The timer will overflow every
* 62.5nS * 256ticks = 16uS, giving a PWM frequency of 62,500Hz, I think. */
sei(); //enable interrupts now that registers have been set
/************************* Timer 1 interrupt configuration *************************/
cli(); //disable interrupts while registers are configured
bitClear(TCCR1A, COM1A1);
bitClear(TCCR1A, COM1A1);
bitClear(TCCR1A, COM1A1);
bitClear(TCCR1A, COM1A1);
/* Normal port operation, pins disconnected from timer operation (breaking pwm).
* Should be set this way by default, anyway. */
bitClear(TCCR1A, WGM10);
bitClear(TCCR1A, WGM11);
bitSet(TCCR1B, WGM12);
bitClear(TCCR1B, WGM13);
/* Mode 4, CTC with TOP set by register OCR1A. Allows us to set variable timing for
* the interrupt by writing new values to OCR1A. */
bitClear(TCCR1B, CS10);
bitSet(TCCR1B, CS11);
bitClear(TCCR1B, CS12);
/* set the clock prescaler to /8. Since the processor ticks every 62.5ns, the timer
* will increment every .5uS. Timer 1 is a 16-bit timer, so the maximum value is 65536,
* Giving us a theoretical range of .5us-32.7mS. There are 48 samples, so the
* theoretical frequency range is 41.7KHz - .635Hz, which neatly covers the audio
* spectrum of 20KHz-20Hz. Theoretical, because I wouldn't recommend actually calling
* the Timer1 interrupt every .5uS :) */
bitClear(TCCR1C, FOC1A);
bitClear(TCCR1C, FOC1B);
/* Disable Force Output Compare for Channels A and B, whatever that is.
* Should be set this way by default anyway. */
OCR1A = 160;
/* Initializes Output Compare Register A at 160, so a match will be generated every
* 62.5nS * 8 * 160 = 80uS, for a 1/(80uS*48) = 260Hz tone. */
bitClear(TIMSK1, ICIE1); //disable input capture interrupt
bitClear(TIMSK1, OCIE1B); //disable Output Compare B Match Interrupt
bitSet(TIMSK1, OCIE1A); //enable Output Compare A Match Interrupt
bitClear(TIMSK1, TOIE1); //disable Overflow Interrupt Enable
sei(); //enable interrupts now that registers have been set
delay(1000);
}//end setup()
ISR(TIMER1_COMPA_vect) {
/* timer1 ISR. Every time it is called it sets
* speakerpin to the next value in Sine[]. frequency modulation is done by changing
* the timing between successive calls of this function, e.g. for a 1KHz tone,
* set the timing so that it runs through Sine[] 1000 times a second. */
if (waveindex > 47) { //reset waveindex if it has reached the end of the array
waveindex = 0;
}
analogWrite(speakerpin, sineovertones[waveindex] + 128);
waveindex++;
OCR1A = period;
} //end Timer1 ISR
void loop()
{
//analog0 = analogRead(0) + 4;
//Serial.println(analog0);
y = 0; // clear out variables
x = 0;
for (i=0; i < 4 ; i++ ){ // do it four times to build up an average - not really neccessary but takes out some jitter
// LOW-to-HIGH transition
digitalWrite(8, HIGH);
// output pin is PortB0 (Arduino 8), sensor pin is PortB1 (Arduinio 9)
while ((PINB & B100) != B100 ) { // while the sense pin is not high
// while (digitalRead(9) != 1) // same as above port manipulation above - only 20 times slower!
x++;
}
delay(1);
// HIGH-to-LOW transition
digitalWrite(8, LOW);
while((PINB & B100) != 0 ){ // while pin is not low -- same as below only 20 times faster
// while(digitalRead(9) != 0 ) // same as above port manipulation - only 20 times slower!
y++;
}
delay(1);
}
fout = (fval * (float)x) + ((1-fval) * accum); // Easy smoothing filter "fval" determines amount of new data in fout
accum = fout;
period = constrain(fout / 3, 50, 500);
Serial.println(period);
}//end loop()
Perhaps whoever maintains the timer1 library should take a gander at why you can’t change timer frequency dynamically?