4.22 LD3320——语音识别模块(来自 TaurusHard 的贡献)
模块来源
资料下载: 链接:https://pan.baidu.com/s/1bgYoX5nbZDvR1CWELzbopw?pwd=8889 提取码:8889
规格参数
工作电压:3.3V~5V
工作温度:-40℃~85℃
引脚数量:9(VCC、GND、IRQ、WR、RST、CS、SCK、MO、MI)
通信方式:SPI
模块尺寸:57mm x 27.5mm
模块原理
模块简介
LD3320 是一款基于非特定人语音识别(SI-ASR:Speaker-IndependentAutomatic Speech Recognition)技术的语音识别/声控芯片,由 ICRoute 公司设计生产。该芯片集成了语音识别处理器和一些外部电路,包括 AD、DA 转换器、麦克风接口、声音输出接口等。本芯片不需要外接任何的辅助芯片如 Flash、RAM 等,直接集成在现有的产品中即可以实现语音识别/声控/人机对话功能,且识别的关键词语列表是可以任意动态编辑的。主要特色功能如下:
① 非特定人语音识别技术:不需要用户进行录音训练;
② 可动态编辑的识别关键词语列表:只需要把识别的关键词语以字符串的形式传送进芯片,即可以在下次识别中立即生效。比如,用户在 51 等 MCU 的编程中,简单地通过设置芯片的寄存器,把诸如“你好”这样的识别关键词的内容动态地传入芯片中,芯片就可以识别这样设定的关键词语了;
③ 真正单芯片解决方案:不需要任何外接的辅助 Flash 和 RAM,真正降低系统成本;
④ 内置高精度 A/D 和 D/A 通道:不需要外接 AD 芯片,只需要把麦克风接在芯片的 AD 引脚上;可以播放声音文件,并提供 550mW 的内置放大器;
⑤ 高准确度和实用的语音识别效果;
⑥ 支持用户自由编辑 50 条关键词语:在同一时刻,最多在 50 条关键词语中进行识别,终端用户可以根据场景需要,随时编辑和更新这 50 条关键词语的内容。
操作时序
本芯片的各种操作,都必须通过寄存器的操作来完成。比如设置标志位、读取状态、向 FIFO 写入数据等。寄存器读写操作有 4 种方式,即并行方式(软、硬)和串行 SPI 方式(软、硬)。 本例使用的模块为 SPI 版本,软件使用模拟 SPI 实现。
并行方式
芯片第 46 脚(MD)接低电平时按照此方式工作。写和读的时序图如下:
并行方式写时序
并行方式读时序
由时序图可以看到,A0 负责通知芯片是数据段还是地址段。A0 为高时是地址,而 A0 为低时是数据。发送地址时 CSB 和 WRB 必须有效,写数据时同样 CSB 和 WRB 必须有效,而读数据时 CSB 和 RDB 必须有效。
串行 SPI 方式
芯片第 46 脚(MD)接高电平且第 42 脚(SPIS*)接地时按照此方式工作。写时序要求先给 SDI 发送一个 “写”指令(04H),然后给 SDI 发送 8 位寄存器地址,再给 SDI 发送 8 位数据。在这期间,SCS 必须保持在有效(低电平);读时序要求先给 SDI 发送一个 “读”指令(05H),然后给 SDI 发送 8 位寄存器地址,再从 SDO 接受 8 位数据。在这期间,SCS 必须保持在有效(低电平)。写和读的时序图如下:
SPI 方式写时序
SPI 方式读时序
寄存器介绍
寄存器的地址空间为 8 位,常用寄存器的详细说明如下:
移植工程
引脚选择
移植步骤
本例基于资料中提供的测试代码进行移植,测试代码路径:LD3320\LD3320 语音识别-长条形\7.1、STM32F103C8T6 应用程序,文件中的关键代码为 HARDWARE\LD3320 下的 Reg_RW.h、Reg_RW.c、LDChip.h 和 LDChip.c,以及 main.c 中的逻辑处理,以下分别移植各个文件。
Reg_RW.h
移植 Reg_RW.h,根据本例连线修改对应的时钟和引脚,具体代码如下。
/********************************************************************************
* 测试硬件:立创·梁山派开发板GD32F470ZGT6 使用主频200Mhz 晶振25Mhz
* 版 本 号: V1.0
* 修改作者: TaurusHard
* 修改日期: 2023年10月18日
* 功能介绍:
******************************************************************************
* 梁山派软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:club.szlcsc.com
* 其余模块移植手册:https://dri8c0qdfb.feishu.cn/docx/EGRVdxunnohkrNxItYTcrwAnnHe
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*********************************************************************************/
#ifndef REG_RW_H
#define REG_RW_H
#include "sys.h"
#include "systick.h"
#define SDCK PBout(10) // SPI 时钟信号
#define SDO PBin(11) // SPI 数据输出
#define SDI PBout(12) // SPI 数据输入
#define SCS PBout(13) // 芯片片选信号
#define RSTB PBout(14) // 复位端口
#define IRQ PBin(1) // 中断引脚
/// LD3320引脚相关定义
#define LD3320_SDCK_GPIO_CLK RCU_GPIOB
#define LD3320_SDCK_GPIO_PORT GPIOB
#define LD3320_SDCK_PIN GPIO_PIN_10
#define LD3320_SDO_GPIO_CLK RCU_GPIOB
#define LD3320_SDO_GPIO_PORT GPIOB
#define LD3320_SDO_PIN GPIO_PIN_11
#define LD3320_SDI_GPIO_CLK RCU_GPIOB
#define LD3320_SDI_GPIO_PORT GPIOB
#define LD3320_SDI_PIN GPIO_PIN_12
#define LD3320_SCS_GPIO_CLK RCU_GPIOB
#define LD3320_SCS_GPIO_PORT GPIOB
#define LD3320_SCS_PIN GPIO_PIN_13
#define LD3320_RSTB_GPIO_CLK RCU_GPIOB
#define LD3320_RSTB_GPIO_PORT GPIOB
#define LD3320_RSTB_PIN GPIO_PIN_14
#define LD3320_IRQ_EXTI_CLK RCU_GPIOB // 按键端口时钟
#define LD3320_IRQ_EXTI_PORT GPIOB // 按键端口
#define LD3320_IRQ_EXTI_PIN GPIO_PIN_1 // 按键引脚
#define LD3320_IRQ_EXTI_IRQN EXTI1_IRQn // 外部中断1
#define LD3320_IRQ_EXTI_PORT_SOURCE EXTI_SOURCE_GPIOB // 外部中断端口资源
#define LD3320_IRQ_EXTI_PIN_SOURCE EXTI_SOURCE_PIN1 // 外部中断引脚资源
#define LD3320_IRQ_EXTI_LINE EXTI_1 // 外部中断
#define LD3320_IRQ_EXTI_IRQHANDLER EXTI1_IRQHandler // 外部中断函数名
// 函数声明
void LD3320_Init(void);
void EXTIX_Init(void);
void LD_WriteReg(unsigned char address, unsigned char dataout);
unsigned char LD_ReadReg(unsigned char address);
#endif2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
Reg_RW.c
移植 Reg_RW.c,根据具体连接引脚修改 LD3320_Init()和 EXTIX_Init()函数,具体代码如下。
/********************************************************************************
* 测试硬件:立创·梁山派开发板GD32F470ZGT6 使用主频200Mhz 晶振25Mhz
* 版 本 号: V1.0
* 修改作者: TaurusHard
* 修改日期: 2023年10月18日
* 功能介绍:
******************************************************************************
* 梁山派软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:club.szlcsc.com
* 其余模块移植手册:https://dri8c0qdfb.feishu.cn/docx/EGRVdxunnohkrNxItYTcrwAnnHe
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*********************************************************************************/
#include "Reg_RW.h"
#include "LDChip.h"
#include "systick.h"
#include "led.h"
#include "stdio.h"
// 软件模拟SPI方式读写
#define DELAY_NOP delay_us(1)
void LD_WriteReg(unsigned char address, unsigned char dataout)
{
unsigned char i = 0;
unsigned char command = 0x04;
SCS = 0;
DELAY_NOP;
// write command
for(i = 0; i < 8; i++)
{
if(command & 0x80)
SDI = 1;
else
SDI = 0;
DELAY_NOP;
SDCK = 0;
command = (command << 1);
DELAY_NOP;
SDCK = 1;
}
// write address
for(i = 0; i < 8; i++)
{
if(address & 0x80)
SDI = 1;
else
SDI = 0;
DELAY_NOP;
SDCK = 0;
address = (address << 1);
DELAY_NOP;
SDCK = 1;
}
// write data
for(i = 0; i < 8; i++)
{
if(dataout & 0x80)
SDI = 1;
else
SDI = 0;
DELAY_NOP;
SDCK = 0;
dataout = (dataout << 1);
DELAY_NOP;
SDCK = 1;
}
DELAY_NOP;
SCS = 1;
}
unsigned char LD_ReadReg(unsigned char address)
{
unsigned char i = 0;
unsigned char datain = 0;
unsigned char temp = 0;
unsigned char command = 0x05;
SCS = 0;
DELAY_NOP;
// write command
for(i = 0; i < 8; i++)
{
if(command & 0x80)
SDI = 1;
else
SDI = 0;
DELAY_NOP;
SDCK = 0;
command = (command << 1);
DELAY_NOP;
SDCK = 1;
}
// write address
for(i = 0; i < 8; i++)
{
if(address & 0x80)
SDI = 1;
else
SDI = 0;
DELAY_NOP;
SDCK = 0;
address = (address << 1);
DELAY_NOP;
SDCK = 1;
}
DELAY_NOP;
// Read
for(i = 0; i < 8; i++)
{
datain = (datain << 1);
temp = SDO;
DELAY_NOP;
SDCK = 0;
if(temp == 1)
datain |= 0x01;
DELAY_NOP;
SDCK = 1;
}
DELAY_NOP;
SCS = 1;
return datain;
}
void LD3320_Init(void)
{
/* 使能时钟 */
rcu_periph_clock_enable(LD3320_SDCK_GPIO_CLK);
/* 配置模式 */
gpio_mode_set(LD3320_SDCK_GPIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, LD3320_SDCK_PIN);
/* 配置为推挽输出 50MHZ */
gpio_output_options_set(LD3320_SDCK_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, LD3320_SDCK_PIN);
/* 使能时钟 */
rcu_periph_clock_enable(LD3320_SDO_GPIO_CLK);
/* 配置模式 */
gpio_mode_set(LD3320_SDO_GPIO_PORT, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, LD3320_SDO_PIN);
/* 使能时钟 */
rcu_periph_clock_enable(LD3320_SDI_GPIO_CLK);
/* 配置模式 */
gpio_mode_set(LD3320_SDI_GPIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, LD3320_SDI_PIN);
/* 配置为推挽输出 50MHZ */
gpio_output_options_set(LD3320_SDI_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, LD3320_SDI_PIN);
/* 使能时钟 */
rcu_periph_clock_enable(LD3320_SCS_GPIO_CLK);
/* 配置模式 */
gpio_mode_set(LD3320_SCS_GPIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, LD3320_SCS_PIN);
/* 配置为推挽输出 50MHZ */
gpio_output_options_set(LD3320_SCS_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, LD3320_SCS_PIN);
/* 使能时钟 */
rcu_periph_clock_enable(LD3320_RSTB_GPIO_CLK);
/* 配置模式 */
gpio_mode_set(LD3320_RSTB_GPIO_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, LD3320_RSTB_PIN);
/* 配置为推挽输出 50MHZ */
gpio_output_options_set(LD3320_RSTB_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, LD3320_RSTB_PIN);
}
void EXTIX_Init(void)
{
/* 开启时钟 */
rcu_periph_clock_enable(LD3320_IRQ_EXTI_CLK);
rcu_periph_clock_enable(RCU_SYSCFG); // 系统配置时钟
/* 配置模式 */
gpio_mode_set(LD3320_IRQ_EXTI_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, LD3320_IRQ_EXTI_PIN);
/* 使能NVIC中断 中断分组为2位抢占优先级,2位子优先级 */
nvic_irq_enable(LD3320_IRQ_EXTI_IRQN, 2U, 2U); // 抢占优先级2,子优先级2
/* 连接中断线到GPIO */
syscfg_exti_line_config(LD3320_IRQ_EXTI_PORT_SOURCE, LD3320_IRQ_EXTI_PIN_SOURCE);
/* 初始化中断线 */
exti_init(LD3320_IRQ_EXTI_LINE, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
/* 使能中断 */
exti_interrupt_enable(LD3320_IRQ_EXTI_LINE);
/* 清除中断标志位 */
exti_interrupt_flag_clear(LD3320_IRQ_EXTI_LINE);
}
void LD3320_IRQ_EXTI_IRQHANDLER(void)
{
if(SET == exti_interrupt_flag_get(LD3320_IRQ_EXTI_LINE)) // 中断标志位为1
{
// printf("进入中断\r\n");
ProcessInt();
exti_interrupt_flag_clear(LD3320_IRQ_EXTI_LINE); // 清中断标志位
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
LDChip.h
移植 LDChip.h,根据具体需求修改识别码,本例增加两个识别码:增加两个宏,分别为 CODE_OPEN_LED 开灯(0x01)和 CODE_CLOSE_LED 关灯(0x02),如下图所示。
/********************************************************************************
* 测试硬件:立创·梁山派开发板GD32F470ZGT6 使用主频200Mhz 晶振25Mhz
* 版 本 号: V1.0
* 修改作者: TaurusHard
* 修改日期: 2023年10月18日
* 功能介绍:
******************************************************************************
* 梁山派软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:club.szlcsc.com
* 其余模块移植手册:https://dri8c0qdfb.feishu.cn/docx/EGRVdxunnohkrNxItYTcrwAnnHe
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*********************************************************************************/
#ifndef LD_CHIP_H
#define LD_CHIP_H
#include "sys.h"
// 以下三个状态定义用来记录程序是在运行ASR识别还是在运行MP3播放
#define LD_MODE_IDLE 0x00
#define LD_MODE_ASR_RUN 0x08
#define LD_MODE_MP3 0x40
// 以下五个状态定义用来记录程序是在运行ASR识别过程中的哪个状态
#define LD_ASR_NONE 0x00 // 表示没有在作ASR识别
#define LD_ASR_RUNING 0x01 // 表示LD3320正在作ASR识别中
#define LD_ASR_FOUNDOK 0x10 // 表示一次识别流程结束后,有一个识别结果
#define LD_ASR_FOUNDZERO 0x11 // 表示一次识别流程结束后,没有识别结果
#define LD_ASR_ERROR 0x31 // 表示一次识别流程中LD3320芯片内部出现不正确的状态
#define CLK_IN 24 /* 用户注意修改输入的晶振时钟大小 */
#define LD_PLL_11 (uint8_t)((CLK_IN / 2.0) - 1)
#define LD_PLL_MP3_19 0x0f
#define LD_PLL_MP3_1B 0x18
#define LD_PLL_MP3_1D (uint8_t)(((90.0 * ((LD_PLL_11) + 1)) / (CLK_IN)) - 1)
#define LD_PLL_ASR_19 (uint8_t)(CLK_IN * 32.0 / (LD_PLL_11 + 1) - 0.51)
#define LD_PLL_ASR_1B 0x48
#define LD_PLL_ASR_1D 0x1f
#define MIC_VOL 0x43 // 咪头增益
// 识别码修改处
#define CODE_CMD 0x00 // 该命令码0x00用户不可进行修改。
#define CODE_OPEN_LED 0x01 // 开灯
#define CODE_CLOSE_LED 0x02 // 关灯
// 函数声明
void LD_Reset(void);
void LD_Init_Common(void);
void LD_Init_ASR(void);
void ProcessInt(void);
void LD_AsrStart(void);
uint8_t LD_AsrRun(void);
uint8_t LD_AsrAddFixed(void);
uint8_t RunASR(void);
uint8_t LD_GetResult(void);
#endif2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
LDChip.c
移植 LDChip.c,修改 LD_AsrAddFixed()函数中的 sRecog[DATE_A][DATE_B]和 pCode[DATE_A]二维数组的值,其中 sRecog 数组为关键词的拼音,增加"liang shan pai"、"kai deng"和"guan deng",pCode 数组为识别码,增加 CODE_CMD、CODE_OPEN_LED、CODE_CLOSE_LED,即 CODE_CMD 对应"liang shan pai"(梁山派的拼音)、CODE_OPEN_LED 对应"kai deng"(开灯的拼音)、CODE_CLOSE_LED 对应"guan deng"(关灯的拼音),如下图所示。
具体代码如下。
/********************************************************************************
* 测试硬件:立创·梁山派开发板GD32F470ZGT6 使用主频200Mhz 晶振25Mhz
* 版 本 号: V1.0
* 修改作者: TaurusHard
* 修改日期: 2023年10月18日
* 功能介绍:
******************************************************************************
* 梁山派软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:club.szlcsc.com
* 其余模块移植手册:https://dri8c0qdfb.feishu.cn/docx/EGRVdxunnohkrNxItYTcrwAnnHe
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*********************************************************************************/
#include "Reg_RW.h"
#include "LDChip.h"
#include "systick.h"
extern uint8_t nAsrStatus;
uint8_t nLD_Mode = LD_MODE_IDLE;
uint8_t ucRegVal;
/************************************************************************
功能描述: 复位LD模块
入口参数: none
返 回 值: none
其他说明: none
**************************************************************************/
void LD_Reset(void)
{
RSTB = 1;
delay_ms(5);
RSTB = 0;
delay_ms(5);
RSTB = 1;
delay_ms(5);
SCS = 0;
delay_ms(5);
SCS = 1;
delay_ms(5);
}
/************************************************************************
功能描述: LD模块命令初始化
入口参数: none
返 回 值: none
其他说明: 该函数为出厂配置,一般不需要修改;
有兴趣的客户可对照开发手册根据需要自行修改。
**************************************************************************/
void LD_Init_Common(void)
{
LD_ReadReg(0x06);
LD_WriteReg(0x17, 0x35);
delay_ms(5);
LD_ReadReg(0x06);
LD_WriteReg(0x89, 0x03);
delay_ms(5);
LD_WriteReg(0xCF, 0x43);
delay_ms(5);
LD_WriteReg(0xCB, 0x02);
/*PLL setting*/
LD_WriteReg(0x11, LD_PLL_11);
if(nLD_Mode == LD_MODE_MP3)
{
LD_WriteReg(0x1E, 0x00);
LD_WriteReg(0x19, LD_PLL_MP3_19);
LD_WriteReg(0x1B, LD_PLL_MP3_1B);
LD_WriteReg(0x1D, LD_PLL_MP3_1D);
}
else
{
LD_WriteReg(0x1E, 0x00);
LD_WriteReg(0x19, LD_PLL_ASR_19);
LD_WriteReg(0x1B, LD_PLL_ASR_1B);
LD_WriteReg(0x1D, LD_PLL_ASR_1D);
}
delay_ms(5);
LD_WriteReg(0xCD, 0x04);
LD_WriteReg(0x17, 0x4c);
delay_ms(5);
LD_WriteReg(0xB9, 0x00);
LD_WriteReg(0xCF, 0x4F);
LD_WriteReg(0x6F, 0xFF);
}
/************************************************************************
功能描述: LD模块 ASR功能初始化
入口参数: none
返 回 值: none
其他说明: 该函数为出厂配置,一般不需要修改;
有兴趣的客户可对照开发手册根据需要自行修改。
**************************************************************************/
void LD_Init_ASR(void)
{
nLD_Mode = LD_MODE_ASR_RUN;
LD_Init_Common();
LD_WriteReg(0xBD, 0x00);
LD_WriteReg(0x17, 0x48);
delay_ms(5);
LD_WriteReg(0x3C, 0x80);
LD_WriteReg(0x3E, 0x07);
LD_WriteReg(0x38, 0xff);
LD_WriteReg(0x3A, 0x07);
LD_WriteReg(0x40, 0);
LD_WriteReg(0x42, 8);
LD_WriteReg(0x44, 0);
LD_WriteReg(0x46, 8);
delay_ms(5);
}
/************************************************************************
功能描述: 中断处理函数
入口参数: none
返 回 值: none
其他说明: 当LD模块接收到音频信号时,将进入该函数,
判断识别是否有结果,如果没有从新配置寄
存器准备下一次的识别。
**************************************************************************/
void ProcessInt(void)
{
uint8_t nAsrResCount = 0;
ucRegVal = LD_ReadReg(0x2B);
LD_WriteReg(0x29, 0);
LD_WriteReg(0x02, 0);
if((ucRegVal & 0x10) && LD_ReadReg(0xb2) == 0x21 && LD_ReadReg(0xbf) == 0x35) /*识别成功*/
{
nAsrResCount = LD_ReadReg(0xba);
if(nAsrResCount > 0 && nAsrResCount <= 4)
{
nAsrStatus = LD_ASR_FOUNDOK;
}
else
{
nAsrStatus = LD_ASR_FOUNDZERO;
}
} /*没有识别结果*/
else
{
nAsrStatus = LD_ASR_FOUNDZERO;
}
LD_WriteReg(0x2b, 0);
LD_WriteReg(0x1C, 0); /*写0:ADC不可用*/
LD_WriteReg(0x29, 0);
LD_WriteReg(0x02, 0);
LD_WriteReg(0x2B, 0);
LD_WriteReg(0xBA, 0);
LD_WriteReg(0xBC, 0);
LD_WriteReg(0x08, 1); /*清除FIFO_DATA*/
LD_WriteReg(0x08, 0); /*清除FIFO_DATA后 再次写0*/
}
/************************************************************************
功能描述: 检测LD模块是否空闲
入口参数: none
返 回 值: flag:1-> 空闲
其他说明: none
**************************************************************************/
uint8_t LD_Check_ASRBusyFlag_b2(void)
{
uint8_t j, i;
uint8_t flag = 0;
for(j = 0; j < 5; j++)
{
i = LD_ReadReg(0xb2);
if(i == 0x21)
{
flag = 1;
break;
}
delay_ms(20);
}
return flag;
}
/************************************************************************
功能描述: 启动ASR
入口参数: none
返 回 值: none
其他说明: none
**************************************************************************/
void LD_AsrStart(void)
{
LD_Init_ASR();
}
/************************************************************************
功能描述: 运行ASR
入口参数: none
返 回 值: 1:启动成功
其他说明: none
**************************************************************************/
uint8_t LD_AsrRun(void)
{
LD_WriteReg(0x35, MIC_VOL);
LD_WriteReg(0x1C, 0x09);
LD_WriteReg(0xBD, 0x20);
LD_WriteReg(0x08, 0x01);
delay_ms(5);
LD_WriteReg(0x08, 0x00);
delay_ms(5);
if(LD_Check_ASRBusyFlag_b2() == 0)
{
return 0;
}
LD_WriteReg(0xB2, 0xff);
LD_WriteReg(0x37, 0x06);
delay_ms(5);
LD_WriteReg(0x37, 0x06);
delay_ms(5);
LD_WriteReg(0x1C, 0x0b);
LD_WriteReg(0x29, 0x10);
LD_WriteReg(0xBD, 0x00);
return 1;
}
/************************************************************************
功能描述: 向LD模块添加关键词
入口参数: none
返 回 值: flag:1->添加成功
其他说明: 用户修改.
1、根据如下格式添加拼音关键词,同时注意修改sRecog 和pCode 数组的长度
和对应变了k的循环置。拼音串和识别码是一一对应的。
2、开发者可以学习"语音识别芯片LD3320高阶秘籍.pdf"中
关于垃圾词语吸收错误的用法,来提供识别效果。
**************************************************************************/
uint8_t LD_AsrAddFixed(void)
{
uint8_t k, flag;
uint8_t nAsrAddLength;
#define DATE_A 14 // 数组二维数值
#define DATE_B 20 // 数组一维数值
uint8_t sRecog[DATE_A][DATE_B] = {
"liang shan pai",
"kai deng",
"guan deng",
}; /*添加关键词,用户修改*/
uint8_t pCode[DATE_A] = {
CODE_CMD,
CODE_OPEN_LED,
CODE_CLOSE_LED
}; /*添加识别码,用户修改*/
flag = 1;
for(k = 0; k < DATE_A; k++)
{
if(LD_Check_ASRBusyFlag_b2() == 0)
{
flag = 0;
break;
}
LD_WriteReg(0xc1, pCode[k]);
LD_WriteReg(0xc3, 0);
LD_WriteReg(0x08, 0x04);
delay_ms(1);
LD_WriteReg(0x08, 0x00);
delay_ms(1);
for(nAsrAddLength = 0; nAsrAddLength < DATE_B; nAsrAddLength++)
{
if(sRecog[k][nAsrAddLength] == 0)
break;
LD_WriteReg(0x5, sRecog[k][nAsrAddLength]);
}
LD_WriteReg(0xb9, nAsrAddLength);
LD_WriteReg(0xb2, 0xff);
LD_WriteReg(0x37, 0x04);
}
return flag;
}
/************************************************************************
功能描述: 运行ASR识别流程
入口参数: none
返 回 值: asrflag:1->启动成功, 0—>启动失败
其他说明: 识别顺序如下:
1、RunASR()函数实现了一次完整的ASR语音识别流程
2、LD_AsrStart() 函数实现了ASR初始化
3、LD_AsrAddFixed() 函数实现了添加关键词语到LD3320芯片中
4、LD_AsrRun() 函数启动了一次ASR语音识别流程
任何一次ASR识别流程,都需要按照这个顺序,从初始化开始
**************************************************************************/
uint8_t RunASR(void)
{
uint8_t i = 0;
uint8_t asrflag = 0;
for(i = 0; i < 5; i++) // 防止由于硬件原因导致LD3320芯片工作不正常,所以一共尝试5次启动ASR识别流程
{
LD_AsrStart();
delay_ms(5);
if(LD_AsrAddFixed() == 0)
{
LD_Reset(); // LD3320芯片内部出现不正常,立即重启LD3320芯片
delay_ms(5); // 并从初始化开始重新ASR识别流程
continue;
}
delay_ms(5);
if(LD_AsrRun() == 0)
{
LD_Reset(); // LD3320芯片内部出现不正常,立即重启LD3320芯片
delay_ms(5); // 并从初始化开始重新ASR识别流程
continue;
}
asrflag = 1;
break; // ASR流程启动成功,退出当前for循环。开始等待LD3320送出的中断信号
}
return asrflag;
}
/************************************************************************
功能描述: 获取识别结果
入口参数: none
返 回 值: LD_ReadReg(0xc5 ); 读取内部寄存器返回识别码。
其他说明: none
**************************************************************************/
uint8_t LD_GetResult(void)
{
return LD_ReadReg(0xc5);
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
main.c
移植 main.c,修改 main()函数和 User_Modification()函数,本例为了识别的精确度,增加了二级口令识别,第一级为识别码 CODE_CMD(0x00),第二级为开灯(0x01)和关灯(0x02),即先说出“梁山派”且被识别到后,再说出“开灯”/“关灯”系统才会执行开灯/关灯操作,具体代码如下。
/********************************************************************************
* 测试硬件:立创·梁山派开发板GD32F470ZGT6 使用主频200Mhz 晶振25Mhz
* 版 本 号: V1.0
* 修改作者: TaurusHard
* 修改日期: 2023年10月18日
* 功能介绍:
******************************************************************************
* 梁山派软硬件资料与相关扩展板软硬件资料官网全部开源
* 开发板官网:www.lckfb.com
* 技术支持常驻论坛,任何技术问题欢迎随时交流学习
* 立创论坛:club.szlcsc.com
* 其余模块移植手册:https://dri8c0qdfb.feishu.cn/docx/EGRVdxunnohkrNxItYTcrwAnnHe
* 关注bilibili账号:【立创开发板】,掌握我们的最新动态!
* 不靠卖板赚钱,以培养中国工程师为己任
*********************************************************************************/
#include "sys.h"
#include "main.h"
#include "systick.h"
#include <stdio.h>
#include "led.h"
#include "uart.h"
#include "Reg_RW.h"
#include "LDChip.h"
void User_Modification(uint8_t dat);
uint8_t nAsrStatus = 0;
uint8_t nAsrRes = 0;
uint8_t flag = 0;
/*!
\brief main function
\param[in] none
\param[out] none
\retval none
*/
int main(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); // 优先级分组
systick_config(); // 滴答定时器初始化
led_init(); // led初始化
usart_gpio_config(115200); // 串口0初始化
LD3320_Init();
EXTIX_Init();
LD_Reset();
nAsrStatus = LD_ASR_NONE; // 初始状态:没有在作ASR
SCS = 0;
printf("运行程序\r\n");
while(1)
{
switch(nAsrStatus)
{
case LD_ASR_RUNING:
case LD_ASR_ERROR:
break;
case LD_ASR_NONE:
{
nAsrStatus = LD_ASR_RUNING;
if(RunASR() == 0) /* 启动一次ASR识别流程:ASR初始化,ASR添加关键词语,启动ASR运算*/
{
nAsrStatus = LD_ASR_ERROR;
}
break;
}
case LD_ASR_FOUNDOK: /* 一次ASR识别流程结束,去取ASR识别结果*/
{
nAsrRes = LD_GetResult(); /*获取结果*/
User_Modification(nAsrRes);
nAsrStatus = LD_ASR_NONE;
break;
}
case LD_ASR_FOUNDZERO:
default:
{
nAsrStatus = LD_ASR_NONE;
break;
}
}
}
}
void User_Modification(uint8_t dat)
{
if(dat == CODE_CMD)
{
flag = 1;
printf("\"梁山派\"识别成功\r\n");
}
else if(flag)
{
flag = 0;
switch(nAsrRes) /*对结果执行相关操作*/
{
case CODE_OPEN_LED:
printf("\"开灯\"识别成功\r\n");
LED1 = 1;
break;
case CODE_CLOSE_LED:
printf("\"关灯\"识别成功\r\n");
LED1 = 0;
break;
default:
break;
}
}
else
{
printf("请说出一级口令\r\n");
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
移植验证
将 DAP-Link 连接至电脑 USB 接口,打开串口调试助手,编译代码下载到梁山派开发板后,运行代码,说出“梁山派”第一级口令,说出“开灯”第二级口令后,LED1 会亮起,同时串口打印出“"开灯"识别成功”;说出“梁山派”第一级口令,说出“关灯”第二级口令后,LED1 会熄灭,同时串口打印出“"关灯"识别成功”,以下为演示视频。
移植成功代码见下文件。
通过网盘分享的文件:立创·梁山派GD32F470ZGT6开发板【模块移植手册代码】
链接: https://pan.baidu.com/s/1pp44yjD1Dhh7U9iZ2a11IA 提取码: LCKF