PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Abfallen von Ausgängen verzögern



modtronic
02.05.2017, 10:14
Guten Morgen

Folgende Situation.
Ich möchte an einem AT-Mega 8 einen kompletten Port als Eingang auf einen I2C IC übertragen.
In diesem Fall habe ich den MCP23017 gewählt.

Im Programm ist das eine einfache Routine, da ich den Port z.b D einfach auf den i2c schreiben kann.
Ich nutze das so , um mit 2 Adern (gepufferter I2c) vielen Daten zu übertragen.

das funktioniert auch soweit.
Zeile aus dem Programm: i2c_write (PIND); (Beispiel)

jetzt habe ich aber das Problem, das das sofort geschieht. ich würde gerne das die Eingänge eines Ports zwar sofort übertragen werden, zb PIND.0 wird direkt zum GBA0 übertragen (HIGH Zustand), der GBA0 aber verzögert wieder 0 wird.(ca. 200ms, bzw auch einstellbar wenn länger oder kürzer)
Die Lösung das ganze über Kondensatoren zu machen finde ich nicht wirklich toll und zu unflexibel.

Vom Gedanke her, müsste ich daher jeden einzelnen AT Mega PIN aufbröseln, verzögern und wieder als Gesamtes Byte zusammenfügen und dann an den i2c übertragen.
Ich habe dazu schonmal was von Bitmanipulation gelesen. oder ist das falsche Ansatz ?
hat da jemand schonmal in der Richtung was gemacht und kann mir da weiterhelfen ??

Gruss
Patrick

Searcher
02.05.2017, 10:48
Hallo modtronic,

So richtig verstehe ich nicht, was Du möchtest.

Du hast einen Atmega8 und einen MCP23017.
Beide sind über einen I2C Bus verbunden.
Mit "i2c_write (PIND);" schickst Du den Inhalt von dem PIND Register zum MCP23017

Meinem Verständnis nach sind dann die PD0 bis PD7 des Mega8 als Eingänge konfiguriert.
Die GPA0 bis GPA7 des MCP23017 nehmen nach dem i2c_write (PIND) dann als Ausgänge den Zustand der Mega8 PortD Eingänge an.

An welcher Stelle soll denn was genau verzögert werden?

Gruß
Searcher

modtronic
02.05.2017, 11:14
Hallo modtronic,

So richtig verstehe ich nicht, was Du möchtest.

Du hast einen Atmega8 und einen MCP23017.
Beide sind über einen I2C Bus verbunden.
Mit "i2c_write (PIND);" schickst Du den Inhalt von dem PIND Register zum MCP23017

Meinem Verständnis nach sind dann die PD0 bis PD7 des Mega8 als Eingänge konfiguriert.
Die GPA0 bis GPA7 des MCP23017 nehmen nach dem i2c_write (PIND) dann als Ausgänge den Zustand der Mega8 PortD Eingänge an.

An welcher Stelle soll denn was genau verzögert werden?

Gruß
Searcher

ja genau, der PORDT fungiert als Eingang der PORT GPA des MCP als Ausgang.
Wenn ich jetzt einen beliegigen PIN des Mega 8 oder mehrere auf 1 lege, wird das ja direkt zum MCP übertragen.
sobald der entsprechende PIN wieder 0 wird wird auch der PIN am MCP wieder 0.
vom Verständnis ist das das PIND.0 gleich GPA0 ist, PIND.1 gleich GPA1 ist usw.

ich möchte nun, sobald ein PIN des Portes D "1" wird das dieser auch sofort "1" am MCP wird, sobald nun der ensprechende PIN wieder 0 wird, dieser verzögert am MCP 0 wird.
das heisst wenn PIN 0, 1 und 2 HIGH sind, sind auch die PINS am MCP HIGH.
Wenn PIN 0, nun 0 wird, 1 und 2 High bleiben soll der PIN GPA0 am MCP verzögert "0" werden, der Rest nicht betroffen sein. bzw das soll für jeden Pin getrennt möglich sein.

Hoffe ich habe das verständlich erklären können ?

Gruss
Patrick

