ページ 11

AVRとカラーセンサーのI2C接続について

Posted: 2015年8月21日(金) 19:13
by keysumer
以下のプログラムによって、AVR(ATmega328P-PU)と、カラーセンサーモジュール(S11059-02DT)をI2Cで接続し、TeraTermにて値を読もうとしています。

現状としては、TeraTermに値は送信されてくるのですが、同じ値ばかり送られてきてしまい、センサーに手をかざしたり、光を当てても変化が見られませんでした。

念のため、別の人にArduinoで確認してもらったところ、センサー自体は壊れていないそうです。
(じゃあその人に聞けば。と思うかも知れませんが、今取り組んでいる内容が、その人から課せられた課題のようなものでして、本人に聞くのはNGなのです。)

そのため、プログラム中での値の読み込み方に問題があるのだと思いますが、どこを直すべきなのかが分からず困っております。


(書き込み機はAVRISP mk2を使用、PCとの接続にはFT232RLを使用しています)

コード:


#include<avr/io.h>
#include<util/twi.h>
#include<util/delay.h>

typedef unsigned int byte;

static unsigned int i=0;

unsigned int red = 0;

/////////////////////////USART///////////////////////

byte getch(void){

	while(!(UCSR0A & _BV(RXC0)));
	return UDR0;
}

void putch(byte c)
{

	while(!(UCSR0A & _BV(UDRE0)));
	UDR0 = c;
}

/*USART通信初期化用関数*/
void usart_init(void){

	UBRR0  = 25;
	
	UCSR0A = 0b00100000;	
	UCSR0C = 0b00000110;
	UCSR0B = 0b00011000;	
}


/////////////////////////I2C///////////////////////
/*16進数1桁計算用関数*/
void puthex1(byte d){

	d += '0';
	if(d>'9') d+='a'-'9'-1;
	putch(d);

}

/*16進数2桁計算用関数*/
void puthex2(byte d){

	puthex1(d>>4);
	puthex1(d & 0x0F);

}

/*結果計算用関数*/
void putdec2(byte x){

	putch('0' + (x/1000)%10);
	putch('0' + (x/100)%10);
	putch('0' + (x/10)%10);
	putch('0' + x%10);

}

/*エラー処理用関数*/
void i2c_error(void){

	putch('[');
	puthex2(TWSR);
	putch(']');
	putch(0x0d);
	putch(0x0a);

	while(1);
}

/*I2C初期化用関数(通信速度)*/
void i2c_init(void){

	TWSR = 0x00;	/*分周値=1*/
	TWBR = 32;		/*100k = 8MHz / (16+2*TWBR*分周値)*/
}

/*master側:1byte送信用関数*/
void i2c_write(byte d){

	TWDR = d;		/*送信データ*/
	TWCR = _BV(TWINT) | _BV(TWEN);	/**/
	while(!(TWCR & _BV(TWINT)));	/*データ送信完了まで待機*/
	if((TWSR & TW_STATUS_MASK) != TW_MT_DATA_ACK)	/*ACKを送れない場合*/
	i2c_error();
}

/*master側:1byte受信用関数(+ack返信)*/
byte i2c_read_ack(void){

	TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWEA);
	while(!(TWCR & _BV(TWINT)));	/*データ受信完了まで待機*/
	if((TWSR & TW_STATUS_MASK) != TW_MR_DATA_ACK)	/*ACKが返ってこない場合*/
	i2c_error();
	return TWDR;	/*データを送り返す*/
}

/*master側:1byte受信用関数(+noack返信)*/
byte i2c_read_nak(void){

	TWCR =_BV(TWINT) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));	/*データ受信完了まで待機*/
	if((TWSR & TW_STATUS_MASK) != TW_MR_DATA_NACK)	/*NACKが返ってこない場合*/
	i2c_error();
	return TWDR;	/*データを送り返す*/
}

/*master側:送信開始用関数*/
void i2c_start(byte id){

	byte s;

	/*開始条件送信*/
	TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));		/*開始条件の送信完了まで待機*/
	s = TWSR & TW_STATUS_MASK;
	if(s != TW_START && s != TW_REP_START) i2c_error();

	/*アドレス送信*/
	TWDR = id;
	TWCR = _BV(TWINT) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));	/*アドレスの送信完了まで待機*/
	s = TWSR & TW_STATUS_MASK;
	if(s != TW_MT_SLA_ACK && s != TW_MR_SLA_ACK) i2c_error();
}

/*master側:送信終了用関数*/
void i2c_stop(void){

	TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
	while(!(TWCR & _BV(TWSTO)));	/*送信終了になるまで待機*/
}


/*スレーブ(センサー)のアドレス設定*/
#define sensor (0x2A << 1)




