본문 바로가기

AVR/AVR 연습, Tutorial

AVR 타이머/카운터 인터럽트 응용 소프트웨어 PWM DC모터 속도 제어하기 - AVR 연습


모터 정역제어를 해보았으니 다음은 모터의 속도를 조절해 봅니다.

DC모터는 전압에 따라 속도가 변하는데 AVR은 전압의 조절은 불가하고 입력이나 출력만 가능합니다. 따라서 그냥 모터의 속도를 조절 할 수는 없는데요. 

이 것을 소프트웨어 적으로 PWM(Pulse Width Modulation) 펄스 폭 변조를 해주어서 일정 주기 내에 On/Off 횟수를 조절해 주면 평균전압이 떨어지게 되어서 모터의 속도를 제어 할 수 있습니다.


 꾸준히 포스팅하는 것도 힘드네요..ㅎ 개학하면 어찌해야 할지...


PWM (Pulse Width Modulation) 이란 무엇일까요??


위 이미지의 타이밍도 처럼 일정한 동작시간(전체주기) 동안 ON과 OFF의 비율을 조정해서 평균 전압을 떨어뜨리는 방법을 말합니다.

예를 들어서 5V를 공급받아 출력하는 AVR에서 전체 동작 시간을 1초로 모터를 동작시키고자 했을때 PWM을 이용해서 저속으로 동작시키고 싶었다면 펄스 한주기 100%의 최대출력을 자신이 원하는 %만큼 ON/OFF 비율을 조정해 줄수 있도록 (제 경우에는 PWM이라는 변수를 만들어 최대 값은 10으로 10%씩 조정이 가능하도록 나누었습니다.) 변수를 만들어서 50%는 ON, 50% OFF 해서 동작시간동안 계속해서 반복 전체 평균전압을 3V로 떨어뜨리는 방법 입니다.


 AVR에서는 PWM기능을 따로 제공하는데요. 이 기능을 이용해서 PWM 출력을 할 경우에는 OC0A, OC0B, OC1A, OC1B 등 해당 기능을 가진 핀 들에서 밖에 PWM출력이 되지 않기 때문에 타이머/카운터를 이용하면 좀더 범용적으로 기능이 없는 다른 입출력 핀들에서도 PWM 출력이 가능해 진답니다.



모터 PWM 제어 연습 회로도




회로는 기존의 모터 정/역 제어를 했던 것과 동일하고, BA6208 모터컨트롤러 IC를 사용하고, AVR 모듈의 B포트 0번핀과 1번핀으로 제어 하는 회로입니다. 


CodeVision AVR 용 소스코드


#include <tiny2313.h>    // tiny2313의 입출력 관련 헤더파일을 포함합니다.

#include <delay.h>    // 지연함수 관련 헤더파일을 포함합니다.


int pwm, count, sec;    // 정수형 전역변수 pwm, count, sec를 생성합니다.


interrupt [TIM0_OVF] void timer0_ovf_isr(void)    // 타이머 인터럽트0번 인터럽트 발생시 점프

{

    if(pwm>10)    // 만약 pwm 변수 값이 10보다 크면

        pwm=0;    // pwm 변수 0으로 초기화

    else    // 아니라면

        pwm++;    // pwm 변수 1증가

    

    if(count==627)    // 만약 count 변수 값이 627과 같다면

    {

        if(sec==100)    // 만약 sec 변수 값이 100과 같다면

            sec=0;    // sec 변수 0으로 초기화

        else    // 아니라면

            sec++;    // sec 변수 1증가

        

        count=0;    // count 변수 0으로 초기화

    }        

    else    // 아니라면

        count++;    // count 변수 1증가

                

    TCNT0=0x00;    // 카운팅 레지스터 TCNT0을 0으로 초기화

}


void motor_pwm(char control)    // motor_pwm 함수를 생성합니다.

{

    pwm=0;    // pwm변수를 0으로 만듭니다.

    while(pwm<=control){}    // pwm 변수가 입력 된 control 변수보다 크거나 같을 때 까지 아무 것도 하지 않습니다.

}


void main(void)    // main 함수를 호출합니다.

