Подключение дальномера HC-SR04 к ATmega16

Как подключить ультразвуковой датчик измерения расстояния HC-SR04 к ATmega16 и передать данные через USART/UART на компьютер

Очень часто нам нужно подключить датчик измерения расстояния HC-SR04 к ATmega16, Arduino или другому микроконтроллеру и отображать данные на экран или передать через USART/UART на наш компьютер.
В данной статье мы решим эти проблемы, а именно:

  1. подключим HC-SR04 к ATmega16;
  2. подключим радио модуль TB387 к ATmega16 или напрямую, или другой имеющийся радио модуль как HC-11/12 или FS1000A/XD-FST;
  3. напишем прошивку для ATmega16;
  4. проверим работоспособность.

Схема подключения модуля TB387 и датчика движения HC-SR04 к ATmega16

Схема подключения датчика движения HC-SR04 к ATmega16

Пример программы в Atmel Studio 7

uart.h

#ifndef UART_H_
#define UART_H_

void USARTInit(unsigned int);
void USARTTransmitChar(char);
void USARTTransmitString(char*);
void USARTTransmitStringLn(char*);
char USARTReceiveChar(void);

#endif /* UART_H_ */

uart.c

#include <avr/io.h>
#include "uart.h"

void USARTInit(unsigned int ubrr) {
  //  нормальный асинхронный двунаправленный режим работы
  //  UBRR = f / (16 * band)
  //  Установка скорости
  UBRRH = (unsigned char)(ubrr>>8);
  UBRRL = (unsigned char)(ubrr);

  //  RXC         - завершение приёма
  //  |TXC        - завершение передачи
  //  ||UDRE      - отсутствие данных для отправки
  //  |||FE       - ошибка кадра
  //  ||||DOR     - ошибка переполнение буфера
  //  |||||PE     - ошибка чётности
  //  ||||||U2X   - Двойная скорость
  //  |||||||MPCM - Многопроцессорный режим
  //  ||||||||
  //  76543210
  UCSRA = 0;
  
  //  RXCIE       - прерывание при приёме данных
  //  |TXCIE      - прерывание при завершение передачи
  //  ||UDRIE     - прерывание отсутствие данных для отправки
  //  |||RXEN     - разрешение приёма
  //  ||||TXEN    - разрешение передачи
  //  |||||UCSZ2  - UCSZ0:2 размер кадра данных
  //  ||||||RXB8  - 9 бит принятых данных
  //  |||||||TXB8 - 9 бит переданных данных
  //  ||||||||
  //  76543210

  //  разрешен приём и передача данных по UART
  UCSRB = 1<<RXEN | 1<<TXEN;
  
  //  URSEL        - всегда 1
  //  |UMSEL       - режим: 1-синхронный 0-асинхронный
  //  ||UPM1       - UPM0:  1 чётность
  //  |||UPM0      - UPM0:  1 чётность
  //  ||||USBS     - стоп биты: 0-1, 1-2
  //  |||||UCSZ1   - UCSZ0: 2 размер кадра данных
  //  ||||||UCSZ0  - UCSZ0: 2 размер кадра данных
  //  |||||||UCPOL - в синхронном режиме - тактирование
  //  ||||||||
  //  76543210
  //  8-битовая посылка, 2 стоп бита
  UCSRC = 1<<URSEL | 1<<USBS | 1<<UCSZ0 | 1<<UCSZ1;
}

//  Отправка байта
void USARTTransmitChar(char c) {
  //  Устанавливается, когда регистр свободен
  while(!( UCSRA & (1<<UDRE)));
  UDR = c;
}

//  Отправка строки
void USARTTransmitString(char str[]) {
  register char i = 0;
  while(str[i]) {
    USARTTransmitChar(str[i++]);
  }
}

//  Отправка строки
void USARTTransmitStringLn(char str[]) {
  USARTTransmitString(str);
  USARTTransmitChar((char)13);
  USARTTransmitChar((char)10);
}

//  Получение байта
char USARTReceiveChar(void) {
  //  Устанавливается, когда регистр свободен
  while(!(UCSRA & (1<<RXC)));
  return UDR;
}

hcsr04.h

#ifndef HCSR04_H_
#define HCSR04_H_

void initHCSR04();
void stopHCSR04();
void trigHCSR04();
void waitHCSR04();
uint32_t getDistance();

#endif /* HCSR04_H_ */

hcsr04.c

// HC-SR04 к ATmega16
#define F_CPU 11059200UL    // Clock Speed

#define TRIG_DDR  DDRD
#define TRIG_PORT PORTD
#define TRIG_PIN  PIND3

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdbool.h>
#include "hcsr04.h"

