LRA1-Basic

すべてが正しいとは限りません。
コードのみで何の説明もありませんが、何かの参考になれば・・・。
2022/11/18 f.izawa


■ 三菱 PLC FX3S 通信

'' FX3S 1C 形式4 チェックサム無し
'' FXシリーズ ユーザーズマニュアル[通信制御編] JY997D13301
'' 計算機リンク機能
'' 7.1 BRコマンド以降(275/710ページあたり)

Ubaud = 19200
While UInkey >= 0: Loop

'' ビット読み出し------------------
^ = Chr($05) '' ENQ
^ = ^; "00" '' 局番
^ = ^; "FF" '' PC番号(FXの場合、固定)
^ = ^; "BR" '' コマンド(ビット読み出し)
^ = ^; "0"  '' 伝文ウェイト(0~F = 0~150ms)
^ = ^; "X0000" '' 5桁で指示:X0000から
^ = ^; "10"  '' HEX 2 桁で指示:16個読み出し
Uprint ^
Delay 100
^ = ""

Do
	X = UInkey '' UART2 受信
	If X >= 0 Then ^ = ^;Chr(X)
	Else Exit EndIf
Loop
'' ex:res= STX(02)+ "00FF" + "0000000000000000" + ETX(03) + CR(13)LF(10)
Print ^[] '' 30
If ^[] = 8 + 16 Then
	For i = 0 To 15
		Print Chr(^[5 + i]);
	Next
	Print ""
EndIf

'' ビット書き込み------------------
'' ※ RUN 中はプログラムで操作しているビットは操作できない
^ = Chr($05) '' ENQ
^ = ^; "00" '' 局番
^ = ^; "FF" '' PC番号(FXの場合、固定)
^ = ^; "BW" '' コマンド(ビット書き込み)
^ = ^; "0"  '' 伝文ウェイト(0~F = 0~150ms)
^ = ^; "Y0000" '' 5桁で指示:Y0000から
^ = ^; "08"  '' HEX 2 桁で指示:8個書き込み
^ = ^; "11000000"  '' 出力:左から
Uprint ^
Delay 100

^ = ""
Do
	X = UInkey '' UART2 受信
	If X >= 0 Then ^ = ^;Chr(X)
	Else Exit EndIf
Loop
'' ex:res= ACK06)+ "00FF" + CR(13)LF(10)
Print ^[] '' 30
If ^[] = 7 Then
	For i = 0 To 6
		Print ^[i]
	Next
	Print ""
EndIf

■ Azbil CPL 通信

LRA1 ファームウェア 1.14c (2022/11/30) で、パリティの設定ができるようになりました。
アズビル SDC との通信は RS485 でなくスマートローダー経由で UART 通信ができます。
一時的な使用であれば問題無さそうです。
UBaud = 19202 //19200 bps + Even = 19202
'' SETUP C64 = 0 で CPL(デフォルト)
UBaud = 19200

Do
	While UInkey >= 0: Loop '' ←必要
	^ = Chr($02) '' STX
	^ = ^;"01" '' 機器アドレス
	^ = ^;"00" '' サブアドレス(未使用)
	^ = ^;"X"  '' デバイス区別コード
	'' 小数点位置 + PV + SP + OUT + MFB + AUT/MAN + マニュアル操作量(MV)
	^ = ^;"RU00";"1454";"3814";"3815";"3816";"3851";"3811";"3902"   '' コマンド
	^ = ^;Chr($03) '' ETX
	
	'' チェックサム
	S = 0
	For i = 0 To ^[] - 1: S = S + ^[i]: Next
	''Print "Sum=";S ''2002
	
	S = S & $FF '' 加算結果の下位1バイト
	S = (- S & $FF) '' 2の補数
	''Print "checkSum=";Form("X02", S) ''2E
	
	^ = ^;Form("X02", S) '' 送信文字列に追加
	UPrint ^ '' UART2 送信
	Delay 100

	^ = ""
	Do
		X = UInkey '' UART2 受信
		If X >= 0 Then ^ = ^;Chr(X)
		Else Exit EndIf
	Loop
	If ^[] = 9 + 4 * 8 Then
		For i = 0 To 7
			@[i] = ("$";Chr(^[8 + i * 4]);Chr(^[9 + i * 4]);Chr(^[10 + i * 4]);Chr(^[11 + i * 4]))
		Next
		Print "DIG = ";@[0]
		Print "PV  = ";Int16(@[1]) / Pow(10, @[0]);".";Abs(Int16(@[1]) % Pow(10, @[0]))
		Print "SP  = ";Int16(@[2]) / Pow(10, @[0]);".";Abs(Int16(@[2]) % Pow(10, @[0]))
		Print "OUT = ";Int16(@[3]) / Pow(10, @[0]);".";Abs(Int16(@[3]) % Pow(10, @[0]))
		Print "MFB = ";@[4] / Pow(10, @[0]);".";@[4] % Pow(10, @[0])
		Print "A/M = ";@[5]
		Print "MAN = ";Int16(@[6]) / Pow(10, @[0]);".";Abs(Int16(@[6]) % Pow(10, @[0]))
		Print "----"
	EndIf
	Delay 500