{

PORTB=0x00;    // 포트B의 초기값은 8비트 전체 논리0 (0V) 출력

DDRB=0xFF;    // 포트B의  입출력 상태는 8비트 전체 논리1 (5V) 출력으로 설정


TCCR0A=0x00;    // 타이머, 카운터0번 컨트롤 레지스터A Normal (OverFlow)모드로 설정

TCCR0B=0x01;    // 타이머, 카운터0번 컨트롤 레지스터B 입력클럭 그대로 사용 분주비 없음

TCNT0=0x00;    // 타이머, 카운터0번 카운팅 레지스터 0으로 초기화


TIMSK=0x02;    // 타이머,카운터 마스터 컨트롤 레지스터, 타이머, 카운터 인터럽트0 허용


#asm ("sei")    // 전체 인터럽트 허용


while (1)    // 아래 구문을 무한 반복합니다.

      {

        sec=0;    // sec변수를 0으로 만듭니다.

         

        while(sec<50)    // sec 변수가 50보다 클때까지

        {

        PORTB.0=1;    // 포트B의 0번 핀에 논리1 (5V) 출력

        PORTB.1=0;    // 포트B의 1번 핀에 논리0 (0V) 출력

        motor_pwm(10);    // pwm변수가 10일 때 까지 지연합니다.

        PORTB.0=0;    // 포트B의 0번 핀에 논리0 (0V) 출력

        PORTB.1=0;    // 포트B의 1번 핀에 논리0 (0V) 출력

        motor_pwm(0);    // pwm 변수가 0일 때 까지 지연합니다.

        }


        PORTB.0=0;    // 포트B의 0번 핀에 논리0 (0V) 출력

        PORTB.1=0;    // 포트B의 1번 핀에 논리0 (0V) 출력

        delay_ms(4000);    // 4초 동안 위 상태를 유지합니다.

        

        sec=0;    // sec변수를 0으로 만듭니다.

        

        while(sec<50)    // sec 변수가 50보다 클때까지

        {

        PORTB.0=1;    // 포트B의 0번 핀에 논리1 (5V) 출력

        PORTB.1=0;    // 포트B의 1번 핀에 논리0 (0V) 출력

        motor_pwm(5);    // pwm변수가 5일 때 까지 지연합니다.

        PORTB.0=0;    // 포트B의 0번 핀에 논리0 (0V) 출력

        PORTB.1=0;    // 포트B의 1번 핀에 논리0 (0V) 출력

        motor_pwm(5);    // pwm변수가 5일 때 까지 지연합니다.

        }


        PORTB.0=0;    // 포트B의 1번 핀에 논리0 (0V) 출력

        PORTB.1=0;    // 포트B의 1번 핀에 논리0 (0V) 출력

        delay_ms(4000);    // 4초 동안 위 상태를 유지합니다.           

      }

}


AVR Studio 용 소스코드


#include <avr/io.h>    // avr 입출력 관련 헤더파일을 포함합니다.

#define F_CPU 8000000UL    // 사용하는 크리스탈 주파수를 상수로 설정합니다.

#include <util/delay.h>    // 지연함수 관련 헤더파일을 포함합니다.

#include <avr/interrupt.h>    // 인터럽트 관련 헤더파일을 포함합니다.


volatile int pwm, count, sec;    // 정수형 전역변수 pwm, count, sec를 생성합니다.


ISR(TIMER0_OVF_vect)    // 타이머 인터럽트0번 인터럽트 발생시 점프

{

    if(pwm>10)    // 만약 pwm 변수 값이 10보다 크면

        pwm=0;    // pwm 변수 0으로 초기화

    else    // 아니라면

        pwm++;    // pwm 변수 1증가

    

    if(count==627)    // 만약 count 변수 값이 627과 같다면

    {

        if(sec==100)    // 만약 sec 변수 값이 100과 같다면

            sec=0;    // sec 변수 0으로 초기화

        else    // 아니라면

            sec++;    // sec 변수 1증가

        

        count=0;    // count 변수 0으로 초기화

    }        

    else    // 아니라면

        count++;    // count 변수 1증가

                

    TCNT0=0x00;    // 카운팅 레지스터 TCNT0을 0으로 초기화

}


void motor_pwm(char control)    // motor_pwm 함수를 생성합니다.

