Skocz do zawartości

Arduino - pomoc z programem do buttonbox'a


Pomocna odpowiedź

Owszem - chociaż jak sam zauważyłeś, można użyć szybszych konstrukcji niż digitalRead/digitalWrite. Tu akurat idealnie pasuje rodzinka digitalReadFast/digitalWriteFast.

  • Lubię! 1

Tak wygląda implementacja digitalWrite w Arduino. Ciekawe że jest tu użyta funkcja cli (funkcja nie funkcja, ponoć po przekompilownaiu jest to jedna linia asemblera). Ale ustawia flagę wyłączająca wszystkie przerwania. Czy mimo to działa trzeba by sprawdzić.

void digitalWrite(uint8_t pin, uint8_t val)
{
	uint8_t timer = digitalPinToTimer(pin);
	uint8_t bit = digitalPinToBitMask(pin);
	uint8_t port = digitalPinToPort(pin);
	volatile uint8_t *out;

	if (port == NOT_A_PIN) return;

	// If the pin that support PWM output, we need to turn it off
	// before doing a digital write.
	if (timer != NOT_ON_TIMER) turnOffPWM(timer);

	out = portOutputRegister(port);

	uint8_t oldSREG = SREG;
	cli();

	if (val == LOW) {
		*out &= ~bit;
	} else {
		*out |= bit;
	}

	SREG = oldSREG;
}

Ciekawe jest to, że w przykładzie od Arduino, w funkcji przerwania specjalnie nie jest używany zapis digitalWrite:

const byte ledPin = 13;
const byte interruptPin = 2;
volatile byte state = LOW;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
}

void loop() {
  digitalWrite(ledPin, state);
}

void blink() {
  state = !state;
}

Możliwe że jest to zabezpieczenie przed kolizją w zapisie/odczycie gdyby operacja odczytu została wyprzedzona zapisem.

7 minut temu, Gieneq napisał:

Ale ustawia flagę wyłączająca wszystkie przerwania.

I za chwilę przywraca stan sprzed ustawienia.

Pewnie dlatego, że zapis typu

PORTx |= BIT

to w rzeczywistości trochę więcej niż jedna instrukcja, odpowiednik mniej więcej:
 

register uint8_t a = PORTx;
a |= BIT;
PORTx = a;

i między odczytem a zapisem PORTx istnieje możliwość zmiany stanu jakiegoś pinu.

5 godzin temu, slon napisał:

Dziękuje za odpowiedzi.  Czy taki zapis z punktu widzenia istoty przerwania będzie poprawny ? (pomijając, że można by użyć bitSet() lub PORTB)


ISR(PCINT0_vect) {
if(digitalRead(12) == HIGH) {
  liczynik=1;
  digitalWrite(13, HIGH);
} else {
  digitalWrite(13, LOW);
 }
}

 

Cześć,

niezbyt poprawny bo digitalRead i digital Write są same wykonywane za pomocą przerwań. Lepiej ustaw sobie jakąś zmienną typu bool na true i na jej podstawie rób digitalWrite w pętli głównej.

Pozdrawiam

Nie spodziewałem się , że temat tak się rozwinie aczkolwiek bardzo jestem z tego zadowolony. Więc wracając do przerwań.  

ISR(PCINT0_vect)

 Ja cały czas myślę o tych przerwaniach i tu jak widać digitalWrite występuję. Jaka jest różnica między Pin- change Interrupt a External Interrupt od strony programowej ? Nie chodzi mi o to , że jedne są na wszystkich pinach arduino a pozostałe na dwóch.

@slon Nie ma żadnej. Przerwanie to przerwanie - a w jaki sposób zostało wywołane to dla programu nieistotne (nawet, jeśli sam program wywołał przerwanie).

digitalWrite może wystąpić - nie ma nic wspólnego z przerwaniami (poza tym, że blokuje na chwilę przerwania co w tym przypadku nie ma żadnego wpływu na działanie, ponieważ w trakcie wywołania procedury ISR przerwania i tak są zablokowane). Pisałem o digitalWriteFast dlatego, że wykonuje się dużo szybciej - argumenty są znane w czasie kompilacji czyli nie trzeba wywoływać funkcji w stylu "a jaki to port i jaki bit i czy mam zrobić or czy and", a ewentualnym blokowaniem przerwań ma się interesować programista (do tego jest makro ATOMIC_BLOCK w util/atomic.h w bibliotece avr-libc).

Oczywiście to co piszę to pewne uproszczenie (mogę w procedurze ISR ręcznie odblokować przerwania jeśli z jakichś powodów jest mi to potrzebne) - ale możemy założyć, że o ile nie stosujemy jakichś tricków to wszystko się odbywa w najprostszy sposób:

a) na stosie zapamiętywane są wartości najważniejszych rejestrów z licznikiem instrukcji na czele oraz blokowane są przerwania

b) procesor wykonuje procedurę ISR

c) wartości rejestrów odtwarzane są ze stosu w ten sposób, że program nawet nie wie że coś go przerwało

Problem zaczyna się pojawiać, gdy następne przerwanie przychodzi w czasie wykonywania pierwszego. Co prawda przerwania są zablokowane, ale w przypadku AVR-ów zapamiętywane jest przyjście przerwania i po wyjściu z pierwszego procesor natychmiast (no, prawie...) wchodzi w obsługę drugiego. Niestety - zapamiętywane jest tylko jedno przerwanie; jeśli w czasie obsługi przerwania przyjdzie więcej niż jedno - pozostałe zostaną zignorowane.

Czy to wyjaśnienie wystarczy?

  • Lubię! 1

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...