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

Ik kan weer verder....
Tips zijn natuurlijk van harte welkom :)

Bedankt !

Op 29 november 2005 13:55:07 schreef RES:
Is er geen PIC met een fast PWM aan boord?
Een AVR kan bijv. een PWM van 250kHz eruit gooien met de fast PWM functie, zou toch denken dat er ook een PIC is die dit aan boord heeft?

Het probleem is niet fast PWM, maar slow PWM. Je kan er geen 10Hz uitgooien , 250 kHz kan weer wel.

Misschien is dit te omzeilen door de xtal heel laag te kiezen, zeg 100kHz zodat de hele processor heel traag gaat werken.
Mits er een minimum aan clocksnelheid staat vermeld in de datasheet, dan houdt het op.

Ik kom nog 2 problemen tegen:

1: Ik heb een Inverse AD waarde nodig.
Nu loop de waarde op als de ingangs spanning stijgt.
0...5 volt = 0...255 moet worden 255...0
Ik kan dit natuurlijk berekenen 255 - X = Waarde.
Maar hoe kan ik een 8 bits getal makkelijk aftrekken ?

2: Hoe lang duurt een AD Conversie ?
Dit kan ik met MPLAB niet bepalen, omdat deze niet met
analoge waardes kan werken (is mij verteld).
Ik start de AD conversie, en wacht tot deze klaar is.
Deze tijd is belangrijk, omdat de uitgangs freq.
niet verstoord mag worden.

Wie kan hier iets over vertellen ?

Edit:
Gegevens:
Xtal: 4 Mhz
Fosc/32
PIC16F876A

Ben alweer een stukje verder, en zit met het volgende probleem.

Het programma ziet er nu zo uit:

-AD Converter starten, Spanning -> getal 0...255
-Wachten tot AD Converter klaar is.
-Bereken 255 - AD waarde = Nieuwe waarde
-Uitgang Setten
-Waarde afhankelijk een x aantal keer een wacht routine doorlopen
-Uitgang Resetten
-Waarde afhankelijk een x aantal keer een wacht routine doorlopen
-Terug naar 1e regel

Hiermee krijg ik een uitgangs freq. van 16...2174 Hz.
Dit werkt helaas heel erg logeritmische
0V = 16,1 Hz.
5V = 31 Hz.
10V = 2174 Hz.

Hoe kan ik dit beter liniaar krijgen ?

Omdat ik niet zo gelukkig was met de vorige oplossing,
ziet het programma er nu zo uit:

-Timer2 genereerd elke 125us een interrupt
-AD Converter starten, Spanning -> getal 0...255
-Wachten tot AD Converter klaar is.
-Bereken 255 - AD waarde = Nieuwe waarde

Interrupt afhandeling:
-Teller verhogen met 1
-Vergelijk: AD waarde <= Teller
-Teller is gelijk of hoger: Uitgang toestand wijzigen, en
teller op 0 zetten (einde Interrupt)
-Teller is lager: Einde Interrupt

Dit werkt prima, er is nu een uitgangs Freq. van 16...4000Hz


Helaas werkt deze opzet niet Liniaar !
AD Waarde    Freq.
    0        4000
    5         800
   10         400
   20         200
   64          63
  128          31
  255          16

Hoe kan ik dit (beter) Liniaar krijgen ?

4000 / 256 is denk ik de enige manier om het linear te krijgen, want elk stapje is dan exact 15 5/8 Hz.
Denk eerst de nieuwe frekwentie laten berekenen en dan pas uitsturen. (bij verderdraaien stopt de uitgang met uitsturen heel even.)

Hier wordt een fundamenteel foutje gemaakt: Linear in tijd is niet linear in frequentie P = 1 / F.
En de periode is wat je programmeert in een timer !!!

Hoe kan ik dit het beste oplossen ?

[Bericht gewijzigd door MMSoft op ]

Op 1 december 2005 16:44:59 schreef JoWi:
Hier wordt een fundamenteel foutje gemaakt: Linear in tijd is niet linear in frequentie P = 1 / F.
En de periode is wat je programmeert in een timer !!!

