KT0913を搭載したDSPラジオモジュールのPCBAを中国試作基板メーカPCBWayに製造依頼してわかったこと

電子工作

はじめに

先日のブログではKTMicro製ラジオICのKT0937を搭載した小型ワイドFMラジオモジュールの製作記事をご紹介しましたが、KT0913でも同形状のモジュールを製作してみました。先行で製作したKT0937を用いたモジュールはJLCPCBに製造依頼をしたのですが、KT0913版のモジュールについてはPCBWayに依頼してみました。よく似た仕様となるPCBAの製作で業者間で何か違いがあるのかの確認をしてみたかったからです。

モジュールの紹介

KT0937版と簡単に置き換えが可能となるような考えのもとに、外形寸法はKT0937版と同一とし、端子配列も電源とGNDおよびI2C入出力端子は同じ配列にしていますので、M5stick-C PlusのHatデバイスとして簡単に使用できます。

Pin No.Pin NameI/OFunction
1GNDGroundGround
2NCNon Connection
3SCLDigital I/OSCL of 2-wire interface
4ENInputChip enable. internal 600kΩ Pulldown
5SDADigital I/OSDA of 2-wire interface
6NCNon Connection
7+3.3VPowerPower supply (※2.1~3.6V)
8NCNon Connection

プログラムの紹介

KT0913はデフォルトのレジスタ設定のままで、ひとまずFMが受信できるようになっており、受信周波数さえ設定すれば良く、とても簡単にFMラジオの受信が楽しめるICです。

私はハードウェアはM5SstackやArduinoのシリーズを使用しながら、趣味として電子工作とArduinoやpythonでのプログラムを学習していますが、今回はArduinoのスケッチ(プログラム)を作成しましたので参考にご紹介します。

// #include <M5Stack.h>//  Environment
// software Arduino version 1.8.15
// board ---> M5stack Arduino(M5stack 2.1.0) ---> M5stickCPlus
// library:M5StickCPlus Version 0.1.0
// library:M5GFX Version 0.1.12

#include <M5Unified.h>
#include <Wire.h>

unsigned const char SDA_hat = 0;
unsigned const char SCL_hat = 26;
unsigned const char EN_pin = 25; // High: Chip enable

uint16_t data ; //  レジスタアドレス2byteデータ格納用

unsigned char num_byte = 2; // 受信バイト数
unsigned char DEVICE_ADDRESS = 0x35; // デバイスアドレス固定
uint8_t REGISTER_ADDRESS;
String FMMODE;
String AM_FM;

//レジスタアドレス
unsigned char CHIP_ID = 0x01;
unsigned char SEEK = 0x02;
unsigned char TUNE = 0x03; //FM Channel
unsigned char VOLUME = 0x04; //Softmute / Mute / Bass Boost Efect / Audio DAC Anti-pop
unsigned char DSPCFGA = 0x05; //Mono
unsigned char LOCFGA = 0x0A;
unsigned char LOCFGC = 0x0C;
unsigned char RXCFG= 0x0F; // Standby mode / Volume Control
unsigned char STATUSA = 0x12; //RSSI
unsigned char STATUSB = 0x13;
unsigned char STATUSC = 0x14; //SNR
unsigned char AMSYSCFG = 0x16; // AM/FM mode Control / Audio Gain Selection
unsigned char AMCHAN = 0x17; // AM Channel Setting
unsigned char AMCALI = 0x18; //On Chip Capacitor for AM
unsigned char GPIOCFG = 0x1D; // Vol Pin Mode Selection, CH Pin Mode Selection
unsigned char AMDSP = 0x22; // AM Channel Bandwidth Selection
unsigned char AMSTATUSA = 0x24; // AM Channel RSSI
unsigned char AMSTATUSB = 0x25;
unsigned char SOFTMUTE = 0x2E; // Softmute Attenuation
unsigned char USERSTARTCH = 0x2F;
unsigned char USERGUARD = 0x30;
unsigned char USERCHANNUM = 0x31;
unsigned char AMCFG = 0x33; //AM Channel Space Selection , Working mode selections when key mode is selected.
unsigned char AMCFG2 = 0x34;
unsigned char VOLGUARD = 0x3A;
unsigned char AFC = 0x3C;
//
unsigned int dial = 0;

const unsigned int FM[]= {	
  // 78.3Mhz
  0x03,0x85F0, // example fmtune1, 76MHz(1520*50KHz) 1520=05F0
  0x04,0xC0B0, // SoftMute dis, Mute enable 10uF
  0x05,0x1800, // stereo, DE50
  0xff,0xffff,
};

