WTN6040 语音芯片使用记录
背景
项目需要, 使用一款语音播放芯片来实现简单的音频播放
手里的项目是ESP32平台的, 但以下驱动代码原理一样, 稍作修改即可移植到其他平台,
需要注意的是, 这款芯片的音频需要提前定制, 也可以自己买工装刷入,
总之音频数据是预置在这颗芯片内的,
主控只需要发送音频地址即可
正文
WTN6040这款IC支持多种通信方式, 具体可参考手册, 但ESP32引脚数量有限, 于是选择一线串口通信就足够了,
原理上和WS2812灯珠的驱动方式一样,
放一个时序图,
/**
* @file wtn6040.c
* @brief wtn6040 driver
* @author William
* @date 2022.5.10
* @version v0.1
*/
#include "wtn6040.h"
#define WTN6040_DATA_IO (42)
#define WTN6040_T1H_US (600)
#define WTN6040_T1L_US (200)
#define WTN6040_T0H_US (200)
#define WTN6040_T0L_US (600)
static const char *TAG = "wtn6040";
/**
* @brief 系统级精确微秒延时
*/
// static __inline void delay_us(int ts)
// {
// uint32_t start, curr;
// ts *= 240; //ts=240时为1us, 这里为方便在内部做微妙凑整处理
// __asm__ __volatile__("rsr %0, ccount" : "=r"(start));
// do
// __asm__ __volatile__("rsr %0, ccount" : "=r"(curr));
// while (curr - start <= ts);
// }
/**
* @brief WTN6040一线串口通信底层函数,必须严格按照时序操作
* @param byte: 一线语音地址或指令,如 00H -> 播放第0段语音
*/
static void wtn6040_1_line(const uint8_t byte)
{
ESP_LOGI(TAG, "Write byte is: 0x%X.", byte);
gpio_set_level(WTN6040_DATA_IO, 0);
vTaskDelay(pdMS_TO_TICKS(8));
for (size_t i = 0; i < 8; i++)
{
// LSB First
if ( byte & (1 << i) )
{
gpio_set_level(WTN6040_DATA_IO, 1);
usleep(WTN6040_T1H_US);
gpio_set_level(WTN6040_DATA_IO, 0);
usleep(WTN6040_T1L_US);
}
else
{
gpio_set_level(WTN6040_DATA_IO, 1);
usleep(WTN6040_T0H_US);
gpio_set_level(WTN6040_DATA_IO, 0);
usleep(WTN6040_T0L_US);
}
}
gpio_set_level(WTN6040_DATA_IO, 1);
vTaskDelay(pdMS_TO_TICKS(4));
}
/**
* @brief 初始化 wtn6040 一线通数据引脚初始化
*/
esp_err_t wtn6040_init(void)
{
gpio_config_t io_conf;
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << WTN6040_DATA_IO);
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
gpio_config(&io_conf);
gpio_set_level(WTN6040_DATA_IO, 0);
vTaskDelay(pdMS_TO_TICKS(100));
ESP_LOGI(TAG, "Initialize wtn6040 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);
// wtn6040_1_line(AUDIO_CLICK); //音量试听
if (vol == 0) {
gpio_set_level(WTN6040_DATA_IO, 0);
vTaskDelay(pdMS_TO_TICKS(50));
}
}
/**
* @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);
}
/**
* @file wtn6040.h
* @author William
* @date 2022.5.10
*/
/* ------------------------------------------------------
| index | brief |
| ----- | ----- |
| 0x00 | 20ms静音 |
| 0x01 | 报警音 |
| 0x02 | 开机音乐 |
| 0x03 | 关机音乐 |
| 0x04 | 安全音效 |
| 0x05 | 按键点击音 |
------------------------------------------------------ */
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_err.h"
#include "esp_log.h"
#include "bsp_board.h"
#include "sys/unistd.h"
#include "driver/rmt.h"
/* 一线语音命令码 */
#define CMD_PLAY_CYCLE (0xF2) //循环播放当前语音:执行此命令可循环播放当前段语音,可在语音播放/语音停止时发送,可被其他指令打断
#define CMD_PLAY_SERIES (0xF3) //连码播放:F3H+语音地址A,F3H+语音地址B,F3H+语音地址C
#define CMD_INSERT_MUTE (0xF8) //插入静音:F8H+静音时间(10MS为单位)
#define CMD_PLAY_STOP (0xFE) //停止播放当前语音
/* 芯片预定义音频地址 */
enum {
AUDIO_MUTE = 0x00, //20ms静音
AUDIO_DANGER, //报警音
AUDIO_WELCOME, //开机欢迎音
AUDIO_BYE, //关机音
AUDIO_SAFE, //安全音
AUDIO_CLICK, //点击音
};
/**
* @brief 初始化 wtn6040 一线通数据引脚
*/
esp_err_t wtn6040_init(void);
/**
* @brief 播放结束或者待机状态发此命令调节音量,E0H音量最小,EFH音量最大,共16级
*/
void wtn6040_volume_adjust(uint8_t vol);
/**
* @brief 播放指定音频或停止播放
* @param index: 16位指令/地址
*/
void wtn6040_audio_set(uint8_t index);
/**
* @brief 测试接口,播放一遍预定义音频
*/
void wtn6040_test(void);
#ifdef __cplusplus
}
#endif