Loop
End

■ Modbus ASCII 通信

'' Azbil SDC15 RS485 type PV値取得
'' SETUP
'' C64: 0=CPL(デフォルト), 1=ASCII, 2=RTU
'' C65: 1~127= 機器アドレス
'' C66: 2=19200(デフォルト)
'' C67: 1=8 データビット(デフォルト)
'' C68: 0=偶数(デフォルト), 1=奇数, 2=無し
'' C69; 0=1 ストップビット(デフォルト)
'' C70: 3msec
'' C80: 2= データ受診時、SP値右下のドットを点滅

UBaud = 19200

Do
	While UInkey >= 0: Loop '' ←必要
	
	'' 機器アドレス
	^="01"
	'' 読み出しコマンド
	^= ^;"03" 
	'' 読み出し先頭アドレス $238D = PV
	^=^;"238D"
	'' 読み出し数 $0001
	^=^;"0001"
	
	'' ChechSum 計算
	S = 0
	For i = 0 To ^[] / 2
		S = S + ("$";Chr(^[i*2]);Chr(^[i*2+1]))
	Next
	S = S & $FF ''加算結果の下位1バイト
	S = (- S & $FF) ''2の補数
	^ = ^;Form("X02",S)'' それを 16進表記に変換して追加
	^ = ^;Chr($0D);Chr($0A)'' CR LF を追加
	^ = ":";^ '' 電文文字を先頭に追加 
	Print "send=";^
	UPrint ^ '' 送信
	
	^ = UGets(100) '' 受信
	Print "recv=";^ '':0103021130B9
	If ^[3] = $30 && ^[4] = $33 Then '' 読み出しコマンド"03" であるか
		A = ("$";Chr(^[7]);Chr(^[8]);Chr(^[9]);Chr(^[10])) '' PV
		Print A '' 4400 = PV値(小数点位置は別に取得する必要がある)
	EndIf
	Delay 500
Loop

■ Modbus RTU 通信

'' Azbil SDC15 RS485 type PV,SP,MV値取得 + SP変更
'' SETUP
'' C64: 2: 0=CPL(デフォルト), 1=ASCII, 2=RTU
'' C65: 1: 1~127= 機器アドレス
'' C66: 2=19200(デフォルト)
'' C67: 1=8 データビット(デフォルト)
'' C68: 2: 0=偶数(デフォルト), 1=奇数, 2=無し
'' C69; 0=1 ストップビット(デフォルト)
'' C70: 3msec
'' C80: 2=データ受信時、SP値右下のドットを点滅

UBaud = 19200

Do
	While UInkey >= 0: Loop '' ←必要
	
	'' 機器アドレス
	^ = Chr($01)
	'' 読み出しコマンド
	^ = ^;Chr($03)
	'' 読み出し先頭アドレス $238D = PV, $238E = SP, $2391 = MV
	^ = ^;Chr($23);Chr($8D)
	'' 読み出し数 $0004
	^ = ^;Chr($00);Chr($05)
	
	'' CRC 追加
	GoSub _AddCRC

	UPrint ^; '' ←;重要 CrLf 無し
	Delay 30
	
	^ = ""
	Do
		X = UInkey
		If X >= 0 Then ^ = ^;Chr(X)
		Else Exit EndIf
	Loop
	'' ex: res= 01 03 02 11 30 B4 00 (3番目の 02 はデータ数?2、最後の2つはエラーチェック)
	If ^[] >= 4 && ^[1] = $03 Then
		P = Int16(^[3] << 8 | ^[4])
		Print "PV = ";P
		S = Int16(^[5] << 8 | ^[6])
		Print "SP = ";S
		M = Int16(^[11] << 8 | ^[12])
		Print "MV = ";M
	EndIf
	
	'' 機器アドレス
	^ = Chr($01)
	'' 書き込みコマンド
	^ = ^;Chr($10)
	'' 書き込み先頭アドレス $238E = SP
	^ = ^;Chr($23);Chr($8E)
	'' 書き込み数 $0001
	^ = ^;Chr($00);Chr($01)
	'' 書き込み数 の2倍
	^ = ^;Chr($02)
	S = 3999 '' SP
	'' 書き込みデータ
	^ = ^;Chr(S >> 8 & $FF);Chr(S & $FF)
	
	'' CRC 追加
	GoSub _AddCRC

	UPrint ^; '' ←;重要 CrLf 無し

	Delay 500
Loop
End

_AddCRC
	'' CRC16 計算 @[10..13] を使用
	'' Azbil CP-SP-1148 デジタル指示調節計 型C15 取扱説明書 詳細編
	'' Modbus/RTU 「チェックサム(CRC)の作成方法」より
	@[10] = $FFFF
	For @[13] = 0 To ^[] - 1
		@[10] = @[10] ^ ^[@[13]]
		For @[12] = 0 To 7
			@[11] = @[10] & $0001
			@[10] = @[10] >> 1
			If @[11] = 1 Then @[10] = @[10] ^ $A001 EndIf
		Next
	Next
	@[13] = (@[10] >> 8) & $00FF
	@[10] = @[10] << 8
	@[10] = (@[10] | @[13]) & $FFFF
	''Print "crc = ";Form("X",@[10])
	'' 最後に CRC を追加
	^ = ^;Chr(@[10] >> 8);Chr(@[10] & $00FF)
