Atmega8 beep. Generating sound with AVR. Sound your device. PWM, two-pin connection

If a sound siren is not installed in your car, and you still do not decide which one to buy and install, then this article is for you. Why buy expensive alarms if you can assemble all this with your own hands in a fairly simple way?

I represent two such simple schemes on microcontrollers AVR ATmega8 and Attiny2313, more precisely, one circuit is simply implemented to work on these two microcontrollers. By the way, in the archive you will find two versions of firmware for the Atmega8 microcontroller, of which the first reproduces a sound similar to car alarm and the second sound is similar to a building burglar alarm (fast and sharp signal).

All firmwares can be downloaded below in the archive (they are all signed), in the archive you will also find a simulation of circuits in Proteus, which means that after listening to all the melodies you yourself can choose from the list what you like best.

Below is the alarm diagram on the Atmega8

List of used radio components in the Atmega8 scheme

U1- AVR microcontroller 8-bit ATmega8-16PU, qty. one,
R1- Resistor with a nominal value of 47 Ohm, qty. one,
R2, R3- Resistor with a nominal value of 270 Ohm, qty. 2,
D2, D3-LED, qty. 2,
LS1 speaker, qty. one,
S1- sensor.

And in the signaling circuit on Attiny2313, only microns were changed.
U1- AVR microcontroller 8-bit ATtiny2313-20PU, qty. one.

The PCB for the Atmega8 looks like this:

As you can see, the circuit is very simple, there is only one microcontroller, 3 resistors, 2 LEDs and one more speaker. Instead of a button, you can use a reed switch, or another contact.

The principle of operation is as follows. As soon as we turn on the power, the LED immediately lights up or starts blinking (depending on the firmware) (in the D3 circuit), and if we do not touch the sensor, the alarm will be silent. Now, if the sensor is triggered, then the siren will work, the LED will also flash, but already D2.

If you want the headlights of the car to blink during the operation of the alarm, then for this you need to connect the output of the microcontroller 24 PC1 to the relay through a transistor, and the relay itself is already to the headlights. To turn off the siren, you must turn off and then turn on the device, or just press the button. For the microcontroller to work, an internal 8 MHz generator is needed,

If you want to somehow amplify the sound of the alarm, then you can assemble an amplifier with transistors and connect it to the circuit. I did just that, only in this diagram I did not depict it.

Let's move on to the diagram on the Attiny 2313, in it, as I said before, all the same details and the same principle of operation, only the MC has been changed, as a result, the connected outputs. Such a microcontroller operates from an internal 4MHz generator, although it is possible to flash it at 1MHz.

Below is the connection diagram already on Attiny2313

For this mk I wrote only one version of the firmware, collected everything on a coquette board, checked it, everything works fine.
And the fuzzy needs to be set up in the following way:


The module works with SD-cards formatted in FAT16 with a capacity not exceeding 2 GB and reproduces sound fragments in .ad4 or .wav formats in any order. The power supply of the internal circuits of the module is provided from the built-in stabilizer for 3.3 V, which is very convenient, since this allows the module itself to be supplied with a voltage of 5 V. pad "3.3V", as shown in Figure 2).

The module can be controlled both "manually" and using a microcontroller. In the "manual" mode, it is enough to connect buttons to the device, according to the diagram shown in Figure 3. In the technical description of the WTV020 module, you can find other connection options that differ little in functionality from the proposed diagram.

The speaker is connected to the PWM outputs or to the built-in 16-bit DAC. In the latter case, you need to connect an external op-amp and an amplifier (Figure 4). When connected to a PWM channel, it is allowed to switch speakers with an impedance of 8 ohms and a power of up to 0.5 W.

The pin assignment of the WTV020 module is shown in Table 1. The channels of the two-wire communication interface can be used both for connecting buttons and for an external microcontroller.

Table 1.

Number
withdrawal

Appointment

Audio output with DAC

Not used

PWM output

PWM output

Not used

Volume "+" / CLK

Play - pause

Volume "-" / DI

Not used

Next file

Not used

For visual control, a 2 × 16 LCD display and a matching controller were used. The general scheme of the player is shown in Figure 5. The microcontroller and the module are powered by 3.3 V, the display - 4 V, since 3.3 V was not enough for the selected LCD. This voltage difference does not affect the reception of data from the MC. The author decided not to activate the internal stabilizer of the WTV020 module.

