Tutorial Arduino programmieren,
-
- Beiträge: 6910
- Registriert: Do 2. Jan 2014, 17:21
- OS, Lazarus, FPC: Linux (die neusten Trunk)
- CPU-Target: 64Bit
- Wohnort: Schweiz
Re: Tutorial Arduino programmieren,
Es ist noch ein Tutorial dazugekommen, wie man ein EEPROM nutzt, welches am I²C-Bus hängt.
http://wiki.freepascal.org/AVR_Embedded ... _EEPROM/de
http://wiki.freepascal.org/AVR_Embedded ... _EEPROM/de
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot
Mit Java und C/C++ sehe ich rot
Re: Tutorial Arduino programmieren,
Hallo zusammen,
habe mich eben extra hier im Forum angemeldet, habe das Tutorial durchgespielt und mirn paar Units zusammen getippelt um mir das ganze leichter zu machen...
Mein Problem ist aber: Ich bekomme I²C einfach nicht zum laufen, war vor 2-3 Wochen schon mal dran, wurmt mich aber immer noch...
Ich programmiere Atmega328p über USB-Programmer getaktet auf 8Mhz ohne externen Quartz (Fusebits entsprechend angepasst, dass er nicht auf 1Mhz läuft), dürfte ja bei einem Bus mit Taktgeber kein Problem sein (schätze ich??).
Ich habe versucht ne RTC (DS3231) über I²C anzusprechen, aber jedes mal, wenn ich den Bus anspreche hängt sich das gesamte Programm auf...
Richtig angeschlossen ist er (PC5 / PC4)...
Eventuell findet ihr ja einen Fehler...
Hier meine Unit für die RTC:
Vielen Dank für die Hilfe
Mfg
Edit: Es hat sich doch ein kleiner Fehler beim Senden eingeschlichen, korrigiert...
habe mich eben extra hier im Forum angemeldet, habe das Tutorial durchgespielt und mirn paar Units zusammen getippelt um mir das ganze leichter zu machen...
Mein Problem ist aber: Ich bekomme I²C einfach nicht zum laufen, war vor 2-3 Wochen schon mal dran, wurmt mich aber immer noch...
Ich programmiere Atmega328p über USB-Programmer getaktet auf 8Mhz ohne externen Quartz (Fusebits entsprechend angepasst, dass er nicht auf 1Mhz läuft), dürfte ja bei einem Bus mit Taktgeber kein Problem sein (schätze ich??).
Ich habe versucht ne RTC (DS3231) über I²C anzusprechen, aber jedes mal, wenn ich den Bus anspreche hängt sich das gesamte Programm auf...
Richtig angeschlossen ist er (PC5 / PC4)...
Eventuell findet ihr ja einen Fehler...
Hier meine Unit für die RTC:
Code: Alles auswählen
unit avr_rtc;
{$mode objfpc}{$H+}
interface
uses
avr_hwi2c_master;
const
RTC_ADDR = $68;
type
Trtc_Time = record
s: Byte;
m: Byte;
h: Byte;
mday: Byte; //Monatstag
wday: Byte; //Wochentag
mon: Byte; //Monat
year: Int16; //Jahr (0..99)
end;
procedure rtc_init;
function rtc_recv(var ATime: Trtc_Time): Boolean;
function rtc_send(ATime: Trtc_Time): Boolean;
implementation
function dec2bcd(ADec: Byte): Byte;
begin
Result := ((ADec div 10 * 16) + (ADec mod 10));
end;
function bcd2dec(ABcd: Byte): Byte;
begin
Result := ((ABcd div 16 * 10) + (ABcd mod 16));
end;
procedure rtc_init;
begin
I2CInit;
end;
function rtc_recv(var ATime: Trtc_Time): Boolean;
var
I: Byte;
Recv: Array [0..6] of Byte;
begin
Result := I2CMasterStart((RTC_ADDR shl 1) or I2C_Write);
If Result = True then
begin
I2CWrite($0);
I2CStop;
Result := I2CMasterStart((RTC_ADDR shl 1) or I2C_Read);
If Result = True then
begin
for I := 0 to 5 do
Recv[I] := I2CRead;
Recv[6] := I2CReadLast;
I2CStop;
ATime.s := Bcd2dec(Recv[0]);
ATime.m := Bcd2dec(Recv[1]);
ATime.h := Bcd2dec(Recv[2]);
ATime.wday := Bcd2dec(Recv[3]);
ATime.mday := Bcd2dec(Recv[4]);
ATime.mon := Bcd2dec(Recv[5] and $1F);
I := (Recv[5] and $80) shr 7;
If I = 1 then
ATime.year := 2000 + bcd2dec(Recv[6]) Else
ATime.year := 1900 + bcd2dec(Recv[6]);
end;
end;
end;
function rtc_send(ATime: Trtc_Time): Boolean;
var
I: Byte;
Send: Array [0..6] of Byte;
begin
Result := I2CMasterStart((RTC_ADDR shl 1) or I2C_Write);
If Result = True then
begin
I2CWrite($0);
Send[0] := Dec2Bcd(ATime.s);
Send[1] := Dec2Bcd(ATime.m);
Send[2] := Dec2Bcd(ATime.h);
Send[3] := Dec2Bcd(ATime.wday);
Send[4] := Dec2Bcd(ATime.mday);
If ATime.year >= 2000 then
begin
I := $80;
Send[6] := ATime.year - 2000;
end Else
begin
I := $0;
Send[6] := ATime.year - 1900;
end;
Send[5] := Dec2Bcd(ATime.mday)+I;
for I := 0 to 6 do
I2CWrite(Send[I]);
I2CStop;
end;
end;
end.
Code: Alles auswählen
unit avr_hwi2c_master;
{$mode objfpc}{$H+}
interface
uses
avr_const;
procedure I2CInit;
procedure I2CUpdateStatus;
function I2CMasterStart(Addr: Byte): Boolean;
procedure I2CStop;
procedure I2CWrite(u8data: byte);
function I2CRead: byte;
function I2CReadLast: byte;
const
I2C_Write = 0;
I2C_Read = 1;
var
//Status des I2C Bus:
TW_STATUS_MASK: Byte;
TW_STATUS: Byte;
implementation
procedure I2CInit;
const
TWBR_val = byte((CPU_Clock div I2C_Speed) - 16) div 2;
begin
TWSR := 0;
TWBR := byte(TWBR_val);
end;
procedure I2CUpdateStatus;
var
TWSR2: bitpacked array [0..7] of boolean absolute TWSR;
TWSRM: bitpacked array [0..7] of boolean absolute TW_STATUS_MASK;
I: Byte;
begin
TW_STATUS_MASK := 0;
for I := 7 downto 3 do
TWSRM[I] := TWSR2[I];
TW_STATUS := TWSR and TW_STATUS_MASK;
end;
function I2CMasterStart(addr: byte): Boolean;
begin
Result := True;
// Senden/Empfangen einleiten
TWCR := 0;
TWCR := (1 shl TWINT) or (1 shl TWSTA) or (1 shl TWEN);
while ((TWCR and (1 shl TWINT)) = 0) do begin
end;
// Adresse des Endgerätes senden
TWDR := addr;
TWCR := (1 shl TWINT) or (1 shl TWEN);
while ((TWCR and (1 shl TWINT)) = 0) do begin
end;
end;
{Versuch den Status vom i²c Bus mit zu überprüfen, Unterschied = 0...
function I2CMasterStart(Addr: byte): Boolean;
const
TW_START = $08;
TW_REP_START = $10;
TW_MT_SLA_ACK = $18;
TW_MR_SLA_ACK = $40;
var
TWST: Byte;
begin
Result := False;
// Senden/Empfangen einleiten
TWCR := 0;
TWCR := (1 shl TWINT) or (1 shl TWSTA) or (1 shl TWEN);
while ((TWCR and (1 shl TWINT)) = 0) do begin
end;
I2CUpdateStatus;
TWST := TW_STATUS and $F8;
if (TWST = TW_START) or (TWST = TW_REP_START) then
begin
// Adresse des Endgerätes senden
TWDR := addr;
TWCR := (1 shl TWINT) or (1 shl TWEN);
while ((TWCR and (1 shl TWINT)) = 0) do begin
end;
I2CUpdateStatus;
TWST := TW_STATUS and $F8;
if (TWST <> TW_MT_SLA_ACK) and (TWST <> TW_MR_SLA_ACK) then
Result := True;
end;
end; }
procedure I2CStop;
begin
TWCR := (1 shl TWINT) or (1 shl TWSTO) or (1 shl TWEN);
end;
procedure I2CWrite(u8data: byte);
begin
TWDR := u8data;
TWCR := (1 shl TWINT) or (1 shl TWEN);
while ((TWCR and (1 shl TWINT)) = 0) do begin
end;
end;
// Lesen bis zum vorletzten Zeichen.
function I2CRead: byte;
begin
TWCR := (1 shl TWINT) or (1 shl TWEN) or (1 shl TWEA);
while (TWCR and (1 shl TWINT)) = 0 do begin
end;
Result := TWDR;
end;
// Letztes Zeichen lesen.
function I2CReadLast: byte;
begin
TWCR := (1 shl TWINT) or (1 shl TWEN);
while (TWCR and (1 shl TWINT)) = 0 do begin
end;
Result := TWDR;
end;
end.
Code: Alles auswählen
unit avr_const;
{$mode objfpc}{$H+}
interface
const
{
CPU_Clock: Bei Start des Programms einzustellen,
wenn nicht 16MHZ
(16MHZ nur bei Arduino oder mit Externem Kristall).
Flashing Script beachten!
}
//CPU_Clock = 1000000; // 1MHZ Fuses auf 1/8 gesetzt
CPU_Clock = 8000000; // 8MHZ Fuses auf int. Osc. ohne 1/8 gesetzt
//CPU_Clock = 16000000; //16MHZ Fuses auf ext. Osc. (Kristall) gesetzt (z.b. im Arduino Uno)
{
Uart / Serielle Verbindung / Nachrichtenanzeige:
}
uart_Baud = 9600; // Baudrate
uart_Div = CPU_Clock div (16 * uart_Baud) - 1;
i2c_Speed = 400000;
implementation
end.
Mfg
Edit: Es hat sich doch ein kleiner Fehler beim Senden eingeschlichen, korrigiert...
Zuletzt geändert von Sfaizst am Mo 2. Apr 2018, 12:35, insgesamt 1-mal geändert.
- kupferstecher
- Beiträge: 431
- Registriert: Do 17. Nov 2016, 11:52
Re: Tutorial Arduino programmieren,
Hallo Sfaizst,
Du sagst, dass das Programm hängenbleibt, das kann ja eigentlich nur bei einer der Flag-Abfragen der Fall sein. ("while ((TWCR and (1 shl TWINT)) = 0) do begin end;"). Also wenn das Flag nie umschällt. Du müsstest rauszufinden an welcher Stelle es genau hängen bleibt. Z.B. kannst du jeweils vor dem Senden/Empfangen eines Bytes den Status per UART an den Computer schicken.
Du sagst, dass das Programm hängenbleibt, das kann ja eigentlich nur bei einer der Flag-Abfragen der Fall sein. ("while ((TWCR and (1 shl TWINT)) = 0) do begin end;"). Also wenn das Flag nie umschällt. Du müsstest rauszufinden an welcher Stelle es genau hängen bleibt. Z.B. kannst du jeweils vor dem Senden/Empfangen eines Bytes den Status per UART an den Computer schicken.
Re: Tutorial Arduino programmieren,
Keine Ahnung, was ich heute anders gemacht habe... aber es funktioniert... (Das erste Erfolgserlebnis mit I²C)
Habe die Vermutung, dass die Leitungen aufm Breadboard nicht immer sauber steckten...
Zudem scheint die RTC wirklich sehr viel Wert auf die 400khz beim I²C zu legen, langsamere Verbindungen hat sie bei mir stets verweigert...
Uhrzeit ist zwar noch nicht gestellt, aber die Zeit vergeht schon mal stimmig (habe nur das Auslesen probiert)...
Zeit: 15:42:25
Datum: 1.1.1900
Am Quelltext oben musste ich nichts verändern...
Trotzdem Vielen Dank, wenn gewollt kann der Quelltext weiter verwendet werden...
Habe die Vermutung, dass die Leitungen aufm Breadboard nicht immer sauber steckten...
Zudem scheint die RTC wirklich sehr viel Wert auf die 400khz beim I²C zu legen, langsamere Verbindungen hat sie bei mir stets verweigert...
Uhrzeit ist zwar noch nicht gestellt, aber die Zeit vergeht schon mal stimmig (habe nur das Auslesen probiert)...
Zeit: 15:42:25
Datum: 1.1.1900
Am Quelltext oben musste ich nichts verändern...
Trotzdem Vielen Dank, wenn gewollt kann der Quelltext weiter verwendet werden...
Re: Tutorial Arduino programmieren,
Noch eine Frage...
Gibt es keine Möglichkeit, dass man den I²C so umprogrammiert, dass er nicht das ganze Programm blockiert, sollte etwas nicht stimmen (z.B.: der Slave nicht erreichbar sein)?
Was für Interrupts würde es geben? Kann man die (endlos) Schleifen bis zu einem Maximalwert hochzählen lassen und dann abbrechen o.ä.?
Ich würde es gerne Vermeiden so eine Falle in einem System zu haben... (ich plane eine Terrariumssteuerung für nen Kumpel zu basteln, wenn sich da dann das komplette Programm aufhängt wäre das nicht sonderlich praktisch)...
Vielen Dank
Gibt es keine Möglichkeit, dass man den I²C so umprogrammiert, dass er nicht das ganze Programm blockiert, sollte etwas nicht stimmen (z.B.: der Slave nicht erreichbar sein)?
Was für Interrupts würde es geben? Kann man die (endlos) Schleifen bis zu einem Maximalwert hochzählen lassen und dann abbrechen o.ä.?
Ich würde es gerne Vermeiden so eine Falle in einem System zu haben... (ich plane eine Terrariumssteuerung für nen Kumpel zu basteln, wenn sich da dann das komplette Programm aufhängt wäre das nicht sonderlich praktisch)...
Vielen Dank
-
- Beiträge: 1224
- Registriert: So 20. Mär 2016, 22:14
- OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
- CPU-Target: Raspberry Pi 3
Re: Tutorial Arduino programmieren,
Also erstens: Kann bitte ein Mod den TWI-Teil abtrennen und in einen eigene Thread packen, das wird hier unübersichtlich.
Zweitens: Die RTC sollte problemlos auch mit 100kHz oder weniger laufen.
Drittens: Das Hardware-TWI ist "empfindlich" für Busfehler.
Das Problem ist bei TWI / I2C prinzipiell, dass ein Slave theoretisch den Bus komplett stillegen kann, wenn er Clock Stretch macht (der Slave legt SCL solange auf low, bis er bereit ist zu antworten) und sich dabei aufhängt, oder wenn es einen Kurzschluss auf SCL gibt. Die Hardware-TWI wartet dann ewig darauf, dass der Bus fertig wird. Sowas kann auch bei Timing-Fehlern zwischen SDA und SCL passieren, die schon auftreten können wenn eine Oszi-Leitung dranhängt und deren Kapazität das Signal verzögert.
Das Problem ist jetzt, dass dann solche Schleifen wie
nie fertig werden, weil der Controller auf das Zeichen wartet welches aber nicht kommt. Man sollte solche potentiellen "Endlosschleifen" immer mit einer Abbruchbedingung durch ein Timeout versehen: Also einen Zähler mitführen, und wenn der abläuft die Schleife mit einem Fehler (Noack) verlassen.
Zweitens: Die RTC sollte problemlos auch mit 100kHz oder weniger laufen.
Drittens: Das Hardware-TWI ist "empfindlich" für Busfehler.
Das Problem ist bei TWI / I2C prinzipiell, dass ein Slave theoretisch den Bus komplett stillegen kann, wenn er Clock Stretch macht (der Slave legt SCL solange auf low, bis er bereit ist zu antworten) und sich dabei aufhängt, oder wenn es einen Kurzschluss auf SCL gibt. Die Hardware-TWI wartet dann ewig darauf, dass der Bus fertig wird. Sowas kann auch bei Timing-Fehlern zwischen SDA und SCL passieren, die schon auftreten können wenn eine Oszi-Leitung dranhängt und deren Kapazität das Signal verzögert.
Das Problem ist jetzt, dass dann solche Schleifen wie
Code: Alles auswählen
while (TWCR and (1 shl TWINT)) = 0 do begin
end;
-
- Beiträge: 6910
- Registriert: Do 2. Jan 2014, 17:21
- OS, Lazarus, FPC: Linux (die neusten Trunk)
- CPU-Target: 64Bit
- Wohnort: Schweiz
Re: Tutorial Arduino programmieren,
Das TWIRead müsste ich mal im Tutorial anpassen.
Wie du schon sagst, mit einem Counter und einer Statusvariable.
Wie du schon sagst, mit einem Counter und einer Statusvariable.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot
Mit Java und C/C++ sehe ich rot
-
- Beiträge: 6910
- Registriert: Do 2. Jan 2014, 17:21
- OS, Lazarus, FPC: Linux (die neusten Trunk)
- CPU-Target: 64Bit
- Wohnort: Schweiz
Re: Tutorial Arduino programmieren,
Dies habe ich gerade ergänzt, wen bei mehreren While-Durchläufen nicht passiert, wird die Schleife mit einem Fehler abgebrochen. Der AVR bleibt dann nicht mehr hängen.Das TWIRead müsste ich mal im Tutorial anpassen.
Anstelle von SendString, könnten man auch eine Variable ErrorState benutzen.
Code: Alles auswählen
function TWIReadACK_Error: byte;
var
err:byte;
begin
err := 0;
TWCR := (1 shl TWINT) or (1 shl TWEN) or (1 shl TWEA);
while ((TWCR and (1 shl TWINT)) = 0) and (err < 255) do begin
Inc(err);
end;
if err = 255 then begin
UARTSendString('I²C-Timeout');
Result := 0;
end else begin
Result := TWDR;
end;
end;

Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot
Mit Java und C/C++ sehe ich rot
-
- Beiträge: 6910
- Registriert: Do 2. Jan 2014, 17:21
- OS, Lazarus, FPC: Linux (die neusten Trunk)
- CPU-Target: 64Bit
- Wohnort: Schweiz
Re: Tutorial Arduino programmieren,
Ich habe dein Turorial zu der Software I²C angeguckt.Timm Thaler hat geschrieben:Ich hab mal noch ein paar AVR Tutorials eingeworfen:
http://wiki.freepascal.org/AVR_Embedded_-_Delays
http://wiki.freepascal.org/AVR_Embedded ... _I2C_/_TWI
http://wiki.freepascal.org/AVR_Embedded_-_SPI
Dies kann gar nicht funktionieren, da du DDRx und PORTx verwechselt hast.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot
Mit Java und C/C++ sehe ich rot
-
- Beiträge: 1224
- Registriert: So 20. Mär 2016, 22:14
- OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
- CPU-Target: Raspberry Pi 3
Re: Tutorial Arduino programmieren,
Oh, das muss ich gleich meiner Heizung mit MAX127, DS1307, PCF8574, meinem Displayexpander mit DS13017 und meiner Wetterstation mit BCM180, HTC21 erzählen.Mathias hat geschrieben:Dies kann gar nicht funktionieren, da du DDRx und PORTx verwechselt hast.
Entweder funktionieren die mit Magie - oder Du hast nicht verstanden, wie I2C funktioniert.
-
- Beiträge: 6910
- Registriert: Do 2. Jan 2014, 17:21
- OS, Lazarus, FPC: Linux (die neusten Trunk)
- CPU-Target: 64Bit
- Wohnort: Schweiz
Re: Tutorial Arduino programmieren,
Was mich verwundert, das man mit dem Konfiguration-Register Daten ausgeben kann.
Da es bei dir funktioniert, hast du sicher nichts neues erfunden.
Ich habe noch ein C++ - Code angeguckt, dort ist es auch mit DDR. http://extremeelectronics.co.in/avrtuto ... or_AVR.zip
Da es bei dir funktioniert, hast du sicher nichts neues erfunden.

