본문 바로가기
# Semiconductor/- Semicon Academy

[Harman 세미콘 아카데미] 24일차 - ATmega128(PWM 활용, Servo Motor)

by Graffitio 2023. 7. 20.
[Harman 세미콘 아카데미] 24일차 - ATmega128(PWM 활용, Servo Motor)
728x90
반응형
[참조]

 

<Mode 사용법>

https://rangvest.tistory.com/manage/newpost/101?type=post&returnURL=https%3A%2F%2Frangvest.tistory.com%2Fmanage%2Fposts 

 

https://rangvest.tistory.com/manage/newpost/101?returnURL=https%3A%2F%2Frangvest.tistory.com%2Fmanage%2Fposts&type=post

 

rangvest.tistory.com

 

<Register 설명>

https://rangvest.tistory.com/manage/newpost/73?type=post&returnURL=https%3A%2F%2Frangvest.tistory.com%2Fmanage%2Fposts%3Fcategory%3D-3%26page%3D3%26searchKeyword%3D%26searchType%3Dtitle%26visibility%3Dall 

 

https://rangvest.tistory.com/manage/newpost/73?returnURL=https%3A%2F%2Frangvest.tistory.com%2Fmanage%2Fposts%3Fcategory%3D-3%26page%3D3%26searchKeyword%3D%26searchType%3Dtitle%26visibility%3Dall&type=post

 

rangvest.tistory.com

 


 

[PWM 활용]

 

CTC mode

: Clear Timer on Compare Match Mode

위 차트와 같이, TCNTn = OCR0 에서 Compare match interrupt가 발생할 때마다 OCn의 모양이 바뀌는 것을 볼 수 있다.

COMn1, COMn0 세팅을 조작하여 interrupt 발생 시, toggle할 수도 0 또는 1로 바꿀수도 아니면 출력을 차단할 수도 있다.

 

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



// CTC mode //
int main(void)
{
	//DDRB = 0x10;
	//DDRB = 0b00010000; // 세 개 다 같은 의미
    DDRB = (1<<PINB4); // PORTB의 PIN4 출력 설정
	//TCCR0 = (0<<FOC0);
	TCCR0 |= (1<<WGM01) | (0<<WGM00); // CTC mode
	TCCR0 |= (0<<COM01) | (1<<COM00); // Toggle OC0 on compare match
	TCCR0 |= (1<<CS02) | (0<<CS01) | (0<<CS00); // 64분주
	//TCCR0 = (1<<WGM01) | (1<<COM00) | (1<<CS02);
	//TCCR0 = 0x1C; // 0b0001_1100
	//TCCR0 = 0x1D; // 0b0001_1101
	OCR0 = 124; // 64분주, 1,000Hz
	//OCR0 = 249; // 128분주, 250Hz
		
    while (1) 
    {
		while((TIFR & 0x02) == 0);
		TIFR = 0x02;
		//OCR0 = 124;
		//OCR0 = 0;
    }
}

위 : 1000 Hz&nbsp; &nbsp; &nbsp; &nbsp;/&nbsp; &nbsp; &nbsp; &nbsp;아래 : 250Hz
인터럽트 발생하면, 플래그 세우는 레지스터

1) OCF0

: 타이머/카운터0와 OCR0의 출력 비교 레지스터의 데이터간 비교 일치가 발생하면, OCF0 bit가 1로 set된다.

ISR 함수로 인해(인터럽트 발생하면) 클리어되고, 해당 bit에 1을 넣어주면 클리어된다(헷갈릴 수 있음)

▶ Compare match가 일어났는가? 를 체크하는 bit

 

2) TOV0

: 타이머/카운터0에서 Overflow interrupt가 발생하면, TOV0가 1로 set된다.

ISR 함수로 인해(인터럽트 발생하면) 클리어되고, 해당 bit에 1을 넣어주면 클리어된다

▶ Overflow가 일어났는가? 를 체크하는 bit

 

 

※ OC 와 OC PIN,  OCR 비교

    - OC : Output Compare의 약자로, 하드웨어적으로 타이머의 신호를 전달

    - OC PIN : 타이머 발생 시, 특정 작업을 수행하는 핀의 이름(OCnB 등)
    - OCR : 출력 비교 레지스터.(OCRxA 등)

                계속해서 TCNT와 비교되며 비교의 결과가 일치하게 되면

                외부 핀인 OC단자에 신호가 출력되도록 설정할 수 있다.

 

 

Normal Mode

 

x축을 올리는 의미이기에 블러 처리함.

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

 // Normal Mode //
