2020年7月18日土曜日

参照 http://blog.livedoor.jp/vine_user/archives/51338759.html さん


PCを再起動したり,USBメモリを挿し直したりすると,/dev/sdbが/dev/sdcや/dev/sddなどの他のデバイス名に置き換わってしまうことがあります。

今回のRAID構築に限らず,/etc/fstabを使った自動マウントの設定の際など,USBメモリのデバイス名を使う場面は結構あるのではないでしょうか?

最近のLinuxでは,udevというプログラムで自動的にデバイスを認識しています。Ubuntuで多くのハードウェアを設定なしに使えるのも,このudevのおかげなわけですが,今回はそれが裏目に出たということです。

このudevの設定を利用して,デバイス名を固定してみましょう。

/dev/sdbや/dev/sdcなどを直接固定するのは難しいようですので,/dev/sdxのxがどのように設定されても,特定のUSBメモリが常に/dev/usbhdaなどの新たな固定されたデバイス名になるようにするという方針で設定します。

STEP1.udevの設定ファイル


udevの設定(udevのルール)は,/etc/udev/rules.dというディレクトリの中にあるファイル群で指定されています。

これらのファイルを直接編集するのは危険なので,独自のルールを設定する場合は,新たに「96-local.rules」などの名前でファイルを作成します。ここで,それぞれのファイルの先頭の数字は,優先順位を表します。

udev-095-udev-late.rules」という数字95のファイルが最後にありましたので,他に影響が及ばないように,数字を96としました。

STEP2.USBメモリの固有情報を表示する


udevを設定するためには,USBメモリの固有情報を確かめておく必要があります。まず,次の手順でUSBメモリの現在のデバイス名を確認します。

USBメモリをUSBポートに挿入する前に,

$ cat /proc/partitions

を実行。USBメモリをUSBポートに挿入して,再び,

$ cat /proc/partitions

を実行。両者を見比べて,追加されたsdb,sdcなどが,そのUSBメモリのデバイス名です。追加されたものが「sdb」であれば,正確には/dev/sdbとなります。

デバイス名がわかったら,次のコマンドを実行します。/dev/sdbの部分は,確認したデバイス名に適宜置き換えて下さい。

$ udevinfo -a -p $(udevinfo -q path -n /dev/sdb)

udev-1KERNELS=="5-4.2"(数字は環境によって異なります)で始まるブロックに注目します。必要なのは,
ATTRS{serial}=="英数字"の部分。


なお,上の例ではUSBハブを利用しています。直接USBポートに挿した場合,ATTRS{serial}=="英数字"の部分は,小数点のないKERNELS=="5-4"で始まるブロックの中にあります。


STEP3.udevの設定ファイルを作成する


それでは,STEP2で確認した情報をもとにして,設定ファイルを書きましょう。次のコマンドで,エディタを開き,下に示したコードを記述するだけです。loval.rulesの先頭の数字は環境により異なります。/etc/udev/rules.d/の中を確認して,最大の数字より大きな数字にしておくといいでしょう。なお,USBハブを使うか使わないかで,微妙に異なります。

$ sudo gedit /etc/udev/rules.d/96-local.rules

【USBハブを介する間接接続の場合】
KERNEL=="sd*", SUBSYSTEM=="block", ATTR{removable}=="1", KERNELS=="[0-9]-[0-9].[0-9]", SUBSYSTEMS=="usb", DRIVERS=="usb", ATTRS{serial}=="VVN1xxxx", SYMLINK+="usbhda%n"
※ 赤字の部分に,STEP2で確認した英数字を記述します。

【USBハブを介さない直接接続の場合】
KERNEL=="sd*", SUBSYSTEM=="block", ATTR{removable}=="1", KERNELS=="[0-9]-[0-9]", SUBSYSTEMS=="usb", DRIVERS=="usb", ATTRS{serial}=="VVN1xxxx", SYMLINK+="usbhda%n"
※ 赤字の部分がハブを使うときとの相違点です。

