Forum: Mikrocontroller und Digitale Elektronik Multiplexer Atmega 8


von Peter Q. (xenonrox)


Lesenswert?

Hi, ich bin habe mir momentan ein kleines Projekt zugemutet aber da ich 
nicht so viel Erfahrung habe in Atmega8 programmieren hab ich gehofft 
ihr könntet mir etwas helfen.

Also meine Idee ist es einen Multiplexer zu programmieren (Platine ist 
schon fertig). Dieser Multiplexer hat 20 Digitale Eingänge 
(PD0...PD7,PB0...PB6, PC0..PC5) die eingelesen werden müssen und in 
einem Array gespeichert.
Nun soll meine SPS eine Anfrage über PB6 schicken( auf steigende Flanke 
schauen) dann soll der Atmega 100ms warten. Anschließende soll nach 
einer festgelegten Reihenfolge die Zustände der 20 Digitalen Eingänge 
über den Ausgang PB7 in einem 50ms Takt ausgegeben werden. Ist so eine 
Art Morse.
Es geht mir nur um die Atmega Programmierung.

Ich weiß garnicht wie ich da Anfangen könnte.

Es würde mich sehr freuen wenn einer von euch genug Zeit und Lust hat 
mir dabei zu helfen.

: Verschoben durch Admin
von Thomas W. (diddl)


Lesenswert?

Ah, so eine Art Bake?

Der PB6 kann nicht als digitaler Eingang benutzt werden, wenn er schon 
mit der SPS kommuniziert.

Das Array braucht es auch nicht, die Abfrage der Eingänge macht man 
während man sie sendet.

Wenn PB6 als open collector arbeitet, dann kann das funktionieren. Die 
SPS gibt quasi die Kommunikation frei.


Die Aufgabe ist super simpel:

- Warte auf PB6 high
- Setze Timer(n) auf 100 ms
- Warte auf Timer(n) abgelafuen, wenn PB6 low dann zum Start
- Schleife über alle Inputs :: frage Input ab und sende byte

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter Q. schrieb:
> Ich weiß garnicht wie ich da Anfangen könnte.

Indem du die Aufgabe in Teilschritte zerlegst (hast du weitgehend
als Prosa bereits hingeschrieben) und daraus einen Programmablauf
planst.

Die nötigen Grundlagen musst du dir natürlich schon erstmal
(spielerisch) vorher aneignen, also:

. wie lese ich Pins ein
. wie erkenne ich die Änderung eines Pins
. wie gebe ich ein Bit aus
. wie warte ich eine bestimmte Zeit

Das alles geht noc völlig linear für den Anfang, ohne Intrrupts,
ohne Timer.  Das ist natürlich nicht sehr elegant dann, aber es löst
die gestellte Aufgabe und braucht am wenigsten Knowhow deinerseits.

Optimieren kannst du danach:

. Schaltflanke per Interrupt erkennen (geht beim ATmega8 nur über 
Timerinterrupt, wenn du einen ATmega88 nimmst, könntest du auch einen 
Pinchange-Interrupt benutzen)
. Ausgabe über Timerinterrupt steuern
. Rest der Zeit schlafen gehen

: Bearbeitet durch Moderator
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Thomas W. schrieb:
> Das Array braucht es auch nicht, die Abfrage der Eingänge macht man
> während man sie sendet.

Dann hast du aber keinen Momentanzustand (*), sondern würdest sich
ggf. ändernde Werte ausgeben.

(*) Hast du natürlich streng genommen sowieso nicht, weil sich nicht
alle drei Ports gleichzeitig abfragen lassen, aber zumindest sehr
schnell hintereinander.

von Peter Q. (xenonrox)


Lesenswert?

okay also der eingang PB6 wird von 24V über einen Optokoppler 
geschaltet, das ich auf die gewünschte eingangspannung komme.

Wie arbeite ich denn mit einem Interrupt? Pins einlesen und ausgänge 
schalten bekomm ich auch noch hin aber die Schleife bei einem Interrupt 
starten usw. raff ich noch nicht so :D

Kann jemand zufällig so eine kleine Code Anleitung schicken :D

von jemand (Gast)


Lesenswert?

Peter Q. schrieb:
> Kann jemand zufällig so eine kleine Code Anleitung schicken :D

http://www.mikrocontroller.net/articles/Interrupt

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Dann fang' doch erstmal ohne Interrupts an.  Das bisschen lässt
sich komplett linear erschlagen.

von Peter Q. (xenonrox)


Lesenswert?

