Atmega8 벨소리. MK AVR의 간단한 사운드 사이렌. 사운드 모듈 사용

이 기사에서는 AVR의 음악 합성 원리를 설명합니다. 포함된 소프트웨어를 사용하면 모든 미디 파일을 미디 파일로 변환할 수 있습니다. 원천 AVR 마이크로컨트롤러용 C에서는 기성 개발에 음악 조각 재생을 추가합니다. 오르골에 소프트웨어를 사용하는 예를 고려합니다.

먼저 모든 작동 방식을 보여주는 짧은 비디오입니다.

소프트웨어가 허용하는 것

PC 소프트웨어를 사용하면 선택한 미디 파일을 재생하는 CodeVision AVR용 C 소스를 얻을 수 있습니다.

1. common\hxMidiPlayer.h, common\hxMidiPlayer.c를 프로젝트에 연결합니다. ATMega8Example\melody.h, ATMega8Example\melody.c, ATMega8Example\hxMidiPlayer_config.h 템플릿을 복사하고 연결합니다.
2. MidiToC.exe 실행
3. 미디 파일을 불러옵니다.
4. 플레이어를 설정합니다: 샘플링 속도, 채널 수, 파형 등. 소프트웨어는 AVR이 재생되는 것과 동일한 방식으로 멜로디를 재생합니다.
5. “Create player config”를 클릭하고 소스를 hxMidiPlayer_config.h에 붙여넣습니다.
6. '멜로디 코드 만들기'를 클릭하고 소스를 melody.c에 붙여넣으세요.
7. 우리 프로젝트에서는 PWM 또는 외부 DAC를 통해 사운드를 출력하기 위해 Player_Output() 메서드를 구현합니다.
8. 타이머를 샘플링 속도로 설정하고 인터럽트에서 Player_TimerFunc()를 호출합니다.
9. Player_StartMelody(&s_melody, 0)를 호출합니다.

멜로디는 타이머 인터럽트에서 재생됩니다. 이는 재생 중에 마이크로컨트롤러가 유용한 작업을 수행할 수도 있음을 의미합니다.

작동 원리

이 기사의 나머지 부분에서는 이 모든 것이 어떻게 구현되는지 간략하게 설명하려고 합니다. 안타깝게도 내용이 너무 짧지는 않을 것입니다. 관심이 없으면 즉시 "소프트웨어 설명" 및 "플레이어 API" 섹션으로 이동할 수 있습니다.

음악이란 무엇인가

음악은 다양한 주파수와 지속 시간을 갖는 일련의 소리입니다. 소리의 기본 고조파의 주파수는 특정 음표의 주파수와 일치해야 합니다. 소리의 진동 주파수가 음표의 주파수와 다르다면 우리는 그 음악가가 "조율이 맞지 않는" 것처럼 보입니다.

테이블. 주파수(Hz)를 참고하세요.

모든 음표는 옥타브로 나누어지며 각 음표는 7개 + 반음 5개(피아노의 검은 건반)입니다. 인접한 옥타브에 있는 음표의 주파수는 정확히 2배만큼 다릅니다.

가장 간단한 음악 플레이어에는 멜로디의 음표 순서(음표 + 지속 시간)가 포함된 표와 음표 주파수가 포함된 표가 포함되어 있습니다. 사운드를 합성하기 위해 타이머 채널 중 하나가 사용되며 이는 구불구불한 형태를 형성합니다.

불행하게도 이러한 원시적인 연주자는 고정된 파형(구형파)을 갖고 있어 실제 악기와 별로 유사하지 않으며 한 번에 하나의 음표만 연주할 수 있습니다.

실제 멜로디는 최소한 두 부분(솔로 + 베이스)으로 구성되며, 피아노로 연주할 때 다음 음이 시작될 때 이전 음이 계속해서 들립니다. 피아노의 구조를 기억하면 이해하기 쉽습니다. 각 음표는 별도의 현에 해당합니다. 건반 위에 손을 대면 동시에 여러 현의 소리를 낼 수 있습니다.

일부 마이크로컨트롤러에는 여러 타이머 채널이 있으며, 이를 사용하여 여러 음을 동시에 연주할 수 있습니다. 그러나 일반적으로 이러한 채널은 귀중한 리소스이므로 모두 사용하는 것은 바람직하지 않습니다. 물론, 우리가 오르골을 만드는 것이 아니라면 말이죠.
전체적으로 다성음악과 악기의 다양한 사운드를 얻으려면 사운드 합성을 사용해야 합니다.

AVR의 사운드 합성

hxMidiPlayer는 오디오 합성을 사용하며 다양한 파형으로 동시발음수를 재생할 수 있습니다. 플레이어는 8-22KHz의 주파수로 타이머 인터럽트 핸들러에서 출력 신호의 진폭을 계산합니다(프로세서 전력이 어느 정도인지, 파형 및 채널 수에 따라 다름).

소리 합성의 원리는 정현파 합성의 예를 사용하여 설명할 수 있습니다.

사인 진폭 값이 포인트 인덱스 * 2 * PI / 64 (한 기간)에 기록되는 각 셀에서 크기 64의 테이블을 살펴 보겠습니다.

정적 const 플래시 uint8_t s_sineTable[ 64 ] = ( 0x80, 0x82, 0x84, 0x86, 0x88, 0x8A, 0x8C, 0x8D, 0x8F, 0x90, 0x91, 0x93, 0x93, 0x94, 0x95, 0x95, 0x9 5, 0x95, 0x95, 0x94 , 0x93, 0x93, 0x91, 0x90, 0x8F, 0x8D, 0x8C, 0x8A, 0x88, 0x86, 0x84, 0x82, 0x80, 0x7E, 0x7C, 0x7A, 0x78, 0x76, 0x74, 0x73, 0 x7 1, 0x70, 0x6F, 0x6D, 0x6D, 0x6C, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6C, 0x6D, 0x6D, 0x6F, 0x70, 0x71, 0x73, 0x74, 0x76, 0x78, 0x7A, 0x7C, 0x7E);

128(0x80)은 0에 해당하고, 255(0xff)는 최대 양수 점, 0은 최대 음수 점에 해당합니다.

이제 1000Hz의 주파수에서 호출되는 타이머 인터럽트에서 테이블의 값을 외부 DAC로 출력한다고 가정해 보겠습니다.

정적 uint8_t s_index = 0; // 타이머1 출력 비교 인터럽트 서비스 루틴 인터럽트 void 타이머1_compa_isr(void) ( SetDac(s_sineTable[ s_index]); if (s_index == 63) ( s_index = 0; ) else ( s_index++; ) )

결과적으로 우리는 무엇을 얻게 될까요? 우리는 1000/64Hz의 주파수로 정현파 진동을 얻을 것입니다.

이제 인터럽트의 인덱스를 1이 아닌 2만큼 늘려보겠습니다.
분명히 출력 발진 주파수는 이미 1000/64 * 2Hz입니다.

일반적으로 빈도 F를 얻으려면 다음과 같이 테이블의 인덱스를 늘려야 합니다.
추가 = F / 1000 * 64

이 숫자는 분수일 수 있지만 고속을 얻기 위해 고정 소수점 연산이 사용됩니다.

