半年ほど前にAmazonで購入した 8桁の7セグLEDとスイッチがついたモジュールをようやく動かしてみました。
このモジュールは、8桁の7セグLEDと、赤色LED 8個、スイッチ(Key) 8個がついて、日本のAmazonでも¥280程度とかなり安いです。(私はCalloyで購入しました)
うまく使えれば、操作パネルとしても使えそうです。LED制御用のコントローラーはTitan Micro Electronics 製のTM1638 というICが使われています。このTM1638は中国では結構ポピュラーなのでしょうかね。これに近い型番でTM1637, TM1640もあるようですが、これらとは少し制御方法が異なるようです。
Arduinoの環境ではTM1638用のライブラリがGitHubに公開されているようですが、ベタのSTM32用としては無いようですので、今回自前で作ってみる事にしました。(HALは使用します)
TM1638の仕様書をみると、STBのポートを別に用意すれば、CLOCK, DIOは共通で複数のTM1638ボードを繋げられますので、C++で作ってみる事にしました。(STM32でC++を使うのは初めてです。やってみたかっただけですけど)
また、今回環境はできたてのSTM32CubeIDE(1.0.0)を使ってみました。
一度、sw4stm32で作ったプロジェクトをC++に変更しようとして上手くいきませんでしたが、今回は上手く行きました。
今回もSTM32F103C8T6を使います。(だってROM 64k, RAM 20kもあってめっちゃ安いんだもん) ただし、今回はBlue Pillではなく、STM32_Smartと呼ばれている(?) JTAGコネクタ付きのボードを使いました。ついでに、赤外線リモコン送信機のデモも兼ねる事にします。(先日の記事の回路につないでみてね)
STM32CubeIDEからCubeMXを呼び出してGPIOとタイマーを設定します。
JTAGで5本ポートを消費しています。TMXX_DO(PB15), TMXX_CLK(PB13), TMXX_CS(PB12)が各々TM1638用の制御ピンDIO,CLK,STBに対応します。一応SPIでも使えるポートですが、今回はポーリングで処理します。
TMXX_DOは、スイッチ(key, S1-S7)の状態を読み出すためI/Oに対応する必要があります。STM32では出力ポートに設定していても、ポートのレベルを読み出すことができます。つまり、出力I/FをOpen Drainに設定し、出力をHにしておけば、ポートの状態を入力レジスタから読み出すことができます。
タイマーは、ちょっと勿体無いけど、赤外線(IR)リモコンのコードを出力するために2本使います。
TIM4がキャリアパルス生成用です。TIM4の出力はTIM4_CH1(PB6)から出力されます。先日の回路図のSTM32_PB6に接続するとリモコン送信機になります。
TIM3でキャリアパルスのON,OFF時間を設定します。TIM3のクロックは1MHz(1μsec)になるように設定します。CPUはSTM32F103の最大クロック72MHzで動作させていますので、Prescalerで72分周します。
設定が終了したら、CubeMXからGenerate Codeを選択します。コードが生成されたら変更する前に一度ビルドしてエラーが無いことを確認しましょう。
C++でプログラムするには、main.cをmain.cppにRenameします。(別のファイルを用意して、C++関係の処理をそこでやってしまうのであれば、不要かも)
CubeMX で何かいじる前にはmain.cppをmain.cに変更し、コードを生成したらmain.cppに戻すという処理が必要です。(多分)
処理用のタイマーはSysTickを使います。SysTickは1msec周期でコールされます。その中で必要なカウンタをカウントダウンしています。mainループ内でのwaitはこのタイマーを利用しています。
TM1638は、16バイトのレジスタに設定したデータを自動でリフレッシュします。Key入力も自動でスキャンします。多重押しも検出できます。
STB(ストローブ)立ち下がり後の最初のデータがコマンドです。
Display Registerは高々16バイトですので、毎回全レジスタを書き換える事にします。LEDやdotも同じレジスタに配置されていますので、ワーク用に16バイトのメモリを用意し、これに書き込むようにします。このデータは別の関数で周期的にTM1638へ送る事にします。
Keyのスキャン情報は、4バイトのデータとして読み出します。このTM1638ボードには8個のKey(S1-S7)が付いています、多重押しも判定できるようにKeyのON,OFFを各bitに割り当てます。TM1638のDIOは入力と出力を兼ねています。
STM32F103では、出力をOD(Open Drain)に設定し出力を”H”にする事で、出力のFETがOFFになり、プルアップ抵抗によりHになります。TM1638の出力はプルアップ抵抗をFET(or トランジスタ)で引っ張る事でLにすることができます。プルアップ抵抗はボードに実装されています。
ODではなくC-MOS出力に設定した場合は、上側のFETがONになり低インピーダンスでドライブされる事になりますので、TM1638の出力とぶつかる事になり正常に動作しません。壊れる恐れもあります。
赤外線リモコンの信号は、キャリア信号を出力するmark期間と、何も出力しないspace期間とで構成されます。各期間にはmarkかspaceを示すbitをisMarkとして付加する事にします。periodは期間を1μsec単位で設定します。つまり、この16bitで32.7msecまで設定できます。これより長い期間の場合は32.7msecに分割して設定します。markの後は必ずspaceと決めてしまえばisMarkは不要ですが、後の拡張性を考えてこのようにしています。
1 2 3 4 |
typedef struct _stIrBit{ uint16_t period:15; uint16_t isMark:1; }irBit; |
void makeIrStreamTvFormat(irStream *stream, uint16_t irCode)
この関数で、リモコンコードを生成していますので参考にしてください。このフォーマットは三菱電機のテレビを制御することができます。同様にして他のフォーマットのリモコン信号も生成できますので、試してください。
なんでマイナーな「三菱電機のテレビのコードなんだよって」言わないでね…
配列もかなり大きく取っていますので、もう少し長いコードも送れます。
では、デモプログラムの紹介です。
Keyを何も押していない時は、何も表示しません。
やっつけで作ったのでまだ改良すべき点がかなりあると思いますが、参考までに、このデモサンプルのソースコードをここにおいてます。私が作った部分と、変更した部分のみです。
ダウンロードされると、以下の注意事項、条件等を承諾されたものとします。
なお、個人で改変して使われるのは自由です。
<注意事項>
・JTAGを使用する設定にしていますので、SWD I/Fのボードに書き込むと以降プログラムを変更できなくなる可能性もあります。
・本プログラムを書き込むとArduino用のブートローダーなど予め書き込まれていたプログラムは消去、上書きされます。その後の復旧手段について当方はサポートできません。
<利用の条件等>
本プログラム、および本ソースコードは、無保証で提供します。全て使用者の自己責任で利用してください。本プログラム、本ソースコードを使用して発生したいかなるトラブルや損害に対して当方は一切の責任を負いません。
本プログラム、および本ソースコードについて、正確性および、正常に動作する事を保証するものではありません。動作サポート等は行いません。
また、本プログラム、および本ソースコード等に関連する一切の事項について誤りがあった場合でも修正する義務を負いません。
また、有償、無償に関わらず、本プログラム、本ソースコード、および本ブログ記事の転載/再配布/頒布を一切禁止します。