RTCサンプル

RTCモジュールとI2Cで通信して時計を作る。
I2Cは前回よくわかってないって書いたけど、前よりはちょっと理解した。

USARTは、2本の線を使って、1本が送信、1本が受信の役割をするけど、I2Cは同じ二本でも、1本がクロック、1本がデータになっていて、複数の機器を制御できるバス通信らしい。
複数の機器は、それぞれがマスターにもスレーブにもなれて、それぞれの状態で送信受信ができるらしい。

AVRと秋月のRTCモジュールで、このI2Cという方式でデータのやり取りをすることにより、時刻の設定、読み出し等が可能になると。

回路図

前回書いたように、UART(USART)はRXD(PD0)とTXD(PD1)をクロスに繋いだけど、I2CはSCL(PC5)とSDA(PC4)は、それぞれ同じもの同士を繋ぐ。

SCLとSDAは、RTCモジュールのジャンパをショートすれば、内部でプルアップ抵抗が有効になるらしいけど、なんとなくそのままにしておきたかったので、自前で内部に持っているのと同じ2.2kΩの抵抗をつけてあります。
プルアップ抵抗値の決め方とかも、理由があるんだろうけど、まだよくわかってません。

RTCサンプル

ソースコード

とりあえず、読み出しだけはできるようになったデバッグコード。

#include <stdio.h>
#include "LcdLib.h"
#include "UsartLib.h"
#include "util.h"

#define RTC_ADDR_WRITE 0xA2
#define RTC_ADDR_READ  0xA3

#define I2C_BPS	2	// ボーレート設定値

// TWIモード列挙型定義
typedef enum _tagTWIMode{
	TWI_MODE_SEND,		// 送信モード
	TWI_MODE_RECEIVE,	// 受信モード
} TWIMode;
typedef enum _tagTWIACKMode{
	TWI_ACKMODE_ACK,	// ACK送信モード
	TWI_ACKMODE_NACK,	// NACK送信モード
} TWIACKMode;

void I2c_init( void )
{
	TWSR &= ~0x03;		// BPSの前置分周は等倍にする
	TWBR = I2C_BPS;		// CPUクロックが1MHzなのに対して、50KHzで通信する
	TWCR = 0b00000100;	// ピンをTWI(SCL/SDA)として使用する
}

int8_t I2c_start( void )
{
	TWCR = 0b10100100;	// スタート設定
	while( !( TWCR & 0b10000000 )){
		;	// 送信開始OKになるまで待つ
	}

	if( 0x08 == ( TWSR & 0xF8 )){
		return 0;
	} else if( 0x10 == ( TWSR & 0xF8 )){
		return 1;
	}
	return -1;
}

int8_t I2c_setaddr( uint8_t addr, TWIMode mode )
{
	TWDR = addr;
	TWCR = 0b10000100;	// 送信設定
	while( !( TWCR & 0b10000000 )){
		;	// 相手の応答まで待つ
	}
	if( TWI_MODE_SEND == mode ){
		if( 0x18 == ( TWSR & 0xF8 )){
			return 0;
		} else if( 0x20 == ( TWSR & 0xF8 )){
			return 1;
		}
	} else {
		if( 0x40 == ( TWSR & 0xF8 )){
			return 0;
		} else if( 0x48 == ( TWSR & 0xF8 )){
			return 1;
		}
	}
	return -1;
}

int8_t I2c_snddata( uint8_t data )
{
	TWDR = data;
	TWCR = 0b10000100;	// 送信設定
	while( !( TWCR & 0b10000000 )){
		;	// 相手の応答まで待つ
	}
	if( 0x28 == ( TWSR & 0xF8 )){
		return 0;
	} else if( 0x30 == ( TWSR & 0xF8 )){
		return 1;
	}
	return -1;
}

int8_t I2c_rcvdata( uint8_t *data, TWIACKMode mode )
{
	if( TWI_ACKMODE_ACK == mode ){
		TWCR = 0b11000100;
	} else {
		TWCR = 0b10000100;
	}
	while( !( TWCR & 0b10000000 )){
		;	// データを受信するまで待つ
	}
	if( 0x50 == ( TWSR & 0xF8 )){
		*data = TWDR;
		return 0;
	} else if( 0x58 == ( TWSR & 0xF8 )){
		*data = TWDR;
		return 1;
	}
	return -1;
}

void I2c_stop( void )
{
	TWCR = 0b10010100;	// ストップ設定
}

int main( void )
{
	uint8_t data[16];	// 受信データ格納用
	uint8_t	i	= 0;	// ループ変数
	char str[5] = "  ";	// 16進コード格納用

	Lcd_init();						// LCD初期化
	Lcd_setstr( "Receive Data:" );	// 初期表示

	Usart_init();	// シリアル通信初期化
	I2c_init();		// I2C通信初期化

	while( 1 ){
		if( 0 > I2c_start() ){
			Usart_sndstr( "I2c_start_err_1" );
			continue;
		}
		if( 0 != I2c_setaddr( RTC_ADDR_WRITE, TWI_MODE_SEND )){
			Usart_sndstr( "I2c_setaddr(send)_err" );
			continue;
		}
		if( 0 != I2c_snddata( 0 )){
			Usart_sndstr( "I2c_snddata_err" );
			continue;
		}

		if( 0 > I2c_start() ){
			Usart_sndstr( "I2c_start_err_2" );
			continue;
		}
		if( 0 != I2c_setaddr( RTC_ADDR_READ, TWI_MODE_RECEIVE )){
			Usart_sndstr( "I2c_setaddr(receive)_err" );
			continue;
		}

		for( i = 0; i < 15; i++ ){
			if( 0 > I2c_rcvdata( &data[i], TWI_ACKMODE_ACK )) break;
		}
		if( 15 != i ){
			continue;
		}
		if( 0 > I2c_rcvdata( &data[i], TWI_ACKMODE_NACK )){
			continue;
		}
		I2c_stop();

		Lcd_setpos( 0, 0 );
		for( i = 0; i < 8; i++ ){
			sprintf( str, "%02X", data[i] );
			Lcd_setstr( str );
		}
		Lcd_setpos( 1, 0 );
		for( i = 8; i < 16; i++ ){
			sprintf( str, "%02X", data[i] );
			Lcd_setstr( str );
		}

		{
			uint8_t sec = data[2];
			uint8_t min = data[3];
			uint8_t hour = data[4];
			uint8_t day = data[5];
			uint8_t week = data[6];
			uint8_t month = data[7];
			uint8_t year = data[8];
			char date[24] = "                       ";
			char weekstr[7][4] = {
				"Sun",
				"Mon",
				"Tue",
				"Wed",
				"Thr",
				"Fri",
				"Sat",
			};
			sec = (( sec >> 4 ) & 0x07 ) * 10 + ( sec & 0x0F );
			min = (( min >> 4 ) & 0x07 ) * 10 + ( min & 0x0F );
			hour = (( hour >> 4 ) & 0x03 ) * 10 + ( hour & 0x0F );
			day = (( day >> 4 ) & 0x03 ) * 10 + ( day & 0x0F );
			week = week & 0x07;
			month = (( month >> 4 ) & 0x01 ) * 10 + ( month & 0x0F );
			year = (( year >> 4 ) & 0x0F ) * 10 + ( year & 0x0F );
			sprintf( date, "20%02d/%02d/%02d %02d:%02d:%02d %s", year, month, day, hour, min, sec,  weekstr[week] );
			Usart_sndstr( date );
		}
		delay_ms( 500 );
	}

	return 0;
}

基本動作


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS