Skocz do zawartości

Funkcja Input Capture w Arduino


ximot

Pomocna odpowiedź

7 minut temu, farmaceuta napisał:

Ja bym tu bardziej byl sklonny zwalic winne na czujniki...wkoncu czego sie spodziewac po module za piatke?😉

Nawet gdyby tak było, to poprawnie napisany program musi uwzględnić taką sytuację i odpowiednio na nią zareagować, zawieszenie się mikrokontrolera jest chyba najgorszą z opcji. Np budujesz sobie czujnik parkowania w garażu ... program się wysypuje a lampa wciąż pokazuje że "spoko ziomeczku cofaj śmiało, zmieścisz się :D"

Edytowano przez _LM_
Link do komentarza
Share on other sites

1 minutę temu, _LM_ napisał:

Zapytaj kolegi @farmaceuta ćwiczyliśmy jakiś czas temu różne warianty timerów, gdzieś są jego posty 😉

Łooo....to byla walka prawie na smierc i zycie...😅 niestety w tym temacie nic nie pomoge bo Input Capture nie byl przez nas testowany...a i wiedzy teoretyczniej tez nie ogarnalem w tym temacie...😕

  • Lubię! 1
Link do komentarza
Share on other sites

To idzie mniej wiecej tak...

if (TCCR1B & (1 << ICES1))

Tutaj tylko sprawdzasz na jakie zbocze ustawiles pin, czyli na jakie zbocze ma zareagowac przerwanie...1 na narastajace, 0 na opadajace...i teraz tak, jesli miales stan nisKi i nagle wystapilo wykrycie zbocza narastajacego zostaje wywolane przerwanie, nastepuje sprawdzenie powyzszego if'a ktory jest prawdziwy i w tym momencie zaczynamy liczyc czas..(powiedzmy ze interesuje nas stan wysoki).

Teraz zerujemy 

TCNT1 = 0;
			

(bo to w nim w rzeczywistosci nastepuje liczenie) i odrazu ustawiamy bit tak zebysmy mogli wykryc zbocze opadajace

TCCR1B &= ~(1 << ICES1); 

 czyli moment w ktorym przechodzimy z high na low...(miedzy czasie nasz timer ciagle liczy i aktualizuje TCNT1). 

Nastepuje zmiana czyli wykrylismy nasz opadajace zbocze, i wywolanie przerwania...sprawdzamy warunek if, ale ten juz nie jest prawdziwy bo zmienilismy bit zbocza, wiec wykona sie else...i tak

TCCR1B |= (1 << ICES1);

znowu zmieniamy bit zeby wykryc zbocze narastajace...i pobieramy wynik bo nasz pomiar stanu wysokiego sie zakonczyl...

wynik = ICR1;
		

To co znajduje sie w rejestrze ICR1 jest przekopiowana wartoscia z TCNT1...dlatego zerowanie tego rejestru na poczatku stanu ktory mierzymy jest wazne (liczymy wtedy od zera)...z tego co jeszcze wyczytalem to nie zaleca sie korzystac z jakiegokolwiek trybu podczas korzystania z IC...uzywamy zwylkego trybu licznika..mozna jeszcze dodac jakas opcje zmniejszajaca szumy (zaklocenia)...wtedy wystapienie przerwania jest opoznione o 4 cykle zegara (zgodnie z ustawionym preskalerem)...nie wiem czy Ci to cokolwiek pomoglo, czy takiej odpowiedzi chciales, no ale napisalem...juz przepadlo😅 (jak cos to mnie prostowac..)

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Przespałem się z tym wszystkim i zakumałem. Teraz piszę jakby troche uporządkowaną bibliotekę, może nie po swojemu, na wzór tego co tu było ale postaram się pare rzeczy dodać, postaram się uwzględnić też ilość przepełnień, gdybym chciał mierzyć większą odległość, choć nie wiem czy dla tego czujnika to ma sens. Jak skończe to wrzuce gotowe pliki, może się komuś przyda jako gotowiec pod HC-SR04

Link do komentarza
Share on other sites

Jeśli będziesz pisał libsa to powinieneś uwzględnić taktowanie mikrokontrolera, bo nie wszyscy wiedzą że może być inne niż 16Mhz 😉 biblioteka powinna również uwzględnić możliwość zawieszenia się samego modułu - pisałem o tym wcześniej. Gdy ją zbudujesz i pokażesz, spróbuj napisać taką samą ale tak aby sterować tym modułem z jednego pinu a jest to możliwe. Dzięki @farmaceuta że opisałeś działanie moich wypocin 😉 

