I2C
여러개의 master과 여러개의 slave가 주소값을 바탕으로 직렬반이중동기 통신을 합니다
(R1, R3는 풀업저항으로 통신을 하고있지 않을 때는 SDA, SCL이 High를 유지하기위함입니다. datasheet상으로는 10k ohm정도를 사용한다고 나와있습니다.)
이때 두가닥의 선으로 통신을 하게되는데
Data를 전송하는 SDA, Clock을 SCL로 공유하며 서로 시간간격을 맞추게 됩니다
레지스터의 상세한 내용이나 datasheet는 구글링이나 서적을 찾아보면 나오니 관련 자세한 정보는 찾아보시기 바랍니다.
여기서 알아야할것은 TWDR, TWCR, TWSR, TWBR이렇게 크게 4개의 레지스터를 다루게 됩니다.
해당코드는 avrstudio 4.0 winavr 환경에서 l3g4200d 자이로센서와의 TWI통신임을 미리 말씀드립니다.
#include<avr/io.h>
#include<stdio.h>
#include<util/delay.h>
#define F_CPU 16000000UL //이부분은 컴파일 제품마다 해줘야하는 경우도 있고 아닌경우도 있다고 합니다. avr이 실제 동작하는 clk스피드입니다.
//////////////////////////////////////////////////////////////UART직렬통신을 위한 함수
void Putch0(char data)
{
while(!(UCSR1A & 0x20));
UDR1 = data;
}
char Getch0(void)
{
while(!(UCSR1A &0x80));
return UDR1;
}
void UART_initialize(void)
{
UCSR1A = 0x0;
UCSR1B =0b00011000;
UCSR1C = 0b00000110;
UBRR1H = 0;
UBRR1L = 8;
}
/////////////////////////////////////////////////////////////////
void TWI_initialize(void)//TWI통신의 초기화함수
{
TWBR = 72;
TWSR = 0;
_delay_ms(100);
}
SCL은l3g4200d twi 통신 normal mode인 100khz로 설정 이값에 따라 TWBR을 설정 TWPS는 TWSR이 라면 무시해도 된다.
//////////////////////////////////////////////////////////////////////
void I2C_write(unsigned char slv_addr, unsigned char address, unsigned char byte){
_delay_ms(1);
TWCR = 0xA4;//마스터 송신모드를 위한 스타트 조건
while(((TWCR & 0x80) == 0x00) || (TWSR & 0xF8) != 0x08);//TWINT가 동작완료시 1로 바뀐다, TWSR은 ACK와 동작에 대한 상태를 표기해놓는 레지스터 이므로 해당 datasheet를 참조
TWDR = slv_addr;//통신하고자하는 slave의 ID 장전
TWCR=0x84;//ID전송 조건
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xF8) != 0x18));//마찬가지
printf("!");
TWDR =address;
TWCR= 0x84;
while(((TWCR & 0x80) == 0x00) || (TWSR & 0xf8) != 0x28){};
printf("!");
TWDR = byte;
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00) || (TWSR & 0xf8) != 0x28){};
TWCR = 0x94;//stop조건
}
/////////////////////////////////////////////////////////////////////////////////////////////
unsigned int I2C_read(unsigned char slv_addr, unsigned char regi){ //l3g4200d의 레지스터를 읽기위한 코드 혼합모드(Combining Several TWI Modes)에 대한 설명을 참조
_delay_ms(1);
unsigned int result;
TWCR = 0xa4;
while(((TWCR & 0x80) == 0x00) || (TWSR & 0xf8) != 0x08){};
TWDR= slv_addr;
TWCR= 0x84;
while(((TWCR & 0x80) == 0x00) || (TWSR & 0xf8) != 0x18);
TWDR = regi;
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00) || ( TWSR & 0xf8) !=0x28){};
TWCR = 0xa4;//여기서 restart에 대한 조건을 넣어줘야한다.
while(((TWCR & 0x80) == 0x00) || (TWSR & 0xf8) !=0x10);
TWDR = slv_addr+1;//slave를 R bit와 함께
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00) || (TWSR & 0xf8) !=0x40 );
TWCR = 0x84; //이 조건이 설정되면 TWDR로 slave정보가 수신이 된다.
while(((TWCR & 0x80) == 0x00) || ( TWSR & 0xf8) != 0x58){
};
result = TWDR;
TWCR= 0x94; //stop
return result;
}
int main(void){
unsigned char x_l, x_h, y_l, y_h, z_l, z_h;
signed int x,y,z;
float x_f, y_f, z_f;
FILE *fp;
fp=fdevopen(Putch0, Getch0);
DDRA = 0xff;
DDRD = 0x01;
UART_initialize();
TWI_initialize();
printf("\n\r zyro test");
I2C_write(0xd0, 0x20, 0x8f);
printf("\n\r zyroL write initialized");
while(1){
PORTA=0xff;
_delay_ms(100);
x_l=I2C_read(0xd0, 0x28);
x_h=I2C_read(0xd0, 0x29);
y_l=I2C_read(0xd0, 0x2a);
y_h=I2C_read(0xd0, 0x2b);
z_l=I2C_read(0xd0, 0x2c);
z_h=I2C_read(0xd0, 0x2d);
x=(x_h<<8) | x_l;
y=(y_h<<8) | y_l;
z=(z_h<<8) | z_l;
x_f = (float) x*0.00875;
y_f = (float) y*0.00875;
z_f = (float) z*0.00875;
printf("\n\r [x]=%3.2f, [y]=%3.2f, [z]=%3.2f");
PORTA=0x00;
_delay_ms(100);
}
}
atmega128은 사진이 따로 없어서...
하지만 별 다를 것은 없고 PD0(SCL), PD1(SDA)를 각각 연결해주면 되고 풀업저항(3.3V)를 연결해주면됩니다.
SDO의 용도는 L3G4200d자체적으로 GND연결시 주소가 0xD0로 설정이 되고 Vcc연결시 0xD2로 설정이됩니다.
그리고 주의해야할 것이 접지가 있는데 고주파의 통신시 접지의 임피던스가 변화하게 되어 통신이 이루어 지지 않게 됩니다. 접지는 2개이상으로 연결하는게 좋을 것 같습니다.(이건 사실 이론적으로 주워듣고 경험적으로 이렇게 되는 거 같아서 말씀드리는 겁니다. 아니라면 지적해주세요.)
관련데이터도 입니다. datasheet에 설명이 잘되어 있으니 간단하게 요약하자면
START, STOP조건을 제외하고는 SCL이 HIGH일때는 SDA가 변하면 안된다.
마스터에서 설정한 통신속도가 너무빠르면 SLAVE에서 하드웨어적으로 SCL의 지연이 가능하다
통신중 ACK발생시 SLAVE가 SDA를 LOW로 떨어뜨린다.
NACK발생시 RESTART조건을 발생시키거나 STOP조건을 설정시킨다.
MSB부터 시작해서 LSB(주소가 7bit) + R/W(1bit) + ACK(1bit)로 총 9bit가 발생한다.
오실로스코프로 측정한 파형은 차후 포스팅하겠습니다.