furi_hal_speaker.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. #include <furi_hal_speaker.h>
  2. #include <furi_hal_gpio.h>
  3. #include <furi_hal_resources.h>
  4. #include <furi_hal_power.h>
  5. #include <stm32wbxx_ll_tim.h>
  6. #include <furi_hal_cortex.h>
  7. #define TAG "FuriHalSpeaker"
  8. #define FURI_HAL_SPEAKER_TIMER TIM16
  9. #define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1
  10. #define FURI_HAL_SPEAKER_PRESCALER 500
  11. #define FURI_HAL_SPEAKER_MAX_VOLUME 60
  12. static FuriMutex* furi_hal_speaker_mutex = NULL;
  13. // #define FURI_HAL_SPEAKER_NEW_VOLUME
  14. void furi_hal_speaker_init() {
  15. furi_assert(furi_hal_speaker_mutex == NULL);
  16. furi_hal_speaker_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  17. FURI_CRITICAL_ENTER();
  18. LL_TIM_DeInit(FURI_HAL_SPEAKER_TIMER);
  19. FURI_CRITICAL_EXIT();
  20. FURI_LOG_I(TAG, "Init OK");
  21. }
  22. void furi_hal_speaker_deinit() {
  23. furi_check(furi_hal_speaker_mutex != NULL);
  24. LL_TIM_DeInit(FURI_HAL_SPEAKER_TIMER);
  25. furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
  26. furi_mutex_free(furi_hal_speaker_mutex);
  27. furi_hal_speaker_mutex = NULL;
  28. }
  29. bool furi_hal_speaker_acquire(uint32_t timeout) {
  30. furi_check(!FURI_IS_IRQ_MODE());
  31. if(furi_mutex_acquire(furi_hal_speaker_mutex, timeout) == FuriStatusOk) {
  32. furi_hal_power_insomnia_enter();
  33. furi_hal_gpio_init_ex(
  34. &gpio_speaker, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16);
  35. return true;
  36. } else {
  37. return false;
  38. }
  39. }
  40. void furi_hal_speaker_release() {
  41. furi_check(!FURI_IS_IRQ_MODE());
  42. furi_check(furi_hal_speaker_is_mine());
  43. furi_hal_speaker_stop();
  44. furi_hal_gpio_init(&gpio_speaker, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
  45. furi_hal_power_insomnia_exit();
  46. furi_check(furi_mutex_release(furi_hal_speaker_mutex) == FuriStatusOk);
  47. }
  48. bool furi_hal_speaker_is_mine() {
  49. return (FURI_IS_IRQ_MODE()) ||
  50. (furi_mutex_get_owner(furi_hal_speaker_mutex) == furi_thread_get_current_id());
  51. }
  52. static inline uint32_t furi_hal_speaker_calculate_autoreload(float frequency) {
  53. uint32_t autoreload = (SystemCoreClock / FURI_HAL_SPEAKER_PRESCALER / frequency) - 1;
  54. if(autoreload < 2) {
  55. autoreload = 2;
  56. } else if(autoreload > UINT16_MAX) {
  57. autoreload = UINT16_MAX;
  58. }
  59. return autoreload;
  60. }
  61. static inline uint32_t furi_hal_speaker_calculate_compare(float volume) {
  62. if(volume < 0) volume = 0;
  63. if(volume > 1) volume = 1;
  64. volume = volume * volume * volume;
  65. #ifdef FURI_HAL_SPEAKER_NEW_VOLUME
  66. uint32_t compare_value = volume * FURI_HAL_SPEAKER_MAX_VOLUME;
  67. uint32_t clip_value = volume * LL_TIM_GetAutoReload(FURI_HAL_SPEAKER_TIMER) / 2;
  68. if(compare_value > clip_value) {
  69. compare_value = clip_value;
  70. }
  71. #else
  72. uint32_t compare_value = volume * LL_TIM_GetAutoReload(FURI_HAL_SPEAKER_TIMER) / 2;
  73. #endif
  74. if(compare_value == 0) {
  75. compare_value = 1;
  76. }
  77. return compare_value;
  78. }
  79. void furi_hal_speaker_start(float frequency, float volume) {
  80. furi_check(furi_hal_speaker_is_mine());
  81. if(volume <= 0) {
  82. furi_hal_speaker_stop();
  83. return;
  84. }
  85. LL_TIM_InitTypeDef TIM_InitStruct = {0};
  86. TIM_InitStruct.Prescaler = FURI_HAL_SPEAKER_PRESCALER - 1;
  87. TIM_InitStruct.Autoreload = furi_hal_speaker_calculate_autoreload(frequency);
  88. LL_TIM_Init(FURI_HAL_SPEAKER_TIMER, &TIM_InitStruct);
  89. LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
  90. TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
  91. TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE;
  92. TIM_OC_InitStruct.CompareValue = furi_hal_speaker_calculate_compare(volume);
  93. LL_TIM_OC_Init(FURI_HAL_SPEAKER_TIMER, FURI_HAL_SPEAKER_CHANNEL, &TIM_OC_InitStruct);
  94. LL_TIM_EnableAllOutputs(FURI_HAL_SPEAKER_TIMER);
  95. LL_TIM_EnableCounter(FURI_HAL_SPEAKER_TIMER);
  96. }
  97. void furi_hal_speaker_set_volume(float volume) {
  98. furi_check(furi_hal_speaker_is_mine());
  99. if(volume <= 0) {
  100. furi_hal_speaker_stop();
  101. return;
  102. }
  103. #if FURI_HAL_SPEAKER_CHANNEL == LL_TIM_CHANNEL_CH1
  104. LL_TIM_OC_SetCompareCH1(FURI_HAL_SPEAKER_TIMER, furi_hal_speaker_calculate_compare(volume));
  105. #else
  106. #error Invalid channel
  107. #endif
  108. }
  109. void furi_hal_speaker_stop() {
  110. furi_check(furi_hal_speaker_is_mine());
  111. LL_TIM_DisableAllOutputs(FURI_HAL_SPEAKER_TIMER);
  112. LL_TIM_DisableCounter(FURI_HAL_SPEAKER_TIMER);
  113. }