int main(){

	byte th, tl;
	
	/*DDRD  = 0x01;*/
	/*PORTD = 0x00;*/

	usart_init();
	i2c_init();

	/*センサーへの設定*/
	i2c_start(sensor | TW_WRITE);	/*センサーへのデータ書き込み*/
	i2c_write(0x00);		/*コントロールバイトを指定*/
	i2c_write(0x84);		/*ADCリセット*/
	i2c_write(0b01010100);	/*アドレス再設定*/
	i2c_write(0x00);		/*コントロールバイトを指定*/
	i2c_write(0x04);		/*ADCリセット解除*/
	i2c_stop();

	_delay_ms(2500);		/*センサの計算終了まで待機*/
	
	i2c_start(sensor | TW_WRITE);
	i2c_write(0b01010100);	/*アドレス再設定*/
	i2c_write(0x03);		/*出力データバイトを指定*/
	i2c_write(0b01010101);	/*リードモードに変更*/
	i2c_stop();


	while(1){

		for(i=0;i<4;i++){
		
			/*センサー値受信*/
			i2c_start(sensor | TW_READ);
			th = i2c_read_ack();	/*上位バイトを読み込み*/
			tl = i2c_read_nak();	/*下位バイトを読み込み*/
			i2c_stop();

			_delay_ms(100);	/*0.1s待機*/
		
			/*結果出力*/
			putdec2(th | tl>>8);	/*結果計算(上位+下位)*/
			//putdec2(red);
			
			putch(',');
		}
		putch(0x0d);	/*改行*/
		putch(0x0a);
		
		i=0;
	}
}




Re: AVRとカラーセンサーのI2C接続について

Posted: 2015年8月21日(金) 20:45
by みけCAT
keysumer さんが書きました:

コード:

	/*センサーへの設定*/
	i2c_start(sensor | TW_WRITE);	/*センサーへのデータ書き込み*/
	i2c_write(0x00);		/*コントロールバイトを指定*/
	i2c_write(0x84);		/*ADCリセット*/
	i2c_write(0b01010100);	/*アドレス再設定*/
	i2c_write(0x00);		/*コントロールバイトを指定*/
	i2c_write(0x04);		/*ADCリセット解除*/
	i2c_stop();
マニュアル設定モードなので、一度測定すると自動的に待機モードに移行するはずです。
(秋月電子通商 「I2C対応カラーセンサ S11059-02DT」 (I-06793)のページで公開されているデータシートより)

従って、固定時間モードに設定するか、読み込み後にもう一度測定を指示しないとデータが更新されなそうな気がします。
keysumer さんが書きました:

コード:

			/*センサー値受信*/
			i2c_start(sensor | TW_READ);
			th = i2c_read_ack();	/*上位バイトを読み込み*/
			tl = i2c_read_nak();	/*下位バイトを読み込み*/
			i2c_stop();

			_delay_ms(100);	/*0.1s待機*/
		
			/*結果出力*/
			putdec2(th | tl>>8);	/*結果計算(上位+下位)*/
			//putdec2(red);
TWDRが8ビットだと仮定すると、その値をunsigned int型(C言語なら少なくとも65535以下の非負整数が格納できるはず)の変数に格納し、
8ビット右にシフトすると常に0になりそうですが、この式は適切ですか?
また、このリードモードに変更した後ストップ、読み出しも2バイトごとにnakしてストップ、という動作も前述のデータシートと異なりますが、大丈夫なのでしょうか?

Re: AVRとカラーセンサーのI2C接続について

Posted: 2015年8月22日(土) 15:00
by keysumer
コメントありがとうございます。

指摘していただいた点の修正を自分なりにおこなってみました。
ですが、自分の修正が良くないようで、まだ値がしっかりと読めておりません。

お手数ですが、書き換えたものをもう一度見ていただいてもよろしいでしょうか。

コード:

#include<avr/io.h>
#include<util/twi.h>
#include<util/delay.h>

typedef unsigned char byte;

/*static unsigned int i=0;*/

unsigned char red   = 0;		/*赤色値格納用*/
unsigned char green = 0;		/*緑色値格納用*/
unsigned char blue  = 0;		/*青色値格納用*/
unsigned char ray   = 0;		/*赤外線値格納用*/


/////////////////////////USART///////////////////////

byte getch(void){

	while(!(UCSR0A & _BV(RXC0)));
	return UDR0;
}

void putch(byte c)
{

	while(!(UCSR0A & _BV(UDRE0)));
	UDR0 = c;
}

/*USART通信初期化用関数*/
void usart_init(void){

	UBRR0  = 25;
	
	UCSR0A = 0b00100000;	
	UCSR0C = 0b00000110;
	UCSR0B = 0b00011000;	
}


/////////////////////////I2C///////////////////////
/*16進数1桁計算用関数*/
void puthex1(byte d){

	d += '0';
	if(d>'9') d+='a'-'9'-1;
	putch(d);

}

/*16進数2桁計算用関数*/
void puthex2(byte d){

	puthex1(d>>4);
	puthex1(d & 0x0F);

}

/*結果計算用関数*/
void putdec2(byte x){

	putch('0' + (x/1000)%10);
	putch('0' + (x/100)%10);
	putch('0' + (x/10)%10);
	putch('0' + x%10);

}

