PIC 12F1822とI2CROMでWAVファイルを再生してみた。

I2CのシリアルROMを読み書きできるようになった(前回の記事参照)ので、ROMに音声ファイルのデータを書き込んで、PIC 12F1822 で再生できるかやってみた。
f:id:manpukukoji:20171123092800j:plain
マイコンで音声データを再生する方法として、PWMでやる方法と、D/Aコンバータでやる方法があると思うのだけど、12F1822は、やろうと思えば、どっちでもできる。 で、やってみたけど、今回は、PWMに軍配があがった。
理由は、12F1822 のDACの出力レベルが32しかないことと、出力にバッファをつけてやらないとだめなこと、で、出力も非常に小さいので、蚊の鳴くような音しか聞こえませんでした。もちろん、ちゃんと作りこめばそれなりのものは、できると思いますけど、今回は、PWMで・・・。と、言うことになりました。
が、PWMでも一応、スピーカも鳴ってはいますが、簡単なアンプをつけてやった方が良さそうではあります。
回路は、以下のようにシンプルにPICとROMをI2Cでつなぐだけです。あとは、PICに発音のスイッチと出力にコンデンサをつけてやるだけです。
抵抗2本は、I2Cのプルアップ用です。
f:id:manpukukoji:20171123091801p:plain
PICのプログラムも、ROMを読んで、データの値に応じたPWM周期をセットするだけですので、データを読み込みさえすれば、後は、いたって簡単ですが、I2Cの読み込みが間に合わなくてはまりました。
I2Cの読み込みに関しては、以下のサイトを参考に(コピー)させてもらいました。
I2CでEEPROMを使う(PIC16F886)

WAVファイルのデータを8bit 8kHz monoral にすると、ちょうどPWMの周期を8bit 256ステップで置き換えられ、8kHzの割り込みでデータを更新していけばいけるだろうと思ったんですが、うまくいかなかったので、いろいろ調べてみたら、ROMの読み込みが間に合っていませんでした。
PICは、8MHzから16MHzに切り替え、I2Cの読み込みも毎回アドレス指定をしてランダムリードしていたのをシーケンシャルリードに変えたところで、なんとか間に合うようになりました。
まぁ、32MHzとかのクロックもあるようなので、今度、試してみようかな・・・? そうすると、16KHzサンプリングとかのデータでもう少しクオリティも良くなるかもしれないしね。
発音データは、あちこちから拾ってきて、WAVファイルにし、必要なデータ部分をテキストファイルに落として、作っています。 動物の鳴き声とか、虫の音とかは、ちょっとわかりにくく、やっぱり、人の音声が使いやすそうですので、一言メッセージとかには、使えるかも。 いろいろためしていると、女性のアニメ声は、他に比べて格段に明瞭なのがよくわかります。逆に、虫の音は、なんだか、言われてみないと良くわからないです。 周波数成分の問題なんでしょうかね・・・?

PIC 12F1822とI2CROMでWAVファイルを再生してみた

できれば、普段は Sleep させて、省電力を試みようかと思ったんですが、Sleepから起こすためのINTPINがI2Cのピンと重なっちゃって使えそうもなかったので、WDTでなんとかならないかと四苦八苦しましたが、あえなく玉砕しました。
PICのプログラムは、以下参照ください。 例によって、古いHiTech-C です。ぼちぼち、アップデートしないと時代遅れなのはわかっているのですが、今度、古いPC新しいのに替えたら、アップデートしようかなと思っています。

/**************************************************************
	12F1822 Play WAV
		Pin assignment:
	(1)Vdd
	(2)RA5 PWM out for WAV play
	(3)RA4 Speak SW
	(4)RA3 MCLR 10Kohm Resister between Vdd 
	(5)RA2 i2c(SDA)
	(6)RA1 i2c(SCL) / ICSPCLK
	(7)RA0 ICSPDAT /(Tx)
	(8)Gnd
**************************************************************/
#include <pic.h>
#define _XTAL_FREQ 16000000

__CONFIG(	
        FOSC_INTOSC & WDTE_OFF & PWRTE_ON & MCLRE_OFF & CP_OFF 
        & CPD_OFF & BOREN_ON & CLKOUTEN_OFF & IESO_OFF & FCMEN_OFF
);

__CONFIG(
	WRT_OFF & PLLEN_OFF & STVREN_ON & BORV_25 & LVP_OFF
);


void outchar(char);
void outbyte(char);
unsigned char i2c_read(unsigned char);

int vdata=128;
unsigned char fl;

//Delay 100msec --------------------------------
void Delay_10ms(unsigned char time){
	while(time--){
		__delay_ms(10);
	}
}


