There are three main parts to the firmware for this project:
-The Arduino Setup
-The Arduino Loop
-The Arduino Interrupt Service Routine
The first part of any arduino program is the setup function. This occurs before the loop which continually executes forever. The setup portion of the code is meant to be run only once, so you should setup variables, interrupts and all hardware modules that you will be using. In our setup() function, you can see we setup the outputs and inputs for data latches, then we setup the timer interrupt and finally we initialize some variables to get standard values for the arduino.
The Arduino Setup
------------« Begin Code »------------
.. ... void setup() { //set port/pin mode DDRD = 0xFF;//all outputs DDRC = 0x00;//all inputs DDRB = 0xFF;//all outputs //TIMER INTERRUPT SETUP cli();//disable interrupts //timer 1: TCCR1A = 0;// set entire TCCR1A register to 0 TCCR1B = 0;// same for TCCR1B //set compare match register- 100khz to start OCR1A = 159; // = (16 000 000 / 100 000) - 1 = 159 //turn on CTC mode TCCR1B |= (1 << WGM12); // Set CS10 bit for 0 prescaler TCCR1B |= (1 << CS10); // enable timer compare interrupt TIMSK1 |= (1 << OCIE1A); samplerate = 100000; PORTB = 0; PORTB = 1<<type; //initialize variables frequency = analogRead(A5);//initialize frequency freqscaled = 48*frequency+1;//from 1 to ~50,000 period = samplerate/freqscaled; pulseWidth = analogRead(A4);//initalize pulse width pulseWidthScaled = int(pulseWidth/1023*period); triInc = 511/period; sawInc = 255/period; sinInc = 20000/period; sei();//enable interrupts } ... ..
------------« End Code »------------
The main loop for any firmware should be small with only the basic functions included. In the main loop here you can see 3 functions are run to check if any of the inputs have changed and to act accordingly. Download the full source code above to see what exactly these functions do.
The Arduino Loop
------------« Begin Code »------------
.. ... void loop() { checkFreq(); checkShape(); checkPW(); PORTB = 1<<type; } ... ..
------------« End Code »------------
The most important part of the code is the ISR or interrupt service routine. In the setup function we made it so that a timer would interrupt the main loop at a common interval, so that we can execute some additional code in case there are input changes. This interrupt service routine is where that additional code is and if you look through it you'll see a big switch statement that acts according to whatever type of waveform we should be currently outputting.
The Arduino Interrupt Service Routine
------------« Begin Code »------------
.. ... ISR(TIMER1_COMPA_vect){//timer 1 interrupt //increment t and reset each time it reaches period t += 1; if (t >= period){ t = 0; } switch (type) { case 0://pulse if (pulseWidthScaled <= t) { wave = 255; } else{ wave = 0; } break; case 1://triangle if((period-t) > t) { if (t == 0){ triByte = 0; } else{ triByte += triInc; } } else{ triByte -= triInc; } if (triByte>255){ triByte = 255; } else if (triByte<0){ triByte = 0; } wave = triByte; break; case 2://saw if (t=0){ sawByte=0; } else{ sawByte+=sawInc; } wave = sawByte; break; case 3://sine sinNum = t*sinInc; wave = pgm_read_byte_near(sine20000 + sinNum); break; } PORTD = wave; } ... ..
------------« End Code »------------
Enough code and firmware, use a usb cable to load the firmware onto the Arduino UNO using Arduino software, then get testing to see how the waveform generation works! On the next page we'll show some demonstrations of the waveform generator working.