void setup() {

  auto cfg = M5.config(); 

  M5.begin(cfg);
  M5.Display.setCursor(0, 0); 
  M5.Display.setFont(&fonts::Font0);
  M5.Display.setTextSize(3)
  M5.Display.print("KT0913");
  Serial.println("KT0913 RADIO sample program");
  pinMode(EN_pin, OUTPUT);
  digitalWrite(EN_pin, HIGH);
  delay(100);

  Wire.begin(SDA_hat, SCL_hat);

  int i=0;
  while (FM[i] !=0xff) {
    Wire.beginTransmission(DEVICE_ADDRESS);
    Wire.write(FM[i]);
    Wire.write(highByte(FM[i+1]));
    Wire.write(lowByte(FM[i+1]));
    Wire.endTransmission();
    i+=2;
    delay(100);
  }

  fm_mode();
  
  dial = 0;
  REGISTER_ADDRESS = GPIOCFG;
  data = 0B0000000000000000;
  I2CsendMULTIbyte();

  REGISTER_ADDRESS = VOLUME; 
  I2CreadMULTIbyte();
  uint16_t dsmute_cfg = data; 
  data = dsmute_cfg | 0B1110000010110000;
  I2CsendMULTIbyte();

  REGISTER_ADDRESS = RXCFG; 
  I2CreadMULTIbyte();
  uint16_t rxcfg_cfg = data;
  data = (rxcfg_cfg & 0B1111111111100000)+0B01000;
  I2CsendMULTIbyte();
}

void I2CsendMULTIbyte() {
  Wire.beginTransmission(DEVICE_ADDRESS);
  Wire.write(REGISTER_ADDRESS);
  Wire.write(highByte(data));
  Wire.write(lowByte(data));
  Wire.endTransmission();
}

void I2CreadMULTIbyte() {
  Wire.beginTransmission(DEVICE_ADDRESS)
  Wire.write(REGISTER_ADDRESS);
  Wire.endTransmission(false);
  Wire.requestFrom(DEVICE_ADDRESS,num_byte);
  while (Wire.available() < num_byte);
  uint8_t Hb = Wire.read();
  uint8_t Lb = Wire.read();
  data = (Hb << 8) | Lb;
}

void fm_mode() {
  REGISTER_ADDRESS = AMSYSCFG;
  I2CreadMULTIbyte();
  uint16_t amsyscfg_mode = data;
  data = 0B0100000000000010;
  I2CsendMULTIbyte();
  uint16_t am_fm = (data & 0B1000000000000000) >> 15;
  if (am_fm == 1) AM_FM ="AM";
  else AM_FM ="FM"; 
  
  Serial.printf("AM/FM: %S\n", AM_FM); 

  REGISTER_ADDRESS = STATUSA;
  I2CreadMULTIbyte();
  uint16_t statusa = data;
  uint16_t fmmode = (statusa & 0B000001100000000) >> 8;

  if (fmmode == 3) FMMODE ="STEREO";
  else FMMODE ="MONO"; 
  M5.Display.setCursor(0, 95);
  M5.Display.printf("FMMODE: %s\n", FMMODE);
  Serial.printf("FMMODE: %S\n", FMMODE); 

  REGISTER_ADDRESS = AMSYSCFG; 
  I2CreadMULTIbyte();
  uint16_t amsyscfg_band = data;
  data = amsyscfg_band | 0B0100000010000010;
  I2CsendMULTIbyte();
  
  REGISTER_ADDRESS = USERSTARTCH; 
  data = 0B0000010111110000; // 76MHz = 1520 * 50kHZ (1520= 5F0h)
  I2CsendMULTIbyte();

  REGISTER_ADDRESS = USERGUARD; 
  data = 0B0000000000010111; //23ch  2kohm
  I2CsendMULTIbyte();

  REGISTER_ADDRESS = USERCHANNUM; 
  data = 0B0000000010110101; //181ch  76MHz - 94MHz  100KHz/step  8kohm
  I2CsendMULTIbyte();
}