void i2c_start(void){
	
	SEN=1;
	while(SEN);
	return;
}

void i2c_restart(){
	SSP1IF = 0;
	RSEN = 1;
	while(RSEN);
	return;
}

void i2c_stop(void){
	
	SSP1IF = 0;
	PEN=1;
	while(PEN);
	SSP1IF = 0;
	return;
}

void i2c_send(unsigned char send_data){
	
	SSP1IF = 0;
	SSP1BUF = send_data;
	while(!SSP1IF);
	return;
}

unsigned char i2c_rcv(){
	unsigned char d;

	SSP1IF = 0;
	RCEN = 1;
	while(RCEN);
	d=SSP1BUF;
	return(d);
}

//------------------------------------------------

// ==================== I2C ACK check =========================
unsigned char i2c_ackchk() {
	unsigned char i2c_data;
 	
	if (ACKSTAT) {
		i2c_data = 1;
	} else {
		i2c_data = 0;
	}

	return(i2c_data);
}

// ==================== I2C ACK send ==========================
void i2c_acksnd() {
	ACKDT = 0;
	ACKEN = 1;
	while (ACKEN);

	return;
}

// ==================== I2C NACK send =========================
void i2c_nacksnd() {
	
	ACKDT = 1;
	ACKEN = 1;
	//while (ACKEN);

	return;
}

// ==================== I2C Recive char ========================
unsigned char i2c_rcv_char(unsigned char addr1 ,unsigned char addr2) {
		unsigned char c;

		i2c_start();
		i2c_send(0b10100000);		// スレーブアドレス & Write
		i2c_send(addr1);		// 読み出しアドレス1
		i2c_send(addr2);		// 読み出しアドレス2

		i2c_start();
		i2c_send(0b10100001);	// スレーブアドレス & Read
		c = i2c_rcv();

		i2c_nacksnd();

		i2c_stop();

	return(c);
}


//------------------------------------------------
void interrupt pwav(void){
	if(TMR0IF==1){
		TMR0 = 6;
		TMR0IF = 0;
		
		CCPR1L=vdata; //PWM set
		fl=1;
	}
}

//------------------main
void main(void){

	unsigned int ndata,mi;
	unsigned char a0, a1, flag, c, ii;

//Initialization
	OSCCON = 0b01111000; // PLL disable, 16MHz internal clock
 	ANSELA = 	0b00000000;	//All digital 
    TRISA 	=	0b00111110;		//GPIO dirction setting  
    PORTA	=	0b00000000;		//GPIO initialize
//---------------------------------------------------------
//PWM
     CCP1SEL = 1;          // 2番ピン(RA5)をCCP1/P1Aピンとして出力
	 CCP1CON=0b00001100;	//PWM mode: P1A, P1C active-high; P1B, P1D active-high
     						// PWM機能(シングル)を使用する
     T2CON   = 0b00000000 ; // TMR2プリスケーラ値を1倍に設定
     CCPR1L  = 128 ;          // デューティ値は128で初期化
     PR2     = 250 ;        // PWMの周期を設定
     TMR2ON  = 1 ;          // TMR2(PWM)スタート
	 while(TMR2IF == 0);
	 TRISA5=0;
	 WPUA4=1;				//Week Pull Up for RA4 SW
	 
//----------------------------------------------------------
//i2c
    SSP1ADD = 9;
    SSP1CON1 = 0b00101000;

//----------------------------------------------------------
//Serial
    SPBRG=103;
    TXSTA=0b00100100;
    TXCKSEL=0;
    SPEN=1;
    TXEN=1;

//----------------------------------------------------------
//TIMER0 INTERRUPT
	OPTION_REG=0b00000000; //pre-Scaler=1:2
	TMR0=6; 
	TMR0IE=0;
	GIE=1;	
//----------------------------------------------------------

		Delay_10ms(50);

		a0=i2c_rcv_char(0,0);
		a1=i2c_rcv_char(0,1);
		ndata=256*a0+a1;


	while(1){

//**********************************************************
		while(RA4==0){
			i2c_start();
			i2c_send(0b10100000);   //Write
			i2c_send(0);
			i2c_send(0x10);  //First data address
			i2c_start();
			i2c_send(0b10100001);   //Read
			vdata=i2c_rcv();
			i2c_acksnd();
			TMR0IE=1;
			while(fl==0);
			fl=0;

			for(mi=0;mi<ndata;mi++){
				vdata=i2c_rcv();
				i2c_acksnd();
				while(fl==0);
				fl=0;

			}
		
			TMR0IE=0;
			i2c_rcv();
			i2c_nacksnd();
			i2c_stop();

			Delay_10ms(70);

		}	//end of while (RA4==0)

//***********************************************************

	}  //end of while
}  //end of main

