AVRのピン1本で2ビットD/A

D/A機能の無いマイコンでD/Aをするためには,複数のピンを使ってラダー抵抗などに繋ぐと思いますが,ピン一本で1ビットというのは,非効率な気がしてきました.ピン1本には,1ビットしか情報が割り当てられていないのだから当然だろ,と言われそうですが,果たしてそうでしょうか?

とうわけで,1本の足で2ビット,つまり4段階の電圧を出力することに挑戦.

まずは結果から

PD-0をオシロスコープで計測した結果.

images/avr_da.jpg

main関数はこんな感じです.

int main()
{
	int i;
	for(;;) {
		i++;

		fake_da(0);
		wait(5);

		fake_da(1);
		wait(5);

		fake_da(2);
		wait(5);

		fake_da(3);
		wait(5);

		fake_da(2);
		wait(5);

		fake_da(1);
		wait(5);
	}
	for(;;);
}

wait()は,ただの時間待ちの関数です.

fake_da()が今回のキモなわけですが,何をやっているのか想像できるでしょうか?

仕組み

データシートを見てて,妙なことを思いつきました.内部プルアップ抵抗を使って2ビットD/Aできないか,ということです.ちょうどいい抵抗を外付けすれば,上手く行くかもしれない.

とりあえず,チャレンジ.最初,ATMEGA88でやろうかと思って,プルアップ抵抗の抵抗値を測って真面目に計算したのですが,その辺に余ってた2313があったので,そっちでやりました.抵抗値の調整に少してこずりましたが,一つのピンで2ビットD/Aは出来ました.

AT90S2313の一つのピンに抵抗が繋がっているだけなのに,オシロスコープ上には,階段状の波形がでてます.AVRでのD/Aのビット数を倍にできるというのは画期的(だと思います).

回路

2313のPD-0を,100Kの抵抗でVccにつなぎ,47Kの抵抗でGNDに繋いだだけです.一応,オシロスコープとPD-0の間にも10Kくらいの抵抗を入れてみたところ,さらに安定した波形になりました.

これだけだと,何かつなぐと波形が変わってしまいますが,オペアンプなどを通せば普通に電流が取り出せるでしょう.プルアップ抵抗の値がどれだけばらついているかも分からないので,半固定抵抗などで調節できるようにするべきですね.

手元にあるMEGA88の内部プルアップの抵抗の実測値は,41.66Kくらいでした.この場合は,出力を125Kで電源に繋いで,62.5KでGNDに落とせば良いかと思います.

ソース

// fake A/D for 2313
#include<avr/io.h>

void wait(unsigned int time){
	register unsigned char lpcnt;
	__asm__ __volatile__("\n"
		"CPU_wait_entry:\n\t"
		"ldi %0,200\n"
		"CPU_wait_lp:\n\t"
		"nop\n\t"
		"dec %0\n\t"
		"brne CPU_wait_lp\n\t"
		"sbiw %1,1\n\t"
		"brne CPU_wait_entry\n\t"
		:"=&a"(lpcnt)
		:"w"(time)
	);
	return;
}

void fake_da(int v)
{
	switch(v) {
	case 0:
		DDRD=0x01;
		PORTD = 0x00;
		break;
	case 1:
		DDRD=0x00;
		PORTD = 0x00;
		break;
	case 2:
		DDRD=0x00;
		PORTD = 0x01;
		break;
	case 3:
		DDRD=0x01;
		PORTD = 0x01;
		break;
	}
}

int main()
{
	int i;
	for(;;) {
		i++;

		fake_da(0);
		wait(5);

		fake_da(1);
		wait(5);

		fake_da(2);
		wait(5);

		fake_da(3);
		wait(5);

		fake_da(2);
		wait(5);

		fake_da(1);
		wait(5);
	}
	for(;;);
}

最後に

素直にピンを2本使った方が良いでしょう.

期待して読んだのに,がっかりしたという苦情は受け付けません(笑).

この文書の履歴

戻る

Copyright © 瓶詰堂 all rights reserved.