logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[winavr]Atmega16 bug w kodzie przerwania

pawelvod 26 Mar 2011 20:28 1272 6
  • #1 9326226
    pawelvod
    Poziom 18  
    Witam. Siedzę już nad tym długo i chyba oślepłem na tego buga więc proszę o pomoc. Na bitach 1-3 portu A odpalam sobie pwm z wypełnieniem 2/255. Wszystko działa ok tzn diody przez oporniki 220ohm do masy ledwo świecą. Potem próbuje wysyłać coś do wyświetlacza obsługiwanego przez piny 5-7 i sprawa się rypie bo diody zaczynają wariować, mimo że fizycznie nic pod te piny nie jest podpięte. W debugerze i wg mnie wyświetlacz na chwilę "nie dotyka" pinów PWM i nie wstrzymuje przerwania więc nie wiem czemu tak się dzieje. Diody są zakłócane przez linie zawierające (&= ~) w procedurze wyświetlacza lcd_out(). Po ich usunięciu miganie ustaje. Kod:
    #include 	<avr/io.h>
    #include 	<avr/interrupt.h>
    #include 	<inttypes.h>
    #include 	<avr/pgmspace.h>
    
    //wyświetlacz piny 5-7
    	#define LCD_PORT PORTA
    	#define LCD_DDR  DDRA
    	#define LCD_CLK (1<<PA7)
    	#define LCD_SIO (1<<PA6)
    	#define LCD_CS  (1<<PA5)
    
    /* PWM piny 1-3*/
    	#define PWM_PORT  	PORTA
    	#define PWM_DDR  	DDRA
    
    int main(void) {
    //ustawienie PWM jako wyjścia
    	PWM_PORT  	|= 	0b00001110;
    	PWM_DDR	  	|= 	0b00001110;
    //ustawienie lcd jako wyjścia
    	LCD_DDR |= (LCD_CLK|LCD_SIO|LCD_CS);
    	LCD_PORT &= ~(LCD_CLK|LCD_SIO|LCD_CS);
    //ustawienia przerwania timer_2
    	TCCR2 |= (1<<CS22)|(1<<CS20);				//preskaler
    	TIMSK |= (1<<TOIE2);						//przepełnienie enable
    	TIMSK |= (1<<OCIE2);						//compare interrupt enable
    	OCR2 = 2;									//wypełnienie 1/255
    	sei();
    	while(1) LCD_Out(27,  1);		//co kolwiek dla przykłądu wysyła
    }
    //obsuga PWM
    ISR(TIMER2_OVF_vect){	
    	PWM_PORT |= 0b00001110;
    }
    ISR(TIMER2_COMP_vect){
    	PWM_PORT &=  ~0b00001110;
    }
    
    void LCD_Out(uint8_t Data, uint8_t isCmd) {
    	uint8_t x;
    	if(isCmd) LCD_PORT |= LCD_CS;
    	LCD_PORT &= ~(LCD_CLK|LCD_CS);
    	LCD_PORT |= LCD_SIO;
    	if(isCmd) LCD_PORT &= ~LCD_SIO;
    	LCD_PORT |= LCD_CLK;
    	for(x=0; x<8; x++)	{
    	LCD_PORT &= ~(LCD_SIO|LCD_CLK);
    		if(Data & 128) LCD_PORT |= LCD_SIO;
    		LCD_PORT |= LCD_CLK;
    		Data=Data<<1;
    	}
    }
    
  • #2 9326392
    nsvinc
    Poziom 35  
    Musisz wyłączyć generowanie PWM w czasie obsługi wyświetlacza! Przerwanie nie może wymuszać dostępu do portu na który masz wyświetlacz w losowym momencie, bo twój procesor nie ma rejestru do niezależnej i atomicznej manipulacji pinami portu.

    Uwalaj wszystkie przerwania na czas wykonywania funkcji LCD_Out.
  • #3 9326510
    pawelvod
    Poziom 18  
    Ok. Oczywiście pomogło tylko nie do końca wiem dlaczego...
    Rozumiem z twojej wypowiedzi (przynajmniej tak mi się zdaje :)) że jak przeznaczę na PWM cały port to problem zniknie. Puki co pomogło:
    void LCD_Out(uint8_t Data, uint8_t isCmd) {
    	uint8_t x;
    	if(isCmd) LCD_PORT |= LCD_CS;
    	cli();
    	LCD_PORT &= ~(LCD_CLK|LCD_CS);
    	sei();
    	nop();
    	LCD_PORT |= LCD_SIO;
    	cli();
    	if(isCmd) LCD_PORT &= ~LCD_SIO;
    	sei();
    	nop();
    	LCD_PORT |= LCD_CLK;
    	for(x=0; x<8; x++)	{
    	cli();
    	LCD_PORT &= ~(LCD_SIO|LCD_CLK);
    	sei();
    		nop();
    		if(Data & 128) LCD_PORT |= LCD_SIO;
    		nop();
    		LCD_PORT |= LCD_CLK;
    		Data=Data<<1;
    	}
    }

    Nie bardzo mogę zatrzymać przerwania na całą procedurę bo po pierwsze mam kilka czasowo krytycznych przerwań a po drugie przy tym preskalerze T2 też widać mruganie wynikające z gubienia cykli PWM. Jakbyś mógł podać źródło gdzie można zgłębić ten problem lub ciut dokładniej wyjaśnić dlaczego tylko AND jest problemem podczas gdy OR działa OK.
  • #4 9326554
    tmf
    VIP Zasłużony dla elektroda
    Jeżeli PORTA leży w obszarze IO i instrukcji sbi/cbi to łatwo problemu uniknąć zmieniając na raz tylko jeden bit w porcie. W przeciwnym przypadku tak jak zrobiłeś dostęp do PORTA należy w głównej procedurze realizować atomowo. Dzieje się tak dlatego, że zmiana stanu portu realizowana tak jak opisałeś powoduje odczyt jego stanu, zmianę i zapis nowej wartości do portu. Jeśli pomiędzy pojawi się przerwanie to klapa - i są efekty jakie opisujesz.
    Swoją drogą, dlaczego nie realizujesz PWM sprzętowo?
  • #5 9326599
    pawelvod
    Poziom 18  
    Dzięki za wyjaśnienie. Może jeszcze wiesz dlaczego to nie wychodzi w softowej emulacji na AVR Studio? Powinno chyba wychodzić? CO do programowej PWM to tylko 1 pin pokazałem a jest 8 niezależnych (obciąłem kod bo i na jednym się krzaczyło). Na 16tce mam tylko 3 dostępne sprzętowo.

    Zmieniłem na
    void LCD_Out(uint8_t Data, uint8_t isCmd) {
    	uint8_t x;
    	if(isCmd) LCD_PORT |= LCD_CS;
    	//cli();
    	LCD_PORT &= ~(LCD_CLK);
    	LCD_PORT &= ~(LCD_CS);
    	//sei();
    	nop();
    	LCD_PORT |= LCD_SIO;
    	//cli();
    	if(isCmd) LCD_PORT &= ~LCD_SIO;
    	sei();
    	//nop();
    	LCD_PORT |= LCD_CLK;
    	for(x=0; x<8; x++)	{
    	//cli();
    	LCD_PORT &= ~(LCD_SIO);
    	LCD_PORT &= ~(LCD_CLK);
    	//sei();
    		nop();
    		if(Data & 128) LCD_PORT |= LCD_SIO;
    		nop();
    		LCD_PORT |= LCD_CLK;
    		Data=Data<<1;
    	}
    }

    i działa jak potrzeba. Jeszcze jedno pytanie. Puki co kompilator zamienia to na sbi. Jednak przy jakiejś następnej wersji może mu przyjść do głowy żeby to skompilować inaczej. Jak wymusić na nim kompilację jako operacje na bitach?
  • #6 9327509
    tmf
    VIP Zasłużony dla elektroda
    Nie da się. To są uroki C, coś zyskujesz, coś tracisz. Jest jednak bardzo mało prawdopodobne, aby przyszłe wersje miały gorszą optymalizację niż starsze. Zawsze też możesz użyć wstawki w asemblerze. Jeśli jednak używasz 8 pinów IO to dlaczego by nie użyć dedykowanego dla nich portu IO? Wtedysposób przeprowadzenia operacji nie miałby znaczenia.
  • #7 9336488
    pawelvod
    Poziom 18  
    Dzięki. To są właśnie problemy przy przesiadaniu się z asm na c:). Sam bym nie doszedł.
REKLAMA