PIC, Analoog naar Frequentie converter (0...10V -> 0...4000Hz)

Ik ben benieuwd of dat met een AVR lukt.

Ik ben nog niet zolang met PIC's bezig, en kies nu niet het meest makkelijk project geloof ik...

Op 4 december 2005 19:48:13 schreef MMSoft:
Ik ben benieuwd of dat met een AVR lukt.

Ik ben nog niet zolang met PIC's bezig, en kies nu niet het meest makkelijk project geloof ik...

:)
Ja klopt, meestal klinkt het simpel, en dan kom je erachter dat het toch pittig is om werkend te krijgen.

Op 4 december 2005 18:57:21 schreef MMSoft:
JoWi: Dus als ik het goed begrijp is dit niet mogelijk met alleen één PIC ?

Nee, dat heb ik niet gezegd. Ik zei:

Het magische woord is trouwens al genoemd door DC2PCC: Een DDS gebruiken. Dat kan met een extern chippie zoals hij zegt, maar voor lage frequenties kan je het ook met software doen.

Alleen haal je met die hardware chips 100 MegaHertz en hoger als klok en en mag je blij zijn als je met software boven de enkele honderden kiloHertz komt.

Een timerloop met variabele delay (al dan niet met hardware en software) is leuk voor een bepaalde frequentie, maar een ramp als je een mooi verloopt wilt. Maar daar komt RES nog wel achter als ie het probeert te maken op zijn AVR >:)

Je moet dus een DDS, dat is een Direct Digital Synthesizer, maken. Zo'n ding bestaat uit een klok, een opteller (voor de faze van je signaal, een sinus ROM (voor de sinus, maar kan ook een driehoek, of zaagtand of welke golfvorm zijn) en een D/A converter.

Aangezien je een blokgolf wilt kan je de sinus tabel eruit gooien. En omdat je gewoon een binair signaal wilt ook de DA converter. Blijft dus over een klok en een opteller.

Voor de klok gebruik je dus een timer, voor het optellen wat registers en de ADD instructie. Om de output hoog of laag te maken kijk je of de faze groter of kleiner dan 180 graden is. (Dus de helft hoog en de helft laag).

Dat is dus alles. Het onderstaande is een 16 bit software DDS die je kan gebruiken om een blokgolf van 0 tot een paar kiloHertz te maken. Theoretisch gaat deze tot 32 kHz maar dat moet je niet proberen ivm fazeruis en alias frequenties. Wat dat is moet je maar opzoeken op het net. Praktisch kan je dit ding gebruiken tot een paar kiloHertz.

Het programma is voor een 16F819 maar werkt op elke 16F pic met TMR2 aan boord (zoals de 16F628, 16F876 etc.) Je specificeert de frequentie in Hz en hij draait in de achtergrond (op de timer interrupt). Het kristal moet 10 MHz zijn, dan klopt de waarde in het getal ongeveer met de frequentie. (Eigenlijk is de frequentie 1.003867701 maal het getal, dus bij het getal 4000 doet ie 4015 Hz). Je kan dus ook de frequentie in stapjes van 1 Hz instellen.


;		SOFTDDS.ASM
;		Current Version:	0.00
;
;		Experiment with a PIC based software DDS generator
;		Stripped for square waves only !!!!!!!!!!!!!!!!!!!
;
        LIST    P=16F819
	ERRORLEVEL	-302
        include <p16f819.inc>

	__config  _CP_OFF & _WDT_OFF & _HS_OSC & _LVP_OFF

	CBLOCK 0x20
	    DDSFlags			; various flag bits,see defines below
	    PhaseRegLo,PhaseRegHi      	; phase reg lo & hi
	    PhaseIncLo,PhaseIncHi	; phase inc lo * hi
	    PhaseIncLoN,PhaseIncHiN	; New phase inc lo & hi
        ENDC

; Some allocations in "shared" memory
temp_S	equ	0x7F
temp_W 	equ	0x7E

