なんとなく思いたったので Twitter 使うのをやめてみることにします。ここ最近のこと考えてみると、特に Twitter を使っていて得るものはないのではないか、という気がしてきました。

何か思うことがあれば日記として書こうと思います。

はてブもやめて、日記にリンクと思うところを書いたほうがいいかもしれません。そのリンクは日記に書くほどのことなのかを一旦考えてみてもいい気がします。


この日記は tech タグか photo タグ、またはアマゾンの商品リンクが含まれている場合しか Atom フィードにコンテンツを出さないようになっています。なので、クソのような日記エントリをいくら書いても誰にも迷惑になるようなことはありません。

日記はそもそも情報としての価値は低いですから、フィードリーダーで読むようなものではないという気がします。「ウェブサイトを開いて読む」というコンテキストが共有されていないと、変なことを言いだす人もいるので (メディアによって読み手が受けとるイメージが変わってしまい、フィードリーダーだと押し付けがましくなる)、このようになっています。


こういう変な仕様のブログシステムというのはASPではまずありえませんから、やはり自分でブログシステムを作っておくというは、良いと思います。

IrKit を cron から読んで朝起きたとき部屋が暖かくなるようにしていましたが、暖くなってきたので、一旦止めました。

cron の有効無効を手動でやっててアホらしいので温度センサーと連携したいところです。

定本 トランジスタ回路の設計―増幅回路技術を実験を通してやさしく解析 (定本シリーズ) - 鈴木 雅臣

鈴木 雅臣

4.0 / 5.0

定本トランジスタ回路の設計 続 (定本シリーズ) - 鈴木 雅臣

鈴木 雅臣

4.0 / 5.0

一回図書館で借りて一通り読んだのですが、トランジスタ回路の定番パターンと設計方法が一通りまとまっているのがほかになく、参照したいと思うことが多々あるので、買うことにしました。

これこそ電子書籍でPDFになっていてほしいのですが、Tech Villageにはないようです。改訂してPDFにして欲しいです。

ebay で800円ぐらいで買ったものです。

この手のモジュールにはAD9851(源クロック6倍周波数逓倍器付き)のものとAD9850のものがあり、さらに基板のタイプが2つあります。AD9850/AD9851は制御方法も含めてほとんど互換なので、源クロックとチップだけ違うものが出回っているみたいです。

試してみたのはおそらく古いタイプのもので、ブレッドボードには直接挿すことができないので、ちょっと面倒くさいタイプです。

周波数の設定・パワーダウンモードへの移行・設定位相(2台以上のDDSでsin/cos作る場合に使う)を40bitレジスタにまとめて突っ込む形になっています。

この40bitレジスタはパラレルモードとシリアルモードで設定でき、ググると大抵の例でシリアルモードを使ってるみたいです。

ただ、リセット直後はパラレルモードで起動するので、シリアルモードに変えてやる必要があります。といっても、D0=1 D1=1 D2=0 にした状態で、W_CLK パルスを送り、すぐ FQ_UD パルスを送るとシリアルモードに入るということになっています。

このモジュールの場合、ジャンパでD0=1 D1=1 D2=0 を設定できるようになっており、デフォルトでジャンパしてあるので、W_CLK と FQ_UD パルスをマイコンから送るだけで初期化できます。

コード

ライブラリみたいにしてあるのもありましたが、結局自分で書きました。

入手したモジュールは125MHzが源クロックでした。

#include <Arduino.h>
#include <Wire.h>

template <uint32_t CLKIN>
class AD9850 {
	static constexpr double PHASE_FACTOR = 0x100000000 / (double)CLKIN;

	const uint16_t PIN_DATA;
	const uint16_t PIN_FQ_UD;
	const uint16_t PIN_W_CLK;
	const uint16_t PIN_RESET;

