이 게시물은 내가 배운순서대로 올라가진 않을것 같다... 오히려 뒷내용으로 가면 갈수록 쉬워질수도 있는 게시물
사실 ATmega 128에 대해 통신부터 적으보자 나중에 까먹으면 볼수 있도록
또한 이게시물도 문제랑 그에대한 코드 위주로 나갈 것 같다.
11장의 SPI Master/Slave 응용문제 (1)의 과정을 수행하는 프로그램을 작성 하시오.
마스터 부 : 키의 입력 상태에 따라 다음의 과정을 수행하는 프로그램을 작성함.
1. PD0가 눌릴 때마다 문자 A를 일정 시간 간격으로 전송하는 프로그램 작성.
2. PD1이 눌리면, 문자 A-Z까지 일정 시간 간격으로 전송하는 프로그램 작성.
3. PD2가 눌리면, 정의된 문자열을 전송하는 프로그램 작성.
(이상의 전송 가정은 polling 방식으로 송신)
위와같은 문제를 풀 것이다.


/*
* twi_send_message.c
*
* Created: 2021-10-13 오후 12:47:59
* Author: 정민규
*/
#define _USE_SAFTY_TWI_
#include <mega128.h>
#include <delay.h>
#include "twi.h"
#include "lcd.h"
unsigned char SLAVE_ADDR = 0x02;
unsigned char *mes = "mingyu";
void main(void)
{
unsigned char idx = 0;
unsigned char key =0;
int i=0;
LCD_Init();
LCD_Str("I2C Master Init");
LCD_Pos(1,0);
LCD_Str("Send -> ");
DDRB = 0x00; //스위치 포트 설정
Init_TWI();
while (1)
{
key = (PINB&0xff);
switch(key)
{
case 0xF7://스위치3
break;
case 0xFB: //스위치2
delay_ms(500);
LCD_Pos(1,9);
for(i=0;i<6;i++)
{
delay_ms(500);
LCD_Char(mes[i]); //지정된 문자열 출력
if( TWI_Master_Transmit(mes[i],SLAVE_ADDR) == 0)
{
}
}
break;
case 0xFD: //스위치1
delay_ms(500);
LCD_Pos(1,9);
LCD_Char('A'+idx);
if( TWI_Master_Transmit('A' +(idx++),SLAVE_ADDR) == 0)
{
if(('A'+idx)>'Z') idx = 0;
}
break;
case 0xFE: //스위치0
delay_ms(500);
LCD_Pos(1,9);
LCD_Char('A');
if( TWI_Master_Transmit('A' ,SLAVE_ADDR) == 0)
break;
default:
break;
}
}
}
위는 마스터슬레이브의 동작이다.
*
* twi_send_message_slave.c
*
* Created: 2021-10-13 오후 12:56:12
* Author: 정민규
*/
#define _USE_SAFTY_TWI_
#include <mega128.h>
#include <delay.h>
#include "twi.h"
#include "lcd.h"
unsigned char SLAVE_ADDR = 0x02;
void main(void)
{
unsigned char ch = 0;
int pos_count = 10;
LCD_Init();
LCD_Str("I2C Slave Init"); //초기값 설정
LCD_Pos(1,0);
LCD_Str("Received-> ");
Init_TWI();
Init_TWI_Slaveaddr(SLAVE_ADDR);
while (1)
{
// Please write your application code here
if(TWI_Slave_Receive(&ch) ==0)
{
LCD_Pos(1,pos_count);
LCD_Char(ch);
pos_count++;
}
if (pos_count > 15) pos_count =10; //16자리 이상이면 LCD재배치
}
}
위 코드들의 동작모습이다!
두개의 아트메가를 가지고 SPI통신을 해보는 아주 간단한 예제라고 할 수있는데... 사실 이 부분을 이해하려면 TWI헤더를 먼저 뜯어보는 과정이 필요할 것 같다.
#ifndef _INCLUDE_TWI_H__
#define _INCDLUE_TWI_H__
// TimeOut 등을 고려한 TWI 동작에 있어 Error 처리를 하는 함수를 사용할 경우,
// 아래와 같이 _USE_SAFTY_TWI_ 를 정의하여 사용하고,
// Error 처리를 하지 않고 간단히 사용할 경우에는 일반적인 함수를 사용하도록 header 파일을 수정
//#define _USE_SAFTY_TWI_
#define ExtDev_ERR_MAX_CNT 2000
// TWI Master Transmitter/Receiver Mode에서의 상태 코드
#define TWI_START 0x08
#define TWI_RESTART 0x10
#define MT_SLA_ACK 0x18
#define MT_DATA_ACK 0x28
#define MR_SLA_ACK 0x40
#define MR_DATA_ACK 0x50
#define MR_DATA_NACK 0x58
// TWI Slave Receiver Mode에서의 상태 코드
#define SR_SLA_ACK 0x60
#define SR_STOP 0xA0
#define SR_DATA_ACK 0x80
#define SR_DATA_NACK 0x58
// TWI Init.
void Init_TWI()
{
TWBR = 0x32; //SCL = 100kHz
TWCR = (1<<TWEN); //TWI Enable
TWSR = 0x00; //100kHz
}
void Init_TWI_400K()
{
// Fsck = 14.7456MHz
// SCL = (Fsck)/(16 + (2*10)) = 409,600
TWBR = 0x0A; //SCL 약 400kHz
TWCR = (1<<TWEN); //TWI Enable
TWSR = 0x00; //100kHz
}
#if defined(_USE_SAFTY_TWI_)
/***************************************************/
/* 마스터 송신기 모드에서의 송신 관련 함수 */
/***************************************************/
// 신호 전송 완료 검사 및 Status 확인 + Timeout Check
// error code 0 : no error, 1 : timeout error 2 : TWI Status error
unsigned char TWI_TransCheck_ACK(unsigned char Stat)
{
unsigned int ExtDev_ErrCnt = 0;
while (!(TWCR & (1<<TWINT))) // 패킷 전송 완료될 때 까지 wait
{
if(ExtDev_ErrCnt++ > ExtDev_ERR_MAX_CNT){ return 1; }
}
if ((TWSR & 0xf8) != Stat)return 2; // 전송 검사(ACK) : error시 2 반환
else return 0;
}
// START 전송
unsigned char TWI_Start()
{
TWCR = ((1<<TWINT) | (1<<TWSTA) | (1<<TWEN)); // START 신호 보내기
// while (TWCR & (1<<TWINT)) == 0x00); // START 신호 전송 완료될 때 까지 wait
return TWI_TransCheck_ACK(TWI_START);
}
// SLA+W 패킷 전송
unsigned char TWI_Write_SLAW(unsigned char Addr)
{
TWDR = Addr; // SLA + W 패킷(슬레이브 주소+Write bit(Low))
TWCR = (1<<TWINT) | (1<<TWEN); // SLA + W 패킷 보내기
return TWI_TransCheck_ACK(MT_SLA_ACK);
}
// 데이터 패킷 전송
unsigned char TWI_Write_Data(unsigned char Data)
{
TWDR = Data; // 데이터
TWCR = (1<<TWINT) | (1<< TWEN); // 데이터 패킷 송신
return TWI_TransCheck_ACK(MT_DATA_ACK);
}
// STOP 전송
void TWI_Stop()
{
TWCR = ((1<<TWINT) | (1<<TWSTO) | (1<<TWEN)); // STOP 신호 보내기
}
// RESTART 전송
unsigned char TWI_Restart()
{
// unsigned char ret_err=0;
TWCR = ((1<<TWINT) | (1<<TWSTA) | (1<<TWEN)); // Restart 신호 보내기
return TWI_TransCheck_ACK(TWI_RESTART);
}
// Write Packet function for Master
unsigned char TWI_Master_Transmit(unsigned char Data, unsigned char Addr)
{
unsigned char ret_err=0;
ret_err = TWI_Start(); // START 신호 송신
if(ret_err != 0) return ret_err; // error시 종료
ret_err = TWI_Write_SLAW(Addr); // 슬레이브 주소 송신
if(ret_err != 0) return ret_err;
ret_err = TWI_Write_Data(Data); // 데이터 송신
if(ret_err != 0) return ret_err;
TWI_Stop(); // STOP 신호 송신
return ret_err; // error 코드 반환
}
/***************************************************/
/* 마스터 수신기 모드에서의 송신 관련 함수 */
/**************************************************/
// SLA+R 패킷 전송
unsigned char TWI_Write_SLAR(unsigned char Addr)
{
// unsigned char ret_err=0;
TWDR = Addr|0x01; // SLA + R 패킷(슬레이브 주소+Read bit(High))
TWCR = (1<<TWINT) | (1<<TWEN); // SLA + R 패킷 보내기
return TWI_TransCheck_ACK(MR_SLA_ACK);
}
// 데이터 패킷 수신
unsigned char TWI_Read_Data(unsigned char* Data)
{
unsigned char ret_err=0;
TWCR = (1<<TWINT)|(1<< TWEN);
ret_err = TWI_TransCheck_ACK(MR_DATA_ACK);
if(ret_err != 0)
return ret_err; // if error, return error code
*Data = TWDR; // no error, return 수신 데이터(포인터로)
return 0; // 정상 종료
}
unsigned char TWI_Read_Data_NACK(unsigned char* Data)
{
unsigned char ret_err=0;
TWCR = (1<<TWINT)|(1<< TWEN); // SLA + W 패킷 보내기
ret_err = TWI_TransCheck_ACK(MR_DATA_NACK);
*Data = TWDR; // no error, return 수신 데이터(포인터로)
return 0; // 정상 종료
}
// Read Packet function for Master
unsigned char TWI_Master_Receive(unsigned char Addr, unsigned char* Data)
{
unsigned char rec_data;
unsigned char ret_err=0;
ret_err = TWI_Start(); // START 신호 송신
if(ret_err != 0) return ret_err; // error시 종료
ret_err = TWI_Write_SLAR(Addr); // 슬레이브 주소 송신
if(ret_err != 0) return ret_err; // error시 종료
ret_err = TWI_Read_Data(&rec_data); // 데이터수신
if(ret_err != 0) return ret_err; // error시 종료
TWI_Stop(); // STOP 신호 송신
*Data = rec_data;
return 0; // 전상 종
}
/*****************************************************/
/* 슬레이브 수신기 모드에서의 수신 관련 함수 */
/*****************************************************/
// Slave 주소 설정 함수
void Init_TWI_Slaveaddr(unsigned char Slave_Addr)
{
TWAR = Slave_Addr;
}
// SLA 패킷에 대한 ACK 생성 함수
unsigned char TWI_Slave_Match_ACK()
{
// unsigned char ret_err=0;
TWCR = ((1<<TWINT)|(1<<TWEA) |(1<<TWEN));
// ACK 생성 모드 활성화
return TWI_TransCheck_ACK(SR_SLA_ACK);
// 패킷 수신 완료 대기 및 SLA + W 패킷에 대한 ACK 확인
}
// STOP 조건 수신 및 ACK 생성 함수
unsigned char TWI_Slave_Stop_ACK()
{
// unsigned char ret_err=0;
TWCR = ((1<<TWINT)|(1<<TWEA) |(1<<TWEN));
// ACK 생성 모드 활성화
return TWI_TransCheck_ACK(SR_STOP);
// STOP 신호 수신 대기
}
// 데이터 수신 함수
unsigned char TWI_Slave_Read_Data(unsigned char* Data)
// unsigned char* Data : 주소 값 입력
{
unsigned char ret_err=0;
TWCR = ((1<<TWINT)|(1<<TWEA) |(1<<TWEN));
// ACK 생성 모드 활성화
ret_err = TWI_TransCheck_ACK(SR_DATA_ACK);
if(ret_err != 0)
return ret_err; // if error, return error code
*Data = TWDR; // no error, return 수신 데이터
return 0; // 정상 종료
}
// Read Packet function for Slave
unsigned char TWI_Slave_Receive(unsigned char* Data)
{
unsigned char ret_err=0;
unsigned char rec_data;
ret_err = TWI_Slave_Match_ACK();
if(ret_err != 0) return ret_err; // error시 종료
ret_err = TWI_Slave_Read_Data(&rec_data);
if(ret_err != 0) return ret_err; // error시 종료
ret_err = TWI_Slave_Stop_ACK();
if(ret_err != 0) return ret_err; // error시 종료
*Data = rec_data; // 수신 데이터 반환
return 0; // 정상 종료
}
/*****************************************************************/
/* Master TX/RX Mixed function like EEPROM, Sonar etc */
/*****************************************************************/
unsigned char TWI_Master_Receive_ExDevice(unsigned char devAddr,unsigned char regAddr, unsigned char* Data)
{
unsigned char rec_data;
unsigned char ret_err=0;
ret_err = TWI_Start(); // START 신호 송신
if(ret_err != 0) return ret_err; // error시 종료
ret_err = TWI_Write_SLAW(devAddr); // 슬레이브 주소 송신
if(ret_err != 0) return ret_err; // error시 종료
ret_err = TWI_Write_Data(regAddr); // 레지스터주소 송신
if(ret_err != 0) return ret_err; // error시 종료
ret_err = TWI_Restart(); // Restart 송신
if(ret_err != 0) return ret_err; // error시 종료
ret_err = TWI_Write_SLAR(devAddr); // 슬레이브 레지스터 주소 송신
if(ret_err != 0) return ret_err; // error시 종료
ret_err = TWI_Read_Data_NACK(&rec_data); // 레지스터 데이터 수신(주소 전달)
if(ret_err != 0) return ret_err; // error시 종료
TWI_Stop(); // STOP 신호 송신
*Data = rec_data;
return 0;
}
#else // Not _USE_SAFTY_TWI_
/**************************************************************/
/* 마스터 송신기 모드에서의 송신 관련 함수(SIMPLY) */
/**************************************************************/
// 신호 전송 완료 검사 및 Status 확인 + Timeout Check
unsigned char TWI_TransCheck_ACK(unsigned char Stat)
{
unsigned int ExtDev_ErrCnt = 0;
while (!(TWCR & (1<<TWINT))); // 패킷 전송 완료될 때 까지 wait
{
if(ExtDev_ErrCnt++>ExtDev_ERR_MAX_CNT){ return 1; }
}
if ((TWSR & 0xf8) != Stat) return 1; // 전송 검사(ACK) : error시 1 반환
else return 0;
}
// START 전송
void TWI_Start()
{
TWCR = ((1<<TWINT) | (1<<TWSTA) | (1<<TWEN)); // START 신호 보내기
// while (TWCR & (1<<TWINT)) == 0x00); // START 신호 전송 완료될 때 까지 wait
TWI_TransCheck_ACK(TWI_START);
}
// SLA+W 패킷 전송
void TWI_Write_SLAW(unsigned char Addr)
{
TWDR = Addr; // SLA + W 패킷(슬레이브 주소+Write bit(Low))
TWCR = (1<<TWINT) | (1<<TWEN); // SLA + W 패킷 보내기
TWI_TransCheck_ACK(MT_SLA_ACK);
}
// 데이터 패킷 전송
void TWI_Write_Data(unsigned char Data)
{
TWDR = Data; // 데이터
TWCR = (1<<TWINT) | (1<< TWEN); // 데이터 패킷 송신
TWI_TransCheck_ACK(MT_DATA_ACK);
}
// STOP 전송
void TWI_Stop()
{
TWCR = ((1<<TWINT) | (1<<TWSTO) | (1<<TWEN)); // STOP 신호 보내기
}
// RESTART 전송
void TWI_Restart()
{
TWCR = ((1<<TWINT) | (1<<TWSTA) | (1<<TWEN)); // Restart 신호 보내기
TWI_TransCheck_ACK(TWI_RESTART);
}
// Write Packet function for Master
void TWI_Master_Transmit(unsigned char Data, unsigned char Addr)
{
TWI_Start(); // START 신호 송신
TWI_Write_SLAW(Addr); // 슬레이브 주소 송신
TWI_Write_Data(Data); // 데이터 송신
TWI_Stop(); // STOP 신호 송신
}
/****************************************************************/
/* 마스터 수신기 모드에서의 송신 관련 함수(SIMPLY) */
/****************************************************************/
// SLA+R 패킷 전송
void TWI_Write_SLAR(unsigned char Addr)
{
TWDR = Addr|0x01; // SLA + R 패킷(슬레이브 주소+Read bit(High))
TWCR = (1<<TWINT) | (1<<TWEN); // SLA + R 패킷 보내기
TWI_TransCheck_ACK(MR_SLA_ACK);
}
// 데이터 패킷 수신
unsigned char TWI_Read_Data()
{
TWCR = (1<<TWINT)|(1<< TWEN); // SLA + W 패킷 보내기
// while (!(TWCR & (1<<TWINT))); // 패킷 전송 완료될 때 까지 wait
TWI_TransCheck_ACK(MR_DATA_ACK);
return TWDR;
}
// 데이터 패킷 수신 with Nack
unsigned char TWI_Read_Data_NACK()
{
TWCR = (1<<TWINT)|(1<< TWEN); // SLA + W 패킷 보내기
// while (!(TWCR & (1<<TWINT))); // 패킷 전송 완료될 때 까지 wait
TWI_TransCheck_ACK(MR_DATA_NACK);
return TWDR;
}
// Read Packet function for Master
unsigned char TWI_Master_Receive(unsigned char Addr)
{
unsigned char rec_data;
TWI_Start(); // START 신호 송신
TWI_Write_SLAR(Addr); // 슬레이브 주소 송신
rec_data = TWI_Read_Data(); // 데이터수신
TWI_Stop(); // STOP 신호 송신
return rec_data;
}
/**************************************************************/
/* 슬레이브 수신기 모드에서의 수신 관련 함수(SIMPLY) */
/**************************************************************/
void Init_TWI_Slaveaddr(unsigned char Slave_Addr)
{
TWAR = Slave_Addr;
}
// SLA 패킷에 대한 ACK 생성 함수
void TWI_Slave_Match_ACK()
{
TWCR = ((1<<TWINT)|(1<<TWEA) |(1<<TWEN));
// ACK 생성 모드 활성화
TWI_TransCheck_ACK(SR_SLA_ACK);
}
// STOP 조건 수신 및 ACK 생성 함수
void TWI_Slave_Stop_ACK()
{
TWCR = ((1<<TWINT)|(1<<TWEA) |(1<<TWEN));
// ACK 생성 모드 활성화
TWI_TransCheck_ACK(SR_STOP);
}
// 데이터 수신 함수
unsigned char TWI_Slave_Read_Data()
{
unsigned char Data;
TWCR = ((1<<TWINT)|(1<<TWEA) |(1<<TWEN));
// ACK 생성 모드 활성화
TWI_TransCheck_ACK(SR_DATA_ACK);
Data = TWDR;
return Data;
}
unsigned char TWI_Slave_Receive()
{
unsigned char Data;
TWI_Slave_Match_ACK();
Data = TWI_Slave_Read_Data();
TWI_Slave_Stop_ACK();
return Data; // 읽은 데이터 반환
}
#endif
#endif
위는 TWI파일의 헤더파일이다. 나도 아직 완전이해는 못했지만 적어도 에러체크를 하는방식과 안하는 방식의 차이, 그리고 마스터모드의 송신, 마스터모드의 수신, 슬레이브 모드의 수신 등 여러 방향으로 쓸 수 있기에 조금 세심히 봐야한다. 나말고 블로그를 누가 본다면 우선 데이터시트들을 분석해서 해당 레지스터들이 뭘 하는건지, 코드 하나하나 읽어보는게 좋을 것 같다.
두번째 문제이다.
마스터
PC에서 수신되를 문자를 LCD와 슬레이브로 전송하는 프로그램 작성
슬레이브
마스터로부터 수신되는 문자를 LCD오 슬레이브 PCD로 전송하는 프로그램 작성.
어떻게보면 첫번째보다 간단한 문제이다! 단, LCD에서 입력받은 문자가 짤리거나 하는 현상이 없게 해보았다.

