Ультразвуковой дальномер

В данной статье рассмотрим учебный проект дальномера на основе ультразвукового трансивера HC-SR04, ПЛИС и четырехразрядного индикатора. Для успешного проведения эксперимента необходимо и достаточно раздобыть:
1. Трансивер HC-SR04. Не уверен, что правильно его называю, но и датчиком расстояния назвать не могу, т.к. девайс ничего не считает, а только посылает звуковой импульс и докладывает, что услышал эхо.

2. Конденсатор 4.7 мкф.

3. Подходящая ПЛИС

4. Четырехзначный индикатор

Проект состоит из трех основных блоков. Блок управления трансивером (transceiver) посылает и принимает импульсы, считает время (точнее количество тактов тактового генератора) которое прошло от излучения импульса до попадания его в микрофон. Результат (количество тактов) появляется в виде двоичного числа на выходной шине. Блок преобразования преобразует количество тактов в расстояние выраженное в миллиметрах. Блок управления дисплеем выводит значение расстояния на дисплей.

Текст основного модуля проекта выглядит так:
/*************** ss_ping.v *****************/
module ss_ping (input wire clock,
input wire echo,
output wire trigger,
output wire [7:0]segments,
output wire [3:0]digits);
wire [20:0] w_time;
wire [13:0] distance;
transceiver TRC1(clock, trigger, echo, w_time);
converter CR1(clock, w_time, distance);
ss4d D1(clock, distance, segments, digits);
endmodule
/*************** ss_ping.v *****************/
Не думаю, что здесь требуются комментарии. Переходим к модулю transceiver:
/*************** transceiver.v *****************/
module transceiver(clock, trigger, echo, distance);
input wire clock;
input wire echo;
output wire trigger;
output wire [20:0] distance;
reg [22:0] clock_reg;
reg [20:0] distance_reg;
reg trigger_reg;
assign distance = distance_reg;
assign trigger = trigger_reg;
always @ (posedge clock)
begin
clock_reg <= clock_reg + 1;
trigger_reg <= (clock_reg < 500) ? 1 : 0;
end
always @ (posedge echo)
begin
distance_reg <= clock_reg[20:0];
end
endmodule
/*************** transceiver.v *****************/
Частота импульсов на выходе trigger зависит от частоты тактового генератора clock и размера регистра clock_reg.

