Use Hongmeng OS to play a song "Two Tigers" on the buzzer

This article describes how to use the PWM interface of the Harmony OS IoT hardware subsystem to drive the buzzer to play music on the HiSpark Wi-Fi IoT kit.

API to output square wave with PWM

Hongmeng system IoT hardware subsystem provides PWM related interfaces, the interface header file is wifiiot_pwm.h, and the interface to start outputting square wave is:

/**
 * @brief Outputs PWM signals based on the input parameters.
 *
 * This function outputs PWM signals from a specified port based on
 * the configured frequency division multiple and duty cycle.
 *
 * @param port Indicates the PWM port number.
 * @param duty Indicates the PWM duty cycle.
 * @param freq Indicates the frequency-division multiple.
 * @return Returns {@link WIFI_IOT_SUCCESS} if the operation is successful;
 * returns an error code defined in {@link wifiiot_errno.h} otherwise.
 * @since 1.0
 * @version 1.0
 */
unsigned int PwmStart(WifiIotPwmPort port, unsigned short duty, unsigned short freq);

Square wave frequency of PWM output

Through the annotation of the PwmStart interface, you can know that the freq parameter is the frequency division multiple, and the square wave frequency actually output by the PWM is equal to the PWM clock source frequency divided by the frequency division multiple, that is

f = Fcs / freq

Among them, Fcs is the PWM clock source frequency;

Duty cycle of PWM output square wave

Through the duty parameter of the PwmStart interface, the duty cycle of the output square wave can be controlled. The duty cycle refers to the ratio of the high level time of the square wave waveform output by the PWM to the entire square wave cycle. The specific duty cycle value is the ratio of duty and freq , For example, if you want to output a square wave signal with a duty cycle of 50%, the value of duty should be freq/2;

Note-frequency correspondence
Insert picture description here

There is a rule in this table-the pitch increases by one octave and the frequency doubles.

The form comes from: https://liam.page/2018/04/09/pitch-interval-and-harmonic/

The lowest frequency that the development board can output
from the previous formula, we know:

  1. The frequency of the square wave output by the PWM is inversely proportional to freq, the larger the freq, the smaller the frequency of the output square wave;

  2. freq is an unsinged short type, and the maximum value is 65535;
    therefore, the minimum value of the output frequency depends on the clock source, and the default clock source of PWM is 160M:
unsigned int HalPwmInit(HalWifiIotPwmPort port)
{
    if (hi_pwm_set_clock(PWM_CLK_160M) != HI_ERR_SUCCESS) {
        return (unsigned int)HAL_WIFI_IOT_FAILURE;
    }
    return hi_pwm_init((hi_pwm_port)port);
}

Under the condition of 160M clock source, the lowest frequency of the output square wave is: 160M/65535=2441.44..., this frequency is still slightly higher, and no sound names are found in the above table. But I can use the values ​​in the above table to calculate two octaves and then I can cover this frequency (but usually only 7 octaves are used, so it is still a bit high).

If the clock source frequency can be lower, the output frequency can also be lower!
Fortunately, the clock source can be modified by calling the hi_pwm_set_clock interface:

/**
 * @ingroup iot_pwm
 *
 * Enumerates the PWM clock sources.CNcomment:PWM时钟源枚举。CNend
 */
typedef enum {
    PWM_CLK_160M, /**< 160M APB clock.CNcomment:160M 工作时钟 CNend */
    PWM_CLK_XTAL, /**< 24M/40M crystal clock.CNcomment:24M或40M 晶体时钟 CNend */
    PWM_CLK_MAX   /**< Maximum value, which cannot be used.CNcomment:最大值,不可使用CNend */
} hi_pwm_clk_source;

hi_u32 hi_pwm_set_clock(hi_pwm_clk_source clk_type);

Through the comments, we know that hi_pwm_set_clock(PWM_CLK_XTAL); can set the clock source to the crystal clock, the crystal clock may be 24M or 40M;
then the question is-what is the crystal clock frequency?

What is the crystal clock frequency?
The crystal clock frequency can be calculated through experiments, the specific steps are as follows:

  1. Use hi_pwm_set_clock(PWM_CLK_XTAL); to set the clock source to crystal clock;

  2. Use PwmStart(WIFI_IOT_PWM_PORT_PWM0, 20 1000, 40 1000); output square wave signal;

  3. Use an oscilloscope to measure the square wave frequency, and calculate the clock source frequency according to the measured frequency;