int main()
{
	DDRC = 0xff; // Normal mode는 아무 포트에서나 쓸 수 있다.(8개 포트에서 다 반전되는 중)
	PORTC = 0x00; // 출력은 0부터 시작
	TCCR0 |= (0<<WGM01) | (0<<WGM00); // Normal mode
	TCCR0 |= (0<<COM01) | (1<<COM00); // Compare Match - Toggle 모드
                                      // Normal mode에서는 OC0핀이 사용되지 않으므로 출력에 영향을 주지 않는다.
	TCCR0 |= (1<<CS02) | (0<<CS01) | (1<<CS00);  // 128분주
	TCNT0 = 131; // 타이머/카운터의 초기값을 설정(131~255 범위를 가짐)
                 // x축을 끌어 올린 것으로 보면 된다.
	
	while(1)
	{
		// 타이머/카운터0의 Overflow를 기다림.
        	// Overflow가 발생하면, TOV0 플래그가 1로 set
        	while((TIFR & 0x01) == 0) // TOV0와 비교해야 하므로, 0x01
		PORTC = ~PORTC; // PORTC의 출력을 반전

		TCNT0 = 131; // 타이머/카운터0의 초기값을 재설정하여 주기를 조절한다.
        
		TIFR = 0x01; // TOV0 클리어
	}
}

 

 

 

8bit Fast PWM

OCRn 값이 변하더라도, TOP값은 일정하므로 주기는 동일하다.

비반전 모드 기준, High로 출력되다가 TCNTn = OCRn 이 되면 Low로 출력된다.

이후 TOP에서 Overflow되면 다시 High

※ Duty Cycle 계산 방법

     ▶ N % = TOP x N/100

 

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

// 8bit Fast PWM Mode //
int main()
{
	DDRB = (1<<PINB4); // PINB4 에서 출력
	TCCR0 |= (1<<WGM01) | (1<<WGM00); // Fast PWM mode
	TCCR0 |= (1<<COM01) | (1<<COM00); // 비반전 모드
	TCCR0 |= (1<<CS02) |(1<<CS01) | (0<<CS00); // 256분주
	// OCR0 = 191; // duty cycle 75%로 변경
	
	while (1)
	{
		for (int i=0 ; i < 255 ; i++) // 0~100%까지 연속적으로 출력하라.
		{
			OCR0 = i;
			_delay_ms(10); 
		}
	}	
}

 

16bit Fast PWM 

 

8bit Fast PWM과 달리, 레지스터와 기능들이 더 많은 것을 볼 수 있다
ICRn 조정으로 주기 변경 가능

8bit Fast PWM과 다른 점은, TOP 값의 조절이 가능하여 주기 변경을 할 수 있다는 점이다.

또한 Wave Generation Mode가 4가지인 8bit Fast PWM에 비해 16가지로 훨씬 다양하다.

이와 같은 특징을 활용하여, 서보 모터의 기동이나 각종 부품들의 스펙에 맞게 구현파를 출력할 수 있다.

 

ICRn 조절 방법

 

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

// 16bit Fast PWM Mode //
// Timer1 PWM(mode 14) OC1A 100Hz
int main() // 64분주, 100Hz 만들 것이다.  -> TOP = 2499
{
	DDRB = (1<<PINB5); // OC1A PIN
	TCCR1A |= (1<<WGM11) | (0<<WGM10); // Fast PWM(mode 14) 세팅
	TCCR1B |= (1<<WGM13) | (1<<WGM12);
	TCCR1A |= (1<<COM1A1) | (0<<COM1A0); // 비반전모드 & OCRnA 쓸 것이므로
	//TCCR1A |= (1<<COM1B1) | (0<<COM1B0); // OCRnB
	//TCCR1A |= (1<<COM1C1) | (0<<COM1C0); // OCRnC
	TCCR1B |= (0<<CS12) | (1<<CS11) | (1<<CS10);
	TCCR1C = 0x00; // default가 0이라 안 써줘도 됨
	
	ICR1 = 2499; // 0~2499까지 카운팅하는 것 반복한다는 의미
	
	while (1)
	{
		for (uint16_t i = 0; i < 2500 ; i++) // int는 4byte, uint16_t도 4byte
		{
			OCR1A = i;
			_delay_ms(5);
		}
	}
}

 

 

[Servo Motor]

 

1. Servo Motor란?

Servo Motor(SG90)

: 명령에 따라 정확한 위치 및 속도를 제어할 수 있는 Motor

원하는 각도와 속도를 명령에 따라 정밀하게 제어할 수 있다.

 

2. Servo Motor의 원리

SG90 기준

PWM의 Duty Cycle에 따라서 각도를 변환시킬 수 있고

회전 속도는 각도 변화(△Duty cycle) 사이에 딜레이를 추가해주면 속도 조절도 가능하다.