#define DDS_NEWFREQ	DDSFlags,1	; Load a new FREQ
#define	DDSOUTPUT	PORTB,3		; Here we get the output signal

					; Each step is 1.0038677 Hz, round to 1 Hz
#define	FREQUENTIE	.1000		; Frequentie in Hz (1003.87 Hz)

		ORG	0
RESET		goto	Init		; Restart vector
		ORG	4
INTERRUPT	movwf   temp_W		; Interrupt vector
		swapf   STATUS,W	; Save W and STATUS
	 	movwf   temp_S
	  	bcf     STATUS,RP0	; ensure bank 0

	   	bcf     PIR1,TMR2IF	; Clr Timer2 interrupt

		btfss   DDS_NEWFREQ	; Should we load a new frequency ?
	 	 goto   inc_phase	; No, increment phase

		movfw	PhaseIncLoN	; Yes, Load the new frequency
		movwf	PhaseIncLo
		movfw	PhaseIncHiN
		movwf	PhaseIncHi
		bcf	DDS_NEWFREQ

inc_phase    	movfw   PhaseIncLo
	    	addwf   PhaseRegLo,F
	    	skpnc
	     	 incf   PhaseRegHi,F
		movfw	PhaseIncHi
	   	addwf   PhaseRegHi,F

	     	btfss	PhaseRegHi,7	; 50 % low, 50 % high
		 bcf	DDSOUTPUT
		btfsc	PhaseRegHi,7
		 bsf	DDSOUTPUT

IntDone		swapf   temp_S,W	; Restore registers
		movwf   STATUS
		swapf   temp_W,F
		swapf	temp_W,W
        	retfie

Init	      	clrf    PIR1            ; reset perif int
		clrf	T2CON		; Stop Timer2, Prescaler = 1:1, Postscaler = 1:1

	  	bsf     STATUS,RP0	; BANK1
        	movlw   b'11110000'     ; port B in/outputs
        	movwf   TRISB
		movlw	.37		; DDSclock (65.789 kHz @ 10 MHz)
		movwf	PR2		; Set period for DDS
		bsf	PIE1,TMR2IE	; Timer 2 interrupt enabled
	  	bcf     STATUS,RP0	; BANK0

		bsf	INTCON,PEIE	; Pheriperal interrupts enabled
		bsf	INTCON,GIE	; Global interrupts enabled
		bsf	T2CON, TMR2ON	; Timer2 starts to increment

		movlw	low FREQUENTIE	; Load frequency
		movwf	PhaseIncLoN
		movlw	high FREQUENTIE
		movwf	PhaseIncHiN
		bsf	DDS_NEWFREQ

		btfsc	DDS_NEWFREQ	; Wait until frequency is latched
		 goto	$-1		; otherwise you get funny glitches

MAIN		goto	$		; Main program, do nothing
        end

Ziet er veel belovend uit !
Morgen een 10 Mhz kristal halen...

Hopelijk stoort de AD Converter deze DDS niet.

Op 4 december 2005 22:17:20 schreef MMSoft:
Ziet er veel belovend uit !
Morgen een 10 Mhz kristal halen...

Uh ? op 20 Mhz komt er gewoon 2* de frequentie uit, op 4 MHz de frequentie gedeeld door 2.5.

Hopelijk stoort de AD Converter deze DDS niet.

Misschien moet je je kennis qua ISRs (Interrupt Service Routines) maar eens bijspijkeren.

De AVR kan op max. 1MHz snelheid samplen (8bit)
Elke ADC conversie duurt 13 ck, dus 1/1MHz*13 = 13usec.
Dus een periodetijd van max. 26usec, dus een output frekwentie van max. 38.46kHz. Maar dit zal nog wat lager liggen, want er moeten nog wat instructies worden uitgevoerd. (pin omschakelen)
Voor de PIC zal dit denk ik komen op zeg 35kHz/4=8500Hz, dus het lijkt toch te kunnen. Weet alleen niet of de PIC zo snel kan converteren?

[Bericht gewijzigd door RES op ]

SOFTDDS: 1004 Hz, dat zit er goed uit !

