www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Siebensegment Multiplexen in C


Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen ,

möchte zwei siebensegmentanzeigen über einen atmel8 gemultiplext 
ansteuern,
habe ich soweit auch schon mehr oder weniger hingekriegt mit 
untenstehenden
Code, allerdings gibt es Probleme wenn sich die Zahl die ausgegeben 
werden soll sehr schnell ändert, dann flackert die anzeige.

Habe leider kein Beispielcode in C gefunden, nur in assembler im 
Tutorial, allerdings, krieg ich es nicht auf die reihe, den in c 
umzuschreiben, wäre schön wenn mir jemand erklären würde wie ich 
vorgehen muss wenn ich siebensegmentanzeigen gemultiplext in c ansteuern 
will.

mfg crashdemon
PORTC = DIGIT_1; // Einerstellen einschalten  
PrintNumber(number1); // Einerstelle ausgeben
_delay_ms(FREQ*4); // 2ms Warten
PORTC = DIGIT_OFF; // SegmentAnzeige komplett ausschalten

PrintNumber(number10); // Zehnerstelle ausgeben
PORTC = DIGIT_10; // Zehnerstelle einschalten
_delay_ms(FREQ*4); // 2ms Warten
PORTC = DIGIT_OFF; // SegmentAnzeige komplett ausschalten

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Crashdemon wrote:
> Hallo zusammen ,
>
> möchte zwei siebensegmentanzeigen über einen atmel8 gemultiplext
> ansteuern,
> habe ich soweit auch schon mehr oder weniger hingekriegt mit
> untenstehenden
> Code, allerdings gibt es Probleme wenn sich die Zahl die ausgegeben
> werden soll sehr schnell ändert, dann flackert die anzeige.
>
> Habe leider kein Beispielcode in C gefunden

Z.B.:

Beitrag "ADC mit Multiplexanzeige"


Peter

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Naja hätte vllt. nicht schreiben sollen keinen Beispielcode gefunden, 
das Beispiel hatte ich schon gefunden allerdings habe ich ein Problem 
mit in den Code hineinzudenken, aber ich schau es mir nochmal an.

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab es auch ein wenig nders gemacht und würde das auch gerne so 
beibehalten nur halt mit einer ordentlichen multiplex funktion, hier 
noch meine komplette multiplex funktion.
char digit[10] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90};

void PrintNumber(int number)
{
  if(number < 0) // Wenn Zahl Negativ
  {
    number = number * (-1); // Wenn Zahl negativ, durch multipliziren positiv machen
  }
 
  if(number >= 100)
  {
    _error(); // Ist ein Fehler aufgetretet Error-Funktion starten
  }

  else
  {
    if(number < 10)
    {  
      // Wenn Zahl einstellig
      PORTC = DIGIT_1; // Einerstelle einschalten
      PORTD = digit[number]; // Hex-Wert aus Array holen  
    }

    if(number > 9)
    {  
      // Wenn Zahl zweistellig
      uint8_t number10; // Bei Nummern größer neun, Zehner
      uint8_t number1; // Bei Nummern größer neun, Einer

      number10 = number / 10; // Zehnerstelle ermitteln, durch zehn dividieren 
      number1 = number - (number10 * 10); // Einerstelle ermitteln

      // Multiplex 
      PORTC = DIGIT_1; // Einerstellen einschalten  
      PrintNumber(number1); // Einerstelle ausgeben
      _delay_ms(FREQ*4); // 2ms Warten
      PORTC = DIGIT_OFF; // SegmentAnzeige komplett ausschalten
      PrintNumber(number10); // Zehnerstelle ausgeben
      PORTC = DIGIT_10; // Zehnerstelle einschalten
      _delay_ms(FREQ*4); // 2ms Warten
      PORTC = DIGIT_OFF; // SegmentAnzeige komplett ausschalten
    }
  }
}