ラズベリーパイをROMライターにしてみた(Writer編)

前回のReader編に引き続き、今日は、Writer編
設定とか、ROMとラズパイの接続とかは、前回のReader編とまったく同じです。

まずは、Pythonのプログラムから。

#!/usr/bin/env python
#-*- coding: UTF-8 -*-

import smbus
import time
import sys

bus = smbus.SMBus(1)


def write_ROM():
    count=0
    ad=0
    f=open(f_name,'r')
    for m in f:
        l1=m[:-1].split(' ')
        l2=[ l1[0][0:2], l1[0][2:4], l1[0][4:6], l1[0][6:8], l1[1][0:2], l1[1][2:4], l1[1][4:6], l1[1][6:8], l1[2][0:2], l1[2][2:4], l1[2][4:6], l1[2][6:8], l1[3][0:2], l1[3][2:4], l1[3][4:6], l1[3][6:8] ]
        l3=[(count%16)*16,int(l2[0],16),int(l2[1],16),int(l2[2],16),int(l2[3],16),int(l2[4],16),int(l2[5],16),int(l2[6],16),int(l2[7],16),int(l2[8],16),int(l2[9],16),int(l2[10],16),int(l2[11],16),int(l2[12],16),int(l2[13],16),int(l2[14],16),int(l2[15],16) ]
        print l2

        bus.write_i2c_block_data(0x50,ad,l3)
        count+=1
        if count%16 == 0:
            ad+=1
        time.sleep(0.2)

    b1 = (count)*16/256
    b2 = (count)*16%256
    l_count=[count*16,b1,b2]
    print('bytes=%d Bytes1=%x bytes2=%x' %(l_count[0],l_count[1],l_count[2]))
    
    f.close()

if __name__ == '__main__':
    args = sys.argv
    if len(args)<3:
        print('usage: python {} <Device Address(HEX)> <source file name>'.format(__file__))
        exit(0)
    addr=int(args[1],16)
    f_name=args[2]

    write_ROM()

若干、ちからまかせ的な、あまりスマートじゃないスクリプトですが、まぁ、そこは目をつぶっていただいて、
使い方です。
もっぱら、自分の目的のためですので、あまり、汎用的ではありませんが、
4バイトごとに空白をはさんだ1行16バイトのデータファイルを読んで、ROMに書き込みます。
例えば、上記のスクリプトを"i2cromwrite.py" というファイルにしたとしますと、

 $ python i2cromwrite.py 50 HONYOHONYO.txt

とやると、HONYOHONYO.txtというファイルのデータが書き込まれます。
最初の50は、デバイスアドレス 0x50 です。
自分の場合は、WAVファイルをROMに落としたかったので、まず、WAVファイルを用意して、バイナリエディタで開きます。
僕の場合は、xedit.exe というフリーのバイナリエディタを使ったので、データが4バイトごとのブロックで1行16バイト表示されます。 ここから、必要な部分を切り取って、テキストファイルにデータを貼り付けます。
テキストファイル上で必要な加工を行った後、ラズパイに転送し、ROMに書き込むという手順でやっています。 ちなみに必要な加工というのは、最初の行にデータのバイト数を書き込んでいます。 そうすると、PICで読み込むときに、その回数読めばすむので・・・。
この一連の作業が、以下のような感じになります。

まず、WAVファイルを開いて、データ部分を切り取ります。
f:id:manpukukoji:20171027110127p:plain
続いて、メモ帳を開いて、データを貼り付け、最初の行にバイト数を書き込みます。
f:id:manpukukoji:20171027110230p:plain
このファイルをラズパイに送ります。
FTPでも良いし、USBメモリやSDカード経由でも良いし、なんでも良いかと思います。
そして、上記のPythonのコードでROMに書き込みます。
f:id:manpukukoji:20171027110745p:plain
これで、ROMに書き込まれている・・・はずです・・・。

ラズベリーパイをROMライターにしてみた(Reader編)