CLK and DI lines are used to transfer data to the WTV020 module. According to technical description(Figure 6a), 16 data bits should be transmitted with a frequency of 200 μs, however, in practice, this value should be increased to 2 ms (Figure 6b).

Based on the documentation, after power-up, it is recommended to send a negative pulse of 5 ms duration to the "Reset" output of the module, and send commands after 300 ms. But this is a clear error, since the initialization time of the WTV020 module is about 600 ms. If commands are given earlier than 600 ms after reset, the module simply does not accept them.

List main teams accepted by the module is presented in Table 2. It can be seen from the table that the maximum number of playable audio files is 512, but the author has limited himself to thirty. The volume is adjustable in 7 ranges. In practice, sound distortions are observed from addresses FFF0 to FFF3, both from the PWM output and from the DAC. FFFE (Play / Pause) and FFFF (Stop / Play) commands are trigger.

The LCD screen displays the number of the file being played and the volume in the form of a scale of 7 filled rectangles. A photo of the finished device is shown in Figure 8.

Demo video:

MK software, Proteus virtual model and .ad4 audio file -

Program for converting audio recordings to .ad4 format -

This article describes the principles of music synthesis on AVR. The included software allows you to convert any midi file to source in C for AVR microcontrollers to add music playback ready-made designs. An example of using software in a music box is considered.

First, a short video of how everything works:

What the software allows

PC software allows you to get a C source for CodeVision AVR, which plays the selected midi file:

1. In your project, include common \ hxMidiPlayer.h, common \ hxMidiPlayer.c. Copy the blanks ATMega8Example \ melody.h, ATMega8Example \ melody.c, ATMega8Example \ hxMidiPlayer_config.h and connect.
2. Run MidiToC.exe
3. Load the Midi file.
4. Set up the player: sampling rate, number of channels, waveform, etc. The software reproduces the melody in the same way as the AVR will play.
5. Click “Create player config” and paste the source into hxMidiPlayer_config.h.
6. Click “Create melody code” and paste the source into melody.c
7. In our project, we implement the Player_Output () method to output sound via PWM or an external DAC.
8. Set the timer to the Sampling rate, call Player_TimerFunc () from the interrupt.
9. Call Player_StartMelody (& s_melody, 0).

The melody is played from the timer interrupt. This means that during playback, the microcontroller can also do useful work.

How it works

In the rest of the article, I will try to briefly explain how this is all implemented. Unfortunately, it will not work out very briefly - there is a lot of material. If you are not interested, you can go directly to the sections “Software Description” and “Player API”.

What is music

Music is a sequence of sounds of varying frequency and duration. The frequency of the fundamental harmonic of the sound must correspond to the frequency of a particular note. If the frequency of vibrations of sounds differs from the frequencies of notes, it seems to us that the musician is “out of tune”.

Table. Frequencies of notes, Hz.

All notes are divided into octaves, 7 notes each + 5 semitones (black keys on the piano). The frequencies of the notes of adjacent octaves differ by exactly 2 times.

The simplest music player contains a table with a sequence of notes (note + duration) of a melody and a table with frequencies of notes. For sound synthesis, one of the timer channels is used, which forms a meander:

Unfortunately, such a primitive player has a fixed waveform (meander), which is not very similar to real musical instruments, and can only play one note at a time.

A real melody contains at least two parts (solo + bass), and when playing the piano, the previous note is still playing when the next one starts. This is easy to understand if you remember the structure of the piano - a separate string corresponds to each note. We can make several strings sound at the same time by moving our hand over the keys.

Some microcontrollers have multiple timer channels that can be used to play multiple notes at the same time. But these channels are usually a valuable resource and it is not desirable to use all of them. Unless, of course, we are just making a music box.
In total, to get polyphony and various sounds of musical instruments, you need to use sound synthesis.

Sound synthesis on AVR

hxMidiPlayer uses sound synthesis and can play polyphony with various waveforms. The player calculates the amplitude of the output signal in the timer interrupt handler with a frequency of 8-22KHz (how much processor power is enough; also depends on the waveform and the number of channels).

The principle of sound synthesis can be explained by the example of sinusoid synthesis.

Let's take a table of size 64, in each cell of which the values ​​of the amplitude of the sine are written in points index * 2 * PI / 64 (one period):

