Forum: Mikrocontroller und Digitale Elektronik C Anfänger: PWM Signal auslesen


von Christian (Gast)


Lesenswert?

Hi ich hab' mir ein Muster von einem ADXL202 Beschleunigungssensor 
schicken lassen - der liefert ein PWM Signal - das Verhältnis ist von 
der Beschleunigung abhängig - jetzt hab'ich kleiner heinzi gleich mal 
eine Routine geschrieben die den IC auslesen soll und dass ganze an 
Portb mit led's darstellen soll. Irgendwann hat sich mal was getan, wenn 
ich die Testplatine gegen den Tisch geschlagen habe - dann hamm aber 
gleich alles geleuchtet - wär super wenn sich mein codevision compiler c 
Programm mal jemand anschauen kann...........
Ich bin mir sicher, dass hätte man wesentlich intelligenter lösen 
können(so mit timer und so....) Ihr könnt ja mal vorschläge machen....
Freue mich auf Verbesserungen!!!

Vielen lieben Dank!!!!!

#include <90s2313.h>
#include <delay.h>

#define PWM PORTD.0

void main (void)
{
unsigned char zaehler1 = 0;
unsigned char zaehler2 = 0;
float ergebnis;

DDRD=0;      //auf eingang schalten
DDRB=255;      //auf ausgang schalten


start:

zaehler1=0;   //zaehler auf null setzten
zaehler2=0;

while (PWM==1){}; //warten bis eine 0 kommt
                  //und eine neue messung losgeht

while ( PWM==0 )  //warten bis eine 1 kommt
{
zaehler1 ++;      //zähler alle 2µshochsetzen
delay_us(2);
};

while ( PWM==1 ) //warten bis eine 0 kommmt
{
zaehler2 ++;
delay_us(2);
};

ergebnis=(zaehler1/zaehler2);
ergebnis-=1;     1 abziehen (ich gehe von zahlen größer 1 aus)
ergebnis*=100;  //prozentwert ausrechnen

PORTB=255;     //alles nochmal ausknipsen, damit man es hernach wieder 
anknipsen kann - sieht man doch eh nicht in der zeit????

if (ergebnis>0)PORTB.0=0;
if (ergebnis>10)PORTB.1=0;
if (ergebnis>20)PORTB.2=0;
if (ergebnis>30)PORTB.3=0;
if (ergebnis>40)PORTB.4=0;
if (ergebnis>50)PORTB.5=0;
if (ergebnis>60)PORTB.6=0;
if (ergebnis>70)PORTB.7=0;


delay_ms(20); // a bisserl warten
goto start;   //neu anfangen

}

von Ingo B. (Gast)


Lesenswert?

Hi,

wie wäre es, wenn Du die PWM in eine analoge Spannung wandelst? Mit 
einen Kondensator und einen Widerstand. Dann das ganze auf den 
Analogeingang des AVR. Die Zeitkonstante der RC musst du an deine Freq 
anpassen.
Sollte doch gehen - anders herum geht's doch auch - uC erzeugt PWM und 
per RC bekommt man eine analoge Spannung....

Bis dann,
Ingo.

PS:
goto start;
--> Jeder C-Programmierer hat einen Schreikrampf bekommen ;-))

Und noch ein Tipp: Verwende keine Gleitkomma-Variablen, es läßt sich so 
ziemlich alles mit Ganzzahlen machen. (wegen Geschwindigkeit und 
Speicherplatz). Am besten gar nicht erst angewöhnen....
Nur ein Rat - keine Nörgelei.

von Christian (Gast)


Lesenswert?

Hi Ingo
Dank dir für deine Antwort!!!
Das mit dem RC Glied hab' ich mir auch schon überlegt, vorallem weil es 
schneller geht - das Porblem im Moment ist, dass ich nur noch einen 
einzigen 2313 habe - gibts auch 20pinner mit AD Wandler (der 2313 hat 
doch keinen??)
Das mit dem Schreikrampf hab' ich mir schon gedacht (hab' ich mal 
irgendwo gelesen) ich bin halt ein Assembler Programmierer ;-).

