Forum: Mikrocontroller und Digitale Elektronik PWM ADC BASCOM


von Basti (Gast)


Lesenswert?

Hallo ich möchte gerne den ADC 0-2 auslesen und in Variablen speichern 
um diese dann in die Register campare1a,compare1b und ocr2 zu schreiben 
und damit dann das PWM signal zu steuern.

Ich weiß in meinem Code ist der ADC zur zeit auf 10bit eingestellt das 
werde ich aber später noch ändern auf 8bit sodas die Variablen in die 
Register passen.

Bis jetzt habe ich einfach erstmal Werte in die 3 Register geschrieben 
um nachzuprüfen ob die PWM Signale funktionieren.Das klappt auch.

Dann wollte ich auf dem Display erstmal die Werte von ADC0 ADC1 und ADC2 
anzeigen lassen bis jetzt wird aber nur 0 angezeigt.

In der ISR_ADC habe ich die ganzen if then drin, wenn das so nicht geht 
dann erklärt mir mal bitte wie man das macht.

Ich poste einfach mal meinen Code bitte erschlagt mich nicht ich bin 
noch Anfänger.



$regfile "m8def.dat "
$crystal = 3689400
$baud = 9600

Config Adc = Single , Prescaler = Auto , Reference = Avcc
Start Adc
Enable Interrupts
On Adc Isr_adc

Dim W As Word , Channel As Byte
Dim Adc0 As Word
Dim Adc1 As Word
Dim Adc2 As Word
Channel = 0

Config Portd = Output
Config Portc = Input

Config Timer0 = Timer , Prescale = 1
Config Timer1 = Pwm , Pwm = 8 , Compare A Pwm = Clear Down , Compare B 
Pwm = Clear Down , Prescale = 1
Config Timer2 = Pwm , Prescale = 1 , Compare Pwm = Clear Up , Pwm = On
Pwm1a = 0
Pwm1b = 0
Ocr2 = 0



'Die Timer freigeben
Enable Timer0
Enable Timer1
Enable Timer2


'Die Timer starten
Start Timer0
Start Timer1
Start Timer2


'Timer Interrups
On Timer0 Isr_timer0
On Timer1 Isr_timer1
On Timer2 Isr_timer2
Sreg.7 = 1

Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.5 , Db6 = Portd.6 , 
Db7 = Portd.7 , E = Portd.3 , Rs = Portd.2
Config Lcd = 16 * 2
Cls

Do


Locate 1 , 1
Lcd "" ; Adc0
Locate 1 , 7
Lcd "" ; Adc1
Locate 2 , 1
Lcd "" ; Adc2


Loop
End
'----------------------------------------------------------------------- 
---
Isr_adc:
W = Getadc(channel)
If Channel = 0 Then Adc0 = W
If Channel = 1 Then Adc1 = W
If Channel = 2 Then Adc2 = W
Incr Channel
If Channel > 2 Then Channel = 0
Return
'----------------------------------------------------------------------- 
----
Isr_timer0:
'Mach irgendwas für was auch immer

Return
'----------------------------------------------------------------------- 
----
Isr_timer1:
Pwm1a = 50
Pwm1b = 10
Return
'----------------------------------------------------------------------- 
----
Isr_timer2:
Ocr2 = 100
Return
'----------------------------------------------------------------------- 
----

von PeterL (Gast)


Lesenswert?

ich denke, dass Getadc eine Wandlung auslöst, wenn die fertig ist gibts 
den ADC Interrupt, du wirst also ständig  in deiner Routine bleiben.

von Karl H. (kbuchegg)


Lesenswert?

> W = Getadc(channel)

