Skocz do zawartości

Zdobądź Proxxona FBS 240/E - #4 - Uniwersalna funkcja


Pomocna odpowiedź

Treker, myślę, że tak. Generalnie wszystkie biblioteki arduino praktycznie są napisane typowo jako kasy - servo, stepper, serial. Napisałem ostatnio np. klasę do obsługi średniej kroczącej (zero sprzętu), wtedy po zaincludowaniu pliku .h kod wygląda tak:

deklaracja:

SMA average(L); //konstruujemy obiekt klasy SMA, parametrem konstruktora jest ilość próbek średniej

używanie:

average.add(probka); //dodajemy próbkę do bufora, reszta zostaje automatycznie przesunięta
zmienna = average.get(); //odczytujemy średnią z ostatnich L próbek

Sama średnia obsługuje na razie tylko twory mojej innej klasy, Cartesian, która to przechowuje współrzędne w układzie kartezjańskim, ale nic nie stoi na przeszkodzie, by oprzeć klasę o szablon - wtedy będzie mogła liczyć średnią z dowolnych zmiennych. Właśnie obiektowość w Arduino jest świetna - tak mi teraz przemknęło przez myśl, by napisać może jakiś odcinek kursu arduino o klasach właśnie. Pozwala to na nieporównywalnie prostszą względem czystego C skalowalność kody, staje się on też prostszy w wykorzystaniu na inne platformy (jeśli tylko znajdzie się dla nich kompilator C++, na przykład STMy).

Chciałbym Wam przedstawić funkcję do obsługi przycisku (ang. switch) w przerwaniu INT0 na pinie PD2 portu D mikrokontrolera AVR firmy ATMEL ATmega8. Kod źródłowy napisany jest w języku C dla 8-bitowych mikrokontrolerów AVR. Podstawowe informacje o tym mikrokontrolerze można znaleźć w jego nocie katalogowej (ang. datasheet) wpisując w przeglądarkę internetową frazę "ATmega8 datasheet".

 

Takie rozwiązanie pozwala na użycie tegoż przycisku jako przycisk ON/OFF naszego robota lub innego urządzenia 🙂

 

Przedstawię teraz konfigurację przycisku i zmiennej globalnej do obsługi przerwania:

// Definiujemy przycisk, który znajduje się na pinie 2 portu D, czyli PD2.
#define Przycisk_PIN (1<<PD2)
#define Przycisk (PIND & Przycisk_PIN)

volatile int jazda = 0;

 

Teraz przechodzimy do "Głównej funkcji programu" i ustawiamy pin PD2 jako wejście, ponieważ będzie on odbierał sygnały, które "wygeneruje" przycisk (stan bliski VCC (+5V), czyli logiczna jedynka (1) lub stany bliskie GND, czyli logiczne zero (0)). Musimy też pamiętać, że aby korzystać z przerwań musimy włączyć globalne przerwania --> sei();

//--- Główna funkcja programu ---
int main(void) {
DDRD &= ~Przycisk_PIN;		// Wejcie - Przycisk
PORTD |= Przycisk_PIN;		// Pull-Up wewnętrzny (rezystor podciągający do VCC, dlatego nie trzeba stosować fizycznego rezystora podciągającego)

sei();						// Włączenie globalnych przerwań
}

 

Przejdźmy teraz do obsługi przerwania INT0 na pinie PD2:

//--- Obsługa przerwania z INT0 ---
ISR(INT0_vect) {

GICR &= ~(1<<INT0);	// Wyłączamy obsługę przerwania na PD2 - drgania styków spowodowałyby wielokrotne jego wywołanie

jazda ^= 1;			// Zmieniamy stan flagi

GICR |= (1<<INT0);	// Włączamy z powrotem obsługę przerwania
}

 

Jeszcze napiszemy uproszczony kod źródłowy do obsługi tego przerwania:

// --- Pętla nieskończona programu ---
while(1) {

		if(jazda) {     	// Sprawdzamy czy "jazda" jest 1-ką lub 0-em
		_delay_ms(50);		// Eliminacja drgania styków
		if(jazda) {
			LED1_ON;     	// Dioda LED świeci
		}else{
			LED1_OFF;     	// Dioda LED nie świeci
		}
		}
}

 

Drgań styków możemy się jeszcze pozbyć sprzętowo, przez realizację podłączenia przycisku do mikrokontrolera. Mianowicie przez użycie odpowiednich rezystorów jak i kondensatorów - na Forum było to omawiane wiele razy (a nawet w #3 odsłonie tego konkursu).

 

Mam nadzieję, że ta prosta funkcja do obsługi przycisku w przerwaniu jako przycisk ON/OFF pozwoli wielu początkującym okiełznanie swojego robota 🙂

 

Pozdrawiam, Adam (aixI).

Tak, wiem. Ale wżywamy tego przycisku jak włącznik ON/OFF i tyle - nic więcej ma nie robić, tylko spełniać swoją funkcję, a taki drobny "delay" przy tym nie przeszkadza.

Nawet jeśli, to nie tłumaczy to użycia delay'a w przerwaniu. To jest po prostu zły nawyk i nie należy go powielać.

Mam nadzieję, że to zmienisz. Ja daję autodelet'y na swoje posty, bo szkoda tutaj śmiecić.

W sumie jakby się tak dobrze zastanowić, to jeżeli mają to być jakieś dobre, pomocne funkcje, które mają nauczać początkujących i wpajać im od samego początku dobre nawyki, to jest to prawdą, że jednak nie powinno się stosować "delay'a" w przerwaniu - sam wiem, że się go tam nie powinno stosować, ale niestety nie usprawiedliwia mnie to. Poprawiłem i ustawiłem autodel.

Pozdrawiam,

Nie chcę być namolny, ale usuwając tego delay'a i nie zastępując go inną metodą eliminacji drgań styków (a są takie 😉 ), problemu wcale się do końca nie pozbywasz. Owszem, kod staje się nieco poprawniejszy, ale inny problem powraca.

Bobby, ok w takim razie dopuszczam również takie rozwiązania.

aixI, zwracam też uwagę, że to rozwiązanie nie do końca jest funkcją. Jest to kilka "rozrzuconych" kawałków kodu. Oczywiście rozumiem, że tutaj inaczej ciężko byłoby to rozwiązać, ale proszę, aby wszyscy pamiętali, że należy ograniczać takie rozdrabnianie.

aixI, piotreks-89, nie wygaszajcie postów, niech zostaną widoczne dla wszystkich.

int8_t uchyb(uint8_t pomiary[], uint8_t czujniki, uint8_t prog)
{
static int8_t wagi[8];						//tablica zawierajaca wagi poszczególnych czujników
if (wagi[0] == 0)							//wyliczenie wag czujników przy pierwszym wywołaniu funkcji
{
	for (uint8_t i=0; i<(czujniki/2); i++)
	{
		wagi[i] = 1 << ((czujniki / 2) - i);
		wagi[czujniki-i-1] = -wagi[i];
	}
}

int8_t uchyb = 0;
uint8_t ile = 0;
for (uint8_t i=0; i<czujniki; i++)
{
	if (pomiary[i] < prog)					//kierunek znaku nierówności zależy od reakcji czujnika na kolor podłoża
	{										// < dla sytuacji gdy linia daje niskie wartości z ADC
		uchyb += wagi[i];					// > dla sytuacji gdy linia daje wysokie wartości z ADC
		ile++;
	}
}
if (ile>1)
	uchyb /= ile;
return uchyb;
}

Funkcja zwraca wartość uchybu dla regulatora P(ID) na podstawie tablicy z wartościami z przetwornika ADC.

Argumentu funkcji:

1. nazwa tablicy z pomiarami z ADC

2. liczba czujników

4. próg rozpoznawania kolorów

przyjąłem 2 założenia: 8-bitowy odczyt ADC i max. 8 czujników

Podaję też cały program wykorzystujący tę funkcję, a także z kodem do generowania tablicy z pomiarami ADC w przerwaniach.

Poniższy kod jest przeznaczony dla mikrokontrolerów ATmega innych niż: ATmega8, ATmega8A, ATmega128, ATmega406. Z oczywistych względów nie będzie działał tez na mikrokontrolerach, które nie są wyposażone w ADC.

#include <avr/io.h>
#include <avr/interrupt.h>

#define SENSORS	8		//liczba czujników [max. 8]

volatile uint8_t reading[SENSORS];   //tablica przechowująca odczyty ADC

void Init_ADC();
uint8_t potega(uint8_t podstawa, uint8_t wykladnik);
int8_t uchyb(uint8_t pomiary[], uint8_t czujniki, uint8_t prog);

int main(void)
{
Init_ADC();
int8_t error;
while (1)
{
	error = uchyb(reading, SENSORS, 128);
}
}

void Init_ADC()
{
ADMUX |= (1<<REFS0);				//VCC jako napięcie odniesienia z pinem AREF podłączonym do GND przez kondensator
ADMUX |= (1<<ADLAR);				//przesunięcie wyniku do lewej -> 8-bitowy odczyt wyniku
ADCSRA |= (1<<ADPS2) | (1<<ADPS1);	//prescaler = 64 -> przy 16MHz daje to 250kHz dla ADC
ADCSRA |= (1<<ADIE);				//włączenie przerwań od ADC
ADCSRA |= (1<<ADEN);				//włączenie przetwornika ADC
ADCSRA |= (1<<ADSC);				//start konwersji
sei();								//globalne zezwolenie na przerwania
}

ISR(ADC_vect)
{
static uint8_t i;					//
reading[i] = ADCH;
i++;
i%=SENSORS;
ADMUX = (ADMUX & 0b11111000) | i;
ADCSRA |= (1<<ADSC);
}

int8_t uchyb(uint8_t pomiary[], uint8_t czujniki, uint8_t prog)
{
static int8_t wagi[8];						//tablica zawierajaca wagi poszczególnych czujników
if (wagi[0] == 0)							//wyliczenie wag czujników przy pierwszym wywołaniu funkcji
{
	for (uint8_t i=0; i<(czujniki/2); i++)
	{
		wagi[i] = 1 << ((czujniki / 2) - i);
		wagi[czujniki-i-1] = -wagi[i];
	}
}

int8_t uchyb = 0;
uint8_t ile = 0;
for (uint8_t i=0; i<czujniki; i++)
{
	if (pomiary[i] < prog)					//kierunek znaku nierówności zależy od reakcji czujnika na kolor podłoża
	{										// < dla sytuacji gdy linia daje niskie wartości z ADC
		uchyb += wagi[i];					// > dla sytuacji gdy linia daje wysokie wartości z ADC
		ile++;
	}
}
if (ile>1)
	uchyb /= ile;
return uchyb;
}

@Edit 14-07-2015: poprawiłem potęgowanie ze znaku ^, który w języku C jest operacją XOR, na przesunięcie bitowe. Podziękowania dla Elvisa za wyłapanie błędu i propozycję rozwiązania.

Ja zaproponowałem funkcję liczącą potęgę jednak rozwiązanie Elvisa jest dużo prostsze.

uint8_t potega(uint8_t podstawa, uint8_t wykladnik)
{
if (wykladnik == 0)
	return 1;
else 
	return podstawa * potega(podstawa, (wykladnik - 1));
}

Warto zwrócić uwagę, że ta funkcja przyjmuje i zwraca zmienne 8-bitowe bez znaku, czyli wartości 0-255. Tak jak wykorzystana jest ona w funkcji głównej będzie działać poprawnie dla maks. 15 czujników.

Hudyvolt, zamiast ciągle uruchamiać od nowa konwersję:

ADCSRA |= (1<<ADSC);

Można ustawić przetwornik w tryb 'Free run' (podczas jego inicjalizacji) poprzez ustawienie bitu 'ADFR', w taki sposób:

ADCSRA |= (1<<ADFR);

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...