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


von Chrsitopher R. (arkon)


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

von Port PA0 (Gast)


Lesenswert?

Port PA0, anwesend!
SCNR

von Simon B. (nomis)


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):
1
uint8_t *p;
2
uint8_t  i;
3
4
for (p = &PORTA; p <= &PORTD; p += (&PORTA - &PORTB)) {
5
  for (i = 0; i < 8; i++)
6
    *p |= (1 << i);
7
}

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

von Lukas K. (carrotindustries)


Lesenswert?

Je nach laufrichtung
[c]PORTA = PORTA>>1;[c]
oder
[c]PORTA = PORTA<<1;[c]

von Ben _. (burning_silicon)


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.

von Simon B. (nomis)


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

von hdd (Gast)


Lesenswert?

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

von Lukas K. (carrotindustries)


Lesenswert?

Simon Budig schrieb:

>
1
> uint8_t *p;
2
> uint8_t  i;
3
> 
4
> for (p = &PORTA; p <= &PORTD; p += (&PORTA - &PORTB)) {
5
>   for (i = 0; i < 8; i++)
6
>     *p |= (1 << i);
7
> }
8
>
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.
1
uint8_t *p;
2
uint8_t  i;
3
4
for (p = &PORTA; p <= &PORTD; p += (&PORTA - &PORTB)) {
5
  *p = 1;
6
  for (i = 0; i < 8; i++)
7
    *p = *p<<1;
8
}
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.

von Chrsitopher R. (arkon)


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

von Simon B. (nomis)


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
1
p += (&PORTB - &PORTA)
heißen.

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

Viele Grüße,
        Simon

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


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.

von Lukas K. (carrotindustries)


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.

von Simon B. (nomis)


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

von Lukas K. (carrotindustries)


Lesenswert?

1
uint8_t p;
2
uint8_t t;
3
uint8_t  i;
4
uint8_t *ports[3];
5
ports[0] = &PORTA;
6
ports[1] = &PORTB;
7
ports[2] = &PORTC;
8
9
for (p = 0; p < 3; p +=1) {
10
  t = 1;
11
  for (i = 0; i < 8; i++){
12
   t <<= 1;
13
   *ports[p] = t; //punkt
14
// *ports[p] |= t; //band
15
  }
16
}
Nicht getestet, sollte aber so passen.

von Chrsitopher R. (arkon)


Lesenswert?

Luk4s K. schrieb:
1
[code]
2
 uint8_t p;
3
 uint8_t t;
4
 uint8_t  i;
5
 uint8_t *ports[3];
6
 ports[0] = &PORTA;
7
 ports[1] = &PORTB;
8
 ports[2] = &PORTC;
9
 
10
 for (p = 0; p < 3; p +=1) {
11
   t = 1;
12
   for (i = 0; i < 8; i++){
13
    t <<= 1;
14
    *ports[p] = t; //punkt
15
 // *ports[p] |= t; //band
16
   }
17
 }
 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

von Chrsitopher R. (arkon)


Lesenswert?

Hier jetzt mein Code mit dem ein Punkt eine 5x5 LED Matrix durchläuft.
1
  #include <avr/io.h>
2
  #ifndef F_CPU
3
  /* Definiere F_CPU, wenn F_CPU nicht bereits vorher definiert 
4
     (z.&nbsp;B. durch Übergabe als Parameter zum Compiler innerhalb 
5
     des Makefiles). Zusätzlich Ausgabe einer Warnung, die auf die
6
     "nachträgliche" Definition hinweist */
7
  #define F_CPU 1000000     /* interner Takt mit 1MHz */
8
  #endif
9
  #include <util/delay.h>     /* in älteren avr-libc Versionen <avr/delay.h> */ 
10
  #include <stdio.h> 
11
12
  uint8_t s;
13
  uint8_t z;
14
  uint8_t i;
15
  uint8_t j;
16
17
18
  void long_delay(uint16_t ms)          //Pausefunktion
19
  {
20
      for(; ms>0; ms--) _delay_ms(1);
21
  }
22
23
  int main( void )
24
  {
25
26
    DDRA = 0xff;                //PORTA als Ausgang
27
    DDRC = 0xff;                //PORTC als Ausgang
28
29
    PORTA = 0xff;                //Alle LEDs aus
30
    PORTB = 0xff;                //Alle LEDs aus
31
32
    while (1)
33
    {
34
      PORTA = 0x01;
35
      PORTC = 0x01;
36
      long_delay(300);
37
38
      s = 1;
39
      z = 1;
40
41
      for (j=0; j<5; j++)
42
      {
43
        for (i=0; i<4; i++)
44
        {
45
          z <<= 1;
46
          PORTC = z;
47
          long_delay(300);
48
        }
49
50
        PORTC = 0x01;
51
        s <<= 1;
52
        PORTA = s;
53
        long_delay(300);
54
        z = 1;
55
      } 
56
    }    
57
    return 0;
58
  }

Danke für die Hilfe!

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.