Leave messages below
의뢰는 http://instructables.tistory.com/64로 남겨주세요
https://electrosome.com/hc-sr04-ultrasonic-sensor-pic/
HC-SR04는 4핀짜리 Ultra Sonic Sensor이다.
여기서는 MikroC로 짠 코드와 MPLAB으로 짠 코드를 비교할 것이다.
▼Proteus ISIS 추가용 라이브러리 (HC-SR04) (☞출처)
Ultrasonic Sensor Library for Proteus.rar
▼Proteus ISIS용 HC-SR04 ---PIC칩용 (☞출처)
▼Proteus, Hyperterminal & Virtual Port
https://www.youtube.com/watch?v=gwkju6f2Twk
▼Google에서 Virtual Port Emulator라고 검색
http://www.eterlogic.com/Products.VSPE.html
▼MikroC
// LCD module connections
sbit LCD_RS at RD2_bit;
sbit LCD_EN at RD3_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;
sbit LCD_RS_Direction at TRISD2_bit;
sbit LCD_EN_Direction at TRISD3_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;
// End LCD module connections
void main()
{
int a;
char txt[7];
Lcd_Init();
Lcd_Cmd(_LCD_CLEAR); // Clear display
Lcd_Cmd(_LCD_CURSOR_OFF); // Cursor off
TRISB = 0b00010000; //RB4 as Input PIN (ECHO)
Lcd_Out(1,1,"Developed By");
Lcd_Out(2,1,"electroSome");
Delay_ms(3000);
Lcd_Cmd(_LCD_CLEAR);
T1CON = 0x10; //Initialize Timer Module
while(1)
{
TMR1H = 0; //Sets the Initial Value of Timer
TMR1L = 0; //Sets the Initial Value of Timer
PORTB.F0 = 1; //TRIGGER HIGH
Delay_us(10); //10uS Delay
PORTB.F0 = 0; //TRIGGER LOW
while(!PORTB.F4); //Waiting for Echo
T1CON.F0 = 1; //Timer Starts
while(PORTB.F4); //Waiting for Echo goes LOW
T1CON.F0 = 0; //Timer Stops
a = (TMR1L | (TMR1H<<8)); //Reads Timer Value
a = a/58.82; //Converts Time to Distance
a = a + 1; //Distance Calibration\
if(a>=2 && a<=400) //Check whether the result is valid or not
{
IntToStr(a,txt);
Ltrim(txt);
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1,1,"Distance = ");
Lcd_Out(1,12,txt);
Lcd_Out(1,15,"cm");
}
else
{
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1,1,"Out of Range");
}
Delay_ms(400);
}
}▼MPLAB XC8
#define _XTAL_FREQ 8000000
#define RS RD2
#define EN RD3
#define D4 RD4
#define D5 RD5
#define D6 RD6
#define D7 RD7
#include <xc.h>
#include "lcd.h";
#include <pic16f877a.h>
// BEGIN CONFIG
#pragma config FOSC = HS
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config BOREN = ON
#pragma config LVP = OFF
#pragma config CPD = OFF
#pragma config WRT = OFF
#pragma config CP = OFF
//END CONFIG
void main()
{
int a;
TRISB = 0b00010000; //RB4 as Input PIN (ECHO)
TRISD = 0x00; // LCD Pins as Output
Lcd_Init();
Lcd_Set_Cursor(1,1);
Lcd_Write_String("Developed By");
Lcd_Set_Cursor(2,1);
Lcd_Write_String("electroSome");
__delay_ms(3000);
Lcd_Clear();
T1CON = 0x10; //Initialize Timer Module
while(1)
{
TMR1H = 0; //Sets the Initial Value of Timer
TMR1L = 0; //Sets the Initial Value of Timer
RB0 = 1; //TRIGGER HIGH
__delay_us(10); //10uS Delay
RB0 = 0; //TRIGGER LOW
while(!RB4); //Waiting for Echo
TMR1ON = 1; //Timer Starts
while(RB4); //Waiting for Echo goes LOW
TMR1ON = 0; //Timer Stops
a = (TMR1L | (TMR1H<<8)); //Reads Timer Value
a = a/58.82; //Converts Time to Distance
a = a + 1; //Distance Calibration
if(a>=2 && a<=400) //Check whether the result is valid or not
{
Lcd_Clear();
Lcd_Set_Cursor(1,1);
Lcd_Write_String("Distance = ");
Lcd_Set_Cursor(1,14);
Lcd_Write_Char(a%10 + 48);
a = a/10;
Lcd_Set_Cursor(1,13);
Lcd_Write_Char(a%10 + 48);
a = a/10;
Lcd_Set_Cursor(1,12);
Lcd_Write_Char(a%10 + 48);
Lcd_Set_Cursor(1,15);
Lcd_Write_String("cm");
}
else
{
Lcd_Clear();
Lcd_Set_Cursor(1,1);
Lcd_Write_String("Out of Range");
}
__delay_ms(400);
}
}-----------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------
I hope that you can understand the working of the above program through comments.
If you have any doubts, just comment below.
Distance Calibration
For accurate distance measuring you may calibrate the obtained result. Here for making the displayed distance more accurate, I added 1 to the the measured distance. This constant of calibration can be find using a series of practical experiments with a ruler scale.
In the above program there is a small problem as it infinitely waiting for ECHO and to ECHO goes LOW. If HIGH echo pulse is not received due to any reason, the program may get stuck there. So it is not a good practice to infinitely wait for ECHO and to ECHO goes LOW. The best solution is to use PORTB On-Change Interrupt feature of PIC Microcontroller.
PORTB On-Change Interrupt
Four pins of PORTB (RB4 – RB7) have interrupt on-change feature.
포트B(RB4~RB7) 는 인터럽트 on-change 특성을 갖고있다.
Only those pins configured as INPUT PIN can cause this Interrupt while this interrupt is enabled.
이런 입력핀들만이 인터럽트를 유발할 수 있다. while 이 인터럽트가 enabled된 동안에
If the Logic State of any of these four pin changes (only Input Pins), interrupt will be generated.
만약 이 4개핀(PB4~PB7)이 바뀌었으면, 인터럽트가 발생할 것이다.
PORTB On-Change Interrupt can be enabled by :
- Set Global Interrupt Enable bit : INTCON.GIE = 1
- Set PORTB On-Change Interrupt Enable Bit : INTCON.RBIE = 1
다음 조건하에 PortB의 On-Change 인터럽트는 인에이블될수 있다.
● INTCON안의 GIE비트가 1로 Set됬을때
● INTCON안의 RBIE비트가 1로 Set됬을때
Note : PORTB On-Change Interrupt flag (INTCON.RBIF) will be set whenever this interrupt is generated and it should be cleared in software
이제 인터럽트가 추가된 코드를 보자
▼MikroC (Interrupt Used)
// LCD module connections
sbit LCD_RS at RD2_bit;
sbit LCD_EN at RD3_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;
sbit LCD_RS_Direction at TRISD2_bit;
sbit LCD_EN_Direction at TRISD3_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;
// End LCD module connections
int a;
//Interrupt function will be automatically executed on Interrupt
void interrupt()
{
if(INTCON.RBIF == 1) //Makes sure that it is PORTB On-Change Interrupt
{
INTCON.RBIE = 0; //Disable On-Change Interrupt
if(PORTB.F4 == 1) //If ECHO is HIGH
T1CON.F0 = 1; //Start Timer
if(PORTB.F4 == 0) //If ECHO is LOW
{
T1CON.F0 = 0; //Stop Timer
a = (TMR1L | (TMR1H<<8))/58.82; //Calculate Distance
}
}
INTCON.RBIF = 0; //Clear PORTB On-Change Interrupt flag
INTCON.RBIE = 1; //Enable PORTB On-Change Interrupt
}
void main()
{
char txt[7];
Lcd_Init();
Lcd_Cmd(_LCD_CLEAR); // Clear display
Lcd_Cmd(_LCD_CURSOR_OFF); // Cursor off
TRISB = 0b00010000;
INTCON.GIE = 1; //Global Interrupt Enable
INTCON.RBIF = 0; //Clear PORTB On-Change Interrupt Flag
INTCON.RBIE = 1; //Enable PORTB On-Change Interrupt
Lcd_Out(1,1,"Developed By");
Lcd_Out(2,1,"electroSome");
Delay_ms(3000);
Lcd_Cmd(_LCD_CLEAR);
T1CON = 0x10; //Initializing Timer Module
while(1)
{
TMR1H = 0; //Setting Initial Value of Timer
TMR1L = 0; //Setting Initial Value of Timer
a = 0;
PORTB.F0 = 1; //TRIGGER HIGH
Delay_us(10); //10uS Delay
PORTB.F0 = 0; //TRIGGER LOW
Delay_ms(100); //Waiting for ECHO
a = a + 1; //Error Correction Constant
if(a>2 && a<400) //Check whether the result is valid or not
{
IntToStr(a,txt);
Ltrim(txt);
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1,1,"Distance = ");
Lcd_Out(1,12,txt);
Lcd_Out(1,15,"cm");
}
else
{
Lcd_Cmd(_LCD_CLEAR);
Lcd_Out(1,1,"Out of Range");
}
Delay_ms(400);
}
}▼MPLAB XC8 (Interrupt Used)
#define _XTAL_FREQ 8000000
#define RS RD2
#define EN RD3
#define D4 RD4
#define D5 RD5
#define D6 RD6
#define D7 RD7
#include <xc.h>
#include "lcd.h";
#include <pic16f877a.h>
// BEGIN CONFIG
#pragma config FOSC = HS
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config BOREN = ON
#pragma config LVP = OFF
#pragma config CPD = OFF
#pragma config WRT = OFF
#pragma config CP = OFF
//END CONFIG
int a;
void interrupt echo()
{
if(RBIF == 1) //Makes sure that it is PORTB On-Change Interrupt
{
RBIE = 0; //Disable On-Change Interrupt
if(RB4 == 1) //If ECHO is HIGH
TMR1ON = 1; //Start Timer
if(RB4 == 0) //If ECHO is LOW
{
TMR1ON = 0; //Stop Timer
a = (TMR1L | (TMR1H<<8))/58.82; //Calculate Distance
}
}
RBIF = 0; //Clear PORTB On-Change Interrupt flag
RBIE = 1; //Enable PORTB On-Change Interrupt
}
void main()
{
TRISB = 0b00010000; //RB4 as Input PIN (ECHO)
TRISD = 0x00; // LCD Pins as Output
GIE = 1; //Global Interrupt Enable
RBIF = 0; //Clear PORTB On-Change Interrupt Flag
RBIE = 1; //Enable PORTB On-Change Interrupt
Lcd_Init();
Lcd_Set_Cursor(1,1);
Lcd_Write_String("Developed By");
Lcd_Set_Cursor(2,1);
Lcd_Write_String("electroSome");
__delay_ms(3000);
Lcd_Clear();
T1CON = 0x10; //Initialize Timer Module
while(1)
{
TMR1H = 0; //Sets the Initial Value of Timer
TMR1L = 0; //Sets the Initial Value of Timer
RB0 = 1; //TRIGGER HIGH
__delay_us(10); //10uS Delay
RB0 = 0; //TRIGGER LOW
__delay_ms(100); //Waiting for ECHO
a = a + 1; //Error Correction Constant
if(a>=2 && a<=400) //Check whether the result is valid or not
{
Lcd_Clear();
Lcd_Set_Cursor(1,1);
Lcd_Write_String("Distance = ");
Lcd_Set_Cursor(1,14);
Lcd_Write_Char(a%10 + 48);
a = a/10;
Lcd_Set_Cursor(1,13);
Lcd_Write_Char(a%10 + 48);
a = a/10;
Lcd_Set_Cursor(1,12);
Lcd_Write_Char(a%10 + 48);
Lcd_Set_Cursor(1,15);
Lcd_Write_String("cm");
}
else
{
Lcd_Clear();
Lcd_Set_Cursor(1,1);
Lcd_Write_String("Out of Range");
}
__delay_ms(400);
}
}--------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------
http://waihung.net/hc-sr04-ultrasonic-sensor-on-pic/
▲ PIC16F877A, HC-SR04, LCD16x2, MPLAB X(MPLAB IDE를 사용해도 무방하다)
▲Hi-Tech C compiler (하이텍 C 컴파일러)
HI-TECH_PICC_PRO_9.71a (Activate하는법).zip
▼좀더 많은 칩을 지원해준다. (9.71a 압축을 풀어보고 사용하려는 칩을 지원해주지 않느다면 아래것을 받도록 하자.)
Main코드는 아래와 같다. (나머지는 파일을 받으면 된다.)
#include <stdio.h>
#include <stdlib.h>
#include <pic.h>
#include <htc.h>
#define _XTAL_FREQ 16e6
__CONFIG(WDTE_OFF & CP_OFF & FOSC_HS & PWRTE_ON & LVP_OFF);
#define RS RB2
#define EN RB3
#define D4 RB4
#define D5 RB5
#define D6 RB6
#define D7 RB7
#include "lcd.h"
#define _XTAL_FREQ 16e6
// Lcd4_Init(); Lcd4_Clear() Lcd4_Set_Cursor() : Lcd4_Write_Char() Lcd4_Write_String()
unsigned int distance = 0;
char lcd_distance[10];
void sendPulse(void);
unsigned int readUltrasonic(void);
int main(int argc, char** argv) {
TRISB = 0;
RB0 = 0;
TRISD = 0xFF;
OPTION_REG &= 0b11000110;
Lcd4_Init();
Lcd4_Set_Cursor(1,0);
Lcd4_Write_String("Ultrasonic");
while(1) {
distance = readUltrasonic()/1.7;
sprintf(lcd_distance,"%d",distance);
Lcd4_Set_Cursor(2,0);
Lcd4_Write_String(lcd_distance);
//Lcd4_Set_Cursor(2,6);
Lcd4_Write_String(" cm ");
__delay_ms(300);
}
return (EXIT_SUCCESS);
}
void sendPulse(void) {
RB0 = 1;
__delay_us(10);
RB0 = 0;
}
unsigned int readUltrasonic(void){
unsigned int time = 0;
while(1) { if(RD0==0) { break; } }
sendPulse();
while(1) {
if(RD0==1)
{
TMR0 = 0;
while(1)
{
if(RD0==0){
time = TMR0;
return time;
}
}
}
}
}---------------------------------------------------------------------------------------
https://electrosome.com/blinking-led-pic-microcontroller-hi-tech-c/
추가로 Hi-Tech 기본코드는 아래와 같다.
#include <htc.h>
#define _XTAL_FREQ 8000000
void main()
{
TRISB=0X00;
PORTB=0X00;
while(1)
{
PORTB=0XFF;
__delay_ms(1000);
PORTB=0X00;
__delay_ms(1000);
}
}