KiCADを使って作った基板をプリント基板メーカーに送る方法

以前はプリント基板を作ってもらうには最低でも5万円以上のお金がかかっていたため、アマチュアは自分でエッチングをしたりして苦労していたものですが今は場合によっては数百円で基板作成を注文できるようになりました。(送料が1000円以上かかりますが)

KiCADを使って基板データを作成したらガーバーデータを出力してプリント基板メーカーに送れば基板を作ってもらうことができます。

ここでは格安で基板を作ってもらえるElecrow向けのデータ出力方法について説明します。
他の基板メーカーにデータを出す場合は基本的に同じ作業でOKです。

データの出力ウィンドウを開く

プリント基板エディタのメニューでファイル→プロットを選択して製造ファイル出力のウィンドウを開きます。

1.ガーバーデータの出力

出力フォーマットは「ガーバー」を指定します

ガーバーオプションで「Protelの拡張子を使用」にチェックを入れます。
Protelとは業務用で最もシェアの高いプリント基板CADでElecrowがそのCADと同じ拡張子でガーバーファイルを作ることを要求しているためです。

他のメーカーに頼む場合もProtelと同じ拡張子を使ったほうが間違いが少ないと思います。

その他のオプションは上の通りでOKです。

レイヤの指定:2層基板の場合以下の6~7種のデータが必要となります。
4層基板の場合はさらに2つの内層レイヤデータを出力します。

F.Cu  : 部品面の銅箔バターンです。
B.Cu:ハンダ面の銅箔パターンです。
B.Silks : ハンダ面のシルクパターンです、裏面に実装部品が無ければ省略して構いません。
F.Silks:部品面のシルクパターンです、無くても構いませんが値段が安くなるわけでは無いのコンポーネントIDの他に基板名やバージョンなどを含めて出来るだけ多くの情報を記述しておくと後々助かります(経験談)
B.Mask:ハンダ面のレジストマスクです、レジストとはハンダ付けしないところに塗布して基板の腐食やショートを防ぐためのものです。
 普通、プリント基板が緑色をしているのは緑色のレジストを使っているためです。Elecrowでは緑の他に(赤)(黒)(黄)(白)などのレジストを指定出来るので基板のバージョンを区別するためなどに基板の色を変えることも出来ます。
F.Mask:部品面のレジストマスクです。
Edge.Cuts:基板外形です、基板の外形加工のために必要となります。長方形以外の任意の形を指定することが出来ます。

オプションとレイヤの指定が終わったら「製造ファイル出力」のボタンをクリックしてガーバーファイルを作成します。
ガーバーファイルの出力は一瞬で終わり、.gtl .gbl .gbo .gto .gbs .gts .gm1 の拡張子を持つファイルが指定したフォルダに出力されます。

2.ドリルデータの出力

「ドリルファイルの生成」ボタンをクリックしてドリルデータ出力ウィンドウを開きます。

上のようにオプションを指定して「ドリルファイル」のボタンをクリックすると拡張子.drlのドリルファイルが出力されます。
スルーホールメッキをしない取り付け穴などを使っているときには名前の後ろに-NPTHがついた別のドリルファイルが出力されます。
ドリルファイルはNCデータでメモ帳などのテキストエディタで開くことができます。
ドリルファイル以外は必要ありませんのでこれでメーカーに提出するデータがそろいました。

※ガーバーデータの確認

ガーバーデータを出力したらガーバービューアを使ってどのようなデータが出力されているかを確認することをお勧めします。
ガーバーデータの確認にはViewMateのフリーバージョンを使うことが出来ます。
出力されたガーバーデータを起動したViewMateのウィンドウにドラッグドロップすればガーバーデータとして出力されたパターンを確認できます。

stm32CubeMX で FSMCによるFPGAアクセスでトラブル

stm32f407IGT6を搭載したモーターコントロールボードでSTM32F4xx_StdPeriph_Driverライブラリを使って作っていたプログラムをバージョンアップしてSTM32Cube FW_F4 V1.16.0に対応した時にFPGAへのインターフェースで悩んだ件。

ボードはFSMCのSRAM較正でFPGAにアクセスしています。
以前のライブラリを使ったプログラムではサンプルプログラムを参考にした初期化で簡単に動いたのでSTM32CubeMXを使えばもっと簡単に出来ると思っていました。

しかしFPGAからの読み出しは動作するものの書き込みが出来ない不具合が発生し原因がどうしてもわからず、レジスタの内容をダンプして比較してみたり色々悩んで3日間ほど回り道をしてしまいました。

FSMCの設定のせいだと思い込んでいたのですが、結局判ったのはstm32f407のMCO出力端子からクロックが供給されていないことがトラブルの原因でした。

勿論端子からMCOを出力する設定は出来ていましたがGPIOの設定で出力端子の出力速度がLowになっていたため50MHzのクロックを出力すると殆どHiレベルのままになっていました。