以前買い置きしていたI2CのROM 24C512 が手元にあり、これを使おうと思ったのだけど、データを書き込むのがめんどくさい。そのためにAruduinoとかPICとかで書き込んでも良いのだけど、PCで作ったデータファイルをROMに書き込むとなると、PCでデータ作って、書き込み用のプログラム作って、シリアルでArduinoに送り、Arduino側でシリアルとI2Cのプログラム作って・・・って、あー、もう、めんどくせー! となるので、なんか、良い方法はないかいな? と、考えてたら、
おー!ラズパイがあった!
ラズパイだと、I2Cもいけるし、ファイルも簡単に読み書きできる!
と、いうわけで、ラズベリーパイで24C512 を読み書きするための、ROM Reader/Writer を試してみた。
f:id:manpukukoji:20171027100652j:plain
と、言っても、ハードウェアは、ブレッドボードにROMを置いて、ラズパイとジャンパー線4本でつなぐだけ。
I2Cのプルアップも、なぜか、必要なかった。・・・ラズパイ側でプルアップされているのかな???
あとは、Pythonでチョコチョコッと・・・。
使ったラズパイは、RasPi3で、OSはRaspbian with Pixcelってやつ。 比較的新しい方だと思う。I2Cのバスも”1”になってました。

古いやつだと、I2Cを使うのに、i2c-tools とか、smbus とかのPythonのライブラリをインストールする必要があったようだけど、僕の場合、何もしなくてもすでに入っていたみたい。ラズパイの設定でインターフェースのタブでI2Cを有効にしておけば、それだけで問題なく使えました。
このあたりは、検索するといろいろ詳しく説明されたサイト(I2C表示器が多いけど同じです。)があるので、省略するとして、こちらでは、いきなりROMをつないで読むところからです。
例えば、このあたりのサイトで、丁寧に説明されています。
Raspberry Pi で I2C を使用する準備 – 或る阿呆の記


