发帖
0 0 0

安信可离线语音模组 VC-01、VC-02 系列教程 【二次开发篇】PWM输出

WangChong
论坛元老

83

主题

863

回帖

1万

积分

论坛元老

积分
11848
VC系列 20 0 昨天 23:31
本帖最后由 WangChong 于 2025-5-23 03:24 编辑

简介


在上一篇文章中我们对SDK的GPIO输出进行了控制, 在上一章节的基础上我们将在本章节对PWM的输出进行控制(如果没有看过上一篇文章的,建议先去看上一篇文章, 对组件和设计的库函数进行了介绍。本章节不多做赘述。)SDK文件还是使用的上一章节的SDK(如无特殊说明,SDK不变)。


默认PWM DEMO输出现象

首先,在user_config.h 文件中开启对PWMLED的宏, 使其用户代码默认烧录USER_DEMO_PWM_LED的例程
360截图20250522221746.png

根据对应的hb_pwm_led.c 得知, 其线程启动后,主要是控制PWM_NUM_1_A27进行输出

  1. #define PWM_LED_GPIO_NUM  PWM_NUM_1_A27   // "MOSI" on HB-M demo board
复制代码


首先我们对这个默认的Demo进行编译,并且下载到VC02中。  并且将示波器连接到开发板背部的GPIOA27上, 那么此时示波器的波形如下所示

微信图片_20250522222247.jpg

占空比缓慢变化

微信图片_20250522222252.jpg

此时默认的出厂PWM demo已经测试完毕。  如果想控制对应的PWM输出的话, 可以参考user_pwm.h 中的函数定义。 函数声明如下所示。

  1. #ifndef USER_INC_USER_PWM_H_
  2. #define USER_INC_USER_PWM_H_
  3.   
  4. #ifdef __cplusplus
  5.   extern "C" {
  6. #endif
  7.   
  8. #include "unione.h"

  9. /** @ingroup uni_pwm_def
  10. * PWM管脚号
  11. */
  12. typedef enum {
  13.   PWM_NUM_1_A27 = 0,
  14.   PWM_NUM_1_B0,
  15.   PWM_NUM_1_B2,      ///< PWM 1 only 1 pin work a time
  16.   PWM_NUM_2_A28,     ///< used for PA enable, don't use it on HB-M demo board
  17.   PWM_NUM_2_B1,
  18.   PWM_NUM_2_B3,      ///< PWM 2 only 1 pin work a time
  19.   PWM_NUM_MAX
  20. }USER_PWM_NUM;

  21. /** @addtogroup uni_pwm_inf
  22. @{*/

  23. /**
  24. *@brief PWM初始化
  25. *@param num PWM管脚号
  26. *@param hz 频率
  27. *@param is_high_duty \a TRUE :占空比用高电平持续时间计算; \a FALSE :占空比用低电平持续时间计算
  28. *@retval 0  操作成功
  29. *@retval -1 操作失败
  30. */
  31. int user_pwm_init(USER_PWM_NUM num, uni_u32 hz, uni_bool is_high_duty);

  32. /**
  33. *@brief PWM反初始化
  34. *@param num PWM管脚号
  35. *@retval 0  操作成功
  36. *@retval -1 操作失败
  37. */
  38. int user_pwm_final(USER_PWM_NUM num);

  39. /**
  40. *@brief 开使PWM输出
  41. *@param num PWM管脚号
  42. *@param duty 占空比
  43. *@retval 0  操作成功
  44. *@retval -1 操作失败
  45. */
  46. int user_pwm_start(USER_PWM_NUM num, uni_u8 duty);

  47. /**
  48. *@brief 停止PWM输出
  49. *@param num PWM管脚号
  50. *@retval 0  操作成功
  51. *@retval -1 操作失败
  52. */
  53. int user_pwm_stop(USER_PWM_NUM num);

  54. /**
  55. *@brief 暂停PWM输出
  56. *@param num PWM管脚号
  57. *@retval 0  操作成功
  58. *@retval -1 操作失败
  59. */
  60. int user_pwm_pause(USER_PWM_NUM num);

  61. /**
  62. *@brief 恢复PWM输出
  63. *@param num PWM管脚号
  64. *@retval 0  操作成功
  65. *@retval -1 操作失败
  66. */
  67. int user_pwm_resume(USER_PWM_NUM num);

  68. /**
  69. *@brief PWM占空比切换
  70. *@param num PWM管脚号
  71. *@param duty 占空比
  72. *@retval 0  操作成功
  73. *@retval -1 操作失败
  74. */
  75. int user_pwm_change_duty(USER_PWM_NUM num, uni_u8 duty);

  76. /**
  77. *@brief PWM占空比增加
  78. *@param num PWM管脚号
  79. *@param ch_duty 增加的占空比
  80. *@retval 0  操作成功
  81. *@retval -1 操作失败
  82. */
  83. int user_pwm_duty_inc(USER_PWM_NUM num, uni_u8 ch_duty);

  84. /**
  85. *@brief PWM占空比减小
  86. *@param num PWM管脚号
  87. *@param ch_duty 减小的占空比
  88. *@retval 0  操作成功
  89. *@retval -1 操作失败
  90. */
  91. int user_pwm_duty_dec(USER_PWM_NUM num, uni_u8 ch_duty);

  92. /** @}*/

  93. #ifdef __cplusplus
  94. }
  95. #endif

  96. #endif