Als ik de ADC heb toegevoegd, waaruit ik de 2 lage bits laat vervallen geeft dit een waarde van 0....255

Nu moet deze waarde omgezet worden in een Freq. waarde.
Ik denk eraan om deze in een Tabel te zetten, zouden jullie dat ook zo doen ?

Vraagje: 'FREQUENTIE .1000'
Is een groter getal dan een 8 bits getal, kan dat zo makkelijk worden opgesplitst in een Hoog en een Laag deel ?

Yep met high en low krijg het hoge en lage byte van een getal. Dat puntje staat trouwens voor "decimaal", vind ik leesbaarder als die andere constructies (met een D en quotes geloof ik). De default staat bij mij altijd op hexadecimaal.
Heb je een 10 MHz kristal ? In dat geval kan je het getal uit de AD met 16 vermenigvuldigen. Kom je aardig in de buurt van de 4000. (x16 kan je met swap,and en or instructies doen)

Kristal is 10 Mhz.
Hoe vermenigvuldig ik precies met swap, and en or
en dan ook nog eens met grotere getalen ?

Op 5 december 2005 22:15:01 schreef MMSoft:
Hoe vermenigvuldig ik precies met swap, and en or
en dan ook nog eens met grotere getalen ?

Wil je leren programmeren of alleen maar leren knippen en plakken ?? |:(
Je hebt een 8 bit getal b.v. 0x12 en na afloop wil je 16 keer dat getal. Dat past niet in 8bit dus heb je een 16 bit getal nodig (2bytes) 0x120, dus 0x01 in het ene en 0x20 in het ander byte. Dat is 16 keer het oorspronkelijke getal. D'r zijn meerdere manieren om dat te doen maar het kan met o.a. met 1 swap, 2 ands en 3 moves. OR is niet eens nodig.

Ik ga hier vanavond mee aan de slag...
De Freq. mag alleen wijzigen als de uitgang laag is !
Ook mogen er geen spikes ontstaan !
Dus de freq. moet geleidelijk wijzigen en altijd in een nul doorgang.
Werkt deze SOFTDDS code zo ?

Edit:
De ingangs spanning regelt heel geleidelijk.

[Bericht gewijzigd door MMSoft op ]

Dit heb ik er van gemaakt:


   LIST P=16F876A          ;Processor type
   include <P16F876A.INC>  ;Register en poort definities 
   include "if_then.inc"   ;Vergelijk functie's 
   __CONFIG   B'11111101111010'  


DDSFlags        EQU     0x20             ;various flag bits,see defines below
PhaseRegLo      EQU     0x21             ;phase reg lo
PhaseRegHi      EQU     0x22             ;phase reg hi
PhaseIncLo      EQU     0x23             ;phase inc lo
PhaseIncHi      EQU     0x24             ;phase inc hi
PhaseIncLoN     EQU     0x25             ;New phase inc 'low'
PhaseIncHiN     EQU     0x26             ;New phase inc 'Hi'
Cntr1           EQU     0x30             ;
Cntr2           EQU     0x31             ;
Cntr3           EQU     0x32             ;
Cnt             EQU     0x33             ;
BACKUP_S        EQU     0x34             ;

AD_W            EQU     0x35             ;
D_Cntr1         EQU     0x36             ;
D_Cntr2         EQU     0x37             ;
D_Cntr3         EQU     0x38             ;

Bin             EQU     0x39             ;
Bcd001          EQU     0x3A             ;
Bcd010          EQU     0x3B             ;
Bcd100          EQU     0x3C             ;

AD_Waarde_N     EQU     0x3D             ;
Register_1      EQU     0x3E             ;
Register_2      EQU     0x3F             ;

BACKUP_W        EQU     0x40             ;
AD_Waarde_O     EQU     0x41             ;
Tyd             EQU     0x42             ;

; Some allocations in "shared" memory
temp_S	equ	0x7F
temp_W 	equ	0x7E


#Define      DDS_NEWFREQ  DDSFlags,1	;Load a new FREQ
#Define      DDSOUTPUT    PORTB,3		;Here we get the output signal
                				;Each step is 1.0038677 Hz, round to 1 Hz
#Define      FREQUENTIE	.1000		      ;Frequentie in Hz (1003.87 Hz)


;** Start adressen *************************************************************
                ORG        00h              ;Start adres
                GOTO       INIT             ;Reset: 'Start regel'  
                NOP                         ;
                NOP                         ;
                NOP                         ;
                GOTO       INTERR           ;Interrupt opgetreden -> Ga naar INTERR

;** Initalisatie ***************************************************************
INIT            BCF        STATUS,RP0       ;Select Bank 0
                MOVLW      B'00000000'      ;Reset
                MOVWF      PORTA            ;-PORTA
                MOVWF      PORTB            ;-PORTB
                MOVWF      PORTC            ;-PORTC
                MOVWF      STATUS           ;-STATUS

                BSF        STATUS,RP0       ;Select Bank 1
                MOVLW      B'11111111'      ;
                MOVWF      TRISA            ;A Ingangen
                MOVLW      B'00000000'      ;
                MOVWF      TRISB            ;B Uitgangen
                MOVLW      B'00000000'      ;
                MOVWF      TRISC            ;C Uitgangen

                ;Analoge comperators instellen
                MOVLW      B'00000111'      ;Bits naar tijdelijk register 
                MOVWF      CMCON            ;Comparators uitschakelen

                ;A/D Convertor instellen
                BCF        STATUS,RP0       ;Select Bank 0
                MOVLW      B'10000001'      ; 
                MOVWF      ADCON0           ;...
                BSF        STATUS,RP0       ;Select Bank 1
                MOVLW      B'00001110'      ;Gemiddelde snelheid 
                MOVWF      ADCON1           ;...
                BCF        STATUS,RP0       ;Select Bank 0
                ;Fosc/32
                ;AN0       = Analoog
                ;AN1...AN7 = Digitaal
                ;Hoge bits laten vervallen

                clrf       PIR1             ;reset perif int
                clrf       T2CON            ;Stop Timer2, Prescaler = 1:1, Postscaler = 1:1

                bsf        STATUS,RP0       ;BANK1
                movlw      b'11110000'      ;port B in/outputs
                movwf      TRISB            ;
                movlw      .37              ;DDSclock (65.789 kHz @ 10 MHz)
                movwf      PR2              ;Set period for DDS
                bsf        PIE1,TMR2IE      ;Timer 2 interrupt enabled
                bcf        STATUS,RP0       ;BANK0

                bsf        INTCON,PEIE      ;Pheriperal interrupts enabled
                bsf        INTCON,GIE       ;Global interrupts enabled
                bsf        T2CON, TMR2ON    ;Timer2 starts to increment

                movlw      low FREQUENTIE   ;Load frequency
                movwf      PhaseIncLoN      ;
                movlw      high FREQUENTIE  ;
                movwf      PhaseIncHiN      ; 
                bsf        DDS_NEWFREQ      ;

                btfsc      DDS_NEWFREQ      ;Wait until frequency is latched
                goto       $-1              ;otherwise you get funny glitches
                
                GOTO       MAIN             ;


;** Hoofd Programma ************************************************************

MAIN            CALL       AD_1             ;            
   
                ;AD_1 waarde gewijzigd ?    ;AD_1 waarde gewijzigd ?
                IREQRT     AD_Waarde_O,AD_Waarde_N ;Gewijzigd ?
                GOTO       MAIN             ;Nee -> Geen actie                
                NOP                         ;JA  -> Freq. wijzigen

                ;Bereken nieuwe Freq.
                ;AD Waarde * 16
                MOVLW      B'00001111'      ;
                ANDWF      AD_W,W           ;Alleen bits 0 t/m 3 van AD waarde
                MOVWF      Tyd              ;
                SWAPF      Tyd,W            ;Verwissel bits 0..3 met 4..7
                MOVWF      PhaseIncLoN      ;Naar Lo Byte  
               
                MOVLW      B'11110000'      ;
                ANDWF      AD_W,W           ;Alleen bits 4 t/m 7 van AD waarde

                MOVWF      PhaseIncHiN      ;Naar Hi Byte                
                SWAPF      PhaseIncHiN,W    ;Verwissel bits 0..3 met 4..7
                MOVWF      PhaseIncHiN
               
                BSF        DDS_NEWFREQ      ;Nieuwe Freq. aanwezig

                GOTO       MAIN 


;** AD Convertor ***************************************************************
AD_1            ;1e Potmeter stand inlezen
                BSF        ADCON0,2         ;A/D Converter starten

                ;Wacht totdat AD Convertor klaar is
M1              BTFSC      ADCON0,2         ;AD Convertor klaar ?
                GOTO       M1               ;Nee
                NOP                         ;Ja
                MOVFW      ADRESH           ;Waarde Uitlezen (in hoog adres)
                MOVWF      AD_W             ;Getal opslaan
 
                MOVFW      AD_Waarde_N      ;
                MOVWF      AD_Waarde_O      ;Oude waarde
                MOVFW      AD_W             ;
                MOVWF      AD_Waarde_N      ;Nieuwe waarde

                Return 


;** Interrupt Afhandeling ****************************************************
INTERR          movwf      temp_W           ;Interrupt vector
                swapf      STATUS,W         ;Save W and STATUS
                movwf      temp_S           ;
                bcf        STATUS,RP0       ;ensure bank 0
                bcf        PIR1,TMR2IF      ;Clr Timer2 interrupt

                ;Waarde <> 0 ?
                IRNEQZT    AD_Waarde_N      ;AD Waarde <> 0                
                GOTO       X1               ;<> 0
                bcf        DDSOUTPUT        ;= 0
                GOTO       IntDone    

X1              btfss      DDS_NEWFREQ      ;Should we load a new frequency ?
                goto       inc_phase        ;No, increment phase

                movfw      PhaseIncLoN      ;Yes, Load the new frequency
                movwf      PhaseIncLo       ;
                movfw      PhaseIncHiN      ;
                movwf      PhaseIncHi       ;
                bcf        DDS_NEWFREQ      ;

inc_phase       movfw      PhaseIncLo       ;
                addwf      PhaseRegLo,F     ;
                skpnc                       ;
                incf       PhaseRegHi,F     ;
                movfw      PhaseIncHi       ;
                addwf      PhaseRegHi,F     ;

                btfss      PhaseRegHi,7     ;50 % low, 50 % high
                bcf        DDSOUTPUT
                btfsc      PhaseRegHi,7     ;
                bsf        DDSOUTPUT        ;

IntDone         swapf      temp_S,W         ;Restore registers
                movwf      STATUS           ;
                swapf      temp_W,F         ;
                swapf	   temp_W,W         ;
                retfie                      ;

;** einde van het programma ****************************************************
                END

Ik ben benieuwd of het werkt, ga er zo de scoop bij pakken.

Edit:
Is en blijft 0 Hz. (continu laag signaal)

Ben al even naar de fout aan het zoeken geweest, maar heb deze nog niet gevonden.
Wie ziet de fout(en) ?

Edit:
Code is gewijzigd op 10-12-2005

Op 2 december 2005 00:42:01 schreef lucy:
is een ILC-8038 niet simpeler ?

dood en begraven.

Tja, je hebt helemaal gelijk !
Omdat ik pas met PICs begonnen ben, probeer ik nu alles met deze dingen op te lossen.

Ik heb het idee dat ik er nu ook bijna ben...

Als het met de PIC niet lukt, dan wordt het hoogst waarschijnlijk een ILC-8038 :)

