Hallo, ich habe nun schon sämtliche Beiträge zu diesem Thema durchforstet, bin aber leider noch nicht fündig geworden. Ich bin noch ein Neuling im Programmieren mit C. Anbei seht Ihr mein Programm. Was es tun soll: Eine PWM wird durch Timer1 erstellt und die ISR mit der Frequenz der PWM synchronisiert. In der ISR wird der Wert eines Potis ausgelesen, mit dem die PWM gesteuert werden kann. Dieser Teil funktioniert auch einwandfrei (getestet an einer LED). Darüberhinaus soll der eingelesene Sollwert in der ISR über ein Display ausgegeben werden. Im Main-Programm seht ihr Timer_Konfigurierung(); . In diesem Programm wird die ISR aktiviert mit sei(); . Das Programm lcd_init(); ist in einem anderen Quellcode hinterlegt, welches das LCD-Display initialisiert. Diese Programm habe ich mit #include "LCD.h" eingebunden.´ Was ist mein Problem: Ich vermute, dass das Programm in der ISR hängen bleibt, da die LCD-Initialisierung nicht ausgeführt wird. Kommentiere ich Timer_Konfigurierung(); oder lediglich das sei(); aus, funktioniert die LCD-Initialisierung. In der while-Schleife wird sollwert_anzeige umgewandelt und auf dem Display positioniert und angezeigt. Dieser Teil funktioniert auch tadellos, sobald ich das Programm Sollwert_einlesen(); in der while-Schleife aufrufe. Doch ich möchte eben den Sollwert in der ISR einlesen und nicht in der while-Schleife. Könnt Ihr mir weiterhelfen? Vielen Dank schonmal! Gruß, Daniel PS: Der Part Istwert_einlesen(); spielt hier noch keine Rolle; deswegen auskommentiert. //////////////////////////////////////////////////////////////////////// ////////////////////// //Sollwertanzeige über Display //////////////////////////////////////////////////////////////////////// ////////////////////// /////////////define//////////// #define F_CPU 3686400 #define Sollwert_Wandler 0 ////////////include/////////// #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include <stdio.h> #include <stdlib.h> #include "LCD.h" ////////////uint_Global//////////// uint16_t sollwert_adc = 0; uint8_t sollwert_anzeige = 0; uint8_t istwert_adc = 0; uint8_t istwert_anzeige = 0; //////////Displayausgabe////////// char SW_char [1]; char IW_char [1]; void Sollwert_einlesen(void); //////////ADC konfigurieren///////////// void ADC_Konfigurierung(void) { ADMUX = 0x40; //Bit7=0 (Refs1), Bit6=1 (Refs0) -> Referenz ist Vcc-Board //Bit5=0 (ADLAR) -> right adjust 10bit //Bit4=0 reserviert //Bit3=0 (Mux3), Bit2=0 (Mux2), Bit1=0 (Mux1), Bit0=0 (Mux0) -> PortC0 als ADC Eingang ADCSRA = 0xC9; //Bit7=1 (ADEN) -> Freigabe ADC //Bit6=1 (ADSC) -> Starten einer Einzelwandlung //Bit5=0 (ADFR) -> Dauerwandlung; 0 weil zeitdiskret (PWM) //Bit4=0 (ADIF) -> wird automatisch nach der Wandlung auf 1 gesetzt //Bit3=1 (ADIE) -> Interrupt wird nach Beendigung ausgeloest //Bit2=0 (ADPS2), Bit1=0 (ADPS1), Bit0=1 (ADPS0) -> Prescaler 2 } /////Interrupt Service Routine, PWM//////////// ISR(TIMER1_OVF_vect) { Sollwert_einlesen(); // Istwert_einlesen(); OCR1A = sollwert_adc; } ///////////Timer/Counter 1 konfigurieren//////////// void Timer_Konfigurierung (void) { OCR1A = 0x00; TCCR1A = 0xF1; //Bit7=1 (COM1A1), Bit6=1 (COM1A0), Bit5=1 (COM1B1), Bit4=1 (COM1B0) -> Set OC1A/OC1B on compare match //Bit3=0 (FOC1A), Bit2=0 (FOC1B) -> no force output compare //Bit1=0, Bit0=1 -> Fast PWM 8 bit TCCR1B = 0x09; //Bit7=0 (ICNC1), Bit6=0 (ICES1) -> noise canceller inactive //Bit5=0 (-) reserviert //Bit4=0 (WGM13), Bit3=1 (WGM12) -> Fast PWM 8 bit //Bit2=0 (CS12), Bit1=0 (CS11), Bit0=1 (CS10) -> Prescaler 1 TIMSK |= 0x04; //Bit0=1 (TOIE0), Bit1=0, //Bit2=1 (TOIE1) Interrupt wird aktiviert //Bit3=0 (OCIE1B), Bit4=0 (OCIE1A), Bit5=0 (TICIE1), Bit6=0 (TOIE2), Bit7=0 (OCIE2) sei(); //Freigabe aller Interrupts } ////////////Sollwert einlesen///////////// void Sollwert_einlesen (void) { sollwert_adc = 0; ADMUX = 0x40; DDRB = 0x02; //Port B als Ausgaenge -> B1 abgreifen (OC1A) DDRC = 0x00; //Port C wird komplett als Eingang deklariert Poti-Signal (Port C-0) _delay_ms(50); ADCSRA |= 0x40; //Wandlung starten while (ADCSRA & 0x40); sollwert_adc= ADC/4; ADMUX =0x41; sollwert_anzeige=sollwert_adc*0.35; } ////////////Istwert einlesen///////// /* void Istwert_einlesen (void) { ADMUX = 0x41; DDRB = 0xFF; /////Dient der Kontrolle nicht löschen!//Port B als Ausgaenge -> B1 abgreifen (OC1A) (nicht notwendig) DDRC = 0x00; /////Dient der Kontrolle nicht löschen!//Port C wird komplett als Eingang deklariert Poti-Signal (Port C-0) (nicht notwenidg) _delay_ms(50); //erforderliches Delay ADCSRA |= 0x40; //Wandlung starten while (ADCSRA & 0x40); //Schleife, bis Wandlung abgeschlossen ist istwert_adc= ADC; ADMUX= 0x40; // Register zurücksetzen //ADMUX &= ~ 0x41; (eventuelle besser, da bit 0x40 komplett ausgeschaltet wird.) istwert_anzeige=sollwert_adc*90/256; // Wert umrechnen für das Anzeigemodul } */ ///////////Hauptprogramm////////////////////// int main (void) { DDRB = 0xFF; //Port B als Ausgaenge -> B1 abgreifen (OC1A) DDRC = 0x00; //Port C wird komplett als Eingang deklariert Poti-Signal (Port C-0) _delay_ms(100); ADC_Konfigurierung(); Timer_Konfigurierung(); lcd_init(); while(1) { char2ascii(sollwert_anzeige,SW_char); set_cursor(14,1); lcd_text(SW_char); } }
Du rufst in der ISR die Sollwert-Einlesen auf und in der wartest du 50ms in einem delay? Böse!
> uint16_t sollwert_adc = 0; > uint8_t sollwert_anzeige = 0; > sollwert_anzeige=sollwert_adc*0.35; sieht auch seltsam aus ... (ohne tiefer zu analysieren, was da gemacht wird)
> uint16_t sollwert_adc = 0; > uint8_t sollwert_anzeige = 0; Hier definiere ich Variablen für den ADC. > sollwert_anzeige=sollwert_adc*0.35; Hier wird der Wert sollwert_anzeige normiert auf den Bereich 0 ... 89. Das wird genau so benötigt, da der Sollwert zur Ansteuerung einer Drosselklappe dient. Deren Winkel reicht von 0 ... 89 °.
Wenn die Timerinterrupts während der init des LCD schon kommen, könnte es da evtl auch zu Problemen führen. Da die aber nicht angehängt ist, kann man das so genau nicht sagen.
Hier mal der Quellcode für das LCD-Display: ///////////////////////////////////////////////////////////// //Konfiguration des LCD-Displays ///////////////////////////////////////////////////////////// #define F_CPU 3686400 #include <avr/io.h> #include <util/delay.h> #include <stdio.h> #include "LCD.h" ///////////////////////////////////////////////////////////// //Programm: Initialisierung ///////////////////////////////////////////////////////////// void lcd_init(void) { char deg = 0xDF; //°-Zeichen aus Tabelle aus Datenblatt DDRD = 0xFF; //Port D als Ausgang PORTD = 0x00; //Port D 0 setzen _delay_ms(30); //wait more than 15 ms after 'Power On' //dreimaliger Reset PORTD = 0x30; //Reset: DB4=1, DB5=1, RS=0, E=0 PORTD = 0x38; //Reset: DB4=1, DB5=1, RS=0, E=1 PORTD = 0x30; //Reset: DB4=1, DB5=1, RS=0, E=0 _delay_ms(10); //wait more than 4.1 ms PORTD = 0x38; //Reset: DB4=1, DB5=1, RS=0, E=1 PORTD = 0x30; //Reset: DB4=1, DB5=1, RS=0, E=0 _delay_ms(1); //wait more than 100 ns PORTD = 0x38; //Reset: DB4=1, DB5=1, RS=0, E=1 PORTD = 0x30; //Reset: DB4=1, DB5=1, RS=0, E=0 _delay_ms(10); //zusätzliches Puffer //4bit Modus aktivieren PORTD = 0x28; //4bit Modus, 2 line display, 5x7 dots PORTD = 0x20; //Enable _delay_ms(5); //LCD 2x16 konfig lcd_cmd(0x28); //4bit Modus, 2 line display, 5x7 dots lcd_cmd(0x08); //Display off lcd_cmd(0x01); //Display clear //set Entry Mode lcd_cmd(0x06); //Cursor Decrease lcd_cmd(0x0C); //Display on, Cursor off, Blinking off //Cursor to first digit (Cursor Home) cursor_home(0x02); //LCD-Anzeige lcd_text("Initialisieren"); set_cursor(1,2); lcd_text("."); _delay_ms(500); set_cursor(2,2); lcd_text("."); _delay_ms(500); set_cursor(3,2); lcd_text("."); _delay_ms(500); set_cursor(4,2); lcd_text("."); _delay_ms(500); set_cursor(5,2); lcd_text("."); _delay_ms(500); set_cursor(6,2); lcd_text("."); _delay_ms(500); lcd_cmd(0x01); //Display clear _delay_ms(5); lcd_text("Sollwert:"); //Anzeigen: 'Sollwert:' set_cursor(16,1); lcd_text(°); set_cursor(1,2); lcd_text("Istwert:"); //Anzeigen: 'Istwert:' set_cursor(16,2); lcd_text(°); //Ende Initialisierung } ///////////////////////////////////////////////////////////// //Programm: Text senden ///////////////////////////////////////////////////////////// void lcd_text(char* pText) { char j = 0; // Zählvariable für Zeichen while(pText[j]!=0) // Ausgabe bis 0-Zeichen als Ende-Kennung { unsigned char send = pText[j]; //Sendevariable definieren send &= 0xF0; //Bit4 bis 7 maskieren send |= 0x04; //RS=1 (senden), E=0 PORTD = send; //Signalausgabe send |= 0x08; //RS=1, E=1 PORTD = send; //Signalausgabe send &= 0xF7; //Bit4 bis 7 maskieren und E=0 PORTD = send; //Signalausgabe send = pText[j]<<4; //Bit0 bis 3 nach links schieben send |= 0x04; //RS=1, E=0 PORTD = send; //Signalausgabe send |= 0x08; //RS=1, E=1 PORTD = send; //Signalausgabe send &= 0xF7; //Bit4 bis 7 maskieren und E=0 PORTD = send; //Signalausgabe _delay_ms(5); //Puffer j++; } } ///////////////////////////////////////////////////////////// //Programm: Kommando ans Display ///////////////////////////////////////////////////////////// void lcd_cmd(unsigned char cmd) { unsigned char send = cmd; //Anlegen der Sendevariable zum Zwischenspeicher PORTD = 0x00; //RS=0, E=0 PORTD = 0x08; //RS=0, E=1 send = cmd & 0xF0; //Bit 4 bis 7 extrahieren send |= 0x08; //in Sendevariable E=1 PORTD = send; //Signalausgabe PORTD &= 0xF7; //RS=1, E=0 PORTD = 0x08; //RS=0, E=1 send = cmd<<4; //Bit 0 bis 3 nach links schieben send |= 0x08; //in Sendevariable E=1 PORTD = send; //Signalausgabe PORTD &= 0xF7; //RS=1, E=0 _delay_ms(5); //zusätzliches Puffer } ///////////////////////////////////////////////////////////// //Programm: Umrechnung einer Zahl in eine Zeichenkette ///////////////////////////////////////////////////////////// void char2ascii(unsigned char zahl,char* text) { char ziffer = 0; //Variable für Ziffer //Zehnerstelle ermitteln while(zahl>=10) { ziffer++; //Zehnerstelle inkrementieren zahl -=10; //Zahl um 10 erniedrigen } text[0] = ziffer + 0x30; //Umrechnung Ziffer in ASCII-Zeichen text[1] = zahl + 0x30; //Umrechnung Zahl in ASCII-Zeichen text[2] = 0; //Ende der Zeichenkette } ///////////////////////////////////////////////////////////// //Programm: Cursor Home ///////////////////////////////////////////////////////////// void cursor_home(unsigned char c_home) { lcd_cmd(c_home); //Cursor move to first digit _delay_ms(5); //Puffer } ///////////////////////////////////////////////////////////// //Programm: Cursor positionieren ///////////////////////////////////////////////////////////// void set_cursor(int Spalte, int Zeile) { if((Spalte <= 16) & (Zeile <= 2)) { if(Zeile <= 1) Zeile = 0x00; //DD RAM -> erste Zeile else Zeile = 0x40; //DD RAM -> zweite Zeile char send = (Spalte-1) | Zeile; send |= 0x80; //Write to DD RAM -> Cursor setzen lcd_cmd(send); } }
dein problem wird sein, dasss du in jeder ISR 50ms wartest. Gaaaanz schlecht. Nicht nur vom Stil her, sondern es wird auch dein Problem verursachen. Wie immer die Faustregel: nur sehr kurze Dinge in der ISR tun, alles andere im normalen Programm. Extrem gesagt: setzte ein Flag und werte es in der Hauptschleife aus.
Daniel schrieb: > //////////Displayausgabe////////// > char SW_char [1]; > char IW_char [1]; > ... > char2ascii(sollwert_anzeige,SW_char); Daniel schrieb: > void char2ascii(unsigned char zahl,char* text) > { > char ziffer = 0; //Variable für Ziffer > > //Zehnerstelle ermitteln > while(zahl>=10) > { > ziffer++; //Zehnerstelle inkrementieren > zahl -=10; //Zahl um 10 erniedrigen > } > > text[0] = ziffer + 0x30; //Umrechnung Ziffer in ASCII-Zeichen > text[1] = zahl + 0x30; //Umrechnung Zahl in ASCII-Zeichen > text[2] = 0; //Ende der Zeichenkette > } Das schreibst du über die Array Grenzen hinaus
:
Bearbeitet durch User
Daniel schrieb: > sei(); //Freigabe aller Interrupts Warum tust du das, bevor du mit den Initialisierungen, insbesondere vom LCD fertig bist. Damit zerschießt du dir dort jegliches Timing.
> dein problem wird sein, dasss du in jeder ISR 50ms wartest. Verstehe ich! Aber wäre das der Fehler, würde das mit dem Dimmen der LED auch nicht funktionieren, oder? Danke Max H. Ist mir gar nicht aufgefallen, hab ich angepasst! Ändert aber leider nichts am eigentlichen Problem.
Wolfgang schrieb: > Warum tust du das, bevor du mit den Initialisierungen, insbesondere vom >LCD fertig bist. Damit zerschießt du dir dort jegliches Timing. Danke für den Tip! Habe es gerade mal ausprobiert und den Befehl sei(); in das Main-Programm hinter lcd_init(); gesetzt. Nun wird die Initialisierung durchgeführt aber kein Wert auf dem Display angezeigt. Das Dimmen der LED funktioniert immer noch. Ich denke, dass das Programm hier wieder in der ISR festhängt, oder?
Wie oft kommt dein Timer Interrupt? Wenn er schneller als 50ms ist, hängst du zu 100% im Interrupt!
Sprich, nimm das delay aus der ISR! Die PWM funktioniert, da du sie in der ISR setzt und der Rest die Hardware über nimmt. Dein Main kommt aber nie dran.
Klasse! Danke Frank! Das Problem war folgendes: PWM-Frequenz: 14kHz -> T_p = 0,07 ms. Da die ISR mit der PWM synchronisiert ist, wurde die ISR alle 0,07 ms durchlaufen, konnte aber ihr beinhaltetes Programm, welches ein delay von 50ms beinhaltete, nicht schnell genug durchlaufen. Habe das delay auskommentiert. Jetzt funktioniert es! Allerdings 'hinkt' die Anzeige der Potenziometerstellung hinterher. Sprich drehe ich am Poti, dauert es ca 4 sek bis der richtige Wert am Display angezeigt wird. Woran könnte das nun liegen?
Daniel schrieb: > Woran könnte das nun liegen? Daran, dass du immer noch das gleiche Problem hast. Quizfrage: wie lange dauert eine ADC-Wandlung?
Ich würde es so machen: Du musst versuchen das warten auf das ADC Ergebnis in der ISR zu vermeiden. Sprich: Timerinterrupt startet nur die ADC Messung, mehr nicht. Im ADC Interrupt setzt Du nun nur ein Flag, mehr nicht. In der Main pollst du dieses Flag und setzt Deinen neuen PWM Wert und zählst einen Zähler hoch. Sobald der Zähler 750 anzeigt aktualisierst Du einmal Dein Display. Denn eine Updaterate mit mehr als 20Hz bringt dir eh nichts!
Frank schrieb: > Im ADC Interrupt setzt Du nun nur ein Flag, mehr nicht. Den Wert evtl auch noch sichern.
Ich verstehe das leider noch nicht ganz. Wann wird nun der Sollwert eingelesen? Und wahrscheinlich verdonnern mich die ein oder anderen für folgende Frage; was ist ein Flag?
Hallo Daniel, das was die anderen Programmierer beschreiben, nennt sich Event-Orientierte Programmierung. Ein Flag ist eine Event-Flag, der nur zwei Zustände kennt. So etwas kann mit mit einer Variable vom Datentype boolean lösen. Ist die Event Variable =false ist das Event /Ereignis noch nicht eingetroffen, im anderen Fall schon.
Daniel schrieb: > sollwert_anzeige=sollwert_adc*0.35; Diese Zeile ist auch etwas unglücklich. Versuch es mit einem Koeffizienten, der ohne Fließkomma auskommt. Btw: Dein Compiler gibt dir keine Warnung bei der Zeile aus?
N. G. schrieb: > dein problem wird sein, dasss du in jeder ISR 50ms wartest. Und danach stößt er auch noch eine ADC-Wandlung an und wartet auf das Ergebnis.
Deine Timer ISR triggert momentan schon deinen ADC. Leider wartet sie dann in der Sollwert-einlesen() auf das Ergebnis. Das ist schlecht und führt im Prinzip zu einem ähnlichem Problem wie vorher das wait! Die Interrupts überholen sich. Deshalb versucht man die schnellen Dinge(wie die PWM anhand des ADCs zu stellen) von den langsamen Dingen (wie die LCD Ausgabe) zu entkoppeln. Deshalb sollte der Timer die Messung des ADCs triggern. Evtl brauchst du da noch nicht mal eine Timer ISR. Schau mal im Datenblatt beim ADC ob der Timer den ADC direkt triggern kann. Ich meine das geht. Dadurch übernimmt das die Hardware und du sparst dir sogar noch die Zeit für den ISR Ein/Aussprung. Dann brauchst du nur noch in der ADC ISR ein Flag* setzen, evtl das PWM Register neu setzen und den Wert des ADCs speichern. In der Main überprüfst du dann das Flag* und aktualisierst das LCD. (Oder nur wenn sich der Wert geändert hat). Grüße Frank *Flag = Bit z.B in einer globalen Variable.
Georg G. schrieb:
> Btw: Dein Compiler gibt dir keine Warnung bei der Zeile aus?
Nein, werd ich aber im Folgenden berücksichtigen, danke!
Danke Frank für deine Erklärung! Den Sinn dahinter habe ich nun
verstanden, doch leider fehlt mir die nötige Programmiererfahrung, um
das eben mal einfach so umzusetzen. Könntest Du mir vielleicht auf die
Sprünge helfen? Die Timer ISR würde ich gerne so beibehalten (Vorgabe),
wenn das möglich ist.
Ich habe nur ein Handy zur Hand, also nein :-) Du musst im wesentlichen folgendes machen: *In der ADC Config den ISR für den ADC aktivieren (siehe Datenblatt oder Forumartikel) *Die ADC ISR einfügen. *Alles aus der Timer ISR raus. *Das starten des ADC aus der Sollwert-Einlesen in die Timer ISR. Die While für das warten auf das Ende der Messung kannst du löschen. *In der ADC ISR den Wert Formatieren,speichern und auf das PWM Register speichern. Zusätzlich ein flag setzen. *In der Main überprüfen ob das flag gesetzt ist. Wenn ja löschen und einen Zähler hochzählen. *Wenn der Zähler größer 700 ist und ein anderer Wert im ADC Wert steht als auf dem Display, dann die Anzeige aktualisieren.
Frank schrieb: > In der ADC ISR den Wert Formatieren,speichern und auf das PWM Register > speichern. Das würde ich nicht machen. Er arbeitet mit Float. Das kostet viel Zeit. Warum nicht nur den ADC auslesen und den Wert merken (als volatile!). Alles andere kann in Ruhe in der Hauptschleife erfolgen.
Georg G. schrieb: > Frank schrieb: > In der ADC ISR den Wert Formatieren,speichern und auf das PWM Register > speichern. > > Das würde ich nicht machen. Er arbeitet mit Float. Das kostet viel Zeit. > Warum nicht nur den ADC auslesen und den Wert merken (als volatile!). > Alles andere kann in Ruhe in der Hauptschleife erfolgen. Kann er. Ich weiß nur nicht ob er nach jeder ADC Messung das PWM Register aktualisieren möchte. Das würde in der Main evtl nicht gehen. Außerdem muss er nicht mit float arbeiten! Ein ADC*7/80 würde auch reichen.
Frank schrieb: > *In der ADC Config den ISR für den ADC aktivieren (siehe Datenblatt oder > Forumartikel) Siehe Bild im Anhang. Habe nun das ACSR = 0x13 gesetzt. Stimmt das? Wie implementiere ich das ADC ISR? Arbeite nun nicht mehr mit Fließkommazahlen, sondern mit ...*10/28
Frank schrieb: > *In der ADC ISR den Wert Formatieren,speichern und auf das PWM Register > speichern. Zusätzlich ein flag setzen. > > *In der Main überprüfen ob das flag gesetzt ist. Wenn ja löschen und > einen Zähler hochzählen. So wohl eher nicht. *In der ADC ISR den Wert in einen Zwischenspeicher sichern und eine Signalflagge setzen. *In der Main überprüfen, ob die Signalflagge gesetzt ist, wenn "ja", Wert aus dem Zwischenspeicher holen, formatieren, umrechnen und was sonst noch damit passieren soll. Signalflagge herunternehmen.
Wolfgang schrieb: > So wohl eher nicht. > *In der ADC ISR den Wert in einen Zwischenspeicher sichern und eine > Signalflagge setzen. > > *In der Main überprüfen, ob die Signalflagge gesetzt ist, wenn "ja", > Wert aus dem Zwischenspeicher holen, formatieren, umrechnen und was > sonst noch damit passieren soll. Signalflagge herunternehmen. Was passiert wenn er in der Main gerade sein Display aktualisiert oder noch andere Dinge macht, dann wertet er das Flag evtl zu spät aus und verpasst ein paar ADC Werte -->OCR1A verpasst Werte. Mit Formatierung meinte ich nur das umrechnen durch 7/80. Das kann er dann noch in der ISR auf sein OCR1A schreiben -->jeder wert landet auf dem PWM Register
Daniel schrieb: > Da die ISR mit der PWM synchronisiert ist, wurde die ISR alle 0,07 ms > durchlaufen Allerdings. Die ISR wird alle 256 Taktzyklen aufgerufen. Das ist schon sehr schnell. Da darf nichts mehr drin sein, das nicht unbedingt nötig ist, und ganz besonders darf da nicht gewartet werden.
Daniel schrieb: > Ich verstehe das leider noch nicht ganz. Wann wird nun der Sollwert > eingelesen? Und wahrscheinlich verdonnern mich die ein oder anderen für > folgende Frage; was ist ein Flag? Du hast ne ganze Menge von Dingen noch nicht verstanden. Also: ein Interrupt ist ein Signal, was den normalen Ablauf des Programmes unterbricht, damit die CPU mal eben was ganz wichtiges tun kann. Aber das MUSS !!! so kurz wie möglich passieren und auf gar keinen Fall durch irgendwelche langatmigen Dinge wie das Starten des ADC oder gar das sture Warten in einer Delay-Routine unnötig verlängert werden, denn es gibt zwischenzeitlich noch ne Menge andere Arbeiten zu verrichten. Und ein Flag ist schlichtweg ein Hinweis. Wie du den machst, ist eigentlich egal. Hier mal ein kleines Beispiel: char iii, ggg; Interrupt-Routine: { tu irgendwas; ++iii; } main: if (iii!=ggg) // das != ist quasi das Flag { reagiere auf dieses Flag ggg = iii; } weiter in main... Also, egal ob beim Inkrementieren iii überläuft, die Abfrage in main findet IMMER heraus, ob die Interrupt-Routine anzeigen will, daß es da einen Hinweis von ihr gibt. W.S.
Kann mir jmd. vielleicht einen Anstoß mit Hilfe eines Quellcodes geben? Wie füge ich die ADC ISR ein? Wie rufe ich das Flag auf, etc.? Sehe ich das richtig, dass das Bit4 ACI im ACSR Register das Flag ist, das ich überprüfen muss? Fragen über Fragen, aber ich danke Euch für Eure Hilfe!
Sowas in der Art. Ist nicht getestet und nicht vollständig.
1 | #define ADC_FLAG 0x01
|
2 | #define LCD_RATE 800
|
3 | |
4 | volatile uint16_t u16SWAdc = 0; |
5 | volatile uint16_t u16SWAdcAnz = 0; |
6 | volatile uint8_t u8Flags = 0; |
7 | |
8 | ISR(TIMER1_OVF_vect) |
9 | {
|
10 | ADCSRA |= 0x40; // ADC-Wandlung starten |
11 | //...evtl anderer Code
|
12 | }
|
13 | |
14 | ISR(RICHTIGER ADC VEKTOR EINTRAGEN) //<-- Richtigen Vektor eintragen |
15 | {
|
16 | u8Flag | = ADC_FLAG; |
17 | u16SWAdc = ADC/4; //Wert fuer PWM berechnen |
18 | OCR1A = u16SWAdc; //& aktualisieren |
19 | |
20 | //...evtl anderer Code
|
21 | }
|
22 | |
23 | ///////////Hauptprogramm//////////////////////
|
24 | |
25 | int main (void) |
26 | {
|
27 | uint16_t counter = 0; |
28 | uint16_t u16OldSWAdcAnz = 0; |
29 | //Initialiserung usw...
|
30 | |
31 | while(1) |
32 | {
|
33 | if(u8Flags & ADC_FLAG) //ADC meldet Event |
34 | {
|
35 | u8Flags &= ~ADC_FLAG; //Flag loeschen |
36 | counter++; //Zaehler fuer LCD erhoehen |
37 | //Fuer kritische Dinge muesste dies
|
38 | //in der ISR erfolgen, da ansonsten
|
39 | //ein Event verpasst werden koennte
|
40 | }
|
41 | |
42 | if(counter > LCD_RATE) //Nur alle LCD_RATE mal das Display aktualisieren |
43 | {
|
44 | counter = 0; |
45 | |
46 | if(u16OldSWAdcAnz != u16SWAdcAnz) //Und nur wenn sich was geaendert hat |
47 | {
|
48 | u16OldSWAdcAnz = u16SWAdcAnz; //Aktuellen Wert als alter Wert speichern fuer naechstes Mal |
49 | char2ascii((u16SWAdc*7)/20,SW_char); //Berechnung nur durchfuehren |
50 | //Wenn sie benoetigt wird
|
51 | set_cursor(14,1); |
52 | lcd_text(SW_char); |
53 | }
|
54 | }
|
55 | }
|
56 | }
|
Grüße Frank
Vielen Dank Frank für deine Mühe! Habe deinen Teil nun bei mir implementiert. Die Ansteuerung der PWM funktioniert (Kontrolle: Dimmen der LED). Die Display-Initialisierung funktioniert auch, doch leider wird kein Sollwert angezeigt.
Wie sieht jetzt der code aus? Am besten so posten
1 | [c] |
2 | //hier code |
3 | [/c] |
Debugger vorhanden? Dann setzte mal ein paar Breakpoints an markanten Stellen! Ansonsten mach mal in den verschiedenen If's eine Ausgabe auf das Display oder setze ein Portpin und lass für jedes if ne andere LED leuchten um zu sehen ob er überall rein geht.
Hab vergessen u16SWAdcAnz zu setzen! Mach mal:
1 | if(u16OldSWAdcAnz != u16SWAdc) //Und nur wenn sich was geaendert hat |
2 | {
|
3 | u16OldSWAdcAnz = u16SWAdc; //Aktuellen Wert als alter Wert speichern fuer naechstes Mal |
4 | char2ascii((u16SWAdc*7)/20,SW_char); //Berechnung nur durchfuehren |
5 | //Wenn sie benoetigt wird
|
6 | set_cursor(14,1); |
7 | lcd_text(SW_char); |
8 | }
|
Dann braucht man u16SWAdcAnz garnicht mehr.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.