	void serial_write(uint32_t freq, uint8_t phase, bool powerdown) {
		// freq (delta phase)
		for (int i = 0; i < 32; i++) {
			digitalWrite(PIN_DATA, (freq>>i) & 1);
			digitalWrite(PIN_W_CLK, HIGH); delayMicroseconds(4);
			digitalWrite(PIN_W_CLK, LOW); delayMicroseconds(4);
		}

		// control bits
		digitalWrite(PIN_DATA, LOW);
		digitalWrite(PIN_W_CLK, HIGH); delayMicroseconds(4);
		digitalWrite(PIN_W_CLK, LOW); delayMicroseconds(4);
		digitalWrite(PIN_W_CLK, HIGH); delayMicroseconds(4);
		digitalWrite(PIN_W_CLK, LOW); delayMicroseconds(4);

		// powerdown
		digitalWrite(PIN_DATA, powerdown ? HIGH : LOW);
		digitalWrite(PIN_W_CLK, HIGH); delayMicroseconds(4);
		digitalWrite(PIN_W_CLK, LOW); delayMicroseconds(4);

		// phase
		for (int i = 0; i < 5; i++) {
			digitalWrite(PIN_DATA, (phase>>i) & 1);
			digitalWrite(PIN_W_CLK, HIGH); delayMicroseconds(4);
			digitalWrite(PIN_W_CLK, LOW); delayMicroseconds(4);
		}

		digitalWrite(PIN_FQ_UD, HIGH); delayMicroseconds(4);
		digitalWrite(PIN_FQ_UD, LOW); delayMicroseconds(4);
	}

public:
	AD9850(
			uint16_t data,
			uint16_t fq_ud,
			uint16_t w_clk,
			uint16_t reset
		) :
			PIN_DATA(data),
			PIN_FQ_UD(fq_ud),
			PIN_W_CLK(w_clk),
			PIN_RESET(reset)
	{
		pinMode(PIN_DATA, OUTPUT);
		pinMode(PIN_FQ_UD, OUTPUT);
		pinMode(PIN_W_CLK, OUTPUT);
		pinMode(PIN_RESET, OUTPUT);
	}
	
	/**
	 * W0 ... W31  -> Freq (LSB first)
	 * W32, W33    -> Control (for factory test)
	 * W34         -> Power-Down
	 * W35 ... W39 -> Phase (LSB first)
	 */

	void reset() {
		// ensure low
		digitalWrite(PIN_DATA, LOW);
		digitalWrite(PIN_FQ_UD, LOW);
		digitalWrite(PIN_W_CLK, LOW);

		// reset
		digitalWrite(PIN_RESET, HIGH); delay(1);
		digitalWrite(PIN_RESET, LOW); delay(1);

		// reset to serial mode
		// Pins of D0, D1 = HIGH, D2 = LOW for serial mode
		digitalWrite(PIN_W_CLK, HIGH); delayMicroseconds(4);
		digitalWrite(PIN_W_CLK, LOW); delayMicroseconds(4);

		digitalWrite(PIN_FQ_UD, HIGH); delayMicroseconds(4);
		digitalWrite(PIN_FQ_UD, LOW); delayMicroseconds(4);
	}

	void set_frequency(uint32_t frequency) {
		set_frequency(frequency, 0);
	}

	void set_frequency(uint32_t frequency, uint8_t phase) {
		uint32_t deltaPhase = PHASE_FACTOR * frequency;
		serial_write(deltaPhase, phase, 0);
	}

	void powerdown() {
		serial_write(0, 0, 1);
	}
};

AD9850<125000000> ad9850(9, 10, 11, 12);

void setup() {
	Serial.begin(9600);

	ad9850.reset();
	ad9850.set_frequency(10e6);
}

void loop() {
}

出力

商品説明だと40MHz まで出せると書いてありました。たしかに出せることは出せるようです。

このモジュールの出力には7次のローパスフィルタがついています。これはAD9851のデータシート通りの定数のフィルタのようで、70MHzぐらいにカットオフ周波数ががあるみたいです。

なおローパスフィルタの後の出力インピーダンスは200Ωになっているみたいです。

AD9850自体の出力は電流出力で、12ピンについている抵抗でフルスケールの出力電流が決まることになっています。このモジュールでは3.9kΩが実装されており、10mA フルスケールの出力に設定されています。200Ωで10mW(10dBm)。実際使う場合バッファして50Ω出力とする必要はありそうです。

片方だけにローパスフィルタが入っており、もう片方の出力は200Ωで電圧変換されて直接ピンヘッダに出ています。ということで差動出力には使えません。





  1. トップ
  2. tech
  3. AD9850 DDS モジュール

AD9850 DDS モジュール に続き AD9851 で、別基板バージョンのものです。

ジャンパとかが一切ない簡略版?なのか進化版?でしょうか。

D0, D1 は 10kΩでプルアップされているので、これらは自分でプルアップする必要はありません。D2 を GND に接続するだけでシリアルモードに入れる実装になっています。

コード

#include <Arduino.h>
#include <Wire.h>

template <uint32_t CLKIN, bool MULTIPLIER>
class AD9851 {
	static constexpr double PHASE_FACTOR = 0x100000000 / (double)(CLKIN * (MULTIPLIER ? 6 : 1));

	const uint16_t PIN_DATA;
	const uint16_t PIN_FQ_UD;
	const uint16_t PIN_W_CLK;
	const uint16_t PIN_RESET;

