DHT11 という比較的価格の安い(ただし精度はいまいちな)デジタル温湿度センサーがあるので読んでみた。1-wire ライクな (1-wireではない) プロトコルで、データシートを読みながら頑張って読む感じ。

  1. 18ms 以上バスをローにすることでデータ送信を行わせる
  2. 18ms のローを送信したあと、入力状態に変えて 50us ロー + 50us ハイを待つ (初期メッセージ)
  3. そのあと 40回 50us ロー + (28us or 70us ハイ) のセットが送られてくる。70us のほうが「1」のビット

だいたいすぐできるんだけど、waiting for slope up と書いてあるところのディレイを入れないと、バスがハイになる前に loop_until_bit_is_clear してしまって状態がズレてしまうので、立ちあがる時間を適当に待つ必要があった。

湿度・温度それぞれ16bitが送られてくるんだけど、下位8bitは、このモジュールの場合、どちらも全部0しか返ってこない。のでコードでは無視している。下位 8bit はAM2302とか精度が良いがコストが高いというモジュールがあって、そちらの場合使われるっぽい。

あと、送られてくるデータは前回測定時のデータなので、2回読み出しを行って2回目を採用しないと、正しいデータにならない。連続して読むなら関係ない。

int main(void) {
	logger_init(9600);
	setup_io();

	printf("initializing...\r\n");
	sei();
	printf("initialized\r\n");

	uint8_t i, j, us;
	int16_t humidity, temperature;
	uint8_t res[5];
	set_input(DDRD, PD7); clear_bit(PORTD, PD7);  // hi-z

	for (;;) {
		set_output(DDRD, PD7); clear_bit(PORTD, PD7);
		_delay_ms(20);
		set_input(DDRD, PD7); // hi-z

		// waiting for slope up
		_delay_us(5);

		// waiting for slave initial response
		loop_until_bit_is_clear(PIND, PD7);
		loop_until_bit_is_set(PIND, PD7);
		loop_until_bit_is_clear(PIND, PD7);

		for (i = 0; i < 5; i++) {
			for (j = 0; j < 8; j++) {
				us = 0;
				// 50us
				loop_until_bit_is_set(PIND, PD7);
				while (bit_is_set(PIND, PD7)) {
					us++;
					_delay_us(1);
				}

				res[i] <<= 1;
				if (us > 50) { // 0: <28us 1: <70us
					res[i] |= 1;
				}
			}
		}

		printf("%02x %02x %02x %02x %02x\r\n", res[0], res[1], res[2], res[3], res[4]);
		if (( (res[0] + res[1] + res[2] + res[3]) & 0xff) == res[4]) {
			humidity    = res[0];
			temperature = res[2];
			printf("%d%%RH %dC\r\n", humidity, temperature);
		} else {
			printf("invalid data\r\n");
		}

		_delay_ms(5000);
	}
}

信号全体

最初の長いロー部分が、AVR 側からのローで 18ms ある。その後、ちょっとハイになってるところは、センサー待ちの部分、そして 50us ロー・50us ハイに続いてデータが流れてくる。目で見てビット読める感じでたのしい。

立ちあがり時間

これは 18ms 待ちあとで AVR 側を入力に切り替えてハイインピーダンス状態にした直後の立ちあがり。立ちあがりにちょっとかかっていて、その後11usぐらい待ち時間がある。

参考

DHT22 というプロトコル互換デバイスについてのものがあったのでコードほぼそのままです。

DHT22を使った温度計・湿度計の製作 - henteko.org

  1. トップ
  2. tech
  3. DHT11 という温湿度センサーを読む

概要

連続信号をADCでサンプリングする場合、前段にローパスフィルタが必要だが、それをどう設計したらいいかわからない、という話。

ローパスフィルタの設計

ADC する場合、サンプリング周波数の2分の1 (ナイキスト周波数) 以上の周波数はローパスで完全に遮断する必要がある。さもなければ折り返し雑音が入り、これは後段では正常なデータと区別がつかないので対処しようがない。

ある周波数で「遮断」するというのは、その周波数において、分解能ないしノイズレベル未満まで減衰するということ。最大 1V の信号を 10bit ADC (1024段階)する場合、0.97mV 未満であれば分解能未満になる。この場合 20 * Math.log10(1024) = 60.2dB 減衰すれば、遮断したといえる。

