• Feed
  • Explore
  • Ranking
/
/
    ATmega128A

    ATmega128A 8bit Timer/Counter0 CTC로 부저 다루기.

    데이터시트땜시 삽질함...ㅠㅠ
    박
    박형진
    2025.03.27
    ·
    9 min read

    Active Buzzer vs. Passive Buzzer

    • Active Buzzer는 전원만 인가하면 소리가 난다.

    • Passive Buzzer는 특정 주파수를 직접 인가해야 소리가 난다.

      • 그러니까, Waveform Generation 기능을 이용해 보려면 Passive Buzzer를 선택해야 한다.

        • 직접 주파수를 인가하기 때문에 원하는 음을 낼 수 있다.

        • 그렇다면 연주 같은 것도 가능하겠지.

    먼저... Timer랑 Counter가 왜 묶여 있지?

    • 16-bit Timer/Counter라고 해 보자

      • 클럭은 16MHz라고 해 보자.

    • 1초에 16,000,000번 클럭이 인가된다는 건데...

      • 좋다. 클럭이 한 번 인가될 때마다 Counter를 1씩 증가시키다가 16,000,000이 되면 1초가 지났다고 하면 되겠군!

      ...안타깝지만 16비트 Timer/Counter에는 0-65535까지만 저장이 가능한데 되겠나?

    이번엔 Prescaler에 대해 간단히 알아야 한다.

    • '분주기'라고, n Clock을 1 Clock으로, 클럭을 나눠주는 장치다.

    • 예를 들어, ATmega128A의 Timer/Counter1의 Prescaler는 아래 값 중 하나를 선택할 수 있다.

      • 8, 32, 64, 128, 256, 1024

      • Prescaler 값으로 1024를 택했다고 해 보자.

      • 16,000,000Hz를 1024로 나누면?

        • 15625

        • 분주기를 거친 클럭이 15625번 인가되면 1초가 지난 것이군!!!!!!

          • 아하!!!!!!! TCNT1이 15625가 되면 1초!!!!!!!

          • 아하!!!!!

        • 얼마나 긴 시간을 잴 것인지 등등... 목적에 맞게 Prescaler를 설정하면 될 것.

    Timer/Counter랑 Waveform Generation이 무슨 상관이지?

    • Timer/Counter는...

      • 카운터가 다 차서 오버플로우가 발생할 때마다 인터럽트가 발생토록 할 수 있다.

        • Normal Mode

        • 물론 Normal Mode를 Waveform Generate용으로 사용하는 건 Not-recommended라고 써져 있긴 하다.

      • 혹은 OCRnA Register에 값을 세팅해 놓고, TCNTn의 값이 OCRnA의 값과 같아졌을 때마다 인터럽트가 발생하게 할 수 있다.

        • CTC Mode, Clear Timer on Compare match Mode.

        • TCNTn : Timer/Counter n이 가지고 있는 카운터 Register.

        • OCRnA : Output Compare Register. 비교할 값이 담긴 Register.

      • 이 인터럽트 때마다(혹은 어떤 이벤트마다) OCn 핀의 신호를 올리고 내리면...?

        • Waveform Generation!!!


    HC12G-1PA Buzzer (= IMT12D2001AP)

    주요 Spec - Passive Buzzer

    • 85db(min) at 10cm

      • 철로변/지하철 소음 수준?

    • Rated Voltage: 1.5v

      • ATmega128A에선 5V output이 나오니까, 저항을 잘 활용해줘야 할 것.

      • 나는 1/4W 100옴 + 15옴 = 115옴 저항을 두었다.

      • R = V / I

        • 1.5v 사용하니까, V = 5v - 1.5v = 3.5v

        • 아래의 Max Current Consumption, I = 30mA

        • 3.5 / 0.03 = 116.666...옴.

        • 115옴 쓰자!

          • 전력 P = IV = 3.5 * 0.03 = 0.105

          • 1/8W가 채 안 되는군!

    • Operating Voltage: 1 - 2v

      • 이 사이에서 전압을 흔들어서 주파수를 주면 된다는 거지...?

    • Resonant Frequency: 2048Hz

      • 이 주파수에서 소리가 가장 크다? 공명주파수.

    • Max Current Consumption: 30mA

      • at 2048Hz, 1/2 Duty Square Wave

    실험 1. 그냥 Power Supply에 연결해 보기.

    • 아무 소리도 안 난다.

    • 전극을 뗐다 붙였다 하면 지지직거리긴 한다.

      • MOSFET 같은 걸 이용해서 소리낼 수도 있겠군. 난 CTC 쓸 거지만...


    CTC, Clear Timer on Compare Match Mode (WGM01:0 = 2) - 8bit Timer/Counter0

    개요

    • OCR0 Register의 값과 TCNT0 값이 일치하면 Counter가 Clear된다.

      • 그러니까, OCR0 값이 곧 Counter의 TOP Value다.

        • TOP Value : 각 동작 모드에서 카운터가 도달하는 최댓값.

          • 설정될 수 있다.

        • MAX Value : 각 동작 모드에서 카운터가 가질 수 있는 최댓값.

          • 8bit 기준 0xFF... 아시잖아요?

        • BOTTOM Value : 카운터가 가질 수 있는 최솟값.

          • 대개 0. 특수한 경우가 아니면.

    • OCF0 Flag를 이용하면 카운터 값이 TOP Value에 도달할 때마다 인터럽트를 발생시킬 수 있다.

      4432
      • 사실 나는 이건 관심 없다

    • Waveform을 Generate하지만 PWM은 아니다.

      • Duty Ratio 50% 고정.

    • CTC Mode에서 COM01:0 = 1로 설정하면...

      • OC0 핀의 출력이 계속 Toggle된다!

      • OCn Interrupt 발생 시마다 Toggle.

    • 만들어낼 수 있는 주파수는?

    fOCn=fclk_I/O2⋅N⋅(1+OCRn)f_{\text{OCn}}=\frac{f_{\text{clk\_I/O}}}{2\cdot N\cdot (1+\text{OCRn})}fOCn​=2⋅N⋅(1+OCRn)fclk_I/O​​

    • .

      • N은 Prescaler Factor.

        • 1, 8, 32, 64, 128, 256, 1024

      • Prescaler Factor를 1(미사용)로 두고... OCRn을 1로 두면 40,000Hz = 40KHz

      • 주의할 것은...

        • Counter가 돌고 있는 중에 TOP Value(OCR0)를 BOTTOM에 가깝게 옮길 때 조심.

        • Counter가 Compare Match를 놓칠 수 있다.

          • 그렇게 되면 TCNT0이 0xFF까지 갔다가 BOTTOM으로 내려온 후부터 제대로 동작한다.

      • 분주비 N과 OCRn의 값을 잘 조정하며 주파수를 제어하면 될 것.

    CTC Mode로 Buzzer 다루기

    코드

    #define F_CPU 16000000UL
    
    #include <avr/io.h>
    #include <util/delay.h>
    
    int main(void){
    	DDRA = 0x01; // 개발보드 상태확인용
    	DDRB = 0x10; // PB4만 Out...
    	PORTB = 0x00;
    	TCCR0 = 0x1C; // 0001 1100, Prescale(64)
    	OCR0 = 60; // 여기까지 하면 2048Hz에 근접. 대략 2049.18Hz.
    	
    	while(1){
    		// 개발보드 상태확인용
    		PORTA = 0x01;
    		_delay_ms(1000);
    		PORTA = 0x00;
    		_delay_ms(1000);
    	}
    }

    설명

    • DDRA와 While문

      • 내가 쓰는 개발보드는 PA0의 Pin-Out이 보드의 LED를 깜빡거리도록 하기 때문에, 개발보드가 잘 돌고 있나 상태 확인을 위해 넣어준 것뿐이다.

    • DDRB = 0x10 -> 0b 0001 0000

      • PB4(OC0) 핀을 Output Pin으로 설정한 것이다.

    • PORTB = 0x00

      • 평범한 기본값 설정이다.

    • TCCR0 = 0x1C -> 0b 0001 1100

      • 이게 중요하다!!!

        4433
        • TCCR0은 이렇게 생겨먹었다. 데이터시트에 오류가 있는지 6번 비트와 3번 비트가 바뀌어 있다(...).

          • WGM01:0 = 2라고 했으므로...

            • WGM01 비트 위치에 1

            • WGM00 비트 위치에 0

          • COM01:0은...

            • 난 Toggle 하고 싶으니까...

              4434
              • COM01 비트 위치에 0

              • COM00 비트 위치에 1

          • CS02:0은 Prescaler 설정하는 영역이다.

            4435
            • 난 분주비 64 쓰고 싶으니까...

              • CS02 위치에 1

              • CS01 위치에 0

              • CS00 위치에 0

      • OCR0 = 60

        • Output Compare Register#1을 60으로...

        • 위~~~에 있는 수식에 넣고 돌려보니 이렇게 설정해야 2048Hz에 가장 가까운 주파수가 나오드라구용.

    결과


    여담 - 이걸로 이틀을 삽질한 이유

    4438443744394440







    - 컬렉션 아티클