WeChat_20231023233924
Preface
Earlier we implemented PWM output, which can set the specified duty cycle and frequency. Based on this, we can implement PWM to play audio.
There are two ways to play audio with PWM
One is that PWM outputs different frequencies corresponding to different tones, similar to MIDI sound. This relatively low requirement for PWM update frequency, a tone does not change during a performance time.
The basic principle is as follows:
Frequency-corresponding pitch
Duty cycle-corresponding loudness
PWM output duration - corresponding playing duration
Another method is the PWM-DAC method, where different PWM output duty cycles represent different DAC output values, which requires a higher PWM update frequency.
We will now use the former to implement music playback.
Prepare
We need to first convert the music score into PWM frequency and duty cycle as well as the parameters of playing time.
Based on the international standard tone A-la-440HZ:
The frequency of do is 261.6HZ,
The frequency of re is 293.6HZ,
The frequency of mi is 329.6HZ,
The frequency of fa is 349.2HZ,
The frequency of sol is 392HZ,
The frequency of la is 440HZ,
The frequency of si is 493.8HZ.
Let’s take “Two Tigers” as an example
First look at the beat description in the upper left corner: 2/4 time, 1/4 note per beat, 2 beats per measure
76 beats per minute.
First define the following correspondence between scale and frequency
#define DO 523
#define RE 587
#define MI 659
#define FA 698
#define _SO 392
#define SO 784
#define LA 880
#define SI 987
#define NO 523
#define DUTY0 0
We can see that if the main frequency is 100MHz, the maximum 16-bit 65535 division frequency can only reach
1525HZ, so we need to reduce the main clock source frequency, we change it to 64 division
The corresponding functions below should also be modified
void pwm_set_frq_period(uint32_t duty, uint32_t frq)
{
uint32_t period = (uint64_t)(100000000ul/64)/frq;
duty = ((uint64_t)duty*period*2+100)/200;
set_intensity(duty, period, TIMER_PIN);
}
Defining Takt Time
#define METERS 76ul // 拍数76 1分钟76拍 每拍60/76秒 2/4 4分音符一拍 每小节2拍
#define NOTE_4 (60000ul/METERS) // 先定一拍对应的音符 4分音符 x1000转化为单位mS
#define NOTE_8 (NOTE_4/2ul) // 8分音符 一个下划线
#define NOTE_16 (NOTE_8/2ul) // 16分音符 二个下划线
Define the scale table, corresponding frequency
const uint16_t beepfrep[] =
{
DO,RE,MI,DO,
DO,RE,MI,DO,
MI,FA,SO,NO,
MI,FA,SO,NO,
SO,LA,SO,FA,MI,DO,
SO,LA,SO,FA,MI,DO,
DO,_SO,DO,NO,
DO,_SO,DO,NO
};
Define the sound size table, corresponding to the duty cycle
const uint16_t beepduty[] =
{
40,40,40,40,
40,40,40,40,
40,40,40,DUTY0,
40,40,40,DUTY0,
40,40,40,40,40,40,
40,40,40,40,40,40,
40,40,40,DUTY0,
40,40,40,DUTY0,
};
Define the beat table, corresponding to the playing time of each scale
const uint32_t beeptime[]=
{
NOTE_8,NOTE_8,NOTE_8,NOTE_8,
NOTE_8,NOTE_8,NOTE_8,NOTE_8,
NOTE_8,NOTE_8,NOTE_8,NOTE_8,
NOTE_8,NOTE_8,NOTE_8,NOTE_8,
NOTE_16,NOTE_16,NOTE_16,NOTE_16,NOTE_8,NOTE_8,
NOTE_16,NOTE_16,NOTE_16,NOTE_16,NOTE_8,NOTE_8,
NOTE_8,NOTE_8,NOTE_8,NOTE_16,
NOTE_8,NOTE_8,NOTE_8,NOTE_16,
};
Implement delay function, unit: mS
void mdelay(uint32_t t)
{
R_BSP_SoftwareDelay(t,BSP_DELAY_UNITS_MILLISECONDS);
}
Implementing the performance function
extern void pwm_set_frq_period(uint32_t duty, uint32_t frq);
void beep_play_music(const uint16_t* freq,const uint16_t* duty, const uint32_t* time, uint16_t len)
{
uint16_t i = 0;
for(i=0; i< len; i++)
{
pwm_set_frq_period(duty[i],freq[i]);
mdelay(time[i]);
}
}
test
Add test function
void pwm_audio_test(void)
{
beep_play_music(beepfrep, beepduty, beeptime, sizeof(beepfrep)/sizeof(beepfrep[0]));
}
Add command line in shell_func.c
{ (const uint8_t*)"pwmaudio", PwmaudioFun, "pwmaudio"},
void pwm_audio_test(void);
void PwmaudioFun(unsigned char* param)
{
(void)param;
pwm_audio_test();
}
Add declaration in shell_func.h
void PwmaudioFun(unsigned char* param);
You can see the added commands are as follows
Connect the buzzer to test the effect, see the video
Summarize
- You can add more second scores to play different music.
- You can also change the above blocking delay to timed interrupt processing, so that non-blocking is more convenient to use.