Return

■ クランプ式交流電流センサー

'' SCT 013-010 + ADC ADS1115 による交流電流の超簡易測定
'' SCT 013-010 は、AC 0~10A に対して AC 0~1V が出力されるクランプ式電流センサー
'' サンプリング数は、Arduino :1サイクル 200回程度に対して LRA1 + ADS1115 : 17回程度と、かなりの差がある
'' 手軽に交流電流を測定できる

I2cD[31] = $48 '' ADS1115 I2C_ADDRS (ADDR-GND:$48, ADDR-VDD:$49, ADDR-SDA:$4A, ADDR-SCL:$4B)

'' 上位 8 ビット = OS[1bit] + MUX[3bit] + PGA[3bit] + MODE[1bit]
''   OS   [1bit] = 1  ' 変換する
''   入力ピン設定
''   MUX  [3bit] = 0 (差動入力: 0 = AN0-AN1, 1 = AN0-AN3, 2 = AN1-AN3, 3 = AN2-AN3)
''                 (シングル入力: 4 = AN0-GND, 5 = AN1-GND, 6 = AN2-GND, 7 = AN3-GND)
''   測定レンジ設定
''   PGA  [3bit] = 0 (0 = +/-6.144, 1 = +/-4.096, 2 = +/-2.048, 3 = +/-1.024, 4 = +/-0.512, 5 = +/-0.256 V)
I2cD[2] = 2 '' 0~2V = 0~2000
'' ADS1115(16bit)
'' 0.1875    mV/bit +/-6.144 V
'' 0.125     mV/bit +/-4.096 V
'' 0.0625    mV/bit +/-2.048 V
'' 0.03125   mV/bit +/-1.024 V
'' 0.015625  mV/bit +/-0.512 V
'' 0.0078125 mV/bit +/-0.256 V

''   変換モード設定
''   MODE [1bit] = 0 (0 = 連続変換, 1 = シングルショット)

'' 下位 8 ビット = DR[3bit] + COMP[5bit]
''   データレート(samples per second)設定
''   DR   [3bit] = 7 ''(ADS1115: 0 = 8, 1 = 16, 2 = 32, 3 = 64, 4 = 128, 5 = 250, 6 = 475, 7 = 860 SPS)
''   COMP [5bit] = 3 (デフォルト値そのまま)

'' 差動入力: 0 = AN0-AN1, +/-2.048V, 連続変換
I2cd[0] = 1 << 7 | 0 << 4 | I2cD[2] << 1 |  0  '' OS[1bit] + MUX[3bit] + PGA[3bit] + MODE[1bit]
'' 860 SPS
I2cd[1] = 7 << 5 | 3                          '' DR[3bit] + COMP[5bit]
I2cW I2cD[31], 1, 2 '' 設定レジスタに書き込み
Delay 20

Do
	S = 0
	Tick = 0
	For j = 0 To 1
		For i = 0 To 255
			I2cR I2cD[31], 0, 2 '' 変換レジスタを読み込み
			@[i] = I2cD[0] << 8 | I2cD[1]
		Next
		For i = 0 To 255
			A = Int16(@[i]) * 625 / 10000 '' 625 = 0.0625 mV/bit
			S = S + A * A
		Next
	Next
	Print "Tick= "; Tick '' 796 msec
	U = Sqrt(S / 512) '' 二乗平均平方根値(取りこぼしがあるため実効値より小さくなる)
	Print "RMS = ";U;" mA"
Loop
End

■ GPS

Ubaud=9600
Do
	UGps 10000,14 '' タイムアウト=10秒、HDOP(水平精度低下率)閾値= 1.4
	@[0] = @[0] + 9 * 60 * 60
	Print "N : "; @[1] / 1000000; "."; @[1] % 1000000 '' 緯度
	Print "E : "; @[2] / 1000000; "."; @[2] % 1000000 '' 経度
	Print "H : "; @[3] / 10; "."; @[3] % 10; "m" '' 標高
	Print "HDOP : ";@[4]/10; "."; @[4] % 10 '' 水平精度低下率	
	DateTime @[0], 10
	Print @[10];"/";Form("02",@[11]);"/";Form("02",@[12]);" ";Form("02",@[13]);":";Form("02",@[14]);":";Form("02",@[15])	
Loop
End

■ GPS リアルタイムクロック (自前で計算)

