Hi Leute,
ich hab eine Frage zum Programmablauf, also mein Programm sieht bislang
so aus.
1 | #include <inttypes.h>
| 2 | #include <stdio.h>
| 3 | #include <stdint.h>
| 4 | #include <stdlib.h>
| 5 | #include <avr/io.h>
| 6 | #include <avr/interrupt.h>
| 7 | #include <avr/signal.h>
| 8 | #include <avr/delay.h>
| 9 | #include <avr/wdt.h>
| 10 |
| 11 | #include "utility.h"
| 12 | #include "eeprom.h"
| 13 | #include "uart.h"
| 14 | #include "schedule.h"
| 15 |
| 16 | #define UART_BAUD_RATE 9600
| 17 |
| 18 | #define CMD_VERSION 0x1000
| 19 | #define CMD_INITIALIZE 0x1001
| 20 | #define CMD_CALIBRATE 0x1002
| 21 |
| 22 | uint8_t LedTestTask( void )
| 23 | {
| 24 | if( bit_is_clear( PORTB, PB1 ) )
| 25 | {
| 26 | sbi( PORTB, PB1 );
| 27 | cbi( DDRB, DDB1 );
| 28 | }
| 29 | else
| 30 | {
| 31 | cbi( DDRB, DDB1 );
| 32 | cbi( PORTB, PB1 );
| 33 | }
| 34 |
| 35 | return 0;
| 36 | }
| 37 |
| 38 | int Initialize( void )
| 39 | {
| 40 | uint8_t uiStatusRegister = SREG;
| 41 |
| 42 | cli();
| 43 |
| 44 | wdt_enable( WDTO_1S );
| 45 |
| 46 | uart_init( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) );
| 47 |
| 48 | schedule_init();
| 49 |
| 50 | schedule_add( 100, 0, LedTestTask );
| 51 |
| 52 | sbi( PORTD, PD7 );
| 53 | cbi( DDRD, DDD7 );
| 54 |
| 55 | fdevopen( uart_putc, NULL, 0 );
| 56 |
| 57 | SREG = uiStatusRegister;
| 58 |
| 59 | return 1;
| 60 | }
| 61 |
| 62 | int main( void )
| 63 | {
| 64 |
| 65 | // Initialize stuff
| 66 | if( Initialize() == 0 )
| 67 | {
| 68 | //do error handling
| 69 | }
| 70 |
| 71 | sei();
| 72 |
| 73 | struct {
| 74 | unsigned bUartCmd:1;
| 75 | unsigned bUartInit:1;
| 76 | unsigned uiUartCmdPos:6;
| 77 | } x;
| 78 |
| 79 | x.bUartCmd = 0;
| 80 | x.bUartInit = 0;
| 81 |
| 82 | char cUartBuffer[64];
| 83 |
| 84 | while( 1 )
| 85 | {
| 86 | wdt_reset();
| 87 |
| 88 | schedule_handle();
| 89 |
| 90 | if( bit_is_clear( PIND, PD7 ) ) // DTR
| 91 | {
| 92 | uint16_t cTmp = uart_getc();
| 93 |
| 94 | if ( ( cTmp & 0xFF00 ) == 0 )
| 95 | {
| 96 | char Char = cTmp & 0xFF;
| 97 |
| 98 | if( x.uiUartCmdPos < ( sizeof( cUartBuffer ) - 1 ) && Char != '\n' )
| 99 | {
| 100 | cUartBuffer[ x.uiUartCmdPos++ ] = Char;
| 101 | }
| 102 | else if( Char == '\n' )
| 103 | {
| 104 | cUartBuffer[ x.uiUartCmdPos ] = 0;
| 105 | x.bUartCmd = 1;
| 106 | }
| 107 | else
| 108 | {
| 109 | x.uiUartCmdPos = 0;
| 110 | }
| 111 | }
| 112 | if( x.bUartCmd )
| 113 | {
| 114 | uint8_t uiBuffer[32];
| 115 | if( atohex( cUartBuffer, uiBuffer ) )
| 116 | {
| 117 | uint16_t wIdentifer = MAKEWORD( uiBuffer[1], uiBuffer[0] );
| 118 |
| 119 | printf( "dwIdentifer: %X\n", wIdentifer);
| 120 |
| 121 | switch( wIdentifer )
| 122 | {
| 123 | case CMD_INITIALIZE:
| 124 | {
| 125 | x.bUartInit = 1;
| 126 | break;
| 127 | }
| 128 | case CMD_VERSION:
| 129 | {
| 130 | if( x.bUartInit == 0 )
| 131 | break;
| 132 |
| 133 | printf("Version: 1.0\n");
| 134 | break;
| 135 | }
| 136 | case CMD_CALIBRATE:
| 137 | {
| 138 | if( x.bUartInit == 0 )
| 139 | break;
| 140 |
| 141 | printf("Calibrate routine\ndon't stop this routine until it's finished!!\n");
| 142 |
| 143 | printf("Press now Button 1 for 5 seconds\n");
| 144 |
| 145 | }
| 146 | }
| 147 | }
| 148 |
| 149 | x.bUartCmd = 0;
| 150 | x.uiUartCmdPos = 0;
| 151 | }
| 152 | }
| 153 | else
| 154 | {
| 155 | while( ( uart_getc() & 0xFF00 ) == 0 )
| 156 | {};
| 157 | x.bUartInit = 0;
| 158 | }
| 159 | }
| 160 |
| 161 | return 0;
| 162 | };
|
Da ich bislang auf Windows programmeirt habe, würde ich nun einfach ein
Thread für die Kalibrierung starten und das Ergebnis vom Thread in der
Haupschleife abfragen, jedoch gibt es keine Threads auf dem AVR AtMega8.
Also weiss ich nicht ganz wie ich nun vorgehen soll, ich hab zwei
ansätze, zunächst einmal eine globale Variable die eine Aktion
beinhaltet, die dann in der Haupschleife ausgefürt wird.
1 | uint8_t uiAction;
| 2 | uint8_t uiActionRetn;
| 3 |
| 4 | #define AC_CALIBRATE 1
| 5 | #define AC_TEST 2
| 6 |
| 7 | - MainLoop
| 8 |
| 9 | switch( uiAction )
| 10 | {
| 11 | case AC_CALIBRATE:
| 12 | {
| 13 | // calibrate stuff
| 14 | uiActionRetn = 1;
| 15 | uiAction = 0;
| 16 | break;
| 17 | }
| 18 | }
|
der Nachteil dabei ist aber, das ich immer 16 Byte vom Ram dafür
benutze.
Der andere Weg sieht noch schlechter aus, einfach per extra Variable für
jedes Ereignis.
Ich hoffe hier habt da gute ideen.
mfg
Tim
hi leute,
mach ich irgendetwas falsch? ich hab bislang zwei threads eröffnet und
bislang weder auf diesen noch auf den anderen eine antwort erhalten.
gibt es hier niemanden der für meine probleme eine lösung kennt? was ich
ehr weniger glaube, oder stell ich die fragen einfach falsch? wenn ja,
was mach ich denn bitte falsch?
:/
Ich denke dein Hauptproblem ist, dass du versuchst den AVR
zu sehr mit Desktopmethoden zu programmieren.
> jedoch gibt es keine Threads auf dem AVR AtMega8
Schon richtig.
Aber es gibt Timer!
So ein Timer kann dir eine Interrupt Funktion in regelmässigen
Zeitabständen aufrufen. In dieser Interruptfunktion verteilst
du dann die Rechenzeit in kleinen Happen auf die Funktionalität
die Rechenzeit braucht. Kurz ist hier Trumpf.
Ich sehe in deiner Hauptschleife, dass du von der UART
einliest und die Eingabe zusammenbaust. Auch dass kann man
in eine Interruptfunktion verlagern. Wenn ein Zeichen am UART
eintrifft, löst der AVR einen Interrupt aus. Die ISR holt
dann das Zeichen, stellt es in einen Buffer und stellt noch
fest ob damit ('\n') ein Kommando vollständig ist.
Auf einem AVR sind Interrupts und die damit zusammenhängenden
ISR deine Tasks. Na ja. Fast.
> würde ich nun einfach ein Thread für die Kalibrierung
Welche Kalibrierung?
Du schmeisst einen Haufen Code her und auf einmal kommt hier
aus dem Nichts eine Kalibrierung ins Spiel.
> was mach ich denn bitte falsch?
Die Krux ist, dass man ohne massiven Programmumbau nicht wirklich
weiterkommt.
PS: An deiner atohex() Funktion knoble ich immer noch.
Die erscheint mir reichlich kompliziert. Ich konnte aber aus
dem Code noch nicht alle Nebenbedingungen ersehen, die deine
Funktion einhält, daher hab ich noch keinen Ersatz dafür :-)
Karl heinz Buchegger wrote:
> Ich denke dein Hauptproblem ist, dass du versuchst den AVR
> zu sehr mit Desktopmethoden zu programmieren.
Nein eben nicht, das ist mein Problem im Kopf :P und genau deswegen bin
ich hier und Frag euch "Cracks" um Rat, damit mir das nicht passiert :)
> Schon richtig.
> Aber es gibt Timer!
> So ein Timer kann dir eine Interrupt Funktion in regelmässigen
> Zeitabständen aufrufen. In dieser Interruptfunktion verteilst
> du dann die Rechenzeit in kleinen Happen auf die Funktionalität
> die Rechenzeit braucht. Kurz ist hier Trumpf.
In Initialize:
> 1 | schedule_init();
| 2 | > schedule_add( 100, 0, LedTestTask );
|
und in der Main-Loop
>
somit hab ich da schon eine Alternative zu den Threads gefunden und auch
schon umgesetzt.
>
> Ich sehe in deiner Hauptschleife, dass du von der UART
> einliest und die Eingabe zusammenbaust. Auch dass kann man
> in eine Interruptfunktion verlagern. Wenn ein Zeichen am UART
> eintrifft, löst der AVR einen Interrupt aus. Die ISR holt
> dann das Zeichen, stellt es in einen Buffer und stellt noch
> fest ob damit ('\n') ein Kommando vollständig ist.
Die uart_getc Funktion ließt nur den Recvbuffer des UART aus, die
verwaltung vom UART läuft schon in Interrupts.
>> würde ich nun einfach ein Thread für die Kalibrierung
>
> Welche Kalibrierung?
> Du schmeisst einen Haufen Code her und auf einmal kommt hier
> aus dem Nichts eine Kalibrierung ins Spiel.
>
>> was mach ich denn bitte falsch?
>
> Die Krux ist, dass man ohne massiven Programmumbau nicht wirklich
> weiterkommt.
Die Kalibrierung war nur ein Musterbeispiel für Funktionen bzw.
Prozeduren die über einen gewissen Zeitraum ausgeführt werden müssen und
anschließend nicht wieder, wie zum Beispiel bei einer Kalibierung von
einem Tastendruck.
mfg
Tim
> somit hab ich da schon eine Alternative zu den Threads gefunden und auch
> schon umgesetzt.
Uns aber nicht gezeigt.
Ich kann nicht riechen wie dein Scheduler funktioniert.
Wenn sich der an einen Timer klinkt, dann ist doch alles
in Ordnung. Was willst du dann von uns?
> Die Kalibrierung war nur ein Musterbeispiel für Funktionen bzw.
> Prozeduren die über einen gewissen Zeitraum ausgeführt werden müssen und
> anschließend nicht wieder, wie zum Beispiel bei einer Kalibierung von
> einem Tastendruck.
das ist mein Problem, eine Funktion über eine gewisse Zeit immer wieder
aufzurufen, ausgelöst von einem Kommando das über UART übermittel wurde
und vorallem wo ich die Werte zwischenspeichern sollte, ansich ja im
SRAM nur wie mach ich das aus einer Funktion heraus ohne globale
Variable, denn dann hab ich den Platz im RAM ja immer in Benutzung.
Vielleicht mit einer kombination aus Flash/EEPROM und und SRAM?
Tim
Was hast du für ein Problem damit, eine Variable dauernd im RAM zu
lassen? Viel anders geht's bei einem µC gar nicht, wenn du da mit
dynamischer Speicherverwaltung anfangen willst, verbrauchst du unnötig
Performance. Du machst dir einen Puffer (Länge = längstmögliches
Kommando) und schreibst da jeweils deine Daten vom UART rein. Sobald du
da ein vollständiges Kommando drin hast führst du die entsprechende
Funktion aus. Wo ist das Problem?
Philipp Burch wrote:
> Was hast du für ein Problem damit, eine Variable dauernd im RAM zu
> lassen? Viel anders geht's bei einem µC gar nicht, wenn du da mit
> dynamischer Speicherverwaltung anfangen willst, verbrauchst du unnötig
> Performance. Du machst dir einen Puffer (Länge = längstmögliches
> Kommando) und schreibst da jeweils deine Daten vom UART rein. Sobald du
> da ein vollständiges Kommando drin hast führst du die entsprechende
> Funktion aus. Wo ist das Problem?
das mach ich ja, wie oben im Code zu sehen sowieso, mein Problem besteht
darin, wenn ich diese gewisse Funktion mehrmals in gewissen Abständen
aufrufen muss, z.b. bei einer Kalibrierung eines Knopfes/Tasters
(Widerstandscodiert) und mit anschließender Speicherung des
Mittelwertes.
Da fällt mir gerade das attribut "static" ein, wie werden static
Variablen in µC verwaltet?
> Da fällt mir gerade das attribut "static" ein, wie werden static
> Variablen in µC verwaltet?
Ganz genau so wie sonst auch:
Im Prinzip ist das eine globale Variable, deren Sichtbarkeit
eingeschränkt ist und für die andere Initialisierungsregeln
gelten. Ein Compiler hat da nicht viele Möglichkeiten das
anders zu implementieren.
Tim S. wrote:
> Philipp Burch wrote:
>> Was hast du für ein Problem damit, eine Variable dauernd im RAM zu
>> lassen? Viel anders geht's bei einem µC gar nicht, wenn du da mit
>> dynamischer Speicherverwaltung anfangen willst, verbrauchst du unnötig
>> Performance. Du machst dir einen Puffer (Länge = längstmögliches
>> Kommando) und schreibst da jeweils deine Daten vom UART rein. Sobald du
>> da ein vollständiges Kommando drin hast führst du die entsprechende
>> Funktion aus. Wo ist das Problem?
>
> das mach ich ja, wie oben im Code zu sehen sowieso, mein Problem besteht
> darin, wenn ich diese gewisse Funktion mehrmals in gewissen Abständen
> aufrufen muss, z.b. bei einer Kalibrierung eines Knopfes/Tasters
> (Widerstandscodiert) und mit anschließender Speicherung des
> Mittelwertes.
Ja, und? Wo ist denn dein Problem dabei?
> Da fällt mir gerade das attribut "static" ein, wie werden static
> Variablen in µC verwaltet?
Soweit ich weiss werden auch die dauerhaft im RAM gehalten.
> wenn ich diese gewisse Funktion mehrmals in gewissen Abständen
> aufrufen muss, z.b. bei einer Kalibrierung eines Knopfes/Tasters
> (Widerstandscodiert) und mit anschließender Speicherung des
> Mittelwertes.
Ich hab dich immer noch nicht.
Ein Timer löst in regelmässigen Abständen einen Interrupt
aus. Der Interrupt sorgt dafür das in der Hauptschleife
die Kalibrierung durchgeführt wird, die wiederum in einer
globalen Variablen abgelegt wird.
Gewöhn dich daran, dass du in einem µC viel mehr mit
globalen Variablen arbeiten musst als du es bisher gewohnt
bist.
Wieder als Skizze:
volatile uint8_t DoKalibrate;
volatile uint8_t Kalibrierung;
uint8_t KalibrateTime;
ISR Timer_Overflow()
{
if( KalibrateTime++ == TIME_TO_KALIBRATE ) {
KalibrateTime = 0;
DoKalibrate = 1;
}
}
int main()
{
Timer aufsetzen;
UART aufsetzen;
while( 1 ) {
if( Kommando_vom_Uart ) {
if( strcmp( Kommando, "AUTO CALIBRATE" ) == 0 ) {
Timer einschalten;
}
if( strcmp( Kommando, "STOP CALIBRATE" ) == 0 ) {
Timer aussschalten;
}
}
if( DoKalibrate ) {
Kalibrierung nachrechnen;
DoKalibrate = 0;
}
}
}
Die Struktur ist simpel: Im main() gibt es die Hauptschleife.
Jeder "Task" wird in der Hauptschleife behandelt, wenn irgend
ein anderer Programmteil ein entsprechendes Bit dafür gesetzt
hat. "Ein anderer Programmtail" kann die Hauptschleife selbst
sein oder ein ISR, der zb auf die Art signalisiert, dass über
die UART ein Kommando eingelangt ist oder dass die Zeit zum
Kalibrieren gekommen ist.
Das Schema hat sich bewährt und ist meist ein guter Anfangspunkt.
hm jo ich glaub ich muss mich langsam daran gewöhnen, bislang hab ich
dafür immer dynamischen speicher verwendet ^^
naja ich werd mich mal dran machen, danke leute :)
Tim S. wrote:
> mach ich irgendetwas falsch? ich hab bislang zwei threads eröffnet und
> bislang weder auf diesen noch auf den anderen eine antwort erhalten.
Was erwartest Du ?
Das wir sofort alles fallen lassen und Deinen Code analysieren ?
Immerhin ist nichts kommentiert und übersetzen kann es auch keiner, da
haufenweise Funktionen fehlen.
Und Code >20 Zeilen gehört in den Anhang, wie soll man sonst Threads
lesen ohne Krampf im Scroll-Finger.
So ein Scheduler ist schon ne feine Sache:
Beitrag "Wartezeiten effektiv (Scheduler)"
Ich benutze ihn z.B. um alle 200ms die Anzeige aufzufrischen und
sämtliche anderen Zeitdauern.
Bloß mußt Du die Aufrufe kommentieren oder den Code mitgeben.
Auch muß der Handler ja in einem bestimmten Zeitintervall aufgerufen
werden und nicht mit maximaler Mainloop-Geschwindigkeit. Es sollen ja
ganz bestimmte Zeiten ablaufen.
Wenn Variablen über die ganze Laufzeit benötigt werden, müssen sie
global oder static sein.
Ein malloc() bringt hier nichts, da ja kein free() kommt. Du hast nur
eine Anwendung und die läuft, bis der Saft weg ist.
Und 8Bit (unsigned char) sind wesentlich effektiver als 16Bit (unsigned
int) aufm 8Bitter.
Das spart Rechenzeit und RAM.
Peter
P.S.:
Also kommentiert, compilierbar und als Anhang und schon hast Du
tausendmal bessere Chancen auf eine Antwort.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|