/*エラー処理用関数*/
void i2c_error(void){

	putch('[');
	puthex2(TWSR);
	putch(']');
	putch(0x0d);
	putch(0x0a);

	while(1);
}

/*I2C初期化用関数(通信速度)*/
void i2c_init(void){

	TWSR = 0x00;	/*分周値=1*/
	TWBR = 32;		/*100k = 8MHz / (16+2*TWBR*分周値)*/
}

/*master側:1byte送信用関数*/
void i2c_write(byte d){

	TWDR = d;		/*送信データ*/
	TWCR = _BV(TWINT) | _BV(TWEN);	/**/
	while(!(TWCR & _BV(TWINT)));	/*データ送信完了まで待機*/
	if((TWSR & TW_STATUS_MASK) != TW_MT_DATA_ACK)	/*ACKを送れない場合*/
	i2c_error();
}

/*master側:1byte受信用関数(+ack返信)*/
byte i2c_read_ack(void){

	TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWEA);
	while(!(TWCR & _BV(TWINT)));	/*データ受信完了まで待機*/
	if((TWSR & TW_STATUS_MASK) != TW_MR_DATA_ACK)	/*ACKが返ってこない場合*/
	i2c_error();
	return TWDR;	/*データを送り返す*/
}

/*master側:1byte受信用関数(+noack返信)*/
byte i2c_read_nak(void){

	TWCR =_BV(TWINT) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));	/*データ受信完了まで待機*/
	if((TWSR & TW_STATUS_MASK) != TW_MR_DATA_NACK)	/*NACKが返ってこない場合*/
	i2c_error();
	return TWDR;	/*データを送り返す*/
}

/*master側:送信開始用関数*/
void i2c_start(byte id){

	byte s;

	/*開始条件送信*/
	TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));		/*開始条件の送信完了まで待機*/
	s = TWSR & TW_STATUS_MASK;
	if(s != TW_START && s != TW_REP_START) i2c_error();

	/*アドレス送信*/
	TWDR = id;
	TWCR = _BV(TWINT) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));	/*アドレスの送信完了まで待機*/
	s = TWSR & TW_STATUS_MASK;
	if(s != TW_MT_SLA_ACK && s != TW_MR_SLA_ACK) i2c_error();
}

/*master側:送信終了用関数*/
void i2c_stop(void){

	TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
	while(!(TWCR & _BV(TWSTO)));	/*送信終了になるまで待機*/
}


/*スレーブ(センサー)のアドレス設定*/
#define sensor (0x2A << 1)




int main(){

	byte th, tl;
	
	/*DDRD  = 0x01;*/
	/*PORTD = 0x00;*/

	usart_init();
	i2c_init();

	/*センサーへの設定*/
	/*条件2:固定時間モードに設定*/

	i2c_start(sensor | TW_WRITE);	/*センサーへのデータ書き込み*/
	i2c_write(0x00);		/*コントロールバイトを指定*/
	i2c_write(0x89);		/*ADCリセット*/
	i2c_write(0b01010100);	/*アドレス再設定*/
	i2c_write(0x00);		/*コントロールバイトを指定*/
	i2c_write(0x09);		/*ADCリセット解除*/
	i2c_stop();

	_delay_ms(6);		/*センサの計算終了まで待機*/
	
	i2c_start(sensor | TW_WRITE);
	i2c_write(0b01010100);	/*アドレス再設定*/
	i2c_write(0x03);		/*出力データバイトを指定*/
	i2c_write(0b01010101);	/*リードモードに変更*/


	while(1){
		
		i2c_start(sensor | TW_READ);	/*センサー値受信*/
		th = i2c_read_ack();	/*上位バイトを読み込み*/
		tl = i2c_read_ack();	/*下位バイトを読み込み*/
		red = th<<1 | tl>>7;
		putch('R');
		putch(':');
		putdec2(red);			/*結果計算(上位+下位)*/
		putch(',');			

		th = i2c_read_ack();
		tl = i2c_read_ack();
		green = th<<1 | tl>>7;
		putch('G');
		putch(':');
		putdec2(green);
		putch(',');

		th = i2c_read_ack();
		tl = i2c_read_ack();
		blue = th<<1 | tl>>7;
		putch('B');
		putch(':');
		putdec2(blue);
		putch(',');

		th = i2c_read_ack();
		tl = i2c_read_nak();	/*nakを返信*/
		ray = th<<4 | tl>>4;
		putch('r');
		putch(':');
		putdec2(ray);

		putch(0x0d);	/*改行*/
		putch(0x0a);
		
		i2c_stop();
	}
}



Re: AVRとカラーセンサーのI2C接続について

