Lazarus on Raspberry Pi/de
│
Deutsch (de) │
English (en) │
español (es) │
suomi (fi) │
中文(中国大陆) (zh_CN) │
Dieser Artikel behandelt ausschließlich Raspberry Pi.
Siehe auch: Multiplatform Programming Guide/de
Der Raspberry Pi ist ein kreditkartengroßer Einplatinenrechner. Er wurde in Großbritannien von der Raspberry Pi Foundation ursprünglich entwickelt, um den grundlegenden Informatikunterricht in Schulen zu fördern. Raspberry Pis können aber nicht nur für die Ausbildung, sondern für die unterschiedlichsten Zwecke, z. B. als Medienserver, Robotersteuerungen und für die Regelungstechnik eingesetzt werden.
Die Raspberry Pi Foundation empfiehlt als Standard-Betriebssystem Raspbian Wheezy. Alternative Betriebssysteme für den RPI sind z. B. RISC OS und verschiedene Linux-Distributionen, einschließlich Android.
Lazarus läuft nativ unter dem Betriebssystem Raspbian.
Lazarus installieren und/oder compilieren
Einfache Installation unter Raspbian
Hinweis:
In der Wheezy Version (2014-09-09) muss folgende Paketquelle in etc/apt/sources.list hinzugefügt werden.
deb http://plugwash.raspbian.org/wsf wheezy-wsf main
Diese Information bezieht sich auf den Artikel:
Raspberry Pi 1
Lazarus und Free Pascal können unter Raspbian OS schnell und einfach installiert werden. Es ist dafür nur nötig, ein Terminal-Fenster zu öffnen und die folgenden Kommandos einzugeben:
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install fpc
sudo apt-get install lazarus
Damit wird, natürlich unter der Voraussetzung einer funktionierenden Netzwerkverbindung, eine vorkompilierte Lazarus-Version auf dem Raspberry Pi installiert. Die Installation kann, je nach Version und Netzwerkgeschwindigkeit, etwa 30 Minuten erfordern, sie läuft aber überwiegend automatisch ab. Nach der Installation kann Lazarus direkt über das "Programming"-Untermenü des LXDE Startmenüs gestartet werden.
Raspberry Pi 2
Seit Juni 2015 funktioniert die oben angeführte "out of the box" Installationsmethode auch auf dem Raspberry Pi 2 Model B. Allerdings ist nach gegenwärtigem Stand die installierte Version recht alt. Wer die neuesten Versionen des Free Pascal Compilers und der Lazarus IDE installieren möchte, findet eine Anleitung in diesem Artikel.
Übersetzen aus den Quelltexten
Alternativ kann Lazarus auch aus den Subversion-Quelltexten übersetzt werden. Einzelheiten sind unter Michell Computing: Lazarus on the Raspberry Pi beschrieben.
Zugriff auf externe Hardware
Eines der Ziele bei der Entwicklung des Raspberry Pi bestand darin, einfachen und schnellen Zugriff auf esterne Geräte wie Sensoren und Aktuatoren zu ermöglichen. Es gibt vier Möglichkeiten, die E/A-Optionen unter Lazarus und Free Pascal zu nutzen:
- Direktzugriff über die BaseUnix-Unit
- Zugriff über gekapselte Shell-Aufrufe
- Zugriff über die wiringPi library.
- Zugriff über die Unit rpi_hal.
1. Nativer Hardware-Zugriff
Diese Methode bietet Zugriff auf externe Hardware, ohne zusätzliche Bibliotheken oder Packages zu benötigen. Die einzige Voraussetzung ist die BaseUnix-Bibliothek, die zur RTL gehört und mit Free Pascal automatisch mitinstalliert wird.
Schalten eines Gerätes über den GPIO-Port
Im folgenden Beispiel ist ein einfaches Programm aufgelistet, das den GPIO-Pin 17 als Ausgang zum Schalten einer LED, eines Transistors oder eines Relais benutzt. Es enthält eine ToggleBox mit dem Namen GPIO17ToggleBox und zur Aufzeichnung von Return-Codes ein TMemo-Feld mit der Bezeichnung LogMemo.
Hardwareseitig wurde die Anode einer LED mit Pin 11 des Anschlusses auf dem Pi (entsprechend GPIO-Pin 17 des BCM2835 SOC) und ihre Kathode über einen 68-Ohm-Schutzwiderstand mit Pin 6 des Pi-Anschlusses (GND = Masse) verbunden (s. auch die Beschreibung bei Upton and Halfacree). Damit kann die LED durch die ToggleBox des Programms ein- und ausgeschaltet werden.
Das Programm muss mit Root-Rechten ausgeführt werden, d. h. entweder von einem Root-Account aus (aus Sicherheitsgründen weniger zu empfehlen) oder über das Shell-Kommando su.
Steuerungs-Unit:
unit Unit1;
{Demo application for GPIO on Raspberry Pi}
{Inspired by the Python input/output demo application by Gareth Halfacree}
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
Unix, BaseUnix;
type
{ TForm1 }
TForm1 = class(TForm)
LogMemo: TMemo;
GPIO17ToggleBox: TToggleBox;
procedure FormActivate(Sender: TObject);
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
procedure GPIO17ToggleBoxChange(Sender: TObject);
private
{ private declarations }
public
{ public declarations }
end;
const
PIN_17: PChar = '17';
PIN_ON: PChar = '1';
PIN_OFF: PChar = '0';
OUT_DIRECTION: PChar = 'out';
var
Form1: TForm1;
gReturnCode: longint; {stores the result of the IO operation}
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.FormActivate(Sender: TObject);
var
fileDesc: integer;
begin
{ Prepare SoC pin 17 (pin 11 on GPIO port) for access: }
try
fileDesc := fpopen('/sys/class/gpio/export', O_WrOnly);
gReturnCode := fpwrite(fileDesc, PIN_17[0], 2);
LogMemo.Lines.Add(IntToStr(gReturnCode));
finally
gReturnCode := fpclose(fileDesc);
LogMemo.Lines.Add(IntToStr(gReturnCode));
end;
{ Set SoC pin 17 as output: }
try
fileDesc := fpopen('/sys/class/gpio/gpio17/direction', O_WrOnly);
gReturnCode := fpwrite(fileDesc, OUT_DIRECTION[0], 3);
LogMemo.Lines.Add(IntToStr(gReturnCode));
finally
gReturnCode := fpclose(fileDesc);
LogMemo.Lines.Add(IntToStr(gReturnCode));
end;
end;
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
var
fileDesc: integer;
begin
{ Free SoC pin 17: }
try
fileDesc := fpopen('/sys/class/gpio/unexport', O_WrOnly);
gReturnCode := fpwrite(fileDesc, PIN_17[0], 2);
LogMemo.Lines.Add(IntToStr(gReturnCode));
finally
gReturnCode := fpclose(fileDesc);
LogMemo.Lines.Add(IntToStr(gReturnCode));
end;
end;
procedure TForm1.GPIO17ToggleBoxChange(Sender: TObject);
var
fileDesc: integer;
begin
if GPIO17ToggleBox.Checked then
begin
{ Swith SoC pin 17 on: }
try
fileDesc := fpopen('/sys/class/gpio/gpio17/value', O_WrOnly);
gReturnCode := fpwrite(fileDesc, PIN_ON[0], 1);
LogMemo.Lines.Add(IntToStr(gReturnCode));
finally
gReturnCode := fpclose(fileDesc);
LogMemo.Lines.Add(IntToStr(gReturnCode));
end;
end
else
begin
{ Switch SoC pin 17 off: }
try
fileDesc := fpopen('/sys/class/gpio/gpio17/value', O_WrOnly);
gReturnCode := fpwrite(fileDesc, PIN_OFF[0], 1);
LogMemo.Lines.Add(IntToStr(gReturnCode));
finally
gReturnCode := fpclose(fileDesc);
LogMemo.Lines.Add(IntToStr(gReturnCode));
end;
end;
end;
end.
Hauptprogramm:
program io_test;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset
Forms, Unit1
{ you can add units after this };
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Den Status eines Anschlusses lesen
Umgekehrt ist es natürlich auch möglich, den Status z. B. eines Schalters abzufragen, der mit dem GPIO-Port verbunden ist.
Das folgende einfache Beispiel ist dem vorherigen sehr ähnlich. Es benutzt den GPIO-Pin 18 als Eingang für ein binäres Gerät, z. B. einen Schalter, einen Transistor oder ein Relais. Das Programm enthält eine CheckBox mit der Bezeichnung GPIO18CheckBox und zur Aufzeichnung von Return-Codes ein TMemo-Feld mit dem Namen LogMemo.
Im Beispiel wurde ein Pol eines Tasters mit Pin 12 an der Anschlussleiste des Pi (entsprechend GPIO-Pin 18 des BCM2835 SOCs) und über einen 10-kOhm Pull-Up-Widerstand mit Pin 1 (+3.3V, siehe Schaltplan) verbunden. Der andere Pol wurde mit Pin 6 der Anschlussleiste (GND = Masse) verdrahtet. Das Programm liest den Status des Tasters aus und schaltet dementsprechend die CheckBox an oder aus.
Das Potential des Pins 18 ist hoch, wenn der Taster nicht gedrückt wird (wegen der Verbindung mit Pin 1 über den Pull-Up-Widerstand) und niedrig, wenn er gedrückt wird (da in dieser Situation Pin 18 über den Taster mit Masse verbunden wird). Der GPIO-Pin meldet daher 0, wenn der Taster gedrückt wird und 1, wenn er losgelassen wird.
Auch dieses Programm ist mit Root-Rechten auszuführen.
Steuerung-Unit:
unit Unit1;
{Demo application for GPIO on Raspberry Pi}
{Inspired by the Python input/output demo application by Gareth Halfacree}
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}
{This application reads the status of a push-button}
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
ButtonPanel, Unix, BaseUnix;
type
{ TForm1 }
TForm1 = class(TForm)
ApplicationProperties1: TApplicationProperties;
GPIO18CheckBox: TCheckBox;
LogMemo: TMemo;
procedure ApplicationProperties1Idle(Sender: TObject; var Done: Boolean);
procedure FormActivate(Sender: TObject);
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
private
{ private declarations }
public
{ public declarations }
end;
const
PIN_18: PChar = '18';
IN_DIRECTION: PChar = 'in';
var
Form1: TForm1;
gReturnCode: longint; {stores the result of the IO operation}
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.FormActivate(Sender: TObject);
var
fileDesc: integer;
begin
{ Prepare SoC pin 18 (pin 12 on GPIO port) for access: }
try
fileDesc := fpopen('/sys/class/gpio/export', O_WrOnly);
gReturnCode := fpwrite(fileDesc, PIN_18[0], 2);
LogMemo.Lines.Add(IntToStr(gReturnCode));
finally
gReturnCode := fpclose(fileDesc);
LogMemo.Lines.Add(IntToStr(gReturnCode));
end;
{ Set SoC pin 18 as input: }
try
fileDesc := fpopen('/sys/class/gpio/gpio18/direction', O_WrOnly);
gReturnCode := fpwrite(fileDesc, IN_DIRECTION[0], 2);
LogMemo.Lines.Add(IntToStr(gReturnCode));
finally
gReturnCode := fpclose(fileDesc);
LogMemo.Lines.Add(IntToStr(gReturnCode));
end;
end;
procedure TForm1.ApplicationProperties1Idle(Sender: TObject; var Done: Boolean);
var
fileDesc: integer;
buttonStatus: string[1] = '1';
begin
try
{ Open SoC pin 18 (pin 12 on GPIO port) in read-only mode: }
fileDesc := fpopen('/sys/class/gpio/gpio18/value', O_RdOnly);
if fileDesc > 0 then
begin
{ Read status of this pin (0: button pressed, 1: button released): }
gReturnCode := fpread(fileDesc, buttonStatus[1], 1);
LogMemo.Lines.Add(IntToStr(gReturnCode) + ': ' + buttonStatus);
LogMemo.SelStart := Length(LogMemo.Lines.Text) - 1;
if buttonStatus = '0' then
GPIO18CheckBox.Checked := true
else
GPIO18CheckBox.Checked := false;
end;
finally
{ Close SoC pin 18 (pin 12 on GPIO port) }
gReturnCode := fpclose(fileDesc);
LogMemo.Lines.Add(IntToStr(gReturnCode));
LogMemo.SelStart := Length(LogMemo.Lines.Text) - 1;
end;
end;
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
var
fileDesc: integer;
begin
{ Free SoC pin 18: }
try
fileDesc := fpopen('/sys/class/gpio/unexport', O_WrOnly);
gReturnCode := fpwrite(fileDesc, PIN_18[0], 2);
LogMemo.Lines.Add(IntToStr(gReturnCode));
finally
gReturnCode := fpclose(fileDesc);
LogMemo.Lines.Add(IntToStr(gReturnCode));
end;
end;
end.
Das Hauptprogramm ist identisch zu dem des obigen Beispiels.
2. Hardware-Zugriff über gekapselte Shell-Aufrufe
Eine andere Möglichkeit, auf Hardware zuzugreifen, besteht in der Nutzung gekapselter Shell-Aufrufe. Dies geschieht mit Hilfe der fpsystem-Funktion. Auf diese Weise ist es möglich, auf Funktionen zuzugreifen, die von der BaseUnix-Unit nicht unterstützt werden. Der folgende Code hat die selbe Funktionalität wie das Programm des ersten Listings oben.
Steuerungs-Unit:
unit Unit1;
{Demo application for GPIO on Raspberry Pi}
{Inspired by the Python input/output demo application by Gareth Halfacree}
{written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5}
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Unix;
type
{ TForm1 }
TForm1 = class(TForm)
LogMemo: TMemo;
GPIO17ToggleBox: TToggleBox;
procedure FormActivate(Sender: TObject);
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
procedure GPIO17ToggleBoxChange(Sender: TObject);
private
{ private declarations }
public
{ public declarations }
end;
var
Form1: TForm1;
gReturnCode: longint; {stores the result of the IO operation}
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.FormActivate(Sender: TObject);
begin
{ Prepare SoC pin 17 (pin 11 on GPIO port) for access: }
gReturnCode := fpsystem('echo "17" > /sys/class/gpio/export');
LogMemo.Lines.Add(IntToStr(gReturnCode));
{ Set SoC pin 17 as output: }
gReturnCode := fpsystem('echo "out" > /sys/class/gpio/gpio17/direction');
LogMemo.Lines.Add(IntToStr(gReturnCode));
end;
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
{ Free SoC pin 17: }
gReturnCode := fpsystem('echo "17" > /sys/class/gpio/unexport');
LogMemo.Lines.Add(IntToStr(gReturnCode));
end;
procedure TForm1.GPIO17ToggleBoxChange(Sender: TObject);
begin
if GPIO17ToggleBox.Checked then
begin
{ Swith SoC pin 17 on: }
gReturnCode := fpsystem('echo "1" > /sys/class/gpio/gpio17/value');
LogMemo.Lines.Add(IntToStr(gReturnCode));
end
else
begin
{ Switch SoC pin 17 off: }
gReturnCode := fpsystem('echo "0" > /sys/class/gpio/gpio17/value');
LogMemo.Lines.Add(IntToStr(gReturnCode));
end;
end;
end.
Das Hauptprogramm ist identisch zu dem der obigen Beispiele. Es erfordert wieder Root-Rechte.
3. wiringPi-Prozeduren und Funktionen
Mit Hilfe von Alex Schallers Wrapper-Unit für Gordon Hendersons Arduino-kompatible wiringPi-Bibliothek kann ein Nummerierungs-Schema verwendet werden, das dem der Arduino-Boards entspricht.
Function wiringPiSetup:longint; {Initialisiert das wiringPi-System entsprechend dem wiringPi Pin-Nummerierungs-Schema.}
Procedure wiringPiGpioMode(mode:longint); {Initialisiert das wiringPi-System mit dem Broadcom GPIO-Pin-Nummerierungs-Schema.}
Procedure pullUpDnControl(pin:longint; pud:longint); {steuert die internen pull-up/down-Widerstände eines GPIO-Pins.}
Procedure pinMode(pin:longint; mode:longint); {setzt den Betriebsmodus eines Pins auf entweder INPUT, OUTPUT oder PWM_OUTPUT.}
Procedure digitalWrite(pin:longint; value:longint); {setzt ein Ausgabe-Bit.}
Procedure pwmWrite(pin:longint; value:longint); {setzt einen Ausgabe-PWM-Wert zwischen 0 und 1024.}
Function digitalRead(pin:longint):longint; {liest den Wert des übergebenen Pins, gibt 1 oder 0.}
Procedure delay(howLong:dword); {wartet howLong Millisekunden.}
Procedure delayMicroseconds(howLong:dword); {wartet howLong Mikrosekunden.}
Function millis:dword; {gibt die Zahl der Millisekunden zurück, seit das Programm eine der wiringPiSetup-Funktionen aufgerufen hat.}
4. rpi_hal-Hardware Abstraction Library (GPIO, I2C und SPI Funktionen und Prozeduren)
Diese Unit mit ca. 1700 Lines of Code wird von Stefan Fischer zur Verfügung gestellt und bietet Prozeduren und Funktionen, die auf die rpi HW Zugriff auf I2C, SPI und GPIO bietet:
Ein Auszug der verfügbaren Funktionen und Prozeduren:
procedure gpio_set_pin (pin:longword;highlevel:boolean); { Set RPi GPIO pin to high or low level; Speed @ 700MHz -> 0.65MHz }
function gpio_get_PIN (pin:longword):boolean; { Get RPi GPIO pin Level is true when Pin level is '1'; false when '0'; Speed @ 700MHz -> 1.17MHz }
procedure gpio_set_input (pin:longword); { Set RPi GPIO pin to input direction }
procedure gpio_set_output(pin:longword); { Set RPi GPIO pin to output direction }
procedure gpio_set_alt (pin,altfunc:longword); { Set RPi GPIO pin to alternate function nr. 0..5 }
procedure gpio_set_gppud (mask:longword); { set RPi GPIO Pull-up/down Register (GPPUD) with mask }
...
function rpi_snr :string; { delivers SNR: 0000000012345678 }
function rpi_hw :string; { delivers Processor Type: BCM2708 }
function rpi_proc:string; { ARMv6-compatible processor rev 7 (v6l) }
...
function i2c_bus_write(baseadr,reg:word; var data:databuf_t; lgt:byte; testnr:integer) : integer;
function i2c_bus_read (baseadr,reg:word; var data:databuf_t; lgt:byte; testnr:integer) : integer;
function i2c_string_read(baseadr,reg:word; var data:databuf_t; lgt:byte; testnr:integer) : string;
function i2c_string_write(baseadr,reg:word; s:string; testnr:integer) : integer;
...
procedure SPI_Write(devnum:byte; reg,data:word);
function SPI_Read(devnum:byte; reg:word) : byte;
procedure SPI_BurstRead2Buffer (devnum,start_reg:byte; xferlen:longword);
procedure SPI_BurstWriteBuffer (devnum,start_reg:byte; xferlen:longword); { Write 'len' Bytes from Buffer SPI Dev startig at address 'reg' }
...
Test Program (testrpi.pas):
//Simple Test program, which is using rpi_hal;
program testrpi;
uses rpi_hal;
begin
writeln('Show CPU-Info, RPI-HW-Info and Registers:');
rpi_show_all_info;
writeln('Let Status LED Blink. Using GPIO functions:');
GPIO_PIN_TOGGLE_TEST;
writeln('Test SPI Read function. (piggy back board with installed RFM22B Module is required!)');
Test_SPI;
end.
5. PascalIO
Mit dem Package PascalIO kann objektorientiert auf die Schnittstellen zugegriffen werden. Es beinhaltet Klassen für die GPIO-Pins sowie für den I2C- und SPI-Bus.
GPIO
Input
program Project1;
uses fpgpio;
var
pin_21: TGpioPin;
begin
pin_21 := TGpioLinuxPin.Create(21);
pin_21.Direction := gdIn;
WriteLn('Pin 21 hat den Wert: ', pin_21.Value);
pin_21.Destroy;
end.
Output
program Project1;
uses sysutils, fpgpio;
var
i: Integer;
pin_21: TGpioPin;
begin
pin_21 := TGpioLinuxPin.Create(21);
pin_21.Direction := gdOut;
for i := 0 to 10 do
begin
pin_21.Value := (i and $01) = 0;
sleep(1000);
end;
pin_21.Destroy;
end.
Interrupt
program Project1;
uses fpgpio;
var
pin_21: TGpioPin;
NewValue: Boolean;
begin
pin_21 := TGpioLinuxPin.Create(21);
pin_21.Direction := gdIn;
pin_21.InterruptMode := [gimRising, gimFalling];
try
repeat
if input.WaitForInterrupt(TGpioLinuxPin.INTERRUPT_WAIT_INFINITE, NewValue) then
Writeln('Interrupt on Pin ', input.PinID, ' with value ', NewValue)
else
WriteLn('Timeout');
until False;
except
// e.g. on sig int
pin_21.Destroy;
end;
end.
Literatur
- Eben Upton and Gareth Halfacree. Raspberry Pi User Guide. John Wiley and Sons, Chichester 2012, ISBN 111846446X
Externe Links
- Raspberry Pi Foundation
- Raspberry Pi Spy: Raspberry Pi tutorials, scripts, help and downloads
- Lazarus wrapper unit for Gordon Henderson's wiringPi C library
- Pin layout of the wiringPi library
- Additional information on Lazarus and Raspberry Pi at eLinux.org
- Getting Started with Pascal on the Pi
- Lazberry Pi: Umfangreiche Informationen zu Lazarus auf dem Raspberry Pi (englisch).
- Stefan Fischer's Lazarus unit rpi_hal.