표의 항목 수와 타이머의 빈도는 합성된 사운드의 품질에 영향을 미칩니다. 우리의 경우에는 주기당 테이블의 항목이 64개이면 충분하며 타이머 주파수는 12kHz입니다. 허용되는 최소 타이머 주파수는 8kHz이고 이상적인 주파수는 44kHz입니다.

분명히 12kHz의 타이머 주파수를 사용하면 주기당 최소 2개의 스위치를 만들어야 하므로 최대 6kHz 구형파를 생성할 수 있습니다. 그러나 각 타이머 틱에서 출력 상태가 올바르게 계산되면 더 높은 주파수를 계속 인식할 수 있습니다.

비정현파 진동 기간에 대한 값을 테이블에 입력하면 다른 소리를 얻을 수 있습니다.

감쇠

악기가 현을 기반으로 하는 경우(예: 피아노) 건반을 누르면 사운드가 부드럽게 페이드 아웃됩니다. 보다 자연스러운 신디사이저 사운드를 얻으려면 음 시작 후 진동의 진폭을 부드럽게 줄여야 합니다(진동을 감쇠 형태로 "감싸는" 즉 "엔벨로프").

플레이어에는 음표가 시작되는 순간부터 사인(또는 기타 파형)의 진폭을 줄이는 데 사용하는 감쇠 테이블이 포함되어 있습니다.
이러한 껍질에 "감겨 있는" "사인"은 기계식 오르골의 소리와 비슷합니다.

사행 합성

구불구불한 파동의 특별한 모양으로 인해 합성이 상당히 단순화됩니다. 이 경우에는 테이블이 사용되지 않습니다. 현재 타이머 틱에서 주어진 주파수에서 출력이 어떤 상태(1 또는 0)를 가져야 하는지 계산하는 것으로 충분합니다. 이는 정수 산술을 사용하여 수행되며 매우 빠르게 작동합니다. 이는 8비트 셋톱 박스에서 음악을 재생하기 위해 구형파를 사용하는 것이 인기가 있는 이유를 설명합니다.

예: 카운터 선언:

정적 uint16_t s_counter = 0;

각 타이머 인터럽트마다 0x8000씩 증가하고 카운터의 가장 중요한 비트가 포트로 출력됩니다.

// 타이머1 출력 비교 A 인터럽트 서비스 루틴 인터럽트 void 타이머1_compa_isr(void) ( PORTA.0 = (s_counter >> 15) & 1; s_counter += 0x8000; )

0x8000 + 0x8000 = 0x10000이므로 s_counter 변수가 오버플로되어 17번째 비트가 삭제되고 변수에 0x0000이 기록됩니다.
따라서 타이머 주파수가 8KHz이면 출력은 4KHz 구형파가 됩니다.
카운터를 0x4000씩 늘리면 2KHz 구형파가 생성됩니다.

일반적으로 다음을 추가하여 주파수 F를 얻을 수 있습니다.
추가 = F / 8000 * 0x10000

예를 들어 주파수가 1234Hz인 구형파를 얻으려면 0x277C를 추가해야 합니다. 실제 빈도는 주어진 빈도와 약간 다를 수 있습니다. 왜냐하면 용어를 정수로 반올림하기 때문입니다. 이것은 신디사이저에서 허용됩니다.

실제 악기의 소리 합성

피아노 연주 전 음표의 사운드를 디지털화할 수 있습니다(ADC를 사용하여 일정한 간격으로 사운드 진폭 값을 메모리에 저장).
그런 다음 사운드를 재생합니다(DAC를 사용하여 일정한 간격으로 녹음된 값을 출력).

일반적으로 드럼을 합성하려면 드럼 사운드를 녹음하고 적절한 순간에 재생해야 합니다. 8비트 콘솔에서는 드럼 소리 대신 "백색 소음"이 사용됩니다. "백색 잡음"에 대한 진폭 값은 생성기를 사용하여 얻습니다. 난수. 메모리 비용은 최소화됩니다.
hxMidiPlayer는 드럼 합성을 위해 "백색 잡음"을 사용합니다.

채널 믹싱

주어진 타이머 틱의 사운드 진폭은 각 채널에 대해 개별적으로 계산됩니다. 최종 진폭 값을 얻으려면 모든 채널의 값을 더해야 합니다. 정확하게 말하면, 인지된 음량은 대수 의존성을 따르기 때문에 합을 조정하는 것이 필요합니다. 그러나 이러한 간단한 신디사이저에서는 간단한 덧셈만으로 해결해야 합니다. 따라서 각 채널의 최대 진폭은 255/N입니다.

AVR에서 사운드 출력

필요한 모든 계산을 수행한 후 플레이어는 아날로그로 변환해야 하는 신호 레벨을 수신합니다. 이러한 목적으로 외부 DAC 또는 PWM을 사용할 수 있습니다.
두 경우 모두 수신된 신호를 필터링하여 합성 및 반올림의 저주파로 인해 발생하는 고주파 노이즈를 제거하는 것이 좋습니다.

외부 병렬 DAC로 출력

정확한 DAC 칩을 사용하는 것은 의미가 없으므로 이러한 프로젝트는 일반적으로 R2R 매트릭스를 사용합니다.

이 방식을 사용하면 계산된 진폭을 포트에 출력하기만 하면 됩니다.

PORTB = 샘플;

결점:
1) R2R 매트릭스의 출력이 너무 약한 신호, 아날로그 증폭기의 사용은 필수입니다.
2) 최소 5개(바람직하게는 8개)의 핀을 사용해야 합니다.
이 방법은 사용 가능한 PWM 채널이 없는 경우에만 정당화됩니다.

(핀을 절약하려면 SPI 인터페이스가 있는 외부 ADC를 사용할 수 있습니다).

PWM

사용 가능한 PWM 채널이 있는 경우 가장 쉬운 방법은 이 방법을 사용하는 것입니다.

PWM 초기화(ATMega8):

// 타이머/카운터 2 초기화 // 클럭 소스: 시스템 클럭 // 클럭 값: 20000.000 kHz // 모드: 고속 PWM top=0xFF // OC2 출력: 비반전 PWM ASSR=0x00; TCCR2=0x69; TCNT2=0x00; OCR2=0x00; 샘플 출력: void Player_Output(uint8_t 샘플) ( OC2 = 샘플. )

PWM을 사용하는 일반적인 방법에는 RC 필터를 사용하여 출력 신호를 평활화하는 작업이 포함됩니다.

안타깝게도 필터링 후에는 신호가 너무 약해지기 때문에 스피커를 연결하려면 아날로그 앰프를 만들어야 합니다.

회로를 단순화하려면 스피커 자체까지 "디지털"을 유지하는 것이 좋습니다. 값싼 스피커는 여전히 30kHz 이상의 주파수를 재생할 수 없으므로 필터링할 필요가 없습니다. 디퓨저 자체는 높은 PWM 주파수를 "필터링"합니다.

더 많은 전류를 얻으려면 트랜지스터 증폭기를 사용할 수 있습니다. R1은 스피커에 필요한 전류를 제공하도록 선택됩니다.

장난감의 소형 스피커를 연결하는 방법은 다음과 같습니다.

대형 스피커의 경우 트랜지스터 2개를 사용하여 드라이브를 조립하고 LC 필터를 설치하여 소음을 제거하는 것이 좋습니다.

