BASIC4MCU | 질문게시판 | ADC를 통해 변환한 가변저항 값으로 피아노 연주하기 질문
페이지 정보
작성자 KHYDH 작성일2022-12-01 02:44 조회395회 댓글0건본문
안녕하세요 주의사항에도 불구하고 현재 프로젝트 진행중에 코딩을 제대로 공부한적이 없어 코딩관련 지식이 매우 부족하고 MCU와 관련하여 지식 수준이 매우낮아 마땅히 어디 물어볼곳도 없고 하여 질문드리게 되었습니다. 염치 없지만 시간이 나셔서 답변을 해주신다면 정말 감사할 것 같습니다!
저는 ADC를 통해 변환한 가변저항 값을 범위를 설정해주어 범위 마다 특정 음이 나도록 하여 가변저항을 돌려가며 특정 값의 범위에서 도레미파솔라시도 소리가 나와 직접 피아노를 연주할 수 있도록 하는 코드를 작성하였습니다. 일단 계이름 도에 대한 코드만을 작성하였는데 동작하지않아 가변저항의 값이 변환되는 속도가 굉장히 빨라 piano_play 함수가 실행은 되지만 adc_data값이 너무 빠르게 갱신되어 소리가 나지않는다고 판단하여 가변저항 값에 대한 데이터를 변환시키고 지속시킬 수 있게 하거나 갱신되는 속도를 늦출 수 있는 방법에 대해 찾아보다가 질문드리게 되었습니다. 제가 생각한 이유 때문에 작성한 코드가 생각한대로 동작하지 않는 것인지 작성한 코드 자체가 잘못된 것인지 아니면 애초에 구현할 수 없는것을 제가 시도하고 있는 것인지에 대해 여쭤 보고 싶습니다. 해당 코드에 다른 함수들도 섞여있고 주석도 달아 놓지 않아 보기 불편하실텐데 양해 부탁드리겠습니다..
ATMEGA128, Microchip studio 사용중입니다
#define F_CPU 16000000
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define STOP 0
#define START 1
#define INIT 2
#define C1 523 // 도
#define D1 587 // 레
#define E1 659 // 미
#define F1 699 // 파
#define G1 784 // 솔
#define A1 880 // 라
#define B1 988 // 시
#define C2 C1*2 // 도
#define DLY_4 4
//mode selector
unsigned char mode_sel=0;
// display
const unsigned char Segment_Data[] =
{0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x27,0x7F,0x6F};
char COLUMN[4]={0,0,0,0};
//clock
unsigned char count_int=0;
unsigned int Seconds=0, Minutes=0, Hours=0;
//stopwatch
int state=STOP;
int SHOW_NUMBER=0, SHOW_NUMBER12=0, SHOW_NUMBER34=0;
//piano
volatile int Doremi[8]={C1,D1,E1,F1,G1,A1,B1,C2};
volatile int Doremi_length[8]={DLY_4,DLY_4,DLY_4,DLY_4,DLY_4,DLY_4,DLY_4,DLY_4};
volatile unsigned char TIMERvalue=0xFF;
volatile int freq = 1000, i,j;
//pianoplay
unsigned int adc_data;
// display
void Show_Display(unsigned int number);
void Show_stop_watch_Display(int number);
void ShowDigit(int i, int digit);
// mode
void mode0_clock();
void mode1_stop_watch();
void mode2_piano();
void mode3_piano_play();
//stopwatch
void Run(void);
// piano
void Shimpyo(int time);
void Cutoff_Play(void);
void piano_up(void);
// piano play
void adc_init(void); // void 변수(입력 x)를 갖는 void 함수 adc_init을 선언
void startConvertion(void); // 입력을 필요로 하지않는 void형 함수 startConvertion을 선언한다.
unsigned int readConvertData(void); // 입력을 필요로 하지않는 unsigned int형 함수 readConvertData 함수를 선언한다.
void piano_play();
ISR(TIMER0_OVF_vect)
{
count_int++;
if(count_int == 244)
{
PORTG ^= 0x03;
Seconds++;
count_int=0;
}
}
ISR(TIMER2_OVF_vect)
{
if(mode_sel==2)
{
TCNT2= TIMERvalue;
PORTB ^= 0x10;
}
}
ISR(INT0_vect)
{
if(++mode_sel>3)
{
mode_sel=0;
}
}
ISR(INT1_vect)
{
if(mode_sel==1)
{
if(state==STOP) state=START;
else state=STOP;
}
}
ISR(INT2_vect)
{
if(mode_sel==1)
{
state=INIT;
}
}
int main(void)
{
DDRA = 0xff;
DDRB = 0x10;
DDRC = 0xff;
DDRG = 0x03;
TCCR0 = 0x06;
TCNT0 = 0x00;
TCCR2 = 0x04;
TCNT2 = 0x00;
TIMSK = 0x41;
EICRA=0x3F;
EIMSK=0x07;
SREG |= 0x80;
while (1)
{
switch(mode_sel)
{
case 0:
mode0_clock();
break;
case 1:
mode1_stop_watch();
break;
case 2:
mode2_piano();
break;
case 3:
mode3_piano_play();
break;
default:
mode0_clock();
break;
}
}
}
void Show_Display(unsigned int number)
{
COLUMN[0] = (Minutes%100)/10;
COLUMN[1] = (Minutes%10);
COLUMN[2] = (Seconds%100)/10;
COLUMN[3] = (Seconds%10);
for(int i=0;i<4;i++)
{
ShowDigit(COLUMN[i],i);
_delay_ms(2); // wait for a second
}
}
void Show_stop_watch_Display(int number)
{
COLUMN[0] = number/1000;
COLUMN[1] = (number%1000)/100;
COLUMN[2] = (number%100)/10;
COLUMN[3] = (number%10);
for(int i=0;i<4;i++){
ShowDigit(COLUMN[i],i);
_delay_ms(2); // wait for a second
}
}
void ShowDigit(int i, int digit)
{
PORTC=~(0x01<<digit);
if(mode_sel==1)
{
if(digit==1)
PORTA = Segment_Data[i]|0x80;
else
PORTA = Segment_Data[i];
}
else PORTA = Segment_Data[i];
}
void mode0_clock()
{
Show_Display(Seconds);
if(Seconds>=60)
{
Seconds=0;
Minutes++;
}
if(Minutes>=60)
{
Minutes=0;
Hours++;
}
if(Hours>=24)
{
Hours=0;
}
}
void mode1_stop_watch()
{
Run();
SHOW_NUMBER=SHOW_NUMBER12*100+SHOW_NUMBER34;
Show_stop_watch_Display(SHOW_NUMBER);
}
void mode2_piano()
{
piano_up();
_delay_ms(1000);
}
void mode3_piano_play()
{
piano_play();
}
void Run(void)
{
switch(state)
{
case STOP : break;
case START: SHOW_NUMBER34++;
if(SHOW_NUMBER34>99)
{
SHOW_NUMBER12++;
if(SHOW_NUMBER12>99) SHOW_NUMBER12=0;
SHOW_NUMBER34=0;
}
break;
case INIT : SHOW_NUMBER12=0, SHOW_NUMBER34=0, state=STOP;
break;
}
}
void piano_up(void)
{
for(int i=0;i<8;i++)
{
freq = Doremi[i];
TIMERvalue = 255-(1000000/(8*freq));
Shimpyo(Doremi_length[i]);
Cutoff_Play();
}
}
void Shimpyo(int time)
{
for(int i=0;i<time;i++)
{
_delay_ms(50);
}
}
void Cutoff_Play(void)
{
_delay_ms(300);
TIMERvalue=255;
_delay_ms(20);
}
//ADC initialize
void adc_init(void) // adc_init 함수
{
ADCSRA = 0x00; // A/D 컨버터에 인가되는 클럭의 분주비는 2이고 나머지 A/D 컨버터의 기능이 전혀 사용
// 되지 않으며 A/D 컨버터의 모든 동작이 금지된다.
ADMUX = 0x00; // AD 변환 설정 레지스터로 기준전압이 Aref단자가 되고 변환결과 저장시 위치를 ADCL 0BIT부터 저장한다.
// ADC의 입력단자를 선택하고 단일입력이므로 단극성입력이 ADC0가 된다.
ACSR = 0x80; // 아날로그 비교기 레지스터로 0x80이 1000 0000이므로 7bit가 Set이어서 아날로그 비교기의 동작을 off한다는
// 의미이다.
ADCSRA = 0xC7; // ADC 변환 시작을 의미한다.
}
void startConvertion(void) // startConvertion 함수
{
ADCSRA =0xc7; // ADC 변환 시작을 의미함
ADMUX = 0x00; // ADC 입력을 0으로 설정한다.
ADCSRA = ADCSRA | 0xc7; // ADCSRA 값과 1100 0111값을 or한 결과값을 ADCSRA가 갖게되는데 OR연산시
// 기본적으로 비트 7,6,2,1,0은 항상 1 값을 갖기 때문에 A/D 컨버터의
// 동작이 허용되고 변환이 시작되며 분주비는 128값을 갖는다.
}
unsigned int readConvertData(void) // readConvertData 함수
{
volatile unsigned int temp; // RAM영역의 unsigned int형 변수 temp 선언
while((ADCSRA & 0x10) ==0); //ADCL, ADCH레지스터가 갱신될때까지 무한루프
ADCSRA = ADCSRA | 0x10; // A/D 변환완료 인터럽트를 요청한다. 이때, SREG레지스터의 |비트가 1로 설정되어 있으면 인터럽트
// 발생
temp = (int)ADCL + (int)ADCH*256; // AD 변환값을 읽는다.
ADCSRA = ADCSRA | 0x10; // A/D 변환완료 인터럽트를 요청한다. 이때, SREG레지스터의 |비트가 1로 설정되어 있으면 인터럽트
// 발생
return temp; // temp값을 반환한다.
}
void piano_play()
{
adc_init();
startConvertion();
adc_data = readConvertData();
if(adc_data>=0 && adc_data<128)
{
freq = Doremi[0];
TIMERvalue = 255-(1000000/(8*freq));
Shimpyo(Doremi_length[0]);
Cutoff_Play();
}
}
댓글 0
조회수 395등록된 댓글이 없습니다.