В моем случае (частота генератора 50 МГц и размер регистра 23 бита) получается приблизительно 6 импульсов в секунду. Длительность импульса задается выражением
trigger_reg <= (clock_reg < 500) ? 1 : 0;
. В данном случае длительность импульса составит 10 мкс. В принципе она может быть другой, потому, что сигнал на входе Trig трансивера HC‑SR04 не задает продолжительность звукового импульса, а служит только для его запуска. Размер регистра distance_reg в принципе избыточен. Его достаточно, чтобы измерять расстояния до 7 метров. Производитель трансивера вообще указывает диапазон измерения до 600 мм.При подключении тарнсивера нас ждет небольшая засада. Если вам доводилось подключать HC-SR04 к плате Ардуино, то проблем вы скорее всего не испытывали, соединили две железки, написали программу и все заработало. Нужные библиотеки уже написаны и доступны для скачивания. В нашем варианте все немного сложнее. Если не соединить выход Echo трансивера с GND через тот самый конденсатор, то ничего не заработает. Судя по всему, сигнал, который появляется на выходе Echo не особо похож на прямоугольный импульс и схема на него не реагирует.
С отпрвкой и получением ультразвуковых импульсов разобрались, сколько тактов проходит от момента отправки импульса до получения эхо-сигнала знаем. Теперь наша задача — преобразовать количество тактов в расстояние. Этим у нас занимается модуль converter.
/****************** converter.v ******************/
module converter(clock, pulses, distance);
input wire clock;
input wire [20:0] pulses;
output wire [13:0] distance;
reg [13:0] distance_reg;
assign distance = distance_reg;
always @ (posedge clock)
begin
distance_reg <= (pulses*331)/(100000); //(pulses*331000)/(2*50000000);
end
endmodule
/****************** converter.v ******************/
Конвертер работает просто. Берет число тактов, умножает на коэффициент, выдает результат. 331000 — скорость звука в мм/с. Делим на два потому, что звук проходит измеряемое расстояние дважды. 50000000 — это частота нашего тактового генератора в Гц. Седьмой, что ли, класс средней школы.
Наконец у нас есть расстояние в миллиметрах, но оно у нас в виде двоичного числа. Как его вывести на семисегментный четырехразрядный дисплей? Готовых решений под мою задачу не нашлось. Сначала я хотел схитрить и подсунуть синтезатору Quartus-а таблицу истинности на 10000 вариантов, т.к. сгенерировать ее можно пятью строчками кода на любом ЯП. Но Quartus, зараза, вроде и не ругался, но и выдавал какую-то нерабочую фигню. Пришлось включать мозг. Для вывода десятичных чисел на индикатор их нужно преобразовать в двоично-десятичный код. А так как у нас четыре разряда, то нам нужно разложить двоичное число на четыре двоично-десятичных. Этим у нас занимается модуль b2b_4d.
/******************* b2b_4dt.v *******************/
module b2b_4dt(input wire [13:0]BIN,
input wire CLK,
// output wire OOR,
output wire [3:0]DOUT0,
output wire [3:0]DOUT1,
output wire [3:0]DOUT2,
output wire [3:0]DOUT3);
wire [9:0] BIN1;
wire [6:0] BIN2;
wire [3:0] BIN3;
reg [3:0]D0;
reg [3:0]D1;
reg [3:0]D2;
reg [3:0]D3;
//reg OOR_reg;
//assign OOR = OOR_reg;
assign DOUT1 = D1;
assign DOUT2 = D2;
assign DOUT3 = D3;
assign BIN1 = BIN - D3*1000;
assign BIN2 = BIN1 - D2*100;
assign BIN3 = BIN2 - D1*10;
assign DOUT0 = BIN3;
always @(posedge CLK)
begin
//OOR_reg <= (BIN > 9999) ? 1 : 0;
if ((BIN <= 9999) && (BIN > 8999)) begin D3 <= 4'b1001; end
if ((BIN < 9000) && (BIN > 7999)) begin D3 <= 4'b1000; end
if ((BIN < 8000) && (BIN > 6999)) begin D3 <= 4'b0111; end
if ((BIN < 7000) && (BIN > 5999)) begin D3 <= 4'b0110; end
if ((BIN < 6000) && (BIN > 4999)) begin D3 <= 4'b0101; end
if ((BIN < 5000) && (BIN > 3999)) begin D3 <= 4'b0100; end
if ((BIN < 4000) && (BIN > 2999)) begin D3 <= 4'b0011; end
if ((BIN < 3000) && (BIN > 1999)) begin D3 <= 4'b0010; end
if ((BIN < 2000) && (BIN > 999)) begin D3 <= 4'b0001; end
if (BIN < 1000) begin D3 <= 4'b0000; end
if ((BIN1 <= 999) && (BIN1 > 899)) begin D2 <= 4'b1001; end
if ((BIN1 < 900) && (BIN1 > 799)) begin D2 <= 4'b1000; end
if ((BIN1 < 800) && (BIN1 > 699)) begin D2 <= 4'b0111; end
if ((BIN1 < 700) && (BIN1 > 599)) begin D2 <= 4'b0110; end
if ((BIN1 < 600) && (BIN1 > 499)) begin D2 <= 4'b0101; end
if ((BIN1 < 500) && (BIN1 > 399)) begin D2 <= 4'b0100; end
if ((BIN1 < 400) && (BIN1 > 299)) begin D2 <= 4'b0011; end
if ((BIN1 < 300) && (BIN1 > 199)) begin D2 <= 4'b0010; end
if ((BIN1 < 200) && (BIN1 > 99)) begin D2 <= 4'b0001; end
if (BIN1 < 100) begin D2 <= 4'b0000; end
if ((BIN2 <= 99) && (BIN2 > 89)) begin D1 <= 4'b1001; end
if ((BIN2 < 90) && (BIN2 > 79)) begin D1 <= 4'b1000; end
if ((BIN2 < 80) && (BIN2 > 69)) begin D1 <= 4'b0111; end
if ((BIN2 < 70) && (BIN2 > 59)) begin D1 <= 4'b0110; end
if ((BIN2 < 60) && (BIN2 > 49)) begin D1 <= 4'b0101; end
if ((BIN2 < 50) && (BIN2 > 39)) begin D1 <= 4'b0100; end
if ((BIN2 < 40) && (BIN2 > 29)) begin D1 <= 4'b0011; end
if ((BIN2 < 30) && (BIN2 > 19)) begin D1 <= 4'b0010; end
if ((BIN2 < 20) && (BIN2 > 9)) begin D1 <= 4'b0001; end
if (BIN2 < 10) begin D1 <= 4'b0000; end
end
endmodule
/******************* b2b_4dt.v *******************/
Включение сегментов индикатора в соответствии с полученным на вход числом — не сложная задача. В одной из статей на сайте должно быть ее решение на VHDL. В данном проекте включает лампочки ss_converter.
/******************* ss_converter.v *******************/
module ss_converter(input wire [3:0]DATA,
input wire CLOCK,
output wire [7:0]DOUT);
reg [7:0] dout_reg;
assign DOUT = dout_reg;
always @(posedge CLOCK)
case (DATA)
// 76543210
4'b0000: begin dout_reg <= 8'b11000000; end // 0
4'b0001: begin dout_reg <= 8'b11111001; end // 1
4'b0010: begin dout_reg <= 8'b10100100; end // 2
4'b0011: begin dout_reg <= 8'b10110000; end // 3
4'b0100: begin dout_reg <= 8'b10011001; end // 4
4'b0101: begin dout_reg <= 8'b10010010; end // 5
4'b0110: begin dout_reg <= 8'b10000010; end // 6
4'b0111: begin dout_reg <= 8'b11111000; end // 7
4'b1000: begin dout_reg <= 8'b10000000; end // 8
4'b1001: begin dout_reg <= 8'b10010000; end // 9
default: begin dout_reg <= 8'b11000000; end
endcase
endmodule
/******************* ss_converter.v *******************/
Полседнее, что осталось сделать — вывести информацию на индикатор. Работает он следующим образом. Выводы D0..D7 подключены к соответствующим сегментам на всех четырех разрядах. Выводы Sel_n0..Sel_n3 управляют подачей питания на соответствующий разряд. Чтобы вывести на индикатор число, необходимо циклично включать разряды с достаточно высокой частотой. Отображением информации на индикаторе управляет модуль ss4d.