Ubaud=9600
Do
	^ = UGets(200)
	Print ^
	If ^[0] = $24 && ^[4] = $4D && ^[5] = $43 && ^[] > 13 Then
		I2cD[13] = (Chr(^[7]);Chr(^[8]))'' H
		I2cD[14] = (Chr(^[9]);Chr(^[10])) '' M"
		I2cD[15] = (Chr(^[11]);Chr(^[12])) '' S
		m = 0		
		For i = 13 To ^[] -1
			If i + 6 < ^[] Then
				If ^[i] = $2C Then m = m + 1 EndIf
				If m = 8 && ^[] > i + 6 Then
					I2cD[12] = (Chr(^[i + 1]);Chr(^[i + 2])) '' D
					I2cD[11] = (Chr(^[i + 3]);Chr(^[i + 4])) '' M
					I2cD[10] = (Chr(^[i + 5]);Chr(^[i + 6])) '' Y
					Exit
				EndIf
			Else
				Exit
			EndIf
		Next
		Print I2cD[10];"/";Form("02",I2cD[11]);"/";Form("02",I2cD[12]);" ";Form("02",I2cD[13]);":";Form("02",I2cD[14]);":";Form("02",I2cD[15])
		Gosub _UTCtoJST
		Print I2cD[10];"/";Form("02",I2cD[11]);"/";Form("02",I2cD[12]);" ";Form("02",I2cD[13]);":";Form("02",I2cD[14]);":";Form("02",I2cD[15])
	EndIf
Loop
End

_UTCtoJST
	I2cD[23] = I2cD[13] + 9 '' H
	I2cD[22] = I2cD[12] '' D
	I2cD[21] = I2cD[11] '' M
	I2cD[20] = I2cD[10] '' Y
	'' その月の日数
	If I2cD[11] = 2 Then
		If I2cD[10] % 4 = 0 Then I2cD[24] = 29 ''閏年
		Else I2cD[24] = 28 EndIf
	ElseIf I2cD[11] = 4 || I2cD[11] = 6 || I2cD[11] = 9 || I2cD[11] = 11 Then
		I2cD[24] = 30
	Else I2cD[24] = 31 EndIf
	If I2cD[23] > 24 Then ''H
		I2cD[23] = I2cD[23] - 24
		I2cD[22] = I2cD[22] + 1 ''D
		If I2cD[22] > I2cD[24] Then
			I2cD[22] = I2cD[22] - I2cD[24]
			I2cD[21] = I2cD[21] + 1 ''M
			If I2cD[21] > 12 Then
				I2cD[21] = I2cD[21] - 12
				I2cD[20] = I2cD[20] + 1 ''Y
			EndIf
		EndIf
	EndIf
	I2cD[13] = I2cD[23] '' H
	I2cD[12] = I2cD[22] '' D
	I2cD[11] = I2cD[21] '' M
	I2cD[10] = I2cD[20] '' Y 
Return

■ RTC DS3231 リアルタイムクロック

Ubaud=9600
Do
	^ = UGets(200)
	Print ^
	If ^[0] = $24 && ^[4] = $4D && ^[5] = $43 && ^[] > 13 Then
		I2cD[13] = (Chr(^[7]);Chr(^[8]))'' H
		I2cD[14] = (Chr(^[9]);Chr(^[10])) '' M"
		I2cD[15] = (Chr(^[11]);Chr(^[12])) '' S
		m = 0		
		For i = 13 To ^[] -1
			If i + 6 < ^[] Then
				If ^[i] = $2C Then m = m + 1 EndIf
				If m = 8 && ^[] > i + 6 Then
					I2cD[12] = (Chr(^[i + 1]);Chr(^[i + 2])) '' D
					I2cD[11] = (Chr(^[i + 3]);Chr(^[i + 4])) '' M
					I2cD[10] = (Chr(^[i + 5]);Chr(^[i + 6])) '' Y
					Exit
				EndIf
			Else
				Exit
			EndIf
		Next
		Print I2cD[10];"/";Form("02",I2cD[11]);"/";Form("02",I2cD[12]);" ";Form("02",I2cD[13]);":";Form("02",I2cD[14]);":";Form("02",I2cD[15])
		Gosub _UTCtoJST
		Print I2cD[10];"/";Form("02",I2cD[11]);"/";Form("02",I2cD[12]);" ";Form("02",I2cD[13]);":";Form("02",I2cD[14]);":";Form("02",I2cD[15])
	EndIf
Loop
End

_UTCtoJST
	I2cD[23] = I2cD[13] + 9 '' H
	I2cD[22] = I2cD[12] '' D
	I2cD[21] = I2cD[11] '' M
	I2cD[20] = I2cD[10] '' Y
	'' その月の日数
	If I2cD[11] = 2 Then
		If I2cD[10] % 4 = 0 Then I2cD[24] = 29 ''閏年
		Else I2cD[24] = 28 EndIf
	ElseIf I2cD[11] = 4 || I2cD[11] = 6 || I2cD[11] = 9 || I2cD[11] = 11 Then
		I2cD[24] = 30
	Else I2cD[24] = 31 EndIf
	If I2cD[23] > 24 Then ''H
		I2cD[23] = I2cD[23] - 24
		I2cD[22] = I2cD[22] + 1 ''D
		If I2cD[22] > I2cD[24] Then
			I2cD[22] = I2cD[22] - I2cD[24]
			I2cD[21] = I2cD[21] + 1 ''M
			If I2cD[21] > 12 Then
				I2cD[21] = I2cD[21] - 12
				I2cD[20] = I2cD[20] + 1 ''Y
			EndIf
		EndIf
	EndIf
	I2cD[13] = I2cD[23] '' H
	I2cD[12] = I2cD[22] '' D
	I2cD[11] = I2cD[21] '' M
	I2cD[10] = I2cD[20] '' Y 
