Harman 세미콘 아카데미/Atmega128a

2024.6.25 [ATmega128a]⑨ - Ultrasonic(int, ICP), EZ motor R300

U_Pong 2024. 6. 25. 20:02

2024.6.25 수업날

오늘은 어제 배웠던 HC-SR04(Ultrasonic) 초음파 센서를 가지고 INT, ICP 형식으로 코드를 작성하고

EZ moter r300으로 선풍기를 만드는 프로젝트를 진행하였다.


< Ultrasonic_int >

어제 배웠던 Ultrasonic 초음파 센서를 INT 형식으로 소스코드를 작성한다.

결과는 똑같지만, 소스코드의 내용에는 차이가 있다.

/*
 * def.h
 *
 * Created: 2024-06-25 오전 9:20:11
 *  Author: USER
 */ 

#ifndef DEF_H_
#define DEF_H_

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>

#endif /* DEF_H_ */

먼저, def.h라는 헤더파일이 추가되었다.

이는 #define, #include와 같이 모든 파일의 서두에 공통적으로 들어가는 부분을 def.h 헤더파일에 하나로 묶어 정리한 것이다.

때문에 이 아래로의 헤더파일에는 def.h 헤더파일을 불러오기 위해 #include "def.h" 라고 추가로 적어야한다.

/*
 * uart0.c
 *
 * Created: 2024-06-11 오후 2:11:35
 *  Author: USER
 */ 


#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>


void UART0_Init()
{
	UBRR0H = 0x00;	// 9~11 bit
	UBRR0L = 207;	// 9600bps 설정
	UCSR0A = (1<<U2X0);	// 2배속 설정
	// 비동기 모드, 8비트, 패리티비트 없음, 스톱비트 1개
	// UCSR0C = 0x06; // 초기값으로 대체 0000 0110
	UCSR0B |= (1<<RXEN0);	// 수신가능
	UCSR0B |= (1<<TXEN0);	// 송신가능
	
	UCSR0B |= (1<<RXCIE0);	// 수신 인터럽트 인에이블
}

void UART0_Transmit(char data)
{
	while(!(UCSR0A & (1<<UDRE0)));	// 송신 가능 하냐?(대기중), UDR이 비어 있는지? //비슷한 구문이 ctc쯤에 있을것이다
	UDR0 = data;
}

unsigned UART0_Recevie()
{
	while(!(UCSR0A & (1<<RXC0)));	// 수신 대기중
	return UDR0;
}

 

/*
 * uart0.h
 *
 * Created: 2024-06-11 오후 2:11:59
 *  Author: USER
 */ 

#ifndef UART0_H_
#define UART0_H_

#include "def.h"


void UART0_Init();
void UART0_Transmit(char data);
unsigned UART0_Recevie();

#endif /* UART0_H_ */

 

/*
 * ultrasonic.c
 *
 * Created: 2024-06-25 오전 9:17:45
 *  Author: USER
 */ 
#include "ultrasonic.h"

// 전역변수 선언
volatile uint16_t startCount;
volatile uint16_t endCount;
volatile uint16_t US_TCNT;


void ultraInit()
{
	TRIGGER_DDR |= (1<<TRIGGER_PIN);	// 트리거핀 출력 설정
	ECHO_DDR &= ~(1<<ECHO_PIN);			// 에코핀 입력 설정
	
	EICRA |= (1<<ISC31) | (1<<ISC30);	// Rising Edge 인터럽트
	EIMSK |= (1<<INT3);					// INT3 인터럽트 활성화
	
	TCCR1B |= (1<<CS11) | (1<<CS10);	// Timer1 의 64분주
	//TCCR1B |= (1<<CS21) | (1<<CS10);	// Timer1 의 1024분주
}

void ultraTrigger()
{
	TCNT1 = 0;
	TRIGGER_PORT &= ~(1<<TRIGGER_PIN);
	_delay_us(1);
	TRIGGER_PORT |= (1<<TRIGGER_PIN);
	_delay_us(10);
	TRIGGER_PORT &= ~(1<<TRIGGER_PIN);
	
}

ISR(INT3_vect)		//이거는 헤더파일에 복붙하지 않아도 됨, 내장되어있는거임, 인터럽트 서비스 루틴
{
	if(ECHO_PORT & (1<<ECHO_PIN))	// echo핀이 Rising 이라면~
	{
		startCount = TCNT1;		// 현재 카운트값을 startCount에 대입
		
		EICRA &= ~(1<<ISC30);
		EICRA |= (1<<ISC31);	// falling edge
		//printf("startCount : %d\n", startCount);
	}
	else
	{
		endCount = TCNT1;	//falling edge 에서의 count
		
		if(endCount >= startCount)
		{
			US_TCNT = endCount - startCount;
		}
		if(endCount < startCount)
		{
			US_TCNT = (0xffff - startCount) + endCount;
		}
		
		US_TCNT = endCount - startCount;
		EICRA |= (1<<ISC31) | (1<<ISC30);	// Rising Edge 인터럽트
		//printf("endCount : %d\n", endCount);
	}
}


uint16_t ultraDistance()
{
	// 64분주로 했을 때
	uint16_t distance = (uint16_t)(US_TCNT * 0.000004 * 34000)/2;	// 4us(0.000004), 340m-->34000cm
	
	// 1024분주로 했을 때
	//uint16_t distance = (uint16_t)(US_TCNT * 0.000064 * 34000)/2;	// 340m-->34000cm
	
	return distance;
}

Ultrasonic_Int를 실행하기 위해서는 ultrasonic.c 파일이 필요하다.

Int는 ISR 함수를 사용한다. ISR는 인터럽트 서비스 루틴의 약자이다.

또한 ultraDostace에 64분주로 설정했을 때의 계산식도 추가로 작성한다.

/*
 * ultrasonic.h
 *
 * Created: 2024-06-25 오전 9:17:57
 *  Author: USER
 */ 


#ifndef ULTRASONIC_H_
#define ULTRASONIC_H_

#include "def.h"

#define TRIGGER_DDR		DDRD
#define ECHO_DDR		DDRD
#define TRIGGER_PORT	PORTD	// 출력
#define ECHO_PORT		PIND	//입력 
#define TRIGGER_PIN		2
#define ECHO_PIN		3


void ultraInit();			//초기화
void ultraTrigger();		// 트리거 올려줌
uint16_t ultraDistance();	// 16진수로 거리 보내줌



#endif /* ULTRASONIC_H_ */

 

/*
 * I2C.c
 *
 * Created: 2024-06-24 오전 9:39:19
 *  Author: USER
 */ 
#include "I2C.h"

void I2C_Init()
{
	I2C_DDR |= (1<<I2C_SCL) | (1<<I2C_SDA);  // 출력 설정
	TWBR = 72;		// 100KHz
	// TWBR = 32;	// 200KHz
	// TWBR = 12;	// 400KHz
}


void I2C_Start()
{
	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
	while(!(TWCR & (1<<TWINT)));
}

void I2C_Stop()
{
	TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
}

void I2C_TxData(uint8_t data)
{
	TWDR = data;	//데이터를 받으면 8비트 레지스터에 집어넣음
	TWCR = (1<<TWINT) | (1<<TWEN);
	while(!(TWCR & (1<<TWINT)));		// 전송 완료 대기
}

void I2C_TxByte(uint8_t devAddrRW, uint8_t data)
{
	I2C_Start();
	I2C_TxData(devAddrRW);
	I2C_TxData(data);
	I2C_Stop();
}

 

/*
 * I2C.h
 *
 * Created: 2024-06-24 오전 9:39:30
 *  Author: USER
 */ 


#ifndef I2C_H_
#define I2C_H_

#include "def.h"

// 데이터 시트 보고 정해진 포트 위치를 지정해준것임
#define I2C_DDR DDRD
#define I2C_SCL PORTD0
#define I2C_SDA PORTD1

void I2C_Init();
void I2C_Start();
void I2C_Stop();
void I2C_TxData(uint8_t data);
void I2C_TxByte(uint8_t devAddrRW, uint8_t data);


#endif /* I2C_H_ */

 

/*
 * I2C_LCD.c
 *
 * Created: 2024-06-24 오전 9:40:07
 *  Author: USER
 */ 
#include "I2C_LCD.h"
uint8_t I2C_LCD_Data;

void LCD_Data4Bit(uint8_t data)	// 4bit
{
	I2C_LCD_Data = (I2C_LCD_Data & 0x0f) | (data & 0xf0);	//  상위4비트
	LCD_EnablePin();
	I2C_LCD_Data = (I2C_LCD_Data & 0x0f) | ((data & 0x0f) << 4);	//하위4비트
	LCD_EnablePin();
}


void LCD_EnablePin()
{
	I2C_LCD_Data &= ~(1<<LCD_E); // E Low 설정
	I2C_TxByte(LCD_DEV_ADDR, I2C_LCD_Data);
	
	I2C_LCD_Data |= (1<<LCD_E); // E High 설정
	I2C_TxByte(LCD_DEV_ADDR, I2C_LCD_Data);
	
	I2C_LCD_Data &= ~(1<<LCD_E); // E Low 설정
	I2C_TxByte(LCD_DEV_ADDR, I2C_LCD_Data);
	
	_delay_us(1800);
}

void LCD_WriteCommand(uint8_t commandData)
{
	I2C_LCD_Data &= ~(1<<LCD_RS);
	I2C_LCD_Data &= ~(1<<LCD_RW);
	LCD_Data4Bit(commandData);
}

void LCD_WriteData(uint8_t charData)
{
	I2C_LCD_Data |= (1<<LCD_RS);
	I2C_LCD_Data &= ~(1<<LCD_RW);
	LCD_Data4Bit(charData);
}

void LCD_BackLightOn()
{
	I2C_LCD_Data |= (1<<LCD_BACKLIGHT);
	I2C_TxByte(LCD_DEV_ADDR, I2C_LCD_Data);
}

void LCD_GotoXY(uint8_t row, uint8_t col)
{
	col %= 16;
	row %= 2;
	uint8_t address = (0x40 * row) + col;
	uint8_t command = 0x80 + address;
	LCD_WriteCommand(command);
}

void LCD_WriteString(char *string)
{
	for (uint8_t i = 0; string[i]; i++)
	{
		LCD_WriteData(string[i]);
	}
}

void LCD_WriteStringXY(uint8_t row, uint8_t col, char *string)
{
	LCD_GotoXY(row, col);
	LCD_WriteString(string);
}

void LCD_Init()
{
	I2C_Init();
	_delay_ms(20);
	LCD_WriteCommand(0x03);
	_delay_ms(10);
	LCD_WriteCommand(0x03);
	_delay_ms(1);
	LCD_WriteCommand(0x03);
	
	LCD_WriteCommand(0x02);
	LCD_WriteCommand(COMMAND_4BIT_MODE);
	LCD_WriteCommand(COMMAND_DISPLAY_OFF);
	LCD_WriteCommand(COMMAND_DISPLAY_CLEAR);
	LCD_WriteCommand(COMMAND_ENTRY_MODE);
	LCD_WriteCommand(COMMAND_DISPLAY_ON);
	LCD_BackLightOn();
}

 

/*
 * I2C_LCD.h
 *
 * Created: 2024-06-24 오전 9:40:21
 *  Author: USER
 */ 
#ifndef I2C_LCD_H_
#define I2C_LCD_H_

#include "def.h"
#include "I2C.h"

#define LCD_RS			0
#define LCD_RW			1
#define LCD_E			2
#define LCD_BACKLIGHT	3

#define LCD_DEV_ADDR	(0x27<<1)  // 주소는 0x27, <<은 W를 유지하기 위해


