2020年12月29日火曜日

遅ればせながらPIC手習い(その3)

  さて、話をプログラミングに戻そう。

 PICを使う上で最も重要なのはpinの使い方だ。全体を眺めると、20pin以下のPICの場合、刻印のある1番pinはVdd、上から見て反時計周りに最後のpinがVssになっているようだ。 他のpinには色々な信号を割り付ける事が出来るようになっている。

 16F18346の場合教本(英文マニュアル)のTable3に信号の一覧表がある。例えば一番上の(20pin-PDIPパッケージで)19番pinの場合デジタル入出力の名前はRA0、つまりAレジスタのビット0である。このpinには他にデフォルトでADコンバータのANA0(A0入力)、或いはDAコンバータの出力(DAC1OUT)等が割り付けられている。

また次のようにpinにデフォルトで割り付けられていない信号も存在する。

  このような信号とpinの割り付けをコントロールするのが内蔵されてるPPS(peripheral pin select)モジュールだ。16F18346の場合使えるポートはA,B,Cの3つである。ただし各ポート毎に全8ビットが使える訳ではなく、実際に16F18346で使えるのはA0~A5、B4~B7、C0~C7のみである。これらはそのままデジタル入出力の信号名RAn、RBn、RCn(nはビットを表す数字)に対応し、電源を除く18本のpinに割り振られている。これが次図である。


 各々のpinをデジタルで使うのかアナログで使うのかを決めるのがPPSのANSELxレジスタである(xはA,B,Cの何れか)。自分が使いたいモジュールの信号がデジタルなのかアナログなのかはマニュアルのTable1-3にある。ANSELxレジスタの対応するビットが1の端子はアナログ、0の端子はデジタル入出力となる。
 デジタル入出力の場合、TRISxレジスタでその端子が入力か出力かを指定する。もしアナログで使う場合はTRISxの対応するビットも1とする。つまりビット単位で次の設定を行うことになる。

 これらのレジスタをC言語でビット単位に扱うにはレジスタ名に「bits」を付けドットを付けた後にビット位置を表す定数名を書く。この定数名はレジスタ名+数字と同じではない場合もあるのでMPLABのプログラム編集画面では示される候補の中から選択すればよい。例えば3番pin(RA4)をデジタル入力に使うためには ANSELAbits.ANSA4=0、TRISAbits.TRISA4=1 とする。また ANSELA=0b10101010、TRISA=0b01010101 のようにレジスタの全ビットを同時に書き込む書き方もできる。

 機能をpinに割り振るためには、ANSELxレジスタとTRISxレジスタの対応するビットを指定する他、どの機能(モジュール)をどのpinに割り振るか指定する必要がある。これは実際には次のように sssPPS或いはRxnPPSのレジスタで指定する。

  • sssPPS  = pin番号  (入力の場合)
  • RxnPPS = 信号番号  (出力の場合)

 入力の場合、左辺のsssは前述のTable3に書いてある信号名、出力の場合のRxnはRC4の様なデジタルのpin名である。 右辺のpin数(実際のpinの番号ではない)や信号番号は英文マニュアルの13.8節のREGISTER 13-1, 13-2 に説明されているが英文なので慣れないと取り付きが悪い(割り付けの変更が出来ない信号もあるようである)。例えばRC7をUARTの出力、RC6をUARTの入力と定義したい場合は、
    RXPPS   = 0b10110;       //RX input is RC6
    RC7PPS = 0b10100;       //RC7 source is TX
 と記述する。

 以上のようにあるpinにある機能を割り付けるのに通常3つのレジスタに値を設定する必要がある。 例外としてデジタルの入出力を直接ソフトで扱う場合(例えばLEDを点滅する場合やスイッチのON/OFFの検知など)はANSELx、TRISxの2つを設定するだけで済む。

 pinの割り付けが済んで、初めてPICに内蔵される機能を使う事ができる。 

(つづく)


2020年12月20日日曜日

