반응형

[8051 강좌] PWM 기본이해 - 8051 외전

 

PWM 이란 말은 아마도 많이 들어봤을 것이다. 요즘 나오는 왠만한 프로세서들은 PWM 기능을 내장하고 있다.

PWM 은 간단히 이해하면 디지탈출력밖에 안되는 놈을 가지고 억지로 아날로그 출력 비스무리한 것을 만들어 내는 것을 말한다. ^^

사실 이게 싫으면 DA 컨버터라는 것을 사용하면 된다. 어쨌거나 우리가 사는 세상은 아날로그 세상이다. 온도를 재려고 해도 연속적인 값 중에 어떤 수치를 적당한 선에서 받아들여야 하는 것이고(정확한 온도란 인간이 알수 없다. 기껏해야 소수점 서너자리 정도까지 아는 것이지, 정확한 온도값을 알아낼수는 없는 것이다), 그 수치를 디지탈 IC 에서 처리하기 위해서는 아날로그값을 디지털로 변환해야 한다. 그때 AD, 즉 Analog to Digital 컨버터라는 것을 사용하고, 반대로 IC 에서 처리한 결과 값을 다시 우리에게 전달해 주기 위해서는 아날로그로 변환할 필요도 있다(이를테면 MP3 를 다시 귀로 듣기 위해서는 스피커에서 사용할 수 있는 아날로그 신호로 바꿔줘야 한다). 그때 사용하는 것이 DA 컨버터, 대충 짐작이 되겠지만 AD 와 반대의 기능을 가진 것이다. Digital to Analog , 디지탈값을 아날로그 값으로 바꿔주는 놈이다.

그냥 이걸 사용해도 된다. 그런데 요즘 추세가 SoC 추세다... 이게 뭔 소린고 하니 System on Chip 이라는 약자로, 모든 것을 하나의 칩 안에 넣어서 끝내 버리겠다는 망측한 발상이다. 솔직히 8051 도 그런 추세에 힘입어 내부 플래시 메모리가 자꾸 확장되 가는 추세다. 예전에는 당연히 CPU 따로, 램 따로, 롬 따로였다. 거기에 추가되는 기능을 위해서는 또 다른 부가적인 칩들이 붙어야 했다. 이를테면 AD 컨버터 IC, DA 컨버터 IC 같은 것 말이다. 그러다보니 기판도 꽤 커졌고, 크기도 자꾸 커져갔다.

그런데 기술이 발전하면서 왠만하면 그냥 칩 숫자를 줄여서, 가능하면 하나로 끝내보자는 생각이 팽배해지기 시작했다... 원래 공학도들이 게으르다. 그러다보니 기판 큰거 만들어서 여러개 칩 배열해서 땜질하고 하는 것이 귀찮아지기 시작한게다. ... 기술도 발전했겠다. 이제 칩 하나에 내가 필요로 하는 것을 다 넣어버리자는 운동이 시작된 게다.

대충 하나의 칩 안에 때려 박았다. 그래서 CPU, RAM, ROM 까지 때려 넣었다. 그런데 DA, AD 컨버터가 문제다. 이것들은 아날로그 값을 다루는 것들이다보니 크기를 줄이기가 그렇게 쉽지 않다. 그래도 어쩌겠나. 어떻게 어떻게 해서 AD 컨버터는 칩안에 대충 때려 넣었다. 그리고 DA 컨버터는 왠만하지 쓰지 말자고 약속을 하고, 대신은 PWM 이란 놈을 쓰자고 나름대로 결의를 했다. .... 도원결의, 아니 게으른 엔지니어들의 결의다... ^^

웃자고 한 이야기다. 하지만 기술 발전은 대부분 편한 쪽으로 이루어진다. 진공관대신 트랜지스터를, 트랜지스터 대신 IC를, IC 여러개 쓰기 귀찮아지니까 이제 SoC 란다... 이런 와중에 PWM 이란 기술로 디지탈출력을 아날로그 출력처럼 쓰게 했다.

이제 PWM 이 뭔지 한번 보자.

Pulse Width Modulation 의 약자로, 하나의 모듈안에 펄스 폭을 조절해서 원하는 값을

말들어 내는 기술이다.

아래 그림을 한번 보자..


디지탈값은 0과 1밖에 출력하지 못한다. 여기서 0이라 함은 아날로그 값으로는 0V가 되는 것이고, 1이라 함은 그 놈이 내보내는 전압 (8051에서는 +5V)을 의미한다. 그러면 디지탈로는 0V, +5V 두가지 밖에 출력이 안된다.

