Tutorial Arduino programmieren,

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.

Re: Tutorial Arduino programmieren,

Beitragvon Mathias » 25. Mär 2018, 16:31 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_Tutorial_-_I%C2%B2C_EEPROM/de
Mit Lazarus sehe ich gün
Mit Java und C/C++ sehe ich rot
Mathias
 
Beiträge: 3701
Registriert: 2. Jan 2014, 17:21
Wohnort: Schweiz
OS, Lazarus, FPC: Linux (die neusten Trunc) | 
CPU-Target: 64Bit
Nach oben

Beitragvon Sfaizst » 2. Apr 2018, 00:44 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:
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.   
 


Vielen Dank für die Hilfe
Mfg

Edit: Es hat sich doch ein kleiner Fehler beim Senden eingeschlichen, korrigiert...
Zuletzt geändert von Sfaizst am 2. Apr 2018, 11:35, insgesamt 1-mal geändert.
Sfaizst
 
Beiträge: 3
Registriert: 2. Apr 2018, 00:21

Beitragvon kupferstecher » 2. Apr 2018, 10:03 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.
kupferstecher
 
Beiträge: 116
Registriert: 17. Nov 2016, 11:52

Beitragvon Sfaizst » 2. Apr 2018, 11:31 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...
Sfaizst
 
Beiträge: 3
Registriert: 2. Apr 2018, 00:21

Beitragvon Sfaizst » 2. Apr 2018, 11:46 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
Sfaizst
 
Beiträge: 3
Registriert: 2. Apr 2018, 00:21

Beitragvon Timm Thaler » 2. Apr 2018, 11:58 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
Code: Alles auswählen
  while (TWCR and (1 shl TWINT)) = 0 do begin
  end;

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.
Timm Thaler
 
Beiträge: 572
Registriert: 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.64 FPC3.0.4, Raspbian Stretch Laz1.62 FPC3.0.2 | 
CPU-Target: Raspberry Pi 3
Nach oben

Beitragvon Mathias » 2. Apr 2018, 12:26 Re: Tutorial Arduino programmieren,

Das TWIRead müsste ich mal im Tutorial anpassen.
Wie du schon sagst, mit einem Counter und einer Statusvariable.
Mit Lazarus sehe ich gün
Mit Java und C/C++ sehe ich rot
Mathias
 
Beiträge: 3701
Registriert: 2. Jan 2014, 17:21
Wohnort: Schweiz
OS, Lazarus, FPC: Linux (die neusten Trunc) | 
CPU-Target: 64Bit
Nach oben

• Themenende •
Vorherige

Zurück zu Sonstiges



Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast

porpoises-institution
accuracy-worried