Voice driver

This commit is contained in:
muzkr committed 2025-10-29 03:23:03 +08:00
1 parent 554315bab5
commit bf31740faa
7 files changed
+296 -102

No files matched your search

+1 -1
View File
@@ -106,7 +106,7 @@ enable_feature(ENABLE_AIRCOPY
)
enable_feature(ENABLE_NOAA)
enable_feature(ENABLE_VOICE
driver/dac.c
driver/voice.c
)
enable_feature(ENABLE_VOX)
enable_feature(ENABLE_ALARM)
+97 -44
View File
@@ -1,4 +1,5 @@
/* Copyright 2023 Dual Tachyon
/* Copyright 2025 muzkr https://github.com/muzkr
* Copyright 2023 Dual Tachyon
* https://github.com/DualTachyon
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,6 +26,8 @@
#include "driver/gpio.h"
#include "driver/system.h"
#include "driver/systick.h"
#include "driver/voice.h"
#include "driver/py25q16.h"
#include "functions.h"
#include "misc.h"
#include "settings.h"
@@ -198,31 +201,10 @@ void AUDIO_PlayBeep(BEEP_Type_t Beep)
#ifdef ENABLE_VOICE
static const uint8_t VoiceClipLengthChinese[58] =
{
0x32, 0x32, 0x32, 0x37, 0x37, 0x32, 0x32, 0x32,
0x32, 0x37, 0x37, 0x32, 0x64, 0x64, 0x64, 0x64,
0x64, 0x69, 0x64, 0x69, 0x5A, 0x5F, 0x5F, 0x64,
0x64, 0x69, 0x64, 0x64, 0x69, 0x69, 0x69, 0x64,
0x64, 0x6E, 0x69, 0x5F, 0x64, 0x64, 0x64, 0x69,
0x69, 0x69, 0x64, 0x69, 0x64, 0x64, 0x55, 0x5F,
0x5A, 0x4B, 0x4B, 0x46, 0x46, 0x69, 0x64, 0x6E,
0x5A, 0x64,
};
static const uint8_t VoiceClipLengthEnglish[76] =
{
0x50, 0x32, 0x2D, 0x2D, 0x2D, 0x37, 0x37, 0x37,
0x32, 0x32, 0x3C, 0x37, 0x46, 0x46, 0x4B, 0x82,
0x82, 0x6E, 0x82, 0x46, 0x96, 0x64, 0x46, 0x6E,
0x78, 0x6E, 0x87, 0x64, 0x96, 0x96, 0x46, 0x9B,
0x91, 0x82, 0x82, 0x73, 0x78, 0x64, 0x82, 0x6E,
0x78, 0x82, 0x87, 0x6E, 0x55, 0x78, 0x64, 0x69,
0x9B, 0x5A, 0x50, 0x3C, 0x32, 0x55, 0x64, 0x64,
0x50, 0x46, 0x46, 0x46, 0x4B, 0x4B, 0x50, 0x50,
0x55, 0x4B, 0x4B, 0x32, 0x32, 0x32, 0x32, 0x37,
0x41, 0x32, 0x3C, 0x37,
};
uint16_t gVoiceBuf[VOICE_BUF_CAP][VOICE_BUF_LEN];
uint8_t gVoiceBufReadIndex = 0;
uint8_t gVoiceBufWriteIndex = 0;
uint8_t gVoiceBufLen = 0;
VOICE_ID_t gVoiceID[8];
uint8_t gVoiceReadIndex;
@@ -231,29 +213,100 @@ volatile uint16_t gCountdownToPlayNextVoice_10ms;
volatile bool gFlagPlayQueuedVoice;
VOICE_ID_t gAnotherVoiceID = VOICE_ID_INVALID;
static void AUDIO_PlayVoice(uint8_t VoiceID)
static const uint16_t VOICE_SAMPLES[256] =
{
unsigned int i;
0x06a8, 0x06b8, 0x0688, 0x0698, 0x06e8, 0x06f8, 0x06c8, 0x06d8, //
0x0628, 0x0638, 0x0608, 0x0618, 0x0668, 0x0678, 0x0648, 0x0658, //
0x0754, 0x075c, 0x0744, 0x074c, 0x0774, 0x077c, 0x0764, 0x076c, //
0x0714, 0x071c, 0x0704, 0x070c, 0x0734, 0x073c, 0x0724, 0x072c, //
0x02a0, 0x02e0, 0x0220, 0x0260, 0x03a0, 0x03e0, 0x0320, 0x0360, //
0x00a0, 0x00e0, 0x0020, 0x0060, 0x01a0, 0x01e0, 0x0120, 0x0160, //
0x0550, 0x0570, 0x0510, 0x0530, 0x05d0, 0x05f0, 0x0590, 0x05b0, //
0x0450, 0x0470, 0x0410, 0x0430, 0x04d0, 0x04f0, 0x0490, 0x04b0, //
0x07ea, 0x07eb, 0x07e8, 0x07e9, 0x07ee, 0x07ef, 0x07ec, 0x07ed, //
0x07e2, 0x07e3, 0x07e0, 0x07e1, 0x07e6, 0x07e7, 0x07e4, 0x07e5, //
0x07fa, 0x07fb, 0x07f8, 0x07f9, 0x07fe, 0x07ff, 0x07fc, 0x07fd, //
0x07f2, 0x07f3, 0x07f0, 0x07f1, 0x07f6, 0x07f7, 0x07f4, 0x07f5, //
0x07aa, 0x07ae, 0x07a2, 0x07a6, 0x07ba, 0x07be, 0x07b2, 0x07b6, //
0x078a, 0x078e, 0x0782, 0x0786, 0x079a, 0x079e, 0x0792, 0x0796, //
0x07d5, 0x07d7, 0x07d1, 0x07d3, 0x07dd, 0x07df, 0x07d9, 0x07db, //
0x07c5, 0x07c7, 0x07c1, 0x07c3, 0x07cd, 0x07cf, 0x07c9, 0x07cb, //
0x0958, 0x0948, 0x0978, 0x0968, 0x0918, 0x0908, 0x0938, 0x0928, //
0x09d8, 0x09c8, 0x09f8, 0x09e8, 0x0998, 0x0988, 0x09b8, 0x09a8, //
0x08ac, 0x08a4, 0x08bc, 0x08b4, 0x088c, 0x0884, 0x089c, 0x0894, //
0x08ec, 0x08e4, 0x08fc, 0x08f4, 0x08cc, 0x08c4, 0x08dc, 0x08d4, //
0x0d60, 0x0d20, 0x0de0, 0x0da0, 0x0c60, 0x0c20, 0x0ce0, 0x0ca0, //
0x0f60, 0x0f20, 0x0fe0, 0x0fa0, 0x0e60, 0x0e20, 0x0ee0, 0x0ea0, //
0x0ab0, 0x0a90, 0x0af0, 0x0ad0, 0x0a30, 0x0a10, 0x0a70, 0x0a50, //
0x0bb0, 0x0b90, 0x0bf0, 0x0bd0, 0x0b30, 0x0b10, 0x0b70, 0x0b50, //
0x0815, 0x0814, 0x0817, 0x0816, 0x0811, 0x0810, 0x0813, 0x0812, //
0x081d, 0x081c, 0x081f, 0x081e, 0x0819, 0x0818, 0x081b, 0x081a, //
0x0805, 0x0804, 0x0807, 0x0806, 0x0801, 0x0800, 0x0803, 0x0802, //
0x080d, 0x080c, 0x080f, 0x080e, 0x0809, 0x0808, 0x080b, 0x080a, //
0x0856, 0x0852, 0x085e, 0x085a, 0x0846, 0x0842, 0x084e, 0x084a, //
0x0876, 0x0872, 0x087e, 0x087a, 0x0866, 0x0862, 0x086e, 0x086a, //
0x082b, 0x0829, 0x082f, 0x082d, 0x0823, 0x0821, 0x0827, 0x0825, //
0x083b, 0x0839, 0x083f, 0x083d, 0x0833, 0x0831, 0x0837, 0x0835 //
};
GPIO_SetBit(&GPIOA->DATA, GPIOA_PIN_VOICE_0);
SYSTEM_DelayMs(20);
GPIO_ClearBit(&GPIOA->DATA, GPIOA_PIN_VOICE_0);
static struct
{
uint32_t Addr;
uint32_t Size;
} VoiceClipState = {0};
for (i = 0; i < 8; i++)
static bool LoadVoiceClip(uint8_t VoiceID)
{
if (VoiceID >= VOICE_ID_END)
{
if ((VoiceID & 0x80U) == 0)
GPIO_ClearBit(&GPIOA->DATA, GPIOA_PIN_VOICE_1);
else
GPIO_SetBit(&GPIOA->DATA, GPIOA_PIN_VOICE_1);
SYSTICK_DelayUs(1000);
GPIO_SetBit(&GPIOA->DATA, GPIOA_PIN_VOICE_0);
SYSTICK_DelayUs(1200);
GPIO_ClearBit(&GPIOA->DATA, GPIOA_PIN_VOICE_0);
VoiceID <<= 1;
SYSTICK_DelayUs(200);
return false;
}
uint32_t Addr = gEeprom.VOICE_PROMPT == VOICE_PROMPT_CHINESE ? 0x14c000 : 0x14c800;
struct
{
uint32_t Offset;
uint32_t Size;
} Info;
PY25Q16_ReadBuffer(Addr + 8 * VoiceID, &Info, 8);
if (Info.Offset > 0x0b0000 || Info.Size > 0x019000)
{
return false;
}
VoiceClipState.Addr = 0x14d000 + Info.Offset;
VoiceClipState.Size = Info.Size;
return true;
}
static inline uint32_t CalcDelay(uint32_t Size)
{
return 2 * Size; // in ms!!
}
static void LoadVoiceSamples() {
if (0 == VoiceClipState.Addr || 0 == VoiceClipState.Size)
{
return;
}
if (gVoiceBufLen >= VOICE_BUF_CAP)
{
return;
}
extern uint8_t **gFrameBuffer;
uint8_t *Buf = (uint8_t *)gFrameBuffer;
PY25Q16_ReadBuffer(VoiceClipState.Addr, Buf, VOICE_BUF_LEN);
VoiceClipState.Addr += VOICE_BUF_LEN;
VoiceClipState.Size -= VOICE_BUF_LEN;
for (uint32_t i = 0; i < VOICE_BUF_LEN; i++)
{
gVoiceBuf[gVoiceBufWriteIndex][i] = VOICE_SAMPLES[Buf[i]];
}
VOCIE_BUF_ForwardWriteIndex();
gVoiceBufLen++;
}
void AUDIO_PlaySingleVoice(bool bFlag)
+2 -2
View File
@@ -25,7 +25,7 @@
#include "py32f071_ll_gpio.h"
#include "py32f071_ll_rcc.h"
#include "py32f071_ll_adc.h"
#include "driver/dac.h"
#include "driver/voice.h"
#include "driver/backlight.h"
#ifdef ENABLE_FMRADIO
#include "driver/bk1080.h"
@@ -180,7 +180,7 @@ void BOARD_Init(void)
BACKLIGHT_InitHardware();
BOARD_ADC_Init();
#ifdef ENABLE_VOICE
DAC_Init();
VOICE_Init();
#endif
PY25Q16_Init();
ST7565_Init();
-50
View File
@@ -1,50 +0,0 @@
/* Copyright 2025 muzkr https://github.com/muzkr
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "driver/dac.h"
#include "driver/systick.h"
#include "py32f071_ll_bus.h"
#include "py32f071_ll_gpio.h"
#include "py32f071_ll_dac.h"
#include "py32f071_ll_tim.h"
#define TIMx TIM6
#define DAC_CHANNEL LL_DAC_CHANNEL_1
static inline void TIM_Init()
{
LL_TIM_SetPrescaler(TIMx, 3);
LL_TIM_SetAutoReload(TIMx, 1499);
LL_TIM_SetTriggerOutput(TIMx, LL_TIM_TRGO_UPDATE);
}
void DAC_Init()
{
// Channel 1: PA4
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_4, LL_GPIO_MODE_ANALOG);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_DAC1);
LL_DAC_SetTriggerSource(DAC1, DAC_CHANNEL, LL_DAC_TRIG_EXT_TIM6_TRGO);
LL_DAC_SetOutputBuffer(DAC1, DAC_CHANNEL, LL_DAC_OUTPUT_BUFFER_ENABLE);
LL_DAC_EnableDMAReq(DAC1, DAC_CHANNEL);
LL_DAC_Enable(DAC1, DAC_CHANNEL);
SYSTICK_DelayUs(15);
TIM_Init();
LL_DAC_EnableTrigger(DAC1, DAC_CHANNEL);
}
+169
View File
@@ -0,0 +1,169 @@
/* Copyright 2025 muzkr https://github.com/muzkr
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "driver/voice.h"
#include "driver/systick.h"
#include "py32f071_ll_bus.h"
#include "py32f071_ll_gpio.h"
#include "py32f071_ll_dac.h"
#include "py32f071_ll_tim.h"
#include "py32f071_ll_dma.h"
#include "py32f071_ll_system.h"
#include <string.h>
#define TIMx TIM6
#define DAC_CHANNEL LL_DAC_CHANNEL_1
#define DMA_CHANNEL LL_DMA_CHANNEL_3
#define VOICE_BUF_SIZE (sizeof(uint16_t) * VOICE_BUF_LEN)
static uint16_t DAC_Buf[VOICE_BUF_LEN * 2];
static inline void DMA_Init()
{
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_SYSCFG);
LL_SYSCFG_SetDMARemap(DMA1, DMA_CHANNEL, LL_SYSCFG_DMA_MAP_DAC1);
LL_DMA_InitTypeDef InitStruct;
// LL_DMA_StructInit( &InitStruct) ;
InitStruct.PeriphOrM2MSrcAddress = (uint32_t)DAC_Buf;
InitStruct.MemoryOrM2MDstAddress = LL_DAC_DMA_GetRegAddr(DAC1, DAC_CHANNEL, LL_DAC_DMA_REG_DATA_12BITS_RIGHT_ALIGNED);
InitStruct.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
InitStruct.Mode = LL_DMA_MODE_CIRCULAR;
InitStruct.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
InitStruct.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
InitStruct.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD;
InitStruct.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD;
InitStruct.NbData = sizeof(DAC_Buf) / sizeof(uint16_t);
InitStruct.Priority = LL_DMA_PRIORITY_HIGH;
LL_DMA_Init(DMA1, DMA_CHANNEL, &InitStruct);
LL_DMA_EnableIT_HT(DMA1, DMA_CHANNEL);
LL_DMA_EnableIT_TC(DMA1, DMA_CHANNEL);
LL_DMA_EnableChannel(DMA1, DMA_CHANNEL);
NVIC_SetPriority(DMA1_Channel2_3_IRQn, 3);
NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
}
static inline void TIM_Init()
{
// Update freq = 48 MHz / 4 / 1500 == 8 KHz
LL_TIM_SetPrescaler(TIMx, 3);
LL_TIM_SetAutoReload(TIMx, 1499);
LL_TIM_SetTriggerOutput(TIMx, LL_TIM_TRGO_UPDATE);
}
void VOICE_Init()
{
DMA_Init();
// Channel 1: PA4
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_4, LL_GPIO_MODE_ANALOG);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_DAC1);
LL_DAC_SetTriggerSource(DAC1, DAC_CHANNEL, LL_DAC_TRIG_EXT_TIM6_TRGO);
LL_DAC_SetOutputBuffer(DAC1, DAC_CHANNEL, LL_DAC_OUTPUT_BUFFER_ENABLE);
LL_DAC_EnableDMAReq(DAC1, DAC_CHANNEL);
LL_DAC_Enable(DAC1, DAC_CHANNEL);
SYSTICK_DelayUs(15);
TIM_Init();
LL_DAC_EnableTrigger(DAC1, DAC_CHANNEL);
}
void VOICE_Start()
{
LL_DAC_Enable(DAC1, DAC_CHANNEL);
LL_TIM_DisableCounter(TIMx);
if (gVoiceBufLen > 0)
{
memcpy(DAC_Buf, gVoiceBuf[gVoiceBufReadIndex], VOICE_BUF_SIZE);
VOCIE_BUF_ForwardReadIndex();
gVoiceBufLen--;
}
else
{
memset(DAC_Buf, 0, VOICE_BUF_SIZE);
}
if (gVoiceBufLen > 0)
{
memcpy(DAC_Buf + VOICE_BUF_SIZE, //
gVoiceBuf[gVoiceBufReadIndex], //
VOICE_BUF_SIZE //
);
VOCIE_BUF_ForwardReadIndex();
gVoiceBufLen--;
}
else
{
memset(DAC_Buf + VOICE_BUF_SIZE, 0, VOICE_BUF_SIZE);
}
LL_DMA_ConfigAddresses(DMA1, DMA_CHANNEL, DAC_Buf, //
LL_DAC_DMA_GetRegAddr(DAC1, DAC_CHANNEL, LL_DAC_DMA_REG_DATA_12BITS_RIGHT_ALIGNED), //
LL_DMA_DIRECTION_MEMORY_TO_PERIPH //
);
LL_DMA_EnableChannel(DMA1, DMA_CHANNEL);
LL_TIM_EnableCounter(TIMx);
}
void VOICE_Stop()
{
LL_TIM_DisableCounter(TIMx);
LL_DMA_DisableChannel(DMA1, DMA_CHANNEL);
LL_DAC_Disable(DAC1, DAC_CHANNEL);
}
void DMA1_Channel2_3_IRQHandler()
{
if (LL_DMA_IsActiveFlag_HT3(DMA1))
{
LL_DMA_ClearFlag_HT3(DMA1);
if (gVoiceBufLen > 0)
{
memcpy(DAC_Buf, gVoiceBuf[gVoiceBufReadIndex], VOICE_BUF_SIZE);
VOCIE_BUF_ForwardReadIndex();
gVoiceBufLen--;
}
else
{
memset(DAC_Buf, 0, VOICE_BUF_SIZE);
}
}
if (LL_DMA_IsActiveFlag_TC3(DMA1))
{
LL_DMA_ClearFlag_TC3(DMA1);
if (gVoiceBufLen > 0)
{
memcpy(DAC_Buf + VOICE_BUF_SIZE, //
gVoiceBuf[gVoiceBufReadIndex], //
VOICE_BUF_SIZE //
);
VOCIE_BUF_ForwardReadIndex();
gVoiceBufLen--;
}
else
{
memset(DAC_Buf + VOICE_BUF_SIZE, 0, VOICE_BUF_SIZE);
}
}
}
+26 -4
View File
@@ -13,9 +13,31 @@
* limitations under the License.
*/
#ifndef DRIVER_DAC_H
#define DRIVER_DAC_H
#ifndef DRIVER_VOICE_H
#define DRIVER_VOICE_H
void DAC_Init();
#include <stdint.h>
#endif // DRIVER_DAC_H
#define VOICE_BUF_CAP 4
#define VOICE_BUF_LEN 160
extern uint16_t gVoiceBuf[VOICE_BUF_CAP][VOICE_BUF_LEN];
extern uint8_t gVoiceBufReadIndex;
extern uint8_t gVoiceBufWriteIndex;
extern uint8_t gVoiceBufLen;
static inline void VOCIE_BUF_ForwardReadIndex()
{
gVoiceBufReadIndex = (1 + gVoiceBufReadIndex) % VOICE_BUF_CAP;
}
static inline void VOCIE_BUF_ForwardWriteIndex()
{
gVoiceBufWriteIndex = (1 + gVoiceBufWriteIndex) % VOICE_BUF_CAP;
}
void VOICE_Init();
void VOICE_Start();
void VOICE_Stop();
#endif // DRIVER_VOICE_H
+1 -1
View File
@@ -76,7 +76,7 @@
"name": "Custom",
"inherits": "Broadcast",
"cacheVariables": {
"ENABLE_VOICE": false,
"ENABLE_VOICE": true,
"ENABLE_SWD": true,
"ENABLE_AIRCOPY": false,
"EDITION_STRING": "Custom",