// lcd4bit.h에서 복사해온 코드
#define COMMAND_DISPLAY_CLEAR		0x01	// Clear all display
#define COMMAND_DISPLAY_ON			0x0C	// 화면 ON, 커서 OFF, 커서 점멸 OFF
#define COMMAND_DISPLAY_OFF			0x08	// 화면 OFF, 커서 OFF, 커서 점멸 OFF
#define COMMAND_ENTRY_MODE			0x06

#define COMMAND_4BIT_MODE			0x28	// 4비트, 화면 2행, 5X8 Font-->8비트

void LCD_Data4Bit(uint8_t data);	// 4bit


void LCD_EnablePin();
void LCD_WriteCommand(uint8_t commandData);
void LCD_WriteData(uint8_t charData);

void LCD_BackLightOn();		//이거는 추가한거임

void LCD_GotoXY(uint8_t row, uint8_t col);
void LCD_WriteString(char *string);
void LCD_WriteStringXY(uint8_t row, uint8_t col, char *string);
void LCD_Init();


#endif /* I2C_LCD_H_ */

 

/*
 * Ultrasonic_INT.c
 *
 * Created: 2024-06-25 오전 9:10:07
 * Author : USER
 */ 

#include "def.h"	// 총집편 설정 파일
#include "I2C_LCD.h"
#include "uart0.h"
#include "ultrasonic.h"

FILE OUTPUT = FDEV_SETUP_STREAM(UART0_Transmit, NULL, _FDEV_SETUP_WRITE);


int main(void)
{
	uint16_t distance;
   
    UART0_Init();
    ultraInit();
   
    stdout = &OUTPUT;
   
   
    sei();
	
    while (1) 
    {
		ultraTrigger();
		_delay_ms(50);		// 인터럽트 시간 확보
		distance = ultraDistance();
		printf("Distance : %d cm\r\n", distance);
		_delay_ms(1000);
    }
}

 

위의 소스코드를 실행하고 comportmaster를 실행하면 아래와 같이 나타난다.

마찬가지로 152~156cm는 초음파 센서가 꽂혀있는 브레드보드를 천장과 수평으로 세웠을 때,

초음파센서와 강의실 천장까지의 거리를 나타낸다.

 

 

< Ultrasonic_INT7 >

이번에는 초음파 센서의 echo 핀 위치를 바꾼 후 소스코드를 작성해본다.

아래는 ATmega128a의 데이터 시트인데, INT7이 위치하는 PE7번을 사용할 것이다.

 

이번에는 Ultrasonic_INT의 소스코드 중에서

def.h, uart0.c, uart.h, I2C.c, I2C.h, I2C_LCD.h, I2C_LCD.c, main.c 파일을 복사+붙여넣기 한 후

ultrasonic.c, ultrasonic.h의 파일을 수정하여 작성한다.

Ultrasonic.c, Ultrasonic.h 소스코드
/*
 * ultrasonic.c
 *
 * Created: 2024-06-25 오전 9:17:45
 *  Author: USER
 */ 
#include "ultrasonic.h"

// 전역변수 선언
volatile uint16_t startCount;
volatile uint16_t endCount;
volatile uint16_t US_TCNT;

volatile uint8_t risingEdge = 1;


void ultraInit()
{
	TRIGGER_DDR |= (1<<TRIGGER_PIN);	// 트리거핀 출력 설정
	ECHO_DDR &= ~(1<<ECHO_PIN);			// 에코핀 입력 설정
	
	EICRB |= (1<<ISC70);
	// EICRA |= (1<<ISC31) | (1<<ISC30);	// Rising Edge 인터럽트
	EIMSK |= (1<<INT7);					// INT7 인터럽트 활성화
	
	TCCR1B |= (1<<CS11) | (1<<CS10);	// Timer1 의 64분주
	//TCCR1B |= (1<<CS21) | (1<<CS10);	// Timer1 의 1024분주
}

