PIC UART problemen

Beste

Ik ben bezig aan een projectje met een PIC16F18325 waarbij de data die ontvangen wordt over de UART wordt door gezonden naar een SPI bus. Voor dat de data verder wordt doorgestuurd wordt er hier nog eerst een header aan toegevoegt. Nu zet ik steeds de data over van de variable die in de interrupt wodt gebruikt naar een variabele die de SPI data zal moeten versturen. Nu is het probleem dat het soms heel goed gaat maar soms mis ik karakters en dit is ongewenst op 36 karakters heb ik al eens 3 karrakters gemist. Dit gebeurt ook volledig random. Ik ga niet de volledige code hier plaatsen maar enkel het stukje waar dit probleem mee te maken heeft anders wordt het wat veel.

Het eerste stukje code is het stukje uit de interrupt service routine. Hier wordt de data steeds aangevult wanneer er niewe data beschikbaar is op de seriele poort.

c code:

   if(RCIF & DataReceive) 
    {
        RXFIFO[RXFIFOCounter] = RCREG;
        RXFIFOCounter ++;
        RCIF = 0;
    }

De DataReceive is een parramater wanneer de buffers worden overgezet. In de laatste nieuwe testen heb ik deze niet meer gebruikt omdat ik dacht dat er juist op die moment data binnen kwam en daar mee vorloren ging. Helaas was dit nog niet de oplossing.

Het stukje code hieronder toont de overzetting van de ene variabele naar de andere. Ook hier is de DataReceive variable nog opgenomen maar dit is ook getest geweets zonder deze variable te gebruiken.

c code:

int i = 0;
        DataReceive = 0;
        char RXCounterStatic = RXFIFOCounter;
        char SPITransmitCounterStatic = SPITransmitCounter[0];
            for(i; i < RXCounterStatic; i++)
            {
                SPITransmit[0][SPITransmitCounterStatic - 2 +i] = RXFIFO[0];
                    int j = 0;
                    for(j; j < RXFIFOCounter; j++)
                    {
                        RXFIFO[j] = RXFIFO[j + 1];
                    }
                RXFIFOCounter --;
                SPITransmitCounter[0] ++;
            }
        DataReceive = 1;
        

Het lijkt er op dat het mis gaat rijdens het overzetten van de data. Op deze momment zal er juist iets nieuw binnen komen waardoor dit gemist wordt. Dat is mijn vermoede maar ik kom er niet goed uit hoe ik dit kan oplossen.

Hopelijk heeft iemand van jullie het verlossend antwoord.

RS232

Arco

Special Member

RXFIFOCounter heeft geen enkele check of die buiten de array bounds komt, dus dat is wachten op ongelukken... :)
De SPI snelheid moet ook groter zijn als de UART anders krijg je ook problemen...

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

Oke dat begrijp ik maar ik heb gezien dat deze nooit meer als 9 elementen gebruikt terwijl deze array 170 elementen groot is dus ik denk niet dat hier het probleem ligt of zie jij dat anders?

De effectiefe SPI snelheid is groter dan de UART snelheid maar het is wel zo dat deze niet constant aangesproken wordt omdat er andere dingenen nog bezeig zijn op de SPI bus. Als het probleem dan hiermee te maken heeft hoe kan ik dit dan oplossen (buiten het snellet aanspreken van deze uc)?

JoWi

Special Member

Ik zou tijdens het kopiëren van de data (de for loop) even de interrupts uitzetten en daarna weer aan. Wanneer je variabelen in een interrupt gebruikt en ook in de maincode is dat vragen om problemen. Die RXCounterStatic is dan ook niet nodig.
Dat kopieren kan trouwens efficiënter dan twee geneste loops.

Ignorance is bliss

JOWI z'n tip dat gaat werken: je hebt problemen met FIFO. Je schrijft aan beide kanten in de FIFO. Dat moet je niet doen.

De belangrijkste fout is dat je de data op gaat schuiven nadat je 1 karakter uit de FIFO hebt gehaald. Als in die tijd nog een karakter binnenkomt, wordt die op de volgende plek in de fifo gezet, maar daarna niet verschoven in de buffer.

