Overall introduction of HaaS EDU scenario application
1. Experimental introduction
Chiptune is a childhood memory of many post-80s and 90s. It is said that the name of Chiptune should be unfamiliar to many people, but it has another name: 8-bit. The so-called Chiptune is a song written by the sound emitted by the chips of old home computers, video game consoles and arcades (also known as CHIP). Strictly speaking, Chiptune is not only 8bit, but also a low bit rate that pursues the sense of retro particles.
In this experiment, we also come to realize a retro "eight tone" box.
2. Knowledge points involved
- Music score coding
- PWM and buzzer
3. Software and hardware environment preparation
3.1 hardware
One computer for development
One HAAS EDU K1 development board (built-in buzzer)
One USB2TypeC data cable
3.2 software
"Retro music box" function has been included in edu_ The demo is applied and included in the release version.
3.2.1 firmware version
Firmware version: v1.0 zero
3.2.2 code path
git clone https://gitee.com/alios-things/AliOS-Things.git -b dev_3.1.0_haas cd AliOS-Things/application/example/edu_demo/k1_apps/musicbox
3.2.3 compilation
Enter the top-level directory of the code, such as Alios things, for compilation. Directly compile the edu in the application/example / directory_ Demo application.
Two methods to compile
Command line mode
aos make distclean aos make edu_demo@haaseduk1 -c config aos make
AliOS Studio IDE mode
3.2.4 burning
See the development environment section
4. Buzzer
The buzzer is a very simple sounding device. Unlike the loudspeaker used for playing, the buzzer can only play a relatively simple frequency.
From the driving principle, buzzer can be divided into passive buzzer and active buzzer. The "source" here refers to whether there is a driving source. Passive buzzer, as the name suggests, does not have its own built-in drive source. Only when the voice coil is connected with alternating current, the electromagnet inside the voice coil and the permanent magnet attract or repel each other to push the diaphragm to make sound. After the direct current is connected, the diaphragm can only be pushed continuously without sound, and the sound can only be generated when it is connected or disconnected. On the contrary, as long as the DC power is connected to the active driver, the internal driving source will drive the diaphragm at a fixed frequency and make sound directly.
In this experiment, it is recommended to use a passive buzzer, because it is only driven by PWM, and the sound will be more crisp and pure. A similar effect can be achieved when using the active buzzer, but the sound will be slightly noisy due to the superposition of its own vibration frequency.
4.1. Driving circuit
The first end of the buzzer is connected to the VCC and the second end is connected to the triode. The triode here is driven by PWM0 to determine whether the two ends of the buzzer are connected with GND, and then cause an oscillation. By continuously turning the IO port, the buzzer can be driven to sound.
4.2 driver code
In order to flip the IO port at a specific frequency, we can use the PWM (pulse width modulation) function. Refer to Chapter 3 for details on PWM resources.
In this experiment, we implemented two methods: tone and noTone. Among them, the tone method is used to drive the buzzer to emit a sound of a specific frequency, that is, "tone". The noTone method is used to turn off the buzzer.
It is worth noting that in the tone method, the duty cycle of pwm is fixed at 0.5, which means that in a vibration cycle, the diaphragm of the buzzer is always on the top half of the time and on the bottom half of the time. Changing the duty cycle here will not change the power of the buzzer, so the volume will not change.
// application/example/edu_demo/k1_apps/musicbox/musicbox.c void tone(uint16_t port, uint16_t frequency, uint16_t duration) { pwm_dev_t pwm = {port, {0.5, frequency}, NULL}; // Set pwm frequency as set frequency if (frequency > 0) // pwm can only be initialized if the frequency value is legal { hal_pwm_init(&pwm); hal_pwm_start(&pwm); } if (duration != 0) { aos_msleep(duration); } if (frequency > 0 && duration > 0) // If duration is set, playback stops after this delay { hal_pwm_stop(&pwm); hal_pwm_finalize(&pwm); } } void noTone(uint16_t port) { pwm_dev_t pwm = {port, {0.5, 1}, NULL}; // Turn off the pwm output of the corresponding port hal_pwm_stop(&pwm); hal_pwm_finalize(&pwm); }
5. From tone to music
After completing the drive of the buzzer, we can make the buzzer sound at the frequency we want. Next, what we need to do is combine these frequencies to form music.
5.1. Define tone
At present, we can only specify the frequency of sound, but we don't know how the frequency corresponds to the tone. Only by following the tone can we splice the music. If we regard the buzzer as the device we want to drive, the corresponding relationship between frequency and tone is the communication protocol, and music is the ideal device output.
We use the commonly used musical formula at present—— twelvetone equal temperament . Using the definition of Wikipedia, it can be calculated as follows:
Set the tonic tone to a1(440Hz) to calculate the frequency of all tones. The results are as follows (in order to make the calculation process clearer, the score is not reduced):
Interval name | Interval halftone number | Multiple of twelve mean law | frequency |
Purity degree (A1) | 0 | {\displaystyle 2^{0}=1\,} | {\displaystyle 440\times 1=440\,} |
One degree increase / two degrees decrease (A ♯ 1/B ♭ 1) | 1 | {\displaystyle {\sqrt[{12}]{2}}=2^{\frac {1}{12}}\approx 1.0594630943592952645618252949463} | {\displaystyle 440\times 2^{\frac {1}{12}}\approx 466.1637615180899164072031297762} |
Large second degree (B1) | 2 | {\displaystyle {\sqrt[{6}]{2}}=2^{\frac {2}{12}}\approx 1.1224620483093729814335330496792} | {\displaystyle 440\times 2^{\frac {2}{12}}\approx 493.8833012561241118307545418586} |
Minor third degree (C) | 3 | {\displaystyle {\sqrt[{4}]{2}}=2^{\frac {3}{12}}\approx 1.1892071150027210667174999705605} | {\displaystyle 440\times 2^{\frac {3}{12}}\approx 523.2511306011972693556999870466} |
Third degree (C ♯) | 4 | {\displaystyle {\sqrt[{3}]{2}}=2^{\frac {4}{12}}\approx 1.2599210498948731647672106072782} | {\displaystyle 440\times 2^{\frac {4}{12}}\approx 554.3652619537441924975726672023} |
Pure fourth degree (D) | 5 | {\displaystyle {\sqrt[{12}]{32}}=2^{\frac {5}{12}}\approx 1.3348398541700343648308318811845} | {\displaystyle 440\times 2^{\frac {5}{12}}\approx 587.3295358348151205255660277209} |
Increase by four degrees / decrease by five degrees (D#/E ♭) | 6 | {\displaystyle {\sqrt {2}}=2^{\frac {6}{12}}\approx 1.4142135623730950488016887242097} | {\displaystyle 440\times 2^{\frac {6}{12}}\approx 622.2539674441618214727430386522} |
Pure five degrees (E) | 7 | {\displaystyle {\sqrt[{12}]{128}}=2^{\frac {7}{12}}\approx 1.4983070768766814987992807320298} | {\displaystyle 440\times 2^{\frac {7}{12}}\approx 659.2551138257398594716835220930} |
Minor six degrees (F) | 8 | {\displaystyle {\sqrt[{3}]{4}}=2^{\frac {8}{12}}\approx 1.5874010519681994747517056392723} | {\displaystyle 440\times 2^{\frac {8}{12}}\approx 698.4564628660077688907504812795} |
Big six degrees (F#) | 9 | {\displaystyle {\sqrt[{4}]{8}}=2^{\frac {9}{12}}\approx 1.6817928305074290860622509524664} | {\displaystyle 440\times 2^{\frac {9}{12}}\approx 739.9888454232687978673904190852} |
Minor seven degrees (G) | 10 | {\displaystyle {\sqrt[{6}]{32}}=2^{\frac {10}{12}}\approx 1.781797436280678609480452411181} | {\displaystyle 440\times 2^{\frac {10}{12}}\approx 783.9908719634985881713990609195} |
Big seven degrees (G#) | 11 | {\displaystyle {\sqrt[{12}]{2048}}=2^{\frac {11}{12}}\approx 1.8877486253633869932838263133351} | {\displaystyle 440\times 2^{\frac {11}{12}}\approx 830.6093951598902770448835778670} |
Pure octave (A) | 12 | {\displaystyle 2^{1}=2\,} | {\displaystyle 440\times 2=880\,} |
In this way, we get the relationship between frequency and tone, and we record it in the header file.
// application/example/edu_demo/k1_apps/musicbox/pitches.h #define NOTE_B0 31 #define NOTE_C1 33 #define NOTE_CS1 35 #define NOTE_D1 37 #define NOTE_DS1 39 ... ... #define NOTE_B7 3951 #define NOTE_C8 4186 #define NOTE_CS8 4435 #define NOTE_D8 4699 #define NOTE_DS8 4978 In this way, we can adopt tone Method to emit the corresponding tone. tone(0, NOTE_B7, 100) // Use pwm0 corresponding buzzer to play NOTE_B7 lasts for 100ms
5.2. Generate music score
Next, we can start composing music. Here we choose a very simple children's Song - "two tigers" to show you how to compose music.
Our tone method has two parameters that need attention: frequency determines the playing tone, and duration determines the playing time of the tone, that is, the beat. Therefore, we also need to pay attention to these two parameters when reading the simplified spectrum.
Interested students can refer to some basic knowledge of simplified music wikipedia - simple score . This experiment will only use very simple methods, so you can also read directly.
Take "two tigers" as an example.
5.2.1 notes
Notes are represented by numbers 1 to 7. These seven numbers are equal to the natural scale in major.
1 = C in the upper left corner represents the key sign, which represents this simple score. If you use C major and add the sound name, it will be like this:
1 = C | |||||||
Scale | C | D | E | F | G | A | B |
roll call | do | re | mi | fa | sol | la | Si |
number | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
code | NOTE_C4 | NOTE_D4 | NOTE_E4 | NOTE_F4 | NOTE_G4 | NOTE_A4 | NOTE_B4 |
If the definition in the upper left corner is 1 = D, then re label it from D, as shown in the following table:
1 = D | |||||||
Scale | D | E | F | G | A | B | C |
roll call | do | re | mi | fa | sol | la | Si |
number | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
code | NOTE_D4 | NOTE_E4 | NOTE_F4 | NOTE_G4 | NOTE_A4 | NOTE_B4 | NOTE_C4 |
5.2.2 octave
If it is an octave higher, it will add a point above the number. If it is an octave lower, a point will be added below the number. You don't need to add anything in the middle octave. If you want to increase another octave, add two points vertically above (for example:); To lower another octave, add two points vertically below (for example:
), and so on.
1 = C | Major scale | ||||||
number | | | | 5 | | | |
code | NOTE_G7 | NOTE_G6 | NOTE_G5 | NOTE_G4 | NOTE_G3 | NOTE_G2 | NOTE_G1 |
1 = C | natural minor | ||||||
number | | | | 5 | | | |
code | NOTE_GS7 | NOTE_GS6 | NOTE_GS5 | NOTE_GS4 | NOTE_GS3 | NOTE_GS2 | NOTE_GS1 |
After knowing the notes and octaves, we can start filling in the tone array. Each element in this array corresponds to the frequency parameter of the tone method.
static int liang_zhi_lao_hu_Notes[] = {
NOTE_C4, NOTE_D4, NOTE_E4, NOTE_C4, NOTE_C4, NOTE_D4, NOTE_E4, NOTE_C4,
//Two old tigers two old tigers
NOTE_E4, NOTE_F4, NOTE_G4, NOTE_E4, NOTE_F4, NOTE_G4,
//♪ run fast ♪
NOTE_G4, NOTE_A4, NOTE_G4, NOTE_F4, NOTE_E4, NOTE_C4,
//One has no eyes
NOTE_G4, NOTE_A4, NOTE_G4, NOTE_F4, NOTE_E4, NOTE_C4,
//One has no tail
NOTE_D4, NOTE_G3, NOTE_C4, 0,
//That's weird
NOTE_D4, NOTE_G3, NOTE_C4, 0};
//That's weird
5.2.3, time sign and sound length
The 2 / 4 in the upper left corner indicates the time sign. Here, 4 means that the quarter note is a beat, and 2 means that there are two beats in each bar.
Usually there are only numbers Quarter note . Adding a horizontal line under the number can halve the length of the quarter note Quaver ; Two horizontal lines can halve the length of an octave to become Sixteenth note , and so on; The horizontal line after the number extends the note, one for each additional horizontal line Quarter note The length of the.
Therefore, we can get the beat array. Each element in this array corresponds to the duration parameter of the tone method.
static int liang_zhi_lao_hu_NoteDurations[] = {
8, 8, 8, 8, 8, 8, 8, 8,
8, 8, 4, 8, 8, 4,
16, 16, 16, 16, 4, 4,
16, 16, 16, 16, 4, 4,
8, 8, 4, 4,
8, 8, 4, 4};
5.2.4 definition of structure
Next, we fill the obtained score information into the structure.
// application/example/edu_demo/k1_apps/musicbox/musicbox.c typedef struct { char *name; // The name of music int *notes; // Note array int *noteDurations; // Beat array unsigned int noteLength; // Number of notes unsigned int musicTime; // The total duration of music is processed by the player and used for interface display. Users don't need to care } music_t; // Musical structure typedef struct { music_t **music_list; // Music List unsigned int music_list_len; // Length of Music List int cur_music_index; // What is the current music unsigned int cur_music_note; // Which note of the current music unsigned int cur_music_time; // The current playback duration is handled by the player and used for interface display. Users do not need to care unsigned int isPlaying; // Whether the music is played / paused is handled by the player, and users do not need to care } player_t; static music_t liang_zhi_lao_hu = { "liang_zhi_lao_hu", liang_zhi_lao_hu_Notes, liang_zhi_lao_hu_NoteDurations, 34 }; music_t *music_list[] = { &liang_zhi_lao_hu_Notes, // Insert music into the music list }; player_t musicbox_player = {music_list, 1, 0, 0, 0, 0}; // Initialize music player
6. Realize playing music
while (1) { // If the current tone subscript is less than the total tone of the music, it has not been played if (musicbox_player.cur_music_note < cur_music->noteLength) { // Calculate the delay of 1000ms / n diaeresis required by the current note through the beat int noteDuration = 1000 / cur_music->noteDurations[musicbox_player.cur_music_note]; // For the notes with dots, we use the reading to mark. After adding a dot, the sound length of the note is half longer than its original sound length, that is, 1.5 times of the original sound length. noteDuration = (noteDuration < 0) ? (-noteDuration * 1.5) : noteDuration; // Get the current tone int note = cur_music->notes[musicbox_player.cur_music_note]; // Play tones using the tone method tone(0, note, noteDuration); // Delay for a period of time to make the tone conversion clearer aos_msleep((int)(noteDuration * NOTE_SPACE_RATIO)); // Calculate the current playback time musicbox_player.cur_music_time += (noteDuration + (int)(noteDuration * NOTE_SPACE_RATIO)); // Ready to play the next tone musicbox_player.cur_music_note++; } }
7. Draw player
As a developer with ideals and pursuit, just playing music can't meet our creative desire. So let's implement another player, which can pause / play, previous / next, and display the song name and progress bar.
To realize the required information, we have completed the relevant definitions in the structure, and only need to complete the corresponding music playback control according to the key operation.
void musicbox_task() { while (1) { // Remove the residue of the last painting OLED_Clear(); // Gets the pointer to the current music music_t *cur_music = musicbox_player.music_list[musicbox_player.cur_music_index]; // Get the name of the current music and draw it char show_song_name[14] = {0}; sprintf(show_song_name, "%-13.13s", cur_music->name); OLED_Show_String(14, 4, show_song_name, 16, 1); // If the current player is not paused (playing) if (musicbox_player.isPlaying) { // If it's not finished if (musicbox_player.cur_music_note < cur_music->noteLength) { int noteDuration = 1000 / cur_music->noteDurations[musicbox_player.cur_music_note]; noteDuration = (noteDuration < 0) ? (-noteDuration * 1.5) : noteDuration; printf("note[%d] = %d\t delay %d ms\n", musicbox_player.cur_music_note, cur_music->noteDurations[musicbox_player.cur_music_note], noteDuration); int note = cur_music->notes[musicbox_player.cur_music_note]; tone(0, note, noteDuration); aos_msleep((int)(noteDuration * NOTE_SPACE_RATIO)); musicbox_player.cur_music_time += (noteDuration + (int)(noteDuration * NOTE_SPACE_RATIO)); musicbox_player.cur_music_note++; } // If you finish playing, switch to the next song else { noTone(0); aos_msleep(1000); next_song(); // musicbox_player.cur_music_index + + player points to the next music } OLED_Icon_Draw(54, 36, &icon_pause_24_24, 1); // Draw a pause icon when the player is playing } else { OLED_Icon_Draw(54, 36, &icon_resume_24_24, 1); // Draw the play icon when the player is paused aos_msleep(500); } // Draw a straight line to represent the progress bar. The length of the straight line is 99.0 (the maximum length of the paintable area) * (musicbox_player.cur_music_time) / cur_ Music - > MusicTime OLED_DrawLine(16, 27, (int)(16 + 99.0 * (musicbox_player.cur_music_time * 1.0 / cur_music->musicTime)), 27, 1); // Draw icons for previous song and next song OLED_Icon_Draw(94, 36, &icon_next_song_24_24, 1); OLED_Icon_Draw(14, 36, &icon_previous_song_24_24, 1); // Displays the drawn information on the screen OLED_Refresh_GRAM(); } }
8. Developer technical support
For more technical support, you can add nail developers or pay attention to WeChat official account.
For more information on technologies and solutions, please visit Alibaba cloud AIoT homepage https://iot.aliyun.com/