Набирал тут недавно в одном интернет магазинчике комплектуху, для будущих поделок и случайно зацепился взглядом за такую штуку
Теория работы с устройством: на вход TRIG подается импульс 10мкс, датчик посылает ультразвуковой сигнал 40кГц, который отражается от препятствий и поступает обратно на датчик. На выходе ECHO формируется импульс, длительность которого пропорциональна расстоянию до объекта. Пересчитываем полученную длительность в сантиметры/метры по формуле из даташита
Test distance = (high level time×velocity of sound(340M/S) / 2,
или uS / 58 = centimeters
По подключению все просто, импульс формировать можно любой ножкой для примера PD3, а вот чтобы не пропустить ответный импульс, заведем эхо на ножку внешнего прерывания.
После того как был послан импульс, смотрим в прерывании что пришло — если передний фронт, то обнуляем таймер, если задний фронт, то считываем время от начала импульса до конца. Полученное время, пересчитываем в сантиметры и выводим в Uart на скорости 9600.
#include <mega8.h> #include <delay.h> // Standard Input/Output functions #include <stdio.h> volatile unsigned char status; volatile int tim = 0; // External Interrupt 0 service routine interrupt [EXT_INT0] void ext_int0_isr(void) { //проверяем уровень синала PD2( ECHO ) if(PIND.2 == 1 ) { //передний фронт - сбрасываем в 0 таймер TCNT1 = 0; status = 1; //измерение } else { //задний фронт - запоминаем значение таймера tim = TCNT1; status = 2; //окончание измерения } } void main(void) { // Port D initialization // Func7=In Func6=In Func5=In Func4=In Func3=Out Func2=In Func1=In Func0=In // State7=T State6=T State5=T State4=T State3=0 State2=T State1=T State0=T PORTD=0x00; DDRD=0x08; // Timer/Counter 1 initialization // Clock source: System Clock // Clock value: 1000,000 kHz // Mode: Normal top=0xFFFF TCCR1A=0x00; TCCR1B=0x02; TCNT1=0x00; // USART initialization // Communication Parameters: 8 Data, 1 Stop, No Parity // USART Receiver: Off // USART Transmitter: On // USART Mode: Asynchronous // USART Baud Rate: 9600 UCSRA=0x00; UCSRB=0x08; UCSRC=0x86; UBRRH=0x00; UBRRL=0x33; // External Interrupt(s) initialization // INT0: On // INT0 Mode: Any change // INT1: Off GICR|=0x40; MCUCR=0x01; GIFR=0x40; // Timer(s)/Counter(s) Interrupt(s) initialization TIMSK=0x00; #asm("sei") while (1) { //начало измерений status = 0; //генерируем импульс 10 мкс на входе trig PORTD.3 = 1; delay_us( 10 ); PORTD.3 = 0; //ждем окончания измерения while( status != 2 ); //переводим в сантиметры и выводим в юарт printf("%d\r\n",tim/58); delay_ms(1000); } } |
Максимальное измеряемое расстояние, заявленное производителем 4м, мне удалось протестировать в диапазоне до 3м, все в точности соответствует. Только нужно понимать, что если отражаемая поверхность неровная то датчик будет показывать все что угодно.
Прошивка, исходники
Матрица на нотик 3 тыщщи и в любом ларьке
Я буквально недавно носил в ремонт свой ноутбук, до сих пор под впечатлением
А где можно скачать библиотеку hc-sr04 для протеуса 7.7 чтобы можно было эксперементировать?
Вряд ли такая существует
Здравствуйте Артур, подскажите пожалуйста как вы подсчитали частоту таймера, чтобы получить на выходе 40 кГц и 10 мкс. Как Я полагаю 10 мкс вы делаете задержкой, а вот 40 кГц не пойму…
Все очень просто, из даташита на HC-SR04: The Module automatically sends eight 40 kHz and detect whether there is a pulse signal back. К нашей прошивке это никак не относится, на выходе датчика просто 1 импульс, длину которого мы должны посчитать. Для этого используется внешнее прерывание, по нарастанию и спаду. Частота таймера выбрана приблизительно, примерно нужно исходить из условия uS / 58 = centimeters. Подставьте верхнее значение расстояния 4м и посмотрите какой таймер подходит, чтобы посчитать столько микросекунд. Кроме того частота таймера будет влиять на точность, опять же попробуйте разные частоты таймера и прикиньте какой будет погрешность.
Здравствуйте. Попробовал датчик с Atmega32. При не больших расстояниях все нормально, но как расстояние увеличивается от 100см и более показания начинают непонятно скакать. Как можно обойти проблему?
тестировал до 4 м с мегой8, полет нормальный, может проблема в поверхности, может в коде.
Делаю так же как у Вас в уроке. Датчик смотрит в потолок (метров около 2 до него). Мега тактуется от кварца 16mНz, таймер на частоте 2000kНz.
вот пример кода:
long tim1 = 0;
if(PIND.2 == 1 )
{
TCNT1=0;
status = 1;
}
else
{
tim1 = TCNT1;
tim1 = tim1*5/10/58; //измерение * 0,0625(мкс в такте 16МHz)*8(предделитель)/58
}
По логике все так, но не забывайте что все переменные больше 1 байта, которые используются в прерывании нужно объявлять volatile, иначе компилятор их порежет
Прошу сильно не пинать, avr только пытаюсь освоить. Не могли бы вы посмотреть мой код, м.б. правда дело где-то в логике? И еще вопрос: ради эксперимента закрыл датчики, однако все равно на выходе есть показания(во всяком случае от 10 до 90см точно). Пробовал выловить переполнение таймера, все чисто. Схема собрана на беспаечной макетке, не могут быть какие-нибудь посторонние наводки?
И еще, попробовал сделать в место:
tim2 = TCNT1;
tim2 = tim2*5/10/58;
написать tim2 = TCNT1*5/10/58; компилятор выдает предупреждение: overflow is possible in 16 bit multiplication, casting to ‘long’ may be required
Хотя volatile long tim2 = 0; объявлена в глобальных переменных.
Выкладывайте тут исходник, можно без дефайнов
Два прерывания:
interrupt [EXT_INT0] void ext_int0_isr(void)
{
if(PIND.2 == 1 )
{
TCNT1=0;
status = 1;
}
else
{
tim1 = TCNT1;
tim1 = tim1*5/10/58; //вычисляем расстояние
if ((tim1=10)&&(dubl1==0)&&(dubl2==0)) //сработал 1 раз
{
dubl1=1;
}
else
{
if ((tim1=10)&&(dubl1==1)&&(dubl2==0)) //нижний датчик сработал 2 раз
{
dubl1=11;
}
}
if ((tim1=10)&&(dubl1==12)&&(dubl2==0)) //сработал 1 когда сработал 1+2
{
dubl1=121;
}
//для 2 датчика
if ((tim1=10)&&(dubl2==21)&&(dubl1==0)) //сработал 1 когда сработал 1
{
dubl2=211;
}
if ((tim1=10)&&(dubl2==1)&&(dubl1==0)) //сработал 1 когда сработал 2
{
dubl2=21;
}
status = 2; //окончание измерения
}
}
interrupt [EXT_INT1] void ext_int1_isr(void)
{
if(PIND.3 == 1 )
{
TCNT1=0;
status1 = 1; //измерение
}
else
{
tim2 = TCNT1;
tim2 = tim2*5/10/58; //вычисляем расстояние
if ((tim2=10)&&(dubl2==0)&&(dubl1==0)) //сработал 2 датчик 1 раз
{
dubl2=1;
}
else
{
if ((tim2=10)&&(dubl2==1)&&(dubl1==0)) //верхний датчик сработал 2 раз
{
dubl2=11;
}
}
//для первого датчика
if ((tim2=10)&&(dubl1==12)&&(dubl2==0)) //сработал 2 когда сработал 2
{
dubl1=122;
}
if ((tim2=10)&&(dubl1==1)&&(dubl2==0)) //сработал 2 когда сработал 1
{
dubl1=12;
}
status1=2;
}
}
Основная:
void main(void)
{
float result;
unsigned char pinC,pinA;
int k;
DDRA=(0<<DDA7) | (0<<DDA6) | (1<<DDA5) | (1<<DDA4) | (1<<DDA3) | (1<<DDA2) | (1<<DDA1) | (1<<DDA0);
PORTA=(0<<PORTA7) | (0<<PORTA6) | (0<<PORTA5) | (0<<PORTA4) | (0<<PORTA3) | (0<<PORTA2) | (0<<PORTA1) | (0<<PORTA0);
DDRB=(0<<DDB7) | (0<<DDB6) | (0<<DDB5) | (0<<DDB4) | (1<<DDB3) | (0<<DDB2) | (0<<DDB1) | (0<<DDB0);
PORTB=(0<<PORTB7) | (0<<PORTB6) | (0<<PORTB5) | (0<<PORTB4) | (0<<PORTB3) | (0<<PORTB2) | (0<<PORTB1) | (0<<PORTB0);
DDRC=(1<<DDC7) | (1<<DDC6) | (1<<DDC5) | (1<<DDC4) | (1<<DDC3) | (1<<DDC2) | (1<<DDC1) | (1<<DDC0);
PORTC=(0<<PORTC7) | (0<<PORTC6) | (0<<PORTC5) | (0<<PORTC4) | (0<<PORTC3) | (0<<PORTC2) | (0<<PORTC1) | (0<<PORTC0);
DDRD=(1<<DDD7) | (0<<DDD6) | (0<<DDD5) | (0<<DDD4) | (0<<DDD3) | (0<<DDD2) | (1<<DDD1) | (1<<DDD0);
PORTD=(0<<PORTD7) | (0<<PORTD6) | (0<<PORTD5) | (0<<PORTD4) | (0<<PORTD3) | (0<<PORTD2) | (0<<PORTD1) | (0<<PORTD0);
TCCR0=(1<<WGM00) | (1<<COM01) | (0<<COM00) | (1<<WGM01) | (0<<CS02) | (1<<CS01) | (1<<CS00); //TCCR0=(1<<WGM00) | (1<<COM01) | (0<<COM00) | (1<<WGM01) | (0<<CS02) | (1<<CS01) | (1<<CS00);
TCNT0=0x00;
OCR0=0x00;
TCCR1A=(0<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (0<<COM1B0) | (0<<WGM11) | (0<<WGM10);
TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (0<<WGM12) | (0<<CS12) | (1<<CS11) | (0<<CS10);
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
ASSR=0<<AS2;
TCCR2=(1<<PWM2) | (1<<COM21) | (0<<COM20) | (1<<CTC2) | (1<<CS22) | (0<<CS21) | (0<<CS20);
TCNT2=0x00;;
OCR2=0x00;
TIMSK=(0<<OCIE2) | (0<<TOIE2) | (0<<TICIE1) | (0<<OCIE1A) | (0<<OCIE1B) | (0<<TOIE1) | (0<<OCIE0) | (0<<TOIE0);
GICR|=(1<<INT1) | (1<<INT0) | (0<<INT2);
MCUCR=(0<<ISC11) | (1<<ISC10) | (0<<ISC01) | (1<<ISC00);
MCUCSR=(0<<ISC2);
GIFR=(1<<INTF1) | (1<<INTF0) | (0<<INTF2);
UCSRB=(0<<RXCIE) | (0<<TXCIE) | (0<<UDRIE) | (0<<RXEN) | (0<<TXEN) | (0<<UCSZ2) | (0<<RXB8) | (0<<TXB8);
ACSR=(1<<ACD) | (0<<ACBG) | (0<<ACO) | (0<<ACI) | (0<<ACIE) | (0<<ACIC) | (0<<ACIS1) | (0<<ACIS0);
ADMUX= FIRST_ADC_INPUT | ADC_VREF_TYPE;
ADCSRA=(1<<ADEN) | (1<<ADSC) | (0<<ADATE) | (0<<ADIF) | (1<<ADIE) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
SFIOR=(0<<ADTS2) | (0<<ADTS1) | (0<<ADTS0);
SPCR=(0<<SPIE) | (0<<SPE) | (0<<DORD) | (0<<MSTR) | (0<<CPOL) | (0<<CPHA) | (0<<SPR1) | (0<<SPR0);
TWCR=(0<<TWEA) | (0<<TWSTA) | (0<<TWSTO) | (0<<TWEN) | (0<<TWIE);
#asm("sei")
while (1)
{ status = 0;
PORTD.0 = 1;
delay_us( 10 );
PORTD.0 = 0;
// ждем окончания измерения
while( status != 2 );
delay_ms(100);
status1 = 0;
PORTD.1 = 1;
delay_us( 10 );
PORTD.1 = 0;
while( status1 != 2 );
delay_ms(100);
if(dubl1==1)//сработал нижний датчик
{…….
зажигаем диод
…….
}
}
}
Извиняюсь, много получилось 🙂
Как минимум в этой строчке ошибка if ((tim1=10)&&(dubl1==0)&&(dubl2==0)) //сработал 1 раз
А вообще начните с того, что запустите один датчик отдельно, посмотрите работает ли он корректно. Из прерываний убрать все вычисления, их место в основном цикле
Ок. Спасибо, попробую.
У меня в коде стоит не = а больше или равно, почему-то при копировании так получилось.
Можно еще, безопасности ради :-), поменять порядок вычислений с tim = tim*5/10/58 на tim = tim/580*5
Просто, такие вычисления выполняються в том же порядке, в каком записаны. И если переменная tim содержит достаточно большое значение — при умножении на 5 полученное значение вызовет переполнение. А если поменять порядок — то переполнения уже не должно быть.
А почему вот такая ошибка получается при компилировании?
«И еще, попробовал сделать в место:
tim2 = TCNT1;
tim2 = tim2*5/10/58;
написать tim2 = TCNT1*5/10/58; компилятор выдает предупреждение: overflow is possible in 16 bit multiplication, casting to ‘long’ may be required
Хотя volatile long tim2 = 0; объявлена в глобальных переменных.»
У вас использовано неявное преобразование 16 битной переменной к 32 битной, об этом скорее всего он и говорит.
10940
9604
10137
8347
10227
11185
8710
8836
9742
11762
11270
11049
9511
9634
10383
10014
10148
11057
8697
9094
9292
10305
Вот лог измерений таймера(без преобразования в сантиметры). Показания как видно скачут. Где еще можно порыться?
Все разобрался. Оказывается датчик очень прихотлив к питанию. Взял отдельное питание с USB компа. Показания сразу встали в норму.
🙂 такие вещи нужно в первую очередь проверять
Здравствуйте!
Решил поэкспериментировать с таким же модулем. Вопрос к автору статьи. Скажите, тактирование МК ведется от встроенного генератора или кварц нужен? Если от встроенного, то на какую частоту настраивать его в проекте? А если внешний-то какой? Спасибо за ответ.
все проекты заточены под внешний кварц 8мгц, http://avr-start.ru/fuse-bit-%D0%B2-avr-%D0%BC%D0%B8%D0%BA%D1%80%D0%BE%D0%BA%D0%BE%D0%BD%D1%82%D1%80%D0%BE%D0%BB%D0%BB%D0%B5%D1%80%D0%B0%D1%85/
admin, Собрал, все работает исправно. Весьма полезные статьи для начинающих. Благодарю Вас.
а как это все сделать без использования прерывания?
никак
Собрал на Atmega16 данный урок, вывод расстояния сделал на дисплей+еще светодиодов дорожку сделал. Все работает. Уверенное отражение сигнала идет только от перпендикулярных предметов. Сам датчик купил за 45 рублей с доставкой из китая. Очень важный для себя момент открыл про объявление переменных с volatile. Остался довольным проделанной работой.
Как вы вывели на ноут показания дальномера?
через терминал
попробовал без юарта на меге8, не формирует импульс 10мкс для датчика, проверял осциллографом, может фьюзы не такие? какие фьюзы выставляли, кроме частотных(8MHz)? Как возможно привязать результат измерений с датчика к определенному порту, например нужно собрать робота который отъезжает от препятствий?!
не формировать может только если нога сдохда, или не так измеряешь
Здравствуйте , у меня похожая схема но с выводом на LCD дисплей , мне подойдет ваш код?
Помогите!!! Китайский модуль HC-SR04, atmega8 от внутреннего тактового генератора на 8Мгц. Не могу адекватно подобрать таймер. Уже даже с UART разобрался. Сил нет
в статье же есть настройки, в чем собственно вопрос?
Я так понимаю, что прерывания като привязаные к таймеру1, Можно ли и что надо изменить в настройках визора , чтоб привязать прерывания к таймеру0 или таймеру2?
привязаны к таймеру, как настраивать таймеры есть отдельная статья http://avr-start.ru/?p=414
на какую частоту настроен таймер в даном случае?)