Skocz do zawartości

Odczyt stanu pinu AVR


slon

Pomocna odpowiedź

W tym co napisałem nie ma nic sprzecznego z notą katalogową. PINx zwaca faktyczny stan pinów wybranego portu - zapis od PORTx jedynie próbuje zapisać określoną wartość. Jeśli zapis się uda, to po pewnym czasie np. 0.5-1.5 cyklu masz taki wynik jak chciałeś. Więc szybciej (optymalniej) będzie działało użycie zmiennej - a inna sprawa, że w zależności od konfiguracji pinu i tego co do niego jest podłączone ten kod może po prostu nie działać tak jak oczekujesz.

  • Pomogłeś! 1
Link do komentarza
Share on other sites

@Elvis dzięki za uwagi

ISR(TIMER1_CAPT_vect) {
  PORTB ^= 1<<PB5;
  __asm__ volatile ("nop");
  //if ((PINB>>PB5) & 0x01) timer1=0xFF00;
  //else timer1 = 0x00FF; 
 timer1= ((PINB>>PB5) & 0x01) ? 0x00FF : 0xFF00;
} 

@ethanak w ten sposób działa.

Link do komentarza
Share on other sites

Mając trochę wolnego czasu ponownie przeanalizowałem timer1 w reference manual . Miałem taką myśl , że gdyby odczyt z PINB nie odbywał się bezpośrednio po zmianie stanu PORTB w przerwaniu to może faktycznie wystarczyła by jedna linijka w ISR. 

#define F_CPU 8000000UL

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

volatile uint16_t timer1=0x00FF;

void timer1_init(){
	// control register A. Toggle OC1A/PB1
TCCR1A = 0<< COM1A1 | 1<< COM1A0 | 0<< COM1B1 | 0<< COM1B0 | 0<< WGM11 | 0<< WGM10;
	// control register B. Prescaler set to 1024,WGM 4
TCCR1B = 0 << WGM13 | 1 << WGM12 | 1 << CS12 | 0 << CS11 | 1 << CS10;
	// Interrupt Mask Register. Enable output compare A interrupt
TIMSK1 = 0<< ICIE1 | 0<< OCIE1B | 1<< OCIE1A | 0<< TOIE1;
OCR1A = 780; //every interrupt around 100ms
}

int main(void) {

DDRB = 1<< PB1 | 1<<PB0;
timer1_init();
sei();

	while(1) 
	{
		uint16_t timer1Copy;
		//comment line below and look on PB0
		ATOMIC_BLOCK(ATOMIC_FORCEON) 
		{
			timer1Copy= timer1;
		}
		
		if (!(timer1Copy == 0x00FF || timer1Copy == 0xFF00)) PORTB ^= 1<<PB0;
	}
	
	return 0;
}
	
ISR(TIMER1_COMPA_vect) {
timer1= ((PINB>>PB1) & 0x01) ? 0xFF00 : 0x00FF;
}

Kod działa analogicznie jak poprzedni. Zmienione jedynie numery pinów, taktowanie procka (sama atmega328p na płytce stykowej) oraz tryb WGM na 4 z 12.

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

To jakiś konkurs na największą ilość kodu która nic nie robi? Może chociaż spróbuj wyjaśnić co starasz się osiągnąć - bo jeszcze niechcący ktoś początkujący kiedyś znajdzie ten wątek i zacznie się z tego uczyć...

Link do komentarza
Share on other sites

Konkursu nie robię. Na hobby czasu ostatnio mało. Na jednej płytce mam timer1 skonfigurowany tak jak wyżej:

  1. F_CPU 16000000UL
  2. Preskaler 1024

1024/16= 64 mikrosekundy dla OCR1A=1 (najkrótszy impuls). Maks. czas trwania stanu wysokiego/niskiego 16ms (256*64=16384). Więcej na razie nie potrzebuję.

Na drugiej płytce timer1 ustawiony w tryb input capture (F_CPU i preskaler tak jak na pierwszej płytce). Czas trwania stanu niskiego i wysokiego (ilość cylki TCNT1) ma być wyświetlana na ledach jako wartość binarna (czyli dobrze by było mieć dwa całe porty do dyspozycji). Na chwilę obecną przetestowałem to na atmedze328p (docelowo kod zostanie nieco zmieniony):


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

volatile uint8_t captureL[256];
volatile uint8_t captureH;
volatile uint8_t i=0xFF;

void timer1_input(){
TCCR1A = 0;
TCCR1B = 1<< ICES1 | 0<< WGM13 | 0<< WGM12 | 1<< CS12 | 0<< CS11 | 1<< CS10; //prescaler 1024
TIMSK1 = 1<< ICIE1; // enable capture event interrupt
TCNT1 = 0xFF00;
}

int main(void) {
  
DDRD |= 1<<DDD7 | 1<<DDD6 | 1<<DDD5 | 1<<DDD4 | 1<<DDD3 | 1<<DDD2;
timer1_input();
sei();

	while(1) {}
	
	return 0;
}
	
ISR(TIMER1_CAPT_vect) {
	i++;
	captureL[i] = ICR1L;
	captureH = ICR1H;
	/* change edge detection */
	TCCR1B ^= 1<< ICES1;
	/* clear input capture interrupt flag */
	TIFR1 |= 1<< ICF1;
	/* ICR1L on PORTD */
	if (i != 0) PORTD = captureL[i] - captureL[i-1];

}

Najniższa wartość dla OCR1A jaką wpisywałem to 4 (czyli 256 mikrosekund). Z racji tego , że chciał bym mieć dwa pełne porty i najlepiej jeden mikrokontroler AVR zastanawiam się nad atmegą 169P ? TQFP64 (cena jak za atmege32).

 

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.