AVR 타이머/카운터 응용 하드웨어 PWM 숨쉬는 LED 만들기 - AVR 연습

2013.08.24 23:32




현재 군 복무중인 상태입니다.



AVR 연습포스팅을 4개월 만에 쓰네요.. 다른 포스팅 보다 이것이 더 중요해 보임에도 말이죠...

사실 이제 여러가지로 포스팅이 어려워져서 비 주기적으로 포스팅 하게 되었습니다.

따라서 부족한점이 많더라도 이해 부탁드립니다.^

그래서 4개월전 포스팅인 소프트웨어 PWM에 이어 하드웨어 PWM 사용법을 올려봅니다.

바로 Studio와 Vision 코드를 확인해 보도록 하지요..!



사용된 회로도를 볼까요!



회로도의 변화는 거의 없는 상태이고 이 회로에서 사용되어지는 LED는 OC0A라고 Netlist되있는 LED7번과 OC0B라고 Netlist되있는 LED5번 입니다.

이에 따른 차이점은 소프트웨어 PWM의 경우 타이머/카운터로 시간을 조절해서 입출력으로 출력하기 때문에 모든 포트에서 PWM을 사용할수 있지만, 하드웨어 PWM의 경우 AVR 내부에 설계되어져 출력으로 설계되어 있는 포트만 하드웨어 PWM 기능을 사용할 수 있습니다.

Attiny2313은 8비트와 16비트의 하드웨어 PWM을 지원하고, 8비트는 OC0A와 OC0B로 각각 PB2, PD5번 핀으로 사용할수 있고 16비트는 OC1A, OC1B로 PB3, PB4핀에서 사용 할 수 있습니다.


CodeVisionAVR용 소스코드


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

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


unsigned char i=0;    // 반복용으로 사용하기 위한 변수 i를 생성합니다.


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

{

DDRB=0x04;    // 포트B의 2번핀만 출력으로 사용합니다.

PORTB=0x00;    // 포트B의 2번핀의 초기값을 0V (논리0)으로 설정합니다.


DDRD=0x20;    // 포트D의 5번핀만 출력으로 사용합니다.

PORTB=0x00;    // 포트D의 5번핀의 초기값을 0V (논리0)으로 설정합니다.


/*-- WGM02, WGM01, WGM00 비트가 011로 Fast PWM모드로 설정합니다.

그리고 COM0A1, COM0A0 비트가 10 이므로 OCR0A값과 TCNT0값이 같을 때 OC0A핀에

5V가 출력되게 됩니다. 같은 방식으로 OC0B도 동일하게 동작합니다. */


TCCR0A=(1<<COM0A1) | (0<<COM0A0) | (1<<COM0B1) | (0<<COM0B0) | (1<<WGM01) | (1<<WGM00);

TCCR0B=(0<<WGM02) | (0<<CS02) | (0<<CS01) | (1<<CS00);

TCNT0=0x00;

OCR0A=0x00;

OCR0B=0x00;


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

   {

      for(i=0;i<255;i++)    // 변수i를 255가 될때까지 아래 구문을 반복합니다.

      {

      OCR0A=i;    // 변수 i 값을 OCR0A 레지스터에 입력합니다. (PWM은 변수i / 255)

OCR0B=i;    // 변수 i 값을 OCR0B 레지스터에 입력합니다. (PWM은 변수i / 255)

      delay_ms(5);    // 위 상태를 약 5mS간 유지합니다.

      }

      

 for(i=255;i>0;i--)    // 255가 된 변수i가 0이 될때까지 아래 구문을 반복합니다.

      {

      OCR0A=i;    // 변수 i 값을 OCR0A 레지스터에 입력합니다. (PWM은 변수i / 255)

OCR0B=i;    // 변수 i 값을 OCR0B 레지스터에 입력합니다. (PWM은 변수i / 255)

      delay_ms(5);    // 위 상태를 약 5mS간 유지합니다.

      }

   }

}



AVR Studio용 소스코드


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

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

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


unsigned char i=0;    // 반복용으로 사용하기 위한 변수 i를 생성합니다.


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

