In a recent Ebay China toy order I grabbed four HY-SRF05 ultrasonic rangefinder sensors to play with. Can't beat the price at $1.89 USD each with free shipping. They'd be dirt cheap at twice that price.
Here I'm controlling the SRF05 with a PIC18F2680 and displaying the results on my "Predko" 2-wire 16x2 LCD. The scope is triggering on the start of the SRF05 measurement pulse so I can watch the pulse width change as the SRF05 measures different distances.
This is a nice easy to use, and very accurate sensor! It does get some bogus readings sometimes, like any rangefinding sensor, but most of the time it's very good.
At first I was having trouble with my interrupt code, so I wrote this super simple program to activate and read the sensor without interrupts. It works fine, but won't be useful for much except as a demo.
I know my lcd_16number() function isn't very good. It's a thing I wrote long ago, before I learned better ways of doing that. I just quickly found it and plugged it in here - couldn't find my better function and was too lazy to write one. It should be replaced with something more efficient and elegant for sure.
(scroll way down for working interrupt code)
#include <system.h>
#pragma DATA _CONFIG1H, _OSC_HS_1H
#pragma DATA _CONFIG2H, _WDT_OFF_2H
#pragma DATA _CONFIG3H, _MCLRE_ON_3H
#pragma DATA _CONFIG4L, _LVP_OFF_4L & _XINST_OFF_4L
#pragma CLOCK_FREQ 20000000
//function prototypes
void lcd_cls();
void lcd_string(char *);
void lcd_cmd(unsigned char);
void lcd_char(char);
void e_togg(void);
void lcd_init(void);
void lcd_nybble(unsigned char,unsigned char);
void lcd_16number(int);
//LCD defines
#define line1 lcd_cmd(0x80);
#define line2 lcd_cmd(0xc0);
#define CLOCK latc.1
#define DATA latc.2
//SRF05 defines
#define TRIG latb.0
#define ECHO portb.4
unsigned int time = 0;
bit flag = 0;
char string[] = " ";
char number[] = " inches ";
void main()
{
osccon = 0b01110000; //20MHz external osc
adcon1 = 0b00001111; //all digital
trisa = trisc = 0;
latc = 0;
trisb = 0b00010000; //RB4 is ECHO input
TRIG = 0;
t1con = 0b00110000; //set up Timer1, 1:8, not enabled
lcd_init();
lcd_string("SRF05 range");
delay_s(1);
while(1){
tmr1h = 0; //zero Timer1
tmr1l = 0;
TRIG = 1; //activate SRF05
delay_10us(1);
TRIG = 0;
while(ECHO == 0){ //wait for pulse start
}
t1con.TMR1ON = 1; //enable Timer1
while(ECHO == 1){ //wait for pulse end
}
t1con.TMR1ON = 0; //stop Timer1
MAKESHORT(time,tmr1l,tmr1h); //read value of Timer1
time = time / 37; //convert to inches
lcd_16number(time); //convert Timer1 to ASCII
line2;
lcd_string(number); //and display it
delay_ms(100);
}
}
void lcd_16number(int num)
{
number[0]=(char)(abs(num/10000)+0x30);
num=num-abs(num/10000)*10000;
number[1]=(char)(abs(num/1000)+0x30);
num=num-abs(num/1000)*1000;
number[2]=(char)(abs(num/100)+0x30);
num=num-abs(num/100)*100;
number[3]=(char)(abs(num/10)+0x30);
num=num-abs(num/10)*10;
number[4]=(char)(num+0x30);
}
//*****************
//* lcd functions *
//*****************
void lcd_cls()
{
line1;
lcd_string(" ");
line2;
lcd_string(" ");
}
void lcd_string(char *senpoint)
{
while(*senpoint != '\0')
{
lcd_char(*senpoint);
senpoint++;
}
}
void lcd_cmd(unsigned char letter)
{
unsigned char temp;
temp = letter;
temp = temp>>4;
lcd_nybble(temp,0);
temp = letter;
temp = temp&0x0f;
lcd_nybble(temp,0);
}
void lcd_char(unsigned char letter)
{
unsigned char temp;
temp = letter;
temp = temp >> 4;
lcd_nybble(temp,1);
temp = letter;
temp = temp & 0x0f;
lcd_nybble(temp,1);
}
void lcd_nybble(unsigned char nyb,unsigned char rs)
{
int i;
DATA = 0; //clear the 174
for(i=0;i<6;i++){ //repeat for 6 bits
CLOCK=1;delay_us(50);CLOCK=0; //write 0's to the 174
}
DATA=1; //output the AND value
CLOCK=1;delay_us(50);CLOCK=0;
DATA=rs; //output the RS bit value
CLOCK=1;delay_us(50);CLOCK=0;
for(i=0;i<4;i++){ //output the nybble
if((nyb & 0x08) != 0)
DATA=1;
else
DATA=0;
CLOCK=1;delay_us(50);CLOCK=0;
nyb=nyb<<1;
}
e_togg();
}
void lcd_init(void)
{
delay_ms(250);
lcd_nybble(0x03,0);
delay_ms(5);
e_togg();
delay_us(160);
e_togg();
delay_us(160);
lcd_nybble(0x02,0);
delay_us(160);
lcd_cmd(0x28); //set 4-bit mode and 2 lines
delay_us(160);
lcd_cmd(0x10); //cursor move & shift left
delay_us(160);
lcd_cmd(0x06); //entry mode = increment
delay_us(160);
lcd_cmd(0x0d); //display on - cursor blink on
delay_us(160);
lcd_cmd(0x01); //clear display
delay_ms(30);
}
void e_togg(void)
{
DATA=1;
delay_us(50);
DATA=0;
}
And here is some much more useful code using the RB port change interrupt. Both programs use Timer1 to measure the SRF05's pulse width.
#include <system.h>
#pragma DATA _CONFIG1H, _OSC_HS_1H
#pragma DATA _CONFIG2H, _WDT_OFF_2H
#pragma DATA _CONFIG3H, _MCLRE_ON_3H
#pragma DATA _CONFIG4L, _LVP_OFF_4L & _XINST_OFF_4L
#pragma CLOCK_FREQ 20000000
//function prototypes
void lcd_cls();
void lcd_string(char *);
void lcd_cmd(unsigned char);
void lcd_char(char);
void e_togg(void);
void lcd_init(void);
void lcd_nybble(unsigned char,unsigned char);
void lcd_16number(int);
//LCD defines
#define line1 lcd_cmd(0x80);
#define line2 lcd_cmd(0xc0);
#define CLOCK latc.1
#define DATA latc.2
//SRF05 defines
#define TRIG latc.3
#define ECHO portb.4
int time = 0;
bit flag = 0;
char string[] = " ";
char number[] = " inches ";
void main()
{
osccon = 0b01110000; //20MHz external osc
adcon1 = 0b00001111; //all digital
rcon.IPEN = 0; //disable priority interrupts
trisa = trisc = 0; //set PortA & C all outs
trisb = 0b00010000; //RB4 is ECHO input
t1con = 0b00110000; //set up Timer1, 1:8, not enabled
lcd_init(); //display title
lcd_string("SRF05 range:");
time = portb; //probably not needed
intcon.RBIE = 1; //enable PortB interrupt on change
intcon.GIE = 1; //enable global interrupts
while(1){
TRIG = 1; //activate SRF05
delay_us(11);
TRIG = 0;
delay_ms(100); //read SRF05 ten times/second
MAKESHORT(time,tmr1l,tmr1h); //write value of Timer1 to time
time = time / 38; //convert to inches
lcd_16number(time); //convert Timer1 to ASCII
line2;
lcd_string(number); //and display it
}
}
void interrupt(void){
if(intcon.RBIF){
if(ECHO == 1){ //if ECHO is high then
tmr1h = 0; //zero out Timer1
tmr1l = 0;
t1con.TMR1ON = 1; //enable Timer1 and wait for ECHO to go low
time = portb; //read PortB to clear mismatch
intcon.RBIF = 0; //clear interrupt flag
}
else{ //when ECHO goes low
t1con.TMR1ON = 0; //stop Timer1
time = portb; //read PortB to clear mismatch
intcon.RBIF = 0; //clear interrupt flag
}
}
}
void lcd_16number(int num)
{
number[0]=(char)(abs(num/10000)+0x30);
num=num-abs(num/10000)*10000;
number[1]=(char)(abs(num/1000)+0x30);
num=num-abs(num/1000)*1000;
number[2]=(char)(abs(num/100)+0x30);
num=num-abs(num/100)*100;
number[3]=(char)(abs(num/10)+0x30);
num=num-abs(num/10)*10;
number[4]=(char)(num+0x30);
}
//*****************
//* lcd functions *
//*****************
void lcd_cls()
{
line1;
lcd_string(" ");
line2;
lcd_string(" ");
}
void lcd_string(char *senpoint)
{
while(*senpoint != '\0')
{
lcd_char(*senpoint);
senpoint++;
}
}
void lcd_cmd(unsigned char letter)
{
unsigned char temp;
temp = letter;
temp = temp>>4;
lcd_nybble(temp,0);
temp = letter;
temp = temp&0x0f;
lcd_nybble(temp,0);
}
void lcd_char(unsigned char letter)
{
unsigned char temp;
temp = letter;
temp = temp >> 4;
lcd_nybble(temp,1);
temp = letter;
temp = temp & 0x0f;
lcd_nybble(temp,1);
}
void lcd_nybble(unsigned char nyb,unsigned char rs)
{
int i;
DATA = 0; //clear the 174
for(i=0;i<6;i++){ //repeat for 6 bits
CLOCK=1;delay_us(50);CLOCK=0; r //write 0's to the 174
}
DATA=1; //output the AND value
CLOCK=1;delay_us(50);CLOCK=0;
DATA=rs; //output the RS bit value
CLOCK=1;delay_us(50);CLOCK=0;
for(i=0;i<4;i++){ //output the nybble
if((nyb & 0x08) != 0)
DATA=1;
else
DATA=0;
CLOCK=1;delay_us(50);CLOCK=0;
nyb=nyb<<1;
}
e_togg();
}
void lcd_init(void)
{
delay_ms(250);
lcd_nybble(0x03,0);
delay_ms(5);
e_togg();
delay_us(160);
e_togg();
delay_us(160);
lcd_nybble(0x02,0);
delay_us(160);
lcd_cmd(0x28); //set 4-bit mode and 2 lines
delay_us(160);
lcd_cmd(0x10); //cursor move & shift left
delay_us(160);
lcd_cmd(0x06); //entry mode = increment
delay_us(160);
lcd_cmd(0x0d); //display on - cursor blink on
delay_us(160);
lcd_cmd(0x01); //clear display
delay_ms(30);
}
void e_togg(void)
{
DATA=1;
delay_us(50);
DATA=0;
}