Posted: 2015年8月22日(土) 22:27
by みけCAT
以下、unsigned char型のサイズが8ビットだという推測に基づいています。間違っていたらごめんなさい。
  • このセンサは16ビットの値が取れるようなのに、なぜデータを入れるred,green,blue,rayを8ビットしか確保していないのですか?
  • なぜputdec2関数の引数のサイズを8ビットに落としてしまったのですか?
  • そもそもputdec2関数はなぜ8ビット(符号なし整数で0~255)には余り、
    16ビット(符号なし整数で0~65535)には足りない十進数4桁を出力する仕様なのですか?
  • red,th,tlは全て8ビットなので、red = th<<1 | tl>>7という式はセンサの値[14:7]を取得しています。
    なぜこの範囲なのでしょうか?(少なくともデバッグ/テスト時は)素直に結果の全ビットを出力してみるべきではないのですか?
    green,blueも同様です。rayはセンサの値[11:4]を取得していますが、他は同様です。
  • 167行目「アドレス再設定」において、資料にあるRestart conditionが無いようですが、大丈夫なのでしょうか?
  • 175行目「アドレス再設定」および177行目「リードモードに変更」は蛇足で、消していいのではないでしょうか?
  • 2回目(whileの2周目)以降にデータを読み出す時、読み込み開始指示(174行目と176行目)を送信する必要は無いのでしょうか?
オフトピック
Datasheet ATmega48A/PA/88A/PA/168A/PA/328/P Complete (配布ページ)によると、
Start conditionとRestart conditionの作り方は同じっぽい?
208ページ さんが書きました: A
special case occurs when a new START condition is issued between a START and STOP condition. This is
referred to as a REPEATED START condition, and is used when the Master wishes to initiate a new transfer
without relinquishing control of the bus. After a REPEATED START, the bus is considered busy until the next
STOP. This is identical to the START behavior, and therefore START is used to describe both START and
REPEATED START for the remainder of this datasheet, unless otherwise noted.

Re: AVRとカラーセンサーのI2C接続について

Posted: 2015年8月23日(日) 14:53
by keysumer
コメントありがとうございます。

現在指摘していただいた点の修正をおこなっています。

また、今回のプログラムは、下記のサイトのものを参考に作成したものになります。

http://blog.goo.ne.jp/sim00/e/f4ab89945 ... 2d157d581b

Re: AVRとカラーセンサーのI2C接続について

Posted: 2015年8月23日(日) 15:32
by みけCAT
keysumer さんが書きました:また、今回のプログラムは、下記のサイトのものを参考に作成したものになります。

http://blog.goo.ne.jp/sim00/e/f4ab89945 ... 2d157d581b
このサイトでは
  • putdec2関数は8ビット(と予想)の引数を受け取り、2桁出力している。コメントで100未満のみ対応と明記している。
  • 使用しているLM73の仕様により、温度が非負の場合は結果[14:7]に温度の整数部分が格納される。
ということを踏まえ、きちんと実装されているようですね、
一方、ここで提示されたプログラムは、
  • 前述の通りputdec2関数の仕様が中途半端である。
  • センサは16ビット精度であり、結果[14:7]に特別な意味は無い。
よって、不適切な実装であると考えられます。

Re: AVRとカラーセンサーのI2C接続について

Posted: 2015年8月23日(日) 16:09
by keysumer
コメントありがとうございます。
なるほど、少しずつですが理解してきました。

以下の様に修正を加えてみたのですが、また一度見ていただいてもよろしいでしょうか。

コード:

#include<avr/io.h>
#include<util/twi.h>
#include<util/delay.h>

typedef unsigned int byte;

/*static unsigned int i=0;*/

unsigned int red   = 0;		/*赤色値格納用*/
unsigned int green = 0;		/*緑色値格納用*/
unsigned int blue  = 0;		/*青色値格納用*/
unsigned int ray   = 0;		/*赤外線値格納用*/


/////////////////////////USART///////////////////////

byte getch(void){

	while(!(UCSR0A & _BV(RXC0)));
	return UDR0;
}

void putch(byte c)
{

	while(!(UCSR0A & _BV(UDRE0)));
	UDR0 = c;
}

/*USART通信初期化用関数*/
void usart_init(void){

	UBRR0  = 25;
	
	UCSR0A = 0b00100000;	
	UCSR0C = 0b00000110;
	UCSR0B = 0b00011000;	
}


/////////////////////////I2C///////////////////////
/*16進数1桁計算用関数*/
void puthex1(byte d){

	d += '0';
	if(d>'9') d+='a'-'9'-1;
	putch(d);

}

/*16進数2桁計算用関数*/
void puthex2(byte d){

	puthex1(d>>4);
	puthex1(d & 0x0F);

}

/*結果計算用関数*/
void putdec2(byte x){

	putch('0' + (x/1000)%10);
	putch('0' + (x/100)%10);
	putch('0' + (x/10)%10);
	putch('0' + x%10);

}

/*エラー処理用関数*/
void i2c_error(void){

	putch('[');
	puthex2(TWSR);
	putch(']');
	putch(0x0d);
	putch(0x0a);

	while(1);
}