{

DDRB=0x04;    // 포트B의 2번핀만 출력으로 사용합니다.

PORTB=0x00;    // 포트B의 2번핀의 초기값을 0V (논리0)으로 설정합니다.


DDRD=0x20;    // 포트D의 5번핀만 출력으로 사용합니다.

PORTB=0x00;    // 포트D의 5번핀의 초기값을 0V (논리0)으로 설정합니다.


/*-- WGM02, WGM01, WGM00 비트가 011로 Fast PWM모드로 설정합니다.

그리고 COM0A1, COM0A0 비트가 10 이므로 OCR0A값과 TCNT0값이 같을 때 OC0A핀에

5V가 출력되게 됩니다. 같은 방식으로 OC0B도 동일하게 동작합니다. */


TCCR0A=(1<<COM0A1) | (0<<COM0A0) | (1<<COM0B1) | (0<<COM0B0) | (1<<WGM01) | (1<<WGM00);

TCCR0B=(0<<WGM02) | (0<<CS02) | (0<<CS01) | (1<<CS00);

TCNT0=0x00;

OCR0A=0x00;

OCR0B=0x00;


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

   {

      for(i=0;i<255;i++)    // 변수i를 255가 될때까지 아래 구문을 반복합니다.

      {

      OCR0A=i;    // 변수 i 값을 OCR0A 레지스터에 입력합니다. (PWM은 변수i / 255)

OCR0B=i;    // 변수 i 값을 OCR0B 레지스터에 입력합니다. (PWM은 변수i / 255)

      _delay_ms(5);    // 위 상태를 약 5mS간 유지합니다.

      }

      

 for(i=255;i>0;i--)    // 255가 된 변수i가 0이 될때까지 아래 구문을 반복합니다.

      {

      OCR0A=i;    // 변수 i 값을 OCR0A 레지스터에 입력합니다. (PWM은 변수i / 255)

OCR0B=i;    // 변수 i 값을 OCR0B 레지스터에 입력합니다. (PWM은 변수i / 255)

      _delay_ms(5);    // 위 상태를 약 5mS간 유지합니다.

      }

  }


return 0;    // 메인함수에 0을 리턴합니다. (프로그램 정상종료)

}



코드 분석


하드웨어 PWM도 일반적인 타이머/카운터와 동일하게 동작하고 비교매치 인터럽트와 비슷한 구조로 PWM을 만들어 냅니다. 먼저 하드웨어 PWM은 두가지 종류가 있는데 Fast PWM과 Phase Correct PWM 두가지가 있습니다.

Fast PWM은...



TOP값 256으로 타이머, 카운터가 꾸준히 계산중일때 OCR0x레지스터와 비교해 동일한 지점에서 COM0A0과 COM0A1 레지스터에 따라 해당 지점에서 5V가 출력되거나 0V가 출력되 PWM의 기능을 하게되는 방식입니다.


Phase Correct Pwm은...



원리는 동일하나 카운팅 소스가 조금 특이합니다.

초기에는 업 카운팅하여 256지점까지 올라간 다음, 다운 카운팅해서 Bottom값 까지 내려값니다.

이 때 OCR0x 값과 비교해서 삼각파에서 잘린 두지점 사이에 5V가 출력되거나, 0V가 출력되 PWM 기능을 하게 되는 방식입니다.


이렇게 PWM 기능은 동일하나 방식에 따라 차이가 있을수 있습니다. 저는 연습용으로 Fast PWM을 해본것이 위 소스입니다. 그래서 설명하자면 TOP값은 이미 256로 지정되있고, 타이머 카운터는 계속 연산하는 와중에 저는 OCR0A, B값을 1씩 증가시키고 감소 시키면서 256 등분된 PWM출력을 이루게 되는것입니다.^



동작 영상




하드웨어 PWM 끝!!!  뭔가 부실한 느낌인데요.