【実際の例】
今回は,3つのUSBメモリを使いましたので,実例を挙げておきます。シリアルの英数字だけ一部伏せ字にしてありますが。

# "JetFlash Mass Storage Device 1"
KERNEL=="sd*", SUBSYSTEM=="block", ATTR{removable}=="1", KERNELS=="[0-9]-[0-9].[0-9]", SUBSYSTEMS=="usb", DRIVERS=="usb", ATTRS{serial}=="D15Ixxxx", SYMLINK+="usbhda%n"

# "JetFlash Mass Storage Device 2"
KERNEL=="sd*", SUBSYSTEM=="block", ATTR{removable}=="1", KERNELS=="[0-9]-[0-9].[0-9]", SUBSYSTEMS=="usb", DRIVERS=="usb", ATTRS{serial}=="WJXCxxxx", SYMLINK+="usbhdb%n"

# "JetFlash Mass Storage Device 3"
KERNEL=="sd*", SUBSYSTEM=="block", ATTR{removable}=="1", KERNELS=="[0-9]-[0-9].[0-9]", SUBSYSTEMS=="usb", DRIVERS=="usb", ATTRS{serial}=="VVN1xxxx", SYMLINK+="usbhdc%n"

これで,3つのUSBメモリは,/dev/usbhda,/dev/usbhdb,/dev/usbhdcという名前に固定されます。

STEP4.デバイス名が固定されていることの確認


以上で,固定された/dev/usbhdaなどの名称を用いて,fdiskなども実行できるようになるはずです。ここで,USBメモリを一度挿し直して,実際にうまくいっているかどうか確認してみましょう。

$ ls -la /dev/usbhd*

上記のコマンドを実行し,USBメモリを挿し直して,再度実行してみます(デバイスファイルのリンク先を確認しているだけです)。

udev-usb/dev/sdcと/dev/sddが入れ替わっています。

USBメモリを挿し直したことにより,/dev/sdcと/dev/sddが入れ替わっていますが,/dev/usbhdbと/dev/usbhdc自体は,元と同じUSBメモリのデバイス名になっているというわけです。

2020年7月11日土曜日



http://izawa-web.com/lazarus/lazarus.html 参照

 Raspberry Pi (ラズベリー・パイ) で Lazarus (FreePascal)ほぼ Delphi ですね。

インストールは、コマンドラインに、次のコマンドを入力するだけでいけました。(結構時間がかかります。)

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install fpc
sudo apt-get install lazarus
sudo apt-get install libfbclient2

プログラム(例えば、projetc1)の起動は、
sudo ./project1
なんですね。


■ rpi_hal ユニットで、i2c を使う

 ADS1015(12ビット4チャンネルAD変換)を使ってみました。
 rpi_hal.pas ユニットを使っています。
 そのままでは、ランタイムエラーで起動できないので、
 メニュー-プロジェクト-プロジェクトオプション-コンパイラオプション-その他 のカスタムオプションに、-dUseCThreads を追加します。
 終了時にエラー(例外クラス)が出る場合は、Uses 節の cmem をコメントアウトします。


unit ads1015Unit;
{$mode objfpc}{$H+}
interface
uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  ExtCtrls,
  // 追加ユニット
  rpi_hal, cthreads, Unix;
type
  { TForm1 }
  TForm1 = class(TForm)
    Edit1: TEdit;
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;
var
  Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.Timer1Timer(Sender: TObject);
var
  s : string;
  ret : integer;
  v : double;
