Cheap Ultrasonic Range Finder
A PIC ultrasonic distance sensor for embedded projects
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.