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

[ATMEGA16][C]Komunikacja z uC po UART(RS485)

sevotharte 31 Paź 2010 15:46 7612 2
REKLAMA
  • #1 8685230
    sevotharte
    Poziom 10  
    Witam.

    Mam taki problem, gdyż chciałbym ogarnąć komunikację poprzez UART między dwoma mikro. Mam dwa uC: Atmegę 16, która działa jako master oraz Atmegę 88 jako Slave (na której będzie termometr, z którego będę odczytywał dane).

    Programy, definicje wyglądają następująco:

    plik RS485.h
    
    #indef  RS_INT_H
    #define RS_INT_H
    
    //rozmiar buforów nadawania i odbierania
    #define TX_BUF_SIZE 64
    #define RX_BUF_SIZE 32
    
    //otwarcie pinu przesyłania w pół duplexie RS 485 (Enable Pin)
    #define TXEN_PORT PORTB
    #define TXEN_DDR  DDRB
    #define TXEN_PIN  PINB
    #define TXEN_BIT  1
    
    //szybkość transmisji
    #define RS_BR 51 //19200 @ 16MHz Ubrr=fckl/[(16*BAUD)-1]
    
    //licznik
    //szybkosc transmisji: 19200/11 =1735.5 znaków/sekundę (11 bitów = bit startu, 8 bitów danych, bit parzystości i stopu)
    //8 bitów danych zajmuje: 8*256 =  3906.25 przerwań/sekundę (256 bo mamy zegar 8MHz, więc 2^8=256)
    //ilość przerwań na każdy znak wynosi: 3906.25/1735.5 = 2.25 przerwań na znak
    #define RS_WAIT_05 2
    #define RS_WAIT_2  6
    #define RS_WAIT_3  9
    #define RS_WAIT_4  10
    
    //stany interfejsu
    #define RS_WAIT_START 0  //czekanie na rozpoczęcie nadawania
    #define RS_ADRESS	  1  //czekanie na odpowiedź od układu podrzędnego
    #define RS_SIZE		  2  //czekanie na długość przesyłanego bloku danych (gdy ok przejdź do RS_DATA, gdy źle RS_BUSY)
    #define RS_DATA		  3  //czekanie na dane (gdy nieodebrano przejdź w stan RS_BUSY, gdy odebrano w RS_CRC_L)
    #define RS_CRC_L 	  4	 //czekanie na młodszy bajt CRC 
    #define RS_CRC_H 	  5  //czekanie na starszy bajt CRC
    #define RS_WAIT_TX	  6  //stan oczekiwania na nadawanie
    #define RS_TX		  7  //interfejs w trakcie nadawania
    #define RS_BUSY       8  //czekanie na koniec ignorowanej transmisji
    
    //adresy flag
    #define RS_BROADCAST_FLAG  0x80
    #define RS_TX_RQ           0x40
    #define RS_BROADCAST_GROUP 0x01
    
    //kody błedów
    #define RS_FL   'L'
    #define RS_BO	'O'
    #define RS_TE	'E'
    #define NAK		'N'
    #define ACK		'A'
    #define NQ		'Q'
    
    //flagi
    #define TX_RTS 0
    #define RX_DR  1
    #define RS_REC 2
    #define RX_ERR 3
    
    #endif


    master.c
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <avr/crc16.h>
    #include <rs485.h>
    
    uint8_t  rs_adr;
    uint8_t  rs_flags, rs_err;
    uint8_t  tx_send, tx_cnt, rx_rec;
    uint16_t rs_crc;
    
    statc uint8_t rs_time, state;
    
    uint8_t rx_buf[RX_BUF_SIZE];
    uint8_t tx_buf[TX_BUF_SIZE];
    
    void crc(uint8_t data)
    {
    	rs_crc = _crc_ccitt_update(rs_crc, data);
    }
    
    //inicjacja UARTA
    void init_uart(void)
    {
    	// Ustawienie prędkości transmisji
    	UBRRL = (uint8_t)(RS_BR);
    	UBRRH = (RS_BR>>8);
    	//włączanie UART na odbieranie
    	UCSRB |= _BV(RXEN); 
    	//włączanie UART na wysyłanie 
    	UCSRB |= _BV(TXEN);
    	//uaktywnienie uarta na bicie 1 portu B
    	TXEN_DDR|=_BV(TXEN_BIT);
    	
    	//ustawienie portów uarta RX i TX
    	DDRD  &= ~_BV(0); //_BV -> (1 << bit) - bit value, ustawienie
    	PORTD |= _BV(0);
    	DDRD  |= _BV(1);
    	
    	//ustawienie parzystości, 8 bitów data, bitu stopu, asynchronizacja
    	UCSRC = _BV(URSEL) | _BV(UPM1) | _BV(UCS0) | _BV(UCS1) ;
    	UCSRB |= _BV(RXCIE); //włącza przerwania od RX
    }
    
    //odmierzanie czasów dla UART poprzez przerwanie TIMER0_OVF_vect
    ISR(TIM0_OVF_vect)
    {
    	if(rs_time) //rs_time - licznik używany do odmierzania przedziałów czasu
    	{
    		rs_time--; //dekrementacja licznika
    		if(!rs_time) //gdy nie rs_time
    		{
    			if(state == RS_BUSY) //gdy stan jest RS_BUSY
    				state = RS_WAIT_START; //zmien stan na RS_WAIT_START
    			else
    			{
    				rs_time = RS_WAIT_3 - RS_WAIT_2; //rs_time jest równy reszcie czasu
    				state = RS_BUSY; //zmień stan na RS_BUSY
    			}
    		}
    	}
    	
    	if ((state == RS_WAIT_START) && (rs_flags & _BV(TX_RTS))) //gdy stan jest równy RS_WAIT_START i (flagi używane przez procedury obsługi interfejsu szeregowego i 
    	{
    		TXEN_PORT |= _BV(TXEN_BIT);
    		rs_flags &= ~_BV(TX_RTS);
    		tx_send = 0;
    		state = RS_TX;
    		UCSRB |= _BV(UDRIE);
    	}
    	sei();
    }
    
    //USART_TXC_vect sprawdza czy interfejs jest w trybie nadawania. 
    ISR(USART_TXC_vect)
    {
    	if(TXEN_PORT & _BV(TXEN_BIT))
    	{
    		TXEN_PORT &= ~_BV(TXEN_BIT);
    		if (rs_flags & _BC(RS_REC))
    		{
    			state = RS_ADRESS;  //ustaw stan na RS_ADREss
    			rs_time = RS_WAIT_2;
    		}
    		else
    		{
    			state = RS_BUSY;  // ustaw stan na zajęty
    			rs_time = RS_WAIT_3;
    		}
    	}
    }
    
    //Za wysyłanie danych odpowiedzialny jest USART_UDRE_vect
    ISR(USART_UDRE_vect)
    {
    	if (TXEN_PORT & _BV(TXEN_BIT) && (state == RS_TX)) // jeśli (konwerter jest podlaczony do PORTU B oraz na bicie 1 jest 1) i ( stan jest ustawiony na wysyłane)
    	{
    		if (tx_send == tx_cnt)  //jeśli liczba wysyłanych bajtów ramki == długości ramki do wysłania
    		{
    			UCSRB |= _BV(TXCIE); //ustaw 1 na bicie odblokowania przerwania zakończenia nadawania
    			UCSRB &= ~_BV(UDRIE); //ustaw 0 na bicie przerwania na pusty rejestr danych (rejestr jest zapełniony)
    		}
    		else
    		{
    			UCSRA |= BV(TXC);  //ustaw 1 na bicie świadczącym o zakończeniu transmisji
    			UDR = tx_buf[tx_send++]; //rejestr UDR jest równy wielkości wysyłanych danych w buforze wysyłania
    		}
    	}
    	else 
    		UCSRB &= ~_BV(UDRIE); //wyłącz przerwanie na pusty rejestr danych
    }
    
    //przerwanie do odbiornika	
    ISR(USART_RXC_vect)
    {
    	uint8_t err, chr;
    	err = UCSRA;
    	chr = UDR;
    	err &= _BV(FE) | _BV(PE) | _BV(DOR);
    	rs_time = RS_WAIT_2;
    	if (state == RS_WAIT_START)
    	{
    		state = RS_BUSY;
    		rs_time = RS_WAIT_3;
    	}
    	else if (state == RS_BUSY)
    		rs_time = RS_WAIT_3;
    	else if (err)
    		goto rs_trans_err;
    	else if (state == RS_ADRESS)
    	{
    		rs_crc = 0xFFFF;
    		rx_rec = 0;
    		state = RS_SIZE;
    		crc(chr);
    		rx_buf[rx_rec++] = chr;
    	}
    	else if (state == RS_SIZE)
    	{
    		if (chr & 0x80)
    		{
    			state = RS_CRC_L;
    			crc(chr);
    			rx_buf[rx_rec++] = chr;
    		}
    		else
    		{
    			uint8_t size = chr;
    			size &= 0x7F;
    			if (size > sizeof(rx_buf) - 3)
    			{
    				rs_err = RS_BO;
    				state = RS_BUSY;
    				rs_time = RS_WAIT_3;
    			}
    			else
    			{
    				state = RS_DATA;
    				crc(chr);
    				rx_buf[rx_rec++] = chr;
    			}
    		}
    	}
    	else if (state == RS_DATA)
    	{
    		if ((uint8_t)(rx_rec - rx_buf[1]) == 1)
    			state = RS_CRC_L;
    			crc(chr);
    			rx_buf[rx_rec++] = chr;
    	}
    	else if (state == RS_CRC_L)
    	{
    		if (chr == (uint8_t)rs_crc)
    			state = RS_CRC_H;
    		else
    			goto rs_trans_err;
    	}
    	else if (state == RS_CRC_H)
    	{
    		if (chr == (uint8_t)(rs_crc >> 8))
    		{
    			rs_flags |= _BV(RX_DR);
    			state = RS_BUSY;
    			rs_time = RS_WAIT_3;
    		}
    		else
    		rs_trans_err:
    		rs_flags |= _BV(RX_ERR);
    		rs_err = RS_TE;
    		state = RS_BUSY;
    		rs_time = RS_WAIT_3;
    	}
    }
    


    slave01.c
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <avr/crc16.h>
    #include <rs485.h>
    
    uint8_t  rs_adr;
    uint8_t  rs_flags, rs_err;
    uint8_t  tx_send, tx_cnt, rx_rec;
    uint16_t rs_crc;
    
    statc uint8_t rs_time, state;
    
    uint8_t rx_buf[RX_BUF_SIZE];
    uint8_t tx_buf[TX_BUF_SIZE];
    
    void crc(uint8_t data)
    {
    	rs_crc = _crc_ccitt_update(rs_crc, data);
    }
    
    void init_uart(void)
    {
    	UBRRL = (uint8_t)(RS_BR);
    	UBRRH = (RS_BR>>8);
    	UCSRB |= _BV(RXEN); //włączanie UART na odbieranie
    	UCSRB |= _BV(TXEN); //włączanie UART na wysyłanie
    	
    	TXEN_DDR|=_BV(TXEN_BIT);
    	
    	//ustawienie portów uarta RX i TX
    	DDRD  &= ~_BV(0);
    	PORTD |= _BV(0);
    	DDRD  |= _BV(1);
    	
    	//ustawienie parzystości, 8 bitów data, bitu stopu, asynchronizacja
    	UCSRC = _BV(URSEL) | _BV(UPM1) | _BV(UCS0) | _BV(UCS1) ;
    	UCSRB |= _BV(RXCIE); //włącza przerwania od RX
    }
    
    //odmierzanie czasów dla UART poprzez przerwanie TIMER0_OVF_vect
    ISR(TIM0_OVF_vect)
    {
    	if(rs_time)
    	{
    		rs_time--;
    		if(!rs_time)
    		{
    			if(state == RS_WAIT_START)
    				state = RS_ADRESS;
    			else if (state == RS_WAIT_TX)
    			{
    				TXEN_PORT |= _BV(TXEN_BIT);
    				tx_send = 0;
    				state = RS_TX;
    				UCSRB &= ~_BV(TXCIE);
    				UCSRB |= _BV(UDRIE);
    			}
    			else
    			{
    				rs_time = RS_WAIT_4 - RS_WAIT_2;
    				start = RS_WAIT_START;
    			}
    		}
    	}
    	
    	if ((sate == RS_WAIT_START) && (rs_flags & _BV(TX_RTS)))
    	{
    		TXEN_PORT |= _BV(TXEN_BIT);
    		rs_flags &= ~_BV(TX_RTS);
    		tx_send = 0;
    		state = RS_TX;
    		UCSRB |= _BV(UDRIE);
    	}
    	sei();
    }
    
    //USART_TXC_vect sprawdza czy interfejs jest w trybie nadawania. 
    ISR(USART_TXC_vect)
    {
    	if(TXEN_PORT & _BV(TXEN_BIT))
    	{
    		TXEN_PORT &= ~_BV(TXEN_BIT);
    			rs_time = RS_WAIT_4;
    			state = RS_WAIT_START;
    		
    	}
    }
    
    //Za wysyłanie danych odpowiedzialny jest USART_UDRE_vect
    ISR(USART_UDRE_vect)
    {
    	if (TXEN_PORT & _BV(TXEN_BIT) && (state == RS_TX))
    	{
    		if (tx_send == tx_cnt)
    		{
    			UCSRB |= _BV(TXCIE);
    			UCSRB &= ~_BV(UDRIE);
    		}
    		else
    		{
    			UCSRA |= BV(TXC);
    			UDR = tx_buf[tx_send++];
    		}
    	}
    	else 
    		UCSRB &= ~_BV(UDRIE);
    }
    
    //przerwanie do odbiornika	
    ISR(USART_RXC_vect)
    {
    	uint8_t err, chr;
    	err = UCSRA;
    	chr = UDR;
    	err &= _BV(FE) | _BV(PE) | _BV(DOR);
    	rs_time = RS_WAIT_2;
    	if ((state == RS_WAIT_START) || (state == RS_WAIT_TX))
    	{
    		rs_time = RS_WAIT_4;
    	}
    	else if (err)
    	{
    		if (state == RS_ADRESS)
    		{
    			rs_flags |= _BV(RS_REC);
    			rs_flags &= ~_BV(TX_RTS);
    		}
    		goto rs_trans_err;
    	}
    	else if (state == RS_ADRESS)
    	{
    		if (chr == rs_adr)
    		{
    			rs_flags |= _BV(RS_REC);
    			rs_flags &= ~_BV(TX_RTS);
    			state = RS_SIZE;
    			rs_crc = 0xFFFF;
    			crc(chr);
    		}
    		
    		
    		else if ((chr & RS_BROADCAST_FLAG) && (chr & RS_BROADCAST_GROUP))
    		{
    			rs_flags |= _BV(RS_REC);
    			rs_flags &= ~_BV(TX_RTS);
    			state = RS_SIZE;
    			rs_crc = 0xFFFF;
    			crc(chr);
    		}
    		
    		else if (chr == (rs_adr | RS_THRQ))
    		{
    			rs_flags &= ~_BV(TX_REC);
    			state = RS_CRC_L;
    			rs_crc = 0xFFFF;
    			crc(chr);
    		}
    		else
    			state = RS_WAIT_START;
    	}
    	
    	else if (state == RS_SIZE)
    	{
    		if (chr)
    		{
    			if (rs_flags & _BV(RX_DR))
    			{
    				rs_err = RS_FL;
    				state = RS_WAIT_START;
    			}
    			else if (chr > sizeof(rx_buf) - 1)
    			{
    				rs_err = RS_BO;
    				state = RS_WAIT_START;
    			}
    			else
    			{
    				rx_rec = 1;
    				state = RS_DATA;
    				rx_buf[rx_rec - 1]=chr;
    			}
    		}
    		else
    		{
    			rx_rec = 0;
    			state = RS_CRC_L;
    		}
    		crc(chr);
    	}
    	
    	else if (state == RS_DATA)
    	{
    		if(rx_rec == rx_buf[0])
    			state = RS_CRC_L;
    			rx_buf[rx_rec++] = chr;
    			crc(chr);
    	}
    	
    	else if (state == RS_CRC_L)
    	{
    		if (chr == (uint8_t)rs_crc)
    			state = RS_CRC_H;
    		else
    			goto rs_trans_err;
    	}
    	
    	else if (state == RS_CRC_H)
    	{
    		if (chr == (uint8_t)(rs_crc >>8))
    		{
    			if (rs_flags & _BV(RS_REC))
    			{
    				if (rx_rec !=0)
    					rs_flags |= _BV(RX_DR)
    				else
    					rs_flags = 0;
    					rs_err = 0;
    					state = RS_WAIT_START;
    			}
    			else
    			{
    				rs_time = RS_WAIT_05;
    				state = RS_WAIT_TX;
    				if (!(rs_flags & _BV(TX_RTS)))
    				{
    					uint8_t tmp;
    					tx_buf[0] = rs_adr;
    					tx_send = 0;
    					tx_cnt = 4;
    					rs_crc = 0xFFFF;
    					sei();
    					crc(rs_adr);
    					if (rs_flags & _BV(RX_DR))
    						tmp=0;
    					else if (rs_err)
    						tmp = rs_err;
    					else
    						tmp = NQ;
    						tmp |= 0x80;
    						tx_buf[1] = tmp;
    						crc(tmp);
    						tx_buf[2] = (uint8_t)rs_crc;
    						tx_buf[3] = (rs_crc >> 8);
    				}
    			}
    		}
    		else
    		{
    			rs_trans_err;
    			rs_time = RS_WAIT_4;
    			state = RS_WAIT_START;
    			if (rs_flags & _BV(RS_REC))
    				rs_err = RS_TE;
    		}
    	}
    }


    powyższe programy zostały zaczerpnięte z książki : A. Witkowski "Programowanie AVR".

    Jaki mam problem:
    to są programy do wysyłania i odbierania ramek. Teraz chciałbym zacząć już wymieniać dane. Podręcznik podaje, że trzeba zacząć od wykonania szeregu czynności:
    - zapisania wysyłanej ramki do bufora nadajnika
    - wpisania długości ramki do tx_cnt (zmienna wskazująca na długość ramki)
    - wyzerowania tx_send
    - ustawienia lub wyzerowania flagi RS_REC
    - poczekanie az interfejs znajdzie się w stanie RS_WAIT_START i przełączenia układu na nadawanie
    - zmiany stanu na RS_TX
    - uaktywnienie przerwania USART_UDRE_vect

    W załączniku jest fragment książki o tym jak to wykonać, a ja nie wiem jak się za to dokładnie wziąć.. największy problem to: zapisanie wysyłanej ramki do bufora nadajnika, oraz długość ramki (skąd to wziąć) i jak uaktywnić przerwanie USART_UDRE_vect.

    Dziękuję za wszelkie sugestie jak się za to zabrać.

    Pozdrawiam
  • REKLAMA
  • #2 8687902
    kubus_puchatek
    Poziom 18  
    Kod wklepany z tej książki jest piiiiiiiii...
    Nie mam czasu dokładnie analizować tego przykładu bo szybciej jest napisać nowy porządny niż zanalizować to coś. Nie wydzielono obsługi buforów okrężnych ale mi wychodzi że aby zapisać dane do bufora to musisz pchać dane pod tx_buf[tx_cnt++] i kontrolować czy nie najechałeś na tx_send. proponuję przepisać ten kod od zera bo to straszna ściema aż włos staje.
    Zapomniałem dodać że aby odblokować przerwanie od nadajnika to w rejestrach nadajnika jest bit który to odblokowuje i o to chodzi z tym wektoerm.
  • #3 8706024
    kubus_puchatek
    Poziom 18  
    Dostałeś porady na privie gdzie się zgłosiłeś więc zamykaj tego temata.
REKLAMA