BASIC4MCU | 질문게시판 | atmega 음질
페이지 정보
작성자 더거덕 작성일2023-11-12 16:33 조회1,103회 댓글6건본문
안녕하세요.
atmega2560으로 음악 재생하는 프로그래밍을 하는 중입니다.
sd 카드 라이브러리를 이용해서 음악과 관련된 data를 찾아 뽑아서 그걸로 pwm을 만들어서 amp에다가 쏴주는 형식인데요 serial print로 원본 데이터와 계산한 데이터를 비교 분석했는데 여기까지는 노이즈가 끼지 않았습니다. 그리고 ISR을 이용해서 OCR 값을 계속 업데이트 하게 만들었는데 음질이 썩 좋아지지가 않네요. 최적화를 해야할 부분을 잘 모르겠습니다. 조언을 얻을 수 있을까요? 기본적인 설정은 했습니다. 첨언을 하자면
4개의 pwm은 8bit fast pwm 이고 clk 는 1 compare match는 clear 입니다!! 회로 설계단계에서 amp에 weighted sum을 하게 만들었습니다.
파일은 44.1kHz, stereo type, 16bit unsigned 입니다.
ps OCR 값을 변경하는걸 5개의 인터럽트에다가 하나씩도 넣어보고 여러개도 넣어보고 했는데 이게 그나마 괜찮은 것 같습니다. 주요 문제점은 음질인 것 같은데 메모리 문제로 double buffer를 3000 size 2개 밖에 쓰지 못합니다. 양해 부탁드려요 감사합니다.
void loop() {
if(song_data == 3000) {
song_data = 0;
WorkingBuf = !WorkingBuf;
playing.read(buf[WorkingBuf],3000);
}
else {
uint16_t buf_16bit_left = buf[WorkingBuf][song_data] + (buf[WorkingBuf][song_data+1] << 8) + 0x8000;
uint16_t buf_16bit_right = buf[WorkingBuf][song_data+2] + (buf[WorkingBuf][song_data+3] << 8) + 0x8000;
song_data += 4;
// Left channel
PWM2H = buf_16bit_left >> 8;
PWM2L = buf_16bit_left & 0x00FF;
// Right channel
PWM4H = buf_16bit_right >> 8;
PWM4L = buf_16bit_right & 0x00FF;
}
}
ISR(TIMER1_COMPA_vect) {
OCR2A = PWM2H;
OCR2B = PWM2L;
OCR4A = PWM4H;
OCR4B = PWM4L;
}
ISR(TIMER2_COMPA_vect) {
}
ISR(TIMER2_COMPB_vect) {
}
ISR(TIMER4_COMPA_vect) {
}
ISR(TIMER4_COMPB_vect) {
}
댓글 6
조회수 1,103더거덕님의 댓글
더거덕 작성일코드를 다시 뜯어보니 loop 문 안에서 읽어오는 것과 계산하는 것이 순차적으로 진행이 되네요. 이 부분이 큰 문제 같은데 병렬적으로 동시에 진행하게 어떻게 하나요?
master님의 댓글
master 작성일
44.1kHz의 주기는 22.67573696145125usec
이 시간안에 sd에서 데이터를 읽어와서 pwm으로 출력해야 합니다.
시간적으로 여유가 있어야 하며
정확한 시간에 맞춰서 출력해야 합니다.
타이머인터럽트를 사용하는 것이 좋겠죠
만약 시간상 타이머인터럽트로 21kHz로 설정해야 한다면
녹음파일을 21KHz로 맞춰서 음원을 변경해주는 것이 좋습니다.
//
PWM2H = buf_16bit_left >> 8; PWM2L = buf_16bit_left & 0x00FF; // Left channel
PWM4H = buf_16bit_right >> 8; PWM4L = buf_16bit_right & 0x00FF; // Right channel
8비트 pwm이라고 하면서 왜 16비트로 읽고 있나요?
8비트로 저장하고, 8비트로 읽으면 이런 연산이 필요없어지죠
파일은 PCM 형식으로 저장하고 있겠죠?
더거덕님의 댓글
더거덕 작성일그렇게 한 이유는 wav 파일이 16비트로 되어있고 그게 left channel right channel로 오기 때문입니다. dac를 사용하면 좋겠지만 그럴 수가 없어 8bit pwm 4개로 16bit dac를 유사하게 흉내내보려고 했습니다. 아날로그로 weighted sum해서 256*pwmH + pwmL은 회로설계 단계에서 해결했습니다. 그리고 44.1kHz 맞추기 위해서 no prescale 하고 OCR1A를 361로 해서 주파수를 맞추었습니다. 더블 버퍼를 잘 이용 못하고 있는 것 같은데 어떻게 생각하시는지 의견을 여쭈어봐도 될까요? 감사합니다.
master님의 댓글
master 작성일
16비트 wave 파일을 8비트 PCM파일로 변경해서 저장하세요
요즘 뭐 사용하는지 잘 모르겠지만 옛날에는 쿨에디터 같은 프로그램으로 파일 변경 했습니다.
master님의 댓글
master 작성일
else{
PWM2H=buf[WorkingBuf][song_data+1]+0x80; PWM2L=buf[WorkingBuf][song_data ]; // Left channel
PWM4H=buf[WorkingBuf][song_data+3]+0x80; PWM4L=buf[WorkingBuf][song_data+2]; // Right channel
song_data += 4;
}
}
불필요한 연산을 제거 했습니다...만
loop()함수에서 읽는 시간과 타이머인터럽트에서 출력하는 시간이 동기가 맞을리 없습니다.
master님의 댓글
master 작성일
void loop(){}
//
ISR(TIMER1_COMPA_vect) {
if(song_data<3000){
OCR2A=buf[WorkingBuf][song_data+1]+0x80; OCR2B=buf[WorkingBuf][song_data ]; // Left
OCR4A=buf[WorkingBuf][song_data+3]+0x80; OCR4B=buf[WorkingBuf][song_data+2]; // Right
song_data+=4;
}
else{ song_data=0; WorkingBuf=!WorkingBuf; playing.read(buf[WorkingBuf],3000); }
}
타이머인터럽트에서 처리하면 동기를 맞출 필요가 없겠죠
인터럽트에서 사용하는 모든 전역변수는 volatile 선언을 해줘야 합니다.