void ultraTrigger()
{
	TCNT1 = 0;
	
	TRIGGER_PORT &= ~(1<<TRIGGER_PIN);
	_delay_us(1);
	TRIGGER_PORT |= (1<<TRIGGER_PIN);
	_delay_us(10);
	TRIGGER_PORT &= ~(1<<TRIGGER_PIN);
	
}

// 7번이 인터럽트 걸렸을 때
ISR(INT7_vect)		//이거는 헤더파일에 복붙하지 않아도 됨, 내장되어있는거임, 인터럽트 서비스 루틴
{
	if(risingEdge)
	{
		startCount = TCNT1;
		risingEdge = 0;
	}
	else
	{
		endCount = TCNT1;
		risingEdge = 1;
	}
}


uint16_t ultraDistance()
{
	uint16_t pulseWidth = endCount - startCount;
	uint32_t timeUs = pulseWidth * 4;
	uint16_t distance = (timeUs * 34)/2000;
	return distance;
	
	
	//US_TCNT =  endCount - startCount;
	
	// 64분주로 했을 때
	//uint16_t distance = (uint16_t)(US_TCNT * 0.000004 * 34000)/2;	// 4us(0.000004), 340m-->34000cm
	
	//return distance;
	
	// 1024분주로 했을 때
	//uint16_t distance = (uint16_t)(US_TCNT * 0.000064 * 34000)/2;	// 340m-->34000cm

이번에는 INT7에서의 결과를 살펴보는 것이기 때문에 ISR(INT7_vect) 로 수정하고 그 아래의 내용도 위의 소스코드처럼 변경해야 한다.

 

또한, Ultrasonic.c.의 UltraDistance에서

아래의 사진과 같이 윗 부분의 주석을 풀어 실행시켜보고, 아래 부분의 주석을 풀어 실행시킨 다음

ComportMaster의 결과가 같은지 확인해야한다.

/*
 * ultrasonic.h
 *
 * Created: 2024-06-25 오전 9:17:57
 *  Author: USER
 */ 


#ifndef ULTRASONIC_H_
#define ULTRASONIC_H_

#include "def.h"

#define TRIGGER_DDR		DDRD
#define ECHO_DDR		DDRE
#define TRIGGER_PORT	PORTD	// 출력
#define ECHO_PORT		PINE	//입력 
#define TRIGGER_PIN		2
#define ECHO_PIN		7


void ultraInit();			//초기화
void ultraTrigger();		// 트리거 올려줌
uint16_t ultraDistance();	// 16진수로 거리 보내줌




#endif /* ULTRASONIC_H_ */

ATmega 128a 데이터 시트에서 본 것처럼

INT7은 PE7번 이기 때문에 ECHO_DDR를 DDRE로, Echo 핀을 7번으로 수정하였다.

 

아래는 Ultrasonic_INT7의 ComportMaster의 결과이다.

 

 

< Ultrasonic_ICP >

ICP란 Input Capture 라는 의미로,

상승 에지(rising edge) 또는 하강 에지(falling edge)가 발견되면 이 때의 TCNT 값이 ICR 레지스터에 기록된다.

즉, TCNT 값이 capture 되어 ICR에 기록되는 것이다.

edge 발견 시점에 인터럽트를 발생시킬 수 있어서 입력 신호의 시간, duty_cycle, 주파수 측정 등에 사용할 수 있다.

 

이번에도 Ultrasonic_ICP 소스코드를 작성할 때 Ultrasonic_INT7 소스코드에서

ultrasonic.h, def.h, uart0.c, uart.h, I2C.c, I2C.h, I2C_LCD.h, I2C_LCD.c, main.c 파일을 복사+붙여넣기 한 후

ultrasonic.c 파일을 수정하여 작성한다.

 

ultrasonic.h의 파일을 수정하지 않는 이유는

아래의 ATmega 128a 데이터시트에서 ICP3이 PE7번으로 동일하기 때문이다.

 

Ultrasonic.c 소스코드 수정
/*
 * ultrasonic.c
 *
 * Created: 2024-06-25 오전 9:17:45
 *  Author: USER
 */ 
#include "ultrasonic.h"

// 전역변수 선언
volatile uint16_t startCount;
volatile uint16_t endCount;
//volatile uint16_t US_TCNT;

volatile uint8_t risingEdge = 1;


void ultraInit()
{
	TRIGGER_DDR |= (1<<TRIGGER_PIN);	// 트리거핀 출력 설정
	ECHO_DDR &= ~(1<<ECHO_PIN);			// 에코핀 입력 설정
	
	// Timer Input Capture, 64분주    ,     추가한 부분
	TCCR3B |= (1<<ICES3) | (1<<CS31) | (1<<CS30);
	ETIMSK |= (1<<TICIE3);	//Input Catpure Enable
	
}

void ultraTrigger()
{
	TCNT1 = 0;
	
	TRIGGER_PORT &= ~(1<<TRIGGER_PIN);
	_delay_us(1);
	TRIGGER_PORT |= (1<<TRIGGER_PIN);
	_delay_us(10);
	TRIGGER_PORT &= ~(1<<TRIGGER_PIN);
	
}

// 7번이 인터럽트 걸렸을 때
ISR(TIMER3_CAPT_vect)		//이거는 헤더파일에 복붙하지 않아도 됨, 내장되어있는거임, 인터럽트 서비스 루틴
{
	// 추가한 부분
	if(risingEdge)
	{
		startCount = ICR3;	// 요게 ICR3L, ICR3H 2개인데..
		TCCR3B &= ~(1<<ICES3);	// 하강엣지로 변경
		risingEdge = 0;
	}
	else
	{
		endCount = ICR3;
		TCCR3B |= (1<<ICES3);	// 상승엣지
		risingEdge = 1;
	}
}


uint16_t ultraDistance()
{
	uint16_t pulseWidth = endCount - startCount;
	uint32_t timeUs = pulseWidth * 4;
	uint16_t distance = (timeUs * 34)/2000;
	return distance;
	
	
	//US_TCNT =  endCount - startCount;
	
	// 64분주로 했을 때
	//uint16_t distance = (uint16_t)(US_TCNT * 0.000004 * 34000)/2;	// 4us(0.000004), 340m-->34000cm
	
	//return distance;
	
	// 1024분주로 했을 때
	//uint16_t distance = (uint16_t)(US_TCNT * 0.000064 * 34000)/2;	// 340m-->34000cm
}

ICP를 사용하기 때문에 ultraInit 에서 데이터시트를 보고 관련된 레지스터로 수정하였고,

ISR(TIMER3_CAPT_vect)에서 상승 에지(rising edge) 또는 하강 에지(falling edge)가 나타나도록 수정하였다.

 

 

< EZ MOTOR R300 >

이번에는 EZ MOTOR R300을 사용하여 Uart, FASTPWM의 소스코드 내용을 가지고

ComportMaster에서 출력된 숫자나 문자에 따라 50% 출력, 75% 출력, 100% 출력, 0% 출력이 나타나는 프로젝트를 만들어본다.

EZ MOTOR R300에 실리콘 팬을 부착한 모습
EZ MOTOR R300의 구성품

 

Fan_making 소스코드(다른분의 코드를 기반으로 수정한 코드입니다. 감사합니다)
/*
 * FAN_making.c
 *
 * Created: 2024-06-25 오후 2:07:35
 * Author : USER
 */ 

#define F_CPU 16000000UL  // CPU 클럭 주파수를 16MHz로 설정
#include <avr/io.h>       // AVR 매크로 및 레지스터 정의
#include <util/delay.h>   // _delay_ms() 함수를 사용하기 위한 헤더 파일

void UART0_Init()  // UART 통신 초기화 함수
{
	// 비동기, 8비트, 패리티 비트 없음, 스톱 비트 1개 설정
	UBRR0H = 0x00;      // 송수신 속도 설정 레지스터 High byte 초기화 (9-11 bit)
	UBRR0L = 207;       // 송수신 속도 설정 레지스터 Low byte 설정 (9600bps)
	UCSR0A = (1<<U2X0); // 2배속 모드 설정 (속도 향상)
	UCSR0B |= (1<<RXEN0); // 수신 가능 설정
	UCSR0B |= (1<<TXEN0); // 송신 가능 설정
}

void UART0_Transmit(char data)  // UART로 문자 전송 함수
{
	while(!(UCSR0A & (1<<UDRE0))); // 데이터 레지스터가 비어있는지 확인하여 대기
	UDR0 = data;                   // 데이터 레지스터에 데이터 쓰기
}

unsigned UART0_Receive()  // UART로부터 문자 수신 함수
{
	while(!(UCSR0A & (1<<RXC0))); // 데이터 수신 대기
	return UDR0;                  // 수신된 데이터 반환
}

void fan_Init()  // 선풍기 제어 핀 초기화 함수
{
	DDRB |= (1 << DDB5);  // PB5 핀을 출력으로 설정 (OC1A 핀)
}

void set_PWM_duty_cycle(uint16_t duty_cycle)  // PWM 듀티 사이클 설정 함수
{
	OCR1A = duty_cycle; // PWM의 듀티 사이클 설정
}

void PWM_Init()  // PWM 초기화 함수
{
	// 빠른 PWM 모드, 비반전 모드 설정
	TCCR1A = (1 << COM1A1) | (1 << WGM11);
	// 분주비 64 설정
	TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11) | (1 << CS10);
	// TOP 값 설정 (ICR1 = 2499)
	ICR1 = 2499;
}

