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

[Harman 세미콘 아카데미] 23일차 - ATmega128(FND count, PWM, Buzzer, Motor driver)

by Graffitio 2023. 7. 19.
[Harman 세미콘 아카데미] 23일차 - ATmega128(FND count, PWM, Buzzer, Motor driver)
728x90
반응형
[FND Count]

 

※ 기능 추가

+ FND overfolw 시, LED_Bar로 출력

+ Up / Down Count

 

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

#define		FND_DATA_DDR	DDRA  // 데이터 포트
#define		FND_SELECT_DDR	DDRF  // 셀렉트 포트(자릿수 선택)
#define		FND_DATA_PORT	PORTA // 0 ~ 7
#define		FND_SELECT_PORT	PORTF // 0 ~ 4

// 디스플레이 함수 선언 //
void FND_Display(uint16_t data); // 8bit면 255까지밖에 없기 때문에, 4자리 다 쓰려고 16bit


volatile uint32_t time_tick;
uint16_t count;
volatile uint8_t start_stop;
uint16_t count_ovfl;
uint8_t LED_Data;
uint8_t up_down;



ISR(TIMER0_OVF_vect)
{
	time_tick++;
	if(up_down%2)
	{
		if(count > 0)
		{
			if(time_tick == 5)
			{
				count--;
				time_tick = 0;
			}
		}
		else count = 150;
	}
	else
	{
		if (count<150)
		{
			if (time_tick == 5)  // 2ms마다 ISR 실행되므로 100ms마다 count++
			{
				count++;
				time_tick = 0;
			}
		}
		else count = 0;
	}
}

ISR(INT4_vect) // start/stop
{
	if (start_stop%2)
		TIMSK = (1<<TOIE0);
	else TIMSK = (0<<TOIE0);
	start_stop++;
}

ISR(INT5_vect) // reset
{
	start_stop = 0;
	count = 0;
	count_ovfl = 0;
	LED_Data = 0;
	PORTD = LED_Data;
}

ISR(INT6_vect)
{
	up_down++;
	TIMSK = (1<<TOIE0);		
}



int main()
{
	FND_DATA_DDR = 0xff; // 출력 모드
	FND_SELECT_DDR = 0xff; // 출력 모드
	FND_DATA_PORT = 0x00; // 0부터 시작
	DDRE &= ~(1<<DDRE4) | ~(1<<DDRE5) | ~(1<<DDRE6);
	DDRD = 0xff;
	PORTD = 0x00;
	
	
	
	TCCR0 = (1<<CS02) | (1<<CS01) | (0<<CS00); // 256 분주
	TCNT0 = 131;
	EICRB |= (1<<ISC41) | (0<<ISC40) | (1<<ISC51) | (0<<ISC50) | (1<<ISC61) | (0<<ISC60);
	EIMSK |= (1<<INT4) | (1<<INT5) | (1<<INT6);
	TIMSK = (1<<TOIE0);
	
	sei();

	while(1)
	{
		FND_Display(count);
		_delay_ms(4);
		if(up_down%2)
		{
			if (count == 0)
			{
				_delay_ms(10);
				if (count_ovfl>0)
				{
					LED_Data &= ~(1<<count_ovfl);
					PORTD = LED_Data;
					count_ovfl--;
				}
				else
				{
					count_ovfl = 8;
					LED_Data = 0xff;
					PORTD = LED_Data;
				}
			}
		}
		else
		{		
			if (count == 150)
			{
				_delay_ms(10);
				if(count_ovfl<8)
				{
					LED_Data |= (1<<count_ovfl);
					PORTD = LED_Data;
					count_ovfl++;
				}
				else
				{
					count_ovfl = 0;
					LED_Data = 0;
					PORTD = LED_Data;
				}
			}
		}
	}
}


