4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DelphiAdvent Calendar 2023

Day 20

【MSX0】I2C.PAS【Turbo Pascal】

Last updated at Posted at 2023-12-19

はじめに

『MSX0 Stack』付属の IoT BASIC サンプルを『Turbo Pascal』へ移植してみます。

I2C.BAS

◆I2Cアクセス

項目 説明
概要 DHT20 に対して直接 I2C アクセスを行い温度を取得します。
ファイル名 I2C.BAS
対応デバイス DHT20 (黒色)
コメント Seeed Grove Kit の DHT20(黒色)温度湿度センサを Port A に接続してください。温度取得の際にCRCチェックなどは省略しています。

このサンプルを動作させるには外部センサーが必要です。

See also:

MSX0 Stack での挙動

まず事前にセンサーをつないでおきます。

image.png

右中央のセンサーが温湿度センサーなのですが、 『Grove Beginner Kit for Arduino』の製造ロットによって、センサーの種類が異なります。具体的には 2022/04 以降製造分に DHT20 (黒色) が付属するようです。DHT11 (水色) は I2C 接続のセンサーではないので使えません。

センサー 接続方法 Grove ポート
[D3] DHT11 (水色) Digital Port C (水色)
[I2C] DHT20 (黒) I2C Port A (赤)

『Grove Beginner Kit for MSX0』には DHT20 が付属します。

I2C.BAS をロードして、

image.png

実行してみました。アレ?行番号 160 のトコでエラーになりますね。

image.png

160   FORI I=0 TO C-1:IF A$(I)="38" THEN 180 ELSE NEXTI

I が一つ多いようです。

160   FOR I=0 TO C-1:IF A$(I)="38" THEN 180 ELSE NEXTI

今度は正しく表示されました。

image.png

センサーが正しく接続されていないとその旨を表示して終了します。

image.png

プログラムは〔Ctrl〕+〔Stop〕(リモートコントロールパネルからは〔Ctrl〕+〔F12〕)で中断できます。

See also:

Turbo Pascal へ移植

別途、MDL-LIB と SYSUTILS.LIBIOT.LIB が必要です。

I2C.PAS
program I2C;
type
  LibStr = string[80];
{$I SYSUTILS.LIB}
{$I IOT.LIB}
var
  List, Last, p: PItem;
  N, S: LibStr;
  T: Real;
  Flg: Boolean;
begin
  ClrScr;
  Flg := False;
  List := nil;
  N := 'device/i2c_a';
  IoTFind(N, List, Last);
  p := List;
  while (p <> nil) and (not Flg) do
  begin
    if p^.Data = '38' then
    begin
      Flg := True;
      N := N + '/' + p^.Data;
    end;
    p := p^.Next;
  end;
  ClearListItem(List);
  if not Flg then
  begin
    Writeln('DHT20 is not found...');
    Exit;
  end;
  while True do
  begin
    IotPutStr(N, #$71);
    S := IoTGetStr(N);
    repeat
      IotPutStr(N, #$AC#$33#$00);
      S := IoTGetStr(N);
      if KeyPressed then
        Exit;
    until (Length(S) >= 6) and (Ord(S[1]) and $18 = $18);
    T := Ord(S[4]) and $0F;
    T := T * 256 + Ord(S[5]);
    T := T * 256 + Ord(S[6]);
    T := T / 1048576.0 * 200 - 50;
    Writeln(T:5:2);
  end;
end.

実行してみました。

image.png

センサーが正しく接続されていれば温度で値が変動します。

任意のキーで中断できます。

解説

文字列 -> 配列変換

Turbo Pascal の文字列は文字型配列としてアクセスできるので、文字列 -> 配列変換は行っていません。

ループ (1)

GOSUB で記述されていたものをループで書き替えました。何をやっているか解りやすくなっているかもです。

ループ (2)

repeat のループは次のようになっています。

    repeat
      IotPutStr(N, #$AC#$33#$00);
      S := IoTGetStr(N);
      if KeyPressed then
        Exit;
    until (Length(S) >= 6) and (Ord(S[1]) and $18 = $18);

Turbo Pascal 3.0 は式の評価が標準 Pascal と同じ完全論理評価なので、Length(S) が 0 の場合 (文字列が存在しない場合) でも S[1] にアクセスしてしまう問題を孕んでいます。

例えば次のコードは i が 11 になった時でも (a[i] = 0) が評価されてしまい、実行時エラーになります。

EvalTest.pas
program EvalTest;
{$R+}
var  
  i: Integer;
  a: array [1..10] of Byte;
begin
  i := 0;
  repeat
    i := i + 1;
  until (i > 10) or (a[i] = 0);
end.

なので、万全を期すなら2重ループでなくてはいけません。

    repeat
      repeat
        IotPutStr(N, #$AC#$33#$00);
        S := IoTGetStr(N);
        if KeyPressed then
          Exit;
      until Length(S) >= 6;
    until Ord(S[1]) and $18 = $18;

ただ、Turbo Pascal 3.0 の文字列型はサイズが決まっており s[1] は範囲外ではないため、Length(S) が 0 の時に S[1] へアクセスしても問題ありません。

べき乗

BASIC での温度計算は次のようになっています。

370   T=D(3) AND &HF
380   T=T*256+D(4)
390   T=T*256+D(5)
400   T=T/(2^20)*200-50

Pascal にはべき乗演算子 (^) がないので、展開した 1048576 という値を使っています。ただ、そのままだと整数値として扱われてしまう (オーバーフローでエラー) ので、小数点を付けて 1048576.0 にし、実数型の定数としています。

べき乗の部分は次のようにする事もできますが、速度が目に見えて落ちます。やはり展開した実数型の定数を使うのが正解だと思います。

    T := T / Exp(Ln(2) * 20)  * 200 - 50;

See also:

おわりに

GOTO 抜き&マルチステートメントなしのプログラムなので、オリジナルの BASIC ソースに比べると少々長くなってはいますが、その分読みやすいプログラムにはなっているかと思います。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?