本文介紹如何在HiSpark Wi-Fi IoT套件上,使用Harmony OS IoT硬件子系統的PWM接口 驅動蜂鳴器 播放音樂。java
用PWM輸出方波的APIgit
鴻蒙系統IoT硬件子系統提供了PWM相關接口,接口頭文件爲wifiiot_pwm.h,其中開始輸出方波的接口爲:ide
/** * @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);
PWM輸出的方波頻率ui
經過PwmStart接口的註釋,能夠知道freq參數是分頻倍數,PWM實際輸出的方波頻率等於 PWM時鐘源頻率 除以 分頻倍數,即code
f = Fcs / freqhtm
其中,Fcs是PWM時鐘源頻率;blog
PWM輸出方波的佔空比接口
經過PwmStart接口的duty參數能夠控制輸出方波的佔空比,佔空比是指PWM輸出的方波波形的高電平時間佔整個方波週期的比例,具體佔空比值是 duty 和 freq的比值,例如想要輸出佔空比 50%的方波信號,那麼duty填的值就要是 freq/2;圖片
音符-頻率對應關係
ip
這個表中有一個規律——音高升高一個八度,頻率升高一倍。
表格來自:https://liam.page/2018/04/09/pitch-interval-and-harmonic/
開發板能夠輸出的最低頻率
經過前面的公式,咱們知道:
PWM輸出的方波頻率和freq成反比,freq越大,輸出的方波頻率越小;
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); }
160M時鐘源條件下,輸出方波的最低頻率是:160M/65535=2441.44...,這個頻率仍是略高,在上面的表格中沒有找到音名。可是我能夠用上面表格值繼續日後推算兩個八度,就可以覆蓋這個頻率(不過一般只使用7個八度,因此仍是有點高)。
若是時鐘源頻率能夠更低,那麼輸出頻率也能夠更低!
幸運的是,經過調用hi_pwm_set_clock接口,能夠修改時鐘源:
/** * @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);
經過註釋咱們知道hi_pwm_set_clock(PWM_CLK_XTAL);能夠將時鐘源設置爲晶體時鐘,晶體時鐘可能爲24M或40M;
那麼問題來了——晶體時鐘頻率究竟是多少?
晶體時鐘頻率是多少?
能夠經過實驗測算出晶體時鐘頻率,具體步驟以下:
使用 hi_pwm_set_clock(PWM_CLK_XTAL); 設置時鐘源爲晶體時鐘;
使用PwmStart(WIFI_IOT_PWM_PORT_PWM0, 201000, 401000);輸出方波信號;
經實際測量,方波頻率爲1000Hz,
所以,時鐘頻率爲 1000 40 1000,即 40 MHz;
能夠輸出的方波最低頻率
所以,方波最低頻率就是 40M / 65535 ,也就是:
40 1000 1000 / 65535
610.3608758678569
對照上面的頻率表,能夠知道,可以輸出E5及以上的全部音符;
準備樂譜
爲了代碼實現起來簡單,我選擇了《兩隻老虎》的樂譜做爲素材,在簡譜網找到了簡譜:
簡譜說明
簡譜上的一些記號,有的同窗可能不太清楚是什麼意思,這裏簡單說明一下:
左上角的1=C是表示調式(能夠不用關心),1是唱名,C是音名,1=C是正調(就是常規的對應關係: 1-C,2-D, 3-E, 4-F, 5-G, 6-A, 7-B);
左上角的 4/4 是四四拍,是指 四分音符爲一拍, 每小節有四拍;
下面譜子上的豎線就是每一個小節分隔符,和4/4對應;
「跑得快」上面5後面的橫線表示延時一拍;
編寫代碼
有了以上知識,咱們就能夠編寫代碼了,關鍵代碼以下:
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; }
譜子中最後兩個5是錯誤的,應該是低八度的5,也就是5下面應該打一個點;我修改了代碼,讓整個曲子聽起來更天然;
完整代碼:https://gitee.com/hihopeorg/HarmonyOS-IoT-Application-Development/blob/master/02_device_control/beeper_music_demo.c
原文連接:
https://developer.huawei.com/consumer/cn/forum/topic/0204398682948650101?fid=0101303901040230869做者:思惟