Dat klopt.

100Hz T=10msec
200Hz T=5msec
300Hz T=3.33msec

Anders een tabel maken met 256 T getallen en daar doorheen lopen, met dat getal telkens de timer presetten.

Ik voorzie 2 problemen:
Een tabel moet toch in de zelfde memory deel staan, als ik mij niet vergis, dan zijn dat telkens delen van 256 Bytes
Dus dan moet wel heel erg precies gebeuren.
Graag had ik hier iets meer info over.

Het uitgangs signaal mag zeer zeker niet haperen, dit moet te alle tijden zo vloeiend mogelijk blijven.
Door nu plotseling de timer te presetten (dat is toch opnieuw starten ?) kan dit signaal naar mijn mening even raar gaan doen...

Die presets zijn ten eerste halve tijden, dus T/2, want na T/2 moet je de pin omschakelen. Dan nog een keer T/2 laten wegticken. (interrupt op T/2 kan ook nog.)

0 = 0 (off) (0V) niks doen.

Als je begint bij 15Hz en telkens verdubbelt kom je uit op 3825 Hz. (15 Hz - 3825 Hz)

15Hz -> T/2 = 33.3 msec
30Hz -> T/2 = 16.6 msec
45Hz -> T/2 = 11.1 msec
60Hz -> T/2 = 8.3 msec
75Hz -> T/2 = 6.6 msec
etc

Totaal 255 getallen (zonder de 0)
Eigenlijk is de formule.

f = 1/T -> T = 1/f dus T/2 = (1/f)/2 = 1/2f.

Het is telkens 1 / 2x de frekwentie aan msec.
Zo te zien kan het dus ook zonder tabel en laten berekenen om linear te verkrijgen. Anders met de hand die getallen berekenen en de overeenkomstige getallen in 255 presets.
Weet alleen niet of de timer opzelfde instelling kan blijven met die zwaai van 15 - 3825 Hz.

is een ILC-8038 niet simpeler ?

Ik heb net even gezocht naar de ILC-8038 maar kan zo snel niets vinden, heb je daar meer gegevens over ?

RES: Ik ga morgen een poging doen.

Ik krijg het nog niet voor elkaar...

Dit is wat ik nu heb, deze Code werkt alleen niet linear.


   LIST P=16F876A          ;Processor type

   include <P16F876A.INC>  ;Register en poort definities 
   include "if_then.inc"   ;Vergelijk functie's 
   __CONFIG   B'11111101111001'  

Cntr1           EQU     0x20             ;
BACKUP_S        EQU     0x21             ;Backup van Status Register
AD_W            EQU     0x22             ;
Register_1      EQU     0x23             ;
Register_2      EQU     0x24             ;
BACKUP_W        EQU     0x25             ;Backup van W-Register


;** Start adressen *************************************************************
                ORG        00h              ;Start adres (Eerste instructie op deze locatie) 
                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       T2CON            ;Stop Timer2, Prescaler = 1:1, Postscaler = 1:1
		CLRF	   TMR2		    ;Clear Timer2 register
		CLRF	   INTCON	    ;Disable interrupts
		BSF	   STATUS,RP0	    ;Select BANK1
		CLRF	   PIE1		    ;Disable peripheral interrupts
		BCF	   STATUS,RP0	    ;Select BANK0
		CLRF       PIR1		    ;Clear peripheral interrupts Flags
					    ;Configure timer 2 to generate a 50 Hz
					    ;interrupt:
					    ;CLK/4 	 = 1.000.000 Hz
					    ;Prescaler 1:4  = 1.000.000 Hz
					    ;PR2 value 125  =     8.000 Hz
					    ;Postscaler 1:1 =     8.000 Hz
		movlw	b'00000000'	    ;Prescaler= 1:1, Postscaler 1:1

		movwf	T2CON		    ;Timer2 is off
		bsf	STATUS,RP0	    ;Select BANK1
		movlw	.124                ;Timer 2, Counts to 125 (0..124) (Lager getal is sneller)
		movwf	PR2                 ;
		bsf	PIE1,TMR2IE	    ;Timer 2, Interrupt on 
		bcf	STATUS,RP0	    ;Select BANK0
		bsf	INTCON,PEIE	    ;Pheriperal interrupts enabled
		bsf	INTCON,GIE	    ;Global interrupts enabled
		bsf	T2CON, TMR2ON	    ;Timer2 starts to increment

                BSF        INTCON,7         ;Interrupt toestaan
                GOTO       MAIN             ;