Danke für den Rat mit den Gleitkomma-Variablen (du meinst doch das 
float)

Vielen Dank!!
mfg
Christian

von Phagsae (Gast)


Lesenswert?

Moin schon wieder Ich :-)


Abgesehen von den obigen Einwänden
( Goto ist basic und deshalb als Spagetticode gefürchtet )

Für Endlosschleife besser

while (1)
{
Das ist meine Schleife
}

Trick : while bricht nur bei (0) ab

alle anderen " Sprünge" werden über funktionen erreicht



Die if Arie zum auswerten funtioniert ist aber nicht sehr elegant

könnte man zb durch

unsigned char Vu_meter = 0 ;
for ( Ergebnis ; Ergebniss>10 ; Ergebniss/10)
{  Vu_meter<<1, Vu_meter++ };

PORTB=Vu_meter

erledigen  -> braucht weniger Speicher

Der Trick funktionier so :
00000000 <<1
00000000 +1
00000001 <<1
00000010 +1
00000011<<1

PS um die Daten via RS232 ans PC Terminal zu liefern
Brauchts eigentlich nur 10 Codezeilen

Da kannste die Messwerte bequem am Monitor Anschauen

Phagsae

von Christian (Gast)


Lesenswert?

Hi Phagsae
Danke nochmal für deine Antwort!!!!
Bevor ich dein Posting gesehen habe, habe ich mich mal mit der Mgl. mit 
dem AD Wandler beschäftigt und hab' für den 2333 ein Experimetierboard 
gebastelt (nur auf einer Lochrasterplatine) - naja und dann hab' ich das 
ADC Code beispiel vom Codevision getetstet - läuft aber nicht (war ja 
klar) - so allgemein läuft der Controller aber schon - er lässt sich 
immerhin programmieren......... Muss ich vielleicht eine andere externe 
Beschaltung nehmen um den ADC zu aktivieren oder hast du irgendeinen 
Vorschlag, woran das liegen kann????????????

Vielen Dank!!!

mfg
Christian

Hier noch das Code Beispiel com codevision compiler:

// I/O register definitions for AT90S8535
#include <90s2333.h>
// delay functions
#include <delay.h>

#define ADC_VREF_TYPE 0x00
// ADC interrupt service routine
interrupt [ADC_INT] void adc_isr(void)
{
// The LEDs will display the 8 most
// semnificative ADC bits
PORTB=(unsigned char) ~(ADCW>>2);
// 20ms delay
delay_ms(20);
// Start a new AD conversion
ADCSR|=0x40;
}

void main(void)
{
// Port B initialization
PORTB=0xFF; // all outputs
DDRB=0xFF;  // all LEDs are initially off

DDRD = 255;
delay_ms(2000);

// ADC initialization
// ADC Clock frequency: 57.656 kHz
// ADC Interrupts: On
ADCSR=0x8E;

// Global enable interrupts
#asm("sei")

// Select ADC input 0
ADMUX=0;

// Start the first AD conversion
ADCSR|=0x40;

// All the job is done by ADC interrupts
while (1);
}

von Christian (Gast)


Lesenswert?

Hallo Leute ich bins wieder.....
ich hirsch hab' den falschen Prozessor eingestellt gehabt (der compiler 
hat für einen anderen AVR Compiliert als ich eingesetzt habe - jetzt 
funktoniert es etwas, aber auch nicht wirklich; folgendes Problem: Die 
led's an denen der AD WErt ausgegebn werden schenken serh sehr schnell 
um - in einem Berich von2,7-2,8 Volt gehen die Led's fast schlagartig an 
oder aus (alle!!) - was hab' ich denn jetzt schon wieder falsch 
gemacht???? Es ist auch egal an welchen AD eingang ich gehe.
2. Problem - weiß jemand, wie ich das RC Glied zu dimesnionieren habe um 
aus einem PWM Signal mit 1000Hz meine Analoge Spannung zu bekommen?????

Vielen lieben Dank für eure Antworten!!!

mfg
Christian

von Phagsae (Gast)


Lesenswert?

Hmmmmmm.....

Moin moin

