mikrocontroller.net

Forum: Compiler & IDEs Programmablauf


Autor: Tim S. (suxx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi Leute,

ich hab eine Frage zum Programmablauf, also mein Programm sieht bislang 
so aus.
#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <avr/delay.h>
#include <avr/wdt.h>

#include "utility.h"
#include "eeprom.h"
#include "uart.h"
#include "schedule.h"

#define UART_BAUD_RATE 9600

#define CMD_VERSION  0x1000
#define CMD_INITIALIZE  0x1001
#define CMD_CALIBRATE  0x1002

uint8_t LedTestTask( void )
{
  if( bit_is_clear( PORTB, PB1 ) )
  {
    sbi( PORTB, PB1 );
    cbi( DDRB, DDB1 );
  }
  else
  {
    cbi( DDRB, DDB1 );
    cbi( PORTB, PB1 );
  }

  return 0;
}  

int Initialize( void )
{
  uint8_t uiStatusRegister = SREG;

  cli();

  wdt_enable( WDTO_1S );

  uart_init( UART_UBRR_CALC( UART_BAUD_RATE, F_CPU ) );
  
  schedule_init();

  schedule_add( 100, 0, LedTestTask );
  
  sbi( PORTD, PD7 );
  cbi( DDRD, DDD7 );

  fdevopen( uart_putc, NULL, 0 );

  SREG = uiStatusRegister;

  return 1;
}

int main( void )
{

  // Initialize stuff
  if( Initialize() == 0 )
  {
    //do error handling
  }

  sei();
  
  struct {
    unsigned bUartCmd:1;
    unsigned bUartInit:1;
    unsigned uiUartCmdPos:6;
  } x;
  
  x.bUartCmd  = 0;
  x.bUartInit  = 0;

  char cUartBuffer[64];

  while( 1 )
  {
    wdt_reset();
  
    schedule_handle();

    if( bit_is_clear( PIND, PD7 ) ) // DTR
    {    
      uint16_t cTmp = uart_getc();
      
      if ( ( cTmp & 0xFF00 ) == 0 )
      {
        char Char = cTmp & 0xFF;

        if( x.uiUartCmdPos < ( sizeof( cUartBuffer ) - 1 ) && Char != '\n' )
        {
          cUartBuffer[ x.uiUartCmdPos++ ]  = Char;
        }
        else if( Char == '\n' )
        {
          cUartBuffer[ x.uiUartCmdPos ] = 0;
          x.bUartCmd = 1;
        }
        else
        {
          x.uiUartCmdPos = 0;
        }
      }
      if( x.bUartCmd )
      {
        uint8_t uiBuffer[32];
        if( atohex( cUartBuffer, uiBuffer ) )
        {
          uint16_t wIdentifer = MAKEWORD( uiBuffer[1], uiBuffer[0] );

          printf( "dwIdentifer: %X\n", wIdentifer);

          switch( wIdentifer )
          { 
            case CMD_INITIALIZE:
            {
              x.bUartInit = 1;
              break;
            }
            case CMD_VERSION:
            {
              if( x.bUartInit == 0 )
                break;

              printf("Version: 1.0\n");
              break;
            }
            case CMD_CALIBRATE:
            {
              if( x.bUartInit == 0 )
                break;

              printf("Calibrate routine\ndon't stop this routine until it's finished!!\n");

              printf("Press now Button 1 for 5 seconds\n");

            }
          }
        }

        x.bUartCmd  = 0;
        x.uiUartCmdPos  = 0;
      }
    }
    else
    {
      while( ( uart_getc() & 0xFF00 ) == 0 )
      {};
      x.bUartInit = 0;
    }
  }

  return 0;
};

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.
uint8_t uiAction;
uint8_t uiActionRetn;

#define AC_CALIBRATE  1
#define AC_TEST    2

- MainLoop

switch( uiAction )
{ 
  case AC_CALIBRATE:
  {
    // calibrate stuff
    uiActionRetn  = 1;
    uiAction  = 0; 
    break;
  }
}

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

Autor: Tim S. (suxx)
Datum:

Bewertung
0 lesenswert
nicht 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?


:/

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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 :-)

Autor: Tim S. (suxx)
Datum:

Bewertung
0 lesenswert
nicht 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:
>
schedule_init();
> schedule_add( 100, 0, LedTestTask );
und in der Main-Loop
>
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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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?


Autor: Tim S. (suxx)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Philipp Burch (philipp_burch)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Tim S. (suxx)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Philipp Burch (philipp_burch)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Tim S. (suxx)
Datum:

Bewertung
0 lesenswert
nicht 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 :)

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.