STM32F4xx_StdPeriph_Driverライブラリを使ったプログラムでは初期化コードを自分で書いていくスタイルなのでこのような見落としは少なかったのですが、STM32CubeMXのように初期化コードが自動的に生成されてしまうとこのようなトラブルが時々発生します。

QtとBeagleBoneBlackでOpenCV

Qtで作るBeagleBoneのプログラムで画像処理ライブラリOpenCVを使う手順についてメモしておきます。

QtCreatorが動作するUbuntu 15.04 i386上でarm用のopencvを構築します。

こちらのサイトを参考にしました

1. Opencvのソースをダウンロードして解凍します
~$ mkdir opencv 
~$cd opencv 
~/opencv$ wget -O opencv-3.1.0.zip https://github.com/opencv/opencv/archive/3.1.0.zip 
~/opencv$ wget -O opencv_contrib-3.1.0.zip https://github.com/opencv/opencv_contrib/archive/3.1.0.zip 
~/opencv$ unzip opencv-3.1.0.zip 
~/opencv$ unzip opencv_contrib-3.1.0.zip
2.使用するコンパイラに合わせてopencv-3.1.0/platforms/linux/arm-qnueabi.toolchain.cmakeを修正します

GCC_COMPILER_VERSIONを”4.6″から”5″に変更しました

set(GCC_COMPILER_VERSION "5" CACHE STRING "GCC Compiler version")
3. cmakeとmakeで構築してインストールします
~/opencv$ cd opencv-3.1.0
~/opencv/opencv-3.1.0$ mkdir build-arm
~/opencv/opencv-3.1.0$ cd build-arm

~/opencv/build/build-arm$ cmake -D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/opt/opencv3.1-arm \
-D CMAKE_TOOLCHAIN_FILE=~/opencv/opencv-3.1.0/platforms/linux/arm-gnueabi.toolchain.cmake \
-D CMAKE_AR=/usr/arm-linux-gnueabihf/bin/ar \
-D OPENCV_EXTRA_MODULES_PATH=~/opencv/opencv_contrib-3.1.0/modules \
-D ENABLE_NEON:BOOL=ON \
-D WITH_TBB=ON \
-D BUILD_TBB=ON \
-D WITH_QT=OFF \
-D WITH_OPENGL=OFF \ 
-D SOFTFP=ON \
..

~/opencv/opencv-3.1.0/build-arm$ sudo make j2