	void serial_write(uint32_t freq, uint8_t phase, bool powerdown) {
		// freq (delta phase)
		for (int i = 0; i < 32; i++) {
			digitalWrite(PIN_DATA, (freq>>i) & 1);
			digitalWrite(PIN_W_CLK, HIGH); delayMicroseconds(4);
			digitalWrite(PIN_W_CLK, LOW); delayMicroseconds(4);
		}

		// control bits
		digitalWrite(PIN_DATA, MULTIPLIER ? HIGH : LOW);
		digitalWrite(PIN_W_CLK, HIGH); delayMicroseconds(4);
		digitalWrite(PIN_W_CLK, LOW); delayMicroseconds(4);
		digitalWrite(PIN_DATA, LOW);
		digitalWrite(PIN_W_CLK, HIGH); delayMicroseconds(4);
		digitalWrite(PIN_W_CLK, LOW); delayMicroseconds(4);

		// powerdown
		digitalWrite(PIN_DATA, powerdown ? HIGH : LOW);
		digitalWrite(PIN_W_CLK, HIGH); delayMicroseconds(4);
		digitalWrite(PIN_W_CLK, LOW); delayMicroseconds(4);

		// phase
		for (int i = 0; i < 5; i++) {
			digitalWrite(PIN_DATA, (phase>>i) & 1);
			digitalWrite(PIN_W_CLK, HIGH); delayMicroseconds(4);
			digitalWrite(PIN_W_CLK, LOW); delayMicroseconds(4);
		}

		digitalWrite(PIN_FQ_UD, HIGH); delayMicroseconds(4);
		digitalWrite(PIN_FQ_UD, LOW); delayMicroseconds(4);
	}

public:
	AD9851(
			uint16_t data,
			uint16_t fq_ud,
			uint16_t w_clk,
			uint16_t reset
		) :
			PIN_DATA(data),
			PIN_FQ_UD(fq_ud),
			PIN_W_CLK(w_clk),
			PIN_RESET(reset)
	{
		pinMode(PIN_DATA, OUTPUT);
		pinMode(PIN_FQ_UD, OUTPUT);
		pinMode(PIN_W_CLK, OUTPUT);
		pinMode(PIN_RESET, OUTPUT);
	}
	
	/**
	 * W0 ... W31  -> Freq (LSB first)
	 * W32, W33    -> Control (for factory test)
	 * W34         -> Power-Down
	 * W35 ... W39 -> Phase (LSB first)
	 */

	void reset() {
		// ensure low
		digitalWrite(PIN_DATA, LOW);
		digitalWrite(PIN_FQ_UD, LOW);
		digitalWrite(PIN_W_CLK, LOW);

		// reset
		digitalWrite(PIN_RESET, HIGH); delay(1);
		digitalWrite(PIN_RESET, LOW); delay(1);

		// reset to serial mode
		// Pins of D0, D1 = HIGH, D2 = LOW for serial mode
		digitalWrite(PIN_W_CLK, HIGH); delayMicroseconds(4);
		digitalWrite(PIN_W_CLK, LOW); delayMicroseconds(4);

		digitalWrite(PIN_FQ_UD, HIGH); delayMicroseconds(4);
		digitalWrite(PIN_FQ_UD, LOW); delayMicroseconds(4);
	}

	void set_frequency(uint32_t frequency) {
		set_frequency(frequency, 0);
	}

	void set_frequency(uint32_t frequency, uint8_t phase) {
		uint32_t deltaPhase = PHASE_FACTOR * frequency;
		serial_write(deltaPhase, phase, 0);
	}

	void powerdown() {
		serial_write(0, 0, 1);
	}
};

AD9851<30000000, true> ad9851(9, 10, 11, 12);

void setup() {
	Serial.begin(9600);

	ad9851.reset();
	ad9851.set_frequency(10e6);
}

void loop() {
}

AD9851 とほぼ同じですが、6倍の周波数逓倍器がの有効無効化のビットがあるので、その部分だけ実装を変えてあります。

出力

購入したモジュールは 30MHz の源発振のものなので、逓倍後は 180MHz になります。

40MHz ぐらいから出力が低くなります。

ZOUT2 (フィルタあり)



逓倍している分スプリアスが多いようです。


ZOUT1 (フィルタなし)


  1. トップ
  2. tech
  3. AD9851 DDS モジュール

ebay で見つけて買ったやつシリーズです。表題のようなものを買ったので動かしました。

https://github.com/adafruit/Adafruit_LED_Backpack が割とよくできていて、これ使えばすぐに動かせました。

#include <Arduino.h>
#include <Wire.h>
// https://github.com/adafruit/Adafruit_LED_Backpack
#include "Adafruit_LEDBackpack.h"

// Adafruit_LEDBackpack matrix = Adafruit_LEDBackpack();
Adafruit_8x8matrix matrix = Adafruit_8x8matrix();


