いろいろ回路の説明を見たりしていたのは自分で作ってみたかったからです。既製品を1つ持っていますが、海外製なので壊れたときに不安です。自分で作れれば既製品に頼る必要がひとまずなくなるので安心できますし、自分にニーズに応じてニッチな機能を足しやすくなります。

設計というほどではないですが、手軽に作れそうでベストな回路で作ってみることにしました。

構成

  • 発振器は AD9851 (DDS) のモジュール
  • 検波は AD8307 (ログアンプ)
  • 検出器はブリッジ
  • 計測ポイントは3つ
    • リファレンス電圧 (入力電圧の半分)
    • 負荷電圧 (アンテナの入力電圧)
    • 平衡電圧 (リファレンス電圧と負荷電圧の差

測れる原理は[tech] アンテナアナライザの回路 - ブリッジの三つの電位差を測るタイプ | Thu, Mar 3. 2016 - 氾濫原で確認しました。

実際の回路

センサー部

AD8307 は1つにします。というのも、AD8307 を複数使う場合、それぞれの特性があってないことを考慮する必要があって面倒だからです。キャリブレーションレスにしたいと思うと1つにするというのは必要要件です。

測定ポイントの切り替えはRFスイッチでやることにします。これには MASWSS0115 (digikey: 1465-1373-1-ND) を使いました。選定理由は

  • MASWSS0115 は単価約70円と安い
  • 電源電圧 2.3V〜5V
  • DC〜3GHz
  • アイソレーションが22dB @ 3GHz (データーシートのグラフを見ると、HF帯ではもっとある)

アイソレーションが低めなのが気になりましたがHF帯でしか使わないので大丈夫そうです。電源はスイッチ入力と共用です。

これを2つ使って、AD8307 の入力前に信号を切り替えます。

DDS+バッファ

このバッファ回路は再考の必要があります。やりたいことはただのバッファです。

MCU

MCU は LPC1114 を使っています。しかしやってることは

  • DDSの制御
  • ADC
  • シリアル通信

だけです。計算処理を入れるとROMにおさまらないので、コンピュータ側で計算します。この構成だとAVRのほうがリファレンス電圧を任意に設定できるのでアドバンテージがあります。Arduino ベースで作りなおしてもいいぐらいです。

書きこみが楽にできるようにピンヘッダを立ててあります。

使えるか

こんな感じで測定できています。なぜか波うっています。ADCの±1エラーもありそうですが、インピーダンスミスマッチでどこかで反射が起きているのかもしれません。外来ノイズ (アンテナなので普通に受信状態にある) も関係ありそうな気がします。よくわかりません。

VSWR の測定 (測定値2つの単純な割り算) では波うってないので、そんなに気にすることもないかもしれません。

既製品で測定した結果のリアクタンスを絶対値になおしたグラフが以下です。そこそこ一致しているように見えます。波うっているのさえなんとかなれば普通に信用にたる測定器がつくれそうです。

いくつかあった問題

RF バッファ

ほぼ唯一の半導体が絡むアナログ回路であるRFバッファ回路でかなり苦労しました。実際組んでみるとまともに出力がでず、かなり苦労しました。上記回路図中のバッファ回路は一応動いたレベルの定数です。どうしても正しくシミュレーションないし設計通りの計算ができず、試行錯誤しました。

  • トランジスタが思いがけず1つ壊れていた
    • ここに気付くまでだいぶ…
  • AD9851 モジュールのフィルタを通ってない出力を使っていた
    • 電流出力なので出力インタピーダンスがシミュレーションと全くあわなかった
    • フィルタ済みの出力にリワーク
  • 定数がおかしかった
    • よくわからないが試行錯誤してしまった

こんな回路もまともに組めないのか、という感じでつらい感じでした。

あとトランジスタの選定を失敗しました。無駄にfTが高いものを使っているのと、Ic が低く、SSM というパッケージだと他のものと互換性が低いので換えにくいです。

RFスイッチ

試していると、片方のRFスイッチで、信号がGND側に通りぬけて、出力レベルが下がっていることがわかりました。

RFスイッチの故障を疑ったりもしましたが、結局電源の接続がちゃんとできていなかったことが原因でした。かなり小さいICなのでなかなか気付きませんでした。

オープンドレインを出力につかっていた

基板のアートワークを作るときに、ピン配置を配線しやすいように変えたのですが、これがもとでうっかりオープンドレインピンを使うようにしてしまいました。当然動かずしばらく悩みました。リワークで対応しました。

まとめ

まだアプリケーションとしては途中ですが、ひとまず動きそうだぞというところまではできました。

測定結果が波うっているのは未解決です。また、RFバッファも微妙なのでもうちょっとマシにするか、素直にバッファICにしてしまいたいです。

  1. トップ
  2. tech
  3. 簡単かつ安く高精度なアンテナアナライザーを自作したい

例によって ebay で300円ぐらいものです。絶対的な解像度はともかく TFT カラー液晶がこの値段だと 16x2 の LCD とかが情報量的にはゴミに思えてきます。(ライブラリサイズとかの兼ね合いもあってそのまま置き換えられるわけではないですが)

購入したものは ST7735 を使っているので、Adafruit-ST7735-Library でだいたい動く雰囲気なのです。以下の2つのライブラリを使える状態にしておく必要があります。

しかし、このモジュールが想定している仕様とは異なる仕様のモジュールのようで、微調整が必要でした。ただし Adafruit-ST7735-Library はクラス構造を private にしているので、残念ながらライブラリ側を無修正のままではどうしようもありませんでした。

最低限の修正に留めるようと、以下のように private を protected にしました。

diff --git a/Adafruit_ST7735.h b/Adafruit_ST7735.h
index 0598720..3b1aa97 100755
--- a/Adafruit_ST7735.h
+++ b/Adafruit_ST7735.h
@@ -152,7 +152,7 @@ class Adafruit_ST7735 : public Adafruit_GFX {
   void     dummyclock(void);
   */
 
- private:
+ protected:
   uint8_t  tabcolor;
 
   void     spiwrite(uint8_t),

実際に使う前に public 継承して初期化処理を奪っています。実際のコードでは冒頭の gif のように millis() を連続して表示させています。

#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h> /* must modify private to protected */
#include <SPI.h>

class TFTST7735 : public Adafruit_ST7735 {
public:
	TFTST7735(int8_t CS, int8_t RS, int8_t RST = -1) :
		Adafruit_ST7735(CS, RS, RST)
	{
	}

	void init() {
		initR(-1);

		_height = 128;
		rowstart = 32;
		colstart = 0;
	}
};

#define TFT_CS     10
#define TFT_RST    9
#define TFT_A0     8

TFTST7735 tft = TFTST7735(TFT_CS, TFT_A0, TFT_RST);

void setup (void) {
	tft.init();
	tft.fillScreen(ST7735_BLACK);
}

void loop() {
	tft.setTextWrap(false);

	// font size is 5x7
	tft.setCursor(5, 5);
	tft.setTextColor(ST7735_RED);
	tft.setTextSize(1);
	tft.fillRect(5, 5, 128, 7, ST7735_BLACK);
	tft.println(millis());

	tft.setCursor(5, 20);
	tft.setTextColor(ST7735_WHITE);
	tft.setTextSize(2);
	tft.fillRect(5, 20, 128, 7*2, ST7735_BLACK);
	tft.println(millis());
	delay(24);
}

所感

128x128 は MCU で扱う解像度としてはかなり広く感じます。文字を dot by dot で表示させると 16x2 などの LCD モジュールと比べてかなり情報量が増えます。カラーなのでさらに情報量が増えます。

描画速度も 16x2 の LCD などよりは早いです。ただしバッファが1つなのであまり書き換えが早いとチラついてしまいます。

デメリットとしては制御コードが複雑になる (フットプリントが増える) ことだと思います。上記のコードはかなりシンプルに見えますがプログラムで23.3%、データで7.7%使っています。ライブラリ側の工夫でもうすこし縮められそうな気はしますが、テキスト表示するならフォントは持たざるを得ないので、16x2 と比べると確実にコード量が増えます。

  1. トップ
  2. tech
  3. ST7735 128x128 TFT カラー液晶モジュール

だいたい300円ぐらいのものです。MAX7219 というLEDドライバを使っていて、SPI で表示を変えられます。(正確には 7219 は SPI 対応とは書いてないのですが、CS の扱いが違うだけ? なのか、ほぼSPIと同じインターフェイスです。よくわかりません)

SPI を使ったことがなかったので一応自分で書いてみました。BCD のデコーダが入っており、数字ぐらいの表示なら自分でLEDとのマッピングを持つ必要はありません。

モードがいくつかあって

  • 全桁自分で制御 (7セグメント+ドットに対応するビットを自分で入れる)
  • 最下位桁だけBCDで残りは自分で制御
  • 上位4桁は自分で制御して、下位4桁はBCD
  • 全桁BCD

と選べます。7セグでアルファベット表示したい、みたいな場合は全桁自分でやる必要があります (マッピングすなわちフォント情報を持つ)

コード

書いたコードでは、全桁BCDでやるようにしてみました。マッピング持つのが面倒なのと、実質自分が使おうと思う用途だと数字以外表示しなそうだな、という気がしています (7セグでアルファベット表示させるのは視認性が悪いので好きじゃない)

SPI 通信はビット数のキリがいいのでハードウェアを使ってやっています。MAX7219 は 10MHz の SPI らしいのでそのようにしてみました。チップセレクトだけコンストラクタに渡すようにしています。

#include <Arduino.h>
#include <SPI.h>

#include <string.h>
class MAX7219 {
	uint8_t CS;

public:
	enum class ADDRESS : uint8_t {
		NOOP = 0b0000,
		DIGIT0 = 1,
		DIGIT1 = 2,
		DIGIT2 = 3,
		DIGIT3 = 4,
		DIGIT4 = 5,
		DIGIT5 = 6,
		DIGIT6 = 7,
		DIGIT7 = 8,
		DECODE_MODE = 0b1001,
		INTENSITY = 0b1010,
		SCAN_LIMIT = 0b1011,
		SHUTDOWN = 0b1100,
		DISPLAY_TEST = 0b1111,
	};

	enum class SHUTDOWN_MODE : uint8_t {
		SHUTDOWN = 0b0,
		NORMAL = 0b1,
	};

	enum class DECODE_MODE : uint8_t {
		NO_DECODE_FOR_DIGITS_7_0 = 0b00000000,
		CODE_B_DECODE_FOR_DIGIT_0_NO_DECODE_FOR_DIGITS_7_1 = 0b00000001,
		CODE_B_DECODE_FOR_DIGIT_3_0_NO_DECODE_FOR_DIGITS_7_4 = 0b00001111,
		CODE_B_DECODE_FOR_DIGIT_7_0 = 0b11111111,
	};

	MAX7219(uint8_t _cs) :
		CS(_cs)
	{
		pinMode(CS, OUTPUT);
	}

	void write(ADDRESS address, uint8_t data) {
		SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));
		digitalWrite(CS, LOW);
		SPI.transfer(static_cast<uint8_t>(address));
		SPI.transfer(data);
		digitalWrite(CS, HIGH);
		SPI.endTransaction();
	}
	void begin() {
		SPI.begin();

		setDecodeMode(DECODE_MODE::CODE_B_DECODE_FOR_DIGIT_7_0);
		setScanLimit(7);
		setShutdown(false);

		setIntensity(8);

		for (int i = 0; i < 8; i++) {
			write(static_cast<ADDRESS>(i + 1), 1);
		}
	}

	void setDecodeMode(DECODE_MODE mode) {
		write(ADDRESS::DECODE_MODE, static_cast<uint8_t>(mode));
	}

	void setScanLimit(uint8_t limit) {
		write(ADDRESS::SCAN_LIMIT, limit);
	}

	void setIntensity(uint8_t intensity) {
		write(ADDRESS::INTENSITY, intensity);
	}

	void setShutdown(bool shutdown) {
		write(ADDRESS::SHUTDOWN, static_cast<uint8_t>(shutdown ? SHUTDOWN_MODE::SHUTDOWN : SHUTDOWN_MODE::NORMAL));
	}

	void print(const char* buf) {
		size_t len = strlen(buf);
		uint8_t digit = 0, dp = 0;
		for (size_t i = 0; i < len && digit < 8; i++) {
			const char c = buf[len - i - 1];
			if (c == '.') {
				dp = 1<<7;
				continue;
			} 

			uint8_t b;
			if (c == '-') {
				b = 0b1010;
			} else
			if (c == ' ') {
				b = 0b1111;
			} else
			if ('0' <= c && c <= '9') {
				b = static_cast<uint8_t>(c - '0');
			} else {
				continue;
			}

			write(static_cast<ADDRESS>(digit + 1), b | dp);
			dp = 0;
			digit++;
		}

		for (; digit < 8; digit++) {
			write(static_cast<ADDRESS>(digit + 1), 0b1111);
		}
	}
};


MAX7219 leds(10);

void setup() {
	leds.begin();
	leds.print("-1234.567");
	delay(3000);
}

void loop() {
	leds.print(String((float)micros() / 1000).c_str());
	delay(29);
}
  1. トップ
  2. tech
  3. MAX7219 8桁 7セグメント LED モジュール / Arduino