Kaynağa Gözat

add: TM1637_pio

Satoshi Yoneda 5 gün önce
ebeveyn
işleme
2858ea59e3

+ 2 - 0
TM1637_pio/.gitignore

@@ -0,0 +1,2 @@
+build
+!.vscode/*

+ 22 - 0
TM1637_pio/.vscode/c_cpp_properties.json

@@ -0,0 +1,22 @@
+{
+    "configurations": [
+        {
+            "name": "Pico",
+            "includePath": [
+                "${workspaceFolder}/**",
+                "${userHome}/.pico-sdk/sdk/2.2.0/**"
+            ],
+            "forcedInclude": [
+                "${workspaceFolder}/build/generated/pico_base/pico/config_autogen.h",
+                "${userHome}/.pico-sdk/sdk/2.2.0/src/common/pico_base_headers/include/pico.h"
+            ],
+            "defines": [],
+            "compilerPath": "${userHome}/.pico-sdk/toolchain/14_2_Rel1/bin/arm-none-eabi-gcc",
+            "compileCommands": "${workspaceFolder}/build/compile_commands.json",
+            "cStandard": "c17",
+            "cppStandard": "c++14",
+            "intelliSenseMode": "linux-gcc-arm"
+        }
+    ],
+    "version": 4
+}

+ 15 - 0
TM1637_pio/.vscode/cmake-kits.json

@@ -0,0 +1,15 @@
+[
+    {
+        "name": "Pico",
+        "compilers": {
+            "C": "${command:raspberry-pi-pico.getCompilerPath}",
+            "CXX": "${command:raspberry-pi-pico.getCxxCompilerPath}"
+        },
+        "environmentVariables": {
+            "PATH": "${command:raspberry-pi-pico.getEnvPath};${env:PATH}"
+        },
+        "cmakeSettings": {
+            "Python3_EXECUTABLE": "${command:raspberry-pi-pico.getPythonPath}"
+        }
+    }
+]

+ 9 - 0
TM1637_pio/.vscode/extensions.json

@@ -0,0 +1,9 @@
+{
+    "recommendations": [
+        "marus25.cortex-debug",
+        "ms-vscode.cpptools",
+        "ms-vscode.cpptools-extension-pack",
+        "ms-vscode.vscode-serial-monitor",
+        "raspberry-pi.raspberry-pi-pico"
+    ]
+}

+ 50 - 0
TM1637_pio/.vscode/launch.json

@@ -0,0 +1,50 @@
+{
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "name": "Pico Debug (Cortex-Debug)",
+            "cwd": "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts",
+            "executable": "${command:raspberry-pi-pico.launchTargetPath}",
+            "request": "launch",
+            "type": "cortex-debug",
+            "servertype": "openocd",
+            "serverpath": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
+            "gdbPath": "${command:raspberry-pi-pico.getGDBPath}",
+            "device": "${command:raspberry-pi-pico.getChipUppercase}",
+            "configFiles": [
+                "interface/cmsis-dap.cfg",
+                "target/${command:raspberry-pi-pico.getTarget}.cfg"
+            ],
+            "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd",
+            "runToEntryPoint": "main",
+            // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected
+            // Also works fine for flash binaries
+            "overrideLaunchCommands": [
+                "monitor reset init",
+                "load \"${command:raspberry-pi-pico.launchTargetPath}\""
+            ],
+            "openOCDLaunchCommands": [
+                "adapter speed 5000"
+            ]
+        },
+        {
+            "name": "Pico Debug (Cortex-Debug with external OpenOCD)",
+            "cwd": "${workspaceRoot}",
+            "executable": "${command:raspberry-pi-pico.launchTargetPath}",
+            "request": "launch",
+            "type": "cortex-debug",
+            "servertype": "external",
+            "gdbTarget": "localhost:3333",
+            "gdbPath": "${command:raspberry-pi-pico.getGDBPath}",
+            "device": "${command:raspberry-pi-pico.getChipUppercase}",
+            "svdFile": "${userHome}/.pico-sdk/sdk/2.2.0/src/${command:raspberry-pi-pico.getChip}/hardware_regs/${command:raspberry-pi-pico.getChipUppercase}.svd",
+            "runToEntryPoint": "main",
+            // Fix for no_flash binaries, where monitor reset halt doesn't do what is expected
+            // Also works fine for flash binaries
+            "overrideLaunchCommands": [
+                "monitor reset init",
+                "load \"${command:raspberry-pi-pico.launchTargetPath}\""
+            ]
+        },
+    ]
+}

+ 40 - 0
TM1637_pio/.vscode/settings.json

@@ -0,0 +1,40 @@
+{
+    "cmake.showSystemKits": false,
+    "cmake.options.statusBarVisibility": "hidden",
+    "cmake.options.advanced": {
+        "build": {
+            "statusBarVisibility": "hidden"
+        },
+        "launch": {
+            "statusBarVisibility": "hidden"
+        },
+        "debug": {
+            "statusBarVisibility": "hidden"
+        }
+    },
+    "cmake.configureOnEdit": false,
+    "cmake.automaticReconfigure": false,
+    "cmake.configureOnOpen": false,
+    "cmake.generator": "Ninja",
+    "cmake.cmakePath": "${userHome}/.pico-sdk/cmake/v3.31.5/bin/cmake",
+    "C_Cpp.debugShortcut": false,
+    "terminal.integrated.env.windows": {
+        "PICO_SDK_PATH": "${env:USERPROFILE}/.pico-sdk/sdk/2.2.0",
+        "PICO_TOOLCHAIN_PATH": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1",
+        "Path": "${env:USERPROFILE}/.pico-sdk/toolchain/14_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.31.5/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:PATH}"
+    },
+    "terminal.integrated.env.osx": {
+        "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0",
+        "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1",
+        "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}"
+    },
+    "terminal.integrated.env.linux": {
+        "PICO_SDK_PATH": "${env:HOME}/.pico-sdk/sdk/2.2.0",
+        "PICO_TOOLCHAIN_PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1",
+        "PATH": "${env:HOME}/.pico-sdk/toolchain/14_2_Rel1/bin:${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool:${env:HOME}/.pico-sdk/cmake/v3.31.5/bin:${env:HOME}/.pico-sdk/ninja/v1.12.1:${env:PATH}"
+    },
+    "raspberry-pi-pico.cmakeAutoConfigure": true,
+    "raspberry-pi-pico.useCmakeTools": false,
+    "raspberry-pi-pico.cmakePath": "${HOME}/.pico-sdk/cmake/v3.31.5/bin/cmake",
+    "raspberry-pi-pico.ninjaPath": "${HOME}/.pico-sdk/ninja/v1.12.1/ninja"
+}

+ 102 - 0
TM1637_pio/.vscode/tasks.json

@@ -0,0 +1,102 @@
+{
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "label": "Compile Project",
+            "type": "process",
+            "isBuildCommand": true,
+            "command": "${userHome}/.pico-sdk/ninja/v1.12.1/ninja",
+            "args": ["-C", "${workspaceFolder}/build"],
+            "group": "build",
+            "presentation": {
+                "reveal": "always",
+                "panel": "dedicated"
+            },
+            "problemMatcher": "$gcc",
+            "windows": {
+                "command": "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja.exe"
+            }
+        },
+        {
+            "label": "Run Project",
+            "type": "process",
+            "command": "${env:HOME}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool",
+            "args": [
+                "load",
+                "${command:raspberry-pi-pico.launchTargetPath}",
+                "-fx"
+            ],
+            "presentation": {
+                "reveal": "always",
+                "panel": "dedicated"
+            },
+            "problemMatcher": [],
+            "windows": {
+                "command": "${env:USERPROFILE}/.pico-sdk/picotool/2.2.0-a4/picotool/picotool.exe"
+            }
+        },
+        {
+            "label": "Flash",
+            "type": "process",
+            "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
+            "args": [
+                "-s",
+                "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts",
+                "-f",
+                "interface/cmsis-dap.cfg",
+                "-f",
+                "target/${command:raspberry-pi-pico.getTarget}.cfg",
+                "-c",
+                "adapter speed 5000; program \"${command:raspberry-pi-pico.launchTargetPath}\" verify reset exit"
+            ],
+            "problemMatcher": [],
+            "windows": {
+                "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
+            }
+        },
+        {
+            "label": "Rescue Reset",
+            "type": "process",
+            "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
+            "args": [
+                "-s",
+                "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts",
+                "-f",
+                "interface/cmsis-dap.cfg",
+                "-f",
+                "target/${command:raspberry-pi-pico.getChip}-rescue.cfg",
+                "-c",
+                "adapter speed 5000; reset halt; exit"
+            ],
+            "problemMatcher": [],
+            "windows": {
+                "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
+            }
+        },
+        {
+            "label": "RISC-V Reset (RP2350)",
+            "type": "process",
+            "command": "${userHome}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
+            "args": [
+                "-s",
+                "${userHome}/.pico-sdk/openocd/0.12.0+dev/scripts",
+                "-c",
+                "set USE_CORE { rv0 rv1 cm0 cm1 }",
+                "-f",
+                "interface/cmsis-dap.cfg",
+                "-f",
+                "target/rp2350.cfg",
+                "-c",
+                "adapter speed 5000; init;",
+                "-c",
+                "write_memory 0x40120158 8 { 0x3 }; echo [format \"Info : ARCHSEL 0x%02x\" [read_memory 0x40120158 8 1]];",
+                "-c",
+                "reset halt; targets rp2350.rv0; echo [format \"Info : ARCHSEL_STATUS 0x%02x\" [read_memory 0x4012015C 8 1]]; exit"
+            ],
+            "problemMatcher": [],
+            "windows": {
+                "command": "${env:USERPROFILE}/.pico-sdk/openocd/0.12.0+dev/openocd.exe",
+            }
+        }
+    ]
+}

+ 57 - 0
TM1637_pio/CMakeLists.txt

@@ -0,0 +1,57 @@
+# Generated Cmake Pico project file
+
+cmake_minimum_required(VERSION 3.13)
+
+set(CMAKE_C_STANDARD 11)
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+# Initialise pico_sdk from installed location
+# (note this can come from environment, CMake cache etc)
+
+# == DO NOT EDIT THE FOLLOWING LINES for the Raspberry Pi Pico VS Code Extension to work ==
+if(WIN32)
+    set(USERHOME $ENV{USERPROFILE})
+else()
+    set(USERHOME $ENV{HOME})
+endif()
+set(sdkVersion 2.2.0)
+set(toolchainVersion 14_2_Rel1)
+set(picotoolVersion 2.2.0-a4)
+set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake)
+if (EXISTS ${picoVscode})
+    include(${picoVscode})
+endif()
+# ====================================================================================
+set(PICO_BOARD pico2 CACHE STRING "Board type")
+
+# Pull in Raspberry Pi Pico SDK (must be before project)
+include(pico_sdk_import.cmake)
+
+project(TM1637_pio C CXX ASM)
+
+# Initialise the Raspberry Pi Pico SDK
+pico_sdk_init()
+
+# サブディレクトリのライブラリを追加
+add_subdirectory(tm1637_lib)
+
+# Add executable. Default name is the project name, version 0.1
+
+add_executable(TM1637_pio
+    TM1637_pio.c
+    )
+
+pico_set_program_name(TM1637_pio "TM1637_pio")
+pico_set_program_version(TM1637_pio "0.1")
+
+# Modify the below lines to enable/disable output over UART/USB
+pico_enable_stdio_uart(TM1637_pio 1)
+pico_enable_stdio_usb(TM1637_pio 0)
+
+# Add the standard library to the build
+target_link_libraries(TM1637_pio
+        pico_stdlib
+        tm1637_lib) # 作成したライブラリをリンク
+
+pico_add_extra_outputs(TM1637_pio)

+ 129 - 0
TM1637_pio/README.md

@@ -0,0 +1,129 @@
+# 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
+

+ 47 - 0
TM1637_pio/TM1637_pio.c

@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include "pico/stdlib.h"
+
+#include "TM1637.h"
+
+#define SDA1_BASE     4
+#define SDA2_BASE     14
+
+#define MY1637_COLS    4
+
+int main()
+{
+    char strbuf[MY1637_COLS+1];
+
+    stdio_init_all();
+    TM1637_t *tm1, *tm2;
+
+    tm1 = TM1637_init(SDA1_BASE, MY1637_COLS, 2);
+    tm2 = TM1637_init(SDA2_BASE, MY1637_COLS, 2);
+
+    
+    if( tm1 != NULL && tm2 != NULL) {
+    
+        TM1637_putchar(tm1, '1',false, 0);
+        TM1637_putchar(tm1, '2',true , 1);
+        TM1637_putchar(tm1, '3',false, 2);
+        TM1637_putchar(tm1, '4',false, 3);
+
+        TM1637_putchar(tm2, '1',false, 0);
+        TM1637_putchar(tm2, '2',true , 1);
+        TM1637_putchar(tm2, '3',false, 2);
+        TM1637_putchar(tm2, '4',false, 3);
+        sleep_ms(1000);
+        TM1637_putstr(tm1, "12:98");
+        TM1637_putstr(tm2, "00:88");
+        sleep_ms(1000);
+        int i = 0;
+        while (true) {
+            snprintf(strbuf, sizeof(strbuf),"%4d",i);
+            TM1637_putstr(tm1, strbuf);
+            TM1637_putstr(tm2, strbuf);
+            if(++i > 9999) i =0;
+            sleep_ms(1000);
+        }
+    }
+    
+}

+ 121 - 0
TM1637_pio/pico_sdk_import.cmake

@@ -0,0 +1,121 @@
+# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
+
+# This can be dropped into an external project to help locate this SDK
+# It should be include()ed prior to project()
+
+# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd.
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+# following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+# disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
+    set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
+    message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
+    set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
+    message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
+    set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
+    message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
+endif ()
+
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG))
+    set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG})
+    message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')")
+endif ()
+
+if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG)
+  set(PICO_SDK_FETCH_FROM_GIT_TAG "master")
+  message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG")
+endif()
+
+set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
+set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
+set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
+set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK")
+
+if (NOT PICO_SDK_PATH)
+    if (PICO_SDK_FETCH_FROM_GIT)
+        include(FetchContent)
+        set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
+        if (PICO_SDK_FETCH_FROM_GIT_PATH)
+            get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
+        endif ()
+        FetchContent_Declare(
+                pico_sdk
+                GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
+                GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
+        )
+
+        if (NOT pico_sdk)
+            message("Downloading Raspberry Pi Pico SDK")
+            # GIT_SUBMODULES_RECURSE was added in 3.17
+            if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
+                FetchContent_Populate(
+                        pico_sdk
+                        QUIET
+                        GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
+                        GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
+                        GIT_SUBMODULES_RECURSE FALSE
+
+                        SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src
+                        BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build
+                        SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild
+                )
+            else ()
+                FetchContent_Populate(
+                        pico_sdk
+                        QUIET
+                        GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
+                        GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG}
+
+                        SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src
+                        BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build
+                        SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild
+                )
+            endif ()
+
+            set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
+        endif ()
+        set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
+    else ()
+        message(FATAL_ERROR
+                "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
+                )
+    endif ()
+endif ()
+
+get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
+if (NOT EXISTS ${PICO_SDK_PATH})
+    message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
+endif ()
+
+set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
+if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
+    message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
+endif ()
+
+set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
+
+include(${PICO_SDK_INIT_CMAKE_FILE})

+ 17 - 0
TM1637_pio/tm1637_lib/CMakeLists.txt

@@ -0,0 +1,17 @@
+# ライブラリのターゲットを作成 (STATICライブラリ)
+add_library(tm1637_lib STATIC
+    TM1637.c
+    tm1637out.pio
+)
+
+# PIOヘッダの生成
+pico_generate_pio_header(tm1637_lib ${CMAKE_CURRENT_LIST_DIR}/tm1637out.pio)
+
+# ヘッダファイルのパスを公開 (このライブラリを使う側が include できるようにする)
+target_include_directories(tm1637_lib PUBLIC ${CMAKE_CURRENT_LIST_DIR})
+
+# ライブラリが必要とする依存関係
+target_link_libraries(tm1637_lib PUBLIC
+    pico_stdlib
+    hardware_pio
+)

+ 194 - 0
TM1637_pio/tm1637_lib/TM1637.c

@@ -0,0 +1,194 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "pico/stdlib.h"
+#include "hardware/pio.h"
+#include "TM1637.h"
+#include "tm1637out.pio.h"
+
+const uint8_t digit_seg[] = {
+    0b00111111,     // 0
+    0b00000110,     // 1
+    0b01011011,     // 2
+    0b01001111,     // 3
+    0b01100110,     // 4
+    0b01101101,     // 5
+    0b01111101,     // 6
+    0b00000111,     // 7
+    0b01111111,     // 8
+    0b01101111,     // 9
+};
+
+const uint8_t letter_seg[] = {
+    0b01110111,     // A
+    0b01111100,     // b
+    0b00111001,     // C
+    0b01011110,     // d
+    0b01111001,     // E
+    0b01110001,     // F
+    0b00111101,     // G
+    0b01110110,     // H
+    0b00000100,     // i
+    0b00001110,     // J
+    0b00000000,     // K
+    0b00111000,     // L
+    0b00000000,     // M
+    0b01010100,     // n
+    0b01011100,     // o
+    0b01110011,     // P
+    0b01100111,     // q
+    0b01010000,     // r
+    0b01101101,     // S
+    0b01111000,     // t
+    0b00011100,     // u
+    0b00000000,     // V
+    0b00000000,     // W
+    0b00000000,     // X
+    0b01101110,     // y
+    0b00000000,     // z
+};
+
+
+// 先頭のTM1637_tのポインタ
+static TM1637_t    *first = NULL;
+
+// 文字コード→セグメントコード
+uint32_t chr_to_seg(char c)
+{
+    if(c >= '0' && c <= '9')
+        return digit_seg[c - '0'];
+    if(c >= 'a' && c <= 'z')
+        return letter_seg[c - 'a'];
+    if( c >= 'A' && c <= 'Z')
+        return letter_seg[c - 'A'];
+    if( c == ':' || c == '.')
+        return DOT_SEG;
+    if( c == '-' )
+        return MINUS_SEG;
+
+    return 0;
+}
+
+// TM1637を追加する
+int TM1637_add(TM1637_t *next)
+{
+    if (first == NULL) {
+        first = next;
+        return 0;
+    }
+
+    TM1637_t *p = first;
+
+    while(p->next != NULL)
+        p = p->next;
+    
+    p->next = next;
+
+    return 0;
+}
+
+TM1637_t *TM1637_init(uint8_t sda_base_pin, uint8_t col, uint8_t cont)
+{
+    uint8_t colmns, contrast;
+    PIO pio;
+    uint    sm, pio_offset;
+    TM1637_t    *p;
+
+    // デフォルト値の設定
+    if(col <= TM1637_MAX_COL && col > 0)
+        colmns = col;
+    else
+        return NULL;
+    
+    if(cont <= TM1637_MAX_CONTRAST)
+        contrast = cont;
+    else
+        return NULL;
+    
+    if(pio_claim_free_sm_and_add_program(&tm1637out_program, &pio, &sm, &pio_offset)) {
+        // PIO初期化
+        tm1637out_program_init(pio, sm, pio_offset, sda_base_pin, sda_base_pin+1);
+        // SM起動
+        pio_sm_set_enabled(pio, sm, true);
+    }
+    else    // PIOを確保できなかった
+        return NULL;
+    
+    // ここまでできたら初期化成功と見て構造体を作成する
+    p = (TM1637_t *)malloc(sizeof(TM1637_t));
+    p->colmns = colmns;
+    p->contrast = contrast;
+    p->pio = pio;
+    p->sm = sm;
+    p->offset = pio_offset;
+    p->next = NULL;
+    p->initialized = true;
+    // 末尾に追加
+    TM1637_add(p);
+    
+    // TM1637初期化
+    // データセット(自動アドレスインクリメント)
+    pio_sm_put_blocking(pio, sm, TM1637_AUTOINC | STOP_COND);
+    // アドレス0からカラム数分を0で初期化
+    pio_sm_put_blocking(pio, sm, TM1637_ADDRSET| 0 );
+    for(int i = 0; i < colmns; i++) {
+        uint32_t data = 0;
+        if( i == (colmns - 1) )
+            data = STOP_COND;
+        pio_sm_put_blocking(pio, sm, data);
+    }
+    // コントラスト値で表示オン
+    TM1637_set_contrast(p, contrast);
+
+    return p;
+}
+
+int TM1637_set_contrast(TM1637_t *p, uint8_t cont)
+{
+    if(cont <= TM1637_MAX_CONTRAST)
+        p->contrast = cont;
+    else
+        return TM1637_ERR_INVALID_PARAM;
+
+    pio_sm_put_blocking(p->pio, p->sm, TM1637_DISPON | cont | STOP_COND);
+    return 0;
+}
+
+int TM1637_putchar(TM1637_t *p, char c, bool dot,uint8_t col)
+{
+    if(col >= p->colmns)
+        return TM1637_ERR_INVALID_PARAM;
+
+    // 固定アドレスデータセット
+    pio_sm_put_blocking(p->pio, p->sm, TM1637_DATASET | STOP_COND);
+    // アドレス
+    pio_sm_put_blocking(p->pio, p->sm, TM1637_ADDRSET | col);
+    // セグメントデータ
+    pio_sm_put_blocking(p->pio, p->sm, chr_to_seg(c) | (dot ? DOT_SEG : 0) | STOP_COND);
+    return 0;
+}
+
+int TM1637_putstr(TM1637_t *t, char *str)
+{
+    int col = 0, p = 0;
+
+    // データセット(自動アドレスインクリメント)
+    pio_sm_put_blocking(t->pio, t->sm, TM1637_AUTOINC | STOP_COND);
+    // アドレス0セット
+    pio_sm_put_blocking(t->pio, t->sm, TM1637_ADDRSET| 0 );
+    // 文字数分を出力
+    while(str[col] != '\0') {
+        uint32_t data = chr_to_seg(str[col++]);
+        // ドットマーク
+        if(str[col] == '.' || str[col] == ':') {
+            data |= DOT_SEG;
+            col++;
+        }
+        // 最後はStop Condition
+        if(str[col] == '\0' || ++p >= t->colmns) data |= STOP_COND;
+        pio_sm_put_blocking(t->pio, t->sm, data);
+        // カラム数に達したら終わり
+        if( p >= t->colmns )
+            break;
+    }
+    return 0;
+}

+ 79 - 0
TM1637_pio/tm1637_lib/TM1637.h

@@ -0,0 +1,79 @@
+#ifndef _TM1637_H
+#define _TM1637_H   1
+
+#include "stdint.h"
+#include "pico/stdlib.h"
+#include "hardware/pio.h"
+
+#define MINUS_SEG   0b01000000  // Minus
+#define DOT_SEG     0b10000000  // DOT
+#define STOP_COND   0x100       // ストップコンディションにする
+
+#define TM1637_MAX_COL      6           // TM1637の最大カラム数は6
+#define TM1637_MAX_CONTRAST 0b111       
+
+#define TM1637_AUTOINC      0b01000000  // Data set Auto Increment
+#define TM1637_DATASET      0b01000100  // Data set
+#define TM1637_ADDRSET      0b11000000  // ADDR SET
+#define TM1637_DISPOFF      0b10000000  // Display Off
+#define TM1637_DISPON       0b10001000  // Display On
+
+#define TM1637_ERR_INVALID_PARAM    -1
+#define TM1637_ERR_NACK             -2
+
+typedef struct TM1637st {
+    PIO     pio;        // PIOインスタンス
+    uint    sm;        // ステートマシン番号
+    uint    offset;    // オフセットアドレス
+
+    uint8_t contrast;   // コントラスト値
+    uint8_t colmns;     // このユニットのカラム数
+
+    bool    initialized;    // 初期化完了フラグ
+
+    struct TM1637st *next;  // 次の構造体へのポインタ
+} TM1637_t;
+
+/*
+TM1637_t *TM1637_init(uint8_t sda_base_pin, int col, uint8_t cont);
+TM1637を初期化しハンドルを返す
+パラメータ:
+uint8_t sda_base_pin: TM1637のSDAが接続されているGPIO。SDAの次のGPIOにSCLKを接続すること
+uint8_t col: 接続しているTM1637モジュールのカラム数
+uint8_t cont: コントラスト値(0~7)
+戻り値:
+TM1637_tのポインタ。NULLは初期化失敗
+*/
+TM1637_t *TM1637_init(uint8_t sda_base_pin, uint8_t col, uint8_t cont);
+/*
+int TM1637_set_contrast(TM1637_t *p, uint8_t cont);
+パラメータ:
+TM1637_t *p: TM1637_tのポインタ
+uint8_t cont: コントラスト値
+戻り値:
+0なら成功、負の値なら失敗
+*/
+int TM1637_set_contrast(TM1637_t *p, uint8_t cont);
+/*
+int TM1637_putchar(TM1637_t *p, char c, bool dot, uint8_t col);
+1文字出力
+パラメータ:
+TM1637_t *p: TM1637_tのポインタ
+bool dot: ドットセグメントをオンにするならtrue
+uint8_t col: 表示するカラム
+戻り値:
+0なら成功、負の値なら失敗
+*/
+int TM1637_putchar(TM1637_t *p, char c, bool dot, uint8_t col);
+/*
+int TM1637_putstr(TM1637_t *t, char *str);
+文字列表示
+パラメータ:
+TM1637_t *p: TM1637_tのポインタ
+char *str: 表示する文字列
+戻り値:
+0なら成功、負の値なら失敗
+*/
+int TM1637_putstr(TM1637_t *t, char *str);
+
+#endif

