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.

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

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

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 ?

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...

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 (41%)]

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:


  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.

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.

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..

Kort omdat het vanaf mijn telefoon moet,
Het is data afkomstig van een "scanner" als daarvoor b.v. een hoofdletter P passeert zie je in die te verzenden data die letter terug, wanneer je de bytes op de juiste volgorde legt.
Er mag best ergens een bitje verkeerd staan. Kabellengte is ca. 10 mrt. Geen opstartende motoren in de buurt. De kans op storingen van buiten af lijkt mij vrij klein.

kans is niet zo groot dat dit gebeurdt,...maar het gaat een keer gebeuren.

Och... de kans is ongeveer 1... :)

hoe los je dit dan op ? ik moet tot 300 kunnen tellen dus 16 bits.

Je blijft maar volhouden aan tellen in een ISR en er gaat niks fout enzo he! DAT WERKT DUS NIET.

ik heb een boaudrate 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 ?

Maak de ISR kort. Dan hoef je daar niet over na te denken. Maar je blijft volhouden om het ingewikkeld te doen met alle ellende die dat tot gevolg heeft...
Kort door de bocht: op 16MHz zal een AVR een kleine 16 miljoen instructies per seconde uitvoeren (niet alles is single-cycle). Met 1000 characters per seconde, kun je dus 16000 instructies uitvoeren. Een beetje ISR is met 50-100 instructies wel klaar, zelf als je die in C schrijft. Dan heb ik een factor 160 over. Zelfs als ik in mijn inschatting een factor 2 verkeerd zit, dan heb ik nog steeds een factor 80 over: dat gaat zonder er over na te denken wel lukken hoor!

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

Lompe variant: stuur van elke byte de hex representatie. Ja, het wordt 2x zoveel data. En je hebt ruimte voor wat control characters. Zit de bitrate 2x zo hoog en 2x zoveel data is er net zo snel...

Er mag best ergens een bitje verkeerd staan. Kabellengte is ca. 10 mrt. Geen opstartende motoren in de buurt. De kans op storingen van buiten af lijkt mij vrij klein.

En nou zet je het ding aan. Die lijnen klapperen wat... Er komt bij je ontvanger 1 byte binnen (het zien van een startbit is tenslotte genoeg voor de UART). De boel is out-of-sync en nu? Alles resetten? Kom, dat maak je toch niet zo!

Als je geen protocol wilt gebruiken, kun je iets simpels als een time-out gebruiken. Als er langer als xxx mS niks meer binnenkomt de code als compleet zien.
Bij nieuwe data gewoon buffer resetten en opnieuw beginnen. Je zult op een of andere manier moeten synchroniseren, anders gaat 't nooit (goed) werken.

Dat is OOK een vorm van protocol... Beetje wonky, maar hey...

Tja, heb je ueberhaupt invloed op de te verzenden dataset ?
- Kun je bijv. start-of-message/end-of-message karakters toevoegen ?
- Kun je een sumcheck toevoegen ?
- zijn er bytewaardes die niet/nooit kunnen/mogen voorkomen
- worden de 300 bytes achterelkaar gestuurd of kunnen er onderbrekingen voorkomen ?
- hoeveel tijd zit er tussen twee van die opvolgende berichten?
Zomaar een paar vragen om te kijken wat er dan wel mogelijk is als je geen echt protocol wilt of kunt gebruiken.
Nou ga ik morgen een week op vakantie met nauwelijks serieuze mogelijkheden om te te reageren. Maar daarna wil ik er best wat verder over nadenken. In mijn werkzame verleden heb ik letterlijk tientallen protocollen moeten implementeren om data uit randapparatuur in onze dataloggers in te lezen.

Ik zit nu op een telefoon te "pielen" ik reageer woensdag inhoudelijk, bedankt voor de reacties _/-\o_

Om de teller in de mainloop uit te lezen kun je een botte interrupt disable / lees teller / interrupt enable doen.

Zoiets dus:



 int atomic_teller;

 cli();
 atomic_teller = TSOP_byte_transfer_counter;
 sei();

 ... doe nu alle compares etc op "atomic_teller"

Met een echt O/S doe je dat met semaphores of mutexes oid.

Hoe bedoel je... een ECHT OS? Er IS helemaal geen OS!

Ik heb er nog ff over na zitten denken, en ik denk dat een werkbare oplossing iets als volgt zou kunnen zijn:

Je maakt een ringbuffer van je 300 bytes. De UART-ISR mikt daar braaf elk volgend character in wat binnen komt tenzij de 'status' flag 'complete' is. Dan doe je helemaal niks (nou ja, UART uitlezen, omdat-ie dat fijn vindt als-ie een IRQ geeft).
Tevens zet je (als je wat doet) de timerCounter variabele op 0 als je een character in de buffer mietert en zet de status op 'reading'.

