Hy Ich möchte bei einem INT0 Interrupt, das die Interrupt Routine etwas per UART versendet. Die Daten sind char strings, dann in zwei Funktionen verschickt werden… Als Test verschicke ich es zur Zeit nach jeder Messung auch noch mal was später dann wegfallen sollte. Leider kann ich nicht mehr in die Chars schreiben, wenn ich sie global def. auch mit volatile bzw. wenn ich sie in der Main def, findet die Interrupt Routine nix!! Was kann ich tun!! #define F_CPU 8000000UL #include <avr/io.h> #include <stdio.h> #include <stdlib.h> #include <inttypes.h> #include <avr/interrupt.h> #include <avr/signal.h> #define BAUD 38400UL #define UBRR_BAUD ((F_CPU)/(16*(BAUD))-1) unsigned char volatile LFC[6]; unsigned char volatile TC[6]; unsigned char volatile LFKC[6]; int uart_putc(unsigned char c) { while (!(UCSRA & (1<<UDRE))); UDR = c; return 0; } int uart_puts( char* str ) { while( *str ) uart_putc( *str++ ); return 0; } int main (void) { double LF = 0; double T = 0; double LFkomp = 0; UCSRB |= (1 << TXEN) | ( 1 << RXEN ); // UART TX, RX einschalten UCSRC |= ( 1 << URSEL )|( 3<<UCSZ0 ); // Asynchron 8N1 UBRRH = (uint8_t) (UBRR_BAUD>>8); // USART Baud UBRRL = (uint8_t) UBRR_BAUD; ADCSRA = _BV(ADEN) | _BV(ADPS1) | _BV(ADPS2); // AD anschalten & Prescaler 64 Takt = (CLK/Prescaler) 50-200kHz MCUCR = _BV(ISC00); GICR = _BV(INT0); sei(); uart_puts("System wurde gestartet\r"); for (;;) { ADMUX = 0; // AD Kanal A.0 ADCSRA |= _BV(ADSC); // AD Messung starten while (ADCSRA & _BV(ADSC)) // bis ein Wert eingelesen ist warten {} LF = ADCW; … ADMUX = 0x01; // AD Kanal A.1 ADCSRA |= _BV(ADSC); // AD Messung starten while (ADCSRA & _BV(ADSC) ) // bis ein Wert eingelesen ist {} T = ADCW; ….. LFkomp =….; dtostrf(LF,6,3,LFC); dtostrf(T,6,3,TC); dtostrf(LFkomp,6,3,LFKC); uart_puts("LFC"); uart_puts(TC); uart_puts(LFKC); } } SIGNAL (SIG_INTERRUPT0) { uart_puts("LFC"); uart_puts(TC); uart_puts(LFKC); }
Könntest Du vielleicht mal präzisieren, wo genau die Probleme auftreten? BTW: SIGNAL ist veraltet. Bitte ISR verwenden und, falls noch nicht geschehen, auf aktuelle WINAVR-Version updaten. Außerdem haben serielle Ausgaberoutinen in einem Interrupt-Handler normalerweise nichts zu suchen.
Übrigens: Du hast Deine Strings alle mit 6 Elementen deklariert, hast aber beim Aufruf von dtostrf 6 Stellen als "width" angegeben. Das passt nicht! Für den Nullterminator brauchst Du eine zusätzliche Stelle. Alle Arrays müssen in dem Fall als "unsigned char NAME[7]" deklariert werden, sonst schreibt dtostrf den Nullterminator ins Speichernirvana und die Ausgaberoutine findet ihn nicht.
Geh noch größer. Vorzeichen, Dezimalpunkt etc. Ist es meistens unklug, die char Felder zu exakt dimensionieren zu wollen (ausser man ist in grosser Speichernot). Mach die Felder 15 oder 20 Zeichen gross. Ob die Bytes jetzt brach liegen oder ob dort char Felder angelegt sind, ist dem RAM aber sowas von Wurscht.
vielen dank für die schnellen antworten. C:/WinAVR/avr/include/avr/signal.h:36:2: warning: #warning "This header file is obsolete. Use <avr/interrupt.h>." woher bekomme ich die neueste? hab die erst vor 1 Woche runtergeladen...
Sagte er doch: signal.h: Warnung,... Diese File ist veraltet. Bitte benutze <avr/interrupt.h> Du möchtest vielleicht mal hier nachlesen. http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Veraltete_Funktionen_zur_Deklaration_von_Interrupt-Routinen
ok danke. Mein Problem lag hoffentlich an der Größe der char Variablen und er hat daher nie etwas in die Interruptfunktion übergeben können... werde es morgen mal testen. aber globale Variablen sollten doch kein Problem in der Interrupt Funktion sein oder? Bzw wie würde es besser gehen? es soll halt beim INTO auslösen die Daten gesendet werden...
@Karl Heinz: Afaik ist die im Aufruf von dtostrf angegebene "width" aber inkl. Dezimalpunkt und eventuellem Vorzeichen. Das sagt mir zumindest der Kommentar in der stdlib.h. Ob es bei seinen zu erwartenden Zahlenwerten ausreicht, mit 6 Stellen (davon drei Nachkomma und ein Dezimalpunkt und u.U. zusätzlich noch ein Vorzeichen, bleibt evtl. nur eine Stelle vor dem Dezimalpunkt übrig) darzustellen, kann man anhand des geposteten Codes nicht nachvollziehen, da die ganzen Zwischenrechnungen fehlen. @Jörn: Code von der Größe demnächst bitte als Anhang schicken! Und bei kleineren Codestücken auf eine übersichtliche Formatierung achten (Code kannst Du auch im Fenster in Farb-Syntax-Darstellung posten, wenn Du ihn in
1 | und
|
einschließt).
Na, das hat ja geklappt. Also C-Code in "eckige Klammer auf, großes C, eckige Klammer zu" und am Ende "eckige Klammer auf, Schrägstrich (/), großes C, eckige Klammer zu"...
> Afaik ist die im Aufruf von dtostrf angegebene "width" aber inkl. > Dezimalpunkt und eventuellem Vorzeichen. Ja schon. Die Zahl vor dem Punkt ist die Gesamtzahl an Stellen, also die Feldbreite. Die spannende Frage ist immer nur: Was passiert, wenn die angegebene Feldbreite nicht ausreicht, weil meine auszugebende Zahl da partout nicht hineinpasst. Sich darauf zu verlassen, dass printf niemals mehr als die Feldbreite schreibt, ist nicht weise. Es tut es nämlich nicht. Wenn das Formatierfeld nicht gross genug ist, dann schreibt printf mehr. Ein 4 stelliger int passt nun mal nicht in einen %3d, also werden auch 4 Zeichen geschrieben.
@Karl Heinz: Er benutzt aber gar kein printf, sondern nur dtostrf...
> Er benutzt aber gar kein printf, sondern nur dtostrf...
<Hochscroll>
<Rotwerd>
Uh, oh, ähm.
Mein Fehler
Grob geschätzt: Es ist bei dtostrf auch nicht
anders.
hy funktioniert alles aber suuuper langsam!! Die umwandlung von double in das char mit dtostrf()... dauert 10ms jeweils!! das ist viel zu lange!!! wie kann ich die double Werte per UART einfacher bzw. schneller übertragen?? max 2 Ziffern vor dem Komma und 2 dahinter reichen aus...
10 ms bei 8 MHz (entspricht 80000 CPU-Takten) kommt mir auch ein bisschen arg viel vor. Da müsste sogar sprintf schneller sein. Woher nimmst Du die Gewissheit, dass das so lange dauert? Bist Du sicher, dass der µC mit 8 MHz läuft?
hab vor und hinter dem befehl PINB=0x02 an aus gemacht und das ganze mit dem Oszi gemessen... fused sind in sut1 nix sut0 haken cksel3 haken cksel2 nix cksel1 haken cksel0 haken
Bin ich blind, oder warum finde ich in dem Code nirgends eine Anweisung "PINB = 0x02"? Was für ein Controller ist das überhaupt?
Zeig nochmal das jetzige Pgm vollständig. 10ms ist in der Tat extrem langsam.
[Code] #define F_CPU 8000000UL #include <avr/io.h> #include <stdio.h> #include <stdlib.h> #include <inttypes.h> #include <avr/interrupt.h> #define BAUD 38400UL #define UBRR_BAUD ((F_CPU)/(16*(BAUD))-1) char LFC[15]; char TC[15]; char LFKC[15]; int uart_putc(unsigned char c) { while (!(UCSRA & (1<<UDRE))); UDR = c; return 0; } int uart_puts( char* str ) { while( *str ) uart_putc( *str++ ); return 0; } int main (void) { double Faktor = 0.0048828125; double LF = 0; double T = 0; double LFkomp = 0; UCSRB |= (1 << TXEN) | ( 1 << RXEN ); // UART TX, RX einschalten UCSRC |= ( 1 << URSEL )|( 3<<UCSZ0 ); // Asynchron 8N1 UBRRH = (uint8_t) (UBRR_BAUD>>8); // USART Baud UBRRL = (uint8_t) UBRR_BAUD; ADCSRA = _BV(ADEN) | _BV(ADPS1) | _BV(ADPS2); // AD anschalten & Prescaler 64 Takt = (CLK/Prescaler) 50-200kHz MCUCR = _BV(ISC00)|_BV(ISC01); GICR = _BV(INT0); sei(); uart_puts("System wurde gestartet\r"); DDRB = 0x03; for (;;) { // Rohleitfähigkeit einlesen // 0-5V 0-24 mS => ADMUX = 0; // AD Kanal A.0 ADCSRA |= _BV(ADSC); // AD Messung starten while (ADCSRA & _BV(ADSC)) // bis ein Wert eingelesen ist warten {} LF = ADCW; // Weise den Wert b zu LF = LF*(double)Faktor; LF = LF * 5.42857; // Umrechnung Spannung pro mS // Temperatur einlesen // 0-5V ,0-60 C ADMUX = 0x01; // AD Kanal A.1 ADCSRA |= _BV(ADSC); // AD Messung starten while (ADCSRA & _BV(ADSC) ) // bis ein Wert eingelesen ist {} T = ADCW; // Weise den Wert b zu T= (T*Faktor)*28.869-27.713; int loesung = 1; // Acetat = 1 // Bicarbonat = 2 if(loesung == 1) { double a = 0.021; LFkomp = LF+LF*a*(T-25); } else if(loesung == 2) { double a = 0.023; LFkomp = LF+a*(T-25); } PORTB=0x01; dtostrf(LF,6,4,LFC); PORTB=0x00; dtostrf(T,6,4,TC); dtostrf(LFkomp,6,4,LFKC); } } ISR (SIG_INTERRUPT0) { uart_puts("\r"); // uart_puts( "Rohleitfähigkeit : " ); uart_puts(LFC); // uart_puts("[mS/cm]\r"); uart_puts("\r"); // uart_puts( "Temperatur : " ); uart_puts(TC); // uart_puts("[°C]\r"); uart_puts("\r"); // uart_puts( "Endleitfähigkeit : " ); uart_puts(LFKC); // uart_puts("[mS/cm]\r"); uart_puts("\r"); } [\Code]
Hmm. In der Tat, das sieht soweit nicht so schlecht aus. Und wenn du sagst, dass der Puls 10ms breit ist, glaub ich dir das (obwohl ichs mir immer noch nicht vorstellen kann, bist du sicher, dass du nicht die Zeit zwischen 2 Pulsen gemessen hast?) Da du anscheinend sowieso immer 2 Vorkommastellen und 2 Nachkommastellen hast, könnte man die dtostrf() auch ersetzen: void Format( double d, char* String ) { int Vorkomma = (int)d; int Nachkomma = (int)( ( d - Vorkomma ) * 100 ); sprintf( String, "%d.%02d", Vorkomma, Nachkomma ); } Wenn der sprintf zu viele Resourcen verbraucht, dann musste man dafür einen Ersatz mit itoa und temporären Strings die dann zum Gesamtergebnis zusammengesetzt werden basteln.
Das > void Format( double d, char* String ) > { > int Vorkomma = (int)d; > int Nachkomma = (int)( ( d - Vorkomma ) * 100 ); > > sprintf( String, "%d.%02d", Vorkomma, Nachkomma ); > } ist vielleicht gar nicht so gut. Könnte Ärger mit der Rechengenauigkeit und Rundungsfehlern geben. Eher mal in die Richtung arbeiten:
1 | void Format( double d, char* String ) |
2 | {
|
3 | int Zahl = (int)( d * 100 + 0.5 ); |
4 | sprintf( String, "%d.%02d", Zahl / 100, Zahl % 100 ); |
5 | }
|
Achtung: Beide Funktionen berücksichtigen kein negatives Vorzeichen!
Hallo, meiner Meinung nach solltest du dein Programm ein bischen abaendern. 1. Nur ein Flag in der ISR setzen und in der Main abfragen z.B.: Pseudo AVR GCC Code
1 | #define TRANSMIT 0
|
2 | volatile u8 TRANSMIT=0; |
3 | |
4 | ISR(xxx) |
5 | {
|
6 | status_flagreg |= (1<<TRANSMIT); |
7 | }
|
8 | |
9 | |
10 | int main(void) |
11 | {
|
12 | for(;;){ |
13 | if(status_flagreg & (1<<TRANSIMT)){ |
14 | uart0_out("uzibuz"); |
15 | }
|
16 | }
|
17 | }
|
2. In der Codesammlung liegt von Peter Danneger seine Output Routinen für den 8051er. Eine Portierung für AVR GCC hab ich in dem selben Thread hinterlegt. Die Funktion sind sehr schnell und Code sparend. Gruß, Dirk
mhh es muss sofort ohne verzögerung gesendet werden. von daher wär das leider im schlimmsten Fall mit fast 30ms zu spät. die output routine werde ich mal testen...
> mhh es muss sofort ohne verzögerung gesendet werden. von daher wär > das leider im schlimmsten Fall mit fast 30ms zu spät. Bei solch engen Zeit-Vorgaben müsste man sinnvollerweise sowieso das Pgm umbauen: In der Hauptschleife wird gar nicht mehr auf das Ende des ADC gewartet, sondern der wird nur noch angestossen. Wenn ein ADC fertig ist, meldet er sich mit einem Interrupt zurück, woraufhin der Wert ausgelesen wird und für die UART bereit- gelegt wird. Die ganzen double-Rechnereien fallen dem Rotstift zum Opfer und werden durch fixed-Point Arithmetik ersetzt. Die Hauptschleife in der main überwacht nur noch, ob ein ADC nichts zu tun hat und stösst wieder eine Wandlung an. Daanch überprüft sie ob eine Interrupt-Anfrage nach den ADC Werten vorliegt. Wenn ja initiiert sie die Übertragung, die auch wieder Interruptgetrieben von alleine so schnell wie möglich von statten geht. Dadurch kriegst du ein Pseudo-Multitasking, in dem niemand auf jemand anderen warten muss und trotzdem alles so schnell wie möglich abgearbeitet wird (und sich die CPU immer noch 95% ihrer Zeit langweilen wird).
hy ja das Problem mit dem unterbrechen des ADC hatte ich auch schon im Kopf. leider machten mir nur die Chars Probleme und so folg das cli wieder raus :-) 1. Wenn ich am anfang von For schleife sei() und am ende CLI mache merk er sich dann den zwischendurch ausgelösten IRQ und löste ihn aus oder nur wenn er im "rücksprung" hinter cli und in der For() schleife nach oben ist zu sei springt? also ... for (;;) { sei(); ADMUX = 0; // AD Kanal A.0 .... cli(); } 2. benötigt werden nur zahlen von 0-50 8. die Nachkommastellen sollten auf 2 gerundet werden so das nur XX.XX übergeben werden müssen. das Protokol am PC kann ich ja selber definieren. Kann ich nicht die 2 Vorkommaziffern und 2 Nachkommazifffern jeweils in einem 8bit Wert senden und am PC einfach zusammensetzen durch ein Protokoll... also AA senden, BB senden.... -> AA.BB und CC.DD könnte ja die Vorkommazahl durch runden bekommen (->int) und die nach kommastellen durch Zahl *100- int*100.... oder geht das einfacher?
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.