/*I2C初期化用関数(通信速度)*/
void i2c_init(void){

	TWSR = 0x00;	/*分周値=1*/
	TWBR = 32;		/*100k = 8MHz / (16+2*TWBR*分周値)*/
}

/*master側:1byte送信用関数*/
void i2c_write(byte d){

	TWDR = d;		/*送信データ*/
	TWCR = _BV(TWINT) | _BV(TWEN);	/**/
	while(!(TWCR & _BV(TWINT)));	/*データ送信完了まで待機*/
	if((TWSR & TW_STATUS_MASK) != TW_MT_DATA_ACK)	/*ACKを送れない場合*/
	i2c_error();
}

/*master側:1byte受信用関数(+ack返信)*/
byte i2c_read_ack(void){

	TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWEA);
	while(!(TWCR & _BV(TWINT)));	/*データ受信完了まで待機*/
	if((TWSR & TW_STATUS_MASK) != TW_MR_DATA_ACK)	/*ACKが返ってこない場合*/
	i2c_error();
	return TWDR;	/*データを送り返す*/
}

/*master側:1byte受信用関数(+noack返信)*/
byte i2c_read_nak(void){

	TWCR =_BV(TWINT) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));	/*データ受信完了まで待機*/
	if((TWSR & TW_STATUS_MASK) != TW_MR_DATA_NACK)	/*NACKが返ってこない場合*/
	i2c_error();
	return TWDR;	/*データを送り返す*/
}

/*master側:送信開始用関数*/
void i2c_start(byte id){

	byte s;

	/*開始条件送信*/
	TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));		/*開始条件の送信完了まで待機*/
	s = TWSR & TW_STATUS_MASK;
	if(s != TW_START && s != TW_REP_START) i2c_error();

	/*アドレス送信*/
	TWDR = id;
	TWCR = _BV(TWINT) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));	/*アドレスの送信完了まで待機*/
	s = TWSR & TW_STATUS_MASK;
	if(s != TW_MT_SLA_ACK && s != TW_MR_SLA_ACK) i2c_error();
}

/*master側:送信終了用関数*/
void i2c_stop(void){

	TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
	while(!(TWCR & _BV(TWSTO)));	/*送信終了になるまで待機*/
}


/*スレーブ(センサー)のアドレス設定*/
#define sensor (0x2A << 1)




int main(){

	byte th, tl;
	
	/*DDRD  = 0x01;*/
	/*PORTD = 0x00;*/

	usart_init();
	i2c_init();

	/*センサーへの設定*/
	/*条件2:固定時間モードに設定*/

	i2c_start(sensor | TW_WRITE);	/*センサーへのデータ書き込み*/
	i2c_write(0x00);		/*コントロールバイトを指定*/
	i2c_write(0x89);		/*ADCリセット*/
	i2c_write(sensor | TW_REP_START);	/*アドレス再設定*/
	i2c_write(0x00);		/*コントロールバイトを指定*/
	i2c_write(0x09);		/*ADCリセット解除*/
	i2c_stop();

	_delay_ms(6);		/*センサの計算終了まで待機*/
	
	//i2c_start(sensor | TW_WRITE);
	//i2c_write(0x03);		/*出力データバイトを指定*/
	//i2c_write(0b01010101 | TW_REP_START);	/*リードモードに変更*/


	while(1){

	i2c_start(sensor | TW_WRITE);
	i2c_write(0x03);		/*出力データバイトを指定*/
	i2c_write(0b01010101 | TW_REP_START);	/*リードモードに変更*/

		i2c_start(sensor | TW_READ);	/*センサー値受信*/
		th = i2c_read_ack();	/*上位バイトを読み込み*/
		tl = i2c_read_ack();	/*下位バイトを読み込み*/
		red = th | tl;
		putch('R');
		putch(':');
		putdec2(red);			/*結果計算(上位+下位)*/
		putch(',');			

		th = i2c_read_ack();
		tl = i2c_read_ack();
		green = th | tl;
		putch('G');
		putch(':');
		putdec2(green);
		putch(',');

		th = i2c_read_ack();
		tl = i2c_read_ack();
		blue = th | tl;
		putch('B');
		putch(':');
		putdec2(blue);
		putch(',');

		th = i2c_read_ack();
		tl = i2c_read_nak();
		ray = th | tl;
		putch('r');
		putch(':');
		putdec2(ray);

		putch(0x0d);
		putch(0x0a);

		i2c_stop();
	}
}



Re: AVRとカラーセンサーのI2C接続について

Posted: 2015年8月23日(日) 16:20
by みけCAT
  • putdec2の桁数が中途半端なのは改善されていないですね。
  • データシートと比べて、183行目の「リードモードに変更」は蛇足であり、削除するべきだと思います。
  • th | tlの値にはあまり意味が無い気がします。素直にth<<8 | tlとしてください。

Re: AVRとカラーセンサーのI2C接続について

Posted: 2015年8月23日(日) 16:27
by keysumer
コメントありがとうございます。
putdec2の桁数を変更するということは、以下の部分を変更するということでしょうか。