이것을 일정한 주기의 선 위에 그림처럼 배열했다.

100us 동안 10us 는 +5V를 출력하고, 90us 는 0V를 출력하게 하자. 그러면 실제로는 10us 동안은 +5V가 나오고 90us 동안은 0V가 나오지만 그 값을 받는 회로에서(DC 모터를 생각하라)는 어떻게 받아들일까? DC 모터 같은 경우는 10us, 90us 의 시간은 너무 짧은 간격이라 10us 동안만 가고, 90us동안은 선다는 개념이 없다. 게다가 내부에 있는 코일과 기타 여러 영향으로 일종의 LPF 같이 작동되면서 5V의 10%되는 값, 즉 0.5V 가 주어진 것으로 작동하게 된다.

이 비율을 적당히 조절해서 DC 모터 구동을 시키는 것이다. 천천히 돌리려면 10% 비율로, 중간 속도로 돌리려면 50% 비율로, 빨리 돌리려면 100%로 돌리면 된다.

이걸 PWM 이라고 한다. 이 기능을 프로세서 내부에 직접 가지고 있는 놈들도 많다. 없으면 타이머를 이용해서 비스무리한 기능을 구현하면 된다. 여기서는 TY52에 있는 AT89S52 의 Timer2 를 이용해서 PWM 기능을 만들어 보았다.

Timer2 는 원래 8051 에는 없는 기능이다. 8052에 추가된 기능이라 8051 책에서는 잘 사용법을 다루지 않는다. 그리고 8051 쓰면서 타이머를 여러개 쓸 일이 거의 없다. 한두개 정도만 잘 쓰면 되지, 굳이 세개까지 쓸일이 있나 싶다.

어쨌거나 그래서 여기서는 보통때는 잘 안쓰는 Timer2를 PWM 기능용으로 사용해 보았다.

그리고 실험은 모터대신(비싸서... ^^; 또 모터를 쓰려면 모터 드라이브도 만들어야 하니까....) TY52에 붙어있는 LED를 사용한다. LED는 지금까지는 불이 켜지거나 꺼지거나 둘중의 하나였다. 하지만 이 기능을 사용하면 이제 LED는 약한 불빚을 내거나 밝은 불빚을 내게 할수 있다. 필요하다면 저항을 470옴 대신 조금 더 작은 것으로 바꿔서 해도 된다(그런데 왠만하면 여기서는 그냥 하자^^).

PWM의 주기를 300us*20 로 했다. 만약 5%로 설정하면 300us 동안은 불이 켜지고, 나머지 5700us 동안은 불이 꺼진다. 10%면, 600:5400 이 될 것이다. 이런 식으로 프로그램하면 된다. 한번 해보면 대충 50%가 넘으면 밝기를 눈으로 보기에 별 차이를 느끼지 못할 것이다.

우선 Timer2 를 300us 주기로 실행되도록 만들어본다.

 

void init_T2(void)          // 24MHz X-TAL
{
    T2MOD   &= 0xFC;        /*  T2OE=0;DCEN=1; */
    T2MOD   |= 0x01;
    EXF2=0;                 /* reset flag */
    TCLK=0;RCLK=0;          /* disable baud rate generator */
    EXEN2=0;                /* ignore events on T2EX    */
    TH2=MSB_reload_value;   /* Init msb_value */
    TL2=LSB_reload_value;   /* Init lsb_value */
    RCAP2H=MSB_reload_value;/* reload msb_value */
    RCAP2L=LSB_reload_value;/* reload lsb_value */
    C_T2=0;                 /* timer mode   */
    CP_RL2=0;               /* reload mode */
    EA=1;                   /* interupt enable */
    ET2=1;                  /* enable timer2 interrupt  */
    TR2=1;                  /* timer2 run */
}

 

위 소스가 Timer2 에 관한 내용이다. Timer에 관한 내용은 아래 링크에서 자세히 살펴볼 수 있다. 아트멜 홈페이지(www.ateml.com)에 가서 Timer2 로 검색하면 쉽게 기술문서를 볼수 있다.

http://www.atmel.com/dyn/resources/prod_documents/doc4345.pdf

