前回に続き、今回はロータリーエンコーダーを2個にしてみました。

複数のロータリーエンコーダーの同時読み取りは、switch-case分で対応出来ました。

その対応と言っても、人の指で2個のロータリーエンコーダーを普通にクルクル回す速さでの事です。モーターなどで機械的に高速で回した場合には対応出来ないかも。

「尚、私はロータリーエンコーダーを読み取るソースコードを書くのは始めたばかりだし、C言語も素人で、しかも初心者なので凄いソースコードは書けません。悪しからず」

この記事のソースコードや考えに誤り、非効率、プロならこんなソースコードは書かないとか、あるでしょうから先に言って置きます。

 

↓ 白色(PETG)の造形物は、私がOnshapeで設計、Ender-3 V3 SEで造形しました。

今は電子工作に3Dプリンターは、半田ごてくらいに必需品だと思う。無いなら自分で作ればいい。電子工作もその考えだし。3DCAD設計も楽しいしね。PC画面の3Dが現物になるんだから凄い。

3D model-viewer

 

 

↑↓ central_line の上下は0、x_speed(グラフ描画間隔)は1のロータリーエンコーダーで変化させる。

x_speedの値は待機時間。つまり、グラフ描画(左端から右端までの描画)後に、待機時間を設けて次のグラフ描画となる。オシロスコープで言う時間軸の変化みたいなもの。

 

 

↓ この関数で複数(2個)のロータリーエンコーダーの読み取りを行います。

void lxs_gpio_callback(uint gpio, uint32_t events)

↓ 次のソースコードは一部分ですが、core1で行うロータリーエンコーダーの読み取りに関係します。

// コールバック関数
// 1つ又は複数のロータリーエンコーダーを同時に回した時
// (割り込みが発生した時)に呼ばれる関数
// gpio の値でどのロータリーエンコーダーなのか特定出来る
void lxs_gpio_callback(uint gpio, uint32_t events) {

    int lxs_clk;

    // この関数実行中のみ点灯させて
    // ロータリーエンコーダーの
    // 立ち上がりや立ち下がりに
    // 発生する割り込みを確認するLED
    gpio_put(5, 1); // GP5のLED点灯

    // 割り込みの禁止
    gpio_set_irq_enabled(gpio, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, false);

    sleep_ms(3); // 0.003秒 念の為のチャタリング対策

    switch (gpio) {

        // DT0 の割り込み ///////////////////////
        case PIN_DT0: // 0のロータリーエンコーダー

            // CLK0の状態を取得
            lxs_clk = gpio_get(PIN_CLK0);

            switch (events) {

                // DT0 の立ち上がり割り込み
                case GPIO_IRQ_EDGE_RISE:        // 0x8u
                    if (lxs_clk == 1) {         // #A 右回り
                        central_line--;
                    } else if (lxs_clk == 0) {  // #C 左回り
                        central_line++;
                    }
                    break;

                // DT0 の立ち下がり割り込み
                case GPIO_IRQ_EDGE_FALL:        // 0x4u
                    if (lxs_clk == 0) {         // #B 右回り
                        central_line--;
                    } else if (lxs_clk == 1) {  // #D 左回り
                        central_line++;
                    }
                    break;

                // evetes値が12(0x0Cかな)とか意味不明の場合
                default:
                    central_line = central_line;
                    break;
            }
            break;

        // DT1 の割り込み ///////////////////////
        case PIN_DT1: // 1のロータリーエンコーダー
        
            // CLK1の状態を取得
            lxs_clk = gpio_get(PIN_CLK1);

            switch (events) {

                // DT1 の立ち上がり割り込み
                case GPIO_IRQ_EDGE_RISE:        // 0x8u
                    if (lxs_clk == 1) {         // #A 右回り
                        x_speed = x_speed + 10; // +10μ秒(+0.00001秒)
                    } else if (lxs_clk == 0) {  // #C 左回り
                        x_speed = x_speed - 10;
                    }
                    break;

                // DT1 の立ち下がり割り込み
                case GPIO_IRQ_EDGE_FALL:        // 0x4u
                    if (lxs_clk == 0) {         // #B 右回り
                        x_speed = x_speed + 10;
                    } else if (lxs_clk == 1) {  // #D 左回り
                        x_speed = x_speed - 10;
                    }
                    break;

                // events値が12(0x0Cかな)とか意味不明の場合
                default:
                    x_speed = x_speed;
                    break;
            }
            break;

        default: // gpio の値が想定外の時
            central_line = 45; // 初期値
            x_speed = 920; // 初期値
            break;
    }

    // 割り込み禁止の解除
    gpio_set_irq_enabled(gpio, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true);

    gpio_put(5, 0); // GP5のLED消灯
}