コード:

/*結果計算用関数*/
void putdec2(byte x){

	putch('0' + (x/1000)%10);
	putch('0' + (x/100)%10);
	putch('0' + (x/10)%10);
	putch('0' + x%10);

}

Re: AVRとカラーセンサーのI2C接続について

Posted: 2015年8月23日(日) 16:40
by みけCAT
keysumer さんが書きました:putdec2の桁数を変更するということは、以下の部分を変更するということでしょうか。

コード:

/*結果計算用関数*/
void putdec2(byte x){

	putch('0' + (x/1000)%10);
	putch('0' + (x/100)%10);
	putch('0' + (x/10)%10);
	putch('0' + x%10);

}
はい。

Re: AVRとカラーセンサーのI2C接続について

Posted: 2015年8月23日(日) 16:45
by keysumer
コメントありがとうございます。
byteを、unsigned int からunsigned short(0~65535)に変更し、
putdec2関数を含め、以下の様に変更しました。

コード:

#include<avr/io.h>
#include<util/twi.h>
#include<util/delay.h>

typedef unsigned short byte;	/*0~65535*/

/*static unsigned int i=0;*/

unsigned int red   = 0;		/*赤色値格納用*/
unsigned int green = 0;		/*緑色値格納用*/
unsigned int blue  = 0;		/*青色値格納用*/
unsigned int ray   = 0;		/*赤外線値格納用*/


/////////////////////////USART///////////////////////

byte getch(void){

	while(!(UCSR0A & _BV(RXC0)));
	return UDR0;
}

void putch(byte c)
{

	while(!(UCSR0A & _BV(UDRE0)));
	UDR0 = c;
}

/*USART通信初期化用関数*/
void usart_init(void){

	UBRR0  = 25;
	
	UCSR0A = 0b00100000;	
	UCSR0C = 0b00000110;
	UCSR0B = 0b00011000;	
}


/////////////////////////I2C///////////////////////
/*16進数1桁計算用関数*/
void puthex1(byte d){

	d += '0';
	if(d>'9') d+='a'-'9'-1;
	putch(d);

}

/*16進数2桁計算用関数*/
void puthex2(byte d){

	puthex1(d>>4);
	puthex1(d & 0x0F);

}

/*結果計算用関数*/
void putdec2(byte x){

	putch('0' + (x/10000)%10);
	putch('0' + (x/1000)%10);
	putch('0' + (x/100)%10);
	putch('0' + (x/10)%10);
	putch('0' + x%10);

}

/*エラー処理用関数*/
void i2c_error(void){

	putch('[');
	puthex2(TWSR);
	putch(']');
	putch(0x0d);
	putch(0x0a);

	while(1);
}

/*I2C初期化用関数(通信速度)*/
void i2c_init(void){

	TWSR = 0x00;	/*分周値=1*/
	TWBR = 32;		/*100k = 8MHz / (16+2*TWBR*分周値)*/
}

/*master側:1byte送信用関数*/
void i2c_write(byte d){

	TWDR = d;		/*送信データ*/
	TWCR = _BV(TWINT) | _BV(TWEN);	/**/
	while(!(TWCR & _BV(TWINT)));	/*データ送信完了まで待機*/
	if((TWSR & TW_STATUS_MASK) != TW_MT_DATA_ACK)	/*ACKを送れない場合*/
	i2c_error();
}

/*master側:1byte受信用関数(+ack返信)*/
byte i2c_read_ack(void){

	TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWEA);
	while(!(TWCR & _BV(TWINT)));	/*データ受信完了まで待機*/
	if((TWSR & TW_STATUS_MASK) != TW_MR_DATA_ACK)	/*ACKが返ってこない場合*/
	i2c_error();
	return TWDR;	/*データを送り返す*/
}

/*master側:1byte受信用関数(+noack返信)*/
byte i2c_read_nak(void){

	TWCR =_BV(TWINT) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));	/*データ受信完了まで待機*/
	if((TWSR & TW_STATUS_MASK) != TW_MR_DATA_NACK)	/*NACKが返ってこない場合*/
	i2c_error();
	return TWDR;	/*データを送り返す*/
}

/*master側:送信開始用関数*/
void i2c_start(byte id){

	byte s;

	/*開始条件送信*/
	TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));		/*開始条件の送信完了まで待機*/
	s = TWSR & TW_STATUS_MASK;
	if(s != TW_START && s != TW_REP_START) i2c_error();

	/*アドレス送信*/
	TWDR = id;
	TWCR = _BV(TWINT) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));	/*アドレスの送信完了まで待機*/
	s = TWSR & TW_STATUS_MASK;
	if(s != TW_MT_SLA_ACK && s != TW_MR_SLA_ACK) i2c_error();
}

/*master側:送信終了用関数*/
void i2c_stop(void){

	TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
	while(!(TWCR & _BV(TWSTO)));	/*送信終了になるまで待機*/
}


