A simple ultrasonic range finder for embedded projects: presence detector, robotics, car parking.
Overview
A simple ultrasonic range finder for embedded projects: presence detector, robotics, car parking. With a few cheap components and less than 200 bytes of code, it works from 30 to 200 cm with approximately 1 cm accuracy.
How it Works
Sound travels at approximately 340 m/s in air. The principle is simple: send a 40 KHz ultrasonic pulse, then listen for the echo. The time between transmission and reception gives you the distance.
The PWM output at 40 KHz drives the ultrasonic transmitter via a transistor and resonator circuit. The resonator circuit boosts the voltage to about 20V, giving a range of up to 200 cm. The receiver is connected directly to the PIC ADC input.
Circuit Schematic
- Decoupling capacitor for power supply filtering
- R1 resistor for transistor base drive
- 330 uH inductor in parallel with the piezo transmitter for resonance at 40 KHz
- D1 protection diode for voltage clamping
- R3 impedance adaptor for the receiver
C Source Code
/* * file : sonar.c * project : Simple UltraSonic Range Finder * compiler : mikroC V6.2 * date : september 30, 2006 * * description : * This is a basic ultrasonic range finder, from 30 to 200 centimeters * * target device : * PIC16F877A with 8 Mhz crystal * or any PIC with at least one ADC and PWM channel * * configuration bits : * HS clock, no watchdog, no power up timer, * no brown out, LVP disabled, ICD disabled * ******************************************************************************* */ /******************** * MACRO DEFINITIONS ********************/ #define PULSELEN 300 // ultra sonic pulse length in microseconds #define BUFSIZE 10 // circular buffer size for averaging #define LCDPORT PORTD #define LCDTRIS TRISD /******************* * GLOBAL VARIABLES *******************/ unsigned char outOfRange ; // out of range flag unsigned int buf[BUFSIZE] ; // samples buffer unsigned char idx = 0 ; // current sample index /***************************************** * INTERRUPT SERVICE ROUTINE * handles TIMER1 overflow only *****************************************/ void interrupt(void) { if(PIR1.TMR1IF) { outOfRange = 1 ; PIR1.TMR1IF = 0 ; } } /************ * MAIN LOOP ************/ void main() { ADCON1 = 0 ; // enables ADC TRISA = 0xff ; // PORTA as inputs PORTA = 0 ; TRISC = 0 ; // PORTC as outputs PORTC = 0 ; T1CON = 0b00001100 ; // prescaler 1:1, osc enabled, internal clk, stopped #ifdef LCDPORT Lcd_Init(&LCDPORT) ; Lcd_Cmd(Lcd_CLEAR) ; Lcd_Cmd(Lcd_CURSOR_OFF) ; Lcd_Out(1, 1, "UltraSonicRanger") ; Lcd_Out(2, 5, "cm") ; #endif // init PWM : 40 KHz, 50% duty cycle PWM1_Init(40000) ; PWM1_Change_Duty(128) ; INTCON.GIE = 1 ; INTCON.PEIE = 1 ; PIE1.TMR1IE = 0 ; PIR1.TMR1IF = 0 ; for(;;) { unsigned char i ; unsigned long cm ; unsigned char str[4] ; // prepare timer T1CON.TMR1ON = 0 ; outOfRange = 0 ; TMR1H = 0 ; TMR1L = 0 ; T1CON.TMR1ON = 1 ; // start timer 1 PIE1.TMR1IE = 1 ; // enable overflow interrupt // send pulse PWM1_Start() ; // pulse at ultrasonic frequency Delay_us(PULSELEN) ; // during PULSELEN microseconds PWM1_Stop() ; Delay_us(PULSELEN * 2) ; // wait to prevent false start while(Adc_Read(1) < 1) // while no echo detected { if(outOfRange) break ; // too late, out of range } T1CON.TMR1ON = 0 ; // stop timer 1 PIE1.TMR1IE = 0 ; #ifdef LCDPORT if(outOfRange) { Lcd_Out(2, 8, "OverRange") ; } else if(TMR1H < ((PULSELEN * 6 * Clock_kHz()) / (1000 * 4 * 256))) { Lcd_Out(2, 8, "UnderRnge") ; } else { buf[idx] = TMR1H ; // build 16-bit value from timer1 buf[idx] <<= 8 ; buf[idx] += TMR1L ; idx++ ; if(idx == BUFSIZE) { idx = 0 ; } cm = 0 ; for(i = 0 ; i < BUFSIZE ; i++) { cm += buf[i] ; } cm /= BUFSIZE ; // average samples /* * cm contains now the number of clock cycles * from start of ultrasonic transmission to first echo * duration in seconds: s = cm / (Clock_Khz() * 1000 / 4) * sound speed in air: 340 m/s * distance (forth and back): d = s * 340 * 100 / 2 * d = 34 * 2 * cm / Clock_Khz() */ cm *= 34 * 2 ; cm /= Clock_Khz() ; ByteToStr(cm, str) ; Lcd_Out(2, 1, str) ; Lcd_Out(2, 8, " ") ; } #endif Delay_ms(10) ; // 10 ms before next sample } }
Note
No separate project ZIP file was provided for this project. The source code above is the complete mikroC program — copy and paste it into your mikroC IDE to build the project.