Sari la conținut
ELFORUM - Forumul electronistilor

timer digital ciclic cu pic


quinn

Postări Recomandate

Manualul de la xc8 e destul de detaliat referitor la volatile:

5.4.7.2 VOLATILE TYPE QUALIFIERThe volatile type qualifier is used to tell the compiler that an object cannot be guaranteed to retain its value between successive accesses. This prevents the optimizer from eliminating apparently redundant references to objects declared volatile because it can alter the behavior of the program to do so.Any SFR which can be modified by hardware or which drives hardware is qualified as volatile, and any variables which can be modified by interrupt routines should use this qualifier as well. For example:

volatile static unsigned int TACTL @ 0x160;

The volatile qualifier does not guarantee that any access will be atomic, which is often not the case with the 8-bit PIC MCU architecture. All these devices can only access a maximum of 1 byte of data per instruction. The code produced by the compiler to access volatile objects can be different to that to access ordinary variables, and typically the code will be longer and slower for volatile objects, so only use this qualifier if it is necessary. However, failure to use this qualifier when it is required can lead to code failure.Another use of the volatile keyword is to prevent variables being removed if they are not used in the C source. If a non-volatile variable is never used, or used in a way that has no effect on the program’s function, then it can be removed before code is generated by the compiler.A C statement that consists only of a volatile variable’s name will produce code that reads the variable’s memory location and discards the result. For example the entire statement:

PORTB;

will produce assembly code the reads PORTB, but does nothing with this value. This is useful for some peripheral registers that require reading to reset the state of interrupt flags. Normally such a statement is not encoded as it has no effect.Some variables are treated as being volatile even though they cannot be qualified in the source code. See Section 5.12.3.4 “Undefined Symbols” if you have assembly code in your project.

Editat de Liviu M
Link spre comentariu

Multumesc Liviu pentru completare. Si eu gasesc ca manualul pt XC8 este foarte instructiv si mai arunc cate o privire din cand in cand. Poate nu atat de des cat ar trebui...

 

Si altfel spus, mai concis, sursa este aici:

 

"What could cause corrupted variables or code failure when I am using interrupts?

FAQs » What could cause corrupted variables or code failure when I am using interrupts?

Posted on Thursday: 17 April 2014

Variables accessed from both interrupt and main-line code can easily become corrupted or misread by the program. Such variables should be qualified as "volatile". This will prevent the compiler from using cached copies of the variable in main line code from the Interrupt Service Routine code and vice versa. Volatile qualified variables will also not be optimized away. The compiler will also attempt to access the variable atomically, but this is not always possible if the assembly instruction set does not permit this.

You can check the assembly list file to see the assembly code used to access variables and determine if the access is atomic. If it is not, consider disabling the interrupts when it is accessed in main-line code."

Editat de mars01
Link spre comentariu

Am facut eu o prima versiune, am atasat simularea in proteus cat si codul sursa in mikroC.