ROMは、ATMELの24C512 です。ですので、他のROMで同じようにいくわけではないと思います。秋月電子通商のサイトで見ると、300円(http://akizukidenshi.com/catalog/g/gI-00561/)だけど、同様の24LC512って言うのもある。こちらは、価格もほぼ半額(http://akizukidenshi.com/catalog/g/gI-02524/)だし、同じように使えるのではないかと思うのだけど、現物を持っていないので、試しておらず、わからないので、あしからず・・・。
ラズパイとの接続は、以下のようにつないだ。

RaspberryPi        24C512
Pin 01  3V    ---   Pin 8
Pin 03  SDA   ---   Pin 5
Pin 05  SCL   ---   Pin 6
Pin 09  GND   ---   Pin 4

f:id:manpukukoji:20171027074550p:plain

Frizingの図は、電源部分、勘違いしやすいので、V+とGND、間違わないように十分注意してね。 ・・・自己責任でよろしく!
上記以外のROMのピンはそのまま(float)ですが、デバイスアドレスを指定するような場合には、アドレスに応じてプルアップすると良いようです。7ピンは、ライトプロテクトなので、書き込みする場合には、そのままか、GNDに落とします。
接続できたら、ラズパイで確認します。

$ i2cdetect -y 1

と、やると、特にアドレスピンを設定していなければ、50 と表示されるはずです。
表示されなければ、ROMが認識されていないかもしれません。
あとは、以下のPythonプログラムを走らせるとROMが読めると思います。
プログラム中の"bus=smbus.SMBus(1)" のところ、もし、バスが0の場合には、添え字を"(0)"にしてくださいね。
ポイントは、24C512は、アドレスが、2バイトなので、smbusのライブラリでは、本来の使い方では、1バイトしか書けないみたいで、"write_i2c_block_data でデータ部分の最初のデータに2バイト目のアドレスを書くことによって、2バイト分のアドレスを指定しているみたいです。

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import smbus
import sys

bus = smbus.SMBus(1)
addr = 0
adr1=0
adr2=0
startaddr = 0
endaddr = 0

def read_ROM():
    l1=[]
    print('%4s : %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X ' %('Adr',0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15) )
    print('-'*54)
    for i in range(startaddr/256,endaddr/256+1):       
        for j in range(16):
            for k in range(16):
                bus.write_i2c_block_data(addr,i,[16*j+k])
                l1.append(bus.read_byte(addr))
        
            print('%4X : %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X ' %(256*i+16*j,l1[0],l1[1],l1[2],l1[3],l1[4],l1[5],l1[6],l1[7],l1[8],l1[9],l1[10],l1[11],l1[12],l1[13],l1[14],l1[15]) )

            l1=[]

if __name__ == '__main__':
    args = sys.argv
    if len(args) < 4 :
        print('usage: python {} <Device Address(HEX)> <Start Address(HEX)> <End Address(HEX)>'.format(__file__))
        exit(0)
    addr = int(args[1],16)
    startaddr = int(args[2],16)
    endaddr = int(args[3],16)
    
    read_ROM()

使い方は、
このスクリプトのファイル名に続いて、デバイスアドレス(50) 読み込みの開始アドレス 読み込み終了アドレス となります。
例えば、"i2cromread.py" という名前で保存し、デバイスは、0x50、読み込み開始アドレスは、0x200、終了アドレス0x21F とすると、以下のようになります。 ただし、表示は、256バイトごとのブロックで表示されるので、要らない部分も表示されるかと思いますが、そこんとこ、よろしく・・・。

$ python i2cromread.py 50 200 21F

f:id:manpukukoji:20171027090206p:plain
と、言うわけで、Writer編は、次回に・・・。

手をかざすと点灯するLEDランプを作ってみた

 シャープの測距モジュールGP2Y0A21YKとPIC 12F1822 を使って、さっと、手をかざすと点灯するLEDランプを作ってみた。
f:id:manpukukoji:20171023113840j:plain
ランプの上で、さっと手をかざすとピッと音が鳴って、ランプが、点灯、消灯する。
ランプの上で、しばらく手をかざしたままでいると、ピピッと鳴って、調光モードとなり、音が鳴ったところから手を下にやると、だんだん暗くなり、上にやると、だんだん明るくなる。
好みの明るさのところで手をはずすと、そのときの明るさがキープされる仕組み。
明るさの最大値、最小値に達すると、また、ピピッとなる。

手をかざすと点灯するLEDランプ

回路図は、以下のようなもの。
f:id:manpukukoji:20171023114952j:plain
パーツは、手元のジャンク箱のトランジスタとか適当ですが、
測距モジュールは、10cmから80cmまで測れて最大3Vのようなので、そのまま、PICのAD変換でいけると思うのですが、いまいち、思うように反応してくれなかったので、トランジスタをはさんでいます。LEDも実際には、3WのパワーLEDを使っていますが、Vfが3Vくらいで、5Vで作っているので、実質2V、電流制限抵抗を5Ωにしているので、400mAくらい流れるはずですが、実測、200mAくらいでした。と、いうわけで、ゆる~く点灯しているので、ぜんぜん熱くならないので、このまま特に放熱もせずに使っています。
f:id:manpukukoji:20171023133859j:plain
もし、しゃかりきに点灯するのであれば、LED駆動用のトランジスタの見直し(ここで使っているのは、ジャンク箱の古いTrなので、どの道、見直しが必要だと思いますが・・・。)とか、何か放熱対策が必要かも・・・。
明るさのコントロールは、PWMでやっています。

PICのプログラムは、以下のような感じです。
回路もプログラムも変なところがあるかと思いますが、とりあえず、動いています。
参考にされる方が、万が一あるようでしたら、あくまでも、自己責任で・・・。よろしく・・・。
あっ、それと、うちのPC古いのでMPLAB IDEも古いままなので、プログラムは、あまり役に立たないかも・・・。

/**************************************************************
	12F1822 LED Motion Control
		Pin assignment:
	(1)Vdd
	(2)RA5 PWM out for LED
	(3)RA4(AN3) ADC----Distance Sensor GP2Y0A21
	(4)RA3 MCLR 10Kohm Resister between Vdd 
	(5)RA2(AN2)
	(6)RA1 SP out / ICSPCLK
	(7)RA0 ICSPDAT 
	(8)Gnd
**************************************************************/
#include <pic.h>
#define _XTAL_FREQ 8000000

__CONFIG(	
        FOSC_INTOSC & WDTE_OFF & PWRTE_ON & MCLRE_OFF & CP_OFF 
        & CPD_OFF & BOREN_ON & CLKOUTEN_OFF & IESO_OFF & FCMEN_OFF
);

__CONFIG(
	WRT_OFF & PLLEN_OFF & STVREN_ON & BORV_25 & LVP_OFF
);

//Delay 100msec --------------------------------
void Delay_10ms(unsigned char time){
	while(time--){
		__delay_ms(10);
	}
}

void beep(void){
	int i;
	for(i=1;i<200;i++){
		RA1=1;
		__delay_us(200);
		RA1=0;
		__delay_us(200);
	}
}


//------------------main
void main(void){
	unsigned int SW=0, command=0,incliment;
	int adcv,getv,bright=30;

//Initialization
	OSCCON = 0b01110010;            // PLL disable, 4MHz internal clock
 	ANSELA = 	0b00010000;     //RA4 to ADC 
        TRISA 	=	0b00111000;	//GPIO dirction setting  
        PORTA	=	0b00000000;	//GPIO initialize
//---------------------------------------------------------
//PWM
     CCP1SEL = 1 ;          // 2番ピン(RA5)をCCP1/P1Aピンとして出力
     CCP1CON=0b00001100;	//PWM mode: P1A, P1C active-high; P1B, P1D active-high
     						// PWM機能(シングル)を使用する
     T2CON   = 0b00000000 ; // TMR2プリスケーラ値を1倍に設定
     CCPR1L  = 0 ;          // デューティ値は0で初期化
     PR2     = 0x65 ;        // PWMの周期を設定(19kHzで設定)
     TMR2ON  = 1 ;          // TMR2(PWM)スタート

     while(TMR2IF == 0);
     TRISA5=0;
	 
//----------------------------------------------------------
//ADC
	ADCON1 = 0b00010000;
	ADCON0 = 0b00001100; //AN3 to ADC
//----------------------------------------------------------

    while(1){
	ADON=1;
	__delay_ms(1);
	GO_nDONE = 1;
	while(GO_nDONE);
	adcv=ADRESH;
	if(adcv<=230){
	   command=1;
	   Delay_10ms(50);
	   ADON=1;
	   __delay_ms(1);
	   GO_nDONE = 1;
	   while(GO_nDONE);
	   adcv=ADRESH;
	   if(adcv>250){
		beep();
		SW=!SW;			
		if(SW==1){bright=30;}
		else{bright=0;}

		CCPR1L=bright;
		command=0;
	   }
	   else if(SW==1){
		beep();Delay_10ms(3);beep();
		Delay_10ms(100);
		getv=adcv;
		while(command==1){
			ADON=1;
			__delay_ms(1);
			GO_nDONE = 1;
			while(GO_nDONE);
			adcv=ADRESH;
			if(adcv<250){
				if(adcv>getv){
					incliment=1;
					bright+=3;
					if(bright>90){
                                             bright=90;
	                                     beep();
                                             Delay_10ms(3);
                                             beep();
                                        }
				}
				else if(adcv<getv){
					incliment=2;
					bright-=3;
					if(bright<=3){
                                            bright=3;
                                            beep();
                                            Delay_10ms(3);
                                            beep();
                                        }
				}
				CCPR1L=bright;
				beep();
				Delay_10ms(50);
			}
			if(adcv>250)command=0;
		}
		}
		}
		Delay_10ms(1);
	}
}

L-メータの製作

だいたいで良いからコイルのインダクタンスが知りたいときがある。 特に、ジャンク箱から引っ張り出してきた得体の知れないコイルとか・・・。
そのために専用のメータを買うのもいまいちなので、簡単な発振器で発振させ、その周波数からインダクタンスを計算するように、発振器を作ってみた。
f:id:manpukukoji:20171009111622j:plain
回路は、こちらのサイトの内容をそっくりそのまま使わせていただきました。
電子回路 Vol.6 【製作編:ピアース発振回路のLメーター】("http://roomx.jp/pc/roomb/?p=734" )
ピアース発振回路の応用でインダクタンスを計測 ("http://ednjapan.com/edn/articles/0711/01/news145.html")


ちょうど、失敗した感光基盤があったので、これに、レジストペンでゴニョゴニョと手書きでプリント基板を作成。
部品数も少ないので簡単にできちゃった。
f:id:manpukukoji:20171009113653j:plain
これに調べたいコイルをつないで発振させて、その周波数をテスターとかオシロとかで測定して、後はそこから計算すれば、インダクタンスが得られる。
コンデンサは、0.1uFを使ったので、計算では0.05uFになるはずだけど、いろいろな既知のコイルを測定してみて、実測値から、0.047uFとして、計算用のスクリプトPythonでチョコちょこっと・・・。
わりと、便利に使えそうです。
ただ、5Vで発振させたら、ICが結構アッチチで、データシートでは7VくらいまでOKのはずなんだけど、電流も180mAくらい流れていて、ちょっと、やばそうだったので、3Vくらいで使っています。これでも、70mAくらい流れていました。 う~ん、何か作り方に問題があったかな・・・? とりあえずは、使えているんだけどね・・・。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#Calcuration script for L-Mater
# Capacitance = 0.047uF

f=input("Input Frequency(kHz) : ")
f=float(f)
l=1/(2*3.1415*f)**2/(0.047*10**-6)
print("L = {0:6.1f} uH".format(l))

ラズベリーパイとUSBカメラでビデオキャプチャしてタイムラプス動画作成

一定時間ごとに写真をとって、定点観察したいと思い、ラズベリーパイとUSBカメラでビデオキャプチャしてみた。
ラズベリーパイは、PythonOpenCVで一定時間ごとにビデオキャプチャするスクリプトを作って、キャプチャした映像をFTPWindowsに取ってきて、タイムラプス動画をWindows上で作成した。
で、とりあえず、できたのがこの動画

ラズベリーパイとUSBカメラでビデオキャプチャしてタイムラプス動画を作成

ま、やり方は、CRON使う手とか、いろいろあると思うけど、とりあえず、手っ取り早くPython上で全部まかなって、凝った仕組みは何もないけど、まぁ、目的は達成できているので、こんなもんかな・・・?
OpenCV使うと、こんなことも、簡単にできちゃうんですね。 
おそるべし、OpenCV
自分では、一からこんなのとても作れるとは思えない・・・。 ありがたいこってす・・・。
Pythonスクリプトは、以下のようなもの。
エラー処理もしていないし、保存先のフォルダも固定だし、ファイル名も固定です。
スクリプトのおいてあるフォルダの下に、Imgというフォルダを作ると、そこに、CapXXXX.jpgというファイルができていきます。 XXXXのところには、キャプチャしたときの日付と時間になります。例えば、2017年10月07日の9時15分00秒だと、Cap20171001091500.jpg となります。 このままだと、180秒(3分)ごとにキャプチャされます。
for文 でごにょごにょしているのは、いきなりキャプチャすると、なぜか真っ黒だったので、苦肉の策です。
もっと、ちゃんとしたスマートな方法があるのでしょうが、まぁ、こんなにちょこっとのスクリプトなんで、良いか・・・。
ってなもんです。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import cv2
import datetime
from time import sleep

cap = None
count=0
camera_port = 0   # USBカメラのデバイスは0 (/dev/video0)
cap = cv2.VideoCapture(camera_port)

while(True):
  for i in range(10):
    ret, im = cap.read()
    if ret==True:
        if i==9:
            tm=datetime.datetime.today().strftime("%Y/%m/%d %X")
            ftm=datetime.datetime.today().strftime("%Y%m%d%H%M")
            cv2.putText(im,tm,(20,450),cv2.FONT_HERSHEY_PLAIN,2,(255,255,0))
            file_name = "./Img/Cap" + ftm + ".jpg"
            print (file_name)
            cv2.imwrite(file_name, im)
            print("File written")
            sleep(180)

cap.release()
cv2.destroyAllWindows()

太陽の追尾装置の製作

サーボモータと光センサーで太陽の追尾装置を作ってみた。 太陽追尾というか、よーするに明るいところを追いかける装置です。 
f:id:manpukukoji:20171004121609j:plain
マイコンは、16F1827、センサーは、Cdsセルで、上下左右、4つのセンサーで、上下でより明るい方、左右でより明るい方を向くようにサーボモータをコントロール。 上下で120度、左右180度の可動範囲になっています。
センサーの隔壁は、最初、筒型のパトリオットミサイルのランチャーみたいなのを試してみたのですが、いまいち、うまく作動しなかったので、シンプルな、田の字型の隔壁スタイルにしたら、わりと良さげだったので、これを採用。
f:id:manpukukoji:20171004121640j:plain
一応、基盤は汎用性をもたせて、サーボのコントロール部分をメイン基盤とし、センサー部分は、ドーターボードのようにして別基盤にしています。
ライトでグリグリやると、なんか面白いです。

サーボモータで光を追跡

回路図は、以下
f:id:manpukukoji:20171004112237p:plain

ほんでもって、PICのプログラムは、以下です。

/*	Project Twin servo control
	PIC16F1827 with two servo moter and 4 Cds cell sensor
	trace the britest spot (Sun tracker)
*/

/*PIN of 16F1827 asign
	RA0 : Cds1 (Upper)		RB0 : Servo PWM Vertical
	RA1 : Cds2 (Lower)		RB1 : Servo PWM Holizontal
	RA2 : Cds3 (Right)		RB2 : Rx
	RA3 : Cds4 (Left)		RB3 : 
	RA4 :  				RB4 : 
	RA5 : MCLR			RB5 : Tx
	RA6 : 				RB6 : 
	RA7 : 				RB7 : 
*/
#include <pic.h>

#define _XTAL_FREQ  8000000    // delay用(クロック8MHzで動作時)
#define INT_START 25536		//20msの割り込みの初期値(65536-25536=40000)

__CONFIG( WRT_OFF & MCLRE_OFF & PWRTE_ON & WDTE_OFF & FOSC_INTOSC & CP_OFF & CPD_OFF) ;
__CONFIG(PLLEN_OFF & STVREN_ON & BORV_25 & LVP_OFF);

unsigned short next1,next2;	//next1 for UpDown, next2 for RightLeft


void delay(unsigned char d){
	int i;
	for(i=0;i<d;i++) __delay_ms(10);
}

void interrupt pwm( void )
{
    // タイマ1割り込み処理
    if( TMR1IF )
    {
        RB0 = 1;
        RB1 = 1;

        CCPR1H = (next1>>8);	//next1 for UpDown, next2 for RightLeft
		CCPR1L = (0x00ff & next1);
        CCPR2H = (next2>>8);
		CCPR2L = (0x00ff & next2);
        TMR1 = INT_START;   // 20msec毎に割り込み
        TMR1IF = 0; // タイマ割り込みフラグをクリア
    }

    // CCP1割り込み処理
    if( CCP1IF )
    {
        RB0 = 0;
        CCP1IF = 0; // コンペアマッチ割り込みフラグをクリア
    }

    // CCP2割り込み処理
    if( CCP2IF )
    {
        RB1 = 0;
        CCP2IF = 0; // コンペアマッチ割り込みフラグをクリア
    }
}



void main(void){

     int m1,m2,vdata;		//m1 for UpDown, m2 for RightLeft
	 int v1,v2,h1,h2;	//ADC value
	 
     m1=3000;		//1.5mSec
     m2=3000;

	vdata=0;
	
     OSCCON     = 0b01110010;    // 内部クロックは8MHzとする
     OPTION_REG = 0b00000000;
//割り込み設定
	 CCP1CON=0b00001010;	//compair mode
	 CCP2CON=0b00001010;
     T1CON=0b00000100;		//1:1 PreScaler
//     CCP1M=0b1010;		//CCP1 Compair Interrupt
//     CCP2M=0b1010;		//CCP2 Compair Interrupt

//pin設定
     TRISA  = 0b00101111;    // 1で入力 0で出力 RA0-RA3:Input RA6-RA7:Outputに設定(RA5は入力専用)
     TRISB  = 0b00000000;    // RB1-RB7全てを出力に設定 RB0,RB1をサーボ制御に使用
     ANSELA = 0b00001111;    //RA0-RA3 をADCにセット
     ANSELB = 0b00000000;    //汎用ポート RB3(AN9)をADCにセット
     PORTA  = 0b00000000 ;   // RA出力ピンの初期化(全てLOWにする)
     PORTB  = 0b00000000 ;   // RB出力ピンの初期化(全てLOWにする)

//ADC設定
	 ADCON0 = 0b00100100;
	 ADCON1 = 0b11000000;	//右寄せ, Fosc/8

//割り込み初期値
    TMR1IE = 1; // タイマ1割り込み有効
    CCP1IE = 1; // コンペアマッチ割り込み有効
    CCP2IE = 1; // コンペアマッチ割り込み有効

    TMR1 = INT_START;
    next1 = INT_START + 3000;  // 1.5msec
    next2 = INT_START + 3000;  // 1.5msec

    TMR1ON = 1; // タイマ1 開始

    TMR1IF = 0;
    CCP1IF = 0;
    CCP2IF = 0;
    PEIE = 1;
    GIE  = 1;    // 全割り込み有効


while(1) {

//ADC 

//ADC0
	ADCON0=0b00000011;
	__delay_us(50);
	GO_nDONE=1;
	while(GO_nDONE);
	vdata=ADRESH;
	vdata<<=8;
	vdata|=ADRESL;
	v1=vdata;
	
//ADC1
	ADCON0=0b00000111;
	__delay_us(50);
	GO_nDONE=1;
	while(GO_nDONE);
	vdata=ADRESH;
	vdata<<=8;
	vdata|=ADRESL;
	v2=vdata;


//ADC2
	ADCON0=0b00001011;
	__delay_us(50);
	GO_nDONE=1;
	while(GO_nDONE);
	vdata=ADRESH;
	vdata<<=8;
	vdata|=ADRESL;
	h1=vdata;

//ADC3
	ADCON0=0b00001111;
	__delay_us(50);
	GO_nDONE=1;
	while(GO_nDONE);
	vdata=ADRESH;
	vdata<<=8;
	vdata|=ADRESL;
	h2=vdata;

//Compair brightness
//Vertical direction
	if(v1>v2){
		if((v1-v2)>3){
			m1=m1+(v1-v2);
			if(m1>=4000){m1=4000;}
		}
	}
	else if(v1<v2){
		if((v2-v1)>3){
			m1=m1-(v2-v1);
			if(m1<=1900){m1=1900;}
		}
	}

	next1=INT_START+m1;
	
//Holizontal direction
	if(h1>h2){
		if((h1-h2)>3){
			m2=m2+(h1-h2);
			if(m2>=4800){m2=4800;}
		}
	}
	else if(h1<h2){
		if((h2-h1)>3){
			m2=m2-(h2-h1);
			if(m2<=1000){m2=1000;}
		}
	}
	
	next2=INT_START+m2;
	

	delay(10);

}	//End of While
}	//End of main