そしてこの場合、サンプリング周波数が 10kHz であれば、5kHz 時点で 60.2dB 減衰させるようにフィルタを設計する。次に、どの程度まで完全に通過させるかでフィルタの性能 (次数) が決定する。当然通過帯域がナイキスト周波数に近ければ近いほどADCの性能を生かせるが、そのためには性能の良いフィルタが必要になる。

フィルタの特性にはいくつか種類がある。(同じぐらいの回路規模での比較)

  • バターワース特性
    • 通過帯域内はフラットだが、減衰傾度がゆるい
  • チェビシェフ特性
    • 減衰傾度はきついが、通過帯域内にリプルが発生する

ローパスフィルタの次数はそのまま減衰傾度になる。1次のバターワースフィルタは周波数が倍で出力が半減 (=-6dB/octave)。次数が高いほど減衰が強くなる (2次=-12dB/octave, 3次=-18dB/octave)。高い性能のフィルタが必要な場合は高次のフィルタ (部品点数の増加) が必要になる。

ADCの前段に入れる場合、できる限り急激な減衰が欲しくなるので、まずチェビシェフを検討することになる。そしてチェビシェフ特性の場合、リプルがどの程度発生するかというのも設計で考慮する必要がある。もしリプルが分解能ないしノイズレベル未満にできれば、それは問題にはならない。

10bitの場合リプルは 20 * Math.log10(1 / (1 - 1/1024.0)) = 0.0085dB 未満であれば完全に問題にならないが、リプルの大きさと減衰傾度はトレードオフの関係にある。

回路構成などによっては妥協する必要がある。妥協点は

  • 分解能
  • 通過帯域

そして、フィルタの実現方法として

  • パッシブ型
    • CR, LCR のみ
  • アクティブ型
    • オペアンプを使う

がある。パッシブ型は減衰のみしかできない。アクティブ型は同時に増幅もでき、特性を改善できる、という特徴があるらしい。あとどうも L を含む回路は好まれないらしく (誤差が多いから?) あまり使われないみたいだ。

わからないこと

  • 応答特性
  • 位相変化特性

フィルタ特性としてこれらもでてくるけどどのように影響していくるかわかってない。

また、ADC の場合で、特に例えば後段で FFT する場合、減衰傾度をゆるやかにしてデジタルでフィルタ特性にあわせたレベル補正をかけたり、という小手先テクニックも使えそうだけど、それがうまくいくかわかっていない。

  1. トップ
  2. tech
  3. ADC とローパスフィルタについてのメモ

これを試しにブレッドボードでやってみた。

  • 上側の「T型」がローパス、下側の「T型」がハイパスフィルタとして働き、ツインT型部分でバンドリジェクトフィルタを構成する?
  • 上側は位相が遅れ、下側は位相が進む
  • トランジスタのコレクタ側から負帰還させて発振させてる

定数決定がむずかしそう。いまいちよくわからない。

Wikipedia によると

R1 = R2, C2 = C3, R3 = R1 / x, C1 = C2 * x で x 倍以上増幅すれば発振器になるとのことらしい。このとき、周波数は以下の式

手元のやつでやって実際計ってみた。電源電圧は 3V ぐらい。電圧をあげると周波数もあがるっぽい…

R1=10k, C2=0.012u

C1 は 0.1u

R = 10e3; C = 0.012e-6; 1 / (2 * Math.PI * R * C )
//=> 1326.2911924324612

実際は 1650Hz ぐらいで発振してる。このぐらい誤差なのかがよくわからない。

R1=22k, C2=0.012u

C1 は 0.1u

R = 22e3; C = 0.012e-6; 1 / (2 * Math.PI * R * C )
//=> 602.859632923846

しかし発振せず。

R1=10k, C2=0.027u

R = 10e3; C = 0.027e-6; 1 / (2 * Math.PI * R * C )
//=> 589.4627521922049

786Hz

R1=10k, C2=0.1u

C1 も C2 も 0.1u

R = 10e3; C = 0.1e-6; 1 / (2 * Math.PI * R * C )
159.15494309189535

244Hz

この状態で C1 に 0.1u 並列でつないで 0.2u にすると、210Hz 付近に発振周波数がさがる。

  1. トップ
  2. tech
  3. ツインT型発振回路
  1. トップ
  2. ham
  3. ツインT型発振回路
  1. トップ
  2. 発振回路
  3. ツインT型発振回路