Lazarus on Raspberry Pi/es: what changed
Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.
| Old | New | Line | |
|---|---|---|---|
| 1 | 1 | La información que viene a continuación es una traducción lo más aproximada posible de la entrada en inglés (con alguna variación allí donde requiera ser más detallada o explicativa) y puede que las que se redacten en los otros idiomas alternativos de esta wiki. Cada uno que utilice esta información bajo su propio riesgo, puede contener errores. Antes de aplicar los ejemplos asegurarse que la información es aplicable (y se corresponde) con la placa sobre la que se va a trabajar dada la existencia de diferentes modelos en los cuales tanto los pines de conexión como su funcionalidad y voltaje pueden variar. Si no se tiene conocimiento de lo que se está haciendo mejor no aplicarlo. Por otro lado siempre que se trabaja con dispositivos electrónicos se deben guardar las medidas de seguridad apropiadas dado que un uso indebido de dicho hardware puede conllevar la pérdida de la garantía. Desde aquí no se dá garantía ni implícita ni explícita sobre el correcto funcionamiento de la información aquí mostrada. Antes de seguir este tutorial es recomendable tener conocimientos de electrónica (también para conocer los riesgos de manejar tensiones e intensidades), aunque los componentes empleados en los ejemplos son simples. | |
| 2 | 2 | ||
| 3 | 3 | <img src="Lazarus_on_Raspberry_Pi_Raspian_Wheezy_version_2012-10-28.png" title="Lazarus on Raspbian Wheezy" width="200" alt="Lazarus on Raspbian Wheezy" /> *(Raspberry Pi only)* | |
| 4 | 4 | ||
| 5 | 5 | **Raspberry Pi** es un ordenador con una placa del tamaño de una tarjeta de crédito (ver fotos más abajo). Ha sido diseñada en Reino Unido (UK) por la Fundación Raspberry Pi con la intención de fomentar el aprendizaje básico de la computación en escuelas. Las Raspberry Pi se utilizan también para propósitos tan diversos como pueden ser servidores de medios, robótica e ingeniería de control. | |
| 6 | 6 | ||
| 7 | 7 | La Fundación Raspberry Pi recomienda [[Raspbian]] Wheezy como la distribución estandar a utilizar con su hardware. También se utilizan sistemas alternativos tales como RISC y distribuciones Linux variadas y [[Android]]. | |
| 8 | 8 | ||
| 9 | 9 | Lazarus corre en modo nativo bajo la distro Raspbian con sistema operativo Linux. | |
| 10 | 10 | ||
| 11 | 11 | ## Installando y compilando Lazarus | |
| 12 | 12 | ||
| 13 | 13 | ### Instalación simple bajo Raspbian | |
| 14 | 14 | ||
| 15 | 15 | #### Raspberry Pi 1 | |
| 16 | 16 | ||
| 17 | 17 | En la distro de Linux Raspbian es fácil instalar Lazarus y FreePascal. Para lograrlo simplemente abre una ventana de terminal y teclea lo siguiente: | |
| 18 | 18 | ||
| 19 | 19 | ``` bash | |
| 20 | 20 | sudo apt-get update | |
| 21 | 21 | sudo apt-get upgrade | |
| 22 | 22 | sudo apt-get install fpc | |
| 23 | 23 | sudo apt-get install lazarus | |
| 24 | 24 | ``` | |
| 25 | 25 | ||
| 26 | 26 | Esto instala una versión precompilada estable de FreePascal y Lazarus en la distro Raspbian que corre en el hardware Raspberry Pi. Por supuesto se necesita una conexión de red. La instalación puede llevar unos 30 minutos, pero la mayor parte de este proceso tiene lugar automáticamente. Después de la instalación ya se puede arrancar Lazarus desde la sección "Programming" en el menú de comienzo de LXDE. | |
| 27 | 27 | ||
| 28 | 28 | Install Lazarus Rasbian Part1 en.png\|Paso instalación 1 Install Lazarus Rasbian Part2 en.png\|Paso instalación 2 Install Lazarus Rasbian Part3 en.png\|Paso instalación 3 Install Lazarus Rasbian Part4 en.png\|Paso instalación 4 | |
| 29 | 29 | ||
| 30 | 30 | Si necesitas una versión más nueva, o si Lazarus/FreePascal muestran errores durante la instalación puedes consultar más información [aquí](http://www.raspberrypi.org/forums/viewtopic.php?f=34&t=88145). | |
| 31 | 31 | ||
| 32 | 32 | #### Raspberry Pi 2 | |
| 33 | 33 | ||
| 34 | 34 | Desde junio de 2015 el método de instalación regular "out of the box (listo para usar)" también funciona para Raspberry Pi 2 Modelo B. La versión que lleva instalada es, por tanto, demasiado antigua. Para aquellos que busquen construir los últimos IDE de FreePascal y Lazarus mejor ver [este artículo](http://www.tweaking4all.com/hardware/raspberry-pi/install-lazarus-pascal-on-raspberry-pi-2/). | |
| 35 | 35 | ||
| 36 | 36 | <File:Lazarus> 0 9 30 4 on RPi 2.png\|Lazarus "out of the box" en Raspberry Pi 2 | |
| 37 | 37 | ||
| 38 | 38 | 160 | |
| 39 | 39 | ||
| 40 | 40 | #### Raspberry Pi 3 | |
| 41 | 41 | ||
| 42 | 42 | Ver Raspberry Pi 2. | |
| 43 | 43 | ||
| 44 | - | ||
| 45 | - | ||
| 46 | - | ||
| 47 | - | ||
| 44 | + |  | |
| 48 | 45 | ||
| 49 | 46 | <File:RaspBerryPi3B+_img1.png> | |
| 50 | 47 | ||
| 51 | 48 | <File:RaspBerryPi3B+_img2.png> | |
| 52 | 49 | ||
| 53 | 50 | Como se puede observar tiene más pines de los cuales se debe consultar la funcionalidad de cada uno antes de conectar algo. | |
| 54 | 51 | ||
| 55 | - | ||
| 56 | - | ||
| 57 | - | ||
| 58 | - | ||
| 52 | + |  | |
| 59 | 53 | ||
| 60 | 54 | ### Compilación cruzada (cross compiling) para Raspberry Pi desde Windows | |
| 61 | 55 | ||
| 62 | 56 | 1\. Utilizando fpcup | |
| 63 | 57 | ||
| 64 | 58 | Un camino es utilizar fpcup para configurar un compilador cruzado; seguir estas instrucciones: | |
| 65 | 59 | ||
| 66 | 60 | [[fpcup|fpcup#Linux_ARM_cross_compiler]] | |
| 67 | 61 | ||
| 68 | 62 | 2\. Utilizando scripts | |
| 69 | 63 | ||
| 70 | 64 | Alternativamente, para una aproximación más manual, y utilizando ficheros de proceso por lotes, puedes seguir los siguientes pasos. | |
| 71 | 65 | ||
| 72 | 66 | 2.1 Prerequisitos | |
| 73 | 67 | ||
| 74 | 68 | `FPC 2.7.1 or higher installed with sourcecode`\ | |
| 75 | 69 | `Install the Windows version from the Linaro binutils for linux gnueabihf into %FPCPATH%/bin/win32-armhf-linux `[`1`](https://launchpad.net/linaro-toolchain-binaries/trunk/2013.10/+download/gcc-linaro-arm-linux-gnueabihf-4.8-2013.10_win32.zip) | |
| 76 | 70 | ||
| 77 | 71 | 2.2 Script ejemplo para construir (adaptar los trayectos a como se necesiten) | |
| 78 | 72 | ||
| 79 | 73 | ``` bat | |
| 80 | 74 | set PATH=C:\pp\bin\i386-win32;%PATH%; | |
| 81 | 75 | set FPCMAKEPATH=C:/pp | |
| 82 | 76 | set FPCPATH=C:/pp | |
| 83 | 77 | set OUTPATH=C:/pp271 | |
| 84 | 78 | %FPCMAKEPATH%/bin/i386-win32/make distclean OS_TARGET=linux CPU_TARGET=arm CROSSBINDIR=%FPCPATH%/bin/win32-armhf-linux CROSSOPT="-CpARMV6 -CfVFPV2 -OoFASTMATH" FPC=%FPCPATH%/bin/i386-win32/ppc386.exe | |
| 85 | 79 | ||
| 86 | 80 | %FPCMAKEPATH%/bin/i386-win32/make all OS_TARGET=linux CPU_TARGET=arm CROSSBINDIR=%FPCPATH%/bin/win32-armhf-linux CROSSOPT="-CpARMV6 -CfVFPV2 -OoFASTMATH" FPC=%FPCPATH%/bin/i386-win32/ppc386.exe | |
| 87 | 81 | if errorlevel 1 goto quit | |
| 88 | 82 | %FPCMAKEPATH%/bin/i386-win32/make crossinstall CROSSBINDIR=%FPCPATH%/bin/win32-armhf-linux CROSSOPT="-CpARMV6 -CfVFPV2 -OoFASTMATH" OS_TARGET=linux CPU_TARGET=arm FPC=%FPCPATH%/bin/i386-win32/ppc386.exe INSTALL_BASEDIR=%OUTPATH% | |
| 89 | 83 | ||
| 90 | 84 | :quit | |
| 91 | 85 | pause | |
| 92 | 86 | ``` | |
| 93 | 87 | ||
| 94 | 88 | Con el resultado de ppcrossarm.exe y ARM RTL deberias ser capaz de construir una versión de Lazarus para plataforma cruzada como habitualmente y compilar proyectos FPC para Raspberry Pi y otros dispositivos armhf. Recuerda que no todas las librerias, - especialmente en Windows - se encuentran disponibles para Linux arm. | |
| 95 | 89 | ||
| 96 | 90 | ### Compilando desde el código fuente | |
| 97 | 91 | ||
| 98 | 92 | Puede que necesites compilar Lazarus a partir de sus fuentes subverion. Ver \[<http://www.michellcomputing.co.uk/blog/?p=72>: Michell Computing: Lazarus en la Raspberry Pi\] para más detalles.. | |
| 99 | 93 | ||
| 100 | 94 | <File:Start> Menu with Lazarus en.png\|Como arrancar Lazarus en Raspbian Wheezy <File:Lazarus> on Raspberry Pi.jpg\|Lazarus bajo Raspberry Pi <File:rpi> gpio pins.png\|Numeración de los pines GPIO | |
| 101 | 95 | ||
| 102 | 96 | Compilando desde las fuentes en Raspberry con Gentoo (y otra distro) | |
| 103 | 97 | ||
| 104 | 98 | ------------------------------------------------------------------------ | |
| 105 | 99 | ||
| 106 | 100 | Si necesitas instalar la última release estable de fpc y, adicional y aisladamente, el compilador fpc del trunk entonces puedes leer la siguiente guía. | |
| 107 | 101 | ||
| 108 | 102 | Esto se escribió utilizando Gentoo, pero esta guía debería ser de utilidad para cualquier otra distro: [[Install fpc on Raspberry with Gentoo]] | |
| 109 | 103 | ||
| 110 | 104 | ## Accediendo a hardware externo | |
| 111 | 105 | ||
| 112 | - | ||
| 113 | 106 | <img src="rpi_pinout_all_50.png" title="Raspberry Pi pinout of external connectors" width="300" /> | |
| 114 | - | ||
| 115 | - | ||
| 116 | 107 | ||
| 117 | 108 | Una de las finalidades en el desarrollo de Raspberry Pi fue la de facilitar el acceso sin esfuerzo a dispositivos externos tales como sensores y actuadores. A nivel de sofware desde FreePascal y Lazarus además presenta varias modalidades de acceso de E/S (Entrada/Salida) / (I/O (Input/Output): | |
| 118 | 109 | ||
| 119 | 110 | 1. [[Lazarus on Raspberry Pi|Direct access]] Utilizando la unidad [[BaseUnix]]. | |
| 120 | 111 | 2. Acceso a través de [[Lazarus on Raspberry Pi|encapsulated shell calls]] | |
| 121 | 112 | 3. Acceso a través de [[Lazarus on Raspberry Pi|wiringPi library]]. | |
| 122 | 113 | 4. Acceso a través de la unidad [[Lazarus on Raspberry Pi|rpi_hal]]. | |
| 123 | 114 | 5. Acceso a través de la unidad [[Lazarus on Raspberry Pi|PiGpio]]. | |
| 124 | 115 | 6. Acceso a través de la librería [PascalIO](https://github.com/SAmeis/pascalio). | |
| 125 | 116 | 7. Acceso a través de la librería [PXL](http://asphyre.net/products/pxl). | |
| 126 | 117 | ||
| 127 | 118 | ### Acceso nativo al hardware | |
| 128 | 119 | ||
| 129 | 120 |    Este método provee acceso a hardware externo que no requiere librerías adicionales. El único requerimiento es la librería BaseUnix que es parte de la RTL de FreePascal. | |
| 130 | 121 | ||
| 131 | 122 | #### Conmutando un dispositivo a través de un puerto GPIO | |
| 132 | 123 | ||
| 133 | 124 | El siguiente ejemplo lista un programa simple que controla el pin 17 GPIO establecido como salida para conmutar el estado de un LED, transistor o relé. Este programa contiene un [[doc:lcl/stdctrls/ttogglebox.html|ToggleBox]] with name *GPIO17ToggleBox* y para logging retorna códigos a un [[doc:lcl/stdctrls/tmemo.html|TMemo]] llamado *LogMemo*. | |
| 134 | 125 | ||
| 135 | 126 | Para el ejemplo, el ánodo del LED se ha conectado al Pin 11 del conector Pi (que a su vez su correspondencia es con el pin 17 del chip BCM2835 SOC) y por otro lado el cátodo del LED se ha conectado al pin 6 del conector (que corresponde a masa (GND: Ground) pero poniendo una resistencia entre medias de 68 Ohm (R1 en el esquema) tal como se describió previamente por Upton and Halfacree (esto nos permite limitar convenientemente la intensidad que circula por el LED). Subsecuentemente el LED puede ser conmutado entre dos estados ON / OFF mediante el toggle box de la aplicación. | |
| 136 | 127 | ||
| 137 | 128 | El código puede requerir en un principio que se ejecute como root, i.e. adicionalmente con una cuenta tipo root (no recomendado) o a través del mandato **su** antepuesto al nombre del programa lo que nos promocionará el acceso como super usuario (su: super user). Una mejor opción es añadir el usuario con el que trabajemos a los grupos gpio, i2c y spi tal como se indica a continuación: | |
| 138 | 129 | ||
| 139 | 130 | ``` bash | |
| 140 | 131 | sudo adduser pi gpio | |
| 141 | 132 | sudo adduser pi i2c | |
| 142 | 133 | sudo adduser pi spi | |
| 143 | 134 | ``` | |
| 144 | 135 | ||
| 145 | 136 | *Unidad de control:* | |
| 146 | 137 | ||
| 147 | 138 | ``` pascal | |
| 148 | 139 | unit Unit1; | |
| 149 | 140 | ||
| 150 | 141 | {Demo application for GPIO on Raspberry Pi} | |
| 151 | 142 | {Inspired by the Python input/output demo application by Gareth Halfacree} | |
| 152 | 143 | {written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5} | |
| 153 | 144 | ||
| 154 | 145 | {$mode objfpc}{$H+} | |
| 155 | 146 | ||
| 156 | 147 | interface | |
| 157 | 148 | ||
| 158 | 149 | uses | |
| 159 | 150 | Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, | |
| 160 | 151 | Unix, BaseUnix; | |
| 161 | 152 | ||
| 162 | 153 | type | |
| 163 | 154 | ||
| 164 | 155 | { TForm1 } | |
| 165 | 156 | ||
| 166 | 157 | TForm1 = class(TForm) | |
| 167 | 158 | LogMemo: TMemo; | |
| 168 | 159 | GPIO17ToggleBox: TToggleBox; | |
| 169 | 160 | procedure FormActivate(Sender: TObject); | |
| 170 | 161 | procedure FormClose(Sender: TObject; var CloseAction: TCloseAction); | |
| 171 | 162 | procedure GPIO17ToggleBoxChange(Sender: TObject); | |
| 172 | 163 | private | |
| 173 | 164 | { private declarations } | |
| 174 | 165 | public | |
| 175 | 166 | { public declarations } | |
| 176 | 167 | end; | |
| 177 | 168 | ||
| 178 | 169 | const | |
| 179 | 170 | PIN_17: PChar = '17'; | |
| 180 | 171 | PIN_ON: PChar = '1'; | |
| 181 | 172 | PIN_OFF: PChar = '0'; | |
| 182 | 173 | OUT_DIRECTION: PChar = 'out'; | |
| 183 | 174 | ||
| 184 | 175 | var | |
| 185 | 176 | Form1: TForm1; | |
| 186 | 177 | gReturnCode: longint; {stores the result of the IO operation} | |
| 187 | 178 | ||
| 188 | 179 | implementation | |
| 189 | 180 | ||
| 190 | 181 | {$R *.lfm} | |
| 191 | 182 | ||
| 192 | 183 | { TForm1 } | |
| 193 | 184 | ||
| 194 | 185 | procedure TForm1.FormActivate(Sender: TObject); | |
| 195 | 186 | var | |
| 196 | 187 | fileDesc: integer; | |
| 197 | 188 | begin | |
| 198 | 189 | { Prepare SoC pin 17 (pin 11 on GPIO port) for access: } | |
| 199 | 190 | try | |
| 200 | 191 | fileDesc := fpopen('/sys/class/gpio/export', O_WrOnly); | |
| 201 | 192 | gReturnCode := fpwrite(fileDesc, PIN_17[0], 2); | |
| 202 | 193 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 203 | 194 | finally | |
| 204 | 195 | gReturnCode := fpclose(fileDesc); | |
| 205 | 196 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 206 | 197 | end; | |
| 207 | 198 | { Set SoC pin 17 as output: } | |
| 208 | 199 | try | |
| 209 | 200 | fileDesc := fpopen('/sys/class/gpio/gpio17/direction', O_WrOnly); | |
| 210 | 201 | gReturnCode := fpwrite(fileDesc, OUT_DIRECTION[0], 3); | |
| 211 | 202 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 212 | 203 | finally | |
| 213 | 204 | gReturnCode := fpclose(fileDesc); | |
| 214 | 205 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 215 | 206 | end; | |
| 216 | 207 | end; | |
| 217 | 208 | ||
| 218 | 209 | procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction); | |
| 219 | 210 | var | |
| 220 | 211 | fileDesc: integer; | |
| 221 | 212 | begin | |
| 222 | 213 | { Free SoC pin 17: } | |
| 223 | 214 | try | |
| 224 | 215 | fileDesc := fpopen('/sys/class/gpio/unexport', O_WrOnly); | |
| 225 | 216 | gReturnCode := fpwrite(fileDesc, PIN_17[0], 2); | |
| 226 | 217 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 227 | 218 | finally | |
| 228 | 219 | gReturnCode := fpclose(fileDesc); | |
| 229 | 220 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 230 | 221 | end; | |
| 231 | 222 | end; | |
| 232 | 223 | ||
| 233 | 224 | procedure TForm1.GPIO17ToggleBoxChange(Sender: TObject); | |
| 234 | 225 | var | |
| 235 | 226 | fileDesc: integer; | |
| 236 | 227 | begin | |
| 237 | 228 | if GPIO17ToggleBox.Checked then | |
| 238 | 229 | begin | |
| 239 | 230 | { Swith SoC pin 17 on: } | |
| 240 | 231 | try | |
| 241 | 232 | fileDesc := fpopen('/sys/class/gpio/gpio17/value', O_WrOnly); | |
| 242 | 233 | gReturnCode := fpwrite(fileDesc, PIN_ON[0], 1); | |
| 243 | 234 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 244 | 235 | finally | |
| 245 | 236 | gReturnCode := fpclose(fileDesc); | |
| 246 | 237 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 247 | 238 | end; | |
| 248 | 239 | end | |
| 249 | 240 | else | |
| 250 | 241 | begin | |
| 251 | 242 | { Switch SoC pin 17 off: } | |
| 252 | 243 | try | |
| 253 | 244 | fileDesc := fpopen('/sys/class/gpio/gpio17/value', O_WrOnly); | |
| 254 | 245 | gReturnCode := fpwrite(fileDesc, PIN_OFF[0], 1); | |
| 255 | 246 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 256 | 247 | finally | |
| 257 | 248 | gReturnCode := fpclose(fileDesc); | |
| 258 | 249 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 259 | 250 | end; | |
| 260 | 251 | end; | |
| 261 | 252 | end; | |
| 262 | 253 | ||
| 263 | 254 | end. | |
| 264 | 255 | ``` | |
| 265 | 256 | ||
| 266 | 257 | *Programa principal:* | |
| 267 | 258 | ||
| 268 | 259 | ``` pascal | |
| 269 | 260 | program io_test; | |
| 270 | 261 | ||
| 271 | 262 | {$mode objfpc}{$H+} | |
| 272 | 263 | ||
| 273 | 264 | uses | |
| 274 | 265 | {$IFDEF UNIX}{$IFDEF UseCThreads} | |
| 275 | 266 | cthreads, | |
| 276 | 267 | {$ENDIF}{$ENDIF} | |
| 277 | 268 | Interfaces, // this includes the LCL widgetset | |
| 278 | 269 | Forms, Unit1 | |
| 279 | 270 | { puedes añadir unidades después de esto }; | |
| 280 | 271 | ||
| 281 | 272 | {$R *.res} | |
| 282 | 273 | ||
| 283 | 274 | begin | |
| 284 | 275 | Application.Initialize; | |
| 285 | 276 | Application.CreateForm(TForm1, Form1); | |
| 286 | 277 | Application.Run; | |
| 287 | 278 | end. | |
| 288 | 279 | ``` | |
| 289 | 280 | ||
| 290 | 281 | #### Leyendo el estado de un pin | |
| 291 | 282 | ||
| 292 | 283 |    | |
| 293 | 284 | ||
| 294 | 285 | Por supuesto también es posible leer el estado de un pulsador conectado a un puerto GPIO (General Porpouse Inpunt Output). | |
| 295 | 286 | ||
| 296 | 287 | El siguiente ejemplo es muy similar al anterior. Controla el pin GPIO 18 como entrada para un dispositivo binario tal como un pulsador, un transistor o un relé. Este programa contiene un [[doc:lcl/stdctrls/tcheckbox.html|CheckBox]] con el nombre *GPIO18CheckBox* y un [[doc:lcl/stdctrls/tmemo.html|TMemo]] llamado *LogMemo* para los códigos retornados de logging. | |
| 297 | 288 | ||
| 298 | 289 | Para el ejemplo se ha conectado un pulsador al Pin 12 del conector de la Pi (correspondiente al pin GPIO 18 del SOC BCM2835) a través de una resistencia de 10 KOhm conectada al pin 1 (+3.3V, ver diagrama de cableado). El otro polo se ha cableado al pin 6 del conector (corresponde a la masa o GND). El programa detecta el estado del pulsador y commuta correlativamente a On u Off según el estado del mismo. | |
| 299 | 290 | ||
| 300 | 291 | Ten en cuenta que el potencial del pin 18 es "alto" (gracias a la conexión al pin 1 mediate la resistencia) si el pulsador está sin pulsar y "bajo" si se encuentra pulsado (porque en esta situación el pin 18 se encuentra conectado a masa (GND) a través del pulsador). Por tanto, el pin GPIO marca 0 si el botón está presionado y 1 si está sin pulsar. | |
| 301 | 292 | ||
| 302 | 293 | Este programa tiene que ser ejecutado también como usuario root. | |
| 303 | 294 | ||
| 304 | 295 | *Unidad de control:* | |
| 305 | 296 | ||
| 306 | 297 | ``` pascal | |
| 307 | 298 | unit Unit1; | |
| 308 | 299 | ||
| 309 | 300 | {Demo application for GPIO on Raspberry Pi} | |
| 310 | 301 | {Inspired by the Python input/output demo application by Gareth Halfacree} | |
| 311 | 302 | {written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5} | |
| 312 | 303 | ||
| 313 | 304 | {This application reads the status of a push-button} | |
| 314 | 305 | ||
| 315 | 306 | {$mode objfpc}{$H+} | |
| 316 | 307 | ||
| 317 | 308 | interface | |
| 318 | 309 | ||
| 319 | 310 | uses | |
| 320 | 311 | Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, | |
| 321 | 312 | ButtonPanel, Unix, BaseUnix; | |
| 322 | 313 | ||
| 323 | 314 | type | |
| 324 | 315 | ||
| 325 | 316 | { TForm1 } | |
| 326 | 317 | ||
| 327 | 318 | TForm1 = class(TForm) | |
| 328 | 319 | ApplicationProperties1: TApplicationProperties; | |
| 329 | 320 | GPIO18CheckBox: TCheckBox; | |
| 330 | 321 | LogMemo: TMemo; | |
| 331 | 322 | procedure ApplicationProperties1Idle(Sender: TObject; var Done: Boolean); | |
| 332 | 323 | procedure FormActivate(Sender: TObject); | |
| 333 | 324 | procedure FormClose(Sender: TObject; var CloseAction: TCloseAction); | |
| 334 | 325 | private | |
| 335 | 326 | { private declarations } | |
| 336 | 327 | public | |
| 337 | 328 | { public declarations } | |
| 338 | 329 | end; | |
| 339 | 330 | ||
| 340 | 331 | const | |
| 341 | 332 | PIN_18: PChar = '18'; | |
| 342 | 333 | IN_DIRECTION: PChar = 'in'; | |
| 343 | 334 | ||
| 344 | 335 | var | |
| 345 | 336 | Form1: TForm1; | |
| 346 | 337 | gReturnCode: longint; {stores the result of the IO operation} | |
| 347 | 338 | ||
| 348 | 339 | implementation | |
| 349 | 340 | ||
| 350 | 341 | {$R *.lfm} | |
| 351 | 342 | ||
| 352 | 343 | { TForm1 } | |
| 353 | 344 | ||
| 354 | 345 | procedure TForm1.FormActivate(Sender: TObject); | |
| 355 | 346 | var | |
| 356 | 347 | fileDesc: integer; | |
| 357 | 348 | begin | |
| 358 | 349 | { Prepare SoC pin 18 (pin 12 on GPIO port) for access: } | |
| 359 | 350 | try | |
| 360 | 351 | fileDesc := fpopen('/sys/class/gpio/export', O_WrOnly); | |
| 361 | 352 | gReturnCode := fpwrite(fileDesc, PIN_18[0], 2); | |
| 362 | 353 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 363 | 354 | finally | |
| 364 | 355 | gReturnCode := fpclose(fileDesc); | |
| 365 | 356 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 366 | 357 | end; | |
| 367 | 358 | { Set SoC pin 18 as input: } | |
| 368 | 359 | try | |
| 369 | 360 | fileDesc := fpopen('/sys/class/gpio/gpio18/direction', O_WrOnly); | |
| 370 | 361 | gReturnCode := fpwrite(fileDesc, IN_DIRECTION[0], 2); | |
| 371 | 362 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 372 | 363 | finally | |
| 373 | 364 | gReturnCode := fpclose(fileDesc); | |
| 374 | 365 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 375 | 366 | end; | |
| 376 | 367 | end; | |
| 377 | 368 | ||
| 378 | 369 | procedure TForm1.ApplicationProperties1Idle(Sender: TObject; var Done: Boolean); | |
| 379 | 370 | var | |
| 380 | 371 | fileDesc: integer; | |
| 381 | 372 | buttonStatus: string[1] = '1'; | |
| 382 | 373 | begin | |
| 383 | 374 | try | |
| 384 | 375 | { Open SoC pin 18 (pin 12 on GPIO port) in read-only mode: } | |
| 385 | 376 | fileDesc := fpopen('/sys/class/gpio/gpio18/value', O_RdOnly); | |
| 386 | 377 | if fileDesc > 0 then | |
| 387 | 378 | begin | |
| 388 | 379 | { Read status of this pin (0: button pressed, 1: button released): } | |
| 389 | 380 | gReturnCode := fpread(fileDesc, buttonStatus[1], 1); | |
| 390 | 381 | LogMemo.Lines.Add(IntToStr(gReturnCode) + ': ' + buttonStatus); | |
| 391 | 382 | LogMemo.SelStart := Length(LogMemo.Lines.Text) - 1; | |
| 392 | 383 | if buttonStatus = '0' then | |
| 393 | 384 | GPIO18CheckBox.Checked := true | |
| 394 | 385 | else | |
| 395 | 386 | GPIO18CheckBox.Checked := false; | |
| 396 | 387 | end; | |
| 397 | 388 | finally | |
| 398 | 389 | { Close SoC pin 18 (pin 12 on GPIO port) } | |
| 399 | 390 | gReturnCode := fpclose(fileDesc); | |
| 400 | 391 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 401 | 392 | LogMemo.SelStart := Length(LogMemo.Lines.Text) - 1; | |
| 402 | 393 | end; | |
| 403 | 394 | end; | |
| 404 | 395 | ||
| 405 | 396 | procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction); | |
| 406 | 397 | var | |
| 407 | 398 | fileDesc: integer; | |
| 408 | 399 | begin | |
| 409 | 400 | { Free SoC pin 18: } | |
| 410 | 401 | try | |
| 411 | 402 | fileDesc := fpopen('/sys/class/gpio/unexport', O_WrOnly); | |
| 412 | 403 | gReturnCode := fpwrite(fileDesc, PIN_18[0], 2); | |
| 413 | 404 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 414 | 405 | finally | |
| 415 | 406 | gReturnCode := fpclose(fileDesc); | |
| 416 | 407 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 417 | 408 | end; | |
| 418 | 409 | end; | |
| 419 | 410 | ||
| 420 | 411 | end. | |
| 421 | 412 | ``` | |
| 422 | 413 | ||
| 423 | 414 | El programa main es idéntico al del ejemplo de arriba. | |
| 424 | 415 | ||
| 425 | 416 | ### Acceso hardware mediante llamadas encapsuladas del shell | |
| 426 | 417 | ||
| 427 | 418 | Otro modo de acceder al hardware es mediante el encapsulamiento de mandatos a través del shell. Esto se consigue mediante el uso de la función [[Executing External Programs|fpsystem]]. Este método permite el acceso a funciones que no están soportadas por la unidad BaseUnix. El siguiente código implementa un programa que tiene la misma funcionalidad que as the program resulting from the first listing above. | |
| 428 | 419 | ||
| 429 | 420 | *Controlling unit:* | |
| 430 | 421 | ||
| 431 | 422 | ``` pascal | |
| 432 | 423 | unit Unit1; | |
| 433 | 424 | ||
| 434 | 425 | {Demo application for GPIO on Raspberry Pi} | |
| 435 | 426 | {Inspired by the Python input/output demo application by Gareth Halfacree} | |
| 436 | 427 | {written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5} | |
| 437 | 428 | ||
| 438 | 429 | {$mode objfpc}{$H+} | |
| 439 | 430 | ||
| 440 | 431 | interface | |
| 441 | 432 | ||
| 442 | 433 | uses | |
| 443 | 434 | Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, Unix; | |
| 444 | 435 | ||
| 445 | 436 | type | |
| 446 | 437 | ||
| 447 | 438 | { TForm1 } | |
| 448 | 439 | ||
| 449 | 440 | TForm1 = class(TForm) | |
| 450 | 441 | LogMemo: TMemo; | |
| 451 | 442 | GPIO17ToggleBox: TToggleBox; | |
| 452 | 443 | procedure FormActivate(Sender: TObject); | |
| 453 | 444 | procedure FormClose(Sender: TObject; var CloseAction: TCloseAction); | |
| 454 | 445 | procedure GPIO17ToggleBoxChange(Sender: TObject); | |
| 455 | 446 | private | |
| 456 | 447 | { private declarations } | |
| 457 | 448 | public | |
| 458 | 449 | { public declarations } | |
| 459 | 450 | end; | |
| 460 | 451 | ||
| 461 | 452 | var | |
| 462 | 453 | Form1: TForm1; | |
| 463 | 454 | gReturnCode: longint; {stores the result of the IO operation} | |
| 464 | 455 | ||
| 465 | 456 | implementation | |
| 466 | 457 | ||
| 467 | 458 | {$R *.lfm} | |
| 468 | 459 | ||
| 469 | 460 | { TForm1 } | |
| 470 | 461 | ||
| 471 | 462 | procedure TForm1.FormActivate(Sender: TObject); | |
| 472 | 463 | begin | |
| 473 | 464 | { Prepare SoC pin 17 (pin 11 on GPIO port) for access: } | |
| 474 | 465 | gReturnCode := fpsystem('echo "17" > /sys/class/gpio/export'); | |
| 475 | 466 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 476 | 467 | { Set SoC pin 17 as output: } | |
| 477 | 468 | gReturnCode := fpsystem('echo "out" > /sys/class/gpio/gpio17/direction'); | |
| 478 | 469 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 479 | 470 | end; | |
| 480 | 471 | ||
| 481 | 472 | procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction); | |
| 482 | 473 | begin | |
| 483 | 474 | { Free SoC pin 17: } | |
| 484 | 475 | gReturnCode := fpsystem('echo "17" > /sys/class/gpio/unexport'); | |
| 485 | 476 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 486 | 477 | end; | |
| 487 | 478 | ||
| 488 | 479 | procedure TForm1.GPIO17ToggleBoxChange(Sender: TObject); | |
| 489 | 480 | begin | |
| 490 | 481 | if GPIO17ToggleBox.Checked then | |
| 491 | 482 | begin | |
| 492 | 483 | { Swith SoC pin 17 on: } | |
| 493 | 484 | gReturnCode := fpsystem('echo "1" > /sys/class/gpio/gpio17/value'); | |
| 494 | 485 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 495 | 486 | end | |
| 496 | 487 | else | |
| 497 | 488 | begin | |
| 498 | 489 | { Switch SoC pin 17 off: } | |
| 499 | 490 | gReturnCode := fpsystem('echo "0" > /sys/class/gpio/gpio17/value'); | |
| 500 | 491 | LogMemo.Lines.Add(IntToStr(gReturnCode)); | |
| 501 | 492 | end; | |
| 502 | 493 | end; | |
| 503 | 494 | ||
| 504 | 495 | end. | |
| 505 | 496 | ``` | |
| 506 | 497 | ||
| 507 | 498 | El programa main es idéntico al del ejemplo de arriba. Este programa necesita ejecutarse con privilegios de root también. | |
| 508 | 499 | ||
| 509 | 500 | ### Procedimientos y funciones de wiringPi | |
| 510 | 501 | ||
| 511 | 502 | Alex Schaller's wrapper unit for Gordon Henderson's Arduino compatible wiringPi library provides a numbering scheme that resembles that of Arduino boards. | |
| 512 | 503 | ||
| 513 | 504 | **Function wiringPiSetup:longint:** Initializes wiringPi system using the wiringPi pin numbering scheme. | |
| 514 | 505 | ||
| 515 | 506 | **Procedure wiringPiGpioMode(mode:longint):** Initializes wiringPi system with the Broadcom GPIO pin numbering scheme. | |
| 516 | 507 | ||
| 517 | 508 | **Procedure pullUpDnControl(pin:longint;** pud:longint): controls the internal pull-up/down resistors on a GPIO pin. | |
| 518 | 509 | ||
| 519 | 510 | **Procedure pinMode(pin:longint; mode:longint):** sets the mode of a pin to either INPUT, OUTPUT, or PWM_OUTPUT. | |
| 520 | 511 | ||
| 521 | 512 | **Procedure digitalWrite(pin:longint; value:longint):** sets an output bit. | |
| 522 | 513 | ||
| 523 | 514 | **Procedure pwmWrite(pin:longint; value:longint):** sets an output PWM value between 0 and 1024. | |
| 524 | 515 | ||
| 525 | 516 | **Function digitalRead(pin:longint):longint:** reads the value of a given Pin, returning 1 or 0. | |
| 526 | 517 | ||
| 527 | 518 | **Procedure delay(howLong:dword):** waits for at least howLong milliseconds. | |
| 528 | 519 | ||
| 529 | 520 | **Procedure delayMicroseconds(howLong:dword):** waits for at least howLong microseconds. | |
| 530 | 521 | ||
| 531 | 522 | **Function millis:dword:** returns the number of milliseconds since the program called one of the wiringPiSetup functions. | |
| 532 | 523 | ||
| 533 | 524 | ### rpi_hal-Hardware Abstraction Library (funciones y procedimientos GPIO, I2C y SPI de la librería de abstracción hardware de la RaspBerry Pi) | |
| 534 | 525 | ||
| 535 | 526 | Esta unidad con cerca de 1700 líneas de código aportada por Stefan Fischer, aporta procedimientos y funciones para acceso HW I2C, SPI y GPIO en la RaspBerry Pi: | |
| 536 | 527 | ||
| 537 | 528 | Justamente un extracto de los procedimientos y funciones disponibles: | |
| 538 | 529 | ||
| 539 | 530 | **procedure gpio_set_pin (pin:longword;highlevel:boolean);** { Establece RPi GPIO pin a nivel alto o bajo; Velocidad @ 700MHz -\> 0.65MHz } | |
| 540 | 531 | ||
| 541 | 532 | **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 } | |
| 542 | 533 | ||
| 543 | 534 | **procedure gpio_set_input (pin:longword);** { Establece RPi GPIO pin en modo entrada } | |
| 544 | 535 | ||
| 545 | 536 | **procedure gpio_set_output(pin:longword);** { Establece RPi GPIO pin en modo salida} | |
| 546 | 537 | ||
| 547 | 538 | **procedure gpio_set_alt (pin,altfunc:longword);** { Establece RPi GPIO pin to alternate function nr. 0..5 } | |
| 548 | 539 | ||
| 549 | 540 | **procedure gpio_set_gppud (mask:longword);** { Establece RPi GPIO Pull-up/down Register (GPPUD) with mask } | |
| 550 | 541 | ||
| 551 | 542 | ... | |
| 552 | 543 | ||
| 553 | 544 | **function rpi_snr :string;** { Emtrega el SNR: 0000000012345678 } | |
| 554 | 545 | ||
| 555 | 546 | **function rpi_hw :string;** { Entrega el tipo de procesador: BCM2708 } | |
| 556 | 547 | ||
| 557 | 548 | **function rpi_proc:string;** { ARMv6-compatible processor rev 7 (v6l) } | |
| 558 | 549 | ||
| 559 | 550 | ... | |
| 560 | 551 | ||
| 561 | 552 | **function i2c_bus_write(baseadr,reg:word; var <data:databuf_t>; lgt:byte; testnr:integer) : integer;** | |
| 562 | 553 | ||
| 563 | 554 | **function i2c_bus_read (baseadr,reg:word; var <data:databuf_t>; lgt:byte; testnr:integer) : integer;** | |
| 564 | 555 | ||
| 565 | 556 | **function i2c_string_read(baseadr,reg:word; var <data:databuf_t>; lgt:byte; testnr:integer) : string;** | |
| 566 | 557 | ||
| 567 | 558 | **function i2c_string_write(baseadr,reg:word; s:string; testnr:integer) : integer;** | |
| 568 | 559 | ||
| 569 | 560 | ... | |
| 570 | 561 | ||
| 571 | 562 | **procedure SPI_Write(devnum:byte; reg,data:word);** | |
| 572 | 563 | ||
| 573 | 564 | **function SPI_Read(devnum:byte; reg:word) : byte;** | |
| 574 | 565 | ||
| 575 | 566 | **procedure SPI_BurstRead2Buffer (devnum,start_reg:byte; xferlen:longword);** | |
| 576 | 567 | ||
| 577 | 568 | **procedure SPI_BurstWriteBuffer (devnum,start_reg:byte; xferlen:longword);** { Write 'len' Bytes from Buffer SPI Dev startig at address 'reg' } | |
| 578 | 569 | ||
| 579 | 570 | ... | |
| 580 | 571 | ||
| 581 | 572 | *Test Program (testrpi.pas):* | |
| 582 | 573 | ||
| 583 | 574 | ``` pascal | |
| 584 | 575 | //Progama simple de chequeo, el cual utiliza rpi_hal; | |
| 585 | 576 | ||
| 586 | 577 | program testrpi; | |
| 587 | 578 | uses rpi_hal; | |
| 588 | 579 | begin | |
| 589 | 580 | writeln('Show CPU-Info, RPI-HW-Info and Registers:'); | |
| 590 | 581 | rpi_show_all_info; | |
| 591 | 582 | writeln('Let Status LED Blink. Using GPIO functions:'); | |
| 592 | 583 | GPIO_PIN_TOGGLE_TEST; | |
| 593 | 584 | writeln('Test SPI Read function. (piggy back board with installed RFM22B Module is required!)'); | |
| 594 | 585 | Test_SPI; | |
| 595 | 586 | end. | |
| 596 | 587 | ``` | |
| 597 | 588 | ||
| 598 | 589 | ### Unidad nativa PiGpio de bajo nivel escrita en pascal (control GPIO en lugar de la librería c de wiringPi) | |
| 599 | 590 | ||
| 600 | 591 | *Esta unit (pigpio.pas[2](https://docs.google.com/file/d/0B-b-5ooIWPvGN1BIcEJzRDgyeUE/edit?usp=sharing)) de 270 líneas de código aportada por Gabor Szollosi, funciona muy rápido (para ex. GPIO pin output conmutando a una frecuencia de 8 MHz) :* | |
| 601 | 592 | ||
| 602 | 593 | ``` pascal | |
| 603 | 594 | ||
| 604 | 595 | unit PiGpio; | |
| 605 | 596 | { | |
| 606 | 597 | BCM2835 GPIO Registry Driver, also can use to manipulate cpu other registry areas | |
| 607 | 598 | ||
| 608 | 599 | This code is tested only Broadcom bcm2835 cpu, different arm cpus may need different | |
| 609 | 600 | gpio driver implementation | |
| 610 | 601 | ||
| 611 | 602 | 2013 Gabor Szollosi | |
| 612 | 603 | } | |
| 613 | 604 | {$mode objfpc}{$H+} | |
| 614 | 605 | ||
| 615 | 606 | interface | |
| 616 | 607 | ||
| 617 | 608 | uses | |
| 618 | 609 | Classes, SysUtils; | |
| 619 | 610 | ||
| 620 | 611 | const | |
| 621 | 612 | REG_GPIO = $3F000;// bcm2835 gpio register 0x2000 0000 (RPi1), 0x3F00 0000 (RPi2) | |
| 622 | 613 | // new fpMap uses page offset, one page is 4096 bytes = 0x1000 so simply calculate 0x2000 0000 / 0x1000 = 0x2000 0 | |
| 623 | 614 | PAGE_SIZE = 4096; | |
| 624 | 615 | BLOCK_SIZE = 4096; | |
| 625 | 616 | // The BCM2835 has 54 GPIO pins. | |
| 626 | 617 | // BCM2835 data sheet, Page 90 onwards. | |
| 627 | 618 | // There are 6 control registers, each control the functions of a block | |
| 628 | 619 | // of 10 pins. | |
| 629 | 620 | ||
| 630 | 621 | CLOCK_BASE = (REG_GPIO + $101); | |
| 631 | 622 | GPIO_BASE = (REG_GPIO + $200); | |
| 632 | 623 | GPIO_PWM = (REG_GPIO + $20C); | |
| 633 | 624 | ||
| 634 | 625 | INPUT = 0; | |
| 635 | 626 | OUTPUT = 1; | |
| 636 | 627 | PWM_OUTPUT = 2; | |
| 637 | 628 | LOW = False; | |
| 638 | 629 | HIGH = True; | |
| 639 | 630 | PUD_OFF = 0; | |
| 640 | 631 | PUD_DOWN = 1; | |
| 641 | 632 | PUD_UP = 2; | |
| 642 | 633 | ||
| 643 | 634 | // PWM | |
| 644 | 635 | ||
| 645 | 636 | PWM_CONTROL = 0; | |
| 646 | 637 | PWM_STATUS = 4; | |
| 647 | 638 | PWM0_RANGE = 16; | |
| 648 | 639 | PWM0_DATA = 20; | |
| 649 | 640 | PWM1_RANGE = 32; | |
| 650 | 641 | PWM1_DATA = 36; | |
| 651 | 642 | ||
| 652 | 643 | PWMCLK_CNTL = 160; | |
| 653 | 644 | PWMCLK_DIV = 164; | |
| 654 | 645 | ||
| 655 | 646 | PWM1_MS_MODE = $8000; // Run in MS mode | |
| 656 | 647 | PWM1_USEFIFO = $2000; // Data from FIFO | |
| 657 | 648 | PWM1_REVPOLAR = $1000; // Reverse polarity | |
| 658 | 649 | PWM1_OFFSTATE = $0800; // Ouput Off state | |
| 659 | 650 | PWM1_REPEATFF = $0400; // Repeat last value if FIFO empty | |
| 660 | 651 | PWM1_SERIAL = $0200; // Run in serial mode | |
| 661 | 652 | PWM1_ENABLE = $0100; // Channel Enable | |
| 662 | 653 | ||
| 663 | 654 | PWM0_MS_MODE = $0080; // Run in MS mode | |
| 664 | 655 | PWM0_USEFIFO = $0020; // Data from FIFO | |
| 665 | 656 | PWM0_REVPOLAR = $0010; // Reverse polarity | |
| 666 | 657 | PWM0_OFFSTATE = $0008; // Ouput Off state | |
| 667 | 658 | PWM0_REPEATFF = $0004; // Repeat last value if FIFO empty | |
| 668 | 659 | PWM0_SERIAL = $0002; // Run in serial mode | |
| 669 | 660 | PWM0_ENABLE = $0001; // Channel Enable | |
| 670 | 661 | ||
| 671 | 662 | type | |
| 672 | 663 | ||
| 673 | 664 | { TIoPort } | |
| 674 | 665 | ||
| 675 | 666 | TIoPort = class // IO bank object | |
| 676 | 667 | private // | |
| 677 | 668 | ||
| 678 | 669 | //function get_pinDirection(aPin: TGpIoPin): TGpioPinConf; | |
| 679 | 670 | ||
| 680 | 671 | public | |
| 681 | 672 | FGpio: ^LongWord; | |
| 682 | 673 | FClk: ^LongWord; | |
| 683 | 674 | FPwm: ^LongWord; | |
| 684 | 675 | procedure SetPinMode(gpin, mode: byte); | |
| 685 | 676 | function GetBit(gpin : byte):boolean;inline; // gets pin bit} | |
| 686 | 677 | procedure ClearBit(gpin : byte);inline;// write pin to 0 | |
| 687 | 678 | procedure SetBit(gpin : byte);inline;// write pin to 1 | |
| 688 | 679 | procedure SetPullMode(gpin, mode: byte); | |
| 689 | 680 | procedure PwmWrite(gpin : byte; value : LongWord);inline;// write pin to pwm value | |
| 690 | 681 | end; | |
| 691 | 682 | ||
| 692 | 683 | { TIoDriver } | |
| 693 | 684 | ||
| 694 | 685 | TIoDriver = class | |
| 695 | 686 | private | |
| 696 | 687 | ||
| 697 | 688 | public | |
| 698 | 689 | destructor Destroy;override; | |
| 699 | 690 | function MapIo:boolean;// creates io memory mapping | |
| 700 | 691 | procedure UnmapIoRegisrty(FMap: TIoPort);// close io memory mapping | |
| 701 | 692 | function CreatePort(PortGpio, PortClk, PortPwm: LongWord):TIoPort; // create new IO port | |
| 702 | 693 | end; | |
| 703 | 694 | ||
| 704 | 695 | var | |
| 705 | 696 | ||
| 706 | 697 | fd: integer;// /dev/mem file handle | |
| 707 | 698 | procedure delayNanoseconds (howLong : LongWord); | |
| 708 | 699 | ||
| 709 | 700 | implementation | |
| 710 | 701 | ||
| 711 | 702 | uses | |
| 712 | 703 | baseUnix, Unix; | |
| 713 | 704 | ||
| 714 | 705 | procedure delayNanoseconds (howLong : LongWord); | |
| 715 | 706 | var | |
| 716 | 707 | sleeper, dummy : timespec; | |
| 717 | 708 | begin | |
| 718 | 709 | sleeper.tv_sec := 0 ; | |
| 719 | 710 | sleeper.tv_nsec := howLong ; | |
| 720 | 711 | fpnanosleep (@sleeper,@dummy) ; | |
| 721 | 712 | end; | |
| 722 | 713 | { TIoDriver } | |
| 723 | 714 | //******************************************************************************* | |
| 724 | 715 | destructor TIoDriver.Destroy; | |
| 725 | 716 | begin | |
| 726 | 717 | inherited Destroy; | |
| 727 | 718 | end; | |
| 728 | 719 | //******************************************************************************* | |
| 729 | 720 | function TIoDriver.MapIo: boolean; | |
| 730 | 721 | begin | |
| 731 | 722 | Result := True; | |
| 732 | 723 | fd := fpopen('/dev/mem', O_RdWr or O_Sync); // Open the master /dev/memory device | |
| 733 | 724 | if fd < 0 then | |
| 734 | 725 | begin | |
| 735 | 726 | Result := False; // unsuccessful memory mapping | |
| 736 | 727 | end; | |
| 737 | 728 | // | |
| 738 | 729 | end; | |
| 739 | 730 | //******************************************************************************* | |
| 740 | 731 | procedure TIoDriver.UnmapIoRegisrty(FMap:TIoPort); | |
| 741 | 732 | begin | |
| 742 | 733 | if FMap.FGpio <> nil then | |
| 743 | 734 | begin | |
| 744 | 735 | fpMUnmap(FMap.FGpio,PAGE_SIZE); | |
| 745 | 736 | FMap.FGpio := Nil; | |
| 746 | 737 | end; | |
| 747 | 738 | if FMap.FClk <> nil then | |
| 748 | 739 | begin | |
| 749 | 740 | fpMUnmap(FMap.FClk,PAGE_SIZE); | |
| 750 | 741 | FMap.FClk := Nil; | |
| 751 | 742 | end; | |
| 752 | 743 | if FMap.FPwm <> nil then | |
| 753 | 744 | begin | |
| 754 | 745 | fpMUnmap(FMap.FPwm ,PAGE_SIZE); | |
| 755 | 746 | FMap.FPwm := Nil; | |
| 756 | 747 | end; | |
| 757 | 748 | end; | |
| 758 | 749 | //******************************************************************************* | |
| 759 | 750 | function TIoDriver.CreatePort(PortGpio, PortClk, PortPwm: LongWord): TIoPort; | |
| 760 | 751 | begin | |
| 761 | 752 | Result := TIoPort.Create;// new io port, pascal calls new fpMap, where offst is page sized 4096 bytes!!! | |
| 762 | 753 | Result.FGpio := FpMmap(Nil, PAGE_SIZE, PROT_READ or PROT_WRITE, MAP_SHARED, fd, PortGpio); // port config gpio memory | |
| 763 | 754 | Result.FClk:= FpMmap(Nil, PAGE_SIZE, PROT_READ or PROT_WRITE, MAP_SHARED, fd, PortClk);; // port clk | |
| 764 | 755 | Result.FPwm:= FpMmap(Nil, PAGE_SIZE, PROT_READ or PROT_WRITE, MAP_SHARED, fd, PortPwm);; // port pwm | |
| 765 | 756 | end; | |
| 766 | 757 | //******************************************************************************* | |
| 767 | 758 | procedure TIoPort.SetPinMode(gpin, mode: byte); | |
| 768 | 759 | var | |
| 769 | 760 | fSel, shift, alt : byte; | |
| 770 | 761 | gpiof, clkf, pwmf : ^LongWord; | |
| 771 | 762 | begin | |
| 772 | 763 | fSel := (gpin div 10)*4 ; //Select Gpfsel 0 to 5 register | |
| 773 | 764 | shift := (gpin mod 10)*3 ; //0-9 pin shift | |
| 774 | 765 | gpiof := Pointer(LongWord(Self.FGpio)+fSel); | |
| 775 | 766 | if (mode = INPUT) then | |
| 776 | 767 | gpiof^ := gpiof^ and ($FFFFFFFF - (7 shl shift)) //7 shl shift komplemens - Sets bits to zero = input | |
| 777 | 768 | else if (mode = OUTPUT) then | |
| 778 | 769 | begin | |
| 779 | 770 | gpiof^ := gpiof^ and ($FFFFFFFF - (7 shl shift)) or (1 shl shift); | |
| 780 | 771 | end | |
| 781 | 772 | else if (mode = PWM_OUTPUT) then | |
| 782 | 773 | begin | |
| 783 | 774 | Case gpin of | |
| 784 | 775 | 12,13,40,41,45 : alt:= 4 ; | |
| 785 | 776 | 18,19 : alt:= 2 ; | |
| 786 | 777 | else alt:= 0 ; | |
| 787 | 778 | end; | |
| 788 | 779 | If alt > 0 then | |
| 789 | 780 | begin | |
| 790 | 781 | gpiof^ := gpiof^ and ($FFFFFFFF - (7 shl shift)) or (alt shl shift); | |
| 791 | 782 | clkf := Pointer(LongWord(Self.FClk)+PWMCLK_CNTL); | |
| 792 | 783 | clkf^ := $5A000011 or (1 shl 5) ; //stop clock | |
| 793 | 784 | delayNanoseconds(200); | |
| 794 | 785 | clkf := Pointer(LongWord(Self.FClk)+PWMCLK_DIV); | |
| 795 | 786 | clkf^ := $5A000000 or (32 shl 12) ; // set pwm clock div to 32 (19.2/3 = 600KHz) | |
| 796 | 787 | clkf := Pointer(LongWord(Self.FClk)+PWMCLK_CNTL); | |
| 797 | 788 | clkf^ := $5A000011 ; //start clock | |
| 798 | 789 | Self.ClearBit(gpin); | |
| 799 | 790 | pwmf := Pointer(LongWord(Self.FPwm)+PWM_CONTROL); | |
| 800 | 791 | pwmf^ := 0 ; // Disable PWM | |
| 801 | 792 | delayNanoseconds(200); | |
| 802 | 793 | pwmf := Pointer(LongWord(Self.FPwm)+PWM0_RANGE); | |
| 803 | 794 | pwmf^ := $400 ; //max: 1023 | |
| 804 | 795 | delayNanoseconds(200); | |
| 805 | 796 | pwmf := Pointer(LongWord(Self.FPwm)+PWM1_RANGE); | |
| 806 | 797 | pwmf^ := $400 ; //max: 1023 | |
| 807 | 798 | delayNanoseconds(200); | |
| 808 | 799 | // Enable PWMs | |
| 809 | 800 | pwmf := Pointer(LongWord(Self.FPwm)+PWM0_DATA); | |
| 810 | 801 | pwmf^ := 0 ; //start value | |
| 811 | 802 | pwmf := Pointer(LongWord(Self.FPwm)+PWM1_DATA); | |
| 812 | 803 | pwmf^ := 0 ; //start value | |
| 813 | 804 | pwmf := Pointer(LongWord(Self.FPwm)+PWM_CONTROL); | |
| 814 | 805 | pwmf^ := PWM0_ENABLE or PWM1_ENABLE ; | |
| 815 | 806 | end; | |
| 816 | 807 | end; | |
| 817 | 808 | end; | |
| 818 | 809 | //******************************************************************************* | |
| 819 | 810 | procedure TIoPort.SetBit(gpin : byte); | |
| 820 | 811 | var | |
| 821 | 812 | gpiof : ^LongWord; | |
| 822 | 813 | begin | |
| 823 | 814 | gpiof := Pointer(LongWord(Self.FGpio) + 28 + (gpin shr 5) shl 2); | |
| 824 | 815 | gpiof^ := 1 shl gpin; | |
| 825 | 816 | end; | |
| 826 | 817 | //******************************************************************************* | |
| 827 | 818 | procedure TIoPort.ClearBit(gpin : byte); | |
| 828 | 819 | var | |
| 829 | 820 | gpiof : ^LongWord; | |
| 830 | 821 | begin | |
| 831 | 822 | gpiof := Pointer(LongWord(Self.FGpio) + 40 + (gpin shr 5) shl 2); | |
| 832 | 823 | gpiof^ := 1 shl gpin; | |
| 833 | 824 | end; | |
| 834 | 825 | //******************************************************************************* | |
| 835 | 826 | function TIoPort.GetBit(gpin : byte):boolean; | |
| 836 | 827 | var | |
| 837 | 828 | gpiof : ^LongWord; | |
| 838 | 829 | begin | |
| 839 | 830 | gpiof := Pointer(LongWord(Self.FGpio) + 52 + (gpin shr 5) shl 2); | |
| 840 | 831 | if (gpiof^ and (1 shl gpin)) = 0 then Result := False else Result := True; | |
| 841 | 832 | end; | |
| 842 | 833 | //******************************************************************************* | |
| 843 | 834 | procedure TIoPort.SetPullMode(gpin, mode: byte); | |
| 844 | 835 | var | |
| 845 | 836 | pudf, pudclkf : ^LongWord; | |
| 846 | 837 | begin | |
| 847 | 838 | pudf := Pointer(LongWord(Self.FGpio) + 148 ); | |
| 848 | 839 | pudf^ := mode; //mode = 0, 1, 2 :Off, Down, Up | |
| 849 | 840 | delayNanoseconds(200); | |
| 850 | 841 | pudclkf := Pointer(LongWord(Self.FGpio) + 152 + (gpin shr 5) shl 2); | |
| 851 | 842 | pudclkf^ := 1 shl gpin ; | |
| 852 | 843 | delayNanoseconds(200); | |
| 853 | 844 | pudf^ := 0 ; | |
| 854 | 845 | pudclkf^ := 0 ; | |
| 855 | 846 | end; | |
| 856 | 847 | //******************************************************************************* | |
| 857 | 848 | procedure TIoPort.PwmWrite(gpin : byte; value : Longword); | |
| 858 | 849 | var | |
| 859 | 850 | pwmf : ^LongWord; | |
| 860 | 851 | port : byte; | |
| 861 | 852 | begin | |
| 862 | 853 | Case gpin of | |
| 863 | 854 | 12,18,40 : port:= PWM0_DATA ; | |
| 864 | 855 | 13,19,41,45 : port:= PWM1_DATA ; | |
| 865 | 856 | else exit; | |
| 866 | 857 | end; | |
| 867 | 858 | pwmf := Pointer(LongWord(Self.FPwm) + port); | |
| 868 | 859 | pwmf^ := value and $FFFFFBFF; // $400 complemens | |
| 869 | 860 | end; | |
| 870 | 861 | //******************************************************************************* | |
| 871 | 862 | end. | |
| 872 | 863 | ``` | |
| 873 | 864 | ||
| 874 | 865 | *Controlling Lazarus unit:(Project files[3](https://drive.google.com/folderview?id=0B-b-5ooIWPvGU043ZTZ0cUc2YkE&usp=sharing))* | |
| 875 | 866 | ||
| 876 | 867 | ``` pascal | |
| 877 | 868 | unit Unit1; | |
| 878 | 869 | ||
| 879 | 870 | {Demo application for GPIO on Raspberry Pi} | |
| 880 | 871 | {Inspired by the Python input/output demo application by Gareth Halfacree} | |
| 881 | 872 | {written for the Raspberry Pi User Guide, ISBN 978-1-118-46446-5} | |
| 882 | 873 | ||
| 883 | 874 | {$mode objfpc}{$H+} | |
| 884 | 875 | ||
| 885 | 876 | interface | |
| 886 | 877 | ||
| 887 | 878 | uses | |
| 888 | 879 | Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls, | |
| 889 | 880 | ExtCtrls, Unix, PiGpio; | |
| 890 | 881 | ||
| 891 | 882 | type | |
| 892 | 883 | ||
| 893 | 884 | { TForm1 } | |
| 894 | 885 | ||
| 895 | 886 | TForm1 = class(TForm) | |
| 896 | 887 | GPIO25In: TButton; | |
| 897 | 888 | SpeedButton: TButton; | |
| 898 | 889 | LogMemo: TMemo; | |
| 899 | 890 | GPIO23switch: TToggleBox; | |
| 900 | 891 | Timer1: TTimer; | |
| 901 | 892 | GPIO18Pwm: TToggleBox; | |
| 902 | 893 | Direction: TToggleBox; | |
| 903 | 894 | procedure DirectionChange(Sender: TObject); | |
| 904 | 895 | procedure GPIO18PwmChange(Sender: TObject); | |
| 905 | 896 | procedure GPIO25InClick(Sender: TObject); | |
| 906 | 897 | procedure FormActivate(Sender: TObject); | |
| 907 | 898 | procedure FormClose(Sender: TObject; var CloseAction: TCloseAction); | |
| 908 | 899 | procedure GPIO23switchChange(Sender: TObject); | |
| 909 | 900 | procedure SpeedButtonClick(Sender: TObject); | |
| 910 | 901 | procedure Timer1Timer(Sender: TObject); | |
| 911 | 902 | private | |
| 912 | 903 | { private declarations } | |
| 913 | 904 | public | |
| 914 | 905 | { public declarations } | |
| 915 | 906 | end; | |
| 916 | 907 | ||
| 917 | 908 | const | |
| 918 | 909 | INPUT = 0; | |
| 919 | 910 | OUTPUT = 1; | |
| 920 | 911 | PWM_OUTPUT = 2; | |
| 921 | 912 | LOW = False; | |
| 922 | 913 | HIGH = True; | |
| 923 | 914 | PUD_OFF = 0; | |
| 924 | 915 | PUD_DOWN = 1; | |
| 925 | 916 | PUD_UP = 2; | |
| 926 | 917 | // Convert Raspberry Pi P1 pins (Px) to GPIO port | |
| 927 | 918 | P3 = 0; | |
| 928 | 919 | P5 = 1; | |
| 929 | 920 | P7 = 4; | |
| 930 | 921 | P8 = 14; | |
| 931 | 922 | P10 = 15; | |
| 932 | 923 | P11 = 17; | |
| 933 | 924 | P12 = 18; | |
| 934 | 925 | P13 = 21; | |
| 935 | 926 | P15 = 22; | |
| 936 | 927 | P16 = 23; | |
| 937 | 928 | P18 = 24; | |
| 938 | 929 | P19 = 10; | |
| 939 | 930 | P21 = 9; | |
| 940 | 931 | P22 = 25; | |
| 941 | 932 | P23 = 11; | |
| 942 | 933 | P24 = 8; | |
| 943 | 934 | P26 = 7; | |
| 944 | 935 | ||
| 945 | 936 | var | |
| 946 | 937 | Form1: TForm1; | |
| 947 | 938 | GPIO_Driver: TIoDriver; | |
| 948 | 939 | GpF: TIoPort; | |
| 949 | 940 | PWM :Boolean; | |
| 950 | 941 | i, d : integer; | |
| 951 | 942 | Pin,Pout,Ppwm : byte; | |
| 952 | 943 | ||
| 953 | 944 | implementation | |
| 954 | 945 | ||
| 955 | 946 | {$R *.lfm} | |
| 956 | 947 | ||
| 957 | 948 | { TForm1 } | |
| 958 | 949 | ||
| 959 | 950 | procedure TForm1.FormActivate(Sender: TObject); | |
| 960 | 951 | begin | |
| 961 | 952 | if not GPIO_Driver.MapIo then | |
| 962 | 953 | begin | |
| 963 | 954 | LogMemo.Lines.Add('Error mapping gpio registry'); | |
| 964 | 955 | end | |
| 965 | 956 | else | |
| 966 | 957 | begin | |
| 967 | 958 | GpF := GpIo_Driver.CreatePort(GPIO_BASE, CLOCK_BASE, GPIO_PWM); | |
| 968 | 959 | end ; | |
| 969 | 960 | Timer1.Enabled:= True; | |
| 970 | 961 | Timer1.Interval:= 25; //25 ms controll interval | |
| 971 | 962 | Pin := P22; | |
| 972 | 963 | Pout := P16; | |
| 973 | 964 | Ppwm := P12; | |
| 974 | 965 | i:=1; | |
| 975 | 966 | GpF.SetPinMode(Pout,OUTPUT); | |
| 976 | 967 | GpF.SetPinMode(Pin,INPUT); | |
| 977 | 968 | GpF.SetPullMode(Pin,PUD_Up); // Input PullUp High level | |
| 978 | 969 | end; | |
| 979 | 970 | ||
| 980 | 971 | procedure TForm1.GPIO25InClick(Sender: TObject); | |
| 981 | 972 | begin | |
| 982 | 973 | If GpF.GetBit(Pin) then LogMemo.Lines.Add('In: '+IntToStr(1)) | |
| 983 | 974 | else LogMemo.Lines.Add('In: '+IntToStr(0)); | |
| 984 | 975 | end; | |
| 985 | 976 | ||
| 986 | 977 | procedure TForm1.GPIO18PwmChange(Sender: TObject); | |
| 987 | 978 | begin | |
| 988 | 979 | if GPIO18Pwm.Checked then | |
| 989 | 980 | begin | |
| 990 | 981 | GpF.SetPinMode(Ppwm,PWM_OUTPUT); | |
| 991 | 982 | PWM := True; //PWM on | |
| 992 | 983 | end | |
| 993 | 984 | else | |
| 994 | 985 | begin | |
| 995 | 986 | GpF.SetPinMode(Ppwm,INPUT); | |
| 996 | 987 | PWM := False; //PWM off | |
| 997 | 988 | end; | |
| 998 | 989 | end; | |
| 999 | 990 | ||
| 1000 | 991 | procedure TForm1.DirectionChange(Sender: TObject); | |
| 1001 | 992 | begin | |
| 1002 | 993 | if Direction.Checked then d:=10 else d:=-10; | |
| 1003 | 994 | end; | |
| 1004 | 995 | ||
| 1005 | 996 | ||
| 1006 | 997 | procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction); | |
| 1007 | 998 | begin | |
| 1008 | 999 | GpF.SetPinMode(Ppwm,INPUT); | |
| 1009 | 1000 | GpF.ClearBit(Pout); | |
| 1010 | 1001 | GpIo_Driver.UnmapIoRegisrty(GpF); | |
| 1011 | 1002 | end; | |
| 1012 | 1003 | ||
| 1013 | 1004 | procedure TForm1.GPIO23switchChange(Sender: TObject); | |
| 1014 | 1005 | ||
| 1015 | 1006 | Begin | |
| 1016 | 1007 | Timer1.Enabled := False; | |
| 1017 | 1008 | if GPIO23switch.Checked then | |
| 1018 | 1009 | begin | |
| 1019 | 1010 | GpF.SetBit(Pout); //Turn LED on | |
| 1020 | 1011 | end | |
| 1021 | 1012 | else | |
| 1022 | 1013 | begin | |
| 1023 | 1014 | GpF.ClearBit(Pout); //Turn LED off | |
| 1024 | 1015 | end; | |
| 1025 | 1016 | Timer1.Enabled := True; | |
| 1026 | 1017 | end; | |
| 1027 | 1018 | ||
| 1028 | 1019 | procedure TForm1.SpeedButtonClick(Sender: TObject); | |
| 1029 | 1020 | var | |
| 1030 | 1021 | i,p,k,v: longint; | |
| 1031 | 1022 | ido:TDateTime; | |
| 1032 | 1023 | begin | |
| 1033 | 1024 | ido:= Time; | |
| 1034 | 1025 | k:= TimeStampToMSecs(DateTimeToTimeStamp(ido)); | |
| 1035 | 1026 | LogMemo.Lines.Add('Start: '+TimeToStr(ido)); | |
| 1036 | 1027 | p:=10000000 ; | |
| 1037 | 1028 | For i:=1 to p do Begin | |
| 1038 | 1029 | GpF.SetBit(P16); | |
| 1039 | 1030 | GpF.ClearBit(P16); | |
| 1040 | 1031 | end; | |
| 1041 | 1032 | ido:= Time; | |
| 1042 | 1033 | v:= TimeStampToMSecs(DateTimeToTimeStamp(ido)); | |
| 1043 | 1034 | LogMemo.Lines.Add('Stop: '+TimeToStr(ido)+' Frequency: '+ | |
| 1044 | 1035 | IntToStr(p div (v-k))+' kHz'); | |
| 1045 | 1036 | end; | |
| 1046 | 1037 | ||
| 1047 | 1038 | procedure TForm1.Timer1Timer(Sender: TObject); | |
| 1048 | 1039 | begin | |
| 1049 | 1040 | If PWM then Begin | |
| 1050 | 1041 | If (d > 0) and (i+d < 1024) then begin | |
| 1051 | 1042 | i:=i+d; | |
| 1052 | 1043 | GpF.PwmWrite(Ppwm,i); | |
| 1053 | 1044 | end ; | |
| 1054 | 1045 | If (d < 0) and (i+d > -1) then begin | |
| 1055 | 1046 | i:=i+d; | |
| 1056 | 1047 | GpF.PwmWrite(Ppwm,i); | |
| 1057 | 1048 | end; | |
| 1058 | 1049 | end; | |
| 1059 | 1050 | end; | |
| 1060 | 1051 | ||
| 1061 | 1052 | ||
| 1062 | 1053 | end. | |
| 1063 | 1054 | ``` | |
| 1064 | 1055 | ||
| 1065 | 1056 | ### PXL (Platform eXtended Library) for low level native access to GPIO, I²C, SPI, PWM, UART, V4L2, displays and sensors | |
| 1066 | 1057 | ||
| 1067 | 1058 | - Fácil y rápido acceso a GPIO, I²C, SPI, PWM, UART y timer de alta preción de la CPU. | |
| 1068 | 1059 | - V4L2 captura de video/imagen utilizando los conectores dedicados de la placa y cámaras USB. Por ejemplo cámaras serie como VC0706 y LSY201. | |
| 1069 | 1060 | - OpenGL ES hardware de renderizado con y sin servidor X. Software de renderizado. | |
| 1070 | 1061 | - Pantallas I²C y SPI tales como SSD1306, SSD1351, PCB8544 (Nokia), HX8357 y ILI9340. | |
| 1071 | 1062 | - LCD de caracteres con cableado de pines directo. | |
| 1072 | 1063 | - Suporte para software SPI y UART (bit-banging). | |
| 1073 | 1064 | - Suporte para SC16IS7x0 (controladora UART conectada a través de I²C o SPI), incluyendo pins GPIO extra. | |
| 1074 | 1065 | - Soporte para sensores tales como BMP180, DHT22, L3GD20, LSM303 y SHT10. | |
| 1075 | 1066 | - Otras características como networking, math, transformación de imágenes, efectos, dibujo de primitivas gráficas... | |
| 1076 | 1067 | ||
| 1077 | 1068 | *Más información y descargas de la [librería PXL](http://asphyre.net/products/pxl).* | |
| 1078 | 1069 | ||
| 1079 | 1070 | ## Referencias | |
| 1080 | 1071 | ||
| 1081 | 1072 | - Eben Upton and Gareth Halfacree. Raspberry Pi User Guide. John Wiley and Sons, Chichester 2012, ISBN 111846446X | |
| 1082 | 1073 | - B. J. Rao (2013) The Raspberry Pi, Pi Vision and Lazarus/FPC. [Blaise Pascal Magazine](http://www.blaisepascal.eu). 29: 14-21. | |
| 1083 | 1074 | ||
| 1084 | 1075 | ## External Links | |
| 1085 | 1076 | ||
| 1086 | 1077 | - [Raspberry Pi Foundation](http://www.raspberrypi.org) | |
| 1087 | 1078 | - [Raspberry Pi Spy: Raspberry Pi tutorials, scripts, help and downloads](http://www.raspberrypi-spy.co.uk) | |
| 1088 | 1079 | - [Lazarus wrapper unit for Gordon Henderson's wiringPi C library](http://www.lazarus.freepascal.org/index.php/topic,17404.0.html) | |
| 1089 | 1080 | - [Pin layout of the wiringPi library](https://projects.drogon.net/raspberry-pi/wiringpi/pins/) | |
| 1090 | 1081 | - [Additional information on Lazarus and Raspberry Pi at eLinux.org](http://www.elinux.org/Lazarus_on_RPi) | |
| 1091 | 1082 | - [Getting Started with Pascal on the Pi](http://www.pp4s.co.uk/main/gs-pi-intro.html) | |
| 1092 | 1083 | - [Lazberry Pi: Comprehensive information on Lazarus on the Raspberry Pi computer](http://superbitysoft.co.uk/lazberrypi/). | |
| 1093 | 1084 | - [Stefan Fischer's Lazarus unit rpi_hal](http://www.lazarus.freepascal.org/index.php/topic,20991.0.html). | |
| 1094 | 1085 | - [Improved Lazarus Unit for fast access to GPIO](http://www.meltonisl.com/software.html). | |
| 1095 | 1086 | - [PXL - Platform eXtended Library](http://asphyre.net/products/pxl). | |
| 1096 | 1087 | ||
| 1097 | 1088 | --- | |
| 1098 | 1089 | *From the Free Pascal wiki, page "Lazarus on Raspberry Pi/es", as it stood on 2020-02-19 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* |
