2015-02-15

Buzzer로 산토끼 만들기 강좌 #2

출처 : cafe.naver.com/laydigital/

4-3 기본강좌에 설명된 아래 코드 에서 EOS -1,  int song[] , volatile int state, tone 일때
tone= EOS값에서 소리가 않나는 이유가 signed int (-1)값으로 전달된 값이 인터럽트시마다 발생되는 주파수가 가청 주파수 밖의 영역에서 소리가 발생하여 않듣리는건지 아니면 다른 이유가 있는건지 궁금합니다.

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

#define DO 0
#define RE 1
#define MI 2
#define FA 3
#define SOL 4
#define RA 5
#define SI 6
#define DDO 7
#define EOS -1    // End Of Song  표시
#define ON 0
#define OFF 1

volatile int state, tone;
char f_table[8] = {17, 43, 66, 77, 97, 114, 117, 137};
             // 도레미파솔라시도 에 해당하는 TCNT0 값을 미리 계산해 놓은 값
int song[] = {SOL, MI, MI, SOL, MI, DO, RE, MI, RE, DO, MI, SOL, DDO, SOL, DDO, SOL, DDO, SOL, MI, SOL, RE, FA, MI, RE, DO, EOS};       // 산토끼 계명

ISR(TIMER0_OVF_vect)                                 // Timer/Counter0 오버플루우 인터럽트
{
             if (state == OFF)
             {
                          PORTB |= 1 << 4; // 버저 포트 ON
                           state = ON;
             }
             else
             {
                          PORTB &= ~(1 << 4);         // 버저 포트 OFF
                           state = OFF;        
             }
             TCNT0 = f_table[tone];
}

int main()
{
             int i=0;
             DDRB = 0x10;                     // 버저 연결 포트(Port B의 4번 비트) 출력으로 설정
             TCCR0 = 0x03;                   // 프리스케일러 8분주
             TIMSK = 0x01;                    // 오버플로우 인터럽트 활성화, 즉, TOIE0 비트 세트
             TCNT0 = f_table[song[i]]; // TCNT0 초기화
             sei();                                 // 전역인터럽트 활성화
             do {
                           tone = song[i++]; // 노래 음계
                           _delay_ms(500);                // 한 계명당 지속 시간
             }while(tone != EOS);                        // 노래 마지막 음인지 검사








위의 내용에 대한 수정입니다.

while(tone != EOS);
이후에
while(1);

이렇게 해서 프로그램이 종료가 되지 않게 안다면 마지막에 어떤 소리가 있습니다.

call main
c6: 0c 94 71 00 jmp 0xe2 ; 0xe2 <_exit>

000000e2 <_exit>:
e2: f8 94 cli

000000e4 <__stop_program>:
e4: ff cf rjmp .-2 ; 0xe4 <__stop_program>


우리가 작성하는 main이 실제로 프로그램의 시작점은 아닙니다.
리셋이 걸리면 리셋벡터에서 시작해서
프로그램이 실행될기 위해
1. 스택 준비(SP를 ram 에 맞게)
2. 초기변수영역 으로 flash에서 데이타 복사
3. bss(초기화없는 변수)를 0으로 clear
4. main으로 호출

그리고 main이 실행이 종료되면 
위 <_exit> 라는 곳으로 가게 됩니다.

인터럽트를 막고
rjmp -2 ( 자신으로 jump 즉 while(1); 과 같음)
을 합니다.

즉 인터럽트가 중단됩니다.
따라서 소리가 나지 않습니다.

처음에 올린 질문에 올려주신 답변이 사라져서 수정전 내용을 다 확인하지 못했습니다. 수정전 내용 설명이 끊기고 수정내용만 있어서 전체적인 설명 문맥을 이해하기 좀 힘듭니다.

이전 글을 보지 않으셔도 됩니다.

programmer가 작성한 소스는
startup 코드와 링크됩니다.

avrgcc 환경에서는 
위 assembly 로 알수 있는 것처럼
main 함수가 종료되면
cli 를 하고 (인터럽트 disable)
rjmp -2
를 수행하도록 되어 있습니다.
즉 인터럽트가 걸리지 않으면서 무한루프에서 실행하고 있습니다.
이 부분만 이해하시면 EOS 값이 된 경우에도 소리가 나지 않는 이유를 아실겁니다.

만약

타이머의 하드웨어적인 기능으로 OC0를 토글시키는 방식으올 구현했다면
인터럽트가 걸리지 않아도 OC0 신호는 지속적으로 나오므로
마지막에 OCR0 에 설정한 주기의 소리가 계속 발생하게 되겠죠

대신 만약 
타이머에서 자동으로 OC0 를 하드웨어적으로 토글하도록 프로그램했다면
인터럽트가 걸리지 않아도 파형은 출력이 됩니다.


s/w로 OCR0 값을 저장하는 경우에도
만약
while(tone != EOS)
를 벗어나서
타이머 인터럽트 걸리기 전에
cli
rjmp -2
이렇게 실행되면 
마지막 tone 값은 OCR0에 저장될 기회를 잃게 됩니다.

ATMEL-128- 8bit DATASHEET 의 8bit timer- PWM쪽을 쭈욱보고 나서 다시 살펴보니 이전에 설명해주신 OCn pin 과 interrupt의 신호 흐름의 부분이 제대로 이해가 되네요. 확실히 이해도가 일정수준 이상은 형성되어 있어야 흐름에 대한 부분이 거부감 없이 다가오네요, 감사합니다.

이해의 정도가 점점 깊어지시네요..^^

포기하지 않으시고, 예전 글까지 이해하실려는 자세가 좋으십니다.. ~

위 님의 코드 경우에
타이머 인터럽트 루틴에서 해당 핀을 토글 한다면
main이 종료된상태에서 인터럽트가 걸리지 않으므로 소리는 나지 않겠죠

중간에 제가 못보셨을까봐 하단 질문을 잘라서 새로 질문을 올리는 바람에 그때 아래쪽에 달아주신 답변이 
사라져서 그런데요, TCNT0= f_tbl[-1];값은 어떻게 되는건가요?

상황에 따라 다릅니다.
변수의 배치는 컴파일러 나릅입니다.

생성된 map 화일을 보시면 
f_tbl의 시작 주소를 알수 있습니다.
그 바로 이전 주소에 할당된 변수 값이 됩니다.

실제로는 invalid 한 값이죠.

임베디드홀릭 아 초보적인 질문입니다만 map 파일을 어디서 보는건가요? 그동안 설명해 주시면서 올려주신 어셈블리 내용그렇고 어셈블리 data도 같이 생성해서 확인해보고 싶은데 어떻게 확인하는건지 생성 방법및 map file 확인 법을 알려주시면 감사하겠습니다.

avrstudio 에서 Other files 디렉토리에 보시면 .map, ..lss 화일이 있습니다.

6.x는 별로 사용을 안하는데, 아마 .hex 화일과 같은 디렉토리일겁니다.  

No comments:

Post a Comment

Total Pageviews