void loop() {

  delay(1);
  M5.update();

  REGISTER_ADDRESS = CHIP_ID;
  I2CreadMULTIbyte();
  uint16_t chipID = data;
  Serial.printf("chipID: %x\n", chipID); 
  REGISTER_ADDRESS = STATUSA;
  I2CreadMULTIbyte();
  uint16_t statusa = data;
  uint16_t fmrssi = (statusa & 0B000000011111000) >> 3;
  int16_t FMRSSI = -100+fmrssi*3;
  uint16_t fmmode = (statusa & 0B000001100000000) >> 8;

  if (fmmode == 3) FMMODE ="STEREO";
  else FMMODE ="MONO  "; 

  Serial.printf("RSSI: %d\n", FMRSSI); 

  M5.Display.setCursor(0, 95);  
  M5.Display.printf("FMMODE: %S\n", FMMODE);
  Serial.printf("FMMODE: %S\n", FMMODE); 

  REGISTER_ADDRESS = STATUSC;
  I2CreadMULTIbyte();
  uint16_t statusc = data;
  uint16_t snr = (statusc & 0B0001111111000000) >> 6;

  Serial.printf("SNR: %d\n", snr); 

  REGISTER_ADDRESS = RXCFG;
  I2CreadMULTIbyte();
  uint16_t rxcfg = data;
  uint16_t volume = rxcfg & 0B0000000000011111;
  M5.Display.setCursor(0, 22);
  M5.Display.setFont(&fonts::Font0);
  M5.Display.setTextSize(3);
  M5.Display.printf("VOLUME");
  M5.Display.setCursor(0, 45);
  M5.Display.setFont(&fonts::Font7);
  M5.Display.setTextSize(1);
  M5.Display.printf("%03d", volume*100 /31);
  M5.Display.setCursor(100, 58);
  M5.Display.setTextSize(3);
  M5.Display.setFont(&fonts::Font0);
  M5.Display.print("%");
  
  M5.Display.setFont(&fonts::Font0);

  if (AM_FM == "FM") {
    REGISTER_ADDRESS = TUNE;
    I2CreadMULTIbyte();
	uint16_t tune = data;
	uint16_t fm_freq = tune & 0B0000111111111111;

	M5.Display.setCursor(10, 70);
	M5.Display.setFont(&fonts::Font0);
	M5.Display.setTextSize(3); 
	M5.Display.setCursor(25, 150);
	M5.Display.setTextSize(1);
	M5.Display.setFont(&fonts::Font7);
	M5.Display.printf("%03.1f", (float)fm_freq/1000*50);
	M5.Display.setCursor(75, 200);
	M5.Display.setTextSize(3);
	M5.Display.setFont(&fonts::Font0);
	M5.Display.print("MHz");

	Serial.printf("FM_FREQ: %.1f\n   MHz", (float)fm_freq/1000*50); 
	M5.Display.setFont(&fonts::Font0);

	Serial.printf("tune: %d\n", tune); 
	Serial.printf("dial: %d\n", dial); 
	Serial.printf("AMFM: %S\n", AM_FM); 

  }

  if (M5.BtnA.wasPressed() && dial == 0 && AM_FM == "FM") {
    REGISTER_ADDRESS = TUNE;
    I2CreadMULTIbyte();
    data = (data + 0x0002) | 0B1000000000000000; // + 100KHz step   = 100/50  76MHz - 94.1MHz
    if ( (data & 0B0000111111111111)  > 1882){
      data = 0B1000010111110000; // 76MHz 1520 = 5F0
    }
  I2CsendMULTIbyte();
  }

  if (M5.BtnB.wasPressed() && dial == 0 && AM_FM == "FM") {
    REGISTER_ADDRESS = TUNE;
    I2CreadMULTIbyte();

    data = (data - 0x0002) | 0B1000000000000000; // - 100KHz step   = 100/50  76MHz - 94.1MHz
    if ( (data & 0B0000111111111111) <1520) {  //<76MHz
      data = 0B1000011101011010; // 94.1MHz  1882
    }
  I2CsendMULTIbyte();
  }
}

PCBWayでの部品実装依頼について

PCBWayでは基板に実装する部品を手配してくれるサービスがあり、実装部品リスト(BOMリスト)にメーカーの部品名を記載しておけば、基本的には全ての部品を手配してもらえるため自分で部品手配をする手間が省けます。
一方、JLCPCBでは、JLCPCBが標準で準備している部品やdigikeyなどの部品通販業者に在庫がある部品は手配可能ですが、在庫が無い場合の手配を行う方法はWEBサイド上では確認することができませんでした

JLCPCBでのPCBA発注に際して、標準部品を使用している場合は、基板に部品実装した状態をPCの画面上の3DViewerで確認することができ、部品の実装角度の確認やピン配列が間違えていないかの確認が簡単に行えます。逆に言うと、JLCPCBの部品ライブラリに合わせた部品登録をする必要があります。具体的には、部品の原点座標や角度をJLCPCBのライブラリと同一にする必要があります。合わせこみが間違えている場合は、角度が90度ずれたり、ICを180度回転して実装されてしまう可能性があると言えます。

JLCBCBでの部品実装状態の確認用 3Dビューワー画面

