ちょっと Tea Time!? PICをI2Cスレーブで使って楽しよう(カウンタ製作)! 2025.10.4
MZ80化計画で、追加の基板として時間を計測できるものを作ろうと思います。
というのも、プログラムの実行時間などの計測に便利そうだからです。
いままでは目覚まし時計の秒針で確認したり、スマホのストップウオッチなどを
つかっていましたが、押し忘れたりしてどうしても誤差が大きくなりやすいです。
そこで、下記のICモジュールに目をつけました。
DS3231をつかったRTCです。
ただ、こいつは所謂時計というもので、1秒毎にしか時を刻むことができません。
まあ、これはこれで使おうかとおもますが、計測するにはすくなくとも0.1秒あるいは
0.01秒刻みでは計れるようにしたいものです。
RTCの定番ですが、1秒毎しかカウントできません。
カウンタ回路を設計しよう!ああ〜
そこで、細かく刻んだ時間計測ができようにカウンタ回路を設計です。
DS3231はI2Cで制御することもあり、おなじくその他もI2Cのみで制御できるように考えます。
発振器には周波数が可変できるようにSi5351をつかったモジュールを使うのが便利です。
ただ.こいつは最低周波数が8kHzなのですが低周波数では希望する周波数への設定が
難しいので、数MHz以上ではつかいたいとことです.となると、プリスケーラが必要です。
そして、計測できる時間レンジは16ビットではすこし物足りないので、24ビットは用意したいところです。
で、カウンタの値を読み込むI/Oエキステンダと組み合わせると下記のような回路になりました。
こりゃあ、結構大がかりだなあ〜。
時間計測の回路ですが、I2C制御できるように考えると、結構規模が大きくなりました。
必要なIC類を基板に乗せてみましが、結構スペースをとりそうです。
もっと簡単にならないかな?
ちょっと、回路図を書いた時点ですこし、気持ちが萎えてきました。
まあ、夜の夜長の工作にはうってつけではありますが、面倒そうです。
そういえば、以前に周波数カウンタを作ったときに、カウンタ部分をPICに
やらせたことがあります。同様に今回もPICをつかったら簡単にできるかもしれません。
しかし、制御はI2Cで行う必要があります。いままで、I2Cはマスタとしてでしか、
使ったことはありませんが、今回ではスレーブとしてつかう必要があります。
この際ですから、PICをI2Cスレーブで使うことを勉強しましょう!
ネットはやはり便利!
PICをI2Cスレーブで使う方法ですが、私のつかっているC言語のCCSコンパイラでの
サンプルになりそうなものが、ネットを探すといくつかでてきました。
やはりネットは便利だなあ〜。調べてみてわかったことは、
1.ネットのサンプルプログラムは比較的古めのPICが主体(最近のPICでは動かない)。
2.Qシリーズ(PIC18F27Q43など最近よくつかうPIC)では、スレーブ使用では問題点がある様子。
ということなので、PIC16F1938(28Pin)という、すこし前のPICで検討することにしました。
これが動けば、18PinのPIC16F1827でも動くでしょう。
まずは基本動作を確認
PIC間でI2Cで通信ができるかどうかを、簡単なプログラムを組んで確認です。
問題なく、動くことが確認できました。
PIC間での動作確認です。右側がスレーブ、左側がホスト側です。
機能はすこし欲張りましょう
基本的にやりたいことは、カウンター機能だけなのですが、
折角なので、その他必要になりそうな機能も付け加えることにしました。
すなわち、なにがしらのゲームにもできそうに、スイッチ入力やADC入力も
可能にしましょう。
ということで、基本的な仕様をまずはまとめます。
1.対象PIC: PIC16F1938
2,ピン機能
機能 | PIC16F1938 | 機能 | |||
未使用 | MCLR | 1 | 28 | B7 | IN7 |
ADC CH0 | A0 | 2 | 27 | B6 | IN6 |
ADC CH1 | A1 | 3 | 26 | B5 | IN5 |
ADC CH2 | A2 | 4 | 25 | B4 | IN4 |
ADC CH3 | A3 | 5 | 24 | B3 | IN3 |
未使用 | A4 | 6 | 23 | B2 | IN2 |
未使用 | A5 | 7 | 22 | B1 | OUT1 |
VSS | 8 | 21 | B0 | OUT0 | |
未使用 | A7 | 9 | 20 | VDD | |
未使用 | A6 | 10 | 19 | VSS | |
クロック入力 | C0 | 11 | 18 | C7 | 未使用 |
未使用 | C1 | 12 | 17 | C6 | 未使用 |
未使用 | C2 | 13 | 16 | C5 | 未使用 |
SCL | C3 | 14 | 15 | C4 | SDA |
3.I2C制御
a.I2Cアドレス:0xA0 (あまり使っていないところ)
b.書き込みと読み出しフォーマット
書き込みフォーマットは次の通り.I2Cアドレスの後はコマンド1バイトだけです.
<START>+<ADDRESS(0xA0):8ビット>+<ACK>+<コマンド:8Bit>+<ACK>+<STOP>
読み出しは、2バイト連速で読み出します。
<START>+<ADDRESS(0xA1):8ビット|0x01>+<ACK>+<読み出しHIGH
BYTE:8Bit>+<ACK>+
<読み出しLOW BYTE:8Bit>+<NACK>+<STOP>
c.コマンド機能
コマンド内容 | ||
コマンド | 機能 | 内容 |
0x00 | カウンターリセット | 計数カウンタ(32Bit)をリセットし計数開始 |
0x01 | カウンタストップ | 計数停止 ※このコマンドを受けた時点での計数値を保持. |
0x02 | カウンタHIGH-SET | 計数カウンタの上位16ビットを読み出しバッファーにセット ※I2Cでは、読み出しはホストのタイミングで行われるので、事前に送信準備を行います。 |
0x03 | カウンタLOW-SET | 計数カウンタの下位16ビットを読み出しバッファーにセット |
0x10 | ADC-CH0 | ADC-CH0の値を読み出しバッファーにセット(12ビット) ※ボリュームなどの接続を想定 |
0x11 | ADC-CH1 | ADC-CH1の値を読み出しバッファーにセット |
0x12 | ADC-CH2 | ADC-CH2の値を読み出しバッファーにセット |
0x13 | ADC-CH3 | ADC-CH3の値を読み出しバッファーにセット |
0x20 | PORT-B READ | PORT B0〜B7の値を読み出しバッファ(下位8ビット)にセット ※上位8ビットは0 ※ゲームパッドのスイッチなどを想定。時間的に間にあるかな? |
0x30 | PIN_B0 RESET | PIN B0をLOWレベルにセット ※LEDを点灯させる用途などには使えるかも. |
0x31 | PIN_B0 SET | PIN B0をHIGHレベルにセット |
0x32 | PIN_B1 RESET | PIN B1をLOWレベルにセット |
0x33 | PIN_B1 SET | PIN B1をHIGHレベルにセット |
d.使い方例
(1)計数カウンタの使い方
カンターリセット(0x00)コマンド送出 ※この時点でカウンタが動作を開始
<待ち時間>
カウンタストップ(0x01)コマンド送出
カウンタHIGH-SET(0x02)コマンド送出
ホスト側で2バイト読み込み(上位16ビット)
カウンタLOW-SET(0x03)コマンド送出
ホスト側で2バイト読み込み(下位16ビット)
ホスト側で上位、下位で32ビットカウンタの値を利用します.
(2)AD変換の使い方
たとえばAD-CH0の値を読む場合
ADC-CH0(0x10)コマンド送出 ※ADC-CH0のピンの電圧値をAD変換
ホスト側で2バイト読み込み ※PIC16F1938のADCは10ビットですが、12ビットフォーマットで格納
(3)ポートBの読み出し
PORT-B READ(0x20)コマンドを送出 ※PORT-Bはプルアップされるいるので、スイッチを直接接続可.
ホスト側で2バイト読み込み ※格納内容は下記の通り。
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 |
(4)ポートBの書き出し
たとえばB0に接続したLEDなどを点灯させる場合
PIN_B0 SET(0x31)コマンド送出
とまあ、こんな感じです.
まだまだバグはあるかもしれませんが、見つけたらその都度修正していきましょう.
ソース(CCSコンパイラ用)
バイナリー(PIC16F1938用)
回路図を書きましょう!
さて、このPICを使えば、作ろうと思っている回路はかなり簡単になるはずです.
まずは回路図を書いてみましょう.
すこし寂しいので、そこらへんにあるI2Cで動くモジュール基板(ADS1115, AHT20)も追加でとりつけました.
さて、夜な夜な製作していきましょう!
あっけなく完成 205.10.6
配線が少ないこともあり、わりとあっさりと完成してしまいました。
短時間で完成です。夜の夜長のお遊びにはならなかったなあ〜。
基本I2Cの配線だけなので、ほとんど配線らしきものもありません。
余分なI2Cプルアップ抵抗は除去
新設なことに、大方のモジュール基板にはI2Cプルアップ抵抗が入っています。
パラ接続すると、段々と抵抗値が小さくなってしまうので、1つだけ残して他は除去しておきました。
重複するI2Cプルアップ抵抗は除去しました。
GPIOのダイレクト出力した場合と、オープンドレイン設定で出力した場合の、
信号を比較してみましたが、10kΩのプルアップでも信号の遅延は
200ns程度のようなので、10kΩもあれば十分でしょう。
GPIOをダイレクトにドライブした場合 GPIOを10kΩのプルアップ抵抗でオープンドレイン出力の場合
プルアップ1kΩだと信号の立ち上がりが早いです。
使えるようにライブラリを構築しましょう 2025.10.7
ライブラリを作るには、各ICとのI2C通信ができていることを確認です。
安物には理由がある?
まずはRTCであるDS3231からのチェックです。今までに何度も使ったことがあるので、
簡単に動くと思いきや、全然動きません。I2C通信がまったくできない状況です。
ただ、ICからのクロック出力(32k)はでているので、IC自体は動いていると思われます。
ただ、手を変え品を変えて試してみるものの、全然動く気配がありません。
気になるのは、高精度RTCと謳っていながら、32kの出力の誤差が4000ppmくらいあります。
32.768kHzの出力なのですが、34kHzほどあります。
あと、バッテリー端子に半田が飛び散ったあとがありました。勿論、使用する前に清掃はしましたが、
ひょっとして半田付け(リフロー)を失敗してICが損傷しているのでは? という気がしてきました。
試しに、違うRTCに交換してみたところ、いとも簡単に動いてしまいました。
やっぱり、ICが壊れていた様子です.
やっぱり、安物には安物の理由があるのだなあ〜。
面倒なのは、壊れたモジュールの交換です.直接半田付けしているので、かなり無理やりにはずことになりました。
違うRTCを仮接続したら、いとも簡単に動いてしまいました。やはり、先につけたモジュールは
壊れていたようです。
交換といっても、ちょくせつ半田付けしているので、取り外しは無理やりでした。
交換したあとのRTCのクロック周波数はオシロでの表示値で32.769kHzでした。
32.768kHzが真値ですから、正常品の誤差はやはり小さいようです。
ちょっとストレス解消!
RTCの不良で時間がとられたこともあり、すこしストレス溜まり気味です。
こんな時は半付けに限ります(笑。
部品箱の肥やしにもなっている7SEGのLEDをつかって、半田付けしましょう!
目的はこのCPM80用の表示基板です。8桁程度の表示をつくります。
8桁となると、ダイナミック点灯となりそうですが、ここは半田付けの量を
増やすためスタティック点灯です。というより、ダイナミック点灯にすると、
その制御だけでソフトも必要になってくるので、簡単なアクセスで点灯できるようにと、
スタティック点灯にします。そうすれば、ちょっとしたソフトの実行状況の確認にも、
あまりマシンステートを使わずにできますからね。
ストレス解消と部品の在庫消化も兼ねてこれを半田付けしましょう!
秋月のアウトレット品です。60個で240円でした。
力技のスタティック点灯です(笑。
完成しました!
結構配線も多くて、ストレス解消になりました。
で、完成したのはいいのですが、いざ点灯させるとあまり明るくなかったなあ〜。
視認には十分だけど、昔のLEDなので、まあ点灯しているなあ〜という程度です。
各セグメントに流れる電流は3mAちょっとですが、今の高輝度LEDだとかなり明るいですが、
昔のLEDなので、普通に点灯している感じです。
まあ、目的が半田付けによるフラストレーション解消ですから、目的は達せられました。
(つづく)