遅ればせながらPIC手習い(その2)

  目標としている回路はLEDで光を出し、その光をフォト検出器(トランジスタ)(PD)で受光し、その比を求める機械である。光の通し方によって透過型や反射型、散乱型など考えられるが回路的には同じで良いと考える(もし受光量が微弱であればロックインアンプを使う事も考える必要がある)。

 LEDの光量は電流値にほぼ比例する。またPDも受光量に比例した電流が得られる。そこでD/Aコンバータの出力電圧でLEDを電流駆動するとともに、PDに流れる電流を電圧に変換してA/Dコンバータに取り込む回路が必要である。PICのD/Aコンバータの外部回路の駆動能力は低いので高インピーダンスで受ける必要がある。またA/Dコンバータの入力インピーダンスも高くないので低インピーダンスで電圧を与える必要がある。いくつか試した後、次の回路に落ち着いた。 

 IC2は単電源のフルスイング汎用OPアンプであり、秋月で売ってるJRCのNJU7043Dを使った。Tr1はジャンク箱に転がっていた普通のNPNトランジスタ(2SC641)で、エミッタフォロワーで電流増幅に使っている。PIC 16F18346のD/Aコンバータの出力はデフォルトの19番pinに出力する。R1とLEDは直列に接続しているのでLEDにはR1と同じ電流が流れる。R1は電流を電圧に変換するための抵抗で、この電圧がD/Aコンバータの出力と同じになるようOPアンプに負帰還をかけている。

 R1の電圧はPICの16番pinからA/Dコンバータに取り込んでLEDの電流をモニターできるようにしている。

 R2はPDの出力電流を電圧に変換する。この電圧はOPアンプによる高インピーダンスのボルテージフォロワーで受けてPICの15番pinからA/Dコンバータに渡している。R2の値は取り扱う光量とフォトトランジスタの性能で決める。感度が足りなければPDをダーリントン接続にするとよい。

 D/Aコンバータの出力電圧をVdaとするとR1及びLEDに流れる電流Iledは Iled=Vda/R1である。このときTr1のエミッタ電圧VeはLEDの順方向電圧降下をVfとすると Ve=Vda+Vf となる。またベース電圧VbはTr1のベースエミッタ間の電圧をVbeとして、Vb= Vda+Vf+Vbe である。OPアンプとTr1のベースは直結しているからOPアンプの出力電圧Vop=Vbである。すなわちVop= Vda+Vf+Vbe という関係が成立する。
 OPアンプの最大出力電圧Vophを4.9V、Vbeを0.7Vとすると、4.9 = Vda+Vf+0.7、すなわち Vdah=4.2-Vf がこの回路が機能するD/Aコンバータの最大出力電圧である。このときLEDにIldehの電流を流すためにはR1の値は、R1=Vdah/Iledh とする必要がある。一般的にIldehは20mA(0.02A)である。
 もし赤色や赤外LED(Vf=1.2V)を使うとすると、Vdahは3.0Vとなり、R1は150Ωとなる。また白色LED(Vf=3.2V)を使うとVdahは1.0VでR1は50Ωとなる。

 このようにPDのLEDに順方向電圧降下Vfの大きい青や白色LEDを使う場合この回路ではダイナミックレンジが狭まるのでTr1をPNP型に替えたほうが良いかもしれない。この場合Tr1のEとCを逆に接続、さらにOPアンプの入力の+/-を逆にする(次図参照)。仮に順方向電圧降下が3.2V/20mAのLEDを使う場合R1は50Ω程度となる。またR9は20KΩ程度である。

 

 ちなみにI2Cを使った液晶表示器(LCD)との接続は次のように簡単である。液晶は秋月で売ってる AE-AQM1602A(16文字×2行)を使った。I2Cの信号線は複数のデバイスが接続できるバス形式のワイヤードOR接続である。従ってプルアップ抵抗が必要であり、LCD補助基板内のプルアップ抵抗を半田付けでONにしている。

 一応ソフトが上手く動くようになったので、ユニバーサル基板に組み上げた(写真は追加回路を含む)。