Eindelijk tijd gehad om de fout te zoeken.

Het (1e) probleem zat in het configuratie word

Nadat deze gewijzigd had in:
__CONFIG B'11111101111010'

Werkt de code.

Helaas klopt de freq. nog niet:
0 Volt = 22 Hz
~ 2,5 v = 16666 Hz
5 v = 250 Hz

Wie ziet de fout ?

Op 9 december 2005 21:46:54 schreef MMSoft:
Wie ziet de fout ?

In ieder geval dit:


  SWAPF      W,W              ;Verwissel bits 0..3 met 4..7

Ga maar eens in de simulator kijken wat er gebeurd.

En denk je dat dit voor jan doedel in de oorspronkelijke code zit ?

                btfsc      DDS_NEWFREQ      ;Wait until frequency is latched
                goto       $-1              ;otherwise you get funny glitches

Code is nu gewijzigd !

Helaas werkt het nog niet goed...
Wie ziet er nog een fout ?

[Bericht gewijzigd door MMSoft op ]

Het werkt !
(Code is aangepast !)

De fout zat in het gedeelte: 'AD Waarde * 16'
Deze kan waarschijnlijk met minder instrukties !

Ook zit er nog een klein foutje in, als de ingangs spanning 0 volt wordt, dan moet de uitgang Laag zijn !
Deze blijft nu ook nog wel eens Hoog bij een ingangs waarde van 0 volt.

