本帖最后由 WangChong 于 2025-5-23 03:24 编辑
简介
在上一篇文章中我们对SDK的GPIO输出进行了控制, 在上一章节的基础上我们将在本章节对PWM的输出进行控制(如果没有看过上一篇文章的,建议先去看上一篇文章, 对组件和设计的库函数进行了介绍。本章节不多做赘述。)SDK文件还是使用的上一章节的SDK(如无特殊说明,SDK不变)。
默认PWM DEMO输出现象
首先,在user_config.h 文件中开启对PWMLED的宏, 使其用户代码默认烧录USER_DEMO_PWM_LED的例程
根据对应的hb_pwm_led.c 得知, 其线程启动后,主要是控制PWM_NUM_1_A27进行输出
- #define PWM_LED_GPIO_NUM PWM_NUM_1_A27 // "MOSI" on HB-M demo board
复制代码
首先我们对这个默认的Demo进行编译,并且下载到VC02中。 并且将示波器连接到开发板背部的GPIOA27上, 那么此时示波器的波形如下所示
占空比缓慢变化
此时默认的出厂PWM demo已经测试完毕。 如果想控制对应的PWM输出的话, 可以参考user_pwm.h 中的函数定义。 函数声明如下所示。
- #ifndef USER_INC_USER_PWM_H_
- #define USER_INC_USER_PWM_H_
-
- #ifdef __cplusplus
- extern "C" {
- #endif
-
- #include "unione.h"
- /** @ingroup uni_pwm_def
- * PWM管脚号
- */
- typedef enum {
- PWM_NUM_1_A27 = 0,
- PWM_NUM_1_B0,
- PWM_NUM_1_B2, ///< PWM 1 only 1 pin work a time
- PWM_NUM_2_A28, ///< used for PA enable, don't use it on HB-M demo board
- PWM_NUM_2_B1,
- PWM_NUM_2_B3, ///< PWM 2 only 1 pin work a time
- PWM_NUM_MAX
- }USER_PWM_NUM;
- /** @addtogroup uni_pwm_inf
- @{*/
- /**
- *@brief PWM初始化
- *@param num PWM管脚号
- *@param hz 频率
- *@param is_high_duty \a TRUE :占空比用高电平持续时间计算; \a FALSE :占空比用低电平持续时间计算
- *@retval 0 操作成功
- *@retval -1 操作失败
- */
- int user_pwm_init(USER_PWM_NUM num, uni_u32 hz, uni_bool is_high_duty);
- /**
- *@brief PWM反初始化
- *@param num PWM管脚号
- *@retval 0 操作成功
- *@retval -1 操作失败
- */
- int user_pwm_final(USER_PWM_NUM num);
- /**
- *@brief 开使PWM输出
- *@param num PWM管脚号
- *@param duty 占空比
- *@retval 0 操作成功
- *@retval -1 操作失败
- */
- int user_pwm_start(USER_PWM_NUM num, uni_u8 duty);
- /**
- *@brief 停止PWM输出
- *@param num PWM管脚号
- *@retval 0 操作成功
- *@retval -1 操作失败
- */
- int user_pwm_stop(USER_PWM_NUM num);
- /**
- *@brief 暂停PWM输出
- *@param num PWM管脚号
- *@retval 0 操作成功
- *@retval -1 操作失败
- */
- int user_pwm_pause(USER_PWM_NUM num);
- /**
- *@brief 恢复PWM输出
- *@param num PWM管脚号
- *@retval 0 操作成功
- *@retval -1 操作失败
- */
- int user_pwm_resume(USER_PWM_NUM num);
- /**
- *@brief PWM占空比切换
- *@param num PWM管脚号
- *@param duty 占空比
- *@retval 0 操作成功
- *@retval -1 操作失败
- */
- int user_pwm_change_duty(USER_PWM_NUM num, uni_u8 duty);
- /**
- *@brief PWM占空比增加
- *@param num PWM管脚号
- *@param ch_duty 增加的占空比
- *@retval 0 操作成功
- *@retval -1 操作失败
- */
- int user_pwm_duty_inc(USER_PWM_NUM num, uni_u8 ch_duty);
- /**
- *@brief PWM占空比减小
- *@param num PWM管脚号
- *@param ch_duty 减小的占空比
- *@retval 0 操作成功
- *@retval -1 操作失败
- */
- int user_pwm_duty_dec(USER_PWM_NUM num, uni_u8 ch_duty);
- /** @}*/
- #ifdef __cplusplus
- }
- #endif
- #endif
复制代码
自定义控制命令
接下来我们尝试使用唤醒命令 “你好小美” 来启动这个PWM进程, 然后使用 “打开台灯” 命令来删除这个进程。 从而控制PWM进行输出。 由于官方的SDK写的非常规范, 所以我建议凡是个人相关的代码都可以写在example目录下。
1- 首先我们在example 目录下创建hb_user_pwm_testing.c 文件
代码如下所示
- #include "user_gpio.h"
- #include "user_pwm_led.h"
- #include "user_event.h"
- #include "user_player.h"
- #include "user_config.h"
- #define PWM_LED_GPIO_NUM PWM_NUM_1_A27 // "MOSI" on HB-M demo board
- static uni_pthread_t g_pwm_thread_id = 0;
- static bool g_pwm_thread_running = false;
- // Thread function to update LED brightness levels
- static void _pwm_led_process(void *args) {
- LED_BRIGHT_LEVEL level = BRIGHT_LEVEL_0;
- g_pwm_thread_running = true;
- while (g_pwm_thread_running) {
- user_pwm_led_set_brightness(PWM_LED_GPIO_NUM, level);
- level += 1;
- if (level >= BRIGHT_LEVEL_MAX) {
- level = BRIGHT_LEVEL_0;
- }
- // uni_sleep(1); // Optional: slow down effect
- }
- }
- // Create PWM LED thread
- static Result _create_pwm_led_thread(void) {
- if (g_pwm_thread_running) {
- return E_OK;
- }
- thread_param param;
- uni_memset(¶m, 0, sizeof(param));
- param.stack_size = STACK_SMALL_SIZE;
- param.priority = OS_PRIORITY_LOW;
- uni_strncpy(param.task_name, "pwm_led", sizeof(param.task_name) - 1);
- if (0 != uni_pthread_create(&g_pwm_thread_id, ¶m, _pwm_led_process, NULL)) {
- return E_FAILED;
- }
- uni_pthread_detach(g_pwm_thread_id); // Auto cleanup
- return E_OK;
- }
- // Stop PWM thread
- static void _stop_pwm_led_thread(void) {
- if (!g_pwm_thread_running) {
- return;
- }
- g_pwm_thread_running = false;
- // Destroy thread if supported (may be platform-specific)
- if (g_pwm_thread_id != 0) {
- uni_pthread_destroy(g_pwm_thread_id); // Hard stop
- g_pwm_thread_id = 0;
- }
- user_pwm_led_set_brightness(PWM_LED_GPIO_NUM, BRIGHT_LEVEL_0); // Turn off LED
- }
- // Callback on voice command
- static void _on_wakeup_cmd_cb(USER_EVENT_TYPE event, user_event_context_t *context) {
- if (context == NULL) return;
- event_goto_awakend_t *awake = &context->goto_awakend;
- if (strcmp(awake->cmd, "wakeup_uni") == 0) {
- _create_pwm_led_thread();
- user_player_reply_list_random(awake->reply_files);
- }
- }
- static void _custom_setting_cb(USER_EVENT_TYPE event,
- user_event_context_t *context) {
- event_custom_setting_t *setting = NULL;
- if (context) {
- setting = &context->custom_setting;
- if (strcmp(setting->cmd, "TurnOn") == 0) {
- _stop_pwm_led_thread();
- user_player_reply_list_random(setting->reply_files);
- }
- }
- }
- // Register event callback
- static void _register_event_callback(void) {
- user_event_subscribe_event(USER_GOTO_AWAKENED, _on_wakeup_cmd_cb);
- user_event_subscribe_event(USER_CUSTOM_SETTING, _custom_setting_cb);
- }
- // Main entry
- int hb_user_pwm_testing(void) {
- if (0 != user_pwm_led_init(PWM_LED_GPIO_NUM)) {
- return -1;
- }
- _register_event_callback();
- return 0;
- }
复制代码
上述代码的主要功能是定义两个用户事件 ,第一个为自定义设置的事件,另一个则是唤醒事件 (上一篇文章中有介绍)使其在识别到 “你好小美” 和 “打开灯光” 的时候控制对应的PWM行为。 如果当前PWM输出的线程没有被创建的话, 那么当识别到“你好小美”的时候将启动线程。 当识别到“打开灯光”的时候则根据上述创建线程时的线程号删除线程。
2- 添加编译支持 在 /home/vc02/Downloads/uni_hb_m_solution/unione_lite_app_hb_m/build/user/src/examples 下的 subdir.mk 添加对当前编译文件的引用。
3- 修改/home/vc02/Downloads/uni_hb_m_solution/unione_lite_app_hb_m/user/inc/user_config.h 文件, 增加对应的demo宏支持
4 - 修改/home/vc02/Downloads/uni_hb_m_solution/unione_lite_app_hb_m/user/src/user_main.c 增加对上述自定义宏的支持
5- 编译并且烧录固件
实验现象
默认上电波形如下(不清楚为什么会有毛刺)
使用“你好小美” 唤醒,波形如下
使用“你好小美”唤醒 + “打开灯光” 关闭PWM输出如下
总结
在上文中我们主要结合IO和PWM的example进行了二次开发。其实我们可以发现,无论什么自定义功能,都是首先找到对应的库函数,或者example进行引用。 在明白原理之后定义创建自己的.c 文件,然后再将当前的文件添加到编译的上下文中。 然后在对应的h文件中开启对自定义C文件的宏定义。即可完成自定义的功能的二次开发。 实际上这个SDK的完整度非常高, 代码也非常规范!相信通过这几篇文章你能很快上手。
附件
|