Jump to content
ELFORUM - Forumul electronistilor

X=X+1; // (Problema de C pt PIC)


Recommended Posts

In ultima vreme am lucrat cu CCS dar cred ca e vorba de orice C pt PIC.

Avem aranjata o rutina de intrerupere de timer (la interval de timp mic, zeci de microsec) in care exista instructiunea:

 

X=X+1;

X fiind o variabila de tip u32 (unsigned int de 32 biti).

 

In main, dupa ceva initializari avem o bucla "loopforever" in care exista instructiunea:

Y=X;

Y fiind de acelasi tip ca si X;

 

Pentru a efectua aceasta atribuire exista (in codul masina generat) un numar de instructiuni elementare care realizeaza copierea celor 4 octeti ai lui X peste cei 4 ai lui Y (translatarea in cod masina a instructiunii "evoluate" scrisa in C).

 

Intrebare: este posibil ca rezultatul obtinut in Y sa nu fie cel asteptat (de exemplu valorile lui Y sa fie incrementate cu foarte mult sau sa apara alte aberatii)? Lasam la o parte cazul de depasire, nu este vorba de asta.

In timp ce se executa secventa de instructiuni menita sa realizeze copierea celor 4 octeti, ce se intampla daca (exemplu) s-au copiat doar 2 octeti si apare "soroc de timp" care produce saltul la handlerul de intrerupere?

 

Exemplu:

- X are valoarea 0x0000FFFF (65535 in baza 10)

- incepe secventa de copiere X peste Y

- se copiaza primii 2 octeti (de la dreapta spre stanga), cei egali cu FF

- apare intreruperea, se sare la ISR, X devine 0x00010000

- se revine din ISR, se continua copierea cu octetii al treilea (devenit 0x01) si al patrulea

- rezultat final: Y = 0x0001FFFF adica "o tampenie".

Adica s-au copiat 2 octeti din valoarea veche a lui X (inainte de intrerupere) si 2 din valoarea noua.

Daca trebuie sa se sara la un handler de intrerupere atunci instructiunea curenta este lasata sa se termine (este o regula, este hardware) dar este vorba de instructiunea elementara curenta, nu de instructiunea C.

 

Conform cu anumite dorinte ale programatorului creator, compilatorul asigura ceva care sa evite aceasta situatie?

In fine, daca situatia este reala, cum se poate evita?

Link to comment
  • Replies 18
  • Created
  • Last Reply

Top Posters In This Topic

Compilatorul nu are cum sa includa asa ceva, el doar genereaza codul in assembler, care copiaza octet cu octet. Alte variante nu prea vad, in afara de activarea intreruperii doar dupa initializarea variabilei. Dar nu vad unde e problema aici: undeva intreruperea tot e activata. Pur si simplu initializezi variabila inainte.

Link to comment

Am spus de bucla "forever". Secventa este cam asa:LoopForever:// ...Y=X;// ...goto LoopForever;Deci atribuirea se intampla in mod repetat. Sa consideram ca bucla este parcursa suficient de des, ca se ajunge pe instructiunea de atribuire asa incat nici o valoare a lui X nu scapa "neatribuita lui Y".Dupa cum am intrebat, este posibil ca Y sa primeasca valoarea 0x0000FFFF (65535) iar la parcurgerea urmatoare a buclei, Y sa primeasca valoarea 0x0001FFFF (131071) ceea ce este de neacceptat.Asta e pericolul sau, ma rog, intreb daca este sau nu este si cum se evita.

Link to comment

Scuze, intelesesem gresit.Trebuie facut un calcul sa vezi cat dureaza atribuirea, si daca e mai mare perioada intreruperilor. Daca e mai mare perioada, atunci poti dezactiva intreruperea, pentru ca flagul oricum se va seta, si se va activa atunci cand setezi bitul IE. Pericolul ca atribuirea sa fie intrerupta la mijloc exista.

Link to comment

Cel mai simplu e sa dezactivezi intreruperile, dupa cum s-a mai spus, flag-ul intreruperii va ramane setat si la re-activarea intreruperilor se va executa rutina de tratare a lor.

 

Alternativ, cred ca se poate face ceva de genul:

 

unsigned long x, y;unsigned long z;bit flag;void interrupt ISR() {     x++;     if(flag)         z = x; }void main() {     while(1)      {          flag = 0;          y = z;          flag = 1;      } }
Link to comment

In esenta vrei ca valoarea dintr-o valoare sa fie incrementata la intervale de timp bine stabilite. Din pacate nu ai solutie in C sau orice limbaj de nivel inalt atunci cand trebuie sa controlezi timpul de executie. Singura solutie ramane limbajul de asamblare unde stii exact cat dureaza executia unei instructiuni.Ar mai fi o solutie. Daca timpul intre 2 intreruperi consecutive este comparabil cu ceasul microcontrolerului, incearca sa treci la o frecventa mai mare si doar schimbi presetarile care timer-ului care genereaza intreruperea

Link to comment

Deci este acceptabil ca bucla sa nu prinda toate valorile (consecutive) ale lui X dar este inacceptabil sa sara chiar cu 65536 cum am spus inainte.

 