Return

■ INA226 直流電流、電圧測定

I2cD[31] = $40 '' INA226PRC I2C_ADDRS

'' Read Configuration Register
'' デフォルトで、平均化なし、サンプリング周期 1.1msec、電圧/電流を交互に計測
I2cR I2cD[31], 0, 2
Print Form("X02", I2cD[0]); Form("X02", I2cD[1]) '' $4127

'' Write Caribration Register(シャント抵抗の補正値)
'' 1.0 倍= 2048, 
'' シャント抵抗2mオームの時、1.25 倍 = 2048 x 1.25 = 2560($0A00) で電流値を直読できる
'' I2cD[0] = $0A:I2cD[1] = $00
'' シャント抵抗25mオームの時は、1.0 倍 = 2048($0800)
I2cD[0] = $08:I2cD[1] = $00
I2cW I2cD[31], 5, 2
Do
	'' Read Bus Voltage
	I2cR I2cD[31], 2, 2
	v = I2cD[0] << 8 | I2cd[1]
	Print v * 125 / 100 ; " mV" ' 1.25mV/bit

	'' Read Current
	I2cR I2cD[31], 4, 2
	i = I2cD[0] << 8 | I2cd[1]
	Print Int16(i) ; " x 0.1mA" ' 0.1mA/bit(レジスタ 5 での補正後の値) 
	'' Read Power
	I2cR I2cD[31], 3, 2
	p = I2cD[0] << 8 | I2cD[1]
	Print p * 25/10 " mW" ' mW	
	Delay 500
Loop
End

■ ADS1115 16 ビット アナログデジタルコンバータ

I2cD[31] = $48 '' ADS1115 I2C_ADDRS (ADDR-GND:$48, ADDR-VDD:$49, ADDR-SDA:$4A, ADDR-SCL:$4B)

'I2cR I2cD[31], 0, 2 '' 設定レジスタを読み込み
'Print Form("X02", I2cD[0]) '' 85 上位
'Print Form("X02", I2cD[1]) '' 83 下位
'' 設定レジスタのデフォルト値は、上位 8bit= $85, 下位 8bit = $83

'' 上位 8 ビット = OS[1bit] + MUX[3bit] + PGA[3bit] + MODE[1bit]
''   OS   [1bit] = 1  ' 変換する
''   入力ピン設定
''   MUX  [3bit] = 0 (差動入力: 0 = AN0-AN1, 1 = AN0-AN3, 2 = AN1-AN3, 3 = AN2-AN3)
''                 (シングル入力: 4 = AN0-GND, 5 = AN1-GND, 6 = AN2-GND, 7 = AN3-GND)
''   測定レンジ設定
''   PGA  [3bit] = 0 (0 = +-6.144, 1 = +-4.096, 2 = +-2.048, 3 = +-1.024, 4 = +-0.512, 5 = +-0.256 V)
I2cD[2] = 1
'' ADS1115(16bit)
@[0] = 1875   ' 0.1875    mV/bit
@[1] = 1250   ' 0.125     mV/bit
@[2] = 625    ' 0.0625    mV/bit
@[3] = 3125   ' 0.03125   mV/bit
@[4] = 15625  ' 0.015625  mV/bit
@[5] = 78125  ' 0.0078125 mV/bit

'' ADS1015(12bit)
'@[0] = 3000 ' 3.0   mV/bit
'@[1] = 2000 ' 2.0   mV/bit
'@[2] = 1000 ' 1.0   mV/bit
'@[3] = 0500 ' 0.5   mV/bit
'@[4] = 0250 ' 0.25  mV/bit
'@[5] = 0125 ' 0.125 mV/bit

''   変換モード設定
''   MODE [1bit] = 0 (0 = 連続変換, 1 = シングルショット)

'' 下位 8 ビット = DR[3bit] + COMP[5bit]
''   データレート(samples per second)設定
''   DR   [3bit] = 4 (ADS1015: 0 = 128, 1 = 250, 2 = 490, 3 = 920, 4 = 1600, 5 = 2400, 6 = 3300 SPS)
''                   (ADS1115: 0 = 8, 1 = 16, 2 = 32, 3 = 64, 4 = 128, 5 = 250, 6 = 475, 7 = 860 SPS)
''   COMP [5bit] = 3 (デフォルト値そのまま)
Do
	I2cd[0] = 1 << 7 | 0 << 4 | I2cD[2] << 1 | 0  '' OS[1bit] + MUX[3bit] + PGA[3bit] + MODE[1bit]
	I2cd[1] = 4 << 5 | 3                          '' DR[3bit] + COMP[5bit]
	I2cW I2cD[31], 1, 2 '' 設定レジスタに書き込み
	Delay 20
	
	I2cR I2cD[31], 0, 2 '' 変換レジスタを読み込み
	@[6] = I2cD[0] << 8 | I2cD[1]
	'' If @[6] > $8000 Then @[6] = @[6] - $10000 EndIf
	@[6] = Int16(@[6])
	
	Print "AN0-AN1 = ";@[6] * @[I2cD[2]] / 10000
	
	I2cd[0] = 1 << 7 | 3 << 4 | I2cD[2] << 1 | 0  '' OS[1bit] + MUX[3bit] + PGA[3bit] + MODE[1bit]
	I2cd[1] = 4 << 5 | 3                          '' DR[3bit] + COMP[5bit]
	I2cW I2cD[31], 1, 2 '' 設定レジスタに書き込み
	Delay 20
	
	I2cR I2cD[31], 0, 2 '' 変換レジスタを読み込み
	@[6] = ((I2cD[0] + $80) % $100 - $80) * $100 + I2cD[1]
	Print "AN2-AN3 = ";@[6] * @[I2cD[2]] / 10000
	
	Delay 500