Stel je hebt nu 10 karakters in de buffer (locaties 0-9), je stuurt de eerste naar SPI en begint te copieeren. Nu komt er een 11e binnen. Die wordt op locatie 10 neer gezet, maar je copieert maar tot en met locatie 9. Als je "recieve interrupt" tijdens de RXFIFOCounter-- plaats vindt, heb je helemaal de poppen aan het dansen. Intern is dat altijd: lees RXFIFOCounter in een register, verlaag het register, en schrijf het terug. Als net voor of na het "verlaag het register" de variabele in de interrupt wordt opgehoogd ben je de pineut....

De beste truuk is om te zeggen dat SCHRIJVEN naar de buffer en naar RXFIFOCounter alleen in de interrupt mag gebeuren. De "lees-de-buffer" code mag RXFIFOCounter NIET veranderen.
In de Uart interrupt:

code:


 RXFIFO[RXFIFO_head] = RCREG;
 if (RXFIFO_head >= RXFIFO_SIZE-1) RXFIFO_head = 0;
 else                              RXFIFO_head++; 

En dan in je verwerk de fifo data:

code:


if (RXFIFO_tail != RXFIFO_head) { // er is data in de buffer. 
   SPI ... =  RXFIFO[RXFIFO_tail];
   if (RXFIFO_tail >= RXFIFO_SIZE-1) RXFIFO_tail = 0;
   else                              RXFIFO_tail++; 
}

In de RX interrupt moet je eigenlijk controleren dat je buffer niet vol is. Dat gebeurt door te controleren dat de nieuwe RXFIFO_head niet gelijk wordt aan RXFIFO_tail.

Merk op dat het ophogen (en omlopen naar nul) een beetje raar gebeurt. Dat is uiteindelijk beter omdat er zo niet een "tijdelijk niet geldige waarde" in de variabelen zitten.

Ohja. In jou code is er een for lus met "j". Jou variabele RXFIFOCounter wordt dan door de compiler in een register gezet voor de duratie van die lus. Dus een verandering van de variabele door de interrupt wordt niet gezien. Of ie NA de lus zo slim is om RXFIFOCounter uit het register te gebruiken, betwijfel ik. Als ie dat wel doet, is het window waarin het bij jou fout gaat veel groter.

Jou code kan je BIJNA goed krijgen door RXFIFOCounter als volatile te declareren. De performance van je onnodige loop gaat dan achteruit. En het window waarin het verkeerd kan gaan wordt veel kleiner. (alleen tijdens de laatste copieer-een-byte-loop.)

P.S. Om mijn code zonder het uitzetten van interrupts (dat is het paardenmiddel) werkend te krijgen, moet je schrijf de head-en-tail variabelen atomisch zijn. Dus als je op een 8bit computer tail++ doet, mag het niet zo zijn dat ie van 0x00ff via 0x0000 naar 0x0100 gaat doordat er twee writes naar het geheugen moeten gebeuren. Die fifo counters kunnen dus het beste als byte gedeclareerd worden op een 8-bitter.

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

Special Member

Ringbuffer gebruiken met een read en write pointer, dan kan het nooit misgaan...

Pseudocode write:

pic basic code:


Buffer[WrPtr] = UartRx
Incr WrPtr
If WrPtr > Ubound(Buffer) Then WrPtr = 0

Pseudocode read:

pic basic code:


If RdPtr <> WrPtr Then
  SPIval = Buffer[RdPtr]
  Incr RdPtr
  If RdPtr > Ubound(Buffer) Then RdPtr = 0
End If