Link do komentarza
Share on other sites

7 minut temu, Krawi92 napisał:

postaram się uwzględnić też ilość przepełnień, gdybym chciał mierzyć większą odległość, choć nie wiem czy dla tego czujnika to ma sens. 

Dla tego czujnika pewnie przepelnie nigdy nie zostanie osiagniete, ale jak najbardziej mozna kombinowac...wtedy jest to uniwersalne dla roznych przypadkow np. guzikow gdzie juz mozna trzymac dlugo wcisniety...ale to akurat proste..dodac przerwanie od przepelnienia i dodawac max wartosc jaka miesci sie w rejestrze TCNT1 do wyniku...

Link do komentarza
Share on other sites

Czy przepełnienie nastąpi to zależy od prescalera. Mnie przy większej odległości się przepełniał, sygnalizowała to dioda, dlatego żeby nie nastąpiło to zwiększyłem prescaler do np: 64. Ja akurat jade na oscylatorze 8mhz bo mam tylko 1 kwarc na chacie już zajęty 😄 Przy prescalerze 8 lekko migotał LCD, wiadomo, zmniejszyła sie niby precyzja, ale dla takiego czujnika to akurat mała różnica. Akurat wrzuciłem fajna możliwość zmiany portu i pinu w 1 miejscu dla pinow trig i echo. Postaram się jak najbardziej uniwersalnie to zrobić. 

Link do komentarza
Share on other sites

14 minut temu, farmaceuta napisał:

Dla tego czujnika pewnie przepelnie nigdy nie zostanie osiagniete

Będzie jeśli sygnał nie powróci lub częstotliwość timera będzie wysoka. Im większa częstotliwość pracy timera tym większa rozdzielczość pomiarowa* i np: zamiast na mieć na 1000mm 1000 zliczeń można ich mieć 10000 itd. 

 

*Rozdzielczość != dokładność

Edytowano przez _LM_
Link do komentarza
Share on other sites

Ok, na razie wrzucę co mam, liczę na podpowiedzi co by tu ulepszyć. Jeszcze nie zabrałem się za obsługę przepełnień i muszę pomyśleć jak rozwiązać w jakiś uniwersalny sposób przeliczanie na cm. Jest to uzależnione od prescalera i taktowania. Tutaj na sztywno zrobiłem dla 8Mhz i prescalera 64. Użyłem timera programowego, żeby sobie HC tykał. Proszę wytknąć ew błędy. 

hcsr04.c

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

volatile uint16_t result; // zmienna przechowywująca wynik
volatile uint8_t start_meas; // zmienna pomocnicza
volatile uint8_t timer; // timer programowy

void hc_Init(void){
// Kierunki Pinów
	DDR(PORT_TRIG) |= (1 << TRIG); // Trig jako wyjście
	DDR(PORT_ECHO) &=~ (1 << ECHO); // Echo jako wejście
	PORT(PORT_TRIG) &=~ (1 << TRIG); // Stan niski na Trig
	PORT(PORT_ECHO) |= (1 << ECHO); // Podciągnięcie do VCC Echo
// Konfiguracja przerwań
	TCCR1B |= (1 << CS11)|(1 << CS10); // prescaler 64.. 1 impuls 8uS
	TIMSK1 |= (1 << ICIE1); // przerwanie input capture
// Konfiguracja timera dla timera programowego
	TCCR0A |= (1 << WGM01); // CTC Timer0
	TCCR0B |= (1 << CS00)|(1 << CS02); // Prescaler 1024
	OCR0A = (F_CPU/1024UL/31UL)-1; // Przerwanie co ok 30ms
	TIMSK0 |= (1 << OCIE0A); // zezwolenie na przerwanie od przepełnienia
}