Loop
End

■ I/O エキスパンダ PCF8575 双方向リモート I/O
 


 お互いに適当なタイミングで送受信しているため、受信のタイミングが合わないことがあります。
 受信のタイムアウト時間は短く設定したほうが違和感が少なくなります。(2022/11/19)
 「電波法上の制限により繰り返してデータを送信する場合、最低でも以下の間隔が必要です。
  これよりも早い間隔でデータを送った場合、データは内部で捨てられます。
  下側バンド:(各モードの送信時間[ms]+休止時間 50[ms]+10~20[ms])」
 と、リファレンスにあるので何回かに1回は送信されない可能性があります。(2022/11/21)

'' 0.128 秒(SF=7,  BW=7 125kHz) 周期(RX = 69 msec, TX=61 msec)
'' 0.627 秒(SF=10, BW=7 125kHz), 0.332 秒(SF=10, BW=8 250kHz) 周期
'' デフォルト値:CR = 1(4/5), SF=10 BW=7(125kHz)
'' MODEM=0 (FSK) で 64 msec 周期 ただしRead Timeout 値の調整が必要

'' SF=7,  BW=7
'' 出力はアクティブロー

I2cD[31] = $20 '' I2C_ADDR

Do
	Tick = 0
	I2cR I2cD[31],-1, 2 '' PCF8575 read
	'' スイッチ入力下位 4 ビット = P00 ~ P03
	I2cD[2] = ~I2cD[0] & $0F '' I2cD[2] を汎用変数として使用
	
	I2cD[0] = $FF '' 入力を H に戻す
	I2cD[1] = $00 '' 出力を初期化
	F = 0
	N = 0
	For i= 0 To 0 '' リトライしない方が違和感が少ない
		Recv 100 '' ←実際の受信時間により調整 MODEM=0(FSK 64) / MODEM=1(LoRa 2000~100)
	
		If (Stat = 10) && (Rxd[7] >= 3) && (Rxd[8] = $24) Then '' 受信バイト数 >= 3 で $24("$") で始まる
			'' 受信データ(ex :"$13")を数値に変換
			I2cD[1] = (Chr(Rxd[8]);Chr(Rxd[9]);Chr(Rxd[10]))
'			Print "recv $";Form("X02",I2cD[1])
			'' 送信用データ = 上位 4bit アンサーバック(相手先のスイッチ入力) + 下位 4bit 自機のスイッチ入力
			I2cD[2] = I2cD[2] | ((I2cD[1] & $0F) << 4)
			'' Status LED 点灯
			I2cD[0] = $7F '' P07 を L に
			F = 1
			Exit
		Else
			N = N + 1
'			Print "Stat ";Stat
		EndIf
		''N = N +1
	Next
	If N > 0 Then Print N EndIf
	If F = 0 Then Print "Timeout Error "; N EndIf
	I2cD[1] = ~I2cD[1] & $FF '' P10 ~ P17
	I2cW I2cD[31], -1, 2 '' PCF8575 write

'	Print "send "; "$";Form("X02", I2cD[2]) ' 上位 4 bit はアンサーバック
	'Tick = 0
	Send "$";Form("X02", I2cD[2])
	'Print "Tick ";Tick
	If Stat >0 Then Print Stat EndIf

	Delay 10 '' ←要調整
Loop
End

■ I/O エキスパンダ PCAL9555APW を使う

'' PCAL9555APW I2C GPIOエクスパンダ
'' https://www.switch-science.com/products/2352

I2cD[31] = $20 '' PCAL9555APW I2C_ADDRS

'' I/O 設定
'' Configration registor ($06, $07)
I2cD[0] = $FF '' port0 = 入力(ハイインピーダンス)
I2cD[1] = $00 '' port1 = 出力
I2cW I2cD[31], $06, 2

'' プルアップ設定
'' Pull-up/pull-down enable($46,$47) 
I2cD[0] = $FF '' プルアップ	、プルダウンを有効に
I2cW I2cD[31], $46, 1
'' Pull-up / pull-down selection ($48,$49)
I2cD[0] = $FF '' 入力をプルアップ
I2cW I2cD[31], $48, 1
'' 出力設定
'' Output port configuration($4E, $4F)
I2cD[0] = $FF '' オープンドレイン
I2cW I2cD[31], $4F, 1

'' 反転設定
I2cD[0] = $FF '' port0 入力を反転
I2cW I2cD[31], $04, 1

