年中部屋に置いておける、実用的な電子温湿度計を作ってみました。
温湿度センサーには、高精度な SHT31 搭載モジュールを採用。フルカラー7セグLEDにより、昨日との気温差表示や、ランダムイルミネーション表示を行います。
(大きい文字は温度、小さい文字は湿度です)
また、就寝時に消灯すると、表示を完全にOFFします。
センサー(左の青いやつ)は着脱可能にしてあるので、劣化させてしまった場合の交換や、別のセンサーにすることもできます。
電源はACアダプタで5V入力、消費電流は30mA~70mA程度、表示OFF時は3mAです。
こんな感じのです
温度表示のカラーリングには、3つのモードを設けました。
ノーマルモード
前日の同じ時刻との温度差を色で表します。色は、HSVカラーモデルの色相と彩度を温度差に応じて連続的に変化させたもので、暑いほど赤方向へ、寒いほど青方向へ、温度差が小さいほど色が薄くなるようにしてみました。
また、温度差が小さい時は色の差も小さく分かりにくいので、ドットの色で高いか低いかを示すようにしています。
昨日より高いけど、差が+0.2℃と小さい。
数字の色は、肉眼だと薄い黄緑に見えます。
昨日と同じ(±0.2℃未満)
色相では緑の位置になりますが、彩度が0のため白色になります。
昨日より低いけど、差が-0.2℃と小さい。
数字の色は、肉眼だと薄い青緑に見えます。
写真だとムラが見えますが、実際にはキレイな発色でムラも目立ちません。
なお、1日分の温度データが集まるまでは比較できませんので、それまでは1桁目のドットを緑色●で表示して通知します。
フィーバーモード
桁単位で、色相と彩度を1秒ごとにランダムに変化させるイルミネーションモード。
彩度も変化するのでダイナミックな色彩が楽しめます。
カオスモード
LED単位で、色相のみを1秒ごとにランダムに変化させるイルミネーションモード。
見にくくならないようにパステル調で美しく表示します。
フィーバーモードとカオスモードは、2つ合わせて1日に数回、3分間だけなります。いつなるかは分かりません。その日見れたらラッキー。
温湿度計の仕組み
電子温度センサーや湿度センサーの仕組みを簡単に説明します。
温度センサー
温度センサーは、温度によって電気抵抗や電圧が変化する素子(セラミックや半導体など)を利用し、その両端電圧を計測することで温度を測ります。
サーミスタと呼ばれる温度検知用に作られている部品もありますが、電子温度計ではマイコンなどに入力しやすい温度センサーと銘打っている部品を使うのが一般的です。
出力は、アナログ(電圧値そのもの)を出力するタイプと、デジタル(I2Cや独自通信)を出力するタイプがあり、現在ではデジタル方式が主流です。デジタル方式であっても、内部ではアナログをADCを使ってデジタルに変換しているだけであり、原理は同じです。
湿度センサー
昔からある温度センサーとは違い、湿度センサーは最近になって出てきたセンサーです。
基本的には、電極の間に感湿剤を配置し、電流を流すことで空気中の水分による電気抵抗の変化を、電圧の変化として捉えます。
最近出てきたということもあり、アナログ出力のものはあまり見かけません。多くがデジタル方式で、しかも温度センサーとセットになっているものが普通です。
湿度センサーでは、一般に感湿剤がデリケートなので注意が必要です。例えば、揮発性の高い溶剤などが感湿剤に触れると、劣化して精度が著しく落ちてしまったりします。
電子工作で使う温度・湿度センサー
今では温度・湿度センサーにはたくさんの種類があって入手性も良いのですが、その中から電子工作でもよく使われている代表的なものを、ほんの一部ですがご紹介します。
※今の所、秋月電子やRSオンラインなどで入手可能です。
20年くらい前からあるナショセミの温度センサーで、出力はアナログです。「D」バージョンでは、0~100℃まで計測可能で、精度は±1.0℃。TO92パッケージで反応も早く、例えば手を10cmくらいの距離に近づけると、約10秒くらいで0.1℃の温度上昇を検知します。
Maxim社のデジタル出力温度センサー。-55~125℃まで計測可能で、精度は±0.5℃です。独自の1wire方式シリアル通信を使って温度を読み出す方式なので、マイコンのポート1つあれば温度を測ることができます。ネットでの作例もよく見かけます。
湿度も測れる温湿度センサーモジュールです。中国製で、安価な家電などでも利用されています。温度は-40~80℃まで計測可能で、精度は±0.5℃。湿度は0~99.9%RHまで計測可能です。ピンが4本出ていますが、使われているのは3本だけで、こちらも1線式シリアル通信方式です。
構成と回路図
要の温湿度計は、センシリオン社の SHT31-DIS で、これを搭載している秋月電子のモジュールキット AE-SHT31 を使っています。
このチップはとても小さく、単品のハンダ付けは感湿部を傷めてしまう可能性が高いことから、このようなモジュールを使うに限ります。
なお、EEPROMは、1日分の計測温度を記録するために使っているだけで、温湿度計の機能に必須のものではありません。
回路の詳細
まず電源ですが、ACアダプタから5Vを入力し、3.6V系統と3.0V系統をLDOで作っています。2系統あるのは、なるべく低い電圧にして消費電力と発熱を抑えるためで、フルカラー7セグ用とその他用に分けています。
フルカラー7セグLEDの最低供給電圧は3.5Vとなっていて、これを下回ると急に色味がおかしくなるため、それ以上下げることはできません。データシート
一方、温湿度センサーやPICなどはもっと低い電圧でも動きますが、白色7セグLEDは3.0Vを下回ると急に暗くなってくるので、これもそれ以上さげれません。しかし、下限である3.0Vとすることで、通常は挿入するLEDの電流制限抵抗を省くことができるので、かなりシンプルな7セグ制御回路となっています。
結果的には、大体50mA程度の消費電流に抑えることができましたので、回路からの発熱もなく温度計測に影響することはほとんどないはずです。
LDO(IC2)のSD端子を使って、消灯時にはフルカラーLEDへの電源供給を完全にOFFします。
プルアップ抵抗(R1)は、データシートでは10Kを使っていますが、なんと!それではONしないことがあったので4.7Kにしました。PIC側のポート(POW)は、OpenDrainに設定します。
フルカラーLED3つは、このようにカスケード接続するだけですので簡単です。
PICと電圧が違いますが、LED側の入力(VIH)は0.7VDD(2.5V)なので、レベル変換しなくてもそのままPIC側から駆動できます。
温湿度センサー SHT31-DIS のデータシートには、電源ノイズに対する耐性については書かれていないので不明ですが、「電源電圧に基づいた計測結果の校正も行う」との記載があるので、実は影響を受ける可能性もあります。
なので、念のためにフィルタを使って、ノイズ源である白色7セグ回路を分離することにしました。基板のパターンにも配慮したので7セグのノイズが影響することはありません。
なお、セットの小型化のために、C5には高分子チップ電解コンデンサーを、C1とC2には、100uFのチップコンデンサを使っています。
どちらも、秋月電子やRSオンラインなどで手に入ります。
最近は、小さくて大容量のコンデンサがあるので助かりますね。
パーツと入手先
今作は小型化するために、ほぼ全ての部品に表面実装品を使いました。
主要な部品は秋月電子、チップコンデンサや抵抗は、MOUSERやRSオンラインなどでまとめ買いしたものです。
主要パーツ
AE-SHT31(秋月電子)取扱説明書
SENSIRION 社の高精度温湿度センサー搭載モジュールで、基板の形がとても良いですね。デリケートですので取扱いには注意が必要です。
※センサー本体 SHT31-DIS の データシート
RGB Digit 1″ full color Segmen(秋月電子)データシート
世界で売られているフルカラー7セグメントLED。品番らしい品番はないようです。秋月電子では、電源は5V1Aをおすすめと書いてありますが、屋内で使うならそんなに必要ありません。
PIC16F1705T-I/SL(秋月電子)データシート
比較的新しいので何かと改善されている汎用PIC。例えば、オペアンプが内蔵されていたり、全てのポートがOpenDrainに設定できるなど。ただ、EEPROMが内蔵されていないので要注意です。
フリーカットアクリル板(はざいや)
今回は小さいアクリル加工なのでなんだか申し訳ないような気もしましたが、今回もこちらで注文しました。「thermo_outline.zip」に含まれるアクリルオーダー図面を参考にしてください。
Full color 7Seg の使用感について
データシートでは分からない実際の使用感について記載しておきます。
明るさと消費電流
この7セグLEDは、WS2812B というマイコン内蔵のRGB LEDを8個使って作られています。データシート
WS2812B は、野外のイルミネーションにも使えるほどハイパワーなLED。それが8個も入っているため、最も明るく光らせた状態では直視できないほど明るく、照明用のLEDに引けをとらないくらいです。
色の指定は、24bitのRGB値ですから、理論上は R:0xFF G:0xFF B:0xFF まで指定できるハズなんですが、明るすぎるため実際にはそんな大きな値で使うことはできません。データシートでは、100(R:0x64 G:0x64 B:0x64)くらいまでが推奨されているようですが、それでも少し眩しいですね。
なので、室内で「落ち着いて光っている表示器」として使う場合ですと、R:0x15 G:0x15 B:0x15 程度までしか上げられないんです。それでも少し明るすぎるくらいで…
主観になりますが、1セグメントに電源電圧3.5Vで、白の「8」を表示した場合の明るさの度合いと、消費電流の実測値を記載しておきます。
※設定値が 0x04 となっている場合は、R:0x04 G:0x04 B:0x04 を意味します
設定値(x3) | 明るさ | 消費電流 |
---|---|---|
0x00 | OFF | 3mA |
0x04 | 暗い | 4mA |
0x08 | やや暗い | 5mA |
0x0C | 普通 | 7mA |
0x10 | 普通+ | 9.5mA |
0x12 | やや明るい | 11mA |
0x14 | 明るい | 12mA |
0x18 | 明るい | 16mA |
0x20 | 十分明るい | 25mA |
0x40 | 眩しい | 62mA |
0xFF | 直視つらい | 281mA |
電源電圧と色味・発色
データシート上では供給電圧は、3.5V~5.3Vとなっています。
実際に3.5Vを下回らせてみると急に色味がおかしくなります。ちょっとくらい低くても大丈夫っしょとかって、都合の良い3.3Vで使うと色がおかしくなるので要注意です。
逆に高い場合はそんなには変わりません。例えば、3.5Vと5.0Vで比べると、5.0Vの方が若干キレイになる感はありますが、さほど大きな違いはないです。
なので、思いっきり光らせるとかしない限り、無理して5Vにしなくても良いと思います。
設定値と色味・発色
フルカラーRGBのLEDは、3色が綺麗に混ざらず色ムラが出るという問題があります。
この7セグも同じで、暗くすればするほど色ムラが目立つようになり、ホワイトバランスも崩れてきます。素子のバラツキによる色の違いも顕著に現れます。
設定値を0x04位まで下げるとこんな感じに。
実際ここまで暗くして使うことはないのですが、写真だと分かりにくくなるのであえて暗くしています。
そこで本作では、各LEDごとにソフトウェアによってRGB値の補正を行い、素子のバラツキを抑えるようにしています。詳細は、ソフトウェアを参照してください。
製作手順
本作は、基板が外部に露出しているため、ガラスエポキシ基板を使います。紙フェノールだと、約5年を経過するあたりから色が変わってきますのでオススメしません。
より詳しく⇒プリント基板の自作!感光基板を使った作り方で簡単製作
緑色は銅箔、黄色は部品外形、灰色はジャンパーなどを表す補助線です。
150x100mmの感光基板を使うのですが、今回はサイズが微妙で余分な部分が多く、もったいないです。配布しているパターン図には、同じパターンを2つ描いています。
サンハヤトの感光基板専用インクジェットフィルムPF-3R-A4にパターンを印刷します。
「基板.pbf」をmikanで開いて、外形、hole、solder、裏B、裏C の5つのレイヤを印刷。
今回は、この後すぐにハンダ付け作業に入るし、サイズも小さいのでフラックスは塗りませんでした。
そのかわり、手洗いを徹底します。
まずは基板裏側。チップコンデンサと抵抗をハンダ付けしていきます。
0と書かれたチップ抵抗は0Ω。つまり、ジャンパーとして使っています。下を通るパターンとのショートに注意。
基板を両サイドで固定するための金具をハンダ付けします。これは秋月で売っている、基板用ネジ端子という金具。
まずは、脚をペンチでカットします(左)
垂直になって基板の側面と面一になるなら、どんなやり方でも良いでしょう。
浮きがないように付けます。写真ではあまりハンダが付いていないように見えますが、しっかり付いています。
穴位置の多少のズレは、アクリル側の穴のマージンや、接着位置で吸収できるので大丈夫。
次に温湿度センサー AE-SHT31 の取り付け。
当作では、このモジュールを完成後も着脱できるように設計してあります。
まず、メスのL型ピンソケット(6ピン)の1ピンをカットして5ピンにします。
次に、AE-SHT31 にピンヘッダを裏側から手早くハンダ付けします。付属のL型ではなく、ストレートのピンヘッダを使います。
ただ、そのまま付けるのではなくて、先程のメス側に挿入した時にちょうど良い長さになるようにピンの長さを調整します。
センサーを下に向けているのは、感湿部にホコリが入らないようにするためです。年中部屋に置いておくので。
表面実装のDCジャックをハンダ付けして、裏面完了です。AE-SHT31 は全体が完成するまで外しておいたほうが良いでしょう。
この後、表側が完成したら、コネクタ類をマスキングしてハヤコートを吹き付けておきます。
基板の表側です。LED以外をハンダ付けしたところ。一部のジャンパー線やセラミックコンデンサは、7セグLEDの下に隠れます。
CDSは脚を長めにハンダ付けし、このように上を向けておきます(下側が上方になる)
最後に7セグLEDです。
フルカラーの方はしっかり押し込んで基板にピッタリ付けます。白色の方は、フルカラーと面一になるように、このように高さを合わせてハンダ付けします。
両7セグとも、フロントアクリルパネルにぴったり接触させるイメージですね。
ここまで来たら、2つのPICにファームを書き込んで動作確認をします。
メイン側のソースには、火入れ時のテストをするためのコードを記述してありますので、ブレークを当ててポートの検証などを行います。
いきなり動かしてみても良いですが…
本体にサイドパネルを軽くネジ留めして、フロントパネルに置いて固定します。
位置合わせをしたら、アクリル接着剤を流し込みます。3分もすると暫定固着しますが、まだまだ弱いので力を加えすぎてはいけません。
インスペクションルーペ SL-54 電子工作、特に表面実装部品のハンダ付けに超オススメ!というか、これがないと作れないと言ってもいいんじゃないでしょうか。 |
アクリル接着剤 30ml 注射器付き。水のようにサラサラした液体で、隙間に染み込ませて使います。アクリルだけでなくABS樹脂などにも使えます。 |
ソフトウェア
全体制御PIC用と、7セグ制御PIC用の2つのプロジェクトになります。
全体制御の処理内容
基本的に、温湿度取得と表示をグルグル回すだけですが、2つのタイマー割込みを使って、次の処理も行っています。
- Tickカウンタ
- 1秒周期生成
温湿度センサー SHT31 の制御
データシートにはいくつかコマンドが載っていますが、当作では、次の基本的なことをやっているだけで簡単です。
- 起動時に念のためソフトウェアリセットコマンドを送る
- 定期的に単発測定コマンドで温湿度を読み出す
温湿度の測定は「精度:高、クロックストレッチ:有効」で行っています。
(コマンドコード 0x2C06)
PICのペリフェラルはクロックストレッチをサポートしているので、2バイトのコマンドを送信後、普通に6バイトの測定データを受信するという操作だけでOK。
「精度:高」の場合、測定に最大15msかかるので、コマンド送信後すぐに受信するとその分待たされることになります。
受信データには、CRC8によるチェックサムが含まれています。無視しようかとも思いましたが、一応CRC8の計算とチェックを行うようにしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | static BYTE CRC8(BYTE* pSrc, int nLen) { #define MSB_CRC8 0x31 // 多項式(x8 + x5 + x4 + x1) #define INI_VAL 0xFF // 初期値 BYTE nCRC8 = INI_VAL; do { nCRC8 ^= *pSrc++; for (int i = 0 ; i < 8 ; i++) { if (nCRC8 & 0x80) { nCRC8 <<= 1; nCRC8 ^= MSB_CRC8; } else { nCRC8 <<= 1; } } } while (--nLen > 0); return nCRC8; } |
Full color 7Seg
7セグの制御は独自の単線シリアル通信で行います。内部で WS2812B が8個カスケード接続されているため、通信仕様は WS2812B のものとなっています。
WS2812B 1つにつき、3Byte(8×3)のRGB値を設定しますので、7セグ1つでは24Byteのデータ量となりますね。
送信方法はいたって簡単。下図のように最初のLED(D1)に対して、GRBGRBGRB…という具合に次々にデータを送信します。
DINに入力された最初の24ビットは自分が取得してDOには何も出力しませんが(LOレベルのまま)、24ビット以降はそのまま出力します。このように動作するLEDをズラッとカスケード接続しているので、単線で全部のLEDへデータを送信することができます。
最初に戻る場合は、リセットコード(50us以上LOレベル)を送出します。
仕様上は、1ビットあたり1.25us(0.8us+0.45us)で、800KHzの信号となっていますが、パルスの幅はさほど厳格ではありません。
というか、最初のHIレベルの期間が重要で、これで0か1かが決定されます。LOの期間は適当でも大丈夫な感じです。(50usを超えるとリセット)
ただ、C言語による 8bit PICのポート操作でこれをやろうとすると、FOSC 32MHzで駆動したとしても厳しいです。特に、XC8のフリー版では最適化が効かないのでムリでしょう。なので、当作ではこの部分だけはアセンブラで記述し、C言語と組み合わせてプログラムしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #define PORT_DAT LATC3 #define PORT_DAT_ASM "LATC & 7FH, 3" static BYTE s_bData __at(0x70); // 共通RAM領域に置く static void SetLED1(BYTE* pData, int nSize) { #define SENDBIT(b) \ asm("BSF _" PORT_DAT_ASM); \ asm("BTFSC _s_bData, "#b); \ asm("GOTO CODE1_"#b); \ \ /* 0 code(375ns→750ns) */ \ asm("BCF _" PORT_DAT_ASM); \ asm("NOP"); asm("NOP"); asm("NOP"); \ asm("GOTO END_"#b); \ \ /* 1 code(750ns→375ns) */ \ asm("CODE1_"#b":"); \ asm("NOP"); asm("NOP"); \ asm("BCF _" PORT_DAT_ASM); \ asm("NOP"); asm("NOP"); \ asm("END_"#b":"); GIE = 0; // 割込みを禁止する PORT_DAT = 0; for(int i = 0; i < nSize; i++) { s_bData = pData[i]; asm("MOVLB 2"); // select BANK2 (for LATC) SENDBIT(7); SENDBIT(6); SENDBIT(5); SENDBIT(4); SENDBIT(3); SENDBIT(2); SENDBIT(1); SENDBIT(0); } GIE = 1; WaitMs(1); } |
アセンブラコードの中ではバンクセレクトしていません。送信データ(s_bData)をアドレス指定「__at()」を使って共通領域に置いており、Bank1の時はPORTCへ、Bank2の時はLATCへの書き込み動作となるので、Bank1/2どちらでも動作します。
PIC16F1705 を32MHzで駆動すると、1命令サイクルは 125ns(0.125us)なので、仕様に近い 375ns と 750ns のパルス幅を作ることができ、約888KHz の信号となります。
上のプログラムで出力される実際の信号波形。
(PIC16F1705 RC3(7pin)へ出力)
「0」送出時のHIの期間、375us のパルス。
なお「1」の時の波形は、HI/LOの長さが逆になるだけです。
これは、バイトとバイトの間に約4.6usの空白期間がある様子を示しています。
C言語のforループの処理などで長くなっているんですが、50nsを超えないのでリセットとは認識されず問題ありません。
色のバラツキ補正
先にも書きましたが、フルカラーLEDには個体のバラツキが色合いに出ます。それを修正するために、簡単な色補正を行うようにしてみました。
全てのLEDのRGBごとに設定値(%)を設け、単純に掛けているだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | static void Correct(BYTE* pDst, BYTE* pSrc, BYTE nNo, BYTE nCell) { // 補正データ(%) Red #define BASE_R (17) const BYTE cDatR[3][7] = { // A B C D E F G { 96, 101, 101, 100, 101, 101, 96 }, // 1桁 { 94, 100, 91, 100, 100, 100, 90 }, // 2桁 { 100, 100, 97, 100, 99, 100, 98 }, // 3桁 }; // 補正データ(%) Green #define BASE_G (23) const BYTE cDatG[3][7] = { // A B C D E F G { 110, 111, 105, 98, 114, 111, 98 }, // 1桁 { 100, 100, 127, 101, 104, 112, 99 }, // 2桁 { 112, 105, 107, 101, 100, 108, 105 }, // 3桁 }; // 補正データ(%) Blue #define BASE_B (-5) const BYTE cDatB[3][7] = { // A B C D E F G { 99, 100, 100, 100, 99, 100, 100 }, // 1桁 { 99, 99, 102, 99, 100, 100, 100 }, // 2桁 { 99, 99, 99, 100, 100, 100, 100 }, // 3桁 }; // 元の値✕補正率(%) pDst[0] = (pSrc[0] * (cDatR[nNo][nCell] + BASE_R)) / 100; pDst[1] = (pSrc[1] * (cDatG[nNo][nCell] + BASE_G)) / 100; pDst[2] = (pSrc[2] * (cDatB[nNo][nCell] + BASE_B)) / 100; } |
実際のLEDの特性はリニアではないので簡易的な補正となりますが、それでも見た目への効果は大きく、見違えるように綺麗になります。
当然ですが、上のデータは当方所有のLEDに固有の値となっているので、それぞれのLEDに合わせて新たに補正データを編集する必要があります。(公開しているソースでは全て100(補正なし)としております)
やり方は、白色の「8」を表示させ、文字を構成する全てのLEDが一様になるべく白く見えるように、コツコツと補正データを訂正していきます。BASE_R/BASE_G/BASE_B では、全体のホワイトバランスを調整できます。
写真ではホント分かりにくいのですが、肉眼で見ると各セルの色の違いが良く分かります。
当方の場合、赤丸の部分だけが特に他と違っていて赤色っぽくなっていたのですが、補正後は違いが分からないレベルまでになりました。
ちなみに、明るくすればするほど色のバラツキは小さくなるのですが、部屋に置く表示器としては明るすぎになります。
カラーモデル
色の算出には、HSVカラーモデルを使用しています。明るさを変えずに色だけを変えるとかするためですね。最終的にはRGBへ変換するのですが、問題はこの変換プログラム。
普通は少数演算による変換を行うのですが、8bit PIC には荷が重いんです。
整数演算にするの面倒やな~と思っていたら、こちらでたまたま見つけたコードが使えそうだったので、少し変更して利用させていただきました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | static void HSV2RGB(BYTE* pRGB, BYTE* pHSV) { if (pHSV[1] == 0) // S { pRGB[0] = pHSV[2]; // V pRGB[1] = pHSV[2]; // V pRGB[2] = pHSV[2]; // V return; } #define FIELD_BITS 8 #define RGB_MULTIPLY 0 #define FIELD_MAX 256 DWORD P = 6; DWORD R = FIELD_BITS; DWORD S = FIELD_BITS + RGB_MULTIPLY; DWORD M = FIELD_MAX; DWORD h = pHSV[0]; // 0~255 DWORD s = pHSV[1]; // 0~255 DWORD v = pHSV[2]; // 0~255 DWORD d = (h * P) % (P << R) >> R; DWORD n = (h * P - (d << R)); DWORD x = (v * ((M ) - (s ))) >> ( S); DWORD y = (v * ((M << R) - (s * (0 + n)))) >> (R + S); DWORD z = (v * ((M << R) - (s * (M - n)))) >> (R + S); DWORD w = v >> RGB_MULTIPLY; switch( d ) { case 0: pRGB[0] = w; pRGB[1] = z; pRGB[2] = x; return; case 1: pRGB[0] = y; pRGB[1] = w; pRGB[2] = x; return; case 2: pRGB[0] = x; pRGB[1] = w; pRGB[2] = z; return; case 3: pRGB[0] = x; pRGB[1] = y; pRGB[2] = w; return; case 4: pRGB[0] = z; pRGB[1] = x; pRGB[2] = w; return; case 5: pRGB[0] = w; pRGB[1] = x; pRGB[2] = y; return; } } |
本来は、Hの入力は360°にすべきですが、この例では255までとなっています。しかし、本作ではそんなに高い分解能で色を出さないので問題ありません。
疑似乱数生成
本作のプログラムでは乱数を使用します。まあ、余りこだわる必要もないのですが、MicrochipのC言語のrand()はイマイチなので・・・
有名なものに「メルセンヌ・ツイスタ」というアルゴリズムがありますが、8bit PIC ではメモリが足りないので実装不可。そこで、軽量なわりに優秀な「Xorshift」というアルゴリズムを使っています。論文はこちら→ Xorshift RNGs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | static WORD Rand(WORD wMax) { static DWORD x = 123456789; static DWORD y = 362436069; static DWORD z = 521288629; static DWORD w = 88675123; DWORD t = (x ^ (x << 11)); x = y; y = z; z = w; w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); return w % (wMax + 1); } |
起動時にその時の光量に基づいて読み捨てることでSeedするようにしています。
7セグ制御の処理内容
I2C経由で受け取った3桁数値に対応する表示データをポートに出力する。それをタイマー割り込み(1KHz周期)でグルグル回してダイナミック表示しているだけです。
I2Cはスレーブ側となるようにSSPを設定し、クロックストレッチを有効にしています。マスター側へ送信することはありません。
ソースとビルド
メインPIC用と7セグPIC用の、2つのプロジェクトを含んでいます。
解凍して出てきたプロジェクトをパソコン上の適当な場所にコピーして、MPLAB X で開けばビルドできます。ビルドに必要な外部ライブラリなどはありません。
使用したIDEのバージョンは下記の通り。
MPLAB X IDE:v5.05
XC8:v2.00
MPLAB- X IDE | Microchip Technology Inc.
書き込みやデバッグには PICkit3 を使いました。
PICkit3 Microchip正規品。PICへのプログラムの書き込やデバッグができます。最近では安い中国製の互換品も出回っていますが微妙です。 |
ダウンロード・ツール
製作に使用した全ファイルです。無断で二次配布することはご遠慮ください。ご紹介いただく場合は当記事へのリンクを張ってください。連絡は不要です。