Wenn du sowieso GETADC benutzt, wozu dann der Interrupt? (ABgesehen 
davon, dass das so sowieso nicht geht. Der INterrupt würde aufgerufen 
werden, wenn das ADC Ergebnis fertig ist, d.h. man benutzt dann gar 
nicht mehr GETADC

Benutze GETADC einfach in der Hauptschleife und gut ists. Du brauchst 
dafür keine Interrupts.
1
Config Adc = Single , Prescaler = Auto , Reference = Avcc
2
Start Adc
3
4
5
Do
6
7
  Adc0 = GetADC( 0 )
8
  Adc1 = GetADC( 1 )
9
  Adc2 = GetADC( 2 )
10
11
  Locate 1 , 1
12
  Lcd "" ; Adc0
13
  Locate 1 , 7
14
  Lcd "" ; Adc1
15
  Locate 2 , 1
16
  Lcd "" ; Adc2
17
18
Loop

UNd die ganze INterruptbehandlung im Zusammenhang mit dem ADC wirfst du 
raus.

von PeterL (Gast)


Lesenswert?

gib das in den Timer0 Interrupt

Isr_timer0:

W = Getadc(channel)
If Channel = 1 Then compare1a = W
If Channel = 2 Then compare1b = W
Incr Channel
If Channel < 2 Then
                incr channel
              else
                 channel = 0
               endif

return



den ADC Interrupt brauchst du nicht.
Prescale 1 ist warscheinlich zu schnell, habs jetzt nicht nachgerechnet

von Basti (Gast)


Lesenswert?

Okay vielen dank Euch allen ich hatte auch alles in der Hauptschleife 
aber irgendeiner sagte mir mal sowas mach man im Interrupt.

von Basti (Gast)


Lesenswert?

So ich habe es jetzt so gemacht könnte mir einer genau erklären was 
getadc macht ich möchte die register selber schreiben.

$regfile "m8def.dat "
$crystal = 3689400
$baud = 9600

Adcsra = &B11000101
Admux = &B01000000


Dim W As Word , Channel As Byte
Dim Adc0 As Word
Dim Adc1 As Word
Dim Adc2 As Word
Channel = 0

Config Portd = Output
Config Portc = Input

Config Timer0 = Timer , Prescale = 8
Config Timer1 = Pwm , Pwm = 8 , Compare A Pwm = Clear Down , Compare B 
Pwm = Clear Down , Prescale = 1
Config Timer2 = Pwm , Prescale = 1 , Compare Pwm = Clear Up , Pwm = On
Pwm1a = 0
Pwm1b = 0
Ocr2 = 0



'Die Timer freigeben
Enable Timer0
Enable Timer1
Enable Timer2


'Die Timer starten
Start Timer0
Start Timer1
Start Timer2


'Timer Interrups
On Timer0 Isr_timer0
On Timer1 Isr_timer1
On Timer2 Isr_timer2
Sreg.7 = 1

Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.5 , Db6 = Portd.6 , 
Db7 = Portd.7 , E = Portd.3 , Rs = Portd.2
Config Lcd = 16 * 2
Cls

Do


Locate 1 , 1
Lcd "" ; Adc0
Locate 1 , 7
Lcd "" ; Adc1
Locate 2 , 1
Lcd "" ; Adc2
Waitms 20
Cls

Loop
End

'----------------------------------------------------------------------- 
----
Isr_timer0:
W = Getadc(channel)
If Channel = 0 Then Adc0 = W
If Channel = 1 Then Adc1 = W
If Channel = 2 Then Adc2 = W
Incr Channel
If Channel > 2 Then Channel = 0




Return
'----------------------------------------------------------------------- 
----
Isr_timer1:
Pwm1a = 50
Pwm1b = 10
Return
'----------------------------------------------------------------------- 
----
Isr_timer2:
Ocr2 = 100
Return
'----------------------------------------------------------------------- 
----

von Karl H. (kbuchegg)


Lesenswert?

Basti schrieb:
> So ich habe es jetzt so gemacht könnte mir einer genau erklären was
> getadc macht

Die BASCOM Hilfe.
Du musst dir angewöhnen, dass die Hilfe des Herstellers deine erste 
Anlaufstelle ist.

> ich möchte die register selber schreiben.

Wozu?
Genau aus dem Grund verwendest du doch BASCOM, damit du dich um diesen 
Kleinkram eben nicht selber kümmern musst.
GETACD startet eine Wandlung, wartet bis sie fertig ist und holt das 
Ergebnis ab.

Ich seh jetzt ehrlich gesagt nicht, warum das in einer Timer ISR 
passieren muss, aber seis drum.

von Mario (Gast)


Lesenswert?

Hi,

man macht im Interrupt so wenig wie möglich aber vor allem wird deine 
Adc_isr nie aufgerufen weil die dann kommt wenn der ADC fertig ist.

Die Ints für die PWM-Timer sind auch Quatsch. Es ist der Sinn der PWM, 
daß man zu beliebiger Zeit Pwm1a usw. setzt und den Rest macht der Timer 
alleine im Hintergrund.
Die Timer-Ints kommen beim Überlauf des Timers, die brauchst Du gar 
nicht.
Also:

Do
ADCs lesen
PWM setzen
Werte anzeigen
Loop

Fertig, Du brauchst keinen einzigen Int.
Noch eleganter wäre es die drei Punkte jeweils in Gosubs zu machen. Hier 
ist das noch übersichtlich aber wenn es mehr wird ist das eleganter.

von Basti (Gast)


Lesenswert?

Ne mir ist klar das ich die ISR nicht brauche die hatte ich nur zum 
Testen mal mit eingebaut.Ich würde gerne die Register selber 
schreiben,weil ich irgendwann auf C umsteigen möchte.Bascom ist nur der 
erste Schritt.Ich habe das ganze jetzt so gemacht.So funktioniert es

$regfile "m8def.dat "
$crystal = 3689400
$baud = 9600

Adcsra = &B11000101
Admux = &B01100000


Dim W As Word , Channel As Byte

Channel = 0

Config Portd = Output
Config Portc = Input

Config Timer0 = Timer , Prescale = 8
Config Timer1 = Pwm , Pwm = 8 , Compare A Pwm = Clear Down , Compare B 
Pwm = Clear Down , Prescale = 1
Config Timer2 = Pwm , Prescale = 1 , Compare Pwm = Clear Up , Pwm = On
Pwm1a = 0
Pwm1b = 0
Ocr2 = 0



'Die Timer freigeben
Enable Timer0
Enable Timer1
Enable Timer2


'Die Timer starten
Start Timer0
Start Timer1
Start Timer2


'Timer Interrups
On Timer0 Isr_timer0

Sreg.7 = 1

Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.5 , Db6 = Portd.6 , 
Db7 = Portd.7 , E = Portd.3 , Rs = Portd.2
Config Lcd = 16 * 2
Cls

Do


Loop
End

'----------------------------------------------------------------------- 
----
Isr_timer0:
W = Getadc(channel)
If Channel = 0 Then Pwm1a = Adch
If Channel = 1 Then Pwm1b = Adch
If Channel = 2 Then Ocr2 = Adch
Incr Channel
If Channel > 2 Then Channel = 0

Return

von Karl H. (kbuchegg)


Lesenswert?

Basti schrieb:
> Ne mir ist klar das ich die ISR nicht brauche die hatte ich nur zum
> Testen mal mit eingebaut.Ich würde gerne die Register selber
> schreiben,weil ich irgendwann auf C umsteigen möchte.

Dann steig auf C um.


> W = Getadc(channel)
> If Channel = 0 Then Pwm1a = Adch

Das ist doch Schwachsinn. Getadc macht die ganze Arbeit und du holst dir 
dann hinten nach noch einmal das Ergebnis ab. Den ADC auf einen Kanal 
konfigurieren, ihn aktivieren, die Wandlung abwarten und das Ergebnis 
abholen, das sind 4 Schritte. Alles was du tust ist, du wiederholst den 
letzten (den einfachsten) dieser Schritte. Du bist im Brotberuf nicht 
zufällig Politiker? Die machen das auch so: andere für sich arbeiten 
lassen und dann so tun, als ob sie alles gemacht hätten.

Wenn du an den ADC auf Registerebene rann willst, dann tu das. Aber dann 
tu es dann auch wirklich und richtig und schmück dich nicht mit fremden 
Federn, indem du GETADC die Arbeit machen lässt und hinten nach das 
Ergebnis noch einmal abstaubst.

von Basti (Gast)


Lesenswert?

Ja möchte ich ja selber machen ich habe das jetzt nur so gemacht wenn 
ich einfach GETADC benutze habe ich keine 8bit und diese passen dann 
auch nicht in den Timer für das PWM Signal oder nicht.Deswegen habe ich 
dann nur das Register ADCH nochmal gelesen.Ich würde ja Getadc weglassen 
das habe ich vorher versucht aber ich weiß nicht genau wie ich ADMUX und 
ADCSRA ansprechen muss.Was ich soweit bis jetzt verstanden habe ist das 
ich ja oben (config ADC) die erste Konversation gestartet habe mit ADC0
Dann muss ich warten bis das Ergebnis in den Registern steht.Ich habe 
den ADC (left adjust ) das heißt  ADCH=0-255.Dann muss ich Admux den 
ADC1 wählen und wieder die Konversation starten.Ich hoffe ich habe das 
so richtig verstanden.

Was mich jetzt interessieren würde wie ich in Bascom warten kann bis das 
Register ADCH ein Ergebnis hat

PS ich hoffe ihr versteht mich!!!!

von Karl H. (kbuchegg)


Lesenswert?

Basti schrieb:
> Ja möchte ich ja selber machen ich habe das jetzt nur so gemacht wenn
> ich einfach GETADC benutze habe ich keine 8bit

sondern 10. ALso Werte von 0 bis 1023

> und diese passen dann
> auch nicht in den Timer für das PWM Signal oder nicht.

Du willst also Werte von 0 bis 255.

Ist dividieren durch 4 wirklich so schwer (zumal das für den µC ein 
Klacks ist, einfach um 2 Bit nach links schieben und ich denke BASCOM 
hat sogar explizite Schiebeinstruktionen)

> Was mich jetzt interessieren würde wie ich in Bascom warten kann bis das
> Register ADCH ein Ergebnis hat

Wenn du sowieso nach C willst:
AVR-GCC-Tutorial

Auch das
AVR-Tutorial
wird hilfreich sein

In beiden Tutorien gibt es einen Abschnitt über den ADC und wie das 
funktioniert. Da sind die Register, die Bits in den Registern und in 
welcher Reihenfolge was zu machen ist. Selbst wenn du nicht in C oder 
Assembler programmierst sind sie hilfreich, denn die Vorgehensweise ist 
absolut identisch. Nur die Schreibweise ist ein wenig anders.

von MWS (Gast)


Lesenswert?

Basti schrieb:
> Was mich jetzt interessieren würde wie ich in Bascom warten kann bis das
> Register ADCH ein Ergebnis hat

Du startest im Single Mode eine Wandlung mit ADSC = 1 und wartest 
solange bis ADSC wieder 0 ist. Dann ist die Wandlung fertig.

von Basti (Gast)


Lesenswert?

Ja das mit dem Devidieren kenne ich dachte sowas sei nicht so gut.Hatte 
gedacht wenn ich direkt das ADCH nehme sei besser

von Karl H. (kbuchegg)


Lesenswert?

Basti schrieb:
> Ja das mit dem Devidieren kenne ich dachte sowas sei nicht so gut.Hatte
> gedacht wenn ich direkt das ADCH nehme sei besser

Mit Verlaub:
Aber wenn du den letzten Taktzyklus aus dem µC herausholen willst, dann 
fang damit an, nicht BASCOM zu benutzen. BASCOM ist zum schnellen 
Entwickeln super. Aber so wie in jeder Hochsprache bleibt immer ein 
bischen was auf der Strecke. Gott sei Dank, kommt es aber nur selten auf 
den letzten einzusparenden Taktzyklus an, so dass man sich ein bischen 
Schwund immer leisten kann. Ob dein µC jetzt 95% oder 94.8% seiner Zeit 
Däumchen dreht und das LCD vollkleistert, spielt keine wirkliche Rolle.

Wie schon gesagt: wenn du dem Compiler nicht traust, dass er eine 
Division durch 4 mittels Schieben realisiert, dann kannst du ja auch die 
Schiebebefehle von BASCOM direkt benutzen.

Besser ist immer relativ: Es ist relativ sinnfrei, GETADC ein 10 Bit 
Ergebnis fertig herrichten zu lassen, nur um es dann zu verwerfen und 
seinen eigenen Zugriff zu machen.

von Basti (Gast)


Lesenswert?

Könnte das ganze dann so aussehen??

Isr-Timer0

If ADCSRA.6 = 0 then ADC0= ADCH
Admux=&B01100001
ADCSRA.6=1
if adcsra.6 = 0 then adc1 = adch
usw.

von Karl H. (kbuchegg)


Lesenswert?

Basti schrieb:
> Könnte das ganze dann so aussehen??
>

Nein.

Was musst du tun?

Du musst das ADSC Bit (welches Bit 6 ist) auf 1 setzen (Siehe Datenblatt 
oder die Tutorien)
dann musst du warten, bis das ADSC Bit wieder auf 0 zurückfällt. (SIehe 
Datenblatt oder die Tutorien)
Dann kannst du dir das Ergebnis abholen (Siehe Datenblatt oder die 
Tutorien)

  ADCSRA.6 = 1           ; bit auf 1

  WHILE ADCSRA.6 = 1     ; warten bis es nicht mehr 1 ist
  WEND

  adc1 = ADCH            ; wert abholen

von Basti (Gast)


Lesenswert?

Danke das ist was ich brauchte (while) jetzt hab ich es verstanden 
vielen lieben dank

von Basti (Gast)


Lesenswert?

So hoffe so habe ich das jetzt richtig funktionieren tut es so

Isr_timer0:
Admux = &B01100000
Adcsra.6 = 1
While Adcsra.6 = 1
Wend
Pwm1a = Adch
Admux = &B01100001
Adcsra.6 = 1
While Adcsra.6 = 1
Wend
Pwm1b = Adch
Admux = &B01100010
Adcsra.6 = 1
While Adcsra.6 = 1
Wend
Ocr2 = Adch
return

von MWS (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> ADCSRA.6 = 1

Karl Heinz,

man kann in Bascom Register zusammen mit Bitnamen verwenden, das erspart 
die kryptische Bitabzählerei, also:

ADCSRA.ADSC = 1

von Basti (Gast)


Lesenswert?

Ja danke gut zu wissen geht das auch irgendwie mit den Mux Registern 
einfacher

von MWS (Gast)


Lesenswert?

Basti schrieb:
> Isr_timer0:
> Admux = &B01100000

Wurde Dir schon gesagt, dass es keine gute Idee ist, in der ISR zu 
wandeln und damit zu warten ?

Wenn ich mich nicht verrechnet hab', dürfte das bei angenommenen 200kHz 
ADC-Clock so ca. 35% der verfügbaren Rechenleistung kosten.

1 /(200000 : 13 : 3) = 195µs pro Wandlung bei 3689400 = 719 µC-Clocks
Abstand zwischen den Timer0-Aufrufen: 256 x 8 = 2048 µC-Clocks
Ergibt +35% Auslastung.

Basti schrieb:
> Ja danke gut zu wissen geht das auch irgendwie mit den Mux Registern
> einfacher

Die Kenntnis darum ist auch in C weiterverwendbar, nur die Syntax ist da 
etwas anders.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.