Do
	For i = 0 To 7
		'' Read Input
		I2cR I2cD[31], $00, 1
		'' Read Output
		'' 反転設定済なのでそのまま表示
		Print "in : ";I2cD[0]; ", out : ";1 << i
		'' Write output
		'' Port1 0 ~ 7 bit を順に ON(Low)に
		I2cD[0] = ~(1 << i)
		I2cW I2cD[31], $03, 1
		''
		Delay 200
	Next	
Loop
End

■ I/O エキスパンダ PCF8575 を使う

'' uses ^, X, Y

'' PCF8575 I2C アドレス(I2cD[31] を汎用 Byte 変数として使用)
I2cD[31] = $20 ''PCF8575 ADDR $20 .. $27 

'' 電源投入後は、すべてのI/O は HIGH(プルアップ)で、すべてを入力として使用できる
'' LOW に設定すると、出力になる

'' Amazon で販売されている PCF8575 モジュールで LRA1 評価ボードの 3V3 を VCC として使う場合、
'' 基板裏面の VCC - VDD をハンダでショートすると VDD が 3.3V になる
'' ショートしない場合は AVR が入るため、VDD は 2.3V程度 

'' I/O 設定
I2cd[0] = $FF' P00 ..P07 = INPUT(PULLUP) x 8
I2cd[1] = $00' P10 ..P17 = OUTPUT x 8
I2cW I2cD[31],-1, 2

Do
	'read
	I2cR I2cD[31],-1, 2
	'' I2cD[2] :入力の反転値を保持
	I2cD[2] = $FF - I2cD[0]
	'' 1, 2, 4, 8, 16, 32, 64, 128 の合計値
	Print "IN=";I2cD[2];" OUT=";I2cD[1]
	
	^ = ""
	For X = 7 To 0 Step - 1 
		Y = (I2cD[2] >> X) & 1
		^ = ^;Y
		Gosub _OutpPCF '' テストとして入力をそのまま出力	
	Next
	'' 右が下位のビット表示 "00000000" 
	Print ^
Loop
End

_OutpPCF ''X = pinNo(0 ..8) Y = 1 or 0
	I2cD[0] = $FF ''入力を H に戻す
	If Y = 1 Then I2cD[1] = I2cD[1] | (1 << X)
	Else I2cD[1] = I2cD[1] & ~(1 << X)
	EndIf 
	I2cW I2cD[31], -1, 2		
Return

■ OLED を使う

 

'' https://akizukidenshi.com/catalog/g/gP-08276/
'' https://akizukidenshi.com/download/ds/akizuki/so1602awgb-uc-wb-u_akizuki_manu.pdf

'' uses: ^, A, B, I, K, N

I2cD[31]=$3C
Gosub _InitOled
Gosub _ContrastMax
Gosub _SetMyChars

Do
	Recv 2000
	Lclr
	Gosub _ClearOled
	If Stat = 10 Then
		Lprint Rxd
		Lpos = 64: Lprint "rssi"; Rssi
		If Rxd[7] > 10 Then
			i = 0: n = 0: ^ = "": A = $20: B = $20
			For i = 0 To Rxd[7] - 1
				If Rxd[i + 8] = $3A Then
					n = n + 1
					If n = 1 Then
						^ = ^; Chr($F2); "C ": Gosub _DrawText
					ElseIf n = 2 Then
						^ = ^; "% ": Gosub _DrawText
					ElseIf n = 3 Then
						^ = ^; Chr($F2);"CDP ": Gosub _DrawText2
					ElseIf n = 4 Then
						I2cD(0) = ^[0]: Gosub _WriteData
					EndIf
					^ = ""
				Else
					^ = ^; Chr(Rxd[i + 8])
				EndIf
			Next
			I2cD(0) = ^[0]: Gosub _WriteData
		Else
			^ = "HC2": Gosub _DrawText1
			^ = "no response": Gosub _DrawText2
		EndIf
		^ = Rssi
		I2cD[1] = 12: I2cD[2] = 1: Gosub _SetCur: Gosub _DrawText
		
		I2cD[1] = 13: I2cD[2] = 0: Gosub _SetCur
		If Rssi < -130 Then I2cD[1] = 0
		Else I2cD[1] = 9 - Abs(Rssi) / 15
		EndIf
		Gosub _DrawAntLevel		
	Else
		Lpos = 64: Lprint "error "; Stat
		I2cD[1] = 14: I2cD[2] = 0: Gosub _SetCur
		I2cD[1] = 0: Gosub _DrawAntLevel
	EndIf
	Send "OK"
	Delay 500
Loop
End

'' カーソル位置
_SetCur
	'' I2cD[1]=Col,I2cD[2]=Row [0 base]
	I2cD[0] = $80 + I2cD[2] * $20  + I2cD[1]
	Gosub _WriteCommand
Return

_DrawText
	For k = 0 To ^[]-1 
		I2cD[0] = ^[k]: Gosub _WriteData
	Next
Return

_DrawText1
	I2cD[0] = $80: Gosub _WriteCommand
	For k = 0 To ^[] - 1 
		I2cD[0] = ^[k]: Gosub _WriteData
	Next	