Ich habe noch ein C++ - Code angeguckt, dort ist es auch mit DDR. http://extremeelectronics.co.in/avrtuto ... or_AVR.zip
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot
Mit Java und C/C++ sehe ich rot
-
- Beiträge: 1224
- Registriert: So 20. Mär 2016, 22:14
- OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
- CPU-Target: Raspberry Pi 3
Re: Tutorial Arduino programmieren,
Also nochmal für Dich, I2C und TWI funktionieren so, dass für ein logisch Null der Master oder der Slave den Bus auf GND ziehen. Für ein logisch Eins "lassen" Master oder Slave den Bus "los", und die Pull-Up-Widerstände ziehen den Bus gegen VCC. Deswegen ist I2C im Vergeich zu anderen Bustypen auch recht langsam und in der Leitungslänge begrenzt, weil hier die Pull-Up-Widerstände gegen die Leitungskapazität arbeiten müssen.Mathias hat geschrieben:Was mich verwundert, das man mit dem Konfiguration-Register Daten ausgeben kann.
Wäre das anders, könnte ein Low am Master und ein Eins am Slave einen Kurzschluss erzeugen, oder Multi-Slave-Betrieb wäre nicht möglich. Andere Bustypen, die mit aktiven Low- und High-Pegeln arbeiten, müssen das anders lösen, zum Beispiel durch Kollisionerkennung, Strombegrenzung oder unidirektionale Übertragung.
Technisch gesehen verwenden Master und Slave jeweils Open-Collector-Ausgänge, der obere Transistor im Ausgangstreiber wird dabei nicht verwendet. Und bei den AVRs kann man dieses Open-Collector-Verhalten am Einfachsten erreichen, indem man den Port auf low läßt und die Datenrichtung zwischen Eingang (offen) und Ausgang (gegen GND geschlossen) umschaltet.
-
- Beiträge: 6910
- Registriert: Do 2. Jan 2014, 17:21
- OS, Lazarus, FPC: Linux (die neusten Trunk)
- CPU-Target: 64Bit
- Wohnort: Schweiz
Re: Tutorial Arduino programmieren,
Kurz gesagt, High, wird mit den Pullup Widerständen erzeugt ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot
Mit Java und C/C++ sehe ich rot
Re: Tutorial Arduino programmieren,
kurz gesagt, ja
Gruß Horst
Gruß Horst
- kupferstecher
- Beiträge: 431
- Registriert: Do 17. Nov 2016, 11:52
Re: Tutorial Arduino programmieren,
Was heisst mit "den" Pullup Widerstaenden?Mathias hat geschrieben:Kurz gesagt, High, wird mit den Pullup Widerständen erzeugt ?
Nicht mit den in den Ports integrierten, diese waeren nur aktiv, wenn das Port-Register 1 waere (bei Eingang).
Hier muss man im Schaltplan explizit Pullups vorsehen fuer die High-Pegel.