Autor: crazy horse (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Komplett falscher Ansatz.
Lass einen Timerinterrupt laufen (1,2 oder 5ms).
-alle digits löschen
-statische Variable hochgezählen, je nach Zählerzustand das passende 
Bitmuster
ausgeben
-entsprechendes Digit einschalten.

Dazu gibts ein Array, wo die anzuzeigenden Daten drinstehen. Das machst 
du aus einem beliebigen Programmteil heraus, die Interruptroutine 
schaufelt nur stur die Bits raus.

Autor: crappythumb (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was Du brauchst ist ein (in Deinem Fall) zweistelliges Array, wo immer 
die aktuellen Ziffern (stellenweise) gespeichert sind.

Dann brauchst Du eine Routine, die periodisch aus diesem Array die 
Zifferninformationen auf den Port umlegt. Am besten machst Du das 
interrupt-gesteuert. Dann läuft das praktisch autonom und Du brauchst 
nur noch in dem normalen Programmablauf Deine Zifferninformationen in 
das Array zu schreiben.

Das erspart Dir nerviges Synchronisieren, was letztlich wahrscheinlich 
Dein Problem (mit den flackenden Ziffern) darstellt.
Vorteil bei dieser Interruptsteuerung: Man kann auch relativ bequem eine 
Dimmfunktion einbauen. Dabei benutzt man zB den T2-Overflow und den 
Comparematch2. Beim Overflow setzt man die jeweils neue (!) Ziffer aus 
dem Array auf die entsprechende 7-Seg-Anzeige/Port um und beim 
Comparematch löscht man alle (!) Ziffern/ den entsprechenden Port. Dann 
kannst Du über das Outputcompare-Register die Pulsweite (=Helligkeit) 
einstellen. Wenn Du ein Beispiel dessen willst, schick mir eine Email - 
nickname von oben und ein @gmx.de anhängen.

Keine Ahnung, wie Peter das in seinem Beispiel gelöst hat, 
möglicherweise ja sogar genauso - ich hab's mir nicht durchgelesen.

Autor: crappythumb (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@verrücktes pferd...

bad timing...aber doppelt hält besser ;)

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

Bewertung
0 lesenswert
nicht lesenswert
Das ADC Beispiel ist wirklich nicht so besonders gut geeignet
um das Prinzip des Multiplexing isoliert zu verstehen. Die
eigentliche Multiplex-Funktionalität ist in die ADC-ISR
hineinverwoben. Ich kann mir schon vorstellen, dass jemand
der nicht weiss, worauf er achten soll, die nicht findet.
Dazu kommt, dass PeDa hier auch noch darauf achten muss,
den Port während der ADC Messung ruhig zu halten (das ist dann
auch der Grund warum das Multiplexing erfolgt, nachdem der ADC
eine Messung abgeschlossen hat und seine ISR ausführt).

Das Prinzip, wie man sowas im Allgemeinen macht wurde ja schon
angesprochen und zusammen mit der Erläuterung von Multiplexing
im Tutorial sollte es kein Problem mehr sein, sowas umzusetzen.
Nicht den Assembler Code umschreiben. Das hat wenig bis keinen
Sinn, da du dich in Assembler Details verlierst. Aber das Prinzip
aus dem Assembler Tutorial kann 1:1 übernommen werden (und ist
genau das, welches in diesem Fred schon angesprochen wurde)

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
danke für die vielen antworten,

ich fasse jetzt mal zusammen wie ich denke vorgehen zu sollen.

- timer basteln der in regelmäßigen abständen einen interrupt erzeugt
- die momentan erleuchtete Anzeige abgeschaltet (kling plausibel)
- das Muster für die nächste Anzeige am Port D ausgegeben
- die nächste Anzeige durch eine entsprechende Ausgabe am Port C 
eingeschaltet

ich würde allerdings gerne mein array behalten und einer und 
zehnerstellen getrennt voneinander ermitteln sprich
23 = (23 / 10) = 2 = zehnerstelle (kein rest, da integer)
23 = 23 - (zehnerstelle(2) * 10 ) = 3 = einerstelle 

würde das auch gehen oder muss es umbedingt mit bitmasken gemacht werden

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

Bewertung
0 lesenswert
nicht lesenswert
Crashdemon wrote:
> danke für die vielen antworten,
>
> ich fasse jetzt mal zusammen wie ich denke vorgehen zu sollen.
>
> - timer basteln der in regelmäßigen abständen einen interrupt erzeugt
> - die momentan erleuchtete Anzeige abgeschaltet (kling plausibel)
> - das Muster für die nächste Anzeige am Port D ausgegeben
> - die nächste Anzeige durch eine entsprechende Ausgabe am Port C
> eingeschaltet

Ganz genau.
Der springende Punkt:
  Bei einem Aufruf der ISR wird nur zur nächsten Anzeige
  umgeschaltet und die Ausgabe für diese Anzeigenstelle gemacht.
  Nicht mehr.
  Da die ISR aber sehr oft aufgerufen wird, bekommst du das
  Umschalten nicht mehr mit und siehst beide Anzeigestellen
  leuchten.

> ich würde allerdings gerne mein array behalten und einer und
> zehnerstellen getrennt voneinander ermitteln sprich

Kannst du ja.
Ganz im Gegenteil: sollst du auch.

Du legst dir ein Array zurecht. In deinem Fall ist es ein
Array mit 2 Elementen. Nennen wir es mal Anzeige
In Anzeige[0] speicherst du das auszugebende Bitmuster für
die 'Hunderterstelle', in Array[1] speicherst du das
auszugebende Bitmuster für die 'Einerstelle'.
Die ISR sorgt dann dafür, dass diese Bitmuster abwechselnd auf
den beiden 7-Segment (natürlich auf den richtigen) ausgegeben
werden.

Dann brauchst du noch eine Funktion, der du eine Zahl
übergibst, und die aus dieser Zahl die den Ziffern
entsprechenden Bitmuster an die richtigen Positionen im
Array schreibt.

Und das wars dann auch schon.

Die ISR zeigt an, was auch immer sie im Array vorfindet.
Und wenn du eine Zahl ausgeben willst, dann rufst du
einfach diese Ausgabefunktion auf, die das Array entsprechend
befüllt. Die grundlegende Funktionalität, nämlich das Zerlegen
einer Zahl und das bestimmen des zugehörigen Bitmusters
hast du ja in deiner Funktion schon gemacht. Das ist also
nichts neues. Aber anders als in deiner Funktion wird nichts
ausgegeben, sondern die Bitmuster lediglich an die richtige
Stelle des Arrays geschrieben, von wo sich die ISR dann diese
Werte holt und die tatsächliche Ausgabe macht.

Das ganze sollte nicht wesentlich mehr als 10 bis 15 Zeilen
Code sein.

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So ganz komme ich mit den Interrupts noch nicht klar, habe das Problem 
sobald ich einmal in den Zweistelligen Bereich kommen bei der 
Siebensegmentanzeige wird der Interrupt danach ständig aufgerufen auch 
wenn ich nur eine einstellige zahl ausgeben will, hab hier anbei mal 
meine funktion und isr mit den wichtigen variablen gapackt, wäre nett 
wenn da mal einer drüber schauen könnte.
volatile char digit[10] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90}; // Bitmaske für die Zahlen
volatile char display[2] = {0x10, 0x08}; // Siebensegmentanzeige (Einer- , Zehnerstelle)

uint8_t DIGIT1; // Wert der Einerstelle
uint8_t DIGIT2; // Wert der Zehnerstelle
volatile int counter = 1; // Anzeige die Angeschaltet werden soll


SIGNAL(SIG_OVERFLOW0) // Aufruf bei Interrupt von Timer0 (8 Bit)
{
  PORTC |= 0xFF; // Alle Siebensegmentanzeigen ausschalten
  PORTC |= display[counter]; // Anzeige einschalten (counter == 1 -> Einerstelle | counter == 2 -> Zehnerstelle)

  for(counter = 1; counter < 2; counter++) // Hochzählen bis 2
  {
    PORTC = display[counter]; // Anzeige einschalten
    
    if(counter == 1) // Wenn Einstellig Anzeige an, Einerwert ausgeben
    {
      PORTD = digit[DIGIT1]; // Einerstelle an Seg. Anzeige ausgeben    
    }

    if(counter == 2) // Wenn Zweistellig Anzeige an, Zehnerwert ausgeben
    {
      PORTD = digit[DIGIT2]; // Einerstelle an Seg. Anzeige ausgeben
      counter = 1; // Anzeige wieder auf Einerstellen umschalten
    }
  }
   
  TIFR = (1 << TOV0); // Overflow INterrupt wieder zurücksetzen
  cli(); // Globale Interrupts auschalten
}


void PrintNumber(int number)
{
  cli(); // Global Interrupt Disabled

  if(number < 0) // Wenn Zahl Negativ
  {
    number = number * (-1); // Wenn Zahl negativ, durch multipliziren positiv machen
  }
 
  if(number >= 100)
  {
    _error(); // Ist ein Fehler aufgetretet Error-Funktion starten
  }

  else
  {
    if(number < 10)
    {  
      // Wenn Zahl einstellig
      PORTC = DIGIT_1; // Einerstelle einschalten
      PORTD = digit[number]; // Hex-Wert aus Array holen  
    }

    if(number > 9)
    {  
      sei(); // Global Interrupt Enabled

      // Wenn Zahl zweistellig
      uint8_t number10; // Bei Nummern größer neun, Zehner
      uint8_t number1; // Bei Nummern größer neun, Einer

      number10 = number / 10; // Zehnerstelle ermitteln, durch zehn dividieren 
      number1 = number - (number10 * 10); // Einerstelle ermitteln

      TCCR0 = (1 << CS02); // Timer einschalten mit einen Teiler von 256
      TIMSK = (1 << TOIE0); // Overflow Interrupt einschalten

      number10 = DIGIT2; // In globale Variable verpacken, damit diese in ISR benutzt werden kann
      number1  = DIGIT1; // In globale Variable verpacken, damit diese in ISR benutzt werden kann

      // Multiplex 
      /*
      PORTC = DIGIT_1; // Einerstellen einschalten
        
      //PrintNumber(number1); // Einerstelle ausgeben
      PORTD = digit[number1];
      // **************************

      _delay_ms(FREQ*4); // 2ms Warten
      PORTC = DIGIT_OFF; // SegmentAnzeige komplett ausschalten

      //PrintNumber(number10); // Zehnerstelle ausgeben
      PORTD = digit[number10];
      // **************************

      PORTC = DIGIT_10; // Zehnerstelle einschalten
      _delay_ms(FREQ*4); // 2ms Warten
      PORTC = DIGIT_OFF; // SegmentAnzeige komplett ausschalten
      */
    }
  }
}

Autor: Gabriel Wegscheider (gagosoft)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
interrupr Serviceroutinen schrieb man früher *SIGNAL()* jetzt wird 
*ISR()* verwendet, ein paar Versionen vom GCC wird SIGNAL noch 
funktionieren, doh ist als depricated markiert.

TIMSK und TCCR0 musst Du einmal bei der initialisierung setzten, dann 
bleibt das eh im ConfigRgister stehen.

bei jedem Aufruf der ISR solltest Du EINEN Wert ausgeben und den Zähler 
erhöhen, beim NÄCHSTEN Aufruf kommt dann das nächste Digit dran.
Wenn Du PORTC 0xFF haben willst nimm statt PORTC |= 0xFF PORTC = oxFF, 
da sparst Du Dir das lesen des Ports, ausserdem reicht eine einfache 
Zuweisung
PORTC = display[counter]; (Dir ist ja egal was vorher auf PORTCV war, es 
soll lediglich display[counter] ausgegeben werden (wieder ein unnötiges 
lesen gespart - und mehr lesbarkeit in den Code gebracht)

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Crashdemon wrote:
> So ganz komme ich mit den Interrupts noch nicht klar, habe das Problem
> sobald ich einmal in den Zweistelligen Bereich kommen bei der
> Siebensegmentanzeige wird der Interrupt danach ständig aufgerufen auch
> wenn ich nur eine einstellige zahl ausgeben will

Dem Multiplexen ists wurscht, ob die Zahl 2-, 1- oder 0-stellig ist, 
gemultiplext wird immer.
Sonst würde Deine Zahl ja je nach Stellenzahl unterschiedlich hell 
leuchten.

Du hast 2 Bytes für die Segmentmuster der beiden Anzeigen und der 
Interrupt gibt diese Bytes abwechselnd aus mit dem zugehörigen 
Stellenbit eingeschaltet.



Peter

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke schonmal für die schnellen antworten, ja hab ich mit signal, jetzt 
in ISR(TIMER0_OVF0_vect)  geändert, werde die die isr jetzt nochmal 
überarbeiten und dann schaune wir mal obs funzt.

Autor: Thorsten (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielleicht verstehe ich dich falsch, aber ich habe den Eindruck du 
willst die ISR nur aufrufen, wenn du zwei Stellen auszugeben hast. Wenn 
nur eine ausgegeben werden soll, dann willst du die ISR anscheinend 
nicht nutzen.

Du aktivierst in deinem Code den Overflow Interrupt, wenn eine Zahl >10 
ausgegeben werden soll mit:

TIMSK = (1 << TOIE0); // Overflow Interrupt einschalten

Das heißt aber nicht, dass dieser Interrupt wieder deaktiviert wird, 
wenn die ISR einmal durchlaufen wurde. Der Interrupt bleibt trotzdem 
aktiv und wird somit immer wieder ausgeführt. Das ist genau das was du 
beobachtest hast, einmal eine Zahl größer 10 und die Routine wird immer 
wieder aufgerufen.


Ich würde es an deiner Stelle so machen, wie es oben auch schon 
beschrieben wurde:
Nutze die ISR um deine Zahlen auf der 7-Segment-Anzeige auszugeben und 
zwar immer, egal ob nur eine oder beide Stellen ausgegeben werden soll. 
Lass den Timer einfach von Anfang an laufen, so wird die Anzeige 
zyklisch aktualisiert.

In deiner main oder in der PrintNumber-Methode schreibst du einfach 
immer die Variablen DIGIT1 und DIGIT2 wieder neu, aber nicht die Ports 
selbst. Für die Ports ist nur die ISR zuständig. Meiner Meinung nach ist 
das die sauberste Methode für deine Ausgabe, aber ich lass mich da auch 
gerne eines besseren belehren.

Autor: Thorsten (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
das muss natürlich >9 statt >10 heißen

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jo richtig ich wollte nur die zahlen die <9 sind über die ISR ausgeben, 
das ist dann natürlich blöd das die interupt routine nach einmaligen 
aufrufen immer wieder durchlaufen wird, kann man das unterbinden? wenn 
nein das muss ich es wohl so machen wie du es beschrieben hast, indem 
ich einstellige sowie zweistellige zahle per isr ausgebe.

mfg crashdemon

Autor: Thorsten (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich muss mich korrigieren. Ich hab das cli() am Ende deiner ISR 
übersehen, das sollte alle Interrupts deaktivieren, so dass die Routine 
nicht erneut aufgerufen wird. Ich hoffe, ich hab dich nun nicht 
durcheinander gebracht damit.

Trotzdem würde ich dir empfehlen alle Ausgaben über die ISR laufen zu 
lassen. Was spricht denn aus deiner Sicht dagegen das zu machen?

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das cli() scheint bei mir allerdings nicht zu funktionieren da er nicht 
wieder aus der ISR rausspringt?, bin leider in interrupts nicht so fit 
und dachte mir das ich es am besten so leicht wie möglich mache aber ich 
werde da wohl nicht rumkommen, bei über eine routine abzuwickeln weiß 
nur noch nicht wie diese aussehen soll, schade das es im avr-gcc 
toutorial nicht so ein schönes beispiel in c gibt, wie im assembler 
tutorial.

Autor: Lötlackl (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

@Crashdemon

kann Dir noch folgendes Beispiel (von mir)
zum Thema "Anzeige multiplexen" beisteuern: 
Beitrag "mal wieder: UV-Belichtungstimer"

mfg Lötlackl

Autor: Thorsten (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie ich jetzt auch erst lesen musste, darfst du kein cli() innerhalb 
einer ISR nutzen. Du sperrst dich damit anscheinend selbst in der ISR 
ein, da das nötige reti nicht ausgeführt werden kann, weil du den 
Interrupt schon gesperrt hast durch das cli().

Falls du weiterhin bei deiner Idee bleiben willst, nur zweistellige 
Zahlen per ISR auszugeben, ersetze mal das cli() durch:

TIMSK = 0;

Damit müsstest du eigentlich dafür sorgen, dass der Interrupt erst 
wieder aufgerufen werden kann, wenn TIMSK wieder gesetzt wird. Das reti 
sollte in diesem Fall aber ausgeführt werden, so dass er zurück in die 
main springen kann.

Vielleicht klappt das, kannst ja mal ausprobieren und Bericht erstatten.

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hat nicht irgendjemand eine funktionierende Interrupt Routine für zwei 
Siebensegmentanzeigen, mit dem Timer0 (8 Bit) und zusätzlichen 
schnickschack, weil es bei rausschrieben aus fremden Code sehr schwer 
ist die Funktionen nachzuvollziehen, erst recht wenn diese über zieg 
headers verstreut sind, wäre dankbar wenn jemand der schonmal so eine 
routine gebastelt hat diese hier posten könnte.

mfg crashdemon

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Crashdemon wrote:
> Das cli() scheint bei mir allerdings nicht zu funktionieren da er nicht
> wieder aus der ISR rausspringt?

Das CLI im Interrupt kannst Du Dir an die Backe schmieren, es bewirkt 
garnichts.

Die globale Interruptfreigabe ist nur ein Bit und kein Zähler.

Du kannst 1000-mal CLI schreiben, das nächste RETI oder SEI gibt 
trotzdem die Interrupts wieder frei.

Du darfst also ruhig im Interrupt CLI oder NOP schreiben, es macht 
keinen Unterschied (also kannst Du es sein lassen).


Peter

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Crashdemon wrote:
> weil es bei rausschrieben aus fremden Code sehr schwer
> ist die Funktionen nachzuvollziehen,

Hast Du schonmal versucht, dem Autor Fragen zu stellen etwa in der Art, 
"was macht Zeile X in File Y von Beispiel Z"?


> erst recht wenn diese über zieg
> headers verstreut sind

Header enthalten normalerweise keinen Code.


Peter

Autor: Matthias Lipinsky (lippy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
versuch mal das. ist aber trocken programmiert. evtl musst du noch paar 
details (Reg.namen, Ausgabe an PORTC,..) anpassen.
Sollte als Start genügen.
//-- constants -------------------------------------------
#define  c_u8MaxCol  1         // must be 2^n-1
//-- globals ---------------------------------------------
volatile  uint8_t  au8SegMem[c_u8MaxCol+1];
//-- 7seg data -------------------------------------------
char digit[] PROGMEM = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 
                  0x92, 0x82, 0xF8, 0x80, 0x90};

//*********************************************************************
ISR  ( Timer0_Overflow)
{
  //-- declare locals ------------------
  static  uint8_t  _u8Coloumn  = 0x00;
  // to next coloumn -------------------
  u8Coloumn  =  (u8Coloumn + 1) & c_u8MaxCol;
  //-- output this coloumn -------------
  PORTC = (1<<u8Coloumn);
  PORTD = au8SegMem[u8Coloumn];
}
//*********************************************************************
void  showDez ( uint8_t  _u8Value )
{
  //-- lower digit (0..9)---------------
  au8SegMem[0] =  pgm_read_byte(digit[_u8Value%10]);
  //-- next higher digit (00..90) ------
  _u8Value     /= 10;
  au8SegMem[1] =  pgm_read_byte(digit[_u8Value%10]);
}
//*********************************************************************
void  main  ( void )
{
  //-- col & 7Seg to output ------------
  DDRD  = 0xFF;
  DDRC  = 0xFF;
  //-- init T0 -------------------------
  TCCR0  |=  (1<<CS00);      // runs with clk
  TIMSK  |=  (1<<TOIE0);     // enable overfl int
  sei ();
  //-- pre-init 7seg with 91 -----------
  showDez (91);
  //-- endless loop --------------------
  while (1)
  {
     // place some code here
  }
}

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hab den Code ausprobiert und angepasst funzt aber irgendwie nicht, er 
zeigt nichts auf der anzeige an, ich denke ich habe nich ein 
zusätzliches problem was mir bis jetzt noch nicht so aufgefallen ist und 
hatte ich bis jetzt immer schön ein while(1) laufen worin der die werte 
des adc eingelesen hatte und mit in der schleife befand sich der aufruf 
der funktion PrintNumber(ADC_Wert)
wenn ich das jetzt aber über Interupts machen würde würde er die 
funktion immer wieder aufrufen, also wie eine art neuen thread, liege 
ich damit richtig?
while(1)
{
    ReadChannel(0); Messwert des AD-Wandlers 
    PrintNumber(ADC_angle); // Wert ausgeben, ganzzahlig nicht negativ
}  

Autor: Matthias Lipinsky (lippy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
1) Satzzeichen setzen! Sonst versteht man ja nichts.

2) Der Code muss, so wie er ist, erstmal eine 91 auf der ANzeige 
darstellen.
   Solange das nicht passiert, brauchst du auch nicht mit dem ADC 
rumkaspern.
   => Poste mal die komplette Schaltung (µC + 7Seg Anbindung), dann kann
      ich den COde anpassen.
3) Es ist sehr sinnvoll, sich auf Funktionen "ReadChannel(0)" zu 
berufen, die
   andere hier nicht kennen!

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Beschaltung der Siebensegmentanzeige ist analog zu der aus dem 
AVR-Tutorial 
(http://www.mikrocontroller.net/articles/AVR-Tutori...), 
wobei der Transisitor für die Einerstelle an PC3, für die Zehnerstelle 
an PC4 angeschlossen ist.

Die Funktion ReadChannel kann im AVR-GCC Tutorial hier nachgelesen 
werden: 
(http://www.mikrocontroller.net/articles/AVR-GCC-Tu...)

danke im voraus

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

Bewertung
0 lesenswert
nicht lesenswert
> das ist dann natürlich blöd das die interupt routine nach einmaligen
> aufrufen immer wieder durchlaufen wird, kann man das unterbinden?

Das ist überhaupt nicht blöd. Ganz im Gegenteil. Die Funktion
soll immer wieder aufgerufen werden. Denn die ISR-Funktion
sorgt dafür, dass deine Anzeige richtig funktioniert, indem
sie abwechselnd die eine und dann die andere Stelle aktiviert.
Du hast eines immer noch nicht verinnerlicht:
Der eigentliche Anzeigevorgang ist unabhängig von dem Generieren
des Anzuzeigenden!
Das ist in wichtiges Prinzip! Denn du brauchst dich dadurch nicht
mehr um die Anzeige kümmern. Das macht die ISR ganz alleine.
Du schreibst lediglich die auszugebenden Bitmuster in die globalen
Variablen. Mehr hast du nicht zu tun, den Rest macht die ISR,
wenn sie das nächste mal drann ist.

Ich geh mal davon aus, dass deine Lösung von oben die
Hardware richtig anspricht.
Du machst das alles viel zu kompliziert! Ein Aufruf der ISR
gibt eine Steller aus. Beim nächsten Aufruf der ISR wird
die nächste Stelle ausgegeben. Und zwar ständig! Immer und
immer wieder!
unsigned char digit[10] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90}; // Bitmaske für die Zahlen

volatile unsigned char segment[2];
unsigned char display[2] = {0x08, 0x10}; // Steuerleitungen für ein Digit
unsigned char counter; // Anzeige die Angeschaltet werden soll

ISR( TIMER0_OVF_vect ) // Aufruf bei Interrupt von Timer0 (8 Bit)
{
  // Die bisherige Anzeige ausschalten
  PORTC |= 0xFF;

  // welches ist die nächste Stelle
  counter = counter + 1;
  if( counter == 2 )
    counter = 0;

  // Diese Stelle ausgeben
  PORTD = segment[counter];
  PORTC = display[counter];
}

void PrintNumber(int number)
{
  if( number < 0 )
    number = -number;

  segment[0] = digit[ number / 10 ];
  segment[1] = digit[ number % 10 ];
}

int main()
{
  DDRC = 0xFF;
  DDRD = 0xFF;

  TCCR0  |=  (1<<CS00);      // Vorteiler 1
  TIMSK  |=  (1<<TOIE0);     // Overflow Interrupt

  PrintNumber( 0 );

  sei();

  while (1)
  {
  }
}

Wann immer du eine Zahl zum Ausgeben hast, dann rufst du die
Funktion PrintNumber auf. Diese zerlegt die Zahl und stellt
die richtigen Bitmuster in das globale Array 'segment'. Wenn
die ISR das nächste mal drann ist, dann gibt sie dann das
neue Bitmuster aus.

Ich hab mich an deine Vorgabe was Hardwareansteuerung geht
gehalten. Wenn da was nicht stimmen sollte, dann wirst du
wahrscheinlich nichts sehen. Versuch wenigstens zu verstehen,
was da passiert, häng dich im Simulator in den Debugger und finde
raus, welche Portzuweisung konkret nicht stimmt. Das ganze ist
viel einfacher als du denkst.

als du denkst.

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für das Programm, es funktioniert jetzt, erst hatte ich ein 
Problem dass er nur den gleichen Wert angezeigt hatt, nachdem ich aber 
das sei() vor PrintNumber(0); gesetzt habe funktionierte alles 
einwandfrei.

thanx crashdemon

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hätte nicht gedacht das es so einfach ist, die Interrupt gibt quasi 
pausenlos die Zahlen an die anzeigen und muss sich nicht um irgendwelche 
timings vom adc kümmern, wie bei meinen alten programm, ändert sie die 
auszugebende zahl, so wird das auch einfach von der ISR berücksichtig.

so noch zu allerletzt noch ein paar fragen zu code, dei eigentlich eher 
die c systax selber betreffen, mir würde auch ein link zur eräuterung 
reichen, leider weiß ich nicht nach was ich in google suchen soll.

Korrigiert mich bitte wenn ich falsch liege
if( number < 0 ) // Vom Verständnis wenn Zahl negativ mit (-1) multipliziern
    number = -number; 

  segment[0] = digit[ number / 10 ]; // Zahl durch 10 teilen, Zahlen werden nach den Komma nich berücksichtigt, da Integer
  segment[1] = digit[ number % 10 ]; // Hmm?? 10% das sagt mir jetzt gar nichts 

Autor: Thorsten (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wenn du eine Zahl durch eine andere dividierst, dann ergibt das bei 
Ganzzahlen ja häufig einen Rest, so dass man eine Division auch 
folgendermaßen beschreiben kann:

a / b = c * b + d  (alle Zahlen sind Ganzzahlen)

Beispiel: 36 / 10 = 3 * 10 + 6



In deinem Quellcode finden sich nun zwei Anweisungen wieder:

number / 10 ergibt, wie du selbst schon erkannt hast, das ganzzahlige 
Ergebnis der Division ohne Nachkommastellen.

number % 10 ergibt jetzt einfach nur den Rest der Division number / 10.

Mit dem Beispiel von oben erklärt:
36 / 10 = 3
36 % 10 = 6

Für dich heißt das einfach nur number / 10 berechnet dir die 
Zehnerstelle, number % 10 die Einerstelle.

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
warum kann ich die PrintNumber Funktion nicht in die while-Schleife 
packen, sobald ich das mache, zeigt die Anzeige nur noch null an.
Würde gerne so etwas in der art Abarbeiten, muss ich dann auch wieder 
zusäzlich mit einen Interrupt vom ADC arbeiten?
int main()
{
  uint8_t analog_value;

  DDRC = 0xFF;
  DDRD = 0xFF;

  TCCR0  |=  (1<<CS00);      // Vorteiler 1
  TIMSK  |=  (1<<TOIE0);     // Overflow Interrupt

  sei();

  while (1)
  {
      analog_value = ReadChannel(0); // Ersten ADC auslesen
      calculated = Calculator(analog_value); // Den Analogwert umrechnen 

      PrintNumber(calculated);
  }
}

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

Bewertung
0 lesenswert
nicht lesenswert
Crashdemon wrote:
> warum kann ich die PrintNumber Funktion nicht in die while-Schleife
> packen,

Es gibt keinen Grund dafür, bzw. der Grund ist nicht in PrintNumber
zu suchen.

> sobald ich das mache, zeigt die Anzeige nur noch null an.

Wird wohl daran liegen, dass PrintNumber ständig mit 0
aufgerufen wird.

Autor: Thorsten (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wenn das vorher alles richtig funktioniert hat als es noch außerhalb der 
while-Schleife stand, dann sollte es nun nicht an der 
PrintNumber-Funktion liegen.

Ich würde den Fehler dann eher mal in den Methoden ReadChannel und 
Calcualator suchen.

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Suche jetzt schon eine ganze weile nach einen Fehler finde aber leider 
keinen ,ich glaube das sich die Interrupt ins gehäge kommen, der vom ADC 
und der vom Timer. Ich poste hier mal meine Source vllt. findet ja 
jemand einen Fehler?
#include <math.h>
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/iom8.h>
#include <util/delay.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>

#define XTAL 4000000L    // Systemtakt in Hz

unsigned char status[2] = {0x01, 0x02}; // Status LED's (Rot, Grün)
unsigned char digit[10] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90}; // Bitmaske für die Zahlen
unsigned char display[2] = {0x10, 0x08}; // Siebensegmentanzeige (Einer- , Zehnerstelle)
volatile unsigned char segment[2];
unsigned char counter; // Anzeige die Angeschaltet werden soll

uint8_t MyADCSRA;

ISR(TIMER0_OVF_vect) // Aufruf bei Interrupt von Timer0 (8 Bit)
{
  PORTC |= 0xFF; // Die bisherige Anzeige ausschalten

    counter = counter + 1; // Welches ist die nächste Stelle

    if(counter == 2) // Wenn Stelle 2 erreicht, zurücksetzen
    {
    counter = 0;
  }

    PORTD = segment[counter]; // Jeweiligen Wert ausgeben
    PORTC = display[counter]; // Jeweilige Stelle einschalten
}

ISR(ADC_vect)
{
  MyADCSRA |= (1 << ADIF); // Speicher für Interrupt Flag
}

uint16_t ReadChannel(uint8_t mux)
{
  uint8_t i;
  uint16_t result;

  MyADCSRA = 0; // Interrupt Variable Funktion 
  cli(); // Global Interrupt Disabled
  
  // AD-Wandler wird eingeschaltet
   // Frequenzvorteiler Teilungsfaktor 32
  // bei 4Mhz Taktfrequenz der CPU 
  //
  //  AD_TAKT = CPU_TAKT / TEILUNGSFAKTOR = 4Mhz / 32 = |125kHz|
  //  PERIODENDAUER = 1 / 125kHz = |8 mikroSec.|
  //
  // Es werden alle 8 mikroSec. neue werte vom AD-Wandler eingelesen.
  // Zusätzlich noch den ADC Interrupt (ADIE) einschalten zur
  // weiterverarbeitung für Sleep mode (ADC Noise Canceler)
  ADCSRA |= (1 << ADEN) | (1 << ADPS2) | (1 << ADPS0) | (1 << ADIE);  
  
  set_sleep_mode(SLEEP_MODE_ADC); // ADC Sleep mode vorwählen
  sei(); // Global Interrupt Enabled

  ADMUX = mux; // Kanal waehlen

    // Nach Aktivieren des AD-Wandlers wird ein "Dummy-Readout" empfohlen, 
  // man liest also einen Wert und verwirft diesen, 
  // um den AD-Wandler "warmlaufen zu lassen". 
    ADCSRA |= (1 << ADSC);              
    while ( ADCSRA & (1 << ADSC)) 
  {
    }
  
  // 10Bit Ergenisregister ADCL und ADCH einlesen, und als 16Bit großen 
  // Integer Wert speichern. Werte aus ADCL und ADCH rechtsbündig 
    result |= ADCL + (ADCH << 8); 

  // Messung - Arith. Mittelwert aus vier aufeinanderfolgenden Wandlungen 
    result = 0; 

    for(i = 0; i < 90; i++)
    {
    ADCSRA |= (1 << ADSC); // eine Wandlung "single conversion"
    MyADCSRA &= ~(1 << ADIF); // ADC Interrupt Flag setzten  

    while(!(MyADCSRA & (1 << ADIF))) // Solange Einlesen bis kein Interrupt Flag von ADC vorhanden
    {
    }      
    result += ADCL + (ADCH << 8);  // Nach jeder Wandlung Ergebnisse aufaddieren
    }

  ADCSRA &= ~(1 << ADEN); // Nach vier Wandlungen AD-Wandler deaktivieren 
 
    result /= 90; // Summe der Wandlungen durch vier teilen = arith. Mittelwert
   return result; // den arith. Mittelwert als Rückgabewert der Funktion ReadChannel
}

void status_LED(uint8_t color) // Zeigt den Zustand an
{
  DDRB = 0x03; // Port (B) 1-3 als Ausgang  
  PORTB = 0xFF; // Alle Led'S aus

  switch(color) // Variable "color" abfragen
  {
    case 1:
      // Rote LED leuchtet - Alarmzustand
      PORTB = status[0];
    break;

    case 2:
      // Grüne LED Leuchtet - Alles Super! 
      PORTB = status[1];
    break;
  }
}

void _error(void)
{
  PORTC = 0x10; // Einerstelle einschalten
  PORTD = 0x06; // E (ERROR) Ausgeben
  status_LED(1); // Status LED auf Rot (Fehler aufgetretet)
}

void PrintNumber(int number)
{
  if(number < 0) // Wenn Zahl Negativ
  {
      number = -number; // Wenn Zahl negativ, durch Multiplizieren mit (-1) positiv machen
  }

  if(number < 10) // Einstellig
  {
    segment[0] = digit[ number % 10 ]; // Einerstelle bestimmen
    segment[1] = 0xFF;  // Zehnerstelle leer lassen
  }

  if(number > 9) // Zweistellig
  {
    segment[0] = digit[ number % 10 ]; // Einerstelle bestimmen
      segment[1] = digit[ number / 10 ]; // Zehnerstelle bestimmen
  }

  if(number > 99) // Wenn Zahl größer als 99, als dreistellig
  {
    _error(); // Ist ein Fehler aufgetretet Error-Funktion starten
  }
}

float Angle(uint16_t x, uint16_t y, uint16_t z)
{
  float tilt; // Winkel in Degree (°)

  //  x-Value | z-Value | Degree
  //   409.6  |  614.4  |    0°
  //   614.4  |  409.6  |   90°
  tilt = (asin((x - 512.0) / 102.4)) * (180 / M_PI);
  
  return tilt; // Rückgabewert (Winkel) der Funktion
}

int main(void)
{
  float ADC_angle;

  uint16_t accelx; // Messergebnisse des Kanals C0 (X) (0-1024)
  uint16_t accely; // Messergebnisse des Kanals C1 (Y) (0-1024)
  uint16_t accelz; // Messergebnisse des Kanals C2 (Z) (0-1024)

  // Eigenschaften der Ports D und C definieren
  DDRC = 0x18; // Nur die Ports 3-5 (C) auf Ausgänge schalten
  DDRD = 0xFF; // Port D Komplett als Ausgang für 7 Segment

  wdt_enable(5); // Watchdog hält wache im 500ms Zyklus

    TCCR0 |= (1 << CS00); // Vorteiler 1
    TIMSK |= (1 << TOIE0); // Overflow Interrupt

  sei(); // Globale Interrupts einschalten

  while(1)
  {
    wdt_reset(); // Watchdog Timer zurücksetzen

    status_LED(2); // Status LED auf Grün (Alles OK)
    
    accelx = ReadChannel(0); // 10Bit - Messwert der x-Achse 
    accely = ReadChannel(1); // 10Bit - Messwert der y-Achse
    accelz = ReadChannel(2); // 10Bit - Messwert der z-Achse

    ADC_angle = Angle(accelx, accely, accelz); // Winkel in Variable verstauen
      
    PrintNumber(ADC_angle); // Wert ausgeben, ganzzahlig nicht negativ
  }
}

Autor: Philipp Burch (philipp_burch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
'segment' und 'display' vielleicht mal als volatile deklarieren...

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habe eine volatile draus gemacht, seh gerade das ich noch gar nicht 
geschrieben habe um was für einen Fehler es sich handelt. Das Problem 
ist das die Siebensegmentanzeige immer Null anzeigt, das tut sie aber 
rst seitdem ich die PrintNumber Funktion mit Interrupts habe, iegentlich 
soll die den Winkel der Platine anzeigen auf der der Sensor 
angeschlossen ist der über die Analog Eingänge eingelesen wird.

Autor: Philipp Burch (philipp_burch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mach' mal beim Start von 'main' eine Überprüfung der Resetquelle 
(Power-On, Reset, Brown-Out oder Watchdog). Ich könnte mir denken, dass 
der Watchdog zuschlägt, bevor 'Angle()' fertig ist. Ist aber nur eine 
Vermutung, hab' mir jetzt nicht den gesamten Code genau angesehn.
Die Verwendung von 'volatile' hat nix geändert?

Autor: Thorsten (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich bin mir nicht 100%ig sicher, aber meiner Meinung nach dürftest du 
Probleme mit dem asin in der Angle-Methode bekommen, da soviel ich weiß 
als Argument nur Werte zwischen -1 und 1 erlaubt sind. Deine Berechnung 
(x-512.0)/102.4 mit x-Werten von 0 bis 1024 ergibt aber Werte zwischen 
-5 und 5.

Eine andere Möglichkeit wäre der AD-Wandler. Hast du mal getestet, ob 
die Werte überhaupt richtig eingelesen werden bzw. ob bei die Methode 
ReagChannel wirklich Werte zwischen 0 und 1024 herauskommen?

-------------------------------------------------------

Zwei andere Sachen, die mir bei deinem Code noch aufgefallen sind, die 
aber eigentlich nur kosmetischer bzw. optimierender Natur sind:

1. in deiner Methode PrintNumber kannst du die If-Anweisungen 
verschachteln, denn wenn du weißt, dass eine Zahl zweistellig ist, 
brauchst du ja nicht mehr kontrollieren, ob sie einstellig sein könnte.

2. in deiner ISR toggelst du counter immer zwischen 0 und 1 hin und her. 
Statt
counter = counter + 1; // Welches ist die nächste Stelle

if(counter == 2) // Wenn Stelle 2 erreicht, zurücksetzen
{
   counter = 0;
}

kannst du den Code folgendermaßen einkürzen:
counter++;     // oder wie bei dir: counter = counter + 1;

counter &= 0x01;   // bitweise Verknüpfung mit 1

Erklärung für die Bitverknüpfung:

hat counter den Wert 1, dann ergibt sich die Verknüfung:
0b00000001 & 0b00000001 zu 0b00000001 -> counter bleibt 1


hat counter den Wert 2, dann ergibt sich die Verknüfung:
0b00000010 & 0b00000001 zu 0b00000000 -> counter kippt auf 0 zurück

Autor: Philipp Burch (philipp_burch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Thorsten wrote:
> 2. in deiner ISR toggelst du counter immer zwischen 0 und 1 hin und her.
> Statt
>
> counter = counter + 1; // Welches ist die nächste Stelle
> 
> if(counter == 2) // Wenn Stelle 2 erreicht, zurücksetzen
> {
>    counter = 0;
> }
> 
>
> kannst du den Code folgendermaßen einkürzen:
>
> counter++;     // oder wie bei dir: counter = counter + 1;
> 
> counter &= 0x01;   // bitweise Verknüpfung mit 1
> 

Er könnte natürlich auch gleich
counter ^= 1 schreiben ;)
Möglicherweise kannst du es aber eh schreiben wie du willst, der 
Optimierer könnte alle Varianten zum selben Ergebnis umwandeln.

Autor: concept (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
noch ne variante
if(counter++ == 2)
  counter = 0;

Autor: concept (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wohl besser
++counter

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Am WatchDog liegt es nicht, habe ihne auskommentiert und es funktioniert 
trotzdem nicht, die sache mit dem asin stimmt es muss ein wert in dem 
bereich von +/-1 übergeben werden, allerdings ist das nicht swo wichtig, 
da der sensor sich nur im Breich der 410 - 614 Befindet, ((614,4 -512) / 
102,4 = 1).
Denn Anwandler konnte ich leider noch nicht testen, da ich noch keinen 
Quarz mit Usart Frequenz hab, mit einem 4Mhz Quarzoszillator scheint das 
nicht zu funktionieren.
Die kosmetischen Fehler habe ich behoben, allerdings zeigt alles in 
allem, immer noch nur eine Null an.

Autor: Crashdemon (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habe eine FAQ im Internet gefunden die meine Frage beantwortet hat, ob 
ich OP's zum angleichen benutzen muss, von 
http://www.dimensionengineering.com/accelerometers.htm
Impedance/buffering issues - This is by far the single most common source of problems in projects involving analog accelerometers, because so few people thoroughly read the required documentation. Both PIC and AVR datasheets specify that for A-D conversion to work properly, the connected device must have an output impedance under 10kΩ. Unfortunately, Analog Devices' analog accelerometers have an output impedance of 32kΩ. The solution to this is to use a low input offset rail to rail op amp as a buffer to lower the output impedance. As far as we know, the DE-ACCM is the only accelerometer solution that takes care of this problem for you. 

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.