/*スレーブ(センサー)のアドレス設定*/
#define sensor (0x2A << 1)




int main(){

	byte th, tl;
	
	/*DDRD  = 0x01;*/
	/*PORTD = 0x00;*/

	usart_init();
	i2c_init();

	/*センサーへの設定*/
	/*条件2:固定時間モードに設定*/

	i2c_start(sensor | TW_WRITE);	/*センサーへのデータ書き込み*/
	i2c_write(0x00);		/*コントロールバイトを指定*/
	i2c_write(0x89);		/*ADCリセット*/
	i2c_write(sensor | TW_REP_START);	/*アドレス再設定*/
	i2c_write(0x00);		/*コントロールバイトを指定*/
	i2c_write(0x09);		/*ADCリセット解除*/
	i2c_stop();

	_delay_ms(6);		/*センサの計算終了まで待機*/
	
	//i2c_start(sensor | TW_WRITE);
	//i2c_write(0x03);		/*出力データバイトを指定*/
	//i2c_write(0b01010101 | TW_REP_START);	/*リードモードに変更*/


	while(1){

	i2c_start(sensor | TW_WRITE);
	i2c_write(0x03);		/*出力データバイトを指定*/

		i2c_start(sensor | TW_READ);	/*センサー値受信*/
		th = i2c_read_ack();	/*上位バイトを読み込み*/
		tl = i2c_read_ack();	/*下位バイトを読み込み*/
		red = th<<8 | tl;
		putch('R');
		putch(':');
		putdec2(red);			/*結果計算(上位+下位)*/
		putch(',');			

		th = i2c_read_ack();
		tl = i2c_read_ack();
		green = th<<8 | tl;
		putch('G');
		putch(':');
		putdec2(green);
		putch(',');

		th = i2c_read_ack();
		tl = i2c_read_ack();
		blue = th<<8 | tl;
		putch('B');
		putch(':');
		putdec2(blue);
		putch(',');

		th = i2c_read_ack();
		tl = i2c_read_nak();
		ray = th<<8 | tl;
		putch('r');
		putch(':');
		putdec2(ray);

		putch(0x0d);
		putch(0x0a);

		i2c_stop();
	}
}



Re: AVRとカラーセンサーのI2C接続について

Posted: 2015年8月23日(日) 17:00
by みけCAT
keysumer さんが書きました:byteを、unsigned int からunsigned short(0~65535)に変更し、
putdec2関数を含め、以下の様に変更しました。
それで、実行結果はどうでしたか?

Re: AVRとカラーセンサーのI2C接続について

Posted: 2015年8月23日(日) 17:16
by keysumer
今までのように、0が連続して表示されるなどの挙動はなくなりました。
それぞれの値が、
R:016384,G:004096,B:008192,r:004096
の様に表示されています。

しかし、この値から一向に変動しない状態で、同じ値が出力され続けています。

Re: AVRとカラーセンサーのI2C接続について

Posted: 2015年8月23日(日) 21:22
by keysumer
みけCATさん、
何度も質問に丁寧にお答えいただきありがとうございました。

自分でコードを何度も確認したところ、
1度目のReStartの部分の記述を
i2c_startではなく、i2c_writeにしていました。
この部分を変更したところ、無事値が読めるようになりました!

本当にありがとうございました!最後に今回のコードを載せておきます。

コード:

#include<avr/io.h>
#include<util/twi.h>
#include<util/delay.h>

typedef unsigned short byte;	/*0~65535*/

/*static unsigned int i=0;*/

unsigned int red   = 0;		/*赤色値格納用*/
unsigned int green = 0;		/*緑色値格納用*/
unsigned int blue  = 0;		/*青色値格納用*/
unsigned int ray   = 0;		/*赤外線値格納用*/


/////////////////////////USART///////////////////////

byte getch(void){

	while(!(UCSR0A & _BV(RXC0)));
	return UDR0;
}

void putch(byte c)
{

	while(!(UCSR0A & _BV(UDRE0)));
	UDR0 = c;
}

/*USART通信初期化用関数*/
void usart_init(void){

	UBRR0  = 25;
	
	UCSR0A = 0b00100000;	
	UCSR0C = 0b00000110;
	UCSR0B = 0b00011000;	
}


/////////////////////////I2C///////////////////////
/*16進数1桁計算用関数*/
void puthex1(byte d){

	d += '0';
	if(d>'9') d+='a'-'9'-1;
	putch(d);

}

/*16進数2桁計算用関数*/
void puthex2(byte d){

	puthex1(d>>4);
	puthex1(d & 0x0F);

}

/*結果計算用関数*/
void putdec2(byte x){

	putch('0' + (x/10000)%10);
	putch('0' + (x/1000)%10);
	putch('0' + (x/100)%10);
	putch('0' + (x/10)%10);
	putch('0' + x%10);

}

/*エラー処理用関数*/
void i2c_error(void){

	putch('[');
	puthex2(TWSR);
	putch(']');
	putch(0x0d);
	putch(0x0a);

	while(1);
}