一方、PCBWayでの部品実装状態は、PC画面上での確認はできないですが、試作依頼時に提出した部品実装配置データの確認は十分時間をかけて行っているようで、ICの1番ピンの位置や極性部品の極性指示を正しく指定していれば、部品の座標や角度の合わせこみはPCBWay側で対応してくれているようでした。PCBAの注文に必要なファイルと情報はPCBWayのサイトに詳しく記載がありますが、例えば次のように部品極性の指示方法が決められています。

The Necessary Files & Info for Assembly Orders – PCBWay

実際の注文してみると、部品ランド形状と手配した部品サイズが異なっているなど、何かイレギュラーな点を発見した場合は、勝手な判断をして誤った処理を行うことはなく、こちらに確認の連絡が入り、どうすべきかの指示を仰ぐ丁寧な対応に安心感がありました。

例えば、コンデンサのランドサイズを基板上では1005(mm)で設計していましたが、BOMリストに記載の部品を誤って0402(mm)サイズの部品番号を記載していた箇所があり、サイズ違いで実装ができない場合には次のような問い合わせがありました。
この質問に対して処理方法を回答することで次の工程に進めることになります。

PCBwayから送られてきた質問一覧表

上記2社を比較してみましたが、どちらのやり方が良いのかは、設計した回路に使用している部品次第ではないかと思います。JLCPCBのようにあらかじめ準備されている標準部品を多数使用して設計すれば、部品手配時間が短縮でき試作スピードが迅速になるでしょうし、一方でPCBwayのようにあらゆる部品の手配をお任せできる場合は、個人のDIYでは入手ルートが不明な部品も手配してもらえるので、使用する部品選定の制約が緩和されるので回路設計の自由度が上がるように思います。

なお、部品サイズ間違いはどうしてもうっかりミスで起きてしまうので、1005サイズと1608サイズの抵抗とコンデンサについては、一通りの定数がそろっているサンプルブックを手元に用意して利用しています。

筐体を3Dプリントで製作

今回、電子部品が剥き出しでは、壊さないように取り扱いに気を使い普段使いが難しいので、M5stickC-Plus用のHatデバイスに倣った形状の筐体を3Dプリントサービスに製造を依頼してみました。

まずは、自分で設計した寸法で筐体内に収めるPCBAとの干渉が無いかどうか、形状が思い通りにできているかどうかを確認する必要があります。
そのために家庭用の3Dプリンターを使用した試作では、設計寸法を調整して印刷することを数回繰り返し問題が無いことを確認した上で、数個を3Dプリントサービスに発注しました。

家庭用3Dプリンターで確認しているにもかかわらず、まず少量を3Dプリントサービスに依頼する理由は、プリントに使用する材料や印刷機器で仕上がり精度が違ってくるためです。
実際に、3Dプリントサービスで作成したものは、私の所有している3Dプリンターで作成したものよりも精度が良いため、わずかながら小さめの仕上がりになっていました。

完成までの時間は掛かりますが、設計が失敗していた時の金銭的な損失、精神的なダメージを抑えるために、一度に多量の製造を依頼するのではなく、少量の試作を1、2度繰り返し問題点が無いことを確認後に数多くの製造をすることは、結果的にはbetterな方法と思います。
betterという意味は、複数回の試作をして寸法を追い込む場合でも、プリントサービス業者で使用する材料や印刷機器で違いがあるので、毎回、同じ仕上がりになるとは限らないためです。
プリントサービスの製造仕様をよく確認しておき、3Dデータを設計する際に、あまりに精度が必要なデザインにしないほうが無難と思います。

以上は設計プロセスとしては当然の工程ではありますが、時間的な制約から、つい焦って「エイヤッ」で確認もほどほどに進めてしまうと、後々痛い目に合うかもしれません。しばしば言われていることですが、「一晩寝かせる」ことも重要です。

さて、プリントサービスに依頼して出来上がってきたものは、所有している3Dプリンターと比べてと圧倒的に良い感じの仕上がりで満足しました。PCBAの納まりも問題が無く、この仕上がりを見ると、3Dプリンターの新機種が欲しくなりますね。

PCBAを収納してタッピングネジで止めすれば完成です。

アマゾンなどの大手通販業者から丁度良いサイズのタッピングネジが入手できるので、M5STACK用のパーツを作る際にとても便利ですね。

おわりに

KT0913版のFMワイドラジオモジュールもI2C制御で周波数や音量の調整が簡単に行い扱いやすく、各種ICのレジスタを弄って調整して遊ぶのも一興です。受信感度も良好ですので3Dプリントの筐体に収めて取り扱いやすくした上でboothにて頒布させて頂いていますのでご興味のあるかたは、ぜひ、下記のリンクからご来店のほどよろしくお願いします。

コメント

タイトルとURLをコピーしました