复制代码



自定义控制命令

接下来我们尝试使用唤醒命令 “你好小美” 来启动这个PWM进程, 然后使用 “打开台灯” 命令来删除这个进程。 从而控制PWM进行输出。 由于官方的SDK写的非常规范, 所以我建议凡是个人相关的代码都可以写在example目录下。


1- 首先我们在example 目录下创建hb_user_pwm_testing.c 文件

代码如下所示
  1. #include "user_gpio.h"
  2. #include "user_pwm_led.h"
  3. #include "user_event.h"
  4. #include "user_player.h"
  5. #include "user_config.h"


  6. #define PWM_LED_GPIO_NUM  PWM_NUM_1_A27   // "MOSI" on HB-M demo board

  7. static uni_pthread_t g_pwm_thread_id = 0;
  8. static bool g_pwm_thread_running = false;

  9. // Thread function to update LED brightness levels
  10. static void _pwm_led_process(void *args) {
  11.   LED_BRIGHT_LEVEL level = BRIGHT_LEVEL_0;
  12.   g_pwm_thread_running = true;

  13.   while (g_pwm_thread_running) {
  14.     user_pwm_led_set_brightness(PWM_LED_GPIO_NUM, level);
  15.     level += 1;
  16.     if (level >= BRIGHT_LEVEL_MAX) {
  17.       level = BRIGHT_LEVEL_0;
  18.     }
  19.     // uni_sleep(1); // Optional: slow down effect
  20.   }
  21. }

  22. // Create PWM LED thread
  23. static Result _create_pwm_led_thread(void) {
  24.   if (g_pwm_thread_running) {
  25.     return E_OK;
  26.   }

  27.   thread_param param;
  28.   uni_memset(&param, 0, sizeof(param));
  29.   param.stack_size = STACK_SMALL_SIZE;
  30.   param.priority = OS_PRIORITY_LOW;
  31.   uni_strncpy(param.task_name, "pwm_led", sizeof(param.task_name) - 1);

  32.   if (0 != uni_pthread_create(&g_pwm_thread_id, &param, _pwm_led_process, NULL)) {
  33.     return E_FAILED;
  34.   }

  35.   uni_pthread_detach(g_pwm_thread_id);  // Auto cleanup
  36.   return E_OK;
  37. }

  38. // Stop PWM thread
  39. static void _stop_pwm_led_thread(void) {
  40.   if (!g_pwm_thread_running) {
  41.     return;
  42.   }

  43.   g_pwm_thread_running = false;

  44.   // Destroy thread if supported (may be platform-specific)
  45.   if (g_pwm_thread_id != 0) {
  46.     uni_pthread_destroy(g_pwm_thread_id);  // Hard stop
  47.     g_pwm_thread_id = 0;
  48.   }

  49.   user_pwm_led_set_brightness(PWM_LED_GPIO_NUM, BRIGHT_LEVEL_0);  // Turn off LED
  50. }

  51. // Callback on voice command
  52. static void _on_wakeup_cmd_cb(USER_EVENT_TYPE event, user_event_context_t *context) {
  53.   if (context == NULL) return;

  54.   event_goto_awakend_t *awake = &context->goto_awakend;

  55.   if (strcmp(awake->cmd, "wakeup_uni") == 0) {
  56.     _create_pwm_led_thread();
  57.     user_player_reply_list_random(awake->reply_files);
  58.   }
  59. }

  60. static void _custom_setting_cb(USER_EVENT_TYPE event,
  61.                                user_event_context_t *context) {
  62.   event_custom_setting_t *setting = NULL;
  63.   if (context) {
  64.     setting = &context->custom_setting;
  65.     if (strcmp(setting->cmd, "TurnOn") == 0) {
  66.        _stop_pwm_led_thread();
  67.        user_player_reply_list_random(setting->reply_files);
  68.     }
  69.   }
  70. }

  71. // Register event callback
  72. static void _register_event_callback(void) {
  73.   user_event_subscribe_event(USER_GOTO_AWAKENED, _on_wakeup_cmd_cb);
  74.   user_event_subscribe_event(USER_CUSTOM_SETTING, _custom_setting_cb);
  75. }

  76. // Main entry
  77. int hb_user_pwm_testing(void) {
  78.   if (0 != user_pwm_led_init(PWM_LED_GPIO_NUM)) {
  79.     return -1;
  80.   }

  81.   _register_event_callback();
  82.   return 0;
  83. }
