#include void init(); //set every thing up, look in here for info on setting up timers int main(){ const uint32_t TIMER_HZ = 1000000; //The speed of our timerclock, after prescaling. uint16_t freq = 0; //this varible will hold our desired frequency uint8_t buttons; //this holds the state of the buttons init(); //set up our timer... while(1){ //do this forever... buttons = ~PINC; switch( buttons ) { case 1: freq=262; PORTA = ~1; break; case 2: freq=294; PORTA = ~2; break; case 4: freq=330; PORTA = ~4; break; case 8: freq=349; PORTA = ~8; break; case 16: freq=392; PORTA = ~16; break; case 32: freq=440; PORTA = ~32; break; case 64: freq=494; PORTA = ~64; break; case 128: freq=523; PORTA = ~128; break; default: freq=0; PORTA = ~0; break; } //Now we use the value in `freq' to set up our timer. OCR1A determines the overal period // of the waveform and OCR1B determines the duty cycle. In this example, we just want to // change the frequency and keep the duty cycle at 50% so we always set OCR1B to be half // of ICR1A OCR1A = (TIMER_HZ/freq + 1)/2; OCR1B = OCR1A/2; } } void init(){ /* General Theory of Operation * The goal is to be able to output a given frequency, to achieve this... * For this program we are going to set up our timer, so that it ticks * one million times per second, assuming we use the on board oscillator * of 8Mhz, that is 8 million cycles per second. To do this, we need to pre-scale (divide down) * the clock by 8. * The next trick is to make a pin automagically toggle (change state: 0-->1 or 1-->0) when we reach a * designated value, this can be done using an Output Compare Register (OCR) * supplied with the timer/counter. For the 16-bit timer on the ATmega16 (timer1) * there are 2 channels of OCR (A and B), we need only use: OCR1A... Output Compare Register timer1 channel A... * we tell the timer to toggle the pin tied to this function. This pin is given the name * OC1A, and is coincident with the 5th pin in PORTD, thus PD5 * The final touch is to change the timer so its TOP value is equal to the OCR1A value. * This allows us to know that the number of tics between overflows is precisely OCR1A, * and thus each time OCR1A toggles twice, we have the right period...., and 1 over period is frequency... yay! */ /*and finally..... to set up hardware in the ATmega16, we must manipulate special function registers. These registers * tell the hardware how it should behave The datasheet describes all the available hardware and * the registers associated with its control. */ //information on the Timer Control Registers for timer1 on page 110 of the ATmega16 datasheet TCCR1A |= _BV(COM1B1); //toggle the PIN OCR1A when Timer 1 has reach the value stored in OCR1; TCCR1B |= _BV(CS11); //prescale the timer clock by 8 TCCR1A |= _BV(WGM10); //Set up Waveform Generation Mode 9... OCR1A as TOP, update OCR1A TCCR1B |= _BV(WGM13); //when we reach BOTTOM value //summary of all WGMs can be found on page 112 of the ATmega16 datasheet... //Set up the port we're using for LEDS to be output and set them high (LED's off) DDRA = 0xFF; PORTA = 0xFF; //final make our OC1B pin (output for waveform) an output DDRD = _BV(PD4); //Turn on the internal pull-ups for the switches. DDRC = 0x00; PORTC = 0xFF; }