두개의 ATmega8 MCU 간의 TWI 기능을 사용하여 데이터 전달을 하는 프로그램을 작성합니다.
IC1 을 마스터로 지정하고 IC2 를 슬레이브로 지정해서 하이퍼터미널로 입력된 데이터를 시리얼 통신으로 IC1 에 전달하고, IC1 은 다시 TWI 로 IC2 에 데이터를 전달합니다.
IC2 는 전달받은 데이터를 LCD 에 표시하고, 다시 IC1 으로 TWI 를 통해서 전달하면 IC1 은 이 데이터를 시리얼 통신으로 하이퍼터미널에 전달해서 표시하는 프로그램입니다.
(AT24C02 칩은 소켓에서 제거하세요.)
코드비젼(CodeVision) 에서 작성한 프로그램은 아래와 같습니다.
마스터 (IC1) 의 프로그램입니다.
// I/O register definitions for ATmega8
#include <mega8.h>
#include <delay.h>
#include <stdio.h>
#define ESC 0x1b
typedef unsigned char byte;
byte twi_master_read(byte addr)
{
byte dat;
TWCR = 0xa4; // START
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0x08));
TWDR = (addr << 1) | 1;
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0x40));
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0x58));
dat = TWDR;
TWCR = 0x94;
return dat;
}
void twi_master_write(byte addr, byte dat)
{
TWCR = 0xa4; // START
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0x08));
TWDR = addr << 1; // ADDRESS
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0x18));
TWDR = dat; // WRITE
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0x28));
TWCR = 0x94;
}
void main(void)
{
byte ch;
UCSRB |= 0x08;
UCSRB |= 0x10;
UBRRL = 5; // X-Tal=11.0592MHz BAUD=115200
DDRC = 0x0f;
TWBR = 12;
TWSR = 0x00;
printf("\n\rEnter a character....");
do {
ch = getchar();
PORTC = ~ch;
twi_master_write(0x38, ch);
delay_us(100);
ch = twi_master_read(0x38);
if (ch == ESC) putchar('\f');
else putchar(ch);
} while(1);
}
슬레이브 (IC2) 의 프로그램입니다.
// I/O register definitions for ATmega8
#include <mega8.h>
#include <delay.h>
#define lcd_inst_wr (PORTC = 0x04)
#define lcd_data_wr (PORTC = 0x05)
#define lcd_data(i) (PORTD = i)
#define ESC 0x1b
typedef unsigned char byte;
void lcd_iw(byte inst)
{
lcd_inst_wr;
lcd_data(inst);
delay_us(40);
PORTC &= 0xfb;
lcd_inst_wr;
lcd_data(inst<<4);
delay_us(40);
PORTC &= 0xfb;
delay_ms(2);
}
void lcd_dw(byte dw)
{
lcd_data_wr;
lcd_data(dw);
delay_us(40);
PORTC &= 0xfb;
lcd_data_wr;
lcd_data(dw<<4);
delay_us(40);
PORTC &= 0xfb;
}
void init_lcd(void)
{
lcd_iw(0x20);
lcd_iw(0x28);
lcd_iw(0x06);
lcd_iw(0x0e);
lcd_iw(0x01);
}
void lcd_str(char *str)
{
while(*str){
lcd_dw(*str++);
}
}
byte twi_slave_read(byte addr)
{
byte dat;
TWAR = addr << 1;
TWCR = 0x44; // SLAVE
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0x60));
TWCR = 0xc4;
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0x80));
dat = TWDR; // READ
TWCR = 0xc4;
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0xa0));
TWCR = 0xc4;
return dat;
}
void twi_slave_write(byte addr, byte dat)
{
TWAR = addr << 1;
TWCR = 0x44; // SLAVE
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0xa8));
TWDR = dat;
TWCR = 0xc4;
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0xc0));
TWCR = 0xc4;
}
void main(void)
{
byte ch;
char *str1 = "<TWI(I2C) - TWI>";
DDRC = 0x07;
DDRD = 0xf0;
init_lcd();
lcd_str(str1);
lcd_iw(0xc0);
TWSR = 0x00;
do {
ch = twi_slave_read(0x38);
if (ch == ESC) lcd_iw(0xc0);
else lcd_dw(ch);
delay_ms(1);
twi_slave_write(0x38, ch);
} while(1);
}
AVR-GCC 에서 작성한 프로그램은 아래와 같습니다.
마스터 (IC1) 의 프로그램입니다.
// I/O register definitions for ATmega8
#include <avr/io.h>
#include <stdio.h>
#define ESC 0x1b
typedef unsigned char byte;
void delay_us(unsigned int time_us)
{
unsigned int i;
for(i=0; i<time_us; i++){ // 4 cycle +
asm("PUSH R0"); // 2 cycle +
asm("POP R0"); // 2 cycle +
asm("PUSH R0"); // 2 cycle +
asm("POP R0"); // 2 cycle + =12 cycle for 11.0592MHZ
// asm("PUSH R0"); // 2 cycle +
// asm("POP R0"); // 2 cycle = 16 cycle = 1us for 16MHz
}
}
void delay_ms(unsigned int time_ms)
{
unsigned int i;
for(i=0; i<time_ms;i++)
delay_us(1000);
}
int uart_putchar(char c)
{
if (c == '\n')
uart_putchar('\r');
loop_until_bit_is_set(UCSRA, UDRE);
UDR = c;
return 0;
}
int uart_getchar(void)
{
char c;
loop_until_bit_is_set(UCSRA, RXC);
c = UDR;
return c;
}
byte twi_master_read(byte addr)
{
byte dat;
TWCR = 0xa4; // START
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0x08));
TWDR = (addr << 1) | 1;
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0x40));
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0x58));
dat = TWDR;
TWCR = 0x94;
return dat;
}
void twi_master_write(byte addr, byte dat)
{
TWCR = 0xa4; // START
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0x08));
TWDR = addr << 1; // ADDRESS
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0x18));
TWDR = dat; // WRITE
TWCR = 0x84;
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0x28));
TWCR = 0x94;
}
int main(void)
{
byte ch;
FILE *fp;
fp = fdevopen(uart_putchar, uart_getchar, 0);
UCSRB = 0x18;
UBRRL = 5; // X-Tal=11.0592MHz BAUD=115200
DDRC = 0x0f;
TWBR = 12;
TWSR = 0x00;
printf("\n\rEnter a character....");
do {
ch = getchar();
PORTC = ~ch;
twi_master_write(0x38, ch);
delay_us(100);
ch = twi_master_read(0x38);
if (ch == ESC) putchar('\f');
else putchar(ch);
} while(1);
}
슬레이브 (IC2) 의 프로그램입니다.
// I/O register definitions for ATmega8
#include <avr/io.h>
#define lcd_inst_wr (PORTC = 0x04)
#define lcd_data_wr (PORTC = 0x05)
#define lcd_data(i) (PORTD = i)
#define ESC 0x1b
typedef unsigned char byte;
void delay_us(unsigned int time_us)
{
unsigned int i;
for(i=0; i<time_us; i++){ // 4 cycle +
asm("PUSH R0"); // 2 cycle +
asm("POP R0"); // 2 cycle +
asm("PUSH R0"); // 2 cycle +
asm("POP R0"); // 2 cycle + =12 cycle for 11.0592MHZ
asm("PUSH R0"); // 2 cycle +
asm("POP R0"); // 2 cycle = 16 cycle = 1us for 16MHz
}
}
void delay_ms(unsigned int time_ms)
{
unsigned int i;
for(i=0; i<time_ms;i++)
delay_us(1000);
}
void lcd_iw(byte inst)
{
lcd_inst_wr;
lcd_data(inst);
delay_us(40);
PORTC &= 0xfb;
lcd_inst_wr;
lcd_data(inst<<4);
delay_us(40);
PORTC &= 0xfb;
delay_ms(2);
}
void lcd_dw(byte dw)
{
lcd_data_wr;
lcd_data(dw);
delay_us(40);
PORTC &= 0xfb;
lcd_data_wr;
lcd_data(dw<<4);
delay_us(40);
PORTC &= 0xfb;
}
void htoa(byte hh)
{
byte temp;
temp=hh;
hh &= 0xf0; hh >>= 4;
if (hh >= 10) hh += 7;
hh += '0';
lcd_dw(hh);
hh=temp;
hh &= 0x0f;
if (hh >= 10) hh += 7;
hh += '0';
lcd_dw(hh);
}
void init_lcd(void)
{
lcd_iw(0x20);
lcd_iw(0x28);
lcd_iw(0x06); //entry mode set
lcd_iw(0x0e); //display on, cursor on, blink off
lcd_iw(0x01); //clear display
}
void lcd_str(char *str)
{
while(*str){
lcd_dw(*str++);
}
}
byte twi_slave_read(byte addr)
{
byte dat;
TWAR = addr << 1;
TWCR = 0x44; // SLAVE
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0x60));
TWCR = 0xc4;
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0x80));
dat = TWDR; // READ
TWCR = 0xc4;
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0xa0));
TWCR = 0xc4;
return dat;
}
void twi_slave_write(byte addr, byte dat)
{
TWAR = addr << 1;
TWCR = 0x44; // SLAVE
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0xa8));
TWDR = dat;
TWCR = 0xc4;
while(((TWCR & 0x80) == 0x00) || ((TWSR & 0xf8) != 0xc0));
TWCR = 0xc4;
}
int main(void)
{
byte ch;
DDRC = 0x07;
DDRD = 0xf0;
init_lcd();
lcd_str("<TWI(I2C) - TWI>");
lcd_iw(0xc0);
TWSR = 0x00;
do {
ch = twi_slave_read(0x38);
if (ch == ESC) lcd_iw(0xc0);
else lcd_dw(ch);
delay_ms(1);
twi_slave_write(0x38, ch);
} while(1);
}