Also wenn du dich um das verstehen der Ausgangssignale
drücken willst ist ( Rüge!)
Die PWM natürlich ne lösung aber ne gaaaaanz schlechte.
:-(
Wir sollten Dich schon fitmachen damit du in ein paar monaten unsere 
Fragen baentworten kannst :-)

Als Faustformel gilt das Tau=RC ungefähr 10..100 mal dem PWM Signaltakt 
entspricht. ( Lade-zeit-konstante der RC combi )

#So berechnet sich übrigens die RC combi
#-> Takt -> Faktor wählen 10...1000 -> Tau -> C vorgeben -> R ausrechnen

Dh erst nach ca 10..100 PWM Ereignissen ist der Analogwert eingstellt

Du nimmst ja ein 6dB Tiefpass und der ist logischerweise nicht mehr 
sonderlich schnell

Bei Beschleunigungs sensoren ist meist der Impulsverlauf und der Peak 
wert wichtig.
( sonst kann man ja auch rechnen )

Als groben Ablauf wenns denn doch sowas wie "messen" werden soll
Und nicht nur  das die LED's funzeln.

Zuerst die Gesamtdauer einer periode ermitteln
Dann speichern = T2
Dann wie schon richtig angefangen auf Signal 1 warten

Ab jetzt einfach zählen
Das delay verschlechtert nur die Auflösung
Als laufvariable würd ich allerdings schon ein int nehmen
vieleicht brauchts sogar ein double
Kommt ordentlich was an impulsen zusammen

Je nach taktfrequenz des g-Sensors -> Datenblatt

So jetzt der Trick:

Die Low-zeit braut nicht ermittelt werden da
die  Gesamtperiode sich nicht ändert = T2

Die Zeit kann aber zur berechnung von g genutzt werden so das kein 
Messwert unausgetastet bleibt.

Die Formel lautet übrigens lt Datenblatt

A(g)=(T1/T2-1/2)*12,5%
f ->A(g)  ist logischerweise signed

für T1=1/2 T2 ist dann auch der Messwert 0
Impulsbreite 1:1

Der Formel nach kann der Sensor +- 8 g
bzw 1/8 = 1 g

Bevor man bei der formel zum float greift hilft umskalieren
ersetzt 12,5% durch 125
-> max = +- 1000 = 0x3e8 -> 3bit müssen schon sein also int
-> diese vorkalkulation auch für T1 und T2 machen

Da klappt auch mit dem G-messenen


Phagsae

von Christian (Gast)


Lesenswert?

Hi Phagsae
nunächst mal vielen lieben Dank für deine ausführliche Antwort. Deine 
Kritik am messen der analogspannung war jetzt also, dass sich die Werte 
über das lahme RC Glied nicht schnell genung ändern???? Außerdem ist es 
genauer, wenn ich das Signal direkt einlese oder???? (vermutlich muss 
ich deinen Beitrag nochein paar mal lesen, um ihn zu verstehen...)
Das Problem beim PWM Signal ist,dass die Dauer über einen Widerstand 
einstellbar ist - ich also wegen den Bauteiltoleranzen nicht so  genau 
sagen kann wieviel Prozent ich jetzt schon hab' - deshalb dachte ich mir 
mess ich halt mal den 2.wert mit...... Nachdem ich leicht gefrustet von 
der Methode mit dem AD Wandler bin stürz ich mich wieder auf die PWM 
Methode. Irgendwann schaff ich s schon noch...... Nochmals vielen 
herzlichen Dank - mal hoffen, dass es nun geht (unter berücksichtigung 
aller deiner Tipps ;-)

MfG
Christian

PS: es soll tatsächlich mal sowas wie "messen" werden, nur wollte ich 
das erstmal testen - wie du siehst bin ich ja bereits daran 
gescheitert.......

von Christian (Gast)


Lesenswert?

Hi - ich bins scho wieder ;-)