Peter(TOO)
02.05.2017, 12:36
Hallo Patrick,

Etwas Software brauchst du dazu schon.

Du brauchst für jedes Bit einen Timer. Das kannst du mit einem Timer-Interrupt machen, welcher z.B. alle 1ms aufgerufen wird.
Wenn die Timer >0 wird der Wert jedes Mal um 1 verringert.

Wenn du PORTD einliest, wird jedes Mal für eine gelesene 1 der entsprechende Timer mit 200 beschrieben.

Für die Ausgabe musst für jeden Timer welcher >0 eine 1 ins entsprechende Bit setzen.

So in der Art (nicht getestet):



unsigned char timer[8];

void interrupt(void)
{
char i;

for (i = 0; i < 8; i++)
{
if (timer[i]) timer[i]--;
}
}

void out(void)
{
char i;
unsigned char x = 0;

for (i = 0; i < 8; i++)
{
if (timer[i]) (x |= (1 << i));
}
i2c_write (x);
}

unsigned char in(void)
{
unsigned char x;
char i;

x = PORTD;
for (i = 0; i < 8; i++)
{
if (x & (1 << i)) timer[i] = 200;
}
}


Kann Reste von Tippfehlern enthalten.

MfG Peter(TOO)

Searcher
02.05.2017, 17:51
Hoffe ich habe das verständlich erklären können ?
:-) Ja, ich glaube wenigstens, daß ich das jetzt verstanden habe. Ich habe auch mal ein Programm in BASCOM mit den wichtigsten Bestandteilen und hoffentlich verständlich kommentiert eingefügt. Leider kann ich C nicht wirklich und kann nicht beurteilen, ob ich das gleiche mache wie Peter. Auf jeden Fall habe ich auch den Millisekundentimer, der "Timer" für die Portpins runterzählt. Programm ist auch nicht getestet.




'
' ab Hochkomma (') beginnt in BASCOM ein Kommentar
'
' mit PIND.Bit_no, also Variable bzw Register Punkt Ziffer kann man einzelne Bits Adressieren.
'
'################################################# ###################
'1. Programm liest PIND Register ein und speichert für spätere Vergleiche.
'2. Stellt Änderungen von Portpins zum vorherigen Einlesen fest
'3.a Wird ein Wechsel von "1" auf "0" festgestellt, wird ein Timerregister für den entsprechenden Eingang auf 200ms gesetzt
'3.b Wird ein Wechsel von "0" auf "1" festgestellt, wird das entsprechende Timerregister auf 0 gesetzt.
'0 Ein Interrupt decrementiert jede Millisekunde alle Timerregister größer 0 um eins.
'4.a Bei einem gesetzten Timerregister fand ein Eingangswechsel von 1 nach 0 statt und der Eingang muß noch auf 1 bleiben
'4.b Bei einem Timerregister = 0 gab es einen Wechsel von 0 nach 1 oder die Zeit lief ab ->
' Die eingelesene "0" kann durchgreifen und zur Ausgabe übernommen bzw. eine eingelesene "1" wird übernommen.
'5. Ausgabe über I2C
'################################################# ###################

Config Timer0 = Timer , Prescale = ... 'für 1ms Interrupts

'time_counter ist ein array für Zeitmessung für einzelne Bits im PIND
Dim Time_counter(8) As Byte

'Initialisierung von Pind_save und output_save
Output_byte = Pind 'erstes Einlesen von PIND
Pind_save = Output_byte

'Erste Ausgabe über I2C
I2c_write(output_byte)

Do 'Hauptschleife

Pind_get = Pind 'Einlesen PIND Register

'Feststellen, welche Eingänge sich geändert haben (entsprechende Pind_change Bits werden zu "1")
Pind_change = Pind_get Xor Pind_save

