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); } }