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...
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
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.
> 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
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
voidFormat(doubled,char*String)
2
{
3
intZahl=(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
volatileu8TRANSMIT=0;
3
4
ISR(xxx)
5
{
6
status_flagreg|=(1<<TRANSMIT);
7
}
8
9
10
intmain(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?