void setup() {
	Serial.begin(9600);
	Serial.println("begin");
	matrix.begin(0x70);
	matrix.setBrightness(10);
	matrix.setTextSize(1);
	matrix.setTextWrap(false);
	matrix.setRotation(1);
}


void loop() {
	char* message = const_cast<char*>("Hello, World!\n");

	int16_t x, y;
	uint16_t w, h;
	// getTextBounds は \n で終わってないと width を正しく計算しない。
	matrix.getTextBounds(message, 0, 0, &x, &y, &w, &h);
	Serial.println("getTextBounds: ");
	Serial.print("  x = "); Serial.println(x);
	Serial.print("  y = "); Serial.println(y);
	Serial.print("  w = "); Serial.println(w);
	Serial.print("  h = "); Serial.println(h);
	Serial.println("");

	for (int16_t x = 8; x >= -(int16_t)w; x--) {
		matrix.clear();
		matrix.setCursor(x, 0);
		matrix.print(message);
		matrix.writeDisplay();
		delay(50);
	}
}
  1. トップ
  2. tech
  3. I2C 8*8 LED dot Matrix module HT16K33

BMP180 搭載のモジュールを ebay で買ってみたので試しました。約$2。どの気圧計にしろ温度計が必要で内部補正には使われていたりしますがだいたい内部用で外から値がとれません。このモジュールは温度もI2C経由で測れて一石二鳥モジュールです。

こんな感じのモジュールで、BMP180 以外に実装があります。これは 3.3V レギュレータで、5V 供給しても大丈夫なようになっています。(I2C のロジックレベル変換は簡易的ですが)。なので 5V の Arduino でも使えます。

BMP085 というものと互換性があるみたいで (BMP085はディスコン) それ用のライブラリがそのまま使えます。

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_BMP085.h>

// https://github.com/adafruit/Adafruit-BMP085-Library
Adafruit_BMP085 bmp;

void setup() {
	Serial.begin(9600);
	int ok = bmp.begin();
	if (!ok) {
		Serial.println("bmp.begin() failed.");
		for (;;);
	}
}

void loop() {
	Serial.print("Temperature = ");
	Serial.print(bmp.readTemperature());
	Serial.println(" *C");

	Serial.print("Pressure = ");
	Serial.print(bmp.readPressure() / 100.0);
	Serial.println(" hPa");

	Serial.println();
	delay(1000);
}
Temperature = 19.40 *C
Pressure = 1011.48 Pa

Temperature = 19.40 *C
Pressure = 1011.47 Pa
  1. トップ
  2. tech
  3. BMP180 I2C 気圧・温度計センサー

ebay で買ったシリーズです。$3 ぐらいで買いました。以下らへんのモジュールとよく似たものです。

MQ-135 のほかすこし実装 (LM393など) があります。LM393 はコンパレータで、基板上の半固定で設定した閾値でデジタル出力できるようになっています。アナログ出力は MQ-135 直結です。

MQ-135

空気の汚染度に感度があるセンサーで、ベンジン・アルコール・煙などに反応しやすいと書いてあります。CO2 にもある程度反応するみたいで、ググると poor man's CO2 センサーとしても一定の人気があるみたいです。

自分としては、換気扇ごしに外からタバコの煙が自宅に入ってくることがあってほとほと不愉快なので、どんな時間帯にそういう状況になるかをつきとめたいというモチベーションがありますが、そういう用途に使えるかはわかりません。というかテストのためにタバコを買うのも絶対に嫌なのでテスト不可能です。

仕様

MQ-135 自体がデータシートでプリヒートを24時間以上求めています。常時起動してないとダメですね。とはいえ1時間ぐらいで値はほぼ安定しているようには見えます。

MQ-135のデータシート的には10kΩ〜47kΩの負荷抵抗を想定しているみたいですが、このモジュールは 102 (1kΩ) の抵抗が負荷抵抗になっているように見えますので、普通に使うより感度が低くなりそうです。

ググると頑張って ppm 単位の絶対値出力を出そうという試みがあるのですが、素人がキャリブレーションしようがない (他の正確な絶対値センサーが必要) ので、基本的には相対的な値を範囲を一定期間モニタリングしたうえで値を把握して使うほかない気がします。

実測出力

  • 200mV / 数時間通電後の無人の部屋
  • 700mV / アルコールティッシュを近付けたとき
  • 800mV / サカムケアを近付けたとき (イソプロパノールなど)
  • 1660mV / プラモデル用接着剤を近付けたとき (アセトンなど)

意外と反応が良くて、臭いがあるとすぐ変化が出力されます。

  1. トップ
  2. tech
  3. MQ-135 Sensor Air Quality Sensor Hazardous Gas Detection LM393 MQ135 gas sensor