どんな分野でもまず「温度感」をつかむのが大事だと思っていて、そもそも「温度感」って何かというと、どの部分の神経を使うか、熱量を使って考えて、どこの手を抜くかという話です。「コツ」を少し具体的に表現して「温度感」といっている、というとちょっと身も蓋もないのですが。

3Dプリンタのモデル設計の温度感

ここでいう 3D プリンタは普及している FDM (Fused Deposition Modeling・熱溶解積層法) の話です。FDM は基本的に X/Y 精度が悪く、Z精度はそれに比べると高いという特徴があります。

具体的には X/Y については±0.5mmぐらいで、Zは±0.1mmで考えます。特に X/Y の精度の悪さは設計難易度にかかってきます。同一条件で出力したパーツを組合せる分にはともかく、他の規格品、例えばナットや、あるいは三脚ネジのようなものを出したいときは、だいたいの場合は何度かプリントして現物あわせをする必要があるのです。

±0.5mm これはキャリブレーションしてフィラメント量を調節していてもなお生じる誤差と考えたほうが良くて、キャリブレーションしていなければもっと精度が出ません。

動くすきまを確保したい場合

基本的に動く面すべてに 0.5mm のトレランスをもうけます。つまり引き出しみたいなものなら、1mm 小さい立体を入れるようにします。

ぴったり圧入する場合

パズルのピースのようにハメこむ場合は、0.25mmのトレランスをもうけます。四角に四角をハメる場合 0.5mm 小さい立体になります。しかし実際には素直にうまくいくことは少ないでしょう。絶対に失敗しない方法は 0.2mm 程度のトレランスで、出力してから削ることです。

圧入して固定するような場合、これはかなり難しいですが、形状で工夫すると楽になります。設計上は、内接多角形の柱と同一内径の穴をモデリングして無理矢理ハメこむのが楽です。

  1. トップ
  2. tech
  3. 3Dプリンタで出力するモデル設計の温度感

しばらくすると忘れてしまうので、現状のフローをまとめておく。

  1. pcb2gcode の millproject
  2. AutoLeveller は基本的には常時使うほうが結果的に早い。DIP しか使わないような電源回路では使わなくてもなんとかなるけど使ったほうが安心。
  3. 基板外形のカッターは 1.5mm が適当。1mm だと 500mm/min でフィードするとちょいちょい折れてストレス
  4. パターン切削は30度0.1mmのカッター

概要

  • KiCAD からの出力
  • pcb2gcode の設定
  • machinekit (LinuxCNC) での操作

KiCAD でのデザインルール

0.25mm 幅で削るので、これが限界。0.05mm でもトレランスをとれれば安定しやすい。

  • 最小配線幅は 0.25mm が限界。できれば 0.3mm のほうが安定。
  • クリアランスも 0.25mm が限界。できれば 0.3mm ぐらいにしたい。

KiCAD からのエクスポート

- 切削する銅箔層 (F.Cu or B.Cu) と Edge.Cut をガーバーに出力する
- ドリルファイルを出力する

pcb2gcode

以下のような前提で gcode 化する

  • 30° 先端0.1mm のVカッターを掘る用に使う
  • φ0.8mm のエンドミルを全ての穴開けに使う
  • φ1.5mm のエンドミルかφ1mmで2枚刃を外形カットに使う
  • 基板の厚さは 0.8mm か 1.6mm を使う。基板の歪みを矯正できるぶん、0.8mm のほうが細かいパターンは削りやすく感じる。

先端0.1mmのエンドミルといっても、実際買ってみると綺麗に 0.1mm になっていることはすくない。というかすぐ先端が折れる。計算するときは 0.15〜0.20mm として扱う。

切削速度を落とせばφ0.8mmで穴開け〜外形カットまで可能で、ツール取り替えの手間が減らせるはずだけど、0.8mm のミルは高価であんまり折りたくないので、別途φ1.5mmのミルを使っている。これは今後 0.8mm にするかも。あんまり太いミルで外形カットすると切削抵抗が大きくなり、両面テープが剥れてずれる可能性もあるので注意。

# Use standard mm
metric=true
metricoutput=1

front=tachometer-B_Cu.gbr
#back=
outline=tachometer-Edge_Cuts.gbr
drill=tachometer.drl

front-output=front.gcode
back-output=back.gcode
outline-output=outline.gcode
drill-output=drill.gcode
milldrill-output=milldrill.gcode