;** Hoofd Programma ************************************************************
MAIN            CALL       AD_1             ;            
                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

                ;255 - AD_Waarde = Waarde
                MOVLW      D'255'           ;
                MOVWF      Register_1       ;
                MOVFW      ADRESH           ;Waarde Uitlezen (in hoog adres)
                MOVWF      Register_2       ;
                movf       Register_2, w    ;
                subwf      Register_1, w    ;
                MOVWF      AD_W             ;Getal opslaan

                Return 

;** Interrupt Afhandeling ****************************************************

;Deze Routine wordt 8000x per Sec opgeroepen
INTERR          MOVWF      BACKUP_W         ;W-Reg bewaren
		movf	   STATUS,W         ;
		movwf	   BACKUP_S         ;
		bcf        STATUS, RP0	    ; Select bank 0
		bcf	   STATUS, RP1	
                BCF        INTCON,2         ;Disable Intterrupt TMR0 
		bcf        PIR1, TMR2IF	    ; Clr Timer2 interrupt
                ;------------------begin---------------------------
 
                INCF       Cntr1,F          ;Teller verhogen (+1)

                IRGEQRT    Cntr1,AD_W       ;<=
                Goto       Wissel
                Goto       IntEnd               


Wissel          ClrF       Cntr1            ;Counter = 0
                BTFSS      PORTC,2          ;Uitgang Laag ?
                GOTO       L_H              ;Ja

                BTFSC      PORTC,2          ;Uitgang Hoog ?
                GOTO       H_L              ;Ja
                                            ;
L_H             BSF        PORTC,2          ;Setten
                GOTO       IntEnd           ;Einde Interrupt Routine

H_L             BCF        PORTC,2          ;Resetten
                GOTO       IntEnd           ;Einde Interrupt Routine

                ;-------------------end----------------------------
IntEnd		movf	BACKUP_W,w	    ;Restore W and STATUS
		movwf	BACKUP_S
		swapf	BACKUP_W,F
		swapf	BACKUP_W,W
                RETFIE

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

Opmerking:
IRGEQRT is een Vergelijk Macro ( <= )
Zie http://home.versatel.nl/edithenwilliam/william.htm

Sorry dat ik wat laat ben met reply.
http://www.williamson-labs.com/images/v-f-v.gif
Is een soort functiegenerator maar erg gevoelig.
met een beetje spelen met een spanningsdeler aan de ingang
moet je er best het gewenste uitkrijgen. Als je in de AVR wil weten wat er gebeurd kun je analoog de spanning inlezen of via een interrupt en timer de blokgolf die eruit komt of beide als je de chip wil controleren LOL
Zo'n oude XR2206 kan mischien ook al helpen.

Lucy: Bedankt, deze houdt ik even in het achterhoofd.

Ik probeer eerst nog even om dit in een PIC voor elkaar te krijgen (hopelijk met een beetje hulp hier :) )

Het is voor mij ook een uitdaging omdat ik pas met PICs begonnen ben...

Op 26 november 2005 17:59:27 schreef JoWi:
[...]Jawel, dat is mogelijk.

Is het ook mogelijk om de verhouding linear te krijgen ?
Zoja: Hoe ?

Laatste poging...

Zou het mogelijk zijn om de verhouding linear te krijgen met een PIC ?
Zoja: Hoe ?

Als 15Hz de stapgrootte is betekent dit telkens 15Hz hoger in frekwentie, dan is het linear.

Je krijgt dus 10V / 255 stapjes, dus elke 39.2mV zal de frekwentie 15Hz hoger worden.