Edit:
Code gewijzigd, de uitgang is nu altijd Laag bij een ingangs spanning van 0 volt.

0v = 0 Hz (Laag)
>0v = 16,7 Hz
1,25v = 1020 Hz
2,5v = 2045 Hz
3,75v = 3065 Hz
5v = 4090 Hz

[Bericht gewijzigd door MMSoft op ]

Op 4 december 2005 19:23:23 schreef RES:
...zal eens kijken of het met een AVR wel lukt, dan kan ...

Lukt het met een AVR ?

Kan de code waar de AD waarde wordt vermenigvuldigd met 16 korter ?

Iedereen die meegedacht heeft Bedankt !

Op 12 december 2005 20:01:38 schreef MMSoft:
[...]Lukt het met een AVR ?

Kan de code waar de AD waarde wordt vermenigvuldigd met 16 korter ?

Iedereen die meegedacht heeft Bedankt !

Een jaartje later... :)

Weet iemand hoe ik die 0-10V om kan zetten naar 0-3.9V?
Ga hier weer even mee aan de slag.

Spanningsdeler is te simpel gedacht? ;)

Een jaartje later...

Weet iemand hoe ik die 0-10V om kan zetten naar 0-3.9V?
Ga hier weer even mee aan de slag.

Door de Vref ingangen te gebruiken.

