각 함수에 대한 설명은 스크롤을 아래로 내리시면 볼수있습니다.


 void I2C_start(void)

{

I2C_DIR=SDIROUT;

sda(1);

scl(1);I2C_delay();

sda(0);I2C_delay();

}

 

 

void I2C_stop(void)

{

I2C_DIR=SDIROUT;

scl(1);I2C_delay();

sda(0);I2C_delay();

sda(0);I2C_delay();

sda(1);I2C_delay();

}

 

void init_ds1307(void){

I2C_start();

I2C_write(ADDRTC);

I2C_write(0x00);

I2C_write(0x00);

I2C_stop();

I2C_start();

I2C_write(ADDRTC);

I2C_write(0x00);

I2C_write(sec);

I2C_write(min);

I2C_write(hr);

I2C_write(dy);

I2C_write(dt);

I2C_write(mn);

I2C_write(yr);

I2C_write(0x10); // sqwe:enable 1Hz output

I2C_stop();

}

 

 

 

 

 

 

void writebyte(unsigned char addr, unsigned char data)

{

I2C_start();

I2C_write(ADDRTC);

 

I2C_DIR=SDIROUT;

I2C_write(addr);

I2C_write(data);

I2C_stop();

}

 

 

unsigned char readbyte(unsigned char addr)

{

unsigned char byte;

I2C_start();

I2C_write(ADDRTC);

I2C_write(addr);

I2C_start();

I2C_write(ADDRTC|1);

byte = I2C_read(NACK);

I2C_stop();

return byte;

}

 

unsigned char I2C_read(unsigned char b)

{

unsigned char data, i;

sda(1);I2C_delay();I2C_delay();I2C_delay();

scl(0);I2C_delay();I2C_delay();

for(i=1;i<=8;i++)

{

scl(1);I2C_delay();

I2C_DIR=SDIRIN;

data=data<<1;

data = data | ((I2C_IN>>1)&0x01);

I2C_DIR=SDIROUT;

scl(0);I2C_delay();

}

sda(b);I2C_delay();

scl(0);I2C_delay();

scl(1);I2C_delay();

if(b==NACK)

sda(1);I2C_delay();

scl(0);I2C_delay();

sda(1);I2C_delay();

return data;

}

 

 

 

void I2C_write(unsigned char data)

{

unsigned char i;

I2C_DIR=SDIROUT;

scl(0);I2C_delay();

for(i=1;i<=8;i++)

{

sda(data>>7);I2C_delay();

scl(1);I2C_delay();

data=data<<1;

scl(0);I2C_delay();

}

 

sda(0);I2C_delay();

scl(0);I2C_delay();

scl(1);I2C_delay();

scl(0);I2C_delay();

sda(1);I2C_delay();

}

 

 

void main(void)

{

unsigned char i;

init_devices();

init_ds1307();

writebyte(0x00,0x00); // 00번지()0 값을 쓴다.

 

while(1)

{

Display();

FourDigit=readbyte(0); // 초값을 읽어온다.

}

}

 

 

 

 

------------------함수의 설명-------------------

I2C_start();

SDA 출력 방향을 AVR이 출력하는 것으로 함

SCL, SDA : High

SDA : Low로 떨굼

 

 

I2C_stop();

SDA 출력 방향을 AVR이 출력하는 것으로 함

SCLHigh

SDALow

SDAHigh

 

 

init_ds1307();

I2C_start(); 를 실행한다.

DS1307 칩을 초기화 한다.

시간, , 초 등을 초기 값으로 초기화 한다.

동작 확인용 LED를 달기 위하여,

1Hz를 사용 가능하도록 설정한다.

 

 

void writebyte(unsigned char addr, unsigned char data);

writebyte(어드레스, 데이터)

설명 : 어드레스에 데이터를 쓴다.

SDA 출력을 AVR에서 출력하는 것으로 한다.

1.SCLLow로 떨군다.

2.최상위 비트부터 한 비트 쓴다.

3.SCLHigh로 올린다.

4.담 비트 준비

1,2,3,48번 반복한다.

Ack를 체크한다.(그냥 무시하고 지나가고 있다.)

 

 

nsigned char readbyte(unsigned char addr);

데이터 = readbyte(어드레스)

설명 : 어드레스에서 데이터를 읽는다.

SDA핀을 AVR에서 입력 받는 것으로 한다.