void meas(void){ // Funkcja wywołująca pomiar

// Rozpoczęcie pomiaru z użyciem timera programowego
if (!timer){ // jesli timer jest 0
	if(!start_meas){ // i jesli zmienna start_meas jest 0
		PORT(PORT_TRIG) |= (1 << TRIG); // Stan wysoki na trig przez 10us
		_delay_us(15);
		PORT(PORT_TRIG) &=~ (1 << TRIG);
		TIFR1 &= ~(1 << ICF1); // kasowanie flagi przerwania na wypadek gdyby sie "uchowalo"
		TIMSK1 |= (1 << ICIE1); // zezwolenie na przerwanie od przechwytu
		TCNT1 = 0; // zerowanie licznika
		TCCR1B |= (1 << ICES1); // Przerwanie na zboczu narastającym
		start_meas = 1; // zakonczono wywolanie pomiary
		timer = 10; // czasookres pomiaru .. ok 300ms
	}
 }
}
uint16_t meas_result(){
	return result/7; // rezultat funkcji w cm
}

ISR(TIMER1_CAPT_vect){
   if (TCCR1B & (1 << ICES1)){ // jesli zbocze narastajace
	   TCNT1 = 0; // zerujemy licznik
	   TCCR1B &=~ (1 << ICES1); // czekamy na zbocze opadajace
	   TIMSK1 |= (1 << TOIE1); // wlaczamy przerwanie od przepelnienia
   }else{ // jesli wystapilo zbocze opadajace
	   TIMSK1 &=~ (1 << TOIE1); // wylaczamy przerwanie od przepelnienia
	   TIMSK1 &=~ (1 << ICIE1); // wylaczamy przerwanie od przechwytu
	   TCCR1B |= (1 << ICES1); // znow czekamy na zbocze narastajace
	   result = ICR1; // zapisujemy wartosc ICR1 do zmiennej result
	   start_meas = 2; // pomiar gotowy do odczytania
 }
}

ISR(TIMER0_COMPA_vect){ // Timer programowy
	if(timer)timer--;
}

hcsr04.h

#ifndef HCSR04_H_
#define HCSR04_H_
//PORTy
#define PORT(x) SPORT(x)
#define SPORT(x) (PORT##x)
//PINy
#define PIN(x) SPIN(x)
#define SPIN(x) (PIN##x)
// Kierunek
#define DDR(x) SDDR(x)
#define SDDR(x) (DDR##x)

#define PORT_TRIG D // PORT pinu trigger
#define TRIG 5 // nr pinu
#define PORT_ECHO B // PORT pinu ECHO (pin ICP)
#define ECHO 5 // nr pinu

void hc_Init(void); // Inicjalizacja modułu i przerwań
void meas(void); // wywołanie pomiaru
uint16_t meas_result(); // zwrot pomiaru

extern volatile uint16_t result; // specyfikator extern by zmienne byly widoczne w innych plikach
extern volatile uint8_t start_meas;

#endif /* HCSR04_H_ */

przykładowe rozwiązanie w main.c

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


int main (void){
	lcd_init(); // inicjalizacja lcd
	hc_Init(); // inicjalizacja HC SR04
	lcd_locate (0,3);
	lcd_str("Odleglosc");
	sei(); // zezwolenie na przerwania globalne

	while(1){
		meas(); // wywolanie funkcji uruchamiajacej pomiar
	if(start_meas == 2){ // Jesli wykonano pomiar
	lcd_locate(1,5);
	lcd_int(meas_result()); // Wyswietl rezultat fukncji
	lcd_str("cm ");
	start_meas=0; // zacznij pomiar od nowa
	}
 }
}

 

Link do komentarza
Share on other sites

Wrzucam lekko poprawioną wersje. Niestety nie ogarnąłem na razie jak to zrobić uniwersalnie dla każdego taktowania. Gdyby ktoś chciał inne taktowanie niż 8Mhz to musi ustawić prescaler tak, żeby miał czas impulsu 1uS 😛 Jedynie co to porty Trig i Echo można w pliku nagłówkowym sobie zmienić. 

hcsr04.c

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

volatile uint16_t result; // zmienna przechowywująca wynik
volatile uint8_t start_meas=0; // zmienna pomocnicza
volatile uint8_t timer; // timer programowy