So ich hab jetzt mal was im Text editor geschrieben, wie ich mir das 
vorstelle, also jetzt darf gelacht werden :D
1
#include <avr/io.h>
2
#define F_CPU 1200000UL  // 1,2 MHz
3
#include <util/delay.h>
4
5
6
// Digitale Eingänge : |PD0,PD1,PD2,PD3,PD4,PD5,PD6,PD7 | PB0,PB1,PB2,PB3,PB4,PB5 | PC0,PC1,PC2,PC3,PC4,PC5
7
// Data-IN:         |PB6
8
// Data-OUT:       |PB7
9
10
uint8_t  flanke;
11
uint8_t  alteflanke;
12
13
int main(void){
14
DDRB |= _BV(PB7); // PB7 ist jetzt Ausgang
15
DDRD &= ~(1 << PD0), ~(1 << PD1),~(1 << PD2),~(1 << PD3),~(1 << PD4),~(1 << PD5),~(1 << PD6),~(1 << PD7);
16
DDRB &= ~(1 << PB0), ~(1 << PB1),~(1 << PB2),~(1 << PB3),~(1 << PB4),~(1 << PB5),~(1 << PB6);
17
DDRC &= ~(1 << PC0), ~(1 << PC1),~(1 << PC2),~(1 << PC3),~(1 << PC4),~(1 << PC5);
18
19
20
21
22
_flanke = PINB;  // zu testende Variable
23
while(1){
24
         if (    ( flanke     & (1<<PB6) ) != 0 )
25
              && ( alteflanke & (1<<PB6) ) == 0 )   )
26
{
27
  _delay_ms(100);
28
  PINB7 = inp (PIND0); // Schreibe Zustand an PD0 in PB7
29
  _delay_ms(50);
30
  PINB7 = inp (PIND1);
31
  _delay_ms(50);
32
  PINB7 = inp (PIND2);
33
  _delay_ms(50);
34
  PINB7 = inp (PIND3);
35
  _delay_ms(50);
36
  PINB7 = inp (PIND4);
37
  _delay_ms(50);
38
  PINB7 = inp (PIND5);
39
  _delay_ms(50);
40
  PINB7 = inp (PIND6);
41
  _delay_ms(50);
42
  PINB7 = inp (PIND7);
43
  _delay_ms(50);
44
  PINB7 = inp (PINB0);
45
  _delay_ms(50);
46
  PINB7 = inp (PINB1);
47
  _delay_ms(50);
48
  PINB7 = inp (PINB2);
49
  _delay_ms(50);
50
  PINB7 = inp (PINB3);
51
  _delay_ms(50);
52
  PINB7 = inp (PINB4);
53
  .
54
  .
55
  .
56
  .
57
  usw..
58
}  
59
60
}
61
}

: Bearbeitet durch Moderator
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter Q. schrieb:

> DDRD &= ~(1 << PD0), ~(1 << PD1),~(1 << PD2),~(1 << PD3),~(1 << PD4),~(1
> << PD5),~(1 << PD6),~(1 << PD7);

Nun, die Übung mit dem Komma-Operator solltest du dir nochmal
ansehen.  Der hat eine ganz andere Bewandnis als das, was du hier
willst.

Aber: die DDRx-Register sind nach einem Reset ohnehin 0, noch
„nuller“ geht's nicht.  Lass sie also einfach in Ruhe.

Korekt wäre übrigens:
1
DDRB &= ~((1 << PB0) | (1 << PB1) |  | (1 << PB6));


> _flanke = PINB;  // zu testende Variable

Lass mal den Unterstrich weg.

> while(1){
>          if (    ( flanke     & (1<<PB6) ) != 0 )
>               && ( alteflanke & (1<<PB6) ) == 0 )   )

Vergiss nicht, „alteflanke“ vor dem Ende der while-Schleife mit
dem aktuell getesteten Wert zu setzen.

>   PINB7 = inp (PIND0); // Schreibe Zustand an PD0 in PB7

inp() ist nirgends definiert.

Ansonsten ist das aber als allereinfachste Implementierung OK.

: Bearbeitet durch Moderator
von Karl M. (Gast)


Lesenswert?

Moin in die Runde.
1
PINB7 = inp (PIND0);

liefert unabhängig davon, ob #define inp(x) (1<<(x)) ist, eine 
Fehlermeldung, da PINB7 eine Konstante =7 ist.

Was soll in deinen Augen PINB7 = <expr> bewirken ?

von Peter Q. (xenonrox)


Lesenswert?

ja das "inp" war ein kopierfehler :D

müsste das jetzt so grob implementierbar sein?

#include <avr/io.h>
#define F_CPU 1200000UL  // 1,2 MHz
#include <util/delay.h>

// Digitale Eingänge : |PD0,PD1,PD2,PD3,PD4,PD5,PD6,PD7 | 
PB0,PB1,PB2,PB3,PB4,PB5 | PC0,PC1,PC2,PC3,PC4,PC5
// Data-IN:         |PB6
// Data-OUT:       |PB7

uint8_t  flanke;
uint8_t  alteflanke;

