/**
 * \file i2c_pcf8570.c
 * \brief I2Cサブシステムのサンプルプログラム
 * \details
 * subsystems ディレクトリ中のライブラリを使うためのサンプルプログラム。
 */

/**
 * \mainpage
 * \author Takemasa Nakamura
 * \date 2011/May/6t
 * \details
 * subsystems ディレクトリ中のI2Cライブラリを使うデモプログラム。
 *
 * 以下の開発環境を使用して検証した
 *　\li LPCXpresso IDE 3.8.2 [build 129]
 *　\li ubuntu 10.04
 *
 * 起動すると、I2Cバスに接続しているPCF8570(I2C SRAM)への読み書きを行い、結果を
 * UARTから出力する。
 *
 * 同じworkspaceに、cmsisアーカイブプロジェクトとsubsystemsアーカイブプロジェクトが
 * 必要である。
 */
#include <kernel.h>
#include <t_syslog.h>
#include <t_stdlib.h>
#include "kernel_cfg.h"
#include "app.h"

/**
 * \brief I2C0, I2C1, I2C2のどのポートを使うか指定する
 * \details
 * 0から2のいずれかを選ぶ。このマクロは main() 関数のなかで使用され、I2Cに割り当てる
 * ピンを決定するために使う。
 */
#define I2CPORT	0

/**
 * \brief サービスコールのエラーのログ出力
 * \details
 * TOPPERS/ASPのsample 1からそのまま流用している。エラーコードが非0のとき、
 * ファイル名、行番号、式とエラーコードを出力する便利なプログラム。 SVC_PERROR マクロで
 * くるんで使う。
 */
Inline void
svc_perror(const char *file, int_t line, const char *expr, ER ercd)
{
	if (ercd < 0) {
		t_perror(LOG_ERROR, file, line, expr, ercd);
	}
}

/**
 * \brief サービスコールのエラーのログ出力
 * \details
 * TOPPERS/ASPのsample 1からそのまま流用している。エラーコードが非0のとき、
 * ファイル名、行番号、式とエラーコードを出力する便利なプログラム。
 */
#define	SVC_PERROR(expr)	svc_perror(__FILE__, __LINE__, #expr, (expr))

	/**
	 * \brief LPCXpresso 1768の基板上のLEDを指定するビットマスク
	 * \details
	 *  LPCXpresso 1768/1769では、GPIO0のbit22がLEDに接続されている
	 */
#define LEDMASK	(1<<22)
	/**
	 * \brief PCF8570の I2C スレーブアドレス
	 * \details
	 * 7bitで表したPCF8570のスレーブアドレス。アドレスはストラップ・ピンの状態により
	 * 変更できるが、このサンプルでは、A0, A1ピンはGNDに続されていると仮定している。
	 * */
#define PCF8570	0x50	/* 7bit address */

/**
 * \brief i2c_master_write() 関数に渡す送信データバッファ
 * \details
 * i2c ポートから PCF5870に送信されるデータを格納しておくバッファ。
 * 最初の1バイトはPCF5870の要請により、RAM内部のアドレスを表す。続くNバイトが
 * そのアドレスから連続して書き込まれる。
 */
unsigned char	tx_data[] = {12, 0xDE, 0xAD};	// address= 12, 2 data
/**
 * \brief i2c_master_write_read() 関数に渡す受信データバッファ
 * \details
 * i2c ポートを通してPCF5870から受信されるデータを格納しておくバッファ。
 * 格納されるのはすべてデータである。
 */
unsigned char	rx_data[] = {0,0 };	// 2 data

/**
 * \brief メインタスク
 */
void main_task(intptr_t exinf)
{
	PINSEL_CFG_Type PinCfg;
	int i = 0;

	/* I2C0 ポートにピンを割り当てる */
#if ( I2CPORT == 0 )
		// I2C0 出力ピンの設定
	PinCfg.OpenDrain = PINSEL_PINMODE_NORMAL;
	PinCfg.Pinmode = PINSEL_PINMODE_PULLUP;
	PinCfg.Funcnum = PINSEL_FUNC_1;
		// ポート0
	PinCfg.Portnum = 0;
		// ピン27と28
	PinCfg.Pinnum = 27;
	PINSEL_ConfigPin(&PinCfg);
	PinCfg.Pinnum = 28;
	PINSEL_ConfigPin(&PinCfg);
#elif  ( I2CPORT == 1 )
		// I2C1 出力ピンの設定
	PinCfg.OpenDrain = PINSEL_PINMODE_OPENDRAIN; // OpenDrain
	PinCfg.Pinmode = PINSEL_PINMODE_TRISTATE;	// neither pull up nor pull down
	PinCfg.Funcnum = PINSEL_FUNC_3;
		// ポート0
	PinCfg.Portnum = 0;
		// ピン0と1
	PinCfg.Pinnum = 0;
	PINSEL_ConfigPin(&PinCfg);
	PinCfg.Pinnum = 1;
	PINSEL_ConfigPin(&PinCfg);
#elif  ( I2CPORT == 2 )
	// I2C2 出力ピンの設定
	PinCfg.OpenDrain = PINSEL_PINMODE_OPENDRAIN; // OpenDrain
	PinCfg.Pinmode = PINSEL_PINMODE_TRISTATE;	// neither pull up nor pull down
	PinCfg.Funcnum = PINSEL_FUNC_2;
	// ポート0
	PinCfg.Portnum = 0;
	// ピン10と11
	PinCfg.Pinnum = 10;
	PINSEL_ConfigPin(&PinCfg);
	PinCfg.Pinnum = 11;
	PINSEL_ConfigPin(&PinCfg);
#else
#error "Wrong Configuration of I2C"
#endif


	syslog(LOG_NOTICE, "I2C test program with PCF8570 starts." );
	syslog(LOG_NOTICE, "Try to write incremental value and compare with read value.");



		/* GPIO0のLED制御ピンを出力にする */
	GPIO_SetDir(
			0,			// GPIOポート番号
			LEDMASK,	// 操作するビットマスク
			1			// 設定する値
			);

		/* 1秒周期で LPCXpressoのLEDを点滅させつつ、I2C SRAMに読み書きを行う */
	while(1)
	{
		tslp_tsk(100);
			// LEDを点灯
		GPIO_ClearValue(
					0,			// GPIOポート番号
					LEDMASK	// 操作するビットマスク
					);

		tx_data[2] = i++;
			/* I2C SRAMにデータを書き込む */
		i2c_master_write(
					I2CPORT,	// i2c peripheral #. I2C0 is 0
					PCF8570,	// Slave address by 7bits
					tx_data,	// transmit data
					sizeof(tx_data)	// number of bytes to transmit
					);
			/* I2C_SRAMからデータを読み込む */
		i2c_master_write_read(
					I2CPORT,	// i2c peripheral #. I2C0 is 0
					PCF8570,	// Slave address by 7bits
					tx_data,	// transmit data
					1,			// number of bytes to transmit
					rx_data,	// receive buffer
					sizeof(rx_data)	// number of bytes to receive
					);

			/* 読み書きが正しければ、結果は書込み値と同じになる */
		syslog(LOG_NOTICE, "written data is %d, read data is %d", tx_data[2], rx_data[1]);

		tslp_tsk(900);
			// LEDを消灯
		GPIO_SetValue(
					0,			// GPIOポート番号
					LEDMASK	// 操作するビットマスク
					);

	}


	syslog(LOG_NOTICE, "Sample program ends.");
	SVC_PERROR(ext_ker());
	assert(0);
}
