www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Atmega Ports "durchzählen"


Autor: Chrsitopher Rohe (arkon)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Moin,

ich experimentiere grad ein wenig für ein kommendes Projekt und brauche 
grade mal einen Denkanstoß (liegt vielleicht an der Uhrzeit aber ich 
will, dass es jetzt geht :D )

Kann ich die Ports eines Atmegas (im Mom am Atmega8515L 8PU, sollte aber 
allgemein funktionieren) "durchzählen" lassen. Also wie z.B. mit i++ von 
z.B. PORTA PA0 bist PA7 hochzählen lassen? Man könnte sich das wie ein 
Lauflicht vorstellen.

Gruß
Christopher

Autor: Port PA0 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Port PA0, anwesend!
SCNR

Autor: Simon Budig (nomis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Chrsitopher Rohe schrieb:
> Kann ich die Ports eines Atmegas (im Mom am Atmega8515L 8PU, sollte aber
> allgemein funktionieren) "durchzählen" lassen. Also wie z.B. mit i++ von
> z.B. PORTA PA0 bist PA7 hochzählen lassen?

Jein, nicht bitweise. Aber die Adressen der Register PORT[ABCD] liegen 
in gleichmäßigen Abstand voneinander im Speicher. Das it mindestens beim 
atmega8515 so, bei den anderen Prozessoren sollte man das verifizieren 
("Register Summary" im Datenblatt), ist aber höchstwahrscheinlich so.

Dann kann man im Prinzip sowas machen (ungetesteter code):
uint8_t *p;
uint8_t  i;

for (p = &PORTA; p <= &PORTD; p += (&PORTA - &PORTB)) {
  for (i = 0; i < 8; i++)
    *p |= (1 << i);
}

Diese Schleife sollte die Pins PA0,..,PA7,PB0,...,PB7,....,PD7 
nacheinander auf 1 setzen.

Wie gesagt, das ist nur mal eben freihändig hingeschrieben, wenn es 
nicht funktioniert sollte aber doch das Prinzip erkennbar sein.

Viele Grüße,
        Simon

Autor: Lukas K. (carrotindustries)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Je nach laufrichtung
[c]PORTA = PORTA>>1;[c]
oder
[c]PORTA = PORTA<<1;[c]

Autor: Ben ___ (burning_silicon)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
kannst du, du mußt aber binär zählen (effektiv mit 2 multiplizieren).

beispielsweise

LDI R16,1
OUT PORTn,R16

damit leuchtet deine LED am niederwertigsten bit/pin auf.
alles weitere würde ich mit einer bitverschiebung machen.

loop:
ROL R16 (rotate left, ROL heißt's jedenfalls beim x86 assembler)
OUT PORTn,R16
<delay> (warteschleife, sonst flackern deine LEDs mit 1-2 MHz)
RJMP loop

habe jetzt den genauen AVR assembler-befehl für die bit-rotation mit 
überlauf nicht im kopf, da schau da bitte nochmal ins datenblatt.

EDIT: war wer schneller.

Autor: Simon Budig (nomis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oh, ich sehe gerade, dass bei dem atmega8515 auch noch ein PORTE 
existiert, das aus diesem Raster rausfällt. Also: Nicht darauf 
verlassen, immer im Datenblatt nachgucken ob es passt...

Viele Grüße,
         Simon

Autor: hdd (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
register mit 0x01 laden, nach portA ausgeben, register 
links/rechtsshiften über carry und wieder ausgeben, usw.
vllt. noch mit etwas wartezeiten dazwischen

Autor: Lukas K. (carrotindustries)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Simon Budig schrieb:

>
> uint8_t *p;
> uint8_t  i;
> 
> for (p = &PORTA; p <= &PORTD; p += (&PORTA - &PORTB)) {
>   for (i = 0; i < 8; i++)
>     *p |= (1 << i);
> }
> 
Bei diesem Code bleiben die LEDs an, es läuft also ein Band, kein Punkt.
Ich habe gehört, dass Konstrukte wie (1<<i) relativ langsam sind.
uint8_t *p;
uint8_t  i;

for (p = &PORTA; p <= &PORTD; p += (&PORTA - &PORTB)) {
  *p = 1;
  for (i = 0; i < 8; i++)
    *p = *p<<1;
}
Simon Budig schrieb:
> Oh, ich sehe gerade, dass bei dem atmega8515 auch noch ein PORTE
> existiert, das aus diesem Raster rausfällt. Also: Nicht darauf
> verlassen, immer im Datenblatt nachgucken ob es passt...
>
> Viele Grüße,
>          Simon

Dann macht man eben ein Array mit Pointern auf die jeweiligen PORTs. 
Dann ist man von den Adressen unabhängig.

Autor: Chrsitopher Rohe (arkon)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ui. Noch richtig was los hier um diese Uhrzeit.

Wie immer zu spät merke ich das ich Infos unterschlagen habe:

1. Ich programmiere in C
2. Ich bin noch recht unerfahren beim Programmieren von µC

@Simon: Wie Luk4s anmerkt läuft bei deinem Code kein Punkt sondern die 
Ports schalten nach und nach alle auf 1

@Luk4s: Bei PORTA = PORTA>>1; bzw. PORTA = PORTA<<1; passiert aber das 
gleiche ^^

Autor: Simon Budig (nomis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Luk4s K. schrieb:
> Bei diesem Code bleiben die LEDs an, es läuft also ein Band, kein Punkt.

Ja, habe ich hier in Kauf genommen, weil es mir um das Prinzip der 
Adressierung ging (aber s.u.).

> Ich habe gehört, dass Konstrukte wie (1<<i) relativ langsam sind.

Eigentlich sollte der C-Compiler das direkt in eine SBI-Instruktion 
übersetzen. Sollte nicht wirklich aufwendig sein...

(hab gerade nachgeguckt: Ok, SBI kostet zwei Taktzyklen und ist damit 
eine der langsameren.)

> Dann macht man eben ein Array mit Pointern auf die jeweiligen PORTs.
> Dann ist man von den Adressen unabhängig.

Ja, das ist vermutlich sinnvoll. Prozessorunabhängig (wie vom OP 
gewünscht) wird man es aber vermutlich nicht wirklich hinkriegen - bei 
manchen AVRs fehlt z.B. PORTA...

In meiner Schleife ist noch ein Bug, das Increment-statement der äußeren 
for-schleife sollte natürlich sinnvollerweise
p += (&PORTB - &PORTA)
heißen.

Und die eine Warnung kriegt man noch weg, wenn man *p als volatile 
deklariert...

Viele Grüße,
        Simon

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

Bewertung
0 lesenswert
nicht lesenswert
Simon Budig schrieb:

>> Ich habe gehört, dass Konstrukte wie (1<<i) relativ langsam sind.
>
> Eigentlich sollte der C-Compiler das direkt in eine SBI-Instruktion
> übersetzen. Sollte nicht wirklich aufwendig sein...

Das wird schwierig für den Compiler, solange i keine Konstante sondern 
eine Variable ist.

OK. Wenn der Compiler sich zu Loop-Unrolling entschliesst, könnte er 
dieses Baby knacken.

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

Bewertung
0 lesenswert
nicht lesenswert
Chrsitopher Rohe schrieb:

> @Simon: Wie Luk4s anmerkt läuft bei deinem Code kein Punkt sondern die
> Ports schalten nach und nach alle auf 1

Das sollte aber für einen alten Bitschieber-Spezi nicht wirklich das 
Porblem sein.

> @Luk4s: Bei PORTA = PORTA>>1; bzw. PORTA = PORTA<<1; passiert aber das
> gleiche ^^

... Nicht ... wirklich
Es ist aber trotzdem eher ungeschickt. Eine zusätzliche Variable, in der 
der 1-er geschoben wird könnte besser sein. Die Chancen, dass der 
Compiler diese Variable in einem Registr halten kann sind sehr gut. Bei 
PORTA <<= 1 muss hingegen jedesmal der PORTA erst ausgelesen werden um 
den Wert dann um 1 Stelle zu schieben.

Autor: Lukas K. (carrotindustries)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Chrsitopher Rohe schrieb:
> Ui. Noch richtig was los hier um diese Uhrzeit.
>
> Wie immer zu spät merke ich das ich Infos unterschlagen habe:
>
> 1. Ich programmiere in C
> 2. Ich bin noch recht unerfahren beim Programmieren von µC
>
> @Simon: Wie Luk4s anmerkt läuft bei deinem Code kein Punkt sondern die
> Ports schalten nach und nach alle auf 1
>
> @Luk4s: Bei PORTA = PORTA>>1; bzw. PORTA = PORTA<<1; passiert aber das
> gleiche ^^


Bei PORTA = PORTA<<1; läuft die LED vom LSB zum MSB. Als Startwert ist 
für PORTA demnach 1 zu setzen.
Bei PORTA = PORTA>>1; läuft die LED vom MSB zum LSB. Als Startwert ist 
für PORTA demnach 128 zu setzen.

Autor: Simon Budig (nomis)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Simon Budig schrieb:
>
>>> Ich habe gehört, dass Konstrukte wie (1<<i) relativ langsam sind.
>>
>> Eigentlich sollte der C-Compiler das direkt in eine SBI-Instruktion
>> übersetzen. Sollte nicht wirklich aufwendig sein...
>
> Das wird schwierig für den Compiler, solange i keine Konstante sondern
> eine Variable ist.

Stimmt. Ich habe die "Instruction Set Summary" nicht richtig gelesen. Da 
hat SBI zwei Operanden: "P,b" und das b muss schon zur Compile-Zeit 
feststehen...

Viele Grüße,
        Simon

Autor: Lukas K. (carrotindustries)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
uint8_t p;
uint8_t t;
uint8_t  i;
uint8_t *ports[3];
ports[0] = &PORTA;
ports[1] = &PORTB;
ports[2] = &PORTC;

for (p = 0; p < 3; p +=1) {
  t = 1;
  for (i = 0; i < 8; i++){
   t <<= 1;
   *ports[p] = t; //punkt
// *ports[p] |= t; //band
  }
}

Nicht getestet, sollte aber so passen.

Autor: Chrsitopher Rohe (arkon)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Luk4s K. schrieb:
[code]
 uint8_t p;
 uint8_t t;
 uint8_t  i;
 uint8_t *ports[3];
 ports[0] = &PORTA;
 ports[1] = &PORTB;
 ports[2] = &PORTC;
 
 for (p = 0; p < 3; p +=1) {
   t = 1;
   for (i = 0; i < 8; i++){
    t <<= 1;
    *ports[p] = t; //punkt
 // *ports[p] |= t; //band
   }
 }
 
 
 Nicht getestet, sollte aber so passen.[/code]

Das sieht schon sehr gut aus. Das werde ich nachher (ist ja schon 
Freitag ^^) mal genau unter die Lupe nehmen und auf meine Bedürfnisse 
anpassen. Dankö und noch eine gute Nacht! Macht nicht mehr so lange :D

Autor: Chrsitopher Rohe (arkon)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hier jetzt mein Code mit dem ein Punkt eine 5x5 LED Matrix durchläuft.
  #include <avr/io.h>
  #ifndef F_CPU
  /* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert 
     (z.&nbsp;B. durch Übergabe als Parameter zum Compiler innerhalb 
     des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die
     "nachträgliche" Definition hinweist */
  #define F_CPU 1000000     /* interner Takt mit 1MHz */
  #endif
  #include <util/delay.h>     /* in älteren avr-libc Versionen <avr/delay.h> */ 
  #include <stdio.h> 

  uint8_t s;
  uint8_t z;
  uint8_t i;
  uint8_t j;


  void long_delay(uint16_t ms)          //Pausefunktion
  {
      for(; ms>0; ms--) _delay_ms(1);
  }

  int main( void )
  {

    DDRA = 0xff;                //PORTA als Ausgang
    DDRC = 0xff;                //PORTC als Ausgang

    PORTA = 0xff;                //Alle LEDs aus
    PORTB = 0xff;                //Alle LEDs aus

    while (1)
    {
      PORTA = 0x01;
      PORTC = 0x01;
      long_delay(300);

      s = 1;
      z = 1;

      for (j=0; j<5; j++)
      {
        for (i=0; i<4; i++)
        {
          z <<= 1;
          PORTC = z;
          long_delay(300);
        }

        PORTC = 0x01;
        s <<= 1;
        PORTA = s;
        long_delay(300);
        z = 1;
      } 
    }    
    return 0;
  }
  

Danke für die Hilfe!

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.