# TM1637 PIO Driver Raspberry Pi Pico (RP2040/RP2350) の PIO (Programmable I/O) で[TM1637](https://akizukidenshi.com/catalog/g/g113224/)を使った7セグメントディスプレイを制御するライブラリです。TM1637は単体で秋月電子通商から買えるほか、AmazonやAliexpressからTM1637を使った7セグメントLEDディスプレイモジュールが安価に入手できます。 TM1637の詳細に関しては秋月にあるデータシートや、ネット上の参考資料を見てもらえばいいと思いますが、I2Cもどきの2線式シリアルで複数桁の7セグメントLEDを制御できるICです。GPIOで制御している作例が多いですが、PIOで制御することでCPUを低速なシリアル通信から解放できます。このライブラリでは、PIOのFIFOを連結して8エントリとしているので、よくある4桁モジュールならCPUがブロックされずに表示を行うことができます。 ## 特徴 - **PIO駆動**: 通信プロトコルをPIOで処理するため、CPU負荷が低く、正確なタイミングで動作します。 - **マルチインスタンス**: 複数のディスプレイを異なるピンで同時に制御可能です。 - **CMake対応**: `add_subdirectory` するだけで簡単にプロジェクトに組み込めます。 ## 概説 TM1637はI2C風のインタフェースを通じてホストと通信します。Start Conditionでトランザクションを開始し、Stop Conditionでトランザクションを終了する点や、1バイトごとのACKはI2Cと同じです。I2Cとのおもな違いは次の2点です。 - 下位ビットから送出(LSB First)。I2CはMSB FirstですがTM1637は逆に下位ビットから順に送受信します。これがI2Cとのもっとも大きな違いです - アドレスを持たない。1つのバスに1つのTM1637しか接続できません このライブラリでは、I2CライクなTM1637のホスト→TM1637のプロトコルをPIOで実装しています。次のように値bをpushするとStart Conditionに続いて1バイトをTM1637に送信します。 ```.c pio_sm_put_blocking(pio, sm, b); ``` 値の9bit目をオンにすると、送信後にStop Conditionにバスを遷移させ、バスを解放します。 ```.c pio_sm_put_blocking(pio, sm, b | STOP_COND); ``` SCLKをSDA(TM1637の端子名はDIO)の隣のGPIOに固定しているのは、PIO命令setを使って2つのGPIOの入出力を切り替えていて、STOP Condition後にSDAとSCLKを入力に切り替えて高インピーダンス状態にしバスを解放する必要があるからです。 `tm1637out.pio`に実装しているPIOコードでは、TM1637のI2C風プロトコルの大部分をデータシートに沿って実装しています。I2C的な通信を行うPIOコード例として活用してください。 なお、PIOコードではACKをチェックしてNACKを検出したらステートマシン番号のIRQフラグをオンにしていますが、Cコード側の割り込み処理は行っていません。テストしてみた限りNACKが生じることがなさそうであることに加えて、仮にNACKが検出されても対応のしようがない(通信エラーが起きたことがわかるだけ)からです。マルチインスタンス化していることもあり、NACKのための割り込み処理を追加するとかなり複雑になりますが、複雑にするだけの価値が見いだせないので、NACKに対応する処理は削除しました。もしに気なるようならIRQフラグをチェックするコードを追加すればいいでしょう。 ## ディレクトリ構成 ``` your_project/ ├── tm1637_lib/ # このライブラリフォルダ │ ├── CMakeLists.txt │ ├── TM1637.c │ ├── TM1637.h │ ├── tm1637out.pio │ └── README.md ├── CMakeLists.txt # 親プロジェクトのCMakeファイル └── main.c # アプリケーションコード ``` ## 使い方 1. **ライブラリの配置**: プロジェクトフォルダに `tm1637_lib` をコピーします。 2. **CMakeLists.txt の編集**: ```cmake add_subdirectory(tm1637_lib) target_link_libraries(your_executable tm1637_lib) ``` 3. **コード例**: ```c #include "TM1637.h" // 初期化 (SDAピン番号, 桁数, 輝度0-7) // SCLピンは SDA + 1 となります TM1637_t *tm = TM1637_init(4, 4, 7); if (tm != NULL) { TM1637_putstr(tm, "12:34"); } ``` ## API リファレンス #### `TM1637_t *TM1637_init(uint8_t sda_base_pin, uint8_t col, uint8_t cont)` TM1637を初期化し成功すればハンドルを返す **パラメータ** - **uint8_t sda_base_pin**: SDA(DIO)を接続しているGPIO番号。PIOコードの制約によりsda_base_pin+1のGPIOにSCLKを接続してください。 - **uint8_t col**: 接続しているモジュールのカラム数。TM1637は最大6カラムの7セグメントLEDを制御できます。 - **uint8_t cont**: コントラスト値。0(暗)~7(明)。 **戻り値** - 初期化に成功するとハンドル(TM1638_tのポインタ)を返す。NULLは失敗。 #### `int TM1637_set_contrast(TM1637_t *p, uint8_t cont)` コントラスト値を設定する **パラメータ** - **TM1638_t \*p**: ハンドル - **uint8_t cont**: コントラスト値。0(暗)~7(明) **戻り値** - 成功したら0 #### `int TM1637_putchar(TM1637_t *p, char c, bool dot, uint8_t col)` 文字を表示 **パラメータ** - **TM1637_t \*p**: ハンドル - **bool dot**: ドットセグメントをオンにするならtrue - **uint8_t col**: 表示するカラム **戻り値** - 成功したら0 #### `int TM1637_putstr(TM1637_t *t, char *str)` 文字列を表示する **パラメータ** - **TM1637_t *p**: ハンドル - **char *str**: 表示する文字列。次の文字にピリオドまたはコロンを挿入すると、その文字のドットセグメントがオンになります。ドットセグメントの形状及び位置は製品によって異なります。ピリオド(.)かコロン(:)が一般的です。 **戻り値** - 成功したら0