void core1_entry() {

    const uint LED_PIN = 25;
    gpio_init(LED_PIN);
    gpio_set_dir(LED_PIN, GPIO_OUT);

    // GPIOの初期化(ロータリーエンコーダー用)
    gpio_init(PIN_CLK0); // GP4
    gpio_init(PIN_DT0); // GP3
    gpio_init(PIN_SW0); // GP2
    gpio_init(PIN_CLK1); // GP12
    gpio_init(PIN_DT1); // GP11
    gpio_init(PIN_SW1); // GP10

    // 立ち上がり、立ち下がりの割り込み確認用LED
    gpio_init(5); // GP5

    // 入出力設定
    gpio_set_dir(PIN_CLK0, GPIO_IN);
    gpio_set_dir(PIN_DT0, GPIO_IN);
    gpio_set_dir(PIN_SW0, GPIO_IN);
    gpio_set_dir(PIN_CLK1, GPIO_IN);
    gpio_set_dir(PIN_DT1, GPIO_IN);
    gpio_set_dir(PIN_SW1, GPIO_IN);
    gpio_set_dir(5, GPIO_OUT);

    // 内部プルダウン設定
    gpio_pull_down(PIN_CLK0);
    gpio_pull_down(PIN_DT0);
    gpio_pull_down(PIN_SW0);
    gpio_pull_down(PIN_CLK1);
    gpio_pull_down(PIN_DT1);
    gpio_pull_down(PIN_SW1);

    // ヒステレリス設定(シュミットトリガ利用)念の為のチャタリング対策
    gpio_set_input_hysteresis_enabled(PIN_CLK0, true);
    gpio_set_input_hysteresis_enabled(PIN_DT0, true);
    gpio_set_input_hysteresis_enabled(PIN_SW0, true);
    gpio_set_input_hysteresis_enabled(PIN_CLK1, true);
    gpio_set_input_hysteresis_enabled(PIN_DT1, true);
    gpio_set_input_hysteresis_enabled(PIN_SW1, true);

    // 割り込み用コールバック関数の登録
    gpio_set_irq_enabled_with_callback(PIN_DT0, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true, &lxs_gpio_callback);
    gpio_set_irq_enabled_with_callback(PIN_DT1, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true, &lxs_gpio_callback);

    // 割り込み禁止の解除
    gpio_set_irq_enabled(PIN_DT0, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true);
    gpio_set_irq_enabled(PIN_DT1, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true);

    // 基板実装LED(GP25)の点灯用ループ
    while (true) {
        if (gpio_get(PIN_CLK0) == 1) {
            gpio_put(LED_PIN, 1);
            //sleep_ms(2);
        } else {
            gpio_put(LED_PIN, 0);
            //sleep_ms(2);
        }
    }
}

 

まぁ〜、このソースコード、C言語初心者の私にとっては趣味に使える。

でも、以下の事が、今後解決したらいいと思う。

 

【理解不能】

↓ 上記ソースコードの一部で、自分で書いて置きながら、私には理解出来ない事がある。割り込み用コールバック関数の登録で、ロータリーエンコーダーが2個だから2つ書いているけど、1つだけでも同様に動作する。なんで?

// 割り込み用コールバック関数の登録
gpio_set_irq_enabled_with_callback(PIN_DT0, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true, &lxs_gpio_callback);
gpio_set_irq_enabled_with_callback(PIN_DT1, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true, &lxs_gpio_callback);

 

【意味不明】

