/* Modulate_ErrorSignal.pde Please note: this file was based on a sound processing example by Martin Nawrath, although the code has been changed to make it easier for beginners to follow and also to suit the actual problem, which in my case is to stabilise the frequency of a laser diode to the peak in a saturated absorption signal. Original header/comments: */ /* Arduino Audio Loopback Test * * Arduino Realtime Audio Processing * 2 ADC 8-Bit Mode * ana�og input 1 is used to sample the audio signal * analog input 0 is used to control an audio effect * PWM DAC with Timer2 as analog output * KHM 2008 / Lab3/ Martin Nawrath nawrath@khm.de * Kunsthochschule fuer Medien Koeln * Academy of Media Arts Cologne */ // Apart from this original header, all other comments and code have been inserted and or modified // by Mark Sadgrove. // In this "annotated" version, I have added references // to the ATMEGA168 datasheet (sometimes just referred to as "the datasheet") // whenever an sbi or cbi function is called, and also tried to explain more // thoroughly what each call is doing! Assume that any long comments are by // Mark Sadgrove. Sometimes, I have explicitly noted this by prefacing comments // by "MS". // Basically, if you want to take advantage of some of the ATMega's functions, such as general // interrupt functions as in this example, you have to learn a bit about the ATMega168 on a // hardware level. Specifically, you have to consider a number of the specialised registers // the ATMega contains. In case anyone reading this is as much of a newbie as me, // I am going to go into patronising detail: A register is just a number of bits (often 8 of them for the ATMega) // which have a fixed address. That is they are a hardware thing, not a software thing! // To control the Atmega's many special functions, we have to set and/or clear individual // bits in a register. The only way to do that in C is to use the bitwise operators, although // there are handy macros called sbi ("set bit") and cbi ("clear bit") which are defined as follows: #define clearbit(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // For the register sfr, clears the bit at position bit. #define setbit(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // For the register sfr, sets the bit at position bit. // The _BV(bit) macro converts the value given by bit into a string of zeros with a 1 at the location given by the argument "bit", // e.g. BV(2) = 0000 0100 // sbi allows us to set a specific bit of the register sfr by creating the appropriate bit string as above and then bitwise or-ing // the result with the register itself. Here is an example: // sfr = 1001 0010 // say we want to set the bit in position 2: // sbi(sfr,2) = 1001 0010 |0000 0100 = 1001 0110 // You can follow the same reasoning to see how cbi clears the bit at position "bit" // of the register sfr int modPin = 6; // Pin used to perform digital current modulation int errPin = 7; // Pin used for digital error signal output boolean div32; // Remaining from the original audio application. Used to divide the timer interrupt into two. boolean div16; // Variables used in the interrupt routine should // be declared volatile. This means that they could be changed // by routines outside of the interrupt handler. volatile boolean f_sample = false; volatile byte Vatmod0 = 0; volatile byte Vatmod1 = 0; volatile byte ibb; int phase = 1; // Can be changed to change the sign of the feedback boolean err; // Error signal //byte dd[512]; // Audio Memory Array 8-Bit void setup() { pinMode(modPin, OUTPUT); pinMode(errPin, OUTPUT); PORTD = B01000000 | PORTD; // Set pin 7 high, leave other pins as they are. phase = 1; // Phase can be +1 or -1; digitalWrite(ledPin,LOW); // Here, we are setting up the Analog to Digital COnverter (ADC). // Obviously, for an audio processing application, we need to set a high sampling rate. // There's no point having interrupt driven output and high frequency analog // output PWM (see below) if we're not sampling the input at a high enough rate. // NB:ORIGINAL COMMENT: set adc prescaler to 64 for 19kHz sampling frequency // MS adds: This does not seem to be true!!!! // According to the datasheet, the prescaler division factor is set to 8 by the following // selection. // See page 256 of ATMEGA168 datasheet clearbit(ADCSRA, ADPS2); // Clear right most bit of 3 bit prescale select register setbit(ADCSRA, ADPS1); // Set middle bit of 3 bit prescale select register ssetbit(ADCSRA, ADPS0); // Set rightmost bit of 3 bit prescale select register // Final selection is 011 which corresponds to a division fator of 8. See pp. 256 of datasheet // Instead of being able to access each analog input channel separately, // the channels are connected to a multiplexer which has to be switched to the // desired output channel (see pp. 244 of the datasheet for a diagram). // For information on these registers and bit assignments, see page 254 // of ATMEGA168 datasheet setbit(ADMUX,ADLAR); // 8-Bit ADC in ADCH Register MS: See pages 154 and 157. Basically, // the ADC is capable of 10 bit conversion, but you need to rad two 8 bit registers // not just one to get 10 bits, thus creating a performance hit. // Setting the ADLAR bit in the ADMUX register sets up the output // registers so that the AD conversion result can be read off from // just one register setbit(ADMUX,REFS0); // VCC Reference MS: Setting this bit and clearing REFS1 // allows an external voltage reference to be // connected to the AREF pin which will be used to determine the maximum voltage clearbit(ADMUX,REFS1); // // See page 255 of the ATMEGA168 datasheet for information on the following registers clearbit(ADMUX,MUX0); // Set Input Multiplexer to Channel 0 clearbit(ADMUX,MUX1); // MS: These four bits allow us to choose which of the 8 analog inputs the clearbit(ADMUX,MUX2); // multiplexer is switched to and thus which input channel the ADC gets clearbit(ADMUX,MUX3); // performed on. Setting them all to zero chooses channel 0. // Timer2 PWM Mode set to fast PWM // MS: There is a fast pwm mode which doubles the output // pwm frequency by counting from bottom-> top/bottom->top/bottom->top/etc // instead of bottom->top/top->bottom/bottom->top/top->bottom/etc // See ATMEGA168 Datasheet page 145 for more information // on fast pwm mode // For the following registers and bits, see page 152 of the ATMEGA168 datasheet clearbit (TCCR2A, COM2A0);// Setting this bit and the one below puts the Output Compare pin OC2A setbit (TCCR2A, COM2A1);// into compare/match mode with the pin being cleared when matching occurs // This comparing and matching of a constant register with a counter is the // essence of how pwm works, with the pwm output pin being flipped whenever matching // between the counter and the Output Compare pin occurs. // Waveform generation bits - see page 154 of ATMEGA168 datasheet. NB there seems // to be a slight difference between the actual naming of the waveform generation bits // as correctly used here and the names given to the bits in table 15-8 on page 155 // of the datasheet. setbit (TCCR2A, WGM20); // Set rightmost wgm bit setbit (TCCR2A, WGM21); // set middle wgm bit // See page 156 of the datasheet for the following register and bit clearbit (TCCR2B, WGM22); // set leftmost WGM bit (note that this bit is actually in a different register - TCCR2B) // MS: This combination of the three wWGM bits selects fast pwm (see @age 155 of datasheet) // Timer2 Clock Prescaler to : 1 // See page 156 of ATMEGA168 datasheet setbit (TCCR2B, CS20); // Sets the "no prescaling" bit clearbit (TCCR2B, CS21); // Turns off the other two bits which control clearbit (TCCR2B, CS22);// prescaling! Timer is now at its highest frequency // cli(); // Disable all interrupts. Below we turn back on the one we want // See page 105 (for timer 0) and 157 (for timer 2) of ATMEGA168 datasheet clearbit (TIMSK0,TOIE0); // Make sure timer 0 overflow interrupt is cleared setbit (TIMSK2,TOIE2); // enable Timer2 Interrupt } void loop() { while (!f_sample) { // Wait for a sample from the spectrum signal } //PORTD = PORTD | 128; // Test Output on pin 7 f_sample=false; err = ((phase * (Vatmod1 - Vatmod0))>0) ? 0 : 1; if (err==true) {PORTD = B10000000 | PORTD; // Set error bit high, leave other ports the same } else {PORTD = B01111111 & PORTD; // Set error bit low, leave other bits the same } } // The name stands for "TIMER 2 OVerFlow VECTor". See also list of interrupt vectors, // page 60 ATMEGA168 datasheet ISR(TIMER2_OVF_vect) { // PORTB = PORTB | 1 ; div32=!div32; // We divide our time between toggling the modulate // pin and sampling. if (div32){ div16=!div16; // if (div16) { Vatmod0=ADCH; // Get the next analog to digital conversion value. See page 257 of datasheet. // ADCH is the higher 8 bits of the ADC register which is an 8 bit digitisation of // the input analog signal PORTD = PORTD ^ B01000000; // Toggle modulation output for next reading } else { Vatmod1=ADCH; // Same as above but sample at the higher value of the current modulation PORTD = PORTD ^ B01000000; // Toggle modulation output back to 0 f_sample=true; } ibb++; ibb--; ibb++; ibb--; // Delay // See page 255 of the datasheet for info on the following register and bit. // As the original comment said, setting the ADSC bit of the ADCSRA register begins the next conversion // of the input analog signal to an 8 bit register value. setbit(ADCSRA,ADSC); } }