このとき電池駆動する事が前提なので3端子レギュレータを使った簡単な電源回路を組み込んだ。

 この回路の消費電流はせいぜい十数mAなので電源に20V程度を供給してもTO-92パッケージの小型3端子レギュレータの定格を超えない。さらに電池駆動を前提として電源電圧の監視回路を組み込んだ。具体的には電源電圧をR7とR8で分圧してA/Dコンバータに与えている。抵抗で分圧するのは 電源電圧が高くなってもA/Dコンバータの入力電圧がA/Dコンバータの上側の参照電圧(Vref+、約4Vに設定)以下に収まるようにするためである。

 最後に、ユーザインターフェイスとして次のような回路を組んだ。

 これらは動作確認用LEDとスイッチ及びジャンパーである。これらはソフトで機能を設定することで自由に使える。

(つづく)
 





2020年12月18日金曜日

遅ればせながらPIC手習い(その1)

 知人に頼まれた機械をどう作るか考えていたら、PICが良いんじゃないかというアイデアに辿り着いた。PICという名前やCPUやメモリ、様々な周辺回路が1つのICチップに内蔵されていて百円程度という夢のような値段で入手できる事は以前から知っており、PICの見本として八潮の秋月で適当に見繕って8pinと14pinの安いPICを買った以外は未だ手を出したことが無い。もちろんライター(プログラムの書き込み器)も持ち合わせていなかった。

 情報を得ようとネットで検索するとPICにはものすごく多くの種類があることが分かり、CPUのビット巾だけでも8ビットから32ビットまである。内蔵する周辺回路もてんこ盛りでかなり複雑に出来ている。私が半世紀近く前に卒論に使ったインテルの8080で同じものをディスクリートで組んだらラック1つを占有するだろう。こんなものが百円程度で買えるとはすごい世の中になったものである。一方こんなアナログとデジタルが混在した複雑なものを僅か8ピンや14ピン程度のICにどうやって押し込んでいるのか謎である。

 PICには通常OSや外部記憶の様なものはなく、プログラムをPICに直書きして動かす。プログラムの開発環境としてはPICの製造元の Microchip Technology 社がMPLAB X IDEというソフトを無償で提供しており、アセンブラやCで書けるようになっている。

 どのPICを使うにしてもライターは必須であるが、これも Microchip Technology 社がPickitという名前で販売しているほか中華製のコピーもあり、秋月電子でも独自に開発したPicKit2の互換品を売っている。ただPicKit2はデザインが古くMPLABではもはやサポートせずWindows10にも対応していないようなので(何故かいまだに秋月で売っている)今後の事を考え最新型のPicKit4を買った。PicKit4にPICを付けるアダプタは簡単なので、とりあえず必要な20pinのものを自作した。要はPicKitの5本の線を決まった足に直接接続するだけで20pin以下のPICには対応できるようだ。

 これでネットで出回っているLEDを点滅させる簡単なプログラム(バージョンが古いのかコンパイルエラーとなり少し修正する必要があった)を、昔見本として買っていた14pinのPIC 16F1823に書き込んで、ブレッドボードで組んだ回路で動かしてみると簡単に動いた。 プログラム本体は次のようなものだ。

     while(1){
        RA2 = 1;
        __delay_ms(100);
        RA2 = 0;
        __delay_ms(100);
    }

しかし、その前段階としての念仏が多い。例えば、

#pragma config FOSC = INTOSC
#pragma config WDTE = OFF

     OSCCON = 0x00;
     TRISA  = 0x00;