复制代码


上述代码的主要功能是定义两个用户事件 ,第一个为自定义设置的事件,另一个则是唤醒事件 (上一篇文章中有介绍)使其在识别到 “你好小美” 和 “打开灯光” 的时候控制对应的PWM行为。 如果当前PWM输出的线程没有被创建的话, 那么当识别到“你好小美”的时候将启动线程。 当识别到“打开灯光”的时候则根据上述创建线程时的线程号删除线程。




2- 添加编译支持 在 /home/vc02/Downloads/uni_hb_m_solution/unione_lite_app_hb_m/build/user/src/examples 下的 subdir.mk 添加对当前编译文件的引用。

360截图20250522232232.png



3- 修改/home/vc02/Downloads/uni_hb_m_solution/unione_lite_app_hb_m/user/inc/user_config.h 文件, 增加对应的demo宏支持

Snipaste_2025-05-23_03-23-13.png


4 - 修改/home/vc02/Downloads/uni_hb_m_solution/unione_lite_app_hb_m/user/src/user_main.c 增加对上述自定义宏的支持

360截图20250522232457.png


5- 编译并且烧录固件

360截图20250522232534.png


实验现象

默认上电波形如下(不清楚为什么会有毛刺)


微信图片_20250522232632.jpg

使用“你好小美” 唤醒,波形如下


微信图片_20250522232729.jpg

使用“你好小美”唤醒 + “打开灯光” 关闭PWM输出如下

微信图片_20250522232843.jpg


总结

在上文中我们主要结合IO和PWM的example进行了二次开发。其实我们可以发现,无论什么自定义功能,都是首先找到对应的库函数,或者example进行引用。 在明白原理之后定义创建自己的.c 文件,然后再将当前的文件添加到编译的上下文中。 然后在对应的h文件中开启对自定义C文件的宏定义。即可完成自定义的功能的二次开发。 实际上这个SDK的完整度非常高, 代码也非常规范!相信通过这几篇文章你能很快上手。




附件
上传的附件: uni_app_release_update.zip (882.62 KB, 下载次数: 0)



──── 0人觉得很赞 ────

使用道具 举报

您需要登录后才可以回帖 立即登录
高级模式
返回
统计信息
  • 会员数: 28792 个
  • 话题数: 41609 篇