커패시터 C1은 PWM이 작동하지 않을 때 스피커를 통한 전류를 제한하는 역할을 합니다. 또한 직렬 커패시터가 포함되어 있어 0에 대해 대칭인 신호가 스피커에 도달합니다. 따라서 스피커 콘은 중앙의 "이완된" 위치를 기준으로 이동하며 이는 음질에 긍정적인 영향을 미칩니다.
이 경우 트랜지스터는 스위칭 모드로 동작하므로 베이스 오프셋을 보상할 필요가 없습니다.

PWM, 2핀 연결

처음 두 회로의 단점은 스피커에 한 방향으로 전류가 공급된다는 것입니다. 전류의 방향을 바꾸면 허용 전력을 초과하지 않고 부피를 2배로 늘릴 수 있습니다. 이를 위해 스피커는 마이크로 컨트롤러의 두 핀(예: OC1A 및 /OC1A)에 비반전 및 반전 핀에 연결됩니다. 반전되지 않은 출력이 없으면 반전 모드(OC1B)에서 두 번째 채널을 사용할 수 있습니다.

// 타이머/카운터 1 초기화 // 클럭 소스: 시스템 클럭 // 클럭 값: 24500,000 kHz // 모드: Fast PWM top=0x00FF // OC1A 출력: Non-Inv. // OC1B 출력: 반전됨 // 노이즈 제거기: 꺼짐 // 하강 에지에서 입력 캡처 // 타이머1 오버플로 인터럽트: 꺼짐 // 입력 캡처 인터럽트: 꺼짐 // 비교 A 일치 인터럽트: 꺼짐 // 비교 B 일치 인터럽트: 꺼짐 TCCR1A =0xB1; TCCR1B=0x09; TCNT1H=0x00; TCNT1L=0x00; ICR1H=0x00; ICR1L=0x00; OCR1AH=0x00; OCR1AL=0x00; OCR1BH=0x00; OCR1BL=0x00; void Player_Output(uint8_t 샘플) ( OCR1A = 샘플; OCR1B = 샘플; )

PWM, 2핀, 클래스 D 증폭기

제안된 회로의 단점은 침묵 동안의 전류 소모이다.
우리에게 "침묵"은 신호 레벨 128, 즉 50% 충전의 PWM에 해당합니다. 전류는 항상 스피커를 통해 흐릅니다!

소프트웨어를 약간 변경하면 상당히 강력한 소프트웨어 및 하드웨어 클래스 D 증폭기를 얻을 수 있습니다.

