Практически для любого современного проекта встраиваемой электроники необходимы датчики. Они являются «глазами» и «ушами» проекта и помогают микроконтроллеру получать информацию о том, что происходит вокруг него. В данной статье мы рассмотрим подключение ультразвукового датчика HC-SR04 к микроконтроллеру PIC. Измеренное с помощью датчика HC-SR04 расстояние мы будем отображать на экране ЖК дисплея 16х2.
HC-SR04 является ультразвуковым датчиком, позволяющим производить измерение расстояний в диапазоне от 2 до 450 см. Находит широкое применение в различных проектах радаров, роботов, объезжающих препятствия и т.д.
Также на нашем сайте мы рассматривали подключение ультразвукового датчика HC-SR04 к другим микроконтроллерам (платам):
- к микроконтроллеру AVR;
- к плате Arduino;
- к плате Raspberry Pi;
- к плате Raspberry Pi Pico;
- к плате STM32 Blue Pill.
Необходимые компоненты
- Микроконтроллер PIC16F877A (купить на AliExpress).
- Ультразвуковой датчик HC-SR04 (купить на AliExpress).
- Держатель микросхем на 40 контактов (купить на AliExpress).
- Программатор PICkit 3 (купить на AliExpress).
- Кварцевый генератор 20 МГц (купить на AliExpress).
- Конденсаторы 22 пФ (2 шт.), 0,1 мкФ и 10 мкФ (купить на AliExpress).
- Регулятор напряжения 7805 (купить на AliExpress).
- ЖК дисплей 16х2 (купить на AliExpress).
- Светодиод.
- Перфорированная плата.
- Соединительные провода.
Реклама: ООО «АЛИБАБА.КОМ (РУ)» ИНН: 7703380158
Принцип работы ультразвукового датчика HC-SR04
Ультразвуковой датчик HC-SR04 в нашем проекте используется для измерения расстояний в диапазоне 2-400 см с точностью 3 мм. Датчик состоит из ультразвукового передатчика, ультразвукового приемника и схемы управления. Основные принципы работы ультразвукового датчика состоят в следующем:
- Вначале с формируется сигнал высокого уровня длительностью 10 мкс, который запускает в работу ультразвуковой датчик.
- Затем модуль автоматически посылает 8 импульсов с частотой 40 кГц, а затем проверяет приняты они или нет.
- Если эти излученные сигналы принимаются, то вычисляется время между временем передачи этих импульсов и их приемом.
Расстояние затем можно рассчитать по следующей формуле:
Distance= (Time x Speed of Sound in Air (340 m/s))/2
где Time – измеренное датчиком время;
Speed of Sound in Air – скорость звука в воздухе, равная 340 м/с.
Временные диаграммы
Как уже указывалось, измерение расстояний осуществляется на основе эхо. Вначале передается импульс длительностью 10 мкс чтобы запустить модуль в работу. После этого модуль автоматически передает 8 импульсов с частотой 40 кГц (то есть ультразвуковая частота) и проверяет эхо – то есть не вернулись ли эти импульсы обратно, отразившись от препятствия. Если импульсы вернулись обратно, то расстояние до препятствия можно рассчитать по следующей формуле:
Distance= (time x speed)/2
В этой формуле мы разделили произведение скорости и времени на 2 потому что измеренное время равно сумме времен распространения звуковой волны до препятствия и обратно. То есть время, чтобы звук достиг препятствия, равно половине времени, измеренного датчиком.
Временные диаграммы работы модуля приведены на следующем рисунке:
Как показано на рисунке сначала нам нужно инициировать датчик для измерения расстояний, для этого на его триггерный контакт (trigger pin) необходимо подать логический сигнал высокого уровня длительностью не менее 10 мкс, после этого датчик генерирует серию звуковых колебаний и после получения отраженного сигнала (эхо) датчик обеспечивает на своем выходе сигнал, пропорциональный расстоянию между ним и препятствием.
Схема проекта
Схема подключения ультразвукового датчика HC-SR04 к микроконтроллеру PIC представлена на следующем рисунке.
№ п/п | № контакта микроконтроллера | Наименование контакта микроконтроллера | Куда подключен |
1 | 21 | RD2 | RS of LCD |
2 | 22 | RD3 | E of LCD |
3 | 27 | RD4 | D4 of LCD |
4 | 28 | RD5 | D5 of LCD |
5 | 29 | RD6 | D6 of LCD |
6 | 30 | RD7 | D7 of LCD |
7 | 34 | RB1 | контакт Trigger ультразвукового датчика |
8 | 35 | RB2 | контакт Echo ультразвукового датчика |
Далее перейдем к программе.
Объяснение программы для микроконтроллера PIC
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты. В данном проекте мы будем использовать работу с таймерами и ЖК дисплеем, поэтому если вы не знакомы с их принципами работы, рекомендуем ознакомиться со следующими статьями нашем сайте:
В программе в начале функции main мы инициализируем необходимые нам контакты ввода/вывода и регистры. Мы будем использовать Timer 1 с коэффициентом деления предделителя 1:4 и внутренней частотой синхронизации (Fosc/4).
1 2 3 4 5 6 |
TRISD = 0x00; //PORTD declared as output for interfacing LCD TRISB0 = 1; //Define the RB0 pin as input to use as interrupt pin TRISB1 = 0; //Trigger pin of US sensor is sent as output pin TRISB2 = 1; //Echo pin of US sensor is set as input pin TRISB3 = 0; //RB3 is output pin for LED T1CON=0x20; //4 pres-scalar and internal clock |
Timer 1 является 16-битным таймером в микроконтроллере PIC16F877A. Для управления работой таймера служит регистр T1CON, а результат сохраняется в регистрах TMR1H и TMR1L (первые 8 бит – в регистре TMR1H, остальные 8 бит – в регистре TMR1L). Таймер можно включить/выключить при помощи установки битов TMR1ON=1 и TMR1ON=0 соответственно.
После настройки таймера нам необходимо излучить ультразвуковую волну в направлении препятствия, для этого мы подаем уровень high на контакт Trigger датчика в течение 10 мкс с помощью следующего фрагмента кода.
1 2 3 |
Trigger = 1; __delay_us(10); Trigger = 0; |
Как показано на временных диаграммах работы датчика, приведенных выше, контакт Echo датчика будет оставаться в состоянии low до тех пор пока излученная ультразвуковая волна не вернется к датчику, после этого на контакте Echo будет уровень high в течение времени, которое потребовалось ультразвуковой волне на распространение до препятствия и обратно. Затем мы это время измеряем с помощью Timer 1 следующим образом:
1 2 3 4 |
while (Echo==0); TMR1ON = 1; while (Echo==1); TMR1ON = 0; |
И затем полученный результат измерения времени считываем из регистров TMR1H и TMR1L.
1 |
time_taken = (TMR1L | (TMR1H<<8)); |
Считанное время будет в формате байтов, для получения истинного значения времени нам необходимо использовать следующую формулу:
1 2 3 4 5 6 7 8 9 10 11 12 |
Time = (16-bit register value) * (1/Internal Clock) * (Pre-scale) Internal Clock = Fosc/4 В нашем случае, Fosc = 20000000Mhz and Pre-scale = 4 Следовательно, значение внутренней тактовой частоты будет 5000000Mhz, а значение времени можно будет определить по формуле: Time = (16-bit register value) * (1/5000000) * (4) = (16-bit register value) * (4/5000000) = (16-bit register value) * 0.0000008 seconds (OR) Time = (16-bit register value) * 0.8 micro seconds |
В нашей программе значение 16-битного регистра мы сохранили в переменной time_taken, следовательно, для расчета времени в микросекундах по вышеприведенной формуле получим:
1 |
time_taken = time_taken * 0.8; |
После этого нам необходимо определить расстояние до препятствия. Как мы знаем, distance = speed * time. Но в нашем случае нам полученный результат расстояния необходимо разделить на 2 поскольку ультразвуковая волна распространялась до препятствия и обратно. Скорость ультразвуковой волны в воздухе равна 34000cm/s, следовательно, получим:
1 2 3 |
Distance = (Speed*Time)/2 = (34000 * (16-bit register value) * 0.0000008) /2 Distance = (0.0272 * 16-bit register value)/2 |
И результирующая формула для расчета расстояния будет выглядеть следующим образом:
1 |
distance= (0.0272*time_taken)/2; |
После этого мы будем выводить на экран ЖК дисплея измеренные значения времени и расстояния.
Тестирование работы проекта
После сборки аппаратной части проекта и загрузки программы в микроконтроллер можно приступать к тестированию его работы.
Перфорированную плату с установленным на нее микроконтроллером PIC мы использовали из наших предыдущих проектов по микроконтроллерам PIC. Программировали мы микроконтроллер PIC с помощью программ MPLABX, XC8 и программатора Pickit 3.
Поместите любой объект перед датчиком – после этого на ЖК дисплее должно начать высвечиваться расстояние до него и время распространения ультразвуковой волны до него и обратно.
Более подробно работу проекта вы можете посмотреть на видео, приведенном в конце статьи.
Исходный код программы
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
/* Interfacing Ultrasonic sensor with PIC16F877A * Code by: B.Aswinth Raj * Dated: 19-07-2017 * More details at: www.CircuitDigest.com */ #define _XTAL_FREQ 20000000 #define RS RD2 #define EN RD3 #define D4 RD4 #define D5 RD5 #define D6 RD6 #define D7 RD7 #define Trigger RB1 //34 is Trigger #define Echo RB2//35 is Echo #include <xc.h> #pragma config FOSC = HS // Oscillator Selection bits (HS oscillator) #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled) #pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled) #pragma config BOREN = ON // Brown-out Reset Enable bit (BOR enabled) #pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming) #pragma config CPD = OFF // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off) #pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control) #pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off) //LCD Functions Developed by Circuit Digest. void Lcd_SetBit(char data_bit) //Based on the Hex value Set the Bits of the Data Lines { if(data_bit& 1) D4 = 1; else D4 = 0; if(data_bit& 2) D5 = 1; else D5 = 0; if(data_bit& 4) D6 = 1; else D6 = 0; if(data_bit& 8) D7 = 1; else D7 = 0; } void Lcd_Cmd(char a) { RS = 0; Lcd_SetBit(a); //Incoming Hex value EN = 1; __delay_ms(4); EN = 0; } void Lcd_Clear() { Lcd_Cmd(0); // очищаем экран ЖК дисплея Lcd_Cmd(1); // перемещаем курсор в 1-ю позицию } void Lcd_Set_Cursor(char a, char b) { char temp,z,y; if(a== 1) { temp = 0x80 + b - 1; //80H is used to move the curser z = temp>>4; //младшие 8 бит y = temp & 0x0F; // старшие 8 бит Lcd_Cmd(z); // устанавливаем строку Lcd_Cmd(y); // устанавливаем столбец } else if(a== 2) { temp = 0xC0 + b - 1; z = temp>>4; // младшие 8 бит y = temp & 0x0F; // старшие 8 бит Lcd_Cmd(z); // устанавливаем строку Lcd_Cmd(y); // устанавливаем столбец } } void Lcd_Start() { Lcd_SetBit(0x00); for(int i=1065244; i<=0; i--) NOP(); Lcd_Cmd(0x03); __delay_ms(5); Lcd_Cmd(0x03); __delay_ms(11); Lcd_Cmd(0x03); Lcd_Cmd(0x02); //02H is used for Return home -> Clears the RAM and initializes the LCD Lcd_Cmd(0x02); //02H is used for Return home -> Clears the RAM and initializes the LCD Lcd_Cmd(0x08); //Select Row 1 Lcd_Cmd(0x00); //Clear Row 1 Display Lcd_Cmd(0x0C); //Select Row 2 Lcd_Cmd(0x00); //Clear Row 2 Display Lcd_Cmd(0x06); } void Lcd_Print_Char(char data) //Send 8-bits through 4-bit mode { char Lower_Nibble,Upper_Nibble; Lower_Nibble = data&0x0F; Upper_Nibble = data&0xF0; RS = 1; // => RS = 1 Lcd_SetBit(Upper_Nibble>>4); //Send upper half by shifting by 4 EN = 1; for(int i=2130483; i<=0; i--) NOP(); EN = 0; Lcd_SetBit(Lower_Nibble); //Send Lower half EN = 1; for(int i=2130483; i<=0; i--) NOP(); EN = 0; } void Lcd_Print_String(char *a) { int i; for(i=0;a[i]!='\0';i++) Lcd_Print_Char(a[i]); //разделяем строку на отдельные символы с помощью указателей и по очереди передаем каждый символ } /*****End of LCD Functions*****/ int time_taken; int distance; char t1,t2,t3,t4,t5; char d1,d2,d3; int main() { TRISD = 0x00; //контакты PORTD будут работать на вывод данных – к ним подключен ЖК дисплей TRISB0 = 1; // на контакте RB0 мы будем производить обработку сигнала внешнего прерывания TRISB1 = 0; //Trigger pin of US sensor is sent as output pin (на вывод данных) TRISB2 = 1; //Echo pin of US sensor is set as input pin (на ввод данных) TRISB3 = 0; //RB3 is output pin for LED (на вывод данных) T1CON=0x20; Lcd_Start(); Lcd_Set_Cursor(1,1); Lcd_Print_String("Ultrasonic sensor"); Lcd_Set_Cursor(2,1); Lcd_Print_String("with PIC16F877A"); __delay_ms(2000); Lcd_Clear(); while(1) { TMR1H =0; TMR1L =0; //clear the timer bits Trigger = 1; __delay_us(10); Trigger = 0; while (Echo==0); TMR1ON = 1; while (Echo==1); TMR1ON = 0; time_taken = (TMR1L | (TMR1H<<8)); distance= (0.0272*time_taken)/2; time_taken = time_taken * 0.8; t1 = (time_taken/1000)%10; t2 = (time_taken/1000)%10; t3 = (time_taken/100)%10; t4 = (time_taken/10)%10; t5 = (time_taken/1)%10; d1 = (distance/100)%10; d2 = (distance/10)%10; d3 = (distance/1)%10; Lcd_Set_Cursor(1,1); Lcd_Print_String("Time_taken:"); Lcd_Print_Char(t1+'0'); Lcd_Print_Char(t2+'0'); Lcd_Print_Char(t3+'0'); Lcd_Print_Char(t4+'0'); Lcd_Print_Char(t5+'0'); Lcd_Set_Cursor(2,1); Lcd_Print_String("distance:"); Lcd_Print_Char(d1+'0'); Lcd_Print_Char(d2+'0'); Lcd_Print_Char(d3+'0'); } return 0; } |