Return

_DrawText2
	I2cD[0] = $80 + $20: Gosub _WriteCommand
	For k = 0 To ^[] - 1 
		I2cD[0] = ^[k]: Gosub _WriteData
	Next	
Return

_ClearOled
	I2cD[0] = $01: Gosub _WriteCommand
Return

_WriteCommand
	I2cW I2cD[31],$00,1: Delay 10
Return

_WriteData
	I2cW I2cD[31],$40, 1: Delay 1
Return

_InitOled
	'' Clear Display
	I2cD[0] = $01: Gosub _WriteCommand
	'' Return Home
	I2cD[0] = $02: Gosub _WriteCommand
	'' Display ON + Cursor ON + Blink ON [8+4+2+1=16=$0F]
	'I2cD[0] = $0F
	'' Display ON + Cursor OFF + Blink OFF [8+4+0+0=12=$0C]
	I2cD[0] = $0C: Gosub _WriteCommand
	'' Clear Display
	I2cD[0] = $01: Gosub _WriteCommand
Return

_ContrastMax
	I2cD[0] = $2A: Gosub _WriteCommand ''RE=1
	I2cD[0] = $79: Gosub _WriteCommand ''SD=1
	I2cD[0] = $81: Gosub _WriteCommand ''コントラストセット
	I2cD[0] = $FF: Gosub _WriteCommand ''輝度MAX ''標準=$7F
	I2cD[0] = $78: Gosub _WriteCommand ''SD を0にもどす
	I2cD[0] = $28: Gosub _WriteCommand ''2C=高文字 28=ノーマル 
Return

_DrawAntLevel
	If I2cD[1] = 0 Then: I2cD[0] = 0: Gosub _WriteData
	ElseIf I2cD[1] =  1 Then I2cD[0] = 1: Gosub _WriteData
	Elseif I2cD[1] >= 2 Then I2cD[0] = 2: Gosub _WriteData
		If I2cD[1] =  3 Then I2cD[0] = 3: Gosub _WriteData
		ElseIf I2cD[1] >= 4 Then I2cD[0] = 4: Gosub _WriteData
			If I2cD[1] >= 5 Then I2cD[0] = 5: Gosub _WriteData
			EndIf
		EndIf
	EndIf
Return

'' 外字登録
_SetMyChars
	I2cD[0] = $40 | 0 * $08: Gosub _WriteCommand
	I2cD[0] = $1C '11100
	I2cD[1] = $08 '01000
	I2cD[2] = $08 '01000
	I2cD[3] = $00 '00000
	I2cD[4] = $00 '00000
	I2cD[5] = $00 '00000
	I2cD[6] = $00 '00000
	I2cD[7] = $00 '00000
	I2cW I2cD[31],$40, 8: Delay 1

	I2cD[0] = $40 | 1 * $08: Gosub _WriteCommand
	I2cD[0] = $1C '11100
	I2cD[1] = $08 '01000
	I2cD[2] = $08 '01000
	I2cD[3] = $00 '00000
	I2cD[4] = $00 '00000
	I2cD[5] = $0C '01100
	I2cD[6] = $0C '01100
'	I2cD[7] = $00 '00000
	I2cW I2cD[31],$40, 8: Delay 1

	I2cD[0] = $40 | 2 * $08: Gosub _WriteCommand
	I2cD[0] = $1C '11100
	I2cD[1] = $08 '01000
	I2cD[2] = $08 '01000
	I2cD[3] = $00 '00000
	I2cD[4] = $03 '00011
	I2cD[5] = $1B '11011
	I2cD[6] = $1B '11011
'	I2cD[7] = $00 '00000
	I2cW I2cD[31],$40, 8: Delay 1

	I2cD[0] = $40 | 3 * $08: Gosub _WriteCommand
	I2cD[0] = $00 '00000
	I2cD[1] = $00 '00000
	I2cD[2] = $00 '00000
	I2cD[3] = $18 '11000
	I2cD[4] = $18 '11000
	I2cD[5] = $18 '11000
	I2cD[6] = $18 '11000
'	I2cD[7] = $00 '00000
	I2cW I2cD[31],$40, 8: Delay 1

	I2cD[0] = $40 | 4 * $08: Gosub _WriteCommand
	I2cD[0] = $00 '00000
	I2cD[1] = $00 '00000
	I2cD[2] = $03 '00011
	I2cD[3] = $1B '11011
	I2cD[4] = $1B '11011
	I2cD[5] = $1B '11011
	I2cD[6] = $1B '11011
'	I2cD[7] = $00 '00000
	I2cW I2cD[31],$40, 8: Delay 1

	I2cD[0] = $40 | 5 * $08: Gosub _WriteCommand
	I2cD[0] = $00 '00000
	I2cD[1] = $18 '11000
	I2cD[2] = $18 '11000
	I2cD[3] = $18 '11000
	I2cD[4] = $18 '11000
	I2cD[5] = $18 '11000
	I2cD[6] = $18 '11000
'	I2cD[7] = $00 '00000
	I2cW I2cD[31],$40, 8: Delay 1
Return