L-a pornire intra meniul de setare a intervalului ON ( OFF se calculeaza automat din moment ce ON+OFF = 1 ora) cu butonul 2 se incrementeaza minutele cu butonul 3 se incrementeaza secundele (pentru "pedalare" la valori mari de ex. 50s se tine butonul apasat")dupa ce setarea este facuta se apasa butonul 1 se afiseaza un mesaj de confirmare si incepe ciclul cu intervalul ON. Daca se doreste modificarea intervalului in timpul functionarii se apasa butonul 1 si se seteaza din nou ca la pornire.

 

 

 

 

poti sa-mi zici te rog ce trebuie modificat in program ca sa nu se mai reia ciclul de temporizare?

Link spre comentariu

poti sa-mi zici te rog ce trebuie modificat in program ca sa nu se mai reia ciclul de temporizare?

 

Am facut modificarile si am lasat si partea cu ciclu repetitiv in comentariu + am facut modificarea recomandata la msTime in volatile

 

Modificarile au fost facute in setTime() si checkTimer()

 

Cod:

 

 

sbit LCD_RS at RA0_bit;sbit LCD_EN at RA1_bit;sbit LCD_D7 at RB7_bit;sbit LCD_D6 at RB6_bit;sbit LCD_D5 at RB5_bit;sbit LCD_D4 at RB4_bit;// Pin directionsbit LCD_RS_Direction at TRISA0_bit;sbit LCD_EN_Direction at TRISA1_bit;sbit LCD_D7_Direction at TRISB7_bit;sbit LCD_D6_Direction at TRISB6_bit;sbit LCD_D5_Direction at TRISB5_bit;sbit LCD_D4_Direction at TRISB4_bit;#define relay PORTB.F3volatile int msTime;//char relay=1;int onTime = 6 ; // in secundeint TimeLeft = 6;    // in secundechar *timeToLCD="00m00s";void Init_LCD() {   Lcd_Init();   Lcd_Cmd(_LCD_CURSOR_OFF);   Lcd_Cmd(_LCD_CLEAR);   Lcd_Out(1,6, "Hello!");   delay_ms(1000);   Lcd_Cmd(_LCD_CLEAR);}void InitTimer1(){  T1CON         = 0x31;  TMR1IF_bit         = 0;  TMR1H         = 0x0B;  TMR1L         = 0xDC;  TMR1IE_bit         = 1;  INTCON         = 0xC0;}void Interrupt(){  if (TMR1IF_bit){    TMR1IF_bit = 0;    TMR1H         = 0x0B;    TMR1L         = 0xDC;    msTime++;  }}void DisplayData() {  int TlSecounds,TlMinutes;  TlMinutes = TimeLeft/60;  TlSecounds = TimeLeft-TlMinutes*60;  Lcd_Out(1,1, "Relay is: ");    if(relay)   Lcd_Out(1,11, "ON ");  else   Lcd_Out(1,11, "OFF ");     Lcd_Out(2,1,"TimeLeft:");  timeToLCD[4] = TlSecounds%10+48;  timeToLCD[3] = TlSecounds/10%10+48;  timeToLCD[1] = TlMinutes%10+48;  timeToLCD[0] = TlMinutes/10%10+48;  Lcd_Out(2,10,timeToLCD);}void DisplaySetupData() { int TlSecounds,TlMinutes;  TlMinutes = onTime/60;  TlSecounds = onTime-TlMinutes*60;  Lcd_Out(2,1,"On Time:");  timeToLCD[4] = TlSecounds%10+48;  timeToLCD[3] = TlSecounds/10%10+48;  timeToLCD[1] = TlMinutes%10+48;  timeToLCD[0] = TlMinutes/10%10+48;  Lcd_Out(2,10,timeToLCD);}void CheckTime() {  if(msTime >= 10)  {      TimeLeft--;      msTime=0;          /*  if(TimeLeft == 0)              ciclu repetitiv      {        if(relay)          TimeLeft = 3600-OnTime;        else          TimeLeft = onTime;                    relay=!relay;            }     */            if(TimeLeft == 0)   // un singur ciclu      {        T1CON.F0 = 0;        relay = 0;      }  }}void SetTime() {    onTime = 0;    Lcd_Cmd(_LCD_CLEAR);    Lcd_Out(1,5,"Set Time");    delay_ms(500);    while(Button(&PORTB,0,10,0) == 0) {        DisplaySetupData();                if(Button(&PORTB,1,10,0))        {          onTime+=60;          delay_ms(250);        }        if(Button(&PORTB,2,10,0))        {          onTime+=1;          delay_ms(250);        }                }    Lcd_Cmd(_LCD_CLEAR);    Lcd_Out(1,2,"Time Setup Done!");    delay_ms(1000);    TimeLeft = onTime;    relay  = 1;    T1CON.F0 = 1;    Lcd_Cmd(_LCD_CLEAR);}void main() {   CMCON  |= 7;   TRISB = 0x7;   OPTION_REG.F7 = 0;   Init_LCD();   InitTimer1(); //100ms   setTime();    while(1)    {      if(Button(&PORTB,0,10,0))         SetTime();          checkTime();      DisplayData();      delay_ms(25);    }} 

 

 

Editat de bandi12
Link spre comentariu

cam asa arata timerul construit de mine cu ajutorul colegului "bandi12".Multumesc!  insa am o problema: atunci cand apesi butonul ON/OFF se activeaza releul si asa ramane si dupa ce iau alimentarea montajului. as vrea daca se poate modifica in program sa nu mai existe functia de ON/OFF.

 

 

post-183704-0-64924200-1443896010_thumb.jpg

Editat de Myhayxx
Link spre comentariu

@Myhayxx Din cate am inteles eu : Sa adaugat un buton On/Off care intrerupe alimentarea montajului, in momentul in care montajul se alimenteaza cupleaza releu si daca se intrerupe alimentarea montajului din butonul On/Off releul ramane cuplat ? 

 

@quinn: Nu este o problema, eu pana acuma nu am folosit abnotatia "volatile" si nu am avut probleme pana acuma. In cel mai extrem caz se pierd cateva millisecunde din timpul setat dar nu cred ca e cazul. 

Link spre comentariu

al patrulea buton care l-am pus este cel de reset. Problema se manifesta in felul urmator: daca tii apasat pe butonul ON/OFF releul isi schimba pozitia (initial fiind neatras) din decuplat in cuplat (ramanand atras tot timpul) chiar daca apas pe butonul de RESET sau ii iau alimentarea montajului(in timpul cand nu este alimentat releul nu mai e atras;dar la revenire cu tensiune se atrage din nou).tot in acest timp pot modifica valoarea de la minute si secunde dar in momentul cand ii dau start nu se intampla nimic(contorul se blocheaza) de aceea vroiam sa dezactivez aceasta functie. Mie imi trebuie ca la alimentarea montajului releul sa nu fie atras,setez timpul,pornesc cronometrul se atrage releul si dupa scurgerea timpului setat sa se opreasca.

Editat de Myhayxx
Link spre comentariu

Am inteles. Deci butonul on/off ar fi cel numit "menu" in simularea pe care l-am atasat. O "eroare" descoperita in simulare ar fi pornirea cronometrului cu 00m00s ( nechimbat valoarea se apasa  butonul start) in acel moment se blocheaza programul, probabil de aicea se trage problema. 

 

Fac corectarea pe codul meu sau postati / imi trimiteti pe privat codul sa nu mai trebuiasca sa modificati din nou textul de afisare ?

Editat de bandi12
Link spre comentariu

tot facand teste am mai intalnit si urmatoarea problema: la prima alimentare a timerului dupa ce a stat ceva timp nealimentat setez timpul iar cand ii dau start nu mai pleaca timerul, ramane blocat si isi revine greu dupa ce ii dau cateva reset-uri iar apoi merge fara probleme. S-ar putea sa fie din cauza ca am folosit PIC16F628 si nu PIC16F628A. Care este diferenta intre cele doua modele?

Link spre comentariu

Pentru un singur ciclu

 

 

 

sbit LCD_RS at RA0_bit;sbit LCD_EN at RA1_bit;sbit LCD_D7 at RB7_bit;sbit LCD_D6 at RB6_bit;sbit LCD_D5 at RB5_bit;sbit LCD_D4 at RB4_bit;// Pin directionsbit LCD_RS_Direction at TRISA0_bit;sbit LCD_EN_Direction at TRISA1_bit;sbit LCD_D7_Direction at TRISB7_bit;sbit LCD_D6_Direction at TRISB6_bit;sbit LCD_D5_Direction at TRISB5_bit;sbit LCD_D4_Direction at TRISB4_bit;#define relay PORTB.F3volatile int msTime;//char relay=1;int onTime = 6 ; // in secundeint TimeLeft = 6;    // in secundechar *timeToLCD="00m00s";void Init_LCD() {   Lcd_Init();   Lcd_Cmd(_LCD_CURSOR_OFF);   Lcd_Cmd(_LCD_CLEAR);   Lcd_Out(1,6, "Hello!");   delay_ms(1000);   Lcd_Cmd(_LCD_CLEAR);}void InitTimer1(){  T1CON         = 0x31;  TMR1IF_bit         = 0;  TMR1H         = 0x0B;  TMR1L         = 0xDC;  TMR1IE_bit         = 1;  INTCON         = 0xC0;}void Interrupt(){  if (TMR1IF_bit){    TMR1IF_bit = 0;    TMR1H         = 0x0B;    TMR1L         = 0xDC;    msTime++;  }}void DisplayData() {  int TlSecounds,TlMinutes;  TlMinutes = TimeLeft/60;  TlSecounds = TimeLeft-TlMinutes*60;  Lcd_Out(1,1, "Relay is: ");    if(relay)   Lcd_Out(1,11, "ON ");  else   Lcd_Out(1,11, "OFF ");     Lcd_Out(2,1,"TimeLeft:");  timeToLCD[4] = TlSecounds%10+48;  timeToLCD[3] = TlSecounds/10%10+48;  timeToLCD[1] = TlMinutes%10+48;  timeToLCD[0] = TlMinutes/10%10+48;  Lcd_Out(2,10,timeToLCD);}void DisplaySetupData() { int TlSecounds,TlMinutes;  TlMinutes = onTime/60;  TlSecounds = onTime-TlMinutes*60;  Lcd_Out(2,1,"On Time:");  timeToLCD[4] = TlSecounds%10+48;  timeToLCD[3] = TlSecounds/10%10+48;  timeToLCD[1] = TlMinutes%10+48;  timeToLCD[0] = TlMinutes/10%10+48;  Lcd_Out(2,10,timeToLCD);}void CheckTime() {  if(msTime >= 10)  {      TimeLeft--;      msTime=0;          /*  if(TimeLeft == 0)              ciclu repetitiv      {        if(relay)          TimeLeft = 3600-OnTime;        else          TimeLeft = onTime;                    relay=!relay;            }     */            if(TimeLeft == 0 && relay == 1)   // un singur ciclu      {        T1CON.F0 = 0;        relay = 0;      }  }}void SetTime() {    onTime = 0;    Lcd_Cmd(_LCD_CLEAR);    Lcd_Out(1,5,"Set Time");    delay_ms(500);    while(Button(&PORTB,0,10,0) == 0) {        DisplaySetupData();                if(Button(&PORTB,1,10,0))        {          onTime+=60;          delay_ms(250);        }        if(Button(&PORTB,2,10,0))        {          onTime+=1;          delay_ms(250);        }                }    Lcd_Cmd(_LCD_CLEAR);    Lcd_Out(1,2,"Time Setup Done!");    delay_ms(1000);    TimeLeft = onTime;    if(TimeLeft == 0)    {      relay = 0;      T1CON.F0 = 0;    }    else    {    relay  = 1;    T1CON.F0 = 1;    }    Lcd_Cmd(_LCD_CLEAR);}void main() {   CMCON  |= 7;   TRISB = 0x7;   relay = 0;   OPTION_REG.F7 = 0;   Init_LCD();   InitTimer1(); //100ms   T1CON.F0 = 0;   setTime();    while(1)    {      if(Button(&PORTB,0,10,0))         SetTime();          checkTime();      DisplayData();      delay_ms(25);    }} 

 

 

 

 

Pentru ciclu repetitiv :

 

 

 

sbit LCD_RS at RA0_bit;sbit LCD_EN at RA1_bit;sbit LCD_D7 at RB7_bit;sbit LCD_D6 at RB6_bit;sbit LCD_D5 at RB5_bit;sbit LCD_D4 at RB4_bit;// Pin directionsbit LCD_RS_Direction at TRISA0_bit;sbit LCD_EN_Direction at TRISA1_bit;sbit LCD_D7_Direction at TRISB7_bit;sbit LCD_D6_Direction at TRISB6_bit;sbit LCD_D5_Direction at TRISB5_bit;sbit LCD_D4_Direction at TRISB4_bit;#define relay PORTB.F3volatile int msTime;//char relay=1;int onTime = 6 ; // in secundeint TimeLeft = 6;    // in secundechar *timeToLCD="00m00s";void Init_LCD() {   Lcd_Init();   Lcd_Cmd(_LCD_CURSOR_OFF);   Lcd_Cmd(_LCD_CLEAR);   Lcd_Out(1,6, "Hello!");   delay_ms(1000);   Lcd_Cmd(_LCD_CLEAR);}void InitTimer1(){  T1CON         = 0x31;  TMR1IF_bit         = 0;  TMR1H         = 0x0B;  TMR1L         = 0xDC;  TMR1IE_bit         = 1;  INTCON         = 0xC0;}void Interrupt(){  if (TMR1IF_bit){    TMR1IF_bit = 0;    TMR1H         = 0x0B;    TMR1L         = 0xDC;    msTime++;  }}void DisplayData() {  int TlSecounds,TlMinutes;  TlMinutes = TimeLeft/60;  TlSecounds = TimeLeft-TlMinutes*60;  Lcd_Out(1,1, "Relay is: ");    if(relay)   Lcd_Out(1,11, "ON ");  else   Lcd_Out(1,11, "OFF ");     Lcd_Out(2,1,"TimeLeft:");  timeToLCD[4] = TlSecounds%10+48;  timeToLCD[3] = TlSecounds/10%10+48;  timeToLCD[1] = TlMinutes%10+48;  timeToLCD[0] = TlMinutes/10%10+48;  Lcd_Out(2,10,timeToLCD);}void DisplaySetupData() { int TlSecounds,TlMinutes;  TlMinutes = onTime/60;  TlSecounds = onTime-TlMinutes*60;  Lcd_Out(2,1,"On Time:");  timeToLCD[4] = TlSecounds%10+48;  timeToLCD[3] = TlSecounds/10%10+48;  timeToLCD[1] = TlMinutes%10+48;  timeToLCD[0] = TlMinutes/10%10+48;  Lcd_Out(2,10,timeToLCD);}void CheckTime() {  if(msTime >= 10)  {      TimeLeft--;      msTime=0;            if(TimeLeft == 0)             // ciclu repetitiv      {        if(relay)          TimeLeft = 3600-OnTime;        else          TimeLeft = onTime;                    relay=!relay;            }      /*      if(TimeLeft == 0 && relay == 1)   // un singur ciclu      {        T1CON.F0 = 0;        relay = 0;      }      */  }}void SetTime() {    onTime = 0;    Lcd_Cmd(_LCD_CLEAR);    Lcd_Out(1,5,"Set Time");    delay_ms(500);    while(Button(&PORTB,0,10,0) == 0) {        DisplaySetupData();                if(Button(&PORTB,1,10,0))        {          onTime+=60;          delay_ms(250);        }        if(Button(&PORTB,2,10,0))        {          onTime+=1;          delay_ms(250);        }                }    Lcd_Cmd(_LCD_CLEAR);    Lcd_Out(1,2,"Time Setup Done!");    delay_ms(1000);    TimeLeft = onTime;    if(TimeLeft == 0)    {      relay = 0;      T1CON.F0 = 0;    }    else    {    relay  = 1;    T1CON.F0 = 1;    }    Lcd_Cmd(_LCD_CLEAR);}void main() {   CMCON  |= 7;   TRISB = 0x7;   relay = 0;   OPTION_REG.F7 = 0;   Init_LCD();   InitTimer1(); //100ms   T1CON.F0 = 0;   setTime();    while(1)    {      if(Button(&PORTB,0,10,0))         SetTime();          checkTime();      DisplayData();      delay_ms(25);    }} 

 

 

 

 

Iesirea la alimentare este o greseala a mea, am uitat sa specific ca la pornire sa fie off si atunci PIC-ul la pornire nestind ce sa faca cu iesirea se produce o alegere aleatorie, de acolo a rezultat ca in momentul alimentarii cand e on cand e off.  

 

Nu ar trebuii sa faca figuri intre varianta normala si cea cu "A", eventual cand compilati codul in MikroC sa alegeti versiunea normala fara "A" si oscilatorul la 20Mhz.

Editat de bandi12
Link spre comentariu

acum programul eset in regula ca functionalitate. A mai ramas doar problema cu prima alimentare. ori de cate ori ii iau alimentarea si revin cu ea dupa ce setez ceasul si ii dau start se blocheaza isi revine doar dupa cateva operatii de setare pornire, reset apoi dupa ce isi revine functioneaza fara probleme de cate ori programez. nu-mi dau seama unde ar fi problema.. o sa fac un filmulet sa vedeti despre ce este vorba. daca nu reusesc il folosesc asa, o sa il las alimentat non-stop.

Link spre comentariu

Creează un cont sau autentifică-te pentru a adăuga comentariu

Trebuie să fi un membru pentru a putea lăsa un comentariu.

Creează un cont

Înregistrează-te pentru un nou cont în comunitatea nostră. Este simplu!

Înregistrează un nou cont

Autentificare

Ai deja un cont? Autentifică-te aici.

Autentifică-te acum
×
×
  • Creează nouă...

Informații Importante

Am plasat cookie-uri pe dispozitivul tău pentru a îmbunătății navigarea pe acest site. Poți modifica setările cookie, altfel considerăm că ești de acord să continui.Termeni de Utilizare si Ghidări