Емельянов Эдуард Владимирович (eddy_em) wrote,
Емельянов Эдуард Владимирович
eddy_em

Categories:

Дальномеры

Дело было вечером, делать было нечего…
Хоть и не вечер, но что-то скучно: выходной, а поезд у меня аж в 23:30. Вот и решил «потренироваться на кошках» — поизучать захваченные с собой на такой случай дальномеры.


1. Sharp 2Y0A02


Простейший аналоговый дальномер (правда, стоит на ибее аж около десяти баксов!). По документации, измеряет расстояние от 20см до 1.5м. Наружу у него торчат только три провода (слева-направо): данные, земля, питание. Питание рекомендовано +5В, но вроде как можно любое до +7В давать.

Выходной сигнал — аналоговый, имеет сложную функциональную зависимость (примерно как 1/x) от расстояния, т.е. без калибровки при желании реального применения никак не обойтись.

Вот когда я пожалел, что не взял в "Икее" измерительную ленту: расстояния-то даже нечем измерить для опытов. Поэтому взял то, чьи размеры точно известны — листок бумаги А4. И измерял расстояние до коробочки, в которой я все это электронное барахло с собой в Нижний Новгород и привез. Поэтому в тестах и будут фигурировать два расстояния: 210 и 297мм. Больше мне просто лень было делать. Если уж понадобится — можно будет спокойно в лабораторных условиях приборчик откалибровать.

Для измерения напряжения я использовал 10-битный АЦП STM8. Обрабатывал результаты в octave. Для повышения надежности делал по 30 измерений. 100 было делать просто лень.

D = 210мм. В ADU получилось (676±5) единиц, в Вольтах это (2.181±0.017)В. По калибровочному графику в даташите должно быть где-то 2.5В (кстати, я перемещал перед датчиком руку, больше 2.2В он не показывал). Разброс значений в пределах измерений: от 2.150 до 2.227 Вольт (0.077В).

D = 297мм. В ADU получилось (554±5) единиц, в Вольтах это (1.786±0.017)В. Калибровочный график говорит, что должно быть около 2В. Разброс: от 1.770В до 1.824В (0.055В).

Вывод: калибровочный график из даташита врет. Калибровка обязательна. Судя по графику из даташита, погрешность 0.017В в районе 20-30см — это где-то ±3мм точности по расстоянию (но из-за нелинейности при увеличении расстояния абсолютная погрешность сильно возрастает). Вполне прилично. Вполне возможно, что если принять меры по снижению шумов АЦП, точность будет выше. Можно на игрушках использовать для измерения расстояний до объектов (правда, учесть, что где-то <15см получим бред)


2. HC-SR04


С этим модулем пришлось чуть повозиться, т.к. не так-то просто оказалось измерить длительность импульса (я долго читал даташит, но только методом тыка понял, что надо таймер все равно включать, хоть по идее он и включается "автоматом" при запуске режима захвата).

Модуль работает достаточно просто: на вход Trig ему подается импульс длительностью не меньше 10мс. По заднему фронту импульса датчик запускает пакет из восьми импульсов и ожидает их прибытия. Затем на выходе Echo формируется сигнал, длительность которого равна задержке между отправкой и приемом пакета.

Вот так таймер настраивается:
TIM1_PSCRH = 0;
TIM1_PSCRL = 15;
TIM1_IER = TIM_IER_CC2IE | TIM_IER_CC1IE | TIM_IER_UIE;
TIM1_CCMR1 = 1;
TIM1_CCMR2 = 2;
TIM1_CCER1 = 0x20;
TIM1_SMCR = 0x54;

Период 1мкс, разрешение прерываний по началу и окончанию счета, разрешение прерываний по переполнению, режим работы — захват импульсов с канала TIM1_CH1. Первый канал захвата обнуляет таймер по нарастающему фронту на этом канале (начало импульса), второй канал помещает значение счетчиков в регистр TIM1_CCR2 и останавливает таймер. Значение регистра TIM1_CCR1 нам не нужно (там будет мусор).