begin
  
  // $48 : ADS1015_I2C_ADDRESS
  // $01 : ADS1015_REG_POINTER_CONFIG
  // $00 : ADS1015_REG_POINTER_CONVERT
  // $F183 : 3CHを±6.144V (Gain 2/3) で変換 (0CH = $C183, 1CH = $D183, 2CH = $E183)
  // ±4.096V (Gain 1) の時は、 $F183 or $0200
  // 実際には、最大 VDD + 0.3V までしか測定できない
  
  i2c_string_write($48$01, #$F1 + #$83 , NO_TEST);
  sleep(1); // 変換待ち
  s := i2c_string_read($48$002, NO_TEST);
  if Length(s) >= 2 then begin
     ret := StrToInt('$' + IntToHex(Ord(s[1]), 2) + IntToHex(Ord(s[2]), 2));
     // 下位 4 ビットは無関係
     ret := ret shr 4;
     v := ret * ((6.144 2) / 4096);
     Edit1.Text := Format('%.3f', [v]);
  end;
end;
end.

■ PiGpio ユニットを使ってステッピングモーターを制御

 ステッピングモーター: ST-42BYG0506H (1回転ステップ数:200 基本ステップ角:1.8度)
 ドライバIC: TB6674PG
 ※TB6674PG(TA7774P互換)と、TA7774PGの違い
  TB6674PG : モータ電源 Vs1A、Vs1B > 6.5V (標準12V)必要
  TA7774PG : モータ電源 Vs1A、Vs1B >= Vcc でOK
unit Unit4;
{$mode objfpc}{$H+}
interface
uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  // 追加ユニット
  PiGpio;
type
  { TForm1 }
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
  private
    { private declarations }
  public
    { public declarations }
  end;
var
  Form1: TForm1;
  GPIO_Driver : TIoDriver;
  Gpf : TIoPort;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.FormActivate(Sender: TObject);
begin
  if GPIO_Driver.MapIo then begin
    Gpf := GpIo_Driver.CreatePort(GPIO_BASE, CLOCK_BASE, GPIO_PWM);
    // GPIO20, 21, 16 を OUTPUT に
    // ICの 3 番ピンに接続(A)
    Gpf.SetPinMode(20, OUTPUT);
    // ICの 6 番ピンに接続(B)
    Gpf.SetPinMode(21, OUTPUT);
    // ICの 8 番ピンに接続(L:スタンバイ/ H:オペレーション)
    Gpf.SetPinMode(16, OUTPUT);
  end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
  i: integer;
  msec : integer;
begin
  if Gpf <> nil then begin
    msec := 10;
    Gpf.SetBit(16); // オペレーション
     // 200 ステップのモーターで、1回転
    for i := 0 to 49 do begin
      Gpf.ClearBit(20);
      Gpf.ClearBit(21);
      sleep(msec);
      Gpf.SetBit(20);
      Gpf.ClearBit(21);
      sleep(msec);
      Gpf.SetBit(20);
      Gpf.SetBit(21);
      sleep(msec);
      Gpf.ClearBit(20);
      Gpf.SetBit(21);
      sleep(msec);
    end;
    Gpf.ClearBit(20);
    Gpf.ClearBit(21);
    Gpf.ClearBit(16); // スタンバイ
   end;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
  i:integer;
  msec : integer;
begin
  if Gpf <> nil then begin
    msec := 10;
    Gpf.SetBit(16); // オペレーション
     // 200 ステップのモーターで、1回転
    for i := 0 to 49 do begin
      Gpf.ClearBit(20);
      Gpf.ClearBit(21);
      sleep(msec);
      Gpf.ClearBit(20);
      Gpf.SetBit(21);
      sleep(msec);
      Gpf.SetBit(20);
      Gpf.SetBit(21);
      sleep(msec);
      Gpf.SetBit(20);
      Gpf.ClearBit(21);
      sleep(msec);
    end;
    Gpf.ClearBit(20);
    Gpf.ClearBit(21);
    Gpf.ClearBit(16); // スタンバイ
  end;
end;
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  if Gpf <> nil then begin
    Gpf.ClearBit(20);
    Gpf.ClearBit(21);
    Gpf.ClearBit(16);
    GpIo_Driver.UnmapIoRegisrty(Gpf);
  end;
end;
end.