Alleen de input loopt van 0V t/m 5V ADC gaat van, dus 5V / 255 = 19.6 mV, dus elke 19.6mV zal de frekwentie met 15Hz opgehoogt moeten worden.

Bij 0V = 0 (pin laag houden)
Bij 19.6mV moet er 15Hz vanaf komen, dus T = 66.7 msec
Bij 39.2mV moet er 30Hz vanaf komen, dus T = 33.3 msec
Bij 58.8mV moet er 45Hz vanaf komen, dus T = 22.2 msec
Bij 78.4mV moet er 60Hz vanaf komen, dus T = 16.7 msec
etc.

Je ziet dat de periodetijd niet linear verloopt, je ziet wel dat de spanning telkens 19.6mV ophoogt.

Als het goed is staat er in de datasheet een formule bij de ADC om een overeenkomstig getal uit de aangeboden spanning te halen.

ADC = (Vin * 1024)/ Vref.

Ik weet niet wat jouw referentiespanning is, ik neem aan 5V. (of 2.56V?)

ADC (15Hz) = (19.6mV * 1024) / 5V = 4 = 0b00000100
ADC (30Hz) = (2 * 19.6mV * 1024) / 5V = 8 = 0b00001000
etc.

Dit zijn afgeronde getallen.
Dus telkens 4 erbij optellen. 255 * 4 = 1020, dus dat klopt, want 0 was niks, dus geen 256, maar 255 stapjes.

Dus telkens als de ADC 4 hoger of 4 lager wordt moet je de periodetijd opnieuw laten berekenen (aanpassen), of uit een tabel halen (nieuwe timer laden, of zelf stukje code schrijven ipv de timer gebruiken.) en dan delen door 2 en daarop de pin schakelen, dan genereert ie f.

Be continued...

>Ik weet niet wat jouw referentiespanning is, ik neem aan 5V. (of 2.56V?)
Deze is 5 Volt.

Ik ga vannavond een poging doe om de Code om te bouwen...

Probeer dat maar niet, want dat gaat niet werken.

Het probleem is dat die tijd dus zo alinear is als je maar kunt bedenken. Het ziet er leuk uit voor 15/30/45 en 60 Hz.
Maar voor 4000 Hz, en bijvoorbeeld bij 3850Hz ? Voor 3850 Hz is de tijd bijvoorbeeld 0.25974 milliseconden. En je kan nooit 15.6 milliseconden (is trouwens 15.66666) en bovengenoemd getal uit een timer krijgen.
Wat als je later 10 bit A/D wilt gebruiken ?

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.

Bij 3850Hz is de periodtijd 259.74 usec.
Ik weet niet hoe het zit bij de PIC, maar de AVR bijv. (ATtiny2313) kan de timer (op ck) op 50 nsec laten ticken, met een 20MHz xtal.
Ik weet niet wat een PIC doet op 20MHz? Was het niet 4x delen in verhouding tot een AVR? Dan nog altijd 200 nsec per tick.
259.74 usec / 200 nsec = 1298 ticks, dus je kan nog heel wat instructies uitvoeren.

PS. er blijft wel een probleem, dat is de snelheid van de ADC, die moet onder de 129usec liggen, om het zo snel om te zetten dat de frekwentie direct reageert op de spanningsverandering.

[Bericht gewijzigd door RES op ]

JoWi: Dus als ik het goed begrijp is dit niet mogelijk met alleen één PIC ?

RES: Ik heb geen idee hoeveel tijd de ADC nodig heeft.

Nu ik er nog eens over nadenk, moet direct invloedbaar volgens mij wel mogelijk zijn, maar dan alleen met de PWM/timer functie (alleen kon die bij de PIC niet zeg 10Hz aan, dus lage frekwenties. Bij de AVR weet ik dit nog niet.), aangezien die onder het samplen van de ADC blijft doordraaien.
Ik zal eens kijken of het met een AVR wel lukt, dan kan ik vertellen hoe ik het heb gedaan, zou voor een PIC dan ook moeten kunnen lijkt mij.