Skip to content
Permalink
Branch: master
Go to file
 
 
Cannot retrieve contributors at this time
executable file 15616 lines (14544 sloc) 587 KB
unit rpi_hal; // V5.2 // 2020-01-15
{ RPI_hal:
* Free Pascal Hardware abstraction library for the Raspberry Pi
* Copyright (c) 2012-2020 Stefan Fischer
***********************************************************************
*
* RPI_hal is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* RPI_hal is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with RPI_hal. If not, see <http://www.gnu.org/licenses/>.
*
***********************************************************************
requires minimum FPC Version: 2.4.6
support for the following RPI-Models: A,B,A+,B+,Pi2B,Zero,Pi3B,4B...
!!!!! In your program, pls. use following uses sequence: !!!!!
uses cthreads,rpi_hal,<yourunits>...
required sw tools (apt-get install curl whois):
- curl (PKG: curl) is used by function RPI_MAINT.
- mkpasswd (PKG: whois) is used by function LNX_ChkUsrPwdValid.
Info: http://wiki.freepascal.org/Lazarus_on_Raspberry_Pi
pls. report bugs and discuss code enhancements on github issues
Supported by the H2020 Project # 664786 - Reservoir Computing with Real-Time Data for Future IT
}
{$MODE OBJFPC}
{ $T+}
{$R+} {$Q+}
{$H+} // Ansistrings
{ $ PACKRECORDS C}
{$PACKRECORDS 16}
{ $ ALIGN 32}
{$MACRO ON}
{$HINTS OFF}
Interface
uses {$IFDEF UNIX} cthreads,initc,ctypes,unixtype,BaseUnix,Unix,unixutil,errors, {$ENDIF}
{$IFDEF WINDOWS} windows, {$ENDIF}
crt,typinfo,sysutils,dateutils,Classes,Process,math,inifiles,md5;
const
supminkrnl=797; supmaxkrnl=970; // not used
fmt_rfc3339='yyyy-mm-dd"T"hh:nn:ss';
tfmt0 = 'hh:mm:ss.zz';
MinSingle= Single (1.5E-45); MaxSingle= Single (3.4E38);
MinDouble= Double (5.0E-324); MaxDouble= Double (1.7E308);
MinExtended= Extended(1.9E-4932); MaxExtended=Extended(1.1E4932);
MinReal= MinDouble; MaxReal= MaxDouble;
eeprom_devadr_c=$50; // EEPROM @ I2C-Adr 0x50
hdl_unvalid=-1;
AN=true; AUS=false; AUF=true; ZU=false; LINKS=false; RECHTS=true;
TestTimeOut_sec=60; // 1min
wdoc_path_c= '/dev/watchdog';
rpi_fw_dev= '/dev/vcio';
rpi_cpu_temp_dev_c= '/sys/class/thermal/thermal_zone0/temp';
//http://makezine.com/2016/03/02/raspberry-pi-3-not-halt-catch-fire/
RPI_TempAlarmCelsius_c= 85; // 85'C according to spec (max. temp), 82'C rpi start to throttle@82Deg
RPI_CTempCool_c= 0.6824; // factor for 58'C
RPI_CTempWarn_c= 0.8824; // factor for 75'C
RPI_CTempHot_c= 0.9412; // factor for 80'C
LF = #$0A; CR = #$0D; STX = #$02; ETX = #$03; ESC=#27;
Cntrl_Z = #$1A; BELL = #7; EOL_char = LF; HT = #$09; // HT=TAB
yes_c='TRUE,YES,1,JA,AN,EIN,HIGH,ON'; nein_c='FALSE,NO,0,NEIN,AUS,LOW,OFF';
CompanyShortName='BASIS';
DfltSect_c='DEFAULT'; HomeSect_c='HOME'; noSect_c='UNKNOWN';
UAgentDefault='Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:36.0) Gecko/20100101 Firefox/36.0';
//https://curl.haxx.se/docs/manpage.html
CURLTimeOut_c= '300'; CURLPorts_c='49152-63000';
CURLFTPDefaults_c='--retry 3 --retry-delay 5 --ftp-pasv --ftp-skip-pasv-ip --disable-epsv --connect-timeout '+CURLTimeOut_c+' --local-port '+CURLPorts_c;
//CURLSSLDefaults_c='-k --ssl --ssl-allow-beast'; // does not work on webgo24
CURLSSLDefaults_c='-k --insecure';
CURLpfext_c='.prog';
cryptext_c= '.cpt';
curlprogsync_ms_c=3000; // > 9
{$IFDEF WINDOWS}
CRLF=CR+LF; dir_sep_c='\';
c_tmpdir='c:\tmp'; AppDataDir_c = 'c:\ProgramData\'+CompanyShortName;
LogDir_c=c_tmpdir; c_cmddir='c:\cmd'; c_etcdir=c_tmpdir;
{$ELSE}
CRLF=LF; dir_sep_c='/';
c_tmpdir='/tmp'; AppDataDir_c = '/var/lib/'+CompanyShortName;
LogDir_c='/var/log'; c_cmddir='/usr/local/sbin'; c_etcdir = '/etc';
dmtdir_c='/etc/service'; // Daemon-Tools directory
{$ENDIF}
// fbtft: framebuffer specific info.
// needed for SPI OLED/TFT/LCD display (SSD1306 sainsmart18 ...) console
// setterm --cursor off --clear all > /dev/tty1
// /usr/bin/fbi -d /dev/fb1 --noverbose -a /opt/splash.png
tty_console_c= '/dev/tty1';
fbdev_c= '/dev/fb1';
fbcon_c= 'fbcon=map:10 fbcon=font:VGA8x8 logo.nologo'; // /dev/fb1 <-> /dev/tty1
(*
/etc/modules-load.d/fbtft.conf
spi_bcm2835
fbtft_device
TFT-Tyoe: 1.8SPI 128x160 kompatibel zu sainsmart18 (evtl. auch ander displays zum setzen!!!!!)
/etc/modprobe.d/fbtft.conf
options fbtft_device name=sainsmart18 debug=3 rotate=90 speed=16000000
TFT-Tyoe: 0.91SPI 128x64 kompatibel zu SSD1306
options fbtft_device name=adafruit13m debug=3 speed=16000000 gpios=dc:9
*)
sslcfgfile_c=AppDataDir_c+'/openssl.cnf';
cert_dir_c= '/etc/ssl';
cert_key_dir_c= cert_dir_c+'/private';
cert_crt_dir_c= cert_dir_c+'/certs';
ca_pem_c= cert_crt_dir_c+'/Deutsche_Telekom_Root_CA_2.pem'; // default ca file
cert0_key_c= cert_key_dir_c+'/ssl-cert-snakeoil.key';
cert0_combined_c= cert_key_dir_c+'/ssl-cert-snakeoil-combined.pem'; // e.g. for lighthttpd, shellinabox
cert0_crtORpem_c= cert_crt_dir_c+'/ssl-cert-snakeoil.pem';
cert1_key_c= cert_key_dir_c+'/server.key';
cert1_combined_c= cert_key_dir_c+'/server-combined.pem';
cert1_crtORpem_c= cert_crt_dir_c+'/server.crt';
letsencryptdir_c= '/etc/letsencrypt/live';
LNX_ShadowFile= '/etc/shadow';
LNX_DevTree= '/proc/device-tree';
ifuap_c= 'ap0';
ifeth_c= 'eth0';
ifwlan_c= 'wlan0';
ifwlan1_c= 'wlan1';
ovpn_dev_c= 'tun0';
noip_c= 'noIPAdr';
noMAC_c= 'noMAC';
noData_c= 'noData';
unknown_c= 'unknown';
exit_c= '<exit>';
none_c= '<none>';
usrbrk_c= 'usr break';
hnamdflt_c= 'raspberrypi';
EncDecPWD_c= 'rpi_hal$4712'; // default pwd, if no encrypt/decrypt pwd is supplied
CRLF4HTTP=CR+LF; // for HTTP-Protocol we have to send 0d0a
ext_sep_c='.';
sep_max_c=6;
sep:array[0..sep_max_c] of char=(';',',','|','*','~','`','^');
osc_freq_c = 19200000; // OSC (19.2Mhz ClkSrc=1)
pllc_freq_c =1000000000; // PLLC (1000Mhz ClkSrc=5, changes with overclock settings)
plld_freq_c = 500000000; // PLLD ( 500Mhz ClkSrc=6)
HDMI_freq_c = 216000000; // HDMI ( 216Mhz ClkSrc=7, auxiliary)
gpiomax_2711_reg_c =58; // max. gpio count (GPIO0-57) (BCM2711)
gpiomax_2708_reg_c =54; // max. gpio count (GPIO0-53) pls. see (BCM2709) 2012 Datasheet page 102ff
GPIO_PWM0 =18; // GPIO18 PWM0 on Connector Pin12
GPIO_PWM1 =19; // GPIO19 PWM1 on Connector Pin35 (RPI2)
GPIO_PWM0A0 =12; // GPIO12 PWM0 on Connector Pin32 (RPI2)
GPIO_PWM1A0 =13; // GPIO13 PWM1 on Connector Pin33 (RPI2)
GPIO_PWM0Audio =40; // GPIO40 PWM0 on Audio
GPIO_PWM1Audio =45; // GPIO45 PWM1 on Audio
GPIO_FRQ04_CLK0 = 4; // GPIO4 GPCLK0 on Connector Pin7
GPIO_FRQ05_CLK1 = 5; // GPIO5 GPCLK1 on Connector Pin29 (reserved for system use)
GPIO_FRQ06_CLK2 = 6; // GPIO6 GPCLK2 on Connector Pin31
GPIO_FRQ20_CLK0 =20; // GPIO20 GPCLK0 on Connector Pin38
GPIO_FRQ21_CLK1 =21; // GPIO21 GPCLK1 on Connector Pin40 (reserved for system use)
GPIO_FRQ32_CLK0 =32; // GPIO32 GPCLK0 Compute module only
GPIO_FRQ34_CLK0 =34; // GPIO34 GPCLK0 Compute module only
GPIO_FRQ42_CLK1 =42; // GPIO42 GPCLK1 Compute module only (reserved for system use)
GPIO_FRQ43_CLK2 =43; // GPIO43 GPCLK3 Compute module only
GPIO_FRQ44_CLK1 =44; // GPIO44 GPCLK1 Compute module only (reserved for system use)
GPIO_path_c='/sys/class/gpio';
mdl=16; // was 9;
wid1=12;
gpiomax_map_idx_c=2;
max_pins_c = 40;
//Map Pin-Nr on HW Header P1 to GPIO-Nr. (http://elinux.org/RPI_Low-level_peripherals)
UKN=-99; WRONGPIN=UKN-1; V5=-98; V33=-97; GND=-96; DNC=-95; IDSC=1; IDSD=0;
GPIO_hdr_map_c:array[1..gpiomax_map_idx_c] of array[1..max_pins_c] of integer = // !! <- Delta rev1 and rev2 --> Pins (27-40) only available on newer RPIs
// I2C I2C SPI SPI
(// HW-PIN 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 [27 28 29 30 31 32 33 34 35 36 37 38 39 40] }
// Desc 3.3V 5V SDA1 5V SCL1 GND 1Wire TxD GND RxD 11 12 13 GND 15 16 3.3V 18 MOSI GND MISO 22 SPI SPI GND SPI IDSD IDSC 29 GND 31 32 33 GND 35 36 37 38 GND 40 }
{ rev1 GPIO } ( (V33),(V5),(UKN),(V5),( 1),(GND),( 4),( 14),(GND),(15),(17),(18),(21),(GND),(22),(23),(V33),(24),(10),(GND),( 9),(25),( 11),( 8),(GND),( 7),(IDSD),(IDSC),( 5),(GND),( 6),(12),(13),(GND),(19),(16),(26),(20),(GND),(21) ),
{ rev2 & B+ } ( (V33),(V5),( 2),(V5),( 3),(GND),( 4),( 14),(GND),(15),(17),(18),(27),(GND),(22),(23),(V33),(24),(10),(GND),( 9),(25),( 11),( 8),(GND),( 7),(IDSD),(IDSC),( 5),(GND),( 6),(12),(13),(GND),(19),(16),(26),(20),(GND),(21) )
);
//Pin-Nr on HW Header P1; definitions for piggy-back board
Int_Pin_on_RPI_Header=15; // =GPIO22 -> PIN Number on rpi HW Header P1 ref: http://elinux.org/RPI_Low-level_peripherals
Ena_Pin_on_RPI_Header=22; // =GPIO25 -> RFM22_SD
OOK_Pin_on_RPI_Header=11; // =GPIO17 -> RFM22_OOK
IO1_Pin_on_RPI_Header=13; // =GPIO21/GPIO27 -> TLP434A OOK
ITX_Pin_on_RPI_Header=12; // =GPIO18 -> IR TX
IRX_Pin_on_RPI_Header=16; // =GPIO23 -> IR RX
W1__Pin_on_RPI_Header=07; // =GPIO4 -> 1Wire BitBang
Int_SPI_01_RPI_Header=18; // =GPIO24 -> Int Pin SPI1 on JP1 Pin5
//ARM Physical to VC IO Mapping
BCM2xxx_VCIO_ALIAS= $7E000000;
//ARM Physical to VC Bus Mapping
GPU_CACHED_BASE= $40000000;
GPU_UNCACHED_BASE= $C0000000;
{ BCM2708: Physical addresses range from 0x20000000 to 0x20FFFFFF for peripherals.
The bus addresses for peripherals are set up to map onto the peripheral
bus address range starting at 0x7E000000.
Thus a peripheral advertised here at bus address 0x7Ennnnnn is available
at physical address 0x20nnnnnn. }
PAGE_SIZE= $1000; // 4k
BCM270x_PSIZ_Byte= $80000000-BCM2xxx_VCIO_ALIAS; // MemoryMap: Size of Peripherals. Docu Page 5
BCM270x_RegSizInByte= SizeOf(longword);
BCM270x_RegMaxIdx= (BCM270x_PSIZ_Byte div BCM270x_RegSizInByte)-1; // Registers 0..RegMaxIdx
BCM2708_PBASE= $20000000; // Peripheral Base in Bytes
BCM2709_PBASE= $3F000000; // Peripheral Base in Bytes (RPI2B Processor)
BCM2711_PBASE= $fe000000; // Peripheral Base in Bytes (rpi4)
STIM_BASE_OFS= $00003000; // Docu Page 172ff SystemTimer
INTR_BASE_OFS= $0000B000; // Docu Page 112ff
TIMR_BASE_OFS= $0000B000; // Docu Page 196ff Timer ARM side
MBX_BASE_OFS= $0000B880; // MailboxBaseAddr
PADS_BASE_OFS= $00100000;
CLK_BASE_OFS= $00101000; // Docu Page 107ff
GPIO_BASE_OFS= $00200000; // Docu Page 90ff GPIO contr. page start (1 page=4096Bytes)
UART_BASE_OFS= $00201000; // Docu Page 177ff
PCM_BASE_OFS= $00203000; // Docu Page 125ff
SPI0_BASE_OFS= $00204000; // Docu Page 152ff
PWM_BASE_OFS= $0020C000; // Docu Page 138ff
BSC_BASE_OFS= $00214000; // Docu Page 160ff
AUX_BASE_OFS= $00215000; // Docu Page 8ff
BSC0_BASE_OFS= $00205000; // Docu Page 28ff
BSC1_BASE_OFS= $00804000; // Docu Page 28ff
BSC2_BASE_OFS= $00805000; // Docu Page 28ff
I2C0_BASE_OFS= BSC0_BASE_OFS;
I2C1_BASE_OFS= BSC1_BASE_OFS;
I2C2_BASE_OFS= BSC2_BASE_OFS;
EMMC_BASE_OFS= $00300000; // Docu Page 66ff
BCM2709_LP_OFS= $01000000; // $40000000 BCM2836 Quad-A7 Core Local PeripheralBase. Docu QA7-rev3.4
//0x 4000 0000
//Indexes (each addresses 4 Bytes)
Q4LP_BASE = BCM2709_LP_OFS div BCM270x_RegSizInByte;
Q4LP_CTL = Q4LP_BASE+ 0; // Control register Docu QA7_rev3.4 Page 7ff
Q4LP_CTIMPRE = Q4LP_BASE+ 2; // Core timer prescaler
Q4LP_GPUINTRTG = Q4LP_BASE+ 3; // GPU interrupts routing
Q4LP_CoreTimAccLS = Q4LP_BASE+ 7; // Core timer access LS 32 bits
Q4LP_CoreTimAccMS = Q4LP_BASE+ 8; // Core timer access MS 32 bits
Q4LP_LOCINTRTG = Q4LP_BASE+ 9; // Local Interrupt 0 [1-7] routing
Q4LP_LOCTIMCTL = Q4LP_BASE+13; // Local timer control & status
Q4LP_Core0IntCtl = Q4LP_BASE+16; // Core0 timer Interrupt control
Q4LP_Core0IrqSrc = Q4LP_BASE+24; // Core0 IRQ Source
Q4LP_Core0FIQSrc = Q4LP_BASE+28; // Core0 FIQ Source
Q4LP_Last = Q4LP_BASE+63; // max. of 64 registers (0..63)
//0x 7E10 0000 // https://de.scribd.com/doc/101830961/GPIO-Pads-Control2
PADS_BASE = PADS_BASE_OFS div BCM270x_RegSizInByte;
PADS_GPIO00_27 = PADS_BASE+$0b; // 0x7e10 002c PADS (GPIO 0-27)
PADS_GPIO28_45 = PADS_BASE+$0c; // 0x7e10 0030 PADS (GPIO 28-45)
PADS_GPIO46_53 = PADS_BASE+$0d; // 0x7e10 0034 PADS (GPIO 46-53)
PADS_BASE_START = PADS_GPIO00_27;
PADS_BASE_LAST = PADS_GPIO46_53;
//0x 7E20 0000
GPIO_BASE = GPIO_BASE_OFS div BCM270x_RegSizInByte;
GPFSEL = GPIO_BASE+$00;
GPSET = GPIO_BASE+$07; // Register Index: set bits which are 1 ignores bits which are 0
GPCLR = GPIO_BASE+$0a; // Register Index: clear bits which are 1 ignores bits which are 0
GPLEV = GPIO_BASE+$0d;
GPEDS = GPIO_BASE+$10; // Pin Event Detection
GPREN = GPIO_BASE+$13; // Pin RisingEdge Detection
GPFEN = GPIO_BASE+$16; // Pin FallingEdge Detection
GPHEN = GPIO_BASE+$19; // Pin High Detection
GPLEN = GPIO_BASE+$1c; // Pin Low Detection
GPAREN = GPIO_BASE+$1f; // Pin Async. RisigngEdge Detection
GPAFEN = GPIO_BASE+$22; // Pin Async. FallingEdge Detection
GPPUD = GPIO_BASE+$25; // Pin Pull-up/down Enable
GPPUDCLK = GPIO_BASE+$26; // Pin Pull-up/down Enable Clock
GPTEST = GPIO_BASE+$29;
GPPUPPDN = GPIO_BASE+$39; // Pin Pull-up/down Enable 2711 pins 15:0
GPPUPPDN1 = GPIO_BASE+$3a; // Pin Pull-up/down Enable 2711 pins 31:16
GPPUPPDN2 = GPIO_BASE+$3b; // Pin Pull-up/down Enable 2711 pins 47:32
GPPUPPDN3 = GPIO_BASE+$3c; // Pin Pull-up/down Enable 2711 pins 57:48
GPIOONLYREAD = GPLEV; // 2x 32Bit Register, which are ReadOnly
GPIO_BASE_LAST = GPPUPPDN3;
TIMR_BASE = (TIMR_BASE_OFS+$400) div BCM270x_RegSizInByte; // Docu Page 196
APMLOAD = TIMR_BASE+0;// 0x00
APMVALUE = TIMR_BASE+1;// 0x04
APMCTL = TIMR_BASE+2;// 0x08
APMIRQCLRACK = TIMR_BASE+3;// 0x0c // reading gives always 0x544D5241
APMRAWIRQ = TIMR_BASE+4;// 0x10
APMMaskedIRQ = TIMR_BASE+5;// 0x14
APMReload = TIMR_BASE+6;// 0x18
APMPreDivider = TIMR_BASE+7;// 0x1c
APMFreeRunCounter = TIMR_BASE+8;// 0x20 // Offset 0x420
INTR_BASE_LAST = APMFreeRunCounter;
TestREG = APMIRQCLRACK;
STIM_BASE = STIM_BASE_OFS div BCM270x_RegSizInByte; // SystemTimer
STIMCS = STIM_BASE+$00; // 0
STIMCLO = STIM_BASE+$01; // 4
STIMCHI = STIM_BASE+$02; // 8
STIMC0 = STIM_BASE+$03; // 12
STIMC1 = STIM_BASE+$04; // 16
STIMC2 = STIM_BASE+$05; // 20
STIMC3 = STIM_BASE+$06; // 24
STIM_BASE_LAST = STIMC3;
I2C0_BASE = I2C0_BASE_OFS div BCM270x_RegSizInByte;
I2C0_C = I2C0_BASE+$00; // 0
I2C0_S = I2C0_BASE+$01; // 4
I2C0_DLEN = I2C0_BASE+$02; // 8
I2C0_A = I2C0_BASE+$03; // 0x0c
I2C0_FIFO = I2C0_BASE+$04; // 0x10
I2C0_DIV = I2C0_BASE+$05; // 0x14
I2C0_DEL = I2C0_BASE+$06; // 0x18
I2C0_CLKT = I2C0_BASE+$07; // 0x1c
I2C0_BASE_LAST = I2C0_CLKT;
I2C1_BASE = I2C1_BASE_OFS div BCM270x_RegSizInByte;
I2C1_C = I2C1_BASE+$00; // 0
I2C1_S = I2C1_BASE+$01; // 4
I2C1_DLEN = I2C1_BASE+$02; // 8
I2C1_A = I2C1_BASE+$03; // 0x0c
I2C1_FIFO = I2C1_BASE+$04; // 0x10
I2C1_DIV = I2C1_BASE+$05; // 0x14
I2C1_DEL = I2C1_BASE+$06; // 0x18
I2C1_CLKT = I2C1_BASE+$07; // 0x1c
I2C1_BASE_LAST = I2C1_CLKT;
I2C2_BASE = I2C2_BASE_OFS div BCM270x_RegSizInByte;
I2C2_C = I2C2_BASE+$00; // 0
I2C2_S = I2C2_BASE+$01; // 4
I2C2_DLEN = I2C2_BASE+$02; // 8
I2C2_A = I2C2_BASE+$03; // 0x0c
I2C2_FIFO = I2C2_BASE+$04; // 0x10
I2C2_DIV = I2C2_BASE+$05; // 0x14
I2C2_DEL = I2C2_BASE+$06; // 0x18
I2C2_CLKT = I2C2_BASE+$07; // 0x1c
I2C2_BASE_LAST = I2C2_CLKT;
SPI0_BASE = SPI0_BASE_OFS div BCM270x_RegSizInByte;
SPI0_CS = SPI0_BASE+$00; // 0
SPI0_FIFO = SPI0_BASE+$01; // 4
SPI0_CLK = SPI0_BASE+$02; // 8
SPI0_DLEN = SPI0_BASE+$03; // 0x0c
SPI0_LTOH = SPI0_BASE+$04; // 0x10
SPI0_DC = SPI0_BASE+$05; // 0x14
SPI0_BASE_LAST = SPI0_DC;
MBX_BASE = MBX_BASE_OFS div BCM270x_RegSizInByte;
MBX_READ0 = MBX_BASE+$00; // 0x00 Read data from VC to ARM
MBX_PEEK0 = MBX_BASE+$04; // 0x10
MBX_SENDER0 = MBX_BASE+$05; // 0x14
MBX_STATUS0 = MBX_BASE+$06; // 0x18 Status of VC to ARM
MBX_CONFIG0 = MBX_BASE+$07; // 0x1c
MBX_WRITE1 = MBX_BASE+$08; // 0x20 Write data from ARM to VC
MBX_PEEK1 = MBX_BASE+$0c; // 0x30
MBX_SENDER1 = MBX_BASE+$0d; // 0x34
MBX_STATUS1 = MBX_BASE+$0e; // 0x38
MBX_CONFIG1 = MBX_BASE+$0f; // 0x3c
PWM_BASE = PWM_BASE_OFS div BCM270x_RegSizInByte;
PWMCTL = PWM_BASE+$00; // 0
PWMSTA = PWM_BASE+$01; // 4
PWMDMAC = PWM_BASE+$02; // 8
PWM0RNG = PWM_BASE+$04; // 0x10
PWM0DAT = PWM_BASE+$05; // 0x14
PWM0FIF = PWM_BASE+$06; // 0x18
PWM1RNG = PWM_BASE+$08; // 0x20
PWM1DAT = PWM_BASE+$09; // 0x24
PWM_BASE_LAST = PWM1DAT;
GMGPxCTL_BASE = CLK_BASE_OFS div BCM270x_RegSizInByte; // Manual Page 107ff
GMGP0CTL = GMGPxCTL_BASE+$1c;// 0x2010 1070
GMGP0DIV = GMGPxCTL_BASE+$1d;// 0x2010 1074
GMGP1CTL = GMGPxCTL_BASE+$1e;// 0x2010 1078
GMGP1DIV = GMGPxCTL_BASE+$1f;// 0x2010 107c
GMGP2CTL = GMGPxCTL_BASE+$20;// 0x2010 1080
GMGP2DIV = GMGPxCTL_BASE+$21;// 0x2010 1084
GMGP_BASE_LAST = GMGP2DIV;
PWMCLK_BASE = CLK_BASE_OFS div BCM270x_RegSizInByte; // Manual Page 107ff
PWMCLKCTL = PWMCLK_BASE+$28; //160 0xA0
PWMCLKDIV = PWMCLK_BASE+$29; //164 0xA4
PWMCLK_BASE_LAST = PWMCLKDIV;
PWM_MS_MODE = $80;
PWM_USEFIFO = $10;
PWM_POLARITY = $08;
PWM_RPTL = $04;
PWM_SERIALIZER = $02;
PWM1_MS_MODE = $8000; // Run in MS mode
PWM1_USEFIFO = $2000; // Data from FIFO
PWM1_REVPOLAR = $1000; // Reverse polarity
PWM1_OFFSTATE = $0800; // Ouput Off state
PWM1_REPEATFF = $0400; // Repeat last value if FIFO empty
PWM1_SERIAL = $0200; // Run in serial mode
PWM1_ENABLE = $0100; // Channel Enable
PWM0_MS_MODE = $0080; // Run in MS mode
PWM0_USEFIFO = $0020; // Data from FIFO
PWM0_REVPOLAR = $0010; // Reverse polarity
PWM0_OFFSTATE = $0008; // Ouput Off state
PWM0_REPEATFF = $0004; // Repeat last value if FIFO empty
PWM0_SERIAL = $0002; // Run in serial mode
PWM0_ENABLE = $0001; // Channel Enable
PWM_DIVImax = $0fff; // 12Bit
PWM_DIVImin = 32; // default
BCM_PWD = $5A000000;
ENC_cnt = 2; // Encoder Count
ENC_SyncTime_c = 12; // max. interval /sync. response time of device in msec and switch debounce time
ENC_SwRepeatTime_c= 1000; // if switch is pressed 1sec, treat as repeated keystroke
ENC_sleeptime_def = 50;
ENC_SwitchShutDown=3000; // Switch pressed 3sec signals ShutDown
TRIG_SyncTime_c = 10;
SERVO_FRQ= 50; // Servo SG90 frequency (Hz) for PWM
SERVO_Speed=100; // Datasheet Value:0.1s/60degree
SRVOMINANG=-90; SRVOMIDANG=0; SRVOMAXANG= 90; // Servo SG90 Datasheet Values (Angles in degree)
//SRVOMINDC=1000; SRVOMIDDC=1500; SRVOMAXDC=2000; // Servo SG90 Datasheet Values (us)
SRVOMINDC= 600; SRVOMIDDC=1600; SRVOMAXDC=2600; // Servo SG90 Values found experimentally (us)
//LOG_All =1; LOG_DEBUG = 2; LOG_INFO = 10; Log_NOTICE = 20; Log_WARNING = 50; Log_ERROR = 100; Log_URGENT = 250; LOG_NONE = 254;
I2C_COMBINED_path_c= '/sys/module/i2c_bcm2708/parameters/combined';
//source: http://I2C-tools.sourcearchive.com/documentation/3.0.3-5/I2C-dev_8h_source.html
I2C_path_c = '/dev/i2c-';
I2C_max_bus = 1;
I2C_unvalid_addr = $ff;
I2C_UseNoReg = $ffff; { use this as Read/Write register,
if I2C device has no registers (RD/WR only one value)
like the pressure sensor HDI M500 }
I2C_M_TEN = $0010; // we have a ten bit chip address
I2C_M_WR = $0000;
I2C_M_RD = $0001;
I2C_M_NOSTART = $4000;
I2C_M_REV_DIR_ADDR = $2000;
I2C_M_IGNORE_NAK = $1000;
I2C_M_NO_RD_ACK = $0800;
I2C_M_RECV_LEN = $0400; // length will be first received byte
I2C_RETRIES = $0701; // number of times a device address should be polled when not acknowledging
I2C_TIMEOUT = $0702; // set timeout - call with int
I2C_SLAVE = $0703; // Change slave address
// Attn.: Slave address is 7 or 10 bits
I2C_SLAVE_FORCE = $0706; { Change slave address
Attn.: Slave address is 7 or 10 bits
This changes the address, even if it
is already taken! }
I2C_TENBIT = $0704; // 0 for 7 bit addrs, != 0 for 10 bit
I2C_FUNCS = $0705; // Get the adapter functionality
I2C_RDWR = $0707; // Combined R/W transfer (one stop only)
I2C_PEC = $0708; // != 0 for SMBus PEC
I2C_SMBUS = $0720; // SMBus-level access
I2C_CTRL_REG = 0; // Register Indexes
I2C_STATUS_REG = 1;
I2C_DLEN_REG = 2;
I2C_A_REG = 3;
I2C_FIFO_REG = 4;
I2C_DIV_REG = 5;
I2C_DEL_REG = 6;
I2C_CLKT_REG = 7;
I2C_RDWR_IOCTL_MAX_MSGS = 42;
//to determine what functionality is present
I2C_FUNC_I2C = $00000001;
I2C_FUNC_10BIT_ADDR = $00000002;
I2C_FUNC_PROTOCOL_MANGLING = $00000004; // I2C_M_[REV_DIR_ADDR,NOSTART,..]
I2C_FUNC_SMBUS_PEC = $00000008;
I2C_FUNC_NOSTART = $00000010; // I2C_M_NOSTART
I2C_FUNC_SLAVE = $00000020;
I2C_FUNC_SMBUS_BLOCK_PROC_CALL = $00008000; // SMBus 2.0
I2C_FUNC_SMBUS_QUICK = $00010000;
I2C_FUNC_SMBUS_READ_BYTE = $00020000;
I2C_FUNC_SMBUS_WRITE_BYTE = $00040000;
I2C_FUNC_SMBUS_READ_BYTE_DATA = $00080000;
I2C_FUNC_SMBUS_WRITE_BYTE_DATA = $00100000;
I2C_FUNC_SMBUS_READ_WORD_DATA = $00200000;
I2C_FUNC_SMBUS_WRITE_WORD_DATA = $00400000;
I2C_FUNC_SMBUS_PROC_CALL = $00800000;
I2C_FUNC_SMBUS_READ_BLOCK_DATA = $01000000;
I2C_FUNC_SMBUS_WRITE_BLOCK_DATA = $02000000;
I2C_FUNC_SMBUS_READ_I2C_BLOCK = $04000000; // I2C-like block xfer
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK = $08000000; // w/ 1-byte reg. addr.
I2C_FUNC_SMBUS_BYTE = I2C_FUNC_SMBUS_READ_BYTE or I2C_FUNC_SMBUS_WRITE_BYTE;
I2C_FUNC_SMBUS_BYTE_DATA = I2C_FUNC_SMBUS_READ_BYTE_DATA or I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
I2C_FUNC_SMBUS_WORD_DATA = I2C_FUNC_SMBUS_READ_WORD_DATA or I2C_FUNC_SMBUS_WRITE_WORD_DATA;
I2C_FUNC_SMBUS_BLOCK_DATA = I2C_FUNC_SMBUS_READ_BLOCK_DATA or I2C_FUNC_SMBUS_WRITE_BLOCK_DATA;
I2C_FUNC_SMBUS_I2C_BLOCK = I2C_FUNC_SMBUS_READ_I2C_BLOCK or I2C_FUNC_SMBUS_WRITE_I2C_BLOCK;
RPI_I2C_general_purpose_bus_c=1;
c_max_Buffer = $ff-1; // was 128 // was 024
SPI_IOC_MAGIC = 'k';
SPI_CPHA = $01;
SPI_CPOL = $02;
SPI_MODE_0 = $00;
SPI_MODE_1 = SPI_CPHA;
SPI_MODE_2 = SPI_CPOL;
SPI_MODE_3 = SPI_CPOL or SPI_CPHA;
SPI_CS_HIGH = $04;
SPI_LSB_FIRST = $08;
SPI_3WIRE = $10;
SPI_LOOP = $20;
SPI_NO_CS = $40;
SPI_READY = $80;
SPI_TX_DUAL = $100;
SPI_TX_QUAD = $200;
SPI_RX_DUAL = $400;
SPI_RX_QUAD = $800;
spi_path_c = '/dev/spidev';
spi_max_bus = 0;
spi_max_dev = 1;
SPI_BUF_SIZE_c = c_max_Buffer; // 255; // was 64;
SPI_unvalid_addr =$ffff;
SPI_Speed_c =500000;
_IOC_NONE =$00; _IOC_WRITE =$01; _IOC_READ =$02;
_IOC_NRBITS = 8; _IOC_TYPEBITS = 8; _IOC_SIZEBITS = 14; _IOC_DIRBITS = 2;
_IOC_NRSHIFT = 0;
_IOC_TYPESHIFT = (_IOC_NRSHIFT+ _IOC_NRBITS);
_IOC_SIZESHIFT = (_IOC_TYPESHIFT+_IOC_TYPEBITS);
_IOC_DIRSHIFT = (_IOC_SIZESHIFT+_IOC_SIZEBITS);
ERR_MAXCNT = 5;
ERR_AutoResetMSec =2000; // AutoReset of Errors in msec. 0=noReset
NO_ERRHNDL = -1;
NO_TEST = NO_ERRHNDL;
//consts for rpi fw mbx access (/dev/vcio)
//source: https://github.com/raspberrypi/linux/blob/rpi-4.9.y/include/soc/bcm2835/raspberrypi-firmware.h
// 14 Nov 2017
//TAG_property_stati
TAG_STATUS_REQUEST= 0;
TAG_STATUS_SUCCESS= $80000000;
TAG_STATUS_ERROR= $80000001;
//TAG_property_tags
TAG_PROPERTY_END= 0;
TAG_GET_FIRMWARE_REVISION= $00000001;
TAG_SET_CURSOR_INFO= $00008010;
TAG_SET_CURSOR_STATE= $00008011;
TAG_GET_BOARD_MODEL= $00010001;
TAG_GET_BOARD_REVISION= $00010002;
TAG_GET_BOARD_MAC_ADDRESS= $00010003;
TAG_GET_BOARD_SERIAL= $00010004;
TAG_GET_ARM_MEMORY= $00010005;
TAG_GET_VC_MEMORY= $00010006;
TAG_GET_CLOCKS= $00010007;
TAG_GET_POWER_STATE= $00020001;
TAG_GET_TIMING= $00020002;
TAG_SET_POWER_STATE= $00028001;
TAG_GET_CLOCK_STATE= $00030001;
TAG_GET_CLOCK_RATE= $00030002;
TAG_GET_VOLTAGE= $00030003;
TAG_GET_MAX_CLOCK_RATE= $00030004;
TAG_GET_MAX_VOLTAGE= $00030005;
TAG_GET_TEMPERATURE= $00030006;
TAG_GET_MIN_CLOCK_RATE= $00030007;
TAG_GET_MIN_VOLTAGE= $00030008;
TAG_GET_TURBO= $00030009;
TAG_GET_MAX_TEMPERATURE= $0003000a;
TAG_GET_STC= $0003000b;
TAG_ALLOCATE_MEMORY= $0003000c;
TAG_LOCK_MEMORY= $0003000d;
TAG_UNLOCK_MEMORY= $0003000e;
TAG_RELEASE_MEMORY= $0003000f;
TAG_EXECUTE_CODE= $00030010;
TAG_EXECUTE_QPU= $00030011;
TAG_SET_ENABLE_QPU= $00030012;
TAG_GET_DISPMANX_RESOURCE_MEM_HANDLE= $00030014;
TAG_GET_EDID_BLOCK= $00030020;
TAG_GET_CUSTOMER_OTP= $00030021;
TAG_GET_DOMAIN_STATE= $00030030;
TAG_SET_CLOCK_STATE= $00038001;
TAG_SET_CLOCK_RATE= $00038002;
TAG_SET_VOLTAGE= $00038003;
TAG_SET_TURBO= $00038009;
TAG_SET_CUSTOMER_OTP= $00038021;
TAG_SET_DOMAIN_STATE= $00038030;
TAG_GET_GPIO_STATE= $00030041;
TAG_SET_GPIO_STATE= $00038041;
TAG_SET_SDHOST_CLOCK= $00038042;
TAG_GET_GPIO_CONFIG= $00030043;
TAG_SET_GPIO_CONFIG= $00038043;
TAG_GET_PERIPH_REG= $00030045;
TAG_SET_PERIPH_REG= $00038045;
//* Dispmanx TAGS */
TAG_FRAMEBUFFER_ALLOCATE= $00040001;
TAG_FRAMEBUFFER_BLANK= $00040002;
TAG_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT= $00040003;
TAG_FRAMEBUFFER_GET_VIRTUAL_WIDTH_HEIGHT= $00040004;
TAG_FRAMEBUFFER_GET_DEPTH= $00040005;
TAG_FRAMEBUFFER_GET_PIXEL_ORDER= $00040006;
TAG_FRAMEBUFFER_GET_ALPHA_MODE= $00040007;
TAG_FRAMEBUFFER_GET_PITCH= $00040008;
TAG_FRAMEBUFFER_GET_VIRTUAL_OFFSET= $00040009;
TAG_FRAMEBUFFER_GET_OVERSCAN= $0004000a;
TAG_FRAMEBUFFER_GET_PALETTE= $0004000b;
TAG_FRAMEBUFFER_GET_TOUCHBUF= $0004000f;
TAG_FRAMEBUFFER_GET_GPIOVIRTBUF= $00040010;
TAG_FRAMEBUFFER_RELEASE= $00048001;
TAG_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT= $00044003;
TAG_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT= $00044004;
TAG_FRAMEBUFFER_TEST_DEPTH= $00044005;
TAG_FRAMEBUFFER_TEST_PIXEL_ORDER= $00044006;
TAG_FRAMEBUFFER_TEST_ALPHA_MODE= $00044007;
TAG_FRAMEBUFFER_TEST_VIRTUAL_OFFSET= $00044009;
TAG_FRAMEBUFFER_TEST_OVERSCAN= $0004400a;
TAG_FRAMEBUFFER_TEST_PALETTE= $0004400b;
TAG_FRAMEBUFFER_TEST_VSYNC= $0004400e;
TAG_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT= $00048003;
TAG_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT= $00048004;
TAG_FRAMEBUFFER_SET_DEPTH= $00048005;
TAG_FRAMEBUFFER_SET_PIXEL_ORDER= $00048006;
TAG_FRAMEBUFFER_SET_ALPHA_MODE= $00048007;
TAG_FRAMEBUFFER_SET_VIRTUAL_OFFSET= $00048009;
TAG_FRAMEBUFFER_SET_OVERSCAN= $0004800a;
TAG_FRAMEBUFFER_SET_PALETTE= $0004800b;
TAG_FRAMEBUFFER_SET_TOUCHBUF= $0004801f;
TAG_FRAMEBUFFER_SET_GPIOVIRTBUF= $00048020;
TAG_FRAMEBUFFER_SET_VSYNC= $0004800e;
TAG_FRAMEBUFFER_SET_BACKLIGHT= $0004800f;
TAG_VCHIQ_INIT= $00048010;
TAG_GET_COMMAND_LINE= $00050001;
TAG_GET_DMA_CHANNELS= $00060001;
MB_CHANNEL_ERROR= $FEEDDEAD;
MB_CHANNEL_SUCCESS=$80000000;
MB_FULL= $80000000;
MB_LEVEL= $400000FF;
MB_EMPTY= $40000000; // Mailbox Status Register: Mailbox Empty
MB_CHANNEL_POWER= $00; // Mailbox Channel 0: Power Management Interface
MB_CHANNEL_FB= $01; // Mailbox Channel 1: Frame Buffer
MB_CHANNEL_VUART= $02; // Mailbox Channel 2: Virtual UART
MB_CHANNEL_VCHIQ= $03; // Mailbox Channel 3: VCHIQ Interface
MB_CHANNEL_LEDS= $04; // Mailbox Channel 4: LEDs Interface
MB_CHANNEL_BUTTONS= $05; // Mailbox Channel 5: Buttons Interface
MB_CHANNEL_TOUCH= $06; // Mailbox Channel 6: Touchscreen Interface
MB_CHANNEL_COUNT= $07; // Mailbox Channel 7: Counter
MB_CHANNEL_TAGS= $08; // Mailbox Channel 8: Tags (ARM to VC)
MB_CHANNEL_GPU= $09; // Mailbox Channel 9: GPU (VC to ARM)
//flags for watchdog
WDIOF_OVERHEAT= $0001; // Reset due to CPU overheat
WDIOF_FANFAULT= $0002; // Fan failed
WDIOF_EXTERN1= $0004; // External relay 1
WDIOF_EXTERN2= $0008; // External relay 2
WDIOF_POWERUNDER= $0010; // Power bad/power fault
WDIOF_CARDRESET= $0020; // Card previously reset the CPU
WDIOF_POWEROVER= $0040; // Power over voltage
WDIOF_SETTIMEOUT= $0080; // Set timeout (in seconds)
WDIOF_MAGICCLOSE= $0100; // Supports magic close char
WDIOF_PRETIMEOUT= $0200; // Pretimeout (in seconds), get/set
WDIOF_ALARMONLY= $0400; // Watchdog triggers a management or other external alarm not a reboot
WDIOF_KEEPALIVEPING= $8000; // Keep alive ping reply
//consts for PseudoTerminal IO (/dev/ptmx)
Terminal_MaxBuf = 1024;
NCCS = 32;
TCSANOW = 0; // make change immediate
TCSADRAIN = 1; // drain output, then change
TCSAFLUSH = 2; // drain output, flush input
TCSASOFT = $10; // flag - don't alter h.w. state
ECHOKE = $1; // visual erase for line kill
ECHOE = $2; // visually erase chars
ECHOK = $4; // echo NL after line kill
ECHO = $8; // enable echoing
ECHONL = $10; // echo NL even if ECHO is off
ECHOPRT = $20; // visual erase mode for hardcopy
ECHOCTL = $40; // echo control chars as ^(Char)
ISIG = $80; // enable signals INTR, QUIT, [D]SUSP
ICANON = $100; // canonicalize input lines
ALTWERASE = $200; // use alternate WERASE algorithm
IEXTEN = $400; // enable DISCARD and LNEXT
EXTPROC = $800; // external processing
TOSTOP = $400000; // stop background jobs from output
FLUSHO = $800000; // output being flushed (state)
NOKERNINFO= $2000000; // no kernel output from VSTATUS
PENDIN = $20000000; // XXX retype pending input (state)
NOFLSH = $80000000; // don't flush after interrupt
RPI_hal_dscl= 20;
HeapStatMax_c=20;
CLOCK_REALTIME=0; // Taken from linux/time.h // Posix timers
CertPublic=1; CertPrivKey=2; CertCA=3; CertCombined=4;
CertPackRPIMaint=0; CertPackSnakeOil=1; CertPackServer=2; CertPackLetsEncrypt=3; CertPackLast=CertPackLetsEncrypt;
iKp=0; iKi=1; iKd=2; // arr-indexes for Kp,Ki,Kd
PID_AVGminNum_c=2; PID_AVGmaxNum_c=50; PID_epsilon_c=0.000001;
PID_nk8=8; PID_timadj_c=0.000001; // usec sensor data
PID_loctusec=4; PID_locsollval=5; PID_locistval=6; // csv field locations
PID_twiddle_tolerance=0.00001; PID_twiddle_saveattol=PID_twiddle_tolerance*100;
PID_twiddle_tolNOTsav=0;
PID_nk15=15;
IP_infomax_c=3;
IP_infoNOadapt_c=-1;
IP_infoWLAN0idx_c=0;
IP_infoETH0idx_c=1;
IP_infoUAP0idx_c=2;
IP_infoWLAN1idx_c=3;
type
E_rpi_hal_Exception= class(Exception);
t_ErrorLevel= ( LOG_NHdr,LOG_WHITE,LOG_BLACK,LOG_BLUE,LOG_GREEN,LOG_LHTGRN,LOG_YELLOW,LOG_ORANGE,LOG_RED,LOG_MAGENTA,
LOG_All,LOG_DEBUG,LOG_INFO,LOG_NOTICE,LOG_WARNING,LOG_ERROR,LOG_URGENT,LOG_NONE,LOG_NONE2);
//t_port_flags order is important, do not change. Ord(t_port_flags) will be used to set ALT-Bits in GPFSELx Registers.
// ORD: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 10
t_port_flags = ( INPUT,OUTPUT,ALT5,ALT4,ALT0,ALT1,ALT2,ALT3,PWMHW,PWMSW,control,
FRQHW,Simulation,PullUP,PullDOWN,RisingEDGE,FallingEDGE,NOpull,
DS2mA,DS4mA,DS6mA,DS8mA,DS10mA,DS12mA,DS14mA,DS16mA,noPADhyst,noPADslew,
ReversePOLARITY,InitialHIGH,WRthrough,IOasync,IOCheck,UseCSec,UseCSecWR,UseCSecRD,I2C,
Bit5,Bit6,Bit7,Bit8,StopBit1,StopBit1H,StopBit2,HShw,HSsw,
ParityNONE,ParityODD,ParityEVEN,ParityMark,ParitySpace,withSTTY,
TTYstartCursor,TTYstopCursor,TTYclearScreen,QRshowCode);
s_port_flags = set of t_port_flags;
t_initpart = ( InitHaltOnError,InitGPIO, (* InitGPIOonly,*) InitRPIfw,InitI2C,InitSPI,
InitCreateScript,InitOnExitShowRuntime,StartShutDownWatcher,InitWDOG,InitWDOGnoThread,
InstSignalHandler,UPDAuthDBDateTime,InitCertSnakeOil,InitCertServer,InitCertLetsEncrypt);
s_initpart = set of t_initpart;
t_IOBusType = ( UnknDev,I2CDev,SPIDev);
t_PowerSwitch = ( ELRO,Sartano,Nexa,Intertechno,FS20);
t_rpimaintflags=( UseENCrypt,UpdExec,UpdPKGGet,UpdPKGcopy,UseDECrypt,UpdPKGInst,UpdPKGInstV,
UpdUpld,UpdDwnld,UpdProtoHTTP,UpdProtoHTTPS,UpdProtoRAW,UAgent,UpdNoRedoRequest,
UpdNOP,UpdSSL,UpdVerbose,UpdQuiet,UpdForce,UpdUpdate,UpdNoProgressBar,UpdLogAppend,UpdNoFTPDefaults, //UpdSUDO,
UpdErrVerbose,UpdNoCreateDir,UpdNewerOnly,UpdCleanUP,UpdKeepFile,
UpdNoWDOGprevent,UpdNoZIP,UpdFollowLink,UpdVerify,UpdDBG1,UpdDBG2,UpdnoMD5Chk,UpdOnlyMD5Chk,
UPDStop,UPDDisable,UPDEnable,UPDStart,UPDReStart,UpdShowThInfo,SysV,Systemd,
WDOG_Close,WDOG_Retrig,WDOG_GTO,WDOG_STO,WDOG_BSTAT,WDOG_GSup,WDOG_Pause,WDOG_Resume);
s_rpimaintflags=set of t_rpimaintflags;
t_RPI_config = ( GET_CAN_EXPAND,EXPAND_FS,GET_HOSTNAME,SET_HOSTNAME,GET_BOOT_CLI,GET_AUTOLOGIN,
SET_BOOT_CLI,SET_BOOT_CLIA,SET_BOOT_GUI,SET_BOOT_GUIA,GET_BOOT_WAIT,SET_BOOT_WAIT,
GET_SPLASH,SET_SPLASH,GET_OVERSCAN,SET_OVERSCAN,GET_PIXDUB,SET_PIXDUB,GET_CAMERA,SET_CAMERA,
GET_SSH,SET_SSH,GET_VNC,SET_VNC,GET_SPI,SET_SPI,GET_I2C,SET_I2C,GET_SERIAL,GET_SERIALHW,SET_SERIAL,
GET_1WIRE,SET_1WIRE,GET_RGPIO,SET_RGPIO,GET_PI_TYPE,GET_OVERCLOCK,SET_OVERCLOCK,
GET_GPU_MEM,GET_GPU_MEM_256,GET_GPU_MEM_512,GET_GPU_MEM_1K,SET_GPU_MEM,
GET_HDMI_GROUP,GET_HDMI_MODE,SET_HDMI_GP_MOD,GET_WIFI_CTRY,SET_WIFI_CTRY,WLAN_INTERFACES);
t_Manu_flag= ( unknownManufacturer,Bosch,HDI,AMS,HTD,MCP,IDT);
t_BIOS_Flags= ( BIOS_secret,BIOS_noOVR,BIOS_DoESC,BIOS_UnESC,BIOS_crypt,
BIOS_bool,BIOS_int,BIOS_uint,BIOS_float,BIOS_NonZero,BIOS_tstmp,BIOS_PrefDflt,
BIOS_1byte,BIOS_2byte,BIOS_4byte,BIOS_lon,BIOS_lat,
BIOS_trim1,BIOS_trim2,BIOS_trim3,BIOS_trim4,BIOS_trim5,BIOS_Printable);
s_BIOS_Flags= set of t_BIOS_Flags;
Cert_Type_t= ( CT_rsa,CT_x509,CT_ssl,CT_serial,CT_modulus,CT_modmd5,CT_md5,CT_sha1,CT_sha256,CT_sha512,CT_combined,CT_Path);
MSG_Type_t= ( noIDaddmsg,dashmsg,pmsg,usrmsg,maintmsg,curlprogmsg);
t_MemoryMapPtr= ^t_MemoryMap;
t_MemoryMap = array[0..BCM270x_RegMaxIdx] of longword; // for 32 Bit access
buftype = array[0..c_max_Buffer-1] of byte;
cint=longint; cuint=longword; cuint64=qword;
Pclockid_t=^clockid_t; clockid_t=longint;
t_CLOption = record Name,Value:string; end;
t_CLOptions= array of t_CLOption;
TProcedureNoArgCall= procedure;
TProcedureOneArgCall= procedure(i:integer);
TProcedureCOneArgCall=procedure(i:cint); cdecl;
TFunctionOneArgCall= function (i:integer):integer;
TcFunctionOneArgCall= function (i:cint):cint;
TThFunctionOneArgCall=function (ptr:pointer):ptrint;
TFunctionThreeArgCall=function (lvl:t_ErrorLevel; msgtype:MSG_Type_t; msg:string):longint;
STAT_struct_t = record
idx: longint;
tim_avg,
dif_val_pms,
old_val,
val_avg,
old_avg,
ist_val: real;
val_arr: array of real;
tim_arr: array of real;
end;
isr_t = record
devnum : byte;
enter_isr_routine,
gpio,fd : longint;
func_ptr : TFunctionOneArgCall;
ThreadId : TThreadID;
ThreadPrio,
flag,
rslt : integer;
rising_edge,
int_enable : boolean; // if INT occures, INT Routine will be started or not
int_cnt,
int_cnt_raw : longword;
enter_isr_time : TDateTime;
last_isr_servicetime : int64;
end;
Thread_name_t= string[16];
Thread_Ctrl_ptr= ^Thread_Ctrl_t;
Thread_Ctrl_t=record
ThreadID: TThreadID; //PtrUInt;
ThreadRunning,
TermThread: boolean;
ThreadFunc: TThFunctionOneArgCall;
ThreadTimeOut: TDateTime;
ThreadInfo,
ThreadCmdStr,
ThreadRetStr: string;
ThreadRetCode: integer;
ThreadProgressOld,
ThreadProgress: integer;
UsrData: array[0..4] of longword;
ThreadPara: array[0..4] of integer;
ThreadParaStr: array[0..4] of string;
end;
TL_prot_t=record
TL_CS:TRTLCriticalSection;
TL:TStringList;
TL_modified:boolean;
ThCtl:Thread_Ctrl_t;
end;
STR_prot_t=record
STR_CS: TRTLCriticalSection;
STR: string;
STR_modified:boolean;
end;
ERR_MGMT_t = record
addr:word;
RDerr,WRerr,CMDerr,MAXerr,AutoReset_ms:longword;
TSok,TSokOld,TSerr,TSerrOld:TDateTime;
desc:string[RPI_hal_dscl];
end;
watchdog_info_t = record
options, // Options the card/driver supports
firmware_version: longword; // Firmware version of the card
identity: array[0..31] of byte; // Identity of the board
end;
watchdog_struct_t = record
NextTrigTime,
WDOGFire: TDateTime;
RetrigAsync:boolean;
Hndl,
retival_msec,
LastBootStat,
ival_sec: longint;
LastChanceHandler_ptr:TProcedureNoArgCall;
info: watchdog_info_t;
devpath: string;
ThreadCtrl: Thread_Ctrl_t;
end;
HAT_Struct_t = record
uuid,vendor,product,snr:string;
product_id,product_ver:longword;
available,overwrite:boolean;
end;
RPI_Temps_t=record
// Temp_CS: TRTLCriticalSection;
// TempIdx: longint; // points to max temp
TempMaxObsTS, TempMinObsTS:TDateTime;
TempMaxObserved,TempMinObserved,
TempCOOL,TempWARN,TempHOT,
TempMax: real;
TempMaxLvl: t_ErrorLevel;
TempMaxObservedNEW,TempMinObservedNEW:boolean;
LastUpdate: TDateTime;
Temp: array[1..2] of real; // CPU GPU Temp
TempLvl: array[1..2] of t_ErrorLevel;
TempUnit: array[1..2] of string; // 'C &#x2103;
// TempInfo: string;
end;
RPI_FW_API_t = record
hndl:longint;
end;
RPI_MBX_tag_t = packed record
tag_id: longword;
buffer_size:longword;
data_size: longword;
dev_id: longword;
val: longword;
end;
RPI_MBX_msgPTR_t= ^RPI_MBX_msg_t;
RPI_MBX_msg_t = packed record
msg_size: longword;
request_code:longword;
// tag: RPI_MBX_tag_t;
tag_id: longword;
buffer_size:longword;
data_size: longword;
dev_id: longword;
val: longword;
end_tag: longword;
end;
HW_Usage_t = record
usecnt,usetimesec:longword;
dat:TDateTime;
end;
HW_DevicePresent_t = record
hndl:integer;
DevType:t_IOBusType;
present:boolean;
BusNum,HWAddr:integer;
descr:string[RPI_hal_dscl];
data:string;
end;
I2C_Bus_Info_t = record
I2C_CS : TRTLCriticalSection;
I2C_useCS : boolean;
I2C_funcs,
I2C_speed : longword;
end;
I2C_databuf_ptr = ^I2C_databuf_t;
I2C_databuf_t = record
buf: string[c_max_Buffer];
hdl: cint;
reperr: boolean;
end;
I2C_msg_ptr = ^I2C_msg_t;
I2C_msg_t = record
addr: word;
flags: word;
len: word;
bptr: I2C_databuf_ptr;
end;
I2C_rdwr_ioctl_data_t = record
msgs: I2C_msg_ptr;
nmsgs: longword;
end;
I2C_cmd_t=string[8];
I2C_rdwr_zip_msg_t = record
msgset: I2C_rdwr_ioctl_data_t;
iomsgs: array[0..1] of I2C_msg_t;
zipbuf: I2C_cmd_t;
end;
I2C_rdwr_zip_data_t = record
hdl: cint;
msgidx,
busno,
datlen: longword;
msgset: I2C_rdwr_ioctl_data_t;
iomsgs: array[0..(I2C_RDWR_IOCTL_MAX_MSGS-1)] of I2C_msg_t;
end;
PWM_struct_t = record
pwm_mode : byte;
pwm_sigalt : boolean;
pwm_dutycycle_us,
pwm_restcycle_us,
pwm_period_us,
pwm_period_ms,
pwm_dutyrange,
pwm_value : longword;
pwm_dtycycl, // 0-1 // 0%-100%
pwm_freq_hz,
pwm_freq_min,
pwm_freq_max : real;
end;
GPIO_ptr = ^GPIO_struct_t;
GPIO_struct_t = record
description : string[RPI_hal_dscl];
gpio,HWPin,
idxofs_1Bit,
idxofs_3Bit,nr : longint;
regget,
regset,regclr,
mask_pol,
mask_3Bit,
mask_1Bit : longword;
initok,ein : boolean;
ThreadCtrl : Thread_Ctrl_t;
FRQ_freq_Hz,
FRQ_freq_min,
FRQ_freq_max : real;
FRQ_CTLIdx,
FRQ_DIVIdx : longword;
PWM : PWM_struct_t;
portCapabilityFlags,
portflags : s_port_flags;
end;
SERVO_struct_t = record
HWAccess : GPIO_struct_t; // e.g. SG90 Micro Servo
min_angle, // -90 Degree (max left turn)
mid_angle, // 0 Degree (mid/neutral position)
max_angle, // 90 Degree (max right turn)
speed60deg, // Servo operating speed in msec for 60deg movement
angle_current : longint;
period_us, // Servo Period in us: 20000 (1000000 div 50Hz)
min_dutycycle, // 1 ms @ 50Hz
mid_dutycycle, // 1.5ms
max_dutycycle : longword; // 2 ms
end;
FREQ_Determine_t = record
fdet_enab:boolean;
fSyncTime:TDateTime;
fTurnRate_Hz:real;
fcnt,fcntold,fdet_ms:longint;
end;
ENC_ptr = ^ENC_struct_t;
ENC_CNT_ptr=^ENC_CNT_struct_t;
ENC_CNT_struct_t = record
Handle:integer;
ENC_activity:boolean;
switchcounter,switchcounterold,switchcountermax,
switchlastpresstime,
counter,counterold,countermax,cycles,cyclesold:longint;
encsteps,enccycles,swsteps,Interval_ms:longint;
enc,encold:real;
fIntervalResetTime:TDateTime;
activitymodedetect,
steps_per_cycle:byte;
kbdcode,kbdupcnt,kbddwncnt,kbdswitch:char;
TurnRateStruct:FREQ_Determine_t;
end;
ENC_struct_t = record // Encoder data structure
ENC_CS : TRTLCriticalSection;
SyncTime: TDateTime; // for syncing max. device queries
// ENCptr:ENC_ptr;
ThreadCtrl:Thread_Ctrl_t;
A_Sig,B_Sig,S_Sig:GPIO_struct_t;
a,b,seq,seqold,deltaold,delta:longint;
idxcounter,SwitchRepeatTime_ms,
sleeptime_ms:longword;
beepgpio:integer;
ok,s2minmax:boolean;
SwitchFiredSpecFunc:TProcedureNoArgCall;
CNTInfo:ENC_CNT_struct_t;
desc:string[RPI_hal_dscl];
end;
TRIG_ptr = ^TRIG_struct_t;
TRIG_struct_t = record
TRIG_CS: TRTLCriticalSection;
SyncTime: TDateTime;
SyncTime_ms:longword;
tim_ms: longint;
flg: boolean;
TGPIO: GPIO_struct_t;
ThreadCtrl: Thread_Ctrl_t;
desc: string[RPI_hal_dscl];
end;
SPI_databuf_t = record
reg: byte;
// buf: array[0..(SPI_BUF_SIZE_c-1)] of byte;
buf: string[SPI_BUF_SIZE_c];
posidx,
endidx: longint;
end;
spi_ioc_transfer_t = record // sizeof(spi_ioc_transfer_t) = 32
tx_buf_ptr : qword; // Ptr to tx buffer
rx_buf_ptr : qword; // Ptr to rx buffer
len : longword; // # of bytes
speed_hz : longword; // Clock rate in Hz
delay_usecs : word; // in msec
bits_per_word : byte;
cs_change : byte; // apply chip select
tx_nbits : byte;
rx_nbits : byte;
pad : word;
end;
SPI_AddrMux_t = record
AdrMuxEnable : boolean;
AdrCSgpio : array[0..1] of longint;
end;
SPI_Bus_Info_t = record
SPI_CS : TRTLCriticalSection;
SPI_useCS : boolean;
SPI_maxspeed : longword;
end;
SPI_Device_Info_t = record
errhndl : integer;
spi_path : string;
spi_fd : cint;
spi_LSB_FIRST : byte; // Zero indicates MSB-first; other values indicate the less common LSB-first encoding.
spi_bpw : byte; // bits per word
spi_delay : word; // delay usec
spi_speed : longword; // spi speed in Hz
spi_cs_change : byte;
spi_mode : byte; // 0..3
spi_IOC_mode : longword;
dev_GPIO_ook,
dev_GPIO_en : integer;
isr_enable : boolean; // decides, establish and prepare INT-Environment. If false, then polling
isr : isr_t;
end;
tcflag_t = cuint;
cc_t = cchar;
speed_t = cuint;
size_t = cuint;
ssize_t = cint;
Ptermios = ^termios;
termios = record
c_iflag : tcflag_t;
c_oflag : tcflag_t;
c_cflag : tcflag_t;
c_lflag : tcflag_t;
c_line : cc_t;
c_cc : array[0..(NCCS)-1] of cc_t;
c_ispeed : speed_t;
c_ospeed : speed_t;
end;
Terminal_device_t = record
fdmaster,fdslave,ridx,rlgt:longint;
masterpath,slavepath,linkpath:string;
si : array [1..Terminal_MaxBuf] of char;
end;
PID_float_t= real;
PID_array_t= array[0..2] of PID_float_t;
PID_Method_t= ( P_Default,PI_Default,PID_Default,
P_Oppelt,PI_Oppelt,PID_Oppelt,
P_ZiegNich,PI_ZiegNich,PID_ZiegNich,
P_SUM,PD_SUM,PI_SUM,PID_SUM,PI_SUM_Fast,PID_SUM_Fast,
P_CHR_GSA,P_CHR_GFA,P_CHR_GS20,P_CHR_GF20,
PI_CHR_GSA,PI_CHR_GFA,PI_CHR_GS20,PI_CHR_GF20,
PID_CHR_GSA,PID_CHR_GFA,PID_CHR_GS20,PID_CHR_GF20,
P_SAMAL_GSA,P_SAMAL_GFA,P_SAMAL_GS20,P_SAMAL_GF20,
PI_SAMAL_GFA,PI_SAMAL_GF20,PI_SAMAL_GSA,PI_SAMAL_GS20,
PID_SAMAL_GFA,PID_SAMAL_GF20,PID_SAMAL_GSA,PID_SAMAL_GS20);
PID_Twiddle_t = record
twiddle_on,
twiddle_saved: boolean;
twiddle_state,
twiddle_idx,
twiddle_intermax,
twiddle_iterations: longint;
twiddle_sum_dp,
twiddle_sum_dp_old,
twiddle_best_error: PID_float_t;
twiddle_tol,
err,p,dp,ps,dps: PID_array_t;
twiddle_INI_sect,
twiddle_INI_key: string;
end;
PID_Struct_t = record
PID_nr: longint;
PID_cnt: longword;
PID_dT,
PID_LastdT: int64; // nano seconds
PID_IntImprove,
PID_DifImprove,
PID_LimImprove,
PID_FirstTime,
PID_STimAdj,
PID_UseSelfTuning: boolean;
PID_Time,
PID_LastTime: timespec;
PID_Ks,
PID_Integrated,
PID_IntegratedWindupResetValue,
PID_SetPoint, // r(t): SP:SetPoint:Führungsgröße
PID_ProcessValue, // y(t): PV:ProcessValue:Regelgröße
PID_ControlOut, // u(t): ControlOut:Stellgröße
PID_MinOutput0, // ControlOut at 0
PID_MinOutput, // ControlOut minimum, if ON
PID_MaxOutput, // ControlOut maximum, if ON
PID_Error,
PID_LastError,
PID_LastSampleTime,
PID_SampleTime,
PID_PrevAbsError: PID_float_t;
PID_K,PID_Ksav: PID_array_t;
PID_Twiddle: PID_Twiddle_t;
end;
T_IniFileDesc = record
inifilbuf: TIniFile;
ok: boolean;
modifydate: TDateTime;
dfltflags: s_BIOS_flags;
dfltsection,
inifilename: string;
end;
WAVE_RampShape_t = (LIN_Ramp,LIN_Triangle,LIN_SawTooth,LIN_Square,SINusoidal,S_Shape);
WAVE_Array_t= array of real;
WAVE_Signal_Struct_t = record
enable,
up: boolean;
mode: WAVE_RampShape_t;
idx,
int_ms: longint;
timer: TDateTime;
end;
cert_t = record
ok:boolean;
certtyp:Cert_Type_t;
desc,filnam,id:string;
end;
cert_pack_t = record
ok: boolean;
idx: longint;
packtyp:Cert_Type_t;
desc,
pwd: string;
cert: array[CertPublic..CertCombined] of cert_t; // 1:publicCert 2:privateKey 3:CaCert 4:CertCombined
end;
IP_Info_t = record
stat,wireless:boolean;
timestamp:TDateTime;
alias,iface,ip4addr,ip6addr,hwaddr,gwaddr,nsaddr,domain,
link,ssid,signal,chan,freq,DNSname:string;
end;
IP_Infos_t = record
idx: longint;
init,
samesubnet: boolean;
devlst,ip4ext,ip6ext,
hostapd_extdev,
hostname: string;
IP_Info: array[0..IP_infomax_c] of IP_Info_t;
end;
AlignmentSize_t = record
c:char;
// a:array[1..16] of byte; // force data alignment to 32 byte
b:array[1..15] of byte;
end;
HeapStat_t = record
lvl: T_ErrorLevel;
HeapStatAlloc: array[0..HeapStatMax_c] of longint;
name: string;
end;
ERR_Rec_t = record
step: longint;
title,msg: string;
end;
var
dummy:AlignmentSize_t; // requires {$PACKRECORDS 32}
msg:RPI_MBX_msg_t; // 32 byte aligned
mmap_arr:t_MemoryMapPtr;
CurlThreadCtrl:Thread_Ctrl_t;
HighPrecisionMillisecondFactor:Int64=1000;
HighPrecisionMicrosecondFactor:Int64=1;
HighPrecisionTimerInit:boolean=false;
terminateProg:boolean;
RPI_MaintMinVersion,RPI_MaintMaxVersion:real;
mem_fd:integer;
wdog:watchdog_struct_t;
SDcard_root_hdl:byte;
RPI_bType:byte;
LNX_UsrAuthModDateTime,RPI_ProgramStartTime,RPI_BootTime:TDateTime;
_TZLocal:longint; _TZOffsetString:string[10];
IniFileDesc:T_IniFileDesc;
RpiMaintCmd:TIniFile;
MSG_HUB_ptr,CURL_ProgressUpdateHook_ptr:TFunctionThreeArgCall;
RPI_SignalHandlerHook_ptr:TProcedureCOneArgCall;
SD_speedRD:string;
USBDEVFS_RESET,
SPI_IOC_RD_MODE,SPI_IOC_WR_MODE,SPI_IOC_RD_LSB_FIRST,SPI_IOC_WR_LSB_FIRST,
SPI_IOC_RD_BITS_PER_WORD,SPI_IOC_WR_BITS_PER_WORD,SPI_IOC_RD_MAX_SPEED_HZ,
SPI_IOC_WR_MAX_SPEED_HZ,IOCTL_TAG_PROPERTY,
WDIOC_SETTIMEOUT,WDIOC_GETTIMEOUT,WDIOC_KEEPALIVE,WDIOC_GETBOOTSTATUS,
WDIOC_GETSUPPORT,WDIOC_GETSTATUS:longword;
RPI_ShutDown_RebootCall,RPI_ShutDown_Call,WDOG_LastChance_ptr:TProcedureNoArgCall;
RPI_ShutDownMin_ms,RPI_ShutDownDebounce_ms:word;
RPI_ShutDown_struct:GPIO_struct_t;
RPI_HW_initpart:s_initpart;
HAT_Info: HAT_Struct_t;
IP_Infos: IP_Infos_t;
HS,WS,DS: HeapStat_t;
RPI_Temps: RPI_Temps_t;
CertPack: array[CertPackRPIMaint..
CertPackLast] of cert_pack_t;
SPI_AddrMux: array[0..spi_max_dev] of SPI_AddrMux_t;
spi_bus: array[0..spi_max_bus] of SPI_Bus_Info_t;
spi_dev: array[0..spi_max_bus,
0..spi_max_dev] of SPI_Device_Info_t;
spi_buf: array[0..spi_max_bus,
0..spi_max_dev] of SPI_databuf_t;
I2C_bus: array[0..I2C_max_bus] of I2C_Bus_Info_t;
I2C_buf: array[0..I2C_max_bus] of I2C_databuf_t;
ENC_struct: array of ENC_struct_t;
TRIG_struct: array of TRIG_struct_t;
SERVO_struct: array of SERVO_struct_t;
ERR_MGMT: array of ERR_MGMT_t;
NGINX_TestContent:TStringList;
procedure AlignShow;
function RPI_HW_Start:boolean; // start all. GPIO,I2C and SPI
function RPI_HW_Start(initpart:s_initpart):boolean; // start dedicated parts. e.g. RPI_HW_Start([InitGPIO,InitI2C,InitSPI]);
function RPI_HW_Start(initpart:s_initpart; p1,p2:string):boolean;
{$IFDEF UNIX} procedure GPIO_int_test; {$ENDIF} // only for test
procedure GPIO_PIN_TOGGLE_TEST; // just for demo reasons, call it from your own program. Be careful, it toggles GPIO pin 16 -> StatusLED }
procedure GPIO_Test(HWPinNr:longword; flags:s_port_flags);
procedure GPIO_TestAll; // Test All GPIOs as OUTPUTs!!!
procedure GPIO_PWM_Test; // Test with GPIO18 PWM0 on Connector Pin12
procedure GPIO_PWM_Test(gpio:longint; HWPWM:boolean; freq_Hz:real; dutyrange,startval:longword);
procedure FRQ_Test; // Test with GPIO4. 100kHz
procedure ENC_Test; // Encoder Test HWPins:15,16,18
procedure SERVO_Test; // Servo Test HWPins:12,16,18 // GPIOs:18,23,24
procedure SPI_Test;
procedure SPI_Loop_Test;
procedure I2C_test;
procedure I2C_ZIP_Test;
procedure MEM_SpeedTest;
procedure CLK_Test;
procedure BIOS_Test; // shows the usages of a config file
procedure CL_Test; // CommandLineParser test
procedure GetDateTimefromXMLTimeStamp_Test;
procedure call_external_prog_Test;
function _IOC (dir:byte; typ:char; nr,size:word):longword;
function _IO (typ:char; nr:word):longword;
function _IOR (typ:char; nr,size:word):longword;
function _IOW (typ:char; nr,size:word):longword;
function _IOWR(typ:char; nr,size:word):longword;
function BIT_Get(v:byte; i:byte):boolean;
function BIT_Get(v:word; i:byte):boolean;
function BIT_Get(v:longword; i:byte):boolean;
function BIT_Get(v:qword; i:byte):boolean;
procedure BIT_Clr(var v:byte; i:byte);
procedure BIT_Clr(var v:word; i:byte);
procedure BIT_Clr(var v:longword; i:byte);
procedure BIT_Clr(var v:qword; i:byte);
procedure BIT_Set(var v:byte; i:byte);
procedure BIT_Set(var v:word; i:byte);
procedure BIT_Set(var v:longword; i:byte);
procedure BIT_Set(var v:qword; i:byte);
procedure BIT_Put(var v:byte; i:byte; b:boolean);
procedure BIT_Put(var v:word; i:byte; b:boolean);
procedure BIT_Put(var v:longword; i:byte; b:boolean);
procedure BIT_Put(var v:qword; i:byte; b:boolean);
function MSK_Get8 (bitnum:byte):byte;
function MSK_Get16_8 (bitnum:byte; var idxofs:byte):byte;
function MSK_Get64_8 (bitnum:byte; var idxofs:byte):byte;
function MSK_Get256_8 (bitnum:byte; var idxofs:byte):byte;
function BCM_REGAdr(idx:longword):longword;
function BCM_GETREG(regidx:longword):longword;
procedure BCM_SETREG(regidx,newval:longword);
procedure BCM_SETREG(regidx,newval:longword; and_mask,readmodifywrite:boolean);
function RPI_Piggyback_board_available : boolean;
function RPI_PiFace_board_available(devadr:byte) : boolean;
function RPI_run_on_known_hw:boolean;
function RPI_platform_ok:boolean;
function RPI_mmap_run_on_unix:boolean;
function RPI_run_on_ARM:boolean;
function RPI_mmap_get_info (modus:longint) : longword;
procedure RPI_HDR_SetDesc(HWPin:longint; desc:string);
procedure RPI_show_all_info;
procedure RPI_show_SBC_info;
function RPI_WLANavailChan(cntry:string):string;
Function RPI_FW_property(req,tag:longword; tag_data:pointer; buf_size:byte):longint;
procedure RPI_FW_test;
procedure RPI_MBX_test;
function ERR_NEW_HNDL(adr:word; descr:string; maxerrs,AutoResetMsec:longword):integer;
function ERR_MGMT_STAT(errhdl:integer):boolean;
function ERR_MGMT_GetErrCnt(errhdl:integer):longword;
procedure ERR_MGMT_UPD(errhdl:integer; cmdcode,datalgt:byte; modus:boolean);
procedure Toggle_STATUSLED_very_fast;
procedure LED_Status (ein:boolean); // Switch Status-LED on or off
procedure HDMI_Switch(ein:boolean); // switch HDMI on/off
function CLK_GetFreq(clksource:longword):real; // Hz
function CLK_GetMinFreq:real;
function CLK_GetMaxFreq:real;
function OSC_Setup(_gpio:longint; pwm_freq_Hz,pwm_dty:real):longint;
procedure OSC_Write(_gpio,pwm_dutyrange:longint; pwm_dty:real);
function TIM_Setup(timr_freq_Hz:real):real;
procedure TIM_Test; // 1MHz
procedure FRQ_SetStruct (var GPIO_struct:GPIO_struct_t); // set default values
procedure FRQ_SetStruct (var GPIO_struct:GPIO_struct_t; freq_Hz,freq_min,freq_max:real);
function FRQ_Setup (var GPIO_struct:GPIO_struct_t):boolean;
procedure FRQ_Switch (var GPIO_struct:GPIO_struct_t; ein:boolean);
procedure PWM_SetStruct (var GPIO_struct:GPIO_struct_t); // set default values
procedure PWM_SetStruct (var GPIO_struct:GPIO_struct_t; mode:byte; freq_Hz:real; dutyrange,startval:longword);
function PWM_Setup (var GPIO_struct:GPIO_struct_t):boolean;
procedure PWM_Write (var GPIO_struct:GPIO_struct_t; value:longword); // value: 0-1023
procedure PWM_setClock (var GPIO_struct:GPIO_struct_t); // same clock for PWM0 and PWM1. Needs only to be set once
procedure PWM_End (var GPIO_struct:GPIO_struct_t);
function PWM_GetDtyRangeVal(var GPIO_struct:GPIO_struct_t; DutyCycle:real):longword;
function PWM_GetMinFreq(dutycycle:longword):real;
function PWM_GetMaxFreq(dutycycle:longword):real;
function PWM_GetMaxDtyC(freq:real):longword;
function PWM_GetDRVal (percent:real; dutyrange:longword):longword;
procedure GPIO_ShowStruct(var GPIO_struct:GPIO_struct_t);
procedure GPIO_SetStruct(var GPIO_struct:GPIO_struct_t); // set default values
procedure GPIO_SetStruct(var GPIO_struct:GPIO_struct_t; num,gpionum:longint; desc:string; flags:s_port_flags);
procedure GPIO_Switch (var GPIO_struct:GPIO_struct_t); // Read GPIOx Signal in Struct
procedure GPIO_Switch (var GPIO_struct:GPIO_struct_t; switchon:boolean);
function GPIO_Setup (var GPIO_struct:GPIO_struct_t):boolean;
function GPIO_MAP_GPIO_NUM_2_HDR_PIN(gpio:longint; mapidx:byte):longint; // Maps GPIO Number to the HDR_PIN
function GPIO_MAP_GPIO_NUM_2_HDR_PIN(gpio:longint):longint;
function GPIO_MAP_HDR_PIN_2_GPIO_NUM(hdr_pin_number:longint; mapidx:byte):longint; // Maps HDR_PIN to the GPIO Number
function GPIO_MAP_HDR_PIN_2_GPIO_NUM(hdr_pin_number:longint):longint;
procedure GPIO_set_HDR_PIN(hw_pin_number:longword;highlevel:boolean); // Maps PIN to the GPIO Header
function GPIO_get_HDR_PIN(hw_pin_number:longword):boolean; // Maps PIN to the GPIO Header
function GPIO_FCTOK(gpio:longint; flags:s_port_flags):boolean;
procedure GPIO_set_pin (gpio:longword;highlevel:boolean); // Set RPi GPIO pin to high or low level; Speed @ 700MHz -> 0.65MHz
function GPIO_get_PIN (gpio:longword):boolean; // Get RPi GPIO pin Level is true when Pin level is '1'; false when '0'; Speed @ 700MHz -> 1.17MHz
procedure GPIO_Pulse (gpio,pulse_ms:longword);
procedure GPIO_set_input (gpio:longword); // Set RPi GPIO pin to input direction
procedure GPIO_set_output (gpio:longword); // Set RPi GPIO pin to output direction
procedure GPIO_set_ALT (gpio:longword; altfunc:t_port_flags); // Set RPi GPIO pin to ALT0..ALT5
procedure GPIO_set_PINMODE (gpio:longword; portfkt:t_port_flags);
procedure GPIO_set_PAD (gpio:longword; noSLEW,noHYST:boolean; drivestrength:byte);
procedure GPIO_set_PULLUP (gpio:longword; enable:boolean); // enable/disable PullUp
procedure GPIO_set_PULLDOWN(gpio:longword; enable:boolean); // enable/disable PullDown
procedure GPIO_set_edge_rising (gpio:longword; enable:boolean); // Pin RisingEdge Detection Register (GPREN)
procedure GPIO_set_edge_falling(gpio:longword; enable:boolean); // Pin FallingEdge Detection Register (GPFEN)
procedure GPIO_get_mask_and_idx(regidx,gpio:longword; var idxabs,mask:longword);
{$IFDEF UNIX}
function GPIO_set_int (var isr:isr_t; GPIO_num:longint; isr_proc:TFunctionOneArgCall; flags:s_port_flags):integer; // set up isr routine, GPIO_number, int_routine which have to be executed, rising or falling_edge
function GPIO_int_release(var isr:isr_t) : integer;
procedure GPIO_int_enable (var isr:isr_t);
procedure GPIO_int_disable(var isr:isr_t);
function GPIO_int_active (var isr:isr_t):boolean;
{$ENDIF}
procedure GPIO_show_regs;
procedure pwm_show_regs;
procedure q4_show_regs;
procedure Clock_show_regs;
function GPIO_get_desc(regidx,regcontent:longword) : string;
procedure GPIO_ShowConnector;
procedure GPIO_ConnectorStringList(tl:TStringList);
function GPIO_PortCapabilityFlags(gpio:longint):s_port_flags;
function GPIO_PortFlags2String(flgs:s_port_flags):string;
function GPIO_String2PortFlags(flagstring:string):s_port_flags;
procedure FREQ_CounterReset (var FREQ_Struct:FREQ_Determine_t);
procedure FREQ_InitStruct (var FREQ_Struct:FREQ_Determine_t; detint_ms:longint);
procedure FREQ_DetTurnRate (var FREQ_Struct:FREQ_Determine_t; steps:longint);
function ENC_GetHdl (descr:string):byte;
procedure ENC_InfoInit (var CNTInfo:ENC_CNT_struct_t);
function ENC_Setup(hdl:integer; stick2minmax:boolean; ctrpreset,ctrmax,stepspercycle:longword; beepergpio:integer):boolean;
procedure ENC_End (hdl:integer);
function ENC_GetVal (hdl:integer; ctrsel:integer):real;
function ENC_GetVal (hdl:integer):real;
function ENC_GetValPercent (hdl:integer):real;
function ENC_GetSwitch (hdl:integer):real;
function ENC_GetCycles (hdl:integer):real;
function ENC_GetCounter (var ENCInfo:ENC_CNT_struct_t):boolean;
procedure ENC_IncEncCnt (var ENCInfo:ENC_CNT_struct_t; cnt:integer);
procedure ENC_IncSwCnt (var ENCInfo:ENC_CNT_struct_t; cnt:integer);
function TRIG_Reg(gpio:longint; descr:string; flags:s_port_flags; synctim_ms:longword):integer;
procedure TRIG_End(hdl:integer);
procedure TRIG_SetValue(hdl:integer; timesig_ms:longint);
function TRIG_GetValue(hdl:integer; var timesig_ms:longint):integer;
procedure SERVO_Setup(var SERVO_struct:SERVO_struct_t;
HWPinNr,nr,maxval,
dcmin,dcmid,dcmax:longword;
angmin,angmid,angmax,speed:longint;
desc:string; freq:real; flags:s_port_flags);
procedure SERVO_SetStruct(var SERVO_struct:SERVO_struct_t; dty_min,dty_mid,dty_max:longword; ang_min,ang_mid,ang_max,speed:longint);
procedure SERVO_Write(var SERVO_struct:SERVO_struct_t; angle:longint; syncwait:boolean);
procedure SERVO_End(var SERVO_struct:SERVO_struct_t);
function BIOS_GetIniFilename:string;
procedure BIOS_ReadIniFile(fname:string);
procedure BIOS_EndIniFile;
function BIOS_CacheUpdate:boolean;
procedure BIOS_CacheUpdate(upd:boolean);
function BIOS_GetIniNum(section,name:string; flgs:s_BIOS_Flags; default,minval,maxval:real):real;
function BIOS_GetIniNum(section,name:string; default,minval,maxval:real):real;
function BIOS_GetIniNum(name:string; default,minval,maxval:real):real;
function BIOS_GetIniString(name,default:string):string;
function BIOS_GetIniString(name,default:string; flgs:s_BIOS_Flags):string;
function BIOS_GetIniString(section,name,default:string):string;
function BIOS_GetIniString(section,name,default:string; flgs:s_BIOS_Flags):string;
function BIOS_SetIniString(name,value:string):boolean;
function BIOS_SetIniString(section,name,value:string):boolean;
function BIOS_SetIniString(section,name,value:string; flgs:s_BIOS_Flags):boolean;
function BIOS_DeleteKey(section,name:string):boolean;
procedure BIOS_EraseSection(section:string);
procedure BIOS_SetDfltSection(section:string);
procedure BIOS_SetDfltFlags(flags:s_BIOS_flags);
procedure USAGE_Init(nr:byte; var struct:HW_Usage_t; sect,key:string);
function RPI_OSrev:string;// 9.1
function RPI_snr :string; // 0000000012345678
function RPI_hw :string; // BCM2708
function RPI_fw :string; // 2018-02-09T14:22:56
function RPI_uname:string;// Linux pump 4.14.18-v7+ #1093 SMP Fri Feb 9 15:33:07 GMT 2018 armv7l GNU/Linux
function RPI_machine:string;// armv7l
function RPI_proc:string; // ARMv6-compatible processor rev 7 (v6l)
function RPI_mips:string; // 697.95
function RPI_feat:string; // swp half thumb fastmult vfp edsp java tls
function RPI_rev :string; // rev1;256MB;1000002
function RPI_freq:string; // 700000;700000;900000;Hz
function RPI_Volt:string; // core:1.2000V;sdram_c:1.2000V;sdram_i:1.2000V;sdram_p:1.2250V
function RPI_FREQs:string; // arm:600000000;core:250000000;h264:250000000;isp:250000000;...
function RPI_whoami:string;
function RPI_ThrottleDesc:string; //under-voltage;arm frequency capped...
procedure RPI_ThrottleGet;
function RPI_Temp(logmsg:boolean; var TempStruct:RPI_Temps_t):t_ERRORLevel;
procedure RPI_TempMaxValuesCheckIn(var TempStruct:RPI_Temps_t; Tmin,Tmax:real; TminTS,TmaxTS:TDateTime);
procedure RPI_TempInit(var TempStruct:RPI_Temps_t);
procedure RPI_TempSaveLimits(var TempStruct:RPI_Temps_t; section:string);
procedure RPI_TempLoadLimits(var TempStruct:RPI_Temps_t; section:string);
function RPI_revnum:real; // 0:error
function RPI_gpiomapidx:byte; // 1:rev1; 2:rev2; 3:B+; 0:error
function RPI_cores:longint;
function RPI_BCM2835:boolean;
function RPI_BCM2835_GetNodeValue(node:string; var nodereturn:string):longint;
function RPI_status_led_GPIO:byte; // give GPIO_NUM of Status LED
function RPI_I2C_BRadj(i2c_speed_kHz:longint):longint;
function RPI_I2C_busnum(func:byte):byte; // get the I2C busnumber, where e.g. the general purpose devices are connected. This depends on rev1 or rev2 board . e.g. RPI_I2C_busnum(RPI_I2C_general_purpose_bus_c)
function RPI_I2C_busgen:byte; // general purpose bus
function RPI_I2C_bus2nd:byte; // 2.nd I2C bus
function RPI_I2C_GetSpeed(bus:byte):longword;
function RPI_I2C_GetFuncs(bus:byte):longword;
function RPI_I2C_ChkFuncs(bus:byte; funcs:longword):boolean;
function RPI_I2C_ChkDev(bus,adr:byte):integer;
function RPI_SPI_GetSpeed(bus:byte):longint;
function RPI_hdrpincount:byte; // connector_pin_count on HW Header
function RPI_GetBuildDateTimeString:string;
procedure RPI_show_cpu_info;
procedure RPI_MaintSetVersions(versmin,versmax:real);
procedure RPI_MaintDelEnv;
procedure RPI_MaintSetEnvExec(EXECcmd:string);
procedure RPI_MaintSetEnvFTP(FTPServer,FTPUser,FTPPwd,FTPLogf,FTPDefaults:string);
procedure RPI_MaintSetEnvUPL(UplSrcPackageRemark,UplSrcFiles,UplDstDir,UplLogf:string);
procedure RPI_MaintSetEnvDWN(DwnSrcDir,DwnlSrcFiles,DwnDstDir,DwnLogf:string);
procedure RPI_MaintSetEnvUPD(UpdPkgSrcFile,UpdPkgDstDir,UpdPkgDstFile,UpdPkgMaintDir,UpdPkgLogf:string);
function RPI_Maint(UpdFlags:s_rpimaintflags; var CurlThCtl:Thread_Ctrl_t):integer;
function RPI_INFO_Split(info:string; var labl,valu:string):boolean;
function RPI_cxt_GPIOopts(flgs:s_port_flags):string;
function RPI_config(raspicmd:t_RPI_config; par1,par2:string; var resultstring:string):integer;
procedure HAT_EEprom_Map(tl:TStringList; hwname,uuid,vendor,product:string; prodid,prodver,gpio_drive,gpio_slew,gpio_hysteresis,back_power:word; useDefault,EnabIO:boolean);
procedure HAT_EEprom_Map_Test;
function HAT_GetInfo:boolean;
function HAT_GetInfo(ovrwrt:boolean; duuid,dvendor,dproduct,dsnr:string; dpid,dpver:longword):boolean;
procedure HAT_ShowStruct;
procedure HAT_GetStructInfo(HAT_INFO_tl:TStringList; lgt:byte);
function HAT_vendor:string;
function HAT_product:string;
function HAT_product_id:string;
function HAT_product_ver:string;
function HAT_uuid:string;
function HAT_custom(tl:TStringList; keys,dflts:string):string;
function HAT_custom(tl:TStringList; keys,dflts,tr1,tr2:string):string;
procedure HAT_Info_Test;
function USB_Reset(buspath:string):integer; // e.g. USB_Reset('/dev/bus/usb/002/004');
function MapUSB(devpath:string):string; // e.g. MapUSB('/dev/ttyUSB0') -> /dev/bus/usb/002/004
procedure I2C_show_struct (busnum:byte);
procedure I2C_Display_struct(busnum:byte; comment:string);
procedure HW_IniInfoStruct(var DeviceStruct:HW_DevicePresent_t);
procedure HW_SetInfoStruct(var DeviceStruct:HW_DevicePresent_t; DevTyp:t_IOBusType; BusNr,HWAdr:integer; dsc:string);
function I2C_HWT(var DeviceStruct:HW_DevicePresent_t; bus,adr,lgt:word; cmds:string; Handle:integer; rv1,nv1,nv2,dsc:string):boolean;
function I2C_HWSpeedT(var DeviceStruct:HW_DevicePresent_t; lgt:word; loops:longword; cmds,dsc:string):real;
function I2C_HWSpeedT(BusNum,HWaddr,rdlgt:word; loops:longword; cmds,dsc:string):real;
procedure I2C_EnterCriticalSection(busnum:byte);
procedure I2C_LeaveCriticalSection(busnum:byte);
//function I2C_bus_SEGMENTS (var zipdata:I2C_rdwr_zip_data_t):integer;
//procedure I2C_prep_iomsg (var zipdata:I2C_rdwr_zip_data_t; baseadr,flag,lgt:word);
//procedure I2C_init_ZIPdata (var zipdata:I2C_rdwr_zip_data_t; siz,busnum:word; errhdl:integer);
function I2C_bus_WrRd (busnum,baseadr:word; const WRbuf:string; WRflgs:word; var RDbuf:string; RDflgs:word; RDlen:byte; errhdl:integer):integer;
function I2C_string_read (busnum,baseadr:word; const WRbuf:string; RDlen:byte; errhdl:integer; var RDbuf:string):integer;
function I2C_string_write (busnum,baseadr:word; const WRbuf:string; errhdl:integer):integer;
function I2C_ChkBusAdr (busnum,baseadr:word):boolean;
// ** old I2C functions, pls. use above only. Just for compatibility reasons
function I2C_byte_write (busnum,baseadr,basereg:word; data:byte; errhdl:integer):integer;
function I2C_byte_read (busnum,baseadr,basereg:word; errhdl:integer):byte;
function I2C_word_write (busnum,baseadr,basereg:word; data:word; flip:boolean; errhdl:integer):integer;
function I2C_word_read (busnum,baseadr,basereg:word; flip:boolean; errhdl:integer):word;
function I2C_string_read (busnum,baseadr,basereg:word; RDlen:byte; errhdl:integer; var RDbuf:string):integer;
function I2C_string_write (busnum,baseadr,basereg:word; WRbuf:string; errhdl:integer):integer;
//function I2C_bus_read (busnum,baseadr,basereg:word; len:byte; errhdl:integer):integer;
//function I2C_bus_write (busnum,baseadr:word; errhdl:integer):integer;
function oldI2C_string_read(busnum,baseadr,basereg:word; len:byte; errhdl:integer; var outs:string):integer;
function oldI2C_string_read(busnum,baseadr:word; cmds:string; len:byte; errhdl:integer; var outs:string):integer;
function oldI2C_string_write(busnum,baseadr:word; datas:string; errhdl:integer):integer;
function oldI2C_string_write(busnum,baseadr,basereg:word; datas:string; errhdl:integer):integer;
// END ** old functions
function SPI_HWT(var DeviceStruct:HW_DevicePresent_t; bus,adr,lgt:word; cmds:string; Handle:integer; rv1,nv1,nv2,dsc:string):boolean;
function SPI_Dev_Init(busnum,devnum,bpw,cs_change:byte; mode,maxspeed_hz:longword; delay_usec:word):boolean;
function SPI_Dev_Init(busnum,devnum:byte):boolean;
function SPI_ClkWrite(spi_hz:real):longword;
procedure SPI_SetDevErrHndl(busnum,devnum:byte; errhdl:integer);
procedure SPI_EnterCriticalSection(busnum:byte);
procedure SPI_LeaveCriticalSection(busnum:byte);
function SPI_Write(busnum,devnum:byte; basereg,data:word):integer;
function SPI_Read (busnum,devnum:byte; basereg:word) : byte;
function SPI_Transfer (busnum,devnum:byte; cmdseq:string):integer;
function SPI_Mode(spifd:cint; mode:longword; pvalue:pointer):integer;
procedure SPI_StartBurst(busnum,devnum:byte; reg:word; writeing:byte; len:longint);
procedure SPI_EndBurst(busnum,devnum:byte);
function SPI_BurstRead(busnum,devnum:byte):byte;
procedure SPI_BurstWriteBuffer(busnum,devnum,basereg:byte; len:longword);
procedure SPI_BurstRead2Buffer(busnum,devnum,basereg:byte; len:longword);
procedure SPI_show_buffer(busnum,devnum:byte);
procedure SPI_show_dev_info_struct(busnum,devnum:byte);
procedure SPI_show_bus_info_struct(busnum:byte);
procedure SPI_show_struct(var spi_strct:spi_ioc_transfer_t);
procedure SPI_AdrMuxInit(CSnum,adr0gpio,adr1gpio:longint);
procedure SPI_AdrMux(CSnum,adr:byte);
procedure SPI_AdrMux(adr:byte);
procedure eeprom_SetAddr(devaddr:word);
function eeprom_write_page(startadr:word; datas:string):integer;
function eeprom_read_page(startadr:word; len:byte; var outs:string):integer;
function Thread_Start (var ThreadCtrl:Thread_Ctrl_t; funcadr:TThreadFunc; paraadr:pointer; delaymsec:longword; prio:longint):boolean;
function Thread_End (var ThreadCtrl:Thread_Ctrl_t; waitmsec:longword):boolean;
procedure Thread_InitStruct0(var ThreadCtrl:Thread_Ctrl_t);
procedure Thread_InitStruct (var ThreadCtrl:Thread_Ctrl_t);
procedure Thread_InitStruct2(var ThreadCtrl:Thread_Ctrl_t; ThFunc:TThFunctionOneArgCall);
procedure Thread_SetName(name:string);
procedure Thread_ShowStruct(var ThreadCtrl:Thread_Ctrl_t);
procedure SetTimeOut (var EndTime:TDateTime;TimeOut_ms:Int64);
function TimeElapsed(var EndTime:TDateTime;Retrig_ms:Int64):boolean;
function TimeElapsed(EndTime:TDateTime):boolean;
procedure SetTimeOut_us (ptspec_start,ptspec_end:Ptimespec; Retrig_us:int64);
procedure SetTimeOut_us (ptspec:Ptimespec; Retrig_us:int64);
function TimeElapsed_us(ptspec:Ptimespec):boolean;
function TimeElapsed_us(ptspec:Ptimespec; Retrig_us:int64):boolean;
//procedure Log_Write (typ:T_ErrorLevel;msg:string); // writes to STDERR
procedure Log_Writeln(typ:T_ErrorLevel;msg:string); // writes to STDERR
procedure LOG_ShowStringList(typ:T_ErrorLevel; ts:TStringList);
function LOG_Level:t_ErrorLevel;
procedure Log_Level(level:t_ErrorLevel);
procedure LOG_LevelSave;
procedure LOG_LevelRestore;
procedure LOG_LevelColor(enab:boolean);
function LOG_GetEndMsg(comment:string):string;
function LOG_GetVersion(version:real):string;
function LOG_Get_LevelStringShort(lvl:T_ErrorLevel):string;
function ERR_string (var ERR_Rec:ERR_Rec_t):string;
procedure ERR_SetStep(var ERR_Rec:ERR_Rec_t; errnr:longint);
procedure ERR_SetStep(var ERR_Rec:ERR_Rec_t; errnr:longint; errmsg:string);
procedure ERR_SetStep(var ERR_Rec:ERR_Rec_t; errnr:longint; errtitle,errmsg:string);
procedure DUMP_CallStack(hdr:string);
procedure DUMP_ExceptionCallStack(hdr:string; E:Exception);
procedure DUMP_ExceptionCallStack(hdr:string; E:Exception; haltprog:boolean);
procedure SAY (typ:T_ErrorLevel; msg:string); // writes to STDOUT
procedure SAY (typ:T_ErrorLevel; const msg:string; const params:array of const);overload;
procedure SAY_TL(typ:T_ErrorLevel; tl:TStringList);
procedure SAY_Level(level:t_ErrorLevel);
function MSG_HUB(lvl:t_ErrorLevel; msgtype:MSG_Type_t; msg:string):longint;
procedure Get_SDcard_RDSpeed;
function GetIPAdr(iface:string; var ipaddr:string; ip4:boolean):boolean;
function GetMACAdr(iface:string; var hwaddr:string):boolean;
function GetHostName:string;
function GetDomainName(iface:string):string;
function GetDomainName:string;
function GetMainDomainName:string;
function GetWLANSignal(iface:string):longint; // -1,0-100
function IP_iface(aliasname:string):string;
function IS_Online(ip4:boolean):boolean;
function MAC_Addr(iface:string; fmt:byte):string;
function MAC_isRPI(macsubstr:string):boolean;
function IP4_Addr(iface:string):string;
function IP6_Addr(iface:string):string;
function IP4_AddrExt:string;
function IP4_AddrValid(ipstr:string):boolean;
function IP4AddrListValid(ipliststr:string):boolean;
function IP6_AddrValid(ipstr:string):boolean;
function IP6AddrListValid(ipliststr:string):boolean;
function IPAddrListValid(ipliststr:string):boolean;
function IP4AddrsInSameSubnet(ip4adr1,ip4adr2:string):boolean;
procedure LNX_sudo(sudouse:boolean);
function LNX_sudo:boolean;
function LNX_ProgInstalled(progname:string):boolean;
function LNX_ParSET(filnam,parnam,parval:string):integer;
function LNX_ParGET(filnam,parnam:string; var parval:string):integer;
function LNX_ParLinEXIST(filnam,parstr:string):boolean;
function LNX_GetProcessNumsByName(processname:string):string;
procedure LNX_KillProcesses(processlist:string; signal:word);
function LNX_chmod(filename:string; mode:TMode):cint;
function LNX_chowngrp(filename:string; owner,group:string):integer;
function LNX_chowngrpmod(filename:string; owner,group:string; mode:TMode):integer;
procedure LNX_GetUsrPwdString(StrList:TStringList; pwdfile,usrlst:string; carveflds:longint);
function LNX_UpdPwdFile(pwdfile,usr,pwd:string):integer;
function LNX_ChkUsrPwdValid(usr,pwd,pwddefault:string):integer;
function LNX_ChgUsrPwd(usr,usrreq,pwd,pwd2,pwddflt,pwdold:string; PWD_OLDsameNEW:boolean; var msg:string):integer;
function LNX_ChgUsrPwd(usr,pwd:string; var msg:string):integer;
function LNX_GetRandomAccessToken(typ:longint):string;
function LNX_GetTZList(ts:TStringList; fmt:byte):integer;
function LNX_GetISOquery(ts:TStringList; opts:string):integer;
function LNX_GetBase64String(str:string):string;
function LNX_GetNewestFile(filnampat:string):string;
function LNX_LinkFile(filnam,linknam:string):integer;
function LNX_tarSAV(target,fillst:string; flags:s_rpimaintflags):longint;
function LNX_tarRST(target,fillst:string; flags:s_rpimaintflags):longint;
function LNX_CertFormatTyp(certtyp:Cert_Type_t):string;
function LNX_CertIDget(filnam:string; certtyp:Cert_Type_t; idouttyp:Cert_Type_t; var id:string):boolean;
procedure LNX_CertIDtest;
procedure LNX_CertInit(var certstruct:cert_t);
function LNX_CertReg(var certstruct:cert_t; certfil:string; certtype:Cert_Type_t):boolean;
procedure LNX_CertPackShow(lvl:T_ErrorLevel; var certpack:cert_pack_t);
procedure LNX_CertInitPack(var certpack:cert_pack_t; num:longint);
function LNX_CertStartPack(var certpack:cert_pack_t; descr,pubcertfil,privkeyfil,cacertfil,combinedfil,passwd:string; certpacktyp:Cert_Type_t):boolean;
function LNX_EncryptFile(filpubkey,filnam,ext:string; flags:s_rpimaintflags):integer;
function LNX_DecryptFile(filprivkey,filnam,ext:string; flags:s_rpimaintflags):integer;
function LNX_RemoveOldFiles(path2files:string; days:longint):integer;
function LNX_ShellESC(s:string):string;
procedure LNX_ADD2Crontab(cmd:string);
function LNX_ErrDesc(errno:longint):string;
function LNX_SetDateTimeUTC(utc:TDateTime):boolean;
function LNX_WDOG(wdog_action:t_rpimaintflags; p1:longint):longint;
function LNX_WDOG(wdog_action:t_rpimaintflags):longint;
function LNX_SSHFSmount(site,pwd,mnt:string; var err:string):integer;
function BTLE_StartBeaconURL(url1,url2:string):longint;
function BTLE_StopBeacon:boolean;
procedure MinMaxAdj(var value:real; valmin,valmax:real);
function Limits(var value:longint; minvalue,maxvalue:longint):longint;
function Limits(var value:longword; minvalue,maxvalue:longword):longword;
function Limits(var value:real; minvalue,maxvalue:real):real;
procedure MinMax(value:longint; var minvalue,maxvalue:longint);
procedure MinMax(value:longword; var minvalue,maxvalue:longword);
procedure MinMax(value:real; var minvalue,maxvalue:real);
procedure STAT_Init(var stats:STAT_struct_t; arrsize:word);
procedure STAT_Reset(var stats:STAT_struct_t);
procedure STAT_Calc(var stats:STAT_struct_t; newval,tim_us:real);
procedure HeapStatINI(var struct:HeapStat_t; HSname:string; indentcnt:byte; replvl:T_ErrorLevel);
procedure HeapStat(var struct:HeapStat_t; idx:longint);
function CL_Compose(cmdLine:string):string;
function CL_Parse (cmdLine:string):t_CLOptions;
function CL_OptGiven(var cl_opts:t_CLOptions; opt:string):integer;
function FileAccessible(filnam:string):boolean;
procedure SetTextCol(typ:T_ErrorLevel);
procedure UnSetTextCol;
function Upper(const s : string) : String;
function Lower(const s : string) : String;
function Bool2Num(b:boolean) : byte;
function Bool2Str(b:boolean) : string;
function Bool2LVL(b:boolean) : string;
function Bool2Dig(b:boolean) : string;
function Bool2Swc(b:boolean) : string;
function Bool2OC (b:boolean) : string;
function Bool2YN (b:boolean) : string;
function Bool2YNS(b:boolean) : string;
function Bool2EA (b:boolean) : string;
function Bool2eas(b:boolean) : string;
function Bool2UpDown(b:boolean):string;
function TimeSpec2Str(pts:Ptimespec):string;
function TimeSpec2Num(pts:Ptimespec):real;
function Str2Bool(s:string):boolean;
function Str2Bool(s:string; var ein:boolean):boolean;
function Num2Limit(var Value:real; MinOut,MaxOut:real):boolean;
function Num2Str(num:int64):string;
function Num2Str(num:longint):string;
function Num2Str(num:longword):string;
function Num2Str(num:real;nk:byte):string;
function Num2Str(num:int64;lgt:byte):string;
function Num2Str(num:longint; lgt:byte):string;
function Num2Str(num:longword;lgt:byte):string;
function Num2Str(num:qword;lgt:byte):string;
function Num2Str(num:real;lgt,nk:byte):string;
function Str2Num(s:string; var num:byte):boolean;
function Str2Num(s:string; var num:smallint):boolean;
function Str2Num(s:string; var num:int64):boolean;
function Str2Num(s:string; var num:qword):boolean;
function Str2Num(s:string; var num:longint):boolean;
function Str2Num(s:string; var num:longword):boolean;
function Str2Num(s:string; var num:real):boolean;
function Str2Num(s:string; var num:extended):boolean;
function Str2NumFMT(s:string; nk:byte):string;
function Num2StrFMT(num:real; nk:byte):string;
function Str2CP437(s:string):string;
function Str2TimeSpec(s:string; var ts:timespec):boolean;
function Str2DateTime(tdstring,fmt:string; var dt:TDateTime):boolean;
function UnicodeStr2UTF8(unicodestr:string):string;
function Str2LogLvl(s:string):T_ErrorLevel;
function LogLvl2Str(lvl:T_ErrorLevel):string;
function GetLogLvls(tr:string):string;
function LeadingZero(w : Word) : String;
function LeadingZeros(l:longint;digits:byte):string;
function Bin(q:longword;lgt:Byte) : string;
//function Hex(nr:qword;lgt:byte) : string;
//function Hex(ptr:pointer;lgt:byte): string;
function HexStr(s:string):string;overload;
function StrHex(Hex_strng:string):string;
function AdjZahlDE(r:real;lgt,nk:byte):string;
function AdjZahl(s:string):string;
function adjL0(s:string):string;
function Adj_LF(strIN:string):string;
function FormatFileSize(const Size: Int64):string;
function scale(valin,min1,max1,min2,max2:real):real;
function Get_FixedStringLen(s:string;cnt:word;leading:boolean):string;
function StringReverse(s:string):string;
function ShortString(fmt,maxlgt,divdr:longint; str:string):string;
function KEYpressedChar(ch:char):boolean;
function ESCpressed:boolean;
procedure AskCR;
procedure AskCR(msg:string);
procedure AskCR(lvl:T_ErrorLevel; msg:string);
function AskYN(msg:string; dflt:string):boolean;
function AskStr(msg:string; var outstr:string):boolean;
function AskNum(von,bis:longint; msg:string; var outnum:longint):boolean;
function SepRemove(s:string):string;
function Trimme(s:string;modus:byte):string;//modus: 1:adjL 2:adjT 3:AdjLT 4:AdjLMT 5:AdjLMTandRemoveTABs
function FilterChar(s,filter:string):string;
function RemoveChar(s,filter:string):string;
function GetNumChar(s:string):string;
function GetAlphaNumChar(s:string):string;
function GetParserTokenChar(s:string):string;
function ContainDescenderLetter(s:string):boolean;
function GetHexChar(s:string):string;
function HashTag(var InString:string):string;
function HashTag(modus:byte; filname,comment1,comment2:string):string;
function ReplaceChars(s,filterchars,replacechar:string):string;
function RM_CRLF(s:string):string;
function SB_LF (s:string):string; // \n -> #$0a
function SB_CR (s:string):string; // \r -> #$0d
function SB_CRLF(s:string):string;
function SB_UnESC(s:string):string;
function BS_LF (s:string):string; // #$0a -> \n
function BS_CR (s:string):string; // #$0d -> \r
function BS_CRLF(s:string):string;
function BS_DoESC(s:string):string;
function BS_ALL (s:string):string;
function IPInfo_GetInterfaceName(intidx:longint):string;
function IPInfo_GetIdx(intface:string):longint;
procedure IPInfo_GetOS; // force OS read
procedure IPInfo_GetOS(var IPInfos:IP_Infos_t);
procedure IPInfoShow(lvl:T_ErrorLevel; var IPInfo:IP_Info_t);
procedure IPInfosShow(lvl:T_ErrorLevel; var IPInfos:IP_Infos_t);
function GetPrintableChars(s:string; c1,c2:char):string;
function CamelCase(strng:string):string;
function GetRndTmpFileName(filhdr,extname:string):string;
function Get_FName(fullfilename:string):string;
function Get_FName(fullfilename:string; withext:boolean):string;
function Get_FNameExt(fullfilename:string):string;
function Get_ExtName(fullfilename:string; extwithdot:boolean):string;
function Get_Dir(fullfilename:string):string;
function Get_DirList(dirname:string; filelist:TStringList):integer;
function GetTildePath(fullpath,homedir:string):string;
function PrepFilePath(fpath:string):string;
function IsDir(filname:string):boolean;
function SetFileAge(filname:string; mode:integer; fdat:TDateTime):integer;
function GetFileAge(filname:string):TDateTime;
function GetFileSize(filname:string):int64;
function GetFileAgeInSec(filname:string):int64;
function FileIsRecent(filepath:string; seconds_old,varianz:longint):boolean;
function FileIsRecent(filepath:string; seconds_old:longint):boolean;
function MStream2String(MStreamIn:TMemoryStream):string;
procedure String2MStream(MStreamIn:TMemoryStream; var SourceString:string);
function MStream2File(filname:string; StreamOut:TMemoryStream):boolean;
function File2MStream(filname:string;StreamOut:TMemoryStream; var hash:string):boolean;
function File2MString(filname:string; var OutString,hash:string):boolean;
function TextFile2StringList(filname:string; StrListOut:TStringList; append:boolean; var hash:string):boolean;
function StringListAdd2List(StrList1,StrList2:TStringList; append:boolean):longword;
function StringListAdd2List(StrList1,StrList2:TStringList):longword; //Adds StringList2 to Stringlist1. result is size of Stringlist in bytes
function StringList2TextFile(filname:string; StrListOut:TStringList):boolean;
function StringList2TextFile(filname:string; StrListOut:TStringList; append_mode:boolean):boolean;
function StringList2String(StrList:TStringList):string;
function StringList2String(StrList:TStringList; tr:string):string;
procedure String2StringList(str:string; StrList:TStringList);
function String2TextFile(filname:string; StrOut:string):boolean;
function TailFile(filname:string; LinesCount:longint):RawByteString;
procedure TailFileFollow(filname:string; LinesCount:longint);
procedure TL_prot_Init(var tlp:TL_prot_t);
procedure TL_prot_Stop(var tlp:TL_prot_t);
procedure STR_prot_Init(var slp:STR_prot_t);
procedure STR_prot_Stop(var slp:STR_prot_t);
function Anz_Item(const strng,trenner,trenner2:string): longint;
function Select_Item(const strng,trenner,trenner2,dflt:string;itemno:longint) : string;
function Select_Item(const strng,trenner,trenner2:string;itemno:longint) : string;
function Select_RightItems(const strng,trenner,trenner2:string;startitemno:longint) : string;
function Select_LeftItems (const strng,trenner,trenner2:string;enditemno:longint) : string;
function Locate_Value(const strng,search,tr1,tr2,tr3,tr4:string; var valoutstrng:string):boolean;
function CSV_RemLastSep(strng:string; sep:char):string;
function CSV_RemFirstSep(strng:string; sep:char):string;
procedure CSV_MaintList(var csvlst:string; entry:string; addit:boolean);
function CSV_MaintListToogleField(var csvlst:string; entry:string):boolean;
function StringPrintable(s:string):string;
procedure ShowStringList(StrList:TStringList);
function StringListMinMaxValue(StrList:TStringList; fieldnr:word; tr1,tr2:string; var min,max:extended; var nk:longint):boolean;
procedure StringListSnap(StrListIn,StrListOut:TStringList; srchstrng:string);
function SearchStringInListIdx(StrList:TStringList; srchstrng:string; occurance,StartIdx:longint):longint;
function SearchStringInList(StrList:TStringList; srchstrng:string):string;
function GiveStringListIdx(StrList:TStringList; srchstrng:string; var idx:longint; occurance:longint):boolean;
function GiveStringListIdx(StrList:TStringList; srchstrng:string; var idx:longint; occurance,StartIdx:longint):boolean;
function GiveStringListIdx(StrList:TStringList; srchstrngSTART,srchstrngEND:string; var idx:longint):boolean;
function GiveStringListIdx2(StrList:TStringList; srchstrng:string; var idxStart,idxEnd:longint):boolean;
procedure MemCopy(src,dst:pointer; size:longint);
procedure MemCopy(src,dst:pointer; size,srcofs,dstofs:longint);
function DeltaTime_in_ms(dt1,dt2:TDateTime):int64;
function CHK8(s:string):byte;
function CRC8(s:string):byte;
function CRC8_ok(s:string):boolean;
function MD5_HashGET(filnam:string; var MD5hash:string):boolean;
function MD5_HashCreateFile(filnam,MD5filnam:string; var MD5hash:string):boolean;
function MD5_HashGETFile(MD5filnam:string; var MD5hash:string):boolean;
function MD5_Check(file1,file2:string):boolean;
function MOD_Euclid(a,b:longint):longint;
function MovAvg(interval:longword; var InpArr,OutArr:array of real):longint; // moving average
function SearchValIdx(var InpArr:array of real; srchval,Epsilon:real):longint;
function TTY_sttySpeed(lvl:t_ErrorLevel; ttyandspeed:string):integer; // e.g. '/dev/ttyAMA0@9600'
function TTY_setterm(lvl:t_ErrorLevel; ttydev,ttyopts:string):integer; // e.g. '/dev/tty1' '--cursor off --clear all'
function TTY_console:string;
procedure SetUTCOffset; // time Offset in minutes form GMT to localTime
function GetDateTimeLocal:TDateTime;
function GetDateTimeLocal(utc:TDateTime):TDateTime;
function CalcUTCOffsetString(offset_Minutes:longint; withcolon:boolean):string; // e.g. '+02:00'
function GetUTCOffsetString:string; // e.g. '+02:00'
function GetUTCOffsetMinutes(offset_String:string):longint; // e.g. '-02:00' -> -120
function GetDateTimeUTC:TDateTime;
function GetDateTimeUTC(dt:TDateTime; tzofs:longint):TDateTime;
function GetTimeStamp(dt:TDateTime):string; // YEAR-MM-DD hh:mm:ss.zzz
function GetTZTimeStamp(dt:TDateTime):string; // YEAR-MM-DD hh:mm:ss.zzz+XX:XX
function GetXMLTimeStamp(dt:TDateTime):string; // YEAR-MM-DDThh:mm:ss.zzz+XX:XX
function GetDateTimefromXMLTimeStamp(tstmp:string; var dt:TDateTime; var tzofs:longint):boolean;
function GetUTCDateTimefromXMLTimeStamp(tstmp:string; var dt:TDateTime):boolean;
function GetDateTimefromUTC(tstmp:string; var dt:TDateTime):boolean;
function call_external_prog(typ:t_ErrorLevel; cmdline:string):integer;
function call_external_prog(typ:t_ErrorLevel; cmdline:string; var receivestring:string):integer;
function call_external_prog(typ:t_ErrorLevel; cmdline:string; receivelist:TStringList):integer;
function call_external_prog(typ:t_ErrorLevel; cmdline:string; receivelist:TStringList; timo_msec:word):integer;
function RunScript(filname,para:string):integer;
function RunScript(ts:TStringList; para:string):integer;
function RunScript(ts:TStringList; filname,para:string):integer;
function RunProcess(filname,para:string; syncwait:boolean):integer;
function RunProcess(cmds,filname,para:string; syncwait:boolean):integer;
function RunProcess(ts:TStringList; filname,para:string; syncwait:boolean):integer;
function PV_Progress(progressfile:string):integer;
function CURLcmdCreate(usrpwd,proxy,ofil,uri:string; flags:s_rpimaintflags):string;
procedure CURL_RemoveProgressfile(progressfile:string);
function CURL_DoProgressAction(var CurlThCtl:Thread_Ctrl_t; var terminate:boolean):boolean;
procedure CURL_SetPara(var CurlThCtl:Thread_Ctrl_t; info,curlcmd,logfile,filenamelist,dirname:string; updintervall_ms:integer; flgs:s_rpimaintflags);
function CURL(var CurlThCtl:Thread_Ctrl_t):integer;
procedure CURL_Test;
procedure TimeElapsed_us_Test;
procedure delay_nanos(Nanoseconds:int64);
procedure delay_us (Microseconds:longword);
procedure delay_msec (Milliseconds:longword);
function GetHighPrecisionCounter: Int64;
function clock_getres (clock_id:clockid_t; res:Ptimespec):longint;cdecl;external clib name 'clock_getres';
function clock_gettime (clock_id:clockid_t; tp: Ptimespec):longint;cdecl;external clib name 'clock_gettime';
function clock_settime (clock_id:clockid_t; tp: Ptimespec):longint;cdecl;external clib name 'clock_settime';
function clock_nanosleep (clock_id:clockid_t; flags:longint; req:Ptimespec; rem:Ptimespec):longint;cdecl;external clib name 'clock_nanosleep';
function clock_getcpuclockid(pid:pid_t; clock_id:Pclockid_t):longint;cdecl;external clib name 'clock_getcpuclockid';
{$IFDEF UNIX}
function usleep(Microseconds:cuint64):longint;cdecl;external 'libc'; //name 'usleep';
function getpt :cint; cdecl;external 'c'; // name 'getpt';
function grantpt (fd:cint):cint; cdecl;external 'c';
function unlockpt(fd:cint):cint; cdecl;external 'c';
function ptsname (fd:cint):pchar;cdecl;external 'c';
function tcgetattr(fd:cint; termios_p:Ptermios):cint;cdecl;external 'c';
function tcsetattr(fd:cint; optional_actions:cint; termios_p:Ptermios):cint;cdecl;external 'c';
procedure cfmakeraw(termios_p:Ptermios);cdecl;external 'c';
function tcsendbreak(fd:cint; duration:cint):cint;cdecl;external 'c';
function tcdrain(fd:cint):cint;cdecl;external 'c';
function tcflush(fd:cint; queue_selector:cint):cint;cdecl;external 'c';
function Term_ptmx(var termio:Terminal_device_t; link:string; menablemask,mdisablemask:longint):boolean;
function TermIO_Read(var term:Terminal_device_t; rawmode:boolean):string;
procedure TermIO_Write(var term:Terminal_device_t; str:string);
procedure Test_BiDirectionDevice_in_UserSpace; // write and read from /dev/testbidir
function FpPrCtl(options:cint; arg2,arg3,arg4,arg5:pointer):cint; cdecl; external clib name 'prctl';
function MicroSecondsBetween(ts1,ts2:timespec):int64;
function MicroSecondsBetween(ts:timespec):int64;
{$ELSE}
function MicroSecondsBetween(ts1,ts2:int64):int64;
{$ENDIF}
function MilliSecsBetween(td:TDateTime):int64;
procedure PID_Test;
function PID_DetPara(StrList:TStringList; idxStart,idxEnd,smoothdata,smoothtdr,loctim,locist,locSetPoint:longint; StoerSprung,timadjfct:real; var Ks,Te,Tb,Tsum,SampleTimeAvg:PID_float_t; tst:boolean):integer;
function PID_DetPara(loglvl:t_ErrorLevel; StrList:TStringList; idxStart,idxEnd,smoothdata,smoothtdr,loctim,locist,locSetPoint:longint; StoerSprung,timadjfct:real; var Ks,Te,Tb,Tsum,SampleTimeAvg:PID_float_t; tst:boolean; filout:string):integer;
function PID_GetPara(loglvl:t_ErrorLevel; Ks,Te,Tb,Tsum:PID_float_t; Method:PID_Method_t; var Ti,Td:PID_float_t; var K:PID_array_t; loginfo:string):integer;
procedure PID_Init(var PID_Struct:PID_Struct_t; nr:longint; itermax:longword; enab_twiddle:boolean; Ks,MinOutput0,MinOutput,MaxOutput,SampleTime_ms,WindupResetValue:PID_float_t; K,dK,tol:PID_array_t);
procedure PID_Reset(var PID_Struct:PID_Struct_t);
function PID_Calc(var PID_Struct:PID_Struct_t; SetPoint,ProcessValue:PID_float_t; Stoersprung:boolean):PID_float_t;
function PID_Calc(var PID_Struct:PID_Struct_t; SetPoint,ProcessValue:PID_float_t):PID_float_t;
procedure PID_SetIntImprove(var PID_Struct:PID_Struct_t; On_:boolean);
procedure PID_SetDifImprove(var PID_Struct:PID_Struct_t; On_:boolean);
procedure PID_SetMinMaxLimit(var PID_Struct:PID_Struct_t; MinOutput0,MinOutput,MaxOutput:PID_float_t);
procedure PID_SetSampleTimeAdjust(var PID_Struct:PID_Struct_t; On_:boolean);
procedure PID_SetSelfTuning(var PID_Struct:PID_Struct_t; On_:boolean);
procedure PID_InitTwiddle(var PID_Struct:PID_Struct_t);
procedure PID_InitTwiddle(var PID_Struct:PID_Struct_t; enab:boolean; itermax:longword; ap,adp,tol:PID_array_t);
procedure PID_SetTwiddle_KeyName(var TWIDDLE_Struct:PID_Twiddle_t; sect,key:string);
function PID_ReadTwiddle(sect,key:string; var K,dK,tol:PID_array_t):boolean;
function PID_ReadTwiddle(var TWIDDLE_Struct:PID_Twiddle_t; var K,dK,tol:PID_array_t):boolean;
procedure PID_SaveTwiddle(var TWIDDLE_Struct:PID_Twiddle_t; K,dK:PID_array_t);
function PID_VectorStr(var pidarr:PID_array_t; vk,nk:integer; sep:char):string;
function PID_Vector(Kp,Ki,Kd:PID_float_t):PID_array_t;
function PID_TDR(var TickArr,ValArr,OutTickDeltaArr,OutValArr:array of PID_float_t):longint;
function PID_DetType(Te,Tb:PID_float_t):integer;
function PID_TimAdj(timadjfct:real; var Te,Tb,TSum:PID_float_t):integer;
function PID_DetAvgs(IdxStart,IdxEnd:longint; var avgnumIst,avgnumPInc:longint):boolean;
function PID_FileLoad(StrList:TStringList; filnam,SearchCrit:string; var IdxStart,IdxEnd:longint):boolean;
function PID_sim(StrList:TStringList; simnr:integer):real;
procedure PID_SimCSV(tl:TStringList; var pid:PID_Struct_t);
procedure PID_Limit(var Value:PID_float_t; MinOut0,MinOut,MaxOut:PID_float_t);
procedure PID_TestSim;
function PID_Info(var PID_Struct:PID_Struct_t; fmt:longint):string;
function WAVE_InitArray(wavelist:TStringList; var wa:WAVE_Array_t; var valmin,valmax:real):longint;
function WAVE_InitArray(var wa:WAVE_Array_t; wavemode:WAVE_RampShape_t; valstart,valend:real; valcnt:longint; dtycycle:real):longint;
procedure WAVE_InitStruct(var wstruct:WAVE_Signal_Struct_t; var wa:WAVE_Array_t; wavemode:WAVE_RampShape_t; intervall_ms:longint);
procedure WAVE_Enable(var wstruct:WAVE_Signal_Struct_t; enab:boolean);
function WAVE_SetIdx(var wstruct:WAVE_Signal_Struct_t; var wa:WAVE_Array_t; startidx:longint):boolean;
function WAVE_GetIdx(var wstruct:WAVE_Signal_Struct_t; var wa:WAVE_Array_t):boolean;
procedure WAVE_Show(var wstruct:WAVE_Signal_Struct_t; var wa:WAVE_Array_t);
procedure WAVE_Test;
implementation
const int_filn_c='/tmp/GPIO_int_setup.sh';
prog_build_date = {$I %DATE%}; prog_build_time = {$I %TIME%};
var
_LOG_Level,_LOG_OLD_Level,_SAY_Level,_SAY_OLD_Level:T_ErrorLevel;
rpi_fw_api:RPI_FW_API_t;
rpi_timespecresolution:timespec;
_LOG_LevelColor,restrict2gpio,_OnExitShowRuntime:boolean;
GPIO_map_idx,I2C_busnum,connector_pin_count,status_led_GPIO:byte;
cpu_snr,cpu_hw,cpu_proc,cpu_rev,cpu_mips,cpu_feat,cpu_fmin,cpu_fcur,
cpu_machine,cpu_fmax,os_rev,cpu_fw,uname,sudo,whoami:string;
cpu_rev_num,cpu_freq,pll_freq:real;
RPI_ShutDownGPIO,cpu_cores: longint;
eeprom_devadr:word;
GPU_MEM_BASE,RPI_Throttle:longword;
oa,na:PSigActionRec;
RPIHDR_Desc:array[1..max_pins_c] of string[mdl];
//function Aligned(p:pointer; alig:byte):boolean; begin Aligned:=((PtrUint(p) mod alig)=0); end;
function Aligned(p:pointer; alig:byte):boolean; begin Aligned:=(p=Align(p,alig)); end;
procedure AlignShow;
begin
writeln('addr 0x'+HexStr(@msg),' (',PtrUInt(@msg),') aligned ',Aligned(@msg,32),' (',(PtrUint(@msg) mod 32),')');
end;
function ERR_string(var ERR_Rec:ERR_Rec_t):string;
begin
with ERR_Rec do
begin
ERR_string:=title+'['+Num2Str(step,0)+']: '+msg;
end; // with
end;
procedure ERR_SetStep(var ERR_Rec:ERR_Rec_t; errnr:longint);
begin ERR_Rec.step:=errnr; end;
procedure ERR_SetStep(var ERR_Rec:ERR_Rec_t; errnr:longint; errmsg:string);
begin ERR_Rec.step:=errnr; ERR_Rec.msg:=errmsg; end;
procedure ERR_SetStep(var ERR_Rec:ERR_Rec_t; errnr:longint; errtitle,errmsg:string);
begin ERR_Rec.step:=errnr; ERR_Rec.title:=errtitle; ERR_Rec.msg:=errmsg; end;
procedure DUMP_CallStack(hdr:string);
// https://wiki.lazarus.freepascal.org/Logging_exceptions
const MaxDepth = 20;
var I:Longint; prevbp:Pointer; CallerFrame,CallerAddress,bp:Pointer; _tl:TStringList;
begin
_tl:=TStringList.create;
_tl.add(hdr+' ##############################+');
bp := get_frame;
// This trick skip SendCallstack item
// bp:= get_caller_frame(get_frame);
try
prevbp := bp - 1;
I := 0;
while bp > prevbp do begin
CallerAddress := get_caller_addr(bp);
CallerFrame := get_caller_frame(bp);
if (CallerAddress = nil) then Break;
_tl.add(BackTraceStrFunc(CallerAddress));
Inc(I);
if (I >= MaxDepth) or (CallerFrame = nil) then Break;
prevbp := bp;
bp := CallerFrame;
end;
except
// prevent endless dump if an exception occured
end;
_tl.add(hdr+' ##############################-');
LOG_ShowStringList(LOG_ERROR,_tl);
_tl.free;
end;
procedure DUMP_ExceptionCallStack(hdr:string; E:Exception; haltprog:boolean);
// https://wiki.lazarus.freepascal.org/Logging_exceptions
var i:integer; Frames:PPointer; _tl:TStringList;
begin
_tl:=TStringList.create;
_tl.add(Trimme(hdr+' '+E.ClassName+' ##############################+',3));
_tl.add('Stacktrace: '+E.Message);
//if (E<>nil) then _tl.add('Exception class: '+E.ClassName);
_tl.add(BackTraceStrFunc(ExceptAddr));
Frames:=ExceptFrames;
for i:= 0 to (ExceptFrameCount-1) do _tl.add(BackTraceStrFunc(Frames[i]));
_tl.add(Trimme(hdr+' '+E.ClassName+' ##############################-',3));
LOG_ShowStringList(LOG_ERROR,_tl);
_tl.free;
if haltprog then Halt; // End of program execution
end;
procedure DUMP_ExceptionCallStack(hdr:string; E:Exception);
begin DUMP_ExceptionCallStack(hdr,E,true); end;
function MOD_Euclid(a,b:longint):longint;
var m:longint;
begin
if (b<>0) then
begin
m:=a mod b;
if (m<0) then
if (b<0) then m:=m-b else m:=m+b;
end else m:=0;
MOD_Euclid:=m;
end;
function RoundUpPow2(nr:real):longword; begin RoundUpPow2:=round(intpower(2,round(log2(nr)))); end;
function DivRoundUp(n,d:real):longword; begin DivRoundUp:=round((n+d-1)/d); end;
procedure delay_msec (Milliseconds:longword); begin if Milliseconds>0 then sysutils.sleep(Milliseconds); end;
function CHK8(s:string):byte; var i,chk:byte; begin chk:=$00; for i := 1 to Length(s) do chk:=chk + ord(s[i]); CHK8:=chk; end;
function CRC8(s:string):byte; var i,crc:byte; begin crc:=$00; for i := 1 to Length(s) do crc:=crc xor ord(s[i]); CRC8:=crc; end;
function CRC8_ok(s:string):boolean; var ok:boolean; begin ok:=false; if s<>'' then ok:=(ord(s[Length(s)])=CRC8(copy(s,1,Length(s)-1))); CRC8_ok:=ok; end;
procedure SetTimeOut (var EndTime:TDateTime;TimeOut_ms:Int64); begin EndTime:=IncMilliSecond(now,TimeOut_ms); end;
function TimeElapsed(EndTime:TDateTime):boolean; begin TimeElapsed:=(EndTime<=now); end;
function TimeElapsed(var EndTime:TDateTime; Retrig_ms:Int64):boolean;
var ok:boolean;
begin
ok:=(EndTime<=now);
if ok and (Retrig_ms>=0) then EndTime:=IncMilliSecond(now,Retrig_ms);
TimeElapsed:=ok;
end;
(*
type timespec = record
tv_sec: time_t; //Seconds cint64; INT64 (valid values are >= 0)
tv_nsec: clong; //Nanoseconds long; i32; nanoseconds(valid values are [0,999999999])
end;
*)
function TimeSpec_Diff(ptspec_start,ptspec_end:Ptimespec):timespec;
// https://gist.github.com/diabloneo/9619917
const nano_c:longword=1000000000;
var ts:timespec;
begin
if ((ptspec_end^.tv_nsec - ptspec_start^.tv_nsec) < 0) then
begin
ts.tv_sec:= ptspec_end^.tv_sec - ptspec_start^.tv_sec - 1;
ts.tv_nsec:=ptspec_end^.tv_nsec - ptspec_start^.tv_nsec + nano_c;
end
else
begin
ts.tv_sec:= ptspec_end^.tv_sec - ptspec_start^.tv_sec;
ts.tv_nsec:=ptspec_end^.tv_nsec - ptspec_start^.tv_nsec;
end;
TimeSpec_Diff:=ts;
end;
procedure SetTimeOut_ns (ptspec_start,ptspec_end:Ptimespec; Retrig_nsec:int64);
const nano_c:longword=1000000000;
begin
clock_gettime(CLOCK_REALTIME,ptspec_start);
ptspec_end^.tv_sec:=ptspec_start^.tv_sec;
if rpi_timespecresolution.tv_nsec=1 then
begin
ptspec_end^.tv_nsec:=(ptspec_start^.tv_nsec + Retrig_nsec) mod nano_c;
end
else
begin
ptspec_end^.tv_nsec:=(ptspec_start^.tv_nsec + Retrig_nsec div rpi_timespecresolution.tv_nsec) mod nano_c;
end;
if (ptspec_end^.tv_nsec < ptspec_start^.tv_nsec)
then begin if (Retrig_nsec>0) then inc(ptspec_end^.tv_sec); end
else begin if (Retrig_nsec<0) then dec(ptspec_end^.tv_sec); end;
//say(Log_INFO,'SetTimeOut_ns: '+TimeSpec2Str(ptspec_start)+' '+TimeSpec2Str(ptspec_end)+' ('+Num2Str(rpi_timespecresolution.tv_nsec,0)+')');
end;
procedure SetTimeOut_us (ptspec_start,ptspec_end:Ptimespec; Retrig_us:int64);
begin
try
SetTimeOut_ns(ptspec_start,ptspec_end,int64(Retrig_us*1000));
except
On E_rpi_hal_Exception :Exception do Writeln('SetTimeOut_us: ',Retrig_us,' ',E_rpi_hal_Exception.Message);
end;
end;
procedure SetTimeOut_us(ptspec:Ptimespec; Retrig_us:int64);
// e.g usage: SetTimeOut_us(@Ptimespec,123);
var tv_start:timespec; begin SetTimeOut_us(@tv_start,ptspec,Retrig_us); end;
function TimeElapsed_us(ptspec:Ptimespec; Retrig_us:int64):boolean;
var ok:boolean; tv_now:timespec;
begin
clock_gettime(CLOCK_REALTIME,@tv_now);
ok:=(ptspec^.tv_nsec<=tv_now.tv_nsec) and (ptspec^.tv_sec<=tv_now.tv_sec);
if ok and (Retrig_us>=0) then SetTimeOut_us(@tv_now,ptspec,Retrig_us);
TimeElapsed_us:=ok;
end;
function TimeElapsed_us(ptspec:Ptimespec):boolean;
begin TimeElapsed_us:=TimeElapsed_us(ptspec,-1) end;
{$IFDEF WINDOWS}
function CPUClockFrequency: Int64; var rslt:Int64; begin if not QueryPerformanceFrequency(rslt) then rslt:=-1; CPUClockFrequency:=rslt; end;
procedure InitHighPrecisionTimer; var F : Int64; begin F := CPUClockFrequency; HighPrecisionMillisecondFactor := F div 1000; HighPrecisionMicrosecondFactor := F div 1000000; HighPrecisionTimerInit := True; end;
function GetHighPrecisionCounter: Int64; var rslt:Int64; begin if not HighPrecisionTimerInit then InitHighPrecisionTimer; QueryPerformanceCounter(rslt); GetHighPrecisionCounter:=rslt; end;
procedure delay_nanos(Nanoseconds:longword); var i:longword; begin for i:=1 to 1000 do; end; // dummy
{$ELSE}
function GetHighPrecisionCounter: Int64; var rslt:Int64; TV : TTimeVal; TZ : PTimeZone; begin TZ := nil; fpGetTimeOfDay(@TV, TZ); rslt := Int64(TV.tv_sec) * 1000000 + Int64(TV.tv_usec); GetHighPrecisionCounter:=rslt; end;
procedure delay_nanos(Nanoseconds:int64);
const nano_c:longword=1000000000;
var sleeper,dummy : timespec;
begin
sleeper.tv_sec := Nanoseconds div nano_c;
sleeper.tv_nsec := Nanoseconds mod nano_c; // 0-999999999
fpnanosleep(@sleeper,@dummy);
end;
{$ENDIF}
{$IFDEF UNIX}
procedure delay_us(Microseconds:longword); begin usleep(Microseconds); end;
function NanoSecondsBetween(ts1,ts2:timespec):int64;
const nano_c:longword=1000000000;
var i64:int64;
begin
i64:= (ts1.tv_sec * nano_c + ts1.tv_nsec) -
(ts2.tv_sec * nano_c + ts2.tv_nsec);
if rpi_timespecresolution.tv_nsec<>1 then
i64:= i64 div rpi_timespecresolution.tv_nsec;
NanoSecondsBetween:=i64;
end;
function MicroSecondsBetween(ts1,ts2:timespec):int64;
begin MicroSecondsBetween:=NanoSecondsBetween(ts1,ts2) div 1000; end;
function MicroSecondsBetween(ts:timespec):int64;
var tsnow:timespec;
begin
clock_gettime(CLOCK_REALTIME,@tsnow);
MicroSecondsBetween:=MicroSecondsBetween(tsnow,ts);
end;
function MilliSecsBetween(td:TDateTime):int64;
begin MilliSecsBetween:=MilliSecondsBetween(now,td); end;
procedure TimeElapsed_us_Test;
(* TimeElapsed_us_Test: 720688613 720863195
TimeDelta in Microseconds: 174us
SetTimeOut_us: 720972204 1 721072204 *)
var tv_start,tv_end,tvh:timespec;
begin
writeln('rpi_timespecresolution: ',rpi_timespecresolution.tv_nsec,'ns');
clock_gettime(CLOCK_REALTIME,@tv_start);
//delay_us(100);
usleep(100);
clock_gettime(CLOCK_REALTIME,@tv_end);
writeln('TimeElapsed_us_Test: ',tv_end.tv_nsec,'-',tv_start.tv_nsec,'=',NanoSecondsBetween(tv_end,tv_start),'ns / ',MicroSecondsBetween(tv_end,tv_start),'us');
writeln('TimeDelta in NanoSeconds: ',tv_end.tv_nsec,'-',tv_start.tv_nsec,'=',(tv_end.tv_nsec-tv_start.tv_nsec),'ns');
tv_end.tv_nsec:=tv_start.tv_nsec+100000;
writeln('TimeElapsed_us_Test: ',tv_end.tv_nsec,'-',tv_start.tv_nsec,'=',NanoSecondsBetween(tv_end,tv_start),'ns / ',MicroSecondsBetween(tv_end,tv_start),'us');
writeln('TimeDelta in NanoSeconds: ',tv_end.tv_nsec,'-',tv_start.tv_nsec,'=',(tv_end.tv_nsec-tv_start.tv_nsec),'ns');
writeln;
SetTimeOut_us(@tv_start, @tv_end,10000); // requested 10000ns
while not TimeElapsed_us(@tv_end) do ;
clock_gettime(CLOCK_REALTIME,@tvh); // real time
writeln('Test TimeElapsed: ',NanoSecondsBetween(tv_end,tv_start),'ns');
writeln(' precision error: ',NanoSecondsBetween(tvh,tv_start)-NanoSecondsBetween(tv_end,tv_start),'ns');
//TimeElapsed_us_Test: 327148143-317147476=10000667ns / 10000us
end;
{$ELSE}
procedure delay_us(Microseconds:int64);
// https://github.com/fundamentalslib/fundamentals5/blob/master/Source/Utils/flcTimers.pas
var i,j,f:int64; n:longint;
begin
if Microseconds>0 then
begin
i:=GetHighPrecisionCounter;
if Microseconds>900 then
begin
n:= longint((Microseconds-900) div 1000); // number of ms with at least 900us in tight loop
if n>0 then begin sysutils.sleep(n); end;
end;
f:=int64(Microseconds*HighPrecisionMicrosecondFactor);
repeat j:=GetHighPrecisionCounter; until (int64(j-i)>=f);
end;
end;
function MicroSecondsBetween(us1,us2:int64):int64;
begin MicroSecondsBetween:=int64((us1-us2)*HighPrecisionMicrosecondFactor); end;
procedure TimeElapsed_us_Test;
const retrig_us=1000;
var i,j,n:int64; td:TDateTime;
begin
writeln('TimeElapsed_us_Test: Start');
n:=1; td:=now; i:=GetHighPrecisionCounter; j:=i;
repeat
if TimeElapsed_us(i,retrig_us) then inc(n);
until (n>=10000);
writeln('TimeElapsed_us_Test: ',MilliSecondsBetween(now,td),'ms ',MicroSecondsBetween(i,j),'us');
end;
{$ENDIF}
procedure USAGE_Init(nr:byte; var struct:HW_Usage_t; sect,key:string);
var sh:string;
begin
with struct do
begin
sh:=Select_Item(BIOS_GetIniString(sect,key,''),';','',nr);
if not Str2Num(Select_Item(sh,',','',1),usecnt) then usecnt:=0;
if not Str2Num(Select_Item(sh,',','',2),usetimesec) then usetimesec:=0;
dat:=now;
end;
end;
function CalcUTCOffsetString(offset_Minutes:longint; withcolon:boolean):string; // e.g. '+02:00'
var sh,sh1:string; mins,hours:longint;
begin
if offset_Minutes<0 then sh:='-' else sh:='+';
mins:=abs(offset_Minutes) mod 60; hours:=abs(offset_Minutes) div 60;
sh1:='00'+Num2Str(hours,0);
sh:=sh+copy(sh1,Length(sh1)-1,2);
if withcolon then sh:=sh+':';
sh1:='00'+Num2Str(mins,0); sh:=sh+copy(sh1,Length(sh1)-1,2);
//if sh='+00:00' then sh:='Z';
CalcUTCOffsetString:=sh;
end;
procedure SetUTCOffset; // time Offset in minutes form GMT to localTime
{$IFDEF MSWINDOWS} var BiasType: Byte; TZInfo: TTimeZoneInformation; {$ENDIF}
begin
_TZLocal:=0;
{$IFDEF WINDOWS}
BiasType := GetTimeZoneInformation(TZInfo);
case BiasType of // Determine offset
0 : _TZLocal := 0;
2 : _TZLocal := -(TZInfo.Bias + TZInfo.DaylightBias);
else _TZLocal := -(TZInfo.Bias + TZInfo.StandardBias);
end;
//writeln('Bias ',BiasType,' ',TZInfo.Bias,' ',TZInfo.DaylightBias,' ',TZInfo.StandardBias);
{$ENDIF}
{$IFDEF UNIX}
_TZLocal := Tzseconds div 60;
{$ENDIF}
_TZOffsetString:=CalcUTCOffsetString(_TZLocal,true);
end;
function GetUTCOffsetString:string; // e.g. '+02:00'
begin GetUTCOffsetString:=_TZOffsetString; end;
function GetUTCOffsetMinutes(offset_String:string):longint; // e.g. -02:00 -> -120
var mins,hours:longint;
begin
mins:=0; hours:=0;
if (Upper(offset_String)<>'Z') and (offset_String<>'') then
begin
if not Str2Num(Select_Item(offset_String,':','',2),mins) then mins:= 0;
if not Str2Num(Select_Item(offset_String,':','',1),hours) then hours:=0;
end;
GetUTCOffsetMinutes:=hours*60+mins;
end;
function GetDateTimeUTC(dt:TDateTime; tzofs:longint):TDateTime; begin GetDateTimeUTC:=IncMinute(dt,-tzofs); end;
function GetDateTimeUTC: TDateTime; begin GetDateTimeUTC:=GetDateTimeUTC(now,_TZLocal); end;
function GetDateTimeLocal: TDateTime; begin GetDateTimeLocal:=now; end;
function GetDateTimeLocal(utc:TDateTime):TDateTime; begin GetDateTimeLocal:=IncMinute(utc,_TZLocal); end;
function GetDateTimefromUTC(tstmp:string; var dt:TDateTime):boolean;
// IN: 'Fri, 22 Jun 2018 15:05:27 GMT'
var _ok:boolean;
begin
try
dt:=ScanDateTime('ddd, dd mmm yyyy hh:nn:ss',tstmp);
_ok:=true;
except
_ok:=false;
end;
GetDateTimefromUTC:=_ok;
end;
function GetXMLTimeStamp(dt:TDateTime):string; // YEAR-MM-DDThh:mm:ss.zzz+XX:XX
begin GetXMLTimeStamp:=FormatDateTime('YYYY-MM-DD"T"hh:mm:ss.zzz',dt)+_TZOffsetString; end;
function GetTimeStamp(dt:TDateTime):string; // YEAR-MM-DD hh:mm:ss.zzz
begin GetTimeStamp:=FormatDateTime('YYYY-MM-DD" "hh:mm:ss.zzz',dt); end;
function GetTZTimeStamp(dt:TDateTime):string; // YEAR-MM-DD hh:mm:ss.zzz+XX:XX
begin GetTZTimeStamp:=GetTimeStamp(dt)+_TZOffsetString; end;
function GetDateTimefromXMLTimeStamp(tstmp:string; var dt:TDateTime; var tzofs:longint):boolean;
// IN: 2018-06-26T16:01:12.070+02:00
// 2019-04-16T20:09:25.745+02:00
var _ok:boolean; p:longint; dats,tims:string;
begin
p:=Pos('T',tstmp);
if (p>0)
then begin tims:=copy(tstmp,p+1,Length(tstmp)); dats:=copy(tstmp,1,p-1); end
else begin tims:=tstmp; dats:=FormatDateTime('YYYY-MM-DD',now); end;
p:=Pos('Z',tims); // 16:01:12.070Z
if (p=0) then p:=Pos('+',tims); // 16:01:12.070+02:00
if (p=0) then p:=Pos('-',tims); // 16:01:12.070-02:00
if (p>0) then
begin
tzofs:= GetUTCOffsetMinutes(copy(tims,p,Length(tims)));
tims:= copy(tims,1,p-1);
end else tzofs:=0;
//writeln;writeln('GetDateTimefromXMLTimeStamp:',dats,'|',tims,'|',tzofs,'|',tstmp);
try
_ok:= Str2DateTime(dats+' '+tims,'YYYY-MM-DD hh:mm:ss.zzz',dt);
if not _ok then
_ok:= Str2DateTime(dats+' '+tims,'YYYY-MM-DD hh:mm:ss',dt);
except
_ok:=false;
end;
GetDateTimefromXMLTimeStamp:=_ok
end;
function GetUTCDateTimefromXMLTimeStamp(tstmp:string; var dt:TDateTime):boolean;
var _ok:boolean; tzofs:longint;
begin
_ok:=GetDateTimefromXMLTimeStamp(tstmp,dt,tzofs);
if _ok then dt:=GetDateTimeUTC(dt,tzofs);
GetUTCDateTimefromXMLTimeStamp:=_ok;
end;
function xGetDateTimefromXMLTimeStamp(tstmp:string; var dt:TDateTime; var tzofs:longint):longint;
// if res bit set (part included): Bit2=1:TZofs Bit1=1:DatePart Bit0=1:msecPart Bit3-6:reserved
// res: <0 -> invalid XMLDateTimeString (and Bit13 set)
// res: 0 -> contains TimePart only
// res: 1-7 -> contains TimePart and further parts according to bit set
var res,p:longint; dats,tims,msecs,tzofss:string;
begin
res:=0; dt:=now; dats:=FormatDateTime('YYYY-MM-DD',now);
tims:=tstmp; msecs:='000'; tzofss:='+00:00';
if Pos('T',tstmp)>0 then
begin
dats:=Select_Item(tstmp,'T','',1); tims:=Select_Item(tstmp,'T','',2);
res:=(res or $02); // contains Date part
end;
p:=Pos('Z',tims); // 16:01:12.070Z
if (p=0) then p:=Pos('+',tims); // 16:01:12.070+02:00
if (p=0) then p:=Pos('-',tims); // 16:01:12.070-02:00
if (p>0) then
begin
tzofss:=copy(tims,p,Length(tims));
tims:= copy(tims,1,p-1);
res:= (res or $04); // contains TZofs part
tzofs:= GetUTCOffsetMinutes(tzofss);
end else tzofs:=0;
if Pos('.',tims)>0 then
begin
msecs:=Select_Item(tims,'.','',2); tims:=Select_Item(tims,'.','',1);
res:=(res or $01); // contains msec part
end;
//det field contains '*'
if msecs='*' then res:=(res or $0010); // msec contains *
if Select_Item(tims,':','',3)='*' then res:=(res or $0020); // sec contains *
if Select_Item(tims,':','',2)='*' then res:=(res or $0040); // min contains *
if Select_Item(tims,':','',1)='*' then res:=(res or $0080); // hour contains *
if Select_Item(dats,'-','',3)='*' then res:=(res or $0100); // day contains *
if Select_Item(dats,'-','',2)='*' then res:=(res or $0200); // month contains *
if Select_Item(dats,'-','',1)='*' then res:=(res or $0400); // year contains *
if ((res and $0010)>0) then msecs:='000'; // msec
if ((res and $00e0)>0) then tims:= StringReplace(tims, '*','00', [rfReplaceAll,rfIgnoreCase]); // hh:mm:ss
if ((res and $0700)>0) then dats:= StringReplace(dats, '*','01', [rfReplaceAll,rfIgnoreCase]); // YYYY-MM-DD
//writeln(dats,'|',tims,'|',msecs,'|',tzofss,'|');
if not Str2DateTime(dats+'T'+tims+'.'+msecs,'YYYY-MM-DD"T"hh:mm:ss.zzz',dt)
then res:=-(res or $2000); // dt is not valid, but Bit0-2 show Parts
xGetDateTimefromXMLTimeStamp:=res;
end;
procedure TST_GetDateTimefromXMLTimeStamp(tstmp:string);
var ok:boolean; dt:TDateTime; tzofs:longint;
begin
dt:=0;
writeln(tstmp);
ok:=GetDateTimefromXMLTimeStamp(tstmp,dt,tzofs);
writeln(FormatDateTime('YYYY-MM-DD" "hh:mm:ss.zzz',dt),' ',tzofs:0,' ',ok);
if ok then
writeln(FormatDateTime('YYYY-MM-DD" "hh:mm:ss.zzz',GetDateTimeUTC(dt,tzofs)),' (UTC)');
writeln;
end;
procedure GetDateTimefromXMLTimeStamp_Test;
begin
TST_GetDateTimefromXMLTimeStamp('2017-07-06T16:01:12.070-02:00');
TST_GetDateTimefromXMLTimeStamp('16:01:12.070+02:00');
TST_GetDateTimefromXMLTimeStamp('16:01:12+02:00');
TST_GetDateTimefromXMLTimeStamp('16:01:12.123456');
TST_GetDateTimefromXMLTimeStamp('16:01:12.070Z');
TST_GetDateTimefromXMLTimeStamp('2017-07-06 16:01:12.070-02:00');
end;
function LogLvl2Str(lvl:T_ErrorLevel):string;
begin
LogLvl2Str:=StringReplace(
GetEnumName(TypeInfo(T_ErrorLevel),ord(lvl)),'LOG_','',[rfReplaceAll,rfIgnoreCase]);
end;
function Str2LogLvl(s:string):T_ErrorLevel;
var lvl:T_ErrorLevel; slvl:string;
begin
lvl:=LOG_WARNING; slvl:=Upper(s);
if Pos('ERROR', slvl)>0 then lvl:=LOG_ERROR;
if Pos('WARNING', slvl)>0 then lvl:=LOG_WARNING;
if Pos('NOTICE', slvl)>0 then lvl:=LOG_NOTICE;
if Pos('INFO', slvl)>0 then lvl:=LOG_INFO;
if Pos('DEBUG', slvl)>0 then lvl:=LOG_DEBUG;
if Pos('URGENT', slvl)>0 then lvl:=LOG_URGENT;
if Pos('NONE', slvl)>0 then lvl:=LOG_NONE;
if Pos('ALL', slvl)>0 then lvl:=LOG_ALL;
Str2LogLvl:=lvl;
end;
function GetLogLvls(tr:string):string;
var sh:string;
begin
sh:='ERROR'+tr+'WARNING'+tr+'INFO';
GetLogLvls:=sh;
end;
function LOG_Get_LevelStringShort(lvl:T_ErrorLevel):string;
var s:string;
begin
s:='UKN';
case lvl of
(* LOG_WHITE,LOG_BLACK,LOG_BLUE,LOG_LHTGRN,
LOG_GREEN,LOG_YELLOW,LOG_ORANGE,
LOG_RED: s:='COL'; *)
LOG_RED: s:='RED';
LOG_ORANGE: s:='ORA';
LOG_YELLOW: s:='YLW';
LOG_GREEN: s:='GRN';
LOG_LHTGRN: s:='LGR';
LOG_BLUE: s:='BLU';
LOG_BLACK: s:='BLK';
LOG_WHITE: s:='WHT';
LOG_MAGENTA:s:='MAG';
LOG_ERROR: s:='ERR';
LOG_WARNING:s:='WRN';
LOG_NOTICE: s:='SUC';
LOG_INFO: s:='INF';
LOG_DEBUG: s:='DBG';
LOG_URGENT: s:='URG';
LOG_ALL: s:='ALL';
LOG_NONE2,
LOG_NONE: s:='NON';
else s:=LogLvl2Str(lvl);
end;
LOG_Get_LevelStringShort:=s;
end;
function Get_LogString(host,processname,processnr:string;typ:T_ErrorLevel):string;
{.c delivers LogString Header with format: YEAR-MM-DD hh:mm:ss host processname[processnr] }
var s:string;
begin
s:=FormatDateTime('YYYY-MM-DD hh:mm:ss',now);
if host <>'' then s:=s+' '+host;
if processname <>'' then s:=s+' '+processname;
if processnr <>'' then s:=s+' ['+processnr+']';
s:=s+' ';
(* s:=s+' NC'+' ['+host+'] '; *)
(* s:=s+' '+host+' ['+processnr+'] '; *)
s:=s+LOG_Get_LevelStringShort(typ)+' ';
Get_LogString:=s;
end;
procedure HeapStatINI(var struct:HeapStat_t; HSname:string; indentcnt:byte; replvl:T_ErrorLevel);
var _n:longint;
begin
with struct do
begin
lvl:=replvl;
if (HSname<>'') then name:=HSname else name:='HeapStat';
name:=copy(' ',1,indentcnt)+name;
for _n:= 0 to HeapStatMax_c do HeapStatAlloc[_n]:=0;
HeapStatAlloc[0]:=GetHeapStatus.TotalAllocated;
end; // with
end;
procedure HeapStat(var struct:HeapStat_t; idx:longint);
begin
with struct do
begin
if (idx>=0) and (idx<=HeapStatMax_c) then
begin
HeapStatAlloc[idx]:=GetHeapStatus.TotalAllocated;
if (HeapStatAlloc[0]<>HeapStatAlloc[idx]) then
begin
(* SetTextCol(lvl);
writeln(Get_LogString('','','',lvl)+name+'[0/'+Num2Str(idx,2)+']: '+Num2Str(HeapStatAlloc[0],7)+' '+Num2Str(HeapStatAlloc[idx],7)+#$0d);
UnSetTextCol; *)
HeapStatAlloc[0]:=HeapStatAlloc[idx];
end;
end else LOG_Writeln(LOG_ERROR,'HeapStat['+Num2Str(idx,0)+'/'+Num2Str(HeapStatMax_c,0)+']: increase const HeapStatMax_c');
end; // with
end;
function FileAccessible(filnam:string):boolean;
var res:longint; {$IFDEF UNIX}info:stat;{$ENDIF}
begin
res:=-1; filnam:=PrepFilePath(Trimme(filnam,3));
if (filnam<>'') then
begin
{$IFDEF UNIX}
if (fpstat(filnam,info)<>0) then
begin
res:=fpGetErrNo;
LOG_Writeln(LOG_ERROR,'FileAccessible['+Num2Str(res,0)+'] '+SysErrorMessage(res)+': '+filnam);
end else res:=0;
{$ELSE}
if FileExists(filnam) then res:=0;
if (res<>0) then LOG_Writeln(LOG_ERROR,'FileAccessible: file not exist '+filnam);
{$ENDIF}
end;
FileAccessible:=(res=0);
end;
procedure ColTest;
var b:byte;
begin
for b:=0 to 255 do
begin
if (b<>blink) then
begin // no blink
TextColor(b);
SAY(LOG_INFO,Num2Str(b,3)+' TextTextTextTextTextTextTextTextTextTextText');
end else SAY(LOG_INFO,Num2Str(b,3)+' Blink');
NormVideo;
end;
end;
procedure SetTextCol(typ:T_ErrorLevel);
begin
if _LOG_LevelColor then
begin
case typ of
LOG_ERROR: TextColor(red);
LOG_WARNING: TextColor(yellow);
LOG_NOTICE: TextColor(green);
LOG_MAGENTA: TextColor(magenta);
LOG_WHITE: TextColor(white);
LOG_BLACK: TextColor(black);
LOG_BLUE: TextColor(blue);
end; // case
end;
end;
procedure UnSetTextCol; begin if _LOG_LevelColor then NormVideo; end;
function MSG_HUB(lvl:t_ErrorLevel; msgtype:MSG_Type_t; msg:string):longint;
// Hook to pass messages to upper level units. OLED displays...
// install: MSG_HUB_ptr:=@YourOwnFunction;
var res:longint;
begin
if (MSG_HUB_ptr<>nil) then res:=MSG_HUB_ptr(lvl,msgtype,msg) else res:=-1;
MSG_HUB:=res;
end;
function Adj_LF(strIN:string):string;
begin Adj_LF:=StringReplace(strIN,#$0a,#$0d+#$0a,[rfReplaceAll]); end;
// writes to STDOUT
function SAY_Level:t_ErrorLevel; begin SAY_Level:=_SAY_Level; end;
procedure SAY_LevelSave; begin _SAY_OLD_Level:=_SAY_Level; end;
procedure SAY_Level(level:t_ErrorLevel); begin SAY_LevelSave; if level<LOG_NONE then _SAY_Level:=level else _SAY_Level:=LOG_NONE2; end;
procedure SAY_LevelRestore; begin SAY_Level(_SAY_OLD_Level); end;
procedure SAY (typ:T_ErrorLevel; msg:string);
begin if typ>=_SAY_Level then begin SetTextCol(typ); writeln(Get_LogString('','','',typ)+msg+#$0d); UnSetTextCol; end; end;
procedure SAY (typ:T_ErrorLevel; const msg:string; const params:array of const);overload; begin SAY(typ,Format(msg,params)); end;
procedure SAY_TL(typ:T_ErrorLevel; tl:TStringList); var i:longint; begin for i:=1 to tl.count do SAY(typ,tl[i-1]); end;
procedure Log_Writeln(typ:T_ErrorLevel;msg:string);
begin
if typ>=_LOG_Level then
begin
SetTextCol(typ);
write(StdErr,#$0d+Get_LogString('','','',typ)+msg+#$0d+#$0a);
UnSetTextCol;
end else write(StdErr,#$0d);
end;
function Log_Shorting:boolean; begin Log_Shorting:=false; end;
procedure LOG_ShowStringList(typ:T_ErrorLevel; ts:TStringList);
var i:longint;
begin
if (typ>=_LOG_Level) then
begin
if LOG_Shorting then
begin
if ts.Count>=35 then
begin
for i := 1 to 13 do Log_Writeln(typ,ts[i-1]);
Log_Writeln(typ,'<! Output shortend, total lines: '+Num2Str(ts.count,0)+'>');
for i := ts.Count-6 to ts.Count do Log_Writeln(typ,ts[i-1]);
end
else for i := 1 to ts.Count do Log_Writeln(typ,ts[i-1]);
end
else
begin
for i := 1 to ts.Count do Log_Writeln(typ,ts[i-1]);
end;
Flush(ErrOutput);
end;
end;
function LOG_Level:t_ErrorLevel; begin LOG_Level:=_LOG_Level; end;
procedure LOG_LevelSave; begin _LOG_OLD_Level:=_LOG_Level; end;
procedure LOG_Level(level:T_ErrorLevel); begin LOG_LevelSave; if level<LOG_NONE then _LOG_Level:=level else _LOG_Level:=LOG_NONE2; end;
procedure LOG_LevelRestore; begin LOG_Level(_LOG_OLD_Level); end;
procedure LOG_LevelColor(enab:boolean); begin _LOG_LevelColor:=enab; end;
function LOG_GetEndMsg(comment:string):string;
var sh:string;
begin
if comment<>'' then sh:=comment else sh:=ApplicationName;
LOG_GetEndMsg:=sh+' ended at '+FormatDateTime('dd.mm.yyyy hh:mm:ss.zzz',now)+', runtime was '+FormatDateTime('hh:mm:ss.zzz',Now-RPI_ProgramStartTime);
end;
function LOG_GetVersion(version:real):string;
begin LOG_GetVersion:=ApplicationName+' V'+Num2Str(version,0,3)+' build '+RPI_GetBuildDateTimeString; end;
function BIT_Get(v:byte; i:byte):boolean; begin BIT_Get:=((v shr i) and 1)=1; end;
function BIT_Get(v:word; i:byte):boolean; begin BIT_Get:=((v shr i) and 1)=1; end;
function BIT_Get(v:longword; i:byte):boolean; begin BIT_Get:=((v shr i) and 1)=1; end;
function BIT_Get(v:qword; i:byte):boolean; begin BIT_Get:=((v shr i) and 1)=1; end;
procedure BIT_Clr(var v:byte; i:byte); begin v:=v and ((1 shl i) xor High(byte)); end;
procedure BIT_Clr(var v:word; i:byte); begin v:=v and ((1 shl i) xor High(word)); end;
procedure BIT_Clr(var v:longword; i:byte); begin v:=v and ((1 shl i) xor High(longword)); end;
procedure BIT_Clr(var v:qword; i:byte); begin v:=v and ((1 shl i) xor High(qword)); end;
procedure BIT_Set(var v:byte; i:byte); begin v:=v or (1 shl i); end;
procedure BIT_Set(var v:word; i:byte); begin v:=v or (1 shl i); end;
procedure BIT_Set(var v:longword; i:byte); begin v:=v or (1 shl i); end;
procedure BIT_Set(var v:qword; i:byte); begin v:=v or (1 shl i); end;
procedure BIT_Put(var v:byte; i:byte; b:boolean); begin v:=(v and ((1 shl i) xor High(byte))) or (byte(b) shl i); end;
procedure BIT_Put(var v:word; i:byte; b:boolean); begin v:=(v and ((1 shl i) xor High(word))) or (word(b) shl i); end;
procedure BIT_Put(var v:longword; i:byte; b:boolean); begin v:=(v and ((1 shl i) xor High(longword))) or (longword(b) shl i); end;
procedure BIT_Put(var v:qword; i:byte; b:boolean); begin v:=(v and ((1 shl i) xor High(qword))) or (qword(b) shl i); end;
function MSK_Get8(bitnum:byte):byte; begin MSK_Get8:=(1 shl (bitnum and $07)); end; //IN: bitnum 0-7
function MSK_Get16_8(bitnum:byte; var idxofs:byte):byte;
//IN: bitnum 0-15
//IDX: 0:bitnum 0-7 1:bitnum 8-15
begin
idxofs:=((bitnum and $0f) shr 3);
MSK_Get16_8:=(1 shl (bitnum mod 8));
end;
function MSK_Get64_8(bitnum:byte; var idxofs:byte):byte;
//IN: bitnum 0-63
begin
idxofs:=((bitnum and $3f) shr 3);
MSK_Get64_8:=(1 shl (bitnum mod 8));
end;
function MSK_Get256_8(bitnum:byte; var idxofs:byte):byte;
//IN: bitnum 0-255
begin
idxofs:=((bitnum and $ff) shr 3);
MSK_Get256_8:=(1 shl (bitnum mod 8));
end;
procedure TL_prot_Init(var tlp:TL_prot_t);
begin
with tlp do
begin
InitCriticalSection(TL_CS);
TL:=TStringList.create;
TL_modified:=false;
Thread_InitStruct0(ThCtl);
end; // with
end;
procedure TL_prot_Stop(var tlp:TL_prot_t);
begin
with tlp do
begin
Thread_InitStruct(ThCtl);
TL.free;
DoneCriticalSection(TL_CS);
end; // with
end;
procedure STR_prot_Init(var slp:STR_prot_t);
begin
with slp do
begin
InitCriticalSection(STR_CS);
STR:='';
STR_modified:=true;
end; // with
end;
procedure STR_prot_Stop(var slp:STR_prot_t);
begin
with slp do
begin
DoneCriticalSection(STR_CS);
STR:='';
STR_modified:=false;
end; // with
end;
function LNX_ResolveIP2name(IP:string):string;
var _tl:TStringList; idx:longint; sh:string;
begin // todo
sh:=IP;
{$IFDEF UNIX}
_tl:=TStringList.create;
if (call_external_prog(LOG_NONE,'arp -a',_tl)=0) then
begin
idx:=SearchStringInListIdx(_tl,' ('+IP+') ',1,0);
if (idx>=0) then
begin // found e.g. rpi3b_1w.abc.def.com (10.8.81.132) at <incomplete> on wlan0
sh:=Trimme(Select_Item(_tl[idx],' (','',1),3); // rpi3b_1w.abc.def.com
if (sh='') then sh:=IP;
end;
end;
_tl.free;
{$ENDIF}
LNX_ResolveIP2name:=sh;
end;
procedure LNX_WDOG_LastChanceHandler;
begin
LOG_Writeln(LOG_WARNING,'LNX_WDOG_LastChanceHandler');
end;
function LNX_WDOG_Thread(ptr:pointer):ptrint;
var i64:int64; sh:string;
begin
//SAY(LOG_WARNING,'LNX_WDOG_Thread: start');
try
Thread_SetName('WDOG');
with wdog do
begin
with ThreadCtrl do
begin
TermThread:=false; ThreadRunning:=true;
repeat
if not TermThread then
begin
i64:=DeltaTime_in_ms(WDOGFire,now);
if (i64<=retival_msec) then
begin
sh:='LNX_WDOG_Thread: WDOG will fire within '+Num2Str(i64,0)+'msec';
if (i64>=1) then LOG_Writeln(LOG_WARNING,sh)
else LOG_Writeln(LOG_ERROR, sh);
if (LastChanceHandler_ptr<>nil) then LastChanceHandler_ptr;
end;
delay_msec(retival_msec);
if RetrigAsync then
begin
// SAY(LOG_WARNING,'WDOG: RetrigAsync');
LNX_WDOG(WDOG_Retrig); // retrigger WDOG
end;
end;
until terminateProg or TermThread;
TermThread:=true; ThreadRunning:=false;
end; // with
end; // with
except
On E_rpi_hal_Exception :Exception do writeln('LNX_WDOG_Thread: ',E_rpi_hal_Exception.Message);
end;
//SAY(LOG_WARNING,'LNX_WDOG_Thread: end');
EndThread;
LNX_WDOG_Thread:=0;
end;
procedure LNX_WDOG_Init(var struct:watchdog_struct_t);
var n:longint;
begin
with struct do
begin
Hndl:=-1; devpath:=wdoc_path_c; RetrigAsync:=true;
ival_sec:=15; retival_msec:=(ival_sec*1000) div 5;
Thread_InitStruct (ThreadCtrl);
LastBootStat:=0; LastChanceHandler_ptr:=@LNX_WDOG_LastChanceHandler;
NextTrigTime:=now; SetTimeOut(WDOGFire,(ival_sec*1000));
with info do
begin
options:=0; firmware_version:=0;
for n:=0 to 31 do identity[n]:=$00;
end;
end; // with
end;
function LNX_WDOG_Start:boolean;
(* https://embeddedfreak.wordpress.com/2010/08/23/howto-use-linux-watchdog/
https://github.com/binerry/RaspberryPi/blob/master/snippets/c/watchdog/wdt_test.c
https://github.com/torvalds/linux/blob/master/include/uapi/linux/watchdog.h
https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/fcntl.h *)
begin
with wdog do
begin
if (Hndl>=0) then LNX_WDOG(WDOG_Close); // close old WDOG and reopen
LNX_WDOG(WDOG_Pause); // retrig by Thread, not controlled by main prog (sync)
Hndl:=fpOpen(devpath, (O_RDWR or O_NOCTTY));
if (Hndl<0) then
begin
LOG_Writeln(LOG_ERROR,'LNX_WDOG['+Num2Str(Hndl,0)+']: can not open '+devpath);
Hndl:=-1;
end
else
begin
ival_sec:=LNX_WDOG(WDOG_GTO);
(* if (wtim_ms=0) then wtim_ms:=((ival_sec*1000) div 3); // get wdog timeout
if (wtim_ms=0) then wtim_ms:=2000;
retival_msec:=wtim_ms; *)
SAY(LOG_INFO,'LNX_WDOG['+Num2Str(Hndl,0)+'/'+Num2Str(ival_sec,0)+'/'+Num2Str(retival_msec,0)+']: init succesful '+devpath);
end;
LNX_WDOG_Start:=(Hndl>=0);
end; // with
end;
function LNX_WDOG(wdog_action:t_rpimaintflags; p1:longint):longint;
var c:char='V'; res:longint; sh:string;
begin
res:=-1;
with wdog do
begin
if (Hndl>=0) then
begin
case wdog_action of
WDOG_Close: begin // disable and close watchdog device
LNX_WDOG(WDOG_Resume);
ThreadCtrl.TermThread:=true; // signal Thread terminate
if ((info.options and WDIOF_MAGICCLOSE)<>0) then
begin
c:='V'; res:=fpwrite(Hndl,c,1); // disable WDOG
fpClose(Hndl);
SAY(LOG_INFO,'LNX_WDOG['+Num2Str(Hndl,0)+'/'+Num2Str(res,0)+']: closed '+devpath);
end else LOG_Writeln(LOG_ERROR,'WDOG no support WDIOF_MAGICCLOSE');
Hndl:=-1;
end;
WDOG_Retrig:begin // retrigger WDOG
if ((info.options and WDIOF_KEEPALIVEPING)<>0) then
begin
{$R-}
if TimeElapsed(NextTrigTime,retival_msec) then
begin
SetTimeOut(WDOGFire,(ival_sec*1000));
SAY(LOG_DEBUG,'LNX_WDOG[Retrig]: retrigger');
res:=fpIOCTL(Hndl, WDIOC_KEEPALIVE, nil);
// c:='W'; res:=fpwrite(Hndl,c,1);
end;
{$R+}
end else LOG_Writeln(LOG_ERROR,'WDOG no support WDIOF_KEEPALIVEPING');
end;
WDOG_GTO: begin // get timeout (sec)
{$R-}
res:=fpIOCTl(Hndl, WDIOC_GETTIMEOUT, @ival_sec);
SAY(LOG_DEBUG,'LNX_WDOG: timeout is '+Num2Str(ival_sec,0));
if (res<>0) then
begin
LOG_Writeln(LOG_ERROR,'LNX_WDOG[GTO]: '+Num2Str(fpGetErrno,0));
res:=-1;
end
else
begin
retival_msec:=(ival_sec*1000) div 5;
res:=ival_sec;
end;
{$R+}
end;
WDOG_STO: begin // set timeout (sec)
if ((info.options and WDIOF_SETTIMEOUT)<>0) then
begin
{$R-}
if (p1>0) then ival_sec:=p1 else ival_sec:=15;
SAY(LOG_DEBUG,'LNX_WDOG: timeout set '+Num2Str(ival_sec,0));
res:=fpIOCTL(Hndl, WDIOC_SETTIMEOUT, @ival_sec);
if (res<>0) then
begin
LOG_Writeln(LOG_ERROR,'LNX_WDOG[STO]: '+Num2Str(fpGetErrno,0));
res:=-1;
end
else
begin
retival_msec:=(ival_sec*1000) div 5;
res:=ival_sec;
end;
{$R+}
end else LOG_Writeln(LOG_ERROR,'WDOG no support WDIOF_SETTIMEOUT');
end;
WDOG_BSTAT: begin // Check if last boot is caused by watchdog
{$R-}
res:=fpIOCTL(Hndl, WDIOC_GETBOOTSTATUS, @LastBootStat);
if (res<>0) then
begin
LOG_Writeln(LOG_ERROR,'LNX_WDOG[BSTAT]: '+Num2Str(fpGetErrno,0));
res:=-1;
end
else
begin
res:=LastBootStat;
if (LastBootStat<>0) then
LOG_WRITELN(LOG_WARNING,'LNX_WDOG: Last boot was caused by: Watchdog');
end;
{$R+}
end;
WDOG_GSup: begin // WDIOC_GETSUPPORT
(* options:0x00008180
wdctl:
Device: /dev/watchdog
Identity: Broadcom BCM2835 Watchdog timer [version 0]
Timeout: 15 seconds
Pre-timeout: 0 seconds
Timeleft: 14 seconds
FLAG DESCRIPTION STATUS BOOT-STATUS
KEEPALIVEPING Keep alive ping reply 1 0
MAGICCLOSE Supports magic close char 0 0
SETTIMEOUT Set timeout (in seconds) 0 0 *)
{$R-}
res:=fpIOCTL(Hndl, WDIOC_GETSUPPORT, @info);
if (res<>0) then
begin
LOG_Writeln(LOG_ERROR,'LNX_WDOG[GSup]: '+Num2Str(fpGetErrno,0));
res:=-1;
end
else
begin
with info do
begin
sh:=''; res:=0;
while (res<=31) do
begin
if (identity[res]<>$00)
then sh:=sh+char(identity[res]) else res:=31;
inc(res);
end;
SAY(LOG_INFO,'LNX_WDOG[GSup]: '+sh+' [version '+Num2Str(firmware_version,0)+'] opts:0x'+HexStr(options,8));
res:=options;
end; // with
end;
{$R+}
end;
WDOG_Pause: begin // pause
SAY(LOG_INFO,'LNX_WDOG: pause');
RetrigAsync:=true;
res:=0;
end;
WDOG_Resume:begin // resume
SAY(LOG_INFO,'LNX_WDOG: resume');
RetrigAsync:=false;
res:=0;
end;
end; // case
end;
end; // with
LNX_WDOG:=res;
end;
function LNX_WDOG(wdog_action:t_rpimaintflags):longint; begin LNX_WDOG:=LNX_WDOG(wdog_action,0); end;
function LNX_ShellESC(s:string):string;
// $.*[\]^
var sh:string;
begin
sh:=s;
sh:=StringReplace(sh,'\','\\',[rfReplaceAll]);
sh:=StringReplace(sh,'$','\$',[rfReplaceAll]);
sh:=StringReplace(sh,'*','\*',[rfReplaceAll]);
sh:=StringReplace(sh,'[','\[',[rfReplaceAll]);
sh:=StringReplace(sh,']','\]',[rfReplaceAll]);
sh:=StringReplace(sh,'^','\^',[rfReplaceAll]);
sh:=StringReplace(sh,'.','\.',[rfReplaceAll]);
sh:=StringReplace(sh,',','\,',[rfReplaceAll]);
sh:=StringReplace(sh,'"','\"',[rfReplaceAll]);
sh:=StringReplace(sh,'(','\(',[rfReplaceAll]);
sh:=StringReplace(sh,')','\)',[rfReplaceAll]);
LNX_ShellESC:=sh;
end;
function LNX_ParLinEXIST(filnam,parstr:string):boolean;
// parstr IN: 'autostart=1'
// filnam IN: '/etc/hostapd/hostapd.conf'
var bool:boolean; n:longint; sh:string;
begin
bool:=false;
if (filnam<>'') and (parstr<>'') then
begin
if (call_external_prog(LOG_NONE,'grep -c -F "'+parstr+'" "'+filnam+'"',sh)=0) then
if Str2Num(Trimme(sh,3),n) then bool:=(n>0); // n linecount of parstr in filnam
end;
LNX_ParLinEXIST:=bool;
end;
function LNX_ParSET(filnam,parnam,parval:string):integer;
// filnam IN: '/etc/hostapd/hostapd.conf'
// parnam IN: 'autostart'
// parval IN: '1'
// OUT OK: >=0
var res:integer;
begin
if (filnam<>'') and (parnam<>'') then
begin
res:=call_external_prog(LOG_NONE,
'sed -i -r "s/'+parnam+'[ ]*=.*/'+parnam+'='+parval+'/g" "'+filnam+'"');
end else res:=-1;
LNX_ParSET:=res;
end;
function LNX_ParGET(filnam,parnam:string; var parval:string):integer;
// filnam IN: '/etc/hostapd/hostapd.conf'
// parnam IN: 'autostart'
// parval OUT: '1'
var res:integer;
begin
if (filnam<>'') and (parnam<>'') then
begin
// res:=call_external_prog(LOG_NONE,'grep -sF "'+parnam+'=" '+filnam+' | sed "s/'+parnam+'=//g"',parval);
res:=call_external_prog(LOG_NONE,'grep -sF "'+parnam+'=" '+filnam,parval);
if (res=0) then
begin
if (parval<>'') then
begin
parval:=StringReplace(parval,parnam+'=','',[]);
end else res:=-3; // no para line
end else res:=-2; // file not exist
end else res:=-1; // file or para not given
LNX_ParGET:=res;
end;
procedure LNX_sudo(sudouse:boolean);
begin if sudouse then sudo:='sudo ' else sudo:=''; end;
function LNX_sudo:boolean; begin LNX_sudo:=(Trimme(sudo,3)<>''); end;
procedure LNX_ADD2Crontab(cmd:string);
var sh:string;
begin
if (cmd<>'') then
begin
sh:=sudo+'(crontab -l; echo "'+LNX_ShellESC(cmd)+'";) | crontab -';
call_external_prog(LOG_NONE,sh,sh);
end;
end;
function LNX_SetDateTimeUTC(utc:TDateTime):boolean;
var cmd:string;
begin
cmd:='timedatectl set-time '''+FormatDateTime('yyyy-mm-dd',utc)+' '+FormatDateTime('hh:nn:ss',utc)+'''';
LNX_SetDateTimeUTC:=(call_external_prog(LOG_NONE,cmd)=0);
end;
function LNX_GetTZList(ts:TStringList; fmt:byte):integer;
function fmtentry(f1,f2:string):string; begin fmtentry:=f1+'*'+f2; end;
var res:integer;
begin
if (fmt=0) then
begin // short list
res:=0; ts.clear;
ts.add(fmtentry('Etc/GMT+12','(GMT-12:00) International Date Line West'));
ts.add(fmtentry('Pacific/Midway','(GMT-11:00) Midway Island, Samoa'));
ts.add(fmtentry('Pacific/Honolulu','(GMT-10:00) Hawaii'));
ts.add(fmtentry('US/Alaska','(GMT-09:00) Alaska'));
ts.add(fmtentry('America/Los_Angeles','(GMT-08:00) Pacific Time (US & Canada)'));
ts.add(fmtentry('America/Tijuana','(GMT-08:00) Tijuana, Baja California'));
ts.add(fmtentry('US/Arizona','(GMT-07:00) Arizona'));
ts.add(fmtentry('America/Chihuahua','(GMT-07:00) Chihuahua, La Paz, Mazatlan'));
ts.add(fmtentry('US/Mountain','(GMT-07:00) Mountain Time (US & Canada)'));
ts.add(fmtentry('America/Managua','(GMT-06:00) Central America'));
ts.add(fmtentry('US/Central','(GMT-06:00) Central Time (US & Canada)'));
ts.add(fmtentry('America/Mexico_City','(GMT-06:00) Guadalajara, Mexico City, Monterrey'));
ts.add(fmtentry('Canada/Saskatchewan','(GMT-06:00) Saskatchewan'));
ts.add(fmtentry('America/Bogota','(GMT-05:00) Bogota, Lima, Quito, Rio Branco'));
ts.add(fmtentry('US/Eastern','(GMT-05:00) Eastern Time (US & Canada)'));
ts.add(fmtentry('US/East-Indiana','(GMT-05:00) Indiana (East)'));
ts.add(fmtentry('Canada/Atlantic','(GMT-04:00) Atlantic Time (Canada)'));
ts.add(fmtentry('America/Caracas','(GMT-04:00) Caracas, La Paz'));
ts.add(fmtentry('America/Manaus','(GMT-04:00) Manaus'));
ts.add(fmtentry('America/Santiago','(GMT-04:00) Santiago'));
ts.add(fmtentry('Canada/Newfoundland','(GMT-03:30) Newfoundland'));
ts.add(fmtentry('America/Sao_Paulo','(GMT-03:00) Brasilia'));
ts.add(fmtentry('America/Argentina/Buenos_Aires','(GMT-03:00) Buenos Aires, Georgetown'));
ts.add(fmtentry('America/Godthab','(GMT-03:00) Greenland'));
ts.add(fmtentry('America/Montevideo','(GMT-03:00) Montevideo'));
ts.add(fmtentry('America/Noronha','(GMT-02:00) Mid-Atlantic'));
ts.add(fmtentry('Atlantic/Cape_Verde','(GMT-01:00) Cape Verde Is.'));
ts.add(fmtentry('Atlantic/Azores','(GMT-01:00) Azores'));
ts.add(fmtentry('Africa/Casablanca','(GMT+00:00) Casablanca, Monrovia, Reykjavik'));
ts.add(fmtentry('Etc/UTC','(GMT+00:00) Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London'));
// ts.add(fmtentry('Etc/Greenwich','(GMT+00:00) Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London'));
ts.add(fmtentry('Europe/Berlin','(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna'));
ts.add(fmtentry('Europe/Belgrade','(GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague'));
ts.add(fmtentry('Europe/Brussels','(GMT+01:00) Brussels, Copenhagen, Madrid, Paris'));
ts.add(fmtentry('Europe/Sarajevo','(GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb'));
ts.add(fmtentry('Africa/Lagos','(GMT+01:00) West Central Africa'));
ts.add(fmtentry('Asia/Amman','(GMT+02:00) Amman'));
ts.add(fmtentry('Europe/Athens','(GMT+02:00) Athens, Bucharest, Istanbul'));
ts.add(fmtentry('Asia/Beirut','(GMT+02:00) Beirut'));
ts.add(fmtentry('Africa/Cairo','(GMT+02:00) Cairo'));
ts.add(fmtentry('Africa/Harare','(GMT+02:00) Harare, Pretoria'));
ts.add(fmtentry('Europe/Helsinki','(GMT+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius'));
ts.add(fmtentry('Asia/Jerusalem','(GMT+02:00) Jerusalem'));
ts.add(fmtentry('Europe/Minsk','(GMT+02:00) Minsk'));
ts.add(fmtentry('Africa/Windhoek','(GMT+02:00) Windhoek'));
ts.add(fmtentry('Asia/Kuwait','(GMT+03:00) Kuwait, Riyadh, Baghdad'));
ts.add(fmtentry('Europe/Moscow','(GMT+03:00) Moscow, St. Petersburg, Volgograd'));
ts.add(fmtentry('Africa/Nairobi','(GMT+03:00) Nairobi'));
ts.add(fmtentry('Asia/Tbilisi','(GMT+03:00) Tbilisi'));
ts.add(fmtentry('Asia/Tehran','(GMT+03:30) Tehran'));
ts.add(fmtentry('Asia/Muscat','(GMT+04:00) Abu Dhabi, Muscat'));
ts.add(fmtentry('Asia/Baku','(GMT+04:00) Baku'));
ts.add(fmtentry('Asia/Yerevan','(GMT+04:00) Yerevan'));
ts.add(fmtentry('Asia/Kabul','(GMT+04:30) Kabul'));
ts.add(fmtentry('Asia/Yekaterinburg','(GMT+05:00) Yekaterinburg'));
ts.add(fmtentry('Asia/Karachi','(GMT+05:00) Islamabad, Karachi, Tashkent'));
ts.add(fmtentry('Asia/Calcutta','(GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi'));
ts.add(fmtentry('Asia/Calcutta','(GMT+05:30) Sri Jayawardenapura'));
ts.add(fmtentry('Asia/Katmandu','(GMT+05:45) Kathmandu'));
ts.add(fmtentry('Asia/Almaty','(GMT+06:00) Almaty, Novosibirsk'));
ts.add(fmtentry('Asia/Dhaka','(GMT+06:00) Astana, Dhaka'));
ts.add(fmtentry('Asia/Rangoon','(GMT+06:30) Yangon (Rangoon)'));
ts.add(fmtentry('Asia/Bangkok','(GMT+07:00) Bangkok, Hanoi, Jakarta'));
ts.add(fmtentry('Asia/Krasnoyarsk','(GMT+07:00) Krasnoyarsk'));
ts.add(fmtentry('Asia/Hong_Kong','(GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi'));
ts.add(fmtentry('Asia/Kuala_Lumpur','(GMT+08:00) Kuala Lumpur, Singapore'));
ts.add(fmtentry('Asia/Irkutsk','(GMT+08:00) Irkutsk, Ulaan Bataar'));
ts.add(fmtentry('Australia/Perth','(GMT+08:00) Perth'));
ts.add(fmtentry('Asia/Taipei','(GMT+08:00) Taipei'));
ts.add(fmtentry('Asia/Tokyo','(GMT+09:00) Osaka, Sapporo, Tokyo'));
ts.add(fmtentry('Asia/Seoul','(GMT+09:00) Seoul'));
ts.add(fmtentry('Asia/Yakutsk','(GMT+09:00) Yakutsk'));
ts.add(fmtentry('Australia/Adelaide','(GMT+09:30) Adelaide'));
ts.add(fmtentry('Australia/Darwin','(GMT+09:30) Darwin'));
ts.add(fmtentry('Australia/Brisbane','(GMT+10:00) Brisbane'));
ts.add(fmtentry('Australia/Canberra','(GMT+10:00) Canberra, Melbourne, Sydney'));
ts.add(fmtentry('Australia/Hobart','(GMT+10:00) Hobart'));
ts.add(fmtentry('Pacific/Guam','(GMT+10:00) Guam, Port Moresby'));
ts.add(fmtentry('Asia/Vladivostok','(GMT+10:00) Vladivostok'));
ts.add(fmtentry('Asia/Magadan','(GMT+11:00) Magadan, Solomon Is., New Caledonia'));
ts.add(fmtentry('Pacific/Auckland','(GMT+12:00) Auckland, Wellington'));
ts.add(fmtentry('Pacific/Fiji','(GMT+12:00) Fiji, Kamchatka, Marshall Is.'));
ts.add(fmtentry('Pacific/Tongatapu','(GMT+13:00) Nuku''alofa'));
end
else
begin // 700 entries, to long for weppage (loadingtime)
res:=call_external_prog(LOG_NONE,'timedatectl list-timezones',ts);
ts.insert(0,'Etc/UTC');
end;
LNX_GetTZList:=res;
end;
function LNX_GetISOquery(ts:TStringList; opts:string):integer;
// requires isoquery command (apt-get install isoquery)
var res:integer;
begin
res:=call_external_prog(LOG_NONE,'isoquery '+opts,ts);
LNX_GetISOquery:=res;
end;
function LNX_GetBase64String(str:string):string;
// IN: fantasticjwt
// OUT: ZmFudGFzdGljand0
var cmd,sh:string;
begin
cmd:='echo -n '+str+' | base64 | tr ''+/'' ''-_'' | tr -d ''=''';
call_external_prog(LOG_NONE,cmd,sh);
LNX_GetBase64String:=sh;
end;
function LNX_GetRandomAccessToken(typ:longint):string;
// openssl rand -base64 12
// openssl rand -hex 12
const cmd1='openssl rand -base64 12'; cmd2='date | md5sum';
var res:integer; token:string;
begin
res:=call_external_prog (LOG_INFO,cmd1,token);
token:=GetAlphaNumChar(token);
if (res<>0) or (token='') then
begin
res:=call_external_prog (LOG_INFO,cmd2,token);
token:=GetAlphaNumChar(token);
if (res<>0) or (token='') then
token:=FormatDateTime('YYYYMMDDhhmmss',now); // last chance
end;
LNX_GetRandomAccessToken:=token;
end;
function LNX_chmod(filename:string; mode:TMode):cint;
var res:cint;
begin
if FileExists(filename) then
begin
res:=0;
{$IFDEF WINDOWS}
res:=call_external_prog(LOG_NONE,'chmod '+HexStr(mode,3)+' '+filename);
{$ELSE}
res:=fpChmod(filename,mode);
{$ENDIF}
end else res:=-1;
LNX_chmod:=res;
end;
function LNX_chowngrp(filename:string; owner,group:string):integer;
var res:integer; cmd:string;
begin
res:=0;
res:=-1;
if FileExists(filename) then
begin
cmd:='';
if (owner<>'') then cmd:=cmd+'chown '+owner+' '+filename;
if (owner<>'') and (group<>'') then cmd:=cmd+' ; ';
if (group<>'') then cmd:=cmd+'chgrp '+group+' '+filename;
res:=call_external_prog(LOG_NONE,cmd);
end;
LNX_chowngrp:=res;
end;
function LNX_chowngrpmod(filename:string; owner,group:string; mode:TMode):integer;
var res:integer;
begin
res:=LNX_chowngrp(filename,owner,group);
if (res=0) then res:=LNX_chmod(filename,mode);
LNX_chowngrpmod:=res;
end;
procedure LNX_GetUsrPwdString(StrList:TStringList; pwdfile,usrlst:string; carveflds:longint);
// pwdfile: /etc/shadow
// usrlst: admin:|pi:
var n:longint;
begin
if (Trimme(usrlst,3)<>'') and (pwdfile<>'') then
call_external_prog(LOG_NONE,'grep -E "'+usrlst+'" "'+PrepFilePath(pwdfile)+'"',StrList);
if (carveflds>0) then
begin
for n:= 1 to StrList.count do
StrList[n-1]:=Select_LeftItems(StrList[n-1],':','',carveflds);
end;
end;
function LNX_UpdPwdFile(pwdfile,usr,pwd:string):integer;
// maintain usr:pwd files (e.g. for lighthttpd webserver)
var res:integer; _idx:longint; _tl:TStringList;
begin
res:=-1;
if (pwdfile<>'') and (usr<>'') then
begin
_tl:=TStringList.create;
if FileExists(pwdfile) then
begin
_idx:=SearchStringInListIdx(_tl,usr+':',1,0);
if (_idx>=0) then _tl.delete(_idx);
end;
_tl.add(usr+':'+pwd);
if StringList2TextFile(pwdfile,_tl) then res:=0;
_tl.free;
end;
LNX_UpdPwdFile:=res;
end;
function LNX_ChkUsrPwdValid(usr,pwd,pwddefault:string):integer;
// IN: usr,password // access to /etc/shadow
// OUT -2:mkpasswd mkpasswd not installed or returned an error -1:not valid 0:valid 1:pwd=pwddefault
// mkpasswd is part of paket whois -> apt-get install whois
const ma=10;
var res:integer; i,j:longint; dt:TDateTime; tlh:TStringList;
sh,salt,algo,cmd,cmd0:string; arr:array[1..ma] of string;
begin
res:=-1;
{$IFDEF UNIX}
if (usr<>'') and (pwd<>'') then
begin
tlh:=TStringList.create;
res:=call_external_prog(LOG_ERROR,sudo+'cat '+LNX_ShadowFile+' | grep '+usr+':',tlh);
if (res=0) and (tlh.count>0) then
begin
sh:=tlh[0]; for i:=1 to ma do arr[i]:=Select_Item(sh,':','',i);
if (arr[2]<>'!') and (arr[2]<>'*') then
begin
algo:=Select_Item (arr[2],'$','',2); sh:=algo;
salt:=Select_Item (arr[2],'$','',3);
algo:='DES';
if sh='1' then algo:='md5';
if sh='2a' then algo:='Blowfish';
if sh='2y' then algo:='Blowfish';
if sh='5' then algo:='sha-256';
if sh='6' then algo:='sha-512';
if sh=algo then res:=-1;
end else res:=-1;
if arr[1]<>usr then res:=-1;
if Str2Num(arr[8],j) then
begin // test account deactivation
dt:=now; dt:=IncDay(EncodeDate(1970,1,1),j);
if dt<=now then res:=-1;
end;
// writeln('salt:',salt); writeln('algo:',algo); for i:=1 to 5 do writeln(i,' ',arr[i]);
if (res=0) then
begin
tlh.clear;
// if (pwd='') then pwd:='-s \<\<\< /dev/null';
cmd0:=sudo+'mkpasswd -m '+algo+' -S '+salt+' ';
cmd:= cmd0+pwd;
if (pwddefault<>'') then cmd:=cmd+' ; '+cmd0+pwddefault;
cmd:=cmd+' 2>&1';
res:=call_external_prog(LOG_ERROR,cmd,tlh);
// SAY(LOG_INFO,'LNX_ChkUsrPwdValid:'+Num2Str(res,0)+' '+Num2Str(tlh.count,0)+' '+cmd); SAY_TL(LOG_INFO,tlh);
if (res=0) then
begin
if (tlh.count>0) then
begin
if (tlh[0]=arr[2]) then
begin
if ((tlh.count>=2) and (tlh[0]=tlh[1])) then res:=1; // default pwd used
end else res:=-1; // different pwd
(* SAY(LOG_INFO,'LNX_ChkUsrPwdValid[infos '+Num2Str(res,2)+'/'+Num2Str(tlh.count,0)+']: usr: '+usr+' pwd: '+pwd+' pwddflt: '+pwddefault);
SAY(LOG_INFO,'LNX_ChkUsrPwdValid[shadowDB]: '+arr[2]);
SAY(LOG_INFO,'LNX_ChkUsrPwdValid[PWDgiven]: '+tlh[0]);
SAY_TL(LOG_INFO,tlh); *)
end else begin LOG_Writeln(LOG_ERROR,'LNX_ChkUsrPwdValid[4]: '+Num2Str(res,0)+' no output '+cmd); res:=-2; end;
end else begin LOG_Writeln(LOG_ERROR,'LNX_ChkUsrPwdValid[3]: '+Num2Str(res,0)+' mkpasswd erroneous call'); res:=-2; end;
end else begin LOG_Writeln(LOG_ERROR,'LNX_ChkUsrPwdValid[2]: '+Num2Str(res,0)+' unknown algo'); res:=-1; end;
end else begin LOG_Writeln(LOG_ERROR,'LNX_ChkUsrPwdValid[1]: '+Num2Str(res,0)+' no access to '+LNX_ShadowFile); res:=-1; end;
tlh.free;
end else begin LOG_Writeln(LOG_ERROR,'LNX_ChkUsrPwdValid[0]: '+Num2Str(res,0)+' empty usr/pwd '); res:=-1; end;
{$ENDIF}
LNX_ChkUsrPwdValid:=res;
end;
function LNX_ChgUsrPwd(usr,usrreq,pwd,pwd2,pwddflt,pwdold:string; PWD_OLDsameNEW:boolean; var msg:string):integer;
var res:integer; tlh:TStringList; cmd:string;
begin
res:=-1; tlh:=TStringList.create;
if (usr=usrreq) then
begin
if (pwd=pwd2) then
begin
if (pwd<>pwddflt) then
begin
{$IFDEF UNIX}
if (pwdold='') then res:=0 else res:=LNX_ChkUsrPwdValid(usr,pwdold,'');
if (res=0) then
begin
if (LNX_ChkUsrPwdValid(usr,pwd,pwddflt)<0) or (PWD_OLDsameNEW) then
begin
// cmd:='echo '''+usr+':'+pwd+''' | '+sudo+'chpasswd'; // does not work
cmd:=sudo+'echo -e "'+pwd+'\n'+pwd+'\n" | passwd '+usr;
res:=call_external_prog(LOG_NONE,cmd,tlh);
if (res<>0) then
begin
LOG_Writeln(LOG_ERROR,'LNX_ChgUsrPwd: can not set pwd for usr: '+usr+' '+Num2Str(res,0));
LOG_ShowStringList(LOG_ERROR,tlh);
res:=-6;
end else LNX_UsrAuthModDateTime:=GetFileAge(LNX_ShadowFile); // upd modification date of shadow file
end else res:=-5; // newpwd=oldpwd
end else res:=-7; // wrong old pwd
{$ELSE}
res:=-8; // not for windows
{$ENDIF}
// SAY(LOG_INFO,'LNX_ChgUsrPwd: '+Num2Str(res,0)+' set new pwd for usr:'+usr+' pwd:'+pwd);
end else res:=-4;
end else res:=-3;
end else res:=-2;
case res of
-8: msg:='no unix system';
-7: msg:='wrong password';
-6: msg:='can not set pwd';
-5: msg:='same pwd not allowed';
-4: msg:='default pwd not allowed';
-3: msg:='passwords do not match';
-2: msg:='wrong usr';
0: msg:='password changed';
else msg:='unknown error';
end; // case
if res=0 then SAY( LOG_NOTICE,'LNX_ChgUsrPwd: '+msg)
else LOG_Writeln(LOG_ERROR, 'LNX_ChgUsrPwd: '+msg);
tlh.free;
LNX_ChgUsrPwd:=res;
end;
function LNX_ChgUsrPwd(usr,pwd:string; var msg:string):integer;
begin LNX_ChgUsrPwd:=LNX_ChgUsrPwd(usr,usr,pwd,pwd,pwd+'x','',true,msg); end;
function LNX_RemoveOldFiles(path2files:string; days:longint):integer;
// e.g. LNX_RemoveOldFiles('/path/to/files*',5);
// delete files older than 5 days
// find /path/to/files* -mtime +5 -exec rm {} \;
var res:integer; cmd,sh:string;
begin
if DirectoryExists(Get_Dir(path2files)) then
begin
cmd:=sudo+'find '+path2files+' -mtime '+Num2Str(days,0)+' -exec rm {} \;';
// SAY(LOG_INFO,'LNX_RemoveOldFiles['+Num2Str(days,0)+'days]: '+cmd);
res:=call_external_prog(LOG_NONE,cmd,sh); res:=0;
end else res:=-1;
LNX_RemoveOldFiles:=res;
end;
function LNX_CertFormatTyp(certtyp:Cert_Type_t):string;
var sh:string;
begin
sh:=StringReplace(GetEnumName(TypeInfo(Cert_Type_t),Ord(certtyp)),'CT_','',[rfReplaceAll,rfIgnoreCase]);
LNX_CertFormatTyp:=sh;
end;
function LNX_CertIDget(filnam:string; certtyp:Cert_Type_t; idouttyp:Cert_Type_t; var id:string):boolean;
// LNX_CertSerialGET('mycert.pem',sha1,id)
var ok:boolean; cmd,cmd2,typs2,typs:string;
begin
if FileExists(filnam) then
begin// openssl x509 -in mycert.pem -noout -serial
typs2:=lowercase(LNX_CertFormatTyp(idouttyp)); cmd2:='';
cmd:='openssl '+lowercase(LNX_CertFormatTyp(certtyp))+' -in "'+filnam+'" -noout';
case idouttyp of
CT_md5: typs:='MD5 Fingerprint';
CT_sha1: typs:='SHA1 Fingerprint';
CT_sha256: typs:='SHA256 Fingerprint';
CT_serial: typs:='serial';
CT_modulus: typs:='Modulus';
CT_modmd5: begin
typs2:=lowercase(LNX_CertFormatTyp(CT_modulus));
cmd2:=' | sed "s/Modulus=//g" | sed "s/://g" | openssl md5';
typs:='(stdin)';
end;
end; // case
cmd:=cmd+' -'+typs2+cmd2+' | sed "s/'+typs+'=//g" | sed "s/://g"';
//writeln(cmd);
call_external_prog(LOG_NONE,cmd,id);
id:=GetHexChar(id);
ok:=(id<>'');
end else ok:=false;
LNX_CertIDget:=ok;
end;
procedure LNX_CertIDtest;
var ok:boolean; hashpub,hashpriv:string;
begin
SAY(LOG_INFO,'Check validity of .pem and .key file with md5 hash of both moduli');
ok:=( LNX_CertIDget(cert0_key_c, CT_rsa, CT_modmd5, hashpriv) and
LNX_CertIDget(cert0_crtORpem_c, CT_x509, CT_modmd5, hashpub) );
if ok then
begin
ok:=(hashpriv=hashpub);
SAY(LOG_INFO,'LNX_CetIDtest ok:'+Bool2Str(ok));
SAY(LOG_INFO,hashpriv+' ('+cert0_key_c+')');
SAY(LOG_INFO,hashpub+ ' ('+cert0_crtORpem_c+')');
end else LOG_Writeln(LOG_ERROR,'LNX_CetIDtest: files not accessable '+cert0_key_c+' or '+cert0_crtORpem_c);
end;
procedure LNX_CertInit(var certstruct:cert_t);
begin
with certstruct do
begin
ok:=false; filnam:=''; id:='';
end; // with
end;
procedure LNX_CertShow(lvl:T_ErrorLevel; var certstruct:cert_t);
begin
with certstruct do
begin
if (certtyp=CT_Path)
then SAY(lvl,Get_FixedStringLen(desc+'Path:',15,false)+filnam)
else SAY(lvl,Get_FixedStringLen(desc+':',15,false)+ filnam+
' id:'+id+' typ:'+LNX_CertFormatTyp(certtyp));
end; // with
end;
procedure LNX_CertPackShow(lvl:T_ErrorLevel; var certpack:cert_pack_t);
begin
with certpack do
begin
SAY(lvl,Get_FixedStringLen('CertInfo['+Num2Str(idx,0)+']:',15,false)+desc+' ok:'+Bool2YN(ok)+' pwdset:'+(Bool2YN(pwd<>'')+' packtyp:'+LNX_CertFormatTyp(packtyp)));
LNX_CertShow(lvl,cert[CertPrivKey]);
LNX_CertShow(lvl,cert[CertPublic]);
LNX_CertShow(lvl,cert[CertCombined]);
LNX_CertShow(lvl,cert[CertCA]);
end; // with
end;
function LNX_CertDir(var certstruct:cert_t; certfil:string; certtype:Cert_Type_t):boolean;
begin
LNX_CertInit(certstruct);
with certstruct do
begin
filnam:=certfil; certtyp:=certtype; ok:=true; id:='';
LNX_CertDir:=ok;
end; // with
end;
function LNX_CertReg(var certstruct:cert_t; certfil:string; certtype:Cert_Type_t):boolean;
begin
LNX_CertInit(certstruct);
with certstruct do
begin
filnam:=certfil; certtyp:=certtype;
ok:=LNX_CertIDget(filnam,certtyp,CT_modmd5,id);
if not ok then id:='';
LNX_CertReg:=ok;
end; // with
//LNX_CertShow(LOG_INFO,certstruct);
end;
procedure LNX_CertInitPack(var certpack:cert_pack_t; num:longint);
var n:longint;
begin
for n:=CertPublic to CertCombined do
begin
LNX_CertInit( certpack.cert[n]);
case n of
CertPublic: certpack.cert[n].desc:='PublicCert';
CertPrivKey: certpack.cert[n].desc:='PrivateKey';
CertCA: certpack.cert[n].desc:='CertAuth';
CertCombined: certpack.cert[n].desc:='CertCombined';
else LOG_Writeln(LOG_ERROR, 'LNX_CertInitPack: invalid idx '+Num2Str(n,0));
end; // case
end;
certpack.desc:=''; certpack.pwd:=''; certpack.ok:=false; certpack.idx:=num;
end;
function LNX_CertStartPack(var certpack:cert_pack_t; descr,pubcertfil,privkeyfil,cacertfil,combinedfil,passwd:string; certpacktyp:Cert_Type_t):boolean;
// https://gist.github.com/BlueT/ee521743fa0da703af68f37ac0f63a90
begin
with certpack do
begin
LNX_CertInitPack(certpack,idx);
desc:=descr; pwd:=passwd; packtyp:=certpacktyp;
// create a combined .pem file for e.g. lighthttp
if (combinedfil<>'') and (not FileExists(combinedfil)) and
FileExists(privkeyfil) and (GetFileSize(privkeyfil)>0) and
FileExists(pubcertfil) and (GetFileSize(pubcertfil)>0) then
call_external_prog(LOG_NONE,'cat '+privkeyfil+' '+pubcertfil+' > '+combinedfil+
' ; chmod 640 '+combinedfil);
LNX_CertReg( cert[CertPublic], pubcertfil, CT_x509);
LNX_CertReg( cert[CertPrivKey], privkeyfil, CT_rsa);
LNX_CertReg( cert[CertCombined], combinedfil,CT_Combined);
if (cacertfil<>'') then
begin
if not IsDir(cacertfil) then
begin
if FileExists(cacertfil) and (GetFileSize(cacertfil)>0)
then LNX_CertReg( cert[CertCA], cacertfil, CT_x509)
else cert[CertCA]:= cert[CertPublic];
end else LNX_CertDir( cert[CertCA], cacertfil, CT_Path);
end else cert[CertCA]:= cert[CertPublic];
ok:=(cert[CertPublic].ok and cert[CertPrivKey].ok and cert[CertCA].ok);
// ok:=(cert[CertPublic].id=cert[CertPrivKey].id);
LNX_CertStartPack:=ok;
end; // with
end;
// https://linuxconfig.org/easy-way-to-encrypt-and-decrypt-large-files-using-openssl-and-linux
function LNX_DecryptFile(filprivkey,filnam,ext:string; flags:s_rpimaintflags):integer;
// e.g. LNX_DecryptFile('/etc/ssl/private/ssl-cert-snakeoil.key','supportfile_123.tgz','ssl',[]);
// openssl smime -decrypt -binary -in supportfile_123.tgz.ssl -out supportfile_123.tgz -inform DEM -inkey /etc/ssl/private/ssl-cert-snakeoil.key
var res:integer; cmd,sh:string;
begin
res:=-1;
if FileExists(filnam+'.'+ext) and FileExists(filprivkey) then
begin
cmd:=''; if (ext='') then ext:=LNX_CertFormatTyp(CT_ssl);
cmd:=cmd+ 'openssl smime -decrypt -binary'+
' -in '+ filnam+'.'+ext+' '+
' -out '+ filnam+' '+
' -inform DEM -inkey '+ filprivkey;
res:=call_external_prog(LOG_NONE,cmd,sh); res:=0;
if (res=0) then res:=GetFileSize(filnam) else res:=-1;
if (res>0) then res:=0; // to set LOG notice
end else LOG_Writeln(LOG_ERROR,'LNX_DecryptFile: files not exist '+filnam+'.'+ext+' '+filprivkey);
LNX_DecryptFile:=res;
end;
function LNX_EncryptFile(filpubkey,filnam,ext:string; flags:s_rpimaintflags):integer;
// e.g. LNX_EncryptFile('/etc/ssl/certs/ssl-cert-snakeoil.pem','supportfile_123.tgz','ssl',[]);
// openssl smime -encrypt -binary -aes-256-cbc -in supportfile_123.tgz -out supportfile_123.tgz.ssl -outform DER /etc/ssl/certs/ssl-cert-snakeoil.pem
var res:integer; cmd,sh:string;
begin
res:=-1;
if FileExists(filnam) and FileExists(filpubkey) then
begin
cmd:=''; if (ext='') then ext:=LNX_CertFormatTyp(CT_ssl);
cmd:=cmd+ 'openssl smime -encrypt -binary -aes-256-cbc'+
' -in '+ filnam+
' -out '+ filnam+'.'+ext+
' -outform DER '+ filpubkey;
res:=call_external_prog(LOG_NONE,cmd,sh); res:=0;
// SAY(LOG_INFO,'LNX_EncryptFile:'+cmd+':'+Num2Str(res,0)+':'+sh+':');
if (res=0) then res:=GetFileSize(filnam+ext) else res:=-1;
if (res>0) then res:=0; // to set LOG notice
end else LOG_Writeln(LOG_ERROR,'LNX_EncryptFile: files not exist '+filnam+' '+filpubkey);
LNX_EncryptFile:=res;
end;
function LNX_LinkFile(filnam,linknam:string):integer;
var res:integer; cmd:string;
begin
if (linknam<>'') then
begin
if (filnam<>'') and (filnam<>linknam) and (FileExists(filnam))
then cmd:='ln -s "'+ filnam+'" "'+linknam+'"'
else cmd:='rm "'+ linknam+'"';
res:=call_external_prog(LOG_NONE,cmd);
//SAY(LOG_WARNING,Num2Str(res,0)+':'+cmd+':'+filnam+':'+linknam);
end else res:=-1;
LNX_LinkFile:=res;
end;
function LNX_GetNewestFile(filnampat:string):string;
// filnampat: /var/lib/BASIS/pump/bck/bckcfg_0000000012345_*
var _tl:TStringList; sh:string;
begin
_tl:=TStringList.create;
call_external_prog(LOG_NONE,'ls -1r '+filnampat,_tl);
if (_tl.count>0) then
begin
if ((_tl[0]<>'') and FileExists(_tl[0])) then sh:=_tl[0];
end else sh:='';
_tl.free;
//writeln('LNX_GetNewestFile:',filnampat,':',sh);
LNX_GetNewestFile:=sh;
end;
function LNX_tarRST(target,fillst:string; flags:s_rpimaintflags):longint;
// restore tar --keep-newer-files -xzvf bck_<snr>.tgz -C /
var res:longint; cmd:string;
begin
res:=-1;
fillst:=PrepFilePath(Trimme(fillst,3));
target:=PrepFilePath(Trimme(target,3));
if (fillst<>'') and (FileExists(fillst)) then
begin
cmd:='';
if (target='') then target:=PrepFilePath(c_tmpdir+'/tmp');
if (UpdNewerOnly IN flags) then cmd:=cmd+'--keep-newer-files ';
cmd:=cmd+'-x';
if (Pos('.GZ', Upper(fillst))>0) or
(Pos('.TGZ',Upper(fillst))>0) then cmd:=cmd+'z';
if (UpdVerbose IN flags) then cmd:=cmd+'v';
cmd:='tar '+cmd+'f '+fillst+' -C '+target;
if not (UpdVerbose IN flags) then cmd:=cmd+' >/dev/null 2>&1';
// SAY(LOG_INFO,'LNX_tarRST: '+cmd);
res:=call_external_prog(LOG_NONE,cmd); res:=0;
if (res>=0) then res:=0 else res:=-1;
// if (res=0) then res:=GetFileSize(fillst) else res:=-1;
end;
LNX_tarRST:=res;
end;
function LNX_tarSAV(target,fillst:string; flags:s_rpimaintflags):longint;
var res:longint; cmd,tflg,ddir,sh:string;
begin
if not (UpdNoWDOGprevent IN flags) then LNX_WDOG(WDOG_Pause); // pause
ddir:=PrepFilePath(AppDataDir_c+'/'+ApplicationName);
if (fillst='') then fillst:=ddir+'/'+ApplicationName+'.ini';
if (target='') then target:=PrepFilePath(c_tmpdir+'/bck_'+RPI_snr);
//adjust extension will exclusively determined by flags
target:=StringReplace(target,'.gz','', [rfReplaceAll,rfIgnoreCase]);
target:=StringReplace(target,'.tgz','', [rfReplaceAll,rfIgnoreCase]);
target:=StringReplace(target,'.tar','', [rfReplaceAll,rfIgnoreCase]);
if (not (UpdNoCreateDir IN flags)) or DirectoryExists(Get_Dir(target)) then
begin
cmd:='';
if not (UpdNoCreateDir IN flags) then cmd:=cmd+'mkdir -p '+Get_Dir(target)+' ; ';
tflg:='';
if not (UpdNoZIP IN flags) then
begin
tflg:=tflg+'z'; target:=target+'.tgz';
end else target:=target+'.tar';
if (UpdFollowLink IN flags) then tflg:=tflg+'h';
if (UpdVerbose IN flags) then tflg:=tflg+'v';
if (UpdVerify IN flags) and
(UpdNoZIP IN flags) then tflg:=tflg+'W'; // tar: Cannot verify compressed archives
cmd:=cmd+ 'tar -c'+tflg+'f '+ target
+' --exclude='+ target
+' '+ fillst;
if not (UpdVerbose IN flags) then cmd:=cmd+' >/dev/null 2>&1';
// SAY(LOG_INFO,'LNX_tar: '+cmd);
res:=call_external_prog(LOG_NONE,cmd,sh); res:=0;
if (res=0) then res:=GetFileSize(target) else res:=-1;
if (UseENCrypt IN flags) then
begin
if (res>0) then
begin
with CertPack[0] do
begin
if (CertPack[0].ok) then
begin
res:=LNX_EncryptFile(
cert[CertPublic].filnam,
target,
cert[CertPublic].id+'.'+LNX_CertFormatTyp(packtyp),
flags);
end else LOG_Writeln(LOG_ERROR,'LNX_tar: CertPack[0] not initialized');
end; // with
end else LOG_Writeln(LOG_ERROR,'LNX_tar: can no encrypt file, filesize not ok');
end;
if (res>0) then res:=0; // to set LOG notice
end else LOG_Writeln(LOG_ERROR,'LNX_tar: target dir does not exist '+Get_Dir(target));
if not (UpdNoWDOGprevent IN flags) then LNX_WDOG(WDOG_Resume); // resume
LNX_tarSAV:=res;
end;
procedure MinMaxAdj(var value:real; valmin,valmax:real);
begin
if not IsNaN(value) then
begin
if not IsNaN(valmin) then if (value<valmin) then value:=valmin;
if not IsNaN(valmax) then if (value>valmax) then value:=valmax;
end
else
begin
if not IsNaN(valmin) then value:=valmin;
end;
end;
function Limits(var value:longint; minvalue,maxvalue:longint):longint;
begin if value>maxvalue then value:=maxvalue; if value<minvalue then value:=minvalue; Limits:=value; end;
function Limits(var value:longword; minvalue,maxvalue:longword):longword;
begin if value>maxvalue then value:=maxvalue; if value<minvalue then value:=minvalue; Limits:=value; end;
function Limits(var value:real; minvalue,maxvalue:real):real;
begin if value>maxvalue then value:=maxvalue; if value<minvalue then value:=minvalue; Limits:=value; end;
function InLimits(value,minvalue,maxvalue:real):boolean;
begin InLimits:=((value>=minvalue) and (value<=maxvalue)); end;
procedure MinMax(value:longint; var minvalue,maxvalue:longint);
begin if value>maxvalue then maxvalue:=value; if value<minvalue then minvalue:=value; end;
procedure MinMax(value:longword; var minvalue,maxvalue:longword);
begin if value>maxvalue then maxvalue:=value; if value<minvalue then minvalue:=value; end;
procedure MinMax(value:real; var minvalue,maxvalue:real);
begin
if not IsNan(value) then
begin
if IsNaN(maxvalue)
then maxvalue:=value
else if value>maxvalue then maxvalue:=value;
if IsNaN(minvalue)
then minvalue:=value
else if value<minvalue then minvalue:=value;
end;
end;
procedure STAT_Reset(var stats:STAT_struct_t);
var i:longint;
begin
with stats do
begin
tim_avg:=0;
ist_val:=0;
old_val:=0;
val_avg:=0;
old_avg:=0;
for i:=1 to Length(val_arr) do val_arr[i-1]:=0;
for i:=1 to Length(tim_arr) do tim_arr[i-1]:=0;
dif_val_pms:=0;
idx:=0;
end; // with
end;
procedure STAT_Init(var stats:STAT_struct_t; arrsize:word);
begin
with stats do
begin
SetLength(val_arr,arrsize);
SetLength(tim_arr,arrsize);
end; // with
STAT_Reset(stats);
end;
procedure STAT_Calc(var stats:STAT_struct_t; newval:real; tim_us:real);
begin
with STATS do
begin
old_val:= ist_val;
old_avg:= val_avg;
ist_val:= newval;
val_arr[idx]:= newval;
tim_arr[idx]:= tim_us;
val_avg:= Mean(val_arr);
tim_avg:= Mean(tim_arr);
if (IsNaN(val_avg) or IsNaN(tim_avg)) then
begin
val_avg:=newval;
tim_avg:=tim_us;
end;
if (tim_avg<>0)
then dif_val_pms:=(val_avg-old_avg)/(tim_avg/1000)
else dif_val_pms:=0;
inc(idx);
if ((idx>=Length(val_arr)) or (idx>=Length(tim_arr))) then idx:=0;
end; // with
end;
function Str2Bool(s:string; var ein:boolean):boolean;
var ok:boolean;
begin
ok:=false;
if Pos(Upper(s),yes_c) >0 then begin ok:=true; ein:=true; end;
if Pos(Upper(s),nein_c)>0 then begin ok:=true; ein:=false; end;
Str2Bool:=ok;
end;
function Str2Bool(s:string):boolean; begin Str2Bool:=(Pos(Upper(s),yes_c)>0); end;
function Bool2Num(b:boolean) : byte; begin if b then Bool2Num:=1 else Bool2Num:=0; end;
function Bool2Dig(b:boolean) : string; begin if b then Bool2Dig:='1' else Bool2Dig:='0'; end;
function Bool2LVL(b:boolean) : string; begin if b then Bool2LVL:='H' else Bool2LVL:='L'; end;
function Bool2Str(b:boolean) : string; begin if b then Bool2Str:='TRUE' else Bool2Str:='FALSE'; end;
function Bool2Swc(b:boolean) : string; begin if b then Bool2Swc:='ON' else Bool2Swc:='OFF'; end;
function Bool2OC (b:boolean) : string; begin if b then Bool2OC:='OPEN' else Bool2OC:='CLOSE'; end;
function Bool2YN (b:boolean) : string; begin if b then Bool2YN:='YES' else Bool2YN:='NO'; end;
function Bool2YNS(b:boolean) : string; begin if b then Bool2YNS:='Y' else Bool2YNS:='N'; end;
function Bool2EA (b:boolean) : string; begin if b then Bool2EA:='ENABLED' else Bool2EA:='DISABLED';end;
function Bool2eas(b:boolean) : string; begin if b then Bool2eas:='enable' else Bool2eas:='disable';end;
function Bool2UpDown(b:boolean):string; begin if b then Bool2UpDown:='up' else Bool2UpDown:='down';end;
function Num2Str(num:int64;lgt:byte):string; var s:string; begin str(num:lgt,s); Num2Str:=s; end;
function Num2Str(num:longint;lgt:byte):string; var s:string; begin str(num:lgt,s); Num2Str:=s; end;
function Num2Str(num:longword;lgt:byte):string; var s:string; begin str(num:lgt,s); Num2Str:=s; end;
function Num2Str(num:qword;lgt:byte):string; var s:string; begin str(num:lgt,s); Num2Str:=s; end;
function Num2Str(num:real;lgt,nk:byte):string; var s:string; begin str(num:lgt:nk,s);Num2Str:=s; end;
function Num2Str(num:int64):string; begin Num2Str:=Num2Str(num,0); end;
function Num2Str(num:longint):string; begin Num2Str:=Num2Str(num,0); end;
function Num2Str(num:longword):string; begin Num2Str:=Num2Str(num,0); end;
function Num2Str(num:real;nk:byte):string; begin Num2Str:=Num2Str(num,0,nk); end;
function Num2Bool(num:int64):boolean; begin Num2Bool:=(num>=0); end;
function TimeSpec2Str(pts:Ptimespec):string; begin TimeSpec2Str:=Num2Str(pts^.tv_sec,0)+'.'+Num2Str(round((pts^.tv_nsec/1000000000)),0) end;
function TimeSpec2Num(pts:Ptimespec):real; begin TimeSpec2Num:= pts^.tv_sec + ( pts^.tv_nsec/1000000000); end;
function Str2Num(s:string; var num:byte):boolean; var code:integer; begin val(StringReplace(s,'$','0x',[rfReplaceAll,rfIgnoreCase]),num,code); Str2Num:=(code=0); end;
function Str2Num(s:string; var num:smallint):boolean; var code:integer; begin val(StringReplace(s,'$','0x',[rfReplaceAll,rfIgnoreCase]),num,code); Str2Num:=(code=0); end;
function Str2Num(s:string; var num:int64):boolean; var code:integer; begin val(StringReplace(s,'$','0x',[rfReplaceAll,rfIgnoreCase]),num,code); Str2Num:=(code=0); end;
function Str2Num(s:string; var num:qword):boolean; var code:integer; begin val(StringReplace(s,'$','0x',[rfReplaceAll,rfIgnoreCase]),num,code); Str2Num:=(code=0); end;
function Str2Num(s:string; var num:longint):boolean; var code:integer; begin val(StringReplace(s,'$','0x',[rfReplaceAll,rfIgnoreCase]),num,code); Str2Num:=(code=0); end;
function Str2Num(s:string; var num:longword):boolean; var code:integer; begin val(StringReplace(s,'$','0x',[rfReplaceAll,rfIgnoreCase]),num,code); Str2Num:=(code=0); end;
function Str2Num(s:string; var num:real):boolean;
var code:integer; i64:int64; sh:string;
begin
sh:=StringReplace(s,'$','0x',[rfReplaceAll,rfIgnoreCase]);
val(sh,num,code);
if (code<>0) and Str2Num(sh,i64) then begin num:=i64; code:=0; end;
Str2Num:=(code=0);
end;
function Str2Num(s:string; var num:extended):boolean;
var code:integer; r:real;
begin
val(s,num,code);
if (code<>0) and Str2Num(s,r) then begin num:=r; code:=0; end;
Str2Num:=(code=0);
end;
function Str2NumFMT(s:string; nk:byte):string;
var r:real; i:integer; sh:string;
begin
if not Str2Num(s,r) then
begin
sh:='';
if (nk>0) then
begin for i:=1 to nk do sh:=sh+'_'; sh:='.'+sh; end;
sh:='_'+sh;
end else sh:=Num2Str(r,nk);
Str2NumFMT:=sh;
end;
function Num2StrFMT(num:real;nk:byte):string;
var sh:string;
begin
if not IsNaN(num)
then sh:=Num2Str(num,0,nk)
else sh:=Str2NumFMT(' ',nk);
Num2StrFMT:=sh;
end;
function Str2CP437(s:string):string;
var sh:string;
begin
sh:=StringReplace(s ,'Ä',#$8e,[rfReplaceAll]);
sh:=StringReplace(sh,'Ö',#$99,[rfReplaceAll]);
sh:=StringReplace(sh,'Ü',#$9a,[rfReplaceAll]);
sh:=StringReplace(sh,'ä',#$84,[rfReplaceAll]);
sh:=StringReplace(sh,'ö',#$94,[rfReplaceAll]);
sh:=StringReplace(sh,'ü',#$81,[rfReplaceAll]);
sh:=StringReplace(sh,'ß',#$e1,[rfReplaceAll]);
sh:=StringReplace(sh,'§',#$15,[rfReplaceAll]);
Str2CP437:=sh;
end;
function StringReverse(s:string):string;
var i : integer; sh:string;
begin
sh:='';
for i := Length(s) downto 1 do sh:=sh+s[i];
StringReverse:=sh;
end;
function Str2TimeSpec(s:string; var ts:timespec):boolean;
var c1,c2:integer;
begin
val(Select_Item(s,'.','',1),ts.tv_sec, c1);
val(Select_Item(s,'.','',2),ts.tv_nsec,c2);
LOG_Writeln(LOG_ERROR,'Str2TimeSpec: '+s);
Str2TimeSpec:=((c1=0) and (c2=0));
end;
//function Hex (nr:qword;lgt:byte) : string; begin Hex:=Format('%0:-*.*x',[lgt,lgt,nr]); end;
//{$warnings off} function Hex (ptr:pointer;lgt:byte): string; begin Hex:=Hex(qword(ptr),lgt); end; {$warnings on}
function HexStr(s:string):string;overload; var sh:string; i:longint; begin sh:=''; for i := 1 to Length(s) do sh:=sh+HexStr(ord(s[i]),2); HexStr:=sh; end;
function LeadingZero(w:word):string; begin LeadingZero:=Format('%0:-*.*d',[2,2,w]); end;
//function Get_FixedStringLen(s:string;cnt:word;leading:boolean):string; var fmt:string; begin fmt:='%0:'; if not leading then fmt:=fmt+'-'; fmt:=fmt+'*.*s'; Get_FixedStringLen:=Format(fmt,[cnt,cnt,s]); end;
function Get_FixedStringLen(s:string;cnt:word;leading:boolean):string; var fmt:string; begin if leading then fmt:='%' else fmt:='%-'; fmt:=fmt+Num2Str(cnt,0)+'s'; Get_FixedStringLen:=Format(fmt,[s]); end;
//function Upper(const s : string) : String; var sh : String; i:word; begin sh:=''; for i:= 1 to Length(s) do sh:=sh+Upcase(s[i]); Upper:=sh; end;
//function Lower(const s : string) : String; var sh : String; i:word; begin sh:=''; for i:= 1 to Length(s) do sh:=sh+LowerCase(s[i]);Lower:=sh; end;
function Upper(const s:string):string; begin Upper:=UpCase(s); end;
function Lower(const s:string):string; begin Lower:=LowerCase(s); end;
function CharPrintable(c:char):string; begin if ord(c)<$20 then CharPrintable:=#$5e+char(ord(c) xor $40) else CharPrintable:=c; end;
function StringPrintable(s:string):string; var sh : string; i : longint; begin sh:=''; for i:=1 to Length(s) do sh:=sh+CharPrintable(s[i]); StringPrintable:=sh; end;
procedure ShowStringList(StrList:TStringList); var n:longint; begin for n:= 1 to StrList.Count do writeln(StrList[n-1]); end;
function AdjZahlDE(r:real;lgt,nk:byte):string; var s:string; begin s:=StringReplace(Num2Str(r,lgt,nk),'.',',',[]); AdjZahlDE:=s; end;
function AdjZahl(s:string):string;
var hs:string; n,pkt,com:integer; DEformat:boolean; r:real;
begin
DEformat:=false; pkt:=POS('.',s); com:=POS(',',s); hs:='';
if (pkt<com) and (com<>0) then DEformat:=true;
//writeln(DEformat,' ',pkt,' ',com);
for n:=1 to Length(s) do
begin
case s[n] of
'.': if (DEformat) then hs:=hs+'' else hs:=hs+'.';
',': if (DEformat) then hs:=hs+'.' else hs:=hs+'';
else hs:=hs+s[n];
end;
end; // only . as decimalpoint
hs:=StringReplace(hs,'.--','.00',[]); hs:=StringReplace(hs,'.-', '.00',[]);
if not Str2Num(hs,r) then hs:='';
AdjZahl:=hs;
end;
procedure IPInfo_Init(intface:string; var IPInfo:IP_Info_t);
begin
with IPInfo do
begin
iface:=intface; timestamp:=now;
ip4addr:=noip_c; ip6addr:=noip_c; gwaddr:=noip_c; nsaddr:=noip_c;
domain:=''; hwaddr:=''; link:='';
ssid:=''; signal:=''; DNSname:='';
stat:=false; wireless:=false; chan:=''; freq:='';
end; // with
end;
procedure IPInfoShow(lvl:T_ErrorLevel; var IPInfo:IP_Info_t);
begin
with IPInfo do
begin
SAY(lvl,alias+' Link: '+link);
SAY(lvl,'iface: '+iface);
SAY(lvl,'wireless: '+Bool2Str(wireless));
SAY(lvl,'stat: '+Bool2Str(stat));
SAY(lvl,'inet: '+ip4addr);
// SAY(lvl,'inetextern: '+ip4ext);
SAY(lvl,'inet6: '+ip6addr);
SAY(lvl,'ether: '+hwaddr);
SAY(lvl,'default via: '+gwaddr);
SAY(lvl,'nameserver: '+nsaddr);
SAY(lvl,'domain: '+domain);
SAY(lvl,'DNSname: '+DNSname);
if wireless then
begin
SAY(lvl,'SSID: '+ssid);
SAY(lvl,'Channel: '+chan);
SAY(lvl,'Freq: '+freq);
SAY(lvl,'Signal: '+signal);
end;
end;
end;
procedure IPInfosShow(lvl:T_ErrorLevel; var IPInfos:IP_Infos_t);
var i:longint;
begin
with IPInfos do
begin
LOG_Writeln(lvl,'################################################');
LOG_Writeln(lvl,'idx:'+Num2Str(idx,0)+' init:'+Bool2Str(init)+' samesubnet:'+Bool2Str(samesubnet));
LOG_Writeln(lvl,'devlst: '+devlst+' ip4ext:'+ip4ext+' ip6ext:'+ip6ext);
LOG_Writeln(lvl,'hostapd_extdev: '+hostapd_extdev+' hostname:'+hostname);
for i:= 0 to IP_infomax_c do
begin
LOG_Writeln(lvl,Num2Str(i,2)+' #############################################');
IPInfoShow(lvl,IP_Info[i]);
end;
LOG_Writeln(lvl,'################################################');
end; // with
end;
function GetHostNameOS:string;
var computer:string; {$IFDEF Win32}c:array[0..127] of Char; sz:dword;{$ENDIF}
begin
computer:='';
{$IFDEF Win32} sz:=SizeOf(c); GetComputerName(c,sz); computer:=c;
{$ELSE} computer:=unix.GetHostName; {$ENDIF}
GetHostNameOS:=computer;
end;
procedure IPInfo_GetOS(var IPInfo:IP_Info_t);
// idee: echo inet `ip a show wlan0 | grep -Po 'inet \K[\d.]+'`
// ip -f inet addr show wlan0 | grep -Po 'inet \K[\d.]+'
// IN: eth0 or wlan0
// eth: enx???????? wlan: wlx????????
procedure xx(srch,istr:string; nr:longint; var ostr:string);
begin
if (Pos(srch,istr)>0) and ((ostr='') or (ostr=noip_c)) then
ostr:=Trimme(Select_Item(istr,' ','',nr),3);
end;
var res:integer; n:longint; _tl:TStringList; sh:string;
begin
//LOG_Writeln(LOG_WARNING,' IPInfo_GetOS+');
_tl:=TStringList.create; // echo wlan0 Link: `cat /sys/class/net/wlan0/carrier`
with IPInfo do
begin
IPInfo_Init(iface,IPInfo);
sh:=sudo+'ip a show '+iface+' ; '+
'echo '+iface+' Link: `cat /sys/class/net/'+iface+'/carrier` ; ';
wireless:=((Pos('wlan',lower(iface))>0) or (Pos('wlx',lower(iface))>0));
if wireless then
begin
sh:=sh+ 'echo SSID: `iwgetid -r` ; '+
'echo Chan: `iwgetid -c | awk -F '':'' ''{print $2}''` ; '+
'echo Freq: `iwgetid -f | awk -F '':'' ''{print $2}''` ; '+
'echo Signal: `cat /proc/net/wireless | tail -1 | awk ''{print $3}''` ; ';
// wlan0: 0000 60. -50. -256 0 0 0 32 0 0
end;
sh:=sh+ 'ip route show ; '+
'cat /etc/resolv.conf';
res:=call_external_prog(LOG_NONE,sh,_tl);
// SAY_TL(LOG_WARNING,_tl);
if (res=0) then
begin
for n:= 1 to _tl.count do
begin
sh:=Trimme(_tl[n-1],4); // remove all unnecessary spaces
// writeln(sh);
xx('inet ', sh,2,ip4addr);
xx('inet6 ', sh,2,ip6addr);
xx('ether ', sh,2,hwaddr);
xx('default via ', sh,3,gwaddr);
xx('nameserver ', sh,2,nsaddr);
xx('domain ', sh,2,domain);
xx(iface+' Link:', sh,3,link);
xx('SSID: ', sh,2,ssid);
if wireless then
begin
if (Pos('Freq: ',sh)>0) then
begin
freq:= Trimme(Select_RightItems(sh,' ','',2),3);
end;
if (Pos('Chan: ',sh)>0) then
begin
chan:= Trimme(Select_RightItems(sh,' ','',2),3);
end;
if (Pos('Signal: ',sh)>0) then
begin
signal:= Trimme(Select_RightItems(sh,' ','',2),3);
// writeln('sig:',signal,':',sh);
if (signal='') or (signal='tus')
then signal:= none_c
else signal:= StringReplace(signal,'.','%',[]);
end;
end;
end; // for
end else LOG_Writeln(LOG_ERROR,'GetIPInfos: '+Num2Str(res,0));
if (link='1') then link:='UP' else link:='DOWN';
_tl.free;
stat:=((link='UP') and (ip4addr<>noip_c));
if stat then DNSname:=LNX_ResolveIP2name(Select_Item(ip4addr,'/','',1));
//GetIPInfos[wlan0]: MAC:b8:27:eb:d9:a6:01 IP4:10.8.81.135/24 IP6:noIPAdr GW:10.8.81.1 DNS:10.8.81.1 Domain:muo.basis.biz ext:188.192.178.135
sh:='GetIPInfos['+alias+'/'+iface+']: MAC:'+hwaddr+' IP4:'+ip4addr+' IP6:'+ip6addr+' GW:'+gwaddr+' DNS:'+nsaddr+' Domain:'+domain+' dnsname:'+DNSname+' wireless:'+Bool2Str(wireless);
// if stat then SAY(LOG_INFO,sh) else SAY(LOG_WARNING,sh);
// IPInfoShow(LOG_INFO,IPInfo);
end; // with
//LOG_Writeln(LOG_WARNING,' IPInfo_GetOS-');
end;
procedure IPInfo_GetOS(var IPInfos:IP_Infos_t);
var ok:boolean; n,i1,i2,anz,_idx:longint; devnam:string;
begin
ok:=false;
//LOG_Writeln(LOG_WARNING,'IPInfo_GetOS+');
with IPInfos do
begin
if not init then // access HW
begin
hostname:=GetHostNameOS;
if (call_external_prog(LOG_NONE,'ls -1r /sys/class/net/',devlst)<>0) then devlst:='';
// https://unix.stackexchange.com/questions/22615/how-can-i-get-my-external-ip-address-in-a-shell-script
if (call_external_prog(LOG_NONE,'dig @resolver1.opendns.com ANY myip.opendns.com -4 +short',ip4ext)<>0) then ip4ext:=noip_c;
if (call_external_prog(LOG_NONE,'dig @resolver1.opendns.com ANY myip.opendns.com -6 +short',ip6ext)<>0) then ip6ext:=noip_c;
devlst:=StringReplace(devlst,LineEnding,',',[rfReplaceAll]); // wlan0,lo,eth0,ap0
// writeln('devlist:',devlst,':');
samesubnet:=false;
anz:=Anz_Item(devlst,',','');
for n:= 1 to anz do
begin
devnam:=Trimme(Select_Item(devlst,',','',n),3); // e.g. wlan0 or wlx?????
_idx:=-1;
if (Pos('wlan0',devnam)>0) or (Pos('wlx',devnam)>0) then _idx:=IP_infoWLAN0idx_c;
if (Pos('wlan1',devnam)>0) then _idx:=IP_infoWLAN1idx_c;
if (Pos('eth', devnam)>0) or (Pos('enx',devnam)>0) then _idx:=IP_infoETH0idx_c;
if (devnam=ifuap_c) then _idx:=IP_infoUAP0idx_c; // IP_infomax_c
if (_idx>=0) and (_idx<=IP_infomax_c) then
begin
IP_Info[_idx].iface:=devnam;
IPInfo_GetOS(IP_Info[_idx]);
// if (IP_Info[_idx].iface=ifuap_c) then IP_Info[_idx].ssid:='';
if not ok then
begin
if IP_Info[_idx].stat then
begin
ok:=true;
idx:=_idx;
end;
end;
// IPInfoShow(LOG_INFO,IP_Info[_idx]);
end; // else LOG_Writeln(LOG_ERROR,'IPInfo_GetOS: wrong idx '+Num2Str(_idx,0));
end; // for
init:=true;
end; // if
hostapd_extdev:=ifeth_c;
if (not IP_Info[IP_infoETH0idx_c].stat) then
begin
if IP_Info[IP_infoWLAN0idx_c].stat then hostapd_extdev:=IP_Info[IP_infoWLAN0idx_c].iface; // wlan0
if IP_Info[IP_infoWLAN1idx_c].stat then hostapd_extdev:=IP_Info[IP_infoWLAN1idx_c].iface; // wlan1
end else hostapd_extdev:=IP_Info[IP_infoETH0idx_c].iface; // eth0
(* for n:= 1 to 2 do
begin
if not samesubnet then
samesubnet:=
((IP_Info[n-1].ip4addr<>noip_c) and (IP_Info[n].ip4addr<>noip_c) and
IP4AddrsInSameSubnet(IP_Info[n-1].ip4addr,IP_Info[n].ip4addr));
end; *)
i1:=IPInfo_GetIdx(ifeth_c); i2:=IPInfo_GetIdx(ifwlan_c);
samesubnet:=
((IP_Info[i1].ip4addr<>noip_c) and (IP_Info[i2].ip4addr<>noip_c) and
IP4AddrsInSameSubnet(IP_Info[i1].ip4addr,IP_Info[i2].ip4addr));
//writeln('idx:',idx,' samesubnet:',samesubnet);
end; // with
//LOG_Writeln(LOG_WARNING,'IPInfo_GetOS-');
end;
procedure IPInfo_GetOS;
begin
IP_Infos.init:=false; // force OS read
IPInfo_GetOS(IP_Infos);
end;
function IPInfo_GetInterfaceName(intidx:longint):string;
var sh:string;
begin
case intidx of
IP_infoWLAN0idx_c: sh:=ifwlan_c;
IP_infoWLAN1idx_c: sh:=ifwlan1_c;
IP_infoETH0idx_c: sh:=ifeth_c;
IP_infoUAP0idx_c: sh:=ifuap_c;
else sh:='';
end; // case
IPInfo_GetInterfaceName:=sh;
end;
function IPInfo_GetIdx(intface:string):longint;
var n,_idx:longint;
begin
_idx:=IP_infoWLAN0idx_c;
for n:= 0 to IP_infomax_c do
begin
with IP_Infos.IP_Info[n] do
begin
if (iface=intface) or (alias=intface) then _idx:=n;
end; // with
end;
IPInfo_GetIdx:=_idx;
end;
function IP_iface(aliasname:string):string;
// IN: wlan0 OUT: wlan0 or wlxxxxxxx
begin
//IPInfo_GetOS;
IP_iface:=IP_Infos.IP_Info[IPInfo_GetIdx(aliasname)].iface;
end;
function IP4_Addr(iface:string):string;
begin
//IPInfo_GetOS;
IP4_Addr:=IP_Infos.IP_Info[IPInfo_GetIdx(iface)].ip4addr;
end;
function IP6_Addr(iface:string):string;
begin
//IPInfo_GetOS;
IP6_Addr:=IP_Infos.IP_Info[IPInfo_GetIdx(iface)].ip6addr;
end;
function GetIPAdr(iface:string; var ipaddr:string; ip4:boolean):boolean;
begin
if ip4 then ipaddr:=IP4_Addr(iface) else ipaddr:=IP6_Addr(iface);
GetIPAdr:=((ipaddr<>'') and (ipaddr<>noip_c));
end;
function GetMACAdr(iface:string; var hwaddr:string):boolean;
begin
hwaddr:=IP_Infos.IP_Info[IPInfo_GetIdx(iface)].hwaddr;
GetMACAdr:=(hwaddr<>'');
end;
function GetDomainName(iface:string):string;
begin
//IPInfo_GetOS;
GetDomainName:=IP_Infos.IP_Info[IPInfo_GetIdx(iface)].domain;
end;
function GetDomainName:string;
var sh:string;
begin
//IPInfo_GetOS;
with IP_Infos do
begin
if (idx>=0) and (idx<=IP_infomax_c) then sh:=IP_Infos.IP_Info[idx].domain else sh:='';
end; // with
GetDomainName:=sh;
end;
function GetMainDomainName:string;
var n:longint; domain:string;
begin
domain:=GetDomainName; // def.ghi.com
n:=Anz_Item(domain,'.','');
if (n>=2) then domain:=Select_RightItems(domain,'.','',(n-1)); // ghi.com
GetMainDomainName:=domain;
end;
function GetHostName:string;
begin
//IPInfo_GetOS;
GetHostName:=IP_Infos.hostname;
end;
function IS_Online(ip4:boolean):boolean;
var _ok:boolean;
begin
_ok:=false;
with IP_Infos do
begin
if ip4 then _ok:=(ip4ext<>noip_c)
else _ok:=(ip6ext<>noip_c);
end; // with
IS_Online:=_ok;
end;
function GetWLANSignal(iface:string):longint; // -1,0-100
// -1: not avail // 0-100%
var _idx,_sig:longint;
begin
_sig:=-1;
_idx:=IPInfo_GetIdx(iface);
with IP_Infos.IP_Info[_idx] do
begin
if wireless then
if not Str2Num(GetNumChar(signal),_sig) then _sig:=-1;
end; // with
(*SAY(LOG_WARNING,'GetWLANSignal: '+iface+' '+Num2Str(_idx,0)+' '+Num2Str(_sig,0));
IPInfoShow(LOG_WARNING,IP_Infos.IP_Info[_idx]); *)
GetWLANSignal:=_sig;
end;
function RPI_cxt_GPIOopts(flgs:s_port_flags):string;
// https://www.raspberrypi.org/documentation/configuration/config-txt/gpio.md
var cmd:string;
begin
cmd:='';
if (OUTPUT IN flgs) then flgs:=flgs-[INPUT];
if (PullUP IN flgs) then flgs:=flgs-[PullDOWN];
if ((flgs*[INPUT,OUTPUT,ALT5,ALT4,ALT3,ALT2,ALT1,ALT0])=[])
then flgs:=flgs+[INPUT];
if (INPUT IN flgs) then cmd:=cmd+'ip,';
if (OUTPUT IN flgs) then cmd:=cmd+'op,';
if (ALT5 IN flgs) then cmd:=cmd+'a5,';
if (ALT4 IN flgs) then cmd:=cmd+'a4,';
if (ALT3 IN flgs) then cmd:=cmd+'a3,';
if (ALT2 IN flgs) then cmd:=cmd+'a2,';
if (ALT1 IN flgs) then cmd:=cmd+'a1,';
if (ALT0 IN flgs) then cmd:=cmd+'a0,';
if (NOpull IN flgs) then begin flgs:=flgs-[PullUP,PullDOWN]; cmd:=cmd+'np,'; end;
if (PullUP IN flgs) then cmd:=cmd+'pu,';
if (PullDOWN IN flgs) then cmd:=cmd+'pd,';
if (OUTPUT IN flgs) then
if (InitialHIGH IN flgs) then cmd:=cmd+'dh,' else cmd:=cmd+'dl,';
RPI_cxt_GPIOopts:=CSV_RemLastSep(cmd,',');
end;
function RPI_config(raspicmd:t_RPI_config; par1,par2:string; var resultstring:string):integer;
// https://github.com/l10n-tw/rc_gui/blob/master/src/rc_gui.c
// 0 is in general success / yes / selected, 1 is failed / no / not selected
var res:integer; sh:string;
begin
res:=0;
sh:=sudo+'raspi-config nonint ';
case raspicmd of
GET_CAN_EXPAND: sh:=sh+'get_can_expand';
EXPAND_FS: sh:=sh+'do_expand_rootfs';
GET_HOSTNAME: sh:=sh+'get_hostname';
SET_HOSTNAME: sh:=sh+'do_hostname '+par1;
GET_BOOT_CLI: sh:=sh+'get_boot_cli';
GET_AUTOLOGIN: sh:=sh+'get_autologin';
SET_BOOT_CLI: sh:=sh+'do_boot_behaviour B1';
SET_BOOT_CLIA: sh:=sh+'do_boot_behaviour B2';
SET_BOOT_GUI: sh:=sh+'do_boot_behaviour B3';
SET_BOOT_GUIA: sh:=sh+'do_boot_behaviour B4';
GET_BOOT_WAIT: sh:=sh+'get_boot_wait';
SET_BOOT_WAIT: sh:=sh+'do_boot_wait '+par1;
GET_SPLASH: sh:=sh+'get_boot_splash';
SET_SPLASH: sh:=sh+'do_boot_splash '+par1;
GET_OVERSCAN: sh:=sh+'get_overscan';
SET_OVERSCAN: sh:=sh+'do_overscan '+par1;
GET_PIXDUB: sh:=sh+'get_pixdub';
SET_PIXDUB: sh:=sh+'do_pixdub '+par1;
GET_CAMERA: sh:=sh+'get_camera';
SET_CAMERA: sh:=sh+'do_camera '+par1;
GET_SSH: sh:=sh+'get_ssh';
SET_SSH: sh:=sh+'do_ssh '+par1;
GET_VNC: sh:=sh+'get_vnc';
SET_VNC: sh:=sh+'do_vnc '+par1;
GET_SPI: sh:=sh+'get_spi';
SET_SPI: sh:=sh+'do_spi '+par1;
GET_I2C: sh:=sh+'get_i2c';
SET_I2C: sh:=sh+'do_i2c '+par1;
GET_SERIAL: sh:=sh+'get_serial';
GET_SERIALHW: sh:=sh+'get_serial_hw';
SET_SERIAL: sh:=sh+'do_serial '+par1;
GET_1WIRE: sh:=sh+'get_onewire';
SET_1WIRE: sh:=sh+'do_onewire '+par1;
GET_RGPIO: sh:=sh+'get_rgpio';
SET_RGPIO: sh:=sh+'do_rgpio '+par1;
GET_PI_TYPE: sh:=sh+'get_pi_type';
GET_OVERCLOCK: sh:=sh+'get_config_var arm_freq /boot/config.txt';
SET_OVERCLOCK: sh:=sh+'do_overclock '+par1;
GET_GPU_MEM: sh:=sh+'get_config_var gpu_mem /boot/config.txt';
GET_GPU_MEM_256:sh:=sh+'get_config_var gpu_mem_256 /boot/config.txt';
GET_GPU_MEM_512:sh:=sh+'get_config_var gpu_mem_512 /boot/config.txt';
GET_GPU_MEM_1K: sh:=sh+'get_config_var gpu_mem_1024 /boot/config.txt';
SET_GPU_MEM: sh:=sh+'do_memory_split '+par1;
GET_HDMI_GROUP: sh:=sh+'get_config_var hdmi_group /boot/config.txt';
GET_HDMI_MODE: sh:=sh+'get_config_var hdmi_mode /boot/config.txt';
SET_HDMI_GP_MOD:sh:=sh+'do_resolution '+par1+' '+par2;
GET_WIFI_CTRY: sh:=sh+'get_wifi_country';
SET_WIFI_CTRY: sh:=sh+'do_wifi_country '+par1;
WLAN_INTERFACES:sh:=sh+'list_wlan_interfaces';
else res:=1;
end; // case
if (res=0) then res:=call_external_prog(LOG_NONE,sh,resultstring);
//LOG_Writeln(LOG_WARNING,sh+': ('+Num2Str(res,0)+') '+resultstring);
RPI_config:=res;
end;
function RPI_WLANavailChan(cntry:string):string;
const _2Ghz='1|2|3|4|5|6|7|8|9|10|11|12|13|';
_5Ghz='36|40|44|48|52|56|60|64|100|104|108|112|116|120|124|128|132|136|140|';
var sh:string;
begin
sh:='';
case RPI_bType of
8,$0a,$0c: sh:=_2GHz;
$0d: sh:=_2GHz+_5GHz;
end; // case
RPI_WLANavailChan:=CSV_RemLastSep(sh,'|');
end;
function MAC_Addr(iface:string; fmt:byte):string;
var n:longint; sh:string;
begin
//IPInfo_GetOS;
sh:=GetHexChar(IP_Infos.IP_Info[IPInfo_GetIdx(iface)].hwaddr);
if (Length(sh)<12) then sh:=cpu_snr;
case fmt of
1..12: begin
n:=Length(sh);
if (n>=fmt) then sh:=copy(sh,n-fmt+1,fmt) else sh:='';
end;
end; // case
MAC_Addr:=sh;
end;
function MAC_isRPI(macsubstr:string):boolean;
begin
macsubstr:=FilterChar(upper(macsubstr),'0123456789abcdefABCDEF');
MAC_isRPI:=( (Pos('B827EB',macsubstr)=1) or (Pos('DCA632',macsubstr)=1) );
end;
function IP4_AddrExt:string;
begin
//IPInfo_GetOS;
IP4_AddrExt:=IP_Infos.ip4ext;
end;
function IP4_AddrValid(ipstr:string):boolean;
// e.g. 192.168.1.2/32
const cnt_c=4;
var ok:boolean; n,anz,li:longint; sh,sh1,sh2:string;
begin
ok:=false;
sh1:=Select_Item(ipstr,'/','',1); // 192.168.1.2
sh2:=Select_Item(ipstr,'/','',2); // 24
sh:=FilterChar(sh1,'0123456789.'); // filter all valid
if (sh=sh1) then
begin
anz:=Anz_Item(sh,'.','');
if (anz=cnt_c) then
begin
ok:=true;
for n:= 1 to cnt_c do
begin
if Str2Num(Select_Item(sh,'.','',n),li) then
begin
if ((li<0) or (li>$ff)) then ok:=false;
end else ok:=false;
end;
end;
if (ok and (sh2<>'')) then
begin // chk netmask
if Str2Num(sh2,li) then
begin
if (li<8) or (li>32) then ok:=false;
end else ok:=false;
end;
end;
IP4_AddrValid:=ok;
end;
function IP4AddrListValid(ipliststr:string):boolean;
// e.g. 192.168.1.0/24,10.8.12.34,10.8.12.56
// check for IPTables
var ok:boolean; n:longint; sh:string;
begin
ok:=true;
for n:=1 to Anz_Item(ipliststr,',','') do
begin
sh:=Select_Item(ipliststr,',','',n);
if not IP4_AddrValid(sh) then
begin
ok:=false;
LOG_Writeln(LOG_ERROR,'IP4AddrListValid: '+sh+' no valid entry in list '+ipliststr);
end;
end;
IP4AddrListValid:=ok;
end;
function IP6_AddrValid(ipstr:string):boolean;
// e.g. 2001:0db8:85a3:08d3:1319:8a2e:0370:7344/48
const cnt_c=8;
var ok:boolean; n,anz,li:longint; sh,sh1,sh2:string;
begin
ok:=false;
sh1:=Select_Item(ipstr,'/','',1); // 2001:0db8:85a3:08d3:1319:8a2e:0370:7344
sh2:=Select_Item(ipstr,'/','',2); // 48
sh:=FilterChar(sh1,'0123456789abcdefABCDEF:'); // filter all valid
if (sh=sh1) then
begin
anz:=Anz_Item(sh,'.','');
if (anz=cnt_c) then
begin
ok:=true;
for n:= 1 to cnt_c do
begin
if Str2Num(Select_Item(sh,':','',n),li) then
begin
if ((li<0) or (li>$ffff)) then ok:=false;
end else begin if (sh<>'') then ok:=false; end;
end;
end;
if (ok and (sh2<>'')) then
begin // chk netmask
if Str2Num(sh2,li) then
begin
if (li<16) or (li>128) then ok:=false;
end else ok:=false;
end;
end;
IP6_AddrValid:=ok;
end;
function IP6AddrListValid(ipliststr:string):boolean;
// e.g. 2001:0db8:85a3:08d3:1319:8a2e:0370:7344/48,2001:0db8:85a3:08d3:1319:8a2e:0370:7345
var ok:boolean; n:longint; sh:string;
begin
ok:=true;
for n:=1 to Anz_Item(ipliststr,',','') do
begin
sh:=Select_Item(ipliststr,',','',n);
if not IP6_AddrValid(sh) then
begin
ok:=false;
LOG_Writeln(LOG_ERROR,'IP6AddrListValid: '+sh+' no valid entry in list '+ipliststr);
end;
end;
IP6AddrListValid:=ok;
end;
function IPAddrListValid(ipliststr:string):boolean;
// e.g. 2001:0db8:85a3:08d3:1319:8a2e:0370:7344/48,2001:0db8:85a3:08d3:1319:8a2e:0370:7345
var ok:boolean; n:longint; sh:string;
begin
ok:=true;
for n:=1 to Anz_Item(ipliststr,',','') do
begin
sh:=Select_Item(ipliststr,',','',n);
if not (IP4_AddrValid(sh) or IP6_AddrValid(sh)) then
begin
ok:=false;
LOG_Writeln(LOG_ERROR,'IPAddrListValid: '+sh+' no valid entry in list '+ipliststr);
end;
end;
writeln('IPAddrListValid:',ipliststr,':',ok);
IPAddrListValid:=ok;
end;
function IP4AddrsInSameSubnet(ip4adr1,ip4adr2:string):boolean;
// ip4adr1: 192.168.1.172
// ip4adr2: 192.168.1.0/24
// valid: /8 /16 /24 /32
var _ok:boolean; subm:longint; ipn1,ipn2:string;
begin
_ok:=false;
if IP4_AddrValid(ip4adr1) and IP4_AddrValid(ip4adr2) then
begin
if not Str2Num(Select_Item(ip4adr2,'/','',2),subm) then
if not Str2Num(Select_Item(ip4adr1,'/','',2),subm) then subm:=24;
subm:=round(subm/8);
ipn2:=Select_Item(ip4adr2,'/','',1); // 192.168.1.0
ipn2:=Select_LeftItems(ipn2,'.','',subm); // 192.168.1
ipn1:= Select_Item(ip4adr1,'/','',1); // 192.168.1.172
ipn1:= Select_LeftItems(ipn1,'.','',subm); // 192.168.1
_ok:=(ipn1=ipn2);
end;
IP4AddrsInSameSubnet:=_ok;
end;
function ShortString(fmt,maxlgt,divdr:longint; str:string):string;
const shrtA='..'; shrtE='\u2026'; // horizontalEllipsis
var li1,li2:longint; sh:string;
begin
if (Length(str)>maxlgt) then
begin
if (divdr<2) then fmt:=1; // avoid div 0
case fmt of
0: sh:=str; // no shorting
3: begin // break string in 2 parts, break defined by 'divdr' e.g. 3
li1:=((maxlgt-Length(shrtA)) div divdr)*(divdr-1); li2:=maxlgt-li1-Length(shrtA);
sh:=copy(str,1,li1)+shrtA+copy(str,(Length(str)+1-li2),li2);
//writeln('origstr:',str); writeln('shorted:',sh);
end;
2: sh:=shrtA+copy(str,Length(str)-maxlgt+1+Length(shrtA),maxlgt); // cut left
4: sh:=copy(str,1,(maxlgt-Length(shrtA)))+shrtA; // cut right
30: begin // Ellipsis: break string in 2 parts, break defined by 'divdr' e.g. 3
li1:=((maxlgt-1) div divdr)*(divdr-1); li2:=maxlgt-li1-1;
sh:=copy(str,1,li1)+shrtE+copy(str,(Length(str)+1-li2),li2);
//writeln('origstr:',str); writeln('shorted:',sh);
end;
20: sh:=shrtE+copy(str,Length(str)-maxlgt+1+1,maxlgt); // cut left
40: sh:=copy(str,1,(maxlgt-1))+shrtE; // cut right
else sh:=ShortString(40,maxlgt,divdr,str);
end;
end else sh:=str;
ShortString:=sh;
end;
function Num2Limit(var Value:real; MinOut,MaxOut:real):boolean;
var valold:real;
begin
valold:=Value;
if Value<MinOut then Value:=MinOut
else if Value>MaxOut then Value:=MaxOut;
Num2Limit:=(Value<>valold);
end;
function FormatFileSize(const Size: Int64):string;
var fSize:real; sh,Fmt,Units:string;
begin
Fmt:='%.1f%s';
if (Size>(1 shl 20)) then begin // Mb
if (Size>(1 shl 30)) then begin // Gb
if (Size>(1 shl 40)) then begin // Tb
fSize:=Size*(1/(1 shl 40));
Units:='Tb';
end else
//if (Size>(1 shl 30)) then // Gb
begin
fSize:=Size*(1/(1 shl 30));
Units:='Gb';
end;
end else
//if (Size>(1 shl 20)) then // Mb
begin
fSize:=Size*(1/(1 shl 20));
Units:='Mb';
end;
end else
if (Size>(1 shl 10)) then begin //kb
fSize:=Size*(1/(1 shl 10));
Units:='kb';
end else begin
fSize:=Size;
Units:='b';
Fmt:='%.0f%s';
end;
FmtStr(sh,Fmt,[fSize,Units]);
FormatFileSize:=sh;
end;
function KEYpressedChar(ch:char):boolean;
var _keypr:boolean;
begin
if KeyPressed then _keypr:=(ReadKey=ch) else _keypr:=false;
KEYpressedChar:=_keypr;
end;
function ESCpressed:boolean; begin ESCpressed:=KEYpressedChar(ESC); end;
procedure AskCR(lvl:T_ErrorLevel; msg:string); begin writeln; write(msg+'<CR>'); readln; end;
procedure AskCR(msg:string); begin AskCR(LOG_INFO,msg); end;
procedure AskCR; begin AskCR(''); end;
function AskStr(msg:string; var outstr:string):boolean;
begin
write('enter '+msg+' (<string> or <CR> for exit): '); readln(outstr);
AskStr:=(outstr<>'');
end;
function AskYN(msg:string; dflt:string):boolean;
const yn_c='y/n';
var outchar,sh:string;
begin
sh:=yn_c; dflt:=Upper(dflt);
if dflt='N' then sh:='y/N'; if dflt='Y' then sh:='Y/n';
repeat
write('enter '+msg+' ('+sh+'): '); readln(outchar); outchar:=Upper(outchar);
if (outchar='') and (sh<>yn_c) then outchar:=dflt;
until ((outchar='Y') or (outchar='N'));
AskYN:=(outchar='Y');
end;
function AskNum(von,bis:longint; msg:string; var outnum:longint):boolean;
var _ok:boolean; sh:string;
begin
repeat
write('enter '+msg+' (',von:0,'-',bis:0,' or -1 for exit): '); readln(sh);
_ok:=Str2Num(sh,outnum);
_ok:=( _ok and (((outnum>=von) and (outnum<=bis)) or (outnum=-1)));
until _ok;
AskNum:=(outnum<>-1);
end;
function StrHex(Hex_strng:string):string;
const tab:array[1..6] of byte=($0a,$0b,$0c,$0d,$0e,$0f);
var s,sh:string; i:longint; b,bh:byte; pending:boolean;
begin
sh:=''; bh:=$00; s:=GetHexChar(Hex_strng); pending:=((Length(s) mod 2)<>0);
for i := 1 to Length(s) do
begin
b:=ord(s[i]);
if (b>=$30) and (b<=$39) then b:=b and $0f else b:=tab[(b and $0f)];
if (((i-1) mod 2) <> 0) or ((i=Length(s)) and pending) then
begin
bh:=bh or b; sh:=sh+char(bh); bh:=$00;
end
else bh:=b shl 4;
end;
StrHex:=sh;
end;
function Str2DateTime(tdstring,fmt:string; var dt:TDateTime):boolean;
var _ok:boolean;
begin
try
_ok:=true;
dt:=ScanDateTime(fmt,tdstring);
except
_ok:=false;
end;
Str2DateTime:=_ok;
end;
function UnicodeStr2UTF8(unicodestr:string):string;
// unicodestr: 'H\u2082O' -> H2O (subscript 2)
var anz,n:longint; lw:longword; _str,sh:string;
begin
sh:=''; _str:=unicodestr; // H\u2082O
anz:=Anz_Item(_str,'\u','');
for n:= 1 to anz do
begin
sh:=sh+Select_Item(_str,'\u','',1); // H
_str:=Select_RightItems(unicodestr,'\u','',n+1); // 2082O subscript2 O
if (Length(_str)>=4) then
begin
if not Str2Num('$'+copy(_str,1,4),lw) then lw:=0;
sh:=sh+UTF8Encode(WideChar(lw));
_str:=copy(_str,5,Length(_str)); // O
end;
end;
//writeln(sh+' '+HexStr(sh));
UnicodeStr2UTF8:=sh;
end;
function scale(valin,min1,max1,min2,max2:real):real;
var r1,r2:real;
begin
r2:=valin;
if (valin>=min1) and (valin<=max1) then
begin
r1:=max1-min1;
if r1<>0 then
begin
r2:=valin*(max2-min2)/r1;
end else LOG_Writeln(LOG_ERROR,'Scale: wrong min1/max1 value pair');
end else LOG_Writeln(LOG_ERROR,'Scale: valin not in range of min1/max1 value pair');
scale:=r2;
end;
function LeadingZeros(l:longint;digits:byte):string;
var s1,s2:string; i:byte;
begin
s1:=''; for i := 1 to digits do s1:=s1+'0'; Str(l:0,s2); s1:=s1+s2;
LeadingZeros:=copy(s1,Length(s1)-digits+1,255);
end;
function IsDir(filname:string):boolean;
begin IsDir:=((FileGetAttr(PrepFilePath(filname)) and faDirectory)<>0); end;
function SetFileAge(filname:string; mode:integer; fdat:TDateTime):integer;
// mode: 1:modification date / 2:access date / 0: both dates
var res:integer; fn,cmd,sh:string;
begin
res:=0; fn:=PrepFilePath(filname);
if FileExists(fn) then
begin
cmd:='touch';
case mode of
1: cmd:=cmd+' -m';
2: cmd:=cmd+' -a';
end; // case
cmd:=cmd+' -t '+FormatDateTime('YYYYMMDDhhmm',fdat)+' '+fn;
if not (call_external_prog(LOG_NONE,cmd,sh)=0) then res:=-1;
end else res:=-1;
if res<0 then Log_Writeln(Log_ERROR,'SetFileAge: '+cmd);
SetFileAge:=res;
end;
function GetFileAge(filname:string):TDateTime;
var fa:longint; fildat:TDateTime; fn:string;
begin
fildat:=0; fn:=PrepFilePath(filname);
if FileExists(fn) then
begin
{$I-} fa:=FileAge(fn); if fa<>-1 then fildat:=FileDateToDateTime(fa); {$I+}
end;
GetFileAge:=fildat;
end;
function GetFileSize(filname:string):int64;
var filsiz:int64; f:file; fn:string;
begin
filsiz:=-1; fn:=PrepFilePath(filname);
if FileExists(fn) then
begin
{$I-}
assign(f,fn);
reset (f,1);
filsiz:=FileSize(f);
close(f);
{$I+}
end;
GetFileSize:=filsiz;
end;
function GetFilePackSize(filelist:string):int64;
var n:longint; res,sum:int64;
begin
sum:=0;
for n:=1 to Anz_Item(filelist,',','"') do
begin
res:=GetFileSize(Select_Item(filelist,',','"',n));
if (res>0) then sum:=sum+res;
end;
GetFilePackSize:=sum;
end;
function GetFileAgeInSec(filname:string):int64;
var fa:longint; res:int64; fildat:TDateTime; fn:string;
begin
fildat:=0; res:=-1; fn:=PrepFilePath(filname);
if FileExists(fn) then
begin
{$I-}
fa:=FileAge(fn);
if fa<>-1 then
begin
fildat:=FileDateToDateTime(fa);
res:=round(SecondsBetween(now,fildat));
end;
{$I+}
end;
GetFileAgeInSec:=res;
end;
function GetRNDsec(seconds_old,varianz:longint):longint;
var v,vh:longint;
begin
v:=seconds_old;
if varianz<>0 then
begin
vh:=varianz div 2; v:=Random(varianz+1); v:=vh-v; v:=seconds_old-v; if v<0 then v:=seconds_old;
end;
GetRNDsec:=v;
end;
function FileIsRecent(filepath:string; seconds_old,varianz:longint):boolean;
var ok:boolean; tdat:TDateTime;
begin
ok:=false;
if FileExists(filepath) then
begin
tdat:=GetFileAge(filepath);
ok:=(SecondsBetween(now,tdat)<=GetRNDsec(seconds_old,varianz));
// LOG_Writeln(LOG_Warning,Bool2Str(ok)+' Delta: '+Num2Str(DeltaTime_in_min(now,tdat),0)+' min FileDate: '+GetXMLTimeStamp(tdat)+' '+Real2Str(v/60,0,2)+' min');
end;
FileIsRecent:=ok;
end;
function FileIsRecent(filepath:string; seconds_old:longint):boolean;
begin
FileIsRecent:=FileIsRecent(filepath,seconds_old,0);
end;
function adjL(s:string):string;
{.c schmeisst leading Blanks weg. }
var i,j : word; sh : string; first : boolean;
begin
first := true; j := 1;
sh := s;
for i := 1 to Length(sh) do
if (sh[i] = ' ') and (first) then j := i else first := false;
if (j>0) and (j<=Length(sh)) then if sh[j] = ' ' then INC(j);
sh := copy(sh,j,Length(sh)-j+1);
adjL := sh;
end;
function adjT(s:string):string;
{.c schmeisst trailing Blanks weg. }
var i,j : integer; sh : string; first : boolean;
begin
sh := s; first := true; j := length(sh);
for i := Length(sh) downto 1 do
if (sh[i] = ' ') and (first) then j := i else first := false;
if (j>0) and (j<=Length(sh)) then if sh[j] = ' ' then DEC(j);
sh := copy(sh,1,j);
adjT := sh;
end;
function adjM(s:string):string;
{.c schmeisst mehrfach folgende Blanks weg. }
var sh,sh2:string;
begin
sh:=s;
repeat sh2:=sh; delete(sh,Pos(' ',sh),1); until sh=sh2;
adjM:=sh;
end;
function adj(s:string):string; begin adj := adjL(adjT(s)); end;
function adjAll(s:string):string; begin adjALL := adjM(adj(s)); end;
function Trimme(s:string;modus:byte):string;
var sh:string; { modus: 1:adjL 2:adjT 3:AdjLT 4:AdjLMT 5:AdjLMTandRemoveTABs }
begin
sh := s;
case modus of
0 : ;
1 : sh := adjL(s);
2 : sh := adjT(s);
3 : sh := adj(s);
4 : sh := adjAll(s);
5 : sh := adjAll(StringReplace(s,#$09,' ',[rfReplaceAll]));
$0a: sh := adjAll(StringReplace(s,#$0a,' ',[rfReplaceAll]));
else sh := adjAll(s);
end;
Trimme := sh;
end;
function adjL0(s:string):string;
var exp:string; num:extended; nk:integer;
begin
if Str2Num(s,num) then
begin
s:=Upper(s); exp:='0';
if Pos('.',s)<>0 then begin exp:=Select_Item(s,'.','',2); exp:=Num2Str(Length(exp),0); end; // preserve accuracy
if Pos('E',s)<>0 then begin exp:=Select_Item(s,'E','',2); end;
if Str2Num(exp,nk) then s:=Num2Str(num,0,abs(nk));
end;
adjL0:=s;
end;
function FilterChar(s,filter:string):string;
{.c filtert aus string s alle char die in filter angegeben sind. }
var sh:string; i,j:integer;
begin
sh:=s;
if Length(filter) > 0 then
begin
sh:='';
for i := 1 to Length(s) do
begin
for j := 1 to Length(filter) do
begin
if s[i]=filter[j] then sh:=sh+s[i];
end;
end;
end;
FilterChar:=sh;
end;
function RemoveChar(s,filter:string):string;
// remove all char from 's' which 'filter' contains
var sh:string; i:integer;
begin
sh:=s;
if Length(filter)>0 then
begin
sh:='';
for i:=1 to Length(s) do
if (Pos(s[i],filter)=0) then sh:=sh+s[i];
end;
RemoveChar:=sh;
end;
function GetHexChar(s:string):string;
begin GetHexChar:=FilterChar(s,'0123456789ABCDEFabcdef'); end;
function GetNumChar(s:string):string;
begin GetNumChar:=FilterChar(s,'0123456789'); end;
function GetAlphaNumChar(s:string):string;
begin GetAlphaNumChar:=FilterChar(s,'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'); end;
function GetParserTokenChar(s:string):string;
begin GetParserTokenChar:=FilterChar(s,'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_'); end;
function ContainDescenderLetter(s:string):boolean; // string has char with descender (unterlaenge)
begin ContainDescenderLetter:=(FilterChar(s,'gjpqyß_,;')<>''); end;
function ReplaceChars(s,filterchars,replacechar:string):string;
{.c ersetzt aus string s alle char die in filter angegeben sind mit replacechar }
var sh:string; i:integer;
begin
sh:=s;
for i := 1 to Length(filterchars) do sh:=StringReplace(sh,filterchars[i],replacechar,[rfReplaceAll]);
ReplaceChars:=sh;
end;
function RM_LF (s:string):string; begin RM_LF:= ReplaceChars(s,#$0a,''); end;
function RM_CR (s:string):string; begin RM_CR:= ReplaceChars(s,#$0d,''); end;
function RM_CRLF(s:string):string; begin RM_CRLF:=ReplaceChars(s,#$0d+#$0a,''); end;
function SB_Null(s:string):string; begin SB_Null:=StringReplace(s,'\0',#$00,[rfReplaceAll]); end;
function SB_Bell(s:string):string; begin SB_Bell:=StringReplace(s,'\a',#$07,[rfReplaceAll]); end;
function SB_BS (s:string):string; begin SB_BS:= StringReplace(s,'\b',#$08,[rfReplaceAll]); end;
function SB_TAB (s:string):string; begin SB_TAB:= StringReplace(s,'\t',#$09,[rfReplaceAll]); end;
function SB_LF (s:string):string; begin SB_LF:= StringReplace(s,'\n',#$0a,[rfReplaceAll]); end;
function SB_CR (s:string):string; begin SB_CR:= StringReplace(s,'\r',#$0d,[rfReplaceAll]); end;
function SB_FF (s:string):string; begin SB_FF:= StringReplace(s,'\f',#$0c,[rfReplaceAll]); end;
function SB_ESC (s:string):string; begin SB_ESC:= StringReplace(s,'\e',#$1b,[rfReplaceAll]); end;
function SB_VT (s:string):string; begin SB_VT:= StringReplace(s,'\v',#$0b,[rfReplaceAll]); end;
function SB_CRLF(s:string):string; begin SB_CRLF:=SB_LF(SB_CR(s)); end;
function SB_UnESC(s:string):string; begin SB_UnESC:= SB_CRLF(SB_FF(SB_TAB(SB_BS(SB_VT(SB_Bell(SB_Null(SB_ESC(s)))))))); end;
function BS_Null(s:string):string; begin BS_Null:=StringReplace(s,#$00,'\0',[rfReplaceAll]); end;
function BS_Bell(s:string):string; begin BS_Bell:=StringReplace(s,#$07,'\a',[rfReplaceAll]); end;
function BS_BS (s:string):string; begin BS_BS:= StringReplace(s,#$08,'\b',[rfReplaceAll]); end;
function BS_TAB (s:string):string; begin BS_TAB:= StringReplace(s,#$09,'\t',[rfReplaceAll]); end;
function BS_LF (s:string):string; begin BS_LF:= StringReplace(s,#$0a,'\n',[rfReplaceAll]); end;
function BS_CR (s:string):string; begin BS_CR:= StringReplace(s,#$0d,'\r',[rfReplaceAll]); end;
function BS_FF (s:string):string; begin BS_FF:= StringReplace(s,#$0c,'\f',[rfReplaceAll]); end;
function BS_ESC (s:string):string; begin BS_ESC:= StringReplace(s,#$1b,'\e',[rfReplaceAll]); end;
function BS_VT (s:string):string; begin BS_VT:= StringReplace(s,#$0b,'\v',[rfReplaceAll]); end;
function BS_CRLF(s:string):string; begin BS_CRLF:=BS_LF(BS_CR(s)); end;
function BS_DoESC(s:string):string; begin BS_DoESC:= BS_CRLF(BS_FF(BS_TAB(BS_BS(BS_VT(BS_Bell(BS_Null(BS_ESC(s)))))))); end;
function BS_HK (s:string):string; begin BS_HK:= StringReplace(s,#$27,'\''',[rfReplaceAll]); end;
function BS_dHK (s:string):string; begin BS_dHK:= StringReplace(s,#$22,'\"',[rfReplaceAll]); end;
function BS_QM (s:string):string; begin BS_QM:= StringReplace(s,#$3f,'\?',[rfReplaceAll]); end;
function BS_Bsl (s:string):string; begin BS_Bsl:= StringReplace(s,#$5c,'\\',[rfReplaceAll]); end;
function BS_ALL (s:string):string; begin BS_ALL:= BS_HK(BS_dHK(BS_QM(BS_DoESC((s))))); end;
//function BS_ALL (s:string):string; begin BS_ALL:= BS_Bsl(BS_HK(BS_dHK(BS_QM(BS_DoESC((s)))))); end;
function CamelCase(strng:string):string;
// IN: CamelCase
// OUT: -camel-case
var i:longint; c:char; sh:string;
begin
sh:='';
for i:= 1 to Length(strng) do
begin
c:=strng[i];
if (Upper(c)=c) and (c<>' ') then sh:=sh+'-';
if c<>' ' then sh:=sh+LowerCase(c);
end;
CamelCase:=sh;
end;
function GetPrintableChars(s:string; c1,c2:char):string;
var sh:string; i:word;
begin
sh:='';
for i := 1 to Length(s) do
if ((ord(s[i])>=ord(c1)) and (ord(s[i])<=ord(c2))) then sh:=sh+s[i]; { #$<c1>..#$<c2> }
GetPrintableChars:=sh;
end;
function HashTag(modus:byte; filname,comment1,comment2:string):string;
var hash,sh,fn:string; dt:TDateTime; m:TMemoryStream; f:file of byte; oldfilemode:byte; siz:int64;
begin
hash:=''; fn:=PrepFilePath(filname);
case modus of
1 : begin // MD5 Hash constructed with FileDate;FileSize and a comment string
dt:=GetFileAge(fn);
if dt>0 then
begin
{$I-} assign(f,fn);
oldfilemode:=filemode; filemode:=0; // readonly
//writeln('HashTag1');
reset(f,1); // hier hängt darwin, wenn access privs auf datei nicht stimmen!!!
//writeln('HashTag2');
filemode:=oldfilemode;
siz:=FileSize(f);
close(f); {$I+}
sh:=FormatDateTime('yyyy-mm-dd',dt)+'T'+ // YEAR-MM-DDThh:mm:ss.zz
FormatDateTime('hh:nn:ss.zz',dt)+';'+
Num2Str(siz,0)+';'+Num2Str(modus,0)+';'+comment1+';'+comment2;
hash:=MD5Print(MD5String(sh));
end
else LOG_Writeln(LOG_Error,'HashTag: file does not exist: '+fn);
end;
2 : begin // MD5 Hash of filecontent
m:=TMemoryStream.create;
if not File2MStream(fn,m,hash) then
begin hash:=''; LOG_Writeln(LOG_Error,'HashTag: file does not exist: '+fn); end;
m.free;
end;
3: begin // MD5 Hash auf String 'comment1'
hash:=MD5Print(MD5String(comment1));
end;
else LOG_Writeln(LOG_ERROR,'HashTag: wrong modus '+Num2Str(modus,0));
end; // case
//writeln('HashTag:',hash,':',fn);
HashTag:=hash;
end;
function HashTag(var InString:string):string; begin HashTag:=HashTag(3,'',InString,''); end;
procedure FSplit(fullfilename:string; var Directory,FName,Extension:string; extwithdot:boolean);
var anz:integer; ext:string;
begin
anz:=Anz_Item(fullfilename,dir_sep_c,''); ext:='';
Directory:=Select_LeftItems (fullfilename,dir_sep_c,'',anz-1);
Fname:= Select_RightItems(fullfilename,dir_sep_c,'',anz);
Extension:=Select_Item(Fname,ext_sep_c,'',Anz_Item(Fname,ext_sep_c,''));
if (Extension<>'') then ext:=ext_sep_c+Extension;
Fname:=StringReplace(Fname,ext,'',[rfReplaceAll,rfIgnoreCase]);
if (Extension<>'') and (extwithdot) then Extension:=ext_sep_c+Extension;
//writeln(fullfilename,'|',directory,'|',fname,'|',extension,'-',dir_sep_c);
end;
function Get_ExtName(fullfilename:string; extwithdot:boolean):string;
var fext:string;
begin
fext:=ExtractFileExt(fullfilename);
if not extwithdot then
if Pos(ext_sep_c,fext)=1 then fext:=copy(fext,2,Length(fext));
Get_ExtName:=fext;
end;
function Get_FName(fullfilename:string; withext:boolean):string;
var Directory,FName,Extension,sh:string;
begin
FSplit(fullfilename,Directory,FName,Extension,true);
sh:=Fname; if withext then sh:=sh+Get_ExtName(fullfilename,true);
Get_FName:=sh;
end;
function Get_FName(fullfilename:string):string; begin Get_FName:=Get_FName(fullfilename,false); end;
function Get_FNameExt(fullfilename:string):string;
var Directory,FName,Extension : string;
begin FSplit(fullfilename,Directory,FName,Extension,true); Get_FNameExt:=Fname+Extension; end;
function Get_Dir(fullfilename:string):string;
var Directory,FName,Extension : string;
begin FSplit(fullfilename,Directory,FName,Extension,true); Get_Dir:=Directory;end;
function Get_Dirs(fullfilenamelist:string):string;
var n,anz:longint; sh:string;
begin
sh:=''; anz:=Anz_Item(fullfilenamelist,',','"');
for n:= 1 to anz do
begin
sh:=sh+Get_Dir(Select_Item(fullfilenamelist,',','"',n));
if (n<anz) then sh:=sh+',';
end;
Get_Dirs:=sh;
end;
function Get_DirList(dirname:string; filelist:TStringList):integer;
const
{$IFDEF WINDOWS} c_dircmd = 'dir'; c_dirpara = '/b /ogne';
{$ELSE} c_dircmd = 'ls'; c_dirpara = '-1'; {$ENDIF}
begin
//writeln('Get_DirList:',c_dircmd+' '+c_dirpara+' '+PrepFilePath(dirname));
Get_DirList:=call_external_prog(LOG_NONE,c_dircmd+' '+c_dirpara+' '+PrepFilePath(dirname), filelist);
end;
function GetTildePath(fullpath,homedir:string):string;
var sh:string;
begin
sh:=StringReplace(fullpath,homedir,'~',[rfReplaceAll,rfIgnoreCase]);
GetTildePath:=sh;
end;
function PrepFilePath(fpath:string):string;
var i:integer; s:string; //Directory,FName,Extension:string;
begin
s:=SetDirSeparators(fpath);
{$IFDEF UNIX}
if Pos(':',s)>0 then LOG_Writeln(LOG_ERROR,'filepath contains windows separator '+fpath);
{$ENDIF}
//FSplit(fpath,Directory,FName,Extension,true); FName:=PrepFileName(FName); s:=Directory+PathDelim+FName+Extension;
for i:= 1 to 3 do s:=StringReplace(s,PathDelim+PathDelim,PathDelim,[rfReplaceAll,rfIgnoreCase]);
PrepFilePath:=s;
end;
function Select_Item(const strng,trenner,trenner2,dflt:string;itemno:longint) : string;
const esc_char='\';
var str,hs,tr1,tr2 : string; bcnt,trcnt : longint; dhk_start,esc_start,xx,ende : boolean;
function detsep(s,seporig,notuse1,notuse2:string):string;
(* find unique Byte as Seperator *)
const sep_start=#$8f; sep_end=#$ff;
var sep : char; ende : boolean;
begin
sep := sep_start; ende := false;
while (ord(sep)<ord(sep_end)) and not ende do
begin
if (Pos(sep,s)=0) and (sep<>notuse1) and (sep<>notuse2) then ende := true else sep:=char(ord(sep)+1);
end;
if not ende then detsep:=seporig else detsep:=sep;
end; (* detsep *)
begin
xx:=Length(trenner2)>0;
if Length(trenner) <=1 then tr1:=trenner else tr1:=detsep(strng,trenner, ' ',' ');
if Length(trenner2)<=1 then tr2:=trenner2 else tr2:=detsep(strng,trenner2,tr1,' ');
(* if not xx then tr2:=''; *)
str:=StringReplace(strng,trenner, tr1,[rfReplaceAll,rfIgnoreCase]);
str:=StringReplace(str, trenner2,tr2,[rfReplaceAll,rfIgnoreCase]);
hs := ''; bcnt := 1; dhk_start := false; ende := false; esc_start := false;
if Length(strng)>0 then trcnt := 1 else trcnt:=0;
while (bcnt <= Length(str)) and not ende do
begin
if (xx) and ( (str[bcnt] = tr2) ) and (not esc_start) then dhk_start := not dhk_start;
if (str[bcnt] = tr1) and (not dhk_start) then INC(trcnt);
if (str[bcnt] <> esc_char) then esc_start := false;
if (trcnt=itemno) and
( ( str[bcnt] <> tr1) or dhk_start) then hs:=hs+str[bcnt];
(* writeln(str[bcnt],' ',bcnt:2,' ',trcnt:2,' '); *)
INC(bcnt);
if (itemno > 0) and (trcnt > itemno) then ende := true;
end;
hs:=StringReplace(hs,tr1,trenner, [rfReplaceAll,rfIgnoreCase]);
if xx then hs:=StringReplace(hs,tr2,'', [rfReplaceAll,rfIgnoreCase])
else hs:=StringReplace(hs,tr2,trenner2,[rfReplaceAll,rfIgnoreCase]);
if itemno <= 0 then system.Str(trcnt:0,hs);
if (hs='') then hs:=dflt;
Select_Item := hs;
end;
function Select_Item(const strng,trenner,trenner2:string; itemno:longint):string;
begin Select_Item:=Select_Item(strng,trenner,trenner2,'',itemno); end;
function Anz_Item(const strng,trenner,trenner2:string): longint;
var anz:longint;
begin
if Length(strng)>0 then
begin if not Str2Num(Select_Item(strng,trenner,trenner2,0),anz) then anz:=0; end
else anz := 0;
Anz_Item := anz;
end;
function Select_RightItems(const strng,trenner,trenner2:string;startitemno:longint) : string;
var sh:string; n,m : longint;
begin
sh:=''; m:=Anz_Item(strng,trenner,trenner2);
for n := startitemno to m do
begin
sh:=sh+Select_Item(strng,trenner,trenner2,n);
if n<m then sh:=sh+trenner;
end;
Select_RightItems := sh;
end;
function Select_LeftItems(const strng,trenner,trenner2:string;enditemno:longint) : string;
var sh:string; n,m : longint;
begin
sh:=''; m:=enditemno;
for n := 1 to m do
begin
sh:=sh+Select_Item(strng,trenner,trenner2,n);
if n<m then sh:=sh+trenner;
end;
Select_LeftItems := sh;
end;
function Locate_Value(const strng,search,tr1,tr2,tr3,tr4:string; var valoutstrng:string):boolean;
// e.g. strng: SMTP_Server=xxx.yyy.com&SMTP_FromAdr=postmaster@yyy.com&SMTP_ToAdr=admin@yyy.com
// tr1='&' tr2='' tr3='=' tr4=''
// search='SMTP_FromAdr'
// valoutstrng: postmaster@yyy.com
var _found:boolean; n,anz:longint; sh:string;
begin
valoutstrng:=''; _found:=false; n:=1; anz:=Anz_Item(strng,tr1,tr2);
while (n<=anz) and (not _found) do
begin
sh:=Select_Item(strng,tr1,tr2,n);
if (Pos(Upper(search),Upper(sh))>0) then
begin
valoutstrng:=Trimme(Select_RightItems(sh,tr3,tr4,2),3);
_found:=true;
end;
inc(n);
end; // while
Locate_Value:=_found;
end;
function SepRemove(s:string):string;
var n:longint; sh:string;
begin
sh:=s;
for n:=0 to sep_max_c do sh:=StringReplace(sh,sep[n],' ',[rfReplaceAll,rfIgnoreCase]);
SepRemove:=sh;
end;
function StringListMinMaxValue(StrList:TStringList; fieldnr:word; tr1,tr2:string; var min,max:extended; var nk:longint):boolean;
var i:longint; e:extended; b1,b2:boolean; nkh,lgt:integer; sh:string;
begin
min:=NaN; max:=NaN; b1:=false; b2:=false; nk:=0;
if StrList.count>0 then
begin
min:=maxfloat; max:=-maxfloat; // was maxextended , creates error on ARM (rpi) with FPC 2.6.4
for i:= 1 to StrList.count do
begin
sh:=Select_Item(StrList[i-1],tr1,tr2,fieldnr); // 12.3456
if Str2Num(sh,e) then
begin
lgt:=Length(sh); nkh:=lgt-Pos('.',sh); if nkh=lgt then nkh:=0;
if nkh>nk then nk:=nkh;
if e>max then begin max:=e; b1:=true; end;
if e<min then begin min:=e; b2:=true; end;
end;
end;
if not b1 then max:=NaN; if not b2 then min:=NaN;
end;
StringListMinMaxValue:=(b1 and b2);
end;
procedure StringListSnap(StrListIn,StrListOut:TStringList; srchstrng:string);
var i:longint;
begin
StrListOut.clear;
for i:=1 to StrListIn.count do
begin
if Pos(srchstrng,StrListIn[i-1])=1 then StrListOut.add(StrListIn[i-1]);
end;
end;
function SearchStringInList(StrList:TStringList; srchstrng:string):string;
var sh:string; n:longint;
begin
n:=1; sh:='';
while (n<=StrList.Count) do
begin
if (Pos(srchstrng,StrList[n-1])>0) then begin sh:=StrList[n-1]; n:=StrList.Count; end;
inc(n);
end;
SearchStringInList:=sh;
end;
function SearchStringInListIdx(StrList:TStringList; srchstrng:string; occurance,StartIdx:longint):longint;
// return idx, where searchstring occurs to the 'occurance' count. If not then return -1;
// if occurence>0 then search list from 1. to last record
// if occurence<0 then search list from end to 1. record
var n,ret,occhelp : longint; found:boolean;
begin
found:=false; ret:=-1; occhelp:=0;
if occurance>0 then
begin // von 1-Ende durchsuchen
n:=StartIdx; if n<0 then n:=0;
while (n<StrList.Count) and not found do
begin
if (Pos(srchstrng,StrList[n])>0) then
begin
inc(occhelp);
if (occhelp=occurance) then begin found :=true; ret:=n; end;
end;
inc(n);
end;
end;
if occurance<0 then
begin // von Ende-1 durchsuchen
n:=StartIdx; if (n<=0) or (n>=StrList.Count) then n:=StrList.Count-1; // new 20190709
while (n>=0) and not found do
begin
if (Pos(srchstrng,StrList[n])>0) then begin inc(occhelp); if (occhelp=abs(occurance)) then begin found :=true; ret:=n; end; end;
dec(n);
end;
end;
SearchStringInListIdx:=ret;
end;
function GiveStringListIdx(StrList:TStringList; srchstrng:string; var idx:longint; occurance,StartIdx:longint):boolean;
var ok:boolean;
begin
idx:=SearchStringInListIdx(StrList, srchstrng, occurance, StartIdx);
if (idx>=0) and (idx<StrList.count) then ok:=true else ok:=false;
GiveStringListIdx:=ok;
end;
function GiveStringListIdx(StrList:TStringList; srchstrng:string; var idx:longint; occurance:longint):boolean;
var StrtIdx:longint;
begin
StrtIdx:=0; if (occurance<0) then StrtIdx:=-1;
GiveStringListIdx:=GiveStringListIdx(StrList,srchstrng,idx,occurance,StrtIdx);
end;
function GiveStringListIdx(StrList:TStringList; srchstrngSTART,srchstrngEND:string; var idx:longint):boolean;
var ok,ende:boolean; sh:string; n,p1,p2:longint;
begin
ok:=false; ende:=false; n:=1;
repeat
idx:=SearchStringInListIdx(StrList, srchstrngSTART, n,0);
// writeln(srchstrngSTART,' ',srchstrngEND,' ',idx);
if (idx>=0) and (idx<StrList.count) then
begin
sh:=StrList[idx]; p1:=Pos(srchstrngSTART,sh); p2:=Pos(srchstrngEND,sh);
ok:=(p2>p1);
// writeln(p1,' ',p2,' ',ok,' ',sh);
end
else ende:=true;
inc(n);
until ok or ende;
GiveStringListIdx:=ok;
end;
function GiveStringListIdx2(StrList:TStringList; srchstrng:string; var idxStart,idxEnd:longint):boolean;
begin
idxStart:=SearchStringInListIdx(StrList,srchstrng, 1,0);
idxEnd:= SearchStringInListIdx(StrList,srchstrng,-1,0);
GiveStringListIdx2:=((idxStart<=idxEnd) and (idxStart>=0));
end;
function SearchInConfigList(inifilbuf:TStringlist; section,name:string; secret:boolean; defaultstring:string; var line,secstart,secend:longint; var history:string): string;
function SectionLineFound(var s:string):boolean; begin SectionLineFound:=((Pos('[',s)=1) and (Pos(']',s)=Length(s))); end;
function SectionFound(var s:string; section:string):boolean; begin SectionFound:=(Pos('['+Upper(section)+']',Upper(s))=1); end;
function NameFound (var s:string; name: string):boolean; begin NameFound:= (Pos( Upper(name)+'=', Upper(s))=1); end;
var sect_found,name_found:boolean; s,sh,seclink:string; n:word; i:integer;
begin
sh:=defaultstring; sect_found:=((section='') and (inifilbuf.Count>0));
name_found:=false; seclink:=''; history:=history+'#'+section+'*';
n:=0; line:=-1; secend:=-1; if sect_found then secstart:=0 else secstart:=-1;
while (n<inifilbuf.Count) and (not (sect_found and name_found)) do
begin
// writeln(n,' ',inifilbuf.Count);
s:=inifilbuf[n];
if SectionLineFound(s) then
begin
if sect_found then secend:=n-1;
sect_found:=SectionFound(s,section);
if sect_found then
begin
secstart:=n;
// writeln('section ',section,' ',sect_found);
end;
end;
if sect_found and NameFound(s,'SECTIONLINK') then
begin
i:=Pos('=',s); seclink:=''; if i>0 then seclink:=copy(s,i+1,Length(s));
end;
if sect_found then name_found:=NameFound(s,name);
if name_found and sect_found then
begin
// inc(n);
line:=n;
i:=Pos('=',s); sh:=''; if i>0 then sh:=copy(s,i+1,Length(s));
while (n<inifilbuf.Count) do
begin s:=inifilbuf[n]; if SectionLineFound(s) then secend:=n-1; inc(n); end;
if secend<0 then secend:=inifilbuf.Count-1;
end;
inc(n);
// writeln('found section:',sect_found,' name:',name_found,' ',sh);
end;
if (secend<0) then
begin
if sect_found then secend:=inifilbuf.Count else secstart:=-1;
end;
//writeln('#',seclink,'#',name_found);
if (not name_found) and (seclink<>'') then
begin
LOG_Writeln(LOG_DEBUG,'SearchInConfigList: SECTIONLINK '+'['+seclink+'|'+name+'] is currently not supported !!! '+history);
if Pos('#'+seclink+'*',history)=0
then sh:=SearchInConfigList(inifilbuf, seclink, name, secret, defaultstring, line, secstart, secend, history)
else LOG_WRITELN(LOG_ERROR,'SearchInConfigList: Loop in SECTIONLINK '+seclink+' '+history);
end;
SearchInConfigList:=sh;
end;
function GetRndTmpFileName(filhdr,extname:string):string;
var sh:string;
begin
sh:=c_tmpdir+'/'+filhdr+FormatDateTime('YYYYMMDDhhmmss',now)+extname; // was '/tmp_' ext: .txt
GetRndTmpFileName:=PrepFilePath(sh);
end;
procedure String2StringList(str:string; StrList:TStringList);
var li:longint; sh:string;
begin
sh:=StringReplace(str,CRLF,LF,[rfReplaceAll]);
for li:= 1 to Anz_Item(sh,LF,'') do
StrList.add(Select_Item(sh,LF,'',li));
end;
function StringList2String(StrList:TStringList; tr:string):string;
var li,anz:longint; sh:string;
begin
sh:=''; anz:=StrList.count;
for li:= 1 to anz do
begin
// sh:=sh+Trimme(StrList[li-1],3); // 5:repl TAB with ' ', remove leading&trailing ' '
sh:=sh+StrList[li-1];
if li<anz then sh:=sh+tr;
end;
StringList2String:=sh;
end;
function StringList2String(StrList:TStringList):string;
begin StringList2String:=StringList2String(StrList,LineEnding); end;
function StringList2TextFile(filname:string; StrListOut:TStringList):boolean;
{ Write StringList to TextFile }
var _ok:boolean; fn:string;
begin
fn:=PrepFilePath(filname);
try
_ok:=true;
StrListOut.SaveToFile(fn);
except
_ok:=false;
LOG_Writeln(LOG_Error,'StringList2TextFile: could not write file '+fn);
end;
StringList2TextFile:=_ok;
end;
function StringList2TextFile(filname:string; StrListOut:TStringList; append_mode:boolean):boolean;
var b:boolean; sh,fn,fn2:string;
begin
b:=true;
if (StrListOut.count>0) then
begin
b:=false; fn:=PrepFilePath(filname);
if append_mode and FileExists(fn) then
begin
fn2:=GetRndTmpFileName('tmp_','.txt');
if StringList2TextFile(fn2,StrListOut) then
begin
{$ifdef WINDOWS}sh:='type '+fn2;{$else}sh:='cat '+fn2;{$endif}
sh:=sh+' >> '+fn;
b:=(call_external_prog(LOG_NONE,sh)=0);
if not b then LOG_Writeln(LOG_ERROR,'StringList2TextFile, failed: '+sh);
{$I-} DeleteFile(fn2); {$I+}
end else LOG_Writeln(LOG_ERROR,'StringList2TextFile: '+fn2);
end else b:=StringList2TextFile(fn,StrListOut);
end;
StringList2TextFile:=b;
end;
function String2TextFile(filname:string; StrOut:string):boolean;
{ Write String to TextFile }
var _ok:boolean; _tl:TStringList; fn:string;
begin
fn:=PrepFilePath(filname);
_tl:=TStringList.create;
try
String2StringList(StrOut,_tl);
_ok:=true;
_tl.SaveToFile(fn);
except
_ok:=false;
LOG_Writeln(LOG_Error,'String2TextFile: could not write file '+fn);
end;
_tl.free;
String2TextFile:=_ok;
end;
function StringListAdd2List(StrList1,StrList2:TStringList; append:boolean):longword; //Adds StringList2 to Stringlist1. result is size of Stringlist in bytes
var n:longint; siz:longword;
begin
siz:=0;
if not append then
begin // add to front
for n := StrList2.count downto 1 do
begin
StrList1.insert(0,StrList2[n-1]);
inc(siz,Length(StrList2[n-1]));
end;
end
else
begin // append
for n := 1 to StrList2.count do
begin
StrList1.add( StrList2[n-1]);
inc(siz,Length(StrList2[n-1]));
end
end;
StringListAdd2List:=siz;
end;
function StringListAdd2List(StrList1,StrList2:TStringList):longword;
begin StringListAdd2List:=StringListAdd2List(StrList1,StrList2,true); end;
function TextFile2StringList(filname:string; StrListOut:TStringList; var hash:string):boolean;
{ Read TextFile into a StringList (also possible from stdin, if filename='' ) }
var b:boolean; fn:string;
begin
b:=true; fn:=PrepFilePath(filname);
{$I-}
if FileExists(fn) then
begin
StrListOut.LoadFromFile(fn); hash:=MD5Print(MD5String(StringList2String(StrListOut,'')));
Log_Writeln(LOG_DEBUG,'Read from file: '+fn+' lines: '+Num2Str(StrListOut.count,0)+' hash: '+hash);
end
else
begin
b:=false; hash:='';
// LOG_Writeln(LOG_Error,'TextFile2StringList: could not read file '+fn);
end;
{$I+}
TextFile2StringList:=b;
end;
function TextFile2StringList(filname:string; StrListOut:TStringList; append:boolean; var hash:string):boolean;
var tl:TStringList; ok:boolean;
begin
ok:=false;
if append then
begin
tl:=TStringList.create;
ok:=TextFile2StringList(filname,tl,hash);
if ok then StringListAdd2List(StrListOut,tl);
tl.free;
end
else
begin
StrListOut.clear;
ok:=TextFile2StringList(filname,StrListOut,hash);
end;
TextFile2StringList:=ok;
end;
function TextFileContentCheck(file1,file2:string; mode:byte):boolean;
var ok:boolean; ts1,ts2:TStringList; i:longint; hash:string;
begin
ok:=false;
if FileExists(file1) and FileExists(file2) then
begin
ts1:=TStringList.create; ts2:=TStringList.create;
if TextFile2StringList(file1,ts1,false,hash) then
if TextFile2StringList(file2,ts2,false,hash) then
if (ts1.count=ts2.count) and (ts1.count>0) then
begin
ok:=true;
for i:= 1 to ts1.count do
begin
case mode of
1 : begin if Select_Item(ts1[i-1],' ','',1)<>Select_Item(ts2[i-1],' ','',1) then ok:=false; end;
else begin if ts1[i-1]<>ts2[i-1] then ok:=false; end;
end; // case
end;
end;
ts1.free; ts2.free;
end;
TextFileContentCheck:=ok;
end;
function TailFile(filname:string; LinesCount:longint):RawByteString;
var S:TStream; Validated,BytesToEnd:longint; rbs:RawByteString;
begin
rbs:='';
if FileExists(filname) then
begin
S:=TFileStream.Create(filname, fmOpenRead or fmShareDenyNone);
try
S.Seek(0,soEnd);
Validated:=0;
while (Validated<LinesCount) and (S.Seek(-2,soCurrent)>=0) do
begin
if S.ReadByte=10 then inc(Validated);
end;
if Validated<LinesCount then S.Position:=0;
BytesToEnd:=S.Size-S.Position;
SetLength(rbs,BytesToEnd);
S.ReadBuffer(PByte(rbs)[0],BytesToEnd);
finally
S.Free;
end;
end; // else LOG_Writeln(LOG_ERROR,'TailFile: does not exist '+filname);
TailFile:=rbs;
end;
procedure TailFileFollow(filname:string; LinesCount:longint);
var timo:TDateTime; so,s:string;
begin
s:=''; timo:=now;
repeat
so:=s;
s:=TailFile(filname,LinesCount);
if (s<>so) then
begin
// write(s,' 0x',HexStr(s));
write(s+#$0d);
SetTimeOut(timo,10000);
end else sleep(50);
until TimeElapsed(timo);
end;
procedure BIOS_EndIniFile;
// https://github.com/graemeg/freepascal/blob/master/packages/fcl-base/src/inifiles.pp
var res:longint;
begin
res:=0;
with IniFileDesc do
begin
if ok then
begin
if inifilbuf.CacheUpdates then
begin
inifilbuf.CacheUpdates:=false; // forces UpdateFile, if dirty
modifydate:=GetFileAge(inifilename);
end;
inifilbuf.free;
{$IFNDEF WINDOWS}
if FileExists(inifilename) then
begin
res:=fpChmod (inifilename,&600);
if (res<>0) then LOG_Writeln(LOG_ERROR,'BIOS_EndIniFile: can not set perm '+inifilename+' 0x'+HexStr(res,8));
end else res:=-1;
{$ENDIF}
end;
ok:=false;
end;
end;
function BIOS_DeleteKey(section,name:string):boolean;
begin
with IniFileDesc do
begin
if ok then
begin
inifilbuf.DeleteKey(section,name);
if (not inifilbuf.CacheUpdates) then modifydate:=GetFileAge(inifilename);
end;
BIOS_DeleteKey:=ok;
end;
end;
procedure BIOS_EraseSection(section:string);
begin
with IniFileDesc do
begin
if ok then
begin
inifilbuf.EraseSection(section);
if (not inifilbuf.CacheUpdates) then modifydate:=GetFileAge(inifilename);
end;
end; // with
end;
procedure BIOS_CacheUpdate(upd:boolean);
begin with IniFileDesc do if ok then inifilbuf.CacheUpdates:=upd; end;
function BIOS_CacheUpdate:boolean;
var upd:boolean;
begin
with IniFileDesc do
begin
if ok then upd:=inifilbuf.CacheUpdates else upd:=false;
end;
BIOS_CacheUpdate:=upd;
end;
function BIOS_GetIniFilename:string;
begin BIOS_GetIniFilename:=IniFileDesc.inifilename; end;
procedure BIOS_ReadIniFile(fname:string);
// e.g. BIOS_ReadIniFile('/etc/configfile.ini')
//var res:longint;
begin
with IniFileDesc do
begin
inifilename:=PrepFilePath(fname); ok:=false; modifydate:=0;
if inifilename<>'' then
begin
if not FileExists(inifilename)
then call_external_prog(LOG_NONE,'touch '+inifilename); // just create on
{$IFNDEF WINDOWS}
// res:=fpChmod (inifilename,&600);
// if (res<>0) then LOG_Writeln(LOG_ERROR,'BIOS_ReadIniFile: can not set perm '+inifilename+' 0x'+HexStr(res,8));
{$ENDIF}
// writeln(inifilename,' ',FileExists(inifilename),' ',(inifilbuf=nil));
// if FileExists(inifilename) then
begin // will be created, if file does not exist
if (inifilbuf<>nil) then inifilbuf.free;
inifilbuf:=TIniFile.Create(inifilename);
inifilbuf.CacheUpdates:=false; // force immediate UpdateFile after a change
modifydate:=GetFileAge(inifilename);
ok:=true;
end
// else LOG_Writeln(LOG_ERROR,'BIOS_ReadIniFile: no config file found '+inifilename);
end;
end;
end;
procedure BIOS_SetDfltSection(section:string); begin IniFileDesc.dfltsection:=section; end;
procedure BIOS_SetDfltFlags(flags:s_BIOS_flags); begin IniFileDesc.dfltflags:=flags; end;
function BIOS_GetIniString(section,name,default:string; flgs:s_BIOS_Flags):string;
// e.g. configfile.ini content:
// [SECNAME1]
// PARA1=Value 1234
// [SECNAME2]
// PARA1=Value 1
// PARAX=ValueX
// e.g. BIOS_GetIniString('SECNAME2','PARA1',false);
// return: 'Value 1'
// if Parameter is not found, then return default-string
var sh:string; bol:boolean; i64:int64; qw:qword; e:extended;
begin
sh:=default;
with IniFileDesc do
begin
if ok then
begin // read in and check. if checks not met then use default value. default val is not checked
if (section='') and (dfltsection<>'') then section:=dfltsection;
sh:=inifilbuf.ReadString(section,name,default);
if (BIOS_UnESC IN flgs) then sh:=SB_UnESC(sh);
if (BIOS_Printable IN flgs) then sh:=StringPrintable(sh);
if (BIOS_trim1 IN flgs) then sh:=Trimme(sh,1);
if (BIOS_trim2 IN flgs) then sh:=Trimme(sh,2);
if (BIOS_trim3 IN flgs) then sh:=Trimme(sh,3);
if (BIOS_trim4 IN flgs) then sh:=Trimme(sh,4);
if (BIOS_trim5 IN flgs) then sh:=Trimme(sh,5);
// checks
if (BIOS_bool IN flgs) then if not Str2Bool(sh,bol) then sh:=default;
if (BIOS_float IN flgs) then
begin
sh:=Trimme(sh,3);
if Str2Num(sh,e) then
begin
if (BIOS_NonZero IN flgs) and IsZero(e) then sh:=default;
if (BIOS_lat IN flgs) and (abs(e)> 90.0) then sh:=default;
if (BIOS_lon IN flgs) and (abs(e)>180.0) then sh:=default;
end else sh:=default;
end;
if (BIOS_int IN flgs) then
begin
sh:=Trimme(sh,3);
if Str2Num(sh,i64) then
begin
if (BIOS_NonZero IN flgs) and (i64=0) then sh:=default;
if (BIOS_1byte IN flgs) and ((i64> 127) or (i64< -128)) then sh:=default;
if (BIOS_2byte IN flgs) and ((i64>32767) or (i64<-32768)) then sh:=default;
if (BIOS_4byte IN flgs) and
((i64>2147483647) or (i64<-2147483648)) then sh:=default;
end else sh:=default;
end;
if (BIOS_uint IN flgs) then
begin
sh:=Trimme(sh,3);
if Str2Num(sh,qw) then
begin
if (BIOS_NonZero IN flgs) and (qw=0) then sh:=default;
if (BIOS_1byte IN flgs) and (qw>$ff) then sh:=default;
if (BIOS_2byte IN flgs) and (qw>$ffff) then sh:=default;
if (BIOS_4byte IN flgs) and (qw>$ffffffff) then sh:=default;
end else sh:=default;
end;
if (BIOS_tstmp IN flgs) then
begin
sh:=Trimme(sh,3);
sh:=StringReplace(sh,'T',' ',[rfReplaceAll,rfIgnoreCase]);
try StrToDateTime(sh); except sh:=default; end;
end;
if (BIOS_PrefDflt IN flgs) and (default<>'') then sh:=default;
end; // else Log_Writeln(LOG_ERROR,'BIOS_GetIniString: INI-File not opened');
end; // with
if (sh='') then sh:=default;
BIOS_GetIniString:=sh;
end;
function BIOS_GetIniString(section,name,default:string):string;
begin BIOS_GetIniString:=BIOS_GetIniString(section,name,default,IniFileDesc.dfltflags); end;
function BIOS_GetIniString(name,default:string):string;
begin BIOS_GetIniString:=BIOS_GetIniString(IniFileDesc.dfltsection,name,default,IniFileDesc.dfltflags); end;
function BIOS_GetIniString(name,default:string; flgs:s_BIOS_Flags):string;
begin BIOS_GetIniString:=BIOS_GetIniString(IniFileDesc.dfltsection,name,default,flgs); end;
function BIOS_GetIniNum(section,name:string; flgs:s_BIOS_Flags; default,minval,maxval:real):real;
var r:real; sh:string;
begin
sh:=BIOS_GetIniString(section,name,'',flgs+[BIOS_float]);
if (sh<>'') then
begin
if Str2Num(sh,r) then
begin
if not IsNan(r) then
begin
if not IsNan(minval) then if (r<minval) then r:=minval;
if not IsNan(maxval) then if (r>maxval) then r:=maxval;
end else r:=default;
end else r:=default;
end else r:=default;
BIOS_GetIniNum:=r;
end;
function BIOS_GetIniNum(section,name:string; default,minval,maxval:real):real;
begin BIOS_GetIniNum:=BIOS_GetIniNum(section,name,[],default,minval,maxval); end;
function BIOS_GetIniNum(name:string; default,minval,maxval:real):real;
begin BIOS_GetIniNum:=BIOS_GetIniNum(IniFileDesc.dfltsection,name,[],default,minval,maxval); end;
//function BIOS_SetIniString(section,name,value:string; secret,overwrite:boolean):boolean;
function BIOS_SetIniString(section,name,value:string; flgs:s_BIOS_Flags):boolean;
begin
with IniFileDesc do
begin
if ok then
begin
if (section='') and (dfltsection<>'') then section:=dfltsection;
if not ((BIOS_noOVR IN flgs) and (BIOS_GetIniString(section,name,'',flgs)<>'')) then
begin
if (BIOS_trim1 IN flgs) then value:=Trimme(value,1);
if (BIOS_trim2 IN flgs) then value:=Trimme(value,2);
if (BIOS_trim3 IN flgs) then value:=Trimme(value,3);
if (BIOS_trim4 IN flgs) then value:=Trimme(value,4);
if (BIOS_trim5 IN flgs) then value:=Trimme(value,5);
if (BIOS_DoESC IN flgs) then value:=BS_DoESC(value);
inifilbuf.WriteString(section,name,value);
if (not inifilbuf.CacheUpdates) then modifydate:=GetFileAge(inifilename);
end;
end else Log_Writeln(LOG_ERROR,'BIOS_SetIniString: INI-File not opened');
end;
BIOS_SetIniString:=true;
end;
function BIOS_SetIniString(section,name,value:string):boolean;
begin BIOS_SetIniString:=BIOS_SetIniString(section,name,value,IniFileDesc.dfltflags); end;
function BIOS_SetIniString(name,value:string):boolean;
begin BIOS_SetIniString:=BIOS_SetIniString(IniFileDesc.dfltsection,name,value,IniFileDesc.dfltflags); end;
procedure BIOS_Test;
var fil:text; sh:string;
begin
{$IFDEF UNIX} // just create a config file, only for demo reasons
sh:=GetRndTmpFileName(ApplicationName,'.ini');
assign (fil,sh); rewrite(fil);
writeln(fil,'[SECNAME1]'); writeln(fil,'PARA1=Value 1234');
writeln(fil,'[SECNAME2]'); writeln(fil,'PARA1=Value 1'); writeln(fil,'PARAX=ValueX');
close(fil);
writeln('Test start: reading the config file ',sh);
BIOS_ReadIniFile(sh);
sh:=BIOS_GetIniString('SECNAME2','PARA1','DefaultValue',[]);
writeln(' Read the parameter "PARA1" from section "SECNAME2"=',sh);
sh:=BIOS_GetIniString('SECNAME1','PARA1','DefaultValue',[]);
writeln(' Read the parameter "PARA1" from section "SECNAME1"=',sh);
sh:=BIOS_GetIniString('SECNAME2','PARA3','DefaultValue',[]);
writeln(' Read the non existent parameter "PARA3" from section "SECNAME2"=',sh);
writeln('Test end.');
BIOS_EndIniFile;
{$ENDIF}
end;
function MStream2String(MStreamIn:TMemoryStream):string;
var s:string;
begin
SetString(s,PAnsiChar(MStreamIn.memory),MStreamIn.size);
MStream2String:=s;
end;
procedure String2MStream(MStreamIn:TMemoryStream; var SourceString:string);
begin
MStreamIn.WriteBuffer(Pointer(SourceString)^, Length(SourceString));
MStreamIn.Position := 0;
end;
function MStream2File(filname:string; StreamOut:TMemoryStream):boolean;
var ok:boolean; fs:TFileStream;
begin
ok:=true; fs:=TFileStream.Create(PrepFilePath(filname), fmCreate);
if StreamOut.Size>0 then
begin StreamOut.Position:=0; fs.CopyFrom(StreamOut,StreamOut.Size); end else ok:=false;
fs.free;
MStream2File:=ok;
end;
function File2MStream(filname:string;StreamOut:TMemoryStream; var hash:string):boolean;
var b:boolean; fn:string;
begin
b:=true; fn:=PrepFilePath(filname);
{$I-}
if FileExists(fn) then
begin
StreamOut.LoadFromFile(fn);
hash:=MD5Print(MD5String(MStream2String(StreamOut)));
end
else begin b:=false; hash:=''; end;
{$I+}
File2MStream:=b;
end;
function File2MString(filname:string; var OutString,hash:string):boolean;
var b:boolean; MStream:TMemoryStream; fn:string;
begin
b:=true; fn:=PrepFilePath(filname);
{$I-}
if FileExists(fn) then
begin
MStream:=TMemoryStream.create;
MStream.LoadFromFile(fn);
OutString:=MStream2String(MStream);
hash:=MD5Print(MD5String(OutString));
MStream.free;
end
else begin b:=false; hash:=''; OutString:=''; end;
{$I+}
File2MString:=b;
end;
procedure MemCopy(src,dst:pointer; size:longint); begin if size>0 then Move(src^, dst^, size); end;
procedure MemCopy(src,dst:pointer; size,srcofs,dstofs:longint);
begin
if size>0 then
begin
{$warnings off}
Move(pointer(longword(src)+srcofs)^, pointer(longword(dst)+dstofs)^, size);
{$warnings on}
end;
end;
function GetVZ(dt1,dt2:TDateTime):integer; var vz:integer; begin if dt1>=dt2 then vz:=1 else vz:=-1; GetVZ:=vz; end;
function DeltaTime_in_ms(dt1,dt2:TDateTime):int64;
begin
DeltaTime_in_ms:=GetVZ(dt1,dt2)*MilliSecondsBetween(dt1,dt2);
end;
function LNX_ProgInstalled(progname:string):boolean;
var sh:string;
begin
if (progname<>'')
then call_external_prog(LOG_NONE,'which '+progname,sh)
else sh:='#';
LNX_ProgInstalled:=(Pos(progname,sh)>0);
end;
procedure LNX_KillProcesses(processlist:string; signal:word);
// IN: '1234 5678'
var n,num,sig:longint; sh:string;
begin
//say(log_warning,'LNX_KillProcesses:'+processlist+':');
for n:=1 to Anz_Item(processlist,' ','') do
begin
sh:=Trimme(Select_Item(processlist,' ','',n),3);
if (sh<>'') then
begin
case signal of
1..31: sig:=signal;
else sig:=1; // -hup
end; // case
if Str2Num(sh,num) then
begin
call_external_prog(LOG_NONE,'kill -'+Num2Str(sig,0)+' '+sh);
// say(log_warning,'kill -'+Num2Str(sig,0)+' '+sh);
end;
end;
end;
end;
function LNX_GetProcessNumsByName(processname:string):string;
// IN: 'tail -f /var/log/syslog'
// OUT: '1234 5678'
var cmd,lst:string;
begin
cmd:='pgrep -f "'+processname+'"';
if (call_external_prog(LOG_ERROR,cmd,lst)<>0) then lst:='';
lst:=Trimme(StringReplace(lst,LineEnding,' ',[rfReplaceAll,rfIgnoreCase]),4);
//say(LOG_WARNING,'LNX_GetProcessNumsByName: '+cmd+' '+lst);
LNX_GetProcessNumsByName:=lst;
end;
function HexStrFrm(str:string):string;
var n:longint; sh:string;
begin
sh:='';
for n:=1 to Length(str) do sh:=sh+HexStr(ord(str[n]),2)+' ';
HexStrFrm:=Trimme(sh,4);
end;
function BTLE_GetBeaconHexStr(url:string; TXPower:byte):string;
// https://developers.google.com/nearby/notifications/get-started
// https://github.com/google/physical-web
// https://learn.adafruit.com/google-physical-web-uribeacon-with-the-bluefruit-le-friend/getting-started
// https://github.com/google/eddystone/tree/master/eddystone-url
// https://play.google.com/store/apps/details?id=com.uriio
(*
hciconfig hci0 up ; hciconfig hci0 noleadv ; hciconfig hci0 noscan
enable advertize: hciconfig hci0 leadv 3
disable advertize: hciconfig hci0 noleadv
*)
const ServiceID='D8FE';
var sh:string;
begin
sh:=url; if (TXPower>3) then TXPower:=3;
//if (Pos('HTTP:',Upper(sh))>0) then LOG_Writeln(LOG_WARNING,'BTLE_GetBeaconHexStr: Nearby Notifications and Physical Web on Chrome require HTTPS URLs');
sh:=StringReplace(sh,'http://www.', #$00,[rfReplaceAll,rfIgnoreCase]);
sh:=StringReplace(sh,'https://www.', #$01,[rfReplaceAll,rfIgnoreCase]);
sh:=StringReplace(sh,'http://', #$02,[rfReplaceAll,rfIgnoreCase]);
sh:=StringReplace(sh,'https://', #$03,[rfReplaceAll,rfIgnoreCase]);
sh:=StringReplace(sh,'.com/', #$00,[rfReplaceAll,rfIgnoreCase]);
sh:=StringReplace(sh,'.org/', #$01,[rfReplaceAll,rfIgnoreCase]);
sh:=StringReplace(sh,'.edu/', #$02,[rfReplaceAll,rfIgnoreCase]);
sh:=StringReplace(sh,'.net/', #$03,[rfReplaceAll,rfIgnoreCase]);
sh:=StringReplace(sh,'.info/', #$04,[rfReplaceAll,rfIgnoreCase]);
sh:=StringReplace(sh,'.biz/', #$05,[rfReplaceAll,rfIgnoreCase]);
sh:=StringReplace(sh,'.gov/', #$06,[rfReplaceAll,rfIgnoreCase]);
sh:=StringReplace(sh,'.com', #$07,[rfReplaceAll,rfIgnoreCase]);
sh:=StringReplace(sh,'.org', #$08,[rfReplaceAll,rfIgnoreCase]);
sh:=StringReplace(sh,'.edu', #$09,[rfReplaceAll,rfIgnoreCase]);
sh:=StringReplace(sh,'.net', #$0a,[rfReplaceAll,rfIgnoreCase]);
sh:=StringReplace(sh,'.info', #$0b,[rfReplaceAll,rfIgnoreCase]);
sh:=StringReplace(sh,'.biz', #$0c,[rfReplaceAll,rfIgnoreCase]);
sh:=StringReplace(sh,'.gov', #$0d,[rfReplaceAll,rfIgnoreCase]);
//writeln('BTLE_GetBeaconHexStr: 0x'+HexStr(sh)+':'+StringPrintable(sh));
sh:=StrHex('16'+ServiceID+'00')+char(TXPower)+sh;
if (Length(sh)>23) then
begin
sh:=''; LOG_Writeln(LOG_ERROR,'BTLE_GetBeaconHexStr: url to long: '+url);
end else sh:=StrHex('0303'+ServiceID+HexStr(Length(sh),2))+sh;
//writeln('0x',HexStrFrm(sh));
BTLE_GetBeaconHexStr:=sh;
end;
function BTLE_StopBeaconStr:string;
begin
BTLE_StopBeaconStr:= 'hciconfig hci0 noleadv >/dev/null 2>&1 ; '+
'hciconfig hci0 down >/dev/null 2>&1'
end;
function BTLE_StopBeacon:boolean; // start async
begin BTLE_StopBeacon:=(RunProcess(BTLE_StopBeaconStr,'',false)=0); end;
function BTLE_StartBeacon(hexstrng:string; TXPower:byte):boolean;
var _ok:boolean;
begin
_ok:=(hexstrng<>'');
if _ok then
begin
// writeln('BTLE_StartBeacon: hcitool -i hci0 cmd 0x08 0x0008 '+HexStr(Length(hexstrng),2)+' '+HexStrFrm(hexstrng));
_ok:=(RunProcess(
BTLE_StopBeaconStr+' ; '+
'sleep 5 ; '+
'hciconfig hci0 up >/dev/null 2>&1 ; '+
'hciconfig hci0 noscan >/dev/null 2>&1 ; '+
'hciconfig hci0 leadv 3 >/dev/null 2>&1 ; '+
'hcitool -i hci0 cmd 0x08 0x0008 '+HexStr(Length(hexstrng),2)+' '+HexStrFrm(hexstrng)+' >/dev/null 2>&1',
'',false)=0); // start async
end else LOG_Writeln(LOG_ERROR,'BTLE_StartBeacon: no HexSting supplied');
BTLE_StartBeacon:=_ok;
end;
function BTLE_StartBeaconURL(url1,url2:string):longint;
// IN url1: https://www.google.com
// IN url2: https://192.168.10.200
const TXPower=0; // 0-3 0:Lowest 3:high
var li:longint; sh:string;
begin
//writeln('btle:',url1,'*',url2,'*');
li:=0;
if (li=0) then
begin
sh:=BTLE_GetBeaconHexStr(url1,TXPower);
if (sh<>'') then li:=1;
end;
if (li=0) then
begin
sh:=BTLE_GetBeaconHexStr(url2,TXPower);
if (sh<>'') then li:=2;
end;
if (li>0) then BTLE_StartBeacon(sh,TXPower)
else LOG_Writeln(LOG_ERROR,'BTLE_StartBeaconURL: to long for beacon '+url1+' '+url2);
BTLE_StartBeaconURL:=li;
end;
function OS_ShellExitDesc(ErrNum:integer):string;
// http://www.tldp.org/LDP/abs/html/exitcodes.html
var sh:string;
begin
sh:='';
{$IFDEF UNIX}
case ErrNum of
1: sh:='General error';
2: sh:='Misuse of shell builtins';
126: sh:='Command invoked cannot execute';
127: sh:='command not found';
128: sh:='Invalid exit argument';
130: sh:='Script terminated by Control-C';
else sh:='unknown error'
end; // case
{$ENDIF}
if (ErrNum<>0) then sh:=Trimme('('+Num2Str(ErrNum,0)+') '+sh,3);
OS_ShellExitDesc:=sh;
end;
function call_external_prog(typ:t_ErrorLevel; cmdline:string; receivelist:TStringList; timo_msec:word):integer;
// http://wiki.freepascal.org/Executing_External_Programs#Reading_large_output
// can return multiple lines in StringList
const BUF_SIZE=2048;
var exitStat,exitCode:integer; BytesRead:LongInt; timo:TDateTime;
OutputStream:TStream; AProcess:TProcess;
Buffer: array[1..BUF_SIZE] of byte;
begin
//writeln('cmdline:',cmdline,':');
if (cmdline<>'') then
begin
AProcess:=TProcess.Create(nil);
AProcess.Options:=[poUsePipes (* ,poWaitOnExit *)];
{$IFDEF WINDOWS}
AProcess.Executable:='c:\windows\system32\cmd.exe';
AProcess.Parameters.Add('/c');
{$ELSE}
AProcess.Executable:=sudo+'/bin/sh';
AProcess.Parameters.Add('-c');
{$ENDIF} // and // was and
if (typ<>LOG_NONE) or (Pos('2>',cmdline)<>0) then
AProcess.Options:=AProcess.Options+[poStderrToOutPut];
AProcess.Parameters.Add(cmdline);
AProcess.Execute;
OutputStream:= TMemoryStream.Create;
if (timo_msec>0) then
begin
SetTimeOut(timo,timo_msec);
repeat
if (AProcess.Output.NumBytesAvailable>0) then
begin
SetTimeOut(timo,timo_msec);
BytesRead:=AProcess.Output.Read(Buffer,BUF_SIZE);
OutputStream.Write(Buffer,BytesRead);
end else delay_msec(1000);
until TimeElapsed(timo);
end
else
begin
repeat
BytesRead:=AProcess.Output.Read(Buffer,BUF_SIZE);
OutputStream.Write(Buffer,BytesRead);
until (BytesRead=0);
end;
OutputStream.Position:=0;
receivelist.LoadFromStream(OutputStream);
OutputStream.free;
exitStat:=AProcess.exitStatus; // reported by the OS
exitCode:=AProcess.exitCode; // exit code of the process
AProcess.free;
// ShowStringlist(receivelist);
with receivelist do
begin
if (count>0) then
begin
// remove last trailing $00 (0 terminated string; remove last trailing LF
receivelist[count-1]:=CSV_RemLastSep(receivelist[count-1],#$00);
receivelist[count-1]:=CSV_RemLastSep(receivelist[count-1],LineEnding);
end;
end; // with
if ((typ< LOG_NONE) and (exitCode<>0)) or
((typ<=LOG_NOTICE) and (exitCode= 0)) then
begin
LOG_Writeln(typ,'ShellExec['+Num2Str(exitStat,0)+']: '+OS_ShellExitDesc(exitCode));
// LOG_ShowStringList(typ,receivelist);
end;
end else begin exitCode:=0; exitStat:=0; end;
call_external_prog:=exitCode;
end;
function call_external_prog(typ:t_ErrorLevel; cmdline:string; receivelist:TStringList):integer;
begin call_external_prog:=call_external_prog(typ,cmdline,receivelist,0); end;
function call_external_prog(typ:t_ErrorLevel; cmdline:string; var receivestring:string):integer;
var exitCode:integer; receivelist:TStringList;
begin
receivelist:=TStringList.create;
exitCode:=call_external_prog(typ,cmdline,receivelist,0);
//showstringlist(receivelist);
receivestring:=StringList2String(receivelist,LineEnding);
receivelist.free;
call_external_prog:=exitCode;
end;
function call_external_prog(typ:t_ErrorLevel; cmdline:string):integer;
// no content return
var exitCode,exitStat:integer; fpErrNo:longint; {$IFDEF WINDOWS} sh:string; {$ENDIF}
begin
{$IFDEF WINDOWS}
exitCode:=call_external_prog(typ,cmdline,sh);
{$ELSE}
// if (typ=LOG_ERROR) and (Pos('2>',cmdline)=0) then cmdline:=cmdline+' 2>&1';
if (typ=LOG_NONE) and (Pos('2>',cmdline)=0) then cmdline:=cmdline+' 2>/dev/null';
exitStat:=fpSystem(cmdline); // faster than TProcess method
fpErrNo :=fpgeterrno;
exitCode:=wexitStatus(exitStat);
if ((typ< LOG_NONE) and (exitCode<>0)) or
((typ<=LOG_NOTICE) and (exitCode= 0)) then
begin
LOG_Writeln(typ,'shellExec['+
Num2Str(exitStat,0)+'/'+
Num2Str(fpErrNo,0)+']: '+OS_ShellExitDesc(exitCode));
end;
{$ENDIF}
call_external_prog:=exitCode;
end;
function call_external_prog(cmdline:string):integer;
begin call_external_prog:=call_external_prog(LOG_ERROR,cmdline); end;
function RunScript(filname,para:string):integer;
var res:integer;
begin
if FileExists(filname) then
begin
// res:=call_external_prog(filname);
res:=call_external_prog(filname+' '+para+' >' +filname+'.log 2>&1');
// res:=call_external_prog(filname+' | tee ' +filname+'.log 2>&1');
end
else
begin
res:=-1;
LOG_Writeln(LOG_ERROR,'RunScript: file not exist '+filname);
end;
RunScript:=res;
end;
function RunScript(ts:TStringList; filname,para:string):integer;
var res:integer;
begin
res:=-1;
//SAY_TL(LOG_INFO,ts);
if StringList2TextFile(filname,ts) then
begin
LNX_chmod (filname,&755);
res:=RunScript(filname,para)
end else LOG_Writeln(LOG_ERROR,'RunScript: can not save '+filname);
RunScript:=res;
end;
function RunScript(ts:TStringList; para:string):integer;
var res:integer; filname:string;
begin
{$IFDEF WINDOWS}
filname:=GetRndTmpFileName('RunScript_','.bat');
{$ELSE}
filname:=GetRndTmpFileName('RunScript_','.sh');
{$ENDIF}
res:=RunScript(ts,filname,para);
DeleteFile(filname);
RunScript:=res;
end;
function RunProcess(filname,para:string; syncwait:boolean):integer;
// http://wiki.freepascal.org/Executing_External_Programs#Run_detached_program
var res,i:integer; tl:TStringList; RunProg:TProcess;
begin
res:=-1;
if FileExists(filname) then
begin
res:=0;
RunProg:=TProcess.create(nil);
RunProg.Executable:=filname;
RunProg.Options:=[];
RunProg.InheritHandles:=false; // SF new 11.11.2018
RunProg.ShowWindow:=swoShow; // SF new 11.11.2018
// Copy default environment variables including DISPLAY variable for GUI application to work
for i:= 1 to GetEnvironmentVariableCount do
RunProg.Environment.Add(GetEnvironmentString(i)); // SF new 11.11.2018
RunProg.Parameters.Add(para);
if syncwait then
begin
tl:=TStringList.Create;
RunProg.Options:=RunProg.Options+[poWaitOnExit];
end;
RunProg.Execute;
if syncwait then
begin
tl.LoadFromStream(RunProg.Output);
tl.SaveToFile(filname+'.log');
tl.Free;
end;
RunProg.Free;
end else LOG_Writeln(LOG_ERROR,'RunProcess: file not exist '+filname);
RunProcess:=res;
end;
function RunProcess(ts:TStringList; filname,para:string; syncwait:boolean):integer;
var res:integer;
begin
res:=-1;
if (filname='') then
begin
{$IFDEF WINDOWS}
filname:=GetRndTmpFileName('RunScript_','.bat');
{$ELSE}
filname:=GetRndTmpFileName('RunScript_','.sh');
{$ENDIF}
end;
if (ts.count>0) then
begin
if StringList2TextFile(filname,ts) then
begin
LNX_chmod (filname,&755);
res:=RunProcess(filname,para,syncwait);
end else LOG_Writeln(LOG_ERROR,'RunProcess: can not write '+filname);
end else LOG_Writeln(LOG_ERROR,'RunProcess: no commands given');
RunProcess:=res;
end;
function RunProcess(cmds,filname,para:string; syncwait:boolean):integer;
var res:integer; _tl:TStringList;
begin
_tl:=TStringList.create;
String2StringList(cmds,_tl);
res:=RunProcess(_tl,filname,para,syncwait);
_tl.free;
RunProcess:=res;
end;
procedure call_external_prog_Test;
const tr='#########################################################';
var res:integer; sh:string='';
begin
writeln(tr); res:=call_external_prog(LOG_WARNING, 'TryThisUnknownCommand1', sh); writeln(res:0,' ',sh);
writeln(tr); res:=call_external_prog(LOG_INFO, 'TryThisUnknownCommand2', sh); writeln(res:0,' ',sh);
{$IFDEF linux}
writeln(tr); res:=call_external_prog(LOG_ERROR, 'cat /etc/debian_version',sh); writeln(res:0,' DebianVers:',sh);
writeln(tr); res:=call_external_prog(LOG_ERROR, 'ls -l /usr/local/xxsbin',sh); writeln(res:0,' ',sh);
writeln(tr); res:=call_external_prog(LOG_ERROR, 'ls -l /usr/local/sbin', sh); writeln(res:0,' ',sh);
{$ENDIF}
writeln(tr);
end;
function LNX_SSHFSmount(site,pwd,mnt:string; var err:string):integer;
// experimental. currently not working 23.11.2018
// site IN: myuser@ftp.mysite.com:/
// pwd IN: mypassword
// mnt IN: ~/mnt/mysite
// res OUT: 0 -> OK; <>0 -> notOK err string returns err desc
// https://www.digitalocean.com/community/tutorials/how-to-use-sshfs-to-mount-remote-file-systems-over-ssh
var res:integer;
begin
if (site<>'') and (pwd<>'') and (mnt<>'') and (DirectoryExists(mnt)) then
begin
res:=call_external_prog(LOG_NONE,
''''+
// 'umount '+mnt+' >/dev/null 2>&1; '+
'echo "'+pwd+'" | sshfs "'+site+'" "'+mnt+'" -o '+
// 'NumberOfPasswordPrompts=1,ServerAliveInterval=15,ServerAliveCountMax=3,'+
// 'Compression=no,reconnect,'+
// 'nonempty,sshfs_debug,debug,loglevel=debug,'+
'workaround=rename,password_stdin,'+
'StrictHostKeyChecking=no,UserKnownHostsFile=/dev/null 2>&1'+'''',err);
end else begin res:=-1; err:='LNX_SSHFSmount: missing param'; end;
//writeln('LNX_SSHFSmount:',res,' err:',err,':');
LNX_SSHFSmount:=res;
end;
function MD5_HashGET(filnam:string; var MD5hash:string):boolean;
// MD5_HashGET('/tmp/rfm.tgz',myhashstr)
//38398e53aa45f86427ada3e9331c24f9 rfm.tgz.md5
var ok:boolean;
begin
if FileExists(filnam) then
begin
call_external_prog(LOG_NONE,'md5sum '+filnam,MD5hash);
MD5hash:=Select_Item(Trimme(MD5hash,4),' ','',1);
ok:=(MD5hash<>'');
end else ok:=false;
MD5_HashGET:=ok;
end;
function MD5_HashCreateFile(filnam,MD5filnam:string; var MD5hash:string):boolean;
// MD5_HashCreateFile('/tmp/rfm.tgz','/tmp/rfm.tgz.md5',myhashstr)
//38398e53aa45f86427ada3e9331c24f9 rfm.tgz.md5
var ok:boolean;
begin
if FileExists(filnam) and DirectoryExists(Get_Dir(MD5filnam)) then
begin
call_external_prog(LOG_NONE,'md5sum '+filnam+' > '+MD5filnam,MD5hash);
MD5hash:=Select_Item(Trimme(MD5hash,4),' ','',1);
ok:=(MD5hash<>'');
end else ok:=false;
MD5_HashCreateFile:=ok;
end;
function MD5_HashGETFile(MD5filnam:string; var MD5hash:string):boolean;
//38398e53aa45f86427ada3e9331c24f9 rfm.tgz.md5
var ok:boolean; res:longint;
begin
ok:=false;
if (GetFileSize(MD5filnam)>0) then
begin
res:=call_external_prog(LOG_NONE,'tail '+MD5filnam,MD5hash);
MD5hash:=Select_Item(Trimme(MD5hash,4),' ','',1);
ok:=(MD5hash<>''); if res>1 then ;
//SAY(LOG_WARNING,'MD5_HashGETFile:'+Num2Str(res,0)+':'+MD5filnam+':'+MD5hash+':'+Bool2Str(ok));
end;
MD5_HashGETFile:=ok;
end;
function MD5_HashGETVersion(MD5filnam:string; var version:string; var versionmd5:real):boolean;
//38398e53aa45f86427ada3e9331c24f9 rfm.tgz.md5
//...
//0.952 version <- via echo "0.952 version">>MD5filnam //
var ok:boolean; sh:string;
begin
ok:=false; versionmd5:=0; version:='';
if (GetFileSize(MD5filnam)>0) then
begin
call_external_prog(LOG_NONE,'tail -1 '+MD5filnam,sh);
sh:=Trimme(sh,4);
version:=Select_Item(sh,' ','',1);
ok:=( (version<>'') and (Pos('VERSION',Upper(Select_Item(sh,' ','',2)))>0) );
//SAY(LOG_INFO,'MD5_HashGETVersion:'+MD5filnam+':'+sh+':'+version+':'+Bool2Str(ok));
if ok then
begin
ok:=Str2Num(version,versionmd5);
if not ok then begin version:=''; versionmd5:=0; end;
end;
end;
MD5_HashGETVersion:=ok;
end;
function MD5_Check(file1,file2:string):boolean;
//file1: 38398e53aa45f86427ada3e9331c24f9 rfm.tgz.md5
//file2: 38398e53aa45f86427ada3e9331c24f9 /tmp/rfm.tgz.md5
var ok:boolean; md5f1,md5f2:string;
begin
ok:=false;
if MD5_HashGETFile(file1,md5f1) and MD5_HashGETFile(file2,md5f2) then
ok:=(Upper(md5f1)=Upper(md5f2));
MD5_Check:=ok;
end;
procedure RPI_MaintDelEnv; begin RpiMaintCmd.EraseSection('RPIMAINT'); end;
procedure RPI_MaintSetEnvExec(EXECcmd:string);
begin
RpiMaintCmd.WriteString('RPIMAINT','EXEC', EXECcmd);
end;
procedure RPI_MaintSetEnvFTP(FTPServer,FTPUser,FTPPwd,FTPLogf,FTPDefaults:string);
var sh:string;
begin
sh:=FTPDefaults; if sh='' then sh:=CURLFTPDefaults_c;
//writeln('RPI_MaintSetEnvFTP:',FTPServer,':',FTPUser,':',FTPPwd,':',FTPLogf,':',sh);
RpiMaintCmd.WriteString('RPIMAINT','FTPSRV', FTPServer);
RpiMaintCmd.WriteString('RPIMAINT','FTPUSR', FTPUser);
RpiMaintCmd.WriteString('RPIMAINT','FTPPWD', FTPPwd);
RpiMaintCmd.WriteString('RPIMAINT','FTPLOG', FTPLogf);
RpiMaintCmd.WriteString('RPIMAINT','FTPOPT', sh);
CURL_RemoveProgressfile(FTPLogf+CURLpfext_c);
end;
procedure RPI_MaintSetEnvUPD(UpdPkgSrcFile,UpdPkgDstDir,UpdPkgDstFile,UpdPkgMaintDir,UpdPkgLogf:string);
begin
RpiMaintCmd.WriteString('RPIMAINT','UPDPSF', UpdPkgSrcFile);
RpiMaintCmd.WriteString('RPIMAINT','UPDPDD', UpdPkgDstDir);
RpiMaintCmd.WriteString('RPIMAINT','UPDPDF', UpdPkgDstFile);
RpiMaintCmd.WriteString('RPIMAINT','UPDMDIR',UpdPkgMaintDir);
RpiMaintCmd.WriteString('RPIMAINT','UPDPLOG',UpdPkgLogf);
CURL_RemoveProgressfile(UpdPkgLogf+CURLpfext_c);
end;
procedure RPI_MaintSetEnvUPL(UplSrcPackageRemark,UplSrcFiles,UplDstDir,UplLogf:string);
begin // FTP-Upload
RpiMaintCmd.WriteString('RPIMAINT','UPLREM', UplSrcPackageRemark);
RpiMaintCmd.WriteString('RPIMAINT','UPLSF', UplSrcFiles);
RpiMaintCmd.WriteString('RPIMAINT','UPLDD', UplDstDir);
RpiMaintCmd.WriteString('RPIMAINT','UPLLOG', UplLogf);
CURL_RemoveProgressfile(UplLogf+CURLpfext_c);
end;
procedure RPI_MaintSetEnvDWN(DwnSrcDir,DwnlSrcFiles,DwnDstDir,DwnLogf:string);
begin // FTP-Download
RpiMaintCmd.WriteString('RPIMAINT','DWNSD', DwnSrcDir);
RpiMaintCmd.WriteString('RPIMAINT','DWNSF', DwnlSrcFiles);
RpiMaintCmd.WriteString('RPIMAINT','DWNDD', DwnDstDir);
RpiMaintCmd.WriteString('RPIMAINT','DWNLOG', DwnLogf);
CURL_RemoveProgressfile(DwnLogf+CURLpfext_c);
end;
function LNX_ErrDesc(errno:longint):string;
begin LNX_ErrDesc:='('+Num2Str(errno,0)+') '+StrError(errno); end;
function FPC_ErrDesc(ErrNum:integer):string;
var sh:string;
begin
case ErrNum of
0 : sh:='Program terminated normally';
1 : sh:='Invalid function number';
2 : sh:='File not found';
3 : sh:='Path not found';
4 : sh:='Too many open files';
5 : sh:='File access denied';
6 : sh:='Invalid file handle';
8 : sh:='Insufficient memory';
12 : sh:='Invalid file access mode';
15 : sh:='Invalid drive number';
16 : sh:='Cannot remove current directory';
17 : sh:='Cannot rename accross drives';
100 : sh:='Disk read error';
101 : sh:='Disk write error';
102 : sh:='File not assigned';
103 : sh:='File not open';
104 : sh:='File not open for input';
105 : sh:='File not open for output';
106 : sh:='Invalid numeric format';
150 : sh:='Disk is write protected';
151 : sh:='Bad drive request struct length';
152 : sh:='Drive not ready';
153 : sh:='Unknown Command';
154 : sh:='CRC error in data';
155 : sh:='Bad drive request structure length';
156 : sh:='Disk seek error';
157 : sh:='Unknown media type';
158 : sh:='Sector not found';
159 : sh:='Printer out of paper';
160 : sh:='Device write fault';
161 : sh:='Device read fault';
162 : sh:='Hardware failure';
200 : sh:='Division by zero';
201 : sh:='Range check error';
202 : sh:='Stack overflow error';
203 : sh:='Heap overflow error';
204 : sh:='Invalid pointer operation';
205 : sh:='Floating point overflow';
206 : sh:='Floating point underflow';
207 : sh:='Invalid floating point operation';
208 : sh:='Overlay manager not installed';
209 : sh:='Overlay file read error';
210 : sh:='Object not initialized';
211 : sh:='Call to abstract method';
212 : sh:='Stream register error';
213 : sh:='Collection index out of range';
214 : sh:='Collection overflow error';
215 : sh:='Arithmetic overflow error';
216 : sh:='General Protection fault';
217 : sh:='invalid operation code';
218 : sh:='Invalid value specified';
219 : sh:='Invalid typecast';
222 : sh:='Variant dispatch error';
223 : sh:='Variant array create';
224 : sh:='Variant is not an array';
225 : sh:='Var Array Bounds check error';
227 : sh:='Assertion failed error';
229 : sh:='Safecall error check';
231 : sh:='Exception stack corrupted';
232 : sh:='Threads not supported';
255 : sh:='Aborted via ^C';
300 : sh:='file IO error';
301 : sh:='non-matched array bounds';
302 : sh:='non-local procedure pointer';
303 : sh:='procedure pointer out of scope';
304 : sh:='function not implemented';
305 : sh:='breakpoint error';
306 : sh:='break by ^C';
307 : sh:='break by ^Break';
308 : sh:='break by other process';
309 : sh:='no floating point coprocessor';
310 : sh:='invalid variant type operation';
else sh:='unknown errornum';
end;
if ErrNum<>0 then sh:='('+Num2Str(ErrNum,0)+') '+sh;
FPC_ErrDesc:=sh;
end; // FPC_ErrDesc
function CURL_ErrDesc(ErrNum:longint):string; // translate some error codes
var sh:string;
begin
case ErrNum of
0: sh:='ok';
1: sh:='Unsupported protocol';
2: sh:='Failed to initialize';
3: sh:='URL malformed';
4: sh:='feature not available';
5: sh:='Couldn''t resolve proxy';
6: sh:='Couldn''t resolve host';
7: sh:='Failed to connect to host';
8: sh:='FTP weird server reply';
9: sh:='FTP access denied';
10: sh:='FTP accept failed';
11: sh:='FTP weird PASS reply';
12: sh:='FTP port timeout';
13: sh:='FTP weird PASV reply';
14: sh:='FTP weird 227 format';
15: sh:='FTP can''t resolve host IP';
16: sh:='HTTP/2 error';
17: sh:='FTP couldn''t set binary';
18: sh:='Partial file';
19: sh:='FTP couldn''t download/access the given file';
21: sh:='FTP quote error';
22: sh:='HTTP page not retrieved';
23: sh:='Write error';
25: sh:='FTP couldn''t STOR file';
26: sh:='Read error';
27: sh:='Out of memory';
28: sh:='Operation timeout';
30: sh:='FTP PORT failed';
31: sh:='FTP could not use REST';
33: sh:='HTTP range error';
34: sh:='HTTP post error';
35: sh:='SSL connect error';
36: sh:='FTP bad download resume';
37: sh:='couldn''t read file. Failed to open the file. Permissions?';
38: sh:='LDAP can not bind';
39: sh:='LDAP search failed';
42: sh:='aborted callback';
43: sh:='bad function argument';
45: sh:='interface error';
47: sh:='too many redirects';
48: sh:='unknown option specified';
49: sh:='Malformed telnet option';
51: sh:='The peer''s SSL certificate or SSH MD5 fingerprint was not OK';
52: sh:='The server didn''t reply anything';
53: sh:='SSL crypto engine not found';
54: sh:='can not set SSL crypto engine';
55: sh:='failed sending network data';
56: sh:='failure in receiving network data';
58: sh:='problem with local certificate';
59: sh:='can not use specified SSL cipher';
60: sh:='Peer certificate can not be authenticated with known CA certificate';
61: sh:='Unrecognized transfer encoding';
62: sh:='Invalid LDAP URL';
63: sh:='Maximum file size exceeded';
64: sh:='Requested FTP SSL level failed';
65: sh:='Sending the data requires a rewind that failed';
66: sh:='Failed to initialize SSL Engine';
67: sh:='failed to log in';
68: sh:='File not found on TFTP server';
69: sh:='Permission problem on TFTP server';
70: sh:='Out of disk space on TFTP server';
71: sh:='Illegal TFTP operation';
72: sh:='Unknown TFTP transfer ID';
73: sh:='File already exists (TFTP)';
74: sh:='No such user (TFTP)';
75: sh:='Character conversion failed';
76: sh:='Character conversion functions required';
77: sh:='Problem with reading the SSL CA cert';
78: sh:='The resource referenced in the URL does not exist';
79: sh:='An unspecified error occurred during the SSH session';
80: sh:='Failed to shut down the SSL connection';
82: sh:='Could not load CRL file, missing or wrong format';
83: sh:='TLS certificate issuer check failed';
84: sh:='The FTP PRET command failed';
85: sh:='RTSP: mismatch of CSeq numbers';
86: sh:='RTSP: mismatch of Session Identifiers';
87: sh:='unable to parse FTP file list';
88: sh:='FTP chunk callback reported error';
89: sh:='No connection available, the session will be queued';
90: sh:='SSL public key does not matched pinned public key';
91: sh:='Invalid SSL certificate status';
92: sh:='Stream error in HTTP/2 framing layer';
else sh:='unknown errornum';
end; // case
if ErrNum<>0 then sh:='('+Num2Str(ErrNum,0)+') '+sh;
CURL_ErrDesc:=sh;
end;
function PV_Progress(progressfile:string):integer;
// asumes that pv output is redirected to progressfile with -n option
// e.g. dd if=/dev/urandom bs=1M count=100 | pv -n -s 100m 2>/tmp/pv.out | dd of=/dev/null
// percentage is in /tmp/pv.out and is assigned to function result res
// requires apt-get install pv
var res:integer; sh:string;
begin
res:=-1;
if call_external_prog(LOG_NONE,'tail -n 1 '+progressfile,sh)=0 then
begin
sh:=Select_Item(sh,#$0a,'',1);
if not Str2Num(sh,res) then res:=-1;
end;
PV_Progress:=res;
end;
function CURLcmdCreate(usrpwd,proxy,ofil,uri:string; flags:s_rpimaintflags):string;
var cmd:string;
begin
cmd:='curl ';
//if UpdSUDO IN flags then cmd:='sudo '+cmd;
if not (UpdNoRedoRequest IN flags) then cmd:=cmd+'-Lf ';
if not (UpdNoFTPDefaults IN flags) then cmd:=cmd+CURLFTPDefaults_c+' ';
if UAgent IN flags then cmd:=cmd+'-A "User-Agent: '+UAgentDefault+'" ';
if usrpwd<>'' then cmd:=cmd+'-u '+usrpwd+' ';
if proxy<>'' then cmd:=cmd+'-x '+proxy+' ';
if UpdVerbose IN flags then cmd:=cmd+'-v ';
if UpdSSL IN flags then cmd:=cmd+CURLSSLDefaults_c+' ';
if not (UpdNoCreateDir IN flags) then cmd:=cmd+'--ftp-create-dirs ';
if ofil<>'' then
begin
if UpdNewerOnly IN flags then cmd:=cmd+'-z '+ofil+' '; // additional to -o <ofile>
cmd:=cmd+'-o '+ofil+' ';
end;
cmd:=cmd+uri;
CURLcmdCreate:=cmd;
end;
function CURL_ProgressUpdateHook(lvl:t_ErrorLevel; msgtype:MSG_Type_t; msg:string):longint;
// e.g. update external OLED, WEBGuiMsgBoard...
var res:longint; xferdata,perc,filnam:string;
begin
//1 2 3 4 5 6 7 8 9 10 11 12
//% Total % Received % Xferd AverageDload SpeedUpload TimeTotal TimeSpent TimeLeft CurrentSpeed
// "96 52.3M 96,50.3M 0 0 3101k 0,0:00:17 0:00:16 0:00:01 3549k","filename"
res:=0;
xferdata:=Select_Item(msg,',','"',1); filnam:=Select_Item(msg,',','"',2);
perc:=Select_Item(xferdata,',','',1)+'%';
writeln(#$0d+'Here is my function, which handles curl progress information asynchronously: '+filnam+' '+perc);
//e.g. code to update OLED Display
CURL_ProgressUpdateHook:=res;
end;
function CURL_ProgressThread(ptr:pointer):ptrint;
// e.g. this thread could update a Gauge on an external OLED display
var term:boolean; res:longint; sh:string;
begin
Thread_SetName('CURL_Progress');
if ptr<>nil then
begin
// SAY(LOG_DEBUG,'CURL_ProgressThread: start');
with Thread_Ctrl_ptr(ptr)^ do
begin
repeat
if CURL_DoProgressAction(Thread_Ctrl_ptr(ptr)^,term) then
begin //
if (CURL_ProgressUpdateHook_ptr<>nil) then
begin
(*% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
ThreadParaStr[4]:
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
100 168 0 168 0 0 132 0 --:--:-- 0:00:01 --:--:-- 132
1 52.3M 1 920k 0 0 352k 0 0:02:32 0:00:02 0:02:30 964k
16 52.3M 16 8719k 0 0 1889k 0 0:00:28 0:00:04 0:00:24 2950k
96 52.3M 96 50.3M 0 0 3101k 0 0:00:17 0:00:16 0:00:01 3549k
100 52.3M 100 52.3M 0 0 3119k 0 0:00:17 0:00:17 --:--:-- 3490k
100 52.3M 100 52.3M 0 0 3119k 0 0:00:17 0:00:17 --:--:-- 3490k *)
//say(LOG_INFO,'CURL_ProgressThread:'+ThreadParaStr[4]);
sh:=Trimme(RM_CRLF(ThreadParaStr[4]),4);
//e.g.: 96 52.3M 96,50.3M 0 0 3101k 0,0:00:17 0:00:16 0:00:01 3549k
res:=CURL_ProgressUpdateHook_ptr(
LOG_INFO,
curlprogmsg,
'"'+sh+'"'+ // csv progress-string: "96 52.3M ..."
','+ // csv
'"'+ThreadParaStr[2]+'"' // "filename"
);
if (res<>0) then ; // react on exit code, future use
// currently only 0 supported
end;
end;
until term or TerminateProg;
delay_msec(250); // let other Threads terminate
// SAY(LOG_DEBUG,'CURL_ProgressThread: end');
end; // with
end else Log_Writeln(LOG_ERROR,'CURL_ProgressThread: no valid ctlstruct');
EndThread;
CURL_ProgressThread:=0;
end;
procedure CURL_SetPara(var CurlThCtl:Thread_Ctrl_t; info,curlcmd,logfile,filenamelist,dirname:string; updintervall_ms:integer; flgs:s_rpimaintflags);
begin
if (CURL_ProgressUpdateHook_ptr<>nil)
then Thread_InitStruct2(CurlThCtl,@CURL_ProgressThread) // routine for handling progress bar
else Thread_InitStruct2(CurlThCtl,nil); // routine disabled
with CurlThCtl do
begin
ThreadInfo:=info;
if (UpdCleanUP IN flgs) then ThreadPara[1]:=1 else ThreadPara[1]:=0; // cleanup log-/progressfile yes/no
if (UpdShowThInfo IN flgs) then ThreadPara[2]:=1 else ThreadPara[2]:=0;
ThreadPara[0]:=updintervall_ms;
if updintervall_ms< 1500 then ThreadPara[0]:= 1500;
if updintervall_ms>15000 then ThreadPara[0]:=15000;
SetTimeOut(ThreadTimeOut,30000);
if logfile='' then
begin
ThreadParaStr[0]:=GetRndTmpFileName('curl_','.log'); // random logfilename
ThreadPara[1]:=1; // cleanup log-/progressfile
end else ThreadParaStr[0]:=PrepFilePath(logfile);
ThreadParaStr[1]:= ThreadParaStr[0]+CURLpfext_c; // progressfile
ThreadParaStr[2]:= filenamelist; // list of filenames that are transferred
ThreadParaStr[3]:= dirname; // dir info
ThreadParaStr[4]:= ''; // reserved, progress threadinfo will be returned
if (curlcmd<>'') then
begin
ThreadCmdStr:=curlcmd;
if (UpdLogAppend IN flgs) then ThreadCmdStr:=ThreadCmdStr+' >>'
else ThreadCmdStr:=ThreadCmdStr+' >';
ThreadCmdStr:=ThreadCmdStr+'"'+ThreadParaStr[0]+'"'; // logfile
if not (UpdNoProgressBar IN flgs) then
ThreadCmdStr:=ThreadCmdStr+' 2>"'+ThreadParaStr[1]+'"';// progressfile
end else Log_Writeln(LOG_ERROR,'CURL_SetPara: no valid curlcmd');
end; // with
end;
function CURL_DoProgressAction(var CurlThCtl:Thread_Ctrl_t; var terminate:boolean):boolean;
var ok:boolean;
begin
with CurlThCtl do
begin
ok:=((ThreadProgressOld<>ThreadProgress) and ThreadRunning (*and FileExists(ThreadParaStr[1])*));
if ok then
begin
ThreadProgressOld:=ThreadProgress;
SetTimeOut(ThreadTimeOut,30000); // if progress changes, retrig timeout
if not TermThread then delay_msec(ThreadPara[0]) // interval in ms
else delay_msec(100);
end;
terminate:=(TimeElapsed(ThreadTimeOut) or (not ThreadRunning) or TerminateProg);
// if terminate then writeln(LOG_INFO,'terminate: ',ThreadRunning,' Telapsed',TimeElapsed(ThreadTimeOut),' ok:',ok);
end; // with
CURL_DoProgressAction:=ok;
end;
procedure CURL_RemoveProgressfile(progressfile:string);
var sh:string;
begin if progressfile<>'' then call_external_prog(LOG_NONE,'rm -f '+progressfile,sh) end;
function CURLThread(ptr:pointer):ptrint;
// executes curl thread
begin
if ptr<>nil then
begin
Thread_SetName('CURL_Thread');
with Thread_Ctrl_ptr(ptr)^ do
begin
// SAY(LOG_INFO,'CURL+: '+ThreadCmdStr);
ThreadRetCode:=call_external_prog(LOG_NONE,ThreadCmdStr,ThreadRetStr); // sync. call
// if (ThreadRetCode<>0) then LOG_Writeln(LOG_ERROR,'CURLThread: '+CURL_ErrDesc(ThreadRetCode));
TermThread:=true; // signal that Thread will end soon
delay_msec(ThreadPara[0]); // give Threads time to react on termination
ThreadRunning:=false; // signal final termination to external Threads
end; // with
end else Log_Writeln(Log_ERROR,'CURLThread: no parameter pointer supplied');
EndThread;
CURLThread:=0;
end;
function CURL(var CurlThCtl:Thread_Ctrl_t):integer;
var cleanup:boolean; ival_ms:longint; logf,pfil:string;
function CURL_Progress:integer;
var sh:string; p:integer;
begin
p:=-1; sh:=TailFile(pfil,1);
with CurlThCtl do
begin
if (sh<>'') then
begin
sh:=RM_CRLF(Select_Item(sh,#$0d,'',Anz_Item(sh,#$0d,'')));
if (sh<>'') then
begin
ThreadParaStr[4]:=#$0d+sh; // last bar available a ThreadParamStr
write(ThreadParaStr[4]);
end;
sh:=Trimme(copy(sh,1,3),3);
if Str2Num(sh,p) and (p>=0) and (p<=100)
then begin if (p>=ThreadProgress) then ThreadProgress:=p; end
else p:=-1;
end;
end; // with
CURL_Progress:=p;
end; // CURL_Progress
begin
with CurlThCtl do
begin
if (ThreadPara[2]<>0) then Thread_ShowStruct(CurlThCtl);
logf:=ThreadParaStr[0]; // logfile
pfil:=ThreadParaStr[1]; // progress file
ival_ms:=ThreadPara[0] div 2; // interval in ms
cleanup:=(ThreadPara[1]<>0); // delete log-/progressfile after execution
CURL_RemoveProgressfile(pfil);
CURL_RemoveProgressfile(logf);
pfil:=RemoveChar(pfil,'"');
if (pfil<>'') then
begin
Thread_Start(CurlThCtl,@CURLThread,@CurlThCtl,250,0); // start curl data transfer
if ThreadFunc<>nil then // do something async with the progress information
begin
delay_msec(5000); // wait 5 sec, progress file will deliver reliable values
BeginThread(ThreadFunc,@CurlThCtl);
end;
repeat
CURL_Progress;
if (ThreadRunning) then delay_msec(ival_ms);
if (not TermThread) then delay_msec(ival_ms);
until (not ThreadRunning) or TimeElapsed(ThreadTimeOut) or TerminateProg;
// Thread_End(CurlThCtl,0);
if (ThreadRetCode<>0) then LOG_Writeln(LOG_ERROR,'CURL: '+CURL_ErrDesc(ThreadRetCode));
if cleanup then
begin
delay_msec(100);
// say(log_info,'CURL cleanup:'+pfil+':'+logf);
CURL_RemoveProgressfile(pfil);
CURL_RemoveProgressfile(logf);
end;
write(#$0d);
end;
CURL:=ThreadRetCode;
end; // with
end;
procedure CURL_Test;
// shows usage curl with progress info update
const filnam_c='52241088c1da59a359110d39c1875cda56496764';
begin
CURL_ProgressUpdateHook_ptr:=@CURL_ProgressUpdateHook; // install ext. routine
CURL_SetPara( CurlThreadCtrl, // control structure, has to be defined globally
'CURL_Test', // give the curl task a name
CURLcmdCreate(
'', // no usrpwd
'', // no proxyserver
'/dev/null', // dir for outfile (demonstration, just drop all files)
'https://github.com/Hexxeh/rpi-firmware/tarball/{'+filnam_c+'}',// files2download
[UpdNoCreateDir,UpdNoFTPDefaults] // curl flags
),
'/tmp/curltest.log', // logfile
filnam_c, // filenames
'/dev/null', // target dir
2500, // update every 2.5s (2500ms)
[UpdShowThInfo] // additional flags
);
with CurlThreadCtrl do
begin
writeln(ThreadCmdStr); // curlcmd: just show what we do
ThreadRetCode:=CURL(CurlThreadCtrl); // initiate curl download
writeln('CURL_Test: RetCode: ',ThreadRetCode);
end; // with
CURL_ProgressUpdateHook_ptr:=nil; // deinstall ext. routine
end;
procedure RPI_MaintSetVersions(versmin,versmax:real);
begin
RPI_MaintMinVersion:=versmin;
RPI_MaintMaxVersion:=versmax;
end;
function RPI_Maint(UpdFlags:s_rpimaintflags; var CurlThCtl:Thread_Ctrl_t):integer;
const test_c=false; test2_c=false; c_maxp=10;
type t_parr = array[1..c_maxp] of string;
var p2:t_parr; j,res:integer; i64:int64; r,version_new_md5,version_old_md5:real;
noMD5Chk,Reprt,test,test2:boolean;
flgs:s_rpimaintflags; cmd:t_rpimaintflags;
tl:TStringList;
sh,filnam,DfltMaintDir,cdmod,usrpwd,cmds,cmdsf,version,versold,
FTPServer,FTPUser,FTPPwd,FTPlogf,FTPOpts,
UpdPkgSrcFile,UpdPkgSrcDir,UpdPkgDstFile,UpdPkgDstDirAndFile,
UpdPkgMaintDir,UpdPkgMD5FileOld,UpdPkgDstDir,UpdPkglogf,
UplSrcFiles,UplSrcPkgRem,UplDstDir,Upllogf,DwnSrcDir,DwnSrcFiles,DwnDstDir,DwnLogf:string;
function creaOutFileOpt(ddir,fils:string):string;
var _i:longint; sh,fil:string;
begin
sh:='';
for _i:=1 to Anz_Item(fils,',','"') do
begin
fil:=Trimme(Select_Item(fils,',','"',_i),3);
if (fil<>'') then
begin
if (ddir='/dev/null') then sh:=sh+'-o /dev/null '
else sh:=sh+'-o "'+PrepFilePath(ddir+'/'+fil)+'" ';
end;
end;
sh:=Trimme(sh,3);
creaOutFileOpt:=sh;
end;
function cmdget(var p:t_parr):string; var i:integer; sh:string; begin sh:=p[1]; for i:=2 to c_maxp do sh:=sh+' '+p[i]; cmdget:=Trimme(sh,4); end;
procedure parr_clean(var p:t_parr); var i:integer; begin for i:=1 to c_maxp do p[i]:=''; end;
function parr_gets (var p:t_parr):string; var i:integer; sh:string; begin sh:=''; if test2 then for i:=1 to c_maxp do if p[i]<>'' then sh:=sh+p[i]+' '; parr_gets:=Trimme(sh,3); end;
procedure parr_show (s:string; var p:t_parr); begin if test2 then say(LOG_WARNING,'maint: '+s+':'+parr_gets(p)+':'); end;
function MD5Chk(oklvl,errlvl:T_errorlevel; file1,file2:string):boolean;
var ok:boolean; sh:string;
begin
ok:=MD5_Check(file1,file2); sh:='MD5_Check: '+file1+' '+file2+' same='+Bool2YN(ok);
if ok then say(oklvl,sh) else say(errlvl,sh);
MD5Chk:=ok;
end;
function cmd_do(p:t_parr):integer;
var cmd:string; res:integer; page3:TStringList;
begin
res:=-1;
if (p[1]<>'') then
begin
cmd:=cmdget(p);
if test then writeln('cmd_do: '+cmd);
page3:=TStringList.create;
res:=call_external_prog(LOG_NONE,cmd,page3);
if not (res=0) then
begin
if (UpdErrVerbose IN UpdFlags) then
begin
LOG_Writeln(LOG_ERROR,'could not exec '+cmd);
if (page3.count>0) then LOG_ShowStringList(LOG_ERROR,page3);
end;
end;
page3.free;
end;
cmd_do:=res;
end;
begin
DfltMaintDir:= AppDataDir_c+'/'+ApplicationName+'/maint'; // /var/lib/<CompanyShortName>/<appname>/maint
res:=-1;
test:= (UpdDBG1 IN UpdFlags);
test2:= (UpdDBG2 IN UpdFlags); //test2:=true;
noMD5Chk:= (UpdnoMD5Chk IN UpdFlags);
Reprt:= (not (UpdQuiet IN UpdFlags));
//test2:=true; test:=true;
flgs:=UpdFlags+[UpdNOP];
if (UpdOnlyMD5Chk IN flgs) then flgs:=flgs-[UpdPKGGet];
FTPServer:= RpiMaintCmd.ReadString('RPIMAINT','FTPSRV', '');
FTPUser:= RpiMaintCmd.ReadString('RPIMAINT','FTPUSR', '');
FTPPwd:= RpiMaintCmd.ReadString('RPIMAINT','FTPPWD', '');
FTPlogf:= RpiMaintCmd.ReadString('RPIMAINT','FTPLOG', '/tmp/rpimaint_ftp.log');
FTPOpts:= RpiMaintCmd.ReadString('RPIMAINT','FTPOPT', CURLFTPDefaults_c);
usrpwd:= FTPUser; if usrpwd='' then usrpwd:='anonymous';
if FTPPwd<>'' then usrpwd:=usrpwd+':'+FTPPwd;
if UpdNoFTPDefaults IN UpdFlags then FTPOpts:='';
UplSrcFiles:= RpiMaintCmd.ReadString('RPIMAINT','UPLSF', DfltMaintDir+'/supportfile_'+RPI_SNR+'.tgz');
UplSrcPkgRem:= RpiMaintCmd.ReadString('RPIMAINT','UPLREM',UplSrcFiles);
UplDstDir:= RpiMaintCmd.ReadString('RPIMAINT','UPLDD', '/'+ApplicationName+'/upload/'+RPI_SNR);
Upllogf:= RpiMaintCmd.ReadString('RPIMAINT','UPLLOG','/tmp/rpimaint_upload.log');
DwnSrcDir:= RpiMaintCmd.ReadString('RPIMAINT','DWNSD', '/'+ApplicationName);
DwnSrcFiles:= RpiMaintCmd.ReadString('RPIMAINT','DWNSF', ApplicationName+'.tgz');
DwnDstDir:= RpiMaintCmd.ReadString('RPIMAINT','DWNDD', DfltMaintDir);
Dwnlogf:= RpiMaintCmd.ReadString('RPIMAINT','DWNLOG','/tmp/rpimaint_dwnload.log');
UpdPkgSrcDir:= RpiMaintCmd.ReadString('RPIMAINT','UPDPSF', '/'+ApplicationName);
UpdPkgSrcFile:= RpiMaintCmd.ReadString('RPIMAINT','UPDPSF', '/'+ApplicationName+'/'+ApplicationName+'.tgz');
UpdPkgDstDir:= RpiMaintCmd.ReadString('RPIMAINT','UPDPDD', '/tmp');
UpdPkgDstFile:= RpiMaintCmd.ReadString('RPIMAINT','UPDPDF', ApplicationName+'.tgz');
UpdPkgMaintDir:= RpiMaintCmd.ReadString('RPIMAINT','UPDMDIR',DfltMaintDir);
UpdPkglogf:= RpiMaintCmd.ReadString('RPIMAINT','UPDPLOG','/tmp/rpimaint_updpkg.log');
UpdPkgMD5FileOld:= PrepFilePath(UpdPkgMaintDir+'/'+UpdPkgDstFile+'.md5');
UpdPkgDstDirAndFile:=PrepFilePath(UpdPkgDstDir+ '/'+UpdPkgDstFile);
for cmd IN flgs do
begin
cmds:=GetEnumName(TypeInfo(t_rpimaintflags),ord(cmd));
cmdsf:='PKGMGT['+StringReplace(cmds,'Upd','',[])+']:';
// say(LOG_Info,'maint cmd/attrib['+cmds+']: last: '+Bool2Str(cmd=High(flgs)));
res:=-1; parr_clean(p2); // clear para array
case cmd of
UpdExec: begin // e.g. EXEC=ls -l /tmp
say(LOG_Info,'enter maint step: '+cmds);
sh:=RpiMaintCmd.ReadString('RPIMAINT','EXEC','');
if (sh<>'') then
begin
for j:=1 to c_maxp do p2[j]:=Select_Item(sh,' ','',j);
// if UpdSUDO IN UpdFlags then p2[1]:='sudo '+p2[1];
MSG_HUB(LOG_INFO,maintmsg,cmdsf);
res:=cmd_do(p2);
end else res:=0;
end;
UpdUpld: begin
say(LOG_Info,'enter maint step: '+cmds);
if (FTPServer='') then
begin
say(LOG_ERROR,cmdsf+' no FTPServerInfo supplied, use RPI_MaintSetEnvFTP');
break;
end;
if (UplSrcFiles='') then
begin
say(LOG_ERROR,cmdsf+' no UpdPkgInfo supplied, use RPI_MaintSetEnvUPL');
break;
end;
//curl -u usr:pwd <curldefaults> -v -k --ssl -T "{file1,file2}" "ftp://host/upload/" > file.log 2> file.log.prog
p2[1]:='curl';
// if UpdSUDO IN UpdFlags then p2[1]:='sudo '+p2[1];
if usrpwd<>'' then p2[2]:='-u '+usrpwd;
if UpdVerbose IN UpdFlags then p2[2]:=p2[2]+' -v';
if UpdSSL IN UpdFlags then p2[2]:=p2[2]+' '+CURLSSLDefaults_c;
if not (UpdNoCreateDir IN UpdFlags) then p2[2]:=p2[2]+' --ftp-create-dirs';
if FTPOpts<>'' then p2[2]:=p2[2]+' '+FTPOpts;
p2[3]:='-T "{'+UplSrcFiles+'}"';
i64:=GetFilePackSize(UplSrcFiles);
if (([UpdProtoHTTP,UpdProtoHTTPS] * UpdFlags) <> []) then
begin
if (UpdProtoHTTPS IN UpdFlags) then p2[4]:='https' else p2[4]:='http'
end else p2[4]:='ftp';
p2[4]:='"'+ p2[4] +'://'+FTPServer+UplDstDir+'"'; // if you have multiple files, do not forget trailing /
parr_show('#1',p2);
//writeln('UplDstDir:',p2[4],' UplSrcFiles:',UplSrcFiles);
MSG_HUB(LOG_INFO,maintmsg,cmdsf+' '+FormatFileSize(i64));
if CURL_ProgressUpdateHook_ptr<>nil then MSG_HUB(LOG_INFO,maintmsg,cmdsf+' starting...');
CURL_SetPara(CurlThCtl,cmdsf,cmdget(p2),Upllogf,UplSrcFiles,Get_Dirs(UplSrcFiles),0,UpdFlags);
res:=CURL(CurlThCtl);
if (res<>0) then
begin
LOG_Writeln(LOG_ERROR,cmdsf+' Step#1 '+parr_gets(p2));
MSG_HUB( LOG_ERROR,maintmsg,'curl#1: '+CURL_ErrDesc(res));
end else say( LOG_NOTICE,cmdsf+' '+Trimme('file '+UplSrcPkgRem+' successfully uploaded',4));
end;
UpdDwnld: begin // download file(s)
say(LOG_Info,'enter maint step: '+cmds);
if (FTPServer='') and (not (UpdProtoRAW IN UpdFlags)) then
begin
say(LOG_ERROR,cmdsf+' no FTPSupportServer supplied, use RPI_MaintSetEnvFTP before');
break;
end;
if (DwnSrcDir='') then
begin
say(LOG_ERROR,cmdsf+' no DwnSrcDir supplied, use RPI_MaintSetEnvDWN');
break;
end;
if (DwnSrcFiles='') then
begin
say(LOG_ERROR,cmdsf+' no DwnSrcFiles supplied, use RPI_MaintSetEnvDWN');
break;
end;
if (DwnDstDir='') then
begin
say(LOG_ERROR,cmdsf+' no DwnDstDir supplied, use RPI_MaintSetEnvDWN');
break;
end;
cdmod:='/#1';
if UpdProtoRAW IN UpdFlags then
begin
sh:=DwnSrcDir+'/'+'{'+DwnSrcFiles+'}';
end
else
begin
if (([UpdProtoHTTP,UpdProtoHTTPS] * UpdFlags) <> []) then
begin
if (UpdProtoHTTPS IN UpdFlags) then sh:='https' else sh:='http'
end else sh:='ftp';
sh:=sh +'://'+FTPServer+PrepFilePath(DwnSrcDir+'/')+'{'+DwnSrcFiles+'}';
end;
if DwnDstDir='/dev/null' then cdmod:=''; if cdmod<>'' then ;
//curl -u usr:pwd -v -k --ssl -o "./file1" -o "./file2" "ftp://www.xyz.com/dir/{file1,file2}" > "file.log" 2> "file.log.prog"
p2[1]:='curl';
if usrpwd<>'' then p2[2]:='-u '+usrpwd;
// if UpdSUDO IN UpdFlags then p2[1]:='sudo '+p2[1];
if not (UpdNoRedoRequest IN UpdFlags) then p2[2]:=p2[2]+' -Lf';
if UpdVerbose IN UpdFlags then p2[2]:=p2[2]+' -v';
if UpdSSL IN UpdFlags then p2[2]:=p2[2]+' '+CURLSSLDefaults_c;
if not (UpdNoCreateDir IN UpdFlags) then p2[2]:=p2[2]+' --ftp-create-dirs';
p2[2]:=p2[2]+' '+FTPOpts;
// p2[3]:='-o'; p2[4]:='"'+PrepFilePath(DwnDstDir+cdmod)+'"';
p2[3]:=creaOutFileOpt(DwnDstDir,DwnSrcFiles); p2[4]:='';
p2[5]:='"'+sh+'"';p2[6]:='';
parr_show('#1',p2);
MSG_HUB(LOG_INFO,maintmsg,cmdsf);
if CURL_ProgressUpdateHook_ptr<>nil then MSG_HUB(LOG_INFO,maintmsg,cmdsf+' starting...');
CURL_SetPara(CurlThCtl,cmdsf,cmdget(p2),Dwnlogf,DwnSrcFiles,DwnDstDir,0,UpdFlags);
res:=CURL(CurlThCtl);
if (res<>0) then
begin
LOG_Writeln(LOG_ERROR,cmdsf+' Step#1 '+parr_gets(p2));
MSG_HUB( LOG_ERROR,maintmsg,'curl#1('+Num2Str(res,0)+') '+CURL_ErrDesc(res));
end else say( LOG_Info,cmdsf+' successfully downloaded '+DwnSrcFiles);
end;
UpdPKGcopy: begin // copy install package from source directory (e.g. USB-Stick)
say(LOG_Info,'enter maint step: '+cmds);
if (UpdPkgSrcDir='') or (UpdPkgDstFile='') then
begin
say(LOG_ERROR,cmdsf+' no UpdPkgSrcFile supplied, use RPI_MaintSetEnvUPD before');
break;
end;
p2[1]:='cp';
if UpdForce IN UpdFlags then p2[1]:=p2[1]+' -f';
if UpdVerbose IN UpdFlags then p2[1]:=p2[1]+' -v';
if UpdUpdate IN UpdFlags then p2[1]:=p2[1]+' -u';
// if UpdSUDO IN UpdFlags then p2[1]:='sudo '+p2[1];
p2[3]:=UpdPkgDstDir;
p2[2]:=PrepFilePath(UpdPkgSrcDir+'/'+UpdPkgDstFile);
if FileExists(p2[2]) then
begin
parr_show('#1',p2); cmd_do(p2);
p2[2]:=p2[2]+'.md5';
if FileExists(p2[2]) then
begin
parr_show('#2',p2); cmd_do(p2);
if FileExists(PrepFilePath(UpdPkgDstDir+'/'+UpdPkgDstFile)) and
FileExists(PrepFilePath(UpdPkgDstDir+'/'+UpdPkgDstFile)+'.md5')
then res:=0
else LOG_Writeln(LOG_ERROR,cmdsf+' Step#3 can not copy required install files '+UpdPkgDstFile);
end else LOG_Writeln(LOG_ERROR,cmdsf+' Step#2 file '+p2[2]+' does not exist');
end else LOG_Writeln(LOG_ERROR,cmdsf+' Step#1 file '+p2[2]+' does not exist');
if (res=0) then MSG_HUB(LOG_NOTICE,maintmsg,cmdsf+' USB-Stick')
else MSG_HUB(LOG_ERROR, maintmsg,cmdsf+' USB-Stick');
end;
UpdOnlyMD5Chk,
UpdPKGGet: begin // get a whole install package, check if download is needed
say(LOG_Info,'enter maint step: '+cmds);
if FTPServer='' then
begin
say(LOG_ERROR,cmdsf+' no FTPSupportServer supplied, use RPI_MaintSetEnvFTP before');
break;
end;
SAY(LOG_INFO,cmdsf+' download md5 file');
if (([UpdProtoHTTP,UpdProtoHTTPS] * UpdFlags) <> []) then
begin
if (UpdProtoHTTPS IN UpdFlags) then sh:='https' else sh:='http'
end else sh:='ftp';
sh:=sh+'://'+FTPServer+UpdPkgSrcFile;
//curl -u usr:pwd <curldefaults> -v -k --ssl -o <dstfile>.md5 "ftp://ftp.host.com/<MaintUpdPkgSrcFile>.md5" > file.log 2> file.log.prog
p2[1]:='curl';
if usrpwd<>'' then p2[2]:='-u '+usrpwd;
// if UpdSUDO IN UpdFlags then p2[1]:='sudo '+p2[1];
if UpdVerbose IN UpdFlags then p2[2]:=p2[2]+' -v';
if UpdSSL IN UpdFlags then p2[2]:=p2[2]+' '+CURLSSLDefaults_c;
if not (UpdNoCreateDir IN UpdFlags) then p2[2]:=p2[2]+' --ftp-create-dirs';
p2[2]:=p2[2]+' '+FTPOpts;
p2[3]:='-o'; p2[4]:='"'+UpdPkgDstDirAndFile+'.md5"';
p2[5]:='"'+sh+'.md5"';p2[6]:='';
parr_show('#1',p2);
if Reprt then MSG_HUB(LOG_INFO,maintmsg,cmdsf+' md5');
CURL_SetPara(CurlThCtl,cmdsf+' md5',cmdget(p2),FTPlogf,UpdPkgDstFile+'.md5',Get_Dirs(UpdPkgDstDirAndFile),0,UpdFlags);
res:=CURL(CurlThCtl);
if (res=0) then
begin
MD5_HashGETVersion(UpdPkgMD5FileOld, versold,version_old_md5);
MD5_HashGETVersion(UpdPkgDstDirAndFile+'.md5', version,version_new_md5);
say(LOG_NOTICE,cmdsf+' successfully downloaded '+UpdPkgDstFile+'.md5 ('+Num2Str(version_old_md5,0,3)+' '+Num2Str(version_new_md5,0,3)+')');
if not MD5Chk(LOG_INFO,LOG_WARNING,UpdPkgDstDirAndFile+'.md5',UpdPkgMD5FileOld) then res:=-1;
// if (cmd=UpdOnlyMD5Chk) then LOG_Writeln(LOG_WARNING,'UpdOnlyMD5Chk: '+Num2Str(res,0));
if (cmd<>UpdOnlyMD5Chk) and (noMD5Chk or (res<0)) then
begin // get big file, there is a different package available
SAY(LOG_INFO,cmdsf+' download tar ball');
p2[4]:='"'+UpdPkgDstDirAndFile+'"'; p2[5]:='"'+sh+'"';
parr_show('#2',p2);
if Reprt then MSG_HUB(LOG_INFO,maintmsg,cmdsf+' '+version);
CURL_SetPara(CurlThCtl,cmdsf,cmdget(p2),FTPlogf,UpdPkgDstFile,Get_Dirs(UpdPkgDstDirAndFile),0,UpdFlags+[UpdLogAppend]);
res:=CURL(CurlThCtl);
if (res=0) then
begin
i64:=GetFilePackSize(UpdPkgDstFile);
if Reprt then MSG_HUB(LOG_NOTICE,maintmsg,cmdsf+' '+FormatFileSize(i64)+' '+version);
say(LOG_NOTICE,cmdsf+' successfully downloaded '+FormatFileSize(i64)+' '+version+' '+UpdPkgDstFile);
parr_clean(p2);
p2[1]:='md5sum'; p2[2]:=UpdPkgDstDirAndFile;
// if UpdSUDO IN UpdFlags then p2[1]:='sudo '+p2[1];
p2[3]:='>'; p2[4]:='"'+UpdPkgDstDirAndFile+'.md5.2"';
parr_show('#3',p2);
if (cmd_do(p2)=0) then
begin
if MD5Chk(LOG_INFO,LOG_ERROR,UpdPkgDstDirAndFile+'.md5',UpdPkgDstDirAndFile+'.md5.2') then
begin
res:=0; say(LOG_NOTICE,cmdsf+' valid md5 of '+UpdPkgDstFile);
if Reprt then MSG_HUB(LOG_NOTICE,maintmsg,cmdsf+' chk md5');
end
else
begin
LOG_Writeln(LOG_ERROR,cmdsf+' Step#4 '+parr_gets(p2));
MSG_HUB(LOG_ERROR,maintmsg,cmdsf+' chk md5 bad xfr');
if UpdKeepFile IN UpdFlags then
begin
parr_clean(p2);
p2[1]:='cp';
p2[2]:=UpdPkgDstDirAndFile;
p2[3]:=UpdPkgDstDirAndFile+'.err';
parr_show('#4',p2);
cmd_do(p2); // cp unvalid package
end;
parr_clean(p2);
p2[1]:='rm'; p2[2]:='-f';
// if UpdSUDO IN UpdFlags then p2[1]:='sudo '+p2[1];
p2[3]:=UpdPkgDstDirAndFile;
parr_show('#5',p2);
LOG_Writeln(LOG_ERROR,cmdsf+' invalid md5 of '+UpdPkgDstFile+' '+parr_gets(p2)+' bad xfr');
cmd_do(p2); // remove unvalid package
end;
end else begin LOG_Writeln(LOG_ERROR,cmdsf+' Step#3 '+parr_gets(p2)); end;
end
else
begin
LOG_Writeln(LOG_ERROR,cmdsf+' Step#2 '+parr_gets(p2));
MSG_HUB( LOG_ERROR,maintmsg,'curl#2('+Num2Str(res,0)+') '+CURL_ErrDesc(res));
end;
end
else
begin
if (cmd<>UpdOnlyMD5Chk) then
begin
res:=0;
say(LOG_Info,cmdsf+' valid md5 of '+UpdPkgDstFile+', file was already successfully transferred');
end;
end;
end
else
begin
LOG_Writeln(LOG_ERROR,cmdsf+' Step#1 '+parr_gets(p2));
MSG_HUB( LOG_ERROR,maintmsg,'curl#1 '+CURL_ErrDesc(res));
end;
end;
UpdPKGInstV,
UpdPKGInst: begin
say(LOG_Info,'enter maint step: '+cmds);
if (UpdPkgDstFile='') then
begin
say(LOG_ERROR,cmdsf+' no UpdPkgInfo supplied, use RPI_MaintSetEnvUPD');
break;
end;
MD5_HashGETVersion(UpdPkgDstDirAndFile+'.md5',version,version_new_md5);
if noMD5Chk or (not MD5Chk(LOG_INFO,LOG_WARNING,UpdPkgDstDirAndFile+'.md5',UpdPkgMD5FileOld)) then
begin // newer pkg should be available, try to install it
say(LOG_INFO,cmdsf+' deploying newer package '+UpdPkgDstFile);
if FileExists(UpdPkgDstDirAndFile) then
begin
p2[1]:='tar'; p2[2]:='-xvzf';
// if UpdSUDO IN UpdFlags then p2[1]:='sudo '+p2[1];
p2[3]:=UpdPkgDstDirAndFile; p2[4]:='-C';
p2[5]:=UpdPkgDstDir; p2[6]:='';
if UpdLogAppend IN UpdFlags then p2[7]:='>>' else p2[7]:='>';
p2[8]:=UpdPkglogf; p2[9]:='2>&1';
parr_show('#1',p2);
MSG_HUB(LOG_INFO,maintmsg,cmdsf+' UnPck');
if (cmd_do(p2)=0) then
begin
if UpdPKGInstV IN UpdFlags then
begin
// LOG_Writeln(LOG_ERROR,cmdsf+' UpdPKGInstVers currently not implemented');
r:=0;
filnam:=PrepFilePath(UpdPkgDstDir+'/version.txt');
if FileExists(filnam) then
begin
tl:=TStringList.create;
if TextFile2StringList(filnam,tl,sh) then
begin
if (tl.count>0) then
begin
sh:=FilterChar(tl[0],'0123456789.');
if Str2Num(sh,r) then
begin
// maint[UpdPKGInstal]: (/tmp/version.txt 0.92) V:0.920
SAY(LOG_Info,cmdsf+' ('+filnam+' '+tl[0]+') V:'+Num2Str(r,0,3)+
' Vmin:'+Num2Str(RPI_MaintMinVersion,0,3)+
' Vmax:'+Num2Str(RPI_MaintMaxVersion,0,3));
if (RPI_MaintMinVersion>0) and (r<RPI_MaintMinVersion) then
begin
LOG_Writeln(LOG_ERROR,cmdsf+' version '+Num2Str(r,0,3)+' < required minimum version '+Num2Str(RPI_MaintMinVersion,0,3)+' stop installation');
break;
end;
if (RPI_MaintMaxVersion>0) and (r>RPI_MaintMaxVersion) then
begin
LOG_Writeln(LOG_ERROR,cmdsf+' version '+Num2Str(r,0,3)+' > required maximum version '+Num2Str(RPI_MaintMaxVersion,0,3)+' stop installation');
break;
end;
end else LOG_Writeln(LOG_ERROR, cmdsf+' no valid version supplied ('+sh+'), installing package');
end else LOG_Writeln(LOG_ERROR, cmdsf+' version file has no content, installing package');
end else LOG_Writeln(LOG_ERROR, cmdsf+' version file not supplied, installing package');
tl.free;
end;
end; // UpdPKGInstVers
parr_clean(p2);
filnam:=PrepFilePath(UpdPkgDstDir+'/install.sh');
p2[1]:='chmod'; p2[2]:='+x';
// if UpdSUDO IN UpdFlags then p2[1]:='sudo '+p2[1];
p2[3]:=filnam;
p2[7]:='>>'; p2[8]:=UpdPkglogf; p2[9]:='2>&1';
parr_show('#2',p2);
if (cmd_do(p2)=0) then
begin
parr_clean(p2);
p2[1]:=filnam+' "'+rpi_snr+'" "'+UpdPkgMaintDir+'" "'+UpdPkglogf+'"'; // execute install.sh
// if UpdSUDO IN UpdFlags then p2[1]:='sudo '+p2[1];
p2[7]:='>>'; p2[8]:=UpdPkglogf; p2[9]:='2>&1';
parr_show('#3',p2);
if (cmd_do(p2)=0) then
begin
res:=0;
parr_clean(p2); // cp -f /tmp/rfm.tgz.md5 <UpdPkgMaintDir>/rfm.tgz.md5
p2[1]:='cp -f '+UpdPkgDstDirAndFile+'.md5 '+UpdPkgMD5FileOld;
// if UpdSUDO IN UpdFlags then p2[1]:='sudo '+p2[1];
cmd_do(p2);
if res=0 then
begin
say(LOG_NOTICE,cmdsf+' package '+UpdPkgDstFile+' successfully deployed');
MSG_HUB(LOG_NOTICE,maintmsg,cmdsf+' Inst');
end else LOG_Writeln(LOG_ERROR,cmdsf+' Step#5 '+p2[1]);
end else LOG_Writeln(LOG_ERROR,cmdsf+' Step#4 '+p2[1]);
end else LOG_Writeln(LOG_ERROR,cmdsf+' Step#3 '+parr_gets(p2));
end else LOG_Writeln(LOG_ERROR,cmdsf+' Step#2 '+parr_gets(p2));
end else LOG_Writeln(LOG_ERROR,cmdsf+' Step#1, Package not available: '+UpdPkgDstFile);
end
else
begin
res:=0;
say(LOG_INFO,cmdsf+' Packages are identical, no update needed');
MSG_HUB(LOG_INFO,maintmsg,cmdsf+' already inst');
end;
end;
else res:=0; // do nothing, just attribs no commands
end; // case
if res<>0 then break;
end; // for
RPI_Maint:=res;
end;
function TTY_console:string;
var sh:string;
begin
call_external_prog(LOG_NONE,'cat /sys/class/tty/console/active',sh);
sh:=Trimme(sh,4);
//writeln('TTY_console:',sh,':');
TTY_console:=sh;
end;
function TTY_setterm(lvl:t_ErrorLevel; ttydev,ttyopts:string):integer;
// setterm --cursor off --clear all > /dev/tty1
var res:integer; sh:string;
begin
res:=-1;
if FileExists(ttydev) and (ttyopts<>'') then
begin
sh:='setterm '+ttyopts+' > '+ttydev;
// SAY(lvl,sh);
res:=call_external_prog(LOG_NONE,sh,sh);
end else LOG_Writeln(LOG_ERROR,'TTY_setterm: device does not exist: '+ttydev);
TTY_setterm:=res;
end;
function TTY_sttySpeed(lvl:t_ErrorLevel; ttyandspeed:string):integer; // e.g. /dev/ttyAMA0@9600 -cstopb -parodd
var res:integer; _speed,_par,_tty,sh:string; baudr:longword;
begin
res:=-1;
_par:= Select_RightItems (ttyandspeed,' ','',2); // -cstopb -parodd
_tty:= Select_Item (ttyandspeed,' ','',1); // /dev/ttyAMA0@9600
_speed:=Select_Item (_tty,'@','',2); // 9600
_tty:= Select_Item (_tty,'@','',1); // /dev/ttyAMA0
if not Str2Num(_speed,baudr) then baudr:=9600;
if FileExists(_tty) then
begin
sh:=Trimme('stty -F '+_tty+' '+Num2Str(baudr,0)+' '+_par,3);
SAY(lvl,sh);
res:=call_external_prog(LOG_NONE,sh,sh);
end else LOG_Writeln(LOG_ERROR,'TTY_sttySpeed: device does not exist: '+_tty);
TTY_sttySpeed:=res;
end;
procedure ERR_MGMT_UPD(errhdl:integer; cmdcode,datalgt:byte; modus:boolean);
begin
if (errhdl<>NO_ERRHNDL) and (Length(ERR_MGMT)>0) and (errhdl<Length(ERR_MGMT)) then
begin
with ERR_MGMT[errhdl] do
begin
if modus then
begin // ok part
TSokOld:=TSok; TSok:=now;
if AutoReset_ms>0 then
begin
if (MilliSecondsBetween(TSok,TSerr)>=AutoReset_ms) then
begin RDerr:=0; WRerr:=0; CMDerr:=0; end;
end;
end
else
begin // error part
TSerrOld:=TSerr; TSerr:=now;
case cmdcode of
_IOC_READ: inc(RDerr);
_IOC_WRITE: inc(WRerr);
else inc(CMDerr);
end; // case
end;
end; // with
end;
end;
function ERR_MGMT_GetErrCnt(errhdl:integer):longword;
var err:longword;
begin
err:=0;
if (Length(ERR_MGMT)>0) and (errhdl>=0) and (errhdl<Length(ERR_MGMT)) then
with ERR_MGMT[errhdl] do err:=RDerr+WRerr+CMDerr;
ERR_MGMT_GetErrCnt:=err;
end;
function ERR_MGMT_STAT(errhdl:integer):boolean;
var ok:boolean;
begin
if (Length(ERR_MGMT)>0) and (errhdl<Length(ERR_MGMT)) then
begin
with ERR_MGMT[errhdl] do begin ok:=((RDerr+WRerr+CMDerr)<=MAXerr); end;
end else ok:=true;
ERR_MGMT_STAT:=ok;
end;
function ERR_NEW_HNDL(adr:word; descr:string; maxerrs,AutoResetMsec:longword):integer;
var h:integer;
begin
SetLength(ERR_MGMT,(Length(ERR_MGMT)+1));
h:=(Length(ERR_MGMT)-1);
if h>=0 then
begin
with ERR_MGMT[h] do
begin
addr:=adr; desc:=descr;
RDErr:=0; WRErr:=0; CMDerr:=0; MaxErr:=maxerrs;
TSok:=now; TSokOld:=TSok; TSerr:=TSok; TSerrOld:=TSerr;
AutoReset_ms:=AutoResetMsec; // 0:off
end; // with
end;
ERR_NEW_HNDL:=h;
end;
procedure ERR_Report(errhdl:integer);
var _lvl:T_ErrorLevel;
begin
if (Length(ERR_MGMT)>0) and (errhdl<Length(ERR_MGMT)) then
begin
with ERR_MGMT[errhdl] do
begin
if ERR_MGMT_STAT(errhdl) then _lvl:=LOG_NOTICE else _lvl:=LOG_ERROR;
LOG_Writeln(_lvl, 'ERR_MGMT[0x'+HexStr(addr,4)+']: '+desc+
' ERR RD:'+Num2Str(RDerr,0)+
' WR:'+Num2Str(WRerr,0)+
' CMD:'+Num2Str(CMDerr,0)+
' AutoReset:'+Num2Str(AutoReset_ms,0)+'ms');
end; // with
end;
end;
procedure ERR_End(hndl:integer);
var i:integer;
begin
for i:= 1 to Length(ERR_MGMT) do ERR_Report(i-1);
SetLength(ERR_MGMT,0);
end;
{$IFDEF UNIX}
function Term_ptmx(var termio:Terminal_device_t; link:string; menablemask,mdisablemask:longint):boolean;
// opens pseudo terminal.
// returns master and slave filedescriptor, and slavename for usage. link, links slavename to link
// masks: Term_ptmx(x,x,x,x, 0,ECHO) -> disables TerminalECHO // 0=noEnableAnything,disable ECHO
const ptmx_c='/dev/ptmx';
var snp:pchar; linkflag:boolean; tl:TStringList; newsettings:termios; sh:string;
begin
with termio do
begin
slavepath:=''; masterpath:=ptmx_c; linkpath:=link;
fdslave:=-1; rlgt:=-1; ridx:=0; linkflag:=true;
fdmaster := fpopen (ptmx_c, Open_RDWR or O_NONBLOCK);
if fdmaster>=0 then
begin
if grantpt(fdmaster)>=0 then
begin
if unlockpt(fdmaster)>=0 then
begin
snp:=ptsname(fdmaster);
if snp<>nil then
begin
slavepath:=snp;
fdslave:=fpopen(snp, Open_RDWR or O_NONBLOCK);
if fdslave>=0 then
begin
if FileExists(slavepath) then
begin
if link<>'' then
begin
tl:=TStringList.create;
if FileExists(link) then
begin
LOG_WRITELN(LOG_Warning,'ptmx, link exits: '+link+' (unlink '+link+')');
call_external_prog(LOG_NONE,'unlink '+link+' ; ls -l '+link,sh);
LOG_ShowStringList(LOG_WARNING,tl);
// sleep(500);
end;
if (not FileExists(link)) then
begin
call_external_prog(LOG_NONE,'ln -s '+slavepath+' '+link+'; ls -l '+link,sh);
//LOG_ShowStringList(LOG_WARNING,tl);
// sleep(500);
linkflag:=FileExists(link);
if not linkflag then
begin
LOG_WRITELN(LOG_ERROR,'ptmx: cannot create link '+link+' (ln -s '+slavepath+' '+link+')');
LOG_ShowStringList(LOG_ERROR,tl);
end;
// if master_termioflags_AND_mask<>0 then
begin
tcgetattr(fdmaster, @newsettings); // pmtx(x,x,x,x, 0,ECHO) -> disables TerminalECHO
newsettings.c_lflag:=(newsettings.c_lflag or menablemask) and (not mdisablemask); // &= ~(ECHO | ICANON | IEXTEN | ISIG);
tcsetattr(fdmaster, TCSANOW, @newsettings); // was TCSADRAIN
end;
end else LOG_WRITELN(LOG_ERROR,'ptmx: link already exists: '+link);
tl.free;
end;
end else LOG_WRITELN(LOG_ERROR,'ptmx: not created '+slavepath);
end else LOG_WRITELN(LOG_ERROR,'ptmx: cannot open '+slavepath);
end else LOG_WRITELN(LOG_ERROR,'ptmx: cannot get slavepath');
end else LOG_WRITELN(LOG_ERROR,'ptmx: cannot unlockpt');
end else LOG_WRITELN(LOG_ERROR,'ptmx: cannot grantpt');
end else LOG_WRITELN(LOG_ERROR,'ptmx: cannot open '+ptmx_c);
// writeln('ptmx fd: ',fdmaster,' ',fdslave,' ',((fdmaster>=0) and (fdslave>=0) and (slavepath<>'') and linkflag),' ',slavename);
Term_ptmx:=((fdmaster>=0) and (fdslave>=0) and (slavepath<>'') and linkflag);
end; // with
end;
function TermIO_Read(var term:Terminal_device_t; rawmode:boolean):string;
var i:longint; str:string; ende:boolean;
begin
str:=''; ende:=false;
with term do
begin
if (fdmaster>=0) then
begin
if ridx<=0 then begin rlgt:=fpread(fdmaster,@si,Terminal_MaxBuf); ridx:=0; end;
if rawmode then
begin
for i := 1 to rlgt do str:=str+si[i];
ridx:=0;
end
else
begin
while (ridx<rlgt) and (not ende) do
begin
inc(ridx);
if (si[ridx]=LF) then ende:=true
else if (si[ridx]<>CR) then str:=str+si[ridx];
end;
if ridx>=rlgt then ridx:=0;
end;
end;
end; // with
TermIO_Read:=str;
end;
procedure TermIO_Write(var term:Terminal_device_t; str:string);
begin
with term do
begin
if (fdmaster>=0) then
begin
if fpwrite(fdmaster,str[1],length(str))<0 then
LOG_Writeln(LOG_ERROR,'TermIO_Write: '+LNX_ErrDesc(fpgeterrno));
end;
end;
end;
procedure DoActionOnReceivedInput(s:string);
// just for Demo. Process can react on InputCommands, written to our device /dev/testbidir
begin writeln('Received: ',s); end;
procedure Test_BiDirectionDevice_in_UserSpace; // write and read from /dev/testbidir
const maxloops=100;
var termio:Terminal_device_t; loop:longint; str:string;
begin
loop:=1;
with termio do
begin
writeln('Start of Test_BiDirectionDevice_in_UserSpace, do ',maxloops:0,' loops (user root)');
if Term_ptmx(termio,'/dev/testbidir',0,ECHO) then
begin
fpclose(fdslave);
writeln('Screen1: pls. open 2 additional terminal sessions (e.g. with putty to your pi user:root)');
writeln('filedescriptor master: ',fdmaster,' fdslave: ',fdslave);
writeln('masterpath: ',masterpath);
writeln('slavepath: ',slavepath);
writeln('linkpath: ',linkpath,' linked to ',slavepath);
writeln('do a cat ',linkpath,' on screen2, to see data which was written to master device');
writeln('do a echo xxxxx >> ',linkpath,' on screen3 to pass data which the master can read');
sleep(5000);
writeln('Start to write Hello#<nr> to master device');
repeat
str:=TermIO_Read(termio,false); // async read from master device
if str<>'' then DoActionOnReceivedInput(str); // process input data, if something was red
TermIO_Write(termio,'Hello#'+Num2Str(loop,0)+LF); // write to master device
sleep(1000); inc(loop);
until (loop>maxloops);
writeln('closing '+linkpath);
fpclose(fdmaster);
writeln('End of Test_BiDirectionDevice_in_UserSpace (you should get an Input/output error on screen2 now)');
end else writeln('ptmx init failed');
end;
end;
{$ENDIF}
function TempLVLset(Temp,Tmax:real):t_ERRORLevel;
var lvl:t_ERRORLevel;
begin
lvl:=LOG_NONE;
if not (IsNaN(Temp) or IsNaN(Tmax)) then
begin
lvl:=LOG_INFO;
if (Temp<=Tmax*RPI_CTempCool_c) then lvl:=LOG_NOTICE;
if (Temp>=Tmax*RPI_CTempWarn_c) then lvl:=LOG_WARNING;
if (Temp>=Tmax*RPI_CTempHot_c) then lvl:=LOG_ERROR;
if (Temp>=Tmax) then lvl:=LOG_URGENT;
end;
TempLVLset:=lvl;
end;
function RPI_Temp(logmsg:boolean; var TempStruct:RPI_Temps_t):t_ERRORLevel;
var n:longint; tag:longword; sh,_unit:string; p:array[0..1] of longword;
begin
with RPI_Temps do
begin
// EnterCriticalSection(Temp_CS);
TempMaxLvl:=LOG_NONE; _unit:=TempUnit[1]; //TempIdx:=1; //TempInfo:='';
if RPI_FW_property(TAG_STATUS_REQUEST,TAG_GET_MAX_TEMPERATURE,addr(p),sizeof(p))>0
then TempMax:=p[1]/1000 else TempMax:=RPI_TempAlarmCelsius_c;
TempCOOL:=TempMax*RPI_CTempCool_c;
TempWARN:=TempMax*RPI_CTempWarn_c;
TempHOT:= TempMax*RPI_CTempHot_c;
for n:= 1 to 2 do
begin
Temp[n]:=NaN; TempLvl[n]:=LOG_NONE;
tag:=TAG_GET_TEMPERATURE; sh:='CPU';
case n of
2: begin sh:='GPU'; tag:=TAG_GET_TEMPERATURE; end; // missing fw tag for GPU temp
end; // case
if RPI_FW_property(TAG_STATUS_REQUEST,tag,addr(p),sizeof(p))>0 then
begin
Temp[n]:=p[1]/1000;
if (Temp[n]>TempMaxObserved) then
begin
TempMaxObserved:=Temp[n];
TempMaxObsTS:=now;
end;
if (Temp[n]<TempMinObserved) then
begin
TempMinObserved:=Temp[n];
TempMinObsTS:=now;
end;
TempLvl[n]:=TempLVLset(Temp[n],TempMax);
if TempLvl[n]>=TempMaxLvl then TempMaxLvl:=TempLvl[n];
// TempInfo:=TempInfo+sh+':'+Num2Str((Temp[n]),0,1)+_unit+';';
if logmsg and (TempMaxLvl>=LOG_WARNING) then
SAY(TempLvl[n],'RPI_TempAlarm['+sh+']: '+Num2Str(Temp[n],0,1)+_unit+' (AlarmTemp: '+Num2Str(TempWARN,0,1)+_unit+')');
end; // else TempInfo:=TempInfo+sh+':--.-'+_unit+';';
end; // for
if not TempMaxObservedNEW then TempMaxObservedNEW:=(TempMaxObserved>TempStruct.TempMaxObserved);
if not TempMinObservedNEW then TempMinObservedNEW:=(TempMinObserved<TempStruct.TempMinObserved);
(* TempInfo:=TempInfo+'COOL:'+ Num2Str(TempCOOL, 0,1)+_unit+';';
TempInfo:=TempInfo+'WARN:'+ Num2Str(TempWARN, 0,1)+_unit+';';
TempInfo:=TempInfo+'HOT:'+ Num2Str(TempHOT, 0,1)+_unit+';';
TempInfo:=TempInfo+'ALARM:'+Num2Str(TempMax, 0,1)+_unit;*)
// TempInfo: CPU:41.8'C;GPU:--.-'C;COOL:40.0'C;WARN:65.0'C;HOT:75.0'C;ALARM:85.0'C // temps in celsius
LastUpdate:=now;
TempStruct:=RPI_Temps;
RPI_Temp:=TempMaxLvl;
// LeaveCriticalSection(Temp_CS);
end; // with
end;
procedure RPI_TempMaxValuesCheckIn(var TempStruct:RPI_Temps_t; Tmin,Tmax:real; TminTS,TmaxTS:TDateTime);
begin
with TempStruct do
begin
TempMinObserved:=Tmin; TempMinObsTS:=TminTS;
TempMaxObserved:=Tmax; TempMaxObsTS:=TmaxTS;
end; // with
end;
procedure RPI_TempInit(var TempStruct:RPI_Temps_t);
var i:longint;
begin
with TempStruct do
begin
// InitCriticalSection(Temp_CS); TempInfo:=''; TempIdx:=1;
TempMax:=RPI_TempAlarmCelsius_c; LastUpdate:=0;
TempCOOL:=TempMax; TempWARN:=TempMax; TempHOT:=TempMax;
TempMaxObservedNEW:=false; TempMinObservedNEW:=false;
for i:= 1 to 2 do
begin
Temp[i]:=0; TempLvl[i]:=LOG_NONE; TempUnit[i]:='''C';
end;
TempUnit[2]:='&#x2103;';
RPI_TempMaxValuesCheckIn(TempStruct,TempMax,0,LastUpdate,LastUpdate);
end; // with
end;
procedure RPI_TempSaveLimits(var TempStruct:RPI_Temps_t; section:string);
begin
with TempStruct do
begin
if TempMinObservedNEW then
begin
BIOS_SetIniString(section,'CPUtempMINobserved',Num2Str(TempMINObserved,0,2)+','+GetXMLTimeStamp(TempMinObsTS)+','+Select_Item(rpi_rev,';','',3));
TempMinObservedNEW:=false;
end;
if TempMaxObservedNEW then
begin
BIOS_SetIniString(section,'CPUtempMAXobserved',Num2Str(TempMaxObserved,0,2)+','+GetXMLTimeStamp(TempMaxObsTS)+','+Select_Item(rpi_rev,';','',3));
TempMaxObservedNEW:=false;
// BIOS_CacheUpdate(false);
end;
end; // with
end;
procedure RPI_TempLoadLimits(var TempStruct:RPI_Temps_t; section:string);
var _tmpStruct:RPI_Temps_t; sh:string;
begin
RPI_TempInit(_tmpStruct);
with _tmpStruct do
begin
// CPUtempMAXobserved=68.00,2019-07-25T15:13:59.177+00:00,4B
sh:=BIOS_GetIniString(section,'CPUtempMAXobserved','',[]);
if (Select_Item(sh,',','',3)=Select_Item(rpi_rev,';','',3)) then
begin
if not Str2Num(Select_Item(sh,',','',1),TempMaxObserved)
then TempMaxObserved:=0;
if not GetUTCDateTimefromXMLTimeStamp(Select_Item(sh,',','',2),TempMaxObsTS)
then TempMaxObsTS:=0;
SAY(LOG_INFO,'TempMax:'+sh+':'+Num2Str(TempMaxObserved,0,2)+'@'+GetXMLTimeStamp(TempMaxObsTS));
end
else
begin // not same cpu
TempMaxObserved:=0; TempMaxObsTS:=0;
LOG_Writeln(LOG_WARNING,'TempMax:'+sh+':'+Num2Str(TempMaxObserved,0,2)+'@'+GetXMLTimeStamp(TempMaxObsTS));
end;
// CPUtempMINobserved=45.00,2019-07-25T13:13:59.177+00:00,4B
sh:=BIOS_GetIniString(section,'CPUtempMINobserved','',[]);
if (Select_Item(sh,',','',3)=Select_Item(rpi_rev,';','',3)) then
begin
if not Str2Num(Select_Item(sh,',','',1),TempMinObserved)
then TempMinObserved:=RPI_TempAlarmCelsius_c;
if not GetUTCDateTimefromXMLTimeStamp(Select_Item(sh,',','',2),TempMinObsTS)
then TempMinObsTS:=0;
SAY(LOG_INFO,'TempMin:'+sh+':'+Num2Str(TempMinObserved,0,2)+'@'+GetXMLTimeStamp(TempMinObsTS));
end
else
begin // not same cpu
TempMinObserved:=RPI_TempAlarmCelsius_c; TempMinObsTS:=0;
LOG_Writeln(LOG_WARNING,'TempMin:'+sh+':'+Num2Str(TempMinObserved,0,2)+'@'+GetXMLTimeStamp(TempMinObsTS));
end;
RPI_TempMaxValuesCheckIn(TempStruct,TempMinObserved,TempMaxObserved,TempMinObsTS,TempMaxObsTS);
end; // with
end;
function RPI_Volt:string; // core:1.2000V;sdram_c:1.2000V;sdram_i:1.2000V;sdram_p:1.2250V
const volt_c='for src in core sdram_c sdram_i sdram_p ; do echo "$src:$(vcgencmd measure_volts $src|awk -F ''='' ''{print $2}'')" ; done';
var _ts:TStringlist; i:longint; sh:string;
begin
_ts:=TStringList.create; sh:='';
call_external_prog(LOG_NONE,volt_c,_ts);
for i:= 1 to _ts.count do
begin
sh:=sh+_ts[i-1];
if i<_ts.count then sh:=sh+';';
end;
_ts.free;
RPI_Volt:=sh;
end;
function RPI_FREQs:string; // arm:600000000;core:250000000;h264:250000000;isp:250000000;...
const frq_c= 'for src in arm core h264 isp v3d uart pwm emmc pixel vec hdmi dpi ; do echo "$src:$(vcgencmd measure_clock $src|awk -F ''='' ''{print $2}'')" ; done';
var _ts:TStringlist; i:longint; sh:string;
begin
_ts:=TStringList.create; sh:='';
call_external_prog(LOG_NONE,frq_c,_ts);
for i:= 1 to _ts.count do
begin
sh:=sh+_ts[i-1];
if i<_ts.count then sh:=sh+';';
end;
_ts.free;
RPI_FREQs:=sh;
end;
function RPI_ThrottleDesc:string;
// vcgencmd get_throttled
var sh:string;
begin
sh:='';
if BIT_Get(RPI_Throttle, 0) then sh:=sh+'under-voltage;';
if BIT_Get(RPI_Throttle, 1) then sh:=sh+'arm frequency capped;';
if BIT_Get(RPI_Throttle, 2) then sh:=sh+'currently throttled;';
if BIT_Get(RPI_Throttle,16) then sh:=sh+'under-voltage has occurred;';
if BIT_Get(RPI_Throttle,17) then sh:=sh+'arm frequency capped has occurred;';
if BIT_Get(RPI_Throttle,18) then sh:=sh+'throttling has occurred;';
RPI_ThrottleDesc:=CSV_RemLastSep(sh,';');
end;
procedure RPI_ThrottleGet;
var val:longword; sh:string;
begin
if (call_external_prog(LOG_NONE,'vcgencmd get_throttled',sh)=0) then
begin // e.g. throttled=0x50005
if not Str2Num(Select_Item(RM_CRLF(sh),'throttled=','',2),val) then val:=0;
end;
RPI_Throttle:=val;
end;
function RPI_GPU_MEM_BASE:longword; begin RPI_GPU_MEM_BASE:=GPU_MEM_BASE; end;
function RPI_INFO_Split(info:string; var labl,valu:string):boolean;
begin // in: CPU:41.8'C out: labl:CPU value:41.8'C
labl:=Trimme(Select_Item(info,':','',1),3);
valu:=Trimme(Select_Item(info,':','',2),3);
RPI_INFO_Split:=((labl<>'') and (valu<>''));
end;
procedure Get_SDcard_RDSpeed;
var cmd,sh:string;
begin
//Timing buffered disk reads: 128 MB in 3.01 seconds = 42.47 MB/sec
cmd:='hdparm -t /dev/mmcblk0 2>/dev/null | grep -i "timing" | awk -F ''='' ''{print $2}''';
call_external_prog(LOG_NONE,cmd,sh);
sh:=Trimme(sh,3); // 42.47 MB/sec
if (sh<>'') then SD_speedRD:=sh;
end;
procedure Get_CPU_INFO_Init;
// https://en.wikipedia.org/wiki/Raspberry_Pi
const proc1_c='cat /proc/cpuinfo'; proc2_c='cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo';
proc3_c='cat /etc/debian_version';
var ts:TStringlist; sh:string; anz:longint; lw:longword;
function cpuinfo_unix(infoline:string; var cnt:longint):string;
var s:string; i:integer;
begin
s:=''; i:=1; cnt:=0;
while (i<=ts.count) do
begin
if Pos(Upper(infoline),Upper(ts[i-1]))=1 then
begin
s:=ts[i-1]; inc(cnt); // i:=ts.count+1;
end;
inc(i);
end;
cpuinfo_unix:=copy(s,Pos(':',s)+2,Length(s));
end;
function getvcgencmd(opt:string; var val:real):boolean;
var _ok:boolean;
begin
ts.clear; _ok:=false;
if (call_external_prog(LOG_NONE,'vcgencmd measure_clock '+opt,sh)=0) then
begin
sh:=RM_CRLF(sh);
if sh<>'' then if Str2Num(copy(sh,Pos(')=',sh)+2,Length(sh)),val) then _ok:=true;
end;
getvcgencmd:=_ok;
end;
function RPI_SetInfo(cpurevs,desc,manuf:string; cpurev:real; I2Cbusnr,gpioidx,slednr,pincnt,cores:byte;memsizMB:word):string;
// RPI_SetInfo('0010', 'B', 'Sony UK', 1.0, 0, 2, 47, 40, 1, 512);
begin
connector_pin_count:=pincnt; cpu_rev_num:=cpurev; I2C_busnum:=I2Cbusnr;
GPIO_map_idx:=gpioidx; status_led_GPIO:=slednr;
RPI_SetInfo:= 'rev'+Num2Str(cpurev,0,1)+';'+
Num2Str(memsizMB,0)+'MB;'+
desc+';'+cpu_hw+';'+cpurevs+';'+
Num2Str(connector_pin_count,0)+';'+
Num2Str(cores,0)+';'+
cpu_machine+';'+
manuf; // rev1.0;512MB;B;BCM2709;0010;40;1;Sony UK
end;
function AnalyzeRevCode(cpurevs:string):string;
// https://www.raspberrypi.org/documentation/hardware/raspberrypi/revision-codes/README.md
var F,M,C,P,R:byte; sh:string;
begin
sh:='';
if Str2Num('0x'+cpurevs,lw) then
begin
F:=((lw and $00800000) shr 23); // New flag 1Bit
if (F=0) then
begin // 0: old style
case (lw and $ff) of
$00: sh:=RPI_SetInfo(cpurevs,'B', '', 1.0, 0, 1, 16, 26, 1, 256);
$01: sh:=RPI_SetInfo(cpurevs,'B', '', 1.0, 0, 1, 16, 26, 1, 256);
$02: sh:=RPI_SetInfo(cpurevs,'B', 'Egoman', 1.0, 0, 1, 16, 26, 1, 256);
$03: sh:=RPI_SetInfo(cpurevs,'B', 'Egoman', 1.0, 0, 1, 16, 26, 1, 256);
$04: sh:=RPI_SetInfo(cpurevs,'B', 'Sony UK', 2.0, 1, 2, 16, 26, 1, 256);
$05: sh:=RPI_SetInfo(cpurevs,'B', 'Qisda', 2.0, 1, 2, 16, 26, 1, 256);
$06: sh:=RPI_SetInfo(cpurevs,'B', 'Egoman', 2.0, 1, 2, 16, 26, 1, 256);
$07: sh:=RPI_SetInfo(cpurevs,'A', 'Egoman', 2.0, 1, 2, 16, 26, 1, 256);
$08: sh:=RPI_SetInfo(cpurevs,'A', 'Sony UK', 2.0, 1, 2, 16, 26, 1, 256);
$09: sh:=RPI_SetInfo(cpurevs,'A', 'Qisda', 2.0, 1, 2, 16, 26, 1, 256);
$0d: sh:=RPI_SetInfo(cpurevs,'A', 'Egoman', 2.0, 1, 2, 16, 26, 1, 256);
$0e: sh:=RPI_SetInfo(cpurevs,'A', 'Sony UK', 2.0, 1, 2, 16, 26, 1, 256);
$0f: sh:=RPI_SetInfo(cpurevs,'B', 'Egoman', 2.0, 1, 2, 16, 26, 1, 512);
$10: sh:=RPI_SetInfo(cpurevs,'B+', 'Sony UK', 1.0, 0, 2, 47, 40, 1, 512);
$11: sh:=RPI_SetInfo(cpurevs,'CM1','Sony UK', 1.1, 0, 2, 47, 0, 1, 512);
$12: sh:=RPI_SetInfo(cpurevs,'A+', 'Sony UK', 1.1, 0, 2, 47, 40, 1, 256);
$13: sh:=RPI_SetInfo(cpurevs,'B+', 'Embest', 1.2, 0, 2, 47, 40, 1, 512);
$14: sh:=RPI_SetInfo(cpurevs,'CM1','Embest', 1.1, 0, 2, 47, 0, 1, 512);
$15: sh:=RPI_SetInfo(cpurevs,'A+', 'Embest', 1.1, 0, 2, 47, 40, 1, 256);
else LOG_Writeln(LOG_ERROR,'Get_CPU_INFO_Init: (0x'+HexStr(lw,8)+') unknown rev:'+cpurevs+': RPI not supported');
end; // case
end
else
begin // 1: new style flag
connector_pin_count:=40; GPIO_map_idx:=2; cpu_hw:='';
status_led_GPIO:=47; I2C_busnum:=1;
// uuuuuuuuFMMMCCCCPPPPTTTTTTTTRRRR
R:= ((lw and $0000000f)); // Revision 4Bit
RPI_bType:= ((lw and $00000ff0) shr 4); // Type 8Bit
P:= ((lw and $0000f000) shr 12); // Processor 4Bit
C:= ((lw and $000f0000) shr 16); // Manufacturer 4Bit
M:= ((lw and $00700000) shr 20); // Memory size 3Bit
// u:= ((lw and $ff000000) shr 24); // Unused 8Bit
// writeln(cpurevs,' F:',F,' R:',R,' T:',T,' P:',P,' C:',C,' M:',M);
// a020d3 F:1 R:3 T:13 P:2 C:0 M:2
// rev1.3;1GB;3B+;BCM2837;a020d3;40;4;Sony UK
sh:=sh+'rev1.'+Num2Str(R,0)+';';
case M of // Memory size
0: sh:=sh+'256MB';
1: sh:=sh+'512MB';
2: sh:=sh+'1GB';
3: sh:=sh+'2GB';
4: sh:=sh+'4GB';
else sh:=sh+'0x'+HexStr(M,2);
end; // case
sh:=sh+';';
case RPI_bType of // Type
0: sh:=sh+'A';
1: sh:=sh+'B';
2: sh:=sh+'A+';
3: sh:=sh+'B+';
4: sh:=sh+'2B';
5: sh:=sh+'Alpha (early prototype)';
6: sh:=sh+'CM1';
8: sh:=sh+'3B';
9: sh:=sh+'Zero';
$0a: sh:=sh+'CM3';
$0c: sh:=sh+'Zero W';
$0d: sh:=sh+'3B+';
$0e: sh:=sh+'3A+';
$10: sh:=sh+'CM3+';
$11: sh:=sh+'4B';
else sh:=sh+'0x'+HexStr(RPI_bType,2);
end; // case
sh:=sh+';';
case P of // Processor
0: cpu_hw:='BCM2835';
1: cpu_hw:='BCM2836';
2: cpu_hw:='BCM2837';
3: cpu_hw:='BCM2711';
else cpu_hw:=cpu_hw+'0x'+HexStr(P,2);
end; // case
sh:=sh+cpu_hw+';'+cpurevs+';'+Num2Str(connector_pin_count,0)+';'+Num2Str(cpu_cores,0)+';'+cpu_machine+';';
case C of // Manufacturer
0: sh:=sh+'Sony UK';
1: sh:=sh+'Egoman';
2: sh:=sh+'Embest';
3: sh:=sh+'Sony Japan';
4: sh:=sh+'Embest';
5: sh:=sh+'Stadium';
else sh:=sh+'0x'+HexStr(C,2);
end; // case
end;
end; // else Log_Writeln(LOG_ERROR,'Get_CPU_INFO_Init: Rev:'+cpurevs+' Hardware:'+cpu_hw+' Processor:'+cpu_proc+' no known platform');
AnalyzeRevCode:=sh;
end;
begin
cpu_snr:=''; cpu_hw:=''; cpu_proc:=''; cpu_rev:=''; cpu_mips:=''; cpu_feat:=''; cpu_rev_num:=0;
cpu_fmin:=''; cpu_fcur:=''; cpu_fmax:=''; os_rev:=''; uname:=''; cpu_machine:='';
cpu_cores:=0; I2C_busnum:=0; status_led_GPIO:=0; whoami:='';
RPI_bType:=0; RPI_ThrottleGet;
for lw:=1 to max_pins_c do RPIHDR_Desc[lw]:='';
connector_pin_count:=40;
cpu_freq:= 700000000; pll_freq:=2000000000;
{$IFDEF UNIX}
call_external_prog(LOG_NONE,'whoami',whoami);
ts:=TStringList.Create;
call_external_prog(LOG_NONE,proc3_c,sh); os_rev:= RM_CRLF(sh);
call_external_prog(LOG_NONE,proc2_c+'_min_freq',sh); cpu_fmin:= RM_CRLF(sh);
call_external_prog(LOG_NONE,proc2_c+'_cur_freq',sh); cpu_fcur:= RM_CRLF(sh);
call_external_prog(LOG_NONE,proc2_c+'_max_freq',sh); cpu_fmax:= RM_CRLF(sh);
call_external_prog(LOG_NONE,'uname -srvmo', sh); uname:= RM_CRLF(sh);
call_external_prog(LOG_NONE,'uname -m', sh); cpu_machine:= RM_CRLF(sh);
if not getvcgencmd('arm', cpu_freq) then cpu_freq:= 700000000;
lw:=round(2*pllc_freq_c/1000000);
pll_freq:=floor(2400 div lw)*lw*1000000; if pll_freq>0 then ;
// writeln('CPU Freq: ',cpu_fmin,' ',cpu_fcur,' ',cpu_fmax,' ',cpu_freq,' ',pllc_freq_c,' ',pll_freq);
if call_external_prog(LOG_NONE,proc1_c,ts)=0 then
begin
I2C_busnum:=1; status_led_GPIO:=47;
cpu_rev_num:=0; GPIO_map_idx:=2;
cpu_snr:= cpuinfo_unix('Serial', anz); // e.g. 0000...
cpu_hw:= cpuinfo_unix('Hardware', anz); // e.g. BCM2709
cpu_proc:=cpuinfo_unix('Processor',cpu_cores);
cpu_mips:=cpuinfo_unix('BogoMIPS', anz);
cpu_feat:=cpuinfo_unix('Features', anz);
cpu_rev:= cpuinfo_unix('Revision', anz); // e.g. a01041
cpu_rev:= AnalyzeRevCode(cpu_rev); // new style
// writeln(cpu_rev);
// writeln(cpu_rev_num);
end;
ts.free;
{$ENDIF}
end;
function Bin(q:longword;lgt:Byte) : string;
{.c shows q in binary representation: bbbb bbbb ... }
var h : string; i : Byte;
begin
h:='';
for i := (lgt-1) downto 0 do
begin
if ((q and (1 shl i))>0) then h:=h+'1' else h:=h+'0';
if ((i mod 4)=0) and (i>0) then h:=h+' ';
end;
Bin:=h;
end; { Bin }
function RPI_GetBuildDateTimeString:string;
var sh:string;
begin
sh:=StringReplace(prog_build_date,'/','-',[rfReplaceAll,rfIgnoreCase]);
sh:=sh+'T'+prog_build_time;
RPI_GetBuildDateTimeString:=sh;
end;
procedure RPI_HDR_SetDesc(HWPin:longint; desc:string);
begin if (HWPin>=1) and (HWPin<=max_pins_c) then RPIHDR_Desc[HWPin]:=copy(desc,1,mdl); end;
function RPI_mmap_get_info (modus:longint) : longword;
// https://github.com/raspberrypi/userland/blob/master/host_applications/linux/libs/bcm_host/bcm_host.c
var valu:longword; li,ofs:longint; sh:string;
begin
valu:=0;
case modus of
1,2 : valu:=PAGE_SIZE;
30 : begin // OLD get peri base from device tree
// e.g. for ZeroW: 7e0000002000000002000000...
// 3B+: 7e0000003f00000001000000400000004000000000001000
// 4B: 7e00000000000000fe000000018000007c00000000000000fc000000020000004000000000000000ff80000000800000
call_external_prog(LOG_NONE,'xxd -ps -c250 '+PrepFilePath(LNX_DevTree+'/soc/ranges'),sh);
ofs:=8;
if (Upper(RPI_hw)='BCM2711') then ofs:=16; // rpi4
if not Str2Num('$'+copy(sh,ofs+1,8),valu) then valu:=0; // $20000000
if (valu=0) then
begin // old variant
valu:=BCM2709_PBASE; // for BCM2709 and BCM2835
if (Upper(RPI_hw)='BCM2708') then valu:=BCM2708_PBASE; // for old RPI
if (Upper(RPI_hw)='BCM2711') then valu:=BCM2711_PBASE; // rpi4
end;
end;
3 : begin // NEW get peri base with iomem
(* cat /proc/iomem | grep gpio@
rpi4: fe200000-fe2000b3 : gpio@7e200000
rpi2-3x: 3f200000-3f2000b3 : gpio@7e200000
zerow: 20200000-202000b3 : gpio@7e200000 *)
call_external_prog(LOG_NONE,'cat /proc/iomem | grep gpio@',sh); // 3f200000-3f2000b3 : gpio@7e200000
sh:=Select_Item(Trimme(sh,3),'-','',1); // 3f200000
if Str2Num('$'+sh,valu) then valu:=valu-GPIO_BASE_OFS // 3f000000
else valu:=RPI_mmap_get_info(30);
// writeln('PBase: 0x',HexStr(valu,8));
end;
4 : begin {$IFDEF UNIX} valu:=1; {$ELSE} valu:=0; {$ENDIF} end; (* if run_on_unix ->1 else 0 *)
5 : if (Upper({$i %FPCTARGETCPU%})='ARM') then valu:=1 else valu:=0; (* if run_on_ARM ->1 else 0 *)
6 : begin valu:=1; end; // if RPI_Piggyback_board_available -> 1 dummy, for future use
7 : if ((RPI_mmap_get_info(5)=1) and
((Upper(RPI_hw)='BCM2708') or
(Upper(RPI_hw)='BCM2835') or // new in Linux raspberrypi 4.9.11-v7+ #971 SMP Mon Feb 20 20:44:55 GMT 2017 armv7l GNU/Linux
(Upper(RPI_hw)='BCM2836') or
(Upper(RPI_hw)='BCM2837') or
(Upper(RPI_hw)='BCM2711') or // rpi4
(Upper(RPI_hw)='BCM2709'))) then valu:=1; // runs on known rpi HW
8 : begin valu:=1; end; // if PiFaceBoard_board_available -> 1 dummy, for future use *)
9 : begin
call_external_prog(LOG_NONE,'uname -v',sh); // e.g. #970 SMP Mon Feb 20 19:18:29 GMT 2017
sh:=Select_Item(sh,' ','',1); // #970
sh:=GetNumChar(sh); // 970
if not Str2Num(sh,li) then li:=-1; // dummy, works with kernel above 4.4.50
if (li<supminkrnl) or (li>supmaxkrnl) then valu:=1 else valu:=1; // dummy, supported min./max. kernel version 4.0.5 - 4.4.50
end;
end;
RPI_mmap_get_info:=valu;
end;
function RPI_BCM2835:boolean; begin RPI_BCM2835:=(Upper(RPI_hw)='BCM2835'); end;
function RPI_BCM2835_GetNodeValue(node:string; var nodereturn:string):longint;
var res:longint;
begin
res:=-1;
if RPI_BCM2835 then
begin
call_external_prog(LOG_NONE,'xxd -ps '+node,nodereturn);
if not Str2Num('$'+GetHexChar(nodereturn),res) then res:=-1;
// nodereturn:=StrHexStr(nodereturn); // if return is ASCII text
end;
RPI_BCM2835_GetNodeValue:=res;
end;
function RPI_FW_open:longint;
begin
with rpi_fw_api do
begin
if (hndl=-1) then
begin
hndl:=fpopen(rpi_fw_dev, O_NONBLOCK);
if (hndl=-1) then LOG_Writeln(LOG_ERROR,'RPI_FW_open: can not open '+rpi_fw_dev);
end;
RPI_FW_open:=hndl;
end; // with
end;
procedure RPI_FW_close;
begin
with rpi_fw_api do
begin
if (hndl<>-1) then
if (fpclose(hndl)=-1) then LOG_Writeln(LOG_ERROR,'RPI_FW_close: can not close '+rpi_fw_dev);
end; // with
end;
function RPI_FW_property(req,tag:longword; tag_data:pointer; buf_size:byte):longint;
// https://github.com/6by9/rpi3-gpiovirtbuf
var res:longint; p:array[0..((256 div 4)+6)] of longword; //n:longint;
begin
res:=-1;
if (rpi_fw_api.hndl<>-1) then
begin
p[0]:=(5+1 + (buf_size div 4)) * sizeof(tag);
p[1]:=req; // TAG_STATUS_REQUEST
p[2]:=tag; // tag
p[3]:=buf_size; // buf_size
p[4]:=0; // req_resp_size
Move(tag_data^,p[5],buf_size); // Move(src^, dest^, size);
p[5+(buf_size div 4)]:=TAG_PROPERTY_END;
// for n:=0 to (5+(buf_size div 4)) do writeln(n:2,'. ',HexStr(p[n],8)); writeln;
{$RANGECHECKS OFF}
if (fpioctl(rpi_fw_api.hndl,IOCTL_TAG_PROPERTY,addr(p[0]))<>-1) then
begin
if (p[1]=TAG_STATUS_SUCCESS) then
begin
// for n:=0 to (5+(buf_size div 4)) do writeln(n:2,'. ',HexStr(p[n],8));
Move(p[5],tag_data^,buf_size);
res:=p[4] and $ff;
end else LOG_Writeln(LOG_ERROR,'RPI_FW_property: firmware returned 0x'+HexStr(p[1],8));
end else LOG_Writeln(LOG_ERROR,'RPI_FW_property: ioctl: IOCTL_TAG_PROPERTY: '+LNX_ErrDesc(fpgeterrno));
{$RANGECHECKS ON}
end; // else LOG_Writeln(LOG_ERROR,'RPI_FW_property['+HexStr(req,2)+'/0x'+HexStr(tag,8)+']: device not opened '+rpi_fw_dev+' use InitRPIfw flag at RPI_HW_Start');
RPI_FW_property:=res;
end;
function MACpretty(macstr:string):string;
var n:longint; sh,MAChexStr:string;
begin
sh:=''; MAChexStr:=StrHex(macstr);
for n:=1 to Length(MAChexStr) do sh:=sh+HexStr(ord(MAChexStr[n]),2)+':';
MACpretty:=CSV_RemLastSep(sh,':');
end;
function RPI_FW_Info(req,tag:longword; var FWinfo:string):boolean;
const mm=50;
var _ok:boolean; n,bcnt,wcnt:longint; p:array[0..mm] of longword;
begin
_ok:=false;
bcnt:=RPI_FW_property(req,tag,addr(p),sizeof(p));
_ok:=(bcnt>0);
wcnt:=(bcnt div 4); bcnt:=(bcnt mod 4);
if _ok then
begin
case tag of
TAG_GET_BOARD_MAC_ADDRESS:
begin
p[0]:= swap(Hi(p[0])) or (swap(Lo(p[0])) shl 16);
p[1]:= swap(Lo(p[1]));
FWinfo:=MACpretty(HexStr(p[0],8)+copy(HexStr(p[1],8),8+1-bcnt*2,bcnt*2));
end;
TAG_GET_FIRMWARE_REVISION:
begin
FWinfo:=FormatDateTime('YYYY-MM-DD"T"hh:mm:ss',UnixToDateTime(p[0]));
end;
TAG_GET_CLOCK_RATE:
begin
FWInfo:='ClockID 0x'+HexStr(p[0],8)+' @ '+Num2Str(p[1],0)+'Hz';
end;
else begin
FWinfo:='';
if bcnt>0 then FWinfo:=FWinfo+copy(HexStr(p[wcnt],8),8+1-bcnt*2,bcnt*2);
for n:=wcnt downto 1 do FWinfo:=FWinfo+HexStr(p[n-1],8);
end;
end; // case
end;
RPI_FW_Info:=_ok;
end;
procedure RPI_FW_test;
const mm=50;
var i:longint; p:array[0..mm] of longword; lw:longword; info:string; // dt1,dt2,dt3:TDateTime; sh:string;
procedure ShowArr(msg:string; cnt:longint);
var _n,_cnt:longint;
begin
if cnt>0 then
begin
writeln(msg+'(',cnt,'byte):');
_cnt:=(cnt div 4); if (cnt mod 4)>0 then inc(_cnt);
for _n:=1 to _cnt do writeln(_n:4,'. 0x',HexStr(p[_n-1],8));
end;
end;
begin
RPI_FW_open; // no need, if rpi_hal was init with InitRPIfw flag
i:=RPI_FW_property(TAG_STATUS_REQUEST,TAG_GET_BOARD_REVISION,addr(p),sizeof(p)); ShowArr('rev',i);
if RPI_FW_Info(TAG_STATUS_REQUEST,TAG_GET_BOARD_REVISION,info) then writeln(info); writeln;
i:=RPI_FW_property(TAG_STATUS_REQUEST,TAG_GET_BOARD_SERIAL,addr(p),sizeof(p)); ShowArr('snr',i);
if RPI_FW_Info(TAG_STATUS_REQUEST,TAG_GET_BOARD_SERIAL,info) then writeln(info); writeln;
i:=RPI_FW_property(TAG_STATUS_REQUEST,TAG_GET_BOARD_MAC_ADDRESS,addr(p),sizeof(p)); ShowArr('MAC',i);
if RPI_FW_Info(TAG_STATUS_REQUEST,TAG_GET_BOARD_MAC_ADDRESS,info) then writeln(info);writeln;
i:=RPI_FW_property(TAG_STATUS_REQUEST,TAG_GET_FIRMWARE_REVISION,addr(lw),sizeof(lw));ShowArr('fw',i);
if RPI_FW_Info(TAG_STATUS_REQUEST,TAG_GET_FIRMWARE_REVISION,info) then writeln(info);writeln;
p[0]:=$3; // get ARM clock
i:=RPI_FW_property(TAG_STATUS_REQUEST,TAG_GET_CLOCK_RATE,addr(lw),sizeof(lw));ShowArr('ClockARM',i);
if RPI_FW_Info(TAG_STATUS_REQUEST,TAG_GET_CLOCK_RATE,info) then writeln(info);writeln;
if RPI_FW_property(TAG_STATUS_REQUEST,TAG_GET_TEMPERATURE,addr(p),8)>0
then writeln('temp: 0x',HexStr(p[1],8),' ',(p[1]/1000):5:2,' celsius');
if RPI_FW_property(TAG_STATUS_REQUEST,TAG_GET_MAX_TEMPERATURE,addr(p),8)>0
then writeln('tmax: 0x',HexStr(p[1],8),' ',(p[1]/1000):5:2,' celsius');
if RPI_FW_property(TAG_STATUS_REQUEST,TAG_GET_VC_MEMORY,addr(p),8)>0
then writeln('VCmem: 0x',HexStr(p[1],8),' ',p[1]:10,' Bytes @ 0x'+HexStr(p[0],8));
if RPI_FW_property(TAG_STATUS_REQUEST,TAG_GET_ARM_MEMORY,addr(p),8)>0
then writeln('ARMmem: 0x',HexStr(p[1],8),' ',p[1]:10,' Bytes @ 0x'+HexStr(p[0],8));
p[0]:=$3; // get ARM clock
i:=RPI_FW_property(TAG_STATUS_REQUEST,TAG_GET_CLOCK_RATE,addr(p),sizeof(p));
if i>0 then begin ShowArr('ClockArm',i); writeln(p[1],'Hz'); writeln; end;
p[0]:=$4; // get Core clock
i:=RPI_FW_property(TAG_STATUS_REQUEST,TAG_GET_CLOCK_RATE,addr(p),sizeof(p));
if i>0 then begin ShowArr('ClockCore',i); writeln(p[1],'Hz'); writeln; end;
p[0]:=$3; // get config of gpio 3
i:=RPI_FW_property(TAG_STATUS_REQUEST,TAG_GET_GPIO_CONFIG,addr(p),sizeof(p)); ShowArr('GPIO3 config',i); writeln;
//i:=RPI_FW_property(TAG_STATUS_REQUEST,TAG_GET_CLOCKS,addr(p),sizeof(p)); ShowArr('clocks',i); writeln;
(*dt1:=now; // speed testing
writeln(GetXMLTimeStamp(dt1));
for lw:=1 to 1000 do call_external_prog(LOG_NONE,'cat '+rpi_cpu_temp_dev_c,sh); // takes 70secs
dt2:=now; writeln(GetXMLTimeStamp(dt2));
for lw:=1 to 1000 do RPI_FW_property(TAG_GET_TEMPERATURE,addr(p),8); // takes 30msecs !!
dt3:=now; writeln(GetXMLTimeStamp(dt3)); *)
//RPI_FW_close; no need to close is done automatically by exit procedure
end;
//RPI_MBX_msg_t
//#define MAILBOX ((volatile __attribute__((aligned(4))) struct MailBoxRegisters*)(uintptr_t)(RPi_IO_Base_Addr + 0xB880));
// http://www.valvers.com/open-software/raspberry-pi/step05-bare-metal-programming-in-c-pt5/
// https://github.com/vanvught/rpidmx512/blob/master/lib-bcm2835/src/bcm2835_vc.c
procedure RPI_MBX_msgshow(msgptr:RPI_MBX_msgPTR_t);
begin
with msgptr^ do
begin
writeln(' msg_size: 0x',HexStr(msg_size,8));
writeln(' request_code: 0x',HexStr(request_code,8));
// with tag do
begin
writeln(' tag_id: 0x',HexStr(tag_id,8));
writeln(' buffer_size: 0x',HexStr(buffer_size,8));
writeln(' data_size: 0x',HexStr(data_size,8));
writeln(' dev_id: 0x',HexStr(dev_id,8));
writeln(' val: 0x',HexStr(val,8));
end; // with
writeln(' end_tag: 0x',HexStr(end_tag,8));
end; // with
end;
procedure RPI_MBX_msgfill(var msg:RPI_MBX_msg_t; reqcode,tagid,bsiz,dsiz,devid,value:longword);
begin
with msg do
begin
msg_size:= sizeof(msg);
request_code:= reqcode; // BCM2837_MBOX_REQUEST_CODE = $00000000;
// with tag do
begin
tag_id:= tagid;
buffer_size:= bsiz; // ResponseLength
data_size:= dsiz; // RequestLength
dev_id:= devid;
val:= value;
end; // with
end_tag:= 0; // structure terminator
end; // with
end;
function RPI_MBX_empty:boolean;
const RPI3_MAILBOX_TIMEOUT=1000;
var _ok:boolean; lw:longword; timo:TDateTime;
begin
_ok:=true; SetTimeOut(timo,RPI3_MAILBOX_TIMEOUT);
while _ok and ((BCM_GETREG(MBX_STATUS0) and MB_EMPTY)<>MB_EMPTY) do
begin
lw:=BCM_GETREG(MBX_READ0); if lw=0 then ; // dummy
_ok:=(not TimeElapsed(timo));
delay_msec(1);
end;
RPI_MBX_empty:=_ok;
end;
function RPI_MBX_read(channel:longword):longword;
// does not work, work in progress
const RPI3_MAILBOX_TIMEOUT=1000;
var _ok:boolean; _value:longword; timo:TDateTime;
begin
_ok:=false; _value:=MB_CHANNEL_ERROR;
if (channel<=MB_CHANNEL_GPU) then
begin
_ok:=true; SetTimeOut(timo,RPI3_MAILBOX_TIMEOUT);
repeat
while ((BCM_GETREG(MBX_STATUS0) and MB_EMPTY)<>0) and _ok do
begin // wait until data is avail in MBX or timeout
_ok:=(not TimeElapsed(timo));
delay_msec(1);
end;
if _ok then _value:=BCM_GETREG(MBX_READ0);
writeln('read1: 0x',HexStr(_value,8),' ',_ok);
until ((_value and $f)=channel) or (not _ok);
if (not _ok) then
begin
LOG_Writeln(LOG_ERROR,'RPI_MBX_read['+HexStr(channel,2)+']: timeout');
_value:=MB_CHANNEL_ERROR;
end else _value:=_value and (not $f); // _value:=_value shr 4;
end else LOG_Writeln(LOG_ERROR,'RPI_MBX_read['+HexStr(channel,2)+']: wrong channel 0x'+HexStr(channel,2));
writeln('read2: 0x',HexStr(_value,8),' ',_ok);
RPI_MBX_read:=_value;
end;
function RPI_MBX_write(channel,value:longword; xxx:boolean):boolean;
const RPI3_MAILBOX_TIMEOUT=1000;
// does not work, work in progress
var _ok:boolean; timo:TDateTime;
begin
_ok:=false;
writeln('write0: value:0x',HexStr(value,8));
if (channel<=MB_CHANNEL_GPU) then
begin // GPU_MEM_BASE
// ??????????? #define BUS_ADDRESS(phys) (((phys) & ~0xC0000000) | GPU_MEM_BASE)
value:=(value and (not $0f)) or channel;
if xxx then value:=(value and (not GPU_MEM_BASE)) or GPU_MEM_BASE;
//value:=(value or $3B400000);
_ok:=true; SetTimeOut(timo,RPI3_MAILBOX_TIMEOUT);
while ((BCM_GETREG(MBX_STATUS1) and MB_FULL)<>0) and _ok do
begin // wait until MBX is empty or timeout
_ok:=(not TimeElapsed(timo));
delay_msec(1);
end;
writeln('write1: value:0x',HexStr(value,8));
if _ok then BCM_SETREG(MBX_WRITE1,value)
else LOG_Writeln(LOG_ERROR,'RPI_MBX_write['+HexStr(channel,2)+']: timeout');
end else LOG_Writeln(LOG_ERROR,'RPI_MBX_write['+HexStr(channel,2)+']: wrong channel 0x'+HexStr(channel,2));
writeln('write2: ',_ok);
RPI_MBX_write:=_ok;
end;
function RPI_MBX_Call(channel:longword; msgptr:RPI_MBX_msgPTR_t; var value:longword):boolean;
// does not work, work in progress
var _ok:boolean;
begin
_ok:=Aligned(msgptr,32);
if _ok then
begin
RPI_MBX_msgshow(@msg); writeln;
_ok:=RPI_MBX_empty;
if _ok then
begin
_ok:=RPI_MBX_write(channel,PtrUInt(msgptr),true);
if _ok then
begin
value:=RPI_MBX_read(channel);
_ok:=(value<>MB_CHANNEL_ERROR);
if not _ok then LOG_Writeln(LOG_ERROR,'RPI_MBX_Call['+HexStr(channel,2)+']: read timeout');
end else LOG_Writeln(LOG_ERROR,'RPI_MBX_Call['+HexStr(channel,2)+']: can not write');
end else LOG_Writeln(LOG_ERROR,'RPI_MBX_Call['+HexStr(channel,2)+']: not empty timeout');
end else LOG_Writeln(LOG_ERROR,'RPI_MBX_Call['+HexStr(channel,2)+']: msgptr not aligned');
RPI_MBX_Call:=_ok;
end;
function bcm2835_vc_get0408(tag,devid:longword; var value:longword):boolean;
// https://www.raspberrypi.org/forums/viewtopic.php?t=205382
// https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
// https://github.com/6by9/rpi3-gpiovirtbuf
var _ok:boolean; msg:RPI_MBX_msg_t;
begin
_ok:=false;
RPI_MBX_msgfill(msg,0,tag,8,4,devid,0);
_ok:=RPI_MBX_write(MB_CHANNEL_TAGS,PtrUInt(@msg),true); // sent the message
if _ok then
begin
RPI_MBX_read (MB_CHANNEL_TAGS); // clear the response
if (msg.request_code=MB_CHANNEL_SUCCESS) then
if (msg.dev_id=devid) then value:=msg.val else _ok:=false
else _ok:=false;
end;
bcm2835_vc_get0408:=_ok;
end;
function bcm2835_vc_get_temperature(var temp:longword):boolean;
begin bcm2835_vc_get_temperature:=bcm2835_vc_get0408(TAG_GET_TEMPERATURE,0,temp); end;
function bcm2835_vc_get_temperature_max(var temp:longword):boolean;
begin bcm2835_vc_get_temperature_max:=bcm2835_vc_get0408(TAG_GET_MAX_TEMPERATURE,0,temp); end;
(*Unique clock IDs:
0x000000000: reserved
0x000000001: EMMC
0x000000002: UART
0x000000003: ARM
0x000000004: CORE
0x000000005: V3D
0x000000006: H264
0x000000007: ISP
0x000000008: SDRAM
0x000000009: PIXEL
0x00000000a: PWM *)
function bcm2835_vc_get_clock(clockid:longword; var rateHz:longword):boolean;
begin bcm2835_vc_get_clock:=bcm2835_vc_get0408(TAG_GET_CLOCK_RATE,clockid,rateHz); end;
procedure RPI_MBX_test;
// does not work, work in progress
var lw:longword; _ok:boolean; // xmsg:RPI_MBX_msg_t;
begin
RPI_MBX_msgfill( msg,
0, // response
$00030002, // mailbox get clock rates
8, // request is 8 bytes long
8, // response expects 8 bytes back
3, // channel 0
0); // empty data field
//RPI_MBX_msgshow(@msg); writeln;
writeln('####1 0x',HexStr(addr(msg)),' ',HexStr(GPU_MEM_BASE,8));
writeln('stat0 0x',HexStr(BCM_REGAdr(MBX_STATUS0),8),' read0 0x',HexStr(BCM_REGAdr(MBX_READ0),8));
writeln('stat1 0x',HexStr(BCM_REGAdr(MBX_STATUS1),8),' write1 0x',HexStr(BCM_REGAdr(MBX_WRITE1),8));
_ok:=RPI_MBX_Call(MB_CHANNEL_TAGS,@msg,lw);
if _ok then
begin
writeln('####2 0x',HexStr(lw,8),' ',HexStr(msg.request_code,8),' ',_ok);
RPI_MBX_msgshow(@msg);
end;
if (msg.request_code=MB_CHANNEL_SUCCESS) then
begin
writeln('CPU speed: ',msg.val,' lw:0x',HexStr(lw,8));
end;
writeln;
//if bcm2835_vc_get_temperature(lw) then writeln('GPUtemp: ',lw);
//if bcm2835_vc_get_temperature_max(lw) then writeln('GPUtempm:',lw);
end;
function RPI_I2C_ChkDev(bus,adr:byte):integer;
// res=-1 not valid // res=0: adr not found // res=1: adr found // res=2: adr found, allocated by driver
var i,j,nr:integer; tl:TStringList; sh:string;
(* 0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- --
70: 70 71 72 73 -- -- -- -- *)
begin
nr:=-1;
if (bus>= 0) and (bus<= 1) and
(adr>=$03) and (adr<=$77) then
begin
tl:=TStringList.create;
if (call_external_prog(LOG_NONE,'i2cdetect -y '+Num2Str(bus,0), tl)=0) then
begin
// showstringlist(tl);
i:=(adr div $10)*$10;
j:=(adr mod $10);
// writeln('RPI_I2C_ChkDev[0x'+HexStr(bus,2)+'/0x'+HexStr(adr,2)+']: 0x'+HexStr(i,2)+' 0x'+HexStr(j,2));
i:=SearchStringInListIdx(tl,HexStr(i,2)+': ',1,0);
if (i>=0) then // e.g. 60: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- --
begin
sh:=Upper(Select_Item(Trimme(tl[i],4),' ','',(j+2)));
// writeln('RPI_I2C_ChkDev[0x'+HexStr(bus,2)+'/0x'+HexStr(adr,2)+']: '+sh);
if (sh='--') then nr:=0;
if (sh=HexStr(adr,2)) then nr:=1;
if (sh='UU') then nr:=2;
end;
end;
tl.free;
end;
//writeln('RPI_I2C_ChkDev[0x'+HexStr(bus,2)+'/0x'+HexStr(adr,2)+']: '+Num2Str(nr,0));
RPI_I2C_ChkDev:=nr;
end;
function RPI_I2C_GetSpeed(bus:byte):longword; begin RPI_I2C_GetSpeed:=I2C_bus[bus].I2C_speed; end;
function RPI_I2C_GetFuncs(bus:byte):longword; begin RPI_I2C_GetFuncs:=I2C_bus[bus].I2C_funcs; end;
function RPI_I2C_ChkFuncs(bus:byte; funcs:longword):boolean;begin RPI_I2C_ChkFuncs:=((RPI_I2C_GetFuncs(bus) and funcs)=funcs); end;
function RPI_SPI_GetSpeed(bus:byte):longint; begin RPI_SPI_GetSpeed:=spi_bus[bus].spi_maxspeed; end;
function RPI_get_GPIO_BASE:longword; begin RPI_get_GPIO_BASE:=RPI_mmap_get_info(3); end;
function RPI_mmap_run_on_unix:boolean; begin RPI_mmap_run_on_unix:=(RPI_mmap_get_info(4)=1); end;
function RPI_run_on_ARM:boolean; begin RPI_run_on_ARM := (RPI_mmap_get_info(5)=1); end;
function RPI_Piggyback_board_available : boolean; begin RPI_Piggyback_board_available:=(RPI_mmap_get_info(6)=1); end;
function RPI_PiFace_board_available(devadr:byte): boolean; begin RPI_PiFace_board_available:= (RPI_mmap_get_info(8)=1); end;
function RPI_run_on_known_hw:boolean; begin RPI_run_on_known_hw := (RPI_mmap_get_info(7)=1); end;
function RPI_platform_ok:boolean; begin RPI_platform_ok:= ((RPI_run_on_known_hw) and ((RPI_mmap_get_info(9)=1))) end;
procedure GPIO_MSG_INFO(lvl:T_ERRORlevel; msg:string; gpio:longword; portfkt:t_port_flags);
begin
Log_Writeln(lvl,msg+'GPIO'+Num2Str(gpio,0)+' set '+GetEnumName(TypeInfo(t_port_flags),ord(portfkt)));
end;
function GPIO_get_ALTMask(gpio:longword; altfunc:t_port_flags):longword;
//INPUT=0; OUTPUT=1; ALT0=4; ALT1=5; ALT2=6; ALT3=7; ALT4=3; ALT5=2;
var msk,afkt:longword;
begin
afkt:=ord(altfunc) and $7;
if (altfunc=INPUT) then afkt:=7; // Reset Mask
msk:=(afkt shl ((gpio mod 10)*3));
GPIO_get_ALTMask:=msk;
end;
procedure GPIO_get_mask_and_idxOfs(regidx,gpio:longword; var idxofs:longint; var mask:longword);
begin
idxofs:=0; mask:=0;
case regidx of
GPFSEL: begin idxofs:=((gpio mod gpiomax_2708_reg_c) div 10); mask:=longword((7 shl ((gpio mod 10)*3))); end;
GPPUPPDN:begin idxofs:=((gpio mod gpiomax_2711_reg_c) div 16); mask:=longword((3 shl ((gpio mod 16)*2))); end;
else begin idxofs:=((gpio mod gpiomax_2708_reg_c) div 32); mask:=(longword(1 shl ( gpio mod 32))); end;
end; // case
end;
procedure GPIO_get_mask_and_idx(regidx,gpio:longword; var idxabs,mask:longword);
// out:idxabs gives absolute index
var iofs:longint;
begin
GPIO_get_mask_and_idxOfs(regidx,gpio,iofs,mask); idxabs:=regidx+iofs;
end;
function valid_regidx(regidx:longword):boolean;
var ok:boolean;
begin
ok:=((mmap_arr<>nil) and (regidx<=BCM270x_RegMaxIdx));
if not ok then
LOG_WRITELN(LOG_ERROR,'valid_regidx: not initialized or regidx not valid: '+num2Str(regidx,0));
valid_regidx:=ok;
end;
function BCM_REGAdr(idx:longword):longword; begin BCM_REGAdr:=RPI_get_GPIO_BASE+(idx*BCM270x_RegSizInByte); end;
function BCM_GETREG (regidx:longword):longword;
begin
//writeln('Boom: 0x',HexStr(regidx,8),' ',regidx);
BCM_GETREG:=mmap_arr^[regidx];
end;
procedure BCM_SETREG (regidx,newval:longword); begin mmap_arr^[regidx]:=newval; end;
procedure BCM_SETREG (regidx,newval:longword; and_mask,readmodifywrite:boolean);
begin
//if valid_regidx(regidx) then
begin
if readmodifywrite then
begin
if and_mask then BCM_SETREG(regidx,BCM_GETREG(regidx) and newval)
else BCM_SETREG(regidx,BCM_GETREG(regidx) or newval);
end else BCM_SETREG(regidx,newval);
end;
end;
procedure MEM_SpeedTest; // just for investigations
// tests access speed to RPI Registers vs. regular memory.
// result: access to register is around 6 times slower than access to memory !!!
// on a Pi3 Model B
// mem: 199ms
// mmap: 1204ms APMIRQCLRACK Value: 0x544D5241
const loops=10000000;
var i,lw,lw1:longword; dt1,dt2,dt3:TDateTime;
begin
lw:=1234; lw1:=lw; if lw1>0 then ;
dt1:=now; for i:=1 to loops do lw1:=lw;
dt2:=now; for i:=1 to loops do lw1:=mmap_arr^[APMIRQCLRACK]; // 0x544D5241
dt3:=now;
writeln('mem: ',MilliSecondsBetween(dt2,dt1),'ms');
writeln('mmap: ',MilliSecondsBetween(dt3,dt2),'ms',' APMIRQCLRACK Value: 0x',HexStr(lw1,4));
end;
function MMAP_start(gpioonly:boolean):integer;
//Set up a memory mapped region to access peripherals
var rslt,errno:longint; lw:longword;
begin
rslt:=-7; errno:=0; restrict2gpio:=false; GPU_MEM_BASE:=0;
{$IFDEF LINUX}
if RPI_run_on_ARM then rslt:=-6 else rslt:=-5;
if RPI_run_on_ARM and (mmap_arr=nil) then
begin
if not gpioonly then
begin
rslt:=-1; restrict2gpio:=false;
mem_fd:=fpOpen('/dev/mem',(O_RDWR or O_SYNC (*or O_CLOEXEC*))); // open /dev/mem
end
else
begin
rslt:=-2; restrict2gpio:=true;
mem_fd:=fpOpen('/dev/gpiomem',(O_RDWR or O_SYNC (*or O_CLOEXEC*))); // open /dev/gpiomem
end;
if mem_fd>=0 then
begin // mmap GPIO
rslt:=-3;
//writeln('MMAP_start: PSIZ:0x',HexStr(BCM270x_PSIZ_Byte,8),' Base: 0x',HexStr(RPI_get_GPIO_BASE,8));
//MMAP_start: PSIZ:0x02000000 Base: 0xFE000000
mmap_arr:=fpMMap(pointer(0),BCM270x_PSIZ_Byte,
(PROT_READ or PROT_WRITE),
(MAP_SHARED {or MAP_FIXED}),
mem_fd,
(RPI_get_GPIO_BASE div PAGE_SIZE)
);
if mmap_arr=MAP_FAILED then errno:=fpgeterrno else rslt:=0;
fpclose(mem_fd);
if (rslt=0) and (not restrict2gpio) then
begin
rslt:=-4; // does not work on ZeroW -> 0 ????
lw:=BCM_GETREG(APMIRQCLRACK);
// When reading this register it returns 0x544D5241 which is the ASCII reversed value for "ARMT".
if (lw=$544D5241) then rslt:=0 // ok
else LOG_Writeln(LOG_ERROR,'MMAP_start: APMIRQCLRACK 0x'+HexStr(lw,8));
//writeln('MMAP_start: ',rslt);
end;
end;
end;
{$ENDIF}
case rslt of
0 : Log_writeln(Log_INFO, 'RPI_mmap_init, init successful');
-1 : Log_writeln(Log_ERROR,'RPI_mmap_init, can not open /dev/mem on target CPU '+{$i %FPCTARGETCPU%}+', result: '+Num2Str(rslt,0));
-2 : Log_writeln(Log_ERROR,'RPI_mmap_init, can not open /dev/gpiomem on target CPU '+{$i %FPCTARGETCPU%}+', result: '+Num2Str(rslt,0));
-3 : Log_writeln(Log_ERROR,'RPI_mmap_init, mmap fpgeterrno: '+Num2Str(errno,0)+' result: '+Num2Str(rslt,0));
-4 : Log_writeln(Log_ERROR,'RPI_mmap_init, can not read test register APMIRQCLRACK');
-5 : Log_writeln(Log_ERROR,'RPI_mmap_init, not supported rpi platform');
-6 : Log_writeln(Log_ERROR,'RPI_mmap_init, mmap already initialized');
-7 : Log_writeln(Log_ERROR,'RPI_mmap_init, no linux platform');
else Log_writeln(Log_ERROR,'RPI_mmap_init, unknown error, result: '+Num2Str(rslt,0));
end;
if rslt=0 then
begin
GPU_MEM_BASE:=GPU_UNCACHED_BASE;
(* todo, set GPU_MEM_BASE for rpi1
#if RASPPI == 1
#ifdef GPU_L2_CACHE_ENABLED
#define GPU_MEM_BASE GPU_CACHED_BASE
#else
#define GPU_MEM_BASE GPU_UNCACHED_BASE
#endif
#else
#define GPU_MEM_BASE GPU_UNCACHED_BASE
#endif *)
if restrict2gpio then Log_writeln(Log_WARNING,'RPI_mmap_init, only GPIO access allowed');
end else mmap_arr:=nil;
MMAP_start:=rslt;
end;
procedure MMAP_end;
var rslt:longint;
begin
rslt:=0;
{$IFDEF UNIX}
if (mmap_arr<>nil) then fpMUnMap(mmap_arr,BCM270x_PSIZ_Byte);
{$ENDIF}
mmap_arr:=nil;
case rslt of
0 : Log_writeln(Log_INFO, 'RPI_mmap_close, successful '+Num2Str(rslt,0));
-1 : Log_writeln(Log_ERROR,'RPI_mmap_close, un-mmapping '+Num2Str(rslt,0));
else Log_writeln(Log_ERROR,'RPI_mmap_close, unknown error '+Num2Str(rslt,0));
end;
end;
function GPIO_HWPWM_capable(gpio:longword; pwmnum:byte):boolean;
var ok:boolean;
begin
ok:=false;
if not ok then ok:=((pwmnum=0) and ((gpio=GPIO_PWM0) or (gpio=GPIO_PWM0A0) or (gpio=GPIO_PWM0Audio)));
if not ok then ok:=((pwmnum=1) and ((gpio=GPIO_PWM1) or (gpio=GPIO_PWM1A0) or (gpio=GPIO_PWM1Audio)));
GPIO_HWPWM_capable:=ok;
end;
function GPIO_HWPWM_capable(gpio:longword):boolean;
begin GPIO_HWPWM_capable:=(GPIO_HWPWM_capable(gpio,0) or GPIO_HWPWM_capable(gpio,1)); end;
function GPIO_FCTOK(gpio:longint; flags:s_port_flags):boolean;
var _ok:boolean;
begin
_ok:=(((gpio>=0) and (GPIO_MAP_GPIO_NUM_2_HDR_PIN(gpio)>=0)) or
(gpio=GPIO_PWM0Audio) or (gpio=GPIO_PWM1Audio));
if _ok and (PWMHW IN flags) then _ok:=GPIO_HWPWM_capable(gpio);
if _ok and (FRQHW IN flags) then
_ok:=((gpio=GPIO_FRQ04_CLK0) or (gpio=GPIO_FRQ05_CLK1) or (gpio=GPIO_FRQ06_CLK2) or
(gpio=GPIO_FRQ20_CLK0) or (gpio=GPIO_FRQ32_CLK0) or (gpio=GPIO_FRQ34_CLK0) or
(gpio=GPIO_FRQ42_CLK1) or (gpio=GPIO_FRQ43_CLK2) or (gpio=GPIO_FRQ42_CLK1));
GPIO_FCTOK:=_ok;
end;
function GPIO_PortCapabilityFlags(gpio:longint):s_port_flags;
var flgs:s_port_flags;
begin
if GPIO_FCTOK(gpio,[]) then
begin // valid GPIO num
flgs:=[INPUT,OUTPUT,PullUP,PullDOWN,RisingEDGE,FallingEDGE,PWMSW];
if GPIO_FCTOK(gpio,[PWMHW]) then flgs:=flgs+[PWMHW];
if GPIO_FCTOK(gpio,[FRQHW]) then flgs:=flgs+[FRQHW];
end else flgs:=[];
//writeln('GPIO_PortCapabilityFlags:',gpio,' ',GPIO_PortFlags2String(flgs));
GPIO_PortCapabilityFlags:=flgs;
end;
function GPIO_String2PortFlags(flagstring:string):s_port_flags;
var i,j:longint; flagsOUT:s_port_flags; sh:string;
begin
flagsOUT:=[]; flagstring:=Trimme(flagstring,4);
for i:=1 to Anz_Item(flagstring,' ','') do
begin
sh:=Select_Item(flagstring,' ','',i);
j:=ord(Low(t_port_flags));
while (j<=ord(High(t_port_flags))) do
begin
if (Upper(GetEnumName(TypeInfo(t_port_flags),j))=Upper(sh)) then
begin
flagsOUT:=flagsOUT+[t_port_flags(j)];
j:=ord(High(t_port_flags));
end;
inc(j);
end;
end;
GPIO_String2PortFlags:=flagsOUT;
end;
(*function GPIO_PortFlags(flagstring:string; flagsALLOW:s_port_flags; var flagsOUT:s_port_flags):boolean;
var _ok:boolean; j:t_port_flags; i:longint; sh:string;
begin
_ok:=false; flagsOUT:=[]; flagstring:=Trimme(flagstring,4);
for i:=1 to Anz_Item(flagstring,' ','') do
begin
sh:=Select_Item(flagstring,' ','',i);
for j IN flagsALLOW do
begin
if (Upper(GetEnumName(TypeInfo(t_port_flags),ord(t_port_flags(j))))=Upper(sh)) then
begin
_ok:=true;
flagsOUT:=flagsOUT+[j];
end;
end;
end;
GPIO_PortFlags:=_ok;
end;*)
function GPIO_get_AltDesc(gpio:longint; altpin:byte; dfltifempty:string):string;
// https://github.com/RPi-Distro/raspi-gpio/blob/master/raspi-gpio.c
const altcnt_c=6;
Alt_2709_hdr_dsc_c: array[0..(gpiomax_2708_reg_c*altcnt_c-1)] of string[mdl] =
(//ALT 0 1, 2, 3, 4, 5,
'SDA0' , 'SA5' , 'PCLK' , 'AVEOUT VCLK' , 'AVEIN VCLK' , '-' ,
'SCL0' , 'SA4' , 'DE' , 'AVEOUT DSYNC' , 'AVEIN DSYNC', '-' ,
'SDA1' , 'SA3' , 'LCD VSYNC' , 'AVEOUT VSYNC' , 'AVEIN VSYNC', '-' ,
'SCL1' , 'SA2' , 'LCD HSYNC' , 'AVEOUT HSYNC' , 'AVEIN HSYNC', '-' ,
'GPCLK0' , 'SA1' , 'DPI D0' , 'AVEOUT VID0' , 'AVEIN VID0' , 'ARM TDI' ,
'GPCLK1' , 'SA0' , 'DPI D1' , 'AVEOUT VID1' , 'AVEIN VID1' , 'ARM TDO' ,
'GPCLK2' , 'SOE/ SE' , 'DPI D2' , 'AVEOUT VID2' , 'AVEIN VID2' , 'ARM RTCK' ,
'SPI0 CE1/' , 'SWE/ SRW/' , 'DPI D3' , 'AVEOUT VID3' , 'AVEIN VID3' , '-' ,
'SPI0 CE0/' , 'SD0' , 'DPI D4' , 'AVEOUT VID4' , 'AVEIN VID4' , '-' ,
'SPI0 MISO' , 'SD1' , 'DPI D5' , 'AVEOUT VID5' , 'AVEIN VID5' , '-' ,
'SPI0 MOSI' , 'SD2' , 'DPI D6' , 'AVEOUT VID6' , 'AVEIN VID6' , '-' ,
'SPI0 SCLK' , 'SD3' , 'DPI D7' , 'AVEOUT VID7' , 'AVEIN VID7' , '-' ,
'PWM0' , 'SD4' , 'DPI D8' , 'AVEOUT VID8' , 'AVEIN VID8' , 'ARM TMS' ,
'PWM1' , 'SD5' , 'DPI D9' , 'AVEOUT VID9' , 'AVEIN VID9' , 'ARM TCK' ,
'TXD0' , 'SD6' , 'DPI D10' , 'AVEOUT VID10' , 'AVEIN VID10', 'TXD1' ,
'RXD0' , 'SD7' , 'DPI D11' , 'AVEOUT VID11' , 'AVEIN VID11', 'RXD1' ,
'FL0' , 'SD8' , 'DPI D12' , 'CTS0' , 'SPI1 CE2/' , 'CTS1' ,
'FL1' , 'SD9' , 'DPI D13' , 'RTS0' , 'SPI1 CE1/' , 'RTS1' ,
'PCM CLK' , 'SD10' , 'DPI D14' , 'I2CSL SDA MOSI', 'SPI1 CE0/' , 'PWM0' ,
'PCM FS' , 'SD11' , 'DPI D15' , 'I2CSL SCL SCLK', 'SPI1 MISO' , 'PWM1' ,
'PCM DIN' , 'SD12' , 'DPI D16' , 'I2CSL MISO' , 'SPI1 MOSI' , 'GPCLK0' ,
'PCM DOUT' , 'SD13' , 'DPI D17' , 'I2CSL CE/' , 'SPI1 SCLK' , 'GPCLK1' ,
'SD0 CLK' , 'SD14' , 'DPI D18' , 'SD1 CLK' , 'ARM TRST' , '-' ,
'SD0 CMD' , 'SD15' , 'DPI D19' , 'SD1 CMD' , 'ARM RTCK' , '-' ,
'SD0 DAT0' , 'SD16' , 'DPI D20' , 'SD1 DAT0' , 'ARM TDO' , '-' ,
'SD0 DAT1' , 'SD17' , 'DPI D21' , 'SD1 DAT1' , 'ARM TCK' , '-' ,
'SD0 DAT2' , 'TE0' , 'DPI D22' , 'SD1 DAT2' , 'ARM TDI' , '-' ,
'SD0 DAT3' , 'TE1' , 'DPI D23' , 'SD1 DAT3' , 'ARM TMS' , '-' ,
'SDA0' , 'SA5' , 'PCM CLK' , 'FL0' , '-' , '-' ,
'SCL0' , 'SA4' , 'PCM FS' , 'FL1' , '-' , '-' ,
'TE0' , 'SA3' , 'PCM DIN' , 'CTS0' , '-' , 'CTS1' ,
'FL0' , 'SA2' , 'PCM DOUT' , 'RTS0' , '-' , 'RTS1' ,
'GPCLK0' , 'SA1' , 'RING OCLK' , 'TXD0' , '-' , 'TXD1' ,
'FL1' , 'SA0' , 'TE1' , 'RXD0' , '-' , 'RXD1' ,
'GPCLK0' , 'SOE/ SE' , 'TE2' , 'SD1 CLK' , '-' , '-' ,
'SPI0 CE1/' , 'SWE/ SRW/' , '-' , 'SD1 CMD' , '-' , '-' ,
'SPI0 CE0/' , 'SD0' , 'TXD0' , 'SD1 DAT0' , '-' , '-' ,
'SPI0 MISO' , 'SD1' , 'RXD0' , 'SD1 DAT1' , '-' , '-' ,
'SPI0 MOSI' , 'SD2' , 'RTS0' , 'SD1 DAT2' , '-' , '-' ,
'SPI0 SCLK' , 'SD3' , 'CTS0' , 'SD1 DAT3' , '-' , '-' ,
'PWM0' , 'SD4' , '-' , 'SD1 DAT4' , 'SPI2 MISO' , 'TXD1' ,
'PWM1' , 'SD5' , 'TE0' , 'SD1 DAT5' , 'SPI2 MOSI' , 'RXD1' ,
'GPCLK1' , 'SD6' , 'TE1' , 'SD1 DAT6' , 'SPI2 SCLK' , 'RTS1' ,
'GPCLK2' , 'SD7' , 'TE2' , 'SD1 DAT7' , 'SPI2 CE0/' , 'CTS1' ,
'GPCLK1' , 'SDA0' , 'SDA1' , 'TE0' , 'SPI2 CE1/' , '-' ,
'PWM1' , 'SCL0' , 'SCL1' , 'TE1' , 'SPI2 CE2/' , '-' ,
'SDA0' , 'SDA1' , 'SPI0 CE0/' , '-' , '-' , 'SPI2 CE1/' ,
'SCL0' , 'SCL1' , 'SPI0 MISO' , '-' , '-' , 'SPI2 CE0/' ,
'SD0 CLK' , 'FL0' , 'SPI0 MOSI' , 'SD1 CLK' , 'ARM TRST' , 'SPI2 SCLK' ,
'SD0 CMD' , 'GPCLK0' , 'SPI0 SCLK' , 'SD1 CMD' , 'ARM RTCK' , 'SPI2 MOSI' ,
'SD0 DAT0' , 'GPCLK1' , 'PCM CLK' , 'SD1 DAT0' , 'ARM TDO' , '-' ,
'SD0 DAT1' , 'GPCLK2' , 'PCM FS' , 'SD1 DAT1' , 'ARM TCK' , '-' ,
'SD0 DAT2' , 'PWM0' , 'PCM DIN' , 'SD1 DAT2' , 'ARM TDI' , '-' ,
'SD0 DAT3' , 'PWM1' , 'PCM DOUT' , 'SD1 DAT3' , 'ARM TMS' , '-'
);
Alt_2711_hdr_dsc_c: array[0..(gpiomax_2708_reg_c*altcnt_c-1)] of string[mdl] =
(//ALT 0 1, 2, 3, 4, 5,
'SDA0' , 'SA5' , 'PCLK' , 'SPI3 CE0/' , 'TXD2' , 'SDA6' ,
'SCL0' , 'SA4' , 'DE' , 'SPI3 MISO' , 'RXD2' , 'SCL6' ,
'SDA1' , 'SA3' , 'LCD VSYNC' , 'SPI3 MOSI' , 'CTS2' , 'SDA3' ,
'SCL1' , 'SA2' , 'LCD HSYNC' , 'SPI3 SCLK' , 'RTS2' , 'SCL3' ,
'GPCLK0' , 'SA1' , 'DPI D0' , 'SPI4 CE0/' , 'TXD3' , 'SDA3' ,
'GPCLK1' , 'SA0' , 'DPI D1' , 'SPI4 MISO' , 'RXD3' , 'SCL3' ,
'GPCLK2' , 'SOE/ SE' , 'DPI D2' , 'SPI4 MOSI' , 'CTS3' , 'SDA4' ,
'SPI0 CE1/' , 'SWE/ SRW/' , 'DPI D3' , 'SPI4 SCLK' , 'RTS3' , 'SCL4' ,
'SPI0 CE0/' , 'SD0' , 'DPI D4' , 'I2CSL CE/' , 'TXD4' , 'SDA4' ,
'SPI0 MISO' , 'SD1' , 'DPI D5' , 'I2CSL SDI MISO', 'RXD4' , 'SCL4' ,
'SPI0 MOSI' , 'SD2' , 'DPI D6' , 'I2CSL SDA MOSI', 'CTS4' , 'SDA5' ,
'SPI0 SCLK' , 'SD3' , 'DPI D7' , 'I2CSL SCL SCLK', 'RTS4' , 'SCL5' ,
'PWM0 0' , 'SD4' , 'DPI D8' , 'SPI5 CE0/' , 'TXD5' , 'SDA5' ,
'PWM0 1' , 'SD5' , 'DPI D9' , 'SPI5 MISO' , 'RXD5' , 'SCL5' ,
'TXD0' , 'SD6' , 'DPI D10' , 'SPI5 MOSI' , 'CTS5' , 'TXD1' ,
'RXD0' , 'SD7' , 'DPI D11' , 'SPI5 SCLK' , 'RTS5' , 'RXD1' ,
'-' , 'SD8' , 'DPI D12' , 'CTS0' , 'SPI1 CE2/' , 'CTS1' ,
'-' , 'SD9' , 'DPI D13' , 'RTS0' , 'SPI1 CE1/' , 'RTS1' ,
'PCM CLK' , 'SD10' , 'DPI D14' , 'SPI6 CE0/' , 'SPI1 CE0/' , 'PWM0 0' ,
'PCM FS' , 'SD11' , 'DPI D15' , 'SPI6 MISO' , 'SPI1 MISO' , 'PWM0 1' ,
'PCM DIN' , 'SD12' , 'DPI D16' , 'SPI6 MOSI' , 'SPI1 MOSI' , 'GPCLK0' ,
'PCM DOUT' , 'SD13' , 'DPI D17' , 'SPI6 SCLK' , 'SPI1 SCLK' , 'GPCLK1' ,
'SD0 CLK' , 'SD14' , 'DPI D18' , 'SD1 CLK' , 'ARM TRST' , 'SDA6' ,
'SD0 CMD' , 'SD15' , 'DPI D19' , 'SD1 CMD' , 'ARM RTCK' , 'SCL6' ,
'SD0 DAT0' , 'SD16' , 'DPI D20' , 'SD1 DAT0' , 'ARM TDO' , 'SPI3 CE1/' ,
'SD0 DAT1' , 'SD17' , 'DPI D21' , 'SD1 DAT1' , 'ARM TCK' , 'SPI4 CE1/' ,
'SD0 DAT2' , '-' , 'DPI D22' , 'SD1 DAT2' , 'ARM TDI' , 'SPI5 CE1/' ,
'SD0 DAT3' , '-' , 'DPI D23' , 'SD1 DAT3' , 'ARM TMS' , 'SPI6 CE1/' ,
'SDA0' , 'SA5' , 'PCM CLK' , '-' , 'MII A RX ERR' , 'RGMII MDIO' ,
'SCL0' , 'SA4' , 'PCM FS' , '-' , 'MII A TX ERR' , 'RGMII MDC' ,
'-' , 'SA3' , 'PCM DIN' , 'CTS0' , 'MII A CRS' , 'CTS1' ,
'-' , 'SA2' , 'PCM DOUT' , 'RTS0' , 'MII A COL' , 'RTS1' ,
'GPCLK0' , 'SA1' , '-' , 'TXD0' , 'SD CARD PRES' , 'TXD1' ,
'-' , 'SA0' , '-' , 'RXD0' , 'SD CARD WRPROT' , 'RXD1' ,
'GPCLK0' , 'SOE/ SE' , '-' , 'SD1 CLK' , 'SD CARD LED' , 'RGMII IRQ' ,
'SPI0 CE1/' , 'SWE/ SRW/' , '-' , 'SD1 CMD' , 'RGMII START STOP', '-' ,
'SPI0 CE0/' , 'SD0' , 'TXD0' , 'SD1 DAT0' , 'RGMII RX OK' , 'MII A RX ERR',
'SPI0 MISO' , 'SD1' , 'RXD0' , 'SD1 DAT1' , 'RGMII MDIO' , 'MII A TX ERR',
'SPI0 MOSI' , 'SD2' , 'RTS0' , 'SD1 DAT2' , 'RGMII MDC' , 'MII A CRS' ,
'SPI0 SCLK' , 'SD3' , 'CTS0' , 'SD1 DAT3' , 'RGMII IRQ' , 'MII A COL' ,
'PWM1 0' , 'SD4' , '-' , 'SD1 DAT4' , 'SPI0 MISO' , 'TXD1' ,
'PWM1 1' , 'SD5' , '-' , 'SD1 DAT5' , 'SPI0 MOSI' , 'RXD1' ,
'GPCLK1' , 'SD6' , '-' , 'SD1 DAT6' , 'SPI0 SCLK' , 'RTS1' ,
'GPCLK2' , 'SD7' , '-' , 'SD1 DAT7' , 'SPI0 CE0/' , 'CTS1' ,
'GPCLK1' , 'SDA0' , 'SDA1' , '-' , 'SPI0 CE1/' , 'SD CARD VOLT',
'PWM0 1' , 'SCL0' , 'SCL1' , '-' , 'SPI0 CE2/' , 'SD CARD PWR0',
'SDA0' , 'SDA1' , 'SPI0 CE0/' , '-' , '-' , 'SPI2 CE1/' ,
'SCL0' , 'SCL1' , 'SPI0 MISO' , '-' , '-' , 'SPI2 CE0/' ,
'SD0 CLK' , '-' , 'SPI0 MOSI' , 'SD1 CLK' , 'ARM TRST' , 'SPI2 SCLK' ,
'SD0 CMD' , 'GPCLK0' , 'SPI0 SCLK' , 'SD1 CMD' , 'ARM RTCK' , 'SPI2 MOSI' ,
'SD0 DAT0' , 'GPCLK1' , 'PCM CLK' , 'SD1 DAT0' , 'ARM TDO' , 'SPI2 MISO' ,
'SD0 DAT1' , 'GPCLK2' , 'PCM FS' , 'SD1 DAT1' , 'ARM TCK' , 'SD CARD LED' ,
'SD0 DAT2' , 'PWM0 0' , 'PCM DIN' , 'SD1 DAT2' , 'ARM TDI' , '-' ,
'SD0 DAT3' , 'PWM0 1' , 'PCM DOUT' , 'SD1 DAT3' , 'ARM TMS' , '-'
);
var sh:string;
begin
{$warnings off}
if (altpin>=0) and (altpin<altcnt_c) and
(gpio>=0) and (gpio<gpiomax_2708_reg_c) then
begin
if (Upper(RPI_hw)='BCM2711')
then sh:=Alt_2711_hdr_dsc_c[gpio*altcnt_c+altpin]
else sh:=Alt_2709_hdr_dsc_c[gpio*altcnt_c+altpin];
end else sh:='-';
{$warnings on}
if sh='-' then sh:=dfltifempty;
GPIO_get_AltDesc:=sh;
end; //GPIO_get_AltDesc
function oldGPIO_get_AltDesc(gpio:longint; altpin:byte; dfltifempty:string):string;
// datasheet page 102
const maxalt_c=5; res=''; intrnl='<intrnl>';
Alt_2709_hdr_dsc_c: array[0..maxalt_c] of array[0..gpiomax_2708_reg_c-1] of string[mdl] =
( // ALT0
( ('SDA0'), ('SCL0'), ('SDA1'), ('SCL1'), ('GPCLK0'),
('GPCLK1'), ('GPCLK2'), ('SPI0 CE1/'), ('SPI0 CE0/'), ('SPI0 MISO'),
('SPI0 MOSI'), ('SPI0 SCLK'), ('PWM0'), ('PWM1'), ('TxD0'),
('RxD0'), ('FL0'), ('FL1'), ('PCM CLK'), ('PCM FS'),
('PCM DIN'), ('PCM DOUT'), ('SD0 CLK'), ('SD0 CMD'), ('SD0 DAT0'),
('SD0 DAT1'), ('SD0 DAT2'), ('SD0 DAT3'), ('SDA0'), ('SCL0'),
('TE0'), ('FL0'), ('GPCLK0'), ('FL1'), ('GPCLK0'),
('SPI0 CE1/'), ('SPI0 CE0/'), ('SPI0 MISO'), ('SPI0 MOSI'), ('SPI0 SCLK'),
('PWM0'), ('PWM1'), ('GPCLK1'), ('GPCLK2'), ('GPCLK1'),
('PWM1'), ('SDA0'), ('SCL0'), ('SD0 CLK'), ('SD0 CMD'),
('SD0 DAT0'), ('SD0 DAT1'), ('SD0 DAT2'), ('SD0 DAT3') ),
// ALT1
( ('SA5'), ('SA4'), ('SA3'), ('SA2'), ('SA1'),
('SA0'), ('SOE/'), ('SWE/'), ('SD0'), ('SD1'),
('SD2'), ('SD3'), ('SD4'), ('SD5'), ('SD6'),
('SD7'), ('SD8'), ('SD9'), ('SD10'), ('SD11'),
('SD12'), ('SD13'), ('SD14'), ('SD15'), ('SD16'),
('SD17'), ('TE0'), ('TE1'), ('SA5'), ('SA4'),
('SA3'), ('SA2'), ('SA1'), ('SA0'), ('SOE/'),
('SWE/'), ('SD0'), ('SD1'), ('SD2'), ('SD3'),
('SD4'), ('SD5'), ('SD6'), ('SD7'), ('SDA0'),
('SCL0'), ('SDA1'), ('SCL1'), ('FL0'), ('GPCLK0'),
('GPCLK1'), ('GPCLK2'), ('PWM0'), ('PWM1') ),
// ALT2
( (res), (res), (res), (res), (res),
(res), (res), (res), (res), (res),
(res), (res), (res), (res), (res),
(res), (res), (res), (res), (res),
(res), (res), (res), (res), (res),
(res), (res), (res), ('PCM CLK'), ('PCM FS'),
('PCM DIN'), ('PCM DOUT'), (res), (res), (res),
(''), ('TxD0'), ('RxD0'), ('RTS0'), ('CTS0'),
(''), (res), (res), (res), ('SDA1'),
('SCL1'), (''), (''), (''), (''),
(''), (''), (''), ('') ),
// ALT3
( (''), (''), (''), (''), (''),
(''), (''), (''), (''), (''),
(''), (''), (''), (''), (''),
(''), ('CTS0'), ('RTS0'), ('BSCL'), ('BSCL'),
('BSCL'), ('BSCL'), ('SD1 CLK'), ('SD1 CMD'), ('SD1 DAT0'),
('SD1 DAT1'), ('SD1 DAT2'), ('SD1 DAT3'), (res), (res),
('CTS0'), ('RTS0'), ('TxD0'), ('RxD0'), (res),
(res), (res), (res), (res), (res),
(res), (res), (res), (res), (res),
(''), (''), (''), (''), (''),
(''), (''), (''), ('') ),
// ALT4
( (''), (''), (''), (''), (''),
(''), (''), (''), (''), (''),
(''), (''), (''), (''), (''),
(''), ('SPI1 CE2/'), ('SPI1 CE1/'), ('SPI1 CE0/'), ('SPI1 MISO'),
('SPI1 MOSI'), ('SPI1 SCLK'), ('ARM TRST'), ('ARM RTCK'), ('ARM TDO'),
('ARM TCK'), ('ARM TDI'), ('ARM TMS'), (''), (''),
(''), (''), (''), (''), (''),
(''), (''), (''), (''), (''),
('SPI2 MISO'), ('SPI2 MOSI'), ('SPI2 SCLK'), ('SPI2 CE0/'), ('SPI2 CE1/'),
('SPI2 CE2/'), (''), (''), (''), (''),
(''), (''), (''), ('') ),
// ALT5
( (''), (''), (''), (''), ('ARM TDI'),
('ARM TDO'), ('ARM RTCK'), (''), (''), (''),
(''), (''), ('ARM TMS'), ('ARM TCK'), ('TxD1'),
('RxD1'), ('CTS1'), ('RTS1'), ('PWM0'), ('PWM1'),
('GPCLK0'), ('GPCLK1'), (''), (''), (''),
(''), (''), (''), (''), (''),
('CTS1'), ('RTS1'), ('TxD1'), ('RxD1'), (''),
(''), (''), (''), (''), (''),
('TxD1'), ('RxD1'), ('RTS1'), ('CTS1'), (''),
(''), (''), (''), (''), (''),
(''), (''), (''), ('') )
);
var sh:string;
begin
{$warnings off}
if (altpin>=0) and (altpin<=maxalt_c) and
(gpio>=0) and (gpio<gpiomax_2708_reg_c)
then sh:=Alt_2709_hdr_dsc_c[altpin,gpio]
else sh:='';
{$warnings on}
if sh='' then sh:=dfltifempty;
oldGPIO_get_AltDesc:=sh;
end; //GPIO_get_AltDesc
function GPIO_get_altval(RegAltVal:byte):byte;
var b:byte;
begin
b:=(RegAltVal and $07);
case b of
$02..$03: b:=$07-b; // A04 A05
$04..$05,
$06..$07: b:=b-$04; // A00 A01 A02 A03
end;
GPIO_get_altval:=b;
end;
function gpiofkt(gpio:longint; gpiofunc:byte; desclong:boolean):string;
var s:string; av:byte;
begin
case (gpiofunc and $7) of
$00 : s:='IN ';
$01 : s:='OUT';
$02..$07: begin
av:=GPIO_get_altval(gpiofunc);
s:='A'+Num2Str(av,0)+' ';
if desclong then s:=GPIO_get_AltDesc(gpio,av,s);
end;
else s:='';
end;
gpiofkt:=s;
end;
function GPIO_get_fkt_value(gpio:longint):byte;
var regidx,mask:longword; altval:byte;
begin
altval:=$00;
if (gpio>=0) and (gpio<gpiomax_2708_reg_c) then
begin
GPIO_get_mask_and_idx(GPFSEL,gpio,regidx,mask);
altval:=Byte(((BCM_GETREG(regidx) and mask) shr ((gpio mod 10)*3)) and $7);
end;
GPIO_get_fkt_value:=altval;
end;
function get_reg_desc(regidx,regcontent:longword):string;
var s:string;
begin
s:='';
case regidx of
GPFSEL..GPFSEL+5: s:='GPFSEL'+ Num2Str(longword(regidx-GPFSEL),0);
GPSET ..GPSET+1: s:='GPSET'+ Num2Str(longword(regidx-GPSET),0);
GPCLR ..GPCLR+1: s:='GPCLR'+ Num2Str(longword(regidx-GPCLR),0);
GPLEV ..GPLEV+1: s:='GPLEV'+ Num2Str(longword(regidx-GPLEV),0);
GPEDS ..GPEDS+1: s:='GPEDS'+ Num2Str(longword(regidx-GPEDS),0);
GPREN ..GPREN+1: s:='GPREN'+ Num2Str(longword(regidx-GPREN),0);
GPFEN ..GPFEN+1: s:='GPFEN'+ Num2Str(longword(regidx-GPFEN),0);
GPHEN ..GPHEN+1: s:='GPHEN'+ Num2Str(longword(regidx-GPHEN),0);
GPLEN ..GPLEN+1: s:='GPLEN'+ Num2Str(longword(regidx-GPLEN),0);
GPAREN..GPAREN+1: s:='GPAREN'+ Num2Str(longword(regidx-GPAREN),0);
GPAFEN..GPAFEN+1: s:='GPAFEN'+ Num2Str(longword(regidx-GPAFEN),0);
GPPUD: s:='GPPUD'+ Num2Str(longword(regidx-GPPUD),0);
GPPUDCLK..GPPUDCLK+1: s:='GPPUDCLK'+Num2Str(longword(regidx-GPPUDCLK),0);
GPPUPPDN: s:='GPPUPPDN'+Num2Str(longword(regidx-GPPUPPDN),0);
STIMCS: s:='SYSTIMCS';
STIMCLO: s:='SYSTIMCLO';
STIMCHI: s:='SYSTIMCHI';
STIMC0: s:='SYSTIMC0';
STIMC1: s:='SYSTIMC1';
STIMC2: s:='SYSTIMC2';
STIMC3: s:='SYSTIMC3';
SPI0_CS: s:='CS';
SPI0_FIFO: s:='FIFO';
SPI0_CLK: s:='CLK';
SPI0_DLEN: s:='DLEN';
SPI0_LTOH: s:='LTOH';
SPI0_DC: s:='DC';
I2C0_C: s:='CONTROL';
I2C0_S: s:='STATUS';
I2C0_DLEN: s:='DLEN';
I2C0_A: s:='SLAVEADR';
I2C0_FIFO: s:='FIFO';
I2C0_DIV: s:='DIV';
I2C0_DEL: s:='DEL';
I2C0_CLKT: s:='CLKT';
I2C1_C: s:='CONTROL';
I2C1_S: s:='STATUS';
I2C1_DLEN: s:='DLEN';
I2C1_A: s:='SLAVEADR';
I2C1_FIFO: s:='FIFO';
I2C1_DIV: s:='DIV';
I2C1_DEL: s:='DEL';
I2C1_CLKT: s:='CLKT';
PWMCTL: s:='PWMCTL';
PWMSTA: s:='PWMSTA';
PWMDMAC: s:='PWMDMAC';
PWM0RNG: s:='PWM0RNG';
PWM0DAT: s:='PWM0DAT';
PWM0FIF: s:='PWM0FIF';
PWM1RNG: s:='PWM1RNG';
PWM1DAT: s:='PWM1DAT';
GMGP0CTL: s:='GMGP0CTL';
GMGP0DIV: s:='GMGP0DIV';
GMGP1CTL: s:='GMGP1CTL';
GMGP1DIV: s:='GMGP1DIV';
GMGP2CTL: s:='GMGP2CTL';
GMGP2DIV: s:='GMGP2DIV';
PWMCLKCTL: s:='PWMCLKCTL';
PWMCLKDIV: s:='PWMCLKDIV';
APMVALUE: s:='APMVALUE';
APMCTL: s:='APMCTL';
APMIRQCLRACK: s:='APMIRQCLRACK';
APMRAWIRQ: s:='APMRAWIRQ';
APMMaskedIRQ: s:='APMMaskedIRQ';
APMReload: s:='APMReload';
APMPreDivider: s:='APMPreDivider';
APMFreeRunCounter: s:='APMFreeRunCounter';
Q4LP_CTL : s:='CTL';
Q4LP_CTIMPRE : s:='CTIMPRE';
Q4LP_LOCINTRTG : s:='LOCINTRTG';
Q4LP_GPUINTRTG : s:='GPUINTRTG';
Q4LP_CoreTimAccLS : s:='CTIMLSB';
Q4LP_CoreTimAccMS : s:='CTIMMSB';
Q4LP_LOCTIMCTL : s:='LOCTIMCTL';
Q4LP_LOCTIMCTL+1: s:='LOCTIMFLG';
Q4LP_Core0IntCtl..
Q4LP_Core0IntCtl+3: s:='C'+Num2Str(longword(regidx-Q4LP_Core0IntCtl),0)+'INTCTL';
Q4LP_Core0IrqSrc..
Q4LP_Core0IrqSrc+3: s:='C'+Num2Str(longword(regidx-Q4LP_Core0IrqSrc),0)+'IRQSRC';
Q4LP_Core0FIQSrc..
Q4LP_Core0FIQSrc+3: s:='C'+Num2Str(longword(regidx-Q4LP_Core0FIQSrc),0)+'FIQSRC';
else s:='['+HexStr(RPI_get_GPIO_BASE+(regidx*BCM270x_RegSizInByte),8)+']';
//s:='Reg['+Num2Str(longword(regidx),0)+']';
end; // case
s:=Get_FixedStringLen(s,wid1,false)+': '+Bin(regcontent,32)+' 0x'+HexStr(regcontent,8);
get_reg_desc:=s;
end;
function GPIO_get_desc(regidx,regcontent:longword) : string;
var s:string; pin:integer;
begin
s:='';
case regidx of
GPFSEL..GPFSEL+5 : begin
for pin:= 9 downto 0 do
s:=s+'P'+LeadingZero(pin+(regidx-GPFSEL)*10)+':'+
gpiofkt((pin+(regidx-GPFSEL)*10),
GPIO_get_fkt_value((pin+(regidx-GPFSEL)*10)),false)+' ';
end;
end;
GPIO_get_desc:=s;
end;
procedure DESC_HWPIN(pin:longint; var desc,dir,pegel:string);
// WRONGPIN=-100; UKN=-99; V5=-98; V33=-97; GND=-96; DNC=-95;
var gpio:longint; altval,av:byte;
begin
gpio:=GPIO_MAP_HDR_PIN_2_GPIO_NUM(pin); dir:=''; pegel:='';
case gpio of
V5: desc:='5V';
V33: desc:='3.3V';
GND: desc:='GND';
IDSC: desc:='ID SC';
IDSD: desc:='ID SD';
DNC: desc:='';
UKN: desc:='';
WRONGPIN: desc:='';
else begin
gpio:=abs(gpio);
if (pin>=1) and (pin<=max_pins_c)
then desc:=RPIHDR_Desc[pin] else desc:='';
altval:=GPIO_get_fkt_value(gpio);
dir:=gpiofkt(gpio,altval,false);
case altval of
$00: begin pegel:=Bool2LVL(GPIO_get_PIN(gpio)); end; // IN
$01: begin pegel:=Bool2LVL(GPIO_get_PIN(gpio)); end; // OUT
else begin
av:=GPIO_get_altval(altval);
if desc='' then desc:=GPIO_get_AltDesc(gpio,av,desc);
// sh:='A0'+Num2Str(av,1);
end;
end; // case
if desc='' then desc:='GPIO'+LeadingZero(gpio);
end;
end; //case
end;
function CEPstring(cmd:string):string; var sh:string;
begin
call_external_prog(Log_NONE,cmd,sh);
CEPstring:=CSV_RemLastSep(sh,#$00);
end;
function HAT_custom(tl:TStringList; keys,dflts,tr1,tr2:string):string;
var sh:string;
begin
sh:=SearchStringInList(tl,keys);
if (sh<>'')
then sh:=Select_RightItems(sh,tr1,tr2,2)
else sh:=dflts;
HAT_custom:=CSV_RemLastSep(sh,#$00);
end;
function HAT_custom(tl:TStringList; keys,dflts:string):string;
begin HAT_custom:=HAT_custom(tl,keys,dflts,' ','"'); end;
function HAT_vendor:string; begin HAT_vendor:= CEPstring('cat '+PrepFilePath(LNX_DevTree+'/hat/vendor')); end;
function HAT_product:string; begin HAT_product:= CEPstring('cat '+PrepFilePath(LNX_DevTree+'/hat/product')); end;
function HAT_product_id:string; begin HAT_product_id:= CEPstring('cat '+PrepFilePath(LNX_DevTree+'/hat/product_id')); end;
function HAT_product_ver:string;begin HAT_product_ver:=CEPstring('cat '+PrepFilePath(LNX_DevTree+'/hat/product_ver')); end;
function HAT_uuid:string; begin HAT_uuid:= CEPstring('cat '+PrepFilePath(LNX_DevTree+'/hat/uuid')); end;
function HAT_GetInfo(ovrwrt:boolean; duuid,dvendor,dproduct,dsnr:string; dpid,dpver:longword):boolean;
begin
with HAT_info do
begin
uuid:=''; vendor:=''; product:=''; snr:=''; product_id:=0; product_ver:=0;
available:=DirectoryExists(PrepFilePath(LNX_DevTree+'/hat'));
overwrite:=false; if not available then overwrite:=ovrwrt;
if available then
begin
uuid:= HAT_uuid;
vendor:= HAT_vendor;
product:= HAT_product; // e.g. productname@snr
snr:= Select_Item(product,'@','',2); // snr
product:= Select_Item(product,'@','',1); // productname
if not Str2Num(HAT_product_id, product_id) then product_id:= 0;
if not Str2Num(HAT_product_ver,product_ver) then product_ver:=0;
end
else
begin
if overwrite then
begin // e.g. for testing
SAY(LOG_WARNING,'HAT_GetInfo: HAT_OVRwrite');
available:= true;
uuid:= duuid;
vendor:= dvendor;
product:= dproduct;
snr:= dsnr;
product_id:=dpid;
product_ver:=dpver;
end;
end;
HAT_GetInfo:=available;
end; // with
end;
function HAT_GetInfo:boolean;
begin HAT_GetInfo:=HAT_GetInfo(false,'00000000-0000-0000-0000-000000000000','vendor','product',rpi_snr,0,0); end;
procedure HAT_GetStructInfo(HAT_INFO_tl:TStringList; lgt:byte);
var ovrstr:string;
begin
with HAT_info do
begin
if overwrite then ovrstr:=' (ovr)' else ovrstr:='';
HAT_INFO_tl.add(Get_FixedStringLen('uuid:',lgt,false)+uuid+ovrstr);
HAT_INFO_tl.add(Get_FixedStringLen('vendor:',lgt,false)+vendor);
HAT_INFO_tl.add(Get_FixedStringLen('product:',lgt,false)+product);
if snr<>'' then HAT_INFO_tl.add(Get_FixedStringLen('snr:',lgt,false)+snr);
HAT_INFO_tl.add(Get_FixedStringLen('prod_id:',lgt,false)+'0x'+HexStr(product_id, 4));
HAT_INFO_tl.add(Get_FixedStringLen('prod_ver:',lgt,false)+'0x'+HexStr(product_ver,4));
end; // with
end;
procedure HAT_ShowStruct;
var _tl:TStringList;
begin
_tl:=TStringList.create;
HAT_GetStructInfo(_tl,10);
ShowStringList(_tl);
_tl.free;
end;
procedure HAT_Info_Test;
begin
if HAT_GetInfo then
begin
writeln('HAT Info:');
HAT_ShowStruct;
end else Log_Writeln(Log_ERROR,'HAT_Info_Test: no HAT installed');
end;
procedure HAT_EEprom_Map(tl:TStringList; hwname,uuid,vendor,product:string; prodid,prodver,gpio_drive,gpio_slew,gpio_hysteresis,back_power:word; useDefault,EnabIO:boolean);
//https://github.com/raspberrypi/hats/blob/master/eeprom-format.md
//https://github.com/raspberrypi/hats/blob/master/devicetree-guide.md
procedure la(str:string); begin tl.add(str); end;
var _hwname,_uuid,_vendor,_product,dir,desc,pegel,sh,sh2,sh3:string; _gd,_gs,_gh,_bp,n,pin:word;
begin
_hwname:=hwname; if _hwname='' then _hwname:=Get_Fname(ParamStr(0));
_uuid:=uuid; if _uuid='' then _uuid:= '00000000-0000-0000-0000-000000000000';
_vendor:=vendor; if _vendor='' then _vendor:= 'ACME Technology Company';
_product:=product;if _product='' then _product:='Special Sensor Board';
_gd:=gpio_drive; if _gd>15 then _gd:=0;
_gs:=gpio_slew; if _gs>3 then _gs:=0;
_gh:=gpio_hysteresis; if _gh>3 then _gh:=0;
_bp:=back_power; if _bp>3 then _bp:=0;
la('########################################################################');
la('# EEPROM settings file for '+_hwname);
la('# Vendor info');
la('');
la('product_uuid '+_uuid);
la('product_id 0x'+ HexStr(prodid, 4));
la('product_ver 0x'+HexStr(prodver,4));
la('vendor "'+ copy(_vendor, 1,255)+'"');
la('product "'+copy(_product,1,255)+'"');
la('');
la('########################################################################');
la('');
la('# drive strength, 0=default, 1-8=2,4,6,8,10,12,14,16mA, 9-15=reserved');
la('gpio_drive '+Num2Str(_gd,0));
la('');
la('# 0=default, 1=slew rate limiting, 2=no slew limiting, 3=reserved');
la('gpio_slew '+Num2Str(_gs,0));
la('');
la('# 0=default, 1=hysteresis disabled, 2=hysteresis enabled, 3=reserved');
la('gpio_hysteresis '+Num2Str(_gh,0));
la('');
la('# If board back-powers Pi via 5V GPIO header pins:');
la('# 0 = board does not back-power');
la('# 1 = board back-powers and can supply the Pi with a minimum of 1.3A');
la('# 2 = board back-powers and can supply the Pi with a minimum of 2A');
la('# 3 = reserved');
la('# If back_power=2 then USB high current mode will be automatically enabled on the Pi');
la('back_power '+Num2Str(_bp,0));
la('');
la('########################################################################');
la('# GPIO pins, uncomment for GPIOs used on board');
la('# Options for FUNCTION: INPUT, OUTPUT, ALT0-ALT5');
la('# Options for PULL: DEFAULT, UP, DOWN, NONE');
la('# NB GPIO0 and GPIO1 are reserved for ID EEPROM so cannot be set');
la('');
la('# GPIO FUNCTION PULL');
la('# ---- -------- ----');
for n:= 2 to 27 do
begin
sh:='#'; if EnabIO then sh:=' ';
if useDefault then
begin
sh:=sh+'setgpio '+Get_FixedStringLen(Num2Str(n,0),2,true)+' INPUT DEFAULT';
end
else
begin
pin:=GPIO_MAP_GPIO_NUM_2_HDR_PIN(n);
DESC_HWPIN(pin,desc,dir,pegel);
desc:= Get_FixedStringLen(desc,mdl,false);
dir:= Get_FixedStringLen(dir, 3,false);
pegel:=Get_FixedStringLen(pegel, 1,false);
if dir<>'' then
begin
sh2:=StringReplace(dir,'IN' ,'INPUT', [rfReplaceAll,rfIgnoreCase]);
sh2:=StringReplace(sh2,'OUT','OUTPUT',[rfReplaceAll,rfIgnoreCase]);
sh2:=StringReplace(sh2,'A', 'ALT', [rfReplaceAll,rfIgnoreCase]);
sh2:=Get_FixedStringLen(sh2,10,false);
sh3:='DEFAULT';
sh:= sh+'setgpio '+Get_FixedStringLen(Num2Str(n,0),6,false)+sh2+sh3+
' # '+Num2Str(pin,2)+' '+pegel+' '+Trimme(desc,3);
end;
end;
if Trimme(sh,3)<>'' then la(sh);
end; // for
end;
procedure HAT_EEprom_Map_Test;
(* ./eepmake eeprom_mycfg.txt eepcfg.eep
./eepflash.sh -w -t=24c256 -f=eepcfg.eep
./eepflash.sh -r -t=24c256 -f=myeep.eep
./eepdump myeep.eep stuff.eep
more stuff.eep *)
var tl:TStringList;
begin
tl:=TStringList.create;
HAT_EEprom_Map(tl,'test','','your company','your board',$0001,$0001,0,0,0,2,true,false);
ShowStringList(tl); // StringList2TextFile('/tmp/eeprom_example.txt',tl);
tl.free;
end;
procedure GPIO_ConnectorStringList(tl:TStringList);
{ shows the actual configuration of the Hardware Connector.
V shows the actual logic level of the PIN 'L' is low and 'H' is high level
DIR: IN=Pin is configured as Input, OUT=Output. A0..A5 shows the ALT level.
pls. see datasheet for definition
PIN Header (rev3;1GB;PI2B;BCM2709;a01041):
Signal DIR V Pin Pin V DIR Signal
3.3V 1 || 2 5V
I2C SDA1 A0 3 || 4 5V
I2C SCL1 A0 5 || 6 GND
GPIO04 IN H 7 || 8 A0 TxD0
GND 9 || 10 A0 RxD0
GPIO17 OUT H 11 || 12 H IN GPIO18
GPIO27 IN L 13 || 14 GND
GPIO22 IN L 15 || 16 L IN GPIO23
3.3V 17 || 18 L IN GPIO24
SPI0 MOSI A0 19 || 20 GND
SPI0 MISO A0 21 || 22 L IN GPIO25
SPI0 SCLK A0 23 || 24 A0 SPI0 CE0/
GND 25 || 26 A0 SPI0 CE1/
ID SD 27 || 28 ID SC
GPIO05 IN H 29 || 30 GND
GPIO06 IN H 31 || 32 L IN GPIO12
GPIO13 IN L 33 || 34 GND
GPIO19 IN L 35 || 36 L IN GPIO16
GPIO26 IN L 37 || 38 L IN GPIO20
GND 39 || 40 L IN GPIO21
}
var pin,pinmax:longint; sh,dir,desc,pegel:string;
begin
pinmax:=40;
begin
sh:='';
tl.add('PIN Header ('+RPI_rev+'):');
tl.add(Get_FixedStringLen('Signal',mdl,false)+' DIR V Pin Pin V DIR Signal');
for pin:= 1 to pinmax do
begin
DESC_HWPIN(pin,desc,dir,pegel);
desc:= Get_FixedStringLen(desc,mdl,false);
dir:= Get_FixedStringLen(dir, 3,false);
pegel:=Get_FixedStringLen(pegel, 1,false);
if (pin mod 2)=0 then
begin
sh:=sh+' || '+Num2Str(pin,2)+' '+pegel+' '+dir+' '+desc;
tl.add(sh);
sh:='';
end
else
begin
sh:=desc+' '+dir+' '+pegel+' '+Num2Str(pin,2);
end;
end;
end;
end;
procedure GPIO_ShowConnector;
var tl:TStringList;
begin
tl:=TStringList.create;
GPIO_ConnectorStringList(tl);
ShowStringList(tl);
tl.free;
end;
function show_reg(regidx,mode:longword):string;
var data:longword; s:string;
begin
data:=BCM_GETREG(regidx);
s:=get_reg_desc(regidx,data);
if mode=1 then s:=s+' '+GPIO_get_desc(regidx,data);
show_reg:=s;
end;
procedure show_regs(desc:string; ofs,startidx,endidx,mode:longword; showhdr:boolean);
var idx:longword; skip:boolean;
begin
skip:=((mode=2) and (RPI_hw='BCM2708'));
writeln(Get_FixedStringLen(desc,wid1,false)+': ',HexStr(RPI_get_GPIO_BASE+ofs,8));
if showhdr then
begin
write (Get_FixedStringLen('Adr(1F-00)',wid1,false)+': ');
for idx:=31 downto 0 do
begin write(HexStr((idx mod $10),1)); if (idx mod 4)=0 then write(' '); end; writeln;
end;
if (not skip) then
begin
for idx:=startidx to endidx do writeln(show_reg(idx,mode));
end
else writeln(RPI_hw,' processor has no registers here');
end;
procedure show_regs(desc:string; ofs,startidx,endidx,mode:longword);
begin show_regs(desc,ofs,startidx,endidx,mode,true); end;
procedure PADS_show_regs;begin show_regs('PADSBase', PADS_BASE_OFS, PADS_BASE_START,PADS_BASE_LAST,0); end;
procedure GPIO_show_regs;begin show_regs('GPIOBase', GPIO_BASE_OFS, GPIO_BASE,GPIO_BASE_LAST,1); end;
procedure SPI0_show_regs;begin show_regs('SPI0Base', SPI0_BASE_OFS, SPI0_BASE,SPI0_BASE_LAST,0); end;
procedure I2C0_show_regs;begin show_regs('I2C0Base', I2C0_BASE_OFS, I2C0_BASE,I2C0_BASE_LAST,0); end;
procedure I2C1_show_regs;begin show_regs('I2C1Base', I2C1_BASE_OFS, I2C1_BASE,I2C1_BASE_LAST,0); end;
procedure I2C2_show_regs;begin show_regs('I2C2Base', I2C2_BASE_OFS, I2C2_BASE,I2C2_BASE_LAST,0); end;
procedure PWM_show_regs; begin show_regs('PWMBase', PWM_BASE_OFS, PWM_BASE, PWM_BASE_LAST,0); end;
procedure STIM_show_regs;begin show_regs('SYSTIMBase', STIM_BASE_OFS, STIM_BASE,STIM_BASE_LAST,0); end;
procedure TIM_show_regs; begin show_regs('TIMRBase', TIMR_BASE_OFS, TIMR_BASE,INTR_BASE_LAST,0); end;
procedure CLK_show_regs; begin show_regs('CLKBase', CLK_BASE_OFS, GMGP0CTL, GMGP2DIV,0); writeln;
show_regs('PWMCLK', CLK_BASE_OFS, PWMCLKCTL,PWMCLKDIV,0); end;
procedure Q4_show_regs; begin show_regs('Q4Base', BCM2709_LP_OFS,Q4LP_BASE,Q4LP_Last,2); end;
procedure Clock_show_regs;
begin
show_regs('SPIClk', SPI0_BASE_OFS, SPI0_CLK, SPI0_CLK,0,false);
show_regs('I2C0Clk', I2C0_BASE_OFS, I2C0_DIV, I2C0_DIV,0,false);
show_regs('I2C1Clk', I2C1_BASE_OFS, I2C1_DIV, I2C1_DIV,0,false);
show_regs('I2C2Clk', I2C2_BASE_OFS, I2C2_DIV, I2C2_DIV,0,false);
show_regs('TIMR', TIMR_BASE_OFS, APMPreDivider, APMPreDivider,0,false);
show_regs('GMGPxCTL', CLK_BASE_OFS, GMGP0DIV, GMGP0DIV,0,false);
show_regs('GMGPxCTL', CLK_BASE_OFS, GMGP1DIV, GMGP1DIV,0,false);
show_regs('GMGPxCTL', CLK_BASE_OFS, GMGP2DIV, GMGP2DIV,0,false);
show_regs('PWMCLK', CLK_BASE_OFS, PWMCLKDIV, PWMCLKDIV,0,false);
show_regs('Q4LP', BCM2709_LP_OFS, Q4LP_CTIMPRE, Q4LP_CTIMPRE,2,false);
end;
procedure GPIO_set_RESET(gpio:longword);
var idx,mask:longword;
begin // RESET 3Bits @ according gpio location within register GPFSELn
GPIO_get_mask_and_idx(GPFSEL,gpio,idx,mask);
BCM_SETREG(idx,(not GPIO_get_ALTMask(gpio,INPUT)),true,true);
end;
procedure GPIO_set_INPUT (gpio:longword);
begin
GPIO_MSG_INFO(LOG_DEBUG,'GPIO_set_INPUT: ',gpio,INPUT);
GPIO_set_RESET(gpio);
end;
procedure GPIO_set_OUTPUT(gpio:longword);
var idx,mask:longword;
begin
GPIO_MSG_INFO(LOG_DEBUG,'GPIO_set_OUTPUT: ',gpio,OUTPUT);
GPIO_get_mask_and_idx(GPFSEL,gpio,idx,mask);
GPIO_set_RESET(gpio); // Always use GPIO_set_RESET(x) before using GPIO_set_OUTPUT(x), to reset Bits
BCM_SETREG(idx,GPIO_get_ALTMask(gpio,OUTPUT),false,true);
end;
procedure GPIO_set_ALT(gpio:longword; altfunc:t_port_flags);
var idx,mask:longword;
begin
GPIO_MSG_INFO(LOG_DEBUG,'GPIO_set_ALT: ',gpio,altfunc);
GPIO_get_mask_and_idx(GPFSEL,gpio,idx,mask);
GPIO_set_RESET(gpio); // Always use GPIO_set_RESET(x) before using GPIO_set_ALT(x,y), to reset Bits
BCM_SETREG(idx,GPIO_get_ALTMask(gpio,altfunc),false,true);
end;
function RPI_ShutDown_Thread(ptr:pointer):ptrint;
(* pushbutton connected to this GPIO pin, using GPIO3/HWPIN:5 (default) also has the benefit of
wakeing / powering up Raspberry Pi when button is pressed *)
var buttonPressedTime:TDateTime; tog,noovrpres,TermThread:boolean; elapsedTime_msec:int64; sh,msg:string;
begin
Thread_SetName('RPIShutDown');
tog:=false; noovrpres:=true; TermThread:=false; buttonPressedTime:=now;
with RPI_ShutDown_struct do
begin
msg:='RPI_ShutDown[Pin#'+Num2Str(HWPin,0)+'/GPIO'+Num2Str(gpio,0)+']: Thread START debounce:'+Num2Str(RPI_ShutDownDebounce_ms,0)+'msec ShutdownTime:'+Num2Str(RPI_ShutDownMin_ms,0)+'msec';
SAY(LOG_WARNING,msg);
repeat
GPIO_Switch(RPI_ShutDown_struct);
if ein and noovrpres then
begin // OnOff-Button
// if not tog then SAY(LOG_INFO,'+########################################################## '+Bool2Str(ein)+' '+Bool2Str(tog));
if not tog
then begin tog:=true; buttonPressedTime:=now; end
else noovrpres:=(MilliSecondsBetween(buttonPressedTime,now)<=(RPI_ShutDownMin_ms+RPI_ShutDownDebounce_ms));
end
else
begin
if tog then
begin
elapsedTime_msec:=MilliSecondsBetween(buttonPressedTime,now);
tog:=false;
msg:='RPI_ShutDown[Pin#'+Num2Str(HWPin,0)+'/GPIO'+Num2Str(gpio,0)+'/'+Num2Str(elapsedTime_msec,0)+'msec]:';
// SAY(LOG_INFO,'-########################################################## '+Bool2Str(ein)+' 0x'+Bool2Str(tog)+' '+Num2Str(RPI_ShutDownMin_ms,0)+' '+Num2Str(RPI_ShutDownDebounce_ms,0));
if (elapsedTime_msec<RPI_ShutDownMin_ms) then
begin
if (elapsedTime_msec>RPI_ShutDownDebounce_ms) then
begin // button pressed for a shorter time, reboot
SAY(LOG_WARNING,msg+' rebooting requested');
if (RPI_ShutDown_RebootCall=nil) then
begin
terminateProg:=true;
delay_msec(10); // let other Threads time to terminate
call_external_prog(LOG_INFO,sudo+' shutdown -r now',sh);
end else RPI_ShutDown_RebootCall;
TermThread:=true;
end else SAY(LOG_WARNING,msg+' debounce');
end
else
begin // button pressed for more than specified time, shutdown
SAY(LOG_WARNING,msg+' shutdown requested');
if (RPI_ShutDown_Call=nil) then
begin
terminateProg:=true;
delay_msec(10); // let other Threads time to terminate
call_external_prog(LOG_INFO,sudo+' shutdown -h now',sh);
end else RPI_ShutDown_Call;
TermThread:=true;
end;
// SAY(LOG_INFO,'');
end;
end; // OnOff-Button
if ein then delay_msec(1) else delay_msec(10);
until (TermThread or terminateProg);
end; // with
SAY(LOG_INFO,'RPI_ShutDown: Thread END');
terminateProg:=true;
EndThread;
RPI_ShutDown_Thread:=0;
end;
function RPI_ShutDownInit(hwpin:longint; shutdownMIN_msec,debounce_msec:word;
RebootCall,ShutDownCall:TProcedureNoArgCall;
desc:string; flags:s_port_flags):boolean;
var _ok:boolean;
begin
_ok:=false;
RPI_ShutDownMin_ms:=shutdownMIN_msec;
RPI_ShutDownDebounce_ms:=debounce_msec;
RPI_ShutDown_RebootCall:= RebootCall;
RPI_ShutDown_Call:= ShutDownCall;
if (hwpin>0) then
begin
RPI_ShutDownGPIO:=GPIO_MAP_HDR_PIN_2_GPIO_NUM(hwpin);
if (RPI_ShutDownGPIO>=0) then
begin
_ok:=true;
GPIO_SetStruct(RPI_ShutDown_struct,1,RPI_ShutDownGPIO,desc,flags);
end;
end;
RPI_ShutDownInit:=_ok;
end;
function RPI_ShutDownInit(hwpin:longint):boolean;
begin
RPI_ShutDownInit:=RPI_ShutDownInit(hwpin,3100,7,nil,nil,'PIShutDown',[INPUT,PullUP,ReversePOLARITY]);
end;
function RPI_ShutDownStart:boolean;
var _ok:boolean;
begin
_ok:=GPIO_Setup(RPI_ShutDown_struct);
if _ok then BeginThread(@RPI_ShutDown_Thread,nil)
else LOG_Writeln(LOG_ERROR,'RPI_ShutDownStart: can not GPIO_Setup');
RPI_ShutDownStart:=_ok;
end;
function pwm_SW_Thread(ptr:pointer):ptrint;
begin
with GPIO_ptr(ptr)^ do
begin
if (gpio>=0) and (ptr<>nil) then
begin
// writeln('pwm_SW_Thread: Start of ',description,' with PWMSW (GPIO',Num2Str(gpio,0),')');
// , period(us):',PWM.pwm_period_us,' dtycycl(us):',PWM.pwm_dutycycle_us,' restcycl(us):',PWM.pwm_restcycle_us);
Thread_SetName(description);
while not ThreadCtrl.TermThread do
begin
if PWM.pwm_sigalt then
begin
if (PWM.pwm_dutycycle_us>0) then
begin
// writeln('PWM.pwm_dutycycle_us:',PWM.pwm_dutycycle_us);
mmap_arr^[regset]:=mask_1Bit;
if (PWM.pwm_restcycle_us>0) then delay_us(PWM.pwm_dutycycle_us)
else PWM.pwm_sigalt:=false;
end;
if (PWM.pwm_restcycle_us>0) then
begin
// writeln('PWM.pwm_restcycle_us:',PWM.pwm_restcycle_us);
mmap_arr^[regclr]:=mask_1Bit;
if (PWM.pwm_dutycycle_us>0) then delay_us(PWM.pwm_restcycle_us)
else PWM.pwm_sigalt:=false;
end;
end
else delay_msec(PWM.pwm_period_ms);
end;
mmap_arr^[regclr]:=mask_1Bit;
end else LOG_Writeln(LOG_ERROR,'pwm_SW_Thread: GPIO not supported or no valid datastruct pointer');
// writeln('pwm_SW_Thread: END of ',description);
EndThread;
end;
pwm_SW_Thread:=0;
end;
function pwm_GetDCSWVal(pwm_period_us,pwm_value,pwm_dutyrange:longword):longword;
var pwm_dutycycle_us:longword;
begin
pwm_dutycycle_us:=0;
if (pwm_dutyrange>0) then pwm_dutycycle_us:=round(pwm_period_us*pwm_value/(pwm_dutyrange-1));
pwm_GetDCSWVal:=pwm_dutycycle_us;
end;
function pwm_GetMODVal(value,maxval:longword):longword;
var res:longword;
begin
res:=value;
if (res>=maxval) then if (maxval>0) then res:=(res mod maxval) else res:=0;
pwm_GetMODVal:=res;
end;
function PWM_GetDRVal(percent:real; dutyrange:longword):longword;
//dutyrange: pwm_dutyrange
//percent: 0-1
//output: 0-(pwm_dutyrange-1)
var res:longword;
begin
res:=0;
if ((dutyrange>0) and (percent>0) and (percent<=1)) then res:=round(percent*(dutyrange-1));
PWM_GetDRVal:=res;
end;
procedure PWM_WriteRange(gpio,range:longword);
begin
case gpio of
GPIO_PWM0,GPIO_PWM0A0,GPIO_PWM0Audio: BCM_SETREG(PWM0RNG,range); // HW PWM
GPIO_PWM1,GPIO_PWM1A0,GPIO_PWM1Audio: BCM_SETREG(PWM1RNG,range); // HW PWM
end; // case
end;
procedure PWM_Write(gpio,value:longword);
begin
case gpio of
GPIO_PWM0,GPIO_PWM0A0,GPIO_PWM0Audio: BCM_SETREG(PWM0DAT,value); // HW PWM
GPIO_PWM1,GPIO_PWM1A0,GPIO_PWM1Audio: BCM_SETREG(PWM1DAT,value); // HW PWM
end; // case
end;
procedure PWM_Write(var GPIO_struct:GPIO_struct_t; value:longword); // value: 0-(pwm_dutyrange-1)
begin
with GPIO_struct do
begin
with PWM do
begin
pwm_value:=pwm_GetMODVal(value,pwm_dutyrange); //value: 0-(pwm_dutyrange-1)
pwm_dutycycle_us:=pwm_GetDCSWVal(pwm_period_us,pwm_value,pwm_dutyrange);
pwm_restcycle_us:=0;
if pwm_period_us>pwm_dutycycle_us
then pwm_restcycle_us:=pwm_period_us-pwm_dutycycle_us
else pwm_dutycycle_us:=pwm_period_us;
pwm_period_ms:=trunc(pwm_period_us/1000);
if pwm_period_ms<=0 then pwm_period_ms:=1;
pwm_sigalt:=true;
(* writeln('PWM_Write:'+
' GPIO'+ Num2Str(gpio,0)+
' value:'+ Num2Str(pwm_value,0)+
' dtyrange:'+ Num2Str(pwm_dutyrange,0)+
' dtyperiod(us):'+ Num2Str(pwm_period_us,0)+
' dtycycl(us):'+ Num2Str(pwm_dutycycle_us,0)+
' dtyrest(us):'+ Num2Str(pwm_restcycle_us,0)
); *)
if (PWMHW IN portflags) then
begin
case gpio of
GPIO_PWM0,GPIO_PWM0A0,GPIO_PWM0Audio: BCM_SETREG(PWM0DAT,pwm_value,false,false); // HW PWM
GPIO_PWM1,GPIO_PWM1A0,GPIO_PWM1Audio: BCM_SETREG(PWM1DAT,pwm_value,false,false); // HW PWM
end; // case
end;
end; // with
end; // with
end;
function CLK_GetFreq(clksource:longword):real; // Hz
(*how to determine PLL freq:
http://blog.riyas.org/2014/01/raspberry-pi-as-simple-low-cost-rf-signal-generator-sweeper.html
http://raspberrypi.stackexchange.com/questions/1153/what-are-the-different-clock-sources-for-the-general-purpose-clocks
The clock frequencies were determined by experiment.
The oscillator (19.2 MHz) and PLLD (500 MHz) are unlikely to change.
Clock sources
0 0 Hz Ground
1 19.2 MHz oscillator
2 0 Hz testdebug0
3 0 Hz testdebug1
4 0 Hz PLLA
5 1000 MHz PLLC (changes with overclock settings)
6 500 MHz PLLD
7 216 MHz HDMI auxiliary
8-15 0 Hz Ground
The integer divider may be 2-4095. The fractional divider may be 0-4095.
There is (probably) no 25MHz cap for using non-zero mash values.
There are three general purpose clocks.
The clocks are named GPCLK0, GPCLK1, and GPCLK2.
Don't use GPCLK1 (it's probably used for the Ethernet clock). *)
var f:real;
begin
case clksource of
1 : f:= osc_freq_c; // OSC (19.2Mhz)
5 : f:= pllc_freq_c; // PLLC (1000Mhz changes with overclock settings)
6 : f:= plld_freq_c; // PLLD (500Mhz)
7 : f:= HDMI_freq_c; // HDMI (216Mhz auxiliary)
else f:= 0.0;
end; // case
//writeln('CLK_GetFreq corefreq:',(pllc_freq):0:5);
CLK_GetFreq:=f;
end;
function CLK_GetMinFreq:real; begin CLK_GetMinFreq:=CLK_GetFreq(1)/(4095.4095); end;
function CLK_GetMaxFreq:real; begin CLK_GetMaxFreq:=CLK_GetFreq(6)/(1.0); end;
function CLK_ValidFreq(freq_Hz:real):boolean;
begin CLK_ValidFreq:=((freq_Hz>=CLK_GetMinFreq) and (freq_Hz<=CLK_GetMaxFreq)); end;
function CLK_CheckFreq(freq_Hz:real; clksrc:longword; var divi,divf,mash:longword):boolean;
// !!todo!!, calc freq for mash>0
var _ok:boolean; da:real; mindivi:byte;
begin
_ok:=CLK_ValidFreq(freq_Hz);
if _ok and (freq_Hz>0) then
begin
case mash of
3: begin mindivi:=5; end;
2: begin mindivi:=3; end;
1: begin mindivi:=2; end;
else begin mindivi:=1; mash:=0; end;
end;
if mash<>0 then LOG_Writeln(LOG_ERROR,'CLK_CheckFreq: currently not implemented mash<>0');
da:=CLK_GetFreq(clksrc)/freq_Hz;
divi:=trunc(da); divf:=round(4096.0*(da-divi));
_ok:=(not ((divi>4095.0) or (divi<mindivi) or (divf>4095.0)));
// writeln('CLK_CheckFreq: freq(Hz):',freq_hz:0:2,' clksrc:',clksrc:0,' PLLfreq(Hz):',CLK_GetFreq(clksrc):0:1,' da:',da:0:2,' divi:',divi,' divf:',divf,' mash:',mash,' ok:',_ok);
end;
CLK_CheckFreq:=_ok;
end;
function CLK_GetSource(freq_Hz:real; var clksrc,divi,divf,mash:longword):boolean;
var _ok:boolean;
begin
_ok:=false; clksrc:=1; divi:=4095; divf:=4095;
if CLK_ValidFreq(freq_Hz) then
begin // find the best clk source // 6/1/7/5
if (not _ok) then begin clksrc:=6; _ok:=CLK_CheckFreq(freq_Hz,clksrc,divi,divf,mash); end;
if (not _ok) then begin clksrc:=1; _ok:=CLK_CheckFreq(freq_Hz,clksrc,divi,divf,mash); end;
if (not _ok) then begin clksrc:=7; _ok:=CLK_CheckFreq(freq_Hz,clksrc,divi,divf,mash); end;
if (not _ok) then begin clksrc:=5; _ok:=CLK_CheckFreq(freq_Hz,clksrc,divi,divf,mash); end;
end;
CLK_GetSource:=_ok;
end;
function CLK_GetRegIdx(mode:byte; var regctlidx,regdividx:longword):boolean;
var _ok:boolean;
begin
_ok:=false;
case mode of
0 : begin _ok:=true; regctlidx:=GMGP0CTL; regdividx:=GMGP0DIV; end;
1 : begin _ok:=true; regctlidx:=GMGP1CTL; regdividx:=GMGP1DIV; end;
2 : begin _ok:=true; regctlidx:=GMGP2CTL; regdividx:=GMGP2DIV; end;
3 : begin _ok:=true; regctlidx:=PWMCLKCTL; regdividx:=PWMCLKDIV; end;
end; // case
CLK_GetRegIdx:=_ok;
end;
function CLK_GetDivisor(regcont:longword):real;
begin
CLK_GetDivisor:=((regcont and $fff000) shr 12)+((regcont and $fff) shl 10);
end;
function CLK_GetMashValue(mode:byte):byte;
var regctl,regdiv:longword;
begin
CLK_GetRegIdx(mode,regctl,regdiv);
CLK_GetMashValue:=byte((BCM_GETREG(regctl) and $600) shr 9);
end;
function CLK_GetClkFreq(mode:byte; PLL_FREQ,FREQ_req:real;
var FREQ_O_min,FREQ_O_avg,FREQ_O_max:real;
var MASH:byte; var DIVIF:longword):boolean;
var DIVImin,DIVI,DIVF:longword; divisor:real; ok:boolean;
begin
ok:=false;
MASH:=CLK_GetMashValue(mode) and $3; // MashValue 0..3
DIVImin:=MASH+1; if MASH=3 then DIVImin:=5;
if abs(FREQ_req)<>0 then
begin
divisor:=PLL_FREQ/FREQ_req;
DIVI:=trunc(divisor) and $fff; DIVF:=round(frac(divisor)/1024) and $fff; // 2x12Bit values
if DIVI<DIVImin then DIVI:=DIVImin;
DIVIF:=((DIVI shl 12) or DIVF);
// writeln('divisor: ',divisor:0:5,' DIVImin:',DIVImin,' DIVI:',DIVI,' DIVF:',DIVF,' MASH:',MASH,' DIVIF:',DIVIF);
case MASH of
0 : begin
FREQ_O_max:=PLL_FREQ/DIVI;
FREQ_O_avg:=PLL_FREQ/DIVI;
FREQ_O_min:=PLL_FREQ/DIVI;
end;
1 : begin
FREQ_O_max:=PLL_FREQ/DIVI;
FREQ_O_avg:=PLL_FREQ/(DIVI+(DIVF shl 10));
FREQ_O_min:=PLL_FREQ/(DIVI+1);
end;
2 : begin
FREQ_O_max:=PLL_FREQ/(DIVI-1);
FREQ_O_avg:=PLL_FREQ/(DIVI+(DIVF shl 10));
FREQ_O_min:=PLL_FREQ/(DIVI+2);
end;
3 : begin
FREQ_O_max:=PLL_FREQ/(DIVI-3);
FREQ_O_avg:=PLL_FREQ/(DIVI+(DIVF shl 10));
FREQ_O_min:=PLL_FREQ/(DIVI+4);
end;
end; // case
ok:=(FREQ_O_max<=Clk_GetFreq(5));
end;
CLK_GetClkFreq:=ok;
end;
function CLK_Write(regctlidx,regdividx:longword; DIVI,DIVF,ctlmask:longword):boolean;
const wt_us=1; maxtry=1000; CLK_CTL_ENAB=$00000010;
var n:longword; ok:boolean;
begin
n:=0;
//writeln('CLK_Write: '+Num2Str(DIVI,0));
BCM_SETREG(regctlidx,BCM_PWD or $01,false,false); // stop clock
while ((BCM_GETREG(regctlidx) and $80)<>0) and (n<=maxtry) do // Wait for clock to be !BUSY
begin inc(n); delay_us(wt_us); end;
ok:=(n<maxtry);
if not ok then
begin
LOG_Writeln(LOG_WARNING,'CLK_Write: take to long time to get ready '+Num2Str(n,0));
delay_msec(1);
end
else if (n>100) then LOG_Writeln(LOG_WARNING,'CLK_Write: n:'+Num2Str(n,0));
BCM_SETREG(regdividx,(BCM_PWD or ((DIVI and $0fff) shl 12) or (DIVF and $0fff)),false,false); // set clock divider
if ctlmask<>0 then
begin
delay_us(10);
BCM_SETREG(regctlidx,(BCM_PWD or (ctlmask and (not CLK_CTL_ENAB))),false,false);
end;
delay_us(10);
BCM_SETREG(regctlidx,(BCM_PWD or ctlmask or CLK_CTL_ENAB),false,false); // start clock
CLK_Write:=ok;
end;
function PWM_ClkWrite(regctlidx,regdividx:longword; DIVI:longword):boolean;
const wt_us=1; maxtry=1000;
var pwm_control:longword; ok:boolean;
begin
//writeln('PWM_ClkWrite: '+Num2Str(DIVI,0));
pwm_control:=BCM_GETREG(PWMCTL); // save register content
//writeln('PWMCTL: 0x',HexStr(pwm_control,8));
BCM_SETREG(PWMCTL,0,false,false); // stop PWM
ok:=CLK_Write(regctlidx,regdividx,DIVI,0,$01);// $01: clock src from osci
BCM_SETREG(PWMCTL,pwm_control,false,false); // restore PWM_CONTROL
PWM_ClkWrite:=ok;
end;
function PWM_GetMinFreq(dutycycle:longword):real;
var r:real;
begin
if dutycycle<>0 then r:=(CLK_GetFreq(1)/(PWM_DIVImax*dutycycle)) else r:=0;
PWM_GetMinFreq:=r;
end;
function PWM_GetMaxFreq(dutycycle:longword):real;
var r:real;
begin
if dutycycle<>0 then r:=(CLK_GetFreq(1)/(PWM_DIVImin*dutycycle)) else r:=0;
PWM_GetMaxFreq:=r;
end;
function PWM_GetMaxDtyC(freq:real):longword;
var lw:longword;
begin
if freq<>0 then lw:=round(CLK_GetFreq(1)/(PWM_DIVImin*freq)) else lw:=0;
PWM_GetMaxDtyC:=lw;
end;
function PWM_GetDtyRangeVal(var GPIO_struct:GPIO_struct_t; DutyCycle:real):longword;
// DutyCycle: 0..1
var dcr:real; drlw:longword;
begin
dcr:=DutyCycle; if dcr<0 then dcr:=0.0; if dcr>1.0 then dcr:=1.0;
with GPIO_struct.PWM do
begin
if pwm_dutyrange>1 then drlw:=round(dcr*(pwm_dutyrange-1)) else drlw:=0;
end; // with
PWM_GetDtyRangeVal:=drlw;
end;
procedure PWM_setClock(var GPIO_struct:GPIO_struct_t);
// same clock for PWM0 and PWM1. Needs only to be set once
var DIVI:longword; r:real;
begin
with GPIO_struct do
begin
with PWM do
begin
if (PWMHW IN portflags) then
begin
DIVI:=PWM_DIVImin; // default
r:=pwm_freq_Hz*pwm_dutyrange;
if (r>0) then DIVI:=round(CLK_GetFreq(1)/r) else DIVI:=0;
// writeln('PWM_setClock0: ',CLK_GetFreq(1):0:5,' freq(Hz):',pwm_freq_Hz:0:5,' dty:',pwm_dutyrange:0,' DIVI:',DIVI);
if (DIVI<PWM_DIVImin) or (DIVI>PWM_DIVImax) then
begin
LOG_Writeln(LOG_ERROR,'PWM_setClock['+Num2Str(gpio,0)+'|'+Num2Str(DIVI,0)+'/'+Num2Str(PWM_DIVImin,0)+'/'+Num2Str(PWM_DIVImax,0)+']: desired PWM-Freq '+Num2Str(pwm_freq_Hz,0,2)+'Hz will not be set. use other duty cycle');
if (DIVI<PWM_DIVImin) then DIVI:=PWM_DIVImin else DIVI:=PWM_DIVImax;
end;
// writeln('PWM_setClock1: ',DIVI);
PWM_ClkWrite(PWMCLKCTL,PWMCLKDIV,DIVI);
end;
end; // with
end; // with
end;
function GPIO_PortFlags2String(flgs:s_port_flags):string;
var j:t_port_flags; sh:string;
begin
sh:='';
for j IN flgs do
sh:=sh+GetEnumName(TypeInfo(t_port_flags),ord(t_port_flags(j)))+' ';
GPIO_PortFlags2String:=sh;
end;
procedure GPIO_ShowStruct(var GPIO_struct:GPIO_struct_t);
begin
with GPIO_struct do
begin
writeln('GPIO_ShowStruct: ',description,' Portflags:',GPIO_PortFlags2String(portflags),' initok:',initok,' Simulation:',simulation);
writeln('HWPin:',HWPin,' GPIO',gpio:0,' nr:',nr:0,' State:',ein);
writeln('idxofs_1Bit:0x',HexStr(idxofs_1Bit,2),' mask_1Bit:0x',HexStr(mask_1Bit,8),' idxofs_3Bit:0x',HexStr(idxofs_3Bit,2),' mask_3Bit:0x',HexStr(mask_3Bit,8));
writeln('pwm_mode:',PWM.pwm_mode,' pwm_freq:',PWM.pwm_freq_hz:0:2,' pwm_dutyrange:',PWM.pwm_dutyrange,' value:',PWM.pwm_value,
' pwm_dutycycle_us:',PWM.pwm_dutycycle_us,' pwm_period_us:',PWM.pwm_period_us);
end;
end;
procedure Thread_SetName(name:string);
const PR_SET_NAME=$0f;
var thread_name:Thread_name_t;
begin
thread_name:=copy(name+#$00,1,16);
if thread_name<>'' then
begin
{$IFDEF LINUX}
if FpPrCtl(PR_SET_NAME,@thread_name[1],nil,nil,nil)<>0 then
LOG_Writeln(LOG_ERROR,'Thread_SetName: can not set name '+name);
{$ENDIF}
end;
end;
procedure Thread_ShowStruct(var ThreadCtrl:Thread_Ctrl_t);
var n:longint; sh:string;
begin
with ThreadCtrl do
begin
SAY(LOG_INFO,'');
SAY(LOG_INFO,'ThreadInfo: '+ThreadInfo);
// SAY(LOG_INFO,'ThreadID: ',TThreadID);
SAY(LOG_INFO,'ThreadRunning: '+Bool2Str(ThreadRunning)+' TermThread: '+Bool2Str(TermThread));
SAY(LOG_INFO,'ThreadFunc: 0x'+HexStr(ThreadFunc));
SAY(LOG_INFO,'ThreadTimeOut: '+FormatDateTime('YYYYMMDD hh:mm:ss.zzz',ThreadTimeOut));
SAY(LOG_INFO,'ThreadCmdStr: '+ThreadCmdStr);
SAY(LOG_INFO,'ThreadRetStr: '+ThreadRetStr);
SAY(LOG_INFO,'ThreadRetCode: '+Num2Str(ThreadRetCode,0));
SAY(LOG_INFO,'ThreadProgressOld: '+Num2Str(ThreadProgressOld,0));
SAY(LOG_INFO,'ThreadProgress: '+Num2Str(ThreadProgress,0));
sh:='UsrData[0-4]: '; for n:=0 to 4 do sh:=sh+Num2Str(UsrData[n],0)+' '; SAY(LOG_INFO,sh);
sh:='ThreadPara[0-4]: '; for n:=0 to 4 do sh:=sh+Num2Str(ThreadPara[n],0)+' '; SAY(LOG_INFO,sh);
sh:='ThreadParaStr[0-4]:'; for n:=0 to 4 do sh:=sh+ThreadParaStr[n]+' '; SAY(LOG_INFO,sh);
SAY(LOG_INFO,'');
end;
end;
procedure Thread_InitStruct(var ThreadCtrl:Thread_Ctrl_t);
begin
with ThreadCtrl do
begin
TermThread:=true; ThreadRunning:=false; ThreadRetCode:=0;
ThreadRetStr:=''; ThreadInfo:='';
ThreadProgress:=0; ThreadProgressOld:=-maxint; ThreadTimeOut:=now;
ThreadID:=TThreadID(0);
end; // with
end;
procedure Thread_InitStruct2(var ThreadCtrl:Thread_Ctrl_t; ThFunc:TThFunctionOneArgCall);
var n:longint;
begin
with ThreadCtrl do
begin
ThreadFunc:=ThFunc;
ThreadCmdStr:='';
for n:=0 to 4 do
begin
ThreadPara[n]:=0; UsrData[n]:=0; ThreadParaStr[n]:='';
end;
end; // with
end;
procedure Thread_InitStruct0(var ThreadCtrl:Thread_Ctrl_t);
begin
Thread_InitStruct (ThreadCtrl);
Thread_InitStruct2(ThreadCtrl,nil);
end;
function Thread_Start(var ThreadCtrl:Thread_Ctrl_t; funcadr:TThreadFunc;
paraadr:pointer; delaymsec:longword; prio:longint):boolean;
begin
with ThreadCtrl do
begin
Thread_InitStruct(ThreadCtrl); TermThread:=false;
ThreadID:=BeginThread(funcadr,paraadr);
ThreadRunning:=(ThreadID<>TThreadID(0));
if ThreadRunning and (delaymsec>0) then delay_msec(delaymsec); // let thread time to start
if ThreadRunning and (prio<>0) then ThreadSetPriority(ThreadID,prio);
if ThreadRunning then SetTimeOut(ThreadTimeOut,15000);
Thread_Start:=ThreadRunning;
end;
end;
function Thread_End(var ThreadCtrl:Thread_Ctrl_t; waitmsec:longword):boolean;
begin
with ThreadCtrl do
begin
TermThread:=true;
// if ThreadRunning then ThreadRunning:=(WaitForThreadTerminate(ThreadID,waitmsec)=0); // does not work on raspian
if (waitmsec>0) then
begin
delay_msec(waitmsec);
if ThreadRunning then ThreadRunning:=(not (KillThread(ThreadID)=0));
end else ThreadRunning:=false;
Thread_InitStruct(ThreadCtrl);
Thread_End:=ThreadRunning;
end;
end;
procedure HDMI_Switch(ein:boolean);
var sh:string;
begin
sh:='tvservice '; if ein then sh:=sh+'-p' else sh:=sh+'-o';
call_external_prog(LOG_NONE,sh,sh);
end;
procedure PWM_End(var GPIO_struct:GPIO_struct_t);
var regsav:longword;
begin
with GPIO_struct do
begin
ThreadCtrl.TermThread:=true;
if (PWMHW IN portflags) then
begin // HW PWM
if GPIO_HWPWM_capable(gpio) then
begin
regsav:=BCM_GETREG(PWMCTL); // save ctl register
// writeln('PWM_End: PWMCTL 0x',HexStr(regsav,8));
if GPIO_HWPWM_capable(gpio,0) // // maskout Bits for channel1/2
then regsav:=(regsav and $0000ff00) and (not PWM0_ENABLE)
else regsav:=(regsav and $000000ff) and (not PWM1_ENABLE);
// writeln('PWM_End: PWMCTL 0x',HexStr(regsav,8));
BCM_SETREG(PWMCTL,regsav,false,false); // Disable channel PWM
end;
end
else Thread_End(ThreadCtrl,100);
end; // with
end;
procedure PWM_SetStruct(var GPIO_struct:GPIO_struct_t; mode:byte; freq_Hz,freq_min,freq_max:real; dutyrange,startval:longword);
begin
with GPIO_struct do
begin
with PWM do
begin
pwm_mode:=mode;
pwm_freq_hz:=freq_Hz; PWM_freq_min:=freq_min; PWM_freq_max:=freq_max;
if (PWMHW IN portflags) then
begin
Limits(PWM_freq_min,PWM_GetMinFreq(dutyrange), PWM_GetMaxFreq(dutyrange));
Limits(PWM_freq_max,PWM_freq_min, PWM_GetMaxFreq(dutyrange));
end
else
begin // SW PWM
Limits(PWM_freq_min,10, 100);
Limits(PWM_freq_max,PWM_freq_min, 150);
end;
Limits( PWM_freq_hz, PWM_freq_min, PWM_freq_max);
with ThreadCtrl do begin TermThread:=true; ThreadRunning:=false; ThreadID:=TThreadID(0); end;
if (pwm_freq_hz<>0) then pwm_period_us:=round(1000000/pwm_freq_hz) else pwm_period_us:=0;
pwm_dutyrange:=dutyrange;
// PWM_Write(GPIO_struct,startval);
(* pwm_value:=startval;
pwm_dutycycle_us:=pwm_GetDCSWVal(pwm_period_us,pwm_value,pwm_dutyrange);
if pwm_period_us>pwm_dutycycle_us
then pwm_restcycle_us:=pwm_period_us-pwm_dutycycle_us
else pwm_dutycycle_us:=pwm_period_us;
pwm_period_ms:=trunc(pwm_period_us/1000);
if pwm_period_ms<=0 then pwm_period_ms:=1;*)
end; // with
end;
end;
procedure PWM_SetStruct(var GPIO_struct:GPIO_struct_t; mode:byte; freq_Hz:real; dutyrange,startval:longword);
begin PWM_SetStruct(GPIO_struct,PWM_MS_MODE,freq_Hz,0,0,dutyrange,0); end;
procedure PWM_SetStruct(var GPIO_struct:GPIO_struct_t);
//HW-PWM: Mark Space mode // set pwm hw clock div to 32 (19.2Mhz/32 = 600kHz) // Default range of 1024
//SW-PWM: Mark Space mode // set pwm sw clock to 50Hz // DutyCycle range of 1000 (0-999)
const dcycl=1000;
var freq_Hz:real;
begin
if (PWMHW IN GPIO_struct.portflags) then freq_Hz:=PWM_GetMaxFreq(dcycl) else freq_Hz:=50;
PWM_SetStruct(GPIO_struct,PWM_MS_MODE,freq_Hz,dcycl,0);
end;
function PWM_Setup(var GPIO_struct:GPIO_struct_t):boolean;
var regsav:longword;
begin
with GPIO_struct do
begin
if initok and (OUTPUT IN portflags) then
begin
initok:=false;
if (PWMHW IN portflags) then
begin // HW PWM
case gpio of
GPIO_PWM0,GPIO_PWM0A0,GPIO_PWM1A0,GPIO_PWM0Audio,GPIO_PWM1Audio,
GPIO_PWM1 : begin // PWM0:Pin12:GPIO18 PWM1:Pin35:GPIO19
initok:=true;
// writeln('PWM_Setup (HW):'); GPIO_ShowStruct(GPIO_struct);
GPIO_set_PINMODE(gpio,PWMHW);
regsav:=BCM_GETREG(PWMCTL); // save ctl register
BCM_SETREG(PWMCTL,0,false,false); // stop PWM
PWM_setClock(GPIO_struct); // set clock external before PWM_Setup
// writeln('PWM_Setup: PWMCTL 0x',HexStr(regsav,8));
// writeln('PWM_Setup: pwm_dutyrange ',PWM.pwm_dutyrange);
if GPIO_HWPWM_capable(gpio,0) then
begin
BCM_SETREG(PWM0RNG,PWM.pwm_dutyrange,false,false); delay_us(10); // set max value for duty cycle
regsav:=regsav and $0000ff00; // maskout Bits for channel1
regsav:=regsav or PWM0_ENABLE;
if ((PWM.pwm_mode and PWM_MS_MODE)<>0) then regsav:=regsav or PWM0_MS_MODE;
if ((PWM.pwm_mode and PWM_USEFIFO)<>0) then regsav:=regsav or PWM0_USEFIFO;
if ((PWM.pwm_mode and PWM_POLARITY)<>0) then regsav:=regsav or PWM0_REVPOLAR;
if ((PWM.pwm_mode and PWM_RPTL)<>0) then regsav:=regsav or PWM0_REPEATFF;
if ((PWM.pwm_mode and PWM_SERIALIZER)<>0) then regsav:=regsav or PWM0_SERIAL;
end
else
begin
BCM_SETREG(PWM1RNG,PWM.pwm_dutyrange,false,false); delay_us(10);
regsav:=regsav and $000000ff; // maskout Bits for channel2
regsav:=regsav or PWM1_ENABLE;
if ((PWM.pwm_mode and PWM_MS_MODE)<>0) then regsav:=regsav or PWM1_MS_MODE;
if ((PWM.pwm_mode and PWM_USEFIFO)<>0) then regsav:=regsav or PWM1_USEFIFO;
if ((PWM.pwm_mode and PWM_POLARITY)<>0) then regsav:=regsav or PWM1_REVPOLAR;
if ((PWM.pwm_mode and PWM_RPTL)<>0) then regsav:=regsav or PWM1_REPEATFF;
if ((PWM.pwm_mode and PWM_SERIALIZER)<>0) then regsav:=regsav or PWM1_SERIAL;
end;
// writeln('PWM_Setup: pwm_value ',PWM.pwm_value);
PWM_Write (GPIO_struct,PWM.pwm_value); // set start value
// writeln('PWM_Setup: PWMCTL 0x',HexStr(regsav,8)); //
BCM_SETREG(PWMCTL,regsav,false,false); // Enable channel PWM
end;
else Log_Writeln(LOG_ERROR,'PWM_Setup: GPIO'+Num2Str(gpio,0)+' not supported for HW PWM');
end;
end
else
begin // SW PWM
case gpio of
-999..-1: Log_Writeln(LOG_ERROR,'PWM_Setup: GPIO'+Num2Str(gpio,0)+' not supported for PWM');
else begin
if (gpio>=0) and (PWMSW IN portflags) then
begin
initok:=true;
GPIO_set_PINMODE(gpio,OUTPUT); portflags:=portflags+[OUTPUT];
// writeln('PWM_Setup (SW):'); GPIO_ShowStruct(GPIO_struct);
// Start SW PWM Thread
Thread_Start(ThreadCtrl,@pwm_SW_Thread,addr(GPIO_struct),100,-1);
(* with ThreadCtrl do
begin
TermThread:=false; ThreadRunning:=true; // Start SW PWM Thread
ThreadID:=BeginThread(@pwm_SW_Thread,addr(GPIO_struct));
end;
delay_msec(100); // let SW-Threads start
*)
end
else Log_Writeln(LOG_ERROR,'PWM_Setup: wrong neg. GPIO Error Code: '+Num2Str(gpio,0)+' '+GPIO_PortFlags2String(portflags));
end;
end; // case
end;
end
else Log_Writeln(LOG_ERROR,'PWM_Setup: GPIO_struct is not initialized');
end;
PWM_Setup:=GPIO_struct.initok;
end;
function TIM_Setup(timr_freq_Hz:real):real;
var _ok:boolean; _divi:longword; _f:real;
begin
_ok:=false; _f:=0;
if timr_freq_Hz>0 then
begin
_divi:=round(CLK_GetFreq(5)/timr_freq_Hz); //250MHz CoreFreq/timr_freq_Hz
if (_divi>0) and (_divi<=$400) then
begin
_f:=CLK_GetFreq(5)/_divi;
dec(_divi); _ok:=true; // the timer divide (10Bit) is base clock / (divide+1)
BCM_SETREG(APMPreDivider,_divi);
BCM_SETREG(APMCTL, $280); // Free running counter Enabled; Timer enable
end;
end;
if not _ok then
LOG_Writeln(LOG_ERROR,'TIM_Setup: can not set freq: '+Num2Str(timr_freq_Hz,0,0));
TIM_Setup:=_f;
end;
procedure TIM_Test; // 1MHz
begin
TIM_Setup(1000000);
end;
function OSC_Setup(_gpio:longint; pwm_freq_Hz,pwm_dty:real):longint;
var flgh:s_port_flags; gpio_struct:GPIO_struct_t;
pwm_dutyrange,_dtyw:longint;
begin
if GPIO_FCTOK(_gpio,[PWMHW]) then flgh:=[PWMHW] else flgh:=[PWMSW];
if pwm_dty<0 then pwm_dty:=0; if pwm_dty>1 then pwm_dty:=1;
if ((PWMSW IN flgh) and (pwm_freq_Hz>200)) then pwm_freq_Hz:=200;
pwm_dutyrange:=PWM_GetMaxDtyC(pwm_freq_Hz); _dtyw:=round(pwm_dutyrange*pwm_dty);
writeln('OSC_Setup: ',(PWMHW IN flgh),' f:',pwm_freq_Hz:0:1,'Hz range:',pwm_dutyrange:0,' dty:',_dtyw:0);
GPIO_SetStruct (gpio_struct,1,_gpio,'OSC',[OUTPUT]+flgh);
PWM_SetStruct (gpio_struct,PWM_MS_MODE,pwm_freq_Hz,pwm_dutyrange,_dtyw);
PWM_setClock (gpio_struct);
if not GPIO_Setup(gpio_struct) then pwm_dutyrange:=-1;
OSC_Setup:=pwm_dutyrange;
end;
procedure OSC_Write(_gpio,pwm_dutyrange:longint; pwm_dty:real);
begin
if pwm_dutyrange>0 then
begin
if pwm_dty<0 then pwm_dty:=0; if pwm_dty>1 then pwm_dty:=1;
PWM_Write(_gpio,round(pwm_dty*(pwm_dutyrange-1)));
end else LOG_Writeln(LOG_ERROR,'OSC_Write: invalid pwm_dutyrange '+Num2Str(pwm_dutyrange,0));
end;
procedure FREQ_CounterReset(var FREQ_Struct:FREQ_Determine_t);
begin
with FREQ_Struct do begin fcnt:=0; fcntold:=0; fTurnRate_Hz:=0; fdet_enab:=false; end;
end;
procedure FREQ_InitStruct(var FREQ_Struct:FREQ_Determine_t; detint_ms:longint);
begin
with FREQ_Struct do
begin
fSyncTime:=now; fdet_ms:=detint_ms; fdet_enab:=false;
FREQ_CounterReset( FREQ_Struct);
end; // with
end;
procedure FREQ_DetTurnRate(var FREQ_Struct:FREQ_Determine_t; steps:longint);
var ms:longint;
begin
with FREQ_Struct do
begin
fcnt:=fcnt+steps;
if TimeElapsed(fSyncTime,fdet_ms) then
begin
if fdet_enab then
begin
ms:=MilliSecondsBetween(now,fSyncTime);
if (ms<>0) then
begin
fTurnRate_Hz:=((fcnt-fcntold)*1000/ms); fcntold:=fcnt;
if (fTurnRate_Hz=0) then FREQ_CounterReset(FREQ_Struct); // new SF 22.5.2018
end;
end
else
begin
FREQ_CounterReset(FREQ_Struct);
fdet_enab:=true; // prepare fdet on next step update
end;
end;
end; // with
end;
function WAVE_InitArray(wavelist:TStringList; var wa:WAVE_Array_t; var valmin,valmax:real):longint;
//IN: StringList which has a number in each line
//OUT: filled Array, min,max value
//Result:ArrayCount
var res,n:longint; r:real;
begin
res:=0; valmin:=maxreal; valmax:=-maxreal;
SetLength(wa,wavelist.count);
for n:=1 to wavelist.count do
begin
if Str2Num(Trimme(wavelist[n-1],3),r) then
begin
wa[res]:=r;
if r<valmin then valmin:=r;
if r>valmax then valmax:=r;
inc(res);
end;
end;
if res<>Length(wa) then SetLength(wa,res);
WAVE_InitArray:=res;
end;
function WAVE_InitArray(var wa:WAVE_Array_t; wavemode:WAVE_RampShape_t; valstart,valend:real; valcnt:longint; dtycycle:real):longint;
const k=1.0; Scnt=21;
var ok:boolean; res,n,siglow,sighig:longint; delta,x,x0:real;
begin
ok:=false;
if valcnt>0 then
begin
SetLength(wa,valcnt); delta:=0;
if (not ok) and (wavemode IN [LIN_Ramp,LIN_Triangle,LIN_SawTooth]) then
begin
ok:=true;
if (valcnt>1) then delta:=(valend-valstart)/(valcnt-1);
wa[valcnt-1]:=valend; wa[0]:=valstart;
for n:=1 to (valcnt-2) do wa[n]:=wa[n-1]+delta;
end;
if (not ok) and (wavemode IN [SINusoidal]) then
begin
ok:=true;
if (valcnt>0) then delta:=(2*pi)/(valcnt-0); // prevent 2x same value (0)
for n:= 0 to (valcnt-1) do wa[n]:=(valend-valstart)*(sin(n*delta))+valstart;
end;
if (not ok) and (wavemode IN [LIN_Square]) then
begin
if (dtycycle<0) or (dtycycle>1) then dtycycle:=0.5; // 0-100% default 50%
sighig:=round(valcnt*dtycycle);
siglow:=valcnt-sighig;
ok:=((sighig>0) or (siglow>0));
if ok then
begin
for n:= 1 to siglow do wa[n-1]:=valstart;
for n:= siglow to (valcnt-1) do wa[n]:=valend;
end;
end;
if (not ok) and (wavemode IN [S_Shape]) then
begin // https://en.wikipedia.org/wiki/Logistic_function
if (valcnt>0) then
begin
ok:=true; x0:=(valcnt-1)/2; x:=0;
delta:=Scnt/valcnt;
for n:= 0 to (valcnt-1) do
begin
wa[n]:=1/(1+exp(-k*(x-x0)));
if n=0 then wa[n]:=0.0;
if n>=(valcnt-1) then wa[n]:=1.0;
wa[n]:=(valend-valstart) * wa[n] + valstart;
x:=x+delta;
end;
end;
end;
(* if (not ok) and (wavemode IN [S_Shape]) then
begin // http://www.pmean.com/04/scurve.html // old approach
if (valcnt>0) then
begin
ok:=true; x:=-10;
delta:=abs(x*2.0)/valcnt;
for n:= 0 to (valcnt-1) do
begin
wa[n]:=(valend-valstart) * roundto(1.0/(1.0+exp(-k*x)),4) + valstart;
x:=x+delta;
end;
end;
end; *)
if not ok then for n:=1 to valcnt do wa[n-1]:=valstart;
end else SetLength(wa,0);
res:=Length(wa); if not ok then res:=-1;
WAVE_InitArray:=res;
end;
function WAVE_SetIdx(var wstruct:WAVE_Signal_Struct_t; var wa:WAVE_Array_t; startidx:longint):boolean;
var ok:boolean;
begin
ok:=false;
with wstruct do
begin
idx:=startidx;
ok:=((idx>=0) and (idx<Length(wa)));
if up then dec(idx) else inc(idx);
end;
WAVE_SetIdx:=ok;
end;
procedure WAVE_Enable(var wstruct:WAVE_Signal_Struct_t; enab:boolean); begin wstruct.enable:=enab; end;
procedure WAVE_InitStruct(var wstruct:WAVE_Signal_Struct_t; var wa:WAVE_Array_t; wavemode:WAVE_RampShape_t; intervall_ms:longint);
begin
with wstruct do
begin
timer:=now;
int_ms:=intervall_ms;
mode:=wavemode;
enable:=false;
up:=true;
// if not WAVE_SetIdx(wstruct,wa,0) then LOG_Writeln(LOG_ERROR,'WAVE_IniStruct: startidx vs. size of WAVE_Array');
end;
end;
procedure WAVE_Show(var wstruct:WAVE_Signal_Struct_t; var wa:WAVE_Array_t);
var n:longint;
begin
writeln;
with wstruct do
begin
writeln('WAVE_Show:');
writeln('mode:',GetEnumName(TypeInfo(WAVE_RampShape_t),ord(mode)),' interval:',int_ms:0,' enable:',enable,' up:',up,' nextidx:',idx+1);
end; // with
for n:=1 to Length(wa) do writeln((n-1):3,' ',wa[n-1]:7:3);
writeln;
end;
//(LIN_Ramp,LIN_Triangle,LIN_SawTooth,LIN_Square,SINusoidal);
function WAVE_GetIdx(var wstruct:WAVE_Signal_Struct_t; var wa:WAVE_Array_t):boolean;
var ok:boolean;
begin
ok:=true;
with wstruct do
begin
if (Length(wa)>=1) and enable then
begin
if up then
begin // direction up: idx will increase
inc(idx);
if idx>=length(wa) then
begin
case mode of
LIN_Ramp: begin idx:=Length(wa)-1; end; // remain at highest idx
LIN_Triangle: begin // symmetric linear waveform, change up/down direction
if idx>=2 then idx:=Length(wa)-2 else idx:=0; up:=false;
end;
else begin idx:=0; end; // start again from 1. indx
end; // case
end;
end
else
begin // direction down: idx will decrease
dec(idx);
if idx<=0 then begin up:=true; idx:=0; end;
end;
SetTimeOut(timer,int_ms);
end else ok:=false;
end; // with
WAVE_GetIdx:=ok;
end;
procedure WAVE_Test;
const iv_ms=500; valcnt=8; dty=0.5; valstart=0; valend=1; startidx=0;
var n,j:longint; wstruct:WAVE_Signal_Struct_t; wa:WAVE_Array_t;
begin
for j:= ord(low(WAVE_RampShape_t)) to ord(high(WAVE_RampShape_t)) do
begin // test all wave shapes
// useful valcnt to S_Shape: 21
write('WAVE_Test: '+GetEnumName(TypeInfo(WAVE_RampShape_t),j)+' '+
'valcnt:'+ Num2Str(valcnt,0)+' '+
'valstart:'+Num2Str(valstart,0,3)+' '+
'valend:'+ Num2Str(valend,0,3)+' '+
'idxstart:'+Num2Str(startidx,0)+' '+
'interval:'+Num2Str(iv_ms,0)+'ms'
);
if j=ord(LIN_Square) then write(' DtyCycle:'+Num2Str(dty*100,0,0)+'%');
writeln;
if WAVE_InitArray(wa,WAVE_RampShape_t(j),valstart,valend,valcnt,dty)>0 then
begin
WAVE_InitStruct(wstruct,wa,WAVE_RampShape_t(j),iv_ms);
WAVE_SetIdx (wstruct,wa,0);
WAVE_Enable (wstruct,true);
n:=0;
while (n<=(2*valcnt-1)) do
begin // 2 full cycles
with wstruct do
begin
if TimeElapsed(timer) then
begin // every 'iv_ms' a new idx to address wa[idx]
if enable and WAVE_GetIdx(wstruct,wa)
then writeln('WAVE_Test['+Num2Str(n,2)+']: '+Num2Str(wa[idx],6,3))
else LOG_Writeln(LOG_ERROR,'WAVE_Test: #2');
inc(n);
end else sleep(10);
end; // with
end; // while
end else LOG_Writeln(LOG_ERROR,'WAVE_Test: #1');
writeln;
end; // for
SetLength(wa,0);
end;
procedure FRQ_Switch(var GPIO_struct:GPIO_struct_t; ein:boolean);
var regsav:longword;
begin
with GPIO_struct do
begin
if ein then
begin // freq on
Log_Writeln(Log_ERROR,'FRQ_ON: currently not implemented'); /// !!!!! TODO !!!!!
// ThreadCtrl.TermThread:=true;
if (FRQHW IN portflags) then
begin
// regsav:=(BCM_GETREG(FRQ_CTLIdx) and $70f); // mask out Enable and unused Bits
// BCM_SETREG(FRQ_CTLIdx,(BCM_PWD or regsav),false,false); // Disable clock
end;
end
else
begin // freq off
ThreadCtrl.TermThread:=true;
if (FRQHW IN portflags) then
begin
regsav:=(BCM_GETREG(FRQ_CTLIdx) and $70f); // mask out Enable and unused Bits
BCM_SETREG(FRQ_CTLIdx,(BCM_PWD or regsav),false,false); // Disable clock
end;
end;
end; // with
end;
function FRQ_GetClkRegIdx(gpio:longint; var mode:byte):boolean;
var _ok:boolean;
begin
_ok:=true; mode:=$ff;
case gpio of // set clocksource
GPIO_FRQ04_CLK0,GPIO_FRQ20_CLK0,
GPIO_FRQ32_CLK0,GPIO_FRQ34_CLK0: mode:=0;
GPIO_FRQ05_CLK1,GPIO_FRQ21_CLK1,
GPIO_FRQ42_CLK1,GPIO_FRQ44_CLK1: mode:=1;
GPIO_FRQ06_CLK2,GPIO_FRQ43_CLK2: mode:=2;
else _ok:=false;
end; // case
if not _ok then LOG_Writeln(LOG_ERROR,'FRQ_GetClkRegIdx: no clock GPIO'+Num2Str(gpio,0));
FRQ_GetClkRegIdx:=_ok;
end;
procedure FRQ_SetStruct(var GPIO_struct:GPIO_struct_t; freq_Hz,freq_min,freq_max:real);
begin
with GPIO_struct do
begin
FRQ_freq_Hz:=freq_Hz; FRQ_freq_min:=freq_min; FRQ_freq_max:=freq_max;
Limits(FRQ_freq_min,CLK_GetMinFreq,CLK_GetMaxFreq);
Limits(FRQ_freq_max,FRQ_freq_min, CLK_GetMaxFreq);
Limits(FRQ_freq_Hz, FRQ_freq_min, FRQ_freq_max);
end; // with
end;
procedure FRQ_SetStruct(var GPIO_struct:GPIO_struct_t; freq_Hz:real);
begin FRQ_SetStruct(GPIO_struct,freq_Hz,CLK_GetMinFreq,CLK_GetMaxFreq); end;
procedure FRQ_SetStruct(var GPIO_struct:GPIO_struct_t);
begin FRQ_SetStruct(GPIO_struct,CLK_GetMinFreq); end;
function FRQ_Setup(var GPIO_struct:GPIO_struct_t):boolean;
var _mode:byte; _clksrc,_msk,_divi,_divf,_mash:longword;
begin
with GPIO_struct do
begin
// initok:=CLK_ValidFreq(freq_Hz);
initok:=InLimits(FRQ_freq_Hz,FRQ_freq_min,FRQ_freq_max);
if initok and (FRQHW IN portflags) then
begin
_mash:=0;
initok:=CLK_GetSource(FRQ_freq_Hz,_clksrc,_divi,_divf,_mash);
if initok then
begin
//writeln('FRQ_Setup: freq(Hz):',FRQ_freq_Hz:0:2,' divi:0x',HexStr(_divi,3),' divf:0x',HexStr(_divf,3),' clksrc:',_clksrc:0,' mash:',_mash:0);
initok:=FRQ_GetClkRegIdx(gpio,_mode);
if initok then initok:=CLK_GetRegIdx(_mode,FRQ_CTLIdx,FRQ_DIVIdx);
if initok then
begin
initok:=false;
if (ALT0 IN portflags) then begin GPIO_set_ALT(gpio,ALT0); initok:=true; end;
if (ALT1 IN portflags) then begin GPIO_set_ALT(gpio,ALT1); initok:=true; end;
if (ALT2 IN portflags) then begin GPIO_set_ALT(gpio,ALT2); initok:=true; end;
if (ALT3 IN portflags) then begin GPIO_set_ALT(gpio,ALT3); initok:=true; end;
if (ALT4 IN portflags) then begin GPIO_set_ALT(gpio,ALT4); initok:=true; end;
if (ALT5 IN portflags) then begin GPIO_set_ALT(gpio,ALT5); initok:=true; end;
if not initok then LOG_Writeln(LOG_ERROR,'FRQ_Setup['+Num2Str(gpio,0)+']: ALTx');
_msk:=((_mash and $3) shl 9) or (_clksrc and $0f); // set mash and clk-src
if initok then initok:=CLK_Write(FRQ_CTLIdx,FRQ_DIVIdx,_divi,_divf,_msk);
// writeln('Mash:0x',HexStr(CLK_GetMashValue(_mode),2),' mode:',_mode,' clksrc:',_clksrc);
if not initok then LOG_Writeln(LOG_ERROR,'FRQ_Setup['+Num2Str(gpio,0)+']: CLK_Write');
end else LOG_Writeln(LOG_ERROR,'FRQ_Setup['+Num2Str(gpio,0)+']: CLK_GetRegIdx');
end else LOG_Writeln(LOG_ERROR,'FRQ_Setup['+Num2Str(gpio,0)+']: CLK_GetSource');
end else LOG_Writeln(LOG_ERROR,'FRQ_Setup['+Num2Str(gpio,0)+']: for freq(Hz): '+Num2Str(FRQ_freq_Hz,0,2)+' not possible');
FRQ_Setup:=initok;
end; // with
end;
procedure FRQ_WaveTest; // !!!! not completed !!!!!
const gpio=4; maxcnt=50; scal=100; freqHz=100000;
type t_MM = array of longword;
var GPIO_struct:GPIO_struct_t; range:t_MM; n:longword;
procedure FillWaveTable;
var step:real; i:longword;
begin
step:=(2*pi)/maxcnt;
for i:= 0 to (maxcnt-1) do range[i]:=round(scal*(sin(i*step)+1));
end;
begin
exit;
RPI_HW_Start([InstSignalHandler]);
SetLength(range,maxcnt);
FillWaveTable;
GPIO_SetStruct(GPIO_struct,1,gpio,'WAVE-TEST',[FRQHW]);
FRQ_SetStruct(GPIO_struct,freqHz);
if GPIO_Setup (GPIO_struct) then
begin
if FRQ_Setup(GPIO_struct) then
begin
FRQ_Switch(GPIO_struct,true); // switch freq ON
repeat
for n:= 0 to (maxcnt-1) do
begin
// mmap_arr^[GPIO_struct.FRQ_DIVIdx]:=(BCM_PWD or ((range[n] and $fff) shl 12));
writeln('#',n,' val:',range[n]);
end;
until terminateProg;
FRQ_Switch(GPIO_struct,false); // switch freq OFF
GPIO_set_INPUT(gpio);
end;
end;
SetLength(range,0);
end;
procedure FRQ_Test;
const freqHz=1000000; gpio=4; // (1MHz on GPIO#4)
var GPIO_struct:GPIO_struct_t; _mode,b:byte; FRQ_CTLIdx,FRQ_DIVIdx:longword;
reg,regctl,regdiv:longword; initok:boolean;
begin
writeln('FRQ_Test: you should see a freq. ',freqHz:0,'Hz on GPIO',gpio:0,' minf:',(CLK_GetMinFreq/1000):0:1,'kHz maxf:',(CLK_GetMaxFreq/1000):0:1,' kHz');
if CLK_ValidFreq(freqHz) then
begin
GPIO_SetStruct(GPIO_struct,1,gpio,'FRQ HW TEST',[FRQHW]);
if GPIO_Setup (GPIO_struct) then
begin
for b:= 0 to 3 do
begin
CLK_GetRegIdx(b,regctl,regdiv);
reg:=BCM_GETREG(regdiv);
writeln(HexStr(BCM_REGAdr(regdiv),8),': ',HexStr(reg,8),' divisor:',CLK_GetDivisor(reg):0:5);
end;
initok:=FRQ_GetClkRegIdx(gpio,_mode);
if initok then initok:=CLK_GetRegIdx(_mode,FRQ_CTLIdx,FRQ_DIVIdx);
if initok then
begin
for b:=0 to 3 do
begin
writeln('Mash: ',b:0,' ',HexStr(CLK_GetMashValue(b),4));
end;
show_regs('GMGP'+Num2Str(_mode,0)+'CTL',CLK_BASE_OFS,FRQ_CTLIdx,FRQ_DIVIdx,0,false);
FRQ_SetStruct(GPIO_struct,freqHz);
initok:=FRQ_Setup(GPIO_struct);
show_regs('GMGP'+Num2Str(_mode,0)+'CTL',CLK_BASE_OFS,FRQ_CTLIdx,FRQ_DIVIdx,0,false);
// Clock_show_regs;
delay_msec(60000);
FRQ_Switch(GPIO_struct,false);
end;
end;
end;
end;
procedure CLK_Test;
const gpioPWM=13; // (PWM1/GPIO#13/Pin33)
gpioFRQ=20; // (OSC/GPIO#20/Pin38)
var mode_pll,MASH,n:byte; reg,regctl,regdiv,DIVIF:longword;
fr,FREQ_O_min,FREQ_O_avg,FREQ_O_max:real; ok:boolean;
begin
mode_pll:=1; fr:=18.32*1000000;
ok:=CLK_GetClkFreq(3,CLK_GetFreq(mode_pll),fr,FREQ_O_min,FREQ_O_avg,FREQ_O_max,MASH,DIVIF);
writeln('CLK_Tst, mode:',mode_pll:0,' f:',fr:0:2,' fmin:',FREQ_O_min:0:2,' favg:',FREQ_O_avg:0:2,' fmax:',FREQ_O_max:0:2,' MASH:',MASH,' DIVIF:0x',HexStr(DIVIF,8),' ok:',ok);
for n:= 0 to 3 do
begin
CLK_GetRegIdx(n,regctl,regdiv);
reg:=BCM_GETREG(regdiv);
writeln(HexStr(BCM_REGAdr(regdiv),8),': ',HexStr(reg,8),' divisor:',CLK_GetDivisor(reg):0:5);
end;
end;
procedure GPIO_set_PINMODE(gpio:longword; portfkt:t_port_flags);
// http://wiki.freepascal.org/Lazarus_on_Raspberry_Pi#5._PiGPIO_Low-level_native_pascal_unit_.28GPIO_control_instead_of_wiringPi_c_library.29
var akft:t_port_flags;
begin
//LOG_Writeln(LOG_DEBUG,'GPIO_set_PINMODE: GPIO'+Num2Str(gpio,0)+' Mode: '+Num2Str(ord(portfkt),0));
case portfkt of
INPUT : GPIO_set_INPUT (gpio);
OUTPUT: GPIO_set_OUTPUT(gpio);
ALT0,ALT1,ALT2,ALT3,ALT4,
ALT5 : GPIO_set_ALT (gpio,portfkt);
I2C: begin
akft:=INPUT;
case gpio of
0,1,2,3,28,29: akft:=ALT0;
end; // case
if (akft<>INPUT) then GPIO_set_ALT(gpio,akft)
else GPIO_MSG_INFO(LOG_ERROR,'GPIO_set_PINMODE: ',gpio,portfkt);
end;
PWMHW : begin
akft:=INPUT;
case gpio of
12,13,40,41,45:akft:=ALT0;
18,19: akft:=ALT5;
52,53: akft:=ALT1;
end; // case
if (akft<>INPUT) then GPIO_set_ALT(gpio,akft)
else GPIO_MSG_INFO(LOG_ERROR,'GPIO_set_PINMODE: ',gpio,portfkt);
end;
else GPIO_MSG_INFO(LOG_ERROR,'GPIO_set_PINMODE: ',gpio,portfkt);
end; // case
end;
procedure GPIO_Switch(var GPIO_struct:GPIO_struct_t); // Read GPIOx Status in Struct
var sh:string;
begin
with GPIO_struct do
begin
if initok then
begin
if not (simulation IN portflags) then
begin
ein:=(GPIO_get_PIN(gpio) xor (mask_pol<>0));
end;
sh:=description;
if sh='' then sh:='GPIO_Switch(Num#'+Num2Str(nr,0)+'/GPIO#'+Num2Str(gpio,0)+
'/HWPin#'+Num2Str(HWPin,0)+')';
// writeln(sh+ ' ReversePolarity: '+Bool2Str((mask_pol<>0))+' SignalLevel: '+Bool2Str(ein));
end
else LOG_Writeln(LOG_ERROR,'GPIO_Switch HDRPin:'+Num2Str(HWPin,0)+' not registered');
end;
end;
procedure GPIO_Switch(var GPIO_struct:GPIO_struct_t; switchon:boolean); // switch GPIOx on/off
var sh:string;
begin
with GPIO_struct do
begin
if initok then
begin
if switchon<>ein then
begin
sh:=description;
if sh='' then sh:='GPIO_Switch(Num#'+Num2Str(nr,0)+'/GPIO#'+Num2Str(gpio,0)+
'/HWPin#'+Num2Str(HWPin,0)+')';
// writeln(sh+ ' ReversePolarity: '+Bool2Str((mask_pol<>0))+' SignalLevel: '+Bool2Str(switchon));
if not (simulation IN portflags) then
begin // only on level change
if switchon then mmap_arr^[regset]:=mask_1Bit else mmap_arr^[regclr]:=mask_1Bit;
end;
end;
ein:=switchon;
end else LOG_Writeln(LOG_ERROR,'GPIO_Switch HDRPin:'+Num2Str(HWPin,0)+' not registered');
end;
end;
procedure GPIO_SetStruct(var GPIO_struct:GPIO_struct_t; num,gpionum:longint; desc:string; flags:s_port_flags);
//e.g. GPIO_SetStruct(structure,3,8,'description',[INPUT,PullUP,ReversePOLARITY]);
begin
with GPIO_struct do
begin
gpio:=gpionum; HWPin:=GPIO_MAP_GPIO_NUM_2_HDR_PIN(gpio);
nr:=num; description:=desc;
portflags:=flags; portCapabilityFlags:=GPIO_PortCapabilityFlags(gpio);
RPI_HDR_SetDesc(HWPin,desc);
idxofs_1Bit:=0; idxofs_3Bit:=0; mask_1Bit:=0; mask_3Bit:=0; mask_pol:=0;
regget:=GPIOONLYREAD; regset:=GPIOONLYREAD; regclr:=GPIOONLYREAD; ein:=false;
with ThreadCtrl do begin TermThread:=true; ThreadRunning:=false; end;
// plausibility check and clean-up of port flags
if (PWMHW IN portflags) or
(PWMSW IN portflags) then portflags:=portflags+[OUTPUT];
if (INPUT IN portflags) and
(OUTPUT IN portflags) then portflags:=portflags-[OUTPUT,PWMHW,PWMSW]; // cannot be both
if (PullUP IN portflags) and
(PullDOWN IN portflags) then portflags:=portflags-[PullDOWN]; // cannot be both
if (not(INPUT IN portflags)) then
begin
if(RisingEDGE IN portflags) then portflags:=portflags-[RisingEDGE];
if(FallingEDGE IN portflags) then portflags:=portflags-[FallingEDGE];
end;
if (PWMHW IN portflags) and (not GPIO_FCTOK(gpio,[PWMHW])) then
begin
LOG_writeln(LOG_WARNING,'GPIO_SetStruct: GPIO'+Num2Str(gpio,0)+' can not be PWMHW');
portflags:=portflags-[PWMHW]+[PWMSW];
end;
if (FRQHW IN portflags) then
begin
// portflags:=portflags-[OUTPUT,ALT0,ALT5];
portflags:=portflags-[ALT0,ALT5];
if GPIO_FCTOK(gpio,[FRQHW]) then
begin
portflags:=portflags+[ALT0];
if (gpio=GPIO_FRQ20_CLK0) or (gpio=GPIO_FRQ21_CLK1)
then portflags:=portflags-[ALT0]+[ALT5];
end
else
begin
LOG_writeln(LOG_ERROR,'GPIO_SetStruct: GPIO'+Num2Str(gpio,0)+' can not be FRQHW');
portflags:=portflags-[FRQHW];
end;
end;
if (portflags=[]) then portflags:=[INPUT];
initok:=((gpio>=0) and (gpio<64));
if initok then
begin
GPIO_get_mask_and_idxOfs(GPFSEL,gpio,idxofs_3Bit,mask_3Bit);
GPIO_get_mask_and_idxOfs(GPSET, gpio,idxofs_1Bit,mask_1Bit);
regget:=GPLEV+idxofs_1Bit;
if (ReversePOLARITY IN portflags) then
begin
regset:=GPCLR+idxofs_1Bit;
regclr:=GPSET+idxofs_1Bit;
mask_pol:=mask_1Bit;
end
else
begin
regset:=GPSET+idxofs_1Bit;
regclr:=GPCLR+idxofs_1Bit;
mask_pol:=0;
end;
end;
FRQ_SetStruct(GPIO_struct); FRQ_freq_Hz:=-1; // set default values for frq
PWM_SetStruct(GPIO_struct); PWM.PWM_freq_hz:=-1; // set default values for pwm
// GPIO_ShowStruct(GPIO_struct);
end;
end;
procedure GPIO_SetStruct(var GPIO_struct:GPIO_struct_t);
begin GPIO_SetStruct(GPIO_struct,0,-1,'',[INPUT]); end;
procedure GPIO_set_edge(gpio:longword; flgs:s_port_flags; enable:boolean);
begin
if (FallingEDGE IN flgs) then GPIO_set_edge_falling(gpio,enable);
if (RisingEDGE IN flgs) then GPIO_set_edge_rising (gpio,enable);
end;
procedure GPIO_set_pull(gpio:longword; flgs:s_port_flags; enable:boolean);
// natural pull of the GPIO (0-8 pull high, 9-27 pull low)
begin
if (PullDOWN IN flgs) then GPIO_set_PULLDOWN (gpio,enable);
if (PullUP IN flgs) then GPIO_set_PULLUP (gpio,enable);
end;
procedure GPIO_set_PAD(gpio:longword; flgs:s_port_flags);
var drivestrength:byte;
begin driveStrength:=$b;
if (DS2mA IN flgs) then driveStrength:= 0;
if (DS4mA IN flgs) then driveStrength:= 1;
if (DS6mA IN flgs) then driveStrength:= 2;
if (DS8mA IN flgs) then driveStrength:= 3;
if (DS10mA IN flgs) then driveStrength:= 4;
if (DS12mA IN flgs) then driveStrength:= 5;
if (DS14mA IN flgs) then driveStrength:= 6;
if (DS16mA IN flgs) then driveStrength:= 7;
if (driveStrength>7) then
GPIO_set_PAD(gpio,(noPADslew IN flgs),(noPADhyst IN flgs),drivestrength);
end;
function GPIO_Setup(var GPIO_struct:GPIO_struct_t):boolean;
begin
with GPIO_struct do
begin
if initok then
begin
if (gpio<0) then
begin
gpio:=-1; initok:=false;
LOG_Writeln(LOG_ERROR,'GPIO_Reg for HDRPin: '+Num2Str(HWPin,0)+' can not be mapped to GPIO num');
end
else
begin
if not (simulation IN portflags)then
begin
if (([OUTPUT,PWMSW,PWMHW,FRQHW] * portflags)<>[]) then
begin // OUTPUTS
if (([PWMSW,PWMHW] * portflags)<>[]) then
begin
initok:=PWM_Setup(GPIO_struct);
end
else
begin
if (([FRQHW] * portflags)<>[]) then
begin
initok:=FRQ_Setup(GPIO_struct);
end
else
begin // pure OUTPUT
GPIO_set_PINMODE(gpio,OUTPUT);
GPIO_Switch(GPIO_struct,(InitialHIGH IN portflags) or
(ReversePolarity IN portflags) );
end;
end;
GPIO_set_PAD (gpio,portflags);
end
else
begin
GPIO_set_PINMODE(gpio,INPUT);
GPIO_set_pull (gpio,portflags,true);
GPIO_set_edge (gpio,portflags,true);
end;
end;
end;
end else Log_Writeln(LOG_ERROR,'GPIO_Setup: GPIO_struct is not initialized');
GPIO_Setup:=initok;
end; // with
end;
procedure xyx(reg1,reg2,mask:longword); begin mmap_arr^[reg1]:=mask; mmap_arr^[reg2]:=mask; end;
procedure Toggle_Pin_very_fast(gpio:longword; cnt:qword);
// just to show how fast (without overhead) we can toggle PINxx.
// with rpi2 B+ @ 900MHz
// Result(fastway=true): >20Mhz // Result(fastway=false): 2.4Mhz
const fastway=true;
var i:qword; GPIO_struct:GPIO_struct_t; s,e:TDateTime;
begin
i:=0;
GPIO_SetStruct(GPIO_struct,1,gpio,'GPIO Toggle TEST',[OUTPUT]);
if GPIO_Setup (GPIO_struct) then
begin
with GPIO_struct do
begin
//GPIO_show_regs;
GPIO_ShowStruct(GPIO_struct);
writeln('Start with ',cnt:0,' samples, GPIO',gpio:0,' Pin:',HWPin:0,' Mask:0x',HexStr(mask_1Bit,8),' idxofs_1Bit:0x',HexStr(idxofs_1Bit,2),')');
s:=now; // start measuring time
repeat
{$warnings off}
if fastway then
begin // >20MHz
// xyx(regset,regclr,mask_1Bit); // 15MHz, takes 30% times longer ??!!
mmap_arr^[regset]:=mask_1Bit; (* High*) mmap_arr^[regclr]:=mask_1Bit; (* Low *)
end
else
begin // 2-3Mhz only ???!!!
GPIO_Switch(GPIO_struct,true); GPIO_Switch(GPIO_struct,false);
end;
{$warnings on}
inc(i);
until (i>=cnt);
e:=now; // end measuring time
writeln('End: ',FormatDateTime('yyyy-mm-dd hh:nn:ss',e),' (',(cnt/MilliSecondsBetween(e,s)/1000):0:3,'MHz)');
end;
end else writeln('Can not initialize GPIO',gpio);
end;
procedure Toggle_STATUSLED_very_fast; begin Toggle_Pin_very_fast(RPI_status_led_GPIO,100000000); end;
procedure LED_Status(ein:boolean); begin GPIO_set_PIN(RPI_status_led_GPIO,ein); end;
procedure GPIO_PIN_TOGGLE_TEST;
{ just for demo reasons }
const looptimes=10; waittime_ms = 1000; // 0.5Hz; let Status LED blink
var lw:longword;
begin
//GPIO_show_regs;
writeln('Start of GPIO_PIN_TOGGLE_TEST (Let the Status-LED blink ',looptimes:0,' times)');
writeln('Set GPIO',RPI_status_led_GPIO:0,' to OUTPUT');
GPIO_set_OUTPUT(RPI_status_led_GPIO);
for lw := 1 to looptimes do
begin
writeln(looptimes-lw+1:3,'. Set StatusLED (GPIO',RPI_status_led_GPIO,') to 1'); LED_Status(true); sleep(waittime_ms);
writeln(looptimes-lw+1:3,'. Set StatusLED (GPIO',RPI_status_led_GPIO,') to 0'); LED_Status(false); sleep(waittime_ms);
writeln;
end;
writeln('End of GPIO_PIN_TOGGLE_TEST');
end;
procedure GPIO_set_BIT(regidx,gpio:longword;setbit,readmodifywrite:boolean); { set or reset pin in gpio register part }
var idx,mask:longword;
begin
GPIO_get_mask_and_idx(regidx,gpio,idx,mask);
//Writeln('GPIO_set_BIT: GPIO'+Num2Str(gpio,0)+' level: '+Bool2Str(setbit)+' Reg: 0x'+HexStr(regidx,8)+' idx: 0x'+HexStr(idx,8)+' mask: 0x'+HexStr(mask,8));
if setbit then BCM_SETREG(idx, mask ,false,readmodifywrite)
else BCM_SETREG(idx,not(mask),true, readmodifywrite);
end;
procedure GPIO_set_PIN(gpio:longword;highlevel:boolean);
{ Set RPi GPIO to high or low level: Speed @ 700MHz -> 1.25MHz }
begin
//Log_Writeln(LOG_DEBUG,'GPIO_set_PIN: '+Num2Str(gpio,0)+' level '+Bool2Str(highlevel));
//Writeln('GPIO_set_PIN: '+Num2Str(gpio,0)+' level '+Bool2Str(highlevel));
if highlevel then GPIO_set_BIT(GPSET,gpio,true,false) else GPIO_set_BIT(GPCLR,gpio,true,false);
{ sleep(1); }
end;
function GPIO_get_PIN (gpio:longword):boolean;
// Get RPi GPIO pin Level is true when Pin level is '1'; false when '0'; Speed @ 700MHz -> 2.33MHz
var idx,mask:longword;
begin
GPIO_get_mask_and_idx(GPLEV,gpio,idx,mask);
GPIO_get_PIN:=((BCM_GETREG(idx) and mask)>0);
end;
procedure GPIO_Pulse(gpio,pulse_ms:longword);
begin
GPIO_set_pin(gpio,true);
delay_msec(pulse_ms);
GPIO_set_pin(gpio,false);
end;
procedure GPIO_set_GPPUD(enable,pullup:boolean);
begin
if enable then
begin
if pullup then BCM_SETREG(GPPUD,$02,false,false) else BCM_SETREG(GPPUD,$01,false,false);
end else BCM_SETREG(GPPUD,$00,false,false);
delay_msec(1);
end; { set GPIO Pull-up/down Register (GPPUD) }
procedure GPIO_set_PAD(gpio:longword; noSLEW,noHYST:boolean; drivestrength:byte);
// https://de.scribd.com/doc/101830961/GPIO-Pads-Control2
var mask:longword;
begin
mask:=BCM_PWD or (drivestrength and $00000007);
if (not noHYST) then mask:=mask or $00000008;
if (not noSLEW) then mask:=mask or $00000010;
LOG_Writeln(LOG_DEBUG,'GPIO_set_PADcurrent: GPIO'+Num2Str(gpio,0)+' '+Num2Str(drivestrength,0));
case gpio of
00..27: BCM_SETREG(PADS_GPIO00_27,mask,false,false); // 0x7e10 002c PADS (GPIO 0-27)
28..45: BCM_SETREG(PADS_GPIO28_45,mask,false,false); // 0x7e10 0030 PADS (GPIO 28-45)
46..53: BCM_SETREG(PADS_GPIO46_53,mask,false,false); // 0x7e10 0034 PADS (GPIO 46-53)
end; // case
end;
procedure GPIO_set_PULLUPORDOWN(gpio:longword; enable,pullup:boolean); // pulldown: pullup=false;
// https://github.com/RPi-Distro/raspi-gpio/blob/master/raspi-gpio.c
// natural pull of the GPIO (0-8 pull high, 9-27 pull low)
// approximately 50KΩ
const no2711=true;
var idx,mask,pull:longword;
begin
LOG_Writeln(LOG_DEBUG,'GPIO_set_PULLUPORDOWN: GPIO'+Num2Str(gpio,0)+' '+Bool2Str(enable)+' '+Bool2Str(pullup));
if (Upper(RPI_hw)='BCM2711') then
begin
GPIO_get_mask_and_idx(GPPUPPDN,gpio,idx,mask);
if enable then begin if pullup then pull:=1 else pull:=2; end else pull:=0;
pull:=(pull shl ((gpio mod 16)*2));
// writeln('BCM2711 PullSet['+Num2Str(gpio,2)+']: reg: GPPUPPDN'+Num2Str(idx-GPPUPPDN,0)+' msk: 0x'+HexStr(mask,8)+' pull: 0x'+HexStr(pull,2));
BCM_SETREG(idx,(BCM_GETREG(idx) and (not mask) or pull));
end
else
begin
GPIO_get_mask_and_idx(GPPUDCLK,gpio,idx,mask);
GPIO_set_GPPUD(enable,pullup); // assert clock to GPPUDCLKn
delay_us(10);
BCM_SETREG(idx,mask,false,false);
delay_us(10);
GPIO_set_GPPUD(false, pullup); // deassert clock from GPPUDCLKn
delay_us(10);
BCM_SETREG(idx,0,false,false);
delay_us(10);
end;
end;
procedure GPIO_set_PULLUP (gpio:longword; enable:boolean); begin GPIO_set_PULLUPORDOWN(gpio,enable,true); end; // enable or disable PULLUP
procedure GPIO_set_PULLDOWN(gpio:longword; enable:boolean); begin GPIO_set_PULLUPORDOWN(gpio,enable,false); end; // enable or disable PULLDOWN
procedure GPIO_set_edge_rising(gpio:longword; enable:boolean); { Pin RisingEdge Detection Register (GPREN) }
begin
Log_Writeln(LOG_DEBUG,'GPIO_set_edge_rising: GPIO'+Num2Str(gpio,0)+' enable: '+Bool2Str(enable));
GPIO_set_BIT(GPREN,gpio,enable,true); { Pin RisingEdge Detection }
end;
procedure GPIO_set_edge_falling(gpio:longword; enable:boolean); { Pin FallingEdge Detection Register (GPFEN) }
begin
Log_Writeln(LOG_DEBUG,'GPIO_set_edge_falling: GPIO'+Num2Str(gpio,0)+' enable: '+Bool2Str(enable));
GPIO_set_BIT(GPFEN,gpio,enable,true); { Pin FallingEdge Detection }
end;
procedure GPIO_PWM_Test(gpio:longint; HWPWM:boolean; freq_Hz:real; dutyrange,startval:longword);
// only for PWM0:Pin12:GPIO18 PWM1:Pin35:GPIO19
const maxcnt=2;
var i,cnt:longint; GPIO_struct:GPIO_struct_t;
begin
if HWPWM then GPIO_SetStruct(GPIO_struct,1,gpio,'HW PWM_TEST',[PWMHW])
else GPIO_SetStruct(GPIO_struct,1,gpio,'SW PWM_TEST',[PWMSW]);
PWM_SetStruct (GPIO_struct,PWM_MS_MODE,freq_Hz,dutyrange,startval); // ca. 50Hz (50000/1000) -> divisor: 384
PWM_setClock (GPIO_struct);
if GPIO_Setup (GPIO_struct) then
begin
GPIO_ShowConnector; GPIO_ShowStruct(GPIO_struct);
i:=0; cnt:=1;
repeat
if (i>(dutyrange-1)) then
begin
PWM_Write(GPIO_struct,dutyrange-1);
writeln('Loop(',cnt,'/',dutyrange,'): reached max. pwm value: ',dutyrange-1); sleep(30);
GPIO_ShowStruct(GPIO_struct);
i:=0; inc(cnt);
end else PWM_Write(GPIO_struct,i);
// if (i=(dutyrange div 2)) then readln; // for measuring with osci
if HWPWM then begin inc(i); sleep(10); end else begin inc(i,10); sleep(10); end; // ms
until (cnt>maxcnt);
PWM_Write (GPIO_struct,0); // set last value to 0
PWM_SetStruct (GPIO_struct); // reset to PWM default values
sleep(100); // let SW Thread time to terminate
end else Log_Writeln(LOG_ERROR,'GPIO_PWM_Test: GPIO'+Num2Str(GPIO_struct.gpio,0)+' Init has failed');
end;
procedure GPIO_PWM_Test; // Test with GPIO18 PWM0 on Connector Pin12
const gpio=GPIO_PWM0;
var dc,f_hz:longword;
begin
f_hz:=50; dc:=PWM_GetMaxDtyC(f_hz); // get the best DutyCycle for this freq.
writeln('GPIO_PWM_Test with GPIO',gpio,' Connector Pin',GPIO_MAP_GPIO_NUM_2_HDR_PIN(gpio),' SOFTWARE based');
GPIO_PWM_Test(gpio,false,f_hz,dc,0); // SW PWM Test
f_hz:=5000; dc:=PWM_GetMaxDtyC(f_hz); // get the best DutyCycle for this freq.
writeln('GPIO_PWM_Test with GPIO',gpio,' Connector Pin',GPIO_MAP_GPIO_NUM_2_HDR_PIN(gpio),' HARDWARE based');
GPIO_PWM_Test(gpio,true, f_hz,dc,0); // HW PWM Test
writeln('GPIO_PWM_Test END');
end;
procedure GPIO_Test(HWPinNr:longword; flags:s_port_flags);
const loopmax=2;
var i:longint; GPIO_struct:GPIO_struct_t;
begin
GPIO_SetStruct(GPIO_struct,1,GPIO_MAP_HDR_PIN_2_GPIO_NUM(HWPinNr),'GPIO_Test',flags);
if GPIO_Setup (GPIO_struct) then
begin
with GPIO_struct do
begin
description:='GPIO_Test(HWPin#'+Num2Str(HWPin,0)+'/GPIO#'+Num2Str(gpio,0)+')';
if (OUTPUT IN flags) then
begin
writeln('Test OUTPUT HWPin: '+Num2Str(HWPin,0)+' GPIO: '+Num2Str(gpio,0));
for i := 1 to loopmax do
begin
writeln(' for setting Pin to HIGH, pls. push <CR> button'); readln;
GPIO_Switch(GPIO_struct,true);
writeln(' for setting Pin to LOW, pls. push <CR> button'); readln;
GPIO_Switch(GPIO_struct,false);
end;
writeln('Test next PIN, pls. push <CR> button'); readln;
end; // Output-Test
if (INPUT IN flags) then
begin
writeln('Test INPUT HWPin: '+Num2Str(HWPin,0)+' GPIO: '+Num2Str(gpio,0));
for i := 1 to loopmax do
begin
writeln(' for reading Pin, pls. push <CR> button'); readln;
GPIO_Switch(GPIO_struct); // Read GPIO
writeln(description+': '+Bool2LVL(ein));
end;
end; // Input-Test
end; // with
end else Writeln('GPIO_Test: can not Map HWPin:'+Num2Str(HWPinNr,0)+' to valid GPIO num');
writeln;
end;
procedure GPIO_TestAll;
// for testing of correct operation. (only OUTPUT tests)
begin
begin // 26 Pin Hdr
GPIO_Test(07,[OUTPUT]); GPIO_Test(11,[OUTPUT]); GPIO_Test(12,[OUTPUT]);
GPIO_Test(13,[OUTPUT]); GPIO_Test(15,[OUTPUT]); GPIO_Test(16,[OUTPUT]);
GPIO_Test(18,[OUTPUT]); GPIO_Test(22,[OUTPUT]);
end;
if RPI_hdrpincount>=40 then
begin // 40 PIN Hdr
GPIO_Test(29,[OUTPUT]); GPIO_Test(31,[OUTPUT]); GPIO_Test(32,[OUTPUT]);
GPIO_Test(33,[OUTPUT]); GPIO_Test(35,[OUTPUT]); GPIO_Test(36,[OUTPUT]);
GPIO_Test(37,[OUTPUT]); GPIO_Test(38,[OUTPUT]); GPIO_Test(40,[OUTPUT]);
end;
end;
procedure SERVO_End(var SERVO_struct:SERVO_struct_t);
begin PWM_End(SERVO_struct.HWAccess); end;
procedure SERVO_End(hndl:longint);
var n:longint;
begin
if hndl<0 then
begin
for n:= 1 to Length(SERVO_struct) do SERVO_End(SERVO_struct[n-1]);
SetLength(SERVO_struct,0);
end else SERVO_End(SERVO_struct[hndl]);
end;
procedure SERVO_SetStruct(var SERVO_struct:SERVO_struct_t; dty_min,dty_mid,dty_max:longword; ang_min,ang_mid,ang_max,speed:longint);
begin
with SERVO_struct do
begin
if ((ang_min<=ang_mid) and (ang_mid<=ang_max)) and
((dty_min<=dty_mid) and (dty_mid<=dty_max)) then
begin
min_dutycycle:=dty_min; mid_dutycycle:=dty_mid; max_dutycycle:=dty_max;
min_angle:= ang_min; mid_angle:= ang_mid; max_angle:= ang_max;
end
else
begin
min_dutycycle:=SRVOMINDC; mid_dutycycle:=SRVOMIDDC; max_dutycycle:=SRVOMAXDC; // SG90 ms in Ticks
min_angle:= SRVOMINANG; mid_angle:= SRVOMIDANG; max_angle:= SRVOMAXANG; // SG90 degree Values
LOG_writeln(LOG_ERROR,'SERVO_SetStruct: invalid duty cycle or angle values. set it to default values');
end;
speed60deg:=speed;
angle_current:=max_angle+1; // just to force 1. servo-movement to 0Deg
end;
end;
procedure SERVO_Write(var SERVO_struct:SERVO_struct_t; angle:longint; syncwait:boolean);
var setval,angle_old:longint;
begin
with SERVO_struct do
begin
if (angle_current<>angle) then
begin
angle_old:=angle_current; angle_current:=angle;
if angle_current<min_angle then angle_current:=min_angle;
if angle_current>max_angle then angle_current:=max_angle;
setval:=mid_dutycycle;
if ((min_angle<>0) and (max_angle<>0) and (angle_current<>mid_angle)) then
begin
if (angle_current>=min_angle) and (angle_current<mid_angle) then
begin
setval:=round(((min_angle-angle_current)/min_angle) *
(mid_dutycycle-min_dutycycle) + min_dutycycle);
// writeln('Angle-: ',angle_current);
end
else
begin
// writeln('Angle+: ',angle_current);
setval:=round((angle_current/max_angle) *
(max_dutycycle-mid_dutycycle) + mid_dutycycle);
end;
end;
// writeln('setval1: ',setval);
// transform setval to dutyrange e.g. 0..1000
with SERVO_struct.HWAccess.PWM do
begin
if (pwm_dutyrange<>0) and (pwm_period_us<>0)
then setval:=abs(round(setval/(pwm_period_us/pwm_dutyrange)))
else setval:=0;
end; // with
// writeln('setval2: ',setval,' #######################################');
PWM_Write(SERVO_struct.HWAccess,setval);
// writeln('SyncWaitTime(ms):',round((abs(angle_old-angle_current)/60)*speed60Deg));
if syncwait then
delay_msec(round((abs(angle_old-angle_current)/60)*speed60Deg));
end;
end; // with
end;
procedure SERVO_Setup(var SERVO_struct:SERVO_struct_t;
HWPinNr,nr,maxval,
dcmin,dcmid,dcmax:longword;
angmin,angmid,angmax,speed:longint;
desc:string; freq:real; flags:s_port_flags);
var flgs:s_port_flags; _gpio:longint;
begin
_gpio:=GPIO_MAP_HDR_PIN_2_GPIO_NUM(HWPinNr);
if (PWMSW IN flags) or (PWMHW IN flags) then flgs:=flags else flgs:=flags+[PWMSW];
if (PWMHW IN flags) and (not GPIO_HWPWM_capable(_gpio)) then flgs:=flags+[PWMSW]-[PWMHW];
with SERVO_struct do
begin
SERVO_SetStruct(SERVO_struct,dcmin,dcmid,dcmax,angmin,angmid,angmax,speed);
GPIO_SetStruct (SERVO_struct.HWAccess,nr,_gpio,desc,flgs);
PWM_SetStruct (SERVO_struct.HWAccess,PWM_MS_MODE,freq,maxval,dcmid);
PWM_setClock (SERVO_struct.HWAccess);
end; // with
end;
procedure SERVO_GetData(var nr,yaw,pitch,roll:longint);
// Get Data from Accelerator-/Gyro-/Compass-Sensors (e.g. Quaternion, Euler-Angels)
// use Data and convert it to new Servo positions
// this is just for demo reasons
var min,mid,max:longint;
begin
min:=SRVOMINANG; mid:=SRVOMIDANG; max:=SRVOMAXANG;
case (nr mod 12) of // just a quick demo
1: begin yaw:=max; pitch:=mid; roll:=mid; end;
2: begin yaw:=max; pitch:=max; roll:=mid; end;
3: begin yaw:=max; pitch:=max; roll:=max; end;
4: begin yaw:=mid; pitch:=max; roll:=max; end;
5: begin yaw:=mid; pitch:=mid; roll:=max; end;
7: begin yaw:=min; pitch:=mid; roll:=mid; end;
8: begin yaw:=min; pitch:=min; roll:=mid; end;
9: begin yaw:=min; pitch:=min; roll:=min; end;
10: begin yaw:=mid; pitch:=min; roll:=min; end;
11: begin yaw:=mid; pitch:=mid; roll:=min; end;
else begin yaw:=mid; pitch:=mid; roll:=mid; end;
end;
inc(nr);
end;
procedure SERVO_Test;
// tested with TowerPro Micro Servos 9g SG90 Datasheet values
// "0" (1.5 ms pulse) is middle,
// "90" ( ~2 ms pulse) is all the way to the right,
// "-90" ( ~1 ms pulse) is all the way to the left.
// Frequency: 50Hz-> 20ms period (20000us)
const
freq=SERVO_FRQ; speed=SERVO_Speed;
HWPinNr_YAW= 12; // GPIO18 HW-PWM
YAW_minAng=SRVOMINANG; YAW_midANG=SRVOMIDANG; YAW_maxAng=SRVOMAXANG;// SG90 degree Values
YAW_min= SRVOMINDC; YAW_mid= SRVOMIDDC; YAW_max= SRVOMAXDC; // SG90 ms in Ticks
HWPinNr_PITCH=16; // GPIO23 SW-PWM
PITCH_min= YAW_min; PITCH_mid= YAW_mid; PITCH_max= YAW_max;
PITCH_minAng=YAW_minAng; PITCH_midAng=YAW_midAng; PITCH_maxAng=YAW_maxANG;
HWPinNr_ROLL= 18; // GPIO24 SW-PWM
ROLL_min= YAW_min; ROLL_mid= YAW_mid; ROLL_max= YAW_max;
ROLL_minAng=YAW_minAng; ROLL_midAng=YAW_midAng; ROLL_maxAng=YAW_maxANG;
var nr,yaw,pitch,roll,_dc:longint;
begin
RPI_HW_Start([InstSignalHandler]);
writeln('SERVO_Test: Start');
SetLength(SERVO_struct,3); // create data structures for 3 servos
_dc:=PWM_GetMaxDtyC(freq); // get the best DutyCycle for this freq.
SERVO_Setup( SERVO_struct[0],HWPinNr_YAW, 0,_dc,YAW_min, YAW_mid, YAW_max, YAW_minAng, YAW_midAng, YAW_maxANG, speed,'SERVO YAW ',freq,[PWMHW]);
SERVO_Setup( SERVO_struct[1],HWPinNr_PITCH,1,_dc,PITCH_min,PITCH_mid,PITCH_max,PITCH_minAng,PITCH_midAng,PITCH_maxAng,speed,'SERVO PITCH',freq,[PWMSW]);
SERVO_Setup( SERVO_struct[2],HWPinNr_ROLL, 2,_dc,ROLL_min, ROLL_mid, ROLL_max, ROLL_minAng, ROLL_midAng, ROLL_maxAng, speed,'SERVO ROLL ',freq,[PWMSW]);
if GPIO_Setup(SERVO_struct[0].HWAccess) and
GPIO_Setup(SERVO_struct[1].HWAccess) and
GPIO_Setup(SERVO_struct[2].HWAccess) then
begin
nr:=0;
repeat // control loop
// Do SERVO_Write(SERVO_struct[<nr>],<new_servo_pos>,<syncwait>);
SERVO_GetData(nr,yaw,pitch,roll); // get new servo position data
writeln('Servos: ',yaw:4,' ',pitch:4,' ',roll:4);
SERVO_Write(SERVO_struct[0],yaw, false);
SERVO_Write(SERVO_struct[1],pitch,false);
SERVO_Write(SERVO_struct[2],roll, false);
delay_msec(SERVO_Speed*round(90/60)); // let servo time for full turn
until (nr>50) or terminateProg;
for nr:=1 to Length(SERVO_struct) do SERVO_Write(SERVO_struct[nr-1],0,false);
delay_msec(SERVO_Speed*round(90/60)); // let servos time to turn to neutral position
SERVO_End(-1);
writeln('SERVO_Test: END');
end else LOG_Writeln(LOG_ERROR,'SERVO_Test: could not be initialized');
end;
function GPIO_MAP_GPIO_NUM_2_HDR_PIN(gpio:longint; mapidx:byte):longint; { Maps GPIO Number to the HDR_PIN, respecting rpi rev1 or rev2 board }
var hwpin,cnt:longint;
begin
hwpin:=-99; cnt:=1;
if ((mapidx=1) or (mapidx<=gpiomax_map_idx_c)) then
begin
while cnt<=max_pins_c do
begin
if abs(GPIO_hdr_map_c[mapidx,cnt])=gpio then begin hwpin:=cnt; cnt:=max_pins_c; end;
inc(cnt);
end;
end;
//writeln('mapidx',mapidx:0,' HW-PIN: ',hwpin:2,' <- ',gpio:2);
GPIO_MAP_GPIO_NUM_2_HDR_PIN:=hwpin;
end;
function GPIO_MAP_GPIO_NUM_2_HDR_PIN(gpio:longint):longint;
begin
GPIO_MAP_GPIO_NUM_2_HDR_PIN:=GPIO_MAP_GPIO_NUM_2_HDR_PIN(gpio,RPI_gpiomapidx);
end;
function GPIO_MAP_HDR_PIN_2_GPIO_NUM(hdr_pin_number:longint; mapidx:byte):longint; { Maps HDR_PIN to the GPIO Number, respecting rpi rev1 or rev2 board }
var GPIO_pin:longint;
begin
if (hdr_pin_number>=1) and (hdr_pin_number<=max_pins_c) and
((mapidx>=1) and (mapidx<=gpiomax_map_idx_c)) then GPIO_pin:=GPIO_hdr_map_c[mapidx,hdr_pin_number] else GPIO_pin:=WRONGPIN;
//writeln('mapidx',mapidx:0,' HW-PIN: ',hdr_pin_number:2,' -> ',GPIO_pin:2);
GPIO_MAP_HDR_PIN_2_GPIO_NUM:=GPIO_pin;
end;
function GPIO_MAP_HDR_PIN_2_GPIO_NUM(hdr_pin_number:longint):longint;
begin
GPIO_MAP_HDR_PIN_2_GPIO_NUM:=GPIO_MAP_HDR_PIN_2_GPIO_NUM(hdr_pin_number,RPI_gpiomapidx);
end;
procedure GPIO_set_HDR_PIN(hw_pin_number:longword;highlevel:boolean); { Maps PIN to the GPIO Header, respecting rpi rev1 or rev2 board }
var pin:longint;
begin
pin:=GPIO_MAP_HDR_PIN_2_GPIO_NUM(hw_pin_number,RPI_gpiomapidx);
if pin>=0 then GPIO_set_PIN(longword(pin),highlevel);
end;
function GPIO_get_HDR_PIN(hw_pin_number:longword):boolean; { Maps PIN to the GPIO Header, respecting rpi rev1 or rev2 board }
var pin:longint; lvl:boolean;
begin
pin:=GPIO_MAP_HDR_PIN_2_GPIO_NUM(hw_pin_number,RPI_gpiomapidx);
if pin>=0 then lvl:=GPIO_get_PIN(longword(pin)) else lvl:=false;
GPIO_get_HDR_PIN:=lvl;
end;
function ENC_GetVal(hdl:integer; ctrsel:integer):real;
var val:real;
begin
val:=0;
{$warnings off}
if (hdl>=0) and (hdl<Length(ENC_struct)) then {$warnings on}
begin
with ENC_struct[hdl] do
begin
with CNTInfo do
begin
EnterCriticalSection(ENC_CS);
case ctrsel of
0: val:=counter;
1: val:=cycles;
2: val:=switchcounter;
3: if (countermax<>0) then val:=counter/countermax;
4: val:=TurnRateStruct.fTurnRate_Hz;
5: val:=switchlastpresstime; // no reset last value
6: begin val:=switchlastpresstime; switchlastpresstime:=0; end;
7: val:=ord(kbdupcnt);
8: val:=ord(kbddwncnt);
9: val:=ord(kbdswitch);
else val:=counter;
end; // case
end; // with
LeaveCriticalSection(ENC_CS);
end; // with
end else LOG_Writeln(LOG_ERROR,'ENC_GetVal: hdl '+Num2Str(hdl,0)+' out of range');
ENC_GetVal:=val;
end;
function ENC_GetVal (hdl:integer):real; begin ENC_GetVal:= ENC_GetVal(hdl, 0); end;
function ENC_GetCycles (hdl:integer):real; begin ENC_GetCycles:= ENC_GetVal(hdl, 1); end;
function ENC_GetValPercent(hdl:integer):real; begin ENC_GetValPercent:=ENC_GetVal(hdl, 3); end;
function ENC_GetSwitch (hdl:integer):real; begin ENC_GetSwitch:= ENC_GetVal(hdl, 2); end;
function ENC_GetSwPtime (hdl:integer):real; begin ENC_GetSwPtime:= ENC_GetVal(hdl, 5); end;
procedure ENC_IncSwCnt (var ENCInfo:ENC_CNT_struct_t; cnt:integer);
begin inc(ENCInfo.switchcounter,cnt); end;
procedure ENC_IncEncCnt(var ENCInfo:ENC_CNT_struct_t; cnt:integer);
begin inc(ENCInfo.counter,cnt); end;
function ENC_GetCounter(var ENCInfo:ENC_CNT_struct_t):boolean;
begin
with ENCInfo do
begin
switchlastpresstime:=round(ENC_GetSwPtime(Handle));
switchcounterold:= switchcounter;
switchcounter:= round(ENC_GetSwitch(Handle));
counterold:= counter;
counter:= round(ENC_GetVal (Handle));
cyclesold:= cycles;
cycles:= round(ENC_GetCycles(Handle));
swsteps:= switchcounter-switchcounterold;
encsteps:= counter- counterold;
enccycles:= cycles- cyclesold;
case activitymodedetect of
1: ENC_activity:= ((encsteps <>0) or (swsteps<>0));
2: ENC_activity:= ((swsteps <>0));
3: ENC_activity:= ((enccycles<>0));
4: ENC_activity:= ((encsteps <>0));
else ENC_activity:= ((enccycles<>0) or (swsteps<>0));
end; // case
if ENC_activity then
begin
if (enccycles>0) then kbdcode:=char(round(ENC_GetVal(Handle,7)));
if (enccycles<0) then kbdcode:=char(round(ENC_GetVal(Handle,8)));
if (swsteps<>0) then kbdcode:=char(round(ENC_GetVal(Handle,9)));
end;
// writeln('ENC_GetCounter: ',counter,' ',counterold,' ',encsteps,' Switch: ',switchcounter,' ',switchcounterold);
ENC_GetCounter:= ENC_activity;
end; // with
end;
procedure ENC_End(hdl:integer);
var i:integer;
begin
if (hdl<0) then
begin
for i:= 1 to Length(ENC_struct) do Thread_End(ENC_struct[i-1].ThreadCtrl,100);
SetLength(ENC_struct,0);
end
else
begin
if (hdl>=0) and (hdl<Length(ENC_struct)) then Thread_End(ENC_struct[hdl].ThreadCtrl,100);
end;
end;
function ENC_Device(ptr:pointer):ptrint;
(* seq B A AxorB delta meaning
0 0 0 0 0 no change
1 0 1 1 1 1 step clockwise
2 1 1 0 2 2 steps clockwise or counter-clockwise (fault condition)
3 1 0 1 3 1 step counter clockwise *)
var hdl,cyclold:longint; regval:longword; dt,dt2:TDateTime; sw_change,swpress,sw1stpress:boolean;
begin
hdl:=longint(ptr);
if (hdl>=0) and (hdl<Length(ENC_struct)) then
begin
with ENC_struct[hdl] do
begin
Thread_SetName(desc);
ThreadCtrl.ThreadRunning:=true; ThreadCtrl.TermThread:=false;
// writeln('ThreadStart ',TermThread,' ',sleeptime_ms);
InitCriticalSection(ENC_CS);
SyncTime:=now; dt:=SyncTime; dt2:=dt; sw1stpress:=true; sw_change:=false;
repeat
cyclold:=CNTInfo.cycles;
regval:=mmap_arr^[A_Sig.regget];
if (((regval and A_Sig.mask_1Bit) xor A_Sig.mask_pol)>0) then a:=1 else a:=0;
if (A_Sig.regget<>B_Sig.regget) then regval:=mmap_arr^[B_Sig.regget];
if (((regval and B_Sig.mask_1Bit) xor B_Sig.mask_pol)>0) then b:=1 else b:=0;
seq:=(a xor b) or (b shl 1);
if (S_Sig.gpio>=0) then
begin // switch
if (B_Sig.regget<>S_Sig.regget) then regval:=(mmap_arr^[S_Sig.regget]);
swpress:=(((regval and S_Sig.mask_1Bit) xor S_Sig.mask_pol)>0);
if swpress then
begin // switch is pressed
if sw1stpress then
begin
SetTimeOut(dt,sleeptime_ms); // Retrigger press time
dt2:=now; // switch pressed start time
sw1stpress:=false;
end
else
begin
EnterCriticalSection(ENC_CS);
if TimeElapsed(dt,SwitchRepeatTime_ms) then
begin
inc(CNTInfo.switchcounter);
sw_change:=true;
end;
LeaveCriticalSection(ENC_CS);
end;
end else sw1stpress:=true;
if sw_change or (swpress and not sw1stpress) then
CNTInfo.switchlastpresstime:=MilliSecondsBetween(now,dt2); // last switch press time
end;
delta:=0;
if seq<>seqold then
begin // turning wheel
// fpc calc neg mod wrong Ex: (−144)%5=5−(144%5)=5−(4)=1(−144)%5=5−(144%5)=5−(4)=1
if seqold>seq then delta:=4-(abs(seq-seqold) mod 4) else delta:=(seq-seqold) mod 4;
if delta=3 then delta:=-1
else if delta=2 then if deltaold<0 then delta:=-delta;
SetTimeOut(CNTInfo.fIntervalResetTime,CNTInfo.Interval_ms);
EnterCriticalSection(ENC_CS);
FREQ_DetTurnRate(CNTInfo.TurnRateStruct,delta);
if s2minmax then
begin
if (CNTInfo.counter+delta)<0 then CNTInfo.counter:=0 else inc(CNTInfo.counter,delta);
if CNTInfo.counter>(CNTInfo.countermax-1) then CNTInfo.counter:=CNTInfo.countermax-1;
end
else begin inc(CNTInfo.counter,delta); end;
// CNTInfo.counter:=CNTInfo.counter mod CNTInfo.countermax; // 0 - countermax-1
CNTInfo.counter:=MOD_Euclid(CNTInfo.counter,CNTInfo.countermax); // 0 - countermax-1
CNTInfo.cycles:= CNTInfo.counter div CNTInfo.steps_per_cycle;
LeaveCriticalSection(ENC_CS);
// writeln('Seq:',seq,' seqold:',seqold,' delta:',delta,' deltaold:',deltaold,' b:',b,' a:',a);
deltaold:=delta; seqold:=seq;
end else if TimeElapsed(CNTInfo.fIntervalResetTime) then FREQ_CounterReset(CNTInfo.TurnRateStruct);
if (beepgpio>=0) and ((CNTInfo.cycles<>cyclold) or sw_change) then GPIO_Pulse(beepgpio,1);
delay_msec(sleeptime_ms);
sw_change:=false;
until ThreadCtrl.TermThread;
// writeln('ENC_Device: Thread will terminate');
DoneCriticalSection(ENC_CS);
EndThread;
ThreadCtrl.ThreadRunning:=false;
end; // with
end else LOG_Writeln(LOG_ERROR,'ENC_Device: hdl '+Num2Str(hdl,0)+' out of range');
ENC_Device:=0;
end;
procedure ENC_InfoKBDInit(var CNTInfo:ENC_CNT_struct_t; kbdup,kbddwn,kbdsw:char);
begin
with CNTInfo do
begin
kbdcode:=' '; kbdupcnt:=kbdup; kbddwncnt:=kbddwn; kbdswitch:=kbdsw;
end;
end;
procedure ENC_InfoInit(var CNTInfo:ENC_CNT_struct_t);
begin
with CNTInfo do
begin
Handle:=-1; steps_per_cycle:=4;
ENC_activity:=false; activitymodedetect:=0;
encsteps:=0; swsteps:=0; enccycles:=0;
counter:=0; counterold:=0; countermax:=$ffff;
switchcounter:=0; switchcounterold:=0; switchcountermax:=$ffff;
enc:=0; encold:=0; Interval_ms:=1000;
fIntervalResetTime:=now; switchlastpresstime:=0;
ENC_InfoKBDInit (CNTInfo,#38,#40,#13);
FREQ_InitStruct (TurnRateStruct, 250);
end; // with
end;
function ENC_Setup(hdl:integer; stick2minmax:boolean;
ctrpreset,ctrmax,stepspercycle:longword;
beepergpio:integer):boolean;
//in: hdl: 1..ENC_cnt
// A/B_Sig: 2 GPIOs, which should be used for the Encoder A,B Signal
// S_Sig: GPIO, which handles SwitchButton of Encoder. e.g. the KY-040 encoder has a switch.
// stick2minmax: true, if we don't want an immediate counter transition from <ctrmax> to 0 or from 0 to <ctrmax>
// ctrpreset: set an initial counter value. multiple of stepspercycle
// ctrmax: counter is always between 0 and <ctrmax>
// stepspercycle: an regular encoder generates 4 steps per cycle (resolution)
//out: true, if we could allocate the HW-Pins (success)
var _ok:boolean;
begin
_ok:=false;
if (hdl>=0) and (hdl<Length(ENC_struct)) then
begin
with ENC_struct[hdl] do
begin
ok:=(GPIO_Setup(A_Sig) and GPIO_Setup(B_Sig));
if S_Sig.gpio>=0 then ok:=ok and GPIO_Setup(S_Sig);
if ok then
begin // Pins are available
ENC_InfoInit(CNTInfo); CNTInfo.Handle:=hdl;
s2minmax:=stick2minmax; sleeptime_ms:=ENC_SyncTime_c;
SwitchRepeatTime_ms:=ENC_SwRepeatTime_c;
seqold:=2; deltaold:=0; SwitchFiredSpecFunc:=nil;
if stepspercycle>0 then CNTInfo.steps_per_cycle:=stepspercycle;
CNTInfo.cycles:=round(ctrpreset/CNTInfo.steps_per_cycle);
idxcounter:=0; beepgpio:=beepergpio;
if ((beepgpio>=0) and
(GPIO_MAP_GPIO_NUM_2_HDR_PIN(beepgpio)>=0)) then GPIO_set_output(beepgpio);
with CNTInfo do
begin
ENC_activity:=false;
counter:=(cycles*steps_per_cycle);
counterold:=counter; countermax:=counter+1;
if ctrmax>counter then countermax:=ctrmax+1; // wg. counter mod countermax
end; // with
ENC_GetCounter(CNTInfo);
// ThreadCtrl.ThreadID:=BeginThread(@ENC_Device,pointer(hdl)); // Start Encoder Thread
Thread_Start(ThreadCtrl,@ENC_Device,pointer(hdl),0,-1); // Start Encoder Thread
end else LOG_Writeln(LOG_ERROR,'ENC_RotEncInit: Checked Pins not ok');
_ok:=ok;
end; // with
end
else
if (hdl>ENC_cnt) then LOG_Writeln(LOG_ERROR,'ENC_RotEncInit: increase ENC_Cnt:'+Num2Str(ENC_cnt,0)+' hdl:'+Num2Str(hdl,0));
ENC_Setup:=_ok;
end;
function ENC_GetHdl(descr:string):byte;
var devnum:longint;
begin
SetLength(ENC_struct,Length(ENC_struct)+1);
devnum:=Length(ENC_struct)-1;
SAY(LOG_DEBUG,'ENC_GetHdl devnum:'+Num2Str(devnum,0));
with ENC_struct[devnum] do
begin
desc:=descr;
ENC_InfoInit(CNTInfo);
CNTInfo.Handle:=devnum;
end;
ENC_GetHdl:=devnum;
end;
procedure ENC_Test;
// tested with Keyes KY-040 Rotary Encoder
// pls. be aware, that the SWitch Input has no external Pullup. Turn on internal Port-PullUP
// Switch Input has active low signal -> ReversePolarity
const StepsPerRev=4; MAXCount=1024; MAXSWCount=6; term=5;
// Pins on Connector, where the Encoder is connected to.
ENC_A_HWPin=15; ENC_B_HWPin=16; ENC_S_HWPin=18; // A:GPIO22(DT) B:GPIO23(CLK) SW:GPIO24(SW)
var ENC_hdl:byte; cnt,swcnt:word; dt:TDateTime;
begin
ENC_hdl:=ENC_GetHdl('ENC-Test');// create a Encoder Data-structure. return is a hdl
with ENC_struct[ENC_hdl] do
begin
GPIO_SetStruct(A_Sig,1,GPIO_MAP_HDR_PIN_2_GPIO_NUM(ENC_A_HWPin),'ENC A-Signal (DT)', [INPUT]);
GPIO_SetStruct(B_Sig,2,GPIO_MAP_HDR_PIN_2_GPIO_NUM(ENC_B_HWPin),'ENC B-Signal (CLK)',[INPUT]);
GPIO_SetStruct(S_Sig,3,GPIO_MAP_HDR_PIN_2_GPIO_NUM(ENC_S_HWPin),'ENC Switch (SW)', [INPUT,PullUP,ReversePOLARITY]);
if ENC_Setup(ENC_hdl,true,0,MAXCount,StepsPerRev,UKN) then
begin
cnt:=0; swcnt:=0;
writeln('Do some manual rotation on encoder. Prog will terminate, if Switch was pressed ',term,' times');
writeln('Used Pins on Connector, A-Pin:',A_SIG.HWPin,' B-Pin:',B_SIG.HWPin,' SW-Pin:',S_SIG.HWPin);
writeln('Used GPIOs with Signal: A on GPIO',A_SIG.gpio,', B on GPIO',B_Sig.gpio,', SW on GPIO',S_Sig.gpio);
writeln('MAXCount:',MAXCount,' MAXSWCount:',MAXSWCount-1);
// InitCriticalSection(ENC_CS);
SetTimeOut(dt,TestTimeOut_sec*1000);
repeat // Main Loop
delay_msec(500); // wait x millisec, relevant for reporting only
swcnt:=round(ENC_GetSwitch(ENC_hdl));
writeln( 'Counter: ', round(ENC_GetVal(ENC_hdl,0)):5,
' Cycles: ', round(ENC_GetVal(ENC_hdl,1)):5,
' Switch: ', (swcnt mod MAXSWCount):5,
' PressTime: ', round(ENC_GetSwPtime(ENC_hdl)):5, // msec
' TurnRate: ', ENC_GetVal(ENC_hdl,4):4:0,'Hz'
); // switch cnt 0..(MAXSWCount-1)
inc(cnt);
until (swcnt>=term) or TimeElapsed(dt); // end, if Encoder Switch was pressed <term> times
// DoneCriticalSection(ENC_CS);
writeln('Encoder Thread will terminate');
ENC_End(ENC_hdl);
end else Log_Writeln(Log_ERROR,'ENC_Test: can not init ENC datastruct');
end; // with
writeln('ENC Test end.');
end;
function TRIG_GetValue(hdl:integer; var timesig_ms:longint):integer;
// out: -1:NO IN signal detected; 0:IN signal active;
// out: 1:IN signal not active anymore, lastsignaltime in ms
var _res:integer;
begin
_res:=-1;
if (hdl>=0) and (hdl<Length(TRIG_struct)) then
begin
with TRIG_struct[hdl] do
begin
EnterCriticalSection(TRIG_CS);
if flg then _res:=0; // in signal high
if ((not flg) and (tim_ms>0)) then _res:=1; // in signal down
if _res=1 then begin timesig_ms:=tim_ms; tim_ms:=0; end;
LeaveCriticalSection(TRIG_CS);
end; // with
end;
TRIG_GetValue:=_res;
end;
function TRIG_IN_Thread(ptr:pointer):ptrint;
var _hdl:longint;
begin
_hdl:=longint(ptr);
if (_hdl>=0) and (_hdl<Length(TRIG_struct)) then
begin
with TRIG_struct[_hdl] do
begin
Thread_SetName(desc);
ThreadCtrl.ThreadRunning:=true; ThreadCtrl.TermThread:=false;
// writeln('ThreadStart ',TermThread,' ',sleeptime_ms);
InitCriticalSection(TRIG_CS);
repeat
GPIO_Switch(TGPIO); // IN Part: Get HW-Signal and update DataStruct
with TGPIO do
begin
if ein and (not flg) then
begin
EnterCriticalSection(TRIG_CS);
SyncTime:=now; // start time
tim_ms:=0;
flg:=true;
LeaveCriticalSection(TRIG_CS);
end;
if (not ein) and flg then
begin
EnterCriticalSection(TRIG_CS);
tim_ms:=MilliSecondsBetween(now,SyncTime);
flg:=false;
LeaveCriticalSection(TRIG_CS);
end;
delay_msec(SyncTime_ms);
end; // with
until ThreadCtrl.TermThread;
DoneCriticalSection(TRIG_CS);
end; // with
end;
TRIG_IN_Thread:=0;
end;
procedure TRIG_SetValue(hdl:integer; timesig_ms:longint);
begin
if (hdl>=0) and (hdl<Length(TRIG_struct)) then
begin
with TRIG_struct[hdl] do
begin
EnterCriticalSection(TRIG_CS);
tim_ms:=timesig_ms; flg:=true;
LeaveCriticalSection(TRIG_CS);
end; // with
end;
end;
function TRIG_OUT_Thread(ptr:pointer):ptrint;
var _hdl:longint;
begin
_hdl:=longint(ptr);
if (_hdl>=0) and (_hdl<Length(TRIG_struct)) then
begin
with TRIG_struct[_hdl] do
begin
Thread_SetName(desc);
ThreadCtrl.ThreadRunning:=true; ThreadCtrl.TermThread:=false;
// writeln('ThreadStart ',TermThread,' ',sleeptime_ms);
InitCriticalSection(TRIG_CS);
repeat
with TGPIO do
begin
EnterCriticalSection(TRIG_CS);
if (tim_ms>0) then
begin
GPIO_set_pin(gpio,true);
delay_msec(tim_ms);
GPIO_set_pin(gpio,false);
end;
if (tim_ms<0) then
begin
GPIO_set_pin(gpio,false);
delay_msec(abs(tim_ms));
GPIO_set_pin(gpio,true);
end;
tim_ms:=0;
LeaveCriticalSection(TRIG_CS);
delay_msec(SyncTime_ms);
end; // with
until ThreadCtrl.TermThread;
DoneCriticalSection(TRIG_CS);
end; // with
end;
TRIG_OUT_Thread:=0;
end;
procedure TRIG_End(hdl:integer);
var i:integer;
begin
if (hdl<0) then
begin
for i:= 1 to Length(TRIG_struct) do Thread_End(TRIG_struct[i-1].ThreadCtrl,100);
SetLength(TRIG_struct,0);
end
else
begin
if (hdl>=0) and (hdl<Length(TRIG_struct)) then Thread_End(TRIG_struct[hdl].ThreadCtrl,100);
end;
end;
function TRIG_Reg(gpio:longint; descr:string; flags:s_port_flags; synctim_ms:longword):integer;
var _hdl,mode:integer;
begin
_hdl:=-1;
if (gpio>=0) then
begin
SetLength(TRIG_struct,Length(TRIG_struct)+1); _hdl:=Length(TRIG_struct)-1;
with TRIG_struct[_hdl] do
begin
desc:=descr; tim_ms:=0; SyncTime:=now; flg:=false; SyncTime_ms:=synctim_ms; mode:=-1;
if (INPUT IN flags) then mode:=0;
if (OUTPUT IN flags) then mode:=1;
if mode>=0 then GPIO_SetStruct(TGPIO,1,gpio,desc,flags);
case mode of
0: if GPIO_Setup (TGPIO)
then Thread_Start(ThreadCtrl,@TRIG_IN_Thread, pointer(_hdl),0,-1)
else _hdl:=-1;
1: if GPIO_Setup (TGPIO)
then Thread_Start(ThreadCtrl,@TRIG_OUT_Thread,pointer(_hdl),0,-1)
else _hdl:=-1;
else _hdl:=-1;
end;
if _hdl=-1 then SetLength(TRIG_struct,Length(TRIG_struct)-1);
end; // with
end;
TRIG_Reg:=_hdl;
end;
procedure TRIG_IN_Test;
const HWPIN=12;
var hdl:integer; timesig_ms:longint;
begin
RPI_HW_Start([InstSignalHandler]);
hdl:=TRIG_Reg(GPIO_MAP_HDR_PIN_2_GPIO_NUM(HWPIN),'TrigInTest',[INPUT],TRIG_SyncTime_c);
if (hdl>=0) then
begin
repeat
if TRIG_GetValue(hdl,timesig_ms)=1 then
writeln('Got a TimeSignal on HWPIN#',HWPIN,' with ',timesig_ms,' msec');
delay_msec(1000); // only for report timing
until terminateProg;
end;
end;
procedure Show_Buffer(var data:I2C_databuf_t);
begin
if LOG_Level<=LOG_DEBUG then LOG_Writeln(LOG_DEBUG,HexStr(data.buf));
end;
{$IFDEF UNIX}
function MapUSB(devpath:string):string; // e.g. MapUSB('/dev/ttyUSB0') -> /dev/bus/usb/002/004
var dpath:string;
begin
dpath:='echo /dev/bus/usb/`udevadm info --name='+devpath+' --attribute-walk ';
dpath:=dpath+'| sed -n ''s/\s*ATTRS{\(\(devnum\)\|\(busnum\)\)}==\"\([^\"]\+\)\"/\4/p'' ';
dpath:=dpath+'| head -n 2 | awk ''{$1 = sprintf("%03d", $1); print}''` ';
dpath:=dpath+'| tr " " "/"';
//writeln('MapUSB:',dpath);
//echo /dev/bus/usb/`udevadm info --name=/dev/ttyUSB0 --attribute-walk | sed -n 's/\s*ATTRS{\(\(devnum\)\|\(busnum\)\)}==\"\([^\"]\+\)\"/\4/p' | head -n 2 | awk '{$1 = sprintf("%03d", $1); print}'` | tr " " "/"
call_external_prog(LOG_NONE,dpath,dpath); dpath:=RM_CRLF(dpath);
if (dpath='') or (upper(dpath)='/DEV/BUS/USB/') then dpath:='';
if (dpath<>'') then if not FileExists(dpath) then dpath:='';
MapUSB:=dpath;
end;
function USB_Reset(buspath:string):integer; // call e.g. USB_Reset('/dev/bus/usb/002/004');
var rc,fd,i:integer; devpath:string;
begin
rc:=-1;
//writeln('buspath:',buspath,' ',USBDEVFS_RESET);
if (buspath<>'') then
begin
for i:=1 to Anz_Item(buspath,',','') do
begin
devpath:=Select_Item(buspath,',','',i);
if (devpath='') or (not FileExists(devpath)) then
begin
LOG_Writeln(LOG_ERROR,'USB_Reset: no valid device path '+devpath);
end
else
begin
fd := fpopen(devpath, O_WRONLY);
if (fd < 0) then
begin
LOG_Writeln(LOG_ERROR,'USB_Reset: Error opening device '+devpath);
end
else
begin
LOG_Writeln(LOG_DEBUG,'USB_Reset: Resetting USB device '+devpath);
rc := fpioctl(fd, USBDEVFS_RESET, nil);
if (rc<0) then begin LOG_Writeln(LOG_ERROR,'USB_Reset: Error in ioctl '+LNX_ErrDesc(fpgeterrno)+' '+devpath); end
else begin LOG_Writeln(LOG_DEBUG,'USB_Reset: successful '+Num2Str(rc,0)+' '+devpath); rc:=0; end;
fpclose(fd);
if rc=0 then delay_msec(2000);
end;
end;
end;
end;
USB_Reset:=rc;
end;
{$ELSE}
function MapUSB(devpath:string):string; begin MapUSB:=''; end;
function USB_Reset(buspath:string):integer; begin USB_Reset:=-1; end;
{$ENDIF}
procedure I2C_EnterCriticalSection(busnum:byte); begin EnterCriticalSection(I2C_bus[busnum].I2C_CS); end;
procedure I2C_LeaveCriticalSection(busnum:byte); begin LeaveCriticalSection(I2C_bus[busnum].I2C_CS); end;
procedure I2C_Show_struct(busnum:byte);
begin
with I2C_buf[busnum] do
begin
Log_Writeln(LOG_DEBUG,'I2C Struct[0x'+HexStr(busnum,2)+']:');
Log_Writeln(LOG_DEBUG,' .hdl: '+Num2Str(hdl,0));
Log_Writeln(LOG_DEBUG,' .buf: 0x'+HexStr(buf));
end;
end;
procedure I2C_Display_struct(busnum:byte; comment:string);
begin
LOG_LevelSave;
LOG_LEVEL(LOG_DEBUG);
Log_Writeln(LOG_Level,comment);
I2C_show_struct(busnum);
LOG_LevelRestore;
end;
function I2C_ChkBusAdr(busnum,baseadr:word):boolean;
var _ok:boolean;
begin
_ok:=((busnum<=I2C_max_bus) and (baseadr>=$03) and (baseadr<=$77));
if not _ok then
LOG_Writeln(LOG_ERROR,'I2C_ChkBusAdr['+HexStr(busnum,2)+'/0x'+HexStr(baseadr,2)+']: not valid');
I2C_ChkBusAdr:=_ok;
end;
function I2C_GetSpeed(bus:byte):longint;
var _speed_Hz:longint; sh:string;
begin
{$warnings off}
if (bus>=0) and (bus<=1) then
{$warnings on}
begin
// xxd -ps /sys/class/i2c-adapter/i2c-1/of_node/clock-frequency
_speed_Hz:=RPI_BCM2835_GetNodeValue('/sys/class/i2c-adapter/i2c-'+Num2Str(bus,0)+'/of_node/clock-frequency',sh);
if _speed_Hz<0 then
begin // last chance, try dmesg
call_external_prog(LOG_NONE,'dmesg | grep bcm2708_i2c',sh);
sh:=Select_Item(Upper (sh), '(BAUDRATE','',2); // 400000)
sh:=Select_Item(Trimme(sh,4), ')','',1); // 400000
if not Str2Num(sh,_speed_Hz) then _speed_Hz:=-1;
end;
end else _speed_Hz:=100000;
I2C_GetSpeed:=_speed_Hz;
end;
function I2C_GetFuncs(bus:byte):longword;
var funcs:longword;
begin
funcs:=0;
with I2C_buf[bus] do
begin
if (hdl>=0) then
begin
if fpIOctl(hdl,I2C_FUNCS,@funcs)<0 then LOG_Writeln(LOG_ERROR,'I2C_GetFuncs: '+LNX_ErrDesc(fpgeterrno));
end;
end; // with
I2C_GetFuncs:=funcs;
end;
procedure I2C_ShowFuncs(bus:byte);
var i:integer; sh:string;
begin
sh:='';
for i:=0 to 30 do
begin
case ((1 shl i) and RPI_I2C_GetFuncs(bus)) of
I2C_FUNC_I2C: sh:=sh+'I2C_FUNC_I2C';
I2C_FUNC_10BIT_ADDR: sh:=sh+'I2C_FUNC_10BIT_ADDR';
I2C_FUNC_PROTOCOL_MANGLING: sh:=sh+'I2C_FUNC_PROTOCOL_MANGLING';
I2C_FUNC_SMBUS_PEC: sh:=sh+'I2C_FUNC_SMBUS_PEC';
I2C_FUNC_NOSTART: sh:=sh+'I2C_FUNC_NOSTART';
I2C_FUNC_SLAVE: sh:=sh+'I2C_FUNC_SLAVE';
I2C_FUNC_SMBUS_BLOCK_PROC_CALL: sh:=sh+'I2C_FUNC_SMBUS_BLOCK_PROC_CALL';
I2C_FUNC_SMBUS_QUICK: sh:=sh+'I2C_FUNC_SMBUS_QUICK';
I2C_FUNC_SMBUS_READ_BYTE: sh:=sh+'I2C_FUNC_SMBUS_READ_BYTE';
I2C_FUNC_SMBUS_WRITE_BYTE: sh:=sh+'I2C_FUNC_SMBUS_WRITE_BYTE';
I2C_FUNC_SMBUS_READ_BYTE_DATA: sh:=sh+'I2C_FUNC_SMBUS_READ_BYTE_DATA';
I2C_FUNC_SMBUS_WRITE_BYTE_DATA: sh:=sh+'I2C_FUNC_SMBUS_WRITE_BYTE_DATA';
I2C_FUNC_SMBUS_READ_WORD_DATA: sh:=sh+'I2C_FUNC_SMBUS_READ_WORD_DATA';
I2C_FUNC_SMBUS_WRITE_WORD_DATA: sh:=sh+'I2C_FUNC_SMBUS_WRITE_WORD_DATA';
I2C_FUNC_SMBUS_PROC_CALL: sh:=sh+'I2C_FUNC_SMBUS_PROC_CALL';
I2C_FUNC_SMBUS_READ_BLOCK_DATA: sh:=sh+'I2C_FUNC_SMBUS_READ_BLOCK_DATA';
I2C_FUNC_SMBUS_WRITE_BLOCK_DATA: sh:=sh+'I2C_FUNC_SMBUS_WRITE_BLOCK_DATA';
I2C_FUNC_SMBUS_READ_I2C_BLOCK: sh:=sh+'I2C_FUNC_SMBUS_READ_I2C_BLOCK';
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK: sh:=sh+'I2C_FUNC_SMBUS_WRITE_I2C_BLOCK';
end;
sh:=sh+' ';
end;
sh:=Trimme(StringReplace(sh,'I2C_FUNC_','',[rfReplaceAll,rfIgnoreCase]),4);
writeln('I2C_FUNC_ ',sh);
end;
procedure I2C_CleanBuffer(busnum:byte);
begin with I2C_buf[busnum] do begin hdl:=-1; buf:=''; reperr:=true; end; end;
procedure I2C_Start(busnum:integer);
var _I2C_path:string;
begin
_I2C_path:='';
with I2C_buf[busnum] do
begin
I2C_CleanBuffer(busnum);
I2C_bus[busnum].I2C_useCS:=false;
I2C_bus[busnum].I2C_speed:=0;
I2C_bus[busnum].I2C_funcs:=0;
{$IFDEF UNIX}
if RPI_run_on_ARM then
begin
_I2C_path:=I2C_path_c+Num2Str(busnum,0);
if (_I2C_path<>'') and FileExists(_I2C_path) then hdl:=fpOpen(_I2C_path,O_RdWr);
if hdl>=0 then
begin
{$R-}
I2C_bus[busnum].I2C_useCS:=false;
InitCriticalSection(I2C_bus[busnum].I2C_CS);
I2C_bus[busnum].I2C_speed:=I2C_GetSpeed(busnum);
I2C_bus[busnum].I2C_funcs:=I2C_GetFuncs(busnum);
{$R+}
if not RPI_I2C_ChkFuncs(busnum,I2C_FUNC_I2C) then
LOG_Writeln(LOG_ERROR,'I2C_start[0x'+HexStr(busnum,2)+']: no I2C_FUNC_I2C');
end;
end;
{$ENDIF}
if (hdl<0) and (busnum=RPI_I2C_busgen) then
LOG_Writeln(LOG_ERROR,'I2C_start[0x'+HexStr(busnum,2)+']: '+_I2C_path);
end; // with
end;
procedure I2C_Start; var b:byte; begin for b:=0 to I2C_max_bus do I2C_Start(b); end;
procedure I2C_End(busnum:integer);
begin
{$IFDEF UNIX}
if RPI_run_on_ARM then
if I2C_buf[busnum].hdl>=0 then
begin
// DoneCriticalSection(I2C_bus[busnum].I2C_CS); // waits forever
fpClose(I2C_buf[busnum].hdl);
end;
{$ENDIF}
I2C_buf[busnum].hdl:=-1;
end;
procedure I2C_Close_All; var b:byte; begin for b:=0 to I2C_max_bus do I2C_End(b); end;
function I2C_bus_read(busnum,baseadr:word; cmds:string; len:byte; errhdl:integer):integer;
var rslt:integer; lgt:byte; test:boolean; info:string;
begin
rslt:=-1;
try
with I2C_buf[busnum] do
begin
if (hdl>=0) then
begin
test:=false; lgt:=len;
info:='I2C_bus_read[0x'+HexStr(busnum,2)+'/0x'+HexStr(baseadr,2);
if cmds<>'' then info:=info+'/0x'+HexStr(cmds);
info:=info+']: ';
// writeln(info+' 0x'+HexStr(cmds));
{$warnings off}
if lgt>SizeOf(buf) then
begin
LOG_Writeln(LOG_ERROR,info+'Length exceed buflgt, got: '+Num2Str(len,0)+' max: '+Num2Str(SizeOf(buf),0));
lgt:=SizeOf(buf);
end;
{$warnings on}
{$IFDEF UNIX}
// if hdl<0 then I2C_start(data);
{$warnings off}
// rslt:=0;
// rslt:=fpIOctl(hdl,I2C_TIMEOUT,pointer(1)); if rslt<0 then begin LOG_Writeln(LOG_ERROR,'I2C_TIMEOUT: '+LNX_ErrDesc(fpgeterrno)); exit(rslt); end;
// rslt:=fpIOctl(hdl,I2C_RETRIES,pointer(2)); if rslt<0 then begin LOG_Writeln(LOG_ERROR,'I2C_RETRIES: '+LNX_ErrDesc(fpgeterrno)); exit(rslt); end;
rslt:=fpIOctl(hdl,I2C_SLAVE, pointer(baseadr));
{$warnings on}
if rslt<0 then
begin
LOG_Writeln(LOG_ERROR,info+'failed to select device: '+LNX_ErrDesc(fpgeterrno));
ERR_MGMT_UPD(errhdl,_IOC_NONE,lgt,false);
buf:='';
exit(rslt);
end;
if cmds<>'' then
begin
rslt:=fpWrite(hdl,cmds[1],Length(cmds));
if rslt<>1 then
begin
LOG_Writeln(LOG_ERROR,info+'failed to write Register: '+LNX_ErrDesc(fpgeterrno));
ERR_MGMT_UPD(errhdl,_IOC_WRITE,lgt,false);
buf:='';
exit(rslt);
end;
end;
SetLength(buf,1);
rslt:=fpRead(hdl,buf[1],lgt);
{$ENDIF}
if test then I2C_Display_struct(busnum,'I2C_bus_read:');
if rslt<0 then
begin
LOG_Writeln(LOG_DEBUG,info+'failed to read device: '+LNX_ErrDesc(fpgeterrno));
ERR_MGMT_UPD(errhdl,_IOC_READ,lgt,false);
buf:='';
end
else
begin
SetLength(buf,rslt);
ERR_MGMT_UPD(errhdl,_IOC_READ,rslt,true);
if rslt<lgt then
LOG_Writeln(LOG_ERROR,info+'Short read, errnum: '+Num2Str(rslt,0)+' expected length: '+Num2Str(lgt,0)+' got: '+Num2Str(rslt,0));
end;
end;
end; // with
except
On E_rpi_hal_Exception :Exception do writeln('I2C_bus_read: ',E_rpi_hal_Exception.Message);
end;
I2C_bus_read:=rslt;
end;
function I2C_bus_read(busnum,baseadr,basereg:word; len:byte; errhdl:integer):integer;
var cmds:string;
begin
if basereg<>I2C_UseNoReg then cmds:=char(byte(basereg)) else cmds:='';
I2C_bus_read:=I2C_bus_read(busnum,baseadr,cmds,len,errhdl);
end;
function I2C_bus_WrRd(busnum,baseadr:word; const WRbuf:string; WRflgs:word; var RDbuf:string; RDflgs:word; RDlen:byte; errhdl:integer):integer;
// https://elixir.bootlin.com/linux/v3.19.8/source/drivers/i2c/i2c-core.c
// https://gist.github.com/JamesDunne/9b7fbedb74c22ccc833059623f47beb7
// http://home.hiwaay.net/~jeffj1/i2c-bcm2708.c
// https://www.raspberrypi.org/forums/viewtopic.php?f=44&t=15840&hilit=i2c+repeated+start&start=50
// not ready, experimental
// @400khz bus speed, each spacing time betweeen two transfers: ca. 30us
// with (I2C_M_RD or I2C_M_NOSTART) 2.5us
// without 14us between I2C_M_WR / I2C_M_RD
var rslt,oklen:integer; msgset:I2C_rdwr_ioctl_data_t; iomsgs:array[0..1] of I2C_msg_t;
begin
try
with I2C_buf[busnum] do
begin
with msgset do
begin
nmsgs:= 0;
if (Length(WRbuf)>0) then
begin
oklen:= Length(WRbuf);
iomsgs[nmsgs].addr:= baseadr;
iomsgs[nmsgs].bptr:= @WRbuf[1];
iomsgs[nmsgs].len:= oklen;
iomsgs[nmsgs].flags:= I2C_M_WR or (WRflgs and (not I2C_M_RD));
inc (nmsgs);
end;
if (RDlen>0) then
begin
oklen:= RDlen;
iomsgs[nmsgs].addr:= baseadr;
iomsgs[nmsgs].bptr:= @buf[1];
iomsgs[nmsgs].len:= oklen;
iomsgs[nmsgs].flags:= I2C_M_RD or RDflgs; // I2C_M_NOSTART 2.5us // no I2C_M_NOSTART 13us @400khz
inc (nmsgs);
end;
msgs := @iomsgs;
if (nmsgs>0) then
begin
{$IFDEF UNIX}
rslt:=fpIOCTL(hdl, I2C_RDWR,@msgset);
{$ELSE}
rslt:= -1;
{$ENDIF}
end else rslt:= -1;
end; // with
if (rslt<0) then
begin
SetLength(buf, 0);
if reperr then
LOG_Writeln(LOG_ERROR,'I2C_bus_WrRd[0x'+HexStr(busnum,2)+'/0x'+HexStr(baseadr,2)+']: failed to read device: '+LNX_ErrDesc(fpgeterrno));
ERR_MGMT_UPD(errhdl, _IOC_READ, RDlen, false);
// ERR_MGMT_UPD(errhdl, _IOC_WRITE,Length(WRbuf),false);
end
else
begin
SetLength(buf, RDlen);
ERR_MGMT_UPD(errhdl, _IOC_READ, RDlen, true);
// ERR_MGMT_UPD(errhdl, _IOC_WRITE,Length(WRbuf),true);
rslt:= oklen;
end;
RDbuf:= buf;
end; // with
except
On E_rpi_hal_Exception :Exception do
begin
LOG_Writeln(LOG_ERROR, 'I2C_bus_WrRd[0x'+HexStr(busnum,2)+'/0x'+HexStr(baseadr,2)+']: exception: '+E_rpi_hal_Exception.Message);
rslt:= -1;
end;
end;
I2C_bus_WrRd:= rslt;
end;
procedure I2C_SwitchCombined(openmode:boolean);
var fd:cint; sh:string;
begin
{$IFDEF UNIX}
fd:=fpOpen(I2C_COMBINED_path_c,O_WRONLY);
if (fd>=0) then
begin
if openmode then sh:='1'+#$0a else sh:='0'+#$0a;
fpwrite(fd,sh,Length(sh));
fpclose(fd);
end;
{$ENDIF}
end;
function I2C_bus_SEGMENTS(var zipdata:I2C_rdwr_zip_data_t):integer;
// http://www.the-cains-group.net/2017_Workshops/Mar17/5_struct_i2c_msg.html
// work in progress, not ready
var rslt:integer;
begin
rslt:=-1;
with zipdata do
begin
// writeln('#:',Length(iomsgs),' ',datlen,' ',I2C_buf[busno].hdl);
if (msgset.nmsgs>0) then
begin
if (I2C_buf[busno].hdl>=0) then
begin
{$IFDEF UNIX}
rslt:=fpIOCTL(I2C_buf[busno].hdl,I2C_RDWR,@msgset);
{$ENDIF}
if rslt<0 then
begin
LOG_Writeln(LOG_ERROR,'I2C_bus_SEGMENTS[0x'+HexStr(busno,2)+'/0x'+HexStr(iomsgs[0].addr,2)+']: failed to read device: '+LNX_ErrDesc(fpgeterrno));
ERR_MGMT_UPD(hdl,_IOC_READ,datlen,false);
I2C_buf[busno].buf:='';
end
else
begin
SetLength(I2C_buf[busno].buf,datlen); rslt:=datlen;
ERR_MGMT_UPD(hdl,_IOC_READ,datlen,true);
end;
end;
end else LOG_Writeln(LOG_ERROR,'I2C_bus_SEGMENTS: Length=0');
end; // with
I2C_bus_SEGMENTS:=rslt;
end;
procedure I2C_prep_iomsg(var zipdata:I2C_rdwr_zip_data_t; baseadr:word; const WRbuf:string; WRflgs:word; RDflgs:word; RDlen:byte);
begin
with zipdata do
begin
with msgset do
begin
if (Length(WRbuf)>0) and (nmsgs<I2C_RDWR_IOCTL_MAX_MSGS) then
begin
with iomsgs[nmsgs] do
begin
addr:= baseadr;
bptr:= @WRbuf[1];
len:= Length(WRbuf);
flags:= I2C_M_WR or (WRflgs and (not I2C_M_RD));
inc( nmsgs);
end; // with
end;
if (RDlen>0) and (nmsgs<I2C_RDWR_IOCTL_MAX_MSGS) then
begin
with iomsgs[nmsgs] do
begin
addr:= baseadr;
bptr:= @I2C_buf[busno].buf[datlen+1];
len:= RDlen;
flags:= I2C_M_RD or RDflgs;
inc(datlen, RDlen);
inc( nmsgs);
end; // with
end;
end; // with
end; // with
end;
procedure I2C_show_ZIPdata(var zipdata:I2C_rdwr_zip_data_t);
var i:integer;
begin
with zipdata do
begin
writeln('datlen:',datlen,' nmsgs:',msgset.nmsgs);
for i:=1 to msgset.nmsgs do
begin
with iomsgs[i-1] do
begin
writeln((i-1):2,' addr:0x',HexStr(addr,2),' ptr:0x',HexStr(bptr),' len:',Num2Str(len,2),' flags:0x',HexStr(flags,4));
end; // with
end;
end; // with
end;
procedure I2C_init_ZIPdata(var zipdata:I2C_rdwr_zip_data_t; busnum:word; errhdl:integer);
begin
with zipdata do
begin
msgset.msgs:= @iomsgs[0];
hdl:= errhdl;
busno:= busnum;
datlen:= 0;
// for msgset.nmsgs:=1 to Length(iomsgs) do I2C_prep_iomsg(zipdata,I2C_UseNoReg,I2C_M_RD,0);
msgset.nmsgs:= 0;
end; // with
end;
procedure I2C_ZIP_Test;
// work in progress, not ready
const adr=$70; lgt=2;
var rslt:integer; zipdata:I2C_rdwr_zip_data_t; sh:string;
begin
//writeln('Funcs: 0x'+HexStr(RPI_I2C_GetFuncs(RPI_I2C_busgen),8)); I2C_ShowFuncs(RPI_I2C_busgen);
rslt:=I2C_bus_WrRd(RPI_I2C_busgen,adr,'',0, sh,0, lgt,NO_ERRHNDL);
writeln(rslt,' ',HexStr(I2C_buf[RPI_I2C_busgen].buf));
writeln;
delay_msec(1);
with zipdata do
begin
I2C_init_ZIPdata(zipdata,RPI_I2C_busgen,NO_ERRHNDL);
I2C_prep_iomsg (zipdata,adr,'',0,0,lgt);
I2C_prep_iomsg (zipdata,adr,'',0,0,lgt); // ERR I2C_bus_SEGMENTS[0x01/0x70]: failed to read device: (95) Operation not supported on transport endpoint
I2C_show_ZIPdata(zipdata);
I2C_SwitchCombined(true); // combined mode only for bcm270x !!!!!
rslt:=I2C_bus_SEGMENTS(zipdata);
I2C_SwitchCombined(false);
writeln('buf['+Num2Str(rslt,2)+']: 0x',HexStr(I2C_buf[RPI_I2C_busgen].buf));
end; // with
end;
function oldI2C_string_read(busnum,baseadr:word; cmds:string; len:byte; errhdl:integer; var outs:string):integer;
var rslt:integer; lgt:byte;
begin
with I2C_buf[busnum] do
begin
lgt:=len;
if len>c_max_Buffer then
begin
LOG_Writeln(LOG_ERROR,'I2C_string_read[0x'+HexStr(busnum,2)+'/0x'+HexStr(baseadr,2)+'/0x'+HexStr(cmds)+']: Length exceed buflgt, got: '+Num2Str(len,0)+' max: '+Num2Str(c_max_Buffer,0));
buf:='';
exit(-1);
lgt:=c_max_Buffer;
end;
// writeln('I2C_string_read1: I2Caddr:0x'+HexStr(baseadr,2)+' reg:0x'+HexStr(cmds)+' busnum:0x'+HexStr(busnum,2)+' lgt:0x'+HexStr(lgt,2));
rslt:=I2C_bus_read(busnum,baseadr,cmds,lgt,errhdl);
// writeln('I2C_string_read2: I2Caddr:0x'+HexStr(baseadr,2)+' reg:0x'+HexStr(cmds)+' busnum:0x'+HexStr(busnum,2)+' lgt:0x'+HexStr(lgt,2)+' rslt:'+Num2Str(rslt,0));
outs:=buf;
oldI2C_string_read:=rslt;
end; // with
end;
function oldI2C_string_read(busnum,baseadr,basereg:word; len:byte; errhdl:integer; var outs:string):integer;
var cmds:string;
begin
if basereg<>I2C_UseNoReg then cmds:=char(byte(basereg)) else cmds:='';
oldI2C_string_read:=oldI2C_string_read(busnum,baseadr,cmds,len,errhdl,outs);
end;
function I2C_string_read(busnum,baseadr,basereg:word; RDlen:byte; errhdl:integer; var RDbuf:string):integer;
var _obuf:string;
begin
if (basereg<>I2C_UseNoReg) then _obuf:=char(byte(basereg)) else _obuf:='';
I2C_string_read:=I2C_bus_WrRd(busnum,baseadr,_obuf,0,RDbuf,0,RDlen,errhdl);
end;
function I2C_string_read(busnum,baseadr:word; const WRbuf:string; RDlen:byte; errhdl:integer; var RDbuf:string):integer;
begin I2C_string_read:=I2C_bus_WrRd(busnum,baseadr,WRbuf,0,RDbuf,0,RDlen,errhdl); end;
function I2C_string_write(busnum,baseadr:word; const WRbuf:string; errhdl:integer):integer;
var _obuf:string;
begin I2C_string_write:=I2C_bus_WrRd(busnum,baseadr,WRbuf,0,_obuf,0,0,errhdl); end;
function I2C_string_write(busnum,baseadr,basereg:word; WRbuf:string; errhdl:integer):integer;
var _obuf:string;
begin
if (basereg<>I2C_UseNoReg) then WRbuf:=char(byte(basereg))+WRbuf;
I2C_string_write:=I2C_bus_WrRd(busnum,baseadr,WRbuf,0,_obuf,0,0,errhdl);
end;
function I2C_word_read(busnum,baseadr,basereg:word; flip:boolean; errhdl:integer):word;
// read from the I2C general purpose bus e.g. s:=I2C_string_read($68,$00,7)
var sh:string; w:word;
begin
w:=0; I2C_string_read(busnum,baseadr,basereg,2,errhdl,sh);
if Length(sh)>=2 then
begin
if flip then w:=word(ord(sh[2]) shl 8) or word(ord(sh[1]))
else w:=word(ord(sh[1]) shl 8) or word(ord(sh[2]));
end;
I2C_word_read:=w;
end;
function I2C_byte_read(busnum,baseadr,basereg:word; errhdl:integer):byte;
// read from the I2C general purpose bus e.g. s:=I2C_string_read($68,$00,7)
var b:byte; sh:string;
begin
I2C_string_read(busnum,baseadr,basereg,1,errhdl,sh);
if Length(sh)>=1 then b:=ord(sh[1]) else b:=0;
I2C_byte_read:=b;
end;
function oldI2C_bus_write(busnum,baseadr:word; errhdl:integer):integer;
var rslt:integer; lgt:byte;
begin
rslt:=-1;
try
with I2C_buf[busnum] do
begin
if (hdl>=0) then
begin
lgt:=Length(buf);
{$IFDEF UNIX}
// writeln('i2cwr: 0x'+HexStr(buf)+' ',hdl);
{$warnings off} rslt:=fpIOctl(hdl,I2C_SLAVE,pointer(baseadr)); {$warnings on}
if rslt<0 then
begin
LOG_Writeln(LOG_ERROR,'I2C_bus_write[0x'+HexStr(busnum,2)+'/0x'+HexStr(baseadr,2)+']: failed to open device: '+LNX_ErrDesc(fpgeterrno));
ERR_MGMT_UPD(errhdl,_IOC_NONE,lgt,false);
exit(rslt);
end;
rslt:=fpWrite(hdl,buf[1],lgt);
{$ENDIF}
// I2C_Display_struct(busnum,'I2C_bus_write:');
if rslt<0 then
begin
LOG_Writeln(LOG_ERROR,'I2C_bus_write[0x'+HexStr(busnum,2)+'/0x'+HexStr(baseadr,2)+']: failed to write to device: '+LNX_ErrDesc(fpgeterrno));
ERR_MGMT_UPD(errhdl,_IOC_WRITE,lgt,false);
end
else
begin
ERR_MGMT_UPD(errhdl,_IOC_WRITE,lgt,true);
if (rslt<lgt) then
LOG_Writeln(LOG_ERROR,'I2C_bus_write[0x'+HexStr(busnum,2)+'/0x'+HexStr(baseadr,2)+']: short write, errnum: '+Num2Str(rslt,0)+' expected: '+Num2Str(lgt+1,0)+' got: '+Num2Str(rslt,0));
end;
end;
end; // with
except
On E_rpi_hal_Exception :Exception do writeln('I2C_bus_write: ',E_rpi_hal_Exception.Message);
end;
oldI2C_bus_write:=rslt;
end;
function oldI2C_string_write(busnum,baseadr:word; datas:string; errhdl:integer):integer;
begin
if length(datas)>=c_max_Buffer then
begin
LOG_Writeln(LOG_ERROR,'I2C_string_write['+HexStr(busnum,2)+'/'+HexStr(baseadr,2)+'/'+HexStr(datas)+']: data length:'+Num2Str(length(datas),0)+' exceeds buffer size:'+Num2Str(c_max_Buffer,0));
exit(-1);
end;
I2C_buf[busnum].buf:=datas;
oldI2C_string_write:=oldI2C_bus_write(busnum,baseadr,errhdl);
end;
function oldI2C_string_write(busnum,baseadr,basereg:word; datas:string; errhdl:integer):integer;
var _datas:string;
begin
_datas:=datas; if basereg<>I2C_UseNoReg then _datas:=char(byte(basereg))+_datas;
oldI2C_string_write:=oldI2C_string_write(busnum,baseadr,_datas,errhdl);
end;
function I2C_word_write(busnum,baseadr,basereg:word; data:word; flip:boolean; errhdl:integer):integer;
var sh:string; // e.g: 0x4321
begin
if flip then sh:=char(byte(data))+char(byte(data shr 8)) // 2143
else sh:=char(byte(data shr 8))+char(byte(data)); // 4321
I2C_word_write:=I2C_string_write(busnum,baseadr,basereg,sh,errhdl);
end;
function I2C_word_write(baseadr,basereg:word; data:word; flip:boolean; errhdl:integer):integer;
begin I2C_word_write:=I2C_word_write(RPI_I2C_busgen,baseadr,basereg,data,flip,errhdl); end;
function I2C_byte_write(busnum,baseadr,basereg:word; data:byte; errhdl:integer):integer;
begin I2C_byte_write:=I2C_string_write(busnum,baseadr,basereg,char(data),errhdl); end;
procedure eeprom_SetAddr(devaddr:word); begin eeprom_devadr:=devaddr; end;
function eeprom_write_page(startadr:word; datas:string):integer;
//write a string to the EEPROM @ I2C-Adr 0x50 startaddr
begin eeprom_write_page:=I2C_string_write(RPI_I2C_bus2nd,eeprom_devadr,startadr,datas,NO_ERRHNDL); end;
function eeprom_read_page(startadr:word; len:byte; var outs:string):integer;
begin eeprom_read_page:=I2C_string_read(RPI_I2C_bus2nd,eeprom_devadr,startadr,len,NO_ERRHNDL,outs); end;
//https://www.raspberrypi.org/forums/viewtopic.php?p=521067#p521067
function BT_RFCOMM(chan:word; bindatstart:boolean; btdev,desc:string):boolean;
// http://www.raspberry-projects.com/pi/pi-operating-systems/raspbian/bluetooth/serial-over-bluetooth
//IN: chan: eg. 1
//IN: bindatstart: e.g. true
//IN: btdev: xx:xx:xx:xx:xx:xx
//IN: desc: My Bluetooth Connection
const fil='/etc/bluetooth/rfcomm.conf';
var ts:TStringList;
begin
if btdev<>'' then
begin
if desc='' then desc:='BT';
ts:=TStringList.create;
ts.add('rfcomm'+Num2Str(chan,0)+' {');
ts.add(' # Automatically bind the device at startup');
ts.add(' bind '+lower(Bool2YN(bindatstart))+';');
ts.add('');
ts.add(' # Bluetooth address of partner device');
ts.add(' device '+btdev+';');
ts.add('');
ts.add(' # RFCOMM channel for the connection');
ts.add(' channel '+Num2Str(chan,0)+';');
ts.add('');
ts.add(' # Description of the connection');
ts.add(' comment "'+desc+'";');
ts.add('}');
StringList2TextFile(fil,ts);
ts.free;
end;
BT_RFCOMM:=(FileExists(fil));
end;
procedure HW_SetInfoStruct(var DeviceStruct:HW_DevicePresent_t; DevTyp:t_IOBusType; BusNr,HWAdr:integer; dsc:string);
begin with DeviceStruct do begin BusNum:=BusNr; HWAddr:=HWAdr; DevType:=DevTyp; descr:=dsc; end; end;
procedure HW_IniInfoStruct(var DeviceStruct:HW_DevicePresent_t);
begin
HW_SetInfoStruct(DeviceStruct,UnknDev,hdl_unvalid,hdl_unvalid,'');
with DeviceStruct do begin present:=false; Hndl:=hdl_unvalid; data:=''; end;
end;
function SPI_HWT(var DeviceStruct:HW_DevicePresent_t; bus,adr,lgt:word; cmds:string; Handle:integer; rv1,nv1,nv2,dsc:string):boolean;
begin
with DeviceStruct do
begin
HW_IniInfoStruct(DeviceStruct);
HW_SetInfoStruct(DeviceStruct,SPIDev,0,hdl_unvalid,dsc);
present:=true; // Dummy, to do !!!!!!!!! read device to determine if it's there
if present then begin BusNum:=bus; HWaddr:=adr; hndl:=Handle; end;
SPI_HWT:=present;
end;
end;
function I2C_HWT(var DeviceStruct:HW_DevicePresent_t; bus,adr,lgt:word; cmds:string; Handle:integer; rv1,nv1,nv2,dsc:string):boolean;
// I2C HardwareTest. used to determine, device available on i2c bus
// usage e.g. DisplayPresent:=I2C_HWT(RPI_I2C_busnum,LCD_I2C_ADR,#$01,1,'','','LCD');
var _lvl:t_errorlevel; info:string;
begin
with DeviceStruct do
begin
HW_IniInfoStruct(DeviceStruct); data:=''; present:=false; _lvl:=LOG_WARNING;
HW_SetInfoStruct(DeviceStruct,I2CDev,rpi_I2C_busgen,i2c_unvalid_addr,dsc);
info:=dsc+'[0x'+HexStr(bus,2)+'/0x'+HexStr(adr,2);
if cmds<>'' then info:=info+'/0x'+HexStr(cmds);
info:=info+']: ';
// writeln('info:',info);
if I2C_ChkBusAdr(bus,adr) then
begin
I2C_buf[bus].reperr:=false;
I2C_string_read(bus,adr,cmds,lgt,NO_ERRHNDL,data);
I2C_buf[bus].reperr:=true;
present:=(data<>'');
if present then present:=present and (Length(data)= lgt);
if present and (rv1<>'') then present:=present and (HexStr(data)= rv1);
if present and (nv1<>'') then present:=present and (HexStr(data)<>nv1);
if present and (nv2<>'') then present:=present and (HexStr(data)<>nv2);
if present then
begin
_lvl:=LOG_NOTICE;
BusNum:=bus; HWaddr:=adr; hndl:=Handle;
end;
if (data<>'') then info:=info+'0x'+HexStr(data) else info:=info+'nodata';
SAY(_lvl,info);
end;
I2C_HWT:=present;
end; // with
end;
function I2C_HWSpeedT(BusNum,HWaddr,rdlgt:word; loops:longword; cmds,dsc:string):real;
// out: kb/sec
const rdflgs=I2C_M_NOSTART;
var n,cnt,rdcnt,wrcnt:longword; hndl:integer; ok:boolean; tstrt,tende:timespec; r:real; data:string;
begin
hndl:=ERR_NEW_HNDL(HWaddr,'I2C_HWSpeedT['+dsc+']:',0,0);
cnt:=0; wrcnt:=Length(cmds); ok:=true;
clock_gettime(CLOCK_REALTIME,@tstrt);
for n:=1 to loops do
begin
rdcnt:=I2C_bus_WrRd(BusNum,HWaddr,cmds,0,data,rdflgs,rdlgt,hndl);
{$warnings off}
if (rdcnt>=0) then inc(cnt,rdcnt+wrcnt) else ok:=false;
{$warnings on}
end;
clock_gettime(CLOCK_REALTIME,@tende);
//writeln('data: ',HexStr(data));
r:=NanoSecondsBetween(tende,tstrt)/(1000000);
if ok and (r>0) then
begin
cnt:=cnt+round(cnt/8); // protocol overhead, 1Bit (ACK/NACK) per Byte
r:=(cnt/1024)/(r/1000); // kB/sec
end else r:=0;
//if not ok then
ERR_Report(hndl);
I2C_HWSpeedT:=r;
end;
function I2C_HWSpeedT(var DeviceStruct:HW_DevicePresent_t; lgt:word; loops:longword; cmds,dsc:string):real;
var r:real;
begin
r:=0;
with DeviceStruct do
begin
if present then r:=I2C_HWSpeedT(BusNum,HWaddr,lgt,loops,cmds,dsc)
else LOG_Writeln(LOG_ERROR,'I2C_HWSpeedT[0x'+HexStr(BusNum,2)+'/0x'+HexStr(HWaddr,2)+']: '+dsc+' not present');
end; // with
I2C_HWSpeedT:=r;
end;
procedure I2C_test;
{V1.0 30-JUL-2013
test on cli, is I2C bus working and determine baseaddr of device.
Newer version of rpi, I2C bus nr 1. older rpi I2Cbus nr 0.
root@rpi# I2Cdetect -y 0
root@rpi# I2Cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
..
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
on 0x68, this is my RTC DS3232m }
procedure showstr(s:string); begin if s<>'' then writeln(hexstr(s)) else writeln('device is not responding'); end;
const testnr=1;
var s:string;
procedure test1_rtc;
begin
I2C_string_read(RPI_I2C_busgen,$68,$05,2,NO_ERRHNDL,s); showstr(s); // read 2 bytes; I2C device addr = 0x68; StartRegister = 0x05; result: content of reg[5..6] in string s
I2C_string_write(RPI_I2C_busgen,$68,$05,#$08+#$12,NO_ERRHNDL); // write 08 in reg 0x05 and 12 in reg 0x06 // set month register to 08 and year to 12
I2C_string_read(RPI_I2C_busgen,$68,$05,2,NO_ERRHNDL,s); showstr(s); // read 2 bytes
I2C_string_write(RPI_I2C_busgen,$68,$05,#$07+#$13,NO_ERRHNDL); // write 07 in reg 0x05 and 13 in reg 0x06 // restore month and year
LOG_Level(LOG_DEBUG);
I2C_show_struct(RPI_I2C_busgen);
LOG_Level(LOG_WARNING);
end;
procedure test2_mma7660;
// chip: accelerometer
begin
I2C_string_write (RPI_I2C_busgen,$4c,$07,#$00,NO_ERRHNDL); // write 00 in reg 0x07
I2C_string_write (RPI_I2C_busgen,$4c,$07,#$04,NO_ERRHNDL); // write 04 in reg 0x07
I2C_string_write (RPI_I2C_busgen,$4c,$00,#$04,NO_ERRHNDL);
I2C_string_write (RPI_I2C_busgen,$4c,$01,#$03,NO_ERRHNDL);
I2C_string_write (RPI_I2C_busgen,$4c,$02,#$02,NO_ERRHNDL);
// I2C_string_read(RPI_I2C_busgen,$4c,$07,1,NO_ERRHNDL,s); showstr(s); // read 1 byte; I2C device addr=0x4c; Register=0x07; MOdeRegister
I2C_string_read(RPI_I2C_busgen,$4c,$00,1,NO_ERRHNDL,s); showstr(s); // read 1 byte; I2C device addr=0x4c; Register=0x00; XOUT
I2C_string_read(RPI_I2C_busgen,$4c,$01,1,NO_ERRHNDL,s); showstr(s); // read 1 byte; I2C device addr=0x4c; Register=0x01; YOUT
I2C_string_read(RPI_I2C_busgen,$4c,$02,1,NO_ERRHNDL,s); showstr(s); // read 1 byte; I2C device addr=0x4c; Register=0x02; ZOUT
LOG_Level(LOG_DEBUG);
I2C_show_struct(RPI_I2C_busgen);
LOG_Level(LOG_WARNING);
end;
begin
case testnr of
1 : test1_rtc;
2 : test2_mma7660;
end; // case
end;
procedure SPI_show_struct(var spi_strct:spi_ioc_transfer_t);
const errlvl=LOG_WARNING;
begin
with spi_strct do
begin
Log_Writeln(errlvl,'SPI Struct: 0x'+HexStr(longword(addr(spi_strct)),8)+' struct size: 0x'+HexStr(sizeof(spi_strct),4));
Log_Writeln(errlvl,' .tx_buf_ptr: 0x'+HexStr(tx_buf_ptr,8));
Log_Writeln(errlvl,' .rx_buf_ptr: 0x'+HexStr(rx_buf_ptr,8));
Log_Writeln(errlvl,' .len: '+Num2Str(len,0));
Log_Writeln(errlvl,' .speed_hz: '+Num2Str(speed_hz,0));
Log_Writeln(errlvl,' .delay_usecs: '+Num2Str(delay_usecs,0));
Log_Writeln(errlvl,' .bits_per_word: '+Num2Str(bits_per_word,0));
Log_Writeln(errlvl,' .cs_change: '+Num2Str(cs_change,0));
end;
end;
procedure SPI_show_bus_info_struct(busnum:byte);
const errlvl=LOG_WARNING;
begin
with spi_bus[busnum] do
begin
Log_Writeln(errlvl,'SPI Bus Info['+Num2Str(busnum,0)+']:');
Log_Writeln(errlvl,' .spi_maxspeed: '+Num2Str(spi_maxspeed,0));
end;
end;
procedure SPI_show_dev_info_struct(busnum,devnum:byte);
const errlvl=LOG_WARNING;
begin
if (busnum<=spi_max_bus) and (devnum<=spi_max_dev) then
begin
with spi_dev[busnum,devnum] do
begin
Log_Writeln(errlvl,'SPI Dev Info['+Num2Str(busnum,0)+'/'+Num2Str(devnum,0)+']:');
Log_Writeln(errlvl,' .spi_path: '+spi_path);
Log_Writeln(errlvl,' .spi_open: '+Bool2Str(spi_fd>=0));
Log_Writeln(errlvl,' .spi_bpw: '+Num2Str(spi_bpw,0));
Log_Writeln(errlvl,' .spi_delay: '+Num2Str(spi_delay,0));
Log_Writeln(errlvl,' .spi_speed: '+Num2Str(spi_speed,0));
Log_Writeln(errlvl,' .spi_cs_change: '+Num2Str(spi_cs_change,0));
Log_Writeln(errlvl,' .spi_LSB_FIRST: '+Num2Str(spi_LSB_FIRST,0));
Log_Writeln(errlvl,' .spi_mode: '+Num2Str(spi_mode,0));
Log_Writeln(errlvl,' .spi_IOC_mode:0x'+HexStr(spi_IOC_mode,8));
// Log_Writeln(errlvl,' .dev_GPIO_int: '+Num2Str(dev_GPIO_int,0));
Log_Writeln(errlvl,' .dev_GPIO_en: '+Num2Str(dev_GPIO_en,0));
Log_Writeln(errlvl,' .dev_GPIO_ook: '+Num2Str(dev_GPIO_ook,0));
end; // with
end else Log_Writeln(Log_ERROR,'SPI_show_dev_info_struct: busnum/devnum out of range');
end;
procedure SPI_show_buffer(busnum,devnum:byte);
const errlvl=LOG_INFO; maxshowbuf=35;
var i,eidx:longint; sh:string;
begin
with spi_buf[busnum,devnum] do
begin
eidx:=endidx; if eidx>maxshowbuf then eidx:=maxshowbuf; // just show the beginning of the buffer
SAY(errlvl,'SPI Buffer['+Num2Str(busnum,0)+'/'+Num2Str(devnum,0)+']:');
SAY(errlvl,' .reg: 0x'+HexStr(reg,4));
if (posidx<=eidx) and (eidx>0) then
begin
sh:=' .buf['+Num2Str(posidx,2)+'..'+Num2Str(eidx,2)+']: 0x';
for i:= posidx to (eidx+1)*2 do sh:=sh+HexStr(ord(buf[i]),2); sh:=sh+' ... ';
for i:= posidx to (eidx+1) do sh:=sh+StringPrintable(buf[i]);
SAY(errlvl,sh);
end
else
begin
SAY(errlvl,' .buf: <empty>');
end;
SAY(errlvl,' .posidx: '+Num2Str(posidx,0));
SAY(errlvl,' .endidx: '+Num2Str(endidx,0));
end;
end;
function _IOC(dir:byte; typ:char; nr,size:word):longword;
{ source http://www.cs.fsu.edu/~baker/devices/lxr/http/source/linux/include/asm-i386/ioctl.h?v=2.6.11.8
http://lkml.indiana.edu/hypermail/linux/kernel/0108.2/0125.html
|dd|ssssssssssssss|tttttttt|nnnnnnnn|
}
begin
_ioc:=(dir shl _IOC_DIRSHIFT) or // dir shl 30
(ord(typ) shl _IOC_TYPESHIFT) or // typ shl 8
(nr shl _IOC_NRSHIFT) or // nr shl 0
(size shl _IOC_SIZESHIFT); // size shl 16
end;
function _IO (typ:char; nr:word):longword; begin _IO :=_IOC(_IOC_NONE, typ,nr,0); end;
function _IOR (typ:char; nr,size:word):longword; begin _IOR :=_IOC(_IOC_Read, typ,nr,size); end;
function _IOW (typ:char; nr,size:word):longword; begin _IOW :=_IOC(_IOC_Write, typ,nr,size); end;
function _IOWR(typ:char; nr,size:word):longword; begin _IOWR:=_IOC((_IOC_Write or _IOC_Read),typ,nr,size); end;
function SPI_GetSpeed(bus:byte):longint;
var _speed_Hz:longint; sh:string;
begin
{$warnings off}
if (bus>=0) and (bus<=1) then
{$warnings on}
begin
_speed_Hz:=RPI_BCM2835_GetNodeValue('/sys/class/spidev/spidev0.'+Num2Str(bus,0)+'/device/of_node/spi-max-frequency',sh);
// writeln('SPI_GetSpeed',bus,' ',_speed_Hz);
if _speed_Hz<=0 then _speed_Hz:=spi_speed_c;
end else _speed_Hz:=spi_speed_c;
SPI_GetSpeed:=_speed_Hz;
end;
function SPI_ClockDivider(spi_hz:real):word;
// Clock Divider // SCLK = Core Clock / CDIV // page 156
var cdiv:word; lw:longword; coreclk:real;
begin
coreclk:=CLK_GetFreq(5);
if (spi_hz<(coreclk/2)) then
begin
cdiv:=0;
if (spi_hz>0) then
begin // CDIV must be a power of two. Odd numbers rounded down.
lw:=RoundUpPow2(DivRoundUp(coreclk,spi_hz));
if (lw<=$ffff) then cdiv:=word(lw) else cdiv:=0 // 0 is the slowest we can go
end;
end else cdiv:=2; // coreclk/2 is the fastest we can go
SPI_ClockDivider:=cdiv;
end;
function SPI_GetFreq(spi_hz:real):longword;
var cdiv:longword;
begin
cdiv:=SPI_ClockDivider(spi_hz);
if cdiv=0 then cdiv:=$10000; // handle slowest
SPI_GetFreq:=round(CLK_GetFreq(5)/cdiv);
end;
function SPI_ClkWrite(spi_hz:real):longword;
//https://github.com/raspberrypi/linux/blob/rpi-4.9.y/drivers/spi/spi-bcm2835.c
var cdiv:word; hz:longword;
begin
cdiv:=SPI_ClockDivider(spi_hz);
hz:=SPI_GetFreq(spi_hz);
SAY(LOG_INFO,'SPI_ClkWrite: '+Num2Str((hz/1000),0,0)+'kHz cdiv:0x'+HexStr(cdiv,4)+' cdivold:0x'+HexStr(BCM_GETREG(SPI0_CLK),8));
BCM_SETREG(SPI0_CLK,cdiv,false,false);
SPI_ClkWrite:=hz;
end;
function SPI_MSGSIZE(n:byte):word;
var siz:word;
begin
if n*SizeOf(spi_ioc_transfer_t)<(1 shl _IOC_SIZEBITS) then
siz:=n*SizeOf(spi_ioc_transfer_t) else siz:=0;
SPI_MSGSIZE:=siz;
end;
function SPI_IOC_MESSAGE(n:byte):longword;
begin SPI_IOC_MESSAGE:=_IOW(SPI_IOC_MAGIC,0,SPI_MSGSIZE(n)); end;
function SPI_Mode(spifd:cint; mode:longword; pvalue:pointer):integer;
var rslt:integer;
begin
rslt:=-1; {$IFDEF UNIX} if spifd>=0 then rslt:=fpioctl(spifd,mode,pvalue); {$ENDIF}
if rslt<0 then Log_Writeln(LOG_ERROR,'SPI_Mode Mode: 0x'+HexStr(mode,8)+' spifd:'+Num2Str(spifd,0)+' err:'+LNX_ErrDesc(fpgeterrno));
SPI_Mode:=rslt;
end;
procedure SPI_EnterCriticalSection(busnum:byte); begin EnterCriticalSection(SPI_bus[busnum].SPI_CS); end;
procedure SPI_LeaveCriticalSection(busnum:byte); begin LeaveCriticalSection(SPI_bus[busnum].SPI_CS); end;
procedure SPI_SetDevErrHndl(busnum,devnum:byte; errhdl:integer);
begin spi_dev[busnum,devnum].errhndl:=errhdl; end;
procedure SPI_Struct_Init(busnum,devnum:byte; var spi_struct:spi_ioc_transfer_t; rx_bufptr,tx_bufptr:pointer; xferlen:longword);
begin
//Log_Writeln(LOG_DEBUG,'SPI_Struct_Init');
if (busnum<=spi_max_bus) and (devnum<=spi_max_dev) then
begin
with spi_dev[busnum,devnum] do
begin
with spi_struct do
begin
{$warnings off}
rx_buf_ptr := qword(rx_bufptr);
tx_buf_ptr := qword(tx_bufptr);
{$warnings on}
delay_usecs := spi_delay;
speed_hz := spi_speed;
bits_per_word := spi_bpw;
cs_change := spi_cs_change;
len := xferlen;
pad := 0;
tx_nbits := 0;
rx_nbits := 0;
if ((spi_mode and SPI_TX_QUAD)<>0)
then tx_nbits:=4
else if ((spi_mode and SPI_TX_DUAL)<>0) then tx_nbits:=2;
if ((spi_mode and SPI_RX_QUAD)<>0)
then rx_nbits:=4
else if ((spi_mode and SPI_RX_DUAL)<>0) then rx_nbits:=2;
if ((spi_mode and SPI_LOOP)<>0) then
begin
if ((spi_mode and SPI_TX_DUAL)<>0) then spi_mode:=spi_mode or SPI_RX_DUAL;
if ((spi_mode and SPI_TX_QUAD)<>0) then spi_mode:=spi_mode or SPI_RX_QUAD;
end;
if len>=SPI_BUF_SIZE_c then len:=SPI_BUF_SIZE_c-1;
end; // with
with spi_buf[busnum,devnum] do
begin
reg:=0; endidx:=0; posidx:=1;
end; // with
end; // with
end else LOG_Writeln(LOG_ERROR,'SPI_Struct_Init: busnum/devnum not in range');
end;
procedure IO_Init_Const;
begin
USBDEVFS_RESET:= _IO ('U', 20);
SPI_IOC_RD_MODE:= _IOR (SPI_IOC_MAGIC, 1, 1);
SPI_IOC_WR_MODE:= _IOW (SPI_IOC_MAGIC, 1, 1);
SPI_IOC_RD_LSB_FIRST:= _IOR (SPI_IOC_MAGIC, 2, 1);
SPI_IOC_WR_LSB_FIRST:= _IOW (SPI_IOC_MAGIC, 2, 1);
SPI_IOC_RD_BITS_PER_WORD:= _IOR (SPI_IOC_MAGIC, 3, 1);
SPI_IOC_WR_BITS_PER_WORD:= _IOW (SPI_IOC_MAGIC, 3, 1);
SPI_IOC_RD_MAX_SPEED_HZ:= _IOR (SPI_IOC_MAGIC, 4, 4); // SizeOf(longint) ??
SPI_IOC_WR_MAX_SPEED_HZ:= _IOW (SPI_IOC_MAGIC, 4, 4); // SizeOf(longint) ??
IOCTL_TAG_PROPERTY:= _IOWR('d', 0, 4); // SizeOf(longint) ??
WDIOC_SETTIMEOUT:= _IOWR('W', 6, SizeOf(longint));
WDIOC_GETTIMEOUT:= _IOR ('W', 7, SizeOf(longint));
WDIOC_KEEPALIVE:= _IOR ('W', 5, SizeOf(longint));
WDIOC_GETBOOTSTATUS:= _IOR ('W', 2, SizeOf(longint));
WDIOC_GETSTATUS:= _IOR ('W', 1, SizeOf(longint));
WDIOC_GETSUPPORT:= _IOR ('W', 0, SizeOf(watchdog_info_t));
end;
function SPI_Transfer(busnum,devnum:byte; cmdseq:string):integer;
// http://www.netzmafia.de/skripten/hardware/RasPi/RasPi_SPI.html
const numxfer=1;
var rslt,xlen:integer; xfer:array[0..(numxfer-1)] of spi_ioc_transfer_t;
begin
rslt:=-1; xlen:=Length(cmdseq);
try
if (busnum<=spi_max_bus) and (devnum<=spi_max_dev) then
begin
if xlen>0 then
begin
if xlen>=SPI_BUF_SIZE_c then
begin
xlen:=SPI_BUF_SIZE_c;
LOG_WRITELN(LOG_ERROR,'spi_transfer: transfer length to long');
end;
with spi_buf[busnum,devnum] do
begin
SPI_Struct_Init(busnum,devnum,xfer[0],addr(buf[1]),addr(buf[1]),xlen);
buf:=copy(cmdseq,1,xlen); endidx:=xlen;
// SPI_show_buffer(busnum,devnum);
// SPI_Show_Struct(xfer[0]);
{$IFDEF UNIX}
rslt:=fpioctl(spi_dev[busnum,devnum].spi_fd,SPI_IOC_MESSAGE(numxfer),addr(xfer[0]));
{$ENDIF}
if rslt<0 then
begin
buf:='';
Log_Writeln(LOG_ERROR,
'SPI_transfer['+Num2Str(busnum,0)+'/'+Num2Str(devnum,0)+'/fd:'+Num2Str(spi_dev[busnum,devnum].spi_fd,0)+']: '+
'cmdseq: 0x'+HexStr(cmdseq)+' '+
LNX_ErrDesc(fpgeterrno));
ERR_MGMT_UPD(spi_dev[busnum,devnum].errhndl,_IOC_WRITE,xlen,false);
end
else
begin
posidx:=1; endidx:=rslt; SetLength(buf,rslt);
ERR_MGMT_UPD(spi_dev[busnum,devnum].errhndl,_IOC_READ, rslt,true);
ERR_MGMT_UPD(spi_dev[busnum,devnum].errhndl,_IOC_WRITE,rslt,true);
end;
end; // with
end;
end else LOG_Writeln(LOG_ERROR,'SPI_Transfer[0x'+HexStr(busnum,2)+'/0x'+HexStr(devnum,2)+']: invalid busnum/devnum');
except
On E_rpi_hal_Exception :Exception do writeln('SPI_Transfer: ',E_rpi_hal_Exception.Message);
end;
SPI_Transfer:=rslt;
end;
function SPI_Write(busnum,devnum:byte; basereg,data:word):integer;
var rslt:integer; xfer:spi_ioc_transfer_t; buf:array[0..1] of byte;
begin
rslt:=-1;
Log_Writeln(LOG_WARNING,'SPI_write Reg: 0x'+HexStr(basereg,4)+' Data: 0x'+HexStr(data,4));
if (busnum<=spi_max_bus) and (devnum<=spi_max_dev) then
begin
SPI_Struct_Init(busnum,devnum,xfer,addr(buf),addr(buf),2);
buf[1]:=byte(data); buf[0]:=byte(basereg);
{$IFDEF UNIX}
rslt:=fpioctl(spi_dev[busnum,devnum].spi_fd,SPI_IOC_MESSAGE(1),addr(xfer));
{$ENDIF}
if rslt<0 then
begin
ERR_MGMT_UPD(spi_dev[busnum,devnum].errhndl,_IOC_WRITE,2,false);
Log_Writeln(LOG_ERROR,'SPI_write '+Num2Str(rslt,0)+
' devnum: ' +Num2Str(devnum,0)+
' spi_busnum: '+Num2Str(busnum,0)+' '+
LNX_ErrDesc(fpgeterrno));
end
else
begin
writeln('SPI_WRITE: result',rslt);
ERR_MGMT_UPD(spi_dev[busnum,devnum].errhndl,_IOC_WRITE,2,true);
end;
end else LOG_Writeln(LOG_ERROR,'SPI_Write[0x'+HexStr(busnum,2)+'/0x'+HexStr(devnum,2)+']: invalid busnum/devnum');
SPI_Write:=rslt;
end;
function SPI_Read(busnum,devnum:byte; basereg:word):byte;
var b:byte; rslt:integer; xfer:array[0..1] of spi_ioc_transfer_t; xbuf:SPI_databuf_t;
begin
rslt:=-1; b:=$ff;
if (busnum<=spi_max_bus) and (devnum<=spi_max_dev) then
begin
SPI_Struct_Init(busnum,devnum,xfer[0],addr(xbuf.buf[1]),addr(xbuf.buf[1]),1);
SPI_Struct_Init(busnum,devnum,xfer[1],addr(xbuf.buf[1]),addr(xbuf.buf[1]),1);
for b:=1 to SPI_BUF_SIZE_c do xbuf.buf[b]:=#$00;
xbuf.buf[1]:=char(byte(basereg));
{$IFDEF UNIX}
rslt:=fpioctl(spi_dev[busnum,devnum].spi_fd,SPI_IOC_MESSAGE(2),addr(xfer));
{$ENDIF}
if rslt<0 then
begin
b:=$ff;
// Log_Writeln(LOG_ERROR,'SPI_read Reg: 0x'+HexStr(reg,4)+' err: '+LNX_ErrDesc(fpgeterrno));
ERR_MGMT_UPD(spi_dev[busnum,devnum].errhndl,_IOC_READ,1,false);
end
else
begin
SetLength(xbuf.buf,rslt);
b:=byte(xbuf.buf[1]);
// Log_Writeln(LOG_ERROR,'SPI_read Reg: 0x'+HexStr(basereg,4)+' Data: 0x'+HexStr(xbuf.buf)+' rslt:'+Num2Str(rslt,0));
ERR_MGMT_UPD(spi_dev[busnum,devnum].errhndl,_IOC_READ,1,true);
end;
end else LOG_Writeln(LOG_ERROR,'SPI_Read[0x'+HexStr(busnum,2)+'/0x'+HexStr(devnum,2)+']: invalid busnum/devnum');
SPI_Read:=b;
end;
function SPI_BurstRead(busnum,devnum:byte):byte;
{ get byte from Buffer. Buffer was filled before with procedure SPI_BurstRead2Buffer }
var b:byte;
begin
b:=$ff;
if (busnum<=spi_max_bus) and (devnum<=spi_max_dev) then
begin
if spi_buf[busnum,devnum].posidx<=spi_buf[busnum,devnum].endidx then
begin
b:=ord(spi_buf[busnum,devnum].buf[spi_buf[busnum,devnum].posidx]);
end;
inc(spi_buf[busnum,devnum].posidx);
end else LOG_Writeln(LOG_ERROR,'SPI_BurstRead[0x'+HexStr(busnum,2)+'/0x'+HexStr(devnum,2)+']: invalid busnum/devnum');
SPI_BurstRead:=b;
end;
procedure SPI_BurstRead2Buffer(busnum,devnum,basereg:byte; len:longword);
{ full duplex, see example spidev_fdx.c}
var rslt:integer; xfer : array[0..1] of spi_ioc_transfer_t;
begin
// Log_Writeln(LOG_DEBUG,'SPI_BurstRead2Buffer devnum:0x'+HexStr(devnum,4)+' reg:0x'+HexStr(start_reg,4)+' len:0x'+HexStr(len,8));
rslt:=-1;
if (busnum<=spi_max_bus) and (devnum<=spi_max_dev) then
begin
if spi_buf[busnum,devnum].posidx>spi_buf[busnum,devnum].endidx then
begin
SPI_Struct_Init(busnum,devnum,xfer[0],addr(spi_buf[busnum,devnum].buf),addr(spi_buf[busnum,devnum].buf),1);
SPI_Struct_Init(busnum,devnum,xfer[1],addr(spi_buf[busnum,devnum].buf),addr(spi_buf[busnum,devnum].buf),len);
spi_buf[busnum,devnum].buf[1]:=char(byte(basereg));
spi_buf[busnum,devnum].reg:=basereg;
// SPI_SetMode(busnum,devnum);
(* if LOG_GetLogLevel<=LOG_DEBUG then show_spi_struct(xfer[0]);
if LOG_GetLogLevel<=LOG_DEBUG then show_spi_struct(xfer[1]);
if LOG_GetLogLevel<=LOG_DEBUG then show_spi_dev_info_struct(busnum,devnum); *)
// Log_Writeln(LOG_DEBUG,'fpioctl('+Num2Str(spi_bus[busnum].spi_fd,0)+', 0x'+HexStr(SPI_IOC_MESSAGE(2),8)+', 0x'+HexStr(longword(addr(xfer)),8)+')');
{$IFDEF UNIX}
rslt:=fpioctl(spi_dev[busnum,devnum].spi_fd,SPI_IOC_MESSAGE(2),addr(xfer)); { full duplex }
{$ENDIF}
if rslt<0 then
begin
// Log_Writeln(LOG_ERROR,'SPI_BurstRead2Buffer fpioctl err: '+LNX_ErrDesc(fpgeterrno));
spi_buf[busnum,devnum].endidx:=0;
spi_buf[busnum,devnum].posidx:=1;
end
else
begin
spi_buf[busnum,devnum].endidx:=rslt;
spi_buf[busnum,devnum].posidx:=1;
end;
// if LOG_Get_Level<=LOG_DEBUG then show_spi_buffer(spi_buf[devnum]);
(* if LOG_GetLogLevel<=LOG_DEBUG then show_spi_struct(rfm22_stat[devnum]); *)
end;
end else LOG_Writeln(LOG_ERROR,'SPI_BurstRead2Buffer[0x'+HexStr(busnum,2)+'/0x'+HexStr(devnum,2)+']: invalid busnum/devnum');
// Log_Writeln(LOG_DEBUG,'SPI_BurstRead2Buffer (end)');
end;
procedure SPI_BurstWriteBuffer(busnum,devnum,basereg:byte; len:longword);
// Write 'len' Bytes from Buffer SPI Dev startig at address 'reg'
var rslt:integer; xfer : spi_ioc_transfer_t;
begin
// Log_Writeln(LOG_DEBUG,'SPI_BurstWriteBuffer devnum:0x'+HexStr(devnum,4)+' reg:0x'+HexStr(start_reg,4)+' xferlen:0x'+HexStr(xferlen,8));
rslt:=-1;
if (busnum<=spi_max_bus) and (devnum<=spi_max_dev) then
begin
if len>0 then
begin
SPI_Struct_Init(busnum,devnum,xfer,addr(spi_buf[busnum,devnum].buf),addr(spi_buf[busnum,devnum].reg),len+1); //+1 Byte, because send reg-content also. transfer starts at addr(spi_buf[devnum].reg)
spi_buf[busnum,devnum].reg:=basereg;
// SPI_SetMode(busnum,devnum);
// if LOG_Get_Level<=LOG_DEBUG then show_spi_struct(xfer);
// if LOG_Get_Level<=LOG_DEBUG then show_spi_dev_info_struct(busnum,devnum);
// if LOG_Get_Level<=LOG_DEBUG then show_spi_buffer(busnum,devnum);
// if LOG_Get_Level<=LOG_DEBUG then Log_Writeln(LOG_DEBUG,'fpioctl('+Num2Str(spi_bus[busnum].spi_fd,0)+', 0x'+HexStr(SPI_IOC_MESSAGE(1),8)+', 0x'+HexStr(longword(addr(xfer)),8)+')');
{$IFDEF UNIX}
rslt:=fpioctl(spi_dev[busnum,devnum].spi_fd,SPI_IOC_MESSAGE(1),addr(xfer));
{$ENDIF}
if rslt<0 then Log_Writeln(LOG_ERROR,'SPI_BurstWriteBuffer fpioctl err: '+LNX_ErrDesc(fpgeterrno))
else inc(spi_buf[busnum,devnum].posidx,rslt-1); //rslt-1 wg. reg + buffer content
// if LOG_Get_Level<=LOG_DEBUG then show_spi_buffer(busnum,devnum);
end;
end else LOG_Writeln(LOG_ERROR,'SPI_BurstWriteBuffer[0x'+HexStr(busnum,2)+'/0x'+HexStr(devnum,2)+']: invalid busnum/devnum');
end;
procedure SPI_StartBurst(busnum,devnum:byte; reg:word; writeing:byte; len:longint);
begin
//Log_Writeln(LOG_DEBUG,'StartBurst StartReg: 0x'+HexStr(reg,4)+' writing: '+Bool2Str(writeing<>0));
if (busnum<=spi_max_bus) and (devnum<=spi_max_dev) then
begin
if (spi_dev[busnum,devnum].spi_fd>=0) then
begin
// SPI_SetMode(busnum,devnum);
spi_buf[busnum,devnum].reg:=byte(reg);
if writeing=1 then
begin
spi_buf[busnum,devnum].endidx:=len; spi_buf[busnum,devnum].posidx:=1;
SPI_BurstWriteBuffer(busnum,devnum,reg,len); { Write 'len' Bytes from Buffer to SPI Dev startig at address 'reg' }
if ((reg and $7f)=$7f) then SPI_write(busnum,devnum,$3e,word(len)); { set packet length for TX FIFO }
end
else
begin
spi_buf[busnum,devnum].endidx:=0;
spi_buf[busnum,devnum].posidx:=1; { initiate BurstRead2Buffer }
SPI_BurstRead2Buffer(busnum,devnum,reg,len); { Read 'len' Bytes from SPI Dev to Buffer }
//inc(spi_buf[busnum,devnum].posidx); //1. Byte in Read Buffer is startregister -> position to 1. register content
end;
end;
end else LOG_Writeln(LOG_ERROR,'SPI_StartBurst[0x'+HexStr(busnum,2)+'/0x'+HexStr(devnum,2)+']: invalid busnum/devnum');
end;
procedure SPI_EndBurst(busnum,devnum:byte);
begin
//Log_Writeln(LOG_DEBUG,'SPI_EndBurst');
if (busnum<=spi_max_bus) and (devnum<=spi_max_dev) then
begin
spi_buf[busnum,devnum].endidx:=0;
spi_buf[busnum,devnum].posidx:=1; // initiate BurstRead2Buffer
end else LOG_Writeln(LOG_ERROR,'SPI_EndBurst[0x'+HexStr(busnum,2)+'/0x'+HexStr(devnum,2)+']: invalid busnum/devnum');
end;
function xxSPI_Dev_Init(busnum,devnum:byte):boolean;
var ok:boolean;
begin
ok:=false;
if (busnum<=spi_max_bus) and (devnum<=spi_max_dev) then
begin
with spi_dev[busnum,devnum] do
begin
errhndl := NO_ERRHNDL;
isr_enable := false;
isr.gpio := -1;
spi_LSB_FIRST := 0;
spi_bpw := 8;
spi_delay := 0;
spi_cs_change := 0; // do not change CS during multiple byte transfers
spi_speed := SPI_GetSpeed(busnum);
spi_mode := SPI_MODE_0;
spi_IOC_mode := SPI_IOC_RD_MODE;
spi_fd := -1;
spi_path :=spi_path_c+Num2Str(busnum,0)+'.'+Num2Str(devnum,0);
//writeln('SPI_Dev_Init: ',spi_path);
if (spi_path<>'') and FileExists(spi_path) then
begin
{$IFDEF UNIX} spi_fd:=fpOpen(spi_path,O_RdWr); {$ENDIF}
end;
if (spi_fd<0) then
begin
Log_Writeln(LOG_ERROR,'SPI_Dev_Init[0x'+HexStr(busnum,2)+'/'+HexStr(devnum,2)+']: '+spi_path);
if LOG_Level<=LOG_DEBUG then SPI_show_dev_info_struct(busnum,devnum);
end
else ok:=true;
end; // with
end else LOG_Writeln(LOG_ERROR,'SPI_Dev_Init[0x'+HexStr(busnum,2)+'/0x'+HexStr(devnum,2)+']: invalid busnum/devnum');
//SPI_show_dev_info_struct(spi_dev[devnum], devnum);
xxSPI_Dev_Init:=ok;
end;
function SPI_Dev_Init(busnum,devnum,bpw,cs_change:byte; mode,maxspeed_hz:longword; delay_usec:word):boolean;
var ok:boolean; res:integer;
begin
ok:=false;
if (busnum<=spi_max_bus) and (devnum<=spi_max_dev) then
begin
with spi_dev[busnum,devnum] do
begin
errhndl := NO_ERRHNDL;
isr_enable := false;
isr.gpio := -1;
spi_bpw := bpw;
spi_delay := delay_usec;
if cs_change<>0 then spi_cs_change:=1 else spi_cs_change:=0;
spi_speed := maxspeed_hz;
spi_mode := mode;
spi_IOC_mode := SPI_IOC_RD_MODE;
spi_path :=spi_path_c+Num2Str(busnum,0)+'.'+Num2Str(devnum,0);
//writeln('SPI_Dev_Init: ',spi_path,' speed:',(spi_speed div 1000),'kHz');
if (spi_path<>'') and FileExists(spi_path) then
begin
{$IFDEF UNIX}
if spi_fd<0 then spi_fd:=fpOpen(spi_path,O_RdWr);
res:=fpioctl(spi_fd,SPI_IOC_WR_MODE,@spi_mode);
if (res=-1) then Log_Writeln(Log_ERROR,'SPI_Dev_Init: can''t set SPI mode 0x'+HexStr(spi_mode,8)+' '+LNX_ErrDesc(fpgeterrno));
res:=fpioctl(spi_fd,SPI_IOC_WR_BITS_PER_WORD,@spi_bpw);
if (res=-1) then Log_Writeln(Log_ERROR,'SPI_Dev_Init: can''t set bits per word '+Num2Str(spi_bpw,0)+' '+LNX_ErrDesc(fpgeterrno));
res:=fpioctl(spi_fd,SPI_IOC_WR_MAX_SPEED_HZ,@spi_speed);
if (res=-1) then Log_Writeln(Log_ERROR,'SPI_Dev_Init: can''t set max speed '+Num2Str(spi_speed,0)+'hz '+LNX_ErrDesc(fpgeterrno));
{$RANGECHECKS OFF}
res:=fpioctl(spi_fd,SPI_IOC_RD_MODE,@spi_mode);
if (res=-1) then Log_Writeln(Log_ERROR,'SPI_Dev_Init: can''t get SPI mode '+LNX_ErrDesc(fpgeterrno));
res:=fpioctl(spi_fd,SPI_IOC_RD_MAX_SPEED_HZ,@spi_speed);
//writeln('SPI-MaxSpeed: ',spi_speed);
if (res=-1) then Log_Writeln(Log_ERROR,'SPI_Dev_Init: can''t get max speed '+LNX_ErrDesc(fpgeterrno));
res:=fpioctl(spi_fd,SPI_IOC_RD_BITS_PER_WORD,@spi_bpw);
if (res=-1) then Log_Writeln(Log_ERROR,'SPI_Dev_Init: can''t get bits per word '+LNX_ErrDesc(fpgeterrno));
{$RANGECHECKS ON}
{$ENDIF}
end;
if (spi_fd<0) then
begin
Log_Writeln(LOG_WARNING,'SPI_Dev_Init[0x'+HexStr(busnum,2)+'/'+HexStr(devnum,2)+']: '+spi_path);
if LOG_Level<=LOG_DEBUG then SPI_show_dev_info_struct(busnum,devnum);
end
else ok:=true;
end; // with
end else LOG_Writeln(LOG_ERROR,'SPI_Dev_Init[0x'+HexStr(busnum,2)+'/0x'+HexStr(devnum,2)+']: invalid busnum/devnum');
//SPI_show_dev_info_struct(spi_dev[devnum], devnum);
SPI_Dev_Init:=ok;
end;
function SPI_Dev_Init(busnum,devnum:byte):boolean;
begin SPI_Dev_Init:=SPI_Dev_Init(busnum,devnum,8,0,SPI_MODE_0,spi_bus[busnum].spi_maxspeed,0); end;
procedure SPI_AdrMuxInit(CSnum,adr0gpio,adr1gpio:longint);
// SPI and chip select pins
// https://www.raspberrypi.org/forums/viewtopic.php?f=44&t=30765
var i:integer;
begin
if (CSnum<=spi_max_dev) then
begin
with SPI_AddrMux[CSnum] do
begin
AdrCSgpio[0]:=adr0gpio; AdrCSgpio[1]:=adr1gpio;
AdrMuxEnable:=((GPIO_MAP_GPIO_NUM_2_HDR_PIN(AdrCSgpio[0])>0) and
(GPIO_MAP_GPIO_NUM_2_HDR_PIN(AdrCSgpio[1])>0));
if AdrMuxEnable then
begin // using valid HWpins only
for i:= 0 to 1 do
begin
LOG_Writeln(LOG_WARNING,'SPI_AdrMuxInit['+Num2Str(i,0)+']: using GPIO#'+Num2Str(AdrCSgpio[i],0));
GPIO_set_OUTPUT(AdrCSgpio[i]);
GPIO_set_pin (AdrCSgpio[i], true); // active low -> deselect
end; // for
end;
end; // with
end else LOG_Writeln(LOG_ERROR,'SPI_AdrMuxInit: CS'+Num2Str(CSnum,0)+' not valid');
end;
procedure SPI_AdrMux(CSnum,adr:byte);
// for using e.g. 74HC139 Dual 2-to-4 line decoder/demultiplexer
// connect CS0 to 1E (Pin 1); GPIO<0> to 1A0 (Pin 2); GPIO<1> to 1A1 (Pin 3)
// connect CS1 to 2E (Pin15); GPIO<2> to 2A0 (Pin14); GPIO<3> to 2A1 (Pin13)
// using only 2 GPIOs: short 1A0 and 2A0; 1A1 and 2A1
begin
if (CSnum<=spi_max_dev) then
begin
with SPI_AddrMux[CSnum] do
begin
if AdrMuxEnable then
begin
case adr of // using negative logic (active low)
0: begin GPIO_set_pin(AdrCSgpio[1],true); GPIO_set_pin(AdrCSgpio[0],true); end;
1: begin GPIO_set_pin(AdrCSgpio[1],true); GPIO_set_pin(AdrCSgpio[0],false); end;
2: begin GPIO_set_pin(AdrCSgpio[1],false); GPIO_set_pin(AdrCSgpio[0],true); end;
3: begin GPIO_set_pin(AdrCSgpio[1],false); GPIO_set_pin(AdrCSgpio[0],false); end;
else LOG_Writeln(LOG_ERROR,'SPI_AdrMux[CS'+Num2Str(CSnum,0)+'/'+Num2Str(adr,0)+']: not valid');
end; // case
end else LOG_Writeln(LOG_ERROR,'SPI_AdrMux['+Num2Str(CSnum,0)+'/'+Num2Str(adr,0)+']: not enabled, GPIOs not defined');
end; // with
end else LOG_Writeln(LOG_ERROR,'SPI_AdrMux: CS'+Num2Str(CSnum,0)+' not valid');
end;
procedure SPI_AdrMux(adr:byte);
// select adr (0..7) before calling e.g. SPI_Transfer ...
begin
case adr of
0..3: SPI_AdrMux(0,(adr and $03)); // CS0
4..7: SPI_AdrMux(1,(adr and $03)); // CS1
end; // case
end;
procedure SPI_Start(busnum:byte);
var devnum:byte;
begin
Log_Writeln(LOG_DEBUG,'SPI_Start busnum: '+Num2Str(busnum,0));
if (busnum<=spi_max_bus) then
begin
with spi_bus[busnum] do
begin
spi_maxspeed:=SPI_GetSpeed(busnum);
SPI_useCS:=false;
InitCriticalSection(SPI_CS);
for devnum:=0 to spi_max_dev do
SPI_Dev_Init(busnum,devnum);
end;
end else LOG_Writeln(LOG_ERROR,'SPI_Start[0x'+HexStr(busnum,2)+']: invalid busnum');
end;
procedure SPI_Start;
var i:integer;
begin
for i:=0 to spi_max_dev do SPI_AdrMuxInit(i,-1,-1);
for i:=0 to spi_max_bus do SPI_Start(i);
end;
procedure SPI_Bus_Close(busnum:byte);
var devnum:byte;
begin
if (busnum<=spi_max_bus) then
begin
for devnum:=0 to spi_max_dev do
begin
with spi_dev[busnum,devnum] do
begin
{$IFDEF UNIX}
if (spi_fd>=0) then
begin
DoneCriticalSection(SPI_bus[busnum].SPI_CS);
fpclose(spi_fd);
end;
{$ENDIF}
spi_fd:=-1;
end;
end; // for
end else LOG_Writeln(LOG_ERROR,'SPI_Bus_Close[0x'+HexStr(busnum,2)+']: invalid busnum');
end;
procedure SPI_Bus_Close_All;
var i:integer;
begin
for i:=0 to spi_max_dev do SPI_AdrMuxInit(i,-1,-1);
for i:=0 to spi_max_bus do SPI_Bus_Close(i);
end;
procedure SPI_Loop_Test;
const busnum=0; devnum=0; // test on /dev/spidev0.0 // spidev<busnum.devnum>
requestedspeed=1000000; // MaxBusSpeed ~7.8MHz
seq= 'HELLO';
// seq= 'HELLO - this is a SPI-Loop-Test'; // 31 Bytes
var rslt,cnt:integer; tv_start,tv_end:timespec; us:int64;
begin
writeln('SPI_Loop_Test+: Start');
writeln(' pls. connect/short MOSI and MISO line (GPIO10/GPIO9).');
writeln(' If you remove the wire between MOSI and MISO, and connect the MISO');
writeln(' "H"-Level (+3.3 V), you should be able to read 0xFFs.');
writeln(' If you connect MISO to ground (GND), you should receive 0x00s for each byte instead.');
writeln(' we will send 8x byte sequence 0x'+HexStr(seq));
writeln(' with a length of '+Num2Str(Length(seq),0)+' bytes and should also receive it. <CR>');
readln;
cnt:=0;
SPI_Dev_Init(busnum,devnum,8,0,SPI_MODE_0,requestedspeed,10);
repeat
clock_gettime(CLOCK_REALTIME,@tv_start);
rslt:=SPI_Transfer(busnum,devnum, seq(*+seq+seq+seq+seq+seq+seq+seq*));
clock_gettime(CLOCK_REALTIME,@tv_end);
if rslt>=0 then
begin
us:=MicroSecondsBetween(tv_end,tv_start);
writeln('SPI_Loop_Test: success, NumBytes:',rslt:0,' within ',us:0,'us (',(rslt/us*1000):0:1,'kB/s MaxBusSpeed:',(SPI_GetFreq(requestedspeed)/1000):0:1,'kHz)');
SPI_Show_Buffer(busnum,devnum);
writeln('responsestr: 0x',HexStr(spi_buf[busnum,devnum].buf));
end else LOG_Writeln(LOG_ERROR,'SPI_Loop_Test: errnum: '+Num2Str(rslt,0));
delay_msec(1000);
inc(cnt);
until (cnt>=1);
writeln('SPI_Loop_Test-: End');
end;
procedure rfm22B_ShowChipType;
(* just to test SPI Read Function. Installed RFM22B Module on piggy back board is required!! *)
const RF22_REG_01_VERSION_CODE = $01; busnum=0; devnum=0;
function GDVC(b:byte):string;
var t:string;
begin
case (b and $1f) of
$01 : t:='SIxxx_X4';
$02 : t:='SI4432_V2';
$03 : t:='SIxxx_A0';
$04 : t:='SI4431_A0';
$05 : t:='SI443x_B0';
$06 : t:='SI443x_B1';
else t:='RFM_UNKNOWN';
end;
GDVC:='0x'+HexStr(b,2)+' '+t;
end;
begin
writeln('Chip-Type: '+
GDVC(SPI_Read(busnum,devnum,RF22_REG_01_VERSION_CODE))+
' (correct answer should be 0x06)');
end;
procedure SPI_Test; begin rfm22B_ShowChipType; end;
function RPI_OSrev:string; begin RPI_OSrev:=os_rev; end;
function RPI_uname:string; begin RPI_uname:=uname; end;
function RPI_hw :string; begin RPI_hw :=cpu_hw; end;
function RPI_fw :string; begin RPI_fw :=cpu_fw; end;
function RPI_proc:string; begin RPI_proc:=cpu_proc; end;
function RPI_mips:string; begin RPI_mips:=cpu_mips; end;
function RPI_feat:string; begin RPI_feat:=cpu_feat; end;
function RPI_rev :string; begin RPI_rev :=cpu_rev; end;
function RPI_machine:string; begin RPI_machine:=cpu_machine; end;
function RPI_cores:longint; begin RPI_cores:=cpu_cores; end;
function RPI_revnum:real; begin RPI_revnum:=cpu_rev_num; end;
function RPI_gpiomapidx:byte; begin RPI_gpiomapidx:=GPIO_map_idx; end;
function RPI_hdrpincount:byte; begin RPI_hdrpincount:=connector_pin_count; end;
function RPI_freq :string; begin RPI_freq :=cpu_fmin+';'+cpu_fcur+';'+cpu_fmax+';Hz'; end;
function RPI_status_led_GPIO:byte; begin RPI_status_led_GPIO:=status_led_GPIO; end;
function RPI_snr :string; begin RPI_snr :=cpu_snr; end;
function RPI_whoami:string; begin RPI_whoami:=whoami; end;
function RPI_I2C_BRadj(i2c_speed_kHz:longint):longint;
// https://periph.io/platform/raspberrypi/
// http://forum.weihenstephan.org/forum/phpBB3/viewtopic.php?t=684
var br:longint; //vs:string;
begin // RPI_rev e.g: rev4;1024MB;3B;BCM2835;a02082;40
// vs:=Upper(Select_Item(RPI_rev,';','',3)); // e.g. 3B
br:=i2c_speed_kHz;
// if (Pos('3B',vs)<>0) then br:=round(i2c_speed_kHz*1.6); // RPI3
// if (Pos('2B',vs)<>0) then br:=round(i2c_speed_kHz*2.0); // RPI2
RPI_I2C_BRadj:=br;
end;
function RPI_I2C_busnum(func:byte):byte;
//get the I2C busnumber, where e.g. the general purpose devices are connected.
//This depends on rev1 or rev2 board . e.g. RPI_I2C_busnum(RPI_I2C_general_purpose_bus_c) }
var b:byte;
begin
b:=I2C_busnum; if func<>RPI_I2C_general_purpose_bus_c then inc(b);
RPI_I2C_busnum:=(b mod 2);
end;
function RPI_I2C_busgen:byte; begin RPI_I2C_busgen:=RPI_I2C_busnum(RPI_I2C_general_purpose_bus_c); end;
function RPI_I2C_bus2nd:byte; begin RPI_I2C_bus2nd:=RPI_I2C_busnum(RPI_I2C_general_purpose_bus_c+1); end;
procedure RPI_show_cpu_info;
begin
writeln('rpi Snr : ',RPI_snr);
writeln('rpi HW : ',RPI_hw);
writeln('rpi proc : ',RPI_proc);
writeln('rpi rev : ',RPI_rev);
writeln('rpi mips : ',RPI_mips);
writeln('rpi Freq : ',RPI_freq);
writeln('rpi Osci : ',(CLK_GetFreq(1)/1000000):7:2,' MHz');
writeln('rpi PLLC : ',(CLK_GetFreq(5)/1000000):7:2,' MHz (CoreFreq)');
writeln('rpi PLLD : ',(CLK_GetFreq(6)/1000000):7:2,' MHz');
writeln('rpi HDMI : ',(CLK_GetFreq(7)/1000000):7:2,' MHz');
writeln('CLK min : ',(CLK_GetMinFreq/1000): 7:2,' kHz');
writeln('CLK max : ',(CLK_GetMaxFreq/1000000):7:2,' MHz');
writeln('PWMHW min: ',(PWM_GetMinFreq(PWM_DIVImax)/1.0):7:2,' Hz');
writeln('PWMHW max: ',(PWM_GetMaxFreq(PWM_DIVImin)/1000):7:2,' kHz');
end;
procedure RPI_show_SBC_info; begin RPI_show_cpu_info; end;
procedure RPI_show_all_info;
begin
RPI_show_SBC_info; writeln;
GPIO_show_regs; writeln;
if (not restrict2gpio) then
begin
spi0_show_regs; writeln;
pwm_show_regs; writeln;
clk_show_regs; writeln;
stim_show_regs; writeln;
tim_show_regs; writeln;
q4_show_regs; writeln;
i2c0_show_regs; writeln;
i2c1_show_regs; writeln;
// i2c2_show_regs; writeln;
Clock_show_regs; writeln;
GPIO_ShowConnector;
end else Log_Writeln(Log_WARNING,'RPI_show_all_info: can report GPIO register only');
end;
procedure GPIO_create_int_script(filn:string);
const logfil_c='/tmp/GPIO_script.log';
var fil:text; sh:string;
begin
{$I-}
assign (fil,filn); rewrite(fil);
writeln(fil,'#!/bin/bash');
writeln(fil,'# script was automatically created. Do not edit');
writeln(fil,'# usage e.g.:');
writeln(fil,'# usage e.g.: '+filn+' 22 in rising');
writeln(fil,'# usage e.g.: '+filn+' 22 stop');
writeln(fil,'#');
writeln(fil,'logf='+logfil_c);
writeln(fil,'path='+GPIO_path_c);
writeln(fil,'gpionum=$1');
writeln(fil,'direction=$2');
writeln(fil,'edgetype=$3');
writeln(fil,'if ([ "$gpionum" == "" ] || [ "$direction" == "" ]) ||');
writeln(fil,' ([ "$direction" != "in" ] && [ "$direction" != "out" ] && [ "$direction" != "stop" ]) || ');
writeln(fil,' ([ "$edgetype" != "rising" ] && [ "$edgetype" != "falling" ] && [ "$direction" != "stop" ]) ; then');
writeln(fil,' echo "no valid parameter $1 $2 $3"');
writeln(fil,' echo "$0 <gpionum> <[in|out|stop]> <[rising|falling]>"');
writeln(fil,' exit 1;');
writeln(fil,'fi');
writeln(fil,'#');
writeln(fil,'echo $0 $1 $2 $3 $4 $5 $6 $7 $8 $9 > $logf');
writeln(fil,'echo $gpionum > $path/unexport');
writeln(fil,'if ([ "$direction" == "in" ] || [ "$direction" == "out" ]); then');
writeln(fil,' echo create gpio$gpionum $direction $edgetype >> $logf');
writeln(fil,' echo $gpionum > $path/export');
writeln(fil,' echo $direction > $path/gpio$gpionum/direction');
writeln(fil,' echo $edgetype > $path/gpio$gpionum/edge');
writeln(fil,'#');
writeln(fil,' echo $path/gpio$gpionum/ >> $logf');
writeln(fil,' ls -l $path/gpio$gpionum/ >> $logf');
writeln(fil,'fi');
writeln(fil,'#');
writeln(fil,'exit 0');
close(fil);
{$I+}
call_external_prog(LOG_NONE,'chmod +x '+filn,sh);
end;
{$IFDEF UNIX}
function RPI_hal_Dummy_INT(GPIO_nr:integer):integer;
// if isr routine is not initialized
begin
writeln ('RPI_hal_Dummy_INT fired for GPIO',GPIO_nr);
RPI_hal_Dummy_INT:=-1;
end;
function my_isr(GPIO_nr:integer):integer;
// for GPIO_int testing. will be called on interrupt
const waittim_ms=1;
begin
writeln ('my_isr fired for GPIO',GPIO_nr,' servicetime: ',waittim_ms:0,'ms');
sleep(waittim_ms);
my_isr:=999;
end;
//* Bits from:
//https://www.ridgerun.com/developer/wiki/index.php/Gpio-int-test.c */
//static void *
// https://github.com/omerk/pihwm/blob/master/lib/pi_gpio.c
// https://github.com/omerk/pihwm/blob/master/demo/GPIO_int.c
// https://github.com/omerk/pihwm/blob/master/lib/pihwm.c
function isr_handler(p:pointer):longint; // (void *isr)
const testrun_c=true;
STDIN_FILENO = 0; STDOUT_FILENO = 1; STDERR_FILENO = 2;
POLLIN = $0001; POLLPRI = $0002;
var rslt:integer; nfds,rc:longint;
buf:array[0..63] of byte; fdset:array[0..1] of pollfd;
testrun:boolean; isr_ptr:^isr_t; Call_Func:TFunctionOneArgCall;
begin
rslt:=0; nfds:=2; testrun:=testrun_c; isr_ptr:=p; Call_Func:=isr_ptr^.func_ptr;
if testrun then writeln('## ',isr_ptr^.gpio);
if (isr_ptr^.flag=1) and (isr_ptr^.fd>=0) then
begin
if testrun then writeln('isr_handler running for GPIO',isr_ptr^.gpio);
while true do
begin
fdset[0].fd := STDIN_FILENO; fdset[0].events := POLLIN; fdset[0].revents:=0;
fdset[1].fd := isr_ptr^.fd; fdset[1].events := POLLPRI; fdset[1].revents:=0;
rc := FPpoll (fdset, nfds, 1000); // Timeout in ms
if (rc < 0) then begin if testrun then writeln('poll() failed!'); rslt:=-1; exit(rslt); end;
if (rc = 0) then
begin
if testrun then writeln('poll() timeout.');
if (isr_ptr^.flag = 0) then
begin
if testrun then writeln('exiting isr_handler (timeout)');
EndThread;
end;
end;
if ((fdset[1].revents and POLLPRI)>0) then
begin //* We have an interrupt! */
if (fpread(fdset[1].fd,buf,SizeOf(buf))=-1) then
begin
if testrun then writeln('read failed for interrupt');
rslt:=-1;
exit(rslt);
end;
InterLockedIncrement(isr_ptr^.int_cnt_raw);
if isr_ptr^.int_enable then
begin
InterLockedIncrement(isr_ptr^.int_cnt);
InterLockedIncrement(isr_ptr^.enter_isr_routine);
isr_ptr^.enter_isr_time:=now;
isr_ptr^.rslt:=Call_Func(isr_ptr^.gpio);
isr_ptr^.last_isr_servicetime:=MilliSecondsBetween(now,isr_ptr^.enter_isr_time);
InterLockedDecrement(isr_ptr^.enter_isr_routine);
end;
end;
if ((fdset[0].revents and POLLIN)>0) then
begin
if (fpread(fdset[0].fd,buf,1)=-1) then
begin
if testrun then writeln('read failed for stdin read');
rslt:=-1;
exit(rslt);
end;
if testrun then writeln('poll() stdin read 0x',HexStr(buf[0],2));
end;
flush (stdout);
end;
end
else
begin
if testrun then writeln('exiting isr_handler (flag)');
EndThread;
end;
isr_handler:=rslt;
end;
function WriteStr2UnixDev(dev,s:string):integer;
var rslt:integer; lgt:byte; buffer:I2C_databuf_t;
begin
rslt:=-1;
{$IFDEF UNIX}
with buffer do
begin
lgt:=length(s);
{$warnings off}
if lgt>SizeOf(buf) then
begin
LOG_Writeln(LOG_ERROR,'WriteStr2UnixDev: string to long: '+Num2Str(lgt,0)+'/'+Num2Str(SizeOf(buf),0));
exit(-1);
end;
{$warnings on}
buf:=s;
hdl:=fpopen(dev, Open_RDWR or O_NONBLOCK);
if hdl<0 then exit(-2);
rslt:=fpWrite(hdl,buf,lgt);
if (rslt<0) then LOG_Writeln(LOG_ERROR,'WriteStr2UnixDev: '+LNX_ErrDesc(fpgeterrno));
if (rslt=lgt) then rslt:=0;
fpclose(hdl);
end; // with
{$ENDIF}
WriteStr2UnixDev:=rslt;
end;
function GPIO_OpenFile(var isr:isr_t):integer;
// needed, because this is the only known possibility to use ints without kernel modifications.
(* path=/sys/class/gpio
echo $gpionum > $path/export
echo in > $path/gpio$gpionum/direction
echo $edgetype > $path/gpio$gpionum/edge
*)
var rslt:integer; pathstr,edge_type:string;
begin
rslt:=0; pathstr:=GPIO_path_c+'/gpio'+Num2Str(isr.gpio,0);
if isr.rising_edge then edge_type:='rising' else edge_type:='falling';
writeln('GPIO_OpenFile');
{$I-}
if (WriteStr2UnixDev(GPIO_path_c+'/export',Num2Str(isr.gpio,0))=0) then
if (WriteStr2UnixDev(pathstr+'/direction','in')=0) then
WriteStr2UnixDev(pathstr+'/edge',edge_type);
if FileExists(pathstr+'/value') then
isr.fd:=fpopen(pathstr+'/value', O_RDONLY or O_NONBLOCK );
{$I+}
if (isr.fd<0) then rslt:=-1;
GPIO_OpenFile:=rslt;
end;
function GPIO_int_active(var isr:isr_t):boolean;
begin
if isr.fd>=0 then GPIO_int_active:=true else GPIO_int_active:=false;
end;
function GPIO_set_int(var isr:isr_t; GPIO_num:longint; isr_proc:TFunctionOneArgCall; flags:s_port_flags) : integer;
var rslt:integer; _flags:s_port_flags; GPIO_struct:GPIO_struct_t;
begin
rslt:=-1; _flags:=flags;
//writeln('GPIO_int_set ',GPIO_num);
isr.gpio:=GPIO_num; isr.flag:=1; isr.rslt:=0; isr.int_enable:=false;
isr.fd:=-1; isr.int_cnt:=0; isr.int_cnt_raw:=0; isr.enter_isr_routine:=0;
isr.last_isr_servicetime:=0; isr.enter_isr_time:=now;
isr.func_ptr:=@RPI_hal_Dummy_INT;
_flags:=_flags+[INPUT]-[OUTPUT,PWMHW,PWMSW]; // interrupt is INPUT, remove all Output flags
isr.rising_edge:=true; // default
if (FallingEdge IN _flags) then isr.rising_edge:=false;
if (RisingEdge IN _flags) then isr.rising_edge:=true;
if isr.rising_edge
then _flags:=_flags+[RisingEdge]
else _flags:=_flags+[FallingEdge];
GPIO_SetStruct(GPIO_struct,1,isr.gpio,'INT',_flags);
if (isr.gpio>=0) and GPIO_Setup(GPIO_struct) then
begin
if GPIO_OpenFile(isr)=0 then
begin
if (isr_proc<>nil) then isr.func_ptr:=isr_proc;
BeginThread(@isr_handler,@isr,isr.ThreadId); // http://www.freepascal.org/docs-html/prog/progse43.html
isr.ThreadPrio:=ThreadGetPriority(isr.ThreadId);
rslt:=0;
end
else LOG_Writeln(LOG_ERROR,'GPIO_SETINT: Could not set INT for GPIO'+Num2Str(GPIO_num,0));
end;
if rslt<>0 then LOG_Writeln(LOG_ERROR,'GPIO_SETINT: err:'+Num2Str(rslt,0));
GPIO_set_int:=rslt;
end;
function GPIO_int_release(var isr:isr_t):integer;
var rslt:integer;
begin
rslt:=0;
//writeln('GPIO_int_release: pin: ',isr.gpio);
isr.flag:=0; isr.int_enable:=false; delay_msec(100); // let Thread Time to terminate
GPIO_set_edge_rising (isr.gpio,false);
GPIO_set_edge_falling(isr.gpio,false);
if isr.fd>=0 then
begin
fpclose(isr.fd); isr.fd:=-1;
WriteStr2UnixDev(GPIO_path_c+'/unexport',Num2Str(isr.gpio,0));
end;
GPIO_int_release:=rslt;
end;
procedure instinthandler; // not ready , inspiration http://lnxpps.de/rpie/
//var rslt:integer; p:pointer;
begin
// writeln(request_irq(110,p,SA_INTERRUPT,'short',nil));
end;
procedure GPIO_int_enable (var isr:isr_t); begin isr.int_enable:=true; (*writeln('int Enable ',isr.gpio);*) end;
procedure GPIO_int_disable(var isr:isr_t); begin isr.int_enable:=false; writeln('int Disable ',isr.gpio); end;
procedure inttest(GPIO_nr:longint);
// shows how to use the GPIO_int functions
const loop_max=30;
var cnt:longint; isr:isr_t;
begin
writeln('INT main start on GPIO',GPIO_nr,' loops: ',loop_max:0);
GPIO_set_int (isr,GPIO_nr,@my_isr,[RisingEdge]); // set up isr routine, initialize isr struct: GPIO_number, int_routine which have to be executed, rising_edge
GPIO_int_enable(isr); // Enable Interrupts, allows execution of isr routine
for cnt:=1 to loop_max do
begin
write ('doing nothing, waiting for an interrupt on GPIO',GPIO_nr:0,' loopcnt: ',cnt:3,' int_cnt: ',isr.int_cnt:3,' ThreadID: ',longword(isr.ThreadID),' ThPrio: ',isr.ThreadPrio);
if isr.rslt<>0 then begin write(' result: ',isr.rslt,' last service time: ',isr.last_isr_servicetime:0,'ms'); isr.rslt:=0; end;
writeln;
sleep (1000);
end;
GPIO_int_disable(isr);
GPIO_int_release(isr);
writeln('INT main end on GPIO',GPIO_nr);
end;
procedure GPIO_int_test; // shows how to use the GPIO_int functions
const gpio=22;
begin
writeln('GPIO_int_test: GPIO',gpio,' HWPin:',GPIO_MAP_GPIO_NUM_2_HDR_PIN(gpio));
inttest(gpio);
end;
{$ENDIF}
function SearchValIdx(var InpArr:array of real; srchval,Epsilon:real):longint;
// in: search a value 'srchval' in an array.
// return: index of the value. -1 if not found
var i,idx:longint;
begin
idx:=-1; i:=1;
while i<=Length(InpArr) do
begin
if SameValue(InpArr[i-1],srchval,Epsilon) then
begin idx:=i-1; i:=Length(InpArr); end;
inc(i);
end;
SearchValIdx:=idx;
end;
function MovAvg(interval:longword; var InpArr,OutArr:array of real):longint; // moving average
var i,j,l:longint; res:real;
begin
res:=0;
if Length(InpArr)>Length(OutArr) then l:=Length(OutArr) else l:=Length(InpArr);
for i:= 1 to l do
begin
res:=res+InpArr[i-1];
if i>=interval then
begin
res:=0;
for j:= 1 to interval do
begin
res:=res+InpArr[i-interval+j-1];
end;
if interval<>0 then OutArr[i-1]:=res/interval else OutArr[i-1]:=0;
end else OutArr[i-1]:=res/i;
end;
MovAvg:=l;
end;
procedure PID_SetTwiddle_KeyName(var TWIDDLE_Struct:PID_Twiddle_t; sect,key:string);
begin
with TWIDDLE_Struct do
begin
twiddle_INI_sect:=sect;
twiddle_INI_key:= key;
end; // with
end;
procedure PID_SaveTwiddle(var TWIDDLE_Struct:PID_Twiddle_t; K,dK:PID_array_t);
begin
with TWIDDLE_Struct do
begin
if (twiddle_INI_sect<>'') or (twiddle_INI_key<>'') then
begin
// if (twiddle_saveattol<>PID_twiddle_tolNOTsav) then
if (twiddle_tol[0]<>twiddle_tol[2]) then
begin
BIOS_SetIniString(twiddle_INI_sect,twiddle_INI_key,
PID_VectorStr(K, 0,PID_nk15,';')+'|'+
PID_VectorStr(dK, 0,PID_nk15,';')+'|'+
PID_VectorStr(twiddle_tol,0,PID_nk15,';')+'|'+
GetXMLTimeStamp(now),[]);
end else LOG_Writeln(LOG_ERROR,'PID_SaveTwiddle['+twiddle_INI_sect+'/'+twiddle_INI_key+']: not saved due to switched off tol param');
end else LOG_Writeln(LOG_ERROR,'PID_SaveTwiddle['+twiddle_INI_sect+'/'+twiddle_INI_key+']: invalid sect/key pair');
end; // with
end;
function PID_ReadTwiddle(sect,key:string; var K,dK,tol:PID_array_t):boolean;
// <key>=3.1089;0.0089;76.9491|0.000004245797254;0.000000011910849;0.000005511092205|0.0001;0.00001;0.0|2017-12-12..
var ok:boolean; i:longint; r:PID_float_t; sh:string;
begin
ok:=false;
if (sect<>'') or (key<>'') then
begin
sh:=Trimme(BIOS_GetIniString(sect,key,''),3);
if sh<>'' then
begin
ok:=true;
for i:=0 to 2 do
begin
if ok then ok:=Str2Num(Select_Item(Select_Item(sh,'|','',1),';','',i+1),r); if ok then K [i]:=r;
if ok then ok:=Str2Num(Select_Item(Select_Item(sh,'|','',2),';','',i+1),r); if ok then dK [i]:=r;
if not Str2Num(Select_Item(Select_Item(sh,'|','',3),';','',i+1),tol[i]) then
begin
case i of
0: tol[i]:=PID_twiddle_saveattol; // 0:twiddle_saveattol
2: tol[i]:=PID_twiddle_tolNOTsav; // 2:PID_twiddle_tolNOTsav
else tol[i]:=PID_twiddle_tolerance; // 1:twiddle_tolerance
end; // case
end; // if
end;
end;
end else LOG_Writeln(LOG_ERROR,'PID_ReadTwiddle['+sect+'/'+key+']: invalid sect/key pair');
PID_ReadTwiddle:=ok;
end;
function PID_ReadTwiddle(var TWIDDLE_Struct:PID_Twiddle_t; var K,dK,tol:PID_array_t):boolean;
begin
with TWIDDLE_Struct do
begin
PID_ReadTwiddle:=PID_ReadTwiddle(twiddle_INI_sect,twiddle_INI_key,K,dK,tol);
end; // with
end;
function PID_DetAvgs(IdxStart,IdxEnd:longint; var avgnumIst,avgnumPInc:longint):boolean;
begin
avgnumIst:=(IdxEnd-IdxStart+1) div 10; // try moving average with lines/10 values
if avgnumIst>PID_AVGmaxNum_c then avgnumIst:=PID_AVGmaxNum_c;
if avgnumIst<PID_AVGminNum_c then avgnumIst:=PID_AVGminNum_c;
avgnumPInc:=avgnumIst;
PID_DetAvgs:=true;
end;
function PID_FileLoad(StrList:TStringList; filnam,SearchCrit:string; var IdxStart,IdxEnd:longint):boolean;
var _ok:boolean; sh:string;
begin
_ok:=TextFile2StringList(filnam,StrList,false,sh);
if _ok then _ok:=GiveStringListIdx2(StrList,SearchCrit,IdxStart,IdxEnd)
else LOG_Writeln(Log_ERROR,'PID_FileLoad: input file '+filnam);
PID_FileLoad:=_ok;
end;
function PID_TDR(var TickArr,ValArr,OutTickDeltaArr,OutValArr:array of PID_float_t):longint;
//time derivative response
var i,l:longint;
begin
if Length(ValArr)>Length(OutValArr) then l:=Length(OutValArr) else l:=Length(ValArr);
if l>Length(TickArr) then l:=Length(TickArr);
for i:= 1 to l do
begin
OutValArr[i-1]:=0; OutTickDeltaArr[i-1]:=0;
if i>1 then
begin
OutTickDeltaArr[i-1]:=(TickArr[i-1]-TickArr[i-2]);
if OutTickDeltaArr[i-1]<>0 then
OutValArr[i-1]:= (ValArr [i-1]-ValArr [i-2])/OutTickDeltaArr[i-1];
end;
end;
PID_TDR:=l;
end;
function PID_VectorStr(var pidarr:PID_array_t; vk,nk:integer; sep:char):string;
var sh:string;
begin
sh:= Num2Str(pidarr[iKp],vk,nk)+sep+
Num2Str(pidarr[iKi],vk,nk)+sep+
Num2Str(pidarr[iKd],vk,nk);
PID_VectorStr:=sh;
end;
function PID_Vector(Kp,Ki,Kd:PID_float_t):PID_array_t;
var i:longint; pa:PID_array_t;
begin
for i:=1 to Length(pa) do pa[i-1]:=0;
pa[iKp]:=Kp; pa[iKi]:=Ki; pa[iKd]:=Kd;
PID_Vector:=pa;
end;
function PID_DetType(Te,Tb:PID_float_t):integer;
var i:integer; r:PID_float_t;
begin
i:=ord(PID_Default);
if not ( (Te=0) or (Tb=0) or IsNaN(Tb) or IsNaN(Te) ) then
begin
r:=Tb/Te;
i:=ord(P_Default); // gut regelbar -> P
if (r<=10) then i:=ord(PI_Default); // regelbar -> PI
if (r<3) then i:=ord(PID_Default); // schlecht regelbar -> PID
end;
PID_DetType:=i;
end;
function PID_TimAdj(timadjfct:real; var Te,Tb,TSum:PID_float_t):integer;
var res:integer;
begin
res:=-1;
if (not IsNaN(timadjfct)) and (timadjfct>0) then
begin
res:=0;
if not IsNaN(Te) then begin Te:=Te *timadjfct; inc(res); end;
if not IsNaN(Tb) then begin Tb:=Tb *timadjfct; inc(res); end;
if not IsNaN(Tsum) then begin Tsum:=Tsum*timadjfct; inc(res); end;
end;
PID_TimAdj:=res;
end;
function PID_sim(StrList:TStringList; simnr:integer):real;
//PID_loctusec=4; PID_locsollval=5; PID_locistval=6;
const hdr1=';;;';
var timadj:real; i:longint;
//Prof. Dr. R. Kessler, FH-Karlsruhe, FB-MN, http://www.home.fh-karlsruhe.de/~kero0001, WendeTangReg3.doc
// returns timebase and a list of values in csv format (for testing)
procedure tlx(hdr,xs,ys,zs:string); begin StrList.add(hdr+xs+';'+zs+';'+ys); end;
procedure tl0(x,y:real); begin tlx(hdr1,AdjZahlDE(x/timadj,0,PID_nk8),AdjZahlDE(y,0,PID_nk8),'0'); end;
procedure tl1(x,y:real); begin tlx(hdr1,AdjZahlDE(x/timadj,0,PID_nk8),AdjZahlDE(y,0,PID_nk8),'1'); end;
procedure tl2(x,y:real); begin tlx('', AdjZahlDE(x/timadj,0,PID_nk8),'',AdjZahlDE(y,0,PID_nk8)); end;
begin
timadj:=1;
case simnr of
1:begin
for i:=0 to 400 do
begin
if i<10 then tl2((i/10),0) else tl2((i/10),1);
end;
end;
else
begin
tl0(0,0); tl1(1,0); tl1(2,0); tl1(3,0); tl1(4,0);
tl1(5,0.01); tl1(6.25,0.05); tl1(7.5,0.15); tl1(8.75,0.25);
tl1(10,0.4); tl1(11.25,0.6); tl1(12.5,0.75); tl1(13.75,0.85);
tl1(15,0.9); tl1(16.25,0.95);tl1(17.5,0.97); tl1(18.75,0.99);
tl1(20,1);
for i:=21 to 40 do tl1(i,1);
end;
end; // case
PID_sim:=timadj;
end;
function PID_DetPara(loglvl:t_ErrorLevel; StrList:TStringList; idxStart,idxEnd,smoothdata,smoothtdr,loctim,locist,locSetPoint:longint; StoerSprung,timadjfct:real; var Ks,Te,Tb,Tsum,SampleTimeAvg:PID_float_t; tst:boolean; filout:string):integer;
//determines Ks,Te,Tb out of a given sensor data (.csv)
//Ks,Te,Tb for feeding PID_GetPara
//Prof. Dr. R. Kessler, FH-Karlsruhe, FB-MN, http://www.home.fh-karlsruhe.de/~kero0001, WendeTangReg3.doc
//StepResponseList.csv -> using values t(usec) and ist. FieldNum 4&6. SetPoint/soll FieldNum 5
//pwm%;pidnr;cnt;t(usec);soll;ist;avg;preached;t2preach;preachedmax;t2preachmax;pincms;pok;calc;stdev;pon;ppc
//0,45;6;0;0;132;-0,15259;-0,15259;133,28756;552630;133,44015;557774;0,24146382;1;0;7,92544758;1;0
//...
//0,45;6;1081;619469;132;129,85428;129,85428;133,28756;552630;133,44015;557774;0,24146382;1;133,4401502;7,92544758;0;0
var _ok:boolean; res,i,linecnt,idx,avgnumIst,avgnumTDR:longint; _tl:TStringList;
maxZ,minZ,tZ,maxXp,tWP,XWP,t1,t2,_Te,_Tb,wt,scaleXp:PID_float_t;
A_t,A_td,A_W,A_U,A_X,A_TDR,A_Xp: array of PID_float_t;
begin
_tl:=TStringList.create;
//writeln('PID_DetPara filout:',filout,' ',idxStart,' ',idxEnd,' ',tst);
linecnt:=idxEnd-idxStart+1; res:=-1; Ks:=NaN; Te:=Nan; Tb:=NaN; Tsum:=Nan;
if (linecnt>0) then
begin
Ks:=1; scaleXp:=1;
SetLength(A_U, linecnt); SetLength(A_X, linecnt); SetLength(A_t,linecnt);
SetLength(A_TDR,linecnt); SetLength(A_Xp,linecnt); SetLength(A_W,linecnt);
SetLength(A_td, linecnt);
for i:=idxStart to idxEnd do
begin // ArrFill
_ok:=true;
if not Str2Num(AdjZahl(Select_Item(StrList[i],';','',loctim)), A_t[i-idxStart]) then _ok:=false; // timeval
if not Str2Num(AdjZahl(Select_Item(StrList[i],';','',locSetPoint)),A_W[i-idxStart]) then _ok:=false; // SetPoint
if not Str2Num(AdjZahl(Select_Item(StrList[i],';','',locist)), A_U[i-idxStart]) then _ok:=false; // istval
if not _ok then LOG_Writeln(LOG_ERROR,'PID_DetPara['+Num2Str(i,0)+'] value not ok: '+StrList[i]);
// writeln(i:5,' ',A_t[i-idxStart],' ',A_W[i-idxStart],' ',A_U[i-idxStart]);
end;
avgnumIST:=smoothdata; if avgnumIST<1 then avgnumIST:=1; // 1=no smoothing
avgnumTDR:=smoothtdr; if avgnumTDR<1 then avgnumTDR:=1;
MovAvg(avgnumIst,A_U,A_X); // smoothen raw input sensor data
PID_TDR(A_t,A_X,A_td,A_TDR);
SampleTimeAvg:=Mean(A_td)*timadjfct;//writeln('SampleTimeAvg: ',SampleTimeAvg:0:4);
MovAvg(avgnumTDR,A_TDR,A_Xp); // smoothen t-derived response
minZ:=MinValue(A_W);
maxZ:=MaxValue(A_W);
if minZ=maxZ then minZ:=0;
idx:=SearchValIdx(A_W,maxZ,PID_epsilon_c);
if idx<0 then tZ:=0 else tZ:=A_t[idx]; // Zeit tZ des Z-Sprungs finden
maxXp:=MaxValue(A_Xp);
idx:=SearchValIdx(A_Xp,maxXp,PID_epsilon_c); // Koordinaten tWP und XWP suchen
if (idx>=0) then
begin
maxXp:= maxXp;
tWP:=A_t[idx]; XWP:=A_X[idx]; // Wendepunkt
t1:= (XWP-minZ)/maxXp; // t1= Zeitabschn. unter Wendetangente bis minZ
t2:= (maxZ-XWP)/maxXp; // t2= Zeitabschn. oberhalb Wendetangente bis maxZ
Te:= tWP-t1-tZ; // Te= Verzugszeit
Tb:= t1+t2; // Tb= Ausgleichszeit
if (StoerSprung<>0) then
Ks:=maxZ/StoerSprung; // Ks= Streckenverstärkung = Endwert der Sprungantwort geteilt durch Höhe des Störsprungs.
if tst then
begin
// create .csv output // overwrite Input StringList !!!!!!!!
_tl.clear;
scaleXp:=maxZ/maxXp; // normalize TDR
_tl.add('time,U,W,U(avg='+Num2Str(avgnumIst,0)+'),Xp(scale='+Num2Str(scaleXp,0,PID_nk8)+'),WT');
for i:=1 to linecnt do
begin
if A_t[i-1]<(tWP-t1) then wt:=minZ else
if A_t[i-1]>(tWP+t2) then wt:=maxZ
else wt:=(A_t[i-1]-(tWP-t1))/Tb*(maxZ-minZ); // calc Wendetangente
_tl.add(
Num2Str(A_t [i-1],0)+','+
Num2Str(A_U [i-1],0,PID_nk8)+','+
Num2Str(A_W [i-1],0,PID_nk8)+','+
Num2Str(A_X [i-1],0,PID_nk8)+','+
Num2Str(A_Xp[i-1]*scaleXp,0,PID_nk8)+','+
Num2Str(wt,0,PID_nk8)
);
end;
if (filout<>'') then
begin
SAY(loglvl,'PID_DetPara: writing to ('+Num2Str(_tl.count,0)+') '+filout);
if not StringList2TextFile(filout,_tl) then
LOG_Writeln(LOG_ERROR,'PID_DetPara: can not write '+filout);
end;
// ShowStringList(_tl);
scaleXp:=1;
end; // tst
_Te:=Te; _Tb:=Tb; // keep calced Te and Tb
if PID_TimAdj(SampleTimeAvg,Te,Tb,TSum)>0 then
begin
res:=PID_DetType(Te,Tb); // Determine P/PI/PID
SAY(loglvl, 'tZ/minZ/maxZ/maxXp/SampleTimeAvg/StoerSprung: '+
Num2Str(tZ,0,PID_nk8)+' '+Num2Str(minZ,0,PID_nk8)+' '+
Num2Str(maxZ,0,PID_nk8)+' '+Num2Str(maxXp,0,PID_nk8)+' '+
Num2Str(SampleTimeAvg,0,PID_nk8)+' '+Num2Str(StoerSprung,0,PID_nk8));
SAY(loglvl, 'avgnumIST/avgnumTDR: '+Num2Str(avgnumIST,0,PID_nk8)+' '+Num2Str(avgnumTDR,0,PID_nk8));
SAY(loglvl, 'WendePunkt['+Num2Str(idx,0)+']: '+
Num2Str(tWP,0,PID_nk8)+'/'+Num2Str(XWP,0,PID_nk8));
SAY(loglvl, 't1/t2: '+Num2Str(t1,0,PID_nk8)+' '+Num2Str(t2,0,PID_nk8));
SAY(loglvl, 'Ks/Te/Tb: '+
Num2Str(Ks,0,PID_nk8)+' '+ Num2Str(_Te,0,PID_nk8)+' '+
Num2Str(_Tb,0,PID_nk8));
SAY(loglvl, 'TimAdj Te/Tb/suggestedPIDMethod: '+
Num2Str(Te,0,PID_nk8)+' '+Num2Str(Tb,0,PID_nk8)+' '+GetEnumName(TypeInfo(PID_Method_t),res));
end else LOG_Writeln(LOG_ERROR,'PID_DetPara: timeadj wrong paras');
end else LOG_Writeln(LOG_ERROR,'PID_DetPara: Xp not found (wrong epsilon?)');
SetLength(A_U,0); SetLength(A_X,0); SetLength(A_t,0); SetLength(A_td,0);
SetLength(A_TDR,0); SetLength(A_Xp,0); SetLength(A_W,0);
end else LOG_Writeln(LOG_ERROR,'PID_DetPara: wrong parameter/empty list');
_tl.free;
PID_DetPara:=res;
end;
function PID_DetPara(StrList:TStringList; idxStart,idxEnd,smoothdata,smoothtdr,loctim,locist,locSetPoint:longint; StoerSprung,timadjfct:real; var Ks,Te,Tb,Tsum,SampleTimeAvg:PID_float_t; tst:boolean):integer;
begin PID_DetPara:=PID_DetPara(LOG_DEBUG,StrList,idxStart,idxEnd,smoothdata,smoothtdr,loctim,locist,locSetPoint,StoerSprung,timadjfct,Ks,Te,Tb,Tsum,SampleTimeAvg,tst,''); end;
function PID_GetPara(loglvl:t_ErrorLevel; Ks,Te,Tb,Tsum:PID_float_t; Method:PID_Method_t; var Ti,Td:PID_float_t; var K:PID_array_t; loginfo:string):integer;
//calcs Kp,Ki,Kd,Ti,Td for feeding PID_Init
//Input: Statische Verstärkung (Ks), Verzugszeit (Te) und Ausgleichszeit (Tb),
//Input: Px_SUM (TSum)
//Input: Einstellregel (Method)
//Output: Ti,Td; Karray:Kp,Ki,Kd
//
//https://de.wikipedia.org/wiki/Faustformelverfahren_(Automatisierungstechnik)
//Script: Spezialgebiete der Steuer- und Regelungstechnik WS 2008/09 FH Dortmund Schriftliche Ausarbeitung Thema: PID - Einstellregeln
//http://www.home.hs-karlsruhe.de/~kero0001/wendtang/wendtang1.html
//Einstellregeln nach Oppelt, ZieglerNichols oder
//Chien/Hrones/Reswick, Samal:
//GSA: gutes Störverhalten, aperiodisch (schwingungsfrei)
//GFA: gutes Führungsverhalten, aperiodisch (schwingungsfrei)
//GS20: gutes Störverhalten, 20% Überschwingen
//GF20: gutes Führungsverhalten, 20% Überschwingen
//
//Tn/Ti: Nachstellzeit (DIN19226/DIN EN 60027-6)
//Tv/Td: Vorhaltzeit (DIN19226/DIN EN 60027-6)
var res:integer;
begin
K:=PID_Vector(0,0,0); Ti:=NaN; Td:=NaN; res:=-1;
if Method IN [P_SUM..PID_SUM_Fast]
then if IsNaN(Ks) or IsNaN(Tsum) or (Ks=0) then exit(res)
else if IsNaN(Ks) or IsNaN(Tb) or IsNaN(Te) or (Ks=0) or (Te=0) then exit(res);
res:=ord(Method);
case Method of
P_Oppelt: begin K[iKp]:=(1.00/Ks)*(Tb/Te); end;
PI_Oppelt: begin K[iKp]:=(0.80/Ks)*(Tb/Te); Ti:=3.00*Te; end;
PID_Oppelt: begin K[iKp]:=(1.20/Ks)*(Tb/Te); Ti:=2.00*Te; Td:=0.42*Te; end;
P_ZiegNich: begin K[iKp]:=(1.00/Ks)*(Tb/Te); end;
PI_ZiegNich: begin K[iKp]:=(0.90/Ks)*(Tb/Te); Ti:=3.33*Te; end;
PID_ZiegNich: begin K[iKp]:=(1.20/Ks)*(Tb/Te); Ti:=2.00*Te; Td:=0.50*Te; end;
P_SUM: begin K[iKp]:=(1.00/Ks); Td:=0; end;
PD_SUM: begin K[iKp]:=(1.00/Ks); Td:=0.33*Tsum; end;
PI_SUM: begin K[iKp]:=(0.50/Ks); Ti:=0.50*Tsum; Td:=0; end;
PID_SUM: begin K[iKp]:=(1.00/Ks); Ti:=0.66*Tsum; Td:=0.167*Tsum; end;
PI_SUM_Fast: begin K[iKp]:=(1.00/Ks); Ti:=0.70*Tsum; Td:=0; end;
PID_SUM_Fast: begin K[iKp]:=(2.00/Ks); Ti:=0.80*Tsum; Td:=0.194*Tsum; end;
P_CHR_GSA,
P_CHR_GFA: begin K[iKp]:=(0.30/Ks)*(Tb/Te); end;
P_CHR_GS20,
P_CHR_GF20: begin K[iKp]:=(0.70/Ks)*(Tb/Te); end;
PI_CHR_GSA: begin K[iKp]:=(0.60/Ks)*(Tb/Te); Ti:=4.00*Te; end;
PI_CHR_GFA: begin K[iKp]:=(0.35/Ks)*(Tb/Te); Ti:=1.20*Tb; end;
PI_CHR_GS20: begin K[iKp]:=(0.70/Ks)*(Tb/Te); Ti:=2.30*Te; end;
PI_CHR_GF20: begin K[iKp]:=(0.60/Ks)*(Tb/Te); Ti:=1.00*Tb; end;
PID_CHR_GSA: begin K[iKp]:=(0.95/Ks)*(Tb/Te); Ti:=2.40*Te; Td:=0.42*Te; end;
PID_CHR_GFA: begin K[iKp]:=(0.60/Ks)*(Tb/Te); Ti:=1.00*Tb; Td:=0.50*Te; end;
PID_CHR_GS20: begin K[iKp]:=(1.20/Ks)*(Tb/Te); Ti:=2.00*Te; Td:=0.42*Te; end;
PID_CHR_GF20: begin K[iKp]:=(0.95/Ks)*(Tb/Te); Ti:=1.35*Tb; Td:=0.47*Te; end;
P_Default,
P_SAMAL_GSA,
P_SAMAL_GFA: begin K[iKp]:=(0.30/Ks)*(Tb/Te); end;
P_SAMAL_GS20,
P_SAMAL_GF20: begin K[iKp]:=(0.71/Ks)*(Tb/Te); end;
PI_Default,
PI_SAMAL_GFA: begin K[iKp]:=(0.34/Ks)*(Tb/Te); Ti:=1.20*Tb; end;
PI_SAMAL_GF20: begin K[iKp]:=(0.59/Ks)*(Tb/Te); Ti:=1.00*Tb; end;
PI_SAMAL_GSA: begin K[iKp]:=(0.59/Ks)*(Tb/Te); Ti:=4.00*Te; end;
PI_SAMAL_GS20: begin K[iKp]:=(0.71/Ks)*(Tb/Te); Ti:=2.30*Te; end;
PID_Default,
PID_SAMAL_GFA: begin K[iKp]:=(0.59/Ks)*(Tb/Te); Ti:=1.00*Tb; Td:=0.50*Te; end;
PID_SAMAL_GF20: begin K[iKp]:=(0.95/Ks)*(Tb/Te); Ti:=1.35*Tb; Td:=0.47*Te; end;
PID_SAMAL_GSA: begin K[iKp]:=(0.95/Ks)*(Tb/Te); Ti:=2.40*Te; Td:=0.42*Te; end;
PID_SAMAL_GS20: begin K[iKp]:=(1.20/Ks)*(Tb/Te); Ti:=2.00*Te; Td:=0.42*Te; end;
else begin K[iKp]:=(1.00/Ks); end;
end; // case
if not IsNaN(Ti) then K[iKi]:=K[0]/Ti;
if not IsNan(Td) then K[iKd]:=K[0]*Td;
SAY(loglvl,'PID_GetParaIn ['+GetEnumName(TypeInfo(PID_Method_t),ord(Method))+' '+loginfo+']: Ks: '+Num2Str(Ks,0,PID_nk8)+' Te: '+Num2Str(Te,0,PID_nk8)+' Tb: '+Num2Str(Tb,0,PID_nk8));
SAY(loglvl,'PID_GetParaOut['+GetEnumName(TypeInfo(PID_Method_t),ord(Method))+' '+loginfo+']: Kp: '+Num2Str(K[0],0,PID_nk8)+' Ki: '+Num2Str(K[1],0,PID_nk8)+' Kd: '+Num2Str(K[2],0,PID_nk8)+' Ti: '+Num2Str(Ti,0,PID_nk8)+' Td: '+Num2Str(Td,0,PID_nk8));
PID_GetPara:=res;
end;
function CSV_RemFirstSep(strng:string; sep:char):string;
var sh:string;
begin
sh:=strng;
if (Length(strng)>0) and (strng[1]=sep) then
sh:=copy(strng,2,Length(strng));
CSV_RemFirstSep:=sh;
end;
function CSV_RemLastSep(strng:string; sep:char):string;
var lgt:longint; sh:string;
begin
sh:=strng; lgt:=Length(sh);
if (lgt>0) then
if (sh[lgt]=sep) then SetLength(sh,(lgt-1));
CSV_RemLastSep:=sh;
end;
procedure CSV_MaintList(var csvlst:string; entry:string; addit:boolean);
begin
if (entry<>'') then
begin
if addit then
begin
if (csvlst<>'') then
begin
if (Pos(entry+',',csvlst+',')=0) then csvlst:=csvlst+','+entry;
end else csvlst:=entry;
end else csvlst:=StringReplace(csvlst+',',entry+',','',[rfReplaceAll]);
csvlst:=CSV_RemLastSep(csvlst,',');
end;
end;
function CSV_MaintListToogleField(var csvlst:string; entry:string):boolean;
var addit:boolean;
begin
addit:=(Pos(entry+',',csvlst+',')=0);
CSV_MaintList(csvlst,entry,addit);
CSV_MaintListToogleField:=addit;
end;
procedure PID_SimCSV(tl:TStringList; var pid:PID_Struct_t);
// time;td;U;cnt;W;U(avg);Xp;WT
var i:longint; r,OldVal,NewVal,Stellgroesse,SetPoint:PID_float_t; sh:string;
begin
with pid do
begin
r:=0; OldVal:=0;
for i:=1 to tl.count do
begin
if i>1 then
begin
sh:=tl[i-1];
if Str2Num(AdjZahl(Select_Item(sh,';','',5)),SetPoint) and
Str2Num(AdjZahl(Select_Item(sh,';','',6)),NewVal) then
begin
Stellgroesse:= PID_Calc(pid,SetPoint,NewVal,false);
r:= r+(Stellgroesse/(SetPoint/PID_Ks))*(NewVal-OldVal);
tl[i-1]:=tl[i-1]+';'+AdjZahlDE(r,0,PID_nk8)+';'+AdjZahlDE(Stellgroesse*PID_Ks, 0,PID_nk8);
OldVal:=NewVal;
end;
end else tl[i-1]:=tl[i-1]+';X;Y(scale='+Num2Str(PID_Ks,0,4)+')'; // csv Hdr
end;
end; // with
end;
procedure PID_TestSim;
var _tl:TStringList; idxa,idxe,avgnumIst,avgnumPInc:longint; Method:PID_Method_t;
timadj,SmplTimAvg,StoerSprung,Ks,Te,Tb,Tsum,Ti,Td:PID_float_t; K:PID_array_t;
pid1:PID_Struct_t;
begin
_tl:=TStringList.create;
timadj:=PID_sim(_tl,0); idxa:=0; idxe:=_tl.count-1; //ShowStringList(_tl);
PID_DetAvgs(idxa,idxe,avgnumIst,avgnumPInc);
StoerSprung:=1; Method:=PID_Default;
avgnumIst:=1; avgnumPInc:=1; // demo, no data smoothing.
PID_DetPara(_tl,idxa,idxe,avgnumIst,avgnumPInc,PID_loctusec,PID_locistval,PID_locsollval,StoerSprung,timadj,Ks,Te,Tb,Tsum,SmplTimAvg,true);
PID_GetPara(LOG_INFO,Ks,Te,Tb,Tsum,Method,Ti,Td,K,'');
writeln('PID_TestSim Kp:',K[iKp]:0:2,' Ki:',K[iKi]:0:2,' Kd:',K[iKd]:0:2);
// Kp:=1.1; Ki:=0.2; Kd:=0.1; // Kp=1.1,Ki=0.2,Kd=0.1; // Kp:=1; Ki:=0; Kd:=0.5;
PID_Init(pid1,1,500,false,Ks,0,-25,25,1000,0,K,PID_Vector(-1.25,1.25,1000),PID_Vector(PID_twiddle_saveattol,PID_twiddle_tolerance,PID_twiddle_tolNOTsav));
PID_SimCSV(_tl,pid1);
ShowStringList(_tl);
_tl.free;
end;
procedure PID_InitTwiddle(var PID_Struct:PID_Struct_t; enab:boolean; itermax:longword; ap,adp,tol:PID_array_t);
begin
//writeln('PID_InitTwiddle['+Num2Str(PID_Struct.PID_nr,0)+']:');
PID_SetSelfTuning(PID_Struct,enab);
with PID_Struct.PID_Twiddle do
begin
twiddle_best_error:=MaxReal;
twiddle_sum_dp:= MaxReal;
twiddle_sum_dp_old:=MaxReal;
twiddle_idx:= 0;
twiddle_state:= 0;
twiddle_iterations:=0;
twiddle_intermax:= itermax;
twiddle_saved:= false;
twiddle_tol:= tol;
p:=ap; dp:=adp;
// ps:=p; dps:=dp;
end; // with
PID_SetTwiddle_KeyName(PID_Struct.PID_Twiddle,'','');
end;
procedure PID_InitTwiddle(var PID_Struct:PID_Struct_t); // try some init values
begin
PID_InitTwiddle(PID_Struct,false,500,
PID_Vector(0,0,0),
PID_Vector(1,1,1),
PID_Vector(PID_twiddle_saveattol,PID_twiddle_tolerance,PID_twiddle_tolNOTsav));
end;
procedure PID_UpdateError(var PID_Twiddle:PID_Twiddle_t; error:PID_float_t);
var errold:PID_float_t;
begin
with PID_Twiddle do
begin
errold:= err[iKp];
err[iKp]:= error;
err[iKi]:= err[iKi] + error;
err[iKd]:= error - errold;
end; // with
end;
function PID_TotalError(var PID_Struct:PID_Struct_t):PID_float_t;
begin
with PID_Struct do
begin
with PID_Twiddle do
begin
PID_TotalError:= -PID_K[iKp]*err[iKp] - PID_K[iKi]*err[iKi] - PID_K[iKd]*err[iKd];
end; // with
end; // with
end;
procedure PID_InitPara(var PID_Struct:PID_Struct_t; K:PID_array_t);
begin
with PID_Struct do
begin
PID_K:=K; PID_Twiddle.err:=PID_Vector(0,0,0);
end; // with
end;
function PID_Info(var PID_Struct:PID_Struct_t; fmt:longint):string;
const nkc=15; gkc=nkc+5;
var li:longint; outstr:string;
begin
outstr:='';
with PID_Struct do
begin
with PID_Twiddle do
begin
case fmt of
1: begin
outstr:='Kp,Ki,Kd: ';
for li:=1 to Length(PID_K) do outstr:=outstr+Num2Str(PID_K[li-1],gkc,nkc)+' ';
end;
2: begin
outstr:='KpS,KiS,KdS: ';
for li:=1 to Length(PID_Ksav) do outstr:=outstr+Num2Str(PID_Ksav[li-1],gkc,nkc)+' ';
end;
3: begin
outstr:='ControlOut,MinOutput0,MinOutput,MaxOutput: ';
outstr:=outstr+Num2Str(PID_ControlOut,gkc,nkc)+' '+Num2Str(PID_MinOutput0,gkc,nkc)+' '+Num2Str(PID_MinOutput,gkc,nkc)+' '+Num2Str(PID_MaxOutput,gkc,nkc);
end;
11: begin // show Twiddle p
outstr:='p [0-2]: ';
for li:=1 to Length(p) do outstr:=outstr+Num2Str(p[li-1],gkc,nkc)+' ';
end;
12: begin // show Twiddle dp
outstr:='dp [0-2]: ';
for li:=1 to Length(dp) do outstr:=outstr+Num2Str(dp[li-1],gkc,nkc)+' ';
end;
13: begin // show Twiddle ps
outstr:='pS [0-2/'+Bool2Dig(sum(ps)<>0)+']: ';
for li:=1 to Length(ps) do outstr:=outstr+Num2Str(ps[li-1],gkc,nkc)+' ';
end;
14: begin // show Twiddle dps
outstr:='dpS[0-2/'+Bool2Dig(sum(dps)<>0)+']: ';
for li:=1 to Length(dps) do outstr:=outstr+Num2Str(dps[li-1],gkc,nkc)+' ';
end;
15: begin // show Twiddle err
outstr:='err[0-2]: ';
for li:=1 to Length(err) do outstr:=outstr+Num2Str(dps[li-1],gkc,nkc)+' ';
end;
else LOG_Writeln(LOG_ERROR,'PID_Info: unknown fmt: '+Num2Str(fmt,0));
end; // case
end; // with
end; // with
PID_Info:=outstr;
end;
procedure PID_TwiddleCalc(var PID_Struct:PID_Struct_t);
// https://github.com/anupriyachhabra/PID-Controller/blob/master/src/PID.cpp
// https://github.com/antevis/CarND_T2_P4_PID/tree/master/src
// https://www.youtube.com/watch?v=2uQ2BSzDvXs
// http://www.htw-mechlab.de/index.php/numerische-optimierung-in-matlab-mit-twiddle-algorithmus/
// https://junshengfu.github.io/PID-controller/
var _err:PID_float_t;
begin
with PID_Struct.PID_Twiddle do
begin
twiddle_sum_dp_old:=twiddle_sum_dp;
twiddle_sum_dp:=sum(dp);
if (not twiddle_saved) and (twiddle_tol[0]<>twiddle_tol[2]) and
(twiddle_sum_dp<=twiddle_tol[0]) then
begin
ps:=p; dps:=dp; twiddle_saved:=true;
SAY(LOG_INFO,'PID_SaveTwiddle['+ Num2Str(PID_Struct.PID_nr,0)+'/'+
twiddle_INI_sect+'/'+twiddle_INI_key+']:'+
' sumdp:'+Num2Str(twiddle_sum_dp,0,PID_nk8)+
' tol:('+PID_VectorStr(twiddle_tol,0,PID_nk8,' ')+')' );
PID_SaveTwiddle(PID_Struct.PID_Twiddle,ps,dps);
end; // keep results
if (twiddle_sum_dp>twiddle_tol[1]) then
begin
case twiddle_state of
0: begin
p[twiddle_idx]:=p[twiddle_idx] + dp[twiddle_idx];
twiddle_state:= 1;
end;
1: begin
_err:=PID_TotalError(PID_Struct);
if (_err < twiddle_best_error) then
begin
twiddle_best_error:=_err;
dp[twiddle_idx]:=dp[twiddle_idx] * 1.1;
twiddle_state:=3;
end
else
begin
p[twiddle_idx]:=p[twiddle_idx] - 2 * dp[twiddle_idx];
// if (p[twiddle_idx]<0) then p[twiddle_idx]:=0;
twiddle_state:=2;
end;
end;
2: begin
_err:=PID_TotalError(PID_Struct);
if (_err < twiddle_best_error) then
begin
twiddle_best_error:= _err;
dp[twiddle_idx]:= dp[twiddle_idx] * 1.1;
end
else
begin
p[twiddle_idx]:= p[twiddle_idx] + dp[twiddle_idx];
dp[twiddle_idx]:= dp[twiddle_idx] * 0.9;
end;
twiddle_state:=3;
end;
3: begin
twiddle_idx:=((twiddle_idx+1) mod Length(p));
twiddle_state:=0;
end;
else LOG_Writeln(LOG_ERROR,'PID_TwiddleCalc, invalid twiddle_idx: '+Num2Str(twiddle_idx,0));
end; // case
end;
PID_InitPara(PID_Struct,p);
// SAY(LOG_INFO,'Twiddle['+Num2Str(PID_Struct.PID_nr,0)+']: '+Num2Str(p[0],9,6)+' '+Num2Str(p[1],9,6)+' '+Num2Str(p[2],9,6));
end; // with
end;
procedure PID_Limit(var Value:PID_float_t; MinOut0,MinOut,MaxOut:PID_float_t);
begin
if (Value=0) then
begin
if Value<MinOut0 then Value:=MinOut0 else if Value>MaxOut then Value:=MaxOut;
end
else
begin
if Value<MinOut then Value:=MinOut else if Value>MaxOut then Value:=MaxOut;
end;
end;
procedure PID_SetPrevInput (var PID_Struct:PID_Struct_t; pval:PID_float_t); begin PID_Struct.PID_ProcessValue:=pval; end;
procedure PID_SetSelfTuning(var PID_Struct:PID_Struct_t; On_:boolean); begin PID_Struct.PID_Twiddle.twiddle_on:=On_; end;
procedure PID_SetIntImprove(var PID_Struct:PID_Struct_t; On_:boolean);
// Default: on
// Switches on or off the "Integration Improvement" mechanism of "PID_Struct".
// This mechanism prevents overshoot/ringing/oscillation
// due to integration. To be used after "PID_Init"
begin PID_Struct.PID_IntImprove:=On_; end;
procedure PID_SetDifImprove(var PID_Struct:PID_Struct_t; On_:boolean);
// Default: on
// Switches on or off the "Differentiation Improvement" mechanism of "PID_Struct".
// This mechanism prevents unnecessary correction
// delay when the actual value is changing towards the SetPoint.
// To be used after "PID_Init"
begin PID_Struct.PID_DifImprove:=On_; end;
procedure PID_SetSampleTimeAdjust(var PID_Struct:PID_Struct_t; On_:boolean);
begin PID_Struct.PID_STimAdj:=On_; end;
procedure PID_ResetIntegrator(var PID_Struct:PID_Struct_t);
// Re-initialises the PID engine of "PID_Struct" without change of settings
begin PID_Struct.PID_Integrated:=PID_Struct.PID_IntegratedWindupResetValue; end;
procedure PID_IntegratedWindupReset(var PID_Struct:PID_Struct_t; WindupResetValue:PID_float_t);
begin PID_Struct.PID_IntegratedWindupResetValue:=WindupResetValue; end;
procedure PID_SetSampleTime(var PID_Struct:PID_Struct_t; New_dT_usec:int64);
var ratio,NewSampleTime:PID_float_t;
begin
with PID_Struct do
begin
if (New_dT_usec>0) and (PID_SampleTime>0) then
begin
NewSampleTime:= New_dT_usec*1000; // micro -> milli secs
ratio:= NewSampleTime/PID_SampleTime;
PID_K[iKi]:= PID_K[iKi]*ratio;
PID_K[iKd]:= PID_K[iKd]/ratio;
PID_SampleTime:= NewSampleTime;
end;
end; // with
end;
procedure PID_SetMinMaxLimit(var PID_Struct:PID_Struct_t; MinOutput0,MinOutput,MaxOutput:PID_float_t);
begin
with PID_Struct do
begin
PID_MinOutput0:=MinOutput0;
PID_MinOutput:= MinOutput;
PID_MaxOutput:= MaxOutput;
end; // with
end;
procedure PID_Reset(var PID_Struct:PID_Struct_t);
begin
with PID_Struct do
begin
PID_ResetIntegrator(PID_Struct); PID_SetPrevInput(PID_Struct,0.0);
PID_Error:=0; PID_LastError:=0.0; PID_PrevAbsError:=0.0; PID_cnt:=0;
end; // with
end;
procedure PID_Init(var PID_Struct:PID_Struct_t; nr:longint; itermax:longword; enab_twiddle:boolean; Ks,MinOutput0,MinOutput,MaxOutput,SampleTime_ms,WindupResetValue:PID_float_t; K,dK,tol:PID_array_t);
// Initialises the PID engine of "PID_Struct"
// Ks = Amplification
// Kp = the "proportional" error multiplier
// Ki = the "integrated value" error multiplier
// Kd = the "derivative" error multiplier
// MinOutput0= the minimal value the output value can have, if switched off
// MinOutput = the minimal value the output value can have, if switched on
// MaxOutput = the maximal value the output can have, if switched on
begin
PID_IntegratedWindupReset (PID_Struct,WindupResetValue);
PID_Reset (PID_Struct);
PID_SetIntImprove (PID_Struct,true);
PID_SetDifImprove (PID_Struct,true);
PID_SetSampleTimeAdjust (PID_Struct,false);
PID_SetMinMaxLimit (PID_Struct,MinOutput0,MinOutput,MaxOutput);
PID_InitPara (PID_Struct,K);
PID_InitTwiddle (PID_Struct,enab_twiddle,itermax,K,dK,tol); // tol=0.00001
with PID_Struct do
begin
PID_nr:=nr; PID_Ks:=Ks; PID_ControlOut:=0; PID_Ksav:=K;
(* PID_K[iKp]:=PID_K[iKp]/PID_Ks;
PID_K[iKi]:=PID_K[iKi]/PID_Ks;
PID_K[iKd]:=PID_K[iKd]/PID_Ks; *)
// writeln('PID_Init['+Num2Str(nr,0)+'] Ks: ',PID_Ks:0:5,' Kp:',PID_K[iKp]:0:5,' Ki:',PID_K[iKi]:0:5,' Kd:',PID_K[iKd]:0:5,' max:',PID_MaxOutput:0:2,' min:',PID_MinOutput:0:2,' min0:',PID_MinOutput0:0:2);
clock_gettime(CLOCK_REALTIME,@PID_Time);
PID_LastTime:=PID_Time;
PID_FirstTime:=true;
PID_SampleTime:=SampleTime_ms; PID_LastSampleTime:=PID_SampleTime;
PID_dT:=round(PID_SampleTime*1000); PID_LastdT:=PID_dT;
end;
end;
procedure PID_Init(var PID_Struct:PID_Struct_t; nr:longint; Ks,MinOutput0,MinOutput,MaxOutput,SampleTime_ms,tolerance,saveattol:PID_float_t; K:PID_array_t);
begin PID_Init(PID_Struct,nr,500,false,Ks,MinOutput0,MinOutput,MaxOutput,SampleTime_ms,0,K,PID_Vector((K[iKp]*0.25),(K[iKi]*0.2),(K[iKd]*0.01)),PID_Vector(PID_twiddle_saveattol,PID_twiddle_tolerance,PID_twiddle_tolNOTsav)); end;
// http://rn-wissen.de/wiki/index.php/Regelungstechnik
function PID_Calc(var PID_Struct:PID_Struct_t; SetPoint,ProcessValue:PID_float_t; Stoersprung:boolean):PID_float_t;
// To be called at a regular time interval (e.g. every 100 msec)
// SetPoint: the target value for "ProcessValue" to be reached
// ProcessValue: the actual value measured in the system
// Stoersprung: SetPoint change, used to prevent twiddle adjust during SetPoint change
// Functionresult: PID function of (SetPoint-ProcessValue) of "PID_Struct",
// a positive value means "ProcessValue" is too low (< SetPoint), the process should take action to increase it
// a negative value means "ProcessValue" is too high (> SetPoint), the process should take action to decrease it
var _p,_i,_d:PID_float_t;
begin
with PID_Struct do
begin
clock_gettime(CLOCK_REALTIME, @PID_Time);
PID_dT := MicroSecondsBetween(PID_Time,PID_LastTime);
if PID_STimAdj and (not PID_FirstTime) and (PID_dT<>PID_LastdT) then PID_SetSampleTime(PID_Struct,PID_dT);
inc(PID_cnt);
PID_Error:= SetPoint - ProcessValue;
// calc p term
_p:= PID_K[iKp] * PID_Error;
// calc i term and limit integral windup
if PID_IntImprove and (Sign(PID_Error)<>Sign(PID_Integrated))
then PID_Integrated := PID_IntegratedWindupResetValue;
if Stoersprung
then PID_Integrated := PID_IntegratedWindupResetValue;
PID_Integrated := PID_Integrated + PID_Error;
_i := PID_K[iKi] * PID_Integrated;
PID_Limit(_i, PID_MinOutput0, PID_MinOutput, PID_MaxOutput);
// calc d term
_d := PID_K[iKd] * (PID_Error - PID_LastError);
if PID_DifImprove and (abs(PID_Error)<abs(PID_LastError)) then _d := 0.0;
PID_LastError := PID_Error;
PID_ControlOut := (_p + _i + _d);
// writeln(pid_cnt:2,' err: ',PID_Error:0:4,' res: ',PID_ControlOut:0:4,' p:',:_p:0:4,' i:',_i:0:4,' d:',_d:0:4);
PID_Limit(PID_ControlOut, PID_MinOutput0, PID_MinOutput, PID_MaxOutput);
with PID_Struct.PID_Twiddle do
begin
if twiddle_on and (not Stoersprung) then
begin // PID self tuning
inc(twiddle_iterations);
PID_UpdateError(PID_Struct.PID_Twiddle,PID_ControlOut);
if (twiddle_iterations>twiddle_intermax) then
begin
PID_TwiddleCalc(PID_Struct);
twiddle_iterations:=0;
end;
end;
end; // with
PID_SetPoint := SetPoint;
PID_ProcessValue := ProcessValue;
PID_LastTime := PID_Time;
PID_LastdT := PID_dT;
PID_FirstTime := false;
PID_Calc := PID_ControlOut;
end; // with
end;
function PID_Calc(var PID_Struct:PID_Struct_t; SetPoint,ProcessValue:PID_float_t):PID_float_t;
begin PID_Calc:=PID_Calc(PID_Struct,SetPoint,ProcessValue,false); end;
procedure PID_TestXX;
//just for demo purposes
//simulate PID. How the to be adjusted Value approaches a SetPoint value
const
Kp=0.15; Ki=0.1; Kd=0.1; // PID parameter
PID_Min=-25; PID_Max=+25; // MinOutput=-25; MaxOutput=+25
STim_msec=1000;
dm_c=13; scale_c=100; ntimes_c=10; errinduct=false;
PID_SetPoints_c:array[0..(dm_c-1)] of
PID_float_t = ( 0, 0.1, 0.2, 0.3, 0.5, 0.7, 0.8, 0.9, 1.1, 1.05, 1.01, 0.9, 0.95);
var loop,n:integer; pid1:PID_Struct_t; NewVal,SetPoint,delta:PID_float_t;
begin
RPI_HW_Start([InstSignalHandler]);
PID_Init(pid1,1,1,0,PID_Min,PID_Max,STim_msec,PID_twiddle_tolerance,PID_twiddle_saveattol,PID_Vector(Kp,Ki,Kd));
PID_SetIntImprove (pid1,true); PID_SetDifImprove(pid1,true); // enable improvements
NewVal:=0; loop:=0; n:=0;
writeln('PID_Test2 Kp:',Kp:0:2,' Ki:',Ki:0:2,' Kd:',Kd:0:2);
repeat
SetPoint:=PID_SetPoints_c[loop]*scale_c;
delta:=PID_Calc(pid1,SetPoint,NewVal,false);
{$warnings off} if errinduct then delta:=delta*random; {$warnings on}
writeln('PID_Test: SetPoint:',SetPoint:7:2,' NewVal:',NewVal:7:2,' delta:',delta:12:8);
NewVal:=NewVal+delta;
// action according to NewVal
sleep(STim_msec);
inc(n); if n>=ntimes_c then begin n:=0; inc(loop); if loop>=dm_c then loop:=0; end;
until terminateProg;
end;
procedure PID_Test;
//just for demo purposes
//simulate PID. How the to be adjusted Value approaches a SetPoint value
const
Kp=1.1; Ki=0.2; Kd=0.1; // PID parameter
PID_Min=-25; PID_Max=+25; // MinOutput=-25; MaxOutput=+25
STim_msec=1000;
dm_c=8; scale_c=47; ntimes_c=16; errinduct=false;
PID_SetPoints_c:array[0..(dm_c-1)] of PID_float_t = ( 1, 0, -1, 0, 2, 3, -1, 0 );
var loop,n:integer; pid1:PID_Struct_t; NewVal,SetPoint,delta:PID_float_t;
begin
RPI_HW_Start([InstSignalHandler]);
PID_Init(pid1,1,1,0,PID_Min,PID_Max,STim_msec,PID_twiddle_tolerance,PID_twiddle_saveattol,PID_Vector(Kp,Ki,Kd));
PID_SetIntImprove (pid1,true); PID_SetDifImprove(pid1,true); // enable improvements
NewVal:=0; loop:=0; n:=0;
writeln('PID_Test2 Kp:',Kp:0:2,' Ki:',Ki:0:2,' Kd:',Kd:0:2);
repeat
SetPoint:=PID_SetPoints_c[loop]*scale_c;
delta:=PID_Calc(pid1,SetPoint,NewVal,false);
{$warnings off} if errinduct then delta:=delta*random; {$warnings on}
writeln('PID_Test: SetPoint:',SetPoint:7:2,' NewVal:',NewVal:7:2,' delta:',delta:12:8);
NewVal:=NewVal+delta;
// action according to NewVal
sleep(STim_msec);
inc(n); if n>=ntimes_c then begin n:=0; inc(loop); if loop>=dm_c then loop:=0; end;
until terminateProg;
end;
function CL_Compose(cmdLine:string):string;
//inspired by Wolverrum
function _AddQuotes(str:string):string;
var sh:string;
begin
if Pos(' ',str)>0 then sh:=Format('"%s"',[str]) else sh:=str;
_AddQuotes:=sh;
end;
var i:longword; sh:string;
begin
sh:='';
if Length(cmdLine)=0 then
begin
for i:= 1 to ParamCount() do
begin
if sh='' then sh:=_AddQuotes(ParamStr(i))
else sh:=Format('%s %s',[sh,_AddQuotes(ParamStr(i))]);
end;
end else sh:=cmdLine;
CL_Compose:=sh;
end;
function CL_Parse(cmdLine:string):t_CLOptions; // Posix CommandLine Parser
// inspired by Wolverrum
const
_SpaceChars = [#$20,#$09,#$0D,#$0A];
_EqChars = [':','='];
_QChars = ['''','"'];
procedure _SkipSpace(var str:string; var i:longword);
begin while cmdLine[i] IN _SpaceChars do inc(i) end;
function _Getstring (var str:string; var i:longword):string;
var chPos:longword; sh:string;
begin
chPos:=i+1;
while (str[chPos]<>str[i]) AND (chPos<=Length(str)) do inc(chPos);
sh:=copy(str,i+1,chPos-i-1);
if str[i]<>str[chPos] then
Log_Writeln(Log_ERROR,Format('CL_Parse: string {%c}[[ %s ]]{%c} must be have quote on the end',[str[i],sh,str[chPos]]));
i:=chPos+1;
_Getstring:=sh;
end;
function _GetValue(var str:string; var i:longword):string;
var chPos:longword; sh:string;
begin
chPos:=i;
while (NOT (str[chpos] IN _SpaceChars)) AND (chPos<=Length(str)) do inc(chPos);
sh:=copy(str,i,chPos-i+1);
i:=chPos;
_GetValue:=sh;
end;
function _GetOptionName(var str:string; var i:longword):string;
var chBeg,chend:longword; sh:string;
begin
if str[i+1]='-' then chBeg:=i+2 else chBeg:=i+1;
chend:=chBeg;
while (NOT (str[chend] IN (_EqChars+_SpaceChars))) AND (chend<=Length(str)) do inc(chend);
sh:=copy(str,chBeg,chend-chBeg);
i:=chend;
_GetOptionName:=sh;
end;
function _GetOptionValue(var str:string; var i:longword):string;
var chPos:longword; sh:string;
begin
chPos:=i;
if str[i] IN _EqChars then
begin
chPos:=i+1;
if str[i+1] IN _QChars then sh:=_Getstring(str,chPos)
else sh:=_GetValue (str,chPos);
end;
i:=chPos;
_GetOptionValue:=sh;
end;
var i,pPos:longword; _CLO:t_CLOptions;
begin
pPos:=0; i:=1; SetLength(_CLO,0);
while i<Length(cmdLine) do
begin
_SkipSpace(cmdLine,i);
case cmdLine[i] OF
'''','"': begin
inc(pPos);
SetLength(_CLO,Length(_CLO)+1);
with _CLO[Length(_CLO)-1] do
begin
Name:= Format('%d',[pPos]);
Value:=_Getstring(cmdLine,i);
end;
end;
'-','/' : begin
SetLength(_CLO,Length(_CLO)+1);
with _CLO[Length(_CLO)-1] do
begin
Name:= _GetOptionName (cmdLine,i);
Value:=_GetOptionValue(cmdLine,i);
end;
end;
else begin
inc(pPos);
SetLength(_CLO,Length(_CLO)+1);
with _CLO[Length(_CLO)-1] do
begin
Name:= Format('%d',[pPos]);
Value:=_GetValue(cmdLine,i);
end;
end;
end; // case
end; // while
CL_Parse:=_CLO;
end;
function CL_OptGiven(var cl_opts:t_CLOptions; opt:string):integer;
// returns index. if index is >=0, then 'opt' was given
var idx,i:integer;
begin
idx:=-1; i:=1;
while (i<=Length(cl_opts)) do
begin
if (opt=cl_opts[i-1].Name) then begin idx:=i-1; i:=Length(cl_opts); end;
inc(i);
end; // while
CL_OptGiven:=idx;
end;
procedure CL_Test; // CommandLine Parser Test
var i:integer; opts:t_CLOptions; sh:string;
begin
sh:='-oabc -h --def="ijk lmno" eben abc -k "klm xyz" --help /? '; // simulates given commandline parameter
writeln(sh); writeln;
//writeln(CL_Compose(sh)); writeln;
opts:=CL_Parse(sh);
for i:= 1 to Length(opts) do
begin
writeln(i,'.:',opts[i-1].Name,'=',opts[i-1].Value);
end;
writeln;
i:=CL_OptGiven(opts,'def');
if i>=0 then writeln('given option "',opts[i].Name,'" with value "',opts[i].Value,'"');
writeln('is help option given?: ',(CL_OptGiven(opts,'help')>=0) or (CL_OptGiven(opts,'?')>=0));
end;
procedure RPI_hal_exit;
begin
//writeln('Exit unit RPI_hal+');
if (ExitCode<>0) then
begin
DUMP_CallStack('rpi_hal['+Num2Str(ExitCode,0)+']:');
if ExitCode=217 then LOG_Writeln(LOG_ERROR,'RPI_hal_exit: maybe RPI_hal was not initialized, check usage of RPI_HW_Start');
end;
if RPI_platform_ok then
begin
TRIG_End(-1);
ENC_End(-1);
SERVO_End(-1);
ERR_END(-1);
SPI_Bus_Close_All;
I2C_Close_All;
RPI_FW_close;
MMAP_end;
end;
//RPI_TempSaveLimits(RPI_Temps,ApplicationName);
//writeln('Exit unit RPI_hal-');
BIOS_EndIniFile;
RpiMaintCmd.free;
if (wdog.Hndl>=0) then
begin
// SAY(LOG_WARNING,'LNX_WDOG['+Num2Str(wdog.Hndl,0)+']: do not forget to close WDOG with LNX_WDOG(0) at end of your application');
LNX_WDOG(WDOG_Close); // DISABLE&close WDOG device
end;
if _OnExitShowRuntime then SAY(LOG_INFO,LOG_GetEndMsg(''));
MSG_HUB_ptr:=nil; CURL_ProgressUpdateHook_ptr:=nil; RPI_SignalHandlerHook_ptr:=nil;
LOG_LevelColor(false);
//say(log_info,'RPI_hal_exit-')
end;
procedure RPI_SignalHandlerErrExit(errno:longint);
begin
LOG_Writeln(LOG_ERROR,'RPI_SignalHandlerErrExit['+Num2Str(errno,0)+']: '+LNX_ErrDesc(errno));
end;
procedure RPI_SignalHandler(sig:cint); cdecl;
// example signal handler
begin
LOG_Writeln(LOG_ERROR,'RPI_SignalHandler: receiving signal: '+Num2Str(sig,0));
case sig of
SIGUSR1:begin // set errorlevel from external e.g. kill -USR1 <pid>
LOG_Level(LOG_INFO);
SAY_Level(LOG_INFO);
end;
SIGUSR2:begin // set errorlevel from external e.g. kill -USR2 <pid>
LOG_Level(LOG_WARNING);
SAY_Level(LOG_WARNING);
end;
SIGTERM,SIGINT,
SIGHUP: begin
terminateProg:=true;
end;
else begin
LOG_WRITELN(LOG_WARNING,'RPI_SignalHandler: unregistered signal ('+Num2Str(sig,0)+'), set variable terminateProg');
terminateProg:=true;
end;
end; // case
end;
procedure RPI_intSignalHandler(sig:cint); cdecl;
begin if (RPI_SignalHandlerHook_ptr<>nil) then RPI_SignalHandlerHook_ptr(sig); end;
function RPI_Init_Allowed:boolean;
var ok:boolean; i:longint;
begin
ok:=false;
for i:=1 to ParamCount do if Upper(ParamStr(i))='-RPIHAL=HWINIT' then ok:=true;
RPI_Init_Allowed:=ok;
end;
function RPI_HW_Start(initpart:s_initpart; p1,p2:string):boolean;
var ok,gpio_only:boolean; _flgtodo:s_initpart; sh:string; // j:t_initpart;
begin
ok:=true; _flgtodo:=initpart; RPI_HW_initpart:=initpart;
//for j IN initpart do SAY(LOG_WARNING,GetEnumName(TypeInfo(t_initpart),Ord(j)));
if (InitOnExitShowRuntime IN RPI_HW_initpart) then
begin
_flgtodo:=_flgtodo-[InitOnExitShowRuntime];
_OnExitShowRuntime:=true;
end;
if (InitCertServer IN RPI_HW_initpart) then
begin
_flgtodo:=_flgtodo-[InitCertServer];
if (not CertPack[CertPackServer].ok) then
begin // just start it, if not already started
ok:=LNX_CertStartPack(
CertPack[CertPackServer],
'ServerCert',
cert1_crtORpem_c,
cert1_key_c,
PrepFilePath(cert_crt_dir_c),
cert1_combined_c,
p2,CT_ssl );
if ok then
begin
if not CertPack[CertPackRPIMaint].ok then
CertPack[CertPackRPIMaint]:=CertPack[CertPackServer];
LNX_CertPackShow(LOG_INFO,CertPack[CertPackServer]);
end; // else LOG_Writeln(LOG_ERROR,'LNX_CertStartPack: can not start cert pack '+CertPack[CertPackServer].desc);
end;
end;
if (InitCertLetsEncrypt IN RPI_HW_initpart) then
begin
_flgtodo:=_flgtodo-[InitCertLetsEncrypt];
if (not CertPack[CertPackLetsEncrypt].ok) then
begin // just start it, if not already started, para = domain
if (p1<>'') then
begin
sh:=letsencryptdir_c+'/'+p1;
ok:=LNX_CertStartPack(
CertPack[CertPackLetsEncrypt],
'Lets Encrypt ('+p1+')',
PrepFilePath(sh+'/fullchain.pem'), // PrepFilePath(sh+'/cert.pem'),
PrepFilePath(sh+'/privkey.pem'),
PrepFilePath(sh+'/fullchain.pem'), // PrepFilePath(sh+'/chain.pem'),
PrepFilePath(sh+'/combined.pem'),
p2,CT_ssl );
if ok then
begin
if not CertPack[CertPackRPIMaint].ok then
CertPack[CertPackRPIMaint]:=CertPack[CertPackLetsEncrypt];
LNX_CertPackShow(LOG_INFO,CertPack[CertPackLetsEncrypt]);
end; // else LOG_Writeln(LOG_ERROR,'LNX_CertStartPack: can not start cert pack '+CertPack[CertPackLetsEncrypt].desc);
end else LOG_Writeln(LOG_ERROR,'LNX_CertStartPack: Lets Encrypt, missing domain name');
end;
end;
if (InitCertSnakeOil IN RPI_HW_initpart) then
begin
_flgtodo:=_flgtodo-[InitCertSnakeOil];
if (not CertPack[CertPackSnakeOil].ok) then
begin // just start it, if not already started
ok:=LNX_CertStartPack(
CertPack[CertPackSnakeOil],
'snakeoil (self signed)',
cert0_crtORpem_c,
cert0_key_c,
cert0_crtORpem_c, // PrepFilePath(cert_crt_dir_c),
cert0_combined_c,
p2,CT_ssl );
if ok then
begin
if not CertPack[CertPackRPIMaint].ok then
CertPack[CertPackRPIMaint]:=CertPack[CertPackSnakeOil];
LNX_CertPackShow(LOG_INFO,CertPack[CertPackSnakeOil]);
end else LOG_Writeln(LOG_ERROR,'LNX_CertStartPack: can not start cert pack '+CertPack[CertPackSnakeOil].desc);
end;
end;
if ok and (UPDAuthDBDateTime IN RPI_HW_initpart) then
begin
_flgtodo:=_flgtodo-[UPDAuthDBDateTime];
{$IFDEF UNIX}
LNX_UsrAuthModDateTime:=GetFileAge(LNX_ShadowFile);
{$ENDIF}
end;
if (InstSignalHandler IN RPI_HW_initpart) then
begin
_flgtodo:=_flgtodo-[InstSignalHandler];
{$IFDEF UNIX}
new(na); new(oa); terminateProg:=false;
na^.sa_Handler:=SigActionHandler(@RPI_intSignalHandler);
fillchar(na^.Sa_Mask,sizeof(na^.sa_mask),#0);
na^.Sa_Flags:=0;
{$ifdef Linux} // Linux specific
na^.Sa_Restorer:=nil;
{$endif}
if (fpSigAction(SIGALRM,na,oa)<>0) then RPI_SignalHandlerErrExit(fpgeterrno);
if (fpSigAction(SIGHUP, na,oa)<>0) then RPI_SignalHandlerErrExit(fpgeterrno);
if (fpSigAction(SIGTERM,na,oa)<>0) then RPI_SignalHandlerErrExit(fpgeterrno);
if (fpSigAction(SIGINT, na,oa)<>0) then RPI_SignalHandlerErrExit(fpgeterrno);
if (fpSigAction(SIGUSR1,na,oa)<>0) then RPI_SignalHandlerErrExit(fpgeterrno);
if (fpSigAction(SIGUSR2,na,oa)<>0) then RPI_SignalHandlerErrExit(fpgeterrno);
{$ENDIF}
end;
if (InitWDOG IN RPI_HW_initpart) or (InitWDOGnoThread IN RPI_HW_initpart) then
begin
{$IFDEF UNIX}
_flgtodo:=_flgtodo-[InitWDOG];
LNX_WDOG_Init(wdog);
if LNX_WDOG_Start then
begin
LNX_WDOG(WDOG_GSup); // WDIOC_GETSUPPORT
LNX_WDOG(WDOG_STO); // Set timeout to default (15 sec)
LNX_WDOG(WDOG_BSTAT); // Get last boot stat
if not (InitWDOGnoThread IN RPI_HW_initpart)
then Thread_Start(wdog.ThreadCtrl,@LNX_WDOG_Thread,nil,0,0)
else _flgtodo:=_flgtodo-[InitWDOGnoThread];
end else LOG_Writeln(LOG_ERROR,'WDOG: can not init');
{$ELSE}
_flgtodo:=_flgtodo-[InitWDOG,InitWDOGnoThread];
{$ENDIF}
end;
//rpi HW dependent:
if (_flgtodo<>[]) then
begin
ok:=RPI_platform_ok; gpio_only:=false;
(*if (InitGPIOonly IN RPI_HW_initpart) then
begin // not supported, does not work on rpi3
RPI_HW_initpart:=[InitGPIO]; gpio_only:=true;
if (StartShutDownWatcher IN initpart) then RPI_HW_initpart:=RPI_HW_initpart+[StartShutDownWatcher];
end; *)
if ok and (InitCreateScript IN RPI_HW_initpart) then
begin
_flgtodo:=_flgtodo-[InitCreateScript];
{$IFDEF UNIX}
GPIO_create_int_script(int_filn_c); // no need for it. Just for convenience
{$ENDIF}
end;
if ok and (InitRPIfw IN RPI_HW_initpart) then
begin
_flgtodo:=_flgtodo-[InitRPIfw];
RPI_FW_open;
if not RPI_FW_Info(TAG_STATUS_REQUEST,TAG_GET_FIRMWARE_REVISION,cpu_fw) then cpu_fw:='';
end;
if (InitI2C IN RPI_HW_initpart) or (InitSPI IN RPI_HW_initpart)
then RPI_HW_initpart:=RPI_HW_initpart+[InitGPIO]; // GPIO is mandatory
if ok and (InitGPIO IN RPI_HW_initpart) or
(StartShutDownWatcher IN RPI_HW_initpart) then
begin
_flgtodo:=_flgtodo-[InitGPIO];
ok:=(MMAP_start(gpio_only)=0);
end;
if ok and (InitI2C IN RPI_HW_initpart) then
begin
_flgtodo:=_flgtodo-[InitI2C];
ok:=(not restrict2gpio);
if ok then I2C_Start else Log_Writeln(Log_ERROR,'RPI_HW_Start: can not start I2C, try with sudo');
end;
if ok and (InitSPI IN RPI_HW_initpart) then
begin
_flgtodo:=_flgtodo-[InitSPI];
ok:=(not restrict2gpio);
if ok then SPI_Start else Log_Writeln(Log_ERROR,'RPI_HW_Start: can not start SPI, try with sudo');
end;
if ok and (StartShutDownWatcher IN RPI_HW_initpart) then
begin
_flgtodo:=_flgtodo-[StartShutDownWatcher];
ok:=RPI_ShutDownStart;
end;
if not ok then
begin
if not RPI_run_on_known_hw
then Log_Writeln(Log_ERROR,'RPI_hal: not running on supported rpi HW');
// else Log_Writeln(Log_ERROR,'RPI_hal: supported min-/maximum kernel #'+Num2Str(supminkrnl,0)+' - #'+Num2Str(supmaxkrnl,0)+' ( uname -a )');
if (InitHaltOnError IN RPI_HW_initpart) then
begin
_flgtodo:=_flgtodo-[InitHaltOnError];
LOG_Writeln(LOG_ERROR,'RPI_hal: can not initialize MemoryMap.');
// Halt(1);
end;
end;
end;
RPI_HW_Start:=ok;
end;
function RPI_HW_Start(initpart:s_initpart):boolean; begin RPI_HW_Start:=RPI_HW_Start(initpart,'',''); end;
function RPI_HW_Start:boolean;
begin
RPI_HW_Start:=RPI_HW_Start([InitHaltOnError,InitRPIfw,InitGPIO,InitI2C,InitSPI,UPDAuthDBDateTime]); // start all HW
end;
procedure inivar;
var i,j:integer; dt:TDateTime; sh:string;
begin
//i:=0; i:=4 div i;
RPI_ProgramStartTime:=now; _OnExitShowRuntime:=false;
try
call_external_prog(LOG_NONE,'uptime -s',sh); // 2019-07-03 09:05:57
if Str2DateTime(sh,'YYYY-MM-DD hh:mm:ss',dt) then RPI_BootTime:=dt;
except
RPI_BootTime:=RPI_ProgramStartTime;
end;
terminateProg:=false;
LNX_sudo(false);
MSG_HUB_ptr:=nil; CURL_ProgressUpdateHook_ptr:=nil;
RPI_SignalHandlerHook_ptr:=@RPI_SignalHandler;
rpi_fw_api.hndl:=-1; GPU_MEM_BASE:=0;
LOG_LevelColor(true);
LOG_Level(LOG_Warning); SAY_Level(LOG_INFO);
SD_speedRD:=noData_c;
with IP_Infos do
begin
idx:=0; init:=false; samesubnet:=false; hostname:='';
devlst:=''; hostapd_extdev:=ifeth_c;
IPInfo_Init(ifwlan_c, IP_Info[0]); IP_Info[0].alias:=ifwlan_c;
IPInfo_Init(ifeth_c, IP_Info[1]); IP_Info[1].alias:=ifeth_c;
IPInfo_Init(ifuap_c, IP_Info[2]); IP_Info[2].alias:=ifuap_c;
end; // with
with IniFileDesc do begin inifilename:=''; ok:=false; end;
BIOS_ReadIniFile(PrepFilePath(AppDataDir_c+'/'+ApplicationName+'/'+ApplicationName+'.ini'));
BIOS_SetDfltFlags([]);
BIOS_SetDfltSection(Upper(DfltSect_c));
LOG_Level(Str2LogLvl(BIOS_GetIniString('LOGERRLVL','WARNING')));
SAY_Level(Str2LogLvl(BIOS_GetIniString('LOGAPPLVL','INFO')));
//LOG_Level(LOG_Warning);
//SAY_Level(LOG_INFO);
BIOS_SetDfltSection(Upper(ApplicationName));
cpu_fw:='';
RpiMaintCmd:=TIniFile.Create('');
RPI_MaintSetVersions(0,0); // disable VersionCheck@RPI_Maint PKGInstall
RPI_TempInit(RPI_Temps);
//RPI_TempLoadLimits(RPI_Temps,ApplicationName);
SetUTCOffset; // set _TZlocal
mem_fd:=-1; mmap_arr:=nil; cpu_rev_num:=0; GPIO_map_idx:=2;
eeprom_SetAddr(eeprom_devadr_c);
for i:=0 to spi_max_bus do for j:=0 to spi_max_dev do spi_dev[i,j].spi_fd:=-1;
if not clock_getres(CLOCK_REALTIME,@rpi_timespecresolution)=0 then
begin
rpi_timespecresolution.tv_nsec:=1;
Log_Writeln(Log_ERROR,'Get_CPU_INFO_Init: can not get timeresolution');
end;
LNX_UsrAuthModDateTime:=0;
LNX_WDOG_Init(wdog);
for i:= CertPackRPIMaint to CertPackLast do LNX_CertInitPack(CertPack[i],i);
//LNX_CertInitPack(CertPackServer);
//LNX_CertInitPack(CertPackLetsEncrypt);
{$IFDEF WINDOWS} SDcard_root_hdl:=3; {$ELSE} SDcard_root_hdl:=AddDisk('/'); {$ENDIF}
end;
begin
//writeln('Enter unit rpi_hal');
AddExitProc(@RPI_hal_exit);
try
inivar;
Get_CPU_INFO_Init;
// RPI_ShutDownInit(-1); // just init data struct, no HW-Pin
IO_Init_Const;
RPI_HW_initpart:=[];
if RPI_Init_Allowed then RPI_HW_Start;
// writeln('Leave unit rpi_hal');
except
On E_rpi_hal_Exception :Exception do
DUMP_ExceptionCallStack('rpi_hal:',E_rpi_hal_Exception);
end;
end.
You can’t perform that action at this time.