PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : PWM mit einstellbarer Frequenz (2313)



Rage_Empire
28.04.2005, 13:14
Hallo,

habe einen Code geschrieben, bei dem man die Frequenz und Duty mit hilfe von timer1 einstellen kann. Jedoch funktioniert es nicht richtig. Kann mir vieleicht jemand sagen, wo mein Fehler liegt?



$regfile = "2313def.dat"
$crystal = 10000000
$baud = 9600


Const True = 1
Const False = 0

Dim Hi As Single
Dim Lo As Single
Dim Freq As Word
Dim Phase As Bit
Dim Duty As Byte

Config Timer1 = Timer , Prescale = 1
On Timer1 Timer1_isr

Config Pinb.5 = Output

Enable Timer1
Enable Interrupts


Do
Input "Zeit [0-65535]" , Freq
Input "Duty [0-100%]" , Duty

Hi = Freq * 0.01
Hi = Duty * Hi
Hi = Int(hi)
Lo = Freq - Hi

Print "High: " ; Hi
Print "Low: " ; Lo
Loop

End

Timer1_isr:

If Phase = True Then
Timer1 = Lo
Phase = False
Else
Timer1 = Hi
Phase = True
End If

Toggle Portb.5

Return

teslanikola
28.04.2005, 15:37
So wird das auch nie gehen.
wann springt der µC in der Timerinterrupt ?
Wenn der Timer voll ist, nicht bei jedem Signaleingang !!!
Da heit dein µC springt alle 6,6*10^3 sek in den interrupt und toggelt dein ausgang, egal was du in deinem Mainprogramm alles machst !!!
Du hast an deinem ausgang eine konstankt Frequenz von 76,3 Hz.
Stimmts ?

28.04.2005, 15:44
soll ja auch, wenn der timer voll ist in den interrupt springen. Aber warum kann ich mit meinem Mainprogramm das nich richtig beeinflussen?

Rage_Empire
28.04.2005, 15:53
...war ich. Ich will mit dem Überlaufinterrupt die Frequenz steuern, die Ausgegeben werden soll. Die Cyclen Steuern, Hi und Low (für die PWM) werden aus der Eingabe des Verhältnis (in %) erechnet. Also, so habe ich es zumindest vor hinzubekommen.

teslanikola
28.04.2005, 16:02
Also ich würde es so machen:
Mit dem interrupt einen FESTE Zeit erstellen zum Beispiel alle 100µS ein interrupt. dann würde ich mit jedem interrupt eine Variable incrementieren, bis die einen bestimmten wert erreicht hat ( dieses kann man in Main festlegen ) und erst den den ausgang toggeln und dann alles auf null setzen ( timer und Variable )

RCO
28.04.2005, 16:08
wann springt der µC in der Timerinterrupt ?

[...] egal was du in deinem Mainprogramm alles machst !!!

Das versteh ich auch nicht ganz! Der Timer wird doch beim Sprung in die Routine wieder neu beladen.
Wie kommst du auf: 6,6*10^3 sek? Warum springt der uC nur jede Stunde und 50 Minuten in den Interrupt, warum kommt dann aber eine Frequenz von 76 Hz raus?

Was mir allerdings nicht gefällt ist das "toggle", schreibt lieber in den Code:

If Phase = True Then
...
Portb.5 = 0
Else

bzw. = 1

Kannst du etwas genauer sagen, was nicht richtig funktioniert? Was passiert am Ausgang?

Rage_Empire
28.04.2005, 16:10
Verstehe ich jetzt nicht ganz...? Ähnlich einer For-Netx-Schalufe? Und wie bekomme ich dann ein variables Puls-Pausenverhältnis hin?

Das mit dem Toggle hatte ich auch erst so wie beschrieben, brachte aber wenig.

Also, wenn ich das Proggy laufen habe, kann ich zwar Zeiten des Timers einstellen, aber irgendwie nicht plausibel. Die Ausgangsfrequenz ist nie und nimmer das, was man sich errechnet hat (mit dem Tool von hier)