여기서는 Timer2 사용에 관한 자세한 내용을 다루지는 않는다. 이미 예전에 다른 타이머를 사용해 봤으니 그 내용을 다시 짚어보고 레지스터를 어떤 걸 썼는지 한번 훝어보면 된다. 레지스터들을 다 외울 필요 없다. 나도 그런건 외우지 않는다. 나중에 필요하면 문서 보면 다 나와있는데.... ^^

여기서 Timer2 설정을 마쳤다. MSB_reload_value, LSB_reload_value 는 각각 0xFD, 0xA8로 해 두었다. 그러면 300us 마다 Timer2 인터럽트가 걸리게 된다. 왜 이 값이냐고? 그건 예전 타이머관련 강좌를 다시 읽어보고, 그 다음 위에 있는 링크에 가서 Timer2 에 관한 내용을 살펴보기 바란다 ^^.

자, 타이머가 작동하기 시작하면 이제 무조건 300us 가 지나면 인터럽트 5가 작동한다. 이건 하드웨어적으로 설정된 값이니 가타부타 할것도 없는 것이다.
그러면 우리가 할 일은 ISR 내부에 내가 원하는 일을 집어 넣으면 된다.

 

void it_timer2(void) interrupt 5 /* interrupt   address is 0x002b   */
{
    if(B_cnt_pwm <= B_dimer) LED2 = ON;
        else LED2 = OFF;
    B_cnt_pwm++;
    if(B_cnt_pwm == 20) B_cnt_pwm = 0;

    TF2 =   0; /*   reset   interrupt   flag */
}

 

이게 Timer2 가 작동할 때 실행되는 함수다. 인터럽트5, ...

우선 여기서는 B_cnt_pwm 이라는 값과 B_dimer 라는 값을 비교하게 했다. 비교해서 만약 B_cnt_pwm 가 B_dimer 보다 작으면 LED에 불을 켠다. 아니면 불을 끈다.
그리고 B_cnt_pwm 값은 1씩 증가시키고, 20이 되면 0으로 초기화 된다.

B_dimer 는 0에서 20사이의 값을 가지게 된다. 타이머2 인터럽트는 20번 진행하면 다시 초기화가 된다. 즉, PWM 의 주기를 300us*20 이라고 보면 된다.

각각 인터럽트가 걸릴때마다 ISR 내부에서는 B_cnt_pwm 와 B_dimer 를 비교해서 LED를 켜고 끄는 작업을 하게 된다.


void main(void){
    init_T2();
    B_dimer=1;
    while(1){
        slow_light();
    }
}

 

메인함수에서는 Timer2를 초기화하는 작업(init_T2)을 하고, B_dimer 값을 초기 설정한 다음, slow_light() 를 무한루프로 돌렸다.

slow_light() 에서는 일정한 시간을 보내게 하고 B_dimer 값을 조절한다.

 

void slow_light(void) {
    u16 i;
    for(i=0;i<20000;i++) ;
    if(Direct) B_dimer++;
    else B_dimer--;
    if((B_dimer>=20)||(B_dimer<=0)) Direct = !Direct;
}

 

여기서 slow_light 를 자세히 보는 것보다는 Timer2 에서 PWM을 사용하는 방법을 익히는 것이 좋다.

이상으로 PWM이 뭔지 대충 살펴봤다. ^^
대충 본 것이니 대충 이해하면 된다. ^^
개념만 이해하고 나중에 다른 프로세서를 사용해서 PWM을 사용하게 된다면 그 칩에 달린 기능을 조금 더 보면 충분히 이용할수 있을 것이다.

여기서는 타이머를 이용해서 PWM을 만들어 보았다. 이런 식으로 기능이 포함되어 있지 않아도 얼마든지 그 기능을 구현해서 사용할수 있다. 물론 대부분의 경우 이렇게 쓰지는 않는다. 그냥 조금 더 돈 주고 그 기능이 구현된 칩을 사서 쓰는 것이 정신건강에도 좋고, 시간도 절약된다.

하지만 여기가 어딘가? .... 취미로 하는 곳이니, PWM도 TY52에서 만들어버렸다.
이걸로 LED 불빛을 조절하는 기법을 익혔으니, 이제 다음번에는 DC모터를 돌려봐도 좋겠다... ^^


          ------------------------------------------
           Timy의 전자카페
           기분좋은 하루되세요. ^^
           블로그 : http://www.electoy.net
           스터디카페 : http://cafe.daum.net/timy8051
          ------------------------------------------

반응형

+ Recent posts