Skocz do zawartości
slon

Odczyt stanu pinu AVR

Pomocna odpowiedź

Mam takie prawdopodobnie proste pytanie na które nie znalazłem jasnej odpowiedzi.  Tak jak w tytule dotyczy odczytu stanu na konkretnym pinie:

1. Czy taki zapis będzie poprawny/optymalny?

ISR(TIMER1_CAPT_vect) { //przerwanie co 100ms
  PORTB ^= 1<<PB5; //zmiana stanu pinu
  __asm__ volatile ("nop"); // odczekaj jeden cykl w celu synchronizacji
  if ((PINB>>PB5) & 0x01) timer1=0xFF00; //odczytaj stan pinu i ustaw wartość
  else timer1 = 0x00FF;
} 

 

 

Udostępnij ten post


Link to post
Share on other sites

Nie wiemy, bo ani nie napisałeś co to za mikrokontroler, ani co próbujesz osiągnąć, ani pod jakim względem optymalizujesz.

Udostępnij ten post


Link to post
Share on other sites

A dlaczego nie:

if (PINB & (1 << PB5))

Zauważ: zakładając, że PB5 to stała a PINB to odwołanie się do zawartości rejestru - 1 << PB5 obliczany jest w czasie kompilacji. PINB >> PB5 w czasie wykonania.

Ergo: jeśli moje założenia są słuszne, nie jest optymalny niezależnie od mikrokontrolera 😉

 

Udostępnij ten post


Link to post
Share on other sites

Przy okazji warto jeszcze wyjaśnić jak ten pin jest skonfigurowany i jak niby miało działać - bo najpierw używany jest rejestr PORTB, czyli wygląda jakby to miał być pin w trybie wyjścia, ale chwilę później następuje odczyt z rejestru PINB, więc jednak wejście. Jestem bardzo ciekaw jak autor rozumie działanie takiego programu.

@ethanak zostawmy te optymalizacje w spokoju, może warto zacząć od poprawności programu. Może nie być różnicy, czy przesunięcie jest wykonywane podczas kompilacji, czy wykonywania.

Udostępnij ten post


Link to post
Share on other sites
5 minut temu, Elvis napisał:

zostawmy te optymalizacje w spokoju

Pytanie było "czy kod jest optymalny" :)

Udostępnij ten post


Link to post
Share on other sites

W każdym razie:

10 minut temu, ethanak napisał:

Zauważ: zakładając, że PB5 to stała a PINB to odwołanie się do zawartości rejestru - 1 << PB5 obliczany jest w czasie kompilacji. PINB >> PB5 w czasie wykonania.

Ergo: jeśli moje założenia są słuszne, nie jest optymalny niezależnie od mikrokontrolera 😉

to nieprawda. Są takie architektury gdzie oba kody mogą być tak samo wydajne.

Udostępnij ten post


Link to post
Share on other sites

Oczywiście. Ale ponieważ autor nie podał jakiego mikrokontrolera to dotyczy - zakładam, że jeśli dla jakiegoś (w miarę popularnego oczywiście) kod nie jest optymalny, to nie jest i koniec 🙂

Udostępnij ten post


Link to post
Share on other sites

Napisałeś że niezależnie od mikrokontrolera - i tylko dlatego napisałem że to nieprawda. Są takie i to dość popularne, sam na takie piszesz programy 🙂

Udostępnij ten post


Link to post
Share on other sites
1 minutę temu, Elvis napisał:

Napisałeś że niezależnie od mikrokontrolera - i tylko dlatego napisałem że to nieprawda

OK, mój błąd.

Udostępnij ten post


Link to post
Share on other sites

Tak dla wyjaśnienia i zamknięcia offtopu - w przypadku większości mikrokontrolerów i mikroprocesorów z rdzeniem ARM, dostępny jest tzn. barrel-shifter. Dzięki niemu wczytując wartość instrukcją LDR można jednocześnie i niejako "za darmo" wykonać przesunięcie bitowe. Oczywiście nie mamy gwarancji że kompilator wygeneruje akurat taki kod i nie powinno się takich optymalizacji wprowadzać bez potrzeby. Ale tak jako ciekawostkę warto wiedzieć, że ARM to niby RISC, a potrafi mieć zaskakująco skomplikowane instrukcje asemblera 🙂

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

@deshipu @ethanak@Elvis dziękuję za udzielone odpowiedzi.  Precyzując:

  1. Mikrokontroler: atmega328p (podałem w tegu 😉 )
  2. Co próbuje osiągnąć/jak to działa. Przeglądając bibliotekę avrlibc pod kontem blokowania przerwań znalazłem tam podany przykład, który pokazuje jak kopiować zmienną 16 bitową. Zależało mi na tym aby na możliwie najprostszym przykładzie zobrazować kiedy ta zmienna zostanie poprawnie odczytana a kiedy nie. 
  3. Optymalizacja -Os 
#define F_CPU 16000000UL

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

volatile uint16_t timer1=0x00FF;