1. SCLHigh로 올린다.(I2C에서 데이터 수신 받을 때 안

정적인 쪽은 클럭이 High일 때이므로)

2. 최상위비트부터 한 비트 받는다.

3. SCLLow로 떨군다.

4. 담 비트 받을 준비한다.

1,2,3,48번 반복한다.

Ackn을 받는다.

 

unsigned char I2C_read(unsigned char b);

데이터 = I2C_read();

현재 번지에서 데이터를 읽어 온다.

ACK를 굳이 읽을 이유가 없어서, ACK는 강제로 1로 주고

있다.

 

void I2C_write(unsigned char data);

I2C_write

현재의 어드레스에 데이터를 쓴다.

ACK0을 실어준다.

 

 

도움이 되실지 모르겠습니다. I2C로 EEPROM의 내용을 R/W하는 예제입니다.


//=======================================

// MAIN PROJECT FILE

//=======================================

//

#include <avr/io.h>

#include <stdio.h>


#include <util/twi.h>


#include "eeprom24lc32.h"

#include "lcd.h"

#include "button.h"


int main()

{

long data, saved_data;

short rtn;

char string[80];


EprmInit(400); // 400 kHz TWI 전송속도

LcdInit();


data = 0x12345678;


if((rtn=EprmWriteNByte(0x500, (char *)&data, sizeof(data)))<0)

{

sprintf(string, "write error: %d", (int) rtn);

LcdMove(0,0); LcdPuts(string);


while(1);

}


// verify


if((rtn=EprmReadNByte(0x500, (char *)&saved_data, sizeof(saved_data)))<0)

{

sprintf(string, "write error: %d", (int) rtn);

LcdMove(0,0); LcdPuts(string);


while(1);

}

else

{

LcdMove(0,0); LcdPuts("Saved Data = ");


sprintf(string, "0x%lx", saved_data);

LcdMove(1,0); LcdPuts(string);

}


while(1);

}



//=======================================

// EEPROM24LC32.H

//=======================================

//


#ifndef __EEPROM24LC32_H__

#define __EEPROM24LC32_H__


void  EprmInit(unsigned int kHz);

short EprmWriteByte(short addr, char data);

short EprmWriteNByte(short addr, char data[], int n);

short EprmReadNByte(short addr, char data[], int n);


#endif


//=======================================

// EEPROM24LC32.C

//=======================================

//

//===========================================
// eerpom24lc32.c
//
// EEPROM 24LC32 구동 프로그램
//===========================================
//
#include <avr/io.h>
#include <util/twi.h>
#include <util/delay.h>

#include "lcd.h"

#include "eeprom24lc32.h"

#define DEVICE 0x00 // Device Select Bit
#define SLA_W (0xA0 | (DEVICE << 1))
#define SLA_R (0xA0 | (DEVICE << 1) | 0x01)

static short setAddr(short addr);

//====================================================
// TWI 버스를 초기화한다.
//====================================================
//
void EprmInit(unsigned int kHz)
{
TWSR = 1<<TWPS0; // 비트율 프리스케일러값을 16으로 한다.

// 비트율 설정
TWBR = (unsigned char) ((F_CPU/(kHz*1000L) - 16L)/(2L*4L));
}

//===================================================
// 1바이트 데이터를 24LC32A EEPROM에 쓴다.
//
// addr : 데이터를 쓸 EEPROM 주소
// 0x00 ~ 0xFFF
//  data : 쓸 데이터
//
// return :
// 0 - 성공
//   -1 - 쓰기 실패
//===================================================
//
short EprmWriteByte(short addr, char data)
{
short rtn;

//-----------------------------------
//  데이터를 쓸 EEPROM 주소를 보낸다.
//-----------------------------------
if((rtn=setAddr(addr)) < 0)
return rtn;

//-----------------------------------
// 데이터 전송
//-----------------------------------
TWDR = data;
TWCR = (1<<TWINT) | (1<<TWEN);

while(!(TWCR & (1<<TWINT))); // DATA가 보내질 때까지 기다린다.
if((TWSR & TW_STATUS_MASK) != TW_MT_DATA_ACK) // DATA 전송후 슬레이브로 부터 ACK를 받았는 지 검사
return -3;

//----------------------------------
// STOP 조건을 보낸다.
//----------------------------------
TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN);
while((TWCR & (1<<TWSTO))); // STOP조건이 보내질 때까지 기다린다.