/*
* twi_usart_to_TWI_master.c
*
* Created: 2021-10-15 오전 12:41:03
* Author: 정민규
*/
#define _USE_SAFTY_TWI_
#include <mega128.h>
#include <delay.h>
#include "twi.h"
#include "lcd.h"
#include "usart.h"
unsigned char SLAVE_ADDR = 0x02;
void main(void)
{
unsigned char key =0,data;
int i=0;
LCD_Init();
LCD_Str("I2C Master Init"); //초기 설정
LCD_Pos(1,0);
LCD_Str("Send -> ");
DDRB = 0x00;
Init_TWI();
Init_USART1(0);
while (1)
{
delay_ms(200);
data = getch_USART1(); //데이터를 usart로 받아옴
if( TWI_Master_Transmit(data,SLAVE_ADDR) == 0) //TWI 통신 전송
{
}
}
}
마스터모드는 간단하다. 데이터를 건내주기만 하면된다.
/*
* twi_usart_to_TWI_slave.c
*
* Created: 2021-10-13 오후 12:56:12
* Author: 정민규
*/
#define _USE_SAFTY_TWI_
#define ENTER '\r'
#define MAXLEN 15
#include <mega128.h>
#include <delay.h>
#include "twi.h"
#include "lcd.h"
unsigned char SLAVE_ADDR = 0x02;
void main(void)
{
unsigned char ch = 0;
unsigned char str[MAXLEN]; //엔터키를 통한 줄바꿈을 위한 문자저장공간
char pos_row = 0, count =0,i;
bit pos_col =0;
LCD_Init();
Init_TWI();
Init_TWI_Slaveaddr(SLAVE_ADDR);
while (1)
{
// Please write your application code here
if(TWI_Slave_Receive(&ch) ==0)
{
delay_ms(100);
str[count] = ch;
count++;
//LCD_Pos(pos_col,pos_row);
LCD_Char(ch);
if(ch == ENTER){ //엔터키를 누르면 Str을 첫째줄에 출력
str[count-1] = 0x00;
LCD_Clear();
LCD_Pos(0,0);
LCD_Str(str);
LCD_Pos(1,0);
count = 0;
for (i=0;i<MAXLEN;i++)
{
str[i]=0;
}
}
if(count>MAXLEN) //16글자 이상일시 에러메시지 출력
{
LCD_Clear();
LCD_Pos(0,0);
LCD_Str("error_message");
for (i=0;i<MAXLEN;i++)
{
str[i]=0;
}
}
}
}
}
슬레이브에서도 사실 예전에 USART로 통신했던거나, 미니금고 프로젝트를 진행했을때와 비슷한 것 같다. USART에 관해서는 나중에 기록해 보겠다. 하더라도 내용은 부실할 수 있다. 적어도 아직까진 개인의 복습을 위한 블로그이다 ㅠ
'C_Atmega128' 카테고리의 다른 글
03_ MCP23008 (0) | 2021.10.30 |
---|---|
02_ TWI_SRF02의 이용 (0) | 2021.10.30 |