본문 바로가기

Enginius/Firmware

PIC32 - I2C

 I2C는 Inter-Integrated Circuit의 약자이다. 
 (IIC하면 없어보이니까 I2C라고 한 것 같다.)
 이는 다른 말로 TWI라고도 하는데 이 것은 two-wire interface를 뜻한다. 말 그대로 두 개의 선을 이용해서 통신을 한다는 것이다. 클럭과 DATA라인, 포트 이름으로는 SDA와 SCL이 되겠다.

 통신 방법은 별 것 없다. 하나의 마스터가(일반적으로) 하나 혹은 여러 개의 슬레이브와 통신을 한다. eeprom의 경우 data를 읽고 쓸테고, 온도 센서의 경우 원하는 Data를 던제 줄 것이다. 

 자세한 프로토콜은 네이버에 치면 무지 많으니까 나중에 정리 하도록 하고 PIC32에서 초기화를 하고, 실제로 사용하는 소스를 설명한다. 이 소스에서는 24lc04와 bq20z70(스마트 배터리) 두 개와 통신을 한다.


I2C Control Registers

I2CxCON(x는 숫자)
 bit31-16
     Reserved
 bit15
     ON: I2C Enable bit
     1: I2C 를 사용한다.
     0: I2C 를 사용하지 않는다. 
 bit14
     FRZ: Freeze in DEBUG mode bit
     1: 디버그 모드에서 멈춘다.
     0: 디버그 모드에서도 계속 동작한다.
 bit13
     SIDL: Stop in IDLE Mode bit
     1: IDLE 상태일 때 멈춘다.
     0: IDLE 상태일 때에도 계속 동작한다.
 bit12
     SCLREL: SCL Release Control bit
     I2C Slave Mode 에서만 사용
 bit11
     STRICT: Strict I2C Reserved Address Rule enable
     1: 사용한다. (위에 것)
     0: 사용하지 않는다.
 bit10
     A10M: 10bit slave address flag bit
     1: I2CxADD가 10bit 주소를 갖는다.
     0: I2CxADD가 7bit 주소를 갖는다.
 bit9
     DISSLW: Slew Rate Control Disable bit
     1: Standard Speed Mode에서(100kHz), 1MHz에서도 사용
     0: High Speed Mode(400kHz)
 bit8
     SMEN: SMBus Input Levels Disable bit
     1: SMBus spec에 맞추도록 Vth를 바꾼다.
     0: 그냥 고
 bit7
     GCEN: General Call Enable bit
     I2C Slave에서만 사용
 bit6
     STREN: SCL Clock Stretch Enable bit
     I2C Slave에서만 사용
 bit5
     ACKDT: Acknowlege Data bit
     Data를 Rx하고 나서 어떤 bit를 보낼 지를 정해준다. 
     1: Nack를 보낸다.
     0: Ack를 보낸다. 
 bit4
     ACKEN: Acknowledge Sequence Enable bit
     1: ACKDT에 해당하는 bit를 보낸다. 
     0: 보내지 않는다. 
 bit3
     RCEN: Receive Enable bit
     1: 8-bit data를 받는다. 
     0: 받지 않는다. 
 bit2:
     PEN: Stop Condition Enable bit
     1: Stop을 보낸다. 
     0: 보내지 않는다. 
 bit1
     RSEN: Restart Condition Enable bit
     1: Restart를 보낸다.
     0: 보내지 않는다.
 bit0
     SEN: Start Condition Enable bit
     1: Start를 보낸다.
     0: 보내지 않는다.