uint16_t rising, falling;
uint32_t counts;
uint32_t dist;

bool star = false;

//  Режим захвата
ISR(TIMER1_CAPT_vect) {
  //  ICES1 = 1
  //  фронт - положительный срез
  if (TCCR1B & (1<<ICES1)) {
    //  Сохраняем текущее значение счетчика
	rising = ICR1;
    //  В следующий раз обнаружить спад (отрицательный срез)
    TCCR1B &= ~(1<<ICES1);
    //  Просто код ошибки, что измерение не произошло
    dist = -2;
  } 
  //  ICES1 = 0
  //  спад - отрицательный срез
  else {
    //  Сохраняем текущее значение счетчика
    falling = ICR1;
    //  считаем число тактов
    counts = falling - rising;
    //  определяем растояние в миллиметрах
    dist = (counts * 100) / 813;
    //  уведомляем функцию waitHCSR04() что можно отправить
    //  результат заказчику т.е. растояние определено 
    star = false;
  }
}

//  обработчик прерывания по совпадению значения таймера со значением регистра OCR1A
//  OCR1A = 0xFFFF
ISR(TIMER1_COMPA_vect) {
  //  уведомляем функцию waitHCSR04() что можно отправить
  //  результат заказчику т.е. растояние не определено
  //  HC-SR04 не получил обратный сигнал т.е. растояние
  //  слишком большая, сенсор не подключён или сигнал слишком слабый
  dist = -1;
  star = false;
}

//  инициализация HCSR04
void initHCSR04() {
  //  инициализация ножки TRIG как выход
  TRIG_DDR |= 1<<TRIG_PIN;
  rising = falling = counts = dist = 0;
  //  инициализация таймера 1 TIMER1
  //  CS12,CS11,CS10: Clock Select. Эти 3 бита задают источник тактового сигнала для счетчика.
  //    0    1    0 : F_CPU/8 (с выхода делителя)
  //  ICNC1: Установка этого бита в лог. 1 активирует входной подавитель шума
  //  WGM13,WGM12,WGM11,WGM10:  
  //    0     1     0     0  : режим CTC, максимальное значение OCR1A = 0xFFFF
  TCCR1B |= (1<<ICNC1) | (1<<CS11) | (1<<WGM12); 
  //  TICIE1 = 1: разрешено прерывание захвата таймера/счетчика 1
  //  OCIE1A = 1: разрешено прерывание по совпадению регистра A с состоянием таймера/счетчика1
  TIMSK |= (1<<TICIE1) | (1<<OCIE1A); 
  //  ICES1 = 1: захват входа ICR1 по нарастающему фронту (положительный срез).
  TCCR1B |= (1<<ICES1);
  //  максимальное значение счетчика
  OCR1A = 0xFFFF;
  //  активируем прерывания
  sei();
}

//  отключаем режим захвата
void stopHCSR04() {
  TRIG_DDR &= ~(1<<TRIG_PIN);
  // TIMER1 INIT
  TCCR1B &= ((1<<ICNC1) | (1<<CS11) | (1<<WGM12));
  TIMSK &= ((1<<TICIE1) | (1<<OCIE1A));
  TCCR1B &= ((1<<ICES1));
  OCR1A = 0;
}

//  Генерируем импульс на 12us
void trigHCSR04() {
  TRIG_PORT ^= (1<<TRIG_PIN);
  _delay_us(12);
  TRIG_PORT ^= (1<<TRIG_PIN);
}

//  ждём пока star равно true т.е. завершение измерения
void waitHCSR04() {
  star = true;
  while(star) {
    _delay_us(1);
  }
}

uint32_t getDistance() {
  //  Инициализируем таймер (HCSR04)
  initHCSR04();
  //  Генерируем импульс на 12us
  trigHCSR04();
  //  Ждем, пока растояние не определено
  waitHCSR04();
  //  Останавливаем таймер
  stopHCSR04();
  //  Возвращяем результат
  return dist;
}

main.c

#define F_CPU 11059200UL    // Clock Speed
#define BAUD 9600
#define MYUBRR F_CPU/16/BAUD-1

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include "uart.h"
#include "hcsr04.h"

int main(void) {
  uint32_t d;
  char str[20];
  USARTInit(MYUBRR);
  while (1) {
    d = getDistance();
    sprintf(str, "Distance: %dmm", d);
    USARTTransmitStringLn(str);
    _delay_ms(1000);
  }
}

Результат

Подключение дальномера HC-SR04 к ATmega16

Скачать
Terminal 1.9b — работаем с COM-портом
datasheet ATMEGA16.pdf
Проект в Atmel Studio 7 HC-SR04+UART+ATmega16

Похожие записи

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

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