Kann es sein das der Timer nur Variable im "Word"-Format frisst? Weil meine Variablen, die ich ihm gebe sind im "Single"-Format.

RCO
28.04.2005, 16:16
Also ich finde die Idee in Ordnung! Schln wäre natürlich, wenn man die Frequenz nicht in [0-65535] sondern in Hz angeben könnte, wobei man natürlich an eine Grenze nach oben und unten stößt, kannst mal ausprobieren, wie weit der uC mitkommt.

Belädt man ihn mit 0, dann entspreicht das: 10 Mio / 2^16 ~ 152 Hz
Belädt man ihn mit 2^16: 10 Mio / 1 = 10 Mhz
Wobei 10 Mhz illosorisch ist, da er die Routine nicht 2 mal in einem Zyklus durchlaufen kann. Wäre interessant rauszufinden, wie hoch er es schafft.
50 Khz?

teslanikola
28.04.2005, 16:17
Ihr das Code gerüst, ab jetzt darfst du mal ein bissele Wurschteln ( schwäb. )

$regfile = "2313def.dat"
$crystal = 10000000
Dim Zeit As Word , Varausmain As Word

Config Portb = Output
Config Timer1 = Timer , Prescale = 1
Enable Interrupts
On Timer1 Icnr
Enable Timer1
Timer1 = 64536 ' timervorgabe zum erzeugen von einer Pause mit 100µS

Do
' MAIN
Varausmain = 10
loop


Icnr:

Incr Zeit

If Zeit = Varausmain Then ' wenn die Pause von 100µS x mal abgelaufen ist dann

Toggle Portb.5 ' toggle portb.5
' setze auf ursprung zurück
Zeit = 0

End If
Timer1 = 64536

Return
mach mal ne Simulation ( aber vorsicht, die läuft extrem langsam, nicht das du denkst die Frequenz ist zu langsam ( hat bei meiner Var eine Frequenz von 500 Hz )

teslanikola
28.04.2005, 16:23
@ RCO
sorry meinte 6,6 mal 10 hoch minus 3.

wenn man bei einem Quarz mit 10Mhz die teilung auf 1 setzt, dann wird alle 65536 Clocks ( 6553.6 µS ) ein interrupt aufgerufen und der zustand des ausgangs wird verändert. D.h. eine periode dauert 2 X 6553.6 µS.
Das entspricht einer Frequenz von 1/13107.2µS = 76,3 Hz
So verstanden?

RCO
28.04.2005, 16:29
Ja klar!
Ein Problem bei deinem Code wird sein, dass man eigentlich ausrechnen müsste, wieviel Zeit (Takte) der uC braucht, um die Routine aufzurufen und zu durchlaufen, denn sie soll ja 10.000 mal in der Sekunde aufgerufen werden. Wenn das ganze so 100 Takte braucht, dann ist der Fehler schon recht hoch!
Ich finde diese Variante auch nicht schelcht, es hängt natürlich davon ab, wieviel Hz das ganze schaffen soll/muss.

teslanikola
28.04.2005, 16:33
@ RCO

Klar, das des so jetzt als 08/15 Code noch recht ungenau ist, wenn man aber noch die Clocks für die incr Zeit...If ...then...toggle...timer1 = 646536... Befehle von der Timervorgabe abzieht, dann wid das ganze extrem genau ( so genau wie das Quarz ).

Rage_Empire
28.04.2005, 20:06
@teslanikola
Danke, verstehe jetzt, wie du das gemeint hattest. aber ich fürchte, daß ich dann mit der Frequenz nicht mehr so hoch kommen werde, oder irre ich mich da? ich will bis mind. 25kHz bei 10Mhz Quarz gehen. 40Khz max. wäre perfekt!

teslanikola
28.04.2005, 21:01
@Rage_Empire
Die maximale Frequenz von meiner methode ist 500khz. Dazu musst du die Timervorgabe auf 65436 ändern ( 500khz bei Varausmain = 1, bei 65535 ist die Frequenz = 0,76 Hz )
Das erfüllt doch deine Ansprüche?????

Rage_Empire
28.04.2005, 21:25
@teslanikola
ich werde den Code von dir Testen, wie weit hoch er zuverlässig funktioniert. 500kHz wäre klasse, da das System zukünftig mit einem tiny2313 mit Quarz 20Mhz laufen soll und dann (theoretisch) 1Mhz max möglich wäre. Leider habe ich zu Hause kein Frequenzzähler (falls jemand eine günstige Bezugsquelle weiß, bitte melden ;-) ), so daß ich das erst bei der Arbeit testen kann.