Void Player_Output(uint8_t 샘플) ( if (sample >= 128) ( TCCR2=0x21; //정상, 비교 일치 시 삭제 TCCR2=0x21 | 0x80; //CLEAR OC2 PORTC.0 = 0; TCCR2=0x69; //non -PWM 반전 OCR2 = (샘플-128) * 2; ) else // if (샘플< 128) { TCCR2=0x31; //normal, set on compare match TCCR2=0x31 | 0x80; //SET OC2 PORTC.0 = 1; TCCR2=0x79; //inverting PWM OCR2 = (128-sample) *2; } }

이 경우 한 쌍의 트랜지스터는 PWM 출력에 연결되고 두 번째 쌍은 일반 디지털 출력에 연결됩니다.

코드에서 볼 수 있듯이 128보다 큰 신호는 한 방향으로 흐르는 전류로 간주하고, 128보다 낮은 신호는 다른 방향으로 흐르는 전류로 간주합니다. 128에서는 두 스피커 핀이 모두 동일한 전원 공급 장치 핀에 연결되어 있으며 전류가 없습니다. 레벨 128을 벗어나면 PWM 충전량이 증가하고 해당 극성의 전류가 스피커를 통해 흐릅니다.

구현에서 중요한 점은 두 번째(일반 디지털) 핀(PORTC.0)을 전환하는 순간 PWM 출력을 원하는 상태로 강제 전환하는 것입니다. OCR2 레지스터에 쓰기는 버퍼링되어 PWM 결함을 제거합니다. 기간이 끝날 때까지 기다리지 않고 즉시 PWM 출력을 전환해야 합니다.

후자의 회로는 단순성, 에너지 절약 및 전력 출력 측면에서 IMHO 최고의 옵션입니다.

SquareWave 파형으로 사운드 출력

사행을 합성할 때 단순화된 알고리즘이 사용됩니다.

각 채널(드럼 포함)은 0 또는 1을 출력합니다. 따라서 3채널 턴테이블은 0..3 범위의 값을 출력합니다. 따라서 PWM을 사용할 때 출력 절차는 다음과 같습니다.

Void Player_Output(uint8_t 샘플) ( OCR2 = 샘플 * (255 / HXMIDIPLAYER_CHANNELS_COUNT); )

PWM을 사용하지 않는 경우 3채널 멜로디를 출력하려면 일반 디지털 출력 2개와 2비트 R2R 매트릭스로 충분합니다.

미디 형식

결과 멜로디 코드를 보면 배열이 작은 범위의 반복되는 숫자를 사용하고 있음을 쉽게 알 수 있습니다. 이는 이해할 수 있습니다. 멜로디는 1-2 옥타브 내에서 제한된 수의 음표를 사용하고 멜로디 템포는 고정되어 있습니다. 동일한 지연, 채널 수는 0..15 범위입니다.
이 모든 것은 일종의 압축 알고리즘을 적용하여 결과 배열을 크게 줄일 수 있음을 의미합니다.
ZIP과 같은 알고리즘은 우수한 압축을 제공하지만 작동하려면 많은 메모리가 필요합니다(ZIP 사전 - 64Kb). 메모리가 거의 필요하지 않은 매우 간단한 압축 방법을 사용할 수 있으며 그 본질은 다음과 같습니다.

1바이트에는 모든 숫자가 0~255 범위에 균등하게 분포되어 있으며 각 숫자는 8비트로 표시됩니다. 우리의 경우 일부 숫자는 다른 숫자보다 훨씬 더 일반적입니다. 자주 발생하는 숫자를 더 적은 비트로 인코딩하고 덜 자주 발생하는 숫자를 더 많은 비트로 인코딩하면 메모리를 얻을 수 있습니다.

고정된 인코딩 방법을 선택합니다. 비트 000,001과 010(길이 - 3비트)의 조합은 가장 자주 발생하는 3개의 숫자를 나타냅니다. 비트 조합 0110, 0111(길이 – 4비트) – 다음 2개의 가장 일반적인 숫자 등:

//000..010 - 0..2 //011 x 3..4 //100 xx 5..8 //101 xxx 9..16 //110 xxx 17..24 //111 즉시

111(길이 – 11비트)로 시작하는 조합은 다른 모든 숫자를 인코딩합니다.
비트 인코딩 방법은 다를 수 있습니다. 나는 여러 가지 방법을 시도해 보았고 그러한 데이터에 대해 최상의 결과를 제공하는 방법을 선택했습니다.

압축 절차는 다음과 같습니다.
1. X = 에 대한 스트림에서 숫자 X의 총 개수를 계산합니다.
2. 스트림에 나타나는 빈도를 줄여 정렬합니다.
3. 처음 25개의 숫자를 선택하세요. 더 적은 비트로 인코딩됩니다.
4. 입력 스트림을 인코딩합니다.

출력은 가장 자주 발생하는 25개의 숫자와 비트 스트림의 배열입니다.
이 압축을 사용하면 최소한의 메모리 및 성능 비용으로 50% 압축을 달성할 수 있습니다. 불행하게도 이로 인해 플레이어 코드가 증가하므로 짧은 멜로디에는 압축을 권장하지 않습니다.

음표 주파수 저장

모든 음표의 주파수를 메모리의 테이블에 저장하는 것은 상당히 비용이 많이 듭니다. 실제로 미디 번호로 음표의 주파수를 결정하는 공식이 있습니다.

F = 2^((N - 69)/12) * 440,Hz

그러나 분수력을 계산하는 것은 매우 어렵습니다. 대신 플레이어는 상위 옥타브에 12개의 음표 주파수를 저장합니다. 낮은 옥타브에 있는 음표의 주파수는 주파수를 2^Y만큼 더 줄여 결정됩니다. 여기서 Y는 아래로 옥타브 수를 나타냅니다.

압축의 추가 개발

멜로디에는 반복되는 부분(“코러스”, “절”)이 포함되는 경우가 많습니다. 반복되는 단편을 찾아 멜로디를 단편의 형태로 제시함으로써 거의 시간을 들이지 않고 멜로디를 50% 더 줄일 수 있습니다. 그리고 생산성. 프로젝트를 복잡하게 만들지 않기 위해 그러한 알고리즘을 구현하지 않았습니다.

소프트웨어 설명

변환기 프로그램의 메인 창:

미디 로드 버튼을 사용하면 미디 파일을 로드할 수 있습니다. 프로그램은 현재 선택된 매개변수로 파일 재생을 즉시 시작하여 하드웨어에 있을 사운드를 시뮬레이션합니다.

정보 창(4)에는 다음이 표시됩니다.
– 길이 – 선택한 멜로디 조각의 길이(ms);
– 최대 활성 신디저 채널 – 동시에 활성화되는 신디사이저 채널의 최대 수;
– 최대 활성 드럼 채널 – "드럼"을 재생하는 동시에 활성화된 신디사이저 채널의 최대 수입니다.
– 최대 활성 스테레오 음표 – 동일한 음표를 재생하는 최대 채널 수(아래 참조)
– 예상 크기(바이트) – 멜로디 크기(바이트). "Custom Sample" 모드에서는 크기가 A+B로 표시됩니다. 여기서 A는 멜로디 크기이고 B는 샘플 크기입니다. 플레이어 코드의 크기는 여기서 지정되지 않습니다.

진행률 창에는 현재 재생 위치가 표시됩니다.
진행률 표시줄을 클릭하면 지정된 지점부터 재생을 시작할 수 있습니다.
왼쪽과 오른쪽에 있는 입력 상자를 사용하면 멜로디 조각의 시작과 끝을 ms 단위로 지정할 수 있습니다.

빨간색으로 표시된 "멜로디를 재생할 채널이 충분하지 않습니다" 라벨은 현재 설정으로 멜로디를 재생할 수 있는 신디사이저 채널이 충분하지 않음을 나타냅니다. 플레이어가 무료 채널을 찾지 못하면 가장 오래된 음표가 꺼집니다. 대부분의 경우 이것은 잘 작동합니다. 멜로디가 귀에 맞지 않게 들리는 경우에만 채널 수를 늘리는 것이 합리적입니다.

설정은 플레이어 설정과 미디 파일 처리 설정으로 나눌 수 있습니다. 플레이어 구성과 멜로디 코드가 동일한 플레이어 설정으로 생성된 경우 플레이어는 결과 멜로디 코드를 재생할 수 있습니다. 또한 플레이어는 채널 수가 더 적은(그러나 더 크지는 않음) 플레이어를 위해 생성된 코드의 멜로디를 재생할 수 있습니다.

플레이어 하드웨어 설정에는 다음이 포함됩니다.

– 샘플링 속도 – 합성 주파수. 최대 융합 빈도는 실험적으로 결정됩니다. Atmega 16MHz를 기반으로 6채널 플레이어의 경우 12000Hz에서 시작하여 하드웨어 플레이어에서 멜로디 왜곡이 눈에 띄게 느껴질 때까지 원하는 대로 늘릴 수 있습니다. 최대 주파수는 채널 수, 파형 및 멜로디 자체의 복잡성에 따라 다릅니다.

– 파형 – 파형:
– 구형파 – 사행;
– 사인 – 사인;
– 사인 + 엔벨로프 – 감쇠가 있는 사인;
– 파형 * + 엔벨로프 – 감쇠 유무에 관계없이 비정현파에 대한 다양한 옵션;
– 맞춤 샘플 – 기기 샘플을 사용합니다.

"샘플 로드" 버튼을 사용하면 WAV 파일에서 샘플을 로드할 수 있습니다. WAV 파일은 PCM 8비트 모노, 4173Hz, C-5 형식이어야 합니다. 힌트: 주파수를 높이고 음표를 낮출 수 있지만 플레이어 설정에서 피치를 변경하세요. 형식 확인이 없습니다. 형식이 다르면 사운드가 올바르게 재생되지 않습니다.
피치 – 사운드의 피치를 변경할 수 있습니다. 예를 들어 1옥타브 높게 연주하려면 피치 +12를 설정해야 합니다.

압축 사용 – 멜로디 압축을 사용합니다.
드럼 신디사이저 활성화 – 드럼 신디사이저를 활성화합니다.

플레이어 채널: 신디사이저 채널 수(동시에 소리가 나는 최대 음 수).

Midi 파일 처리 설정에는 다음이 포함됩니다.

일반적으로 이러한 미세 조정은 필요하지 않습니다. 이러한 설정은 기본값으로 남겨둘 수 있습니다.

플레이어 API

플레이어 구현은 Common\hxMidiPlayer.c 및 Common\hxMidiPlayer.h 파일에 있습니다. 이러한 파일은 프로젝트에 포함되어야 합니다. 또한 구성을 배치해야 하는 파일 hxMidiPlayer_config.h를 생성해야 합니다.
플레이어는 어셈블리 인서트 없이 C로 작성되었으므로 다른 마이크로컨트롤러로 쉽게 포팅할 수 있습니다.

Extern void Player_StartMelody(const flash TMelody* _pMelody, uint16_t _delay);

멜로디 연주를 시작하세요. _delay는 재생 전 초기 지연을 설정합니다(255단위 = 1초).

무효 Player_Stop();

멜로디 연주를 중지하세요.

외부 bool Player_IsPlaying();

멜로디 재생이 끝나면 false를 반환합니다.

외부 무효 Player_WaitFinish();

멜로디 재생이 끝날 때까지 기다리세요.

외부 무효 Player_TimerFunc();

이 함수는 구성에 지정된 샘플링 속도로 타이머 인터럽트에서 호출되어야 합니다. 멜로디 연주가 끝나면 전화를 걸 필요가 없습니다.

외부 void Player_Output(uint8_t 샘플);

사용자가 구현해야 합니다. 다음 샘플을 출력해야 할 때 플레이어에서 호출됩니다.

외부 무효 Player_Started();

사용자가 구현해야 합니다. 플레이어가 멜로디 연주를 시작할 때 호출됩니다. 타이머 인터럽트를 구성하는 데 사용할 수 있습니다.

외부 무효 Player_Finished();

사용자가 구현해야 합니다. 플레이어가 멜로디 연주를 마쳤을 때 호출됩니다. 타이머 인터럽트를 비활성화하거나 다른 곡 재생을 시작하는 데 사용할 수 있습니다.

//#define NOTES_TO_EEPROM //#define SINETABLE_TO_EEPROM //#define ENVELOPE_TO_EEPROM

노트 테이블, 사인 테이블 및 감쇠 테이블을 eeprom에 배치해야 하는 경우 hxMidiPlayer_config.h 파일에서 이 줄의 주석 처리를 제거해야 합니다.

예시 프로젝트

ATMega644Example – ATMega644용 프로젝트, 25MHz, PB3의 PWM 출력.

메모리 요구 사항

테이블. 플래시의 플레이어 및 멜로디 크기.

*비어 있지 않은 기존 프로젝트에 플레이어를 추가하면 코드 크기가 더 작아집니다.

**일반 멜로디 재생을 위한 채널이 부족합니다.

멜로디 1: bach_minuet_in_g.mid, 35초
멜로디 2: yiruma-river_flows_in_you.mid, 165초
멜로디 3: 프란츠 슈베르트 – Serenade.mid, 217초

표에서 볼 수 있듯이 최소 구성에서는 ATTiny2313에도 상당히 긴 멜로디를 짜낼 수 있습니다. 압축하면 멜로디가 2배 이상 줄어들 수 있지만 플레이어 코드의 크기는 최대 600바이트까지 늘어납니다.

사인 및 붕괴 노트 테이블을 EEPROM에 배치하여 각각 약 16, 50 및 100바이트의 플래시를 절약할 수 있습니다.

wav 파일의 샘플을 사용하는 경우 플레이어 코드 크기에 샘플 크기(바이트)를 추가해야 합니다.

사용예

플레이어를 사용하는 예로 오르골을 만드는 과정을 생각해 봅시다.

기성품 MDF 상자를 사용합니다.

마이크로 컨트롤러로서 우리는 SO-8 패키지의 ATTiny85를 충분히 많은 양의 메모리를 갖춘 가장 저렴한 것으로 간주합니다. 4개의 사인+엔벨로프 채널로 18KHz의 합성 주파수를 얻기 위해 27MHz로 오버클럭할 것입니다.

앰프는 배터리를 절약하기 위해 4개의 트랜지스터를 갖춘 D클래스가 될 것입니다.

트랜지스터는 스위칭 모드에서 작동하며 모든 유형이 가능합니다. 고주파 노이즈가 없는 사운드를 얻기 위해 인덕터 L1과 커패시터 C6을 취향에 따라 선택합니다. R1과 R2를 최대 2K까지 올려 볼륨을 낮추고 스피커 바운스를 줄일 수 있습니다.

디스크 드라이브의 리미트 스위치는 마치 상자용으로 특별히 제작된 것처럼 완벽하게 맞습니다(열리도록 작동합니다. 뚜껑을 열면 보드에 전원이 공급됩니다).

펌웨어 소스는 ATTiny85MusicBox 디렉터리에 있습니다.

8Kb 적합:
1) 플레이어: 18000Hz, 4채널, Sine+Envelope, Pitch+12, 압축, 멜로디를 하나씩 재생합니다(마지막 멜로디는 EEPROM에 저장됩니다).
2) 이루마 - River Flows in You
3) 프란츠 슈베르트 - 세레나데
4) PI 차이콥스키 <10월>