{

    pwm=0;    // pwm변수를 0으로 만듭니다.

    while(pwm<=control){}    // pwm 변수가 입력 된 control 변수보다 크거나 같을 때 까지 아무 것도 하지 않습니다.

}


int main(void)    // main 함수를 호출합니다.

{

PORTB=0x00;    // 포트B의 초기값은 8비트 전체 논리0 (0V) 출력

DDRB=0xFF;    // 포트B의  입출력 상태는 8비트 전체 논리1 (5V) 출력으로 설정


TCCR0A=0x00;    // 타이머, 카운터0번 컨트롤 레지스터A Normal (OverFlow)모드로 설정

TCCR0B=0x01;    // 타이머, 카운터0번 컨트롤 레지스터B 입력클럭 그대로 사용 분주비 없음

TCNT0=0x00;    // 타이머, 카운터0번 카운팅 레지스터 0으로 초기화


TIMSK=0x02;    // 타이머,카운터 마스터 컨트롤 레지스터, 타이머, 카운터 인터럽트0 허용


sei();    // 전체 인터럽트 허용


while (1)    // 아래 구문을 무한 반복합니다.

      {

        sec=0;    // sec변수를 0으로 만듭니다.

         

        while(sec<50)    // sec 변수가 50보다 클때까지

        {

        PORTB=(1<<PB0) | (0<<PB1);    // 포트B의 0, 1번 핀에 각각 논리1, 0 (5V), (0V)출력

        motor_pwm(10);    // pwm변수가 10일 때 까지 지연합니다.

        PORTB=(0<<PB0) | (0<<PB1);    // 포트B의 0, 1번 핀에 각각 논리0, 0 (0V), (0V)출력

        motor_pwm(0);    // pwm변수가 0일 때 까지 지연합니다.

        }


        PORTB=(0<<PB0) | (0<<PB1);    // 포트B의 0, 1번 핀에 각각 논리0, 0 (0V), (0V)출력

        _delay_ms(4000);    // 위 상태를 4초 동안 유지합니다.

        

        sec=0;

        

        while(sec<50)

        {

        PORTB=(1<<PB0) | (0<<PB1);    // 포트B의 0, 1번 핀에 각각 논리1, 0 (5V), (0V)출력

        motor_pwm(5);    // pwm변수가 5일 때 까지 지연합니다.

        PORTB=(0<<PB0) | (0<<PB1);    // 포트B의 0, 1번 핀에 각각 논리0, 0 (0V), (0V)출력

        motor_pwm(5);    // pwm변수가 5일 때 까지 지연합니다.

        }


        PORTB=(0<<PB0) | (0<<PB1);    // 포트B의 0, 1번 핀에 각각 논리0, 0 (0V), (0V)출력

        _delay_ms(4000);    // 위 상태를 4초 동안 유지합니다. 

      }


return 0;    // main함수에 0을 리턴합니다.

}


프로그램 동작 설명


STEP 1 타이머/카운터0의 기본설정은 CK로 분주비를 사용하지 않고, Normal(OverFlow)모드로 동작합니다.


STEP 2 count변수와, sec변수는 while반복문으로 일정시간동안 지연시키면서 계속해서 pwm구문을 동작시키기 위해서 사용되고, count변수가 627이 되면 0.000031875초 동안 지연되고, 이 때 sec 변수가 1 증가되는데 sec 변수가 50이 되면 약 1초 동안 지연이 가능 해 집니다.


STEP 3 이를 이용해서 while(1) 반복구문에서 while(sec<50) sec변수가 50보다 클 때까지, 모터의 정회전을 100%동작 시키고, 모터의 정지를 0% 출력시키면 PWM출력은 100%로 1초동안 DC모터가 정회전 동작하고, 아래 두번째 반복구문 에서 while(sec<50) 에서는 모터의 정회전을 50%동작시키고 50%는 정지를 시켰기 때문에 PWM출력은 50%로 약 3V정도 출력되어 저속으로 정회전 동작하게 됩니다.



DC모터 PWM 구동영상



총 동작은 PWM 100%로 모터 정회전 1초 > 4초동안 정지 

> PWM 50%로 모터 정회전 1초 > 4초동안 정지 입니다.


PWM 제어 끝 다음은 무엇을 할까요??