/******************* ss4d.v *******************/
module ss4d (input wire CLK,
input wire [13:0]DATA,
output wire [7:0]DOUT,
output wire [3:0]DNR);
reg [14:0] f_reg;
reg fclk;
reg [1:0] s_reg;
wire [3:0] cr_bus0;
wire [3:0] cr_bus1;
wire [3:0] cr_bus2;
wire [3:0] cr_bus3;
wire [7:0] out_bus0;
wire [7:0] out_bus1;
wire [7:0] out_bus2;
wire [7:0] out_bus3;
reg [7:0] dout_reg;
reg [3:0] dnr_reg;
assign DOUT = dout_reg;
assign DNR = dnr_reg;
b2b_4dt DR1(DATA,
CLK,
cr_bus0,
cr_bus1,
cr_bus2,
cr_bus3);
ss_converter CR0(cr_bus0, CLK, out_bus0);
ss_converter CR1(cr_bus1, CLK, out_bus1);
ss_converter CR2(cr_bus2, CLK, out_bus2);
ss_converter CR3(cr_bus3, CLK, out_bus3);
always @ (posedge CLK)
begin
f_reg <= f_reg + 1;
if (f_reg == 0) begin fclk <= (fclk == 0) ? 1 : 0; end
end
always @ (posedge fclk)
begin
case (s_reg)
2'b00: begin dout_reg <= out_bus3; dnr_reg <= 4'b1110; end
2'b01: begin dout_reg <= out_bus2; dnr_reg <= 4'b1101; end
2'b10: begin dout_reg <= out_bus1; dnr_reg <= 4'b1011; end
2'b11: begin dout_reg <= out_bus0; dnr_reg <= 4'b0111; end
endcase
s_reg = s_reg + 1;
end
endmodule
/******************* ss4d.v *******************/
Модуль написан не совсем правильно. Одновременно работает только одна цифра, а ss_converter-ов зачем-то четыре. Что ж. Есть повод поупражняться в оптимизации.
- +3
- 14 июня 2011, 23:26
- digides
Производитель трансивера вообще указывает диапазон измерения до 600 мм.60см? Это максимальное? Уныло как-то :(
А вообще предлагаю перенести пост в блог про ПЛИС.
в вашем коде есть одна не очень хорошая вещь — использование 2х источников тактовой частоты. Наверняка Quartus дает критическое сообщение. Смотрите clock_reg меняется с частотой clock, а чтение из него производится с ассинхронной тактовой частотой echo. Использование двух частот — это потенциальное место для метастабильности (это когда схема вроде и работает, но непонятно что происходит и иногда происходит сбой).
было бы лучше использовать один clock, с его помощью выделить фронт сигнала echo и уже тогда читать.
reg prev_echo;
always @(posedge clock)
prev_echo<=echo;
always @ (posedge clock)
if(prev_echo==1'b0 && echo==1'b1)
distance_reg <= clock_reg[20:0];
как-то так…
было бы лучше использовать один clock, с его помощью выделить фронт сигнала echo и уже тогда читать.
reg prev_echo;
always @(posedge clock)
prev_echo<=echo;
always @ (posedge clock)
if(prev_echo==1'b0 && echo==1'b1)
distance_reg <= clock_reg[20:0];
как-то так…
еще одна вещь меня просто поразила:
distance_reg <= (pulses*331)/(100000);
Интересно, сколько эта функция заняла у Вас логических элементов? Тысячу? Имхо можно как-то полегче и проще сделать…
Да вот кстати была статья про дальномер marsohod.org/index.php/projects/159-sonar
distance_reg <= (pulses*331)/(100000);
Интересно, сколько эта функция заняла у Вас логических элементов? Тысячу? Имхо можно как-то полегче и проще сделать…
Да вот кстати была статья про дальномер marsohod.org/index.php/projects/159-sonar
Я пока мало знаю про оптимизацию электронных схем. Единственное что пришло в данном случае в голову — раскладывать числа на множители и сокращать.
Жаль, что пропустил эту страницу с сайта «Марсохода». Не пришлось бы искать информацию о работе HC-SR04 на буржуйских сайтах. При чем, такого подробного описания все равно не нашел.
Жаль, что пропустил эту страницу с сайта «Марсохода». Не пришлось бы искать информацию о работе HC-SR04 на буржуйских сайтах. При чем, такого подробного описания все равно не нашел.
Встречал данные, что в подобных устройствах необходимо еще вводить температурную компенсацию. при нормальном атмосферном давлении при 15 ° С скорость звука около 340 м/с, и на каждый дополнительный градус увеличивается на 0.607м/с При 35 ° C летом, скорость звука 352m / с, а зимой при -20° С, скорость звука 319м/с.
Это смотря какая точность нужна.
Если по уму тогда надо делать и поправку на атмосферное давление, скорости и направления ветра, а можно еще и состав воздуха анализировать.
Если по уму тогда надо делать и поправку на атмосферное давление, скорости и направления ветра, а можно еще и состав воздуха анализировать.
В моем мастековском дальномере датчик температуры стоит, а вот на остальное китайцы забили. Видимо, она или наиболее существенна, или проще измеряется)
Я бы проще поступал, сервиком выставлять (выдвигал) перед датчиком скажем на расстоянии 10 см преграду, делать контрольный замер и это будет поправочный коэффициент для последующих измерений.
Мой менее 60см (по ману, реально 55) вообще не видит. Ну и от калибровки по такому расстоянию толку не так много — оно не учтет ни ветер, ни неравномерность среды на протяжении луча, а температуру проще так померить. Насчет давления хз.
Да забейте, мне кажется что при таких расстояниях погрешность будет невелика, можно действительно просто использовать температуру.
Простите мою наивность, но чтоже все-таки являются входными и выходными сигналами самого модуля дальномера? Что подавать на ножку рядом с питанием, и что ожидать на выходе Echo?
У датчика «Trig» — это вход, а «Echo» — выход. На вход подается импульс. У меня продолжительность импульса выставлена достаточно небольшая — 10 мксек, а значение из даташита было то ли 1, то ли 10 мсек. Я, ради интереса, перебрал несколько вариантов в меньшую сторону. Оказалось, что и на 10 мксек уверенно срабатывает. На «Echo» появляется сигнал, когда датчик поймает эхо.
Ну, а у управляющей схемы «echo» — вход, а «trigger» — выход.
Ну, а у управляющей схемы «echo» — вход, а «trigger» — выход.
Не знаю какой у вас даташит, а в моем сказано про 500 сантиметров.
При отсутствии объектов, сенсор должен выдавать на echo импульс длиной 38 или 40 миллисекунд. У меня выдавалось значение около 200 000. Подобного в интернете не нашел, возможно мне попались бракованные сенсоры (хотя их 3 штуки, работают одинаково).
Оказалось, при питании от usb получается не более 60 сантиметров. Но когда подключил в качестве питания Крону (через регулятор на 5 В), дальность сонара сразу возросла до 3 метров 70 сантиметров. У меня под рукой был регулятор только на 1А. Похоже модулю надо около 2 А (видел у какого-то подобного сонара эту цифру).
При отсутствии объектов, сенсор должен выдавать на echo импульс длиной 38 или 40 миллисекунд. У меня выдавалось значение около 200 000. Подобного в интернете не нашел, возможно мне попались бракованные сенсоры (хотя их 3 штуки, работают одинаково).
Оказалось, при питании от usb получается не более 60 сантиметров. Но когда подключил в качестве питания Крону (через регулятор на 5 В), дальность сонара сразу возросла до 3 метров 70 сантиметров. У меня под рукой был регулятор только на 1А. Похоже модулю надо около 2 А (видел у какого-то подобного сонара эту цифру).
Вообще расстроен, что взял эти модули. Подкупила меньшая цена, большая дальность указанная в даташите, и более компактные размеры.
Раньше использовал TS601-01. Тут дальность 3 м. 30 см. но работает очень стабильно, даже будучи направленным под достаточно большим углом к измеряемой поверхности.
Раньше использовал TS601-01. Тут дальность 3 м. 30 см. но работает очень стабильно, даже будучи направленным под достаточно большим углом к измеряемой поверхности.
Комментарии (38)
RSS свернуть / развернуть