danpin Postat Iulie 8, 2015 Autor Partajează Postat Iulie 8, 2015 (editat) Salutari, am reluat de curand proiectul pe care-l abandonasem un timp... Am avut ceva probleme la scrierea PIC-ului; cu o clona Pickit2 nu vroia de fel sa scrie programul corect, am incercat cu un programator pe serial, clona dupa un Velleman si merge dar de fiecare data cand vreau sa scriu in Pic trebuie sa-l sterg mai intai altfel imi da eroare. Am doua pic-uri amandoua se comporta la fel. Cum spunea Fratello intr-o postare anterioara: " Multe aplicatii software (in care generam o purtatoare de 38 KHz sau un PWM variabil) imi mergeau sacadat IN SIMULARE, dar perfect in realitate !!!" asa este, in simulare digitii se vad sacadat dar in montaj functioneaza bine. Singura chestie care nu-mi prea place este faptul ca ultimul digit dupa virgula si uneori cel al unitatilor cam "salta" aiurea, ceva de genul: 27,4; 27,8; 27,3, 26,9; 27,2; 26,7 cam asa ceva. Rog pe cineva care se pricepe sa-mi dea un sfat cum asi putea face sa am o indicatie mai stabila, eventual la modalitatea de a face calculele mai precise la masurarea temperaturii. In simulare indicatia este stabila, in montaj nu. In motaj la iesirea lui LM35 am pus si un AO cu amplif. x2, oricum cu sau fara AO indicatia ultimilor 2 digiti tot "salta" ciudat. In program am mai introdus si niste functii pt masurarea curentului si tensiunii pt. protectia la suprasarcina si scc. Cam asta ar fi programul: unsigned short mask(int num){ switch (num) { case 0 : return 0xC0; // anod comun 0 case 1 : return 0xF9; // anod comun 1 case 2 : return 0xA4; // anod comun 2 case 3 : return 0xB0; // anod comun 3 case 4 : return 0x99; // anod comun 4 case 5 : return 0x92; // anod comun 5 case 6 : return 0x82; // anod comun 6 case 7 : return 0xF8; // anod comun 7 case 8 : return 0x80; // anod comun 8 case 9 : return 0x90; // anod comun 9 case 10 : return 0xFF; // anod comun blank case 11 : return 0xFE; // anod comun seg a case 12 : return 0xFD; // anod comun seg b case 13 : return 0xFB; // anod comun seg c case 14 : return 0xF7; // anod comun seg d case 15 : return 0xEF; // anod comun seg e case 16 : return 0xDF; // anod comun seg f case 17 : return 0xBF; // anod comun seg g case 18 : return 0x7F; // anod comun dp case 19 : return 0xFF; // anod comun blank }}unsigned char ch, ADCx;unsigned char anods_index; // index anods (1,2,3)unsigned int Voltage, Current, digit, x=0;unsigned short anods_array[3]; // anodsunsigned long V, A, Temperature, number, tlong;unsigned short current_duty, current_duty1;void anods () { ++anods_index; // Increment anods_index with one every interrupt if (anods_index>3) anods_index=0; switch (anods_index) // Turn ON one anod every interrupt { case 1: PORTA.F2 = 1; break; case 2: PORTC.F4 = 1; break; case 3: PORTA.F5 = 1; break; } } void my_port(int ValueToSend) // Group various pins in one virtual port { RB4_bit = (ValueToSend); //lsb RB5_bit = (ValueToSend>>=1); RB6_bit = (ValueToSend>>=1); RB7_bit = (ValueToSend>>=1); RC0_bit = (ValueToSend>>=1); RC1_bit = (ValueToSend>>=1); RC2_bit = (ValueToSend>>=1); RC3_bit = (ValueToSend>>=1); //msb }void interrupt(){ if(T0IF) // Check Timer0 overflow interrupt flag { PORTA.F2 = 0; // Turn OFF anod digit1 PORTC.F4 = 0; // Turn OFF anod digit2 PORTA.F5 = 0; // Turn OFF anod digit3 my_port(anods_array[anods_index]); anods(); TMR0 = 100; // TMR0 preload INTCON.T0IF = 0; // Clear timer0 interrupt flag x=++x; // Increment x with one every interrupt }} void intro() // Initial segments check{ for (digit=10; digit<20; digit++) { Delay_ms(250); anods_array[0] = mask(digit); anods_array[1] = mask(digit); anods_array[2] = mask(digit); }}void display(){ if (number<1000) { digit = (number/100u); if(digit<1) { digit=10; anods_array[0] = mask(digit); } else anods_array[0] = mask(digit); digit = (number/10u) % 10u; anods_array[1] = mask(digit)&0x7F; digit = number%10u; anods_array[2] = mask(digit); } else { digit = (number/1000u); anods_array[0] = mask(digit); digit = (number/100u) % 10u; anods_array[1] = mask(digit); digit = (number/10)% 10u; anods_array[2] = mask(digit); }}void temp_ADC() // Reading the analog inputs (ADC){ Temperature = 0; for (ADCx=0; ADCx<5; ADCx++) { Temperature += ADC_Read(9); // Reading voltage values from channel 9 //Delay_us(50); } Temperature = (Temperature/ADCx); // Temperature (Voltage) calculation tlong = (long)Temperature*2500; // Millivolts conversion number = (long)tlong/1023; // 0...1023 => 0...2500mV //Delay_us(50);}void volt_ADC() // Reading the analog inputs (ADC){ Voltage = 0; for (ADCx=0; ADCx<5; ADCx++) { Voltage += ADC_Read(3); // Reading voltage values from channel 3 //Delay_us(20); } Voltage = Voltage/ADCx; // Voltage calculation V = (long)Voltage*2500; // Millivolts conversion V = V/1023; // 0...1023 => 0...2500mV}void crt_ADC() // Reading the analog inputs (ADC){ Current = 0; for (ADCx=0; ADCx<5; ADCx++) { Current += ADC_Read(0); // Reading current values from channel 0 //Delay_us(20); } Current = Current/ADCx; // Current calculation A = (long)Current*2500; // Millivolts conversion A = A/1023; // 0..1023 => 0...2500mV}void fan(){ if (number >=350) // Start PWM for Temperature >35°C { PWM1_Start(); // Init PWM for Single Output PWM1_Set_Duty(number*0.28); // Set the PWM Duty Cycle if (number >=800) PWM1_Set_Duty(255); // Start with Max Duty Cycle for Temperature >80°C } if (number <300) PWM1_Stop(); // Turn OFF PWM for Temperature <30°C} void main(){ OPTION_REG = 0; OPTION_REG = 0b10000101; //Prescaler 1:64; TMR0 Preload = 100; //Actual Interrupt Time: 4.992 ms INTCON = 0b10100000; OSCCON = 0b01110111; // 8Mz; Internal oscillator ADCON0 = 0b11100111; // Right justified; Vref pin ADCON1 = 0b01010000; // A/D Conversion clock: Fosc/16 TRISA = 0x1B; // Set PORTA direction: RA2,5 output; // RA0,1,3,4 input TRISB = 0x00; // Set PORTB direction to be output TRISC = 0x80; // Set PORTC direction: RC0-6 output; // RC7 input ANSEL = 0x0B; // Set analog inputs: AN0,1,3 ANSELH = 0x02; // Set analog inputs: AN9 PWM1_Init(20000); // Initialize PWM1 module at 20KHz current_duty = 64; // Initial value for current_duty; 25% PORTC = 0x00; // Turn OFF PORTC TMR0 = 100; // TMR0 Initial value PORTA.F2 = 0; // Turn OFF anod digit1 PORTC.F4 = 0; // Turn OFF anod digit2 PORTA.F5 = 0; // Turn OFF anod digit3 intro(); // Initial segments check PORTA.F2 = 0; // Turn OFF anod digit1 PORTC.F4 = 0; // Turn OFF anod digit2 PORTA.F5 = 0; // Turn OFF anod digit3 number = 0; // Initial value; digit = 0; // Initial value; anods_index = 0; // Initial value; PORTC.F6 = 1; // Turn ON Relay for power supply while(1) //Endless loop; { volt_ADC(); // Read voltage crt_ADC(); // Read current temp_ADC(); // Read temperature if((V<250) && (A>0)) PORTC.F6=0; // Check for output short circuit if((V>0) && (A>2000)) PORTC.F6=0; // Check for output overcurrent if (number >800) PORTC.F6=0; // Check for overtemperature if (x>300) // If x=300, 300*5ms=1,5s refresh display // with new values { x=0; // Reset counter x display(); } fan(); }} Termometru sursa LM35-2015.rar Editat Iulie 8, 2015 de danpin Link spre comentariu
mars01 Postat Iulie 8, 2015 Partajează Postat Iulie 8, 2015 (editat) Salut, Sunt mai multe lucruri care eu le-as face putin diferit dar am sa ma concentrez pe problema enuntata de tine. Pentru a elimina asa zisele schimbari frecvente ale digit-ului cel mai putin semnificativ poti face asa: - este recomandat ca prima masuratoare a ADC-ului sa nu o folosesti. Numai stiu in ce datasheet sau erata am citit acest lucru dar cred ca nu ai prea mult de pierdut prin aceasta. Faci acest lucru prin efectuarea unei ADC_read inainte de bucla ta in care faci media, si nu folosesti aceasta prima masuratoare. - daca banuiesti ca zgomotul este cauza problemei atunci poti sa faci in loc de medie un oversampling sa zicem pentru inca 2 biti si apoi shiftezi la dreapta de 2 ori rezultatul si renunti la acei biti (daca nu te intereseaza o rezolutie crescuta) - o alta modalitate este sa folosesti asa numitul running-average (se face medierea la fiecare noua masuratoare, ponderea erorii se reduce) - sau pur si simplu, efectueaza masuratoarea temperaturii la un interval de timp mai mare, sa zicem odata la 0.5 secunde. Asa cum este acum, efectuezi masuratoarea la fiecare trecere prin bucla principala si este normal ca ultimul digit sa faca pe nebunul (la fiecare adiere pe care nici nu o simti). Editat Iulie 8, 2015 de mars01 Link spre comentariu
danpin Postat Iulie 8, 2015 Autor Partajează Postat Iulie 8, 2015 @mars01Multumesc pt. sfaturi! O sa ma documentez cu privire la oversampling si am sa incerc. Am sa probez si celelalte optiuni, renuntarea la prima masuratoare sau efectuare de masuratori la intervale mai mari. Oricum am incercat sa implementez ceva de genul, nu o masurare la un interval mai mare ci doar afisarea la un interval mai mare, 1,5s. Ce ma intriga este faptul ca digitul zecilor sta fix, ok, cel al unitatilor sta si el fix daca valoarea nu e in apropierea lui 1 sau 9, in schimb digitul de dupa virgula ia parca valori aleatorii. Valoarea tensiunii pe pinul AI este constanta, masurata cu multimetrul cat si cu osciloscopul, nu sunt salturi, nu vad oscilatii, zgomot.... Link spre comentariu
mars01 Postat Iulie 8, 2015 Partajează Postat Iulie 8, 2015 Sa inteleg ca nu este vorba doar de masuratoarea temperaturii. Atunci vorbim de zgomot electric. Daca vine de la ADC atunci se poate minimiza software cum am scris mai sus. Hardware se poate minimiza zgomotul folosind referinta externa separata (eventual un regulator separat de tensiune iar pe GND-ul sau intercalata o inductanta mica). Sau daca vrei, conform la datasheet, poti face si asa: 9.3 A/D Operation During Sleep The A/D converter module can operate during Sleep. This requires the A/D clock source to be set to the FRC option. When the RC clock source is selected, the A/D waits one instruction before starting the conversion. This allows the SLEEP instruction to be executed, thus eliminating much of the switching noise from the conversion. When the conversion is complete, the GO/DONE bit is cleared and the result is loaded into the ADRESH:ADRESL registers. If the A/D interrupt is enabled, the device awakens from Sleep. If the GIE bit (INTCON<7>) is set, the program counter is set to the interrupt vector (0004h). If GIE is clear, the next instruction is executed. If the A/D interrupt is not enabled (ADIE and PEIE bits set), the A/D module is turned off, although the ADON bit remains set. Link spre comentariu
danpin Postat Iulie 8, 2015 Autor Partajează Postat Iulie 8, 2015 Pt. +Vref in montaj am folosit MCP1525, regulator de 2,5V. Cum am mai spus, nu ma pricep prea mult la electronica/microcontrolere, ma gandeam ca poate nu am declarat bine variabilele, acei long la inceputul programului si apoi in calculul temperaturii nu stiu daca le-am folosit bine in sensul ca pierd din calcule prea multe zecimale, adica acele trunchieri ce sunt facute cand se lucreaza cu int. Link spre comentariu
mars01 Postat Iulie 8, 2015 Partajează Postat Iulie 8, 2015 (editat) Pai problema pe care ai enuntat-o nu face referire la cat de precisa este masuratoarea ci la faptul ca zecimala este fluctuanta iar acest lucru apare din cauza zgomotului electric. In simulare zgomotul electric nu este luat in considerare asa ca valoarea afisata este stabila. Cum spuneam pentru ca valoarea zecimalei sa fie mai stabila ori se fac masuratorile la un interval de timp mai mare ori se minimizeaza zgomotul electric. Acuratetea masuratorii este limitata de ADC-ul PIC-ului (rezolutia sa) iar repetabilitatea (precizia) este limitata de zgomotul din circuit. Pentru marirea rezolutiei solutia este oversampling-ul (vor fi mai multe zecimale) influentata fiind si de marimea tensiunii de referinta. O tensiune de referitna de 2.5V si un ADC pe 10 biti inseamna ca rezolutia ADC-ului este de ~24mV, tensiune care poate fi foarte usor saturata de zgomot-ul circuitului. Editat Iulie 8, 2015 de mars01 Link spre comentariu
danpin Postat Iulie 8, 2015 Autor Partajează Postat Iulie 8, 2015 (editat) Rezolutia este ~24mV sau 2,4mv? Zgomot, lipsa preciziei, una din ele sau poate amandoua, ideea este ca ultima cifra nu este stabila. Cand valoarea este apropiata de 1 sau 9 incepe sa joace si cifra unitatilor. Editat Iulie 8, 2015 de danpin Link spre comentariu
Elison Postat Iulie 8, 2015 Partajează Postat Iulie 8, 2015 Rezolutia este 2,4mV. Link spre comentariu
mars01 Postat Iulie 8, 2015 Partajează Postat Iulie 8, 2015 (editat) tot editez eu posturile ca sa nu apara greseli (litere lipsa sau mai des inversate) dar uite ca lipsa la punctul ala n-am bagat-o de seama. Raspuns: 2.5V / 1024 = 0.002441V = 2.441mV Editat Iulie 8, 2015 de mars01 Link spre comentariu
danpin Postat Iulie 8, 2015 Autor Partajează Postat Iulie 8, 2015 @mars01Ok, nici o problema, mi-am dat seama ca e o eroare de tipar, intrebarea mea a fost retorica, stiu si am inteles foarte bine cum si care-i treaba cu rezolutia, ce reprezinta si cum se calculeaza. Am citit pe internet si diverse opinii despre impartirea la 1024 sau la 1023. Cum nu sunt specialist si nici nu mi-am facut un scop in viata de a fi un perfectionist in programarea microcontrolelelor le dau dreptate tuturor, eu impart la 1023 ca cica ar fi 1023 intervale de esantionare. Oricum nu nconteaza pt. aplicatia mea. In urma sugestiilor tale am studiat putin mai in profunzime chestia cu oversamplingul, mediere etc. (n-am terminat inca) si am facut urmatoarea chestie ce a dus la un rezultat bun, acum probez pe "viu" si sunt multumit. Am sa mai incerc si alte variante. void temp_ADC() // Reading the analog inputs (ADC){ Temperature = 0; for (ADCx=0; ADCx<16; ADCx++) { Temperature += ADC_Read(9); // Reading voltage values from channel 9 //Delay_us(50); // 16 times, add results } result=(Temperature>>4); // divide by 16 Temperature -= result; // temp.= temp-1/16 Temperature += ADC_Read(9); // temp.=(temp-1/16)+analog read Temperature =(Temperature>>4); // divide by 16 tlong = (long)Temperature*2500; // Millivolts conversion number = (long)tlong>>11; // 0...2046 => 0...2500mV (am o amplificare cu // 2x in motaj) } Ceva de genul: masor de 16 ori si adun rezultatele, apoi imart la 16 si extrag o saisprezecime din adunare, apoi adun o alta citire din ADC. Vad ca acum, cu ventilatorul care sufla pe radiatorul unde este montat senzorul LM35, indicatie este destul de stabila, am variatii lente, adica cam 2-3s, uneori mai mult, de genul:36.2 , 36.1 , 36.4 , 36.0 , 35.8 dar pare bine. Am sa mai incerc si alte variante, dati-va cu parerea, intrarea este libera. Link spre comentariu
mars01 Postat Iulie 8, 2015 Partajează Postat Iulie 8, 2015 (editat) @mars01 Ok, nici o problema, mi-am dat seama ca e o eroare de tipar, intrebarea mea a fost retorica, stiu si am inteles foarte bine cum si care-i treaba cu rezolutia, ce reprezinta si cum se calculeaza. Am citit pe internet si diverse opinii despre impartirea la 1024 sau la 1023. Cum nu sunt specialist si nici nu mi-am facut un scop in viata de a fi un perfectionist in programarea microcontrolelelor le dau dreptate tuturor, eu impart la 1023 ca cica ar fi 1023 intervale de esantionare. Oricum nu nconteaza pt. aplicatia mea. Se imparte la 2^N in cazul tau la 2^10 = 1024. Pentru clarificare recomand lectura aici: https://en.wikipedia.org/wiki/Analog-to-digital_converter#Resolution Mai sutn chestii gresite pe Wikipedia dar nu aici. LE: Sau aici: http://www.ni.com/white-paper/4806/en/ Programarea se sprijina pe matematica iar matematica este o stiinta exacta. Sigur ca mai si gresim dar nu tebuie sa ne-o facem cu mana noastra. LLE: Am mai citit putin postul si am inteles de unde vine confuzia. ADC-ul pe 10biti numara de la 0 la (2^10)-1 = 1023. Dar sunt 1024 valori (1023 valori + 0). Din aceasta cauza, rezolutia in mV este de Vref/1024, in cazul tau 2.5V/1024 = 2.441mV. Daca dorim sa aflam ce tensiune corespunde la o citire binara atunci se foloseste formula: Vin = Vref * (ADC_read/((2^N)-1). In cazul tau, Vin = 2.5V * (ADC_read/1023) Prima data aflam rezolutia in tensiune. A doua oara interpretam o citire binara a ADC-ului pentru a afla tensiunea la intrarea ADC-ului. LLLE: Sper ca senzorul nu masoara temperatura la tine in casa, la ora aceasta. Editat Iulie 8, 2015 de mars01 Link spre comentariu
danpin Postat Iulie 9, 2015 Autor Partajează Postat Iulie 9, 2015 Ok, multumesc pt. implicare. "LLLE: Sper ca senzorul nu masoara temperatura la tine in casa, la ora aceasta. :)" Nu, senzorul masura ce am spus in postarea de mai sus: "Vad ca acum, cu ventilatorul care sufla pe radiatorul unde este montat senzorul LM35, indicatie este destul de stabila, am variatii lente, adica cam 2-3s, uneori mai mult, de genul:36.2 , 36.1 , 36.4 , 36.0 , 35.8 dar pare bine." M-ar interesa ca cineva cu experienta sa controleze daca am stabilit bine, sau ce se poate optimiza, in alocarea tipurilor pt. diversele variabile din program, adica daca e bine cum am pus unsigned char, unsigned long, unsigned short etc. iar pt. long daca e buna sintaxa dpdv al mikroC folosita in program. Link spre comentariu
Bandi Szasz Postat Iulie 9, 2015 Partajează Postat Iulie 9, 2015 Sintaxa long pare in regula. Treaba cu "signed" si "unsigned" este in felul urmator "long" scris simplu este luat ca "signed" inseamna ca un bit este utilizat ca semn ( numar pozitiv sau numar negativ) , daca se pune "unsigned long" inseamna ca acel bit o sa faca parte din valoarea retinuta in long si nu mai este folosit ca semn in concluzie orice declarat cu "unsigned" nu poate contine valori negative ( de ex degeaba scrii -2 ca o sa fie transformat in 2). Deci daca nu ai nevoie de valori negative poti folosii "unsigned" care este mai rapid la operatii aritmetice dacat "signed". Link spre comentariu
mars01 Postat Iulie 9, 2015 Partajează Postat Iulie 9, 2015 (editat) O explicatie (a tipurilor de variabile) a dat-o @bandi12. Dar eu as renunta la functia anods() si de vreme ce este chemata doar in functia de intrerupere, as include corpul ei direct in functia interrupt(). De ce? Pentru ca functia anods() nu intoarce nimic si nu are parametri (desi daca avea parametri se rezolva inlocuind cu pointeri). Cu exceptia situatiei cand aceasta functie ar fi inlined de catre compilator, faptul ca anods() este o functie de sine statatoare nu face decat sa ocupe niste loc in stiva aiurea. Plus ca se utilizeaza ceva cicli ceas doar ca sa faca operatiile cu stiva. Inutil, parerea mea, mai ales ca functiile care deservesc intreruperi trebuie optimizate cat se poate. Editat Iulie 9, 2015 de mars01 Link spre comentariu
danpin Postat Iulie 10, 2015 Autor Partajează Postat Iulie 10, 2015 (editat) La declararea variabilelor am pus: unsigned long Temperature, number, tlong;. In program este corect asa? tlong = (unsigned long)tmp*2500; number = (unsigned long)tlong>>11; sau este suficient doar long? Am introdus in intrerupere tot ce tinea de functia anods(). Daca am inteles bine, cu anods() separat in afara buclei de intrerupere ar fi functionat asa: se executa instructiunile din intrerupere pas cu pas cand se ajunge la functia anods() se pune in stiva adresa instructiunii la care s-a ajuns se executa anods() apoi programul se intoarce sa execute ce a mai ramas in intr. dupa anods(). Corect, asa intr-o explicare simpla? Am mai facut o varianta pt. achizitia si calcularea temp.: void temp_ADC() // Reading the analog inputs (ADC){ Temperature = 0; for (ADCx=0; ADCx<16; ADCx++) { Temperature += ADC_Read(9); // Reading voltage values from channel 9 } } void temp_calculation(){ result=(Temperature>>4); // divide by 16 Temperature -= result; // temp.= temp.-1/16 Temperature += ADC_Read(9); // temp.=(temp.-1/16)+analog read tmp = Temperature; tmp =(tmp>>4); // divide by 16 tlong = (unsigned long)tmp*2500; // Millivolts conversion number = (unsigned long)tlong>>11; // 0...2046 => 0...2500mV (am o amplificare // cu 2x in motaj)} Ideea ar fi sa fac 16 achizitii, apoi sa mentin rezultatul precedent, sa scad 1/16 din el apoi sa adaug mereu o achizitie noua. Am incercat pe viu si pare ca functioneaza bine. Cele 16 achizitii le fac la inceput, in main inainte de bucla while(1). Dintre varianta asta si cea de mai sus, dpdv teoretic care ar fi mai buna? Editat Iulie 10, 2015 de danpin Link spre comentariu
Postări Recomandate
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 contAutentificare
Ai deja un cont? Autentifică-te aici.
Autentifică-te acum