동영상 결과:

추가 개발

원칙적으로 플레이어를 추가로 개발하여 본격적인 Midi 또는 MOD 플레이어로 가져올 수 있습니다. 저는 개인적으로 고품질의 멜로디를 얻으려면 SD 카드를 연결하고 거기에서 WAV 파일을 훨씬 더 많이 재생하는 것이 더 쉬울 것이라고 생각합니다. 최고의 품질일반적으로 소프트웨어 합성을 통해 얻을 수 있는 것보다 그리고 그러한 플레이어는 소프트웨어와 하드웨어 측면에서 훨씬 간단합니다. hxMidiPlayer의 틈새 시장은 두 개의 다리가 남아 있고 플래시에 약간의 공간이 있을 때 기성 프로젝트에 좋은 사운드를 추가하는 것입니다. 이 작업은 이미 기존 형태로 "훌륭하게" 처리됩니다.

나는 이것이 AVR에서 모든 종류의 오르골/벨을 만드는 문제를 해결할 수 있다고 생각합니다 :)

수업을 계속하는 데 시간이 오래 걸렸기 때문에 이해할 수 있습니다. 메모리 카드 및 파일 작업을 마스터해야 했습니다. 지방 시스템. 그러나 여전히 수업이 준비되었습니다. 실제로 새해의 기적입니다.

기사에 정보가 너무 많이 포함되지 않도록 wav 파일 형식의 구조는 설명하지 않겠습니다. 검색 엔진에는 정보가 충분합니다. 일종의 Hex 편집기로 파일을 열면 처음 44바이트에 파일 유형, 샘플링 속도, 채널 수 등에 대한 모든 정보가 포함되어 있다고만 말하면 충분합니다. 파일을 분석해야 하는 경우 이 내용을 읽으십시오. 헤더와 당신은 행복할 것입니다.

페이로드 데이터는 44바이트에서 시작하며 기본적으로 사운드를 구성하는 전압 레벨을 포함합니다. 우리는 수업의 마지막 부분에서 이미 전압 레벨에 대해 이야기했습니다. 따라서 모든 것이 간단합니다. 파일의 샘플링 주파수에서 이러한 단계를 스피커로 출력해야 합니다.

스피커를 물리적으로 흔들리게 만드는 방법은 무엇입니까? PWM을 사용하거나 R2R을 사용하여 이러한 전압 레벨을 출력해야 합니다. 어쨌든 사용은 매우 간단합니다. 번호를 읽고 OCR이나 PORTx에 입력하세요. 그러다가 일정 시간이 지나면 파일 끝까지 다음 값 등을 대체했습니다.

예를 들어, 특정 wav 파일의 경우 데이터는 바이트 44=0x2C에서 나오고 거기에 숫자 0x80이 기록되어 있습니다. 예를 들어 첫 번째 타이머의 PWM으로 사운드를 재생하고 OCR1A=0x80을 씁니다. 샘플 샘플링 주파수가 8kHz라고 가정하면 인터럽트는 동일한 주파수로 설정되어야 합니다. 인터럽트에서 1/8000 = 125 µs 이후에 다음 값 0x85를 대체합니다.

인터럽트를 8kHz로 설정하는 방법은 무엇입니까? 타이머가 250kHz의 주파수에서 작동하는 경우 인터럽트 비교 레지스터는 (250/8)-1=31-1 또는 0x1E로 대체되어야 함을 기억하십시오. PWM을 사용하면 모든 것이 간단해지며 작동 주파수가 높을수록 좋습니다.

펌웨어가 작동하려면 레슨 23.2의 PetitFat lib를 사용하여 플래시 드라이브를 FAT32로 포맷하는 데 동의합니다. 파일은 8kHz 또는 22.050kHz 모노 wav 형식입니다. 파일명 1.wav. 펌웨어를 분석해 보겠습니다.

