2024年6月30日日曜日

クリスタル発振回路(1)

クリスタル発振回路をシミュレーションしてみる。

いまさら感があるけど、なんとなくやってみたくなった。最近は素子の加工精度が上がったことから基本波発振の発振子が普通になってきて、動けばだいたいOKなので、そんなに一所懸命やることもなくなった(昔はMHz以上からはオーバートーンで使うことが多くて、異常発振とかが多くの設計者を悩ませていたに違いない)昨今、、、まぁメモなんよ。こういうのできるとか、こういうのなんでやろとか、書いとくんよここに。
で、今回はたわいもない内容なのでngspiceを直で使うことにする(GUI苦手なんよねーGUIは右の人差し指に作業が集中するのでじじいにはこたえるんよ)。で、まずはインバーターをどうするか。通常は発振回路評価とかで74HCU04とかを使うわけで、この記述を探すっと、LTspiceにあるらしいじゃないか、、、いや、すばらしい。ここ(https://github.com/texane/power_inverter/tree/master/ltspice/logic/74hc)にあるので、ありがたく頂いてくる。
で、まずはインバーターとしてちゃんと動くことを確認する。
  1. * inverter_74hcu04.cir
  2. .title inverter_74hcu04
  3.  
  4. .include 74hc.lib
  5.  
  6. XU1 1 2 3 0 74HCU04 vcc1=3.3 speed1=1.0 tripdt1=1e-9
  7. VCC 3 0 DC 3.3
  8. VIN 4 0 PULSE ( 0 3.3 0 0 0 0.5ms 1ms 10)
  9. R1 2 0 1k
  10. R2 4 1 10k
  11.  
  12. .control
  13. tran 0.01ms 20ms
  14. plot v(1) v(2) v(4)
  15. .endc
  16. .end
まぁ、こんなかんじで。で、 ngspiceを起動して、
source inverter_74hcu04.cir
で、実行。すると、
となるので、インバーターとして動作していることがわかる。
で、どんな特性なのかいちおう調べておくってことで、パルスの代わりにPWLで三角波をいれることでリサージュっぽいものを見てみる。
  1. * char_74hcu04.cir
  2. .title char_74hcu04
  3.  
  4. .include 74hc.lib
  5.  
  6. XU1 1 2 3 0 74HCU04 vcc1=3.3 speed1=1.0 tripdt1=1e-9
  7. VCC 3 0 DC 3.3
  8. VIN 4 0 PWL(0 0 10ms 4 20ms 0)
  9. R1 2 0 1k
  10. R2 4 1 10k
  11.  
  12. .control
  13. tran 0.01ms 20ms
  14. plot v(1) v(2) v(4)
  15. .endc
  16. .end
まぁ、こういう特性のモデルらしい。
では、32.768kHzのクリスタルを発振させてみる。クリスタルの等価回路定数はあまり公開されていないので、実測する必要があるけど、EPSONは32.768kHzクリスタルの等価回路定数を公開しているので、これでやってみる。 まぁ、こんなかんじ。
  1. .title xtal_osc
  2. * xtal_osc_study_002.cir
  3.  
  4. .include 74hc.lib
  5.  
  6. XU1 1 2 5 0 74HCU04 vcc1=3.3 speed1=1.0 tripdt1=1e-9
  7. R2 1 2 10MEG
  8.  
  9. L1 1 3 2986.16
  10. C1 3 4 7.9e-15
  11. R1 4 2 40e3
  12. C2 1 2 1.5e-12
  13. C3 1 0 680e-12
  14. C4 2 0 680e-12
  15. VCC 5 0 DC 3.3
  16. .ic v(1)=3.3
  17. *.options abstol=1e-12
  18. *.options pivtol=1e-13
  19. *.options reltol=0.003
  20. *.options vntol=1e-6
  21. *.options chgtol=1e-14
  22. *.options trtol=7
  23.  
  24. .options abstol=1e-12
  25. .options pivtol=1e-13
  26. .options reltol=0.003
  27. .options vntol=1e-6
  28. .options chgtol=1e-14
  29. .options trtol=5
  30.  
  31. .control
  32. tran 10us 10000ms
  33. plot v(2) v(1)
  34. .endc
  35. .end
なんやけど、実は発振させるのにかなり苦労した。結局、負荷容量を実際に使用する値(10pF前後)よりもかなり大きくして、シミュレーション時間をとんでもなく長くしてやっと発振した。で、
こんなかんじ。現実は結構簡単に発振開始するけど、シミュレーションではなかなか難しい。でさらに、発振開始時間ってけっこう神のイカヅチ(.icで初期状態を入れているところ)によってかわる。高い電圧だと、早く発振開始する。発振開始時間って結局現物で繰り返し評価するしかないって現実をやっぱそうかーって受け入れる。が、もっと現実に近い発振開始時間にするにはどうしたらいいのか。
、、、メンドクサくなってきたので、今日はこのくらいにしておく。

2024年6月16日日曜日

Seleniumをpowershellから使う(2)

 

webページの操作を自動化できるSelenium。これをpowershellから使おうっていう試み。今回は、指定する要素をどうやって知ることができるのかってのを書いてみる。Seleniumの紹介ページってものすごーくたくさんあるけど、要素(element)の指定方法について詳しく書かれたものはないと思う。ていうか、そんなの常識?なのか?ってところに切り込んでみる。
例題として、SundayEngineerのページに自動的にコメントするスクリプトを書いてみる。
どのページにするかって言うと前回記事のこちら(https://sunday-engineer.jimdofree.com/2024/06/11/selenium%E3%82%92powershell%E3%81%8B%E3%82%89%E4%BD%BF%E3%81%86-1/)とする。まずは、chromeでそのページを開いてみる。そして、ctrl+shift+iを押す。そすっと、
こんな感じで、開発者用のウィンドウが現れる。で、今回自動入力したいコメント欄が見えるように下のほうまでスクロールして、その状態で、開発者ウィンドウのElementsタブで閉じているアイテムを開いたりなんだりしながらマウスを行ったり来たりさせると、Webページのほうが色が変わったりする。で、
こんな感じで、まずはコメントの「名前」ってところが
inputって要素でそのnameが"name"であり、idが"name11138124819"であるので、これのどっちかを使って指定できそうだなってわかる。
でコメント欄は
textareaって要素でそのnameが"comment"であり、idが"comment11138124819"。
で、送信ボタンは
inputって要素でそのnameが"送信"であり、idが"submit11138124819"で、valueが"送信"。ってことがわかる。
で、いろいろ調べると、send-sekeysだけじゃなくてsetypeってやつでもテキストを送れるらしいので、今回はこっちを使ってみる。-clearfirst使うと入力前のクリアが少し楽になるっぽいし(試したわけではない)。
そして、一旦、はまったのが、jimboのホームページって、cookieの同意をしないとまともに表示されないので、cookieの同意も押さないと入力できないってこと。
これも要素を調べて自動で押しちゃう。
で、idがcookie-settings-selectedってことで要素を選択してクリックすればよいのだろう。それと、(テキストを入力する)要素をクリックしてからじゃないと、入力できないことがある気が何となくする(てきとーすぎやろ)ので、setypeの前にclickしておくことにする。
そしてコードがこちら。
  1. write-output "hello selenium!"
  2. $binary_path="C:\Program Files\Google\Chrome\Application\chrome.exe"
  3. $driver_path="C:\Users\hoge\Documents\work\powershell\selenium_study\chromedriver-win64-125.0.6422.141"
  4. $start_url="https://sunday-engineer.jimdofree.com/2024/06/11/selenium%E3%82%92powershell%E3%81%8B%E3%82%89%E4%BD%BF%E3%81%86-1/"
  5. $driver=start-sechrome -binarypath $binary_path -webdriverdirectory $driver_path
  6. enter-seurl -url $start_url -driver $driver
  7. start-sleep -Milliseconds 300
  8. $element=find-seelement -driver $driver -Id 'cookie-settings-selected'
  9. send-seclick -element $element
  10. start-sleep -Milliseconds 100
  11. $element=find-seelement -driver $driver -Id 'name11138124819'
  12. setype -element $element -keys "ディグダ" -ClearFirst
  13. $element=find-seelement -driver $driver -Id 'comment11138124819'
  14. setype -element $element -keys "地面ポケモン" -ClearFirst
  15. $element=find-seelement -driver $driver -Id 'submit11138124819'
  16. send-seclick -element $element
  17. start-sleep 1
  18. stop-sedriver -driver $driver
  19. write-output "completed"
まぁこれでうまくいく。
ところで、このコードはsjisで保存してpowershellから実行するとうまくいくんだけど、vscodeから実行すると文字化けする。powershellの文字コード問題は、、、また別の機会に考える。c#だと、このへんが少し楽になりそうな(というのも情報が多いから)気がする。が、powershellはマイブームなので。

powershellからseleniumを使ってwebブラウザ操作を自動化するってなかなか面白いと思う。業務ソフトがwebブラウザベースなら取り組まない手はない。seleniumと格闘するのが正解か、人力でやり切るのが正解か、、、って悩んで躊躇することがあると思うけど、躊躇するくらいだったらやってみるのがいいと思う。

今回の例題を誰かがそのまま試して、たくさんのコメント(しかも文字化けしているのもあったり)が来たとしたらまじうけるー

2024年6月9日日曜日

Seleniumをpowershellから使う(1)

 

webページの操作を自動化できるSelenium。これをpowershellから使おうっていう試み。

色んな会社のシステムってWebブラウザを使ってることが多いんじゃないかと思う。で、クソ遅いとかシステムによって使用感が統一されていないとか色々不満があると思う。、、、知らんけど。いや、知っている。まじgmks。
で、Seleniumを使うと、RPAのようにPCを占有することもなくWebブラウザでの操作を自動化できるので、いくつかのシステムの連携もローカルでこなせるわけで、使わない手はない。ところで、RPAのはしり、UWSCが登場したのが1999年。2010年ごろマイブームが来たんだけど、PCを占有されるってので、使い方が難しいなーって思ってた。結局、ぼーっっと待ってるんよ。終わるのを。もちろんブラウザ以外のアプリも操作できるというRPAの利点はある。

で、参考サイトはこちら(https://qiita.com/_YuruTa_/items/91224e038ae565fb1a83)(いつも先人の足跡をたどっているだけなのに、えらそうなこと言うんじゃないって怒られそうやな、、、)。
まずは管理者モードでPowerShellを起動する。で、
install-module -name Selenium
って入力する。そすっと
こうなる。迷わず「Y」押してENTER。
なんか知らんけど、信頼されていないらしい。信頼を得るには地道な積み重ねが必要だよなーと思いつつ、「Y」ENTER。そすっと、上のほうにこしょこしょ青い画面が出た後、何事もなかったようにプロンプトが表示される。(ここまででつまづいたら上記参考サイトをみるべし)、、で、たぶんこれでいいんだろう。
で、参考サイトに倣って、
get-command -module selenium
ってすると、なんと、
  • CommandType Name Version Source
  • ----------- ---- ------- ------
  • Alias Enter-SeUrl 3.0.1 selenium
  • Alias Find-SeElement 3.0.1 selenium
  • Alias Start-SeLegacyEdge 3.0.1 selenium
  • Function Clear-SeAlert 3.0.1 selenium
  • Function Get-SeCookie 3.0.1 selenium
  • Function Get-SeElement 3.0.1 selenium
  • Function Get-SeElementAttribute 3.0.1 selenium
  • Function Get-SeElementCssValue 3.0.1 selenium
  • Function Get-SeKeys 3.0.1 selenium
  • Function Get-SeSelectionOption 3.0.1 selenium
  • Function Get-SeWindow 3.0.1 selenium
  • Function Invoke-SeClick 3.0.1 selenium
  • Function Invoke-SeScreenshot 3.0.1 selenium
  • Function New-SeScreenshot 3.0.1 selenium
  • Function Open-SeUrl 3.0.1 selenium
  • Function Remove-SeCookie 3.0.1 selenium
  • Function Save-SeScreenshot 3.0.1 selenium
  • Function Send-SeClick 3.0.1 selenium
  • Function Send-SeKeys 3.0.1 selenium
  • Function SeOpen 3.0.1 selenium
  • Function SeShouldHave 3.0.1 selenium
  • Function Set-SeCookie 3.0.1 selenium
  • Function SeType 3.0.1 selenium
  • Function Start-SeChrome 3.0.1 selenium
  • Function Start-SeEdge 3.0.1 selenium
  • Function Start-SeFirefox 3.0.1 selenium
  • Function Start-SeInternetExplorer 3.0.1 selenium
  • Function Start-SeNewEdge 3.0.1 selenium
  • Function Start-SeRemote 3.0.1 selenium
  • Function Stop-SeDriver 3.0.1 selenium
  • Function Switch-SeFrame 3.0.1 selenium
  • Function Switch-SeWindow 3.0.1 selenium
ってなる。すなわち-AllowPrereleaseがないとSelenium3がインストールされる。まぁこれでチャレンジしてみるっス。で、web driverをとってくる。chromewebdriver.exeでぐぐると、ここ(https://developer.chrome.com/docs/chromedriver/downloads?hl=ja)がひっかかる。で、微妙にわかりにくいけど、
からの
からの
これを、コピーして、ブラウザのアドレスに貼り付けてダウンロード。
落ちてきたzipファイルの中からchromedriver.exeをよさげなところにコピペしておく。

さらに、google chromeブラウザの実行ファイルの場所を調べておく。だいたい「C:\Program Files\Google\Chrome\Application」にある。

では、普通にPowerrshellを起動する。で、例によって作業用のフォルダを作って、そこでvscodeを起動する。(mkdirとかcdとかした後、code .ってやる人は少数派だろうな、、、)

で、参考サイト通りにやると、はまる。seleniumに色々と教えてあげながら動かしてあげるといいらしい。なんじゃもんじゃやって、こんなかんじ。ファイル名はいつものようにいつ終わるかわからんstudy1.ps1。
なんちゃら_pathはPCによって違う(前述のように置いた場所やら調べた場所やらを書くんやで)。
  1. write-output "hello selenium!"
  2. $binary_path="C:\Program Files\Google\Chrome\Application\chrome.exe"
  3. $driver_path="C:\Users\hoge\Documents\work\powershell\selenium_study\chromedriver-win64-125.0.6422.141"
  4. $start_url="https://www.google.co.jp"
  5. $driver=start-sechrome -binarypath $binary_path -webdriverdirectory $driver_path
  6. enter-seurl -url $start_url -driver $driver
  7. $element=find-seelement -driver $driver -classname "gLFyf"
  8. send-sekeys -element $element -keys "sunday engineer"
  9. send-sekeys -element $element -keys "`n"
  10. start-sleep 5
  11. stop-sedriver -driver $driver
  12. write-output "completed"
で、vscodeの再生ボタン(2つあるうちの左が「実行」で右は「デバッグ」なので、左のほう)
を押すと。chromeが立ち上がって、勝手に検索して、勝手に終了する。ターミナルはこうなっている。
はいー。とりあえずうまくいったと思いますよー。

seleniumを使って、じゃんじゃん自動化や。ローコード・ノーコードくそくらえ。コードを書きまくるんや。そして惜しげもなく(というか恥ずかしげもなく)公開しまくる。オープンソース万歳!

、、、あ、どうやってelementを探すん?すなわち、なんで、
$element=find-seelement -driver $driver -classname "gLFyf"
で、検索のテキストボックスを指定できるのがわかるん?とかは次の機会に(あれば)。

だんだん人間としての品がなくなってきた気がする。いや、そもそも下賤なものでござりますゆえ。

2024年6月6日木曜日

RC-S300をpowershellから動かしてみる

 

RC-S300を買ってみたよ。winscard.dllの関数をcallすることで簡単に読み取りとかできる(もちろん読み取れるようにしてあるデータだけ)。が、なぜかpowershellから動かしてみようと思う。

さて、C#から動かしている事例はまぁまぁあるし、PC/SC wrapper classes for .NET(https://www.nuget.org/packages/PCSC/)を使うという手もある。が、あえて、ここは最近マイブームのpowershellを使ってみる。
で、参考サイトはこちらhttps://qiita.com/rhene/items/725dfe4a6b6307731cbfていうかこちらの記事をまるっとpowershellに移植する。powershellにはC#を取り込めるので、APIの宣言とかはそのまま流用する(というかそうする必要がある)。Main部分をpowershellに翻訳していくが、powershellでは記述が難しい部分はdllをインポートする(APIを宣言してある)ためのC#コードに追加して乗り切る。
まずは、定数
nfc_constant.ps1
  1. $nfc_constant_cscode = @'
  2. public const uint SCARD_S_SUCCESS = 0;
  3. public const uint SCARD_E_NO_SERVICE = 0x8010001D;
  4. public const uint SCARD_E_TIMEOUT = 0x8010000A;
  5. public const uint SCARD_SCOPE_USER = 0;
  6. public const uint SCARD_SCOPE_TERMINAL = 1;
  7. public const uint SCARD_SCOPE_SYSTEM = 2;
  8. public const int SCARD_STATE_UNAWARE = 0x0000;
  9. public const int SCARD_STATE_CHANGED = 0x00000002;
  10. public const int SCARD_STATE_PRESENT = 0x00000020;
  11. public const UInt32 SCARD_STATE_EMPTY = 0x00000010;
  12. public const int SCARD_SHARE_SHARED = 0x00000002;
  13. public const int SCARD_SHARE_EXCLUSIVE = 0x00000001;
  14. public const int SCARD_SHARE_DIRECT = 0x00000003;
  15. public const int SCARD_PROTOCOL_T0 = 1;
  16. public const int SCARD_PROTOCOL_T1 = 2;
  17. public const int SCARD_PROTOCOL_RAW = 4;
  18. public const int SCARD_LEAVE_CARD = 0;
  19. public const int SCARD_RESET_CARD = 1;
  20. public const int SCARD_UNPOWER_CARD = 2;
  21. public const int SCARD_EJECT_CARD = 3;
  22. // SCardStatus status values
  23. public const int SCARD_UNKNOWN = 0x00000000;
  24. public const int SCARD_ABSENT = 0x00000001;
  25. public const int SCARD_PRESENT = 0x00000002;
  26. public const int SCARD_SWALLOWED = 0x00000003;
  27. public const int SCARD_POWERED = 0x00000004;
  28. public const int SCARD_NEGOTIABLE = 0x00000005;
  29. public const int SCARD_SPECIFICMODE = 0x00000006;
  30. '@
  31. set-variable -name SCARD_S_SUCCESS -value 0 -option constant
  32. set-variable -name SCARD_E_NO_SERVICE -value 0x8010001D -option constant
  33. set-variable -name SCARD_E_TIMEOUT -value 0x8010000A -option constant
  34. set-variable -name SCARD_SCOPE_USER -value 0 -option constant
  35. set-variable -name SCARD_SCOPE_TERMINAL -value 1 -option constant
  36. set-variable -name SCARD_SCOPE_SYSTEM -value 2 -option constant
  37. set-variable -name SCARD_STATE_UNAWARE -value 0x0000 -option constant
  38. set-variable -name SCARD_STATE_CHANGED -value 0x00000002 -option constant
  39. set-variable -name SCARD_STATE_PRESENT -value 0x00000020 -option constant
  40. set-variable -name SCARD_STATE_EMPTY -value 0x00000010 -option constant
  41. set-variable -name SCARD_SHARE_SHARED -value 0x00000002 -option constant
  42. set-variable -name SCARD_SHARE_EXCLUSIVE -value 0x00000001 -option constant
  43. set-variable -name SCARD_SHARE_DIRECT -value 0x00000003 -option constant
  44. set-variable -name SCARD_PROTOCOL_T0 -value 1 -option constant
  45. set-variable -name SCARD_PROTOCOL_T1 -value 2 -option constant
  46. set-variable -name SCARD_PROTOCOL_RAW -value 4 -option constant
  47. set-variable -name SCARD_LEAVE_CARD -value 0 -option constant
  48. set-variable -name SCARD_RESET_CARD -value 1 -option constant
  49. set-variable -name SCARD_UNPOWER_CARD -value 2 -option constant
  50. set-variable -name SCARD_EJECT_CARD -value 3 -option constant
  51. # SCardStatus status values
  52. set-variable -name SCARD_UNKNOWN -value 0x00000000 -option constant
  53. set-variable -name SCARD_ABSENT -value 0x00000001 -option constant
  54. set-variable -name SCARD_PRESENT -value 0x00000002 -option constant
  55. set-variable -name SCARD_SWALLOWED -value 0x00000003 -option constant
  56. set-variable -name SCARD_POWERED -value 0x00000004 -option constant
  57. set-variable -name SCARD_NEGOTIABLE -value 0x00000005 -option constant
  58. set-variable -name SCARD_SPECIFICMODE -value 0x00000006 -option constant
C#のコードのまま取り込めるのかと思ったけど、うまくいかなかったのでpowershell的な定義を追加。C#記述はそのままそうっとしておく。
次はwinscard.dllを呼び出すためのおまじない。
nfc_pcsc.ps1
  1. $nfc_pcsc_cscode = @'
  2. [DllImport("winscard.dll")]
  3. public static extern uint SCardEstablishContext(uint dwScope, IntPtr pvReserved1, IntPtr pvReserved2, out IntPtr phContext);
  4. [DllImport("winscard.dll", EntryPoint = "SCardListReadersW", CharSet = CharSet.Unicode)]
  5. public static extern uint SCardListReaders(
  6.     IntPtr hContext, byte[] mszGroups, byte[] mszReaders, ref UInt32 pcchReaders);
  7. [DllImport("winscard.dll")]
  8. public static extern uint SCardReleaseContext(IntPtr phContext);
  9. [DllImport("winscard.dll", EntryPoint = "SCardConnectW", CharSet = CharSet.Unicode)]
  10. public static extern uint SCardConnect(IntPtr hContext, string szReader,
  11.         uint dwShareMode, uint dwPreferredProtocols, ref IntPtr phCard,
  12.         ref IntPtr pdwActiveProtocol);
  13. [DllImport("winscard.dll")]
  14. public static extern uint SCardDisconnect(IntPtr hCard, int Disposition);
  15. [StructLayout(LayoutKind.Sequential)]
  16. public class SCARD_IO_REQUEST
  17. {
  18.     internal uint dwProtocol;
  19.     internal int cbPciLength;
  20.     public SCARD_IO_REQUEST()
  21.     {
  22.         dwProtocol = 0;
  23.     }
  24. }
  25. [DllImport("winscard.dll")]
  26. public static extern uint SCardTransmit(IntPtr hCard, IntPtr pioSendRequest, byte[] SendBuff, int SendBuffLen, SCARD_IO_REQUEST pioRecvRequest,
  27.         byte[] RecvBuff, ref int RecvBuffLen);
  28. [DllImport("winscard.dll")]
  29. public static extern uint SCardControl(IntPtr hCard, int controlCode, byte[] inBuffer, int inBufferLen, byte[] outBuffer, int outBufferLen, ref int bytesReturned);
  30. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  31. public struct SCARD_READERSTATE
  32. {
  33.     internal string szReader;
  34.     internal IntPtr pvUserData;
  35.     internal UInt32 dwCurrentState;
  36.     internal UInt32 dwEventState;
  37.     internal UInt32 cbAtr;
  38.     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 36)]
  39.     internal byte[] rgbAtr;
  40. }
  41. [DllImport("winscard.dll", EntryPoint = "SCardGetStatusChangeW", CharSet = CharSet.Unicode)]
  42. public static extern uint SCardGetStatusChange(IntPtr hContext, int dwTimeout, [In, Out] SCARD_READERSTATE[] rgReaderStates, int cReaders);
  43. [DllImport("winscard.dll")]
  44. public static extern int SCardStatus(IntPtr hCard, string szReader, ref int cch, ref int state, ref int protocol, ref byte[] bAttr, ref int cByte);
  45. [DllImport("kernel32.dll", SetLastError = true)]
  46. public static extern IntPtr LoadLibrary(string lpFileName);
  47. [DllImport("kernel32.dll")]
  48. public static extern void FreeLibrary(IntPtr handle);
  49. [DllImport("kernel32.dll")]
  50. public static extern IntPtr GetProcAddress(IntPtr handle, string procName);
  51. public static uint MySCardTransmit(
  52.     IntPtr hCard,
  53.     byte[] SendBuff,
  54.     int SendBuffLen,
  55.     int cbPciLength,
  56.     byte[] RecvBuff,
  57.     ref int RecvBuffLen
  58. ){
  59.     uint ret;
  60.     SCARD_IO_REQUEST ioRecv = new SCARD_IO_REQUEST();
  61.     ioRecv.cbPciLength = cbPciLength;
  62.     IntPtr handle = LoadLibrary("Winscard.dll");
  63.     IntPtr pci = GetProcAddress(handle, "g_rgSCardT1Pci");
  64.     FreeLibrary(handle);
  65.     ret = SCardTransmit(hCard, pci, SendBuff, SendBuffLen, ioRecv,RecvBuff, ref RecvBuffLen);
  66.     return ret;
  67. }
  68. '@
MySCardTransmitってのは参考サイトのコードの中でpowershellで表現が難しかった部分をC#側に記述したもの。ここ以外は工夫はない。
で、次は本命のスクリプト
nfc_read.ps1
  1. . ".\nfc_constant.ps1"
  2. . ".\nfc_pcsc.ps1"
  3. #add-type -name nfc_constant -namespace user -memberdefinition $nfc_constant_cscode
  4. add-type -name nfc_pcsc -namespace user -memberdefinition $nfc_pcsc_cscode
  5. $hContext=1
  6. <# #############################
  7. 1. SCardEstablishContext
  8. ############################# #>
  9. write-output "***** 1. SCardEstablishContext *****"
  10. $ret = [user.nfc_pcsc]::SCardEstablishContext($SCARD_SCOPE_USER, 0, 0, [ref]$hContext)
  11. if ($ret -ne $SCARD_S_SUCCESS){
  12.     $msg=$null
  13.     if($ret -eq $SCARD_E_NO_SERVICE){
  14.         $msg="No service found."
  15.     }else{
  16.         $msg="Failed to connect the service. code = " + $ret
  17.     }
  18.     write-output $msg
  19.     exit
  20. }
  21. if($hContext -eq 0){
  22.     write-output "Failed to take the context."
  23.     exit
  24. }
  25. write-output "Connected to the service."
  26. <# #############################
  27. 2. SCardListReaders
  28. ############################# #>
  29. write-output "***** 2. SCardListReaders *****"
  30. $pcchReaders=0
  31. $ret = [user.nfc_pcsc]::SCardListReaders($hContext, $null, $null, [ref]$pcchReaders)
  32. if ($ret -ne $SCARD_S_SUCCESS){
  33.     write-output "Failed to detect reader."
  34.     exit
  35. }
  36. [array]$bszReaders=new-object byte[] ($pcchReaders*2)
  37. $ret = [user.nfc_pcsc]::SCardListReaders($hContext, $null, $bszReaders, [ref]$pcchReaders)
  38. if ($ret -ne $SCARD_S_SUCCESS){
  39.     write-output "Failed to get reader name."
  40.     exit
  41. }
  42. $mszReaders=[System.Text.Encoding]::Unicode.GetString($bszReaders)
  43. $nullindex=$mszReaders.IndexOf("`0`0")
  44. $readerName=$mszReaders.substring(0,$nullindex)
  45. write-output "Reader detected."
  46. write-output $readerName
  47. <# #############################
  48. 3. SCardConnect
  49. ############################# #>
  50. write-output "***** 3. SCardConnect *****"
  51. $hCard=0
  52. $activeProtocol=0
  53. $ret = [user.nfc_pcsc]::SCardConnect($hContext, $readerName, $SCARD_SHARE_SHARED, $SCARD_PROTOCOL_T1, [ref]$hCard, [ref]$activeProtocol);
  54. if ($ret -ne $SCARD_S_SUCCESS){
  55.     write-output "Failed to connect to cards. code = "+$ret
  56.     exit
  57. }
  58. write-output "Connected to a card."
  59. <# #############################
  60. 4. SCardTransmit
  61. ############################# #>
  62. write-output "***** 4. SCardTransmit *****"
  63. $maxRecvDataLen=256
  64. [array]$recvBuffer=new-object byte[] ($maxRecvDataLen+2)
  65. $sendBuffer=@(0xff, 0xca, 0x00, 0x00, 0x00)
  66. $pcbRecvLength=$recvBuffer.Length
  67. $cbSendLength=$sendBuffer.Length
  68. $ret = [user.nfc_pcsc]::MySCardTransmit($hCard, $sendBuffer, $cbSendLength,255, $recvBuffer, [ref]$pcbRecvLength)
  69. if ($ret -ne $SCARD_S_SUCCESS)
  70. {
  71.     write-output "Failed to send to NFC card. code = " + $ret
  72.     exit
  73. }
  74. $szRecv = ([System.Text.Encoding]::ASCII.GetString($recvBuffer)).substring(0,$pcbRecvLength)
  75. write-output "Received data from the card."
  76. write-output $szRecv | Format-Hex
  77. <# #############################
  78. 5. SCardDisconnect
  79. ############################# #>
  80. write-output "***** 5. SCardDisconnect *****"
  81. $ret = [user.nfc_pcsc]::SCardDisconnect($hCard, $SCARD_LEAVE_CARD)
  82. if ($ret -ne $SCARD_S_SUCCESS)
  83. {
  84.     write-output "Failed to disconnect the card. code = " + $ret
  85.     exit
  86. }
  87. write-output "Disconnected the card."
コンソール出力を英文化したのは決してかぶれているからではない。VSCodeの化け回避のためなの(υ´•̥̥̥ ﻌ •̥̥̥`υ)
では、動かしてみる。ちょっと前にPN5180のモジュールを買ったときにおまけでついていたタグを読んでみる。
こんなん。で、
まぁ、うまくいったらしい。

Powershell、、、意外と面白い。コンパイラやインタプリタが不要なのがよい。記述がちょっと呪文系だけど、まぁ言語なんてそんなもん。C#で記述して取り込めることを利用してWindowsAPIをCallできるので、応用できる範囲も広い。こういうので、こまいことをちゃちゃっとやれるような、そんな能力が給料に反映され、みんながデジタル技術の獲得を目指すことで本当のDXがちょっと近づいてくるんじゃないだろうかな、、、って発想は自分で手を動かしている小者にしか湧いてこないよなー。世の中そんなもん。せめてお互い称賛しあえるような雰囲気になってほしい。

2024年6月2日日曜日

FT232HにdotnetIoTでSPIは

 

dotnet IoTでFT232Hをいじってみるんやけど、何かがおかしいんよ。

dotnet IoTからのFt232HでSPIを出すってのをやってみようと思う。SPIのターゲットはとりあえず考えない。
で、作成した回路はこんな感じ。
USB電圧を多回転可変抵抗で分圧して、それを基準電圧としてオペアンプの定電圧源回路にぶっこんでいる。これを2回路つくってあって、そのうちの一つをターゲット電圧として、ターゲットに供給するとともにFT232HからのSPIを秋月のFXMA108を使ったレベル変換ICでターゲット電圧ドメインに変換している。4066は可変抵抗で分圧した電圧を供給するか、GNDレベルを供給するかを選択できるようにしてある。これはFT232HのGPIO機能で操作する。
久しぶりのブレッドボードで、ちょっと苦戦した。いや、苦戦というよりは躊躇した。躊躇している時間って冷静に考えると、それは無駄時間。あーでもないこーでもない頭で考えるのならそれを文字起こしすればいいと思う。すっきり取りかかれる、、、仕事なら。今日は仕事じゃないので、躊躇することも許してほしい。
で、苦労の末に電源供給部は正しく動作して、SPIも何となく動作した(dotnet IoTのリファレンスではCSを-1にすれば指定しなないってことになる的な記述があったんだけど、この設定は物理的に存在するピンを必ず指定しないといけないらしいってのを気づくのにクソ時間かかった)んだけど、、、何かが違う。
8ビット送信後SCKが初期状態にならないまま次の8ビット送信が開始されているように見える、、、つまり使えんやん。自分が何か間違っていると思って、dotnet IoTのリファレンスやら見まくった。で、結局のところググってみたところ、こんなのがヒットした。
https://electronics.stackexchange.com/questions/360086/make-ftdi-2232d-do-spi-mode-1-properly-data-seems-1-2-clock-cycle-off
https://forums.adafruit.com/viewtopic.php?t=124869
つまりFT2XXHチップのMPSSEモードでSPIを撃つときはMode0かMode2しかまともに撃てないんだぜってことらしい、、、あかんやん。1バイトづつ撃てばいけるような気もするけど、心が折れた。
今回のソースコードはこちら。

  1. using System.Device.Gpio;
  2. using System.Device.Spi;
  3. using Iot.Device.Ft232H;
  4. using Iot.Device.FtCommon;
  5. namespace ft232h_device_test
  6. {
  7.     class Program
  8.     {
  9.         public static int[]? pins;
  10.         public static GpioController? gpioController;
  11.         public static SpiDevice? spi;
  12.         public static int pin_csn,pin_dummy;
  13.         static void Main(string[] args)
  14.         {
  15.             byte[] txdata=new byte[3]{0x55,0x55,0x55};
  16.             byte[] rxdata=new byte[3]{0x00,0x00,0x00};
  17.             var devices = FtCommon.GetDevices();
  18.             Console.WriteLine($"{devices.Count} available device(s)");
  19.             foreach (var device in devices)
  20.             {
  21.                 Console.WriteLine($" {device.Description}");
  22.                 Console.WriteLine($" Flags: {device.Flags}");
  23.                 Console.WriteLine($" Id: {device.Id}");
  24.                 Console.WriteLine($" LocId: {device.LocId}");
  25.                 Console.WriteLine($" Serial number: {device.SerialNumber}");
  26.                 Console.WriteLine($" Type: {device.Type}");
  27.             }
  28.             if (devices.Count == 0)
  29.             {
  30.                 Console.WriteLine("No device connected");
  31.                 return;
  32.             }
  33.             Ft232HDevice ft232h=new Ft232HDevice(FtCommon.GetDevices()[0]);
  34.             gpioController=ft232h.CreateGpioController();
  35.             pin_dummy=Ft232HDevice.GetPinNumberFromString("D7");
  36.             SpiConnectionSettings settings = new SpiConnectionSettings(0) {ClockFrequency = 100000, DataBitLength = 8,Mode=SpiMode.Mode1,ChipSelectLine=pin_dummy,ChipSelectLineActiveState=PinValue.Low};
  37.             spi = ft232h.CreateSpiDevice(settings);
  38.             pin_csn=Ft232HDevice.GetPinNumberFromString("D4");
  39.             gpioController.OpenPin(pin_csn, PinMode.Output);
  40.             gpioController.Write(pin_csn,PinValue.High);
  41.             pins=new int[4];
  42.             pins[0]=Ft232HDevice.GetPinNumberFromString("C0");
  43.             pins[1]=Ft232HDevice.GetPinNumberFromString("C1");
  44.             pins[2]=Ft232HDevice.GetPinNumberFromString("C2");
  45.             pins[3]=Ft232HDevice.GetPinNumberFromString("C3");
  46.             for(int i=0;i<4;i++){
  47.                 gpioController.OpenPin(pins[i], PinMode.Output);
  48.                 gpioController.Write(pins[i],PinValue.Low);
  49.             }
  50.             OUT1_0V_OUT2_0V();
  51.             Console.WriteLine("Entering test loop");
  52.             while(true){
  53.                 Thread.Sleep(500);
  54.                 OUT1_ON_OUT2_0V();
  55.                 Thread.Sleep(500);
  56.                 OUT1_ON_OUT2_ON();
  57.                 SendSpi(txdata,rxdata,3);
  58.                 Thread.Sleep(500);
  59.                 OUT1_0V_OUT2_ON();
  60.                 Thread.Sleep(500);
  61.                 OUT1_0V_OUT2_0V();
  62.             }
  63.         }
  64.         static void SendSpi(byte[] tx,byte[] rx,int n)
  65.         {
  66.             if((spi!=null)&&(gpioController!=null)){
  67.                 Span<byte> toSend = stackalloc byte[n];
  68.                 Span<byte> toRead = stackalloc byte[n];
  69.                 for(int i=0;i<n;i++){
  70.                     toSend[i]=tx[i];
  71.                 }
  72.                 gpioController.Write(pin_csn,PinValue.Low);
  73.                 spi.TransferFullDuplex(toSend,toRead);
  74.                 gpioController.Write(pin_csn,PinValue.High);
  75.                 for(int i=0;i<n;i++){
  76.                     rx[i]=toRead[i];
  77.                 }
  78.             }
  79.         }
  80.         static void OUT1_ON_OUT2_ON()
  81.         {
  82.             if((gpioController!=null)&&(pins!=null)){
  83.                 gpioController.Write(pins[0],PinValue.Low);
  84.                 gpioController.Write(pins[2],PinValue.High);
  85.                 gpioController.Write(pins[3],PinValue.Low);
  86.                 gpioController.Write(pins[1],PinValue.High);
  87.             }
  88.         }
  89.          static void OUT1_ON_OUT2_0V()
  90.         {
  91.             if((gpioController!=null)&&(pins!=null)){
  92.                 gpioController.Write(pins[0],PinValue.Low);
  93.                 gpioController.Write(pins[2],PinValue.High);
  94.                 gpioController.Write(pins[3],PinValue.High);
  95.                 gpioController.Write(pins[1],PinValue.Low);
  96.             }
  97.         }
  98.          static void OUT1_0V_OUT2_ON()
  99.         {
  100.             if((gpioController!=null)&&(pins!=null)){
  101.                 gpioController.Write(pins[0],PinValue.High);
  102.                 gpioController.Write(pins[2],PinValue.Low);
  103.                 gpioController.Write(pins[3],PinValue.Low);
  104.                 gpioController.Write(pins[1],PinValue.High);
  105.             }
  106.         }
  107.          static void OUT1_0V_OUT2_0V()
  108.         {
  109.             if((gpioController!=null)&&(pins!=null)){
  110.                 gpioController.Write(pins[0],PinValue.High);
  111.                 gpioController.Write(pins[2],PinValue.Low);
  112.                 gpioController.Write(pins[3],PinValue.High);
  113.                 gpioController.Write(pins[1],PinValue.Low);
  114.             }
  115.         }
  116.     }
  117. }
ちなみにうちのLinuxをxubuntu24にクリーンインストールしたらsudo dotnet runってしないとftdiデバイスを認識してくれなくなった。usbadmとかrulesとかいじらんといかんかったような、、、