Static const flash uint8_t s_sineTable [64] = (0x80, 0x82, 0x84, 0x86, 0x88, 0x8A, 0x8C, 0x8D, 0x8F, 0x90, 0x91, 0x93, 0x93, 0x94, 0x95, 0x95, 0x95, 0x95, 094, 0x93, 0x93, 0x91, 0x90, 0x8F, 0x8D, 0x8C, 0x8A, 0x88, 0x86, 0x84, 0x82, 0x80, 0x7E, 0x7C, 0x7A, 0x78, 0x76, 0x74, 0x73, 0x71, 0x70, 0x6D6F, 0x6C, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6C, 0x6D, 0x6D, 0x6F, 0x70, 0x71, 0x73, 0x74, 0x76, 0x78, 0x7A, 0x7C, 0x7E);

128 (0x80) is zero, 255 (0xff) is the largest positive point, 0 is the largest negative point.

Now let's say we will output the values ​​from the table to an external DAC in an interrupt from a timer called with a frequency of 1000 Hz:

Static uint8_t s_index = 0; // Timer1 output compare A interrupt service routine interrupt void timer1_compa_isr (void) (SetDac (s_sineTable [s_index]); if (s_index == 63) (s_index = 0;) else (s_index ++;))

What will we get on the way out? We will get sinusoidal oscillations with a frequency of 1000/64 Hz.

Now let's increase the index in the interrupt not by 1, but by two.
Obviously, the frequency of the output oscillations will already be 1000/64 * 2 Hz.

In general, to get the frequency F, you need to increase the index in the table by:
add = F / 1000 * 64

This number can be fractional, but to get a high speed of work, use a fixed point arithmetic.

The number of entries in the table and the frequency of the timer affect the quality of the synthesized sound. In our case, 64 entries in the table per period are sufficient, and the timer frequency is 12kHz. The minimum acceptable frequency of the timer is 8 kHz, the ideal is 44 kHz.

Obviously, with a timer frequency of 12 kHz, we will be able to generate a maximum of 6 kHz square wave, since we need to make at least two switches per period. However, the frequencies higher will still be recognizable if the output state is correctly calculated at each tick of the timer.

You can enter values ​​for the non-sinusoidal oscillation period in the table and get a different sound.

Attenuation

If a musical instrument is based on strings (for example, a piano), then after pressing a key, the sound fades out smoothly. To get a more natural sound of the synthesizer, it is necessary to smoothly reduce the amplitude of the oscillations after the start of the note (“wrap” the oscillations in the form of decay - “envelope”).

The player contains an attenuation table that it uses to reduce the amplitude of the sine (or other waveform) from the moment the note starts.
“Sine” “wrapped” in such a shell resembles the sound of a mechanical music box.

Synthesis of meander

The special meander waveform makes the synthesis much easier. Tables are not used in this case. It is enough to calculate what state (1 or 0) the output should have at a given frequency at the current tick of the timer. This is done using integer arithmetic, it works very quickly, which explains the popularity of using the meander for playing tunes in 8-bit consoles.

Example: declaring a counter:

Static uint16_t s_counter = 0;

which we will increase by 0x8000 in each interrupt from the timer, and we will output the most significant bit of the counter to the port:

// Timer1 output compare A interrupt service routine interrupt void timer1_compa_isr (void) (PORTA.0 = (s_counter >> 15) & 1; s_counter + = 0x8000;)

Since 0x8000 + 0x8000 = 0x10000, the s_counter variable overflows, the 17th bit is discarded, and 0x0000 is written to the variable.
Thus, with a timer frequency of 8KHz, the output will be a 4KHz square wave.
If you increase the counter by 0x4000, you get a 2KHz square wave.

In general, the frequency F can be obtained by adding:
add = F / 8000 * 0x10000

For example, to get a 1234Hz square wave, add 0x277C. The actual frequency will be slightly different from the specified one, because we are rounding the term to an integer. This is acceptable in a synthesizer.

Synthesis of sounds of real instruments

You can digitize the sound of a note to the piano (using the ADC, store the amplitude values ​​of the sound in memory at regular intervals):
and then play the sound (use the DAC to output the recorded values ​​at regular intervals).