上記の理解不能な件に加えて、コールバック関数内で使用するeventsの値が意味不明なのがある。
それは、ロータリーエンコーダーを回している時(1クリックごとに)、私が想定するeventsの値は0x4uと0x8uなんだけど、12(多分0x0Cuかな)が頻繁に発生する。
この12とは? なに? エラーとかファールのコード?
ググり方が不味いのか、ググっても解決していない。

これは仕方ないので、その部分のソースコードで想定外の値の時は、ロータリーエンコーダーを回しても(1クリック分だけ)変化しない事にした。↓

// evetes値が12(0x0Cかな)とか意味不明の場合
default:
    central_line = central_line;
    break;
// events値が12(0x0Cかな)とか意味不明の場合
default:
    x_speed = x_speed;
    break;

次のリンク先のモデルをパクり、一部のデザインを変更して冷却ファンのブラケットを造形しました。

Ender 3 V3 SE Lightweight Fan Shroud

This link is the closest model to what I think is ideal.
I designed my model in homage to this model.

 

印刷条件は以下の通り。部屋温度が低く、PETGフィラメントなので、印刷速度は「超ゆっくり」ファン速度も「最低速」

そうしないと、角やカーブの所が積層に失敗してショートカットしちゃたり。ファン速度を速くすると直線でもカーブでも所々積層を失敗して、印刷面は荒れ凸凹になり、ビルドプートもフィラメントのカスだらけになってしまう。PETGフィラメントって、こんな感じ?

↓ 画像造形物の印刷条件

Ender-3 V3 SE
0.3mm ノズル Amazon
PETGフィラメント Amazon
スライサー UltiMaker Cura
レイヤー高さ 0.16mm
印刷速度 20mm/s
印刷温度 250℃
ビルドプート温度 80℃
ファン速度 1%
部屋温度 18℃

↓ フィラメントのカスも無く綺麗に造形出来ました。超時間かかったけど。

 

純正のエクストルーダーのカバーを外して、これにしたら、正面からノズルが良く見えるようになって、プリント中の確認をする姿勢が楽になりました。

このモデルの元になったオリジナルはネットに有り、それを少し私の考えを取り入れてOnshapeで作りました。

 

↓ 純正のカバーを外すと向かって左側にカバー用のビスとビス穴が1組余る。これを利用して解除レバーを操作しやすい様に突起物も造形しました。

 

【2024/11/18 追記】
PETGで造形したファンブラケットとエアダクトですが、これらを取り付けて印刷温度260℃、ビルドプート温度100℃で、別の物を連続6時間以上印刷したら、エアダクト(断面図のオレンジ色)が少し変形しました。

↓ 断面図

↓ 実際の画像

この後、エアダクトだけはABSフィラメントで造形しました。ファンブラケットもABSにしたいのですが、印刷中に積層割れしたり、印刷後まで割れしなくても、指で力を加えると必ず割れして脆いんです。

勿論、思っ切り力を加えればPLAやPETGだって割れちゃうけど、その割れ方とは違う。積層方向にカッターの刃を入れると、ABSは簡単に入るけど、PETGは苦労するくらい入らない。

エンクロージャーが無いとABSフィラメントは難しいのでしょうか。エンクロージャーが無い私のEnder-3 V3 SEでは、PLAやPETGフィラメントと比べるとABSは非常に脆いんです。

 

Ender-3 V3 SEのエクストルーダーモーターの軸に取り付けて、クルクル回るコールサインのエンブレムを造形しました。

Ender-3 V3 SE
0.3mmノズル
レイヤー高さ0.16mm
PLAフィラメント 緑と白の2色(手動でフィラメント交換)

 

3D model-viewer

 

【2024年12月12日追記】↑ 「回転軸と重心が合って無いな多分」とか思っていた。でも「重心の位置がわからん」でした。

Onshapeは出来る子です。予めバーツに材料を割り当てて置くと、右下の隅にあった(今日知った)天秤ばかりの⚖️アイコンをクリックする事で、重心の位置を教えてくれます。嬉しい事に無課金でも使える機能です。

↓ 上面から見たXY平面です。回転軸からやや右下に重心があるようです。