Ce opinie aveti de urmatoarea idee? In bucla de forever sa nu folosim variabila X ci o copie a ei, numita XXX. Depunerea lui X peste XXX se va face in ISR, intr-un moment cand nu este pericol, cand utilizarea celor 4 octeti cu scop de depunere peste Y nu este "in progress". Introducem un delay si un flag boolean, astfel:

LoopForever:// ...SuntInDelay=TRUE;delay_ms(5);   // 5 milisecSuntInDelay=FALSE;Y=XXX;// ...goto LoopForever;

iar in ISR (de la timer1, la 100 microsec) sa avem ceva de genul:

X++;if(  TRUE==SuntInDelay ){ // acum este cert ca bucla NU executa o atribuire cu destinatia Y // XXX=X; // alte copieri de variabile in aceeasi situatie }
Este destul de asemanator cu ceea ce a spus Cristiano, in plus fiind doar "asigurarea suplimentara" cu delay-ul.
Link to comment

eu zic sa faci asain ISR dupa ce ai incrementat X pui Z=1in bucla loopforever testezi Z daca este 1 atunci copiezi X in Y si Z=0void interrupt ISR() { x++; z = 1; } void main() { if (Z=1) { y = x; Z= 0; } } cam asa ceva in C(eu nu stiu C prea bine)

Link to comment

Eu unul zic ca te invarti in jurul cozii.Daca "bucata protejata" + durata ISR este mai scurta decat perioada intreruperii, atunci blochezi pur si simplu intreruperile pe durata... protejata.Problemele adevarate le vei avea doar in caz contrar si atunci va trebui sa reproiectezi un pic.

Link to comment

eu zic sa faci asain ISR dupa ce ai incrementat X pui Z=1in bucla loopforever testezi Z daca este 1 atunci copiezi X in Y si Z=0void interrupt ISR() { x++; z = 1; } void main() { if (Z=1) { y = x; Z= 0; } } cam asa ceva in C(eu nu stiu C prea bine)

Nu merge, pentru ca in timpul asignarii y=x din main se poate produce o intrerupere care sa modifice valoarea lui x (deoarece lucrandu-se cu intregi, asignarea nu se poate efectua intr-un singur ciclu - chiar daca s-ar lucra cu octeti, adica char ori unsigned char, la asignare se genereaza ceva de genul:movfw reg1movwf reg2iar intre cele 2 operatii o intrerupere poate modifica valoarea lui reg1).
Link to comment

:drinkers: e posibil sa sara la o valoare aiurea pt ca intreruperea modifica pe X posibil in timp ce main_loop face atribuirea care dureaza mai multe instructiuni. Trbuie dezactivate intreruperea care modifica pe X pe durata atribuirii, daca asta nu este acceptabil atunci trebuie regandit programul, si in general pt chestii de finete ranunta la C si foloseste ASM, incearca de exemplu sa rezolvi totul in rutina de intrerupere inclusiv atribuirea.

Link to comment

Cristiano, consider solutia ta de mai sus valida, buna, corecta. Dar tu cum o consideri pe a mea? :) Am adugat tehnologia cu delay-ul pentru a putea rezolva mai multe situatii asemanatoare, mai multe variabile pe care ISR-ul si bucla le "share-aza". Pe durata delay-ului variabila X nu este utilizata de catre loop deci ISR-ul o poate utiliza fara pericol. In 5 milisec (5000 microsec) variabila X va fi incrementata de 50 de ori iar bucla va prinde in cel mai bun caz valori din 50 in 50. Aceasta este acceptabil sau, ma rog, se mai poate tatona (micsora) parametrul delay-ului. Esential este ca se evita ca bucla sa "perceapa" o incrementare aiurea a lui X, de exemplu cu 65536 cum am descris.Analog, prim "metoda delay-ului" se pot rezolva si alte chestiuni de concurenta intre ISR si bucla.

Link to comment

cum am mai zis nu stiu C prea bine

 

@cristiano

eu zic ca merge ptr ca in bucla main se tot testeaza Z=1

si abia cind este Z=1 atunci incepe si copiaza 4 bytes

sa zicem ca apare o intrerupere undeva cind a testat Z=1 si se duce sa testeze iar (GOTO)

 

Picul se duce in ISR pune x++ si z=1

se intoarce si testeaza Z=1 si atunci incepe si copiaza

 

o singura problema este

atunci cind copieza timpul sa nu fie mai lung decit intreruperea (100uSecunde)

 

in asm se face asa :

 

cblock 0x20	X0,X1,X2,X3	Y0,Y1,Y2,Y3	Flagendc#define	ISR_Ok	Flag,0ISR :	incf	X0,f		;X0..X3 variabila pe 4Bytes (X0 low, X3 high)	btfss	STATUS,Z	goto	ISR_End	incf	X1,f	btfss	STATUS,Z	goto	ISR_End	incf	X2,f	btfss	STATUS,Z	goto	ISR_End	incf	X3,fISR_End:	bsf	ISR_Ok		;Set Intrerupere	retfieMain:	btfss	ISR_Ok		;AICI se tot invirte pina apare o intrerupere	goto	Main		;De la un timer sau altceva	movfw	X0	movwf	Y0	movfw	X1	movwf	Y1	movfw	X2	movwf	Y2	movfw	X3	movwf	Y3	bsf	ISR_Ok		;Reset intrerupere	goto	Main
Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now



×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.Terms of Use si Guidelines