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
master.c
slave01.c
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
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
#endifmaster.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