'Scannen nach geänderten Eingängen
For Bit_no = 0 To 7
'Nur veränderte PIND Eingänge bearbeiten
If Pind_change.bit_no = 1 Then
'Falls Eingang = "0" gab es Wechsel nach 0 -> Timer zum runterzählen setzen, sonst Wechsel nach "1" und Timer auf "0" setzen
If Pind_get.bit_no = 0 Then Time_counter(bit_no) = 200 Else Time_counter(bit_no) = 0
'Wenn Timer läuft, output noch auf "1" lassen sonst gegenwärtigen Zustand "0" oder "1" nach output übernehmen.
If Time_counter(bit_no) > 0 Then Output_byte.bit_no = 1 Else Output_byte.bit_no = Pind_get.bit_no
End If
Next I
Pind_save = Pind_get
I2c_write(output_byte) 'Ausgabe

Loop 'Ende Hauptschleife


Isr_timer: 'soll jede ms aufgerufen werden
For I = 0 To 7
If Time_counter(i) > 0 Then Time_counter(i) = Time_counter(i) - 1 'zählt Zeit für den "1" auf "0" Wechsel runter
Next I
Return

oberallgeier
02.05.2017, 18:56
.. Problem .. Eingänge eines Ports .. sofort übertragen werden .. aber verzögert wieder 0 wird ..Wenn ich Dich nu richtig verstanden habe - so etwas läuft bei mir öfters.
Hintergrund: seit "Urzeiten" läuft auf fast allen meinen Controllern ein Heartbeat, den ich schon anfangs auf 50 µs festgelegt hatte. Diese Routine erledigt nur ein paar Timer, sonst nix. Beispiel Heartbeat. Die Routine zählt 20000 (zwanzigtausend) Interrupts, dann ist genau eine Sekunde rum - und eine LED wird getoggelt <=> solange diese LED blinkt ist der Controller "am Leben". Anfangs musste ich nämlich eigentlich bestimmte Laufzeiten messen, dabei erschienen mir die 50 µs als sinnvoller Kompromiss zwischen Auflösung und Interruptzeit-besetzte-CPU-time. Mittlerweile benutze ich diesen Takt zu mancherlei Zeitmessungen. Bei Dir fiele mir ne Lösung ein wie sie schon oben skizziert wurde:

Du setzt einen Ausgang auf 1 - und der soll nach 200 ms wieder auf 0 gehen.
Nun könnte ich auf meinen Platinen gleichzeitig mit dem "Ausgang-auf-1-setzen" einen separaten Timer>>WERT<< , sagen wir "modtron" auf 4001 setzen - dies entspräche bei meinem 50µs-Timer 200 ms + 50 µs *gg*. Der Wert modtron wird in der Timerroutine runtergezählt und ausgewertet, etwa so:


set(ausgang, 1);
modtron = 4001;
..
// In der Timer-ISR
if ( modtron ) modtron --; // zählt nur bis 0 runter, 1Byte => max 255, 2 Byte max 65535
if ( modtron == 1 ) set(ausgang, 0);
..

Nun wird also der Ausgang 200 ms halten. Durch Setzen von modtron auf verschiedene Werte lässt sich die Abfallverzögerung variieren.

Ist das verständlich ?

modtronic
03.05.2017, 09:14
Hallo Patrick,

Etwas Software brauchst du dazu schon.

Du brauchst für jedes Bit einen Timer. Das kannst du mit einem Timer-Interrupt machen, welcher z.B. alle 1ms aufgerufen wird.
Wenn die Timer >0 wird der Wert jedes Mal um 1 verringert.

Wenn du PORTD einliest, wird jedes Mal für eine gelesene 1 der entsprechende Timer mit 200 beschrieben.

Für die Ausgabe musst für jeden Timer welcher >0 eine 1 ins entsprechende Bit setzen.

So in der Art (nicht getestet):



unsigned char timer[8];

void interrupt(void)
{
char i;

for (i = 0; i < 8; i++)
{
if (timer[i]) timer[i]--;
}
}

void out(void)
{
char i;
unsigned char x = 0;

for (i = 0; i < 8; i++)
{
if (timer[i]) (x |= (1 << i));
}
i2c_write (x);
}

unsigned char in(void)
{
unsigned char x;
char i;

x = PORTD;
for (i = 0; i < 8; i++)
{
if (x & (1 << i)) timer[i] = 200;
}
}