In general, drum synthesis requires recording drum sounds and playing them back at the right time. 8-bit set-top boxes use “white noise” instead of drum sounds. Amplitude values ​​for "white noise" are obtained using a random number generator. At the same time, memory costs are minimal.
hxMidiPlayer uses white noise for drum synthesis.

Channel mixing

The amplitude of the sound at a given tick of the timer is calculated for each channel separately. To get the final value of the amplitude, it is necessary to add the values ​​of all channels. Correctly it is necessary to correct the sum, since the perceived loudness obeys a logarithmic dependence, but in such a simple synthesizer you have to do with simple addition. Therefore, the maximum amplitude of each channel is 255 / N.

Audio output from AVR

After carrying out all the necessary calculations, the player receives the signal level, which must be converted to analog. For these purposes, you can use an external DAC or PWM.
It should be noted here that in both cases it is desirable to filter the received signal - to remove high-frequency noise that occurs due to the low synthesis frequency and rounding.

Output to external parallel DAC

Since it doesn't make sense to use precise DAC chips, in such projects, the R2R matrix is ​​usually bypassed:

With this setup, we simply output the calculated amplitude to the port:

PORTB = sample;

Flaws:
1) a too weak signal is obtained at the output of the R2R matrix, the use of an analog amplifier is mandatory;
2) it is necessary to use at least 5 pins (or better 8);
This method is only justified when there are no free PWM channels.

(to save pins, you can use an external ADC with SPI interface).

PWM

If there is a free PWM channel, then the easiest way is to use this method.

PWM initialization (ATMega8):

// Timer / Counter 2 initialization // Clock source: System Clock // Clock value: 20000,000 kHz // Mode: Fast PWM top = 0xFF // OC2 output: Non-Inverted PWM ASSR = 0x00; TCCR2 = 0x69; TCNT2 = 0x00; OCR2 = 0x00; And the sample output: void Player_Output (uint8_t sample) (OC2 = sample.)

A common practice with PWM is to smooth the output signal with an RC filter:

Unfortunately, after filtering, the signal weakens too much, so you have to make an analog amplifier to connect the speaker.

To simplify the circuit, it is best to stay “digital” all the way to the speaker. Since a cheap speaker still cannot reproduce frequencies above 30 kHz, there is no need to filter them. The diffuser "filters" itself high frequencies PWM.

If you need to get more current, you can use a transistor amplifier. R1 is selected to provide the required speaker current.

So you can connect small speakers from toys:

For larger speakers, it is better to collect the swing on 2 transistors and put an LC filter to remove noise:

Capacitor C1 serves to limit the current through the speaker when the PWM is not working. Also thanks to the inclusion series capacitor, the speaker receives a signal that is symmetrical about zero. Thus, the speaker cone will move relative to the center “relaxed” position, which has a positive effect on the sound quality.
In this case, the transistors operate in the key mode, so there is no need to compensate for the base offset.

PWM, two-pin connection

The disadvantage of the first two circuits is that the speaker is supplied with current in one direction. If we change the direction of the current, then the volume can be increased 2 times without exceeding the permissible power. To do this, the speaker is connected to two pins of the microcontroller - non-inverted and inverted, for example OC1A and / OC1A. If there is no non-inverted output, you can use the second channel in inverted mode (OC1B):

// Timer / Counter 1 initialization // Clock source: System Clock // Clock value: 24500,000 kHz // Mode: Fast PWM top = 0x00FF // OC1A output: Non-Inv. // OC1B output: Inverted // Noise Canceler: Off // Input Capture on Falling Edge // Timer1 Overflow Interrupt: Off // Input Capture Interrupt: Off // Compare A Match Interrupt: Off // Compare B Match Interrupt: Off 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 sample) (OCR1A = sample; OCR1B = sample;)

PWM, Dual Leads, D-Class Amplifier

The disadvantage of the proposed circuits is the current consumption during silence.
“Silence” we have corresponds to a signal level of 128, that is, PWM with 50% filling - the current always flows through the speaker!

By changing a little software part, you can get a fairly powerful hardware-software class D amplifier:

Void Player_Output (uint8_t sample) (if (sample> = 128) (TCCR2 = 0x21; // normal, clear on compare match TCCR2 = 0x21 | 0x80; // CLEAR OC2 PORTC.0 = 0; TCCR2 = 0x69; // non -inverting PWM OCR2 = (sample-128) * 2;) else // if (sample< 128) { TCCR2=0x31; //normal, set on compare match TCCR2=0x31 | 0x80; //SET OC2 PORTC.0 = 1; TCCR2=0x79; //inverting PWM OCR2 = (128-sample) *2; } }

In this case, one pair of transistors is connected to the PWM output, the second to a regular digital output.

As you can see from the code, we consider a signal above 128 as a current directed in one direction, and a signal below 128 as a current directed in the other direction. At level 128, both speaker pins are connected to the same power supply pin and there is no current. With a deviation from level 128, the PWM filling increases, and a current of the corresponding polarity flows through the speaker.

An important point of implementation is the forced switching of the PWM output to the desired state at the moment of switching the second (normal digital) output (PORTC.0). Writes to the OCR2 register are buffered to eliminate PWM glitches. We need to switch the PWM output immediately, without waiting for the end of the period.

The last schema IMHO is the best option in terms of simplicity, energy savings, and power output.

Audio Output with SquareWave Waveform

Simplified algorithms are used to synthesize the meander.

Each channel (including drums) outputs either 0 or 1. Thus, a 3-channel turntable outputs values ​​in the range 0..3. Therefore, when using PWM, the withdrawal procedure looks like:

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

If you do not use PWM, then two ordinary digital outputs and a 2-bit R2R matrix are enough to output a 3-channel melody.

MIDI format

If you look at the received melody code, you will easily notice that the array uses repeating numbers from a small range. This is understandable: the melody uses limited quantity notes within 1-2 octaves, the melody tempo is fixed - the same delays, the number of channels is in the range 0..15.
All this means that the resulting array can be significantly reduced by using some kind of compression algorithm.
Algorithms like ZIP give good compression, but also require a lot of memory to work (ZIP dictionary - 64Kb). We can use a very simple compression method that requires almost no memory, the essence of which is as follows.

In one byte, all numbers are evenly distributed in the range 0 ... 255, and each number is represented by 8 bits. In our case, some numbers are much more common than others. If you encode frequently occurring numbers with fewer bits, and less frequently occurring numbers with more, you can get a memory gain.

We choose a fixed coding method: the combinations of bits 000,001 and 010 (length - 3 bits) will represent the 3 most common numbers. Combinations of bits 0110, 0111 (length - 4 bits) - the following 2 most common numbers, etc.:

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

A combination starting with 111 (11 bits long) will encode all other numbers.
Bit encoding may be different. I tried several methods and chose this one as it gives the best results on this kind of data.

The compression procedure looks like this:
1. Calculate the total number of X in the stream for X =.
2. Sort by decreasing frequency of occurrence in the stream.
3. Take the first 25 numbers. They will be encoded with fewer bits.
4. Encode the input stream.

At the output, we get an array of 25 most common numbers and a bitstream.
This compression allows you to get 50% compression with scanty memory and performance costs. Unfortunately, this increases the player code, so compression is not recommended for short melodies.

Storing note frequencies

It is rather expensive to store the frequencies of all notes in a table from memory. In fact, there is a formula for determining the frequency of a note by its midi number:

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

But calculating a fractional power is quite difficult. Instead, the player stores 12 frequencies of the upper octave notes. The frequencies of the notes of the lower octaves are determined by decreasing the frequency by 2 ^ Y more times, where Y is the number of octaves down.

Further development of compression

The melody often contains repeated fragments ("choruses", "verses"). By finding repeating fragments and presenting the melody in the form of fragments, you can reduce the melody by another 50%, almost without spending RAM and performance. I did not implement such an algorithm so as not to complicate the project.

Software description

The main window of the converter program:

The Load Midi button allows you to load a midi file. The program immediately starts playing the file with the currently selected parameters, simulating the sound that will be in the hardware.

Information window (4) displays:
- Length - the length of the selected melody fragment in ms;
- Max Active syntezer channels - maximum number of simultaneously active synthesizer channels;
- Max active drum channels - the maximum number of simultaneously active synthesizer channels that reproduce "drums";
- Max active stereo notes - maximum number of channels playing the same note (see below);
- Estimated size, bytes - melody size in bytes. In the “Custom Sample” mode, the size is displayed as A + B, where A is the size of the melody, B is the size of the sample. The player code size is not specified here.

The progress window displays the current playback position.
You can click on the progressbar to start playback from the specified moment.
Input Boxes on the left and on the right allow you to specify the beginning and end of a melody fragment in ms.

The label “Not enought channels to play melody” in red indicates that there are not enough synthesizer channels to play the melody at the current settings. If the player does not find a free channel, it turns off the oldest note. This will work fine in many cases. It makes sense to increase the number of channels only when the melody sounds wrong by ear.

Settings can be conditionally divided into player settings and midi file processing settings. The player will be able to play the received melody code if the player's configuration and melody code were created with the same player settings. In addition, the player will be able to play a melody, the code of which was created for a player with fewer (but not more) channels.

The player's hardware settings include:

- Sampling Rate - synthesis frequency. The maximum synthesis frequency is determined experimentally. Based on the Atmega 16MHz, you can start at 12000Hz for a 6-channel turntable, and increase as desired until you hear melody distortion in the hardware turntable. The maximum frequency depends on the number of channels, the waveform and the complexity of the melody itself.

- Waveform - waveform:
- Square wave - meander;
- Sine - sine;
- Sine + Envelope - sine with attenuation;
- Waveform * + Envelope - various options for non-sinusoidal waves with and without attenuation;
- Custom Sample - use a sample of the instrument.

The “Load Sample” button allows you to load a sample from a WAV file. WAV file must be in PCM 8-bit mono, 4173Hz, C-5 note. Hint: You can raise the frequency and lower the note, but change the Pitch in the player settings. No format checks are performed - if the format is different, the sound will not play correctly.
Pitch - allows you to change the pitch. For example, to play 1 octave higher, set Pitch +12.

Use compression - use compression of the melody.
Enable drums synteser - enable drum synthesizer.

Player Channels: the number of synthesizer channels (the maximum number of notes that will sound simultaneously).

Midi file processing settings include:

Usually, such fine tuning is not required. These settings can be left as default.

Player API

The player is implemented in the files Common \ hxMidiPlayer.c and Common \ hxMidiPlayer.h. These files must be connected to the project. You also need to create a file hxMidiPlayer_config.h, in which you need to place the configuration.
The player is written in C without assembler inserts, which will make it easy to port it to other microcontrollers.

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

Start playing a melody. _delay sets the initial delay before playback, 255 units = 1 second.

Void Player_Stop ();

Stop playing the melody.

Extern bool Player_IsPlaying ();

Returns false if the melody has finished playing.

Extern void Player_WaitFinish ();

Wait for the melody to finish playing.

Extern void Player_TimerFunc ();

This function must be called in a timer interrupt with the sampling rate specified in the configuration. When the melody is finished playing, calls do not need to be made.

Extern void Player_Output (uint8_t sample);

Must be implemented by the user. Called by the player when the next sample needs to be output.

Extern void Player_Started ();

Must be implemented by the user. Called when the player starts playing a melody. Can be used to configure timer interrupts.

Extern void Player_Finished ();

Must be implemented by the user. Called when the player has finished playing the melody. Can be used to disable timer interrupts, or start playing another melody.

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

These lines need to be uncommented in the hxMidiPlayer_config.h file if the note table, sine table and decay table need to be located in the eeprom.

Sample projects

ATMega644Example - project for ATMega644, 25MHz, PWM output to PB3.

Memory requirements

Table. The size of the player and melodies in flash.

* when adding a player to an existing non-empty project, the code size will be smaller

** there are not enough channels for normal melody playback

Melody 1: bach_minuet_in_g.mid, 35 sec
Melody 2: yiruma-river_flows_in_you.mid, 165 sec
Melody 3: Franz Schubert - Serenade.mid, 217 sec

As you can see from the table, in the minimal configuration it is possible to squeeze a rather long melody even into ATTiny2313. Compression can give more than a two-fold decrease in the melody, but the size of the player's code increases by ~ 600 bytes.

The sine and decay note tables can be placed in the EEPROM, saving approximately 16, 50 and 100 bytes of flash, respectively.

When using a sample from a wav file, add the actual sample size in bytes to the player code size.

Usage example

As an example of using the player, consider the process of creating a music box.

We take the finished MDF box:

As a microcontroller, we take ATTiny85 in SO-8 package as the cheapest with enough big amount memory. We'll overclock it to 27MHz to get a synthesis rate of 18KHz with 4 channels of Sine + Envelope.

The amplifier will be D-class on 4 transistors to conserve batteries.

Transistors operate in a key mode and can be any. Choke L1 and capacitor C6 are selected according to taste to obtain sound without high frequency noise. R1 and R2 can be raised up to 2K to lower volume and reduce speaker bounce.

The limit switch from the drive fits perfectly, as if it was specially created for the box (it works for opening - when the cover is opened, power is supplied to the board):

The firmware sources are located in the ATTiny85MusicBox directory.

8Kb fit:
1) player: 18000Hz, 4 channels, Sine + Envelope, Pitch + 12, compression, plays melodies in turn (the latter is saved in EEPROM)
2) Yiruma - River Flows in You
3) Franz Schubert - Serenade
4) P.I. Tchaikovsky "October"