I2CxSTAT(x는 숫자)
 bit31-16
     Reserved
 bit15
     ACKSTAT: Acknowledge Status bit
     1: Ack가 오지 않았다.
     0: Ack가 왔다.  
 bit14
     TRSTAT: Transmit Status bit
     1: 마스터가 보내고 있다.
     0: 보내고 있지 않다.   
 bit13-11
     Reserved
 bit110
     BCL: Master Bus Collision Detect bit
     I2CxCONBits.ON = 0 이 되어야 초기화 된다.
     1: 충돌이 생겼다.  
     0: 충돌이 생기지 않았다.  
 bit9
     GCSTAT: General Call Status bit
     1: General Call Address를 받았다.
     0: General Call Address를 받지않았다.
 bit8
     ADD10: 10-bit Address Status bit
     1: 10-bit address 가 match 되었다.
     0: 10-bit address 가 match 되지 않았다.
 bit7
     IWCOL: Write Collision Detect bit
     1: Write Collision 이 일어났다.
     0: 충돌이 일어나지 않았다.  
 bit6
     I2COV: I2C Receive Overflow Status bit
     1: Rx Overflow가 일어났다.  
     0: Overflow가 일어나지 않았다.   
 bit5
     D/A: Data/Address bit
     Slave에서만 동작.  
 bit4
     P: Stop bit
     1: Stop이 감지 되었다.
     0: Stop이 감지 되지 않았다.  
 bit3
     S: Start bit
     1: Start가 감지 되었다.
     0: Start가 감지 되지 않았다.  
 bit2
     R/W: Read Write Information bit
     Slave에서만 동작. 
 bit1
     RBF: Receive Buffer Full Status bit
     1: Receive 종료; I2CxRCV 가 full 
     0: Receive 가 끝나지 않았다; I2CxRCV 가 비어있다.   
 bit0
     TBF: Transmit Buffer Full Status bit
     1: Transmit이 끝나지 않았다; I2CxTRN 가 full 
     0: Transmit이 끝났다; I2CxTRN 가 비어있다.   


I2C로 eeprom과 smbus를 사용하고 이 두 개의 protocol은 약간 다르다.
자세한 spec은 인터넷을 참조하면 된다.
아래는 i2c의 초기화 함수와 실제 사용 함수이다.



< pic32_i2c.h >
#include 	/* PIC32 processor header file */
#include 		/* peripheral library header file */
#define True	1
#define False	0
#define ACK	0
#define NACK	1

#define Fsck    	50000			//50kHz
#define BRG_VAL     	(PBCLK / 2 / Fsck) -2

#define I2C_INT_ENABLE		IEC0bits.I2C1MIE
#define I2C_INT_FLAG		IFS0bits.I2C1MIF
#define I2C_BUS_BUSY		IFS0bits.I2C1BIF
#define I2C_TX_BUFFER 		I2C1TRN
#define I2C_RX_BUFFER		I2C1RCV
#define I2C_ACKNOWLEDGE		I2C1STATbits.ACKSTAT

#define I2C_EEPROM_ADDR		0xa0
#define I2C_BATTERY_ADDR	  	0x16	//bq20z90 address


// 배터리 Command 정의 
#define BATTERY_TEMPERATURE		0x08
#define BATTERY_VOLTAGE		0x09
#define BATTERY_CURRENT		0x0A
#define BATTERY_AVERAGE_CURRENT	0x0B
#define BATTERY_RSOC		0x0D	// relative state of charge
#define BATTERY_ASOC		0x0E
#define BATTERY_CHARGING_CURRENT	0x14
#define BATTERY_CHARGING_VOLTAGE	0x15
#define BATTERY_STATUS		0x16 

#define ADDR_8BIT	1
#define ADDR_16BIT	2

void Init_i2c();

void Reset_i2c();

void i2c_wait(unsigned int cnt);

unsigned char I2C_Write(unsigned char devId, unsigned int Addr, unsigned char numBytes, unsigned char* data, unsigned char type);

unsigned char I2C_Read(unsigned char devId, unsigned int Addr, unsigned char numBytes, unsigned char* data, unsigned char type);

unsigned char SMBus_Read(unsigned char devId, unsigned int Addr, unsigned char numBytes, unsigned int* data, unsigned char type);

 

< pic32_i2c.c >

#include "pic32_i2c.h"
#include "main.h"

unsigned char i2cReadByte = 0x00;
unsigned char I2C_INT = False;

unsigned char PEC_Table[256] = 
{
	0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 
	0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d,
	0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
	0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
	0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
	0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,	
	0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 
	0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, 
	0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 
	0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, 
	0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 
	0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, 
	0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 
	0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, 
	0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 
	0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, 
	0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 
	0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, 
	0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 
	0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, 
	0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 
	0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, 
	0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 
	0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, 
	0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 
	0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, 
	0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 
	0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 
	0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 
	0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, 
	0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 
	0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
};
void i2c_wait(unsigned int cnt)
{
	while(--cnt)	asm( "nop" );asm( "nop" );
}