void timer1_init(){
TCCR1A = 0;
TCCR1B = 1 << WGM13 | 1 << WGM12 | 1 << CS12 | 0 << CS11 | 1 << CS10; //prescaler set to 1024
TIMSK1 = 1<< ICIE1;  // enable capture event interrupt
ICR1 = 1562; //interrupt every 100ms
}

int main(void) {
  
DDRB = 1<< PB5 | 1<<PB4;
timer1_init();
sei();

	while(1) 
	{

		uint16_t timer1Copy;
		//comment line below and look on PB4
		ATOMIC_BLOCK(ATOMIC_FORCEON) 
		{
			timer1Copy= timer1;
		}
		
		if (!(timer1Copy == 0x00FF || timer1Copy == 0xFF00)) PORTB ^= 1<<PB4;
		
	}
	
	return 0;
}
	
ISR(TIMER1_CAPT_vect) {
  PORTB ^= 1<<PB5;
  __asm__ volatile ("nop");
  if ((PINB>>PB5) & 0x01) timer1=0xFF00;
  else timer1 = 0x00FF;
} 

Ogólnie działa to tak jak bym chciał czyli po zakomentowaniu makra atomic_block led na PB4 zaczyna migać. W takiej wersji jaką wkleiłem miga tylko led na PB5 sygnalizując przerwanie. Więc ponownie moje pytanie: jeśli kod wynikowy miał by być możliwie najmniejszy to co można by zmienić?

Udostępnij ten post


Link to post
Share on other sites

Ok, teraz rozumiem o co chodziło 🙂 W każdym razie używanie PINB jako zmiennej jest mocno mylące, może być mało wydajne, albo i zupełnie nie działać.

Nie wiem jak jest na AVR, ale na ARM dostęp do rejestrów może być o wiele wolniejszy niż do pamięci. Poza tym PINB może zwrócić inną wartość niż wpisałeś do PORTB... Proponuję po prostu dodać zmienną, która będzie przechowywała stan diody i odpowiednio do jej stanu sterować programem. Będzie prościej, szybciej i czytelniej.

Udostępnij ten post


Link to post
Share on other sites
3 minuty temu, slon napisał:

Zależało mi na tym aby na możliwie najprostszym przykładzie zobrazować kiedy ta zmienna zostanie poprawnie odczytana a kiedy nie. 

A gdzie masz przykład niepoprawnego przeczytania zmiennej i co ma do tego ten galimatias w procedurze przerwania? Po co odczytujesz stan pinu jeśli wiesz co do niego wysyłasz, sprawdzasz czy mikrokontroler nie jest zepsuty?

Co sądzisz o:

if (PORTB ^= 1<<PB5) ...

albo wręcz:

timer1 = (PORTB ^= 1 << PB5) ? 0x00FF : 0xFF00;

@Elvis byłeś szybszy ale i tak chciałbym abyś się wypowiedział...

 

 

  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites

Zacznijmy od nieporozumienia - PORTB i PINB to nie są zmienne. To rejestry i chociaż czasem zachowują się podobnie do zmiennych to zupełnie co innego.

Z rejestru PORTB możesz odczytać to co niego zapisałeś - więc zamiast sprawdzać PINB lepiej sprawdzać bit PORTB. Ale w niektórych mikrokontrolerach, np. stm32 jest tak, że dostęp do rejestru zajmuje o wiele więcej czasu niż odczyt ze zmiennej, czy rejestru procesora.

Natomiast to co odczytujesz z PINB to faktyczny stan piny PB5. Okazuje się, że może być on inny niż wynikałoby z zapisu do PORTB - dlatego to może zupełnie nie działać. Jeśli wyjście jest typu push-pull to na 99% będzie działało, ale już mając wyjście open-drain, zapis do PORTB może nie mieć wpływu na wartość w PINB. Wystarczy, że zewnętrzny układ zewrze do masy i PINB zwróci 0 nawet jeśli PORTB zapisywał 1. Czegoś takiego mniej więcej się spodziewałem, stąd było moje pytanie.

Udostępnij ten post


Link to post
Share on other sites

@ethanak zapis , który podałeś:

1 godzinę temu, ethanak napisał:

timer1 = (PORTB ^= 1 << PB5) ? 0x00FF : 0xFF00;

 

nie działa w procedurze przerwania. No chyba, że ja coś źle robię. Możesz napisać jak miała by wyglądać cała procedura przerwania? 

2 godziny temu, ethanak napisał:

co ma do tego ten galimatias w procedurze przerwania?

Wzorowałem się na karcie katalogowej 14,2,4  Reading the Pin Value. Tak jak pisałem wcześniej jeśli zakomentuję makro atomic_block dioda na PB4 zaczyna migać co oznacza , że zmienna timer1Copy przyjmuje wrtości 0x0000 lub 0xFFFF.

@Elvis rozumiem , że PORTx i PINx to rejestry. Natomiast tak jak napisałem wcześniej wzorowałem się a karcie katalogowej do atemga328p w ,której jest napisane , że sprawdzanie stanu pinu należy dokonać przez rejestr PINx a nie bezpośrednio przez bit PORTx i dodatkowo należy odczekać od 0.5 do 1.5 cyklu  zegara w celu synchronizacji.

Udostępnij ten post


Link to post
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!

Gość
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...