Forum: Compiler & IDEs Programmablauf


von Tim S. (suxx)


Lesenswert?

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

von Tim S. (suxx)


Lesenswert?

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?


:/

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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 :-)

von Tim S. (suxx)


Lesenswert?

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
>
1
schedule_handle();

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

von Karl H. (kbuchegg)


Lesenswert?

> 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?


von Tim S. (suxx)


Lesenswert?

> 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

von Philipp B. (philipp_burch)


Lesenswert?

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?

von Tim S. (suxx)


Lesenswert?

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?

von Karl H. (kbuchegg)


Lesenswert?

> 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.

von Philipp B. (philipp_burch)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

> 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.

von Tim S. (suxx)


Lesenswert?

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 :)

von Peter D. (peda)


Lesenswert?

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.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.