BinGoon AVR/AVR 연습, Tutorial , , , , ,

  1. Blog Icon
    [Adios]

    PWM이 이런거였군요! 간만에 올라온 포스팅 잘 보고 갑니다 ~_~

  2. 또 찾아오셨네요.^ 이건 AVR에 기본적으로 구성되어있는 하드웨어 PWM을 응용해본 코드에요.^ 찾아주셔서 감사드려요.^
    이제 주말마다 블로그 업데이트 할려구요.^

  3. PWM에 대해서 궁금했는데 타이머/카운터로 구현하는거였군요. 좋은글 잘 보았습니다! ;-)

  4. PWM 구현방법도 소프트웨어적으로 모든 입출력핀에서 사용할수 있는 방법과 하드웨어 식으로 기능이 있는 핀에서 사용하는 두가지 방식이 있습니다^
    필요처에 따라 골라서 구현하시면 될 것같네요^

  5. Blog Icon
    행복한사람

    잘 보았습니다. 이해가 잘 못하지만 소스 따라서 해볼께요
    감사합니다.

  6. 아래 Fast PWM과 Phase Correct PWM을 제가 직접 만든 이미지를 사용해서 이해를 도와야 하는데 데이터 시트에 있는 이미지를 사용했더니, 조금 어렵게 느껴진 부분이 있는것 같습니다. 될수 있는대로 보정하도록 노력해 보겠습니다. 앞으로도 가끔 방문해 주세요.^

  7. Blog Icon
    avr

    안녕하세요 정말 잘 보고 있습니다^^*

    근데 블로그 글씨가 이상하게 보여요 어떤거 진하게 보이고 다른 글씨는 또 연하게 보여요ㅠㅠ

    어제까지만 해도 잘 보였는데... 저만 그렇게 보이는 건가요? 확인 부탁드려요

  8. 안녕하세요.^
    다른컴퓨터로도 해봤는데 정상적으로 보이고 있습니다, 사용하시는 브라우져에서 캐시하고 쿠키 등 임시파일을 삭제하고 계속 그러신다면 다시 점검토록 하겠습니다.

  9. Blog Icon
    이영근

    와....잘봤어용^^
    우앙...
    전 초보라 연습키트로 연습중인
    학생인데 jkit-128로도 이런
    기능이 가능한가요?

  10. 안녕하세요^
    디바이스마트에서 판매하는 jkit-128 맞죠??
    보드에 확장 IO핀이 헤더핀으로 설치되어있어서 회로 구성하신 다음에 코드를 짜면 기능이 가능합니다. 그리고 블로그에 사용한 Attiny2313모듈보다 jkit-128에 사용된 Atmega128이 기능이 더 많기 때문에 배울수 있는점이 많답니다.^ 참고해 주세요.

  11. Blog Icon
    기계과 전기회로 초보

    질문드립니다. 지금 소스에 대한 이해가 전반적으로 ㅠㅠ 잘안가네요 초보자라서 나름 데이터 시트보고 이해한 부분 설명드리고 모르는부분 질문하겠습니다. PWM모드를 사용하겠다는 선언이 TCCR0A,TCCR0B라고 이해하였씁니다 그리고 FAST PWM을 사용하기위해 011(WGM2.WGM1.WGM0)을 사용하셨구요 이부분에 대한 궁금증은 데이터 시트를 보면 옆에 TOP->0xff Update of OCRx at->TOP TOV Flag set on->MAX 이렇게 되어 있는데요 이것들의 의미를 모르겠습니다. 그리고 TCCR0A의 7654비트 가 1010인데 이부분 데이터 시트를 보니까 Clear OC0A on Compare Match,Set OC0a at TOP라고 되어있는데 이 설명이 무엇을 의미하는지도 잘 모르겠습니다 FOC0A,B부분은 FAST PWM에선 안쓰니까 00으로 둔것으로 보고있씁니다 다음으로 CS2,1,0은 분주비 설정으로 보고있구요. 다음에 소스를 보면 TNCT0=0x00,OCR0A=0x00 이부분 이 정말 이해가안됩니다. 세 레지스터의 정의부터 시작해서 어떻게 작용하는 루프인지 이해가 안되서 지금 골머리가 났습니다.ㅠㅠ 계~~속 고민중인데 조금만더 정확한 뜻을 알고싶습니다.

  12. 먼저 main()구문 바로 아래에 있는 TCNT0=0x00;과 OCR0A=0x00;, OCR0B=0x00; 은 초기화 구문입니다. TCNT는 Timer Counter Register로 타이머 카운터 이용시에 실제 1클럭이 들어왔을 때 1씩 상승하며 값을 저장하는 역할을 합니다. 그런데 TCNT0번 이므로 8Bit까지 셀수 있어 256이상 증가하면 Overflow가 걸리게 됩니다. 이 값이 TOP값이며 MAX값이 되지요.

    그럼 구체적인 원리로 들어가게 되면, PWM은 한 주기에서 ON시간과 OFF시간의 비율에 따라서 결정되게 됩니다. 그것을 타이머 카운터 비교매치를 이용해서 AVR에서는 구현을 하게되며 다음 이미지에서 http://cfile1.uf.tistory.com/image/23493B455218C082346291 // OCRnx Interrupt Flag Set 표시된 지점이 보이실 텐데요. 이 지점이 OCR0A, OCR0B에 직접 값을 써넣어 비교매치되면 OC0A핀 OC0B핀의 출력 상태를 변경하는 역할을 하게됩니다. 그리고 다시 이미지에서 OCRnx Update and TOVn Interrupt Flag Set이라고 표시된 지점에서 원래 출력 상태로 돌아오게 되지요.

    이 때 원래 출력 상태와 비교매치시 출력상태를 결정짓는 레지스터 요소가 질문주신 TCCR0A의 7, 6, 5, 4번 비트 입니다. 7, 6번 비트는 OC0A와 OCR0A에 미치는 비교매치설정 레지스터이고, 5, 4번 비트는 OC0B와 OCR0B에 영향을 미치는 비교매치설정 레지스터 입니다. 여기서 1, 0으로 각각 입력하여 비교매치시 0으로 클리어, TOP에서 OCR0A 1로 세트라는 조건을 주었는데요. OC0A핀의 초기상태가 5V를 출력하고 있는 상태이고, 카운터레지스터 TCNT0레지스터가 0~256까지 셀 때 OCR0A레지스터에 128을 입력했다면 TCNT0레지스터가 1씩 증가하다가 128과 같아지면 0으로 클리어(OC0A핀 0V)로 출력 됩니다. 이 클리어가 되는 시간이 128~256으로 MAX값이 될때 까지 계속 0V를 출력하다가 256이 넘어 OVF가 되면 다시 5V로 돌아오고, 또다시 128 같아서 0V 출력하고 하는 것을 매우 빠른속도로 반복하게 되면 실제 눈으로 LED나 멀티미터로 해당 포트를 찍어 보았을 때는 약 2.5V가 출력되는 것으로 보이게 되는 것입니다.

    풀어서 적느라 설명이 길어 졌네요. 도움되셨기를 바랍니다.^^

  13. Blog Icon

    비밀댓글입니다

  14. 메일로 연락 드렸습니다.^^

  15. Blog Icon
    한뜻

    안녕하세요 무작정 따라하는데...
    코드비전
    TCCR0A=(1<<COM0A1)|(0<<COM0A0)|(1<<COM0B1)|(0<<COM0B0)|(1<<WGM01)|(1<<WGM00);
    TCCR0B=(0<<WGM02)|(0<<CS02)|(0<<CS01)|(1<<CS00);
    이구분이 자꾸 에러나네요.. WGM 들어간 구문은 다 에러인데.. 설정이 잘못되서 그럴까요?
    몇번을 다시치고 다시치고 했는데.. ㅜㅜ

    에러메모에 이렇게 나와있네요..
    error: .............. undefined symbol 'COM0A1'
    error: .............. undefined symbol 'WGM02'

  16. 안녕하세요.
    혹시 사용하시는 AVR이 Attiny2313이 아니신가요?? 아니면 AVR Studio4 Toolchain에 iotn2313.h파일에 TCCR0B에 대해서 정의되어 있는지 확인이 필요해 보입니다. 혹시 Atmega128같은 다른 시리즈를 사용중이시면 TCCR0A, B로 나누어 진것이 아니라 TCCR0하나로 통합되어 있으니 참고하시기 바랍니다.

    부족한 답변이지만, 도움이 되셨길 바라고 더 필요하신 사항 있으면 연락주시기 바랍니다.

  17. Blog Icon
    한뜻

    안녕하세요 질문하는놈이 정확하게 상황을 말해야 하는데
    죄송합니다.
    사용중인 MCU 는 attiny2313 20pu 이며 2313헤더 보드는
    BinGoon님의 보드를 모방하여 만들었습니다.
    컴파일러는 코드비전 입니다.
    시간 내주셨는데.. 죄송합니다~ 그리고 매번 퀄리티 있는 답변 너무 감사합니다.

    질문사항은 위의 내용과 같습니다.

    반드시 한번 뵙고 싶네요 ~~!!

  18. Blog Icon

    비밀댓글입니다

  19. 안녕하세요.
    먼저 답변이 개인적인 사정으로 지체되어 죄송하구요. 해당 코드는 PWM LED출력을 위해서 구성된 코드라서, PCM 데이터를 재생하려면 전부 재구성해야 됩니다.

    그러니 http://binworld.kr/51 해당 포스팅에 제공되어있는 소스코드를 참조하시기 바랍니다. 답변 늦어 정말 죄송하고, 도움되시기를 바라겠습니다.^

  20. Blog Icon

    비밀댓글입니다

  21. Blog Icon

    비밀댓글입니다

  22. Blog Icon

    비밀댓글입니다

티스토리 툴바