return 0;
}

//===================================================
// n 바이트 데이터를 EEPROM에 쓴다.
//
// addr : EEPROM 주소 
// 0x00 ~ 0xFFF
//  data : 쓸 데이터를 저장한 버퍼주소
//  n : 쓸 데이터 개수
//
// return :
// 양수 - 다음 쓸 EEPROM 주소
//   -1 - 쓰기 실패
//===================================================
//

short EprmWriteNByte(short addr, char *data, int n)
{
int i, rtn;

for(i=0; i<n; i++)
{
if((rtn=EprmWriteByte(addr, *data))<0)
return rtn;
addr++;
data++;
}

return addr;
}

//===================================================
// n 바이트 데이터를 EEPROM으로부터 읽어들인다.
//
// addr : EEPROM 주소 
// 0x00 ~ 0xFFF
//  data : 읽은 데이터를 저장할 버퍼
//  n : 읽을 데이터 개수
//
// return :
//    0 - 성공
//   -1 - 읽기 실패 
//===================================================
//
short EprmReadNByte(short addr, char data[], int n)
{
int i;

//----------------------------------------
// 읽을 데이터의 주소를 보낸다.
//----------------------------------------
if(setAddr(addr) < 0)
return -5;

//---------------------------------------
// REPEATED START
//---------------------------------------
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // START 조건을 보낸다.
while(!(TWCR & (1<<TWINT))); // START조건이 보내질 때까지 기다린다.
if((TWSR & TW_STATUS_MASK) != TW_REP_START) // 정상적으로 REPEATED START조건이 보내졌는 지 검사한다.
return -6;


//--------------------------------------
// SLA_R를 보낸다.
//--------------------------------------
TWDR = SLA_R;
TWCR = (1<<TWINT) | (1<<TWEN);

while(!(TWCR & (1<<TWINT))); // SLA_R이 보내질 때까지 기다린다.
if((TWSR & TW_STATUS_MASK) != TW_MR_SLA_ACK) // SLA_R 전송후 슬레이브로 부터 ACK를 받았는 지 검사
return -7;

//----------------------
// 데이터 읽기
//----------------------
for(i=0; i<n-1; i++)
{
TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN); // TWI동작을 시작한다.  데이터를 받으면 ACK를 보낸다.
while(!(TWCR & (1<<TWINT))); // 데이터를 받을 때까지 기다린다.
if((TWSR & TW_STATUS_MASK) != TW_MR_DATA_ACK)
return -8;
data[i] = TWDR; // 데이터를 읽는다.
}

// 마지막 바이트를 받으면 ACK를 보내지 않는다.

TWCR = (1<<TWINT) | (1<<TWEN); // TWI동작을 시작한다. 데이터를 받으면 ACK를 보내지 않는다.
while(!(TWCR & (1<<TWINT))); // 데이터를 받을 때까지 기다린다.
if((TWSR & TW_STATUS_MASK) != TW_MR_DATA_NACK)
return -9;
data[i] = TWDR; // 마지막 데이터를 읽는다.

//-----------------------------
// STOP 조건을 보낸다.
//-----------------------------

TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN);
while(TWCR & (1<<TWSTO)); // STOP조건이 보내질 때까지 기다란다.
return 0;
}


//=========================================-------===================
// eeprom에 데이터를 쓰기/읽기를 위한 제어 및 주소 바이트를 보낸다.
//
//  addr : 데이터를 쓰기/읽기를 할 주소
//
// return :
// 0   - 성공
//   음수 - 전송 실패
//===================================================================
//
static short setAddr(short addr)
{
while(1)
{
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); // START 조건을 보낸다.
while(!(TWCR & (1<<TWINT))); // START조건이 보내질 때까지 기다린다.
if((TWSR & TW_STATUS_MASK) != TW_START) // 정상적으로 START조건이 보내졌는 지 검사한다.
return -10;

TWDR = SLA_W;
TWCR = (1<<TWINT) | (1<<TWEN); // SLA_W를 보낸다.
while(!(TWCR & (1<<TWINT))); // SLA_W가 보내질 때까지 기다린다.

if((TWSR & TW_STATUS_MASK) == TW_MT_SLA_ACK)
break;
else // eeprom이 준비되지 않았음
{
TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN); // STOP 조건을 보낸다.
while(TWCR & (1<<TWSTO)); // STOP조건이 보내질 때까지 기다란다.
}
};