void Init_i2c()
{
	unsigned int pbclk;
	pbclk = PBCLK;
	//PORT Init
//	_TRISA2 = 1 ;  
//	_TRISA3 = 1 ;

	//Register setting
	I2C1CON = 0x000;		
	I2C1CONbits.I2CEN = 1;
	I2C1CONbits.ON = 1;
	I2C1CONbits.DISSLW = 1;		
	I2C1STAT = 0x000;
	I2C1MSK = 0x000;

	I2C1BRG = (pbclk / (2 *Fsck)) - 2;
/*
	//Interrupt setting
	I2C_INT_FLAG = 0;
	I2C_INT_ENABLE = 0;
	//I2C Interrupt priority 3
	IPC6bits.I2C1IP=3;
//*/
}

unsigned char I2C_Done()
{
	//while(!I2C_INT_FLAG); I2C_INT_FLAG = 0;
	unsigned long long g_Tick2_prev = g_Tick2;
	for(;;)
	{
		if(I2C_INT_FLAG)
		{
			I2C_INT_FLAG = 0;
			break;
		}
		if(g_Tick2 - g_Tick2_prev > 2)	
		{
			myprintf("ERR @ I2C_Done() ");	return False;
			break;
		}
	}
	i2c_wait(2000);
	i2c_wait(2000);
	return True;
}

unsigned char I2C_Ack()
{
	if(I2C_ACKNOWLEDGE == ACK)	return True;
	else						return False;
}

unsigned char SMBus_Read(unsigned char devId, unsigned int Addr, unsigned char numBytes, unsigned int* data, unsigned char type)
{
//	__("SMBus Read !!");
	unsigned char i, i2c_err = False;
	if(I2C_BUS_BUSY)
	{
		//myprintf("I2C_Read: I2C_BUS_BUSY \r\n");
		//return False;
	}

	//Start 
	I2C1CONbits.SEN = 1;		//start
	i2c_err = !I2C_Done();

	//DevID+W(0x00) 보내고 ACK확인
	I2C_TX_BUFFER = devId & 0b11111110 | 0x00 ;		//write가 0
	i2c_err = !I2C_Done();
	if( !I2C_Ack() )		return False;

	if(type == ADDR_16BIT)
	{
		//상위 주소를 쓰고 ACK확인
		I2C_TX_BUFFER = (BYTE)(Addr>>8);
		i2c_err = !I2C_Done();
		if( !I2C_Ack() )		return False;
	}

	//하위 주소를 쓰고 ACK확인
	I2C_TX_BUFFER = (BYTE)(Addr);
	i2c_wait(4000);
	//Wait4msec(1);
	//i2c_err = !I2C_Done(); 
	if( !I2C_Ack() )		return False;


	//리스타트 
	I2C1CONbits.RSEN = 1;
	//I2C1CONbits.SEN = 1;
	i2c_err = !I2C_Done();
	

	//DevID+R(0x01) 보내기
	I2C_TX_BUFFER = ( devId & 0b11111110 ) | 0x01 ;
	i2c_err = ! I2C_Done();
	

	//수신하자 LOW
	I2C1CONbits.ACKDT = ACK;		// Send ACK at end of RX
	I2C1CONbits.RCEN = 1;			// Enable RX
	i2c_err = ! I2C_Done();			// Get data
	*(data++) = I2C_RX_BUFFER;
	I2C1CONbits.ACKEN = 1;		// Send Ack (ACKDT)
	i2c_err = ! I2C_Done();


	//수신하자 HIGH
	if(numBytes >= 3)
	{//3	
		I2C1CONbits.ACKDT = ACK;		// Send ACK at end of RX
		I2C1CONbits.RCEN = 1;			// Enable RX
		i2c_err = ! I2C_Done();			// Get data
		*(data++) = I2C_RX_BUFFER;
		I2C1CONbits.ACKEN = 1;		// Send Ack (ACKDT)
		i2c_err = ! I2C_Done();
	}

	
	//수신하자 PEC
	I2C1CONbits.ACKDT = NACK;		// Send NACK at end of RX
	I2C1CONbits.RCEN = 1;			// Enable RX
	i2c_err = ! I2C_Done();			// Get data
	*(data++) = I2C_RX_BUFFER;
	I2C1CONbits.ACKEN = 1;		// Send Nack (ACKDT)
	i2c_err = ! I2C_Done();

	//STOP 신호 
	I2C1CONbits.PEN=1;			// Send Stop
	i2c_err = ! I2C_Done();

	if(i2c_err)	return False;
	return True;

}