int main(void)
{
	UART0_Init();   // UART 초기화
	fan_Init();     // 선풍기 제어 핀 초기화
	PWM_Init();     // PWM 초기화
	
	while (1)  // 무한 루프
	{
		char command = UART0_Receive(); // UART로부터 명령 문자 수신
		
		// 수신된 명령에 따라 선풍기 제어
		switch (command)
		{
			case '0':   // 0: 선풍기 끄기
			set_PWM_duty_cycle(0); // 듀티 사이클 0 설정
			UART0_Transmit('0');
			break;
			
			case '1':   // 1: 선풍기 LOW 속도
			set_PWM_duty_cycle(625); // 듀티 사이클 25% 설정
			UART0_Transmit('1');
			break;
			
			case '2':   // 2: 선풍기 MEDIUM 속도
			set_PWM_duty_cycle(1250); // 듀티 사이클 50% 설정
			UART0_Transmit('2');
			break;
			
			case '3':   // 3: 선풍기 HIGH 속도
			set_PWM_duty_cycle(1875); // 듀티 사이클 75% 설정
			UART0_Transmit('3');
			break;
			
			case '4':   // 4: 선풍기 최대 속도
			set_PWM_duty_cycle(2498); // 듀티 사이클 100% 설정
			UART0_Transmit('4');
			break;
			
			default:
			break; // 잘못된 명령인 경우 아무 작업도 하지 않음
		}
	}
}

위의 소스코드에서 EZ MOTOR R300을 ATmega 128a와 연결할 때

PB5번을 출력으로 설정하여 EZ MOTOR R300과 같이 동봉된 한 케이블에는 PB5번에, 다른 케이블에는 gnd에 연결해야한다.

EZ MOTOR R300과 같이 동봉된 케이블의 극성은 없지만, 실리콘 팬이 돌아가는 방향이 반대가 된다.

 

Fan_Init, PWM_Init을 FastPWM의 소스코드를 기반으로 추가작성하였다.

또한, Fan_making 소스코드에 ComportMaster의 Recv 부분에 숫자로 표시되도록 소스코드를 작성하였다.

 

아래는 Fan_making 소스코드의 ComportMaster 결과이다.

 

fan_making 결과

Ultrasonic(int, ICP), EZ motor R300 끝!