teslanikola
29.04.2005, 10:11
Ich hab einen Frequenzzähler selbstgebaut ( mit 2313 ), der geht bis 2 Mhz ohne und 200 Mhz mit vorteilung. Kostenpunkt ca. 10 € und trotzdem seht präzise. Werde ihn demnächst aun meiner Homepage veröffentlichen.

RCO
29.04.2005, 14:34
Die maximale Frequenz von meiner methode ist 500khz.

Ja, wenn nur um die Frequenz geht ist dein Code sicher ausreichend, aber man kann bisher keinen Duty-cycle einstellen! Das ist ja im Grunde der Resourcenfressende Teil der ganzen Geschichte.


Ich hab einen Frequenzzähler selbstgebaut ( mit 2313 ), der geht bis 2 Mhz ohne und 200 Mhz mit vorteilung.

Würde mich interessieren, such nämlich einen Frequenzgenerator, der auch einige Mhz schafft.

teslanikola
29.04.2005, 17:56
AUCH VARIABLES DUNTY möglich, du musst halt nur 2 Variable aus deinm Main Festlegen ( eine für Hightime und eine für Lowtime ) und fertig.

Rage_Empire
30.04.2005, 11:29
@tesla: so habe ich das Ding auch umgeschrieben.

@RCO:also, für einen Frequenzgenerator, der nur Frequenzen ohne variable Duty bringen muß, sind doch eineige Mhz kein Problem!? Und wenn der Tiny2313 (den es glaub schon mit bis zu 24Mhz gibt) nicht genügend bringen sollte, mit nem DDS oder ner PLL biste sicher gut bedient (wohl bemerkt, ohne variable Duty!)

teslanikola
30.04.2005, 12:21
Du kanst theoretisch bis auf etwa 1/5 der Quartfrequenz, bei 24Mhz also fast 5 Mhz, aber da ist die Frequen zimmlich ungenau!!!!!!!!

RCO
01.05.2005, 15:59
sind doch eineige Mhz kein Problem!?


Du kanst theoretisch bis auf etwa 1/5 der Quartfrequenz, bei 24Mhz also fast 5 Mhz, aber da ist die Frequen zimmlich ungenau!!!!!!!!

Also einige Mhz ist gut, sagen wir mal, wir würden 5 schaffen. Das wäre vermutlich sowas wie Port.x = 1 Portd.x = 0.
Also ich bräuchte einen Generator, wo ich zwischen 1 und 10 Mhz auf ca. 1 khz genau einstellen kann. Ich denke nicht, dass man das mit einem AVR hinkriegt. Darum soll es ja aber auch garnicht gehen.

Ich wollte mit:

aber man kann bisher keinen Duty-cycle einstellen!

ja nur sagen, dass der Code keinen Wikrlich großen Vorteil gegenüber dem anderen hat (auf der anderen Seite auch keinen Nachteil). So oder so, je genauer der Cycle ist, desto niedriger wird die höchstmögliche Frequenz.

