serieele comunicatie loopt soms "vast"

trix

Golden Member

goedendag,

dit is een onderdeel van een groter project, maar te groot om het even volledig uit te leggen. vandaar dit specifieke probleem.

ik moet vanuit een atmega32 300 bytes versturen naar een atmega2560.
dit gaat over de UART (TX & RX).
het werkt wel maar het loopt nogal vaak "vast" ik weet ook waar, maar weet de oplossing nog niet. het loopt vast in de ISR, dit heb ik bepaald d.m.v. een LED.

nou gebruik ik een methode die denk ik niet goed is:
de slave stuurd 300 bytes naar de master, om te bepalen of die 300 bytes binnen zijn hoog ik in de ISR (v/d RX) een teller op, wanner deze de 300 bereikt ga ik verder....en dat doet hij ds niet altijd.

is dit wel de juiste methode om te bepalen of alle bytes v/d slave binnen zijn ?
of moet je b.v. als laatste byte een speciale code meesturen zodat de master weet dat alles binnen is ?

ik wil uiteraard wel de code posten, maar ik probeer het even bij de basics te houden.

tnx.

eigenwijs = ook wijs
Arco

Special Member

In de ISR moet je altijd *alles* lezen wat binnenkomt. Je kunt niet na 300 karakters stoppen als er nog meer komt.
(de ISR loopt dan vast)

Desnoods doe je dummy reads, maar je moet de boel uitlezen.
Ik stuur meestal een <CR> of <ETX> als laatste karakter. (soms gebruik ik een '#')

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com

Wat doe je met de teller als er per ongeluk maar 299 bytes binnenkomen in plaats van de 300? gaat het programma dan niet verder? En loopt het daardoor vast?

Ik zou denk ik een buffer volschrijven en weer opnieuw beginnen bij ontvangst van een Eol (End Of Line) of zo iets. En ook opnieuw beginnen bij een time out als die om een of andere reden nooit komt.

Edit:

Eol = End Of Line. Werd vroeger bij sommige communicatie's wel gebruikt. Maar elk willekeurig voor jou handig bitpatroon is natuurlijk bruikbaar.