해당 서보 모터는 0~180˚의 범위를 가지며 360˚의 범위를 가진 서보모터도 존재한다.

 

 

3. Connection Diagram(SG90)

Connection Diagram

 

4. Servo Motor의 동작

 

<0˚, 90˚, 180˚ 회전 동작>

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

// 16bit Fast PWM Mode //
int main() // 64분주, 50Hz
{
	DDRB = (1<<PINB5);
	TCCR1A |= (1<<WGM11) | (0<<WGM10); // Fast PWM(mode 14) 세팅
	TCCR1B |= (1<<WGM13) | (1<<WGM12);
	TCCR1A |= (1<<COM1A1) | (0<<COM1A0);
	TCCR1B |= (0<<CS12) | (1<<CS11) | (1<<CS10);
	TCCR1C = 0x00; // default가 0이라 안 써줘도 됨

	ICR1 = 4999;
	
	while (1)
	{
		OCR1A = 125; // 1ms (0')
		_delay_ms(500);
		
		OCR1A = 375; // 1.5ms (90')
		_delay_ms(500);
		
		OCR1A = 625; // 2ms (180')
		_delay_ms(500);
	}
}

 

<각도 조절 함수를 활용한 동작1>

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

void servo_stop();
void servo_run(uint8_t degree);

// 16bit Fast PWM Mode //
int main() // 64분주, 50Hz
{
	DDRB = (1<<PINB5);
	TCCR1A |= (1<<WGM11) | (0<<WGM10); // Fast PWM(mode 14) 세팅
	TCCR1B |= (1<<WGM13) | (1<<WGM12);
	TCCR1A |= (1<<COM1A1) | (0<<COM1A0);
	TCCR1B |= (0<<CS12) | (1<<CS11) | (1<<CS10);
	TCCR1C = 0x00; // default가 0이라 안 써줘도 됨

	ICR1 = 4999; // 0~2499까지 카운팅하는 것 반복한다는 의미
	
	while (1)
	{
		servo_run(0);
		_delay_ms(1500);
		servo_run(45);
		_delay_ms(1500);
		servo_run(90);
		_delay_ms(1500);
		servo_run(180);
		_delay_ms(1500);
	}
}


// 모터 stop //
void servo_stop()
{
	TCCR1A &= ~((1<<COM1A1) | (1<<COM1A0)); // PWM 출력 안 되도록
}


// 회전 각도 설정 함수 //
void servo_run(uint8_t degree)
{
	// 0도 : 125 / 180도 : 625
	uint16_t degree_value;	
	TCCR1A |= (1<<COM1A1); // PWM 다시 출력되도록
	if (degree_value < 0) // 제한 범위를 벗어나 고장나지 않도록 설정
	{
		degree = 0;
	}
	else if(degree > 180)
	{
		degree = 180;
	}
	degree_value = (uint16_t)((degree/180.0)*500 + 125); // 우리가 흔히 쓰는 각도로 표현하기 위한 식
	OCR1A = degree_value;
}

 

<각도 조절 함수를 활용한 동작2>

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

void servo_stop();
void servo_run(uint8_t degree);

// 16bit Fast PWM Mode //
int main() // 64분주, 50Hz
{
	DDRB = (1<<PINB5);
	TCCR1A |= (1<<WGM11) | (0<<WGM10); // Fast PWM(mode 14) 세팅
	TCCR1B |= (1<<WGM13) | (1<<WGM12);
	TCCR1A |= (1<<COM1A1) | (0<<COM1A0);
	TCCR1B |= (0<<CS12) | (1<<CS11) | (1<<CS10);
	TCCR1C = 0x00; // default가 0이라 안 써줘도 됨

	ICR1 = 4999; // 0~2499까지 카운팅하는 것 반복한다는 의미
	
	while (1)
	{
		for (int i = 0; i<180 ; i++)
		{
			servo_run(i);
			_delay_ms(15);
		}
		for (int i = 180 ; i>0 ; i--)
		{
			servo_run(i);
			_delay_ms(10);
		}
	}
}


void servo_stop()
{
	TCCR1A &= ~((1<<COM1A1) | (1<<COM1A0)); // PWM 출력 안 되도록
}

void servo_run(uint8_t degree)
{
	// 0도 : 125 / 180도 : 625
	uint16_t degree_value;	
	TCCR1A |= (1<<COM1A1); // PWM 다시 출력되도록
	if (degree_value < 0)
	{
		degree = 0;
	}
	else if(degree > 180)
	{
		degree = 180;
	}
	degree_value = (uint16_t)((degree/180.0)*500 + 125); // 우리가 흔히 쓰는 각도로 표현하기 위한 식
	OCR1A = degree_value;	
}

 

728x90
반응형