Hab' jetzt mal so rumgebazelt wie du es mir empfohlen hast (denk ich 
jedenfalls) - funktoniert schon wieder nicht (hab' ich mir fast gedacht) 
- ist aber nicht so schlimm....... Ich kommm schon noch drauf - nur hat 
mich jetzt wieder ein wenig der Mut und die Zeit verlassen.
Würd' mich freuen, wenn du's dir mal anschauen würdest;

MfG
Christian

#include <90s2313.h>
#include <delay.h>

#define PWM PORTD.0

unsigned int T2;


void T2_ermitteln (void)
{
while (PWM==1){}; //warten bis eine 0 kommt

while ( PWM==0 )  //warten bis eine 1 kommt
{
T2 ++;
};

while (PWM==1)
{
T2 ++;
};
}


void main (void)
{
unsigned int T1;
float Ag;
unsigned char Vu_meter = 0;
DDRD=0;      //auf eingang schalten
DDRB=255;      //auf ausgang schalten

T2_ermitteln();

while (1)  //endlosschleife
{
T1=0;   //zähler wieder zurücksetzen

while (PWM==0){}; //warten bis eine 1 kommt

while ( PWM==1 )  //warten bis eine 0 kommt
{
T1 ++;
};

Ag=(T1/T2-0.5)*12,5;


PORTB=255;     //alles nochmal ausknipsen, damit man es hernach wieder 
anknipsen kann


Vu_meter = 0;
for ( Ag ; Ag>10 ; Ag/10)
{ Vu_meter<<1, Vu_meter++; }

PORTB=Vu_meter;
};
}

von Phagsae (Gast)


Lesenswert?

He da gibts ja eigentlich nix mehr zu meckern
Das sollte bis auf eine kleine logelei klappen
(kommt aber später)
Ich verbessere noch einige kleinigkeiten die
man sich nicht angewöhnen sollte bzw die das listing
besser lesbar machen


unsigned int T2;
### Globale declaration nur wenn nötig
### Klaut dem prozer für die gesamte Zeit ein Speicherplatz
### ob man die Variable noch braucht oder nicht

void T2_ermitteln (void)
{
    while (PWM==1){}; //warten bis eine 0 kommt
    while (PWM==0 ) {T2 ++;};       ## besser lesbar
    while (PWM==1){T2 ++;};        ## besser lesbar
}


####### Besser mit return arbeiten
int T_2ermitteln (void);  Deklaration der Funktion

int T2_ermitteln (void)
{

Deine funktion
## Die funktion T2_ermitteln liefer den wert T2 zurück
return T2;
## Die bezeichnung ist nur lokal gültig !!
## Muss also nicht wie unten wieder an T2 übergeben werden
}

void main (void)
{
unsigned int T1;
float Ag;
unsigned char Vu_meter = 0;
DDRD=0;      //auf eingang schalten
DDRB=255;      //auf ausgang schalten

T2=T2_ermitteln(); ###schupps Da isser wieder der T2 :-)

## aber viel eleganter und eine Speicherstelle sparend
## du kannst "T2_ermitteln" gleich in die Ag funktion einbauen
## also Ag=(T1/T2_ermitteln(void)-0,5.... usw

while (1)  //endlosschleife
{
    T1=0;   //zähler wieder zurücksetzen
    while (PWM==0){}; //warten bis eine 1 kommt
    while ( PWM==1 )  { T1 ++; };

Ag=(T1/T2-0.5)*12,5; < liegts am komma ???
PORTB=255;     //alles nochmal ausknipsen, damit man es hernach wieder 
anknipsen kann
Vu_meter = 0;
for ( Ag ; Ag>10 ; Ag/10)
{ Vu_meter<<1, Vu_meter++; }
PORTB=Vu_meter;

## Hier steckt der Hund !!!!
## Ich hab bei meinem Vorschlag einer gesetzt das ist latürnich nix
## Zuerst löscht du alles und dann will die Funktion einser setzten
## Da passiert natürlich nix ( zumindest nix sichtbares )
## Die Led leuchtet ja bei 0
## Also nich addieren sondern nur nuller einschieben

};
}



Hast du das mal im AVR Studio debugged ?


Phagsae

von Phagsae (Gast)


Lesenswert?

Uuuuups

Die Formel stimmt ja gar nicht
Im Analog Device  Datenblatt

(Bei MAXIM hab ich den nicht gefunden)

Steht (T1/T2-0,5)/12,5%

int und -,5 kann probleme machen
besser umformen

(T1/T2-1/2)/(1/8)=

8*(T1-T2)/(T2)

So ist das ohne komma und ohne float
Somit kann man das float auch durch ein int ersetzten
Spart wieder 4 bit


Phagsae
Der eben festgestellt hat das 2000 wörter doch 4k sind
Somit kann ich meinen 4433 in die tonne treten

Schupps sitzt der übergewichtige ATMEGA163 im Sockel
Wenn schon 40polig dann aber richtig !!!!!!
Muss jetzt eher schaun ob sich meine code nicht irgendwo im Speicher
verirrt
( ist  ja  jetzt alles mit unendlichen weiten gefüllt)

DA hätt ich mir jetzt gleich das LCD library schreiben sparen können
Naja

von Christian (Gast)


Lesenswert?

Hallo Phagsae
nochmals vielen lieben Dank für deine ausführliche (nächtlichen) 
Antworten!!!!!!!
Das mit dem debuggen in AVR Studio hab' ich nun zum ersten mal pobiert 
und  find's irgendwie komisch - wo gibts denn meine Variablen zum 
anschauen und wie kann ich einen Befehl überspringen ohne ihn 
auszuführen????
Das eigentliche Problem schaut im Moment so aus, dass die leds an PORTB 
immer leuchten - warum weiß ich eben nicht - selbst wenn ich hinschreibe 
PORTB=0; delay_ms(200) - irgendwie überreiß ich was grundlegendes an 
dieser lustigen Sprache wohl nicht ;-)
Naja und damit hab' ich gerade die letzten 3h  verbracht - irgendwie 
komm ich mir leicht unprodukiv vor. Mal hoffen dass du noch eine geniale 
Lsg auf Lager hast, damit ich wieder einen sperrlichen  Schritt nach 
vorne mache :-)
Das eigentliche Projekt ist ein Flugroboter - ich möchte mein 
elektroflugzeug automatisieren - im Prinzip kein großes Problem, wenn 
da nicht C wäre....... Aber in Assembler wird es wohl schwierig die 
ganzen Formeln unterzubringen - abgesehen davon  kann ich nur Assembler 
für 8051....