Для начала измерения расстояния при помощи УЗ дальнометра запускается эта функция:
void US_start(){
	U8 i;
	TIM1_CNTRH = 0; TIM1_CNTRL = 0;
	TIM1_CCR2H = 0; TIM1_CCR2L = 0;
	US_flag = 0;
	// post initial pulse
	PORT(US_OUT_PORT, ODR) |= US_OUT_PIN;
	for(i = 0; i < 160; i++) nop(); // wait at least for 10us
	PORT(US_OUT_PORT, ODR) &= ~US_OUT_PIN;
	// Enable the captures by writing the CC1E and CC2E bits to 1 in the TIM1_CCER1 register.
	TIM1_CCER1 |= 0x11;
	TIM1_CR1 = TIM_CR1_CEN; // turn on timer
}

На всякий случай регистры обнуляются (хотя этого делать не обязательно), сбрасывается флаг окончания измерений, выдается импульс на Trig и запускается таймер.
Здесь опять мое рукожопие проявилось: лень мне было в таком тестике запускать таймер в режиме одновибратора, чтобы сформировать импульс нужной длительности. Цикл с "нопами" для отсчета задержки — это классический признак рукожопия и индусокодерства.

Я разрешил еще и прерывание по переполнению (на всякий случай) если расстояние будет больше 11м (все равно в этом случае будет ошибка и overcapture, но на всякий случай не помешает:
volatile U8 in_measure = 0; // flag of measurenent
// Timer1 Update/Overflow/Trigger/Break Interrupt
// in_measure == 1 means that we will get wrong result as pulse length is too long
// pulse of 65536us is 32768us to something == near 11 meters
INTERRUPT_HANDLER(TIM1_UPD_OVF_TRG_BRK_IRQHandler, 11){
	TIM1_SR1 &= ~TIM_SR1_UIF; // clear this flag
	if(!in_measure) return; // not process when there's nothing started
	US_flag = 3;
	TIM1_SR1 = 0;
	TIM1_SR2 = 0;
	TIM1_CR1 = 0;TIM1_CCER1 &= ~0x11; // stop timer
	in_measure = 0;
}

Кстати, пробовал отключать обработку переполнения таймера в надежде выловить overcapture. Фигвам! Почему-то результаты все равно получались как будто бы все ОК.
А вот когда я отлавливаю переполнение, то при затыкании одной из пищалок пальцем получаю сообщение об ошибке.

Ну и обработчик для измерения длительности импульса:
INTERRUPT_HANDLER(TIM1_CAP_COM_IRQHandler, 12){
	if(TIM1_SR1 & TIM_SR1_CC2IF){
		TIM1_SR1 = 0; // clear all interrupt flags
		TIM1_CR1 = 0;TIM1_CCER1 &= ~0x11; // stop timer
		in_measure = 0; // work done
		if(TIM1_SR2){ // overcapture: noice etc.
			TIM1_SR2 = 0;
			US_flag = 1;
		}else{
			US_flag = 2;
		}
	}
	if(TIM1_SR1 & TIM_SR1_CC1IF){
		TIM1_SR1 &= ~TIM_SR1_CC1IF; // clear this interrupt flags
		in_measure = 1; // start of measurement
	}
}

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

В бесконечном цикле внутри main есть такая строчка:
if(US_flag) US_check(); // end of measurement with ultrasonic?

которая запускает эту функцию:
void US_check(){
	unsigned long L;
	if(!US_flag) return;
	if(US_flag == 1){ // error - write message
		error_msg("measurement overcapture");
	}else if(US_flag == 3){ // overflow
		error_msg("TIM1 overflow");
	}else{ // all OK - write measured length
		Pulse_length =  ((U16)TIM1_CCR2H << 8) | TIM1_CCR2L;
		printUint((U8*)&Pulse_length,2);
		// sound velocity == 340m/s in normal conditions
		// So distance = pulse * 1e-6(us) * 340 / 2
		// in millimeters this is equal of pulse*170/1000
		L = ((unsigned long) Pulse_length) * 170L;
		L /= 1000L;
		uart_write("Distance (mm): ");
		printUint((U8*)&L, 4);
	}
	US_flag = 0;
}

В целях отладки я выдаю на консоль длительность импульса (в мкс) и пишу измеренное расстояние в предположении того, что скорость звука составляет 340м/с (при 15°C).
Правда, учитывая то, что скорость звука — величина очень уж непостоянная, без хотя бы термодатчика не обойтись, иначе показания совсем уж потолочными будут. А еще лучше — добавить помимо термодатчика и измеритель атмосферного давления.

Результаты.
Я положил датчик на стол, чтобы он смотрел в потолок. Сделал пару десятков измерений. Получилось: расстояние = (1789.9±2.6)мм.
Положил в коробку для измерения расстояния до ближайшей стенки: вышло 68мм и 108мм (реальные расстояния от края датчика до стенки: 73мм и 115мм), показания постоянно повторялись, без изменений. Расстояние в 100мм тоже было измерено как 93мм. Похоже, откуда-то берутся лишние 5мм. Либо скорость звука при измерениях была совсем не 340м/с!

При повторном измерении расстояния до потолка я натолкнулся на то, что диаграмма направленности у датчика очень даже широкая: в нее попал край полки и расстояние было измерено неправильно. Кстати, измерил рулеткой расстояние до потолка: не 1.79, а 1.85м. Опять примерно 3% ошибки.

Судя по таблицам, при увеличении нормальной температуры на 10°C, скорость звука увеличивается на 1.5%. А вот от давления скорость звука не сильно изменяется: при 15°C и 760мм.рт.ст. на уровне моря, на высоте 650м (Ставрополь) она будет чуть больше 337м/с (меньше на 0.9%), а на высоте Букова она будет где-то 336.4м/с.

Откуда взялись эти 3% — непонятно. Но явно датчик тоже надо калибровать и работать совместно с термодатчиком, если хочется получить более-менее правдоподобные результаты.

В качестве же датчика, сигнализирующего о значительном изменении расстояния (что-то попало в его диаграмму направленности) он вполне годится! Работает он быстро: его вполне можно раз 5 в секунду опрашивать (а то и чаще)! Для тестов я в консоли зажал клавишу "D" (отобразить расстояние) и поводил рукой перед датчиком. Спокойный взмах вызывал заметное изменение показаний (с 1800мм до 70мм, когда датчик смотрел вверх).
Еще помахал быстро рукой вверх-вниз, надеясь сбить с толку счетчик пакетов (задержки-то между импульсами в пакете из-за допплеровского эффекта изменяются!), но датчик уверенно показывал нормальные результаты.

В общем, вполне годный датчик для использовании в игрушках (расстояние до преграды), в датчиках вроде "парктроника", в регистраторах проезда человека на финише велогонки.


UPD. Забыл дать ссылку на код. Исправляюсь: на сосфорже, на гитхабе. Как часть репозитория stm8_samples.
Tags: stm8, железяки
Subscribe

  • Дурацкий перекресток

    Был на днях в Пятигорске. Ну и движение там! Просто жесть!!! Вечные пробки, куча "кругов" и грохотящие трамваи… А когда выезжал оттуда, на углу пр.…

  • А что, в С так нельзя?

    Пытаюсь передать в функцию цвет как массив. Функция такая: void Pattern_draw3(Img3 *img, Pattern *p, int xul, int yul, uint8_t colr[3]); И…

  • Документация! Никто не любит документацию…

    Хочу использовать занятную header-only библиотеку STB для чтения-записи изображений (останется лишь фитсы добавить), а также добавления к ним…

promo eddy_em august 17, 2019 12:33 3
Buy for 10 tokens
Юра намедни напечатал корпус для хронометра. Для первого блина получилось неплохо: И еще немного фотографий:
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

  • 0 comments