[Bericht gewijzigd door Arco op maandag 23 mei 2016 11:38:50 (64%)

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

Oke als eerste bedankt voor al de reacties!!!

Het afzetten van de interrupt had ik ook al aan gedacht en ook al geprobeerd. Helaas verdween de fout hierdoor niet omdat ik dan in tussentijd al een andere byte binnen kreeg waardoor de voorgaande verloren ging.

@Arco
Als ik het dan goed begrijp komt de pseudocode van de write in de interrupt te staan en zal deze altijd een goede counter hebben die altijd overeen komt met de effectief ontvangen data. Zo dus kan de interrupt ook steeds blijven opstaan en kan er zo al geen data meer verloren gaan. Het uit lezen gebeurt dan buiten de interrupt met een aparte counter zodat deze nooit in conflict kan komen met de write counter. Is de redenering correct?

Arco

Special Member

Klopt. Enige manier waarop nog data kan zoekraken is als de UART data sneller binnenkomt als dat 'ie verwerkt wordt, en het buffer een overflow krijgt...

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

Even voor de goede orde: Arco zegt precies hetzelfde als ik. Ik heb er alleen wat meer omheen getypt.

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

@rew
Jij ook bedankt voor de goede uitleg! Met alle input ben ik tot een goede conclusie kunnen komen waarvoor dank!

Door dit toe te passen is dit probleem vermeden. Enkel als ik wat veel interrupts binnen krijg van de SPI module gaat het nog de mist in. Ik heb dan ook gemerkt dat er dan een overrun is van de UART poort. Het is zo dat de code die in de SPI interrupt staat wel redelijk groot is maar dit is echt noodzakelijk.

Hoe kan ik dit nog volgens jullie oplossen?

Bij de 8-bit µC is het niet mogelijk om prioriteiten voor de interrupt in te stellen maar is hier misschien nog een andere oplossing voor buiten het verkleinen van de code in de interrupt routine van de SPI module?

Arco

Special Member

SPI zaken kun je toch ook in main() afhandelen? (waarom moet dat in een interrupt?)

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

voor de toepassing die ik ga gebruiken is dit noodzakelijk dus hier kan ik niet van afstappen. Is er nog een andere mogelijkheid volgens jullie?

Arco

Special Member

SPI wordt altijd door de master geinitieerd. Dus ik zie niet waar een interrupt voor nodig is?
Mocht er toch een reden te bedenken zijn, beperk dan de acties in die interrupt tot een minimum.

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

Op 24 mei 2016 23:05:51 schreef RS232:
Hoe kan ik dit nog volgens jullie oplossen?

Kijk: Je doet TE VEEL in je SPI interrupt. Of je interrupt prioriteiten zijn verkeerd. (misschien kan je dat niet instellen).

Maar de enige hint die ik kan geven zonder verdere informatie is: kies een andere (snellere) processor.

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

Als de beschreven taak (bijna) het enige is dat die uC moet doen, dan heb je echt geen snellere nodig hoor. Dit ding kan op 32 MHz (als ik Google na 2 sec althans mag geloven).

Als het dan niet lukt, dan klinkt dat meer als slecht doordachte code.
Ik kan me helemaal vinden in wat Arco en rew zeggen: je interruptroutine lijkt gewoon te lang. Liefst alleen een vlaggetje zetten, en indien nodig lepel je snel even de (kleine) hardwarebuffer leeg naar je s/w-FIFO, maar that's it.

If you want to succeed, double your failure rate.

Op 25 mei 2016 10:51:42 schreef Jochem:
Dit ding kan op 32 MHz (als ik Google na 2 sec althans mag geloven).

Enig wantrouwen is op z'n plaats. Microchip had toen ik daar nog mee werkte processoren op 12MHz, die dan 12 clocks per instructie namen....

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

Special Member

Op 25 mei 2016 11:46:55 schreef rew:
[...]Enig wantrouwen is op z'n plaats. Microchip had toen ik daar nog mee werkte processoren op 12MHz, die dan 12 clocks per instructie namen....

Liet je die speciaal maken?... :) (12 clockcycle pics zijn er nooit geweest, en 12MHz versies ook niet)
Waren 4 clockcycles per instructie. De oudste (de 16C54) was dat al en die was er in 4 en 20MHz versie. Zelfs de 'oer-pic' (PIC1650) van GI was dat (alleen maar 1MHz)
Nieuwere pics zijn 1 clock per instructie en tot 200MHz...

Ik denk dat je in de knoop zit met de 8051, die was wel 12 clocks per instructie en was er ook in 12 MHz...

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

Je heb weer helemaal gelijk. Dat was het. Maar goed, d'r was dus ook met PICs iets, maar niet helemaal zo erg als 12 clocks per instructie.

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