Kann Reste von Tippfehlern enthalten.

MfG Peter(TOO)

Guten Morgen

Vielen Dank. mit dieser Lösung könnte ich etwas anfängen
könntest du mir dazu noch mehr erklären..
zb was hinkommt wo das x ist ??

Danke und Gruss
Patrick

- - - Aktualisiert - - -

Moin

Ich nochmal..manchmal sieht man ja den Wald vor lauter Bäumen nicht.
ich habe eine Lösung gefunden, die zwar nicht so elegant ist wie hier oben beschrieben, aber jetzt auch läuft.

Folgender Weg wurde eingeschlagen.
Der i2c Datenwert besteht ja aus den Werten 0 - 255. Mein Gedanke war wie ich nun jeden einzelnen Pin steuern kann.
Ich habe daher nun die Grundwerte 1, 2,4,8,16,32,64 und 128 auf eine Variable aufgeteilt, die immer nur diese Wertigkeit oder 0 hat.

Ich habe dann diese einzelnen Variablen auf eine Gesamtvariable addiert, und diese dann an den Bus gesendet.
Gleichzeitig werden diese Einzelvariablen immer von je einem PIN des AT-Mega gesteuert. Das erlaubt mir wiederum, jeden Beliebigen Pin zu verwenden und nicht zwingend ein komplettes Byte.
Durch diese Addition wird z.b bei der Variable 1 und 2 eine 3 gesendet, was zwei Ausgänge GPA0 und GPA1 des MCP steuern.
wenn nun die Variable 1 Low wird, und die 2 aber High bleibt, wird die 1 von dem Gesamtwert 3 abgezogen und es wird GPA1 HIGH bleiben und GPA0 wird Low werden.

so kann ich nun jeden einzelnen Pin des MCP gezielt ansprechen.
Wenn ich nun diese Einzelvariable noch im Abfall verzögere (Timer, Zähler) kann ich gezielt eine Abfallzeit für jeden Pin steuern.

Wunderbar.

Gruss
Patte

Peter(TOO)
03.05.2017, 21:17
Hallo Platte,

Ich habe daher nun die Grundwerte 1, 2,4,8,16,32,64 und 128 auf eine Variable aufgeteilt, die immer nur diese Wertigkeit oder 0 hat.
In manchen Programmiersprachen, wie z.B. ursprünglich in BASIC, gab es keinen anderen Weg um einzelne Bits manipulieren zu können. :-)

C/C++ hat aber von Anfang an extra Mechanismen um einzelne Bits manipulieren zu können.

(1 << i) macht auch nichts anderes ;-)
1 << 0 = 1
1 << 1 = 2
1 << 2 = 4

C wurde ursprünglich entwickelt um ein Betriebssystem (Unix) schreiben zu können. Zu einem Betriebssystem gehören auch eine Menge Hardware-Treiber und folglich auch eine Menge Bits, welche bearbeitet werden müssen.

BASIC steht für "Beginner’s All-purpose Symbolic Instruction Code", was so viel bedeutet wie „symbolische Allzweck-Programmiersprache für Anfänger. War also nur für Anwender-Programme gedacht. Auf vielen kleineren Computern wurde BASIC auch als "Betriebssystem" eingesetzt.

Bis Auf Unix waren praktisch alle Betriebssysteme komplett in Assembler programmiert. Für eine neue CPU musste immer alles neu geschrieben werden. Unix bestand dann nur noch aus ein paar Prozent aus Assembler, der ganze Rest war in C geschrieben.

Einen anderen Weg ging, ein paar Jahre später, U.C.S.D.-Pascal. Dabei bastelte man sich eine theoretische CPU zusammen, welche dann auf einer konkreten CPU simuliert wurde. Den Assembler-Code für diese simulierte CPU nannte man P-Code (Pseudo-Code). Man musste dann immer nur den P-Code für den einfachen Interpreter für eine neue CPU neu in Assembler schreiben.
Java verwendet diese Technik heute noch, dort nennt man den P-Code Byte-Code.

MfG Peter(TOO)