//--------------------------------------------------------
// eeprom 주소를 상위바이트, 하위바이트 순으로 보낸다.
//--------------------------------------------------------

TWDR = (unsigned char) (addr >> 8);
TWCR = (1<<TWINT) | (1<<TWEN); // 상위바이트 전송
while(!(TWCR & (1<<TWINT))); // DATA가 보내질 때까지 기다린다.

// DATA 전송후 슬레이브로 부터 ACK를 받았는 지 검사
if((TWSR & TW_STATUS_MASK) != TW_MT_DATA_ACK)
return -12;

TWDR = (unsigned char) addr; // 하위바이트 전송
TWCR = (1<<TWINT) | (1<<TWEN);
while(!(TWCR & (1<<TWINT))); // DATA가 보내질 때까지 기다린다.

// DATA 전송후 슬레이브로 부터 ACK를 받았는 지 검사
if((TWSR & TW_STATUS_MASK) != TW_MT_DATA_ACK)
return -13;

return 0;
}


9 FORMATS WITH 7-BIT ADDRESSES


Data transfers follow the format shown in Fig.10. After the START condition (S), a slave address is sent. This address is 7 bits long followed by an eighth bit which is a data direction bit (R/W) - a ‘zero’ indicates a transmission (WRITE), a ‘one’ ndicates a request for data (READ). A data transfer is always terminated by a STOP condition (P) generated by the master. However, if a master still wishes to communicate on the bus, it can generate a repeated START condition (Sr) and address another slave without first generating a STOP condition. Various combinations of read/write formats are then possible within such a transfer





Possible data transfer formats are:

· Master-transmitter transmits to slave-receiver. The transfer direction is not changed (see Fig.11).

· Master reads slave immediately after first byte (see Fig.12). At the moment of the first acknowledge, the master- transmitter becomes a master- receiver and the slave-receiver becomes a slave-transmitter. This first acknowledge is still generated by the slave. The STOP condition is generated by the master, which has previously sent a not-acknowledge (A).

· Combined format (see Fig.13). During a change of direction within a transfer, the START condition and the slave address are both repeated, but with the R/W bit reversed. If a master receiver sends a repeated START condition, it has previously sent a not-acknowledge (A).

NOTES:

1. Combined formats can be used, for example, to control a serial memory. During the first data byte, the internal memory location has to be written. After the START condition and slave address is repeated, data can be transferred.

2. All decisions on auto-increment or decrement of previously accessed memory locations etc. are taken by the designer of the device.

3. Each byte is followed by an acknowledgment bit as indicated by the A or A blocks in the sequence.

4. I2C-bus compatible devices must reset their bus logic on receipt of a START or repeated START condition such that they all anticipate the sending of a slaveaddress, even if these START conditions are not positioned according to the proper format.

5. A START condition immediately followed by a STOP , condition (void message) is an illegal format.






위의 Fig11,12,13 의 형태로 데이터를 전송합니다.


-> 요약하면, DATA는 반드시 8bit 크기로 보내어진다. 그리고 Slave가 data를 다 수신하지 못했다면 slave는 SCL신호를 LOW로 유지함으로써 Data를 모두 전달 받을수가 있다. (그래서 신호선이 풀업저항설계가 되어있는 것.) //


[통신의 송수신 순서]

1. SDA,SCL 둘다 HIGH 상태인가?  - 그렇다면 Bus는 비어있다.

2. 시작비트를 보낸다. (SCL: : HIGH, SDA : LOW)

3. 7bit의 주소값을 보내고 1bit의 R/W를 보낸다.

4. ACK를 수신하기 위해서 SDA방향을 마스터기준으로 INPUT으로 변경하고 SCL만 출력한다 (클럭신호만 아웃)

5. ACk왔다면 실어보낼 데이터를 전송한다. (1byte)

6. ACK를 수신하기 위해서 SDA의 방향을 마스터기준으로 INPUT으로 또다시 변경하고 SCL만을 출력

7. 더이상 전송할 데이터가 없다면 SDA와 SCL을 HIGH상태로 변경시킨다.

8. 정지비트를 보낸다.



[인터넷에 있는 데이터 송수신 예제입니다. ] : southlife님의 블로그참조 http://southlife.tistory.com/49 







+ Recent posts