#포함하다 #include "diskio.h" #include "pff.h" 부호 없는 문자 버퍼[ 512 ] ; /* 플래시 드라이브에서 정보가 복사되는 버퍼 */휘발성 부호 없는 정수 개수; //복사된 데이터 카운터인터럽트 [TIM2_COMP] 무효 타이머2_comp_isr(void) //값이 대체되는 인터럽트( OCR1A = 버퍼[ 개수] ; //스피커로 소리 출력 if (++ 개수 >= 512 ) //카운터 증가개수 = 0; //512가 재설정된 경우) void main(void) ( unsigned int br; /* 파일 읽기/쓰기 카운터 */부호 없는 char buf = 0 ; //버퍼의 어느 부분을 읽을지 정의하는 변수 FATFS fs; /* 논리 드라이브에 대한 작업 공간(파일 시스템 개체) */포트B= 0x00 ; DDRB= 0x02 ; // 심 ocr1a 점프 // 타이머/카운터 1 초기화// 클럭 소스: 시스템 클럭 // 클럭 값: 8000,000 kHz // 모드: Fast PWM top=0x00FF // OC1A 출력: Non-Inv. TCCR1A= 0x81 ; TCCR1B= 0x09 ; TCNT1= 0x00 ; OCR1A= 0x00 ; // 타이머/카운터 2 초기화// 클럭 소스: 시스템 클럭 // 클럭 값: 250,000 kHz // 모드: CTC top=OCR2 TCCR2= 0x0B ; TCNT2= 0x00 ; //OCR2=0x1E; //8kHz에 대한 비교 레지스터 설정 OCR2= 0xA ; //22kHz의 경우 #asm("sei") // 타이머/카운터(들) 인터럽트(들) 초기화 if (disk_initialize() == 0 ) //플래시 드라이브 초기화( pf_마운트(&fs) ; //산 파일 시스템 pf_open("1.wav" ) ; //스레드 열기 pf_lseek(44) ; //포인터를 44로 이동 pf_read(버퍼, 512 ,& br) ; //처음으로 한 번에 512바이트를 삼켰습니다. TIMSK= 0x80 ; //음악을 켜는 동안 (1) ( if (! buf && count> 255 ) //255바이트 이상 재생되는 경우( pf_read(& 버퍼[ 0 ], 256 ,& br) ; //그런 다음 플래시 드라이브의 정보를 버퍼의 전반부로 읽습니다.버프= 1 ; 만약 (br< 256 ) //버퍼에 256개의 값이 포함되어 있지 않으면 파일의 끝을 의미합니다.부서지다 ; ) if (buf && 개수< 256 ) { pf_read(& buffer[ 256 ] , 256 ,& br) ; // 플래시 드라이브에서 버퍼의 두 번째 부분을 읽습니다.버프 = 0 ; 만약 (br< 256 ) break ; } } TIMSK = 0x00 ; //глушим все pf_mount(0x00 ) ; //베일을 해체한다) 동안 (1 ) ( ) )

#포함하다 #include "diskio.h" #include "pff.h" 부호 없는 문자 버퍼; /* 플래시 드라이브에서 정보가 복사되는 버퍼 */ 휘발성 unsigned int count; //복사된 데이터의 카운터 인터럽트 void 타이머2_comp_isr(void) //값이 치환되는 인터럽트 ( OCR1A = buffer; //스피커로 사운드 출력 if (++count >= 512) //카운터 카운트 증가 = 0; //512 재설정인 경우 ) void main(void) ( unsigned int br; /* 파일 읽기/쓰기 카운터 */ unsigned char buf = 0; //읽을 버퍼의 부분을 정의하는 변수 FATFS fs; /* 작동 중 논리 드라이브용 영역(파일 시스템 개체) */ PORTB=0x00; DDRB=0x02; //심 점프 ocr1a // 타이머/카운터 1 초기화 // 클럭 소스: 시스템 클럭 // 클럭 값: 8000,000kHz // 모드: Fast PWM top=0x00FF // OC1A 출력: Non-Inv. TCCR1A=0x81; TCCR1B=0x09; TCNT1=0x00; OCR1A=0x00; // 타이머/카운터 2 초기화 // 클럭 소스: 시스템 클럭 // 클럭 값 : 250,000 kHz // 모드: CTC top= OCR2 TCCR2=0x0B; TCNT2=0x00; //OCR2=0x1E; //8kHz에 대한 비교 레지스터 설정 OCR2=0xA; //22kHz에 대해 #asm("sei") // 타이머/카운터 인터럽트(들) 초기화 if(disk_initialize()==0) //플래시 드라이브 초기화( pf_mount(&fs); //파일 시스템 마운트 pf_open("1.wav"); //브랜치 열기 pf_lseek(44); //포인터를 44로 이동 pf_read(buffer, 512,&br); //처음으로 512바이트를 한 번에 삼켰습니다. TIMSK=0x80; //음악을 켜는 동안(1) ( if(!buf && count>255) //255바이트 이상이 재생된 경우, ( pf_read(&buffer, 256,&br);//그 다음 플래시에서 정보를 읽습니다. 버퍼의 전반부로 이동 buf=1 ; if (br< 256) //если буфер не содержит 256 значений значит конец файла break; } if(buf && count<256) { pf_read(&buffer, 256,&br); // читаем во вторую часть буфера с флешки buf = 0; if (br < 256) break; } } TIMSK = 0x00; //глушим все pf_mount(0x00); //демонтируем фат } while (1) { } }

확인하기 위해 100uF 커패시터를 통해 스피커를 OCR1A 핀에 연결하고, "+"는 마이크로 컨트롤러 핀에, "-"는 스피커에 연결합니다. "-" 스피커는 접지에, "+"는 커패시터에 연결됩니다.

출력에서 큰 신호를 기대하지 마십시오. 큰 소리를 내려면 증폭기가 필요합니다. 이것은 비디오에서 명확하게 볼 수 있습니다. 테스트를 위해 수탉에는 8kHz를, 트랙에는 22kHz를 로드했습니다.

원하는 경우 44kHz 파일을 재생하기 위해 타이머2의 주파수를 안전하게 높일 수 있으며, 실험에 따르면 상당히 좋은 음질을 얻을 수 있는 것으로 나타났습니다. 영상에서는 소리가 약하고 화질도 좋지 않은데 사실 이는 카메라로 촬영했기 때문입니다.

CAVR용 펌웨어를 작성한 GCC의 소스 코드인 Apparatchik에서 친절하게 제공한 자료도 게시합니다.

그리고 44kHz로 재생되는 비디오.

이 기회를 빌어 모두의 새해를 축하합니다. 모든 펌웨어와 장치가 잘 작동하길 바랍니다 :)

Atmega8의 wav 플레이어 프로젝트

나는 AVR 마이크로컨트롤러의 거의 모든 프로젝트에 멜로디나 사운드 시퀀스를 재생하는 기능을 추가할 수 있는 소프트웨어 모듈을 작성했습니다.

모듈 기능:

기성 프로젝트와 쉽게 통합

8비트 타이머 t2만 사용되며 시간 간격을 폴링하거나 형성하는 데 사용할 수 있습니다.

모듈은 거의 모든 클록 생성기 주파수로 조정 가능

음표의 높이는 기호 상수(C0, A2 등) 또는 헤르츠 단위로 지정됩니다.

기간은 표준 형식(4분의 1, 8분의 1 등) 또는 밀리초로 지정됩니다.

멜로디 재생 템포와 반복 횟수를 설정할 수 있습니다.

재생 중에 멜로디를 일시 정지할 수 있습니다.


사운드 모듈 연결

1. 모든 모듈 파일(tone.h, sound.h, sound.c)을 프로젝트 폴더에 복사합니다.

2. sound.c 파일을 프로젝트에 연결합니다.