~/opencv/opencv-3.1.0/build-arm$ sudo make install
3.BeagleBoneBlackへライブラリを転送します
~$ scp /opt/opencv3.1-arm/lib/* root@192.168.0.xxx:/usr/lib
4.Qtプロジェクトの設定  

myproject.pro の最後にインクルードパスとライブラリの参照を追加します

DEPENDPATH += /opt/opencv3.1-arm/include
INCLUDEPATH += /opt/opencv3.1-arm/include
LIBS += -L/opt/opencv3.1-arm/lib/ \
 -lopencv_core \
 -lopencv_imgproc \
 -lopencv_highgui \
 -lopencv_imgcodecs
5. OpenCVのサンプル

Qtデータ形式との変換、2値化

cvfunc.h

#ifndef CVFUNC_H
#define CVFUNC_H

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/core/core.hpp"
#include <iostream>
#include <stdlib.h>

extern void read_qimageformat(cv::Mat &mat, const char *filename);
extern void read_qimageformat0(cv::Mat &mat, const char *filename);
extern void binarize(cv::Mat &mat, const char *filename);
extern void detect_plate(cv::Mat &mat, int th);
extern void mask_plate(cv::Mat mat, cv::Mat &dst, int th);
extern void copypart(cv::Mat src, cv::Mat &dst, int x0, int y0);

#endif // CVFUNC_H

cvfunc.cpp

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/core/core.hpp"
#include <iostream>
#include <stdlib.h>
#include "mainwindow.h"
#include <QtGui>

#include "cvfunc.h"

using namespace cv;

// copy part of image
// input : src,dst: CV_8UC3
void copypart(cv::Mat src, cv::Mat &dst, int x0, int y0)
{
  for(int y=0; y < dst.rows; y++){
    for( int x=0; x < dst.cols; x++){
      dst.data[y * dst.step + x * dst.elemSize() + 0] // G
        = src.data[(y+y0) * src.step + (x+x0) * src.elemSize() + 0];
      dst.data[y * dst.step + x * dst.elemSize() + 1] // B
        = src.data[(y+y0) * src.step + (x+x0) * src.elemSize() + 1];
      dst.data[y * dst.step + x * dst.elemSize() + 2] // R
        = src.data[(y+y0) * src.step + (x+x0) * src.elemSize() + 2];
    }
  }
}

// Detect plate
// input : mat: CV_8UC1, th:threshold
void detect_plate(cv::Mat &mat, int th)
{
 // srcと同じサイズで8ビット×3チャンネルのMatデータを作成
 // cv::Mat (GBR) を QImage (RGB)に入れ替える
  for(int y=0; y < mat.rows; y++){
    for( int x=mat.cols-3; 0 < x ; x--){
      if( mat.data[y * mat.step + x] <= th )
        mat.data[y * mat.step + x] = 255;
      else break;
    }
  }
}

// Mask plate
// input : mat,dst: CV_8UC1, th:threshold
void mask_plate(cv::Mat mat, cv::Mat &dst, int th)
{
 // srcと同じサイズで8ビット×3チャンネルのMatデータを作成
 // cv::Mat (GBR) を QImage (RGB)に入れ替える
  for(int y=0; y < mat.rows; y++){
    for( int x=mat.cols-3; 0 < x ; x--){
      if( mat.data[y * mat.step + x] <= th )
        dst.data[y * dst.step + x] = 255;
      else break;
    }
  }
}

//
// OpenCv形式からQImage形式への変換
//
void read_qimageformat(cv::Mat &mat, const char *filename)
{
 // Matにイメージを読み込む
  Mat src = imread(filename,IMREAD_COLOR);
 // cv::Mat と QImage で異なるのでRGBの順番を入れ替える
  cvtColor(src, mat, CV_RGB2BGR); //OpenCVの命令でRGBの順番を入れ替える
 // 次のようにしてQIMageに変換する
 //QImage qimg(mat.data, mat.cols, mat.rows, QImage::Format_RGB888);
}

//
// OpenCv形式からQImage形式への変換
// Matの初期化と画素直接アクセスのサンプル
//
void read_qimageformat0(cv::Mat &mat, const char *filename)
{
 // Matにイメージを読み込む
  Mat src = imread(filename,IMREAD_COLOR);
 // srcと同じサイズで8ビット×3チャンネルのMatデータを作成
  Mat dst(src.size().height,src.size().width,CV_8UC3);
 // cv::Mat (GBR) を QImage (RGB)に入れ替える
  for(int y=0; y < src.rows; y++){
    for( int x=0; x < src.cols; x++){
      dst.data[y * src.step + x * src.elemSize() + 0] // R
        = src.data[y * src.step + x * src.elemSize() + 2];
      dst.data[y * src.step + x * src.elemSize() + 1] // G
        = src.data[y * src.step + x * src.elemSize() + 0];
      dst.data[y * src.step + x * src.elemSize() + 2] // B
        = src.data[y * src.step + x * src.elemSize() + 1];
    }
  }
  mat = dst;
}

#if 0
//
// 二値化
//
void binarize(cv::Mat &mat, const char *filename)
{
 // Matにイメージを読み込む
  Mat src_img = imread(filename,IMREAD_COLOR);
 // srcと同じサイズで8ビット×1チャンネルのMatデータを作成
  Mat gray_img(src_img.size(),CV_8UC1);
 // グレースケールに変換
 //cvtColor(src_img, gray_img, CV_BGR2GRAY);
  cvtColor(src_img, gray_img, CV_);
 // 変換先のイメージ作成
  Mat bin_img(src_img.size(),CV_8UC1);
#if 0
 // 二値化、大津の手法(自動的にスレッショルドを決定)
  threshold(gray_img, bin_img, 0, 255,  CV_THRESH_BINARY|CV_THRESH_OTSU);
#else
 // (3)apply an adaptive threshold to a grayscale image
  adaptiveThreshold(gray_img, bin_img, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 7, 8);
#endif
 // bin 8bit x 1ch を mat 8bit x 3ch に変換
  cvtColor(bin_img, mat, CV_GRAY2RGB);
}
#else
//
// 二値化
//
void binarize(cv::Mat &mat, const char *filename)
{
 // Matにイメージを読み込む
  Mat src_img = imread(filename,IMREAD_COLOR);
 // srcと同じサイズで8ビット×1チャンネルのMatデータを作成
  Mat gray_img(src_img.size(),CV_8UC1);
 // 青色を取り出すグレースケールに変換
 //cvtColor(src_img, gray_img, CV_BGR2GRAY);
  for(int y=0; y < src_img.rows; y++){
    for( int x=0; x < src_img.cols; x++){
      gray_img.data[y * gray_img.step + x * gray_img.elemSize() + 0] //
        = src_img.data[y * src_img.step + x * src_img.elemSize() + 0]; // B
    }
  }
#if 0 // 青色のグレースケール表示
 // gray_img 8bit x 1ch を mat 8bit x 3ch に変換
  cvtColor(gray_img, mat, CV_GRAY2RGB);
#else
 // 変換先のイメージ作成
  Mat bin_img(src_img.size(),CV_8UC1);
#if 0
 // 二値化、大津の手法(自動的にスレッショルドを決定)
  threshold(gray_img, bin_img, 0, 255, CV_THRESH_BINARY|CV_THRESH_OTSU);
#else
 // (3)apply an adaptive threshold to a grayscale image
  adaptiveThreshold(gray_img, bin_img, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 7, 8);
#endif
 // bin 8bit x 1ch を mat 8bit x 3ch に変換
  cvtColor(bin_img, mat, CV_GRAY2RGB);
#endif
}

#endif