Rage_Empire
01.05.2005, 17:18
@RCO:
naja, bei 24Mhz hat der AVR 24MIPS, die würde ich nicht unterschätzen. Anderseits weiß ich nicht, was du mit dem Funktionsgenerator vor hast. Fertige Beispiele findet man für einfache Frequenzgeneratoren mit einem AVR im Netz genügend. Das mit der Genauigkeit der Duty und der damit verbundenen Frequenzhöhe stimmt sicherlich, jedoch sind die angegebenen 500kHz für mich nie in Frage gekommen. Es ist aber bestimmt immer gut, noch etwas Reserve in den Resourcen zu haben! Wie schon erwähnt, mit einem DDS oder ner PLL bekommst sicher einige zig MHz hin!

Rage_Empire
02.05.2005, 15:16
So, habe den Source mal frisiert und getsetet. Leider geht bei 10Mhz Quarz grade mal etwas mehr als 29Khz :-(

@Tesla: sind wohl doch weniger als 40kHz, sorry!

Bekomme ich das noch schneller, oder mache ich was falsch?



$regfile = "2313def.dat"
$crystal = 10000000
$baud = 9600

Dim Hi As Byte
Dim Lo As Byte
Dim Freq As Byte
Dim Zeit As Byte

Config Pind.2 = Output

Config Timer1 = Timer , Prescale = 1

Enable Interrupts
On Timer1 Icnr
Enable Timer1
Timer1 = 65526



Do
Input "High x mal 1us: " , Hi
Input "Low x mal 1us: " , Lo


Loop



Icnr:

Incr Zeit

If Zeit = Freq Then

Zeit = 0
Portd.2 = Not Pind.2

If Freq = Hi Then
Freq = Lo
Else
Freq = Hi
End If
End If

Timer1 = 65526

Return

teslanikola
02.05.2005, 16:18
1. Du hast vergessen die Zeit zu reseten
2. Nimm Toggle ( If schlaufe frisst zu viel zeit )
3. Deine Frequ = Hi, Lo then ... frisst auch zu viel zeit
4. ich hab dir den code mal verändert und für die If und incr usw mock ein paar clocks mehr gegeben.
Hast du ein Oszi? Zum kalibrieren?
ICQ?



$regfile = "2313def.dat"
$crystal = 10000000
$baud = 9600

Dim Hi As Byte
Dim Lo As Byte
Dim Freq As Byte
Dim Zeit As Byte

Config Pind.2 = Output

Config Timer1 = Timer , Prescale = 1

Enable Interrupts
On Timer1 Icnr
Enable Timer1
Timer1 = 65531



Do
Input "High x mal 1us: " , Hi
Input "Low x mal 1us: " , Lo
Loop



Icnr:

Incr Zeit

If Zeit = Hi And Portd.2 = 1 Then

Portb.2 = 0
Timer1 = 65531
Zeit = 0
End If

If Zeit = Lo And Portd.2 = 0 Then

Portb.2 = 0
Timer1 = 65531
Zeit = 0
End If

Return

Rage_Empire
02.05.2005, 18:02
Oszi? klar, nicht nur einen (....halt bei der Arbeit).Frisst die If - then wirklich so viel und warum benutzt du kein else? Und was ich an dem obigen wenigsten verstehe:

bei der Lo- Abfrage müsste doch der Port auf 1 gesetzt werden, oder irre ich mich da?

PS: ICQ habe ich, bin dort im Mom. aber sehr selten erreichbar.

teslanikola
02.05.2005, 18:22
Upps , hab Hi und Lo vertauscht.


$regfile = "2313def.dat"
$crystal = 10000000
$baud = 9600

Dim Hi As Byte
Dim Lo As Byte
Dim Freq As Byte
Dim Zeit As Byte

Config Pind.2 = Output

Config Timer1 = Timer , Prescale = 1

Enable Interrupts
On Timer1 Icnr
Enable Timer1
Timer1 = 65531



Do
Input "High x mal 1us: " , Hi
Input "Low x mal 1us: " , Lo
Loop



Icnr:

Incr Zeit

If Zeit = Lo And Portd.2 = 1 Then

Portb.2 = 0
Timer1 = 65531
Zeit = 0
End If

If Zeit = Hi And Portd.2 = 0 Then

Portb.2 = 0
Timer1 = 65531
Zeit = 0
End If

Return

warum benutzt du kein else?
Ist doch einfach, wenn ich ein else benutzen würde, würde bei JEDEM Interrupr der port verändert, die Zeit auf null und der Timer auf 65531 gesetzt, und das DARF ned sein.

Oszi?
Ganz einfach Hi und lo auf 1 setzen ( ergibt 0,5 Mhz ) und timervorgabe so lange verändern, bis man am oszi eine Frequen von 500kHz misst.

02.05.2005, 18:37
Hast du den Code schon mal real getestet?

Rage_Empire
02.05.2005, 18:48
@Tesla:
Hm, meinte ich anders mit der null, is aber nicht so wichtig. Probier das morgen nochmal. Jedoch benutze ich für Frequenzanalysen lieber nen Frequenzzähler (Rohde & Schwarz), der macht am wenigsten Zicken und ist sehr präzise.

teslanikola
02.05.2005, 20:23
So der code

$regfile = "2313def.dat"
$crystal = 10000000
Dim Zeit As Byte , Hi As Byte , Lo As Byte

Config Portb = Output

Config Timer1 = Timer , Prescale = 1

Enable Interrupts

On Timer1 Icnr

Enable Timer1

Timer1 = 65535
' timervorgabe zum erzeugen von einer Pause mit 100µS

Hi = 1
Lo = 1

Do
Loop


Icnr:
Incr Zeit

If Zeit = Hi And Portb.5 = 0 Then
Portb.5 = 1
Zeit = 0
End If

If Zeit = Lo And Portb.5 = 1 Then
Portb.5 = 0
Zeit = 0
End If

Timer1 = 65535

Return
komm mit dem beiu Hi = 1, lo = 1 auf 25 khz?????, very strange!!!!
Die ifs schlucken mächtig clocks

teslanikola
02.05.2005, 20:35
Das ist die besste möglichkeit (kommste auuuuuuuf
30 khz. MIST


$regfile = "2313def.dat"
$crystal = 10000000
Dim Hi As Word , Lo As Word

Config Portb = Output

Config Timer1 = Timer , Prescale = 1

Enable Interrupts

On Timer1 Icnr

Enable Timer1



Do
Hi = 65535
Lo = 65535
Loop


Icnr:
Toggle Portb.5
If Portb.5 = 0 Then Timer1 = Hi
If Portb.5 = 1 Then Timer1 = Lo
Return
Aber wenn man mehr will dann kommt man um ASM nicht mehr rum, hier endet dan mein Verständnis

Rage_Empire
02.05.2005, 21:45
Hm, ähnlich meinem Ursprünglichem Prinzip, nur sehr gekürzt würd ich behaupten. Das Prob ist, daß das Programm per Slave-I2C gesteuert werden soll, was (wenn es mal funzt) auch noch Resourcen frisst!
SHIT!!!
eigentlich sind es ja nur Zahen......wäre etwas mit "on Goto" vieleicht denkbar!? Oder ne Gleichung, wo nur 0 und 1 Entscheiden muß, wäre schneller. Sch.... daß ich kein Frequenzzähler Zuhause habe!!!
Beim x51 gibts im ASM-Befehlssatz nen Befehl, der eine Bedingung auf Wert 0 bezieht. Weiß nicht, ob der AVR auch so etwas besitzt.
Irgendwie ist das kleine Progy schon nen Akt!!!

teslanikola
03.05.2005, 11:04
denke nie es wird ein kleines Progy, denn damit liegst du meistens Falsch.Ach ein I²C Slave ist in bascom nicht gerade geschickt, da man eine speziella LIbix bracuh, und die koste Geld.

Rage_Empire
03.05.2005, 18:53
Habe es mal so versucht:



$regfile = "2313def.dat"
$crystal = 10000000
$baud = 9600

Dim Hi As Byte
Dim Lo As Byte
Dim Zeit As Byte

Config Pind.2 = Output

Config Timer1 = Timer , Prescale = 1

Enable Interrupts
On Timer1 Icnr
Enable Timer1
Timer1 = 65526
Hi = 1
Lo = 1

Do

Input "High x mal 1us: " , Hi
Input "Low x mal 1us: " , Lo

Loop



Icnr:

Incr Zeit

If Zeit = Hi Then

Swap Hi , Lo

Zeit = 0
Portd.2 = Not Pind.2

End If

Timer1 = 65526

Return


Laut sim geht die meiste Zeit beim Return drauf. Hat glaub ich was mit dem sichern der Register zu tun. Da war mal was mit NOSAVE, was ich aber erst mal genauer betrachten muß. Denke, daß es die Lösung ist!

teslanikola
03.05.2005, 19:01
einfachmal Nosave an den On befehl hängen und schauen ob alles noch so läuft wies soll, kann ja nix kaputt gehen.

Rage_Empire
03.05.2005, 19:57
So, mal schaun wieviel der Code hergibt:



$regfile = "2313def.dat"
$crystal = 10000000
$baud = 9600

Dim Hi As Byte
Dim Lo As Byte
Dim Zeit As Byte

Config Pind.2 = Output

Config Timer1 = Timer , Prescale = 1

Enable Interrupts
On Timer1 Icnr Nosave
Enable Timer1
Timer1 = 65526
Hi = 1
Lo = 1

Do

Input "High x mal 1us: " , Hi
Input "Low x mal 1us: " , Lo


Loop



Icnr:

Push r0
push r4
push r15
push r16
push r19
push r20

Incr Zeit

If Zeit = Hi Then

Swap Hi , Lo

Zeit = 0
Portd.2 = Not Pind.2

End If

Timer1 = 65526

pop r20
pop r19
pop r16
pop r15
Pop r4
pop r0


Return




...leider werd ich erst am Freitag dazu kommen, die max. Frequenz zu ermitteln!

Rage_Empire
06.05.2005, 11:40
Tada! Hier ist er.... Der Source schafft ne max. Frequenz von ca. 60kHz bei 10Mhz Quarz und einstellbarem Puls- Pausenverhältnis!


$regfile = "2313def.dat"
$crystal = 10000000
$baud = 9600

Dim Hi As Word
Dim Lo As Word
Dim Tim1 As Word
Dim Tim2 As Word

Config Pind.5 = Output

Config Timer1 = Timer , Prescale = 1

Enable Interrupts
On Timer1 Icnr Nosave
Enable Timer1

Do

Input "High [1-65535]" , Hi
Input "Low [1-65535]" , Lo

Disable Timer1
Tim1 = Hi
Tim2 = Lo
Portd.5 = 1
Enable Timer1

Loop



Icnr:

$asm
in r2, sreg
'push r0
'push r1
push r2
'push r3
'push r4
'push r5
'push r6
'push r7
'push r8
'push r9
'push r10
'push r11
'push r12
'push r13
'push r14
'push r15
push r16
push r17
'push r18
push r19
'push r20
'push r21
'push r22
'push r23
push r24
'push r25
push r26
'push r27
'push r28
'push r29
'push r30
'push r31
$end Asm

Load Timer1 , Tim1


Swap Tim1 , Tim2
Portd.5 = Not Pind.5


$asm
'pop r31
'Pop r30
'pop r29
'pop r28
'pop r27
pop r26
'pop r25
pop r24
'pop r23
'pop r22
'pop r21
'pop r20
pop r19
'pop r18
pop r17
pop r16
'pop r15
'pop r14
'pop r13
'pop r12
'pop r11
'pop r10
'pop r9
'pop r8
'pop r7
'pop r6
'pop r5
'pop r4
'pop r3
pop r2
'pop r1
'pop r0
Out Sreg , R2
$end Asm




Falls jemand ne andere Idee hat, dies in Bscom zu realisieren kann er sich hierzu gerne äusern!