# https://github.com/chrysn-pull-requests/pcb2gcode/blob/graphical-documentation/man/options.svg
zwork=-0.1
zsafe=1
mill-feed=220
mill-speed=10000
mill-vertfeed=150
mill-diameters=0.22
milling-overlap=25%
isolation-width=0.5

zdrill=-1.7
zmilldrill=-1.7
zchange=20
drill-feed=100
drill-speed=10000
min-milldrill-hole-diameter=0
milldrill-diameter=0.8

# 外形カット時のミル直径
cutter-diameter=1
# PCB板厚+0.1mm
zcut=-1.7
cut-feed=500
cut-speed=10000
cut-infeed=1
cut-side=front
# mirror-axis=51.575mm

optimise=true
zero-start=true

pcb2gcode 2.0.0 で動くように変更

pcb2gcode して生成 gcode をデスクトップとリモートの Machinekit に転送する。デスクトップにコピーしているのは CAMotics で読みこませて確認するときに便利なので。

  • mirror-axis を基板サイズの半分に設定すること
    • これやらないと back 面がマイナスからはじまってしまう
    • 一度実行して表示される width を元に書きかえる必要がある
~/ghq/github.com/pcb2gcode/pcb2gcode/pcb2gcode && cp *.gcode ~/Desktop/ && scp *.gcode machinekit@192.168.0.240:gcode

確認

  • CAMotics を起動して、すべてのファイルを読みこむ。
  • Workpiece を以下のように設定して再描画
    • X: xxx/0
    • Y: xxx/0
    • Z: 1.6/-1.6

段取り

  • 事前に捨て板の面出しをする (φ6mm ぐらいのミルで加工面全てを 0.2mm ほどさらう)
    • AutoLeveller 使うなら毎回やる必要はない。
  • ニチバン ナイスタック 透明タイプ (材質がセロハンのもの) を使って切削基板を捨て板に固定する
  • このときしっかり全面を押さえつけて捨て板に接着する (加工時の圧力で高さが変わらないように)

AutoLeveller

  • AutoLeveller に切削対象 gcode を読みこませる
  • Probe Clearance は 0.5〜1、Z Safe Height は 1、Z feed は 50 にする
  • create probe file only にチェックを入れて、Create Levelled Gcode ボタンを押すと、ALProbeback.ngc ができる
  • ALProbeback.ngc を machinekit に読みこませる
  • 一旦 Probe を行い、Z を Touch Off する (原点を設定する)
  • 実行して基板全体を Probe する。
  • .ini と同じディレクトリに RawProbeLog.txt ができているので、ローカルに転送する
    • gcode 用のディレクトリに RawProbeLog.txt への symlink を貼っておくと便利
  • RawProbeLog.txt を AutoLeveller に読みこませる
  • create probe file only にチェックをはずして Create Levelled Gcode を押す
  • ALback.ngc ができるので転送する

drill や外形カットなどは面倒なので AutoLeveller はかけず、0.1mm 余計に掘る。

https://lowreal.net/2016/10/19/1

AutoLeveller の起動

java -cp ~/ghq/bitbucket.org/daedelus1982/autoleveller/out/artifacts/autoleveller_jar/autoleveller.jar  com.cncsoftwaretools.autoleveller.Autoleveller

生成 gcode の転送

