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

[ATMEGA8] Problem z obsługą RS - co robie nie tak?

mrrudzin 29 Cze 2009 13:32 2748 15
REKLAMA
  • #1 6717566
    mrrudzin
    Poziom 39  
    Mam problem z uruchomieniem prostego programu na ATMEGA8.
    Mega jest podpięta z SN75176 (odpowiednik MAX485) i mam podłączone dwie takie płytki.
    Każda płytka ma przycisk i element wykonawczy (triak). Program ma za zadanie zbierać informacje z sieci (gdy wciśniemy przycisk na płytce B, powinien się załączyć triak na płytce A i odwrotnie) i w razie potrzeby samemu wysyłać informacje.
    Wszystko działa ślicznie do momentu gdy w jednym programie nie używam jednocześnie (w różnych momentach) polecenia nadawania i odbioru (w przerwaniu) informacji z sieci. Proba nadania czegokolwiek albo odebrania kończy się resetem ATMEGI (na początku programu zapalam sobie dwie diody - stąd wiem że następuje reset).
    Co robie nie tak?

    
    
    define F_CPU 8000000 // 8MHz zegar procesora
    #define CYCLES_PER_US ((F_CPU+500000)/1000000) // cpu cycles per microsecond
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <avr/signal.h> 
    #include <util/delay.h>
    #include <stdio.h> 
    #include <avr/pgmspace.h>
    
    
    #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) 
    #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
    /*
    Struktura ramki danych
    
    Bajt 1 Familly code
    Bajt 2 Adres
    Bajt 3 Funkcja
    Bajt 4 Dane 
    Bajt 5 Dane
    Bajt 6 Dane
    Bajt 7 Dane
    Bajt 8 CRC16
    
    */
    
    /* */
    
    //Definicje i markodeklaracje
    
    
    const int FAMILLY_CODE = 51;
    const int ADRES = 52;
    
    volatile int j;
    volatile int FLAGA_komunikat_OK=0;
    volatile int iRX;
    volatile int QQ=0;
    volatile unsigned char RS232_RX[16];
    volatile unsigned char RS232_RXd[8];
    volatile unsigned char RS232_TX[10];
    volatile int FLAGA;
    volatile int VALUE;
    volatile int stan=0;
    volatile int wartosc=250;
    volatile int i=0;
    volatile int ip;
    volatile uint16_t LICZNIK;
    volatile uint8_t data[96];
    volatile uint16_t wtg_transmision=0;//zabezpieczenie transmisji (w razie zgubienia jednego bajtu)
    
    int dataDS[2];
    
    //DIODY LED------------------------------
    
    #define LEDP1ON sbi(PORTB,PB0) //dioda LED1
    #define LEDP2ON sbi(PORTD,PD7) //dioda LED2
    
    #define LEDP1OFF cbi(PORTB,PB0)
    #define LEDP2OFF cbi(PORTD,PD7)
    
    #define LEDP1 PB0 
    #define LEDP2 PD7
    
    #define DIMMON sbi(PORTD,PD5) //sterowanie triakiem
    #define DIMMOFF cbi(PORTD,PD5)
    #define DIMM PD5 
    
    #define TRANON sbi(PORTD,PD4) //sterowanie transmisją RS485
    #define TRANOFF cbi(PORTD,PD4)
    #define TRAN PD4
    
    //------------------------------
    
    
    
    //RS232----------------------------------------------------------------------------------------
    
    const unsigned char Wea[]="                "; //stala dla wyswietlacza
    
    void InitUSART( unsigned long int baud ) // baud - czyli bitrate portu COM
    {
       
       UBRRH = (unsigned char)(((F_CPU/(16UL*baud))-1)>>8);
       UBRRL = (unsigned char)((F_CPU/(16UL*baud))-1);
       // Otwarty kanal odbioru i nadawania + przerwania
       UCSRB |= (1<<RXEN)|(1<<TXEN)| (1 << RXCIE)| (1 << TXCIE);;
       // 8bitów, 1bity stopu */
       UCSRC |= (1<<URSEL)|(1<<UCSZ1) | (1<<UCSZ0);
    }
    
    unsigned char ReceiveUSART( void )  // funkcja odbioru bajtu z RS232
    {
       while ( !(UCSRA & (1<<RXC)) );
       return UDR;
    }
    
    void TransmitUSART( unsigned char data )  // funkcja wysłania bajtu po RS232
    {
       while ( !( UCSRA & (1<<UDRE)) );
       UDR = data;
    } 
    
    //RS232----------------------------------------------------------------------------------------
    void odebral()
    {
    
    
    for(i=0;i<8;i++)
    {
    RS232_RXd[i]=RS232_RX[i];
    RS232_RX[i]=0;
    }
    
    
    if(RS232_RXd[3]==1) LEDP1ON;
    else LEDP1OFF;
    
    wartosc=RS232_RXd[2];
    stan=RS232_RXd[3];
    OCR1AH=0;
    if(stan==1) OCR1AL=wartosc;
    else OCR1AL=0;
    }
    
    
    //odbior bajtu z uarta
    SIGNAL (SIG_UART_RECV)
    {
    
    wtg_transmision=0;
    
    if(QQ==2)
    {
    
    RS232_RX[iRX]=UDR;
    iRX++;
    
    if(iRX>5) 
    	{
    	QQ=0; 
    	odebral();
    	wtg_transmision=500;
    	}
    }
    
    if(QQ==1) 
    {
    if(UDR==ADRES) QQ=2;
    else QQ=0;
    }
    
    if(UDR==FAMILLY_CODE && QQ==0) {QQ=1;}
    
    
    }
    
    
    
    //STEROWANIE TRIAKIEM----------------------------------
    SIGNAL (SIG_INTERRUPT0) 
    {
    TCNT1=0; //wyzeruj licznik
    }
    
    SIGNAL(SIG_OUTPUT_COMPARE1A) 
    {
    
    if(stan==1)
    {
    DIMMON;
    _delay_us(10);
    }
    DIMMOFF;
    
     
    }
    
    //STEROWANIE TRIAKIEM----------------------------------
    
    
    int main() 
    {
    //Deklaracja zmiennych i stalych
    
    	
      GIMSK |=_BV(INT0); //włącz obsługę przerwań Int0
      MCUCR |=_BV(ISC01);   // włącz generowanie przerwań przez
                            // opadające zbocze na Int0
    
    //--------------- TIMER1 ----------------------
       TCCR1A=0xC; 
       TCCR1B = 0x04; //preskaler ustawiony na 256
    
       TCNT1H = 0;   //wartość początkowa
       TCNT1L = 0;
       OCR1AH = 0x00;   //7A12 = 31250 -> 1s
       OCR1AL = 0xF2;
                   
       TIMSK = 0x10;    //przerwanie gry osagnięta wartosc = OCR1A
    
       //--------------------------------------------- 
    
    int pomstan=0;
    int staraw=0;
    int starystan=0;
    int kierunek=2;
    
    DDRB|=_BV(PB0);
    DDRC|=_BV(PC0)|_BV(PC1)|_BV(PC2)|_BV(PC3)|_BV(PC4)|_BV(PC5);
    DDRD|=_BV(DIMM)|_BV(PD7)|_BV(PD4);
    
    
    InitUSART(9600);
    
    TRANOFF;
    LEDP1ON;
    LEDP2ON;
    _delay_ms(100);
    LEDP1OFF;
    LEDP2OFF;
    
    //ustawienia poczatkowe
    
    RS232_TX[0]=51;
    RS232_TX[1]=52;
    RS232_TX[2]=53;
    RS232_TX[3]=54;
    RS232_TX[4]=250; //Jasnosc
    RS232_TX[5]=0; // 0 wylacz / 1 wlacz
    RS232_TX[6]=60;
    RS232_TX[7]=61;
    RS232_TX[8]=62;
    
    staraw=wartosc; 
    starystan=stan;
    
    sei();
    do
    {
    
    //Obsluga przycisku 
    
    if(bit_is_clear(PINB,PB2))
    	{
    	pomstan++;
    		
    	if(pomstan>400)//&&stan==1)
    	{
    	if(kierunek==1 ) {wartosc=wartosc+1;}
    	if(kierunek==2 ) {wartosc=wartosc-1;}
    	}
    	
    	_delay_ms(1);
    	}
    else
    	{
    	
    	if(pomstan<=400&&pomstan>20) 
    	{
    	stan=((stan+1)%2);
    	if(stan==0) LEDP1ON;
    	else LEDP1OFF;
    	_delay_ms(10);
    	}
    			
    	pomstan=0;
    	_delay_ms(1);
    	}
    
    //Obsluga sytuacji brzegowych
    if(pomstan>1000) pomstan=1000; 
    if(wartosc<20) kierunek=1;
    if(wartosc>255) kierunek=2;
    
    
    
    /* Zmiana stanu
    Jesli zmienimuy stan albo wartosc - wysylamy nowe wartosci 
     */
    
    if(staraw!=wartosc||starystan!=stan)
    {
    cli();
    RS232_TX[4]=wartosc;
    RS232_TX[5]=stan;
    
    OCR1AH=0;
    if(stan==1) OCR1AL=wartosc;
    else OCR1AL=0;
    
    TRANON; //przelacz MAX-a w tryb nadawania
    _delay_us(100);
    	for(i=0;i<8;i++)
    	{
    	TransmitUSART(RS232_TX[i]);
    	}
    _delay_ms(1);	
    TRANOFF; //przelacz MAX-a w tryb odbioru
    sei();
    staraw=wartosc;
    starystan=stan;
    }
    
    /* Kontrola transmisji
    Resetujemy bufor gdy trzeba zbyt dlugo czekac na nastepny bajt
     */
    wtg_transmision++;
    _delay_us(1000);
    
    if(wtg_transmision>50) 
    	{
    	iRX=0;	
    	wtg_transmision=0;
    	LEDP2ON;
    	_delay_ms(5);
    	LEDP2OFF;
    	
    	//dolozyc resetowanie bufora uarta !!!!
    	}
    
    
    }
    while(1);
    
    return 0;
    }
    
    
  • REKLAMA
  • Pomocny post
    #2 6717909
    chudybyk
    Poziom 31  
    W przerwaniu dwukrotnie sprawdzasz warunek z użyciem rejestru UDR. Ta operacja odbiera bajt w celu porównania. Przy drugim if-ie możesz dostać ten sam bajt, albo następny z odbieranych. Jeśli to ma być kolejny, to musisz na niego poczekać (co nie jest dobrym pomysłem w przerwaniu). Myślę, że masz tu błąd.
    Pozdrawiam!
  • REKLAMA
  • #3 6718454
    mrrudzin
    Poziom 39  
    A czy procesor sprawdza w jakiś sposób czy dane z UDR-a zostały odczytane?
    Rozumiem że przepisanie zawartości rejestru UDR do jakiejś zmiennej pomocniczej załatwi ten ewentualny problem.

    Martwi mnie jedynie dlaczego samo nadawanie albo samo odbieranie działa...

    Niestety megę będe miał pod ręką dopiero wieczorem...
  • Pomocny post
    #4 6718559
    chudybyk
    Poziom 31  
    W zasadzie tak. UDR to jest dosyć szczególny rejestr, bo jakikolwiek jego odczyt sygnalizuje procesorowi odebranie znaku z USART-a i wtedy automatycznie dzieje się kilka rzeczy, m. innymi gaszona flaga RXC, załadowanie do UDR następnego przyjętego znaku, jeśli zdążył się pojawić (jest to możliwe, bo AVR-ki mają 3-bajtowy bufor sprzętowy, który jest "ukryty" pod UDR).
    Zauważyłem teraz jeszcze jedną rzecz: wyłączasz przerwania w czasie nadawania całej ramki - ośmiu bajtów. To musi troszkę trwać. Jeśli w tym czasie dostaniesz jakąś ramkę, to sprzęt zbuforuje Ci 3 bajty w buforze sprzętowym, a resztę oleje, bo już nie zmieści. Po włączeniu przerwań dostaniesz od razu trzy przerwania kolejno po sobie - dla trzech znaków, które odbiornik "złapał". Nie wyłączaj przerwań w czasie wysyłania znaków, a jeśli już musisz, to je przywróć i znowu wyłącz między kolejnymi wysyłanymi znakami. To spowoduje rozładowanie oczekującego przerwania.
  • #5 6720074
    mrrudzin
    Poziom 39  
    Poprawiłem wg wskazówek, ale bez efektów.
    Wszystko działa do momentu wysłania / odebrania czegoś po sieci. Program wysyła bajt danych poprawnie po czym się resetuje.
    Wycięcie komendy TransmitUSART(RS232_TX[i]); powoduje że program zaczyna poprawnie odbierać info z sieci. Wyłączenie na sztywno obsługi przerwań ( cli(); ) powoduje że program poprawnie nadaje ramki danych.
  • #6 6727055
    pkoper
    Poziom 11  
    Z tego co piszesz wynika:
    1. Działa regulacja fazowa.
    2. Działa transmisja.
    3. Nie działają obie te rzeczy razem.

    Może gdy wyzwalasz triak i masz jeszcze coś wysłać przez RS485 to spada Ci napięcie zasilania. Dostaw blisko ATmegi np. 100nF + 100uF między VCC a GND.
  • Pomocny post
    #7 6727412
    maly_elektronik
    Poziom 23  
    Skoro już przekopiowałeś cały przykład z noty katalogowej procesora :)
    to może zostaw makro:
    
    #define baud xxxxxx
    #define CPU xxxxxx
    #define UBRR F_CPU/(CPU*baud)
    

    tak jak było w przykładzie :idea:
    Czasami drobnostka a może mieć fatalne skutki :)
  • Pomocny post
    #8 6729028
    Konto nie istnieje
    Konto nie istnieje  
  • Pomocny post
    #9 6729314
    papikus
    Poziom 15  
    Polecam specyfikacje Modbus do poczytania i książkę Pana Witkowskiego "Mikrokontrolery AVR programowanie w języku C - przykłady zastosowań", gdzie bardzo przystępnie pokazuje w C komunikację po RS485 z crc16 i detekcją błędów.
    Zakładasz w programie środowisko idealne, pomyśl co się zacznie dziać, gdy pojawią się zakłócenia...
    Tak jak mówił albertb nawet w prostych programikach należy kierować się dobrymi nawykami programistycznymi.
    Maszyna stanów? ;-)
  • REKLAMA
  • #10 6729520
    mrrudzin
    Poziom 39  
    Witam

    Przepraszam za tak długą absencję. Program udało mi się uruchomić - po sugestiach kolegi chudybyk wyłączyłem przerawnia nadawania. Teraz potwierdził to kolega papikus :)

    Narazie zakładam że wszystko jest idealne - chciałem zacząć od "czegoś" :)
    Co ewentualnie możnaby jeszcze poprawić / na co zwrócić uwagę (oprócz wyrzucenia procedury odbioru z przerwania)?

    Książkę jestem w stanie zdobyć - zapoznam się z treścią.
    Czy jest jeszcze jakieś źródło z którym warto się zapoznać?

    EDIT: Poprawiłem
  • Pomocny post
    #12 6729820
    Konto nie istnieje
    Konto nie istnieje  
  • REKLAMA
  • #13 6731102
    papikus
    Poziom 15  
    masz Link do rozwiązania wykorzystującego układ NE555 do automatycznego wykrywania kierunku transmisji.

    pozdr
  • Pomocny post
    #14 6731130
    maly_elektronik
    Poziom 23  
    Zamiast stosować układ NE555 lepiej zastosować tranzystory :)
  • #15 6732561
    mrrudzin
    Poziom 39  
    Cytat:

    Wyłączyć musisz przerwanie nadawania (a właściwie go nie włączać) bo nie zaprogramowałeś co procesor ma robić, gdy skończy nadawać i dlatego się resetuje.

    Oczywiście chodziło o przerwanie nadawania :)

    Cytat:

    masz Link do rozwiązania wykorzystującego układ NE555 do automatycznego wykrywania kierunku transmisji.

    A moje rozwiązanie przez sterowanie kierunkiem za pomocą nogi procesora jest złe/to na 555 jest w czymś lepsze?
  • #16 6733466
    papikus
    Poziom 15  
    można to nazwać sprzętową kontrolą kierunku transmisji
REKLAMA