After actual measurement, the square wave frequency is 1000 Hz,

Therefore, the clock frequency is 1000 40 1000, which is 40 MHz;

The lowest frequency of the square wave that can be output.
Therefore, the lowest frequency of the square wave is 40M / 65535, which is:

40 1000 1000/65535
610.3608758678569
control frequency table above, it is known, above all capable of outputting notes and E5;

Preparing the score
In order to make the code easier to implement, I chose the score of "Two Tigers" as the material, and found the score on the numbered music network:
Insert picture description here

The numbered musical notation explains
some of the marks on the numbered musical notation. Some students may not know what they mean. Here is a brief explanation:

  1. The 1=C in the upper left corner means the mode (you don’t need to care about it), 1 is the roll name, C is the note name, 1=C is the forward tone (the normal correspondence: 1-C, 2-D, 3-E, 4 -F, 5-G, 6-A, 7-B);

  2. The 4/4 in the upper left corner is four or four beats, which means that the quarter note is one beat, and there are four beats per measure;

  3. The vertical line on the score below is each bar separator, corresponding to 4/4;

  4. "Run fast" The horizontal line behind 5 above indicates one beat delay;

  5. In the sentence "one without eyes", the dot after 5 means half a beat, an underline means half the time, and two underlines mean a quarter time;

Writing code
With the above knowledge, we can write code, the key code is as follows:

static const uint16_t g_tuneFreqs[] = { // 音符对应的分频系数
    0, // 40M Hz 时钟源,C6 ~ B6:
    38223, // 1 1046.5
    34052, // 2 1174.7
    30338, // 3 1318.5
    28635, // 4 1396.9
    25511, // 5 1568
    22728, // 6 1760
    20249, // 7 1975.5
    51021 // 5_ 783.99 // 低一个八度的 5
};

// 曲谱音符
static const uint8_t g_scoreNotes[] = {
    // 《两只老虎》简谱:http://www.jianpu.cn/pu/33/33945.htm
    1, 2, 3, 1,        1, 2, 3, 1,        3, 4, 5,  3, 4, 5,
    5, 6, 5, 4, 3, 1,  5, 6, 5, 4, 3, 1,  1, 8, 1,  1, 8, 1, // 最后两个 5 应该是低八度的,链接图片中的曲谱不对,声音到最后听起来不太对劲
};

// 曲谱时值,根据简谱记谱方法转写
static const uint8_t g_scoreDurations[] = {
    4, 4, 4, 4,        4, 4, 4, 4,        4, 4, 8,  4, 4, 8,
    3, 1, 3, 1, 4, 4,  3, 1, 3, 1, 4, 4,  4, 4, 8,  4, 4, 8,
};

static void *BeeperMusicTask(const char *arg)
{
    (void)arg;

    printf("BeeperMusicTask start!\r\n");

    hi_pwm_set_clock(PWM_CLK_XTAL); // 设置时钟源为晶体时钟(40MHz,默认时钟源160MHz)

    for (size_t i = 0; i < sizeof(g_scoreNotes)/sizeof(g_scoreNotes[0]); i++) {
        uint32_t tune = g_scoreNotes[i]; // 音符
        uint16_t freqDivisor = g_tuneFreqs[tune];
        uint32_t tuneInterval = g_scoreDurations[i] * (125*1000); // 音符时间
        printf("%d %d %d %d\r\n", tune, (40*1000*1000) / freqDivisor, freqDivisor, tuneInterval);
        PwmStart(WIFI_IOT_PWM_PORT_PWM0, freqDivisor/2, freqDivisor);
        usleep(tuneInterval);
        PwmStop(WIFI_IOT_PWM_PORT_PWM0);
    }

    return NULL;
}

The last two 5s in the score are wrong, they should be 5 octaves lower, that is, there should be a dot below the 5; I modified the code to make the whole song sound more natural;

Complete code:https://gitee.com/hihopeorg/HarmonyOS-IoT-Application-Development/blob/master/02_device_control/beeper_music_demo.c


Original link:
https://developer.huawei.com/consumer/cn/forum/topic/0204398682948650101?fid=0101303901040230869
Author: Thinking

Guess you like

Origin blog.51cto.com/14772288/2549430