Video result:

Further development

In principle, the player can be further "screwed up", bringing it to a full-fledged Midi or MOD player. I personally think that to get a high-quality melody, it will be easier to connect an SD card and play any WAV files with much better quality than can be obtained by software synthesis at all. And such a player is much simpler in software and hardware. The niche of hxMidiPlayer is adding a good sound to the finished projects, when there are a couple of legs and a little space left in the flash. He copes with this task perfectly in its existing form.

I think this question of creating all kinds of music boxes / bells on the AVR can be closed 🙂

I wrote a software module that allows you to add the function of playing melodies or sound sequences to almost any project on microcontroller AVR.

Features of the module:

Easy integration with a finished project

Only the 8-bit timer t2 is involved, while it remains possible to use it for polling or forming time intervals

The module adjusts to almost any clock frequency

The pitch of the notes is set as symbolic constants (C0, A2, etc.) or in Hertz

Durations are set in standard form (quarters, eighths, etc.) or in milliseconds

It is possible to set the tempo of the melody playback and the number of its repetitions

During playback, the melody can be paused


Connecting a sound module

1. Rewrite all module files (tone.h, sound.h, sound.c) into the project folder.

2. Connect the sound.c file to the project.

For IAR `a - click right click mouse in the workspace window and select Add> Add Files ...

For WINAVR, it's about the same, only sound.c needs to be added to the makefile:

SRC = $ (TARGET) .c sound.c

3. We include the header file sound.h in the appropriate module. For example in main.c

#include "sound.h"

4. Set the module settings in the sound.h file

// if you comment out, the duration of the notes will be

// calculate from the BPM specified in the melody

// if left, then from the value specified below

// # define SOUND_BPM 24

// clock frequency mk

#define SOUND_F_CPU 16U

// microcontroller pin on which sound will be generated

#define PORT_SOUND PORTB

#define PINX_SOUND 0

// number of set melodies.

#define SOUND_AMOUNT_MELODY 4

5. Add our melodies to sound.c and write the names of the melodies to the melody array.

Adding melodies

A melody is an array of 16-bit numbers and has the following structure

BPM (number of quarter notes per minute) Is a constant used to calculate the duration of notes and determines the speed at which the melody is played.

BPM can take values ​​from 1 to 24, which corresponds to 10 and 240 quarter notes per minute, respectively.

If the duration of notes / sounds is specified in milliseconds, then the BPM written in the array must be equal to 1.

If the SOUND_BPM constant is commented out in the header file sound.h, then the duration of the notes is calculated during the program execution according to the BPM specified in the array. If SOUND_BPM is not commented out, the duration of the notes is calculated at the compilation stage, based on the value of this constant, while all the melodies will be played at the same tempo. This limits functionality, but saves a few bytes of code.

The number of repetitions. It can take values ​​1 ... 254 and LOOP (255). LOOP - means that the melody will repeat indefinitely until the command SOUND_STOP or SOUND_PAUSE is given.

Note duration- the time during which a given sound tone is generated or a pause is maintained. It can be specified in ms, using the ms (x) macro, or in the form of standard note durations - eighths, sixteenths, etc. Below is a list of supported durations. If there is a need for some exotic durations, you can always add them in the tone.h file.

n1 - whole note

n2 - half note

n4 - quarter

n8 - eighth

n3 - eighth triplet

n16 - sixteenth

n6 - sextuple

n32 - thirty-second

Note height is set using symbolic constants described in the tone.h file, for example C2, A1, etc. Also, the pitch of the notes can be set in Hertz using the f (x) macro.

The program has restrictions on the minimum and maximum sound frequency!

Melody end marker. The value of the last element of the array must be zero.

Using the sound module

At the beginning of main`a, you must call the SOUND_Init () function. This function sets the microcontroller pin to output, configures timer T2 and initializes the module variables.