void hc_Init(void){
// Kierunki Pinów
	DDRB |= (1 << PB1);
	DDR(PORT_TRIG) |= (1 << TRIG); // Trig jako wyjście
	DDR(PORT_ECHO) &=~ (1 << ECHO); // Echo jako wejście
	PORT(PORT_TRIG) &=~ (1 << TRIG); // Stan niski na Trig
	PORT(PORT_ECHO) |= (1 << ECHO); // Podciągnięcie do VCC Echo
// Konfiguracja przerwań
	TCCR1B |= (1 << CS11); // prescaler 8..Przy 8Mhz = 1 impuls 1uS
	// prescaler 8 przy 8Mhz = 1uS
	TIMSK1 |= (1 << ICIE1); // przerwanie input capture
	TCCR1B |= (1 << ICES1); // Przerwanie na zboczu narastającym
// Konfiguracja timera dla timera programowego
	TCCR0A |= (1 << WGM01); // CTC Timer0
	TCCR0B |= (1 << CS00)|(1 << CS02); // Prescaler 1024
	OCR0A = (F_CPU/1024UL/31UL)-1; // Przerwanie co ok 30ms 8000000/1024/31-1
	TIMSK0 |= (1 << OCIE0A); // zezwolenie na przerwanie od porównania
}

void meas(void){ // Funkcja wywołująca pomiar

// Rozpoczęcie pomiaru z użyciem timera programowego
if (!timer){ // jesli timer jest 0
	if(!start_meas){ // i jesli zmienna start_meas jest 0
		PORT(PORT_TRIG) |= (1 << TRIG); // Stan wysoki na trig przez 10us
		_delay_us(10);
		PORT(PORT_TRIG) &=~ (1 << TRIG);
		start_meas = 1; // zakonczono wywolanie pomiary
		timer = 10; // czasookres pomiaru .. ok 300ms
	}
 }
}
uint16_t meas_result(){

	return (result+9)/56UL; // rezultat funkcji w cm(Operacja w nawiasie w celu dokladnej kalibracji)
}

ISR(TIMER1_CAPT_vect){ // procedura obslugi przerwania od przechwytu
	if(TCCR1B & (1 << ICES1)){ // Jesli zbocza narastające
		TCNT1=0; // zerujemy liczenik
	    TCCR1B ^= (1 << ICES1); // zmieniamy bit ICES1 na przeciwny
	}else{ // jesli jednak zobacze opadające
		result = ICR1; // zapisujemy zawartosc ICR1 do zmiennej
		TCCR1B ^= (1 << ICES1); // zmieniamy bit ICES1 na przeciwny
		start_meas = 2; // flaga informuje ze pomiar gotowy do wyswietlenia
 }
}

ISR(TIMER0_COMPA_vect){ // Timer programowy
	if(timer)timer--;
}

main.c

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


int main (void){
	
	lcd_init(); // inicjalizacja lcd
	hc_Init(); // inicjalizacja HC SR04
		lcd_locate (0,4);
		lcd_str("Dalmierz");
	sei(); // zezwolenie na przerwania globalne
	_delay_ms(20); // Opoznienie 20ms, brak opóźnienia powoduje czasem zawieszenie sie czujnika
	while(1){
		meas(); // wywolanie funkcji uruchamiajacej pomiar
	if(start_meas == 2){ // Jesli wykonano pomiar
	lcd_locate(1,0);
	lcd_str("     ");
	lcd_locate(1,5);
	lcd_int(meas_result()); // Wyswietl rezultat funkcji
	lcd_str("cm        ");
		if(meas_result() > 400){ // Jeśli pomiar przekroczy 400cm
			lcd_locate(1,0);
			lcd_str("Za daleko !!!!!!");
		}
		if(meas_result() < 3){ // Jesli pomiar poniżej 3 cm
			lcd_locate(1,0);
			lcd_str("Za blisko !!!!!!");
		}
	start_meas=0; // zacznij pomiar od nowa
	}
 }
}

Może komuś się kiedyś przyda, jak będzie chciał odpalić sobie czujniczek 😛

Link do komentarza
Share on other sites

11 godzin temu, Krawi92 napisał:

Gdyby ktoś chciał inne taktowanie niż 8Mhz to musi ustawić prescaler tak, żeby miał czas impulsu 1uS 😛

chyba 10µS chodzi Ci o ten kawałek kodu?

if (!timer){ // jesli timer jest 0
	if(!start_meas){ // i jesli zmienna start_meas jest 0
		PORT(PORT_TRIG) |= (1 << TRIG); // Stan wysoki na trig przez 10us
		_delay_us(10);
		PORT(PORT_TRIG) &=~ (1 << TRIG);
		start_meas = 1; // zakonczono wywolanie pomiary
		timer = 10; // czasookres pomiaru .. ok 300ms
	}

 

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.