Содержание
Урок 1. Первый проект
Урок 2. Управление кнопками
Урок 3. Подключение LCD
Урок 4. Использование ШИМ
Урок 5. Таймеры
Урок 6.1. Статическая индикация
Урок 6.2. Динамическая индикация
Урок 7.1. Генерация звука
Урок 7.2. Генерация звука. Продолжение
Урок 8.1. Передача данных через UART
Урок 8.2. Передача данных через UART. Продолжение»
Урок 9. Передача данных через SPI
Урок 10. Изучение АЦП. Простой вольтметр
Урок 11. Получение синуса при помощи ШИМ
Урок 12. Измерение температуры
Урок 13. Внешние прерывания.
Урок 14. Использование отладчика
Урок 15.1. Управление инкрементальным энкодером
Урок 15.2. Управление громкостью, при помощи энкодера
Урок 16. Управление RGB светодиодом
Урок 17. Использование ИК
Урок 18.1. Знакомство с графическим дисплеем
Урок 18.2 Вывод изображения на графический дисплей
Урок 18.3 Вывод русскоязычного текста
Урок 19. Формирование сигнала, при помощи ЦАП (R2R)
Урок 20. Опрос матричной клавиатуры
Урок 21. Сторожевой таймер
Урок 22.1 Воспроизведение wav. Введение.
Урок 22.2 Воспроизведение wav. Продолжение.
Урок 23.1 Работа с внешней памятью
Урок 23.2 Работа с файловой системой Fat

Набирал тут недавно в одном интернет магазинчике комплектуху, для будущих поделок и случайно зацепился взглядом за такую штуку
hc-sr04

Теория работы с устройством: на вход TRIG подается импульс 10мкс, датчик посылает ультразвуковой сигнал 40кГц, который отражается от препятствий и поступает обратно на датчик. На выходе ECHO формируется импульс, длительность которого пропорциональна расстоянию до объекта. Пересчитываем полученную длительность в сантиметры/метры по формуле из даташита

Test distance = (high level time×velocity of sound(340M/S) / 2,
или uS / 58 = centimeters

По подключению все просто, импульс формировать можно любой ножкой для примера PD3, а вот чтобы не пропустить ответный импульс, заведем эхо на ножку внешнего прерывания.
hc-sr04_sch

После того как был послан импульс, смотрим в прерывании что пришло — если передний фронт, то обнуляем таймер, если задний фронт, то считываем время от начала импульса до конца. Полученное время, пересчитываем в сантиметры и выводим в 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м, все в точности соответствует. Только нужно понимать, что если отражаемая поверхность неровная то датчик будет показывать все что угодно.

Прошивка, исходники

41 комментарий: Ультразвуковой измеритель расстояния HC-SR04

  • Матрица на нотик 3 тыщщи и в любом ларьке

  • Я буквально недавно носил в ремонт свой ноутбук, до сих пор под впечатлением :mrgreen:

  • А где можно скачать библиотеку 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 компа. Показания сразу встали в норму.

  • 🙂 такие вещи нужно в первую очередь проверять

  • Здравствуйте!
    Решил поэкспериментировать с таким же модулем. Вопрос к автору статьи. Скажите, тактирование МК ведется от встроенного генератора или кварц нужен? Если от встроенного, то на какую частоту настраивать его в проекте? А если внешний-то какой? Спасибо за ответ.

  • admin, Собрал, все работает исправно. Весьма полезные статьи для начинающих. Благодарю Вас.

  • а как это все сделать без использования прерывания?

  • никак

  • Собрал на Atmega16 данный урок, вывод расстояния сделал на дисплей+еще светодиодов дорожку сделал. Все работает. Уверенное отражение сигнала идет только от перпендикулярных предметов. Сам датчик купил за 45 рублей с доставкой из китая. Очень важный для себя момент открыл про объявление переменных с volatile. Остался довольным проделанной работой.

  • Как вы вывели на ноут показания дальномера?

  • через терминал

  • попробовал без юарта на меге8, не формирует импульс 10мкс для датчика, проверял осциллографом, может фьюзы не такие? какие фьюзы выставляли, кроме частотных(8MHz)? Как возможно привязать результат измерений с датчика к определенному порту, например нужно собрать робота который отъезжает от препятствий?!

  • не формировать может только если нога сдохда, или не так измеряешь

  • Здравствуйте , у меня похожая схема но с выводом на LCD дисплей , мне подойдет ваш код?

  • Помогите!!! Китайский модуль HC-SR04, atmega8 от внутреннего тактового генератора на 8Мгц. Не могу адекватно подобрать таймер. Уже даже с UART разобрался. Сил нет

  • в статье же есть настройки, в чем собственно вопрос?

  • Я так понимаю, что прерывания като привязаные к таймеру1, Можно ли и что надо изменить в настройках визора , чтоб привязать прерывания к таймеру0 или таймеру2?

  • привязаны к таймеру, как настраивать таймеры есть отдельная статья http://avr-start.ru/?p=414

  • на какую частоту настроен таймер в даном случае?)

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Последние комментарии
  • Загрузка...
Счетчик
Яндекс.Метрика