srdjan Postat Noiembrie 18, 2011 Partajează Postat Noiembrie 18, 2011 Salut, am nevoie de ajutor pe partea de timing in generarea sinusului intre 10-100Hz. Vreau frecventa sa varieze liniar in functie de valoarea citita de la ADC0 (7, 8, 9 sau 10 biti); As vrea ca pasul la frecventa sa fie de 1Hz sau ceva apropiat. Am ales ca solutie o iesire pe 8bit (PORTD) si folosesc ca DAC o scara R/2R. In codul de mai jos nu am folosit inca valoarea citita de la ADC. Toate experimentele au fost esuate pana acum. De tinut cont ca atmega8 are 1kb sram si alearga cu un cristal de 16MHz. Codul pentru moment arata asa: /* * main.c * * Created on: Nov 14, 2011 * Author: srdjan */#define F_CPU 16000000UL#include <avr/io.h>#include <avr/interrupt.h>#include <util/delay.h>#include <stdlib.h>#define MIN 38;#define MAX 400;void ADC_init();void ADC_start();volatile uint16_t ADC_data=0;int main(){ DDRD=0xff; PORTD=0x80; char vect[256]={ 0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae, 0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8, 0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5, 0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7, 0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc, 0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3, 0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83, 0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51, 0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27, 0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a, 0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08, 0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23, 0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c, 0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c}; ADC_init(); sei(); ADC_start(); double delay=MIN; uint8_t skip=1; while(1) { for(int x=0;x<256;x+=skip) { PORTD=vect[x]; _delay_us(delay); } }}void ADC_init(){ ADCSRA|=(1<<ADEN)|(1<<ADFR)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); ADMUX|=(1<<REFS0);//|(1<<ADLAR);}void ADC_start(){ ADCSRA|=(1<<ADSC);}ISR(ADC_vect){ ADC_data=ADCW;} Orice ajutor este binevenit. Daca stie cineva o solutie open source de-a gata rog sa ma anunte. Eu nu am gasit ce cautam. Am studiat si problema sinusului folosind PWM dar am ajuns la concluzia ca nu poate varia pe plaja asa mare, desi poate ma insel. Link spre comentariu
godFather89 Postat Noiembrie 18, 2011 Partajează Postat Noiembrie 18, 2011 Poti da mai multe detalii cu privire la ce se intampla (sau nu se intampla) cu codul de mai sus? while(1) { for(int x=0;x<256;x+=skip) { PORTD=vect[x]; _delay_us(delay); } } ar fi putut fi optimizat in: uint8_t i;while (1){ i += skip; PORTD = vect[i]; _delay_us(delay);}Pentru ca nu ai decat 256 de valori in vector. LE: acum ca am citit mai bine, tu ai probleme cu timing-ul... Cel mai simplu ar fi sa faci cu un timer si o intrerupere. Astfel modifici perioada timer-ului incat sa fie declansata intreruperea de 256 * frecventa (10..100) ori pe secunda. In intrerupere incrementezi i -ul si setezi valoarea in PORTD. Ceva de genul: volatile uint8_t i = 0;ISR(TIMER_OVF_vect){ PORTD = vect[i++];}int main(void){ //init timer... while (1); return 0;} Link spre comentariu
srdjan Postat Noiembrie 18, 2011 Autor Partajează Postat Noiembrie 18, 2011 Merci de raspuns.eu am control asupra perioadei in functie de citirea ADC-ului (max 10 biti).frecventa o vreau liniara aproximativ in intervalul 10-100Hz cu pas aproximativ de 1Hz.deci eu practic trebuie sa controlez logaritmic perioada pentru a obtine o frecventa liniara dar nu stiu cum sa fac treaba asta cu resursele care le am.m-am gandit sa-mi fac un lookup table in care pentru 90 valori distincte de la ADC sa gasesc valoarea perioadei.din 1k sram deja pierd 256 bytes pentru lookup table pentru valorile sinusului.delay-ul cred ca imi trebuie double deci 4bytes/valoare * 90 = 360 bytes.pana acum s-au adunat 616 bytes numai pt look-up-uri. restul mi-ar ajunge pentru variabile.nu-mi dau seama totusi daca solutia mea e buna in sensul ca se poate aplica in situatia mea. oare nu exista o alta solutie?as putea sa tin look-up tables in eeprom, cel putin 1 din ele ca eeprom-ul are doar 512bytes. preferabil cel pentru corespondenta ADC->perioada deoarece se acceseaza de 256 de ori mai putin decat cel cu valorile sinusului, in felul asta timpii de acces m-ar incurca mai putin in calculul perioadei.Bat campii sau are sens ce spun? Link spre comentariu
godFather89 Postat Noiembrie 18, 2011 Partajează Postat Noiembrie 18, 2011 eu am control asupra perioadei in functie de citirea ADC-ului (max 10 biti). Despre ce perioada e vorba? deci eu practic trebuie sa controlez logaritmic perioada pentru a obtine o frecventa liniara dar nu stiu cum sa fac treaba asta cu resursele care le am. Perioada este invers proportionala cu frecventa (f=1/T). Nu inteleg de unde cuvantul "logaritmic"... Cu adc-ul citesti valoarea data de un potentiometru logaritmic? m-am gandit sa-mi fac un lookup table in care pentru 90 valori distincte de la ADC sa gasesc valoarea perioadei. din 1k sram deja pierd 256 bytes pentru lookup table pentru valorile sinusului. Nu are rost. Pentru ca perioada o sa fie direct proportionala cu valoarea citita de ADC. delay-ul cred ca imi trebuie double deci 4bytes/valoare * 90 = 360 bytes. Delay-ul (din avr\util) nu functioneaza cum trebuie decat cand e folosit cu o constanta (Citeste AVR LIB C) si chiar si asa nu este suficient de precis. De aceea iti sugeram sa folosesti un timer caruia sa ii modifici perioada in functie de valoarea citita de ADC. Aici e nevoie de ceva calcule sa vezi de ce frecventa are nevoie timer-ul pentru a se incadra in 10..100 Hz. Practic, ai nevoie de o intrerupere care sa se execute intre 2560 (pentru 10 Hz) si 25600 (pentru 100 Hz) de ori pe secunda. In intrerupere incrementezi pozitia (volatiel uint8_t i) si setezi iesirea portului D (PORTD = vect). Link spre comentariu
srdjan Postat Noiembrie 18, 2011 Autor Partajează Postat Noiembrie 18, 2011 Despre ce perioada e vorba? delay-ul meu e intre 2 citiri din look-up-ul tabelului de valori sinus. 256 * delay e periada sinusului. Perioada este invers proportionala cu frecventa (f=1/T). Nu inteleg de unde cuvantul "logaritmic"... Cu adc-ul citesti valoarea data de un potentiometru logaritmic? din neliniaritate P(erioada)/F(recventa): p=1 f=1 p=2 f=0.5 p=3 f=0.(3) p=4 f=0.25 p=5 f=0.2 potentiometrul e liniar. Nu are rost. Pentru ca perioada o sa fie direct proportionala cu valoarea citita de ADC. tocmai ca nu. frecventa trebuie sa fie direct proportionala cu valoarea citita de la ADC Delay-ul (din avr\util) nu functioneaza cum trebuie decat cand e folosit cu o constanta (Citeste AVR LIB C) si chiar si asa nu este suficient de precis. nu inteleg de ce? avr-gcc are un bug cand folosesti volatile reprezentate altfel deca pe 8 biti dar aici nu este cazul. dar _delay_us nu vad ce probleme ar avea daca folosesti constanta sau variabila pentru ca el oricum lucreaza cu valoarea ei cast-uita la double. functia _delay_ms face doar niste calcule pentru a obtine numarul de iteratii si parametrul functiei _delay_loop_1 care la randul ei primeste un parametru uint pe 8 biti care resprezinta numarul de ciclii de NOP. Singura problema ar fi de compensat intarzierile calculelor facute in _delay_us dar asta nu ma intereseaza prea mult deocamdata. De aceea iti sugeram sa folosesti un timer caruia sa ii modifici perioada in functie de valoarea citita de ADC. Aici e nevoie de ceva calcule sa vezi de ce frecventa are nevoie timer-ul pentru a se incadra in 10..100 Hz. Practic, ai nevoie de o intrerupere care sa se execute intre 2560 (pentru 10 Hz) si 25600 (pentru 100 Hz) de ori pe secunda. In intrerupere incrementezi pozitia (volatiel uint8_t i) si setezi iesirea portului D (PORTD = vect). m-am gandit deja la asta. nu am apucat sa incerc, poate in weekend o sa am destul timp sa incerc si asta dar din cate imi dau seama problema este aceasi ca si in cazul folosirii _delay_us. oricum counterul pentru ISR-ul de overflow trebuie sa varieze exact ca si argumentul functiei _delay_us. avantaje ar fi: nu mai trebuie sa compensez durata calculelor; pot folosi timer-ul de 16biti cu sursa de tact separata. as putea folosi surse de tact diferite (1,2,3) rutate printr-un MUX pentru anumite valori de la ADC. oricum solutia se complica... de aceea mai astept ajutor daca se poate sa rezolv totul din soft in configuratia actuala. apreciez foarte mult interesul tau. daca mi-ai oferit solutia si nu o inteleg eu m-as bucura daca ai reusi sa ma faci sa inteleg. Link spre comentariu
godFather89 Postat Noiembrie 18, 2011 Partajează Postat Noiembrie 18, 2011 Delay-ul (din avr\util) nu functioneaza cum trebuie decat cand e folosit cu o constanta (Citeste AVR LIB C) si chiar si asa nu este suficient de precis. nu inteleg de ce? avr-gcc are un bug cand folosesti volatile reprezentate altfel deca pe 8 biti dar aici nu este cazul. dar _delay_us nu vad ce probleme ar avea daca folosesti constanta sau variabila pentru ca el oricum lucreaza cu valoarea ei cast-uita la double. functia _delay_ms face doar niste calcule pentru a obtine numarul de iteratii si parametrul functiei _delay_loop_1 care la randul ei primeste un parametru uint pe 8 biti care resprezinta numarul de ciclii de NOP. Singura problema ar fi de compensat intarzierile calculelor facute in _delay_us dar asta nu ma intereseaza prea mult deocamdata. Nu functioneaza corect din cauza ca timpul necesar calcularii numarului de loopuri este foarte mare (lucrul cu virgula mobila pe AVR-uri este... lent). Dar daca este folosit corect (parametrul este o constanta cunoscuta in momentul compilarii si optimizarile sunt pornite), totul este calculat in momentul compilarii iar programul stie exact cate loop-uri/NOP-uri trebuie executate pentru a obtine delay-ul necesar. http://www.nongnu.org/avr-libc/user-manual/group__util__delay.html In order for these functions to work as intended, compiler optimizations must be enabled, and the delay time must be an expression that is a known constant at compile-time. If these requirements are not met, the resulting delay will be much longer (and basically unpredictable), and applications that otherwise do not use floating-point calculations will experience severe code bloat by the floating-point library routines linked into the application. Link spre comentariu
srdjan Postat Noiembrie 18, 2011 Autor Partajează Postat Noiembrie 18, 2011 dap... are sens perfect ce spui... eu credeam ca desi ii dau constanta ca argument la _delay_us, calculele din _delay_us se fac oricum. acum inteleg ca practic si rezultatul acelor calcule este inlocuit cu o constanta. merci mult ca mi-ai deschis ochii.mutam problema in alta parte pentru a obtine ce vreau :)o sa inlocuiesc _delay_us cu _delay_loop_2(uint16_t) si argumentele o sa le iau dintr-un tabel lookup sa vad daca ma apropii de rezultatele dorite.o sa revin cu constatari de indata ce am reusit sa testez. Link spre comentariu
srdjan Postat Noiembrie 18, 2011 Autor Partajează Postat Noiembrie 18, 2011 Am generat niste date si mi-am propus sa vi l-e prezint si voua inainte sa implementez in C povestea. Datele, ce reprezinta si formula de calcu (in tabelul PDF atasat): Coloana A=frecventa dorita Coloana B=perioada sinusului pentru a obtine frecventa dorita (1/Frecventa [Hz]) Coloana C=durata delay-ului pentru urmatoarea valoare din tabelul lookup sine (T/256 ) Coloana D=argumentul necesar functiei _delay_loop_2(uint16_t) pentru a obtine frecventa dorita (val_col_C/(F_CPU[Hz]/4)) - o iteratie a functiei _delay_loop_2 dureaza 4 ciclii procesor Coloana E=reprezentarea in baza 16 a celei mai apropiate valori intregi a valorii din coloana D In imagine se afla graficul coloanei A cu albastru si a coloanei D cu rosu: L.E. Incepe sa semene cu ce am vrut. Codul in stadiul actual. /* * main.c * * Created on: Nov 14, 2011 * Author: srdjan */#define F_CPU 16000000UL#include <avr/io.h>#include <avr/interrupt.h>#include <util/delay.h>#include <stdlib.h>void ADC_init();void ADC_start();volatile uint8_t ADC_value=0;char sine[256]={ 0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae, 0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8, 0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5, 0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7, 0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc, 0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3, 0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83, 0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51, 0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27, 0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a, 0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08, 0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23, 0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c, 0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c};uint16_t delay[91]={ 0x061B, 0x058C, 0x0516, 0x04B2, 0x045C, 0x0412, 0x03D1, 0x0397, 0x0364, 0x0336, 0x030D, 0x02E8, 0x02C6, 0x02A7, 0x028B, 0x0271, 0x0259, 0x0243, 0x022E, 0x021B, 0x0209, 0x01F8, 0x01E8, 0x01D9, 0x01CC, 0x01BE, 0x01B2, 0x01A6, 0x019B, 0x0191, 0x0187, 0x017D, 0x0174, 0x016B, 0x0163, 0x015B, 0x0154, 0x014C, 0x0146, 0x013F, 0x0139, 0x0132, 0x012C, 0x0127, 0x0121, 0x011C, 0x0117, 0x0112, 0x010D, 0x0109, 0x0104, 0x0100, 0x00FC, 0x00F8, 0x00F4, 0x00F0, 0x00ED, 0x00E9, 0x00E6, 0x00E2, 0x00DF, 0x00DC, 0x00D9, 0x00D6, 0x00D3, 0x00D0, 0x00CE, 0x00CB, 0x00C8, 0x00C6, 0x00C3, 0x00C1, 0x00BF, 0x00BC, 0x00BA, 0x00B8, 0x00B6, 0x00B4, 0x00B2, 0x00B0, 0x00AE, 0x00AC, 0x00AA, 0x00A8, 0x00A6, 0x00A4, 0x00A3, 0x00A1, 0x009F, 0x009E, 0x009C};int main(){ DDRD=0xff; PORTD=0x80; ADC_init(); sei(); ADC_start(); PORTD=0xF0; uint16_t p; while(1) { p=delay[ADC_value]; for(int i=0; i<256; i++) { PORTD=sine[i]; _delay_loop_2(p); } }}void ADC_init(){ ADCSRA|=(1<<ADEN)|(1<<ADFR)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); ADMUX|=(1<<REFS0)|(1<<ADLAR);}void ADC_start(){ ADCSRA|=(1<<ADSC);}ISR(ADC_vect){ ADC_value=ADCH/2; if(ADC_value>90) ADC_value=90;} Mai am de umblat putin in ISR sau sa pun o rezistenta astfel incat pe aproape toata cursa potentiometrului sa-mi iasa in ISR valori de la 0 la 90. Deocamdata m-am uitat la semnal doar cu VisualAnalyzer si placa de sunet... nu arata rau dar trebuie sa-l pun pe un osciloscop decent sa-l studiez mai bine. Atasez si schema in caz ca e cineva interesat. In simulator lucrurile stau ceva mai prost decat in realitate si nu inteleg de ce... tbl_delay.pdf Link spre comentariu
cirip Postat Noiembrie 19, 2011 Partajează Postat Noiembrie 19, 2011 Salut,Nu stiu daca ai rezolvat pana la urma, dar iti raspund ptr ca mi-ai trimis PM.In primul rand trebuie sa spun ca nu sunt foarte familiar cu Atmega; nu-mi permite religia.Pe de alta parte, modalitatea ta de abordare mi se pare cam neslefuita. Cand scoti ceva audio, orice decalaj fata de momentul ideal in care ar trebui sa dai afara esantionul urmator se traduce in distorsiuni. Puristii se chinuie sa reduca jitterul DACurilor la valori de ordinul ps. In conditiile astea, modul in care e executata bucla for(), va determina aparitia unei perturbatii in timing la fiecare reincarcare a variabilei contor. Cred ca au remarcat si altii chestia asta.Am tras un ochi la Atmega8 si am constatat ca are cel putin un taimer de 16 biti. Asa ca iti supun atentiei urmatoarea abordare:1)Intai putina aritmeticaVrei sa generezi un sinus intre 10-100Hz cu rezolutie de minim 1Hz. Ai la dispozitie un tabel cu 256 esantioane ale sinusului si un clock de 16MHz. Daca ar fi sa folosesti un divizor (timer programabil) si sa scoti cate un esantion la fiecare expirare, ratele de divizare ar fi:16M/(10*256)=6250 pentru 10Hz16M/(100*256)=625 pentru 100HzAmbele valori se pot inscrie lejer intr-un taimer de 16 biti. Pana aici suntem OK.2)Verificarea rezolutieiIa sa vedem cam ce pasi obtinem la capetele gamei daca marim/scadem rata de divizare cu o unitate.16M/(256*625)=10016M/(256*626)=99.84...16M/(256*6249)=10.001616M/(256*6250)=10Rezolutia cea mai proasta e la 100Hz si are valoarea de aprox 0.16Hz. Ne convine.3)Scalarea ADCuluiSa presupunem ca ADCul e de 10 biti, deci poate sa scoata pe goarna valori de la 0 la 1023.Pe de alta parte, avem nevoie de ratele de divizare ale timerului, care tre' sa fie intre 6250 si 625 ptr frecv de la 10 la 100Hz. Nimic mai simplu. Tre' sa ticluim o functie de gradul 1 de forma f(x)=a*x+b cu proprietatile:f(0)=6250, ca sa generam 10Hz sif(1023)=625 ca sa generam 100HzRezolvand sistemutul de ecuatii in a si b rezulta f(x)= -5.4985*x + 6250. Se poate implementa in virgula fixa. Nu e necesar sa folosesti float. E adevarat ca te costa o inmultire, dar fiind cu o valoare fixa, o poti implementa folosind numai shifturi si adunari.In consecinta, o astfel de functie scrisa in programul tau va primi ca argument valoarea citita de ADC si va scoate rata de divizare care trebuie incarcata in timer.4)Implementare-Pregatesti o variabila globala care va fi pointerul la tabelul cu valorile sinusului. Initializezi cu adresa de la inceputul tabelului. Tabelul il poti tine in flash, ca sa nu ocupi RAM. Sper ca poti face adresare indirecta la flash.-Setezi timerul ca sa dea intreruperi periodice cu ratele determinate ca mai sus. -Intri intr-o bucla while(1); si astepti intreruperea de la timer-La fiecare intrerupere extragi valoarea aratata de pointer si o pui la portul paralel unde ai R-2R.-Incrementezi pointerul modulo 256 ca sa o iei de la inceput cand ai ajuns la sfarsitul tabelului.-Din cand in cand, mai citesti ADCul, ca sa vezi daca s-a mai schimbat ceva pe acolo.In felul asta ai garantata aceeasi latenta de scriere si minimizezi zgomotul.=============== * ================Acuma, pe cinstitelea, si metoda asta e cam slabista. Motivul este ca frecventa de esantionare este variabila si din cauza asta filtrul de reconstructie (cel care ar trebui sa fie dupa reteaua R-2R) tre' sa cam sape din greu.DDS-ul adevarat merge pe cu totul alt principiu. Plecand de la ideea ca faza este o functie de gradul 1 de timp, ceea ce face DDS-ul este sa integreze timpul si sa-l puna intr-un acumulator de faza. Incrementul de faza (sau rata de crestere a fazei) da frecventa generata, pentru simplul motiv ca frecventa nu e altceva decat derivata intai a fazei in raport cu timpul. Continutul acumulatorului de faza este folosit la adresarea tabelului cu esantioanele sinusului. Si aici exista niste rafinamente, dar despre asta ... alta data. Un ochi pe DDS-urile adevarate de la Analog Devices, AD9850 de exemplu, ar fi util pentru intelegerea principiului.Spor!Cirip Link spre comentariu
Craciun Postat Noiembrie 19, 2011 Partajează Postat Noiembrie 19, 2011 Maestre cirip jos palaria pentru tutorial ! Link spre comentariu
srdjan Postat Noiembrie 19, 2011 Autor Partajează Postat Noiembrie 19, 2011 @cirip Multumesc mult pentru interventie. Ma bucur ca ai avut timp sa arunci o geana peste topic si mai mult, sa imi raspunzi.Dupa cum se vede eu am abordat metoda trial and error. Rezultatele obtinute de mine probabil sunt suficiente (ma voi convinge cand o sa il pun pe un osciloscop adevarat). Dar si eu sunt adeptul lucrurilor cat mai bine facute. O sa incerc sa abordez si metoda sugerata de tine si @godFather89.O sa revin cu rezultatele sau eventual nelamuriri daca nu ajung la rezultate Va multumesc inca o data tuturor si topicul ramane la dispozitia tuturor pentru eventuale sugestii sau inspiratii. Link spre comentariu
srdjan Postat Noiembrie 20, 2011 Autor Partajează Postat Noiembrie 20, 2011 Dupa cum am promis am revenit cu o versiune bazata pe temporizator/numarator pe 16 biti. In simularea din Proteus nu merge bine nu ajunge la 100Hz si nu inteleg de ce. Nu am incercat inca pe viu. Codul: /* * main.c * * Created on: Nov 14, 2011 * Author: srdjan */#define F_CPU 16000000UL#include <avr/io.h>#include <avr/interrupt.h>#include <util/delay.h>#include <stdlib.h>#define MIN 6250void ADC_init();void TMR1_init();void ADC_start();volatile uint16_t ADC_data=0;volatile uint8_t sine_idx=0;char sine[256]={ 0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae, 0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8, 0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5, 0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7, 0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc, 0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3, 0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83, 0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51, 0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27, 0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a, 0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08, 0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23, 0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c, 0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c};int main(){ DDRD=0xff; PORTD=0x80; ADC_init(); TMR1_init(); sei(); ADC_start(); while(1) { OCR1A=(uint16_t)(62500/(10+(ADC_data/1023.0)*90)); }}void ADC_init(){ ADCSRA|=(1<<ADEN)|(1<<ADFR)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); ADMUX|=(1<<REFS0);//|(1<<ADLAR);}void TMR1_init(){ OCR1A=MIN; TCCR1B|=(1<<WGM12)|(1<<CS10); TIMSK|=(1<<OCIE1A);}void ADC_start(){ ADCSRA|=(1<<ADSC);}ISR(ADC_vect){ ADC_data=ADCW;}ISR(TIMER1_COMPA_vect){ PORTD=sine[sine_idx]; sine_idx++;} Link spre comentariu
cirip Postat Noiembrie 20, 2011 Partajează Postat Noiembrie 20, 2011 versiune bazata pe temporizator/numarator pe 16 biti.In simularea din Proteus nu merge bine nu ajunge la 100Hz si nu inteleg de ce. while(1) { OCR1A=(uint16_t)(62500/(10+(ADC_data/1023.0)*90)); } Facand asa, re-incarci registrul ala de-l bagi in boale. Oare are timp sa expire din proprie initiativa? Eu as face cate o citire ADC la 0.1 secunde si apoi as reincarca timerul. Nu stiu exact functionarea timerului, dar nu imi suna bine sa il reincarc intr-o bucla while(1).De asemenea, esti absolt sigur ca rezultatul operatiei de scalare e cel la care te astepti? Adica generezi rata de divizare corecta? Si daca da, lasa-i un pic de timp timerului, sa apuce sa termine de numarat, nu-l lovi imediat cu o noua reincarcare. Experienta mea e ca daca nu merge in simulator, e garantat ca nu merge nici practic.Cirip Link spre comentariu
srdjan Postat Noiembrie 20, 2011 Autor Partajează Postat Noiembrie 20, 2011 si eu sunt de aceasi parere... si anume daca simularea nu merge bine, nici pe viu nu va merge. din pacate am avut prea putin timp "compact" ca sa studiez mai bine problema. am scris codul si am facut o simulare rapida. am vazut ca nu e tocmai bine si am lasat balta problema pentru moment din lipsa de timp. registrul care il "forjez" e double buffered si update-ul e instantaneu in modul in configuratia aleasa de mine conform datasheet-ului.o sa fac niste experimente cu diferite configuratii sa incerc sa izolez problema. o sa revin cu constatari. Link spre comentariu
srdjan Postat Noiembrie 20, 2011 Autor Partajează Postat Noiembrie 20, 2011 Imi cer scuze pt. dublu post. am ratat la mustata timpul de modificare l-am pus pe VisualAnalyser si acolo se pare ca ajunge la 99.82Hz cu cristal de 15.997824MHz. in capatul celalalt coboara putin sub 10Hz dar nu pot sa masor cu precizie din cauza instrumentului prea slab. are rezolutie mai buna in reglaj in felul acesta, este liniara frecventa si proportionala cu curorul potentiometrului pe toata lungimea cursei exact cum am dorit. se pare ca simulatorul totusi NU greseste. Aveam eu o rezistenta intre VCC si potentiometru uitata in schema de la un experiment provizoriu . aici este un print screen cu rezultatul. Multumesc tuturor pentru ajutorul acordat. 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