PIC, simplistisk frekvensgenerator (Fixat)

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
Walle
Moderator
Inlägg: 7701
Blev medlem: 14 december 2004, 10:32:18
Ort: Stockholm

PIC, simplistisk frekvensgenerator (Fixat)

Inlägg av Walle »

Tänkte att jag skulle göra en liten simplistisk frekvensgenerator, väldigt avskalad. Helt enkelt vill jag med en PIC (12F675 i det här fallet, för att jag råkar ha några) läsa in ett analogt värde från en POT och på en pinne skicka ut en frekvens med (ca) 50% duty cycle. Jag vill hålla det extremt enkelt till att börja med, dels på grund av att jag är rostig på PIC och C, och dels på grund av att jag bara behöver en väldigt simpel frekvensgenerator för att kunna komma igång med ett annat intressantare projekt.

Det jag behöver en spark i rätt riktning med är hur jag på bästa sätt utnyttjar mina 10 bitar från ADCn för att generera en frekvens inom ett så stort omfång som är enkelt att åstadkomma.

Det första som slår mig är att helt enkelt köra Timer0 med prescaler/16, dumpa mina två lägsta bitar från ADCn, i princip ladda värdet till TMR0 (+2 för att kompensera för att TMR0 inte ändras på två klockcykler efter skrivning) och ändra pinstatus på interrupt. Kör jag PICen på INTOSC så borde jag då få ett spann från ca 244 Hz till 62.5 kHz, vilket skulle duga mer än väl för mig. Är det tvärkorkat att göra så? :) Finns det något sätt som är mycket bättre, och lika bra att köra på direkt?

Jag behöver inte veta vilken frekvens jag ställer in, det räcker med att mäta med oscilloskopet tills vidare. Senare vill jag utveckla frekvensgeneratorn lite, så den får flera områden, visning av inställd frekvens, o.s.v. Men det kommer inte ske innan nyår, och den enklare varianten vill jag ha innan slutet på veckan.

Givetvis blir det någon form av skyddselektronik på utgången, lite beroende på vad som finns hemma, och jag tänkte för övrigt även dra ut ett par fasta frekvenser också.

Edit: Det blir naturligtvis bara 8 bitars upplösning, det kanske är smartare att använda Timer1 och nyttja alla 10 bitar från ADC?
dangraf
Inlägg: 530
Blev medlem: 9 juni 2003, 15:30:56
Ort: göteborg

Re: Spark i rätt riktning med PIC, simplistisk frekvensgener

Inlägg av dangraf »

mitt tips är att köra på 8 bitar.. ADCn är lite brusig så om man ska använda fler bitar brukar iaf jag sampla flera gånger och få ut ett medelväde. Men om det ska vara så enkelt som möjlgt så börja med timer0 och 8 bitar.

Har du hittat en kompilator för din pic? Jag tror att den största utmaningen är att få till miljön och veta att koden man kompilerar faktiskt snurrar utan att t.ex wdg timern löser ut eller att den av andra skäl startar om hela tiden.
Användarvisningsbild
Walle
Moderator
Inlägg: 7701
Blev medlem: 14 december 2004, 10:32:18
Ort: Stockholm

Re: Spark i rätt riktning med PIC, simplistisk frekvensgener

Inlägg av Walle »

Ok, jo jag har märkt att de lägsta bitarna fladdrar lite mycket. Så jag kör nog med Timer0 tills vidare.

Japp, jag använder PICkit1 (ska köpa en PICkit 3 när jag får lön tänkte jag), och kör MPLAB med Hi-Tech-kompilatorn. Inga problem att få körbar kod :)
dangraf
Inlägg: 530
Blev medlem: 9 juni 2003, 15:30:56
Ort: göteborg

Re: Spark i rätt riktning med PIC, simplistisk frekvensgener

Inlägg av dangraf »

Ok, gött :-)

Tycker din idee om interrupt låter som ett bra val med tanke på att det inte verkar finnas någon perferienhet som hanterar detta.
Det är nog bara att tuta och köra ;)
Användarvisningsbild
Walle
Moderator
Inlägg: 7701
Blev medlem: 14 december 2004, 10:32:18
Ort: Stockholm

Re: Spark i rätt riktning med PIC, simplistisk frekvensgener

Inlägg av Walle »

Jag pillade lite med det här ikväll, och det blev ju helt ok! Och simplistiskt, fyra rader kod i Isr-rutinen är allt, i main ligger det bara en tom while-loop. Totalt är det 32 rader kod, inklusive includes, { och }. Jag vet dock inte riktigt hur jag räknade när jag fick maxfrekvensen 62500 Hz, jag får upp den i 36697 Hz, om jag kalibrerar oscillatorn bara 31250 Hz.