Freu mich auf deine Antwort!!!!!

MfG
Christian

von Phagsae (Gast)


Lesenswert?

Meine Formel ist ein rechter Schhhhh...
Warum fällt das keinem auf ?

Phagsae ? Erweitern setzten 6 !!


Hab mal in ruhe drüber nachgedacht
Für einen Flugroboter ist die PWM methode geeignet
Die Bewegungs Grenzfrequenz des Roboters wird ja kaum im Bereich von 
100Hz liegen oder ??
Egal.... das kann man am sensor eintelllen da ist ja schon ne Filterung 
vorgesehen.

Eigentlich würde man jetzt erstmal eine Betrachtung anstellen bzgl der 
maximalen Beschleunigung etc

Aber das ist was fürs Physik forum

Die Formel jaaaaaa.... das ist gar nicht so trival..oder eigentlich doch
wenn man mal die Bretter vorm Hirn abmontiert


Ich führ das mal exemplarisch vor
( Sollte mich demnächst selbst dran halten spart zeit  )

1. Mal überschlagen welche Daten denn vom Sensor kommen
wenn man mal von 1ms T2 ausgeht und den uC mit 4Mhz treibt.
Die while n++ schleife ( Das solle man überprüfen)
wird warscheinlich aus sbrs  ink in bestehen
sind also ca 2+1+1 Takte eher mehr
4Mhz->1 Tkt 25 0,25 uS -> 1 Schleife 1us
Messdauer 1ms ( T2) ->1ms/1us=1000

Also gehen wir mal von max 1000 Werten aus

Die Formel ist mit ganzzahlen zunächst nicht brauchbar
( auch r i c h t i g umgestellt nicht )

Ag(einheit 1g)=(T1/T2 -0,5)/12,5%

kommt dann richtig umgestellt auf
Ag=((2T1-T2)*8)/2T2