+ 98 - 0
TM1637_pio/tm1637_lib/tm1637out.pio

@@ -0,0 +1,98 @@
+; CLOCK = 2MHz
+
+.program tm1637out
+.side_set 1 opt
+
+.wrap_target            ; Start Condition
+    pull block
+    set pindirs,0b00    ; SDA/SCL input
+    nop     [7]         ; 8 Clocks
+    set pindirs,0b01    ; SDA output/SCL input
+    set pins,0          ; SDA is Low
+    nop     [7]         ; 8 Clocks
+    set pindirs,0b11    ; SDA/SCL output
+    set x,7 side 0      ; CLK is Low
+    jmp output
+
+; 1バイト出力
+byte_loop:
+    set x, 7 side 0     ; Clock Low
+    pull block
+output:
+    nop     [7]         ; 8 Clock
+    out pins,1          ; LSB output
+    nop     side 1      ; CLK is High
+    nop     [7]         ; 8 clocks
+    nop     side 0      ; CLK is Low
+    jmp x-- output      ;
+
+; ACK判定
+    set pindirs,0b10  [7]  ; SDA input/SCL output
+    nop     side 1      ; SCL = High
+    jmp pin nack        ; if SDA == 0 then ACK
+    jmp ack
+
+nack:
+    irq 0 rel          ; NACK
+
+; Stop判定
+ack:
+    nop     [6]
+    nop     side 0          ; CLK is Low (ACK cycle end)
+    set pindirs, 0b11  [7]  ; SDA/SCL Output (next byte or stop)
+    out x, 1                ; 9bit目
+    jmp !x byte_loop        ; 0ならStopせずにループ
+
+; Stop Condition
+stop:
+    set pins, 0     [7]     ; SDA is Low
+    nop     side 1          ; SCL is High
+    nop     [7]
+    set pindirs, 0b00       ; SDA/SCL Input (pull-up makes it High) -> STOP
+    nop     [7]
+.wrap
+
+
+% c-sdk {
+
+#include "hardware/clocks.h"
+
+#define SM_CLOCK    2000000.0
+
+void tm1637out_program_init(PIO pio, uint sm, uint offset, uint sda, uint clk) {
+
+   // pull up
+   gpio_pull_up(sda);
+   gpio_pull_up(clk);    
+
+   // SDA & CLK
+   pio_gpio_init(pio, sda);
+   pio_gpio_init(pio, clk);
+   pio_sm_set_consecutive_pindirs(pio, sm, clk, 1, false);  // 初期状態はInput
+   pio_sm_set_consecutive_pindirs(pio, sm, sda, 1, false);  // 同上
+
+   pio_sm_config c = tm1637out_program_get_default_config(offset);
+
+   // out = set = jmp = SDA
+   sm_config_set_out_pin_base(&c, sda);
+   sm_config_set_out_pin_count(&c, 1);
+   sm_config_set_set_pin_base(&c, sda);
+   sm_config_set_set_pin_count(&c, 2);
+   sm_config_set_jmp_pin(&c, sda);
+
+   // sideset = clk
+   sm_config_set_sideset_pin_base(&c, clk);
+
+   // out = Right shift
+   sm_config_set_out_shift(&c, true, false, 0);
+
+   // SM Clock = 2MHz
+   float clkdiv = (float)clock_get_hz(clk_sys) / SM_CLOCK;
+   sm_config_set_clkdiv(&c, clkdiv);
+
+   // TX FIFO join
+   sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
+   
+   pio_sm_init(pio, sm, offset, &c);
+}
+%}