Then you need to set the interrupt enable flag - __enable_interrupt (), because the module uses timer T2 overflow and coincidence interrupts.

Then you can start playing melodies.

For example, like this:

SOUND_SetSong (2);

SOUND_Com (SOUND_PLAY); // play the melody

// set pointer to 2nd melody

// and start playback

SOUND_PlaySong (2);

The melody playback can be stopped at any time by issuing the SOUND_STOP command.
You can also pause the melody using the SOUND_PAUSE command. Subsequent issuance of the SOUND_PLAY command resumes playing the melody from the point at which it was stopped.

In principle, this functionality is not particularly needed (I just screwed it up), and when working with the module, the SOUND_PlaySong (unsigned char numSong) function is enough;

Files

You can download examples of using the sound module from the links below. I didn’t draw the scheme, because everything is simple there. connected to the PB0 pin, the melody start button is connected to the PD3 pin. There are 4 melodies defined in the projects. Pressing the button starts a new melody each time. The microcontroller used is atmega8535. Initially I wanted to get confused on a project with four buttons - PLAY, STOP, PAUSE and NEXT, but then I thought it was unnecessary.

PS: The module has not been extensively tested and is provided “as is”. If there are any rational suggestions, let's finalize it.

I was sitting the other day thinking, why would this "attach" to my scooter: there is music, there is backlighting, but something is missing, and then I remembered about the alarm, for sure! After all, it’s just that I don’t have! I suggest that you also assemble an alarm for your two-wheeled - for example, a bicycle, or maybe a four-wheeled friend. The signaling is collected on the AVR ATmega8 microcontroller, the project is also repeated on the Attiny2313 microcontroller. For the circuit options on the Atmega8, I wrote three firmware options, one firmware reproduces a sound reminiscent of a car alarm, and the other looks like a siren for an alarm located in a building (faster and sharper melody). All firmwares are signed and are in the archive below, I think you will understand them. In addition, the archive contains a simulation of circuits in the proteus, so you can listen to the sounds and choose the version that suits you best.