Preisfrage kommt man mit nem int aus ?
Ag kann + oder - sein
also signed

grösster Faktor ist (EGAL)*8 wobei 8* einem <<3 entspricht

(EGAL) kann maximal 1000 werden -> 0x3E8
mal 8 ist das 0x1F40
OK mit nem normalen  signed int geht das locker

ABER

Probleme macht (Egal)*8/T2
T2 ist einfach zu gross für Ganzzahl division

>>Wer denken kann ist da klar im vorteil was soll den auch rauskommen >>wenn
>>Der Messbereich des Sensors +-8g ist 255 ?????
>>Brett #2

Da kann man gerade noch 1 Stelle sehen

Also: Her mit dem Trick

1Ag( g ) = 1000 Ag(mg)

zauber zauber......

Ag(mg)=8000*(Egal)/2T2

Ag(mg)=(8000*(2T1-T2))/2T2
Ag(mg)=(4000*(2T1-T2))/ T2

Aber 4000*1000= 4*10^6 -> also signed long int


T1=250 -> -2000mg
T1=500 -> 0g
T1=750 -> +2000mg

Das solls denn dann mal sein...

Phagsae

von Christian (Gast)


Lesenswert?

Hi Phagsae
nochmals vielen lieben Dank für deine zahlreichen ausführlichen 
Antworten und deine Ausdauer, die du mit entgegebracht hast ;-)

Es funktioniert jetzt (endlich)!!!!!

Irgendwie ist noch ein kleiner Wurm in der Schleife drin, die die LED's 
an und aus schaltet - ist aber nicht so schlimm. Der Sensor liefert 
irgendwie ein wenig zittrige Werte - die led's zittern teilweise sogar 
im Ruhezustand, obwohl die so eine schlechte Auflösung haben.
In einem anderemPostin hast du mal geschrieben,dass du studiert hast - 
was denn und was machst du jetzt?????

Danke nochmals für deine Unterstützun - langsam macht C spaß

mfg
Christian

von Phagsae (Gast)


Lesenswert?

Danke fürs Bartkraulen :-)

Aber so nett binn ich gar nicht.
Hab ja auch mal so nebenbei was Gelernt.
Ich weiss ja jetzt das es interessant Beschl Sensoren gibt wie man sie 
ausliest und welche Formel wohl funktioniert bzw wie die Auswertung 
beschaffen sein muss.

Wie du an der Formel sehen konntes ist ja auch oft unausgegorener 
Quatsch dabei.

Hast du ein Oszi ? und ein Signalgenerator ?

Dann überprüf doch mal ob der Output auch mit den Messergebnissen 
übereinstimmt.

Oder mach mit dem TIMER2 mal ne PWM die du dann dem Messeingang 
zuführst.
( ich hoffe das geht is auch nur ne uaQ Idee )

Oder mach beides über die Soundkarte und entsprechender Software.

Der AVR kann ja mit beliebig kleinem Takt arbeiten
nennt sich "full static"

Als wirklich gute debbug methode hat sich für mich das anzeigen best
Zustände via UART auf dem PC herausgestellt )

Mach starte ein neues Projekt
Und aktiviere im CVR Code Wizard einfach die RS232

Verbinde TX RX mit deinem PC
Wenn du keine fertige Schnittstelle hast hilf da ein MAX323 plus 4 Cond

Dann schreib ein putchar( char ) ein zeichen richtung stdio
ist immer auf RS232 eingestellt sofern man das nicht ändert

Zeichenketten gehen via printf("Laberrabarber", ptoTEXT )
ptoTEXT ist ein zwischenpuffer
der als char ptoTEXT[] angelegt wird

Der CVAVR hat  reduzierten formatierungs befehle
Das kann aber vorher ausgeglichen werden

Das Zittern kann normal sein
Welche Werte gibst du denn in die LED Kette ?
1 mg kann der sensor eigentlich gar nicht auflösen
Ich wollt nur nicht so exotische einheiten wie 10-2 g einführen
Als kleinste einheit konnte der glaube ich 10mg ( ? )

Je nach lage müsste er ja auch 1g durch die Erdanziehung anzeigen.

Macht er das ?

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.