Edit: Nu är det sent och jag är trött, men jag hade ju uppenbarligen tänkt lite fel, jag räknade ut hur många halvperioder det blir, inte helperioder. Så det verifierar ju bara att kalibreringsvärdet är hyffsat korrekt, d.v.s. mina uppmätta 31250 Hz som blir exakt 62500 halvperioder. 62500*16*4=4.000.000

Men det duger gott för det jag ska ha det till! På skopet får jag ut stabil och fin fyrkantsvåg. Det var helt klart bra att skippa att utnyttja de två lägsta bitarna från ADCn.

Nu ska det bara fixas ett kort och en låda.

Edit: Lika bra att lägga ut koden.

Kod: Markera allt

#define _LEGACY_HEADERS
#include "Pulseout.h"

void main(void)
{
	Init();
	while(1)
	{
	}	
}

void Init(void)
{
	/*Comment out if using simulator, ICD2, or ICE2000*/
	#asm			
		call 0x3FF	    //Load Factory Calibration Value Into OSCCAL
				
		bsf _STATUS,5  //BANK1
		movwf _OSCCAL
	#endasm
	
	ANSEL = 0b00010001;	// Select AN0 
	ADCON0 = 0b00000001; //Configure A/D - Select AN0, Left justified & enables A/D
	TRISIO = PulseTris;	// GPIO 3 output, GPIO 0,1,2,4,5,6,7 inputs
	OPTION = TMRPRESCALE;	// Timer0 internal clock, Prescaler assigned to WDT
	GPIO = Off;
	CMCON = 0x07;	// //Turn Off Comparator Peripheral
	VRCON = CLEAR; 						
	TMR0 = CLEAR;                        //Clear Timer0
	IOCB3 = CLEAR;                         //GP3 Interrupt On Pin Changed Disabled
	T0IE = SET;                          //Timer0 Overflow Interrupt Enabled
	T0IF = CLEAR;                        //Clear Timer0 Overflow Interrupt Flag
	GIE = SET;                           //Enable All Interrupts

	return;
}
	
void interrupt Isr(void)
{
		PULSEOUT = PULSEOUT ^ 1;  // Toggle the output. 
		TMR0 = ADRESH;                  //Set TMR0 register to the value from the ADC.
		T0IF = CLEAR;                     //Clear Timer0 Interrupt Flag
		GODONE = SET;                   //When ADC > 252, one ADC conversion will be done every other interrupt. This does not matter.


	return;
}
Man kan tycka att det vore mer effektivt att läsa ur ADC'n bara ibland, men de extra instruktionerna gör att man förlorar lite tid, och alltså får en lägre högsta frekvens. Men det fungerar alldeles utmärkt som det är nu, frekvensen är tvärstabil helt utan synbara fluktuationer även när jag ställer skopet på väldigt kort tid per ruta.

Och include-filen:

Kod: Markera allt

#ifndef _Pulseout
#define _Pulseout

#include <htc.h>

__CONFIG(UNPROTECT & BORDIS & MCLRDIS & PWRTEN & WDTDIS & INTIO);   //No code protection, Don't use BOD, No MCLR-reset, or WDT, 
                                                                        //use PWR-on tmr, disable WDT, and internal oscillator with GP4
                                                                        // as I/O.
//Defines

#define      PULSEOUT		GPIO2
#define	TMRPRESCALE	0b10001000  		//TMR0 with /16 prescaler
#define	SET				1
#define	CLEAR			0

#define       PulseTris	        0b00111011

#define      Off		                0

//Global Variable Declarations
// No variables needed :)

//Function Prototypes

void Init(void);

#endif
Angående INTIO i konfigurationsbitarna så testade jag att dra ut klockfrekvensen på GP4 med INTCLK, men det överlagrar klockfrekvensen med ca 500 mV på utgångssignalen :/ Eftersom jag inte behöver en så hög frekvens ut som 1 MHz så kör jag med INTIO tills vidare.

Edit ytterligare igen:

Någon som har tips på en bra koppling för utgångsbuffer med kortslutningsskydd? Jag funderade på att göra en kombo, en utgång där signalen är +5 V mot COM, och en open collector. Antingen gör man utgångarna kortslutningsskyddade, eller så väljer man en väldigt billig utgångstrissa och gör den lätt bytbar :)
Skriv svar