scp ~/Desktop/*.ngc machinekit@192.168.0.240:gcode 

プローブログを手元に転送

scp machinekit@192.168.0.240:gcode/RawProbeLog.txt ~/Desktop/

切削手順

基板パターン

  • ALback.ngc を machinekit に読みこませる
  • 実行すると Probe をつけろと言われるので、つけて Resume
  • Probe後、Probe をはずせと言われるので、はずして Resume
  • 切削がはじまる

ドリル

  • エンドミルをφ0.8mmに交換
  • Probe を行い、Z Touch Off
  • Feed Override を 10% ぐらいに下げる (折れ対策。pcb2gcode が mill 時と milldrill 時でフィードレートが変えられないので)
  • drill.gcode を読みこんで実行

外形

  • エンドミルをφ1.5mmに交換
  • Probe を行い、Z Touch Off
  • outline.gcode を読みこんで実行

備考

pcb2gcode の --al 系オプションは使わないの?

ある程度規模が大きくなると machinekit に読みこませたときに非常に時間がかかってしまうので、その時間を使うぐらいなら AutoLeveller を使ったほうがよいと考える。時間がかかるのはプレビューのために gcode を一通り仮想的に実行しているためだと思う。

また、--al のオプションは Probe → 切削 がひとつの gcode で行われるため、リトライすることができない。途中でミルが折れたりすると交換してやりなおすということができなくなる。

エンドミルの不具合

基板切削をはじめた直後に削った銅がけばだつようだったらミル先端が既に折れている可能性がある。そのまますすめてもうまくいかないので新しいのに変えること

または確認しても折れていないようなら、ミルの突き出し量を見直すこと。ぎりぎりまでマシンに指しこんでいないと、同様にけばだつことがある。

  1. トップ
  2. tech
  3. KiCAD + Machinekit (LinuxCNC) で切削して基板をつくるときの手順

秋月でLPC11U35 が乗っているボードを買った。うっかり3枚買っていた。EA LPC11U35 QuickStart Board
と互換のもの。

RAM や Flash が少なめだけど以下の点で魅力的なボード

  • mbed 対応
  • 850円と安価
  • 単体で USB 経由で書きこめる
    • デバッグするならシリアルは別途繋ぐ必要あるけど
  • 別のチップに対して USB SWD インターフェイスになれる

コード的な備考を先に書いておく

  • LED1 〜 LED4 は全部 P0_7 のエイリアスになっている。
  • USBTX/USBRX はサポートされない

Lチカのコードは何の変哲もない。

#include "mbed.h"

DigitalOut led(LED1);
// Serial serial(UART_TX, UART_RX);

int main() {
	for (;;) {
		led = 1;
		wait(0.5);
		led = 0;
		wait(0.5);
	}
}

ビルドするも動かない

platformio でビルドしたのを書きこんでもさっぱり動かず、再度 CRP DISABLED というボリュームがマウントされてしまう。オンラインコンパイラでは動くので、手元の環境の問題であることはわかったが、なかなか原因がわからなかった。

結局 platformio は mbed OS 5 の環境でビルドしようとするが、LPC11U35 では RAM が足りず起動できないようだ。

実際、mbed の公式を見て mbed OS 5 系に対応するボードをリストにすると (そういうことができることにはじめて気付いたが)、LPC11U35 は出てこない。

platformio でフレームワークのバージョンを指定してビルドする方法がどう調べてもわからなかった。

mbed cli を使う

そういうことで、いろいろ試したけど、あきらめて platformio のことは忘れましょう。

ARM 公式で提供されている mbed-cli をいれるのが今のところは最良のようです。

インストール

まず Python のツールなので python が必要なのと、pip も必要です。ここでは

  • python は macOS のシステムに入っているものを使う
  • pip はグローバルに入れる
  • mbed-cli もグローバルに入れる
  • ほかだいたいの依存モジュールはユーザー領域にいれる (pip --user)
    • 既存の setuptools をアップグレードしようとするのだが OS 保護に守られて root でも上書きできないので、基本的に --user で入れたほうが良い。

という感じでいきます。

curl https://bootstrap.pypa.io/get-pip.py | sudo python
sudo pip install mbed-cli IntelHex

mbed-cli には GCC_ARM のツールチェーンを入れてくれる機能はなく、前もって arm-none-eabi-gcc とかを入れておく必要がある。https://launchpad.net/gcc-arm-embedded とかを入れてパスを通しておく。

もしくは、platformio で既に入れてあるのがある場合は以下のようにしてパスを通せば使える。

export PATH=$HOME/.platformio/packages/toolchain-gccarmnoneeabi/bin:$PATH

プロジェクト作成

今回使う LPC11U35 は mbed OS 5 未対応のため、mbed 2 (いろいろ別名がある。mbed library / mbed classic など) を使うようにプロジェクトを作る。--mbedlib を指定すると mbed 2 環境になる。

sketch はプロジェクト名。

mbed new sketch --mbedlib
cd sketch
vim main.cpp
mbed deploy
pip install --upgrade --ignore-installed --user -r .temp/tools/requirements.txt
mbed compile -t GCC_ARM -m LPC11U35_401
# LPC11U35 を USB かきこみモードにして
cp ./BUILD/LPC11U35_401/GCC_ARM/sketch2.bin /Volumes/CRP\ DISABLD/firmware.bin 
#リセットボタンで動く

しかし mbed-cli は凶悪で、プロジェクトディレクトリ以下に 2GB ぐらいファイルをコピーしてくる (mbed 2 の全ソースコード)。

自分は複数端末 (ノートとデスクトップ) で開発する関係で Dropbox のディレクトリで同期させておくことが多いのだが、これだと死ねる。つらい。

ピン名など

回路図

見比べると結構違う。

  • Q1 の違い。秋月版はFET
  • 3.3V レギュレータの違い
  • リセットICの違い
  • USB 入力の保護の違い

~/.platformio/packages/framework-mbed/targets/TARGET_NXP/TARGET_LPC11UXX/TARGET_LPC11U35_401 以下にある。

https://os.mbed.com/users/mbed_official/code/mbed-dev/file/57724642e740/targets/TARGET_NXP/TARGET_LPC11UXX/

  1. トップ
  2. tech
  3. LPC11U35 の mbed ローカル開発環境構築

LM1972 デジタルボリューム | tech - 氾濫原 の続きで、Arduino ではなくmbed環境での実装。

LPC11U35 で試した。AD1 にBカーブボリュームをつけてこれによって 78dBから0dbまでアッテネーションレベルをかえる。

#include "mbed.h"

// mosi miso sck
//SPI spi(P0_9, P0_8, P0_10);
//DigitalOut cs(P0_11);

class LM1972 {
    SPI spi;
    DigitalOut cs;
    const uint8_t daisy_chain;

public:
    static const uint8_t MUTE = 0xff;

    // uint16_t to 1dB step attenuation value
    static inline uint8_t volumeToAttenuation(const uint16_t v) {
        // volume 0%   -> 78dB (126)
        // volume 100% -> 0dB (0)
        const uint8_t dB =  ((uint32_t)(1023 - v) * 78 / 1023);
        uint8_t att = 0;
        if (dB < 48) {
            att = 2 * dB;
        } else {
            att = 96 + dB - 48;
        }
        return att;
    }

    static inline uint8_t volumeToAttenuation(const float v) {
        // volume 0%   -> 78dB (126)
        // volume 100% -> 0dB (0)
        const uint8_t dB =  78 * v;
        uint8_t att = 0;
        if (dB < 48) {
            att = 2 * dB;
        } else {
            att = 96 + dB - 48;
        }
        return att;
    }

    LM1972(
            PinName _mosi,
            PinName _miso,
            PinName _sck,
            PinName _cs,
            uint8_t _daisy_chain = 1
          ) :
        spi(_mosi, _miso, _sck),
        cs(_cs),
        daisy_chain(_daisy_chain)
    {
        spi.format(8, 0);
        spi.frequency(1e6);
        cs = 1;
    }

    inline void setAttenuation(const uint8_t channel, const uint8_t v) {
        cs = 0;
        // >150ns
        wait_us(2);
        spi.write(channel);
        spi.write(v);
        // >150ns
        wait_us(2);
        cs = 1;
    }

    uint8_t setVolume(const float v) {
        const uint8_t att = volumeToAttenuation(v);
        for (int i = 0; i < daisy_chain; i++) {
            setAttenuation(0, att);
            setAttenuation(1, att);
        }
        return att;
    }

    uint8_t setMute() {
        for (int i = 0; i < daisy_chain; i++) {
            setAttenuation(0, MUTE);
            setAttenuation(1, MUTE);
        }
        return MUTE;
    }
};

DigitalOut led(LED1);
Serial serial(UART_TX, UART_RX);
AnalogIn pot(P0_12); // AD1

LM1972 volume(P0_9, P0_8, P0_10, P0_11);

int main() {
    serial.baud(9600);
    serial.printf("init\r\n");

    volume.setMute();

    for (;;) {
        led = 1;
        wait(0.5);
        led = 0;
        wait(0.5);

        float val = pot.read();
        serial.printf("pot val = %d\r\n", (int)(val * 100));
        uint8_t att = volume.setVolume(val);
        serial.printf("set att = %x\r\n", att);
    }
}

多摩川河口付近にある旧穴守稲荷神社大鳥居 (大田区羽田空港) から、阿蘇神社鳥居 (東京都羽村市) までのサイクリングロードを往復で走った。116kmを休憩込みで5時間37分ぐらい。

少し前にほぼ同じことをしたんだけど、完全走破ではなかったので、もう一度チャンレンジした。この往復は目標のひとつだったのでひとまず達成できてよかった。

多摩川は今、各所で堤防強化の工事をやっていて、本来のルートを通れないことが多い。堤防上と河川敷をいったりきたりすることになる。

今の自分だとこのぐらい走ると限界。気温5℃ぐらいで寒くて寒くてつらかった。