前言
前期在唯创知音WTN6040 esp32驱动代码这篇文章中介绍了驱动 WTN6040 的原理及demo,使用的是延时方案,
实际测试后发现这种方式偶尔会延时不精准,导致功能异常,
于是就有了下面这种方式 —— 使用 ESP32 原生的 RMT 外设更精准的控制时间。
部分代码
此方式驱动需要进行编码然后发送,分为4个文件,篇幅会很长,仅放一部分
完整的使用代码:ESP32 RMT WTN6040 Driver
/**
* @file wtn6040.c
* @brief wtn6040 driver
* @author William
* @version v0.2
*/
#include "wtn6040.h"
#include "bsp_board.h"
#include "driver/rmt_tx.h"
#include "wtn6_encoder.h"
#define RMT_WTN6_RESOLUTION_HZ 1000000 // 1MHz resolution, 1 tick = 1us
#define WTN6040_DATA_IO (GPIO_RMT_AUDIO)
static const char *TAG = "wtn6040";
rmt_channel_handle_t tx_channel = NULL;
rmt_encoder_handle_t wtn6_encoder = NULL;
// this function won't send frames in a loop
rmt_transmit_config_t transmit_config = {
.loop_count = 0, // no loop
.flags.eot_level = 1,
};
/**
* @brief WTN6040一线串口通信底层函数,必须严格按照时序操作
* @param byte: 一线语音地址或指令,如 00H -> 播放第0段语音
*/
static void wtn6040_1_line(const uint8_t byte)
{
// ESP_LOGI(TAG, "Write byte is: 0x%02X.", byte);
ESP_ERROR_CHECK(rmt_transmit(tx_channel, wtn6_encoder, &byte, sizeof(byte), &transmit_config));
}
/**
* @brief 初始化 wtn6040 一线通数据引脚初始化
*/
esp_err_t wtn6040_init(void)
{
ESP_LOGI(TAG, "create RMT TX channel");
rmt_tx_channel_config_t tx_channel_cfg = {
.clk_src = RMT_CLK_SRC_DEFAULT,
.resolution_hz = RMT_WTN6_RESOLUTION_HZ,
.mem_block_symbols = 64, // amount of RMT symbols that the channel can store at a time
.trans_queue_depth = 1, // number of transactions that allowed to pending in the background, this example won't queue multiple transactions, so queue depth > 1 is sufficient
.gpio_num = WTN6040_DATA_IO,
};
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_channel_cfg, &tx_channel));
ESP_LOGI(TAG, "Install wtn6 encoder");
wtn6_encoder_config_t encoder_config = {
.resolution = RMT_WTN6_RESOLUTION_HZ,
};
ESP_ERROR_CHECK(rmt_new_wtn6_encoder(&encoder_config, &wtn6_encoder));
ESP_LOGI(TAG, "enable RMT TX channel");
ESP_ERROR_CHECK(rmt_enable(tx_channel));
vTaskDelay(pdMS_TO_TICKS(100));
ESP_LOGI(TAG, "Initialize wtn6040 rmt peripheral OK.");
return ESP_OK;
}
/**
* @brief 播放结束或者待机状态发此命令调节音量,E0H音量最小,EFH音量最大,共16级
* @param vol: range = 0x00~0x0f
*/
void wtn6040_volume_adjust(uint8_t vol)
{
uint8_t volume;
if (vol > 0x0F) {
ESP_LOGE(TAG, "wtn6040 volume must be 0~15, your input is %d, it`s over range!", vol);
return;
}
volume = 0xE0 + vol;
wtn6040_1_line(volume);
ESP_LOGI(TAG, "Set volume level %d.", vol);
}
/**
* @brief 播放指定音频或停止播放
* @param index: 16位指令/地址
*/
void wtn6040_audio_set(const uint8_t index)
{
switch (index)
{
case AUDIO_MUTE:
ESP_LOGI(TAG, "Play AUDIO_MUTE.");
wtn6040_1_line(AUDIO_MUTE);
break;
case AUDIO_WELCOME:
ESP_LOGI(TAG, "Play AUDIO_WELCOME.");
wtn6040_1_line(AUDIO_WELCOME);
break;
case AUDIO_CLICK:
ESP_LOGI(TAG, "Play AUDIO_CLICK.");
wtn6040_1_line(AUDIO_CLICK);
break;
case AUDIO_SAFE:
ESP_LOGI(TAG, "Play AUDIO_SAFE.");
wtn6040_1_line(AUDIO_SAFE);
break;
case AUDIO_DANGER:
ESP_LOGI(TAG, "Play AUDIO_DANGER.");
wtn6040_1_line(AUDIO_DANGER);
wtn6040_1_line(CMD_PLAY_CYCLE);
break;
case AUDIO_BYE:
ESP_LOGI(TAG, "Play AUDIO_BYE.");
wtn6040_1_line(AUDIO_BYE);
break;
default:
ESP_LOGI(TAG, "Stop play audio.");
wtn6040_1_line(CMD_PLAY_STOP);
break;
}
}
void wtn6040_test(void)
{
ESP_LOGI(TAG, "Running test code about wtn6040 audio control.");
wtn6040_1_line(CMD_PLAY_SERIES);
wtn6040_1_line(AUDIO_DANGER);
wtn6040_1_line(CMD_PLAY_SERIES);
wtn6040_1_line(AUDIO_WELCOME);
wtn6040_1_line(CMD_PLAY_SERIES);
wtn6040_1_line(AUDIO_BYE);
wtn6040_1_line(CMD_PLAY_SERIES);
wtn6040_1_line(AUDIO_SAFE);
wtn6040_1_line(CMD_PLAY_SERIES);
wtn6040_1_line(AUDIO_CLICK);
}