Atmega8 circuit:

As you can see, nothing special, a microcontroller, three resistors and two LEDs with a speaker. Instead of a button on the diagram, you can use, for example, a reed switch, or another contact. The circuit works as follows, if power is applied, then LED D3 will light up (or blink - depending on the option of the circuit), if the sensor is not touched, the siren will be silent. As soon as the sensor is triggered, the alarm will be triggered and LED D2 will flash at the same time. Personally, I connected the terminal 24 PС1 through a transistor key to the relay, and the relay in series with the headlight of the scooter, so that when the alarm is triggered, the headlight of the scooter blinks. In order to stop the siren, you need to turn off and turn on the circuit or press the button again. I want to note that the signal from the controller can be amplified by several transistors by assembling a small amplifier - which, in principle, I did, although I did not depict this circuit on the diagram. The microcontroller operates from an internal 8 MHz oscillator, we set the fuses accordingly.

The PCB for the Atmega8 looks like this:

The scheme on the Attiny2313 does not differ much from the first option, it is just that there are different output ports.

Diagram on Attiny2313:

For this version of the circuit, I wrote only one firmware, with one version of the signal, just in case, I assembled the circuit with a hinged installation and checked the operability. The microcontroller operates from an internal 4 MHz generator (you can flash it at 1 MHz), when programming, we set the following fuses:

Since I did not have a live Atmega8 controller at hand, I assembled the circuit on Attiny2313, the circuit started working right away, I assembled the circuit by mounting, below is the photo:

Well, the video of the work of the circuit, the video is really not the very best quality and there is no LED blinking on it because the frame rate is low.

Download projects in, firmware and files printed circuit boards you can below

List of radioelements

Designation A type Denomination Quantity NoteShopMy notebook
U1 MK AVR 8-bit

ATmega8-16PU

1 Into notepad
R1 Resistor

47 Ohm

1 Into notepad
R2, R3 Resistor

270 Ohm

2 Into notepad
Diagram on Attiny2313
U1 MK AVR 8-bit