/*I2C初期化用関数(通信速度)*/
void i2c_init(void){

	TWSR = 0x00;	/*分周値=1*/
	TWBR = 32;		/*100k = 8MHz / (16+2*TWBR*分周値)*/
}

/*master側:1byte送信用関数*/
void i2c_write(byte d){

	TWDR = d;		/*送信データ*/
	TWCR = _BV(TWINT) | _BV(TWEN);	/**/
	while(!(TWCR & _BV(TWINT)));	/*データ送信完了まで待機*/
	if((TWSR & TW_STATUS_MASK) != TW_MT_DATA_ACK)	/*ACKを送れない場合*/
	i2c_error();
}

/*master側:1byte受信用関数(+ack返信)*/
byte i2c_read_ack(void){

	TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWEA);
	while(!(TWCR & _BV(TWINT)));	/*データ受信完了まで待機*/
	if((TWSR & TW_STATUS_MASK) != TW_MR_DATA_ACK)	/*ACKが返ってこない場合*/
	i2c_error();
	return TWDR;	/*データを送り返す*/
}

/*master側:1byte受信用関数(+noack返信)*/
byte i2c_read_nak(void){

	TWCR =_BV(TWINT) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));	/*データ受信完了まで待機*/
	if((TWSR & TW_STATUS_MASK) != TW_MR_DATA_NACK)	/*NACKが返ってこない場合*/
	i2c_error();
	return TWDR;	/*データを送り返す*/
}

/*master側:送信開始用関数*/
void i2c_start(byte id){

	byte s;

	/*開始条件送信*/
	TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));		/*開始条件の送信完了まで待機*/
	s = TWSR & TW_STATUS_MASK;
	if(s != TW_START && s != TW_REP_START) i2c_error();

	/*アドレス送信*/
	TWDR = id;
	TWCR = _BV(TWINT) | _BV(TWEN);
	while(!(TWCR & _BV(TWINT)));	/*アドレスの送信完了まで待機*/
	s = TWSR & TW_STATUS_MASK;
	if(s != TW_MT_SLA_ACK && s != TW_MR_SLA_ACK) i2c_error();
}

/*master側:送信終了用関数*/
void i2c_stop(void){

	TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
	while(!(TWCR & _BV(TWSTO)));	/*送信終了になるまで待機*/
}


/*スレーブ(センサー)のアドレス設定*/
#define sensor (0x2A << 1)




int main(){

	byte th, tl;
	
	/*DDRD  = 0x01;*/
	/*PORTD = 0x00;*/

	usart_init();
	i2c_init();

	/*センサーへの設定*/
	/*条件2:固定時間モードに設定*/




	while(1){

		i2c_start(sensor | TW_WRITE);	/*センサーへのデータ書き込み*/
		i2c_write(0x00);		/*コントロールバイトを指定*/
		i2c_write(0x89);		/*ADCリセット*/
		i2c_start(sensor | TW_REP_START);	/*アドレス再設定*/
		i2c_write(0x00);		/*コントロールバイトを指定*/
		i2c_write(0x09);		/*ADCリセット解除*/
		i2c_stop();

		_delay_ms(6);		/*センサの計算終了まで待機*/


		i2c_start(sensor | TW_WRITE);
		i2c_write(0x03);		/*出力データバイトを指定*/

		i2c_start(0b01010101 | TW_REP_START);	/*センサー値受信*/
		th = i2c_read_ack();	/*上位バイトを読み込み*/
		tl = i2c_read_ack();	/*下位バイトを読み込み*/
		red = th<<8 | tl;
		putch('R');
		putch(':');
		putdec2(red);			/*結果計算(上位+下位)*/
		putch(',');			

		th = i2c_read_ack();
		tl = i2c_read_ack();
		green = th<<8 | tl;
		putch('G');
		putch(':');
		putdec2(green);
		putch(',');

		th = i2c_read_ack();
		tl = i2c_read_ack();
		blue = th<<8 | tl;
		putch('B');
		putch(':');
		putdec2(blue);
		putch(',');

		th = i2c_read_ack();
		tl = i2c_read_nak();
		ray = th<<8 | tl;
		putch('r');
		putch(':');
		putdec2(ray);

		putch(0x0d);
		putch(0x0a);

		i2c_stop();;
	}
}



Re: AVRとカラーセンサーのI2C接続について

Posted: 2015年8月23日(日) 22:05
by みけCAT
keysumer さんが書きました:自分でコードを何度も確認したところ、
1度目のReStartの部分の記述を
i2c_startではなく、i2c_writeにしていました。
この部分を変更したところ、無事値が読めるようになりました!
解決したのでしたら、解決チェックをお願いします。
返信画面の「送信」ボタンの右にある「解決!」にチェックを入れた状態で返信を投稿してください。

Re: AVRとカラーセンサーのI2C接続について

Posted: 2015年8月24日(月) 09:02
by keysumer
無事解決いたしました。
ありがとうございます!