One of the biggest problems with small robots and other embedded systems projects is that the sensor interface - or driver as some call it - may take up a significant portion of the main processors memory. Such was the example, in our recent highly sophisticated home plant watering autonomous robot I ran out of program memory many times and had to compromise the software to get it all to fit. The addition of sensor drivers along with their attendant math libraries took up to 40% of the ROM. Here we off load all the sensor processing onto another small inexpensive processor, and it communicates with the main processor using 9600 kb RS232 serial communication.
A very important feature of the reception of the 232 data is to accommodate what happens if the sensor is disconnected and the data flow terminated. Most 232 operations will seize up the main processor if this happens, as it will try to wait forever stuck in a loop for the data that will never arrive. Here Ive used the khbit function on the receive end to sense the data input, and skip over it in half a second if it does not appear, thus not locking up the processor. This is important especially when you have dozens of remote sensors!
Complete bench setup to develop and test the Sonar Chip. The 16F887 master processor to receive the data is on top on a proto board running at 10mhz. Below is the SRF04 sonar, sonar chip board with 16F628a chip and a New Haven transreflective LCD to display the results from the master processor as received on the lower right. (Jameco - $17)
Close up of the sonar assembly, and the new sonar chip board, it runs at 10mhz as well to ensure accurate RS232 transmission.
By VERY carefully moving a wood panel target in hair fine increments back and forth, I was able to get the output to read 4.00 inches. You can nearly resolve individual sheets of paper with this thing, and would be perfect for counting stacks of coins, or on a production line for qualification testing. the maximum range is bout 80 inches, again with the same .01 inch accuracy throughout the entire range.
The actual point to measure from is .1" in from the end of the sonar tube, inside the metal screen where the speaker cones are located. That is the source of the ultrasonic sound.
Schematic of sonar chip, click to enlarge! Code for Sonar PIC Chip
Schematic of the master processor, click to enlarge. Code for Master Processor PIC Chip
//****************************************************************************
//Chris Schur
//(Sonar Chip 16F628)
//Date: 8/23/15
//****************************************************************************/*Description of this Program:
This program sends raw serial data at 9600kb out to be reciveved by the main
processor every 100ms. */
//I/O Designations ---------------------------------------------------
// RA0: sonar TRIG Output
// RA1: sonar Pulse Input
// RA2:
// RA3:
// RA4: (Open Collector output)
// RA5: (MCLR)
// RA6: Xtal Output
// RA7: Xtal Input// RB0: rs232 output to main uC
// RB1: GREEN LED output
// RB2: BLUE LED output
// RB3:
// RB4:
// RB5:
// RB6:
// RB7:
//--------------------------------------------------------------------
//Include Files:
#include <16F628.h> //Normally chip, math, etc. used is here.//Directives and Defines:
//#device ADC=10 //Set ADC when used to 10 bit = 0 - 1023
#fuses NOPROTECT,HS,NOWDT //xtal is used
#use delay(crystal=10MHz) //xtal speed
#use fast_io(ALL) //must define tris below in main when using this//for LCD:
//#use rs232(baud=9600, xmit=Pin_B3, bits=8, parity=N,stream=SERIALNH)
//for data out to main processor:
#use rs232(baud=9600, xmit=Pin_B0, bits=8, parity=N,STREAM=SONAR)
//****************************************************************************
//Global Variables:
int16 sonarmeas = 0; //distance in inches from internal horns
float sonardist = 0;
int highbyte,lowbyte; //high byte and low byte for serial data//****************************************************************************
//Functions/Subroutines, Prototypes:int16 sonar1 (int16 sonarpulse, int16 sonartime, int16 d); //get sonar dist.
//****************************************************************************
//-- Main Program
//****************************************************************************void main(void) {
// Set TRIS I/O directions, define analog inputs, compartors:
set_tris_A(0b01111110);
set_tris_B(0b11110000);
//(analog inputs digital by default)//Initialize variables and Outputs: --------------------------------------
output_low(Pin_B0); //status off
output_low(Pin_B1);
output_low(Pin_B2);
output_low(Pin_B3);
//Setup for timers, PWM, and other peripherals:
//----------------------------------------------------------------//MAIN LOOP:
while (true) {
//get sonar current dist:
sonarmeas = sonar1(0,0,0); //call up sonar1 function, variables start at 0
//sonardist = (float) sonarmeas / 155; //convert to inches-change typedef//Next send the raw measurement data out serially:
//Convert to two 8 bit integers to send:
lowbyte = MAKE8(sonarmeas,0);
highbyte = MAKE8(sonarmeas,1);delay_ms(100); //space between trig pulses
//trig pip for oscilloscope to see data clearly
output_high(Pin_B2);
delay_us(10);
output_low(Pin_B2);//send data serially:
fputc(lowbyte,SONAR);
//delay_ms(5);
fputc(highbyte,SONAR);
//delay_ms(10);
}
}//********* Functions which have prototypes at top of program ****************
int16 sonar1 (int16 sonarpulse, int16 sonartime, int16 d) {setup_timer_1( T1_INTERNAL | T1_DIV_BY_2 );//must be inside function!!!!!
//max 52.6ms = 65000 x .8uS res per count
output_high(Pin_A0); //send trig pulse
delay_us(10);
output_low(Pin_A0);delay_us(200); //this delay is added to end 70uS before the rising edge of echo
//start isr to time out if no echo:
SET_TIMER1(0); //reset to zero on timer counter.
//clear_interrupt (INT_TIMER1); //you must do this or it will have int allready.
//enable_interrupts (INT_TIMER1); //turn on TIMER1 interrupt//now look for L to H transition:
while (input(Pin_A1) == 0); //wait for 0 to 1//reset timer:
SET_TIMER1(0); //reset to zero on timer counter.//now look for H to L transition:
while (input(Pin_A1) == 1); //wait for 1 to 0//turn off isr:
//disable_interrupts (INT_TIMER1); //turn off TIMER1 interruptsonarpulse = get_timer1();
sonartime = sonarpulse * .8; //actual uS pulse widthif (sonartime > 18000) //max range is 18ms = 116" = 9.7 feet.
sonartime = 18000; //set to max if beyond range.
//d = sonartime / 155; //18000/155= 116;return sonartime;
}
HOME
//****************************************************************************
//Chris Schur
//(sonar recieve 16F887)
//Date: 8/28/15
//****************************************************************************/*Description of this Program:
This short program recieves the 9600 kb serial data from the sonar chip and converts
it to a distance in inches with two decimal places, then sends it to the serial LCD.*/
//I/O Designations ---------------------------------------------------
// RA0: (AN0) Sonar serial data input
// RA1: (AN1)
// RA2: (AN2)
// RA3: (AN3)
// RA4: (Open Collector output)
// RA5: (AN4)
// RA6: OSC OUT
// RA7: OSC IN// RB0: (AN12,EXT INT) Status LED output
// RB1: (AN10) LCD output
// RB2: (AN8)
// RB3: (AN9)
// RB4: (AN11)
// RB5: (AN13)
// RB6:
// RB7:// RC0:
// RC1:
// RC2:
// RC3: (SCLK)
// RC4: (SDA)
// RC5:
// RC6:
// RC7:// RD0:
// RD1:
// RD2:
// RD3:
// RD4:
// RD5:
// RD6:
// RD7:// RE0: (AN5)
// RE1: (AN6)
// RE2: (AN7)
// RE3: (MCLR INPUT - Pull High)//--------------------------------------------------------------------
//Include Files:
#include <16F887.h> //Normally chip, math, etc. used is here.//Directives and Defines:
#device ADC=10 //Set ADC when used to 10 bit = 0 - 1023
#fuses NOPROTECT,HS,NOWDT //xtal is used
#use delay(crystal=10MHz) //xtal speed
#use fast_io(ALL) //must define tris below in main when using this//for LCD:
#use rs232(baud=9600, xmit=Pin_B1, bits=8, parity=N,stream=SERIALNH)//For reciving sonar data on pin A0:
#use rs232(baud=9600, rcv=Pin_A0, bits=8, parity=N, DISABLE_INTS,stream=SONAR)
//Note: the "disable_ints is critical, turns off constant barrage of interrupts
//during the aquisition of data in the get command so timing is not affected.//NO_ANALOGS is default in device file...(All I/Os are digital)
//***Global Variables:********************************************************
int16 sonarmeas = 0; //distance in inches from internal horns
float sonardist = 0;
int highbyte,lowbyte; //high byte and low byte for serial data
int1 sonarbit; //incoming data verification bit
int16 sonartimeout; //counts of kbit delay increrments//***Functions/Subroutines, Prototypes:***************************************
//Clears LCD Display:
void LCDCLR() {
fputc(0xFE,SERIALNH); //Command Prefix
fputc(0x51,SERIALNH); //Clear screen
}//Sets LCD to line 2 start point
void LCDLN2() {
fputc(0xFE,SERIALNH); //Command Prefix
fputc(0x45,SERIALNH); //set cursor command
fputc(0x40,SERIALNH); //Set cursor to next line, pos 40 = start line 2
}void GETSONAR(void);
//****************************************************************************
//***-- Main Program*********************************************************void main(void) {
// Set TRIS I/O directions, define analog inputs, compartors:
set_tris_A(0b10111111);
set_tris_B(0b11111100);
set_tris_C(0b11111111);
set_tris_D(0b11111111);
set_tris_E(0b1111);//Initialize variables and Outputs: --------------------------------------
//Setup for timers, PWM, and other peripherals:
//----------------------------------------------------------------
//Main Program
while (true) {
GETSONAR(); //go get sonar data serially
LCDCLR(); //clear screen
delay_ms(10);
fprintf(SERIALNH,"%f"sonardist); //NOTE: %Lu in command is int16 only output
delay_ms(250);
output_high(Pin_B0);
delay_ms(25);
output_low(Pin_B0);
delay_ms(25);} //while
} //main//********* Functions which have prototypes at top of program ****************
void GETSONAR(void) {
//test loop to see if data has come in lasting maximum of 1/2 second:sonartimeout = 0; //reset timeout
while (!kbhit(SONAR) && (++sonartimeout < 50000)) //half second wait IF NO SIGNAL
delay_us(10);
if (kbhit(SONAR)) { //We have a signal
lowbyte = fgetc(SONAR);
highbyte = fgetc(SONAR);
sonarmeas = MAKE16(highbyte,lowbyte); //RAW
sonardist = (float) sonarmeas / 155; //inches with 2 decimals
}
}