といった書き方で、これはPICの初期設定だったり、ハードウェアの操作であったりする訳であるが、ここに登場する大文字の名前がプログラム中には定義されておらず、ある意味仲間内での秘密の呪文であり私みたいな異教徒には理解できない言葉である(実際には#include文を辿っていけば発見できるかもしれないが、そこには値は定義されていても意味は含まれていない)。しかし、これらを知らないとPICのプログラミングは不可能。
 これを身に着けPIC教徒の洗礼を受けるには経典を読むしかない、ということでAmazonを彷徨って経典を探した。PICに関する経典は古いものが多く最新の情報が全般的に得られそうなものは少ない。とりあえず「C言語によるPICプログラミング大全」という経典を得た。これも元は改訂版が2009年という古いもののようであるが、2018年にリニューアルされ、さらにネットで2020年の最新情報が提供されている。

 これを手元に大航海に出た。 次の目標は16F18346を使ったとある回路である。この16F18346は20pinのICであり電源を除く18pinをプログラミングにより色々な目的に使うことが出来る。

 

 16F18346を使う理由はI2Cインターフェイスを持ちLCDと簡単に接続できてお勧めとのネット記事を見かけたからである。それにA/DやD/Aコンバータを内蔵しており、メモリーもそこそこあり目的には十分である。おまけに秋月で1個160円と安い。内部の構成は次のようになっている。


 とりあえず、ブレッドボードで組んでネット情報の再現を試みた。

  そこで分かったのは、私が買った経典は数多くあるPICの一般的な使い方について書いてあるが個々のPICの方言には対応していないという事である。LEDチカチカやLCD表示程度はネットで得た情報で何とかなったがそれでは応用が効かない。こうなったら原典に当たるしかない。

 そこでMicrochipから16F18346のマニュアル(PDF)をダウンロードした。英文でなんと500ページある。1ページ10円のコンビニ印刷でも全部印刷したら5千円!!!、160円のPICを使うのにこんなに経費がかかるとは。
 とりあえず必要そうな部分300ページ程を印刷した。

 (つづく)

 

2020年3月4日水曜日

ロト6は攻略できるか(その3)

 前回はロト6の当選数字のパターンを色々調べて出現傾向を調べてみた。実は昨年8月、数年ぶりに何となく買ったロト6が4等に当たり(それから助平根性が出て)その4等を原資として時々買い、その後暫くはあまり原資を減らすことなく買い続けることが出来たが10月頃に数字の出方が変わったようで全く当たらなくなり、11月に原資が底を突いた。
 そこで前からやりたかったAIによる予測に挑戦してみる事とした。いわゆる Deep Learning である。数年前に興味があって買った Deep Learning の本(Pythonで記述)が積読になっていたので、改めてこれを読んでロト6の Deep Learning に挑んた。残念ながら私はPythonは不慣れだしバグ大好きの弱い型付けの言語は嫌いなので同じような流れをFortranで書いてみた。
 Deep Learning の処理は Y=f(W*X+B) という基本演算で構成される。Xが入力でありYが出力 、Wが重み、Bがバイアスで、Wが2次元配列、Y,X,Bは1次元配列、fが非線形の出力関数である。WとBが任意パラメータで、これらの値が学習内容に相当する。この基本演算を複数層直列に積み上げることでAIを構成する(Wikipediaによると4層以上ないと Deep Learning とは言わないらしいが、以下は爺の手習いということで勘弁してもらいたい)。
 第1層の入力X1がAIの入力であり最終層の出力YnがAIの出力である。ロト6の場合X1は学習や予測に使う過去の抽選結果であり、その大きさは学習に使うデータ数(=抽選回数)×43、Ynは各数字の出現確率を表す43個の数値とした。1層のみのDeepLearning は単なるx元1次連立方程式の最適パラメータ決定になる。基本演算を2段以上積み重ねると層間に外部には出ない接続 Ym⇒Xm+1(隠れ層)が出来る。
 学習を行うためには最終的な演算結果(output)と真値(truth)の差(誤差、loss)をフィードバックしWとBの値を修正するカラクリが必要であり、誤差を 微分(dX/dY)的な係数を掛けながら処理とは逆方向に伝搬させ、係数(学習率?)を掛けてWやBを修正する。WやBの初期値には乱数を入れている。

 幸いな事にPythonで配列を計算するライブラリー(NumPy)がFortranの配列演算と相性が良いので助かった。出来上がったプログラムは全部で800行程度であるが、Deep Learning の処理自体は100行程度だろう。層数や隠れ層の幅、学習率は任意パラメータとしている。

 実際に2層の Deep Learning、隠れ層を100にしてロト6の過去データ(ボーナス数字を含む7つの数字)を読み込ませると、あっという間に過去データを学習し100%正解できるようになった。じっさい高々1500回にもならない過去データではAIと言うほどの性能も必要無いようだ。しかし過去データの最後の部分を隠して予想させてみるとこれが全く当たらない。次図は第1459回までのデータを使ったとある結果である(**が当たった数字)。

  ここで過去データを Deep Learning のやり方に従って学習データと評価データに分け、 学習データで学習させた後で評価データを評価してみた。そうすると繰り返し回数を多くして学習を進めるほど評価は悪化する。いわゆる過学習の状態なのだろう。ロト6は原理的にランダムな現象なので当然と言えば当然である。
 何回も試しているうちに、乱数による初期設定を変更しても隠れ層の幅を変更しても結果の傾向はあまり変わらない事に気づいた。簡単な Deep Learning でも過去のデータは十分学習できて方向性を持った予想を出せるようである(ただし予想結果は現実とは合っていないが)。次表は第1459回までのデータを使って色々パラメータを変えながら第1460回を予想した結果である(黄色マーカが本数字、緑はボーナス数字)。値は確率(%)を表す(水平方向の合計値はボーナス数字を含んでいて約700%になる。##は100%を超えた値)。

 3層の Deep Learning は学習に時間がかかる。2層だと50回も繰り返せば概ね安定するが3層だと収束するまで1桁上の学習が必要のようである。直近の予想確率例と抽選結果は次のようなもので当たるも八卦当たらぬも八卦といった所である。
(3層100回学習の結果)

 4層の(本来の)Deep Learning も試してみた。ある回の当選番号とその直前50回分との関係を全てのデータで500回ほど学習させ次回を予想する、これを初期値を変えて10回行い結果を平均してみたが、これには私のCore i5-9600K(3.7GHz)で80分ほどかかり、以下の表を作成するだけで計17時間以上かかった。この表では平均値(16.3)より大きい数字は赤色で表示している。理想的には100%の数字が7つあり他が0%なら完璧に予想出来ている訳であるが、結果を見ると予想出来ているとは言い難い。実際の抽選では必ずしも予想確率の高い赤い数字が選択されておらず、回によって確率が平均より大きいか小さいかに偏った数字が選択されている場合が多いように見受けられる。
(4層500回学習の結果)


 そこで最近13回のAI予想確率と実際に当たった数字の関係を調べた。
 43個の数字各々の予想確率で平均値(16.3)より大きいものと小さい物の数をカウントした(優勢の物に色を付けている)。また実際に選ばれた数字が予想確率分布の上下どちらに属するか当選数字分布を調べた。
 本来AI予想が当たっている場合、当選数字は全て予想確率の高いものの中から選ばれ、当選数字分布は(0,7)となる筈である。しかしこの表ではそうなっておらず、逆に予想確率の低い番号だけで構成されている場合もある事を示している。情報理論によると良く当たる天気予報と同様によく外す天気予報も同じくらい情報を持っている。当たらない事が分かっていればいわゆる逆張りをすればよい。
 そこで再度この表を見ると、当選数字は予想確率分布の数の多い方に偏っている場合が多いことに気づく(そうでない場合を灰色にした)。表で示した13回のうち9回がこれに当たる。 そういう意味ではこのAI予想はある程度の情報を与えていることになる。要するに予想確率分布の数の多い方に限定して張れば良い。そうすると選ぶべき数字が43の1/2~2/3程度に限定され当選確率が上がる事になる。
 
 この数をもっと限定できる適切な Deep Learning のパラメータがあるのかどうか、あれこれ探ってみたいが時間がかかりすぎるのが欠点。

・・・つづく・・・