unsigned char I2C_Read(unsigned char devId, unsigned int Addr, unsigned char numBytes, unsigned char* data, unsigned char type)
{
	unsigned char i, i2c_err = False;
	if(I2C_BUS_BUSY)
	{
		//myprintf("I2C_Read: I2C_BUS_BUSY \r\n");
		//return False;
	}

	I2C1STATbits.I2COV = 0;

	//Start 
	I2C1CONbits.SEN = 1;		//start
	i2c_err = !I2C_Done();

	//DevID 보내고 ACK확인
	I2C_TX_BUFFER = devId & 0b11111110;		//write가 0
	i2c_err = !I2C_Done();
	if( !I2C_Ack() )		return False;

	if(type == ADDR_16BIT)
	{
		//상위 주소를 쓰고 ACK확인
		I2C_TX_BUFFER = (BYTE)(Addr>>8);
		i2c_err = !I2C_Done();
		if( !I2C_Ack() )		return False;
	}

	//하위 주소를 쓰고 ACK확인
	I2C_TX_BUFFER = (BYTE)(Addr);
	i2c_err = !I2C_Done();
	if( !I2C_Ack() )		return False;

	//리스타트 
	I2C1CONbits.RSEN = 1;

	i2c_err = !I2C_Done();
	
	//DevID 보내고 ACK확인 (Read)
	I2C_TX_BUFFER = ( devId & 0b11111110 ) | 0x01;
	i2c_err = !I2C_Done();
	if( !I2C_Ack() )		return False;
		
	//수신하자 
	I2C1CONbits.ACKDT = ACK;		// Send ACK
	I2C1CONbits.RCEN = 1;			// Enable RX
	
	for(;;)
	{
		i2c_err = !I2C_Done();
		*(data++) = I2C_RX_BUFFER;
		if( (--numBytes) == 0 )
		{
			I2C1CONbits.ACKDT = 1;
			break;
		}
		I2C1CONbits.ACKEN=1;					// Send Ack/Nack	
		i2c_err = !I2C_Done();
		I2C1CONbits.RCEN = 1;
	}

	I2C1CONbits.PEN=1;
	i2c_err = !I2C_Done();

	if(i2c_err)	return False;
	return True;
}

unsigned char I2C_Write(unsigned char devId, unsigned int Addr, unsigned char numBytes, unsigned char* data, unsigned char type)
{
	unsigned char i, i2c_err = False;
	if(I2C_BUS_BUSY)
	{
		//myprintf("I2C_Write: I2C_BUS_BUSY \r\n");
		//return False;
	}
	
	//Start 
	I2C1CONbits.SEN = 1;		//start
	i2c_err = !I2C_Done();
	
	//DevID 보내고 ACK확인
	I2C_TX_BUFFER = devId & 0b11111110;		//write가 0
	i2c_err = !I2C_Done();
	if( !I2C_Ack() )		return False;

	if(type == ADDR_16BIT)
	{	
		//상위 주소를 쓰고 ACK확인
		I2C_TX_BUFFER = (BYTE)(Addr>>8);
		i2c_err = !I2C_Done();
		if( !I2C_Ack() )		return False;
	}

	//하위 주소를 쓰고 ACK확인
	I2C_TX_BUFFER = (BYTE)(Addr);
	i2c_err = !I2C_Done();
	if( !I2C_Ack() )		return False;

	//값 쓰고 ACK확인
	for(i=0; i

'Enginius > Firmware' 카테고리의 다른 글

PIC32 - Input Capture  (0) 2010.02.19
PID 제어  (1) 2010.02.18
PIC32 - GPIO  (0) 2010.02.17
PIC32 - Interrupt  (0) 2010.02.17
PIC32 - ADC  (0) 2010.02.17