IAR`a의 경우 작업 공간 창을 마우스 오른쪽 버튼으로 클릭하고 추가 > 파일 추가...를 선택합니다.

WINAVR의 경우에도 마찬가지입니다. makefile에 sound.c만 추가하면 됩니다.

SRC = $(TARGET).c 사운드.c

3. 해당 모듈에 헤더 파일 sound.h를 포함시킵니다. 예를 들어, main.c에서

#include "sound.h"

4. sound.h 파일에서 모듈 설정을 지정합니다.

//주석을 달면 메모의 지속 시간은

//멜로디에 지정된 BPM에서 계산됩니다.

//남으면 아래에 지정된 값에서

//#define SOUND_BPM 24

//클럭 주파수 μ

#SOUND_F_CPU 16U 정의

//소리가 생성될 마이크로컨트롤러의 출력

#PORT_SOUND PORTB 정의

#define PINX_SOUND 0

//지정된 멜로디의 수.

#SOUND_AMOUNT_MELODY 4 정의

5. sound.c에 멜로디를 추가하고 멜로디 배열에 멜로디 이름을 씁니다.

벨소리 추가

멜로디는 16비트 숫자의 배열이며 다음과 같은 구조를 갖습니다.

BPM(분당 4분음표)음표의 길이를 계산하고 멜로디가 연주되는 속도를 결정하는 데 사용되는 상수입니다.

BPM 범위는 1에서 24까지이며, 이는 각각 분당 10과 240 4분음표에 해당합니다.

음표/사운드의 지속 시간이 밀리초로 지정된 경우 배열에 기록된 BPM은 1과 같아야 합니다.

SOUND_BPM 상수가 헤더 파일 sound.h에서 주석 처리된 경우 음표의 지속 시간은 배열에 지정된 BPM에 따라 프로그램 실행 중에 계산됩니다. SOUND_BPM을 주석 처리하지 않으면 편집 단계에서 이 상수 값을 기준으로 음표의 지속 시간이 계산되며 모든 멜로디가 동일한 템포로 재생됩니다. 이는 기능을 제한하지만 몇 바이트의 코드를 절약합니다.

반복 횟수. 1~254 및 LOOP(255) 값을 사용할 수 있습니다. LOOP - SOUND_STOP 또는 SOUND_PAUSE 명령이 주어질 때까지 멜로디가 끝없이 반복된다는 의미입니다.

메모 기간– 특정 사운드 톤이 생성되거나 일시정지가 유지되는 시간입니다. ms(x) 매크로를 사용하여 ms 단위로 지정하거나 표준 음표 값(8분음표, 16분음표 등)으로 지정할 수 있습니다. 다음은 지원되는 기간 목록입니다. 이국적인 지속 시간이 필요한 경우 언제든지tone.h 파일에 추가할 수 있습니다.

n1 - 온음표

n2 - 2분음표

n4 - 분기

n8 - 여덟 번째

n3 - 여덟 번째 삼중항

n16 - 열여섯 번째

n6 - 섹스톨

n32 - 30초

음높이 Tone.h 파일에 설명된 기호 상수(예: C2, A1 등)를 사용하여 지정됩니다. 또한 f(x) 매크로를 사용하여 음표의 피치를 헤르츠 단위로 지정할 수 있습니다.

이 프로그램에는 최소 및 최대 사운드 주파수에 대한 제한이 있습니다!

멜로디 종료 마커.배열의 마지막 요소 값은 0이어야 합니다.

사운드 모듈 사용

main 시작 부분에서 SOUND_Init() 함수를 호출해야 합니다. 이 함수는 마이크로컨트롤러 출력 핀을 설정하고, 타이머 T2를 구성하고, 모듈 변수를 초기화합니다.

그런 다음 모듈이 T2 타이머 오버플로 및 동시 인터럽트를 사용하므로 인터럽트 활성화 플래그(__enable_interrupt())를 설정해야 합니다.

그런 다음 멜로디 연주를 시작할 수 있습니다.

예를 들어 다음과 같습니다.

SOUND_SetSong(2);

SOUND_Com(SOUND_PLAY); //멜로디 연주

//두 번째 멜로디에 포인터를 설정합니다.

//그리고 재생을 시작합니다

SOUND_PlaySong(2);

SOUND_STOP 명령을 실행하여 언제든지 멜로디 재생을 중지할 수 있습니다.
SOUND_PAUSE 명령을 사용하여 멜로디를 일시 중지할 수도 있습니다. 이후에 SOUND_PLAY 명령을 실행하면 멜로디가 중지된 지점부터 멜로디 재생이 다시 시작됩니다.

원칙적으로 이 기능은 특별히 필요하지 않으며(방금 만든 것임) 모듈 작업 시 SOUND_PlaySong(unsigned char numSong) 기능이면 충분합니다.

파일

아래 링크에서 사운드 모듈 사용 예제를 다운로드할 수 있습니다. 모든 것이 간단하기 때문에 다이어그램을 그리지 않았습니다. 핀 PB0에 연결되면 멜로디 시작 버튼이 핀 PD3에 연결됩니다. 프로젝트에는 4개의 멜로디가 정의되어 있습니다. 버튼을 누를 때마다 새로운 멜로디가 시작됩니다. atmega8535 마이크로 컨트롤러가 사용됩니다. 처음에는 PLAY, STOP, PAUSE, NEXT라는 네 개의 버튼이 있는 프로젝트를 만들고 싶었지만 나중에는 그것이 불필요하다고 생각했습니다.

추신: 모듈은 광범위한 테스트를 거치지 않았으며 "있는 그대로" 제공됩니다. 합리적인 제안이 있다면 최종 확정하도록 하겠습니다.

이번 글에서는 음을 연주하는 방법과 모노포닉 멜로디를 연주하는 방법을 알아보겠습니다.

취업 준비

프로그램은 두 개의 배열을 선언합니다. 메모가 있는 배열 노트간단한 메모 목록이 포함되어 있습니다. 이 노트는 배열의 사운드 지속 시간과 일치합니다. 박자. 음악의 지속 시간은 전체 음표에 대한 음표의 제수에 의해 결정됩니다. 전체 메모로 간주되는 값은 다음과 같습니다. 255 . 이 숫자를 나누어서 반쪽, 4분의 1, 8분의 1을 얻습니다.
첫 번째 음표의 지속 시간은 255를 2의 거듭제곱으로 나눈 값이 아니라는 점에 유의하십시오. 여기서는 음악 이론으로 전환해야 합니다. 원곡의 음표를 볼 수 있습니다. 이 음표는 세 개의 음표로 결합됩니다. 이런 식으로 결합하면 8분음표 3개가 4분음표 1개와 같은 소리가 납니다. 따라서 상대 지속 기간은 21입니다.
또한 사용자는 다음 지시문을 사용하여 시퀀스의 음표 수를 명시적으로 지정해야 합니다.

# SEQU_SIZE 19를 정의합니다.

메인 프로그램에서는 우선 주파수 배열이 다시 계산되고 신호 기간과 음표 기간이 다시 계산됩니다.
신호 주기(배열 신호_기간) 모든 것이 간단합니다. 주기 기간을 마이크로초 단위로 얻으려면 1,000,000을 신호 주파수로 나누면 됩니다.
음표의 절대 지속 시간을 계산하려면 음악 작품의 템포를 지정해야 합니다. 이는 지시문에 의해 수행됩니다.

# TEMPO 108 정의

음악의 템포는 분당 4분음표의 수입니다. 줄을 서서

# WHOLE_NOTE_DUR 240000 / TEMPO 정의

전체 음표의 지속 시간은 밀리초 단위로 계산됩니다. 이제 수식을 사용하여 배열에서 상대 값을 다시 계산하는 것으로 충분합니다. 박자절대 배열로 note_duration.
메인 루프에서 변수 경과 시간음표의 지속 시간을 초과할 때까지 이 기간만큼 재생된 신호의 각 기간 이후에 증가됩니다. 이 항목에 주목할 가치가 있습니다.

동안(경과_시간< 1000 * ((uint32_t) note_duration[ i] ) )

변하기 쉬운 경과 시간 32비트 및 배열 요소 메모_기간 16비트. 16비트 숫자에 1000을 곱하면 오버플로가 보장되고 변수는 경과 시간쓰레기와 비교될 것이다. 수정자 (uint32_t)배열 요소를 변환합니다. Notes_기간[i] 32비트 숫자에는 오버플로가 없습니다.
오디오 루프에서 또 다른 기능을 볼 수 있습니다. 기능을 사용할 수 없게 됩니다 _delay_us(), 그 인수는 변수가 될 수 없기 때문입니다.
이러한 지연을 생성하려면 다음 기능을 사용하십시오. VarDelay_us(). 여기서는 1μs의 지연을 갖는 루프가 지정된 횟수만큼 스크롤됩니다.

void VarDelay_us(uint32_t takt) ( while (takt- - ) ( _delay_us(1 ) ; ) )

멜로디를 연주할 때 두 개의 딜레이가 더 사용됩니다. 음표를 일시 정지 없이 연주하면 하나로 병합됩니다. 이를 위해 다음 지시어에 지정된 대로 1ms 지연이 둘 사이에 삽입됩니다.

# NOTES_PAUSE 1 정의

멜로디 재생이 완료될 때마다 프로그램은 1초 동안 일시 중지되었다가 다시 재생되기 시작합니다.
그 결과, 템포를 바꾸거나, 길이를 조정하거나, 멜로디를 완전히 다시 쓰는 것이 쉬운 코드를 받았습니다. 이렇게 하려면 지시문과 변수 선언이 포함된 프로그램의 일부만 변환하는 것으로 충분합니다.

개별 작업

  1. 제안된 멜로디에서 템포를 변경하고 반복 사이에 5초 동안 잠시 멈추어 보세요.
  2. 배열 요소 박자 0에서 255 사이의 값만 허용됩니다. 배열 요소의 비트 너비를 변경하고 컴파일러 출력을 살펴보고 이것이 프로그램이 차지하는 메모리 양에 어떤 영향을 미치는지 확인하십시오.
  3. 이제 직접 멜로디를 바꿔보세요. 예를 들어, 다음은 같은 영화에 나오는 "제국 행진곡"입니다: int Notes = ( A4, R, A4, R, A4, R, F4, R, C5, R, A4, R, F4, R, C5, R, A4, R, E5, R, E5, R, E5, R, F5, R, C5, R, G5, R, F5, R, C5, R, A4, R); int 비트 = (50, 20, 50, 20, 50, 20, 40, 5, 20, 5, 60, 10, 40, 5, 20, 5, 60, 80, 50, 20, 50, 20, 50, 20, 40, 5, 20

    귀하의 자동차에 사이렌이 설치되어 있지 않고 어떤 것을 구입하고 설치할지 여전히 결정할 수 없다면 이 기사가 귀하를 위한 것입니다. 아주 간단한 방법으로 직접 조립할 수 있는데 왜 값비싼 경보기를 구입합니까?

    이 중 두 가지를 소개합니다 간단한 회로 AVR ATmega8 및 Attiny2313 마이크로컨트롤러에서 또는 오히려 동일한 회로가 이 두 마이크로컨트롤러에서 작동하도록 간단히 구현됩니다. 그건 그렇고, 아카이브에는 Atmega8 마이크로 컨트롤러 용 펌웨어의 두 가지 버전이 있습니다. 그 중 첫 번째 버전은 다음과 유사한 사운드를 재생합니다. 자동차 알람, 두 번째 소리는 건물 보안 경보음과 유사합니다(빠르고 날카로운 신호).

    아래의 모든 펌웨어를 아카이브에서 다운로드할 수 있습니다(모두 서명됨). 아카이브에서는 Proteus의 회로 시뮬레이션도 찾을 수 있습니다. 즉, 모든 곡을 들은 후 목록에서 가장 좋아하는 곡을 선택할 수 있습니다. .

    아래는 Atmega8의 신호 다이어그램입니다.

    Atmega8 회로에 사용되는 무선 구성 요소 목록

    U1- AVR 마이크로컨트롤러 8비트 ATmega8-16PU, 수량 1,
    R1- 공칭 값이 47Ω인 저항기, no. 1,
    R2, R3 - 공칭 값이 270Ω인 저항기, no. 2,
    D2,D3-LED, 아니. 2,
    LS1-스피커, 아니오. 1,
    S1-센서.

    그리고 Attiny2313의 신호 회로에서는 MK만 변경되었습니다.
    U1- 마이크로컨트롤러 AVR 8비트 ATtiny2313-20PU, 수량. 1.

    인쇄 회로 기판 Atmega8의 경우 다음과 같습니다.

    보시다시피 회로는 매우 간단합니다. 마이크로컨트롤러 1개, 저항 3개, LED 2개, 스피커 1개만 더 있습니다. 버튼 대신 리드 스위치나 기타 접점을 사용할 수 있습니다.

    작동 원리는 다음과 같습니다. 전원을 공급하자마자 LED(회로 D3)가 즉시 켜지거나 깜박이기 시작합니다(펌웨어에 따라 다름). 센서를 건드리지 않으면 알람이 꺼집니다. 이제 센서가 작동하면 사이렌도 작동하고 LED도 깜박이지만 D2.

    알람이 작동할 때 자동차의 헤드라이트가 깜박이도록 하려면 마이크로컨트롤러 24 PC1의 핀을 트랜지스터를 통해 릴레이에 연결하고 릴레이 자체를 헤드라이트에 연결해야 합니다. 사이렌을 끄려면 장치를 껐다가 다시 켜거나 버튼을 누르기만 하면 됩니다. 마이크로 컨트롤러를 작동하려면 내부 8MHz 발진기가 필요합니다.

    알람 소리를 어떻게든 강화하려면 트랜지스터로 증폭기를 조립하여 회로에 연결할 수 있습니다. 그것이 바로 내가 한 일이지만, 이 다이어그램에는 그것을 묘사하지 않았습니다.

    앞에서 말했듯이 Attiny 2313의 회로로 넘어가겠습니다. 세부 사항과 작동 원리는 모두 동일하며 MK만 변경되었으며 결과적으로 연결된 핀이 변경되었습니다. 내부 4MHz 발진기(1MHz로 플래시할 수 있음)

    아래는 이미 Attiny2313에 있는 연결 다이어그램입니다.

    이 MK의 경우 한 가지 버전의 펌웨어만 작성하고 회로 기판의 모든 것을 조립하고 확인한 결과 모든 것이 잘 작동합니다.
    그리고 퓨즈는 아래와 같이 설정되어야 합니다.





맨 위