Altans bij een PIC, of een AVR deze mogelijkheid ook heeft weet ik niet.

Edit:
De 0..10 volt wordt gedeeld, dit is nu niet nodig, het wordt dus 0...5 volt.
Als je nu bij 3.9 volt toch de maximale waarde wilt, dan verlaag je de boven grens (Vref+) van de DA converter.

[Bericht gewijzigd door MMSoft op ]

Op 18 januari 2007 19:06:24 schreef MMSoft:
[...]
Door de Vref ingangen te gebruiken.

Altans bij een PIC, of een AVR deze mogelijkheid ook heeft weet ik niet.

Ja zit erop een Vref.

Edit:
De 0..10 volt wordt gedeeld, dit is nu niet nodig, het wordt dus 0...5 volt.
Als je nu bij 3.9 volt toch de maximale waarde wilt, dan verlaag je de boven grens (Vref+) van de DA converter.

Dus ik kan een potmeter aan die Vref hangen? Want ik wil exact 1000 zien te halen bij 3.9V. (geen 1024)
Weet ook niet of die 0-10V zo exact is? Stel het is 0-9.9V, dan wordt het bereik van 4000 Hz niet gehaald.

Dus ik kan een potmeter aan die Vref hangen? Want ik wil exact 1000 zien te halen bij 3.9V

Ik bedoel eigenlijk een vaste spanning van 3.9 volt op de Vref+ ingang.

Als je exact 1000 wilt halen, dan kan je waarschijnlijk het beste de waarde uit de ADC begrenzen op een bepaalde maximale waarde.

0....1024, begrenzen op bijvoorbeeld 1022.
IF waarde > 1022 then waarde = 1022.

[Bericht gewijzigd door MMSoft op ]