int main(void){
DDRB |= _BV(PB7); // PB4 ist jetzt Ausgang

flanke = PINB;  // zu testende Variable
while(1){
if (    ( flanke     & (1<<PB6) ) != 0 )
     && ( alteflanke & (1<<PB6) ) == 0 )   )
{
  _delay_ms(100);
  PINB7 =(PIND0); // Schreibe Zustand an PD0 in PB7
  _delay_ms(50);
  PINB7 =(PIND1);
  _delay_ms(50);
  PINB7 =(PIND2);
  _delay_ms(50);
  PINB7 = (PIND3);
  _delay_ms(50);
  PINB7 = (PIND4);
  _delay_ms(50);
  PINB7 = (PIND5);
  _delay_ms(50);
  PINB7 = (PIND6);
  _delay_ms(50);
  PINB7 =(PIND7);
  _delay_ms(50);
  PINB7 =(PINB0);
  _delay_ms(50);
  PINB7 = PINB1);
  _delay_ms(50);
  PINB7 =(PINB2);
  _delay_ms(50);
  PINB7 = (PINB3);
  _delay_ms(50);
  PINB7 = (PINB4);
  .
  .
  .
  .
  usw..
  alteflanke = flanke;
}}
}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl M. schrieb:
> eine Fehlermeldung, da PINB7 eine Konstante =7 ist.

Stimmt, den Teil hatte ich mir nicht angesehen.

Vorschlag:
1
void pinb7(uint8_t val)
2
{
3
  if (val)
4
    PORTB |= _BV(7);
5
  else
6
    PORTB &= ~ _BV(7);
7
}

Bitmanipulation

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bitte gewöhn' dir mal an, [ c ] ... [ /c ] um deinen Code zu schreiben
(ohne die Leerzeichen in den Klammern).

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Per default sind im  AVR-GCC keine Bitvariablen definiert.
Dazu kann man aber meine "sbit.h" benutzen.
1
#include "sbit.h"
2
// ...
3
  PORT_B7 = PIN_D0;
4
  _delay_ms(50);
5
  PORT_B7 = PIN_D1;
6
  _delay_ms(50);
7
// usw.

von Peter Q. (xenonrox)


Lesenswert?

Dann müsste es ja so gehen oder?
Wenn ich jetzt das Programm mit dem Programmer auf den Atmega8 brennen 
möchte, woraum muss ich da achten damit der seinen internen Ozilator 
benutzt?



#include <avr/io.h>
#define F_CPU 1200000UL  // 1,2 MHz
#include <util/delay.h>

// Digitale Eingänge : |PD0,PD1,PD2,PD3,PD4,PD5,PD6,PD7 | 
PB0,PB1,PB2,PB3,PB4,PB5 | PC0,PC1,PC2,PC3,PC4,PC5
// Data-IN:         |PB6
// Data-OUT:       |PB7

uint8_t  flanke;
uint8_t  alteflanke;

void pinb7(uint8_t val)
{
  if (val)
    PORTB |= _BV(7);
  else
    PORTB &= ~ _BV(7);
}

int main(void){

DDRB |= _BV(PB7); // PB4 ist jetzt Ausgang
pinb7(uint8_t val);

flanke = PINB;  // zu testende Variable
while(1){
if ((flanke &(1<<PB6))!= 0)
     && (alteflanke &(1<<PB6))==0))
{
  _delay_ms(100);
  PINB7 =(PIND0); // Schreibe Zustand an PD0 in PB7
  _delay_ms(50);
  PINB7 =(PIND1);
  _delay_ms(50);
  PINB7 =(PIND2);
  _delay_ms(50);
  PINB7 = (PIND3);
  _delay_ms(50);
  PINB7 = (PIND4);
  _delay_ms(50);
  PINB7 = (PIND5);
  _delay_ms(50);
  PINB7 = (PIND6);
  _delay_ms(50);
  PINB7 =(PIND7);
  _delay_ms(50);
  PINB7 =(PINB0);
  _delay_ms(50);
  PINB7 = PINB1);
  _delay_ms(50);
  PINB7 =(PINB2);
  _delay_ms(50);
  PINB7 = (PINB3);
  _delay_ms(50);
  PINB7 = (PINB4);
  .
  .
  .
  .
  usw..
  alteflanke = flanke;
}}
}

von Karl M. (Gast)


Lesenswert?

Hallo

Peter Q. schrieb:
> Dann müsste es ja so gehen oder?
> Wenn ich jetzt das Programm mit dem Programmer auf den Atmega8 brennen
> möchte, woraum muss ich da achten damit der seinen internen Ozilator
> benutzt?

Nicht glauben, sondern wissen ist angesagt.
Also über testen und abändern kann man vielleicht verstehen, was Jörg 
Wunsch und Peter Dannegger die mitteilen möchten.

Du denkst an PINB7 kann man Informationen ausgaben und das ist falsch.

Schau dir bitte auch die Procedure
1
pinb7(uint8_t val);
, dies ist eine besondere Funktion.

Die Nahmeswahl ist nicht die Beste, da Du so den Unterschied zu PINB7 
nicht siehst!
1
writeBit_to_B7(uint8_t);

Am effizientesten wird Dir, nach eine weile, 1-2 Jahre, lernen die 
Macrosammlung sbit.h von Peter Dannegger vorkommen.

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.