(video demo of echo generation using atmega32)
Introduction:
Hi,
While I was studying at 10th standard, I used to play with small electronic circuits mostly based on transistor bc547 - bc557 pair. At that time, I just asked myself, I can amplify audio signals using few transistor combinations, but how can I make an echo effect which I used to hear in almost all loudspeaker announcements? I can't imagine what I can do with few transistors and resistors to make such an effect! I have no answer at that time because it was beyond my limitation..
But now I can do this very easily by a simple digital signal processing using a microcontroller. It's concept is very simple, ie we need to apply a proper delayed feedback in digital samples with in a circular buffer. I did this using an atmega32 microcontroller and it worked fine. This is simple but really an interesting project. Not only an echo, but we can do a lot of fun with this type of small DSP experiments if we have considerably large RAM in the mcu...
I am using an Atmega32 microcontroller for the purpose. It is having RAM of 2KB and an ADC which is enough for demonstrating the concept of echo generation. An electert mic is used for capturing the voice. It is introduced to ADC with proper amplification and level shifting, which are more critical for the perfect operation. Now the ADC module inside the AVR will convert the analog signal to digital signal at a particular sampling rate(which we can decide). Now a 1900byte circular buffer is introduced. Our first aim is to make a delay in input and output audio. So, we can do one thing, ie we can populate the buffer from one end and we can read the buffer from another point so that there will be a delay! So how to make this delay to it's maximum? I think it will be better to explain it via a diagram.
uint8_t buf[1900];
Now, the next process is to make echo effect. For this,we need to provide a feedback from reading point to writing point. In simple words, we need to add a scaled version of reading signal along with the writing sample. The feedback gain should always be <1. So, now the digital signal in the buffer is processed by adding an echo effect. We can provide the reading samples to PWM of AVR to get the pulse width modulated signal. This could be demodulated by using a simple RC filter and can be provided to a power amplifier and we can notice an echo effect in the audio...
In my demo code, I also provided a 5 level echo selection via external push button, ie echo 0 to echo 4.
We can do a lot of adjustments here.
Observation:
- If we increase the sampling rate, the echo time gap and echo duration will be reduced but the audio quality will be increased.
- If we are decreasing the sampling rate, the echo time gap and echo duration will be increased.
- If we increase the feedback gain (but always < 1), the echo volume and echo number will be increased..
- If we increase the feedback gain above 1, a totally disturbed audio is obtained due to oscillation.
- To obtain a very good audio quality with good echo effect, both the echo buffer (RAM) and the sampling rate should be high..... Atmega2560 may be best for the purpose because it have 8KB RAM.
Circuit diagram:
Now, for an audio amplifier, just search in google, we will get a lot of circuits which suits for our speaker specifications. So, I am not putting the diagram here. Still couldn't find it, just mail me. I will suggest suitable amplifier for your speaker.
Source code:
#include <avr/io.h>To compile it in linux, check my previous post of the atmega32 based wav player. I have explained it in detail about the command line, the makefile etc...
#define F_CPU 16000000
#include <util/delay.h>
uint8_t buf[1900];
void pwm_init();
void adc_init();
uint16_t adc_read();
void switch_enable();
void main()
{
int i,j;
int8_t echo_level = 4;
uint16_t rd;
adc_init();
pwm_init();
switch_enable();
i = 0;j=1;
while(1) {
rd = adc_read() + buf[j]*echo_level;
rd/=8;
if(rd>255)rd=255;
_delay_us(90);
buf[i] = rd;
OCR0 = rd;
if(!(PINB & (1<<PB1))) {
if(echo_level < 4) {
echo_level++;
_delay_ms(300);
}
} else if(!(PINB & (1<<PB2))) {
if(echo_level > 0) {
echo_level--;
_delay_ms(300);
}
}
i++;
if(i==1899)i=0;
j++;
if(j==1899)j=0;
}
}
void adc_init()
{
ADMUX = 0b11000000;
ADCSRA =0b10000010;
}
void switch_enable()
{
DDRB &= ~((1<<PB1)|(1<<PB2));
PORTB = (1<<PB1)|(1<<PB2);
}
uint16_t adc_read()
{
uint16_t retl,reth;
ADCSRA |= 1<<ADSC;
while(!ADIF);
ADCSRA |= 1<<ADIF;
retl = ADCL;
reth = ADCH;
reth<<=8;
reth|=retl;
return reth;
}
void pwm_init()
{
TCCR0|=(1<<WGM00)|(1<<WGM01)|(1<<COM01)|(1<<CS00);
DDRB|=(1<<PB3);
}
Here is the hex for lazy guys...;-)
:100000000C942A000C943C000C943C000C943C0092
:100010000C943C000C943C000C943C000C943C0070
:100020000C943C000C943C000C943C000C943C0060
:100030000C943C000C943C000C943C000C943C0050
:100040000C943C000C943C000C943C000C943C0040
:100050000C943C0011241FBECFE5D8E0DEBFCDBF1D
:1000600017E0A0E6B0E001C01D92AC3CB107E1F79B
:100070000E943E000C94DB000C9400001F93CF9371
:10008000DF9380EC87B982E886B983B7896683BF3E
:10009000BB9A87B3897F87BB86E088BB14E041E0C9
:1000A00050E020E030E0A4E0B0E0369A349A64B149
:1000B00085B1F82FE0E0EA01C05ADF4FC881D0E0F7
:1000C000CA9FC001CB9F900DDA9F900D112470E064
:1000D0006E2B7F2B860F971F969587959695879504
:1000E00096958795C7E6D1E02197F1F700C000000B
:1000F0008F3F910519F010F08FEF90E0E901C05AA1
:10010000DF4F88838CBFB19911C0143051F52F5F38
:100110003F4F97E02B36390709F14F5F5F4FC7E03C
:100120004B365C0711F640E050E0BFCFB299EFCFFD
:1001300011166CF711508FEF95EAAEE081509040A8
:10014000A040E1F700C00000A12FBB27A7FDB0959C
:100150002F5F3F4F97E02B363907F9F620E030E06C
:10016000DCCF1F5F8FEF95EAAEE081509040A0405A
:10017000E1F700C00000A12FBB27A7FDB095C7CFB6
:1001800080EC87B982E886B9089587B3897F87BBF9
:1001900086E088BB0895369A349A24B135B1932FFE
:1001A00080E030E0282B392BC901089583B7896698
:0A01B00083BFBB9A0895F894FFCFB7
:00000001FF
If you have any questions, you can ask me at [vinodstanur at gmail dot com]