void FND_Display(uint16_t data)
{
	static uint8_t position = 0;
	uint8_t fnd_data[] = {
		0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x27, 0x7f, 0x67
	};
		
	switch(position)
	{
		case 0 : // 첫 번째 자리 -> 0번 pin Low, 나머지는 High  / 출력을 받아서 입력으로 넣어주면 회로 완성
		FND_SELECT_PORT &= ~(1<<PINF0);
		FND_SELECT_PORT |= (1<<PINF1) | (1<<PINF2) | (1<<PINF3);
		FND_DATA_PORT = fnd_data[data/1000];
		break;
		
		case 1 :
		FND_SELECT_PORT &= ~(1<<PINF1);
		FND_SELECT_PORT |= (1<<PINF0) | (1<<PINF2) | (1<<PINF3);
		FND_DATA_PORT = fnd_data[data/100%10];
		break;

		case 2 :
		FND_SELECT_PORT &= ~(1<<PINF2);
		FND_SELECT_PORT |= (1<<PINF1) | (1<<PINF0) | (1<<PINF3);
		FND_DATA_PORT = fnd_data[data/10%10];
		break;
		
		case 3 :
		FND_SELECT_PORT &= ~(1<<PINF3);
		FND_SELECT_PORT |= (1<<PINF1) | (1<<PINF2) | (1<<PINF0);
		FND_DATA_PORT = fnd_data[data%10];
		break;
	}
	position++;
	position = position % 4; // 4자리만 출력하고 다시 처음으로
	
}

 

좌 : up          /       우 : down

 


 

[PWM]

 

Timer Register

 

PWM에 대해 알아보기 전,

Timer 관련 레지스터들을 알고갈 필요가 있다.

 

관련 내용은 아래 링크 참조

https://rangvest.tistory.com/entry/Harman-%EC%84%B8%EB%AF%B8%EC%BD%98-%EC%95%84%EC%B9%B4%EB%8D%B0%EB%AF%B8-18%EC%9D%BC%EC%B0%A8-Interrupt-TimerCounter-%EC%9D%B4%EB%A1%A0

 

[Harman 세미콘 아카데미] 18일차 - Interrupt, Timer/Counter 이론

[Interrupt] 인터럽트란? CPU가 프로그램을 실행하고 있을 때, 입출력 하드웨어 등의 장치에 예외상황이 발생하여 처리가 필요할 경우에 CPU에게 알려 처리할 수 있도록 하는 것 우선적으로 처리해야

rangvest.tistory.com

 


 

PWM

 

1. PWM이란?

: PWM(Pulse Width Modulation)

 

PWM은 High 값의 비율로 펄스를 표현한 방식이며 개별 bit가 아닌, 한 주기에 반응하고

그 비율은 MCU 내부에 내장된 타이머 카운트로 제어한다.

이러한 원리를 이용하면 아날로그 신호와 유사한 효과를 내기 때문에

LED의 밝기를 조절할 수도, Motor의 세기를 제어할 수도 있다.

 

2. Duty Cycle

High 신호와 Low 신호의 비율을 의미하며, 그 비율은 다음과 같다.

 

 

3. PWM 모드의 종류

 

    ※ 용어 정리

        - BOTTOM : 카운터의 값이 0x00 또는 0x0000일 때를 가리킨다.

        - MAX : 카운터가 0xff 또는 0xffff일 때를 가리킨다.

        - TOP : 카운터가 가질 수 있는 최대값을 가리킨다.

                    Overflow interrupt일 경우, TOP은 0xff이지만

                    비교 일치 인터럽트일 경우, 사용자가 설정한 값이 TOP가 된다.

 

 

    1) Fast PWM mode

 

OCRn 조정 시, 펄스 시작이 일치되도록 배치된다.

        - 고속 PWM 모드이며, 단일 경사 모드이다.

        - BOTTOM에서 TOP까지 카운트를 진행하는 상향 카운트만 존재

        - 고주파수 PWM 파형 출력에 사용

        - 카운트 값이 BOTTOM일 때, 파형 출력 핀으로 High 출력(비반전)

        - 비교 일치가 발생하면, 파형 출력 핀으로 Low 출력(비반전)

        - 비교 일치 값 조정에 의해 듀티사이클 조정됨.

        - OCRn이 TOP에 가까울수록 High 비율이 높아진다.

 

 

    2) Phase Correct PWM

 