Je maakt een timer-ISR die zeg elke 100μS ofzo loopt. Die doet iets als: if timerCounter<255 timerCounter++ ofzo.
Verder doet die: if (status=='reading') && (timerCounter>timeout) status='complete'

In je main loop kijk je alleen naar status. Zodra die 'complete' wordt, hebben we blijkbaar een time-out. De laatste 300 bytes zijn blijkbaar mijn data. Je kijkt waar het begin van de ringbuffer zit, doet wat leuks met je data. Let wel: alles wat op dit moment binnen komt wordt naar /dev/null gerouteerd.

Als je data 'veilig' is (of verwerkt...) dan zet je status op 'waiting'. Vanaf nu kan er weer in de buffer geschreven worden en is de data daarin dus 'instabiel'.

De waarden van 'status' zijn iets van 'reading', 'complete' en 'waiting' ofzo. Ik zou daar defines van maken om de code wat leesbaarder te houden.

Door dit foefje voorkom je ook het 16-bit variabele probleem. Immers, OF de ISR zit aan de buffer OF iets wat vanuit main aangeroepen is. Ofwel: als 'main' aan je buffer zit, dan blijft de ISR er vanaf. Als de ISR er aan zit, dan wordt main geacht niks te doen.

Daarnaast zou je kunnen overwegen om met je 300 bytes iets als CRC ofzo mee te sturen. Dat worden dan gewoon een paar bytes extra.

Voorwaarde is wel dat de zendende kant iets met die timing kan. Als timing daar absoluut niet kritisch is, dan zou ik het verzenden gewoon lekker vanuit de main doen. Daarna een delay van 100mS ofzo. En dan ga je weer wat doen. Lomp, maar simpel en effectief.

Maar Eric, Een "300 byte buffer" is voor een ATMEGA328 een behoorlijke aanslag op het geheugen.

Dit project wordt zonder "architectuur" aan mekaar geprutst waardoor er steeds weer nieuwe problemen opduiken die met een beetje "vooraf nadenken" makkelijk voorkomen hadden kunnen worden.

Extern RAM? WTF? jaren zeventig technologie! (ok, jaren tachtig dan!). (Huidige, 2020 technologie is: 256Mb onchip RAM, maar dat is een beetje "state of the art" en nog niet makkelijk te verwerken voor hobbyisten.

Nog steeds vanaf de telefoon:
het is een atmega 2560.

Kom, dat ding van trix heeft 8k. Dat moet met die 300 bytes toch wel lukken?

Op 1 september 2020 07:13:28 schreef EricP:
Hoe bedoel je... een ECHT OS? Er IS helemaal geen OS!

Weet is dat het er niet is, dus "echt" mag er tussen uit.

Ook dat gedoe met flags en die 100us timer is absoluut niet thread safe. Je verschuift het probleem van de atomic compare van je "main" naar je timer ISR en wint er dus niks mee.

@trix: Het wordt misschien tijd dat ik weer eens een bakkie koffie kom drinken?

Ook dat gedoe met flags en die 100us timer is absoluut niet thread safe. Je verschuift het probleem van de atomic compare van je "main" naar je timer ISR en wint er dus niks mee.

Het grappige is... dat dat dus WEL safe is. Om te beginnen werk je overal waar mogelijk meerdere 'threads' tegelijk in roeren met 8-bits variabelen. Die compare is daarmee atomair. En dus thread safe. Daarnaast doen AVRs - tenzij je echt gaat trucen - niet aan interruptable interrupts. Dus er KAN maar 1 ISR tegelijk in zitten roeren - waarmee een atomaire compare dus niet eens meer nodig is. Let wel: het is geen OS met pre-emtive multi-tasking. Het gaat over een ISR op een μC.

Op 1 september 2020 20:35:29 schreef henri62:
@trix: Het wordt misschien tijd dat ik weer eens een bakkie koffie kom drinken?

dat mag altijd natuurlijk, graag.

Op 1 september 2020 21:23:18 schreef EricP:
[...]Het grappige is... dat dat dus WEL safe is. Om te beginnen werk je overal waar mogelijk meerdere 'threads' tegelijk in roeren met 8-bits variabelen. Die compare is daarmee atomair. En dus thread safe.

Je moet ergens die 300 comparen, dat is per definitie 2 bytes dus niet atomic. Die bewering klopt niet.

Daarnaast doen AVRs - tenzij je echt gaat trucen - niet aan interruptable interrupts. Dus er KAN maar 1 ISR tegelijk in zitten roeren - waarmee een atomaire compare dus niet eens meer nodig is.

Inderdaad, het lijkt erop dat de atmega2560 niet aan nested interrupts doet, dan is het inderdaad safe om dat te doen. Ik ging er vanuit dat de atmega dat wel doet (zoals heel veel cpu's en controllers). Dus daarmee kom je dus weg.