[Bericht gewijzigd door Ex-fietser op zaterdag 29 augustus 2020 11:56:33 (18%)

trix

Golden Member

@ arco: het gaat bij mij niet specifiek over karakters, maar gewoon data.
maar jij gebruikt dus gewoon een "slotbyte"

wat is een Eol ? (slotbyte denk ik),....end of line zie ik net

eigenwijs = ook wijs

Laatste byte heeft niet veel zin denk ik als deze niet binnen komt.

Wat wel kan is beginnen met een start byte en eventueel eindigen met een stopbyte.

De isr laat je dan pas starten met tellen als het startbyte gezien word.
Waar het mis kan gaan dat de ontvanger halverwege een bericht start.

Een timeout indien niet het hele bericht ontvangen is erbij maken, en isr op 0 zetten.

trix

Golden Member

Op 29 augustus 2020 11:51:36 schreef Ex-fietser:
Ik zou denk ik een buffer volschrijven en weer opnieuw beginnen bij ontvangst van een Eol (End Of Line) of zo iets. En ook opnieuw beginnen bij een time out als die om een of andere reden nooit komt.

die binnen gekomen data word meteen in een ext. RAM geschreven (23LC1024)

eigenwijs = ook wijs
Arco

Special Member

Foutje...
Moet ETX zijn (0x03, End of transmission)

De routine loopt trouwens niet vast, maar staat netjes te wachten tot je de interrupt hebt afgehandeld...

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com

Je hebt weer zo'n "vage omschrijving" waarbij het enige juiste antwoord is: "Je hebt een fout gemaakt in je programma".

Je moet "beducht" zijn op foutjes. Dus zoals ex-fietser zegt: Wat als er maar 299 karakters binnen komen?

Als de boel dan niet vastloopt maar zegt: Ik had 300 verwacht maar krijg er maar 298 dan weet je dat er twee zoek zijn geraakt. Of de zender is ze vergeten te zenden of de ontvanger heeft er twee gemist.

Verder op een AVR: een teller die tot 300 kan lopen is een int van 16 bits. Dan moet je een beetje uitkijken: als jij in het gewone programma dan die variabele ook verandert, dan krijg je gezeik: Stel de waarde is 255 (0xff) en je pakt een byte uit de buffer, dus hij moet 1 omlaag. het programma leest dan de 255, verlaagt hem met 1, en bewaart de 254 dan. Vervolgens wordt de bovenste byte opgehaald, niet veranderd en teruggezet. (dat is sneller dan een if om te kijken OF ie veranderd moet worden).

Als nu na het ophalen van de 255 uit het geheugen een ISR tussendoor de boel ophoogt naar 256, dan wordt daarna de 254 teruggeschreven en het bovenste byte niet veranderd: resultaat 0x01fe: 510.

Je stelt de vraag alsof sommige dingen MOETEN. Dat is gewoon niet zo. Je mag best het aantal bytes tellen en dan weten: Nu moet het klaar zijn. Je kan ook een start-code en eind-code gebruiken, maar dan moet je zorgen dat de start- en eind-codes niet in de data voorkomen. Je krijgt dan iets als <start> = 2, <eind> = 3, <escape> =4; Stuur je <escape><escape> dan is dat escape, escape-5 = "2" en escape-6 = "3". Als je random data hebt wordt een gemiddeld pakket van 256 bytes nu 259 bytes. Dat is te overzien. In jou geval met 300 bytes dus gemiddeld iets van bijna 304. En dan de start- en eindcode nog, dus 306 bytes. Maar dan kan je dus zonder te "hangen" als er niet precies 300 bytes binnenkomen zien: hij denkt nu dat ie klaar is, dus het zouden 300 bytes moeten zijn. Als je er dan 150 ,299 of 301 hebt dan weet je dat het niet klopt en kan je ergens een foutmelding geven en gaan uitzoeken wat er aan de hand is. Dat vastlopen is Heel vervelend en maakt het uitzoeken wat er gebeurd is heel lastig.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
Arco

Special Member

Als je (zoals meestal gebruikelijk) een ringbuffer met read/write pointers gebruikt kan er al helemaal nooit wat vastlopen...

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com
trix

Golden Member

hoe gaat dat ongeveer zo'n ringbuffer ?

eigenwijs = ook wijs
bprosman

Golden Member

De jongere generatie loopt veel te vaak zijn PIC achterna.
trix

Golden Member

ik heb een code geschreven waarbij het volgens mij niet uitmaakt hoeveel bytes de slave verstuurd. de master telt gewoon tot 300 en stopt dan.
maar met deze code loopt hij nogal vaak vast, dus iets is er natuurlijk niet goed.

korte beschrijving code:
1e - verzoek aan slave: send your dat 1e byte
2e - binnen halen data v/d slave en meteen in de ext. RAM schrijven.

voor de duidelijkheid heb ik de code terug gebracht tot het nodige. dit is de code voor 1 byte. in werkelijkheid haal ik 3 bytes achter elkaar binnen.
wanneer ik de allerlaatste _delay_ms(10); verhoog naar b.v. 200 mS gaat het een stuk beter, en blijft de code minder vaak hangen. dat wekt de indruk dat een delay op de juiste plek wellicht voldoet.

c code:


volatile int receivedbyte               = 0; // needed for incoming data from TSOP's
volatile int TSOP_byte_transfer_counter	= 0;
volatile int transfer_ready		= 0;

ISR(USART0_RX_vect) // receving data from TSOP's
{
    receivedbyte = UDR0; // copy the received byte value into the variable "receivedbyte"	
    TSOP_byte_transfer_counter += 1;	
	
    _delay_us(50); // just for testing ******************************************	

    SPDR = receivedbyte; // data what you write in the ext. RAM
    while(!(SPSR & (1<<SPIF))); // Wait for transmission complete		
}

c code:


PORTE |= (1 << PINE2); // switch MAX485 as transmitter pin 2 & 3 = "1"
PORTE |= (1 << PINE3); // switch MAX485 as transmitter pin 2 & 3 = "1"					
					
UCSR0B |= (1 << TXB80); //Set the TXB8 bit (9e bit) to 1: we gonna send a addres
	
// *** adress numbering: TSAL 1-20 and TSOP 21-40 *******************************************************

TSOP_adres = scanner_read + 21;			
	
_delay_ms(1);
while ((UCSR0A & (1 << UDRE0)) == 0) {}; // do nothing till the UDR is ready to receive
bytetosend = TSOP_adres; // is the adress TSOP
UDR0 = bytetosend;		
							
_delay_ms(100);
while ((UCSR0A & (1 << UDRE0)) == 0) {}; // do nothing till the UDR is ready to receive
bytetosend = 0b00000001; // is the data: TSOP send your data 1e byte to me.
UDR0 = bytetosend;					
				
_delay_ms(1);
while ((UCSR0A & (1 << UDRE0)) == 0) {}; // do nothing till the UDR is ready to receive
bytetosend = 0b11111111; // is the data witch is the stop code
UDR0 = bytetosend;			
			
tsop_request_send_your_data_to_me_finished = 1; // used in interrupt, changed PINE2 & PINE3	to "0"				
			 
UCSR0A &= ~(1 << MPCM0); // switch off the MPCM mode for receiving data		
			 			
// *** below: putting the data from the TSOP in to the ext. RAM ******************************************
		
PORTH |= (1 << PINH7); // LED = 1 just for checking ROOD ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
PORTG &= ~(1 << PING5); // make SS ext. RAM pin = 0						
						
RAM_adress = ((scanner_read * 3000) + 1);						
			
SPI_transfer(0b00000010);  // write data to memory
address = RAM_adress; // is address in ext. RAM
			
SPI_transfer((address>>16)); // adress
SPI_transfer((address>>8)); // adress
SPI_transfer((address)); // adress						
			
while (TSOP_byte_transfer_counter < 300) // doing loop until 300 bytes are in. ^^^^^^^^^^^^^^^^^^^^
{
        // TSOP_byte_transfer_counter is increased in the ISR
	// data is there also written to the ext. RAM  								
}
TSOP_byte_transfer_counter = 0; // incr. in the ISR. if 300 bytes are in make variable "0"						

											
PORTG |= (1 << PING5); // make SS ext. RAM pin = 1									
PORTH &= ~(1 << PINH7); // LED = 0 just for checking ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
				
_delay_ms(10);
eigenwijs = ook wijs
trix

Golden Member

het werkt,
zoals al gezegd, wil ik gewoon 300 bytes binnen halen en dan stoppen.
het probleem zat hem in de manier waarop ik tot 300 telde en daar op actie ondernam.
ik heb een extra variabele toegevoegd: transfer_ready
waarom het ene wel werkt en het andere niet :? ....maar het werkt.

eerst deed ik:
in de ISR:

c code:


TSOP_byte_transfer_counter += 1;

en in main:

c code:


TSOP_byte_transfer_counter = 0; // incr. in the ISR. if 300 bytes are in make variable "0"

while (TSOP_byte_transfer_counter < 300) // doing loop until 300 bytes are in. ^^^^^^^^^^^^^^^^^^^^
{
        // TSOP_byte_transfer_counter is increased in the ISR
	// data is there also written to the ext. RAM  								
}

nu doe ik:
in de ISR:

c code:


TSOP_byte_transfer_counter += 1;
	
	if (TSOP_byte_transfer_counter == 300)
	{
		transfer_ready = 1;
	}

en in main:

c code:


TSOP_byte_transfer_counter = 0; // incr. in the ISR. if 300 bytes are in make variable "0"
						
		while (transfer_ready == 0) // doing loop until 300 bytes are in. ^^^^^^^^^^^^^^^^^^^^
		{
			// TSOP_byte_transfer_counter is increased in the ISR
			// data is there also written to the ext. RAM
		}
												
		transfer_ready = 0;
eigenwijs = ook wijs
EricP

mét CE

Je concept is verkeerd.
ISR mikt data in een buffer. Meer niet. Op buffer full kun je bepalen dat data die dan binnen komt gewoon weg gegooid word of oudere data overschreven wordt (en dat is eng omdat het rare bijverschijnselen kan hebben die je niet altijd aan dit verschijnsel kunt relateren).

Ergens in je programma lees je data uit die buffer. Op de een of andere manier herken je het 'begin' en zodra je dat tegen komen ga je uit de buffer lezen tot je je 300 bytes hebt. Dan ff kijken of eea. ook zinnig is - iets met een CRC ofzo. En DAN kun je wat gaan processen.

Uiteraard kun je eea. ook samen in de ISR frotten. Iets complexer programmeren. En je loopt het risico dat je '300 bytes image' half oud, half nieuwe data bevat. Scheelt wel in memory. Of je moet pas als je de boel compleet hebt een copy doen. Maar ja, zoiets in een ISR is ook weer 'not done'.

Gewoon er van uit gaan dat wat je ontvangt altijd door de ander verstuurd is en dat er nooit wat ontbreekt / extra bij zit, is vragen om ellende. Dat zou best een tijdje synchroon kunnen werken, maar robuust is het absoluut niet...

Kijk eens hoe ethernet packets in elkaar zitten. Of hoe IP packets in elkaar zitten. Dan heb je enig idee hoe zij binaire dataoverdracht hebben gedaan.

JoWi

Special Member

Op 30 augustus 2020 09:37:28 schreef trix:
waarom het ene wel werkt en het andere niet :?

Omdat je niet begrijpt hoe je met interrupts moet werken.

Dat het nu werkt komt omdat tranfer_ready een 8bit variabele is en je counter een 16 bit variabele. Je heb gewoon mazzel.

Bestudeer dit eens: https://www.embedded.com/interrupts-short-simple-part-2-variables-buff…

Afgezien daarvan ga je ervanuit dat je in een foutloze wereld leeft waar errors nooit voorkomen.
Zo iets van: Mijn programma doet het, maar als het onweert of de buurman zijn draaibank aanzet in de buurt gaat het soms fout
(D'r gaat gewoon een van je karakters om zeep door een glitch die ge-induceerd wordt)

Ignorance is bliss

Ik ga helemaal mee met EricP.
Dit concept deugt niet. Je moet iets van een protocol gebruiken waarbij je aan het einde van het bericht een CRC16 of desnoods een sumcheck van de verstuurde data meezend.
Bijv. een uitgeklede variant van het Modbus RTU protocol waarbij je de lengte en instructiecode zou kunnen weglaten omdat je altijd (?) 300 bytes verstuurd. Afhankelijk van kabellengte en omgeving (storingsrijk ?) kun je in plaats van een crc16 een wat eenvoudiger sumcheck gebruiken. Verder is van belang of de zender moet weten dat het bericht goed ontvangen is en eventueel moet herhalen. Dus de ontvanger moet een responsecode sturen waarop de zender eventueel het bericht moet herhalen.
Modbus is een relatief eenvoudig en goedwerkend protocol. In jouw geval kun je het wat versimpelen.

bprosman

Golden Member

Heb SNAP altijd een mooi licht, makkelijk te implementeren protocol gevonden.

http://www.hth.com/snap/

De jongere generatie loopt veel te vaak zijn PIC achterna.
trix

Golden Member

bedankt voor de input.

Op 30 augustus 2020 09:51:23 schreef EricP:
Je concept is verkeerd.
ISR mikt data in een buffer. Meer niet. Op buffer full kun je bepalen dat data die dan binnen komt gewoon weg gegooid word of oudere data overschreven wordt (en dat is eng omdat het rare bijverschijnselen kan hebben die je niet altijd aan dit verschijnsel kunt relateren).

mikt data in een buffer: wat/waar is die buffer ?
ik schrijf het nu meteen in de ext. RAM
na buffer full (300 bytes) mag de rest worden weggegooid.

Op 30 augustus 2020 09:51:23 schreef EricP:
Ergens in je programma lees je data uit die buffer.

en dat mag dus niet in de ISR, ik moet dan buiten de ISR de data in de ext. RAM schrijven ?

Op 30 augustus 2020 09:51:23 schreef EricP:
Uiteraard kun je eea. ook samen in de ISR frotten. Iets complexer programmeren. En je loopt het risico dat je '300 bytes image' half oud, half nieuwe data bevat.

en dat is dus wat ik nu doe ?

Op 30 augustus 2020 10:04:50 schreef JoWi:
Dat het nu werkt komt omdat tranfer_ready een 8bit variabele is en je counter een 16 bit variabele. Je heb gewoon mazzel.

transfer_ready is toch gedeclareerd als een int = 16 bit toch ?

eigenwijs = ook wijs
Arco

Special Member

Het belangrijkste probleem is dat je nu blijft wachten op 300 bytes.
Als om een of andere reden er een byte (of meer) wegvalt, blijft je code eeuwig wachten op de ontbrekende bytes.
(en misgaan doet 't ooit, dat is nu eenmaal een zekerheid... ;) )

Zelfs als de code weer 'doorloopt' bij ontvangst van de volgende 300 bytes, dan wordt dat een onbruikbare mix van de eerste en tweede 300 bytes data.
Kortom, je hebt inderdaad een protocol (en eventueel een time-out) nodig om alles in goede banen te leiden.

Wat betreft de interrupt: daar moet je zo min mogelijk doen / alleen essentieele zaken.

In sommige gevallen kan het handig zijn om toch meer 'processing' te doen in een interrupt, meestal als de processor niet teveel verschillende taken heeft uit te voeren.
Je moet dan wel heel zeker weten dat die extra code uitgevoerd kan worden voordat de volgende interrupt arriveert, anders ga je interrupts missen...

Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com
JoWi

Special Member

Op 30 augustus 2020 10:39:02 schreef trix:
transfer_ready is toch gedeclareerd als een int = 16 bit toch ?

Waarvan het hoge byte altijd 0 blijft.

Als je variabelen test of manipuleert in twee threads heb je daar een mutex voor nodig (of het moet met een atomic operation). In jouw geval heb je de main code en de interrupt routine. (Voor een 8bit mcu is de oplossing vaak: disable ints, test variable, enable ints).

[Bericht gewijzigd door JoWi op zondag 30 augustus 2020 13:01:21 (41%)

Ignorance is bliss
EricP

mét CE

mikt data in een buffer: wat/waar is die buffer ?

Nou eh... gewoon een stukkie RAM van die controller? Waar wil je het anders laten?

ik schrijf het nu meteen in de ext. RAM

Dat is dus erg onhandig en vragen om problemen. Zeker vanuit een ISR

na buffer full (300 bytes) mag de rest worden weggegooid.

En hoe weet je dan dat je de juist 300 bytes hebt? ZO simpel is het dus niet.

en dat mag dus niet in de ISR, ik moet dan buiten de ISR de data in de ext. RAM schrijven ?

Van mij wel hoor. Maar... OF je maakt je ISR 'rete snel' en laat 'm geen rare dingen doen. Dan hoef je er (bijna) niet over na te denken. OF je doet alles in de ISR, MAAR dan moet je na gaan denken over timing en andere interrupts die niet afgehandeld worden in de tijd dat jij met die RAM staat te pielen. Alhoewel de laatste niet onmogelijk is (ja, ik heb het ook wel eens gedaan), moet je wel verduveld goed snappen wat je aan het doen bent. Met alle respect: als ik je vragen zo zie, ben jij daar nog lang niet. (geeft niks, ik was er ook niet in de eerste week dat ik met interrupts werkte hoor)

en dat is dus wat ik nu doe ?

Je haalt nu gewoon 300 bytes binnen. Zijn het er 301... eh? Zijn het er 299... eh... En valt er onderweg een bit om... eh... Stap af van het concept '300 bytes'. Er komt gewoon data binnen op die UART. En het zou kunnen dat die 300 bytes die jij hebben wilt daar tussen zitten. DAT is de juiste benadering. Ofwel: input bekijken tot je iets tegen komt waarvan je denkt... Hmz... dit zou wel eens voor mij kunnen zijn. Kijken of het klopt. En DAN ga je er eens wat mee doen.

Als je variabelen test of manipuleert in twee threads heb je daar een mutex voor nodig (of het moet met een atomic operation). In jouw geval heb je de main code en de interrupt routine. (Voor een 8bit mcu is de oplossing vaak: disable ints, test variable, enable ints).

Je kunt overdrijven he... Een 8-bit compare is atomair. Dus dat gaat altijd wel goed. Een 16-bits compare wordt al een ander paar mouwen.

@Arco: bij een AVR gaat 'de volgende' interrupt nog goed. Zolang die maar niet dezelfde source heeft :) Maar in essentie zie ik het ook zo.

[edit]
Concept van een stukkie code wat NMEA strings binnen lepelt. Helaas een stuk bestaande hardware wat ff voor een ander doel gebruikt moet worden met een wat 'krappe' controller, dus ff trucen: niet genoeg ram om het eerst fatsoenlijk te bufferen en eruit te lepelen wat ik hebben wil...
De ISR in pseudo code:

code:


  c=getFromUART
  if ('$' == c)  (daar begint een NMEA string mee)
  {
    stringReady = 0;
    weAreReading = 1;
    charInBuff = 0;
  } else if (CR == c ) (dat is bruikbaar als terminator)
  {
    stringReady = 1;
    weAreReading = 0;
  }
  else
  {
    if (( charInBuff < MAX ) && (0 != weAreReading ))
    {
       buff[charInBuf] = c;
       charInBuff++;
    }
  }

In de main kijk je naar stringReady en zodra die 1 is, moet je als je raket wat doen met die string (en daarna StringReady 0 maken). Dit gaat goed, omdat

  • NMEA data 'traag' binnen komt. Als je main te traag is in deze variant, zul je daar wat mee moeten
  • Mocht je een keer wat missen, dan is het geen drama. Alles wat van belang is (in deze context) komt elke seconde binnen. Mis je wat... nou ja, het komt zo weer voorbij
  • Je kunt evt. ook nog een beetje filtering in je ISR doen. Als je bijvoorbeeld alleen GPRMC wilt hebben, dan kun je kijken of die ook binnen komen en zo nee, dan al afbreken. Je ISR wordt wel steeds groter en complexer.

Kortom: dit is een beetje 'wonky', maar voor het doel werkt het uitsteekbaar.

Je hebt zowizo een probleem met de variable "TSOP_byte_transfer_counter " die wordt opgehoogd in de ISR en getest in je main loop. Het ophogen en testen van die variable is in een 8-bit controller niet atomic. Dat wil zeggen dat er tussen het vergelijken van het LSB van deze teller en de MSB er een ISR tussendoor kan komen en dan kan de zaak de soep in lopen.

Stel de counter is 255 dus 0x00ff. In je main loop test de code eerst het low byte dus de 0xff, dan komt de interrupt en maakt van die variable 0x0100 (+1 dus), dan kom je terug in de main loop en gaat de code het MSB testen, wat nu dus geen 0x00 meer is maar 0x01.
De main loop ziet dan dus heel even 0x01ff = 511 dan kun je wel raden wat er gebeurd.

Dit gebeurd dus niet altijd maar "soms".

-edit- Post een stukje gekruist met EricP.

1-st law of Henri: De wet van behoud van ellende. 2-nd law of Henri: Ellende komt nooit alleen.
trix

Golden Member

dat is wel duidelijk uit gelegd wat er fout kan gaan met MSB & LSB.
kans is niet zo groot dat dit gebeurd,...maar het gaat een keer gebeuren.
hoe los je dit dan op ? ik moet tot 300 kunnen tellen dus 16 bits.

op het moment van data versturen v/d slave naar de master, gebeurt er niks anders, zeg maar gerust helemaal niks.

ik heb een boudrate van 9600, dat betekent volgens mij dat er elke milli seconde 1 byte binnen komt. dus mijn bewerkingen in de ISR mogen nooit langer duren dan 1 milli seconden, hoe controleer ik of dit ook het geval is ?

ik moet nog even nadenken en lezen over een protocol, hoe dat het best gaat in mijn niet veel eisende specifieke situatie.

eigenwijs = ook wijs

Een beetje controller loopt gemakkelijk aan 16 MHz, dat zijn dus wel 16000 instructies per ms ! Zorg er dus voor dat in de ISR alleen het noodzakelijke wordt geprogrammeerd, en zeker geen delay's of wachten op externe gebeurtenissen. Een eenvoudige test om te tijdsduur van een ISR te bepalen is het zetten/resetten van een uitgangspin. Op de scoop kan je dan perfect de tijdsduur meten.

Ik ben niet bekend met Atmega processoren; dit vooropgesteld.
Maar in mijn microcontroller verleden (vooral 8051 derivaten) had je hetzelfde probleem dat er voor operaties over meer dan 8 bits twee of meer instructies nodig waren waardoor dus het genoemde probleem kon optreden.
Er was een relatief simpele oplossing daarvoor:
Tellen in de interruptroutine; als er een byte ontvangen is tel je dat daar. Vervolgens uitlezen in de mainloop via een disable/lees/enable instructie combinatie.
Ga er maar niet vanuit dat als je processor maar snel genoeg is dat de kans dat dit probleem optreedt zo klein is dat.....het gaat eens gebeuren. Ik spreek uit ervaring :-)

Wat ik nog niet gehoord heb is hoe die 300 bytes zijn opgebouwd.
Zijn het bijv. gewoon opvolgende meetwaarden van een sensor waarbij het onbelangrijk is dat er een keer eentje wegvalt? Of is het een samenhangende gestructureerde dataset waarvan er echt niet zomaar eentje mag ontbreken..