OCRn 조정 시, 펄스의 중심이 일치되도록 배치된다.

       - 위상 교정 PWM 모드이며, 이중 경사모드이다.

       - BOTTOM에서 TOP까지 상향 카운트 후 TOP에서 BOTTOM으로 하향 카운트

       - 고해상도 PWM 파형 출력에 사용

       - 상향 카운트에서 비교 일치가 발생하면, 파형 출력 핀으로 Low 출력(비반전)

       - 하향 카운트에서 비교 일치가 발생하면, 파형 출력으로 High 출력(비반전)

       - 단일 경사 모드에 비해 2배의 해상도를 가지므로, 듀티사이클 미세 조정 가능

        - OCRn이 TOP에 가까울수록 High 비율이 높아진다.

 

4. PWM setting 방법

    1) TCCRn Register

          : 파형 생성 방법 결정

 

8bit Timer TCCRn Register

        - 16bit Timer의 경우, TCCRnA / TCCRnB로 각각 8bit씩 나눠져 있다.

           따라서 mode에 맞춰서 각각 세팅해줘야 한다.

        - WGM bit (Wave Generation Mode)

           : PWM 관련 mode 중 하나 선택

        - COM bit (Compare Match Output Mode)

           : 비반전 모드(비교 일치 시, Low / BOTTOM에서 High 출력)

             반전 모드(비교 일치 시, High / BOTTOM에서 Low 출력)

             각 WGM별로 세팅법이 다르다.(Data sheet 참조)

8bit WGM Description
16bit WGM Discription
8bit CS Description

    2) PWM 신호 생성

        - 타이머/카운터에서 비교 일치 발생 시, 파형 생성 기능을 통해 사용 가능하다

        - 파형 출력 핀(OCnx)으로 PWM 신호 출력

        - OCRn : TCNT와 비교하여 OC0 단자에 출력 신호를 발생시키기 위한 8bit값 저장

 


 

[Buzzer]

 

Buzzer 종류

좌 : Active Buzzer&nbsp; &nbsp;/&nbsp; &nbsp;우 : Passive Buzzer

[Buzzer]
Active Buzzer(능동형 부저) Passive Buzzer(수동형 부저)
회로가 내장되어 있어, 전원만 인가하면 소리가 나므로
프로그램 제어가 편하다.
내부에 진동원이 없어, 스케치에 주파수를 지정하여야만
소리를 낸다.
원하는 주파수의 소리를 낼 수 없다. 원하는 주파수의 소리를 만들어낼 수 있다.

 


 

Buzzer 활용

 

 

<Buzzer.h>

#ifndef BUZZER_H_
#define BUZZER_H_ // 헤더파일 정의

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

void buzzer_init();
void no_buzzer();
void play_buzzer();
void set_buzzer(int note);
void power_buzzer(); // 각 함수들 선언

#endif /* BUZZER_H_ */

 

<Buzzer.c>

#include "Buzzer.h"

void buzzer_init()
{
	DDRB |= (1<<DDRB5); // 출력 핀 지정
	TCCR1B |= (0<<CS12) | (1<<CS11) | (0<<CS10); // 8분주
	TCCR1B |= (0<<WGM13) | (1<<WGM12); // Wave genetation : CTC mode
	TCCR1A |= (0<<WGM11) | (0<<WGM10);
}

void no_buzzer()
{
	TCCR1A &= ~((1<<COM1A1) | (1<<COM1A0)); // 출력x
}

void play_buzzer()
{
	TCCR1A |= (0<<COM1A1) | (1<<COM1A0); // 출력o
}

void set_buzzer(int note)
{
	OCR1A = 1000000 / note;
}

void power_buzzer()
{
	play_buzzer();
	set_buzzer(2000); // OCR값을 조금씩 줄여가면서 측정
	_delay_ms(100);
	set_buzzer(3000);
	_delay_ms(100);
	set_buzzer(4000);
	_delay_ms(100);
	set_buzzer(5000);
	_delay_ms(100);
	set_buzzer(6000);
	_delay_ms(100);
	set_buzzer(7000);
	_delay_ms(100);
	no_buzzer();
}

 

<main.c>

#include "Buzzer.h"

int main(void)
{
	buzzer_init(); // PIN5 사용을 위한 초기화
    
	TCCR0 |= (0<<CS02) | (1<<CS01) | (0<<CS00);
	TCCR0 |= (1<<WGM01) | (1<<WGM00); // Fast PWM mode 사용
	TCCR0 |= (1<<COM01) | (0<<COM00); // 비교 일치가 발생하면, OCn 핀의 출력은 High / BOTTOM에서 Low로 바뀐다.(반전 모드)
	OCR0 = 100;
	DDRB |= (1<<DDRB4); // 여기까지는 PIN4를 사용하기 위한 초기화

    while (1) 
    {
        power_buzzer(); // PIN5 사용을 위한 power_buzzer 함수 호출
        
        OCR0 += 10; // PIN4를 사용하기 위한 문장들↓
        if(OCR0 >= 250)
        {
        	OCR0 = 0;
        }
        _delay_ms(200);
    }
}
PIN4로 출력 / Buzzer 대신 LED로 대체
PIN5로 출력

 


 

[Motor Driver]

 

<Control port를 활용한 Motor 제어>

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


int main(void)
{
	DDRC = 0xff;
		
    while (1) 
    {
		PORTC &= ~(1<<PINC4) | ~(1<<PINC5); // 전원 연결 안 된 상태
		_delay_ms(3000);
		
		PORTC &= ~(1<<PINC4); // 역방향 회전
		PORTC |= (1<<PINC5);
		_delay_ms(3000);
		
		PORTC |= (1<<PINC4) | (1<<PINC5); // 정방향 회전
		_delay_ms(3000);		

		PORTC &= ~(1<<PINC5); // 브레이크
		PORTC |= (1<<PINC4);
		_delay_ms(3000);
    }
}

※ Bitmasking 활용한 소프트웨어 제어

관련 내용은 아래 링크 참조

https://rangvest.tistory.com/entry/Harman-%EC%84%B8%EB%AF%B8%EC%BD%98-%EC%95%84%EC%B9%B4%EB%8D%B0%EB%AF%B8-11%EC%9D%BC%EC%B0%A8-%EB%B0%98%EB%8F%84%EC%B2%B4-%EA%B0%9C%EC%9A%94-C%EC%96%B8%EC%96%B4-%EA%B0%9C%EC%9A%94-%EB%B0%8F-%EB%AC%B8%EB%B2%95

 

[Harman 세미콘 아카데미] 11일차 - 반도체 개요, C언어 개요 및 문법

[반도체 개요] 반도체란? https://rangvest.tistory.com/entry/%EB%B0%98%EB%8F%84%EC%B2%B4%EB%9E%80-%EB%B0%98%EB%8F%84%EC%B2%B4%EC%9D%98-%EC%A0%95%EC%9D%98-%EB%B0%8F-%ED%8A%B9%EC%A7%95-%EC%86%8C%EC%9E%90-%EB%B0%98%EB%8F%84%EC%B2%B4-%EC%A2%85%EB%A5%98

rangvest.tistory.com

 

 

<PWM으로 Motor 출력 제어>

 

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


int main(void)
{
	DDRC = 0xff;
	TCCR0 |= (0<<CS02) | (1<<CS01) | (0<<CS00);
	TCCR0 |= (1<<WGM01) | (1<<WGM00);
	TCCR0 |= (1<<COM01) | (0<<COM00);
	OCR0 = 100;
	DDRB |= (1<<DDRB4);

	
	while (1)
	{

		PORTC &= ~(1<<PINC5); // Motor 정방향 회전 
		PORTC |= (1<<PINC4);
		
		OCR0 += 10; // wave 변형
		if(OCR0 >= 250)
		{
			OCR0 = 0;
		}
		_delay_ms(1000);
	}
}

 

728x90
반응형